@meet-im/meet 3.2.5 → 3.3.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 +7 -0
- package/dist/src/config-schema.d.ts +15 -0
- package/dist/src/config-schema.js +2 -0
- package/dist/src/generated/plugin-build-meta.d.ts +1 -1
- package/dist/src/generated/plugin-build-meta.js +1 -1
- package/dist/src/reply-dispatcher.d.ts +5 -1
- package/dist/src/reply-dispatcher.js +66 -4
- package/dist/src/typing.d.ts +24 -0
- package/dist/src/typing.js +44 -0
- package/package.json +3 -3
package/dist/src/bot.js
CHANGED
|
@@ -381,6 +381,8 @@ export async function handleMeetMessage(params) {
|
|
|
381
381
|
});
|
|
382
382
|
const { createMeetReplyDispatcher } = await import("./reply-dispatcher.js");
|
|
383
383
|
const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
|
|
384
|
+
// 默认 typingMode 为 "instant"
|
|
385
|
+
const effectiveTypingMode = meetCfg.typingMode ?? "instant";
|
|
384
386
|
const { dispatcher, replyOptions, markDispatchIdle, markRunComplete } = await createMeetReplyDispatcher({
|
|
385
387
|
cfg,
|
|
386
388
|
agentId: route.agentId,
|
|
@@ -391,6 +393,11 @@ export async function handleMeetMessage(params) {
|
|
|
391
393
|
bot,
|
|
392
394
|
botUserId,
|
|
393
395
|
mediaLocalRoots,
|
|
396
|
+
// typing 相关参数
|
|
397
|
+
sessionInfo: ctx.sessionInfo,
|
|
398
|
+
apiToken: account.apiToken,
|
|
399
|
+
apiEndpoint: account.apiEndpoint,
|
|
400
|
+
typingMode: effectiveTypingMode,
|
|
394
401
|
});
|
|
395
402
|
log(`[${accountId}]: dispatch ctx replyToId=${inboundCtx.ReplyToId ?? "undefined"} replyToBody=${JSON.stringify(inboundCtx.ReplyToBody ?? "")} rawBody=${JSON.stringify(inboundCtx.RawBody ?? "")} commandBody=${JSON.stringify(inboundCtx.CommandBody ?? "")} bodyForAgent=${JSON.stringify(inboundCtx.BodyForAgent ?? "")}`);
|
|
396
403
|
log(`[${accountId}]: dispatching to AI agent=${route.agentId} session=${route.sessionKey} history=${inboundHistory?.length ?? 0}`);
|
|
@@ -46,6 +46,11 @@ export declare const MeetAccountConfigSchema: z.ZodObject<{
|
|
|
46
46
|
dmHistoryLimit: z.ZodOptional<z.ZodNumber>;
|
|
47
47
|
textChunkLimit: z.ZodOptional<z.ZodNumber>;
|
|
48
48
|
mediaMaxMb: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
typingMode: z.ZodOptional<z.ZodEnum<{
|
|
50
|
+
message: "message";
|
|
51
|
+
none: "none";
|
|
52
|
+
instant: "instant";
|
|
53
|
+
}>>;
|
|
49
54
|
groups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
50
55
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
51
56
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -106,6 +111,11 @@ export declare const MeetConfigSchema: z.ZodObject<{
|
|
|
106
111
|
dmHistoryLimit: z.ZodOptional<z.ZodNumber>;
|
|
107
112
|
textChunkLimit: z.ZodOptional<z.ZodNumber>;
|
|
108
113
|
mediaMaxMb: z.ZodOptional<z.ZodNumber>;
|
|
114
|
+
typingMode: z.ZodOptional<z.ZodEnum<{
|
|
115
|
+
message: "message";
|
|
116
|
+
none: "none";
|
|
117
|
+
instant: "instant";
|
|
118
|
+
}>>;
|
|
109
119
|
accounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
110
120
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
111
121
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -136,6 +146,11 @@ export declare const MeetConfigSchema: z.ZodObject<{
|
|
|
136
146
|
dmHistoryLimit: z.ZodOptional<z.ZodNumber>;
|
|
137
147
|
textChunkLimit: z.ZodOptional<z.ZodNumber>;
|
|
138
148
|
mediaMaxMb: z.ZodOptional<z.ZodNumber>;
|
|
149
|
+
typingMode: z.ZodOptional<z.ZodEnum<{
|
|
150
|
+
message: "message";
|
|
151
|
+
none: "none";
|
|
152
|
+
instant: "instant";
|
|
153
|
+
}>>;
|
|
139
154
|
groups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
140
155
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
141
156
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -32,6 +32,7 @@ export const MeetAccountConfigSchema = z.object({
|
|
|
32
32
|
dmHistoryLimit: z.number().min(0).optional(),
|
|
33
33
|
textChunkLimit: z.number().min(1).optional(),
|
|
34
34
|
mediaMaxMb: z.number().min(0).optional(),
|
|
35
|
+
typingMode: z.enum(["none", "instant", "message"]).optional(),
|
|
35
36
|
groups: z.record(z.string(), MeetGroupConfigSchema).optional(),
|
|
36
37
|
});
|
|
37
38
|
export const MeetConfigSchema = z.object({
|
|
@@ -55,6 +56,7 @@ export const MeetConfigSchema = z.object({
|
|
|
55
56
|
dmHistoryLimit: z.number().min(0).optional(),
|
|
56
57
|
textChunkLimit: z.number().min(1).optional(),
|
|
57
58
|
mediaMaxMb: z.number().min(0).optional(),
|
|
59
|
+
typingMode: z.enum(["none", "instant", "message"]).optional(),
|
|
58
60
|
accounts: z.record(z.string(), MeetAccountConfigSchema).optional(),
|
|
59
61
|
});
|
|
60
62
|
export const MeetPluginConfigSchema = buildChannelConfigSchema(MeetConfigSchema);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const MEET_PLUGIN_VERSION = "3.
|
|
1
|
+
export declare const MEET_PLUGIN_VERSION = "3.3.1";
|
|
2
2
|
export declare const MEET_OPENCLAW_VERSION = "2026.5.18";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const MEET_PLUGIN_VERSION = "3.
|
|
1
|
+
export const MEET_PLUGIN_VERSION = "3.3.1";
|
|
2
2
|
export const MEET_OPENCLAW_VERSION = "2026.5.18";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MeetBot } from "@meet-im/meet-bot-jssdk";
|
|
1
|
+
import type { MeetBot, SessionInfo } from "@meet-im/meet-bot-jssdk";
|
|
2
2
|
import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
3
3
|
/**
|
|
4
4
|
* 保护 mention 格式在分片后不被截断
|
|
@@ -20,6 +20,10 @@ export type CreateMeetReplyDispatcherOpts = {
|
|
|
20
20
|
bot: MeetBot;
|
|
21
21
|
botUserId: string;
|
|
22
22
|
mediaLocalRoots?: readonly string[];
|
|
23
|
+
sessionInfo?: SessionInfo;
|
|
24
|
+
apiToken?: string;
|
|
25
|
+
apiEndpoint?: string;
|
|
26
|
+
typingMode?: "none" | "instant" | "message";
|
|
23
27
|
};
|
|
24
28
|
export declare function createMeetReplyDispatcher(opts: CreateMeetReplyDispatcherOpts): Promise<{
|
|
25
29
|
dispatcher: import("node_modules/openclaw/dist/plugin-sdk/src/auto-reply/reply/reply-dispatcher.types.js").ReplyDispatcher;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { resolveChannelSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-reply-pipeline";
|
|
2
2
|
import { createReplyPrefixContext } from "openclaw/plugin-sdk/channel-runtime";
|
|
3
|
+
import { createTypingCallbacks } from "openclaw/plugin-sdk/channel-message";
|
|
3
4
|
import { getMeetRuntime } from "./runtime.js";
|
|
4
5
|
import { sendMessageMeet, sendMediaMeet } from "./send.js";
|
|
6
|
+
import { sendTypingMeet, stopTypingMeet } from "./typing.js";
|
|
5
7
|
function resolveMeetConversationType(chatId) {
|
|
6
8
|
if (chatId.startsWith("channel:")) {
|
|
7
9
|
return "group";
|
|
@@ -73,7 +75,7 @@ export function protectMentionsInChunks(chunks) {
|
|
|
73
75
|
return result;
|
|
74
76
|
}
|
|
75
77
|
export async function createMeetReplyDispatcher(opts) {
|
|
76
|
-
const { cfg, agentId, chatId, replyToMessageId, accountId, mediaLocalRoots } = opts;
|
|
78
|
+
const { cfg, agentId, chatId, replyToMessageId, accountId, mediaLocalRoots, sessionInfo, apiToken, apiEndpoint, typingMode } = opts;
|
|
77
79
|
const core = getMeetRuntime();
|
|
78
80
|
const textChunkLimit = core.channel.text.resolveTextChunkLimit(cfg, "meet", accountId, {
|
|
79
81
|
fallbackLimit: 4000,
|
|
@@ -87,6 +89,57 @@ export async function createMeetReplyDispatcher(opts) {
|
|
|
87
89
|
ctx: { ChatType: chatType },
|
|
88
90
|
})
|
|
89
91
|
: undefined;
|
|
92
|
+
// 创建 typing callbacks(如果配置了 typing 且有必要参数)
|
|
93
|
+
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
|
+
};
|
|
103
|
+
const typingCallbacks = hasTypingParams
|
|
104
|
+
? createTypingCallbacks({
|
|
105
|
+
start: async () => {
|
|
106
|
+
const result = await enqueueTypingRequest("start", async () => await sendTypingMeet({
|
|
107
|
+
accountId,
|
|
108
|
+
chatId,
|
|
109
|
+
chatType: chatType ?? "channel",
|
|
110
|
+
sessionInfo,
|
|
111
|
+
token: apiToken,
|
|
112
|
+
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}`);
|
|
118
|
+
},
|
|
119
|
+
stop: async () => {
|
|
120
|
+
await enqueueTypingRequest("stop", async () => await stopTypingMeet({
|
|
121
|
+
accountId,
|
|
122
|
+
chatId,
|
|
123
|
+
chatType: chatType ?? "channel",
|
|
124
|
+
sessionInfo,
|
|
125
|
+
token: apiToken,
|
|
126
|
+
apiEndpoint,
|
|
127
|
+
}));
|
|
128
|
+
},
|
|
129
|
+
onStartError: (err) => {
|
|
130
|
+
opts.runtime.error?.(`[${accountId}][${chatId}]: typing start failed: ${String(err)}`);
|
|
131
|
+
},
|
|
132
|
+
onStopError: (err) => {
|
|
133
|
+
opts.runtime.error?.(`[${accountId}][${chatId}]: typing stop failed: ${String(err)}`);
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
: undefined;
|
|
137
|
+
// instant 模式:立即启动 typing(不等 AI 开始响应)
|
|
138
|
+
if (typingMode === "instant" && typingCallbacks) {
|
|
139
|
+
void typingCallbacks.onReplyStart().catch((err) => {
|
|
140
|
+
opts.runtime.error?.(`[${accountId}][${chatId}]: instant typing start failed: ${String(err)}`);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
90
143
|
const { dispatcher, replyOptions, markDispatchIdle, markRunComplete } = core.channel.reply.createReplyDispatcherWithTyping({
|
|
91
144
|
responsePrefix: prefixContext.responsePrefix,
|
|
92
145
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
|
@@ -96,7 +149,18 @@ export async function createMeetReplyDispatcher(opts) {
|
|
|
96
149
|
conversationType: resolveMeetConversationType(chatId),
|
|
97
150
|
},
|
|
98
151
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, agentId),
|
|
99
|
-
|
|
152
|
+
// instant 和 message 模式都传递 typingCallbacks(用于 onIdle/onCleanup)
|
|
153
|
+
typingCallbacks: typingCallbacks,
|
|
154
|
+
// instant 模式:首发已在外层手动触发,这里传空函数阻止 fallback
|
|
155
|
+
// message 模式:onReplyStart 在 AI 开始响应时触发
|
|
156
|
+
onReplyStart: typingCallbacks
|
|
157
|
+
? typingMode === "instant"
|
|
158
|
+
? async () => { } // 阻止 fallback,首发已手动触发
|
|
159
|
+
: async () => await typingCallbacks.onReplyStart()
|
|
160
|
+
: undefined,
|
|
161
|
+
onIdle: typingCallbacks?.onIdle,
|
|
162
|
+
onCleanup: () => {
|
|
163
|
+
typingCallbacks?.onCleanup?.();
|
|
100
164
|
},
|
|
101
165
|
deliver: async (payload, _info) => {
|
|
102
166
|
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}`);
|
|
@@ -192,8 +256,6 @@ export async function createMeetReplyDispatcher(opts) {
|
|
|
192
256
|
// 忽略错误消息发送失败
|
|
193
257
|
}
|
|
194
258
|
},
|
|
195
|
-
onIdle: async () => {
|
|
196
|
-
},
|
|
197
259
|
});
|
|
198
260
|
return {
|
|
199
261
|
dispatcher,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SessionInfo } from "@meet-im/meet-bot-jssdk";
|
|
2
|
+
export type SendTypingParams = {
|
|
3
|
+
accountId: string;
|
|
4
|
+
chatId: string;
|
|
5
|
+
chatType: "direct" | "channel";
|
|
6
|
+
sessionInfo?: SessionInfo;
|
|
7
|
+
token?: string;
|
|
8
|
+
apiEndpoint?: string;
|
|
9
|
+
};
|
|
10
|
+
export type TypingResult = {
|
|
11
|
+
ok: true;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
reason: "unsupported" | "error";
|
|
15
|
+
error?: unknown;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* 发送 typing 指示器
|
|
19
|
+
*/
|
|
20
|
+
export declare function sendTypingMeet(params: SendTypingParams): Promise<TypingResult>;
|
|
21
|
+
/**
|
|
22
|
+
* 停止 typing 指示器
|
|
23
|
+
*/
|
|
24
|
+
export declare function stopTypingMeet(params: SendTypingParams): Promise<TypingResult>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { sendUserTyping, TYPING_ACTION } from "@meet-im/meet-bot-jssdk";
|
|
2
|
+
/**
|
|
3
|
+
* 发送 typing 指示器
|
|
4
|
+
*/
|
|
5
|
+
export async function sendTypingMeet(params) {
|
|
6
|
+
const { sessionInfo, token, apiEndpoint } = params;
|
|
7
|
+
// 缺少必要参数时返回 unsupported
|
|
8
|
+
if (!sessionInfo || !token) {
|
|
9
|
+
return { ok: false, reason: "unsupported" };
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
await sendUserTyping({
|
|
13
|
+
token,
|
|
14
|
+
baseUrl: apiEndpoint,
|
|
15
|
+
sessionInfo,
|
|
16
|
+
action: TYPING_ACTION.TYPING,
|
|
17
|
+
});
|
|
18
|
+
return { ok: true };
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return { ok: false, reason: "error", error };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 停止 typing 指示器
|
|
26
|
+
*/
|
|
27
|
+
export async function stopTypingMeet(params) {
|
|
28
|
+
const { sessionInfo, token, apiEndpoint } = params;
|
|
29
|
+
if (!sessionInfo || !token) {
|
|
30
|
+
return { ok: false, reason: "unsupported" };
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
await sendUserTyping({
|
|
34
|
+
token,
|
|
35
|
+
baseUrl: apiEndpoint,
|
|
36
|
+
sessionInfo,
|
|
37
|
+
action: TYPING_ACTION.STOPPED,
|
|
38
|
+
});
|
|
39
|
+
return { ok: true };
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
return { ok: false, reason: "error", error };
|
|
43
|
+
}
|
|
44
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meet-im/meet",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw Meet channel plugin",
|
|
6
6
|
"scripts": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@meet-im/meet-bot-jssdk": "^1.
|
|
58
|
+
"@meet-im/meet-bot-jssdk": "^1.3.0",
|
|
59
59
|
"zod": "^4.4.3"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"vitest": "4.1.5"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"openclaw": "2026.5.18"
|
|
71
|
+
"openclaw": ">=2026.5.18"
|
|
72
72
|
},
|
|
73
73
|
"publishConfig": {
|
|
74
74
|
"access": "public"
|