koishi-plugin-chat-patch 5.3.0 → 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-CTdyeaX2.css → Chat-DTEsebnY.css} +1 -1
- package/client/web/dist/assets/{MsgBody-DDacjWJ9.js → MsgBody-C0TKsXhC.js} +1 -1
- package/client/web/dist/assets/{MsgBody.vue_vue_type_script_setup_true_lang-B1dWyCB0.js → MsgBody.vue_vue_type_script_setup_true_lang-CCdBwlN_.js} +2 -2
- 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-BlLut5xn.js → index-BA37EQGa.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 +7 -4
- package/client/web/src/components/BulletinBody.vue +1 -1
- package/client/web/src/components/DepPan.vue +1 -1
- package/client/web/src/components/FriendBody.vue +1 -0
- package/client/web/src/components/MsgBody.vue +1 -3
- package/client/web/src/components/ThemeColorPickerPan.vue +1 -1
- package/client/web/src/components/TinySessionBody.vue +1 -0
- package/client/web/src/components/UmamiInfoPan.vue +1 -1
- package/client/web/src/components/UpdatePan.vue +2 -2
- package/client/web/src/components/WelPan.vue +1 -1
- package/client/web/src/components/tooltip/UserInfoTooltip.vue +1 -1
- package/client/web/src/function/connect.ts +279 -86
- 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 +35 -13
- package/client/web/src/function/satori.ts +33 -2
- package/client/web/src/function/utils/appUtil.ts +3 -11
- package/client/web/src/function/utils/avatarUtil.ts +18 -3
- 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/main.ts +5 -0
- package/client/web/src/pages/Chat.vue +50 -47
- package/client/web/src/pages/Friends.vue +28 -24
- package/client/web/src/pages/Info.vue +5 -5
- package/client/web/src/pages/Messages.vue +57 -36
- package/client/web/src/pages/Qzone.vue +2 -2
- package/client/web/src/pages/chat-view/ChatTerminal.vue +4 -1
- package/client/web/src/pages/chat-view/SystemNotice.vue +3 -3
- package/client/web/src/pages/options/OptAccount.vue +6 -4
- package/client/web/src/pages/options/OptDev.vue +45 -2
- package/lib/database.d.ts +58 -0
- package/lib/index.js +234 -82
- 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 +121 -0
- package/lib/web.d.ts +7 -0
- package/package.json +1 -1
- package/src/bootstrap.ts +1 -0
- package/src/cache.ts +1 -5
- package/src/config.ts +1 -1
- package/src/database.ts +52 -8
- package/src/index.ts +4 -4
- package/src/media.ts +97 -0
- package/src/recorder.ts +35 -26
- package/src/self-message.ts +68 -19
- package/src/types.ts +10 -0
- package/src/web.ts +57 -48
- package/client/web/dist/assets/Chat-B448YOeN.js +0 -28
- package/client/web/dist/assets/index-fW_zcub3.css +0 -1
package/src/self-message.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { createHash, randomUUID } from 'node:crypto'
|
|
|
3
3
|
|
|
4
4
|
import { Config } from './config'
|
|
5
5
|
import { ChatDatabase } from './database'
|
|
6
|
+
import { MediaManager } from './media'
|
|
6
7
|
import { PluginLogger } from './logger'
|
|
7
8
|
import { SelfMessageChannelType, SelfMessageRecord, SelfMessageSource } from './types'
|
|
8
9
|
|
|
@@ -14,11 +15,13 @@ type SendOptionsWithMeta = SendOptions & {
|
|
|
14
15
|
type BotSendMethod = Bot['sendMessage']
|
|
15
16
|
type BotCreateMethod = Bot['createMessage']
|
|
16
17
|
type BotPrivateMethod = Bot['sendPrivateMessage']
|
|
18
|
+
type BotDeleteMethod = Bot['deleteMessage']
|
|
17
19
|
|
|
18
20
|
interface BotMethods {
|
|
19
21
|
createMessage: BotCreateMethod
|
|
20
22
|
sendMessage: BotSendMethod
|
|
21
23
|
sendPrivateMessage: BotPrivateMethod
|
|
24
|
+
deleteMessage: BotDeleteMethod
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
interface SatoriElement {
|
|
@@ -38,6 +41,11 @@ function getString(value: unknown): string {
|
|
|
38
41
|
return typeof value === 'string' ? value : ''
|
|
39
42
|
}
|
|
40
43
|
|
|
44
|
+
function getMessageId(value: unknown): string {
|
|
45
|
+
const raw = getObject(value).id ?? getObject(value).message_id
|
|
46
|
+
return raw == null ? '' : String(raw)
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
function getNumber(value: unknown): number {
|
|
42
50
|
const num = Number(value)
|
|
43
51
|
return Number.isFinite(num) ? num : 0
|
|
@@ -51,8 +59,9 @@ function getObject(value: unknown): Record<string, unknown> {
|
|
|
51
59
|
|
|
52
60
|
function toSatoriElement(value: unknown): SatoriElement {
|
|
53
61
|
const raw = getObject(value)
|
|
54
|
-
const
|
|
55
|
-
|
|
62
|
+
const rawAttrs = raw.attrs ?? raw.data
|
|
63
|
+
const attrs = typeof rawAttrs === 'object' && rawAttrs !== null
|
|
64
|
+
? rawAttrs as Record<string, unknown>
|
|
56
65
|
: {}
|
|
57
66
|
const children = Array.isArray(raw.children)
|
|
58
67
|
? raw.children.map(toSatoriElement)
|
|
@@ -71,10 +80,6 @@ function isForwardContainer(element: SatoriElement): boolean {
|
|
|
71
80
|
&& ('forward' in (element.attrs ?? {}) || element.attrs?.forward === true))
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
function isPrivateChannel(value: string): boolean {
|
|
75
|
-
return /^(?:private|direct):/i.test(value)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
83
|
function toSegments(
|
|
79
84
|
elements: unknown[],
|
|
80
85
|
resolveI18n?: (attrs: Record<string, unknown>) => string,
|
|
@@ -85,9 +90,9 @@ function toSegments(
|
|
|
85
90
|
const attrs = element.attrs ?? {}
|
|
86
91
|
const type = getString(element.type)
|
|
87
92
|
if (type === 'text') {
|
|
88
|
-
result.push({ type: 'text', text: getString(attrs.content) })
|
|
93
|
+
result.push({ type: 'text', text: getString(attrs.content) || getString(attrs.text) })
|
|
89
94
|
} else if (type === 'at') {
|
|
90
|
-
const id = getString(attrs.id)
|
|
95
|
+
const id = getString(attrs.id) || getString(attrs.qq)
|
|
91
96
|
const name = getString(attrs.name) || (attrs.type === 'all' ? '所有人' : id)
|
|
92
97
|
result.push({ type: 'at', qq: id, text: name.startsWith('@') ? name : `@${name}` })
|
|
93
98
|
} else if (type === 'img' || type === 'image') {
|
|
@@ -210,6 +215,7 @@ export class SelfMessageRecorder {
|
|
|
210
215
|
private ctx: Context,
|
|
211
216
|
private config: Config,
|
|
212
217
|
private database: ChatDatabase,
|
|
218
|
+
private media: MediaManager,
|
|
213
219
|
private logger: PluginLogger,
|
|
214
220
|
) {}
|
|
215
221
|
|
|
@@ -245,6 +251,7 @@ export class SelfMessageRecorder {
|
|
|
245
251
|
createMessage: bot.createMessage,
|
|
246
252
|
sendMessage: bot.sendMessage,
|
|
247
253
|
sendPrivateMessage: bot.sendPrivateMessage,
|
|
254
|
+
deleteMessage: bot.deleteMessage,
|
|
248
255
|
}
|
|
249
256
|
this.wrapped.set(bot, methods)
|
|
250
257
|
|
|
@@ -258,7 +265,7 @@ export class SelfMessageRecorder {
|
|
|
258
265
|
try {
|
|
259
266
|
const messages = await methods.createMessage.call(bot, channelId, content, referrer, sendOptions)
|
|
260
267
|
const ids = Array.isArray(messages)
|
|
261
|
-
? messages.map((item) =>
|
|
268
|
+
? messages.map((item) => getMessageId(item)).filter((id) => id !== '')
|
|
262
269
|
: []
|
|
263
270
|
await this.recordFromWrapper(bot, channelId, content, ids, options, 'create', localId)
|
|
264
271
|
return messages
|
|
@@ -267,6 +274,11 @@ export class SelfMessageRecorder {
|
|
|
267
274
|
}
|
|
268
275
|
}
|
|
269
276
|
|
|
277
|
+
bot.deleteMessage = async (channelId, messageId) => {
|
|
278
|
+
await methods.deleteMessage.call(bot, channelId, messageId)
|
|
279
|
+
await this.markSelfMessageRevoked(bot, channelId, messageId)
|
|
280
|
+
}
|
|
281
|
+
|
|
270
282
|
bot.sendMessage = async (channelId, content, referrer, options) => {
|
|
271
283
|
const sendOptions = this.prepareOptions(options)
|
|
272
284
|
if (sendOptions.chatPatchId) {
|
|
@@ -275,7 +287,8 @@ export class SelfMessageRecorder {
|
|
|
275
287
|
const localId = randomUUID()
|
|
276
288
|
sendOptions.chatPatchId = localId
|
|
277
289
|
try {
|
|
278
|
-
const ids = await methods.sendMessage.call(bot, channelId, content, referrer, sendOptions)
|
|
290
|
+
const ids = (await methods.sendMessage.call(bot, channelId, content, referrer, sendOptions))
|
|
291
|
+
.filter((id) => id !== '')
|
|
279
292
|
await this.recordFromWrapper(bot, channelId, content, ids, options, 'send', localId)
|
|
280
293
|
return ids
|
|
281
294
|
} finally {
|
|
@@ -291,7 +304,8 @@ export class SelfMessageRecorder {
|
|
|
291
304
|
const localId = randomUUID()
|
|
292
305
|
sendOptions.chatPatchId = localId
|
|
293
306
|
try {
|
|
294
|
-
const ids = await methods.sendPrivateMessage.call(bot, userId, content, guildId, sendOptions)
|
|
307
|
+
const ids = (await methods.sendPrivateMessage.call(bot, userId, content, guildId, sendOptions))
|
|
308
|
+
.filter((id) => id !== '')
|
|
295
309
|
await this.recordFromWrapper(bot, userId, content, ids, options, 'private', localId)
|
|
296
310
|
return ids
|
|
297
311
|
} finally {
|
|
@@ -306,6 +320,7 @@ export class SelfMessageRecorder {
|
|
|
306
320
|
bot.createMessage = methods.createMessage
|
|
307
321
|
bot.sendMessage = methods.sendMessage
|
|
308
322
|
bot.sendPrivateMessage = methods.sendPrivateMessage
|
|
323
|
+
bot.deleteMessage = methods.deleteMessage
|
|
309
324
|
this.wrapped.delete(bot)
|
|
310
325
|
}
|
|
311
326
|
|
|
@@ -323,6 +338,10 @@ export class SelfMessageRecorder {
|
|
|
323
338
|
localId: string,
|
|
324
339
|
) {
|
|
325
340
|
if (!channelId || this.isBlocked(bot.platform ?? '')) return
|
|
341
|
+
if (!ids.length) {
|
|
342
|
+
this.logger.warn('发送未返回消息 ID,判定为发送失败:', bot.platform, channelId)
|
|
343
|
+
return
|
|
344
|
+
}
|
|
326
345
|
const source: SelfMessageSource = options?.session ? 'plugin' : 'bot'
|
|
327
346
|
const record = this.buildRecord(
|
|
328
347
|
bot.platform ?? '',
|
|
@@ -336,9 +355,26 @@ export class SelfMessageRecorder {
|
|
|
336
355
|
localId,
|
|
337
356
|
)
|
|
338
357
|
await this.database.upsertSelfMessage(record)
|
|
358
|
+
void this.media.cacheMessageMedia(content, channelId).catch((error) => {
|
|
359
|
+
this.logger.warn('缓存机器人消息媒体失败:', error)
|
|
360
|
+
})
|
|
339
361
|
this.logger.logInfo('机器人消息已记录:', record.kind, record.channelId, record.messageId || record.id)
|
|
340
362
|
}
|
|
341
363
|
|
|
364
|
+
private async markSelfMessageRevoked(bot: Bot, channelId: string, messageId: string) {
|
|
365
|
+
if (!messageId || !channelId || this.isBlocked(bot.platform ?? '')) return
|
|
366
|
+
const updated = await this.database.updateSelfMessageByMessageId(
|
|
367
|
+
bot.platform ?? '',
|
|
368
|
+
bot.selfId,
|
|
369
|
+
channelId,
|
|
370
|
+
messageId,
|
|
371
|
+
{ revoked: true, revokedAt: Date.now() },
|
|
372
|
+
)
|
|
373
|
+
if (!updated) {
|
|
374
|
+
this.logger.warn('撤回消息未找到本地自消息记录:', bot.platform, channelId, messageId)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
342
378
|
// 收到 send 事件时只补消息 id,避免重复记录
|
|
343
379
|
private async recordSendEvent(session: Session) {
|
|
344
380
|
if (session.type !== 'send') return
|
|
@@ -350,7 +386,7 @@ export class SelfMessageRecorder {
|
|
|
350
386
|
const channelId = session.channelId || getString(eventChannel.id)
|
|
351
387
|
if (this.isBlocked(platform) || !platform || !selfId || !channelId) return
|
|
352
388
|
|
|
353
|
-
const messageId = session.messageId ||
|
|
389
|
+
const messageId = session.messageId || getMessageId(eventMessage)
|
|
354
390
|
if (messageId) {
|
|
355
391
|
const updated = await this.database.updateSelfMessageByMessageId(
|
|
356
392
|
platform,
|
|
@@ -376,6 +412,9 @@ export class SelfMessageRecorder {
|
|
|
376
412
|
randomUUID(),
|
|
377
413
|
)
|
|
378
414
|
await this.database.upsertSelfMessage(record)
|
|
415
|
+
void this.media.cacheMessageMedia(session.content ?? '', channelId).catch((error) => {
|
|
416
|
+
this.logger.warn('缓存 send 事件媒体失败:', error)
|
|
417
|
+
})
|
|
379
418
|
}
|
|
380
419
|
|
|
381
420
|
private resolveI18nElement(attrs: Record<string, unknown>): string {
|
|
@@ -404,10 +443,9 @@ export class SelfMessageRecorder {
|
|
|
404
443
|
mode: 'create' | 'send' | 'private',
|
|
405
444
|
localId: string,
|
|
406
445
|
): SelfMessageRecord {
|
|
407
|
-
const normalizedChannelId =
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const channelType: SelfMessageChannelType = mode === 'private' || isPrivateChannel(normalizedChannelId)
|
|
446
|
+
const normalizedChannelId = channelId
|
|
447
|
+
// 只按调用入口判断私聊,不根据频道 ID 前缀推断
|
|
448
|
+
const channelType: SelfMessageChannelType = mode === 'private'
|
|
411
449
|
? 'user'
|
|
412
450
|
: 'group'
|
|
413
451
|
const elements = this.normalizeElements(content)
|
|
@@ -445,9 +483,20 @@ export class SelfMessageRecorder {
|
|
|
445
483
|
|
|
446
484
|
private normalizeElements(content: MessageContent): unknown[] {
|
|
447
485
|
try {
|
|
448
|
-
const
|
|
449
|
-
?
|
|
450
|
-
:
|
|
486
|
+
const normalized = typeof content === 'string'
|
|
487
|
+
? content
|
|
488
|
+
: Array.isArray(content)
|
|
489
|
+
? content.map((raw) => {
|
|
490
|
+
const item = getObject(raw)
|
|
491
|
+
if (typeof item.data === 'object' && item.data !== null && item.attrs === undefined) {
|
|
492
|
+
return h(getString(item.type), item.data as Record<string, unknown>)
|
|
493
|
+
}
|
|
494
|
+
return raw
|
|
495
|
+
})
|
|
496
|
+
: content
|
|
497
|
+
const elements = typeof normalized === 'string'
|
|
498
|
+
? h.parse(normalized)
|
|
499
|
+
: h.toElementArray(normalized)
|
|
451
500
|
return JSON.parse(JSON.stringify(elements)) as unknown[]
|
|
452
501
|
} catch {
|
|
453
502
|
return []
|
package/src/types.ts
CHANGED
|
@@ -5,6 +5,10 @@ export interface SatoriBootstrap {
|
|
|
5
5
|
endpoint: string
|
|
6
6
|
token: string
|
|
7
7
|
basePath: string
|
|
8
|
+
blockedPlatforms: Array<{
|
|
9
|
+
platformName: string
|
|
10
|
+
exactMatch: boolean
|
|
11
|
+
}>
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
export interface MessageRecord {
|
|
@@ -22,6 +26,8 @@ export interface MessageRecord {
|
|
|
22
26
|
content?: string
|
|
23
27
|
elements?: unknown[]
|
|
24
28
|
raw?: unknown
|
|
29
|
+
revoked?: boolean
|
|
30
|
+
revokedAt?: number
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
export type SelfMessageChannelType = 'group' | 'user'
|
|
@@ -45,6 +51,8 @@ export interface SelfMessageRecord {
|
|
|
45
51
|
source: SelfMessageSource
|
|
46
52
|
kind: string
|
|
47
53
|
fingerprint?: string
|
|
54
|
+
revoked?: boolean
|
|
55
|
+
revokedAt?: number
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
export interface SelfMessagePayload {
|
|
@@ -64,6 +72,8 @@ export interface SelfMessagePayload {
|
|
|
64
72
|
sequence?: number
|
|
65
73
|
source?: SelfMessageSource
|
|
66
74
|
kind?: string
|
|
75
|
+
revoked?: boolean
|
|
76
|
+
revokedAt?: number
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
export interface HistoryQuery {
|
package/src/web.ts
CHANGED
|
@@ -9,9 +9,10 @@ import type { DefaultContext, DefaultState, ParameterizedContext } from 'koa'
|
|
|
9
9
|
import FileType from 'file-type'
|
|
10
10
|
|
|
11
11
|
import { Config } from './config'
|
|
12
|
-
import { ContactCacheService } from './cache'
|
|
13
|
-
import { ChatDatabase } from './database'
|
|
14
|
-
import {
|
|
12
|
+
import { ContactCacheService } from './cache'
|
|
13
|
+
import { ChatDatabase } from './database'
|
|
14
|
+
import { MediaManager } from './media'
|
|
15
|
+
import { PluginLogger } from './logger'
|
|
15
16
|
import { ContactCacheItem, SelfMessagePayload, SelfMessageRecord } from './types'
|
|
16
17
|
|
|
17
18
|
interface ViteConsoleServer {
|
|
@@ -29,11 +30,12 @@ interface ViteConsoleLike {
|
|
|
29
30
|
|
|
30
31
|
export function registerWeb(
|
|
31
32
|
ctx: Context,
|
|
32
|
-
config: Config,
|
|
33
|
-
database: ChatDatabase,
|
|
34
|
-
contactCache: ContactCacheService,
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
config: Config,
|
|
34
|
+
database: ChatDatabase,
|
|
35
|
+
contactCache: ContactCacheService,
|
|
36
|
+
media: MediaManager,
|
|
37
|
+
logger: PluginLogger,
|
|
38
|
+
) {
|
|
37
39
|
const webRoot = path.resolve(__dirname, '..', 'client', 'web', 'dist')
|
|
38
40
|
const webSource = path.resolve(__dirname, '..', 'client', 'web', 'src')
|
|
39
41
|
const webPublic = path.resolve(__dirname, '..', 'client', 'web', 'public')
|
|
@@ -82,40 +84,27 @@ export function registerWeb(
|
|
|
82
84
|
koa.body = ''
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
// web 应用里仍有 /img
|
|
86
|
-
const
|
|
87
|
-
ctx.server.get(pattern, async (koa: WebContext
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
registerDevPublic('/img(.*)', 'img')
|
|
87
|
+
// web 应用里仍有 /img 这类运行时绝对路径,生产/开发模式都要由 Koishi 服务托管
|
|
88
|
+
const registerPublic = (pattern: string, prefix: string) => {
|
|
89
|
+
ctx.server.get(pattern, async (koa: WebContext) => {
|
|
90
|
+
await servePublicFile(koa, prefix)
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
registerPublic('/img(.*)', 'img')
|
|
94
95
|
|
|
95
96
|
const toExtension = (value: string): string => {
|
|
96
97
|
const ext = value.startsWith('.') ? value : `.${value}`
|
|
97
98
|
return ext.toLowerCase()
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
const normalizeGroupId = (value: string): string => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return wrapped ? wrapped[1] : raw || value
|
|
104
|
-
}
|
|
101
|
+
const normalizeGroupId = (value: string): string => {
|
|
102
|
+
return String(value)
|
|
103
|
+
}
|
|
105
104
|
|
|
106
|
-
const historyChannelCandidates = (channelId: string): string[] => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (raw && raw !== channelId) candidates.push(raw)
|
|
110
|
-
const id = raw || channelId
|
|
111
|
-
if (!/^(?:group|room|chat|channel|guild|private):/i.test(channelId)) {
|
|
112
|
-
candidates.push(`group:${id}`, `private:${id}`)
|
|
113
|
-
}
|
|
114
|
-
if (id) {
|
|
115
|
-
candidates.push(` [_${id}_] `, `_${id}_`, `[${id}]`, `group:${id}`)
|
|
116
|
-
}
|
|
117
|
-
return [...new Set(candidates)]
|
|
118
|
-
}
|
|
105
|
+
const historyChannelCandidates = (channelId: string): string[] => {
|
|
106
|
+
return channelId ? [channelId] : []
|
|
107
|
+
}
|
|
119
108
|
|
|
120
109
|
const mimeToExt: Record<string, string> = {
|
|
121
110
|
'audio/mpeg': '.mp3',
|
|
@@ -380,7 +369,9 @@ export function registerWeb(
|
|
|
380
369
|
channelId: String(body.channelId ?? ''),
|
|
381
370
|
guildId: typeof body.guildId === 'string' ? body.guildId : undefined,
|
|
382
371
|
channelType: body.channelType === 'user' ? 'user' : 'group',
|
|
383
|
-
messageId: typeof body.messageId === 'string'
|
|
372
|
+
messageId: typeof body.messageId === 'string' || typeof body.messageId === 'number'
|
|
373
|
+
? String(body.messageId)
|
|
374
|
+
: undefined,
|
|
384
375
|
content: typeof body.content === 'string' ? body.content : undefined,
|
|
385
376
|
elements: Array.isArray(body.elements) ? body.elements as unknown[] : undefined,
|
|
386
377
|
message: Array.isArray(body.message) ? body.message as unknown[] : undefined,
|
|
@@ -392,8 +383,12 @@ export function registerWeb(
|
|
|
392
383
|
sequence: typeof body.sequence === 'number' && Number.isFinite(body.sequence)
|
|
393
384
|
? body.sequence
|
|
394
385
|
: 0,
|
|
395
|
-
source: body.source === 'plugin' ? 'plugin' : 'webui',
|
|
396
|
-
kind: typeof body.kind === 'string' ? body.kind : 'text',
|
|
386
|
+
source: body.source === 'plugin' ? 'plugin' : 'webui',
|
|
387
|
+
kind: typeof body.kind === 'string' ? body.kind : 'text',
|
|
388
|
+
revoked: body.revoked === true,
|
|
389
|
+
revokedAt: typeof body.revokedAt === 'number' && Number.isFinite(body.revokedAt)
|
|
390
|
+
? body.revokedAt
|
|
391
|
+
: undefined,
|
|
397
392
|
}
|
|
398
393
|
if (!payload.platform || !payload.selfId || !payload.channelId) {
|
|
399
394
|
koa.status = 400
|
|
@@ -415,9 +410,11 @@ export function registerWeb(
|
|
|
415
410
|
forwardContent: payload.forwardContent,
|
|
416
411
|
sentAt: payload.sentAt ?? Date.now(),
|
|
417
412
|
sequence: payload.sequence ?? 0,
|
|
418
|
-
source: payload.source ?? 'webui',
|
|
419
|
-
kind: payload.kind ?? 'text',
|
|
420
|
-
|
|
413
|
+
source: payload.source ?? 'webui',
|
|
414
|
+
kind: payload.kind ?? 'text',
|
|
415
|
+
revoked: payload.revoked,
|
|
416
|
+
revokedAt: payload.revokedAt,
|
|
417
|
+
}
|
|
421
418
|
await database.upsertSelfMessage(record)
|
|
422
419
|
koa.body = { ok: true, message: record }
|
|
423
420
|
})
|
|
@@ -630,7 +627,7 @@ export function registerWeb(
|
|
|
630
627
|
}
|
|
631
628
|
})
|
|
632
629
|
|
|
633
|
-
ctx.server.post(`${config.basePath}/api/cache`, async (koa) => {
|
|
630
|
+
ctx.server.post(`${config.basePath}/api/cache`, async (koa) => {
|
|
634
631
|
const requestBody = (koa.request as unknown as { body?: unknown }).body
|
|
635
632
|
const body = (requestBody ?? {}) as Record<string, unknown>
|
|
636
633
|
const platform = String(body.platform ?? '')
|
|
@@ -670,12 +667,24 @@ export function registerWeb(
|
|
|
670
667
|
await database.setContacts(platform, selfId, normalizedType, contacts)
|
|
671
668
|
}
|
|
672
669
|
}
|
|
673
|
-
koa.body = {
|
|
674
|
-
contacts: await database.getContacts(platform, selfId, normalizedType),
|
|
675
|
-
}
|
|
676
|
-
})
|
|
677
|
-
|
|
678
|
-
|
|
670
|
+
koa.body = {
|
|
671
|
+
contacts: await database.getContacts(platform, selfId, normalizedType),
|
|
672
|
+
}
|
|
673
|
+
})
|
|
674
|
+
|
|
675
|
+
ctx.server.post(`${config.basePath}/api/clear-all`, async (koa) => {
|
|
676
|
+
try {
|
|
677
|
+
await database.clearAll()
|
|
678
|
+
await media.clearAll()
|
|
679
|
+
koa.body = { ok: true }
|
|
680
|
+
} catch (error) {
|
|
681
|
+
logger.warn('清空全部缓存失败:', error)
|
|
682
|
+
koa.status = 500
|
|
683
|
+
koa.body = { ok: false, error: String(error) }
|
|
684
|
+
}
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
const serveFile = async (koa: WebContext, fileName: string) => {
|
|
679
688
|
const fullPath = path.join(webRoot, fileName)
|
|
680
689
|
if (existsSync(fullPath) && statSync(fullPath).isFile()) {
|
|
681
690
|
await send(koa, path.relative(webRoot, fullPath), { root: webRoot })
|