Discord: a voice message cannot be sent or played #186

Closed
opened 2026-09-11 22:25:59 -04:00 by Salastil · 1 comment
Owner

No voice message support - nothing reads the IS_VOICE_MESSAGE flag or the waveform
metadata, and there is no way to record one.

A voice message arrives as an attachment with no indication of what it is, and cannot
be sent at all. This is now ordinary traffic in DMs, so it is not a corner case.

No voice message support - nothing reads the `IS_VOICE_MESSAGE` flag or the waveform metadata, and there is no way to record one. A voice message arrives as an attachment with no indication of what it is, and cannot be sent at all. This is now ordinary traffic in DMs, so it is not a corner case.
Salastil added the feature label 2026-09-11 22:25:59 -04:00
Author
Owner

Finished. Both halves.

Receiving — nobilis 54ac74e, client a39ee5f.

The flag, duration_secs and waveform were all being dropped, so a voice message arrived as an audio file called voice-message.ogg with nothing to say what it was - fourteen of them in this account's own scrollback. They are read now and drawn as a voice message: the sender's own waveform, the length, play/pause, progress, and click-anywhere-to-seek. All of it shows before a byte of audio is fetched, which is the point - fetching it is the thing being decided about.

The flag decides, not the mimetype: an .ogg somebody attached on purpose stays a file they sent, and that case is tested.

Sending — nobilis f9b5be5, client e519652.

Recorded and encoded in the daemon, which already captures the microphone for calls and links the same libopus. The missing piece was an Ogg container - a call puts Opus straight on the wire - so that is written here, about eighty lines of RFC 7845 framing.

Hand-written rather than a new dependency, which is only defensible because the result is checkable by a decoder nobody here wrote: ffprobe reads it as ogg/opus, mono, 48kHz, 2.000000s, with nothing on stderr. Ogg's CRC is not the common CRC32 (no reflection, zero init), and a wrong one fails as "not an Ogg file" rather than as a checksum error, so it has known answers computed by a second implementation in Python. My first expected value was wrong and the code was right.

Verified end to end against real Discord, with permission, by sending one to a DM between two accounts the same person owns:

  • Discord stored it with flags=8192, so it is a voice message and not an attachment.
  • duration_secs: 2.0 and the waveform survived, and content_type came back audio/ogg - Discord's own check agreeing about the container.
  • Read back through extract_attachments, the parser the window draws from: voice=true duration=Some(2.0) waveform=344 bytes.

Both probes are #[ignore]d and take the token and channel from the environment, like the Sneedchat ones.

One thing deliberately left: Matrix voice messages (MSC3245) are shaped differently - milliseconds and an array of 0-1024 samples against seconds and base64 bytes - and are their own piece of work rather than a branch in this one.

Finished. Both halves. **Receiving** — nobilis [`54ac74e`](https://git.salastil.com/Salastil/nobilis/commit/54ac74e), client [`a39ee5f`](https://git.salastil.com/Salastil/moho/commit/a39ee5f). The flag, `duration_secs` and `waveform` were all being dropped, so a voice message arrived as an audio file called voice-message.ogg with nothing to say what it was - fourteen of them in this account's own scrollback. They are read now and drawn as a voice message: the sender's own waveform, the length, play/pause, progress, and click-anywhere-to-seek. All of it shows before a byte of audio is fetched, which is the point - fetching it is the thing being decided about. The flag decides, not the mimetype: an .ogg somebody attached on purpose stays a file they sent, and that case is tested. **Sending** — nobilis [`f9b5be5`](https://git.salastil.com/Salastil/nobilis/commit/f9b5be5), client [`e519652`](https://git.salastil.com/Salastil/moho/commit/e519652). Recorded and encoded in the daemon, which already captures the microphone for calls and links the same libopus. The missing piece was an Ogg container - a call puts Opus straight on the wire - so that is written here, about eighty lines of RFC 7845 framing. Hand-written rather than a new dependency, which is only defensible because the result is checkable by a decoder nobody here wrote: **ffprobe reads it as ogg/opus, mono, 48kHz, 2.000000s, with nothing on stderr.** Ogg's CRC is not the common CRC32 (no reflection, zero init), and a wrong one fails as "not an Ogg file" rather than as a checksum error, so it has known answers computed by a second implementation in Python. My first expected value was wrong and the code was right. **Verified end to end against real Discord**, with permission, by sending one to a DM between two accounts the same person owns: - Discord stored it with `flags=8192`, so it is a voice message and not an attachment. - `duration_secs: 2.0` and the waveform survived, and `content_type` came back `audio/ogg` - Discord's own check agreeing about the container. - Read back through `extract_attachments`, the parser the window draws from: `voice=true duration=Some(2.0) waveform=344 bytes`. Both probes are `#[ignore]`d and take the token and channel from the environment, like the Sneedchat ones. One thing deliberately left: Matrix voice messages (MSC3245) are shaped differently - milliseconds and an array of 0-1024 samples against seconds and base64 bytes - and are their own piece of work rather than a branch in this one.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Salastil/moho#186