magpie-plugin-loomy-auth 0.1.8 → 0.1.9

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/README.md CHANGED
@@ -99,8 +99,9 @@ Loomy 的邀请奖励:**邀请好友双方各得 5000 积分**(实测,附
99
99
  你只要照常把新账号登进 magpie,下一次签到时会自动:查本机其它账号的邀请码 →
100
100
  挑一个 → 兑换。新号 +5000,老号 +5000。
101
101
 
102
- ⚠ **不支持互相邀请。** 本号的邀请码一旦被别人用过,就不再兑换别人的码 ——
103
- 否则 A 邀请 B、B 再邀请 A,两个号各拿两笔,那是刷量。单向成链没问题。
102
+ 每条邀请码能邀请 1000 人;**每个号只能被邀请一次**,所以「A 邀 B、B 再邀 A」
103
+ 平台是允许的(实测过,服务端不拦)。插件不干预这个 —— 它只守平台自己的那条:
104
+ 本号已经被邀请过就不再兑换。
104
105
 
105
106
  ---
106
107
 
package/docs/internals.md CHANGED
@@ -175,21 +175,33 @@ E4DPT6 usedCount 0 → 1 181 activation false → true
175
175
 
176
176
  ②③ 失败都不影响签到结果 —— 它们是顺带做的事,不是前置条件。
177
177
 
178
- #### ⚠ 互刷防护(实测踩到过)
178
+ #### 判据只有一条:本号是否已经被邀请过
179
179
 
180
- 第一次实现只排除了「自己的码」,于是发生了:
180
+ 平台规则:**每个账号仅能兑换一次**(客户端文案「你已经兑换过积分啦」)。
181
+ 所以判断依据就是 `GET /points/activation` 的 `activated`,跟「谁邀请了谁」无关。
182
+
183
+ **为什么不需要知道对方是谁**:`GET /api/v1/invitation-codes` 只回
184
+ `{inviteCode, usedCount, maxUses, status}` —— 有数量、没有名单。
185
+ 客户端里也没有「被邀请的好友」列表接口;探测 `/invitation-codes/invitees`、
186
+ `/invitation-records`、`/points/invitation-records` 全部 404。
187
+ 既然拿不到,就不该假装能判断。
188
+
189
+ #### 平台**允许**互刷(实测,不是推测)
181
190
 
182
191
  ```
183
- 189 兑换了 181 的 FDW3CU → +5000(填写邀请码奖励)
184
- 181 兑换了 189 的 E4DPT6 → +5000(填写邀请码奖励)
185
- 两个号又各自拿到 邀请好友奖励 +5000
186
- ⇒ 各得 10000,等于两笔邀请的全部收益
192
+ 189 兑换 181 的 FDW3CU → +5000 填写邀请码奖励
193
+ 181 兑换 189 的 E4DPT6 → +5000 填写邀请码奖励
194
+ 两边各再收 +5000 邀请好友奖励
195
+ ⇒ 各得 10000,服务端全程 200,没有任何拦截
187
196
  ```
188
197
 
189
- **修法**:本号自己的码 `usedCount > 0`(说明已有人邀请过本号这边的关系)时,
190
- **不再兑换别人的码**。互刷必然要求双方各用一次,所以这条即可堵住。
191
- 单向成链仍然允许 —— 实测 181 兑换了 189 的码之后,它自己的 `FDW3CU` 仍是
192
- `active` / `usedCount 0`,可以继续邀请第三个人。
198
+ **这是平台的规则漏洞,不是插件的责任。** 本插件只做两件合规的事:
199
+
200
+ 1. 本号 `activated: true` 时不再兑换(平台规则);
201
+ 2. 把本号自己的码从候选里排除(不能自己邀请自己)。
202
+
203
+ ⚠ 曾经加过一条「本号码被用过就不再兑换别人的码」的规则,**已移除** —— 它拦不住互刷
204
+ (先兑换的一方 `usedCount` 还是 0),却会误伤 A→B→C 这种正常长链里的 B。
193
205
 
194
206
  #### 候选从哪来
195
207
 
package/index.mjs CHANGED
@@ -179,17 +179,23 @@ export const LoomyAuthPlugin = async ({ client, directory } = {}) => {
179
179
  * 平台侧可能视作互刷。
180
180
  */
181
181
  const pickInviteFor = async (session) => {
182
- // 已兑换过就到此为止(每账号一次)
182
+ // ① **判据只有一条:本号是否已经被邀请过。**
183
+ //
184
+ // 平台规则就这一条(客户端文案:「每个账号仅能兑换一次」「你已经兑换过积分啦」)。
185
+ // 所以不需要知道「谁邀请了谁」—— 那信息接口根本不提供
186
+ // (`invitation-codes` 只回 `usedCount` 这个数量,不说是谁)。
187
+ // `appliedInvitationCode` 只用于日志,不参与判断。
183
188
  let applied
184
189
  try {
185
190
  const st = await fetchActivation(session)
186
191
  if (st.activated) return undefined
187
192
  applied = st.appliedInvitationCode
188
193
  } catch {
194
+ // 读不到状态就别乱兑换 —— 宁可这一天不换
189
195
  return undefined
190
196
  }
191
197
 
192
- // 本号自己的码(要从候选里排除)
198
+ // ② 本号自己的码要从候选里排除(不能自己邀请自己)
193
199
  let selfCode
194
200
  try {
195
201
  const mine = await fetchInviteCodes(session)
@@ -198,35 +204,11 @@ export const LoomyAuthPlugin = async ({ client, directory } = {}) => {
198
204
  selfCode = undefined
199
205
  }
200
206
 
201
- // ⚠⚠ **互相邀请**要去掉:如果本号的码已经被别人用过(`usedCount > 0`),
202
- // 那用它的那个人反过来又能拿本号的码 —— 两个号互相邀请、各拿两笔
203
- // 5000,就是刷。实测踩到过:189 与 181 互相兑换,各得 10000。
204
- //
205
- // 判据不靠猜:本机每张码的 `usedCount` 就是「被几个人用过」。
206
- // 只要本号自己的码 `usedCount > 0`,说明有人邀请过本号这边的关系,
207
- // 此时**已经兑换过就别再换**(上面 `activated` 已拦住),
208
- // 而本号自己的码继续留给**新来的号**用 —— 单向,不成环。
209
- //
210
- // 更严格的一层:把「已经用掉本号码的那些账号的码」也排除。
211
- // 但 `usedCount` 只给数量、不给是谁,所以能做到的就是:
212
- // **本号码一旦被用过,就再也不用别人的码**(互刷必然要求双方各用一次)。
213
- let selfUsed = 0
214
- try {
215
- const mine = await fetchInviteCodes(session)
216
- selfUsed = mine[0]?.usedCount ?? 0
217
- } catch {
218
- selfUsed = 0
219
- }
220
- if (selfUsed > 0) {
221
- log(`本号的邀请码已被 ${selfUsed} 人使用,不再兑换别人的码(避免互刷)`)
222
- return undefined
223
- }
224
-
207
+ // ③ 从本机**别的账号**的码里挑一个还有余量的
225
208
  const candidates = await collectInviteCandidates(session)
226
209
  const picked = _pure.pickInviteCode(candidates, selfCode)
227
210
  if (!picked) return undefined
228
- // 再排一层:**本号已经用过的那个码**不能重复用(服务端也会拒,但别白发请求)
229
- if (applied && picked.code === applied) return undefined
211
+ if (applied && picked.code === applied) return undefined // 理论上不可达,防御性保留
230
212
  return picked.code
231
213
  }
232
214
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magpie-plugin-loomy-auth",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Loomy (iFlytek) for magpie: desktop-session import / SMS / session sign-in, live model catalog with reasoning efforts, chat with tools and images, and the two credit pools reported as the host routes them — the daily pool counts, the permanent one is shown aside.",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",