167 lines
6.0 KiB
JavaScript
167 lines
6.0 KiB
JavaScript
//[open_source: true]
|
||
//[create_at: 2026-05-17 02:50:00]
|
||
//[title: 起点读书签到]
|
||
//[author: Hermes]
|
||
//[service: BOSS]
|
||
//[class: 工具类]
|
||
//[version: 1.0.0]
|
||
//[platform: web,qq,wx,tg,tb]
|
||
//[public: false]
|
||
//[price: 0]
|
||
//[disable: false]
|
||
//[description: 起点读书(QDReader) Autman 路由插件:接收 Quantumult X 重写上传的 Cookie,按 uid 自动新增/更新;也可查看 Cookie 状态。实际每日签到建议仍由 QX 定时运行仓库内 qdreader.upstream.js,或把 QDREADER_COOKIE JSON 同步到 Node/青龙环境执行。]
|
||
//[router: /qdreader/cookie]
|
||
//[method: get]
|
||
//[method: post]
|
||
//[param: {"required":false,"key":"otto.qdreader_token","bool":false,"placeholder":"访问Token","name":"接口鉴权Token","desc":"外部请求携带 access_token 或 token;留空则不校验"}]
|
||
//[param: {"required":false,"key":"otto.qdreader_cookie_json","bool":false,"placeholder":"{\"uid\":\"cookie\"}","name":"QDReader Cookie JSON","desc":"由 QX 重写自动维护,JSON Map 格式"}]
|
||
|
||
var STORE_KEY = "qdreader_cookie_json"
|
||
|
||
function replyJSON(obj) {
|
||
if (typeof response === "function") response(obj)
|
||
else if (typeof sender !== "undefined" && sender.reply) sender.reply(obj)
|
||
else if (typeof sendText === "function") sendText(JSON.stringify(obj))
|
||
else console.log(JSON.stringify(obj))
|
||
}
|
||
|
||
function fail(code, msg, data) {
|
||
replyJSON({ ok: false, status: "fail", retcode: code, msg: msg, data: data || {} })
|
||
}
|
||
|
||
function ok(data, msg) {
|
||
replyJSON({ ok: true, status: "ok", retcode: 0, msg: msg || "success", data: data || {} })
|
||
}
|
||
|
||
function safeJsonParse(s, def) {
|
||
if (!s) return 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 getHeader(obj, name) {
|
||
if (!obj) return ""
|
||
var lower = String(name).toLowerCase()
|
||
for (var k in obj) if (String(k).toLowerCase() === lower) return obj[k]
|
||
return ""
|
||
}
|
||
|
||
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 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)
|
||
if (!decoded) return ""
|
||
var parts = decoded.split("|")
|
||
return String(parts[0] || "").trim()
|
||
}
|
||
|
||
function extractUid(cookie, body) {
|
||
var jar = parseCookieString(cookie)
|
||
var uid = uidFromQDHeader(jar.QDHeader || jar.qdheader || jar.QDHEADER || "")
|
||
if (uid) return uid
|
||
if (jar.uid) return String(jar.uid)
|
||
var obj = safeJsonParse(body, null)
|
||
try {
|
||
uid = obj && obj.Data && obj.Data.UserInfo && (obj.Data.UserInfo.UserId || obj.Data.UserInfo.userId)
|
||
if (uid) return String(uid)
|
||
} catch (e) {}
|
||
return ""
|
||
}
|
||
|
||
function readRequest() {
|
||
var method = typeof getMethod === "function" ? getMethod() : "get"
|
||
var params = typeof getRouterParams === "function" ? (getRouterParams() || {}) : {}
|
||
var body = {}
|
||
var raw = ""
|
||
if (String(method).toLowerCase() === "post") {
|
||
raw = typeof getRouterData === "function" ? (getRouterData() || "") : ""
|
||
body = safeJsonParse(raw, {})
|
||
}
|
||
return { method: method, params: params, body: body, raw: raw }
|
||
}
|
||
|
||
var req = readRequest()
|
||
var token = req.body.access_token || req.body.token || req.params.access_token || req.params.token || ""
|
||
var configuredToken = get("qdreader_token") || ""
|
||
if (configuredToken && configuredToken !== token) {
|
||
fail(-1, "token错误")
|
||
return
|
||
}
|
||
|
||
var action = req.body.action || req.params.action || "status"
|
||
var cookie = req.body.cookie || req.body.ck || req.params.cookie || req.params.ck || ""
|
||
var uid = req.body.uid || req.params.uid || ""
|
||
var responseBody = req.body.response_body || req.body.body || ""
|
||
|
||
if (action === "status") {
|
||
var store = getStore()
|
||
ok({ count: Object.keys(store).length, uids: Object.keys(store), envName: "QDREADER_COOKIE" }, "起点读书 Cookie 接收插件已启用")
|
||
return
|
||
}
|
||
|
||
if (action === "delete") {
|
||
var storeDel = getStore()
|
||
if (!uid) { fail(-2, "uid不能为空"); return }
|
||
var existed = !!storeDel[uid]
|
||
delete storeDel[uid]
|
||
var rawDel = setStore(storeDel)
|
||
ok({ uid: uid, existed: existed, count: Object.keys(storeDel).length, qdreader_cookie: rawDel }, existed ? "已删除" : "原本不存在")
|
||
return
|
||
}
|
||
|
||
if (action === "replace_all") {
|
||
var incoming = req.body.cookies || req.body.qdreader_cookie || req.body.QDREADER_COOKIE || req.params.cookies || ""
|
||
var obj = typeof incoming === "object" ? incoming : safeJsonParse(incoming, null)
|
||
if (!obj || typeof obj !== "object" || Array.isArray(obj)) { fail(-3, "cookies 必须是 JSON Map"); return }
|
||
var rawAll = setStore(obj)
|
||
ok({ count: Object.keys(obj).length, uids: Object.keys(obj), qdreader_cookie: rawAll }, "已整体替换")
|
||
return
|
||
}
|
||
|
||
// default: update cookie
|
||
if (!cookie) {
|
||
fail(-4, "cookie不能为空", { usage: "POST JSON: {action:'update', uid:'可选', cookie:'Cookie字符串', response_body:'可选'}" })
|
||
return
|
||
}
|
||
uid = uid || extractUid(cookie, responseBody)
|
||
if (!uid) {
|
||
fail(-5, "无法识别 uid:Cookie 中缺少 QDHeader/uid,或 response_body 无 UserInfo.UserId")
|
||
return
|
||
}
|
||
var store = getStore()
|
||
var isNew = !store[uid]
|
||
var changed = store[uid] !== cookie
|
||
store[uid] = cookie
|
||
var rawStore = setStore(store)
|
||
ok({ uid: uid, is_new: isNew, changed: changed, count: Object.keys(store).length, envName: "QDREADER_COOKIE", qdreader_cookie: rawStore }, (isNew ? "新增" : (changed ? "更新" : "未变化")) + "成功")
|