osu: Introduce Env and propagate it to other functions (#40)

This commit is contained in:
huynd2001 2024-03-17 12:21:13 -04:00 committed by GitHub
parent 94dc225b86
commit 1066f249b0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 598 additions and 433 deletions

View file

@ -1,4 +1,5 @@
use crate::{AppData, MemberCache, Result};
use std::{collections::HashMap, sync::Arc};
use async_trait::async_trait;
use futures_util::{
future::{join_all, ready, FutureExt},
@ -18,9 +19,11 @@ use serenity::{
prelude::*,
utils::MessageBuilder,
};
use std::{collections::HashMap, sync::Arc};
use youmubot_db::DB;
use crate::{AppData, MemberCache, Result};
#[derive(Debug, Clone)]
pub struct CacheAndHttp(Arc<Cache>, Arc<Http>);
@ -28,15 +31,19 @@ impl CacheAndHttp {
pub fn from_client(client: &Client) -> Self {
Self(client.cache.clone(), client.http.clone())
}
pub fn from_context(context: &Context) -> Self {
Self(context.cache.clone(), context.http.clone())
}
}
impl CacheHttp for CacheAndHttp {
fn cache(&self) -> Option<&Arc<Cache>> {
Some(&self.0)
}
fn http(&self) -> &Http {
&self.1
}
fn cache(&self) -> Option<&Arc<Cache>> {
Some(&self.0)
}
}
/// A list of assigned channels for an announcer.
@ -94,23 +101,14 @@ impl MemberToChannels {
///
/// This struct manages the list of all Announcers, firing them in a certain interval.
pub struct AnnouncerHandler {
cache_http: CacheAndHttp,
data: AppData,
announcers: HashMap<&'static str, RwLock<Box<dyn Announcer + Send + Sync>>>,
}
// Querying for the AnnouncerHandler in the internal data returns a vec of keys.
impl TypeMapKey for AnnouncerHandler {
type Value = Vec<&'static str>;
}
/// Announcer-managing related.
impl AnnouncerHandler {
/// Create a new instance of the handler.
pub fn new(client: &serenity::Client) -> Self {
pub fn new() -> Self {
Self {
cache_http: CacheAndHttp(client.cache.clone(), client.http.clone()),
data: client.data.clone(),
announcers: HashMap::new(),
}
}
@ -136,10 +134,30 @@ impl AnnouncerHandler {
self
}
}
pub fn run(self, client: &Client) -> AnnouncerRunner {
let runner = AnnouncerRunner {
cache_http: CacheAndHttp::from_client(client),
data: client.data.clone(),
announcers: self.announcers,
};
runner
}
}
pub struct AnnouncerRunner {
cache_http: CacheAndHttp,
data: AppData,
announcers: HashMap<&'static str, RwLock<Box<dyn Announcer + Send + Sync>>>,
}
// Querying for the AnnouncerRunner in the internal data returns a vec of keys.
impl TypeMapKey for AnnouncerRunner {
type Value = Vec<&'static str>;
}
/// Execution-related.
impl AnnouncerHandler {
impl AnnouncerRunner {
/// Collect the list of guilds and their respective channels, by the key of the announcer.
async fn get_guilds(data: &AppData, key: &'static str) -> Result<Vec<(GuildId, ChannelId)>> {
let data = AnnouncerChannels::open(&*data.read().await)
@ -214,7 +232,7 @@ pub async fn list_announcers(ctx: &Context, m: &Message, _: Args) -> CommandResu
let guild_id = m.guild_id.unwrap();
let data = &*ctx.data.read().await;
let announcers = AnnouncerChannels::open(data);
let channels = data.get::<AnnouncerHandler>().unwrap();
let channels = data.get::<AnnouncerRunner>().unwrap();
let channels = channels
.iter()
.filter_map(|&key| {
@ -249,7 +267,7 @@ pub async fn list_announcers(ctx: &Context, m: &Message, _: Args) -> CommandResu
pub async fn register_announcer(ctx: &Context, m: &Message, mut args: Args) -> CommandResult {
let data = ctx.data.read().await;
let key = args.single::<String>()?;
let keys = data.get::<AnnouncerHandler>().unwrap();
let keys = data.get::<AnnouncerRunner>().unwrap();
if !keys.contains(&&key[..]) {
m.reply(
&ctx,
@ -296,7 +314,7 @@ pub async fn register_announcer(ctx: &Context, m: &Message, mut args: Args) -> C
pub async fn remove_announcer(ctx: &Context, m: &Message, mut args: Args) -> CommandResult {
let data = ctx.data.read().await;
let key = args.single::<String>()?;
let keys = data.get::<AnnouncerHandler>().unwrap();
let keys = data.get::<AnnouncerRunner>().unwrap();
if !keys.contains(&key.as_str()) {
m.reply(
&ctx,

View file

@ -1,7 +1,24 @@
use std::sync::Arc;
/// Re-export the anyhow errors
pub use anyhow::{anyhow as error, bail, Error, Result};
/// Re-exporting async_trait helps with implementing Announcer.
pub use async_trait::async_trait;
/// Re-export useful future and stream utils
pub use futures_util::{future, stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt};
/// Module `prelude` provides a sane set of default imports that can be used inside
/// a Youmubot source file.
pub use serenity::prelude::*;
use std::sync::Arc;
/// Re-export the spawn function
pub use tokio::spawn as spawn_future;
pub use announcer::{Announcer, AnnouncerRunner};
pub use args::{ChannelId, Duration, RoleId, UserId, UsernameArg};
pub use debugging_ok::OkPrint;
pub use flags::Flags;
pub use hook::Hook;
pub use member_cache::MemberCache;
pub use pagination::{paginate, paginate_fn, paginate_reply, paginate_reply_fn, Paginate};
pub mod announcer;
pub mod args;
@ -13,26 +30,6 @@ pub mod ratelimit;
pub mod setup;
pub mod table_format;
pub use announcer::{Announcer, AnnouncerHandler};
pub use args::{ChannelId, Duration, RoleId, UserId, UsernameArg};
pub use flags::Flags;
pub use hook::Hook;
pub use member_cache::MemberCache;
pub use pagination::{paginate, paginate_fn, paginate_reply, paginate_reply_fn, Paginate};
/// Re-exporting async_trait helps with implementing Announcer.
pub use async_trait::async_trait;
/// Re-export the anyhow errors
pub use anyhow::{anyhow as error, bail, Error, Result};
pub use debugging_ok::OkPrint;
/// Re-export useful future and stream utils
pub use futures_util::{future, stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt};
/// Re-export the spawn function
pub use tokio::spawn as spawn_future;
/// The global app data.
pub type AppData = Arc<RwLock<TypeMap>>;
@ -50,8 +47,18 @@ impl TypeMapKey for SQLClient {
type Value = youmubot_db_sql::Pool;
}
/// The created base environment.
#[derive(Debug, Clone)]
pub struct Env {
// clients
pub http: reqwest::Client,
pub sql: youmubot_db_sql::Pool,
pub members: Arc<MemberCache>,
// databases
// pub(crate) announcer_channels: announcer::AnnouncerChannels,
}
pub mod prelude_commands {
use crate::announcer::ANNOUNCERCOMMANDS_GROUP;
use serenity::{
framework::standard::{
macros::{command, group},
@ -61,6 +68,8 @@ pub mod prelude_commands {
prelude::Context,
};
use crate::announcer::ANNOUNCERCOMMANDS_GROUP;
#[group("Prelude")]
#[description = "All the commands that makes the base of Youmu"]
#[commands(ping)]

View file

@ -1,12 +1,14 @@
use std::ops::Deref;
/// Provides a simple ratelimit lock (that only works in tokio)
// use tokio::time::
use std::time::Duration;
use crate::Result;
use flume::{bounded as channel, Receiver, Sender};
use std::ops::Deref;
use crate::Result;
/// Holds the underlying `T` in a rate-limited way.
#[derive(Debug, Clone)]
pub struct Ratelimit<T> {
inner: T,
recv: Receiver<()>,

View file

@ -1,6 +1,9 @@
use serenity::prelude::*;
use std::{path::Path, time::Duration};
use serenity::prelude::*;
use crate::Env;
/// Set up the prelude libraries.
///
/// Panics on failure: Youmubot should *NOT* attempt to continue when this function fails.
@ -8,8 +11,8 @@ pub async fn setup_prelude(
db_path: impl AsRef<Path>,
sql_path: impl AsRef<Path>,
data: &mut TypeMap,
) {
// Setup the announcer DB.
) -> Env {
// Set up the announcer DB.
crate::announcer::AnnouncerChannels::insert_into(
data,
db_path.as_ref().join("announcers.yaml"),
@ -22,17 +25,25 @@ pub async fn setup_prelude(
.expect("SQL database set up");
// Set up the HTTP client.
data.insert::<crate::HTTPClient>(
reqwest::ClientBuilder::new()
.connect_timeout(Duration::from_secs(5))
.timeout(Duration::from_secs(60))
.build()
.expect("Build be able to build HTTP client"),
);
let http_client = reqwest::ClientBuilder::new()
.connect_timeout(Duration::from_secs(5))
.timeout(Duration::from_secs(60))
.build()
.expect("Build be able to build HTTP client");
data.insert::<crate::HTTPClient>(http_client.clone());
// Set up the member cache.
data.insert::<crate::MemberCache>(std::sync::Arc::new(crate::MemberCache::default()));
let member_cache = std::sync::Arc::new(crate::MemberCache::default());
data.insert::<crate::MemberCache>(member_cache.clone());
// Set up the SQL client.
data.insert::<crate::SQLClient>(sql_pool);
data.insert::<crate::SQLClient>(sql_pool.clone());
let env = Env {
http: http_client,
sql: sql_pool,
members: member_cache,
};
env
}