@openclaw/discord 2026.8.1 → 2026.8.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/account-inspect-BY-ve3yH.js +45 -0
- package/dist/account-inspect-api.js +1 -1
- package/dist/{accounts-CfQ-vSyf.js → accounts-CwJQoLjM.js} +89 -45
- package/dist/action-runtime-api.js +1 -1
- package/dist/{activities-api-Dl-TOyOX.js → activities-api-CkKcHUB6.js} +14 -13
- package/dist/activities-api.js +1 -1
- package/dist/{allow-list-BDMimASD.js → allow-list-C2JuH-R0.js} +10 -6
- package/dist/{api-DNVbJyEy.js → api-BfmEP6wp.js} +6 -5
- package/dist/api.js +17 -17
- package/dist/{approval-handler.runtime-q4Zqo44V.js → approval-handler.runtime-DZM6Kt1Z.js} +8 -7
- package/dist/{audit-DgrvFPGt.js → audit-DIbQA4VT.js} +8 -7
- package/dist/{channel-BsQUalPw.js → channel-CarzU3bg.js} +24 -22
- package/dist/{channel-actions.runtime-UMGGIfSy.js → channel-actions.runtime-B2wMqeVK.js} +13 -11
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-rPs4dsk_.js → channel.setup-z_ewMVa9.js} +18 -18
- package/dist/{components-Dk35LTf_.js → components-gvRA0oaI.js} +1 -1
- package/dist/{config-schema-CxPqtVI1.js → config-schema-B797-Spt.js} +5 -4
- package/dist/{conversation-binding-route-BjvX4e35.js → conversation-binding-route-DPpBRpYI.js} +1 -1
- package/dist/{directory-config-B7DY_pNG.js → directory-config-AA8wQx0D.js} +2 -2
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-DuAXKkan.js → directory-live-D_kFz_Fj.js} +4 -4
- package/dist/{doctor-zQTtn6hm.js → doctor-QQTOkBfW.js} +5 -5
- package/dist/{doctor-contract-DsWvKIXF.js → doctor-contract-Co5vRCZ6.js} +9 -6
- package/dist/doctor-contract-api.js +3 -2
- package/dist/{handle-action.guild-admin-DZewwLr1.js → handle-action.guild-admin-Cc88ZGon.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{ingress-identity-CzZKcPK8.js → ingress-identity-BCzfPPYM.js} +1 -1
- package/dist/{message-handler-C5By3yl5.js → message-handler-CAeEpqWo.js} +14 -10
- package/dist/{message-handler.preflight-DrszWuHw.js → message-handler.preflight-CoKoE3dY.js} +24 -19
- package/dist/{message-handler.process-DyFPDLCt.js → message-handler.process-cdIJ_fDT.js} +56 -74
- package/dist/{message-text-Ca8M6TU8.js → message-text-C0K4-fkg.js} +9 -4
- package/dist/{outbound-adapter-Dif8p481.js → outbound-adapter-Ch0At5Ra.js} +34 -24
- package/dist/{outbound-components-wjv7yyAj.js → outbound-components-Bnt_7x7r.js} +3 -3
- package/dist/{outbound-session-route-Bn4uXaoE.js → outbound-session-route-CzzerX2K.js} +10 -7
- package/dist/{owner-access-UlYi9t0M.js → owner-access-yc1sX7Gx.js} +7 -6
- package/dist/{pluralkit-9EuEN4UD.js → pluralkit-B3cUVI7i.js} +2 -2
- package/dist/{probe-n6ZXxK8V.js → probe-3ovFAp3b.js} +3 -3
- package/dist/{probe.runtime-CN4bdPfZ.js → probe.runtime-BckHn3cJ.js} +1 -1
- package/dist/{provider-LXiOcJrH.js → provider-CjI8BSzS.js} +97 -76
- package/dist/{provider-session.runtime-DSNe7r2W.js → provider-session.runtime-DdKRMcE9.js} +3 -3
- package/dist/provider.runtime-Bo1WbLLr.js +2 -0
- package/dist/{realtime-session.runtime-DOViOIjY.js → realtime-session.runtime-37IC-p5K.js} +5 -5
- package/dist/{resolve-allowlist-common-BLoYaxEG.js → resolve-allowlist-common-BJ5UZNNT.js} +3 -3
- package/dist/{resolve-channels-D2yl_2CN.js → resolve-channels-BexR2eUn.js} +4 -4
- package/dist/{resolve-users-BLxfqajZ.js → resolve-users-BtXBCH5W.js} +5 -4
- package/dist/{retry-after-DdLPmPoa.js → retry-after-BrLjLUJi.js} +2 -1
- package/dist/{retry-DqHCW6PV.js → retry-t5RcEsPk.js} +37 -29
- package/dist/{runtime-DrxVBTtp.js → runtime-BNlDfLwS.js} +2 -2
- package/dist/{runtime-BYamRVPN.js → runtime-DydyfxST.js} +126 -92
- package/dist/runtime-api.js +19 -19
- package/dist/{secret-config-contract-B3i6WxV5.js → secret-config-contract-DFu9hfU8.js} +14 -6
- package/dist/secret-contract-api.js +1 -1
- package/dist/{send-UJHiAm4C.js → send-mTKSBdmZ.js} +76 -48
- package/dist/{send.components-CrBJpGT_.js → send.components-nsPnc7kK.js} +20 -14
- package/dist/{send.outbound-D7JYmfm9.js → send.outbound-CaOs9Zy1.js} +34 -20
- package/dist/{send.shared-DZJ1akvQ.js → send.shared-BJHt0Vyf.js} +24 -17
- package/dist/{sender-identity-BJtfyise.js → sender-identity-Dt0imZyY.js} +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{shared-Dii2HTn7.js → shared-BNesVA4P.js} +17 -14
- package/dist/{subagent-hooks-JyGrtQIl.js → subagent-hooks-IeZVPLGj.js} +2 -2
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/{system-events-BEmcj4xJ.js → system-events-Be5EJI3b.js} +2 -2
- package/dist/{target-resolver-V-IYae6s.js → target-resolver-C0MnwEki.js} +6 -5
- package/dist/targets-DQSye1Rv.js +3 -0
- package/dist/{thread-bindings-DvYP79fy.js → thread-bindings-wECUSIIG.js} +6 -5
- package/dist/{thread-bindings.manager-Clm77DBM.js → thread-bindings.manager-B-3UHCc1.js} +28 -25
- package/dist/{thread-bindings.session-updates-CJb-BpPL.js → thread-bindings.session-updates-CC7HoQvt.js} +2 -1
- package/dist/{threading-CXuu_7o9.js → threading-te5ADPQI.js} +8 -6
- package/dist/{timeouts-B9cy6RvQ.js → timeouts-D86uWLt_.js} +1 -1
- package/dist/{transcripts-source-BLEMH_yi.js → transcripts-source-DkUhR7YK.js} +3 -3
- package/dist/transcripts-source-api.js +1 -1
- package/dist/{typing-B9snnwMe.js → typing-DAwJF5VT.js} +2 -2
- package/dist/{voice-runtime-DY6_o73o.js → voice-runtime-COyYYTDD.js} +348 -14
- package/node_modules/@discord/embedded-app-sdk/node_modules/@types/lodash/README.md +1 -1
- package/node_modules/@discord/embedded-app-sdk/node_modules/@types/lodash/conforms.d.ts +2 -0
- package/node_modules/@discord/embedded-app-sdk/node_modules/@types/lodash/package.json +3 -3
- package/node_modules/@discord/embedded-app-sdk/node_modules/@types/lodash/stubArray.d.ts +2 -0
- package/node_modules/@discord/embedded-app-sdk/node_modules/@types/lodash/stubObject.d.ts +2 -0
- package/node_modules/@discord/embedded-app-sdk/node_modules/@types/lodash/stubString.d.ts +2 -0
- package/node_modules/typebox/build/format/email.mjs +1 -1
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
- package/node_modules/typebox/package.json +1 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +5 -5
- package/dist/account-inspect-BDfKyuvL.js +0 -89
- package/dist/provider.runtime-DiInaDXO.js +0 -2
- package/dist/sdk-runtime-6tfgYDjY.js +0 -338
- package/dist/targets-CQp53cGj.js +0 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { on as __exportAll } from "./retry-
|
|
2
|
-
import { a as listEnabledDiscordAccounts, c as resolveDiscordAccount } from "./accounts-
|
|
3
|
-
import { n as resolveDiscordVoiceEnabled, r as authorizeDiscordVoiceIngress, t as resolveDiscordVoiceAccess } from "./owner-access-
|
|
1
|
+
import { on as __exportAll } from "./retry-t5RcEsPk.js";
|
|
2
|
+
import { a as listEnabledDiscordAccounts, c as resolveDiscordAccount } from "./accounts-CwJQoLjM.js";
|
|
3
|
+
import { n as resolveDiscordVoiceEnabled, r as authorizeDiscordVoiceIngress, t as resolveDiscordVoiceAccess } from "./owner-access-yc1sX7Gx.js";
|
|
4
4
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
5
5
|
import { summarizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
6
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as discordVoiceTranscriptsSourceProvider } from "./transcripts-source-
|
|
1
|
+
import { t as discordVoiceTranscriptsSourceProvider } from "./transcripts-source-DkUhR7YK.js";
|
|
2
2
|
//#region extensions/discord/transcripts-source-api.ts
|
|
3
3
|
function registerDiscordTranscriptSourceProvider(api) {
|
|
4
4
|
api.registerTranscriptSourceProvider(discordVoiceTranscriptsSourceProvider);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Rt as sendChannelTyping, on as __exportAll } from "./retry-
|
|
2
|
-
import { c as raceWithTimeout } from "./timeouts-
|
|
1
|
+
import { Rt as sendChannelTyping, on as __exportAll } from "./retry-t5RcEsPk.js";
|
|
2
|
+
import { c as raceWithTimeout } from "./timeouts-D86uWLt_.js";
|
|
3
3
|
//#region extensions/discord/src/monitor/typing.ts
|
|
4
4
|
var typing_exports = /* @__PURE__ */ __exportAll({ sendTyping: () => sendTyping });
|
|
5
5
|
const DISCORD_TYPING_START_TIMEOUT_MS = 5e3;
|
|
@@ -1,24 +1,350 @@
|
|
|
1
|
-
import { D as ReadyListener, L as isUnknownDiscordVoiceStateError, O as ResumedListener, Yt as getGuildVoiceState, _ as parseDiscordTarget, j as VoiceStateUpdateListener, y as discord_exports } from "./retry-
|
|
1
|
+
import { D as ReadyListener, L as isUnknownDiscordVoiceStateError, O as ResumedListener, Yt as getGuildVoiceState, _ as parseDiscordTarget, j as VoiceStateUpdateListener, on as __exportAll, y as discord_exports } from "./retry-t5RcEsPk.js";
|
|
2
2
|
import { t as getDiscordRuntime } from "./runtime-DgnVQ7zW.js";
|
|
3
|
-
import { a as normalizeDiscordSlug, h as resolveDiscordOwnerAccess, x as formatDiscordUserTag } from "./allow-list-
|
|
4
|
-
import { a as formatMention } from "./send.outbound-
|
|
5
|
-
import { n as resolveDiscordVoiceEnabled, r as authorizeDiscordVoiceIngress, t as resolveDiscordVoiceAccess } from "./owner-access-
|
|
6
|
-
import { b as resolveFetchedDiscordThreadLikeChannelContext, m as buildDiscordGroupSystemPrompt } from "./provider-
|
|
7
|
-
import {
|
|
3
|
+
import { a as normalizeDiscordSlug, h as resolveDiscordOwnerAccess, x as formatDiscordUserTag } from "./allow-list-C2JuH-R0.js";
|
|
4
|
+
import { a as formatMention } from "./send.outbound-CaOs9Zy1.js";
|
|
5
|
+
import { n as resolveDiscordVoiceEnabled, r as authorizeDiscordVoiceIngress, t as resolveDiscordVoiceAccess } from "./owner-access-yc1sX7Gx.js";
|
|
6
|
+
import { b as resolveFetchedDiscordThreadLikeChannelContext, m as buildDiscordGroupSystemPrompt } from "./provider-CjI8BSzS.js";
|
|
7
|
+
import { createRequire } from "node:module";
|
|
8
|
+
import fs from "node:fs/promises";
|
|
8
9
|
import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
9
10
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
10
11
|
import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
11
12
|
import { resolveAgentRoute } from "openclaw/plugin-sdk/routing";
|
|
12
13
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
13
|
-
import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
|
|
14
|
+
import { createSubsystemLogger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
14
15
|
import { escapeRegExp, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
15
16
|
import { stripInlineDirectiveTagsForDisplay } from "openclaw/plugin-sdk/text-chunking";
|
|
16
|
-
import { unlinkIfExists } from "openclaw/plugin-sdk/media-runtime";
|
|
17
|
+
import { resolveFfmpegBin, unlinkIfExists } from "openclaw/plugin-sdk/media-runtime";
|
|
18
|
+
import { resolvePreferredOpenClawTmpDir, tempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
|
17
19
|
import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
|
|
18
20
|
import { enqueueRoutedSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
+
import { controlRealtimeVoiceAgentRun, resamplePcm, shouldAutoControlRealtimeVoiceAgentText } from "openclaw/plugin-sdk/realtime-voice";
|
|
22
|
+
import { spawn } from "node:child_process";
|
|
23
|
+
import { Readable, Transform } from "node:stream";
|
|
24
|
+
import { StringDecoder } from "node:string_decoder";
|
|
25
|
+
import { Application, OpusError, createDecoder, createEncoder } from "libopus-wasm";
|
|
21
26
|
import { resolveRealtimeBootstrapContextInstructions } from "openclaw/plugin-sdk/realtime-bootstrap-context";
|
|
27
|
+
//#region extensions/discord/src/voice/log-preview.ts
|
|
28
|
+
const DISCORD_VOICE_LOG_PREVIEW_CHARS = 500;
|
|
29
|
+
function formatVoiceLogPreview(text) {
|
|
30
|
+
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
31
|
+
if (oneLine.length <= DISCORD_VOICE_LOG_PREVIEW_CHARS) return oneLine;
|
|
32
|
+
return `${truncateUtf16Safe(oneLine, DISCORD_VOICE_LOG_PREVIEW_CHARS)}...`;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region extensions/discord/src/voice/agent-control.ts
|
|
36
|
+
async function maybeControlDiscordVoiceAgentRun(params) {
|
|
37
|
+
if (!shouldAutoControlRealtimeVoiceAgentText(params.text)) return { handled: false };
|
|
38
|
+
const result = await controlRealtimeVoiceAgentRun({
|
|
39
|
+
sessionKey: params.entry.route.sessionKey,
|
|
40
|
+
text: params.text
|
|
41
|
+
});
|
|
42
|
+
if (!result.active) return {
|
|
43
|
+
handled: false,
|
|
44
|
+
result
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
handled: true,
|
|
48
|
+
result,
|
|
49
|
+
...result.speak && !result.suppress ? { speakText: result.message } : {}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region extensions/discord/src/voice/prompt.ts
|
|
54
|
+
const DISCORD_VOICE_SPOKEN_OUTPUT_CONTRACT = [
|
|
55
|
+
"You are OpenClaw's Discord voice interface in a live voice channel.",
|
|
56
|
+
"Discord voice reply requirements:",
|
|
57
|
+
"- Return only the concise text that should be spoken aloud in the voice channel.",
|
|
58
|
+
"- Treat the transcript as speech-to-text from a live conversation; repair obvious transcription artifacts and ignore repeated partial fragments caused by voice buffering.",
|
|
59
|
+
"- If the transcript is garbled, incomplete, or missing the user's intent, ask one brief clarifying question instead of guessing.",
|
|
60
|
+
"- If the request needs deeper reasoning, current information, or tools, use the available tools before answering.",
|
|
61
|
+
"- Do not call the tts tool; Discord voice will synthesize and play the returned text.",
|
|
62
|
+
"- Do not reply with NO_REPLY unless no spoken response is appropriate.",
|
|
63
|
+
"- Keep the response brief, natural, and conversational. Prefer one to three short sentences.",
|
|
64
|
+
"- Avoid markdown tables, code fences, citations, and visual formatting unless the user explicitly asks for something that cannot be spoken naturally."
|
|
65
|
+
].join("\n");
|
|
66
|
+
function formatVoiceIngressPrompt(transcript, speakerLabel) {
|
|
67
|
+
const cleanedTranscript = transcript.trim();
|
|
68
|
+
const cleanedLabel = speakerLabel?.trim();
|
|
69
|
+
const voiceInput = cleanedLabel ? [`Voice transcript from speaker "${cleanedLabel}":`, cleanedTranscript].join("\n") : cleanedTranscript;
|
|
70
|
+
return [DISCORD_VOICE_SPOKEN_OUTPUT_CONTRACT, voiceInput].join("\n\n");
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region extensions/discord/src/voice/audio.ts
|
|
74
|
+
const SAMPLE_RATE = 48e3;
|
|
75
|
+
const CHANNELS = 2;
|
|
76
|
+
const BIT_DEPTH = 16;
|
|
77
|
+
const FFMPEG_ERROR_OUTPUT_BYTES = 8192;
|
|
78
|
+
const DISCORD_OPUS_FRAME_SIZE = 960;
|
|
79
|
+
const DISCORD_OPUS_FRAME_BYTES = DISCORD_OPUS_FRAME_SIZE * CHANNELS * (BIT_DEPTH / 8);
|
|
80
|
+
const FFMPEG_PCM_ARGUMENTS = [
|
|
81
|
+
"-analyzeduration",
|
|
82
|
+
"0",
|
|
83
|
+
"-loglevel",
|
|
84
|
+
"error",
|
|
85
|
+
"-vn",
|
|
86
|
+
"-sn",
|
|
87
|
+
"-dn",
|
|
88
|
+
"-f",
|
|
89
|
+
"s16le",
|
|
90
|
+
"-ar",
|
|
91
|
+
String(SAMPLE_RATE),
|
|
92
|
+
"-ac",
|
|
93
|
+
String(CHANNELS)
|
|
94
|
+
];
|
|
95
|
+
let warnedOpusMissing = false;
|
|
96
|
+
function buildWavBuffer(pcm) {
|
|
97
|
+
const blockAlign = 4;
|
|
98
|
+
const byteRate = SAMPLE_RATE * blockAlign;
|
|
99
|
+
const header = Buffer.alloc(44);
|
|
100
|
+
header.write("RIFF", 0);
|
|
101
|
+
header.writeUInt32LE(36 + pcm.length, 4);
|
|
102
|
+
header.write("WAVE", 8);
|
|
103
|
+
header.write("fmt ", 12);
|
|
104
|
+
header.writeUInt32LE(16, 16);
|
|
105
|
+
header.writeUInt16LE(1, 20);
|
|
106
|
+
header.writeUInt16LE(CHANNELS, 22);
|
|
107
|
+
header.writeUInt32LE(SAMPLE_RATE, 24);
|
|
108
|
+
header.writeUInt32LE(byteRate, 28);
|
|
109
|
+
header.writeUInt16LE(blockAlign, 32);
|
|
110
|
+
header.writeUInt16LE(BIT_DEPTH, 34);
|
|
111
|
+
header.write("data", 36);
|
|
112
|
+
header.writeUInt32LE(pcm.length, 40);
|
|
113
|
+
return Buffer.concat([header, pcm]);
|
|
114
|
+
}
|
|
115
|
+
function createDiscordOpusEncodeStream() {
|
|
116
|
+
return new DiscordOpusEncodeStream();
|
|
117
|
+
}
|
|
118
|
+
function createDiscordOpusPlaybackStream(input) {
|
|
119
|
+
const inputSource = typeof input === "string" ? input : "pipe:0";
|
|
120
|
+
const ffmpeg = spawn(resolveFfmpegBin(), [
|
|
121
|
+
"-i",
|
|
122
|
+
inputSource,
|
|
123
|
+
...FFMPEG_PCM_ARGUMENTS,
|
|
124
|
+
"pipe:1"
|
|
125
|
+
], {
|
|
126
|
+
stdio: [
|
|
127
|
+
"pipe",
|
|
128
|
+
"pipe",
|
|
129
|
+
"pipe"
|
|
130
|
+
],
|
|
131
|
+
windowsHide: true
|
|
132
|
+
});
|
|
133
|
+
const opusStream = createDiscordOpusEncodeStream();
|
|
134
|
+
const stderr = Buffer.alloc(FFMPEG_ERROR_OUTPUT_BYTES);
|
|
135
|
+
let stderrBytes = 0;
|
|
136
|
+
let ffmpegClosed = false;
|
|
137
|
+
const killFfmpeg = (signal = "SIGTERM") => {
|
|
138
|
+
if (!ffmpegClosed && !ffmpeg.killed) ffmpeg.kill(signal);
|
|
139
|
+
};
|
|
140
|
+
ffmpeg.stderr.on("data", (chunk) => {
|
|
141
|
+
if (stderrBytes < FFMPEG_ERROR_OUTPUT_BYTES) stderrBytes += chunk.copy(stderr, stderrBytes, 0, FFMPEG_ERROR_OUTPUT_BYTES - stderrBytes);
|
|
142
|
+
});
|
|
143
|
+
ffmpeg.once("error", (err) => {
|
|
144
|
+
opusStream.destroy(err);
|
|
145
|
+
});
|
|
146
|
+
ffmpeg.once("close", (code, signal) => {
|
|
147
|
+
ffmpegClosed = true;
|
|
148
|
+
if (code && code !== 0) {
|
|
149
|
+
const stderrText = new StringDecoder("utf8").write(stderr.subarray(0, stderrBytes)).trim();
|
|
150
|
+
const suffix = stderrText ? `: ${stderrText}` : "";
|
|
151
|
+
opusStream.destroy(/* @__PURE__ */ new Error(`ffmpeg exited with code ${code}${suffix}`));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (signal) opusStream.destroy(/* @__PURE__ */ new Error(`ffmpeg exited with signal ${signal}`));
|
|
155
|
+
});
|
|
156
|
+
for (const readable of [ffmpeg.stdout, ffmpeg.stderr]) readable.on("error", (err) => {
|
|
157
|
+
killFfmpeg("SIGKILL");
|
|
158
|
+
opusStream.destroy(err);
|
|
159
|
+
});
|
|
160
|
+
ffmpeg.stdin.on("error", (err) => {
|
|
161
|
+
if (err.code !== "EPIPE") opusStream.destroy(err);
|
|
162
|
+
});
|
|
163
|
+
ffmpeg.stdout.pipe(opusStream);
|
|
164
|
+
opusStream.once("close", () => {
|
|
165
|
+
if (!opusStream.readableEnded) killFfmpeg();
|
|
166
|
+
});
|
|
167
|
+
if (typeof input !== "string") {
|
|
168
|
+
input.on("error", (err) => {
|
|
169
|
+
ffmpeg.stdin.destroy(err);
|
|
170
|
+
opusStream.destroy(err);
|
|
171
|
+
});
|
|
172
|
+
input.pipe(ffmpeg.stdin);
|
|
173
|
+
} else ffmpeg.stdin.end();
|
|
174
|
+
return opusStream;
|
|
175
|
+
}
|
|
176
|
+
var DiscordOpusEncodeStream = class extends Transform {
|
|
177
|
+
#buffer = Buffer.alloc(0);
|
|
178
|
+
#encoder;
|
|
179
|
+
constructor() {
|
|
180
|
+
super({ readableObjectMode: true });
|
|
181
|
+
}
|
|
182
|
+
_construct(done) {
|
|
183
|
+
createEncoder({
|
|
184
|
+
application: Application.Audio,
|
|
185
|
+
channels: CHANNELS,
|
|
186
|
+
sampleRate: SAMPLE_RATE
|
|
187
|
+
}).then((encoder) => {
|
|
188
|
+
this.#encoder = encoder;
|
|
189
|
+
done();
|
|
190
|
+
}, (err) => done(err instanceof Error ? err : new Error(formatErrorMessage(err))));
|
|
191
|
+
}
|
|
192
|
+
_transform(chunk, _encoding, done) {
|
|
193
|
+
try {
|
|
194
|
+
this.#buffer = this.#buffer.length > 0 ? Buffer.concat([this.#buffer, chunk]) : Buffer.from(chunk);
|
|
195
|
+
while (this.#buffer.length >= DISCORD_OPUS_FRAME_BYTES) {
|
|
196
|
+
const frame = this.#buffer.subarray(0, DISCORD_OPUS_FRAME_BYTES);
|
|
197
|
+
this.#buffer = this.#buffer.subarray(DISCORD_OPUS_FRAME_BYTES);
|
|
198
|
+
this.#encodeFrame(frame);
|
|
199
|
+
}
|
|
200
|
+
done();
|
|
201
|
+
} catch (err) {
|
|
202
|
+
done(err instanceof Error ? err : new Error(formatErrorMessage(err)));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
_flush(done) {
|
|
206
|
+
try {
|
|
207
|
+
if (this.#buffer.length > 0) {
|
|
208
|
+
const frame = Buffer.alloc(DISCORD_OPUS_FRAME_BYTES);
|
|
209
|
+
this.#buffer.copy(frame);
|
|
210
|
+
this.#buffer = Buffer.alloc(0);
|
|
211
|
+
this.#encodeFrame(frame);
|
|
212
|
+
}
|
|
213
|
+
done();
|
|
214
|
+
} catch (err) {
|
|
215
|
+
done(err instanceof Error ? err : new Error(formatErrorMessage(err)));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
_destroy(err, done) {
|
|
219
|
+
this.#encoder?.free();
|
|
220
|
+
this.#buffer = Buffer.alloc(0);
|
|
221
|
+
done(err);
|
|
222
|
+
}
|
|
223
|
+
#encodeFrame(frame) {
|
|
224
|
+
this.push(Buffer.from(this.#encoder.encode(frame, { frameSize: DISCORD_OPUS_FRAME_SIZE })));
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
function pcmInt16ToBuffer(pcm) {
|
|
228
|
+
return Buffer.from(pcm.buffer, pcm.byteOffset, pcm.byteLength);
|
|
229
|
+
}
|
|
230
|
+
async function decodeOpusStream(stream, params) {
|
|
231
|
+
const chunks = [];
|
|
232
|
+
await decodeOpusStreamChunks(stream, {
|
|
233
|
+
...params,
|
|
234
|
+
onChunk: (chunk) => chunks.push(chunk)
|
|
235
|
+
});
|
|
236
|
+
return Buffer.concat(chunks);
|
|
237
|
+
}
|
|
238
|
+
async function decodeOpusStreamChunks(stream, params) {
|
|
239
|
+
let decoder;
|
|
240
|
+
try {
|
|
241
|
+
decoder = await createDecoder({
|
|
242
|
+
channels: CHANNELS,
|
|
243
|
+
sampleRate: SAMPLE_RATE
|
|
244
|
+
});
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (!warnedOpusMissing) {
|
|
247
|
+
warnedOpusMissing = true;
|
|
248
|
+
params.onWarn(`discord voice: no usable opus decoder available (libopus-wasm: ${formatErrorMessage(err)}); cannot decode voice audio`);
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
params.onVerbose("opus decoder: libopus-wasm");
|
|
253
|
+
try {
|
|
254
|
+
for await (const chunk of stream) {
|
|
255
|
+
if (!chunk || !(chunk instanceof Buffer) || chunk.length === 0) continue;
|
|
256
|
+
const decoded = decoder.decode(chunk, { maxFrameSize: DISCORD_OPUS_FRAME_SIZE });
|
|
257
|
+
if (decoded.length > 0) params.onChunk(pcmInt16ToBuffer(decoded));
|
|
258
|
+
}
|
|
259
|
+
} catch (err) {
|
|
260
|
+
params.onError?.(err);
|
|
261
|
+
if (shouldLogVerbose()) logVerbose(`discord voice: opus decode failed: ${formatErrorMessage(err)}`);
|
|
262
|
+
} finally {
|
|
263
|
+
decoder.free();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function convertDiscordPcm48kStereoToRealtimePcm24kMono(pcm) {
|
|
267
|
+
const frameCount = Math.floor(pcm.length / 4);
|
|
268
|
+
if (frameCount === 0) return Buffer.alloc(0);
|
|
269
|
+
const mono48k = Buffer.alloc(frameCount * 2);
|
|
270
|
+
for (let frame = 0; frame < frameCount; frame += 1) {
|
|
271
|
+
const offset = frame * 4;
|
|
272
|
+
const left = pcm.readInt16LE(offset);
|
|
273
|
+
const right = pcm.readInt16LE(offset + 2);
|
|
274
|
+
mono48k.writeInt16LE(Math.round((left + right) / 2), frame * 2);
|
|
275
|
+
}
|
|
276
|
+
return resamplePcm(mono48k, SAMPLE_RATE, 24e3);
|
|
277
|
+
}
|
|
278
|
+
function convertRealtimePcm24kMonoToDiscordPcm48kStereo(pcm) {
|
|
279
|
+
const mono48k = resamplePcm(pcm, 24e3, SAMPLE_RATE);
|
|
280
|
+
const sampleCount = Math.floor(mono48k.length / 2);
|
|
281
|
+
if (sampleCount === 0) return Buffer.alloc(0);
|
|
282
|
+
const stereo = Buffer.alloc(sampleCount * 4);
|
|
283
|
+
for (let sampleIndex = 0; sampleIndex < sampleCount; sampleIndex += 1) {
|
|
284
|
+
const sample = mono48k.readInt16LE(sampleIndex * 2);
|
|
285
|
+
const offset = sampleIndex * 4;
|
|
286
|
+
stereo.writeInt16LE(sample, offset);
|
|
287
|
+
stereo.writeInt16LE(sample, offset + 2);
|
|
288
|
+
}
|
|
289
|
+
return stereo;
|
|
290
|
+
}
|
|
291
|
+
function estimateDurationSeconds(pcm) {
|
|
292
|
+
const bytesPerSample = BIT_DEPTH / 8 * CHANNELS;
|
|
293
|
+
return pcm.length / (bytesPerSample * SAMPLE_RATE);
|
|
294
|
+
}
|
|
295
|
+
async function writeVoiceWavFile(pcm) {
|
|
296
|
+
const workspace = await tempWorkspace({
|
|
297
|
+
rootDir: resolvePreferredOpenClawTmpDir(),
|
|
298
|
+
prefix: "discord-voice-"
|
|
299
|
+
});
|
|
300
|
+
scheduleTempCleanup(workspace.dir);
|
|
301
|
+
const wav = buildWavBuffer(pcm);
|
|
302
|
+
return {
|
|
303
|
+
path: await workspace.write("segment.wav", wav),
|
|
304
|
+
durationSeconds: estimateDurationSeconds(pcm)
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function scheduleTempCleanup(tempDir, delayMs = 18e5) {
|
|
308
|
+
setTimeout(() => {
|
|
309
|
+
fs.rm(tempDir, {
|
|
310
|
+
recursive: true,
|
|
311
|
+
force: true
|
|
312
|
+
}).catch((err) => {
|
|
313
|
+
if (shouldLogVerbose()) logVerbose(`discord voice: temp cleanup failed for ${tempDir}: ${formatErrorMessage(err)}`);
|
|
314
|
+
});
|
|
315
|
+
}, delayMs).unref();
|
|
316
|
+
}
|
|
317
|
+
const CAPTURE_FINALIZE_GRACE_MS = 2e3;
|
|
318
|
+
const VOICE_CONNECT_READY_TIMEOUT_MS = 3e4;
|
|
319
|
+
const VOICE_RECONNECT_GRACE_MS = 15e3;
|
|
320
|
+
const PLAYBACK_READY_TIMEOUT_MS = 6e4;
|
|
321
|
+
function resolveVoiceTimeoutMs(value, fallbackMs) {
|
|
322
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return fallbackMs;
|
|
323
|
+
return Math.floor(value);
|
|
324
|
+
}
|
|
325
|
+
function resolveDiscordVoiceMode(voice) {
|
|
326
|
+
const mode = voice?.mode;
|
|
327
|
+
if (mode === "stt-tts" || mode === "bidi") return mode;
|
|
328
|
+
return "agent-proxy";
|
|
329
|
+
}
|
|
330
|
+
function isDiscordRealtimeVoiceMode(mode) {
|
|
331
|
+
return mode === "agent-proxy" || mode === "bidi";
|
|
332
|
+
}
|
|
333
|
+
function logVoiceVerbose(message) {
|
|
334
|
+
logVerbose(`discord voice: ${message}`);
|
|
335
|
+
}
|
|
336
|
+
function isVoiceChannel(type) {
|
|
337
|
+
return type === discord_exports.ChannelType.GuildVoice || type === discord_exports.ChannelType.GuildStageVoice;
|
|
338
|
+
}
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region extensions/discord/src/voice/sdk-runtime.ts
|
|
341
|
+
let cachedDiscordVoiceSdk = null;
|
|
342
|
+
function loadDiscordVoiceSdk() {
|
|
343
|
+
if (cachedDiscordVoiceSdk) return cachedDiscordVoiceSdk;
|
|
344
|
+
cachedDiscordVoiceSdk = createRequire(import.meta.url)("@discordjs/voice");
|
|
345
|
+
return cachedDiscordVoiceSdk;
|
|
346
|
+
}
|
|
347
|
+
//#endregion
|
|
22
348
|
//#region extensions/discord/src/voice/ingress.ts
|
|
23
349
|
const DISCORD_VOICE_MESSAGE_PROVIDER = "discord-voice";
|
|
24
350
|
const logger$7 = createSubsystemLogger("discord/voice");
|
|
@@ -1115,12 +1441,13 @@ function sanitizeVoiceReplyTextForSpeech(text, speakerLabel) {
|
|
|
1115
1441
|
//#endregion
|
|
1116
1442
|
//#region extensions/discord/src/voice/tts.ts
|
|
1117
1443
|
async function transcribeVoiceAudio(params) {
|
|
1118
|
-
|
|
1444
|
+
const result = await getDiscordRuntime().mediaUnderstanding.transcribeAudioFile({
|
|
1119
1445
|
filePath: params.filePath,
|
|
1120
1446
|
cfg: params.cfg,
|
|
1121
1447
|
agentDir: resolveAgentDir(params.cfg, params.agentId),
|
|
1122
1448
|
mime: "audio/wav"
|
|
1123
|
-
})
|
|
1449
|
+
});
|
|
1450
|
+
return normalizeOptionalString(result.text);
|
|
1124
1451
|
}
|
|
1125
1452
|
async function synthesizeVoiceReplyAudio(params) {
|
|
1126
1453
|
const runtime = getDiscordRuntime();
|
|
@@ -2052,7 +2379,7 @@ var DiscordVoiceSessions = class {
|
|
|
2052
2379
|
ok: false,
|
|
2053
2380
|
message: "Discord realtime voice session stopped before startup completed."
|
|
2054
2381
|
};
|
|
2055
|
-
const { DiscordRealtimeVoiceSession } = await import("./realtime-session.runtime-
|
|
2382
|
+
const { DiscordRealtimeVoiceSession } = await import("./realtime-session.runtime-37IC-p5K.js");
|
|
2056
2383
|
const realtime = new DiscordRealtimeVoiceSession({
|
|
2057
2384
|
accountId: this.params.accountId,
|
|
2058
2385
|
bootstrapContextInstructions,
|
|
@@ -2156,6 +2483,13 @@ var DiscordVoiceStateUpdateListener = class extends VoiceStateUpdateListener {
|
|
|
2156
2483
|
};
|
|
2157
2484
|
//#endregion
|
|
2158
2485
|
//#region extensions/discord/src/voice/voice-runtime.ts
|
|
2486
|
+
var voice_runtime_exports = /* @__PURE__ */ __exportAll({
|
|
2487
|
+
DiscordVoiceGuildCreateListener: () => DiscordVoiceGuildCreateListener,
|
|
2488
|
+
DiscordVoiceManager: () => DiscordVoiceManager,
|
|
2489
|
+
DiscordVoiceReadyListener: () => DiscordVoiceReadyListener,
|
|
2490
|
+
DiscordVoiceResumedListener: () => DiscordVoiceResumedListener,
|
|
2491
|
+
DiscordVoiceStateUpdateListener: () => DiscordVoiceStateUpdateListener
|
|
2492
|
+
});
|
|
2159
2493
|
const logger = createSubsystemLogger("discord/voice");
|
|
2160
2494
|
const DISCORD_VOICE_FATAL_AUTOJOIN_ERROR_PATTERNS = [
|
|
2161
2495
|
"api key missing",
|
|
@@ -2556,4 +2890,4 @@ var DiscordVoiceManager = class {
|
|
|
2556
2890
|
}
|
|
2557
2891
|
};
|
|
2558
2892
|
//#endregion
|
|
2559
|
-
export { DiscordVoiceGuildCreateListener, DiscordVoiceManager, DiscordVoiceReadyListener, DiscordVoiceResumedListener, DiscordVoiceStateUpdateListener };
|
|
2893
|
+
export { DiscordVoiceGuildCreateListener, DiscordVoiceManager, DiscordVoiceReadyListener, DiscordVoiceResumedListener, DiscordVoiceStateUpdateListener, convertRealtimePcm24kMonoToDiscordPcm48kStereo as a, maybeControlDiscordVoiceAgentRun as c, convertDiscordPcm48kStereoToRealtimePcm24kMono as i, formatVoiceLogPreview as l, loadDiscordVoiceSdk as n, createDiscordOpusEncodeStream as o, logVoiceVerbose as r, formatVoiceIngressPrompt as s, voice_runtime_exports as t };
|
|
@@ -8,7 +8,7 @@ This package contains type definitions for lodash (https://lodash.com).
|
|
|
8
8
|
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/lodash.
|
|
9
9
|
|
|
10
10
|
### Additional Details
|
|
11
|
-
* Last updated:
|
|
11
|
+
* Last updated: Sat, 01 Aug 2026 03:43:12 GMT
|
|
12
12
|
* Dependencies: none
|
|
13
13
|
|
|
14
14
|
# Credits
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@types/lodash",
|
|
3
|
-
"version": "4.17.
|
|
3
|
+
"version": "4.17.25",
|
|
4
4
|
"description": "TypeScript definitions for lodash",
|
|
5
5
|
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/lodash",
|
|
6
6
|
"license": "MIT",
|
|
@@ -51,6 +51,6 @@
|
|
|
51
51
|
"scripts": {},
|
|
52
52
|
"dependencies": {},
|
|
53
53
|
"peerDependencies": {},
|
|
54
|
-
"typesPublisherContentHash": "
|
|
55
|
-
"typeScriptVersion": "5.
|
|
54
|
+
"typesPublisherContentHash": "5b89763740a3523d5295cc411a286fab1b0799800b598f81ec76d2f90a1ffce3",
|
|
55
|
+
"typeScriptVersion": "5.6"
|
|
56
56
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]
|
|
1
|
+
const Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]|\\[\x20-\x7e])*")@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*|\[(?:IPv6:[a-f0-9:]+|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})\])$/i;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is an Email
|
|
4
4
|
* @specification https://datatracker.ietf.org/doc/html/rfc5322
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as FormatBidi from './format/bidi.mjs';
|
|
2
2
|
import * as LabelUnicode from './label/unicode.mjs';
|
|
3
3
|
import * as LabelPuny from './label/puny.mjs';
|
|
4
|
+
// ------------------------------------------------------------------
|
|
5
|
+
// IsLabel
|
|
6
|
+
// ------------------------------------------------------------------
|
|
4
7
|
function IsValidLabelLength(value) {
|
|
5
8
|
return value.length > 0 && value.length <= 63;
|
|
6
9
|
}
|
|
@@ -8,9 +11,24 @@ function IsLabel(value) {
|
|
|
8
11
|
return IsValidLabelLength(value) && (LabelPuny.IsPunyLabel(value) ||
|
|
9
12
|
LabelUnicode.IsUnicodeLabel(value));
|
|
10
13
|
}
|
|
14
|
+
// ------------------------------------------------------------------
|
|
15
|
+
// NormalizeHostname
|
|
16
|
+
//
|
|
17
|
+
// Normalizes a hostname for label validation. Normalizes for NFC
|
|
18
|
+
// such that equivalent codepoint sequences can be uniformly
|
|
19
|
+
// compared. It also removes codepoints that IDNA mapping ignores,
|
|
20
|
+
// and converts full stop variants to ASCII '.' so the hostname can
|
|
21
|
+
// be split into labels.
|
|
22
|
+
// ------------------------------------------------------------------
|
|
11
23
|
function NormalizeHostname(value) {
|
|
12
|
-
return value
|
|
24
|
+
return value
|
|
25
|
+
.normalize('NFC')
|
|
26
|
+
.replace(/[\u00ad\u034f\u180b-\u180d\u200b\ufe00-\ufe0f\u{e0100}-\u{e01ef}]/gu, '') // RE_IGNORED_MAPPING
|
|
27
|
+
.replace(/[\u002E\u3002\uFF0E\uFF61]/g, '.'); // RE_FULL_STOP_MAPPING
|
|
13
28
|
}
|
|
29
|
+
// ------------------------------------------------------------------
|
|
30
|
+
// IsIdnHostname
|
|
31
|
+
// ------------------------------------------------------------------
|
|
14
32
|
export function IsIdnHostname(value) {
|
|
15
33
|
if (value.length === 0 || value.includes(' '))
|
|
16
34
|
return false;
|
package/openclaw.plugin.json
CHANGED
|
@@ -3795,11 +3795,11 @@
|
|
|
3795
3795
|
},
|
|
3796
3796
|
"threadBindings.enabled": {
|
|
3797
3797
|
"label": "Discord Thread Binding Enabled",
|
|
3798
|
-
"help": "Enable Discord thread
|
|
3798
|
+
"help": "Enable Discord thread-bound session spawning, routing, and delivery. Overrides session.threadBindings.enabled when set."
|
|
3799
3799
|
},
|
|
3800
3800
|
"threadBindings.idleHours": {
|
|
3801
3801
|
"label": "Discord Thread Binding Idle Timeout (hours)",
|
|
3802
|
-
"help": "Inactivity window in hours for Discord thread-bound sessions
|
|
3802
|
+
"help": "Inactivity window in hours for Discord thread-bound sessions. Set 0 to disable idle expiry (default: 24). Overrides session.threadBindings.idleHours when set."
|
|
3803
3803
|
},
|
|
3804
3804
|
"threadBindings.maxAgeHours": {
|
|
3805
3805
|
"label": "Discord Thread Binding Max Age (hours)",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/discord",
|
|
3
|
-
"version": "2026.8.
|
|
3
|
+
"version": "2026.8.2",
|
|
4
4
|
"description": "OpenClaw Discord channel plugin for channels, DMs, commands, and app events.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
"discord-api-types": "0.38.53",
|
|
14
14
|
"libopus-wasm": "0.2.0",
|
|
15
15
|
"mdast-util-from-markdown": "2.0.3",
|
|
16
|
-
"typebox": "1.3.
|
|
16
|
+
"typebox": "1.3.17",
|
|
17
17
|
"undici": "8.10.0",
|
|
18
18
|
"ws": "8.21.3",
|
|
19
19
|
"zod": "4.4.3"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"openclaw": ">=2026.8.
|
|
22
|
+
"openclaw": ">=2026.8.2"
|
|
23
23
|
},
|
|
24
24
|
"peerDependenciesMeta": {
|
|
25
25
|
"openclaw": {
|
|
@@ -94,10 +94,10 @@
|
|
|
94
94
|
"allowInvalidConfigRecovery": true
|
|
95
95
|
},
|
|
96
96
|
"compat": {
|
|
97
|
-
"pluginApi": ">=2026.8.
|
|
97
|
+
"pluginApi": ">=2026.8.2"
|
|
98
98
|
},
|
|
99
99
|
"build": {
|
|
100
|
-
"openclawVersion": "2026.8.
|
|
100
|
+
"openclawVersion": "2026.8.2",
|
|
101
101
|
"staticAssets": [
|
|
102
102
|
{
|
|
103
103
|
"source": "./assets/embedded-app-sdk.mjs",
|