magicrew-auth 0.0.0-stage → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/tasks.mjs ADDED
@@ -0,0 +1,119 @@
1
+ // 签到 = 自动做完新手福利
2
+ //
3
+ // ═══════════════════════════════════════════════════════════════════════
4
+ // 结论先说:不用浏览器
5
+ // ═══════════════════════════════════════════════════════════════════════
6
+ //
7
+ // 一开始从主 chunk 里只挖到埋点事件名(`onboarding_guide_step_click`),
8
+ // 所以以为任务要靠前端导览判定。翻到 `onboarding-CZBI0pQt.js` 才发现
9
+ // 真正的接口只有两行:
10
+ //
11
+ // GET /api/v1/onboarding/tasks
12
+ // PUT /api/v1/onboarding/tasks/{code}/completion
13
+ //
14
+ // 8 项任务里 7 项 PUT 一下就完成、积分当场到账。只有 `first_ai_work`
15
+ // 回 9303「该新人引导任务不支持手动完成」—— 那项得真的跑一次 AI 任务。
16
+ //
17
+ // 实测(2026-10-27):1240 → 1540,+300,六项全部当场入账。
18
+ //
19
+ // ═══════════════════════════════════════════════════════════════════════
20
+ //
21
+ // 所以 magpie 的 auth.checkin 钩子做的事:
22
+ // ① 拉任务列表
23
+ // ② 把 PENDING 的挨个 PUT(跳过不支持手动的)
24
+ // ③ 报积分变化
25
+ //
26
+ // 每天跑一次,第一次就把 300 分拿完;之后每次都是 done。
27
+
28
+ import { fetchPoints, fetchOnboardingTasks, completeOnboardingTask } from "./api.mjs"
29
+
30
+ /** 已知不支持手动完成的任务,别再白试 */
31
+ const NOT_MANUALLY_COMPLETABLE = new Set(["first_ai_work"])
32
+
33
+ /** 9303 = 该新人引导任务不支持手动完成 */
34
+ const CODE_NOT_SUPPORTED = 9303
35
+
36
+ /**
37
+ * @param {{ jwt: string, org: string, host: string }} auth
38
+ * host 由调用方的插件闭包给(每个供应商绑定一个站点,不在 auth 里存站点代号)
39
+ */
40
+ export async function runCheckin(auth) {
41
+ const host = auth.host
42
+ const h = { jwt: auth.jwt, org: auth.org, host }
43
+
44
+ let before = null
45
+ try {
46
+ before = (await fetchPoints(h.jwt, h.org, h.host)).total
47
+ } catch (e) {
48
+ return { outcome: "failed", message: `读不到积分:${e?.message ?? e}` }
49
+ }
50
+
51
+ let tasks = []
52
+ try {
53
+ tasks = await fetchOnboardingTasks(h.jwt, h.org, h.host)
54
+ } catch (e) {
55
+ return { outcome: "failed", message: `读不到新手任务:${e?.message ?? e}` }
56
+ }
57
+
58
+ const done = tasks.filter((t) => t.status === "COMPLETED" || t.rewardStatus === "GRANTED")
59
+ const pending = tasks.filter((t) => t.status !== "COMPLETED" && t.rewardStatus !== "GRANTED")
60
+
61
+ if (!pending.length) {
62
+ return {
63
+ outcome: "done",
64
+ message: `新手福利 ${tasks.length} 项已全部完成(当前积分 ${before})`,
65
+ }
66
+ }
67
+
68
+ // 挨个完成
69
+ const claimed = []
70
+ const skipped = []
71
+ const failed = []
72
+
73
+ for (const t of pending) {
74
+ if (NOT_MANUALLY_COMPLETABLE.has(t.code)) {
75
+ skipped.push(`${t.title}(${t.points} 分,要真跑一次 AI 任务)`)
76
+ continue
77
+ }
78
+ try {
79
+ const r = await completeOnboardingTask(h.jwt, h.org, t.code, h.host)
80
+ claimed.push({ code: t.code, title: t.title, points: r.points || t.points })
81
+ } catch (e) {
82
+ if (e?.code === CODE_NOT_SUPPORTED) {
83
+ skipped.push(`${t.title}(${t.points} 分,不支持手动完成)`)
84
+ } else {
85
+ failed.push(`${t.code}: ${e?.message ?? e}`)
86
+ }
87
+ }
88
+ // 服务端限流留点余地
89
+ await new Promise((r) => setTimeout(r, 400))
90
+ }
91
+
92
+ let after = before
93
+ try {
94
+ after = (await fetchPoints(h.jwt, h.org, h.host)).total
95
+ } catch {}
96
+
97
+ const gained = after - before
98
+ const parts = []
99
+ if (gained > 0) parts.push(`积分 +${gained}(${before} → ${after})`)
100
+ else parts.push(`积分 ${after}(本次无变动)`)
101
+ if (claimed.length) parts.push(`完成 ${claimed.length} 项:${claimed.map((c) => c.title).join("、")}`)
102
+ if (skipped.length) parts.push(`需手动:${skipped.join("、")}`)
103
+ if (failed.length) parts.push(`失败 ${failed.length} 项:${failed.join(";")}`)
104
+
105
+ if (claimed.length) {
106
+ return {
107
+ outcome: "claimed",
108
+ credit: gained > 0 ? gained : undefined,
109
+ message: parts.join("。"),
110
+ }
111
+ }
112
+
113
+ return {
114
+ outcome: failed.length ? "failed" : "done",
115
+ message: parts.join("。"),
116
+ }
117
+ }
118
+
119
+ export { NOT_MANUALLY_COMPLETABLE }