koishi-plugin-chat-patch 5.3.1 → 5.5.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.
- package/client/web/dist/assets/Chat-Clqxn7CB.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-C0TKsXhC.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-CCdBwlN_.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-BA37EQGa.js} +96 -75
- 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 +42 -23
- package/client/web/src/function/satori-model.ts +27 -13
- 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 -22
- 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
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
getSessionDedupKey,
|
|
32
32
|
normalizeSessionId,
|
|
33
33
|
setSessionContact,
|
|
34
|
+
upsertSessionContact,
|
|
34
35
|
} from './utils/sessionUtil'
|
|
35
36
|
import { buildQqGroupAvatar } from './utils/avatarUtil'
|
|
36
37
|
import type { ConnectionHistoryItem, LoginCacheElem } from './elements/system'
|
|
@@ -504,7 +505,7 @@ async function fetchUserIdentity(
|
|
|
504
505
|
const guildId = getString(msg.guild_id) || getString(msg.group_id)
|
|
505
506
|
const isPrivate = isPrivateMessage(msg)
|
|
506
507
|
const channelId = isPrivate
|
|
507
|
-
? (getString(msg.channel_id) ||
|
|
508
|
+
? (getString(msg.channel_id) || userId)
|
|
508
509
|
: ''
|
|
509
510
|
const sender = getObject(msg.sender)
|
|
510
511
|
const senderName = getString(sender.nickname) || getString(sender.card) || getString(sender.name)
|
|
@@ -555,10 +556,11 @@ function recordBotMessage(platform: string, selfId: string, msg: Record<string,
|
|
|
555
556
|
})
|
|
556
557
|
const isGroup = isGroupMessage(msg)
|
|
557
558
|
const channelId = typeof msg.channel_id === 'string' ? msg.channel_id : ''
|
|
559
|
+
const guildId = typeof msg.guild_id === 'string' ? msg.guild_id : ''
|
|
558
560
|
const sessionId = String(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
561
|
+
isGroup
|
|
562
|
+
? (msg.group_id || channelId || guildId || '')
|
|
563
|
+
: (msg.user_id || channelId || ''),
|
|
562
564
|
)
|
|
563
565
|
if (!sessionId || sessionId === '0') {
|
|
564
566
|
identityDebug('recordBotMessage: skipped invalid session', { sessionId, msg })
|
|
@@ -571,7 +573,8 @@ function recordBotMessage(platform: string, selfId: string, msg: Record<string,
|
|
|
571
573
|
const senderId = String(
|
|
572
574
|
sender.user_id
|
|
573
575
|
|| msg.user_id
|
|
574
|
-
||
|
|
576
|
+
|| channelId
|
|
577
|
+
|| '',
|
|
575
578
|
)
|
|
576
579
|
const raw = getMsgRawTxt(msg) || (typeof msg.raw_message === 'string' ? msg.raw_message : '')
|
|
577
580
|
const senderName = String(sender.card || sender.nickname || (isGroup ? senderId : ''))
|
|
@@ -611,60 +614,108 @@ function recordBotMessage(platform: string, selfId: string, msg: Record<string,
|
|
|
611
614
|
channel_id: typeof msg.channel_id === 'string' ? msg.channel_id : undefined,
|
|
612
615
|
guild_id: typeof msg.guild_id === 'string' ? msg.guild_id : undefined,
|
|
613
616
|
}
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
617
|
+
const typedSession = session as unknown as UserFriendElem & UserGroupElem
|
|
618
|
+
const memberContact = isGroup && senderId && senderId !== '0'
|
|
619
|
+
? {
|
|
620
|
+
user_id: senderId,
|
|
621
|
+
nickname: String(sender.nickname || sender.card || senderName || senderId),
|
|
622
|
+
remark: String(sender.nickname || sender.card || senderName || senderId),
|
|
623
|
+
avatar: hasUsableAvatar(sender.avatar) ? getString(sender.avatar) : undefined,
|
|
624
|
+
class_id: 0,
|
|
625
|
+
class_name: '我的好友',
|
|
626
|
+
_groupMember: true,
|
|
627
|
+
raw_msg: senderName ? `${senderName}: ${raw}` : raw,
|
|
628
|
+
raw_msg_base: raw,
|
|
629
|
+
time: sessionTime,
|
|
630
|
+
message_id: String(msg.message_id ?? ''),
|
|
631
|
+
guild_id: typeof msg.guild_id === 'string' ? msg.guild_id : undefined,
|
|
632
|
+
} as UserFriendElem & UserGroupElem
|
|
633
|
+
: undefined
|
|
634
|
+
let baseList = [...state.baseList]
|
|
635
|
+
if (isGroup) {
|
|
636
|
+
// 群消息进入群会话列表,群成员只进联系人列表
|
|
637
|
+
state.userList = upsertSessionContact(state.userList, typedSession)
|
|
638
|
+
if (memberContact) {
|
|
639
|
+
state.userList = upsertSessionContact(state.userList, memberContact)
|
|
640
|
+
}
|
|
641
|
+
const existing = state.onMsgList.find((item) => {
|
|
642
|
+
const itemChannelId = String(item.channel_id || item.channelId || '')
|
|
643
|
+
if (itemChannelId) {
|
|
618
644
|
return normalizeGroupId(itemChannelId) === normalizeGroupId(channelId || sessionId)
|
|
619
645
|
}
|
|
620
|
-
return
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
646
|
+
return String(item.group_id) === sessionId
|
|
647
|
+
})
|
|
648
|
+
if (existing) {
|
|
649
|
+
const previousAvatar = existing.avatar
|
|
650
|
+
const previousGroupName = existing.group_name
|
|
651
|
+
Object.assign(existing, session)
|
|
652
|
+
if (!hasUsableAvatar(session.avatar) && hasUsableAvatar(previousAvatar)) {
|
|
653
|
+
existing.avatar = previousAvatar
|
|
654
|
+
}
|
|
655
|
+
if (
|
|
656
|
+
!msg.group_name &&
|
|
657
|
+
previousGroupName &&
|
|
658
|
+
String(previousGroupName) !== String(sessionId)
|
|
659
|
+
) {
|
|
660
|
+
existing.group_name = previousGroupName
|
|
661
|
+
}
|
|
662
|
+
session = existing
|
|
663
|
+
} else {
|
|
664
|
+
state.onMsgList.unshift(session)
|
|
635
665
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
existing.group_name = previousGroupName
|
|
666
|
+
const baseKey = normalizeSessionId(sessionKey)
|
|
667
|
+
const baseIndex = baseList.findIndex(([, value]) => value === session)
|
|
668
|
+
if (baseIndex >= 0) {
|
|
669
|
+
baseList[baseIndex] = [baseKey, typedSession]
|
|
670
|
+
} else {
|
|
671
|
+
baseList.push([baseKey, typedSession])
|
|
643
672
|
}
|
|
644
|
-
|
|
645
|
-
|
|
673
|
+
} else {
|
|
674
|
+
const existing = state.onMsgList.find((item) => {
|
|
675
|
+
const itemChannelId = String(item.channel_id || item.channelId || '')
|
|
676
|
+
if (itemChannelId) {
|
|
677
|
+
return itemChannelId === String(sessionId)
|
|
678
|
+
}
|
|
679
|
+
return String(item.user_id) === sessionId
|
|
680
|
+
})
|
|
681
|
+
if (existing) {
|
|
682
|
+
const previousAvatar = existing.avatar
|
|
683
|
+
const previousNickname = existing.nickname
|
|
684
|
+
const previousRemark = existing.remark
|
|
685
|
+
Object.assign(existing, session)
|
|
686
|
+
if (existing._groupMember) existing._groupMember = false
|
|
687
|
+
if (!hasUsableAvatar(session.avatar) && hasUsableAvatar(previousAvatar)) {
|
|
688
|
+
existing.avatar = previousAvatar
|
|
689
|
+
}
|
|
690
|
+
if (!isGroup && !sender.nickname && previousNickname) {
|
|
691
|
+
existing.nickname = previousNickname
|
|
692
|
+
}
|
|
693
|
+
if (!isGroup && !existing.remark && previousRemark) {
|
|
694
|
+
existing.remark = previousRemark
|
|
695
|
+
}
|
|
696
|
+
session = existing
|
|
697
|
+
} else {
|
|
698
|
+
state.onMsgList.unshift(session)
|
|
646
699
|
}
|
|
647
|
-
|
|
648
|
-
|
|
700
|
+
const baseKey = normalizeSessionId(sessionKey)
|
|
701
|
+
const baseIndex = baseList.findIndex(([, value]) => value === session)
|
|
702
|
+
if (baseIndex >= 0) {
|
|
703
|
+
baseList[baseIndex] = [baseKey, typedSession]
|
|
704
|
+
} else {
|
|
705
|
+
baseList.push([baseKey, typedSession])
|
|
649
706
|
}
|
|
650
|
-
session = existing
|
|
651
|
-
} else {
|
|
652
|
-
state.onMsgList.unshift(session)
|
|
653
|
-
}
|
|
654
|
-
const baseKey = normalizeSessionId(sessionKey)
|
|
655
|
-
const baseList = [...state.baseList]
|
|
656
|
-
const baseIndex = baseList.findIndex(([, value]) => value === session)
|
|
657
|
-
const typedSession = session as unknown as UserFriendElem & UserGroupElem
|
|
658
|
-
if (baseIndex >= 0) {
|
|
659
|
-
baseList[baseIndex] = [baseKey, typedSession]
|
|
660
|
-
} else {
|
|
661
|
-
baseList.push([baseKey, typedSession])
|
|
662
707
|
}
|
|
663
708
|
contactStore.botStates.set(botKey(platform, selfId), {
|
|
664
709
|
userList: [...state.userList],
|
|
665
710
|
baseList,
|
|
666
711
|
onMsgList: [...state.onMsgList],
|
|
667
712
|
})
|
|
713
|
+
if (isGroup && isActiveBot({ platform, selfId })) {
|
|
714
|
+
contactStore.userList = upsertSessionContact(contactStore.userList, typedSession)
|
|
715
|
+
if (memberContact) {
|
|
716
|
+
contactStore.userList = upsertSessionContact(contactStore.userList, memberContact)
|
|
717
|
+
}
|
|
718
|
+
}
|
|
668
719
|
if (
|
|
669
720
|
isActiveBot({ platform, selfId }) &&
|
|
670
721
|
!contactStore.baseOnMsgList.has(normalizeSessionId(sessionKey))
|
|
@@ -674,7 +725,7 @@ function recordBotMessage(platform: string, selfId: string, msg: Record<string,
|
|
|
674
725
|
}
|
|
675
726
|
|
|
676
727
|
const chatStore = useChatStore()
|
|
677
|
-
const cacheKey = [platform, selfId,
|
|
728
|
+
const cacheKey = [platform, selfId, sessionKey].map(encodeKeyPart).join(':')
|
|
678
729
|
const messages = chatStore.sessionMessageCache.get(cacheKey) ?? []
|
|
679
730
|
const messageId = String(msg.message_id ?? '')
|
|
680
731
|
if (messageId && !messages.some((item) => String(item.message_id) === messageId)) {
|
|
@@ -778,7 +829,9 @@ export function restoreBotStateFromMessageCache(platform: string, selfId: string
|
|
|
778
829
|
const msg = messages[messages.length - 1]
|
|
779
830
|
if (!msg) continue
|
|
780
831
|
const isGroup = isGroupMessage(msg)
|
|
781
|
-
const id = String(isGroup
|
|
832
|
+
const id = String(isGroup
|
|
833
|
+
? (msg.group_id || msg.channel_id || msg.guild_id)
|
|
834
|
+
: (msg.user_id || msg.channel_id))
|
|
782
835
|
if (!id || id === '0') continue
|
|
783
836
|
const sender = getObject(msg.sender)
|
|
784
837
|
const senderId = getString(sender.user_id) || getString(msg.user_id) || ''
|
|
@@ -818,23 +871,23 @@ export function restoreBotStateFromMessageCache(platform: string, selfId: string
|
|
|
818
871
|
const baseMap = new Map<string, UserFriendElem & UserGroupElem>()
|
|
819
872
|
for (const [, value] of state.baseList) {
|
|
820
873
|
const id = getSessionDedupKey(value)
|
|
821
|
-
if (
|
|
874
|
+
if (id !== '0' && id !== '') baseMap.set(id, value)
|
|
822
875
|
}
|
|
823
876
|
const baseList = [...baseMap.entries()]
|
|
824
877
|
const baseKeys = new Set(baseList.map(([key]) => key))
|
|
825
878
|
const existing = new Set(
|
|
826
879
|
state.onMsgList.map((item) => {
|
|
827
880
|
const id = getSessionDedupKey(item)
|
|
828
|
-
return
|
|
881
|
+
return id !== '0' && id !== '' ? id : ''
|
|
829
882
|
}).filter(Boolean),
|
|
830
883
|
)
|
|
831
884
|
for (const session of sessions) {
|
|
832
885
|
const id = getSessionDedupKey(session)
|
|
833
|
-
if (
|
|
886
|
+
if (id !== '0' && id !== '' && !existing.has(id)) {
|
|
834
887
|
state.onMsgList.unshift(session)
|
|
835
888
|
existing.add(id)
|
|
836
889
|
}
|
|
837
|
-
if (
|
|
890
|
+
if (id !== '0' && id !== '' && !baseKeys.has(id)) {
|
|
838
891
|
baseList.push([normalizeSessionId(String(session.channel_id || session.channelId || session.user_id || session.group_id || '')), session])
|
|
839
892
|
baseKeys.add(id)
|
|
840
893
|
}
|
|
@@ -977,9 +1030,7 @@ function contactId(item: unknown): string {
|
|
|
977
1030
|
}
|
|
978
1031
|
|
|
979
1032
|
function normalizeGroupId(value: string): string {
|
|
980
|
-
|
|
981
|
-
const wrapped = raw.match(/^\[_?([\s\S]+?)_?\]$/)
|
|
982
|
-
return wrapped ? wrapped[1] : raw || value
|
|
1033
|
+
return String(value)
|
|
983
1034
|
}
|
|
984
1035
|
|
|
985
1036
|
function contactName(item: unknown): string {
|
|
@@ -1125,6 +1176,7 @@ export interface SendForwardResult {
|
|
|
1125
1176
|
ok: boolean
|
|
1126
1177
|
native: boolean
|
|
1127
1178
|
messageId?: string
|
|
1179
|
+
channelId?: string
|
|
1128
1180
|
}
|
|
1129
1181
|
|
|
1130
1182
|
function extractFirstMessageId(data: unknown): string | undefined {
|
|
@@ -1135,7 +1187,56 @@ function extractFirstMessageId(data: unknown): string | undefined {
|
|
|
1135
1187
|
? root.data as unknown[]
|
|
1136
1188
|
: []
|
|
1137
1189
|
const first = list.length > 0 ? getObject(list[0]) : root
|
|
1138
|
-
|
|
1190
|
+
const id = first.id ?? first.message_id
|
|
1191
|
+
return id == null ? undefined : String(id)
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
function extractDirectChannelId(data: unknown): string {
|
|
1195
|
+
const root = getObject(data)
|
|
1196
|
+
const nested = getObject(root.data)
|
|
1197
|
+
const channel = getObject(root.channel)
|
|
1198
|
+
const id = getString(channel.id)
|
|
1199
|
+
|| getString(nested.id)
|
|
1200
|
+
|| getString(root.id)
|
|
1201
|
+
if (!id) throw new Error('user.channel.create 未返回频道 ID')
|
|
1202
|
+
return id
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
function getPrivateSendUserId(action: string, params: Record<string, unknown>): string {
|
|
1206
|
+
const normalized = action.replace(/^_/, '')
|
|
1207
|
+
const userId = params.user_id == null ? '' : String(params.user_id)
|
|
1208
|
+
const groupId = params.group_id == null ? '' : String(params.group_id)
|
|
1209
|
+
const guildId = params.guild_id == null ? '' : String(params.guild_id)
|
|
1210
|
+
if (normalized === 'send_private_msg') return userId
|
|
1211
|
+
if (normalized === 'send_msg'
|
|
1212
|
+
&& userId
|
|
1213
|
+
&& !groupId
|
|
1214
|
+
&& !guildId) {
|
|
1215
|
+
return userId
|
|
1216
|
+
}
|
|
1217
|
+
return ''
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
async function sendPrivateSatoriMessage(
|
|
1221
|
+
userId: string,
|
|
1222
|
+
content: string,
|
|
1223
|
+
guildId: string | undefined,
|
|
1224
|
+
active: ReturnType<typeof getActiveBot>,
|
|
1225
|
+
channelIdHint?: string,
|
|
1226
|
+
): Promise<{ data: unknown; channelId: string }> {
|
|
1227
|
+
// 不构造前缀,优先透传前端已有的完整频道 ID
|
|
1228
|
+
let channelId = channelIdHint || userId
|
|
1229
|
+
try {
|
|
1230
|
+
const direct = await request('user.channel.create', { user_id: userId, guild_id: guildId }, active)
|
|
1231
|
+
channelId = extractDirectChannelId(direct)
|
|
1232
|
+
} catch {
|
|
1233
|
+
// 兼容未实现 createDirectChannel 的适配器,无完整频道 ID 时退回 private: 前缀私聊
|
|
1234
|
+
channelId = channelIdHint || (/^(?:private|direct):/i.test(userId) ? userId : `private:${userId}`)
|
|
1235
|
+
}
|
|
1236
|
+
return {
|
|
1237
|
+
data: await request('message.create', { channel_id: channelId, content }, active),
|
|
1238
|
+
channelId,
|
|
1239
|
+
}
|
|
1139
1240
|
}
|
|
1140
1241
|
|
|
1141
1242
|
export async function sendForwardMessage(
|
|
@@ -1144,29 +1245,33 @@ export async function sendForwardMessage(
|
|
|
1144
1245
|
type: 'group' | 'user',
|
|
1145
1246
|
id: string,
|
|
1146
1247
|
messages: unknown[],
|
|
1248
|
+
channelIdHint?: string,
|
|
1147
1249
|
): Promise<SendForwardResult> {
|
|
1148
1250
|
if (!platform || !selfId || !id || messages.length === 0) {
|
|
1149
1251
|
return { ok: false, native: false }
|
|
1150
1252
|
}
|
|
1151
1253
|
// 优先走 Satori 原生合并转发,避免依赖 onebot 专属的 send-forward API
|
|
1152
1254
|
if (platform) {
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
? id
|
|
1156
|
-
: platform === 'yunhu'
|
|
1157
|
-
? `group:${id}`
|
|
1158
|
-
: id
|
|
1159
|
-
: /^(?:private|direct):/i.test(id)
|
|
1160
|
-
? id
|
|
1161
|
-
: `private:${id}`
|
|
1255
|
+
// Satori 的 ID 是完整 string,调用方传什么就原样发什么
|
|
1256
|
+
const channelId = channelIdHint || id
|
|
1162
1257
|
const content = buildForwardMessage(messages)
|
|
1163
1258
|
if (content) {
|
|
1164
1259
|
try {
|
|
1165
|
-
|
|
1260
|
+
let finalChannelId = channelId
|
|
1261
|
+
if (type === 'user') {
|
|
1262
|
+
try {
|
|
1263
|
+
const direct = await request('user.channel.create', { user_id: id }, { platform, selfId })
|
|
1264
|
+
finalChannelId = extractDirectChannelId(direct)
|
|
1265
|
+
} catch {
|
|
1266
|
+
finalChannelId = channelIdHint || (/^(?:private|direct):/i.test(id) ? id : `private:${id}`)
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
const data = await request('message.create', { channel_id: finalChannelId, content }, { platform, selfId })
|
|
1166
1270
|
return {
|
|
1167
1271
|
ok: true,
|
|
1168
1272
|
native: true,
|
|
1169
1273
|
messageId: extractFirstMessageId(data),
|
|
1274
|
+
channelId: finalChannelId,
|
|
1170
1275
|
}
|
|
1171
1276
|
} catch {
|
|
1172
1277
|
// 原生路径失败时回退到旧 API,方便兼容尚未热重载的 Koishi 后端
|
|
@@ -1230,10 +1335,8 @@ function selfMessageToOneBot(record: unknown): Record<string, unknown> | null {
|
|
|
1230
1335
|
const sentAt = Number(obj.sentAt ?? Date.now())
|
|
1231
1336
|
const messageId = getString(obj.messageId)
|
|
1232
1337
|
const localId = getString(obj.id) || `self-${sentAt}`
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
? 'user'
|
|
1236
|
-
: 'group'
|
|
1338
|
+
// 只按记录里的显式 channelType 判断,不根据频道 ID 前缀推断
|
|
1339
|
+
const channelType = getString(obj.channelType) === 'user' ? 'user' : 'group'
|
|
1237
1340
|
if (!platform || !selfId || !channelId) return null
|
|
1238
1341
|
|
|
1239
1342
|
const login = getLogins().find((item) => {
|
|
@@ -1284,7 +1387,7 @@ function selfMessageToOneBot(record: unknown): Record<string, unknown> | null {
|
|
|
1284
1387
|
if (message.length === 0) return null
|
|
1285
1388
|
|
|
1286
1389
|
const groupId = channelType === 'group' ? normalizeGroupId(channelId) : ''
|
|
1287
|
-
const userId = channelType === 'user' ? channelId
|
|
1390
|
+
const userId = channelType === 'user' ? channelId : ''
|
|
1288
1391
|
const guildId = getString(obj.guildId)
|
|
1289
1392
|
const fallbackId = messageId || `self-${localId}`
|
|
1290
1393
|
const rawMessage = getString(obj.content) || getMsgRawTxt({ message })
|
|
@@ -1321,9 +1424,26 @@ function selfMessageToOneBot(record: unknown): Record<string, unknown> | null {
|
|
|
1321
1424
|
},
|
|
1322
1425
|
_from_self_cache: true,
|
|
1323
1426
|
}
|
|
1427
|
+
if (obj.revoked === true) {
|
|
1428
|
+
msg.revoke = true
|
|
1429
|
+
msg.revoked = true
|
|
1430
|
+
msg.revokeTime = Number(obj.revokedAt) || Date.now() / 1000
|
|
1431
|
+
}
|
|
1324
1432
|
return msg
|
|
1325
1433
|
}
|
|
1326
1434
|
|
|
1435
|
+
export async function clearAllCache(): Promise<boolean> {
|
|
1436
|
+
try {
|
|
1437
|
+
const info = await getBootstrap()
|
|
1438
|
+
const response = await fetch(`${location.origin}${info.basePath}/api/clear-all`, {
|
|
1439
|
+
method: 'POST',
|
|
1440
|
+
})
|
|
1441
|
+
return response.ok
|
|
1442
|
+
} catch {
|
|
1443
|
+
return false
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1327
1447
|
export async function loadChatHistoryFromCache(params: {
|
|
1328
1448
|
platform: string
|
|
1329
1449
|
selfId: string
|
|
@@ -1369,6 +1489,11 @@ export async function loadChatHistoryFromCache(params: {
|
|
|
1369
1489
|
target_id: getString(msg.target_id),
|
|
1370
1490
|
sender: userId,
|
|
1371
1491
|
}
|
|
1492
|
+
if (recordObj.revoked === true) {
|
|
1493
|
+
msg.revoke = true
|
|
1494
|
+
msg.revoked = true
|
|
1495
|
+
msg.revokeTime = Number(recordObj.revokedAt) || Date.now() / 1000
|
|
1496
|
+
}
|
|
1372
1497
|
messages.push(msg)
|
|
1373
1498
|
}
|
|
1374
1499
|
const existingIds = new Set(messages.map((item) => getString(item.message_id)))
|
|
@@ -1502,6 +1627,7 @@ function buildFriendItem(raw: unknown, data: unknown): Record<string, unknown> {
|
|
|
1502
1627
|
const item: Record<string, unknown> = { ...getObject(raw) }
|
|
1503
1628
|
if (!item.channel_id && item.channelId) item.channel_id = item.channelId
|
|
1504
1629
|
if (!item.guild_id && item.guildId) item.guild_id = item.guildId
|
|
1630
|
+
if (getString(item.guild_id) || getString(item.guildId)) item._groupMember = true
|
|
1505
1631
|
if (name) {
|
|
1506
1632
|
item.nickname = name
|
|
1507
1633
|
item.remark = name
|
|
@@ -2011,6 +2137,20 @@ export class Connector {
|
|
|
2011
2137
|
const mapped = mapAction(api, args)
|
|
2012
2138
|
if (!mapped) return undefined
|
|
2013
2139
|
const active = getActiveBot()
|
|
2140
|
+
const privateUserId = getPrivateSendUserId(api, args)
|
|
2141
|
+
if (privateUserId) {
|
|
2142
|
+
const result = await sendPrivateSatoriMessage(
|
|
2143
|
+
privateUserId,
|
|
2144
|
+
getString(mapped.params.content),
|
|
2145
|
+
getString(args.group_id) || getString(args.guild_id) || undefined,
|
|
2146
|
+
active,
|
|
2147
|
+
getString(mapped.params.channel_id) || getString(args.channel_id),
|
|
2148
|
+
)
|
|
2149
|
+
const response = satoriResponseToOneBot(api, result.data, active.platform)
|
|
2150
|
+
response.channel_id = result.channelId
|
|
2151
|
+
response.channelId = result.channelId
|
|
2152
|
+
return response.data
|
|
2153
|
+
}
|
|
2014
2154
|
const data = await request(mapped.method, mapped.params, active)
|
|
2015
2155
|
return satoriResponseToOneBot(api, data, active.platform).data
|
|
2016
2156
|
}
|
|
@@ -2022,6 +2162,26 @@ export class Connector {
|
|
|
2022
2162
|
return
|
|
2023
2163
|
}
|
|
2024
2164
|
const active = getActiveBot()
|
|
2165
|
+
const privateUserId = getPrivateSendUserId(action, value)
|
|
2166
|
+
if (privateUserId) {
|
|
2167
|
+
void sendPrivateSatoriMessage(
|
|
2168
|
+
privateUserId,
|
|
2169
|
+
getString(mapped.params.content),
|
|
2170
|
+
getString(value.group_id) || getString(value.guild_id) || undefined,
|
|
2171
|
+
active,
|
|
2172
|
+
getString(mapped.params.channel_id) || getString(value.channel_id),
|
|
2173
|
+
)
|
|
2174
|
+
.then((result) => {
|
|
2175
|
+
const response = satoriResponseToOneBot(action, result.data, active.platform)
|
|
2176
|
+
response.channel_id = result.channelId
|
|
2177
|
+
response.channelId = result.channelId
|
|
2178
|
+
dispatch(response, echo)
|
|
2179
|
+
})
|
|
2180
|
+
.catch((error: unknown) => {
|
|
2181
|
+
dispatch({ retcode: -1, data: null, error: String(error) }, echo)
|
|
2182
|
+
})
|
|
2183
|
+
return
|
|
2184
|
+
}
|
|
2025
2185
|
if (active && (action === 'get_friend_list' || action === 'get_group_list')) {
|
|
2026
2186
|
const type = action === 'get_friend_list' ? 'friend' : 'group'
|
|
2027
2187
|
void loadContactType(active, { type, action, echo })
|
|
@@ -95,6 +95,7 @@ import {
|
|
|
95
95
|
normalizeSessionId,
|
|
96
96
|
resolveIncomingSession,
|
|
97
97
|
setSessionContact,
|
|
98
|
+
upsertSessionContact,
|
|
98
99
|
} from './utils/sessionUtil'
|
|
99
100
|
|
|
100
101
|
const popInfo = new PopInfo()
|
|
@@ -120,11 +121,6 @@ function getString(value: unknown): string {
|
|
|
120
121
|
return typeof value === 'string' ? value : String(value)
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
function normalizeChannelForMatch(value: string): string {
|
|
124
|
-
if (/^(?:private|direct):/i.test(value)) return value.replace(/^(?:private|direct):/i, '')
|
|
125
|
-
return normalizeGroupId(value)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
124
|
// 其他 tag
|
|
129
125
|
let listLoadTimes = 0
|
|
130
126
|
const logger = new Logger()
|
|
@@ -995,10 +991,13 @@ const msgFunctions = {
|
|
|
995
991
|
const chatStore = useChatStore()
|
|
996
992
|
const rawData = msg.data
|
|
997
993
|
const firstData = Array.isArray(rawData) ? rawData[0] : rawData
|
|
994
|
+
const firstRawId = getDataObject(firstData).id
|
|
995
|
+
?? getDataObject(rawData).id
|
|
998
996
|
const realMessageId = getString(msg.message_id)
|
|
999
997
|
|| getString(getDataObject(rawData).message_id)
|
|
1000
998
|
|| getString(getDataObject(firstData).id)
|
|
1001
999
|
|| getString(getDataObject(firstData).message_id)
|
|
1000
|
+
|| (firstRawId == null ? '' : String(firstRawId))
|
|
1002
1001
|
if (realMessageId) msg.message_id = realMessageId
|
|
1003
1002
|
if (echoList[1] == 'forward') {
|
|
1004
1003
|
// PS:这儿写是写了转发成功,事实上不确定消息有没有真的发送出去(x
|
|
@@ -1341,8 +1340,16 @@ const msgFunctions = {
|
|
|
1341
1340
|
)
|
|
1342
1341
|
getMessageList(trueMsg).then((trueMsg) => {
|
|
1343
1342
|
if (trueMsg?.length == 1) {
|
|
1343
|
+
const nextMessage = Array.isArray(trueMsg[0].message)
|
|
1344
|
+
&& trueMsg[0].message.some((item: any) => {
|
|
1345
|
+
if (item?.type !== 'text') return true
|
|
1346
|
+
const text = String(item?.text ?? item?.content ?? '')
|
|
1347
|
+
return text.trim() !== ''
|
|
1348
|
+
})
|
|
1349
|
+
? trueMsg[0].message
|
|
1350
|
+
: fakeMsg.message
|
|
1344
1351
|
// 使用消息对象引用直接更新,避免索引问题
|
|
1345
|
-
fakeMsg.message =
|
|
1352
|
+
if (Array.isArray(nextMessage)) fakeMsg.message = nextMessage
|
|
1346
1353
|
fakeMsg.raw_message = trueMsg[0].raw_message
|
|
1347
1354
|
fakeMsg.time = trueMsg[0].time
|
|
1348
1355
|
fakeMsg.fake_msg = undefined
|
|
@@ -1699,8 +1706,9 @@ function saveUser(msg: { [key: string]: any }, type: string) {
|
|
|
1699
1706
|
const channel = String(item.channel_id ?? item.channelId ?? '')
|
|
1700
1707
|
const group = String(item.group_id ?? '')
|
|
1701
1708
|
const user = String(item.user_id ?? '')
|
|
1702
|
-
if (
|
|
1703
|
-
|
|
1709
|
+
if (channel) return normalizeSessionId(channel)
|
|
1710
|
+
if (group) return normalizeSessionId(group)
|
|
1711
|
+
return normalizeSessionId(user)
|
|
1704
1712
|
}
|
|
1705
1713
|
const existingIds = new Set(contactStore.userList.map(sessionDedupKey))
|
|
1706
1714
|
const freshList = list.filter((item) => {
|
|
@@ -1891,19 +1899,28 @@ async function saveMsg(msg: any, append = undefined as undefined | string) {
|
|
|
1891
1899
|
const lastMsg =
|
|
1892
1900
|
chatStore.messageList[chatStore.messageList.length - 1]
|
|
1893
1901
|
if (lastMsg) {
|
|
1902
|
+
const isGroup = chatStore.chatInfo.show.type === 'group'
|
|
1894
1903
|
const user = findSessionContact(contactStore.userList, chatStore.chatInfo.show.id)
|
|
1895
1904
|
const sessionId = normalizeSessionId(
|
|
1896
1905
|
chatStore.chatInfo.show.channel_id
|
|
1897
1906
|
?? chatStore.chatInfo.show.id,
|
|
1898
1907
|
)
|
|
1899
|
-
const session =
|
|
1908
|
+
const session = isGroup
|
|
1909
|
+
? user
|
|
1910
|
+
: contactStore.baseOnMsgList.get(sessionId) ?? user
|
|
1900
1911
|
if (session) {
|
|
1901
1912
|
const preview = formatMessageData(
|
|
1902
1913
|
lastMsg,
|
|
1903
|
-
|
|
1914
|
+
isGroup,
|
|
1904
1915
|
)
|
|
1905
1916
|
if (user) Object.assign(user, preview)
|
|
1906
1917
|
Object.assign(session, preview)
|
|
1918
|
+
if (isGroup) {
|
|
1919
|
+
// 群历史同步到联系人列表,同时进入群会话列表
|
|
1920
|
+
contactStore.userList = upsertSessionContact(contactStore.userList, session)
|
|
1921
|
+
} else if (session._groupMember) {
|
|
1922
|
+
session._groupMember = false
|
|
1923
|
+
}
|
|
1907
1924
|
contactStore.baseOnMsgList.set(sessionId, session)
|
|
1908
1925
|
updateBaseOnMsgList()
|
|
1909
1926
|
}
|
|
@@ -2326,7 +2343,7 @@ function newMsg(_: string, data: any) {
|
|
|
2326
2343
|
}
|
|
2327
2344
|
// 消息基础信息:优先使用 Satori 转换层生成的 infoList,避免 OneBot pathMap 误解析。
|
|
2328
2345
|
const rawInfo = getDataObject(data.infoList ?? data)
|
|
2329
|
-
const
|
|
2346
|
+
const rawId = getString(rawInfo.group_id) ||
|
|
2330
2347
|
getString(rawInfo.private_id) ||
|
|
2331
2348
|
getString(data.group_id) ||
|
|
2332
2349
|
getString(data.user_id) ||
|
|
@@ -2354,12 +2371,16 @@ function newMsg(_: string, data: any) {
|
|
|
2354
2371
|
: data.message_type === 'group' || Boolean(data.group_id) || Boolean(rawInfo.group_id)
|
|
2355
2372
|
const sessionChannelId = getString(rawInfo.channel_id) || getString(data.channel_id)
|
|
2356
2373
|
const sessionGuildId = getString(rawInfo.guild_id) || getString(data.guild_id)
|
|
2374
|
+
// 群消息必须用群/频道 ID 作为会话,不能退化成发送者用户 ID
|
|
2375
|
+
const id = isGroupMessage
|
|
2376
|
+
? getString(rawInfo.group_id) || getString(data.group_id) || sessionChannelId || sessionGuildId || ''
|
|
2377
|
+
: rawId
|
|
2357
2378
|
if (!id || id === '0') return
|
|
2358
2379
|
const isCurrentSession = Boolean(
|
|
2359
2380
|
(
|
|
2360
2381
|
sessionChannelId &&
|
|
2361
2382
|
showChannelId &&
|
|
2362
|
-
|
|
2383
|
+
sessionChannelId === showChannelId
|
|
2363
2384
|
) || (
|
|
2364
2385
|
(!sessionChannelId || !showChannelId) &&
|
|
2365
2386
|
(id === showId || targetId === showId || String(sessionChannelId || sessionGuildId || '') === showId)
|
|
@@ -2572,6 +2593,13 @@ function newMsg(_: string, data: any) {
|
|
|
2572
2593
|
if (data.atall) { session.highlight = $t('[@全体]') }
|
|
2573
2594
|
if (isImportant) { session.highlight = $t('[特別关心]') }
|
|
2574
2595
|
}
|
|
2596
|
+
if (!isGroupMessage && session._groupMember) {
|
|
2597
|
+
session._groupMember = false
|
|
2598
|
+
}
|
|
2599
|
+
if (isGroupMessage) {
|
|
2600
|
+
// 群消息同步到联系人列表,同时进入群会话列表
|
|
2601
|
+
contactStore.userList = upsertSessionContact(contactStore.userList, session)
|
|
2602
|
+
}
|
|
2575
2603
|
setSessionContact(contactStore.baseOnMsgList, session)
|
|
2576
2604
|
updateBaseOnMsgList()
|
|
2577
2605
|
}
|
|
@@ -2589,17 +2617,8 @@ function newMsg(_: string, data: any) {
|
|
|
2589
2617
|
hidden: document.hidden,
|
|
2590
2618
|
isImportant: isImportant
|
|
2591
2619
|
})
|
|
2592
|
-
//
|
|
2593
|
-
|
|
2594
|
-
const forceImportantNotice = isImportant ||
|
|
2595
|
-
(isGroupMessage && (data.atme || data.atall || isGroupNotice))
|
|
2596
|
-
if (
|
|
2597
|
-
forceGroupSystemNotice ||
|
|
2598
|
-
forceImportantNotice ||
|
|
2599
|
-
!isCurrentSession ||
|
|
2600
|
-
!document.hasFocus() ||
|
|
2601
|
-
document.hidden
|
|
2602
|
-
) {
|
|
2620
|
+
// 当前会话已打开时不弹系统通知,新消息交给右下角置底箭头提示
|
|
2621
|
+
if (!isCurrentSession) {
|
|
2603
2622
|
// 准备消息内容
|
|
2604
2623
|
let raw = getMsgRawTxt(data)
|
|
2605
2624
|
raw = raw === '' ? data.raw_message : raw
|