@lark-project/openclaw-lark-project 2026.3.131
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/LICENSE +21 -0
- package/README.md +80 -0
- package/README.zh.md +80 -0
- package/dist/index.js +172 -0
- package/dist/index.js.map +7 -0
- package/dist/skills/feishu-bitable/SKILL.md +248 -0
- package/dist/skills/feishu-bitable/references/examples.md +813 -0
- package/dist/skills/feishu-bitable/references/field-properties.md +763 -0
- package/dist/skills/feishu-bitable/references/record-values.md +911 -0
- package/dist/skills/feishu-calendar/SKILL.md +244 -0
- package/dist/skills/feishu-channel-rules/SKILL.md +18 -0
- package/dist/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
- package/dist/skills/feishu-create-doc/SKILL.md +719 -0
- package/dist/skills/feishu-fetch-doc/SKILL.md +93 -0
- package/dist/skills/feishu-im-read/SKILL.md +163 -0
- package/dist/skills/feishu-project/SKILL.md +122 -0
- package/dist/skills/feishu-task/SKILL.md +293 -0
- package/dist/skills/feishu-troubleshoot/SKILL.md +70 -0
- package/dist/skills/feishu-update-doc/SKILL.md +285 -0
- package/dist/src/card/builder.js +293 -0
- package/dist/src/card/builder.js.map +7 -0
- package/dist/src/card/cardkit.js +126 -0
- package/dist/src/card/cardkit.js.map +7 -0
- package/dist/src/card/flush-controller.js +107 -0
- package/dist/src/card/flush-controller.js.map +7 -0
- package/dist/src/card/markdown-style.js +57 -0
- package/dist/src/card/markdown-style.js.map +7 -0
- package/dist/src/card/reply-dispatcher-types.js +39 -0
- package/dist/src/card/reply-dispatcher-types.js.map +7 -0
- package/dist/src/card/reply-dispatcher.js +245 -0
- package/dist/src/card/reply-dispatcher.js.map +7 -0
- package/dist/src/card/reply-mode.js +29 -0
- package/dist/src/card/reply-mode.js.map +7 -0
- package/dist/src/card/streaming-card-controller.js +653 -0
- package/dist/src/card/streaming-card-controller.js.map +7 -0
- package/dist/src/card/unavailable-guard.js +76 -0
- package/dist/src/card/unavailable-guard.js.map +7 -0
- package/dist/src/channel/abort-detect.js +79 -0
- package/dist/src/channel/abort-detect.js.map +7 -0
- package/dist/src/channel/chat-queue.js +50 -0
- package/dist/src/channel/chat-queue.js.map +7 -0
- package/dist/src/channel/config-adapter.js +89 -0
- package/dist/src/channel/config-adapter.js.map +7 -0
- package/dist/src/channel/directory.js +133 -0
- package/dist/src/channel/directory.js.map +7 -0
- package/dist/src/channel/event-handlers.js +175 -0
- package/dist/src/channel/event-handlers.js.map +7 -0
- package/dist/src/channel/monitor.js +108 -0
- package/dist/src/channel/monitor.js.map +7 -0
- package/dist/src/channel/onboarding-config.js +76 -0
- package/dist/src/channel/onboarding-config.js.map +7 -0
- package/dist/src/channel/onboarding-migrate.js +55 -0
- package/dist/src/channel/onboarding-migrate.js.map +7 -0
- package/dist/src/channel/onboarding.js +285 -0
- package/dist/src/channel/onboarding.js.map +7 -0
- package/dist/src/channel/plugin.js +260 -0
- package/dist/src/channel/plugin.js.map +7 -0
- package/dist/src/channel/probe.js +14 -0
- package/dist/src/channel/probe.js.map +7 -0
- package/dist/src/channel/types.js +1 -0
- package/dist/src/channel/types.js.map +7 -0
- package/dist/src/commands/auth.js +73 -0
- package/dist/src/commands/auth.js.map +7 -0
- package/dist/src/commands/diagnose.js +658 -0
- package/dist/src/commands/diagnose.js.map +7 -0
- package/dist/src/commands/doctor.js +327 -0
- package/dist/src/commands/doctor.js.map +7 -0
- package/dist/src/commands/index.js +124 -0
- package/dist/src/commands/index.js.map +7 -0
- package/dist/src/core/accounts.js +129 -0
- package/dist/src/core/accounts.js.map +7 -0
- package/dist/src/core/agent-config.js +60 -0
- package/dist/src/core/agent-config.js.map +7 -0
- package/dist/src/core/api-error.js +55 -0
- package/dist/src/core/api-error.js.map +7 -0
- package/dist/src/core/app-owner-fallback.js +17 -0
- package/dist/src/core/app-owner-fallback.js.map +7 -0
- package/dist/src/core/app-scope-checker.js +95 -0
- package/dist/src/core/app-scope-checker.js.map +7 -0
- package/dist/src/core/auth-errors.js +120 -0
- package/dist/src/core/auth-errors.js.map +7 -0
- package/dist/src/core/chat-info-cache.js +102 -0
- package/dist/src/core/chat-info-cache.js.map +7 -0
- package/dist/src/core/config-schema.js +150 -0
- package/dist/src/core/config-schema.js.map +7 -0
- package/dist/src/core/device-flow.js +174 -0
- package/dist/src/core/device-flow.js.map +7 -0
- package/dist/src/core/feishu-fetch.js +12 -0
- package/dist/src/core/feishu-fetch.js.map +7 -0
- package/dist/src/core/footer-config.js +16 -0
- package/dist/src/core/footer-config.js.map +7 -0
- package/dist/src/core/lark-client.js +322 -0
- package/dist/src/core/lark-client.js.map +7 -0
- package/dist/src/core/lark-logger.js +92 -0
- package/dist/src/core/lark-logger.js.map +7 -0
- package/dist/src/core/lark-ticket.js +18 -0
- package/dist/src/core/lark-ticket.js.map +7 -0
- package/dist/src/core/message-unavailable.js +119 -0
- package/dist/src/core/message-unavailable.js.map +7 -0
- package/dist/src/core/owner-policy.js +25 -0
- package/dist/src/core/owner-policy.js.map +7 -0
- package/dist/src/core/permission-url.js +37 -0
- package/dist/src/core/permission-url.js.map +7 -0
- package/dist/src/core/project-auth.js +177 -0
- package/dist/src/core/project-auth.js.map +7 -0
- package/dist/src/core/project-oauth-flow.js +124 -0
- package/dist/src/core/project-oauth-flow.js.map +7 -0
- package/dist/src/core/project-token-store.js +172 -0
- package/dist/src/core/project-token-store.js.map +7 -0
- package/dist/src/core/raw-request.js +45 -0
- package/dist/src/core/raw-request.js.map +7 -0
- package/dist/src/core/scope-manager.js +62 -0
- package/dist/src/core/scope-manager.js.map +7 -0
- package/dist/src/core/security-check.js +118 -0
- package/dist/src/core/security-check.js.map +7 -0
- package/dist/src/core/shutdown-hooks.js +37 -0
- package/dist/src/core/shutdown-hooks.js.map +7 -0
- package/dist/src/core/targets.js +55 -0
- package/dist/src/core/targets.js.map +7 -0
- package/dist/src/core/token-store.js +215 -0
- package/dist/src/core/token-store.js.map +7 -0
- package/dist/src/core/tool-client.js +335 -0
- package/dist/src/core/tool-client.js.map +7 -0
- package/dist/src/core/tool-scopes.js +207 -0
- package/dist/src/core/tool-scopes.js.map +7 -0
- package/dist/src/core/tools-config.js +57 -0
- package/dist/src/core/tools-config.js.map +7 -0
- package/dist/src/core/types.js +1 -0
- package/dist/src/core/types.js.map +7 -0
- package/dist/src/core/uat-client.js +124 -0
- package/dist/src/core/uat-client.js.map +7 -0
- package/dist/src/core/version.js +27 -0
- package/dist/src/core/version.js.map +7 -0
- package/dist/src/messaging/converters/audio.js +19 -0
- package/dist/src/messaging/converters/audio.js.map +7 -0
- package/dist/src/messaging/converters/calendar.js +46 -0
- package/dist/src/messaging/converters/calendar.js.map +7 -0
- package/dist/src/messaging/converters/content-converter.js +61 -0
- package/dist/src/messaging/converters/content-converter.js.map +7 -0
- package/dist/src/messaging/converters/file.js +18 -0
- package/dist/src/messaging/converters/file.js.map +7 -0
- package/dist/src/messaging/converters/folder.js +18 -0
- package/dist/src/messaging/converters/folder.js.map +7 -0
- package/dist/src/messaging/converters/hongbao.js +14 -0
- package/dist/src/messaging/converters/hongbao.js.map +7 -0
- package/dist/src/messaging/converters/image.js +16 -0
- package/dist/src/messaging/converters/image.js.map +7 -0
- package/dist/src/messaging/converters/index.js +48 -0
- package/dist/src/messaging/converters/index.js.map +7 -0
- package/dist/src/messaging/converters/interactive/card-converter.js +1040 -0
- package/dist/src/messaging/converters/interactive/card-converter.js.map +7 -0
- package/dist/src/messaging/converters/interactive/card-utils.js +36 -0
- package/dist/src/messaging/converters/interactive/card-utils.js.map +7 -0
- package/dist/src/messaging/converters/interactive/index.js +19 -0
- package/dist/src/messaging/converters/interactive/index.js.map +7 -0
- package/dist/src/messaging/converters/interactive/legacy.js +53 -0
- package/dist/src/messaging/converters/interactive/legacy.js.map +7 -0
- package/dist/src/messaging/converters/interactive/types.js +23 -0
- package/dist/src/messaging/converters/interactive/types.js.map +7 -0
- package/dist/src/messaging/converters/location.js +17 -0
- package/dist/src/messaging/converters/location.js.map +7 -0
- package/dist/src/messaging/converters/merge-forward.js +143 -0
- package/dist/src/messaging/converters/merge-forward.js.map +7 -0
- package/dist/src/messaging/converters/post.js +113 -0
- package/dist/src/messaging/converters/post.js.map +7 -0
- package/dist/src/messaging/converters/share.js +22 -0
- package/dist/src/messaging/converters/share.js.map +7 -0
- package/dist/src/messaging/converters/sticker.js +16 -0
- package/dist/src/messaging/converters/sticker.js.map +7 -0
- package/dist/src/messaging/converters/system.js +25 -0
- package/dist/src/messaging/converters/system.js.map +7 -0
- package/dist/src/messaging/converters/text.js +12 -0
- package/dist/src/messaging/converters/text.js.map +7 -0
- package/dist/src/messaging/converters/todo.js +37 -0
- package/dist/src/messaging/converters/todo.js.map +7 -0
- package/dist/src/messaging/converters/types.js +1 -0
- package/dist/src/messaging/converters/types.js.map +7 -0
- package/dist/src/messaging/converters/unknown.js +13 -0
- package/dist/src/messaging/converters/unknown.js.map +7 -0
- package/dist/src/messaging/converters/utils.js +35 -0
- package/dist/src/messaging/converters/utils.js.map +7 -0
- package/dist/src/messaging/converters/video-chat.js +21 -0
- package/dist/src/messaging/converters/video-chat.js.map +7 -0
- package/dist/src/messaging/converters/video.js +30 -0
- package/dist/src/messaging/converters/video.js.map +7 -0
- package/dist/src/messaging/converters/vote.js +24 -0
- package/dist/src/messaging/converters/vote.js.map +7 -0
- package/dist/src/messaging/inbound/dedup.js +82 -0
- package/dist/src/messaging/inbound/dedup.js.map +7 -0
- package/dist/src/messaging/inbound/dispatch-builders.js +98 -0
- package/dist/src/messaging/inbound/dispatch-builders.js.map +7 -0
- package/dist/src/messaging/inbound/dispatch-commands.js +94 -0
- package/dist/src/messaging/inbound/dispatch-commands.js.map +7 -0
- package/dist/src/messaging/inbound/dispatch-context.js +96 -0
- package/dist/src/messaging/inbound/dispatch-context.js.map +7 -0
- package/dist/src/messaging/inbound/dispatch.js +150 -0
- package/dist/src/messaging/inbound/dispatch.js.map +7 -0
- package/dist/src/messaging/inbound/enrich.js +137 -0
- package/dist/src/messaging/inbound/enrich.js.map +7 -0
- package/dist/src/messaging/inbound/gate-effects.js +28 -0
- package/dist/src/messaging/inbound/gate-effects.js.map +7 -0
- package/dist/src/messaging/inbound/gate.js +163 -0
- package/dist/src/messaging/inbound/gate.js.map +7 -0
- package/dist/src/messaging/inbound/handler.js +132 -0
- package/dist/src/messaging/inbound/handler.js.map +7 -0
- package/dist/src/messaging/inbound/media-resolver.js +70 -0
- package/dist/src/messaging/inbound/media-resolver.js.map +7 -0
- package/dist/src/messaging/inbound/mention.js +50 -0
- package/dist/src/messaging/inbound/mention.js.map +7 -0
- package/dist/src/messaging/inbound/parse-io.js +41 -0
- package/dist/src/messaging/inbound/parse-io.js.map +7 -0
- package/dist/src/messaging/inbound/parse.js +79 -0
- package/dist/src/messaging/inbound/parse.js.map +7 -0
- package/dist/src/messaging/inbound/permission.js +30 -0
- package/dist/src/messaging/inbound/permission.js.map +7 -0
- package/dist/src/messaging/inbound/policy.js +83 -0
- package/dist/src/messaging/inbound/policy.js.map +7 -0
- package/dist/src/messaging/inbound/reaction-handler.js +162 -0
- package/dist/src/messaging/inbound/reaction-handler.js.map +7 -0
- package/dist/src/messaging/inbound/user-name-cache.js +172 -0
- package/dist/src/messaging/inbound/user-name-cache.js.map +7 -0
- package/dist/src/messaging/outbound/actions.js +239 -0
- package/dist/src/messaging/outbound/actions.js.map +7 -0
- package/dist/src/messaging/outbound/chat-manage.js +74 -0
- package/dist/src/messaging/outbound/chat-manage.js.map +7 -0
- package/dist/src/messaging/outbound/deliver.js +162 -0
- package/dist/src/messaging/outbound/deliver.js.map +7 -0
- package/dist/src/messaging/outbound/fetch.js +7 -0
- package/dist/src/messaging/outbound/fetch.js.map +7 -0
- package/dist/src/messaging/outbound/forward.js +31 -0
- package/dist/src/messaging/outbound/forward.js.map +7 -0
- package/dist/src/messaging/outbound/media-url-utils.js +101 -0
- package/dist/src/messaging/outbound/media-url-utils.js.map +7 -0
- package/dist/src/messaging/outbound/media.js +463 -0
- package/dist/src/messaging/outbound/media.js.map +7 -0
- package/dist/src/messaging/outbound/outbound.js +95 -0
- package/dist/src/messaging/outbound/outbound.js.map +7 -0
- package/dist/src/messaging/outbound/reactions.js +312 -0
- package/dist/src/messaging/outbound/reactions.js.map +7 -0
- package/dist/src/messaging/outbound/send.js +194 -0
- package/dist/src/messaging/outbound/send.js.map +7 -0
- package/dist/src/messaging/outbound/typing.js +77 -0
- package/dist/src/messaging/outbound/typing.js.map +7 -0
- package/dist/src/messaging/shared/message-lookup.js +84 -0
- package/dist/src/messaging/shared/message-lookup.js.map +7 -0
- package/dist/src/messaging/types.js +1 -0
- package/dist/src/messaging/types.js.map +7 -0
- package/dist/src/tools/auto-auth.js +714 -0
- package/dist/src/tools/auto-auth.js.map +7 -0
- package/dist/src/tools/helpers.js +133 -0
- package/dist/src/tools/helpers.js.map +7 -0
- package/dist/src/tools/mcp/doc/create.js +35 -0
- package/dist/src/tools/mcp/doc/create.js.map +7 -0
- package/dist/src/tools/mcp/doc/fetch.js +33 -0
- package/dist/src/tools/mcp/doc/fetch.js.map +7 -0
- package/dist/src/tools/mcp/doc/index.js +32 -0
- package/dist/src/tools/mcp/doc/index.js.map +7 -0
- package/dist/src/tools/mcp/doc/update.js +61 -0
- package/dist/src/tools/mcp/doc/update.js.map +7 -0
- package/dist/src/tools/mcp/project/endpoint.js +25 -0
- package/dist/src/tools/mcp/project/endpoint.js.map +7 -0
- package/dist/src/tools/mcp/project/index.js +27 -0
- package/dist/src/tools/mcp/project/index.js.map +7 -0
- package/dist/src/tools/mcp/project/tools.js +579 -0
- package/dist/src/tools/mcp/project/tools.js.map +7 -0
- package/dist/src/tools/mcp/shared.js +170 -0
- package/dist/src/tools/mcp/shared.js.map +7 -0
- package/dist/src/tools/oapi/bitable/app-table-field.js +244 -0
- package/dist/src/tools/oapi/bitable/app-table-field.js.map +7 -0
- package/dist/src/tools/oapi/bitable/app-table-record.js +501 -0
- package/dist/src/tools/oapi/bitable/app-table-record.js.map +7 -0
- package/dist/src/tools/oapi/bitable/app-table-view.js +226 -0
- package/dist/src/tools/oapi/bitable/app-table-view.js.map +7 -0
- package/dist/src/tools/oapi/bitable/app-table.js +278 -0
- package/dist/src/tools/oapi/bitable/app-table.js.map +7 -0
- package/dist/src/tools/oapi/bitable/app.js +200 -0
- package/dist/src/tools/oapi/bitable/app.js.map +7 -0
- package/dist/src/tools/oapi/bitable/index.js +13 -0
- package/dist/src/tools/oapi/bitable/index.js.map +7 -0
- package/dist/src/tools/oapi/calendar/calendar.js +131 -0
- package/dist/src/tools/oapi/calendar/calendar.js.map +7 -0
- package/dist/src/tools/oapi/calendar/event-attendee.js +301 -0
- package/dist/src/tools/oapi/calendar/event-attendee.js.map +7 -0
- package/dist/src/tools/oapi/calendar/event.js +834 -0
- package/dist/src/tools/oapi/calendar/event.js.map +7 -0
- package/dist/src/tools/oapi/calendar/freebusy.js +111 -0
- package/dist/src/tools/oapi/calendar/freebusy.js.map +7 -0
- package/dist/src/tools/oapi/calendar/index.js +11 -0
- package/dist/src/tools/oapi/calendar/index.js.map +7 -0
- package/dist/src/tools/oapi/chat/chat.js +132 -0
- package/dist/src/tools/oapi/chat/chat.js.map +7 -0
- package/dist/src/tools/oapi/chat/index.js +11 -0
- package/dist/src/tools/oapi/chat/index.js.map +7 -0
- package/dist/src/tools/oapi/chat/members.js +83 -0
- package/dist/src/tools/oapi/chat/members.js.map +7 -0
- package/dist/src/tools/oapi/common/get-user.js +95 -0
- package/dist/src/tools/oapi/common/get-user.js.map +7 -0
- package/dist/src/tools/oapi/common/index.js +7 -0
- package/dist/src/tools/oapi/common/index.js.map +7 -0
- package/dist/src/tools/oapi/common/search-user.js +67 -0
- package/dist/src/tools/oapi/common/search-user.js.map +7 -0
- package/dist/src/tools/oapi/drive/doc-comments.js +310 -0
- package/dist/src/tools/oapi/drive/doc-comments.js.map +7 -0
- package/dist/src/tools/oapi/drive/doc-media.js +314 -0
- package/dist/src/tools/oapi/drive/doc-media.js.map +7 -0
- package/dist/src/tools/oapi/drive/file.js +548 -0
- package/dist/src/tools/oapi/drive/file.js.map +7 -0
- package/dist/src/tools/oapi/drive/index.js +29 -0
- package/dist/src/tools/oapi/drive/index.js.map +7 -0
- package/dist/src/tools/oapi/helpers.js +199 -0
- package/dist/src/tools/oapi/helpers.js.map +7 -0
- package/dist/src/tools/oapi/im/format-messages.js +128 -0
- package/dist/src/tools/oapi/im/format-messages.js.map +7 -0
- package/dist/src/tools/oapi/im/index.js +15 -0
- package/dist/src/tools/oapi/im/index.js.map +7 -0
- package/dist/src/tools/oapi/im/message-read.js +404 -0
- package/dist/src/tools/oapi/im/message-read.js.map +7 -0
- package/dist/src/tools/oapi/im/message.js +179 -0
- package/dist/src/tools/oapi/im/message.js.map +7 -0
- package/dist/src/tools/oapi/im/resource.js +126 -0
- package/dist/src/tools/oapi/im/resource.js.map +7 -0
- package/dist/src/tools/oapi/im/time-utils.js +169 -0
- package/dist/src/tools/oapi/im/time-utils.js.map +7 -0
- package/dist/src/tools/oapi/im/user-name-uat.js +103 -0
- package/dist/src/tools/oapi/im/user-name-uat.js.map +7 -0
- package/dist/src/tools/oapi/index.js +56 -0
- package/dist/src/tools/oapi/index.js.map +7 -0
- package/dist/src/tools/oapi/sdk-types.js +1 -0
- package/dist/src/tools/oapi/sdk-types.js.map +7 -0
- package/dist/src/tools/oapi/search/doc-search.js +215 -0
- package/dist/src/tools/oapi/search/doc-search.js.map +7 -0
- package/dist/src/tools/oapi/search/index.js +25 -0
- package/dist/src/tools/oapi/search/index.js.map +7 -0
- package/dist/src/tools/oapi/sheets/index.js +25 -0
- package/dist/src/tools/oapi/sheets/index.js.map +7 -0
- package/dist/src/tools/oapi/sheets/sheet.js +652 -0
- package/dist/src/tools/oapi/sheets/sheet.js.map +7 -0
- package/dist/src/tools/oapi/task/comment.js +151 -0
- package/dist/src/tools/oapi/task/comment.js.map +7 -0
- package/dist/src/tools/oapi/task/index.js +11 -0
- package/dist/src/tools/oapi/task/index.js.map +7 -0
- package/dist/src/tools/oapi/task/subtask.js +175 -0
- package/dist/src/tools/oapi/task/subtask.js.map +7 -0
- package/dist/src/tools/oapi/task/task.js +405 -0
- package/dist/src/tools/oapi/task/task.js.map +7 -0
- package/dist/src/tools/oapi/task/tasklist.js +366 -0
- package/dist/src/tools/oapi/task/tasklist.js.map +7 -0
- package/dist/src/tools/oapi/wiki/index.js +27 -0
- package/dist/src/tools/oapi/wiki/index.js.map +7 -0
- package/dist/src/tools/oapi/wiki/space-node.js +311 -0
- package/dist/src/tools/oapi/wiki/space-node.js.map +7 -0
- package/dist/src/tools/oapi/wiki/space.js +148 -0
- package/dist/src/tools/oapi/wiki/space.js.map +7 -0
- package/dist/src/tools/oauth-batch-auth.js +125 -0
- package/dist/src/tools/oauth-batch-auth.js.map +7 -0
- package/dist/src/tools/oauth-cards.js +269 -0
- package/dist/src/tools/oauth-cards.js.map +7 -0
- package/dist/src/tools/oauth.js +538 -0
- package/dist/src/tools/oauth.js.map +7 -0
- package/dist/src/tools/onboarding-auth.js +101 -0
- package/dist/src/tools/onboarding-auth.js.map +7 -0
- package/dist/src/tools/project-oauth.js +305 -0
- package/dist/src/tools/project-oauth.js.map +7 -0
- package/dist/src/tools/tat/im/index.js +9 -0
- package/dist/src/tools/tat/im/index.js.map +7 -0
- package/dist/src/tools/tat/im/resource.js +123 -0
- package/dist/src/tools/tat/im/resource.js.map +7 -0
- package/package.json +64 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { LarkClient } from "../../core/lark-client";
|
|
2
|
+
import { downloadMessageResourceFeishu } from "../outbound/media";
|
|
3
|
+
async function downloadResources(params) {
|
|
4
|
+
const { cfg, messageId, resources, maxBytes, log, accountId } = params;
|
|
5
|
+
if (resources.length === 0) return [];
|
|
6
|
+
const out = [];
|
|
7
|
+
const core = LarkClient.runtime;
|
|
8
|
+
for (const res of resources) {
|
|
9
|
+
try {
|
|
10
|
+
const resourceType = res.type === "image" ? "image" : "file";
|
|
11
|
+
const result = await downloadMessageResourceFeishu({
|
|
12
|
+
cfg,
|
|
13
|
+
messageId,
|
|
14
|
+
fileKey: res.fileKey,
|
|
15
|
+
type: resourceType,
|
|
16
|
+
accountId
|
|
17
|
+
});
|
|
18
|
+
let contentType = result.contentType;
|
|
19
|
+
if (!contentType) {
|
|
20
|
+
contentType = await core.media.detectMime({ buffer: result.buffer });
|
|
21
|
+
}
|
|
22
|
+
const fileName = result.fileName || res.fileName;
|
|
23
|
+
const saved = await core.channel.media.saveMediaBuffer(result.buffer, contentType, "inbound", maxBytes, fileName);
|
|
24
|
+
const placeholder = inferPlaceholderFromType(res.type);
|
|
25
|
+
out.push({
|
|
26
|
+
path: saved.path,
|
|
27
|
+
contentType: saved.contentType,
|
|
28
|
+
placeholder,
|
|
29
|
+
fileKey: res.fileKey,
|
|
30
|
+
resourceType: res.type
|
|
31
|
+
});
|
|
32
|
+
log?.(`feishu: downloaded ${res.type} resource ${res.fileKey}, saved to ${saved.path}`);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
log?.(`feishu: failed to download ${res.type} resource ${res.fileKey}: ${String(err)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function inferPlaceholderFromType(type) {
|
|
40
|
+
switch (type) {
|
|
41
|
+
case "image":
|
|
42
|
+
return "<media:image>";
|
|
43
|
+
case "file":
|
|
44
|
+
return "<media:document>";
|
|
45
|
+
case "audio":
|
|
46
|
+
return "<media:audio>";
|
|
47
|
+
case "video":
|
|
48
|
+
return "<media:video>";
|
|
49
|
+
case "sticker":
|
|
50
|
+
return "<media:sticker>";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function buildFeishuMediaPayload(mediaList) {
|
|
54
|
+
const first = mediaList[0];
|
|
55
|
+
const mediaPaths = mediaList.map((m) => m.path);
|
|
56
|
+
const mediaTypes = mediaList.map((m) => m.contentType).filter(Boolean);
|
|
57
|
+
return {
|
|
58
|
+
MediaPath: first?.path,
|
|
59
|
+
MediaType: first?.contentType,
|
|
60
|
+
MediaUrl: first?.path,
|
|
61
|
+
MediaPaths: mediaPaths.length > 0 ? mediaPaths : void 0,
|
|
62
|
+
MediaUrls: mediaPaths.length > 0 ? mediaPaths : void 0,
|
|
63
|
+
MediaTypes: mediaTypes.length > 0 ? mediaTypes : void 0
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
buildFeishuMediaPayload,
|
|
68
|
+
downloadResources
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=media-resolver.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/messaging/inbound/media-resolver.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Media resolution and payload building for inbound Feishu messages.\n *\n * Downloads media files based on ResourceDescriptors extracted during\n * the content converter phase, and builds the payload object spread\n * into the agent envelope.\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport type { FeishuMediaInfo, ResourceDescriptor } from '../types';\nimport { LarkClient } from '../../core/lark-client';\nimport { downloadMessageResourceFeishu } from '../outbound/media';\n\n// ---------------------------------------------------------------------------\n// Resource-descriptor-based download\n// ---------------------------------------------------------------------------\n\n/**\n * Download media files based on pre-extracted ResourceDescriptors from\n * the converter phase.\n */\nexport async function downloadResources(params: {\n cfg: ClawdbotConfig;\n messageId: string;\n resources: ResourceDescriptor[];\n maxBytes: number;\n log?: (msg: string) => void;\n accountId?: string;\n}): Promise<FeishuMediaInfo[]> {\n const { cfg, messageId, resources, maxBytes, log, accountId } = params;\n\n if (resources.length === 0) return [];\n\n const out: FeishuMediaInfo[] = [];\n const core = LarkClient.runtime;\n\n for (const res of resources) {\n try {\n const resourceType = res.type === 'image' ? 'image' : 'file';\n const result = await downloadMessageResourceFeishu({\n cfg,\n messageId,\n fileKey: res.fileKey,\n type: resourceType,\n accountId,\n });\n\n let contentType = result.contentType;\n if (!contentType) {\n contentType = await core.media.detectMime({ buffer: result.buffer });\n }\n\n const fileName = result.fileName || res.fileName;\n const saved = await core.channel.media.saveMediaBuffer(result.buffer, contentType, 'inbound', maxBytes, fileName);\n\n const placeholder = inferPlaceholderFromType(res.type);\n out.push({\n path: saved.path,\n contentType: saved.contentType,\n placeholder,\n fileKey: res.fileKey,\n resourceType: res.type,\n });\n\n log?.(`feishu: downloaded ${res.type} resource ${res.fileKey}, saved to ${saved.path}`);\n } catch (err) {\n log?.(`feishu: failed to download ${res.type} resource ${res.fileKey}: ${String(err)}`);\n }\n }\n\n return out;\n}\n\nfunction inferPlaceholderFromType(type: ResourceDescriptor['type']): string {\n switch (type) {\n case 'image':\n return '<media:image>';\n case 'file':\n return '<media:document>';\n case 'audio':\n return '<media:audio>';\n case 'video':\n return '<media:video>';\n case 'sticker':\n return '<media:sticker>';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Media payload builder\n// ---------------------------------------------------------------------------\n\nexport function buildFeishuMediaPayload(mediaList: FeishuMediaInfo[]): {\n MediaPath?: string;\n MediaType?: string;\n MediaUrl?: string;\n MediaPaths?: string[];\n MediaUrls?: string[];\n MediaTypes?: string[];\n} {\n const first = mediaList[0];\n const mediaPaths = mediaList.map((m) => m.path);\n const mediaTypes = mediaList.map((m) => m.contentType).filter(Boolean) as string[];\n\n return {\n MediaPath: first?.path,\n MediaType: first?.contentType,\n MediaUrl: first?.path,\n MediaPaths: mediaPaths.length > 0 ? mediaPaths : undefined,\n MediaUrls: mediaPaths.length > 0 ? mediaPaths : undefined,\n MediaTypes: mediaTypes.length > 0 ? mediaTypes : undefined,\n };\n}\n"],
|
|
5
|
+
"mappings": "AAaA,SAAS,kBAAkB;AAC3B,SAAS,qCAAqC;AAU9C,eAAsB,kBAAkB,QAOT;AAC7B,QAAM,EAAE,KAAK,WAAW,WAAW,UAAU,KAAK,UAAU,IAAI;AAEhE,MAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AAEpC,QAAM,MAAyB,CAAC;AAChC,QAAM,OAAO,WAAW;AAExB,aAAW,OAAO,WAAW;AAC3B,QAAI;AACF,YAAM,eAAe,IAAI,SAAS,UAAU,UAAU;AACtD,YAAM,SAAS,MAAM,8BAA8B;AAAA,QACjD;AAAA,QACA;AAAA,QACA,SAAS,IAAI;AAAA,QACb,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAED,UAAI,cAAc,OAAO;AACzB,UAAI,CAAC,aAAa;AAChB,sBAAc,MAAM,KAAK,MAAM,WAAW,EAAE,QAAQ,OAAO,OAAO,CAAC;AAAA,MACrE;AAEA,YAAM,WAAW,OAAO,YAAY,IAAI;AACxC,YAAM,QAAQ,MAAM,KAAK,QAAQ,MAAM,gBAAgB,OAAO,QAAQ,aAAa,WAAW,UAAU,QAAQ;AAEhH,YAAM,cAAc,yBAAyB,IAAI,IAAI;AACrD,UAAI,KAAK;AAAA,QACP,MAAM,MAAM;AAAA,QACZ,aAAa,MAAM;AAAA,QACnB;AAAA,QACA,SAAS,IAAI;AAAA,QACb,cAAc,IAAI;AAAA,MACpB,CAAC;AAED,YAAM,sBAAsB,IAAI,IAAI,aAAa,IAAI,OAAO,cAAc,MAAM,IAAI,EAAE;AAAA,IACxF,SAAS,KAAK;AACZ,YAAM,8BAA8B,IAAI,IAAI,aAAa,IAAI,OAAO,KAAK,OAAO,GAAG,CAAC,EAAE;AAAA,IACxF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,yBAAyB,MAA0C;AAC1E,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAMO,SAAS,wBAAwB,WAOtC;AACA,QAAM,QAAQ,UAAU,CAAC;AACzB,QAAM,aAAa,UAAU,IAAI,CAAC,MAAM,EAAE,IAAI;AAC9C,QAAM,aAAa,UAAU,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,OAAO;AAErE,SAAO;AAAA,IACL,WAAW,OAAO;AAAA,IAClB,WAAW,OAAO;AAAA,IAClB,UAAU,OAAO;AAAA,IACjB,YAAY,WAAW,SAAS,IAAI,aAAa;AAAA,IACjD,WAAW,WAAW,SAAS,IAAI,aAAa;AAAA,IAChD,YAAY,WAAW,SAAS,IAAI,aAAa;AAAA,EACnD;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { escapeRegExp } from "../converters/utils";
|
|
2
|
+
function mentionedBot(ctx) {
|
|
3
|
+
return ctx.mentions.some((m) => m.isBot);
|
|
4
|
+
}
|
|
5
|
+
function nonBotMentions(ctx) {
|
|
6
|
+
return ctx.mentions.filter((m) => !m.isBot);
|
|
7
|
+
}
|
|
8
|
+
function extractMessageBody(text, allMentionKeys) {
|
|
9
|
+
let result = text;
|
|
10
|
+
for (const key of allMentionKeys) {
|
|
11
|
+
result = result.replace(new RegExp(escapeRegExp(key) + "\\s*", "g"), "");
|
|
12
|
+
}
|
|
13
|
+
return result.trim();
|
|
14
|
+
}
|
|
15
|
+
function formatMentionForText(target) {
|
|
16
|
+
return `<at user_id="${target.openId}">${target.name}</at>`;
|
|
17
|
+
}
|
|
18
|
+
function formatMentionAllForText() {
|
|
19
|
+
return `<at user_id="all">Everyone</at>`;
|
|
20
|
+
}
|
|
21
|
+
function formatMentionForCard(target) {
|
|
22
|
+
return `<at id=${target.openId}></at>`;
|
|
23
|
+
}
|
|
24
|
+
function formatMentionAllForCard() {
|
|
25
|
+
return `<at id=all></at>`;
|
|
26
|
+
}
|
|
27
|
+
function buildMentionedMessage(targets, message) {
|
|
28
|
+
if (targets.length === 0) return message;
|
|
29
|
+
const mentionTags = targets.map(formatMentionForText).join(" ");
|
|
30
|
+
return `${mentionTags}
|
|
31
|
+
${message}`;
|
|
32
|
+
}
|
|
33
|
+
function buildMentionedCardContent(targets, message) {
|
|
34
|
+
if (targets.length === 0) return message;
|
|
35
|
+
const mentionTags = targets.map(formatMentionForCard).join(" ");
|
|
36
|
+
return `${mentionTags}
|
|
37
|
+
${message}`;
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
buildMentionedCardContent,
|
|
41
|
+
buildMentionedMessage,
|
|
42
|
+
extractMessageBody,
|
|
43
|
+
formatMentionAllForCard,
|
|
44
|
+
formatMentionAllForText,
|
|
45
|
+
formatMentionForCard,
|
|
46
|
+
formatMentionForText,
|
|
47
|
+
mentionedBot,
|
|
48
|
+
nonBotMentions
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=mention.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/messaging/inbound/mention.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * @mention utilities for the Lark/Feishu channel plugin.\n *\n * All logic is based on `MentionInfo[]` from `MessageContext.mentions`.\n * Provides:\n * - Derive helpers: `mentionedBot()`, `nonBotMentions()`\n * - Format helpers for outbound text and card messages.\n */\n\nimport type { MentionInfo, MessageContext } from '../types';\nimport { escapeRegExp } from '../converters/utils';\n\nexport type { MentionInfo } from '../types';\n\n// ---------------------------------------------------------------------------\n// Derive helpers (work on MentionInfo[])\n// ---------------------------------------------------------------------------\n\n/** Whether the bot was @-mentioned. */\nexport function mentionedBot(ctx: MessageContext): boolean {\n return ctx.mentions.some((m) => m.isBot);\n}\n\n/** All non-bot mentions. */\nexport function nonBotMentions(ctx: MessageContext): MentionInfo[] {\n return ctx.mentions.filter((m) => !m.isBot);\n}\n\n// ---------------------------------------------------------------------------\n// extractMessageBody\n// ---------------------------------------------------------------------------\n\n/**\n * Remove all @mention placeholder keys from the message text.\n */\nexport function extractMessageBody(text: string, allMentionKeys: string[]): string {\n let result = text;\n for (const key of allMentionKeys) {\n result = result.replace(new RegExp(escapeRegExp(key) + '\\\\s*', 'g'), '');\n }\n return result.trim();\n}\n\n// ---------------------------------------------------------------------------\n// Format helpers -- text messages\n// ---------------------------------------------------------------------------\n\n/**\n * Format a mention for a Feishu text / post message.\n * @returns e.g. `<at user_id=\"ou_xxx\">Alice</at>`\n */\nexport function formatMentionForText(target: MentionInfo): string {\n return `<at user_id=\"${target.openId}\">${target.name}</at>`;\n}\n\n/** Format an @everyone mention for text / post. */\nexport function formatMentionAllForText(): string {\n return `<at user_id=\"all\">Everyone</at>`;\n}\n\n// ---------------------------------------------------------------------------\n// Format helpers -- interactive card messages\n// ---------------------------------------------------------------------------\n\n/**\n * Format a mention for a Feishu Interactive Card.\n * @returns e.g. `<at id=ou_xxx></at>`\n */\nexport function formatMentionForCard(target: MentionInfo): string {\n return `<at id=${target.openId}></at>`;\n}\n\n/** Format an @everyone mention for card. */\nexport function formatMentionAllForCard(): string {\n return `<at id=all></at>`;\n}\n\n// ---------------------------------------------------------------------------\n// Build helpers (prepend mentions to message body)\n// ---------------------------------------------------------------------------\n\n/** Prepend @mention tags (text format) to a message body. */\nexport function buildMentionedMessage(targets: MentionInfo[], message: string): string {\n if (targets.length === 0) return message;\n const mentionTags = targets.map(formatMentionForText).join(' ');\n return `${mentionTags}\\n${message}`;\n}\n\n/** Prepend @mention tags (card format) to card markdown content. */\nexport function buildMentionedCardContent(targets: MentionInfo[], message: string): string {\n if (targets.length === 0) return message;\n const mentionTags = targets.map(formatMentionForCard).join(' ');\n return `${mentionTags}\\n${message}`;\n}\n"],
|
|
5
|
+
"mappings": "AAaA,SAAS,oBAAoB;AAStB,SAAS,aAAa,KAA8B;AACzD,SAAO,IAAI,SAAS,KAAK,CAAC,MAAM,EAAE,KAAK;AACzC;AAGO,SAAS,eAAe,KAAoC;AACjE,SAAO,IAAI,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK;AAC5C;AASO,SAAS,mBAAmB,MAAc,gBAAkC;AACjF,MAAI,SAAS;AACb,aAAW,OAAO,gBAAgB;AAChC,aAAS,OAAO,QAAQ,IAAI,OAAO,aAAa,GAAG,IAAI,QAAQ,GAAG,GAAG,EAAE;AAAA,EACzE;AACA,SAAO,OAAO,KAAK;AACrB;AAUO,SAAS,qBAAqB,QAA6B;AAChE,SAAO,gBAAgB,OAAO,MAAM,KAAK,OAAO,IAAI;AACtD;AAGO,SAAS,0BAAkC;AAChD,SAAO;AACT;AAUO,SAAS,qBAAqB,QAA6B;AAChE,SAAO,UAAU,OAAO,MAAM;AAChC;AAGO,SAAS,0BAAkC;AAChD,SAAO;AACT;AAOO,SAAS,sBAAsB,SAAwB,SAAyB;AACrF,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,cAAc,QAAQ,IAAI,oBAAoB,EAAE,KAAK,GAAG;AAC9D,SAAO,GAAG,WAAW;AAAA,EAAK,OAAO;AACnC;AAGO,SAAS,0BAA0B,SAAwB,SAAyB;AACzF,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,cAAc,QAAQ,IAAI,oBAAoB,EAAE,KAAK,GAAG;AAC9D,SAAO,GAAG,WAAW;AAAA,EAAK,OAAO;AACnC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { larkLogger } from "../../core/lark-logger";
|
|
2
|
+
import { createBatchResolveNames } from "./user-name-cache";
|
|
3
|
+
const log = larkLogger("inbound/parse-io");
|
|
4
|
+
async function fetchCardContent(messageId, larkClient) {
|
|
5
|
+
try {
|
|
6
|
+
const response = await larkClient.sdk.request({
|
|
7
|
+
method: "GET",
|
|
8
|
+
url: `/open-apis/im/v1/messages/${messageId}`,
|
|
9
|
+
params: {
|
|
10
|
+
user_id_type: "open_id",
|
|
11
|
+
card_msg_content_type: "raw_card_content"
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
return response?.data?.items?.[0]?.body?.content ?? void 0;
|
|
15
|
+
} catch (err) {
|
|
16
|
+
log.warn(`fetchCardContent failed for ${messageId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
17
|
+
return void 0;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function createFetchSubMessages(larkClient) {
|
|
21
|
+
return async (msgId) => {
|
|
22
|
+
const response = await larkClient.sdk.request({
|
|
23
|
+
method: "GET",
|
|
24
|
+
url: `/open-apis/im/v1/messages/${msgId}`,
|
|
25
|
+
params: { user_id_type: "open_id", card_msg_content_type: "raw_card_content" }
|
|
26
|
+
});
|
|
27
|
+
if (response?.code !== 0) {
|
|
28
|
+
throw new Error(`API error: code=${response?.code} msg=${response?.msg}`);
|
|
29
|
+
}
|
|
30
|
+
return response?.data?.items ?? [];
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function createParseResolveNames(account) {
|
|
34
|
+
return createBatchResolveNames(account, (...args) => log.info(args.map(String).join(" ")));
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
createFetchSubMessages,
|
|
38
|
+
createParseResolveNames,
|
|
39
|
+
fetchCardContent
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=parse-io.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/messaging/inbound/parse-io.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * I/O adapters for inbound message parsing.\n *\n * Contains API-calling functions that are used during the parse phase\n * but separated from the pure parsing logic in parse.ts.\n */\n\nimport type { LarkClient as LarkClientType } from '../../core/lark-client';\nimport type { LarkAccount } from '../../core/types';\nimport { larkLogger } from '../../core/lark-logger';\nimport { createBatchResolveNames } from './user-name-cache';\n\nconst log = larkLogger('inbound/parse-io');\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Shape of a single message item returned by the im/v1/messages API. */\nexport interface ApiMessageItem {\n message_id?: string;\n msg_type?: string;\n body?: { content?: string };\n sender?: { id?: string; sender_type?: string };\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Card content fetcher\n// ---------------------------------------------------------------------------\n\n/**\n * \u5BF9 interactive \u6D88\u606F\uFF0C\u901A\u8FC7 TAT \u8C03\u7528 API \u83B7\u53D6\u5B8C\u6574 v2 \u5361\u7247\u5185\u5BB9\u3002\n * \u4E8B\u4EF6\u63A8\u9001\u7684 content \u53EF\u80FD\u4E0D\u5305\u542B json_card\uFF0CAPI \u8C03\u7528\u53EF\u8FD4\u56DE\u5B8C\u6574\u7684 raw_card_content\u3002\n * \u5931\u8D25\u65F6\u8FD4\u56DE undefined\uFF0C\u8C03\u7528\u65B9 fallback \u5230\u539F\u59CB content\u3002\n *\n * Note: `larkClient.sdk` \u7684\u7C7B\u578B\u5B9A\u4E49\u4E0D\u66B4\u9732 raw `request` \u65B9\u6CD5\uFF0C\n * \u56E0\u6B64\u8FD9\u91CC\u4F7F\u7528 `as any` \u65AD\u8A00\u8C03\u7528\u3002\n */\nexport async function fetchCardContent(messageId: string, larkClient: LarkClientType): Promise<string | undefined> {\n try {\n // SDK \u7C7B\u578B\u4E0D\u66B4\u9732 raw request \u65B9\u6CD5\uFF0C\u9700\u8981 as any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const response = await (larkClient.sdk as any).request({\n method: 'GET',\n url: `/open-apis/im/v1/messages/${messageId}`,\n params: {\n user_id_type: 'open_id',\n card_msg_content_type: 'raw_card_content',\n },\n });\n return response?.data?.items?.[0]?.body?.content ?? undefined;\n } catch (err) {\n log.warn(`fetchCardContent failed for ${messageId}: ${err instanceof Error ? err.message : String(err)}`);\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sub-message fetcher (for merge_forward)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a `fetchSubMessages` callback for use in `ConvertContext`.\n *\n * The returned function calls the im/v1/messages API to fetch\n * sub-messages of a merge_forward message.\n *\n * Note: `larkClient.sdk` \u7684\u7C7B\u578B\u5B9A\u4E49\u4E0D\u66B4\u9732 raw `request` \u65B9\u6CD5\uFF0C\n * \u56E0\u6B64\u8FD9\u91CC\u4F7F\u7528 `as any` \u65AD\u8A00\u8C03\u7528\u3002\n */\nexport function createFetchSubMessages(larkClient: LarkClientType): (msgId: string) => Promise<ApiMessageItem[]> {\n return async (msgId) => {\n // SDK \u7C7B\u578B\u4E0D\u66B4\u9732 raw request \u65B9\u6CD5\uFF0C\u9700\u8981 as any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const response = await (larkClient.sdk as any).request({\n method: 'GET',\n url: `/open-apis/im/v1/messages/${msgId}`,\n params: { user_id_type: 'open_id', card_msg_content_type: 'raw_card_content' },\n });\n if (response?.code !== 0) {\n throw new Error(`API error: code=${response?.code} msg=${response?.msg}`);\n }\n return response?.data?.items ?? [];\n };\n}\n\n// ---------------------------------------------------------------------------\n// Batch resolve names callback factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a `batchResolveNames` callback for use in `ConvertContext`.\n *\n * Wraps `createBatchResolveNames` from user-name-cache.ts, providing\n * the account and log function.\n */\nexport function createParseResolveNames(account: LarkAccount): (openIds: string[]) => Promise<void> {\n return createBatchResolveNames(account, (...args: unknown[]) => log.info(args.map(String).join(' ')));\n}\n"],
|
|
5
|
+
"mappings": "AAYA,SAAS,kBAAkB;AAC3B,SAAS,+BAA+B;AAExC,MAAM,MAAM,WAAW,kBAAkB;AA2BzC,eAAsB,iBAAiB,WAAmB,YAAyD;AACjH,MAAI;AAGF,UAAM,WAAW,MAAO,WAAW,IAAY,QAAQ;AAAA,MACrD,QAAQ;AAAA,MACR,KAAK,6BAA6B,SAAS;AAAA,MAC3C,QAAQ;AAAA,QACN,cAAc;AAAA,QACd,uBAAuB;AAAA,MACzB;AAAA,IACF,CAAC;AACD,WAAO,UAAU,MAAM,QAAQ,CAAC,GAAG,MAAM,WAAW;AAAA,EACtD,SAAS,KAAK;AACZ,QAAI,KAAK,+BAA+B,SAAS,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AACxG,WAAO;AAAA,EACT;AACF;AAeO,SAAS,uBAAuB,YAA0E;AAC/G,SAAO,OAAO,UAAU;AAGtB,UAAM,WAAW,MAAO,WAAW,IAAY,QAAQ;AAAA,MACrD,QAAQ;AAAA,MACR,KAAK,6BAA6B,KAAK;AAAA,MACvC,QAAQ,EAAE,cAAc,WAAW,uBAAuB,mBAAmB;AAAA,IAC/E,CAAC;AACD,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,IAAI,MAAM,mBAAmB,UAAU,IAAI,QAAQ,UAAU,GAAG,EAAE;AAAA,IAC1E;AACA,WAAO,UAAU,MAAM,SAAS,CAAC;AAAA,EACnC;AACF;AAYO,SAAS,wBAAwB,SAA4D;AAClG,SAAO,wBAAwB,SAAS,IAAI,SAAoB,IAAI,KAAK,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC;AACtG;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { convertMessageContent } from "../converters/content-converter";
|
|
2
|
+
import { getUserNameCache } from "./user-name-cache";
|
|
3
|
+
import { getLarkAccount } from "../../core/accounts";
|
|
4
|
+
import { LarkClient } from "../../core/lark-client";
|
|
5
|
+
import { larkLogger } from "../../core/lark-logger";
|
|
6
|
+
import { fetchCardContent, createFetchSubMessages, createParseResolveNames } from "./parse-io";
|
|
7
|
+
const log = larkLogger("inbound/parse");
|
|
8
|
+
async function parseMessageEvent(event, botOpenId, expandCtx) {
|
|
9
|
+
const mentionMap = /* @__PURE__ */ new Map();
|
|
10
|
+
const mentionList = [];
|
|
11
|
+
for (const m of event.message.mentions ?? []) {
|
|
12
|
+
const openId = m.id?.open_id ?? "";
|
|
13
|
+
if (!openId) continue;
|
|
14
|
+
const info = {
|
|
15
|
+
key: m.key,
|
|
16
|
+
openId,
|
|
17
|
+
name: m.name,
|
|
18
|
+
isBot: Boolean(botOpenId && openId === botOpenId)
|
|
19
|
+
};
|
|
20
|
+
mentionMap.set(m.key, info);
|
|
21
|
+
mentionList.push(info);
|
|
22
|
+
}
|
|
23
|
+
const mentionsByOpenId = /* @__PURE__ */ new Map();
|
|
24
|
+
for (const info of mentionList) {
|
|
25
|
+
mentionsByOpenId.set(info.openId, info);
|
|
26
|
+
}
|
|
27
|
+
const acctId = expandCtx?.accountId;
|
|
28
|
+
const larkClient = expandCtx ? LarkClient.fromCfg(expandCtx.cfg, acctId) : void 0;
|
|
29
|
+
let fetchSubMessages;
|
|
30
|
+
let batchResolveNames;
|
|
31
|
+
if (expandCtx) {
|
|
32
|
+
const account = getLarkAccount(expandCtx.cfg, acctId);
|
|
33
|
+
fetchSubMessages = createFetchSubMessages(larkClient);
|
|
34
|
+
batchResolveNames = createParseResolveNames(account);
|
|
35
|
+
}
|
|
36
|
+
let effectiveContent = event.message.content;
|
|
37
|
+
if (event.message.message_type === "interactive" && expandCtx) {
|
|
38
|
+
const fullContent = await fetchCardContent(event.message.message_id, larkClient);
|
|
39
|
+
if (fullContent) {
|
|
40
|
+
effectiveContent = fullContent;
|
|
41
|
+
log.info("replaced interactive content with full v2 card data");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const convertCtx = {
|
|
45
|
+
mentions: mentionMap,
|
|
46
|
+
mentionsByOpenId,
|
|
47
|
+
messageId: event.message.message_id,
|
|
48
|
+
botOpenId,
|
|
49
|
+
cfg: expandCtx?.cfg,
|
|
50
|
+
accountId: acctId,
|
|
51
|
+
resolveUserName: acctId ? (openId) => getUserNameCache(acctId).get(openId) : void 0,
|
|
52
|
+
fetchSubMessages,
|
|
53
|
+
batchResolveNames,
|
|
54
|
+
stripBotMentions: true
|
|
55
|
+
};
|
|
56
|
+
const { content, resources } = await convertMessageContent(effectiveContent, event.message.message_type, convertCtx);
|
|
57
|
+
const createTimeStr = event.message.create_time;
|
|
58
|
+
const createTime = createTimeStr ? parseInt(createTimeStr, 10) : void 0;
|
|
59
|
+
return {
|
|
60
|
+
chatId: event.message.chat_id,
|
|
61
|
+
messageId: event.message.message_id,
|
|
62
|
+
senderId: event.sender.sender_id.open_id || "",
|
|
63
|
+
chatType: event.message.chat_type,
|
|
64
|
+
rootId: event.message.root_id || void 0,
|
|
65
|
+
parentId: event.message.parent_id || void 0,
|
|
66
|
+
threadId: event.message.thread_id || void 0,
|
|
67
|
+
content,
|
|
68
|
+
contentType: event.message.message_type,
|
|
69
|
+
resources,
|
|
70
|
+
mentions: mentionList,
|
|
71
|
+
createTime: Number.isNaN(createTime) ? void 0 : createTime,
|
|
72
|
+
rawMessage: effectiveContent !== event.message.content ? { ...event.message, content: effectiveContent } : event.message,
|
|
73
|
+
rawSender: event.sender
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
parseMessageEvent
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/messaging/inbound/parse.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Event parsing for inbound Feishu messages.\n *\n * Converts a raw FeishuMessageEvent into a normalised MessageContext.\n * All mention information is captured in `mentions: MentionInfo[]`;\n * downstream logic derives `mentionedBot` and non-bot targets from it.\n *\n * When `expandCtx` is provided, `cfg` and `accountId` are passed into\n * the converter context so that async converters (e.g. merge_forward)\n * can make API calls during parsing.\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport type { FeishuMessageEvent, MessageContext, MentionInfo } from '../types';\nimport { convertMessageContent, type ConvertContext } from '../converters/content-converter';\nimport { getUserNameCache } from './user-name-cache';\nimport { getLarkAccount } from '../../core/accounts';\nimport { LarkClient } from '../../core/lark-client';\nimport { larkLogger } from '../../core/lark-logger';\nimport { fetchCardContent, createFetchSubMessages, createParseResolveNames } from './parse-io';\n\nconst log = larkLogger('inbound/parse');\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a raw Feishu message event into a normalised MessageContext.\n *\n * @param expandCtx When provided, cfg/accountId are used to create\n * callbacks for async converters (e.g. merge_forward)\n * to fetch sub-messages and resolve sender names.\n */\nexport async function parseMessageEvent(\n event: FeishuMessageEvent,\n botOpenId?: string,\n expandCtx?: {\n /** account \u7EA7\u522B\u7684 ClawdbotConfig\uFF08channels.feishu \u5DF2\u66FF\u6362\u4E3A per-account \u5408\u5E76\u540E\u7684\u914D\u7F6E\uFF09 */\n cfg: ClawdbotConfig;\n accountId?: string;\n },\n): Promise<MessageContext> {\n // 1. Build MentionInfo list from event mentions\n const mentionMap = new Map<string, MentionInfo>();\n const mentionList: MentionInfo[] = [];\n\n for (const m of event.message.mentions ?? []) {\n const openId = m.id?.open_id ?? '';\n if (!openId) continue;\n\n const info: MentionInfo = {\n key: m.key,\n openId,\n name: m.name,\n isBot: Boolean(botOpenId && openId === botOpenId),\n };\n mentionMap.set(m.key, info);\n mentionList.push(info);\n }\n\n // Build reverse map for O(1) openId lookup\n const mentionsByOpenId = new Map<string, MentionInfo>();\n for (const info of mentionList) {\n mentionsByOpenId.set(info.openId, info);\n }\n\n // 2. Convert content via registered converter\n const acctId = expandCtx?.accountId;\n\n // Create larkClient once when expandCtx is available (used for merge_forward & card fetch)\n const larkClient = expandCtx ? LarkClient.fromCfg(expandCtx.cfg, acctId) : undefined;\n\n // Build merge_forward callbacks when expandCtx is provided\n let fetchSubMessages: ConvertContext['fetchSubMessages'];\n let batchResolveNames: ConvertContext['batchResolveNames'];\n if (expandCtx) {\n const account = getLarkAccount(expandCtx.cfg, acctId);\n fetchSubMessages = createFetchSubMessages(larkClient!);\n batchResolveNames = createParseResolveNames(account);\n }\n\n // For interactive messages, fetch full v2 card content via API\n let effectiveContent = event.message.content;\n if (event.message.message_type === 'interactive' && expandCtx) {\n const fullContent = await fetchCardContent(event.message.message_id, larkClient!);\n if (fullContent) {\n effectiveContent = fullContent;\n log.info('replaced interactive content with full v2 card data');\n }\n }\n\n const convertCtx: ConvertContext = {\n mentions: mentionMap,\n mentionsByOpenId,\n messageId: event.message.message_id,\n botOpenId,\n cfg: expandCtx?.cfg,\n accountId: acctId,\n resolveUserName: acctId ? (openId) => getUserNameCache(acctId).get(openId) : undefined,\n fetchSubMessages,\n batchResolveNames,\n stripBotMentions: true,\n };\n const { content, resources } = await convertMessageContent(effectiveContent, event.message.message_type, convertCtx);\n\n const createTimeStr = event.message.create_time;\n const createTime = createTimeStr ? parseInt(createTimeStr, 10) : undefined;\n\n return {\n chatId: event.message.chat_id,\n messageId: event.message.message_id,\n senderId: event.sender.sender_id.open_id || '',\n chatType: event.message.chat_type,\n rootId: event.message.root_id || undefined,\n parentId: event.message.parent_id || undefined,\n threadId: event.message.thread_id || undefined,\n content,\n contentType: event.message.message_type,\n resources,\n mentions: mentionList,\n createTime: Number.isNaN(createTime) ? undefined : createTime,\n rawMessage:\n effectiveContent !== event.message.content ? { ...event.message, content: effectiveContent } : event.message,\n rawSender: event.sender,\n };\n}\n"],
|
|
5
|
+
"mappings": "AAiBA,SAAS,6BAAkD;AAC3D,SAAS,wBAAwB;AACjC,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB,wBAAwB,+BAA+B;AAElF,MAAM,MAAM,WAAW,eAAe;AAatC,eAAsB,kBACpB,OACA,WACA,WAKyB;AAEzB,QAAM,aAAa,oBAAI,IAAyB;AAChD,QAAM,cAA6B,CAAC;AAEpC,aAAW,KAAK,MAAM,QAAQ,YAAY,CAAC,GAAG;AAC5C,UAAM,SAAS,EAAE,IAAI,WAAW;AAChC,QAAI,CAAC,OAAQ;AAEb,UAAM,OAAoB;AAAA,MACxB,KAAK,EAAE;AAAA,MACP;AAAA,MACA,MAAM,EAAE;AAAA,MACR,OAAO,QAAQ,aAAa,WAAW,SAAS;AAAA,IAClD;AACA,eAAW,IAAI,EAAE,KAAK,IAAI;AAC1B,gBAAY,KAAK,IAAI;AAAA,EACvB;AAGA,QAAM,mBAAmB,oBAAI,IAAyB;AACtD,aAAW,QAAQ,aAAa;AAC9B,qBAAiB,IAAI,KAAK,QAAQ,IAAI;AAAA,EACxC;AAGA,QAAM,SAAS,WAAW;AAG1B,QAAM,aAAa,YAAY,WAAW,QAAQ,UAAU,KAAK,MAAM,IAAI;AAG3E,MAAI;AACJ,MAAI;AACJ,MAAI,WAAW;AACb,UAAM,UAAU,eAAe,UAAU,KAAK,MAAM;AACpD,uBAAmB,uBAAuB,UAAW;AACrD,wBAAoB,wBAAwB,OAAO;AAAA,EACrD;AAGA,MAAI,mBAAmB,MAAM,QAAQ;AACrC,MAAI,MAAM,QAAQ,iBAAiB,iBAAiB,WAAW;AAC7D,UAAM,cAAc,MAAM,iBAAiB,MAAM,QAAQ,YAAY,UAAW;AAChF,QAAI,aAAa;AACf,yBAAmB;AACnB,UAAI,KAAK,qDAAqD;AAAA,IAChE;AAAA,EACF;AAEA,QAAM,aAA6B;AAAA,IACjC,UAAU;AAAA,IACV;AAAA,IACA,WAAW,MAAM,QAAQ;AAAA,IACzB;AAAA,IACA,KAAK,WAAW;AAAA,IAChB,WAAW;AAAA,IACX,iBAAiB,SAAS,CAAC,WAAW,iBAAiB,MAAM,EAAE,IAAI,MAAM,IAAI;AAAA,IAC7E;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA,EACpB;AACA,QAAM,EAAE,SAAS,UAAU,IAAI,MAAM,sBAAsB,kBAAkB,MAAM,QAAQ,cAAc,UAAU;AAEnH,QAAM,gBAAgB,MAAM,QAAQ;AACpC,QAAM,aAAa,gBAAgB,SAAS,eAAe,EAAE,IAAI;AAEjE,SAAO;AAAA,IACL,QAAQ,MAAM,QAAQ;AAAA,IACtB,WAAW,MAAM,QAAQ;AAAA,IACzB,UAAU,MAAM,OAAO,UAAU,WAAW;AAAA,IAC5C,UAAU,MAAM,QAAQ;AAAA,IACxB,QAAQ,MAAM,QAAQ,WAAW;AAAA,IACjC,UAAU,MAAM,QAAQ,aAAa;AAAA,IACrC,UAAU,MAAM,QAAQ,aAAa;AAAA,IACrC;AAAA,IACA,aAAa,MAAM,QAAQ;AAAA,IAC3B;AAAA,IACA,UAAU;AAAA,IACV,YAAY,OAAO,MAAM,UAAU,IAAI,SAAY;AAAA,IACnD,YACE,qBAAqB,MAAM,QAAQ,UAAU,EAAE,GAAG,MAAM,SAAS,SAAS,iBAAiB,IAAI,MAAM;AAAA,IACvG,WAAW,MAAM;AAAA,EACnB;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { extractPermissionGrantUrl } from "../../core/permission-url";
|
|
2
|
+
import { LARK_ERROR } from "../../core/auth-errors";
|
|
3
|
+
function extractPermissionError(err) {
|
|
4
|
+
if (!err || typeof err !== "object") {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const axiosErr = err;
|
|
8
|
+
const data = axiosErr.response?.data;
|
|
9
|
+
if (!data || typeof data !== "object") {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const feishuErr = data;
|
|
13
|
+
if (feishuErr.code !== LARK_ERROR.APP_SCOPE_MISSING) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const msg = feishuErr.msg ?? "";
|
|
17
|
+
const grantUrl = extractPermissionGrantUrl(msg);
|
|
18
|
+
if (!grantUrl) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return { code: feishuErr.code, message: msg, grantUrl };
|
|
22
|
+
}
|
|
23
|
+
const PERMISSION_ERROR_COOLDOWN_MS = 5 * 60 * 1e3;
|
|
24
|
+
const permissionErrorNotifiedAt = /* @__PURE__ */ new Map();
|
|
25
|
+
export {
|
|
26
|
+
PERMISSION_ERROR_COOLDOWN_MS,
|
|
27
|
+
extractPermissionError,
|
|
28
|
+
permissionErrorNotifiedAt
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=permission.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/messaging/inbound/permission.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Permission error extraction and cooldown tracking for Feishu API calls.\n *\n * Extracted from bot.ts: PermissionError type, extractPermissionError,\n * PERMISSION_ERROR_COOLDOWN_MS, permissionErrorNotifiedAt.\n */\n\nimport { extractPermissionGrantUrl } from '../../core/permission-url';\nimport { LARK_ERROR } from '../../core/auth-errors';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface PermissionError {\n code: number;\n message: string;\n grantUrl?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Permission error extraction\n// ---------------------------------------------------------------------------\n\nexport function extractPermissionError(err: unknown): PermissionError | null {\n if (!err || typeof err !== 'object') {\n return null;\n }\n\n const axiosErr = err as { response?: { data?: unknown } };\n const data = axiosErr.response?.data;\n if (!data || typeof data !== 'object') {\n return null;\n }\n\n const feishuErr = data as {\n code?: number;\n msg?: string;\n error?: { permission_violations?: Array<{ uri?: string }> };\n };\n\n // Feishu permission error code\n if (feishuErr.code !== LARK_ERROR.APP_SCOPE_MISSING) {\n return null;\n }\n\n const msg = feishuErr.msg ?? '';\n const grantUrl = extractPermissionGrantUrl(msg);\n\n if (!grantUrl) {\n return null;\n }\n\n return { code: feishuErr.code, message: msg, grantUrl };\n}\n\n// ---------------------------------------------------------------------------\n// Cooldown tracking\n// ---------------------------------------------------------------------------\n\nexport const PERMISSION_ERROR_COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes\nexport const permissionErrorNotifiedAt = new Map<string, number>();\n"],
|
|
5
|
+
"mappings": "AAUA,SAAS,iCAAiC;AAC1C,SAAS,kBAAkB;AAgBpB,SAAS,uBAAuB,KAAsC;AAC3E,MAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACnC,WAAO;AAAA,EACT;AAEA,QAAM,WAAW;AACjB,QAAM,OAAO,SAAS,UAAU;AAChC,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO;AAAA,EACT;AAEA,QAAM,YAAY;AAOlB,MAAI,UAAU,SAAS,WAAW,mBAAmB;AACnD,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,UAAU,OAAO;AAC7B,QAAM,WAAW,0BAA0B,GAAG;AAE9C,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,MAAM,UAAU,MAAM,SAAS,KAAK,SAAS;AACxD;AAMO,MAAM,+BAA+B,IAAI,KAAK;AAC9C,MAAM,4BAA4B,oBAAI,IAAoB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { getLarkAccount } from "../../core/accounts";
|
|
2
|
+
function resolveFeishuAllowlistMatch(params) {
|
|
3
|
+
const allowFrom = params.allowFrom.map((entry) => String(entry).trim().toLowerCase()).filter(Boolean);
|
|
4
|
+
if (allowFrom.length === 0) {
|
|
5
|
+
return { allowed: false };
|
|
6
|
+
}
|
|
7
|
+
if (allowFrom.includes("*")) {
|
|
8
|
+
return { allowed: true, matchKey: "*", matchSource: "wildcard" };
|
|
9
|
+
}
|
|
10
|
+
const senderId = params.senderId.toLowerCase();
|
|
11
|
+
if (allowFrom.includes(senderId)) {
|
|
12
|
+
return { allowed: true, matchKey: senderId, matchSource: "id" };
|
|
13
|
+
}
|
|
14
|
+
return { allowed: false };
|
|
15
|
+
}
|
|
16
|
+
function resolveFeishuGroupConfig(params) {
|
|
17
|
+
const groups = params.cfg?.groups ?? {};
|
|
18
|
+
const groupId = params.groupId?.trim();
|
|
19
|
+
if (!groupId) {
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
const direct = groups[groupId];
|
|
23
|
+
if (direct) {
|
|
24
|
+
return direct;
|
|
25
|
+
}
|
|
26
|
+
const lowered = groupId.toLowerCase();
|
|
27
|
+
const matchKey = Object.keys(groups).find((key) => key.toLowerCase() === lowered);
|
|
28
|
+
return matchKey ? groups[matchKey] : void 0;
|
|
29
|
+
}
|
|
30
|
+
function resolveFeishuGroupToolPolicy(params) {
|
|
31
|
+
const account = getLarkAccount(params.cfg, params.accountId ?? void 0);
|
|
32
|
+
const accountFeishuCfg = account.config;
|
|
33
|
+
if (!accountFeishuCfg) {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
const groupConfig = resolveFeishuGroupConfig({
|
|
37
|
+
cfg: accountFeishuCfg,
|
|
38
|
+
groupId: params.groupId
|
|
39
|
+
});
|
|
40
|
+
return groupConfig?.tools;
|
|
41
|
+
}
|
|
42
|
+
function isFeishuGroupAllowed(params) {
|
|
43
|
+
const { groupPolicy } = params;
|
|
44
|
+
if (groupPolicy === "disabled") {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (groupPolicy === "open") {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return resolveFeishuAllowlistMatch(params).allowed;
|
|
51
|
+
}
|
|
52
|
+
function splitLegacyGroupAllowFrom(rawGroupAllowFrom) {
|
|
53
|
+
const legacyChatIds = [];
|
|
54
|
+
const senderAllowFrom = [];
|
|
55
|
+
for (const entry of rawGroupAllowFrom) {
|
|
56
|
+
const str = String(entry);
|
|
57
|
+
if (str.startsWith("oc_")) {
|
|
58
|
+
legacyChatIds.push(str);
|
|
59
|
+
} else {
|
|
60
|
+
senderAllowFrom.push(str);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { legacyChatIds, senderAllowFrom };
|
|
64
|
+
}
|
|
65
|
+
function resolveGroupSenderPolicyContext(params) {
|
|
66
|
+
const { groupConfig, defaultConfig, accountFeishuCfg, senderGroupAllowFrom } = params;
|
|
67
|
+
const senderPolicy = groupConfig?.groupPolicy ?? defaultConfig?.groupPolicy ?? accountFeishuCfg?.groupPolicy ?? "open";
|
|
68
|
+
const senderAllowFrom = [
|
|
69
|
+
...senderGroupAllowFrom,
|
|
70
|
+
...groupConfig?.allowFrom ?? [],
|
|
71
|
+
...!groupConfig && defaultConfig?.allowFrom ? defaultConfig.allowFrom : []
|
|
72
|
+
];
|
|
73
|
+
return { senderPolicy, senderAllowFrom };
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
isFeishuGroupAllowed,
|
|
77
|
+
resolveFeishuAllowlistMatch,
|
|
78
|
+
resolveFeishuGroupConfig,
|
|
79
|
+
resolveFeishuGroupToolPolicy,
|
|
80
|
+
resolveGroupSenderPolicyContext,
|
|
81
|
+
splitLegacyGroupAllowFrom
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/messaging/inbound/policy.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Access control policies for the Lark/Feishu channel plugin.\n *\n * Provides allowlist matching, group configuration lookup, tool policy\n * extraction, and group access checks.\n */\n\nimport type { ChannelGroupContext, GroupToolPolicyConfig } from 'openclaw/plugin-sdk';\nimport type { FeishuConfig, FeishuGroupConfig } from '../../core/types';\nimport { getLarkAccount } from '../../core/accounts';\n\n// ---------------------------------------------------------------------------\n// Allowlist matching\n// ---------------------------------------------------------------------------\n\nexport interface FeishuAllowlistMatch {\n allowed: boolean;\n matchKey?: string;\n matchSource?: 'wildcard' | 'id' | 'name';\n}\n\n/**\n * Check whether a sender is permitted by a given allowlist.\n *\n * Entries are normalised to lowercase strings before comparison.\n * A single \"*\" entry acts as a wildcard that matches everyone.\n * When the allowlist is empty the result is `{ allowed: false }`.\n */\nexport function resolveFeishuAllowlistMatch(params: {\n allowFrom: Array<string | number>;\n senderId: string;\n senderName?: string | null;\n}): FeishuAllowlistMatch {\n const allowFrom = params.allowFrom.map((entry) => String(entry).trim().toLowerCase()).filter(Boolean);\n\n if (allowFrom.length === 0) {\n return { allowed: false };\n }\n\n // Wildcard: allow everyone\n if (allowFrom.includes('*')) {\n return { allowed: true, matchKey: '*', matchSource: 'wildcard' };\n }\n\n // Match by sender ID\n const senderId = params.senderId.toLowerCase();\n if (allowFrom.includes(senderId)) {\n return { allowed: true, matchKey: senderId, matchSource: 'id' };\n }\n\n/* // Match by sender display name\n const senderName = params.senderName?.toLowerCase();\n if (senderName && allowFrom.includes(senderName)) {\n return { allowed: true, matchKey: senderName, matchSource: 'name' };\n }*/\n\n return { allowed: false };\n}\n\n// ---------------------------------------------------------------------------\n// Group configuration lookup\n// ---------------------------------------------------------------------------\n\n/**\n * Look up the per-group configuration by group ID.\n *\n * Performs a case-insensitive lookup against the keys in `cfg.groups`.\n * Returns `undefined` when no matching group entry is found.\n */\nexport function resolveFeishuGroupConfig(params: {\n cfg?: FeishuConfig;\n groupId?: string | null;\n}): FeishuGroupConfig | undefined {\n const groups = params.cfg?.groups ?? {};\n const groupId = params.groupId?.trim();\n if (!groupId) {\n return undefined;\n }\n\n // Direct (exact-key) lookup first\n const direct = groups[groupId];\n if (direct) {\n return direct;\n }\n\n // Case-insensitive fallback\n const lowered = groupId.toLowerCase();\n const matchKey = Object.keys(groups).find((key) => key.toLowerCase() === lowered);\n return matchKey ? groups[matchKey] : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Group tool policy\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the tool policy configuration from the group config that\n * corresponds to the given group context.\n *\n * \u2605 \u591A\u8D26\u53F7\u914D\u7F6E\u9694\u79BB\uFF1ASDK \u56DE\u8C03\u4F20\u5165\u7684 params.cfg \u662F\u9876\u5C42\u5168\u5C40\u914D\u7F6E\uFF0C\n * cfg.channels.feishu \u4E0D\u5305\u542B per-account \u7684\u8986\u76D6\u503C\u3002\n * \u8FD9\u91CC\u901A\u8FC7 getLarkAccount() \u83B7\u53D6\u5F53\u524D account \u5408\u5E76\u540E\u7684\u914D\u7F6E\uFF0C\n * \u786E\u4FDD\u6BCF\u4E2A\u8D26\u53F7\u7684 groups / tool policy \u914D\u7F6E\u72EC\u7ACB\u751F\u6548\u3002\n */\nexport function resolveFeishuGroupToolPolicy(params: ChannelGroupContext): GroupToolPolicyConfig | undefined {\n // \u4F7F\u7528 getLarkAccount \u83B7\u53D6 per-account \u5408\u5E76\u540E\u7684\u98DE\u4E66\u6E20\u9053\u914D\u7F6E\uFF0C\n // \u800C\u975E\u76F4\u63A5\u8BFB\u53D6 cfg.channels.feishu\uFF08\u9876\u5C42\u5168\u5C40\u914D\u7F6E\uFF09\u3002\n const account = getLarkAccount(params.cfg, params.accountId ?? undefined);\n const accountFeishuCfg = account.config;\n if (!accountFeishuCfg) {\n return undefined;\n }\n\n const groupConfig = resolveFeishuGroupConfig({\n cfg: accountFeishuCfg,\n groupId: params.groupId,\n });\n\n return groupConfig?.tools;\n}\n\n// ---------------------------------------------------------------------------\n// Group access gate\n// ---------------------------------------------------------------------------\n\n/**\n * Determine whether an inbound group message should be processed.\n *\n * - `disabled` --> always rejected\n * - `open` --> always allowed\n * - `allowlist` --> allowed only when the sender matches the allowlist\n */\nexport function isFeishuGroupAllowed(params: {\n groupPolicy: 'open' | 'allowlist' | 'disabled';\n allowFrom: Array<string | number>;\n senderId: string;\n senderName?: string | null;\n}): boolean {\n const { groupPolicy } = params;\n if (groupPolicy === 'disabled') {\n return false;\n }\n if (groupPolicy === 'open') {\n return true;\n }\n // allowlist\n return resolveFeishuAllowlistMatch(params).allowed;\n}\n\n// ---------------------------------------------------------------------------\n// Legacy compat: groupAllowFrom splitting\n// ---------------------------------------------------------------------------\n\n/**\n * Split a raw `groupAllowFrom` array into legacy chat-ID entries\n * (`oc_xxx`) and sender-level entries.\n *\n * Older Feishu configs used `groupAllowFrom` with `oc_xxx` chat IDs to\n * control which groups are allowed. The correct semantic (aligned with\n * Telegram) is sender IDs. This function separates the two concerns so\n * both layers can work independently.\n */\nexport function splitLegacyGroupAllowFrom(rawGroupAllowFrom: Array<string | number>): {\n legacyChatIds: string[];\n senderAllowFrom: string[];\n} {\n const legacyChatIds: string[] = [];\n const senderAllowFrom: string[] = [];\n for (const entry of rawGroupAllowFrom) {\n const str = String(entry);\n if (str.startsWith('oc_')) {\n legacyChatIds.push(str);\n } else {\n senderAllowFrom.push(str);\n }\n }\n return { legacyChatIds, senderAllowFrom };\n}\n\n// ---------------------------------------------------------------------------\n// Sender policy context resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the effective sender-level group policy and the merged\n * `allowFrom` list for sender filtering within a group.\n *\n * The precedence chain for `senderPolicy` is:\n * per-group `groupPolicy` > default (\"*\") group `groupPolicy` >\n * global `groupPolicy` > \"open\" (default).\n *\n * The `senderAllowFrom` is the union of global (non-oc_) entries,\n * per-group entries, and default (\"*\") entries (when no per-group config).\n */\nexport function resolveGroupSenderPolicyContext(params: {\n groupConfig?: FeishuGroupConfig;\n defaultConfig?: FeishuGroupConfig;\n accountFeishuCfg?: FeishuConfig;\n senderGroupAllowFrom: Array<string | number>;\n}): {\n senderPolicy: 'open' | 'allowlist' | 'disabled';\n senderAllowFrom: Array<string | number>;\n} {\n const { groupConfig, defaultConfig, accountFeishuCfg, senderGroupAllowFrom } = params;\n\n const senderPolicy: 'open' | 'allowlist' | 'disabled' =\n groupConfig?.groupPolicy ?? defaultConfig?.groupPolicy ?? accountFeishuCfg?.groupPolicy ?? 'open';\n\n const senderAllowFrom: Array<string | number> = [\n ...senderGroupAllowFrom,\n ...(groupConfig?.allowFrom ?? []),\n ...(!groupConfig && defaultConfig?.allowFrom ? defaultConfig.allowFrom : []),\n ];\n\n return { senderPolicy, senderAllowFrom };\n}\n"],
|
|
5
|
+
"mappings": "AAYA,SAAS,sBAAsB;AAmBxB,SAAS,4BAA4B,QAInB;AACvB,QAAM,YAAY,OAAO,UAAU,IAAI,CAAC,UAAU,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,EAAE,OAAO,OAAO;AAEpG,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B;AAGA,MAAI,UAAU,SAAS,GAAG,GAAG;AAC3B,WAAO,EAAE,SAAS,MAAM,UAAU,KAAK,aAAa,WAAW;AAAA,EACjE;AAGA,QAAM,WAAW,OAAO,SAAS,YAAY;AAC7C,MAAI,UAAU,SAAS,QAAQ,GAAG;AAChC,WAAO,EAAE,SAAS,MAAM,UAAU,UAAU,aAAa,KAAK;AAAA,EAChE;AAQA,SAAO,EAAE,SAAS,MAAM;AAC1B;AAYO,SAAS,yBAAyB,QAGP;AAChC,QAAM,SAAS,OAAO,KAAK,UAAU,CAAC;AACtC,QAAM,UAAU,OAAO,SAAS,KAAK;AACrC,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAGA,QAAM,SAAS,OAAO,OAAO;AAC7B,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AAGA,QAAM,UAAU,QAAQ,YAAY;AACpC,QAAM,WAAW,OAAO,KAAK,MAAM,EAAE,KAAK,CAAC,QAAQ,IAAI,YAAY,MAAM,OAAO;AAChF,SAAO,WAAW,OAAO,QAAQ,IAAI;AACvC;AAeO,SAAS,6BAA6B,QAAgE;AAG3G,QAAM,UAAU,eAAe,OAAO,KAAK,OAAO,aAAa,MAAS;AACxE,QAAM,mBAAmB,QAAQ;AACjC,MAAI,CAAC,kBAAkB;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,yBAAyB;AAAA,IAC3C,KAAK;AAAA,IACL,SAAS,OAAO;AAAA,EAClB,CAAC;AAED,SAAO,aAAa;AACtB;AAaO,SAAS,qBAAqB,QAKzB;AACV,QAAM,EAAE,YAAY,IAAI;AACxB,MAAI,gBAAgB,YAAY;AAC9B,WAAO;AAAA,EACT;AACA,MAAI,gBAAgB,QAAQ;AAC1B,WAAO;AAAA,EACT;AAEA,SAAO,4BAA4B,MAAM,EAAE;AAC7C;AAeO,SAAS,0BAA0B,mBAGxC;AACA,QAAM,gBAA0B,CAAC;AACjC,QAAM,kBAA4B,CAAC;AACnC,aAAW,SAAS,mBAAmB;AACrC,UAAM,MAAM,OAAO,KAAK;AACxB,QAAI,IAAI,WAAW,KAAK,GAAG;AACzB,oBAAc,KAAK,GAAG;AAAA,IACxB,OAAO;AACL,sBAAgB,KAAK,GAAG;AAAA,IAC1B;AAAA,EACF;AACA,SAAO,EAAE,eAAe,gBAAgB;AAC1C;AAiBO,SAAS,gCAAgC,QAQ9C;AACA,QAAM,EAAE,aAAa,eAAe,kBAAkB,qBAAqB,IAAI;AAE/E,QAAM,eACJ,aAAa,eAAe,eAAe,eAAe,kBAAkB,eAAe;AAE7F,QAAM,kBAA0C;AAAA,IAC9C,GAAG;AAAA,IACH,GAAI,aAAa,aAAa,CAAC;AAAA,IAC/B,GAAI,CAAC,eAAe,eAAe,YAAY,cAAc,YAAY,CAAC;AAAA,EAC5E;AAEA,SAAO,EAAE,cAAc,gBAAgB;AACzC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import { DEFAULT_GROUP_HISTORY_LIMIT } from "openclaw/plugin-sdk";
|
|
3
|
+
import { getLarkAccount } from "../../core/accounts";
|
|
4
|
+
import { getMessageFeishu } from "../shared/message-lookup";
|
|
5
|
+
import { isThreadCapableGroup, getChatTypeFeishu } from "../../core/chat-info-cache";
|
|
6
|
+
import { resolveUserName } from "./user-name-cache";
|
|
7
|
+
import { dispatchToAgent } from "./dispatch";
|
|
8
|
+
import { resolveFeishuGroupConfig } from "./policy";
|
|
9
|
+
import { larkLogger } from "../../core/lark-logger";
|
|
10
|
+
const logger = larkLogger("inbound/reaction-handler");
|
|
11
|
+
const REACTION_VERIFY_TIMEOUT_MS = 3e3;
|
|
12
|
+
async function resolveReactionContext(params) {
|
|
13
|
+
const { cfg, event, botOpenId, runtime, accountId } = params;
|
|
14
|
+
const log = runtime?.log ?? ((...args) => logger.info(args.map(String).join(" ")));
|
|
15
|
+
const account = getLarkAccount(cfg, accountId);
|
|
16
|
+
const reactionMode = account.config?.reactionNotifications ?? "own";
|
|
17
|
+
if (reactionMode === "off") {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const emojiType = event.reaction_type?.emoji_type;
|
|
21
|
+
const messageId = event.message_id;
|
|
22
|
+
const operatorOpenId = event.user_id?.open_id ?? "";
|
|
23
|
+
if (!emojiType || !messageId || !operatorOpenId) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
if (event.operator_type === "app" || operatorOpenId === botOpenId) {
|
|
27
|
+
log(`feishu[${accountId}]: ignoring app/self reaction on ${messageId}`);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
if (emojiType === "Typing") {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
if (reactionMode === "own" && !botOpenId) {
|
|
34
|
+
log(`feishu[${accountId}]: bot open_id unavailable, skipping reaction on ${messageId}`);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const msg = await Promise.race([
|
|
38
|
+
getMessageFeishu({ cfg, messageId, accountId }),
|
|
39
|
+
new Promise((resolve) => setTimeout(() => resolve(null), REACTION_VERIFY_TIMEOUT_MS))
|
|
40
|
+
]).catch(() => null);
|
|
41
|
+
if (!msg) {
|
|
42
|
+
log(`feishu[${accountId}]: reacted message ${messageId} not found or timed out, skipping`);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const isBotMessage = msg.senderType === "app" && msg.senderId === account.appId;
|
|
46
|
+
if (reactionMode === "own" && !isBotMessage) {
|
|
47
|
+
log(
|
|
48
|
+
`feishu[${accountId}]: reaction on non-bot message ${messageId}, skipping (senderId=${msg.senderId}, senderType=${msg.senderType}, botOpenId=${botOpenId}, appId=${account.appId})`
|
|
49
|
+
);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const rawChatId = event.chat_id?.trim() || msg.chatId?.trim() || "";
|
|
53
|
+
const effectiveChatId = rawChatId || `p2p:${operatorOpenId}`;
|
|
54
|
+
let chatType = event.chat_type === "group" ? "group" : event.chat_type === "p2p" || event.chat_type === "private" ? "p2p" : msg.chatType === "group" || msg.chatType === "p2p" ? msg.chatType : "p2p";
|
|
55
|
+
if (rawChatId && chatType === "p2p" && !event.chat_type && !msg.chatType) {
|
|
56
|
+
try {
|
|
57
|
+
chatType = await getChatTypeFeishu({ cfg, chatId: rawChatId, accountId });
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
let threadCapable = false;
|
|
62
|
+
const threadSessionEnabled = account.config?.threadSession === true;
|
|
63
|
+
if (rawChatId && chatType === "group") {
|
|
64
|
+
threadCapable = await isThreadCapableGroup({ cfg, chatId: rawChatId, accountId });
|
|
65
|
+
if (threadSessionEnabled && threadCapable) {
|
|
66
|
+
log(`feishu[${accountId}]: reaction on thread-capable group ${rawChatId}, skipping (threadSession enabled)`);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
chatId: effectiveChatId,
|
|
72
|
+
chatType,
|
|
73
|
+
threadId: msg.threadId,
|
|
74
|
+
threadCapable,
|
|
75
|
+
msg
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function handleFeishuReaction(params) {
|
|
79
|
+
const { cfg, event, runtime, chatHistories, accountId, preResolved } = params;
|
|
80
|
+
const log = runtime?.log ?? ((...args) => logger.info(args.map(String).join(" ")));
|
|
81
|
+
const error = runtime?.error ?? ((...args) => logger.error(args.map(String).join(" ")));
|
|
82
|
+
const emojiType = event.reaction_type?.emoji_type;
|
|
83
|
+
const messageId = event.message_id;
|
|
84
|
+
const operatorOpenId = event.user_id?.open_id ?? "";
|
|
85
|
+
const account = getLarkAccount(cfg, accountId);
|
|
86
|
+
const accountFeishuCfg = account.config;
|
|
87
|
+
const accountScopedCfg = {
|
|
88
|
+
...cfg,
|
|
89
|
+
channels: { ...cfg.channels, feishu: accountFeishuCfg }
|
|
90
|
+
};
|
|
91
|
+
const excerpt = preResolved.msg.content.length > 200 ? preResolved.msg.content.slice(0, 200) + "\u2026" : preResolved.msg.content;
|
|
92
|
+
const syntheticText = excerpt ? `[reacted with ${emojiType} to message ${messageId}: "${excerpt}"]` : `[reacted with ${emojiType} to message ${messageId}]`;
|
|
93
|
+
const syntheticMessageId = `${messageId}:reaction:${emojiType}:${crypto.randomUUID()}`;
|
|
94
|
+
let ctx = {
|
|
95
|
+
chatId: preResolved.chatId,
|
|
96
|
+
messageId: syntheticMessageId,
|
|
97
|
+
senderId: operatorOpenId,
|
|
98
|
+
chatType: preResolved.chatType,
|
|
99
|
+
content: syntheticText,
|
|
100
|
+
contentType: "text",
|
|
101
|
+
resources: [],
|
|
102
|
+
mentions: [],
|
|
103
|
+
threadId: preResolved.threadId,
|
|
104
|
+
rawMessage: {
|
|
105
|
+
message_id: syntheticMessageId,
|
|
106
|
+
chat_id: preResolved.chatId,
|
|
107
|
+
chat_type: preResolved.chatType,
|
|
108
|
+
message_type: "text",
|
|
109
|
+
content: JSON.stringify({ text: syntheticText }),
|
|
110
|
+
create_time: event.action_time ?? String(Date.now()),
|
|
111
|
+
thread_id: preResolved.threadId
|
|
112
|
+
},
|
|
113
|
+
rawSender: {
|
|
114
|
+
sender_id: {
|
|
115
|
+
open_id: operatorOpenId,
|
|
116
|
+
user_id: event.user_id?.user_id,
|
|
117
|
+
union_id: event.user_id?.union_id
|
|
118
|
+
},
|
|
119
|
+
sender_type: "user"
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const senderResult = await resolveUserName({ account, openId: operatorOpenId, log });
|
|
123
|
+
if (senderResult.name) {
|
|
124
|
+
ctx = { ...ctx, senderName: senderResult.name };
|
|
125
|
+
}
|
|
126
|
+
log(
|
|
127
|
+
`feishu[${accountId}]: reaction "${emojiType}" by ${operatorOpenId} on ${messageId} (chatId=${preResolved.chatId}, chatType=${preResolved.chatType}${preResolved.threadId ? `, thread=${preResolved.threadId}` : ""}), dispatching to AI`
|
|
128
|
+
);
|
|
129
|
+
logger.info(`reaction "${emojiType}" by ${operatorOpenId} on ${messageId} (chatType=${preResolved.chatType})`);
|
|
130
|
+
const isGroup = ctx.chatType === "group";
|
|
131
|
+
const groupConfig = isGroup ? resolveFeishuGroupConfig({ cfg: accountFeishuCfg, groupId: ctx.chatId }) : void 0;
|
|
132
|
+
const defaultGroupConfig = isGroup ? accountFeishuCfg?.groups?.["*"] : void 0;
|
|
133
|
+
const historyLimit = Math.max(
|
|
134
|
+
0,
|
|
135
|
+
accountFeishuCfg?.historyLimit ?? accountScopedCfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT
|
|
136
|
+
);
|
|
137
|
+
try {
|
|
138
|
+
await dispatchToAgent({
|
|
139
|
+
ctx,
|
|
140
|
+
permissionError: void 0,
|
|
141
|
+
mediaPayload: {},
|
|
142
|
+
quotedContent: void 0,
|
|
143
|
+
account,
|
|
144
|
+
accountScopedCfg,
|
|
145
|
+
runtime,
|
|
146
|
+
chatHistories,
|
|
147
|
+
historyLimit,
|
|
148
|
+
replyToMessageId: messageId,
|
|
149
|
+
commandAuthorized: false,
|
|
150
|
+
groupConfig,
|
|
151
|
+
defaultGroupConfig,
|
|
152
|
+
skipTyping: true
|
|
153
|
+
});
|
|
154
|
+
} catch (err) {
|
|
155
|
+
error(`feishu[${accountId}]: error dispatching reaction event: ${String(err)}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export {
|
|
159
|
+
handleFeishuReaction,
|
|
160
|
+
resolveReactionContext
|
|
161
|
+
};
|
|
162
|
+
//# sourceMappingURL=reaction-handler.js.map
|