@milerliu/feishu 0.1.11 → 0.1.13
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 +78 -50
package/package.json
CHANGED
package/src/send.ts
CHANGED
|
@@ -41,88 +41,116 @@ export async function sendStreamingMessageFeishu(params: {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const receiveIdType = resolveReceiveIdType(receiveId);
|
|
44
|
-
const tableMode = getFeishuRuntime().channel.text.resolveMarkdownTableMode({
|
|
45
|
-
cfg,
|
|
46
|
-
channel: "feishu",
|
|
47
|
-
});
|
|
48
44
|
|
|
49
|
-
// Build message content
|
|
45
|
+
// Build message content with @mention support
|
|
50
46
|
let rawText = text ?? "";
|
|
51
47
|
if (mentions && mentions.length > 0) {
|
|
52
|
-
rawText =
|
|
48
|
+
rawText = buildMentionedMessage(mentions, rawText);
|
|
53
49
|
}
|
|
54
|
-
const messageText = getFeishuRuntime().channel.text.convertMarkdownTables(rawText, tableMode);
|
|
55
50
|
|
|
56
|
-
//
|
|
51
|
+
// Create streaming card
|
|
52
|
+
// Using inline card (type: "adaptive") instead of template for dynamic content
|
|
57
53
|
const initialCard = {
|
|
58
|
-
|
|
59
|
-
title: {
|
|
60
|
-
tag: "plain_text",
|
|
61
|
-
content: "AI 回复",
|
|
62
|
-
},
|
|
63
|
-
},
|
|
54
|
+
type: "adaptive",
|
|
64
55
|
config: {
|
|
65
56
|
wide_screen_mode: true,
|
|
66
|
-
streaming_update: true,
|
|
57
|
+
streaming_update: true,
|
|
67
58
|
},
|
|
68
59
|
elements: [
|
|
69
60
|
{
|
|
70
61
|
tag: "markdown",
|
|
71
|
-
content:
|
|
62
|
+
content: rawText,
|
|
72
63
|
},
|
|
73
64
|
],
|
|
74
65
|
};
|
|
75
66
|
|
|
76
|
-
const
|
|
67
|
+
const cardContent = JSON.stringify(initialCard);
|
|
77
68
|
|
|
78
|
-
// Create
|
|
79
|
-
const
|
|
80
|
-
params: { receive_id_type: receiveIdType },
|
|
69
|
+
// Create card instance
|
|
70
|
+
const cardResponse = await (client as any).cardkit.v1.card.create({
|
|
81
71
|
data: {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
msg_type: "interactive",
|
|
72
|
+
type: "adaptive",
|
|
73
|
+
data: cardContent,
|
|
85
74
|
},
|
|
86
75
|
});
|
|
87
76
|
|
|
88
|
-
if (
|
|
89
|
-
|
|
77
|
+
if (cardResponse.code !== 0) {
|
|
78
|
+
console.error(`Card creation failed: ${cardResponse.msg}`);
|
|
79
|
+
// Fallback to regular card
|
|
80
|
+
const fallbackCard = buildMarkdownCard(rawText);
|
|
81
|
+
return sendCardFeishu({ cfg, to, card: fallbackCard });
|
|
90
82
|
}
|
|
91
83
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
// Extract card_id from response
|
|
95
|
-
const cardData = response.data as Record<string, unknown> | undefined;
|
|
96
|
-
const cardId = cardData?.card_id as string | undefined;
|
|
97
|
-
|
|
84
|
+
const cardId = cardResponse.data?.card_id;
|
|
98
85
|
if (!cardId) {
|
|
99
|
-
console.
|
|
100
|
-
const fallbackCard = buildMarkdownCard(
|
|
86
|
+
console.error('No card_id returned');
|
|
87
|
+
const fallbackCard = buildMarkdownCard(rawText);
|
|
101
88
|
return sendCardFeishu({ cfg, to, card: fallbackCard });
|
|
102
89
|
}
|
|
103
90
|
|
|
104
|
-
// Send
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
91
|
+
// Send the card as a message
|
|
92
|
+
const messageContent = JSON.stringify({ card_id: cardId });
|
|
93
|
+
const messageResponse = await client.im.message.create({
|
|
94
|
+
params: { receive_id_type: receiveIdType },
|
|
95
|
+
data: {
|
|
96
|
+
receive_id: receiveId,
|
|
97
|
+
content: messageContent,
|
|
98
|
+
msg_type: "interactive",
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (messageResponse.code !== 0) {
|
|
103
|
+
throw new Error(`Failed to send streaming card: ${messageResponse.msg || `code ${messageResponse.code}`}`);
|
|
109
104
|
}
|
|
110
105
|
|
|
111
|
-
|
|
112
|
-
const firstCard = buildMarkdownCard(chunks[0]);
|
|
113
|
-
const firstResult = await sendCardFeishu({ cfg, to, card: firstCard });
|
|
106
|
+
const messageId = messageResponse.data?.message_id ?? "unknown";
|
|
114
107
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
108
|
+
// Now perform streaming updates using batch_update
|
|
109
|
+
// Split text into chunks for streaming
|
|
110
|
+
const chunkSize = 300;
|
|
111
|
+
const chunks: string[] = [];
|
|
112
|
+
for (let i = 0; i < rawText.length; i += chunkSize) {
|
|
113
|
+
chunks.push(rawText.slice(i, i + chunkSize));
|
|
120
114
|
}
|
|
121
115
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
116
|
+
// Perform streaming updates
|
|
117
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
118
|
+
const uuid = crypto.randomUUID?.() ?? `${Date.now()}-${i}`;
|
|
119
|
+
const sequence = i;
|
|
120
|
+
|
|
121
|
+
const updateAction = JSON.stringify({
|
|
122
|
+
tag: "markdown",
|
|
123
|
+
content: chunks[i],
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const actions = JSON.stringify([
|
|
127
|
+
{
|
|
128
|
+
action_name: "update_content",
|
|
129
|
+
action_param: {
|
|
130
|
+
tag: "markdown",
|
|
131
|
+
content: chunks[i],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
await (client as any).cardkit.v1.card.batch_update({
|
|
138
|
+
path: { card_id: cardId },
|
|
139
|
+
data: {
|
|
140
|
+
uuid,
|
|
141
|
+
sequence,
|
|
142
|
+
actions,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Delay between updates for streaming effect
|
|
147
|
+
if (i < chunks.length - 1) {
|
|
148
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.error(`Streaming update failed at chunk ${i}: ${err}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
126
154
|
|
|
127
155
|
return {
|
|
128
156
|
messageId,
|