feishu-bridge 1.0.5 → 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
  */
@@ -2441,6 +2349,7 @@ var KiroAdapter = class extends BaseIDEAdapter {
2441
2349
  };
2442
2350
 
2443
2351
  // src/adapters/opencode.ts
2352
+ var import_execa6 = require("execa");
2444
2353
  var import_child_process = require("child_process");
2445
2354
  var net = __toESM(require("net"));
2446
2355
  var fs7 = __toESM(require("fs"));
@@ -2450,6 +2359,8 @@ var OpenCodeAdapter = class extends BaseIDEAdapter {
2450
2359
  constructor() {
2451
2360
  super(...arguments);
2452
2361
  this.type = "opencode";
2362
+ this.ideType = "opencode";
2363
+ this.ideName = "OpenCode";
2453
2364
  this.useSocket = false;
2454
2365
  this.messageId = 0;
2455
2366
  this.pendingMessages = /* @__PURE__ */ new Map();
@@ -2462,6 +2373,17 @@ var OpenCodeAdapter = class extends BaseIDEAdapter {
2462
2373
  async checkAvailability() {
2463
2374
  return this.checkAvailabilitySync();
2464
2375
  }
2376
+ async isInstalled() {
2377
+ return this.checkAvailabilitySync();
2378
+ }
2379
+ async getVersion() {
2380
+ try {
2381
+ const { stdout } = await (0, import_execa6.execa)("opencode", ["--version"]);
2382
+ return stdout.trim();
2383
+ } catch {
2384
+ return "unknown";
2385
+ }
2386
+ }
2465
2387
  checkCLIExists() {
2466
2388
  try {
2467
2389
  const { execSync } = require("child_process");
@@ -2652,10 +2574,21 @@ var OpenCodeAdapter = class extends BaseIDEAdapter {
2652
2574
  };
2653
2575
  }
2654
2576
  }
2577
+ async getAvailableModels() {
2578
+ return [
2579
+ "claude-3-5-sonnet-20241022",
2580
+ "claude-3-opus-20240229",
2581
+ "claude-3-haiku-20240307"
2582
+ ];
2583
+ }
2584
+ async executeAI(prompt) {
2585
+ const content = await this.getAIResponse(prompt);
2586
+ return { content, model: "claude-3-5-sonnet" };
2587
+ }
2655
2588
  };
2656
2589
 
2657
2590
  // src/adapters/claude-code.ts
2658
- var import_execa6 = require("execa");
2591
+ var import_execa7 = require("execa");
2659
2592
  var fs8 = __toESM(require("fs"));
2660
2593
  var path8 = __toESM(require("path"));
2661
2594
  var os8 = __toESM(require("os"));
@@ -2675,7 +2608,7 @@ var ClaudeCodeAdapter = class extends BaseIDEAdapter {
2675
2608
  findClaudeExecutable() {
2676
2609
  const platform5 = os8.platform();
2677
2610
  try {
2678
- const result = import_execa6.execa.sync("claude", ["--version"], { reject: false });
2611
+ const result = import_execa7.execa.sync("claude", ["--version"], { reject: false });
2679
2612
  if (result.exitCode === 0) {
2680
2613
  return "claude";
2681
2614
  }
@@ -2716,7 +2649,7 @@ var ClaudeCodeAdapter = class extends BaseIDEAdapter {
2716
2649
  this.logger.info(`Sending command to Claude Code: ${command}`);
2717
2650
  if (this.claudePath) {
2718
2651
  try {
2719
- const result = await (0, import_execa6.execa)(this.claudePath, [
2652
+ const result = await (0, import_execa7.execa)(this.claudePath, [
2720
2653
  "execute",
2721
2654
  "--command",
2722
2655
  command
@@ -2780,7 +2713,7 @@ var ClaudeCodeAdapter = class extends BaseIDEAdapter {
2780
2713
  this.logger.info(`Getting AI response from Claude Code for prompt: ${prompt}`);
2781
2714
  if (this.claudePath) {
2782
2715
  try {
2783
- const result = await (0, import_execa6.execa)(this.claudePath, [
2716
+ const result = await (0, import_execa7.execa)(this.claudePath, [
2784
2717
  "ask",
2785
2718
  "--prompt",
2786
2719
  prompt
@@ -2845,7 +2778,7 @@ var ClaudeCodeAdapter = class extends BaseIDEAdapter {
2845
2778
  }
2846
2779
  async runProgram(command, cwd) {
2847
2780
  try {
2848
- const result = await import_execa6.execa.command(command, {
2781
+ const result = await import_execa7.execa.command(command, {
2849
2782
  cwd: cwd || process.cwd(),
2850
2783
  shell: true,
2851
2784
  timeout: 6e4,
@@ -2874,7 +2807,7 @@ var ClaudeCodeAdapter = class extends BaseIDEAdapter {
2874
2807
 
2875
2808
  // src/adapters/gemini-cli.ts
2876
2809
  var import_child_process2 = require("child_process");
2877
- var import_execa7 = require("execa");
2810
+ var import_execa8 = require("execa");
2878
2811
  var GeminiCLIAdapter = class extends BaseIDEAdapter {
2879
2812
  constructor() {
2880
2813
  super(...arguments);
@@ -2893,6 +2826,9 @@ var GeminiCLIAdapter = class extends BaseIDEAdapter {
2893
2826
  async checkAvailability() {
2894
2827
  return this.checkAvailabilitySync();
2895
2828
  }
2829
+ async isInstalled() {
2830
+ return this.checkAvailabilitySync();
2831
+ }
2896
2832
  async activate() {
2897
2833
  const available = await this.checkAvailability();
2898
2834
  if (!available) {
@@ -3000,7 +2936,7 @@ var GeminiCLIAdapter = class extends BaseIDEAdapter {
3000
2936
  */
3001
2937
  async getVersion() {
3002
2938
  try {
3003
- const result = await (0, import_execa7.execa)(this.cliPath, ["version"]);
2939
+ const result = await (0, import_execa8.execa)(this.cliPath, ["version"]);
3004
2940
  return result.stdout;
3005
2941
  } catch {
3006
2942
  return "unknown";
@@ -3417,18 +3353,42 @@ var ReverseChannel = class extends import_events.EventEmitter {
3417
3353
  };
3418
3354
 
3419
3355
  // src/skills/builtin/feishu.ts
3356
+ init_config();
3420
3357
  var FeishuSkills = class {
3421
3358
  constructor(client) {
3359
+ this.accessToken = null;
3360
+ this.tokenExpireTime = 0;
3422
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;
3423
3386
  }
3424
- /**
3425
- * 发送消息技能
3426
- */
3427
3387
  sendMessageSkill() {
3428
3388
  return {
3429
3389
  name: "feishu.sendMessage",
3430
3390
  description: "\u53D1\u9001\u6D88\u606F\u5230\u6307\u5B9A\u4F1A\u8BDD",
3431
- trigger: /发送消息|发信息/,
3391
+ trigger: /发送消息|发信息|发消息/,
3432
3392
  riskLevel: "low",
3433
3393
  examples: [
3434
3394
  "\u53D1\u9001\u6D88\u606F\u7ED9\u5F20\u4E09\uFF1A\u4E0B\u5348\u5F00\u4F1A",
@@ -3436,32 +3396,23 @@ var FeishuSkills = class {
3436
3396
  ],
3437
3397
  execute: async (context) => {
3438
3398
  try {
3439
- const { target, content } = this.parseMessageArgs(context.message);
3399
+ const { content } = this.parseMessageArgs(context.message);
3440
3400
  await this.client.sendMessage(context.chatId, {
3441
3401
  msg_type: "text",
3442
3402
  content: { text: content }
3443
3403
  });
3444
- return {
3445
- success: true,
3446
- content: "\u2705 \u6D88\u606F\u5DF2\u53D1\u9001"
3447
- };
3404
+ return { success: true, content: "\u2705 \u6D88\u606F\u5DF2\u53D1\u9001" };
3448
3405
  } catch (error) {
3449
- return {
3450
- success: false,
3451
- content: `\u274C \u53D1\u9001\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
3452
- };
3406
+ return { success: false, content: `\u274C \u53D1\u9001\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}` };
3453
3407
  }
3454
3408
  }
3455
3409
  };
3456
3410
  }
3457
- /**
3458
- * 创建日程技能
3459
- */
3460
3411
  createEventSkill() {
3461
3412
  return {
3462
3413
  name: "feishu.createEvent",
3463
3414
  description: "\u5728\u98DE\u4E66\u65E5\u5386\u521B\u5EFA\u65E5\u7A0B",
3464
- trigger: /创建日程|创建会议|添加日程/,
3415
+ trigger: /创建日程|创建会议|添加日程|新建日程/,
3465
3416
  riskLevel: "medium",
3466
3417
  examples: [
3467
3418
  "\u521B\u5EFA\u65E5\u7A0B\uFF1A\u660E\u5929\u4E0B\u53483\u70B9\u56E2\u961F\u5468\u4F1A",
@@ -3469,36 +3420,57 @@ var FeishuSkills = class {
3469
3420
  ],
3470
3421
  execute: async (context) => {
3471
3422
  try {
3472
- 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 || "";
3473
3451
  return {
3474
3452
  success: true,
3475
3453
  content: `\u2705 \u65E5\u7A0B\u5DF2\u521B\u5EFA
3476
3454
 
3477
- \u6807\u9898: ${title}
3478
- \u65F6\u95F4: ${startTime}
3479
- \u53C2\u4E0E\u8005: ${attendees.join(", ") || "\u65E0"}`,
3455
+ \u{1F4C5} ${title}
3456
+ \u{1F550} ${this.formatDateTime(startTime)} - ${this.formatDateTime(endTime)}
3457
+
3458
+ \u{1F517} ${eventLink}`,
3480
3459
  actions: [
3481
- { label: "\u67E5\u770B\u65E5\u7A0B", action: "view_calendar" },
3482
- { label: "\u7F16\u8F91", action: "edit_event" }
3460
+ { label: "\u67E5\u770B\u65E5\u7A0B", action: "view_calendar", url: eventLink }
3483
3461
  ]
3484
3462
  };
3485
3463
  } catch (error) {
3486
- return {
3487
- success: false,
3488
- content: `\u274C \u521B\u5EFA\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
3489
- };
3464
+ return { success: false, content: `\u274C \u521B\u5EFA\u65E5\u7A0B\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}` };
3490
3465
  }
3491
3466
  }
3492
3467
  };
3493
3468
  }
3494
- /**
3495
- * 创建文档技能
3496
- */
3497
3469
  createDocSkill() {
3498
3470
  return {
3499
3471
  name: "feishu.createDoc",
3500
3472
  description: "\u521B\u5EFA\u98DE\u4E66\u4E91\u6587\u6863",
3501
- trigger: /创建文档|新建文档/,
3473
+ trigger: /创建文档|新建文档|生成文档/,
3502
3474
  riskLevel: "low",
3503
3475
  examples: [
3504
3476
  "\u521B\u5EFA\u6587\u6863\uFF1A\u9879\u76EE\u9700\u6C42\u6587\u6863",
@@ -3506,89 +3478,114 @@ var FeishuSkills = class {
3506
3478
  ],
3507
3479
  execute: async (context) => {
3508
3480
  try {
3509
- 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}`;
3510
3498
  return {
3511
3499
  success: true,
3512
3500
  content: `\u2705 \u6587\u6863\u5DF2\u521B\u5EFA
3513
3501
 
3514
- \u6807\u9898: ${title}
3515
- \u4F4D\u7F6E: ${folder || "\u6211\u7684\u7A7A\u95F4"}`,
3502
+ \u{1F4C4} ${title}
3503
+
3504
+ \u{1F517} ${documentUrl}`,
3516
3505
  actions: [
3517
- { label: "\u6253\u5F00\u6587\u6863", action: "open_doc" },
3518
- { label: "\u5206\u4EAB", action: "share_doc" }
3506
+ { label: "\u6253\u5F00\u6587\u6863", action: "open_doc", url: documentUrl }
3519
3507
  ]
3520
3508
  };
3521
3509
  } catch (error) {
3522
- return {
3523
- success: false,
3524
- content: `\u274C \u521B\u5EFA\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
3525
- };
3510
+ return { success: false, content: `\u274C \u521B\u5EFA\u6587\u6863\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}` };
3526
3511
  }
3527
3512
  }
3528
3513
  };
3529
3514
  }
3530
- /**
3531
- * 上传文件技能
3532
- */
3533
3515
  uploadFileSkill() {
3534
3516
  return {
3535
3517
  name: "feishu.uploadFile",
3536
3518
  description: "\u4E0A\u4F20\u6587\u4EF6\u5230\u98DE\u4E66",
3537
- trigger: /上传文件|发送文件/,
3519
+ trigger: /上传文件|发送文件|传文件/,
3538
3520
  riskLevel: "low",
3539
3521
  execute: async (context) => {
3540
- return {
3541
- success: true,
3542
- content: "\u{1F4CE} \u6587\u4EF6\u4E0A\u4F20\u529F\u80FD\u5F00\u53D1\u4E2D"
3543
- };
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" };
3544
3523
  }
3545
3524
  };
3546
3525
  }
3547
- /**
3548
- * 创建群公告技能
3549
- */
3550
3526
  createAnnouncementSkill() {
3551
3527
  return {
3552
3528
  name: "feishu.createAnnouncement",
3553
3529
  description: "\u53D1\u9001\u7FA4\u516C\u544A",
3554
- trigger: /发送公告|群公告/,
3530
+ trigger: /发送公告|群公告|发布公告|发公告/,
3555
3531
  riskLevel: "medium",
3556
3532
  requireConfirm: true,
3557
3533
  execute: async (context) => {
3558
- return {
3559
- success: true,
3560
- content: "\u{1F4E2} \u516C\u544A\u529F\u80FD\u5F00\u53D1\u4E2D"
3561
- };
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
+ }
3562
3546
  }
3563
3547
  };
3564
3548
  }
3565
- // 辅助方法
3566
3549
  parseMessageArgs(message) {
3567
- const match = message.match(/给(.+?)(.+)/);
3568
- if (match) {
3569
- return { target: match[1], content: match[2] };
3570
- }
3550
+ const match = message.match(/给(.+?)[::]\s*(.+)/);
3551
+ if (match) return { target: match[1], content: match[2] };
3571
3552
  return { target: "current", content: message };
3572
3553
  }
3573
3554
  parseEventArgs(message) {
3574
- const titleMatch = message.match(/日程[::]\s*(.+?)(?=\s|$)/);
3575
- const timeMatch = message.match(/(明天|今天|后天|\d{1,2}点|\d{1,2}:\d{2})/);
3576
- return {
3577
- title: titleMatch ? titleMatch[1] : "\u65B0\u65E5\u7A0B",
3578
- startTime: timeMatch ? timeMatch[1] : "\u672A\u6307\u5B9A\u65F6\u95F4",
3579
- attendees: []
3580
- };
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: "" };
3581
3573
  }
3582
3574
  parseDocArgs(message) {
3583
- const match = message.match(/文档[::]\s*(.+?)(?=\s|$)/);
3584
- return {
3585
- title: match ? match[1] : "\u672A\u547D\u540D\u6587\u6863",
3586
- content: ""
3587
- };
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")}`;
3588
3588
  }
3589
- /**
3590
- * 获取所有飞书技能
3591
- */
3592
3589
  getAllSkills() {
3593
3590
  return [
3594
3591
  this.sendMessageSkill(),