Core: asyncify Votes

This commit is contained in:
Natsu Kagami 2020-09-06 22:33:48 -04:00
parent 890cb21770
commit 9975aa5880
Signed by: nki
GPG key ID: 73376E117CD20735

View file

@ -1,14 +1,18 @@
use serenity::framework::standard::CommandError as Error;
use serenity::{
collector::ReactionAction,
framework::standard::{macros::command, Args, CommandResult},
model::{
channel::{Message, Reaction, ReactionType},
channel::{Message, ReactionType},
id::UserId,
},
utils::MessageBuilder,
};
use std::collections::{HashMap as Map, HashSet as Set};
use std::time::Duration;
use std::{
collections::{HashMap as Map, HashSet as Set},
convert::TryFrom,
};
use youmubot_prelude::{Duration as ParseDuration, *};
#[command]
@ -19,13 +23,13 @@ use youmubot_prelude::{Duration as ParseDuration, *};
#[only_in(guilds)]
#[min_args(2)]
#[owner_privilege]
pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
pub async fn vote(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
// Parse stuff first
let args = args.quoted();
let _duration = args.single::<ParseDuration>()?;
let duration = &_duration.0;
if *duration < Duration::from_secs(2 * 60) || *duration > Duration::from_secs(60 * 60 * 24) {
msg.reply(ctx, format!("😒 Invalid duration ({}). The voting time should be between **2 minutes** and **1 day**.", _duration))?;
msg.reply(ctx, format!("😒 Invalid duration ({}). The voting time should be between **2 minutes** and **1 day**.", _duration)).await?;
return Ok(());
}
let question = args.single::<String>()?;
@ -41,7 +45,8 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
msg.reply(
ctx,
"😒 Can't have a nice voting session if you only have one choice.",
)?;
)
.await?;
return Ok(());
}
if choices.len() > MAX_CHOICES {
@ -52,7 +57,8 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
"😵 Too many choices... We only support {} choices at the moment!",
MAX_CHOICES
),
)?;
)
.await?;
return Ok(());
}
@ -89,70 +95,59 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
.description(MessageBuilder::new().push_bold_line_safe(&question).push("\nThis question was asked by ").push(author.mention()))
.fields(fields.into_iter())
})
})?;
msg.delete(&ctx)?;
}).await?;
msg.delete(&ctx).await?;
// React on all the choices
choices
.iter()
.try_for_each(|(emote, _)| panel.react(&ctx, &emote[..]))?;
.map(|(emote, _)| {
panel
.react(&ctx, ReactionType::try_from(&emote[..]).unwrap())
.map_ok(|_| ())
})
.collect::<stream::FuturesUnordered<_>>()
.try_collect::<()>()
.await?;
// A handler for votes.
struct VoteHandler {
pub ctx: Context,
pub msg: Message,
pub user_reactions: Map<String, Set<UserId>>,
pub panel: Message,
}
impl VoteHandler {
fn new(ctx: Context, msg: Message, panel: Message, choices: &[(String, String)]) -> Self {
VoteHandler {
ctx,
msg,
user_reactions: choices
let mut user_reactions: Map<String, Set<UserId>> = choices
.iter()
.map(|(emote, _)| (emote.clone(), Set::new()))
.collect(),
panel,
}
}
}
.collect();
impl ReactionHandler for VoteHandler {
fn handle_reaction(&mut self, reaction: &Reaction, is_add: bool) -> CommandResult {
if reaction.message_id != self.panel.id {
return Ok(());
}
if reaction.user(&self.ctx)?.bot {
return Ok(());
}
// Collect reactions...
msg.await_reactions(&ctx)
.timeout(*duration)
.await
.scan(user_reactions, |set, reaction| async move {
let (reaction, is_add) = match &*reaction {
ReactionAction::Added(r) => (r, true),
ReactionAction::Removed(r) => (r, false),
};
let users = if let ReactionType::Unicode(ref s) = reaction.emoji {
if let Some(users) = self.user_reactions.get_mut(s.as_str()) {
if let Some(users) = set.get_mut(s.as_str()) {
users
} else {
return Ok(());
return None;
}
} else {
return Ok(());
return None;
};
let user_id = match reaction.user_id {
Some(v) => v,
None => return None,
};
if is_add {
users.insert(reaction.user_id);
users.insert(user_id);
} else {
users.remove(&reaction.user_id);
}
Ok(())
}
users.remove(&user_id);
}
Some(())
})
.collect::<()>()
.await;
ctx.data
.get_cloned::<ReactionWatcher>()
.handle_reactions_timed(
VoteHandler::new(ctx.clone(), msg.clone(), panel, &choices),
*duration,
move |vh| {
let (ctx, msg, user_reactions, panel) =
(vh.ctx, vh.msg, vh.user_reactions, vh.panel);
// Handle choices
let choice_map = choices.into_iter().collect::<Map<_, _>>();
let result: Vec<(String, Vec<UserId>)> = user_reactions
.into_iter()
@ -169,8 +164,10 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
.push(", sorry 😭")
.build(),
)
.ok();
} else {
.await?;
return Ok(());
}
channel
.send_message(&ctx, |c| {
c.content({
@ -201,11 +198,8 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
content.build()
})
})
.ok();
}
panel.delete(&ctx).ok();
},
);
.await?;
panel.delete(&ctx).await?;
Ok(())
// unimplemented!();