Download internal/upstream/school.go from a3216/gcli2api: direct link, hf CLI and curl.
- Browser
- Download file 9.84 kB
-
https://huggingface.co/spaces/a3216/gcli2api/resolve/main/internal/upstream/school.go
- Command line
-
hf download hf://spaces/a3216/gcli2api/internal/upstream/school.go
-
curl -L -o school.go https://huggingface.co/spaces/a3216/gcli2api/resolve/main/internal/upstream/school.go
9.84 kB
| // school.go 开学季活动(school-season,活动期 2026-09-13 ~ 09-24)纯 API 自动化。 | |
| // | |
| // 判据(2026-09-13 小程序 MCP 逆向 + 三账号实测,protocol.md §7.11): | |
| // - share_invite(每日 +100c +1抽奖):POST /tasks/share-complete {channel:"wechat"} | |
| // 即点亮——纯前端上报,服务端不校验真实分享回执。本模块的主目标。 | |
| // - chat_3_times / expert_use:判据绑定小程序原生沙箱会话(e2b runtime), | |
| // webchat 普通会话不计数,纯 API 不做(需小程序内人工对话)。 | |
| // - 抽奖:POST /wheel/draw {draw_uuid}(前端生成 uuid,消耗 1 chance)。 | |
| // | |
| // 端点基址 www.codebuddy.cn(billing 同域);信封 {code,msg,data},code=0 成功。 | |
| package upstream | |
| import ( | |
| "bytes" | |
| "encoding/json" | |
| "fmt" | |
| "net/http" | |
| "time" | |
| "github.com/linguo2625469/workbuddy2api-panel/internal/auth" | |
| ) | |
| const schoolBase = "/portal/activity/school" | |
| // schoolJSON 学院活动 API 请求(剥信封,业务 code≠0 返回带 msg 的 error)。 | |
| func (c *Client) schoolJSON(a *auth.Auth, method, path string, body map[string]any, out any) error { | |
| var raw []byte | |
| if body != nil { | |
| raw, _ = json.Marshal(body) | |
| } | |
| req, err := http.NewRequest(method, c.billingBase(a)+schoolBase+path, bytes.NewReader(raw)) | |
| if err != nil { | |
| return err | |
| } | |
| req.Header.Set("Authorization", "Bearer "+a.AccessTokenValue()) | |
| req.Header.Set("Content-Type", "application/json") | |
| req.Header.Set("Accept", "application/json") | |
| if a.UID != "" { | |
| req.Header.Set("X-User-Id", a.UID) | |
| } | |
| data, err := c.doJSON(req) | |
| if err != nil { | |
| return err | |
| } | |
| if out != nil { | |
| return json.Unmarshal(data, out) | |
| } | |
| return nil | |
| } | |
| // - chat_3_times:3 条 chat_request_send 即 3/3(conversationId 任意、桌面/mp | |
| // 头族均可计数,无需真实沙箱会话)。 | |
| // - expert_use:mp 指纹事件链 expert_summon_click + expert_summoned + | |
| // expert_actual_use + chat_request_send(开学季分类专家)即点亮。 | |
| const mpReportPath = "/v2/report" | |
| // schoolOpenDayActivityID 开学季/校园日活动 id(事件 activityId 字段值,两域共用)。 | |
| const schoolOpenDayActivityID = "school_open_day_2026" | |
| // mpEventBase 小程序埋点公共指纹(appservice wQ()+Ao() 对齐)。 | |
| func mpEventBase(a *auth.Auth) map[string]any { | |
| return map[string]any{ | |
| "timestamp": time.Now().UnixMilli(), | |
| "ideType": "WorkBuddy_MP", | |
| "ideVersion": "2.4.0", | |
| "extName": "workbuddy-mp", | |
| "extVersion": "2.4.0", | |
| "product": "SaaS", | |
| "ideName": "wx_app_cloud", | |
| "platform": "mini_program", | |
| "os": "windows", | |
| "osVersion": "11", | |
| "arch": "x64", | |
| "machineId": "0655736a-607f-4d9d-b430-58176ee9a090", | |
| "timezone": "Asia/Shanghai", | |
| "userId": a.UID, | |
| "userNickname": a.Nickname, | |
| } | |
| } | |
| // ReportMPEvent 以小程序指纹向 www.codebuddy.cn/v2/report 批量上报事件。 | |
| func (c *Client) ReportMPEvent(a *auth.Auth, events ...map[string]any) error { | |
| if len(events) == 0 { | |
| return fmt.Errorf("mp report: no events") | |
| } | |
| base := mpEventBase(a) | |
| arr := make([]map[string]any, 0, len(events)) | |
| for _, ev := range events { | |
| m := map[string]any{} | |
| for k, v := range base { | |
| m[k] = v | |
| } | |
| for k, v := range ev { | |
| m[k] = v | |
| } | |
| arr = append(arr, m) | |
| } | |
| raw, err := json.Marshal(arr) | |
| if err != nil { | |
| return err | |
| } | |
| req, err := http.NewRequest(http.MethodPost, c.BillingBaseCN+mpReportPath, bytes.NewReader(raw)) | |
| if err != nil { | |
| return err | |
| } | |
| req.Header.Set("Authorization", "Bearer "+a.AccessTokenValue()) | |
| req.Header.Set("Content-Type", "application/json") | |
| req.Header.Set("Accept", "application/json") | |
| if a.UID != "" { | |
| req.Header.Set("X-User-Id", a.UID) | |
| } | |
| req.Header.Set("X-Client-Product", "workbuddy-mp") | |
| req.Header.Set("X-Client-Version", "2.4.0") | |
| req.Header.Set("X-Client-Platform", "mp-weixin") | |
| req.Header.Set("X-Platform", "wechatmp") | |
| _, err = c.doJSON(req) | |
| return err | |
| } | |
| // SchoolChatTimesEvents 构造一条 chat_request_send 事件(chat_3_times 计数)。 | |
| func SchoolChatTimesEvents(conversationID string) map[string]any { | |
| rid := "wb2api-" + clientToken() | |
| return map[string]any{ | |
| "eventCode": "chat_request_send", | |
| "inputLength": 14, "isPlan": false, "isAutoExecuteTerminal": false, | |
| "isAutoModify": false, "codebaseEnable": false, "maxToken": 0, | |
| "maxSteps": 500, "temperature": 0, "maxRetries": 0, | |
| "mentionContexts": []any{}, "knowledgeId": []any{}, "knowledgeName": []any{}, | |
| "codebaseId": "", "mentionContextCount": 0, "command": "", | |
| "recommendId": "", "skillId": "", "skillCount": 0, "totalCount": 0, | |
| "traceId": rid, "rootRequestId": rid, | |
| "parentConversationId": conversationID, "conversationId": conversationID, | |
| "messageId": "msg-" + rid[len(rid)-8:], | |
| "agentName": "mp", "agentType": "main", | |
| "codebuddy.session_id": conversationID, | |
| "codebuddy.conversation_request_id": rid, | |
| } | |
| } | |
| // SchoolSeasonChatEvent 构造 growth 域「校园日」(school_season)判据事件: | |
| // mini 指纹 chat_request_send + activityId=school_open_day_2026(与 school 域 | |
| // 开学季同 activityId 关联;实测无 activityId 的事件不点亮)。事件形状与 | |
| // SchoolChatTimesEvents 同构(school 域 chat_3_times 同款),仅追加 activityId。 | |
| func SchoolSeasonChatEvent(conversationID string) map[string]any { | |
| ev := SchoolChatTimesEvents(conversationID) | |
| ev["activityId"] = schoolOpenDayActivityID | |
| return ev | |
| } | |
| // MiniExpertUseEvent 构造 growth 域 Sequential_Tasks_2「在小程序内选中专家并完成 | |
| // 有效对话」的判据事件:mp 指纹 expert_actual_use。形状对齐小程序源码 | |
| // app-service.js 的真实发射点(上游 task_runner 实测 2026-09-23:上报即 completed, | |
| // claim +200c+5e)。与 school 域的 SchoolExpertUseEvents 是**两套口径**,勿照抄: | |
| // - 不带 conversationId/activityId——真实事件就是这两个字段都不带; | |
| // - extVersion 用小程序自身版本 2.2.8(覆盖 mpEventBase 的 2.4.0); | |
| // - source=mini_program + type 固定 "send_message"(小程序恒发此值)。 | |
| // | |
| // expertID 必须是专家市场真实 ex_ id(ListMarketExperts),空 id 服务端不入账。 | |
| func MiniExpertUseEvent(expertID, expertName, expertType string) map[string]any { | |
| if expertType == "" { | |
| expertType = "agent" | |
| } | |
| if expertName == "" { | |
| expertName = expertID | |
| } | |
| return map[string]any{ | |
| "eventCode": "expert_actual_use", "reportDelay": 0, | |
| "extVersion": "2.2.8", "source": "mini_program", | |
| "id": expertID, "name": expertID, | |
| "expertTitle": expertName, "type": "send_message", | |
| "characterCount": 12, "expertType": expertType, | |
| } | |
| } | |
| // MiniChatModelEvent mp 对话事件 + 模型字段(Sequential_Tasks_5「使用 GLM5.2」判据 | |
| // 载体):小程序 chat_request_send 真实发射点(mpsrc main 32904 模块)带 | |
| // requestModelId / requestModelName——Tasks_1/3 的裸对话事件不带模型,模型任务 | |
| // 须用本形态(判据待解锁实测验证)。 | |
| func MiniChatModelEvent(conversationID, modelID, modelName string) map[string]any { | |
| ev := SchoolChatTimesEvents(conversationID) | |
| ev["requestModelId"] = modelID | |
| ev["requestModelName"] = modelName | |
| return ev | |
| } | |
| // MiniPlaybookEvents mp 指纹灵感事件组(Sequential_Tasks_7「体验灵感功能」判据 | |
| // 载体,形状对齐 mpsrc main 73640/73665 发射点:playbook_cta_click → | |
| // playbook_prompt_send)。issue #42 称该任务为 PC 口径(+500c+5e)——PC 序列 | |
| // (DesktopPlaybookPromptSequence)已实测点亮 playbook_prompt,本组作为 mp 形态 | |
| // 补充(任务在 mp 链上,判据究竟认哪侧待解锁实测)。 | |
| func MiniPlaybookEvents(caseID, caseName string) []map[string]any { | |
| base := map[string]any{ | |
| "id": caseID, "name": caseName, "type": "document", | |
| "categoryId": "", "categoryName": "", | |
| "skills": "", "skillNames": "", | |
| } | |
| cta := map[string]any{ | |
| "eventCode": "playbook_cta_click", "source": "discover", "position": 1, | |
| "extVersion": "2.2.8", | |
| } | |
| for k, v := range base { | |
| cta[k] = v | |
| } | |
| send := map[string]any{ | |
| "eventCode": "playbook_prompt_send", "source": "discover", | |
| "promptLength": 96, "isOfficial": 1, | |
| "conversationId": "wb2api-mp-pb-" + clientToken(), | |
| "extVersion": "2.2.8", | |
| } | |
| for k, v := range base { | |
| send[k] = v | |
| } | |
| return []map[string]any{cta, send} | |
| } | |
| // ---- 我的券码(#/prizes?tab=vouchers,2026-09-16 接入)---- | |
| // SchoolVoucher 开学季抽奖抽中的第三方券(KFC/瑞幸/酷狗等)。 | |
| // 字段结构按真实响应样本:GET /vouchers 单次拉全(无分页),data.items[]。 | |
| type SchoolVoucher struct { | |
| GrantID int64 `json:"grant_id"` | |
| DrawUUID string `json:"draw_uuid,omitempty"` | |
| SKUCode string `json:"sku_code,omitempty"` // kfc_ice_cream / voucher_luckin / voucher_kugou … | |
| PrizeName string `json:"prize_name,omitempty"` // 肯德基冰淇淋 | |
| Code string `json:"code"` // 券码本体(复制给店员核销) | |
| ValidFrom string `json:"valid_from,omitempty"` // 上游常为空 | |
| ValidTo string `json:"valid_to,omitempty"` // "2026-10-24" | |
| GrantedAt string `json:"granted_at,omitempty"` // RFC3339 | |
| } | |
| // SchoolVouchers 查询账号的开学季券码列表(只读)。 | |
| // 抽到积分的记录不在此端点(那是 /rewards 的 type=credit 条目)。 | |
| func (c *Client) SchoolVouchers(a *auth.Auth) ([]SchoolVoucher, error) { | |
| var out struct { | |
| Items []SchoolVoucher `json:"items"` | |
| } | |
| if err := c.schoolJSON(a, http.MethodGet, "/vouchers", nil, &out); err != nil { | |
| return nil, err | |
| } | |
| return out.Items, nil | |
| } | |