koishi-plugin-chat-patch 5.1.0 → 5.3.1

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 (53) hide show
  1. package/client/web/dist/assets/Chat-Bo5JdcNg.js +28 -0
  2. package/client/web/dist/assets/{Chat-CTdyeaX2.css → Chat-C-rmzMIw.css} +1 -1
  3. package/client/web/dist/assets/{MsgBody-DLeOklIb.js → MsgBody-h4C1nnBQ.js} +1 -1
  4. package/client/web/dist/assets/MsgBody.vue_vue_type_script_setup_true_lang-a902ps2r.js +67 -0
  5. package/client/web/dist/assets/{index-Cz89G4we.js → index-CdTmWcO4.js} +121 -129
  6. package/client/web/dist/assets/index-wjp_A4n1.css +1 -0
  7. package/client/web/dist/assets/pinyin-Dsq98249.js +157657 -0
  8. package/client/web/dist/index.html +2 -2
  9. package/client/web/dist/notice_local.json +1 -0
  10. package/client/web/package-lock.json +49 -0
  11. package/client/web/package.json +1 -0
  12. package/client/web/public/notice_local.json +1 -0
  13. package/client/web/src/components/AboutPan.vue +2 -1
  14. package/client/web/src/components/BulletinBody.vue +1 -1
  15. package/client/web/src/components/EmojiFace.vue +0 -6
  16. package/client/web/src/components/FacePan.vue +0 -12
  17. package/client/web/src/components/FriendBody.vue +1 -0
  18. package/client/web/src/components/MsgBody.vue +4 -4
  19. package/client/web/src/components/SettingsTab.vue +133 -0
  20. package/client/web/src/components/TinySessionBody.vue +1 -0
  21. package/client/web/src/components/UpdatePan.vue +2 -2
  22. package/client/web/src/components/tooltip/UserInfoTooltip.vue +1 -1
  23. package/client/web/src/function/connect.ts +81 -28
  24. package/client/web/src/function/model/emoji.ts +7 -67
  25. package/client/web/src/function/msg.ts +68 -26
  26. package/client/web/src/function/satori-model.ts +8 -0
  27. package/client/web/src/function/satori.ts +31 -1
  28. package/client/web/src/function/utils/appUtil.ts +1 -4
  29. package/client/web/src/function/utils/avatarUtil.ts +18 -3
  30. package/client/web/src/function/utils/msgUtil.ts +86 -52
  31. package/client/web/src/function/utils/pinyin.ts +25 -65
  32. package/client/web/src/function/utils/sessionUtil.ts +90 -14
  33. package/client/web/src/main.ts +5 -0
  34. package/client/web/src/pages/Chat.vue +3 -4
  35. package/client/web/src/pages/Friends.vue +72 -30
  36. package/client/web/src/pages/Info.vue +3 -3
  37. package/client/web/src/pages/Messages.vue +59 -26
  38. package/client/web/src/pages/Options.vue +4 -4
  39. package/client/web/src/pages/Qzone.vue +2 -2
  40. package/client/web/src/pages/chat-view/SystemNotice.vue +3 -3
  41. package/client/web/src/pages/options/OptAccount.vue +6 -4
  42. package/client/web/src/pages/options/OptDev.vue +1 -14
  43. package/lib/index.js +14 -14
  44. package/lib/types.d.ts +115 -0
  45. package/package.json +1 -1
  46. package/src/bootstrap.ts +1 -0
  47. package/src/config.ts +1 -1
  48. package/src/recorder.ts +7 -7
  49. package/src/types.ts +4 -0
  50. package/src/web.ts +8 -9
  51. package/client/web/dist/assets/Chat-BoNiWGxb.js +0 -28
  52. package/client/web/dist/assets/MsgBody.vue_vue_type_script_setup_true_lang-CGjy4Jzz.js +0 -67
  53. package/client/web/dist/assets/index-BgOX_tNa.css +0 -1
@@ -38,17 +38,17 @@
38
38
  </div>
39
39
  </div>
40
40
  <div id="message-list-body" class="robot-accordion-list">
41
- <div v-for="bot in satoriLogins" :key="'bot-' + bot.selfId"
42
- :class="['robot-accordion', { expanded: bot.selfId === activeBotId }]">
41
+ <div v-for="bot in satoriLogins" :key="'bot-' + botKey(bot.platform, bot.selfId)"
42
+ :class="['robot-accordion', { expanded: botKey(bot.platform, bot.selfId) === activeBotId }]">
43
43
  <div class="robot-accordion-header" @click="toggleRobot(bot)">
44
- <img :src="bot.avatar || '/img/icons/icon.svg'" :alt="bot.name" @error="avatarError">
44
+ <img data-avatar :src="bot.avatar || '/img/icons/icon.svg'" :alt="bot.name" @error="avatarError">
45
45
  <div>
46
46
  <span :title="bot.name">{{ bot.name }}</span>
47
47
  <small>{{ bot.platform }}</small>
48
48
  </div>
49
- <font-awesome-icon :icon="['fas', bot.selfId === activeBotId ? 'angle-up' : 'angle-down']" />
49
+ <font-awesome-icon :icon="['fas', botKey(bot.platform, bot.selfId) === activeBotId ? 'angle-up' : 'angle-down']" />
50
50
  </div>
51
- <div v-if="bot.selfId === activeBotId" class="robot-accordion-content">
51
+ <div v-if="botKey(bot.platform, bot.selfId) === activeBotId" class="robot-accordion-content">
52
52
  <div v-if="!showGroupAssist &&
53
53
  !contactStore.systemNoticesList &&
54
54
  (!contactStore.groupAssistList || contactStore.groupAssistList.length === 0) &&
@@ -98,7 +98,7 @@
98
98
  <!-- 其他消息 -->
99
99
  <FriendBody
100
100
  v-for="item in contactStore.onMsgList"
101
- :key="'inMessage-' + (item.user_id ? item.user_id : item.group_id)"
101
+ :key="'inMessage-' + (item.channel_id || item.channelId || item.user_id || item.group_id)"
102
102
  :select="chat.show.id === item.user_id || (chat.show.id === item.group_id && chat.group_name != '')"
103
103
  :menu="menu.select && menu.select == item"
104
104
  :data="item"
@@ -150,7 +150,7 @@
150
150
  <!-- 其他消息 -->
151
151
  <FriendBody
152
152
  v-for="item in contactStore.groupAssistList"
153
- :key="'inMessage-' + (item.user_id ? item.user_id : item.group_id)"
153
+ :key="'inMessage-' + (item.channel_id || item.channelId || item.user_id || item.group_id)"
154
154
  :select="chat.show.id === item.user_id || (chat.show.id === item.group_id && chat.group_name != '')"
155
155
  :menu="menu.select && menu.select == item"
156
156
  :data="item"
@@ -218,7 +218,7 @@
218
218
  import { library } from '@fortawesome/fontawesome-svg-core'
219
219
  import { login as loginInfo } from '../function/connect'
220
220
  import { Connector, flushPendingBotEvents, loadContactsFromCache, restoreBotStateFromMessageCache } from '../function/connect'
221
- import { getActiveBot, getLogins, setActiveBot } from '../function/satori'
221
+ import { botKey, getActiveBot, getLogins, setActiveBot } from '../function/satori'
222
222
  import { getShowName, updateBaseOnMsgList } from '../function/utils/msgUtil'
223
223
 
224
224
  import {
@@ -232,7 +232,7 @@
232
232
  import { refreshFavicon } from '../function/favicon'
233
233
  import { backend } from '../runtime/backend'
234
234
  import History from '../components/History.vue'
235
- import { normalizeSessionId } from '../function/utils/sessionUtil'
235
+ import { findSessionContact, normalizeSessionId } from '../function/utils/sessionUtil'
236
236
  import { avatarError } from '../function/utils/avatarUtil'
237
237
  import { useUIStore } from '../state/ui'
238
238
  import { useAuthStore } from '../state/auth'
@@ -249,7 +249,12 @@
249
249
  const chatStore = useChatStore()
250
250
  const settingsStore = useSettingsStore()
251
251
  const satoriLogins = ref<Array<{ platform: string; selfId: string; name: string; avatar?: string; status?: number; features?: string[] }>>(getLogins())
252
- const activeBotId = ref(getActiveBot()?.selfId ?? loginInfo.selectedSatoriBot ?? '')
252
+ const active = getActiveBot()
253
+ const activeBotId = ref(
254
+ active?.platform && active.selfId
255
+ ? botKey(active.platform, active.selfId)
256
+ : loginInfo.selectedSatoriBot ?? '',
257
+ )
253
258
 
254
259
  watch(() => loginInfo.satoriLogins, (list) => {
255
260
  satoriLogins.value = list ?? []
@@ -259,7 +264,7 @@
259
264
  }, { immediate: true })
260
265
 
261
266
  const currentBotName = computed(() => {
262
- return satoriLogins.value.find((item) => item.selfId === activeBotId.value)?.name ?? ''
267
+ return satoriLogins.value.find((item) => botKey(item.platform, item.selfId) === activeBotId.value)?.name ?? ''
263
268
  })
264
269
 
265
270
  function backToRobots() {
@@ -280,27 +285,39 @@
280
285
  }
281
286
 
282
287
  function restoreBotStateUi(bot: { platform: string; selfId: string }) {
283
- const restored = contactStore.botStates.get(bot.selfId)
288
+ const restored = contactStore.botStates.get(botKey(bot.platform, bot.selfId))
284
289
  if (!restored) return
285
290
  contactStore.baseOnMsgList.clear()
286
- for (const [key, value] of restored.baseList) {
287
- contactStore.baseOnMsgList.set(normalizeSessionId(key), value)
291
+ for (const [, value] of restored.baseList) {
292
+ const id = String(value.channel_id || value.channelId || value.user_id || value.group_id || '')
293
+ if (id && id !== '0') {
294
+ contactStore.baseOnMsgList.set(normalizeSessionId(id), value)
295
+ const legacyId = String(value.user_id || value.group_id || '')
296
+ if (legacyId && legacyId !== id) {
297
+ contactStore.baseOnMsgList.set(normalizeSessionId(legacyId), value)
298
+ }
299
+ }
288
300
  }
289
301
  for (const item of restored.onMsgList) {
290
- const id = String(item.user_id ?? item.group_id ?? '')
302
+ const id = String(item.channel_id || item.channelId || item.user_id || item.group_id || '')
291
303
  if (!id || id === '0') continue
292
304
  contactStore.baseOnMsgList.set(
293
305
  normalizeSessionId(id),
294
306
  item as UserFriendElem & UserGroupElem,
295
307
  )
308
+ const legacyId = String(item.user_id || item.group_id || '')
309
+ if (legacyId && legacyId !== id) {
310
+ contactStore.baseOnMsgList.set(normalizeSessionId(legacyId), item as UserFriendElem & UserGroupElem)
311
+ }
296
312
  }
297
313
  contactStore.onMsgList = restored.onMsgList
298
314
  }
299
315
 
300
316
  function selectRobot(bot: { platform: string; selfId: string; name: string; avatar?: string; features?: string[] }) {
301
- if (bot.selfId === activeBotId.value) return
317
+ const key = botKey(bot.platform, bot.selfId)
318
+ if (key === activeBotId.value) return
302
319
  snapshotCurrentBot()
303
- activeBotId.value = bot.selfId
320
+ activeBotId.value = key
304
321
  setActiveBot(bot.platform, bot.selfId)
305
322
  loginInfo.uin = bot.selfId
306
323
  loginInfo.nickname = bot.name
@@ -313,9 +330,9 @@
313
330
  platform: bot.platform,
314
331
  avatar: bot.avatar,
315
332
  satoriLogins: satoriLogins.value,
316
- selectedSatoriBot: bot.selfId,
333
+ selectedSatoriBot: key,
317
334
  }
318
- const saved = contactStore.botStates.get(bot.selfId)
335
+ const saved = contactStore.botStates.get(key)
319
336
  if (saved) {
320
337
  contactStore.userList = saved.userList
321
338
  contactStore.baseOnMsgList.clear()
@@ -343,7 +360,7 @@
343
360
  }
344
361
 
345
362
  function toggleRobot(bot: { platform: string; selfId: string; name: string; avatar?: string; features?: string[] }) {
346
- if (activeBotId.value === bot.selfId) {
363
+ if (activeBotId.value === botKey(bot.platform, bot.selfId)) {
347
364
  backToRobots()
348
365
  } else {
349
366
  selectRobot(bot)
@@ -376,7 +393,7 @@
376
393
  * @param data 联系人对象
377
394
  */
378
395
  function userClick(data: UserFriendElem & UserGroupElem) {
379
- const id = data.user_id ? data.user_id : data.group_id
396
+ const id = data.group_id || data.user_id
380
397
  if (id === undefined || id === null || String(id) === '' || String(id) === '0') return
381
398
  if (!trRead.value && id != props.chat.show.id) {
382
399
  if (uiStore.openSideBar) {
@@ -385,11 +402,11 @@
385
402
  const back = {
386
403
  // 临时会话标志
387
404
  temp: data.group_name == '' ? data.group_id : undefined,
388
- type: data.user_id ? 'user' : 'group',
405
+ type: data.group_id ? 'group' : 'user',
389
406
  id: id,
390
407
  name: getShowName(data.group_name || data.nickname, data.remark),
391
408
  avatar: data.avatar || '/img/icons/icon.svg',
392
- channel_id: data.channel_id ?? data.channelId,
409
+ channel_id: data.channel_id || data.channelId,
393
410
  guild_id: data.guild_id ?? data.guildId,
394
411
  }
395
412
  if (props.chat.id != back.id) {
@@ -401,9 +418,10 @@
401
418
  }
402
419
  }
403
420
  // 清除新消息标记
404
- const sessionKey = data.channel_id ?? data.channelId ?? id
421
+ const sessionKey = data.channel_id || data.channelId || id
405
422
  const item = contactStore.baseOnMsgList.get(normalizeSessionId(sessionKey))
406
423
  ?? contactStore.baseOnMsgList.get(normalizeSessionId(id))
424
+ ?? findSessionContact(Array.from(contactStore.baseOnMsgList.values()), id)
407
425
  if(item) {
408
426
  if(item.new_msg) {
409
427
  item.new_msg = false
@@ -411,6 +429,7 @@
411
429
  }
412
430
  item.highlight = undefined
413
431
  contactStore.baseOnMsgList.set(normalizeSessionId(sessionKey), item)
432
+ updateBaseOnMsgList()
414
433
  // 关闭所有通知
415
434
  new Notify().closeAll((item.group_id ?? item.user_id).toString())
416
435
  }
@@ -474,9 +493,10 @@
474
493
  */
475
494
  function readMsg(data: UserFriendElem & UserGroupElem) {
476
495
  const id = data.group_id ? data.group_id : data.user_id
477
- const sessionKey = data.channel_id ?? data.channelId ?? id
496
+ const sessionKey = data.channel_id || data.channelId || id
478
497
  const item = contactStore.baseOnMsgList.get(normalizeSessionId(sessionKey))
479
498
  ?? contactStore.baseOnMsgList.get(normalizeSessionId(id))
499
+ ?? findSessionContact(Array.from(contactStore.baseOnMsgList.values()), id)
480
500
  if(item) {
481
501
  if(item.new_msg) {
482
502
  item.new_msg = false
@@ -484,6 +504,7 @@
484
504
  }
485
505
  item.highlight = undefined
486
506
  contactStore.baseOnMsgList.set(normalizeSessionId(sessionKey), item)
507
+ updateBaseOnMsgList()
487
508
  }
488
509
  // pop
489
510
  new PopInfo().add(
@@ -518,7 +539,16 @@
518
539
  )
519
540
  if (topList.indexOf(id) >= 0) {
520
541
  item.always_top = true
521
- contactStore.baseOnMsgList.set(normalizeSessionId(id), item)
542
+ const sessionId = String(
543
+ item.channel_id
544
+ ?? item.channelId
545
+ ?? item.user_id
546
+ ?? item.group_id
547
+ ?? '',
548
+ )
549
+ if (sessionId && sessionId !== '0') {
550
+ contactStore.baseOnMsgList.set(normalizeSessionId(sessionId), item)
551
+ }
522
552
  }
523
553
  })
524
554
  }
@@ -558,7 +588,10 @@
558
588
  break
559
589
  case 'remove': {
560
590
  const id = item.user_id ? item.user_id : item.group_id
591
+ const sessionId = String(item.channel_id || item.channelId || id || '')
561
592
  contactStore.baseOnMsgList.delete(normalizeSessionId(id))
593
+ contactStore.baseOnMsgList.delete(normalizeSessionId(sessionId))
594
+ updateBaseOnMsgList()
562
595
  refreshFavicon()
563
596
  break
564
597
  }
@@ -11,7 +11,7 @@
11
11
  <!-- 保留占位元素,避免设置页右侧布局被原有左侧选择器样式影响 -->
12
12
  <div class="opt-side-placeholder" style="display: none" />
13
13
  <div>
14
- <BcTab v-show="show" :title="$t('设置')" class="opt-tab">
14
+ <SettingsTab v-show="show" class="opt-tab">
15
15
  <div :name="$t('账号')">
16
16
  <OptAccount :config="config" />
17
17
  </div>
@@ -27,18 +27,18 @@
27
27
  <div v-if="showAbout" :name="$t('关于')">
28
28
  <AboutPan class="opt-about" show-u-i />
29
29
  </div>
30
- </BcTab>
30
+ </SettingsTab>
31
31
  </div>
32
32
  </div>
33
33
  </template>
34
34
 
35
35
  <script setup lang="ts">
36
- import { ref, watch, onMounted, nextTick } from 'vue'
36
+ import { ref, watch, onMounted } from 'vue'
37
37
 
38
38
  import { i18n } from '../main'
39
39
  import { useSettingsStore } from '../state/settings'
40
40
 
41
- import BcTab from 'vue3-bcui/packages/bc-tab'
41
+ import SettingsTab from '../components/SettingsTab.vue'
42
42
  import OptAccount from './options/OptAccount.vue'
43
43
  import OptView from './options/OptView.vue'
44
44
  import OptDev from './options/OptDev.vue'
@@ -65,7 +65,7 @@
65
65
  class="qzone-feed-card ss-card">
66
66
  <article>
67
67
  <header class="qzone-feed-card-header">
68
- <img :src="item.avatar" :alt="item.nickname">
68
+ <img data-avatar :src="item.avatar" :alt="item.nickname">
69
69
  <div class="qzone-feed-meta">
70
70
  <a>{{ item.nickname }}</a>
71
71
  <span>{{ item.timeText }}</span>
@@ -149,7 +149,7 @@
149
149
  </template>
150
150
  </div>
151
151
  <div class="qzone-feed-reply">
152
- <img :src="authStore.loginInfo.avatar || '/img/icons/icon.svg'">
152
+ <img data-avatar :src="authStore.loginInfo.avatar || '/img/icons/icon.svg'">
153
153
  <label :for="`qzone-reply-input-${item.id}`" class="sr-only">{{ $t('评论输入框') }}</label>
154
154
  <input :id="`qzone-reply-input-${item.id}`"
155
155
  v-model="getReplyDraft(item.id).content"
@@ -20,7 +20,7 @@
20
20
  :key="'sysNot-' + index">
21
21
  <div v-if="notice.request_type == 'friend'">
22
22
  <div>
23
- <img :src="notice.avatar || '/img/icons/icon.svg'">
23
+ <img data-avatar :src="notice.avatar || '/img/icons/icon.svg'">
24
24
  <div>
25
25
  <span>{{ notice.user_id }}
26
26
  {{ $t('请求加为好友') }}</span>
@@ -49,7 +49,7 @@
49
49
  </div>
50
50
  <div v-else-if="notice.request_type == 'group'">
51
51
  <div v-if="notice.sub_type == 'add'">
52
- <img :src="notice.avatar || '/img/icons/icon.svg'">
52
+ <img data-avatar :src="notice.avatar || '/img/icons/icon.svg'">
53
53
  <div>
54
54
  <span>{{ getName(notice.user_id) }}
55
55
  {{ $t('申请加入群聊') }}
@@ -68,7 +68,7 @@
68
68
  </div>
69
69
  <div v-else>
70
70
  <!-- TODO:这情况会出现在 notice 里?记不太清了,先放着吧 😭 -->
71
- <img :src="notice.group_avatar || '/img/icons/icon.svg'">
71
+ <img data-avatar :src="notice.group_avatar || '/img/icons/icon.svg'">
72
72
  <div>
73
73
  <span>{{ getName(notice.user_id) }}
74
74
  {{ $t('邀请你加入群聊') }}
@@ -13,7 +13,7 @@
13
13
  <div v-for="account in accounts"
14
14
  :key="account.platform + ':' + account.selfId"
15
15
  :class="['ss-card', 'account-info', { active: isActive(account) }]">
16
- <img :src="account.avatar || '/img/icons/icon.svg'">
16
+ <img data-avatar :src="account.avatar || '/img/icons/icon.svg'">
17
17
  <div>
18
18
  <div>
19
19
  <span>{{ account.name || account.selfId }}</span>
@@ -38,7 +38,7 @@
38
38
  <script setup lang="ts">
39
39
  import { computed } from 'vue'
40
40
  import { login as loginInfo } from '../../function/connect'
41
- import { getActiveBot } from '../../function/satori'
41
+ import { botKey, getActiveBot, getLogins } from '../../function/satori'
42
42
  import { useAuthStore } from '../../state/auth'
43
43
  import { i18n } from '../../main'
44
44
 
@@ -63,9 +63,11 @@ const accounts = computed<SatoriAccount[]>(() => {
63
63
  const activeBot = computed(() => {
64
64
  const current = getActiveBot()
65
65
  if (current?.platform && current.selfId) return current
66
+ const selectedKey = String(authStore.loginInfo.selectedSatoriBot ?? '')
67
+ const selected = getLogins().find((item) => botKey(item.platform, item.selfId) === selectedKey)
66
68
  return {
67
- platform: String(authStore.loginInfo.platform ?? ''),
68
- selfId: String(authStore.loginInfo.uin ?? authStore.loginInfo.selectedSatoriBot ?? ''),
69
+ platform: String(selected?.platform ?? authStore.loginInfo.platform ?? ''),
70
+ selfId: String(selected?.selfId ?? authStore.loginInfo.uin ?? ''),
69
71
  }
70
72
  })
71
73
 
@@ -315,20 +315,7 @@ import packageInfo from '../../../package.json'
315
315
  }
316
316
 
317
317
  info += 'Network Info:\n'
318
- const testList = [
319
- ['Github ', 'https://api.github.com'],
320
- ['SSQQ API ', 'https://api.stapxs.cn'],
321
- ]
322
- for (const item of testList) {
323
- const start = new Date().getTime()
324
- try {
325
- await fetch(item[1], { method: 'GET' })
326
- const end = new Date().getTime()
327
- info += ` ${item[0]} -> ${end - start} ms\n`
328
- } catch (e) {
329
- info += ` ${item[0]} -> failed\n`
330
- }
331
- }
318
+ info += ' local only\n'
332
319
  info += '```'
333
320
  // 构建 popBox 内容
334
321
  const popInfo = {
package/lib/index.js CHANGED
@@ -844,8 +844,8 @@ var _Recorder = class _Recorder {
844
844
  }
845
845
  async handleSession(session) {
846
846
  const platform = session.platform || "unknown";
847
- if (this.isBlocked(platform)) return;
848
847
  if (!MESSAGE_TYPES.has(session.type)) return;
848
+ if (this.isBlocked(platform)) return;
849
849
  const event = session.toJSON();
850
850
  const message = event.message;
851
851
  const record = {
@@ -866,15 +866,15 @@ var _Recorder = class _Recorder {
866
866
  };
867
867
  try {
868
868
  await this.database.appendMessage(record);
869
- void this.cacheMessageContacts(event).catch((error) => {
870
- this.logger.warn("缓存消息联系人失败:", error);
871
- });
872
- void this.cacheMessageMedia(event).catch((error) => {
873
- this.logger.warn("异步缓存消息媒体失败:", error);
874
- });
875
869
  } catch (error) {
876
870
  this.logger.warn("写入历史消息失败:", error);
877
871
  }
872
+ void this.cacheMessageContacts(event).catch((error) => {
873
+ this.logger.warn("缓存消息联系人失败:", error);
874
+ });
875
+ void this.cacheMessageMedia(event).catch((error) => {
876
+ this.logger.warn("异步缓存消息媒体失败:", error);
877
+ });
878
878
  }
879
879
  async cacheMessageContacts(event) {
880
880
  const platform = event.platform || "";
@@ -1622,7 +1622,8 @@ function registerBootstrap(ctx, config, database, logger) {
1622
1622
  return {
1623
1623
  endpoint: resolveEndpoint(ctx, config),
1624
1624
  token: ctx.satori?.server?.config?.token ?? "",
1625
- basePath: config.basePath
1625
+ basePath: config.basePath,
1626
+ blockedPlatforms: config.blockedPlatforms ?? []
1626
1627
  };
1627
1628
  }, { authority: 4 });
1628
1629
  ctx.console.addListener("chat-patch/history", async (query) => {
@@ -1706,13 +1707,12 @@ function registerWeb(ctx, config, database, contactCache, logger) {
1706
1707
  koa.status = 404;
1707
1708
  koa.body = "";
1708
1709
  }, "servePublicFile");
1709
- const registerDevPublic = /* @__PURE__ */ __name((pattern, prefix) => {
1710
- ctx.server.get(pattern, async (koa, next) => {
1711
- if (!getVite()) return next();
1710
+ const registerPublic = /* @__PURE__ */ __name((pattern, prefix) => {
1711
+ ctx.server.get(pattern, async (koa) => {
1712
1712
  await servePublicFile(koa, prefix);
1713
1713
  });
1714
- }, "registerDevPublic");
1715
- registerDevPublic("/img(.*)", "img");
1714
+ }, "registerPublic");
1715
+ registerPublic("/img(.*)", "img");
1716
1716
  const toExtension2 = /* @__PURE__ */ __name((value) => {
1717
1717
  const ext = value.startsWith(".") ? value : `.${value}`;
1718
1718
  return ext.toLowerCase();
@@ -2283,7 +2283,7 @@ var Config = import_koishi2.Schema.intersect([
2283
2283
  blockedPlatforms: import_koishi2.Schema.array(import_koishi2.Schema.object({
2284
2284
  platformName: import_koishi2.Schema.string(),
2285
2285
  exactMatch: import_koishi2.Schema.boolean().default(true)
2286
- })).role("table").description("不记录历史的平台").default([
2286
+ })).role("table").description("屏蔽消息接收的平台").default([
2287
2287
  { platformName: "qq", exactMatch: true },
2288
2288
  { platformName: "qqguild", exactMatch: true },
2289
2289
  { platformName: "sandbox", exactMatch: false }
package/lib/types.d.ts ADDED
@@ -0,0 +1,115 @@
1
+ import type { Awaitable } from 'koishi';
2
+ export interface SatoriBootstrap {
3
+ endpoint: string;
4
+ token: string;
5
+ basePath: string;
6
+ blockedPlatforms: Array<{
7
+ platformName: string;
8
+ exactMatch: boolean;
9
+ }>;
10
+ }
11
+ export interface MessageRecord {
12
+ id?: string;
13
+ sequence?: number;
14
+ type: string;
15
+ platform: string;
16
+ selfId: string;
17
+ channelId?: string;
18
+ guildId?: string;
19
+ userId?: string;
20
+ timestamp: number;
21
+ timestampMs?: number;
22
+ receivedAt?: number;
23
+ content?: string;
24
+ elements?: unknown[];
25
+ raw?: unknown;
26
+ }
27
+ export type SelfMessageChannelType = 'group' | 'user';
28
+ export type SelfMessageSource = 'webui' | 'bot' | 'plugin';
29
+ export interface SelfMessageRecord {
30
+ id: string;
31
+ platform: string;
32
+ selfId: string;
33
+ channelId: string;
34
+ guildId?: string;
35
+ channelType?: SelfMessageChannelType;
36
+ messageId?: string;
37
+ content?: string;
38
+ elements?: unknown[];
39
+ message?: unknown[];
40
+ forwardId?: string;
41
+ forwardContent?: unknown[];
42
+ sentAt: number;
43
+ sequence: number;
44
+ source: SelfMessageSource;
45
+ kind: string;
46
+ fingerprint?: string;
47
+ }
48
+ export interface SelfMessagePayload {
49
+ id?: string;
50
+ platform: string;
51
+ selfId: string;
52
+ channelId: string;
53
+ guildId?: string;
54
+ channelType?: SelfMessageChannelType;
55
+ messageId?: string;
56
+ content?: string;
57
+ elements?: unknown[];
58
+ message?: unknown[];
59
+ forwardId?: string;
60
+ forwardContent?: unknown[];
61
+ sentAt?: number;
62
+ sequence?: number;
63
+ source?: SelfMessageSource;
64
+ kind?: string;
65
+ }
66
+ export interface HistoryQuery {
67
+ platform: string;
68
+ selfId: string;
69
+ channelId: string;
70
+ limit?: number;
71
+ }
72
+ export interface HistoryResult {
73
+ messages: MessageRecord[];
74
+ }
75
+ export interface PinnedState {
76
+ bots: string[];
77
+ channels: string[];
78
+ }
79
+ export interface PluginConfigPayload {
80
+ basePath: string;
81
+ maxMessagesPerChannel: number;
82
+ historyPageSize: number;
83
+ maxMediaFiles: number;
84
+ blockedPlatforms: Array<{
85
+ platformName: string;
86
+ exactMatch: boolean;
87
+ }>;
88
+ loggerinfo: boolean;
89
+ }
90
+ export interface ContactCacheItem {
91
+ id: string;
92
+ name: string;
93
+ avatar?: string;
94
+ channelId?: string;
95
+ guildId?: string;
96
+ raw?: unknown;
97
+ }
98
+ export interface ContactCacheQuery {
99
+ platform: string;
100
+ selfId: string;
101
+ type: string;
102
+ contacts?: ContactCacheItem[];
103
+ append?: boolean;
104
+ }
105
+ export interface ContactCacheResult {
106
+ contacts?: ContactCacheItem[];
107
+ }
108
+ declare module '@koishijs/console' {
109
+ interface Events {
110
+ 'chat-patch/bootstrap'(): SatoriBootstrap;
111
+ 'chat-patch/history'(query: HistoryQuery): Awaitable<HistoryResult>;
112
+ 'chat-patch/config'(): PluginConfigPayload;
113
+ 'chat-patch/contact-cache'(query: ContactCacheQuery): Awaitable<ContactCacheResult>;
114
+ }
115
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-chat-patch",
3
3
  "description": "[<ruby>chat-patch<rp>(</rp><rt>点我预览效果</rt><rp>)</rp></ruby>](https://i0.hdslb.com/bfs/openplatform/71074dfc9e5256fc3333d8bd8478bec1af874046.png) 视奸小插件((bushi( (低性能警告)。手机端适配。灵感来自 chat 插件。",
4
- "version": "5.1.0",
4
+ "version": "5.3.1",
5
5
  "scripts": {
6
6
  "build:web": "npm --prefix client/web run build",
7
7
  "prepack": "npm run build:web",
package/src/bootstrap.ts CHANGED
@@ -31,6 +31,7 @@ export function registerBootstrap(
31
31
  endpoint: resolveEndpoint(ctx, config),
32
32
  token: ctx.satori?.server?.config?.token ?? '',
33
33
  basePath: config.basePath,
34
+ blockedPlatforms: config.blockedPlatforms ?? [],
34
35
  }
35
36
  }, { authority: 4 })
36
37
 
package/src/config.ts CHANGED
@@ -25,7 +25,7 @@ export const Config: Schema<Config> = Schema.intersect([
25
25
  blockedPlatforms: Schema.array(Schema.object({
26
26
  platformName: Schema.string(),
27
27
  exactMatch: Schema.boolean().default(true),
28
- })).role('table').description('不记录历史的平台').default([
28
+ })).role('table').description('屏蔽消息接收的平台').default([
29
29
  { platformName: 'qq', exactMatch: true },
30
30
  { platformName: 'qqguild', exactMatch: true },
31
31
  { platformName: 'sandbox', exactMatch: false },
package/src/recorder.ts CHANGED
@@ -41,8 +41,8 @@ export class Recorder {
41
41
 
42
42
  private async handleSession(session: Session) {
43
43
  const platform = session.platform || 'unknown'
44
- if (this.isBlocked(platform)) return
45
44
  if (!MESSAGE_TYPES.has(session.type)) return
45
+ if (this.isBlocked(platform)) return
46
46
 
47
47
  const event = session.toJSON()
48
48
  const message = event.message
@@ -65,15 +65,15 @@ export class Recorder {
65
65
 
66
66
  try {
67
67
  await this.database.appendMessage(record)
68
- void this.cacheMessageContacts(event).catch((error) => {
69
- this.logger.warn('缓存消息联系人失败:', error)
70
- })
71
- void this.cacheMessageMedia(event).catch((error) => {
72
- this.logger.warn('异步缓存消息媒体失败:', error)
73
- })
74
68
  } catch (error) {
75
69
  this.logger.warn('写入历史消息失败:', error)
76
70
  }
71
+ void this.cacheMessageContacts(event).catch((error) => {
72
+ this.logger.warn('缓存消息联系人失败:', error)
73
+ })
74
+ void this.cacheMessageMedia(event).catch((error) => {
75
+ this.logger.warn('异步缓存消息媒体失败:', error)
76
+ })
77
77
  }
78
78
 
79
79
  private async cacheMessageContacts(event: ReturnType<Session['toJSON']>) {
package/src/types.ts CHANGED
@@ -5,6 +5,10 @@ export interface SatoriBootstrap {
5
5
  endpoint: string
6
6
  token: string
7
7
  basePath: string
8
+ blockedPlatforms: Array<{
9
+ platformName: string
10
+ exactMatch: boolean
11
+ }>
8
12
  }
9
13
 
10
14
  export interface MessageRecord {
package/src/web.ts CHANGED
@@ -82,15 +82,14 @@ export function registerWeb(
82
82
  koa.body = ''
83
83
  }
84
84
 
85
- // web 应用里仍有 /img 这类运行时绝对路径,开发模式直接由 Koishi 服务托管
86
- const registerDevPublic = (pattern: string, prefix: string) => {
87
- ctx.server.get(pattern, async (koa: WebContext, next: () => Promise<void>) => {
88
- if (!getVite()) return next()
89
- await servePublicFile(koa, prefix)
90
- })
91
- }
92
-
93
- registerDevPublic('/img(.*)', 'img')
85
+ // web 应用里仍有 /img 这类运行时绝对路径,生产/开发模式都要由 Koishi 服务托管
86
+ const registerPublic = (pattern: string, prefix: string) => {
87
+ ctx.server.get(pattern, async (koa: WebContext) => {
88
+ await servePublicFile(koa, prefix)
89
+ })
90
+ }
91
+
92
+ registerPublic('/img(.*)', 'img')
94
93
 
95
94
  const toExtension = (value: string): string => {
96
95
  const ext = value.startsWith('.') ? value : `.${value}`