@zaofan/dsh-qqbot 1.11.8 → 1.11.10

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.
@@ -2735,7 +2735,7 @@ var QQS_THEME_CSS = [
2735
2735
  // · 审批/提问 → 打断式弹出(#qqs-ap-float 保持)
2736
2736
  // · 入群申请 → 悬浮球上安静红点(不弹窗)
2737
2737
  // · 群管理(发消息/审批入群/禁言) → 悬浮球点开成操作台
2738
- var DOCK_CSS = "#qqs-dock-wrap{position:fixed;right:18px;bottom:190px;z-index:9997;font-family:-apple-system,'PingFang SC','Microsoft YaHei',sans-serif}#qqs-dock-ball{width:52px;height:52px;border-radius:50%;background:linear-gradient(160deg,#7c6cf0,#5b4fd8);color:#fff;font-size:24px;line-height:52px;text-align:center;cursor:pointer;box-shadow:0 6px 20px rgba(90,70,220,.4);user-select:none;transition:transform .12s,box-shadow .12s;position:relative}#qqs-dock-ball:hover{transform:scale(1.06)}#qqs-dock-badge{position:absolute;top:-4px;right:-4px;min-width:18px;height:18px;border-radius:9px;background:#ff4d4f;color:#fff;font-size:11px;font-weight:700;line-height:18px;padding:0 4px;box-sizing:border-box;text-align:center;display:none}#qqs-dock-panel{position:fixed;right:18px;bottom:190px;z-index:9998;width:min(720px,94vw);max-height:68vh;display:none;flex-direction:column;background:#fff;border:1px solid #d9c6ff;border-radius:16px;box-shadow:0 12px 40px rgba(60,40,140,.25);overflow:hidden;font-family:-apple-system,'PingFang SC','Microsoft YaHei',sans-serif}#qqs-dock-panel .dk-h{display:flex;align-items:center;gap:8px;padding:10px 14px;background:linear-gradient(90deg,#7c6cf01f,#7c6cf008);font-size:14px;font-weight:700;color:#4a3a9f;border-bottom:1px solid #efe8ff}#qqs-dock-panel .dk-b{padding:10px 14px;overflow:auto;font-size:13px;color:#1f2329}#qqs-dock-panel .dk-tab{display:flex;gap:4px;border-bottom:1px solid #eee;margin-bottom:10px}#qqs-dock-panel .dk-tab button{font:inherit;font-size:13px;padding:6px 14px;border:none;background:none;cursor:pointer;color:#666;border-bottom:2px solid transparent}#qqs-dock-panel .dk-tab button.on{color:#4a3a9f;font-weight:700;border-bottom-color:#7c6cf0}#qqs-dock-panel select.qqs-sel,#qqs-dock-panel input.qqs-txt,#qqs-dock-panel textarea.qqs-txt{font:inherit;color:#1f2329;background:#fff;border:1px solid #d0d5dd;border-radius:8px;padding:5px 8px;outline:none}#qqs-dock-panel .dk-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:6px 0}#qqs-dock-panel .dk-btn{font:inherit;font-size:13px;padding:5px 12px;border-radius:8px;cursor:pointer;border:1px solid #d9c6ff;background:#f1ecff;color:#4a3a9f}#qqs-dock-panel .dk-btn.ok{background:#e6f7ec;color:#187a3d;border-color:#b8e6c8}#qqs-dock-panel .dk-btn.no{background:#fdeeee;color:#c23131;border-color:#f3c4c4}#qqs-dock-panel .dk-btn:disabled{opacity:.5;cursor:default}#qqs-dock-panel .dk-msg{color:#888;font-size:12px;padding:2px 0}#qqs-dock-panel .dk-list{max-height:34vh;overflow:auto;border:1px solid #f0ecff;border-radius:10px;padding:4px}#qqs-dock-panel .dk-item{display:flex;align-items:center;gap:8px;padding:6px 8px;border-bottom:1px solid #f5f2ff;flex-wrap:wrap;font-size:13px}#qqs-dock-panel .dk-item:last-child{border-bottom:none}#qqs-dock-panel .dk-empty{color:#aaa;text-align:center;padding:18px 0;font-size:12px}"
2738
+ var DOCK_CSS = "#qqs-dock-wrap{position:fixed;right:18px;bottom:190px;z-index:9997;font-family:-apple-system,'PingFang SC','Microsoft YaHei',sans-serif}#qqs-dock-ball{width:52px;height:52px;border-radius:50%;background:linear-gradient(160deg,#7c6cf0,#5b4fd8);color:#fff;font-size:24px;line-height:52px;text-align:center;cursor:pointer;box-shadow:0 6px 20px rgba(90,70,220,.4);user-select:none;transition:transform .12s,box-shadow .12s;position:relative}#qqs-dock-ball:hover{transform:scale(1.06)}#qqs-dock-badge{position:absolute;top:-4px;right:-4px;min-width:18px;height:18px;border-radius:9px;background:#ff4d4f;color:#fff;font-size:11px;font-weight:700;line-height:18px;padding:0 4px;box-sizing:border-box;text-align:center;display:none}#qqs-dock-ball.qqs-alert{background:linear-gradient(160deg,#ff8a3d,#ff4d4f)!important;box-shadow:0 6px 20px rgba(255,77,79,.5)!important;animation:qqsDockPulse 1.6s ease-in-out infinite}@keyframes qqsDockPulse{0%,100%{box-shadow:0 6px 20px rgba(255,77,79,.45)}50%{box-shadow:0 6px 28px rgba(255,77,79,.9)}}#qqs-dock-panel{position:fixed;right:18px;bottom:190px;z-index:9998;width:min(720px,94vw);max-height:68vh;display:none;flex-direction:column;background:#fff;border:1px solid #d9c6ff;border-radius:16px;box-shadow:0 12px 40px rgba(60,40,140,.25);overflow:hidden;font-family:-apple-system,'PingFang SC','Microsoft YaHei',sans-serif}#qqs-dock-panel .dk-h{display:flex;align-items:center;gap:8px;padding:10px 14px;background:linear-gradient(90deg,#7c6cf01f,#7c6cf008);font-size:14px;font-weight:700;color:#4a3a9f;border-bottom:1px solid #efe8ff}#qqs-dock-panel .dk-b{padding:10px 14px;overflow:auto;font-size:13px;color:#1f2329}#qqs-dock-panel .dk-tab{display:flex;gap:4px;border-bottom:1px solid #eee;margin-bottom:10px}#qqs-dock-panel .dk-tab button{font:inherit;font-size:13px;padding:6px 14px;border:none;background:none;cursor:pointer;color:#666;border-bottom:2px solid transparent}#qqs-dock-panel .dk-tab button.on{color:#4a3a9f;font-weight:700;border-bottom-color:#7c6cf0}#qqs-dock-panel select.qqs-sel,#qqs-dock-panel input.qqs-txt,#qqs-dock-panel textarea.qqs-txt{font:inherit;color:#1f2329;background:#fff;border:1px solid #d0d5dd;border-radius:8px;padding:5px 8px;outline:none}#qqs-dock-panel .dk-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:6px 0}#qqs-dock-panel .dk-btn{font:inherit;font-size:13px;padding:5px 12px;border-radius:8px;cursor:pointer;border:1px solid #d9c6ff;background:#f1ecff;color:#4a3a9f}#qqs-dock-panel .dk-btn.ok{background:#e6f7ec;color:#187a3d;border-color:#b8e6c8}#qqs-dock-panel .dk-btn.no{background:#fdeeee;color:#c23131;border-color:#f3c4c4}#qqs-dock-panel .dk-btn:disabled{opacity:.5;cursor:default}#qqs-dock-panel .dk-msg{color:#888;font-size:12px;padding:2px 0}#qqs-dock-panel .dk-list{max-height:34vh;overflow:auto;border:1px solid #f0ecff;border-radius:10px;padding:4px}#qqs-dock-panel .dk-item{display:flex;align-items:center;gap:8px;padding:6px 8px;border-bottom:1px solid #f5f2ff;flex-wrap:wrap;font-size:13px}#qqs-dock-panel .dk-item:last-child{border-bottom:none}#qqs-dock-panel .dk-empty{color:#aaa;text-align:center;padding:18px 0;font-size:12px}"
2739
2739
  // ── 💬 聊天视图样式(dock 追加段, 2026-09-07): QQ 风格气泡, 群友左(bot)右 ──
2740
2740
  var DOCK_CSS2 = "#qqs-dock-panel .dk-chat-head{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:0 0 6px}#qqs-dock-panel .dk-chat-box{overflow-y:auto;overscroll-behavior:contain;background:#f5f6f8;border:1px solid #e6e8ec;border-radius:10px;padding:10px 12px;box-sizing:border-box;height:min(36vh,300px);min-height:140px;scroll-behavior:auto}#qqs-dock-panel .dk-chat-box::-webkit-scrollbar{width:6px}#qqs-dock-panel .dk-chat-box::-webkit-scrollbar-thumb{background:#d3d7dd;border-radius:3px}#qqs-dock-panel .dk-crow{display:flex;gap:8px;align-items:flex-start;margin:0 0 12px}#qqs-dock-panel .dk-crow.out{flex-direction:row-reverse}#qqs-dock-panel .dk-ava{width:32px;height:32px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:600;color:#fff;overflow:hidden;user-select:none;background:linear-gradient(150deg,#8fb3e8,#5f8fd9)}#qqs-dock-panel .dk-crow.out .dk-ava{background:linear-gradient(150deg,#5ec7f2,#3b8fe0)}#qqs-dock-panel .dk-cmain{display:flex;flex-direction:column;max-width:calc(100% - 40px);min-width:0}#qqs-dock-panel .dk-crow.in .dk-cmain{align-items:flex-start}#qqs-dock-panel .dk-crow.out .dk-cmain{align-items:flex-end}#qqs-dock-panel .dk-cmeta{font-size:11px;color:#9aa0a8;margin:0 6px 2px;max-width:100%;display:flex;align-items:center;gap:5px;flex-wrap:wrap}#qqs-dock-panel .dk-crow.out .dk-cmeta{flex-direction:row-reverse}#qqs-dock-panel .dk-cbubble{padding:7px 11px;font-size:13px;line-height:1.55;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;box-shadow:0 1px 2px rgba(20,30,60,.06);max-width:100%}#qqs-dock-panel .dk-cbubble a.dk-link{color:#1a66d6;text-decoration:underline;word-break:break-all;cursor:pointer}#qqs-dock-panel .dk-crow.out .dk-cbubble a.dk-link{color:#eaf3ff}#qqs-dock-panel .dk-crow.in .dk-cbubble{background:#fff;border:1px solid #e3e6ea;color:#1f2329;border-radius:3px 10px 10px 10px}#qqs-dock-panel .dk-crow.out .dk-cbubble{background:linear-gradient(180deg,#69a6ff,#3d7df5);color:#fff;border-radius:10px 3px 10px 10px}#qqs-dock-panel .dk-img{display:block;max-width:min(230px,52vw);max-height:200px;border-radius:6px;margin:0 0 3px;object-fit:cover;cursor:zoom-in}#qqs-dock-panel .dk-audio{display:block;max-width:min(260px,60vw);width:100%;height:34px;margin:0 0 2px}#qqs-dock-panel .dk-file{display:inline-flex;align-items:center;gap:5px;max-width:100%;padding:6px 12px;border-radius:8px;background:#f0f6ff;border:1px solid #cfe0fa;color:#2b6bd8;font-size:13px;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#qqs-dock-panel .dk-file:hover{background:#e2edff}#qqs-dock-panel .dk-video{display:block;max-width:min(260px,60vw);max-height:180px;border-radius:6px;margin:0 0 2px}#qqs-dock-panel .dk-ctag{display:inline-block;font-size:10px;color:#5b8ff0;background:#eaf2ff;border:1px solid #d4e3fd;border-radius:8px;padding:0 6px}#qqs-dock-panel .dk-chat-top{text-align:center;color:#b0b4bb;font-size:11px;padding:2px 0 6px;user-select:none}#qqs-dock-panel .dk-chat-bottom{text-align:center;color:#c3c7cd;font-size:11px;padding:6px 0 0}#qqs-dock-panel .dk-composer{margin-top:8px;border:1px solid #e3e6ea;border-radius:10px;background:#fff;overflow:visible}#qqs-dock-panel .dk-composer textarea{width:100%;box-sizing:border-box;border:none;outline:none;resize:none;font:inherit;font-size:13px;color:#1f2329;background:transparent;padding:8px 10px 4px;line-height:1.5;max-height:120px}#qqs-dock-panel .dk-cbar{display:flex;align-items:center;gap:4px;padding:4px 8px 6px;flex-wrap:wrap}#qqs-dock-panel .dk-cbar .dk-btn{padding:3px 10px;font-size:12px;border-radius:7px}#qqs-dock-panel .dk-cbar .dk-send{background:linear-gradient(180deg,#69a6ff,#3d7df5);color:#fff;border:none;border-radius:8px;padding:5px 18px;font-size:13px;font-weight:600;cursor:pointer}#qqs-dock-panel .dk-cbar .dk-send:disabled{opacity:.5;cursor:default}#qqs-lightbox{position:fixed;inset:0;z-index:2147483000;background:rgba(8,10,18,.82);display:flex;align-items:center;justify-content:center;cursor:zoom-out}#qqs-lightbox img{max-width:92vw;max-height:92vh;border-radius:8px;box-shadow:0 10px 60px rgba(0,0,0,.6)}#qqs-dock-panel .dk-composer{position:relative}#qqs-dock-panel .dk-at-pop{position:absolute;left:6px;bottom:calc(100% - 4px);z-index:30;min-width:200px;max-width:90%;max-height:190px;overflow-y:auto;background:#fff;border:1px solid #e0e4ea;border-radius:10px;box-shadow:0 8px 24px rgba(30,40,80,.16);padding:4px;display:none}#qqs-dock-panel .dk-at-item{display:flex;align-items:center;gap:6px;padding:5px 9px;border-radius:7px;cursor:pointer;font-size:12px;color:#1f2329;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#qqs-dock-panel .dk-at-item.on,#qqs-dock-panel .dk-at-item:hover{background:#eef3ff;color:#2b5fd0}#qqs-dock-panel .dk-at-empty{color:#aaa;font-size:12px;padding:6px 9px}#qqs-lightbox .lb-x{position:fixed;right:16px;top:10px;color:#fff;font-size:30px;cursor:pointer;line-height:1;padding:6px}#qqs-dock-panel.dk-full{left:0!important;top:0!important;right:0!important;bottom:0!important;width:100vw!important;max-width:100vw!important;height:100vh!important;max-height:100vh!important;border-radius:0;z-index:2147482000;display:flex;flex-direction:column}#qqs-dock-panel.dk-full .dk-h,#qqs-dock-panel.dk-full .dk-detect{flex:none}#qqs-dock-panel.dk-full .dk-b{flex:1;min-height:0;overflow:hidden;display:flex;flex-direction:column;padding:8px 14px 6px}#qqs-dock-panel.dk-full .dk-chat-wrap{display:flex;flex-direction:column;flex:1;min-height:0}#qqs-dock-panel.dk-full .dk-chat-head{flex:none}#qqs-dock-panel.dk-full .dk-chat-box{flex:1;height:auto!important;min-height:0!important;max-height:none!important;overflow-y:auto;overscroll-behavior:contain}#qqs-dock-panel.dk-full .dk-composer{flex:none;margin-top:6px}#qqs-dock-panel .dk-tab{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;scrollbar-width:none}#qqs-dock-panel .dk-tab::-webkit-scrollbar{display:none}#qqs-dock-panel .dk-tab button{white-space:nowrap;flex:0 0 auto}@media (max-width:560px){#qqs-dock-panel:not(.dk-full){left:8px!important;right:8px!important;width:auto!important;max-width:calc(100vw - 16px)!important;bottom:96px!important}#qqs-dock-panel .dk-h{font-size:13px;padding:8px 10px;gap:6px}#qqs-dock-panel .dk-b{padding:8px 10px;font-size:13px}#qqs-dock-panel .dk-tab button{font-size:12px;padding:7px 10px}#qqs-dock-panel .dk-btn{font-size:12px;padding:6px 10px}#qqs-dock-panel .dk-row{gap:6px;margin:5px 0}#qqs-dock-panel select.qqs-sel,#qqs-dock-panel input.qqs-txt,#qqs-dock-panel textarea.qqs-txt{padding:7px 9px;font-size:16px}#qqs-dock-panel .dk-list{max-height:46vh}#qqs-dock-panel .dk-item{padding:8px 6px;gap:6px}}@media (max-width:560px){#qqs-dock-panel.dk-full{border-radius:0}#qqs-dock-panel.dk-full .dk-b{padding-bottom:calc(8px + env(safe-area-inset-bottom,0px))}#qqs-dock-panel.dk-full .dk-tab{position:relative;background:#fff;z-index:2}}@media (max-width:560px){#qqs-dock-ball{width:46px;height:46px;line-height:46px;font-size:21px}}#qqs-dock-panel .dk-chat-wrap{position:relative}#qqs-dock-panel .dk-jump-latest{display:none;position:absolute;right:12px;bottom:76px;z-index:6;align-items:center;gap:4px;padding:5px 13px;font-size:12px;font-weight:600;color:#fff;cursor:pointer;border:none;border-radius:15px;background:linear-gradient(180deg,#69a6ff,#3d7df5);box-shadow:0 3px 12px rgba(30,60,140,.32);transition:transform .12s,box-shadow .12s}#qqs-dock-panel .dk-jump-latest.on{display:inline-flex}#qqs-dock-panel .dk-jump-latest:hover{background:linear-gradient(180deg,#5c9bfa,#2f6fe6);box-shadow:0 4px 16px rgba(30,60,140,.4)}#qqs-dock-panel .dk-jump-latest:active{transform:scale(.95)}"
2741
2741
  function ensureDockCss2() { try { if (!document.getElementById('qqs-dock-css2')) { var st = document.createElement('style'); st.id = 'qqs-dock-css2'; st.textContent = DOCK_CSS2; document.head.appendChild(st) } } catch (e) {} };
@@ -2760,6 +2760,36 @@ var QQS_THEME_CSS = [
2760
2760
  document.body.appendChild(wrap)
2761
2761
  var ball = wrap.querySelector('#qqs-dock-ball')
2762
2762
  var badge = wrap.querySelector('#qqs-dock-badge')
2763
+ // ── 2026-10-28(主人定):悬浮球**只在 QQ 会话里显示** ──
2764
+ // 判定沿用 session-lookup(state.detectedHit 的同一个接口),未命中就不显示。
2765
+ // 为什么还要轮询:会话切换(web 侧点开另一个会话)不会通知这里;
2766
+ // 只比 sessionId 字符串,变了才发请求,开销可忽略。
2767
+ var _dockSid = ''
2768
+ var _dockHit = null
2769
+ var _dockPending = 0
2770
+ function refreshDockVisibility() {
2771
+ if (_dockPending > 0) { wrap.style.display = ''; return }
2772
+ var sid = ''
2773
+ try { sid = curSessionId() } catch (eS) { sid = '' }
2774
+ // 同一会话、已知结论 → 直接套用(不发请求,也不闪)
2775
+ if (sid === _dockSid && _dockHit !== null) { wrap.style.display = _dockHit ? '' : 'none'; return }
2776
+ _dockSid = sid
2777
+ if (!sid) { _dockHit = false; wrap.style.display = 'none'; return }
2778
+ // 会话变了:先按"未命中"藏起来,拿到结论再显示(宁可晚一点,也别让它乱闪)
2779
+ _dockHit = null
2780
+ wrap.style.display = 'none'
2781
+ qqHitOf(sid, function (hit) {
2782
+ if (sid !== _dockSid) return // 查询期间又切了会话:这次结论作废
2783
+ _dockHit = hit
2784
+ wrap.style.display = hit ? '' : 'none'
2785
+ })
2786
+ }
2787
+ try {
2788
+ refreshDockVisibility()
2789
+ // 事件驱动(2026-10-28,主人嫌 2s 轮询费性能):不再 setInterval ——
2790
+ // apply() 里的 refreshQqUi 在「sessions.list 变化 / 窗口聚焦 / 页面重新可见」时调这里。
2791
+ window.__qqsRefreshDock = refreshDockVisibility
2792
+ } catch (eVis) { /* 显隐失败不影响球本身 */ }
2763
2793
  var panel = null
2764
2794
  var open = false
2765
2795
  // ⚠️ 2026-09-27 修复「拖到屏幕外后球永久消失」:拖拽时本来有 clamp(行1749),
@@ -2956,11 +2986,14 @@ var QQS_THEME_CSS = [
2956
2986
  d.items.forEach(function (it) { n += (it.pending || 0) })
2957
2987
  if (n > 0) { badge.style.display = 'block'; badge.textContent = n > 99 ? '99+' : String(n) }
2958
2988
  else badge.style.display = 'none'
2989
+ _dockPending = n
2990
+ try { if (n > 0) ball.classList.add('qqs-alert'); else ball.classList.remove('qqs-alert') } catch (eA) {}
2991
+ try { if (window.__qqsRefreshDock) window.__qqsRefreshDock() } catch (eB) {}
2959
2992
  if (panel && open) { var b = panel.querySelector('.dk-join-badge'); if (b) b.style.display = n > 0 ? 'inline-block' : 'none'; if (b) b.textContent = n > 99 ? '99+' : String(n) }
2960
2993
  })
2961
2994
  }
2962
2995
  refreshBadge()
2963
- setInterval(refreshBadge, 20000)
2996
+ setInterval(refreshBadge, 10000)
2964
2997
 
2965
2998
  // ── 面板状态(每个实例独立保存, 切回不丢) ──
2966
2999
  var state = { ns: '', accts: [], gid: '', groups: [], tab: 'chat', sendScope: 'group', sendTo: '', sendName: '', sendText: '', insertCtx: true, targetQ: '', c2cs: [], joins: null, mutes: null, members: null, muteSecs: '60', bindGid: '', bindName: '', msg: '', busy: '', wantPeer: null, lookedUp: false, detected: null, detectedHit: null, chatItems: [], chatMore: false, chatBusy: '', chatErr: '', chatOldest: 0, chatText: '', chatIns: true, outMode: '', outRev: undefined, bpEvents: [], bpSel: null, bpDraft: null, bpExt: null, bpExtBusy: '', bpReq: '', bpReqHint: '', bpSrcText: null, bpSrcPath: '', rosterSel: {}, rosterScope: 'all', rosterQ: '', hubSid: '', hubRev: undefined, hubBusy: '', hubMsg: '', gaEnabled: false, gaPoll: false, gaPollWake: true, gaHubNotify: true, gaNotifyGroup: true, gaInterval: 5, gaMinCount: 1, gaMsg: '', gaBusy: '', bcDraft: null, bcTasks: null, cardMd: '', cardBtns: '', cardGid: '', cardBusy: '', cardQ: '', tgGroups: [], tgCur: '', hammerSub: 'mute', arOn: false, arMode: 'keep', arKws: '', arKinds: [], arMute: 0, arDry: false, arMsg: '', qunLogged: null, qunUin: '', qunGroups: [], qunGc: '', qunKw: '', qunMembers: [], qunCount: 0, qunQr: false, qunMsg: '', qunBusy: false, qunLoading: false }
@@ -6718,6 +6751,31 @@ var QQS_THEME_CSS = [
6718
6751
  return ''
6719
6752
  }
6720
6753
 
6754
+ /**
6755
+ * 当前会话是不是 QQ 会话(2026-10-28)。
6756
+ * 同一次查询在窗口内共享 —— 悬浮球显隐、扩展页签显隐都走这里,不重复打后端。
6757
+ * ⚠️ 否定结论只缓存 2 秒:宿主刚启动时后端可能还没就绪,长缓存会让悬浮球一直不出现。
6758
+ */
6759
+ function qqHitOf(sid, cb) {
6760
+ if (!sid) { try { cb(false) } catch (e0) {} ; return }
6761
+ var c = (window.__qqsHitCache = window.__qqsHitCache || {})
6762
+ var e = c[sid]
6763
+ if (e && Date.now() - e.at < (e.hit ? 10000 : 2000)) { try { cb(e.hit) } catch (e1) {} ; return }
6764
+ try {
6765
+ fetch('/api/qqbot-settings/session-lookup?sessionId=' + encodeURIComponent(sid))
6766
+ .then(function (r) { return r.json() })
6767
+ .then(function (d) {
6768
+ var hit = !!(d && d.hits && d.hits.length)
6769
+ c[sid] = { at: Date.now(), hit: hit }
6770
+ try { cb(hit) } catch (e2) {}
6771
+ })
6772
+ .catch(function () { try { cb(false) } catch (e3) {} })
6773
+ } catch (e4) { try { cb(false) } catch (e5) {} }
6774
+ }
6775
+
6776
+ // (2026-10-28)页签显隐不用 DOM 方案了 —— 见 apply() 里的 setExtTabVisible:
6777
+ // `slots.inject` 的返回值是 disposer,注销后宿主自己会刷新页签列表。
6778
+
6721
6779
  function curWorkspaceCwd() {
6722
6780
  try {
6723
6781
  var svc = _extSessionsSvc
@@ -7762,14 +7820,28 @@ var QQS_THEME_CSS = [
7762
7820
  function (props) { return h(QqbotHome, { close: props && props.close }) })
7763
7821
  })
7764
7822
  })
7765
- // 「QQ机器人扩展」会话页签(2026-10-24):与「对话/轨迹/记忆/白板看板」并列
7766
- safe('conversation view', function () {
7767
- slots.inject('conversation.view', function () {
7768
- return slots.register(
7769
- { name: 'conversation.view', id: 'qqbot-ext', order: 81, label: 'QQ机器人扩展' },
7770
- ExtPanel)
7771
- })
7772
- })
7823
+ // 「QQ机器人扩展」会话页签:**只在 QQ 会话里注入**(2026-10-28 主人定)
7824
+ // 为什么不用 DOM 隐藏:`slots.inject(key, cb)` 的返回值是 disposer(已核 ui-renderer 源码:
7825
+ // `return () => { disposeController(); }`),注销后宿主 `slots.subscribe("conversation.view")`
7826
+ // 会自动刷新页签列表 —— 比按文案找按钮稳得多。
7827
+ var _extTabDisposed = null
7828
+ function setExtTabVisible(show) {
7829
+ if (show) {
7830
+ if (_extTabDisposed) return
7831
+ try {
7832
+ _extTabDisposed = slots.inject('conversation.view', function () {
7833
+ return slots.register(
7834
+ { name: 'conversation.view', id: 'qqbot-ext', order: 81, label: 'QQ机器人扩展' },
7835
+ ExtPanel)
7836
+ }) || null
7837
+ if (!_extTabDisposed) _extTabDisposed = function () {}
7838
+ } catch (eTab) { _extTabDisposed = null }
7839
+ } else {
7840
+ if (!_extTabDisposed) return
7841
+ try { _extTabDisposed() } catch (eTab2) {}
7842
+ _extTabDisposed = null
7843
+ }
7844
+ }
7773
7845
  safe('approval float', startApprovalFloat)
7774
7846
  // sessions 服务(宿主根服务): 权威读 web 当前会话(current)与 cwd(agent id === session id)
7775
7847
  var sessionsSvc = null
@@ -7777,6 +7849,44 @@ var QQS_THEME_CSS = [
7777
7849
  if (!sessionsSvc) { try { sessionsSvc = ctx.get && ctx.get('sessions') } catch (e) {} }
7778
7850
  _extSessionsSvc = sessionsSvc // 供 ExtPanel 读当前工作区 cwd
7779
7851
  safe('dock', function () { startQqDock(sessionsSvc) })
7852
+ // ── 统一刷新:页签 + 悬浮球,**事件驱动,不轮询**(2026-10-28)──
7853
+ // 信号源:`sessions.list`(ObservableSnapshot,宿主在左侧会话列表切换时就会推)
7854
+ // + window focus / 页面可见性(兜底,切回标签页时补一次)。
7855
+ var _qqUiRetry = 0
7856
+ var refreshQqUi = function () {
7857
+ // dock 球的显隐也走这同一个信号(否则切会话时页签跟走了、球还赖着不动)
7858
+ try { if (typeof window !== 'undefined' && typeof window.__qqsRefreshDock === 'function') window.__qqsRefreshDock() } catch (eDock) {}
7859
+ var sid = ''
7860
+ try { sid = curSessionId() } catch (eSid) { sid = '' }
7861
+ // 有限次重试(≈7 秒内 6 次):宿主刚启动时"当前会话"或后端 lookup 可能还没就绪,
7862
+ // 一次性判空就再也不显示球了(2026-10-28 主人实测)。不是长期轮询。
7863
+ function retryLater() {
7864
+ if (_qqUiRetry >= 6) return
7865
+ _qqUiRetry++
7866
+ setTimeout(function () { refreshQqUi() }, 1200)
7867
+ }
7868
+ if (!sid) { setExtTabVisible(false); retryLater(); return }
7869
+ qqHitOf(sid, function (hit) {
7870
+ var now = ''
7871
+ try { now = curSessionId() } catch (eNow) {}
7872
+ if (now !== sid) return // 查询期间切走了:别把旧结论套到新会话上
7873
+ setExtTabVisible(!!hit)
7874
+ if (hit) { _qqUiRetry = 0; return }
7875
+ retryLater()
7876
+ })
7877
+ }
7878
+ safe('qq ui refresh', function () {
7879
+ refreshQqUi()
7880
+ try {
7881
+ if (sessionsSvc && sessionsSvc.list && typeof sessionsSvc.list.subscribe === 'function') {
7882
+ sessionsSvc.list.subscribe(function () { refreshQqUi() })
7883
+ }
7884
+ } catch (eSub) {}
7885
+ try {
7886
+ window.addEventListener('focus', refreshQqUi)
7887
+ document.addEventListener('visibilitychange', function () { if (!document.hidden) refreshQqUi() })
7888
+ } catch (eWin) {}
7889
+ })
7780
7890
  }
7781
7891
 
7782
7892
  exports.inject = ['slots', 'sessions']
@@ -110,6 +110,24 @@ export declare function syncContextTools(agentCtx: unknown, shouldHave: boolean,
110
110
  */
111
111
  export declare function ensureExtToolsGlobal(globalCtx: unknown): void;
112
112
  export declare function ensureContextToolsGlobal(globalCtx: unknown): void;
113
+ /**
114
+ * 按会话同步「上下文压缩三件套 + QQ 群管理」的**注册状态**(幂等)。
115
+ *
116
+ * @param agentCtx 该 QQ 会话的 agent 作用域(`record.agentCtx` 或 `agent.ctx`)
117
+ * @param opts.ctxTools 要不要 context_memo / context_compact / context_drop(跟「无上下文·智能判断」开关)
118
+ * @param opts.groupAdmin 要不要 qq_group_admin(跟 groupAdmin.enabled)
119
+ * @param opts.dataRoot 会话坐标(工具执行时反查数据根用)
120
+ * @param opts.sessionKey 会话坐标(备忘类工具要求非空)
121
+ *
122
+ * 失败策略:拿不到 `tools.register` 或注册抛错时**不记状态**(写 `__retry`),下一次 pre-step 重试;
123
+ * 绝不影响会话主流程。
124
+ */
125
+ export declare function syncChannelScopedTools(agentCtx: unknown, opts: {
126
+ ctxTools: boolean;
127
+ groupAdmin: boolean;
128
+ dataRoot?: string;
129
+ sessionKey?: string;
130
+ }): void;
113
131
  export declare function applyExtDisabledRestrict(agentCtx: unknown, dataRoot: string, extToolNames: string[]): void;
114
132
  export declare function restrictContextTools(agentCtx: unknown, shouldHave: boolean, env?: {
115
133
  dataRoot?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"channel-tools.d.ts","sourceRoot":"","sources":["../src/channel-tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"channel-tools.d.ts","sourceRoot":"","sources":["../src/channel-tools.ts"],"names":[],"mappings":"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"}
@@ -4047,6 +4047,83 @@ function toolsOfScope(agentCtx) {
4047
4047
  *
4048
4048
  * 幂等:restrict 返回 dispose;每次 apply 先撤销上一次的,再按当前 disabled 重挂。
4049
4049
  */
4050
+ // ══════════════════════════════════════════════════════════════════════════
4051
+ // 2026-10-28(主人定,对应 issue #26):上下文/群管理工具改为**按 QQ 会话注册**
4052
+ // ══════════════════════════════════════════════════════════════════════════
4053
+ // 旧做法(2026-10-09 起):「插件根作用域全局注册一次 + pre-step 逐会话 restrict deny」。
4054
+ // · 当初改成全局,是为了避开"每步先注销再重注册 → 工具表反复变动 → 宿主每步发
4055
+ // 「工具已更新·移除/新增」"的抖动(那批通知还进上下文,纯烧 token)。
4056
+ // · 代价是:① 第一次 pre-step 之前,无关工作区的会话也能看到这四个名字;
4057
+ // ② `restrict` 只过滤**继承来的**工具,全局层本身就是泄漏源,剪枝只是事后补救。
4058
+ //
4059
+ // 现在照 `mountChannelTools` 的路子(**per-agent register**),配一个幂等状态机:
4060
+ // · 状态没变 → 一根手指都不动(不产生任何变更通知,同样不抖);
4061
+ // · 状态变了 → 先撤旧的、再按新状态注册(面板一改开关,下一 step 生效)。
4062
+ // 非 QQ 会话根本不经过这里 ⇒ 天生看不到,零窗口期、零 token。
4063
+ const ctxScopeState = new WeakMap();
4064
+ /**
4065
+ * 按会话同步「上下文压缩三件套 + QQ 群管理」的**注册状态**(幂等)。
4066
+ *
4067
+ * @param agentCtx 该 QQ 会话的 agent 作用域(`record.agentCtx` 或 `agent.ctx`)
4068
+ * @param opts.ctxTools 要不要 context_memo / context_compact / context_drop(跟「无上下文·智能判断」开关)
4069
+ * @param opts.groupAdmin 要不要 qq_group_admin(跟 groupAdmin.enabled)
4070
+ * @param opts.dataRoot 会话坐标(工具执行时反查数据根用)
4071
+ * @param opts.sessionKey 会话坐标(备忘类工具要求非空)
4072
+ *
4073
+ * 失败策略:拿不到 `tools.register` 或注册抛错时**不记状态**(写 `__retry`),下一次 pre-step 重试;
4074
+ * 绝不影响会话主流程。
4075
+ */
4076
+ export function syncChannelScopedTools(agentCtx, opts) {
4077
+ try {
4078
+ if (!agentCtx || typeof agentCtx !== 'object')
4079
+ return;
4080
+ const key = agentCtx;
4081
+ // 会话坐标每次都刷新(工具执行时靠它定位数据根/会话)
4082
+ if (opts.dataRoot) {
4083
+ ctxEnvMap.set(key, { dataRoot: String(opts.dataRoot), sessionKey: String(opts.sessionKey ?? '') });
4084
+ }
4085
+ const sig = `${opts.ctxTools ? '1' : '0'}${opts.groupAdmin ? '1' : '0'}`;
4086
+ const st = ctxScopeState.get(key);
4087
+ if (st && st.sig === sig)
4088
+ return; // 状态一致 → 不动(这是"不抖"的关键)
4089
+ const toolsObj = toolsOfScope(agentCtx);
4090
+ const reg = toolsObj?.register;
4091
+ if (!toolsObj || typeof reg !== 'function') {
4092
+ traceToolReg('[scoped-tool] 拿不到 tools.register,跳过(下次再试)');
4093
+ return;
4094
+ }
4095
+ // 先撤旧的(换开关后不能叠加)
4096
+ if (st)
4097
+ for (const d of st.disposers) {
4098
+ try {
4099
+ d();
4100
+ }
4101
+ catch { /* ignore */ }
4102
+ }
4103
+ const want = [
4104
+ ...(opts.ctxTools ? ctxToolsRef() : []),
4105
+ ...(opts.groupAdmin ? qunAdminRef() : []),
4106
+ ];
4107
+ const disposers = [];
4108
+ for (const t of want) {
4109
+ try {
4110
+ const d = reg.call(toolsObj, t);
4111
+ if (typeof d === 'function')
4112
+ disposers.push(d);
4113
+ }
4114
+ catch { /* 单个工具失败不影响其它 */ }
4115
+ }
4116
+ // 全成功才认这个 sig;否则标 __retry,下一 step 重来
4117
+ const okAll = disposers.length === want.length;
4118
+ ctxScopeState.set(key, { sig: okAll ? sig : `__retry-${sig}`, disposers });
4119
+ traceToolReg(`[scoped-tool] 按会话注册 ctx=${opts.ctxTools ? 1 : 0} groupAdmin=${opts.groupAdmin ? 1 : 0}`
4120
+ + ` want=${want.length} ok=${disposers.length}`
4121
+ + ` dataRoot=${String(opts.dataRoot ?? '')} sk=${String(opts.sessionKey ?? '')}`);
4122
+ }
4123
+ catch (e) {
4124
+ traceToolReg('[scoped-tool] 异常: ' + String(e?.message ?? e).slice(0, 160));
4125
+ }
4126
+ }
4050
4127
  let __extRestrictDispose;
4051
4128
  export function applyExtDisabledRestrict(agentCtx, dataRoot, extToolNames) {
4052
4129
  try {