mioku-plugin-chat 3.0.2 → 3.0.4

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/core/chat-turn.ts CHANGED
@@ -1,12 +1,8 @@
1
1
  import type { MiokuContext } from "mioku";
2
- import type { AITool, ChatRuntimePromptInjection } from "mioku";
2
+ import type { AITool, Bot, ChatRuntimePromptInjection } from "mioku";
3
3
  import type { ChatPluginContext, ChatRuntimeState } from "../context";
4
4
  import type { ChatConfig, ChatMessage, TargetMessage } from "../types";
5
- import {
6
- getGroupHistory,
7
- getBotRole,
8
- getQuotedContent,
9
- } from "../utils";
5
+ import { getGroupHistory, getBotRole, getQuotedContent } from "../utils";
10
6
  import { buildStructuredUserInputFromTarget } from "../manage/group-structured-history";
11
7
 
12
8
  export type RuntimeReplyContextType =
@@ -53,7 +49,8 @@ function buildRuntimeTargetMessageContent(
53
49
  overrideContent?: string,
54
50
  ): string {
55
51
  if (overrideContent?.trim()) return overrideContent.trim();
56
- if (!event || !event.message || !Array.isArray(event.message)) return NO_NEW_MESSAGE;
52
+ if (!event || !event.message || !Array.isArray(event.message))
53
+ return NO_NEW_MESSAGE;
57
54
  return ctx.text(event)?.trim() || NO_NEW_MESSAGE;
58
55
  }
59
56
 
@@ -74,7 +71,8 @@ function resolveRuntimeContext(
74
71
  selfId: event.self_id,
75
72
  sessionId: groupId ? `group:${groupId}` : `personal:${userId}`,
76
73
  personalSessionId: groupId ? `personal:${userId}` : undefined,
77
- senderName: event.sender?.card || event.sender?.nickname || String(userId),
74
+ senderName:
75
+ event.sender?.card || event.sender?.nickname || String(userId),
78
76
  userRole: event.sender?.role || "member",
79
77
  userTitle: event.sender?.title || undefined,
80
78
  groupName: event.group_name,
@@ -85,7 +83,10 @@ function resolveRuntimeContext(
85
83
  if (typeof options.selfId !== "number") {
86
84
  throw new Error("Chat runtime requires either event or selfId");
87
85
  }
88
- if (typeof options.groupId !== "number" && typeof options.userId !== "number") {
86
+ if (
87
+ typeof options.groupId !== "number" &&
88
+ typeof options.userId !== "number"
89
+ ) {
89
90
  throw new Error("Chat runtime requires groupId or userId");
90
91
  }
91
92
 
@@ -112,7 +113,9 @@ function resolveRuntimeContext(
112
113
  groupId: options.groupId,
113
114
  userId,
114
115
  selfId: options.selfId,
115
- sessionId: options.groupId ? `group:${options.groupId}` : `personal:${userId}`,
116
+ sessionId: options.groupId
117
+ ? `group:${options.groupId}`
118
+ : `personal:${userId}`,
116
119
  personalSessionId:
117
120
  options.groupId && userId ? `personal:${userId}` : undefined,
118
121
  senderName: options.groupId ? "system" : String(userId),
@@ -144,39 +147,90 @@ export async function finalizeChatTurn(
144
147
  return;
145
148
  }
146
149
  const { ctx } = pluginCtx;
147
- const { groupId, groupSessionId, userId, selfId, cfg, toolCtx, result, event } = args;
150
+ const {
151
+ groupId,
152
+ groupSessionId,
153
+ userId,
154
+ selfId,
155
+ cfg,
156
+ toolCtx,
157
+ result,
158
+ event,
159
+ } = args;
160
+
161
+ const actingBot = (selfId ? ctx.pickBot(selfId) : undefined) ?? event?.bot;
162
+ const eventBotId = event?.bot ? String(event.bot.bot_id) : undefined;
163
+ const actingIsEventBot =
164
+ eventBotId == null ||
165
+ (actingBot != null && String(actingBot.bot_id) === eventBotId);
148
166
 
149
167
  if (groupId) {
150
168
  const sentMessageIds = await pluginCtx.sendAIResponse(
151
- { ctx, groupId, messages: result.messages, config: cfg, sentIndices: toolCtx.sentMessageIndices, audioService: pluginCtx.audioService },
169
+ {
170
+ ctx,
171
+ groupId,
172
+ messages: result.messages,
173
+ config: cfg,
174
+ sentIndices: toolCtx.sentMessageIndices,
175
+ audioService: pluginCtx.audioService,
176
+ },
152
177
  selfId,
153
178
  );
154
- await pluginCtx.sendEmoji(ctx, groupId, result.emojiPath, event?.bot);
179
+ await pluginCtx.sendEmoji(ctx, groupId, result.emojiPath, actingBot);
155
180
  const now = Date.now();
156
- pluginCtx.saveBotMessages(groupId, groupSessionId, result.messages, now, cfg, pluginCtx.db, ctx, event?.bot, sentMessageIds);
181
+ pluginCtx.saveBotMessages(
182
+ groupId,
183
+ groupSessionId,
184
+ result.messages,
185
+ now,
186
+ cfg,
187
+ pluginCtx.db,
188
+ ctx,
189
+ actingBot,
190
+ sentMessageIds,
191
+ );
157
192
  if (args.isLive) {
158
- pluginCtx.idleCheckManager.recordBotMessages(groupSessionId, result.messages.length, selfId);
193
+ pluginCtx.idleCheckManager.recordBotMessages(
194
+ groupSessionId,
195
+ result.messages.length,
196
+ selfId,
197
+ );
159
198
  }
160
- pluginCtx.cooldownManager.startCooldownTimer(groupSessionId, groupId, selfId);
199
+ pluginCtx.cooldownManager.startCooldownTimer(
200
+ groupSessionId,
201
+ groupId,
202
+ selfId,
203
+ );
161
204
  } else {
162
205
  const sentIndices = toolCtx.sentMessageIndices;
163
206
  for (let i = 0; i < result.messages.length; i++) {
164
207
  if (sentIndices?.has(i)) continue;
165
- await pluginCtx.sendMessage(ctx, undefined, userId, result.messages[i], cfg, selfId, pluginCtx.audioService);
208
+ await pluginCtx.sendMessage(
209
+ ctx,
210
+ undefined,
211
+ userId,
212
+ result.messages[i],
213
+ cfg,
214
+ selfId,
215
+ pluginCtx.audioService,
216
+ );
166
217
  }
167
218
  if (result.emojiPath) {
168
219
  try {
169
220
  const emojiSegment = ctx.segment.image(`file://${result.emojiPath}`);
170
- if (args.isLive) {
221
+ if (args.isLive && actingIsEventBot && event?.reply) {
171
222
  await event.reply([emojiSegment]);
172
223
  } else {
173
- const bot = args.event?.bot;
174
- if (!bot) throw new Error(`bot ${selfId} not found`);
175
- await bot.sendMessage({ type: "private", user_id: userId}, [emojiSegment]);
224
+ if (!actingBot) throw new Error(`bot ${selfId} not found`);
225
+ await actingBot.sendMessage({ type: "private", user_id: userId }, [
226
+ emojiSegment,
227
+ ]);
176
228
  }
177
229
  } catch (err) {
178
230
  ctx.logger.warn(
179
- args.isLive ? `[Emoticon] Send failed: ${err}` : `[chat-runtime] Send emoji failed: ${err}`,
231
+ args.isLive
232
+ ? `[Emoticon] Send failed: ${err}`
233
+ : `[chat-runtime] Send emoji failed: ${err}`,
180
234
  );
181
235
  }
182
236
  }
@@ -189,12 +243,13 @@ export async function processChat(
189
243
  e: any,
190
244
  pluginCtx: ChatPluginContext,
191
245
  runtimeState: ChatRuntimeState,
246
+ options: { replyBot?: Bot } = {},
192
247
  ): Promise<void> {
193
248
  const { ctx } = pluginCtx;
194
249
  const isGroup = e.message_type === "group";
195
250
  const groupId: number | undefined = isGroup ? e.group_id : undefined;
196
251
  const userId: number = e.user_id || e.sender?.user_id;
197
- const selfId = e.self_id;
252
+ const selfId = options.replyBot ? Number(options.replyBot.bot_id) : e.self_id;
198
253
  const cfg = await pluginCtx.getConfig(groupId);
199
254
 
200
255
  const personalSessionId = `personal:${userId}`;
@@ -212,7 +267,11 @@ export async function processChat(
212
267
  groupId ?? userId,
213
268
  );
214
269
  if (groupId) {
215
- pluginCtx.sessionManager.getOrCreate(personalSessionId, "personal", userId);
270
+ pluginCtx.sessionManager.getOrCreate(
271
+ personalSessionId,
272
+ "personal",
273
+ userId,
274
+ );
216
275
  }
217
276
 
218
277
  const quotedInfo = await getQuotedContent(e, ctx);
@@ -242,7 +301,7 @@ export async function processChat(
242
301
  groupId,
243
302
  ctx,
244
303
  cfg.historyCount,
245
- e.self_id,
304
+ selfId,
246
305
  pluginCtx.db,
247
306
  pluginCtx.buildHistoryMediaOptions(pluginCtx.aiInstance, cfg),
248
307
  )
@@ -260,15 +319,18 @@ export async function processChat(
260
319
  }));
261
320
 
262
321
  const botNickname =
263
- cfg.nicknames[0] || e.bot?.nickname || "Bot";
264
- const botRole = groupId ? await getBotRole(groupId, ctx, e.self_id) : "member";
322
+ cfg.nicknames[0] ||
323
+ options.replyBot?.nickname ||
324
+ e.bot?.nickname ||
325
+ "Bot";
326
+ const botRole = groupId ? await getBotRole(groupId, ctx, selfId) : "member";
265
327
  let groupName: string | undefined;
266
328
  let memberCount: number | undefined;
267
329
  if (groupId) {
268
330
  const groupInfo = await pluginCtx.getGroupInfoData(
269
331
  ctx,
270
332
  groupId,
271
- e.self_id,
333
+ selfId,
272
334
  e.group_name,
273
335
  );
274
336
  groupName = groupInfo.groupName;
@@ -311,7 +373,7 @@ export async function processChat(
311
373
  pendingImageUrls: imageUrls,
312
374
  humanize: pluginCtx.humanize,
313
375
  targetMessage,
314
- selfId: e.self_id,
376
+ selfId,
315
377
  audioService: pluginCtx.audioService,
316
378
  });
317
379
 
@@ -351,7 +413,12 @@ export async function processChat(
351
413
  }
352
414
  : undefined,
353
415
  ),
354
- { userId, groupId, label: isGroup ? "group-chat" : "private-chat", skipRetryOnRateLimit: true },
416
+ {
417
+ userId,
418
+ groupId,
419
+ label: isGroup ? "group-chat" : "private-chat",
420
+ skipRetryOnRateLimit: true,
421
+ },
355
422
  );
356
423
 
357
424
  if (!result) {
@@ -366,7 +433,7 @@ export async function processChat(
366
433
  groupId,
367
434
  groupSessionId,
368
435
  userId,
369
- selfId: e.self_id,
436
+ selfId,
370
437
  toolCtx,
371
438
  send: true,
372
439
  isLive: true,
@@ -435,7 +502,13 @@ async function executeChatRuntimeRequestNow(
435
502
  }
436
503
 
437
504
  const rawHistory = groupId
438
- ? await getGroupHistory(groupId, pluginCtx.ctx, cfg.historyCount, selfId, pluginCtx.db)
505
+ ? await getGroupHistory(
506
+ groupId,
507
+ pluginCtx.ctx,
508
+ cfg.historyCount,
509
+ selfId,
510
+ pluginCtx.db,
511
+ )
439
512
  : [];
440
513
  const history: ChatMessage[] = rawHistory.map((msg) => ({
441
514
  sessionId,
@@ -449,9 +522,10 @@ async function executeChatRuntimeRequestNow(
449
522
  messageId: msg.messageId,
450
523
  }));
451
524
 
452
- const botRole = groupId ? await getBotRole(groupId, pluginCtx.ctx, selfId) : "member";
453
- const botNickname =
454
- cfg.nicknames[0] || event?.bot?.nickname || "Bot";
525
+ const botRole = groupId
526
+ ? await getBotRole(groupId, pluginCtx.ctx, selfId)
527
+ : "member";
528
+ const botNickname = cfg.nicknames[0] || event?.bot?.nickname || "Bot";
455
529
 
456
530
  let groupName: string | undefined;
457
531
  let memberCount: number | undefined;
@@ -11,7 +11,7 @@ export async function handleIdleCheckDebug(
11
11
  cfg: ChatConfig,
12
12
  ): Promise<void> {
13
13
  const ctx: MiokuContext = pluginCtx.ctx;
14
- const isOwner = ctx.isOwner?.(e) ?? false;
14
+ const isOwner = ctx.isMaster?.(e) ?? false;
15
15
  if (!isOwner) {
16
16
  await e.reply("只有主人才能使用这个指令~");
17
17
  return;
@@ -22,7 +22,6 @@ import {
22
22
  summarizeHistoryVideo,
23
23
  } from "../core/media/history-media";
24
24
  import { handleIdleCheckDebug } from "./idle-debug";
25
- import { handleTtsCommand } from "./tts-command";
26
25
  import { processChat } from "../core/chat-turn";
27
26
 
28
27
  const POKE_COOLDOWN_MS = 10 * 60_000;
@@ -55,25 +54,6 @@ export function createMessageHandler(
55
54
  return;
56
55
  }
57
56
 
58
- if (text.startsWith("/tts")) {
59
- const payload = text.replace(/^\/tts\s*/, "");
60
- await handleTtsCommand(pluginCtx, e, payload);
61
- return;
62
- }
63
-
64
- if (text === "/重置会话") {
65
- if (groupId) {
66
- pluginCtx.sessionManager.resetBotMessages(`group:${groupId}`);
67
- pluginCtx.groupStructuredHistory.clear(`group:${groupId}`);
68
- await e.reply("已清除本群会话中 AI 发送的消息~");
69
- } else {
70
- pluginCtx.sessionManager.resetBotMessages(`personal:${userId}`);
71
- pluginCtx.groupStructuredHistory.clear(`personal:${userId}`);
72
- await e.reply("已清除你的个人会话中 AI 发送的消息~");
73
- }
74
- return;
75
- }
76
-
77
57
  if (groupId && !isGroupAllowed(groupId, cfg)) return;
78
58
 
79
59
  // 媒体分析
@@ -222,6 +202,10 @@ export function createMessageHandler(
222
202
  }
223
203
 
224
204
  const atBot = shouldTrigger(e, text, cfg, ctx);
205
+ const replyBot = ctx.pickReplyBot(e);
206
+ const actorSelfId = replyBot
207
+ ? Number(replyBot.bot_id)
208
+ : Number(e.self_id || 0);
225
209
  const quotedBot = isGroup ? await isQuotingBot(e, ctx) : null;
226
210
  const mentionedNickname =
227
211
  cfg.nicknames.length > 0 &&
@@ -275,14 +259,14 @@ export function createMessageHandler(
275
259
  groupSessionId,
276
260
  groupId,
277
261
  delayInfo.delayMs,
278
- Number(e.self_id || 0),
262
+ actorSelfId,
279
263
  );
280
264
  return;
281
265
  }
282
266
  }
283
267
 
284
268
  pluginCtx.rateLimiter.record(userId, groupId, text);
285
- await processChat(e, pluginCtx, runtimeState);
269
+ await processChat(e, pluginCtx, runtimeState, { replyBot });
286
270
  return;
287
271
  }
288
272
 
@@ -294,10 +278,11 @@ export function createMessageHandler(
294
278
  ctx,
295
279
  cfg.historyCount,
296
280
  pluginCtx.db,
297
- Number(e.self_id || 0),
281
+ actorSelfId,
298
282
  pluginCtx.buildHistoryMediaOptions(pluginCtx.aiInstance, cfg),
299
283
  );
300
- const botNickname = cfg.nicknames[0] || e.bot?.nickname || "Bot";
284
+ const botNickname =
285
+ cfg.nicknames[0] || replyBot?.nickname || e.bot?.nickname || "Bot";
301
286
  const planResult = await pluginCtx.humanize.actionPlanner.plan(
302
287
  groupSessionId,
303
288
  botNickname,
@@ -307,7 +292,7 @@ export function createMessageHandler(
307
292
  if (planResult.action !== "reply") return;
308
293
  if (!pluginCtx.rateLimiter.canProcess(userId, groupId, text)) return;
309
294
  pluginCtx.rateLimiter.record(userId, groupId, text);
310
- await processChat(e, pluginCtx, runtimeState);
295
+ await processChat(e, pluginCtx, runtimeState, { replyBot });
311
296
  }
312
297
  };
313
298
 
@@ -319,7 +304,7 @@ export function createMessageHandler(
319
304
  pluginCtx.rateLimiter.recordInteraction(groupId, userId);
320
305
  pluginCtx.queueProcessor.scheduleQueuedMessages(
321
306
  groupSessionId,
322
- Number(e.self_id || 0),
307
+ actorSelfId,
323
308
  );
324
309
  }
325
310
  return;
@@ -15,7 +15,7 @@ export async function handleTtsCommand(
15
15
  payload: string,
16
16
  ): Promise<void> {
17
17
  const { ctx } = pluginCtx;
18
- const isOwner = ctx.isOwner?.(e) ?? false;
18
+ const isOwner = ctx.isMaster?.(e) ?? false;
19
19
  // if (!isOwner) {
20
20
  // await e.reply("只有主人才能使用 /tts 指令~");
21
21
  // return;
package/index.ts CHANGED
@@ -43,6 +43,7 @@ import { RateLimitGuard } from "./manage/rate-limit-guard";
43
43
  import { createChatRuntime } from "./runtime/chat-runtime";
44
44
  import { createMessageHandler } from "./handlers/message";
45
45
  import { createPokeHandler } from "./handlers/poke";
46
+ import { handleTtsCommand } from "./handlers/tts-command";
46
47
  import { buildHistoryMediaOptions } from "./core/media/segment";
47
48
  import type { ChatConfigProvider } from "./humanize";
48
49
  import type { ChatPluginContext, ChatHandlerState } from "./context";
@@ -115,8 +116,6 @@ function resolveRoleInstances(aiService: AIService): {
115
116
 
116
117
  export default definePlugin({
117
118
  name: "chat",
118
- version: "1.1.0",
119
- description: "AI 智能聊天插件",
120
119
  async setup(ctx: MiokuContext) {
121
120
  ctx.logger.info("聊天插件正在初始化...");
122
121
 
@@ -511,6 +510,59 @@ roleModels = resolved.models;
511
510
  const runtime = createChatRuntime(pluginCtx, getConfig);
512
511
  aiService.registerChatRuntime(runtime);
513
512
 
513
+ ctx.command({
514
+ name: "/重置会话",
515
+ match: /^\/重置会话$/,
516
+ prefixes: false,
517
+ description: "重置自己的AI聊天记录",
518
+ handler: async ({ event }) => {
519
+ if (event.user_id === event.self_id) return;
520
+ const groupId =
521
+ event.message_type === "group" ? Number(event.group_id) : undefined;
522
+ if (groupId) {
523
+ pluginCtx.sessionManager.resetBotMessages(`group:${groupId}`);
524
+ pluginCtx.groupStructuredHistory.clear(`group:${groupId}`);
525
+ await event.reply("已清除本群会话中 AI 发送的消息~");
526
+ } else {
527
+ const userId = Number(event.user_id || event.sender?.user_id || 0);
528
+ pluginCtx.sessionManager.resetBotMessages(`personal:${userId}`);
529
+ pluginCtx.groupStructuredHistory.clear(`personal:${userId}`);
530
+ await event.reply("已清除你的个人会话中 AI 发送的消息~");
531
+ }
532
+ },
533
+ });
534
+ ctx.command({
535
+ name: "/重置群会话",
536
+ match: /^\/重置群会话$/,
537
+ prefixes: false,
538
+ permission: "admin",
539
+ description: "重置当前群的AI聊天记录",
540
+ handler: async ({ event }) => {
541
+ if (event.user_id === event.self_id) return;
542
+ const groupId =
543
+ event.message_type === "group" ? Number(event.group_id) : undefined;
544
+ if (!groupId) {
545
+ await event.reply("在群里使用试试看吧~", true);
546
+ return;
547
+ }
548
+ pluginCtx.sessionManager.resetBotMessages(`group:${groupId}`);
549
+ pluginCtx.groupStructuredHistory.clear(`group:${groupId}`);
550
+ await event.reply("已清除本群会话中 AI 发送的消息~");
551
+ },
552
+ });
553
+ ctx.command({
554
+ name: "/tts",
555
+ match: /^\/tts\s*(.*)$/,
556
+ prefixes: false,
557
+ permission: "owner",
558
+ description: "TTS 推理",
559
+ usage: "/tts <文本>",
560
+ handler: async ({ event, match }) => {
561
+ if (event.user_id === event.self_id) return;
562
+ await handleTtsCommand(pluginCtx, event, String(match?.[1] ?? "").trim());
563
+ },
564
+ });
565
+
514
566
  ctx.handle("message", createMessageHandler(pluginCtx, handlerState));
515
567
  ctx.handle("notice.group.poke", createPokeHandler(pluginCtx, handlerState));
516
568
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-chat",
3
- "version": "3.0.2",
3
+ "version": "3.0.4",
4
4
  "description": "AI 智能聊天插件",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -17,35 +17,7 @@
17
17
  "config",
18
18
  "help",
19
19
  "screenshot"
20
- ],
21
- "accessHooks": [
22
- {
23
- "id": "戳一戳触发对话",
24
- "event": "notice.group.poke",
25
- "description": "群内戳一戳 bot"
26
- }
27
- ],
28
- "help": {
29
- "title": "AI 聊天",
30
- "description": "智能 AI 聊天插件",
31
- "commands": [
32
- {
33
- "cmd": "/重置会话",
34
- "desc": "重置自己的AI聊天记录",
35
- "role": "member"
36
- },
37
- {
38
- "cmd": "/重置群会话",
39
- "desc": "重置当前群的AI聊天记录",
40
- "role": "admin"
41
- },
42
- {
43
- "cmd": "/tts <文本>",
44
- "desc": "TTS 推理",
45
- "role": "owner"
46
- }
47
- ]
48
- }
20
+ ]
49
21
  },
50
22
  "dependencies": {
51
23
  "openai": "^4.0.0",
package/utils/message.ts CHANGED
@@ -27,11 +27,7 @@ export function shouldTrigger(
27
27
  ): boolean {
28
28
  if (e.message_type === "private") return false;
29
29
 
30
- const atSeg = e.message?.find((seg: any) => seg.type === "at");
31
- if (!atSeg) return false;
32
- const data = (atSeg.data ?? {}) as { qq?: unknown; target?: unknown };
33
- const qq = data.qq ?? data.target;
34
- return String(qq ?? "") === String(e.self_id ?? "");
30
+ return ctx.mentionedBots(e).length > 0;
35
31
  }
36
32
 
37
33
  /**
@@ -45,8 +41,13 @@ export async function isQuotingBot(
45
41
  if (e.quote_id) {
46
42
  try {
47
43
  const quoteMsg = await fetchQuotedMessage(e, ctx);
48
- if (quoteMsg && String(quoteMsg.sender?.user_id) === String(e.self_id)) {
49
- const quotedText = quoteMsg.message
44
+ const quotedUserId = String(quoteMsg?.sender?.user_id ?? "");
45
+ const quotingAnyBot =
46
+ quoteMsg != null &&
47
+ quotedUserId !== "" &&
48
+ ctx.bots.some((bot) => String(bot.bot_id) === quotedUserId);
49
+ if (quotingAnyBot) {
50
+ const quotedText = quoteMsg!.message
50
51
  ?.filter((s: any) => s.type === "text")
51
52
  .map((s: any) => String(s.data?.text ?? s.text ?? ""))
52
53
  .join("");
@@ -130,15 +131,9 @@ export function extractContent(
130
131
  text = ctx.text(e) || "";
131
132
  } catch {}
132
133
 
133
- // If text is empty but user @'d the bot, describe the action
134
+ // If text is empty but the message @'d a connected bot, describe the action
134
135
  if (!text.trim() && e.message) {
135
- const hasAt = e.message.some((seg: any) => {
136
- if (seg.type !== "at") return false;
137
- const data = (seg.data ?? {}) as { qq?: unknown; target?: unknown };
138
- const qq = data.qq ?? data.target;
139
- return String(qq ?? "") === String(e.self_id ?? "");
140
- });
141
- if (hasAt) {
136
+ if (ctx.mentionedBots(e).length > 0) {
142
137
  text = "[@you with no text]";
143
138
  }
144
139
  }