aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMishio595 <[email protected]>2018-07-09 21:57:59 -0600
committerMishio595 <[email protected]>2018-07-09 21:57:59 -0600
commitb457eb5415e692f2edc62c39a7d936d6976654de (patch)
tree0155ba89ea4d16a3ec05a5ee88e5844c58f40c27 /src
parentMerge https://github.com/serenity-rs/serenity (diff)
parentFix ffmpeg_optioned doctest (diff)
downloadserenity-b457eb5415e692f2edc62c39a7d936d6976654de.tar.xz
serenity-b457eb5415e692f2edc62c39a7d936d6976654de.zip
Merge https://github.com/serenity-rs/serenity
Diffstat (limited to 'src')
-rw-r--r--src/cache/mod.rs77
-rw-r--r--src/cache/settings.rs50
-rw-r--r--src/client/dispatch.rs8
-rw-r--r--src/framework/standard/args.rs19
-rw-r--r--src/framework/standard/command.rs4
-rw-r--r--src/framework/standard/mod.rs2
-rw-r--r--src/lib.rs9
-rw-r--r--src/model/channel/message.rs25
-rw-r--r--src/model/event.rs81
-rw-r--r--src/model/id.rs6
-rw-r--r--src/utils/colour.rs78
-rw-r--r--src/voice/mod.rs1
-rw-r--r--src/voice/streamer.rs43
13 files changed, 343 insertions, 60 deletions
diff --git a/src/cache/mod.rs b/src/cache/mod.rs
index 3bf5a5a..6b6e1c4 100644
--- a/src/cache/mod.rs
+++ b/src/cache/mod.rs
@@ -47,7 +47,8 @@ use parking_lot::RwLock;
use std::collections::{
hash_map::Entry,
HashMap,
- HashSet
+ HashSet,
+ VecDeque,
};
use std::{
default::Default,
@@ -55,8 +56,12 @@ use std::{
};
mod cache_update;
+mod settings;
pub use self::cache_update::CacheUpdate;
+pub use self::settings::Settings;
+
+type MessageCache = HashMap<ChannelId, HashMap<MessageId, Message>>;
/// A cache of all events received over a [`Shard`], where storing at least
/// some data from the event is possible.
@@ -97,6 +102,12 @@ pub struct Cache {
/// [`Emoji`]: ../model/guild/struct.Emoji.html
/// [`Role`]: ../model/guild/struct.Role.html
pub guilds: HashMap<GuildId, Arc<RwLock<Guild>>>,
+ /// A map of channels to messages.
+ ///
+ /// This is a map of channel IDs to another map of message IDs to messages.
+ ///
+ /// This keeps only the ten most recent messages.
+ pub messages: MessageCache,
/// A map of notes that a user has made for individual users.
///
/// An empty note is equivalent to having no note, and creating an empty
@@ -160,6 +171,15 @@ pub struct Cache {
/// [`PresenceUpdateEvent`]: ../model/event/struct.PresenceUpdateEvent.html
/// [`ReadyEvent`]: ../model/event/struct.ReadyEvent.html
pub users: HashMap<UserId, Arc<RwLock<User>>>,
+ /// Queue of message IDs for each channel.
+ ///
+ /// This is simply a vecdeque so we can keep track of the order of messages
+ /// inserted into the cache. When a maximum number of messages are in a
+ /// channel's cache, we can pop the front and remove that ID from the cache.
+ pub(crate) message_queue: HashMap<ChannelId, VecDeque<MessageId>>,
+ /// The settings for the cache.
+ settings: Settings,
+ __nonexhaustive: (),
}
impl Cache {
@@ -169,6 +189,25 @@ impl Cache {
Self::default()
}
+ /// Creates a new cache instance with settings applied.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::cache::{Cache, Settings};
+ ///
+ /// let mut settings = Settings::new();
+ /// settings.max_messages(10);
+ ///
+ /// let cache = Cache::new_with_settings(settings);
+ /// ```
+ pub fn new_with_settings(settings: Settings) -> Self {
+ Self {
+ settings,
+ ..Default::default()
+ }
+ }
+
/// Fetches the number of [`Member`]s that have not had data received.
///
/// The important detail to note here is that this is the number of
@@ -622,6 +661,38 @@ impl Cache {
.and_then(|g| g.read().roles.get(&role_id).cloned())
}
+ /// Returns an immutable reference to the settings.
+ ///
+ /// # Examples
+ ///
+ /// Printing the maximum number of messages in a channel to be cached:
+ ///
+ /// ```rust
+ /// use serenity::cache::Cache;
+ ///
+ /// let mut cache = Cache::new();
+ /// println!("Max settings: {}", cache.settings().max_messages);
+ /// ```
+ pub fn settings(&self) -> &Settings {
+ &self.settings
+ }
+
+ /// Returns a mutable reference to the settings.
+ ///
+ /// # Examples
+ ///
+ /// Create a new cache and modify the settings afterwards:
+ ///
+ /// ```rust
+ /// use serenity::cache::Cache;
+ ///
+ /// let mut cache = Cache::new();
+ /// cache.settings_mut().max_messages(10);
+ /// ```
+ pub fn settings_mut(&mut self) -> &mut Settings {
+ &mut self.settings
+ }
+
/// Retrieves a `User` from the cache's [`users`] map, if it exists.
///
/// The only advantage of this method is that you can pass in anything that
@@ -704,13 +775,17 @@ impl Default for Cache {
categories: HashMap::default(),
groups: HashMap::with_capacity(128),
guilds: HashMap::default(),
+ messages: HashMap::default(),
notes: HashMap::default(),
presences: HashMap::default(),
private_channels: HashMap::with_capacity(128),
+ settings: Settings::default(),
shard_count: 1,
unavailable_guilds: HashSet::default(),
user: CurrentUser::default(),
users: HashMap::default(),
+ message_queue: HashMap::default(),
+ __nonexhaustive: (),
}
}
}
diff --git a/src/cache/settings.rs b/src/cache/settings.rs
new file mode 100644
index 0000000..e8e456b
--- /dev/null
+++ b/src/cache/settings.rs
@@ -0,0 +1,50 @@
+/// Settings for the cache.
+///
+/// # Examples
+///
+/// Create new settings, specifying the maximum number of messages:
+///
+/// ```rust
+/// use serenity::cache::Settings as CacheSettings;
+///
+/// let mut settings = CacheSettings::new();
+/// settings.max_messages(10);
+/// ```
+#[derive(Clone, Debug, Default)]
+pub struct Settings {
+ /// The maximum number of messages to store in a channel's message cache.
+ ///
+ /// Defaults to 0.
+ pub max_messages: usize,
+ __nonexhaustive: (),
+}
+
+impl Settings {
+ /// Creates new settings to be used with a cache.
+ #[inline]
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Sets the maximum number of messages to cache in a channel.
+ ///
+ /// Refer to [`max_messages`] for more information.
+ ///
+ /// # Examples
+ ///
+ /// Set the maximum number of messages to cache:
+ ///
+ /// ```rust
+ /// use serenity::cache::Settings;
+ ///
+ /// let mut settings = Settings::new();
+ /// settings.max_messages(10);
+ /// ```
+ ///
+ /// [`max_messages`]: #structfield.max_messages
+ pub fn max_messages(&mut self, max: usize) -> &mut Self {
+ self.max_messages = max;
+
+ self
+ }
+}
diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs
index b91b8d7..04bd8c9 100644
--- a/src/client/dispatch.rs
+++ b/src/client/dispatch.rs
@@ -68,7 +68,9 @@ pub(crate) fn dispatch<H: EventHandler + Send + Sync + 'static>(
shard_id: u64,
) {
match event {
- DispatchEvent::Model(Event::MessageCreate(event)) => {
+ DispatchEvent::Model(Event::MessageCreate(mut event)) => {
+ update!(event);
+
let context = context(data, runner_tx, shard_id);
dispatch_message(
context.clone(),
@@ -103,6 +105,8 @@ pub(crate) fn dispatch<H: EventHandler + Send + Sync + 'static>(
) {
match event {
DispatchEvent::Model(Event::MessageCreate(event)) => {
+ update!(event);
+
let context = context(data, runner_tx, shard_id);
dispatch_message(context, event.message, event_handler, threadpool);
},
@@ -506,6 +510,8 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::MessageUpdate(mut event)) => {
+ update!(event);
+
let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs
index 2648a30..8af8124 100644
--- a/src/framework/standard/args.rs
+++ b/src/framework/standard/args.rs
@@ -53,6 +53,20 @@ impl<E: StdError> fmt::Display for Error<E> {
type Result<T, E> = ::std::result::Result<T, Error<E>>;
+fn find_start(s: &str, i: usize) -> Option<usize> {
+ if i > s.len() {
+ return None;
+ }
+
+ let mut start = i - 1;
+
+ while !s.is_char_boundary(start) {
+ start -= 1;
+ }
+
+ Some(start)
+}
+
fn find_end(s: &str, i: usize) -> Option<usize> {
if i > s.len() {
return None;
@@ -176,10 +190,11 @@ impl<'a> Lexer<'a> {
}
self.next();
+
let end = self.offset;
- return if self.at_end() && &self.msg[end-1..end] != "\"" {
- // invalid, missing an end quote; view it as a normal argument instead.
+ return if self.at_end() && &self.msg[find_start(self.msg, end).unwrap()..end] != "\"" {
+ // We're missing an end quote. View this as a normal argument.
Token::new(TokenKind::Argument, &self.msg[start..], start)
} else {
Token::new(TokenKind::QuotedArgument, &self.msg[start..end], start)
diff --git a/src/framework/standard/command.rs b/src/framework/standard/command.rs
index f88d2de..0fa51f9 100644
--- a/src/framework/standard/command.rs
+++ b/src/framework/standard/command.rs
@@ -247,8 +247,8 @@ impl Default for HelpOptions {
lacking_role: HelpBehaviour::Strike,
lacking_permissions: HelpBehaviour::Strike,
wrong_channel: HelpBehaviour::Strike,
- embed_error_colour: Colour::dark_red(),
- embed_success_colour: Colour::rosewater(),
+ embed_error_colour: Colour::DARK_RED,
+ embed_success_colour: Colour::ROSEWATER,
}
}
}
diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs
index 95fa074..435069e 100644
--- a/src/framework/standard/mod.rs
+++ b/src/framework/standard/mod.rs
@@ -529,7 +529,7 @@ impl StandardFramework {
// If not, assert that it does always.
let apply = bucket.check.as_ref().map_or(true, |check| {
let apply = feature_cache! {{
- let guild_id = message.guild_id();
+ let guild_id = message.guild_id;
(check)(context, guild_id, message.channel_id, message.author.id)
} else {
(check)(context, message.channel_id, message.author.id)
diff --git a/src/lib.rs b/src/lib.rs
index 196a3e0..74f050a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -202,6 +202,15 @@ lazy_static! {
/// println!("{}", CACHE.read().user.id);
/// ```
///
+ /// Update the cache's settings to enable caching of channels' messages:
+ ///
+ /// ```rust
+ /// use serenity::CACHE;
+ ///
+ /// // Cache up to the 10 most recent messages per channel.
+ /// CACHE.write().settings_mut().max_messages(10);
+ /// ```
+ ///
/// [`CurrentUser`]: model/struct.CurrentUser.html
/// [`Cache`]: cache/struct.Cache.html
/// [cache module documentation]: cache/index.html
diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs
index be17772..3134725 100644
--- a/src/model/channel/message.rs
+++ b/src/model/channel/message.rs
@@ -30,17 +30,17 @@ pub struct Message {
///
/// [`Channel`]: enum.Channel.html
pub channel_id: ChannelId,
- /// The Id of the [`Guild`] that the message was sent in. This value will
- /// only be present if this message was received over the gateway.
- ///
- /// [`Guild`]: ../guild/struct.Guild.html
- pub guild_id: Option<GuildId>,
/// The content of the message.
pub content: String,
/// The timestamp of the last time the message was updated, if it was.
pub edited_timestamp: Option<DateTime<FixedOffset>>,
/// Array of embeds sent with the message.
pub embeds: Vec<Embed>,
+ /// The Id of the [`Guild`] that the message was sent in. This value will
+ /// only be present if this message was received over the gateway.
+ ///
+ /// [`Guild`]: ../guild/struct.Guild.html
+ pub guild_id: Option<GuildId>,
/// Indicator of the type of message this is, i.e. whether it is a regular
/// message or a system message.
#[serde(rename = "type")]
@@ -337,21 +337,18 @@ impl Message {
/// [`guild_id`]: #method.guild_id
#[cfg(feature = "cache")]
pub fn guild(&self) -> Option<Arc<RwLock<Guild>>> {
- self.guild_id()
- .and_then(|guild_id| CACHE.read().guild(guild_id))
+ CACHE.read().guild(self.guild_id?)
}
/// Retrieves the Id of the guild that the message was sent in, if sent in
/// one.
///
- /// Returns `None` if the channel data or guild data does not exist in the
- /// cache.
- #[cfg(feature = "cache")]
+ /// Refer to [`guild_id`] for more information.
+ ///
+ /// [`guild_id`]: #structfield.guild_id
+ #[deprecated(note = "Use `guild_id` structfield instead", since = "0.5.5")]
pub fn guild_id(&self) -> Option<GuildId> {
- match CACHE.read().channel(self.channel_id) {
- Some(Channel::Guild(ch)) => Some(ch.read().guild_id),
- _ => None,
- }
+ self.guild_id
}
/// True if message was sent using direct messages.
diff --git a/src/model/event.rs b/src/model/event.rs
index d78e7df..7a7e3de 100644
--- a/src/model/event.rs
+++ b/src/model/event.rs
@@ -153,6 +153,9 @@ impl CacheUpdate for ChannelDeleteEvent {
Channel::Private(_) | Channel::Group(_) => unreachable!(),
};
+ // Remove the cached messages for the channel.
+ cache.messages.remove(&self.channel.id());
+
None
}
}
@@ -400,7 +403,11 @@ impl CacheUpdate for GuildDeleteEvent {
// Remove channel entries for the guild if the guild is found.
cache.guilds.remove(&self.guild.id).map(|guild| {
for channel_id in guild.write().channels.keys() {
+ // Remove the channel from the cache.
cache.channels.remove(channel_id);
+
+ // Remove the channel's cached messages.
+ cache.messages.remove(channel_id);
}
guild
@@ -759,6 +766,40 @@ pub struct MessageCreateEvent {
pub message: Message,
}
+#[cfg(feature = "cache")]
+impl CacheUpdate for MessageCreateEvent {
+ /// The oldest message, if the channel's message cache was already full.
+ type Output = Message;
+
+ fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
+ let max = cache.settings().max_messages;
+
+ if max == 0 {
+ return None;
+ }
+
+ let messages = cache.messages
+ .entry(self.message.channel_id)
+ .or_insert_with(Default::default);
+ let queue = cache.message_queue
+ .entry(self.message.channel_id)
+ .or_insert_with(Default::default);
+
+ let mut removed_msg = None;
+
+ if messages.len() == max {
+ if let Some(id) = queue.pop_front() {
+ removed_msg = messages.remove(&id);
+ }
+ }
+
+ queue.push_back(self.message.id);
+ messages.insert(self.message.id, self.message.clone());
+
+ removed_msg
+ }
+}
+
impl<'de> Deserialize<'de> for MessageCreateEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
Ok(Self {
@@ -805,6 +846,46 @@ pub struct MessageUpdateEvent {
pub embeds: Option<Vec<Value>>,
}
+#[cfg(feature = "cache")]
+impl CacheUpdate for MessageUpdateEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
+ let messages = cache.messages.get_mut(&self.channel_id)?;
+ let message = messages.get_mut(&self.id)?;
+
+ if let Some(attachments) = self.attachments.clone() {
+ message.attachments = attachments;
+ }
+
+ if let Some(content) = self.content.clone() {
+ message.content = content;
+ }
+
+ if let Some(edited_timestamp) = self.edited_timestamp.clone() {
+ message.edited_timestamp = Some(edited_timestamp);
+ }
+
+ if let Some(mentions) = self.mentions.clone() {
+ message.mentions = mentions;
+ }
+
+ if let Some(mention_everyone) = self.mention_everyone {
+ message.mention_everyone = mention_everyone;
+ }
+
+ if let Some(mention_roles) = self.mention_roles.clone() {
+ message.mention_roles = mention_roles;
+ }
+
+ if let Some(pinned) = self.pinned {
+ message.pinned = pinned;
+ }
+
+ None
+ }
+}
+
#[derive(Clone, Debug, Serialize)]
pub struct PresenceUpdateEvent {
pub guild_id: Option<GuildId>,
diff --git a/src/model/id.rs b/src/model/id.rs
index 4e2fbc3..046de11 100644
--- a/src/model/id.rs
+++ b/src/model/id.rs
@@ -27,6 +27,12 @@ macro_rules! id_u64 {
}
}
+ impl<'a> From<&'a $name> for $name {
+ fn from(id: &'a $name) -> $name {
+ id.clone()
+ }
+ }
+
impl From<u64> for $name {
fn from(id_as_u64: u64) -> $name {
$name(id_as_u64)
diff --git a/src/utils/colour.rs b/src/utils/colour.rs
index 81b6ee9..cac3d46 100644
--- a/src/utils/colour.rs
+++ b/src/utils/colour.rs
@@ -2,10 +2,16 @@
#![allow(unreadable_literal)]
macro_rules! colour {
- ($(#[$attr:meta] $name:ident, $val:expr;)*) => {
+ ($(#[$attr:meta] $constname:ident, $name:ident, $val:expr;)*) => {
impl Colour {
$(
#[$attr]
+ pub const $constname: Colour = Colour($val);
+ )*
+
+ $(
+ #[$attr]
+ #[deprecated(note = "Use the constant instead", since = "0.5.5")]
pub fn $name() -> Colour {
Colour::new($val)
}
@@ -33,7 +39,7 @@ macro_rules! colour {
/// # use serenity::model::permissions;
/// #
/// # let role = Role {
-/// # colour: Colour::blurple(),
+/// # colour: Colour::BLURPLE,
/// # hoist: false,
/// # id: RoleId(1),
/// # managed: false,
@@ -52,12 +58,12 @@ macro_rules! colour {
/// println!("The green component is: {}", green);
/// ```
///
-/// Creating an instance with the [`dark_teal`] presets:
+/// Creating an instance with the [`DARK_TEAL`] preset:
///
/// ```rust
/// use serenity::utils::Colour;
///
-/// let colour = Colour::dark_teal();
+/// let colour = Colour::DARK_TEAL;
///
/// assert_eq!(colour.tuple(), (17, 128, 106));
/// ```
@@ -67,16 +73,16 @@ macro_rules! colour {
/// ```rust
/// use serenity::utils::Colour;
///
-/// let blitz_blue = Colour::blitz_blue();
-/// let fooyoo = Colour::fooyoo();
-/// let fooyoo2 = Colour::fooyoo();
+/// let blitz_blue = Colour::BLITZ_BLUE;
+/// let fooyoo = Colour::FOOYOO;
+/// let fooyoo2 = Colour::FOOYOO;
/// assert!(blitz_blue != fooyoo);
/// assert_eq!(fooyoo, fooyoo2);
/// assert!(blitz_blue > fooyoo);
/// ```
///
/// [`Role`]: ../model/guild/struct.Role.html
-/// [`dark_teal`]: #method.dark_teal
+/// [`DARK_TEAL`]: #associatedconstant.DARK_TEAL
/// [`g`]: #method.g
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Colour(pub u32);
@@ -254,61 +260,61 @@ impl From<(u8, u8, u8)> for Colour {
colour! {
/// Creates a new `Colour`, setting its RGB value to `(111, 198, 226)`.
- blitz_blue, 0x6FC6E2;
+ BLITZ_BLUE, blitz_blue, 0x6FC6E2;
/// Creates a new `Colour`, setting its RGB value to `(52, 152, 219)`.
- blue, 0x3498DB;
+ BLUE, blue, 0x3498DB;
/// Creates a new `Colour`, setting its RGB value to `(114, 137, 218)`.
- blurple, 0x7289DA;
+ BLURPLE, blurple, 0x7289DA;
/// Creates a new `Colour`, setting its RGB value to `(32, 102, 148)`.
- dark_blue, 0x206694;
+ DARK_BLUE, dark_blue, 0x206694;
/// Creates a new `Colour`, setting its RGB value to `(194, 124, 14)`.
- dark_gold, 0xC27C0E;
+ DARK_GOLD, dark_gold, 0xC27C0E;
/// Creates a new `Colour`, setting its RGB value to `(31, 139, 76)`.
- dark_green, 0x1F8B4C;
+ DARK_GREEN, dark_green, 0x1F8B4C;
/// Creates a new `Colour`, setting its RGB value to `(96, 125, 139)`.
- dark_grey, 0x607D8B;
+ DARK_GREY, dark_grey, 0x607D8B;
/// Creates a new `Colour`, setting its RGB value to `(173, 20, 87)`.
- dark_magenta, 0xAD1457;
+ DARK_MAGENTA, dark_magenta, 0xAD1457;
/// Creates a new `Colour`, setting its RGB value to `(168, 67, 0)`.
- dark_orange, 0xA84300;
+ DARK_ORANGE, dark_orange, 0xA84300;
/// Creates a new `Colour`, setting its RGB value to `(113, 54, 138)`.
- dark_purple, 0x71368A;
+ DARK_PURPLE, dark_purple, 0x71368A;
/// Creates a new `Colour`, setting its RGB value to `(153, 45, 34)`.
- dark_red, 0x992D22;
+ DARK_RED, dark_red, 0x992D22;
/// Creates a new `Colour`, setting its RGB value to `(17, 128, 106)`.
- dark_teal, 0x11806A;
+ DARK_TEAL, dark_teal, 0x11806A;
/// Creates a new `Colour`, setting its RGB value to `(84, 110, 122)`.
- darker_grey, 0x546E7A;
+ DARKER_GREY, darker_grey, 0x546E7A;
/// Creates a new `Colour`, setting its RGB value to `(250, 177, 237)`.
- fabled_pink, 0xFAB1ED;
+ FABLED_PINK, fabled_pink, 0xFAB1ED;
/// Creates a new `Colour`, setting its RGB value to `(136, 130, 196)`.`
- faded_purple, 0x8882C4;
+ FADED_PURPLE, faded_purple, 0x8882C4;
/// Creates a new `Colour`, setting its RGB value to `(17, 202, 128)`.
- fooyoo, 0x11CA80;
+ FOOYOO, fooyoo, 0x11CA80;
/// Creates a new `Colour`, setting its RGB value to `(241, 196, 15)`.
- gold, 0xF1C40F;
+ GOLD, gold, 0xF1C40F;
/// Creates a new `Colour`, setting its RGB value to `(186, 218, 85)`.
- kerbal, 0xBADA55;
+ KERBAL, kerbal, 0xBADA55;
/// Creates a new `Colour`, setting its RGB value to `(151, 156, 159)`.
- light_grey, 0x979C9F;
+ LIGHT_GREY, light_grey, 0x979C9F;
/// Creates a new `Colour`, setting its RGB value to `(149, 165, 166)`.
- lighter_grey, 0x95A5A6;
+ LIGHTER_GREY, lighter_grey, 0x95A5A6;
/// Creates a new `Colour`, setting its RGB value to `(233, 30, 99)`.
- magenta, 0xE91E63;
+ MAGENTA, magenta, 0xE91E63;
/// Creates a new `Colour`, setting its RGB value to `(230, 131, 151)`.
- meibe_pink, 0xE68397;
+ MEIBE_PINK, meibe_pink, 0xE68397;
/// Creates a new `Colour`, setting its RGB value to `(230, 126, 34)`.
- orange, 0xE67E22;
+ ORANGE, orange, 0xE67E22;
/// Creates a new `Colour`, setting its RGB value to `(155, 89, 182)`.
- purple, 0x9B59B6;
+ PURPLE, purple, 0x9B59B6;
/// Creates a new `Colour`, setting its RGB value to `(231, 76, 60)`.
- red, 0xE74C3C;
+ RED, red, 0xE74C3C;
/// Creates a new `Colour`, setting its RGB value to `(117, 150, 255)`.
- rohrkatze_blue, 0x7596FF;
+ ROHRKATZE_BLUE, rohrkatze_blue, 0x7596FF;
/// Creates a new `Colour`, setting its RGB value to `(246, 219, 216)`.
- rosewater, 0xF6DBD8;
+ ROSEWATER, rosewater, 0xF6DBD8;
/// Creates a new `Colour`, setting its RGB value to `(26, 188, 156)`.
- teal, 0x1ABC9C;
+ TEAL, teal, 0x1ABC9C;
}
impl Default for Colour {
diff --git a/src/voice/mod.rs b/src/voice/mod.rs
index 51fb0e2..7b5aaa0 100644
--- a/src/voice/mod.rs
+++ b/src/voice/mod.rs
@@ -26,6 +26,7 @@ pub use self::{
streamer::{
dca,
ffmpeg,
+ ffmpeg_optioned,
opus,
pcm,
ytdl
diff --git a/src/voice/streamer.rs b/src/voice/streamer.rs
index cd7cae8..893f92a 100644
--- a/src/voice/streamer.rs
+++ b/src/voice/streamer.rs
@@ -108,7 +108,7 @@ fn _ffmpeg(path: &OsStr) -> Result<Box<AudioSource>> {
let is_stereo = is_stereo(path).unwrap_or(false);
let stereo_val = if is_stereo { "2" } else { "1" };
- let args = [
+ ffmpeg_optioned(path, &[
"-f",
"s16le",
"-ac",
@@ -118,12 +118,49 @@ fn _ffmpeg(path: &OsStr) -> Result<Box<AudioSource>> {
"-acodec",
"pcm_s16le",
"-",
- ];
+ ])
+}
+
+/// Opens an audio file through `ffmpeg` and creates an audio source, with
+/// user-specified arguments to pass to ffmpeg.
+///
+/// Note that this does _not_ build on the arguments passed by the [`ffmpeg`]
+/// function.
+///
+/// # Examples
+///
+/// Pass options to create a custom ffmpeg streamer:
+///
+/// ```rust,no_run
+/// use serenity::voice;
+///
+/// let stereo_val = "2";
+///
+/// let streamer = voice::ffmpeg_optioned("./some_file.mp3", &[
+/// "-f",
+/// "s16le",
+/// "-ac",
+/// stereo_val,
+/// "-ar",
+/// "48000",
+/// "-acodec",
+/// "pcm_s16le",
+/// "-",
+/// ]);
+pub fn ffmpeg_optioned<P: AsRef<OsStr>>(
+ path: P,
+ args: &[&str],
+) -> Result<Box<AudioSource>> {
+ _ffmpeg_optioned(path.as_ref(), args)
+}
+
+fn _ffmpeg_optioned(path: &OsStr, args: &[&str]) -> Result<Box<AudioSource>> {
+ let is_stereo = is_stereo(path).unwrap_or(false);
let command = Command::new("ffmpeg")
.arg("-i")
.arg(path)
- .args(&args)
+ .args(args)
.stderr(Stdio::null())
.stdin(Stdio::null())
.stdout(Stdio::piped())