mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-18 16:28:55 +00:00
fun module (#2)
This commit is contained in:
parent
384b7be52c
commit
ea7c6d8072
8 changed files with 2329 additions and 223 deletions
532
Cargo.lock
generated
532
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -11,6 +11,8 @@ serenity = "0.7"
|
||||||
dotenv = "0.15"
|
dotenv = "0.15"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
chrono = "0.4.9"
|
chrono = "0.4.9"
|
||||||
|
rand = "0.7.2"
|
||||||
|
static_assertions = "1.1.0"
|
||||||
|
|
||||||
[dependencies.rustbreak]
|
[dependencies.rustbreak]
|
||||||
version = "2.0.0-rc3"
|
version = "2.0.0-rc3"
|
||||||
|
|
|
@ -2,7 +2,8 @@ pub use duration::Duration;
|
||||||
|
|
||||||
mod duration {
|
mod duration {
|
||||||
use chrono::Duration as StdDuration;
|
use chrono::Duration as StdDuration;
|
||||||
use serenity::framework::standard::CommandError as Error;
|
use std::fmt;
|
||||||
|
use String as Error;
|
||||||
// Parse a single duration unit
|
// Parse a single duration unit
|
||||||
fn parse_duration_string(s: &str) -> Result<StdDuration, Error> {
|
fn parse_duration_string(s: &str) -> Result<StdDuration, Error> {
|
||||||
// We reject the empty case
|
// We reject the empty case
|
||||||
|
@ -28,12 +29,12 @@ mod duration {
|
||||||
(item, Some(v)) => Ok(ParseStep {
|
(item, Some(v)) => Ok(ParseStep {
|
||||||
current_value: None,
|
current_value: None,
|
||||||
current_duration: s.current_duration
|
current_duration: s.current_duration
|
||||||
+ match item {
|
+ match item.to_ascii_lowercase() {
|
||||||
's' => StdDuration::seconds,
|
's' => StdDuration::seconds,
|
||||||
'm' => StdDuration::minutes,
|
'm' => StdDuration::minutes,
|
||||||
'h' => StdDuration::hours,
|
'h' => StdDuration::hours,
|
||||||
'D' => StdDuration::days,
|
'd' => StdDuration::days,
|
||||||
'W' => StdDuration::weeks,
|
'w' => StdDuration::weeks,
|
||||||
_ => return Err(Error::from("Not a valid duration")),
|
_ => return Err(Error::from("Not a valid duration")),
|
||||||
}(v as i64),
|
}(v as i64),
|
||||||
}),
|
}),
|
||||||
|
@ -64,6 +65,40 @@ mod duration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Duration {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let d = &self.0;
|
||||||
|
// weeks
|
||||||
|
let weeks = d.num_weeks();
|
||||||
|
let days = d.num_days() - d.num_weeks() * 7;
|
||||||
|
let hours = d.num_hours() - d.num_days() * 24;
|
||||||
|
let minutes = d.num_minutes() - d.num_hours() * 60;
|
||||||
|
let seconds = d.num_seconds() - d.num_minutes() * 60;
|
||||||
|
let formats = [
|
||||||
|
(weeks, "week"),
|
||||||
|
(days, "day"),
|
||||||
|
(hours, "hour"),
|
||||||
|
(minutes, "minute"),
|
||||||
|
(seconds, "second"),
|
||||||
|
];
|
||||||
|
let mut first = true;
|
||||||
|
for (val, counter) in formats.into_iter() {
|
||||||
|
if *val > 0 {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}{} {}{}",
|
||||||
|
(if first { "" } else { " " }),
|
||||||
|
val,
|
||||||
|
counter,
|
||||||
|
(if *val == 1 { "" } else { "s" })
|
||||||
|
)?;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
182
youmubot/src/commands/fun/mod.rs
Normal file
182
youmubot/src/commands/fun/mod.rs
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
use rand::{
|
||||||
|
distributions::{Distribution, Uniform},
|
||||||
|
thread_rng,
|
||||||
|
};
|
||||||
|
use serenity::prelude::*;
|
||||||
|
use serenity::{
|
||||||
|
framework::standard::{
|
||||||
|
macros::{command, group},
|
||||||
|
Args, CommandResult,
|
||||||
|
},
|
||||||
|
model::{channel::Message, id::UserId},
|
||||||
|
utils::MessageBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod names;
|
||||||
|
mod votes;
|
||||||
|
|
||||||
|
use votes::VOTE_COMMAND;
|
||||||
|
|
||||||
|
group!({
|
||||||
|
name: "fun",
|
||||||
|
options: {
|
||||||
|
prefixes: ["fun", "f"],
|
||||||
|
description: "Random commands",
|
||||||
|
},
|
||||||
|
commands: [roll, pick, name, vote],
|
||||||
|
});
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
#[description = "🎲 Rolls a dice that gives you a random number."]
|
||||||
|
#[min_args(0)]
|
||||||
|
#[max_args(2)]
|
||||||
|
#[usage = "[max-dice-faces = 6] / [message]"]
|
||||||
|
#[example = "100 / What's my score?"]
|
||||||
|
fn roll(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
|
let dice = if args.is_empty() {
|
||||||
|
6
|
||||||
|
} else {
|
||||||
|
args.single::<u64>()?
|
||||||
|
};
|
||||||
|
|
||||||
|
if dice == 0 {
|
||||||
|
msg.reply(&ctx, "Give me a dice with 0 faces, what do you expect 😒")?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = {
|
||||||
|
let dice_rng = Uniform::from(1..=dice);
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
dice_rng.sample(&mut rng)
|
||||||
|
};
|
||||||
|
|
||||||
|
match args.single_quoted::<String>() {
|
||||||
|
Ok(s) => msg.reply(
|
||||||
|
&ctx,
|
||||||
|
MessageBuilder::new()
|
||||||
|
.push("you asked ")
|
||||||
|
.push_bold_safe(s)
|
||||||
|
.push(format!(
|
||||||
|
", so I rolled a 🎲 of **{}** faces, and got **{}**!",
|
||||||
|
dice, result
|
||||||
|
))
|
||||||
|
.build(),
|
||||||
|
),
|
||||||
|
Err(_) if args.is_empty() => msg.reply(
|
||||||
|
&ctx,
|
||||||
|
format!(
|
||||||
|
"I rolled a 🎲 of **{}** faces, and got **{}**!",
|
||||||
|
dice, result
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
#[description = r#"👈 Pick a choice from the available list of choices.
|
||||||
|
You may prefix the first choice with `?` to make it a question!
|
||||||
|
If no choices are given, Youmu defaults to `Yes!` and `No!`"#]
|
||||||
|
#[usage = "[?question]/[choice #1]/[choice #2]/..."]
|
||||||
|
#[example = "?What for dinner/Pizza/Hamburger"]
|
||||||
|
fn pick(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
|
let (question, choices) = {
|
||||||
|
// Get a list of options.
|
||||||
|
let mut choices = args
|
||||||
|
.quoted()
|
||||||
|
.trimmed()
|
||||||
|
.iter::<String>()
|
||||||
|
.map(|v| v.unwrap())
|
||||||
|
.peekable();
|
||||||
|
// If we have the first argument as question, use it.
|
||||||
|
let question = match choices.peek() {
|
||||||
|
Some(ref q) if q.starts_with("?") => Some(q.replacen("?", "", 1) + "?"),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
// If we have a question, that's not a choice.
|
||||||
|
let mut choices = match question {
|
||||||
|
Some(_) => {
|
||||||
|
choices.next();
|
||||||
|
choices
|
||||||
|
}
|
||||||
|
None => choices,
|
||||||
|
};
|
||||||
|
// If there are no choices, default to Yes! and No!
|
||||||
|
let choices = match choices.peek() {
|
||||||
|
None => vec!["Yes!".to_owned(), "No!".to_owned()],
|
||||||
|
_ => choices.collect(),
|
||||||
|
};
|
||||||
|
(question, choices)
|
||||||
|
};
|
||||||
|
|
||||||
|
let choice = {
|
||||||
|
let uniform = Uniform::from(0..choices.len());
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
&choices[uniform.sample(&mut rng)]
|
||||||
|
};
|
||||||
|
|
||||||
|
match question {
|
||||||
|
None => msg.reply(
|
||||||
|
&ctx,
|
||||||
|
MessageBuilder::new()
|
||||||
|
.push("Youmu picks 👉")
|
||||||
|
.push_bold_safe(choice)
|
||||||
|
.push("👈!")
|
||||||
|
.build(),
|
||||||
|
),
|
||||||
|
Some(s) => msg.reply(
|
||||||
|
&ctx,
|
||||||
|
MessageBuilder::new()
|
||||||
|
.push("you asked ")
|
||||||
|
.push_bold_safe(s)
|
||||||
|
.push(", and Youmu picks 👉")
|
||||||
|
.push_bold_safe(choice)
|
||||||
|
.push("👈!")
|
||||||
|
.build(),
|
||||||
|
),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
#[description = "Wanna know what your name is in Japanese🇯🇵?"]
|
||||||
|
#[usage = "[user_mention = yourself]"]
|
||||||
|
#[example = "@user#1234"]
|
||||||
|
#[max_args(1)]
|
||||||
|
fn name(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
|
let user_id = if args.is_empty() {
|
||||||
|
msg.author.id
|
||||||
|
} else {
|
||||||
|
args.single::<UserId>()?
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_mention = if user_id == msg.author.id {
|
||||||
|
"your".to_owned()
|
||||||
|
} else {
|
||||||
|
MessageBuilder::new()
|
||||||
|
.push_bold_safe(user_id.to_user(&ctx)?.tag())
|
||||||
|
.push("'s")
|
||||||
|
.build()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rule out a couple of cases
|
||||||
|
if user_id == ctx.http.get_current_application_info()?.id {
|
||||||
|
// This is my own user_id
|
||||||
|
msg.reply(&ctx, "😠 My name is **Youmu Konpaku**!")?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (first_name, last_name) = names::name_from_userid(user_id);
|
||||||
|
|
||||||
|
msg.reply(
|
||||||
|
&ctx,
|
||||||
|
format!(
|
||||||
|
"{} Japanese🇯🇵 name is **{} {}**!",
|
||||||
|
user_mention, first_name, last_name
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
1547
youmubot/src/commands/fun/names.rs
Normal file
1547
youmubot/src/commands/fun/names.rs
Normal file
File diff suppressed because it is too large
Load diff
235
youmubot/src/commands/fun/votes.rs
Normal file
235
youmubot/src/commands/fun/votes.rs
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
use crate::commands::args::Duration as ParseDuration;
|
||||||
|
use chrono::Duration;
|
||||||
|
use serenity::framework::standard::CommandError as Error;
|
||||||
|
use serenity::prelude::*;
|
||||||
|
use serenity::{
|
||||||
|
framework::standard::{macros::command, Args, CommandResult},
|
||||||
|
model::{
|
||||||
|
channel::{Message, MessageReaction, ReactionType},
|
||||||
|
id::UserId,
|
||||||
|
},
|
||||||
|
utils::MessageBuilder,
|
||||||
|
};
|
||||||
|
use std::collections::HashMap as Map;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
#[description = "🎌 Cast a poll upon everyone and ask them for opinions!"]
|
||||||
|
#[usage = "[duration] / [question] / [answer #1 = Yes!] / [answer #2 = No!] ..."]
|
||||||
|
#[example = "2m/How early do you get up?/Before 6/Before 7/Before 8/Fuck time"]
|
||||||
|
#[bucket = "voting"]
|
||||||
|
#[only_in(guilds)]
|
||||||
|
#[min_args(2)]
|
||||||
|
pub fn vote(ctx: &mut 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::minutes(2) || *duration > Duration::days(1) {
|
||||||
|
msg.reply(ctx, format!("😒 Invalid duration ({}). The voting time should be between **2 minutes** and **1 day**.", _duration))?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let question = args.single::<String>()?;
|
||||||
|
let (choices, reactions) = if args.is_empty() {
|
||||||
|
(
|
||||||
|
vec!["Yes! 😍".to_owned(), "No! 🤢".to_owned()],
|
||||||
|
vec!["😍", "🤢"],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let choices: Vec<_> = args.iter().map(|v| v.unwrap()).collect();
|
||||||
|
if choices.len() > MAX_CHOICES {
|
||||||
|
if choices.len() < 2 {
|
||||||
|
// Where are the choices?
|
||||||
|
msg.reply(
|
||||||
|
ctx,
|
||||||
|
"😒 Can't have a nice voting session if you only have one choice.",
|
||||||
|
)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
// Too many choices!
|
||||||
|
msg.reply(
|
||||||
|
ctx,
|
||||||
|
format!(
|
||||||
|
"😵 Too many choices... We only support {} choices at the moment!",
|
||||||
|
MAX_CHOICES
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let reactions = pick_n_reactions(choices.len())?;
|
||||||
|
(choices, reactions)
|
||||||
|
};
|
||||||
|
|
||||||
|
let fields: Vec<_> = {
|
||||||
|
choices
|
||||||
|
.iter()
|
||||||
|
.zip(reactions.iter())
|
||||||
|
.map(|(choice, reaction)| {
|
||||||
|
(
|
||||||
|
MessageBuilder::new().push_bold_safe(choice).build(),
|
||||||
|
format!("React with {}", reaction),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ok... now we post up a nice voting panel.
|
||||||
|
let channel = msg.channel_id;
|
||||||
|
let author = &msg.author;
|
||||||
|
let panel = channel.send_message(&ctx, |c| {
|
||||||
|
c.content("@here").embed(|e| {
|
||||||
|
e.author(|au| {
|
||||||
|
au.icon_url(author.avatar_url().unwrap_or("".to_owned()))
|
||||||
|
.name(&author.name)
|
||||||
|
})
|
||||||
|
.title(format!("You have {} to vote!", _duration))
|
||||||
|
.thumbnail("https://images-ext-2.discordapp.net/external/BK7injOyt4XT8yNfbCDV4mAkwoRy49YPfq-3IwCc_9M/http/cdn.i.ntere.st/p/9197498/image")
|
||||||
|
.description(MessageBuilder::new().push_bold_line_safe(&question).push("\nThis question was asked by ").push(author.mention()))
|
||||||
|
.fields(fields.into_iter())
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
msg.delete(&ctx)?;
|
||||||
|
// React on all the choices
|
||||||
|
reactions.iter().try_for_each(|v| panel.react(&ctx, *v))?;
|
||||||
|
|
||||||
|
// Start sleeping
|
||||||
|
thread::sleep(duration.to_std()?);
|
||||||
|
|
||||||
|
let result = collect_reactions(ctx, panel, &reactions, &choices)?;
|
||||||
|
if result.len() == 0 {
|
||||||
|
msg.reply(
|
||||||
|
&ctx,
|
||||||
|
MessageBuilder::new()
|
||||||
|
.push("no one answer your question ")
|
||||||
|
.push_bold_safe(&question)
|
||||||
|
.push(", sorry 😭")
|
||||||
|
.build(),
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
channel.send_message(&ctx, |c| {
|
||||||
|
c.content({
|
||||||
|
let mut content = MessageBuilder::new();
|
||||||
|
content
|
||||||
|
.push("@here, ")
|
||||||
|
.push(author.mention())
|
||||||
|
.push(" previously asked ")
|
||||||
|
.push_bold_safe(&question)
|
||||||
|
.push(", and here are the results!");
|
||||||
|
result.iter().for_each(|(choice, votes)| {
|
||||||
|
content
|
||||||
|
.push("\n - ")
|
||||||
|
.push_bold(format!("{}", votes.len()))
|
||||||
|
.push(" voted for ")
|
||||||
|
.push_bold_safe(choice)
|
||||||
|
.push(": ")
|
||||||
|
.push(
|
||||||
|
votes
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.mention())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
content.build()
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
msg.delete(&ctx)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
// unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect reactions and store them as a map from choice to
|
||||||
|
fn collect_reactions<'a>(
|
||||||
|
ctx: &mut Context,
|
||||||
|
msg: Message,
|
||||||
|
reaction_emojis: &[&'static str],
|
||||||
|
choices: &'a [String],
|
||||||
|
) -> Result<Vec<(&'a str, Vec<UserId>)>, Error> {
|
||||||
|
// Get a brand new version of the Message
|
||||||
|
let reactions = msg.channel_id.message(&ctx, msg.id)?.reactions;
|
||||||
|
let reaction_to_choice: Map<_, _> = reaction_emojis
|
||||||
|
.into_iter()
|
||||||
|
.zip(choices.into_iter())
|
||||||
|
.collect();
|
||||||
|
let result: Result<Vec<_>, Error> = {
|
||||||
|
let mut vec: Vec<(&str, Vec<UserId>)> = Vec::new();
|
||||||
|
reactions
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|r| {
|
||||||
|
if let ReactionType::Unicode(ref v) = r.reaction_type {
|
||||||
|
reaction_to_choice
|
||||||
|
.get(&&v[..])
|
||||||
|
.cloned()
|
||||||
|
.filter(|_| r.count > 1)
|
||||||
|
.map(|choice| (r.clone(), choice))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.try_for_each(|(r, choice)| -> Result<_, Error> {
|
||||||
|
let users = collect_reaction_users(ctx, &msg, &r)?;
|
||||||
|
vec.push((choice, users));
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
vec.sort_by(|(_, b): &(_, Vec<_>), (_, d)| d.len().cmp(&b.len()));
|
||||||
|
Ok(vec)
|
||||||
|
};
|
||||||
|
let result = result?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_reaction_users(
|
||||||
|
ctx: &mut Context,
|
||||||
|
msg: &Message,
|
||||||
|
reaction: &MessageReaction,
|
||||||
|
) -> Result<Vec<UserId>, Error> {
|
||||||
|
let mut res = Vec::with_capacity(reaction.count as usize);
|
||||||
|
(0..reaction.count)
|
||||||
|
.step_by(100)
|
||||||
|
.try_for_each(|_| -> Result<_, Error> {
|
||||||
|
let user_ids = msg
|
||||||
|
.reaction_users(
|
||||||
|
&ctx,
|
||||||
|
reaction.reaction_type.clone(),
|
||||||
|
Some(100),
|
||||||
|
res.last().cloned(),
|
||||||
|
)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| i.id);
|
||||||
|
res.extend(user_ids);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
// Pick a set of random n reactions!
|
||||||
|
fn pick_n_reactions(n: usize) -> Result<Vec<&'static str>, Error> {
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
if n > MAX_CHOICES {
|
||||||
|
Err(Error::from("Too many options"))
|
||||||
|
} else {
|
||||||
|
let mut rand = rand::thread_rng();
|
||||||
|
Ok(REACTIONS
|
||||||
|
.choose_multiple(&mut rand, n)
|
||||||
|
.map(|v| *v)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_CHOICES: usize = 15;
|
||||||
|
|
||||||
|
// All the defined reactions.
|
||||||
|
const REACTIONS: [&'static str; 90] = [
|
||||||
|
"😀", "😁", "😂", "🤣", "😃", "😄", "😅", "😆", "😉", "😊", "😋", "😎", "😍", "😘", "🥰", "😗",
|
||||||
|
"😙", "😚", "☺️", "🙂", "🤗", "🤩", "🤔", "🤨", "😐", "😑", "😶", "🙄", "😏", "😣", "😥", "😮",
|
||||||
|
"🤐", "😯", "😪", "😫", "😴", "😌", "😛", "😜", "😝", "🤤", "😒", "😓", "😔", "😕", "🙃", "🤑",
|
||||||
|
"😲", "☹️", "🙁", "😖", "😞", "😟", "😤", "😢", "😭", "😦", "😧", "😨", "😩", "🤯", "😬", "😰",
|
||||||
|
"😱", "🥵", "🥶", "😳", "🤪", "😵", "😡", "😠", "🤬", "😷", "🤒", "🤕", "🤢", "🤮", "🤧", "😇",
|
||||||
|
"🤠", "🤡", "🥳", "🥴", "🥺", "🤥", "🤫", "🤭", "🧐", "🤓",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
static_assertions::const_assert!(MAX_CHOICES <= REACTIONS.len());
|
|
@ -7,10 +7,13 @@ use serenity::{
|
||||||
};
|
};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
pub mod admin;
|
|
||||||
mod args;
|
mod args;
|
||||||
|
|
||||||
|
pub mod admin;
|
||||||
|
pub mod fun;
|
||||||
|
|
||||||
pub use admin::ADMIN_GROUP;
|
pub use admin::ADMIN_GROUP;
|
||||||
|
pub use fun::FUN_GROUP;
|
||||||
|
|
||||||
// A help command
|
// A help command
|
||||||
#[help]
|
#[help]
|
||||||
|
|
|
@ -96,8 +96,12 @@ fn setup_framework(mut client: Client) -> Client {
|
||||||
.normal_message(|_, message| {
|
.normal_message(|_, message| {
|
||||||
println!("Message is not a command '{}'", message.content);
|
println!("Message is not a command '{}'", message.content);
|
||||||
})
|
})
|
||||||
|
.bucket("voting", |c| {
|
||||||
|
c.delay(120 /* 2 minutes */).time_span(120).limit(1)
|
||||||
|
})
|
||||||
// groups here
|
// groups here
|
||||||
.group(&commands::ADMIN_GROUP),
|
.group(&commands::ADMIN_GROUP)
|
||||||
|
.group(&commands::FUN_GROUP),
|
||||||
);
|
);
|
||||||
client
|
client
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue