koishi-plugin-message-dedup 0.0.1 → 0.0.2
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/README.md +3 -1
- package/assets/dup-1.jpg +0 -0
- package/lib/config.js +2 -2
- package/lib/index.js +98 -6
- package/package.json +1 -1
package/README.md
CHANGED
package/assets/dup-1.jpg
CHANGED
|
Binary file
|
package/lib/config.js
CHANGED
|
@@ -4,8 +4,8 @@ exports.Config = void 0;
|
|
|
4
4
|
const koishi_1 = require("koishi");
|
|
5
5
|
exports.Config = koishi_1.Schema.object({
|
|
6
6
|
enableImage: koishi_1.Schema.boolean()
|
|
7
|
-
.default(
|
|
8
|
-
.description('
|
|
7
|
+
.default(true)
|
|
8
|
+
.description('启用图片去重(自动排除表情包)'),
|
|
9
9
|
enableLink: koishi_1.Schema.boolean()
|
|
10
10
|
.default(true)
|
|
11
11
|
.description('启用链接去重'),
|
package/lib/index.js
CHANGED
|
@@ -94,10 +94,18 @@ async function processMessage(session, config, ctx, logger) {
|
|
|
94
94
|
return null;
|
|
95
95
|
const username = session.author?.nickname || session.author?.username || session.username || '未知用户';
|
|
96
96
|
const originalContent = session.content || extractTextFromElements(elements) || '';
|
|
97
|
-
// 1.
|
|
97
|
+
// 1. 检查图片(排除表情包:subType=1)
|
|
98
98
|
if (config.enableImage) {
|
|
99
99
|
for (const elem of elements) {
|
|
100
100
|
if (elem.type === 'img' && elem.attrs?.src) {
|
|
101
|
+
// 表情包 subType 为 1,跳过
|
|
102
|
+
const subType = elem.attrs['sub-type'] ?? elem.attrs.subType;
|
|
103
|
+
if (subType === 1 || subType === '1') {
|
|
104
|
+
if (config.debug) {
|
|
105
|
+
logger.info('跳过表情包');
|
|
106
|
+
}
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
101
109
|
const duplicate = await processImage(elem.attrs.src, session, username, originalContent, config, ctx, logger);
|
|
102
110
|
if (duplicate)
|
|
103
111
|
return duplicate;
|
|
@@ -116,6 +124,9 @@ async function processMessage(session, config, ctx, logger) {
|
|
|
116
124
|
}
|
|
117
125
|
// 3. 检查转发消息
|
|
118
126
|
if (config.enableForward) {
|
|
127
|
+
if (config.debug) {
|
|
128
|
+
logger.info(`检查转发消息, elements types: ${elements.map(e => e.type).join(', ')}`);
|
|
129
|
+
}
|
|
119
130
|
for (const elem of elements) {
|
|
120
131
|
if (elem.type === 'forward') {
|
|
121
132
|
const duplicate = await processForward(elem, session, username, originalContent, config, ctx, logger);
|
|
@@ -217,14 +228,73 @@ async function processLink(url, session, username, originalContent, config, ctx,
|
|
|
217
228
|
}
|
|
218
229
|
async function processForward(forwardElem, session, username, originalContent, config, ctx, logger) {
|
|
219
230
|
try {
|
|
220
|
-
|
|
221
|
-
|
|
231
|
+
if (config.debug) {
|
|
232
|
+
logger.info(`处理转发消息, elem: ${JSON.stringify(forwardElem, null, 2)}`);
|
|
233
|
+
}
|
|
234
|
+
// 获取转发消息 ID
|
|
235
|
+
const forwardId = forwardElem.attrs?.id;
|
|
236
|
+
if (!forwardId) {
|
|
222
237
|
return null;
|
|
223
238
|
}
|
|
224
|
-
|
|
239
|
+
// 先尝试通过 OneBot API 获取转发消息内容
|
|
240
|
+
let content = '';
|
|
241
|
+
if (session.platform === 'onebot' && session.bot?.internal) {
|
|
242
|
+
const internal = session.bot.internal;
|
|
243
|
+
// 尝试多种 payload 格式
|
|
244
|
+
const payloads = [
|
|
245
|
+
{ message_id: forwardId },
|
|
246
|
+
{ id: forwardId },
|
|
247
|
+
];
|
|
248
|
+
// 尝试多种 API 调用方式
|
|
249
|
+
const callApi = async (action, params) => {
|
|
250
|
+
if (typeof internal._get === 'function') {
|
|
251
|
+
return await internal._get(action, params);
|
|
252
|
+
}
|
|
253
|
+
if (typeof internal.request === 'function') {
|
|
254
|
+
return await internal.request(action, params);
|
|
255
|
+
}
|
|
256
|
+
if (typeof internal.callAction === 'function') {
|
|
257
|
+
return await internal.callAction(action, params);
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
};
|
|
261
|
+
for (const payload of payloads) {
|
|
262
|
+
try {
|
|
263
|
+
const forwardData = await callApi('get_forward_msg', payload);
|
|
264
|
+
if (forwardData) {
|
|
265
|
+
const messages = extractMessagesArray(forwardData);
|
|
266
|
+
if (messages && Array.isArray(messages) && messages.length > 0) {
|
|
267
|
+
content = messages.map((node) => {
|
|
268
|
+
if (node.message) {
|
|
269
|
+
return node.message
|
|
270
|
+
.filter((m) => m.type === 'text')
|
|
271
|
+
.map((m) => m.data?.text || '')
|
|
272
|
+
.join('');
|
|
273
|
+
}
|
|
274
|
+
return '';
|
|
275
|
+
}).join('\n');
|
|
276
|
+
if (config.debug) {
|
|
277
|
+
logger.info(`get_forward_msg成功获取内容, 长度: ${content.length}`);
|
|
278
|
+
}
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
// API 调用失败,继续尝试
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// 如果 API 获取失败,直接用 forwardId 作为去重标识
|
|
289
|
+
// 同一内容的转发消息在不同用户发送时应该有相同的 ID
|
|
290
|
+
const hashSource = content || forwardId;
|
|
291
|
+
if (config.debug) {
|
|
292
|
+
logger.info(`转发消息去重标识: ${hashSource.slice(0, 50)} (来源: ${content ? 'API内容' : 'forwardId'})`);
|
|
293
|
+
}
|
|
294
|
+
const truncated = hashSource.slice(0, config.forwardContentMaxLength);
|
|
225
295
|
const hash = (0, hash_1.calculateStringHash)(truncated);
|
|
226
296
|
if (config.debug) {
|
|
227
|
-
logger.info(`转发消息哈希: ${hash}
|
|
297
|
+
logger.info(`转发消息哈希: ${hash}`);
|
|
228
298
|
}
|
|
229
299
|
const guildId = session.guildId;
|
|
230
300
|
const duplicate = await (0, database_1.findDuplicate)(ctx, guildId, 'forward', hash);
|
|
@@ -243,7 +313,7 @@ async function processForward(forwardElem, session, username, originalContent, c
|
|
|
243
313
|
contentHash: hash,
|
|
244
314
|
originalMessageId: session.messageId,
|
|
245
315
|
originalContent: truncated.slice(0, 100),
|
|
246
|
-
extraInfo: JSON.stringify({ preview: truncated.slice(0, 100) })
|
|
316
|
+
extraInfo: JSON.stringify({ forwardId, preview: truncated.slice(0, 100) })
|
|
247
317
|
});
|
|
248
318
|
return null;
|
|
249
319
|
}
|
|
@@ -269,6 +339,28 @@ function extractForwardContent(elem) {
|
|
|
269
339
|
}
|
|
270
340
|
return content;
|
|
271
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* 从多种可能的数据结构中提取 messages 数组
|
|
344
|
+
* 参考 chatluna-forward-msg 的实现
|
|
345
|
+
*/
|
|
346
|
+
function extractMessagesArray(data) {
|
|
347
|
+
const candidates = [
|
|
348
|
+
data,
|
|
349
|
+
data?.messages,
|
|
350
|
+
data?.data,
|
|
351
|
+
data?.result,
|
|
352
|
+
data?.response,
|
|
353
|
+
data?.data?.messages,
|
|
354
|
+
data?.response?.messages,
|
|
355
|
+
data?.envelope?.result?.messages,
|
|
356
|
+
];
|
|
357
|
+
for (const item of candidates) {
|
|
358
|
+
if (Array.isArray(item)) {
|
|
359
|
+
return item;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
272
364
|
async function sendDuplicateWarning(session, duplicate, config, ctx) {
|
|
273
365
|
const date = new Date(duplicate.timestamp);
|
|
274
366
|
const dateStr = date.toLocaleString('zh-CN', {
|