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
@@ -11,8 +11,8 @@
11
11
  <link rel="stylesheet" href="./bcui/css/style.css">
12
12
  <link rel="stylesheet" href="./bcui/css/color-light.css">
13
13
  <link rel="stylesheet" href="./css/append-light.css">
14
- <script type="module" crossorigin src="./assets/index-Cz89G4we.js"></script>
15
- <link rel="stylesheet" crossorigin href="./assets/index-BgOX_tNa.css">
14
+ <script type="module" crossorigin src="./assets/index-CdTmWcO4.js"></script>
15
+ <link rel="stylesheet" crossorigin href="./assets/index-wjp_A4n1.css">
16
16
  </head>
17
17
 
18
18
  <body>
@@ -0,0 +1 @@
1
+ []
@@ -30,6 +30,7 @@
30
30
  "jsonpath": "^1.1.1",
31
31
  "markdown-it": "^14.1.1",
32
32
  "pinia": "^3.0.3",
33
+ "pinyin": "^4.0.0",
33
34
  "pofile": "^1.1.4",
34
35
  "querystring": "^0.2.1",
35
36
  "spacingjs": "^1.0.8",
@@ -2422,6 +2423,12 @@
2422
2423
  "underscore": "1.13.6"
2423
2424
  }
2424
2425
  },
2426
+ "node_modules/keypress": {
2427
+ "version": "0.1.0",
2428
+ "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz",
2429
+ "integrity": "sha512-x0yf9PL/nx9Nw9oLL8ZVErFAk85/lslwEP7Vz7s5SI1ODXZIgit3C5qyWjw4DxOuO/3Hb4866SQh28a1V1d+WA==",
2430
+ "license": "MIT"
2431
+ },
2425
2432
  "node_modules/klona": {
2426
2433
  "version": "2.0.6",
2427
2434
  "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
@@ -2645,6 +2652,48 @@
2645
2652
  }
2646
2653
  }
2647
2654
  },
2655
+ "node_modules/pinyin": {
2656
+ "version": "4.0.0",
2657
+ "resolved": "https://registry.npmjs.org/pinyin/-/pinyin-4.0.0.tgz",
2658
+ "integrity": "sha512-vHpV5K+vpp6XUUpZNGRDuHoN+1xcmieM3EWlH4QjSX2kkpG/gVOwpqwV9EOJ9x9c9UERFKeLml5XVSukE/PLgQ==",
2659
+ "license": "MIT",
2660
+ "dependencies": {
2661
+ "commander": "~1.1.1"
2662
+ },
2663
+ "bin": {
2664
+ "pinyin": "bin/pinyin"
2665
+ },
2666
+ "engines": {
2667
+ "install-node": "^18.0.0"
2668
+ },
2669
+ "peerDependencies": {
2670
+ "@node-rs/jieba": "^1.6.0",
2671
+ "nodejieba": "^3.4.4",
2672
+ "segmentit": "^2.0.3"
2673
+ },
2674
+ "peerDependenciesMeta": {
2675
+ "@node-rs/jieba": {
2676
+ "optional": true
2677
+ },
2678
+ "nodejieba": {
2679
+ "optional": true
2680
+ },
2681
+ "segmentit": {
2682
+ "optional": true
2683
+ }
2684
+ }
2685
+ },
2686
+ "node_modules/pinyin/node_modules/commander": {
2687
+ "version": "1.1.1",
2688
+ "resolved": "https://registry.npmjs.org/commander/-/commander-1.1.1.tgz",
2689
+ "integrity": "sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA==",
2690
+ "dependencies": {
2691
+ "keypress": "0.1.x"
2692
+ },
2693
+ "engines": {
2694
+ "node": ">= 0.6.x"
2695
+ }
2696
+ },
2648
2697
  "node_modules/pofile": {
2649
2698
  "version": "1.1.4",
2650
2699
  "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.4.tgz",
@@ -33,6 +33,7 @@
33
33
  "jsonpath": "^1.1.1",
34
34
  "markdown-it": "^14.1.1",
35
35
  "pinia": "^3.0.3",
36
+ "pinyin": "^4.0.0",
36
37
  "pofile": "^1.1.4",
37
38
  "querystring": "^0.2.1",
38
39
  "spacingjs": "^1.0.8",
@@ -0,0 +1 @@
1
+ []
@@ -22,7 +22,7 @@
22
22
  <span> {{ $t('赞助者') }} </span>
23
23
  <div class="contributors">
24
24
  <div v-for="info in sponsorList.slice(0, 3)" :key="info.user.name">
25
- <img lazy :src="info.user.avatar">
25
+ <img lazy data-avatar :src="info.user.avatar">
26
26
  <div>
27
27
  <span>{{ info.user.name }}</span>
28
28
  <span>{{ Intl.DateTimeFormat(trueLang, {
@@ -37,6 +37,7 @@
37
37
  <img v-for="info in sponsorList.slice(3)"
38
38
  :key="info.user.name"
39
39
  lazy
40
+ data-avatar
40
41
  :src="info.user.avatar">
41
42
  </div>
42
43
  </div>
@@ -35,7 +35,7 @@
35
35
  </div>
36
36
  <span v-show="needShow && !showAll">{{ $t('点击展开') }}</span>
37
37
  <div class="info">
38
- <img :src="data.avatar || '/img/icons/icon.svg'">
38
+ <img data-avatar :src="data.avatar || '/img/icons/icon.svg'">
39
39
  <a>{{
40
40
  chatStore.chatInfo.info.group_members.filter((item) => {
41
41
  return Number(item.user_id) === Number(data.sender)
@@ -10,12 +10,6 @@
10
10
  :icon="['fas', 'face-grin-wide']"
11
11
  :title="$t('表情')"
12
12
  class="emoji-face" />
13
- <img v-else-if="emoji.type === 'apng'"
14
- class="emoji-face"
15
- loading="lazy"
16
- :alt="emoji.description"
17
- :title="emoji.description"
18
- :src="emoji.value">
19
13
  <span v-else
20
14
  :title="emoji.description"
21
15
  class="emoji-face">
@@ -26,18 +26,6 @@
26
26
  </template>
27
27
  </div>
28
28
  </template>
29
- <div class="title">
30
- <span>{{ $t('小黄脸表情') }}</span>
31
- </div>
32
- <div class="face">
33
- <template v-for="num in Emoji.normalList" :key="num">
34
- <div>
35
- <EmojiFace
36
- :emoji="Emoji.get(num)"
37
- @click="addBaseFace(num)" />
38
- </div>
39
- </template>
40
- </div>
41
29
  <div class="title">
42
30
  <span>{{ $t('emoji 表情') }}</span>
43
31
  </div>
@@ -15,6 +15,7 @@
15
15
  <font-awesome-icon v-if="data.user_id == -10000" :icon="['fas', 'bell']" />
16
16
  <font-awesome-icon v-else-if="data.user_id == -10001" :icon="['fas', 'user-group']" />
17
17
  <img v-else loading="lazy" :title="getShowName(data.group_name || data.nickname, data.remark)"
18
+ data-avatar
18
19
  :src="data.avatar || '/img/icons/icon.svg'" @error="avatarError">
19
20
  <div>
20
21
  <div>
@@ -31,6 +31,7 @@
31
31
  <template v-if="type != 'body'">
32
32
  <img v-menu.prevent="event => $emit('showMenu', event, data)"
33
33
  v-user-tooltip="() => getUserById(data.sender.user_id)"
34
+ data-avatar
34
35
  name="avatar"
35
36
  :src="data.sender.avatar || '/img/icons/icon.svg'"
36
37
  :alt="data.sender.card ? data.sender.card : data.sender.nickname"
@@ -81,9 +82,9 @@
81
82
  <!-- 超级表情 -->
82
83
  <template v-else-if="isSuperFaceMsg()">
83
84
  <div class="msg-img face lottie-face alone">
84
- <LazyLottie
85
- :animation-link="Emoji.get(Number(data.message[0].id))!.superValue!"
86
- :title="Emoji.get(Number(data.message[0].id))!.description" />
85
+ <EmojiFace
86
+ :emoji="Emoji.get(Number(data.message[0].id))!"
87
+ class="msg-face" />
87
88
  </div>
88
89
  </template>
89
90
  <template v-else-if="!hasCard()">
@@ -436,7 +437,6 @@ const chatStore = useChatStore()
436
437
  const settingsStore = useSettingsStore()
437
438
  import Emoji from '../function/model/emoji'
438
439
  import EmojiFace from './EmojiFace.vue'
439
- import LazyLottie from './LazyLottie.vue'
440
440
  import { Img } from '../function/model/img'
441
441
  import { dbGetImage, hashUrl } from '../function/utils/localHistoryUtil'
442
442
  import JsonSegComp from './msg-component/JsonSegComp.vue'
@@ -0,0 +1,133 @@
1
+ <template>
2
+ <!-- 设置页专用选项卡,避免 BcTab 在多实例/HMR 下取错 tab body -->
3
+ <div class="tab-main">
4
+ <Card>
5
+ <ul v-if="tabs.length" class="tab-bar">
6
+ <span v-if="title">{{ title }}</span>
7
+ <li
8
+ v-for="(tab, index) in tabs"
9
+ :key="tab.name"
10
+ :class="{ select: activeIndex === index }"
11
+ @click="selectTab(index)">
12
+ <span>{{ tab.name }}</span>
13
+ <div
14
+ v-if="index === tabs.length - 1"
15
+ :style="getTabLineStyle(index)" />
16
+ </li>
17
+ </ul>
18
+ </Card>
19
+ <div ref="bodyRef" class="tab-body">
20
+ <slot />
21
+ </div>
22
+ </div>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ import { computed, nextTick, onMounted, ref, useSlots, watch } from 'vue'
27
+ import Card from 'vue3-bcui/packages/ss-card'
28
+
29
+ const props = defineProps<{
30
+ title?: string
31
+ }>()
32
+
33
+ const slots = useSlots()
34
+ const bodyRef = ref<HTMLElement>()
35
+ const activeIndex = ref(0)
36
+
37
+ const tabs = computed(() => {
38
+ const nodes = slots.default?.() ?? []
39
+ return nodes.filter((node) => {
40
+ const props = node.props as Record<string, unknown> | undefined
41
+ return Boolean(props?.name)
42
+ }).map((node) => {
43
+ const props = node.props as Record<string, unknown>
44
+ return {
45
+ name: String(props.name ?? ''),
46
+ }
47
+ })
48
+ })
49
+
50
+ function selectTab(index: number) {
51
+ activeIndex.value = index
52
+ nextTick(() => {
53
+ const children = bodyRef.value?.children
54
+ if (!children) return
55
+ const activeName = tabs.value[index]?.name
56
+ for (let i = 0; i < children.length; i++) {
57
+ const child = children[i] as HTMLElement
58
+ if (child.nodeName === 'DIV' && child.hasAttribute('name')) {
59
+ child.style.display = child.getAttribute('name') === activeName
60
+ ? 'block'
61
+ : 'none'
62
+ }
63
+ }
64
+ })
65
+ }
66
+
67
+ function getTabLineStyle(maxIndex: number) {
68
+ const offset = maxIndex - activeIndex.value
69
+ return `transform: translateX(calc(-${offset}00% - (var(--bc-tab-margin) * 2 + 10px) * ${offset}))`
70
+ }
71
+
72
+ onMounted(() => {
73
+ selectTab(0)
74
+ })
75
+
76
+ watch(tabs, () => {
77
+ nextTick(() => selectTab(activeIndex.value))
78
+ })
79
+ </script>
80
+
81
+ <style scoped>
82
+ .tab-main > div:first-child {
83
+ margin-bottom: 10px;
84
+ }
85
+ .tab-bar {
86
+ --bc-tab-margin: 20px;
87
+
88
+ margin: -17px -20px -17px -40px;
89
+ justify-content: center;
90
+ align-items: center;
91
+ position: relative;
92
+ display: flex;
93
+ }
94
+ .tab-bar > span {
95
+ font-weight: bold;
96
+ flex: 0 0 auto;
97
+ margin-right: 20px;
98
+ }
99
+ .tab-bar > li {
100
+ margin: 0 var(--bc-tab-margin);
101
+ justify-content: center;
102
+ flex-direction: column;
103
+ list-style-type: none;
104
+ align-items: center;
105
+ border-radius: 3px;
106
+ min-width: 30px;
107
+ cursor: pointer;
108
+ padding: 10px;
109
+ display: flex;
110
+ }
111
+ .tab-bar > li span,
112
+ .tab-bar > li svg {
113
+ color: var(--color-font);
114
+ transition: color 0.3s;
115
+ font-size: 0.9rem;
116
+ }
117
+
118
+ .tab-bar > li > div {
119
+ width: calc(100% + 10px);
120
+ margin-bottom: -10px;
121
+ transition: all .35s;
122
+ border-radius: 7px;
123
+ margin-top: 6px;
124
+ height: 3px;
125
+ }
126
+ .tab-bar > li:last-child > div {
127
+ background: var(--color-main);
128
+ }
129
+ .tab-bar > li.select span,
130
+ .tab-bar > li.select svg {
131
+ color: var(--color-main);
132
+ }
133
+ </style>
@@ -17,6 +17,7 @@
17
17
  <div />
18
18
  <img loading="lazy"
19
19
  :title="getShowName(session.group_name || session.nickname, session.remark)"
20
+ data-avatar
20
21
  :src="session.avatar || '/img/icons/icon.svg'">
21
22
  <div>
22
23
  <p>
@@ -4,7 +4,7 @@
4
4
  <span>{{ updated ? $t('更新记录') : $t('新版本') }}</span>
5
5
  <a>{{ version }}</a>
6
6
  <div class="title">
7
- <img :src="user?.avatar || ''">
7
+ <img data-avatar :src="user?.avatar || ''">
8
8
  <a :href="user?.url || ''">{{ user?.name || '' }}</a>
9
9
  <span>
10
10
  {{
@@ -37,7 +37,7 @@
37
37
  <div class="release-header">
38
38
  <a class="release-version">{{ release.version }} - {{ release.info.title.replace('Release', '') }}</a>
39
39
  <div class="release-meta">
40
- <img :src="release.user.avatar">
40
+ <img data-avatar :src="release.user.avatar">
41
41
  <a :href="release.user.url" target="_blank">{{ release.user.name }}</a>
42
42
  <span>
43
43
  {{
@@ -26,7 +26,7 @@
26
26
  <!-- 群成员 -->
27
27
  <template v-else>
28
28
  <div>
29
- <img :src="userInfo.avatar || '/img/icons/icon.svg'">
29
+ <img data-avatar :src="userInfo.avatar || '/img/icons/icon.svg'">
30
30
  <div>
31
31
  <span name="id">{{ userInfo.user_id }}</span>
32
32
  <div>
@@ -3,8 +3,10 @@
3
3
  import { reactive } from 'vue'
4
4
  import { dispatch, msgPreprocess } from './msg'
5
5
  import {
6
+ botKey,
6
7
  connect as connectSatori,
7
8
  getActiveBot,
9
+ getBlockedPlatforms,
8
10
  getBootstrap,
9
11
  getLogins,
10
12
  request,
@@ -25,7 +27,11 @@ import { useChatStore } from '../state/chat'
25
27
  import { useContactStore } from '../state/contact'
26
28
  import { useSettingsStore } from '../state/settings'
27
29
  import { getMsgRawTxt, hasAtMe, updateBaseOnMsgList } from './utils/msgUtil'
28
- import { normalizeSessionId, setSessionContact } from './utils/sessionUtil'
30
+ import {
31
+ getSessionDedupKey,
32
+ normalizeSessionId,
33
+ setSessionContact,
34
+ } from './utils/sessionUtil'
29
35
  import { buildQqGroupAvatar } from './utils/avatarUtil'
30
36
  import type { ConnectionHistoryItem, LoginCacheElem } from './elements/system'
31
37
  import type { UserFriendElem, UserGroupElem } from './elements/information'
@@ -38,6 +44,14 @@ function encodeKeyPart(value: string): string {
38
44
  return encodeURIComponent(value)
39
45
  }
40
46
 
47
+ function isBlockedPlatform(platform: string): boolean {
48
+ return getBlockedPlatforms().some((item) => {
49
+ return item.exactMatch
50
+ ? platform === item.platformName
51
+ : platform.includes(item.platformName)
52
+ })
53
+ }
54
+
41
55
  function getChannelKind(value: unknown): 'group' | 'direct' | 'unknown' {
42
56
  if (typeof value === 'number') {
43
57
  if (value === 0) return 'group'
@@ -346,20 +360,20 @@ function updateBotStateGroup(
346
360
  apply: (target: Record<string, unknown>) => void,
347
361
  ) {
348
362
  const contactStore = useContactStore()
349
- const state = contactStore.botStates.get(selfId)
363
+ const state = contactStore.botStates.get(botKey(platform, selfId))
350
364
  const session = state?.onMsgList.find((item) => String(item.group_id) === sessionId)
351
365
  if (!state || !session) return
352
366
  apply(session as unknown as Record<string, unknown>)
353
- const key = normalizeSessionId(String(session.channel_id ?? session.channelId ?? sessionId))
367
+ const key = normalizeSessionId(String(session.channel_id || session.channelId || sessionId))
354
368
  const baseList = [...state.baseList]
355
- const baseIndex = baseList.findIndex(([baseKey]) => normalizeSessionId(String(baseKey)) === key)
369
+ const baseIndex = baseList.findIndex(([, value]) => value === session)
356
370
  const typedSession = session as unknown as UserFriendElem & UserGroupElem
357
371
  if (baseIndex >= 0) {
358
372
  baseList[baseIndex] = [key, typedSession]
359
373
  } else {
360
374
  baseList.push([key, typedSession])
361
375
  }
362
- contactStore.botStates.set(selfId, {
376
+ contactStore.botStates.set(botKey(platform, selfId), {
363
377
  userList: [...state.userList],
364
378
  baseList,
365
379
  onMsgList: [...state.onMsgList],
@@ -574,7 +588,7 @@ function recordBotMessage(platform: string, selfId: string, msg: Record<string,
574
588
  ? (eventGroupAvatar || cachedGroupInfo?.avatar || buildQqGroupAvatar(platform, sessionId) || undefined)
575
589
  : undefined
576
590
  const contactStore = useContactStore()
577
- const state = contactStore.botStates.get(selfId) ?? {
591
+ const state = contactStore.botStates.get(botKey(platform, selfId)) ?? {
578
592
  userList: [],
579
593
  baseList: [],
580
594
  onMsgList: [],
@@ -598,6 +612,14 @@ function recordBotMessage(platform: string, selfId: string, msg: Record<string,
598
612
  guild_id: typeof msg.guild_id === 'string' ? msg.guild_id : undefined,
599
613
  }
600
614
  const existing = state.onMsgList.find((item) => {
615
+ const itemChannelId = String(item.channel_id || item.channelId || '')
616
+ if (itemChannelId) {
617
+ if (isGroup) {
618
+ return normalizeGroupId(itemChannelId) === normalizeGroupId(channelId || sessionId)
619
+ }
620
+ return itemChannelId.replace(/^(?:private|direct):/i, '')
621
+ === String(sessionId).replace(/^(?:private|direct):/i, '')
622
+ }
601
623
  return isGroup
602
624
  ? String(item.group_id) === sessionId
603
625
  : String(item.user_id) === sessionId
@@ -631,14 +653,14 @@ function recordBotMessage(platform: string, selfId: string, msg: Record<string,
631
653
  }
632
654
  const baseKey = normalizeSessionId(sessionKey)
633
655
  const baseList = [...state.baseList]
634
- const baseIndex = baseList.findIndex(([key]) => normalizeSessionId(String(key)) === baseKey)
656
+ const baseIndex = baseList.findIndex(([, value]) => value === session)
635
657
  const typedSession = session as unknown as UserFriendElem & UserGroupElem
636
658
  if (baseIndex >= 0) {
637
659
  baseList[baseIndex] = [baseKey, typedSession]
638
660
  } else {
639
661
  baseList.push([baseKey, typedSession])
640
662
  }
641
- contactStore.botStates.set(selfId, {
663
+ contactStore.botStates.set(botKey(platform, selfId), {
642
664
  userList: [...state.userList],
643
665
  baseList,
644
666
  onMsgList: [...state.onMsgList],
@@ -714,6 +736,26 @@ function recordBotMessage(platform: string, selfId: string, msg: Record<string,
714
736
  )
715
737
  if (senderId && senderId !== '0') {
716
738
  void fetchUserIdentity(platform, selfId, senderId, null, msg)
739
+ if (isCurrentChat) {
740
+ const members = chatStore.chatInfo.info.group_members
741
+ const member = {
742
+ user_id: senderId,
743
+ nickname: String(sender.nickname || sender.card || senderName || senderId),
744
+ card: String(sender.card || ''),
745
+ role: String(sender.role || ''),
746
+ avatar: hasUsableAvatar(sender.avatar) ? getString(sender.avatar) : undefined,
747
+ title: '',
748
+ join_time: 0,
749
+ last_sent_time: Math.floor(sessionTime),
750
+ level: 0,
751
+ rank: '',
752
+ sex: '',
753
+ shutup_time: 0,
754
+ }
755
+ const index = members.findIndex((item) => String(item.user_id) === senderId)
756
+ if (index >= 0) Object.assign(members[index], member)
757
+ else members.push(member)
758
+ }
717
759
  }
718
760
  } else if (senderId && senderId !== '0') {
719
761
  identityDebug('recordBotMessage: private identity branch', {
@@ -768,28 +810,36 @@ export function restoreBotStateFromMessageCache(platform: string, selfId: string
768
810
  }
769
811
  if (!sessions.length) return
770
812
 
771
- const state = contactStore.botStates.get(selfId) ?? {
813
+ const state = contactStore.botStates.get(botKey(platform, selfId)) ?? {
772
814
  userList: [],
773
815
  baseList: [],
774
816
  onMsgList: [],
775
817
  }
776
- const baseList = [...state.baseList]
777
- const baseKeys = new Set(baseList.map(([key]) => normalizeSessionId(key)))
778
- const existing = new Set(state.onMsgList.map((item) => {
779
- return String(item.channel_id ?? item.channelId ?? item.user_id ?? item.group_id ?? '')
780
- }))
818
+ const baseMap = new Map<string, UserFriendElem & UserGroupElem>()
819
+ for (const [, value] of state.baseList) {
820
+ const id = getSessionDedupKey(value)
821
+ if (!id.endsWith(':0') && !id.endsWith(':')) baseMap.set(id, value)
822
+ }
823
+ const baseList = [...baseMap.entries()]
824
+ const baseKeys = new Set(baseList.map(([key]) => key))
825
+ const existing = new Set(
826
+ state.onMsgList.map((item) => {
827
+ const id = getSessionDedupKey(item)
828
+ return !id.endsWith(':0') && !id.endsWith(':') ? id : ''
829
+ }).filter(Boolean),
830
+ )
781
831
  for (const session of sessions) {
782
- const id = String(session.channel_id ?? session.channelId ?? session.user_id ?? session.group_id ?? '')
783
- if (id && !existing.has(id)) {
832
+ const id = getSessionDedupKey(session)
833
+ if (!id.endsWith(':0') && !id.endsWith(':') && !existing.has(id)) {
784
834
  state.onMsgList.unshift(session)
785
835
  existing.add(id)
786
836
  }
787
- if (id && !baseKeys.has(normalizeSessionId(id))) {
788
- baseList.push([normalizeSessionId(id), session])
789
- baseKeys.add(normalizeSessionId(id))
837
+ if (!id.endsWith(':0') && !id.endsWith(':') && !baseKeys.has(id)) {
838
+ baseList.push([normalizeSessionId(String(session.channel_id || session.channelId || session.user_id || session.group_id || '')), session])
839
+ baseKeys.add(id)
790
840
  }
791
841
  }
792
- contactStore.botStates.set(selfId, {
842
+ contactStore.botStates.set(botKey(platform, selfId), {
793
843
  userList: [...state.userList],
794
844
  baseList,
795
845
  onMsgList: [...state.onMsgList],
@@ -797,6 +847,7 @@ export function restoreBotStateFromMessageCache(platform: string, selfId: string
797
847
  }
798
848
 
799
849
  function onSatoriEvent(event: SatoriEvent) {
850
+ if (isBlockedPlatform(event.platform)) return
800
851
  const oneBot = satoriEventToOneBot(event.body, String(event.platform || ''))
801
852
  if (oneBot) {
802
853
  oneBot._rawSatori = {
@@ -845,6 +896,7 @@ export function flushPendingBotEvents(platform: string, selfId: string) {
845
896
  if (!events?.length) return
846
897
  pendingBotEvents.delete(key)
847
898
  for (const event of events) {
899
+ if (isBlockedPlatform(event.platform)) continue
848
900
  const oneBot = satoriEventToOneBot(event.body, String(event.platform || ''))
849
901
  if (oneBot) {
850
902
  oneBot._rawSatori = {
@@ -873,7 +925,8 @@ function onSatoriReady(logins: Array<{ platform: string; selfId: string; name: s
873
925
  login.creating = false
874
926
  login.status = true
875
927
  const authStore = useAuthStore()
876
- for (const item of logins) {
928
+ const visibleLogins = logins.filter((item) => !isBlockedPlatform(item.platform))
929
+ for (const item of visibleLogins) {
877
930
  if (!item.platform || !item.selfId) continue
878
931
  identityInfoCache.set(identityCacheKey('user', item.platform, item.selfId, item.selfId), {
879
932
  name: item.name || item.selfId,
@@ -887,7 +940,7 @@ function onSatoriReady(logins: Array<{ platform: string; selfId: string; name: s
887
940
  },
888
941
  })
889
942
  }
890
- const first = logins[0]
943
+ const first = visibleLogins[0]
891
944
  if (!first) return
892
945
 
893
946
  setActiveBot(first.platform, first.selfId)
@@ -895,16 +948,16 @@ function onSatoriReady(logins: Array<{ platform: string; selfId: string; name: s
895
948
  login.uin = first.selfId
896
949
  login.nickname = first.name
897
950
  login.platform = first.platform
898
- login.satoriLogins = logins
899
- login.selectedSatoriBot = first.selfId
951
+ login.satoriLogins = visibleLogins
952
+ login.selectedSatoriBot = botKey(first.platform, first.selfId)
900
953
  authStore.loginInfo = {
901
954
  uin: first.selfId,
902
955
  user_id: first.selfId,
903
956
  nickname: first.name,
904
957
  platform: first.platform,
905
958
  avatar: first.avatar,
906
- satoriLogins: logins,
907
- selectedSatoriBot: first.selfId,
959
+ satoriLogins: visibleLogins,
960
+ selectedSatoriBot: botKey(first.platform, first.selfId),
908
961
  }
909
962
  authStore.botInfo = {
910
963
  app_name: 'Satori',
@@ -1386,9 +1439,9 @@ async function loadAllBotsCache() {
1386
1439
  }
1387
1440
  const userList = [...groupRaws, ...friendRaws] as unknown as (UserFriendElem & UserGroupElem)[]
1388
1441
 
1389
- const state = contactStore.botStates.get(selfId)
1442
+ const state = contactStore.botStates.get(botKey(platform, selfId))
1390
1443
  if (!state || state.userList.length === 0) {
1391
- contactStore.botStates.set(selfId, {
1444
+ contactStore.botStates.set(botKey(platform, selfId), {
1392
1445
  userList,
1393
1446
  baseList: state?.baseList ?? [],
1394
1447
  onMsgList: state?.onMsgList ?? [],