index.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /**
  2. * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. *
  5. * OpenClaw Lark/Feishu plugin entry point.
  6. *
  7. * Registers the Feishu channel and all tool families:
  8. * doc, wiki, drive, perm, bitable, task, calendar.
  9. *
  10. * NOTE: 本文件位于 openclaw-lark-patch,Docker 构建时覆盖 vendor 包(见 docker/Dockerfile.feishu)。
  11. */
  12. import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';
  13. import { emptyPluginConfigSchema } from 'openclaw/plugin-sdk';
  14. import { feishuPlugin } from './src/channel/plugin';
  15. import { LarkClient } from './src/core/lark-client';
  16. import { registerOapiTools } from './src/tools/oapi/index';
  17. import { registerFeishuMcpDocTools } from './src/tools/mcp/doc/index';
  18. import { registerFeishuOAuthTool } from './src/tools/oauth';
  19. import { registerFeishuOAuthBatchAuthTool } from './src/tools/oauth-batch-auth';
  20. import {
  21. runDiagnosis,
  22. formatDiagReportCli,
  23. traceByMessageId,
  24. formatTraceOutput,
  25. analyzeTrace,
  26. } from './src/commands/diagnose';
  27. import { registerCommands } from './src/commands/index';
  28. import { larkLogger } from './src/core/lark-logger';
  29. import { emitSecurityWarnings } from './src/core/security-check';
  30. const log = larkLogger('plugin');
  31. // ---------------------------------------------------------------------------
  32. // Re-exports for external consumers
  33. // ---------------------------------------------------------------------------
  34. export { monitorFeishuProvider } from './src/channel/monitor';
  35. export { sendMessageFeishu, sendCardFeishu, updateCardFeishu, editMessageFeishu } from './src/messaging/outbound/send';
  36. export { getMessageFeishu } from './src/messaging/outbound/fetch';
  37. export {
  38. uploadImageLark,
  39. uploadFileLark,
  40. sendImageLark,
  41. sendFileLark,
  42. sendAudioLark,
  43. uploadAndSendMediaLark,
  44. } from './src/messaging/outbound/media';
  45. export {
  46. sendTextLark,
  47. sendCardLark,
  48. sendMediaLark,
  49. sendRawImLark,
  50. type SendTextLarkParams,
  51. type SendCardLarkParams,
  52. type SendMediaLarkParams,
  53. type SendRawImLarkParams,
  54. } from './src/messaging/outbound/deliver';
  55. export { type FeishuChannelData } from './src/messaging/outbound/outbound';
  56. export { probeFeishu } from './src/channel/probe';
  57. export {
  58. addReactionFeishu,
  59. removeReactionFeishu,
  60. listReactionsFeishu,
  61. FeishuEmoji,
  62. VALID_FEISHU_EMOJI_TYPES,
  63. } from './src/messaging/outbound/reactions';
  64. export { forwardMessageFeishu } from './src/messaging/outbound/forward';
  65. export {
  66. updateChatFeishu,
  67. addChatMembersFeishu,
  68. removeChatMembersFeishu,
  69. listChatMembersFeishu,
  70. } from './src/messaging/outbound/chat-manage';
  71. export { feishuMessageActions } from './src/messaging/outbound/actions';
  72. export {
  73. mentionedBot,
  74. nonBotMentions,
  75. extractMessageBody,
  76. formatMentionForText,
  77. formatMentionForCard,
  78. formatMentionAllForText,
  79. formatMentionAllForCard,
  80. buildMentionedMessage,
  81. buildMentionedCardContent,
  82. type MentionInfo,
  83. } from './src/messaging/inbound/mention';
  84. export { feishuPlugin } from './src/channel/plugin';
  85. export type {
  86. MessageContext,
  87. RawMessage,
  88. RawSender,
  89. FeishuMessageContext,
  90. FeishuReactionCreatedEvent,
  91. } from './src/messaging/types';
  92. export { handleFeishuReaction } from './src/messaging/inbound/reaction-handler';
  93. export { parseMessageEvent } from './src/messaging/inbound/parse';
  94. export { checkMessageGate } from './src/messaging/inbound/gate';
  95. export { isMessageExpired } from './src/messaging/inbound/dedup';
  96. // ---------------------------------------------------------------------------
  97. // Plugin definition
  98. // ---------------------------------------------------------------------------
  99. const plugin = {
  100. id: 'openclaw-lark',
  101. name: 'Feishu',
  102. description: 'Lark/Feishu channel plugin with im/doc/wiki/drive/task/calendar tools',
  103. configSchema: emptyPluginConfigSchema(),
  104. register(api: OpenClawPluginApi) {
  105. LarkClient.setRuntime(api.runtime);
  106. api.registerChannel({ plugin: feishuPlugin });
  107. // ========================================
  108. // Register OAPI tools (calendar, task - using Feishu Open API directly)
  109. registerOapiTools(api);
  110. // Register MCP doc tools (using Model Context Protocol)
  111. registerFeishuMcpDocTools(api);
  112. // Register OAuth tool (UAT device flow authorization)
  113. registerFeishuOAuthTool(api);
  114. // Register OAuth batch auth tool (batch authorization for all app scopes)
  115. registerFeishuOAuthBatchAuthTool(api);
  116. // ---- Tool call hooks (auto-trace AI tool invocations) ----
  117. api.on('before_tool_call', (event) => {
  118. log.info(`tool call: ${event.toolName} params=${JSON.stringify(event.params)}`);
  119. });
  120. api.on('after_tool_call', (event) => {
  121. if (event.error) {
  122. log.error(`tool fail: ${event.toolName} ${event.error} (${event.durationMs ?? 0}ms)`);
  123. } else {
  124. log.info(`tool done: ${event.toolName} ok (${event.durationMs ?? 0}ms)`);
  125. }
  126. });
  127. // ---- Diagnostic commands ----
  128. // CLI: openclaw feishu-diagnose [--trace <messageId>]
  129. api.registerCli(
  130. (ctx) => {
  131. ctx.program
  132. .command('feishu-diagnose')
  133. .description('运行飞书插件诊断,检查配置、连通性和权限状态')
  134. .option('--trace <messageId>', '按 message_id 追踪完整处理链路')
  135. .option('--analyze', '分析追踪日志(需配合 --trace 使用)')
  136. .action(async (opts: { trace?: string; analyze?: boolean }) => {
  137. try {
  138. if (opts.trace) {
  139. const lines = await traceByMessageId(opts.trace);
  140. // eslint-disable-next-line no-console -- CLI 命令直接输出到终端
  141. console.log(formatTraceOutput(lines, opts.trace));
  142. if (opts.analyze && lines.length > 0) {
  143. // eslint-disable-next-line no-console -- CLI 命令直接输出到终端
  144. console.log(analyzeTrace(lines, opts.trace));
  145. }
  146. } else {
  147. const report = await runDiagnosis({
  148. config: ctx.config,
  149. logger: ctx.logger,
  150. });
  151. // eslint-disable-next-line no-console -- CLI 命令直接输出到终端
  152. console.log(formatDiagReportCli(report));
  153. if (report.overallStatus === 'unhealthy') {
  154. process.exitCode = 1;
  155. }
  156. }
  157. } catch (err) {
  158. ctx.logger.error(`诊断命令执行失败: ${err}`);
  159. process.exitCode = 1;
  160. }
  161. });
  162. },
  163. { commands: ['feishu-diagnose'] },
  164. );
  165. // Chat commands: /feishu_diagnose, /feishu_doctor, /feishu_auth, /feishu
  166. registerCommands(api);
  167. // ---- Multi-account security checks ----
  168. if (api.config) {
  169. emitSecurityWarnings(api.config, api.logger);
  170. }
  171. },
  172. };
  173. export default plugin;