@milerliu/feishu 0.1.17 → 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 +105 -24
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
|
+
}
|
|
72
105
|
|
|
73
|
-
|
|
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
|
+
}
|
|
113
|
+
|
|
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",
|