Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog/unreleased/qoder-cn-daily-checkin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### English

- Fix Qoder CN daily check-in by sending the machine identity headers required by the campaign service.

### 中文

- 修复 Qoder 国内版每日签到,补齐活动服务要求的机器身份请求头。
18 changes: 18 additions & 0 deletions worker/src/checkin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@ function rewardFrom(campaign) {
return benefit.amount;
}

async function machineHeaders(auth) {
let machineId = auth.machineId;
if (typeof auth.getMachineId === "function") {
try {
machineId = await auth.getMachineId();
} catch {
machineId = "";
}
}
if (typeof machineId !== "string" || !machineId) return {};
return {
"Cosy-MachineId": machineId,
"Cosy-MachineToken": machineId,
};
}

export function createQoderCheckin({ region, getAuthManager, fetchImpl = (...args) => globalThis.fetch(...args) }) {
let pending;

Expand All @@ -85,6 +101,7 @@ export function createQoderCheckin({ region, getAuthManager, fetchImpl = (...arg
} catch {
throw new Error("qoder_checkin_auth_refresh_failed");
}
const machineHeadersValue = await machineHeaders(auth);

async function request(path, method, refreshed = false) {
const user = auth.getUserInfo();
Expand All @@ -101,6 +118,7 @@ export function createQoderCheckin({ region, getAuthManager, fetchImpl = (...arg
"User-Agent": "Qoder",
"Cosy-ClientType": "10",
"Cosy-Version": "0.3.4",
...machineHeadersValue,
Origin: endpoint.origin,
Referer: `${endpoint.base}/growth-page/activity-iframe`,
},
Expand Down
12 changes: 12 additions & 0 deletions worker/test/checkin.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ function fixture(responses, options = {}) {
getUserInfo: () => ({ security_oauth_token: refreshed ? "new-test-token" : "test-token" }),
refreshTokenIfNeeded: async () => {},
forceRefreshToken: async () => { refreshed++; },
getMachineId: async () => options.machineId,
};
const checkin = createQoderCheckin({
region: "cn",
Expand Down Expand Up @@ -66,12 +67,23 @@ test("CN check-in lists campaigns then claims the CLAIM_BENEFIT campaign", async
assert.equal(calls[1].init.headers["User-Agent"], "Qoder");
assert.equal(calls[1].init.headers["Cosy-ClientType"], "10");
assert.equal(calls[1].init.headers["Cosy-Version"], "0.3.4");
assert.equal(calls[1].init.headers["Cosy-MachineId"], undefined);
assert.equal(calls[1].init.headers["Cosy-MachineToken"], undefined);
assert.equal(calls[1].init.headers.Referer, "https://openapi.qoder.com.cn/growth-page/activity-iframe");
assert.equal(calls[1].init.redirect, "manual");
assert.ok(calls[1].init.signal instanceof AbortSignal);
assert.ok(!calls.some((call) => String(call.url).includes("daily-check-in")));
});

test("CN check-in sends the machine identity headers used by the desktop client", async () => {
const { checkin, calls } = fixture([json(listed(credit())), json({ status: "CLAIMED" })], { machineId: "machine-test-id" });
await checkin();
assert.equal(calls[0].init.headers["Cosy-MachineId"], "machine-test-id");
assert.equal(calls[0].init.headers["Cosy-MachineToken"], "machine-test-id");
assert.equal(calls[1].init.headers["Cosy-MachineId"], "machine-test-id");
assert.equal(calls[1].init.headers["Cosy-MachineToken"], "machine-test-id");
});

test("VIEW_DETAILS campaigns are never claimed", async () => {
const { checkin, calls } = fixture([json(listed(details()))]);
assert.equal((await checkin()).status, "skipped");
Expand Down
Loading