@max1874/feishu 0.2.23 → 0.2.25
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 +19 -1
- package/src/config-schema.ts +16 -0
- package/src/reply-dispatcher.ts +5 -1
- package/src/send.ts +10 -4
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -752,12 +752,30 @@ export async function handleFeishuMessage(params: {
|
|
|
752
752
|
...mediaPayload,
|
|
753
753
|
});
|
|
754
754
|
|
|
755
|
+
// Resolve replyToMode: per-chat-type override > global > default ("all")
|
|
756
|
+
const chatTypeKey = isGroup ? "group" : "direct";
|
|
757
|
+
const replyToMode =
|
|
758
|
+
feishuCfg?.replyToModeByChatType?.[chatTypeKey] ??
|
|
759
|
+
feishuCfg?.replyToMode ??
|
|
760
|
+
"all";
|
|
761
|
+
|
|
762
|
+
// Compute effective replyToMessageId based on mode
|
|
763
|
+
let effectiveReplyToMessageId: string | undefined;
|
|
764
|
+
if (replyToMode === "all") {
|
|
765
|
+
// Always thread: reply to the triggering message
|
|
766
|
+
effectiveReplyToMessageId = ctx.messageId;
|
|
767
|
+
} else {
|
|
768
|
+
// "off": only thread if the triggering message is already in a thread
|
|
769
|
+
effectiveReplyToMessageId = ctx.rootId;
|
|
770
|
+
}
|
|
771
|
+
|
|
755
772
|
const { dispatcher, replyOptions, markDispatchIdle } = createFeishuReplyDispatcher({
|
|
756
773
|
cfg,
|
|
757
774
|
agentId: route.agentId,
|
|
758
775
|
runtime: runtime as RuntimeEnv,
|
|
759
776
|
chatId: ctx.chatId,
|
|
760
|
-
replyToMessageId:
|
|
777
|
+
replyToMessageId: effectiveReplyToMessageId,
|
|
778
|
+
replyInThread: !!effectiveReplyToMessageId,
|
|
761
779
|
mentionTargets: ctx.mentionTargets,
|
|
762
780
|
});
|
|
763
781
|
|
package/src/config-schema.ts
CHANGED
|
@@ -33,6 +33,20 @@ const MarkdownConfigSchema = z
|
|
|
33
33
|
// Message render mode: auto (default) = detect markdown, raw = plain text, card = always card
|
|
34
34
|
const RenderModeSchema = z.enum(["auto", "raw", "card"]).optional();
|
|
35
35
|
|
|
36
|
+
// Reply-to (threading) mode: controls whether bot replies are sent as threaded replies.
|
|
37
|
+
// - "all" (default): always reply in thread (current behavior)
|
|
38
|
+
// - "off": reply in main chat; only thread if the triggering message was already in a thread
|
|
39
|
+
const ReplyToModeSchema = z.enum(["off", "all"]).optional();
|
|
40
|
+
|
|
41
|
+
// Per-chat-type overrides for replyToMode
|
|
42
|
+
const ReplyToModeByChatTypeSchema = z
|
|
43
|
+
.object({
|
|
44
|
+
direct: ReplyToModeSchema,
|
|
45
|
+
group: ReplyToModeSchema,
|
|
46
|
+
})
|
|
47
|
+
.strict()
|
|
48
|
+
.optional();
|
|
49
|
+
|
|
36
50
|
const BlockStreamingCoalesceSchema = z
|
|
37
51
|
.object({
|
|
38
52
|
enabled: z.boolean().optional(),
|
|
@@ -90,6 +104,8 @@ export const FeishuConfigSchema = z
|
|
|
90
104
|
mediaMaxMb: z.number().positive().optional(),
|
|
91
105
|
heartbeat: ChannelHeartbeatVisibilitySchema,
|
|
92
106
|
renderMode: RenderModeSchema, // raw = plain text (default), card = interactive card with markdown
|
|
107
|
+
replyToMode: ReplyToModeSchema, // "all" = always thread (default), "off" = main chat unless already in thread
|
|
108
|
+
replyToModeByChatType: ReplyToModeByChatTypeSchema, // per-chat-type overrides for replyToMode
|
|
93
109
|
})
|
|
94
110
|
.strict()
|
|
95
111
|
.superRefine((value, ctx) => {
|
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
|
/**
|