koishi-plugin-chat-patch 5.0.3 → 5.3.0

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 (32) hide show
  1. package/client/web/dist/assets/Chat-B448YOeN.js +28 -0
  2. package/client/web/dist/assets/{MsgBody-LNGNGtE3.js → MsgBody-DDacjWJ9.js} +1 -1
  3. package/client/web/dist/assets/MsgBody.vue_vue_type_script_setup_true_lang-B1dWyCB0.js +67 -0
  4. package/client/web/dist/assets/{index-CwyyQN13.js → index-BlLut5xn.js} +121 -129
  5. package/client/web/dist/assets/index-fW_zcub3.css +1 -0
  6. package/client/web/dist/assets/pinyin-Dsq98249.js +157657 -0
  7. package/client/web/dist/index.html +2 -2
  8. package/client/web/dist/notice_local.json +1 -0
  9. package/client/web/package-lock.json +49 -0
  10. package/client/web/package.json +1 -0
  11. package/client/web/public/notice_local.json +1 -0
  12. package/client/web/src/components/EmojiFace.vue +0 -6
  13. package/client/web/src/components/FacePan.vue +0 -12
  14. package/client/web/src/components/MsgBody.vue +3 -4
  15. package/client/web/src/components/SettingsTab.vue +133 -0
  16. package/client/web/src/function/connect.ts +42 -21
  17. package/client/web/src/function/model/emoji.ts +7 -67
  18. package/client/web/src/function/msg.ts +74 -23
  19. package/client/web/src/function/satori-model.ts +4 -3
  20. package/client/web/src/function/utils/appUtil.ts +1 -4
  21. package/client/web/src/function/utils/msgUtil.ts +121 -87
  22. package/client/web/src/function/utils/pinyin.ts +25 -65
  23. package/client/web/src/function/utils/sessionUtil.ts +107 -9
  24. package/client/web/src/pages/Friends.vue +49 -13
  25. package/client/web/src/pages/Messages.vue +45 -14
  26. package/client/web/src/pages/Options.vue +4 -4
  27. package/client/web/src/pages/options/OptDev.vue +1 -14
  28. package/package.json +1 -1
  29. package/src/web.ts +712 -712
  30. package/client/web/dist/assets/Chat-CXjwWCiV.js +0 -28
  31. package/client/web/dist/assets/MsgBody.vue_vue_type_script_setup_true_lang-BK5okBfM.js +0 -67
  32. package/client/web/dist/assets/index-BgOX_tNa.css +0 -1
@@ -5,30 +5,25 @@ export type PinYinData = {
5
5
  short: string[]
6
6
  }
7
7
 
8
- /* eslint-disable no-console */
9
-
10
- const PINYIN_SCRIPT_SRC = 'https://lib.stapxs.cn/modules/pinyin.min.js'
8
+ type PinyinModule = typeof import('pinyin')
11
9
 
10
+ let pinyinModule: PinyinModule | null = null
12
11
  let pinyinLoadPromise: Promise<boolean> | null = null
13
12
 
14
13
  function createEmptyPinyinData(): PinYinData {
15
14
  return {
16
15
  main: [],
17
- short: []
16
+ short: [],
18
17
  }
19
18
  }
20
19
 
21
- function hasPinyinLib() {
22
- return typeof window !== 'undefined' && typeof window.pinyin !== 'undefined'
23
- }
24
-
25
20
  function scheduleIdleTask(task: () => void) {
26
21
  if (typeof window === 'undefined') return
27
22
 
28
23
  const idleWindow = window as Window & {
29
24
  requestIdleCallback?: (
30
25
  callback: IdleRequestCallback,
31
- options?: IdleRequestOptions
26
+ options?: IdleRequestOptions,
32
27
  ) => number
33
28
  }
34
29
 
@@ -41,57 +36,23 @@ function scheduleIdleTask(task: () => void) {
41
36
  }
42
37
 
43
38
  export function isPinyinReady() {
44
- return hasPinyinLib()
39
+ return pinyinModule !== null
45
40
  }
46
41
 
47
42
  export function ensurePinyinLoaded(): Promise<boolean> {
48
- if (hasPinyinLib()) return Promise.resolve(true)
49
- if (typeof window === 'undefined' || typeof document === 'undefined') {
50
- return Promise.resolve(false)
51
- }
52
- if (pinyinLoadPromise !== null) return pinyinLoadPromise
53
-
54
- pinyinLoadPromise = new Promise((resolve) => {
55
- let script = document.querySelector(
56
- 'script[data-ssqq-pinyin-lib="true"]',
57
- ) as HTMLScriptElement | null
58
-
59
- const finish = (success: boolean) => {
60
- if (!success) {
61
- pinyinLoadPromise = null
62
- }
63
- resolve(success)
64
- }
65
-
66
- const handleLoad = () => {
67
- if (script) {
68
- script.dataset.loaded = 'true'
69
- }
70
- finish(hasPinyinLib())
71
- }
72
-
73
- const handleError = () => {
74
- if (isDebugMode()) console.warn('拼音库加载失败')
75
- script?.remove()
76
- finish(false)
77
- }
78
-
79
- if (script?.dataset.loaded === 'true') {
80
- finish(hasPinyinLib())
81
- return
82
- }
83
-
84
- if (!script) {
85
- script = document.createElement('script')
86
- script.src = PINYIN_SCRIPT_SRC
87
- script.async = true
88
- script.dataset.ssqqPinyinLib = 'true'
89
- document.body.appendChild(script)
90
- }
91
-
92
- script.addEventListener('load', handleLoad, { once: true })
93
- script.addEventListener('error', handleError, { once: true })
94
- })
43
+ if (pinyinModule) return Promise.resolve(true)
44
+ if (pinyinLoadPromise) return pinyinLoadPromise
45
+
46
+ pinyinLoadPromise = import('pinyin')
47
+ .then((mod) => {
48
+ pinyinModule = mod
49
+ return true
50
+ })
51
+ .catch((error: unknown) => {
52
+ if (isDebugMode()) console.warn('本地拼音库加载失败:', error)
53
+ pinyinLoadPromise = null
54
+ return false
55
+ })
95
56
 
96
57
  return pinyinLoadPromise
97
58
  }
@@ -103,22 +64,21 @@ export function preloadPinyin() {
103
64
  }
104
65
 
105
66
  export function getPinyin(name: string): PinYinData {
106
- if (!hasPinyinLib()) return createEmptyPinyinData()
67
+ if (!pinyinModule) return createEmptyPinyinData()
107
68
 
108
69
  try {
109
- const pinyinLib = window.pinyin
110
- if (!pinyinLib) return createEmptyPinyinData()
70
+ const pinyinLib = pinyinModule.pinyin
111
71
  return {
112
- main: pinyinLib.pinyin(name, {
72
+ main: pinyinLib(name, {
113
73
  heteronym: true,
114
74
  compact: true,
115
75
  style: 'normal',
116
- }).map((item: string[]) => item.join('').toLowerCase()),
117
- short: pinyinLib.pinyin(name, {
76
+ }).map((item) => item.join('').toLowerCase()),
77
+ short: pinyinLib(name, {
118
78
  heteronym: true,
119
79
  compact: true,
120
80
  style: 'first_letter',
121
- }).map((item: string[]) => item.join('').toLowerCase()),
81
+ }).map((item) => item.join('').toLowerCase()),
122
82
  }
123
83
  } catch (error) {
124
84
  if (isDebugMode()) console.warn('拼音转换失败:', error)
@@ -128,7 +88,7 @@ export function getPinyin(name: string): PinYinData {
128
88
 
129
89
  export function matchPinyin(
130
90
  pinyinData: PinYinData,
131
- matchStr: string
91
+ matchStr: string,
132
92
  ): boolean {
133
93
  const str = matchStr.toLowerCase()
134
94
  for (const py of pinyinData.main) {
@@ -1,19 +1,76 @@
1
1
  import type { Session } from '../elements/information'
2
2
 
3
+ export function normalizeGroupId(value: string): string {
4
+ const raw = value.replace(/^(?:group|room|chat|channel|guild):/i, '').trim()
5
+ const wrapped = raw.match(/^\[_?([\s\S]+?)_?\]$/)
6
+ return wrapped ? wrapped[1] : raw || value
7
+ }
8
+
3
9
  export function normalizeSessionId(value: number | string): string {
4
10
  return String(value)
5
11
  }
6
12
 
7
13
  export function getSessionId(item: Session): number | string {
14
+ const channelId = item.channel_id ?? item.channelId
15
+ if (channelId !== undefined && channelId !== null && String(channelId) !== '') {
16
+ return channelId
17
+ }
8
18
  return item.user_id ?? item.group_id ?? 0
9
19
  }
10
20
 
21
+ function getLegacySessionId(item: Session): number | string | undefined {
22
+ return item.user_id ?? item.group_id
23
+ }
24
+
25
+ export function getSessionAliases(item: Session): string[] {
26
+ const rawAliases: Array<number | string | undefined> = [
27
+ getSessionId(item),
28
+ getLegacySessionId(item),
29
+ ]
30
+ if (item.group_id !== undefined && item.group_id !== null) {
31
+ rawAliases.push(normalizeGroupId(String(item.group_id)))
32
+ }
33
+ if (item.user_id !== undefined && item.user_id !== null) {
34
+ rawAliases.push(String(item.user_id).replace(/^(?:private|direct):/i, ''))
35
+ }
36
+ const aliases = rawAliases.filter((value): value is number | string => {
37
+ const text = String(value ?? '')
38
+ return text !== '' && text !== '0'
39
+ })
40
+ return [...new Set(aliases.map((value) => normalizeSessionId(value)))]
41
+ }
42
+
43
+ export function getSessionDedupKey(item: Session): string {
44
+ const channelId = String(item.channel_id ?? item.channelId ?? '')
45
+ const groupId = String(item.group_id ?? '')
46
+ const userId = String(item.user_id ?? '')
47
+ if (groupId) return `group:${normalizeGroupId(channelId || groupId)}`
48
+ if (userId) return `user:${userId.replace(/^(?:private|direct):/i, '')}`
49
+ return /^(?:private|direct):/i.test(channelId)
50
+ ? `user:${channelId.replace(/^(?:private|direct):/i, '')}`
51
+ : `group:${normalizeGroupId(channelId)}`
52
+ }
53
+
54
+ export function setSessionContact(
55
+ map: Map<number | string, Session>,
56
+ item: Session,
57
+ ) {
58
+ for (const alias of getSessionAliases(item)) {
59
+ map.set(alias, item)
60
+ }
61
+ }
62
+
11
63
  export function findSessionContact(
12
64
  contacts: Session[],
13
65
  sessionId: number | string,
14
66
  ) {
67
+ const target = normalizeSessionId(sessionId)
15
68
  return contacts.find((item) => {
16
- return normalizeSessionId(getSessionId(item)) === normalizeSessionId(sessionId)
69
+ return getSessionAliases(item).some((alias) => alias === target)
70
+ || (
71
+ Boolean(item.group_id) &&
72
+ normalizeGroupId(String(item.group_id)) === normalizeGroupId(target)
73
+ )
17
74
  })
18
75
  }
19
76
 
@@ -76,6 +133,28 @@ function copyDefinedSessionState<K extends SessionStateKey>(
76
133
  ) {
77
134
  const value = currentSession[key]
78
135
  if (value !== undefined) {
136
+ const current = contact[key]
137
+ if (
138
+ key === 'time' &&
139
+ typeof current === 'number' &&
140
+ typeof value === 'number' &&
141
+ current > value
142
+ ) {
143
+ return
144
+ }
145
+ contact[key] = value
146
+ }
147
+ }
148
+
149
+ function copyMissingIdentity<K extends keyof Session>(
150
+ contact: Session,
151
+ currentSession: Session,
152
+ key: K,
153
+ ) {
154
+ const value = currentSession[key]
155
+ if (value === undefined || value === null || String(value) === '') return
156
+ const current = contact[key]
157
+ if (current === undefined || current === null || String(current) === '') {
79
158
  contact[key] = value
80
159
  }
81
160
  }
@@ -84,6 +163,21 @@ export function mergeSessionState(
84
163
  contact: Session,
85
164
  currentSession: Session,
86
165
  ) {
166
+ const identityKeys = [
167
+ 'channel_id',
168
+ 'channelId',
169
+ 'guild_id',
170
+ 'guildId',
171
+ 'group_id',
172
+ 'user_id',
173
+ 'group_name',
174
+ 'nickname',
175
+ 'remark',
176
+ 'avatar',
177
+ ] as const
178
+ identityKeys.forEach((key) =>
179
+ copyMissingIdentity(contact, currentSession, key),
180
+ )
87
181
  SESSION_STATE_KEYS.forEach((key) =>
88
182
  copyDefinedSessionState(contact, currentSession, key),
89
183
  )
@@ -100,14 +194,18 @@ export function mergeEarlySessionContacts(
100
194
  ) {
101
195
  let didMerge = false
102
196
  contacts.forEach((contact) => {
103
- const sessionId = normalizeSessionId(getSessionId(contact))
104
- const currentSession = sessions.get(sessionId)
105
- if (currentSession && currentSession !== contact) {
106
- sessions.set(
107
- sessionId,
108
- mergeSessionState(contact, currentSession),
109
- )
110
- didMerge = true
197
+ for (const [key, currentSession] of sessions.entries()) {
198
+ if (currentSession === contact) continue
199
+ if (
200
+ findSessionContact([currentSession], getSessionId(contact))
201
+ || getSessionDedupKey(contact) === getSessionDedupKey(currentSession)
202
+ ) {
203
+ sessions.set(
204
+ key,
205
+ mergeSessionState(contact, currentSession),
206
+ )
207
+ didMerge = true
208
+ }
111
209
  }
112
210
  })
113
211
  return didMerge
@@ -50,7 +50,7 @@
50
50
  {{ $t('空') }}
51
51
  </div>
52
52
  <FriendBody v-for="item in contactStore.showList"
53
- :key="'search-' + item.user_id + '-' + item.group_id"
53
+ :key="'search-' + (item.channel_id || item.channelId || item.user_id || item.group_id)"
54
54
  :data="item"
55
55
  from="friend"
56
56
  @click="userClick(item, $event)" />
@@ -100,7 +100,7 @@
100
100
  return ( get.class_id == info.class_id )
101
101
  },
102
102
  )"
103
- :key=" 'fb-' + (item.user_id ? item.user_id : item.group_id) "
103
+ :key=" 'fb-' + (item.channel_id || item.channelId || item.user_id || item.group_id) "
104
104
  :data="item" from="friend"
105
105
  @click="userClick(item, $event)" />
106
106
  </div>
@@ -125,7 +125,7 @@
125
125
  return get.class_id == undefined
126
126
  },
127
127
  )"
128
- :key="'fb-' + (item.user_id ? item.user_id : item.group_id)"
128
+ :key="'fb-' + (item.channel_id || item.channelId || item.user_id || item.group_id)"
129
129
  :data="item"
130
130
  from="friend"
131
131
  @click="userClick(item, $event)" />
@@ -134,7 +134,7 @@
134
134
  </template>
135
135
  <template v-else>
136
136
  <FriendBody v-for="item in contactStore.userList"
137
- :key="'fb-' + (item.user_id ? item.user_id : item.group_id)"
137
+ :key="'fb-' + (item.channel_id || item.channelId || item.user_id || item.group_id)"
138
138
  :data="item"
139
139
  from="friend"
140
140
  @click="userClick(item, $event)" />
@@ -144,7 +144,7 @@
144
144
  <div v-else class="list">
145
145
  <div>
146
146
  <FriendBody v-for="item in contactStore.showList"
147
- :key="'fb-' + (item.user_id ? item.user_id : item.group_id)"
147
+ :key="'fb-' + (item.channel_id || item.channelId || item.user_id || item.group_id)"
148
148
  :data="item" from="friend"
149
149
  @click="userClick(item, $event)" />
150
150
  </div>
@@ -184,7 +184,7 @@
184
184
  import { reloadUsers } from '../function/utils/appUtil'
185
185
  import { Connector, flushPendingBotEvents, loadContactsFromCache, login as loginInfo } from '../function/connect'
186
186
  import { getActiveBot, getLogins, setActiveBot } from '../function/satori'
187
- import { normalizeSessionId } from '../function/utils/sessionUtil'
187
+ import { normalizeGroupId, normalizeSessionId } from '../function/utils/sessionUtil'
188
188
  import { avatarError } from '../function/utils/avatarUtil'
189
189
  import { backend } from '../runtime/backend'
190
190
  import { matchPinyin } from '../function/utils/pinyin'
@@ -240,7 +240,17 @@
240
240
  bot?: ContactWithBot['_bot'],
241
241
  ) => {
242
242
  for (const item of items) {
243
- const id = String(item.user_id ?? item.group_id ?? '')
243
+ const rawId = String(
244
+ item.channel_id
245
+ || item.channelId
246
+ || item.user_id
247
+ || item.group_id
248
+ || '',
249
+ )
250
+ const groupId = String(item.group_id || '')
251
+ const id = groupId
252
+ ? `group:${normalizeGroupId(rawId || groupId)}`
253
+ : `user:${rawId.replace(/^(?:private|direct):/i, '')}`
244
254
  const key = [bot?.platform ?? '', bot?.selfId ?? '', id]
245
255
  .map((value) => encodeURIComponent(String(value)))
246
256
  .join(':')
@@ -295,8 +305,25 @@
295
305
  if (saved) {
296
306
  contactStore.userList = saved.userList
297
307
  contactStore.baseOnMsgList.clear()
298
- for (const [key, value] of saved.baseList) {
299
- contactStore.baseOnMsgList.set(normalizeSessionId(key), value)
308
+ for (const [, value] of saved.baseList) {
309
+ const id = String(value.channel_id || value.channelId || value.user_id || value.group_id || '')
310
+ if (id && id !== '0') {
311
+ contactStore.baseOnMsgList.set(normalizeSessionId(id), value)
312
+ const legacyId = String(value.user_id || value.group_id || '')
313
+ if (legacyId && legacyId !== id) {
314
+ contactStore.baseOnMsgList.set(normalizeSessionId(legacyId), value)
315
+ }
316
+ }
317
+ }
318
+ for (const item of saved.onMsgList) {
319
+ const id = String(item.channel_id || item.channelId || item.user_id || item.group_id || '')
320
+ if (id && id !== '0') {
321
+ contactStore.baseOnMsgList.set(normalizeSessionId(id), item)
322
+ const legacyId = String(item.user_id || item.group_id || '')
323
+ if (legacyId && legacyId !== id) {
324
+ contactStore.baseOnMsgList.set(normalizeSessionId(legacyId), item)
325
+ }
326
+ }
300
327
  }
301
328
  contactStore.onMsgList = saved.onMsgList
302
329
  flushPendingBotEvents(bot.platform, bot.selfId)
@@ -380,18 +407,27 @@
380
407
  contactStore.showList = [] as any[]
381
408
 
382
409
  const back = {
383
- type: data.user_id ? 'user' : 'group',
384
- id: data.user_id ? data.user_id : data.group_id,
410
+ type: data.group_id ? 'group' : 'user',
411
+ id: data.group_id || data.user_id,
385
412
  name: getShowName(data),
386
413
  avatar: data.avatar || '/img/icons/icon.svg',
387
414
  jump: sender.dataset.jump,
388
- channel_id: data.channel_id,
415
+ channel_id: data.channel_id || data.channelId,
389
416
  guild_id: data.guild_id,
390
417
  } as BaseChatInfoElem
391
418
  if (back.id === undefined || back.id === null || String(back.id) === '' || String(back.id) === '0') return
392
419
  // 更新聊天框
393
420
  emit('userClick', back)
394
- contactStore.baseOnMsgList.set(normalizeSessionId(back.id), data)
421
+ const sessionId = String(
422
+ data.channel_id
423
+ || data.channelId
424
+ || data.user_id
425
+ || data.group_id
426
+ || '',
427
+ )
428
+ if (sessionId && sessionId !== '0') {
429
+ contactStore.baseOnMsgList.set(normalizeSessionId(sessionId), data)
430
+ }
395
431
  // 获取历史消息
396
432
  if(!uiStore.nowGetHistory) {
397
433
  emit('loadHistory', back)
@@ -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"
@@ -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'
@@ -283,16 +283,27 @@
283
283
  const restored = contactStore.botStates.get(bot.selfId)
284
284
  if (!restored) return
285
285
  contactStore.baseOnMsgList.clear()
286
- for (const [key, value] of restored.baseList) {
287
- contactStore.baseOnMsgList.set(normalizeSessionId(key), value)
286
+ for (const [, value] of restored.baseList) {
287
+ const id = String(value.channel_id || value.channelId || value.user_id || value.group_id || '')
288
+ if (id && id !== '0') {
289
+ contactStore.baseOnMsgList.set(normalizeSessionId(id), value)
290
+ const legacyId = String(value.user_id || value.group_id || '')
291
+ if (legacyId && legacyId !== id) {
292
+ contactStore.baseOnMsgList.set(normalizeSessionId(legacyId), value)
293
+ }
294
+ }
288
295
  }
289
296
  for (const item of restored.onMsgList) {
290
- const id = String(item.user_id ?? item.group_id ?? '')
297
+ const id = String(item.channel_id || item.channelId || item.user_id || item.group_id || '')
291
298
  if (!id || id === '0') continue
292
299
  contactStore.baseOnMsgList.set(
293
300
  normalizeSessionId(id),
294
301
  item as UserFriendElem & UserGroupElem,
295
302
  )
303
+ const legacyId = String(item.user_id || item.group_id || '')
304
+ if (legacyId && legacyId !== id) {
305
+ contactStore.baseOnMsgList.set(normalizeSessionId(legacyId), item as UserFriendElem & UserGroupElem)
306
+ }
296
307
  }
297
308
  contactStore.onMsgList = restored.onMsgList
298
309
  }
@@ -376,7 +387,7 @@
376
387
  * @param data 联系人对象
377
388
  */
378
389
  function userClick(data: UserFriendElem & UserGroupElem) {
379
- const id = data.user_id ? data.user_id : data.group_id
390
+ const id = data.group_id || data.user_id
380
391
  if (id === undefined || id === null || String(id) === '' || String(id) === '0') return
381
392
  if (!trRead.value && id != props.chat.show.id) {
382
393
  if (uiStore.openSideBar) {
@@ -385,11 +396,11 @@
385
396
  const back = {
386
397
  // 临时会话标志
387
398
  temp: data.group_name == '' ? data.group_id : undefined,
388
- type: data.user_id ? 'user' : 'group',
399
+ type: data.group_id ? 'group' : 'user',
389
400
  id: id,
390
401
  name: getShowName(data.group_name || data.nickname, data.remark),
391
402
  avatar: data.avatar || '/img/icons/icon.svg',
392
- channel_id: data.channel_id ?? data.channelId,
403
+ channel_id: data.channel_id || data.channelId,
393
404
  guild_id: data.guild_id ?? data.guildId,
394
405
  }
395
406
  if (props.chat.id != back.id) {
@@ -401,14 +412,18 @@
401
412
  }
402
413
  }
403
414
  // 清除新消息标记
404
- const item = contactStore.baseOnMsgList.get(normalizeSessionId(id))
415
+ const sessionKey = data.channel_id || data.channelId || id
416
+ const item = contactStore.baseOnMsgList.get(normalizeSessionId(sessionKey))
417
+ ?? contactStore.baseOnMsgList.get(normalizeSessionId(id))
418
+ ?? findSessionContact(Array.from(contactStore.baseOnMsgList.values()), id)
405
419
  if(item) {
406
420
  if(item.new_msg) {
407
421
  item.new_msg = false
408
422
  contactStore.newMsgCount--
409
423
  }
410
424
  item.highlight = undefined
411
- contactStore.baseOnMsgList.set(normalizeSessionId(id), item)
425
+ contactStore.baseOnMsgList.set(normalizeSessionId(sessionKey), item)
426
+ updateBaseOnMsgList()
412
427
  // 关闭所有通知
413
428
  new Notify().closeAll((item.group_id ?? item.user_id).toString())
414
429
  }
@@ -472,14 +487,18 @@
472
487
  */
473
488
  function readMsg(data: UserFriendElem & UserGroupElem) {
474
489
  const id = data.group_id ? data.group_id : data.user_id
475
- const item = contactStore.baseOnMsgList.get(normalizeSessionId(id))
490
+ const sessionKey = data.channel_id || data.channelId || id
491
+ const item = contactStore.baseOnMsgList.get(normalizeSessionId(sessionKey))
492
+ ?? contactStore.baseOnMsgList.get(normalizeSessionId(id))
493
+ ?? findSessionContact(Array.from(contactStore.baseOnMsgList.values()), id)
476
494
  if(item) {
477
495
  if(item.new_msg) {
478
496
  item.new_msg = false
479
497
  contactStore.newMsgCount--
480
498
  }
481
499
  item.highlight = undefined
482
- contactStore.baseOnMsgList.set(normalizeSessionId(id), item)
500
+ contactStore.baseOnMsgList.set(normalizeSessionId(sessionKey), item)
501
+ updateBaseOnMsgList()
483
502
  }
484
503
  // pop
485
504
  new PopInfo().add(
@@ -514,7 +533,16 @@
514
533
  )
515
534
  if (topList.indexOf(id) >= 0) {
516
535
  item.always_top = true
517
- contactStore.baseOnMsgList.set(normalizeSessionId(id), item)
536
+ const sessionId = String(
537
+ item.channel_id
538
+ ?? item.channelId
539
+ ?? item.user_id
540
+ ?? item.group_id
541
+ ?? '',
542
+ )
543
+ if (sessionId && sessionId !== '0') {
544
+ contactStore.baseOnMsgList.set(normalizeSessionId(sessionId), item)
545
+ }
518
546
  }
519
547
  })
520
548
  }
@@ -554,7 +582,10 @@
554
582
  break
555
583
  case 'remove': {
556
584
  const id = item.user_id ? item.user_id : item.group_id
585
+ const sessionId = String(item.channel_id || item.channelId || id || '')
557
586
  contactStore.baseOnMsgList.delete(normalizeSessionId(id))
587
+ contactStore.baseOnMsgList.delete(normalizeSessionId(sessionId))
588
+ updateBaseOnMsgList()
558
589
  refreshFavicon()
559
590
  break
560
591
  }
@@ -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'
@@ -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/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.0.3",
4
+ "version": "5.3.0",
5
5
  "scripts": {
6
6
  "build:web": "npm --prefix client/web run build",
7
7
  "prepack": "npm run build:web",