koishi-plugin-chat-patch 5.3.1 → 5.5.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.
- package/client/web/dist/assets/Chat-B5Jdrz_2.js +28 -0
- package/client/web/dist/assets/{Chat-C-rmzMIw.css → Chat-DTEsebnY.css} +1 -1
- package/client/web/dist/assets/{MsgBody-h4C1nnBQ.js → MsgBody-tPnKSZOs.js} +1 -1
- package/client/web/dist/assets/{MsgBody.vue_vue_type_script_setup_true_lang-a902ps2r.js → MsgBody.vue_vue_type_script_setup_true_lang-ClDybRo_.js} +1 -1
- package/client/web/dist/assets/{append_darwin-CqBNU62J.css → append_darwin-Cql-dV6d.css} +1 -1
- package/client/web/dist/assets/{append_full_vibrancy-j0D2LM9m.css → append_full_vibrancy-ih3loy1O.css} +1 -1
- package/client/web/dist/assets/{append_linux-uhxeBqeC.css → append_linux-B3LvR8KB.css} +1 -1
- package/client/web/dist/assets/{append_new-407Lo5_V.css → append_new-DfhN_NX9.css} +1 -1
- package/client/web/dist/assets/{append_vibrancy-6FHctkOY.css → append_vibrancy-D3pVsmAa.css} +1 -1
- package/client/web/dist/assets/{append_win32-srmD-78y.css → append_win32-Dh9JlmlE.css} +1 -1
- package/client/web/dist/assets/index-B71pPwae.css +1 -0
- package/client/web/dist/assets/{index-CdTmWcO4.js → index-CSGfOsfr.js} +97 -76
- package/client/web/dist/index.html +2 -2
- package/client/web/src/App.vue +2 -2
- package/client/web/src/assets/css/append/append_darwin.css +2 -2
- package/client/web/src/assets/css/append/append_full_vibrancy.css +2 -2
- package/client/web/src/assets/css/append/append_linux.css +2 -2
- package/client/web/src/assets/css/append/append_new.css +2 -2
- package/client/web/src/assets/css/append/append_vibrancy.css +1 -1
- package/client/web/src/assets/css/append/append_win32.css +2 -2
- package/client/web/src/assets/css/append/mobile/append_mobile_horizontal.css +1 -1
- package/client/web/src/assets/css/chat.css +4 -3
- package/client/web/src/assets/css/msg.css +7 -3
- package/client/web/src/assets/css/options.css +2 -2
- package/client/web/src/assets/css/view.css +14 -3
- package/client/web/src/assets/l10n/zh-CN.po +21 -0
- package/client/web/src/components/AboutPan.vue +5 -3
- package/client/web/src/components/DepPan.vue +1 -1
- package/client/web/src/components/MsgBody.vue +0 -3
- package/client/web/src/components/ThemeColorPickerPan.vue +1 -1
- package/client/web/src/components/UmamiInfoPan.vue +1 -1
- package/client/web/src/components/WelPan.vue +1 -1
- package/client/web/src/function/connect.ts +232 -72
- package/client/web/src/function/elements/information.ts +1 -0
- package/client/web/src/function/msg.ts +43 -24
- package/client/web/src/function/satori-model.ts +47 -15
- package/client/web/src/function/satori.ts +2 -1
- package/client/web/src/function/utils/appUtil.ts +3 -11
- package/client/web/src/function/utils/localHistoryUtil.ts +36 -12
- package/client/web/src/function/utils/msgUtil.ts +32 -21
- package/client/web/src/function/utils/sessionUtil.ts +20 -18
- package/client/web/src/pages/Chat.vue +47 -43
- package/client/web/src/pages/Friends.vue +5 -7
- package/client/web/src/pages/Info.vue +2 -2
- package/client/web/src/pages/Messages.vue +37 -23
- package/client/web/src/pages/chat-view/ChatTerminal.vue +4 -1
- package/client/web/src/pages/options/OptDev.vue +45 -2
- package/lib/database.d.ts +58 -0
- package/lib/index.js +220 -68
- package/lib/media.d.ts +24 -0
- package/lib/recorder.d.ts +21 -0
- package/lib/self-message.d.ts +27 -0
- package/lib/types.d.ts +6 -0
- package/lib/web.d.ts +7 -0
- package/package.json +1 -1
- package/src/cache.ts +1 -5
- package/src/database.ts +52 -8
- package/src/index.ts +4 -4
- package/src/media.ts +97 -0
- package/src/recorder.ts +28 -19
- package/src/self-message.ts +68 -19
- package/src/types.ts +6 -0
- package/src/web.ts +49 -39
- package/client/web/dist/assets/Chat-Bo5JdcNg.js +0 -28
- package/client/web/dist/assets/index-wjp_A4n1.css +0 -1
|
@@ -21,9 +21,7 @@ function getString(value: unknown): string {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function normalizeGroupId(value: string): string {
|
|
24
|
-
|
|
25
|
-
const wrapped = raw.match(/^\[_?([\s\S]+?)_?\]$/)
|
|
26
|
-
return wrapped ? wrapped[1] : raw || value
|
|
24
|
+
return String(value)
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
function isDirectChannelType(channel: SatoriObject): boolean {
|
|
@@ -231,16 +229,23 @@ function normalizeAtText(name: string, id: string): string {
|
|
|
231
229
|
}
|
|
232
230
|
|
|
233
231
|
function containsElementMarkup(source: string): boolean {
|
|
234
|
-
return
|
|
232
|
+
return /(?:<|&(?:amp;)?lt;)(?:img|image|audio|video|file|mface|face|faceType|quote|at|forward|json|xml|markdown|keyboard|p|br|i18n|a|text|sharp)(?:\s|\/|>|&(?:amp;)?gt;)/i.test(source)
|
|
235
233
|
}
|
|
236
234
|
|
|
237
235
|
// QQ 官方机器人会把部分大表情/图片消息的私有标记塞进 content,渲染前需要去掉
|
|
238
236
|
function cleanQqFaceMarkup(source: string): string {
|
|
239
237
|
return source
|
|
240
238
|
.replace(/<faceType\b[^>]*>/gi, '')
|
|
241
|
-
.replace(/<faceType\b[^>]*>/gi, '')
|
|
239
|
+
.replace(/&(?:amp;)?lt;faceType\b[^>]*&(?:amp;)?gt;/gi, '')
|
|
242
240
|
}
|
|
243
241
|
|
|
242
|
+
const QQ_EXPRESSION_PLACEHOLDER = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(
|
|
243
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 120 120">'
|
|
244
|
+
+ '<rect width="120" height="120" rx="12" fill="#f3f4f6"/>'
|
|
245
|
+
+ '<text x="60" y="62" text-anchor="middle" dominant-baseline="middle" font-size="18" fill="#333">表情消息</text>'
|
|
246
|
+
+ '</svg>',
|
|
247
|
+
)
|
|
248
|
+
|
|
244
249
|
function splitMarkupTextSegments(
|
|
245
250
|
segments: Array<Record<string, unknown>>,
|
|
246
251
|
): Array<Record<string, unknown>> {
|
|
@@ -257,7 +262,18 @@ function splitMarkupTextSegments(
|
|
|
257
262
|
}
|
|
258
263
|
|
|
259
264
|
export function parseSatoriMarkup(source: string): Array<Record<string, unknown>> {
|
|
265
|
+
const hadQqFaceMarkup = /(?:&(?:amp;)?lt;|<)faceType\b/i.test(source)
|
|
260
266
|
source = cleanQqFaceMarkup(source)
|
|
267
|
+
if (hadQqFaceMarkup && !source.trim()) {
|
|
268
|
+
return [{
|
|
269
|
+
type: 'image',
|
|
270
|
+
file: QQ_EXPRESSION_PLACEHOLDER,
|
|
271
|
+
url: QQ_EXPRESSION_PLACEHOLDER,
|
|
272
|
+
summary: '表情消息',
|
|
273
|
+
width: 120,
|
|
274
|
+
height: 120,
|
|
275
|
+
}]
|
|
276
|
+
}
|
|
261
277
|
const result: Array<Record<string, unknown>> = []
|
|
262
278
|
let index = 0
|
|
263
279
|
while (index < source.length) {
|
|
@@ -449,11 +465,11 @@ export function satoriEventToOneBot(
|
|
|
449
465
|
if (type === 'message' || type === 'message-created' || type === 'send') {
|
|
450
466
|
const isGroup = isGroupChannel(channel, guild)
|
|
451
467
|
const userId = getString(user.id)
|
|
452
|
-
|| (isGroup ? '' : getString(channel.id)
|
|
468
|
+
|| (isGroup ? '' : getString(channel.id))
|
|
453
469
|
const groupId = isGroup
|
|
454
470
|
? normalizeGroupId(getString(guild.id)) || normalizeGroupId(getString(channel.id))
|
|
455
471
|
: ''
|
|
456
|
-
const directChannelId = isGroup ? groupId : userId
|
|
472
|
+
const directChannelId = isGroup ? groupId : getString(channel.id) || userId
|
|
457
473
|
const nickname = getString(user.name) || getString(user.nick) || userId
|
|
458
474
|
const card = getString(member.nick) || getString(member.name) || ''
|
|
459
475
|
return {
|
|
@@ -470,7 +486,7 @@ export function satoriEventToOneBot(
|
|
|
470
486
|
group_avatar: isGroup
|
|
471
487
|
? (usableAvatar(guild.avatar) || usableAvatar(channel.avatar) || buildQqGroupAvatar(platform, groupId) || undefined)
|
|
472
488
|
: undefined,
|
|
473
|
-
channel_id: getString(channel.id) || (isGroup ? groupId :
|
|
489
|
+
channel_id: getString(channel.id) || (isGroup ? groupId : userId),
|
|
474
490
|
guild_id: getString(guild.id),
|
|
475
491
|
target_id: selfId,
|
|
476
492
|
message: messageSegments(message),
|
|
@@ -486,7 +502,7 @@ export function satoriEventToOneBot(
|
|
|
486
502
|
message_id: getString(message.id),
|
|
487
503
|
private_id: isGroup ? '' : directChannelId,
|
|
488
504
|
group_id: groupId,
|
|
489
|
-
channel_id: getString(channel.id) || (isGroup ? groupId :
|
|
505
|
+
channel_id: getString(channel.id) || (isGroup ? groupId : userId),
|
|
490
506
|
guild_id: getString(guild.id),
|
|
491
507
|
target_id: selfId,
|
|
492
508
|
sender: userId,
|
|
@@ -533,6 +549,24 @@ export function satoriEventToOneBot(
|
|
|
533
549
|
}
|
|
534
550
|
}
|
|
535
551
|
|
|
552
|
+
if (type === 'message-deleted') {
|
|
553
|
+
const isGroup = isGroupChannel(channel, guild)
|
|
554
|
+
const userId = getString(user.id) || (isGroup ? '' : getString(channel.id))
|
|
555
|
+
const groupId = isGroup
|
|
556
|
+
? normalizeGroupId(getString(guild.id)) || normalizeGroupId(getString(channel.id))
|
|
557
|
+
: ''
|
|
558
|
+
return {
|
|
559
|
+
...base,
|
|
560
|
+
post_type: 'notice',
|
|
561
|
+
notice_type: isGroup ? 'group_recall' : 'friend_recall',
|
|
562
|
+
group_id: groupId,
|
|
563
|
+
user_id: userId,
|
|
564
|
+
message_id: getString(message.id),
|
|
565
|
+
channel_id: getString(channel.id) || (isGroup ? groupId : userId),
|
|
566
|
+
guild_id: getString(guild.id),
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
536
570
|
return null
|
|
537
571
|
}
|
|
538
572
|
|
|
@@ -637,9 +671,7 @@ export function mapAction(action: string, params: Record<string, unknown>): {
|
|
|
637
671
|
const guildId = getString(params.group_id) || getString(params.guild_id)
|
|
638
672
|
const userId = getString(params.user_id)
|
|
639
673
|
const messageId = getString(params.message_id)
|
|
640
|
-
const directChannel = userId
|
|
641
|
-
? `private:${userId}`
|
|
642
|
-
: userId
|
|
674
|
+
const directChannel = userId
|
|
643
675
|
const channelId = getString(params.channel_id) || guildId || directChannel || userId
|
|
644
676
|
const content = Array.isArray(params.message)
|
|
645
677
|
? serializeSatoriContent(params.message)
|
|
@@ -696,8 +728,8 @@ function messageListFromResponse(data: unknown): unknown[] {
|
|
|
696
728
|
? normalizeGroupId(getString(guild.id)) || normalizeGroupId(getString(channel.id))
|
|
697
729
|
: ''
|
|
698
730
|
const userId = getString(user.id)
|
|
699
|
-
|| (isGroup ? '' : getString(channel.id)
|
|
700
|
-
const directChannelId = isGroup ? groupId : userId
|
|
731
|
+
|| (isGroup ? '' : getString(channel.id))
|
|
732
|
+
const directChannelId = isGroup ? groupId : getString(channel.id) || userId
|
|
701
733
|
return {
|
|
702
734
|
message_id: getString(message.id),
|
|
703
735
|
sn: getNumber(message.sn),
|
|
@@ -709,7 +741,7 @@ function messageListFromResponse(data: unknown): unknown[] {
|
|
|
709
741
|
channel_type: channel.type,
|
|
710
742
|
group_id: groupId,
|
|
711
743
|
user_id: groupId ? userId : directChannelId,
|
|
712
|
-
channel_id: getString(channel.id) || (isGroup ? groupId :
|
|
744
|
+
channel_id: getString(channel.id) || (isGroup ? groupId : userId),
|
|
713
745
|
guild_id: getString(guild.id),
|
|
714
746
|
sender: {
|
|
715
747
|
user_id: userId,
|
|
@@ -75,7 +75,8 @@ async function parseJsonResponse(response: Response): Promise<unknown> {
|
|
|
75
75
|
try {
|
|
76
76
|
return JSON.parse(text) as unknown
|
|
77
77
|
} catch {
|
|
78
|
-
|
|
78
|
+
// 部分平台对 delete 等操作只回显纯文本(例如 OK),HTTP 200 即视为成功
|
|
79
|
+
return text
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -53,11 +53,6 @@ function encodeKeyPart(value: unknown): string {
|
|
|
53
53
|
return encodeURIComponent(String(value ?? ''))
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
function ensureChannelPrefix(channelId: string, prefix: string): string {
|
|
57
|
-
if (/^(?:group|room|chat|channel|guild|private):/i.test(channelId)) return channelId
|
|
58
|
-
return `${prefix}:${channelId}`
|
|
59
|
-
}
|
|
60
|
-
|
|
61
56
|
/**
|
|
62
57
|
* 滚动到目标消息(不自动加载)
|
|
63
58
|
* @param seqName DOM 名(chat-xx)
|
|
@@ -121,7 +116,8 @@ export async function loadHistory(info: BaseChatInfoElem) {
|
|
|
121
116
|
uiStore.nowGetHistory = false
|
|
122
117
|
uiStore.historyBeforeTime = undefined
|
|
123
118
|
chatStore.messageList = []
|
|
124
|
-
const
|
|
119
|
+
const channelId = String(info.channel_id ?? info.id ?? '')
|
|
120
|
+
const cacheKey = [authStore.loginInfo.platform, authStore.loginInfo.uin, channelId].map(encodeKeyPart).join(':')
|
|
125
121
|
const cachedMessages = chatStore.sessionMessageCache.get(cacheKey)
|
|
126
122
|
if (cachedMessages?.length) {
|
|
127
123
|
chatStore.messageList = [...cachedMessages]
|
|
@@ -133,17 +129,13 @@ export async function loadHistory(info: BaseChatInfoElem) {
|
|
|
133
129
|
) {
|
|
134
130
|
const localMsgs = await dbGetLatest(
|
|
135
131
|
authStore.loginInfo.uin,
|
|
136
|
-
|
|
132
|
+
channelId,
|
|
137
133
|
20,
|
|
138
134
|
)
|
|
139
135
|
if (localMsgs.length > 0) {
|
|
140
136
|
chatStore.messageList = localMsgs
|
|
141
137
|
}
|
|
142
138
|
}
|
|
143
|
-
const rawChannelId = String(info.channel_id ?? '')
|
|
144
|
-
const channelId = info.type === 'group'
|
|
145
|
-
? ensureChannelPrefix(rawChannelId || String(info.id), 'group')
|
|
146
|
-
: ensureChannelPrefix(rawChannelId || String(info.id), 'private')
|
|
147
139
|
try {
|
|
148
140
|
const cachedMessages = await loadChatHistoryFromCache({
|
|
149
141
|
platform: String(authStore.loginInfo.platform ?? ''),
|
|
@@ -18,7 +18,7 @@ const logger = new Logger()
|
|
|
18
18
|
export interface LocalMsgRecord {
|
|
19
19
|
/** Bot 侧的消息 ID */
|
|
20
20
|
message_id: string
|
|
21
|
-
/** 会话 ID
|
|
21
|
+
/** 会话 ID:保存完整 channel_id,不剥离前缀 */
|
|
22
22
|
chat_id: number | string
|
|
23
23
|
/** 会话类型:"group" | "private" */
|
|
24
24
|
chat_type: string
|
|
@@ -115,18 +115,28 @@ function computeRawMessage(msg: any): string | null {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
function firstDefinedId(value: any, keys: string[]): number | string | undefined {
|
|
119
|
+
for (const key of keys) {
|
|
120
|
+
const current = value?.[key]
|
|
121
|
+
if (current != null && String(current) !== '') return current
|
|
122
|
+
}
|
|
123
|
+
return undefined
|
|
124
|
+
}
|
|
125
|
+
|
|
118
126
|
function deriveChatId(selfId: string | number, msgs: any[]): number | string | undefined {
|
|
119
127
|
const firstMsg = msgs[0]
|
|
120
|
-
|
|
128
|
+
const firstInfo = firstMsg?.infoList ?? firstMsg
|
|
129
|
+
const chatId = firstDefinedId(firstInfo, ['channel_id', 'channelId', 'private_id', 'group_id', 'user_id'])
|
|
130
|
+
?? firstDefinedId(firstMsg, ['channel_id', 'channelId', 'private_id', 'group_id', 'user_id'])
|
|
121
131
|
if (chatId != null) return chatId
|
|
122
132
|
|
|
123
133
|
for (const item of msgs) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
134
|
+
const info = item?.infoList ?? item
|
|
135
|
+
if (info?.sender != null && String(info.sender) !== String(selfId)) {
|
|
136
|
+
return info.sender
|
|
127
137
|
}
|
|
128
138
|
}
|
|
129
|
-
return
|
|
139
|
+
return undefined
|
|
130
140
|
}
|
|
131
141
|
|
|
132
142
|
export function ensureChatIdOnMsgs(selfId: string | number, msgs: any[]): any[] {
|
|
@@ -135,14 +145,21 @@ export function ensureChatIdOnMsgs(selfId: string | number, msgs: any[]): any[]
|
|
|
135
145
|
|
|
136
146
|
return msgs.map((item: any) => {
|
|
137
147
|
if (!item?.infoList) return item
|
|
138
|
-
if (
|
|
148
|
+
if (
|
|
149
|
+
item.infoList.channel_id != null
|
|
150
|
+
|| item.infoList.channelId != null
|
|
151
|
+
|| item.infoList.private_id != null
|
|
152
|
+
|| item.infoList.group_id != null
|
|
153
|
+
|| item.infoList.user_id != null
|
|
154
|
+
) {
|
|
139
155
|
return item
|
|
140
156
|
}
|
|
141
157
|
return {
|
|
142
158
|
...item,
|
|
143
159
|
infoList: {
|
|
144
160
|
...item.infoList,
|
|
145
|
-
|
|
161
|
+
channel_id: chatId,
|
|
162
|
+
channelId: chatId,
|
|
146
163
|
},
|
|
147
164
|
}
|
|
148
165
|
})
|
|
@@ -158,12 +175,14 @@ export function msgToRecord(msg: any): LocalMsgRecord | null {
|
|
|
158
175
|
const messageId = msg.message_id
|
|
159
176
|
if (!messageId) return null
|
|
160
177
|
|
|
161
|
-
const
|
|
178
|
+
const info = msg.infoList ?? msg
|
|
179
|
+
const chatId = firstDefinedId(info, ['channel_id', 'channelId', 'private_id', 'group_id', 'user_id'])
|
|
180
|
+
?? firstDefinedId(msg, ['channel_id', 'channelId', 'private_id', 'group_id', 'user_id'])
|
|
162
181
|
if (chatId == null) return null
|
|
163
182
|
|
|
164
183
|
const chatType: string =
|
|
165
184
|
msg.message_type ?? (msg.group_id != null ? 'group' : 'private')
|
|
166
|
-
const senderId: number | string = msg.
|
|
185
|
+
const senderId: number | string = info.sender ?? msg.sender?.user_id
|
|
167
186
|
if (senderId == null) return null
|
|
168
187
|
|
|
169
188
|
const senderName: string | null =
|
|
@@ -490,7 +509,8 @@ function deserializeRecord(record: LocalMsgRecord): any {
|
|
|
490
509
|
message_id: record.message_id,
|
|
491
510
|
private_id: isGroup ? undefined : record.chat_id,
|
|
492
511
|
group_id: isGroup ? record.chat_id : undefined,
|
|
493
|
-
|
|
512
|
+
channel_id: record.chat_id,
|
|
513
|
+
channelId: record.chat_id,
|
|
494
514
|
sender: record.sender_id,
|
|
495
515
|
}
|
|
496
516
|
|
|
@@ -499,7 +519,9 @@ function deserializeRecord(record: LocalMsgRecord): any {
|
|
|
499
519
|
message_id: record.message_id,
|
|
500
520
|
message_type: record.chat_type,
|
|
501
521
|
// 根据 chat_type 恢复对应的 id 字段
|
|
502
|
-
...(isGroup ? { group_id: record.chat_id } : { user_id: record.
|
|
522
|
+
...(isGroup ? { group_id: record.chat_id } : { user_id: record.sender_id }),
|
|
523
|
+
channel_id: record.chat_id,
|
|
524
|
+
channelId: record.chat_id,
|
|
503
525
|
sender,
|
|
504
526
|
time: record.time,
|
|
505
527
|
timestamp_ms: timeMs || undefined,
|
|
@@ -509,6 +531,8 @@ function deserializeRecord(record: LocalMsgRecord): any {
|
|
|
509
531
|
infoList,
|
|
510
532
|
raw_message: record.raw_message ?? '',
|
|
511
533
|
revoked: record.revoked,
|
|
534
|
+
revoke: record.revoked,
|
|
535
|
+
revokeTime: record.revoked ? Date.now() / 1000 : undefined,
|
|
512
536
|
// 消息序列号(并非所有 Bot 都提供,可为 null)
|
|
513
537
|
...(record.seq != null ? { message_seq: record.seq, seq_id: record.seq } : {}),
|
|
514
538
|
// 标记来源为本地缓存,业务层可按需用此字段区分
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
getSessionId,
|
|
28
28
|
mergeSessionState,
|
|
29
29
|
normalizeSessionId,
|
|
30
|
+
upsertSessionContact,
|
|
30
31
|
} from './sessionUtil'
|
|
31
32
|
|
|
32
33
|
const logger = new Logger()
|
|
@@ -681,21 +682,31 @@ export async function sendMsgRaw(
|
|
|
681
682
|
chatStore.messageList = chatStore.messageList.concat([showMsg])
|
|
682
683
|
|
|
683
684
|
// 发送方不一定会上报自身消息事件,先用预发送消息同步会话预览。
|
|
684
|
-
const sessionId = normalizeSessionId(
|
|
685
|
+
const sessionId = normalizeSessionId(
|
|
686
|
+
targetChannelId
|
|
687
|
+
|| chatStore.chatInfo.show.channel_id
|
|
688
|
+
|| String(id).split('/')[0],
|
|
689
|
+
)
|
|
685
690
|
const session = contactStore.baseOnMsgList.get(sessionId) ??
|
|
686
691
|
findSessionContact(contactStore.userList, sessionId)
|
|
687
692
|
if (session) {
|
|
688
693
|
const raw = getMsgRawTxt(showMsg)
|
|
689
694
|
const senderName = authStore.loginInfo.nickname
|
|
690
|
-
Object.assign(session, {
|
|
691
|
-
message_id: showMsg.message_id,
|
|
692
|
-
raw_msg: type === 'group' && senderName ? `${senderName}: ${raw}` : raw,
|
|
693
|
-
raw_msg_base: raw,
|
|
694
|
-
time: showMsg.time * 1000,
|
|
695
|
-
})
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
695
|
+
Object.assign(session, {
|
|
696
|
+
message_id: showMsg.message_id,
|
|
697
|
+
raw_msg: type === 'group' && senderName ? `${senderName}: ${raw}` : raw,
|
|
698
|
+
raw_msg_base: raw,
|
|
699
|
+
time: showMsg.time * 1000,
|
|
700
|
+
})
|
|
701
|
+
if (type === 'group') {
|
|
702
|
+
// 群发送预览同步到联系人列表,同时进入群会话列表
|
|
703
|
+
contactStore.userList = upsertSessionContact(contactStore.userList, session)
|
|
704
|
+
} else if (session._groupMember) {
|
|
705
|
+
session._groupMember = false
|
|
706
|
+
}
|
|
707
|
+
contactStore.baseOnMsgList.set(normalizeSessionId(sessionId), session)
|
|
708
|
+
updateBaseOnMsgList()
|
|
709
|
+
}
|
|
699
710
|
}
|
|
700
711
|
// 预发送保留 Base64 用于本地预览;真正发送前先写入后端临时目录
|
|
701
712
|
try {
|
|
@@ -762,7 +773,7 @@ export async function sendMsgRaw(
|
|
|
762
773
|
{
|
|
763
774
|
user_id: id.split('/')[0],
|
|
764
775
|
group_id: id.split('/')[1],
|
|
765
|
-
channel_id: String(targetChannelId ?? chatStore.chatInfo.show.channel_id ??
|
|
776
|
+
channel_id: String(targetChannelId ?? chatStore.chatInfo.show.channel_id ?? String(id).split('/')[0]),
|
|
766
777
|
message: msg,
|
|
767
778
|
},
|
|
768
779
|
echo + '_uuid_' + msgUUID,
|
|
@@ -773,7 +784,7 @@ export async function sendMsgRaw(
|
|
|
773
784
|
'send_msg',
|
|
774
785
|
{
|
|
775
786
|
user_id: id,
|
|
776
|
-
channel_id: String(targetChannelId ?? chatStore.chatInfo.show.channel_id ??
|
|
787
|
+
channel_id: String(targetChannelId ?? chatStore.chatInfo.show.channel_id ?? String(id)),
|
|
777
788
|
message: msg,
|
|
778
789
|
},
|
|
779
790
|
echo + '_uuid_' + msgUUID,
|
|
@@ -827,7 +838,7 @@ function normalizeBaseSessionMap() {
|
|
|
827
838
|
const seen = new Map<string, UserFriendElem & UserGroupElem>()
|
|
828
839
|
for (const [rawKey, item] of contactStore.baseOnMsgList) {
|
|
829
840
|
const id = getSessionDedupKey(item)
|
|
830
|
-
if (id
|
|
841
|
+
if (id === '0' || id === '') continue
|
|
831
842
|
const existing = seen.get(id)
|
|
832
843
|
if (existing && existing !== item) {
|
|
833
844
|
needNormalize = true
|
|
@@ -846,7 +857,7 @@ function normalizeBaseSessionMap() {
|
|
|
846
857
|
const canonical = new Map<string, UserFriendElem & UserGroupElem>()
|
|
847
858
|
contactStore.baseOnMsgList.forEach((item) => {
|
|
848
859
|
const id = getSessionDedupKey(item)
|
|
849
|
-
if (id === '
|
|
860
|
+
if (id === '0' || id === '') return
|
|
850
861
|
const existing = canonical.get(id)
|
|
851
862
|
if (existing && existing !== item) {
|
|
852
863
|
canonical.set(id, mergeSessionState(existing, item))
|
|
@@ -869,7 +880,7 @@ function getSessionList() {
|
|
|
869
880
|
|
|
870
881
|
const addSession = (item: UserFriendElem & UserGroupElem) => {
|
|
871
882
|
const id = getSessionDedupKey(item)
|
|
872
|
-
if (id === '
|
|
883
|
+
if (id === '0' || id === '') return
|
|
873
884
|
const existing = sessionMap.get(id)
|
|
874
885
|
if (existing && existing !== item) {
|
|
875
886
|
sessionMap.set(id, mergeSessionState(existing, item))
|
|
@@ -880,7 +891,7 @@ function getSessionList() {
|
|
|
880
891
|
|
|
881
892
|
if (settingsStore.sysConfig.session_display_mode === 'all') {
|
|
882
893
|
contactStore.userList.forEach((item) => {
|
|
883
|
-
addSession(item)
|
|
894
|
+
if (!item._groupMember) addSession(item)
|
|
884
895
|
})
|
|
885
896
|
}
|
|
886
897
|
|
|
@@ -894,11 +905,11 @@ function getSessionList() {
|
|
|
894
905
|
/**
|
|
895
906
|
* 刷新消息列表排序
|
|
896
907
|
*/
|
|
897
|
-
export function updateBaseOnMsgList() {
|
|
898
|
-
const contactStore = useContactStore()
|
|
899
|
-
const settingsStore = useSettingsStore()
|
|
900
|
-
normalizeBaseSessionMap()
|
|
901
|
-
const allList = getSessionList()
|
|
908
|
+
export function updateBaseOnMsgList() {
|
|
909
|
+
const contactStore = useContactStore()
|
|
910
|
+
const settingsStore = useSettingsStore()
|
|
911
|
+
normalizeBaseSessionMap()
|
|
912
|
+
const allList = getSessionList()
|
|
902
913
|
// 先更具 item.always_top 是不是 true 拆为两个数组
|
|
903
914
|
const topList = allList.filter((item) => item.always_top)
|
|
904
915
|
const normalList = allList.filter((item) => !item.always_top)
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { Session } from '../elements/information'
|
|
2
2
|
|
|
3
3
|
export function normalizeGroupId(value: string): string {
|
|
4
|
-
|
|
5
|
-
const wrapped = raw.match(/^\[_?([\s\S]+?)_?\]$/)
|
|
6
|
-
return wrapped ? wrapped[1] : raw || value
|
|
4
|
+
return String(value)
|
|
7
5
|
}
|
|
8
6
|
|
|
9
7
|
export function normalizeSessionId(value: number | string): string {
|
|
@@ -27,12 +25,6 @@ export function getSessionAliases(item: Session): string[] {
|
|
|
27
25
|
getSessionId(item),
|
|
28
26
|
getLegacySessionId(item),
|
|
29
27
|
]
|
|
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
28
|
const aliases = rawAliases.filter((value): value is number | string => {
|
|
37
29
|
const text = String(value ?? '')
|
|
38
30
|
return text !== '' && text !== '0'
|
|
@@ -44,11 +36,10 @@ export function getSessionDedupKey(item: Session): string {
|
|
|
44
36
|
const channelId = String(item.channel_id ?? item.channelId ?? '')
|
|
45
37
|
const groupId = String(item.group_id ?? '')
|
|
46
38
|
const userId = String(item.user_id ?? '')
|
|
47
|
-
if (
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
: `group:${normalizeGroupId(channelId)}`
|
|
39
|
+
if (channelId) return normalizeSessionId(channelId)
|
|
40
|
+
if (groupId) return normalizeSessionId(groupId)
|
|
41
|
+
if (userId) return normalizeSessionId(userId)
|
|
42
|
+
return ''
|
|
52
43
|
}
|
|
53
44
|
|
|
54
45
|
export function setSessionContact(
|
|
@@ -60,6 +51,21 @@ export function setSessionContact(
|
|
|
60
51
|
}
|
|
61
52
|
}
|
|
62
53
|
|
|
54
|
+
export function upsertSessionContact(
|
|
55
|
+
contacts: Session[],
|
|
56
|
+
item: Session,
|
|
57
|
+
): Session[] {
|
|
58
|
+
const key = getSessionDedupKey(item)
|
|
59
|
+
if (!key) return contacts
|
|
60
|
+
const index = contacts.findIndex((current) => getSessionDedupKey(current) === key)
|
|
61
|
+
if (index >= 0) {
|
|
62
|
+
const next = [...contacts]
|
|
63
|
+
Object.assign(next[index], item)
|
|
64
|
+
return next
|
|
65
|
+
}
|
|
66
|
+
return [...contacts, item]
|
|
67
|
+
}
|
|
68
|
+
|
|
63
69
|
export function findSessionContact(
|
|
64
70
|
contacts: Session[],
|
|
65
71
|
sessionId: number | string,
|
|
@@ -67,10 +73,6 @@ export function findSessionContact(
|
|
|
67
73
|
const target = normalizeSessionId(sessionId)
|
|
68
74
|
return contacts.find((item) => {
|
|
69
75
|
return getSessionAliases(item).some((alias) => alias === target)
|
|
70
|
-
|| (
|
|
71
|
-
Boolean(item.group_id) &&
|
|
72
|
-
normalizeGroupId(String(item.group_id)) === normalizeGroupId(target)
|
|
73
|
-
)
|
|
74
76
|
})
|
|
75
77
|
}
|
|
76
78
|
|