@zaofan/dsh-qqbot 1.5.30 → 1.5.31

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.
Files changed (37) hide show
  1. package/client/qqbot-settings.js +117 -24
  2. package/client/qqbot-settings.js.bak-inc-1790865292905 +4967 -0
  3. package/client/qqbot-settings.js.bak-inc-1790865309979 +4967 -0
  4. package/client/qqbot-settings.js.bak-newer-1790865544006 +5023 -0
  5. package/client/qqbot-settings.js.bak-stick-1790865591295 +5033 -0
  6. package/dist/features/chat-ledger.d.ts +13 -0
  7. package/dist/features/chat-ledger.d.ts.map +1 -1
  8. package/dist/features/chat-ledger.js +52 -0
  9. package/dist/features/chat-ledger.js.map +1 -1
  10. package/dist/gateway/debounce.d.ts.map +1 -1
  11. package/dist/gateway/debounce.js +32 -2
  12. package/dist/gateway/debounce.js.map +1 -1
  13. package/dist/gateway/middleware-setup.d.ts.map +1 -1
  14. package/dist/gateway/middleware-setup.js +25 -5
  15. package/dist/gateway/middleware-setup.js.map +1 -1
  16. package/dist/middleware/media-history.d.ts +27 -0
  17. package/dist/middleware/media-history.d.ts.map +1 -1
  18. package/dist/middleware/media-history.js +19 -5
  19. package/dist/middleware/media-history.js.map +1 -1
  20. package/dist/transport/inbound.d.ts +33 -0
  21. package/dist/transport/inbound.d.ts.map +1 -1
  22. package/dist/transport/inbound.js +232 -28
  23. package/dist/transport/inbound.js.map +1 -1
  24. package/dist/transport/msg-content-cache.d.ts +15 -0
  25. package/dist/transport/msg-content-cache.d.ts.map +1 -0
  26. package/dist/transport/msg-content-cache.js +105 -0
  27. package/dist/transport/msg-content-cache.js.map +1 -0
  28. package/dist/transport/quote-images.d.ts +17 -0
  29. package/dist/transport/quote-images.d.ts.map +1 -0
  30. package/dist/transport/quote-images.js +91 -0
  31. package/dist/transport/quote-images.js.map +1 -0
  32. package/dist/transport/quote-text.d.ts +30 -0
  33. package/dist/transport/quote-text.d.ts.map +1 -0
  34. package/dist/transport/quote-text.js +119 -0
  35. package/dist/transport/quote-text.js.map +1 -0
  36. package/package.json +1 -1
  37. package/settings-host.js +52 -5
@@ -3973,6 +3973,20 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
3973
3973
  if (state.tab !== 'chat' || !state.chatMore || state.chatBusy) return
3974
3974
  if (cbox.scrollTop <= 4) loadChat(false) // 顶部 → 加载更早
3975
3975
  }
3976
+ // 2026-10-01 主人要求:「滚轮向下滚一次就刷新一下」——**不管有没有滚动成功**
3977
+ // (已经停在底部也刷:方便随时确认有没有新消息,比每个来回点 🔄 刷新省事)。
3978
+ // 向上滚不拦(交给上面的"到顶部加载更早",两个功能不打架)。
3979
+ // ⚠️ 一次滚轮会连发多个 wheel 事件 → 按 400ms 时间窗合并成一次,避免刷爆;
3980
+ // ⚠️ 加载中(busy)直接跳过,别打断正在进行的请求。
3981
+ if (cbox) cbox.onwheel = function (e) {
3982
+ if (state.tab !== 'chat') return
3983
+ if (e && e.deltaY <= 0) return
3984
+ var now = Date.now()
3985
+ if (now - (state.chatWheelAt || 0) < 400) return
3986
+ state.chatWheelAt = now
3987
+ if (state.chatBusy) return
3988
+ loadChatNewer() // ★ 增量: 只把新消息接在下面, 不重绘不闪不跳位置
3989
+ }
3976
3990
  var cinput = panel.querySelector('#dk-chat-input')
3977
3991
  if (cinput) {
3978
3992
  cinput.oninput = function (e) {
@@ -4313,6 +4327,11 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
4313
4327
  state.chatMore = d.hasMore === true
4314
4328
  state.chatErr = ''
4315
4329
  state.chatOldest = state.chatItems.length ? state.chatItems[0].seq : 0
4330
+ // 2026-10-01: 记住"已加载到的**最新事件 seq**" —— 滚轮增量刷新(loadChatNewer)的起点。
4331
+ // ⚠️ 不能拿 d.tailSeq(= sess.seq = 下一个可用 seq): 正扫是 afterSeq+1 起, 记大会**漏一条/取不到**。
4332
+ var _maxSeq = 0
4333
+ state.chatItems.forEach(function (x) { var n = Number(x.seq); if (n > _maxSeq) _maxSeq = n })
4334
+ state.chatNewest = _maxSeq
4316
4335
  renderChatList(mode === 'more' ? keep : null)
4317
4336
  }).catch(function () {
4318
4337
  state.chatBusy = ''
@@ -4360,26 +4379,21 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
4360
4379
  try { dd = curDataDir() || '' } catch (e) { dd = '' }
4361
4380
  return '/api/qqbot-settings/sticker-img?id=' + encodeURIComponent(idm[1]) + (dd ? '&dataDir=' + encodeURIComponent(dd) : '')
4362
4381
  }
4363
- function renderChatList(keepScrollOffset) {
4364
- var box = document.getElementById('dk-chat-box')
4365
- if (!box) return
4382
+ // 2026-10-01: 状态行单独成函数 —— 增量追加新消息后只更新这行, 不重绘整个列表
4383
+ function updateChatStatus() {
4366
4384
  var st = document.getElementById('dk-chat-status')
4367
- if (st) {
4368
- if (state.chatBusy === 'send') st.textContent = '发送中…'
4369
- else if (chatFlash) st.textContent = chatFlash
4370
- else if (state.chatBusy) st.textContent = state.chatBusy === 'chat' ? '加载中…' : '加载更早…'
4371
- else if (state.chatErr) st.textContent = state.chatErr
4372
- else if (!state.chatItems.length) st.textContent = '暂无记录 —— 机器人和该目标聊过后会显示在这里'
4373
- else st.textContent = '共 ' + state.chatItems.length + ' 条 · ' + chatPeerName() + (state.chatMore ? ' · 上滑加载更早' : ' · 已到最早')
4374
- }
4375
- if (!state.chatItems.length) {
4376
- var emptyTxt = state.chatErr || (!chatPeerReady() ? '先选一个群/私聊目标' : '还没有聊天记录(该目标暂无活跃会话)')
4377
- box.innerHTML = '<div class="dk-empty" style="padding:26px 0">' + esc(emptyTxt) + '</div>'
4378
- return
4379
- }
4380
- var html = ''
4381
- if (state.chatMore) html += '<div class="dk-chat-top">↑ 上滑加载更早消息</div>'
4382
- html += state.chatItems.map(function (it) {
4385
+ if (!st) return
4386
+ if (state.chatBusy === 'send') st.textContent = '发送中…'
4387
+ else if (chatFlash) st.textContent = chatFlash
4388
+ else if (state.chatBusy === 'new') st.textContent = '检查新消息…'
4389
+ else if (state.chatBusy) st.textContent = state.chatBusy === 'chat' ? '加载中…' : '加载更早…'
4390
+ else if (state.chatErr) st.textContent = state.chatErr
4391
+ else if (!state.chatItems.length) st.textContent = '暂无记录 —— 机器人和该目标聊过后会显示在这里'
4392
+ else st.textContent = '共 ' + state.chatItems.length + ' 条 · ' + chatPeerName() + (state.chatMore ? ' · 上滑加载更早' : ' · 已到最早')
4393
+ }
4394
+ // 2026-10-01: 单条气泡 HTML 抽成函数 —— renderChatList 与"增量追加"共用同一套渲染
4395
+ function chatRowHTML(it) {
4396
+ {
4383
4397
  var isOut = it.dir === 'out'
4384
4398
  var who = isOut ? '我' : (it.sender || (state.sendScope === 'c2c' ? c2cSelName() : '群友'))
4385
4399
  var ava = isOut ? '🐳' : chatAvaOf(who)
@@ -4410,11 +4424,13 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
4410
4424
  + '<div class="dk-cbubble">' + imgs + txt + '</div>'
4411
4425
  + '</div>'
4412
4426
  + '</div>'
4413
- }).join('')
4414
- html += '<div class="dk-chat-bottom">—— 会话尾部 · ' + esc(chatPeerName()) + ' ——</div>'
4415
- box.innerHTML = html
4427
+ }
4428
+ }
4429
+ // 2026-10-01: 图片交互绑定抽成函数(增量追加的新节点也要绑; 属性赋值 → 幂等)
4430
+ function bindChatImgs(root) {
4431
+ if (!root) return
4416
4432
  // 图片: 单击/右键 → 灯箱放大(右键不再弹浏览器菜单, 避免误触发 onerror 变加载失败)
4417
- box.querySelectorAll('img.dk-img').forEach(function (img) {
4433
+ root.querySelectorAll('img.dk-img').forEach(function (img) {
4418
4434
  var lbOf = function () { return img.getAttribute('data-lb') || img.src }
4419
4435
  img.onclick = function (e) { e.preventDefault(); e.stopPropagation(); openLightbox(lbOf()) }
4420
4436
  img.oncontextmenu = function (e) { e.preventDefault(); e.stopPropagation(); openLightbox(lbOf()) }
@@ -4432,10 +4448,87 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
4432
4448
  img.outerHTML = '<span style="color:#888">[图加载失败]</span>'
4433
4449
  }
4434
4450
  })
4451
+ }
4452
+ function renderChatList(keepScrollOffset) {
4453
+ var box = document.getElementById('dk-chat-box')
4454
+ if (!box) return
4455
+ updateChatStatus()
4456
+ if (!state.chatItems.length) {
4457
+ var emptyTxt = state.chatErr || (!chatPeerReady() ? '先选一个群/私聊目标' : '还没有聊天记录(该目标暂无活跃会话)')
4458
+ box.innerHTML = '<div class="dk-empty" style="padding:26px 0">' + esc(emptyTxt) + '</div>'
4459
+ return
4460
+ }
4461
+ var html = ''
4462
+ if (state.chatMore) html += '<div class="dk-chat-top">↑ 上滑加载更早消息</div>'
4463
+ html += state.chatItems.map(chatRowHTML).join('')
4464
+ html += '<div class="dk-chat-bottom">—— 会话尾部 · ' + esc(chatPeerName()) + ' ——</div>'
4465
+ box.innerHTML = html
4466
+ bindChatImgs(box)
4435
4467
  // 滚动: 刷新/切目标 → 滚到底; 上滚加载更早 → 保持视口
4436
- if (keepScrollOffset == null) box.scrollTop = box.scrollHeight
4468
+ if (keepScrollOffset == null) {
4469
+ box.scrollTop = box.scrollHeight
4470
+ // 2026-10-01 主人实测「打开 dock 显示的不是最新消息」:
4471
+ // 气泡里的图是 loading="lazy",刚渲染时高度还没撑开 → 首次贴底其实只贴到"半截",
4472
+ // 等图加载完内容变高,视口就停在中间了。这里随后再补贴几次;
4473
+ // 用户一旦手动滚(wheel)立刻停手,不跟他抢滚动条。
4474
+ var stickCancel = false
4475
+ try { box.addEventListener('wheel', function () { stickCancel = true }, { once: true, passive: true }) } catch (e) { /* 老浏览器忽略 */ }
4476
+ var stickBottom = function () { if (!stickCancel) box.scrollTop = box.scrollHeight }
4477
+ setTimeout(stickBottom, 80)
4478
+ setTimeout(stickBottom, 240)
4479
+ setTimeout(stickBottom, 600)
4480
+ setTimeout(stickBottom, 1200)
4481
+ }
4437
4482
  else box.scrollTop = box.scrollHeight - keepScrollOffset
4438
4483
  }
4484
+ // 2026-10-01 主人要求:滚轮向下 → **增量**追加新消息
4485
+ // · 只 append 新气泡(不 box.innerHTML 重绘 → **不闪**)
4486
+ // · **不动 scrollTop**(不跳位置;想看最新的自己继续滑)
4487
+ function appendChatRows(list) {
4488
+ var box = document.getElementById('dk-chat-box')
4489
+ if (!box || !Array.isArray(list) || !list.length) return
4490
+ var html = list.map(chatRowHTML).join('')
4491
+ var tail = box.querySelector('.dk-chat-bottom')
4492
+ if (tail) tail.insertAdjacentHTML('beforebegin', html)
4493
+ else box.insertAdjacentHTML('beforeend', html)
4494
+ bindChatImgs(box)
4495
+ }
4496
+ // 滚轮触发的"取更新的消息":afterSeq = 当前已加载的最新位置
4497
+ function loadChatNewer() {
4498
+ if (!state.ns || !chatPeerReady() || state.chatBusy) return
4499
+ var after = Number(state.chatNewest || 0)
4500
+ if (!(after > 0)) { loadChat(true); return } // 还没加载过 → 走整页首屏
4501
+ state.chatBusy = 'new'
4502
+ updateChatStatus()
4503
+ var peerId = state.sendScope === 'c2c' ? state.sendTo : state.gid
4504
+ var q = 'ns=' + encodeURIComponent(state.ns) + '&scope=' + state.sendScope
4505
+ + '&peerId=' + encodeURIComponent(peerId) + '&afterSeq=' + after + '&limit=100'
4506
+ api('chat/history', q).then(function (d) {
4507
+ state.chatBusy = ''
4508
+ if (!d || !d.ok) { updateChatStatus(); return }
4509
+ var list = (Array.isArray(d.items) ? d.items : []).filter(function (it) {
4510
+ return it && ((typeof it.text === 'string' && it.text) || (Array.isArray(it.images) && it.images.length))
4511
+ })
4512
+ // 去重键含 seq+方向+正文前缀 —— 群历史打包块里多条共用同一 seq, 只按 seq 去重会误删
4513
+ var keyOf = function (x) { return String(x.seq) + '|' + String(x.dir) + '|' + String(x.text || '').slice(0, 24) }
4514
+ var have = {}
4515
+ state.chatItems.forEach(function (x) { have[keyOf(x)] = 1 })
4516
+ // ⚠️ 2026-10-01 主人实测「向下滚后最新消息后面冒出历史消息」:
4517
+ // 群打包块([历史]…[/历史])会被拆成多条挂在**同一个新 seq** 上, 内容却是**更早**的消息;
4518
+ // 不过滤就会被当新事件整块接到列表尾部。只收"时间不早于列表尾部那条"的条目。
4519
+ var _tailT = state.chatItems.length ? (Number(state.chatItems[state.chatItems.length - 1].time) || 0) : 0
4520
+ var fresh = list.filter(function (x) {
4521
+ return !have[keyOf(x)] && (Number(x.time) || 0) >= _tailT
4522
+ })
4523
+ if (fresh.length) {
4524
+ state.chatItems = state.chatItems.concat(fresh)
4525
+ appendChatRows(fresh) // ★ 增量追加: 不重绘、不闪、不跳位置
4526
+ }
4527
+ // 已加载位置 = 列表里最大的事件 seq(同 ①:不能用 tailSeq)
4528
+ state.chatItems.forEach(function (x) { var n = Number(x.seq); if (n > state.chatNewest) state.chatNewest = n })
4529
+ updateChatStatus()
4530
+ }).catch(function () { state.chatBusy = ''; updateChatStatus() })
4531
+ }
4439
4532
  // 聊天文本渲染(2026-09-13 引用消息功能): 把引用块渲染成"引用样式"气泡(左边框+灰底), 其余正文照常。
4440
4533
  // 2026-09-15 标记改短([引]…[/引][当前]) —— **新旧都认**, 否则老会话里的英文标记会显示成裸文本。
4441
4534
  function chatRenderText(t) {