Files
autman-plugins/plugins/qdreader/qdreader_sign_autman.js
T

1353 lines
70 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//[open_source: true]
//[create_at: 2026-05-17 03:00:00]
//[title: 启点读书签到]
//[author: Hermes]
//[service: BOSS]
//[class: 工具类]
// [version: 2.15.4]
// [platform: web,qq,wx,tg,tb,fs,we]
// [public: false]
// [price: 0]
// [disable: false]
// [description: 启点读书(QDReader) Autman 签到插件:保存 QX 抓到的 H5 Cookie,支持手动“启点签到”;定时使用 `1 7 * * *` 每日 07:01 签到;完全体可选任务按上游原项目解密流程执行;每次签到运行结束后立即归档并清空最近运行日志,不再依赖单独归档定时;带独立 bucket、多用户隔离、请求重试、签名响应解析、结果摘要和安全脱敏。]
// [rule: ^启点(账号|查询|ck|CK|cookie|Cookie|签到|读书|归档|日志归档)(.*)$]
// [rule: ^qdreader(.*)$]
// [admin: true]
// [priority: 600]
// [cron: 1 7 * * *]
var BUCKET = "hermes_qidian"
var STORE_KEY = "qdreader_cookie_json"
var BIND_KEY = "qdreader_user_bind_json"
var REMARK_KEY = "qdreader_remark_json"
var SIGN_API_KEY = "qdreader_sign_api"
var RETRY_KEY = "qdreader_retry_count"
var CRON_SILENT_KEY = "qdreader_cron_silent"
var TASK_PREF_KEY = "qdreader_task_pref_json"
var ADV_JOB_ENABLE_KEY = "qdreader_adv_job_enable"
var EXTRA_ADV_JOB_ENABLE_KEY = "qdreader_extra_adv_job_enable"
var LOTTERY_ENABLE_KEY = "qdreader_lottery_enable"
var WEEKLY_EXCHANGE_ENABLE_KEY = "qdreader_weekly_exchange_enable"
var CHAPTER_CARD_ENABLE_KEY = "qdreader_chapter_card_enable"
var MESSAGE_BOX_ENABLE_KEY = "qdreader_message_box_enable"
var LAST_RESULT_KEY = "qdreader_last_result_json"
var LAST_RUN_KEY = "qdreader_last_run_json"
var ARCHIVE_KEY = "qdreader_archive_json"
var DEFAULT_SIGN_API = "https://api.120399.xyz/qdreader"
var QIDIAN_BASE = "https://h5.if.qidian.com"
var CHECKIN_PATH = "/argus/api/v3/checkin/checkin"
var QD_TASKS = [
{ key: "reward", label: "领取奖励", handler: "receiveGifts", defaultEnabled: true, builtin: true },
{ key: "adv", label: "激励任务", handler: "adv", settingKey: ADV_JOB_ENABLE_KEY },
{ key: "extraAdv", label: "每日任务", handler: "daily", settingKey: EXTRA_ADV_JOB_ENABLE_KEY },
{ key: "lottery", label: "每日抽奖", handler: "lottery", settingKey: LOTTERY_ENABLE_KEY },
{ key: "weeklyExchange", label: "每周兑换", handler: "exchange", settingKey: WEEKLY_EXCHANGE_ENABLE_KEY, sundayOnly: true },
{ key: "chapterCard", label: "章节卡片", handler: "chapterCard", settingKey: CHAPTER_CARD_ENABLE_KEY },
{ key: "messageBox", label: "大咖荐书", handler: "messageBox", settingKey: MESSAGE_BOX_ENABLE_KEY }
]
function defined(name) { try { return typeof this[name] !== "undefined" } catch (e) { return false } }
function globalValue(name) { try { return this[name] } catch (e) { return undefined } }
function safeJsonParse(s, def) { try { return JSON.parse(s) } catch (e) { return def } }
function isObject(x) { return !!x && typeof x === "object" && !Array.isArray(x) }
function trim(s) { return String(s === undefined || s === null ? "" : s).replace(/^\s+|\s+$/g, "") }
function lower(s) { return String(s || "").toLowerCase() }
function cfgGet(key, def) {
try { if (typeof bucketGet === "function") { var bv = bucketGet(BUCKET, key); if (bv !== undefined && bv !== null && String(bv) !== "") return bv } } catch (e) {}
try { if (typeof get === "function") { var v = get(key); if (v !== undefined && v !== null && String(v) !== "") return v } } catch (e) {}
return def
}
function cfgSet(key, value) {
var s = String(value)
var ok = false
try { if (typeof bucketSet === "function") { bucketSet(BUCKET, key, s); ok = true } } catch (e) {}
try { if (typeof set === "function") { set(key, s); ok = true } } catch (e) {}
if (!ok) throw new Error("当前 Autman 环境没有 set/bucketSet,无法保存配置")
return s
}
function getBool(key, def) {
var v = cfgGet(key, def ? "true" : "false")
if (typeof v === "boolean") return v
v = lower(trim(v))
if (v === "1" || v === "true" || v === "yes" || v === "on" || v === "启用" || v === "是") return true
if (v === "0" || v === "false" || v === "no" || v === "off" || v === "禁用" || v === "否") return false
return !!def
}
function getInt(key, def, min, max) {
var n = parseInt(cfgGet(key, String(def)), 10)
if (isNaN(n)) n = def
if (min !== undefined && n < min) n = min
if (max !== undefined && n > max) n = max
return n
}
function reply(msg) {
var out = typeof msg === "string" ? msg : JSON.stringify(msg)
if (!out) return
try { if (typeof sender !== "undefined" && sender && sender.reply) { sender.reply(out); return } } catch (e) {}
try { if (typeof sendText === "function") { sendText(out); return } } catch (e) {}
try { if (typeof push === "function") { push({ content: out }); return } } catch (e) {}
try { console.log(out) } catch (e) {}
}
function pushToUser(userId, content) {
userId = trim(userId || "")
content = typeof content === "string" ? content : JSON.stringify(content)
if (!userId || !content) return false
if (typeof push !== "function") return false
var payloads = [
{ imType: "wx", userID: userId, content: content },
{ imType: "we", userID: userId, content: content },
{ imType: "web", userID: userId, content: content },
{ userID: userId, content: content }
]
for (var i = 0; i < payloads.length; i++) {
try { push(payloads[i]); return true } catch (e) {}
}
return false
}
function debug(msg) { try { if (typeof Debug === "function") Debug(String(msg)); else console.log(String(msg)) } catch (e) {} }
function cleanStringMap(obj) {
if (!isObject(obj)) return {}
var clean = {}
for (var k in obj) if (obj.hasOwnProperty(k) && obj[k] !== undefined && obj[k] !== null && String(obj[k]) !== "") clean[String(k)] = String(obj[k])
return clean
}
function getStore() { return cleanStringMap(safeJsonParse(cfgGet(STORE_KEY, "{}"), {})) }
function setStore(obj) { return cfgSet(STORE_KEY, JSON.stringify(obj || {})) }
function getBindStore() { return cleanStringMap(safeJsonParse(cfgGet(BIND_KEY, "{}"), {})) }
function setBindStore(obj) { return cfgSet(BIND_KEY, JSON.stringify(obj || {})) }
function getRemarkStore() { return cleanStringMap(safeJsonParse(cfgGet(REMARK_KEY, "{}"), {})) }
function setRemarkStore(obj) { return cfgSet(REMARK_KEY, JSON.stringify(obj || {})) }
function cookieGuid(cookie) {
var jar = parseCookieString(cookie)
return trim(jar.ywguid || jar.gwguid || jar.guid || "")
}
function remarksForStore(store) {
var remarks = getRemarkStore()
var changed = false
for (var uid in (store || {})) if (store.hasOwnProperty(uid)) {
var guid = cookieGuid(store[uid])
if (guid && guid !== uid && remarks[guid] && !remarks[uid]) {
remarks[uid] = remarks[guid]
delete remarks[guid]
changed = true
}
}
if (changed) setRemarkStore(remarks)
return remarks
}
function callNoArg(obj, name) {
try { if (obj && typeof obj[name] === "function") return obj[name]() } catch (e) {}
return ""
}
function currentUserId() {
var v = ""
try { if (typeof GetUserID === "function") v = GetUserID() || "" } catch (e) {}
try { if (!v && typeof Sender !== "undefined" && Sender && typeof Sender.GetUserID === "function") v = Sender.GetUserID() || "" } catch (e) {}
var names = ["getUserID", "getUserId", "getUserid", "getUser", "getSenderID", "getSenderId", "getSender", "getFromUserID", "getFromUserId"]
try { if (!v && typeof sender !== "undefined" && sender) for (var i = 0; i < names.length && !v; i++) v = callNoArg(sender, names[i]) || "" } catch (e) {}
var globals = ["userId", "userID", "senderID", "senderId", "fromUserId", "fromUserID"]
for (var j = 0; j < globals.length && !v; j++) { try { if (typeof this[globals[j]] !== "undefined") v = this[globals[j]] || "" } catch (e) {} }
for (var k = 0; k < names.length && !v; k++) { try { if (typeof this[names[k]] === "function") v = this[names[k]]() || "" } catch (e) {} }
return trim(v)
}
function currentOwnerId() {
return currentUserId()
}
function currentIsAdmin() {
try { if (typeof isAdmin === "function" && isAdmin()) return true } catch (e) {}
try { if (typeof sender !== "undefined" && sender && typeof sender.isAdmin === "function" && sender.isAdmin()) return true } catch (e2) {}
try { if (typeof Sender !== "undefined" && Sender && typeof Sender.isAdmin === "function" && Sender.isAdmin()) return true } catch (e3) {}
return false
}
function looksLikeLegacyFallbackOwner(owner) {
owner = trim(owner)
if (!owner) return false
if (owner === "__qdreader_default_owner__") return true
if (/^[A-Za-z0-9._-]{1,32}$/.test(owner) && !/^\d{5,}$/.test(owner)) return true
return false
}
function canRepairBinding(oldOwner, me) {
if (!oldOwner || oldOwner === me) return true
return currentIsAdmin() && looksLikeLegacyFallbackOwner(oldOwner)
}
function currentUserDebug() {
var parts = []
var fns = ["GetUserID", "GetChatID", "GetImType", "ImType", "GetUsername"]
for (var f = 0; f < fns.length; f++) try { if (typeof this[fns[f]] === "function") { var gv = this[fns[f]](); parts.push(fns[f] + "=" + (gv ? maskUid(String(gv)) : "空")) } } catch (e) {}
try { if (typeof Sender !== "undefined" && Sender) {
var snames = ["GetUserID", "GetChatID", "GetImType", "GetUsername"]
for (var s = 0; s < snames.length; s++) if (typeof Sender[snames[s]] === "function") {
var sv = Sender[snames[s]](); parts.push("Sender." + snames[s] + "=" + (sv ? maskUid(String(sv)) : "空"))
}
} } catch (e) {}
try { if (typeof sender !== "undefined" && sender) {
var names = ["getUserID", "getUserId", "getUserid", "getUser", "getSenderID", "getSenderId", "getSender", "getFromUserID", "getFromUserId", "getChatID", "getChatId", "getImtype"]
for (var i = 0; i < names.length; i++) if (typeof sender[names[i]] === "function") {
var v = callNoArg(sender, names[i]); parts.push("sender." + names[i] + "=" + (v ? maskUid(String(v)) : "空"))
}
} } catch (e) {}
return parts.length ? parts.join("; ") : "未发现可用用户方法"
}
function boundToCurrent(uid, bind) {
var owner = bind && bind[String(uid)] ? String(bind[String(uid)]) : ""
var me = currentOwnerId()
return !!owner && !!me && owner === me
}
function visibleUids(store, bind, allUsers) {
var uids = Object.keys(store || {}).sort()
if (allUsers) return uids
return uids.filter(function (uid) { return boundToCurrent(uid, bind || {}) })
}
function parseCookieString(cookie) {
var jar = {}
String(cookie || "").split(";").forEach(function (part) {
var i = part.indexOf("=")
if (i <= 0) return
var k = trim(part.slice(0, i))
var v = trim(part.slice(i + 1))
if (k) jar[k] = v
})
return jar
}
function cookieToString(jar) {
var arr = []
for (var k in jar) if (jar.hasOwnProperty(k) && jar[k] !== undefined && jar[k] !== null && jar[k] !== "") arr.push(k + "=" + jar[k])
return arr.join("; ")
}
function b64decode(s) {
s = String(s || "").trim()
try { s = decodeURIComponent(s) } catch (e) {}
s = s.replace(/-/g, "+").replace(/_/g, "/")
while (s.length % 4) s += "="
try { if (typeof Buffer !== "undefined") return Buffer.from(s, "base64").toString("utf8") } catch (e) {}
try { if (typeof atob === "function") return decodeURIComponent(escape(atob(s))) } catch (e) {}
try {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
var output = "", buffer = 0, bits = 0
for (var i = 0; i < s.length; i++) {
var c = chars.indexOf(s.charAt(i))
if (c < 0) continue
if (c === 64) break
buffer = (buffer << 6) | c
bits += 6
if (bits >= 8) {
bits -= 8
output += String.fromCharCode((buffer >> bits) & 0xff)
}
}
try { return decodeURIComponent(escape(output)) } catch (e2) { return output }
} catch (e3) { return "" }
}
function isRealUid(uid) {
uid = String(uid === undefined || uid === null ? "" : uid).trim()
return /^\d{5,}$/.test(uid) ? uid : ""
}
function uidFromQDHeader(qdheader) {
var decoded = b64decode(qdheader)
if (!decoded) return ""
var parts = decoded.split("|").map(function (x) { return trim(x || "") })
for (var i = 0; i < [0, 11].length; i++) {
var uid = isRealUid(parts[[0, 11][i]])
if (uid) return uid
}
return ""
}
function extractUid(cookie) {
var jar = parseCookieString(cookie)
var uid = uidFromQDHeader(jar.QDHeader || jar.qdheader || jar.QDHEADER || "")
if (uid) return uid
uid = isRealUid(jar.uid || jar.userid || jar.userId || jar.UserId)
if (uid) return uid
return isRealUid(jar.ywguid || jar.gwguid || jar.guid)
}
function maskCookie(cookie) {
cookie = String(cookie || "")
if (!cookie) return ""
var jar = parseCookieString(cookie)
var keys = []
for (var k in jar) if (jar.hasOwnProperty(k)) keys.push(k)
var prefix = keys.slice(0, 5).join(",")
return "[cookie keys:" + prefix + (keys.length > 5 ? ",..." : "") + "; len=" + cookie.length + "]"
}
function maskUid(uid) {
uid = String(uid || "")
if (uid.length <= 4) return uid
return uid.slice(0, 2) + "***" + uid.slice(-2)
}
function runtimeImType() {
var im = ""
try { if (typeof ImType === "function") im = ImType() || "" } catch (e) {}
try { if (!im && typeof GetImType === "function") im = GetImType() || "" } catch (e2) {}
return lower(trim(im))
}
function isSchedulerImType(im) {
im = lower(trim(im))
return im === "cron" || im === "task" || im === "fake"
}
function contentText() {
var s = ""
if (isSchedulerImType(runtimeImType())) return ""
try { if (typeof getContent === "function") s = getContent() || "" } catch (e3) {}
try { if (!s && typeof GetContent === "function") s = GetContent() || "" } catch (e4) {}
try { if (!s && typeof sender !== "undefined" && sender && sender.getContent) s = sender.getContent() || "" } catch (e5) {}
try { if (!s && typeof sender !== "undefined" && sender && sender.getMessage) s = sender.getMessage() || "" } catch (e6) {}
try { if (!s && typeof message !== "undefined") s = String(message) } catch (e7) {}
return trim(s)
}
function usage() {
return [
"启点读书签到插件 v2.15.3",
"【一级菜单】",
"账号管理:启点账号",
"执行查询:启点查询",
"【快速通道】",
"快速提交CK:启点ck <QX抓到的cookie>",
"直接签到:启点签到",
"指定签到:启点签到 <uid>",
"账号任务:启点账号 → 账号详情/任务配置",
"【配置】",
"签名网关:Autman 参数 hermes_qidian.qdreader_sign_api,默认 " + DEFAULT_SIGN_API,
"完全体任务:领取奖励固定执行;激励/每日/抽奖/兑换/章节卡/大咖荐书按账号单独管理,默认关闭。",
"定时:每日 07:01 自动执行签到链路;QX 只负责抓 cookie。"
].join("\n")
}
function stripCommand(text) {
var s = String(text || "")
if (/^启点(?:签到|读书)(?:\s|$)/i.test(s)) return trim(s.replace(/^启点(?:签到|读书)\s*/i, ""))
if (/^启点(?:归档|日志归档)(?:\s|$)/i.test(s)) return trim(s.replace(/^启点(?:归档|日志归档)\s*/i, ""))
if (/^启点(?:查询)(?:\s|$)/i.test(s)) return trim(s.replace(/^启点(?:查询)\s*/i, ""))
if (/^启点(?:账号)(?:\s|$)/i.test(s)) return trim(s.replace(/^启点(?:账号)\s*/i, ""))
if (/^启点(?:ck|CK|cookie|Cookie)(?:\s|$)/i.test(s)) return trim(s.replace(/^启点(?:ck|CK|cookie|Cookie)\s*/i, ""))
return trim(s.replace(/^qdreader\s*/i, ""))
}
function commandName(text) {
if (/^启点(?:签到|读书)(?:\s|$)/i.test(text)) return "sign"
if (/^启点(?:归档|日志归档)(?:\s|$)/i.test(text)) return "archive"
if (/^qdreader\s+sign(?:\s|$)/i.test(text)) return "sign"
if (/^qdreader\s+archive(?:\s|$)/i.test(text)) return "archive"
if (/^启点(?:查询)(?:\s|$)/i.test(text)) return "status"
if (/^启点(?:账号)(?:\s|$)/i.test(text)) return "account"
return "ck"
}
function qs(obj) {
var parts = []
for (var k in (obj || {})) {
if (!obj.hasOwnProperty(k)) continue
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) if (v.hasOwnProperty(sk)) 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
var guid = jar.ywguid || jar.gwguid || jar.guid || ""
var qdh = jar.qdh || jar.QDH || ""
var qdheader = jar.QDHeader || jar.qdheader || jar.QDHEADER || ""
return { jar: jar, cookie: cookieToString(jar), qdh: qdh, qdheader: qdheader, uid: extractUid(cookie), guid: guid, qimei: jar.qimei || jar.QIMEI || "", qimei36: jar.qimei36 || jar.QIMEI36 || "" }
}
function httpReq(opt) {
if (!opt.headers) opt.headers = {}
if (typeof request === "function") return request(opt)
if (typeof $request === "function") return $request(opt)
throw new Error("当前 Autman 环境没有 request/$request 方法,无法发起签到请求")
}
function parseResp(res) {
if (res === undefined || res === null) return null
var body = null
if (res && typeof res === "object") {
if (res.body !== undefined) body = res.body
else if (res.data !== undefined) body = res.data
else if (res.content !== undefined) body = res.content
else if (res.responseText !== undefined) body = res.responseText
else if (res.statusCode !== undefined || res.status !== undefined || res.headers !== undefined) body = ""
else body = res
} else {
body = res
}
if (typeof body === "string") return safeJsonParse(body, body)
return body
}
function requestWithRetry(opt, retryCount) {
var lastErr = null
var attempts = Math.max(1, (retryCount || 0) + 1)
for (var i = 0; i < attempts; i++) {
try {
return httpReq(opt)
} catch (e) {
lastErr = e
debug("QDReader request retry " + (i + 1) + "/" + attempts + " failed: " + (e && e.message ? e.message : String(e)))
}
}
throw lastErr || new Error("请求失败")
}
function postJson(url, data) {
var retry = getInt(RETRY_KEY, 1, 0, 3)
return parseResp(requestWithRetry({ url: url, method: "post", dataType: "json", timeOut: 10000, headers: { "Content-Type": "application/json", "Accept": "application/json" }, body: JSON.stringify(data) }, retry))
}
function normalizeSignHeaders(signed) {
if (typeof signed === "string") signed = safeJsonParse(signed, {})
if (!signed) throw new Error("签名网关无返回")
var code = signed.code !== undefined ? signed.code : (signed.retcode !== undefined ? signed.retcode : signed.status)
var ok = code === undefined || code === 0 || code === "0" || code === 200 || code === "200" || code === "ok" || code === true
var data = signed.data || signed.result || signed.headers || signed
var headers = data && (data.headers || data.header || data)
if (!ok || !isObject(headers)) throw new Error("签名失败:" + JSON.stringify(signed).slice(0, 500))
return headers
}
function qidianRequest(path, method, data, cookie) {
method = String(method || "GET").toUpperCase()
var info = normalizeCookie(cookie)
if (!info.uid) throw new Error("Cookie 缺少 uid/QDHeader")
var signApi = String(cfgGet(SIGN_API_KEY, DEFAULT_SIGN_API)).replace(/\/+$/, "")
var targetUrl = /^https?:\/\//i.test(path) ? path : QIDIAN_BASE + path
var payload = { qdh: info.qdh, qdheader: info.qdheader, url: targetUrl, method: lower(method), data: data || {}, uid: info.uid, guid: info.guid, qimei: info.qimei, qimei36: info.qimei36 }
var headers = normalizeSignHeaders(postJson(signApi + "/sign", payload))
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: targetUrl, method: lower(method), 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 retry = getInt(RETRY_KEY, 1, 0, 3)
var out = parseResp(requestWithRetry(req, retry))
if (typeof out === "string") out = safeJsonParse(out, out)
return out
}
function resultCode(obj) {
if (!obj) return undefined
var keys = ["Result", "result", "Code", "code", "retcode", "RetCode"]
for (var i = 0; i < keys.length; i++) if (obj[keys[i]] !== undefined) return obj[keys[i]]
var data = obj.Data || obj.data || obj.ResultData || obj.resultData
if (data && typeof data === "object") for (var j = 0; j < keys.length; j++) if (data[keys[j]] !== undefined) return data[keys[j]]
return undefined
}
function resultOk(obj) {
var code = resultCode(obj)
var status = obj && (obj.status || obj.Status)
return code === undefined || code === 0 || code === "0" || code === 200 || code === "200" || status === "ok" || status === "success"
}
function resultMsg(obj) {
if (!obj) return ""
return obj.Message || obj.Msg || obj.message || obj.msg || obj.toast || (obj.data && (obj.data.message || obj.data.msg || obj.data.toast)) || (obj.Data && (obj.Data.message || obj.Data.msg || obj.Data.toast)) || ""
}
function successLikeMsg(msg) {
msg = String(msg || "")
return /今日已签到|已经签到|已签到|重复签到|已领取|已经领取|已完成|完成|无可领取|无礼包|暂无可领取/.test(msg)
}
function taskResultOk(obj) {
return resultOk(obj) || successLikeMsg(resultMsg(obj))
}
function isWhitelistBlockedError(msg) {
msg = String(msg || "")
return /url\s*不在允许的接口列表内|不在允许的接口列表|参数异常.*允许的接口/.test(msg)
}
function pad2(n) { n = String(n); return n.length < 2 ? "0" + n : n }
function beijingDate(dateLike) {
var d = dateLike ? new Date(dateLike) : new Date()
if (!d || isNaN(d.getTime())) d = new Date()
return new Date(d.getTime() + 8 * 60 * 60 * 1000)
}
function beijingTime(dateLike) {
var d = beijingDate(dateLike)
return d.getUTCFullYear() + "-" + pad2(d.getUTCMonth() + 1) + "-" + pad2(d.getUTCDate()) + " " + pad2(d.getUTCHours()) + ":" + pad2(d.getUTCMinutes()) + ":" + pad2(d.getUTCSeconds())
}
function nowIso() { return beijingTime() }
function setLastResult(uid, result) {
var obj = cleanStringMap(safeJsonParse(cfgGet(LAST_RESULT_KEY, "{}"), {}))
obj[String(uid)] = JSON.stringify(result || {})
cfgSet(LAST_RESULT_KEY, JSON.stringify(obj))
}
function getLastResults() {
var raw = cleanStringMap(safeJsonParse(cfgGet(LAST_RESULT_KEY, "{}"), {}))
var out = {}
for (var uid in raw) if (raw.hasOwnProperty(uid)) out[uid] = safeJsonParse(raw[uid], { msg: raw[uid] })
return out
}
function setLastRunLog(log) { cfgSet(LAST_RUN_KEY, JSON.stringify(log || {})) }
function getLastRunLog() { return safeJsonParse(cfgGet(LAST_RUN_KEY, "{}"), {}) || {} }
function getArchiveStore() { return safeJsonParse(cfgGet(ARCHIVE_KEY, "{}"), {}) || {} }
function setArchiveStore(obj) { cfgSet(ARCHIVE_KEY, JSON.stringify(obj || {})) }
function localDateKey(iso) {
var s = String(iso || "")
var m0 = s.match(/^(\d{4}-\d{2}-\d{2})/)
if (m0) return m0[1]
var d = beijingDate(iso)
return d.getUTCFullYear() + "-" + pad2(d.getUTCMonth() + 1) + "-" + pad2(d.getUTCDate())
}
function archiveRunLog(reason) {
var run = getLastRunLog()
var archive = getArchiveStore()
var archived = false
var key = ""
if (run && run.finishedAt && run.total !== undefined) {
key = localDateKey(run.finishedAt)
archive[key] = run
archive[key].archivedAt = nowIso()
archive[key].archiveReason = reason || "daily-rollover"
archived = true
}
setArchiveStore(archive)
cfgSet(LAST_RESULT_KEY, "{}")
cfgSet(LAST_RUN_KEY, "{}")
return { archived: archived, key: key, archive: archive, run: run || {} }
}
function latestArchiveForVisible(uids) {
var archive = getArchiveStore()
var keys = []
for (var k in archive) if (archive.hasOwnProperty(k)) keys.push(k)
keys.sort()
for (var i = keys.length - 1; i >= 0; i--) {
var log = archive[keys[i]] || {}
var items = log.items || []
var map = {}
for (var j = 0; j < items.length; j++) map[String(items[j].uid)] = items[j]
if (!uids || !uids.length) return { key: keys[i], log: log, itemMap: map }
for (var u = 0; u < uids.length; u++) if (map[String(uids[u])]) return { key: keys[i], log: log, itemMap: map }
}
return null
}
function cronMode() {
// Cron only runs the signing branch. Archive is performed immediately after each sign run.
return "sign"
}
function formatArchiveReply(ret) {
if (ret && ret.archived) {
var run = ret.run || {}
return "启点读书日志归档完成:" + ret.key + "\n" +
"来源:" + sourceName(run.source) + "\n" +
"成功 " + (run.okCount || 0) + " / 失败 " + (run.failCount || 0) + " / 总计 " + (run.total || 0) + "\n" +
"已清空当日运行日志,后续查询可从归档判断签到情况"
}
return "启点读书日志归档完成:无可归档批次;已清空当日运行日志"
}
function sourceName(source) {
if (source === "cron") return "定时任务/零点任务"
if (source === "menu") return "菜单立即执行"
if (source === "manual") return "手动签到"
return source || "未知"
}
function getTaskPrefs() { return safeJsonParse(cfgGet(TASK_PREF_KEY, "{}"), {}) || {} }
function setTaskPrefs(obj) { return cfgSet(TASK_PREF_KEY, JSON.stringify(obj || {})) }
function accountTaskPrefs(uid) {
var all = getTaskPrefs()
var prefs = all[String(uid)]
return isObject(prefs) ? prefs : {}
}
function taskEnabledForUid(task, uid) {
if (task.defaultEnabled || task.builtin) return true
if (task.sundayOnly) { var d = beijingDate(); if (d.getUTCDay() !== 0) return false }
var prefs = accountTaskPrefs(uid)
return prefs[task.key] === true || prefs[task.key] === "true" || prefs[task.key] === 1 || prefs[task.key] === "1"
}
function taskEnabled(task) { return taskEnabledForUid(task, "") }
function taskStatusLines(uid) {
var lines = ["启点账号任务开关:" + (uid ? maskUid(uid) : "未选择账号")]
var prefs = uid ? accountTaskPrefs(uid) : {}
for (var i = 0; i < QD_TASKS.length; i++) {
var t = QD_TASKS[i]
var enabled = t.builtin || t.defaultEnabled || prefs[t.key] === true || prefs[t.key] === "true" || prefs[t.key] === 1 || prefs[t.key] === "1"
var suffix = t.builtin ? "(固定)" : ""
if (t.sundayOnly) suffix += "(仅周日执行)"
lines.push((i + 1) + ". " + t.label + "" + (enabled ? "开启" : "关闭") + suffix)
}
lines.push("回复:序号 on/off 切换,例如 2 onall on 一键开启;all off 一键关闭;q 退出")
return lines.join("\n")
}
function taskMenuForUid(uid) {
if (!uid) return reply("未选择账号,已退出")
reply(taskStatusLines(uid))
var op = listenInput(60000)
if (!op || /^(q|退出|取消)$/i.test(op)) return reply("已退出")
var all = getTaskPrefs()
var prefs = isObject(all[String(uid)]) ? all[String(uid)] : {}
var allMatch = op.match(/^(all|全部|所有|一键|全开|全关)\s*(on|off|开启|关闭|启用|禁用|1|0)?$/i)
if (allMatch) {
var enableAll = /^(全开)$/i.test(allMatch[1]) || /^(on|开启|启用|1)$/i.test(allMatch[2] || "")
if (/^(全关)$/i.test(allMatch[1])) enableAll = false
if (!allMatch[2] && /^(all|全部|所有|一键)$/i.test(allMatch[1])) return reply("请指定 all on 或 all off")
for (var ai = 0; ai < QD_TASKS.length; ai++) {
var at = QD_TASKS[ai]
if (at.builtin || !at.settingKey) continue
prefs[at.key] = !!enableAll
}
all[String(uid)] = prefs
setTaskPrefs(all)
return reply("已" + (enableAll ? "一键开启" : "一键关闭") + "账号 " + maskUid(uid) + " 的全部可选任务\n" + taskStatusLines(uid))
}
var m = op.match(/^(\d+)\s*(on|off|开启|关闭|启用|禁用|1|0)?$/i)
if (!m) return reply("未识别选项,已退出")
var idx = parseInt(m[1], 10) - 1
if (idx < 0 || idx >= QD_TASKS.length) return reply("编号无效,已退出")
var task = QD_TASKS[idx]
if (task.builtin || !task.settingKey) return reply(task.label + " 为固定任务,不能关闭")
var currently = prefs[task.key] === true || prefs[task.key] === "true" || prefs[task.key] === 1 || prefs[task.key] === "1"
var enable = m[2] ? /^(on|开启|启用|1)$/i.test(m[2]) : !currently
prefs[task.key] = !!enable
all[String(uid)] = prefs
setTaskPrefs(all)
return reply(task.label + "已" + (enable ? "开启" : "关闭") + "(账号 " + maskUid(uid) + "\n" + taskStatusLines(uid))
}
function qdGet(path, data, cookie) { return qidianRequest(path, "GET", data || {}, cookie) }
function qdPost(path, data, cookie) { return qidianRequest(path, "POST", data || {}, cookie) }
function firstArray() {
for (var i = 0; i < arguments.length; i++) if (Array.isArray(arguments[i])) return arguments[i]
return []
}
function nested(obj, path) {
var cur = obj
for (var i = 0; i < path.length; i++) {
if (!cur || cur[path[i]] === undefined || cur[path[i]] === null) return undefined
cur = cur[path[i]]
}
return cur
}
function payloadData(obj) { return (obj && (obj.Data || obj.data)) || {} }
function looksLikeVideoTask(task) { return isObject(task) && !!taskIdOf(task) && (task["IsFinished"] !== undefined || task["IsReceived"] !== undefined || task["Title"] !== undefined || task["TaskName"] !== undefined) }
function taskArrayScore(list, kind) {
if (!Array.isArray(list) || !list.length) return 0
var score = 0
for (var i = 0; i < list.length; i++) {
var t = list[i] || {}
if (!looksLikeVideoTask(t)) continue
var title = taskTitle(t)
score += 2
if (kind === "daily" && (title === "完成1个广告任务得奖励" || title === "完成3个广告任务得奖励" || t["IsReceived"] !== undefined)) score += 5
if (kind === "adv" && (t["IsFinished"] !== undefined || /激励|碎片|福利|广告/.test(title))) score += 3
}
return score
}
function findTaskListByShape(root, kind) {
var best = []
var bestScore = 0
var seen = []
function walk(node, depth) {
if (!node || depth > 8) return
if (Array.isArray(node)) {
var score = taskArrayScore(node, kind)
if (score > bestScore) { bestScore = score; best = node }
for (var i = 0; i < node.length; i++) walk(node[i], depth + 1)
return
}
if (!isObject(node) || seen.indexOf(node) >= 0) return
seen.push(node)
for (var k in node) walk(node[k], depth + 1)
}
walk(root, 0)
return best
}
function advTaskList(main) {
return firstArray(
nested(main, ["Data", "DailyBenefitModule", "TaskList"]),
nested(main, ["data", "DailyBenefitModule", "TaskList"]),
nested(main, ["Data", "AdvTaskList"]),
nested(main, ["data", "AdvTaskList"]),
findTaskListByShape(payloadData(main), "adv")
)
}
function dailyTaskList(main) {
return firstArray(
nested(main, ["Data", "VideoRewardTab", "TaskList"]),
nested(main, ["data", "VideoRewardTab", "TaskList"]),
nested(main, ["Data", "TaskList"]),
nested(main, ["data", "TaskList"]),
findTaskListByShape(payloadData(main), "daily")
)
}
function surpriseBenefit(data) {
return data.SurpriseBenefit || data.SurpriseTask || data.SurpriseBenefitTask || null
}
function makeVideoPayload(taskId) {
return { taskId: String(taskId || ""), BanId: "0", BanMessage: "", CaptchaAId: "", CaptchaURL: "", CaptchaType: "0", Challenge: "", Gt: "", NewCaptcha: "0", Offline: "0", PhoneNumber: "", SessionKey: "" }
}
function field(obj, names, def) {
for (var i = 0; i < names.length; i++) if (obj && obj[names[i]] !== undefined && obj[names[i]] !== null) return obj[names[i]]
return def
}
function taskTitle(task) { return String(field(task, ["Title", "TaskName", "Name", "TaskTitle", "Desc", "Description"], "")) }
function taskIdOf(task) { return field(task, ["TaskId", "TaskID", "taskId", "TaskIDStr", "Id", "id"], "") }
function taskFinished(task) { return Number(field(task, ["IsFinished", "Finished", "Status", "status"], 0)) === 1 }
function taskIdKey(id) { return String(id === undefined || id === null ? "" : id) }
function taskRunCount(task, def) {
var n = Number(field(task, ["NeedCount", "needCount", "Count", "count", "TotalCount", "totalCount", "Times", "times", "Limit", "limit"], def || 1))
if (!n || isNaN(n) || n < 1) n = def || 1
if (n > 10) n = 10
return n
}
function taskAccepted(res) { return taskResultOk(res) || Number(resultCode(res)) === 9 }
function riskConf(res) {
var data = res && (res.Data || res.data || res.ResultData || res.resultData)
return data && (data.RiskConf || data.riskConf || data.Risk || data.risk)
}
function finishVideoTask(taskOrId, cookie, repeat) {
var tid = typeof taskOrId === "object" ? taskIdOf(taskOrId || {}) : taskOrId
var n = repeat || (typeof taskOrId === "object" ? taskRunCount(taskOrId, 1) : 1)
var ok = 0
var last = null
if (!tid) return { ok: 0, total: 0, last: null }
for (var i = 0; i < n; i++) {
last = qdPost("/argus/api/v1/video/adv/finishWatch", makeVideoPayload(tid), cookie)
if (taskAccepted(last)) ok++
else break
}
return { ok: ok, total: n, last: last, taskId: tid }
}
function makeBase62Token(num) {
var alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
var n = BigInt(num)
var zero = BigInt(0)
var base = BigInt(62)
if (n === zero) return "0"
var out = ""
while (n > zero) { out = alphabet[Number(n % base)] + out; n = n / base }
return out
}
function doReceiveGifts(cookie) {
var res = qdPost("/argus/api/v3/checkin/receivegifts", { GameId: "0" }, cookie)
var ok = taskResultOk(res)
return { name: "领取奖励", ok: !!ok, msg: resultMsg(res) || (ok ? "领取成功" : "领取失败"), path: "/argus/api/v3/checkin/receivegifts" }
}
function doAdvTask(cookie) {
var main = qdGet("/argus/api/v2/video/adv/mainPage", {}, cookie)
if (!taskResultOk(main)) return { name: "激励任务", ok: false, msg: resultMsg(main) || "获取任务列表失败", path: "/argus/api/v2/video/adv/mainPage" }
var tasks = advTaskList(main)
if (!tasks.length) return { name: "激励任务", ok: false, msg: "任务列表为空", path: "/argus/api/v2/video/adv/mainPage" }
if (taskFinished(tasks[tasks.length - 1])) return { name: "激励任务", ok: true, msg: "任务已完成", path: "/argus/api/v2/video/adv/mainPage" }
var submittedCount = 0
var riskSubmitted = false
for (var i = 0; i < tasks.length; i++) {
var t = tasks[i] || {}
if (taskFinished(t)) continue
var tid = taskIdOf(t)
if (!tid) continue
var fr = finishVideoTask(tid, cookie, 1)
submittedCount += fr.ok
if (riskConf(fr.last)) riskSubmitted = true
if (fr.ok < fr.total) return { name: "激励任务", ok: false, msg: resultMsg(fr.last) || ("第 " + (i + 1) + " 个任务失败"), path: "/argus/api/v1/video/adv/finishWatch" }
}
var verify = qdGet("/argus/api/v2/video/adv/mainPage", {}, cookie)
var after = advTaskList(verify)
var finished = !!(after && after.length && after.every(function (t) { return taskFinished(t) }))
if (finished) return { name: "激励任务", ok: true, msg: "任务已完成", path: "/argus/api/v2/video/adv/mainPage" }
if (submittedCount > 0 && riskSubmitted) return { name: "激励任务", ok: true, msg: "已提交,状态待刷新", path: "/argus/api/v2/video/adv/mainPage" }
return { name: "激励任务", ok: false, msg: submittedCount ? "部分任务未完成" : "未找到可提交任务", path: "/argus/api/v2/video/adv/mainPage" }
}
function taskReceived(task) {
return Number(field(task, ["IsReceived", "Received", "Status", "status"], 0)) === 1
}
function doDailyTask(cookie) {
var main = qdGet("/argus/api/v2/video/adv/mainPage", {}, cookie)
if (!taskResultOk(main)) return { name: "每日任务", ok: false, msg: resultMsg(main) || "获取任务列表失败", path: "/argus/api/v2/video/adv/mainPage" }
var data = payloadData(main)
var done = 0
var pre = surpriseBenefit(data)
if (pre && !taskFinished(pre) && taskIdOf(pre)) {
var pr = finishVideoTask(taskIdOf(pre), cookie, 1)
done += pr.ok
if (pr.ok < pr.total) return { name: "每日任务", ok: false, msg: resultMsg(pr.last) || "惊喜福利任务失败", path: "/argus/api/v1/video/adv/finishWatch" }
}
var tasks = dailyTaskList(main)
if (!tasks.length) return { name: "每日任务", ok: false, msg: "任务列表为空", path: "/argus/api/v2/video/adv/mainPage" }
var oneAd = null
var target = null
for (var i = 0; i < tasks.length; i++) {
var title = taskTitle(tasks[i])
if (!oneAd && title === "完成1个广告任务得奖励") oneAd = tasks[i]
if (!target && title === "完成3个广告任务得奖励") target = tasks[i]
}
if (oneAd && !taskReceived(oneAd) && taskIdOf(oneAd)) {
var ar = finishVideoTask(taskIdOf(oneAd), cookie, 1)
done += ar.ok
if (ar.ok < ar.total) return { name: "每日任务", ok: false, msg: resultMsg(ar.last) || "完成1个广告任务失败", path: "/argus/api/v1/video/adv/finishWatch" }
}
if (!target) return { name: "每日任务", ok: false, msg: "未找到每日任务任务", path: "/argus/api/v2/video/adv/mainPage" }
if (taskReceived(target)) return { name: "每日任务", ok: true, msg: "任务已完成", path: "/argus/api/v2/video/adv/mainPage" }
var tid = taskIdOf(target)
if (!tid) return { name: "每日任务", ok: false, msg: "每日任务缺少 TaskId", path: "/argus/api/v2/video/adv/mainPage" }
var tr = finishVideoTask(tid, cookie, 3)
done += tr.ok
if (tr.ok < tr.total) return { name: "每日任务", ok: false, msg: resultMsg(tr.last) || "每日任务广告失败", path: "/argus/api/v1/video/adv/finishWatch" }
var verify = qdGet("/argus/api/v2/video/adv/mainPage", {}, cookie)
var after = dailyTaskList(verify)
var finished = false
for (var v = 0; v < after.length; v++) if (taskTitle(after[v]) === "完成3个广告任务得奖励" && taskReceived(after[v])) { finished = true; break }
return { name: "每日任务", ok: finished, msg: finished ? "任务已完成" : "任务未完成", path: "/argus/api/v2/video/adv/mainPage" }
}
function doLotteryTask(cookie) {
var detail = qdGet("/argus/api/v2/checkin/detail", {}, cookie)
if (!taskResultOk(detail)) return { name: "每日抽奖", ok: false, msg: resultMsg(detail) || "获取抽奖信息失败", path: "/argus/api/v2/checkin/detail" }
var data = detail.Data || detail.data || {}
var times = Number(data.HasVideoUrge || 0) + Number(data.LotteryCount || data.LotteryChance || data.ChanceCount || 0)
if (!times) return { name: "每日抽奖", ok: true, msg: "无可用抽奖次数", path: "/argus/api/v2/checkin/detail" }
times = Math.min(times, 10)
var okCount = 0
for (var i = 0; i < times; i++) { var r = qdPost("/argus/api/v2/checkin/lottery", { GameId: "0", IsVideo: i < Number(data.HasVideoUrge || 0) ? "1" : "0" }, cookie); if (taskResultOk(r)) okCount++ }
return { name: "每日抽奖", ok: okCount > 0, msg: "成功 " + okCount + "/" + times + " 次抽奖", path: "/argus/api/v2/checkin/lottery" }
}
function doWeeklyExchange(cookie) {
var page = qdGet("/argus/api/v3/checkin/checkinexchangepage", {}, cookie)
if (!taskResultOk(page)) return { name: "每周兑换", ok: false, msg: resultMsg(page) || "获取兑换页失败", path: "/argus/api/v3/checkin/checkinexchangepage" }
var data = page.Data || page.data || {}
var balance = Number(data.Balance || data.Score || 0)
if (balance <= 0) return { name: "每周兑换", ok: false, msg: "余额不足(" + balance + ")", path: "/argus/api/v3/checkin/checkinexchangepage" }
var goods = firstArray(data.Goods, data.GoodsList, data.GiftList, data.List, data.Items)
if (!goods.length) return { name: "每周兑换", ok: false, msg: "章节卡列表为空", path: "/argus/api/v3/checkin/checkinexchangepage" }
var chosen = null
for (var i = 0; i < goods.length; i++) {
var price = Number(goods[i].GoodsScore || goods[i].NeedScore || goods[i].Price || goods[i].Score || 0)
if (price > 0 && price <= balance && (!chosen || price > Number(chosen.GoodsScore || chosen.NeedScore || chosen.Price || chosen.Score || 0))) chosen = goods[i]
}
if (!chosen) return { name: "每周兑换", ok: false, msg: "余额不足(" + balance + ")", path: "/argus/api/v3/checkin/checkinexchangepage" }
var gid = chosen.GoodsId || chosen.Id || chosen.GoodsID
var priceText = Number(chosen.GoodsScore || chosen.NeedScore || chosen.Price || chosen.Score || 0)
var cardCount = chosen.ChapterCardCount || chosen.CardCount || chosen.Count || ""
var r = qdPost("/argus/api/v3/checkin/exchangegoods", { goodId: gid }, cookie)
var code = Number(r && (r.result !== undefined ? r.result : (r.Result !== undefined ? r.Result : (r.code !== undefined ? r.code : r.Code))))
if (code === -220000) return { name: "每周兑换", ok: false, msg: "未到兑换时间", path: "/argus/api/v3/checkin/exchangegoods" }
var ok = taskResultOk(r)
var successMsg = cardCount ? ("消耗 " + priceText + " 点兑换 " + cardCount + " 点章节卡") : "兑换成功"
return { name: "每周兑换", ok: ok, msg: resultMsg(r) || (ok ? successMsg : "兑换失败"), path: "/argus/api/v3/checkin/exchangegoods" }
}
function doChapterCard(cookie) {
var res = qdGet("/argus/api/v1/readtime/scoremall/chaptercardwithbook", {}, cookie)
var ok = taskResultOk(res)
var cards = firstArray(nested(res, ["Data", "Cards"]), nested(res, ["data", "Cards"]), nested(res, ["Data", "ChapterCards"]), nested(res, ["data", "ChapterCards"]))
return { name: "章节卡片", ok: !!ok, msg: ok ? (cards.length ? ("章节卡 " + cards.length + " 张") : "暂无章节卡") : (resultMsg(res) || "查询失败"), path: "/argus/api/v1/readtime/scoremall/chaptercardwithbook" }
}
function messageTokenFromCookie(cookie) {
var jar = parseCookieString(cookie)
var uid = extractUid(cookie)
var raw = uid ? ((BigInt(uid) * BigInt(4) & BigInt("0x7ffffffffffffffc")) ^ BigInt("0x113a4f")) : BigInt(0)
return raw ? makeBase62Token(raw) : (uid || jar.ywguid || "")
}
function recentCreateTime(v) {
if (!v) return false
var t = typeof v === "string" ? new Date(v).getTime() : Number(v)
if (!t || isNaN(t)) return false
if (t < 1000000000000) t = t * 1000
var now = (new Date()).getTime()
return now - t >= 0 && now - t <= 24 * 60 * 60 * 1000
}
function beijingHour(dateLike) { return beijingDate(dateLike).getUTCHours() }
function messageBoxWindowLabel() { return "全天" }
function messageBoxAllowedNow() {
return true
}
function orderIdFromText(s) {
var m = String(s || "").match(/orderId=([a-f0-9]+)/i)
return m ? m[1] : ""
}
function doMessageBox(cookie) {
var token = messageTokenFromCookie(cookie)
if (!token) return { name: "大咖荐书", ok: true, msg: "缺少消息 token,已跳过", path: "https://magev6.if.qidian.com/argus/api/v2/message/pullmessage" }
var baseParams = { appappend: token, categoryIds: "0", pg: "1", pz: "10" }
var res = qdGet("https://magev6.if.qidian.com/argus/api/v2/message/pullmessage", baseParams, cookie)
if (!taskResultOk(res)) return { name: "大咖荐书", ok: false, msg: resultMsg(res) || "获取消息列表失败", path: "https://magev6.if.qidian.com/argus/api/v2/message/pullmessage" }
var groups = firstArray(nested(res, ["Data", "UserList"]), nested(res, ["data", "UserList"]), nested(res, ["Data", "List"]), nested(res, ["data", "List"]), nested(res, ["Data", "MsgList"]), nested(res, ["data", "MsgList"]))
if (!groups.length) return { name: "大咖荐书", ok: true, msg: "无消息", path: "https://magev6.if.qidian.com/argus/api/v2/message/pullmessage" }
var senders = []
for (var i = 0; i < groups.length; i++) if (Number(field(groups[i], ["RedPoint", "redPoint"], 0)) === 1 && field(groups[i], ["FromId", "fromId", "SenderId", "senderId", "FromUserId", "fromUserId"], "")) senders.push(groups[i])
if (!senders.length) return { name: "大咖荐书", ok: true, msg: "无未读消息", path: "https://magev6.if.qidian.com/argus/api/v2/message/pullmessage" }
var handled = 0
for (var s = 0; s < senders.length; s++) {
var sid = field(senders[s], ["FromId", "fromId", "SenderId", "senderId", "FromUserId", "fromUserId"], "")
var msgRes = qdGet("https://magev6.if.qidian.com/argus/api/v2/message/pullmessage", { appappend: token, categoryIds: "0", fromId: sid, pg: "1", pz: "10" }, cookie)
if (!taskResultOk(msgRes)) continue
var msgs = firstArray(nested(msgRes, ["Data", "MsgList"]), nested(msgRes, ["data", "MsgList"]), nested(msgRes, ["Data", "List"]), nested(msgRes, ["data", "List"]), nested(msgRes, ["Data", "Messages"]), nested(msgRes, ["data", "Messages"]))
for (var j = 0; j < msgs.length; j++) {
if (!recentCreateTime(field(msgs[j], ["CreateTime", "createTime"], ""))) continue
var orderId = orderIdFromText(field(msgs[j], ["ActionUrl", "actionUrl", "RefUrl", "refUrl", "Content", "content", "Link", "link", "Url", "url"], ""))
if (!orderId) continue
var detail = qdGet("/argus/api/v1/interaction/rewarddetail", { orderId: orderId, refresh: "0" }, cookie)
if (!taskResultOk(detail)) continue
var data = detail.Data || detail.data || {}
var chance = Number(data.ChanceCount || 0)
var bookStatus = Number(data.BookStatus || 0)
var bookId = data.BookId || ""
if (data.Status !== undefined && Number(data.Status) !== 1) continue
if (chance <= 0 && bookStatus !== 0 && bookStatus !== 1 && bookStatus !== 2) continue
if (chance > 0) { var cb = qdPost("/argus/api/v1/interaction/rewardcallback", { orderId: orderId, checkRisk: "1" }, cookie); if (taskResultOk(cb) || String(resultCode(cb)) === "0") handled++; continue }
if (bookStatus === 0 || bookStatus === 1) { if (!bookId) continue; var add = qdPost("/argus/api/v1/interaction/rewardaddshelf", { orderId: orderId, bookId: bookId }, cookie); if (taskResultOk(add) || String(resultCode(add)) === "0") handled++; continue }
if (bookStatus === 2) { var rc = qdPost("/argus/api/v1/interaction/rewardcallback", { orderId: orderId, checkRisk: "1" }, cookie); if (!taskResultOk(rc) && String(resultCode(rc)) !== "0") continue; var r2 = qdPost("/argus/api/v1/interaction/rewardcallback", { orderId: orderId, checkRisk: "0" }, cookie); if (taskResultOk(r2) || String(resultCode(r2)) === "0") handled++ }
}
}
return { name: "大咖荐书", ok: true, msg: handled ? ("成功处理 " + handled + " 个荐书") : "无待处理荐书", path: "https://magev6.if.qidian.com/argus/api/v2/message/pullmessage" }
}
function doExtraTask(task, cookie) {
if (task.handler === "receiveGifts") return doReceiveGifts(cookie)
if (task.handler === "adv") return doAdvTask(cookie)
if (task.handler === "daily") return doDailyTask(cookie)
if (task.handler === "lottery") return doLotteryTask(cookie)
if (task.handler === "exchange") return doWeeklyExchange(cookie)
if (task.handler === "chapterCard") return doChapterCard(cookie)
if (task.handler === "messageBox") return doMessageBox(cookie)
var data = task.body || task.params || {}
var res = qidianRequest(task.path, task.method || "GET", data, cookie)
var ok = taskResultOk(res)
var msg = resultMsg(res) || (ok ? "完成" : ("失败" + (resultCode(res) !== undefined ? " code=" + resultCode(res) : "")))
return { name: task.label, ok: !!ok, msg: String(msg || ""), path: task.path }
}
function doTaskChain(uid, cookie, source, runId) {
var startedAt = nowIso()
var details = []
var checkin = qidianRequest(CHECKIN_PATH, "POST", {}, cookie)
var ok = taskResultOk(checkin)
var msg = resultMsg(checkin) || (ok ? "签到请求成功" : ("签到失败" + (resultCode(checkin) !== undefined ? " code=" + resultCode(checkin) : "")))
details.push({ name: "签到结果", ok: !!ok, msg: String(msg || ""), path: CHECKIN_PATH })
for (var i = 0; i < QD_TASKS.length; i++) {
var task = QD_TASKS[i]
if (!taskEnabledForUid(task, uid)) continue
try { details.push(doExtraTask(task, cookie)) }
catch (e) {
var tmsg = e && e.message ? e.message : String(e)
if (isWhitelistBlockedError(tmsg)) details.push({ name: task.label, ok: true, skipped: true, msg: "签名网关暂不支持该接口,已跳过", rawMsg: tmsg, path: task.path })
else details.push({ name: task.label, ok: false, msg: tmsg, path: task.path })
}
}
var failed = []
for (var j = 0; j < details.length; j++) if (!details[j].ok && !details[j].skipped) failed.push(details[j].name + ":" + details[j].msg)
var finalOk = failed.length === 0
var detailMsg = []
for (var k = 0; k < details.length; k++) detailMsg.push(details[k].name + (details[k].skipped ? "⏭" : (details[k].ok ? "✅" : "❌")) + (details[k].msg ? "(" + displayMsg(details[k].msg) + ")" : ""))
var finalMsg = (finalOk ? "完全体任务完成" : "部分任务失败") + "" + detailMsg.join("")
var result = { uid: String(uid), ok: !!finalOk, msg: String(finalMsg || ""), time: nowIso(), startedAt: startedAt, source: source || "manual", runId: runId || "", tasks: details }
setLastResult(uid, result)
return { uid: uid, ok: finalOk, msg: finalMsg, raw: checkin, log: result }
}
function doCheckin(uid, cookie, source, runId) {
return doTaskChain(uid, cookie, source, runId)
}
function signAll(targetUid, allUsers, source, archiveAfterRun) {
source = source || (allUsers ? "cron" : "manual")
if (archiveAfterRun === undefined) archiveAfterRun = true
var startedAt = nowIso()
var runId = startedAt.replace(/[-:.TZ]/g, "").slice(0, 14) + "_" + source
var store = getStore()
var bind = getBindStore()
var remarks = remarksForStore(store)
var uids = visibleUids(store, bind, !!allUsers)
if (targetUid) uids = uids.filter(function (u) { return u === targetUid })
if (!uids.length) {
var emptyMsg = targetUid ? ("未找到 uid: " + targetUid) : "还没有保存启点读书CK"
setLastRunLog({ runId: runId, source: source, startedAt: startedAt, finishedAt: nowIso(), targetUid: targetUid || "", total: 0, okCount: 0, failCount: 0, message: emptyMsg, items: [] })
if (archiveAfterRun) archiveRunLog(source + "-after-sign")
return emptyMsg
}
var okCount = 0
var failCount = 0
var items = []
var accountReplies = []
var accountNotices = []
for (var i = 0; i < uids.length; i++) {
var uid = uids[i]
var prefix = "启点读书签到结果:\n"
var suffix = ""
if (remarks[uid]) suffix = "" + remarks[uid] + ""
try {
var r = doCheckin(uid, store[uid], source, runId)
if (r.ok) okCount++; else failCount++
items.push({ uid: String(uid), ok: !!r.ok, msg: String(r.msg || ""), time: r.log.time, remark: remarks[uid] || "" })
var msg = prefix + (r.ok ? "✅ " : "❌ ") + maskUid(uid) + suffix + "" + r.msg
accountReplies.push(msg)
accountNotices.push({ uid: String(uid), owner: bind[String(uid)] || "", msg: msg })
} catch (e) {
failCount++
var emsg = e && e.message ? e.message : String(e)
var er = { uid: String(uid), ok: false, msg: emsg, time: nowIso(), startedAt: startedAt, source: source, runId: runId, error: true, remark: remarks[uid] || "" }
setLastResult(uid, er)
items.push(er)
var errMsg = prefix + "❌ " + maskUid(uid) + suffix + "" + emsg
accountReplies.push(errMsg)
accountNotices.push({ uid: String(uid), owner: bind[String(uid)] || "", msg: errMsg })
}
}
var finishedAt = nowIso()
setLastRunLog({ runId: runId, source: source, startedAt: startedAt, finishedAt: finishedAt, targetUid: targetUid || "", allUsers: !!allUsers, total: uids.length, okCount: okCount, failCount: failCount, items: items })
var tail = []
tail.push("汇总:成功 " + okCount + " / 失败 " + failCount + " / 总计 " + uids.length)
tail.push("记录时间:" + finishedAt + "" + sourceName(source) + "")
var archiveLine = ""
if (archiveAfterRun) {
var ar = archiveRunLog(source + "-after-sign")
if (ar && ar.archived) { archiveLine = "日志已归档:" + ar.key; tail.push(archiveLine) }
}
if (allUsers && source === "cron") {
for (var pi = 0; pi < accountNotices.length; pi++) {
var notice = accountNotices[pi]
var body = notice.msg + "\n" + tail.join("\n")
if (!pushToUser(notice.owner, body)) debug("QDReader cron push skipped for uid " + notice.uid + ": empty owner or push unavailable")
}
return tail.join("\n")
}
if (accountReplies.length > 1) {
for (var ai = 0; ai < accountReplies.length; ai++) reply(accountReplies[ai])
return tail.join("\n")
}
return accountReplies[0] + "\n" + tail.join("\n")
}
function shortTime(iso) {
if (!iso) return "未知时间"
var s = String(iso)
var m = s.match(/^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2})/)
if (m) return m[1] + " " + m[2]
return beijingTime(iso).slice(0, 16)
}
function displayMsg(msg) {
msg = trim(msg || "无结果")
if (!msg) return "无结果"
msg = msg.replace(/\s+/g, " ")
if (msg.length > 60) msg = msg.slice(0, 57) + "..."
return msg
}
function accountName(uid, remarks) {
return maskUid(uid) + (remarks && remarks[uid] ? "[" + remarks[uid] + "]" : "")
}
function compactTaskMsg(msg) {
msg = displayMsg(msg)
msg = msg.replace(/^完全体任务完成:/, "")
msg = msg.replace(/^部分任务失败:/, "")
return msg
}
function formatResultLine(uid, r, remarks, emptyText) {
var name = accountName(uid, remarks)
if (!r) return "- " + name + ":⚪ " + (emptyText || "暂无执行记录")
var status = r.ok ? "✅" : "❌"
var line = "- " + name + "" + status + " " + compactTaskMsg(r.msg)
if (r.time) line += "\n 时间:" + shortTime(r.time)
return line
}
function formatRunSummary(log, label, key) {
log = log || {}
var lines = []
lines.push("批次:" + (key || log.runId || "最近") + "" + (label || "最近记录") + "")
lines.push("时间:" + shortTime(log.finishedAt) + " 来源:" + sourceName(log.source))
lines.push("统计:成功 " + (log.okCount || 0) + " / 失败 " + (log.failCount || 0) + " / 总计 " + (log.total || 0))
return lines
}
function queryStatus(targetUid) {
var store = getStore()
var bind = getBindStore()
var last = getLastResults()
var run = getLastRunLog()
var remarks = remarksForStore(store)
var uids = visibleUids(store, bind, false)
if (targetUid) {
if (uids.indexOf(targetUid) < 0) return store[targetUid] ? ("无权查询 uid: " + targetUid) : ("未找到 uid: " + targetUid)
uids = [targetUid]
}
if (!uids.length) return "当前用户还没有保存启点读书CK"
var archiveInfo = latestArchiveForVisible(uids)
var useArchive = archiveInfo && (!run || !run.finishedAt)
var lines = [targetUid ? "启点查询|单账号" : "启点查询|最近签到"]
if (useArchive) {
var arun = archiveInfo.log || {}
lines = lines.concat(formatRunSummary(arun, "归档", archiveInfo.key))
lines.push("明细:")
for (var ai = 0; ai < uids.length; ai++) lines.push(formatResultLine(uids[ai], archiveInfo.itemMap[String(uids[ai])], remarks, "归档中暂无该账号记录"))
return lines.join("\n")
}
if (!targetUid && run && run.finishedAt) lines = lines.concat(formatRunSummary(run, "最近", ""))
else if (run && run.finishedAt) lines.push("时间:" + shortTime(run.finishedAt) + " 来源:" + sourceName(run.source))
else lines.push("批次:暂无")
lines.push("明细:")
for (var i = 0; i < uids.length; i++) lines.push(formatResultLine(uids[i], last[uids[i]], remarks, "暂无执行记录"))
if (archiveInfo) lines.push("归档:" + archiveInfo.key)
return lines.join("\n")
}
function listenInput(timeoutMs) {
try { if (typeof sender !== "undefined" && sender && sender.listen) return trim(sender.listen(timeoutMs || 60000)) } catch (e) {}
return ""
}
function accountMenuText(store, bind) {
var uids = visibleUids(store || getStore(), bind || getBindStore(), false)
var remarks = remarksForStore(store || {})
var lines = ["启点账号管理", "账号列表如下:"]
if (!uids.length) lines.push("暂无账号,请回复 0 添加/更新账号")
else {
for (var i = 0; i < uids.length; i++) {
var uid = uids[i]
lines.push((i + 1) + "、" + maskUid(uid) + (remarks[uid] ? "[" + remarks[uid] + "]" : ""))
}
}
lines.push("0、添加/更新账号")
lines.push("-删除账号")
lines.push("q、退出")
return lines.join("\n")
}
function queryMenuText() {
return [
"启点查询(最近签到记录)",
"1. 查看最近签到记录",
"2. 查询单个账号最近记录",
"3. 立即执行签到",
"说明:附加任务开关在 启点账号 → 账号任务配置 中按账号管理",
"q. 退出",
"请回复序号"
].join("\n")
}
function accountListText(store, bind) {
var uids = visibleUids(store, bind, false)
var remarks = remarksForStore(store)
if (!uids.length) return "当前用户还没有保存启点读书CK"
var lines = ["启点账号:当前用户共 " + uids.length + " 个账号"]
for (var i = 0; i < uids.length; i++) {
var uid = uids[i]
lines.push(maskUid(uid) + (remarks[uid] ? "" + remarks[uid] + "" : ""))
}
lines.push("发送 启点签到 可执行签到")
return lines.join("\n")
}
function accountSelectText(store, bind, title) {
var uids = visibleUids(store, bind, false)
var remarks = remarksForStore(store)
if (!uids.length) return { text: "当前用户还没有保存启点读书CK", uids: [] }
var lines = [title || "请选择账号:"]
for (var i = 0; i < uids.length; i++) {
var uid = uids[i]
lines.push((i + 1) + ". " + maskUid(uid) + (remarks[uid] ? "" + remarks[uid] + "" : ""))
}
lines.push("q. 退出")
lines.push("请回复编号")
return { text: lines.join("\n"), uids: uids }
}
function chooseUid(store, bind, title) {
var sel = accountSelectText(store, bind, title)
reply(sel.text)
if (!sel.uids.length) return ""
var op = listenInput(60000)
if (!op || /^(q|退出|取消)$/i.test(op)) return ""
var n = parseInt(op, 10)
if (isNaN(n) || n < 1 || n > sel.uids.length) { reply("编号无效,已退出"); return "" }
return sel.uids[n - 1]
}
function maybePromptRemark(uid, store) {
if (!uid) return ""
reply("是否为该账号添加备注?回复备注名,发送 n/跳过 则不添加")
var remark = listenInput(60000)
if (!remark || /^(n|no|否|不|跳过|q|退出|取消)$/i.test(remark)) return ""
var remarks = store ? remarksForStore(store) : getRemarkStore()
remarks[uid] = remark.slice(0, 30)
setRemarkStore(remarks)
return "\n备注: " + remarks[uid]
}
function accountDetailText(uid, store, bind) {
var uids = visibleUids(store, bind, false)
if (uids.indexOf(uid) < 0) return store[uid] ? ("无权查看 uid: " + uid) : ("未找到 uid: " + uid)
var remarks = remarksForStore(store)
var last = getLastResults()
return [
"启点账号详情:",
"uid: " + maskUid(uid),
"备注: " + (remarks[uid] || "未设置"),
"cookie: " + maskCookie(store[uid]),
taskStatusLines(uid).replace(/\n回复:[\s\S]*$/, ""),
formatResultLine(uid, last[uid], remarks, "暂无执行记录"),
"修改备注请回复新备注名,发送 清空 可删除备注,发送 q 退出"
].join("\n")
}
function accountConfigText(uid, store, bind) {
var detail = accountDetailText(uid, store, bind)
if (/^(无权|未找到)/.test(detail)) return detail
return detail + "\n\n账号操作:\n1、修改备注\n2、任务配置\n3、立即签到该账号\n-删除该账号\nq、返回/退出"
}
function accountConfigMenu(uid, store, bind) {
if (!uid) return reply("未选择账号,已退出")
var cfg = accountConfigText(uid, store, bind)
reply(cfg)
if (/^(无权|未找到)/.test(cfg)) return
var op = listenInput(60000)
if (!op || /^(q|退出|取消|返回)$/i.test(op)) return reply("已退出")
if (/^(1|备注|改名|名称)$/i.test(op)) {
reply("请回复新备注名;发送 清空 可删除备注;q 退出")
var remark = listenInput(60000)
if (!remark || /^(q|退出|取消)$/i.test(remark)) return reply("已退出")
var remarks = remarksForStore(store)
if (/^(清空|删除|none)$/i.test(remark)) delete remarks[uid]; else remarks[uid] = remark.slice(0, 30)
setRemarkStore(remarks)
return reply("已更新备注:" + maskUid(uid) + (remarks[uid] ? "" + remarks[uid] + "" : ""))
}
if (/^(2|任务|配置|开关)$/i.test(op)) return taskMenuForUid(uid)
if (/^(3|签到|执行|立即)$/i.test(op)) return reply(signAll(uid, false, "menu"))
if (/^-1$/.test(op) || /^(删除|delete|del|rm)$/i.test(op)) return reply(handleDelete("删除 " + uid, store, bind))
return reply("未识别选项,已退出")
}
function accountDetailMenu(store, bind) {
var uid = chooseUid(store, bind, "请选择要查看/修改备注的账号:")
if (!uid) return reply("已退出")
return accountConfigMenu(uid, store, bind)
}
function accountMenu(store, bind) {
var uids = visibleUids(store, bind, false)
reply(accountMenuText(store, bind))
var op = listenInput(60000)
if (!op || /^(q|退出|取消)$/i.test(op)) return reply("已退出")
if (/^(0|a|add|添加|新增|更新)$/i.test(op)) {
reply("请在60秒内发送 QX 抓到的完整 Cookieq:退出)")
var ck = listenInput(60000)
if (!ck || /^(q|退出|取消)$/i.test(ck)) return reply("已退出")
var save = handleSaveCookie(ck, store, bind)
var remarkMsg = (save.ok && save.uids.length === 1) ? maybePromptRemark(save.uids[0], store) : ""
return reply(save.text + remarkMsg)
}
var n = parseInt(op, 10)
if (!isNaN(n) && String(n) === op.replace(/^\+/, "")) {
if (n > 0) {
if (n > uids.length) return reply("编号无效,已退出")
return accountConfigMenu(uids[n - 1], store, bind)
}
if (n < 0) {
var idx = Math.abs(n)
if (idx > uids.length) return reply("编号无效,已退出")
return reply(handleDelete("删除 " + uids[idx - 1], store, bind))
}
}
return reply("未识别选项,已退出")
}
function queryMenu() {
reply(queryMenuText())
var op = listenInput(60000)
if (!op || /^(q|退出|取消)$/i.test(op)) return reply("已退出")
if (/^(1|查看|查询|状态|结果)$/i.test(op)) return reply(queryStatus())
if (/^(2|单个|单号|账号)$/i.test(op)) {
var store = getStore()
var bind = getBindStore()
var uid = chooseUid(store, bind, "请选择要查询的账号:")
if (!uid) return reply("已退出")
return reply(queryStatus(uid))
}
if (/^(3|执行|签到|立即)$/i.test(op)) return reply(signAll("", false, "menu"))
return reply("未识别选项,已退出")
}
function handleDelete(arg, store, bind) {
var del = arg.match(/^(删除|delete|del|rm)\s+(\S+)/i)
if (!del) return null
var duid = del[2]
var uids = visibleUids(store, bind, false)
if (/^(all|全部|所有)$/i.test(duid)) {
for (var i = 0; i < uids.length; i++) { delete store[uids[i]]; delete bind[uids[i]] }
var remarks = remarksForStore(store); for (var j = 0; j < uids.length; j++) delete remarks[uids[j]]
var allPrefs = getTaskPrefs(); for (var p = 0; p < uids.length; p++) delete allPrefs[uids[p]]
setStore(store); setBindStore(bind); setRemarkStore(remarks); setTaskPrefs(allPrefs)
return "已删除全部当前用户绑定的启点读书CK,共 " + uids.length + " 个账号"
}
if (uids.indexOf(duid) < 0) return store[duid] ? ("无权删除 uid: " + duid) : ("未找到 uid: " + duid)
delete store[duid]; delete bind[duid]
var remarks = remarksForStore(store); delete remarks[duid]
var prefs = getTaskPrefs(); delete prefs[duid]
setStore(store); setBindStore(bind); setRemarkStore(remarks); setTaskPrefs(prefs)
return "已删除 uid: " + duid
}
function normalizeCookieInput(arg) {
var s = trim(arg)
var obj = safeJsonParse(s, null)
if (isObject(obj)) {
var out = []
for (var uid in obj) if (obj.hasOwnProperty(uid)) {
var realUid = isRealUid(uid)
var cookie = trim(obj[uid])
if (!realUid || !cookie) continue
out.push({ uid: realUid, cookie: cookie })
}
return out
}
return [{ uid: "", cookie: s }]
}
function handleSaveCookie(arg, store, bind) {
var entries = normalizeCookieInput(arg)
if (!entries.length) return { ok: false, text: "未识别到 Cookie。请发送:启点ck {\"uid\":\"cookie\"} 或 启点ck <QX抓到的完整cookie>", uids: [] }
var me = currentOwnerId()
if (!me) return { ok: false, text: "无法识别当前用户,已拒绝保存CK,避免错误绑定。请确认插件在聊天会话中触发;诊断: " + currentUserDebug(), uids: [] }
var lines = []
var savedUids = []
for (var i = 0; i < entries.length; i++) {
var cookie = entries[i].cookie
if (cookie.indexOf("=") < 0 || cookie.indexOf(";") < 0) return { ok: false, text: "未识别到 Cookie。请发送:启点ck {\"uid\":\"cookie\"} 或 启点ck <QX抓到的完整cookie>", uids: savedUids }
var parsedUid = extractUid(cookie)
var uid = entries[i].uid || parsedUid
if (!uid) return { ok: false, text: "无法从 Cookie 识别 uid。请确认 QX 抓的是启点读书福利中心 getlogininfo 请求,Cookie 内应包含 QDHeader 或 uid。", uids: savedUids }
var isNew = !store[uid]
var oldOwner = bind[uid] || ""
if (!store[uid] && oldOwner && oldOwner !== me) {
delete bind[uid]
oldOwner = ""
lines.push("检测到 uid: " + uid + " 只有旧绑定没有CK,已自动清理残留绑定")
}
if (store[uid] && oldOwner && oldOwner !== me && canRepairBinding(oldOwner, me)) {
lines.push("检测到 uid: " + uid + " 旧绑定来自历史兜底标识 " + maskUid(oldOwner) + ",已修复为当前用户")
oldOwner = ""
}
if (store[uid] && oldOwner && me && oldOwner !== me) {
lines.push("无权更新 uid: " + uid + "(该账号已绑定其他用户)")
continue
}
var changed = store[uid] !== cookie
store[uid] = cookie
bind[uid] = me
savedUids.push(uid)
lines.push("启点读书CK" + (isNew ? "新增" : (changed ? "更新" : "未变化")) + "成功")
lines.push("uid: " + uid)
if (entries.length > 1 && parsedUid && parsedUid !== uid) lines.push("提示: JSON uid 与 Cookie 解析 uid 不一致,已按 JSON uid 保存")
}
setStore(store); setBindStore(bind)
lines.push(me ? ("绑定用户: " + maskUid(me)) : "绑定用户: 未识别")
lines.push("账号数: " + visibleUids(store, bind, false).length)
lines.push("cookie: " + maskCookie(entries[0].cookie))
lines.push("签到:发送 启点签到")
return { ok: savedUids.length > 0, text: lines.join("\n"), uids: savedUids }
}
function main() {
var text = contentText()
var isCronRun = !text
var cmd = isCronRun ? cronMode() : commandName(text)
var arg = isCronRun ? "" : trim(stripCommand(text).replace(/^(sign|archive)\s*/i, ""))
if (!arg && cmd !== "sign" && cmd !== "archive" && cmd !== "account" && cmd !== "status") { reply(usage()); return }
if (/^(帮助|help|h)$/i.test(arg)) { reply(usage()); return }
var store = getStore()
var bind = getBindStore()
if (cmd === "archive" || /^(归档|日志归档|archive)(?:\s+|$)/i.test(arg)) {
reply(formatArchiveReply(archiveRunLog(isCronRun ? "midnight-archive" : "manual-archive")))
return
}
if (cmd === "sign" || /^(签到|sign|run)(?:\s+|$)/i.test(arg)) {
var target = trim(arg.replace(/^(签到|sign|run)\s*/i, ""))
var out = signAll(target, isCronRun, isCronRun ? "cron" : "manual")
if (!(isCronRun && getBool(CRON_SILENT_KEY, false) && out === "还没有保存启点读书CK")) reply(out)
return
}
if (cmd === "account") { accountMenu(store, bind); return }
if (cmd === "status") { queryMenu(); return }
if (cmd === "ck" && /^(查询|状态|status|list|账号|管理|任务|配置)$/i.test(arg)) { reply("请使用一级菜单:启点账号 或 启点查询;任务配置在 启点账号 → 账号任务配置;快速提交CK:启点ck <cookie>"); return }
var delMsg = handleDelete(arg, store, bind)
if (delMsg !== null) { reply(delMsg); return }
if (cmd === "ck") {
var save = handleSaveCookie(arg, store, bind)
if (save.ok && save.uids.length === 1) save.text += maybePromptRemark(save.uids[0], store)
reply(save.text)
return
}
var fallbackSave = handleSaveCookie(arg, store, bind)
reply(fallbackSave.text)
}
main()