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/lib/index.js
CHANGED
|
@@ -72,9 +72,7 @@ function isUsableName(value, id) {
|
|
|
72
72
|
}
|
|
73
73
|
__name(isUsableName, "isUsableName");
|
|
74
74
|
function normalizeGroupId(value) {
|
|
75
|
-
|
|
76
|
-
const wrapped = raw.match(/^\[_?([\s\S]+?)_?\]$/);
|
|
77
|
-
return wrapped ? wrapped[1] : raw || value;
|
|
75
|
+
return String(value);
|
|
78
76
|
}
|
|
79
77
|
__name(normalizeGroupId, "normalizeGroupId");
|
|
80
78
|
function isPrivateChannelType(value) {
|
|
@@ -170,7 +168,6 @@ var _ContactCacheService = class _ContactCacheService {
|
|
|
170
168
|
const guild = guildId && guildId !== "0" ? guildId : void 0;
|
|
171
169
|
const channel = channelId && channelId !== "0" ? channelId : void 0;
|
|
172
170
|
if (isPrivateChannelType(channelType)) return null;
|
|
173
|
-
if (channelType === void 0 && (/^(?:private|direct):/i.test(id) || /^(?:private|direct):/i.test(channel || ""))) return null;
|
|
174
171
|
const rawId = id && id !== "0" ? normalizeGroupId(id) : "";
|
|
175
172
|
const cacheId = rawId || guild || normalizeGroupId(channel || "") || "";
|
|
176
173
|
if (!cacheId) return null;
|
|
@@ -353,10 +350,6 @@ function legacyGroupMemberKey(platform, selfId, groupId) {
|
|
|
353
350
|
return `gm:${platform}:${selfId}:${groupId}`;
|
|
354
351
|
}
|
|
355
352
|
__name(legacyGroupMemberKey, "legacyGroupMemberKey");
|
|
356
|
-
function isPrivateChannelId(value) {
|
|
357
|
-
return typeof value === "string" && /^(?:private|direct):/i.test(value);
|
|
358
|
-
}
|
|
359
|
-
__name(isPrivateChannelId, "isPrivateChannelId");
|
|
360
353
|
function isPrivateChannelType2(value) {
|
|
361
354
|
const num = Number(value);
|
|
362
355
|
if (Number.isFinite(num)) return num === 1;
|
|
@@ -365,10 +358,9 @@ function isPrivateChannelType2(value) {
|
|
|
365
358
|
}
|
|
366
359
|
__name(isPrivateChannelType2, "isPrivateChannelType");
|
|
367
360
|
function isUsableGroupContact(item) {
|
|
368
|
-
if (isPrivateChannelId(item.channelId)) return false;
|
|
369
361
|
if (typeof item.raw === "object" && item.raw !== null) {
|
|
370
362
|
const raw = item.raw;
|
|
371
|
-
if (isPrivateChannelType2(raw.channel_type)
|
|
363
|
+
if (isPrivateChannelType2(raw.channel_type)) return false;
|
|
372
364
|
}
|
|
373
365
|
return true;
|
|
374
366
|
}
|
|
@@ -393,6 +385,12 @@ var _ChatDatabase = class _ChatDatabase {
|
|
|
393
385
|
this.opened = false;
|
|
394
386
|
this.logger.logInfo("LevelDB closed");
|
|
395
387
|
}
|
|
388
|
+
async clearAll() {
|
|
389
|
+
await this.db.clear();
|
|
390
|
+
const db = this.db;
|
|
391
|
+
await db.compactRange("", "");
|
|
392
|
+
this.logger.logInfo("数据库缓存已全部清空并完成压缩");
|
|
393
|
+
}
|
|
396
394
|
async appendMessage(record) {
|
|
397
395
|
await this.db.put(messageKey(record), JSON.stringify(record));
|
|
398
396
|
await this.trimMessages(record.platform, record.selfId, record.channelId || "");
|
|
@@ -458,6 +456,33 @@ var _ChatDatabase = class _ChatDatabase {
|
|
|
458
456
|
}
|
|
459
457
|
return false;
|
|
460
458
|
}
|
|
459
|
+
async updateMessageRevoked(platform, selfId, channelId, messageId, patch) {
|
|
460
|
+
const operations = [];
|
|
461
|
+
for (const prefix of [messagePrefix(platform, selfId, channelId), legacyMessagePrefix(platform, selfId, channelId)]) {
|
|
462
|
+
for await (const [key, value] of this.db.iterator({
|
|
463
|
+
gte: prefix,
|
|
464
|
+
lte: `${prefix}`
|
|
465
|
+
})) {
|
|
466
|
+
try {
|
|
467
|
+
const parsed = JSON.parse(value);
|
|
468
|
+
if (typeof parsed !== "object" || parsed === null) continue;
|
|
469
|
+
const record = parsed;
|
|
470
|
+
const raw = typeof record.raw === "object" && record.raw !== null ? record.raw : {};
|
|
471
|
+
const rawMessage = typeof raw.message === "object" && raw.message !== null ? raw.message : {};
|
|
472
|
+
const id = String(record.id ?? rawMessage.id ?? "");
|
|
473
|
+
if (!id || id !== messageId) continue;
|
|
474
|
+
operations.push({
|
|
475
|
+
type: "put",
|
|
476
|
+
key,
|
|
477
|
+
value: JSON.stringify({ ...parsed, ...patch })
|
|
478
|
+
});
|
|
479
|
+
} catch {
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
if (operations.length) await this.db.batch(operations);
|
|
484
|
+
return operations.length > 0;
|
|
485
|
+
}
|
|
461
486
|
async findSelfForwardContent(platform, selfId, channelId, id) {
|
|
462
487
|
const prefixes = channelId ? [selfMessagePrefix(platform, selfId, channelId)] : [`sm:${encodeKeyPart2(platform)}:${encodeKeyPart2(selfId)}:`];
|
|
463
488
|
for (const prefix of prefixes) {
|
|
@@ -818,9 +843,7 @@ var ChatDatabase = _ChatDatabase;
|
|
|
818
843
|
// src/recorder.ts
|
|
819
844
|
var MESSAGE_TYPES = /* @__PURE__ */ new Set(["message", "message-created"]);
|
|
820
845
|
function normalizeGroupId2(value) {
|
|
821
|
-
|
|
822
|
-
const wrapped = raw.match(/^\[_?([\s\S]+?)_?\]$/);
|
|
823
|
-
return wrapped ? wrapped[1] : raw || value;
|
|
846
|
+
return String(value);
|
|
824
847
|
}
|
|
825
848
|
__name(normalizeGroupId2, "normalizeGroupId");
|
|
826
849
|
var _Recorder = class _Recorder {
|
|
@@ -834,6 +857,12 @@ var _Recorder = class _Recorder {
|
|
|
834
857
|
}
|
|
835
858
|
start() {
|
|
836
859
|
this.ctx.on("internal/session", (session) => {
|
|
860
|
+
if (session.type === "message-deleted") {
|
|
861
|
+
void this.handleMessageDeleted(session).catch((error) => {
|
|
862
|
+
this.logger.warn("处理消息撤回失败:", error);
|
|
863
|
+
});
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
837
866
|
void this.handleSession(session);
|
|
838
867
|
});
|
|
839
868
|
}
|
|
@@ -844,8 +873,8 @@ var _Recorder = class _Recorder {
|
|
|
844
873
|
}
|
|
845
874
|
async handleSession(session) {
|
|
846
875
|
const platform = session.platform || "unknown";
|
|
847
|
-
if (this.isBlocked(platform)) return;
|
|
848
876
|
if (!MESSAGE_TYPES.has(session.type)) return;
|
|
877
|
+
if (this.isBlocked(platform)) return;
|
|
849
878
|
const event = session.toJSON();
|
|
850
879
|
const message = event.message;
|
|
851
880
|
const record = {
|
|
@@ -866,15 +895,29 @@ var _Recorder = class _Recorder {
|
|
|
866
895
|
};
|
|
867
896
|
try {
|
|
868
897
|
await this.database.appendMessage(record);
|
|
869
|
-
void this.cacheMessageContacts(event).catch((error) => {
|
|
870
|
-
this.logger.warn("缓存消息联系人失败:", error);
|
|
871
|
-
});
|
|
872
|
-
void this.cacheMessageMedia(event).catch((error) => {
|
|
873
|
-
this.logger.warn("异步缓存消息媒体失败:", error);
|
|
874
|
-
});
|
|
875
898
|
} catch (error) {
|
|
876
899
|
this.logger.warn("写入历史消息失败:", error);
|
|
877
900
|
}
|
|
901
|
+
void this.cacheMessageContacts(event).catch((error) => {
|
|
902
|
+
this.logger.warn("缓存消息联系人失败:", error);
|
|
903
|
+
});
|
|
904
|
+
void this.cacheMessageMedia(event).catch((error) => {
|
|
905
|
+
this.logger.warn("异步缓存消息媒体失败:", error);
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
async handleMessageDeleted(session) {
|
|
909
|
+
const platform = session.platform || "unknown";
|
|
910
|
+
if (this.isBlocked(platform)) return;
|
|
911
|
+
const event = session.toJSON();
|
|
912
|
+
const rawMessage = typeof event.message === "object" && event.message !== null ? event.message : {};
|
|
913
|
+
const messageId = session.messageId || String(rawMessage.id ?? "");
|
|
914
|
+
const channelId = String(
|
|
915
|
+
event.channel?.id || event.guild?.id || session.channelId || ""
|
|
916
|
+
);
|
|
917
|
+
if (!messageId || !channelId) return;
|
|
918
|
+
const patch = { revoked: true, revokedAt: Date.now() };
|
|
919
|
+
await this.database.updateMessageRevoked(platform, session.selfId, channelId, messageId, patch);
|
|
920
|
+
await this.database.updateSelfMessageByMessageId(platform, session.selfId, channelId, messageId, patch);
|
|
878
921
|
}
|
|
879
922
|
async cacheMessageContacts(event) {
|
|
880
923
|
const platform = event.platform || "";
|
|
@@ -887,8 +930,8 @@ var _Recorder = class _Recorder {
|
|
|
887
930
|
const guildId = guild?.id || "";
|
|
888
931
|
const channelId = channel?.id || "";
|
|
889
932
|
const channelType = String(channel?.type ?? "");
|
|
890
|
-
const
|
|
891
|
-
const groupId = guildId || (
|
|
933
|
+
const isPrivateChannel = channelType === "1" || ["direct", "private"].includes(channelType.toLowerCase());
|
|
934
|
+
const groupId = guildId || (isPrivateChannel ? "" : normalizeGroupId2(channelId)) || "";
|
|
892
935
|
const userName = user?.name || user?.nick || member?.nick || member?.name || "";
|
|
893
936
|
const userAvatar = user?.avatar || member?.avatar || "";
|
|
894
937
|
const groupName = guild?.name || channel?.name || "";
|
|
@@ -947,24 +990,10 @@ var _Recorder = class _Recorder {
|
|
|
947
990
|
}
|
|
948
991
|
}
|
|
949
992
|
async cacheMessageMedia(event) {
|
|
950
|
-
const elements = Array.isArray(event.message?.elements) ? event.message.elements : [];
|
|
951
993
|
const channelId = String(
|
|
952
994
|
event.channel?.id || event.guild?.id || ""
|
|
953
995
|
);
|
|
954
|
-
|
|
955
|
-
const collect = /* @__PURE__ */ __name((list) => {
|
|
956
|
-
for (const raw of list) {
|
|
957
|
-
const element = typeof raw === "object" && raw !== null ? raw : {};
|
|
958
|
-
const attrs = typeof element.attrs === "object" && element.attrs !== null ? element.attrs : {};
|
|
959
|
-
if (["img", "image", "mface", "audio", "video", "file"].includes(String(element.type))) {
|
|
960
|
-
const url = String(attrs.src ?? attrs.url ?? attrs.file ?? "");
|
|
961
|
-
if (url) urls.push(url);
|
|
962
|
-
}
|
|
963
|
-
if (Array.isArray(element.children)) collect(element.children);
|
|
964
|
-
}
|
|
965
|
-
}, "collect");
|
|
966
|
-
collect(elements);
|
|
967
|
-
await Promise.allSettled(urls.map((url) => this.media.cacheUrl(url, channelId)));
|
|
996
|
+
await this.media.cacheMessageMedia(event.message, channelId);
|
|
968
997
|
}
|
|
969
998
|
};
|
|
970
999
|
__name(_Recorder, "Recorder");
|
|
@@ -993,6 +1022,68 @@ function toLocalFilePath(value) {
|
|
|
993
1022
|
return null;
|
|
994
1023
|
}
|
|
995
1024
|
__name(toLocalFilePath, "toLocalFilePath");
|
|
1025
|
+
var MEDIA_TYPES = /* @__PURE__ */ new Set([
|
|
1026
|
+
"img",
|
|
1027
|
+
"image",
|
|
1028
|
+
"face",
|
|
1029
|
+
"mface",
|
|
1030
|
+
"audio",
|
|
1031
|
+
"record",
|
|
1032
|
+
"video",
|
|
1033
|
+
"file"
|
|
1034
|
+
]);
|
|
1035
|
+
function isCacheableMediaUrl(value) {
|
|
1036
|
+
const url = String(value ?? "").trim();
|
|
1037
|
+
if (!url || url.startsWith("data:") || url.startsWith("blob:") || url.startsWith("base64://")) return "";
|
|
1038
|
+
return url;
|
|
1039
|
+
}
|
|
1040
|
+
__name(isCacheableMediaUrl, "isCacheableMediaUrl");
|
|
1041
|
+
function isUrlLike(value) {
|
|
1042
|
+
return /^(?:https?:\/\/|file:\/\/|[a-z]:[\\/]|\\\\)/i.test(value);
|
|
1043
|
+
}
|
|
1044
|
+
__name(isUrlLike, "isUrlLike");
|
|
1045
|
+
function decodeMarkupValue(value) {
|
|
1046
|
+
return value.replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
1047
|
+
}
|
|
1048
|
+
__name(decodeMarkupValue, "decodeMarkupValue");
|
|
1049
|
+
function collectMarkupMediaUrls(markup, result) {
|
|
1050
|
+
const pattern = /<(?:img|image|face|mface|audio|record|video|file)\b[^>]*?\b(?:src|url|file|href)\s*=\s*(["'])([\s\S]*?)\1/gi;
|
|
1051
|
+
let match;
|
|
1052
|
+
while ((match = pattern.exec(markup)) !== null) {
|
|
1053
|
+
const url = isCacheableMediaUrl(decodeMarkupValue(match[2]));
|
|
1054
|
+
if (url) result.add(url);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
__name(collectMarkupMediaUrls, "collectMarkupMediaUrls");
|
|
1058
|
+
function collectMediaUrls(source, result) {
|
|
1059
|
+
if (typeof source === "string") {
|
|
1060
|
+
const direct = isCacheableMediaUrl(source);
|
|
1061
|
+
if (direct && isUrlLike(direct)) result.add(direct);
|
|
1062
|
+
collectMarkupMediaUrls(source, result);
|
|
1063
|
+
try {
|
|
1064
|
+
collectMediaUrls(JSON.parse(source), result);
|
|
1065
|
+
} catch {
|
|
1066
|
+
}
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
if (Array.isArray(source)) {
|
|
1070
|
+
for (const item of source) collectMediaUrls(item, result);
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
if (typeof source !== "object" || source === null) return;
|
|
1074
|
+
const record = source;
|
|
1075
|
+
const attrs = typeof record.attrs === "object" && record.attrs !== null ? record.attrs : {};
|
|
1076
|
+
if (MEDIA_TYPES.has(String(record.type ?? ""))) {
|
|
1077
|
+
for (const key of ["src", "url", "file", "href", "path"]) {
|
|
1078
|
+
const url = isCacheableMediaUrl(attrs[key]);
|
|
1079
|
+
if (url) result.add(url);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
for (const key of ["elements", "children", "content", "data", "html", "message"]) {
|
|
1083
|
+
collectMediaUrls(attrs[key] ?? record[key], result);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
__name(collectMediaUrls, "collectMediaUrls");
|
|
996
1087
|
var _MediaManager = class _MediaManager {
|
|
997
1088
|
constructor(ctx, config, database, logger) {
|
|
998
1089
|
this.ctx = ctx;
|
|
@@ -1062,6 +1153,15 @@ var _MediaManager = class _MediaManager {
|
|
|
1062
1153
|
dispose() {
|
|
1063
1154
|
this.cleanupTimer?.();
|
|
1064
1155
|
}
|
|
1156
|
+
async clearAll() {
|
|
1157
|
+
const root = import_node_path2.default.resolve(this.ctx.baseDir, "data", "chat-patch");
|
|
1158
|
+
for (const dir of [this.mediaDir, this.uploadDir]) {
|
|
1159
|
+
const resolved = import_node_path2.default.resolve(dir);
|
|
1160
|
+
if (resolved !== root && !resolved.startsWith(`${root}${import_node_path2.default.sep}`)) continue;
|
|
1161
|
+
await import_node_fs.promises.rm(resolved, { recursive: true, force: true });
|
|
1162
|
+
}
|
|
1163
|
+
this.logger.logInfo("媒体缓存目录已全部清空");
|
|
1164
|
+
}
|
|
1065
1165
|
async cacheUrl(url, channelId = "") {
|
|
1066
1166
|
try {
|
|
1067
1167
|
const filePath = await this.resolveAndDownload(url, channelId);
|
|
@@ -1074,6 +1174,16 @@ var _MediaManager = class _MediaManager {
|
|
|
1074
1174
|
return null;
|
|
1075
1175
|
}
|
|
1076
1176
|
}
|
|
1177
|
+
// 收到或发送消息时立即预缓存富媒体,避免等前端渲染时原 URL 已过期
|
|
1178
|
+
async cacheMessageMedia(source, channelId = "") {
|
|
1179
|
+
const urls = /* @__PURE__ */ new Set();
|
|
1180
|
+
collectMediaUrls(source, urls);
|
|
1181
|
+
await Promise.allSettled([...urls].map((url) => this.cacheUrl(url, channelId)));
|
|
1182
|
+
if (urls.size) {
|
|
1183
|
+
this.logger.logInfo("消息媒体已预缓存:", urls.size, "个 URL");
|
|
1184
|
+
}
|
|
1185
|
+
return [...urls];
|
|
1186
|
+
}
|
|
1077
1187
|
async resolveAndDownload(url, channelId) {
|
|
1078
1188
|
try {
|
|
1079
1189
|
await import_node_fs.promises.mkdir(this.mediaDir, { recursive: true });
|
|
@@ -1251,6 +1361,11 @@ function getString2(value) {
|
|
|
1251
1361
|
return typeof value === "string" ? value : "";
|
|
1252
1362
|
}
|
|
1253
1363
|
__name(getString2, "getString");
|
|
1364
|
+
function getMessageId(value) {
|
|
1365
|
+
const raw = getObject(value).id ?? getObject(value).message_id;
|
|
1366
|
+
return raw == null ? "" : String(raw);
|
|
1367
|
+
}
|
|
1368
|
+
__name(getMessageId, "getMessageId");
|
|
1254
1369
|
function getNumber(value) {
|
|
1255
1370
|
const num = Number(value);
|
|
1256
1371
|
return Number.isFinite(num) ? num : 0;
|
|
@@ -1262,7 +1377,8 @@ function getObject(value) {
|
|
|
1262
1377
|
__name(getObject, "getObject");
|
|
1263
1378
|
function toSatoriElement(value) {
|
|
1264
1379
|
const raw = getObject(value);
|
|
1265
|
-
const
|
|
1380
|
+
const rawAttrs = raw.attrs ?? raw.data;
|
|
1381
|
+
const attrs = typeof rawAttrs === "object" && rawAttrs !== null ? rawAttrs : {};
|
|
1266
1382
|
const children = Array.isArray(raw.children) ? raw.children.map(toSatoriElement) : [];
|
|
1267
1383
|
return {
|
|
1268
1384
|
type: raw.type,
|
|
@@ -1275,10 +1391,6 @@ function isForwardContainer(element) {
|
|
|
1275
1391
|
return element.type === "figure" || element.type === "forward" || element.type === "message" && ("forward" in (element.attrs ?? {}) || element.attrs?.forward === true);
|
|
1276
1392
|
}
|
|
1277
1393
|
__name(isForwardContainer, "isForwardContainer");
|
|
1278
|
-
function isPrivateChannel(value) {
|
|
1279
|
-
return /^(?:private|direct):/i.test(value);
|
|
1280
|
-
}
|
|
1281
|
-
__name(isPrivateChannel, "isPrivateChannel");
|
|
1282
1394
|
function toSegments(elements, resolveI18n) {
|
|
1283
1395
|
const result = [];
|
|
1284
1396
|
for (const raw of elements) {
|
|
@@ -1286,9 +1398,9 @@ function toSegments(elements, resolveI18n) {
|
|
|
1286
1398
|
const attrs = element.attrs ?? {};
|
|
1287
1399
|
const type = getString2(element.type);
|
|
1288
1400
|
if (type === "text") {
|
|
1289
|
-
result.push({ type: "text", text: getString2(attrs.content) });
|
|
1401
|
+
result.push({ type: "text", text: getString2(attrs.content) || getString2(attrs.text) });
|
|
1290
1402
|
} else if (type === "at") {
|
|
1291
|
-
const id = getString2(attrs.id);
|
|
1403
|
+
const id = getString2(attrs.id) || getString2(attrs.qq);
|
|
1292
1404
|
const name2 = getString2(attrs.name) || (attrs.type === "all" ? "所有人" : id);
|
|
1293
1405
|
result.push({ type: "at", qq: id, text: name2.startsWith("@") ? name2 : `@${name2}` });
|
|
1294
1406
|
} else if (type === "img" || type === "image") {
|
|
@@ -1400,10 +1512,11 @@ function forwardId(elements) {
|
|
|
1400
1512
|
}
|
|
1401
1513
|
__name(forwardId, "forwardId");
|
|
1402
1514
|
var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
1403
|
-
constructor(ctx, config, database, logger) {
|
|
1515
|
+
constructor(ctx, config, database, media, logger) {
|
|
1404
1516
|
this.ctx = ctx;
|
|
1405
1517
|
this.config = config;
|
|
1406
1518
|
this.database = database;
|
|
1519
|
+
this.media = media;
|
|
1407
1520
|
this.logger = logger;
|
|
1408
1521
|
this.wrapped = /* @__PURE__ */ new Map();
|
|
1409
1522
|
this.sequence = 0;
|
|
@@ -1434,7 +1547,8 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1434
1547
|
const methods = {
|
|
1435
1548
|
createMessage: bot.createMessage,
|
|
1436
1549
|
sendMessage: bot.sendMessage,
|
|
1437
|
-
sendPrivateMessage: bot.sendPrivateMessage
|
|
1550
|
+
sendPrivateMessage: bot.sendPrivateMessage,
|
|
1551
|
+
deleteMessage: bot.deleteMessage
|
|
1438
1552
|
};
|
|
1439
1553
|
this.wrapped.set(bot, methods);
|
|
1440
1554
|
bot.createMessage = async (channelId, content, referrer, options) => {
|
|
@@ -1446,13 +1560,17 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1446
1560
|
sendOptions.chatPatchId = localId;
|
|
1447
1561
|
try {
|
|
1448
1562
|
const messages = await methods.createMessage.call(bot, channelId, content, referrer, sendOptions);
|
|
1449
|
-
const ids = Array.isArray(messages) ? messages.map((item) =>
|
|
1563
|
+
const ids = Array.isArray(messages) ? messages.map((item) => getMessageId(item)).filter((id) => id !== "") : [];
|
|
1450
1564
|
await this.recordFromWrapper(bot, channelId, content, ids, options, "create", localId);
|
|
1451
1565
|
return messages;
|
|
1452
1566
|
} finally {
|
|
1453
1567
|
delete sendOptions.chatPatchId;
|
|
1454
1568
|
}
|
|
1455
1569
|
};
|
|
1570
|
+
bot.deleteMessage = async (channelId, messageId) => {
|
|
1571
|
+
await methods.deleteMessage.call(bot, channelId, messageId);
|
|
1572
|
+
await this.markSelfMessageRevoked(bot, channelId, messageId);
|
|
1573
|
+
};
|
|
1456
1574
|
bot.sendMessage = async (channelId, content, referrer, options) => {
|
|
1457
1575
|
const sendOptions = this.prepareOptions(options);
|
|
1458
1576
|
if (sendOptions.chatPatchId) {
|
|
@@ -1461,7 +1579,7 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1461
1579
|
const localId = (0, import_node_crypto3.randomUUID)();
|
|
1462
1580
|
sendOptions.chatPatchId = localId;
|
|
1463
1581
|
try {
|
|
1464
|
-
const ids = await methods.sendMessage.call(bot, channelId, content, referrer, sendOptions);
|
|
1582
|
+
const ids = (await methods.sendMessage.call(bot, channelId, content, referrer, sendOptions)).filter((id) => id !== "");
|
|
1465
1583
|
await this.recordFromWrapper(bot, channelId, content, ids, options, "send", localId);
|
|
1466
1584
|
return ids;
|
|
1467
1585
|
} finally {
|
|
@@ -1476,7 +1594,7 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1476
1594
|
const localId = (0, import_node_crypto3.randomUUID)();
|
|
1477
1595
|
sendOptions.chatPatchId = localId;
|
|
1478
1596
|
try {
|
|
1479
|
-
const ids = await methods.sendPrivateMessage.call(bot, userId, content, guildId, sendOptions);
|
|
1597
|
+
const ids = (await methods.sendPrivateMessage.call(bot, userId, content, guildId, sendOptions)).filter((id) => id !== "");
|
|
1480
1598
|
await this.recordFromWrapper(bot, userId, content, ids, options, "private", localId);
|
|
1481
1599
|
return ids;
|
|
1482
1600
|
} finally {
|
|
@@ -1490,6 +1608,7 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1490
1608
|
bot.createMessage = methods.createMessage;
|
|
1491
1609
|
bot.sendMessage = methods.sendMessage;
|
|
1492
1610
|
bot.sendPrivateMessage = methods.sendPrivateMessage;
|
|
1611
|
+
bot.deleteMessage = methods.deleteMessage;
|
|
1493
1612
|
this.wrapped.delete(bot);
|
|
1494
1613
|
}
|
|
1495
1614
|
prepareOptions(options) {
|
|
@@ -1497,6 +1616,10 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1497
1616
|
}
|
|
1498
1617
|
async recordFromWrapper(bot, channelId, content, ids, options, mode, localId) {
|
|
1499
1618
|
if (!channelId || this.isBlocked(bot.platform ?? "")) return;
|
|
1619
|
+
if (!ids.length) {
|
|
1620
|
+
this.logger.warn("发送未返回消息 ID,判定为发送失败:", bot.platform, channelId);
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1500
1623
|
const source = options?.session ? "plugin" : "bot";
|
|
1501
1624
|
const record = this.buildRecord(
|
|
1502
1625
|
bot.platform ?? "",
|
|
@@ -1510,8 +1633,24 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1510
1633
|
localId
|
|
1511
1634
|
);
|
|
1512
1635
|
await this.database.upsertSelfMessage(record);
|
|
1636
|
+
void this.media.cacheMessageMedia(content, channelId).catch((error) => {
|
|
1637
|
+
this.logger.warn("缓存机器人消息媒体失败:", error);
|
|
1638
|
+
});
|
|
1513
1639
|
this.logger.logInfo("机器人消息已记录:", record.kind, record.channelId, record.messageId || record.id);
|
|
1514
1640
|
}
|
|
1641
|
+
async markSelfMessageRevoked(bot, channelId, messageId) {
|
|
1642
|
+
if (!messageId || !channelId || this.isBlocked(bot.platform ?? "")) return;
|
|
1643
|
+
const updated = await this.database.updateSelfMessageByMessageId(
|
|
1644
|
+
bot.platform ?? "",
|
|
1645
|
+
bot.selfId,
|
|
1646
|
+
channelId,
|
|
1647
|
+
messageId,
|
|
1648
|
+
{ revoked: true, revokedAt: Date.now() }
|
|
1649
|
+
);
|
|
1650
|
+
if (!updated) {
|
|
1651
|
+
this.logger.warn("撤回消息未找到本地自消息记录:", bot.platform, channelId, messageId);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1515
1654
|
// 收到 send 事件时只补消息 id,避免重复记录
|
|
1516
1655
|
async recordSendEvent(session) {
|
|
1517
1656
|
if (session.type !== "send") return;
|
|
@@ -1522,7 +1661,7 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1522
1661
|
const eventMessage = getObject(event.message);
|
|
1523
1662
|
const channelId = session.channelId || getString2(eventChannel.id);
|
|
1524
1663
|
if (this.isBlocked(platform) || !platform || !selfId || !channelId) return;
|
|
1525
|
-
const messageId = session.messageId ||
|
|
1664
|
+
const messageId = session.messageId || getMessageId(eventMessage);
|
|
1526
1665
|
if (messageId) {
|
|
1527
1666
|
const updated = await this.database.updateSelfMessageByMessageId(
|
|
1528
1667
|
platform,
|
|
@@ -1547,6 +1686,9 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1547
1686
|
(0, import_node_crypto3.randomUUID)()
|
|
1548
1687
|
);
|
|
1549
1688
|
await this.database.upsertSelfMessage(record);
|
|
1689
|
+
void this.media.cacheMessageMedia(session.content ?? "", channelId).catch((error) => {
|
|
1690
|
+
this.logger.warn("缓存 send 事件媒体失败:", error);
|
|
1691
|
+
});
|
|
1550
1692
|
}
|
|
1551
1693
|
resolveI18nElement(attrs) {
|
|
1552
1694
|
const path5 = getString2(attrs.path);
|
|
@@ -1562,8 +1704,8 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1562
1704
|
return `[${path5}]`;
|
|
1563
1705
|
}
|
|
1564
1706
|
buildRecord(platform, selfId, channelId, guildId, content, messageId, source, mode, localId) {
|
|
1565
|
-
const normalizedChannelId =
|
|
1566
|
-
const channelType = mode === "private"
|
|
1707
|
+
const normalizedChannelId = channelId;
|
|
1708
|
+
const channelType = mode === "private" ? "user" : "group";
|
|
1567
1709
|
const elements = this.normalizeElements(content);
|
|
1568
1710
|
const contentText = typeof content === "string" ? content : import_koishi.h.toElementArray(content).join("");
|
|
1569
1711
|
const fingerprint = (0, import_node_crypto3.createHash)("sha256").update([
|
|
@@ -1594,7 +1736,14 @@ var _SelfMessageRecorder = class _SelfMessageRecorder {
|
|
|
1594
1736
|
}
|
|
1595
1737
|
normalizeElements(content) {
|
|
1596
1738
|
try {
|
|
1597
|
-
const
|
|
1739
|
+
const normalized = typeof content === "string" ? content : Array.isArray(content) ? content.map((raw) => {
|
|
1740
|
+
const item = getObject(raw);
|
|
1741
|
+
if (typeof item.data === "object" && item.data !== null && item.attrs === void 0) {
|
|
1742
|
+
return (0, import_koishi.h)(getString2(item.type), item.data);
|
|
1743
|
+
}
|
|
1744
|
+
return raw;
|
|
1745
|
+
}) : content;
|
|
1746
|
+
const elements = typeof normalized === "string" ? import_koishi.h.parse(normalized) : import_koishi.h.toElementArray(normalized);
|
|
1598
1747
|
return JSON.parse(JSON.stringify(elements));
|
|
1599
1748
|
} catch {
|
|
1600
1749
|
return [];
|
|
@@ -1622,7 +1771,8 @@ function registerBootstrap(ctx, config, database, logger) {
|
|
|
1622
1771
|
return {
|
|
1623
1772
|
endpoint: resolveEndpoint(ctx, config),
|
|
1624
1773
|
token: ctx.satori?.server?.config?.token ?? "",
|
|
1625
|
-
basePath: config.basePath
|
|
1774
|
+
basePath: config.basePath,
|
|
1775
|
+
blockedPlatforms: config.blockedPlatforms ?? []
|
|
1626
1776
|
};
|
|
1627
1777
|
}, { authority: 4 });
|
|
1628
1778
|
ctx.console.addListener("chat-patch/history", async (query) => {
|
|
@@ -1667,7 +1817,7 @@ var import_node_path3 = __toESM(require("node:path"));
|
|
|
1667
1817
|
var import_node_url2 = require("node:url");
|
|
1668
1818
|
var import_koa_send = __toESM(require("koa-send"));
|
|
1669
1819
|
var import_file_type2 = __toESM(require("file-type"));
|
|
1670
|
-
function registerWeb(ctx, config, database, contactCache, logger) {
|
|
1820
|
+
function registerWeb(ctx, config, database, contactCache, media, logger) {
|
|
1671
1821
|
const webRoot = import_node_path3.default.resolve(__dirname, "..", "client", "web", "dist");
|
|
1672
1822
|
const webSource = import_node_path3.default.resolve(__dirname, "..", "client", "web", "src");
|
|
1673
1823
|
const webPublic = import_node_path3.default.resolve(__dirname, "..", "client", "web", "public");
|
|
@@ -1706,34 +1856,21 @@ function registerWeb(ctx, config, database, contactCache, logger) {
|
|
|
1706
1856
|
koa.status = 404;
|
|
1707
1857
|
koa.body = "";
|
|
1708
1858
|
}, "servePublicFile");
|
|
1709
|
-
const
|
|
1710
|
-
ctx.server.get(pattern, async (koa
|
|
1711
|
-
if (!getVite()) return next();
|
|
1859
|
+
const registerPublic = /* @__PURE__ */ __name((pattern, prefix) => {
|
|
1860
|
+
ctx.server.get(pattern, async (koa) => {
|
|
1712
1861
|
await servePublicFile(koa, prefix);
|
|
1713
1862
|
});
|
|
1714
|
-
}, "
|
|
1715
|
-
|
|
1863
|
+
}, "registerPublic");
|
|
1864
|
+
registerPublic("/img(.*)", "img");
|
|
1716
1865
|
const toExtension2 = /* @__PURE__ */ __name((value) => {
|
|
1717
1866
|
const ext = value.startsWith(".") ? value : `.${value}`;
|
|
1718
1867
|
return ext.toLowerCase();
|
|
1719
1868
|
}, "toExtension");
|
|
1720
1869
|
const normalizeGroupId3 = /* @__PURE__ */ __name((value) => {
|
|
1721
|
-
|
|
1722
|
-
const wrapped = raw.match(/^\[_?([\s\S]+?)_?\]$/);
|
|
1723
|
-
return wrapped ? wrapped[1] : raw || value;
|
|
1870
|
+
return String(value);
|
|
1724
1871
|
}, "normalizeGroupId");
|
|
1725
1872
|
const historyChannelCandidates = /* @__PURE__ */ __name((channelId) => {
|
|
1726
|
-
|
|
1727
|
-
const candidates = [channelId];
|
|
1728
|
-
if (raw && raw !== channelId) candidates.push(raw);
|
|
1729
|
-
const id = raw || channelId;
|
|
1730
|
-
if (!/^(?:group|room|chat|channel|guild|private):/i.test(channelId)) {
|
|
1731
|
-
candidates.push(`group:${id}`, `private:${id}`);
|
|
1732
|
-
}
|
|
1733
|
-
if (id) {
|
|
1734
|
-
candidates.push(` [_${id}_] `, `_${id}_`, `[${id}]`, `group:${id}`);
|
|
1735
|
-
}
|
|
1736
|
-
return [...new Set(candidates)];
|
|
1873
|
+
return channelId ? [channelId] : [];
|
|
1737
1874
|
}, "historyChannelCandidates");
|
|
1738
1875
|
const mimeToExt = {
|
|
1739
1876
|
"audio/mpeg": ".mp3",
|
|
@@ -1973,7 +2110,7 @@ function registerWeb(ctx, config, database, contactCache, logger) {
|
|
|
1973
2110
|
channelId: String(body.channelId ?? ""),
|
|
1974
2111
|
guildId: typeof body.guildId === "string" ? body.guildId : void 0,
|
|
1975
2112
|
channelType: body.channelType === "user" ? "user" : "group",
|
|
1976
|
-
messageId: typeof body.messageId === "string" ? body.messageId : void 0,
|
|
2113
|
+
messageId: typeof body.messageId === "string" || typeof body.messageId === "number" ? String(body.messageId) : void 0,
|
|
1977
2114
|
content: typeof body.content === "string" ? body.content : void 0,
|
|
1978
2115
|
elements: Array.isArray(body.elements) ? body.elements : void 0,
|
|
1979
2116
|
message: Array.isArray(body.message) ? body.message : void 0,
|
|
@@ -1982,7 +2119,9 @@ function registerWeb(ctx, config, database, contactCache, logger) {
|
|
|
1982
2119
|
sentAt: typeof body.sentAt === "number" && Number.isFinite(body.sentAt) ? body.sentAt : Date.now(),
|
|
1983
2120
|
sequence: typeof body.sequence === "number" && Number.isFinite(body.sequence) ? body.sequence : 0,
|
|
1984
2121
|
source: body.source === "plugin" ? "plugin" : "webui",
|
|
1985
|
-
kind: typeof body.kind === "string" ? body.kind : "text"
|
|
2122
|
+
kind: typeof body.kind === "string" ? body.kind : "text",
|
|
2123
|
+
revoked: body.revoked === true,
|
|
2124
|
+
revokedAt: typeof body.revokedAt === "number" && Number.isFinite(body.revokedAt) ? body.revokedAt : void 0
|
|
1986
2125
|
};
|
|
1987
2126
|
if (!payload.platform || !payload.selfId || !payload.channelId) {
|
|
1988
2127
|
koa.status = 400;
|
|
@@ -2005,7 +2144,9 @@ function registerWeb(ctx, config, database, contactCache, logger) {
|
|
|
2005
2144
|
sentAt: payload.sentAt ?? Date.now(),
|
|
2006
2145
|
sequence: payload.sequence ?? 0,
|
|
2007
2146
|
source: payload.source ?? "webui",
|
|
2008
|
-
kind: payload.kind ?? "text"
|
|
2147
|
+
kind: payload.kind ?? "text",
|
|
2148
|
+
revoked: payload.revoked,
|
|
2149
|
+
revokedAt: payload.revokedAt
|
|
2009
2150
|
};
|
|
2010
2151
|
await database.upsertSelfMessage(record);
|
|
2011
2152
|
koa.body = { ok: true, message: record };
|
|
@@ -2236,6 +2377,17 @@ function registerWeb(ctx, config, database, contactCache, logger) {
|
|
|
2236
2377
|
contacts: await database.getContacts(platform, selfId, normalizedType)
|
|
2237
2378
|
};
|
|
2238
2379
|
});
|
|
2380
|
+
ctx.server.post(`${config.basePath}/api/clear-all`, async (koa) => {
|
|
2381
|
+
try {
|
|
2382
|
+
await database.clearAll();
|
|
2383
|
+
await media.clearAll();
|
|
2384
|
+
koa.body = { ok: true };
|
|
2385
|
+
} catch (error) {
|
|
2386
|
+
logger.warn("清空全部缓存失败:", error);
|
|
2387
|
+
koa.status = 500;
|
|
2388
|
+
koa.body = { ok: false, error: String(error) };
|
|
2389
|
+
}
|
|
2390
|
+
});
|
|
2239
2391
|
const serveFile = /* @__PURE__ */ __name(async (koa, fileName) => {
|
|
2240
2392
|
const fullPath = import_node_path3.default.join(webRoot, fileName);
|
|
2241
2393
|
if ((0, import_node_fs2.existsSync)(fullPath) && (0, import_node_fs2.statSync)(fullPath).isFile()) {
|
|
@@ -2283,7 +2435,7 @@ var Config = import_koishi2.Schema.intersect([
|
|
|
2283
2435
|
blockedPlatforms: import_koishi2.Schema.array(import_koishi2.Schema.object({
|
|
2284
2436
|
platformName: import_koishi2.Schema.string(),
|
|
2285
2437
|
exactMatch: import_koishi2.Schema.boolean().default(true)
|
|
2286
|
-
})).role("table").description("
|
|
2438
|
+
})).role("table").description("屏蔽消息接收的平台").default([
|
|
2287
2439
|
{ platformName: "qq", exactMatch: true },
|
|
2288
2440
|
{ platformName: "qqguild", exactMatch: true },
|
|
2289
2441
|
{ platformName: "sandbox", exactMatch: false }
|
|
@@ -2316,18 +2468,18 @@ async function apply(ctx, config) {
|
|
|
2316
2468
|
media.start();
|
|
2317
2469
|
const recorder = new Recorder(ctx, config, database, media, contactCache, pluginLogger);
|
|
2318
2470
|
recorder.start();
|
|
2319
|
-
const selfMessages = new SelfMessageRecorder(ctx, config, database, pluginLogger);
|
|
2471
|
+
const selfMessages = new SelfMessageRecorder(ctx, config, database, media, pluginLogger);
|
|
2320
2472
|
selfMessages.start();
|
|
2321
2473
|
registerBootstrap(ctx, config, database, pluginLogger);
|
|
2322
|
-
registerWeb(ctx, config, database, contactCache, pluginLogger);
|
|
2474
|
+
registerWeb(ctx, config, database, contactCache, media, pluginLogger);
|
|
2323
2475
|
ctx.console.addEntry({
|
|
2324
2476
|
dev: import_node_path4.default.resolve(__dirname, "../client/index.ts"),
|
|
2325
2477
|
prod: import_node_path4.default.resolve(__dirname, "../dist")
|
|
2326
2478
|
});
|
|
2327
|
-
ctx.on("dispose", () => {
|
|
2479
|
+
ctx.on("dispose", async () => {
|
|
2328
2480
|
selfMessages.dispose();
|
|
2329
2481
|
media.dispose();
|
|
2330
|
-
|
|
2482
|
+
await database.dispose();
|
|
2331
2483
|
pluginLogger.logInfo("chat-patch 已卸载");
|
|
2332
2484
|
});
|
|
2333
2485
|
}
|
package/lib/media.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { Config } from './config';
|
|
3
|
+
import { ChatDatabase } from './database';
|
|
4
|
+
import { PluginLogger } from './logger';
|
|
5
|
+
export declare class MediaManager {
|
|
6
|
+
private ctx;
|
|
7
|
+
private config;
|
|
8
|
+
private database;
|
|
9
|
+
private logger;
|
|
10
|
+
private mediaDir;
|
|
11
|
+
private uploadDir;
|
|
12
|
+
private cleanupTimer?;
|
|
13
|
+
constructor(ctx: Context, config: Config, database: ChatDatabase, logger: PluginLogger);
|
|
14
|
+
start(): void;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
clearAll(): Promise<void>;
|
|
17
|
+
cacheUrl(url: string, channelId?: string): Promise<string | null>;
|
|
18
|
+
cacheMessageMedia(source: unknown, channelId?: string): Promise<string[]>;
|
|
19
|
+
private resolveAndDownload;
|
|
20
|
+
private detectExtension;
|
|
21
|
+
private migrateCachedMedia;
|
|
22
|
+
private cleanup;
|
|
23
|
+
private exists;
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { Config } from './config';
|
|
3
|
+
import { ContactCacheService } from './cache';
|
|
4
|
+
import { ChatDatabase } from './database';
|
|
5
|
+
import { MediaManager } from './media';
|
|
6
|
+
import { PluginLogger } from './logger';
|
|
7
|
+
export declare class Recorder {
|
|
8
|
+
private ctx;
|
|
9
|
+
private config;
|
|
10
|
+
private database;
|
|
11
|
+
private media;
|
|
12
|
+
private contactCache;
|
|
13
|
+
private logger;
|
|
14
|
+
constructor(ctx: Context, config: Config, database: ChatDatabase, media: MediaManager, contactCache: ContactCacheService, logger: PluginLogger);
|
|
15
|
+
start(): void;
|
|
16
|
+
private isBlocked;
|
|
17
|
+
private handleSession;
|
|
18
|
+
private handleMessageDeleted;
|
|
19
|
+
private cacheMessageContacts;
|
|
20
|
+
private cacheMessageMedia;
|
|
21
|
+
}
|