@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/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