@sunjuntao/dsh-qqbot 0.1.7 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +428 -222
- package/lib/index.js +335 -30
- package/package.json +4 -6
package/lib/client.js
CHANGED
|
@@ -112,7 +112,7 @@ var cn = Object.freeze({
|
|
|
112
112
|
"session.defaultWorkspace": "默认工作区(~/.dsh/file)",
|
|
113
113
|
"session.modelHint": "这个机器人会话使用的模型;留空则跟随宿主默认模型。切换后已有会话需要重置才会生效。",
|
|
114
114
|
"session.followDefaultModel": "跟随默认模型",
|
|
115
|
-
"session.presetHint": "决定机器人的行事风格与可用工具。@
|
|
115
|
+
"session.presetHint": "决定机器人的行事风格与可用工具。@ 机器人、单聊、以及群里非 @ 的回复都走这个 Preset,群聊与私聊能力完全一致,均可调用工具。",
|
|
116
116
|
// ── status ──
|
|
117
117
|
"status.disabled": "已停用",
|
|
118
118
|
"status.connected": "已连接",
|
|
@@ -217,7 +217,7 @@ var cn = Object.freeze({
|
|
|
217
217
|
"policy.title": "消息与回复策略",
|
|
218
218
|
"policy.hint": "控制这个机器人「听哪些消息、怎么回」,每个机器人彼此独立。所有开关改完立即生效,不需要重启。",
|
|
219
219
|
"policy.fullGroupReply": "群全量消息回复",
|
|
220
|
-
"policy.fullGroupReplyHint": "开启后,群里没有 @
|
|
220
|
+
"policy.fullGroupReplyHint": "开启后,群里没有 @ 机器人的消息也会参与价值评分,达到阈值才回复(与 @、单聊一样可调用工具);@ 机器人的消息始终回复。关闭后,机器人只处理 @ 它的群消息。",
|
|
221
221
|
"policy.acceptDm": "接受单聊消息",
|
|
222
222
|
"policy.acceptDmHint": "是否响应 QQ 私聊(C2C)消息。关闭后机器人只处理群消息,私聊一律忽略。",
|
|
223
223
|
"policy.respondBots": "响应机器人消息",
|
|
@@ -232,8 +232,8 @@ var cn = Object.freeze({
|
|
|
232
232
|
"policy.quoteScopeAll": "all(群聊全部回复)",
|
|
233
233
|
"policy.quoteLimit": "引用字数上限",
|
|
234
234
|
"policy.quoteLimitHint": "文本引用最多显示多少字(仅主动消息回退为文本引用时使用;原生引用气泡由 QQ 客户端自行截断),超出部分以省略号结尾。",
|
|
235
|
-
"policy.chatPreset": "群聊聊天 Preset",
|
|
236
|
-
"policy.chatPresetHint": "
|
|
235
|
+
"policy.chatPreset": "群聊聊天 Preset(已停用)",
|
|
236
|
+
"policy.chatPresetHint": "群聊现已与单聊能力完全一致(均可调用工具),不再单独限制群全量消息。该选项已停用,仅供历史配置识别,新配置请勿使用。",
|
|
237
237
|
"policy.followAgentPreset": "跟随 Agent Preset",
|
|
238
238
|
"policy.secretEnv": "AppSecret 凭据引用(secretEnv)",
|
|
239
239
|
"policy.secretEnvShort": "AppSecret 凭据引用",
|
|
@@ -419,6 +419,16 @@ var cn = Object.freeze({
|
|
|
419
419
|
"sched.enabledToast": "已启用,下次运行 {0}",
|
|
420
420
|
"sched.enabledNoNext": "已启用,但算不出下次运行时间,请检查触发配置",
|
|
421
421
|
"sched.disabledToast": "已禁用,不再执行",
|
|
422
|
+
"sched.bg.create": "新建定时消息",
|
|
423
|
+
"sched.bg.update": "修改定时消息",
|
|
424
|
+
"sched.bg.remove": "删除定时消息",
|
|
425
|
+
"sched.bg.enable": "启用定时消息",
|
|
426
|
+
"sched.bg.disable": "禁用定时消息",
|
|
427
|
+
"sched.bg.runOnce": "测试执行定时消息",
|
|
428
|
+
"sched.bg.send": "定时消息发送",
|
|
429
|
+
"sched.bg.sent": "已发送",
|
|
430
|
+
"sched.bg.skipped": "已跳过发送",
|
|
431
|
+
"sched.bg.done": "操作成功",
|
|
422
432
|
"sched.noNextRun": "算不出下次运行,请检查触发配置",
|
|
423
433
|
"sched.commandSource": "命令来源",
|
|
424
434
|
"sched.commandManual": "手写命令",
|
|
@@ -608,6 +618,7 @@ var cn = Object.freeze({
|
|
|
608
618
|
"update.noNewVersion": "暂无新版本(当前 v{0} 已是最新)",
|
|
609
619
|
"update.found": "发现新版本 v{0},正在自动更新…",
|
|
610
620
|
"update.updatedTo": "已自动更新到 v{0}(备份于安装目录 .update-backup/),重启 DSH 后生效",
|
|
621
|
+
"update.keepOpen": "更新尚未执行完,请勿关闭本面板",
|
|
611
622
|
// ── replyLocale ──
|
|
612
623
|
"replyLocale.label": "回复语言(replyLocale)",
|
|
613
624
|
"replyLocale.labelShort": "回复语言",
|
|
@@ -659,7 +670,6 @@ var cn = Object.freeze({
|
|
|
659
670
|
"group.summaryMarkdown": "Markdown {0}",
|
|
660
671
|
"group.summaryMemory": "记忆 {0}",
|
|
661
672
|
"group.summaryBannedWords": "敏感词 {0} 个",
|
|
662
|
-
"group.summaryChatPreset": "聊天 Preset {0}",
|
|
663
673
|
// ── tz ──
|
|
664
674
|
"tz.shanghai": "中国标准时间 · Asia/Shanghai(UTC+8)",
|
|
665
675
|
"tz.hongkong": "中国香港 · Asia/Hong_Kong(UTC+8)",
|
|
@@ -727,7 +737,6 @@ var cn = Object.freeze({
|
|
|
727
737
|
// ── unit ──
|
|
728
738
|
"unit.minutes": "{0} 分钟",
|
|
729
739
|
"unit.seconds": "{0} 秒",
|
|
730
|
-
"panel.title": "QQ 定时消息",
|
|
731
740
|
"panel.subtitle": "管理定时消息:增删改查与测试发送",
|
|
732
741
|
"panel.scope": "机器人",
|
|
733
742
|
"panel.allBots": "所有机器人",
|
|
@@ -753,8 +762,6 @@ var cn = Object.freeze({
|
|
|
753
762
|
"panel.bot": "机器人",
|
|
754
763
|
"panel.archiveAllHint": "所有机器人模式仅显示聚合条数;选择单个机器人可展开预览记录",
|
|
755
764
|
"panel.tabId": "面板",
|
|
756
|
-
"ui.panelSwitchTitle": "右侧面板「QQ 定时消息」入口",
|
|
757
|
-
"ui.panelSwitchDesc": "开启后,编辑器右侧栏显示 QQ 定时消息管理入口;关闭即隐藏(仍要求至少一个机器人已连接,且改动即时生效)",
|
|
758
765
|
"sched.view": "查看",
|
|
759
766
|
"sched.back": "返回",
|
|
760
767
|
"sched.detailTitle": "执行逻辑详情",
|
|
@@ -848,7 +855,7 @@ var en = Object.freeze({
|
|
|
848
855
|
"session.defaultWorkspace": "Default workspace (~/.dsh/file)",
|
|
849
856
|
"session.modelHint": "The model used by this bot’s sessions; leave empty to follow the host default. Existing sessions need a reset for the change to apply.",
|
|
850
857
|
"session.followDefaultModel": "Follow default model",
|
|
851
|
-
"session.presetHint": "Sets the bot’s behavior style and available tools. @-mentions
|
|
858
|
+
"session.presetHint": "Sets the bot’s behavior style and available tools. @-mentions, direct messages, and non-@ group replies all use this Preset — group and direct chat now have identical capabilities and can both run tools.",
|
|
852
859
|
// ── status ──
|
|
853
860
|
"status.disabled": "Disabled",
|
|
854
861
|
"status.connected": "Connected",
|
|
@@ -953,7 +960,7 @@ var en = Object.freeze({
|
|
|
953
960
|
"policy.title": "Message & reply policy",
|
|
954
961
|
"policy.hint": "Controls which messages this bot listens to and how it replies; each bot is independent. Every switch takes effect immediately — no restart needed.",
|
|
955
962
|
"policy.fullGroupReply": "Reply to all group messages",
|
|
956
|
-
"policy.fullGroupReplyHint": "When on, group messages that do not @ the bot also go through value scoring and get a reply only above the threshold; @-mentions are always answered
|
|
963
|
+
"policy.fullGroupReplyHint": "When on, group messages that do not @ the bot also go through value scoring and get a reply only above the threshold (same tool capability as @-mentions and DMs); @-mentions are always answered. When off, only messages that @ the bot are processed.",
|
|
957
964
|
"policy.acceptDm": "Accept direct messages",
|
|
958
965
|
"policy.acceptDmHint": "Whether to respond to QQ direct (C2C) messages. When off, the bot processes group messages only and ignores DMs.",
|
|
959
966
|
"policy.respondBots": "Respond to bot messages",
|
|
@@ -968,8 +975,8 @@ var en = Object.freeze({
|
|
|
968
975
|
"policy.quoteScopeAll": "all (every group reply)",
|
|
969
976
|
"policy.quoteLimit": "Quote preview length",
|
|
970
977
|
"policy.quoteLimitHint": "How many characters of the quoted text to show when a reply falls back to a text quote (proactive messages only; native quote cards are truncated by the QQ client itself); longer text ends with an ellipsis.",
|
|
971
|
-
"policy.chatPreset": "Group chat preset",
|
|
972
|
-
"policy.chatPresetHint": "
|
|
978
|
+
"policy.chatPreset": "Group chat preset (retired)",
|
|
979
|
+
"policy.chatPresetHint": "Group chat now has identical capabilities to direct chat (both can run tools); the separate group-chat preset restriction is removed. This option is retired for historical config compatibility only — do not use it for new configs.",
|
|
973
980
|
"policy.followAgentPreset": "Follow Agent Preset",
|
|
974
981
|
"policy.secretEnv": "AppSecret credential reference (secretEnv)",
|
|
975
982
|
"policy.secretEnvShort": "AppSecret credential reference",
|
|
@@ -1155,6 +1162,16 @@ var en = Object.freeze({
|
|
|
1155
1162
|
"sched.enabledToast": "Enabled — next run {0}",
|
|
1156
1163
|
"sched.enabledNoNext": "Enabled, but the next run time cannot be computed. Check the trigger settings.",
|
|
1157
1164
|
"sched.disabledToast": "Disabled — will no longer run",
|
|
1165
|
+
"sched.bg.create": "Create scheduled message",
|
|
1166
|
+
"sched.bg.update": "Update scheduled message",
|
|
1167
|
+
"sched.bg.remove": "Delete scheduled message",
|
|
1168
|
+
"sched.bg.enable": "Enable scheduled message",
|
|
1169
|
+
"sched.bg.disable": "Disable scheduled message",
|
|
1170
|
+
"sched.bg.runOnce": "Test-run scheduled message",
|
|
1171
|
+
"sched.bg.send": "Scheduled message send",
|
|
1172
|
+
"sched.bg.sent": "Sent",
|
|
1173
|
+
"sched.bg.skipped": "Send skipped",
|
|
1174
|
+
"sched.bg.done": "Done",
|
|
1158
1175
|
"sched.noNextRun": "Next run time cannot be computed — check the trigger settings",
|
|
1159
1176
|
"sched.commandSource": "Command source",
|
|
1160
1177
|
"sched.commandManual": "Manual command",
|
|
@@ -1344,6 +1361,7 @@ var en = Object.freeze({
|
|
|
1344
1361
|
"update.noNewVersion": "No new version (v{0} is the latest)",
|
|
1345
1362
|
"update.found": "New version v{0} found; updating automatically…",
|
|
1346
1363
|
"update.updatedTo": "Updated to v{0} (old files backed up in .update-backup/ inside the install directory). Restart DSH to take effect.",
|
|
1364
|
+
"update.keepOpen": "Update still running — do not close this panel",
|
|
1347
1365
|
// ── replyLocale ──
|
|
1348
1366
|
"replyLocale.label": "Reply language (replyLocale)",
|
|
1349
1367
|
"replyLocale.labelShort": "Reply language",
|
|
@@ -1395,7 +1413,6 @@ var en = Object.freeze({
|
|
|
1395
1413
|
"group.summaryMarkdown": "Markdown {0}",
|
|
1396
1414
|
"group.summaryMemory": "Memory {0}",
|
|
1397
1415
|
"group.summaryBannedWords": "{0} banned words",
|
|
1398
|
-
"group.summaryChatPreset": "Chat preset {0}",
|
|
1399
1416
|
// ── tz ──
|
|
1400
1417
|
"tz.shanghai": "China Standard Time · Asia/Shanghai (UTC+8)",
|
|
1401
1418
|
"tz.hongkong": "Hong Kong, China · Asia/Hong_Kong (UTC+8)",
|
|
@@ -1463,7 +1480,6 @@ var en = Object.freeze({
|
|
|
1463
1480
|
// ── unit ──
|
|
1464
1481
|
"unit.minutes": "{0} min",
|
|
1465
1482
|
"unit.seconds": "{0} s",
|
|
1466
|
-
"panel.title": "QQ Schedules",
|
|
1467
1483
|
"panel.subtitle": "Manage scheduled messages: create, edit, delete and test",
|
|
1468
1484
|
"panel.scope": "Bot",
|
|
1469
1485
|
"panel.allBots": "All bots",
|
|
@@ -1489,8 +1505,6 @@ var en = Object.freeze({
|
|
|
1489
1505
|
"panel.bot": "Bot",
|
|
1490
1506
|
"panel.archiveAllHint": "All-bots mode shows aggregated counts only; pick a single bot to expand and preview records",
|
|
1491
1507
|
"panel.tabId": "Tab",
|
|
1492
|
-
"ui.panelSwitchTitle": 'Right panel "QQ Scheduled Messages" entry',
|
|
1493
|
-
"ui.panelSwitchDesc": "When on, the QQ scheduled-messages manager appears in the right sidebar; off hides it (still requires at least one connected bot, and takes effect immediately)",
|
|
1494
1508
|
"sched.view": "View",
|
|
1495
1509
|
"sched.back": "Back",
|
|
1496
1510
|
"sched.detailTitle": "Execution details",
|
|
@@ -1741,8 +1755,52 @@ function QqBotGlyph(props) {
|
|
|
1741
1755
|
h("rect", { x: 4.5, y: 6, width: 15, height: 13, rx: 4.5, fill: "currentColor", mask: `url(#qbot-face-${props.uid})` })
|
|
1742
1756
|
);
|
|
1743
1757
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1758
|
+
|
|
1759
|
+
// src/client/settings-nav-icon.ts
|
|
1760
|
+
var QBOT_SETTINGS_NAV_MARKER = "data-qbot-settings-nav";
|
|
1761
|
+
var NAV_ICON_MASK = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.5v2.5'/%3E%3Ccircle cx='12' cy='2.5' r='.9' fill='black' stroke='none'/%3E%3Crect x='5' y='6' width='14' height='12' rx='3.5'/%3E%3Ccircle cx='9.4' cy='11.5' r='.9' fill='black' stroke='none'/%3E%3Ccircle cx='14.6' cy='11.5' r='.9' fill='black' stroke='none'/%3E%3Cpath d='M9.5 14.8q2.5 1.8 5 0'/%3E%3C/svg%3E";
|
|
1762
|
+
var QBOT_SETTINGS_NAV_CSS = `
|
|
1763
|
+
[${QBOT_SETTINGS_NAV_MARKER}] > svg:first-child {
|
|
1764
|
+
display: none;
|
|
1765
|
+
}
|
|
1766
|
+
[${QBOT_SETTINGS_NAV_MARKER}]::before {
|
|
1767
|
+
content: '';
|
|
1768
|
+
flex: none;
|
|
1769
|
+
width: 16px;
|
|
1770
|
+
height: 16px;
|
|
1771
|
+
background: currentColor;
|
|
1772
|
+
-webkit-mask: url("${NAV_ICON_MASK}") center / contain no-repeat;
|
|
1773
|
+
mask: url("${NAV_ICON_MASK}") center / contain no-repeat;
|
|
1774
|
+
}
|
|
1775
|
+
`;
|
|
1776
|
+
function registerQbotSettingsNavIcon(label) {
|
|
1777
|
+
let disposed = false;
|
|
1778
|
+
const sync = () => {
|
|
1779
|
+
if (disposed) return;
|
|
1780
|
+
const currentLabel = label().trim();
|
|
1781
|
+
const buttons = document.querySelectorAll(
|
|
1782
|
+
'[role="dialog"] nav button'
|
|
1783
|
+
);
|
|
1784
|
+
for (const button of Array.from(buttons)) {
|
|
1785
|
+
const matches2 = currentLabel.length > 0 && button.textContent?.trim() === currentLabel;
|
|
1786
|
+
if (matches2) button.setAttribute(QBOT_SETTINGS_NAV_MARKER, "");
|
|
1787
|
+
else button.removeAttribute(QBOT_SETTINGS_NAV_MARKER);
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
sync();
|
|
1791
|
+
const observer = new MutationObserver(sync);
|
|
1792
|
+
observer.observe(document.body, {
|
|
1793
|
+
childList: true,
|
|
1794
|
+
subtree: true,
|
|
1795
|
+
characterData: true
|
|
1796
|
+
});
|
|
1797
|
+
return () => {
|
|
1798
|
+
disposed = true;
|
|
1799
|
+
observer.disconnect();
|
|
1800
|
+
document.querySelectorAll(`[${QBOT_SETTINGS_NAV_MARKER}]`).forEach((element) => {
|
|
1801
|
+
element.removeAttribute(QBOT_SETTINGS_NAV_MARKER);
|
|
1802
|
+
});
|
|
1803
|
+
};
|
|
1746
1804
|
}
|
|
1747
1805
|
|
|
1748
1806
|
// src/client/ui.tsx
|
|
@@ -1990,7 +2048,7 @@ var CSS_TEXT = `
|
|
|
1990
2048
|
.qbot-listHeading { min-height: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 0; }
|
|
1991
2049
|
.qbot-listHeading h3 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: normal; font-weight: 650; }
|
|
1992
2050
|
.qbot-listTitle { min-width: 0; display: inline-flex; align-items: center; gap: 10px; }
|
|
1993
|
-
.qbot-hint { margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 1.6; }
|
|
2051
|
+
.qbot-hint { margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 1.6; overflow-wrap: anywhere; }
|
|
1994
2052
|
|
|
1995
2053
|
/* ── 通知(dim-statusNotice / info 变体)──────────────────────────────── */
|
|
1996
2054
|
.qbot-statusNotice { display: flex; align-items: flex-start; gap: 10px; padding: 13px 15px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 22%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 10px; color: var(--dsw-alias-state-error-primary, #d54941); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 8%, var(--dsw-alias-bg-layer-1, #fff)); font-size: 13px; line-height: 1.5; }
|
|
@@ -1999,6 +2057,10 @@ var CSS_TEXT = `
|
|
|
1999
2057
|
.qbot-noticeCount { margin-left: auto; flex: none; align-self: flex-start; font-variant-numeric: tabular-nums; font-weight: 600; font-size: 12px; line-height: 1.4; padding: 1px 9px; border-radius: 999px; white-space: nowrap; background: color-mix(in srgb, var(--qbot-business) 14%, var(--dsw-alias-bg-layer-1, #fff)); border: 1px solid color-mix(in srgb, var(--qbot-business) 28%, var(--dsw-alias-border-l2, #dfe1e5)); }
|
|
2000
2058
|
/* 页面级更新提示(qbot-page 是普通块布局,无 channelPage 的 flex gap,需自带下边距与面板隔开) */
|
|
2001
2059
|
.qbot-updateNotice { margin: 0 0 18px; }
|
|
2060
|
+
/* 进行中:不吃 8s 淡出动画(更新可能耗时较久),并改为竖排以突出「请勿关闭」警示行。 */
|
|
2061
|
+
.qbot-updateNotice.is-busy { flex-direction: column; align-items: stretch; gap: 6px; animation: none; }
|
|
2062
|
+
.qbot-updateMsg { min-width: 0; overflow-wrap: anywhere; }
|
|
2063
|
+
.qbot-updateKeepOpen { color: var(--dsw-alias-state-error-primary, #d54941); font-size: 13px; font-weight: 650; line-height: 1.5; }
|
|
2002
2064
|
|
|
2003
2065
|
/* ── 机器人卡片(dim-botCard)─────────────────────────────────────────── */
|
|
2004
2066
|
.qbot-botList { min-width: 0; width: 100%; max-width: 100%; display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; }
|
|
@@ -2159,7 +2221,7 @@ select.qbot-input { cursor: pointer; font-family: inherit; }
|
|
|
2159
2221
|
.qbot-dirRow svg { flex: none; width: 16px; height: 16px; color: var(--qbot-business); }
|
|
2160
2222
|
.qbot-modalState { display: flex; align-items: center; justify-content: center; gap: 10px; min-height: 96px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; }
|
|
2161
2223
|
.qbot-modalError { color: var(--dsw-alias-state-error-primary, #d54941); }
|
|
2162
|
-
.qbot-modalFoot { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding:
|
|
2224
|
+
.qbot-modalFoot { min-width: 0; flex: none; display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px clamp(10px, 3.2%, 20px) 14px; font-variant-numeric: tabular-nums; }
|
|
2163
2225
|
/* 弹窗底部内联错误:常驻可见,不随弹窗内容滚动而移出视口 */
|
|
2164
2226
|
.qbot-footError { margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.6; overflow-wrap: anywhere; }
|
|
2165
2227
|
/* 宽弹窗:定时消息 / 消息归档 列表内容较长,放宽上限 */
|
|
@@ -2172,7 +2234,7 @@ select.qbot-input { cursor: pointer; font-family: inherit; }
|
|
|
2172
2234
|
.qbot-schedTab:hover { background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
|
|
2173
2235
|
.qbot-schedTab.is-active { background: var(--dsw-alias-bg-layer-1, #fff); border-color: var(--dsw-alias-border-l2, #e5e6eb); color: var(--qbot-blue); font-weight: 600; box-shadow: 0 1px 2px rgb(31 35 41 / 6%); }
|
|
2174
2236
|
/* 右侧面板「定时消息」工具栏:机器人作用域 + 刷新(自适应面板宽度、可换行) */
|
|
2175
|
-
.qbot-schedToolbar { flex: none; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; padding: 10px clamp(8px, 3%, 14px); border-bottom: 1px solid var(--dsw-alias-border-l1, #eef0f3); background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
|
|
2237
|
+
.qbot-schedToolbar { flex: none; position: sticky; top: 0; z-index: 2; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; padding: 10px clamp(8px, 3%, 14px); border-bottom: 1px solid var(--dsw-alias-border-l1, #eef0f3); background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
|
|
2176
2238
|
.qbot-fieldInline { min-width: 0; flex: 1 1 auto; display: inline-flex; align-items: center; gap: 8px; }
|
|
2177
2239
|
.qbot-fieldInlineLabel { flex: none; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-secondary, #646a73); }
|
|
2178
2240
|
.qbot-schedToolbar .qbot-settingSelect { flex: 1 1 auto; min-width: 0; max-width: 240px; }
|
|
@@ -3322,7 +3384,6 @@ function buildDraft(overrides, openid) {
|
|
|
3322
3384
|
memoryEnabled: tri(ov.memoryEnabled),
|
|
3323
3385
|
replyChunkChars: num(ov.replyChunkChars),
|
|
3324
3386
|
maxRepliesPerMessage: num(ov.maxRepliesPerMessage),
|
|
3325
|
-
agentPresetChat: typeof ov.agentPresetChat === "string" ? ov.agentPresetChat : "",
|
|
3326
3387
|
bannedWords: Array.isArray(ov.bannedWords) ? ov.bannedWords.join(", ") : ""
|
|
3327
3388
|
};
|
|
3328
3389
|
}
|
|
@@ -3358,7 +3419,6 @@ function OverrideDialog(props) {
|
|
|
3358
3419
|
if (d.memoryEnabled) ov.memoryEnabled = d.memoryEnabled === "on";
|
|
3359
3420
|
if (d.replyChunkChars !== "") ov.replyChunkChars = Number(d.replyChunkChars);
|
|
3360
3421
|
if (d.maxRepliesPerMessage !== "") ov.maxRepliesPerMessage = Number(d.maxRepliesPerMessage);
|
|
3361
|
-
if (d.agentPresetChat.trim()) ov.agentPresetChat = d.agentPresetChat.trim();
|
|
3362
3422
|
if (d.bannedWords.trim()) {
|
|
3363
3423
|
ov.bannedWords = d.bannedWords.split(/[,,]/).map((w) => w.trim()).filter(Boolean);
|
|
3364
3424
|
}
|
|
@@ -3546,9 +3606,8 @@ function OverrideDialog(props) {
|
|
|
3546
3606
|
h("option", { value: "off" }, t("conn.disable"))
|
|
3547
3607
|
)
|
|
3548
3608
|
),
|
|
3549
|
-
// 长期记忆(memoryEnabled
|
|
3550
|
-
|
|
3551
|
-
// 草稿仍读取/回写这两个字段,避免保存时丢掉配置文件里已设置的值。
|
|
3609
|
+
// 长期记忆(memoryEnabled)已从界面移除(默认常开,仅 bots.json 可配);
|
|
3610
|
+
// 草稿仍读取/回写该字段,避免保存时丢掉配置文件里已设置的值。
|
|
3552
3611
|
editRow(
|
|
3553
3612
|
t("feature.bannedWords"),
|
|
3554
3613
|
t("group.bannedWordsHint"),
|
|
@@ -3755,6 +3814,32 @@ function nextCronRun(expr, tz, now) {
|
|
|
3755
3814
|
return null;
|
|
3756
3815
|
}
|
|
3757
3816
|
|
|
3817
|
+
// src/client/workbench/bg-tasks.ts
|
|
3818
|
+
function getBackgroundTasks() {
|
|
3819
|
+
const wb = window.__dshFileWorkbenchWorkbench__;
|
|
3820
|
+
if (wb && typeof wb.apiVersion === "number" && wb.apiVersion >= 3 && wb.backgroundTasks && typeof wb.backgroundTasks.start === "function") {
|
|
3821
|
+
return wb.backgroundTasks;
|
|
3822
|
+
}
|
|
3823
|
+
return null;
|
|
3824
|
+
}
|
|
3825
|
+
async function runAsBackgroundTask(label, fn, detail) {
|
|
3826
|
+
const api = getBackgroundTasks();
|
|
3827
|
+
if (!api) {
|
|
3828
|
+
await fn();
|
|
3829
|
+
return;
|
|
3830
|
+
}
|
|
3831
|
+
const task = api.start(label, detail ? { detail } : void 0);
|
|
3832
|
+
let outcome = { ok: true };
|
|
3833
|
+
try {
|
|
3834
|
+
const r = await fn();
|
|
3835
|
+
if (r && typeof r === "object") outcome = r;
|
|
3836
|
+
} catch (e) {
|
|
3837
|
+
outcome = { ok: false, message: e instanceof Error ? e.message : String(e) };
|
|
3838
|
+
}
|
|
3839
|
+
if (outcome.ok) task.done(outcome.message);
|
|
3840
|
+
else task.fail(outcome.message);
|
|
3841
|
+
}
|
|
3842
|
+
|
|
3758
3843
|
// src/client/schedule-manager.tsx
|
|
3759
3844
|
var WEEKDAYS = [
|
|
3760
3845
|
{ v: 0, label: t("weekday.sun"), en: "Su" },
|
|
@@ -3800,6 +3885,12 @@ function localTimeZone() {
|
|
|
3800
3885
|
return DEFAULT_TZ;
|
|
3801
3886
|
}
|
|
3802
3887
|
}
|
|
3888
|
+
function schedBgDetail(entry) {
|
|
3889
|
+
if (!entry) return void 0;
|
|
3890
|
+
const s = entry.mode === "tool" ? String(entry.command ?? entry.content ?? "") : String(entry.content ?? "");
|
|
3891
|
+
const text = s.trim();
|
|
3892
|
+
return text ? text.slice(0, 60) : void 0;
|
|
3893
|
+
}
|
|
3803
3894
|
function cmdTemplates() {
|
|
3804
3895
|
return [
|
|
3805
3896
|
{ label: "Python", cmd: "python C:/scripts/report.py" },
|
|
@@ -3894,8 +3985,12 @@ function blankEntry(detailAppId) {
|
|
|
3894
3985
|
};
|
|
3895
3986
|
}
|
|
3896
3987
|
function ScheduleManager(props) {
|
|
3897
|
-
const { rpcCall, detailAppId, forceScope, onClose } = props;
|
|
3988
|
+
const { rpcCall, detailAppId, forceScope, onClose, toast } = props;
|
|
3898
3989
|
useLocale();
|
|
3990
|
+
const notify = (ok, text) => {
|
|
3991
|
+
if (toast) toast(ok ? "ok" : "error", text);
|
|
3992
|
+
else setSchedNotice({ ok, text });
|
|
3993
|
+
};
|
|
3899
3994
|
const [scheduleModal, setScheduleModal] = React7.useState({
|
|
3900
3995
|
loading: true,
|
|
3901
3996
|
error: "",
|
|
@@ -3935,10 +4030,16 @@ function ScheduleManager(props) {
|
|
|
3935
4030
|
}
|
|
3936
4031
|
};
|
|
3937
4032
|
React7.useEffect(() => {
|
|
3938
|
-
if (!schedNotice) return;
|
|
4033
|
+
if (!schedNotice || toast) return;
|
|
3939
4034
|
const timer = setTimeout(() => setSchedNotice(null), 8e3);
|
|
3940
4035
|
return () => clearTimeout(timer);
|
|
3941
4036
|
}, [schedNotice]);
|
|
4037
|
+
React7.useEffect(() => {
|
|
4038
|
+
if (scheduleModal.editError && toast) toast("error", scheduleModal.editError);
|
|
4039
|
+
}, [scheduleModal.editError]);
|
|
4040
|
+
React7.useEffect(() => {
|
|
4041
|
+
if (scheduleModal.error && toast) toast("error", scheduleModal.error);
|
|
4042
|
+
}, [scheduleModal.error]);
|
|
3942
4043
|
React7.useEffect(() => {
|
|
3943
4044
|
const s = forceScope ?? "current";
|
|
3944
4045
|
setScheduleViewing(null);
|
|
@@ -4112,22 +4213,39 @@ function ScheduleManager(props) {
|
|
|
4112
4213
|
} : {},
|
|
4113
4214
|
...e.appId ? { appId: e.appId } : detailAppId ? { appId: detailAppId } : {}
|
|
4114
4215
|
};
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4216
|
+
await runAsBackgroundTask(
|
|
4217
|
+
e.id ? t("sched.bg.update") : t("sched.bg.create"),
|
|
4218
|
+
async () => {
|
|
4219
|
+
const res = await rpcCall("schedule.add", payload);
|
|
4220
|
+
if (res.ok) {
|
|
4221
|
+
setScheduleModal((prev) => prev ? { ...prev, editing: null, saving: false } : prev);
|
|
4222
|
+
await loadSchedules(scheduleModal?.botScope ?? "current");
|
|
4223
|
+
return { ok: true, message: t("sched.bg.done") };
|
|
4224
|
+
}
|
|
4225
|
+
setScheduleModal((prev) => prev ? { ...prev, saving: false, editError: errText(res.error) } : prev);
|
|
4226
|
+
return { ok: false, message: errText(res.error) };
|
|
4227
|
+
},
|
|
4228
|
+
String(e.content ?? "").trim().slice(0, 60) || void 0
|
|
4229
|
+
);
|
|
4122
4230
|
};
|
|
4123
4231
|
const removeSchedule = async (id) => {
|
|
4124
4232
|
if (!await confirmDlg({ message: t("sched.removeTaskConfirm"), danger: true })) return;
|
|
4125
4233
|
setScheduleRemoving(id);
|
|
4126
4234
|
const scopeNow = scheduleModal?.botScope ?? "current";
|
|
4127
4235
|
try {
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4236
|
+
await runAsBackgroundTask(
|
|
4237
|
+
t("sched.bg.remove"),
|
|
4238
|
+
async () => {
|
|
4239
|
+
const res = await rpcCall("schedule.remove", { id });
|
|
4240
|
+
if (res.ok) {
|
|
4241
|
+
await loadSchedules(scopeNow);
|
|
4242
|
+
return { ok: true, message: t("sched.bg.done") };
|
|
4243
|
+
}
|
|
4244
|
+
notify(false, errText(res.error));
|
|
4245
|
+
return { ok: false, message: errText(res.error) };
|
|
4246
|
+
},
|
|
4247
|
+
schedBgDetail(scheduleModal?.items.find((x) => String(x.id) === id))
|
|
4248
|
+
);
|
|
4131
4249
|
} finally {
|
|
4132
4250
|
setScheduleRemoving("");
|
|
4133
4251
|
}
|
|
@@ -4144,15 +4262,25 @@ function ScheduleManager(props) {
|
|
|
4144
4262
|
setScheduleToggling(id);
|
|
4145
4263
|
const scopeNow = scheduleModal?.botScope ?? "current";
|
|
4146
4264
|
try {
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4265
|
+
await runAsBackgroundTask(
|
|
4266
|
+
currentlyEnabled ? t("sched.bg.disable") : t("sched.bg.enable"),
|
|
4267
|
+
async () => {
|
|
4268
|
+
const res = await rpcCall("schedule.setEnabled", { id, enabled: !currentlyEnabled });
|
|
4269
|
+
if (!res.ok) {
|
|
4270
|
+
notify(false, errText(res.error));
|
|
4271
|
+
return { ok: false, message: errText(res.error) };
|
|
4272
|
+
}
|
|
4273
|
+
const s = val(res) ?? {};
|
|
4274
|
+
const nextAt = s.schedule?.nextRunAt ?? s.nextRunAt;
|
|
4275
|
+
notify(
|
|
4276
|
+
currentlyEnabled ? true : nextAt ? true : false,
|
|
4277
|
+
currentlyEnabled ? t("sched.disabledToast") : nextAt ? fmt("sched.enabledToast", formatTime(nextAt)) : t("sched.enabledNoNext")
|
|
4278
|
+
);
|
|
4279
|
+
await loadSchedules(scopeNow);
|
|
4280
|
+
return { ok: true, message: t("sched.bg.done") };
|
|
4281
|
+
},
|
|
4282
|
+
schedBgDetail(scheduleModal?.items.find((x) => String(x.id) === id))
|
|
4283
|
+
);
|
|
4156
4284
|
} finally {
|
|
4157
4285
|
setScheduleToggling("");
|
|
4158
4286
|
}
|
|
@@ -4161,13 +4289,23 @@ function ScheduleManager(props) {
|
|
|
4161
4289
|
if (!id) return;
|
|
4162
4290
|
setSchedNotice(null);
|
|
4163
4291
|
setScheduleTesting(id);
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4292
|
+
try {
|
|
4293
|
+
await runAsBackgroundTask(
|
|
4294
|
+
t("sched.bg.runOnce"),
|
|
4295
|
+
async () => {
|
|
4296
|
+
const res = await rpcCall("schedule.runOnce", { id });
|
|
4297
|
+
if (res.ok) {
|
|
4298
|
+
const v = val(res) ?? {};
|
|
4299
|
+
notify(true, typeof v.message === "string" ? v.message : t("sched.testSent"));
|
|
4300
|
+
return { ok: true, message: typeof v.message === "string" ? v.message : t("sched.testSent") };
|
|
4301
|
+
}
|
|
4302
|
+
notify(false, errText(res.error));
|
|
4303
|
+
return { ok: false, message: errText(res.error) };
|
|
4304
|
+
},
|
|
4305
|
+
schedBgDetail(scheduleModal?.items.find((x) => String(x.id) === id))
|
|
4306
|
+
);
|
|
4307
|
+
} finally {
|
|
4308
|
+
setScheduleTesting("");
|
|
4171
4309
|
}
|
|
4172
4310
|
await loadSchedules(scheduleModal?.botScope ?? "current");
|
|
4173
4311
|
};
|
|
@@ -4642,7 +4780,7 @@ function ScheduleManager(props) {
|
|
|
4642
4780
|
),
|
|
4643
4781
|
h("div", { className: "qbot-schedDetailBody" }, h("div", { className: "qbot-flow" }, ...buildFlow(e)))
|
|
4644
4782
|
);
|
|
4645
|
-
const ROOT = { display: "flex", flexDirection: "column",
|
|
4783
|
+
const ROOT = { display: "flex", flexDirection: "column", flex: "1 1 auto", minHeight: 0, boxSizing: "border-box" };
|
|
4646
4784
|
return h(
|
|
4647
4785
|
"div",
|
|
4648
4786
|
{ className: "qbot-schedRoot", style: ROOT },
|
|
@@ -4653,6 +4791,7 @@ function ScheduleManager(props) {
|
|
|
4653
4791
|
h(
|
|
4654
4792
|
"div",
|
|
4655
4793
|
{ className: "qbot-editForm qbot-schedForm" },
|
|
4794
|
+
scheduleModal.editError && !toast ? h("div", { key: "editErr", className: "qbot-schedNotice is-error" }, scheduleModal.editError) : null,
|
|
4656
4795
|
section(
|
|
4657
4796
|
t("sched.form.stepRecipient"),
|
|
4658
4797
|
t("sched.form.stepRecipientHint"),
|
|
@@ -4805,7 +4944,7 @@ function ScheduleManager(props) {
|
|
|
4805
4944
|
"div",
|
|
4806
4945
|
{ className: `qbot-modalList${scheduleModal.loading && scheduleModal.items.length > 0 ? " is-refreshing" : ""}` },
|
|
4807
4946
|
[
|
|
4808
|
-
schedNotice ? h(
|
|
4947
|
+
schedNotice && !toast ? h(
|
|
4809
4948
|
"div",
|
|
4810
4949
|
{ key: "notice", className: `qbot-schedNotice is-autoHide${schedNotice.ok ? "" : " is-error"}` },
|
|
4811
4950
|
schedNotice.text
|
|
@@ -4847,7 +4986,11 @@ function ScheduleManager(props) {
|
|
|
4847
4986
|
h(
|
|
4848
4987
|
"div",
|
|
4849
4988
|
{ key: "scroll", className: "qbot-modalListScroll" },
|
|
4850
|
-
scheduleModal.error ?
|
|
4989
|
+
scheduleModal.error ? h(
|
|
4990
|
+
"div",
|
|
4991
|
+
{ key: "listErr", className: "qbot-schedNotice is-error" },
|
|
4992
|
+
scheduleModal.error
|
|
4993
|
+
) : scheduleModal.items.length === 0 ? scheduleModal.loading ? h(
|
|
4851
4994
|
"div",
|
|
4852
4995
|
{ key: "loading", className: "qbot-modalState" },
|
|
4853
4996
|
h("span", { className: "qbot-spinner", "aria-hidden": "true" }),
|
|
@@ -5024,7 +5167,6 @@ function ScheduleManager(props) {
|
|
|
5024
5167
|
h(
|
|
5025
5168
|
"div",
|
|
5026
5169
|
{ className: "qbot-viewActions" },
|
|
5027
|
-
h("button", { className: "qbot-btn", type: "button", disabled: scheduleModal.loading, onClick: () => void loadSchedules(scheduleModal.botScope) }, t("common.refresh")),
|
|
5028
5170
|
onClose ? h("button", { className: "qbot-btn qbot-btnPrimary", type: "button", onClick: onClose }, t("common.close")) : null
|
|
5029
5171
|
)
|
|
5030
5172
|
) : null
|
|
@@ -5177,41 +5319,16 @@ function setRpcCall(fn) {
|
|
|
5177
5319
|
function getRpcCall() {
|
|
5178
5320
|
return rpcCallRef;
|
|
5179
5321
|
}
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
return () => botsListeners.delete(fn);
|
|
5184
|
-
}
|
|
5185
|
-
function notifyBotsChanged() {
|
|
5186
|
-
for (const fn of botsListeners) {
|
|
5187
|
-
try {
|
|
5188
|
-
fn();
|
|
5189
|
-
} catch {
|
|
5190
|
-
}
|
|
5191
|
-
}
|
|
5192
|
-
}
|
|
5193
|
-
var PANEL_VISIBLE_KEY = "dsh-qqbot.panel.visible";
|
|
5194
|
-
function getPanelVisible() {
|
|
5195
|
-
try {
|
|
5196
|
-
return localStorage.getItem(PANEL_VISIBLE_KEY) !== "0";
|
|
5197
|
-
} catch {
|
|
5198
|
-
return true;
|
|
5199
|
-
}
|
|
5200
|
-
}
|
|
5201
|
-
function setPanelVisible(v) {
|
|
5202
|
-
try {
|
|
5203
|
-
localStorage.setItem(PANEL_VISIBLE_KEY, v ? "1" : "0");
|
|
5204
|
-
} catch {
|
|
5205
|
-
}
|
|
5206
|
-
notifyBotsChanged();
|
|
5207
|
-
}
|
|
5322
|
+
|
|
5323
|
+
// src/client/workbench/schedule-view.tsx
|
|
5324
|
+
var import_client = require("react-dom/client");
|
|
5208
5325
|
|
|
5209
5326
|
// src/client/right-panel/ScheduleTab.tsx
|
|
5210
5327
|
var React9 = __toESM(require("react"), 1);
|
|
5211
5328
|
var root = {
|
|
5212
5329
|
display: "flex",
|
|
5213
5330
|
flexDirection: "column",
|
|
5214
|
-
|
|
5331
|
+
flex: "1 1 auto",
|
|
5215
5332
|
minHeight: 0,
|
|
5216
5333
|
boxSizing: "border-box",
|
|
5217
5334
|
color: "inherit"
|
|
@@ -5222,7 +5339,8 @@ var body = {
|
|
|
5222
5339
|
display: "flex",
|
|
5223
5340
|
flexDirection: "column"
|
|
5224
5341
|
};
|
|
5225
|
-
function ScheduleTab() {
|
|
5342
|
+
function ScheduleTab(props) {
|
|
5343
|
+
const { toast } = props ?? {};
|
|
5226
5344
|
useLocale();
|
|
5227
5345
|
const [bots, setBots] = React9.useState([]);
|
|
5228
5346
|
const [scopeAppId, setScopeAppId] = React9.useState("");
|
|
@@ -5294,35 +5412,197 @@ function ScheduleTab() {
|
|
|
5294
5412
|
botError ? h("div", { className: "qbot-schedPanelError" }, botError) : rpc ? h(
|
|
5295
5413
|
"div",
|
|
5296
5414
|
{ style: body, key: scopeAppId || "all" },
|
|
5297
|
-
h(ScheduleManager, { rpcCall: rpc, detailAppId, forceScope })
|
|
5415
|
+
h(ScheduleManager, { rpcCall: rpc, detailAppId, forceScope, toast })
|
|
5298
5416
|
) : h("div", { className: "qbot-modalState" }, t("panel.loading"))
|
|
5299
5417
|
);
|
|
5300
5418
|
}
|
|
5301
5419
|
|
|
5302
|
-
// src/client/
|
|
5303
|
-
var
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
}
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5420
|
+
// src/client/workbench/schedule-view.tsx
|
|
5421
|
+
var QQBOT_WORKBENCH_VIEW_ID = "@sunjuntao/dsh-qqbot.schedule";
|
|
5422
|
+
function getWorkbenchApi() {
|
|
5423
|
+
try {
|
|
5424
|
+
const w = window;
|
|
5425
|
+
const api = w.__dshFileWorkbenchWorkbench__;
|
|
5426
|
+
if (api && api.activityBar && typeof api.activityBar.register === "function") return api;
|
|
5427
|
+
} catch {
|
|
5428
|
+
}
|
|
5429
|
+
return null;
|
|
5430
|
+
}
|
|
5431
|
+
var installed = false;
|
|
5432
|
+
function installScheduleWorkbenchView() {
|
|
5433
|
+
if (installed) return;
|
|
5434
|
+
installed = true;
|
|
5435
|
+
const attempt = (n) => {
|
|
5436
|
+
const api = getWorkbenchApi();
|
|
5437
|
+
if (api) {
|
|
5438
|
+
api.activityBar.register({
|
|
5439
|
+
id: QQBOT_WORKBENCH_VIEW_ID,
|
|
5440
|
+
title: { zh: "QQ 定时消息", en: "QQ Scheduled Messages" },
|
|
5441
|
+
icon: "clock",
|
|
5442
|
+
order: 60,
|
|
5443
|
+
mount(el, ctx) {
|
|
5444
|
+
el.style.minHeight = "0";
|
|
5445
|
+
el.style.overflowY = "auto";
|
|
5446
|
+
el.style.overflowX = "hidden";
|
|
5447
|
+
el.style.position = "relative";
|
|
5448
|
+
el.style.boxSizing = "border-box";
|
|
5449
|
+
const onWheel = (e) => {
|
|
5450
|
+
if (!e.deltaY) return;
|
|
5451
|
+
const max = el.scrollHeight - el.clientHeight;
|
|
5452
|
+
if (max <= 1) return;
|
|
5453
|
+
const atTop = el.scrollTop <= 0;
|
|
5454
|
+
const atBottom = el.scrollTop >= max - 1;
|
|
5455
|
+
if (e.deltaY < 0 && atTop || e.deltaY > 0 && atBottom) return;
|
|
5456
|
+
const unit = e.deltaMode === 1 ? 18 : e.deltaMode === 2 ? el.clientHeight : 1;
|
|
5457
|
+
el.scrollTop += e.deltaY * unit;
|
|
5458
|
+
e.preventDefault();
|
|
5459
|
+
e.stopPropagation();
|
|
5460
|
+
};
|
|
5461
|
+
el.addEventListener("wheel", onWheel, { passive: false, capture: true });
|
|
5462
|
+
const KEY = "__qbotSchedReact__";
|
|
5463
|
+
const store = el;
|
|
5464
|
+
let entry = store[KEY];
|
|
5465
|
+
let container;
|
|
5466
|
+
let root2;
|
|
5467
|
+
if (entry && entry.root) {
|
|
5468
|
+
container = entry.container;
|
|
5469
|
+
root2 = entry.root;
|
|
5470
|
+
} else {
|
|
5471
|
+
container = document.createElement("div");
|
|
5472
|
+
container.className = "qbot-schedReactRoot";
|
|
5473
|
+
container.style.cssText = "min-height:100%;width:100%;box-sizing:border-box";
|
|
5474
|
+
el.appendChild(container);
|
|
5475
|
+
root2 = (0, import_client.createRoot)(container);
|
|
5476
|
+
store[KEY] = { root: root2, container };
|
|
5477
|
+
}
|
|
5478
|
+
root2.render(
|
|
5479
|
+
h(ScheduleTab, {
|
|
5480
|
+
toast: typeof ctx?.toast === "function" ? (lvl, msg) => ctx.toast(lvl, msg) : void 0
|
|
5481
|
+
})
|
|
5482
|
+
);
|
|
5483
|
+
return () => {
|
|
5484
|
+
el.removeEventListener("wheel", onWheel, { capture: true });
|
|
5485
|
+
try {
|
|
5486
|
+
store[KEY] = void 0;
|
|
5487
|
+
} catch {
|
|
5488
|
+
}
|
|
5489
|
+
try {
|
|
5490
|
+
root2.unmount();
|
|
5491
|
+
} catch {
|
|
5492
|
+
}
|
|
5493
|
+
try {
|
|
5494
|
+
container.remove();
|
|
5495
|
+
} catch {
|
|
5496
|
+
}
|
|
5497
|
+
};
|
|
5498
|
+
}
|
|
5499
|
+
});
|
|
5500
|
+
return;
|
|
5501
|
+
}
|
|
5502
|
+
if (n < 150) window.setTimeout(() => attempt(n + 1), 200);
|
|
5503
|
+
};
|
|
5504
|
+
attempt(0);
|
|
5505
|
+
}
|
|
5506
|
+
|
|
5507
|
+
// src/client/workbench/sched-send-tasks.ts
|
|
5508
|
+
var WS_PATH = "/qqbot-settings/events";
|
|
5509
|
+
var RECONNECT_MAX_MS = 3e4;
|
|
5510
|
+
var installed2 = false;
|
|
5511
|
+
function installSchedSendTasks() {
|
|
5512
|
+
if (installed2 || typeof window === "undefined") return;
|
|
5513
|
+
installed2 = true;
|
|
5514
|
+
connectWithBackoff();
|
|
5515
|
+
}
|
|
5516
|
+
function connectWithBackoff() {
|
|
5517
|
+
let backoff = 1e3;
|
|
5518
|
+
const open = () => {
|
|
5519
|
+
let ws;
|
|
5520
|
+
try {
|
|
5521
|
+
ws = new WebSocket(`${location.protocol === "https:" ? "wss:" : "ws:"}//${location.host}${WS_PATH}`);
|
|
5522
|
+
} catch {
|
|
5523
|
+
scheduleReconnect();
|
|
5524
|
+
return;
|
|
5525
|
+
}
|
|
5526
|
+
ws.onopen = () => {
|
|
5527
|
+
backoff = 1e3;
|
|
5528
|
+
};
|
|
5529
|
+
ws.onmessage = (ev) => {
|
|
5530
|
+
handleFrame(typeof ev.data === "string" ? ev.data : "");
|
|
5531
|
+
};
|
|
5532
|
+
ws.onclose = () => scheduleReconnect();
|
|
5533
|
+
ws.onerror = () => {
|
|
5534
|
+
try {
|
|
5535
|
+
ws.close();
|
|
5536
|
+
} catch {
|
|
5537
|
+
}
|
|
5538
|
+
};
|
|
5539
|
+
};
|
|
5540
|
+
const scheduleReconnect = () => {
|
|
5541
|
+
setTimeout(open, backoff);
|
|
5542
|
+
backoff = Math.min(backoff * 2, RECONNECT_MAX_MS);
|
|
5543
|
+
};
|
|
5544
|
+
open();
|
|
5545
|
+
}
|
|
5546
|
+
function handleFrame(text) {
|
|
5547
|
+
if (!text) return;
|
|
5548
|
+
let event = "";
|
|
5549
|
+
let data = null;
|
|
5550
|
+
try {
|
|
5551
|
+
const parsed = JSON.parse(text);
|
|
5552
|
+
event = typeof parsed.event === "string" ? parsed.event : "";
|
|
5553
|
+
data = parsed.data ?? null;
|
|
5554
|
+
} catch {
|
|
5555
|
+
return;
|
|
5556
|
+
}
|
|
5557
|
+
if (event !== "sched-send") return;
|
|
5558
|
+
const info = data;
|
|
5559
|
+
if (!info || typeof info.summary !== "string") return;
|
|
5560
|
+
const api = getBackgroundTasks();
|
|
5561
|
+
if (!api) return;
|
|
5562
|
+
const detail = info.appId ? `${info.appId} · ${info.summary}` : info.summary;
|
|
5563
|
+
const task = api.start(t("sched.bg.send"), { detail });
|
|
5564
|
+
if (info.skipped) {
|
|
5565
|
+
task.done(info.reason ?? t("sched.bg.skipped"));
|
|
5566
|
+
} else if (info.ok) {
|
|
5567
|
+
task.done(t("sched.bg.sent"));
|
|
5568
|
+
} else {
|
|
5569
|
+
task.fail(info.reason ?? "failed");
|
|
5570
|
+
}
|
|
5317
5571
|
}
|
|
5318
5572
|
|
|
5319
5573
|
// src/client/index.tsx
|
|
5320
5574
|
var name = "qqbot-settings";
|
|
5321
|
-
var inject = ["slots", "connection", "locale"
|
|
5575
|
+
var inject = ["slots", "connection", "locale"];
|
|
5322
5576
|
var RPC_CHANNEL = "/qqbot-settings";
|
|
5323
|
-
var QQBOT_PANEL_ID = "@sunjuntao/dsh-qqbot";
|
|
5324
|
-
var QQBOT_PANEL_KIND = "qqbot-sched";
|
|
5325
5577
|
var NOTICE_TTL_SECONDS = 8;
|
|
5578
|
+
var UPDATE_IDLE = { busy: false, message: "", done: false };
|
|
5579
|
+
var updateStore = /* @__PURE__ */ (() => {
|
|
5580
|
+
let current = UPDATE_IDLE;
|
|
5581
|
+
const listeners2 = /* @__PURE__ */ new Set();
|
|
5582
|
+
return {
|
|
5583
|
+
snapshot() {
|
|
5584
|
+
return current;
|
|
5585
|
+
},
|
|
5586
|
+
publish(next) {
|
|
5587
|
+
current = next.busy ? next : UPDATE_IDLE;
|
|
5588
|
+
for (const listener of [...listeners2]) {
|
|
5589
|
+
try {
|
|
5590
|
+
listener(next);
|
|
5591
|
+
} catch {
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
},
|
|
5595
|
+
subscribe(listener) {
|
|
5596
|
+
listeners2.add(listener);
|
|
5597
|
+
return () => {
|
|
5598
|
+
listeners2.delete(listener);
|
|
5599
|
+
};
|
|
5600
|
+
}
|
|
5601
|
+
};
|
|
5602
|
+
})();
|
|
5603
|
+
function publishUpdate(next) {
|
|
5604
|
+
updateStore.publish(next);
|
|
5605
|
+
}
|
|
5326
5606
|
function QqbotSettingsTab({ rpcCall }) {
|
|
5327
5607
|
const [status, setStatus] = React10.useState(null);
|
|
5328
5608
|
const [bots, setBots] = React10.useState(null);
|
|
@@ -5338,12 +5618,9 @@ function QqbotSettingsTab({ rpcCall }) {
|
|
|
5338
5618
|
const [loadError, setLoadError] = React10.useState("");
|
|
5339
5619
|
const [refreshing, setRefreshing] = React10.useState(false);
|
|
5340
5620
|
const [reconnecting, setReconnecting] = React10.useState(false);
|
|
5341
|
-
const [
|
|
5342
|
-
const
|
|
5343
|
-
|
|
5344
|
-
message: "",
|
|
5345
|
-
done: false
|
|
5346
|
-
});
|
|
5621
|
+
const [update, setLocalUpdate] = React10.useState(() => updateStore.snapshot());
|
|
5622
|
+
const setUpdate = publishUpdate;
|
|
5623
|
+
React10.useEffect(() => updateStore.subscribe(setLocalUpdate), []);
|
|
5347
5624
|
const refresh = React10.useCallback(async (appId) => {
|
|
5348
5625
|
try {
|
|
5349
5626
|
const [s, c, b, cat] = await Promise.all([
|
|
@@ -5359,7 +5636,6 @@ function QqbotSettingsTab({ rpcCall }) {
|
|
|
5359
5636
|
setForm({ ...stored });
|
|
5360
5637
|
}
|
|
5361
5638
|
if (b.ok) setBots(val(b) ?? null);
|
|
5362
|
-
notifyBotsChanged();
|
|
5363
5639
|
if (cat.ok) setCatalogs({ ...EMPTY_CATALOGS, ...val(cat) ?? {} });
|
|
5364
5640
|
} catch (error) {
|
|
5365
5641
|
setLoadError(error instanceof Error ? error.message : String(error));
|
|
@@ -5488,9 +5764,9 @@ function QqbotSettingsTab({ rpcCall }) {
|
|
|
5488
5764
|
};
|
|
5489
5765
|
React10.useEffect(() => {
|
|
5490
5766
|
if (!update.message || update.busy) return void 0;
|
|
5491
|
-
const timer = setTimeout(() =>
|
|
5767
|
+
const timer = setTimeout(() => publishUpdate({ ...update, message: "" }), NOTICE_TTL_SECONDS * 1e3);
|
|
5492
5768
|
return () => clearTimeout(timer);
|
|
5493
|
-
}, [update
|
|
5769
|
+
}, [update]);
|
|
5494
5770
|
const groupOverrides = form.groupOverrides && typeof form.groupOverrides === "object" && !Array.isArray(form.groupOverrides) ? form.groupOverrides : {};
|
|
5495
5771
|
const overrideSummary = (ov) => {
|
|
5496
5772
|
const parts = [];
|
|
@@ -5504,7 +5780,6 @@ function QqbotSettingsTab({ rpcCall }) {
|
|
|
5504
5780
|
if (ov.markdownReply !== void 0) parts.push(fmt("group.summaryMarkdown", ov.markdownReply ? t("sched.form.onShort") : t("sched.form.offShort")));
|
|
5505
5781
|
if (ov.memoryEnabled !== void 0) parts.push(fmt("group.summaryMemory", ov.memoryEnabled ? t("sched.form.onShort") : t("sched.form.offShort")));
|
|
5506
5782
|
if (Array.isArray(ov.bannedWords) && ov.bannedWords.length > 0) parts.push(fmt("group.summaryBannedWords", ov.bannedWords.length));
|
|
5507
|
-
if (typeof ov.agentPresetChat === "string" && ov.agentPresetChat) parts.push(fmt("group.summaryChatPreset", ov.agentPresetChat));
|
|
5508
5783
|
return parts.length > 0 ? parts.join(" · ") : t("group.noOverrides");
|
|
5509
5784
|
};
|
|
5510
5785
|
const removeOverride = async (openid) => {
|
|
@@ -5663,40 +5938,6 @@ function QqbotSettingsTab({ rpcCall }) {
|
|
|
5663
5938
|
)
|
|
5664
5939
|
) : null,
|
|
5665
5940
|
h("p", { className: "qbot-hint" }, t("list.cardHint"))
|
|
5666
|
-
),
|
|
5667
|
-
// ── 界面偏好:右侧面板「QQ 定时消息」入口开关(客户端本地设置) ──
|
|
5668
|
-
h(
|
|
5669
|
-
"div",
|
|
5670
|
-
{ className: "qbot-surfaceCard" },
|
|
5671
|
-
h(
|
|
5672
|
-
"div",
|
|
5673
|
-
{ className: "qbot-surfaceBody" },
|
|
5674
|
-
h(
|
|
5675
|
-
"label",
|
|
5676
|
-
{ className: "qbot-switchRow" },
|
|
5677
|
-
h(
|
|
5678
|
-
"span",
|
|
5679
|
-
{ className: "qbot-switchText" },
|
|
5680
|
-
h("strong", null, t("ui.panelSwitchTitle")),
|
|
5681
|
-
h(
|
|
5682
|
-
"p",
|
|
5683
|
-
{ style: { margin: "2px 0 0", fontSize: "12px", color: "var(--dsw-alias-label-tertiary, #8f959e)" } },
|
|
5684
|
-
t("ui.panelSwitchDesc")
|
|
5685
|
-
)
|
|
5686
|
-
),
|
|
5687
|
-
h("input", {
|
|
5688
|
-
type: "checkbox",
|
|
5689
|
-
className: "qbot-switch",
|
|
5690
|
-
checked: panelVisible,
|
|
5691
|
-
onChange: (e) => {
|
|
5692
|
-
const v = Boolean(e.target.checked);
|
|
5693
|
-
setPanelVisibleState(v);
|
|
5694
|
-
setPanelVisible(v);
|
|
5695
|
-
},
|
|
5696
|
-
"aria-label": t("ui.panelSwitchTitle")
|
|
5697
|
-
})
|
|
5698
|
-
)
|
|
5699
|
-
)
|
|
5700
5941
|
)
|
|
5701
5942
|
);
|
|
5702
5943
|
const wsInfo = detailBot ? {
|
|
@@ -5965,8 +6206,6 @@ function QqbotSettingsTab({ rpcCall }) {
|
|
|
5965
6206
|
"aria-label": "Agent Preset"
|
|
5966
6207
|
}, presetOptions(catalogs.agentPresets).map((o) => h("option", { key: o.value, value: o.value }, o.label)))
|
|
5967
6208
|
})
|
|
5968
|
-
// 群聊聊天 Preset(agentPresetChat)与 AppSecret 凭据引用(secretEnv)已从界面移除,
|
|
5969
|
-
// 仅通过 bots.json 配置——默认 agentPresetChat 留空即跟随上方 Agent Preset(见 rule.ts)。
|
|
5970
6209
|
)
|
|
5971
6210
|
),
|
|
5972
6211
|
// ── 消息与回复策略(开关) ──
|
|
@@ -6179,7 +6418,7 @@ function QqbotSettingsTab({ rpcCall }) {
|
|
|
6179
6418
|
"div",
|
|
6180
6419
|
{ className: "qbot-brandHeading" },
|
|
6181
6420
|
h("strong", { className: "qbot-brandName" }, t("app.title")),
|
|
6182
|
-
h("span", { className: "qbot-brandVersion" }, `v${true ? "0.1.
|
|
6421
|
+
h("span", { className: "qbot-brandVersion" }, `v${true ? "0.1.13" : "0.0.2"}`),
|
|
6183
6422
|
h("button", {
|
|
6184
6423
|
className: `qbot-btn qbot-updateBtn${update.done ? " is-done" : ""}`,
|
|
6185
6424
|
type: "button",
|
|
@@ -6193,7 +6432,19 @@ function QqbotSettingsTab({ rpcCall }) {
|
|
|
6193
6432
|
),
|
|
6194
6433
|
h("div", { className: "qbot-titleActions" }, globalBadge)
|
|
6195
6434
|
),
|
|
6196
|
-
|
|
6435
|
+
// 更新期间(busy)的提示条不自动淡出,并额外给出「请勿关闭」警示:
|
|
6436
|
+
// update.apply 会下载并覆盖安装目录,中途切走 / 关闭面板不影响宿主执行,
|
|
6437
|
+
// 但用户看不到结果、也容易误以为卡死,所以显式告知。
|
|
6438
|
+
update.message ? h(
|
|
6439
|
+
"div",
|
|
6440
|
+
{
|
|
6441
|
+
className: `qbot-infoNotice qbot-updateNotice${update.busy ? " is-busy" : ""}`,
|
|
6442
|
+
role: "status",
|
|
6443
|
+
"aria-busy": update.busy ? "true" : "false"
|
|
6444
|
+
},
|
|
6445
|
+
h("span", { className: "qbot-updateMsg" }, update.message),
|
|
6446
|
+
update.busy ? h("strong", { className: "qbot-updateKeepOpen" }, t("update.keepOpen")) : null
|
|
6447
|
+
) : null,
|
|
6197
6448
|
h(
|
|
6198
6449
|
"div",
|
|
6199
6450
|
{ className: "qbot-panel", id: "qbot-panel" },
|
|
@@ -6270,82 +6521,37 @@ function apply(ctx) {
|
|
|
6270
6521
|
};
|
|
6271
6522
|
setRpcCall(rpcCall);
|
|
6272
6523
|
ctx.effect(() => installStyles(), "qqbot-settings: styles");
|
|
6524
|
+
ctx.effect(
|
|
6525
|
+
() => {
|
|
6526
|
+
let style = document.getElementById("qbot-settings-nav-style");
|
|
6527
|
+
if (!style) {
|
|
6528
|
+
style = document.createElement("style");
|
|
6529
|
+
style.id = "qbot-settings-nav-style";
|
|
6530
|
+
style.textContent = QBOT_SETTINGS_NAV_CSS;
|
|
6531
|
+
document.head.appendChild(style);
|
|
6532
|
+
}
|
|
6533
|
+
const disposeMarker = registerQbotSettingsNavIcon(() => t("app.title"));
|
|
6534
|
+
return () => {
|
|
6535
|
+
disposeMarker();
|
|
6536
|
+
style?.remove();
|
|
6537
|
+
};
|
|
6538
|
+
},
|
|
6539
|
+
"qqbot-settings: settings navigation icon"
|
|
6540
|
+
);
|
|
6273
6541
|
ctx.slots.inject("settings.section", () => ctx.slots.register(
|
|
6274
6542
|
{
|
|
6275
6543
|
name: "settings.section",
|
|
6276
6544
|
id: "dsh-qqbot",
|
|
6277
6545
|
order: 31,
|
|
6278
|
-
label
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
h(QqBotGlyph, { className: "qbot-navGlyph", uid: "nav" }),
|
|
6282
|
-
h("span", { className: "qbot-navText" }, t("app.title"))
|
|
6283
|
-
),
|
|
6546
|
+
// 宿主新版导航行自带系统图标(navIcon),label 契约是纯文本;
|
|
6547
|
+
// 这里不再内嵌 QqBotGlyph,否则会和宿主图标重复。
|
|
6548
|
+
label: () => t("app.title"),
|
|
6284
6549
|
inject: () => ({ rpcCall })
|
|
6285
6550
|
},
|
|
6286
6551
|
QqbotSettingsTab
|
|
6287
6552
|
));
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
if (disposePanelTab) return;
|
|
6291
|
-
disposePanelTab = ctx.sidebarRightTabs.register({
|
|
6292
|
-
id: QQBOT_PANEL_ID,
|
|
6293
|
-
kind: QQBOT_PANEL_KIND,
|
|
6294
|
-
// title / guide 由宿主在打开面板时求值,这里每次调用都走 i18n,切换语言后即为当前语言。
|
|
6295
|
-
title: () => t("panel.title"),
|
|
6296
|
-
guide: [
|
|
6297
|
-
{
|
|
6298
|
-
order: 100,
|
|
6299
|
-
title: () => t("panel.title"),
|
|
6300
|
-
description: () => t("panel.subtitle"),
|
|
6301
|
-
// 入口胶囊的图标;不传则宿主画占位方块。用插件统一的 QQ 机器人 glyph。
|
|
6302
|
-
icon: QqBotGuideIcon
|
|
6303
|
-
}
|
|
6304
|
-
]
|
|
6305
|
-
});
|
|
6306
|
-
};
|
|
6307
|
-
const unregisterPanelTab = () => {
|
|
6308
|
-
try {
|
|
6309
|
-
disposePanelTab?.();
|
|
6310
|
-
} catch {
|
|
6311
|
-
}
|
|
6312
|
-
disposePanelTab = null;
|
|
6313
|
-
};
|
|
6314
|
-
const syncPanelTab = async () => {
|
|
6315
|
-
try {
|
|
6316
|
-
const res = await rpcCall("bots.list");
|
|
6317
|
-
const bots = res.ok ? val(res)?.bots : void 0;
|
|
6318
|
-
const anyConnected = Array.isArray(bots) && bots.some((b) => b?.ws?.state === "connected");
|
|
6319
|
-
if (anyConnected && getPanelVisible()) registerPanelTab();
|
|
6320
|
-
else unregisterPanelTab();
|
|
6321
|
-
} catch {
|
|
6322
|
-
}
|
|
6323
|
-
};
|
|
6324
|
-
void syncPanelTab();
|
|
6325
|
-
const offBotsChanged = onBotsChanged(() => void syncPanelTab());
|
|
6326
|
-
const panelTabTimer = setInterval(() => void syncPanelTab(), 15e3);
|
|
6327
|
-
ctx.effect(
|
|
6328
|
-
() => () => {
|
|
6329
|
-
clearInterval(panelTabTimer);
|
|
6330
|
-
offBotsChanged();
|
|
6331
|
-
unregisterPanelTab();
|
|
6332
|
-
},
|
|
6333
|
-
"qqbot: right-panel tab lifecycle"
|
|
6334
|
-
);
|
|
6335
|
-
ctx.effect(
|
|
6336
|
-
() => ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register(
|
|
6337
|
-
{ name: "sidebar.right.pane.tab", key: QQBOT_PANEL_ID },
|
|
6338
|
-
ScheduleTab
|
|
6339
|
-
)),
|
|
6340
|
-
"qqbot: right-panel tab body"
|
|
6341
|
-
);
|
|
6342
|
-
ctx.effect(
|
|
6343
|
-
() => ctx.slots.inject("sidebar.right.pane.tab.title", () => ctx.slots.register(
|
|
6344
|
-
{ name: "sidebar.right.pane.tab.title", key: QQBOT_PANEL_ID },
|
|
6345
|
-
TitleView
|
|
6346
|
-
)),
|
|
6347
|
-
"qqbot: right-panel tab title"
|
|
6348
|
-
);
|
|
6553
|
+
installScheduleWorkbenchView();
|
|
6554
|
+
installSchedSendTasks();
|
|
6349
6555
|
}
|
|
6350
6556
|
function installStyles() {
|
|
6351
6557
|
if (typeof document === "undefined") return () => {
|