@kodelyth/discord 2026.5.42 → 2026.6.1
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/klaw.plugin.json +3822 -2
- package/package.json +16 -4
- package/account-inspect-api.ts +0 -6
- package/action-runtime-api.ts +0 -1
- package/api.ts +0 -130
- package/channel-config-api.ts +0 -1
- package/channel-plugin-api.ts +0 -3
- package/config-api.ts +0 -4
- package/configured-state.ts +0 -6
- package/contract-api.ts +0 -21
- package/directory-contract-api.ts +0 -4
- package/doctor-contract-api.ts +0 -1
- package/index.test.ts +0 -13
- package/index.ts +0 -24
- package/runtime-api.actions.ts +0 -15
- package/runtime-api.lookup.ts +0 -22
- package/runtime-api.monitor.ts +0 -50
- package/runtime-api.send.ts +0 -79
- package/runtime-api.threads.ts +0 -31
- package/runtime-api.ts +0 -181
- package/runtime-setter-api.ts +0 -3
- package/secret-contract-api.ts +0 -4
- package/security-audit-contract-api.ts +0 -1
- package/security-contract-api.ts +0 -4
- package/session-key-api.ts +0 -1
- package/setup-entry.ts +0 -9
- package/setup-plugin-api.ts +0 -3
- package/src/account-inspect.test.ts +0 -126
- package/src/account-inspect.ts +0 -128
- package/src/accounts.test.ts +0 -381
- package/src/accounts.ts +0 -205
- package/src/actions/handle-action.guild-admin.ts +0 -421
- package/src/actions/handle-action.test.ts +0 -480
- package/src/actions/handle-action.ts +0 -402
- package/src/actions/runtime.guild.ts +0 -446
- package/src/actions/runtime.messaging.messages.ts +0 -226
- package/src/actions/runtime.messaging.reactions.ts +0 -67
- package/src/actions/runtime.messaging.runtime.ts +0 -73
- package/src/actions/runtime.messaging.send.ts +0 -336
- package/src/actions/runtime.messaging.shared.ts +0 -97
- package/src/actions/runtime.messaging.ts +0 -37
- package/src/actions/runtime.moderation-shared.ts +0 -48
- package/src/actions/runtime.moderation.authz.test.ts +0 -151
- package/src/actions/runtime.moderation.ts +0 -116
- package/src/actions/runtime.presence.test.ts +0 -165
- package/src/actions/runtime.presence.ts +0 -117
- package/src/actions/runtime.shared.ts +0 -86
- package/src/actions/runtime.test.ts +0 -1337
- package/src/actions/runtime.ts +0 -87
- package/src/api-barrel.test.ts +0 -78
- package/src/api.test.ts +0 -152
- package/src/api.ts +0 -215
- package/src/approval-handler.runtime.test.ts +0 -41
- package/src/approval-handler.runtime.ts +0 -633
- package/src/approval-native.test.ts +0 -330
- package/src/approval-native.ts +0 -219
- package/src/approval-runtime.ts +0 -14
- package/src/approval-shared.ts +0 -50
- package/src/audit-core.ts +0 -178
- package/src/audit.test.ts +0 -204
- package/src/audit.ts +0 -32
- package/src/channel-actions.contract.test.ts +0 -45
- package/src/channel-actions.runtime.ts +0 -1
- package/src/channel-actions.test.ts +0 -504
- package/src/channel-actions.ts +0 -254
- package/src/channel-api.ts +0 -29
- package/src/channel.conversation.ts +0 -159
- package/src/channel.loaders.ts +0 -50
- package/src/channel.message-adapter.test.ts +0 -230
- package/src/channel.runtime.ts +0 -1
- package/src/channel.setup.ts +0 -12
- package/src/channel.test.ts +0 -828
- package/src/channel.ts +0 -728
- package/src/chunk.test.ts +0 -170
- package/src/chunk.ts +0 -321
- package/src/client.proxy.test.ts +0 -177
- package/src/client.test.ts +0 -83
- package/src/client.ts +0 -143
- package/src/component-custom-id.ts +0 -72
- package/src/components-registry.ts +0 -356
- package/src/components.builders.ts +0 -409
- package/src/components.modal.ts +0 -124
- package/src/components.parse.ts +0 -407
- package/src/components.test.ts +0 -345
- package/src/components.ts +0 -54
- package/src/components.types.ts +0 -187
- package/src/config-schema.test.ts +0 -439
- package/src/config-schema.ts +0 -6
- package/src/config-ui-hints.ts +0 -354
- package/src/conversation-identity.ts +0 -58
- package/src/delivery-retry.ts +0 -52
- package/src/directory-cache.ts +0 -116
- package/src/directory-config.ts +0 -58
- package/src/directory-contract.test.ts +0 -129
- package/src/directory-live.test.ts +0 -141
- package/src/directory-live.ts +0 -135
- package/src/doctor-contract.ts +0 -477
- package/src/doctor-shared.ts +0 -5
- package/src/doctor.test.ts +0 -393
- package/src/doctor.ts +0 -340
- package/src/draft-chunking.test.ts +0 -64
- package/src/draft-chunking.ts +0 -43
- package/src/draft-stream.test.ts +0 -193
- package/src/draft-stream.ts +0 -162
- package/src/durable-delivery.test.ts +0 -103
- package/src/error-body.ts +0 -38
- package/src/exec-approvals.test.ts +0 -88
- package/src/exec-approvals.ts +0 -110
- package/src/gateway-logging.test.ts +0 -98
- package/src/gateway-logging.ts +0 -67
- package/src/group-policy.ts +0 -113
- package/src/guilds.ts +0 -29
- package/src/inbound-context.contract.test.ts +0 -11
- package/src/inbound-event-delivery.ts +0 -135
- package/src/interactive-dispatch.ts +0 -104
- package/src/internal/api.commands.ts +0 -51
- package/src/internal/api.guild.ts +0 -164
- package/src/internal/api.interactions.ts +0 -53
- package/src/internal/api.messages.ts +0 -113
- package/src/internal/api.reactions.ts +0 -38
- package/src/internal/api.test.ts +0 -260
- package/src/internal/api.ts +0 -61
- package/src/internal/api.users.ts +0 -19
- package/src/internal/api.webhooks.ts +0 -13
- package/src/internal/client.test.ts +0 -472
- package/src/internal/client.ts +0 -310
- package/src/internal/command-deploy.test.ts +0 -197
- package/src/internal/command-deploy.ts +0 -352
- package/src/internal/commands.ts +0 -188
- package/src/internal/components.base.ts +0 -65
- package/src/internal/components.message.ts +0 -279
- package/src/internal/components.modal.ts +0 -95
- package/src/internal/components.ts +0 -31
- package/src/internal/discord.ts +0 -11
- package/src/internal/embeds.ts +0 -35
- package/src/internal/entity-cache.ts +0 -98
- package/src/internal/event-queue.ts +0 -185
- package/src/internal/gateway-close-codes.ts +0 -25
- package/src/internal/gateway-dispatch.ts +0 -96
- package/src/internal/gateway-identify-limiter.ts +0 -26
- package/src/internal/gateway-lifecycle.test.ts +0 -114
- package/src/internal/gateway-lifecycle.ts +0 -75
- package/src/internal/gateway-rate-limit.ts +0 -104
- package/src/internal/gateway.test.ts +0 -676
- package/src/internal/gateway.ts +0 -479
- package/src/internal/interaction-dispatch.test.ts +0 -148
- package/src/internal/interaction-dispatch.ts +0 -162
- package/src/internal/interaction-options.ts +0 -98
- package/src/internal/interaction-response.ts +0 -53
- package/src/internal/interactions.test.ts +0 -329
- package/src/internal/interactions.ts +0 -378
- package/src/internal/listeners.ts +0 -91
- package/src/internal/live-smoke.live.test.ts +0 -26
- package/src/internal/modal-fields.ts +0 -95
- package/src/internal/payload.ts +0 -69
- package/src/internal/rest-body.ts +0 -115
- package/src/internal/rest-errors.ts +0 -88
- package/src/internal/rest-routes.ts +0 -50
- package/src/internal/rest-scheduler.ts +0 -557
- package/src/internal/rest.test.ts +0 -681
- package/src/internal/rest.ts +0 -322
- package/src/internal/schemas.ts +0 -36
- package/src/internal/structures.test.ts +0 -43
- package/src/internal/structures.ts +0 -280
- package/src/internal/test-builders.test-support.ts +0 -167
- package/src/internal/voice.ts +0 -49
- package/src/media-detection.ts +0 -28
- package/src/mentions.test.ts +0 -111
- package/src/mentions.ts +0 -147
- package/src/monitor/ack-reactions.ts +0 -70
- package/src/monitor/acp-bind-here.integration.test.ts +0 -219
- package/src/monitor/agent-components-auth.ts +0 -7
- package/src/monitor/agent-components-context.ts +0 -154
- package/src/monitor/agent-components-data.ts +0 -224
- package/src/monitor/agent-components-dm-auth.ts +0 -177
- package/src/monitor/agent-components-guild-auth.ts +0 -322
- package/src/monitor/agent-components-helpers.runtime.ts +0 -3
- package/src/monitor/agent-components-helpers.ts +0 -34
- package/src/monitor/agent-components-reply.ts +0 -10
- package/src/monitor/agent-components.deps.runtime.ts +0 -2
- package/src/monitor/agent-components.dispatch.ts +0 -359
- package/src/monitor/agent-components.handlers.ts +0 -303
- package/src/monitor/agent-components.modal.ts +0 -160
- package/src/monitor/agent-components.plugin-interactive.ts +0 -187
- package/src/monitor/agent-components.runtime.ts +0 -14
- package/src/monitor/agent-components.system-controls.ts +0 -215
- package/src/monitor/agent-components.ts +0 -70
- package/src/monitor/agent-components.types.ts +0 -58
- package/src/monitor/agent-components.wildcard-controls.ts +0 -171
- package/src/monitor/agent-components.wildcard.test.ts +0 -71
- package/src/monitor/allow-list.test.ts +0 -14
- package/src/monitor/allow-list.ts +0 -631
- package/src/monitor/auto-presence.test.ts +0 -184
- package/src/monitor/auto-presence.ts +0 -356
- package/src/monitor/channel-access.test.ts +0 -113
- package/src/monitor/channel-access.ts +0 -102
- package/src/monitor/commands.test.ts +0 -24
- package/src/monitor/commands.ts +0 -9
- package/src/monitor/dm-command-auth.test.ts +0 -274
- package/src/monitor/dm-command-auth.ts +0 -259
- package/src/monitor/dm-command-decision.test.ts +0 -108
- package/src/monitor/dm-command-decision.ts +0 -49
- package/src/monitor/exec-approvals.test.ts +0 -225
- package/src/monitor/exec-approvals.ts +0 -158
- package/src/monitor/format.ts +0 -45
- package/src/monitor/gateway-handle.ts +0 -33
- package/src/monitor/gateway-metadata.test.ts +0 -29
- package/src/monitor/gateway-metadata.ts +0 -298
- package/src/monitor/gateway-plugin.test.ts +0 -320
- package/src/monitor/gateway-plugin.ts +0 -302
- package/src/monitor/gateway-registry.ts +0 -37
- package/src/monitor/gateway-supervisor.test.ts +0 -157
- package/src/monitor/gateway-supervisor.ts +0 -206
- package/src/monitor/inbound-context.test-helpers.ts +0 -37
- package/src/monitor/inbound-context.test.ts +0 -112
- package/src/monitor/inbound-context.ts +0 -95
- package/src/monitor/inbound-dedupe.ts +0 -79
- package/src/monitor/inbound-job.test.ts +0 -216
- package/src/monitor/inbound-job.ts +0 -118
- package/src/monitor/listeners.queue.ts +0 -91
- package/src/monitor/listeners.reactions.ts +0 -594
- package/src/monitor/listeners.test.ts +0 -209
- package/src/monitor/listeners.ts +0 -150
- package/src/monitor/message-channel-info.ts +0 -96
- package/src/monitor/message-forwarded.ts +0 -114
- package/src/monitor/message-handler.batch-gate.test.ts +0 -22
- package/src/monitor/message-handler.batch-gate.ts +0 -19
- package/src/monitor/message-handler.bot-self-filter.test.ts +0 -68
- package/src/monitor/message-handler.context.ts +0 -492
- package/src/monitor/message-handler.dm-preflight.ts +0 -119
- package/src/monitor/message-handler.draft-preview.ts +0 -426
- package/src/monitor/message-handler.hydration.test.ts +0 -80
- package/src/monitor/message-handler.hydration.ts +0 -198
- package/src/monitor/message-handler.inbound-context.test.ts +0 -61
- package/src/monitor/message-handler.module-test-helpers.ts +0 -31
- package/src/monitor/message-handler.preflight-channel-access.ts +0 -86
- package/src/monitor/message-handler.preflight-channel-context.test.ts +0 -18
- package/src/monitor/message-handler.preflight-channel-context.ts +0 -58
- package/src/monitor/message-handler.preflight-context.ts +0 -54
- package/src/monitor/message-handler.preflight-helpers.ts +0 -164
- package/src/monitor/message-handler.preflight-history.ts +0 -23
- package/src/monitor/message-handler.preflight-logging.ts +0 -36
- package/src/monitor/message-handler.preflight-pluralkit.ts +0 -26
- package/src/monitor/message-handler.preflight-runtime.ts +0 -28
- package/src/monitor/message-handler.preflight-thread.ts +0 -49
- package/src/monitor/message-handler.preflight.acp-bindings.test.ts +0 -371
- package/src/monitor/message-handler.preflight.test-helpers.ts +0 -114
- package/src/monitor/message-handler.preflight.test.ts +0 -2255
- package/src/monitor/message-handler.preflight.ts +0 -822
- package/src/monitor/message-handler.preflight.types.ts +0 -115
- package/src/monitor/message-handler.process.test.ts +0 -2520
- package/src/monitor/message-handler.process.ts +0 -1027
- package/src/monitor/message-handler.queue.test.ts +0 -680
- package/src/monitor/message-handler.routing-preflight.ts +0 -112
- package/src/monitor/message-handler.test-harness.ts +0 -99
- package/src/monitor/message-handler.test-helpers.ts +0 -75
- package/src/monitor/message-handler.ts +0 -309
- package/src/monitor/message-media.ts +0 -536
- package/src/monitor/message-run-queue.ts +0 -101
- package/src/monitor/message-text.ts +0 -171
- package/src/monitor/message-utils.test.ts +0 -1234
- package/src/monitor/message-utils.ts +0 -34
- package/src/monitor/model-picker-preferences.test.ts +0 -67
- package/src/monitor/model-picker-preferences.ts +0 -184
- package/src/monitor/model-picker.state.ts +0 -364
- package/src/monitor/model-picker.test-utils.ts +0 -26
- package/src/monitor/model-picker.test.ts +0 -869
- package/src/monitor/model-picker.ts +0 -38
- package/src/monitor/model-picker.view.ts +0 -722
- package/src/monitor/monitor.agent-components.test.ts +0 -410
- package/src/monitor/monitor.test.ts +0 -919
- package/src/monitor/monitor.threading-utils.test.ts +0 -614
- package/src/monitor/native-command-agent-reply.ts +0 -125
- package/src/monitor/native-command-arg-ui.ts +0 -233
- package/src/monitor/native-command-auth.ts +0 -309
- package/src/monitor/native-command-bypass.ts +0 -13
- package/src/monitor/native-command-context.test.ts +0 -105
- package/src/monitor/native-command-context.ts +0 -109
- package/src/monitor/native-command-dispatch.ts +0 -35
- package/src/monitor/native-command-model-picker-apply.ts +0 -209
- package/src/monitor/native-command-model-picker-interaction.ts +0 -516
- package/src/monitor/native-command-model-picker-ui.ts +0 -357
- package/src/monitor/native-command-reply.test.ts +0 -68
- package/src/monitor/native-command-reply.ts +0 -185
- package/src/monitor/native-command-route.ts +0 -91
- package/src/monitor/native-command-status.ts +0 -76
- package/src/monitor/native-command-ui.ts +0 -26
- package/src/monitor/native-command-ui.types.ts +0 -20
- package/src/monitor/native-command.args.ts +0 -45
- package/src/monitor/native-command.command-arg.test.ts +0 -108
- package/src/monitor/native-command.commands-allowfrom.test.ts +0 -504
- package/src/monitor/native-command.model-picker.test.ts +0 -930
- package/src/monitor/native-command.options.test.ts +0 -379
- package/src/monitor/native-command.options.ts +0 -153
- package/src/monitor/native-command.plugin-dispatch.test.ts +0 -1212
- package/src/monitor/native-command.runtime.ts +0 -51
- package/src/monitor/native-command.status-direct.test.ts +0 -278
- package/src/monitor/native-command.test-helpers.ts +0 -64
- package/src/monitor/native-command.think-autocomplete.test.ts +0 -411
- package/src/monitor/native-command.ts +0 -747
- package/src/monitor/native-command.types.ts +0 -9
- package/src/monitor/native-interaction-channel-context.ts +0 -50
- package/src/monitor/preflight-audio.runtime.ts +0 -9
- package/src/monitor/preflight-audio.test.ts +0 -157
- package/src/monitor/preflight-audio.ts +0 -130
- package/src/monitor/presence-cache.ts +0 -61
- package/src/monitor/presence.test.ts +0 -61
- package/src/monitor/presence.ts +0 -50
- package/src/monitor/provider-session.runtime.ts +0 -12
- package/src/monitor/provider.acp.ts +0 -89
- package/src/monitor/provider.allowlist.test.ts +0 -217
- package/src/monitor/provider.allowlist.ts +0 -398
- package/src/monitor/provider.cleanup.ts +0 -41
- package/src/monitor/provider.commands.ts +0 -129
- package/src/monitor/provider.config-log.ts +0 -45
- package/src/monitor/provider.deploy-errors.ts +0 -362
- package/src/monitor/provider.deploy.ts +0 -221
- package/src/monitor/provider.interactions.ts +0 -160
- package/src/monitor/provider.lifecycle.test.ts +0 -734
- package/src/monitor/provider.lifecycle.ts +0 -562
- package/src/monitor/provider.proxy.test.ts +0 -804
- package/src/monitor/provider.rest-proxy.test.ts +0 -389
- package/src/monitor/provider.runtime.ts +0 -1
- package/src/monitor/provider.skill-dedupe.test.ts +0 -42
- package/src/monitor/provider.startup-log.ts +0 -32
- package/src/monitor/provider.startup.test.ts +0 -440
- package/src/monitor/provider.startup.ts +0 -323
- package/src/monitor/provider.test.ts +0 -1173
- package/src/monitor/provider.ts +0 -688
- package/src/monitor/reply-context.ts +0 -64
- package/src/monitor/reply-delivery.test.ts +0 -474
- package/src/monitor/reply-delivery.ts +0 -212
- package/src/monitor/reply-safety.ts +0 -96
- package/src/monitor/rest-fetch.ts +0 -94
- package/src/monitor/route-resolution.test.ts +0 -209
- package/src/monitor/route-resolution.ts +0 -140
- package/src/monitor/sender-identity.ts +0 -81
- package/src/monitor/startup-status.test.ts +0 -30
- package/src/monitor/startup-status.ts +0 -10
- package/src/monitor/status.ts +0 -22
- package/src/monitor/system-events.ts +0 -55
- package/src/monitor/thread-bindings.config.ts +0 -35
- package/src/monitor/thread-bindings.discord-api.test.ts +0 -250
- package/src/monitor/thread-bindings.discord-api.ts +0 -310
- package/src/monitor/thread-bindings.lifecycle.test.ts +0 -1994
- package/src/monitor/thread-bindings.lifecycle.ts +0 -354
- package/src/monitor/thread-bindings.manager.ts +0 -551
- package/src/monitor/thread-bindings.messages.ts +0 -6
- package/src/monitor/thread-bindings.persona.test.ts +0 -34
- package/src/monitor/thread-bindings.persona.ts +0 -25
- package/src/monitor/thread-bindings.session-adapter.ts +0 -229
- package/src/monitor/thread-bindings.session-shared.ts +0 -59
- package/src/monitor/thread-bindings.session-updates.ts +0 -35
- package/src/monitor/thread-bindings.shared-state.test.ts +0 -39
- package/src/monitor/thread-bindings.state.ts +0 -540
- package/src/monitor/thread-bindings.ts +0 -48
- package/src/monitor/thread-bindings.types.ts +0 -83
- package/src/monitor/thread-channel-context.ts +0 -112
- package/src/monitor/thread-session-close.test.ts +0 -180
- package/src/monitor/thread-session-close.ts +0 -63
- package/src/monitor/thread-title.generate.test.ts +0 -209
- package/src/monitor/thread-title.test.ts +0 -31
- package/src/monitor/thread-title.ts +0 -181
- package/src/monitor/threading.auto-thread.test.ts +0 -330
- package/src/monitor/threading.auto-thread.ts +0 -287
- package/src/monitor/threading.cache.ts +0 -45
- package/src/monitor/threading.parent-info.test.ts +0 -156
- package/src/monitor/threading.starter.test.ts +0 -279
- package/src/monitor/threading.starter.ts +0 -288
- package/src/monitor/threading.ts +0 -20
- package/src/monitor/threading.types.ts +0 -102
- package/src/monitor/timeouts.ts +0 -84
- package/src/monitor/typing.test.ts +0 -42
- package/src/monitor/typing.ts +0 -17
- package/src/monitor.gateway.test.ts +0 -187
- package/src/monitor.gateway.ts +0 -75
- package/src/monitor.test.ts +0 -1416
- package/src/monitor.ts +0 -28
- package/src/network-config.test.ts +0 -92
- package/src/network-config.ts +0 -79
- package/src/normalize.test.ts +0 -56
- package/src/normalize.ts +0 -86
- package/src/outbound-adapter.interactive-order.test.ts +0 -82
- package/src/outbound-adapter.test-harness.ts +0 -207
- package/src/outbound-adapter.test.ts +0 -804
- package/src/outbound-adapter.ts +0 -326
- package/src/outbound-approval.ts +0 -29
- package/src/outbound-components.ts +0 -86
- package/src/outbound-payload.contract.test.ts +0 -49
- package/src/outbound-payload.ts +0 -208
- package/src/outbound-send-context.ts +0 -89
- package/src/outbound-session-route.test.ts +0 -42
- package/src/outbound-session-route.ts +0 -72
- package/src/pluralkit.test.ts +0 -67
- package/src/pluralkit.ts +0 -58
- package/src/preview-streaming.ts +0 -18
- package/src/probe.intents.test.ts +0 -94
- package/src/probe.parse-token.test.ts +0 -43
- package/src/probe.runtime.ts +0 -1
- package/src/probe.ts +0 -237
- package/src/proxy-fetch.ts +0 -92
- package/src/proxy-request-client.test.ts +0 -100
- package/src/proxy-request-client.ts +0 -21
- package/src/recipient-resolution.ts +0 -39
- package/src/resolve-allowlist-common.test.ts +0 -40
- package/src/resolve-allowlist-common.ts +0 -39
- package/src/resolve-channels.test.ts +0 -341
- package/src/resolve-channels.ts +0 -369
- package/src/resolve-users.test.ts +0 -243
- package/src/resolve-users.ts +0 -184
- package/src/retry.test.ts +0 -83
- package/src/retry.ts +0 -98
- package/src/runtime-api.ts +0 -61
- package/src/runtime-config.ts +0 -16
- package/src/runtime.ts +0 -23
- package/src/secret-config-contract.ts +0 -140
- package/src/security-audit.runtime.ts +0 -1
- package/src/security-audit.test.ts +0 -245
- package/src/security-audit.ts +0 -208
- package/src/security-contract.ts +0 -47
- package/src/security-doctor.test.ts +0 -25
- package/src/security-doctor.ts +0 -20
- package/src/security.ts +0 -60
- package/src/send-target-parsing.ts +0 -14
- package/src/send.channels.ts +0 -139
- package/src/send.components.test.ts +0 -330
- package/src/send.components.ts +0 -391
- package/src/send.creates-thread.test.ts +0 -681
- package/src/send.emojis-stickers.ts +0 -57
- package/src/send.guild.ts +0 -170
- package/src/send.message-request.ts +0 -112
- package/src/send.messages.test.ts +0 -59
- package/src/send.messages.ts +0 -229
- package/src/send.outbound.ts +0 -459
- package/src/send.permissions.authz.test.ts +0 -190
- package/src/send.permissions.ts +0 -283
- package/src/send.reactions.ts +0 -155
- package/src/send.receipt.ts +0 -69
- package/src/send.sends-basic-channel-messages.test.ts +0 -1068
- package/src/send.shared.ts +0 -469
- package/src/send.test-harness.ts +0 -56
- package/src/send.ts +0 -82
- package/src/send.types.ts +0 -191
- package/src/send.typing.test.ts +0 -41
- package/src/send.typing.ts +0 -9
- package/src/send.voice.ts +0 -136
- package/src/send.webhook-activity.test.ts +0 -152
- package/src/send.webhook.proxy.test.ts +0 -210
- package/src/send.webhook.ts +0 -137
- package/src/session-contract.ts +0 -3
- package/src/session-key-normalization.test.ts +0 -44
- package/src/session-key-normalization.ts +0 -47
- package/src/setup-account-state.test.ts +0 -113
- package/src/setup-account-state.ts +0 -141
- package/src/setup-adapter.ts +0 -14
- package/src/setup-core.ts +0 -215
- package/src/setup-runtime-helpers.ts +0 -10
- package/src/setup-surface.test.ts +0 -137
- package/src/setup-surface.ts +0 -132
- package/src/shared-interactive.test.ts +0 -153
- package/src/shared-interactive.ts +0 -161
- package/src/shared.test.ts +0 -186
- package/src/shared.ts +0 -197
- package/src/status-issues.test.ts +0 -97
- package/src/status-issues.ts +0 -198
- package/src/subagent-hooks.test.ts +0 -465
- package/src/subagent-hooks.ts +0 -232
- package/src/target-parsing.ts +0 -70
- package/src/target-resolver.ts +0 -129
- package/src/targets.test.ts +0 -393
- package/src/targets.ts +0 -12
- package/src/test-http-helpers.ts +0 -10
- package/src/test-support/component-runtime.ts +0 -194
- package/src/test-support/config.ts +0 -7
- package/src/test-support/configured-binding-runtime.ts +0 -29
- package/src/test-support/partial-channel.ts +0 -26
- package/src/test-support/provider.test-support.ts +0 -547
- package/src/token.test.ts +0 -174
- package/src/token.ts +0 -107
- package/src/ui-colors.ts +0 -27
- package/src/ui.ts +0 -20
- package/src/voice/access.test.ts +0 -288
- package/src/voice/access.ts +0 -126
- package/src/voice/audio.test.ts +0 -47
- package/src/voice/audio.ts +0 -249
- package/src/voice/capture-state.test.ts +0 -48
- package/src/voice/capture-state.ts +0 -120
- package/src/voice/command.test.ts +0 -170
- package/src/voice/command.ts +0 -284
- package/src/voice/config.ts +0 -8
- package/src/voice/ingress.ts +0 -164
- package/src/voice/manager.e2e.test.ts +0 -3286
- package/src/voice/manager.ready-listener.test.ts +0 -54
- package/src/voice/manager.runtime.ts +0 -14
- package/src/voice/manager.ts +0 -1155
- package/src/voice/prompt.test.ts +0 -30
- package/src/voice/prompt.ts +0 -22
- package/src/voice/realtime.ts +0 -1370
- package/src/voice/receive-recovery.test.ts +0 -81
- package/src/voice/receive-recovery.ts +0 -159
- package/src/voice/sanitize.test.ts +0 -34
- package/src/voice/sanitize.ts +0 -29
- package/src/voice/sdk-runtime.ts +0 -14
- package/src/voice/segment.ts +0 -160
- package/src/voice/session.ts +0 -81
- package/src/voice/speaker-context.ts +0 -127
- package/src/voice/tts.ts +0 -151
- package/src/voice-message.test.ts +0 -376
- package/src/voice-message.ts +0 -474
- package/subagent-hooks-api.ts +0 -27
- package/test-api.ts +0 -4
- package/thread-binding-api.ts +0 -1
- package/timeouts.ts +0 -6
- package/tsconfig.json +0 -16
|
@@ -1,681 +0,0 @@
|
|
|
1
|
-
import { ChannelType, MessageFlags, Routes } from "discord-api-types/v10";
|
|
2
|
-
import { loadWebMediaRaw } from "klaw/plugin-sdk/web-media";
|
|
3
|
-
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
-
import { RateLimitError } from "./internal/discord.js";
|
|
5
|
-
import { makeDiscordRest } from "./send.test-harness.js";
|
|
6
|
-
|
|
7
|
-
vi.mock("klaw/plugin-sdk/web-media", async () => {
|
|
8
|
-
const { discordWebMediaMockFactory } = await import("./send.test-harness.js");
|
|
9
|
-
return discordWebMediaMockFactory();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
let addRoleDiscord: typeof import("./send.js").addRoleDiscord;
|
|
13
|
-
let banMemberDiscord: typeof import("./send.js").banMemberDiscord;
|
|
14
|
-
let createThreadDiscord: typeof import("./send.js").createThreadDiscord;
|
|
15
|
-
let DiscordThreadInitialMessageError: typeof import("./send.js").DiscordThreadInitialMessageError;
|
|
16
|
-
let listGuildEmojisDiscord: typeof import("./send.js").listGuildEmojisDiscord;
|
|
17
|
-
let listThreadsDiscord: typeof import("./send.js").listThreadsDiscord;
|
|
18
|
-
let reactMessageDiscord: typeof import("./send.js").reactMessageDiscord;
|
|
19
|
-
let removeRoleDiscord: typeof import("./send.js").removeRoleDiscord;
|
|
20
|
-
let sendMessageDiscord: typeof import("./send.js").sendMessageDiscord;
|
|
21
|
-
let sendPollDiscord: typeof import("./send.js").sendPollDiscord;
|
|
22
|
-
let sendStickerDiscord: typeof import("./send.js").sendStickerDiscord;
|
|
23
|
-
let timeoutMemberDiscord: typeof import("./send.js").timeoutMemberDiscord;
|
|
24
|
-
let uploadEmojiDiscord: typeof import("./send.js").uploadEmojiDiscord;
|
|
25
|
-
let uploadStickerDiscord: typeof import("./send.js").uploadStickerDiscord;
|
|
26
|
-
|
|
27
|
-
const DISCORD_TEST_CFG = {
|
|
28
|
-
channels: {
|
|
29
|
-
discord: {
|
|
30
|
-
accounts: {
|
|
31
|
-
default: {},
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
function discordClientOpts(rest: ReturnType<typeof makeDiscordRest>["rest"]) {
|
|
38
|
-
return { cfg: DISCORD_TEST_CFG, rest, token: "t" };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
type MockCallSource = {
|
|
42
|
-
mock: {
|
|
43
|
-
calls: ArrayLike<ReadonlyArray<unknown>>;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
function requireRecord(value: unknown, label: string): Record<string, unknown> {
|
|
48
|
-
if (!value || typeof value !== "object") {
|
|
49
|
-
throw new Error(`expected ${label}`);
|
|
50
|
-
}
|
|
51
|
-
return value as Record<string, unknown>;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function mockArg(source: MockCallSource, callIndex: number, argIndex: number, label: string) {
|
|
55
|
-
const call = source.mock.calls[callIndex];
|
|
56
|
-
if (!call) {
|
|
57
|
-
throw new Error(`expected mock call: ${label}`);
|
|
58
|
-
}
|
|
59
|
-
return call[argIndex];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function requestOptions(source: MockCallSource, callIndex = 0) {
|
|
63
|
-
return requireRecord(
|
|
64
|
-
mockArg(source, callIndex, 1, `request options ${callIndex}`),
|
|
65
|
-
"request options",
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function requestPath(source: MockCallSource, callIndex = 0) {
|
|
70
|
-
return mockArg(source, callIndex, 0, `request path ${callIndex}`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function requestBody(source: MockCallSource, callIndex = 0) {
|
|
74
|
-
return requireRecord(requestOptions(source, callIndex).body, `request body ${callIndex}`);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function timerDelayAt(source: MockCallSource, callIndex = 0) {
|
|
78
|
-
return mockArg(source, callIndex, 1, `timer delay ${callIndex}`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function createRateLimitError(
|
|
82
|
-
response: Response,
|
|
83
|
-
body: { message: string; retry_after: number; global: boolean },
|
|
84
|
-
request?: Request,
|
|
85
|
-
): RateLimitError {
|
|
86
|
-
const fallbackRequest =
|
|
87
|
-
request ??
|
|
88
|
-
new Request("https://discord.com/api/v10/channels/789/messages", {
|
|
89
|
-
method: "POST",
|
|
90
|
-
});
|
|
91
|
-
const RateLimitErrorCtor = RateLimitError as unknown as new (
|
|
92
|
-
response: Response,
|
|
93
|
-
body: { message: string; retry_after: number; global: boolean },
|
|
94
|
-
request?: Request,
|
|
95
|
-
) => RateLimitError;
|
|
96
|
-
return new RateLimitErrorCtor(response, body, fallbackRequest);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
beforeAll(async () => {
|
|
100
|
-
({
|
|
101
|
-
addRoleDiscord,
|
|
102
|
-
banMemberDiscord,
|
|
103
|
-
createThreadDiscord,
|
|
104
|
-
DiscordThreadInitialMessageError,
|
|
105
|
-
listGuildEmojisDiscord,
|
|
106
|
-
listThreadsDiscord,
|
|
107
|
-
reactMessageDiscord,
|
|
108
|
-
removeRoleDiscord,
|
|
109
|
-
sendMessageDiscord,
|
|
110
|
-
sendPollDiscord,
|
|
111
|
-
sendStickerDiscord,
|
|
112
|
-
timeoutMemberDiscord,
|
|
113
|
-
uploadEmojiDiscord,
|
|
114
|
-
uploadStickerDiscord,
|
|
115
|
-
} = await import("./send.js"));
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
beforeEach(() => {
|
|
119
|
-
vi.clearAllMocks();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
afterEach(() => {
|
|
123
|
-
vi.useRealTimers();
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
afterAll(() => {
|
|
127
|
-
vi.doUnmock("klaw/plugin-sdk/web-media");
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
describe("sendMessageDiscord", () => {
|
|
131
|
-
it("creates a thread", async () => {
|
|
132
|
-
const { rest, getMock, postMock } = makeDiscordRest();
|
|
133
|
-
postMock.mockResolvedValue({ id: "t1" });
|
|
134
|
-
await createThreadDiscord(
|
|
135
|
-
"chan1",
|
|
136
|
-
{ name: "thread", messageId: "m1" },
|
|
137
|
-
discordClientOpts(rest),
|
|
138
|
-
);
|
|
139
|
-
expect(getMock).not.toHaveBeenCalled();
|
|
140
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.threads("chan1", "m1"));
|
|
141
|
-
expect(requestBody(postMock as unknown as MockCallSource)).toEqual({ name: "thread" });
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("creates forum threads with an initial message", async () => {
|
|
145
|
-
const { rest, getMock, postMock } = makeDiscordRest();
|
|
146
|
-
getMock.mockResolvedValue({ type: ChannelType.GuildForum });
|
|
147
|
-
postMock.mockResolvedValue({ id: "t1" });
|
|
148
|
-
await createThreadDiscord("chan1", { name: "thread" }, discordClientOpts(rest));
|
|
149
|
-
expect(getMock).toHaveBeenCalledWith(Routes.channel("chan1"));
|
|
150
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.threads("chan1"));
|
|
151
|
-
expect(requestBody(postMock as unknown as MockCallSource)).toEqual({
|
|
152
|
-
name: "thread",
|
|
153
|
-
message: { content: "thread" },
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("creates media threads with provided content", async () => {
|
|
158
|
-
const { rest, getMock, postMock } = makeDiscordRest();
|
|
159
|
-
getMock.mockResolvedValue({ type: ChannelType.GuildMedia });
|
|
160
|
-
postMock.mockResolvedValue({ id: "t1" });
|
|
161
|
-
await createThreadDiscord(
|
|
162
|
-
"chan1",
|
|
163
|
-
{ name: "thread", content: "initial forum post" },
|
|
164
|
-
discordClientOpts(rest),
|
|
165
|
-
);
|
|
166
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.threads("chan1"));
|
|
167
|
-
expect(requestBody(postMock as unknown as MockCallSource)).toEqual({
|
|
168
|
-
name: "thread",
|
|
169
|
-
message: { content: "initial forum post" },
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("passes applied_tags for forum threads", async () => {
|
|
174
|
-
const { rest, getMock, postMock } = makeDiscordRest();
|
|
175
|
-
getMock.mockResolvedValue({ type: ChannelType.GuildForum });
|
|
176
|
-
postMock.mockResolvedValue({ id: "t1" });
|
|
177
|
-
await createThreadDiscord(
|
|
178
|
-
"chan1",
|
|
179
|
-
{ name: "tagged post", appliedTags: ["tag1", "tag2"] },
|
|
180
|
-
discordClientOpts(rest),
|
|
181
|
-
);
|
|
182
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.threads("chan1"));
|
|
183
|
-
expect(requestBody(postMock as unknown as MockCallSource)).toEqual({
|
|
184
|
-
name: "tagged post",
|
|
185
|
-
message: { content: "tagged post" },
|
|
186
|
-
applied_tags: ["tag1", "tag2"],
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it("omits applied_tags for non-forum threads", async () => {
|
|
191
|
-
const { rest, getMock, postMock } = makeDiscordRest();
|
|
192
|
-
getMock.mockResolvedValue({ type: ChannelType.GuildText });
|
|
193
|
-
postMock.mockResolvedValue({ id: "t1" });
|
|
194
|
-
await createThreadDiscord(
|
|
195
|
-
"chan1",
|
|
196
|
-
{ name: "thread", appliedTags: ["tag1"] },
|
|
197
|
-
discordClientOpts(rest),
|
|
198
|
-
);
|
|
199
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.threads("chan1"));
|
|
200
|
-
expect("applied_tags" in requestBody(postMock as unknown as MockCallSource)).toBe(false);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("falls back when channel lookup is unavailable", async () => {
|
|
204
|
-
const { rest, getMock, postMock } = makeDiscordRest();
|
|
205
|
-
getMock.mockRejectedValue(new Error("lookup failed"));
|
|
206
|
-
postMock.mockResolvedValue({ id: "t1" });
|
|
207
|
-
await createThreadDiscord("chan1", { name: "thread" }, discordClientOpts(rest));
|
|
208
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.threads("chan1"));
|
|
209
|
-
expect(requestBody(postMock as unknown as MockCallSource).name).toBe("thread");
|
|
210
|
-
expect(requestBody(postMock as unknown as MockCallSource).type).toBe(ChannelType.PublicThread);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("respects explicit thread type for standalone threads", async () => {
|
|
214
|
-
const { rest, getMock, postMock } = makeDiscordRest();
|
|
215
|
-
getMock.mockResolvedValue({ type: ChannelType.GuildText });
|
|
216
|
-
postMock.mockResolvedValue({ id: "t1" });
|
|
217
|
-
await createThreadDiscord(
|
|
218
|
-
"chan1",
|
|
219
|
-
{ name: "thread", type: ChannelType.PrivateThread },
|
|
220
|
-
discordClientOpts(rest),
|
|
221
|
-
);
|
|
222
|
-
expect(getMock).toHaveBeenCalledWith(Routes.channel("chan1"));
|
|
223
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.threads("chan1"));
|
|
224
|
-
expect(requestBody(postMock as unknown as MockCallSource).name).toBe("thread");
|
|
225
|
-
expect(requestBody(postMock as unknown as MockCallSource).type).toBe(ChannelType.PrivateThread);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it("sends initial message for non-forum threads with content", async () => {
|
|
229
|
-
const { rest, getMock, postMock } = makeDiscordRest();
|
|
230
|
-
getMock.mockResolvedValue({ type: ChannelType.GuildText });
|
|
231
|
-
postMock.mockResolvedValue({ id: "t1" });
|
|
232
|
-
await createThreadDiscord(
|
|
233
|
-
"chan1",
|
|
234
|
-
{ name: "thread", content: "Hello thread!" },
|
|
235
|
-
discordClientOpts(rest),
|
|
236
|
-
);
|
|
237
|
-
expect(postMock).toHaveBeenCalledTimes(2);
|
|
238
|
-
// First call: create thread
|
|
239
|
-
expect(requestPath(postMock as unknown as MockCallSource, 0)).toBe(Routes.threads("chan1"));
|
|
240
|
-
expect(requestBody(postMock as unknown as MockCallSource, 0).name).toBe("thread");
|
|
241
|
-
expect(requestBody(postMock as unknown as MockCallSource, 0).type).toBe(
|
|
242
|
-
ChannelType.PublicThread,
|
|
243
|
-
);
|
|
244
|
-
// Second call: send message to thread
|
|
245
|
-
expect(requestPath(postMock as unknown as MockCallSource, 1)).toBe(
|
|
246
|
-
Routes.channelMessages("t1"),
|
|
247
|
-
);
|
|
248
|
-
expect(requestBody(postMock as unknown as MockCallSource, 1)).toEqual({
|
|
249
|
-
content: "Hello thread!",
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it("keeps created non-forum thread details when initial message send fails", async () => {
|
|
254
|
-
const { rest, getMock, postMock } = makeDiscordRest();
|
|
255
|
-
getMock.mockResolvedValue({ type: ChannelType.GuildText });
|
|
256
|
-
postMock
|
|
257
|
-
.mockResolvedValueOnce({ id: "t1", name: "thread", type: ChannelType.PublicThread })
|
|
258
|
-
.mockRejectedValueOnce(new Error("missing access"));
|
|
259
|
-
|
|
260
|
-
let thrown: unknown;
|
|
261
|
-
try {
|
|
262
|
-
await createThreadDiscord(
|
|
263
|
-
"chan1",
|
|
264
|
-
{ name: "thread", content: "Hello thread!" },
|
|
265
|
-
discordClientOpts(rest),
|
|
266
|
-
);
|
|
267
|
-
} catch (error) {
|
|
268
|
-
thrown = error;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
expect(thrown).toBeInstanceOf(DiscordThreadInitialMessageError);
|
|
272
|
-
const error = requireRecord(thrown, "thread initial message error");
|
|
273
|
-
expect(error.name).toBe("DiscordThreadInitialMessageError");
|
|
274
|
-
expect(error.initialMessageError).toBe("missing access");
|
|
275
|
-
expect(error.thread).toEqual({ id: "t1", name: "thread", type: ChannelType.PublicThread });
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
it("sends initial message for message-attached threads with content", async () => {
|
|
279
|
-
const { rest, getMock, postMock } = makeDiscordRest();
|
|
280
|
-
postMock.mockResolvedValue({ id: "t1" });
|
|
281
|
-
await createThreadDiscord(
|
|
282
|
-
"chan1",
|
|
283
|
-
{ name: "thread", messageId: "m1", content: "Discussion here" },
|
|
284
|
-
discordClientOpts(rest),
|
|
285
|
-
);
|
|
286
|
-
// Should not detect channel type for message-attached threads
|
|
287
|
-
expect(getMock).not.toHaveBeenCalled();
|
|
288
|
-
expect(postMock).toHaveBeenCalledTimes(2);
|
|
289
|
-
// First call: create thread from message
|
|
290
|
-
expect(requestPath(postMock as unknown as MockCallSource, 0)).toBe(
|
|
291
|
-
Routes.threads("chan1", "m1"),
|
|
292
|
-
);
|
|
293
|
-
expect(requestBody(postMock as unknown as MockCallSource, 0)).toEqual({ name: "thread" });
|
|
294
|
-
// Second call: send message to thread
|
|
295
|
-
expect(requestPath(postMock as unknown as MockCallSource, 1)).toBe(
|
|
296
|
-
Routes.channelMessages("t1"),
|
|
297
|
-
);
|
|
298
|
-
expect(requestBody(postMock as unknown as MockCallSource, 1)).toEqual({
|
|
299
|
-
content: "Discussion here",
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it("lists active threads by guild", async () => {
|
|
304
|
-
const { rest, getMock } = makeDiscordRest();
|
|
305
|
-
getMock.mockResolvedValue({ threads: [] });
|
|
306
|
-
await listThreadsDiscord({ guildId: "g1" }, discordClientOpts(rest));
|
|
307
|
-
expect(getMock).toHaveBeenCalledWith(Routes.guildActiveThreads("g1"));
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it("times out a member", async () => {
|
|
311
|
-
const { rest, patchMock } = makeDiscordRest();
|
|
312
|
-
patchMock.mockResolvedValue({ id: "m1" });
|
|
313
|
-
await timeoutMemberDiscord(
|
|
314
|
-
{ guildId: "g1", userId: "u1", durationMinutes: 10 },
|
|
315
|
-
discordClientOpts(rest),
|
|
316
|
-
);
|
|
317
|
-
expect(requestPath(patchMock as unknown as MockCallSource)).toBe(
|
|
318
|
-
Routes.guildMember("g1", "u1"),
|
|
319
|
-
);
|
|
320
|
-
expect(
|
|
321
|
-
requestBody(patchMock as unknown as MockCallSource).communication_disabled_until,
|
|
322
|
-
).toBeTypeOf("string");
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it("adds and removes roles", async () => {
|
|
326
|
-
const { rest, putMock, deleteMock } = makeDiscordRest();
|
|
327
|
-
putMock.mockResolvedValue({});
|
|
328
|
-
deleteMock.mockResolvedValue({});
|
|
329
|
-
await addRoleDiscord({ guildId: "g1", userId: "u1", roleId: "r1" }, discordClientOpts(rest));
|
|
330
|
-
await removeRoleDiscord({ guildId: "g1", userId: "u1", roleId: "r1" }, discordClientOpts(rest));
|
|
331
|
-
expect(putMock).toHaveBeenCalledWith(Routes.guildMemberRole("g1", "u1", "r1"));
|
|
332
|
-
expect(deleteMock).toHaveBeenCalledWith(Routes.guildMemberRole("g1", "u1", "r1"));
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
it("bans a member", async () => {
|
|
336
|
-
const { rest, putMock } = makeDiscordRest();
|
|
337
|
-
putMock.mockResolvedValue({});
|
|
338
|
-
await banMemberDiscord(
|
|
339
|
-
{ guildId: "g1", userId: "u1", deleteMessageDays: 2 },
|
|
340
|
-
discordClientOpts(rest),
|
|
341
|
-
);
|
|
342
|
-
expect(requestPath(putMock as unknown as MockCallSource)).toBe(Routes.guildBan("g1", "u1"));
|
|
343
|
-
expect(requestBody(putMock as unknown as MockCallSource)).toEqual({ delete_message_days: 2 });
|
|
344
|
-
});
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
describe("listGuildEmojisDiscord", () => {
|
|
348
|
-
beforeEach(() => {
|
|
349
|
-
vi.clearAllMocks();
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
it("lists emojis for a guild", async () => {
|
|
353
|
-
const { rest, getMock } = makeDiscordRest();
|
|
354
|
-
getMock.mockResolvedValue([{ id: "e1", name: "party" }]);
|
|
355
|
-
await listGuildEmojisDiscord("g1", discordClientOpts(rest));
|
|
356
|
-
expect(getMock).toHaveBeenCalledWith(Routes.guildEmojis("g1"));
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
describe("uploadEmojiDiscord", () => {
|
|
361
|
-
beforeEach(() => {
|
|
362
|
-
vi.clearAllMocks();
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
it("uploads emoji assets", async () => {
|
|
366
|
-
const { rest, postMock } = makeDiscordRest();
|
|
367
|
-
postMock.mockResolvedValue({ id: "e1" });
|
|
368
|
-
await uploadEmojiDiscord(
|
|
369
|
-
{
|
|
370
|
-
guildId: "g1",
|
|
371
|
-
name: "party_blob",
|
|
372
|
-
mediaUrl: "file:///tmp/party.png",
|
|
373
|
-
roleIds: ["r1"],
|
|
374
|
-
},
|
|
375
|
-
discordClientOpts(rest),
|
|
376
|
-
);
|
|
377
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.guildEmojis("g1"));
|
|
378
|
-
expect(requestBody(postMock as unknown as MockCallSource)).toEqual({
|
|
379
|
-
name: "party_blob",
|
|
380
|
-
image: "data:image/png;base64,aW1n",
|
|
381
|
-
roles: ["r1"],
|
|
382
|
-
});
|
|
383
|
-
expect(loadWebMediaRaw).toHaveBeenCalledWith("file:///tmp/party.png", 256 * 1024);
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
describe("uploadStickerDiscord", () => {
|
|
388
|
-
beforeEach(() => {
|
|
389
|
-
vi.clearAllMocks();
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it("uploads sticker assets", async () => {
|
|
393
|
-
const { rest, postMock } = makeDiscordRest();
|
|
394
|
-
postMock.mockResolvedValue({ id: "s1" });
|
|
395
|
-
await uploadStickerDiscord(
|
|
396
|
-
{
|
|
397
|
-
guildId: "g1",
|
|
398
|
-
name: "klaw_wave",
|
|
399
|
-
description: "Klaw waving",
|
|
400
|
-
tags: "👋",
|
|
401
|
-
mediaUrl: "file:///tmp/wave.png",
|
|
402
|
-
},
|
|
403
|
-
discordClientOpts(rest),
|
|
404
|
-
);
|
|
405
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.guildStickers("g1"));
|
|
406
|
-
const stickerBody = requestBody(postMock as unknown as MockCallSource);
|
|
407
|
-
expect(stickerBody.name).toBe("klaw_wave");
|
|
408
|
-
expect(stickerBody.description).toBe("Klaw waving");
|
|
409
|
-
expect(stickerBody.tags).toBe("👋");
|
|
410
|
-
const files = stickerBody.files as Array<{ name?: string; contentType?: string }>;
|
|
411
|
-
expect(files).toHaveLength(1);
|
|
412
|
-
expect(files[0]?.name).toBe("asset.png");
|
|
413
|
-
expect(files[0]?.contentType).toBe("image/png");
|
|
414
|
-
expect(loadWebMediaRaw).toHaveBeenCalledWith("file:///tmp/wave.png", 512 * 1024);
|
|
415
|
-
});
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
describe("sendStickerDiscord", () => {
|
|
419
|
-
beforeEach(() => {
|
|
420
|
-
vi.clearAllMocks();
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it("sends sticker payloads", async () => {
|
|
424
|
-
const { rest, postMock } = makeDiscordRest();
|
|
425
|
-
postMock.mockResolvedValue({ id: "msg1", channel_id: "789" });
|
|
426
|
-
const res = await sendStickerDiscord("channel:789", ["123"], {
|
|
427
|
-
cfg: DISCORD_TEST_CFG,
|
|
428
|
-
rest,
|
|
429
|
-
token: "t",
|
|
430
|
-
content: "hiya",
|
|
431
|
-
});
|
|
432
|
-
expect(res.messageId).toBe("msg1");
|
|
433
|
-
expect(res.channelId).toBe("789");
|
|
434
|
-
expect(res.receipt.parts[0]?.platformMessageId).toBe("msg1");
|
|
435
|
-
expect(res.receipt.parts[0]?.kind).toBe("card");
|
|
436
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.channelMessages("789"));
|
|
437
|
-
expect(requestBody(postMock as unknown as MockCallSource)).toEqual({
|
|
438
|
-
content: "hiya",
|
|
439
|
-
flags: MessageFlags.SuppressEmbeds,
|
|
440
|
-
sticker_ids: ["123"],
|
|
441
|
-
});
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
it("allows sticker content link embeds when disabled", async () => {
|
|
445
|
-
const { rest, postMock } = makeDiscordRest();
|
|
446
|
-
postMock.mockResolvedValue({ id: "msg1", channel_id: "789" });
|
|
447
|
-
await sendStickerDiscord("channel:789", ["123"], {
|
|
448
|
-
cfg: DISCORD_TEST_CFG,
|
|
449
|
-
rest,
|
|
450
|
-
token: "t",
|
|
451
|
-
content: "https://example.com",
|
|
452
|
-
suppressEmbeds: false,
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
expect(requestBody(postMock as unknown as MockCallSource)).toEqual({
|
|
456
|
-
content: "https://example.com",
|
|
457
|
-
sticker_ids: ["123"],
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
describe("sendPollDiscord", () => {
|
|
463
|
-
beforeEach(() => {
|
|
464
|
-
vi.clearAllMocks();
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
it("sends polls with answers", async () => {
|
|
468
|
-
const { rest, postMock } = makeDiscordRest();
|
|
469
|
-
postMock.mockResolvedValue({ id: "msg1", channel_id: "789" });
|
|
470
|
-
const res = await sendPollDiscord(
|
|
471
|
-
"channel:789",
|
|
472
|
-
{
|
|
473
|
-
question: "Lunch?",
|
|
474
|
-
options: ["Pizza", "Sushi"],
|
|
475
|
-
},
|
|
476
|
-
{
|
|
477
|
-
cfg: DISCORD_TEST_CFG,
|
|
478
|
-
rest,
|
|
479
|
-
token: "t",
|
|
480
|
-
},
|
|
481
|
-
);
|
|
482
|
-
expect(res.messageId).toBe("msg1");
|
|
483
|
-
expect(res.channelId).toBe("789");
|
|
484
|
-
expect(res.receipt.parts[0]?.platformMessageId).toBe("msg1");
|
|
485
|
-
expect(res.receipt.parts[0]?.kind).toBe("card");
|
|
486
|
-
expect(requestPath(postMock as unknown as MockCallSource)).toBe(Routes.channelMessages("789"));
|
|
487
|
-
expect(requestBody(postMock as unknown as MockCallSource).flags).toBe(
|
|
488
|
-
MessageFlags.SuppressEmbeds,
|
|
489
|
-
);
|
|
490
|
-
expect(requestBody(postMock as unknown as MockCallSource).poll).toEqual({
|
|
491
|
-
question: { text: "Lunch?" },
|
|
492
|
-
answers: [{ poll_media: { text: "Pizza" } }, { poll_media: { text: "Sushi" } }],
|
|
493
|
-
duration: 24,
|
|
494
|
-
allow_multiselect: false,
|
|
495
|
-
layout_type: 1,
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
it("combines silent and suppress-embeds flags for polls", async () => {
|
|
500
|
-
const { rest, postMock } = makeDiscordRest();
|
|
501
|
-
postMock.mockResolvedValue({ id: "msg1", channel_id: "789" });
|
|
502
|
-
await sendPollDiscord(
|
|
503
|
-
"channel:789",
|
|
504
|
-
{
|
|
505
|
-
question: "Lunch?",
|
|
506
|
-
options: ["Pizza", "Sushi"],
|
|
507
|
-
},
|
|
508
|
-
{
|
|
509
|
-
cfg: DISCORD_TEST_CFG,
|
|
510
|
-
rest,
|
|
511
|
-
token: "t",
|
|
512
|
-
content: "https://example.com",
|
|
513
|
-
silent: true,
|
|
514
|
-
},
|
|
515
|
-
);
|
|
516
|
-
|
|
517
|
-
expect(requestBody(postMock as unknown as MockCallSource).flags).toBe(
|
|
518
|
-
MessageFlags.SuppressEmbeds | MessageFlags.SuppressNotifications,
|
|
519
|
-
);
|
|
520
|
-
});
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
function createMockRateLimitError(retryAfter = 0.001): RateLimitError {
|
|
524
|
-
const request = new Request("https://discord.com/api/v10/channels/789/messages", {
|
|
525
|
-
method: "POST",
|
|
526
|
-
});
|
|
527
|
-
const response = new Response(null, {
|
|
528
|
-
status: 429,
|
|
529
|
-
headers: {
|
|
530
|
-
"X-RateLimit-Scope": "user",
|
|
531
|
-
"X-RateLimit-Bucket": "test-bucket",
|
|
532
|
-
},
|
|
533
|
-
});
|
|
534
|
-
return createRateLimitError(
|
|
535
|
-
response,
|
|
536
|
-
{
|
|
537
|
-
message: "You are being rate limited.",
|
|
538
|
-
retry_after: retryAfter,
|
|
539
|
-
global: false,
|
|
540
|
-
},
|
|
541
|
-
request,
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
describe("retry rate limits", () => {
|
|
546
|
-
beforeEach(() => {
|
|
547
|
-
vi.clearAllMocks();
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
it("retries on Discord rate limits", async () => {
|
|
551
|
-
const { rest, postMock } = makeDiscordRest();
|
|
552
|
-
const rateLimitError = createMockRateLimitError(0);
|
|
553
|
-
|
|
554
|
-
postMock
|
|
555
|
-
.mockRejectedValueOnce(rateLimitError)
|
|
556
|
-
.mockResolvedValueOnce({ id: "msg1", channel_id: "789" });
|
|
557
|
-
|
|
558
|
-
const res = await sendMessageDiscord("channel:789", "hello", {
|
|
559
|
-
cfg: DISCORD_TEST_CFG,
|
|
560
|
-
rest,
|
|
561
|
-
token: "t",
|
|
562
|
-
retry: { attempts: 2, minDelayMs: 0, maxDelayMs: 0, jitter: 0 },
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
expect(res.messageId).toBe("msg1");
|
|
566
|
-
expect(postMock).toHaveBeenCalledTimes(2);
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
it("uses retry_after delays when rate limited", async () => {
|
|
570
|
-
const setTimeoutSpy = vi.spyOn(global, "setTimeout");
|
|
571
|
-
try {
|
|
572
|
-
const { rest, postMock } = makeDiscordRest();
|
|
573
|
-
const rateLimitError = createMockRateLimitError(0.001);
|
|
574
|
-
|
|
575
|
-
postMock
|
|
576
|
-
.mockRejectedValueOnce(rateLimitError)
|
|
577
|
-
.mockResolvedValueOnce({ id: "msg1", channel_id: "789" });
|
|
578
|
-
|
|
579
|
-
const promise = sendMessageDiscord("channel:789", "hello", {
|
|
580
|
-
cfg: DISCORD_TEST_CFG,
|
|
581
|
-
rest,
|
|
582
|
-
token: "t",
|
|
583
|
-
retry: { attempts: 2, minDelayMs: 0, maxDelayMs: 1000, jitter: 0 },
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
const result = await promise;
|
|
587
|
-
expect(result.messageId).toBe("msg1");
|
|
588
|
-
expect(result.channelId).toBe("789");
|
|
589
|
-
expect(result.receipt.primaryPlatformMessageId).toBe("msg1");
|
|
590
|
-
expect(result.receipt.platformMessageIds).toEqual(["msg1"]);
|
|
591
|
-
expect(timerDelayAt(setTimeoutSpy as unknown as MockCallSource)).toBe(1);
|
|
592
|
-
} finally {
|
|
593
|
-
setTimeoutSpy.mockRestore();
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
it("stops after max retry attempts", async () => {
|
|
598
|
-
const { rest, postMock } = makeDiscordRest();
|
|
599
|
-
const rateLimitError = createMockRateLimitError(0);
|
|
600
|
-
|
|
601
|
-
postMock.mockRejectedValue(rateLimitError);
|
|
602
|
-
|
|
603
|
-
await expect(
|
|
604
|
-
sendMessageDiscord("channel:789", "hello", {
|
|
605
|
-
cfg: DISCORD_TEST_CFG,
|
|
606
|
-
rest,
|
|
607
|
-
token: "t",
|
|
608
|
-
retry: { attempts: 2, minDelayMs: 0, maxDelayMs: 0, jitter: 0 },
|
|
609
|
-
}),
|
|
610
|
-
).rejects.toBeInstanceOf(RateLimitError);
|
|
611
|
-
expect(postMock).toHaveBeenCalledTimes(2);
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
it("does not retry permanent non-rate-limit errors", async () => {
|
|
615
|
-
const { rest, postMock } = makeDiscordRest();
|
|
616
|
-
postMock.mockRejectedValueOnce(new Error("invalid request"));
|
|
617
|
-
|
|
618
|
-
await expect(
|
|
619
|
-
sendMessageDiscord("channel:789", "hello", discordClientOpts(rest)),
|
|
620
|
-
).rejects.toThrow("invalid request");
|
|
621
|
-
expect(postMock).toHaveBeenCalledTimes(1);
|
|
622
|
-
});
|
|
623
|
-
|
|
624
|
-
it("retries transient network errors", async () => {
|
|
625
|
-
const { rest, postMock } = makeDiscordRest();
|
|
626
|
-
postMock
|
|
627
|
-
.mockRejectedValueOnce(new TypeError("fetch failed"))
|
|
628
|
-
.mockResolvedValueOnce({ id: "msg1", channel_id: "789" });
|
|
629
|
-
|
|
630
|
-
const result = await sendMessageDiscord("channel:789", "hello", {
|
|
631
|
-
cfg: DISCORD_TEST_CFG,
|
|
632
|
-
rest,
|
|
633
|
-
token: "t",
|
|
634
|
-
retry: { attempts: 2, minDelayMs: 0, maxDelayMs: 0, jitter: 0 },
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
expect(result.messageId).toBe("msg1");
|
|
638
|
-
expect(result.channelId).toBe("789");
|
|
639
|
-
expect(result.receipt.platformMessageIds).toEqual(["msg1"]);
|
|
640
|
-
expect(postMock).toHaveBeenCalledTimes(2);
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
it("retries reactions on rate limits", async () => {
|
|
644
|
-
const { rest, putMock } = makeDiscordRest();
|
|
645
|
-
const rateLimitError = createMockRateLimitError(0);
|
|
646
|
-
|
|
647
|
-
putMock.mockRejectedValueOnce(rateLimitError).mockResolvedValueOnce(undefined);
|
|
648
|
-
|
|
649
|
-
const res = await reactMessageDiscord("chan1", "msg1", "ok", {
|
|
650
|
-
cfg: DISCORD_TEST_CFG,
|
|
651
|
-
rest,
|
|
652
|
-
token: "t",
|
|
653
|
-
retry: { attempts: 2, minDelayMs: 0, maxDelayMs: 0, jitter: 0 },
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
expect(res.ok).toBe(true);
|
|
657
|
-
expect(putMock).toHaveBeenCalledTimes(2);
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
it("retries media upload without duplicating overflow text", async () => {
|
|
661
|
-
const { rest, postMock } = makeDiscordRest();
|
|
662
|
-
const rateLimitError = createMockRateLimitError(0);
|
|
663
|
-
const text = "a".repeat(2005);
|
|
664
|
-
|
|
665
|
-
postMock
|
|
666
|
-
.mockRejectedValueOnce(rateLimitError)
|
|
667
|
-
.mockResolvedValueOnce({ id: "msg1", channel_id: "789" })
|
|
668
|
-
.mockResolvedValueOnce({ id: "msg2", channel_id: "789" });
|
|
669
|
-
|
|
670
|
-
const res = await sendMessageDiscord("channel:789", text, {
|
|
671
|
-
cfg: DISCORD_TEST_CFG,
|
|
672
|
-
rest,
|
|
673
|
-
token: "t",
|
|
674
|
-
mediaUrl: "https://example.com/photo.jpg",
|
|
675
|
-
retry: { attempts: 2, minDelayMs: 0, maxDelayMs: 0, jitter: 0 },
|
|
676
|
-
});
|
|
677
|
-
|
|
678
|
-
expect(res.messageId).toBe("msg1");
|
|
679
|
-
expect(postMock).toHaveBeenCalledTimes(3);
|
|
680
|
-
});
|
|
681
|
-
});
|