@elizaos/plugin-feishu 2.0.0-alpha
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/dist/accounts.d.ts +148 -0
- package/dist/accounts.d.ts.map +1 -0
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/sendMessage.d.ts +4 -0
- package/dist/actions/sendMessage.d.ts.map +1 -0
- package/dist/constants.d.ts +20 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/environment.d.ts +38 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/formatting.d.ts +104 -0
- package/dist/formatting.d.ts.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1391 -0
- package/dist/index.js.map +19 -0
- package/dist/messageManager.d.ts +52 -0
- package/dist/messageManager.d.ts.map +1 -0
- package/dist/providers/chatState.d.ts +7 -0
- package/dist/providers/chatState.d.ts.map +1 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/service.d.ts +56 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/types.d.ts +288 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +94 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1391 @@
|
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
var FEISHU_SERVICE_NAME = "feishu";
|
|
3
|
+
var FEISHU_DOMAINS = {
|
|
4
|
+
feishu: "https://open.feishu.cn",
|
|
5
|
+
lark: "https://open.larksuite.com"
|
|
6
|
+
};
|
|
7
|
+
var MAX_MESSAGE_LENGTH = 4000;
|
|
8
|
+
var DEFAULT_TIMEOUT_MS = 30000;
|
|
9
|
+
|
|
10
|
+
// src/actions/sendMessage.ts
|
|
11
|
+
var SEND_MESSAGE_ACTION = "SEND_FEISHU_MESSAGE";
|
|
12
|
+
var sendMessageAction = {
|
|
13
|
+
name: SEND_MESSAGE_ACTION,
|
|
14
|
+
similes: [
|
|
15
|
+
"FEISHU_SEND_MESSAGE",
|
|
16
|
+
"FEISHU_REPLY",
|
|
17
|
+
"FEISHU_MESSAGE",
|
|
18
|
+
"SEND_FEISHU",
|
|
19
|
+
"REPLY_FEISHU",
|
|
20
|
+
"LARK_SEND_MESSAGE",
|
|
21
|
+
"LARK_REPLY",
|
|
22
|
+
"SEND_LARK"
|
|
23
|
+
],
|
|
24
|
+
description: "Send a message to a Feishu/Lark chat",
|
|
25
|
+
validate: async (_runtime, message) => {
|
|
26
|
+
const source = message.content?.source;
|
|
27
|
+
return source === "feishu";
|
|
28
|
+
},
|
|
29
|
+
handler: async (runtime, message, state, _options, callback) => {
|
|
30
|
+
const feishuService = runtime.getService(FEISHU_SERVICE_NAME);
|
|
31
|
+
if (!feishuService) {
|
|
32
|
+
if (callback) {
|
|
33
|
+
await callback({
|
|
34
|
+
text: "Feishu service not available"
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return { success: false, error: "Feishu service not initialized" };
|
|
38
|
+
}
|
|
39
|
+
const currentState = state ?? await runtime.composeState(message);
|
|
40
|
+
const responseText = currentState.values?.response?.toString() || "";
|
|
41
|
+
const chatId = message.content?.chatId;
|
|
42
|
+
if (!chatId) {
|
|
43
|
+
if (callback) {
|
|
44
|
+
await callback({
|
|
45
|
+
text: "No chat ID available"
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return { success: false, error: "Missing chat ID" };
|
|
49
|
+
}
|
|
50
|
+
if (callback) {
|
|
51
|
+
await callback({
|
|
52
|
+
text: responseText,
|
|
53
|
+
action: SEND_MESSAGE_ACTION
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
success: true,
|
|
58
|
+
data: {
|
|
59
|
+
action: SEND_MESSAGE_ACTION,
|
|
60
|
+
chatId,
|
|
61
|
+
text: responseText,
|
|
62
|
+
replyToMessageId: message.content?.messageId
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
examples: [
|
|
67
|
+
[
|
|
68
|
+
{
|
|
69
|
+
name: "{{name1}}",
|
|
70
|
+
content: {
|
|
71
|
+
text: "Send a message to this Feishu chat"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "{{agentName}}",
|
|
76
|
+
content: {
|
|
77
|
+
text: "I'll send a message to this chat now.",
|
|
78
|
+
actions: [SEND_MESSAGE_ACTION]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
]
|
|
83
|
+
};
|
|
84
|
+
// src/messageManager.ts
|
|
85
|
+
import {
|
|
86
|
+
ChannelType,
|
|
87
|
+
createUniqueUuid,
|
|
88
|
+
EventType,
|
|
89
|
+
logger
|
|
90
|
+
} from "@elizaos/core";
|
|
91
|
+
|
|
92
|
+
// src/environment.ts
|
|
93
|
+
function getFeishuConfig(runtime) {
|
|
94
|
+
const appId = runtime.getSetting("FEISHU_APP_ID");
|
|
95
|
+
const appSecret = runtime.getSetting("FEISHU_APP_SECRET");
|
|
96
|
+
if (!appId || !appSecret) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const domainSetting = runtime.getSetting("FEISHU_DOMAIN")?.toLowerCase();
|
|
100
|
+
const domain = domainSetting === "lark" ? "lark" : "feishu";
|
|
101
|
+
const apiRoot = FEISHU_DOMAINS[domain];
|
|
102
|
+
let allowedChatIds = [];
|
|
103
|
+
const allowedChatsRaw = runtime.getSetting("FEISHU_ALLOWED_CHATS");
|
|
104
|
+
if (allowedChatsRaw) {
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(allowedChatsRaw);
|
|
107
|
+
if (Array.isArray(parsed)) {
|
|
108
|
+
allowedChatIds = parsed.map(String);
|
|
109
|
+
}
|
|
110
|
+
} catch {}
|
|
111
|
+
}
|
|
112
|
+
const testChatId = runtime.getSetting("FEISHU_TEST_CHAT_ID");
|
|
113
|
+
const shouldIgnoreBotMessages = runtime.getSetting("FEISHU_IGNORE_BOT_MESSAGES")?.toLowerCase() !== "false";
|
|
114
|
+
const shouldRespondOnlyToMentions = runtime.getSetting("FEISHU_RESPOND_ONLY_TO_MENTIONS")?.toLowerCase() === "true";
|
|
115
|
+
return {
|
|
116
|
+
appId,
|
|
117
|
+
appSecret,
|
|
118
|
+
domain,
|
|
119
|
+
apiRoot,
|
|
120
|
+
allowedChatIds,
|
|
121
|
+
testChatId,
|
|
122
|
+
shouldIgnoreBotMessages,
|
|
123
|
+
shouldRespondOnlyToMentions
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function validateConfig(config) {
|
|
127
|
+
if (!config.appId) {
|
|
128
|
+
return { valid: false, error: "FEISHU_APP_ID is required" };
|
|
129
|
+
}
|
|
130
|
+
if (!config.appId.startsWith("cli_")) {
|
|
131
|
+
return { valid: false, error: "FEISHU_APP_ID should start with 'cli_'" };
|
|
132
|
+
}
|
|
133
|
+
if (!config.appSecret) {
|
|
134
|
+
return { valid: false, error: "FEISHU_APP_SECRET is required" };
|
|
135
|
+
}
|
|
136
|
+
return { valid: true };
|
|
137
|
+
}
|
|
138
|
+
function isChatAllowed(config, chatId) {
|
|
139
|
+
if (config.allowedChatIds.length === 0) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return config.allowedChatIds.includes(chatId);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/types.ts
|
|
146
|
+
var FeishuEventTypes;
|
|
147
|
+
((FeishuEventTypes2) => {
|
|
148
|
+
FeishuEventTypes2["WORLD_JOINED"] = "FEISHU_WORLD_JOINED";
|
|
149
|
+
FeishuEventTypes2["WORLD_CONNECTED"] = "FEISHU_WORLD_CONNECTED";
|
|
150
|
+
FeishuEventTypes2["WORLD_LEFT"] = "FEISHU_WORLD_LEFT";
|
|
151
|
+
FeishuEventTypes2["ENTITY_JOINED"] = "FEISHU_ENTITY_JOINED";
|
|
152
|
+
FeishuEventTypes2["ENTITY_LEFT"] = "FEISHU_ENTITY_LEFT";
|
|
153
|
+
FeishuEventTypes2["ENTITY_UPDATED"] = "FEISHU_ENTITY_UPDATED";
|
|
154
|
+
FeishuEventTypes2["MESSAGE_RECEIVED"] = "FEISHU_MESSAGE_RECEIVED";
|
|
155
|
+
FeishuEventTypes2["MESSAGE_SENT"] = "FEISHU_MESSAGE_SENT";
|
|
156
|
+
FeishuEventTypes2["REACTION_RECEIVED"] = "FEISHU_REACTION_RECEIVED";
|
|
157
|
+
FeishuEventTypes2["INTERACTION_RECEIVED"] = "FEISHU_INTERACTION_RECEIVED";
|
|
158
|
+
FeishuEventTypes2["SLASH_START"] = "FEISHU_SLASH_START";
|
|
159
|
+
})(FeishuEventTypes ||= {});
|
|
160
|
+
var FeishuChatType;
|
|
161
|
+
((FeishuChatType2) => {
|
|
162
|
+
FeishuChatType2["P2P"] = "p2p";
|
|
163
|
+
FeishuChatType2["GROUP"] = "group";
|
|
164
|
+
})(FeishuChatType ||= {});
|
|
165
|
+
|
|
166
|
+
// src/messageManager.ts
|
|
167
|
+
class MessageManager {
|
|
168
|
+
client;
|
|
169
|
+
runtime;
|
|
170
|
+
config;
|
|
171
|
+
processedMessages = new Set;
|
|
172
|
+
botOpenId = null;
|
|
173
|
+
constructor(client, runtime, config) {
|
|
174
|
+
this.client = client;
|
|
175
|
+
this.runtime = runtime;
|
|
176
|
+
this.config = config;
|
|
177
|
+
}
|
|
178
|
+
setBotOpenId(openId) {
|
|
179
|
+
this.botOpenId = openId;
|
|
180
|
+
}
|
|
181
|
+
async handleMessage(event) {
|
|
182
|
+
try {
|
|
183
|
+
const message = event.event?.message;
|
|
184
|
+
if (!message) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (this.processedMessages.has(message.messageId)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
this.processedMessages.add(message.messageId);
|
|
191
|
+
if (this.processedMessages.size > 1000) {
|
|
192
|
+
const firstKey = this.processedMessages.values().next().value;
|
|
193
|
+
this.processedMessages.delete(firstKey);
|
|
194
|
+
}
|
|
195
|
+
const chatId = message.chatId;
|
|
196
|
+
const chatType = event.event?.chat_type || "p2p";
|
|
197
|
+
if (!isChatAllowed(this.config, chatId)) {
|
|
198
|
+
logger.debug(`[Feishu] Chat ${chatId} not authorized, skipping`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const sender = this.parseSender(event);
|
|
202
|
+
if (this.config.shouldIgnoreBotMessages && sender.isBot) {
|
|
203
|
+
logger.debug("[Feishu] Ignoring bot message");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (this.config.shouldRespondOnlyToMentions && chatType !== "p2p") {
|
|
207
|
+
if (!this.isBotMentioned(message)) {
|
|
208
|
+
logger.debug("[Feishu] Bot not mentioned, skipping");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const text = this.parseMessageContent(message);
|
|
213
|
+
const chat = {
|
|
214
|
+
chatId,
|
|
215
|
+
chatType,
|
|
216
|
+
name: event.event?.chat_name || undefined
|
|
217
|
+
};
|
|
218
|
+
const roomId = createUniqueUuid(this.runtime, chatId);
|
|
219
|
+
const entityId = createUniqueUuid(this.runtime, sender.openId);
|
|
220
|
+
const worldId = createUniqueUuid(this.runtime, chatId);
|
|
221
|
+
await this.runtime.ensureConnection({
|
|
222
|
+
entityId,
|
|
223
|
+
roomId,
|
|
224
|
+
userName: sender.name,
|
|
225
|
+
userId: sender.openId,
|
|
226
|
+
name: sender.name || "Unknown User",
|
|
227
|
+
source: "feishu",
|
|
228
|
+
channelId: chatId,
|
|
229
|
+
messageServerId: worldId,
|
|
230
|
+
type: chatType === "p2p" ? ChannelType.DM : ChannelType.GROUP,
|
|
231
|
+
worldId
|
|
232
|
+
});
|
|
233
|
+
const memory = {
|
|
234
|
+
id: createUniqueUuid(this.runtime, message.messageId),
|
|
235
|
+
entityId,
|
|
236
|
+
roomId,
|
|
237
|
+
agentId: this.runtime.agentId,
|
|
238
|
+
content: {
|
|
239
|
+
text,
|
|
240
|
+
source: "feishu",
|
|
241
|
+
chatId,
|
|
242
|
+
messageId: message.messageId
|
|
243
|
+
},
|
|
244
|
+
createdAt: Number.parseInt(message.createTime, 10)
|
|
245
|
+
};
|
|
246
|
+
const payload = {
|
|
247
|
+
runtime: this.runtime,
|
|
248
|
+
message: memory,
|
|
249
|
+
source: "feishu",
|
|
250
|
+
originalMessage: message,
|
|
251
|
+
chat,
|
|
252
|
+
sender
|
|
253
|
+
};
|
|
254
|
+
this.runtime.emitEvent("FEISHU_MESSAGE_RECEIVED" /* MESSAGE_RECEIVED */, payload);
|
|
255
|
+
this.runtime.emitEvent(EventType.MESSAGE_RECEIVED, payload);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
logger.error(`[Feishu] Error handling message: ${error instanceof Error ? error.message : String(error)}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async sendMessage(chatId, content) {
|
|
261
|
+
const messageIds = [];
|
|
262
|
+
try {
|
|
263
|
+
const text = content.text || "";
|
|
264
|
+
if (content.card) {
|
|
265
|
+
const response = await this.client.im.message.create({
|
|
266
|
+
params: { receive_id_type: "chat_id" },
|
|
267
|
+
data: {
|
|
268
|
+
receive_id: chatId,
|
|
269
|
+
msg_type: "interactive",
|
|
270
|
+
content: JSON.stringify(content.card)
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
if (response.data?.message_id) {
|
|
274
|
+
messageIds.push(response.data.message_id);
|
|
275
|
+
}
|
|
276
|
+
return messageIds;
|
|
277
|
+
}
|
|
278
|
+
if (content.imageKey) {
|
|
279
|
+
const response = await this.client.im.message.create({
|
|
280
|
+
params: { receive_id_type: "chat_id" },
|
|
281
|
+
data: {
|
|
282
|
+
receive_id: chatId,
|
|
283
|
+
msg_type: "image",
|
|
284
|
+
content: JSON.stringify({ image_key: content.imageKey })
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if (response.data?.message_id) {
|
|
288
|
+
messageIds.push(response.data.message_id);
|
|
289
|
+
}
|
|
290
|
+
return messageIds;
|
|
291
|
+
}
|
|
292
|
+
const parts = this.splitMessage(text);
|
|
293
|
+
for (const part of parts) {
|
|
294
|
+
const response = await this.client.im.message.create({
|
|
295
|
+
params: { receive_id_type: "chat_id" },
|
|
296
|
+
data: {
|
|
297
|
+
receive_id: chatId,
|
|
298
|
+
msg_type: "text",
|
|
299
|
+
content: JSON.stringify({ text: part })
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
if (response.data?.message_id) {
|
|
303
|
+
messageIds.push(response.data.message_id);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return messageIds;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
logger.error(`[Feishu] Error sending message: ${error instanceof Error ? error.message : String(error)}`);
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
async replyToMessage(messageId, content) {
|
|
313
|
+
const messageIds = [];
|
|
314
|
+
try {
|
|
315
|
+
const text = content.text || "";
|
|
316
|
+
const parts = this.splitMessage(text);
|
|
317
|
+
for (const part of parts) {
|
|
318
|
+
const response = await this.client.im.message.reply({
|
|
319
|
+
path: { message_id: messageId },
|
|
320
|
+
data: {
|
|
321
|
+
msg_type: "text",
|
|
322
|
+
content: JSON.stringify({ text: part })
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
if (response.data?.message_id) {
|
|
326
|
+
messageIds.push(response.data.message_id);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return messageIds;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
logger.error(`[Feishu] Error replying to message: ${error instanceof Error ? error.message : String(error)}`);
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
parseSender(event) {
|
|
336
|
+
const sender = event.event?.sender;
|
|
337
|
+
const isBot = sender?.sender_type === "app";
|
|
338
|
+
const openId = sender?.sender_id?.open_id || "";
|
|
339
|
+
return {
|
|
340
|
+
openId,
|
|
341
|
+
unionId: sender?.sender_id?.union_id,
|
|
342
|
+
userId: sender?.sender_id?.user_id,
|
|
343
|
+
isBot
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
parseMessageContent(message) {
|
|
347
|
+
try {
|
|
348
|
+
const content = JSON.parse(message.content);
|
|
349
|
+
switch (message.msgType) {
|
|
350
|
+
case "text":
|
|
351
|
+
return content.text || "";
|
|
352
|
+
case "post":
|
|
353
|
+
return this.extractTextFromPost(content);
|
|
354
|
+
case "image":
|
|
355
|
+
case "file":
|
|
356
|
+
case "audio":
|
|
357
|
+
case "video":
|
|
358
|
+
case "sticker":
|
|
359
|
+
return `[${message.msgType}]`;
|
|
360
|
+
case "interactive":
|
|
361
|
+
return "[interactive card]";
|
|
362
|
+
default:
|
|
363
|
+
return content.text || "";
|
|
364
|
+
}
|
|
365
|
+
} catch {
|
|
366
|
+
return "";
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
extractTextFromPost(content) {
|
|
370
|
+
const parts = [];
|
|
371
|
+
if (content.title) {
|
|
372
|
+
parts.push(content.title);
|
|
373
|
+
}
|
|
374
|
+
if (content.content && Array.isArray(content.content)) {
|
|
375
|
+
for (const line of content.content) {
|
|
376
|
+
if (Array.isArray(line)) {
|
|
377
|
+
const lineText = line.filter((elem) => elem.tag === "text" && elem.text).map((elem) => elem.text).join("");
|
|
378
|
+
if (lineText) {
|
|
379
|
+
parts.push(lineText);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return parts.join(`
|
|
385
|
+
`);
|
|
386
|
+
}
|
|
387
|
+
isBotMentioned(message) {
|
|
388
|
+
if (!this.botOpenId || !message.mentions) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
return message.mentions.some((mention) => mention.id === this.botOpenId);
|
|
392
|
+
}
|
|
393
|
+
splitMessage(content) {
|
|
394
|
+
if (content.length <= MAX_MESSAGE_LENGTH) {
|
|
395
|
+
return [content];
|
|
396
|
+
}
|
|
397
|
+
const parts = [];
|
|
398
|
+
let current = "";
|
|
399
|
+
for (const line of content.split(`
|
|
400
|
+
`)) {
|
|
401
|
+
const lineWithNewline = current ? `
|
|
402
|
+
${line}` : line;
|
|
403
|
+
if (current.length + lineWithNewline.length > MAX_MESSAGE_LENGTH) {
|
|
404
|
+
if (current) {
|
|
405
|
+
parts.push(current);
|
|
406
|
+
current = "";
|
|
407
|
+
}
|
|
408
|
+
if (line.length > MAX_MESSAGE_LENGTH) {
|
|
409
|
+
const words = line.split(/\s+/);
|
|
410
|
+
for (const word of words) {
|
|
411
|
+
const wordWithSpace = current ? ` ${word}` : word;
|
|
412
|
+
if (current.length + wordWithSpace.length > MAX_MESSAGE_LENGTH) {
|
|
413
|
+
if (current) {
|
|
414
|
+
parts.push(current);
|
|
415
|
+
current = "";
|
|
416
|
+
}
|
|
417
|
+
if (word.length > MAX_MESSAGE_LENGTH) {
|
|
418
|
+
for (let i = 0;i < word.length; i += MAX_MESSAGE_LENGTH) {
|
|
419
|
+
parts.push(word.slice(i, i + MAX_MESSAGE_LENGTH));
|
|
420
|
+
}
|
|
421
|
+
} else {
|
|
422
|
+
current = word;
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
current += wordWithSpace;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
current = line;
|
|
430
|
+
}
|
|
431
|
+
} else {
|
|
432
|
+
current += lineWithNewline;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (current) {
|
|
436
|
+
parts.push(current);
|
|
437
|
+
}
|
|
438
|
+
return parts;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/providers/chatState.ts
|
|
443
|
+
var CHAT_STATE_PROVIDER = "FEISHU_CHAT_STATE";
|
|
444
|
+
var chatStateProvider = {
|
|
445
|
+
name: CHAT_STATE_PROVIDER,
|
|
446
|
+
description: "Provides Feishu chat context and state information",
|
|
447
|
+
get: async (_runtime, message, state) => {
|
|
448
|
+
if (message.content?.source !== "feishu") {
|
|
449
|
+
return { text: "" };
|
|
450
|
+
}
|
|
451
|
+
const chatId = message.content?.chatId;
|
|
452
|
+
const messageId = message.content?.messageId;
|
|
453
|
+
if (!chatId) {
|
|
454
|
+
return { text: "" };
|
|
455
|
+
}
|
|
456
|
+
const stateInfo = [`Platform: Feishu/Lark`, `Chat ID: ${chatId}`];
|
|
457
|
+
if (messageId) {
|
|
458
|
+
stateInfo.push(`Message ID: ${messageId}`);
|
|
459
|
+
}
|
|
460
|
+
if (state?.values?.feishuChatType) {
|
|
461
|
+
stateInfo.push(`Chat Type: ${state.values.feishuChatType}`);
|
|
462
|
+
}
|
|
463
|
+
if (state?.values?.feishuChatName) {
|
|
464
|
+
stateInfo.push(`Chat Name: ${state.values.feishuChatName}`);
|
|
465
|
+
}
|
|
466
|
+
return { text: stateInfo.join(`
|
|
467
|
+
`) };
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
// src/service.ts
|
|
471
|
+
import {
|
|
472
|
+
ChannelType as ChannelType2,
|
|
473
|
+
createUniqueUuid as createUniqueUuid2,
|
|
474
|
+
EventType as EventType2,
|
|
475
|
+
logger as logger2,
|
|
476
|
+
Service
|
|
477
|
+
} from "@elizaos/core";
|
|
478
|
+
import * as lark from "@larksuiteoapi/node-sdk";
|
|
479
|
+
class FeishuService extends Service {
|
|
480
|
+
static serviceType = FEISHU_SERVICE_NAME;
|
|
481
|
+
capabilityDescription = "The agent is able to send and receive messages on Feishu/Lark";
|
|
482
|
+
client = null;
|
|
483
|
+
wsClient = null;
|
|
484
|
+
messageManager = null;
|
|
485
|
+
feishuConfig = null;
|
|
486
|
+
botOpenId = null;
|
|
487
|
+
knownChats = new Map;
|
|
488
|
+
constructor(runtime) {
|
|
489
|
+
super(runtime);
|
|
490
|
+
if (!runtime) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const config = getFeishuConfig(runtime);
|
|
494
|
+
if (!config) {
|
|
495
|
+
logger2.warn("[Feishu] App ID or App Secret not provided - Feishu functionality will be unavailable");
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const validation = validateConfig(config);
|
|
499
|
+
if (!validation.valid) {
|
|
500
|
+
logger2.warn(`[Feishu] Invalid configuration: ${validation.error}`);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
this.feishuConfig = config;
|
|
504
|
+
this.client = new lark.Client({
|
|
505
|
+
appId: config.appId,
|
|
506
|
+
appSecret: config.appSecret,
|
|
507
|
+
domain: config.domain === "lark" ? lark.Domain.Lark : lark.Domain.Feishu,
|
|
508
|
+
loggerLevel: lark.LoggerLevel.warn
|
|
509
|
+
});
|
|
510
|
+
this.messageManager = new MessageManager(this.client, runtime, config);
|
|
511
|
+
}
|
|
512
|
+
static async start(runtime) {
|
|
513
|
+
const service = new FeishuService(runtime);
|
|
514
|
+
if (!service.client || !service.feishuConfig) {
|
|
515
|
+
logger2.warn("[Feishu] Service started without client - no credentials provided");
|
|
516
|
+
return service;
|
|
517
|
+
}
|
|
518
|
+
const maxRetries = 5;
|
|
519
|
+
let retryCount = 0;
|
|
520
|
+
let lastError = null;
|
|
521
|
+
while (retryCount < maxRetries) {
|
|
522
|
+
try {
|
|
523
|
+
logger2.info(`[Feishu] Starting service for character ${runtime.character.name}`);
|
|
524
|
+
await service.initializeBot();
|
|
525
|
+
await service.setupWebSocket();
|
|
526
|
+
logger2.success(`[Feishu] Service started successfully`);
|
|
527
|
+
return service;
|
|
528
|
+
} catch (error) {
|
|
529
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
530
|
+
logger2.error(`[Feishu] Initialization attempt ${retryCount + 1} failed: ${lastError.message}`);
|
|
531
|
+
retryCount++;
|
|
532
|
+
if (retryCount < maxRetries) {
|
|
533
|
+
const delay = 2 ** retryCount * 1000;
|
|
534
|
+
logger2.info(`[Feishu] Retrying in ${delay / 1000} seconds...`);
|
|
535
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
logger2.error(`[Feishu] Initialization failed after ${maxRetries} attempts. Last error: ${lastError?.message}`);
|
|
540
|
+
return service;
|
|
541
|
+
}
|
|
542
|
+
static async stop(runtime) {
|
|
543
|
+
const service = runtime.getService(FEISHU_SERVICE_NAME);
|
|
544
|
+
if (service) {
|
|
545
|
+
await service.stop();
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
async stop() {
|
|
549
|
+
logger2.info("[Feishu] Stopping service...");
|
|
550
|
+
if (this.wsClient) {
|
|
551
|
+
try {
|
|
552
|
+
const wsClientWithStop = this.wsClient;
|
|
553
|
+
if (typeof wsClientWithStop.stop === "function") {
|
|
554
|
+
await wsClientWithStop.stop();
|
|
555
|
+
}
|
|
556
|
+
} catch (error) {
|
|
557
|
+
logger2.error(`[Feishu] Error stopping WebSocket client: ${error instanceof Error ? error.message : String(error)}`);
|
|
558
|
+
}
|
|
559
|
+
this.wsClient = null;
|
|
560
|
+
}
|
|
561
|
+
this.client = null;
|
|
562
|
+
logger2.info("[Feishu] Service stopped");
|
|
563
|
+
}
|
|
564
|
+
async initializeBot() {
|
|
565
|
+
if (!this.client) {
|
|
566
|
+
throw new Error("Client not initialized");
|
|
567
|
+
}
|
|
568
|
+
try {
|
|
569
|
+
const client = this.client;
|
|
570
|
+
if (client.bot?.botInfo?.get) {
|
|
571
|
+
const botInfo = await client.bot.botInfo.get({});
|
|
572
|
+
this.botOpenId = botInfo.data?.bot?.open_id || null;
|
|
573
|
+
if (this.botOpenId && this.messageManager) {
|
|
574
|
+
this.messageManager.setBotOpenId(this.botOpenId);
|
|
575
|
+
}
|
|
576
|
+
logger2.info(`[Feishu] Bot initialized: ${botInfo.data?.bot?.app_name || "Unknown"}`);
|
|
577
|
+
} else {
|
|
578
|
+
logger2.warn("[Feishu] Bot info API not available, some features may not work");
|
|
579
|
+
}
|
|
580
|
+
} catch (error) {
|
|
581
|
+
logger2.error(`[Feishu] Failed to get bot info: ${error instanceof Error ? error.message : String(error)}`);
|
|
582
|
+
throw error;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
async setupWebSocket() {
|
|
586
|
+
if (!this.client || !this.feishuConfig || !this.runtime) {
|
|
587
|
+
throw new Error("Client not initialized");
|
|
588
|
+
}
|
|
589
|
+
const eventDispatcher = new lark.EventDispatcher({}).register({
|
|
590
|
+
"im.message.receive_v1": async (data) => {
|
|
591
|
+
await this.handleMessageEvent(data);
|
|
592
|
+
return {};
|
|
593
|
+
},
|
|
594
|
+
"im.chat.member.bot.added_v1": async (data) => {
|
|
595
|
+
await this.handleBotAddedEvent(data);
|
|
596
|
+
return {};
|
|
597
|
+
},
|
|
598
|
+
"im.chat.member.bot.deleted_v1": async (data) => {
|
|
599
|
+
await this.handleBotRemovedEvent(data);
|
|
600
|
+
return {};
|
|
601
|
+
},
|
|
602
|
+
"im.chat.member.user.added_v1": async (data) => {
|
|
603
|
+
await this.handleUserAddedEvent(data);
|
|
604
|
+
return {};
|
|
605
|
+
},
|
|
606
|
+
"im.chat.member.user.deleted_v1": async (data) => {
|
|
607
|
+
await this.handleUserRemovedEvent(data);
|
|
608
|
+
return {};
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
this.wsClient = new lark.WSClient({
|
|
612
|
+
appId: this.feishuConfig.appId,
|
|
613
|
+
appSecret: this.feishuConfig.appSecret,
|
|
614
|
+
domain: this.feishuConfig.domain === "lark" ? lark.Domain.Lark : lark.Domain.Feishu,
|
|
615
|
+
loggerLevel: lark.LoggerLevel.warn
|
|
616
|
+
});
|
|
617
|
+
await this.wsClient.start({ eventDispatcher });
|
|
618
|
+
this.runtime.emitEvent("FEISHU_WORLD_CONNECTED" /* WORLD_CONNECTED */, {
|
|
619
|
+
runtime: this.runtime,
|
|
620
|
+
source: "feishu",
|
|
621
|
+
botOpenId: this.botOpenId
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
async handleMessageEvent(event) {
|
|
625
|
+
if (!this.messageManager)
|
|
626
|
+
return;
|
|
627
|
+
await this.messageManager.handleMessage(event);
|
|
628
|
+
}
|
|
629
|
+
async handleBotAddedEvent(event) {
|
|
630
|
+
if (!this.runtime)
|
|
631
|
+
return;
|
|
632
|
+
try {
|
|
633
|
+
const chatId = event.event?.chat_id;
|
|
634
|
+
if (!chatId)
|
|
635
|
+
return;
|
|
636
|
+
const chat = {
|
|
637
|
+
chatId,
|
|
638
|
+
chatType: "group" /* GROUP */,
|
|
639
|
+
name: event.event?.chat_name
|
|
640
|
+
};
|
|
641
|
+
this.knownChats.set(chatId, chat);
|
|
642
|
+
const worldId = createUniqueUuid2(this.runtime, chatId);
|
|
643
|
+
const roomId = createUniqueUuid2(this.runtime, chatId);
|
|
644
|
+
const world = {
|
|
645
|
+
id: worldId,
|
|
646
|
+
name: chat.name || `Feishu Chat ${chatId}`,
|
|
647
|
+
agentId: this.runtime.agentId,
|
|
648
|
+
messageServerId: worldId,
|
|
649
|
+
metadata: {
|
|
650
|
+
extra: {
|
|
651
|
+
chatType: chat.chatType
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
await this.runtime.ensureWorldExists(world);
|
|
656
|
+
const room = {
|
|
657
|
+
id: roomId,
|
|
658
|
+
name: chat.name || `Feishu Chat ${chatId}`,
|
|
659
|
+
source: "feishu",
|
|
660
|
+
type: ChannelType2.GROUP,
|
|
661
|
+
channelId: chatId,
|
|
662
|
+
messageServerId: worldId,
|
|
663
|
+
worldId
|
|
664
|
+
};
|
|
665
|
+
await this.runtime.ensureRoomExists(room);
|
|
666
|
+
const payload = {
|
|
667
|
+
runtime: this.runtime,
|
|
668
|
+
world,
|
|
669
|
+
rooms: [room],
|
|
670
|
+
entities: [],
|
|
671
|
+
source: "feishu",
|
|
672
|
+
chat,
|
|
673
|
+
botOpenId: this.botOpenId || undefined
|
|
674
|
+
};
|
|
675
|
+
this.runtime.emitEvent("FEISHU_WORLD_JOINED" /* WORLD_JOINED */, payload);
|
|
676
|
+
this.runtime.emitEvent(EventType2.WORLD_JOINED, {
|
|
677
|
+
runtime: this.runtime,
|
|
678
|
+
world,
|
|
679
|
+
rooms: [room],
|
|
680
|
+
entities: [],
|
|
681
|
+
source: "feishu"
|
|
682
|
+
});
|
|
683
|
+
logger2.info(`[Feishu] Bot added to chat: ${chat.name || chatId}`);
|
|
684
|
+
} catch (error) {
|
|
685
|
+
logger2.error(`[Feishu] Error handling bot added event: ${error instanceof Error ? error.message : String(error)}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
async handleBotRemovedEvent(event) {
|
|
689
|
+
if (!this.runtime)
|
|
690
|
+
return;
|
|
691
|
+
try {
|
|
692
|
+
const chatId = event.event?.chat_id;
|
|
693
|
+
if (!chatId)
|
|
694
|
+
return;
|
|
695
|
+
const chat = this.knownChats.get(chatId) || {
|
|
696
|
+
chatId,
|
|
697
|
+
chatType: "group" /* GROUP */
|
|
698
|
+
};
|
|
699
|
+
this.knownChats.delete(chatId);
|
|
700
|
+
this.runtime.emitEvent("FEISHU_WORLD_LEFT" /* WORLD_LEFT */, {
|
|
701
|
+
runtime: this.runtime,
|
|
702
|
+
source: "feishu",
|
|
703
|
+
chat,
|
|
704
|
+
botOpenId: this.botOpenId
|
|
705
|
+
});
|
|
706
|
+
logger2.info(`[Feishu] Bot removed from chat: ${chatId}`);
|
|
707
|
+
} catch (error) {
|
|
708
|
+
logger2.error(`[Feishu] Error handling bot removed event: ${error instanceof Error ? error.message : String(error)}`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
async handleUserAddedEvent(event) {
|
|
712
|
+
if (!this.runtime)
|
|
713
|
+
return;
|
|
714
|
+
try {
|
|
715
|
+
const chatId = event.event?.chat_id;
|
|
716
|
+
const users = event.event?.users;
|
|
717
|
+
if (!chatId || !users)
|
|
718
|
+
return;
|
|
719
|
+
for (const user of users) {
|
|
720
|
+
const openId = user.user_id?.open_id;
|
|
721
|
+
if (!openId)
|
|
722
|
+
continue;
|
|
723
|
+
this.runtime.emitEvent("FEISHU_ENTITY_JOINED" /* ENTITY_JOINED */, {
|
|
724
|
+
runtime: this.runtime,
|
|
725
|
+
source: "feishu",
|
|
726
|
+
feishuUser: {
|
|
727
|
+
openId,
|
|
728
|
+
name: user.name
|
|
729
|
+
},
|
|
730
|
+
chat: this.knownChats.get(chatId) || {
|
|
731
|
+
chatId,
|
|
732
|
+
chatType: "group" /* GROUP */
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
} catch (error) {
|
|
737
|
+
logger2.error(`[Feishu] Error handling user added event: ${error instanceof Error ? error.message : String(error)}`);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
async handleUserRemovedEvent(event) {
|
|
741
|
+
if (!this.runtime)
|
|
742
|
+
return;
|
|
743
|
+
try {
|
|
744
|
+
const chatId = event.event?.chat_id;
|
|
745
|
+
const users = event.event?.users;
|
|
746
|
+
if (!chatId || !users)
|
|
747
|
+
return;
|
|
748
|
+
for (const user of users) {
|
|
749
|
+
const openId = user.user_id?.open_id;
|
|
750
|
+
if (!openId)
|
|
751
|
+
continue;
|
|
752
|
+
this.runtime.emitEvent("FEISHU_ENTITY_LEFT" /* ENTITY_LEFT */, {
|
|
753
|
+
runtime: this.runtime,
|
|
754
|
+
source: "feishu",
|
|
755
|
+
feishuUser: {
|
|
756
|
+
openId,
|
|
757
|
+
name: user.name
|
|
758
|
+
},
|
|
759
|
+
chat: this.knownChats.get(chatId) || {
|
|
760
|
+
chatId,
|
|
761
|
+
chatType: "group" /* GROUP */
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
} catch (error) {
|
|
766
|
+
logger2.error(`[Feishu] Error handling user removed event: ${error instanceof Error ? error.message : String(error)}`);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
static registerSendHandlers(runtime, serviceInstance) {
|
|
770
|
+
if (serviceInstance?.client && serviceInstance?.messageManager) {
|
|
771
|
+
runtime.registerSendHandler("feishu", serviceInstance.handleSendMessage.bind(serviceInstance));
|
|
772
|
+
logger2.info("[Feishu] Registered send handler");
|
|
773
|
+
} else {
|
|
774
|
+
logger2.warn("[Feishu] Cannot register send handler - client not initialized");
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
async handleSendMessage(runtime, target, content) {
|
|
778
|
+
if (!this.messageManager) {
|
|
779
|
+
logger2.error("[Feishu] Message manager not initialized");
|
|
780
|
+
throw new Error("Feishu message manager is not initialized");
|
|
781
|
+
}
|
|
782
|
+
let chatId;
|
|
783
|
+
if (target.channelId) {
|
|
784
|
+
chatId = target.channelId;
|
|
785
|
+
} else if (target.roomId) {
|
|
786
|
+
const room = await runtime.getRoom(target.roomId);
|
|
787
|
+
chatId = room?.channelId;
|
|
788
|
+
if (!chatId) {
|
|
789
|
+
throw new Error(`Could not resolve Feishu chat ID from roomId ${target.roomId}`);
|
|
790
|
+
}
|
|
791
|
+
} else {
|
|
792
|
+
throw new Error("Feishu SendHandler requires channelId or roomId");
|
|
793
|
+
}
|
|
794
|
+
if (!chatId) {
|
|
795
|
+
throw new Error(`Could not determine target Feishu chat ID for target: ${JSON.stringify(target)}`);
|
|
796
|
+
}
|
|
797
|
+
const feishuContent = {
|
|
798
|
+
text: content.text || ""
|
|
799
|
+
};
|
|
800
|
+
const contentData = content.data;
|
|
801
|
+
if (contentData?.card) {
|
|
802
|
+
feishuContent.card = contentData.card;
|
|
803
|
+
}
|
|
804
|
+
if (contentData?.imageKey) {
|
|
805
|
+
feishuContent.imageKey = contentData.imageKey;
|
|
806
|
+
}
|
|
807
|
+
if (contentData?.fileKey) {
|
|
808
|
+
feishuContent.fileKey = contentData.fileKey;
|
|
809
|
+
}
|
|
810
|
+
await this.messageManager.sendMessage(chatId, feishuContent);
|
|
811
|
+
logger2.info(`[Feishu] Message sent to chat ID: ${chatId}`);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/accounts.ts
|
|
816
|
+
var DEFAULT_ACCOUNT_ID = "default";
|
|
817
|
+
function normalizeAccountId(accountId) {
|
|
818
|
+
if (!accountId || typeof accountId !== "string") {
|
|
819
|
+
return DEFAULT_ACCOUNT_ID;
|
|
820
|
+
}
|
|
821
|
+
const trimmed = accountId.trim().toLowerCase();
|
|
822
|
+
if (!trimmed || trimmed === "default") {
|
|
823
|
+
return DEFAULT_ACCOUNT_ID;
|
|
824
|
+
}
|
|
825
|
+
return trimmed;
|
|
826
|
+
}
|
|
827
|
+
function getMultiAccountConfig(runtime) {
|
|
828
|
+
const characterFeishu = runtime.character?.settings?.feishu;
|
|
829
|
+
return {
|
|
830
|
+
enabled: characterFeishu?.enabled,
|
|
831
|
+
appId: characterFeishu?.appId,
|
|
832
|
+
appSecret: characterFeishu?.appSecret,
|
|
833
|
+
appSecretFile: characterFeishu?.appSecretFile,
|
|
834
|
+
encryptKey: characterFeishu?.encryptKey,
|
|
835
|
+
verificationToken: characterFeishu?.verificationToken,
|
|
836
|
+
apiUrl: characterFeishu?.apiUrl,
|
|
837
|
+
dmPolicy: characterFeishu?.dmPolicy,
|
|
838
|
+
groupPolicy: characterFeishu?.groupPolicy,
|
|
839
|
+
mediaMaxMb: characterFeishu?.mediaMaxMb,
|
|
840
|
+
textChunkLimit: characterFeishu?.textChunkLimit,
|
|
841
|
+
webhookPath: characterFeishu?.webhookPath,
|
|
842
|
+
accounts: characterFeishu?.accounts,
|
|
843
|
+
groups: characterFeishu?.groups
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
function listFeishuAccountIds(runtime) {
|
|
847
|
+
const config = getMultiAccountConfig(runtime);
|
|
848
|
+
const accounts = config.accounts;
|
|
849
|
+
const ids = new Set;
|
|
850
|
+
const envAppId = runtime.getSetting("FEISHU_APP_ID");
|
|
851
|
+
const envAppSecret = runtime.getSetting("FEISHU_APP_SECRET");
|
|
852
|
+
const baseConfigured = Boolean(config.appId?.trim() && (config.appSecret?.trim() || config.appSecretFile));
|
|
853
|
+
const envConfigured = Boolean(envAppId?.trim() && envAppSecret?.trim());
|
|
854
|
+
if (baseConfigured || envConfigured) {
|
|
855
|
+
ids.add(DEFAULT_ACCOUNT_ID);
|
|
856
|
+
}
|
|
857
|
+
if (accounts && typeof accounts === "object") {
|
|
858
|
+
for (const id of Object.keys(accounts)) {
|
|
859
|
+
if (id) {
|
|
860
|
+
ids.add(normalizeAccountId(id));
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
const result = Array.from(ids);
|
|
865
|
+
if (result.length === 0) {
|
|
866
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
867
|
+
}
|
|
868
|
+
return result.toSorted((a, b) => a.localeCompare(b));
|
|
869
|
+
}
|
|
870
|
+
function resolveDefaultFeishuAccountId(runtime) {
|
|
871
|
+
const ids = listFeishuAccountIds(runtime);
|
|
872
|
+
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
|
|
873
|
+
return DEFAULT_ACCOUNT_ID;
|
|
874
|
+
}
|
|
875
|
+
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
876
|
+
}
|
|
877
|
+
function getAccountConfig(runtime, accountId) {
|
|
878
|
+
const config = getMultiAccountConfig(runtime);
|
|
879
|
+
const accounts = config.accounts;
|
|
880
|
+
if (!accounts || typeof accounts !== "object") {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const direct = accounts[accountId];
|
|
884
|
+
if (direct) {
|
|
885
|
+
return direct;
|
|
886
|
+
}
|
|
887
|
+
const normalized = normalizeAccountId(accountId);
|
|
888
|
+
const matchKey = Object.keys(accounts).find((key) => normalizeAccountId(key) === normalized);
|
|
889
|
+
return matchKey ? accounts[matchKey] : undefined;
|
|
890
|
+
}
|
|
891
|
+
function mergeFeishuAccountConfig(runtime, accountId) {
|
|
892
|
+
const multiConfig = getMultiAccountConfig(runtime);
|
|
893
|
+
const { accounts: _ignored, ...baseConfig } = multiConfig;
|
|
894
|
+
const accountConfig = getAccountConfig(runtime, accountId) ?? {};
|
|
895
|
+
const envAppId = runtime.getSetting("FEISHU_APP_ID");
|
|
896
|
+
const envAppSecret = runtime.getSetting("FEISHU_APP_SECRET");
|
|
897
|
+
const envEncryptKey = runtime.getSetting("FEISHU_ENCRYPT_KEY");
|
|
898
|
+
const envVerificationToken = runtime.getSetting("FEISHU_VERIFICATION_TOKEN");
|
|
899
|
+
const envDmPolicy = runtime.getSetting("FEISHU_DM_POLICY");
|
|
900
|
+
const envGroupPolicy = runtime.getSetting("FEISHU_GROUP_POLICY");
|
|
901
|
+
const envConfig = {
|
|
902
|
+
appId: envAppId || undefined,
|
|
903
|
+
appSecret: envAppSecret || undefined,
|
|
904
|
+
encryptKey: envEncryptKey || undefined,
|
|
905
|
+
verificationToken: envVerificationToken || undefined,
|
|
906
|
+
dmPolicy: envDmPolicy,
|
|
907
|
+
groupPolicy: envGroupPolicy
|
|
908
|
+
};
|
|
909
|
+
return {
|
|
910
|
+
...envConfig,
|
|
911
|
+
...baseConfig,
|
|
912
|
+
...accountConfig
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
function resolveFeishuAccount(runtime, accountId) {
|
|
916
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
917
|
+
const multiConfig = getMultiAccountConfig(runtime);
|
|
918
|
+
const baseEnabled = multiConfig.enabled !== false;
|
|
919
|
+
const merged = mergeFeishuAccountConfig(runtime, normalizedAccountId);
|
|
920
|
+
const accountEnabled = merged.enabled !== false;
|
|
921
|
+
const enabled = baseEnabled && accountEnabled;
|
|
922
|
+
const appId = merged.appId?.trim() || "";
|
|
923
|
+
const appSecret = merged.appSecret?.trim() || "";
|
|
924
|
+
let tokenSource = "none";
|
|
925
|
+
if (merged.appSecret?.trim()) {
|
|
926
|
+
tokenSource = "config";
|
|
927
|
+
} else {
|
|
928
|
+
const envAppSecret = runtime.getSetting("FEISHU_APP_SECRET");
|
|
929
|
+
if (envAppSecret?.trim()) {
|
|
930
|
+
tokenSource = "env";
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
if (!appId || !appSecret) {
|
|
934
|
+
tokenSource = "none";
|
|
935
|
+
}
|
|
936
|
+
const configured = Boolean(appId && appSecret);
|
|
937
|
+
const name = merged.name?.trim() || merged.botName?.trim() || undefined;
|
|
938
|
+
return {
|
|
939
|
+
accountId: normalizedAccountId,
|
|
940
|
+
enabled,
|
|
941
|
+
name,
|
|
942
|
+
appId,
|
|
943
|
+
appSecret,
|
|
944
|
+
tokenSource,
|
|
945
|
+
configured,
|
|
946
|
+
config: merged
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
function listEnabledFeishuAccounts(runtime) {
|
|
950
|
+
return listFeishuAccountIds(runtime).map((accountId) => resolveFeishuAccount(runtime, accountId)).filter((account) => account.enabled && account.configured);
|
|
951
|
+
}
|
|
952
|
+
function isMultiAccountEnabled(runtime) {
|
|
953
|
+
const accounts = listEnabledFeishuAccounts(runtime);
|
|
954
|
+
return accounts.length > 1;
|
|
955
|
+
}
|
|
956
|
+
function resolveFeishuGroupConfig(runtime, accountId, groupId) {
|
|
957
|
+
const multiConfig = getMultiAccountConfig(runtime);
|
|
958
|
+
const accountConfig = getAccountConfig(runtime, accountId);
|
|
959
|
+
const accountGroup = accountConfig?.groups?.[groupId];
|
|
960
|
+
if (accountGroup) {
|
|
961
|
+
return accountGroup;
|
|
962
|
+
}
|
|
963
|
+
return multiConfig.groups?.[groupId];
|
|
964
|
+
}
|
|
965
|
+
function isFeishuUserAllowed(params) {
|
|
966
|
+
const { userId, accountConfig, isGroup, groupConfig } = params;
|
|
967
|
+
if (isGroup) {
|
|
968
|
+
const policy2 = accountConfig.groupPolicy ?? "allowlist";
|
|
969
|
+
if (policy2 === "disabled") {
|
|
970
|
+
return false;
|
|
971
|
+
}
|
|
972
|
+
if (policy2 === "open") {
|
|
973
|
+
return true;
|
|
974
|
+
}
|
|
975
|
+
if (groupConfig?.allowFrom?.length) {
|
|
976
|
+
return groupConfig.allowFrom.some((allowed) => String(allowed) === userId);
|
|
977
|
+
}
|
|
978
|
+
if (accountConfig.groupAllowFrom?.length) {
|
|
979
|
+
return accountConfig.groupAllowFrom.some((allowed) => String(allowed) === userId);
|
|
980
|
+
}
|
|
981
|
+
return policy2 !== "allowlist";
|
|
982
|
+
}
|
|
983
|
+
const policy = accountConfig.dmPolicy ?? "pairing";
|
|
984
|
+
if (policy === "disabled") {
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
if (policy === "open") {
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
if (policy === "pairing") {
|
|
991
|
+
return true;
|
|
992
|
+
}
|
|
993
|
+
if (accountConfig.allowFrom?.length) {
|
|
994
|
+
return accountConfig.allowFrom.some((allowed) => String(allowed) === userId);
|
|
995
|
+
}
|
|
996
|
+
return false;
|
|
997
|
+
}
|
|
998
|
+
function isFeishuMentionRequired(params) {
|
|
999
|
+
const { groupConfig } = params;
|
|
1000
|
+
return groupConfig?.requireMention ?? false;
|
|
1001
|
+
}
|
|
1002
|
+
// src/formatting.ts
|
|
1003
|
+
var FEISHU_TEXT_CHUNK_LIMIT = 4000;
|
|
1004
|
+
function parseMarkdownToIR(markdown) {
|
|
1005
|
+
const styles = [];
|
|
1006
|
+
const links = [];
|
|
1007
|
+
let text = markdown;
|
|
1008
|
+
const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
1009
|
+
const linkReplacements = [];
|
|
1010
|
+
let linkMatch = linkRegex.exec(markdown);
|
|
1011
|
+
while (linkMatch !== null) {
|
|
1012
|
+
linkReplacements.push({
|
|
1013
|
+
start: linkMatch.index,
|
|
1014
|
+
end: linkMatch.index + linkMatch[0].length,
|
|
1015
|
+
text: linkMatch[1],
|
|
1016
|
+
href: linkMatch[2]
|
|
1017
|
+
});
|
|
1018
|
+
linkMatch = linkRegex.exec(markdown);
|
|
1019
|
+
}
|
|
1020
|
+
let offset = 0;
|
|
1021
|
+
for (const repl of linkReplacements) {
|
|
1022
|
+
const before = text.slice(0, repl.start + offset);
|
|
1023
|
+
const after = text.slice(repl.end + offset);
|
|
1024
|
+
text = before + repl.text + after;
|
|
1025
|
+
links.push({
|
|
1026
|
+
start: repl.start + offset,
|
|
1027
|
+
end: repl.start + offset + repl.text.length,
|
|
1028
|
+
href: repl.href,
|
|
1029
|
+
text: repl.text
|
|
1030
|
+
});
|
|
1031
|
+
offset += repl.text.length - (repl.end - repl.start);
|
|
1032
|
+
}
|
|
1033
|
+
text = processStyle(text, /\*\*([^*]+)\*\*/g, "bold", styles);
|
|
1034
|
+
text = processStyle(text, /__([^_]+)__/g, "bold", styles);
|
|
1035
|
+
text = processStyle(text, /(?<!\*)\*(?!\*)([^*]+)(?<!\*)\*(?!\*)/g, "italic", styles);
|
|
1036
|
+
text = processStyle(text, /(?<!_)_(?!_)([^_]+)(?<!_)_(?!_)/g, "italic", styles);
|
|
1037
|
+
text = processStyle(text, /~~([^~]+)~~/g, "strikethrough", styles);
|
|
1038
|
+
text = processStyle(text, /`([^`]+)`/g, "code", styles);
|
|
1039
|
+
text = text.replace(/```(\w*)\n?([\s\S]*?)```/g, (_, _lang, code) => {
|
|
1040
|
+
const start = text.indexOf(_);
|
|
1041
|
+
const trimmedCode = code.trim();
|
|
1042
|
+
styles.push({
|
|
1043
|
+
start,
|
|
1044
|
+
end: start + trimmedCode.length,
|
|
1045
|
+
style: "code"
|
|
1046
|
+
});
|
|
1047
|
+
return trimmedCode;
|
|
1048
|
+
});
|
|
1049
|
+
text = text.replace(/^#{1,6}\s+/gm, "");
|
|
1050
|
+
text = text.replace(/^>\s?/gm, "| ");
|
|
1051
|
+
text = text.replace(/\n{3,}/g, `
|
|
1052
|
+
|
|
1053
|
+
`).trim();
|
|
1054
|
+
return { text, styles, links };
|
|
1055
|
+
}
|
|
1056
|
+
function processStyle(text, pattern, style, styles) {
|
|
1057
|
+
let result = text;
|
|
1058
|
+
const matches = [];
|
|
1059
|
+
let match = pattern.exec(text);
|
|
1060
|
+
while (match !== null) {
|
|
1061
|
+
matches.push({
|
|
1062
|
+
index: match.index,
|
|
1063
|
+
fullLength: match[0].length,
|
|
1064
|
+
content: match[1]
|
|
1065
|
+
});
|
|
1066
|
+
match = pattern.exec(text);
|
|
1067
|
+
}
|
|
1068
|
+
let offset = 0;
|
|
1069
|
+
for (const m of matches) {
|
|
1070
|
+
const before = result.slice(0, m.index + offset);
|
|
1071
|
+
const after = result.slice(m.index + offset + m.fullLength);
|
|
1072
|
+
result = before + m.content + after;
|
|
1073
|
+
styles.push({
|
|
1074
|
+
start: m.index + offset,
|
|
1075
|
+
end: m.index + offset + m.content.length,
|
|
1076
|
+
style
|
|
1077
|
+
});
|
|
1078
|
+
offset += m.content.length - m.fullLength;
|
|
1079
|
+
}
|
|
1080
|
+
return result;
|
|
1081
|
+
}
|
|
1082
|
+
function buildStyleRanges(styles, textLength) {
|
|
1083
|
+
const ranges = Array(textLength).fill(null).map(() => ({
|
|
1084
|
+
bold: false,
|
|
1085
|
+
italic: false,
|
|
1086
|
+
strikethrough: false,
|
|
1087
|
+
code: false
|
|
1088
|
+
}));
|
|
1089
|
+
for (const span of styles) {
|
|
1090
|
+
for (let i = span.start;i < span.end && i < textLength; i++) {
|
|
1091
|
+
switch (span.style) {
|
|
1092
|
+
case "bold":
|
|
1093
|
+
ranges[i].bold = true;
|
|
1094
|
+
break;
|
|
1095
|
+
case "italic":
|
|
1096
|
+
ranges[i].italic = true;
|
|
1097
|
+
break;
|
|
1098
|
+
case "strikethrough":
|
|
1099
|
+
ranges[i].strikethrough = true;
|
|
1100
|
+
break;
|
|
1101
|
+
case "code":
|
|
1102
|
+
ranges[i].code = true;
|
|
1103
|
+
break;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
return ranges;
|
|
1108
|
+
}
|
|
1109
|
+
function buildLinkMap(links) {
|
|
1110
|
+
const map = new Map;
|
|
1111
|
+
for (const link of links) {
|
|
1112
|
+
for (let i = link.start;i < link.end; i++) {
|
|
1113
|
+
map.set(i, link.href);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
return map;
|
|
1117
|
+
}
|
|
1118
|
+
function getStylesAt(ranges, pos) {
|
|
1119
|
+
return ranges[pos] ?? {
|
|
1120
|
+
bold: false,
|
|
1121
|
+
italic: false,
|
|
1122
|
+
strikethrough: false,
|
|
1123
|
+
code: false
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
function getLinkAt(linkMap, pos) {
|
|
1127
|
+
return linkMap.get(pos);
|
|
1128
|
+
}
|
|
1129
|
+
function stylesEqual(a, b) {
|
|
1130
|
+
return a.bold === b.bold && a.italic === b.italic && a.strikethrough === b.strikethrough && a.code === b.code;
|
|
1131
|
+
}
|
|
1132
|
+
function createPostElement(text, styles, link) {
|
|
1133
|
+
const styleArray = [];
|
|
1134
|
+
if (styles.bold) {
|
|
1135
|
+
styleArray.push("bold");
|
|
1136
|
+
}
|
|
1137
|
+
if (styles.italic) {
|
|
1138
|
+
styleArray.push("italic");
|
|
1139
|
+
}
|
|
1140
|
+
if (styles.strikethrough) {
|
|
1141
|
+
styleArray.push("lineThrough");
|
|
1142
|
+
}
|
|
1143
|
+
if (styles.code) {
|
|
1144
|
+
styleArray.push("code");
|
|
1145
|
+
}
|
|
1146
|
+
if (link) {
|
|
1147
|
+
return {
|
|
1148
|
+
tag: "a",
|
|
1149
|
+
text,
|
|
1150
|
+
href: link,
|
|
1151
|
+
...styleArray.length > 0 ? { style: styleArray } : {}
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
return {
|
|
1155
|
+
tag: "text",
|
|
1156
|
+
text,
|
|
1157
|
+
...styleArray.length > 0 ? { style: styleArray } : {}
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function renderFeishuPost(ir) {
|
|
1161
|
+
const lines = [];
|
|
1162
|
+
const text = ir.text;
|
|
1163
|
+
if (!text) {
|
|
1164
|
+
return { zh_cn: { content: [[{ tag: "text", text: "" }]] } };
|
|
1165
|
+
}
|
|
1166
|
+
const styleRanges = buildStyleRanges(ir.styles, text.length);
|
|
1167
|
+
const linkMap = buildLinkMap(ir.links);
|
|
1168
|
+
const textLines = text.split(`
|
|
1169
|
+
`);
|
|
1170
|
+
let charIndex = 0;
|
|
1171
|
+
for (const line of textLines) {
|
|
1172
|
+
const lineElements = [];
|
|
1173
|
+
if (line.length === 0) {
|
|
1174
|
+
lineElements.push({ tag: "text", text: "" });
|
|
1175
|
+
} else {
|
|
1176
|
+
let segmentStart = charIndex;
|
|
1177
|
+
let currentStyles = getStylesAt(styleRanges, segmentStart);
|
|
1178
|
+
let currentLink = getLinkAt(linkMap, segmentStart);
|
|
1179
|
+
for (let i = 0;i < line.length; i++) {
|
|
1180
|
+
const pos = charIndex + i;
|
|
1181
|
+
const newStyles = getStylesAt(styleRanges, pos);
|
|
1182
|
+
const newLink = getLinkAt(linkMap, pos);
|
|
1183
|
+
const stylesChanged = !stylesEqual(currentStyles, newStyles);
|
|
1184
|
+
const linkChanged = currentLink !== newLink;
|
|
1185
|
+
if (stylesChanged || linkChanged) {
|
|
1186
|
+
const segmentText = text.slice(segmentStart, pos);
|
|
1187
|
+
if (segmentText) {
|
|
1188
|
+
lineElements.push(createPostElement(segmentText, currentStyles, currentLink));
|
|
1189
|
+
}
|
|
1190
|
+
segmentStart = pos;
|
|
1191
|
+
currentStyles = newStyles;
|
|
1192
|
+
currentLink = newLink;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
const finalText = text.slice(segmentStart, charIndex + line.length);
|
|
1196
|
+
if (finalText) {
|
|
1197
|
+
lineElements.push(createPostElement(finalText, currentStyles, currentLink));
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
lines.push(lineElements.length > 0 ? lineElements : [{ tag: "text", text: "" }]);
|
|
1201
|
+
charIndex += line.length + 1;
|
|
1202
|
+
}
|
|
1203
|
+
return {
|
|
1204
|
+
zh_cn: {
|
|
1205
|
+
content: lines
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
function markdownToFeishuPost(markdown) {
|
|
1210
|
+
const ir = parseMarkdownToIR(markdown ?? "");
|
|
1211
|
+
return renderFeishuPost(ir);
|
|
1212
|
+
}
|
|
1213
|
+
function splitAtBreakPoint(text, limit) {
|
|
1214
|
+
if (text.length <= limit) {
|
|
1215
|
+
return { chunk: text, remainder: "" };
|
|
1216
|
+
}
|
|
1217
|
+
const searchArea = text.slice(0, limit);
|
|
1218
|
+
const doubleNewline = searchArea.lastIndexOf(`
|
|
1219
|
+
|
|
1220
|
+
`);
|
|
1221
|
+
if (doubleNewline > limit * 0.5) {
|
|
1222
|
+
return {
|
|
1223
|
+
chunk: text.slice(0, doubleNewline).trimEnd(),
|
|
1224
|
+
remainder: text.slice(doubleNewline + 2).trimStart()
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
const singleNewline = searchArea.lastIndexOf(`
|
|
1228
|
+
`);
|
|
1229
|
+
if (singleNewline > limit * 0.5) {
|
|
1230
|
+
return {
|
|
1231
|
+
chunk: text.slice(0, singleNewline).trimEnd(),
|
|
1232
|
+
remainder: text.slice(singleNewline + 1).trimStart()
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
const sentenceEnd = Math.max(searchArea.lastIndexOf(". "), searchArea.lastIndexOf("! "), searchArea.lastIndexOf("? "));
|
|
1236
|
+
if (sentenceEnd > limit * 0.5) {
|
|
1237
|
+
return {
|
|
1238
|
+
chunk: text.slice(0, sentenceEnd + 1).trimEnd(),
|
|
1239
|
+
remainder: text.slice(sentenceEnd + 2).trimStart()
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
const space = searchArea.lastIndexOf(" ");
|
|
1243
|
+
if (space > limit * 0.5) {
|
|
1244
|
+
return {
|
|
1245
|
+
chunk: text.slice(0, space).trimEnd(),
|
|
1246
|
+
remainder: text.slice(space + 1).trimStart()
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
return {
|
|
1250
|
+
chunk: text.slice(0, limit),
|
|
1251
|
+
remainder: text.slice(limit)
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
function chunkFeishuText(text, opts = {}) {
|
|
1255
|
+
const limit = opts.limit ?? FEISHU_TEXT_CHUNK_LIMIT;
|
|
1256
|
+
if (!text?.trim()) {
|
|
1257
|
+
return [];
|
|
1258
|
+
}
|
|
1259
|
+
const normalizedText = text.trim();
|
|
1260
|
+
if (normalizedText.length <= limit) {
|
|
1261
|
+
return [normalizedText];
|
|
1262
|
+
}
|
|
1263
|
+
const chunks = [];
|
|
1264
|
+
let remaining = normalizedText;
|
|
1265
|
+
while (remaining.length > 0) {
|
|
1266
|
+
const { chunk, remainder } = splitAtBreakPoint(remaining, limit);
|
|
1267
|
+
if (chunk) {
|
|
1268
|
+
chunks.push(chunk);
|
|
1269
|
+
}
|
|
1270
|
+
remaining = remainder;
|
|
1271
|
+
}
|
|
1272
|
+
return chunks.filter((c) => c.length > 0);
|
|
1273
|
+
}
|
|
1274
|
+
function markdownToFeishuChunks(markdown, limit = FEISHU_TEXT_CHUNK_LIMIT) {
|
|
1275
|
+
const textChunks = chunkFeishuText(markdown, { limit });
|
|
1276
|
+
return textChunks.map((chunk) => ({
|
|
1277
|
+
post: markdownToFeishuPost(chunk),
|
|
1278
|
+
text: chunk
|
|
1279
|
+
}));
|
|
1280
|
+
}
|
|
1281
|
+
function containsMarkdown(text) {
|
|
1282
|
+
if (!text) {
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
const markdownPatterns = [
|
|
1286
|
+
/\*\*[^*]+\*\*/,
|
|
1287
|
+
/\*[^*]+\*/,
|
|
1288
|
+
/~~[^~]+~~/,
|
|
1289
|
+
/`[^`]+`/,
|
|
1290
|
+
/```[\s\S]*```/,
|
|
1291
|
+
/\[.+\]\(.+\)/,
|
|
1292
|
+
/^#{1,6}\s/m,
|
|
1293
|
+
/^[-*]\s/m,
|
|
1294
|
+
/^\d+\.\s/m
|
|
1295
|
+
];
|
|
1296
|
+
return markdownPatterns.some((pattern) => pattern.test(text));
|
|
1297
|
+
}
|
|
1298
|
+
function stripMarkdown(text) {
|
|
1299
|
+
let result = text;
|
|
1300
|
+
result = result.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
1301
|
+
result = result.replace(/__(.+?)__/g, "$1");
|
|
1302
|
+
result = result.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "$1");
|
|
1303
|
+
result = result.replace(/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, "$1");
|
|
1304
|
+
result = result.replace(/~~(.+?)~~/g, "$1");
|
|
1305
|
+
result = result.replace(/^#{1,6}\s+(.+)$/gm, "$1");
|
|
1306
|
+
result = result.replace(/^>\s?(.*)$/gm, "$1");
|
|
1307
|
+
result = result.replace(/```(\w*)\n?([\s\S]*?)```/g, "$2");
|
|
1308
|
+
result = result.replace(/`([^`]+)`/g, "$1");
|
|
1309
|
+
result = result.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
1310
|
+
result = result.replace(/\n{3,}/g, `
|
|
1311
|
+
|
|
1312
|
+
`);
|
|
1313
|
+
result = result.trim();
|
|
1314
|
+
return result;
|
|
1315
|
+
}
|
|
1316
|
+
function formatFeishuUserMention(userId) {
|
|
1317
|
+
return `<at user_id="${userId}"></at>`;
|
|
1318
|
+
}
|
|
1319
|
+
function formatFeishuAtAll() {
|
|
1320
|
+
return '<at user_id="all"></at>';
|
|
1321
|
+
}
|
|
1322
|
+
function truncateText(text, maxLength) {
|
|
1323
|
+
if (text.length <= maxLength) {
|
|
1324
|
+
return text;
|
|
1325
|
+
}
|
|
1326
|
+
if (maxLength <= 3) {
|
|
1327
|
+
return "...".slice(0, maxLength);
|
|
1328
|
+
}
|
|
1329
|
+
return `${text.slice(0, maxLength - 3)}...`;
|
|
1330
|
+
}
|
|
1331
|
+
function resolveFeishuSystemLocation(params) {
|
|
1332
|
+
const { chatType, chatId, chatName } = params;
|
|
1333
|
+
const name = chatName || chatId.slice(0, 8);
|
|
1334
|
+
return `Feishu ${chatType}:${name}`;
|
|
1335
|
+
}
|
|
1336
|
+
function isGroupChat(chatType) {
|
|
1337
|
+
return chatType === "group";
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// src/index.ts
|
|
1341
|
+
var feishuPlugin = {
|
|
1342
|
+
name: FEISHU_SERVICE_NAME,
|
|
1343
|
+
description: "Feishu/Lark client plugin for elizaOS",
|
|
1344
|
+
services: [FeishuService],
|
|
1345
|
+
actions: [sendMessageAction],
|
|
1346
|
+
providers: [chatStateProvider],
|
|
1347
|
+
tests: []
|
|
1348
|
+
};
|
|
1349
|
+
var src_default = feishuPlugin;
|
|
1350
|
+
export {
|
|
1351
|
+
validateConfig,
|
|
1352
|
+
truncateText,
|
|
1353
|
+
stripMarkdown,
|
|
1354
|
+
sendMessageAction,
|
|
1355
|
+
resolveFeishuSystemLocation,
|
|
1356
|
+
resolveFeishuGroupConfig,
|
|
1357
|
+
resolveFeishuAccount,
|
|
1358
|
+
resolveDefaultFeishuAccountId,
|
|
1359
|
+
normalizeAccountId,
|
|
1360
|
+
markdownToFeishuPost,
|
|
1361
|
+
markdownToFeishuChunks,
|
|
1362
|
+
listFeishuAccountIds,
|
|
1363
|
+
listEnabledFeishuAccounts,
|
|
1364
|
+
isMultiAccountEnabled,
|
|
1365
|
+
isGroupChat,
|
|
1366
|
+
isFeishuUserAllowed,
|
|
1367
|
+
isFeishuMentionRequired,
|
|
1368
|
+
isChatAllowed,
|
|
1369
|
+
getFeishuConfig,
|
|
1370
|
+
formatFeishuUserMention,
|
|
1371
|
+
formatFeishuAtAll,
|
|
1372
|
+
src_default as default,
|
|
1373
|
+
containsMarkdown,
|
|
1374
|
+
chunkFeishuText,
|
|
1375
|
+
chatStateProvider,
|
|
1376
|
+
SEND_MESSAGE_ACTION,
|
|
1377
|
+
MessageManager,
|
|
1378
|
+
MAX_MESSAGE_LENGTH,
|
|
1379
|
+
FeishuService,
|
|
1380
|
+
FeishuEventTypes,
|
|
1381
|
+
FeishuChatType,
|
|
1382
|
+
FEISHU_TEXT_CHUNK_LIMIT,
|
|
1383
|
+
FEISHU_SERVICE_NAME,
|
|
1384
|
+
FEISHU_DOMAINS,
|
|
1385
|
+
DEFAULT_TIMEOUT_MS,
|
|
1386
|
+
DEFAULT_ACCOUNT_ID,
|
|
1387
|
+
CHAT_STATE_PROVIDER
|
|
1388
|
+
};
|
|
1389
|
+
|
|
1390
|
+
//# debugId=B56498A90F39E45564756E2164756E21
|
|
1391
|
+
//# sourceMappingURL=index.js.map
|