fix(qdreader): archive daily sign logs at midnight
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 启点读书 QDReader 签到 Autman 插件
|
||||
|
||||
QDReader/起点读书 Autman 签到插件。用户侧触发词统一使用“启点”,避免平台关键词麻烦;QX 只负责抓取 Cookie,账号管理、执行查询和签到都在 Autman 插件内完成。
|
||||
QDReader/起点读书 Autman 签到插件。用户侧触发词统一使用“启点”,避免平台关键词麻烦;QX 只负责抓取 Cookie,账号管理、执行查询、日志归档和签到都在 Autman 插件内完成。
|
||||
|
||||
## 文件位置
|
||||
|
||||
@@ -93,8 +93,9 @@ hermes_qidian
|
||||
qdreader_cookie_json # uid -> full cookie
|
||||
qdreader_user_bind_json # uid -> sender.getUserID(),用户绑定索引
|
||||
qdreader_remark_json # uid -> 备注名
|
||||
qdreader_last_result_json # uid -> 单账号最后执行结果 JSON 字符串
|
||||
qdreader_last_run_json # 最近一次执行批次日志、来源和汇总
|
||||
qdreader_last_result_json # uid -> 单账号当日/当前批次结果 JSON 字符串(每日零点归档前清空)
|
||||
qdreader_last_run_json # 当日/当前批次日志、来源和汇总(每日零点归档前清空)
|
||||
qdreader_archive_json # yyyy-mm-dd -> 已归档批次日志,用于判断历史签到结果
|
||||
qdreader_sign_api # 签名网关
|
||||
qdreader_retry_count # 请求失败重试次数
|
||||
qdreader_cron_silent # 定时无账号静默
|
||||
@@ -127,20 +128,22 @@ set(key, value)
|
||||
- 同时提取 `qdh/QDHeader/guid/qimei/qimei36` 供签名网关使用,其中 `qdh` 只取 Cookie 的 `qdh/QDH`,不再用 `QDHeader` 顶替,避免签名网关 `decodeQDHAuto` 解码失败。
|
||||
2. `POST <qdreader_sign_api>/sign` 获取请求签名头。默认签名网关基址是 `https://api.120399.xyz/qdreader`。
|
||||
3. `POST https://h5.if.qidian.com/argus/api/v3/checkin/checkin` 完成签到。
|
||||
4. 写入单账号最后结果 `qdreader_last_result_json`,包括 `ok/msg/time/source/runId`。
|
||||
5. 写入批次日志 `qdreader_last_run_json`,包括触发来源、开始/结束时间、成功/失败/总数和每账号结果;即使账号执行异常也会记录失败日志。
|
||||
6. 汇总每个账号签到结果。
|
||||
4. 写入单账号当日结果 `qdreader_last_result_json`,包括 `ok/msg/time/source/runId`。
|
||||
5. 写入当日批次日志 `qdreader_last_run_json`,包括触发来源、开始/结束时间、成功/失败/总数和每账号结果;即使账号执行异常也会记录失败日志。
|
||||
6. 每日 00:00 的 cron 触发时,先把上一份 `qdreader_last_run_json` 按完成日期写入 `qdreader_archive_json`,再清空 `qdreader_last_result_json` / `qdreader_last_run_json`,然后执行新一天签到并反馈结果。
|
||||
7. 汇总每个账号签到结果。
|
||||
|
||||
## 查询数据来源
|
||||
|
||||
`启点查询` 不请求起点实时接口,只读取最后执行日志:
|
||||
|
||||
```text
|
||||
qdreader_last_run_json # 最近一次执行批次
|
||||
qdreader_last_result_json # 每个 uid 最近一次执行结果
|
||||
qdreader_last_run_json # 当日/当前批次
|
||||
qdreader_last_result_json # 每个 uid 当日/当前批次结果
|
||||
qdreader_archive_json # 每日零点归档后的历史批次
|
||||
```
|
||||
|
||||
因此“查询”语义是“最近签到记录/最后执行日志”。刚添加但从未执行过的账号会显示“暂无执行记录”。
|
||||
因此“查询”语义是“最近签到记录/最后执行日志”。刚添加但从未执行过的账号会显示“暂无执行记录”。如果当日日志已被零点任务清空,`启点查询` 会回退到最近归档,根据归档中的账号结果判断昨日/历史签到情况并反馈给用户。
|
||||
|
||||
## 本地验证
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//[author: Hermes]
|
||||
//[service: BOSS]
|
||||
//[class: 工具类]
|
||||
//[version: 2.4.2]
|
||||
//[version: 2.5.0]
|
||||
//[platform: web,qq,wx,tg,tb,fs,we]
|
||||
//[public: false]
|
||||
//[price: 0]
|
||||
@@ -14,7 +14,7 @@
|
||||
//[rule: ^qdreader(.*)$]
|
||||
//[admin: true]
|
||||
//[priority: 600]
|
||||
//[cron: 15 7 * * *]
|
||||
//[cron: 0 0 * * *]
|
||||
|
||||
|
||||
var BUCKET = "hermes_qidian"
|
||||
@@ -26,6 +26,7 @@ var RETRY_KEY = "qdreader_retry_count"
|
||||
var CRON_SILENT_KEY = "qdreader_cron_silent"
|
||||
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"
|
||||
@@ -259,7 +260,7 @@ function contentText() {
|
||||
}
|
||||
function usage() {
|
||||
return [
|
||||
"启点读书签到插件 v2.4.2",
|
||||
"启点读书签到插件 v2.5.0",
|
||||
"【一级菜单】",
|
||||
"账号管理:启点账号",
|
||||
"执行查询:启点查询",
|
||||
@@ -269,7 +270,7 @@ function usage() {
|
||||
"指定签到:启点签到 <uid>",
|
||||
"【配置】",
|
||||
"签名网关:Autman 参数 hermes_qidian.qdreader_sign_api,默认 " + DEFAULT_SIGN_API,
|
||||
"定时:每日 07:15 自动执行;QX 只负责抓 cookie。"
|
||||
"定时:每日 00:00 自动执行、归档上一日日志并清空当日日志;QX 只负责抓 cookie。"
|
||||
].join("\n")
|
||||
}
|
||||
function stripCommand(text) {
|
||||
@@ -405,8 +406,50 @@ function getLastResults() {
|
||||
}
|
||||
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 d = iso ? new Date(iso) : new Date()
|
||||
if (!d || isNaN(d.getTime())) d = new Date()
|
||||
var y = d.getFullYear()
|
||||
var m = String(d.getMonth() + 1); if (m.length < 2) m = "0" + m
|
||||
var day = String(d.getDate()); if (day.length < 2) day = "0" + day
|
||||
return y + "-" + m + "-" + day
|
||||
}
|
||||
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 sourceName(source) {
|
||||
if (source === "cron") return "定时任务"
|
||||
if (source === "cron") return "定时任务/零点任务"
|
||||
if (source === "menu") return "菜单立即执行"
|
||||
if (source === "manual") return "手动签到"
|
||||
return source || "未知"
|
||||
@@ -467,6 +510,12 @@ function formatStatusLine(uid, last, remarks) {
|
||||
if (!r) return "- " + name + ":暂无执行记录"
|
||||
return "- " + name + ":" + (r.ok ? "✅ " : "❌ ") + (r.msg || "无结果") + (r.time ? " @ " + r.time : "")
|
||||
}
|
||||
function formatArchiveStatusLine(uid, archiveInfo, remarks) {
|
||||
var name = maskUid(uid) + (remarks && remarks[uid] ? "(" + remarks[uid] + ")" : "")
|
||||
var r = archiveInfo && archiveInfo.itemMap ? archiveInfo.itemMap[String(uid)] : null
|
||||
if (!r) return "- " + name + ":归档中暂无该账号记录"
|
||||
return "- " + name + ":" + (r.ok ? "✅ " : "❌ ") + (r.msg || "无结果") + (r.time ? " @ " + r.time : "")
|
||||
}
|
||||
function queryStatus(targetUid) {
|
||||
var store = getStore()
|
||||
var bind = getBindStore()
|
||||
@@ -480,9 +529,19 @@ function queryStatus(targetUid) {
|
||||
}
|
||||
if (!uids.length) return "当前用户还没有保存启点读书CK"
|
||||
var title = targetUid ? "启点单账号最近签到记录:" : "启点最近签到记录:"
|
||||
var archiveInfo = latestArchiveForVisible(uids)
|
||||
var useArchive = archiveInfo && (!run || !run.finishedAt)
|
||||
if (useArchive) title = targetUid ? "启点单账号最近归档签到记录:" : "启点最近归档签到记录:"
|
||||
var lines = [title]
|
||||
if (!targetUid && run && run.finishedAt) lines.push("最近批次:" + sourceName(run.source) + " @ " + run.finishedAt + ";成功 " + (run.okCount || 0) + " / 失败 " + (run.failCount || 0) + " / 总计 " + (run.total || 0))
|
||||
for (var i = 0; i < uids.length; i++) lines.push(formatStatusLine(uids[i], last, remarks))
|
||||
if (useArchive) {
|
||||
var arun = archiveInfo.log || {}
|
||||
lines.push("最近归档:" + archiveInfo.key + ";" + sourceName(arun.source) + " @ " + (arun.finishedAt || "未知时间") + ";成功 " + (arun.okCount || 0) + " / 失败 " + (arun.failCount || 0) + " / 总计 " + (arun.total || 0))
|
||||
for (var ai = 0; ai < uids.length; ai++) lines.push(formatArchiveStatusLine(uids[ai], archiveInfo, remarks))
|
||||
} else {
|
||||
if (!targetUid && run && run.finishedAt) lines.push("最近批次:" + sourceName(run.source) + " @ " + run.finishedAt + ";成功 " + (run.okCount || 0) + " / 失败 " + (run.failCount || 0) + " / 总计 " + (run.total || 0))
|
||||
for (var i = 0; i < uids.length; i++) lines.push(formatStatusLine(uids[i], last, remarks))
|
||||
if (archiveInfo) lines.push("最近归档:" + archiveInfo.key + "(发送启点查询可按归档判断昨日签到)")
|
||||
}
|
||||
return lines.join("\n")
|
||||
}
|
||||
|
||||
@@ -709,6 +768,7 @@ function main() {
|
||||
var store = getStore()
|
||||
var bind = getBindStore()
|
||||
if (cmd === "sign" || /^(签到|sign|run)(?:\s+|$)/i.test(arg)) {
|
||||
if (isCronRun) archiveRunLog("midnight-before-sign")
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user