feat(discord): add voice message support

Adds support for sending Discord voice messages via the message tool
with asVoice: true parameter.

Voice messages require:
- OGG/Opus format (auto-converted if needed via ffmpeg)
- Waveform data (generated from audio samples)
- Duration in seconds
- Message flag 8192 (IS_VOICE_MESSAGE)

Implementation:
- New voice-message.ts with audio processing utilities
- getAudioDuration() using ffprobe
- generateWaveform() samples audio and creates base64 waveform
- ensureOggOpus() converts audio to required format
- sendDiscordVoiceMessage() handles 3-step Discord upload process

Usage:
message(action='send', channel='discord', target='...',
        path='/path/to/audio.mp3', asVoice=true)

Note: Voice messages cannot include text content (Discord limitation)
This commit is contained in:
nyanjou
2026-02-02 17:00:19 +01:00
committed by Shadow
parent aec3221391
commit a09e4fac3f
5 changed files with 444 additions and 1 deletions

View File

@@ -18,6 +18,7 @@ import {
sendMessageDiscord,
sendPollDiscord,
sendStickerDiscord,
sendVoiceMessageDiscord,
unpinMessageDiscord,
} from "../../discord/send.js";
import { resolveDiscordChannelId } from "../../discord/targets.js";
@@ -230,11 +231,25 @@ export async function handleDiscordMessagingAction(
const to = readStringParam(params, "to", { required: true });
const content = readStringParam(params, "content", {
required: true,
allowEmpty: true,
});
const mediaUrl = readStringParam(params, "mediaUrl");
const replyTo = readStringParam(params, "replyTo");
const asVoice = params.asVoice === true;
const embeds =
Array.isArray(params.embeds) && params.embeds.length > 0 ? params.embeds : undefined;
// Handle voice message sending
if (asVoice && mediaUrl) {
// Voice messages require a local file path or downloadable URL
// They cannot include text content (Discord limitation)
const result = await sendVoiceMessageDiscord(to, mediaUrl, {
...(accountId ? { accountId } : {}),
replyTo,
});
return jsonResult({ ok: true, result, voiceMessage: true });
}
const result = await sendMessageDiscord(to, content, {
...(accountId ? { accountId } : {}),
mediaUrl,