@meet-im/meet 3.3.1 → 3.4.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/dist/src/bot.js CHANGED
@@ -108,6 +108,7 @@ export async function handleMeetMessage(params) {
108
108
  to: `user:${ctx.senderId}`,
109
109
  text: replyText,
110
110
  accountId,
111
+ runtime,
111
112
  });
112
113
  }
113
114
  catch (err) {
@@ -388,6 +389,8 @@ export async function handleMeetMessage(params) {
388
389
  agentId: route.agentId,
389
390
  runtime: runtime,
390
391
  chatId: ctx.chatId,
392
+ senderId: ctx.senderId,
393
+ mentionedBot: ctx.mentionedBot,
391
394
  replyToMessageId: ctx.messageId,
392
395
  accountId,
393
396
  bot,
@@ -1,2 +1,2 @@
1
- export declare const MEET_PLUGIN_VERSION = "3.3.1";
1
+ export declare const MEET_PLUGIN_VERSION = "3.4.1";
2
2
  export declare const MEET_OPENCLAW_VERSION = "2026.5.18";
@@ -1,2 +1,2 @@
1
- export const MEET_PLUGIN_VERSION = "3.3.1";
1
+ export const MEET_PLUGIN_VERSION = "3.4.1";
2
2
  export const MEET_OPENCLAW_VERSION = "2026.5.18";
@@ -1,6 +1,4 @@
1
- import type { RuntimeEnv } from "openclaw/plugin-sdk";
2
1
  import type { ResolvedMeetAccount } from "./types.js";
3
- export declare function setProbeLogger(logger: RuntimeEnv): void;
4
2
  export type MeetProbeResult = {
5
3
  ok: boolean;
6
4
  error?: string;
package/dist/src/probe.js CHANGED
@@ -1,16 +1,4 @@
1
1
  import { getMeetClient } from "./client.js";
2
- let _logger = null;
3
- export function setProbeLogger(logger) {
4
- _logger = logger;
5
- }
6
- function log(message) {
7
- if (_logger) {
8
- _logger.log(message);
9
- }
10
- else {
11
- console.log(message);
12
- }
13
- }
14
2
  const probeCache = new Map();
15
3
  const PROBE_CACHE_TTL_MS = 5 * 60 * 1000;
16
4
  export async function probeMeet(account) {
@@ -36,7 +24,7 @@ export async function probeMeet(account) {
36
24
  botId: account.apiToken?.split(":")[0],
37
25
  };
38
26
  if (updates && updates.msgs.length > 0) {
39
- log(`[${account.accountId}] probe: received ${updates.msgs.length} update(s)`);
27
+ console.log(`[${account.accountId}] probe: received ${updates.msgs.length} update(s)`);
40
28
  }
41
29
  probeCache.set(cacheKey, { result, timestamp: Date.now() });
42
30
  return result;
@@ -15,6 +15,8 @@ export type CreateMeetReplyDispatcherOpts = {
15
15
  agentId: string;
16
16
  runtime: RuntimeEnv;
17
17
  chatId: string;
18
+ senderId?: string;
19
+ mentionedBot?: boolean;
18
20
  replyToMessageId?: string;
19
21
  accountId: string;
20
22
  bot: MeetBot;
@@ -75,7 +75,7 @@ export function protectMentionsInChunks(chunks) {
75
75
  return result;
76
76
  }
77
77
  export async function createMeetReplyDispatcher(opts) {
78
- const { cfg, agentId, chatId, replyToMessageId, accountId, mediaLocalRoots, sessionInfo, apiToken, apiEndpoint, typingMode } = opts;
78
+ const { cfg, agentId, chatId, senderId, mentionedBot, replyToMessageId, accountId, mediaLocalRoots, sessionInfo, apiToken, apiEndpoint, typingMode } = opts;
79
79
  const core = getMeetRuntime();
80
80
  const textChunkLimit = core.channel.text.resolveTextChunkLimit(cfg, "meet", accountId, {
81
81
  fallbackLimit: 4000,
@@ -89,42 +89,46 @@ export async function createMeetReplyDispatcher(opts) {
89
89
  ctx: { ChatType: chatType },
90
90
  })
91
91
  : undefined;
92
+ const shouldAutoMentionSender = chatType === "channel" && mentionedBot === true && !!senderId;
93
+ const senderMentionText = shouldAutoMentionSender ? `<@${senderId}>` : undefined;
92
94
  // 创建 typing callbacks(如果配置了 typing 且有必要参数)
93
95
  const hasTypingParams = typingMode && typingMode !== "none" && sessionInfo && apiToken;
94
- let typingRequestChain = Promise.resolve();
95
- const enqueueTypingRequest = (label, run) => {
96
- const next = typingRequestChain.then(async () => {
97
- opts.runtime.log?.(`[${accountId}][${chatId}]: typing ${label} sending...`);
98
- return await run();
99
- });
100
- typingRequestChain = next.then(() => undefined).catch(() => { });
101
- return next;
102
- };
96
+ // stop 请求需要等待最后一个 start 完成,避免乱序
97
+ let lastStartPromise = Promise.resolve();
103
98
  const typingCallbacks = hasTypingParams
104
99
  ? createTypingCallbacks({
105
100
  start: async () => {
106
- const result = await enqueueTypingRequest("start", async () => await sendTypingMeet({
101
+ // 直接发送,不串行化。typing start 是幂等的,重复发送无害
102
+ opts.runtime.log?.(`[${accountId}][${chatId}]: typing start sending...`);
103
+ const startPromise = sendTypingMeet({
107
104
  accountId,
108
105
  chatId,
109
106
  chatType: chatType ?? "channel",
110
107
  sessionInfo,
111
108
  token: apiToken,
112
109
  apiEndpoint,
113
- }));
114
- if (!result.ok && result.reason === "error") {
115
- throw result.error;
116
- }
117
- opts.runtime.log?.(`[${accountId}][${chatId}]: typing start sent ok=${result.ok}`);
110
+ }).then((result) => {
111
+ if (!result.ok && result.reason === "error") {
112
+ throw result.error;
113
+ }
114
+ opts.runtime.log?.(`[${accountId}][${chatId}]: typing start sent ok=${result.ok}`);
115
+ });
116
+ // 记录当前 start 请求,供 stop 等待
117
+ lastStartPromise = startPromise;
118
+ await startPromise;
118
119
  },
119
120
  stop: async () => {
120
- await enqueueTypingRequest("stop", async () => await stopTypingMeet({
121
+ // 等待最后一个 start 完成,避免 stop start 之前到达
122
+ await lastStartPromise;
123
+ opts.runtime.log?.(`[${accountId}][${chatId}]: typing stop sending...`);
124
+ await stopTypingMeet({
121
125
  accountId,
122
126
  chatId,
123
127
  chatType: chatType ?? "channel",
124
128
  sessionInfo,
125
129
  token: apiToken,
126
130
  apiEndpoint,
127
- }));
131
+ });
128
132
  },
129
133
  onStartError: (err) => {
130
134
  opts.runtime.error?.(`[${accountId}][${chatId}]: typing start failed: ${String(err)}`);
@@ -181,6 +185,7 @@ export async function createMeetReplyDispatcher(opts) {
181
185
  mediaUrl: mediaUrls[0],
182
186
  mediaLocalRoots,
183
187
  accountId,
188
+ runtime: opts.runtime,
184
189
  });
185
190
  // 后续媒体不带文本
186
191
  for (let i = 1; i < mediaUrls.length; i++) {
@@ -191,6 +196,7 @@ export async function createMeetReplyDispatcher(opts) {
191
196
  mediaUrl: mediaUrls[i],
192
197
  mediaLocalRoots,
193
198
  accountId,
199
+ runtime: opts.runtime,
194
200
  });
195
201
  }
196
202
  return;
@@ -198,13 +204,20 @@ export async function createMeetReplyDispatcher(opts) {
198
204
  // 只有文本,分片发送
199
205
  const rawChunks = core.channel.text.chunkTextWithMode(text, textChunkLimit, chunkMode);
200
206
  const protectedChunks = protectMentionsInChunks(rawChunks);
201
- for (const chunk of protectedChunks) {
207
+ for (const [index, rawChunk] of protectedChunks.entries()) {
208
+ const chunk = index === 0 && senderMentionText
209
+ ? rawChunk.includes(senderMentionText)
210
+ ? rawChunk
211
+ : `${senderMentionText} ${rawChunk}`
212
+ : rawChunk;
202
213
  await sendMessageMeet({
203
214
  cfg,
204
215
  to: chatId,
205
216
  text: chunk,
206
217
  accountId,
207
218
  replyToMessageId,
219
+ atIds: index === 0 && shouldAutoMentionSender && senderId ? [Number(senderId)] : undefined,
220
+ runtime: opts.runtime,
208
221
  });
209
222
  }
210
223
  },
@@ -250,6 +263,7 @@ export async function createMeetReplyDispatcher(opts) {
250
263
  to: chatId,
251
264
  text: userMessage,
252
265
  accountId,
266
+ runtime: opts.runtime,
253
267
  });
254
268
  }
255
269
  catch {
@@ -10,11 +10,11 @@ export declare function inferContentTypeFromFileName(fileName: string): string |
10
10
  * 如果原始 contentType 缺失或为通用二进制流,则根据文件名推断
11
11
  */
12
12
  export declare function resolveContentType(fileName: string, originalContentType?: string): string;
13
- export declare function setSendMessageLogger(logger: RuntimeEnv): void;
14
13
  export declare function extractAtIds(text: string): {
15
14
  text: string;
16
15
  atIds: number[];
17
16
  };
17
+ export declare function mergeAtIds(explicitAtIds?: number[], extractedAtIds?: number[]): number[];
18
18
  export type SendMessageMeetOpts = {
19
19
  cfg: ClawdbotConfig;
20
20
  to: string;
@@ -22,6 +22,8 @@ export type SendMessageMeetOpts = {
22
22
  accountId?: string;
23
23
  replyToMessageId?: string;
24
24
  atIds?: number[];
25
+ /** Optional runtime for consistent logging. If not provided, falls back to console. */
26
+ runtime?: RuntimeEnv;
25
27
  };
26
28
  export declare function sendMessageMeet(opts: SendMessageMeetOpts): Promise<{
27
29
  messageId: string;
@@ -41,6 +43,8 @@ export type SendMediaMeetOpts = {
41
43
  total: number;
42
44
  speedPerSecond: string;
43
45
  }) => void;
46
+ /** Optional runtime for consistent logging. If not provided, falls back to console. */
47
+ runtime?: RuntimeEnv;
44
48
  };
45
49
  export declare function sendMediaMeet(opts: SendMediaMeetOpts): Promise<{
46
50
  messageId: string;
package/dist/src/send.js CHANGED
@@ -108,25 +108,6 @@ export function resolveContentType(fileName, originalContentType) {
108
108
  const inferred = inferContentTypeFromFileName(fileName);
109
109
  return inferred || originalContentType || "application/octet-stream";
110
110
  }
111
- let _logger = null;
112
- export function setSendMessageLogger(logger) {
113
- _logger = logger;
114
- }
115
- function log(message) {
116
- if (_logger) {
117
- _logger.log(message);
118
- return;
119
- }
120
- console.log(message);
121
- }
122
- function logError(message) {
123
- if (_logger) {
124
- _logger.error(message);
125
- }
126
- else {
127
- console.error(message);
128
- }
129
- }
130
111
  export function extractAtIds(text) {
131
112
  const atIds = [];
132
113
  text.replace(MENTION_PATTERN, (_, id1, id2) => {
@@ -138,8 +119,13 @@ export function extractAtIds(text) {
138
119
  });
139
120
  return { text: text.trim(), atIds };
140
121
  }
122
+ export function mergeAtIds(explicitAtIds, extractedAtIds) {
123
+ return [...new Set([...(explicitAtIds ?? []), ...(extractedAtIds ?? [])])];
124
+ }
141
125
  export async function sendMessageMeet(opts) {
142
- const { cfg, to, text, accountId, atIds: explicitAtIds } = opts;
126
+ const { cfg, to, text, accountId, atIds: explicitAtIds, runtime } = opts;
127
+ const log = runtime?.log ?? console.log;
128
+ const logError = runtime?.error ?? console.error;
143
129
  const account = resolveMeetAccount({ cfg, accountId });
144
130
  if (!account.configured) {
145
131
  throw new Error(`Meet account not configured: ${accountId ?? "default"}`);
@@ -181,9 +167,7 @@ export async function sendMessageMeet(opts) {
181
167
  // 先重写 @handle 为 <@userId>,再提取 atIds
182
168
  const textWithMentions = rewriteMeetKnownMentions(text, account.accountId);
183
169
  const { text: cleanText, atIds: extractedAtIds } = extractAtIds(textWithMentions);
184
- const finalAtIds = explicitAtIds
185
- ? [...explicitAtIds, ...extractedAtIds]
186
- : extractedAtIds;
170
+ const finalAtIds = mergeAtIds(explicitAtIds, extractedAtIds);
187
171
  log(`send message to=${to} atIds=${finalAtIds.join(",") || "none"}`);
188
172
  const sessionInfo = parseTargetToSessionInfo(to, Number(botUserId));
189
173
  try {
@@ -202,7 +186,9 @@ export async function sendMessageMeet(opts) {
202
186
  }
203
187
  }
204
188
  export async function sendMediaMeet(opts) {
205
- const { cfg, to, text, mediaUrl, mediaLocalRoots, accountId, onProgress } = opts;
189
+ const { cfg, to, text, mediaUrl, mediaLocalRoots, accountId, onProgress, runtime: logRuntime } = opts;
190
+ const log = logRuntime?.log ?? console.log;
191
+ const logError = logRuntime?.error ?? console.error;
206
192
  const account = resolveMeetAccount({ cfg, accountId });
207
193
  if (!account.configured) {
208
194
  throw new Error(`Meet account not configured: ${accountId ?? "default"}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meet-im/meet",
3
- "version": "3.3.1",
3
+ "version": "3.4.1",
4
4
  "type": "module",
5
5
  "description": "OpenClaw Meet channel plugin",
6
6
  "scripts": {