provider: MiniMax Code's international site is checked in daily too (ARNO on Discord: MiniMax Code 国际版能否也自动签到) — v0.1.910's check-in asked only agent.minimax.cn, for the plugin's minimax-code accounts, since the international site's wasn't known. It is the same: agent.minimax.io's page has the Daily check-in panel (signin.menu_label in its bundle), and its /minimax-cloud/api/v1/signin/status and /claim answer as the China site's do — {"error":"invalid signature"} unsigned, 401 signed with MiniMax Code's page secret and no sign-in, where a path that isn't there is a 404 page. The check-in now takes the accounts of both of the plugin's providers, minimax-code and minimax-code-global, each asked at its own site through the plugin's fetch, and keeps each apart in minimax-checkin.json (site|uid), its result shown on that site's card. TestMiniMaxGlobalCheckin (a global account claimed at its site, a China one with the same uid asked only at the China site, keys apart) fails on the old code; TestMiniMaxCheckin and TestMiniMaxClaimAlready pass. Not tried with a real international account (none here). go vet, builds (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass.
This commit is contained in:
1 parent
a84f1052e0
commit
98b3ed5aad
2 files changed
+104
-27
No files matched your search
@@ -3,15 +3,19 @@ package provider
|
||||
// MiniMax Code's daily check-in (签到, #811: a week of daily credits, more
|
||||
// on days 4 and 7): MiniMax Code is served only by its community plugin,
|
||||
// @magpie-community/opencode-minimax-auth (provider "minimax-code", the
|
||||
// China site; no built-in, no mover). While the setting is on, whichever
|
||||
// magpie runs the gateway presses it for each of the plugin's accounts once
|
||||
// a Beijing day, as MiniMax Code's web page does: it asks the check-in's
|
||||
// panel on agent.minimax.cn, and claims while today's is claimable. Both go
|
||||
// China site, and "minimax-code-global", the international one; no
|
||||
// built-in, no mover). While the setting is on, whichever magpie runs the
|
||||
// gateway presses it for each of the plugin's accounts once a Beijing day,
|
||||
// as MiniMax Code's web page does: it asks the check-in's panel on the
|
||||
// account's site (agent.minimax.cn, agent.minimax.io), and claims while
|
||||
// today's is claimable. Both go
|
||||
// through the plugin's fetch, which sends them as the account (its access
|
||||
// token, renewed when near its end); the page's own signature
|
||||
// (x-timestamp, x-signature) goes with them, though MiniMax doesn't check
|
||||
// it now. The international site's check-in isn't known, so its accounts
|
||||
// aren't checked in.
|
||||
// it now. The international site has the same check-in (ARNO on Discord):
|
||||
// its page has the Daily check-in panel, and its /signin/status and /claim
|
||||
// answer as the China site's do, refusing a bad signature and then asking
|
||||
// for a sign-in.
|
||||
//
|
||||
// What came of it is kept in minimax-checkin.json by account id, as
|
||||
// WorkBuddy's and Trae CN's are (checkin.go).
|
||||
@@ -36,12 +40,19 @@ import (
|
||||
"github.com/yetone/magpie/internal/settings"
|
||||
)
|
||||
|
||||
// MiniMaxCodeID is the MiniMax Code plugin's provider (the China site).
|
||||
const MiniMaxCodeID = "minimax-code"
|
||||
// MiniMaxCodeID is the MiniMax Code plugin's provider (the China site),
|
||||
// MiniMaxCodeGlobalID its international site's.
|
||||
const (
|
||||
MiniMaxCodeID = "minimax-code"
|
||||
MiniMaxCodeGlobalID = "minimax-code-global"
|
||||
)
|
||||
|
||||
// miniMaxCheckinURL is MiniMax Code's check-in: /status, then /claim, each
|
||||
// with the day's time zone.
|
||||
var miniMaxCheckinURL = "https://agent.minimax.cn/minimax-cloud/api/v1/signin"
|
||||
// miniMaxCheckinURLs are MiniMax Code's check-in on each site, by the
|
||||
// plugin's provider: /status, then /claim, each with the day's time zone.
|
||||
var miniMaxCheckinURLs = map[string]string{
|
||||
MiniMaxCodeID: "https://agent.minimax.cn/minimax-cloud/api/v1/signin",
|
||||
MiniMaxCodeGlobalID: "https://agent.minimax.io/minimax-cloud/api/v1/signin",
|
||||
}
|
||||
|
||||
// miniMaxSignSecret is what MiniMax Code's page signs its requests with:
|
||||
// x-signature is md5(seconds + it + the body).
|
||||
@@ -84,16 +95,25 @@ type miniMaxClaim struct {
|
||||
Base *miniMaxBase `json:"base_resp"`
|
||||
}
|
||||
|
||||
// miniMaxAccount is a MiniMax Code account checked in: via sends a request
|
||||
// as it.
|
||||
// miniMaxAccount is a MiniMax Code account checked in: site is the
|
||||
// plugin's provider it is signed in to ("" the China site's), via sends a
|
||||
// request as it.
|
||||
type miniMaxAccount struct {
|
||||
User string
|
||||
On bool
|
||||
site string
|
||||
uid string
|
||||
card string
|
||||
via func(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
func (a miniMaxAccount) siteID() string {
|
||||
if a.site == "" {
|
||||
return MiniMaxCodeID
|
||||
}
|
||||
return a.site
|
||||
}
|
||||
|
||||
func miniMaxCheckinPath() string {
|
||||
return filepath.Join(filepath.Dir(Path()), "minimax-checkin.json")
|
||||
}
|
||||
@@ -102,7 +122,7 @@ func miniMaxCheckinKey(a miniMaxAccount) string {
|
||||
if a.uid == "" {
|
||||
return ""
|
||||
}
|
||||
return MiniMaxCodeID + "|" + a.uid
|
||||
return a.siteID() + "|" + a.uid
|
||||
}
|
||||
|
||||
var miniMaxCheckinMu sync.Mutex
|
||||
@@ -177,7 +197,7 @@ func miniMaxCall(ctx context.Context, a miniMaxAccount, method, path string, dst
|
||||
if body != "" {
|
||||
rd = strings.NewReader(body)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, method, miniMaxCheckinURL+path+"?timezone_id=Asia%2FShanghai", rd)
|
||||
req, err := http.NewRequestWithContext(ctx, method, miniMaxCheckinURLs[a.siteID()]+path+"?timezone_id=Asia%2FShanghai", rd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -199,7 +219,9 @@ func miniMaxCall(ctx context.Context, a miniMaxAccount, method, path string, dst
|
||||
if err := json.Unmarshal(b, dst); err != nil || res.StatusCode < 200 || res.StatusCode >= 300 {
|
||||
if err == nil {
|
||||
// MiniMax's own answer, in an error status
|
||||
var e struct{ Base *miniMaxBase `json:"base_resp"` }
|
||||
var e struct {
|
||||
Base *miniMaxBase `json:"base_resp"`
|
||||
}
|
||||
if json.Unmarshal(b, &e) == nil && e.Base != nil && e.Base.Code != 0 {
|
||||
return fmt.Errorf("code %d: %s", e.Base.Code, e.Base.Msg)
|
||||
}
|
||||
@@ -233,7 +255,8 @@ func MiniMaxCheckins() []WorkBuddyCheckin {
|
||||
return out
|
||||
}
|
||||
|
||||
// HasMiniMax says whether a MiniMax Code (China) account is signed in.
|
||||
// HasMiniMax says whether a MiniMax Code account is signed in, on either
|
||||
// site.
|
||||
func HasMiniMax() bool { return len(miniMaxCheckinAccounts()) > 0 }
|
||||
|
||||
// miniMaxCards are the usage cards of accts, for WithCheckins.
|
||||
@@ -247,13 +270,20 @@ func miniMaxCards(accts []miniMaxAccount) []checkinCard {
|
||||
return out
|
||||
}
|
||||
|
||||
// miniMaxCheckinAccounts are the MiniMax Code plugin's accounts, each sent
|
||||
// through the plugin's fetch, which signs it in as the account.
|
||||
// miniMaxCheckinAccounts are the MiniMax Code plugin's accounts on both
|
||||
// sites, each sent through the plugin's fetch, which signs it in as the
|
||||
// account.
|
||||
func miniMaxCheckinAccounts() []miniMaxAccount {
|
||||
pp, ok := PluginOf(MiniMaxCodeID)
|
||||
if !ok {
|
||||
return nil
|
||||
var out []miniMaxAccount
|
||||
for _, site := range []string{MiniMaxCodeID, MiniMaxCodeGlobalID} {
|
||||
if pp, ok := PluginOf(site); ok {
|
||||
out = append(out, miniMaxSiteAccounts(site, pp)...)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func miniMaxSiteAccounts(site string, pp plugin.Provider) []miniMaxAccount {
|
||||
auths := plugin.Auths(pp.ID)
|
||||
card := PluginID(pp.ID)
|
||||
var out []miniMaxAccount
|
||||
@@ -263,7 +293,7 @@ func miniMaxCheckinAccounts() []miniMaxAccount {
|
||||
if uid == "" {
|
||||
uid = str(auths[key]["uid"])
|
||||
}
|
||||
out = append(out, miniMaxAccount{User: l.User, On: l.On, uid: uid, card: card,
|
||||
out = append(out, miniMaxAccount{User: l.User, On: l.On, site: site, uid: uid, card: card,
|
||||
via: func(req *http.Request) (*http.Response, error) {
|
||||
h := map[string]string{}
|
||||
for k, vs := range req.Header {
|
||||
|
||||
@@ -41,7 +41,10 @@ func (f *fakeMiniMaxCheckin) panel() string {
|
||||
return `{"scene":2,"days":[` + strings.Join(days, ",") + `]}`
|
||||
}
|
||||
|
||||
func (f *fakeMiniMaxCheckin) serve(t *testing.T) {
|
||||
func (f *fakeMiniMaxCheckin) serve(t *testing.T) { f.serveSite(t, MiniMaxCodeID) }
|
||||
|
||||
// serveSite serves the check-in of the plugin's provider site.
|
||||
func (f *fakeMiniMaxCheckin) serveSite(t *testing.T, site string) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
@@ -80,9 +83,9 @@ func (f *fakeMiniMaxCheckin) serve(t *testing.T) {
|
||||
}
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
old := miniMaxCheckinURL
|
||||
miniMaxCheckinURL = srv.URL + "/minimax-cloud/api/v1/signin"
|
||||
t.Cleanup(func() { miniMaxCheckinURL = old })
|
||||
old := miniMaxCheckinURLs[site]
|
||||
miniMaxCheckinURLs[site] = srv.URL + "/minimax-cloud/api/v1/signin"
|
||||
t.Cleanup(func() { miniMaxCheckinURLs[site] = old })
|
||||
}
|
||||
|
||||
func (f *fakeMiniMaxCheckin) take() []string {
|
||||
@@ -171,3 +174,47 @@ func TestMiniMaxClaimAlready(t *testing.T) {
|
||||
t.Fatalf("already: %+v", r)
|
||||
}
|
||||
}
|
||||
|
||||
// MiniMax Code's international site has the same check-in (ARNO on
|
||||
// Discord): an account signed in there is checked in at its own site, not
|
||||
// the China one, and kept apart from a China account of the same uid.
|
||||
func TestMiniMaxGlobalCheckin(t *testing.T) {
|
||||
if !strings.HasPrefix(miniMaxCheckinURLs[MiniMaxCodeGlobalID], "https://agent.minimax.io/") {
|
||||
t.Fatalf("the international site's check-in is %q", miniMaxCheckinURLs[MiniMaxCodeGlobalID])
|
||||
}
|
||||
cn := &fakeMiniMaxCheckin{today: mmDayClaimed}
|
||||
cn.serve(t)
|
||||
io := &fakeMiniMaxCheckin{today: mmDayClaimable}
|
||||
io.serveSite(t, MiniMaxCodeGlobalID)
|
||||
now := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
c := newMiniMaxCheckiner(func() []miniMaxAccount {
|
||||
return []miniMaxAccount{
|
||||
{User: "arno", On: true, site: MiniMaxCodeGlobalID, uid: "42", card: MiniMaxCodeGlobalID, via: http.DefaultClient.Do},
|
||||
{User: "hu", On: true, site: MiniMaxCodeID, uid: "42", card: MiniMaxCodeID, via: http.DefaultClient.Do},
|
||||
}
|
||||
})
|
||||
c.path = filepath.Join(t.TempDir(), "minimax-checkin.json")
|
||||
c.now = func() time.Time { return now }
|
||||
|
||||
rs := c.checkinNow(t.Context(), false)
|
||||
got := map[string]string{}
|
||||
for _, r := range rs {
|
||||
got[r.User] = r.Outcome
|
||||
}
|
||||
if len(rs) != 2 || got["arno"] != CheckinClaimed || got["hu"] != CheckinDone {
|
||||
t.Fatalf("checked in: %+v", rs)
|
||||
}
|
||||
if a := strings.Join(io.take(), ", "); a != "GET /status, POST /claim" {
|
||||
t.Fatalf("the international site was asked %s", a)
|
||||
}
|
||||
if a := strings.Join(cn.take(), ", "); a != "GET /status" {
|
||||
t.Fatalf("the China site was asked %s", a)
|
||||
}
|
||||
if len(io.bad)+len(cn.bad) != 0 {
|
||||
t.Fatalf("sent wrong: %v %v", io.bad, cn.bad)
|
||||
}
|
||||
k := miniMaxCheckinKey(miniMaxAccount{site: MiniMaxCodeGlobalID, uid: "42"})
|
||||
if k == miniMaxCheckinKey(miniMaxAccount{uid: "42"}) || !strings.HasPrefix(k, MiniMaxCodeGlobalID+"|") {
|
||||
t.Fatalf("key %q", k)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user