diff --git a/README.md b/README.md index bc1c8ae..360d2af 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,25 @@ GET /api/sms/stats ## 当前状态 初始通用仓库骨架已创建。等待 BOSS 上传既有 Autman 插件后,再按实际风格补齐实现。 + +### 起点读书 QDReader 签到插件 + +- 正式源码:`plugins/qdreader/qdreader_sign_autman.js` +- 实机 JS 副本:`js/qdreader_sign_autman.js` +- 定时运行:`cron: 15 7 * * *`,每日 07:15 自动签到 +- 手动运行:`起点签到` / `起点签到 ` +- Cookie 维护:`起点ck ` / `起点ck 查询` / `起点ck 删除 ` + +Raw 导入: + +```text +https://gitea.chickliu.fun/Hermes/autman-plugins/raw/branch/main/plugins/qdreader/qdreader_sign_autman.js +``` + ## 文档 - [Autman 插件开发规则与仓库约定](docs/autman-plugin-rules.md) - [JS 微服务路由插件模板](templates/js-router-plugin-template.js) - [Python middleware 插件模板](templates/python-middleware-plugin-template.py) + diff --git a/js/qdreader_sign_autman.js b/js/qdreader_sign_autman.js new file mode 100644 index 0000000..6f84540 --- /dev/null +++ b/js/qdreader_sign_autman.js @@ -0,0 +1,231 @@ +//[open_source: true] +//[create_at: 2026-05-17 03:00:00] +//[title: 起点读书签到] +//[author: Hermes] +//[service: BOSS] +//[class: 工具类] +//[version: 2.1.0] +//[platform: web,qq,wx,tg,tb] +//[public: false] +//[price: 0] +//[disable: false] +//[description: 起点读书(QDReader) Autman 签到插件:保存 QX 抓到的 H5 Cookie,支持手动“起点签到”和每日 07:15 定时签到。签名沿用上游 QDReader 的 api.120399.xyz 签名网关。] +//[rule: ^起点(ck|CK|cookie|Cookie|签到|读书)(.*)$] +//[rule: ^qdreader(.*)$] +//[admin: true] +//[priority: 600] +//[cron: 15 7 * * *] +//[param: {"required":false,"key":"otto.qdreader_cookie_json","bool":false,"placeholder":"{\"uid\":\"cookie\"}","name":"QDReader Cookie JSON","desc":"机器人交互维护,JSON Map 格式"}] +//[param: {"required":false,"key":"otto.qdreader_sign_api","bool":false,"placeholder":"https://api.120399.xyz","name":"QDReader 签名网关","desc":"默认 https://api.120399.xyz"}] + +var STORE_KEY = "qdreader_cookie_json" +var SIGN_API_KEY = "qdreader_sign_api" +var DEFAULT_SIGN_API = "https://api.120399.xyz" +var QIDIAN_BASE = "https://h5.if.qidian.com" + +function reply(msg) { + var out = typeof msg === "string" ? msg : JSON.stringify(msg) + if (typeof sender !== "undefined" && sender.reply) sender.reply(out) + else if (typeof sendText === "function") sendText(out) + else if (typeof push === "function") push({ content: out }) + else console.log(out) +} +function safeJsonParse(s, def) { try { return JSON.parse(s) } catch (e) { return def } } +function getStore() { + var raw = get(STORE_KEY) || "{}" + var obj = safeJsonParse(raw, {}) + if (!obj || typeof obj !== "object" || Array.isArray(obj)) return {} + return obj +} +function setStore(obj) { var s = JSON.stringify(obj); set(STORE_KEY, s); return s } +function parseCookieString(cookie) { + var jar = {} + String(cookie || "").split(";").forEach(function (part) { + var i = part.indexOf("=") + if (i <= 0) return + var k = part.slice(0, i).trim() + var v = part.slice(i + 1).trim() + if (k) jar[k] = v + }) + return jar +} +function cookieToString(jar) { + var arr = [] + for (var k in jar) if (jar[k] !== undefined && jar[k] !== null && jar[k] !== "") arr.push(k + "=" + jar[k]) + return arr.join("; ") +} +function b64decode(s) { + s = String(s || "") + try { if (typeof Buffer !== "undefined") return Buffer.from(s, "base64").toString("utf8") } catch (e) {} + try { return decodeURIComponent(escape(atob(s))) } catch (e) { return "" } +} +function uidFromQDHeader(qdheader) { var decoded = b64decode(qdheader); return decoded ? String(decoded.split("|")[0] || "").trim() : "" } +function extractUid(cookie) { + var jar = parseCookieString(cookie) + var uid = uidFromQDHeader(jar.QDHeader || jar.qdheader || jar.QDHEADER || "") + if (uid) return uid + if (jar.uid) return String(jar.uid) + return "" +} +function maskCookie(cookie) { cookie = String(cookie || ""); return cookie.length <= 24 ? cookie : cookie.slice(0, 12) + "..." + cookie.slice(-8) } +function contentText() { + var s = "" + try { if (typeof getContent === "function") s = getContent() || "" } catch (e) {} + try { if (!s && typeof GetContent === "function") s = GetContent() || "" } catch (e) {} + try { if (!s && typeof sender !== "undefined" && sender.getContent) s = sender.getContent() || "" } catch (e) {} + try { if (!s && typeof sender !== "undefined" && sender.getMessage) s = sender.getMessage() || "" } catch (e) {} + try { if (!s && typeof message !== "undefined") s = String(message) } catch (e) {} + return String(s || "").trim() +} +function usage() { + return [ + "起点读书签到插件", + "提交CK:起点ck ", + "签到全部:起点签到", + "签到单号:起点签到 ", + "查询CK:起点ck 查询", + "导出CK:起点ck 导出", + "删除CK:起点ck 删除 ", + "说明:QX只负责抓cookie;签到由本 Autman 插件执行。" + ].join("\n") +} +function stripCommand(text) { + var s = String(text || "") + if (/^起点(?:签到|读书)(?:\s|$)/i.test(s)) return s.replace(/^起点(?:签到|读书)\s*/i, "").trim() + if (/^起点(?:ck|CK|cookie|Cookie)(?:\s|$)/i.test(s)) return s.replace(/^起点(?:ck|CK|cookie|Cookie)\s*/i, "").trim() + return s.replace(/^qdreader\s*/i, "").trim() +} +function commandName(text) { + if (/^起点(?:签到|读书)(?:\s|$)/i.test(text)) return "sign" + if (/^qdreader\s+sign(?:\s|$)/i.test(text)) return "sign" + return "ck" +} +function qs(obj) { + var parts = [] + for (var k in (obj || {})) { + var v = obj[k] + if (v === undefined || v === null) continue + if (Array.isArray(v)) { for (var i = 0; i < v.length; i++) parts.push(encodeURIComponent(k + "[" + i + "]") + "=" + encodeURIComponent(String(v[i]))) } + else if (typeof v === "object") { for (var sk in v) parts.push(encodeURIComponent(k + "[" + sk + "]") + "=" + encodeURIComponent(String(v[sk]))) } + else parts.push(encodeURIComponent(k) + "=" + encodeURIComponent(String(v))) + } + return parts.join("&") +} +function normalizeCookie(cookie) { + var jar = parseCookieString(cookie) + var session = jar.sessionyw || jar.session || jar.sessionKey || "" + if (session) jar.sessionyw = session + var appid = jar.appId || jar.appid || "" + if (appid) jar.appId = appid + return { jar: jar, cookie: cookieToString(jar), qdh: jar.QDHeader || jar.qdh || "", qdheader: jar.QDHeader || jar.qdheader || "", uid: extractUid(cookie), guid: jar.gwguid || jar.guid || "", qimei: jar.qimei || "", qimei36: jar.qimei36 || jar.QIMEI36 || "" } +} +function httpReq(opt) { + if (typeof request === "function") return request(opt) + if (typeof $request === "function") return $request(opt) + throw new Error("当前 Autman 环境没有 request/$request 方法,无法发起签到请求") +} +function parseResp(res) { + var body = res && (res.body !== undefined ? res.body : (res.data !== undefined ? res.data : res)) + if (typeof body === "string") return safeJsonParse(body, body) + return body +} +function postJson(url, data) { + return parseResp(httpReq({ url: url, method: "post", dataType: "json", timeOut: 10000, headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) })) +} +function qidianRequest(path, method, data, cookie) { + method = String(method || "GET").toUpperCase() + var info = normalizeCookie(cookie) + var signApi = (get(SIGN_API_KEY) || DEFAULT_SIGN_API).replace(/\/$/, "") + var payload = { qdh: info.qdh, qdheader: info.qdheader, url: QIDIAN_BASE + path, method: method, data: data || {}, uid: info.uid, guid: info.guid, qimei: info.qimei, qimei36: info.qimei36 } + var signed = postJson(signApi + "/sign", payload) + if (typeof signed === "string") signed = safeJsonParse(signed, {}) + if (!signed || (signed.code !== 200 && signed.code !== "200" && signed.code !== 0 && signed.code !== "0") || !signed.data) throw new Error("签名失败:" + JSON.stringify(signed)) + var headers = signed.data.headers || signed.data + headers.Cookie = info.cookie + if (!headers["User-Agent"]) headers["User-Agent"] = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/QDReaderiOS/5.9.456/728/QDReaderAppstore" + var req = { url: QIDIAN_BASE + path, method: method.toLowerCase(), dataType: "json", timeOut: 10000, headers: headers } + if (method === "GET") { + var query = qs(data || {}) + if (query) req.url += (req.url.indexOf("?") >= 0 ? "&" : "?") + query + } else { + req.headers["Content-Type"] = req.headers["Content-Type"] || "application/x-www-form-urlencoded" + req.body = qs(data || {}) + } + var out = parseResp(httpReq(req)) + if (typeof out === "string") out = safeJsonParse(out, out) + return out +} +function resultOk(obj) { + var code = obj && (obj.Result !== undefined ? obj.Result : obj.Code !== undefined ? obj.Code : obj.code) + return code === undefined || code === 0 || code === "0" || code === 200 || code === "200" +} +function resultMsg(obj) { return (obj && (obj.Message || obj.Msg || obj.message || obj.msg)) || "" } +function doCheckin(uid, cookie) { + var res = qidianRequest("/argus/api/v3/checkin/checkin", "POST", {}, cookie) + var ok = resultOk(res) + var msg = resultMsg(res) || (ok ? "签到请求成功" : "签到失败") + return { uid: uid, ok: ok, msg: msg, raw: res } +} +function signAll(targetUid) { + var store = getStore() + var uids = Object.keys(store) + if (targetUid) uids = uids.filter(function (u) { return u === targetUid }) + if (!uids.length) return targetUid ? ("未找到 uid: " + targetUid) : "还没有保存起点读书CK" + var lines = ["起点读书签到结果:"] + for (var i = 0; i < uids.length; i++) { + var uid = uids[i] + try { + var r = doCheckin(uid, store[uid]) + lines.push((r.ok ? "✅ " : "❌ ") + uid + ":" + r.msg) + } catch (e) { + lines.push("❌ " + uid + ":" + (e && e.message ? e.message : String(e))) + } + } + return lines.join("\n") +} + +function main() { + var text = contentText() + var isCronRun = !text + var cmd = isCronRun ? "sign" : commandName(text) + var arg = isCronRun ? "" : stripCommand(text).replace(/^sign\s*/i, "").trim() + if (!arg && cmd !== "sign") { reply(usage()); return } + if (/^(帮助|help|h)$/i.test(arg)) { reply(usage()); return } + + var store = getStore() + + if (cmd === "sign" || /^(签到|sign|run)(?:\s+|$)/i.test(arg)) { + var target = arg.replace(/^(签到|sign|run)\s*/i, "").trim() + reply(signAll(target)) + return + } + + if (/^(查询|状态|status|list)$/i.test(arg)) { + var uids = Object.keys(store) + reply(uids.length ? ("起点读书CK:共 " + uids.length + " 个账号\n" + uids.join("\n") + "\n发送 起点签到 可执行签到") : "还没有保存起点读书CK") + return + } + if (/^(导出|export)$/i.test(arg)) { reply("QDREADER_COOKIE=" + JSON.stringify(store)); return } + var del = arg.match(/^(删除|delete|del|rm)\s+(\S+)/i) + if (del) { var duid = del[2]; var existed = !!store[duid]; delete store[duid]; setStore(store); reply(existed ? ("已删除 uid: " + duid) : ("未找到 uid: " + duid)); return } + var jsonMatch = arg.match(/^json\s+([\s\S]+)$/i) + if (jsonMatch) { + var obj = safeJsonParse(jsonMatch[1], null) + if (!obj || typeof obj !== "object" || Array.isArray(obj)) { reply("JSON格式错误,示例:起点ck json {\"uid\":\"cookie\"}"); return } + var count = 0 + for (var uid in obj) { if (!obj[uid]) continue; store[uid] = String(obj[uid]); count++ } + setStore(store); reply("起点读书CK JSON已导入/更新 " + count + " 个账号,当前共 " + Object.keys(store).length + " 个账号\n发送 起点签到 可执行签到"); return + } + var cookie = arg + if (cookie.indexOf("=") < 0 || cookie.indexOf(";") < 0) { reply("未识别到 Cookie。请发送:起点ck "); return } + var uid = extractUid(cookie) + if (!uid) { reply("无法从 Cookie 识别 uid。请确认 QX 抓的是起点读书福利中心 getlogininfo 请求,Cookie 内应包含 QDHeader 或 uid。"); return } + var isNew = !store[uid] + var changed = store[uid] !== cookie + store[uid] = cookie + setStore(store) + reply(["起点读书CK" + (isNew ? "新增" : (changed ? "更新" : "未变化")) + "成功", "uid: " + uid, "账号数: " + Object.keys(store).length, "cookie: " + maskCookie(cookie), "签到:发送 起点签到"].join("\n")) + +} +main() + diff --git a/plugins/qdreader/README.md b/plugins/qdreader/README.md new file mode 100644 index 0000000..7519305 --- /dev/null +++ b/plugins/qdreader/README.md @@ -0,0 +1,43 @@ +# 起点读书签到 Autman 插件 + +正式插件文件: + +```text +plugins/qdreader/qdreader_sign_autman.js +``` + +实机 JS 导入副本: + +```text +js/qdreader_sign_autman.js +``` + +Raw 链接: + +```text +https://gitea.chickliu.fun/Hermes/autman-plugins/raw/branch/main/plugins/qdreader/qdreader_sign_autman.js +https://gitea.chickliu.fun/Hermes/autman-plugins/raw/branch/main/js/qdreader_sign_autman.js +``` + +## 功能 + +- QX 抓到 Cookie 后,把 `起点ck ` 发给 Autman 保存。 +- 手动执行:`起点签到`。 +- 手动单账号:`起点签到 `。 +- 定时执行:头注 `//[cron: 15 7 * * *]`,每日 07:15 自动签到。 +- 查询/维护:`起点ck 查询`、`起点ck 导出`、`起点ck 删除 `。 + +## 配置 + +Autman 参数: + +```text +otto.qdreader_cookie_json = {"uid":"cookie"} +otto.qdreader_sign_api = https://api.120399.xyz +``` + +签名与签到流程沿用上游 QDReader: + +1. 从 Cookie 解析 `QDHeader/qdh/uid/guid/qimei/qimei36`。 +2. `POST https://api.120399.xyz/sign` 获取签名头。 +3. `POST https://h5.if.qidian.com/argus/api/v3/checkin/checkin` 完成签到。 diff --git a/plugins/qdreader/qdreader_sign_autman.js b/plugins/qdreader/qdreader_sign_autman.js new file mode 100644 index 0000000..6f84540 --- /dev/null +++ b/plugins/qdreader/qdreader_sign_autman.js @@ -0,0 +1,231 @@ +//[open_source: true] +//[create_at: 2026-05-17 03:00:00] +//[title: 起点读书签到] +//[author: Hermes] +//[service: BOSS] +//[class: 工具类] +//[version: 2.1.0] +//[platform: web,qq,wx,tg,tb] +//[public: false] +//[price: 0] +//[disable: false] +//[description: 起点读书(QDReader) Autman 签到插件:保存 QX 抓到的 H5 Cookie,支持手动“起点签到”和每日 07:15 定时签到。签名沿用上游 QDReader 的 api.120399.xyz 签名网关。] +//[rule: ^起点(ck|CK|cookie|Cookie|签到|读书)(.*)$] +//[rule: ^qdreader(.*)$] +//[admin: true] +//[priority: 600] +//[cron: 15 7 * * *] +//[param: {"required":false,"key":"otto.qdreader_cookie_json","bool":false,"placeholder":"{\"uid\":\"cookie\"}","name":"QDReader Cookie JSON","desc":"机器人交互维护,JSON Map 格式"}] +//[param: {"required":false,"key":"otto.qdreader_sign_api","bool":false,"placeholder":"https://api.120399.xyz","name":"QDReader 签名网关","desc":"默认 https://api.120399.xyz"}] + +var STORE_KEY = "qdreader_cookie_json" +var SIGN_API_KEY = "qdreader_sign_api" +var DEFAULT_SIGN_API = "https://api.120399.xyz" +var QIDIAN_BASE = "https://h5.if.qidian.com" + +function reply(msg) { + var out = typeof msg === "string" ? msg : JSON.stringify(msg) + if (typeof sender !== "undefined" && sender.reply) sender.reply(out) + else if (typeof sendText === "function") sendText(out) + else if (typeof push === "function") push({ content: out }) + else console.log(out) +} +function safeJsonParse(s, def) { try { return JSON.parse(s) } catch (e) { return def } } +function getStore() { + var raw = get(STORE_KEY) || "{}" + var obj = safeJsonParse(raw, {}) + if (!obj || typeof obj !== "object" || Array.isArray(obj)) return {} + return obj +} +function setStore(obj) { var s = JSON.stringify(obj); set(STORE_KEY, s); return s } +function parseCookieString(cookie) { + var jar = {} + String(cookie || "").split(";").forEach(function (part) { + var i = part.indexOf("=") + if (i <= 0) return + var k = part.slice(0, i).trim() + var v = part.slice(i + 1).trim() + if (k) jar[k] = v + }) + return jar +} +function cookieToString(jar) { + var arr = [] + for (var k in jar) if (jar[k] !== undefined && jar[k] !== null && jar[k] !== "") arr.push(k + "=" + jar[k]) + return arr.join("; ") +} +function b64decode(s) { + s = String(s || "") + try { if (typeof Buffer !== "undefined") return Buffer.from(s, "base64").toString("utf8") } catch (e) {} + try { return decodeURIComponent(escape(atob(s))) } catch (e) { return "" } +} +function uidFromQDHeader(qdheader) { var decoded = b64decode(qdheader); return decoded ? String(decoded.split("|")[0] || "").trim() : "" } +function extractUid(cookie) { + var jar = parseCookieString(cookie) + var uid = uidFromQDHeader(jar.QDHeader || jar.qdheader || jar.QDHEADER || "") + if (uid) return uid + if (jar.uid) return String(jar.uid) + return "" +} +function maskCookie(cookie) { cookie = String(cookie || ""); return cookie.length <= 24 ? cookie : cookie.slice(0, 12) + "..." + cookie.slice(-8) } +function contentText() { + var s = "" + try { if (typeof getContent === "function") s = getContent() || "" } catch (e) {} + try { if (!s && typeof GetContent === "function") s = GetContent() || "" } catch (e) {} + try { if (!s && typeof sender !== "undefined" && sender.getContent) s = sender.getContent() || "" } catch (e) {} + try { if (!s && typeof sender !== "undefined" && sender.getMessage) s = sender.getMessage() || "" } catch (e) {} + try { if (!s && typeof message !== "undefined") s = String(message) } catch (e) {} + return String(s || "").trim() +} +function usage() { + return [ + "起点读书签到插件", + "提交CK:起点ck ", + "签到全部:起点签到", + "签到单号:起点签到 ", + "查询CK:起点ck 查询", + "导出CK:起点ck 导出", + "删除CK:起点ck 删除 ", + "说明:QX只负责抓cookie;签到由本 Autman 插件执行。" + ].join("\n") +} +function stripCommand(text) { + var s = String(text || "") + if (/^起点(?:签到|读书)(?:\s|$)/i.test(s)) return s.replace(/^起点(?:签到|读书)\s*/i, "").trim() + if (/^起点(?:ck|CK|cookie|Cookie)(?:\s|$)/i.test(s)) return s.replace(/^起点(?:ck|CK|cookie|Cookie)\s*/i, "").trim() + return s.replace(/^qdreader\s*/i, "").trim() +} +function commandName(text) { + if (/^起点(?:签到|读书)(?:\s|$)/i.test(text)) return "sign" + if (/^qdreader\s+sign(?:\s|$)/i.test(text)) return "sign" + return "ck" +} +function qs(obj) { + var parts = [] + for (var k in (obj || {})) { + var v = obj[k] + if (v === undefined || v === null) continue + if (Array.isArray(v)) { for (var i = 0; i < v.length; i++) parts.push(encodeURIComponent(k + "[" + i + "]") + "=" + encodeURIComponent(String(v[i]))) } + else if (typeof v === "object") { for (var sk in v) parts.push(encodeURIComponent(k + "[" + sk + "]") + "=" + encodeURIComponent(String(v[sk]))) } + else parts.push(encodeURIComponent(k) + "=" + encodeURIComponent(String(v))) + } + return parts.join("&") +} +function normalizeCookie(cookie) { + var jar = parseCookieString(cookie) + var session = jar.sessionyw || jar.session || jar.sessionKey || "" + if (session) jar.sessionyw = session + var appid = jar.appId || jar.appid || "" + if (appid) jar.appId = appid + return { jar: jar, cookie: cookieToString(jar), qdh: jar.QDHeader || jar.qdh || "", qdheader: jar.QDHeader || jar.qdheader || "", uid: extractUid(cookie), guid: jar.gwguid || jar.guid || "", qimei: jar.qimei || "", qimei36: jar.qimei36 || jar.QIMEI36 || "" } +} +function httpReq(opt) { + if (typeof request === "function") return request(opt) + if (typeof $request === "function") return $request(opt) + throw new Error("当前 Autman 环境没有 request/$request 方法,无法发起签到请求") +} +function parseResp(res) { + var body = res && (res.body !== undefined ? res.body : (res.data !== undefined ? res.data : res)) + if (typeof body === "string") return safeJsonParse(body, body) + return body +} +function postJson(url, data) { + return parseResp(httpReq({ url: url, method: "post", dataType: "json", timeOut: 10000, headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) })) +} +function qidianRequest(path, method, data, cookie) { + method = String(method || "GET").toUpperCase() + var info = normalizeCookie(cookie) + var signApi = (get(SIGN_API_KEY) || DEFAULT_SIGN_API).replace(/\/$/, "") + var payload = { qdh: info.qdh, qdheader: info.qdheader, url: QIDIAN_BASE + path, method: method, data: data || {}, uid: info.uid, guid: info.guid, qimei: info.qimei, qimei36: info.qimei36 } + var signed = postJson(signApi + "/sign", payload) + if (typeof signed === "string") signed = safeJsonParse(signed, {}) + if (!signed || (signed.code !== 200 && signed.code !== "200" && signed.code !== 0 && signed.code !== "0") || !signed.data) throw new Error("签名失败:" + JSON.stringify(signed)) + var headers = signed.data.headers || signed.data + headers.Cookie = info.cookie + if (!headers["User-Agent"]) headers["User-Agent"] = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/QDReaderiOS/5.9.456/728/QDReaderAppstore" + var req = { url: QIDIAN_BASE + path, method: method.toLowerCase(), dataType: "json", timeOut: 10000, headers: headers } + if (method === "GET") { + var query = qs(data || {}) + if (query) req.url += (req.url.indexOf("?") >= 0 ? "&" : "?") + query + } else { + req.headers["Content-Type"] = req.headers["Content-Type"] || "application/x-www-form-urlencoded" + req.body = qs(data || {}) + } + var out = parseResp(httpReq(req)) + if (typeof out === "string") out = safeJsonParse(out, out) + return out +} +function resultOk(obj) { + var code = obj && (obj.Result !== undefined ? obj.Result : obj.Code !== undefined ? obj.Code : obj.code) + return code === undefined || code === 0 || code === "0" || code === 200 || code === "200" +} +function resultMsg(obj) { return (obj && (obj.Message || obj.Msg || obj.message || obj.msg)) || "" } +function doCheckin(uid, cookie) { + var res = qidianRequest("/argus/api/v3/checkin/checkin", "POST", {}, cookie) + var ok = resultOk(res) + var msg = resultMsg(res) || (ok ? "签到请求成功" : "签到失败") + return { uid: uid, ok: ok, msg: msg, raw: res } +} +function signAll(targetUid) { + var store = getStore() + var uids = Object.keys(store) + if (targetUid) uids = uids.filter(function (u) { return u === targetUid }) + if (!uids.length) return targetUid ? ("未找到 uid: " + targetUid) : "还没有保存起点读书CK" + var lines = ["起点读书签到结果:"] + for (var i = 0; i < uids.length; i++) { + var uid = uids[i] + try { + var r = doCheckin(uid, store[uid]) + lines.push((r.ok ? "✅ " : "❌ ") + uid + ":" + r.msg) + } catch (e) { + lines.push("❌ " + uid + ":" + (e && e.message ? e.message : String(e))) + } + } + return lines.join("\n") +} + +function main() { + var text = contentText() + var isCronRun = !text + var cmd = isCronRun ? "sign" : commandName(text) + var arg = isCronRun ? "" : stripCommand(text).replace(/^sign\s*/i, "").trim() + if (!arg && cmd !== "sign") { reply(usage()); return } + if (/^(帮助|help|h)$/i.test(arg)) { reply(usage()); return } + + var store = getStore() + + if (cmd === "sign" || /^(签到|sign|run)(?:\s+|$)/i.test(arg)) { + var target = arg.replace(/^(签到|sign|run)\s*/i, "").trim() + reply(signAll(target)) + return + } + + if (/^(查询|状态|status|list)$/i.test(arg)) { + var uids = Object.keys(store) + reply(uids.length ? ("起点读书CK:共 " + uids.length + " 个账号\n" + uids.join("\n") + "\n发送 起点签到 可执行签到") : "还没有保存起点读书CK") + return + } + if (/^(导出|export)$/i.test(arg)) { reply("QDREADER_COOKIE=" + JSON.stringify(store)); return } + var del = arg.match(/^(删除|delete|del|rm)\s+(\S+)/i) + if (del) { var duid = del[2]; var existed = !!store[duid]; delete store[duid]; setStore(store); reply(existed ? ("已删除 uid: " + duid) : ("未找到 uid: " + duid)); return } + var jsonMatch = arg.match(/^json\s+([\s\S]+)$/i) + if (jsonMatch) { + var obj = safeJsonParse(jsonMatch[1], null) + if (!obj || typeof obj !== "object" || Array.isArray(obj)) { reply("JSON格式错误,示例:起点ck json {\"uid\":\"cookie\"}"); return } + var count = 0 + for (var uid in obj) { if (!obj[uid]) continue; store[uid] = String(obj[uid]); count++ } + setStore(store); reply("起点读书CK JSON已导入/更新 " + count + " 个账号,当前共 " + Object.keys(store).length + " 个账号\n发送 起点签到 可执行签到"); return + } + var cookie = arg + if (cookie.indexOf("=") < 0 || cookie.indexOf(";") < 0) { reply("未识别到 Cookie。请发送:起点ck "); return } + var uid = extractUid(cookie) + if (!uid) { reply("无法从 Cookie 识别 uid。请确认 QX 抓的是起点读书福利中心 getlogininfo 请求,Cookie 内应包含 QDHeader 或 uid。"); return } + var isNew = !store[uid] + var changed = store[uid] !== cookie + store[uid] = cookie + setStore(store) + reply(["起点读书CK" + (isNew ? "新增" : (changed ? "更新" : "未变化")) + "成功", "uid: " + uid, "账号数: " + Object.keys(store).length, "cookie: " + maskCookie(cookie), "签到:发送 起点签到"].join("\n")) + +} +main() +