koishi-plugin-aipo-asbbot 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/chatSend.d.ts +29 -9
- package/lib/index.js +143 -66
- package/package.json +1 -1
package/lib/chatSend.d.ts
CHANGED
|
@@ -24,26 +24,46 @@ export interface ChatSendResult {
|
|
|
24
24
|
}
|
|
25
25
|
/** 调用者头像 + @(QQ 原生 MD 排版通用前缀) */
|
|
26
26
|
export declare function avatarAt(userId: string): string;
|
|
27
|
+
type PassiveSource = {
|
|
28
|
+
type: 'msg_id';
|
|
29
|
+
id: string;
|
|
30
|
+
payload: {
|
|
31
|
+
msg_id: string;
|
|
32
|
+
};
|
|
33
|
+
} | {
|
|
34
|
+
type: 'event_id';
|
|
35
|
+
id: string;
|
|
36
|
+
payload: {
|
|
37
|
+
event_id: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
27
40
|
/** 内容发送模块 */
|
|
28
41
|
export declare const Chat: {
|
|
29
42
|
config: Config;
|
|
30
43
|
ctx: Context;
|
|
31
|
-
chache:
|
|
32
|
-
messageIdTemp:
|
|
44
|
+
chache: Record<string, number>;
|
|
45
|
+
messageIdTemp: Record<string, string>;
|
|
46
|
+
buttonEventIdTemp: Record<string, string>;
|
|
33
47
|
init(config: Config, ctx: Context): Promise<void>;
|
|
34
48
|
/**
|
|
35
49
|
* 发送消息(Markdown 优先,失败自动降级)。
|
|
36
|
-
* @param session Koishi 会话
|
|
37
|
-
* @param content Markdown 内容
|
|
38
|
-
* @param fallbackText 降级用纯文本(不传时自动从 content 提取纯文本,避免把 MD 语法当普通文本发出)
|
|
39
|
-
* @param keyboard QQ 下挂按钮组件(仅 Markdown 模式生效,降级纯文本时忽略)
|
|
40
|
-
* @returns 实际发送模式 { sent: true, mode: 'markdown' | 'text' }
|
|
41
|
-
* @throws 两种模式均发送失败时抛出“消息发送失败”
|
|
42
50
|
*/
|
|
43
51
|
send(session: Session, content: string, fallbackText?: string, keyboard?: QQKeyboard): Promise<ChatSendResult>;
|
|
44
52
|
isUseMd(session: Session): boolean;
|
|
45
|
-
/**
|
|
53
|
+
/** 优先取当前 session 的 msg_id;如果是按钮事件,则取 event_id */
|
|
54
|
+
getCurrentPassiveSource(session: Session): PassiveSource | null;
|
|
55
|
+
/**
|
|
56
|
+
* 超过被动 MD 上限时:
|
|
57
|
+
* 1. 优先找 message 事件中缓存的新 msg_id
|
|
58
|
+
* 2. 没有新 msg_id,再找按钮事件缓存的 event_id
|
|
59
|
+
*/
|
|
60
|
+
getNextPassiveSource(userId: string, current: PassiveSource): PassiveSource | null;
|
|
61
|
+
getCacheKey(source: PassiveSource): string;
|
|
62
|
+
/** 清除一条 msg_id/event_id 计数记录 */
|
|
46
63
|
clearChache(): void;
|
|
47
64
|
/** 清除一条 messageId 缓存 */
|
|
48
65
|
clearMessageIdTemp(): void;
|
|
66
|
+
/** 清除一条按钮 event_id 缓存 */
|
|
67
|
+
clearButtonEventIdTemp(): void;
|
|
49
68
|
};
|
|
69
|
+
export {};
|
package/lib/index.js
CHANGED
|
@@ -202,95 +202,169 @@ var Chat = {
|
|
|
202
202
|
config: {},
|
|
203
203
|
ctx: {},
|
|
204
204
|
chache: {},
|
|
205
|
+
// message 事件缓存:userId -> msg_id
|
|
205
206
|
messageIdTemp: {},
|
|
207
|
+
// 按钮事件缓存:userId -> event_id
|
|
208
|
+
buttonEventIdTemp: {},
|
|
206
209
|
async init(config, ctx) {
|
|
207
210
|
Chat.config = config;
|
|
208
211
|
Chat.ctx = ctx;
|
|
209
212
|
Chat.config.deBug && console.log(Chat.chache);
|
|
210
213
|
ctx.on("message", (session) => {
|
|
211
|
-
if (!
|
|
212
|
-
Chat.messageIdTemp[session.userId] = {};
|
|
213
|
-
}
|
|
214
|
+
if (!session.userId || !session.messageId) return;
|
|
214
215
|
Chat.messageIdTemp[session.userId] = session.messageId;
|
|
215
216
|
Chat.clearMessageIdTemp();
|
|
216
217
|
}, true);
|
|
218
|
+
ctx.on("interaction/button", (session) => {
|
|
219
|
+
const eventId = session.event?._data?.id;
|
|
220
|
+
if (!session.userId || !eventId) {
|
|
221
|
+
Chat.config.deBug && console.log("[Chat] button interaction has no event_id:", session.event?._data);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
Chat.buttonEventIdTemp[session.userId] = eventId;
|
|
225
|
+
Chat.clearButtonEventIdTemp();
|
|
226
|
+
Chat.config.deBug && console.log("[Chat] cached button event_id:", eventId);
|
|
227
|
+
});
|
|
217
228
|
},
|
|
218
229
|
/**
|
|
219
230
|
* 发送消息(Markdown 优先,失败自动降级)。
|
|
220
|
-
* @param session Koishi 会话
|
|
221
|
-
* @param content Markdown 内容
|
|
222
|
-
* @param fallbackText 降级用纯文本(不传时自动从 content 提取纯文本,避免把 MD 语法当普通文本发出)
|
|
223
|
-
* @param keyboard QQ 下挂按钮组件(仅 Markdown 模式生效,降级纯文本时忽略)
|
|
224
|
-
* @returns 实际发送模式 { sent: true, mode: 'markdown' | 'text' }
|
|
225
|
-
* @throws 两种模式均发送失败时抛出“消息发送失败”
|
|
226
231
|
*/
|
|
227
|
-
async send(session, content, fallbackText, keyboard2) {
|
|
228
|
-
const resolvedFallback = fallbackText ?? markdownToPlainText(content);
|
|
232
|
+
async send(session, content, fallbackText = content, keyboard2) {
|
|
229
233
|
if (session.platform !== "qq") {
|
|
230
|
-
await session.send(
|
|
234
|
+
await session.send(fallbackText);
|
|
231
235
|
return { sent: true, mode: "text" };
|
|
232
236
|
}
|
|
233
|
-
if (Chat.config.useMd) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
237
|
+
if (!Chat.config.useMd) {
|
|
238
|
+
await session.send(fallbackText);
|
|
239
|
+
return { sent: true, mode: "text" };
|
|
240
|
+
}
|
|
241
|
+
const userId = session.userId;
|
|
242
|
+
let source = Chat.getCurrentPassiveSource(session);
|
|
243
|
+
if (!source) {
|
|
244
|
+
Chat.config.deBug && console.log("[Chat.send] no msg_id/event_id, fallback to text");
|
|
245
|
+
await session.send(fallbackText);
|
|
246
|
+
return { sent: true, mode: "text" };
|
|
247
|
+
}
|
|
248
|
+
const cacheKey = Chat.getCacheKey(source);
|
|
249
|
+
if (Chat.chache[cacheKey] == void 0) {
|
|
250
|
+
Chat.chache[cacheKey] = 0;
|
|
251
|
+
}
|
|
252
|
+
let seq = ++Chat.chache[cacheKey];
|
|
253
|
+
let msgSeq = seq === 1 ? 0 : seq;
|
|
254
|
+
if (seq > 1) {
|
|
255
|
+
Chat.config.deBug && console.log(`检测到 MD 连续发送,尝试递增 msg_seq;当前第 ${seq} 次。`);
|
|
256
|
+
}
|
|
257
|
+
if (seq > 5) {
|
|
258
|
+
const nextSource = Chat.getNextPassiveSource(userId, source);
|
|
259
|
+
if (nextSource) {
|
|
260
|
+
source = nextSource;
|
|
261
|
+
const nextCacheKey = Chat.getCacheKey(source);
|
|
262
|
+
if (Chat.chache[nextCacheKey] == void 0) {
|
|
263
|
+
Chat.chache[nextCacheKey] = 0;
|
|
260
264
|
}
|
|
265
|
+
const nextSeq = ++Chat.chache[nextCacheKey];
|
|
266
|
+
msgSeq = nextSeq === 1 ? 0 : nextSeq;
|
|
267
|
+
Chat.config.deBug && console.log(`[Chat.send] 被动 MD 超过上限,切换到 ${source.type}: ${source.id}`);
|
|
268
|
+
} else {
|
|
269
|
+
await session.send(fallbackText);
|
|
270
|
+
Chat.clearChache();
|
|
271
|
+
return { sent: true, mode: "text" };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
const mdContent = {
|
|
275
|
+
...source.payload,
|
|
276
|
+
msg_type: 2,
|
|
277
|
+
msg_seq: msgSeq,
|
|
278
|
+
markdown: {
|
|
279
|
+
content
|
|
280
|
+
},
|
|
281
|
+
...keyboard2 ? { keyboard: keyboard2 } : {}
|
|
282
|
+
};
|
|
283
|
+
try {
|
|
284
|
+
if (session.guildId) {
|
|
285
|
+
await session.bot.internal.sendMessage(session.channelId, mdContent);
|
|
286
|
+
} else {
|
|
287
|
+
mdContent.markdown.content = mdContent.markdown.content.replace(/<qqbot-at-user[^>]*\/>/g, "");
|
|
288
|
+
await session.bot.internal.sendPrivateMessage(session.userId, mdContent);
|
|
261
289
|
}
|
|
290
|
+
Chat.clearChache();
|
|
291
|
+
return { sent: true, mode: "markdown" };
|
|
292
|
+
} catch (error) {
|
|
293
|
+
Chat.config.deBug && console.log("[Chat.send] Markdown 发送失败,回退普通文本", error);
|
|
262
294
|
try {
|
|
263
|
-
|
|
264
|
-
await session.bot.internal.sendMessage(session.channelId, mdContent);
|
|
265
|
-
} else {
|
|
266
|
-
mdContent.markdown.content = mdContent.markdown.content = mdContent.markdown.content.replace(/<qqbot-at-user[^>]*\/>/g, "");
|
|
267
|
-
await session.bot.internal.sendPrivateMessage(session.userId, mdContent);
|
|
268
|
-
}
|
|
295
|
+
await session.send(fallbackText);
|
|
269
296
|
Chat.clearChache();
|
|
270
|
-
return { sent: true, mode: "
|
|
297
|
+
return { sent: true, mode: "text" };
|
|
271
298
|
} catch {
|
|
272
|
-
Chat.
|
|
273
|
-
|
|
274
|
-
await session.send(resolvedFallback);
|
|
275
|
-
Chat.clearChache();
|
|
276
|
-
return { sent: true, mode: "text" };
|
|
277
|
-
} catch {
|
|
278
|
-
Chat.clearChache();
|
|
279
|
-
throw new Error("消息发送失败");
|
|
280
|
-
}
|
|
299
|
+
Chat.clearChache();
|
|
300
|
+
throw new Error("消息发送失败");
|
|
281
301
|
}
|
|
282
|
-
} else {
|
|
283
|
-
await session.send(resolvedFallback);
|
|
284
|
-
return { sent: true, mode: "text" };
|
|
285
302
|
}
|
|
286
303
|
},
|
|
287
|
-
// 当前 session 是否可以使用MD
|
|
304
|
+
// 当前 session 是否可以使用 MD
|
|
288
305
|
isUseMd(session) {
|
|
289
306
|
if (session.platform !== "qq") return false;
|
|
290
|
-
|
|
291
|
-
|
|
307
|
+
const source = Chat.getCurrentPassiveSource(session);
|
|
308
|
+
if (!source) return false;
|
|
309
|
+
const cacheKey = Chat.getCacheKey(source);
|
|
310
|
+
if (Chat.chache[cacheKey] == void 0) return true;
|
|
311
|
+
return Chat.chache[cacheKey] <= 5;
|
|
312
|
+
},
|
|
313
|
+
/** 优先取当前 session 的 msg_id;如果是按钮事件,则取 event_id */
|
|
314
|
+
getCurrentPassiveSource(session) {
|
|
315
|
+
if (session.messageId) {
|
|
316
|
+
return {
|
|
317
|
+
type: "msg_id",
|
|
318
|
+
id: session.messageId,
|
|
319
|
+
payload: {
|
|
320
|
+
msg_id: session.messageId
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
const eventId = session.event?._data?.id;
|
|
325
|
+
if (eventId) {
|
|
326
|
+
return {
|
|
327
|
+
type: "event_id",
|
|
328
|
+
id: eventId,
|
|
329
|
+
payload: {
|
|
330
|
+
event_id: eventId
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
292
335
|
},
|
|
293
|
-
/**
|
|
336
|
+
/**
|
|
337
|
+
* 超过被动 MD 上限时:
|
|
338
|
+
* 1. 优先找 message 事件中缓存的新 msg_id
|
|
339
|
+
* 2. 没有新 msg_id,再找按钮事件缓存的 event_id
|
|
340
|
+
*/
|
|
341
|
+
getNextPassiveSource(userId, current) {
|
|
342
|
+
const cachedMessageId = Chat.messageIdTemp[userId];
|
|
343
|
+
if (cachedMessageId && !(current.type === "msg_id" && current.id === cachedMessageId)) {
|
|
344
|
+
return {
|
|
345
|
+
type: "msg_id",
|
|
346
|
+
id: cachedMessageId,
|
|
347
|
+
payload: {
|
|
348
|
+
msg_id: cachedMessageId
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
const cachedButtonEventId = Chat.buttonEventIdTemp[userId];
|
|
353
|
+
if (cachedButtonEventId && !(current.type === "event_id" && current.id === cachedButtonEventId)) {
|
|
354
|
+
return {
|
|
355
|
+
type: "event_id",
|
|
356
|
+
id: cachedButtonEventId,
|
|
357
|
+
payload: {
|
|
358
|
+
event_id: cachedButtonEventId
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
return null;
|
|
363
|
+
},
|
|
364
|
+
getCacheKey(source) {
|
|
365
|
+
return `${source.type}:${source.id}`;
|
|
366
|
+
},
|
|
367
|
+
/** 清除一条 msg_id/event_id 计数记录 */
|
|
294
368
|
clearChache() {
|
|
295
369
|
const chache = Object.keys(Chat.chache);
|
|
296
370
|
if (chache.length >= 40) {
|
|
@@ -303,12 +377,15 @@ var Chat = {
|
|
|
303
377
|
if (messageIdTempList.length > 150) {
|
|
304
378
|
delete Chat.messageIdTemp[messageIdTempList[0]];
|
|
305
379
|
}
|
|
380
|
+
},
|
|
381
|
+
/** 清除一条按钮 event_id 缓存 */
|
|
382
|
+
clearButtonEventIdTemp() {
|
|
383
|
+
const buttonEventIdTempList = Object.keys(Chat.buttonEventIdTemp);
|
|
384
|
+
if (buttonEventIdTempList.length > 150) {
|
|
385
|
+
delete Chat.buttonEventIdTemp[buttonEventIdTempList[0]];
|
|
386
|
+
}
|
|
306
387
|
}
|
|
307
388
|
};
|
|
308
|
-
function markdownToPlainText(md) {
|
|
309
|
-
return md.replace(/!\[[^\]]*\]\([^)]*\)/g, "").replace(/```[\s\S]*?```/g, (m) => m.replace(/```(?:[a-zA-Z]*)?\s*\n?|```/g, "")).replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/__([^_]+)__/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/_([^_]+)_/g, "$1").replace(/~~([^~]+)~~/g, "$1").replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1").replace(/<qqbot-cmd-input[^>]*?show="([^"]*)"[^>]*\/>/g, "$1").replace(/<qqbot-cmd-input[^>]*\/>/g, "").replace(/<qqbot-at-user[^>]*\/>/g, "").replace(/^#+\s*/gm, "").replace(/^>\s*/gm, "").replace(/^\|[-:\s|]+\|$/gm, "").replace(/^\|/gm, "").replace(/\|$/gm, "").replace(/\|/g, " ").replace(/[ ]{3,}/g, " ").replace(/\n{3,}/g, "\n\n").replace(/^ +| +$/gm, "").trim();
|
|
310
|
-
}
|
|
311
|
-
__name(markdownToPlainText, "markdownToPlainText");
|
|
312
389
|
|
|
313
390
|
// src/storageQueue.ts
|
|
314
391
|
var queues = {};
|