@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/send.ts +78 -50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milerliu/feishu",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "type": "module",
5
5
  "description": "OpenClaw Feishu/Lark channel plugin",
6
6
  "license": "MIT",
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 (with @mention support)
45
+ // Build message content with @mention support
50
46
  let rawText = text ?? "";
51
47
  if (mentions && mentions.length > 0) {
52
- rawText = buildMentionedCardContent(mentions, rawText);
48
+ rawText = buildMentionedMessage(mentions, rawText);
53
49
  }
54
- const messageText = getFeishuRuntime().channel.text.convertMarkdownTables(rawText, tableMode);
55
50
 
56
- // Build initial card with streaming_update header for proper streaming behavior
51
+ // Create streaming card
52
+ // Using inline card (type: "adaptive") instead of template for dynamic content
57
53
  const initialCard = {
58
- header: {
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, // Enable streaming update mode
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 content = JSON.stringify(initialCard);
67
+ const cardContent = JSON.stringify(initialCard);
77
68
 
78
- // Create initial card with empty content
79
- const response = await client.im.message.create({
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
- receive_id: receiveId,
83
- content,
84
- msg_type: "interactive",
72
+ type: "adaptive",
73
+ data: cardContent,
85
74
  },
86
75
  });
87
76
 
88
- if (response.code !== 0) {
89
- throw new Error(`Feishu streaming card creation failed: ${response.msg || `code ${response.code}`}`);
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 messageId = response.data?.message_id ?? "unknown";
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.warn('No card_id returned, falling back to regular card send');
100
- const fallbackCard = buildMarkdownCard(messageText);
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 multiple messages with delay to simulate streaming effect
105
- const chunkSize = 500;
106
- const chunks: string[] = [];
107
- for (let i = 0; i < messageText.length; i += chunkSize) {
108
- chunks.push(messageText.slice(i, i + chunkSize));
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
- // Send first message
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
- // Send remaining chunks as separate messages
116
- for (let i = 1; i < chunks.length; i++) {
117
- await new Promise((resolve) => setTimeout(resolve, 100));
118
- const chunkCard = buildMarkdownCard(chunks[i]);
119
- await sendCardFeishu({ cfg, to, card: chunkCard });
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
- return {
123
- messageId: firstResult.messageId,
124
- chatId: receiveId,
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,