@max1874/feishu 0.2.24 → 0.2.26
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 +1 -1
- package/src/bot.ts +1 -0
- package/src/channel.ts +1 -0
- package/src/docx.ts +109 -1
- package/src/reply-dispatcher.ts +5 -1
- package/src/send.ts +10 -4
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -775,6 +775,7 @@ export async function handleFeishuMessage(params: {
|
|
|
775
775
|
runtime: runtime as RuntimeEnv,
|
|
776
776
|
chatId: ctx.chatId,
|
|
777
777
|
replyToMessageId: effectiveReplyToMessageId,
|
|
778
|
+
replyInThread: !!effectiveReplyToMessageId,
|
|
778
779
|
mentionTargets: ctx.mentionTargets,
|
|
779
780
|
});
|
|
780
781
|
|
package/src/channel.ts
CHANGED
|
@@ -55,6 +55,7 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
|
|
55
55
|
messageToolHints: () => [
|
|
56
56
|
"- Feishu targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:open_id` or `chat:chat_id`.",
|
|
57
57
|
"- Feishu supports interactive cards for rich messages.",
|
|
58
|
+
"- Use feishu_chat_messages to read message history. Omit chat_id to read from the current conversation.",
|
|
58
59
|
],
|
|
59
60
|
},
|
|
60
61
|
groups: {
|
package/src/docx.ts
CHANGED
|
@@ -1247,5 +1247,113 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
|
|
|
1247
1247
|
{ name: "feishu_app_scopes" },
|
|
1248
1248
|
);
|
|
1249
1249
|
|
|
1250
|
-
|
|
1250
|
+
// Tool 15: feishu_chat_messages
|
|
1251
|
+
const ChatMessagesSchema = Type.Object({
|
|
1252
|
+
chat_id: Type.Optional(
|
|
1253
|
+
Type.String({
|
|
1254
|
+
description: "Chat ID (oc_xxx). Omit to use the current conversation.",
|
|
1255
|
+
}),
|
|
1256
|
+
),
|
|
1257
|
+
start_time: Type.Optional(
|
|
1258
|
+
Type.String({ description: "Start time, Unix timestamp in seconds" }),
|
|
1259
|
+
),
|
|
1260
|
+
end_time: Type.Optional(
|
|
1261
|
+
Type.String({ description: "End time, Unix timestamp in seconds" }),
|
|
1262
|
+
),
|
|
1263
|
+
sort_type: Type.Optional(
|
|
1264
|
+
Type.Union([Type.Literal("ByCreateTimeAsc"), Type.Literal("ByCreateTimeDesc")], {
|
|
1265
|
+
description: "Sort order (default: ByCreateTimeDesc)",
|
|
1266
|
+
}),
|
|
1267
|
+
),
|
|
1268
|
+
page_size: Type.Optional(
|
|
1269
|
+
Type.Number({ description: "Page size, 1-50 (default 20)" }),
|
|
1270
|
+
),
|
|
1271
|
+
page_token: Type.Optional(
|
|
1272
|
+
Type.String({ description: "Pagination token from previous response" }),
|
|
1273
|
+
),
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
api.registerTool(
|
|
1277
|
+
{
|
|
1278
|
+
name: "feishu_chat_messages",
|
|
1279
|
+
label: "Feishu Chat Messages",
|
|
1280
|
+
description:
|
|
1281
|
+
"Read message history from a Feishu chat (group or DM). " +
|
|
1282
|
+
"Omit chat_id to read from the current conversation. " +
|
|
1283
|
+
"Returns messages with sender info, content, and timestamps. " +
|
|
1284
|
+
"Requires im:message scope; bot must be a member of the chat.",
|
|
1285
|
+
parameters: ChatMessagesSchema,
|
|
1286
|
+
async execute(_toolCallId, params) {
|
|
1287
|
+
const {
|
|
1288
|
+
chat_id,
|
|
1289
|
+
start_time,
|
|
1290
|
+
end_time,
|
|
1291
|
+
sort_type,
|
|
1292
|
+
page_size,
|
|
1293
|
+
page_token,
|
|
1294
|
+
} = params as {
|
|
1295
|
+
chat_id?: string;
|
|
1296
|
+
start_time?: string;
|
|
1297
|
+
end_time?: string;
|
|
1298
|
+
sort_type?: "ByCreateTimeAsc" | "ByCreateTimeDesc";
|
|
1299
|
+
page_size?: number;
|
|
1300
|
+
page_token?: string;
|
|
1301
|
+
};
|
|
1302
|
+
try {
|
|
1303
|
+
const container_id = chat_id || getConversationContext()?.chatId;
|
|
1304
|
+
if (!container_id) {
|
|
1305
|
+
return json({
|
|
1306
|
+
error:
|
|
1307
|
+
"No chat_id provided and no current conversation context available.",
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
const client = getClient();
|
|
1312
|
+
const res = await client.im.message.list({
|
|
1313
|
+
params: {
|
|
1314
|
+
container_id_type: "chat",
|
|
1315
|
+
container_id,
|
|
1316
|
+
...(start_time && { start_time }),
|
|
1317
|
+
...(end_time && { end_time }),
|
|
1318
|
+
...(sort_type && { sort_type }),
|
|
1319
|
+
...(page_size && { page_size }),
|
|
1320
|
+
...(page_token && { page_token }),
|
|
1321
|
+
},
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
1325
|
+
|
|
1326
|
+
const messages = (res.data?.items ?? []).map((m) => {
|
|
1327
|
+
let content: unknown = m.body?.content;
|
|
1328
|
+
if (typeof content === "string") {
|
|
1329
|
+
try {
|
|
1330
|
+
content = JSON.parse(content);
|
|
1331
|
+
} catch {
|
|
1332
|
+
// keep raw string
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return {
|
|
1336
|
+
message_id: m.message_id,
|
|
1337
|
+
msg_type: m.msg_type,
|
|
1338
|
+
sender: m.sender,
|
|
1339
|
+
content,
|
|
1340
|
+
create_time: m.create_time,
|
|
1341
|
+
deleted: m.deleted,
|
|
1342
|
+
};
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
return json({
|
|
1346
|
+
messages,
|
|
1347
|
+
has_more: res.data?.has_more ?? false,
|
|
1348
|
+
page_token: res.data?.page_token,
|
|
1349
|
+
});
|
|
1350
|
+
} catch (err) {
|
|
1351
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
1352
|
+
}
|
|
1353
|
+
},
|
|
1354
|
+
},
|
|
1355
|
+
{ name: "feishu_chat_messages" },
|
|
1356
|
+
);
|
|
1357
|
+
|
|
1358
|
+
api.logger.info?.(`feishu_doc: Registered 15 document/wiki/messaging tools`);
|
|
1251
1359
|
}
|
package/src/reply-dispatcher.ts
CHANGED
|
@@ -34,13 +34,15 @@ export type CreateFeishuReplyDispatcherParams = {
|
|
|
34
34
|
runtime: RuntimeEnv;
|
|
35
35
|
chatId: string;
|
|
36
36
|
replyToMessageId?: string;
|
|
37
|
+
/** When true, reply only appears in thread (not in main chat timeline) */
|
|
38
|
+
replyInThread?: boolean;
|
|
37
39
|
/** Mention targets, will be auto-included in replies */
|
|
38
40
|
mentionTargets?: MentionTarget[];
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherParams) {
|
|
42
44
|
const core = getFeishuRuntime();
|
|
43
|
-
const { cfg, agentId, chatId, replyToMessageId, mentionTargets } = params;
|
|
45
|
+
const { cfg, agentId, chatId, replyToMessageId, replyInThread, mentionTargets } = params;
|
|
44
46
|
|
|
45
47
|
const prefixContext = createReplyPrefixContext({
|
|
46
48
|
cfg,
|
|
@@ -127,6 +129,7 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
|
|
127
129
|
to: chatId,
|
|
128
130
|
text: chunk,
|
|
129
131
|
replyToMessageId,
|
|
132
|
+
replyInThread,
|
|
130
133
|
mentions: isFirstChunk ? mentionTargets : undefined,
|
|
131
134
|
});
|
|
132
135
|
isFirstChunk = false;
|
|
@@ -142,6 +145,7 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
|
|
142
145
|
to: chatId,
|
|
143
146
|
text: chunk,
|
|
144
147
|
replyToMessageId,
|
|
148
|
+
replyInThread,
|
|
145
149
|
mentions: isFirstChunk ? mentionTargets : undefined,
|
|
146
150
|
});
|
|
147
151
|
isFirstChunk = false;
|
package/src/send.ts
CHANGED
|
@@ -201,12 +201,14 @@ export type SendFeishuMessageParams = {
|
|
|
201
201
|
to: string;
|
|
202
202
|
text: string;
|
|
203
203
|
replyToMessageId?: string;
|
|
204
|
+
/** When true, reply only appears in thread (not in main chat timeline) */
|
|
205
|
+
replyInThread?: boolean;
|
|
204
206
|
/** Mention target users */
|
|
205
207
|
mentions?: MentionTarget[];
|
|
206
208
|
};
|
|
207
209
|
|
|
208
210
|
export async function sendMessageFeishu(params: SendFeishuMessageParams): Promise<FeishuSendResult> {
|
|
209
|
-
const { cfg, to, text, replyToMessageId, mentions } = params;
|
|
211
|
+
const { cfg, to, text, replyToMessageId, replyInThread, mentions } = params;
|
|
210
212
|
const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
|
|
211
213
|
if (!feishuCfg) {
|
|
212
214
|
throw new Error("Feishu channel not configured");
|
|
@@ -239,6 +241,7 @@ export async function sendMessageFeishu(params: SendFeishuMessageParams): Promis
|
|
|
239
241
|
data: {
|
|
240
242
|
content,
|
|
241
243
|
msg_type: "text",
|
|
244
|
+
...(replyInThread ? { reply_in_thread: true } : {}),
|
|
242
245
|
},
|
|
243
246
|
});
|
|
244
247
|
|
|
@@ -276,10 +279,11 @@ export type SendFeishuCardParams = {
|
|
|
276
279
|
to: string;
|
|
277
280
|
card: Record<string, unknown>;
|
|
278
281
|
replyToMessageId?: string;
|
|
282
|
+
replyInThread?: boolean;
|
|
279
283
|
};
|
|
280
284
|
|
|
281
285
|
export async function sendCardFeishu(params: SendFeishuCardParams): Promise<FeishuSendResult> {
|
|
282
|
-
const { cfg, to, card, replyToMessageId } = params;
|
|
286
|
+
const { cfg, to, card, replyToMessageId, replyInThread } = params;
|
|
283
287
|
const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
|
|
284
288
|
if (!feishuCfg) {
|
|
285
289
|
throw new Error("Feishu channel not configured");
|
|
@@ -300,6 +304,7 @@ export async function sendCardFeishu(params: SendFeishuCardParams): Promise<Feis
|
|
|
300
304
|
data: {
|
|
301
305
|
content,
|
|
302
306
|
msg_type: "interactive",
|
|
307
|
+
...(replyInThread ? { reply_in_thread: true } : {}),
|
|
303
308
|
},
|
|
304
309
|
});
|
|
305
310
|
|
|
@@ -383,17 +388,18 @@ export async function sendMarkdownCardFeishu(params: {
|
|
|
383
388
|
to: string;
|
|
384
389
|
text: string;
|
|
385
390
|
replyToMessageId?: string;
|
|
391
|
+
replyInThread?: boolean;
|
|
386
392
|
/** Mention target users */
|
|
387
393
|
mentions?: MentionTarget[];
|
|
388
394
|
}): Promise<FeishuSendResult> {
|
|
389
|
-
const { cfg, to, text, replyToMessageId, mentions } = params;
|
|
395
|
+
const { cfg, to, text, replyToMessageId, replyInThread, mentions } = params;
|
|
390
396
|
// Build message content (with @mention support)
|
|
391
397
|
let cardText = text;
|
|
392
398
|
if (mentions && mentions.length > 0) {
|
|
393
399
|
cardText = buildMentionedCardContent(mentions, text);
|
|
394
400
|
}
|
|
395
401
|
const card = buildMarkdownCard(cardText);
|
|
396
|
-
return sendCardFeishu({ cfg, to, card, replyToMessageId });
|
|
402
|
+
return sendCardFeishu({ cfg, to, card, replyToMessageId, replyInThread });
|
|
397
403
|
}
|
|
398
404
|
|
|
399
405
|
/**
|