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 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
- /** 清除一条 msgid 记录 */
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 (!Chat.messageIdTemp[session.userId]) {
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(resolvedFallback);
234
+ await session.send(fallbackText);
231
235
  return { sent: true, mode: "text" };
232
236
  }
233
- if (Chat.config.useMd) {
234
- const messageId = session.messageId;
235
- const userId = session.userId;
236
- if (Chat.chache[messageId] == void 0) {
237
- Chat.chache[messageId] = 0;
238
- }
239
- const mdContent = {
240
- msg_id: session.messageId,
241
- msg_type: 2,
242
- msg_seq: ++Chat.chache[messageId] == 1 ? 0 : Chat.chache[messageId],
243
- markdown: {
244
- content
245
- },
246
- ...keyboard2 ? { keyboard: keyboard2 } : {}
247
- };
248
- if (Chat.chache[messageId] > 1) {
249
- Chat.config.deBug && console.log(`检测到MD连续发送,尝试递增 msg_seq 再进行再次发送;第${Chat.chache[messageId] + 1}次。`);
250
- if (Chat.chache[messageId] > 5) {
251
- if (Chat.messageIdTemp[userId] && mdContent.msg_id != Chat.messageIdTemp[userId]) {
252
- Chat.config.deBug && console.log("middleware 存在新 mse_id 数据,使用更新数据");
253
- mdContent.msg_id = Chat.messageIdTemp[userId];
254
- mdContent.msg_seq = 0;
255
- } else {
256
- await session.send(resolvedFallback);
257
- Chat.clearChache();
258
- return { sent: true, mode: "text" };
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
- if (session.guildId) {
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: "markdown" };
297
+ return { sent: true, mode: "text" };
271
298
  } catch {
272
- Chat.config.deBug && console.log("[Chat.send] Markdown 发送失败,回退普通文本");
273
- try {
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
- if (Chat.chache[session.messageId] == void 0) return true;
291
- return Chat.chache[session.messageId] <= 5;
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
- /** 清除一条 msgid 记录 */
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 = {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-aipo-asbbot",
3
3
  "description": "乐高的API服务[自用]",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [