fix: push qdreader cron results to account owners

This commit is contained in:
2026-05-23 13:54:33 +08:00
parent 7388cf8eb5
commit e03ad733f4
3 changed files with 90 additions and 17 deletions
+35 -5
View File
@@ -4,7 +4,7 @@
//[author: Hermes]
//[service: BOSS]
//[class: 工具类]
// [version: 2.15.2]
// [version: 2.15.3]
// [platform: web,qq,wx,tg,tb,fs,we]
// [public: false]
// [price: 0]
@@ -91,6 +91,22 @@ function reply(msg) {
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) {
@@ -286,7 +302,7 @@ function contentText() {
}
function usage() {
return [
"启点读书签到插件 v2.15.2",
"启点读书签到插件 v2.15.3",
"【一级菜单】",
"账号管理:启点账号",
"执行查询:启点查询",
@@ -874,6 +890,7 @@ function signAll(targetUid, allUsers, source, archiveAfterRun) {
var failCount = 0
var items = []
var accountReplies = []
var accountNotices = []
for (var i = 0; i < uids.length; i++) {
var uid = uids[i]
var prefix = "启点读书签到结果:\n"
@@ -883,14 +900,18 @@ function signAll(targetUid, allUsers, source, archiveAfterRun) {
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] || "" })
accountReplies.push(prefix + (r.ok ? "✅ " : "❌ ") + maskUid(uid) + suffix + "" + r.msg)
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)
accountReplies.push(prefix + "❌ " + maskUid(uid) + suffix + "" + emsg)
var errMsg = prefix + "❌ " + maskUid(uid) + suffix + "" + emsg
accountReplies.push(errMsg)
accountNotices.push({ uid: String(uid), owner: bind[String(uid)] || "", msg: errMsg })
}
}
var finishedAt = nowIso()
@@ -898,9 +919,18 @@ function signAll(targetUid, allUsers, source, archiveAfterRun) {
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) tail.push("日志已归档:" + ar.key)
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])
+35 -5
View File
@@ -4,7 +4,7 @@
//[author: Hermes]
//[service: BOSS]
//[class: 工具类]
// [version: 2.15.2]
// [version: 2.15.3]
// [platform: web,qq,wx,tg,tb,fs,we]
// [public: false]
// [price: 0]
@@ -91,6 +91,22 @@ function reply(msg) {
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) {
@@ -286,7 +302,7 @@ function contentText() {
}
function usage() {
return [
"启点读书签到插件 v2.15.2",
"启点读书签到插件 v2.15.3",
"【一级菜单】",
"账号管理:启点账号",
"执行查询:启点查询",
@@ -874,6 +890,7 @@ function signAll(targetUid, allUsers, source, archiveAfterRun) {
var failCount = 0
var items = []
var accountReplies = []
var accountNotices = []
for (var i = 0; i < uids.length; i++) {
var uid = uids[i]
var prefix = "启点读书签到结果:\n"
@@ -883,14 +900,18 @@ function signAll(targetUid, allUsers, source, archiveAfterRun) {
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] || "" })
accountReplies.push(prefix + (r.ok ? "✅ " : "❌ ") + maskUid(uid) + suffix + "" + r.msg)
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)
accountReplies.push(prefix + "❌ " + maskUid(uid) + suffix + "" + emsg)
var errMsg = prefix + "❌ " + maskUid(uid) + suffix + "" + emsg
accountReplies.push(errMsg)
accountNotices.push({ uid: String(uid), owner: bind[String(uid)] || "", msg: errMsg })
}
}
var finishedAt = nowIso()
@@ -898,9 +919,18 @@ function signAll(targetUid, allUsers, source, archiveAfterRun) {
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) tail.push("日志已归档:" + ar.key)
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])
+20 -7
View File
@@ -20,12 +20,16 @@ function fixedDateClass(iso) {
function runPlugin({ pluginCode = code, filename = pluginPath, content = '', store = {}, bucketStore = {}, requests = [], userId = 'user-a', listens = [], senderOverrides = {}, globals = {}, admin = true } = {}) {
const replies = [];
const pushes = [];
const calls = [];
let requestIndex = 0;
let listenIndex = 0;
const baseSender = {
reply: (msg) => replies.push(String(msg)),
push: (msg) => replies.push(typeof msg === 'string' ? msg : String((msg && (msg.content || msg.message || msg.text)) || JSON.stringify(msg))),
push: (msg) => {
pushes.push(msg);
replies.push(typeof msg === 'string' ? msg : String((msg && (msg.content || msg.message || msg.text)) || JSON.stringify(msg)));
},
sendText: (msg) => replies.push(String(msg)),
getMessage: () => content,
getContent: () => content,
@@ -38,7 +42,10 @@ function runPlugin({ pluginCode = code, filename = pluginPath, content = '', sto
Buffer,
sender: { ...baseSender, ...senderOverrides },
sendText: (msg) => replies.push(String(msg)),
push: (msg) => replies.push(typeof msg === 'string' ? msg : String((msg && (msg.content || msg.message || msg.text)) || JSON.stringify(msg))),
push: (msg) => {
pushes.push(msg);
replies.push(typeof msg === 'string' ? msg : String((msg && (msg.content || msg.message || msg.text)) || JSON.stringify(msg)));
},
GetUserID: () => userId,
GetContent: () => content,
getContent: () => content,
@@ -60,7 +67,7 @@ function runPlugin({ pluginCode = code, filename = pluginPath, content = '', sto
};
vm.createContext(ctx);
vm.runInContext(pluginCode, ctx, { filename });
return { replies, calls, store, bucketStore };
return { replies, pushes, calls, store, bucketStore };
}
const cookie = 'uid=12345; QDHeader=MTIzNDV8eA==; qdh=qdh; ywguid=guid; qimei=qimei; qimei36=qimei36; sessionyw=s; appId=10';
@@ -769,9 +776,13 @@ test('cron detection uses ImType cron/fake and does not consume input as command
},
});
assert.equal(r.calls.length, 4, imType);
assert.equal(r.replies.length, 1, imType);
assert.match(r.replies[0], /汇总:成功 1 \/ 失败 0 \/ 总计 1/, imType);
assert.match(r.replies[0], /定时任务\/零点任务/, imType);
assert.equal(r.replies.length, 2, imType);
assert.equal(r.pushes.length, 1, imType);
assert.equal(r.pushes[0].userID, 'user-a', imType);
assert.match(r.pushes[0].content, /汇总:成功 1 \/ 失败 0 \/ 总计 1/, imType);
assert.match(r.pushes[0].content, /日志已归档:2026-05-19/, imType);
assert.match(r.replies.join('\n'), /汇总:成功 1 \/ 失败 0 \/ 总计 1/, imType);
assert.match(r.replies.join('\n'), /定时任务\/零点任务/, imType);
}
});
@@ -786,7 +797,9 @@ test('manual sign only runs current user accounts while cron runs all accounts',
const cron = runPlugin({ content: '', store: { ...store }, userId: 'cron', globals: { Date: fixedDateClass('2026-05-19T07:01:00.000+08:00') } });
assert.equal(cron.calls.length, 8);
assert.equal(cron.replies.length, 3);
assert.match(cron.replies[2], /总计 2/);
assert.equal(cron.pushes.length, 2);
assert.deepEqual(cron.pushes.map((p) => p.userID).sort(), ['user-a', 'user-b']);
assert.match(cron.replies.join('\n'), /总计 2/);
});
test('unbound global cookies are not visible or usable by normal users', () => {