feishu-bridge 1.0.6 → 1.0.7

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/cli/index.js CHANGED
@@ -616,9 +616,6 @@ ${error.substring(0, 2e3)}`;
616
616
  }
617
617
  };
618
618
 
619
- // src/feishu/websocket.ts
620
- var import_ws = require("ws");
621
-
622
619
  // src/core/message-router.ts
623
620
  init_logger();
624
621
  var MessageRouter = class {
@@ -761,17 +758,14 @@ var MessageRouter = class {
761
758
  // src/feishu/websocket.ts
762
759
  init_logger();
763
760
  init_config();
761
+ var Lark = __toESM(require("@larksuiteoapi/node-sdk"));
764
762
  var FeishuWebSocketHandler = class {
765
763
  constructor(client, commandExecutor, adapters) {
766
764
  this.client = client;
767
765
  this.commandExecutor = commandExecutor;
768
766
  this.adapters = adapters;
769
- this.ws = null;
770
- this.reconnectAttempts = 0;
771
- this.maxReconnectAttempts = 5;
772
- this.reconnectInterval = 5e3;
773
- // 5秒
774
- this.heartbeatInterval = null;
767
+ this.wsClient = null;
768
+ this.eventDispatcher = null;
775
769
  this.isRunning = false;
776
770
  this.logger = new Logger();
777
771
  this.messageRouter = new MessageRouter(adapters);
@@ -784,227 +778,141 @@ var FeishuWebSocketHandler = class {
784
778
  this.logger.warn("WebSocket handler already running");
785
779
  return;
786
780
  }
787
- this.isRunning = true;
788
- this.reconnectAttempts = 0;
789
- await this.connect();
781
+ const config = ConfigManager.getInstance().getFeishuConfig();
782
+ try {
783
+ this.logger.info("Creating Feishu WebSocket client...");
784
+ this.wsClient = new Lark.WSClient({
785
+ appId: config.appId,
786
+ appSecret: config.appSecret,
787
+ domain: config.domain === "lark" ? Lark.Domain.Lark : Lark.Domain.Feishu,
788
+ loggerLevel: Lark.LoggerLevel.info
789
+ });
790
+ this.eventDispatcher = new Lark.EventDispatcher({
791
+ encryptKey: config.encryptKey,
792
+ verificationToken: config.verificationToken
793
+ });
794
+ this.registerEventHandlers();
795
+ this.wsClient.on("message", async (event) => {
796
+ await this.handleEvent(event);
797
+ });
798
+ this.wsClient.on("error", (error) => {
799
+ this.logger.error("WebSocket error:", error);
800
+ });
801
+ this.wsClient.on("close", () => {
802
+ this.logger.info("WebSocket connection closed");
803
+ if (this.isRunning) {
804
+ this.logger.info("Attempting to reconnect...");
805
+ setTimeout(() => this.start(), 5e3);
806
+ }
807
+ });
808
+ this.isRunning = true;
809
+ this.logger.info("Feishu WebSocket handler started successfully");
810
+ } catch (error) {
811
+ this.logger.error("Failed to start WebSocket handler:", error);
812
+ throw error;
813
+ }
790
814
  }
791
815
  /**
792
816
  * 停止WebSocket连接
793
817
  */
794
818
  async stop() {
795
819
  this.isRunning = false;
796
- if (this.heartbeatInterval) {
797
- clearInterval(this.heartbeatInterval);
798
- this.heartbeatInterval = null;
799
- }
800
- if (this.ws) {
801
- this.ws.close();
802
- this.ws = null;
820
+ if (this.wsClient) {
821
+ this.wsClient.close();
822
+ this.wsClient = null;
803
823
  }
804
824
  this.logger.info("WebSocket handler stopped");
805
825
  }
806
826
  /**
807
- * 建立WebSocket连接
808
- */
809
- async connect() {
810
- try {
811
- const config = ConfigManager.getInstance().getFeishuConfig();
812
- const wsUrl = config.domain === "feishu" ? "wss://ws.feishu.cn/passport/" : "wss://ws.larksuite.com/passport/";
813
- this.logger.info(`Connecting to WebSocket: ${wsUrl}`);
814
- this.ws = new import_ws.WebSocket(wsUrl, {
815
- headers: {
816
- "Authorization": `Bearer ${await this.getAccessToken()}`
817
- }
818
- });
819
- this.setupWebSocketHandlers();
820
- } catch (error) {
821
- this.logger.error("Failed to connect WebSocket:", error);
822
- this.handleReconnect();
823
- }
824
- }
825
- /**
826
- * 设置WebSocket事件处理器
827
+ * 注册事件处理器
827
828
  */
828
- setupWebSocketHandlers() {
829
- if (!this.ws) return;
830
- this.ws.on("open", () => {
831
- this.logger.info("WebSocket connected");
832
- this.reconnectAttempts = 0;
833
- this.startHeartbeat();
834
- });
835
- this.ws.on("message", (data) => {
836
- this.handleMessage(data.toString());
829
+ registerEventHandlers() {
830
+ if (!this.eventDispatcher) return;
831
+ this.eventDispatcher.on("im.message.receive_v1", async (event) => {
832
+ await this.handleMessageEvent(event);
837
833
  });
838
- this.ws.on("close", (code, reason) => {
839
- this.logger.info(`WebSocket closed: ${code} - ${reason.toString()}`);
840
- this.stopHeartbeat();
841
- if (this.isRunning) {
842
- this.handleReconnect();
843
- }
834
+ this.eventDispatcher.on("im.chat.member.bot.added_v1", (event) => {
835
+ this.logger.info("Bot added to chat:", event);
844
836
  });
845
- this.ws.on("error", (error) => {
846
- this.logger.error("WebSocket error:", error);
847
- });
848
- this.ws.on("ping", () => {
849
- this.ws?.pong();
837
+ this.eventDispatcher.on("im.chat.member.bot.deleted_v1", (event) => {
838
+ this.logger.info("Bot removed from chat:", event);
850
839
  });
851
840
  }
852
841
  /**
853
- * 处理收到的消息
842
+ * 处理事件
854
843
  */
855
- async handleMessage(data) {
844
+ async handleEvent(event) {
856
845
  try {
857
- const event = JSON.parse(data);
858
- this.logger.debug("Received WebSocket message:", event);
859
- if (event.type === "url_verification") {
860
- this.logger.info("Received URL verification challenge via WebSocket");
861
- return;
862
- }
863
- if (event.type === "event_callback" && event.event) {
864
- await this.handleEvent(event.event);
846
+ if (this.eventDispatcher) {
847
+ await this.eventDispatcher.dispatch(event);
865
848
  }
866
849
  } catch (error) {
867
- this.logger.error("Error handling WebSocket message:", error);
850
+ this.logger.error("Error dispatching event:", error);
868
851
  }
869
852
  }
870
853
  /**
871
- * 处理事件
854
+ * 处理消息事件
872
855
  */
873
- async handleEvent(event) {
874
- const { message } = event;
856
+ async handleMessageEvent(event) {
857
+ const { message, sender } = event;
875
858
  if (!message || message.message_type !== "text") {
876
859
  this.logger.debug("Ignoring non-text message");
877
860
  return;
878
861
  }
879
862
  try {
880
863
  const content = JSON.parse(message.content);
881
- const text = content.text.trim();
882
- const sender = event.sender?.sender_id?.open_id;
883
- if (!sender) {
864
+ const text = content.text?.trim() || "";
865
+ const senderId = sender?.sender_id?.open_id;
866
+ if (!senderId) {
884
867
  this.logger.warn("Missing sender ID");
885
868
  return;
886
869
  }
887
- this.logger.info(`Received message from ${sender}: ${text}`);
870
+ this.logger.info(`Received message from ${senderId}: ${text}`);
888
871
  const route = await this.messageRouter.route(text);
889
872
  if (route.type === "error") {
890
873
  await this.client.sendMessage(message.chat_id, {
891
874
  msg_type: "text",
892
- content: {
893
- text: route.content
894
- }
875
+ content: { text: route.content }
895
876
  });
896
877
  return;
897
878
  }
898
879
  if (route.type === "prompt_choice") {
899
880
  await this.client.sendMessage(message.chat_id, {
900
881
  msg_type: "text",
901
- content: {
902
- text: route.content
903
- }
882
+ content: { text: route.content }
904
883
  });
905
884
  return;
906
885
  }
907
886
  if (route.type === "direct" && route.target) {
908
887
  const command = this.messageRouter.extractCommand(text);
909
- this.logger.info(`Routing to ${route.target}: ${command}`);
888
+ this.logger.info(`Executing command for ${route.target}: ${command}`);
889
+ await this.client.sendMessage(message.chat_id, {
890
+ msg_type: "text",
891
+ content: { text: `\u23F3 \u6B63\u5728\u6267\u884C: ${command}` }
892
+ });
910
893
  const result = await this.commandExecutor.execute({
911
894
  target: route.target,
912
895
  command,
913
- sender,
896
+ sender: senderId,
914
897
  chatId: message.chat_id
915
898
  });
916
899
  await this.client.sendMessage(message.chat_id, {
917
900
  msg_type: "text",
918
- content: {
919
- text: this.formatResponse(result)
920
- }
901
+ content: { text: this.formatResponse(result) }
921
902
  });
922
903
  }
923
904
  } catch (error) {
924
- this.logger.error("Error processing event:", error);
905
+ this.logger.error("Error processing message event:", error);
925
906
  try {
926
907
  await this.client.sendMessage(message.chat_id, {
927
908
  msg_type: "text",
928
- content: {
929
- text: `\u274C \u5904\u7406\u6D88\u606F\u65F6\u51FA\u9519: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
930
- }
909
+ content: { text: `\u274C \u5904\u7406\u6D88\u606F\u65F6\u51FA\u9519: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}` }
931
910
  });
932
911
  } catch (sendError) {
933
912
  this.logger.error("Failed to send error message:", sendError);
934
913
  }
935
914
  }
936
915
  }
937
- /**
938
- * 启动心跳保活
939
- */
940
- startHeartbeat() {
941
- this.heartbeatInterval = setInterval(() => {
942
- if (this.ws?.readyState === import_ws.WebSocket.OPEN) {
943
- this.ws.ping();
944
- }
945
- }, 3e4);
946
- }
947
- /**
948
- * 停止心跳
949
- */
950
- stopHeartbeat() {
951
- if (this.heartbeatInterval) {
952
- clearInterval(this.heartbeatInterval);
953
- this.heartbeatInterval = null;
954
- }
955
- }
956
- /**
957
- * 处理重连
958
- */
959
- handleReconnect() {
960
- if (this.reconnectAttempts >= this.maxReconnectAttempts) {
961
- this.logger.error(`Max reconnection attempts (${this.maxReconnectAttempts}) reached`);
962
- return;
963
- }
964
- this.reconnectAttempts++;
965
- const delay = this.reconnectInterval * this.reconnectAttempts;
966
- this.logger.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
967
- setTimeout(() => {
968
- if (this.isRunning) {
969
- this.connect();
970
- }
971
- }, delay);
972
- }
973
- /**
974
- * 获取访问令牌
975
- */
976
- async getAccessToken() {
977
- const config = ConfigManager.getInstance().getFeishuConfig();
978
- const response = await fetch(
979
- `https://open.${config.domain === "lark" ? "larksuite" : "feishu"}.cn/open-apis/auth/v3/tenant_access_token/internal`,
980
- {
981
- method: "POST",
982
- headers: { "Content-Type": "application/json" },
983
- body: JSON.stringify({
984
- app_id: config.appId,
985
- app_secret: config.appSecret
986
- })
987
- }
988
- );
989
- const data = await response.json();
990
- if (data.code !== 0) {
991
- throw new Error(`Failed to get access token: ${data.msg}`);
992
- }
993
- return data.tenant_access_token;
994
- }
995
- /**
996
- * 提取目标IDE
997
- */
998
- extractTargetIDE(text) {
999
- const match = text.match(/@(vscode|cursor|trae|antigravity|kiro|opencode|claude)/i);
1000
- return match ? match[1].toLowerCase() : "auto";
1001
- }
1002
- /**
1003
- * 提取命令
1004
- */
1005
- extractCommand(text) {
1006
- return text.replace(/@(vscode|cursor|trae|antigravity|kiro|opencode|claude)\s*/i, "").trim();
1007
- }
1008
916
  /**
1009
917
  * 格式化响应
1010
918
  */
@@ -3445,18 +3353,42 @@ var ReverseChannel = class extends import_events.EventEmitter {
3445
3353
  };
3446
3354
 
3447
3355
  // src/skills/builtin/feishu.ts
3356
+ init_config();
3448
3357
  var FeishuSkills = class {
3449
3358
  constructor(client) {
3359
+ this.accessToken = null;
3360
+ this.tokenExpireTime = 0;
3450
3361
  this.client = client;
3362
+ const config = ConfigManager.getInstance().getFeishuConfig();
3363
+ this.baseUrl = config.domain === "lark" ? "https://open.larksuite.com/open-apis" : "https://open.feishu.cn/open-apis";
3364
+ }
3365
+ async getAccessToken() {
3366
+ const now = Date.now();
3367
+ if (this.accessToken && now < this.tokenExpireTime) {
3368
+ return this.accessToken;
3369
+ }
3370
+ const config = ConfigManager.getInstance().getFeishuConfig();
3371
+ const response = await fetch(`${this.baseUrl}/auth/v3/tenant_access_token/internal`, {
3372
+ method: "POST",
3373
+ headers: { "Content-Type": "application/json" },
3374
+ body: JSON.stringify({
3375
+ app_id: config.appId,
3376
+ app_secret: config.appSecret
3377
+ })
3378
+ });
3379
+ const data = await response.json();
3380
+ if (data.code !== 0) {
3381
+ throw new Error(`Failed to get access token: ${data.msg}`);
3382
+ }
3383
+ this.accessToken = data.tenant_access_token;
3384
+ this.tokenExpireTime = now + (data.expire - 300) * 1e3;
3385
+ return this.accessToken;
3451
3386
  }
3452
- /**
3453
- * 发送消息技能
3454
- */
3455
3387
  sendMessageSkill() {
3456
3388
  return {
3457
3389
  name: "feishu.sendMessage",
3458
3390
  description: "\u53D1\u9001\u6D88\u606F\u5230\u6307\u5B9A\u4F1A\u8BDD",
3459
- trigger: /发送消息|发信息/,
3391
+ trigger: /发送消息|发信息|发消息/,
3460
3392
  riskLevel: "low",
3461
3393
  examples: [
3462
3394
  "\u53D1\u9001\u6D88\u606F\u7ED9\u5F20\u4E09\uFF1A\u4E0B\u5348\u5F00\u4F1A",
@@ -3464,32 +3396,23 @@ var FeishuSkills = class {
3464
3396
  ],
3465
3397
  execute: async (context) => {
3466
3398
  try {
3467
- const { target, content } = this.parseMessageArgs(context.message);
3399
+ const { content } = this.parseMessageArgs(context.message);
3468
3400
  await this.client.sendMessage(context.chatId, {
3469
3401
  msg_type: "text",
3470
3402
  content: { text: content }
3471
3403
  });
3472
- return {
3473
- success: true,
3474
- content: "\u2705 \u6D88\u606F\u5DF2\u53D1\u9001"
3475
- };
3404
+ return { success: true, content: "\u2705 \u6D88\u606F\u5DF2\u53D1\u9001" };
3476
3405
  } catch (error) {
3477
- return {
3478
- success: false,
3479
- content: `\u274C \u53D1\u9001\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
3480
- };
3406
+ return { success: false, content: `\u274C \u53D1\u9001\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}` };
3481
3407
  }
3482
3408
  }
3483
3409
  };
3484
3410
  }
3485
- /**
3486
- * 创建日程技能
3487
- */
3488
3411
  createEventSkill() {
3489
3412
  return {
3490
3413
  name: "feishu.createEvent",
3491
3414
  description: "\u5728\u98DE\u4E66\u65E5\u5386\u521B\u5EFA\u65E5\u7A0B",
3492
- trigger: /创建日程|创建会议|添加日程/,
3415
+ trigger: /创建日程|创建会议|添加日程|新建日程/,
3493
3416
  riskLevel: "medium",
3494
3417
  examples: [
3495
3418
  "\u521B\u5EFA\u65E5\u7A0B\uFF1A\u660E\u5929\u4E0B\u53483\u70B9\u56E2\u961F\u5468\u4F1A",
@@ -3497,36 +3420,57 @@ var FeishuSkills = class {
3497
3420
  ],
3498
3421
  execute: async (context) => {
3499
3422
  try {
3500
- const { title, startTime, attendees } = this.parseEventArgs(context.message);
3423
+ const { title, startTime, endTime, description } = this.parseEventArgs(context.message);
3424
+ const token = await this.getAccessToken();
3425
+ const response = await fetch(`${this.baseUrl}/calendar/v4/calendars/primary/events`, {
3426
+ method: "POST",
3427
+ headers: {
3428
+ "Authorization": `Bearer ${token}`,
3429
+ "Content-Type": "application/json"
3430
+ },
3431
+ body: JSON.stringify({
3432
+ summary: title,
3433
+ description,
3434
+ start_time: {
3435
+ timestamp: Math.floor(startTime / 1e3).toString(),
3436
+ timezone: "Asia/Shanghai"
3437
+ },
3438
+ end_time: {
3439
+ timestamp: Math.floor(endTime / 1e3).toString(),
3440
+ timezone: "Asia/Shanghai"
3441
+ },
3442
+ attendee_ability: "can_see_others",
3443
+ free_busy_status: "busy"
3444
+ })
3445
+ });
3446
+ const data = await response.json();
3447
+ if (data.code !== 0) {
3448
+ throw new Error(data.msg || "\u521B\u5EFA\u65E5\u7A0B\u5931\u8D25");
3449
+ }
3450
+ const eventLink = data.data?.event_link || "";
3501
3451
  return {
3502
3452
  success: true,
3503
3453
  content: `\u2705 \u65E5\u7A0B\u5DF2\u521B\u5EFA
3504
3454
 
3505
- \u6807\u9898: ${title}
3506
- \u65F6\u95F4: ${startTime}
3507
- \u53C2\u4E0E\u8005: ${attendees.join(", ") || "\u65E0"}`,
3455
+ \u{1F4C5} ${title}
3456
+ \u{1F550} ${this.formatDateTime(startTime)} - ${this.formatDateTime(endTime)}
3457
+
3458
+ \u{1F517} ${eventLink}`,
3508
3459
  actions: [
3509
- { label: "\u67E5\u770B\u65E5\u7A0B", action: "view_calendar" },
3510
- { label: "\u7F16\u8F91", action: "edit_event" }
3460
+ { label: "\u67E5\u770B\u65E5\u7A0B", action: "view_calendar", url: eventLink }
3511
3461
  ]
3512
3462
  };
3513
3463
  } catch (error) {
3514
- return {
3515
- success: false,
3516
- content: `\u274C \u521B\u5EFA\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
3517
- };
3464
+ return { success: false, content: `\u274C \u521B\u5EFA\u65E5\u7A0B\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}` };
3518
3465
  }
3519
3466
  }
3520
3467
  };
3521
3468
  }
3522
- /**
3523
- * 创建文档技能
3524
- */
3525
3469
  createDocSkill() {
3526
3470
  return {
3527
3471
  name: "feishu.createDoc",
3528
3472
  description: "\u521B\u5EFA\u98DE\u4E66\u4E91\u6587\u6863",
3529
- trigger: /创建文档|新建文档/,
3473
+ trigger: /创建文档|新建文档|生成文档/,
3530
3474
  riskLevel: "low",
3531
3475
  examples: [
3532
3476
  "\u521B\u5EFA\u6587\u6863\uFF1A\u9879\u76EE\u9700\u6C42\u6587\u6863",
@@ -3534,89 +3478,114 @@ var FeishuSkills = class {
3534
3478
  ],
3535
3479
  execute: async (context) => {
3536
3480
  try {
3537
- const { title, content, folder } = this.parseDocArgs(context.message);
3481
+ const { title } = this.parseDocArgs(context.message);
3482
+ const token = await this.getAccessToken();
3483
+ const response = await fetch(`${this.baseUrl}/docx/v1/documents`, {
3484
+ method: "POST",
3485
+ headers: {
3486
+ "Authorization": `Bearer ${token}`,
3487
+ "Content-Type": "application/json"
3488
+ },
3489
+ body: JSON.stringify({ title })
3490
+ });
3491
+ const data = await response.json();
3492
+ if (data.code !== 0) {
3493
+ throw new Error(data.msg || "\u521B\u5EFA\u6587\u6863\u5931\u8D25");
3494
+ }
3495
+ const documentId = data.data?.document?.document_id;
3496
+ const domain = ConfigManager.getInstance().getFeishuConfig().domain === "lark" ? "larksuite.com" : "feishu.cn";
3497
+ const documentUrl = `https://${domain}/docx/${documentId}`;
3538
3498
  return {
3539
3499
  success: true,
3540
3500
  content: `\u2705 \u6587\u6863\u5DF2\u521B\u5EFA
3541
3501
 
3542
- \u6807\u9898: ${title}
3543
- \u4F4D\u7F6E: ${folder || "\u6211\u7684\u7A7A\u95F4"}`,
3502
+ \u{1F4C4} ${title}
3503
+
3504
+ \u{1F517} ${documentUrl}`,
3544
3505
  actions: [
3545
- { label: "\u6253\u5F00\u6587\u6863", action: "open_doc" },
3546
- { label: "\u5206\u4EAB", action: "share_doc" }
3506
+ { label: "\u6253\u5F00\u6587\u6863", action: "open_doc", url: documentUrl }
3547
3507
  ]
3548
3508
  };
3549
3509
  } catch (error) {
3550
- return {
3551
- success: false,
3552
- content: `\u274C \u521B\u5EFA\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
3553
- };
3510
+ return { success: false, content: `\u274C \u521B\u5EFA\u6587\u6863\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}` };
3554
3511
  }
3555
3512
  }
3556
3513
  };
3557
3514
  }
3558
- /**
3559
- * 上传文件技能
3560
- */
3561
3515
  uploadFileSkill() {
3562
3516
  return {
3563
3517
  name: "feishu.uploadFile",
3564
3518
  description: "\u4E0A\u4F20\u6587\u4EF6\u5230\u98DE\u4E66",
3565
- trigger: /上传文件|发送文件/,
3519
+ trigger: /上传文件|发送文件|传文件/,
3566
3520
  riskLevel: "low",
3567
3521
  execute: async (context) => {
3568
- return {
3569
- success: true,
3570
- content: "\u{1F4CE} \u6587\u4EF6\u4E0A\u4F20\u529F\u80FD\u5F00\u53D1\u4E2D"
3571
- };
3522
+ return { success: true, content: "\u{1F4CE} \u6587\u4EF6\u4E0A\u4F20\u529F\u80FD\u9700\u8981\u6587\u4EF6\u8DEF\u5F84\uFF0C\u8BF7\u4F7F\u7528\uFF1A\u4E0A\u4F20\u6587\u4EF6 /path/to/file" };
3572
3523
  }
3573
3524
  };
3574
3525
  }
3575
- /**
3576
- * 创建群公告技能
3577
- */
3578
3526
  createAnnouncementSkill() {
3579
3527
  return {
3580
3528
  name: "feishu.createAnnouncement",
3581
3529
  description: "\u53D1\u9001\u7FA4\u516C\u544A",
3582
- trigger: /发送公告|群公告/,
3530
+ trigger: /发送公告|群公告|发布公告|发公告/,
3583
3531
  riskLevel: "medium",
3584
3532
  requireConfirm: true,
3585
3533
  execute: async (context) => {
3586
- return {
3587
- success: true,
3588
- content: "\u{1F4E2} \u516C\u544A\u529F\u80FD\u5F00\u53D1\u4E2D"
3589
- };
3534
+ try {
3535
+ const { title, content } = this.parseAnnouncementArgs(context.message);
3536
+ await this.client.sendMessage(context.chatId, {
3537
+ msg_type: "text",
3538
+ content: { text: `\u{1F4E2} **${title}**
3539
+
3540
+ ${content}` }
3541
+ });
3542
+ return { success: true, content: "\u2705 \u516C\u544A\u5DF2\u53D1\u9001" };
3543
+ } catch (error) {
3544
+ return { success: false, content: `\u274C \u53D1\u5E03\u516C\u544A\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}` };
3545
+ }
3590
3546
  }
3591
3547
  };
3592
3548
  }
3593
- // 辅助方法
3594
3549
  parseMessageArgs(message) {
3595
- const match = message.match(/给(.+?)(.+)/);
3596
- if (match) {
3597
- return { target: match[1], content: match[2] };
3598
- }
3550
+ const match = message.match(/给(.+?)[::]\s*(.+)/);
3551
+ if (match) return { target: match[1], content: match[2] };
3599
3552
  return { target: "current", content: message };
3600
3553
  }
3601
3554
  parseEventArgs(message) {
3602
- const titleMatch = message.match(/日程[::]\s*(.+?)(?=\s|$)/);
3603
- const timeMatch = message.match(/(明天|今天|后天|\d{1,2}点|\d{1,2}:\d{2})/);
3604
- return {
3605
- title: titleMatch ? titleMatch[1] : "\u65B0\u65E5\u7A0B",
3606
- startTime: timeMatch ? timeMatch[1] : "\u672A\u6307\u5B9A\u65F6\u95F4",
3607
- attendees: []
3608
- };
3555
+ const now = /* @__PURE__ */ new Date();
3556
+ const titleMatch = message.match(/日程[::]\s*(.+?)(?=(明天|今天|后天|时间|地点|$))/i);
3557
+ const title = titleMatch ? titleMatch[1].trim() : "\u65B0\u65E5\u7A0B";
3558
+ const timeMatch = message.match(/(明天|今天|后天)?\s*(上午|下午|晚上)?\s*(\d{1,2})[:点时]?(\d{0,2})?/);
3559
+ let startTime = new Date(now.getTime() + 60 * 60 * 1e3);
3560
+ if (timeMatch) {
3561
+ const day = timeMatch[1];
3562
+ const period = timeMatch[2];
3563
+ let hour = parseInt(timeMatch[3]);
3564
+ const minute = parseInt(timeMatch[4]) || 0;
3565
+ if (day === "\u660E\u5929") startTime.setDate(startTime.getDate() + 1);
3566
+ else if (day === "\u540E\u5929") startTime.setDate(startTime.getDate() + 2);
3567
+ if (period === "\u4E0B\u5348" && hour < 12) hour += 12;
3568
+ if (period === "\u665A\u4E0A" && hour < 12) hour += 12;
3569
+ startTime.setHours(hour, minute, 0, 0);
3570
+ }
3571
+ const endTime = new Date(startTime.getTime() + 60 * 60 * 1e3);
3572
+ return { title, startTime: startTime.getTime(), endTime: endTime.getTime(), description: "" };
3609
3573
  }
3610
3574
  parseDocArgs(message) {
3611
- const match = message.match(/文档[::]\s*(.+?)(?=\s|$)/);
3612
- return {
3613
- title: match ? match[1] : "\u672A\u547D\u540D\u6587\u6863",
3614
- content: ""
3615
- };
3575
+ const match = message.match(/文档[::]\s*(.+?)(?=(内容|文件夹|$))/i);
3576
+ return { title: match ? match[1].trim() : "\u672A\u547D\u540D\u6587\u6863" };
3577
+ }
3578
+ parseAnnouncementArgs(message) {
3579
+ const titleMatch = message.match(/公告[::]\s*(.+?)(?=内容|$)/i);
3580
+ const title = titleMatch ? titleMatch[1].trim() : "\u7FA4\u516C\u544A";
3581
+ const contentMatch = message.match(/内容[::]\s*(.+)/);
3582
+ const content = contentMatch ? contentMatch[1] : "";
3583
+ return { title, content };
3584
+ }
3585
+ formatDateTime(timestamp) {
3586
+ const date = new Date(timestamp);
3587
+ return `${date.getMonth() + 1}\u6708${date.getDate()}\u65E5 ${date.getHours()}:${date.getMinutes().toString().padStart(2, "0")}`;
3616
3588
  }
3617
- /**
3618
- * 获取所有飞书技能
3619
- */
3620
3589
  getAllSkills() {
3621
3590
  return [
3622
3591
  this.sendMessageSkill(),