Skip to content

Commit

Permalink
Implement voice messages
Browse files Browse the repository at this point in the history
  • Loading branch information
kangalio committed Apr 20, 2023
1 parent fd0b573 commit 16fd3f9
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 0 deletions.
16 changes: 16 additions & 0 deletions examples/testing/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,22 @@ async fn message(ctx: &Context, msg: Message) -> Result<(), serenity::Error> {
});
let _ = tokio::time::timeout(Duration::from_millis(2000), message_updates.next()).await;
msg.edit(&ctx, EditMessage::new().suppress_embeds(true)).await?;
} else if msg.content == "voicemessage" {
let audio_url =
"https://upload.wikimedia.org/wikipedia/commons/8/81/Short_Silent%2C_Empty_Audio.ogg";
// As of 2023-04-20, bots are still not allowed to sending voice messages
msg.author
.id
.create_dm_channel(ctx)
.await?
.id
.send_message(
ctx,
CreateMessage::new()
.flags(MessageFlags::IS_VOICE_MESSAGE)
.add_file(CreateAttachment::url(ctx, audio_url).await?),
)
.await?;
} else {
return Ok(());
}
Expand Down
31 changes: 31 additions & 0 deletions src/model/channel/attachment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@ use crate::internal::prelude::*;
use crate::model::id::AttachmentId;
use crate::model::utils::is_false;

fn base64_bytes<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
where
D: serde::Deserializer<'de>,
{
use base64::Engine as _;

Check failure on line 13 in src/model/channel/attachment.rs

View workflow job for this annotation

GitHub Actions / Test (unstable Discord API (no default features), unstable_discord_api, true)

unresolved import `base64`

Check failure on line 13 in src/model/channel/attachment.rs

View workflow job for this annotation

GitHub Actions / Test (no default features)

unresolved import `base64`

Check failure on line 13 in src/model/channel/attachment.rs

View workflow job for this annotation

GitHub Actions / Test (chrono, chrono)

unresolved import `base64`
use serde::de::Error;
use serde::Deserialize;

let base64 = <Option<String>>::deserialize(deserializer)?;
let bytes = match base64 {
Some(base64) => {
Some(base64::prelude::BASE64_STANDARD.decode(base64).map_err(D::Error::custom)?)

Check failure on line 20 in src/model/channel/attachment.rs

View workflow job for this annotation

GitHub Actions / Test (unstable Discord API (no default features), unstable_discord_api, true)

failed to resolve: use of undeclared crate or module `base64`

Check failure on line 20 in src/model/channel/attachment.rs

View workflow job for this annotation

GitHub Actions / Test (no default features)

failed to resolve: use of undeclared crate or module `base64`

Check failure on line 20 in src/model/channel/attachment.rs

View workflow job for this annotation

GitHub Actions / Test (chrono, chrono)

failed to resolve: use of undeclared crate or module `base64`
},
None => None,
};
Ok(bytes)
}

/// A file uploaded with a message. Not to be confused with [`Embed`]s.
///
/// [Discord docs](https://discord.com/developers/docs/resources/channel#attachment-object).
Expand Down Expand Up @@ -41,6 +59,19 @@ pub struct Attachment {
/// itself exists.
#[serde(default, skip_serializing_if = "is_false")]
pub ephemeral: bool,
/// The duration of the audio file (present if [`MessageFlags::IS_VOICE_MESSAGE`]).

Check failure on line 62 in src/model/channel/attachment.rs

View workflow job for this annotation

GitHub Actions / Build docs

unresolved link to `MessageFlags::IS_VOICE_MESSAGE`
pub duration_secs: Option<f64>,
/// List of bytes representing a sampled waveform (present if
/// [`MessageFlags::IS_VOICE_MESSAGE`]).

Check failure on line 65 in src/model/channel/attachment.rs

View workflow job for this annotation

GitHub Actions / Build docs

unresolved link to `MessageFlags::IS_VOICE_MESSAGE`
///
/// The waveform is intended to be a preview of the entire voice message, with 1 byte per
/// datapoint. Clients sample the recording at most once per 100 milliseconds, but will
/// downsample so that no more than 256 datapoints are in the waveform.
///
/// The waveform details are a Discord implementation detail and may change without warning or
/// documentation.
#[serde(deserialize_with = "base64_bytes")]
pub waveform: Option<Vec<u8>>,
}

#[cfg(feature = "model")]
Expand Down
18 changes: 18 additions & 0 deletions src/model/channel/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,11 @@ impl Message {
}
}
}
if let Some(flags) = self.flags {
if flags.contains(MessageFlags::IS_VOICE_MESSAGE) {
return Err(Error::Model(ModelError::CannotEditVoiceMessage));
}
}

*self = builder.execute(cache_http, (self.channel_id, self.id)).await?;
Ok(())
Expand Down Expand Up @@ -1018,6 +1023,19 @@ bitflags! {
const FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8;
/// This message will not trigger push and desktop notifications.
const SUPPRESS_NOTIFICATIONS = 1 << 12;
/// This message is a voice message.
///
/// Voice messages gave the following properties:
/// - They cannot be edited.
/// - Only a single audio attachment is allowed. No content, stickers, etc...
/// - The [`Attachment`] has additional fields: `duration_secs` and `waveform`.
///
/// As of 2023-04-14, clients upload a 1 channel, 48000 Hz, 32kbps Opus stream in an OGG container.
/// The encoding is a Discord implementation detail and may change without warning or documentation.
///
/// As of 2023-04-20, bots are currently not able to send voice messages
/// ([source](https://github.com/discord/discord-api-docs/pull/6082)).
const IS_VOICE_MESSAGE = 1 << 13;
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/model/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ pub enum Error {
NoStickerFileSet,
/// When attempting to send a message with over 3 stickers.
StickerAmount,
/// When attempting to edit a voice message.
CannotEditVoiceMessage,
}

impl Error {
Expand Down Expand Up @@ -205,6 +207,7 @@ impl fmt::Display for Error {
Self::DeleteNitroSticker => f.write_str("Cannot delete an official sticker."),
Self::NoStickerFileSet => f.write_str("Sticker file is not set."),
Self::StickerAmount => f.write_str("Too many stickers in a message."),
Self::CannotEditVoiceMessage => f.write_str("Cannot edit voice message."),
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/model/permissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ bitflags::bitflags! {
/// Allows for timing out users to prevent them from sending or reacting to messages in
/// chat and threads, and from speaking in voice and stage channels.
const MODERATE_MEMBERS = 1 << 40;
// MISSING: VIEW_CREATOR_MONETIZATION_ANALYTICS (1 << 41), USE_SOUNDBOARD (1 << 42)
/// Allows sending voice messages.
const SEND_VOICE_MESSAGES = 1 << 46;
}
}

Expand Down

0 comments on commit 16fd3f9

Please sign in to comment.