fix(discord): make opus optional and log fallback

This commit is contained in:
Peter Steinberger
2026-02-22 18:45:31 +01:00
parent 24fd8cbdc8
commit 3c6a15ce98
4 changed files with 53 additions and 35 deletions

View File

@@ -46,6 +46,7 @@ Docs: https://docs.openclaw.ai
- Memory/Remote HTTP: centralize remote memory HTTP calls behind a shared guarded helper (`withRemoteHttpResponse`) so embeddings and batch flows use one request/release path.
- Memory/Embeddings: apply configured remote-base host pinning (`allowedHostnames`) across OpenAI/Voyage/Gemini embedding requests to keep private/self-hosted endpoints working without cross-host drift. (#18198) Thanks @ianpcook.
- Memory/Batch: route OpenAI/Voyage/Gemini batch upload/create/status/download requests through the same guarded HTTP path for consistent SSRF policy enforcement.
- Install/Discord Voice: make `@discordjs/opus` an optional dependency so `openclaw` install/update no longer hard-fails when native Opus builds fail, while keeping `opusscript` as the runtime fallback decoder for Discord voice flows. (#23737, #23733, #23703)
- Signal/RPC: guard malformed Signal RPC JSON responses with a clear status-scoped error and add regression coverage for invalid JSON responses. (#22995) Thanks @adhitShet.
- Gateway/Subagents: guard gateway and subagent session-key/message trim paths against undefined inputs to prevent early `Cannot read properties of undefined (reading 'trim')` crashes during subagent spawn and wait flows.
- Agents/Workspace: guard `resolveUserPath` against undefined/null input to prevent `Cannot read properties of undefined (reading 'trim')` crashes when workspace paths are missing in embedded runner flows.

View File

@@ -142,7 +142,6 @@
"@aws-sdk/client-bedrock": "^3.995.0",
"@buape/carbon": "0.0.0-beta-20260216184201",
"@clack/prompts": "^1.0.1",
"@discordjs/opus": "^0.10.0",
"@discordjs/voice": "^0.19.0",
"@grammyjs/runner": "^2.0.3",
"@grammyjs/transformer-throttler": "^1.2.1",
@@ -217,6 +216,9 @@
"@napi-rs/canvas": "^0.1.89",
"node-llama-cpp": "3.15.1"
},
"optionalDependencies": {
"@discordjs/opus": "^0.10.0"
},
"engines": {
"node": ">=22.12.0"
},

51
pnpm-lock.yaml generated
View File

@@ -32,9 +32,6 @@ importers:
'@clack/prompts':
specifier: ^1.0.1
version: 1.0.1
'@discordjs/opus':
specifier: ^0.10.0
version: 0.10.0
'@discordjs/voice':
specifier: ^0.19.0
version: 0.19.0(@discordjs/opus@0.10.0)(opusscript@0.0.8)
@@ -243,6 +240,10 @@ importers:
vitest:
specifier: ^4.0.18
version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
optionalDependencies:
'@discordjs/opus':
specifier: ^0.10.0
version: 0.10.0
extensions/bluebubbles:
devDependencies:
@@ -6525,6 +6526,7 @@ snapshots:
transitivePeerDependencies:
- encoding
- supports-color
optional: true
'@discordjs/opus@0.10.0':
dependencies:
@@ -6533,6 +6535,7 @@ snapshots:
transitivePeerDependencies:
- encoding
- supports-color
optional: true
'@discordjs/voice@0.19.0(@discordjs/opus@0.10.0)(opusscript@0.0.8)':
dependencies:
@@ -6880,7 +6883,7 @@ snapshots:
'@larksuiteoapi/node-sdk@1.59.0':
dependencies:
axios: 1.13.5
axios: 1.13.5(debug@4.4.3)
lodash.identity: 3.0.0
lodash.merge: 4.6.2
lodash.pickby: 4.6.0
@@ -6896,7 +6899,7 @@ snapshots:
dependencies:
'@types/node': 24.10.13
optionalDependencies:
axios: 1.13.5
axios: 1.13.5(debug@4.4.3)
transitivePeerDependencies:
- debug
@@ -7085,7 +7088,7 @@ snapshots:
'@azure/core-auth': 1.10.1
'@azure/msal-node': 5.0.4
'@microsoft/agents-activity': 1.3.1
axios: 1.13.5
axios: 1.13.5(debug@4.4.3)
jsonwebtoken: 9.0.3
jwks-rsa: 3.2.2
object-path: 0.11.8
@@ -7987,7 +7990,7 @@ snapshots:
'@slack/types': 2.20.0
'@slack/web-api': 7.14.1
'@types/express': 5.0.6
axios: 1.13.5
axios: 1.13.5(debug@4.4.3)
express: 5.2.1
path-to-regexp: 8.3.0
raw-body: 3.0.2
@@ -8033,7 +8036,7 @@ snapshots:
'@slack/types': 2.20.0
'@types/node': 25.3.0
'@types/retry': 0.12.0
axios: 1.13.5
axios: 1.13.5(debug@4.4.3)
eventemitter3: 5.0.4
form-data: 2.5.4
is-electron: 2.2.2
@@ -8771,7 +8774,8 @@ snapshots:
curve25519-js: 0.0.4
protobufjs: 6.8.8
abbrev@1.1.1: {}
abbrev@1.1.1:
optional: true
abort-controller@3.0.0:
dependencies:
@@ -8798,6 +8802,7 @@ snapshots:
debug: 4.4.3
transitivePeerDependencies:
- supports-color
optional: true
agent-base@7.1.4: {}
@@ -8848,6 +8853,7 @@ snapshots:
dependencies:
delegates: 1.0.0
readable-stream: 3.6.2
optional: true
are-we-there-yet@3.0.1:
dependencies:
@@ -8922,14 +8928,6 @@ snapshots:
aws4@1.13.2: {}
axios@1.13.5:
dependencies:
follow-redirects: 1.15.11
form-data: 2.5.4
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
axios@1.13.5(debug@4.4.3):
dependencies:
follow-redirects: 1.15.11(debug@4.4.3)
@@ -9499,8 +9497,6 @@ snapshots:
flatbuffers@24.12.23: {}
follow-redirects@1.15.11: {}
follow-redirects@1.15.11(debug@4.4.3):
optionalDependencies:
debug: 4.4.3
@@ -9537,7 +9533,8 @@ snapshots:
jsonfile: 6.2.0
universalify: 2.0.1
fs.realpath@1.0.0: {}
fs.realpath@1.0.0:
optional: true
fsevents@2.3.2:
optional: true
@@ -9558,6 +9555,7 @@ snapshots:
string-width: 4.2.3
strip-ansi: 6.0.1
wide-align: 1.1.5
optional: true
gauge@4.0.4:
dependencies:
@@ -9652,6 +9650,7 @@ snapshots:
minimatch: 10.2.1
once: 1.4.0
path-is-absolute: 1.0.1
optional: true
google-auth-library@10.5.0:
dependencies:
@@ -9781,6 +9780,7 @@ snapshots:
debug: 4.4.3
transitivePeerDependencies:
- supports-color
optional: true
https-proxy-agent@7.0.6:
dependencies:
@@ -9816,6 +9816,7 @@ snapshots:
dependencies:
once: 1.4.0
wrappy: 1.0.2
optional: true
inherits@2.0.4: {}
@@ -10178,6 +10179,7 @@ snapshots:
make-dir@3.1.0:
dependencies:
semver: 6.3.1
optional: true
make-dir@4.0.0:
dependencies:
@@ -10386,6 +10388,7 @@ snapshots:
nopt@5.0.0:
dependencies:
abbrev: 1.1.1
optional: true
nostr-tools@2.23.1(typescript@5.9.3):
dependencies:
@@ -10407,6 +10410,7 @@ snapshots:
console-control-strings: 1.1.0
gauge: 3.0.2
set-blocking: 2.0.0
optional: true
npmlog@6.0.2:
dependencies:
@@ -10624,7 +10628,8 @@ snapshots:
partial-json@0.1.7: {}
path-is-absolute@1.0.1: {}
path-is-absolute@1.0.1:
optional: true
path-key@3.1.1: {}
@@ -10898,6 +10903,7 @@ snapshots:
rimraf@3.0.2:
dependencies:
glob: 7.2.3
optional: true
rimraf@5.0.10:
dependencies:
@@ -11002,7 +11008,8 @@ snapshots:
dependencies:
parseley: 0.12.1
semver@6.3.1: {}
semver@6.3.1:
optional: true
semver@7.7.4: {}

View File

@@ -146,25 +146,33 @@ type OpusDecoder = {
decode: (buffer: Buffer) => Buffer;
};
let warnedOpusFallback = false;
function createOpusDecoder(): { decoder: OpusDecoder; name: string } | null {
try {
const OpusScript = require("opusscript") as {
new (sampleRate: number, channels: number, application: number): OpusDecoder;
Application: { AUDIO: number };
};
const decoder = new OpusScript(SAMPLE_RATE, CHANNELS, OpusScript.Application.AUDIO);
return { decoder, name: "opusscript" };
} catch (err) {
logger.warn(`discord voice: opusscript init failed: ${formatErrorMessage(err)}`);
}
try {
const { OpusEncoder } = require("@discordjs/opus") as {
OpusEncoder: new (sampleRate: number, channels: number) => OpusDecoder;
};
const decoder = new OpusEncoder(SAMPLE_RATE, CHANNELS);
return { decoder, name: "@discordjs/opus" };
} catch (err) {
logger.warn(`discord voice: opus decoder init failed: ${formatErrorMessage(err)}`);
} catch (nativeErr) {
try {
const OpusScript = require("opusscript") as {
new (sampleRate: number, channels: number, application: number): OpusDecoder;
Application: { AUDIO: number };
};
const decoder = new OpusScript(SAMPLE_RATE, CHANNELS, OpusScript.Application.AUDIO);
if (!warnedOpusFallback) {
warnedOpusFallback = true;
logger.warn(
`discord voice: @discordjs/opus unavailable (${formatErrorMessage(nativeErr)}); using opusscript fallback`,
);
}
return { decoder, name: "opusscript" };
} catch (jsErr) {
logger.warn(`discord voice: opus decoder init failed: ${formatErrorMessage(nativeErr)}`);
logger.warn(`discord voice: opusscript init failed: ${formatErrorMessage(jsErr)}`);
}
}
return null;
}