@gakr-gakr/discord 0.1.0
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/account-inspect-api.ts +6 -0
- package/action-runtime-api.ts +1 -0
- package/api.ts +130 -0
- package/autobot.plugin.json +15 -0
- package/channel-config-api.ts +1 -0
- package/channel-plugin-api.ts +3 -0
- package/config-api.ts +4 -0
- package/configured-state.ts +6 -0
- package/contract-api.ts +21 -0
- package/directory-contract-api.ts +4 -0
- package/doctor-contract-api.ts +1 -0
- package/index.ts +24 -0
- package/package.json +79 -0
- package/runtime-api.actions.ts +15 -0
- package/runtime-api.lookup.ts +22 -0
- package/runtime-api.monitor.ts +50 -0
- package/runtime-api.send.ts +79 -0
- package/runtime-api.threads.ts +31 -0
- package/runtime-api.ts +181 -0
- package/runtime-setter-api.ts +3 -0
- package/secret-contract-api.ts +4 -0
- package/security-audit-contract-api.ts +1 -0
- package/security-contract-api.ts +4 -0
- package/session-key-api.ts +1 -0
- package/setup-entry.ts +9 -0
- package/setup-plugin-api.ts +3 -0
- package/src/account-inspect.ts +131 -0
- package/src/accounts.ts +205 -0
- package/src/actions/handle-action.guild-admin.ts +421 -0
- package/src/actions/handle-action.ts +402 -0
- package/src/actions/runtime.guild.ts +446 -0
- package/src/actions/runtime.messaging.messages.ts +226 -0
- package/src/actions/runtime.messaging.reactions.ts +67 -0
- package/src/actions/runtime.messaging.runtime.ts +73 -0
- package/src/actions/runtime.messaging.send.ts +336 -0
- package/src/actions/runtime.messaging.shared.ts +97 -0
- package/src/actions/runtime.messaging.ts +37 -0
- package/src/actions/runtime.moderation-shared.ts +48 -0
- package/src/actions/runtime.moderation.ts +116 -0
- package/src/actions/runtime.presence.ts +117 -0
- package/src/actions/runtime.shared.ts +86 -0
- package/src/actions/runtime.ts +87 -0
- package/src/api.ts +219 -0
- package/src/approval-handler.runtime.ts +636 -0
- package/src/approval-native.ts +219 -0
- package/src/approval-runtime.ts +14 -0
- package/src/approval-shared.ts +56 -0
- package/src/audit-core.ts +178 -0
- package/src/audit.ts +32 -0
- package/src/channel-actions.runtime.ts +1 -0
- package/src/channel-actions.ts +254 -0
- package/src/channel-api.ts +29 -0
- package/src/channel.conversation.ts +159 -0
- package/src/channel.loaders.ts +50 -0
- package/src/channel.runtime.ts +1 -0
- package/src/channel.setup.ts +12 -0
- package/src/channel.ts +728 -0
- package/src/chunk.ts +321 -0
- package/src/client.ts +143 -0
- package/src/component-custom-id.ts +72 -0
- package/src/components-registry.ts +356 -0
- package/src/components.builders.ts +410 -0
- package/src/components.modal.ts +124 -0
- package/src/components.parse.ts +407 -0
- package/src/components.ts +54 -0
- package/src/components.types.ts +187 -0
- package/src/config-schema.ts +6 -0
- package/src/config-ui-hints.ts +354 -0
- package/src/conversation-identity.ts +58 -0
- package/src/delivery-retry.ts +56 -0
- package/src/directory-cache.ts +116 -0
- package/src/directory-config.ts +58 -0
- package/src/directory-live.ts +135 -0
- package/src/doctor-contract.ts +477 -0
- package/src/doctor-shared.ts +5 -0
- package/src/doctor.ts +340 -0
- package/src/draft-chunking.ts +43 -0
- package/src/draft-stream.ts +162 -0
- package/src/error-body.ts +38 -0
- package/src/exec-approvals.ts +110 -0
- package/src/gateway-logging.ts +67 -0
- package/src/group-policy.ts +113 -0
- package/src/guilds.ts +29 -0
- package/src/inbound-event-delivery.ts +135 -0
- package/src/interactive-dispatch.ts +104 -0
- package/src/internal/api.commands.ts +51 -0
- package/src/internal/api.guild.ts +164 -0
- package/src/internal/api.interactions.ts +53 -0
- package/src/internal/api.messages.ts +113 -0
- package/src/internal/api.reactions.ts +38 -0
- package/src/internal/api.ts +61 -0
- package/src/internal/api.users.ts +19 -0
- package/src/internal/api.webhooks.ts +13 -0
- package/src/internal/client.ts +310 -0
- package/src/internal/command-deploy.ts +352 -0
- package/src/internal/commands.ts +188 -0
- package/src/internal/components.base.ts +65 -0
- package/src/internal/components.message.ts +279 -0
- package/src/internal/components.modal.ts +95 -0
- package/src/internal/components.ts +31 -0
- package/src/internal/discord.ts +11 -0
- package/src/internal/embeds.ts +35 -0
- package/src/internal/entity-cache.ts +98 -0
- package/src/internal/event-queue.ts +185 -0
- package/src/internal/gateway-close-codes.ts +25 -0
- package/src/internal/gateway-dispatch.ts +96 -0
- package/src/internal/gateway-identify-limiter.ts +26 -0
- package/src/internal/gateway-lifecycle.ts +75 -0
- package/src/internal/gateway-rate-limit.ts +104 -0
- package/src/internal/gateway.ts +479 -0
- package/src/internal/interaction-dispatch.ts +162 -0
- package/src/internal/interaction-options.ts +98 -0
- package/src/internal/interaction-response.ts +53 -0
- package/src/internal/interactions.ts +378 -0
- package/src/internal/listeners.ts +91 -0
- package/src/internal/modal-fields.ts +95 -0
- package/src/internal/payload.ts +69 -0
- package/src/internal/rest-body.ts +115 -0
- package/src/internal/rest-errors.ts +88 -0
- package/src/internal/rest-routes.ts +50 -0
- package/src/internal/rest-scheduler.ts +557 -0
- package/src/internal/rest.ts +322 -0
- package/src/internal/schemas.ts +36 -0
- package/src/internal/structures.ts +280 -0
- package/src/internal/test-builders.test-support.ts +167 -0
- package/src/internal/voice.ts +49 -0
- package/src/media-detection.ts +28 -0
- package/src/mentions.ts +147 -0
- package/src/monitor/ack-reactions.ts +70 -0
- package/src/monitor/agent-components-auth.ts +7 -0
- package/src/monitor/agent-components-context.ts +154 -0
- package/src/monitor/agent-components-data.ts +224 -0
- package/src/monitor/agent-components-dm-auth.ts +177 -0
- package/src/monitor/agent-components-guild-auth.ts +322 -0
- package/src/monitor/agent-components-helpers.runtime.ts +3 -0
- package/src/monitor/agent-components-helpers.ts +34 -0
- package/src/monitor/agent-components-reply.ts +10 -0
- package/src/monitor/agent-components.deps.runtime.ts +2 -0
- package/src/monitor/agent-components.dispatch.ts +359 -0
- package/src/monitor/agent-components.handlers.ts +303 -0
- package/src/monitor/agent-components.modal.ts +160 -0
- package/src/monitor/agent-components.plugin-interactive.ts +187 -0
- package/src/monitor/agent-components.runtime.ts +14 -0
- package/src/monitor/agent-components.system-controls.ts +215 -0
- package/src/monitor/agent-components.ts +70 -0
- package/src/monitor/agent-components.types.ts +58 -0
- package/src/monitor/agent-components.wildcard-controls.ts +171 -0
- package/src/monitor/allow-list.ts +631 -0
- package/src/monitor/auto-presence.ts +356 -0
- package/src/monitor/channel-access.ts +102 -0
- package/src/monitor/commands.ts +9 -0
- package/src/monitor/dm-command-auth.ts +259 -0
- package/src/monitor/dm-command-decision.ts +49 -0
- package/src/monitor/exec-approvals.ts +161 -0
- package/src/monitor/format.ts +45 -0
- package/src/monitor/gateway-handle.ts +34 -0
- package/src/monitor/gateway-metadata.ts +298 -0
- package/src/monitor/gateway-plugin.ts +302 -0
- package/src/monitor/gateway-registry.ts +37 -0
- package/src/monitor/gateway-supervisor.ts +206 -0
- package/src/monitor/inbound-context.ts +95 -0
- package/src/monitor/inbound-dedupe.ts +79 -0
- package/src/monitor/inbound-job.ts +118 -0
- package/src/monitor/listeners.queue.ts +91 -0
- package/src/monitor/listeners.reactions.ts +594 -0
- package/src/monitor/listeners.ts +150 -0
- package/src/monitor/message-channel-info.ts +96 -0
- package/src/monitor/message-forwarded.ts +114 -0
- package/src/monitor/message-handler.batch-gate.ts +19 -0
- package/src/monitor/message-handler.context.ts +492 -0
- package/src/monitor/message-handler.dm-preflight.ts +119 -0
- package/src/monitor/message-handler.draft-preview.ts +436 -0
- package/src/monitor/message-handler.hydration.ts +198 -0
- package/src/monitor/message-handler.module-test-helpers.ts +31 -0
- package/src/monitor/message-handler.preflight-channel-access.ts +86 -0
- package/src/monitor/message-handler.preflight-channel-context.ts +58 -0
- package/src/monitor/message-handler.preflight-context.ts +54 -0
- package/src/monitor/message-handler.preflight-helpers.ts +164 -0
- package/src/monitor/message-handler.preflight-history.ts +23 -0
- package/src/monitor/message-handler.preflight-logging.ts +36 -0
- package/src/monitor/message-handler.preflight-pluralkit.ts +28 -0
- package/src/monitor/message-handler.preflight-runtime.ts +28 -0
- package/src/monitor/message-handler.preflight-thread.ts +49 -0
- package/src/monitor/message-handler.preflight.ts +822 -0
- package/src/monitor/message-handler.preflight.types.ts +115 -0
- package/src/monitor/message-handler.process.ts +1033 -0
- package/src/monitor/message-handler.routing-preflight.ts +112 -0
- package/src/monitor/message-handler.ts +309 -0
- package/src/monitor/message-media.ts +536 -0
- package/src/monitor/message-run-queue.ts +101 -0
- package/src/monitor/message-text.ts +171 -0
- package/src/monitor/message-utils.ts +34 -0
- package/src/monitor/model-picker-preferences.ts +184 -0
- package/src/monitor/model-picker.state.ts +364 -0
- package/src/monitor/model-picker.test-utils.ts +26 -0
- package/src/monitor/model-picker.ts +38 -0
- package/src/monitor/model-picker.view.ts +722 -0
- package/src/monitor/native-command-agent-reply.ts +125 -0
- package/src/monitor/native-command-arg-ui.ts +233 -0
- package/src/monitor/native-command-auth.ts +309 -0
- package/src/monitor/native-command-bypass.ts +13 -0
- package/src/monitor/native-command-context.ts +109 -0
- package/src/monitor/native-command-dispatch.ts +35 -0
- package/src/monitor/native-command-model-picker-apply.ts +209 -0
- package/src/monitor/native-command-model-picker-interaction.ts +516 -0
- package/src/monitor/native-command-model-picker-ui.ts +357 -0
- package/src/monitor/native-command-reply.ts +185 -0
- package/src/monitor/native-command-route.ts +91 -0
- package/src/monitor/native-command-status.ts +76 -0
- package/src/monitor/native-command-ui.ts +26 -0
- package/src/monitor/native-command-ui.types.ts +20 -0
- package/src/monitor/native-command.args.ts +45 -0
- package/src/monitor/native-command.options.ts +153 -0
- package/src/monitor/native-command.runtime.ts +51 -0
- package/src/monitor/native-command.ts +747 -0
- package/src/monitor/native-command.types.ts +9 -0
- package/src/monitor/native-interaction-channel-context.ts +50 -0
- package/src/monitor/preflight-audio.runtime.ts +9 -0
- package/src/monitor/preflight-audio.ts +130 -0
- package/src/monitor/presence-cache.ts +61 -0
- package/src/monitor/presence.ts +50 -0
- package/src/monitor/provider-session.runtime.ts +12 -0
- package/src/monitor/provider.acp.ts +89 -0
- package/src/monitor/provider.allowlist.ts +398 -0
- package/src/monitor/provider.cleanup.ts +41 -0
- package/src/monitor/provider.commands.ts +129 -0
- package/src/monitor/provider.config-log.ts +45 -0
- package/src/monitor/provider.deploy-errors.ts +362 -0
- package/src/monitor/provider.deploy.ts +221 -0
- package/src/monitor/provider.interactions.ts +160 -0
- package/src/monitor/provider.lifecycle.ts +562 -0
- package/src/monitor/provider.runtime.ts +1 -0
- package/src/monitor/provider.startup-log.ts +32 -0
- package/src/monitor/provider.startup.ts +323 -0
- package/src/monitor/provider.ts +688 -0
- package/src/monitor/reply-context.ts +64 -0
- package/src/monitor/reply-delivery.ts +216 -0
- package/src/monitor/reply-safety.ts +96 -0
- package/src/monitor/rest-fetch.ts +97 -0
- package/src/monitor/route-resolution.ts +140 -0
- package/src/monitor/sender-identity.ts +81 -0
- package/src/monitor/startup-status.ts +10 -0
- package/src/monitor/status.ts +22 -0
- package/src/monitor/system-events.ts +55 -0
- package/src/monitor/thread-bindings.config.ts +35 -0
- package/src/monitor/thread-bindings.discord-api.ts +310 -0
- package/src/monitor/thread-bindings.lifecycle.ts +354 -0
- package/src/monitor/thread-bindings.manager.ts +554 -0
- package/src/monitor/thread-bindings.messages.ts +6 -0
- package/src/monitor/thread-bindings.persona.ts +25 -0
- package/src/monitor/thread-bindings.session-adapter.ts +229 -0
- package/src/monitor/thread-bindings.session-shared.ts +59 -0
- package/src/monitor/thread-bindings.session-updates.ts +35 -0
- package/src/monitor/thread-bindings.state.ts +540 -0
- package/src/monitor/thread-bindings.ts +48 -0
- package/src/monitor/thread-bindings.types.ts +83 -0
- package/src/monitor/thread-channel-context.ts +112 -0
- package/src/monitor/thread-session-close.ts +63 -0
- package/src/monitor/thread-title.ts +181 -0
- package/src/monitor/threading.auto-thread.ts +287 -0
- package/src/monitor/threading.cache.ts +45 -0
- package/src/monitor/threading.starter.ts +288 -0
- package/src/monitor/threading.ts +20 -0
- package/src/monitor/threading.types.ts +102 -0
- package/src/monitor/timeouts.ts +84 -0
- package/src/monitor/typing.ts +17 -0
- package/src/monitor.gateway.ts +75 -0
- package/src/monitor.ts +28 -0
- package/src/network-config.ts +79 -0
- package/src/normalize.ts +86 -0
- package/src/outbound-adapter.ts +327 -0
- package/src/outbound-approval.ts +29 -0
- package/src/outbound-components.ts +86 -0
- package/src/outbound-payload.ts +208 -0
- package/src/outbound-send-context.ts +92 -0
- package/src/outbound-session-route.ts +72 -0
- package/src/pluralkit.ts +58 -0
- package/src/preview-streaming.ts +18 -0
- package/src/probe.runtime.ts +1 -0
- package/src/probe.ts +237 -0
- package/src/proxy-fetch.ts +92 -0
- package/src/proxy-request-client.ts +21 -0
- package/src/recipient-resolution.ts +39 -0
- package/src/resolve-allowlist-common.ts +39 -0
- package/src/resolve-channels.ts +369 -0
- package/src/resolve-users.ts +184 -0
- package/src/retry.ts +98 -0
- package/src/runtime-api.ts +64 -0
- package/src/runtime-config.ts +16 -0
- package/src/runtime.ts +23 -0
- package/src/secret-config-contract.ts +140 -0
- package/src/security-audit.runtime.ts +1 -0
- package/src/security-audit.ts +208 -0
- package/src/security-contract.ts +47 -0
- package/src/security-doctor.ts +20 -0
- package/src/security.ts +60 -0
- package/src/send-target-parsing.ts +14 -0
- package/src/send.channels.ts +139 -0
- package/src/send.components.ts +391 -0
- package/src/send.emojis-stickers.ts +57 -0
- package/src/send.guild.ts +170 -0
- package/src/send.message-request.ts +112 -0
- package/src/send.messages.ts +229 -0
- package/src/send.outbound.ts +459 -0
- package/src/send.permissions.ts +283 -0
- package/src/send.reactions.ts +155 -0
- package/src/send.receipt.ts +69 -0
- package/src/send.shared.ts +469 -0
- package/src/send.ts +82 -0
- package/src/send.types.ts +191 -0
- package/src/send.typing.ts +9 -0
- package/src/send.voice.ts +140 -0
- package/src/send.webhook.ts +137 -0
- package/src/session-contract.ts +3 -0
- package/src/session-key-normalization.ts +47 -0
- package/src/setup-account-state.ts +144 -0
- package/src/setup-adapter.ts +14 -0
- package/src/setup-core.ts +215 -0
- package/src/setup-runtime-helpers.ts +10 -0
- package/src/setup-surface.ts +132 -0
- package/src/shared-interactive.ts +167 -0
- package/src/shared.ts +197 -0
- package/src/status-issues.ts +201 -0
- package/src/subagent-hooks.ts +232 -0
- package/src/target-parsing.ts +70 -0
- package/src/target-resolver.ts +129 -0
- package/src/targets.ts +12 -0
- package/src/token.ts +107 -0
- package/src/ui-colors.ts +27 -0
- package/src/ui.ts +20 -0
- package/src/voice/access.ts +126 -0
- package/src/voice/audio.ts +249 -0
- package/src/voice/capture-state.ts +120 -0
- package/src/voice/command.ts +284 -0
- package/src/voice/config.ts +8 -0
- package/src/voice/ingress.ts +164 -0
- package/src/voice/manager.runtime.ts +14 -0
- package/src/voice/manager.ts +1155 -0
- package/src/voice/prompt.ts +22 -0
- package/src/voice/realtime.ts +1370 -0
- package/src/voice/receive-recovery.ts +159 -0
- package/src/voice/sanitize.ts +29 -0
- package/src/voice/sdk-runtime.ts +14 -0
- package/src/voice/segment.ts +160 -0
- package/src/voice/session.ts +81 -0
- package/src/voice/speaker-context.ts +127 -0
- package/src/voice/tts.ts +151 -0
- package/src/voice-message.ts +474 -0
- package/subagent-hooks-api.ts +27 -0
- package/test-api.ts +4 -0
- package/thread-binding-api.ts +1 -0
- package/timeouts.ts +6 -0
- package/tsconfig.json +16 -0
package/src/chunk.ts
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { chunkMarkdownTextWithMode, type ChunkMode } from "autobot/plugin-sdk/reply-chunking";
|
|
2
|
+
|
|
3
|
+
type ChunkDiscordTextOpts = {
|
|
4
|
+
/** Max characters per Discord message. Default: 2000. */
|
|
5
|
+
maxChars?: number;
|
|
6
|
+
/**
|
|
7
|
+
* Soft max line count per message. Default: 17.
|
|
8
|
+
*
|
|
9
|
+
* Discord clients can clip/collapse very tall messages in the UI; splitting
|
|
10
|
+
* by lines keeps long multi-paragraph replies readable.
|
|
11
|
+
*/
|
|
12
|
+
maxLines?: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type OpenFence = {
|
|
16
|
+
indent: string;
|
|
17
|
+
markerChar: string;
|
|
18
|
+
markerLen: number;
|
|
19
|
+
openLine: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const DEFAULT_MAX_CHARS = 2000;
|
|
23
|
+
const DEFAULT_MAX_LINES = 17;
|
|
24
|
+
const FENCE_RE = /^( {0,3})(`{3,}|~{3,})(.*)$/;
|
|
25
|
+
const CJK_PUNCTUATION_BREAK_AFTER_RE = /[、。,.!?;:)]}〉》」』】〕〗〙]/u;
|
|
26
|
+
|
|
27
|
+
function countLines(text: string) {
|
|
28
|
+
if (!text) {
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
return text.split("\n").length;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseFenceLine(line: string): OpenFence | null {
|
|
35
|
+
const match = line.match(FENCE_RE);
|
|
36
|
+
if (!match) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const indent = match[1] ?? "";
|
|
40
|
+
const marker = match[2] ?? "";
|
|
41
|
+
return {
|
|
42
|
+
indent,
|
|
43
|
+
markerChar: marker[0] ?? "`",
|
|
44
|
+
markerLen: marker.length,
|
|
45
|
+
openLine: line,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function closeFenceLine(openFence: OpenFence) {
|
|
50
|
+
return `${openFence.indent}${openFence.markerChar.repeat(openFence.markerLen)}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function closeFenceIfNeeded(text: string, openFence: OpenFence | null) {
|
|
54
|
+
if (!openFence) {
|
|
55
|
+
return text;
|
|
56
|
+
}
|
|
57
|
+
const closeLine = closeFenceLine(openFence);
|
|
58
|
+
if (!text) {
|
|
59
|
+
return closeLine;
|
|
60
|
+
}
|
|
61
|
+
if (!text.endsWith("\n")) {
|
|
62
|
+
return `${text}\n${closeLine}`;
|
|
63
|
+
}
|
|
64
|
+
return `${text}${closeLine}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isHighSurrogate(code: number) {
|
|
68
|
+
return code >= 0xd800 && code <= 0xdbff;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isLowSurrogate(code: number) {
|
|
72
|
+
return code >= 0xdc00 && code <= 0xdfff;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function clampToCodePointBoundary(text: string, index: number) {
|
|
76
|
+
const boundary = Math.min(Math.max(0, index), text.length);
|
|
77
|
+
if (boundary <= 0 || boundary >= text.length) {
|
|
78
|
+
return boundary;
|
|
79
|
+
}
|
|
80
|
+
const previous = text.charCodeAt(boundary - 1);
|
|
81
|
+
const next = text.charCodeAt(boundary);
|
|
82
|
+
if (isHighSurrogate(previous) && isLowSurrogate(next)) {
|
|
83
|
+
return boundary > 1 ? boundary - 1 : boundary + 1;
|
|
84
|
+
}
|
|
85
|
+
return boundary;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function findWhitespaceBreak(window: string) {
|
|
89
|
+
for (let i = window.length - 1; i >= 0; i--) {
|
|
90
|
+
if (/\s/.test(window[i])) {
|
|
91
|
+
// Return the separator index so whitespace stays with the next segment.
|
|
92
|
+
return i;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return -1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function findCjkPunctuationBreak(window: string) {
|
|
99
|
+
for (let end = window.length; end > 0; ) {
|
|
100
|
+
const code = window.charCodeAt(end - 1);
|
|
101
|
+
const start = isLowSurrogate(code) && end > 1 ? end - 2 : end - 1;
|
|
102
|
+
const char = window.slice(start, end);
|
|
103
|
+
if (start > 0 && CJK_PUNCTUATION_BREAK_AFTER_RE.test(char)) {
|
|
104
|
+
// Return the exclusive end so CJK punctuation stays with the current segment.
|
|
105
|
+
return end;
|
|
106
|
+
}
|
|
107
|
+
end = start;
|
|
108
|
+
}
|
|
109
|
+
return -1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function splitLongLine(
|
|
113
|
+
line: string,
|
|
114
|
+
maxChars: number,
|
|
115
|
+
opts: { preserveWhitespace: boolean },
|
|
116
|
+
): string[] {
|
|
117
|
+
const limit = Math.max(1, Math.floor(maxChars));
|
|
118
|
+
if (line.length <= limit) {
|
|
119
|
+
return [line];
|
|
120
|
+
}
|
|
121
|
+
const out: string[] = [];
|
|
122
|
+
let remaining = line;
|
|
123
|
+
while (remaining.length > limit) {
|
|
124
|
+
if (opts.preserveWhitespace) {
|
|
125
|
+
const breakIdx = clampToCodePointBoundary(remaining, limit);
|
|
126
|
+
out.push(remaining.slice(0, breakIdx));
|
|
127
|
+
remaining = remaining.slice(breakIdx);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const window = remaining.slice(0, limit);
|
|
131
|
+
let breakIdx = findWhitespaceBreak(window);
|
|
132
|
+
if (breakIdx <= 0) {
|
|
133
|
+
breakIdx = findCjkPunctuationBreak(window);
|
|
134
|
+
}
|
|
135
|
+
if (breakIdx <= 0) {
|
|
136
|
+
breakIdx = clampToCodePointBoundary(remaining, limit);
|
|
137
|
+
}
|
|
138
|
+
out.push(remaining.slice(0, breakIdx));
|
|
139
|
+
// Keep the separator for the next segment so words don't get glued together.
|
|
140
|
+
remaining = remaining.slice(breakIdx);
|
|
141
|
+
}
|
|
142
|
+
if (remaining.length) {
|
|
143
|
+
out.push(remaining);
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Chunks outbound Discord text by both character count and (soft) line count,
|
|
150
|
+
* while keeping fenced code blocks balanced across chunks.
|
|
151
|
+
*/
|
|
152
|
+
export function chunkDiscordText(text: string, opts: ChunkDiscordTextOpts = {}): string[] {
|
|
153
|
+
const maxChars = Math.max(1, Math.floor(opts.maxChars ?? DEFAULT_MAX_CHARS));
|
|
154
|
+
const maxLines = Math.max(1, Math.floor(opts.maxLines ?? DEFAULT_MAX_LINES));
|
|
155
|
+
|
|
156
|
+
const body = text ?? "";
|
|
157
|
+
if (!body) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const alreadyOk = body.length <= maxChars && countLines(body) <= maxLines;
|
|
162
|
+
if (alreadyOk) {
|
|
163
|
+
return [body];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const lines = body.split("\n");
|
|
167
|
+
const chunks: string[] = [];
|
|
168
|
+
|
|
169
|
+
let current = "";
|
|
170
|
+
let currentLines = 0;
|
|
171
|
+
let openFence: OpenFence | null = null;
|
|
172
|
+
|
|
173
|
+
const flush = () => {
|
|
174
|
+
if (!current) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const payload = closeFenceIfNeeded(current, openFence);
|
|
178
|
+
if (payload.trim().length) {
|
|
179
|
+
chunks.push(payload);
|
|
180
|
+
}
|
|
181
|
+
current = "";
|
|
182
|
+
currentLines = 0;
|
|
183
|
+
if (openFence) {
|
|
184
|
+
current = openFence.openLine;
|
|
185
|
+
currentLines = 1;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
for (const originalLine of lines) {
|
|
190
|
+
const fenceInfo = parseFenceLine(originalLine);
|
|
191
|
+
const wasInsideFence = openFence !== null;
|
|
192
|
+
let nextOpenFence: OpenFence | null = openFence;
|
|
193
|
+
if (fenceInfo) {
|
|
194
|
+
if (!openFence) {
|
|
195
|
+
nextOpenFence = fenceInfo;
|
|
196
|
+
} else if (
|
|
197
|
+
openFence.markerChar === fenceInfo.markerChar &&
|
|
198
|
+
fenceInfo.markerLen >= openFence.markerLen
|
|
199
|
+
) {
|
|
200
|
+
nextOpenFence = null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const reserveChars = nextOpenFence ? closeFenceLine(nextOpenFence).length + 1 : 0;
|
|
205
|
+
const reserveLines = nextOpenFence ? 1 : 0;
|
|
206
|
+
const effectiveMaxChars = maxChars - reserveChars;
|
|
207
|
+
const effectiveMaxLines = maxLines - reserveLines;
|
|
208
|
+
const charLimit = effectiveMaxChars > 0 ? effectiveMaxChars : maxChars;
|
|
209
|
+
const lineLimit = effectiveMaxLines > 0 ? effectiveMaxLines : maxLines;
|
|
210
|
+
const prefixLen = current.length > 0 ? current.length + 1 : 0;
|
|
211
|
+
const segmentLimit = Math.max(1, charLimit - prefixLen);
|
|
212
|
+
const segments = splitLongLine(originalLine, segmentLimit, {
|
|
213
|
+
preserveWhitespace: wasInsideFence,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
for (let segIndex = 0; segIndex < segments.length; segIndex++) {
|
|
217
|
+
const segment = segments[segIndex];
|
|
218
|
+
const isLineContinuation = segIndex > 0;
|
|
219
|
+
const delimiter = isLineContinuation ? "" : current.length > 0 ? "\n" : "";
|
|
220
|
+
const addition = `${delimiter}${segment}`;
|
|
221
|
+
const nextLen = current.length + addition.length;
|
|
222
|
+
const nextLines = currentLines + (isLineContinuation ? 0 : 1);
|
|
223
|
+
|
|
224
|
+
const wouldExceedChars = nextLen > charLimit;
|
|
225
|
+
const wouldExceedLines = nextLines > lineLimit;
|
|
226
|
+
|
|
227
|
+
if ((wouldExceedChars || wouldExceedLines) && current.length > 0) {
|
|
228
|
+
flush();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (current.length > 0) {
|
|
232
|
+
current += addition;
|
|
233
|
+
if (!isLineContinuation) {
|
|
234
|
+
currentLines += 1;
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
current = segment;
|
|
238
|
+
currentLines = 1;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
openFence = nextOpenFence;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (current.length) {
|
|
246
|
+
const payload = closeFenceIfNeeded(current, openFence);
|
|
247
|
+
if (payload.trim().length) {
|
|
248
|
+
chunks.push(payload);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return rebalanceReasoningItalics(text, chunks);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function chunkDiscordTextWithMode(
|
|
256
|
+
text: string,
|
|
257
|
+
opts: ChunkDiscordTextOpts & { chunkMode?: ChunkMode },
|
|
258
|
+
): string[] {
|
|
259
|
+
const chunkMode = opts.chunkMode ?? "length";
|
|
260
|
+
if (chunkMode !== "newline") {
|
|
261
|
+
return chunkDiscordText(text, opts);
|
|
262
|
+
}
|
|
263
|
+
const lineChunks = chunkMarkdownTextWithMode(
|
|
264
|
+
text,
|
|
265
|
+
Math.max(1, Math.floor(opts.maxChars ?? DEFAULT_MAX_CHARS)),
|
|
266
|
+
"newline",
|
|
267
|
+
);
|
|
268
|
+
const chunks: string[] = [];
|
|
269
|
+
for (const line of lineChunks) {
|
|
270
|
+
const nested = chunkDiscordText(line, opts);
|
|
271
|
+
if (!nested.length && line) {
|
|
272
|
+
chunks.push(line);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
chunks.push(...nested);
|
|
276
|
+
}
|
|
277
|
+
return chunks;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Keep italics intact for reasoning payloads that are wrapped once with `_…_`.
|
|
281
|
+
// When Discord chunking splits the message, we close italics at the end of
|
|
282
|
+
// each chunk and reopen at the start of the next so every chunk renders
|
|
283
|
+
// consistently.
|
|
284
|
+
function rebalanceReasoningItalics(source: string, chunks: string[]): string[] {
|
|
285
|
+
if (chunks.length <= 1) {
|
|
286
|
+
return chunks;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const opensWithReasoningItalics =
|
|
290
|
+
/^(?:Reasoning:|Thinking\.{0,3})\n+_/u.test(source) && source.trimEnd().endsWith("_");
|
|
291
|
+
if (!opensWithReasoningItalics) {
|
|
292
|
+
return chunks;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const adjusted = [...chunks];
|
|
296
|
+
for (let i = 0; i < adjusted.length; i++) {
|
|
297
|
+
const isLast = i === adjusted.length - 1;
|
|
298
|
+
const current = adjusted[i];
|
|
299
|
+
|
|
300
|
+
// Ensure current chunk closes italics so Discord renders it italicized.
|
|
301
|
+
const needsClosing = !current.trimEnd().endsWith("_");
|
|
302
|
+
if (needsClosing) {
|
|
303
|
+
adjusted[i] = `${current}_`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (isLast) {
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Re-open italics on the next chunk if needed.
|
|
311
|
+
const next = adjusted[i + 1];
|
|
312
|
+
const leadingWhitespaceLen = next.length - next.trimStart().length;
|
|
313
|
+
const leadingWhitespace = next.slice(0, leadingWhitespaceLen);
|
|
314
|
+
const nextBody = next.slice(leadingWhitespaceLen);
|
|
315
|
+
if (!nextBody.startsWith("_")) {
|
|
316
|
+
adjusted[i + 1] = `${leadingWhitespace}_${nextBody}`;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return adjusted;
|
|
321
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
|
|
2
|
+
import { requireRuntimeConfig } from "autobot/plugin-sdk/plugin-config-runtime";
|
|
3
|
+
import type { RetryConfig, RetryRunner } from "autobot/plugin-sdk/retry-runtime";
|
|
4
|
+
import { normalizeAccountId } from "autobot/plugin-sdk/routing";
|
|
5
|
+
import type { RuntimeEnv } from "autobot/plugin-sdk/runtime-env";
|
|
6
|
+
import { normalizeOptionalString } from "autobot/plugin-sdk/string-coerce-runtime";
|
|
7
|
+
import {
|
|
8
|
+
mergeDiscordAccountConfig,
|
|
9
|
+
resolveDiscordAccount,
|
|
10
|
+
type ResolvedDiscordAccount,
|
|
11
|
+
} from "./accounts.js";
|
|
12
|
+
import { RequestClient } from "./internal/discord.js";
|
|
13
|
+
import { resolveDiscordProxyFetchForAccount } from "./proxy-fetch.js";
|
|
14
|
+
import { createDiscordRequestClient } from "./proxy-request-client.js";
|
|
15
|
+
import { createDiscordRetryRunner } from "./retry.js";
|
|
16
|
+
import type { DiscordRuntimeAccountContext } from "./send.types.js";
|
|
17
|
+
import { normalizeDiscordToken } from "./token.js";
|
|
18
|
+
|
|
19
|
+
export type DiscordClientOpts = {
|
|
20
|
+
cfg: AutoBotConfig;
|
|
21
|
+
token?: string;
|
|
22
|
+
accountId?: string;
|
|
23
|
+
rest?: RequestClient;
|
|
24
|
+
retry?: RetryConfig;
|
|
25
|
+
verbose?: boolean;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function createDiscordRuntimeAccountContext(params: {
|
|
29
|
+
cfg: AutoBotConfig;
|
|
30
|
+
accountId: string;
|
|
31
|
+
}): DiscordRuntimeAccountContext {
|
|
32
|
+
return {
|
|
33
|
+
cfg: params.cfg,
|
|
34
|
+
accountId: normalizeAccountId(params.accountId),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function resolveDiscordClientAccountContext(
|
|
39
|
+
opts: Pick<DiscordClientOpts, "cfg" | "accountId">,
|
|
40
|
+
runtime?: Pick<RuntimeEnv, "error">,
|
|
41
|
+
) {
|
|
42
|
+
const resolvedCfg = requireRuntimeConfig(opts.cfg, "Discord client");
|
|
43
|
+
const account = resolveAccountWithoutToken({
|
|
44
|
+
cfg: resolvedCfg,
|
|
45
|
+
accountId: opts.accountId,
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
cfg: resolvedCfg,
|
|
49
|
+
account,
|
|
50
|
+
proxyFetch: resolveDiscordProxyFetchForAccount(account, resolvedCfg, runtime),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveToken(params: {
|
|
55
|
+
account: ResolvedDiscordAccount;
|
|
56
|
+
accountId: string;
|
|
57
|
+
fallbackToken?: string;
|
|
58
|
+
}) {
|
|
59
|
+
const fallback = normalizeDiscordToken(params.fallbackToken, "channels.discord.token");
|
|
60
|
+
if (!fallback) {
|
|
61
|
+
if (params.account.tokenStatus === "configured_unavailable") {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Discord bot token configured for account "${params.accountId}" is unavailable; resolve SecretRefs against the active runtime snapshot before using this account.`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Discord bot token missing for account "${params.accountId}" (set discord.accounts.${params.accountId}.token or DISCORD_BOT_TOKEN for default).`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return fallback;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function resolveRest(
|
|
74
|
+
token: string,
|
|
75
|
+
account: ResolvedDiscordAccount,
|
|
76
|
+
cfg: AutoBotConfig,
|
|
77
|
+
rest?: RequestClient,
|
|
78
|
+
proxyFetch?: typeof fetch,
|
|
79
|
+
) {
|
|
80
|
+
if (rest) {
|
|
81
|
+
return rest;
|
|
82
|
+
}
|
|
83
|
+
const resolvedProxyFetch = proxyFetch ?? resolveDiscordProxyFetchForAccount(account, cfg);
|
|
84
|
+
return createDiscordRequestClient(
|
|
85
|
+
token,
|
|
86
|
+
resolvedProxyFetch ? { fetch: resolvedProxyFetch } : undefined,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function resolveAccountWithoutToken(params: {
|
|
91
|
+
cfg: AutoBotConfig;
|
|
92
|
+
accountId?: string;
|
|
93
|
+
}): ResolvedDiscordAccount {
|
|
94
|
+
const accountId = normalizeAccountId(params.accountId);
|
|
95
|
+
const merged = mergeDiscordAccountConfig(params.cfg, accountId);
|
|
96
|
+
const baseEnabled = params.cfg.channels?.discord?.enabled !== false;
|
|
97
|
+
const accountEnabled = merged.enabled !== false;
|
|
98
|
+
return {
|
|
99
|
+
accountId,
|
|
100
|
+
enabled: baseEnabled && accountEnabled,
|
|
101
|
+
name: normalizeOptionalString(merged.name),
|
|
102
|
+
token: "",
|
|
103
|
+
tokenSource: "none",
|
|
104
|
+
tokenStatus: "missing",
|
|
105
|
+
config: merged,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function createDiscordRestClient(opts: DiscordClientOpts) {
|
|
110
|
+
const explicitToken = normalizeDiscordToken(opts.token, "channels.discord.token");
|
|
111
|
+
const proxyContext = resolveDiscordClientAccountContext(opts);
|
|
112
|
+
const resolvedCfg = proxyContext.cfg;
|
|
113
|
+
const account = explicitToken
|
|
114
|
+
? proxyContext.account
|
|
115
|
+
: resolveDiscordAccount({ cfg: resolvedCfg, accountId: opts.accountId });
|
|
116
|
+
const token =
|
|
117
|
+
explicitToken ??
|
|
118
|
+
resolveToken({
|
|
119
|
+
account,
|
|
120
|
+
accountId: account.accountId,
|
|
121
|
+
fallbackToken: account.token,
|
|
122
|
+
});
|
|
123
|
+
const rest = resolveRest(token, account, resolvedCfg, opts.rest, proxyContext.proxyFetch);
|
|
124
|
+
return { token, rest, account };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function createDiscordClient(opts: DiscordClientOpts): {
|
|
128
|
+
token: string;
|
|
129
|
+
rest: RequestClient;
|
|
130
|
+
request: RetryRunner;
|
|
131
|
+
} {
|
|
132
|
+
const { token, rest, account } = createDiscordRestClient(opts);
|
|
133
|
+
const request = createDiscordRetryRunner({
|
|
134
|
+
retry: opts.retry,
|
|
135
|
+
configRetry: account.config.retry,
|
|
136
|
+
verbose: opts.verbose,
|
|
137
|
+
});
|
|
138
|
+
return { token, rest, request };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function resolveDiscordRest(opts: DiscordClientOpts) {
|
|
142
|
+
return createDiscordRestClient(opts).rest;
|
|
143
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { parseCustomId, type ComponentParserResult } from "./internal/discord.js";
|
|
2
|
+
|
|
3
|
+
export const DISCORD_COMPONENT_CUSTOM_ID_KEY = "occomp";
|
|
4
|
+
export const DISCORD_MODAL_CUSTOM_ID_KEY = "ocmodal";
|
|
5
|
+
|
|
6
|
+
export function buildDiscordComponentCustomId(params: {
|
|
7
|
+
componentId: string;
|
|
8
|
+
modalId?: string;
|
|
9
|
+
}): string {
|
|
10
|
+
const base = `${DISCORD_COMPONENT_CUSTOM_ID_KEY}:cid=${params.componentId}`;
|
|
11
|
+
return params.modalId ? `${base};mid=${params.modalId}` : base;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function buildDiscordModalCustomId(modalId: string): string {
|
|
15
|
+
return `${DISCORD_MODAL_CUSTOM_ID_KEY}:mid=${modalId}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function parseDiscordComponentCustomId(
|
|
19
|
+
id: string,
|
|
20
|
+
): { componentId: string; modalId?: string } | null {
|
|
21
|
+
const parsed = parseCustomId(id);
|
|
22
|
+
if (parsed.key !== DISCORD_COMPONENT_CUSTOM_ID_KEY) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const componentId = parsed.data.cid;
|
|
26
|
+
if (typeof componentId !== "string" || !componentId.trim()) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const modalId = parsed.data.mid;
|
|
30
|
+
return {
|
|
31
|
+
componentId,
|
|
32
|
+
modalId: typeof modalId === "string" && modalId.trim() ? modalId : undefined,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function parseDiscordModalCustomId(id: string): string | null {
|
|
37
|
+
const parsed = parseCustomId(id);
|
|
38
|
+
if (parsed.key !== DISCORD_MODAL_CUSTOM_ID_KEY) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const modalId = parsed.data.mid;
|
|
42
|
+
if (typeof modalId !== "string" || !modalId.trim()) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return modalId;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isDiscordComponentWildcardRegistrationId(id: string): boolean {
|
|
49
|
+
return /^__autobot_discord_component_[a-z_]+_wildcard__$/.test(id);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function parseDiscordComponentCustomIdForInteraction(id: string): ComponentParserResult {
|
|
53
|
+
if (id === "*" || isDiscordComponentWildcardRegistrationId(id)) {
|
|
54
|
+
return { key: "*", data: {} };
|
|
55
|
+
}
|
|
56
|
+
const parsed = parseCustomId(id);
|
|
57
|
+
if (parsed.key !== DISCORD_COMPONENT_CUSTOM_ID_KEY) {
|
|
58
|
+
return parsed;
|
|
59
|
+
}
|
|
60
|
+
return { key: "*", data: parsed.data };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function parseDiscordModalCustomIdForInteraction(id: string): ComponentParserResult {
|
|
64
|
+
if (id === "*" || isDiscordComponentWildcardRegistrationId(id)) {
|
|
65
|
+
return { key: "*", data: {} };
|
|
66
|
+
}
|
|
67
|
+
const parsed = parseCustomId(id);
|
|
68
|
+
if (parsed.key !== DISCORD_MODAL_CUSTOM_ID_KEY) {
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
return { key: "*", data: parsed.data };
|
|
72
|
+
}
|