mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-19 16:58:55 +00:00
Core: asyncify Votes
This commit is contained in:
parent
890cb21770
commit
9975aa5880
1 changed files with 100 additions and 106 deletions
|
@ -1,14 +1,18 @@
|
||||||
use serenity::framework::standard::CommandError as Error;
|
use serenity::framework::standard::CommandError as Error;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
|
collector::ReactionAction,
|
||||||
framework::standard::{macros::command, Args, CommandResult},
|
framework::standard::{macros::command, Args, CommandResult},
|
||||||
model::{
|
model::{
|
||||||
channel::{Message, Reaction, ReactionType},
|
channel::{Message, ReactionType},
|
||||||
id::UserId,
|
id::UserId,
|
||||||
},
|
},
|
||||||
utils::MessageBuilder,
|
utils::MessageBuilder,
|
||||||
};
|
};
|
||||||
use std::collections::{HashMap as Map, HashSet as Set};
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::{
|
||||||
|
collections::{HashMap as Map, HashSet as Set},
|
||||||
|
convert::TryFrom,
|
||||||
|
};
|
||||||
use youmubot_prelude::{Duration as ParseDuration, *};
|
use youmubot_prelude::{Duration as ParseDuration, *};
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
|
@ -19,13 +23,13 @@ use youmubot_prelude::{Duration as ParseDuration, *};
|
||||||
#[only_in(guilds)]
|
#[only_in(guilds)]
|
||||||
#[min_args(2)]
|
#[min_args(2)]
|
||||||
#[owner_privilege]
|
#[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
|
// Parse stuff first
|
||||||
let args = args.quoted();
|
let args = args.quoted();
|
||||||
let _duration = args.single::<ParseDuration>()?;
|
let _duration = args.single::<ParseDuration>()?;
|
||||||
let duration = &_duration.0;
|
let duration = &_duration.0;
|
||||||
if *duration < Duration::from_secs(2 * 60) || *duration > Duration::from_secs(60 * 60 * 24) {
|
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(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let question = args.single::<String>()?;
|
let question = args.single::<String>()?;
|
||||||
|
@ -41,7 +45,8 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
msg.reply(
|
msg.reply(
|
||||||
ctx,
|
ctx,
|
||||||
"😒 Can't have a nice voting session if you only have one choice.",
|
"😒 Can't have a nice voting session if you only have one choice.",
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if choices.len() > MAX_CHOICES {
|
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!",
|
"😵 Too many choices... We only support {} choices at the moment!",
|
||||||
MAX_CHOICES
|
MAX_CHOICES
|
||||||
),
|
),
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
return Ok(());
|
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()))
|
.description(MessageBuilder::new().push_bold_line_safe(&question).push("\nThis question was asked by ").push(author.mention()))
|
||||||
.fields(fields.into_iter())
|
.fields(fields.into_iter())
|
||||||
})
|
})
|
||||||
})?;
|
}).await?;
|
||||||
msg.delete(&ctx)?;
|
msg.delete(&ctx).await?;
|
||||||
// React on all the choices
|
// React on all the choices
|
||||||
choices
|
choices
|
||||||
.iter()
|
.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.
|
// A handler for votes.
|
||||||
struct VoteHandler {
|
let mut user_reactions: Map<String, Set<UserId>> = choices
|
||||||
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
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(emote, _)| (emote.clone(), Set::new()))
|
.map(|(emote, _)| (emote.clone(), Set::new()))
|
||||||
.collect(),
|
.collect();
|
||||||
panel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReactionHandler for VoteHandler {
|
// Collect reactions...
|
||||||
fn handle_reaction(&mut self, reaction: &Reaction, is_add: bool) -> CommandResult {
|
msg.await_reactions(&ctx)
|
||||||
if reaction.message_id != self.panel.id {
|
.timeout(*duration)
|
||||||
return Ok(());
|
.await
|
||||||
}
|
.scan(user_reactions, |set, reaction| async move {
|
||||||
if reaction.user(&self.ctx)?.bot {
|
let (reaction, is_add) = match &*reaction {
|
||||||
return Ok(());
|
ReactionAction::Added(r) => (r, true),
|
||||||
}
|
ReactionAction::Removed(r) => (r, false),
|
||||||
|
};
|
||||||
let users = if let ReactionType::Unicode(ref s) = reaction.emoji {
|
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
|
users
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return None;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return None;
|
||||||
|
};
|
||||||
|
let user_id = match reaction.user_id {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return None,
|
||||||
};
|
};
|
||||||
if is_add {
|
if is_add {
|
||||||
users.insert(reaction.user_id);
|
users.insert(user_id);
|
||||||
} else {
|
} else {
|
||||||
users.remove(&reaction.user_id);
|
users.remove(&user_id);
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Some(())
|
||||||
|
})
|
||||||
|
.collect::<()>()
|
||||||
|
.await;
|
||||||
|
|
||||||
ctx.data
|
// Handle choices
|
||||||
.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);
|
|
||||||
let choice_map = choices.into_iter().collect::<Map<_, _>>();
|
let choice_map = choices.into_iter().collect::<Map<_, _>>();
|
||||||
let result: Vec<(String, Vec<UserId>)> = user_reactions
|
let result: Vec<(String, Vec<UserId>)> = user_reactions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -169,8 +164,10 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
.push(", sorry 😭")
|
.push(", sorry 😭")
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
.ok();
|
.await?;
|
||||||
} else {
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
channel
|
channel
|
||||||
.send_message(&ctx, |c| {
|
.send_message(&ctx, |c| {
|
||||||
c.content({
|
c.content({
|
||||||
|
@ -201,11 +198,8 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
content.build()
|
content.build()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.ok();
|
.await?;
|
||||||
}
|
panel.delete(&ctx).await?;
|
||||||
panel.delete(&ctx).ok();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
// unimplemented!();
|
// unimplemented!();
|
||||||
|
|
Loading…
Add table
Reference in a new issue