@meet-im/meet 2.0.5 → 3.0.0-beta.0

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.
Files changed (70) hide show
  1. package/dist/account-inspect-api.d.ts +2 -0
  2. package/dist/account-inspect-api.js +4 -0
  3. package/dist/channel-plugin-api.d.ts +1 -0
  4. package/dist/channel-plugin-api.js +3 -0
  5. package/dist/index.d.ts +14 -0
  6. package/dist/index.js +43 -0
  7. package/dist/monitor-api.d.ts +1 -0
  8. package/dist/monitor-api.js +1 -0
  9. package/dist/probe-api.d.ts +1 -0
  10. package/dist/probe-api.js +1 -0
  11. package/dist/runtime-setter-api.d.ts +1 -0
  12. package/dist/runtime-setter-api.js +2 -0
  13. package/dist/send-api.d.ts +1 -0
  14. package/dist/send-api.js +1 -0
  15. package/dist/src/account-inspect.d.ts +5 -0
  16. package/dist/src/account-inspect.js +9 -0
  17. package/dist/src/accounts.d.ts +12 -0
  18. package/dist/src/accounts.js +134 -0
  19. package/dist/src/bot.d.ts +15 -0
  20. package/dist/src/bot.js +355 -0
  21. package/dist/src/channel.d.ts +3 -0
  22. package/dist/src/channel.js +402 -0
  23. package/dist/src/client.d.ts +8 -0
  24. package/dist/src/client.js +49 -0
  25. package/dist/src/config-schema.d.ts +82 -0
  26. package/dist/src/config-schema.js +46 -0
  27. package/dist/src/media.d.ts +57 -0
  28. package/dist/src/media.js +140 -0
  29. package/dist/src/monitor.d.ts +9 -0
  30. package/dist/src/monitor.js +153 -0
  31. package/dist/src/outbound.d.ts +2 -0
  32. package/dist/src/outbound.js +34 -0
  33. package/dist/src/policy.d.ts +30 -0
  34. package/dist/src/policy.js +78 -0
  35. package/dist/src/probe.d.ts +10 -0
  36. package/dist/src/probe.js +56 -0
  37. package/dist/src/reply-dispatcher.d.ts +29 -0
  38. package/dist/src/reply-dispatcher.js +173 -0
  39. package/dist/src/runtime.d.ts +11 -0
  40. package/dist/src/runtime.js +6 -0
  41. package/dist/src/sdk-bridge.d.ts +21 -0
  42. package/dist/src/sdk-bridge.js +214 -0
  43. package/dist/src/send.d.ts +60 -0
  44. package/dist/src/send.js +317 -0
  45. package/dist/src/targets.d.ts +15 -0
  46. package/dist/src/targets.js +63 -0
  47. package/dist/src/types.d.ts +76 -0
  48. package/dist/src/types.js +1 -0
  49. package/dist/vitest.config.d.ts +8 -0
  50. package/dist/vitest.config.js +7 -0
  51. package/openclaw.plugin.json +116 -0
  52. package/package.json +18 -17
  53. package/skills/meet-emojis/SKILL.md +13 -15
  54. package/index.ts +0 -26
  55. package/src/accounts.ts +0 -182
  56. package/src/bot.ts +0 -418
  57. package/src/channel.ts +0 -396
  58. package/src/client.ts +0 -63
  59. package/src/config-schema.ts +0 -50
  60. package/src/media.ts +0 -198
  61. package/src/monitor.ts +0 -195
  62. package/src/outbound.ts +0 -43
  63. package/src/policy.ts +0 -131
  64. package/src/probe.ts +0 -75
  65. package/src/reply-dispatcher.ts +0 -207
  66. package/src/runtime.ts +0 -14
  67. package/src/sdk-bridge.ts +0 -268
  68. package/src/send.ts +0 -363
  69. package/src/targets.ts +0 -101
  70. package/src/types.ts +0 -96
package/src/bot.ts DELETED
@@ -1,418 +0,0 @@
1
- import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk"
2
- import type { HistoryEntry } from "openclaw/plugin-sdk/reply-history"
3
- import {
4
- buildPendingHistoryContextFromMap,
5
- clearHistoryEntriesIfEnabled,
6
- recordPendingHistoryEntryIfEnabled,
7
- } from "openclaw/plugin-sdk/reply-history"
8
- import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id"
9
- import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime"
10
- import type { MeetBot, MsgContent } from "@meet-im/meet-bot-jssdk"
11
- import type { ResolvedMeetAccount, MeetMessageContext } from "./types.js"
12
- import { msgContentToContext, extractQuoteMessageMedia } from "./sdk-bridge.js"
13
- import { getMeetRuntime } from "./runtime.js"
14
- import { resolveMeetAllowlistMatch, resolveMeetGroupPolicy, resolveMeetGroupConfig, resolveMeetGroupUserPolicy } from "./policy.js"
15
- import { sendMessageMeet } from "./send.js"
16
- import { resolveMediaAttachments, setMediaDebugLogger } from "./media.js"
17
-
18
- const DEFAULT_GROUP_SYSTEM_PROMPT =
19
- "你正在 Meet 群组中对话。请保持回复简洁明了,适合群聊场景。如需回复特定用户,请使用 <@USER_ID> 格式提及对方,例如 <@553>。注意:Meet 不支持用反引号包住 Markdown 语法标记,描述语法时直接写符号,不要加反引号。发送图片或文件时,使用 message 工具的 media 参数,不要用 Markdown 图片语法 ![](path)。"
20
-
21
- const DEFAULT_DM_SYSTEM_PROMPT =
22
- "你正在 Meet 私聊中对话。注意:Meet 不支持用反引号包住 Markdown 语法标记,描述语法时直接写符号,不要加反引号。发送图片或文件时,使用 message 工具的 media 参数,不要用 Markdown 图片语法 ![](path)。"
23
-
24
- function formatHistoryEntry(entry: HistoryEntry): string {
25
- return `${entry.sender}: ${entry.body}`
26
- }
27
-
28
- export async function handleMeetMessage(params: {
29
- cfg: ClawdbotConfig
30
- msg: MsgContent
31
- botUserId: string
32
- runtime?: RuntimeEnv
33
- accountId: string
34
- account: ResolvedMeetAccount
35
- bot: MeetBot
36
- groupHistories: Map<string, HistoryEntry[]>
37
- quoteMsgMap?: Record<string, MsgContent>
38
- }): Promise<void> {
39
- const { cfg, msg, botUserId, runtime, accountId, account, bot, groupHistories, quoteMsgMap } = params
40
- const log = runtime?.log ?? console.log
41
- const error = runtime?.error ?? console.error
42
-
43
- let ctx: MeetMessageContext
44
- try {
45
- ctx = msgContentToContext(msg, botUserId, quoteMsgMap)
46
- } catch (err) {
47
- error(`[${accountId}]: failed to parse message: ${String(err)}`)
48
- return
49
- }
50
-
51
- const isGroup = ctx.chatType === "channel"
52
-
53
- if (ctx.senderId === botUserId) {
54
- log(`[${accountId}]: skipping own message ${ctx.messageId}`)
55
- return
56
- }
57
-
58
- log(
59
- `[${accountId}]: received message from ${ctx.senderId} in ${ctx.chatId} (${ctx.chatType})`,
60
- )
61
-
62
- const meetCfg = account.config
63
- const dmPolicy = meetCfg.dmPolicy ?? "pairing"
64
- const allowFrom = meetCfg.allowFrom ?? []
65
- const historyLimit = isGroup
66
- ? Math.max(0, meetCfg.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? 20)
67
- : Math.max(0, meetCfg.dmHistoryLimit ?? 0)
68
- const speaker = ctx.senderName ?? ctx.senderId
69
- // Discord 做法:文字优先,无文字时用媒体占位符
70
- const messageBody = ctx.content.trim()
71
- ? `${speaker}: ${ctx.content.trim()}`
72
- : `${speaker}: ${ctx.placeholder || ""}`
73
-
74
- const pendingEntry: HistoryEntry = {
75
- sender: speaker,
76
- body: ctx.content.trim() || ctx.placeholder || "",
77
- timestamp: ctx.timestamp,
78
- messageId: ctx.messageId,
79
- }
80
-
81
- try {
82
- const core = getMeetRuntime()
83
-
84
- if (!isGroup) {
85
- // pairing 模式:需要从 pairing store 读取已授权用户列表
86
- // allowlist 模式:只使用配置中的 allowFrom
87
- let effectiveAllowFrom = allowFrom
88
- if (dmPolicy === "pairing") {
89
- try {
90
- const storeAllowFrom = await core.channel.pairing.readAllowFromStore({
91
- channel: "meet",
92
- accountId,
93
- })
94
- // 合并配置中的 allowFrom 和 store 中的授权列表
95
- effectiveAllowFrom = [...allowFrom, ...storeAllowFrom]
96
- } catch (err) {
97
- error(`[${accountId}]: failed to read pairing store: ${String(err)}`)
98
- }
99
- }
100
-
101
- const dmAllowed = resolveMeetAllowlistMatch({
102
- allowFrom: effectiveAllowFrom,
103
- senderId: ctx.senderId,
104
- }).allowed
105
-
106
- // pairing 模式:创建配对请求并发送授权码给用户
107
- // allowlist 模式:直接拒绝未授权用户
108
- if (dmPolicy !== "open" && !dmAllowed) {
109
- if (dmPolicy === "pairing") {
110
- log(`[${accountId}]: pairing request from ${ctx.senderId}`)
111
-
112
- // 创建或更新配对请求
113
- const { code, created } = await core.channel.pairing.upsertPairingRequest({
114
- channel: "meet",
115
- id: ctx.senderId,
116
- accountId,
117
- meta: {
118
- name: ctx.senderName,
119
- },
120
- })
121
-
122
- // 发送配对码给用户(新创建或已存在都发送)
123
- if (code) {
124
- const accountArg = accountId === DEFAULT_ACCOUNT_ID ? "" : ` --account ${accountId}`
125
- const lines = [
126
- "OpenClaw: 尚未授权访问。",
127
- "",
128
- `您的 Meet 用户 ID: ${ctx.senderId}`,
129
- "",
130
- `配对码: ${code}`,
131
- "",
132
- "请联系机器人管理员审批:",
133
- `openclaw pairing approve meet ${code}${accountArg}`,
134
- ]
135
- // 如果是已存在的请求,添加提示
136
- if (!created) {
137
- lines.splice(2, 0, "(您的配对请求已在等待审批中)")
138
- }
139
- const replyText = lines.join("\n")
140
- try {
141
- await sendMessageMeet({
142
- cfg,
143
- to: `user:${ctx.senderId}`,
144
- text: replyText,
145
- accountId,
146
- })
147
- } catch (err) {
148
- error(`[${accountId}]: failed to send pairing reply to ${ctx.senderId}: ${String(err)}`)
149
- }
150
- }
151
- } else {
152
- log(
153
- `[${accountId}]: blocked unauthorized sender ${ctx.senderId} (dmPolicy=${dmPolicy})`,
154
- )
155
- }
156
- return
157
- }
158
- }
159
-
160
- if (isGroup) {
161
- const groupPolicy = resolveMeetGroupPolicy({
162
- groupPolicy: meetCfg.groupPolicy,
163
- groupAllowFrom: meetCfg.groupAllowFrom ?? [],
164
- chatId: ctx.chatId,
165
- groups: meetCfg.groups,
166
- })
167
-
168
- if (!groupPolicy.allowed) {
169
- log(
170
- `[${accountId}]: group ${ctx.chatId} not allowed (groupPolicy=${meetCfg.groupPolicy})`,
171
- )
172
- return
173
- }
174
-
175
- const groupConfig = resolveMeetGroupConfig({
176
- meetConfig: meetCfg,
177
- chatId: ctx.chatId,
178
- })
179
-
180
- const groupUserPolicy = resolveMeetGroupUserPolicy({
181
- groupConfig: groupConfig.groupConfig,
182
- senderId: ctx.senderId,
183
- })
184
-
185
- if (!groupUserPolicy.allowed) {
186
- log(`[${accountId}]: user ${ctx.senderId} not allowed in group ${ctx.chatId}`)
187
- return
188
- }
189
-
190
- if (groupConfig.requireMention && !ctx.mentionedBot) {
191
- log(`[${accountId}]: message in group ${ctx.chatId} skipped (mention required)`)
192
- recordPendingHistoryEntryIfEnabled({
193
- historyMap: groupHistories,
194
- historyKey: ctx.chatId,
195
- entry: pendingEntry,
196
- limit: historyLimit,
197
- })
198
- return
199
- }
200
- }
201
-
202
- const meetFrom = `meet:${ctx.senderId}`
203
- const meetTo = ctx.chatId
204
-
205
- const peerId = isGroup ? ctx.chatId : ctx.senderId
206
-
207
- const route = core.channel.routing.resolveAgentRoute({
208
- cfg,
209
- channel: "meet",
210
- accountId,
211
- peer: {
212
- kind: isGroup ? "group" : "direct",
213
- id: peerId,
214
- },
215
- })
216
-
217
- // 处理媒体附件
218
- let mediaContext = ""
219
- let mediaPaths: string[] = []
220
- if (ctx.media && ctx.media.length > 0) {
221
- log(`[${accountId}]: processing ${ctx.media.length} media attachment(s)`)
222
- // 初始化媒体调试日志
223
- setMediaDebugLogger(log, error)
224
- try {
225
- const maxBytes = meetCfg.mediaMaxMb ? meetCfg.mediaMaxMb * 1024 * 1024 : undefined
226
- const mediaInfos = await resolveMediaAttachments({
227
- accountId,
228
- attachments: ctx.media,
229
- sessionInfo: {
230
- firstId: ctx.sessionInfo.firstID,
231
- secondId: ctx.sessionInfo.secondID,
232
- sessionType: ctx.sessionInfo.sessionType,
233
- companyId: ctx.sessionInfo.companyID,
234
- },
235
- seqId: Number(ctx.messageId),
236
- maxBytes,
237
- })
238
- log(`[${accountId}]: resolved ${mediaInfos.length} media, paths=${mediaInfos.map(m => m.path).join(",")}`)
239
- mediaPaths = mediaInfos.map((m) => m.path)
240
- if (mediaInfos.length > 0) {
241
- // 为 BodyForAgent 生成详细媒体描述(包含路径)
242
- mediaContext = "\n\n" + mediaInfos.map((m) => {
243
- const typeLabel = m.contentType?.startsWith("image/") ? "<media:image>"
244
- : m.contentType?.startsWith("video/") ? "<media:video>"
245
- : m.contentType?.startsWith("audio/") ? "<media:audio>"
246
- : "<media:document>"
247
- return `${typeLabel}: ${m.path}`
248
- }).join("\n")
249
- }
250
- } catch (err) {
251
- error(`[${accountId}]: failed to resolve media: ${String(err)}`)
252
- }
253
- }
254
-
255
- // 处理引用消息的媒体附件
256
- if (quoteMsgMap && ctx.replyContext) {
257
- const quoteMedia = extractQuoteMessageMedia(msg, quoteMsgMap)
258
- if (quoteMedia && quoteMedia.length > 0) {
259
- log(`[${accountId}]: processing ${quoteMedia.length} quote message media attachment(s)`)
260
- try {
261
- const maxBytes = meetCfg.mediaMaxMb ? meetCfg.mediaMaxMb * 1024 * 1024 : undefined
262
- const quoteMediaInfos = await resolveMediaAttachments({
263
- accountId,
264
- attachments: quoteMedia,
265
- sessionInfo: {
266
- firstId: ctx.sessionInfo.firstID,
267
- secondId: ctx.sessionInfo.secondID,
268
- sessionType: ctx.sessionInfo.sessionType,
269
- companyId: ctx.sessionInfo.companyID,
270
- },
271
- seqId: Number(ctx.replyContext.messageId),
272
- maxBytes,
273
- })
274
- log(`[${accountId}]: resolved ${quoteMediaInfos.length} quote media, paths=${quoteMediaInfos.map(m => m.path).join(",")}`)
275
- // 将引用消息的媒体路径合并到 mediaPaths
276
- mediaPaths = [...mediaPaths, ...quoteMediaInfos.map((m) => m.path)]
277
- } catch (err) {
278
- error(`[${accountId}]: failed to resolve quote media: ${String(err)}`)
279
- }
280
- }
281
- }
282
-
283
- // 构建最终的消息内容
284
- // Discord 做法:文字优先,无文字时用媒体占位符
285
- // 媒体路径通过 MediaPaths 传递,mediaContext 仅作为 BodyForAgent 的补充描述
286
- const finalContent = ctx.content.trim()
287
- ? `${ctx.content.trim()}${mediaContext}`
288
- : (ctx.placeholder || "") + mediaContext
289
-
290
- // Discord 做法:跳过空内容消息
291
- if (!finalContent.trim() && mediaPaths.length === 0) {
292
- log(`[${accountId}]: skip message ${ctx.messageId} (empty content)`)
293
- return
294
- }
295
-
296
- const preview = finalContent.replace(/\s+/g, " ").slice(0, 160)
297
- const inboundLabel = isGroup
298
- ? `Meet[${accountId}] message in group ${ctx.chatId}`
299
- : `Meet[${accountId}] DM from ${ctx.senderId}`
300
-
301
- core.system.enqueueSystemEvent(`${inboundLabel}: ${preview}`, {
302
- sessionKey: route.sessionKey,
303
- contextKey: `meet:message:${ctx.chatId}:${ctx.messageId}`,
304
- })
305
-
306
- const envelopeFrom = isGroup ? `${ctx.chatId}:${ctx.senderId}` : ctx.senderId
307
-
308
- const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg)
309
-
310
- // 将当前消息添加到历史(在确认要处理消息之后)
311
- const historyEntries = isGroup && historyLimit > 0
312
- ? (() => {
313
- const entries = groupHistories.get(ctx.chatId) ?? []
314
- entries.push(pendingEntry)
315
- while (entries.length > historyLimit) {
316
- entries.shift()
317
- }
318
- groupHistories.set(ctx.chatId, entries)
319
- return entries
320
- })()
321
- : []
322
-
323
- const bodyWithContext = buildPendingHistoryContextFromMap({
324
- historyMap: groupHistories,
325
- historyKey: ctx.chatId,
326
- limit: historyLimit,
327
- currentMessage: messageBody,
328
- formatEntry: formatHistoryEntry,
329
- })
330
-
331
- const body = core.channel.reply.formatAgentEnvelope({
332
- channel: "Meet",
333
- from: envelopeFrom,
334
- timestamp: new Date(),
335
- envelope: envelopeOptions,
336
- body: bodyWithContext,
337
- })
338
-
339
- const inboundHistory =
340
- isGroup && historyLimit > 0
341
- ? historyEntries.map((entry) => ({
342
- sender: entry.sender,
343
- body: entry.body,
344
- timestamp: entry.timestamp,
345
- }))
346
- : undefined
347
-
348
- const channelConfig = isGroup ? meetCfg.channels?.[ctx.chatId] : undefined
349
- const groupConfig = isGroup ? resolveMeetGroupConfig({ meetConfig: meetCfg, chatId: ctx.chatId }) : undefined
350
- const systemPrompt = isGroup
351
- ? (groupConfig?.systemPrompt ?? channelConfig?.systemPrompt ?? meetCfg.systemPrompt ?? DEFAULT_GROUP_SYSTEM_PROMPT)
352
- : (meetCfg.systemPrompt ?? DEFAULT_DM_SYSTEM_PROMPT)
353
-
354
- const inboundCtx = core.channel.reply.finalizeInboundContext({
355
- Body: body,
356
- BodyForAgent: finalContent,
357
- RawBody: ctx.content,
358
- CommandBody: ctx.content,
359
- From: meetFrom,
360
- To: meetTo,
361
- SessionKey: route.sessionKey,
362
- AccountId: route.accountId,
363
- ChatType: isGroup ? "group" : "direct",
364
- GroupSubject: isGroup ? ctx.chatId : undefined,
365
- GroupSystemPrompt: systemPrompt,
366
- SenderName: ctx.senderName,
367
- SenderId: ctx.senderId,
368
- Provider: "meet" as const,
369
- Surface: "meet" as const,
370
- MessageSid: ctx.messageId,
371
- Timestamp: ctx.timestamp ?? Date.now(),
372
- WasMentioned: ctx.mentionedBot,
373
- ReplyToId: ctx.replyContext?.messageId,
374
- ReplyToBody: ctx.replyContext?.content,
375
- ReplyToSender: ctx.replyContext?.senderId,
376
- InboundHistory: inboundHistory,
377
- CommandAuthorized: true,
378
- OriginatingChannel: "meet" as const,
379
- OriginatingTo: meetTo,
380
- MediaPaths: mediaPaths.length > 0 ? mediaPaths : undefined,
381
- })
382
-
383
- const { createMeetReplyDispatcher } = await import("./reply-dispatcher.js")
384
- const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId)
385
- const { dispatcher, replyOptions, markDispatchIdle } = await createMeetReplyDispatcher({
386
- cfg,
387
- agentId: route.agentId,
388
- runtime: runtime as RuntimeEnv,
389
- chatId: ctx.chatId,
390
- replyToMessageId: ctx.messageId,
391
- accountId,
392
- bot,
393
- botUserId,
394
- mediaLocalRoots,
395
- })
396
-
397
- log(`[${accountId}]: dispatching to AI agent=${route.agentId} session=${route.sessionKey} history=${inboundHistory?.length ?? 0}`)
398
-
399
- await core.channel.reply.dispatchReplyFromConfig({
400
- ctx: inboundCtx,
401
- cfg,
402
- dispatcher,
403
- replyOptions,
404
- })
405
-
406
- log(`[${accountId}]: AI response completed for message ${ctx.messageId}`)
407
-
408
- markDispatchIdle()
409
-
410
- clearHistoryEntriesIfEnabled({
411
- historyMap: groupHistories,
412
- historyKey: ctx.chatId,
413
- limit: historyLimit,
414
- })
415
- } catch (err) {
416
- error(`[${accountId}]: error processing message: ${String(err)}`)
417
- }
418
- }