@meet-im/meet 2.0.1 → 2.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meet-im/meet",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "type": "module",
5
5
  "description": "OpenClaw Meet channel plugin",
6
6
  "scripts": {
package/src/bot.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  recordPendingHistoryEntryIfEnabled,
7
7
  } from "openclaw/plugin-sdk/reply-history"
8
8
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id"
9
+ import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime"
9
10
  import type { MeetBot, MsgContent } from "@meet-im/meet-bot-jssdk"
10
11
  import type { ResolvedMeetAccount, MeetMessageContext } from "./types.js"
11
12
  import { msgContentToContext, extractQuoteMessageMedia } from "./sdk-bridge.js"
@@ -199,7 +200,7 @@ export async function handleMeetMessage(params: {
199
200
  }
200
201
 
201
202
  const meetFrom = `meet:${ctx.senderId}`
202
- const meetTo = isGroup ? `channel:${ctx.chatId}` : `user:${ctx.senderId}`
203
+ const meetTo = ctx.chatId
203
204
 
204
205
  const peerId = isGroup ? ctx.chatId : ctx.senderId
205
206
 
@@ -380,6 +381,7 @@ export async function handleMeetMessage(params: {
380
381
  })
381
382
 
382
383
  const { createMeetReplyDispatcher } = await import("./reply-dispatcher.js")
384
+ const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId)
383
385
  const { dispatcher, replyOptions, markDispatchIdle } = await createMeetReplyDispatcher({
384
386
  cfg,
385
387
  agentId: route.agentId,
@@ -389,6 +391,7 @@ export async function handleMeetMessage(params: {
389
391
  accountId,
390
392
  bot,
391
393
  botUserId,
394
+ mediaLocalRoots,
392
395
  })
393
396
 
394
397
  log(`[${accountId}]: dispatching to AI agent=${route.agentId} session=${route.sessionKey} history=${inboundHistory?.length ?? 0}`)
package/src/channel.ts CHANGED
@@ -65,6 +65,7 @@ export const meetPlugin: ChannelPlugin<ResolvedMeetAccount> = {
65
65
  messageToolHints: () => [
66
66
  "- Meet targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:<id>` or `channel:<id>`.",
67
67
  "- Meet mentions: use `<@USER_ID>` to mention users, `<@-1>` to mention everyone. Examples: `<@553> hello` or `<@-1> important announcement`.",
68
+ "- Meet media: use `media` parameter to send images/files as attachments. DO NOT use Markdown image syntax like `![](path)` - Meet does not render it.",
68
69
  ],
69
70
  },
70
71
  groups: {
@@ -2,7 +2,7 @@ import type { MeetBot } from "@meet-im/meet-bot-jssdk"
2
2
  import type { ClawdbotConfig, RuntimeEnv, ReplyPayload } from "openclaw/plugin-sdk"
3
3
  import { createReplyPrefixContext } from "openclaw/plugin-sdk/channel-runtime"
4
4
  import { getMeetRuntime } from "./runtime.js"
5
- import { sendMessageMeet } from "./send.js"
5
+ import { sendMessageMeet, sendMediaMeet } from "./send.js"
6
6
 
7
7
  /**
8
8
  * 匹配末尾不完整的 mention 开始: <@ 或 <@xxx (没有闭合的 >)
@@ -73,12 +73,13 @@ export type CreateMeetReplyDispatcherOpts = {
73
73
  accountId: string
74
74
  bot: MeetBot
75
75
  botUserId: string
76
+ mediaLocalRoots?: readonly string[]
76
77
  }
77
78
 
78
79
  export async function createMeetReplyDispatcher(
79
80
  opts: CreateMeetReplyDispatcherOpts,
80
81
  ) {
81
- const { cfg, agentId, chatId, replyToMessageId, accountId } = opts
82
+ const { cfg, agentId, chatId, replyToMessageId, accountId, mediaLocalRoots } = opts
82
83
  const core = getMeetRuntime()
83
84
 
84
85
  const textChunkLimit = core.channel.text.resolveTextChunkLimit(cfg, "meet", accountId, {
@@ -97,10 +98,40 @@ export async function createMeetReplyDispatcher(
97
98
  },
98
99
  deliver: async (payload: ReplyPayload, _info) => {
99
100
  const text = payload.text ?? ""
100
- if (!text.trim()) {
101
+ const mediaUrls = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : [])
102
+
103
+ // 如果既没有文本也没有媒体,直接返回
104
+ if (!text.trim() && mediaUrls.length === 0) {
101
105
  return
102
106
  }
103
107
 
108
+ // 处理媒体文件:先发送媒体,再发送文本
109
+ // 如果有媒体,第一个媒体带上文本作为 caption,后续媒体不带文本
110
+ if (mediaUrls.length > 0) {
111
+ // 第一个媒体带上 caption
112
+ await sendMediaMeet({
113
+ cfg,
114
+ to: chatId,
115
+ text: text.trim() || undefined,
116
+ mediaUrl: mediaUrls[0],
117
+ mediaLocalRoots,
118
+ accountId,
119
+ })
120
+ // 后续媒体不带文本
121
+ for (let i = 1; i < mediaUrls.length; i++) {
122
+ await sendMediaMeet({
123
+ cfg,
124
+ to: chatId,
125
+ text: undefined,
126
+ mediaUrl: mediaUrls[i],
127
+ mediaLocalRoots,
128
+ accountId,
129
+ })
130
+ }
131
+ return
132
+ }
133
+
134
+ // 只有文本,分片发送
104
135
  const rawChunks = core.channel.text.chunkTextWithMode(text, textChunkLimit, chunkMode)
105
136
  const protectedChunks = protectMentionsInChunks(rawChunks)
106
137
 
package/src/send.ts CHANGED
@@ -2,7 +2,7 @@ import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
2
2
  import type { MeetMediaInfo } from "./types.js";
3
3
  import type { UploadProgress as SdkUploadProgress } from "@meet-im/meet-bot-jssdk";
4
4
  import { resolveMeetAccount } from "./accounts.js";
5
- import { getMeetClient } from "./client.js";
5
+ import { getMeetClient, createMeetClient } from "./client.js";
6
6
  import { parseTargetToSessionInfo } from "./sdk-bridge.js";
7
7
  import { getMeetRuntime } from "./runtime.js";
8
8
 
@@ -180,13 +180,22 @@ export async function sendMessageMeet(
180
180
  if (!token) {
181
181
  throw new Error("Meet API token not configured");
182
182
  }
183
+ // 检查 token 是否未解析(仍是 secret ref 格式)
184
+ if (token.startsWith("${secret:")) {
185
+ throw new Error(
186
+ `Meet API token not resolved (still a secret reference). ` +
187
+ `Ensure Gateway is running or token is configured directly. ` +
188
+ `AccountId: ${account.accountId}`
189
+ );
190
+ }
183
191
  const botUserId = token.split(":")[0];
184
192
  if (!botUserId) {
185
193
  throw new Error("Invalid Meet API token format");
186
194
  }
187
- const bot = getMeetClient(account.accountId);
195
+ // 如果 client 不存在则自动创建(支持 message tool 直接发送消息)
196
+ let bot = getMeetClient(account.accountId);
188
197
  if (!bot) {
189
- throw new Error(`Meet client not found for account: ${account.accountId}`);
198
+ bot = createMeetClient(account);
190
199
  }
191
200
  const { text: cleanText, atIds: extractedAtIds } = extractAtIds(text);
192
201
  const finalAtIds = explicitAtIds
@@ -232,13 +241,22 @@ export async function sendMediaMeet(
232
241
  if (!token) {
233
242
  throw new Error("Meet API token not configured");
234
243
  }
244
+ // 检查 token 是否未解析(仍是 secret ref 格式)
245
+ if (token.startsWith("${secret:")) {
246
+ throw new Error(
247
+ `Meet API token not resolved (still a secret reference). ` +
248
+ `Ensure Gateway is running or token is configured directly. ` +
249
+ `AccountId: ${account.accountId}`
250
+ );
251
+ }
235
252
  const botUserId = token.split(":")[0];
236
253
  if (!botUserId) {
237
254
  throw new Error("Invalid Meet API token format");
238
255
  }
239
- const bot = getMeetClient(account.accountId);
256
+ // 如果 client 不存在则自动创建(支持 message tool 直接发送消息)
257
+ let bot = getMeetClient(account.accountId);
240
258
  if (!bot) {
241
- throw new Error(`Meet client not found for account: ${account.accountId}`);
259
+ bot = createMeetClient(account);
242
260
  }
243
261
 
244
262
  const runtime = getMeetRuntime();