@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
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type APIRole, type APIUser } from "discord-api-types/v10";
|
|
2
|
+
import { Role, User, type StructureClient } from "./structures.js";
|
|
3
|
+
|
|
4
|
+
type ModalResolvedData = {
|
|
5
|
+
roles?: Record<string, { id: string; name?: string }>;
|
|
6
|
+
users?: Record<string, { id: string; username?: string; discriminator?: string }>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function extractModalFields(components: unknown[]): Record<string, string | string[]> {
|
|
10
|
+
const out: Record<string, string | string[]> = {};
|
|
11
|
+
for (const component of flattenModalComponents(components)) {
|
|
12
|
+
const raw = component as { custom_id?: unknown; value?: unknown; values?: unknown };
|
|
13
|
+
if (typeof raw.custom_id !== "string") {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(raw.values)) {
|
|
17
|
+
out[raw.custom_id] = raw.values.map(String);
|
|
18
|
+
} else if (
|
|
19
|
+
typeof raw.value === "string" ||
|
|
20
|
+
typeof raw.value === "number" ||
|
|
21
|
+
typeof raw.value === "boolean"
|
|
22
|
+
) {
|
|
23
|
+
out[raw.custom_id] = String(raw.value);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function flattenModalComponents(components: unknown[]): unknown[] {
|
|
30
|
+
const out: unknown[] = [];
|
|
31
|
+
for (const entry of components) {
|
|
32
|
+
if (!entry || typeof entry !== "object") {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const component = entry as { component?: unknown; components?: unknown[] };
|
|
36
|
+
if (component.component && typeof component.component === "object") {
|
|
37
|
+
out.push(component.component);
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(component.components)) {
|
|
40
|
+
out.push(...flattenModalComponents(component.components));
|
|
41
|
+
}
|
|
42
|
+
out.push(entry);
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class ModalFields {
|
|
48
|
+
constructor(
|
|
49
|
+
private values: Record<string, string | string[]>,
|
|
50
|
+
private resolved?: ModalResolvedData,
|
|
51
|
+
private client?: StructureClient,
|
|
52
|
+
) {}
|
|
53
|
+
|
|
54
|
+
private value(id: string, required: boolean): string | string[] | undefined {
|
|
55
|
+
const value = this.values[id];
|
|
56
|
+
if (required && (value === undefined || (Array.isArray(value) && value.length === 0))) {
|
|
57
|
+
throw new Error(`Missing required modal field ${id}`);
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getText(id: string, required = false): string | null {
|
|
63
|
+
const value = this.value(id, required);
|
|
64
|
+
return typeof value === "string" ? value : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getStringSelect(id: string, required = false): string[] {
|
|
68
|
+
const value = this.value(id, required);
|
|
69
|
+
if (Array.isArray(value)) {
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
return typeof value === "string" ? [value] : [];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getRoleSelect(id: string, required = false): Role[] {
|
|
76
|
+
const values = this.getStringSelect(id, required);
|
|
77
|
+
return values.map((roleId) => {
|
|
78
|
+
const raw = this.resolved?.roles?.[roleId];
|
|
79
|
+
return raw
|
|
80
|
+
? new Role(this.client!, { id: roleId, name: raw.name ?? "" } as APIRole)
|
|
81
|
+
: new Role<true>(this.client!, roleId);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getUserSelect(id: string, required = false): User[] {
|
|
86
|
+
const values = this.getStringSelect(id, required);
|
|
87
|
+
return values.map((userId) => {
|
|
88
|
+
const raw = this.resolved?.users?.[userId];
|
|
89
|
+
return new User(this.client!, {
|
|
90
|
+
id: userId,
|
|
91
|
+
username: raw?.username ?? "",
|
|
92
|
+
} as APIUser);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { MessageFlags, type APIEmbed } from "discord-api-types/v10";
|
|
2
|
+
import { Embed } from "./embeds.js";
|
|
3
|
+
|
|
4
|
+
export type MessagePayloadFile = {
|
|
5
|
+
name: string;
|
|
6
|
+
data: Blob | Uint8Array | ArrayBuffer;
|
|
7
|
+
description?: string;
|
|
8
|
+
duration_secs?: number;
|
|
9
|
+
waveform?: string;
|
|
10
|
+
};
|
|
11
|
+
export type MessagePayloadObject = {
|
|
12
|
+
content?: string;
|
|
13
|
+
embeds?: Array<APIEmbed | Embed>;
|
|
14
|
+
components?: TopLevelComponents[];
|
|
15
|
+
allowedMentions?: unknown;
|
|
16
|
+
allowed_mentions?: unknown;
|
|
17
|
+
flags?: number;
|
|
18
|
+
tts?: boolean;
|
|
19
|
+
files?: MessagePayloadFile[];
|
|
20
|
+
poll?: unknown;
|
|
21
|
+
ephemeral?: boolean;
|
|
22
|
+
stickers?: [string, string, string] | [string, string] | [string];
|
|
23
|
+
};
|
|
24
|
+
export type MessagePayload = string | MessagePayloadObject;
|
|
25
|
+
export type TopLevelComponents = {
|
|
26
|
+
isV2?: boolean;
|
|
27
|
+
serialize: () => unknown;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function clean<T extends Record<string, unknown>>(value: T): T {
|
|
31
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)) as T;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function serializeAnyComponent(component: { serialize: () => unknown }): unknown {
|
|
35
|
+
return component.serialize();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function payloadHasV2Components(payload: MessagePayloadObject): boolean {
|
|
39
|
+
return Boolean(payload.components?.some((component) => component.isV2));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function normalizePayloadFlags(payload: MessagePayloadObject): number | undefined {
|
|
43
|
+
const flags = payload.ephemeral ? (payload.flags ?? 0) | MessageFlags.Ephemeral : payload.flags;
|
|
44
|
+
if (!payloadHasV2Components(payload)) {
|
|
45
|
+
return flags;
|
|
46
|
+
}
|
|
47
|
+
if (payload.content || payload.embeds?.length) {
|
|
48
|
+
throw new Error("Discord Components V2 payloads cannot include content or embeds");
|
|
49
|
+
}
|
|
50
|
+
return (flags ?? 0) | MessageFlags.IsComponentsV2;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function serializePayload(payload: MessagePayload) {
|
|
54
|
+
if (typeof payload === "string") {
|
|
55
|
+
return { content: payload };
|
|
56
|
+
}
|
|
57
|
+
const flags = normalizePayloadFlags(payload);
|
|
58
|
+
return clean({
|
|
59
|
+
content: payload.content,
|
|
60
|
+
embeds: payload.embeds?.map((entry) => ("serialize" in entry ? entry.serialize() : entry)),
|
|
61
|
+
components: payload.components?.map((entry) => serializeAnyComponent(entry)),
|
|
62
|
+
allowed_mentions: payload.allowed_mentions ?? payload.allowedMentions,
|
|
63
|
+
flags,
|
|
64
|
+
tts: payload.tts,
|
|
65
|
+
files: payload.files,
|
|
66
|
+
poll: payload.poll,
|
|
67
|
+
sticker_ids: payload.stickers,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
type RequestData = {
|
|
2
|
+
body?: unknown;
|
|
3
|
+
multipartStyle?: "message" | "form";
|
|
4
|
+
rawBody?: boolean;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function serializeRequestBody(
|
|
9
|
+
data: RequestData | undefined,
|
|
10
|
+
headers: Headers,
|
|
11
|
+
): BodyInit | undefined {
|
|
12
|
+
if (data?.headers) {
|
|
13
|
+
for (const [key, value] of Object.entries(data.headers)) {
|
|
14
|
+
headers.set(key, value);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (data?.body == null) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
if (typeof data.body === "object") {
|
|
21
|
+
const bodyObject = data.body as Record<string, unknown>;
|
|
22
|
+
const topLevelFiles = Array.isArray(bodyObject.files) ? bodyObject.files : undefined;
|
|
23
|
+
const nestedData =
|
|
24
|
+
bodyObject.data && typeof bodyObject.data === "object"
|
|
25
|
+
? (bodyObject.data as Record<string, unknown>)
|
|
26
|
+
: undefined;
|
|
27
|
+
const nestedFiles =
|
|
28
|
+
nestedData && Array.isArray(nestedData.files) ? nestedData.files : undefined;
|
|
29
|
+
const files = topLevelFiles ?? nestedFiles;
|
|
30
|
+
const filesContainer = topLevelFiles ? bodyObject : nestedFiles ? nestedData : undefined;
|
|
31
|
+
if (files?.length && filesContainer) {
|
|
32
|
+
if (data.multipartStyle === "form") {
|
|
33
|
+
const formData = new FormData();
|
|
34
|
+
for (const [key, value] of Object.entries(filesContainer)) {
|
|
35
|
+
if (key === "files" || value === undefined || value === null) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
formData.append(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
39
|
+
}
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
const item = file as {
|
|
42
|
+
fieldName?: unknown;
|
|
43
|
+
name?: unknown;
|
|
44
|
+
data?: unknown;
|
|
45
|
+
contentType?: unknown;
|
|
46
|
+
};
|
|
47
|
+
const name = typeof item.name === "string" && item.name ? item.name : "file";
|
|
48
|
+
const blob =
|
|
49
|
+
item.data instanceof Blob
|
|
50
|
+
? item.data
|
|
51
|
+
: new Blob([item.data as BlobPart], {
|
|
52
|
+
type: typeof item.contentType === "string" ? item.contentType : undefined,
|
|
53
|
+
});
|
|
54
|
+
formData.append(
|
|
55
|
+
typeof item.fieldName === "string" && item.fieldName ? item.fieldName : "file",
|
|
56
|
+
blob,
|
|
57
|
+
name,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return formData;
|
|
61
|
+
}
|
|
62
|
+
const payloadJson = topLevelFiles
|
|
63
|
+
? { ...bodyObject }
|
|
64
|
+
: { ...bodyObject, data: { ...nestedData } };
|
|
65
|
+
const payloadFilesContainer = topLevelFiles
|
|
66
|
+
? (payloadJson as Record<string, unknown>)
|
|
67
|
+
: ((payloadJson as { data: Record<string, unknown> }).data ?? {});
|
|
68
|
+
const formData = new FormData();
|
|
69
|
+
const existingAttachments = Array.isArray(payloadFilesContainer.attachments)
|
|
70
|
+
? [...payloadFilesContainer.attachments]
|
|
71
|
+
: [];
|
|
72
|
+
const uploaded = files.map((file, index) => {
|
|
73
|
+
const item = file as {
|
|
74
|
+
name?: unknown;
|
|
75
|
+
data?: unknown;
|
|
76
|
+
contentType?: unknown;
|
|
77
|
+
description?: unknown;
|
|
78
|
+
duration_secs?: unknown;
|
|
79
|
+
waveform?: unknown;
|
|
80
|
+
};
|
|
81
|
+
const name = typeof item.name === "string" && item.name ? item.name : `file-${index}`;
|
|
82
|
+
const blob =
|
|
83
|
+
item.data instanceof Blob
|
|
84
|
+
? item.data
|
|
85
|
+
: new Blob([item.data as BlobPart], {
|
|
86
|
+
type: typeof item.contentType === "string" ? item.contentType : undefined,
|
|
87
|
+
});
|
|
88
|
+
const id = existingAttachments.length + index;
|
|
89
|
+
formData.append(`files[${id}]`, blob, name);
|
|
90
|
+
const attachment: Record<string, unknown> = {
|
|
91
|
+
id,
|
|
92
|
+
filename: name,
|
|
93
|
+
};
|
|
94
|
+
if (typeof item.description === "string") {
|
|
95
|
+
attachment.description = item.description;
|
|
96
|
+
}
|
|
97
|
+
if (typeof item.duration_secs === "number") {
|
|
98
|
+
attachment.duration_secs = item.duration_secs;
|
|
99
|
+
}
|
|
100
|
+
if (typeof item.waveform === "string") {
|
|
101
|
+
attachment.waveform = item.waveform;
|
|
102
|
+
}
|
|
103
|
+
return attachment;
|
|
104
|
+
});
|
|
105
|
+
payloadFilesContainer.attachments = [...existingAttachments, ...uploaded];
|
|
106
|
+
delete payloadFilesContainer.files;
|
|
107
|
+
formData.append("payload_json", JSON.stringify(payloadJson));
|
|
108
|
+
return formData;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (!data.rawBody) {
|
|
112
|
+
headers.set("Content-Type", "application/json");
|
|
113
|
+
}
|
|
114
|
+
return data.rawBody ? (data.body as BodyInit) : JSON.stringify(data.body);
|
|
115
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export function readDiscordCode(body: unknown): number | undefined {
|
|
2
|
+
const value =
|
|
3
|
+
body && typeof body === "object" && "code" in body
|
|
4
|
+
? (body as { code?: unknown }).code
|
|
5
|
+
: undefined;
|
|
6
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
10
|
+
return Number(value);
|
|
11
|
+
}
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function readDiscordMessage(body: unknown, fallback: string): string {
|
|
16
|
+
const value =
|
|
17
|
+
body && typeof body === "object" && "message" in body
|
|
18
|
+
? (body as { message?: unknown }).message
|
|
19
|
+
: undefined;
|
|
20
|
+
return typeof value === "string" && value.trim() ? value : fallback;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readRetryAfterHeader(value: string | null, now = Date.now()): number | undefined {
|
|
24
|
+
if (!value) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const seconds = Number(value);
|
|
28
|
+
if (Number.isFinite(seconds)) {
|
|
29
|
+
return seconds;
|
|
30
|
+
}
|
|
31
|
+
const retryAt = Date.parse(value);
|
|
32
|
+
return Number.isFinite(retryAt) ? (retryAt - now) / 1000 : undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function coerceRetryAfterSeconds(value: unknown): number | undefined {
|
|
36
|
+
if (typeof value !== "number" && typeof value !== "string") {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const seconds = typeof value === "number" ? value : Number(value);
|
|
40
|
+
return Number.isFinite(seconds) && seconds >= 0 ? Math.max(0, seconds) : undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function readRetryAfter(body: unknown, response: Response, fallbackSeconds = 0): number {
|
|
44
|
+
const bodyValue =
|
|
45
|
+
body && typeof body === "object" && "retry_after" in body
|
|
46
|
+
? (body as { retry_after?: unknown }).retry_after
|
|
47
|
+
: undefined;
|
|
48
|
+
return (
|
|
49
|
+
coerceRetryAfterSeconds(bodyValue) ??
|
|
50
|
+
coerceRetryAfterSeconds(readRetryAfterHeader(response.headers.get("Retry-After"))) ??
|
|
51
|
+
fallbackSeconds
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class DiscordError extends Error {
|
|
56
|
+
readonly status: number;
|
|
57
|
+
readonly statusCode: number;
|
|
58
|
+
readonly rawBody: unknown;
|
|
59
|
+
readonly rawError: unknown;
|
|
60
|
+
discordCode?: number;
|
|
61
|
+
|
|
62
|
+
constructor(response: Response, body: unknown) {
|
|
63
|
+
super(readDiscordMessage(body, `Discord API request failed (${response.status})`));
|
|
64
|
+
this.name = "DiscordError";
|
|
65
|
+
this.status = response.status;
|
|
66
|
+
this.statusCode = response.status;
|
|
67
|
+
this.rawBody = body;
|
|
68
|
+
this.rawError = body;
|
|
69
|
+
this.discordCode = readDiscordCode(body);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class RateLimitError extends DiscordError {
|
|
74
|
+
readonly retryAfter: number;
|
|
75
|
+
readonly scope: string | null;
|
|
76
|
+
readonly bucket: string | null;
|
|
77
|
+
|
|
78
|
+
constructor(
|
|
79
|
+
response: Response,
|
|
80
|
+
body: { message: string; retry_after: number; global: boolean; code?: number | string },
|
|
81
|
+
) {
|
|
82
|
+
super(response, body);
|
|
83
|
+
this.name = "RateLimitError";
|
|
84
|
+
this.retryAfter = readRetryAfter(body, response, 1);
|
|
85
|
+
this.scope = body.global ? "global" : response.headers.get("X-RateLimit-Scope");
|
|
86
|
+
this.bucket = response.headers.get("X-RateLimit-Bucket");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
type QueryValue = string | number | boolean;
|
|
2
|
+
|
|
3
|
+
export function createRouteKey(method: string, path: string): string {
|
|
4
|
+
return `${method.toUpperCase()} ${path.split("?")[0] ?? path}`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function readTopLevelRouteKey(path: string): string {
|
|
8
|
+
const [pathname = path] = path.split("?");
|
|
9
|
+
const [first, id, token] = pathname.replace(/^\/+/, "").split("/");
|
|
10
|
+
if (!first || !id) {
|
|
11
|
+
return pathname;
|
|
12
|
+
}
|
|
13
|
+
if (first === "channels" || first === "guilds" || first === "webhooks") {
|
|
14
|
+
return first === "webhooks" && token ? `${first}/${id}/${token}` : `${first}/${id}`;
|
|
15
|
+
}
|
|
16
|
+
return first;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createBucketKey(bucket: string, path: string): string {
|
|
20
|
+
return `${bucket}:${readTopLevelRouteKey(path)}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function readHeaderNumber(headers: Headers, name: string): number | undefined {
|
|
24
|
+
const value = headers.get(name);
|
|
25
|
+
if (!value) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const parsed = Number(value);
|
|
29
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function readResetAt(response: Response): number | undefined {
|
|
33
|
+
const resetAfter = readHeaderNumber(response.headers, "X-RateLimit-Reset-After");
|
|
34
|
+
if (resetAfter !== undefined) {
|
|
35
|
+
return Date.now() + Math.max(0, resetAfter * 1000);
|
|
36
|
+
}
|
|
37
|
+
const reset = readHeaderNumber(response.headers, "X-RateLimit-Reset");
|
|
38
|
+
return reset !== undefined ? reset * 1000 : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function appendQuery(path: string, query?: Record<string, QueryValue>): string {
|
|
42
|
+
if (!query || Object.keys(query).length === 0) {
|
|
43
|
+
return path;
|
|
44
|
+
}
|
|
45
|
+
const search = new URLSearchParams();
|
|
46
|
+
for (const [key, value] of Object.entries(query)) {
|
|
47
|
+
search.set(key, String(value));
|
|
48
|
+
}
|
|
49
|
+
return `${path}?${search.toString()}`;
|
|
50
|
+
}
|