@milerliu/feishu 0.1.16 → 0.2.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/package.json +1 -1
- package/src/send.ts +129 -29
package/package.json
CHANGED
package/src/send.ts
CHANGED
|
@@ -18,8 +18,8 @@ export type FeishuMessageInfo = {
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Send text with streaming effect using Feishu streaming update card.
|
|
21
|
-
* This creates an initial card and then streams updates to it.
|
|
22
21
|
* Uses the official Feishu streaming update card API.
|
|
22
|
+
* Reference: https://open.feishu.cn/document/cardkit-v1/streaming-updates-openapi-overview
|
|
23
23
|
*/
|
|
24
24
|
export async function sendStreamingMessageFeishu(params: {
|
|
25
25
|
cfg: ClawdbotConfig;
|
|
@@ -48,34 +48,76 @@ export async function sendStreamingMessageFeishu(params: {
|
|
|
48
48
|
rawText = buildMentionedMessage(mentions, rawText);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
// Create
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
// Create streaming card according to official docs
|
|
52
|
+
// Reference: https://open.feishu.cn/document/cardkit-v1/streaming-updates-openapi-overview
|
|
53
|
+
const streamingCard = {
|
|
54
|
+
schema: "2.0",
|
|
55
|
+
config: {
|
|
56
|
+
streaming_mode: true,
|
|
57
|
+
streaming_config: {
|
|
58
|
+
print_frequency_ms: {
|
|
59
|
+
default: 30,
|
|
60
|
+
},
|
|
61
|
+
print_step: {
|
|
62
|
+
default: 2,
|
|
63
|
+
},
|
|
64
|
+
print_strategy: "fast",
|
|
65
|
+
},
|
|
66
|
+
summary: {
|
|
67
|
+
content: "AI 正在生成内容...",
|
|
57
68
|
},
|
|
58
69
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
body: {
|
|
71
|
+
elements: [
|
|
72
|
+
{
|
|
73
|
+
tag: "markdown",
|
|
74
|
+
content: "",
|
|
75
|
+
element_id: "ai_response",
|
|
76
|
+
},
|
|
77
|
+
],
|
|
62
78
|
},
|
|
63
|
-
elements: [
|
|
64
|
-
{
|
|
65
|
-
tag: "markdown",
|
|
66
|
-
content: rawText,
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
79
|
};
|
|
70
80
|
|
|
71
|
-
const cardContent = JSON.stringify(
|
|
81
|
+
const cardContent = JSON.stringify(streamingCard);
|
|
82
|
+
|
|
83
|
+
// Step 1: Create card instance
|
|
84
|
+
let cardResponse;
|
|
85
|
+
try {
|
|
86
|
+
cardResponse = await (client as any).cardkit.v1.card.create({
|
|
87
|
+
data: {
|
|
88
|
+
type: "adaptive",
|
|
89
|
+
data: cardContent,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error("Failed to create streaming card:", error);
|
|
94
|
+
// Fallback to regular card
|
|
95
|
+
const fallbackCard = buildMarkdownCard(rawText);
|
|
96
|
+
return sendCardFeishu({ cfg, to, card: fallbackCard });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (cardResponse.code !== 0) {
|
|
100
|
+
console.error(`Card creation failed: ${cardResponse.msg}`);
|
|
101
|
+
// Fallback to regular card
|
|
102
|
+
const fallbackCard = buildMarkdownCard(rawText);
|
|
103
|
+
return sendCardFeishu({ cfg, to, card: fallbackCard });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const cardId = cardResponse.data?.card_id;
|
|
107
|
+
if (!cardId) {
|
|
108
|
+
console.error("No card_id returned");
|
|
109
|
+
// Fallback to regular card
|
|
110
|
+
const fallbackCard = buildMarkdownCard(rawText);
|
|
111
|
+
return sendCardFeishu({ cfg, to, card: fallbackCard });
|
|
112
|
+
}
|
|
72
113
|
|
|
73
|
-
//
|
|
114
|
+
// Step 2: Send card to chat
|
|
115
|
+
const messageContent = JSON.stringify({ card_id: cardId });
|
|
74
116
|
const messageResponse = await client.im.message.create({
|
|
75
117
|
params: { receive_id_type: receiveIdType },
|
|
76
118
|
data: {
|
|
77
119
|
receive_id: receiveId,
|
|
78
|
-
content:
|
|
120
|
+
content: messageContent,
|
|
79
121
|
msg_type: "interactive",
|
|
80
122
|
},
|
|
81
123
|
});
|
|
@@ -86,17 +128,56 @@ export async function sendStreamingMessageFeishu(params: {
|
|
|
86
128
|
|
|
87
129
|
const messageId = messageResponse.data?.message_id ?? "unknown";
|
|
88
130
|
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
const chunkSize =
|
|
131
|
+
// Step 3: Stream updates using batch_update
|
|
132
|
+
// Split text into chunks for streaming effect
|
|
133
|
+
const chunkSize = 10; // Small chunks for streaming effect
|
|
92
134
|
const chunks: string[] = [];
|
|
93
135
|
for (let i = 0; i < rawText.length; i += chunkSize) {
|
|
94
136
|
chunks.push(rawText.slice(i, i + chunkSize));
|
|
95
137
|
}
|
|
96
138
|
|
|
97
|
-
|
|
139
|
+
let cumulativeContent = "";
|
|
140
|
+
let sequence = 0;
|
|
141
|
+
|
|
98
142
|
for (let i = 0; i < chunks.length; i++) {
|
|
99
|
-
|
|
143
|
+
cumulativeContent += chunks[i];
|
|
144
|
+
sequence = i;
|
|
145
|
+
|
|
146
|
+
const actions = JSON.stringify([
|
|
147
|
+
{
|
|
148
|
+
action_name: "update_content",
|
|
149
|
+
action_param: {
|
|
150
|
+
tag: "markdown",
|
|
151
|
+
content: cumulativeContent,
|
|
152
|
+
element_id: "ai_response",
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
await (client as any).cardkit.v1.card.batch_update({
|
|
159
|
+
path: { card_id: cardId },
|
|
160
|
+
data: {
|
|
161
|
+
uuid: `stream-${Date.now()}-${i}`,
|
|
162
|
+
sequence,
|
|
163
|
+
actions,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Small delay between updates for streaming effect
|
|
168
|
+
if (i < chunks.length - 1) {
|
|
169
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error(`Streaming update failed at chunk ${i}: ${error}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
messageId,
|
|
178
|
+
chatId: receiveId,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
100
181
|
header: {
|
|
101
182
|
title: {
|
|
102
183
|
tag: "plain_text",
|
|
@@ -309,19 +390,38 @@ export async function sendCardFeishu(params: SendFeishuCardParams): Promise<Feis
|
|
|
309
390
|
}
|
|
310
391
|
|
|
311
392
|
const receiveIdType = resolveReceiveIdType(receiveId);
|
|
312
|
-
|
|
393
|
+
|
|
394
|
+
// Extract content from card for post message
|
|
395
|
+
const markdownContent = card.elements?.[0]?.content || "";
|
|
396
|
+
|
|
397
|
+
// Build post-type rich text message
|
|
398
|
+
const postContent = {
|
|
399
|
+
zh_cn: {
|
|
400
|
+
title: "AI 回复",
|
|
401
|
+
content: [
|
|
402
|
+
[
|
|
403
|
+
{
|
|
404
|
+
tag: "text",
|
|
405
|
+
text: markdownContent,
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
],
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const content = JSON.stringify(postContent);
|
|
313
413
|
|
|
314
414
|
if (replyToMessageId) {
|
|
315
415
|
const response = await client.im.message.reply({
|
|
316
416
|
path: { message_id: replyToMessageId },
|
|
317
417
|
data: {
|
|
318
418
|
content,
|
|
319
|
-
msg_type: "
|
|
419
|
+
msg_type: "post",
|
|
320
420
|
},
|
|
321
421
|
});
|
|
322
422
|
|
|
323
423
|
if (response.code !== 0) {
|
|
324
|
-
throw new Error(`Feishu
|
|
424
|
+
throw new Error(`Feishu post reply failed: ${response.msg || `code ${response.code}`}`);
|
|
325
425
|
}
|
|
326
426
|
|
|
327
427
|
return {
|
|
@@ -335,12 +435,12 @@ export async function sendCardFeishu(params: SendFeishuCardParams): Promise<Feis
|
|
|
335
435
|
data: {
|
|
336
436
|
receive_id: receiveId,
|
|
337
437
|
content,
|
|
338
|
-
msg_type: "
|
|
438
|
+
msg_type: "post",
|
|
339
439
|
},
|
|
340
440
|
});
|
|
341
441
|
|
|
342
442
|
if (response.code !== 0) {
|
|
343
|
-
throw new Error(`Feishu
|
|
443
|
+
throw new Error(`Feishu post send failed: ${response.msg || `code ${response.code}`}`);
|
|
344
444
|
}
|
|
345
445
|
|
|
346
446
|
return {
|