aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands.rs257
-rw-r--r--src/main.rs70
-rw-r--r--src/util.rs20
3 files changed, 316 insertions, 31 deletions
diff --git a/src/commands.rs b/src/commands.rs
index e802bde..ae8ae25 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -1,6 +1,251 @@
-//use serenity::prelude::*;
-//use serenity::framework::StandardFramework;
-//
-//pub fn register_commands(mut f: StandardFramework) -> StandardFramework {
-// f
-//}
+use std::sync::{Arc, Mutex as SMutex};
+use std::collections::VecDeque;
+
+use serenity::prelude::*;
+use serenity::client::bridge::voice::ClientVoiceManager;
+use serenity::framework::StandardFramework;
+use serenity::model::channel::GuildChannel;
+use serenity::voice::LockedAudio;
+
+use typemap::Key;
+
+use {Result, TARGET_GUILD_ID};
+
+pub struct VoiceManager;
+
+impl Key for VoiceManager {
+ type Value = Arc<Mutex<ClientVoiceManager>>;
+}
+
+impl VoiceManager {
+ pub fn register(c: &mut Client) {
+ let mut data = c.data.lock();
+ data.insert::<VoiceManager>(Arc::clone(&c.voice_manager));
+ }
+}
+
+struct PlayArgs {
+ url: String,
+ initiator: String,
+}
+
+struct CurrentItem {
+ init_args: PlayArgs,
+ audio: Option<LockedAudio>,
+}
+
+pub struct PlayQueue {
+ queue: VecDeque<PlayArgs>,
+ playing: Option<CurrentItem>,
+}
+
+impl Key for PlayQueue {
+ type Value = Arc<SMutex<PlayQueue>>;
+}
+
+impl PlayQueue {
+ fn new() -> Self {
+ PlayQueue {
+ queue: VecDeque::new(),
+ playing: None,
+ }
+ }
+
+ pub fn register(c: &mut Client) {
+ let mut data = c.data.lock();
+ data.insert::<PlayQueue>(Arc::new(SMutex::new(PlayQueue::new())));
+ }
+
+ fn advance(&mut self) {
+ self.queue.pop_front().map(|info| {
+ self.playing = Some(CurrentItem {
+ init_args: info,
+ audio: None,
+ });
+ });
+ }
+}
+
+fn send(channel: &GuildChannel, text: &str, tts: bool) -> Result<()> {
+ channel.send_message(|m| m.content(text).tts(tts))?;
+ Ok(())
+}
+
+pub fn register_commands(f: StandardFramework) -> StandardFramework {
+ f
+ .cmd("skip", skip)
+ .cmd("pause", pause)
+ .cmd("resume", resume)
+ .cmd("list", list)
+ .cmd("die", die)
+ .cmd("meme", meme)
+ .cmd("mute", mute)
+ .cmd("unmute", unmute)
+}
+
+command!(skip(ctx, _msg) {
+ let data = ctx.data.lock();
+
+ let mut mgr_lock = data.get::<VoiceManager>().cloned().unwrap();
+ let mut manager = mgr_lock.lock();
+
+ let mut queue_lock = data.get::<PlayQueue>().cloned().unwrap();
+ let mut play_queue = queue_lock.lock().unwrap();
+
+ if let Some(handler) = manager.get_mut(*TARGET_GUILD_ID) {
+ handler.stop();
+ play_queue.advance();
+ } else {
+ debug!("got skip with no handler attached");
+ }
+});
+
+command!(pause(ctx, msg) {
+ let mut queue_lock = ctx.data.lock().get::<PlayQueue>().cloned().unwrap();
+ let mut play_queue = queue_lock.lock().unwrap();
+
+ let channel_tmp = msg.channel().unwrap().guild().unwrap();
+ let channel = channel_tmp.read();
+
+ let done = || send(&channel, "r u srs", msg.tts);
+
+ let current_item = match play_queue.playing {
+ Some(ref x) => x,
+ None => {
+ done()?;
+ return Ok(());
+ },
+ };
+
+ let locked_audio = match current_item.audio {
+ Some(ref x) => x,
+ None => {
+ done()?;
+ return Ok(());
+ },
+ };
+
+ let mut audio = locked_audio.lock();
+
+ if !audio.playing {
+ done()?;
+ return Ok(());
+ }
+
+ audio.pause();
+});
+
+command!(resume(ctx, msg) {
+ let mut queue_lock = ctx.data.lock().get::<PlayQueue>().cloned().unwrap();
+ let mut play_queue = queue_lock.lock().unwrap();
+
+ let channel_tmp = msg.channel().unwrap().guild().unwrap();
+ let channel = channel_tmp.read();
+
+ let done = || send(&channel, "r u srs", msg.tts);
+
+ let current_item = match play_queue.playing {
+ Some(ref x) => x,
+ None => {
+ done()?;
+ return Ok(());
+ },
+ };
+
+ let locked_audio = match current_item.audio {
+ Some(ref x) => x,
+ None => {
+ done()?;
+ return Ok(());
+ },
+ };
+
+ let mut audio = locked_audio.lock();
+
+ if audio.playing {
+ done()?;
+ return Ok(());
+ }
+
+ audio.play();
+});
+
+command!(die(ctx, msg) {
+ let data = ctx.data.lock();
+
+ let mut mgr_lock = data.get::<VoiceManager>().cloned().unwrap();
+ let mut manager = mgr_lock.lock();
+
+ let mut queue_lock = data.get::<PlayQueue>().cloned().unwrap();
+ let mut play_queue = queue_lock.lock().unwrap();
+
+ let channel_tmp = msg.channel().unwrap().guild().unwrap();
+ let channel = channel_tmp.read();
+
+ play_queue.playing = None;
+ play_queue.queue.clear();
+
+ if let Some(handler) = manager.get_mut(*TARGET_GUILD_ID) {
+ handler.stop();
+ } else {
+ send(&channel, "YOU die", msg.tts)?;
+ debug!("got die with no handler attached");
+ }
+});
+
+command!(list(ctx, msg) {
+ let mut queue_lock = ctx.data.lock().get::<PlayQueue>().cloned().unwrap();
+ let mut play_queue = queue_lock.lock().unwrap();
+
+ let channel_tmp = msg.channel().unwrap().guild().unwrap();
+ let channel = channel_tmp.read();
+
+ match play_queue.playing {
+ Some(ref info) => {
+ send(&channel, &format!("Currently playing {} ({})", info.init_args.url, info.init_args.initiator), msg.tts)?;
+ },
+ None => {
+ debug!("`list` called with no items in queue");
+ send(&channel, "Nothing is playing you fucking meme", msg.tts)?;
+ return Ok(());
+ },
+ }
+
+ play_queue.queue.iter().for_each(|info| {
+ channel.say(&format!("{} ({})", info.url, info.initiator)).unwrap();
+ });
+});
+
+command!(meme(_ctx, _msg) {
+
+});
+
+command!(mute(ctx, _msg) {
+ let mut mgr_lock = ctx.data.lock().get::<VoiceManager>().cloned().unwrap();
+ let mut manager = mgr_lock.lock();
+
+ manager.get_mut(*TARGET_GUILD_ID)
+ .map(|handler| {
+ if handler.self_mute {
+ trace!("Already muted.")
+ } else {
+ handler.mute(true);
+ trace!("Muted");
+ }
+ });
+});
+
+command!(unmute(ctx, _msg) {
+ let mut mgr_lock = ctx.data.lock().get::<VoiceManager>().cloned().unwrap();
+ let mut manager = mgr_lock.lock();
+
+ manager.get_mut(*TARGET_GUILD_ID)
+ .map(|handler| {
+ if !handler.self_mute {
+ trace!("Already unmuted.")
+ } else {
+ handler.mute(false);
+ trace!("Unmuted");
+ }
+ });
+}); \ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 58e059e..007aae9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,5 @@
+#![feature(rustc_private)]
+
#[macro_use] extern crate serenity;
#[macro_use] extern crate log;
#[macro_use] extern crate error_chain;
@@ -7,44 +9,50 @@ extern crate dotenv;
extern crate simple_logger;
extern crate typemap;
extern crate url;
+extern crate parking_lot;
mod commands;
mod util;
+use std::env;
+use std::thread;
+use std::time::{Duration, Instant};
+
+use serenity::prelude::*;
+use serenity::framework::StandardFramework;
+use serenity::framework::standard::help_commands;
+use serenity::model::gateway::Ready;
+use serenity::model::id::{UserId, GuildId};
+
+use dotenv::dotenv;
+
+use commands::register_commands;
+
mod errors {
error_chain! {
foreign_links {
Serenity(::serenity::Error);
- OS(::std::env::VarError);
+ MissingVar(::std::env::VarError);
}
}
}
use errors::*;
-
pub use util::*;
-use std::env;
-use std::collections::HashSet;
-use std::thread;
-use std::time::{Duration, Instant};
-
-use serenity::prelude::*;
-use serenity::framework::StandardFramework;
-use serenity::framework::standard::help_commands;
-use serenity::model::gateway::Ready;
-
-use dotenv::dotenv;
+lazy_static! {
+ static ref TARGET_GUILD: u64 = must_env_lookup::<u64>("TARGET_GUILD");
+ static ref TARGET_GUILD_ID: GuildId = GuildId(*TARGET_GUILD);
+}
struct Handler;
impl EventHandler for Handler {
- fn ready(&self, _c: Context, r: Ready) {
- let guild = r.guilds.iter().find(|g| {
- g.id().0 == 0
- });
+ fn ready(&self, _: Context, r: Ready) {
+ let guild = r.guilds.iter()
+ .find(|g| g.id().0 == *TARGET_GUILD);
if guild.is_none() {
- info!("bot isn't in configured guild. let it join here: {:?}", OAUTH_URL.as_str());
+ info!("bot isn't in configured guild. join here: {:?}", OAUTH_URL.as_str());
}
}
}
@@ -52,25 +60,30 @@ impl EventHandler for Handler {
fn run() -> Result<()> {
let token = &env::var("THULANI_TOKEN")?;
let mut client = Client::new(token, Handler)?;
- let framework = StandardFramework::new()
+
+ commands::VoiceManager::register(&mut client);
+ commands::PlayQueue::register(&mut client);
+
+ let owner_id = must_env_lookup::<u64>("OWNER_ID");
+ let mut framework = StandardFramework::new()
.configure(|c| c
.allow_dm(false)
.allow_whitespace(true)
.prefixes(vec!["!thulani ", "!thulan ", "!thulando madando ", "!thulando "])
.ignore_bots(true)
.on_mention(false)
- .owners(HashSet::new())
+ .owners(vec![UserId(owner_id)].into_iter().collect())
.case_insensitivity(true)
)
.before(|_ctx, message, cmd| {
debug!("got command {} from user '{}' ({})", cmd, message.author.name, message.author.id);
-
- true
+
+ message.guild_id().map_or(false, |x| x.0 == *TARGET_GUILD)
})
- .after(|_ctx, _msg, cmd, err| {
+ .after(|_ctx, _msg, _cmd, err| {
match err {
Ok(()) => {},
- Err(e) => {
+ Err(_e) => {
}
@@ -81,6 +94,8 @@ fn run() -> Result<()> {
c
});
+ framework = register_commands(framework);
+
client.with_framework(framework);
client.start()?;
@@ -107,9 +122,14 @@ fn main() {
Err(e) => {
error!("error encountered running client: {}", e);
e.iter().skip(1).for_each(|e| {
-
+ error!("caused by: {}", e);
});
+ if let Some(bt) = e.backtrace() {
+ error!("backtrace: {:?}", bt);
+ }
+
+ ::std::process::exit(1);
},
_ => {
warn!("somehow `run` completed without an error. should probably take a look at this.");
diff --git a/src/util.rs b/src/util.rs
index 6f70037..54f10ee 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,6 +1,11 @@
use std::env;
+use std::str::FromStr;
use serenity::model::permissions::Permissions;
+use serenity::model::id::GuildId;
+use serenity::model::channel::Message;
+use serenity::client::CACHE;
+
use url::Url;
lazy_static! {
@@ -26,3 +31,18 @@ lazy_static! {
)
).unwrap();
}
+
+pub fn must_env_lookup<T: FromStr>(s: &str) -> T {
+ env::var(s).expect(&format!("missing env var {}", s))
+ .parse::<T>().unwrap_or_else(|_| panic!(format!("bad format for {}", s)))
+}
+
+pub trait GuildLookup {
+ fn guild_id(&self) -> Option<GuildId>;
+}
+
+impl GuildLookup for Message {
+ fn guild_id(&self) -> Option<GuildId> {
+ CACHE.read().guild_channel(self.channel_id).map(|c| c.read().guild_id)
+ }
+} \ No newline at end of file