@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.
- package/dist/account-inspect-api.d.ts +2 -0
- package/dist/account-inspect-api.js +4 -0
- package/dist/channel-plugin-api.d.ts +1 -0
- package/dist/channel-plugin-api.js +3 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +43 -0
- package/dist/monitor-api.d.ts +1 -0
- package/dist/monitor-api.js +1 -0
- package/dist/probe-api.d.ts +1 -0
- package/dist/probe-api.js +1 -0
- package/dist/runtime-setter-api.d.ts +1 -0
- package/dist/runtime-setter-api.js +2 -0
- package/dist/send-api.d.ts +1 -0
- package/dist/send-api.js +1 -0
- package/dist/src/account-inspect.d.ts +5 -0
- package/dist/src/account-inspect.js +9 -0
- package/dist/src/accounts.d.ts +12 -0
- package/dist/src/accounts.js +134 -0
- package/dist/src/bot.d.ts +15 -0
- package/dist/src/bot.js +355 -0
- package/dist/src/channel.d.ts +3 -0
- package/dist/src/channel.js +402 -0
- package/dist/src/client.d.ts +8 -0
- package/dist/src/client.js +49 -0
- package/dist/src/config-schema.d.ts +82 -0
- package/dist/src/config-schema.js +46 -0
- package/dist/src/media.d.ts +57 -0
- package/dist/src/media.js +140 -0
- package/dist/src/monitor.d.ts +9 -0
- package/dist/src/monitor.js +153 -0
- package/dist/src/outbound.d.ts +2 -0
- package/dist/src/outbound.js +34 -0
- package/dist/src/policy.d.ts +30 -0
- package/dist/src/policy.js +78 -0
- package/dist/src/probe.d.ts +10 -0
- package/dist/src/probe.js +56 -0
- package/dist/src/reply-dispatcher.d.ts +29 -0
- package/dist/src/reply-dispatcher.js +173 -0
- package/dist/src/runtime.d.ts +11 -0
- package/dist/src/runtime.js +6 -0
- package/dist/src/sdk-bridge.d.ts +21 -0
- package/dist/src/sdk-bridge.js +214 -0
- package/dist/src/send.d.ts +60 -0
- package/dist/src/send.js +317 -0
- package/dist/src/targets.d.ts +15 -0
- package/dist/src/targets.js +63 -0
- package/dist/src/types.d.ts +76 -0
- package/dist/src/types.js +1 -0
- package/dist/vitest.config.d.ts +8 -0
- package/dist/vitest.config.js +7 -0
- package/openclaw.plugin.json +116 -0
- package/package.json +18 -17
- package/index.ts +0 -26
- package/src/accounts.ts +0 -182
- package/src/bot.ts +0 -418
- package/src/channel.ts +0 -396
- package/src/client.ts +0 -63
- package/src/config-schema.ts +0 -50
- package/src/media.ts +0 -198
- package/src/monitor.ts +0 -195
- package/src/outbound.ts +0 -43
- package/src/policy.ts +0 -131
- package/src/probe.ts +0 -75
- package/src/reply-dispatcher.ts +0 -207
- package/src/runtime.ts +0 -14
- package/src/sdk-bridge.ts +0 -268
- package/src/send.ts +0 -383
- package/src/targets.ts +0 -101
- package/src/types.ts +0 -96
package/dist/src/send.js
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { resolveMeetAccount } from "./accounts.js";
|
|
2
|
+
import { getMeetClient, createMeetClient } from "./client.js";
|
|
3
|
+
import { parseTargetToSessionInfo } from "./sdk-bridge.js";
|
|
4
|
+
import { getMeetRuntime } from "./runtime.js";
|
|
5
|
+
const MENTION_PATTERN = /<@(-?\d+)>|@(-?\d+)(?![\d])/g;
|
|
6
|
+
/**
|
|
7
|
+
* 根据文件扩展名推断 MIME 类型
|
|
8
|
+
* 支持常见图片格式,包括现代格式如 avif, webp, heic
|
|
9
|
+
*/
|
|
10
|
+
export function inferContentTypeFromFileName(fileName) {
|
|
11
|
+
const ext = fileName.toLowerCase().split(".").pop();
|
|
12
|
+
if (!ext)
|
|
13
|
+
return undefined;
|
|
14
|
+
const mimeMap = {
|
|
15
|
+
// 常见图片格式
|
|
16
|
+
jpg: "image/jpeg",
|
|
17
|
+
jpeg: "image/jpeg",
|
|
18
|
+
png: "image/png",
|
|
19
|
+
gif: "image/gif",
|
|
20
|
+
webp: "image/webp",
|
|
21
|
+
avif: "image/avif",
|
|
22
|
+
heic: "image/heic",
|
|
23
|
+
heif: "image/heif",
|
|
24
|
+
bmp: "image/bmp",
|
|
25
|
+
ico: "image/x-icon",
|
|
26
|
+
svg: "image/svg+xml",
|
|
27
|
+
tiff: "image/tiff",
|
|
28
|
+
tif: "image/tiff",
|
|
29
|
+
// 视频格式
|
|
30
|
+
mp4: "video/mp4",
|
|
31
|
+
webm: "video/webm",
|
|
32
|
+
mov: "video/quicktime",
|
|
33
|
+
avi: "video/x-msvideo",
|
|
34
|
+
mkv: "video/x-matroska",
|
|
35
|
+
// 音频格式
|
|
36
|
+
mp3: "audio/mpeg",
|
|
37
|
+
wav: "audio/wav",
|
|
38
|
+
ogg: "audio/ogg",
|
|
39
|
+
flac: "audio/flac",
|
|
40
|
+
m4a: "audio/mp4",
|
|
41
|
+
// 文档格式
|
|
42
|
+
pdf: "application/pdf",
|
|
43
|
+
json: "application/json",
|
|
44
|
+
xml: "application/xml",
|
|
45
|
+
};
|
|
46
|
+
return mimeMap[ext];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 根据 MIME 类型获取文件扩展名
|
|
50
|
+
*/
|
|
51
|
+
function getExtensionFromContentType(contentType) {
|
|
52
|
+
const mimeToExt = {
|
|
53
|
+
"image/jpeg": ".jpg",
|
|
54
|
+
"image/png": ".png",
|
|
55
|
+
"image/gif": ".gif",
|
|
56
|
+
"image/webp": ".webp",
|
|
57
|
+
"image/avif": ".avif",
|
|
58
|
+
"image/heic": ".heic",
|
|
59
|
+
"image/heif": ".heif",
|
|
60
|
+
"image/bmp": ".bmp",
|
|
61
|
+
"image/x-icon": ".ico",
|
|
62
|
+
"image/svg+xml": ".svg",
|
|
63
|
+
"image/tiff": ".tiff",
|
|
64
|
+
"video/mp4": ".mp4",
|
|
65
|
+
"video/webm": ".webm",
|
|
66
|
+
"video/quicktime": ".mov",
|
|
67
|
+
"video/x-msvideo": ".avi",
|
|
68
|
+
"video/x-matroska": ".mkv",
|
|
69
|
+
"audio/mpeg": ".mp3",
|
|
70
|
+
"audio/wav": ".wav",
|
|
71
|
+
"audio/ogg": ".ogg",
|
|
72
|
+
"audio/flac": ".flac",
|
|
73
|
+
"audio/mp4": ".m4a",
|
|
74
|
+
"application/pdf": ".pdf",
|
|
75
|
+
"application/json": ".json",
|
|
76
|
+
"application/xml": ".xml",
|
|
77
|
+
};
|
|
78
|
+
return mimeToExt[contentType];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 确保文件名有正确的扩展名
|
|
82
|
+
* 如果文件名没有扩展名,根据 contentType 添加
|
|
83
|
+
*/
|
|
84
|
+
function ensureFileNameExtension(fileName, contentType) {
|
|
85
|
+
// 检查是否已有扩展名
|
|
86
|
+
const hasExtension = /\.[a-zA-Z0-9]+$/.test(fileName);
|
|
87
|
+
if (hasExtension) {
|
|
88
|
+
return fileName;
|
|
89
|
+
}
|
|
90
|
+
// 根据 contentType 添加扩展名
|
|
91
|
+
const ext = getExtensionFromContentType(contentType);
|
|
92
|
+
if (ext) {
|
|
93
|
+
return fileName + ext;
|
|
94
|
+
}
|
|
95
|
+
return fileName;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 获取最终的 contentType
|
|
99
|
+
* 如果原始 contentType 缺失或为通用二进制流,则根据文件名推断
|
|
100
|
+
*/
|
|
101
|
+
export function resolveContentType(fileName, originalContentType) {
|
|
102
|
+
// 如果有明确的 MIME 类型(非通用二进制流),直接使用
|
|
103
|
+
if (originalContentType && originalContentType !== "application/octet-stream") {
|
|
104
|
+
return originalContentType;
|
|
105
|
+
}
|
|
106
|
+
// 根据文件名推断
|
|
107
|
+
const inferred = inferContentTypeFromFileName(fileName);
|
|
108
|
+
return inferred || originalContentType || "application/octet-stream";
|
|
109
|
+
}
|
|
110
|
+
let _logger = null;
|
|
111
|
+
export function setSendMessageLogger(logger) {
|
|
112
|
+
_logger = logger;
|
|
113
|
+
}
|
|
114
|
+
function log(message) {
|
|
115
|
+
if (_logger) {
|
|
116
|
+
_logger.log(message);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
console.log(message);
|
|
120
|
+
}
|
|
121
|
+
function logError(message) {
|
|
122
|
+
if (_logger) {
|
|
123
|
+
_logger.error(message);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
console.error(message);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export function extractAtIds(text) {
|
|
130
|
+
const atIds = [];
|
|
131
|
+
text.replace(MENTION_PATTERN, (_, id1, id2) => {
|
|
132
|
+
const id = id1 ?? id2;
|
|
133
|
+
if (id) {
|
|
134
|
+
atIds.push(Number(id));
|
|
135
|
+
}
|
|
136
|
+
return _;
|
|
137
|
+
});
|
|
138
|
+
return { text: text.trim(), atIds };
|
|
139
|
+
}
|
|
140
|
+
export async function sendMessageMeet(opts) {
|
|
141
|
+
const { cfg, to, text, accountId, atIds: explicitAtIds } = opts;
|
|
142
|
+
const account = resolveMeetAccount({ cfg, accountId });
|
|
143
|
+
if (!account.configured) {
|
|
144
|
+
throw new Error(`Meet account not configured: ${accountId ?? "default"}`);
|
|
145
|
+
}
|
|
146
|
+
// logLevel: info 时记录 AI 输出的原始内容,方便调试 mention 格式问题
|
|
147
|
+
// 默认为 silent,只有显式设置为 info 时才输出
|
|
148
|
+
if (account.config.logLevel === "info") {
|
|
149
|
+
console.log(`[${account.accountId}] AI output raw text: ${JSON.stringify(text)}`);
|
|
150
|
+
}
|
|
151
|
+
const token = account.apiToken;
|
|
152
|
+
if (!token) {
|
|
153
|
+
throw new Error("Meet API token not configured");
|
|
154
|
+
}
|
|
155
|
+
// 检查 token 是否未解析(仍是 secret ref 格式)
|
|
156
|
+
if (token.startsWith("${secret:")) {
|
|
157
|
+
const accountArg = accountId ? ` --account ${accountId}` : "";
|
|
158
|
+
throw new Error(`Meet API token not resolved (still a secret reference).\n` +
|
|
159
|
+
`\n` +
|
|
160
|
+
`This usually means:\n` +
|
|
161
|
+
`1. Gateway is not running in this workspace, or\n` +
|
|
162
|
+
`2. Token is already in use by another process (Meet only allows single login)\n` +
|
|
163
|
+
`\n` +
|
|
164
|
+
`Solutions:\n` +
|
|
165
|
+
`- If Gateway is running elsewhere: use \`message\` tool (OpenClaw will route through Gateway)\n` +
|
|
166
|
+
`- If you need to send files: put them in a shared location and ask someone with Gateway access to send\n` +
|
|
167
|
+
`- To start Gateway: openclaw gateway start${accountArg}\n` +
|
|
168
|
+
`\n` +
|
|
169
|
+
`AccountId: ${account.accountId}`);
|
|
170
|
+
}
|
|
171
|
+
const botUserId = token.split(":")[0];
|
|
172
|
+
if (!botUserId) {
|
|
173
|
+
throw new Error("Invalid Meet API token format");
|
|
174
|
+
}
|
|
175
|
+
// 如果 client 不存在则自动创建(支持 message tool 直接发送消息)
|
|
176
|
+
let bot = getMeetClient(account.accountId);
|
|
177
|
+
if (!bot) {
|
|
178
|
+
bot = createMeetClient(account);
|
|
179
|
+
}
|
|
180
|
+
const { text: cleanText, atIds: extractedAtIds } = extractAtIds(text);
|
|
181
|
+
const finalAtIds = explicitAtIds
|
|
182
|
+
? [...explicitAtIds, ...extractedAtIds]
|
|
183
|
+
: extractedAtIds;
|
|
184
|
+
log(`send message to=${to} atIds=${finalAtIds.join(",") || "none"}`);
|
|
185
|
+
const sessionInfo = parseTargetToSessionInfo(to, Number(botUserId));
|
|
186
|
+
try {
|
|
187
|
+
const result = await bot.sendMessage(sessionInfo, {
|
|
188
|
+
content: cleanText,
|
|
189
|
+
atIds: finalAtIds,
|
|
190
|
+
});
|
|
191
|
+
return {
|
|
192
|
+
messageId: String(result.msgContent?.seqId ?? 0),
|
|
193
|
+
chatId: to,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
logError(`send message error: ${String(error)}`);
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
export async function sendMediaMeet(opts) {
|
|
202
|
+
const { cfg, to, text, mediaUrl, mediaLocalRoots, accountId, onProgress } = opts;
|
|
203
|
+
const account = resolveMeetAccount({ cfg, accountId });
|
|
204
|
+
if (!account.configured) {
|
|
205
|
+
throw new Error(`Meet account not configured: ${accountId ?? "default"}`);
|
|
206
|
+
}
|
|
207
|
+
const token = account.apiToken;
|
|
208
|
+
if (!token) {
|
|
209
|
+
throw new Error("Meet API token not configured");
|
|
210
|
+
}
|
|
211
|
+
// 检查 token 是否未解析(仍是 secret ref 格式)
|
|
212
|
+
if (token.startsWith("${secret:")) {
|
|
213
|
+
const accountArg = accountId ? ` --account ${accountId}` : "";
|
|
214
|
+
throw new Error(`Meet API token not resolved (still a secret reference).\n` +
|
|
215
|
+
`\n` +
|
|
216
|
+
`This usually means:\n` +
|
|
217
|
+
`1. Gateway is not running in this workspace, or\n` +
|
|
218
|
+
`2. Token is already in use by another process (Meet only allows single login)\n` +
|
|
219
|
+
`\n` +
|
|
220
|
+
`Solutions:\n` +
|
|
221
|
+
`- If Gateway is running elsewhere: use \`message\` tool (OpenClaw will route through Gateway)\n` +
|
|
222
|
+
`- If you need to send files: put them in a shared location and ask someone with Gateway access to send\n` +
|
|
223
|
+
`- To start Gateway: openclaw gateway start${accountArg}\n` +
|
|
224
|
+
`\n` +
|
|
225
|
+
`AccountId: ${account.accountId}`);
|
|
226
|
+
}
|
|
227
|
+
const botUserId = token.split(":")[0];
|
|
228
|
+
if (!botUserId) {
|
|
229
|
+
throw new Error("Invalid Meet API token format");
|
|
230
|
+
}
|
|
231
|
+
// 如果 client 不存在则自动创建(支持 message tool 直接发送消息)
|
|
232
|
+
let bot = getMeetClient(account.accountId);
|
|
233
|
+
if (!bot) {
|
|
234
|
+
bot = createMeetClient(account);
|
|
235
|
+
}
|
|
236
|
+
const runtime = getMeetRuntime();
|
|
237
|
+
const maxBytes = account.config.mediaMaxMb
|
|
238
|
+
? account.config.mediaMaxMb * 1024 * 1024
|
|
239
|
+
: undefined;
|
|
240
|
+
log(`loading media: ${mediaUrl}`);
|
|
241
|
+
const media = await runtime.media.loadWebMedia(mediaUrl, {
|
|
242
|
+
maxBytes,
|
|
243
|
+
localRoots: mediaLocalRoots,
|
|
244
|
+
});
|
|
245
|
+
// 检查媒体大小限制
|
|
246
|
+
if (maxBytes && media.buffer.length > maxBytes) {
|
|
247
|
+
throw new Error(`Media file too large: ${media.buffer.length} bytes (max: ${maxBytes})`);
|
|
248
|
+
}
|
|
249
|
+
const sessionInfo = parseTargetToSessionInfo(to, Number(botUserId));
|
|
250
|
+
const rawFileName = media.fileName || "file";
|
|
251
|
+
const contentType = resolveContentType(rawFileName, media.contentType);
|
|
252
|
+
// 确保文件名有正确的扩展名
|
|
253
|
+
const fileName = ensureFileNameExtension(rawFileName, contentType);
|
|
254
|
+
log(`sending media to=${to} fileName=${fileName} size=${media.buffer.length} contentType=${contentType}`);
|
|
255
|
+
// 包装进度回调
|
|
256
|
+
const progressCallback = onProgress
|
|
257
|
+
? (progress) => {
|
|
258
|
+
onProgress({
|
|
259
|
+
percent: progress.percent,
|
|
260
|
+
loaded: progress.loaded,
|
|
261
|
+
total: progress.total,
|
|
262
|
+
speedPerSecond: progress.speedPerSecond,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
: undefined;
|
|
266
|
+
try {
|
|
267
|
+
const result = await bot.sendMedia(sessionInfo, {
|
|
268
|
+
buffer: media.buffer,
|
|
269
|
+
fileName,
|
|
270
|
+
contentType,
|
|
271
|
+
content: text || "",
|
|
272
|
+
onProgress: progressCallback,
|
|
273
|
+
});
|
|
274
|
+
return {
|
|
275
|
+
messageId: String(result.msgContent?.seqId ?? 0),
|
|
276
|
+
chatId: to,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
logError(`send media error: ${String(error)}`);
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
export async function resolveMeetMedia(mediaUrl, opts) {
|
|
285
|
+
const runtime = getMeetRuntime();
|
|
286
|
+
let maxBytes;
|
|
287
|
+
if (opts?.cfg) {
|
|
288
|
+
const account = resolveMeetAccount({
|
|
289
|
+
cfg: opts.cfg,
|
|
290
|
+
accountId: opts.accountId,
|
|
291
|
+
});
|
|
292
|
+
if (account.config.mediaMaxMb) {
|
|
293
|
+
maxBytes = account.config.mediaMaxMb * 1024 * 1024;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const media = await runtime.media.loadWebMedia(mediaUrl, {
|
|
297
|
+
maxBytes,
|
|
298
|
+
localRoots: opts?.mediaLocalRoots,
|
|
299
|
+
});
|
|
300
|
+
return {
|
|
301
|
+
path: media.fileName || "file",
|
|
302
|
+
contentType: media.contentType,
|
|
303
|
+
placeholder: `[Meet file: ${media.fileName || "file"}]`,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
export async function getMessageMeet(opts) {
|
|
307
|
+
const { cfg, accountId } = opts;
|
|
308
|
+
const account = resolveMeetAccount({ cfg, accountId });
|
|
309
|
+
if (!account.configured) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
const bot = getMeetClient(account.accountId);
|
|
313
|
+
if (!bot) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type MeetTargetKind = "user" | "channel";
|
|
2
|
+
export type MeetTarget = {
|
|
3
|
+
kind: MeetTargetKind;
|
|
4
|
+
id: string;
|
|
5
|
+
raw: string;
|
|
6
|
+
normalized: string;
|
|
7
|
+
};
|
|
8
|
+
export type MeetTargetParseOptions = {
|
|
9
|
+
defaultKind?: MeetTargetKind;
|
|
10
|
+
ambiguousMessage?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function parseMeetTarget(raw: string, options?: MeetTargetParseOptions): MeetTarget | undefined;
|
|
13
|
+
export declare function resolveMeetChannelId(raw: string): string;
|
|
14
|
+
export declare function formatMeetTarget(target: MeetTarget): string;
|
|
15
|
+
export declare function looksLikeMeetId(input: string): boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
function buildMessagingTarget(kind, id, raw) {
|
|
2
|
+
return { kind, id, raw, normalized: `${kind}:${id}` };
|
|
3
|
+
}
|
|
4
|
+
function parseMentionPrefixOrAtUserTarget(params) {
|
|
5
|
+
const { raw, mentionPattern, prefixes, atUserPattern } = params;
|
|
6
|
+
const mentionMatch = raw.match(mentionPattern);
|
|
7
|
+
if (mentionMatch) {
|
|
8
|
+
return buildMessagingTarget("user", mentionMatch[2], raw);
|
|
9
|
+
}
|
|
10
|
+
for (const { prefix, kind } of prefixes) {
|
|
11
|
+
if (raw.startsWith(prefix)) {
|
|
12
|
+
const id = raw.slice(prefix.length);
|
|
13
|
+
if (id) {
|
|
14
|
+
return buildMessagingTarget(kind, id, raw);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (atUserPattern.test(raw)) {
|
|
19
|
+
return buildMessagingTarget("user", raw, raw);
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
export function parseMeetTarget(raw, options = {}) {
|
|
24
|
+
const trimmed = raw.trim();
|
|
25
|
+
if (!trimmed) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const userTarget = parseMentionPrefixOrAtUserTarget({
|
|
29
|
+
raw: trimmed,
|
|
30
|
+
mentionPattern: /^@\[([^\]]+)\]\((\d+)\)$/,
|
|
31
|
+
prefixes: [
|
|
32
|
+
{ prefix: "user:", kind: "user" },
|
|
33
|
+
{ prefix: "channel:", kind: "channel" },
|
|
34
|
+
{ prefix: "meet:", kind: "user" },
|
|
35
|
+
],
|
|
36
|
+
atUserPattern: /^\d+$/,
|
|
37
|
+
atUserErrorMessage: "Meet DMs require a user id (use user:<id>)",
|
|
38
|
+
});
|
|
39
|
+
if (userTarget) {
|
|
40
|
+
return userTarget;
|
|
41
|
+
}
|
|
42
|
+
if (/^\d+$/.test(trimmed)) {
|
|
43
|
+
if (options.defaultKind) {
|
|
44
|
+
return buildMessagingTarget(options.defaultKind, trimmed, trimmed);
|
|
45
|
+
}
|
|
46
|
+
throw new Error(options.ambiguousMessage ??
|
|
47
|
+
`Ambiguous Meet recipient "${trimmed}". Use "user:${trimmed}" for DMs or "channel:${trimmed}" for channel messages.`);
|
|
48
|
+
}
|
|
49
|
+
return buildMessagingTarget("channel", trimmed, trimmed);
|
|
50
|
+
}
|
|
51
|
+
export function resolveMeetChannelId(raw) {
|
|
52
|
+
const target = parseMeetTarget(raw, { defaultKind: "channel" });
|
|
53
|
+
if (!target) {
|
|
54
|
+
throw new Error(`Invalid Meet channel: ${raw}`);
|
|
55
|
+
}
|
|
56
|
+
return target.id;
|
|
57
|
+
}
|
|
58
|
+
export function formatMeetTarget(target) {
|
|
59
|
+
return `${target.kind}:${target.id}`;
|
|
60
|
+
}
|
|
61
|
+
export function looksLikeMeetId(input) {
|
|
62
|
+
return /^\d+$/.test(input) || /^(user:|channel:|meet:)/.test(input);
|
|
63
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { SessionInfo, AttachmentInfo, UploadProgress } from "@meet-im/meet-bot-jssdk";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import { MeetConfigSchema, MeetAccountConfigSchema, MeetChannelConfigSchema, MeetGroupConfigSchema } from "./config-schema.js";
|
|
4
|
+
export type MeetConfig = z.infer<typeof MeetConfigSchema>;
|
|
5
|
+
export type MeetAccountConfig = z.infer<typeof MeetAccountConfigSchema>;
|
|
6
|
+
export type MeetChannelConfig = z.infer<typeof MeetChannelConfigSchema>;
|
|
7
|
+
export type MeetGroupConfig = z.infer<typeof MeetGroupConfigSchema>;
|
|
8
|
+
export type { AttachmentInfo, UploadProgress };
|
|
9
|
+
export type ResolvedMeetAccount = {
|
|
10
|
+
accountId: string;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
configured: boolean;
|
|
13
|
+
name?: string;
|
|
14
|
+
apiEndpoint?: string;
|
|
15
|
+
apiToken?: string;
|
|
16
|
+
config: MeetConfig;
|
|
17
|
+
};
|
|
18
|
+
export type MeetReplyContext = {
|
|
19
|
+
messageId: string;
|
|
20
|
+
senderId?: string;
|
|
21
|
+
senderName?: string;
|
|
22
|
+
content?: string;
|
|
23
|
+
timestamp?: number;
|
|
24
|
+
mediaPaths?: string[];
|
|
25
|
+
};
|
|
26
|
+
export type MeetMessageContext = {
|
|
27
|
+
chatId: string;
|
|
28
|
+
messageId: string;
|
|
29
|
+
senderId: string;
|
|
30
|
+
senderOpenId: string;
|
|
31
|
+
senderName?: string;
|
|
32
|
+
chatType: "direct" | "channel";
|
|
33
|
+
mentionedBot: boolean;
|
|
34
|
+
hasAnyMention?: boolean;
|
|
35
|
+
content: string;
|
|
36
|
+
contentType: string;
|
|
37
|
+
placeholder?: string;
|
|
38
|
+
rawPayload?: string;
|
|
39
|
+
sessionInfo: SessionInfo;
|
|
40
|
+
timestamp?: number;
|
|
41
|
+
atIds?: number[];
|
|
42
|
+
replyContext?: MeetReplyContext;
|
|
43
|
+
media?: MeetMediaAttachment[];
|
|
44
|
+
};
|
|
45
|
+
export type MeetSendResult = {
|
|
46
|
+
messageId: string;
|
|
47
|
+
chatId: string;
|
|
48
|
+
};
|
|
49
|
+
export type MeetProbeResult = {
|
|
50
|
+
ok: boolean;
|
|
51
|
+
error?: string;
|
|
52
|
+
botId?: string;
|
|
53
|
+
};
|
|
54
|
+
export type MeetMediaInfo = {
|
|
55
|
+
path: string;
|
|
56
|
+
contentType?: string;
|
|
57
|
+
placeholder: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* 入站消息中的媒体附件信息
|
|
61
|
+
*/
|
|
62
|
+
export type MeetMediaAttachment = {
|
|
63
|
+
fileId: string | number;
|
|
64
|
+
fileName?: string;
|
|
65
|
+
fileSize?: number;
|
|
66
|
+
mimeType?: string;
|
|
67
|
+
fileUrl?: string;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* 文件上传结果
|
|
71
|
+
*/
|
|
72
|
+
export type MeetUploadResult = {
|
|
73
|
+
fileID: number;
|
|
74
|
+
path: string;
|
|
75
|
+
size: number;
|
|
76
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,6 +2,122 @@
|
|
|
2
2
|
"id": "meet",
|
|
3
3
|
"channels": ["meet"],
|
|
4
4
|
"skills": ["./skills"],
|
|
5
|
+
"channelConfigs": {
|
|
6
|
+
"meet": {
|
|
7
|
+
"label": "Meet",
|
|
8
|
+
"description": "Meet channel configuration",
|
|
9
|
+
"schema": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"additionalProperties": false,
|
|
12
|
+
"properties": {
|
|
13
|
+
"enabled": { "type": "boolean" },
|
|
14
|
+
"defaultAccount": { "type": "string" },
|
|
15
|
+
"apiEndpoint": { "type": "string" },
|
|
16
|
+
"token": { "type": "string" },
|
|
17
|
+
"apiToken": { "type": "string" },
|
|
18
|
+
"pollTimeout": { "type": "number", "minimum": 1000, "maximum": 300000 },
|
|
19
|
+
"pollLimit": { "type": "number", "minimum": 1, "maximum": 1000 },
|
|
20
|
+
"logLevel": { "type": "string", "enum": ["silent", "info"] },
|
|
21
|
+
"dmPolicy": { "type": "string", "enum": ["open", "pairing", "allowlist"] },
|
|
22
|
+
"allowFrom": {
|
|
23
|
+
"type": "array",
|
|
24
|
+
"items": { "anyOf": [{ "type": "string" }, { "type": "number" }] }
|
|
25
|
+
},
|
|
26
|
+
"groupPolicy": { "type": "string", "enum": ["open", "allowlist", "disabled"] },
|
|
27
|
+
"groupAllowFrom": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": { "anyOf": [{ "type": "string" }, { "type": "number" }] }
|
|
30
|
+
},
|
|
31
|
+
"requireMention": { "type": "boolean" },
|
|
32
|
+
"systemPrompt": { "type": "string" },
|
|
33
|
+
"channels": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"additionalProperties": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"properties": {
|
|
39
|
+
"enabled": { "type": "boolean" },
|
|
40
|
+
"systemPrompt": { "type": "string" },
|
|
41
|
+
"requireMention": { "type": "boolean" }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"groups": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"additionalProperties": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"properties": {
|
|
51
|
+
"enabled": { "type": "boolean" },
|
|
52
|
+
"name": { "type": "string" },
|
|
53
|
+
"requireMention": { "type": "boolean" },
|
|
54
|
+
"systemPrompt": { "type": "string" },
|
|
55
|
+
"users": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"items": { "anyOf": [{ "type": "string" }, { "type": "number" }] }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"historyLimit": { "type": "number", "minimum": 0 },
|
|
63
|
+
"dmHistoryLimit": { "type": "number", "minimum": 0 },
|
|
64
|
+
"textChunkLimit": { "type": "number", "minimum": 1 },
|
|
65
|
+
"mediaMaxMb": { "type": "number", "minimum": 0 },
|
|
66
|
+
"accounts": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"additionalProperties": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"additionalProperties": false,
|
|
71
|
+
"properties": {
|
|
72
|
+
"enabled": { "type": "boolean" },
|
|
73
|
+
"name": { "type": "string" },
|
|
74
|
+
"apiEndpoint": { "type": "string" },
|
|
75
|
+
"token": { "type": "string" },
|
|
76
|
+
"apiToken": { "type": "string" },
|
|
77
|
+
"pollTimeout": { "type": "number", "minimum": 1000, "maximum": 300000 },
|
|
78
|
+
"pollLimit": { "type": "number", "minimum": 1, "maximum": 1000 },
|
|
79
|
+
"logLevel": { "type": "string", "enum": ["silent", "info"] },
|
|
80
|
+
"dmPolicy": { "type": "string", "enum": ["open", "pairing", "allowlist"] },
|
|
81
|
+
"allowFrom": {
|
|
82
|
+
"type": "array",
|
|
83
|
+
"items": { "anyOf": [{ "type": "string" }, { "type": "number" }] }
|
|
84
|
+
},
|
|
85
|
+
"groupPolicy": { "type": "string", "enum": ["open", "allowlist", "disabled"] },
|
|
86
|
+
"groupAllowFrom": {
|
|
87
|
+
"type": "array",
|
|
88
|
+
"items": { "anyOf": [{ "type": "string" }, { "type": "number" }] }
|
|
89
|
+
},
|
|
90
|
+
"requireMention": { "type": "boolean" },
|
|
91
|
+
"systemPrompt": { "type": "string" },
|
|
92
|
+
"historyLimit": { "type": "number", "minimum": 0 },
|
|
93
|
+
"dmHistoryLimit": { "type": "number", "minimum": 0 },
|
|
94
|
+
"textChunkLimit": { "type": "number", "minimum": 1 },
|
|
95
|
+
"mediaMaxMb": { "type": "number", "minimum": 0 },
|
|
96
|
+
"groups": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"additionalProperties": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"additionalProperties": false,
|
|
101
|
+
"properties": {
|
|
102
|
+
"enabled": { "type": "boolean" },
|
|
103
|
+
"name": { "type": "string" },
|
|
104
|
+
"requireMention": { "type": "boolean" },
|
|
105
|
+
"systemPrompt": { "type": "string" },
|
|
106
|
+
"users": {
|
|
107
|
+
"type": "array",
|
|
108
|
+
"items": { "anyOf": [{ "type": "string" }, { "type": "number" }] }
|
|
109
|
+
},
|
|
110
|
+
"groupPolicy": { "type": "string", "enum": ["open", "allowlist", "disabled"] }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
5
121
|
"configSchema": {
|
|
6
122
|
"type": "object",
|
|
7
123
|
"additionalProperties": false,
|