From a2f8d36a3747a1a07212ed4af5d3fb857687e96a Mon Sep 17 00:00:00 2001 From: NatrixAeria Date: Sun, 27 Sep 2020 20:58:35 +0200 Subject: Cache channels --- src/config.rs | 22 +++++++-- src/main.rs | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 148 insertions(+), 23 deletions(-) diff --git a/src/config.rs b/src/config.rs index cd8a55b..fd00c24 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,20 @@ +macro_rules! bot_name { + () => { + "Cupido" + }; +} + pub const TOKEN_ENV: &str = "DISCORD_TOKEN"; pub const GLOBAL_COMMAND_PREFIX: &str = "!<3"; -pub const META_CHANNEL_NAME: &str = "Cupido Shuffler"; -pub const META_CHANNEL_DESCRIPTION: &str = "All channels related to cupido"; -pub const HELP_TEXT: &str = "Cupido is a discord bot to get to know your people. -Cupido is open-source !"; +pub const META_CHANNEL_NAME: &str = concat!(bot_name!(), " Shuffler"); +pub const META_CHANNEL_DESCRIPTION: &str = concat!("All channels related to ", bot_name!()); +pub const LOBBY_CHANNEL_NAME: &str = "lobby"; +pub const LOBBY_CHANNEL_DESCRIPTION: &str = "You wanna get shuffled? You're at the right place!"; +pub const HELP_TEXT: &str = concat!( + bot_name!(), + " - your partner for getting shuffled. +", + bot_name!(), + " is a discord bot to get to know your people. +This software is open-source !" +); diff --git a/src/main.rs b/src/main.rs index 455da50..a3c0222 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,12 @@ +#![feature(once_cell)] + mod config; use serenity::framework::StandardFramework; +use serenity::model::id::ChannelId; use serenity::Client; +use std::lazy::SyncLazy; +use std::sync::{Arc, Mutex}; fn configure(conf: &mut serenity::framework::standard::Configuration) { conf.prefix(config::GLOBAL_COMMAND_PREFIX) @@ -26,20 +31,32 @@ impl std::fmt::Display for BotError { impl std::error::Error for BotError {} +#[derive(Debug, Clone, Default)] +pub struct ChannelCache { + pub meta: Option, + pub lobby: Option, +} + +static CHANNEL_CACHE: SyncLazy>> = + SyncLazy::new(|| Arc::new(Mutex::new(Default::default()))); + mod commands { + use super::{ChannelCache, CHANNEL_CACHE}; use crate::config; use serenity::client::Context; use serenity::framework::standard::{macros::*, CommandError, CommandResult}; use serenity::model::{ - channel::{ChannelType, GuildChannel, Message}, + channel::{Channel, ChannelType, GuildChannel, Message}, guild::Guild, id::ChannelId, }; + #[group] - #[description = "Commands for the cupido bot"] + #[description("Commands for this bot")] #[commands(help, create)] struct Group; + #[derive(Debug, Clone)] struct ChannelDescriptor<'n, 'd> { name: &'n str, description: &'d str, @@ -70,22 +87,116 @@ mod commands { .await?) } + async fn get_guild_channel_by_optional_id( + ctx: &Context, + id: Option, + ) -> Option { + match id { + Some(id) => ctx.cache.guild_channel(id).await, + None => None, + } + } + + async fn get_channel_by_optional_id( + ctx: &Context, + kind: ChannelType, + id: Option, + ) -> Option { + match (kind, id) { + (ChannelType::Category, Some(id)) => ctx + .cache + .categories() + .await + .get(&id) + .cloned() + .map(Channel::Category), + (_, opt_id) => get_guild_channel_by_optional_id(ctx, opt_id) + .await + .map(Channel::Guild), + } + } + async fn create_channel<'n, 'd>( ctx: &Context, guild: &Guild, desc: ChannelDescriptor<'n, 'd>, - ) -> Result { - let channel = guild.channel_id_from_name(&ctx.cache, desc.name).await; - let optional_channel = match channel { - Some(channel) => ctx.cache.guild_channel(channel).await, - None => None, + ) -> Result { + let optional_channel = match desc.kind { + ChannelType::Category => { + println!("{:?}", ctx.cache.categories().await); + ctx.cache.categories().await.values().find_map(|channel| { + if channel.name() == desc.name { + Some(Channel::Category(channel.clone())) + } else { + None + } + }) + } + _ => get_guild_channel_by_optional_id( + ctx, + guild.channel_id_from_name(&ctx.cache, desc.name).await, + ) + .await + .map(Channel::Guild), }; match optional_channel { Some(channel) => Ok(channel), - None => create_channel_no_cache(ctx, guild, desc).await, + None => { + let channel = create_channel_no_cache(ctx, guild, desc) + .await + .map(Channel::Guild); + channel + } } } + async fn get_channel_or_create<'n, 'd, F: Fn(&mut ChannelCache) -> &mut Option>( + ctx: &Context, + guild: &Guild, + desc: ChannelDescriptor<'n, 'd>, + f: F, + ) -> Result { + let lock = || CHANNEL_CACHE.lock().unwrap(); + Ok({ + let optional_id = *f(&mut *lock()); + match get_channel_by_optional_id(ctx, desc.kind, optional_id).await { + Some(channel) => channel, + None => { + let channel = create_channel(ctx, &guild, desc).await?; + *f(&mut *lock()) = Some(channel.id()); + channel + } + } + }) + } + + async fn get_meta_channel_or_create( + ctx: &Context, + guild: &Guild, + ) -> Result { + let desc = ChannelDescriptor { + name: config::META_CHANNEL_NAME, + description: config::META_CHANNEL_DESCRIPTION, + kind: ChannelType::Category, + parent: None, + }; + get_channel_or_create(ctx, guild, desc, |cache| &mut cache.meta).await + } + + async fn get_lobby_channel_or_create( + ctx: &Context, + guild: &Guild, + meta_channel_id: ChannelId, + ) -> Result { + let desc = ChannelDescriptor { + name: config::LOBBY_CHANNEL_NAME, + description: config::LOBBY_CHANNEL_DESCRIPTION, + kind: ChannelType::Voice, + parent: Some(meta_channel_id), + }; + get_channel_or_create(ctx, guild, desc, |cache| &mut cache.lobby).await + } + #[command] #[aliases("hepl", "?", "h")] async fn help(ctx: &Context, msg: &Message) -> CommandResult { @@ -97,17 +208,17 @@ mod commands { #[aliases("craete", "+", "c", "init")] async fn create(ctx: &Context, msg: &Message) -> CommandResult { let guild = get_guild(ctx, msg).await?; - let meta_channel = create_channel( - ctx, - &guild, - ChannelDescriptor { - name: config::META_CHANNEL_NAME, - description: config::META_CHANNEL_DESCRIPTION, - kind: ChannelType::Category, - parent: None, - }, - ) - .await?; + let meta_channel = get_meta_channel_or_create(ctx, &guild).await?; + + let lobby_channel = get_lobby_channel_or_create(ctx, &guild, meta_channel.id()).await?; + + Ok(()) + } + + #[command] + #[aliases("strat", "s", "r", "run", "rnu")] + async fn start(ctx: &Context, msg: &Message) -> CommandResult { + let guild = get_guild(ctx, msg).await?; Ok(()) } -- cgit v1.2.3-54-g00ecf