@zhin.js/core 1.1.34 → 1.1.35
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/README.md +121 -65
- package/lib/adapter.d.ts +72 -23
- package/lib/adapter.js +222 -62
- package/lib/built/adapter-process.d.ts +20 -5
- package/lib/built/adapter-process.js +54 -7
- package/lib/built/agent-preset.d.ts +1 -2
- package/lib/built/agent-preset.js +1 -2
- package/lib/built/ai-access.d.ts +36 -0
- package/lib/built/ai-access.js +46 -0
- package/lib/built/ai-outbound/adapter-access.d.ts +3 -0
- package/lib/built/ai-outbound/adapter-access.js +9 -0
- package/lib/built/ai-outbound/extensions/onebot-keyboard.d.ts +2 -0
- package/lib/built/ai-outbound/extensions/onebot-keyboard.js +67 -0
- package/lib/built/ai-outbound/extensions/qq-message.d.ts +3 -0
- package/lib/built/ai-outbound/extensions/qq-message.js +23 -0
- package/lib/built/ai-outbound/index.d.ts +9 -0
- package/lib/built/ai-outbound/index.js +9 -0
- package/lib/built/ai-outbound/parse.d.ts +13 -0
- package/lib/built/ai-outbound/parse.js +104 -0
- package/lib/built/ai-outbound/plain-mention-rewrite.d.ts +6 -0
- package/lib/built/ai-outbound/plain-mention-rewrite.js +37 -0
- package/lib/built/ai-outbound/prompt.d.ts +11 -0
- package/lib/built/ai-outbound/prompt.js +44 -0
- package/lib/built/ai-outbound/resolve.d.ts +4 -0
- package/lib/built/ai-outbound/resolve.js +64 -0
- package/lib/built/ai-outbound/structured-detect.d.ts +3 -0
- package/lib/built/ai-outbound/structured-detect.js +6 -0
- package/lib/built/ai-outbound/types.d.ts +58 -0
- package/lib/built/ai-outbound/types.js +5 -0
- package/lib/built/ai-trigger.d.ts +17 -17
- package/lib/built/ai-trigger.js +65 -70
- package/lib/built/authorization.d.ts +10 -0
- package/lib/built/authorization.js +76 -0
- package/lib/built/command.d.ts +9 -5
- package/lib/built/command.js +20 -7
- package/lib/built/common-adapter-tools.d.ts +35 -29
- package/lib/built/common-adapter-tools.js +83 -67
- package/lib/built/component.d.ts +1 -3
- package/lib/built/component.js +2 -4
- package/lib/built/config.d.ts +4 -5
- package/lib/built/config.js +26 -9
- package/lib/built/connect-endpoint-instance.d.ts +17 -0
- package/lib/built/connect-endpoint-instance.js +42 -0
- package/lib/built/database.d.ts +5 -3
- package/lib/built/database.js +10 -4
- package/lib/built/dispatcher.d.ts +10 -8
- package/lib/built/dispatcher.js +31 -9
- package/lib/built/endpoint-commands.d.ts +3 -0
- package/lib/built/endpoint-commands.js +99 -0
- package/lib/built/endpoint-lifecycle-service.d.ts +28 -0
- package/lib/built/endpoint-lifecycle-service.js +242 -0
- package/lib/built/endpoint-lifecycle.d.ts +27 -0
- package/lib/built/endpoint-lifecycle.js +36 -0
- package/lib/built/endpoint-manager.d.ts +34 -0
- package/lib/built/endpoint-manager.js +1 -0
- package/lib/built/generated-qrcode.d.ts +24 -0
- package/lib/built/generated-qrcode.js +45 -0
- package/lib/built/generic-segment-mapper.d.ts +4 -0
- package/lib/built/generic-segment-mapper.js +190 -0
- package/lib/built/html-renderer-loader.d.ts +13 -0
- package/lib/built/html-renderer-loader.js +33 -0
- package/lib/built/html-segment-fallback.d.ts +2 -2
- package/lib/built/html-segment-fallback.js +0 -1
- package/lib/built/html-to-text.d.ts +0 -1
- package/lib/built/html-to-text.js +15 -15
- package/lib/built/inbound-pipeline.d.ts +37 -0
- package/lib/built/inbound-pipeline.js +63 -0
- package/lib/built/inbound-runner.d.ts +0 -1
- package/lib/built/inbound-runner.js +0 -3
- package/lib/built/interactive-segment-contract.d.ts +6 -0
- package/lib/built/interactive-segment-contract.js +9 -0
- package/lib/built/interactive-segments/action.d.ts +17 -0
- package/lib/built/interactive-segments/action.js +69 -0
- package/lib/built/interactive-segments/button-spec.d.ts +10 -0
- package/lib/built/interactive-segments/button-spec.js +21 -0
- package/lib/built/interactive-segments/fallback-store.d.ts +29 -0
- package/lib/built/interactive-segments/fallback-store.js +63 -0
- package/lib/built/interactive-segments/handlers.d.ts +16 -0
- package/lib/built/interactive-segments/handlers.js +74 -0
- package/lib/built/interactive-segments/index.d.ts +8 -0
- package/lib/built/interactive-segments/index.js +8 -0
- package/lib/built/interactive-segments/keyboard-segment.d.ts +11 -0
- package/lib/built/interactive-segments/keyboard-segment.js +12 -0
- package/lib/built/interactive-segments/onebot-keyboard.d.ts +7 -0
- package/lib/built/interactive-segments/onebot-keyboard.js +51 -0
- package/lib/built/interactive-segments/resolve.d.ts +15 -0
- package/lib/built/interactive-segments/resolve.js +110 -0
- package/lib/built/interactive-segments/types.d.ts +50 -0
- package/lib/built/interactive-segments/types.js +9 -0
- package/lib/built/introspection-format.d.ts +10 -0
- package/lib/built/introspection-format.js +28 -0
- package/lib/built/login-assist.d.ts +43 -9
- package/lib/built/login-assist.js +113 -11
- package/lib/built/management-command-guard.d.ts +15 -0
- package/lib/built/management-command-guard.js +25 -0
- package/lib/built/message-enrich.d.ts +38 -0
- package/lib/built/message-enrich.js +75 -0
- package/lib/built/message-filter.d.ts +4 -5
- package/lib/built/message-filter.js +5 -7
- package/lib/built/optional-peer-import.d.ts +3 -0
- package/lib/built/optional-peer-import.js +33 -0
- package/lib/built/outbound-media-contract.d.ts +22 -0
- package/lib/built/outbound-media-contract.js +10 -0
- package/lib/built/outbound-media-utils.d.ts +9 -0
- package/lib/built/outbound-media-utils.js +62 -0
- package/lib/built/permission.d.ts +3 -7
- package/lib/built/permission.js +39 -52
- package/lib/built/permit-check.d.ts +7 -0
- package/lib/built/permit-check.js +35 -0
- package/lib/built/permit-parse.d.ts +16 -0
- package/lib/built/permit-parse.js +32 -0
- package/lib/built/platform-permit.d.ts +20 -0
- package/lib/built/platform-permit.js +91 -0
- package/lib/built/queue-im-field-contract.d.ts +7 -2
- package/lib/built/queue-im-field-contract.js +13 -4
- package/lib/built/rich-segments/adapter-policies.d.ts +9 -0
- package/lib/built/rich-segments/adapter-policies.js +27 -0
- package/lib/built/rich-segments/base.d.ts +11 -0
- package/lib/built/rich-segments/base.js +24 -0
- package/lib/built/rich-segments/builtins.d.ts +4 -0
- package/lib/built/rich-segments/builtins.js +40 -0
- package/lib/built/rich-segments/capabilities.d.ts +6 -0
- package/lib/built/rich-segments/capabilities.js +38 -0
- package/lib/built/rich-segments/html-segment.d.ts +15 -0
- package/lib/built/rich-segments/html-segment.js +62 -0
- package/lib/built/rich-segments/index.d.ts +13 -0
- package/lib/built/rich-segments/index.js +13 -0
- package/lib/built/rich-segments/markdown-segment.d.ts +14 -0
- package/lib/built/rich-segments/markdown-segment.js +58 -0
- package/lib/built/rich-segments/markdown-to-text.d.ts +3 -0
- package/lib/built/rich-segments/markdown-to-text.js +27 -0
- package/lib/built/rich-segments/qrcode-segment.d.ts +18 -0
- package/lib/built/rich-segments/qrcode-segment.js +73 -0
- package/lib/built/rich-segments/registry.d.ts +16 -0
- package/lib/built/rich-segments/registry.js +52 -0
- package/lib/built/rich-segments/resolve.d.ts +5 -0
- package/lib/built/rich-segments/resolve.js +87 -0
- package/lib/built/rich-segments/tts-segment.d.ts +13 -0
- package/lib/built/rich-segments/tts-segment.js +51 -0
- package/lib/built/rich-segments/types.d.ts +79 -0
- package/lib/built/rich-segments/types.js +17 -0
- package/lib/built/roles.d.ts +4 -5
- package/lib/built/roles.js +4 -5
- package/lib/built/runtime-io.d.ts +0 -1
- package/lib/built/runtime-io.js +0 -1
- package/lib/built/schedule.d.ts +54 -0
- package/lib/built/schedule.js +112 -0
- package/lib/built/schema-endpoint-manager.d.ts +17 -0
- package/lib/built/schema-endpoint-manager.js +73 -0
- package/lib/built/schema-feature.d.ts +0 -1
- package/lib/built/schema-feature.js +1 -3
- package/lib/built/segment-contract/assert.d.ts +7 -0
- package/lib/built/segment-contract/assert.js +39 -0
- package/lib/built/segment-contract/delivery.d.ts +5 -0
- package/lib/built/segment-contract/delivery.js +40 -0
- package/lib/built/segment-contract/image.d.ts +5 -0
- package/lib/built/segment-contract/image.js +10 -0
- package/lib/built/segment-contract/index.d.ts +10 -0
- package/lib/built/segment-contract/index.js +9 -0
- package/lib/built/segment-contract/json-schema.d.ts +28 -0
- package/lib/built/segment-contract/json-schema.js +128 -0
- package/lib/built/segment-contract/media.d.ts +12 -0
- package/lib/built/segment-contract/media.js +22 -0
- package/lib/built/segment-contract/mention.d.ts +10 -0
- package/lib/built/segment-contract/mention.js +26 -0
- package/lib/built/segment-contract/preview.d.ts +3 -0
- package/lib/built/segment-contract/preview.js +159 -0
- package/lib/built/segment-contract/text.d.ts +7 -0
- package/lib/built/segment-contract/text.js +34 -0
- package/lib/built/segment-contract/types.d.ts +58 -0
- package/lib/built/segment-contract/types.js +1 -0
- package/lib/built/segment-contract/validate.d.ts +229 -0
- package/lib/built/segment-contract/validate.js +174 -0
- package/lib/built/skill.d.ts +0 -1
- package/lib/built/skill.js +0 -1
- package/lib/built/speech-loader.d.ts +14 -0
- package/lib/built/speech-loader.js +32 -0
- package/lib/built/tool.d.ts +14 -13
- package/lib/built/tool.js +30 -25
- package/lib/built/user-interaction.d.ts +30 -0
- package/lib/built/user-interaction.js +248 -0
- package/lib/command.d.ts +19 -11
- package/lib/command.js +42 -11
- package/lib/component.d.ts +0 -1
- package/lib/component.js +30 -14
- package/lib/endpoint-capabilities.d.ts +46 -0
- package/lib/endpoint-capabilities.js +55 -0
- package/lib/endpoint.d.ts +19 -0
- package/lib/endpoint.js +3 -0
- package/lib/errors.d.ts +2 -3
- package/lib/errors.js +4 -5
- package/lib/feature/adapter.d.ts +2 -0
- package/lib/feature/adapter.js +2 -0
- package/lib/feature/command.d.ts +2 -0
- package/lib/feature/command.js +2 -0
- package/lib/feature/component.d.ts +2 -0
- package/lib/feature/component.js +2 -0
- package/lib/feature/handler.d.ts +52 -0
- package/lib/feature/handler.js +26 -0
- package/lib/feature/middleware.d.ts +2 -0
- package/lib/feature/middleware.js +2 -0
- package/lib/host-plugin-registry.d.ts +8 -0
- package/lib/host-plugin-registry.js +12 -0
- package/lib/im-scene.d.ts +29 -0
- package/lib/im-scene.js +75 -0
- package/lib/im-session-id.d.ts +39 -0
- package/lib/im-session-id.js +47 -0
- package/lib/index.d.ts +59 -25
- package/lib/index.js +53 -31
- package/lib/jsx-dev-runtime.d.ts +0 -1
- package/lib/jsx-dev-runtime.js +0 -1
- package/lib/jsx-runtime.d.ts +1 -1
- package/lib/jsx-runtime.js +0 -1
- package/lib/jsx.d.ts +19 -22
- package/lib/jsx.js +0 -1
- package/lib/message.d.ts +16 -10
- package/lib/message.js +16 -13
- package/lib/models/system-log.d.ts +0 -1
- package/lib/models/system-log.js +0 -1
- package/lib/models/user.d.ts +0 -1
- package/lib/models/user.js +0 -1
- package/lib/notice.d.ts +16 -59
- package/lib/notice.js +1 -4
- package/lib/plugin-context.d.ts +27 -0
- package/lib/plugin-context.js +64 -0
- package/lib/plugin-runtime/im/contracts.d.ts +191 -0
- package/lib/plugin-runtime/im/contracts.js +169 -0
- package/lib/plugin-runtime/im/im-runtime.d.ts +177 -0
- package/lib/plugin-runtime/im/im-runtime.js +1204 -0
- package/lib/plugin-runtime/im/index.d.ts +9 -0
- package/lib/plugin-runtime/im/index.js +9 -0
- package/lib/plugin-runtime/im/interactive.d.ts +26 -0
- package/lib/plugin-runtime/im/interactive.js +46 -0
- package/lib/plugin-runtime/im/login-assist-host.d.ts +6 -0
- package/lib/plugin-runtime/im/login-assist-host.js +6 -0
- package/lib/plugin-runtime/im/message-bus.d.ts +38 -0
- package/lib/plugin-runtime/im/message-bus.js +40 -0
- package/lib/plugin-runtime/im/message-dispatcher.d.ts +18 -0
- package/lib/plugin-runtime/im/message-dispatcher.js +107 -0
- package/lib/plugin-runtime/im/outbound-renderer.d.ts +7 -0
- package/lib/plugin-runtime/im/outbound-renderer.js +70 -0
- package/lib/plugin-runtime/im/outbound-segments.d.ts +72 -0
- package/lib/plugin-runtime/im/outbound-segments.js +220 -0
- package/lib/plugin-runtime/im/public-api.d.ts +6 -0
- package/lib/plugin-runtime/im/public-api.js +6 -0
- package/lib/plugin-runtime/im/service-tokens.d.ts +18 -0
- package/lib/plugin-runtime/im/service-tokens.js +13 -0
- package/lib/plugin.d.ts +34 -24
- package/lib/plugin.js +119 -224
- package/lib/request.d.ts +17 -61
- package/lib/request.js +0 -4
- package/lib/{prompt.d.ts → schema-interaction.d.ts} +6 -7
- package/lib/{prompt.js → schema-interaction.js} +20 -17
- package/lib/side-event/base.d.ts +52 -0
- package/lib/side-event/base.js +30 -0
- package/lib/side-event/dispatch.d.ts +15 -0
- package/lib/side-event/dispatch.js +140 -0
- package/lib/side-event/index.d.ts +4 -0
- package/lib/side-event/index.js +4 -0
- package/lib/side-event/normalize.d.ts +42 -0
- package/lib/side-event/normalize.js +121 -0
- package/lib/side-event/types.d.ts +23 -0
- package/lib/side-event/types.js +55 -0
- package/lib/system-event.d.ts +15 -0
- package/lib/system-event.js +7 -0
- package/lib/tool-zod.d.ts +18 -3
- package/lib/tool-zod.js +140 -54
- package/lib/types.d.ts +44 -95
- package/lib/types.js +0 -1
- package/lib/utils.d.ts +61 -13
- package/lib/utils.js +66 -25
- package/package.json +98 -10
- package/lib/adapter.d.ts.map +0 -1
- package/lib/adapter.js.map +0 -1
- package/lib/agent-prompt.d.ts +0 -37
- package/lib/agent-prompt.d.ts.map +0 -1
- package/lib/agent-prompt.js +0 -2
- package/lib/agent-prompt.js.map +0 -1
- package/lib/bot.d.ts +0 -38
- package/lib/bot.d.ts.map +0 -1
- package/lib/bot.js +0 -2
- package/lib/bot.js.map +0 -1
- package/lib/built/adapter-process.d.ts.map +0 -1
- package/lib/built/adapter-process.js.map +0 -1
- package/lib/built/agent-preset.d.ts.map +0 -1
- package/lib/built/agent-preset.js.map +0 -1
- package/lib/built/ai-trigger.d.ts.map +0 -1
- package/lib/built/ai-trigger.js.map +0 -1
- package/lib/built/command.d.ts.map +0 -1
- package/lib/built/command.js.map +0 -1
- package/lib/built/common-adapter-tools.d.ts.map +0 -1
- package/lib/built/common-adapter-tools.js.map +0 -1
- package/lib/built/component.d.ts.map +0 -1
- package/lib/built/component.js.map +0 -1
- package/lib/built/config.d.ts.map +0 -1
- package/lib/built/config.js.map +0 -1
- package/lib/built/cron.d.ts +0 -75
- package/lib/built/cron.d.ts.map +0 -1
- package/lib/built/cron.js +0 -122
- package/lib/built/cron.js.map +0 -1
- package/lib/built/database.d.ts.map +0 -1
- package/lib/built/database.js.map +0 -1
- package/lib/built/dispatcher.d.ts.map +0 -1
- package/lib/built/dispatcher.js.map +0 -1
- package/lib/built/html-segment-fallback.d.ts.map +0 -1
- package/lib/built/html-segment-fallback.js.map +0 -1
- package/lib/built/html-to-text.d.ts.map +0 -1
- package/lib/built/html-to-text.js.map +0 -1
- package/lib/built/inbound-runner.d.ts.map +0 -1
- package/lib/built/inbound-runner.js.map +0 -1
- package/lib/built/login-assist.d.ts.map +0 -1
- package/lib/built/login-assist.js.map +0 -1
- package/lib/built/mcp-mesh-registrar.d.ts +0 -9
- package/lib/built/mcp-mesh-registrar.d.ts.map +0 -1
- package/lib/built/mcp-mesh-registrar.js +0 -17
- package/lib/built/mcp-mesh-registrar.js.map +0 -1
- package/lib/built/message-filter.d.ts.map +0 -1
- package/lib/built/message-filter.js.map +0 -1
- package/lib/built/permission.d.ts.map +0 -1
- package/lib/built/permission.js.map +0 -1
- package/lib/built/prepend-quote-context.d.ts +0 -35
- package/lib/built/prepend-quote-context.d.ts.map +0 -1
- package/lib/built/prepend-quote-context.js +0 -131
- package/lib/built/prepend-quote-context.js.map +0 -1
- package/lib/built/queue-im-field-contract.d.ts.map +0 -1
- package/lib/built/queue-im-field-contract.js.map +0 -1
- package/lib/built/roles.d.ts.map +0 -1
- package/lib/built/roles.js.map +0 -1
- package/lib/built/runtime-io.d.ts.map +0 -1
- package/lib/built/runtime-io.js.map +0 -1
- package/lib/built/schema-feature.d.ts.map +0 -1
- package/lib/built/schema-feature.js.map +0 -1
- package/lib/built/skill.d.ts.map +0 -1
- package/lib/built/skill.js.map +0 -1
- package/lib/built/tool.d.ts.map +0 -1
- package/lib/built/tool.js.map +0 -1
- package/lib/command.d.ts.map +0 -1
- package/lib/command.js.map +0 -1
- package/lib/component.d.ts.map +0 -1
- package/lib/component.js.map +0 -1
- package/lib/errors.d.ts.map +0 -1
- package/lib/errors.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/jsx-dev-runtime.d.ts.map +0 -1
- package/lib/jsx-dev-runtime.js.map +0 -1
- package/lib/jsx-runtime.d.ts.map +0 -1
- package/lib/jsx-runtime.js.map +0 -1
- package/lib/jsx.d.ts.map +0 -1
- package/lib/jsx.js.map +0 -1
- package/lib/message-quote.d.ts +0 -11
- package/lib/message-quote.d.ts.map +0 -1
- package/lib/message-quote.js +0 -91
- package/lib/message-quote.js.map +0 -1
- package/lib/message.d.ts.map +0 -1
- package/lib/message.js.map +0 -1
- package/lib/models/system-log.d.ts.map +0 -1
- package/lib/models/system-log.js.map +0 -1
- package/lib/models/user.d.ts.map +0 -1
- package/lib/models/user.js.map +0 -1
- package/lib/notice.d.ts.map +0 -1
- package/lib/notice.js.map +0 -1
- package/lib/plugin.d.ts.map +0 -1
- package/lib/plugin.js.map +0 -1
- package/lib/prompt.d.ts.map +0 -1
- package/lib/prompt.js.map +0 -1
- package/lib/request.d.ts.map +0 -1
- package/lib/request.js.map +0 -1
- package/lib/scheduler/scheduler.d.ts +0 -49
- package/lib/scheduler/scheduler.d.ts.map +0 -1
- package/lib/scheduler/scheduler.js +0 -352
- package/lib/scheduler/scheduler.js.map +0 -1
- package/lib/scheduler/types.d.ts +0 -71
- package/lib/scheduler/types.d.ts.map +0 -1
- package/lib/scheduler/types.js +0 -8
- package/lib/scheduler/types.js.map +0 -1
- package/lib/tool-zod.d.ts.map +0 -1
- package/lib/tool-zod.js.map +0 -1
- package/lib/types-generator.d.ts +0 -6
- package/lib/types-generator.d.ts.map +0 -1
- package/lib/types-generator.js +0 -72
- package/lib/types-generator.js.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js.map +0 -1
- package/lib/utils.d.ts.map +0 -1
- package/lib/utils.js.map +0 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { isMediaRef, readMentionTarget, } from './segment-contract/index.js';
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function readPlatform(seg) {
|
|
6
|
+
const platform = seg.platform;
|
|
7
|
+
return platform && typeof platform === 'object' ? platform : undefined;
|
|
8
|
+
}
|
|
9
|
+
function normalizeMention(seg) {
|
|
10
|
+
const data = seg.data;
|
|
11
|
+
const target = readMentionTarget(data);
|
|
12
|
+
const name = typeof data.name === 'string' && data.name ? data.name : undefined;
|
|
13
|
+
const platform = readPlatform(seg);
|
|
14
|
+
return {
|
|
15
|
+
type: 'mention',
|
|
16
|
+
data: name ? { target, name } : { target },
|
|
17
|
+
...(platform ? { platform } : {}),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const MEDIA_SEGMENT_TYPES = new Set(['image', 'audio', 'video', 'file']);
|
|
21
|
+
function nonEmptyString(value) {
|
|
22
|
+
return typeof value === 'string' && value.trim() ? value : undefined;
|
|
23
|
+
}
|
|
24
|
+
function legacyMediaRef(data) {
|
|
25
|
+
if (isMediaRef(data.media))
|
|
26
|
+
return data.media;
|
|
27
|
+
const url = nonEmptyString(data.url) ?? nonEmptyString(data.href) ?? nonEmptyString(data.src);
|
|
28
|
+
const path = nonEmptyString(data.path) ?? nonEmptyString(data.file_path);
|
|
29
|
+
const base64 = nonEmptyString(data.base64);
|
|
30
|
+
const file = nonEmptyString(data.file) ?? nonEmptyString(data.file_id);
|
|
31
|
+
const value = url ?? path ?? base64 ?? file;
|
|
32
|
+
if (!value)
|
|
33
|
+
return undefined;
|
|
34
|
+
const kind = url ? 'url'
|
|
35
|
+
: path ? 'path'
|
|
36
|
+
: base64 ? 'base64'
|
|
37
|
+
: /^(?:[a-z][a-z0-9+.-]*:)?\/\//iu.test(value) ? 'url'
|
|
38
|
+
: /^(?:\.{1,2}\/|\/|~\/)/u.test(value) ? 'path'
|
|
39
|
+
: 'file';
|
|
40
|
+
const mimeType = nonEmptyString(data.mime_type) ?? nonEmptyString(data.mimeType);
|
|
41
|
+
// Legacy top-level fileName names the rendered file segment. Keep source
|
|
42
|
+
// metadata reserved for an explicit media object instead of duplicating it.
|
|
43
|
+
const fileName = nonEmptyString(data.media_file_name);
|
|
44
|
+
const size = typeof data.size === 'number' && Number.isFinite(data.size) ? data.size : undefined;
|
|
45
|
+
return {
|
|
46
|
+
kind,
|
|
47
|
+
value,
|
|
48
|
+
...(mimeType ? { mime_type: mimeType } : {}),
|
|
49
|
+
...(fileName ? { file_name: fileName } : {}),
|
|
50
|
+
...(size === undefined ? {} : { size }),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The sole ingress compatibility boundary for media. Adapters may emit their
|
|
55
|
+
* native `url`/`path`/`file` fields, but every consumer receives `data.media`.
|
|
56
|
+
*/
|
|
57
|
+
function normalizeMedia(seg) {
|
|
58
|
+
const data = seg.data;
|
|
59
|
+
const platform = readPlatform(seg);
|
|
60
|
+
const media = legacyMediaRef(data);
|
|
61
|
+
if (!media)
|
|
62
|
+
return seg;
|
|
63
|
+
const attributes = seg.type === 'image'
|
|
64
|
+
? { ...(typeof data.alt === 'string' ? { alt: data.alt } : {}) }
|
|
65
|
+
: seg.type === 'audio'
|
|
66
|
+
? { ...(typeof data.duration === 'number' ? { duration: data.duration } : {}) }
|
|
67
|
+
: seg.type === 'video'
|
|
68
|
+
? {
|
|
69
|
+
...(typeof data.duration === 'number' ? { duration: data.duration } : {}),
|
|
70
|
+
...(typeof data.alt === 'string' ? { alt: data.alt } : {}),
|
|
71
|
+
}
|
|
72
|
+
: {
|
|
73
|
+
...(nonEmptyString(data.name) ?? nonEmptyString(data.file_name) ?? nonEmptyString(data.fileName)
|
|
74
|
+
? { name: nonEmptyString(data.name) ?? nonEmptyString(data.file_name) ?? nonEmptyString(data.fileName) }
|
|
75
|
+
: {}),
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
type: seg.type,
|
|
79
|
+
data: { media, ...attributes },
|
|
80
|
+
...(platform ? { platform } : {}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function normalizeReply(seg) {
|
|
84
|
+
const data = seg.data;
|
|
85
|
+
const messageId = String(data.message_id ?? data.id ?? '').trim();
|
|
86
|
+
if (!messageId)
|
|
87
|
+
return seg;
|
|
88
|
+
return { type: 'reply', data: { message_id: messageId } };
|
|
89
|
+
}
|
|
90
|
+
function normalizeLink(seg) {
|
|
91
|
+
const data = seg.data;
|
|
92
|
+
const url = String(data.url ?? data.href ?? '').trim();
|
|
93
|
+
if (!url)
|
|
94
|
+
return seg;
|
|
95
|
+
const text = typeof data.text === 'string' && data.text ? data.text : undefined;
|
|
96
|
+
const platform = readPlatform(seg);
|
|
97
|
+
return {
|
|
98
|
+
type: 'link',
|
|
99
|
+
data: text ? { url, text } : { url },
|
|
100
|
+
...(platform ? { platform } : {}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function normalizeForward(seg) {
|
|
104
|
+
const data = seg.data;
|
|
105
|
+
const forwardId = String(data.forward_id ?? data.id ?? data.resid ?? '').trim();
|
|
106
|
+
if (!forwardId)
|
|
107
|
+
return seg;
|
|
108
|
+
const title = typeof data.title === 'string' ? data.title : undefined;
|
|
109
|
+
const messages = Array.isArray(data.messages) ? data.messages : undefined;
|
|
110
|
+
const platform = { ...(readPlatform(seg) ?? {}) };
|
|
111
|
+
if (data.resid != null)
|
|
112
|
+
platform.resid = String(data.resid);
|
|
113
|
+
return {
|
|
114
|
+
type: 'forward',
|
|
115
|
+
data: {
|
|
116
|
+
forward_id: forwardId,
|
|
117
|
+
...(title ? { title } : {}),
|
|
118
|
+
...(messages ? { messages: messages } : {}),
|
|
119
|
+
},
|
|
120
|
+
...(Object.keys(platform).length ? { platform } : {}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function isPlatformSticker(seg) {
|
|
124
|
+
if (seg.type !== 'sticker')
|
|
125
|
+
return false;
|
|
126
|
+
const data = seg.data;
|
|
127
|
+
return data.package_id != null
|
|
128
|
+
|| data.sticker_id != null
|
|
129
|
+
|| data.packageId != null
|
|
130
|
+
|| data.stickerId != null;
|
|
131
|
+
}
|
|
132
|
+
function normalizeFace(seg) {
|
|
133
|
+
if (isPlatformSticker(seg))
|
|
134
|
+
return seg;
|
|
135
|
+
const data = seg.data;
|
|
136
|
+
const rawId = data.id ?? data.face_id;
|
|
137
|
+
const name = typeof data.name === 'string'
|
|
138
|
+
? data.name
|
|
139
|
+
: typeof data.text === 'string'
|
|
140
|
+
? data.text
|
|
141
|
+
: undefined;
|
|
142
|
+
const platform = readPlatform(seg);
|
|
143
|
+
return {
|
|
144
|
+
type: 'face',
|
|
145
|
+
data: {
|
|
146
|
+
...(rawId != null ? { id: rawId } : {}),
|
|
147
|
+
...(name ? { name } : {}),
|
|
148
|
+
},
|
|
149
|
+
...(platform ? { platform } : {}),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function normalizeMarkdown(seg) {
|
|
153
|
+
const data = seg.data;
|
|
154
|
+
const content = typeof data.content === 'string'
|
|
155
|
+
? data.content
|
|
156
|
+
: typeof data.text === 'string'
|
|
157
|
+
? data.text
|
|
158
|
+
: '';
|
|
159
|
+
return { type: 'markdown', data: { content } };
|
|
160
|
+
}
|
|
161
|
+
function normalizeSegment(seg) {
|
|
162
|
+
if (seg.type === 'at' || seg.type === 'mention')
|
|
163
|
+
return normalizeMention(seg);
|
|
164
|
+
if (MEDIA_SEGMENT_TYPES.has(seg.type))
|
|
165
|
+
return normalizeMedia(seg);
|
|
166
|
+
if (seg.type === 'reply')
|
|
167
|
+
return normalizeReply(seg);
|
|
168
|
+
if (seg.type === 'forward')
|
|
169
|
+
return normalizeForward(seg);
|
|
170
|
+
if (seg.type === 'sticker' && isPlatformSticker(seg))
|
|
171
|
+
return seg;
|
|
172
|
+
if (seg.type === 'face' || seg.type === 'sticker' || seg.type === 'emoji')
|
|
173
|
+
return normalizeFace(seg);
|
|
174
|
+
if (seg.type === 'link')
|
|
175
|
+
return normalizeLink(seg);
|
|
176
|
+
if (seg.type === 'markdown')
|
|
177
|
+
return normalizeMarkdown(seg);
|
|
178
|
+
return seg;
|
|
179
|
+
}
|
|
180
|
+
function asMessageSegments(content) {
|
|
181
|
+
return content.map((item) => {
|
|
182
|
+
if (typeof item === 'string')
|
|
183
|
+
return { type: 'text', data: { text: item } };
|
|
184
|
+
return item;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/** wire 段数组 → canonical Segment[](包括所有媒体的 `data.media` 归一)。 */
|
|
188
|
+
export function toCanonicalSegments(content) {
|
|
189
|
+
return asMessageSegments(content).map((seg) => normalizeSegment(seg));
|
|
190
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HtmlRendererForRichSegment } from './rich-segments/types.js';
|
|
2
|
+
/** 动态 import 时使用的包名(与 @zhin.js/html-renderer package.json 一致) */
|
|
3
|
+
export declare const HTML_RENDERER_PACKAGE = "@zhin.js/html-renderer";
|
|
4
|
+
export interface LoadHtmlRendererOptions {
|
|
5
|
+
getConfig?: () => Record<string, unknown> | undefined;
|
|
6
|
+
warn?: (message: string) => void;
|
|
7
|
+
}
|
|
8
|
+
/** zhin 启动时已创建 renderer 时注入,避免 core 目录下 peer 解析失败 */
|
|
9
|
+
export declare function seedHtmlRenderer(renderer: HtmlRendererForRichSegment): void;
|
|
10
|
+
/** 动态加载 @zhin.js/html-renderer;未安装时 warn 一次并返回 undefined */
|
|
11
|
+
export declare function loadHtmlRenderer(opts?: LoadHtmlRendererOptions): Promise<HtmlRendererForRichSegment | undefined>;
|
|
12
|
+
/** 测试用:重置加载缓存 */
|
|
13
|
+
export declare function resetHtmlRendererLoaderForTests(): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { importOptionalPeerPackage } from './optional-peer-import.js';
|
|
2
|
+
/** 动态 import 时使用的包名(与 @zhin.js/html-renderer package.json 一致) */
|
|
3
|
+
export const HTML_RENDERER_PACKAGE = '@zhin.js/html-renderer';
|
|
4
|
+
let cached;
|
|
5
|
+
let warned = false;
|
|
6
|
+
/** zhin 启动时已创建 renderer 时注入,避免 core 目录下 peer 解析失败 */
|
|
7
|
+
export function seedHtmlRenderer(renderer) {
|
|
8
|
+
cached = renderer;
|
|
9
|
+
}
|
|
10
|
+
/** 动态加载 @zhin.js/html-renderer;未安装时 warn 一次并返回 undefined */
|
|
11
|
+
export async function loadHtmlRenderer(opts) {
|
|
12
|
+
if (cached !== undefined) {
|
|
13
|
+
return cached ?? undefined;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const mod = await importOptionalPeerPackage(HTML_RENDERER_PACKAGE);
|
|
17
|
+
cached = mod.createHtmlRenderer(opts?.getConfig?.() ?? {});
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
if (!warned) {
|
|
22
|
+
warned = true;
|
|
23
|
+
opts?.warn?.(`未安装 ${HTML_RENDERER_PACKAGE},html/markdown=>image 已降级为 text。安装: pnpm add ${HTML_RENDERER_PACKAGE}`);
|
|
24
|
+
}
|
|
25
|
+
cached = null;
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** 测试用:重置加载缓存 */
|
|
30
|
+
export function resetHtmlRendererLoaderForTests() {
|
|
31
|
+
cached = undefined;
|
|
32
|
+
warned = false;
|
|
33
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 出站 `html`
|
|
2
|
+
* 出站 `html` 消息段兜底:等价于 Adapter policy `html:'text'`。
|
|
3
|
+
* 出站渲染首选 `resolveRichSegments`(renderSendMessage 首步)。
|
|
3
4
|
*/
|
|
4
5
|
import type { Plugin } from '../plugin.js';
|
|
5
6
|
import type { SendContent } from '../types.js';
|
|
6
7
|
export declare function coerceHtmlSegmentsToText(content: SendContent): SendContent;
|
|
7
8
|
/** 注册链尾 before.sendMessage:将未转换的 html 段降级为 text */
|
|
8
9
|
export declare function registerHtmlSegmentFallback(plugin: Plugin): () => void;
|
|
9
|
-
//# sourceMappingURL=html-segment-fallback.d.ts.map
|
|
@@ -14,4 +14,3 @@ export declare function htmlToPlainTextWithBlockBreaks(html: string, options?: H
|
|
|
14
14
|
* HTML 卡片出站回退:块级换行 + 语义标题轻量 markdown 前缀。
|
|
15
15
|
*/
|
|
16
16
|
export declare function htmlToFallbackText(html: string, options?: HtmlToPlainTextOptions): string;
|
|
17
|
-
//# sourceMappingURL=html-to-text.d.ts.map
|
|
@@ -79,20 +79,21 @@ function stripRemainingTags(html) {
|
|
|
79
79
|
return out;
|
|
80
80
|
}
|
|
81
81
|
function decodeCommonEntities(text) {
|
|
82
|
-
return text
|
|
83
|
-
.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
82
|
+
return text.replace(/&(#x[0-9a-fA-F]+|#\d+|nbsp|amp|lt|gt|quot|apos);/gi, (entity) => {
|
|
83
|
+
const key = entity.slice(1, -1).toLowerCase();
|
|
84
|
+
if (key === 'nbsp')
|
|
85
|
+
return ' ';
|
|
86
|
+
if (key === 'amp')
|
|
87
|
+
return '&';
|
|
88
|
+
if (key === 'lt')
|
|
89
|
+
return '<';
|
|
90
|
+
if (key === 'gt')
|
|
91
|
+
return '>';
|
|
92
|
+
if (key === 'quot')
|
|
93
|
+
return '"';
|
|
94
|
+
if (key === 'apos')
|
|
95
|
+
return "'";
|
|
96
|
+
const n = key.startsWith('#x') ? parseInt(key.slice(2), 16) : Number(key.slice(1));
|
|
96
97
|
return Number.isFinite(n) && n >= 0 && n <= 0x10ffff ? String.fromCodePoint(n) : '';
|
|
97
98
|
});
|
|
98
99
|
}
|
|
@@ -163,4 +164,3 @@ export function htmlToFallbackText(html, options) {
|
|
|
163
164
|
.replace(/\n{3,}/g, '\n\n')
|
|
164
165
|
.trim();
|
|
165
166
|
}
|
|
166
|
-
//# sourceMappingURL=html-to-text.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InboundMessagePipeline — ordered IM inbound pipeline for adapter message.receive.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from Adapter.emit() override to improve testability and decoupling.
|
|
5
|
+
* Handles: event routing, logging, backpressure, pipeline execution, observer emission.
|
|
6
|
+
*/
|
|
7
|
+
import { type Logger } from '@zhin.js/logger';
|
|
8
|
+
import { type Message as MessageType } from '../message.js';
|
|
9
|
+
import type { Plugin } from '../plugin.js';
|
|
10
|
+
export interface InboundPipelineOptions {
|
|
11
|
+
getPlugin: () => Plugin | null;
|
|
12
|
+
logger: Logger;
|
|
13
|
+
getMaxConcurrentMessages: () => number;
|
|
14
|
+
getPendingMessages: () => number;
|
|
15
|
+
decrementPending: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare class InboundMessagePipeline {
|
|
18
|
+
private readonly options;
|
|
19
|
+
constructor(options: InboundPipelineOptions);
|
|
20
|
+
/**
|
|
21
|
+
* Check if message should be dropped due to backpressure (sync check).
|
|
22
|
+
* Call this BEFORE receive() to match original emit() backpressure contract.
|
|
23
|
+
*/
|
|
24
|
+
shouldDropDueToBackpressure(): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Process a message.receive event through the full inbound pipeline:
|
|
27
|
+
* 1. Logging (caller should have already logged via emit)
|
|
28
|
+
* 2. runInboundMessage (middleware → dispatcher → lifecycle)
|
|
29
|
+
* 3. Adapter observer emission
|
|
30
|
+
*
|
|
31
|
+
* Note: backpressure check is done by the caller via shouldDropDueToBackpressure()
|
|
32
|
+
*/
|
|
33
|
+
receive(message: MessageType<any>, emitAdapterObservers: () => void): Promise<void>;
|
|
34
|
+
/** Bridge notice/request events to plugin dispatch */
|
|
35
|
+
bridgeNoticeOrRequest(event: string, args: unknown[], directEmit: () => boolean): boolean;
|
|
36
|
+
private logIncoming;
|
|
37
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InboundMessagePipeline — ordered IM inbound pipeline for adapter message.receive.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from Adapter.emit() override to improve testability and decoupling.
|
|
5
|
+
* Handles: event routing, logging, backpressure, pipeline execution, observer emission.
|
|
6
|
+
*/
|
|
7
|
+
import { formatCompact, truncatePreview } from '@zhin.js/logger';
|
|
8
|
+
import { segment } from '../utils.js';
|
|
9
|
+
import { runInboundMessage } from './inbound-runner.js';
|
|
10
|
+
export class InboundMessagePipeline {
|
|
11
|
+
options;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.options = options;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if message should be dropped due to backpressure (sync check).
|
|
17
|
+
* Call this BEFORE receive() to match original emit() backpressure contract.
|
|
18
|
+
*/
|
|
19
|
+
shouldDropDueToBackpressure() {
|
|
20
|
+
const limit = this.options.getMaxConcurrentMessages();
|
|
21
|
+
return limit > 0 && this.options.getPendingMessages() >= limit;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Process a message.receive event through the full inbound pipeline:
|
|
25
|
+
* 1. Logging (caller should have already logged via emit)
|
|
26
|
+
* 2. runInboundMessage (middleware → dispatcher → lifecycle)
|
|
27
|
+
* 3. Adapter observer emission
|
|
28
|
+
*
|
|
29
|
+
* Note: backpressure check is done by the caller via shouldDropDueToBackpressure()
|
|
30
|
+
*/
|
|
31
|
+
async receive(message, emitAdapterObservers) {
|
|
32
|
+
this.logIncoming(message);
|
|
33
|
+
try {
|
|
34
|
+
await runInboundMessage({
|
|
35
|
+
plugin: this.options.getPlugin(),
|
|
36
|
+
message,
|
|
37
|
+
emitAdapterObservers,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
this.options.logger.warn(`message.receive handling error: ${e instanceof Error ? e.message : String(e)}`);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
this.options.decrementPending();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Bridge notice/request events to plugin dispatch */
|
|
48
|
+
bridgeNoticeOrRequest(event, args, directEmit) {
|
|
49
|
+
const result = directEmit();
|
|
50
|
+
const plugin = this.options.getPlugin();
|
|
51
|
+
if (plugin) {
|
|
52
|
+
void plugin.dispatch(event, args[0]);
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
logIncoming(message) {
|
|
57
|
+
this.options.logger.debug(formatCompact({
|
|
58
|
+
recv: `${message.$channel.type}(${message.$channel.id})`,
|
|
59
|
+
endpoint: message.$endpoint,
|
|
60
|
+
preview: truncatePreview(segment.raw(message.$content)),
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Inbound runner — one ordered entry for IM message.receive processing.
|
|
3
3
|
*/
|
|
4
|
-
import { Message } from '../message.js';
|
|
5
4
|
function getDispatcher(plugin) {
|
|
6
5
|
const dispatcher = plugin?.inject('dispatcher');
|
|
7
6
|
if (dispatcher && typeof dispatcher.dispatch === 'function') {
|
|
@@ -19,7 +18,6 @@ function getDispatcher(plugin) {
|
|
|
19
18
|
*/
|
|
20
19
|
export async function runInboundMessage(options) {
|
|
21
20
|
const { plugin, message, emitAdapterObservers } = options;
|
|
22
|
-
Message.syncQuoteId(message);
|
|
23
21
|
const dispatcher = getDispatcher(plugin);
|
|
24
22
|
const root = plugin?.root;
|
|
25
23
|
if (dispatcher && root) {
|
|
@@ -34,4 +32,3 @@ export async function runInboundMessage(options) {
|
|
|
34
32
|
emitAdapterObservers();
|
|
35
33
|
return { dispatched: !!dispatcher };
|
|
36
34
|
}
|
|
37
|
-
//# sourceMappingURL=inbound-runner.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InteractivePolicy } from './interactive-segments/types.js';
|
|
2
|
+
/** 契约测试:adapter 须声明 interactivePolicy */
|
|
3
|
+
export declare function assertAdapterDeclaresInteractivePolicy(adapterClass: {
|
|
4
|
+
interactivePolicy?: InteractivePolicy;
|
|
5
|
+
}): void;
|
|
6
|
+
export type { InteractivePolicy };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** 契约测试:adapter 须声明 interactivePolicy */
|
|
2
|
+
export function assertAdapterDeclaresInteractivePolicy(adapterClass) {
|
|
3
|
+
if (!adapterClass.interactivePolicy) {
|
|
4
|
+
throw new Error('Adapter must declare static interactivePolicy');
|
|
5
|
+
}
|
|
6
|
+
if (adapterClass.interactivePolicy !== 'native' && adapterClass.interactivePolicy !== 'text') {
|
|
7
|
+
throw new Error('interactivePolicy must be "native" or "text"');
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Message } from '../../message.js';
|
|
2
|
+
import type { MessageElement } from '../../types.js';
|
|
3
|
+
import { type ActionSegmentData } from './types.js';
|
|
4
|
+
export declare function actionSegment(data: ActionSegmentData): MessageElement;
|
|
5
|
+
export declare function getActionFromMessage(message: Message<any>): ActionSegmentData | undefined;
|
|
6
|
+
/** 入站互动 action(按钮点击等),不应计入用户发言/消息统计 */
|
|
7
|
+
export declare function isActionMessage(message: Message<any>): boolean;
|
|
8
|
+
/** 去掉 @bot、XML at 段、首尾空白(QQ 指令预填文本归一化) */
|
|
9
|
+
export declare function stripInteractiveCommandText(raw: string): string;
|
|
10
|
+
/** 从文本降级输入解析 payload(需在 active session 的 fallback.map 中查找) */
|
|
11
|
+
export declare function resolveTextFallbackPayload(raw: string, map: Record<string, string>): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* 从用户文本入站解析 interactive payload:
|
|
14
|
+
* 归一化 → 直接匹配 prefix:session:id → fallback 数字映射
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolvePayloadFromText(raw: string, map?: Record<string, string>): string | undefined;
|
|
17
|
+
export declare function formatActionAsRaw(data: ActionSegmentData): string;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ACTION_SEGMENT_TYPE, isActionSegment, } from './types.js';
|
|
2
|
+
export function actionSegment(data) {
|
|
3
|
+
return { type: ACTION_SEGMENT_TYPE, data };
|
|
4
|
+
}
|
|
5
|
+
export function getActionFromMessage(message) {
|
|
6
|
+
for (const item of message.$content ?? []) {
|
|
7
|
+
if (typeof item === 'string')
|
|
8
|
+
continue;
|
|
9
|
+
if (isActionSegment(item))
|
|
10
|
+
return item.data;
|
|
11
|
+
}
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
/** 入站互动 action(按钮点击等),不应计入用户发言/消息统计 */
|
|
15
|
+
export function isActionMessage(message) {
|
|
16
|
+
return getActionFromMessage(message) != null;
|
|
17
|
+
}
|
|
18
|
+
const PAYLOAD_PATTERN = /^[a-z0-9_]+:[^:\s]+:[a-z0-9_-]+$/i;
|
|
19
|
+
/** 去掉 @bot、XML at 段、首尾空白(QQ 指令预填文本归一化) */
|
|
20
|
+
export function stripInteractiveCommandText(raw) {
|
|
21
|
+
let text = raw.trim();
|
|
22
|
+
text = stripXmlAtSegments(text);
|
|
23
|
+
text = text.replace(/^@\S+\s+/u, '');
|
|
24
|
+
text = text.replace(/\s+/g, ' ').trim();
|
|
25
|
+
return text;
|
|
26
|
+
}
|
|
27
|
+
function stripXmlAtSegments(text) {
|
|
28
|
+
let out = '';
|
|
29
|
+
let cursor = 0;
|
|
30
|
+
while (cursor < text.length) {
|
|
31
|
+
const start = text.toLowerCase().indexOf('<at ', cursor);
|
|
32
|
+
if (start < 0)
|
|
33
|
+
break;
|
|
34
|
+
const end = text.indexOf('/>', start + 4);
|
|
35
|
+
if (end < 0)
|
|
36
|
+
break;
|
|
37
|
+
out += text.slice(cursor, start) + ' ';
|
|
38
|
+
cursor = end + 2;
|
|
39
|
+
}
|
|
40
|
+
return out + text.slice(cursor);
|
|
41
|
+
}
|
|
42
|
+
/** 从文本降级输入解析 payload(需在 active session 的 fallback.map 中查找) */
|
|
43
|
+
export function resolveTextFallbackPayload(raw, map) {
|
|
44
|
+
const trimmed = raw.trim();
|
|
45
|
+
if (map[trimmed])
|
|
46
|
+
return map[trimmed];
|
|
47
|
+
const match = trimmed.match(/^(?:ttt\s+)?(\d)$/i);
|
|
48
|
+
if (match && map[match[1]])
|
|
49
|
+
return map[match[1]];
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 从用户文本入站解析 interactive payload:
|
|
54
|
+
* 归一化 → 直接匹配 prefix:session:id → fallback 数字映射
|
|
55
|
+
*/
|
|
56
|
+
export function resolvePayloadFromText(raw, map) {
|
|
57
|
+
const normalized = stripInteractiveCommandText(raw);
|
|
58
|
+
if (!normalized)
|
|
59
|
+
return undefined;
|
|
60
|
+
if (PAYLOAD_PATTERN.test(normalized))
|
|
61
|
+
return normalized;
|
|
62
|
+
if (map && Object.keys(map).length > 0) {
|
|
63
|
+
return resolveTextFallbackPayload(normalized, map);
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
export function formatActionAsRaw(data) {
|
|
68
|
+
return data.payload;
|
|
69
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ButtonData } from './types.js';
|
|
2
|
+
/** 键盘布局单元:`segment.button()` 产物,仅用于组装 `segment.keyboard()` */
|
|
3
|
+
export declare class ButtonSpec {
|
|
4
|
+
readonly data: ButtonData;
|
|
5
|
+
constructor(data: ButtonData);
|
|
6
|
+
}
|
|
7
|
+
export type KeyboardRowInput = Array<ButtonSpec | ButtonData>;
|
|
8
|
+
export declare function isButtonLike(value: unknown): value is ButtonSpec | ButtonData;
|
|
9
|
+
export declare function normalizeButton(btn: ButtonSpec | ButtonData): ButtonData;
|
|
10
|
+
export declare function normalizeKeyboardRows(rows: KeyboardRowInput[]): ButtonData[][];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** 键盘布局单元:`segment.button()` 产物,仅用于组装 `segment.keyboard()` */
|
|
2
|
+
export class ButtonSpec {
|
|
3
|
+
data;
|
|
4
|
+
constructor(data) {
|
|
5
|
+
this.data = data;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export function isButtonLike(value) {
|
|
9
|
+
if (value == null || typeof value !== 'object')
|
|
10
|
+
return false;
|
|
11
|
+
if (value instanceof ButtonSpec)
|
|
12
|
+
return true;
|
|
13
|
+
const v = value;
|
|
14
|
+
return typeof v.id === 'string' && typeof v.label === 'string' && typeof v.payload === 'string';
|
|
15
|
+
}
|
|
16
|
+
export function normalizeButton(btn) {
|
|
17
|
+
return btn instanceof ButtonSpec ? btn.data : btn;
|
|
18
|
+
}
|
|
19
|
+
export function normalizeKeyboardRows(rows) {
|
|
20
|
+
return rows.map((row) => row.map(normalizeButton));
|
|
21
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* keyboard 文本降级 fallback map 的中央存储。
|
|
3
|
+
*
|
|
4
|
+
* 出站:端点 interactive 策略为 'text' 时,框架把 keyboard 段降级为编号文本,
|
|
5
|
+
* 同时把「数字 → payload」映射按频道写入本存储(后写覆盖先写,等价
|
|
6
|
+
* game-kit lastMenus 的“最近一张键盘”语义)。
|
|
7
|
+
* 入站:框架中间件 / dispatcher 钩子用本存储把用户回复的裸数字解析回
|
|
8
|
+
* payload,再按 prefix 最长匹配路由给注册的 interactive handler。
|
|
9
|
+
*
|
|
10
|
+
* 模块级共享实例与 handlers.ts 的 handler 注册表同例:状态按频道键控、
|
|
11
|
+
* TTL 有界(对齐 game-hub 菜单上下文的 1h),无裸悬挂业务对象。
|
|
12
|
+
*/
|
|
13
|
+
export declare const KEYBOARD_FALLBACK_TTL_MS: number;
|
|
14
|
+
export declare class KeyboardFallbackStore {
|
|
15
|
+
#private;
|
|
16
|
+
private readonly defaultTtlMs;
|
|
17
|
+
constructor(defaultTtlMs?: number);
|
|
18
|
+
/** 记录频道最近一次 keyboard 降级的数字→payload 映射(空 map 忽略)。 */
|
|
19
|
+
remember(channelKey: string, map: Record<string, string>, ttlMs?: number): void;
|
|
20
|
+
/** 频道当前有效的 fallback map(过期即删并返回 undefined)。 */
|
|
21
|
+
mapFor(channelKey: string): Record<string, string> | undefined;
|
|
22
|
+
/** 裸数字 / fallback 键 → payload(无映射或过期返回 undefined)。 */
|
|
23
|
+
resolve(channelKey: string, raw: string): string | undefined;
|
|
24
|
+
clear(): void;
|
|
25
|
+
}
|
|
26
|
+
/** 中央共享实例:出站降级写入、入站回跳读取。 */
|
|
27
|
+
export declare const keyboardFallbackStore: KeyboardFallbackStore;
|
|
28
|
+
/** 测试专用:清空中央 fallback 存储。 */
|
|
29
|
+
export declare function resetKeyboardFallbackStoreForTests(): void;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { resolveTextFallbackPayload } from './action.js';
|
|
2
|
+
/**
|
|
3
|
+
* keyboard 文本降级 fallback map 的中央存储。
|
|
4
|
+
*
|
|
5
|
+
* 出站:端点 interactive 策略为 'text' 时,框架把 keyboard 段降级为编号文本,
|
|
6
|
+
* 同时把「数字 → payload」映射按频道写入本存储(后写覆盖先写,等价
|
|
7
|
+
* game-kit lastMenus 的“最近一张键盘”语义)。
|
|
8
|
+
* 入站:框架中间件 / dispatcher 钩子用本存储把用户回复的裸数字解析回
|
|
9
|
+
* payload,再按 prefix 最长匹配路由给注册的 interactive handler。
|
|
10
|
+
*
|
|
11
|
+
* 模块级共享实例与 handlers.ts 的 handler 注册表同例:状态按频道键控、
|
|
12
|
+
* TTL 有界(对齐 game-hub 菜单上下文的 1h),无裸悬挂业务对象。
|
|
13
|
+
*/
|
|
14
|
+
export const KEYBOARD_FALLBACK_TTL_MS = 60 * 60 * 1000;
|
|
15
|
+
export class KeyboardFallbackStore {
|
|
16
|
+
defaultTtlMs;
|
|
17
|
+
#entries = new Map();
|
|
18
|
+
constructor(defaultTtlMs = KEYBOARD_FALLBACK_TTL_MS) {
|
|
19
|
+
this.defaultTtlMs = defaultTtlMs;
|
|
20
|
+
}
|
|
21
|
+
/** 记录频道最近一次 keyboard 降级的数字→payload 映射(空 map 忽略)。 */
|
|
22
|
+
remember(channelKey, map, ttlMs = this.defaultTtlMs) {
|
|
23
|
+
if (Object.keys(map).length === 0)
|
|
24
|
+
return;
|
|
25
|
+
this.#prune();
|
|
26
|
+
this.#entries.set(channelKey, {
|
|
27
|
+
map: Object.freeze({ ...map }),
|
|
28
|
+
expiresAt: Date.now() + ttlMs,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** 频道当前有效的 fallback map(过期即删并返回 undefined)。 */
|
|
32
|
+
mapFor(channelKey) {
|
|
33
|
+
const entry = this.#entries.get(channelKey);
|
|
34
|
+
if (!entry)
|
|
35
|
+
return undefined;
|
|
36
|
+
if (entry.expiresAt < Date.now()) {
|
|
37
|
+
this.#entries.delete(channelKey);
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
return entry.map;
|
|
41
|
+
}
|
|
42
|
+
/** 裸数字 / fallback 键 → payload(无映射或过期返回 undefined)。 */
|
|
43
|
+
resolve(channelKey, raw) {
|
|
44
|
+
const map = this.mapFor(channelKey);
|
|
45
|
+
return map ? resolveTextFallbackPayload(raw, map) : undefined;
|
|
46
|
+
}
|
|
47
|
+
clear() {
|
|
48
|
+
this.#entries.clear();
|
|
49
|
+
}
|
|
50
|
+
#prune() {
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
for (const [key, entry] of this.#entries) {
|
|
53
|
+
if (entry.expiresAt < now)
|
|
54
|
+
this.#entries.delete(key);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** 中央共享实例:出站降级写入、入站回跳读取。 */
|
|
59
|
+
export const keyboardFallbackStore = new KeyboardFallbackStore();
|
|
60
|
+
/** 测试专用:清空中央 fallback 存储。 */
|
|
61
|
+
export function resetKeyboardFallbackStoreForTests() {
|
|
62
|
+
keyboardFallbackStore.clear();
|
|
63
|
+
}
|