mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-18 16:28:55 +00:00
Move the prelude into a seperate package
This commit is contained in:
parent
d5f7a17a2c
commit
03be1a4acc
20 changed files with 88 additions and 44 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -1689,6 +1689,7 @@ dependencies = [
|
||||||
"static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"youmubot-db 0.1.0",
|
"youmubot-db 0.1.0",
|
||||||
"youmubot-osu 0.1.0",
|
"youmubot-osu 0.1.0",
|
||||||
|
"youmubot-prelude 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1715,6 +1716,15 @@ dependencies = [
|
||||||
"serenity 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serenity 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "youmubot-prelude"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serenity 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"youmubot-db 0.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
|
"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
|
||||||
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
|
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
|
"youmubot-prelude",
|
||||||
"youmubot-db",
|
"youmubot-db",
|
||||||
"youmubot-osu",
|
"youmubot-osu",
|
||||||
"youmubot",
|
"youmubot",
|
||||||
|
|
12
youmubot-prelude/Cargo.toml
Normal file
12
youmubot-prelude/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "youmubot-prelude"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Natsu Kagami <natsukagami@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serenity = "0.8"
|
||||||
|
youmubot-db = { path = "../youmubot-db" }
|
||||||
|
reqwest = "0.10"
|
|
@ -1,14 +1,16 @@
|
||||||
use crate::db::AnnouncerChannels;
|
use crate::AppData;
|
||||||
use crate::prelude::*;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{CommandError as Error, CommandResult},
|
framework::standard::{CommandError as Error, CommandResult},
|
||||||
http::{CacheHttp, Http},
|
http::{CacheHttp, Http},
|
||||||
model::id::{ChannelId, GuildId, UserId},
|
model::id::{ChannelId, GuildId, UserId},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::{HashMap, HashSet},
|
||||||
thread::{spawn, JoinHandle},
|
thread::{spawn, JoinHandle},
|
||||||
};
|
};
|
||||||
|
use youmubot_db::DB;
|
||||||
|
|
||||||
|
pub(crate) type AnnouncerChannels = DB<HashMap<String, HashMap<GuildId, ChannelId>>>;
|
||||||
|
|
||||||
pub trait Announcer {
|
pub trait Announcer {
|
||||||
fn announcer_key() -> &'static str;
|
fn announcer_key() -> &'static str;
|
|
@ -1,24 +1,29 @@
|
||||||
use std::sync::Arc;
|
|
||||||
use youmubot_osu::Client as OsuHttpClient;
|
|
||||||
|
|
||||||
pub use serenity::prelude::*;
|
pub use serenity::prelude::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub mod announcer;
|
||||||
|
pub mod args;
|
||||||
|
pub mod setup;
|
||||||
|
|
||||||
|
pub use announcer::Announcer;
|
||||||
|
pub use args::Duration;
|
||||||
|
|
||||||
/// The global app data.
|
/// The global app data.
|
||||||
pub type AppData = Arc<RwLock<ShareMap>>;
|
pub type AppData = Arc<RwLock<ShareMap>>;
|
||||||
|
|
||||||
/// The HTTP client.
|
/// The HTTP client.
|
||||||
pub(crate) struct HTTPClient;
|
pub struct HTTPClient;
|
||||||
|
|
||||||
impl TypeMapKey for HTTPClient {
|
impl TypeMapKey for HTTPClient {
|
||||||
type Value = reqwest::blocking::Client;
|
type Value = reqwest::blocking::Client;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The osu! client.
|
/// The osu! client.
|
||||||
pub(crate) struct OsuClient;
|
// pub(crate) struct OsuClient;
|
||||||
|
|
||||||
impl TypeMapKey for OsuClient {
|
// impl TypeMapKey for OsuClient {
|
||||||
type Value = OsuHttpClient;
|
// type Value = OsuHttpClient;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// The TypeMap trait that allows TypeMaps to quickly get a clonable item.
|
/// The TypeMap trait that allows TypeMaps to quickly get a clonable item.
|
||||||
pub trait GetCloned {
|
pub trait GetCloned {
|
14
youmubot-prelude/src/setup.rs
Normal file
14
youmubot-prelude/src/setup.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use serenity::{framework::standard::StandardFramework, prelude::*};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
/// Set up the prelude libraries.
|
||||||
|
///
|
||||||
|
/// Panics on failure: Youmubot should *NOT* attempt to continue when this function fails.
|
||||||
|
pub fn setup_prelude(db_path: &Path, data: &mut ShareMap, _: &mut StandardFramework) {
|
||||||
|
// Setup the announcer DB.
|
||||||
|
crate::announcer::AnnouncerChannels::insert_into(data, db_path.join("announcers.yaml"))
|
||||||
|
.expect("Announcers DB set up");
|
||||||
|
|
||||||
|
data.insert::<crate::HTTPClient>(reqwest::blocking::Client::new())
|
||||||
|
.expect("Should be able to insert");
|
||||||
|
}
|
|
@ -19,4 +19,5 @@ lazy_static = "1"
|
||||||
youmubot-osu = { path = "../youmubot-osu" }
|
youmubot-osu = { path = "../youmubot-osu" }
|
||||||
rayon = "1.1"
|
rayon = "1.1"
|
||||||
youmubot-db = { path = "../youmubot-db" }
|
youmubot-db = { path = "../youmubot-db" }
|
||||||
|
youmubot-prelude = { path = "../youmubot-prelude" }
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use serenity::prelude::*;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
macros::{command, group},
|
macros::{command, group},
|
||||||
|
@ -11,6 +10,7 @@ use serenity::{
|
||||||
};
|
};
|
||||||
use soft_ban::{SOFT_BAN_COMMAND, SOFT_BAN_INIT_COMMAND};
|
use soft_ban::{SOFT_BAN_COMMAND, SOFT_BAN_INIT_COMMAND};
|
||||||
use std::{thread::sleep, time::Duration};
|
use std::{thread::sleep, time::Duration};
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
mod soft_ban;
|
mod soft_ban;
|
||||||
pub use soft_ban::watch_soft_bans;
|
pub use soft_ban::watch_soft_bans;
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::{
|
||||||
db::{ServerSoftBans, SoftBans},
|
db::{ServerSoftBans, SoftBans},
|
||||||
};
|
};
|
||||||
use chrono::offset::Utc;
|
use chrono::offset::Utc;
|
||||||
use serenity::prelude::*;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{macros::command, Args, CommandError as Error, CommandResult},
|
framework::standard::{macros::command, Args, CommandError as Error, CommandResult},
|
||||||
model::{
|
model::{
|
||||||
|
@ -12,6 +11,7 @@ use serenity::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[required_permissions(ADMINISTRATOR)]
|
#[required_permissions(ADMINISTRATOR)]
|
||||||
|
|
|
@ -2,7 +2,6 @@ use rand::{
|
||||||
distributions::{Distribution, Uniform},
|
distributions::{Distribution, Uniform},
|
||||||
thread_rng,
|
thread_rng,
|
||||||
};
|
};
|
||||||
use serenity::prelude::*;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
macros::{command, group},
|
macros::{command, group},
|
||||||
|
@ -14,6 +13,7 @@ use serenity::{
|
||||||
},
|
},
|
||||||
utils::MessageBuilder,
|
utils::MessageBuilder,
|
||||||
};
|
};
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
mod votes;
|
mod votes;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::commands::args::Duration as ParseDuration;
|
use crate::commands::args::Duration as ParseDuration;
|
||||||
use serenity::framework::standard::CommandError as Error;
|
use serenity::framework::standard::CommandError as Error;
|
||||||
use serenity::prelude::*;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{macros::command, Args, CommandResult},
|
framework::standard::{macros::command, Args, CommandResult},
|
||||||
model::{
|
model::{
|
||||||
|
@ -12,6 +11,7 @@ use serenity::{
|
||||||
use std::collections::HashMap as Map;
|
use std::collections::HashMap as Map;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[description = "🎌 Cast a poll upon everyone and ask them for opinions!"]
|
#[description = "🎌 Cast a poll upon everyone and ask them for opinions!"]
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serenity::framework::standard::CommandError as Error;
|
use serenity::framework::standard::CommandError as Error;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
|
@ -9,6 +8,7 @@ use serenity::{
|
||||||
model::channel::{Channel, Message},
|
model::channel::{Channel, Message},
|
||||||
};
|
};
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[checks(nsfw)]
|
#[checks(nsfw)]
|
||||||
|
|
|
@ -2,7 +2,6 @@ use rand::{
|
||||||
distributions::{Distribution, Uniform},
|
distributions::{Distribution, Uniform},
|
||||||
thread_rng,
|
thread_rng,
|
||||||
};
|
};
|
||||||
use serenity::prelude::*;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
macros::{command, group},
|
macros::{command, group},
|
||||||
|
@ -11,6 +10,7 @@ use serenity::{
|
||||||
model::{channel::Message, id::UserId},
|
model::{channel::Message, id::UserId},
|
||||||
utils::MessageBuilder,
|
utils::MessageBuilder,
|
||||||
};
|
};
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
mod images;
|
mod images;
|
||||||
mod names;
|
mod names;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use serenity::prelude::*;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
help_commands, macros::help, Args, CommandGroup, CommandResult, HelpOptions,
|
help_commands, macros::help, Args, CommandGroup, CommandResult, HelpOptions,
|
||||||
|
@ -6,9 +5,7 @@ use serenity::{
|
||||||
model::{channel::Message, id::UserId},
|
model::{channel::Message, id::UserId},
|
||||||
};
|
};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use youmubot_prelude::*;
|
||||||
mod announcer;
|
|
||||||
mod args;
|
|
||||||
|
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod community;
|
pub mod community;
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
use super::{embeds::score_embed, BeatmapWithMode};
|
use super::{embeds::score_embed, BeatmapWithMode};
|
||||||
use crate::{
|
use crate::db::{OsuSavedUsers, OsuUser};
|
||||||
commands::announcer::Announcer,
|
|
||||||
db::{OsuSavedUsers, OsuUser},
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{CommandError as Error, CommandResult},
|
framework::standard::{CommandError as Error, CommandResult},
|
||||||
|
@ -15,6 +11,7 @@ use youmubot_osu::{
|
||||||
request::{BeatmapRequestKind, UserID},
|
request::{BeatmapRequestKind, UserID},
|
||||||
Client as Osu,
|
Client as Osu,
|
||||||
};
|
};
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
/// Announce osu! top scores.
|
/// Announce osu! top scores.
|
||||||
pub struct OsuAnnouncer;
|
pub struct OsuAnnouncer;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
|
@ -11,6 +10,7 @@ use youmubot_osu::{
|
||||||
models::{Beatmap, Mode},
|
models::{Beatmap, Mode},
|
||||||
request::BeatmapRequestKind,
|
request::BeatmapRequestKind,
|
||||||
};
|
};
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
use super::embeds::{beatmap_embed, beatmapset_embed};
|
use super::embeds::{beatmap_embed, beatmapset_embed};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::db::{OsuSavedUsers, OsuUser};
|
use crate::db::{OsuSavedUsers, OsuUser};
|
||||||
use crate::prelude::*;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
macros::{command, group},
|
macros::{command, group},
|
||||||
|
@ -13,6 +12,7 @@ use youmubot_osu::{
|
||||||
models::{Beatmap, Mode, User},
|
models::{Beatmap, Mode, User},
|
||||||
request::{BeatmapRequestKind, UserID},
|
request::{BeatmapRequestKind, UserID},
|
||||||
};
|
};
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
mod announcer;
|
mod announcer;
|
||||||
mod cache;
|
mod cache;
|
||||||
|
|
|
@ -12,9 +12,6 @@ use std::path::PathBuf;
|
||||||
use youmubot_db::{GuildMap, DB};
|
use youmubot_db::{GuildMap, DB};
|
||||||
use youmubot_osu::models::{Beatmap, Mode};
|
use youmubot_osu::models::{Beatmap, Mode};
|
||||||
|
|
||||||
/// A map from announcer keys to guild IDs and to channels.
|
|
||||||
pub type AnnouncerChannels = DB<HashMap<String, GuildMap<ChannelId>>>;
|
|
||||||
|
|
||||||
/// A list of SoftBans for all servers.
|
/// A list of SoftBans for all servers.
|
||||||
pub type SoftBans = DB<GuildMap<ServerSoftBans>>;
|
pub type SoftBans = DB<GuildMap<ServerSoftBans>>;
|
||||||
|
|
||||||
|
@ -34,7 +31,7 @@ pub fn setup_db(client: &mut Client) -> Result<(), Error> {
|
||||||
SoftBans::insert_into(&mut *data, &path.join("soft_bans.yaml"))?;
|
SoftBans::insert_into(&mut *data, &path.join("soft_bans.yaml"))?;
|
||||||
OsuSavedUsers::insert_into(&mut *data, &path.join("osu_saved_users.yaml"))?;
|
OsuSavedUsers::insert_into(&mut *data, &path.join("osu_saved_users.yaml"))?;
|
||||||
OsuLastBeatmap::insert_into(&mut *data, &path.join("last_beatmaps.yaml"))?;
|
OsuLastBeatmap::insert_into(&mut *data, &path.join("last_beatmaps.yaml"))?;
|
||||||
AnnouncerChannels::insert_into(&mut *data, &path.join("announcers.yaml"))?;
|
// AnnouncerChannels::insert_into(&mut *data, &path.join("announcers.yaml"))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
use dotenv;
|
use dotenv;
|
||||||
use dotenv::var;
|
use dotenv::var;
|
||||||
use reqwest;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{DispatchError, StandardFramework},
|
framework::standard::{DispatchError, StandardFramework},
|
||||||
model::{channel::Message, gateway},
|
model::{channel::Message, gateway},
|
||||||
};
|
};
|
||||||
use youmubot_osu::Client as OsuApiClient;
|
use youmubot_osu::Client as OsuApiClient;
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod db;
|
mod db;
|
||||||
mod prelude;
|
|
||||||
|
|
||||||
use commands::osu::OsuAnnouncer;
|
use commands::osu::OsuAnnouncer;
|
||||||
use commands::Announcer;
|
|
||||||
use prelude::*;
|
|
||||||
|
|
||||||
const MESSAGE_HOOKS: [fn(&mut Context, &Message) -> (); 1] = [commands::osu::hook];
|
const MESSAGE_HOOKS: [fn(&mut Context, &Message) -> (); 1] = [commands::osu::hook];
|
||||||
|
|
||||||
|
@ -40,16 +37,30 @@ fn main() {
|
||||||
// Collect the token
|
// Collect the token
|
||||||
let token = var("TOKEN").expect("Please set TOKEN as the Discord Bot's token to be used.");
|
let token = var("TOKEN").expect("Please set TOKEN as the Discord Bot's token to be used.");
|
||||||
// Attempt to connect and set up a framework
|
// Attempt to connect and set up a framework
|
||||||
setup_framework(Client::new(token, Handler).expect("Cannot connect..."))
|
Client::new(token, Handler).expect("Cannot connect")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Set up base framework
|
||||||
|
let mut fw = setup_framework(&client);
|
||||||
|
|
||||||
|
// Setup each package starting from the prelude.
|
||||||
|
{
|
||||||
|
let mut data = client.data.write();
|
||||||
|
let db_path = var("DBPATH")
|
||||||
|
.map(|v| std::path::PathBuf::from(v))
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
println!("No DBPATH set up ({:?}), using `/data`", e);
|
||||||
|
std::path::PathBuf::from("data")
|
||||||
|
});
|
||||||
|
youmubot_prelude::setup::setup_prelude(&db_path, &mut data, &mut fw);
|
||||||
|
}
|
||||||
|
|
||||||
// Setup initial data
|
// Setup initial data
|
||||||
db::setup_db(&mut client).expect("Setup db should succeed");
|
db::setup_db(&mut client).expect("Setup db should succeed");
|
||||||
// Setup shared instances of things
|
// Setup shared instances of things
|
||||||
{
|
{
|
||||||
let mut data = client.data.write();
|
let mut data = client.data.write();
|
||||||
let http_client = reqwest::blocking::Client::new();
|
let http_client = data.get_cloned::<HTTPClient>();
|
||||||
data.insert::<HTTPClient>(http_client.clone());
|
|
||||||
data.insert::<OsuClient>(OsuApiClient::new(
|
data.insert::<OsuClient>(OsuApiClient::new(
|
||||||
http_client.clone(),
|
http_client.clone(),
|
||||||
var("OSU_API_KEY").expect("Please set OSU_API_KEY as osu! api key."),
|
var("OSU_API_KEY").expect("Please set OSU_API_KEY as osu! api key."),
|
||||||
|
@ -71,7 +82,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets up a framework for a client
|
// Sets up a framework for a client
|
||||||
fn setup_framework(mut client: Client) -> Client {
|
fn setup_framework(client: &Client) -> StandardFramework {
|
||||||
// Collect owners
|
// Collect owners
|
||||||
let owner = client
|
let owner = client
|
||||||
.cache_and_http
|
.cache_and_http
|
||||||
|
@ -80,8 +91,7 @@ fn setup_framework(mut client: Client) -> Client {
|
||||||
.expect("Should be able to get app info")
|
.expect("Should be able to get app info")
|
||||||
.owner;
|
.owner;
|
||||||
|
|
||||||
client.with_framework(
|
StandardFramework::new()
|
||||||
StandardFramework::new()
|
|
||||||
.configure(|c| {
|
.configure(|c| {
|
||||||
c.with_whitespace(false)
|
c.with_whitespace(false)
|
||||||
.prefix("y!")
|
.prefix("y!")
|
||||||
|
@ -141,6 +151,4 @@ fn setup_framework(mut client: Client) -> Client {
|
||||||
.group(&commands::FUN_GROUP)
|
.group(&commands::FUN_GROUP)
|
||||||
.group(&commands::COMMUNITY_GROUP)
|
.group(&commands::COMMUNITY_GROUP)
|
||||||
.group(&commands::OSU_GROUP)
|
.group(&commands::OSU_GROUP)
|
||||||
);
|
|
||||||
client
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue