diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/10_voice_receive/Cargo.toml | 14 | ||||
| -rw-r--r-- | examples/10_voice_receive/src/main.rs | 158 |
2 files changed, 172 insertions, 0 deletions
diff --git a/examples/10_voice_receive/Cargo.toml b/examples/10_voice_receive/Cargo.toml new file mode 100644 index 0000000..fd1a520 --- /dev/null +++ b/examples/10_voice_receive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "10_voice_receive" +version = "0.1.0" +authors = ["my name <[email protected]>"] + +[dependencies] +byteorder = "^1.2" +env_logger = "~0.4" +log = "~0.4" +typemap = "~0.3" + +[dependencies.serenity] +features = ["client", "standard_framework", "voice"] +path = "../../" diff --git a/examples/10_voice_receive/src/main.rs b/examples/10_voice_receive/src/main.rs new file mode 100644 index 0000000..09449d6 --- /dev/null +++ b/examples/10_voice_receive/src/main.rs @@ -0,0 +1,158 @@ +//! Requires the "client", "standard_framework", and "voice" features be enabled +//! in your Cargo.toml, like so: +//! +//! ```toml +//! [dependencies.serenity] +//! git = "https://github.com/zeyla/serenity.git" +//! features = ["client", "standard_framework", "voice"] +//! ``` + +#[macro_use] extern crate serenity; + +extern crate typemap; + +use serenity::client::bridge::voice::ClientVoiceManager; +use serenity::client::{CACHE, Client, Context, EventHandler}; +use serenity::framework::StandardFramework; +use serenity::model::channel::Message; +use serenity::model::gateway::Ready; +use serenity::model::id::ChannelId; +use serenity::model::misc::Mentionable; +use serenity::prelude::Mutex; +use serenity::voice::AudioReceiver; +use serenity::Result as SerenityResult; +use std::sync::Arc; +use std::env; +use typemap::Key; + +struct VoiceManager; + +impl Key for VoiceManager { + type Value = Arc<Mutex<ClientVoiceManager>>; +} + +struct Handler; + +impl EventHandler for Handler { + fn ready(&self, _: Context, ready: Ready) { + println!("{} is connected!", ready.user.name); + } +} + +struct Receiver; + +impl Receiver { + pub fn new() -> Self { + // You can manage state here, such as a buffer of audio packet bytes so + // you can later store them in intervals. + Self { } + } +} + +impl AudioReceiver for Receiver { + fn speaking_update(&mut self, _ssrc: u32, _user_id: u64, _speaking: bool) { + // You can implement logic here so that you can differentiate users' + // SSRCs and map the SSRC to the User ID and maintain a state in + // `Receiver`. Using this map, you can map the `ssrc` in `voice_packet` + // to the user ID and handle their audio packets separately. + } + + fn voice_packet(&mut self, ssrc: u32, sequence: u16, _timestamp: u32, _stereo: bool, data: &[i16]) { + println!("Audio packet's first 5 bytes: {:?}", data.get(..5)); + println!( + "Audio packet sequence {:05} has {:04} bytes, SSRC {}", + sequence, + data.len(), + ssrc, + ); + } +} + +fn main() { + // Configure the client with your Discord bot token in the environment. + let token = env::var("DISCORD_TOKEN") + .expect("Expected a token in the environment"); + let mut client = Client::new(&token, Handler).expect("Err creating client"); + + // Obtain a lock to the data owned by the client, and insert the client's + // voice manager into it. This allows the voice manager to be accessible by + // event handlers and framework commands. + { + let mut data = client.data.lock(); + data.insert::<VoiceManager>(Arc::clone(&client.voice_manager)); + } + + client.with_framework(StandardFramework::new() + .configure(|c| c + .prefix("~") + .on_mention(true)) + .cmd("join", join) + .cmd("leave", leave) + .cmd("ping", ping)); + + let _ = client.start().map_err(|why| println!("Client ended: {:?}", why)); +} + +command!(join(ctx, msg, args) { + let connect_to = match args.single::<u64>() { + Ok(id) => ChannelId(id), + Err(_) => { + check_msg(msg.reply("Requires a valid voice channel ID be given")); + + return Ok(()); + }, + }; + + let guild_id = match CACHE.read().guild_channel(msg.channel_id) { + Some(channel) => channel.read().guild_id, + None => { + check_msg(msg.channel_id.say("Groups and DMs not supported")); + + return Ok(()); + }, + }; + + let mut manager_lock = ctx.data.lock().get::<VoiceManager>().cloned().unwrap(); + let mut manager = manager_lock.lock(); + + if let Some(handler) = manager.join(guild_id, connect_to) { + handler.listen(Some(Box::new(Receiver::new()))); + check_msg(msg.channel_id.say(&format!("Joined {}", connect_to.mention()))); + } else { + check_msg(msg.channel_id.say("Error joining the channel")); + } +}); + +command!(leave(ctx, msg) { + let guild_id = match CACHE.read().guild_channel(msg.channel_id) { + Some(channel) => channel.read().guild_id, + None => { + check_msg(msg.channel_id.say("Groups and DMs not supported")); + + return Ok(()); + }, + }; + + let mut manager_lock = ctx.data.lock().get::<VoiceManager>().cloned().unwrap(); + let mut manager = manager_lock.lock(); + let has_handler = manager.get(guild_id).is_some(); + + if has_handler { + manager.remove(guild_id); + + check_msg(msg.channel_id.say("Left voice channel")); + } else { + check_msg(msg.reply("Not in a voice channel")); + } +}); + +command!(ping(_context, msg) { + check_msg(msg.channel_id.say("Pong!")); +}); + +/// Checks that a message successfully sent; if not, then logs why to stdout. +fn check_msg(result: SerenityResult<Message>) { + if let Err(why) = result { + println!("Error sending message: {:?}", why); + } +} |