@meet-im/meet 2.0.6 → 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 (69) 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/index.ts +0 -26
  54. package/src/accounts.ts +0 -182
  55. package/src/bot.ts +0 -418
  56. package/src/channel.ts +0 -396
  57. package/src/client.ts +0 -63
  58. package/src/config-schema.ts +0 -50
  59. package/src/media.ts +0 -198
  60. package/src/monitor.ts +0 -195
  61. package/src/outbound.ts +0 -43
  62. package/src/policy.ts +0 -131
  63. package/src/probe.ts +0 -75
  64. package/src/reply-dispatcher.ts +0 -207
  65. package/src/runtime.ts +0 -14
  66. package/src/sdk-bridge.ts +0 -268
  67. package/src/send.ts +0 -383
  68. package/src/targets.ts +0 -101
  69. package/src/types.ts +0 -96
@@ -0,0 +1,173 @@
1
+ import { createReplyPrefixContext } from "openclaw/plugin-sdk/channel-runtime";
2
+ import { getMeetRuntime } from "./runtime.js";
3
+ import { sendMessageMeet, sendMediaMeet } from "./send.js";
4
+ /**
5
+ * 匹配末尾不完整的 mention 开始: <@ 或 <@xxx (没有闭合的 >)
6
+ */
7
+ const INCOMPLETE_MENTION_START = /<@(-?\d*)$/;
8
+ /**
9
+ * 匹配开头不完整的 mention 结束: xxx> (没有开始的 <@)
10
+ */
11
+ const INCOMPLETE_MENTION_END = /^(-?\d+)>/;
12
+ /**
13
+ * 保护 mention 格式在分片后不被截断
14
+ *
15
+ * 当文本被分片后,`<@userId>` 格式可能被截断成:
16
+ * - 第一个 chunk 末尾: `<@123`
17
+ * - 第二个 chunk 开头: `456>`
18
+ *
19
+ * 此函数检测并修复这种情况,确保 mention 格式完整。
20
+ */
21
+ export function protectMentionsInChunks(chunks) {
22
+ if (chunks.length <= 1) {
23
+ return chunks;
24
+ }
25
+ const result = [];
26
+ let pendingSuffix = "";
27
+ for (let i = 0; i < chunks.length; i++) {
28
+ let chunk = pendingSuffix + chunks[i];
29
+ pendingSuffix = "";
30
+ // 检测末尾是否有不完整的 mention 开始 (<@ 或 <@xxx)
31
+ const startMatch = chunk.match(INCOMPLETE_MENTION_START);
32
+ if (startMatch) {
33
+ // 检查下一个 chunk 是否有对应的结束部分
34
+ const nextChunk = chunks[i + 1];
35
+ if (nextChunk !== undefined) {
36
+ const endMatch = nextChunk.match(INCOMPLETE_MENTION_END);
37
+ if (endMatch) {
38
+ // 将不完整的部分移到下一个 chunk 前面
39
+ const splitIndex = chunk.lastIndexOf("<@");
40
+ // splitIndex >= 0 时处理(包括 chunk 只有 "<@" 的情况)
41
+ if (splitIndex >= 0) {
42
+ // 如果 splitIndex 之前有内容,保留到 result
43
+ if (splitIndex > 0) {
44
+ result.push(chunk.slice(0, splitIndex));
45
+ }
46
+ pendingSuffix = chunk.slice(splitIndex);
47
+ continue;
48
+ }
49
+ }
50
+ }
51
+ }
52
+ result.push(chunk);
53
+ }
54
+ return result;
55
+ }
56
+ export async function createMeetReplyDispatcher(opts) {
57
+ const { cfg, agentId, chatId, replyToMessageId, accountId, mediaLocalRoots } = opts;
58
+ const core = getMeetRuntime();
59
+ const textChunkLimit = core.channel.text.resolveTextChunkLimit(cfg, "meet", accountId, {
60
+ fallbackLimit: 4000,
61
+ });
62
+ const chunkMode = core.channel.text.resolveChunkMode(cfg, "meet", accountId);
63
+ const prefixContext = createReplyPrefixContext({ cfg, agentId });
64
+ const { dispatcher, replyOptions, markDispatchIdle, markRunComplete } = core.channel.reply.createReplyDispatcherWithTyping({
65
+ responsePrefix: prefixContext.responsePrefix,
66
+ responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
67
+ humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, agentId),
68
+ onReplyStart: async () => {
69
+ },
70
+ deliver: async (payload, _info) => {
71
+ opts.runtime.log?.(`[${accountId}]: reply deliver kind=${_info.kind} text_len=${payload.text?.length ?? 0} media_count=${payload.mediaUrls?.length ?? (payload.mediaUrl ? 1 : 0)} reasoning=${payload.isReasoning === true} error=${payload.isError === true}`);
72
+ const text = payload.text ?? "";
73
+ const mediaUrls = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
74
+ // 如果既没有文本也没有媒体,直接返回
75
+ if (!text.trim() && mediaUrls.length === 0) {
76
+ return;
77
+ }
78
+ // 处理媒体文件:先发送媒体,再发送文本
79
+ // 如果有媒体,第一个媒体带上文本作为 caption,后续媒体不带文本
80
+ if (mediaUrls.length > 0) {
81
+ // 第一个媒体带上 caption
82
+ await sendMediaMeet({
83
+ cfg,
84
+ to: chatId,
85
+ text: text.trim() || undefined,
86
+ mediaUrl: mediaUrls[0],
87
+ mediaLocalRoots,
88
+ accountId,
89
+ });
90
+ // 后续媒体不带文本
91
+ for (let i = 1; i < mediaUrls.length; i++) {
92
+ await sendMediaMeet({
93
+ cfg,
94
+ to: chatId,
95
+ text: undefined,
96
+ mediaUrl: mediaUrls[i],
97
+ mediaLocalRoots,
98
+ accountId,
99
+ });
100
+ }
101
+ return;
102
+ }
103
+ // 只有文本,分片发送
104
+ const rawChunks = core.channel.text.chunkTextWithMode(text, textChunkLimit, chunkMode);
105
+ const protectedChunks = protectMentionsInChunks(rawChunks);
106
+ for (const chunk of protectedChunks) {
107
+ await sendMessageMeet({
108
+ cfg,
109
+ to: chatId,
110
+ text: chunk,
111
+ accountId,
112
+ replyToMessageId,
113
+ });
114
+ }
115
+ },
116
+ onError: async (error, info) => {
117
+ const errorMessage = String(error);
118
+ opts.runtime.error?.(`meet[${accountId}] ${info.kind} reply failed: ${errorMessage}`);
119
+ // 发送错误提示给用户,让 AI 也能看到错误并修正
120
+ // 根据错误类型生成友好提示
121
+ let userMessage;
122
+ const lowerError = errorMessage.toLowerCase();
123
+ if (lowerError.includes("not found") ||
124
+ lowerError.includes("enoent") ||
125
+ lowerError.includes("does not exist") ||
126
+ lowerError.includes("local media file not found")) {
127
+ userMessage = `发送失败: 文件不存在或无法访问。请提供有效的文件路径(建议使用绝对路径)。\n错误详情: ${errorMessage}`;
128
+ }
129
+ else if (lowerError.includes("mediafetcherror") ||
130
+ lowerError.includes("fetch_failed") ||
131
+ lowerError.includes("failed to fetch")) {
132
+ userMessage = `发送失败: 无法获取媒体文件。请检查 URL 是否正确或网络是否可用。\n错误详情: ${errorMessage}`;
133
+ }
134
+ else if (lowerError.includes("localmediaaccesserror") ||
135
+ lowerError.includes("path-not-allowed") ||
136
+ lowerError.includes("not safe to read") ||
137
+ lowerError.includes("network-path-not-allowed")) {
138
+ userMessage = `发送失败: 文件路径不在允许访问的目录内。请使用工作区内的文件路径,或联系管理员配置 mediaLocalRoots。\n错误详情: ${errorMessage}`;
139
+ }
140
+ else if (lowerError.includes("max_bytes") ||
141
+ lowerError.includes("exceeds maxbytes") ||
142
+ lowerError.includes("exceeds") && lowerError.includes("limit")) {
143
+ userMessage = `发送失败: 文件大小超过限制。请使用较小的文件。\n错误详情: ${errorMessage}`;
144
+ }
145
+ else if (lowerError.includes("not a file")) {
146
+ userMessage = `发送失败: 路径不是文件。请提供文件路径而非目录。\n错误详情: ${errorMessage}`;
147
+ }
148
+ else {
149
+ userMessage = `发送失败: ${errorMessage}`;
150
+ }
151
+ // 尝试发送错误消息,忽略发送失败(避免递归)
152
+ try {
153
+ await sendMessageMeet({
154
+ cfg,
155
+ to: chatId,
156
+ text: userMessage,
157
+ accountId,
158
+ });
159
+ }
160
+ catch {
161
+ // 忽略错误消息发送失败
162
+ }
163
+ },
164
+ onIdle: async () => {
165
+ },
166
+ });
167
+ return {
168
+ dispatcher,
169
+ replyOptions,
170
+ markDispatchIdle,
171
+ markRunComplete,
172
+ };
173
+ }
@@ -0,0 +1,11 @@
1
+ import type { PluginRuntime } from "openclaw/plugin-sdk/channel-core";
2
+ type MeetChannelRuntime = {
3
+ sendMessageMeet?: typeof import("./send.js").sendMessageMeet;
4
+ };
5
+ export type MeetRuntime = PluginRuntime & {
6
+ channel: PluginRuntime["channel"] & {
7
+ meet?: MeetChannelRuntime;
8
+ };
9
+ };
10
+ declare const setMeetRuntime: (next: MeetRuntime) => void, getOptionalMeetRuntime: () => MeetRuntime | null, getMeetRuntime: () => MeetRuntime;
11
+ export { getMeetRuntime, getOptionalMeetRuntime, setMeetRuntime };
@@ -0,0 +1,6 @@
1
+ import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
2
+ const { setRuntime: setMeetRuntime, tryGetRuntime: getOptionalMeetRuntime, getRuntime: getMeetRuntime, } = createPluginRuntimeStore({
3
+ pluginId: "meet",
4
+ errorMessage: "Meet runtime not initialized",
5
+ });
6
+ export { getMeetRuntime, getOptionalMeetRuntime, setMeetRuntime };
@@ -0,0 +1,21 @@
1
+ import type { MsgContent, SessionInfo, SessionType } from "@meet-im/meet-bot-jssdk";
2
+ import type { MeetMessageContext, MeetReplyContext, MeetMediaAttachment } from "./types.js";
3
+ export type QuoteMsgMap = Record<string, MsgContent>;
4
+ export declare function mapSessionType(sessionType: SessionType): "direct" | "channel";
5
+ /**
6
+ * 构建引用消息查找 Key
7
+ * 群聊: firstID+secondID:quoteSeqID
8
+ * 私聊: min(firstID,secondID):max(firstID,secondID):quoteSeqID
9
+ */
10
+ export declare function buildQuoteMsgKey(sessionInfo: SessionInfo, quoteSeqID: number): string;
11
+ /**
12
+ * 解析引用消息
13
+ */
14
+ export declare function resolveQuoteMessage(msg: MsgContent, quoteMsgMap: QuoteMsgMap): MeetReplyContext | undefined;
15
+ /**
16
+ * 从引用消息中提取媒体附件信息(用于下载)
17
+ */
18
+ export declare function extractQuoteMessageMedia(msg: MsgContent, quoteMsgMap: QuoteMsgMap): MeetMediaAttachment[] | undefined;
19
+ export declare function msgContentToContext(msg: MsgContent, botUserId: string, quoteMsgMap?: QuoteMsgMap): MeetMessageContext;
20
+ export declare function parseTargetToSessionInfo(target: string, botUserId: number): SessionInfo;
21
+ export declare function buildMeetTarget(sessionInfo: SessionInfo, botUserId: number): string;
@@ -0,0 +1,214 @@
1
+ export function mapSessionType(sessionType) {
2
+ return sessionType === 1 ? "direct" : "channel";
3
+ }
4
+ /**
5
+ * 生成与 Discord 一致的媒体占位符
6
+ * 格式:<media:image> (2 images) 或 <media:document> (3 files)
7
+ */
8
+ function buildMediaPlaceholder(media) {
9
+ const images = media.filter((m) => m.mimeType?.startsWith("image/"));
10
+ const videos = media.filter((m) => m.mimeType?.startsWith("video/"));
11
+ const audios = media.filter((m) => m.mimeType?.startsWith("audio/"));
12
+ const documents = media.filter((m) => !m.mimeType?.startsWith("image/") &&
13
+ !m.mimeType?.startsWith("video/") &&
14
+ !m.mimeType?.startsWith("audio/"));
15
+ const parts = [];
16
+ if (images.length > 0) {
17
+ const label = images.length === 1 ? "image" : "images";
18
+ parts.push(`<media:image> (${images.length} ${label})`);
19
+ }
20
+ if (videos.length > 0) {
21
+ const label = videos.length === 1 ? "video" : "videos";
22
+ parts.push(`<media:video> (${videos.length} ${label})`);
23
+ }
24
+ if (audios.length > 0) {
25
+ const label = audios.length === 1 ? "audio" : "audios";
26
+ parts.push(`<media:audio> (${audios.length} ${label})`);
27
+ }
28
+ if (documents.length > 0) {
29
+ const label = documents.length === 1 ? "document" : "documents";
30
+ parts.push(`<media:document> (${documents.length} ${label})`);
31
+ }
32
+ return parts.join("\n");
33
+ }
34
+ /**
35
+ * 构建引用消息查找 Key
36
+ * 群聊: firstID+secondID:quoteSeqID
37
+ * 私聊: min(firstID,secondID):max(firstID,secondID):quoteSeqID
38
+ */
39
+ export function buildQuoteMsgKey(sessionInfo, quoteSeqID) {
40
+ const { firstID, secondID, sessionType } = sessionInfo;
41
+ if (sessionType === 3) {
42
+ return `${firstID}+${secondID}:${quoteSeqID}`;
43
+ }
44
+ else {
45
+ const [minId, maxId] = firstID < secondID ? [firstID, secondID] : [secondID, firstID];
46
+ return `${minId}:${maxId}:${quoteSeqID}`;
47
+ }
48
+ }
49
+ /**
50
+ * 解析引用消息
51
+ */
52
+ export function resolveQuoteMessage(msg, quoteMsgMap) {
53
+ // 使用 quoteSeqID 字段(SDK V2)
54
+ const quoteSeqID = msg.quoteSeqID;
55
+ if (!quoteSeqID || !msg.sessionInfo) {
56
+ return undefined;
57
+ }
58
+ const key = buildQuoteMsgKey(msg.sessionInfo, quoteSeqID);
59
+ const quoteMsg = quoteMsgMap[key];
60
+ if (!quoteMsg) {
61
+ return undefined;
62
+ }
63
+ // 对齐 Discord: 引用消息的 body 包含媒体占位符
64
+ const body = buildQuoteMessageBody(quoteMsg);
65
+ return {
66
+ messageId: String(quoteMsg.seqId ?? 0),
67
+ senderId: quoteMsg.fromUid ? String(quoteMsg.fromUid) : undefined,
68
+ content: body,
69
+ timestamp: quoteMsg.timestamp,
70
+ };
71
+ }
72
+ /**
73
+ * 从引用消息中提取媒体附件信息(用于下载)
74
+ */
75
+ export function extractQuoteMessageMedia(msg, quoteMsgMap) {
76
+ const quoteSeqID = msg.quoteSeqID;
77
+ if (!quoteSeqID || !msg.sessionInfo) {
78
+ return undefined;
79
+ }
80
+ const key = buildQuoteMsgKey(msg.sessionInfo, quoteSeqID);
81
+ const quoteMsg = quoteMsgMap[key];
82
+ if (!quoteMsg) {
83
+ return undefined;
84
+ }
85
+ return extractMediaAttachments(quoteMsg);
86
+ }
87
+ /**
88
+ * 从消息中提取媒体附件信息
89
+ */
90
+ function extractMediaAttachments(msg) {
91
+ const extraInfo = msg.extraInfo;
92
+ if (!extraInfo) {
93
+ return undefined;
94
+ }
95
+ const attachments = [];
96
+ // 单附件
97
+ if (extraInfo.attechmentInfo) {
98
+ const att = extraInfo.attechmentInfo;
99
+ attachments.push({
100
+ fileId: att.fileID,
101
+ fileName: att.fileName,
102
+ fileSize: att.fileSize,
103
+ mimeType: att.mimeType,
104
+ fileUrl: att.fileUrl,
105
+ });
106
+ }
107
+ // 多附件
108
+ if (extraInfo.attechmentInfos && Array.isArray(extraInfo.attechmentInfos)) {
109
+ for (const att of extraInfo.attechmentInfos) {
110
+ attachments.push({
111
+ fileId: att.fileID,
112
+ fileName: att.fileName,
113
+ fileSize: att.fileSize,
114
+ mimeType: att.mimeType,
115
+ fileUrl: att.fileUrl,
116
+ });
117
+ }
118
+ }
119
+ return attachments.length > 0 ? attachments : undefined;
120
+ }
121
+ /**
122
+ * 构建引用消息的 body(对齐 Discord)
123
+ * 包含文本内容 + 媒体占位符
124
+ */
125
+ function buildQuoteMessageBody(quoteMsg) {
126
+ const parts = [];
127
+ // 添加文本内容
128
+ const text = quoteMsg.content?.trim();
129
+ if (text) {
130
+ parts.push(text);
131
+ }
132
+ // 添加媒体占位符
133
+ const media = extractMediaAttachments(quoteMsg);
134
+ if (media && media.length > 0) {
135
+ const placeholder = buildMediaPlaceholder(media);
136
+ if (placeholder) {
137
+ parts.push(placeholder);
138
+ }
139
+ }
140
+ return parts.join("\n");
141
+ }
142
+ export function msgContentToContext(msg, botUserId, quoteMsgMap = {}) {
143
+ if (!msg) {
144
+ throw new Error("MsgContent is undefined or null");
145
+ }
146
+ if (!msg.sessionInfo) {
147
+ throw new Error("MsgContent missing sessionInfo");
148
+ }
149
+ const chatType = mapSessionType(msg.sessionInfo.sessionType);
150
+ const chatId = chatType === "direct"
151
+ ? `user:${msg.fromUid}`
152
+ : `channel:${msg.sessionInfo.secondID}`;
153
+ const mentionedBot = msg.atIds?.includes(Number(botUserId)) ?? false;
154
+ const replyContext = resolveQuoteMessage(msg, quoteMsgMap);
155
+ const media = extractMediaAttachments(msg);
156
+ // 检测消息类型,生成与 Discord 一致的占位符
157
+ let contentType = "text";
158
+ let placeholder;
159
+ if (media && media.length > 0) {
160
+ contentType = "media";
161
+ placeholder = buildMediaPlaceholder(media);
162
+ }
163
+ return {
164
+ chatId,
165
+ messageId: String(msg.seqId ?? 0),
166
+ senderId: String(msg.fromUid ?? 0),
167
+ senderOpenId: String(msg.fromUid ?? 0),
168
+ chatType,
169
+ mentionedBot,
170
+ content: msg.content ?? "",
171
+ contentType,
172
+ placeholder,
173
+ sessionInfo: msg.sessionInfo,
174
+ timestamp: msg.timestamp,
175
+ atIds: msg.atIds,
176
+ replyContext,
177
+ media,
178
+ };
179
+ }
180
+ export function parseTargetToSessionInfo(target, botUserId) {
181
+ const userMatch = target.match(/^user:(\d+)$/);
182
+ if (userMatch) {
183
+ return {
184
+ firstID: botUserId,
185
+ secondID: Number(userMatch[1]),
186
+ sessionType: 1,
187
+ };
188
+ }
189
+ const channelMatch = target.match(/^channel:(\d+)$/);
190
+ if (channelMatch) {
191
+ return {
192
+ firstID: 1,
193
+ secondID: Number(channelMatch[1]),
194
+ sessionType: 3,
195
+ };
196
+ }
197
+ const numericMatch = target.match(/^(\d+)$/);
198
+ if (numericMatch) {
199
+ return {
200
+ firstID: botUserId,
201
+ secondID: Number(numericMatch[1]),
202
+ sessionType: 1,
203
+ };
204
+ }
205
+ throw new Error(`Invalid Meet target: ${target}`);
206
+ }
207
+ export function buildMeetTarget(sessionInfo, botUserId) {
208
+ if (sessionInfo.sessionType === 1) {
209
+ return sessionInfo.secondID === botUserId
210
+ ? `user:${sessionInfo.firstID}`
211
+ : `user:${sessionInfo.secondID}`;
212
+ }
213
+ return `channel:${sessionInfo.secondID}`;
214
+ }
@@ -0,0 +1,60 @@
1
+ import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
2
+ import type { MeetMediaInfo } from "./types.js";
3
+ /**
4
+ * 根据文件扩展名推断 MIME 类型
5
+ * 支持常见图片格式,包括现代格式如 avif, webp, heic
6
+ */
7
+ export declare function inferContentTypeFromFileName(fileName: string): string | undefined;
8
+ /**
9
+ * 获取最终的 contentType
10
+ * 如果原始 contentType 缺失或为通用二进制流,则根据文件名推断
11
+ */
12
+ export declare function resolveContentType(fileName: string, originalContentType?: string): string;
13
+ export declare function setSendMessageLogger(logger: RuntimeEnv): void;
14
+ export declare function extractAtIds(text: string): {
15
+ text: string;
16
+ atIds: number[];
17
+ };
18
+ export type SendMessageMeetOpts = {
19
+ cfg: ClawdbotConfig;
20
+ to: string;
21
+ text: string;
22
+ accountId?: string;
23
+ replyToMessageId?: string;
24
+ atIds?: number[];
25
+ };
26
+ export declare function sendMessageMeet(opts: SendMessageMeetOpts): Promise<{
27
+ messageId: string;
28
+ chatId: string;
29
+ }>;
30
+ export type SendMediaMeetOpts = {
31
+ cfg: ClawdbotConfig;
32
+ to: string;
33
+ text?: string;
34
+ mediaUrl: string;
35
+ mediaLocalRoots?: readonly string[];
36
+ accountId?: string;
37
+ /** 上传进度回调 */
38
+ onProgress?: (progress: {
39
+ percent: string;
40
+ loaded: number;
41
+ total: number;
42
+ speedPerSecond: string;
43
+ }) => void;
44
+ };
45
+ export declare function sendMediaMeet(opts: SendMediaMeetOpts): Promise<{
46
+ messageId: string;
47
+ chatId: string;
48
+ }>;
49
+ export declare function resolveMeetMedia(mediaUrl: string, opts?: {
50
+ cfg?: ClawdbotConfig;
51
+ accountId?: string;
52
+ mediaLocalRoots?: readonly string[];
53
+ }): Promise<MeetMediaInfo>;
54
+ export declare function getMessageMeet(opts: {
55
+ cfg: ClawdbotConfig;
56
+ messageId: string;
57
+ accountId?: string;
58
+ }): Promise<{
59
+ content: string;
60
+ } | null>;