aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNathan Perry <np@nathanperry.dev>2024-05-08 10:28:04 -0400
committerNathan Perry <np@nathanperry.dev>2024-05-08 14:16:01 -0400
commitfe467f60d99efa54f2ef64606e7d39b9b06d7294 (patch)
treea62bb50fedb1959d1a155878f0ff0ab7b1f699b6 /src
parent48aa684dece2696e21fd871eb6f3825f28fe0200 (diff)
update all deps
Diffstat (limited to 'src')
-rw-r--r--src/audio/mod.rs28
-rw-r--r--src/audio/play_queue.rs4
-rw-r--r--src/bot.rs234
-rw-r--r--src/commands/help.rs8
-rw-r--r--src/commands/meme/create.rs127
-rw-r--r--src/commands/meme/delete.rs24
-rw-r--r--src/commands/meme/history.rs188
-rw-r--r--src/commands/meme/invoke.rs81
-rw-r--r--src/commands/meme/mod.rs115
-rw-r--r--src/commands/mod.rs74
-rw-r--r--src/commands/playback.rs144
-rw-r--r--src/commands/roll.rs86
-rw-r--r--src/commands/sound_levels.rs85
-rw-r--r--src/commands/today/mod.rs93
-rw-r--r--src/config.rs2
-rw-r--r--src/db/mod.rs2
-rw-r--r--src/db/models.rs1
-rw-r--r--src/db/schema.rs32
-rw-r--r--src/game.rs176
-rw-r--r--src/main.rs25
-rw-r--r--src/util.rs120
21 files changed, 937 insertions, 712 deletions
diff --git a/src/audio/mod.rs b/src/audio/mod.rs
index 3c3041f..9affdb1 100644
--- a/src/audio/mod.rs
+++ b/src/audio/mod.rs
@@ -3,24 +3,20 @@ use std::sync::Arc;
use chrono::Duration;
use either::Either;
use serenity::{
- client::bridge::voice::ClientVoiceManager,
- model::{
- id::{
- ChannelId,
- },
- },
+ model::id::ChannelId,
prelude::*,
- voice::LockedAudio,
};
use typemap::Key;
-pub use self::play_queue::PlayQueue;
-pub use self::timeutil::parse_times;
-pub use self::ytdl::*;
+pub use self::{
+ play_queue::PlayQueue,
+ timeutil::parse_times,
+ ytdl::*,
+};
+mod play_queue;
mod timeutil;
mod ytdl;
-mod play_queue;
pub struct VoiceManager;
@@ -37,15 +33,15 @@ impl VoiceManager {
#[derive(Clone, Debug)]
pub struct PlayArgs {
- pub data: Either<String, Vec<u8>>,
- pub initiator: String,
+ pub data: Either<String, Vec<u8>>,
+ pub initiator: String,
pub sender_channel: ChannelId,
- pub start: Option<Duration>,
- pub end: Option<Duration>,
+ pub start: Option<Duration>,
+ pub end: Option<Duration>,
}
#[derive(Clone)]
pub struct CurrentItem {
pub init_args: PlayArgs,
- pub audio: LockedAudio,
+ pub audio: LockedAudio,
}
diff --git a/src/audio/play_queue.rs b/src/audio/play_queue.rs
index ff1c2c9..34fc113 100644
--- a/src/audio/play_queue.rs
+++ b/src/audio/play_queue.rs
@@ -54,6 +54,10 @@ impl Key for PlayQueue {
type Value = Arc<RwLock<PlayQueue>>;
}
+impl serenity::prelude::TypeMapKey for PlayQueue {
+ type Value = Arc<RwLock<PlayQueue>>;
+}
+
impl PlayQueue {
pub fn new() -> Self {
PlayQueue {
diff --git a/src/bot.rs b/src/bot.rs
index abc0fd0..b3db4e9 100644
--- a/src/bot.rs
+++ b/src/bot.rs
@@ -1,54 +1,67 @@
use std::{
- sync::Mutex,
fs::File,
- result::Result as StdResult, path::PathBuf, str::FromStr,
-};
-
-use serenity::{
- prelude::*,
- model::{
- gateway::Ready,
- id::{
- ChannelId,
- MessageId,
- },
- channel::Message,
- event::ResumedEvent,
- },
- framework::StandardFramework,
+ future::Future,
+ path::PathBuf,
+ pin::Pin,
+ result::Result as StdResult,
+ str::FromStr,
+ sync::Mutex,
};
+use chrono::Datelike;
use fnv::{
FnvHashMap,
FnvHashSet,
};
-
-use serde_json::Value;
-use chrono::Datelike;
use lazy_static::lazy_static;
use log::{
debug,
- info,
error,
+ info,
trace,
warn,
};
+use serenity::{
+ all::{
+ GuildId,
+ ReactionType,
+ },
+ framework::{
+ standard::{
+ BucketBuilder,
+ CommandError,
+ Configuration,
+ },
+ StandardFramework,
+ },
+ model::{
+ channel::Message,
+ event::ResumedEvent,
+ gateway::Ready,
+ id::{
+ ChannelId,
+ MessageId,
+ },
+ },
+ prelude::*,
+};
use crate::{
- Result,
- Error,
audio,
- util::OAUTH_URL,
- util::CtxExt,
commands::register_commands,
config::CONFIG,
+ util,
+ util::OAUTH_URL,
+ Error,
+ Result,
};
struct Handler;
+
+#[serenity::async_trait]
impl EventHandler for Handler {
- fn ready(&self, ctx: Context, r: Ready) {
- let guild = r.guilds.iter()
- .find(|g| g.id() == CONFIG.discord.guild());
+ async fn ready(&self, ctx: Context, r: Ready) {
+ let guild = r.guilds.iter().find(|g| g.id() == CONFIG.discord.guild());
if guild.is_none() {
info!("bot isn't in configured guild. join here: {:?}", OAUTH_URL.as_str());
@@ -70,24 +83,26 @@ impl EventHandler for Handler {
});
}
- fn resume(&self, _ctx: Context, _resume: ResumedEvent) {
+ async fn resume(&self, _ctx: Context, _resume: ResumedEvent) {
info!("reconnected to discord");
}
- fn message_delete(&self, _ctx: Context, _channel_id: ChannelId, deleted_message_id: MessageId) {
- MESSAGE_WATCH.lock()
- .unwrap()
- .remove(&deleted_message_id);
- }
-
- fn unknown(&self, _ctx: Context, name: String, _raw: Value) {
- info!("unknown event: {:?}", name);
+ async fn message_delete(
+ &self,
+ _ctx: Context,
+ _: ChannelId,
+ deleted_message_id: MessageId,
+ _: Option<GuildId>,
+ ) {
+ MESSAGE_WATCH.lock().unwrap().remove(&deleted_message_id);
}
}
lazy_static! {
- static ref MESSAGE_WATCH: Mutex<FnvHashMap<MessageId, MessageId>> = Mutex::new(FnvHashMap::default());
- static ref PREFIXES: Vec<&'static str> = vec!["!thulani ", "!thulan ", "!thulando madando ", "!thulando "];
+ static ref MESSAGE_WATCH: Mutex<FnvHashMap<MessageId, MessageId>> =
+ Mutex::new(FnvHashMap::default());
+ static ref PREFIXES: Vec<&'static str> =
+ vec!["!thulani ", "!thulan ", "!thulando madando ", "!thulando "];
static ref RESTRICTED_PREFIXES: Vec<&'static str> = vec!["!todd ", "!toddbert ", "!toddlani "];
static ref ALL_PREFIXES: Vec<&'static str> = {
let mut all_prefixes: Vec<&'static str> = vec![];
@@ -95,7 +110,6 @@ lazy_static! {
all_prefixes.extend(RESTRICTED_PREFIXES.iter());
all_prefixes
};
-
static ref RESTRICT_IDS: FnvHashSet<u64> = {
let default_path = PathBuf::from_str("restrict.json").unwrap();
let restrict_path = CONFIG.restrict.as_ref().unwrap_or(&default_path);
@@ -108,10 +122,7 @@ lazy_static! {
warn!("opening restrict file: {}", e);
}
- let result = restrict_ids
- .unwrap_or_default()
- .into_iter()
- .collect::<FnvHashSet<_>>();
+ let result = restrict_ids.unwrap_or_default().into_iter().collect::<FnvHashSet<_>>();
info!("restricted ids: {:?}", result);
@@ -119,91 +130,107 @@ lazy_static! {
};
}
-fn framework() -> StandardFramework {
+async fn framework() -> StandardFramework {
+ let builder = BucketBuilder::default().delay(1).limit(20).time_span(60);
+
let framework = StandardFramework::new()
- .configure(|c| c
- .allow_dm(false)
- .with_whitespace(true)
- .prefixes(ALL_PREFIXES.iter())
- .ignore_bots(true)
- .on_mention(None)
- .owners(vec![CONFIG.discord.owner()].into_iter().collect())
- .case_insensitivity(true)
- )
.before(before_handle)
.after(after_handle)
- .bucket("Standard", |b| {
- b.delay(1).limit(20).time_span(60)
- });
+ .bucket("Standard", builder)
+ .await;
+
+ let config = Configuration::default()
+ .allow_dm(false)
+ .with_whitespace(true)
+ .prefixes(ALL_PREFIXES.iter().map(|x| x.to_string()))
+ .ignore_bots(true)
+ .on_mention(None)
+ .owners(vec![CONFIG.discord.owner()].into_iter().collect())
+ .case_insensitivity(true);
+
+ framework.configure(config);
register_commands(framework)
}
-fn before_handle(ctx: &mut Context, message: &Message, cmd: &str) -> bool {
+fn before_handle(
+ ctx: &Context,
+ message: &Message,
+ cmd: &str,
+) -> Pin<Box<dyn Future<Output = bool> + Send>> {
debug!("got command '{}' from user '{}' ({})", cmd, message.author.name, message.author.id);
- if !message.guild_id.map_or(false, |x| x == CONFIG.discord.guild()) {
- info!("rejecting command '{}' from user '{}': wrong guild", cmd, message.author.name);
- return false;
- }
+ Box::pin(async move {
+ if !message.guild_id.map_or(false, |x| x == CONFIG.discord.guild()) {
+ info!("rejecting command '{}' from user '{}': wrong guild", cmd, message.author.name);
+ return false;
+ }
- if message.author.id == CONFIG.discord.owner() {
- return true;
- }
+ if message.author.id == CONFIG.discord.owner() {
+ return true;
+ }
- let restricted_prefix = RESTRICTED_PREFIXES.iter()
- .any(|prefix| message.content.starts_with(prefix));
+ let restricted_prefix =
+ RESTRICTED_PREFIXES.iter().any(|prefix| message.content.starts_with(prefix));
- if !restricted_prefix {
- return true;
- }
+ if !restricted_prefix {
+ return true;
+ }
- const PERMITTED_WEEKDAY: chrono::Weekday = chrono::Weekday::Tue;
+ const PERMITTED_WEEKDAY: chrono::Weekday = chrono::Weekday::Tue;
- let user_is_restricted = RESTRICT_IDS.contains(&message.author.id.0);
- let restrictions_flipped = chrono::Local::now().weekday() == PERMITTED_WEEKDAY;
+ let user_is_restricted = RESTRICT_IDS.contains(&message.author.id.get());
+ let restrictions_flipped = chrono::Local::now().weekday() == PERMITTED_WEEKDAY;
- if user_is_restricted == restrictions_flipped {
- return true;
- }
+ if user_is_restricted == restrictions_flipped {
+ return true;
+ }
- let reason = if !restrictions_flipped {
- "restricted prefix".to_owned()
- } else {
- format!("it is {:?}", PERMITTED_WEEKDAY)
- };
+ let reason = if !restrictions_flipped {
+ "restricted prefix".to_owned()
+ } else {
+ format!("it is {:?}", PERMITTED_WEEKDAY)
+ };
- info!("rejecting command '{}' from user '{}': {}", cmd, message.author.name, reason);
+ info!("rejecting command '{}' from user '{}': {}", cmd, message.author.name, reason);
- match ctx.send_result(message.channel_id, "no", message.tts) {
- Err(e) => error!("sending restricted prefix response: {}", e),
- Ok(msg_id) => {
- let mut mp = MESSAGE_WATCH.lock().unwrap();
- mp.insert(message.id, msg_id);
+ match util::send_result(ctx, message.channel_id, "no", message.tts).await {
+ Err(e) => error!("sending restricted prefix response: {}", e),
+ Ok(msg_id) => {
+ let mut mp = MESSAGE_WATCH.lock().unwrap();
+ mp.insert(message.id, msg_id);
+ },
}
- }
- false
+ false
+ })
}
-fn after_handle(ctx: &mut Context, msg: &Message, cmd: &str, err: StdResult<(), Error>) {
- match err {
- Ok(()) => {
- trace!("command '{}' completed successfully", cmd);
- },
+fn after_handle<'fut>(
+ ctx: &'fut Context,
+ msg: &'fut Message,
+ cmd: &'fut str,
+ err: StdResult<(), CommandError>,
+) -> Pin<Box<dyn Future<Output = ()> + Send + 'fut>> {
+ Box::pin(async move {
+ match err {
+ Ok(()) => {
+ trace!("command '{}' completed successfully", cmd);
+ },
- Err(e) => {
- if let Err(e) = msg.react(&ctx, "❌") {
- error!("reacting to failed message: {}", e);
- }
+ Err(e) => {
+ if let Err(e) = msg.react(&ctx, ReactionType::Unicode("❌".to_owned())).await {
+ error!("reacting to failed message: {}", e);
+ }
- if let Err(e) = ctx.send(msg.channel_id, "BANIC", msg.tts) {
- error!("sending BANIC: {}", e);
- }
+ if let Err(e) = util::send(ctx, msg.channel_id, "BANIC", msg.tts).await {
+ error!("sending BANIC: {}", e);
+ }
- error!("error encountered handling command '{}': {:?}", cmd, e);
+ error!("error encountered handling command '{}': {:?}", cmd, e);
+ },
}
- }
+ })
}
pub fn run() -> Result<()> {
@@ -219,7 +246,8 @@ pub fn run() -> Result<()> {
ctrlc::set_handler(move || {
info!("shutting down");
shard_manager.lock().shutdown_all();
- }).expect("unable to create SIGINT/SIGTERM handlers");
+ })
+ .expect("unable to create SIGINT/SIGTERM handlers");
client.start()
}
diff --git a/src/commands/help.rs b/src/commands/help.rs
index 743a099..588cdf7 100644
--- a/src/commands/help.rs
+++ b/src/commands/help.rs
@@ -16,17 +16,17 @@ use serenity::{
},
prelude::*,
};
+use serenity::framework::standard::CommandResult;
-use crate::Result;
#[help]
-pub fn help(
- ctx: &mut Context,
+pub async fn help(
+ ctx: &Context,
msg: &Message,
args: Args,
opts: &'static HelpOptions,
groups: &[&'static CommandGroup],
owners: HashSet<UserId>,
-) -> Result<()> {
+) -> CommandResult {
help_commands::with_embeds(ctx, msg, args, opts, groups, owners)
}
diff --git a/src/commands/meme/create.rs b/src/commands/meme/create.rs
index 06cc4ef..97c5276 100644
--- a/src/commands/meme/create.rs
+++ b/src/commands/meme/create.rs
@@ -14,9 +14,9 @@ use log::{
};
use serenity::{
framework::standard::{
+ macros::command,
Args,
Delimiter,
- macros::command,
},
model::channel::Message,
prelude::*,
@@ -25,20 +25,28 @@ use url::Url;
use anyhow::anyhow;
use lazy_static::lazy_static;
+use serenity::{
+ all::ReactionType,
+ framework::standard::{
+ CommandError,
+ CommandResult,
+ },
+ futures::TryFutureExt,
+};
use crate::{
- Result,
audio::{
parse_times,
ytdl_url,
},
db::{
- Audio,
connection,
+ Audio,
Image,
NewMeme,
},
- util::CtxExt, FFMPEG_COMMAND,
+ util,
+ FFMPEG_COMMAND,
};
lazy_static! {
@@ -46,13 +54,17 @@ lazy_static! {
}
#[command]
-pub fn addmeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
+pub async fn addmeme(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let mut args = Args::new(args.rest(), DELIMS.as_ref());
let title = args.single_quoted::<String>()?;
let text = args.rest().to_owned();
- let text = if text.is_empty() { None } else { Some(text) };
+ let text = if text.is_empty() {
+ None
+ } else {
+ Some(text)
+ };
let mut conn = connection()?;
@@ -60,13 +72,17 @@ pub fn addmeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
if image.is_none() && text.is_none() {
warn!("tried to create non-audio meme with no image or text");
- return ctx.send(msg.channel_id, "hahAA it's empty xdddd", msg.tts);
+ return util::send(ctx, msg.channel_id, "hahAA it's empty xdddd", msg.tts)
+ .map_err(CommandError::from)
+ .await;
}
- let image_id = image.map(|att| {
- let data = att.download()?;
- Image::create(&mut conn, &att.filename, data, msg.author.id.0)
- }).transpose()?;
+ let image_id = image
+ .map(|att| {
+ let data = att.download()?;
+ Image::create(&mut conn, &att.filename, data, msg.author.id.get())
+ })
+ .transpose()?;
let save_result = NewMeme {
title,
@@ -74,25 +90,31 @@ pub fn addmeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
image_id,
audio_id: None,
metadata_id: 0,
- }.save(&mut conn, msg.author.id.0).map(|_| {});
+ }
+ .save(&mut conn, msg.author.id.get())
+ .map(|_| {});
use diesel::result::DatabaseErrorKind;
match save_result {
Ok(_) => msg.react(&ctx, "👌"),
Err(e) => {
- if let Some(DieselError::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) = e.downcast_ref::<DieselError>() {
+ if let Some(DieselError::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) =
+ e.downcast_ref::<DieselError>()
+ {
error!("tried to create meme that already exists");
- msg.react(&ctx, "❌")?;
- return ctx.send(msg.channel_id, "that meme already exists", msg.tts);
+ msg.react(&ctx, ReactionType::Unicode("❌".to_owned())).await?;
+ return util::send(ctx, msg.channel_id, "that meme already exists", msg.tts)
+ .map_err(CommandError::from)
+ .await;
}
- return Err(e);
- }
+ return Err(e.into());
+ },
}
}
#[command]
-pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
+pub async fn addaudiomeme(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let mut args = Args::new(args.rest(), DELIMS.as_ref());
let title = args.single_quoted::<String>()?;
@@ -101,8 +123,8 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()>
let elems = audio_str.split_whitespace().collect::<Vec<_>>();
if elems.len() == 0 {
- ctx.send(msg.channel_id, "are you stupid", msg.tts)?;
- return Err(anyhow!("no audio link was provided"))
+ util::send(ctx, msg.channel_id, "are you stupid", msg.tts).await?;
+ return Err(anyhow!("no audio link was provided").into());
}
let audio_link = Url::parse(elems[0])?;
@@ -112,16 +134,24 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()>
let youtube_url = ytdl_url(audio_link.as_str())?;
let duration_opts = if let Some(e) = end {
- vec! [
- "-ss".to_owned(), start.map_or_else(
+ vec![
+ "-ss".to_owned(),
+ start.map_or_else(
|| "00:00:00".to_owned(),
- |s| format!("{:02}:{:02}:{:02}", s.num_hours(), s.num_minutes() % 60, s.num_seconds() % 60)
+ |s| {
+ format!(
+ "{:02}:{:02}:{:02}",
+ s.num_hours(),
+ s.num_minutes() % 60,
+ s.num_seconds() % 60
+ )
+ },
),
-
- "-to".to_owned(), format!("{:02}:{:02}:{:02}", e.num_hours(), e.num_minutes() % 60, e.num_seconds() % 60),
+ "-to".to_owned(),
+ format!("{:02}:{:02}:{:02}", e.num_hours(), e.num_minutes() % 60, e.num_seconds() % 60),
]
} else {
- vec! []
+ vec![]
};
let ffmpeg_command = Command::new(&*FFMPEG_COMMAND)
@@ -129,13 +159,8 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()>
.arg(youtube_url)
.args(duration_opts)
.args(&[
- "-ac", "2",
- "-ar", "48000",
- "-f", "opus",
- "-acodec", "libopus",
- "-b:a", "96k",
- "-fs", "5M",
- "-",
+ "-ac", "2", "-ar", "48000", "-f", "opus", "-acodec", "libopus", "-b:a", "96k", "-fs",
+ "5M", "-",
])
.stdout(Stdio::piped())
.stderr(Stdio::null())
@@ -145,15 +170,21 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()>
let mut audio_reader = ffmpeg_command.stdout.unwrap();
let text = args.rest().to_owned();
- let text = if text.is_empty() { None } else { Some(text) };
+ let text = if text.is_empty() {
+ None
+ } else {
+ Some(text)
+ };
let mut conn = connection()?;
- let image = msg.attachments.first()
+ let image = msg
+ .attachments
+ .first()
.ok_or(anyhow!("no attachment"))
.and_then(|att| {
let data = att.download()?;
- Image::create(&mut conn, &att.filename, data, msg.author.id.0)
+ Image::create(&mut conn, &att.filename, data, msg.author.id.get())
})
.ok();
@@ -162,10 +193,12 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()>
if bytes == 0 {
debug!("read 0 bytes from audio reader");
- return ctx.send(msg.channel_id, "🔇🔇🔇🔕🔕🔕🔕🔕🔇🔕🔕🔇🔕🔕📣📢📣📢📣", msg.tts);
+ return util::send(ctx, msg.channel_id, "🔇🔇🔇🔕🔕🔕🔕🔕🔇🔕🔕🔇🔕🔕📣📢📣📢📣", msg.tts)
+ .map_err(CommandError::from)
+ .await;
}
- let audio_id = Audio::create(&mut conn, audio_data, msg.author.id.0)?;
+ let audio_id = Audio::create(&mut conn, audio_data, msg.author.id.get())?;
let save_result = NewMeme {
title,
@@ -173,19 +206,25 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()>
image_id: image,
audio_id: Some(audio_id),
metadata_id: 0,
- }.save(&mut conn, msg.author.id.0).map(|_| {});
+ }
+ .save(&mut conn, msg.author.id.get())
+ .map(|_| {});
use diesel::result::DatabaseErrorKind;
match save_result {
- Ok(_) => msg.react(&ctx, "👌"),
+ Ok(_) => msg.react(&ctx, ReactionType::Unicode("👌".to_owned())),
Err(e) => {
- if let Some(DieselError::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) = e.downcast_ref::<DieselError>() {
+ if let Some(DieselError::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) =
+ e.downcast_ref::<DieselError>()
+ {
error!("tried to create meme that already exists");
- msg.react(&ctx, "❌")?;
- return ctx.send(msg.channel_id, "that meme already exists", msg.tts);
+ msg.react(&ctx, ReactionType::Unicode("❌".to_owned())).await?;
+ return util::send(ctx, msg.channel_id, "that meme already exists", msg.tts)
+ .map_err(CommandError::from)
+ .await;
}
return Err(e);
- }
+ },
}
}
diff --git a/src/commands/meme/delete.rs b/src/commands/meme/delete.rs
index 7eafc80..c06e9d0 100644
--- a/src/commands/meme/delete.rs
+++ b/src/commands/meme/delete.rs
@@ -1,45 +1,49 @@
use diesel::{
- NotFound,
result::Error as DieselError,
+ NotFound,
};
use log::info;
use serenity::{
+ all::ReactionType,
framework::standard::{
- Args,
macros::command,
+ Args,
+ CommandResult,
},
model::channel::Message,
prelude::*,
};
use crate::{
- Result,
db::{
connection,
delete_meme,
},
- util::CtxExt,
+ util,
};
#[command]
#[aliases("delmem")]
-pub fn delmeme(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
+pub async fn delmeme(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let title = args.single_quoted::<String>()?;
let mut conn = connection()?;
- match delete_meme(&mut conn, &title, msg.author.id.0) {
- Ok(_) => msg.react(ctx, "💀"),
+ match delete_meme(&mut conn, &title, msg.author.id.get()) {
+ Ok(_) => {
+ msg.react(ctx, ReactionType::Unicode("💀".to_owned())).await?;
+ Ok(())
+ },
Err(e) => {
if let Some(NotFound) = e.downcast_ref::<DieselError>() {
- msg.react(&ctx, "❓")?;
+ msg.react(&ctx, ReactionType::Unicode("❓".to_owned())).await?;
info!("attempted to delete nonexistent meme: '{}'", title);
- ctx.send(msg.channel_id, "nice try", msg.tts)?;
+ util::send(ctx, msg.channel_id, "nice try", msg.tts).await?;
return Ok(());
}
Err(e)?;
Ok(())
- }
+ },
}
}
diff --git a/src/commands/meme/history.rs b/src/commands/meme/history.rs
index f9e8851..5e200b1 100644
--- a/src/commands/meme/history.rs
+++ b/src/commands/meme/history.rs
@@ -1,6 +1,6 @@
use diesel::{
- NotFound,
result::Error as DieselError,
+ NotFound,
};
use log::{
debug,
@@ -9,8 +9,8 @@ use log::{
};
use serenity::{
framework::standard::{
- Args,
macros::command,
+ Args,
},
model::channel::Message,
prelude::*,
@@ -22,6 +22,13 @@ use timeago::{
use anyhow::anyhow;
use lazy_static::lazy_static;
+use serenity::{
+ framework::standard::{
+ CommandError,
+ CommandResult,
+ },
+ futures::TryFutureExt,
+};
use crate::{
db::{
@@ -31,9 +38,9 @@ use crate::{
Meme,
Metadata,
},
- CONFIG,
+ util,
Result,
- util::CtxExt,
+ CONFIG,
};
lazy_static! {
@@ -50,7 +57,7 @@ static CLEAN_DATE_FORMAT: &'static str = "%b %-e %Y";
#[command]
#[aliases("what")]
-pub fn wat(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
+pub async fn wat(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
let mut conn = connection()?;
let record = match InvocationRecord::last(&mut conn) {
@@ -58,11 +65,13 @@ pub fn wat(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
Err(e) => {
if let Some(NotFound) = e.downcast_ref::<DieselError>() {
info!("found no memes in history");
- return ctx.send(msg.channel_id, "no one has ever memed before", msg.tts);
+ return util::send(ctx, msg.channel_id, "no one has ever memed before", msg.tts)
+ .map_err(CommandError::from)
+ .await;
}
- ctx.send(msg.channel_id, "BAD MEME BAD MEME", msg.tts)?;
- return Err(e);
+ util::send(ctx, msg.channel_id, "BAD MEME BAD MEME", msg.tts).await?;
+ return Err(e.into());
},
};
@@ -73,17 +82,27 @@ pub fn wat(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
let metadata = Metadata::find(&mut conn, meme.metadata_id)?;
let author = CONFIG.discord.guild().member(&ctx, metadata.created_by as u64)?;
- ctx.send(msg.channel_id,
- &format!("that was \"{}\" by {} ({})",
- meme.title, author.mention(), metadata.created.date().format(CLEAN_DATE_FORMAT)), msg.tts)?
+ util::send(
+ ctx,
+ msg.channel_id,
+ &format!(
+ "that was \"{}\" by {} ({})",
+ meme.title,
+ author.mention(),
+ metadata.created.date().format(CLEAN_DATE_FORMAT)
+ ),
+ msg.tts,
+ )
+ .await?
},
Err(e) => {
if let Some(NotFound) = e.downcast_ref::<DieselError>() {
info!("last meme not found in database");
- return ctx.send(msg.channel_id, "heuueueeeeh?", msg.tts);
+ return util::send(ctx, msg.channel_id, "heuueueeeeh?", msg.tts).await;
}
- ctx.send(msg.channel_id, "do i look like i know what a jpeg is", msg.tts)?;
+ util::send(ctx, msg.channel_id, "do i look like i know what a jpeg is", msg.tts)
+ .await?;
return Err(e);
},
};
@@ -92,7 +111,7 @@ pub fn wat(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
}
#[command]
-pub fn history(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
+pub async fn history(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
use itertools::Itertools;
let mut conn = connection()?;
@@ -101,7 +120,7 @@ pub fn history(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
if n > CONFIG.max_hist {
debug!("user requested more than MAX_HIST ({}) items from history", CONFIG.max_hist);
- ctx.send(msg.channel_id, "YER PUSHIN ME OVER THE FUCKIN LINE", true)?;
+ util::send(ctx, msg.channel_id, "YER PUSHIN ME OVER THE FUCKIN LINE", true).await?;
}
let n = n.min(CONFIG.max_hist);
@@ -110,7 +129,9 @@ pub fn history(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
if records.len() == 0 {
info!("no memes in history");
- return ctx.send(msg.channel_id, "i don't remember anything :(", msg.tts);
+ return util::send(ctx, msg.channel_id, "i don't remember anything :(", msg.tts)
+ .map_err(CommandError::from)
+ .await;
}
info!("reporting meme history (len {})", n);
@@ -119,18 +140,41 @@ pub fn history(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
.enumerate()
.rev()
.map(|(i, rec)| {
- let dt = chrono::DateTime::from_utc(rec.time, chrono::Utc{});
+ let dt = chrono::DateTime::from_utc(rec.time, chrono::Utc {});
let ago = TIME_FORMATTER.convert((chrono::Utc::now() - dt).to_std().unwrap());
- let rand = if rec.random { "R, " } else { "" };
+ let rand = if rec.random {
+ "R, "
+ } else {
+ ""
+ };
Meme::find(&mut conn, rec.meme_id)
.and_then(|meme| {
Metadata::find(&mut conn, meme.metadata_id).map(|metadata| (metadata, meme))
})
.map(|(metadata, meme)| {
- let author_name = CONFIG.discord.guild().member(&ctx, metadata.created_by as u64).map(|m| m.display_name().into_owned()).unwrap_or("???".to_owned());
- let invoker_name = CONFIG.discord.guild().member(&ctx, rec.user_id as u64).map(|m| m.display_name().into_owned()).unwrap_or("???".to_owned());
- format!("{}. [{}{}] \"{}\" by {} ({}). invoked by {}.", i + 1, rand, ago, meme.title, author_name, metadata.created.date().format(CLEAN_DATE_FORMAT), invoker_name)
+ let author_name = CONFIG
+ .discord
+ .guild()
+ .member(&ctx, metadata.created_by as u64)
+ .map(|m| m.display_name().into_owned())
+ .unwrap_or("???".to_owned());
+ let invoker_name = CONFIG
+ .discord
+ .guild()
+ .member(&ctx, rec.user_id as u64)
+ .map(|m| m.display_name().into_owned())
+ .unwrap_or("???".to_owned());
+ format!(
+ "{}. [{}{}] \"{}\" by {} ({}). invoked by {}.",
+ i + 1,
+ rand,
+ ago,
+ meme.title,
+ author_name,
+ metadata.created.date().format(CLEAN_DATE_FORMAT),
+ invoker_name
+ )
})
.unwrap_or_else(|e| {
if let Some(variant) = e.downcast_ref::<DieselError>() {
@@ -139,18 +183,23 @@ pub fn history(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
}
}
- let invoker_name = CONFIG.discord.guild().member(&ctx, rec.user_id as u64).map(|m| m.display_name().into_owned()).unwrap_or("???".to_owned());
+ let invoker_name = CONFIG
+ .discord
+ .guild()
+ .member(&ctx, rec.user_id as u64)
+ .map(|m| m.display_name().into_owned())
+ .unwrap_or("???".to_owned());
format!("{}. [{}{}] not found. invoked by {}.", i + 1, rand, ago, invoker_name)
})
})
.join("\n");
- ctx.send(msg.channel_id, &resp, false)
+ util::send(ctx, msg.channel_id, &resp, false).await
}
#[command]
#[aliases("stat")]
-pub fn stats(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
+pub async fn stats(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
use db;
use serenity::model::{
id::UserId,
@@ -162,8 +211,8 @@ pub fn stats(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
debug!("reporting stats");
- let rand_user: User = UserId(stats.most_random_meme_user).to_user(&ctx)?;
- let direct_user: User = UserId(stats.most_directly_named_meme_user).to_user(&ctx)?;
+ let rand_user: User = UserId::new(stats.most_random_meme_user).to_user(&ctx)?;
+ let direct_user: User = UserId::new(stats.most_directly_named_meme_user).to_user(&ctx)?;
let rand_user = rand_user.nick_in(&ctx, CONFIG.discord.guild()).unwrap_or(rand_user.name);
let direct_user = direct_user.nick_in(&ctx, CONFIG.discord.guild()).unwrap_or(direct_user.name);
@@ -200,29 +249,35 @@ and *{}* was the most-memed overall ({})"#,
(stats.random_meme_invocations as f64) / (stats.total_meme_invocations as f64) * 100.,
stats.audio_meme_invocations,
(stats.audio_meme_invocations as f64) / (stats.total_meme_invocations as f64) * 100.,
- stats.most_active_day.format(CLEAN_DATE_FORMAT), stats.most_active_day_count,
- stats.most_audio_active_day.format(CLEAN_DATE_FORMAT), stats.most_audio_active_count,
- rand_user, stats.most_random_meme_user_count,
- direct_user, stats.most_directly_named_meme_count,
- stats.most_popular_named_meme, stats.most_popular_named_meme_count,
- stats.most_popular_random_meme, stats.most_popular_random_meme_count,
- stats.most_popular_meme_overall, stats.most_popular_meme_overall_count,
+ stats.most_active_day.format(CLEAN_DATE_FORMAT),
+ stats.most_active_day_count,
+ stats.most_audio_active_day.format(CLEAN_DATE_FORMAT),
+ stats.most_audio_active_count,
+ rand_user,
+ stats.most_random_meme_user_count,
+ direct_user,
+ stats.most_directly_named_meme_count,
+ stats.most_popular_named_meme,
+ stats.most_popular_named_meme_count,
+ stats.most_popular_random_meme,
+ stats.most_popular_random_meme_count,
+ stats.most_popular_meme_overall,
+ stats.most_popular_meme_overall_count,
);
- ctx.send(msg.channel_id, s, msg.tts)
+
+ util::send(ctx, msg.channel_id, s, msg.tts).map_err(CommandError::from).await
}
#[command]
-pub fn memers(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> {
+pub async fn memers(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
use db;
use itertools::Itertools;
- use serenity::model::{
- id::UserId,
- };
+ use serenity::model::id::UserId;
let s = db::memers()?
.into_iter()
.map(|info| {
- let user = UserId(info.user_id).to_user(&ctx)?;
+ let user = UserId::new(info.user_id).to_user(&ctx)?;
let username = user.nick_in(&ctx, CONFIG.discord.guild()).unwrap_or(user.name);
let res = format!(
@@ -241,11 +296,11 @@ pub fn memers(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> {
.into_iter()
.join("\n");
- ctx.send(msg.channel_id, &s, msg.tts)
+ util::send(ctx, msg.channel_id, &s, msg.tts).map_err(CommandError::from).await
}
#[command]
-pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
+pub async fn query(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
use std::borrow::Borrow;
use itertools::Itertools;
@@ -253,8 +308,8 @@ pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
use serenity::model::id::UserId;
use crate::{
- game::get_user_id,
db,
+ game::get_user_id,
CONFIG,
};
@@ -263,24 +318,21 @@ pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
static ref AGE_REGEX: Regex = Regex::new(r"(?i)(?:age|order)=(.*)").unwrap();
}
- let guild = msg.channel_id.to_channel(&ctx)?
- .guild()
- .ok_or(anyhow!("couldn't find guild"))?;
+ let guild = msg.channel_id.to_channel(&ctx)?.guild().ok_or(anyhow!("couldn't find guild"))?;
- let guild = guild.read()
- .guild(&ctx)
- .ok_or(anyhow!("couldn't find guild"))?;
+ let guild = guild.read().guild(&ctx).ok_or(anyhow!("couldn't find guild"))?;
- let guild = guild
- .read();
+ let guild = guild.read();
let creator: Option<u64> = {
let creator = args.quoted().current().map(|s| CREATOR_REGEX.is_match(s)).unwrap_or(false);
if creator {
args.single_quoted::<String>()
.ok()
- .and_then(|s| CREATOR_REGEX.captures(&s).and_then(|c| c.get(1)).map(|x| x.as_str().to_owned()))
- .and_then(|s| get_user_id(guild.borrow(), s).ok().map(|s| s.0))
+ .and_then(|s| {
+ CREATOR_REGEX.captures(&s).and_then(|c| c.get(1)).map(|x| x.as_str().to_owned())
+ })
+ .and_then(|s| get_user_id(guild.borrow(), s).ok().map(UserId::get))
} else {
None
}
@@ -290,8 +342,11 @@ pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
let order = args.quoted().current().map(|s| AGE_REGEX.is_match(s)).unwrap_or(false);
if order {
- args.single_quoted::<String>().ok()
- .and_then(|s| AGE_REGEX.captures(&s).and_then(|c| c.get(1)).map(|x| x.as_str().to_owned()))
+ args.single_quoted::<String>()
+ .ok()
+ .and_then(|s| {
+ AGE_REGEX.captures(&s).and_then(|c| c.get(1)).map(|x| x.as_str().to_owned())
+ })
.map(|s: String| s.contains("new"))
.unwrap_or(true)
} else {
@@ -302,16 +357,17 @@ pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
let result = db::query_meme(args.rest(), creator, order)?
.into_iter()
.map(|(meme, metadata)| {
- let user = UserId(metadata.created_by as u64).to_user(&ctx)?;
+ let user = UserId::new(metadata.created_by as u64).to_user(&ctx)?;
let username = user.nick_in(&ctx, CONFIG.discord.guild()).unwrap_or(user.name);
- Ok(format!("*{}* by **{}** ({}). text length: **{}**, image: **{}**, audio: **{}**",
- meme.title,
- username,
- metadata.created.date().format(CLEAN_DATE_FORMAT),
- meme.content.map_or(0, |s| s.len()),
- meme.image_id.map_or("NO", |_s| "YES"),
- meme.audio_id.map_or("NO", |_s| "YES"),
+ Ok(format!(
+ "*{}* by **{}** ({}). text length: **{}**, image: **{}**, audio: **{}**",
+ meme.title,
+ username,
+ metadata.created.date().format(CLEAN_DATE_FORMAT),
+ meme.content.map_or(0, |s| s.len()),
+ meme.image_id.map_or("NO", |_s| "YES"),
+ meme.audio_id.map_or("NO", |_s| "YES"),
))
})
.collect::<Result<Vec<_>>>()?
@@ -329,9 +385,11 @@ pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
if result.len() == 0 {
info!("no memes matched query");
- return ctx.send(msg.channel_id, "no match".to_owned(), msg.tts);
- }
+ return util::send(ctx, msg.channel_id, "no match".to_owned(), msg.tts)
+ .map_err(CommandError::from)
+ .await;
+ }
- ctx.send(msg.channel_id, &result, msg.tts)
+ util::send(ctx, msg.channel_id, &result, msg.tts).map_err(CommandError::from).await
}
diff --git a/src/commands/meme/invoke.rs b/src/commands/meme/invoke.rs
index de0272f..03c6251 100644
--- a/src/commands/meme/invoke.rs
+++ b/src/commands/meme/invoke.rs
@@ -1,63 +1,65 @@
use diesel::{
- NotFound,
result::Error as DieselError,
+ NotFound,
};
use itertools::Itertools;
use log::info;
use serenity::{
framework::standard::{
- Args,
macros::command,
+ Args,
+ CommandError,
+ CommandResult,
},
+ futures::TryFutureExt,
model::channel::Message,
prelude::*,
};
use crate::{
commands::meme::send_meme,
- Result,
db::{
self,
connection,
find_meme,
InvocationRecord,
},
- util::CtxExt,
+ util,
};
#[command]
#[aliases("mem")]
-pub fn meme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
+pub async fn meme(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
_meme(ctx, msg, args, AudioPlayback::Optional)
}
#[command]
-pub fn omen(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> {
+pub async fn omen(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let args = Args::new("", &[]);
_meme(ctx, msg, args, AudioPlayback::Optional)
}
#[command]
-pub fn silentomen(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> {
+pub async fn silentomen(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let args = Args::new("", &[]);
_meme(ctx, msg, args, AudioPlayback::Prohibited)
}
#[command]
-pub fn audioomen(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> {
+pub async fn audioomen(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let args = Args::new("", &[]);
_meme(ctx, msg, args, AudioPlayback::Required)
}
#[command]
#[aliases("audiomeme", "audiomem")]
-pub fn audio_meme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
+pub async fn audio_meme(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
_meme(ctx, msg, args, AudioPlayback::Required)
}
#[command]
#[aliases("silentmeme", "silentmem")]
-pub fn silent_meme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
+pub async fn silent_meme(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
_meme(ctx, msg, args, AudioPlayback::Prohibited)
}
@@ -68,9 +70,14 @@ enum AudioPlayback {
Prohibited,
}
-fn _meme(ctx: &mut Context, msg: &Message, args: Args, audio_playback: AudioPlayback) -> Result<()> {
+async fn _meme(
+ ctx: &Context,
+ msg: &Message,
+ args: Args,
+ audio_playback: AudioPlayback,
+) -> CommandResult {
if args.len() == 0 || audio_playback != AudioPlayback::Optional {
- return rand_meme(ctx, msg, audio_playback);
+ return rand_meme(ctx, msg, audio_playback).await;
}
let search = args.raw().join(" ");
@@ -78,28 +85,34 @@ fn _meme(ctx: &mut Context, msg: &Message, args: Args, audio_playback: AudioPlay
let mut conn = connection()?;
let mem = match find_meme(&mut conn, search) {
Ok(x) => {
- InvocationRecord::create(&mut conn, msg.author.id.0, msg.id.0, x.id, false)?;
+ InvocationRecord::create(&mut conn, msg.author.id.get(), msg.id.get(), x.id, false)?;
x
},
Err(e) => {
return if let Some(NotFound) = e.downcast_ref::<DieselError>() {
info!("requested meme not found in database");
- ctx.send(msg.channel_id, "c'mon baby, guesstimate", msg.tts)
+ util::send(ctx, msg.channel_id, "c'mon baby, guesstimate", msg.tts)
+ .await
+ .map_err(CommandError::from)
} else {
- ctx.send(msg.channel_id, "what in ryan's name", msg.tts)?;
- Err(e)
+ util::send(ctx, msg.channel_id, "what in ryan's name", msg.tts).await?;
+ Err(e.into())
};
},
};
- send_meme(ctx, &mem, &mut conn, msg)
+ send_meme(ctx, &mem, &mut conn, msg).await
}
-fn rand_meme(ctx: &Context, message: &Message, audio_playback: AudioPlayback) -> Result<()> {
+async fn rand_meme(
+ ctx: &Context,
+ message: &Message,
+ audio_playback: AudioPlayback,
+) -> CommandResult {
let mut conn = connection()?;
- let should_audio = ctx.users_listening()?;
+ let should_audio = util::users_listening(ctx).await?;
let mem = match audio_playback {
AudioPlayback::Required => db::rand_audio_meme(&mut conn),
@@ -109,28 +122,36 @@ fn rand_meme(ctx: &Context, message: &Message, audio_playback: AudioPlayback) ->
match mem {
Ok(mem) => {
- InvocationRecord::create(&mut conn, message.author.id.0, message.id.0, mem.id, true)?;
- send_meme(ctx, &mem, &mut conn, message)?;
+ InvocationRecord::create(
+ &mut conn,
+ message.author.id.get(),
+ message.id.get(),
+ mem.id,
+ true,
+ )?;
+ send_meme(ctx, &mem, &mut conn, message).await?;
Ok(())
},
Err(e) => {
match e.downcast_ref::<DieselError>() {
Some(NotFound) => {
info!("random meme not found");
- return ctx.send(message.channel_id, "i don't know any :(", message.tts)
+ return util::send(ctx, message.channel_id, "i don't know any :(", message.tts)
+ .map_err(CommandError::from)
+ .await;
},
_ => {},
}
- ctx.send(message.channel_id, "HELP", message.tts)?;
- return Err(e);
+ util::send(ctx, message.channel_id, "HELP", message.tts).await?;
+ return Err(e.into());
},
}
}
#[command]
#[aliases("rarememe", "raremem")]
-pub fn rare_meme(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> {
+pub async fn rare_meme(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let should_audio = ctx.users_listening()?;
let mut conn = connection()?;
@@ -138,19 +159,23 @@ pub fn rare_meme(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> {
let meme = db::rare_meme(&mut conn, should_audio);
match meme {
Ok(meme) => {
- InvocationRecord::create(&mut conn, msg.author.id.0, msg.id.0, meme.id, true)?;
+ InvocationRecord::create(&mut conn, msg.author.id.get(), msg.id.get(), meme.id, true)?;
send_meme(ctx, &meme, &mut conn, msg)
},
Err(e) => {
match e.downcast_ref::<DieselError>() {
Some(NotFound) => {
info!("rare meme not found");
- return ctx.send(msg.channel_id, "i don't know any :(", msg.tts)
+ return util::send(ctx, msg.channel_id, "i don't know any :(", msg.tts)
+ .map_err(CommandError::from)
+ .await;
},
_ => {},
}
- ctx.send(msg.channel_id, "THE MEME MARKET IS IN FREEFALL", msg.tts)?;
+ util::send(ctx, msg.channel_id, "THE MEME MARKET IS IN FREEFALL", msg.tts)
+ .map_err(CommandError::from)
+ .await?;
Err(e)
},
diff --git a/src/commands/meme/mod.rs b/src/commands/meme/mod.rs
index 6ce30b6..31d9b78 100644
--- a/src/commands/meme/mod.rs
+++ b/src/commands/meme/mod.rs
@@ -1,9 +1,16 @@
use diesel::PgConnection;
use log::debug;
-use rand::{Rng, thread_rng};
+use rand::random;
use serenity::{
- framework::standard::macros::group,
- http::AttachmentType,
+ all::ReactionType,
+ builder::{
+ CreateAttachment,
+ CreateMessage,
+ },
+ framework::standard::{
+ macros::group,
+ CommandResult,
+ },
model::channel::Message,
prelude::*,
};
@@ -14,7 +21,6 @@ use crate::{
PlayQueue,
},
db::Meme,
- Result,
};
pub use self::{
@@ -24,68 +30,67 @@ pub use self::{
invoke::*,
};
-mod history;
mod create;
-mod invoke;
mod delete;
+mod history;
+mod invoke;
-group!({
- name: "memes",
- options: {
- only_in: "guild",
- },
- commands: [
- meme,
- audio_meme,
- silent_Meme,
- omen,
- audioomen,
- silentomen,
- addmeme,
- addaudiomeme,
- delmeme,
- wat,
- stats,
- history,
- rare_meme,
- memers,
- query,
- ],
-});
+#[group]
+#[commands(
+ meme,
+ audio_meme,
+ silent_meme,
+ omen,
+ audioomen,
+ silentomen,
+ addmeme,
+ addaudiomeme,
+ delmeme,
+ wat,
+ stats,
+ history,
+ rare_meme,
+ memers,
+ query
+)]
+struct Memes;
-fn send_meme(ctx: &Context, t: &Meme, conn: &mut PgConnection, msg: &Message) -> Result<()> {
- let should_tts = t.content.as_ref().map(|t| t.len() > 0).unwrap_or(false) &&
- thread_rng().gen::<u32>() % 25 == 0;
+async fn send_meme(
+ ctx: &Context,
+ t: &Meme,
+ conn: &mut PgConnection,
+ msg: &Message,
+) -> CommandResult {
+ let should_tts =
+ t.content.as_ref().map(|t| t.len() > 0).unwrap_or(false) && random::<u32>() % 25 == 0;
debug!("sending meme (tts: {}): {:?}", should_tts, t);
let image = t.image(conn);
let audio = t.audio(conn);
+ let cmsg = {
+ let ret = CreateMessage::default().tts(should_tts);
+
+ match t.content {
+ Some(ref text) if text.len() > 0 => ret.content(text),
+ _ => ret,
+ }
+ };
+
match image {
Some(image) => {
let image = image?;
- msg.channel_id.send_files(ctx, vec!(AttachmentType::Bytes((&image.data, &image.filename))), |m| {
- let ret = m.tts(should_tts);
+ let att = CreateAttachment::bytes(image.data.as_slice(), &image.filename);
- match t.content {
- Some(ref text) if text.len() > 0 => ret.content(text),
- _ => ret,
- }
- })?;
+ msg.channel_id.send_files(ctx, vec![att], cmsg).await?;
},
None => match t.content {
- Some(_) => { msg.channel_id.send_message(ctx, |m| {
- let ret = m.tts(should_tts);
-
- match t.content {
- Some(ref text) if text.len() > 0 => ret.content(text),
- _ => ret,
- }
- })?; },
+ Some(_) => {
+ msg.channel_id.send_message(ctx, cmsg).await?;
+ },
None => {},
-
},
};
@@ -96,19 +101,19 @@ fn send_meme(ctx: &Context, t: &Meme, conn: &mut PgConnection, msg: &Message) ->
let audio = audio?;
{
- let queue_lock = ctx.data.write().get::<PlayQueue>().cloned().unwrap();
+ let queue_lock = ctx.data.write().await.get::<PlayQueue>().cloned().unwrap();
let mut play_queue = queue_lock.write().unwrap();
- play_queue.meme_queue.push_back(PlayArgs{
- initiator: msg.author.name.clone(),
- data: ::either::Right(audio.data.clone()),
+ play_queue.meme_queue.push_back(PlayArgs {
+ initiator: msg.author.name.clone(),
+ data: ::either::Right(audio.data.clone()),
sender_channel: msg.channel_id,
- start: None,
- end: None,
+ start: None,
+ end: None,
});
}
- msg.react(ctx, "📣")?;
+ msg.react(ctx, ReactionType::Unicode("📣".to_owned())).await?;
}
Ok(())
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 705d697..4893e73 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -1,71 +1,55 @@
use log::info;
-use serenity::{
- framework::{
- StandardFramework,
- standard::{
- macros::group,
- },
- },
-};
-
-use crate::{
- util::CtxExt,
+use serenity::framework::{
+ standard::macros::group,
+ StandardFramework,
};
+#[cfg(feature = "diesel")]
+pub use self::meme::*;
pub use self::{
playback::*,
- sound_levels::*,
- roll::{roll, ROLL_COMMAND},
- today::{today, TODAY_COMMAND},
+ roll::ROLL_COMMAND,
+ today::{
+ today,
+ TODAY_COMMAND,
+ },
};
-#[cfg(feature = "diesel")]
-pub use self::meme::*;
pub(crate) mod playback;
-pub(crate) mod sound_levels;
pub(crate) mod roll;
+pub(crate) mod sound_levels;
pub(crate) mod today;
mod help;
-group!({
- name: "general",
- options: {
- only_in: "guild",
- },
- commands: [
- roll,
- today,
- ],
-});
+#[group]
+#[prefix = ""]
+#[only_in(guild)]
+#[commands(roll, today)]
+struct General;
pub fn register_commands(f: StandardFramework) -> StandardFramework {
- let result = f
- .group(&self::playback::PLAYBACK_GROUP)
- .group(&GENERAL_GROUP);
+ let result = f.group(&PLAYBACK_GROUP).group(&GENERAL_GROUP);
#[cfg(feature = "diesel")]
- let result = result.group(&self::meme::MEMES_GROUP);
+ let result = result.group(&MEMES_GROUP);
#[cfg(feature = "games")]
let result = result.group(&crate::game::GAME_GROUP);
- result
- .help(&help::HELP)
- .unrecognised_command(|ctx, msg, unrec| {
- let url = match msg.content.split_whitespace().skip(1).next() {
- Some(x) if x.starts_with("http") => x,
- _ => {
- info!("bad command formatting: '{}'", unrec);
- let _ = ctx.send(msg.channel_id, "format your commands right. fuck you.", msg.tts);
- return;
- }
- };
+ result.help(&help::HELP).unrecognised_command(|ctx, msg, unrec| {
+ let url = match msg.content.split_whitespace().skip(1).next() {
+ Some(x) if x.starts_with("http") => x,
+ _ => {
+ info!("bad command formatting: '{}'", unrec);
+ let _ = ctx.send(msg.channel_id, "format your commands right. fuck you.", msg.tts);
+ return;
+ },
+ };
- let _ = self::playback::_play(ctx, msg, &url);
- })
+ let _ = _play(ctx, msg, &url);
+ })
}
-
#[cfg(feature = "diesel")]
mod meme;
diff --git a/src/commands/playback.rs b/src/commands/playback.rs
index f80ec17..21393a2 100644
--- a/src/commands/playback.rs
+++ b/src/commands/playback.rs
@@ -1,4 +1,7 @@
-use either::{Left, Right};
+use either::{
+ Left,
+ Right,
+};
use log::{
debug,
error,
@@ -7,12 +10,22 @@ use log::{
};
use serenity::{
framework::standard::{
+ macros::{
+ command,
+ group,
+ },
Args,
- macros::{command, group},
+ CommandError,
+ CommandResult,
},
+ futures::TryFutureExt,
model::channel::Message,
prelude::*,
};
+use tap::{
+ Conv,
+ Pipe,
+};
use crate::{
audio::{
@@ -21,44 +34,34 @@ use crate::{
PlayQueue,
VoiceManager,
},
- Result,
- CONFIG,
- util::CtxExt,
commands::sound_levels::*,
+ util,
+ CONFIG,
};
-group!({
- name: "playback",
- options: {
- only_in: "guild",
- },
- commands: [
- skip,
- pause,
- resume,
- list,
- die,
- mute,
- unmute,
- play,
- volume,
- ],
-});
+#[group]
+#[commands(skip, pause, resume, list, die, mute, unmute, play, volume)]
+#[only_in(guild)]
+struct Playback;
-pub fn _play(ctx: &Context, msg: &Message, url: &str) -> Result<()> {
- use url::{Url, Host};
+pub async fn _play(ctx: &Context, msg: &Message, url: &str) -> CommandResult {
+ use url::{
+ Host,
+ Url,
+ };
debug!("playing '{}'", url);
if !url.starts_with("http") {
warn!("got bad url argument to play: {}", url);
- ctx.send(msg.channel_id, "bAD LiNk", msg.tts)?;
+ util::send(ctx, msg.channel_id, "bAD LiNk", msg.tts).await?;
return Ok(());
}
let url = match Url::parse(url) {
Err(e) => {
error!("bad url: {}", e);
- return ctx.send(msg.channel_id, "INVALID URL", msg.tts);
+ util::send(ctx, msg.channel_id, "INVALID URL", msg.tts).await?;
+ return Ok(());
},
Ok(u) => u,
};
@@ -71,10 +74,10 @@ pub fn _play(ctx: &Context, msg: &Message, url: &str) -> Result<()> {
if host.map(|h| h.to_lowercase().contains("imgur")).unwrap_or(false) {
info!("detected imgur link");
- if msg.author.id.0 == 106160362109272064 {
- ctx.send(msg.channel_id, "fuck you conway", true)?;
+ if msg.author.id.get() == 106160362109272064 {
+ util::send(ctx, msg.channel_id, "fuck you conway", true).await?;
} else {
- ctx.send(msg.channel_id, "IMGUR IS BAD, YOU TRASH CAN MAN", msg.tts)?;
+ util::send(ctx, msg.channel_id, "IMGUR IS BAD, YOU TRASH CAN MAN", msg.tts).await?;
}
return Ok(());
@@ -82,12 +85,12 @@ pub fn _play(ctx: &Context, msg: &Message, url: &str) -> Result<()> {
let (start, end) = parse_times(&msg.content);
- let queue_lock = ctx.data.write().get::<PlayQueue>().cloned().unwrap();
+ let queue_lock = ctx.data.write().await.get::<PlayQueue>().cloned().unwrap();
let mut play_queue = queue_lock.write().unwrap();
- play_queue.general_queue.push_back(PlayArgs{
+ play_queue.general_queue.push_back(PlayArgs {
initiator: msg.author.name.clone(),
- data: Left(url.into_string()),
+ data: Left(url.conv::<String>()),
sender_channel: msg.channel_id,
start,
end,
@@ -97,16 +100,18 @@ pub fn _play(ctx: &Context, msg: &Message, url: &str) -> Result<()> {
}
#[command]
-pub fn play(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
+pub async fn play(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
if args.len() == 0 {
- return _resume(ctx, msg);
+ return _resume(ctx, msg).await;
}
let url = match args.single::<String>() {
Ok(url) => url,
Err(e) => {
error!("unable to parse url from args: {}", e);
- return ctx.send(msg.channel_id, "BAD LINK", msg.tts);
+ return util::send(ctx, msg.channel_id, "BAD LINK", msg.tts)
+ .await
+ .map_err(CommandError::from);
},
};
@@ -114,16 +119,16 @@ pub fn play(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
}
#[command]
-pub fn pause(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
- let queue_lock = ctx.data.write().get::<PlayQueue>().cloned().unwrap();
+pub async fn pause(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
+ let queue_lock = ctx.data.write().await.get::<PlayQueue>().cloned().unwrap();
- let done = || ctx.send(msg.channel_id, "r u srs", msg.tts);
+ let done = || util::send(ctx, msg.channel_id, "r u srs", msg.tts).map_err(CommandError::from);
let playing = {
let play_queue = queue_lock.read().unwrap();
let current_item = match play_queue.playing {
Some(ref x) => x,
- None => return done(),
+ None => return done().await,
};
let audio = current_item.audio.lock();
@@ -131,7 +136,7 @@ pub fn pause(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
};
if !playing {
- return done();
+ return done().await;
}
{
@@ -147,21 +152,21 @@ pub fn pause(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
#[command]
#[aliases("continue")]
-pub fn resume(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
- _resume(ctx, msg)
+pub async fn resume(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
+ _resume(ctx, msg).await
}
-fn _resume(ctx: &mut Context, msg: &Message) -> Result<()> {
- let queue_lock = ctx.data.write().get::<PlayQueue>().cloned().unwrap();
+async fn _resume(ctx: &Context, msg: &Message) -> CommandResult {
+ let queue_lock = ctx.data.write().await.get::<PlayQueue>().cloned().unwrap();
- let done = || ctx.send(msg.channel_id, "r u srs", msg.tts);
+ let done = || util::send(ctx, msg.channel_id, "r u srs", msg.tts).map_err(CommandError::from);
let playing = {
let play_queue = queue_lock.read().unwrap();
let current_item = match play_queue.playing {
Some(ref x) => x,
None => {
- done()?;
+ done().await?;
return Ok(());
},
};
@@ -171,7 +176,7 @@ fn _resume(ctx: &mut Context, msg: &Message) -> Result<()> {
};
if playing {
- done()?;
+ done().await?;
debug!("attempted to resume playback while sound was already playing");
return Ok(());
}
@@ -188,8 +193,8 @@ fn _resume(ctx: &mut Context, msg: &Message) -> Result<()> {
#[command]
#[aliases("next")]
-pub fn skip(ctx: &mut Context, _msg: &Message, _args: Args) -> Result<()> {
- let data = ctx.data.write();
+pub async fn skip(ctx: &Context, _msg: &Message, _args: Args) -> CommandResult {
+ let data = ctx.data.write().await;
let mgr_lock = data.get::<VoiceManager>().cloned().unwrap();
let mut manager = mgr_lock.lock();
@@ -210,8 +215,8 @@ pub fn skip(ctx: &mut Context, _msg: &Message, _args: Args) -> Result<()> {
#[command]
#[aliases("sudoku", "fuckoff", "stop")]
-pub fn die(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
- let data = ctx.data.write();
+pub async fn die(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
+ let data = ctx.data.write().await;
let mgr_lock = data.get::<VoiceManager>().cloned().unwrap();
let mut manager = mgr_lock.lock();
@@ -231,7 +236,7 @@ pub fn die(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
handler.stop();
handler.leave();
} else {
- ctx.send(msg.channel_id, "YOU die", msg.tts)?;
+ util::send(ctx, msg.channel_id, "YOU die", msg.tts).await?;
debug!("got die with no handler attached");
}
@@ -240,43 +245,56 @@ pub fn die(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
#[command]
#[aliases("queue")]
-pub fn list(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
- let queue_lock = ctx.data.write().get::<PlayQueue>().cloned().unwrap();
+pub async fn list(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
+ let queue_lock = ctx.data.write().await.get::<PlayQueue>().cloned().unwrap();
let play_queue = queue_lock.read().unwrap();
- let channel_tmp = msg.channel(&ctx).unwrap().guild().unwrap();
- let channel = channel_tmp.read();
+ let channel = msg.channel(&ctx).await.unwrap().guild().unwrap();
info!("listing queue");
match play_queue.playing {
Some(ref info) => {
let audio = info.audio.lock();
- let status = if audio.playing { "playing" } else { "paused:" };
+ let status = if audio.playing {
+ "playing"
+ } else {
+ "paused:"
+ };
let playing_info = match info.init_args.data {
Left(ref url) => format!(" `{}`", url),
Right(_) => "memeing".to_owned(),
};
- ctx.send(msg.channel_id, &format!("Currently {} {} ({})", status, playing_info, info.init_args.initiator), msg.tts)?;
+ util::send(
+ ctx,
+ msg.channel_id,
+ &format!("Currently {} {} ({})", status, playing_info, info.init_args.initiator),
+ msg.tts,
+ )
+ .await?;
},
None => {
debug!("`list` called with no items in queue");
- ctx.send(msg.channel_id, "Nothing is playing you meme", msg.tts)?;
+ util::send(ctx, msg.channel_id, "Nothing is playing you meme", msg.tts).await?;
return Ok(());
},
}
- play_queue.meme_queue.iter()
+ play_queue
+ .meme_queue
+ .iter()
.chain(play_queue.general_queue.iter())
- .for_each(|info| {
+ .pipe(serenity::futures::stream::iter)
+ .for_each(|info| async move {
let playing_info = match info.data {
Left(ref url) => format!("`{}`", url),
Right(_) => "meme".to_owned(),
};
- let _ = channel.say(&ctx, &format!("{} ({})", playing_info, info.initiator));
- });
+ let _ = channel.say(&ctx, &format!("{} ({})", playing_info, info.initiator)).await;
+ })
+ .await;
Ok(())
}
diff --git a/src/commands/roll.rs b/src/commands/roll.rs
index 5f5ff6d..6aefe34 100644
--- a/src/commands/roll.rs
+++ b/src/commands/roll.rs
@@ -7,23 +7,26 @@ use log::{
use rand::prelude::*;
use serenity::{
framework::standard::{
- Args,
macros::command,
+ Args,
},
model::channel::Message,
prelude::*,
};
-use statrs;
use thiserror::Error;
use lazy_static::lazy_static;
-
-use crate::{
- Result,
- util::CtxExt,
+use serenity::{
+ framework::standard::{
+ CommandError,
+ CommandResult,
+ },
+ futures::TryFutureExt,
};
-#[derive(Parser)]
+use crate::util;
+
+#[derive(pest_derive::Parser)]
#[grammar = "commands/calc.pest"]
struct Calc;
@@ -42,9 +45,12 @@ pub(crate) enum CalcError {
impl Calc {
fn eval<S: AsRef<str>>(s: S) -> StdResult<f64, CalcError> {
use pest::{
- Parser,
+ iterators::{
+ Pair,
+ Pairs,
+ },
prec_climber::PrecClimber,
- iterators::{Pair, Pairs},
+ Parser,
};
use self::Rule::*;
@@ -52,12 +58,14 @@ impl Calc {
lazy_static! {
static ref CLIMBER: PrecClimber<self::Rule> = {
use pest::prec_climber::{
- Operator,
Assoc::*,
+ Operator,
};
PrecClimber::new(vec![
- Operator::new(add, Left) | Operator::new(sub, Left) | Operator::new(modulo, Left),
+ Operator::new(add, Left)
+ | Operator::new(sub, Left)
+ | Operator::new(modulo, Left),
Operator::new(mul, Left) | Operator::new(div, Left),
Operator::new(dice, Left),
Operator::new(pow, Right),
@@ -77,7 +85,8 @@ impl Calc {
_ => unreachable!(),
};
- u64::from_str_radix(&pair.as_str()[2..], base).map_err(|_| CalcError::NumberFormat)? as f64
+ u64::from_str_radix(&pair.as_str()[2..], base)
+ .map_err(|_| CalcError::NumberFormat)? as f64
},
float => pair.as_str().parse::<f64>().map_err(|_| CalcError::NumberFormat)?,
expr | num => eval_expr(pair.into_inner())?,
@@ -151,32 +160,29 @@ impl Calc {
}
fn eval_expr(p: Pairs<self::Rule>) -> StdResult<f64, CalcError> {
- CLIMBER.climb(
- p,
- eval_single_pair,
- |lhs, op, rhs| {
- let lhs = lhs?;
- let rhs = rhs?;
+ CLIMBER.climb(p, eval_single_pair, |lhs, op, rhs| {
+ let lhs = lhs?;
+ let rhs = rhs?;
- let result = match op.as_rule() {
- add => lhs + rhs,
- sub => lhs - rhs,
- mul => lhs * rhs,
- div => lhs / rhs,
- pow => lhs.powf(rhs),
- dice => {
- let dice_count = lhs as usize;
- let dice_faces = rhs as usize;
+ let result = match op.as_rule() {
+ add => lhs + rhs,
+ sub => lhs - rhs,
+ mul => lhs * rhs,
+ div => lhs / rhs,
+ pow => lhs.powf(rhs),
+ dice => {
+ let dice_count = lhs as usize;
+ let dice_faces = rhs as usize;
- let mut rng = thread_rng();
- (0..dice_count).map(|_| rng.gen_range(1, dice_faces + 1)).sum::<usize>() as f64
- },
- _ => unreachable!(),
- };
+ let mut rng = thread_rng();
+ (0..dice_count).map(|_| rng.gen_range(1..(dice_faces + 1))).sum::<usize>()
+ as f64
+ },
+ _ => unreachable!(),
+ };
- Ok(result)
- }
- )
+ Ok(result)
+ })
}
eval_expr(result)
@@ -208,15 +214,19 @@ mod test {
#[command]
#[aliases("calc", "calculate")]
-pub fn roll(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
+pub async fn roll(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
match Calc::eval(args.rest()) {
Ok(result) => {
debug!("got calc result '{}'", result);
- ctx.send(msg.channel_id, &format!("{}", result), msg.tts)
+ util::send(ctx, msg.channel_id, &format!("{}", result), msg.tts)
+ .map_err(CommandError::from)
+ .await
},
Err(e) => {
error!("error encountered reading calc '{}': {}", args.rest(), e);
- ctx.send(msg.channel_id, "I COULDN'T READ THAT YOU FUCK", msg.tts)
+ util::send(ctx, msg.channel_id, "I COULDN'T READ THAT YOU FUCK", msg.tts)
+ .map_err(CommandError::from)
+ .await
},
}
}
diff --git a/src/commands/sound_levels.rs b/src/commands/sound_levels.rs
index d913d06..db0b6a6 100644
--- a/src/commands/sound_levels.rs
+++ b/src/commands/sound_levels.rs
@@ -6,94 +6,107 @@ use log::{
};
use serenity::{
framework::standard::{
- Args,
macros::command,
+ Args,
+ CommandError,
+ CommandResult,
},
+ futures::TryFutureExt,
model::channel::Message,
prelude::*,
};
use crate::{
- Result,
+ audio::{
+ PlayQueue,
+ VoiceManager,
+ },
+ util,
CONFIG,
- audio::{PlayQueue, VoiceManager},
- util::CtxExt,
};
pub const DEFAULT_VOLUME: f32 = 0.20;
const MAX_VOLUME: f32 = 5.0;
#[command]
-pub fn mute(ctx: &mut Context, _: &Message, _: Args) -> Result<()> {
- let mgr_lock = ctx.data.write().get::<VoiceManager>().cloned().unwrap();
+pub async fn mute(ctx: &Context, _: &Message, _: Args) -> CommandResult {
+ let mgr_lock = ctx.data.write().await.get::<VoiceManager>().cloned().unwrap();
let mut manager = mgr_lock.lock();
- manager.get_mut(CONFIG.discord.guild())
- .map(|handler| {
- if handler.self_mute {
- trace!("Already muted.")
- } else {
- handler.mute(true);
- trace!("Muted");
- }
- });
+ manager.get_mut(CONFIG.discord.guild()).map(|handler| {
+ if handler.self_mute {
+ trace!("Already muted.")
+ } else {
+ handler.mute(true);
+ trace!("Muted");
+ }
+ });
Ok(())
}
#[command]
-pub fn unmute(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> {
- let mgr_lock = ctx.data.write().get::<VoiceManager>().cloned().unwrap();
+pub async fn unmute(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
+ let mgr_lock = ctx.data.write().await.get::<VoiceManager>().cloned().unwrap();
let mut manager = mgr_lock.lock();
- manager.get_mut(CONFIG.discord.guild())
- .map(|handler| {
- if !handler.self_mute {
- trace!("Already unmuted.")
- } else {
- handler.mute(false);
- trace!("Unmuted");
- let _ = ctx.send(msg.channel_id, "REEEEEEEEEEEEEE", msg.tts);
- }
- });
+ if let Some(handler) = manager.get_mut(CONFIG.discord.guild()) {
+ if !handler.self_mute {
+ trace!("Already unmuted.")
+ } else {
+ handler.mute(false);
+ trace!("Unmuted");
+ let _ = util::send(ctx, msg.channel_id, "REEEEEEEEEEEEEE", msg.tts)
+ .map_err(CommandError::from)
+ .await;
+ }
+ }
Ok(())
}
#[command]
-pub fn volume(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
+pub async fn volume(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
if args.len() == 0 {
let vol = {
- let queue_lock = ctx.data.write().get::<PlayQueue>().cloned().unwrap();
+ let queue_lock = ctx.data.write().await.get::<PlayQueue>().cloned().unwrap();
let play_queue = queue_lock.read().unwrap();
(play_queue.volume / DEFAULT_VOLUME * 100.0) as usize
};
trace!("reporting volume {}", vol);
- return ctx.send(msg.channel_id, &format!("volume: {}%", vol), msg.tts);
+ return util::send(ctx, msg.channel_id, &format!("volume: {}%", vol), msg.tts)
+ .map_err(CommandError::from)
+ .await;
}
let vol: usize = match args.single::<f32>() {
Ok(vol) if vol.is_nan() => {
warn!("reporting NaN volume");
- return ctx.send(msg.channel_id, "you're a fuck", msg.tts);
+ return util::send(ctx, msg.channel_id, "you're a fuck", msg.tts)
+ .map_err(CommandError::from)
+ .await;
},
Ok(vol) => vol as usize,
Err(e) => {
error!("parsing volume arg: {}", e);
- return ctx.send(msg.channel_id, "???????", msg.tts)
+ return util::send(ctx, msg.channel_id, "???????", msg.tts)
+ .map_err(CommandError::from)
+ .await;
},
};
- let mut vol: f32 = (vol as f32)/100.0; // force aliasing to reasonable values
+ let mut vol: f32 = (vol as f32) / 100.0; // force aliasing to reasonable values
let adjusted_text = if vol > MAX_VOLUME {
format!(" ({:.0}% max)", MAX_VOLUME * 100.0)
- } else { "".to_owned() };
+ } else {
+ "".to_owned()
+ };
vol = vol.clamp(0.0, MAX_VOLUME);
- let queue_lock = ctx.data.write().get::<PlayQueue>().cloned().unwrap();
+ let queue_lock = ctx.data.write().await.get::<PlayQueue>().cloned().unwrap();
{
let mut play_queue = queue_lock.write().unwrap();
@@ -101,7 +114,7 @@ pub fn volume(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
info!("volume updated to {}", vol);
}
- ctx.send(msg.channel_id, format!("volume adjusted{}", adjusted_text), msg.tts)?;
+ util::send(ctx, msg.channel_id, format!("volume adjusted{}", adjusted_text), msg.tts).await?;
{
let play_queue = queue_lock.read().unwrap();
diff --git a/src/commands/today/mod.rs b/src/commands/today/mod.rs
index 0a5eefe..0a0ba7b 100644
--- a/src/commands/today/mod.rs
+++ b/src/commands/today/mod.rs
@@ -1,106 +1,98 @@
-use serenity::{
- prelude::*,
- model::{
- channel::Message,
- },
- framework::standard::{
- Args,
- macros::command,
- },
-};
-use chrono::{Duration};
+use chrono::Duration;
use either::Left;
use lazy_static::lazy_static;
+use log::debug;
use rand::{
- thread_rng,
seq::SliceRandom,
+ thread_rng,
+};
+use serenity::{
+ framework::standard::{
+ macros::command,
+ Args,
+ CommandResult,
+ },
+ model::channel::Message,
+ prelude::*,
};
-use log::debug;
use crate::{
- Result,
- CtxExt,
audio::{
PlayArgs,
PlayQueue,
},
+ util,
};
mod prelude;
-mod sept_21;
mod nov_5;
+mod sept_21;
-mod halloween;
-mod ussr;
mod france;
-mod shrek;
+mod halloween;
mod putin;
+mod shrek;
+mod ussr;
-mod wednesday;
mod thursday;
mod tomorrow;
+mod wednesday;
mod pianoman;
-pub type TodayIter = Box<dyn Iterator<Item=TodayArgs>>;
+pub type TodayIter = Box<dyn Iterator<Item = TodayArgs>>;
#[derive(Clone, Debug, Hash, Default)]
pub struct TodayArgs {
- pub url: &'static str,
+ pub url: &'static str,
pub start: Option<Duration>,
- pub end: Option<Duration>,
+ pub end: Option<Duration>,
}
impl TodayArgs {
#[inline]
pub fn as_play_args(&self, msg: &Message) -> PlayArgs {
PlayArgs {
- initiator: "you have done this to yourself :^)".to_string(),
- data: Left(self.url.to_owned()),
+ initiator: "you have done this to yourself :^)".to_string(),
+ data: Left(self.url.to_owned()),
sender_channel: msg.channel_id,
- start: self.start,
- end: self.end,
+ start: self.start,
+ end: self.end,
}
}
}
-
lazy_static! {
- static ref ALL: Vec<fn(chrono::NaiveDateTime) -> TodayIter> = vec! [
+ static ref ALL: Vec<fn(chrono::NaiveDateTime) -> TodayIter> = vec![
sept_21::sept_21,
nov_5::nov_5,
-
halloween::halloween,
ussr::ussr,
france::france,
shrek::shrek,
putin::putin,
-
wednesday::wednesday,
thursday::thursday,
tomorrow::tomorrow,
-
pianoman::pianoman,
];
}
-
#[command]
-pub fn today(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> {
+pub async fn today(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let today = {
#[allow(unused_mut)]
let mut result = chrono::Local::now().naive_local();
- #[cfg(debug_assertions)] {
- let dt = _args.parse::<chrono::NaiveDateTime>()
- .or_else(|_| {
- _args.parse::<chrono::NaiveDate>()
- .map(|date| {
- let time = chrono::NaiveTime::from_hms_opt(12, 0, 0).unwrap();
- date.and_time(time)
- })
- });
+ #[cfg(debug_assertions)]
+ {
+ let dt = _args.parse::<chrono::NaiveDateTime>().or_else(|_| {
+ _args.parse::<chrono::NaiveDate>().map(|date| {
+ let time = chrono::NaiveTime::from_hms_opt(12, 0, 0).unwrap();
+ date.and_time(time)
+ })
+ });
match dt {
Ok(dt) => {
@@ -109,34 +101,31 @@ pub fn today(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> {
},
Err(e) => {
log::debug!("parsing datetime: {:?}", e);
- }
+ },
};
}
result
};
- let options: Vec<TodayArgs> = ALL.iter()
- .flat_map(|f| f(today))
- .collect();
+ let options: Vec<TodayArgs> = ALL.iter().flat_map(|f| f(today)).collect();
debug!("{} options for {}", options.len(), today);
- let play_args = options.choose(&mut thread_rng())
- .map(|x| x.as_play_args(msg));
+ let play_args = options.choose(&mut thread_rng()).map(|x| x.as_play_args(msg));
if let Some(play_args) = play_args {
play_args.data.as_ref().left().iter().for_each(|url| {
debug!("today selected: {}", url);
});
- let queue_lock = ctx.data.write().get::<PlayQueue>().cloned().unwrap();
+ let queue_lock = ctx.data.write().await.get::<PlayQueue>().cloned().unwrap();
let mut play_queue = queue_lock.write().unwrap();
play_queue.general_queue.push_front(play_args);
} else {
- ctx.send(msg.channel_id, "no", false)?;
- ctx.send(msg.channel_id, ":angry:", false)?;
+ util::send(ctx, msg.channel_id, "no", false).await?;
+ util::send(ctx, msg.channel_id, ":angry:", false).await?;
}
Ok(())
diff --git a/src/config.rs b/src/config.rs
index 846b25b..e03dbbe 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -16,7 +16,7 @@ lazy_static! {
pub static ref CONFIG: Config = {
dotenv().ok();
- Config::init().unwrap()
+ Config::init_from_env().unwrap()
};
pub static ref FFMPEG_COMMAND: String = {
diff --git a/src/db/mod.rs b/src/db/mod.rs
index 476d9d9..1ac44e9 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -235,7 +235,7 @@ pub fn rare_meme(conn: &mut PgConnection, audio: bool) -> Result<Meme> {
}
let mut rng = thread_rng();
- let target_prob = rng.gen_range(0, elems.last().unwrap().1);
+ let target_prob = rng.gen_range(0..elems.last().unwrap().1);
let meme_id = elems.into_iter()
.find(|(_, x)| target_prob < *x)
diff --git a/src/db/models.rs b/src/db/models.rs
index f2127b5..cdcdd99 100644
--- a/src/db/models.rs
+++ b/src/db/models.rs
@@ -5,6 +5,7 @@ use diesel::{
prelude::*,
Queryable,
};
+use sha1::Digest;
use crate::{
db::schema::*,
diff --git a/src/db/schema.rs b/src/db/schema.rs
index 01ec1d0..e89b24a 100644
--- a/src/db/schema.rs
+++ b/src/db/schema.rs
@@ -1,4 +1,4 @@
-table! {
+diesel::table! {
audio (id) {
id -> Int4,
data -> Bytea,
@@ -7,7 +7,7 @@ table! {
}
}
-table! {
+diesel::table! {
audit_records (id) {
id -> Int4,
updated -> Timestamp,
@@ -16,7 +16,7 @@ table! {
}
}
-table! {
+diesel::table! {
images (id) {
id -> Int4,
data -> Bytea,
@@ -26,7 +26,7 @@ table! {
}
}
-table! {
+diesel::table! {
invocation_records (id) {
id -> Int4,
user_id -> Int8,
@@ -37,7 +37,7 @@ table! {
}
}
-table! {
+diesel::table! {
memes (id) {
id -> Int4,
title -> Varchar,
@@ -48,7 +48,7 @@ table! {
}
}
-table! {
+diesel::table! {
metadata (id) {
id -> Int4,
created -> Timestamp,
@@ -56,7 +56,7 @@ table! {
}
}
-table! {
+diesel::table! {
tombstones (id) {
id -> Int4,
meme_id -> Int4,
@@ -66,16 +66,16 @@ table! {
}
}
-joinable!(audio -> metadata (metadata_id));
-joinable!(audit_records -> metadata (metadata_id));
-joinable!(images -> metadata (metadata_id));
-joinable!(memes -> audio (audio_id));
-joinable!(memes -> images (image_id));
-joinable!(memes -> metadata (metadata_id));
-joinable!(tombstones -> metadata (metadata_id));
-joinable!(invocation_records -> memes (meme_id));
+diesel::joinable!(audio -> metadata (metadata_id));
+diesel::joinable!(audit_records -> metadata (metadata_id));
+diesel::joinable!(images -> metadata (metadata_id));
+diesel::joinable!(memes -> audio (audio_id));
+diesel::joinable!(memes -> images (image_id));
+diesel::joinable!(memes -> metadata (metadata_id));
+diesel::joinable!(tombstones -> metadata (metadata_id));
+diesel::joinable!(invocation_records -> memes (meme_id));
-allow_tables_to_appear_in_same_query!(
+diesel::allow_tables_to_appear_in_same_query!(
audio,
audit_records,
images,
diff --git a/src/game.rs b/src/game.rs
index 0f6637e..362e304 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -2,12 +2,12 @@ use std::{
convert::Infallible,
fs,
iter,
+ path::PathBuf,
result::Result as StdResult,
str::{
self,
FromStr,
},
- path::PathBuf,
};
use fnv::{
@@ -44,27 +44,20 @@ use anyhow::{
Error,
};
use lazy_static::lazy_static;
+use serenity::framework::standard::CommandResult;
use crate::{
- util::CtxExt,
+ util,
Result,
CONFIG,
};
-pub use self::GAME_GROUP as GROUP;
+pub use self::Game as GROUP;
-group!({
- name: "game",
- options: {
- only_in: "guild",
- },
- commands: [
- game,
- installedgame,
- ownedgame,
- updategaem,
- ],
-});
+#[group]
+#[prefix = "game"]
+#[commands(game, installedgame, ownedgame, updategaem)]
+pub struct Game;
lazy_static! {
static ref SPREADSHEET_URL: Url = Url::parse(&format!(
@@ -101,7 +94,8 @@ lazy_static! {
static ref USER_INFO_MAP: FnvHashMap<String, ProfileInfo> = {
let v: Vec<UserInfo> = serde_json::from_str(&USER_MAP_STR).unwrap();
- let result = v.into_iter()
+ let result = v
+ .into_iter()
.map(|ui| {
let UserInfo {
name,
@@ -112,7 +106,11 @@ lazy_static! {
})
.collect::<FnvHashMap<_, _>>();
- log::info!("loaded user info for {} users ({:#?})", result.len(), result.keys().collect::<Vec<_>>());
+ log::info!(
+ "loaded user info for {} users ({:#?})",
+ result.len(),
+ result.keys().collect::<Vec<_>>()
+ );
result
};
@@ -120,14 +118,16 @@ lazy_static! {
USER_INFO_MAP
.clone()
.into_iter()
- .map(|(name, profile)| (UserId(profile.discord_user_id), name))
+ .map(|(name, profile)| (UserId::new(profile.discord_user_id), name))
.collect::<FnvHashMap<_, _>>()
};
static ref STEAM_MAP: FnvHashMap<UserId, u64> = {
USER_INFO_MAP
.clone()
.into_iter()
- .filter_map(|(_, profile)| profile.steam_id.map(|sid| (UserId(profile.discord_user_id), sid)))
+ .filter_map(|(_, profile)| {
+ profile.steam_id.map(|sid| (UserId::new(profile.discord_user_id), sid))
+ })
.collect::<FnvHashMap<_, _>>()
};
static ref ALPHABET: Vec<char> = (0..26).map(|x| (x + b'a') as char).collect();
@@ -163,13 +163,13 @@ impl FromStr for GameStatus {
#[command]
#[aliases("installedgaem")]
-pub fn installedgame(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
+pub async fn installedgame(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
_game(ctx, msg, args, GameStatus::Installed)
}
#[command]
#[aliases("ownedgaem")]
-pub fn ownedgame(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
+pub async fn ownedgame(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
_game(ctx, msg, args, GameStatus::NotInstalled)
}
@@ -186,30 +186,34 @@ pub fn get_user_id<S: AsRef<str>>(g: &Guild, s: S) -> StdResult<UserId, UserLook
let s = s.as_ref().trim_start_matches("@").to_lowercase();
if let Some(info) = USER_INFO_MAP.get(&s) {
- return Ok(UserId(info.discord_user_id));
+ return Ok(UserId::new(info.discord_user_id));
}
let nicks = g.members_nick_containing(&s, false, false);
{
- let exact_match = nicks.iter().find(|m| m.user.read().name.to_lowercase() == s);
+ let exact_match = nicks.iter().find(|(m, _)| m.display_name().to_lowercase() == s);
- if let Some(m) = exact_match {
- return Ok(m.user_id());
+ if let Some((m, _)) = exact_match {
+ return Ok(m.user.id);
}
}
let usernames = g.members_username_containing(&s, false, false);
{
- let exact_match = usernames.iter().find(|m| m.user.read().name.to_lowercase() == s);
+ let exact_match = usernames.iter().find(|(m, _)| m.user.name.to_lowercase() == s);
- if let Some(m) = exact_match {
- return Ok(m.user_id());
+ if let Some((m, _)) = exact_match {
+ return Ok(m.user.id);
}
}
- let opts = nicks.into_iter().chain(usernames.into_iter()).map(|member| member.user_id()).collect::<FnvHashSet<_>>();
+ let opts = nicks
+ .into_iter()
+ .chain(usernames.into_iter())
+ .map(|(member, _)| member.user.id)
+ .collect::<FnvHashSet<_>>();
match opts.len() {
0 => Err(UserLookupError::NotFound),
@@ -220,16 +224,19 @@ pub fn get_user_id<S: AsRef<str>>(g: &Guild, s: S) -> StdResult<UserId, UserLook
#[command]
#[aliases("gaem")]
-fn game(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> {
- _game(ctx, msg, args, GameStatus::Installed)
+async fn game(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
+ _game(ctx, msg, args, GameStatus::Installed).await
}
-fn _game(ctx: &mut Context, msg: &Message, mut args: Args, min_status: GameStatus) -> Result<()> {
- let guild = msg.channel_id.to_channel(&ctx)?.guild().ok_or(anyhow!("couldn't find guild"))?;
-
- let guild = guild.read().guild(&ctx).ok_or(anyhow!("couldn't find guild"))?;
-
- let guild = guild.read();
+async fn _game(
+ ctx: &Context,
+ msg: &Message,
+ mut args: Args,
+ min_status: GameStatus,
+) -> CommandResult {
+ let guild =
+ msg.channel_id.to_channel(&ctx).await?.guild().ok_or(anyhow!("couldn't find guild"))?;
+ let guild = guild.guild(&ctx).ok_or_else(|| anyhow!("couldn't find guild"))?;
let user_args: Vec<String> = if args.rest().is_empty() {
Vec::new()
@@ -248,12 +255,22 @@ fn _game(ctx: &mut Context, msg: &Message, mut args: Args, min_status: GameStatu
match possible {
Err(UserLookupError::NotFound) => {
- let _ = ctx.send(msg.channel_id, &format!("didn't recognize {}", &u), msg.tts);
+ let _ = util::send(
+ ctx,
+ msg.channel_id,
+ &format!("didn't recognize {}", &u),
+ msg.tts,
+ );
None
},
Ok(x) => Some(x),
Err(UserLookupError::Ambiguous(x)) => {
- let _ = ctx.send(msg.channel_id, &format!("too many matches ({}) for {}", x, &u), msg.tts);
+ let _ = util::send(
+ ctx,
+ msg.channel_id,
+ &format!("too many matches ({}) for {}", x, &u),
+ msg.tts,
+ );
None
},
}
@@ -294,11 +311,11 @@ fn _game(ctx: &mut Context, msg: &Message, mut args: Args, min_status: GameStatu
if inferred && users.len() < 2 || !inferred && users.len() < 1 {
info!("too few known users to make game comparison");
- ctx.send(msg.channel_id, "yer too lonely", msg.tts)?;
+ util::send(ctx, msg.channel_id, "yer too lonely", msg.tts).await?;
return Ok(());
}
- let data = load_spreadsheet()?;
+ let data = load_spreadsheet().await?;
let user_indexes = (0..data.len())
.filter_map(|i| {
@@ -328,7 +345,8 @@ fn _game(ctx: &mut Context, msg: &Message, mut args: Args, min_status: GameStatu
.collect::<FnvHashMap<_, _>>();
(1..data[*col].len()).for_each(|i| {
- let status = &data_ref[*col][i].parse::<GameStatus>().unwrap_or(GameStatus::Unknown);
+ let status =
+ &data_ref[*col][i].parse::<GameStatus>().unwrap_or(GameStatus::Unknown);
let game = &data_ref[0][i];
game_map.get_mut(status).unwrap().insert(game);
@@ -338,23 +356,29 @@ fn _game(ctx: &mut Context, msg: &Message, mut args: Args, min_status: GameStatu
})
.collect::<FnvHashMap<_, _>>();
- let statuses = vec![GameStatus::Installed, GameStatus::NotOwned, GameStatus::NotInstalled, GameStatus::Unknown]
- .into_iter()
- .filter(|s| s <= &min_status)
- .collect::<Vec<_>>();
+ let statuses = vec![
+ GameStatus::Installed,
+ GameStatus::NotOwned,
+ GameStatus::NotInstalled,
+ GameStatus::Unknown,
+ ]
+ .into_iter()
+ .filter(|s| s <= &min_status)
+ .collect::<Vec<_>>();
let mut games_in_common = {
let game_map = user_games.values().nth(0).unwrap();
- statuses
- .iter()
- .fold(iter::empty().collect::<FnvHashSet<_>>(), |acc, s| acc.union(&game_map[s]).cloned().collect())
+ statuses.iter().fold(iter::empty().collect::<FnvHashSet<_>>(), |acc, s| {
+ acc.union(&game_map[s]).cloned().collect()
+ })
};
for (_user, game_map) in user_games.iter() {
- let relevant_games = statuses
- .iter()
- .fold(iter::empty().collect::<FnvHashSet<_>>(), |acc, s| acc.union(&game_map[s]).cloned().collect());
+ let relevant_games =
+ statuses.iter().fold(iter::empty().collect::<FnvHashSet<_>>(), |acc, s| {
+ acc.union(&game_map[s]).cloned().collect()
+ });
games_in_common = games_in_common.intersection(&relevant_games).cloned().collect();
}
@@ -366,12 +390,12 @@ fn _game(ctx: &mut Context, msg: &Message, mut args: Args, min_status: GameStatu
games_formatted = "**LITERALLY NOTHING**".to_owned();
}
- ctx.send(msg.channel_id, &games_formatted, msg.tts)?;
+ util::send(ctx, msg.channel_id, &games_formatted, msg.tts).await?;
Ok(())
}
-fn load_spreadsheet() -> Result<Vec<Vec<String>>> {
+async fn load_spreadsheet() -> Result<Vec<Vec<String>>> {
let mut u = SPREADSHEET_URL.clone();
u.query_pairs_mut()
@@ -382,7 +406,7 @@ fn load_spreadsheet() -> Result<Vec<Vec<String>>> {
let req = reqwest::Request::new(reqwest::Method::GET, u);
let client = reqwest::Client::new();
- let mut resp = client.execute(req)?;
+ let resp = client.execute(req).await?;
#[derive(Deserialize)]
struct Resp {
@@ -395,14 +419,14 @@ fn load_spreadsheet() -> Result<Vec<Vec<String>>> {
values: Vec<Vec<String>>,
}
- let resp = resp.json::<Resp>()?;
+ let resp = resp.json::<Resp>().await?;
Ok(resp.value_ranges.into_iter().next().unwrap().values)
}
#[command]
#[aliases("updategame")]
-pub fn updategaem(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> {
+pub async fn updategaem(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
use regex::Regex;
let arg_user = args.single_quoted::<String>();
@@ -412,7 +436,8 @@ pub fn updategaem(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()
} else {
use std::borrow::Borrow;
- let guild = msg.channel_id.to_channel(&ctx)?.guild().ok_or(anyhow!("couldn't find guild"))?;
+ let guild =
+ msg.channel_id.to_channel(&ctx)?.guild().ok_or(anyhow!("couldn't find guild"))?;
let guild = guild.read().guild(&ctx).ok_or(anyhow!("couldn't find guild"))?;
@@ -425,21 +450,24 @@ pub fn updategaem(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()
let username = match DISCORD_MAP.get(&user) {
Some(s) => s,
- None => return ctx.send(msg.channel_id, "WHO THE FUCK ARE YE", msg.tts),
+ None => return util::send(ctx, msg.channel_id, "WHO THE FUCK ARE YE", msg.tts).await,
};
let steam_id = match STEAM_MAP.get(&user) {
Some(u) => u,
- None => return ctx.send(msg.channel_id, "WHO ARE YE ON STEAM", msg.tts),
+ None => return util::send(ctx, msg.channel_id, "WHO ARE YE ON STEAM", msg.tts).await,
};
- let spreadsheet = load_spreadsheet()?;
+ let spreadsheet = load_spreadsheet().await?;
- let user_column = (0..spreadsheet.len()).find(|x| spreadsheet[*x][0].to_lowercase() == username.to_lowercase());
+ let user_column = (0..spreadsheet.len())
+ .find(|x| spreadsheet[*x][0].to_lowercase() == username.to_lowercase());
let user_column = match user_column {
Some(c) => &spreadsheet[c][1..],
- None => return ctx.send(msg.channel_id, "YER NOT IN THE SPREADSHEET", msg.tts),
+ None => {
+ return util::send(ctx, msg.channel_id, "YER NOT IN THE SPREADSHEET", msg.tts).await;
+ },
};
lazy_static! {
@@ -452,14 +480,16 @@ pub fn updategaem(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()
Some(c) => &spreadsheet[c][1..],
None => {
error!("didn't find an appid column in the spreadsheet");
- return ctx.send(msg.channel_id, "SPREADSHEET BROKE", msg.tts);
+ return util::send(ctx, msg.channel_id, "SPREADSHEET BROKE", msg.tts).await;
},
};
let missing_appids = (0..user_column.len())
.filter_map(|x| user_column[x].parse::<GameStatus>().ok().map(|s| (x, s)))
.filter(|(_, s)| *s == GameStatus::Unknown || *s == GameStatus::NotOwned)
- .filter_map(|(x, _)| appid_column.get(x).and_then(|s| s.parse::<u64>().ok().map(|appid| (appid, x))));
+ .filter_map(|(x, _)| {
+ appid_column.get(x).and_then(|s| s.parse::<u64>().ok().map(|appid| (appid, x)))
+ });
let mut u = Url::parse("https://api.steampowered.com/IPlayerService/GetOwnedGames/v1")?;
@@ -487,8 +517,15 @@ pub fn updategaem(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()
play_time: u64,
}
- let games_owned =
- reqwest::get(u)?.json::<SteamResp>()?.response.games.into_iter().map(|ge| ge.app_id).collect::<FnvHashSet<_>>();
+ let games_owned = reqwest::get(u)
+ .await?
+ .json::<SteamResp>()
+ .await?
+ .response
+ .games
+ .into_iter()
+ .map(|ge| ge.app_id)
+ .collect::<FnvHashSet<_>>();
let found_games = missing_appids
.filter_map(|(ai, x)| {
@@ -501,7 +538,8 @@ pub fn updategaem(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()
.join("\n");
if found_games.len() > 0 {
- ctx.send(
+ util::send(
+ ctx,
msg.channel_id,
&format!(
"{} games owned on steam that are missing from the list:\n{}",
@@ -511,6 +549,6 @@ pub fn updategaem(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()
msg.tts,
)
} else {
- ctx.send(msg.channel_id, "up to date", msg.tts)
+ util::send(ctx, msg.channel_id, "up to date", msg.tts)
}
}
diff --git a/src/main.rs b/src/main.rs
index 1bf3e98..ab5db5d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,13 +2,8 @@
#![feature(pattern)]
#![feature(concat_idents)]
#![feature(associated_type_defaults)]
-
-#![feature(box_syntax, box_patterns)]
-
-// trash dependencies that can't be fucked to upgrade to ed. 2018
-#[macro_use] extern crate diesel;
-#[macro_use] extern crate pest_derive;
-#[macro_use] extern crate envconfig_derive;
+#![feature(box_patterns)]
+#![allow(deprecated)]
use std::{
thread,
@@ -23,8 +18,10 @@ use log::{
info,
};
-pub use self::util::*;
-pub use self::config::*;
+pub use self::{
+ config::*,
+ util::*,
+};
#[cfg(feature = "diesel")]
mod db;
@@ -38,16 +35,16 @@ mod game {
#[inline]
fn register(f: StandardFramework) -> StandardFramework {
- return f
+ return f;
}
}
-mod commands;
-mod util;
mod audio;
+mod bot;
+mod commands;
mod config;
mod log_setup;
-mod bot;
+mod util;
pub type Error = anyhow::Error;
pub type Result<T> = anyhow::Result<T>;
@@ -74,7 +71,7 @@ fn main() {
_ => {
// NOTE: we MUST have gotten here through SIGINT/SIGTERM handlers
::std::process::exit(0);
- }
+ },
}
if Instant::now() - start >= MIN_RUN_DURATION {
diff --git a/src/util.rs b/src/util.rs
index e9b6203..ed5fd54 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -6,79 +6,95 @@ use serenity::{
MessageId,
},
permissions::Permissions,
- }
+ },
};
-use url::Url;
use lazy_static::lazy_static;
use log::debug;
+use serenity::{
+ all::CreateMessage,
+ futures::{
+ AsyncReadExt,
+ StreamExt,
+ },
+};
+use url::Url;
use crate::{
- CONFIG,
audio::PlayQueue,
Result,
+ CONFIG,
};
-pub trait CtxExt {
- fn currently_playing(&self) -> bool;
- fn users_listening(&self) -> Result<bool>;
- fn send<A: AsRef<str>>(&self, channel: ChannelId, text: A, tts: bool) -> Result<()>;
- fn send_result<A: AsRef<str>>(&self, channel: ChannelId, text: A, tts: bool) -> Result<MessageId>;
+pub async fn currently_playing(ctx: &Context) -> bool {
+ let queue_lock = {
+ let data = ctx.data.read().await;
+ data.get::<PlayQueue>().cloned().unwrap()
+ };
+
+ let play_queue = queue_lock.read().unwrap();
+ play_queue.playing.is_some()
}
-impl CtxExt for Context {
- fn currently_playing(&self) -> bool {
- let queue_lock = self.data.read().get::<PlayQueue>().cloned().unwrap();
- let play_queue = queue_lock.read().unwrap();
- play_queue.playing.is_some()
- }
+pub async fn users_listening(ctx: &Context) -> Result<bool> {
+ let channel = CONFIG.discord.voice_channel().to_channel(&ctx).await?;
- fn users_listening(&self) -> Result<bool> {
- let channel = CONFIG.discord.voice_channel().to_channel(self)?;
- let res = channel.guild()
- .and_then(|ch| ch.read().guild(self))
- .map(|g| (&g.read().voice_states)
+ let res = channel
+ .guild()
+ .and_then(|ch| ch.guild(&ctx))
+ .map(|g| {
+ (&g.voice_states)
.into_iter()
- .any(|(_, state)| state.channel_id == Some(CONFIG.discord.voice_channel())))
- .unwrap_or(false);
+ .any(|(_, state)| state.channel_id == Some(CONFIG.discord.voice_channel()))
+ })
+ .unwrap_or(false);
- Ok(res)
- }
+ Ok(res)
+}
+
+#[inline]
+pub async fn send(
+ ctx: &Context,
+ channel: ChannelId,
+ text: impl AsRef<str>,
+ tts: bool,
+) -> Result<()> {
+ send_result(ctx, channel, text, tts).await.map(|_| ())
+}
- #[inline]
- fn send<A: AsRef<str>>(&self, channel: ChannelId, text: A, tts: bool) -> Result<()> {
- self.send_result(channel, text, tts).map(|_| ())
- }
+pub async fn send_result(
+ ctx: &Context,
+ channel: ChannelId,
+ text: impl AsRef<str>,
+ tts: bool,
+) -> Result<MessageId> {
+ let text = text.as_ref();
+ debug!("sending message {:?} to channel {:?} (tts: {})", text, channel, tts);
- #[inline]
- fn send_result<A: AsRef<str>>(&self, channel: ChannelId, text: A, tts: bool) -> Result<MessageId> {
- let text = text.as_ref();
- debug!("sending message {:?} to channel {:?} (tts: {})", text, channel, tts);
- let result = channel.send_message(self, |m| m.content(text).tts(tts))?;
- Ok(result.id)
- }
+ let result = channel.send_message(ctx, CreateMessage::default().content(text).tts(tts)).await?;
+ Ok(result.id)
}
lazy_static! {
- static ref REQUIRED_PERMS: Permissions = Permissions::EMBED_LINKS |
- Permissions::READ_MESSAGES |
- Permissions::ADD_REACTIONS |
- Permissions::SEND_MESSAGES |
- Permissions::SEND_TTS_MESSAGES |
- Permissions::MENTION_EVERYONE |
- Permissions::USE_EXTERNAL_EMOJIS |
- Permissions::CONNECT |
- Permissions::SPEAK |
- Permissions::CHANGE_NICKNAME |
- Permissions::USE_VAD |
- Permissions::ATTACH_FILES;
+ static ref REQUIRED_PERMS: Permissions = Permissions::EMBED_LINKS
+ | Permissions::READ_MESSAGES
+ | Permissions::ADD_REACTIONS
+ | Permissions::SEND_MESSAGES
+ | Permissions::SEND_TTS_MESSAGES
+ | Permissions::MENTION_EVERYONE
+ | Permissions::USE_EXTERNAL_EMOJIS
+ | Permissions::CONNECT
+ | Permissions::SPEAK
+ | Permissions::CHANGE_NICKNAME
+ | Permissions::USE_VAD
+ | Permissions::ATTACH_FILES;
}
lazy_static! {
- pub static ref OAUTH_URL: Url = Url::parse(
- &format!(
- "https://discordapp.com/api/oauth2/authorize?scope=bot&permissions={}&client_id={}",
- REQUIRED_PERMS.bits(), CONFIG.discord.auth.client_id,
- )
- ).unwrap();
+ pub static ref OAUTH_URL: Url = Url::parse(&format!(
+ "https://discordapp.com/api/oauth2/authorize?scope=bot&permissions={}&client_id={}",
+ REQUIRED_PERMS.bits(),
+ CONFIG.discord.auth.client_id,
+ ))
+ .unwrap();
}