@openclaw/discord 2026.5.2 → 2026.5.3-beta.2
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/dist/access-B9ujuUtS.js +89 -0
- package/dist/account-inspect-C2UEUhbG.js +81 -0
- package/dist/account-inspect-api.js +10 -0
- package/dist/accounts-BKnkNaoA.js +128 -0
- package/dist/action-runtime-api.js +2 -0
- package/dist/agent-components.runtime-DUhLr9hy.js +4 -0
- package/dist/allow-list-ek-1hMKN.js +336 -0
- package/dist/api-DzNBVTto.js +130 -0
- package/dist/api.js +24 -0
- package/dist/approval-handler.runtime-v8nzQHlT.js +426 -0
- package/dist/approval-native-DqWGp0bM.js +153 -0
- package/dist/approval-shared-DKnwwjZM.js +93 -0
- package/dist/audit-CJ92YD6J.js +102 -0
- package/dist/channel-B3aTtBj1.js +745 -0
- package/dist/channel-access-ewDxhd9q.js +62 -0
- package/dist/channel-actions-TNih7k3w.js +140 -0
- package/dist/channel-actions.runtime-CaPytiY4.js +236 -0
- package/dist/channel-api-CTSWMrnD.js +21 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.setup-Dt4tIDrl.js +336 -0
- package/dist/components-BapWDmDM.js +760 -0
- package/dist/config-api-CFZtoMaS.js +2 -0
- package/dist/config-schema-DwFkL904.js +252 -0
- package/dist/configured-state.js +6 -0
- package/dist/contract-api.js +8 -0
- package/dist/conversation-identity-BN9wSmxJ.js +31 -0
- package/dist/directory-cache-D93eSrpB.js +62 -0
- package/dist/directory-config-LyMP0sdv.js +49 -0
- package/dist/directory-contract-api.js +2 -0
- package/dist/directory-live-BQapdpkZ.js +101 -0
- package/dist/discord-D1kDh0X_.js +2751 -0
- package/dist/doctor-B2G7WqO0.js +244 -0
- package/dist/doctor-contract-D3pSutkb.js +383 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/doctor-shared-DU8RcnF5.js +4 -0
- package/dist/format-D8TsaXxW.js +24 -0
- package/dist/gateway-registry-BKG4KIVC.js +74 -0
- package/dist/handle-action.guild-admin-BuqsSVXu.js +283 -0
- package/dist/inbound-context-e_oBBJtF.js +51 -0
- package/dist/index.js +26 -0
- package/dist/manager.runtime-M2aAa7qA.js +1019 -0
- package/dist/mentions-BPZUaFk7.js +88 -0
- package/dist/message-handler-D6JfFV3P.js +381 -0
- package/dist/message-handler.preflight-DqaF3vHm.js +1022 -0
- package/dist/message-handler.process-tl3Nwnhr.js +1124 -0
- package/dist/message-utils-Dmgu-7fC.js +512 -0
- package/dist/normalize-B-ktw-T_.js +275 -0
- package/dist/outbound-adapter-DJf9_sfH.js +451 -0
- package/dist/outbound-session-route-uHGLDP-Y.js +43 -0
- package/dist/pluralkit-voQvSN3g.js +22 -0
- package/dist/preflight-audio-BpYtUAT6.js +72 -0
- package/dist/preflight-audio.runtime-BAGmU6uO.js +7 -0
- package/dist/preview-streaming-C0O92Qqz.js +14 -0
- package/dist/probe-DcNEodPI.js +139 -0
- package/dist/probe.runtime-P-e4r1Hl.js +2 -0
- package/dist/provider-CMvXOp-3.js +8440 -0
- package/dist/provider-session.runtime-JFemrDZT.js +6 -0
- package/dist/provider.runtime-BO007oR2.js +2 -0
- package/dist/reply-delivery-D9So77a6.js +131 -0
- package/dist/resolve-allowlist-common-DqqFY_qa.js +34 -0
- package/dist/resolve-channels-CGPntufJ.js +265 -0
- package/dist/resolve-users-CDvSlW0V.js +120 -0
- package/dist/rolldown-runtime-C3SqQTfK.js +28 -0
- package/dist/route-resolution-BYiC-6Cc.js +236 -0
- package/dist/runtime-K9RT6Egn.js +8 -0
- package/dist/runtime-api.actions.js +3 -0
- package/dist/runtime-api.js +31 -0
- package/dist/runtime-api.lookup.js +7 -0
- package/dist/runtime-api.monitor-BC-XN0tY.js +6 -0
- package/dist/runtime-api.monitor.js +9 -0
- package/dist/runtime-api.send.js +6 -0
- package/dist/runtime-api.threads.js +6 -0
- package/dist/runtime-n5xZHW55.js +1001 -0
- package/dist/runtime-setter-api.js +2 -0
- package/dist/secret-config-contract-CoGryS5c.js +115 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/security-audit-Cdz2iq3m.js +120 -0
- package/dist/security-audit-contract-api.js +2 -0
- package/dist/security-audit.runtime-DBV1T1_N.js +2 -0
- package/dist/security-contract-api.js +2 -0
- package/dist/security-contract-ei3Mz8Sa.js +26 -0
- package/dist/security-doctor-CzTzpXV8.js +18 -0
- package/dist/send-B_frVn_Q.js +845 -0
- package/dist/send.components-B1EgHAds.js +468 -0
- package/dist/send.outbound-DlBAuW7y.js +211 -0
- package/dist/send.shared-Db0opnak.js +708 -0
- package/dist/sender-identity-BiSDAk2P.js +43 -0
- package/dist/session-contract-goJZckp2.js +6 -0
- package/dist/session-key-api.js +2 -0
- package/dist/session-key-normalization-Daag9II6.js +23 -0
- package/dist/setup-entry.js +11 -0
- package/dist/setup-plugin-api.js +2 -0
- package/dist/shared-CqlrJmSs.js +166 -0
- package/dist/shared-interactive-KgJjCqnB.js +79 -0
- package/dist/subagent-hooks-api.js +22 -0
- package/dist/subagent-hooks-mEK5ARfP.js +113 -0
- package/dist/system-events-Bu9jmO4W.js +34 -0
- package/dist/targets-kKlbZ4ai.js +3 -0
- package/dist/test-api.js +45 -0
- package/dist/thread-binding-api.js +4 -0
- package/dist/thread-bindings-Bj1R-6QH.js +256 -0
- package/dist/thread-bindings.discord-api-ClPMuIr8.js +184 -0
- package/dist/thread-bindings.manager-BaN0l4y8.js +535 -0
- package/dist/thread-bindings.session-updates-TTP020qQ.js +54 -0
- package/dist/thread-bindings.state-Dzu1gCE7.js +318 -0
- package/dist/threading-CWhdYHVx.js +475 -0
- package/dist/timeouts-C7jeTtGs.js +52 -0
- package/dist/timeouts.js +2 -0
- package/dist/token-D-w3Rigl.js +42 -0
- package/dist/typing-CJiowRTZ.js +15 -0
- package/package.json +14 -6
- package/account-inspect-api.ts +0 -6
- package/action-runtime-api.ts +0 -1
- package/api.ts +0 -132
- 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 -30
- package/runtime-api.ts +0 -180
- 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 -132
- package/src/accounts.test.ts +0 -247
- package/src/accounts.ts +0 -196
- package/src/actions/handle-action.guild-admin.ts +0 -411
- package/src/actions/handle-action.test.ts +0 -306
- package/src/actions/handle-action.ts +0 -372
- package/src/actions/runtime.guild.ts +0 -446
- package/src/actions/runtime.messaging.messages.ts +0 -205
- package/src/actions/runtime.messaging.reactions.ts +0 -67
- package/src/actions/runtime.messaging.runtime.ts +0 -69
- package/src/actions/runtime.messaging.send.ts +0 -248
- 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 -160
- package/src/actions/runtime.presence.ts +0 -117
- package/src/actions/runtime.shared.ts +0 -83
- package/src/actions/runtime.test.ts +0 -1087
- package/src/actions/runtime.ts +0 -87
- package/src/api-barrel.test.ts +0 -80
- package/src/api.test.ts +0 -130
- package/src/api.ts +0 -169
- package/src/approval-handler.runtime.test.ts +0 -41
- package/src/approval-handler.runtime.ts +0 -632
- 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 -53
- package/src/audit-core.ts +0 -141
- package/src/audit.test.ts +0 -145
- 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 -275
- package/src/channel-actions.ts +0 -203
- package/src/channel-api.ts +0 -29
- package/src/channel.conversation.ts +0 -159
- package/src/channel.loaders.ts +0 -47
- package/src/channel.runtime.ts +0 -1
- package/src/channel.setup.ts +0 -12
- package/src/channel.test.ts +0 -571
- package/src/channel.ts +0 -629
- package/src/chunk.test.ts +0 -157
- package/src/chunk.ts +0 -321
- package/src/client.proxy.test.ts +0 -176
- package/src/client.test.ts +0 -76
- package/src/client.ts +0 -132
- 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 -312
- package/src/components.ts +0 -54
- package/src/components.types.ts +0 -187
- package/src/config-schema.test.ts +0 -325
- package/src/config-schema.ts +0 -6
- package/src/config-ui-hints.ts +0 -249
- package/src/conversation-identity.ts +0 -58
- package/src/delivery-retry.ts +0 -56
- 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 -126
- 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 -405
- 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 -159
- package/src/draft-stream.ts +0 -154
- 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/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 -262
- 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 -440
- package/src/internal/client.ts +0 -310
- package/src/internal/command-deploy.ts +0 -297
- 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 -162
- 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.ts +0 -61
- package/src/internal/gateway-rate-limit.ts +0 -104
- package/src/internal/gateway.test.ts +0 -603
- package/src/internal/gateway.ts +0 -476
- 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 -325
- package/src/internal/interactions.ts +0 -378
- package/src/internal/listeners.ts +0 -85
- 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 -673
- 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/access-groups.ts +0 -55
- package/src/monitor/ack-reactions.ts +0 -70
- package/src/monitor/acp-bind-here.integration.test.ts +0 -211
- 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 -221
- package/src/monitor/agent-components-guild-auth.ts +0 -322
- package/src/monitor/agent-components-helpers.runtime.ts +0 -5
- 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 -366
- 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 -211
- 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 -168
- 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 -633
- package/src/monitor/auto-presence.test.ts +0 -156
- package/src/monitor/auto-presence.ts +0 -356
- package/src/monitor/channel-access.test.ts +0 -99
- 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 -197
- package/src/monitor/dm-command-auth.ts +0 -158
- package/src/monitor/dm-command-decision.test.ts +0 -113
- package/src/monitor/dm-command-decision.ts +0 -49
- package/src/monitor/exec-approvals.test.ts +0 -226
- package/src/monitor/exec-approvals.ts +0 -158
- package/src/monitor/format.ts +0 -45
- package/src/monitor/gateway-handle.ts +0 -34
- 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 -297
- package/src/monitor/gateway-plugin.ts +0 -294
- package/src/monitor/gateway-registry.ts +0 -37
- package/src/monitor/gateway-supervisor.test.ts +0 -150
- 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 -106
- package/src/monitor/inbound-context.ts +0 -103
- package/src/monitor/inbound-dedupe.ts +0 -79
- package/src/monitor/inbound-job.test.ts +0 -203
- package/src/monitor/inbound-job.ts +0 -118
- package/src/monitor/listeners.queue.ts +0 -91
- package/src/monitor/listeners.reactions.ts +0 -610
- package/src/monitor/listeners.test.ts +0 -200
- package/src/monitor/listeners.ts +0 -150
- package/src/monitor/message-channel-info.ts +0 -96
- package/src/monitor/message-forwarded.ts +0 -107
- 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 -406
- package/src/monitor/message-handler.dm-preflight.ts +0 -123
- package/src/monitor/message-handler.draft-preview.ts +0 -246
- 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 -59
- 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 -369
- package/src/monitor/message-handler.preflight.test-helpers.ts +0 -111
- package/src/monitor/message-handler.preflight.test.ts +0 -1623
- package/src/monitor/message-handler.preflight.ts +0 -679
- package/src/monitor/message-handler.preflight.types.ts +0 -110
- package/src/monitor/message-handler.process.test.ts +0 -1369
- package/src/monitor/message-handler.process.ts +0 -686
- package/src/monitor/message-handler.queue.test.ts +0 -496
- 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 -274
- package/src/monitor/message-media.ts +0 -509
- 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 -1157
- package/src/monitor/message-utils.ts +0 -32
- 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 -794
- package/src/monitor/model-picker.ts +0 -38
- package/src/monitor/model-picker.view.ts +0 -695
- package/src/monitor/monitor.agent-components.test.ts +0 -375
- package/src/monitor/monitor.test.ts +0 -849
- package/src/monitor/monitor.threading-utils.test.ts +0 -598
- 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 -308
- package/src/monitor/native-command-bypass.ts +0 -13
- package/src/monitor/native-command-context.test.ts +0 -98
- package/src/monitor/native-command-context.ts +0 -103
- package/src/monitor/native-command-dispatch.ts +0 -35
- package/src/monitor/native-command-model-picker-apply.ts +0 -177
- package/src/monitor/native-command-model-picker-interaction.ts +0 -461
- package/src/monitor/native-command-model-picker-ui.ts +0 -368
- 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 -99
- package/src/monitor/native-command.commands-allowfrom.test.ts +0 -490
- package/src/monitor/native-command.model-picker.test.ts +0 -767
- package/src/monitor/native-command.options.test.ts +0 -369
- package/src/monitor/native-command.options.ts +0 -153
- package/src/monitor/native-command.plugin-dispatch.test.ts +0 -961
- package/src/monitor/native-command.runtime.ts +0 -50
- package/src/monitor/native-command.status-direct.test.ts +0 -272
- package/src/monitor/native-command.test-helpers.ts +0 -64
- package/src/monitor/native-command.think-autocomplete.test.ts +0 -416
- package/src/monitor/native-command.ts +0 -700
- 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 -44
- 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 -149
- package/src/monitor/provider.allowlist.ts +0 -394
- 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 -713
- package/src/monitor/provider.lifecycle.ts +0 -552
- package/src/monitor/provider.proxy.test.ts +0 -745
- package/src/monitor/provider.rest-proxy.test.ts +0 -121
- 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 -426
- package/src/monitor/provider.startup.ts +0 -330
- package/src/monitor/provider.test.ts +0 -1111
- package/src/monitor/provider.ts +0 -713
- package/src/monitor/reply-context.ts +0 -64
- package/src/monitor/reply-delivery.test.ts +0 -244
- package/src/monitor/reply-delivery.ts +0 -203
- package/src/monitor/rest-fetch.ts +0 -43
- package/src/monitor/route-resolution.test.ts +0 -204
- 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 -229
- package/src/monitor/thread-bindings.discord-api.ts +0 -310
- package/src/monitor/thread-bindings.lifecycle.test.ts +0 -1871
- package/src/monitor/thread-bindings.lifecycle.ts +0 -354
- package/src/monitor/thread-bindings.manager.ts +0 -553
- 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 -36
- 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 -197
- 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 -327
- 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 -260
- package/src/monitor/threading.starter.ts +0 -287
- 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 -1397
- package/src/monitor.ts +0 -28
- package/src/normalize.test.ts +0 -56
- package/src/normalize.ts +0 -86
- package/src/outbound-adapter.interactive-order.test.ts +0 -64
- package/src/outbound-adapter.test-harness.ts +0 -207
- package/src/outbound-adapter.test.ts +0 -696
- package/src/outbound-adapter.ts +0 -291
- package/src/outbound-approval.ts +0 -29
- package/src/outbound-components.ts +0 -81
- package/src/outbound-payload.contract.test.ts +0 -38
- package/src/outbound-payload.ts +0 -134
- package/src/outbound-send-context.ts +0 -92
- package/src/outbound-session-route.test.ts +0 -34
- 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 -32
- 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 -78
- package/src/proxy-request-client.ts +0 -21
- package/src/recipient-resolution.ts +0 -39
- package/src/resolve-allowlist-common.test.ts +0 -36
- package/src/resolve-allowlist-common.ts +0 -39
- package/src/resolve-channels.test.ts +0 -340
- package/src/resolve-channels.ts +0 -369
- package/src/resolve-users.test.ts +0 -222
- 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 -64
- 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 -246
- 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 -275
- package/src/send.components.ts +0 -381
- package/src/send.creates-thread.test.ts +0 -643
- package/src/send.emojis-stickers.ts +0 -57
- package/src/send.guild.ts +0 -170
- package/src/send.message-request.ts +0 -97
- package/src/send.messages.test.ts +0 -53
- package/src/send.messages.ts +0 -225
- package/src/send.outbound.ts +0 -413
- package/src/send.permissions.authz.test.ts +0 -188
- package/src/send.permissions.ts +0 -283
- package/src/send.reactions.ts +0 -155
- package/src/send.sends-basic-channel-messages.test.ts +0 -941
- package/src/send.shared.ts +0 -447
- package/src/send.test-harness.ts +0 -56
- package/src/send.ts +0 -82
- package/src/send.types.ts +0 -188
- package/src/send.typing.test.ts +0 -41
- package/src/send.typing.ts +0 -9
- package/src/send.voice.ts +0 -134
- package/src/send.webhook-activity.test.ts +0 -105
- package/src/send.webhook.proxy.test.ts +0 -191
- package/src/send.webhook.ts +0 -133
- 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 -91
- package/src/setup-account-state.ts +0 -144
- package/src/setup-adapter.ts +0 -12
- package/src/setup-core.ts +0 -212
- package/src/setup-runtime-helpers.ts +0 -10
- package/src/setup-surface.test.ts +0 -137
- package/src/setup-surface.ts +0 -129
- package/src/shared-interactive.test.ts +0 -153
- package/src/shared-interactive.ts +0 -124
- package/src/shared.test.ts +0 -165
- package/src/shared.ts +0 -190
- package/src/status-issues.test.ts +0 -70
- package/src/status-issues.ts +0 -169
- package/src/subagent-hooks.test.ts +0 -432
- package/src/subagent-hooks.ts +0 -214
- package/src/target-parsing.ts +0 -53
- package/src/target-resolver.ts +0 -129
- package/src/targets.test.ts +0 -367
- package/src/targets.ts +0 -12
- package/src/test-http-helpers.ts +0 -10
- package/src/test-support/component-runtime.ts +0 -190
- 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 -545
- package/src/token.test.ts +0 -107
- package/src/token.ts +0 -60
- package/src/ui-colors.ts +0 -27
- package/src/ui.ts +0 -20
- package/src/voice/access.test.ts +0 -217
- package/src/voice/access.ts +0 -124
- package/src/voice/audio.ts +0 -173
- package/src/voice/capture-state.test.ts +0 -48
- package/src/voice/capture-state.ts +0 -120
- package/src/voice/command.test.ts +0 -164
- package/src/voice/command.ts +0 -283
- package/src/voice/config.ts +0 -8
- package/src/voice/manager.e2e.test.ts +0 -928
- package/src/voice/manager.ready-listener.test.ts +0 -37
- package/src/voice/manager.runtime.ts +0 -11
- package/src/voice/manager.ts +0 -691
- package/src/voice/prompt.test.ts +0 -16
- package/src/voice/prompt.ts +0 -17
- package/src/voice/receive-recovery.test.ts +0 -79
- package/src/voice/receive-recovery.ts +0 -159
- package/src/voice/sanitize.test.ts +0 -34
- package/src/voice/sanitize.ts +0 -32
- package/src/voice/sdk-runtime.ts +0 -14
- package/src/voice/segment.ts +0 -156
- package/src/voice/session.ts +0 -50
- package/src/voice/speaker-context.ts +0 -127
- package/src/voice/tts.ts +0 -125
- package/src/voice-message.test.ts +0 -234
- package/src/voice-message.ts +0 -444
- 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
|
@@ -0,0 +1,1019 @@
|
|
|
1
|
+
import { c as resolveDiscordAccountAllowFrom } from "./accounts-BKnkNaoA.js";
|
|
2
|
+
import { c as ResumedListener, s as ReadyListener, t as discord_exports } from "./discord-D1kDh0X_.js";
|
|
3
|
+
import { n as formatDiscordUserTag } from "./format-D8TsaXxW.js";
|
|
4
|
+
import { a as normalizeDiscordSlug, m as resolveDiscordOwnerAccess } from "./allow-list-ek-1hMKN.js";
|
|
5
|
+
import { t as formatMention } from "./mentions-BPZUaFk7.js";
|
|
6
|
+
import { t as getDiscordRuntime } from "./runtime-K9RT6Egn.js";
|
|
7
|
+
import { t as buildDiscordGroupSystemPrompt } from "./inbound-context-e_oBBJtF.js";
|
|
8
|
+
import { n as resolveDiscordVoiceEnabled, t as authorizeDiscordVoiceIngress } from "./access-B9ujuUtS.js";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import { normalizeOptionalString, stripInlineDirectiveTagsForDisplay } from "openclaw/plugin-sdk/text-runtime";
|
|
11
|
+
import { resolveAgentRoute } from "openclaw/plugin-sdk/routing";
|
|
12
|
+
import { randomUUID } from "node:crypto";
|
|
13
|
+
import fs from "node:fs/promises";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { createSubsystemLogger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
16
|
+
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
|
17
|
+
import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
18
|
+
import { agentCommandFromIngress, getTtsProvider, resolveAgentDir, resolveTtsConfig, resolveTtsPrefsPath } from "openclaw/plugin-sdk/agent-runtime";
|
|
19
|
+
import { parseTtsDirectives } from "openclaw/plugin-sdk/speech";
|
|
20
|
+
//#region extensions/discord/src/voice/audio.ts
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
const SAMPLE_RATE = 48e3;
|
|
23
|
+
const CHANNELS = 2;
|
|
24
|
+
const BIT_DEPTH = 16;
|
|
25
|
+
let warnedOpusMissing = false;
|
|
26
|
+
let cachedOpusDecoderFactory = "unresolved";
|
|
27
|
+
function buildWavBuffer(pcm) {
|
|
28
|
+
const blockAlign = CHANNELS * BIT_DEPTH / 8;
|
|
29
|
+
const byteRate = SAMPLE_RATE * blockAlign;
|
|
30
|
+
const header = Buffer.alloc(44);
|
|
31
|
+
header.write("RIFF", 0);
|
|
32
|
+
header.writeUInt32LE(36 + pcm.length, 4);
|
|
33
|
+
header.write("WAVE", 8);
|
|
34
|
+
header.write("fmt ", 12);
|
|
35
|
+
header.writeUInt32LE(16, 16);
|
|
36
|
+
header.writeUInt16LE(1, 20);
|
|
37
|
+
header.writeUInt16LE(CHANNELS, 22);
|
|
38
|
+
header.writeUInt32LE(SAMPLE_RATE, 24);
|
|
39
|
+
header.writeUInt32LE(byteRate, 28);
|
|
40
|
+
header.writeUInt16LE(blockAlign, 32);
|
|
41
|
+
header.writeUInt16LE(BIT_DEPTH, 34);
|
|
42
|
+
header.write("data", 36);
|
|
43
|
+
header.writeUInt32LE(pcm.length, 40);
|
|
44
|
+
return Buffer.concat([header, pcm]);
|
|
45
|
+
}
|
|
46
|
+
function resolveOpusDecoderFactory(params) {
|
|
47
|
+
const factories = [{
|
|
48
|
+
name: "@discordjs/opus",
|
|
49
|
+
load: () => {
|
|
50
|
+
return new (require("@discordjs/opus")).OpusEncoder(SAMPLE_RATE, CHANNELS);
|
|
51
|
+
}
|
|
52
|
+
}, {
|
|
53
|
+
name: "opusscript",
|
|
54
|
+
load: () => {
|
|
55
|
+
const OpusScript = require("opusscript");
|
|
56
|
+
return new OpusScript(SAMPLE_RATE, CHANNELS, OpusScript.Application.AUDIO);
|
|
57
|
+
}
|
|
58
|
+
}];
|
|
59
|
+
const failures = [];
|
|
60
|
+
for (const factory of factories) try {
|
|
61
|
+
factory.load();
|
|
62
|
+
return factory;
|
|
63
|
+
} catch (err) {
|
|
64
|
+
failures.push(`${factory.name}: ${formatErrorMessage(err)}`);
|
|
65
|
+
}
|
|
66
|
+
if (!warnedOpusMissing) {
|
|
67
|
+
warnedOpusMissing = true;
|
|
68
|
+
params.onWarn(`discord voice: no usable opus decoder available (${failures.join("; ")}); cannot decode voice audio`);
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
function getOrCreateOpusDecoderFactory(params) {
|
|
73
|
+
if (cachedOpusDecoderFactory !== "unresolved") return cachedOpusDecoderFactory;
|
|
74
|
+
cachedOpusDecoderFactory = resolveOpusDecoderFactory(params);
|
|
75
|
+
return cachedOpusDecoderFactory;
|
|
76
|
+
}
|
|
77
|
+
function createOpusDecoder(params) {
|
|
78
|
+
const factory = getOrCreateOpusDecoderFactory(params);
|
|
79
|
+
if (!factory) return null;
|
|
80
|
+
return {
|
|
81
|
+
decoder: factory.load(),
|
|
82
|
+
name: factory.name
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
async function decodeOpusStream(stream, params) {
|
|
86
|
+
const selected = createOpusDecoder({ onWarn: params.onWarn });
|
|
87
|
+
if (!selected) return Buffer.alloc(0);
|
|
88
|
+
params.onVerbose(`opus decoder: ${selected.name}`);
|
|
89
|
+
const chunks = [];
|
|
90
|
+
try {
|
|
91
|
+
for await (const chunk of stream) {
|
|
92
|
+
if (!chunk || !(chunk instanceof Buffer) || chunk.length === 0) continue;
|
|
93
|
+
const decoded = selected.decoder.decode(chunk);
|
|
94
|
+
if (decoded && decoded.length > 0) chunks.push(Buffer.from(decoded));
|
|
95
|
+
}
|
|
96
|
+
} catch (err) {
|
|
97
|
+
if (shouldLogVerbose()) logVerbose(`discord voice: opus decode failed: ${formatErrorMessage(err)}`);
|
|
98
|
+
}
|
|
99
|
+
return chunks.length > 0 ? Buffer.concat(chunks) : Buffer.alloc(0);
|
|
100
|
+
}
|
|
101
|
+
function estimateDurationSeconds(pcm) {
|
|
102
|
+
const bytesPerSample = BIT_DEPTH / 8 * CHANNELS;
|
|
103
|
+
if (bytesPerSample <= 0) return 0;
|
|
104
|
+
return pcm.length / (bytesPerSample * SAMPLE_RATE);
|
|
105
|
+
}
|
|
106
|
+
async function writeVoiceWavFile(pcm) {
|
|
107
|
+
const tempDir = await fs.mkdtemp(path.join(resolvePreferredOpenClawTmpDir(), "discord-voice-"));
|
|
108
|
+
const filePath = path.join(tempDir, `segment-${randomUUID()}.wav`);
|
|
109
|
+
const wav = buildWavBuffer(pcm);
|
|
110
|
+
await fs.writeFile(filePath, wav);
|
|
111
|
+
scheduleTempCleanup(tempDir);
|
|
112
|
+
return {
|
|
113
|
+
path: filePath,
|
|
114
|
+
durationSeconds: estimateDurationSeconds(pcm)
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function scheduleTempCleanup(tempDir, delayMs = 1800 * 1e3) {
|
|
118
|
+
setTimeout(() => {
|
|
119
|
+
fs.rm(tempDir, {
|
|
120
|
+
recursive: true,
|
|
121
|
+
force: true
|
|
122
|
+
}).catch((err) => {
|
|
123
|
+
if (shouldLogVerbose()) logVerbose(`discord voice: temp cleanup failed for ${tempDir}: ${formatErrorMessage(err)}`);
|
|
124
|
+
});
|
|
125
|
+
}, delayMs).unref();
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region extensions/discord/src/voice/capture-state.ts
|
|
129
|
+
function createVoiceCaptureState() {
|
|
130
|
+
return {
|
|
131
|
+
activeSpeakers: /* @__PURE__ */ new Set(),
|
|
132
|
+
activeCaptureStreams: /* @__PURE__ */ new Map(),
|
|
133
|
+
captureFinalizeTimers: /* @__PURE__ */ new Map(),
|
|
134
|
+
captureGenerations: /* @__PURE__ */ new Map()
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function stopVoiceCaptureState(state) {
|
|
138
|
+
for (const { timer } of state.captureFinalizeTimers.values()) clearTimeout(timer);
|
|
139
|
+
state.captureFinalizeTimers.clear();
|
|
140
|
+
for (const { stream } of state.activeCaptureStreams.values()) stream.destroy();
|
|
141
|
+
state.activeCaptureStreams.clear();
|
|
142
|
+
state.captureGenerations.clear();
|
|
143
|
+
state.activeSpeakers.clear();
|
|
144
|
+
}
|
|
145
|
+
function getActiveVoiceCapture(state, userId) {
|
|
146
|
+
return state.activeCaptureStreams.get(userId);
|
|
147
|
+
}
|
|
148
|
+
function isVoiceCaptureActive(state, userId) {
|
|
149
|
+
return state.activeSpeakers.has(userId);
|
|
150
|
+
}
|
|
151
|
+
function clearVoiceCaptureFinalizeTimer(state, userId, generation) {
|
|
152
|
+
const scheduled = state.captureFinalizeTimers.get(userId);
|
|
153
|
+
if (!scheduled || generation !== void 0 && scheduled.generation !== generation) return false;
|
|
154
|
+
clearTimeout(scheduled.timer);
|
|
155
|
+
state.captureFinalizeTimers.delete(userId);
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
function beginVoiceCapture(state, userId, stream) {
|
|
159
|
+
const generation = (state.captureGenerations.get(userId) ?? 0) + 1;
|
|
160
|
+
state.captureGenerations.set(userId, generation);
|
|
161
|
+
state.activeSpeakers.add(userId);
|
|
162
|
+
state.activeCaptureStreams.set(userId, {
|
|
163
|
+
generation,
|
|
164
|
+
stream
|
|
165
|
+
});
|
|
166
|
+
clearVoiceCaptureFinalizeTimer(state, userId, generation);
|
|
167
|
+
return generation;
|
|
168
|
+
}
|
|
169
|
+
function finishVoiceCapture(state, userId, generation) {
|
|
170
|
+
clearVoiceCaptureFinalizeTimer(state, userId, generation);
|
|
171
|
+
if (state.activeCaptureStreams.get(userId)?.generation !== generation) return false;
|
|
172
|
+
state.activeCaptureStreams.delete(userId);
|
|
173
|
+
state.activeSpeakers.delete(userId);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
function scheduleVoiceCaptureFinalize(params) {
|
|
177
|
+
const { state, userId, delayMs, onFinalize } = params;
|
|
178
|
+
const capture = state.activeCaptureStreams.get(userId);
|
|
179
|
+
if (!capture) return false;
|
|
180
|
+
clearVoiceCaptureFinalizeTimer(state, userId, capture.generation);
|
|
181
|
+
const timer = setTimeout(() => {
|
|
182
|
+
const activeCapture = state.activeCaptureStreams.get(userId);
|
|
183
|
+
if (!activeCapture || activeCapture.generation !== capture.generation) return;
|
|
184
|
+
state.captureFinalizeTimers.delete(userId);
|
|
185
|
+
state.activeCaptureStreams.delete(userId);
|
|
186
|
+
state.activeSpeakers.delete(userId);
|
|
187
|
+
onFinalize?.(activeCapture);
|
|
188
|
+
activeCapture.stream.destroy();
|
|
189
|
+
}, delayMs);
|
|
190
|
+
state.captureFinalizeTimers.set(userId, {
|
|
191
|
+
generation: capture.generation,
|
|
192
|
+
timer
|
|
193
|
+
});
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region extensions/discord/src/voice/receive-recovery.ts
|
|
198
|
+
const DECRYPT_FAILURE_WINDOW_MS = 3e4;
|
|
199
|
+
const DECRYPT_FAILURE_RECONNECT_THRESHOLD = 3;
|
|
200
|
+
const DECRYPT_FAILURE_MARKER = "DecryptionFailed(";
|
|
201
|
+
const DAVE_PASSTHROUGH_DISABLED_MARKER = "UnencryptedWhenPassthroughDisabled";
|
|
202
|
+
function createVoiceReceiveRecoveryState() {
|
|
203
|
+
return {
|
|
204
|
+
decryptFailureCount: 0,
|
|
205
|
+
lastDecryptFailureAt: 0,
|
|
206
|
+
decryptRecoveryInFlight: false
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function isAbortLikeReceiveError(err) {
|
|
210
|
+
if (!err || typeof err !== "object") return false;
|
|
211
|
+
const name = "name" in err && typeof err.name === "string" ? err.name : "";
|
|
212
|
+
const message = "message" in err && typeof err.message === "string" ? err.message : "";
|
|
213
|
+
return name === "AbortError" || message.includes("The operation was aborted") || message.includes("aborted");
|
|
214
|
+
}
|
|
215
|
+
function analyzeVoiceReceiveError(err) {
|
|
216
|
+
const message = formatErrorMessage(err);
|
|
217
|
+
const shouldAttemptPassthrough = message.includes(DAVE_PASSTHROUGH_DISABLED_MARKER);
|
|
218
|
+
return {
|
|
219
|
+
message,
|
|
220
|
+
isAbortLike: isAbortLikeReceiveError(err),
|
|
221
|
+
shouldAttemptPassthrough,
|
|
222
|
+
countsAsDecryptFailure: message.includes(DECRYPT_FAILURE_MARKER) || shouldAttemptPassthrough
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function noteVoiceDecryptFailure(state, now = Date.now()) {
|
|
226
|
+
if (now - state.lastDecryptFailureAt > DECRYPT_FAILURE_WINDOW_MS) state.decryptFailureCount = 0;
|
|
227
|
+
state.lastDecryptFailureAt = now;
|
|
228
|
+
state.decryptFailureCount += 1;
|
|
229
|
+
const firstFailure = state.decryptFailureCount === 1;
|
|
230
|
+
if (state.decryptFailureCount < DECRYPT_FAILURE_RECONNECT_THRESHOLD || state.decryptRecoveryInFlight) return {
|
|
231
|
+
firstFailure,
|
|
232
|
+
shouldRecover: false
|
|
233
|
+
};
|
|
234
|
+
state.decryptRecoveryInFlight = true;
|
|
235
|
+
resetVoiceReceiveRecoveryState(state);
|
|
236
|
+
return {
|
|
237
|
+
firstFailure,
|
|
238
|
+
shouldRecover: true
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function resetVoiceReceiveRecoveryState(state) {
|
|
242
|
+
state.decryptFailureCount = 0;
|
|
243
|
+
state.lastDecryptFailureAt = 0;
|
|
244
|
+
}
|
|
245
|
+
function finishVoiceDecryptRecovery(state) {
|
|
246
|
+
state.decryptRecoveryInFlight = false;
|
|
247
|
+
}
|
|
248
|
+
function enableDaveReceivePassthrough(params) {
|
|
249
|
+
const { target, sdk, reason, expirySeconds, onVerbose, onWarn } = params;
|
|
250
|
+
const networkingState = target.connection.state.networking?.state;
|
|
251
|
+
if (target.connection.state.status !== sdk.VoiceConnectionStatus.Ready || !networkingState || networkingState.code !== sdk.NetworkingStatusCode.Ready && networkingState.code !== sdk.NetworkingStatusCode.Resuming) return false;
|
|
252
|
+
const daveSession = networkingState.dave?.session;
|
|
253
|
+
if (!daveSession) return false;
|
|
254
|
+
try {
|
|
255
|
+
daveSession.setPassthroughMode(true, expirySeconds);
|
|
256
|
+
onVerbose(`enabled DAVE receive passthrough: guild ${target.guildId} channel ${target.channelId} expiry=${expirySeconds}s reason=${reason}`);
|
|
257
|
+
return true;
|
|
258
|
+
} catch (err) {
|
|
259
|
+
onWarn(`discord voice: failed to enable DAVE passthrough guild=${target.guildId} channel=${target.channelId} reason=${reason}: ${formatErrorMessage(err)}`);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region extensions/discord/src/voice/sdk-runtime.ts
|
|
265
|
+
let cachedDiscordVoiceSdk = null;
|
|
266
|
+
function loadDiscordVoiceSdk() {
|
|
267
|
+
if (cachedDiscordVoiceSdk) return cachedDiscordVoiceSdk;
|
|
268
|
+
cachedDiscordVoiceSdk = createRequire(import.meta.url)("@discordjs/voice");
|
|
269
|
+
return cachedDiscordVoiceSdk;
|
|
270
|
+
}
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region extensions/discord/src/voice/prompt.ts
|
|
273
|
+
const DISCORD_VOICE_SPOKEN_OUTPUT_CONTRACT = [
|
|
274
|
+
"Discord voice reply requirements:",
|
|
275
|
+
"- Return only the concise text that should be spoken aloud in the voice channel.",
|
|
276
|
+
"- Do not call the tts tool; Discord voice will synthesize and play the returned text.",
|
|
277
|
+
"- Do not reply with NO_REPLY unless no spoken response is appropriate.",
|
|
278
|
+
"- Keep the response brief and conversational."
|
|
279
|
+
].join("\n");
|
|
280
|
+
function formatVoiceIngressPrompt(transcript, speakerLabel) {
|
|
281
|
+
const cleanedTranscript = transcript.trim();
|
|
282
|
+
const cleanedLabel = speakerLabel?.trim();
|
|
283
|
+
return [DISCORD_VOICE_SPOKEN_OUTPUT_CONTRACT, cleanedLabel ? [`Voice transcript from speaker "${cleanedLabel}":`, cleanedTranscript].join("\n") : cleanedTranscript].join("\n\n");
|
|
284
|
+
}
|
|
285
|
+
const CAPTURE_FINALIZE_GRACE_MS = 1200;
|
|
286
|
+
const VOICE_CONNECT_READY_TIMEOUT_MS = 3e4;
|
|
287
|
+
const VOICE_RECONNECT_GRACE_MS = 15e3;
|
|
288
|
+
const PLAYBACK_READY_TIMEOUT_MS = 6e4;
|
|
289
|
+
const SPEAKING_READY_TIMEOUT_MS = 6e4;
|
|
290
|
+
function resolveVoiceTimeoutMs(value, fallbackMs) {
|
|
291
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return fallbackMs;
|
|
292
|
+
return Math.floor(value);
|
|
293
|
+
}
|
|
294
|
+
function logVoiceVerbose(message) {
|
|
295
|
+
logVerbose(`discord voice: ${message}`);
|
|
296
|
+
}
|
|
297
|
+
function isVoiceChannel(type) {
|
|
298
|
+
return type === discord_exports.ChannelType.GuildVoice || type === discord_exports.ChannelType.GuildStageVoice;
|
|
299
|
+
}
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region extensions/discord/src/voice/sanitize.ts
|
|
302
|
+
const SPEECH_EMOJI_RE = /(?:\p{Extended_Pictographic}(?:\uFE0F|\u200D|\p{Extended_Pictographic}|\p{Emoji_Modifier})*)+/gu;
|
|
303
|
+
function escapeRegExp(value) {
|
|
304
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
305
|
+
}
|
|
306
|
+
function stripEmojiForSpeech(text) {
|
|
307
|
+
return text.replace(SPEECH_EMOJI_RE, " ").replace(/\s+([?!.,:;])/g, "$1").replace(/[ \t]{2,}/g, " ").replace(/ *\n */g, "\n").trim();
|
|
308
|
+
}
|
|
309
|
+
function sanitizeVoiceReplyTextForSpeech(text, speakerLabel) {
|
|
310
|
+
let cleaned = stripInlineDirectiveTagsForDisplay(text).text.trim();
|
|
311
|
+
if (!cleaned) return "";
|
|
312
|
+
const label = speakerLabel?.trim();
|
|
313
|
+
if (label) {
|
|
314
|
+
const prefix = new RegExp(`^${escapeRegExp(label)}\\s*:\\s*`, "i");
|
|
315
|
+
cleaned = cleaned.replace(prefix, "").trim();
|
|
316
|
+
}
|
|
317
|
+
return stripEmojiForSpeech(cleaned);
|
|
318
|
+
}
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region extensions/discord/src/voice/tts.ts
|
|
321
|
+
function mergeTtsConfig(base, override) {
|
|
322
|
+
if (!override) return base;
|
|
323
|
+
const baseProviders = base.providers ?? {};
|
|
324
|
+
const overrideProviders = override.providers ?? {};
|
|
325
|
+
const mergedProviders = Object.fromEntries([...new Set([...Object.keys(baseProviders), ...Object.keys(overrideProviders)])].map((providerId) => {
|
|
326
|
+
const baseProvider = baseProviders[providerId] ?? {};
|
|
327
|
+
const overrideProvider = overrideProviders[providerId] ?? {};
|
|
328
|
+
return [providerId, {
|
|
329
|
+
...baseProvider,
|
|
330
|
+
...overrideProvider
|
|
331
|
+
}];
|
|
332
|
+
}));
|
|
333
|
+
return {
|
|
334
|
+
...base,
|
|
335
|
+
...override,
|
|
336
|
+
modelOverrides: {
|
|
337
|
+
...base.modelOverrides,
|
|
338
|
+
...override.modelOverrides
|
|
339
|
+
},
|
|
340
|
+
...Object.keys(mergedProviders).length === 0 ? {} : { providers: mergedProviders }
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function resolveVoiceTtsConfig(params) {
|
|
344
|
+
if (!params.override) return {
|
|
345
|
+
cfg: params.cfg,
|
|
346
|
+
resolved: resolveTtsConfig(params.cfg)
|
|
347
|
+
};
|
|
348
|
+
const merged = mergeTtsConfig(params.cfg.messages?.tts ?? {}, params.override);
|
|
349
|
+
const messages = params.cfg.messages ?? {};
|
|
350
|
+
const cfg = {
|
|
351
|
+
...params.cfg,
|
|
352
|
+
messages: {
|
|
353
|
+
...messages,
|
|
354
|
+
tts: merged
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
return {
|
|
358
|
+
cfg,
|
|
359
|
+
resolved: resolveTtsConfig(cfg)
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
async function transcribeVoiceAudio(params) {
|
|
363
|
+
return normalizeOptionalString((await getDiscordRuntime().mediaUnderstanding.transcribeAudioFile({
|
|
364
|
+
filePath: params.filePath,
|
|
365
|
+
cfg: params.cfg,
|
|
366
|
+
agentDir: resolveAgentDir(params.cfg, params.agentId),
|
|
367
|
+
mime: "audio/wav"
|
|
368
|
+
})).text);
|
|
369
|
+
}
|
|
370
|
+
async function synthesizeVoiceReplyAudio(params) {
|
|
371
|
+
const { cfg: ttsCfg, resolved: ttsConfig } = resolveVoiceTtsConfig({
|
|
372
|
+
cfg: params.cfg,
|
|
373
|
+
override: params.override
|
|
374
|
+
});
|
|
375
|
+
const directive = parseTtsDirectives(params.replyText, ttsConfig.modelOverrides, {
|
|
376
|
+
cfg: ttsCfg,
|
|
377
|
+
providerConfigs: ttsConfig.providerConfigs,
|
|
378
|
+
preferredProviderId: getTtsProvider(ttsConfig, resolveTtsPrefsPath(ttsConfig))
|
|
379
|
+
});
|
|
380
|
+
const speakText = sanitizeVoiceReplyTextForSpeech(directive.overrides.ttsText ?? directive.cleanedText.trim(), params.speakerLabel);
|
|
381
|
+
if (!speakText) return { status: "empty" };
|
|
382
|
+
const result = await getDiscordRuntime().tts.textToSpeech({
|
|
383
|
+
text: speakText,
|
|
384
|
+
cfg: ttsCfg,
|
|
385
|
+
channel: "discord",
|
|
386
|
+
overrides: directive.overrides
|
|
387
|
+
});
|
|
388
|
+
if (!result.success || !result.audioPath) return {
|
|
389
|
+
status: "failed",
|
|
390
|
+
error: result.error ?? "unknown error"
|
|
391
|
+
};
|
|
392
|
+
return {
|
|
393
|
+
status: "ok",
|
|
394
|
+
audioPath: result.audioPath,
|
|
395
|
+
speakText
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region extensions/discord/src/voice/segment.ts
|
|
400
|
+
const DISCORD_VOICE_MESSAGE_PROVIDER = "discord-voice";
|
|
401
|
+
const logger$1 = createSubsystemLogger("discord/voice");
|
|
402
|
+
async function processDiscordVoiceSegment(params) {
|
|
403
|
+
const { entry, wavPath, userId, durationSeconds } = params;
|
|
404
|
+
logVoiceVerbose(`segment processing (${durationSeconds.toFixed(2)}s): guild ${entry.guildId} channel ${entry.channelId}`);
|
|
405
|
+
if (!entry.guildName) entry.guildName = await params.fetchGuildName(entry.guildId);
|
|
406
|
+
const speaker = await params.speakerContext.resolveContext(entry.guildId, userId);
|
|
407
|
+
const speakerIdentity = await params.speakerContext.resolveIdentity(entry.guildId, userId);
|
|
408
|
+
const access = await authorizeDiscordVoiceIngress({
|
|
409
|
+
cfg: params.cfg,
|
|
410
|
+
discordConfig: params.discordConfig,
|
|
411
|
+
guildName: entry.guildName,
|
|
412
|
+
guildId: entry.guildId,
|
|
413
|
+
channelId: entry.channelId,
|
|
414
|
+
channelName: entry.channelName,
|
|
415
|
+
channelSlug: entry.channelName ? normalizeDiscordSlug(entry.channelName) : "",
|
|
416
|
+
channelLabel: formatMention({ channelId: entry.channelId }),
|
|
417
|
+
memberRoleIds: speakerIdentity.memberRoleIds,
|
|
418
|
+
ownerAllowFrom: params.ownerAllowFrom,
|
|
419
|
+
sender: {
|
|
420
|
+
id: speakerIdentity.id,
|
|
421
|
+
name: speakerIdentity.name,
|
|
422
|
+
tag: speakerIdentity.tag
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
if (!access.ok) {
|
|
426
|
+
logVoiceVerbose(`segment unauthorized: guild ${entry.guildId} channel ${entry.channelId} user ${userId} reason=${access.message}`);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const transcript = await transcribeVoiceAudio({
|
|
430
|
+
cfg: params.cfg,
|
|
431
|
+
agentId: entry.route.agentId,
|
|
432
|
+
filePath: wavPath
|
|
433
|
+
});
|
|
434
|
+
if (!transcript) {
|
|
435
|
+
logVoiceVerbose(`transcription empty: guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
logVoiceVerbose(`transcription ok (${transcript.length} chars): guild ${entry.guildId} channel ${entry.channelId}`);
|
|
439
|
+
const prompt = formatVoiceIngressPrompt(transcript, speaker.label);
|
|
440
|
+
const extraSystemPrompt = buildDiscordGroupSystemPrompt(access.channelConfig);
|
|
441
|
+
const modelOverride = normalizeOptionalString(params.discordConfig.voice?.model);
|
|
442
|
+
const replyText = ((await agentCommandFromIngress({
|
|
443
|
+
message: prompt,
|
|
444
|
+
sessionKey: entry.route.sessionKey,
|
|
445
|
+
agentId: entry.route.agentId,
|
|
446
|
+
messageChannel: "discord",
|
|
447
|
+
messageProvider: DISCORD_VOICE_MESSAGE_PROVIDER,
|
|
448
|
+
extraSystemPrompt,
|
|
449
|
+
senderIsOwner: speaker.senderIsOwner,
|
|
450
|
+
allowModelOverride: Boolean(modelOverride),
|
|
451
|
+
model: modelOverride,
|
|
452
|
+
deliver: false
|
|
453
|
+
}, params.runtime)).payloads ?? []).map((payload) => payload.text).filter((text) => typeof text === "string" && text.trim()).join("\n").trim();
|
|
454
|
+
if (!replyText) {
|
|
455
|
+
logVoiceVerbose(`reply empty: guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
logVoiceVerbose(`reply ok (${replyText.length} chars): guild ${entry.guildId} channel ${entry.channelId}`);
|
|
459
|
+
const voiceReplyAudio = await synthesizeVoiceReplyAudio({
|
|
460
|
+
cfg: params.cfg,
|
|
461
|
+
override: params.discordConfig.voice?.tts,
|
|
462
|
+
replyText,
|
|
463
|
+
speakerLabel: speaker.label
|
|
464
|
+
});
|
|
465
|
+
if (voiceReplyAudio.status === "empty") {
|
|
466
|
+
logVoiceVerbose(`tts skipped (empty): guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (voiceReplyAudio.status === "failed") {
|
|
470
|
+
logger$1.warn(`discord voice: TTS failed: ${voiceReplyAudio.error ?? "unknown error"}`);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
logVoiceVerbose(`tts ok (${voiceReplyAudio.speakText.length} chars): guild ${entry.guildId} channel ${entry.channelId}`);
|
|
474
|
+
params.enqueuePlayback(entry, async () => {
|
|
475
|
+
logVoiceVerbose(`playback start: guild ${entry.guildId} channel ${entry.channelId} file ${path.basename(voiceReplyAudio.audioPath)}`);
|
|
476
|
+
const voiceSdk = loadDiscordVoiceSdk();
|
|
477
|
+
const resource = voiceSdk.createAudioResource(voiceReplyAudio.audioPath);
|
|
478
|
+
entry.player.play(resource);
|
|
479
|
+
await voiceSdk.entersState(entry.player, voiceSdk.AudioPlayerStatus.Playing, PLAYBACK_READY_TIMEOUT_MS).catch(() => void 0);
|
|
480
|
+
await voiceSdk.entersState(entry.player, voiceSdk.AudioPlayerStatus.Idle, SPEAKING_READY_TIMEOUT_MS).catch(() => void 0);
|
|
481
|
+
logVoiceVerbose(`playback done: guild ${entry.guildId} channel ${entry.channelId}`);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
//#endregion
|
|
485
|
+
//#region extensions/discord/src/voice/speaker-context.ts
|
|
486
|
+
const SPEAKER_CONTEXT_CACHE_TTL_MS = 6e4;
|
|
487
|
+
var DiscordVoiceSpeakerContextResolver = class {
|
|
488
|
+
constructor(params) {
|
|
489
|
+
this.params = params;
|
|
490
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
491
|
+
}
|
|
492
|
+
async resolveContext(guildId, userId) {
|
|
493
|
+
const cached = this.getCachedContext(guildId, userId);
|
|
494
|
+
if (cached) return cached;
|
|
495
|
+
const identity = await this.resolveIdentity(guildId, userId);
|
|
496
|
+
const context = {
|
|
497
|
+
id: identity.id,
|
|
498
|
+
label: identity.label,
|
|
499
|
+
name: identity.name,
|
|
500
|
+
tag: identity.tag,
|
|
501
|
+
senderIsOwner: this.resolveIsOwner(identity)
|
|
502
|
+
};
|
|
503
|
+
this.setCachedContext(guildId, userId, context);
|
|
504
|
+
return context;
|
|
505
|
+
}
|
|
506
|
+
async resolveIdentity(guildId, userId) {
|
|
507
|
+
try {
|
|
508
|
+
const member = await this.params.client.fetchMember(guildId, userId);
|
|
509
|
+
const username = member.user?.username ?? void 0;
|
|
510
|
+
return {
|
|
511
|
+
id: userId,
|
|
512
|
+
label: member.nickname ?? member.user?.globalName ?? username ?? userId,
|
|
513
|
+
name: username,
|
|
514
|
+
tag: member.user ? formatDiscordUserTag(member.user) : void 0,
|
|
515
|
+
memberRoleIds: Array.isArray(member.roles) ? member.roles.map((role) => typeof role === "string" ? role : typeof role?.id === "string" ? role.id : "").filter(Boolean) : []
|
|
516
|
+
};
|
|
517
|
+
} catch {
|
|
518
|
+
try {
|
|
519
|
+
const user = await this.params.client.fetchUser(userId);
|
|
520
|
+
const username = user.username ?? void 0;
|
|
521
|
+
return {
|
|
522
|
+
id: userId,
|
|
523
|
+
label: user.globalName ?? username ?? userId,
|
|
524
|
+
name: username,
|
|
525
|
+
tag: formatDiscordUserTag(user),
|
|
526
|
+
memberRoleIds: []
|
|
527
|
+
};
|
|
528
|
+
} catch {
|
|
529
|
+
return {
|
|
530
|
+
id: userId,
|
|
531
|
+
label: userId,
|
|
532
|
+
memberRoleIds: []
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
resolveIsOwner(identity) {
|
|
538
|
+
return resolveDiscordOwnerAccess({
|
|
539
|
+
allowFrom: this.params.ownerAllowFrom,
|
|
540
|
+
sender: {
|
|
541
|
+
id: identity.id,
|
|
542
|
+
name: identity.name,
|
|
543
|
+
tag: identity.tag
|
|
544
|
+
},
|
|
545
|
+
allowNameMatching: false
|
|
546
|
+
}).ownerAllowed;
|
|
547
|
+
}
|
|
548
|
+
resolveCacheKey(guildId, userId) {
|
|
549
|
+
return `${guildId}:${userId}`;
|
|
550
|
+
}
|
|
551
|
+
getCachedContext(guildId, userId) {
|
|
552
|
+
const key = this.resolveCacheKey(guildId, userId);
|
|
553
|
+
const cached = this.cache.get(key);
|
|
554
|
+
if (!cached) return;
|
|
555
|
+
if (cached.expiresAt <= Date.now()) {
|
|
556
|
+
this.cache.delete(key);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
return {
|
|
560
|
+
id: cached.id,
|
|
561
|
+
label: cached.label,
|
|
562
|
+
name: cached.name,
|
|
563
|
+
tag: cached.tag,
|
|
564
|
+
senderIsOwner: cached.senderIsOwner
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
setCachedContext(guildId, userId, context) {
|
|
568
|
+
const key = this.resolveCacheKey(guildId, userId);
|
|
569
|
+
this.cache.set(key, {
|
|
570
|
+
...context,
|
|
571
|
+
expiresAt: Date.now() + SPEAKER_CONTEXT_CACHE_TTL_MS
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
//#endregion
|
|
576
|
+
//#region extensions/discord/src/voice/manager.ts
|
|
577
|
+
const logger = createSubsystemLogger("discord/voice");
|
|
578
|
+
function isVoiceConnectionDestroyed(connection, voiceSdk) {
|
|
579
|
+
return connection.state.status === voiceSdk.VoiceConnectionStatus.Destroyed;
|
|
580
|
+
}
|
|
581
|
+
function destroyVoiceConnectionSafely(params) {
|
|
582
|
+
if (isVoiceConnectionDestroyed(params.connection, params.voiceSdk)) {
|
|
583
|
+
logVoiceVerbose(`destroy skipped: ${params.reason}; connection already destroyed`);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
params.connection.destroy();
|
|
588
|
+
} catch (err) {
|
|
589
|
+
const message = formatErrorMessage(err);
|
|
590
|
+
if (message.includes("already been destroyed")) {
|
|
591
|
+
logVoiceVerbose(`destroy skipped: ${params.reason}; ${message}`);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
logger.warn(`discord voice: destroy failed: ${params.reason}: ${message}`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function startAutoJoin(manager) {
|
|
598
|
+
manager.autoJoin().catch((err) => logger.warn(`discord voice: autoJoin failed: ${formatErrorMessage(err)}`));
|
|
599
|
+
}
|
|
600
|
+
var DiscordVoiceManager$1 = class {
|
|
601
|
+
constructor(params) {
|
|
602
|
+
this.params = params;
|
|
603
|
+
this.sessions = /* @__PURE__ */ new Map();
|
|
604
|
+
this.autoJoinTask = null;
|
|
605
|
+
this.botUserId = params.botUserId;
|
|
606
|
+
this.voiceEnabled = resolveDiscordVoiceEnabled(params.discordConfig.voice);
|
|
607
|
+
this.ownerAllowFrom = resolveDiscordAccountAllowFrom({
|
|
608
|
+
cfg: params.cfg,
|
|
609
|
+
accountId: params.accountId
|
|
610
|
+
}) ?? params.discordConfig.allowFrom ?? params.discordConfig.dm?.allowFrom ?? [];
|
|
611
|
+
this.speakerContext = new DiscordVoiceSpeakerContextResolver({
|
|
612
|
+
client: params.client,
|
|
613
|
+
ownerAllowFrom: this.ownerAllowFrom
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
setBotUserId(id) {
|
|
617
|
+
if (id) this.botUserId = id;
|
|
618
|
+
}
|
|
619
|
+
isEnabled() {
|
|
620
|
+
return this.voiceEnabled;
|
|
621
|
+
}
|
|
622
|
+
async autoJoin() {
|
|
623
|
+
if (!this.voiceEnabled) return;
|
|
624
|
+
if (this.autoJoinTask) return this.autoJoinTask;
|
|
625
|
+
this.autoJoinTask = (async () => {
|
|
626
|
+
const entries = this.params.discordConfig.voice?.autoJoin ?? [];
|
|
627
|
+
logVoiceVerbose(`autoJoin: ${entries.length} entries`);
|
|
628
|
+
const seenGuilds = /* @__PURE__ */ new Set();
|
|
629
|
+
for (const entry of entries) {
|
|
630
|
+
const guildId = entry.guildId.trim();
|
|
631
|
+
if (!guildId) continue;
|
|
632
|
+
if (seenGuilds.has(guildId)) {
|
|
633
|
+
logger.warn(`discord voice: autoJoin has multiple entries for guild ${guildId}; skipping`);
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
seenGuilds.add(guildId);
|
|
637
|
+
logVoiceVerbose(`autoJoin: joining guild ${guildId} channel ${entry.channelId}`);
|
|
638
|
+
await this.join({
|
|
639
|
+
guildId: entry.guildId,
|
|
640
|
+
channelId: entry.channelId
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
})().finally(() => {
|
|
644
|
+
this.autoJoinTask = null;
|
|
645
|
+
});
|
|
646
|
+
return this.autoJoinTask;
|
|
647
|
+
}
|
|
648
|
+
status() {
|
|
649
|
+
return Array.from(this.sessions.values()).map((session) => ({
|
|
650
|
+
ok: true,
|
|
651
|
+
message: `connected: guild ${session.guildId} channel ${session.channelId}`,
|
|
652
|
+
guildId: session.guildId,
|
|
653
|
+
channelId: session.channelId
|
|
654
|
+
}));
|
|
655
|
+
}
|
|
656
|
+
async join(params) {
|
|
657
|
+
if (!this.voiceEnabled) return {
|
|
658
|
+
ok: false,
|
|
659
|
+
message: "Discord voice is disabled (channels.discord.voice.enabled)."
|
|
660
|
+
};
|
|
661
|
+
const guildId = params.guildId.trim();
|
|
662
|
+
const channelId = params.channelId.trim();
|
|
663
|
+
if (!guildId || !channelId) return {
|
|
664
|
+
ok: false,
|
|
665
|
+
message: "Missing guildId or channelId."
|
|
666
|
+
};
|
|
667
|
+
logVoiceVerbose(`join requested: guild ${guildId} channel ${channelId}`);
|
|
668
|
+
const existing = this.sessions.get(guildId);
|
|
669
|
+
if (existing && existing.channelId === channelId) {
|
|
670
|
+
logVoiceVerbose(`join: already connected to guild ${guildId} channel ${channelId}`);
|
|
671
|
+
return {
|
|
672
|
+
ok: true,
|
|
673
|
+
message: `Already connected to ${formatMention({ channelId })}.`,
|
|
674
|
+
guildId,
|
|
675
|
+
channelId
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
if (existing) {
|
|
679
|
+
logVoiceVerbose(`join: replacing existing session for guild ${guildId}`);
|
|
680
|
+
await this.leave({ guildId });
|
|
681
|
+
}
|
|
682
|
+
const channelInfo = await this.params.client.fetchChannel(channelId).catch(() => null);
|
|
683
|
+
if (!channelInfo || "type" in channelInfo && !isVoiceChannel(channelInfo.type)) return {
|
|
684
|
+
ok: false,
|
|
685
|
+
message: `Channel ${channelId} is not a voice channel.`
|
|
686
|
+
};
|
|
687
|
+
const channelGuildId = "guildId" in channelInfo ? channelInfo.guildId : void 0;
|
|
688
|
+
if (channelGuildId && channelGuildId !== guildId) return {
|
|
689
|
+
ok: false,
|
|
690
|
+
message: "Voice channel is not in this guild."
|
|
691
|
+
};
|
|
692
|
+
const voicePlugin = this.params.client.getPlugin("voice");
|
|
693
|
+
if (!voicePlugin) return {
|
|
694
|
+
ok: false,
|
|
695
|
+
message: "Discord voice plugin is not available."
|
|
696
|
+
};
|
|
697
|
+
const voiceConfig = this.params.discordConfig.voice;
|
|
698
|
+
const adapterCreator = voicePlugin.getGatewayAdapterCreator(guildId);
|
|
699
|
+
const daveEncryption = voiceConfig?.daveEncryption;
|
|
700
|
+
const decryptionFailureTolerance = voiceConfig?.decryptionFailureTolerance;
|
|
701
|
+
const connectReadyTimeoutMs = resolveVoiceTimeoutMs(voiceConfig?.connectTimeoutMs, VOICE_CONNECT_READY_TIMEOUT_MS);
|
|
702
|
+
const reconnectGraceMs = resolveVoiceTimeoutMs(voiceConfig?.reconnectGraceMs, VOICE_RECONNECT_GRACE_MS);
|
|
703
|
+
logVoiceVerbose(`join: DAVE settings encryption=${daveEncryption === false ? "off" : "on"} tolerance=${decryptionFailureTolerance ?? "default"} connectTimeout=${connectReadyTimeoutMs}ms reconnectGrace=${reconnectGraceMs}ms`);
|
|
704
|
+
const voiceSdk = loadDiscordVoiceSdk();
|
|
705
|
+
const existingEntry = this.sessions.get(guildId);
|
|
706
|
+
if (existingEntry) {
|
|
707
|
+
existingEntry.stop();
|
|
708
|
+
this.sessions.delete(guildId);
|
|
709
|
+
}
|
|
710
|
+
const staleConnection = voiceSdk.getVoiceConnection(guildId);
|
|
711
|
+
if (staleConnection) destroyVoiceConnectionSafely({
|
|
712
|
+
connection: staleConnection,
|
|
713
|
+
voiceSdk,
|
|
714
|
+
reason: `stale connection before join guild ${guildId}`
|
|
715
|
+
});
|
|
716
|
+
const connection = voiceSdk.joinVoiceChannel({
|
|
717
|
+
channelId,
|
|
718
|
+
guildId,
|
|
719
|
+
adapterCreator,
|
|
720
|
+
selfDeaf: false,
|
|
721
|
+
selfMute: false,
|
|
722
|
+
daveEncryption,
|
|
723
|
+
decryptionFailureTolerance
|
|
724
|
+
});
|
|
725
|
+
try {
|
|
726
|
+
await voiceSdk.entersState(connection, voiceSdk.VoiceConnectionStatus.Ready, connectReadyTimeoutMs);
|
|
727
|
+
logVoiceVerbose(`join: connected to guild ${guildId} channel ${channelId}`);
|
|
728
|
+
} catch (err) {
|
|
729
|
+
logger.warn(`discord voice: join failed before ready: guild ${guildId} channel ${channelId} timeout=${connectReadyTimeoutMs}ms error=${formatErrorMessage(err)}`);
|
|
730
|
+
destroyVoiceConnectionSafely({
|
|
731
|
+
connection,
|
|
732
|
+
voiceSdk,
|
|
733
|
+
reason: `failed join cleanup guild ${guildId} channel ${channelId}`
|
|
734
|
+
});
|
|
735
|
+
return {
|
|
736
|
+
ok: false,
|
|
737
|
+
message: `Failed to join voice channel: ${formatErrorMessage(err)}`
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
const sessionChannelId = channelInfo?.id ?? channelId;
|
|
741
|
+
if (sessionChannelId !== channelId) logVoiceVerbose(`join: using session channel ${sessionChannelId} for voice channel ${channelId}`);
|
|
742
|
+
const route = resolveAgentRoute({
|
|
743
|
+
cfg: this.params.cfg,
|
|
744
|
+
channel: "discord",
|
|
745
|
+
accountId: this.params.accountId,
|
|
746
|
+
guildId,
|
|
747
|
+
peer: {
|
|
748
|
+
kind: "channel",
|
|
749
|
+
id: sessionChannelId
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
const player = voiceSdk.createAudioPlayer();
|
|
753
|
+
connection.subscribe(player);
|
|
754
|
+
let speakingHandler;
|
|
755
|
+
let speakingEndHandler;
|
|
756
|
+
let disconnectedHandler;
|
|
757
|
+
let destroyedHandler;
|
|
758
|
+
let playerErrorHandler;
|
|
759
|
+
const clearSessionIfCurrent = () => {
|
|
760
|
+
if (this.sessions.get(guildId)?.connection === connection) this.sessions.delete(guildId);
|
|
761
|
+
};
|
|
762
|
+
const entry = {
|
|
763
|
+
guildId,
|
|
764
|
+
guildName: channelInfo && "guild" in channelInfo && channelInfo.guild && typeof channelInfo.guild.name === "string" ? channelInfo.guild.name : void 0,
|
|
765
|
+
channelId,
|
|
766
|
+
channelName: channelInfo && "name" in channelInfo && typeof channelInfo.name === "string" ? channelInfo.name : void 0,
|
|
767
|
+
sessionChannelId,
|
|
768
|
+
route,
|
|
769
|
+
connection,
|
|
770
|
+
player,
|
|
771
|
+
playbackQueue: Promise.resolve(),
|
|
772
|
+
processingQueue: Promise.resolve(),
|
|
773
|
+
capture: createVoiceCaptureState(),
|
|
774
|
+
receiveRecovery: createVoiceReceiveRecoveryState(),
|
|
775
|
+
stop: () => {
|
|
776
|
+
if (speakingHandler) connection.receiver.speaking.off("start", speakingHandler);
|
|
777
|
+
if (speakingEndHandler) connection.receiver.speaking.off("end", speakingEndHandler);
|
|
778
|
+
stopVoiceCaptureState(entry.capture);
|
|
779
|
+
if (disconnectedHandler) connection.off(voiceSdk.VoiceConnectionStatus.Disconnected, disconnectedHandler);
|
|
780
|
+
if (destroyedHandler) connection.off(voiceSdk.VoiceConnectionStatus.Destroyed, destroyedHandler);
|
|
781
|
+
if (playerErrorHandler) player.off("error", playerErrorHandler);
|
|
782
|
+
player.stop();
|
|
783
|
+
destroyVoiceConnectionSafely({
|
|
784
|
+
connection,
|
|
785
|
+
voiceSdk,
|
|
786
|
+
reason: `stop guild ${guildId} channel ${channelId}`
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
speakingHandler = (userId) => {
|
|
791
|
+
this.handleSpeakingStart(entry, userId).catch((err) => {
|
|
792
|
+
logger.warn(`discord voice: capture failed: ${formatErrorMessage(err)}`);
|
|
793
|
+
});
|
|
794
|
+
};
|
|
795
|
+
speakingEndHandler = (userId) => {
|
|
796
|
+
this.scheduleCaptureFinalize(entry, userId, "speaker end");
|
|
797
|
+
};
|
|
798
|
+
disconnectedHandler = async () => {
|
|
799
|
+
try {
|
|
800
|
+
logVoiceVerbose(`disconnected: attempting recovery guild ${guildId} channel ${channelId} grace=${reconnectGraceMs}ms`);
|
|
801
|
+
await Promise.race([voiceSdk.entersState(connection, voiceSdk.VoiceConnectionStatus.Signalling, reconnectGraceMs), voiceSdk.entersState(connection, voiceSdk.VoiceConnectionStatus.Connecting, reconnectGraceMs)]);
|
|
802
|
+
logVoiceVerbose(`disconnected: recovery started guild ${guildId} channel ${channelId}`);
|
|
803
|
+
} catch (err) {
|
|
804
|
+
logger.warn(`discord voice: disconnect recovery failed: guild ${guildId} channel ${channelId} timeout=${reconnectGraceMs}ms error=${formatErrorMessage(err)}; destroying connection`);
|
|
805
|
+
clearSessionIfCurrent();
|
|
806
|
+
destroyVoiceConnectionSafely({
|
|
807
|
+
connection,
|
|
808
|
+
voiceSdk,
|
|
809
|
+
reason: `disconnect recovery failed guild ${guildId} channel ${channelId}`
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
destroyedHandler = () => {
|
|
814
|
+
clearSessionIfCurrent();
|
|
815
|
+
};
|
|
816
|
+
playerErrorHandler = (err) => {
|
|
817
|
+
logger.warn(`discord voice: playback error: ${formatErrorMessage(err)}`);
|
|
818
|
+
};
|
|
819
|
+
this.enableDaveReceivePassthrough(entry, "post-join warmup", 30);
|
|
820
|
+
connection.receiver.speaking.on("start", speakingHandler);
|
|
821
|
+
connection.receiver.speaking.on("end", speakingEndHandler);
|
|
822
|
+
connection.on(voiceSdk.VoiceConnectionStatus.Disconnected, disconnectedHandler);
|
|
823
|
+
connection.on(voiceSdk.VoiceConnectionStatus.Destroyed, destroyedHandler);
|
|
824
|
+
player.on("error", playerErrorHandler);
|
|
825
|
+
this.sessions.set(guildId, entry);
|
|
826
|
+
return {
|
|
827
|
+
ok: true,
|
|
828
|
+
message: `Joined ${formatMention({ channelId })}.`,
|
|
829
|
+
guildId,
|
|
830
|
+
channelId
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
async leave(params) {
|
|
834
|
+
const guildId = params.guildId.trim();
|
|
835
|
+
logVoiceVerbose(`leave requested: guild ${guildId} channel ${params.channelId ?? "current"}`);
|
|
836
|
+
const entry = this.sessions.get(guildId);
|
|
837
|
+
if (!entry) return {
|
|
838
|
+
ok: false,
|
|
839
|
+
message: "Not connected to a voice channel."
|
|
840
|
+
};
|
|
841
|
+
if (params.channelId && params.channelId !== entry.channelId) return {
|
|
842
|
+
ok: false,
|
|
843
|
+
message: "Not connected to that voice channel."
|
|
844
|
+
};
|
|
845
|
+
entry.stop();
|
|
846
|
+
this.sessions.delete(guildId);
|
|
847
|
+
logVoiceVerbose(`leave: disconnected from guild ${guildId} channel ${entry.channelId}`);
|
|
848
|
+
return {
|
|
849
|
+
ok: true,
|
|
850
|
+
message: `Left ${formatMention({ channelId: entry.channelId })}.`,
|
|
851
|
+
guildId,
|
|
852
|
+
channelId: entry.channelId
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
async destroy() {
|
|
856
|
+
for (const entry of this.sessions.values()) entry.stop();
|
|
857
|
+
this.sessions.clear();
|
|
858
|
+
}
|
|
859
|
+
enqueueProcessing(entry, task) {
|
|
860
|
+
entry.processingQueue = entry.processingQueue.then(task).catch((err) => logger.warn(`discord voice: processing failed: ${formatErrorMessage(err)}`));
|
|
861
|
+
}
|
|
862
|
+
enqueuePlayback(entry, task) {
|
|
863
|
+
entry.playbackQueue = entry.playbackQueue.then(task).catch((err) => logger.warn(`discord voice: playback failed: ${formatErrorMessage(err)}`));
|
|
864
|
+
}
|
|
865
|
+
clearCaptureFinalizeTimer(entry, userId, generation) {
|
|
866
|
+
return clearVoiceCaptureFinalizeTimer(entry.capture, userId, generation);
|
|
867
|
+
}
|
|
868
|
+
scheduleCaptureFinalize(entry, userId, reason) {
|
|
869
|
+
scheduleVoiceCaptureFinalize({
|
|
870
|
+
state: entry.capture,
|
|
871
|
+
userId,
|
|
872
|
+
delayMs: CAPTURE_FINALIZE_GRACE_MS,
|
|
873
|
+
onFinalize: () => {
|
|
874
|
+
logVoiceVerbose(`capture finalize: guild ${entry.guildId} channel ${entry.channelId} user ${userId} reason=${reason} grace=${CAPTURE_FINALIZE_GRACE_MS}ms`);
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
async handleSpeakingStart(entry, userId) {
|
|
879
|
+
if (!userId) return;
|
|
880
|
+
if (this.botUserId && userId === this.botUserId) return;
|
|
881
|
+
if (isVoiceCaptureActive(entry.capture, userId)) {
|
|
882
|
+
const activeCapture = getActiveVoiceCapture(entry.capture, userId);
|
|
883
|
+
const extended = activeCapture ? this.clearCaptureFinalizeTimer(entry, userId, activeCapture.generation) : false;
|
|
884
|
+
logVoiceVerbose(`capture start ignored (already active): guild ${entry.guildId} channel ${entry.channelId} user ${userId}${extended ? " (finalize canceled)" : ""}`);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
logVoiceVerbose(`capture start: guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
|
|
888
|
+
const voiceSdk = loadDiscordVoiceSdk();
|
|
889
|
+
this.enableDaveReceivePassthrough(entry, `speaker ${userId} start`, 15);
|
|
890
|
+
if (entry.player.state.status === voiceSdk.AudioPlayerStatus.Playing) entry.player.stop(true);
|
|
891
|
+
const stream = entry.connection.receiver.subscribe(userId, { end: { behavior: voiceSdk.EndBehaviorType.Manual } });
|
|
892
|
+
const generation = beginVoiceCapture(entry.capture, userId, stream);
|
|
893
|
+
let streamAborted = false;
|
|
894
|
+
stream.on("error", (err) => {
|
|
895
|
+
streamAborted = analyzeVoiceReceiveError(err).isAbortLike;
|
|
896
|
+
this.handleReceiveError(entry, err);
|
|
897
|
+
});
|
|
898
|
+
try {
|
|
899
|
+
const pcm = await decodeOpusStream(stream, {
|
|
900
|
+
onVerbose: logVoiceVerbose,
|
|
901
|
+
onWarn: (message) => logger.warn(message)
|
|
902
|
+
});
|
|
903
|
+
if (pcm.length === 0) {
|
|
904
|
+
logVoiceVerbose(`capture empty: guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
this.resetDecryptFailureState(entry);
|
|
908
|
+
const { path: wavPath, durationSeconds } = await writeVoiceWavFile(pcm);
|
|
909
|
+
if (durationSeconds < (streamAborted ? .2 : .35)) {
|
|
910
|
+
logVoiceVerbose(`capture too short (${durationSeconds.toFixed(2)}s): guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
logVoiceVerbose(`capture ready (${durationSeconds.toFixed(2)}s): guild ${entry.guildId} channel ${entry.channelId} user ${userId}`);
|
|
914
|
+
this.enqueueProcessing(entry, async () => {
|
|
915
|
+
await this.processSegment({
|
|
916
|
+
entry,
|
|
917
|
+
wavPath,
|
|
918
|
+
userId,
|
|
919
|
+
durationSeconds
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
} finally {
|
|
923
|
+
finishVoiceCapture(entry.capture, userId, generation);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
async processSegment(params) {
|
|
927
|
+
await processDiscordVoiceSegment({
|
|
928
|
+
...params,
|
|
929
|
+
cfg: this.params.cfg,
|
|
930
|
+
discordConfig: this.params.discordConfig,
|
|
931
|
+
ownerAllowFrom: this.ownerAllowFrom,
|
|
932
|
+
runtime: this.params.runtime,
|
|
933
|
+
speakerContext: this.speakerContext,
|
|
934
|
+
fetchGuildName: async (guildId) => {
|
|
935
|
+
const guild = await this.params.client.fetchGuild(guildId).catch(() => null);
|
|
936
|
+
return guild && typeof guild.name === "string" && guild.name.trim() ? guild.name : void 0;
|
|
937
|
+
},
|
|
938
|
+
enqueuePlayback: (entry, task) => {
|
|
939
|
+
this.enqueuePlayback(entry, task);
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
handleReceiveError(entry, err) {
|
|
944
|
+
const analysis = analyzeVoiceReceiveError(err);
|
|
945
|
+
logger.warn(`discord voice: receive error: ${analysis.message}`);
|
|
946
|
+
if (analysis.shouldAttemptPassthrough) this.enableDaveReceivePassthrough(entry, "receive decrypt error", 15);
|
|
947
|
+
if (!analysis.countsAsDecryptFailure) return;
|
|
948
|
+
const decryptFailure = noteVoiceDecryptFailure(entry.receiveRecovery);
|
|
949
|
+
if (decryptFailure.firstFailure) logger.warn("discord voice: DAVE decrypt failures detected; voice receive may be unstable (upstream: discordjs/discord.js#11419)");
|
|
950
|
+
if (!decryptFailure.shouldRecover) return;
|
|
951
|
+
this.recoverFromDecryptFailures(entry).catch((recoverErr) => logger.warn(`discord voice: decrypt recovery failed: ${formatErrorMessage(recoverErr)}`)).finally(() => {
|
|
952
|
+
finishVoiceDecryptRecovery(entry.receiveRecovery);
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
enableDaveReceivePassthrough(entry, reason, expirySeconds) {
|
|
956
|
+
const voiceSdk = loadDiscordVoiceSdk();
|
|
957
|
+
return enableDaveReceivePassthrough({
|
|
958
|
+
target: {
|
|
959
|
+
guildId: entry.guildId,
|
|
960
|
+
channelId: entry.channelId,
|
|
961
|
+
connection: entry.connection
|
|
962
|
+
},
|
|
963
|
+
sdk: {
|
|
964
|
+
VoiceConnectionStatus: { Ready: voiceSdk.VoiceConnectionStatus.Ready },
|
|
965
|
+
NetworkingStatusCode: {
|
|
966
|
+
Ready: voiceSdk.NetworkingStatusCode.Ready,
|
|
967
|
+
Resuming: voiceSdk.NetworkingStatusCode.Resuming
|
|
968
|
+
}
|
|
969
|
+
},
|
|
970
|
+
reason,
|
|
971
|
+
expirySeconds,
|
|
972
|
+
onVerbose: logVoiceVerbose,
|
|
973
|
+
onWarn: (message) => logger.warn(message)
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
resetDecryptFailureState(entry) {
|
|
977
|
+
resetVoiceReceiveRecoveryState(entry.receiveRecovery);
|
|
978
|
+
}
|
|
979
|
+
async recoverFromDecryptFailures(entry) {
|
|
980
|
+
const active = this.sessions.get(entry.guildId);
|
|
981
|
+
if (!active || active.connection !== entry.connection) return;
|
|
982
|
+
logger.warn(`discord voice: repeated decrypt failures; attempting rejoin for guild ${entry.guildId} channel ${entry.channelId}`);
|
|
983
|
+
const leaveResult = await this.leave({ guildId: entry.guildId });
|
|
984
|
+
if (!leaveResult.ok) {
|
|
985
|
+
logger.warn(`discord voice: decrypt recovery leave failed: ${leaveResult.message}`);
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
const result = await this.join({
|
|
989
|
+
guildId: entry.guildId,
|
|
990
|
+
channelId: entry.channelId
|
|
991
|
+
});
|
|
992
|
+
if (!result.ok) logger.warn(`discord voice: rejoin after decrypt failures failed: ${result.message}`);
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
var DiscordVoiceReadyListener$1 = class extends ReadyListener {
|
|
996
|
+
constructor(manager) {
|
|
997
|
+
super();
|
|
998
|
+
this.manager = manager;
|
|
999
|
+
}
|
|
1000
|
+
async handle(_data, _client) {
|
|
1001
|
+
startAutoJoin(this.manager);
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
var DiscordVoiceResumedListener$1 = class extends ResumedListener {
|
|
1005
|
+
constructor(manager) {
|
|
1006
|
+
super();
|
|
1007
|
+
this.manager = manager;
|
|
1008
|
+
}
|
|
1009
|
+
async handle(_data, _client) {
|
|
1010
|
+
startAutoJoin(this.manager);
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
//#endregion
|
|
1014
|
+
//#region extensions/discord/src/voice/manager.runtime.ts
|
|
1015
|
+
var DiscordVoiceManager = class extends DiscordVoiceManager$1 {};
|
|
1016
|
+
var DiscordVoiceReadyListener = class extends DiscordVoiceReadyListener$1 {};
|
|
1017
|
+
var DiscordVoiceResumedListener = class extends DiscordVoiceResumedListener$1 {};
|
|
1018
|
+
//#endregion
|
|
1019
|
+
export { DiscordVoiceManager, DiscordVoiceReadyListener, DiscordVoiceResumedListener };
|