youmubot/youmubot-core/src/community/mod.rs
Natsu Kagami bd845d9662
More osu improvements (#11)
* Don't show DT when NC is on

* Allow leading + in mods parsing

* Extend Pagination

* Implement beatmapset display

* Move OkPrint to prelude

* Beatmapset display seems to work

* Put reaction handler into static

* Make clippy happy

* Add beatmapset caching and last --set

* Delay loading of beatmap info

* Simplify hook link handling

* Replies everywhere!

* Replies everywhereee!
2021-02-01 17:13:40 +00:00

122 lines
3.8 KiB
Rust

use rand::{
distributions::{Distribution, Uniform},
thread_rng,
};
use serenity::{
framework::standard::{
macros::{command, group},
Args, CommandError as Error, CommandResult,
},
model::{
channel::{Channel, Message},
id::RoleId,
user::OnlineStatus,
},
utils::MessageBuilder,
};
use youmubot_prelude::*;
mod roles;
mod votes;
use roles::{ADD_COMMAND, LIST_COMMAND, REMOVE_COMMAND, TOGGLE_COMMAND};
use votes::VOTE_COMMAND;
#[group]
#[description = "Community related commands. Usually comes with some sort of delays, since it involves pinging"]
#[only_in("guilds")]
#[commands(choose, vote, add, list, remove, toggle)]
struct Community;
#[command]
#[description = r"👑 Randomly choose an active member and mention them!
Note that only online/idle users in the channel are chosen from."]
#[usage = "[limited roles = everyone online] / [title = the chosen one]"]
#[example = "the strongest in Gensokyo"]
#[bucket = "community"]
#[max_args(2)]
pub async fn choose(ctx: &Context, m: &Message, mut args: Args) -> CommandResult {
let role = args.find::<RoleId>().ok();
let title = if args.is_empty() {
"the chosen one".to_owned()
} else {
args.single::<String>()?
};
let users: Result<Vec<_>, Error> = {
let guild = m.guild(&ctx).await.unwrap();
let presences = &guild.presences;
let channel = m.channel_id.to_channel(&ctx).await?;
if let Channel::Guild(channel) = channel {
Ok(channel
.members(&ctx)
.await?
.into_iter()
.filter(|v| !v.user.bot) // Filter out bots
.filter(|v| {
// Filter out only online people
presences
.get(&v.user.id)
.map(|presence| {
presence.status == OnlineStatus::Online
|| presence.status == OnlineStatus::Idle
})
.unwrap_or(false)
})
.map(|mem| future::ready(mem))
.collect::<stream::FuturesUnordered<_>>()
.filter_map(|member| async move {
// Filter by role if provided
match role {
Some(role) if member.roles.iter().any(|r| role == *r) => Some(member),
None => Some(member),
_ => None,
}
})
.collect()
.await)
} else {
unreachable!()
}
};
let users = users?;
if users.len() < 2 {
m.reply(
&ctx,
"🍰 Have this cake for yourself because no-one is here for the gods to pick.",
)
.await?;
return Ok(());
}
let winner = {
let uniform = Uniform::from(0..users.len());
let mut rng = thread_rng();
&users[uniform.sample(&mut rng)]
};
m.channel_id
.send_message(&ctx, |c| {
c.content(
MessageBuilder::new()
.push("👑 The Gensokyo gods have gathered around and decided, out of ")
.push_bold(format!("{}", users.len()))
.push(" ")
.push(
role.map(|r| format!("{}s", r.mention()))
.unwrap_or("potential prayers".to_owned()),
)
.push(", ")
.push(winner.mention())
.push(" will be ")
.push_bold_safe(title)
.push(". Congrats! 🎉 🎊 🥳")
.build(),
)
.reference_message(m)
})
.await?;
Ok(())
}