guild data caching
This commit is contained in:
@ -5,14 +5,11 @@ use serenity::{client::Context, model::channel::Message};
|
||||
use chrono::offset::Utc;
|
||||
|
||||
use crate::{
|
||||
command_help,
|
||||
consts::DEFAULT_PREFIX,
|
||||
get_ctx_data,
|
||||
language_manager::LanguageManager,
|
||||
models::{GuildData, UserData},
|
||||
FrameworkCtx, THEME_COLOR,
|
||||
command_help, consts::DEFAULT_PREFIX, get_ctx_data, language_manager::LanguageManager,
|
||||
models::UserData, FrameworkCtx, THEME_COLOR,
|
||||
};
|
||||
|
||||
use crate::models::CtxGuildData;
|
||||
use serenity::builder::CreateEmbedFooter;
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
@ -107,7 +104,7 @@ async fn help(ctx: &Context, msg: &Message, args: String) {
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
|
||||
let language = UserData::language_of(&msg.author, &pool);
|
||||
let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx);
|
||||
let prefix = ctx.prefix(msg.guild_id);
|
||||
|
||||
if !args.is_empty() {
|
||||
let framework = ctx
|
||||
@ -138,7 +135,7 @@ async fn info(ctx: &Context, msg: &Message, _args: String) {
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
|
||||
let language = UserData::language_of(&msg.author, &pool);
|
||||
let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx);
|
||||
let prefix = ctx.prefix(msg.guild_id);
|
||||
let current_user = ctx.cache.current_user();
|
||||
let footer = footer(ctx).await;
|
||||
|
||||
|
@ -31,6 +31,7 @@ use crate::{
|
||||
#[cfg(feature = "prefix-cache")]
|
||||
use crate::PrefixCache;
|
||||
|
||||
use crate::models::CtxGuildData;
|
||||
use std::{collections::HashMap, iter, time::Duration};
|
||||
|
||||
#[command]
|
||||
@ -175,10 +176,9 @@ async fn timezone(ctx: &Context, msg: &Message, args: String) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let content = lm.get(&user_data.language, "timezone/no_argument").replace(
|
||||
"{prefix}",
|
||||
&GuildData::prefix_from_id(msg.guild_id, &ctx).await,
|
||||
);
|
||||
let content = lm
|
||||
.get(&user_data.language, "timezone/no_argument")
|
||||
.replace("{prefix}", &ctx.prefix(msg.guild_id).await);
|
||||
|
||||
let popular_timezones = ctx
|
||||
.data
|
||||
@ -255,7 +255,7 @@ async fn change_meridian(ctx: &Context, msg: &Message, args: String) {
|
||||
})
|
||||
.await;
|
||||
} else {
|
||||
let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await;
|
||||
let prefix = ctx.prefix(msg.guild_id).await;
|
||||
|
||||
command_help(ctx, msg, lm, &prefix, &user_data.language, "meridian").await;
|
||||
}
|
||||
@ -557,7 +557,7 @@ WHERE
|
||||
})
|
||||
.await;
|
||||
} else {
|
||||
let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await;
|
||||
let prefix = ctx.prefix(msg.guild_id).await;
|
||||
|
||||
command_help(ctx, msg, lm, &prefix, &language, "restrict").await;
|
||||
}
|
||||
@ -684,7 +684,7 @@ SELECT command FROM command_aliases WHERE guild_id = (SELECT id FROM guilds WHER
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await;
|
||||
let prefix = ctx.prefix(msg.guild_id).await;
|
||||
|
||||
command_help(ctx, msg, lm, &prefix, &language, "alias").await;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ use std::{
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use crate::models::MeridianType;
|
||||
use crate::models::{CtxGuildData, MeridianType};
|
||||
use regex::Captures;
|
||||
use serenity::model::channel::Channel;
|
||||
|
||||
@ -177,7 +177,7 @@ async fn offset(ctx: &Context, msg: &Message, args: String) {
|
||||
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
||||
|
||||
if args.is_empty() {
|
||||
let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await;
|
||||
let prefix = ctx.prefix(msg.guild_id).await;
|
||||
|
||||
command_help(ctx, msg, lm, &prefix, &user_data.language, "offset").await;
|
||||
} else {
|
||||
@ -815,7 +815,7 @@ DELETE FROM timers WHERE owner = ? AND name = ?
|
||||
}
|
||||
|
||||
_ => {
|
||||
let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await;
|
||||
let prefix = ctx.prefix(msg.guild_id).await;
|
||||
|
||||
command_help(ctx, msg, lm, &prefix, &language, "timer").await;
|
||||
}
|
||||
@ -851,7 +851,6 @@ enum ReminderError {
|
||||
InvalidTag,
|
||||
InvalidTime,
|
||||
InvalidExpiration,
|
||||
NeedSubscription,
|
||||
DiscordError(String),
|
||||
}
|
||||
|
||||
@ -879,7 +878,6 @@ impl ToResponse for ReminderError {
|
||||
Self::InvalidTag => "remind/invalid_tag",
|
||||
Self::InvalidTime => "remind/invalid_time",
|
||||
Self::InvalidExpiration => "interval/invalid_expiration",
|
||||
Self::NeedSubscription => "interval/donor",
|
||||
Self::DiscordError(_) => "remind/generic_error",
|
||||
}
|
||||
}
|
||||
@ -1146,7 +1144,7 @@ INSERT INTO reminders (
|
||||
ctx,
|
||||
msg,
|
||||
lm,
|
||||
&GuildData::prefix_from_id(msg.guild_id, &ctx).await,
|
||||
&ctx.prefix(msg.guild_id).await,
|
||||
&language,
|
||||
"countdown",
|
||||
)
|
||||
@ -1157,10 +1155,8 @@ INSERT INTO reminders (
|
||||
.channel_id
|
||||
.say(
|
||||
&ctx,
|
||||
lm.get(&language, "interval/donor").replace(
|
||||
"{prefix}",
|
||||
&GuildData::prefix_from_id(msg.guild_id, &ctx).await,
|
||||
),
|
||||
lm.get(&language, "interval/donor")
|
||||
.replace("{prefix}", &ctx.prefix(msg.guild_id).await),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@ -1229,10 +1225,8 @@ async fn remind_command(ctx: &Context, msg: &Message, args: String, command: Rem
|
||||
.channel_id
|
||||
.say(
|
||||
&ctx,
|
||||
lm.get(&language, "interval/donor").replace(
|
||||
"{prefix}",
|
||||
&GuildData::prefix_from_id(msg.guild_id, &ctx).await,
|
||||
),
|
||||
lm.get(&language, "interval/donor")
|
||||
.replace("{prefix}", &ctx.prefix(msg.guild_id).await),
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
@ -1383,7 +1377,7 @@ async fn remind_command(ctx: &Context, msg: &Message, args: String, command: Rem
|
||||
}
|
||||
|
||||
None => {
|
||||
let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await;
|
||||
let prefix = ctx.prefix(msg.guild_id).await;
|
||||
|
||||
match command {
|
||||
RemindCommand::Remind => {
|
||||
@ -1608,7 +1602,7 @@ async fn natural(ctx: &Context, msg: &Message, args: String) {
|
||||
ctx,
|
||||
msg,
|
||||
lm,
|
||||
&GuildData::prefix_from_id(msg.guild_id, &ctx).await,
|
||||
&ctx.prefix(msg.guild_id).await,
|
||||
&user_data.language,
|
||||
"natural",
|
||||
)
|
||||
|
@ -12,10 +12,8 @@ use serenity::{
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::{
|
||||
command_help, get_ctx_data,
|
||||
models::{GuildData, UserData},
|
||||
};
|
||||
use crate::models::CtxGuildData;
|
||||
use crate::{command_help, get_ctx_data, models::UserData};
|
||||
use sqlx::MySqlPool;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
@ -233,7 +231,7 @@ DELETE FROM todos WHERE user_id = (SELECT id FROM users WHERE user = ?) AND guil
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
|
||||
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
||||
let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await;
|
||||
let prefix = ctx.prefix(msg.guild_id).await;
|
||||
|
||||
match subcommand {
|
||||
SubCommand::View => {
|
||||
@ -426,7 +424,7 @@ async fn show_help(ctx: &Context, msg: &Message, target: Option<TodoTarget>) {
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
|
||||
let language = UserData::language_of(&msg.author, &pool);
|
||||
let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx);
|
||||
let prefix = ctx.prefix(msg.guild_id);
|
||||
|
||||
let command = match target {
|
||||
None => "todo",
|
||||
|
@ -20,7 +20,7 @@ use regex::{Match, Regex, RegexBuilder};
|
||||
use std::{collections::HashMap, fmt};
|
||||
|
||||
use crate::language_manager::LanguageManager;
|
||||
use crate::models::{GuildData, UserData};
|
||||
use crate::models::{CtxGuildData, GuildData, UserData};
|
||||
use crate::{models::ChannelData, SQLPool};
|
||||
|
||||
type CommandFn = for<'fut> fn(&'fut Context, &'fut Message, String) -> BoxFuture<'fut, ()>;
|
||||
@ -335,7 +335,7 @@ impl Framework for RegexFramework {
|
||||
|
||||
async fn check_prefix(ctx: &Context, guild: &Guild, prefix_opt: Option<Match<'_>>) -> bool {
|
||||
if let Some(prefix) = prefix_opt {
|
||||
let guild_prefix = GuildData::prefix_from_id(Some(guild.id), &ctx).await;
|
||||
let guild_prefix = ctx.prefix(Some(guild.id)).await;
|
||||
|
||||
guild_prefix.as_str() == prefix.as_str()
|
||||
} else {
|
||||
@ -419,11 +419,7 @@ impl Framework for RegexFramework {
|
||||
lm.get(&language.await, "no_perms_managed")
|
||||
.replace(
|
||||
"{prefix}",
|
||||
&GuildData::prefix_from_id(
|
||||
msg.guild_id,
|
||||
&ctx,
|
||||
)
|
||||
.await,
|
||||
&ctx.prefix(msg.guild_id).await,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
33
src/main.rs
33
src/main.rs
@ -34,6 +34,7 @@ use crate::{
|
||||
consts::{CNC_GUILD, DEFAULT_PREFIX, SUBSCRIPTION_ROLES, THEME_COLOR},
|
||||
framework::RegexFramework,
|
||||
language_manager::LanguageManager,
|
||||
models::GuildData,
|
||||
};
|
||||
|
||||
use serenity::futures::TryFutureExt;
|
||||
@ -41,14 +42,16 @@ use serenity::futures::TryFutureExt;
|
||||
use inflector::Inflector;
|
||||
use log::info;
|
||||
|
||||
use crate::models::GuildData;
|
||||
use dashmap::DashMap;
|
||||
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use chrono_tz::Tz;
|
||||
|
||||
#[cfg(feature = "prefix-cache")]
|
||||
struct PrefixCache;
|
||||
#[cfg(feature = "prefix-cache")]
|
||||
impl TypeMapKey for PrefixCache {
|
||||
type Value = Arc<dashmap::DashMap<GuildId, String>>;
|
||||
struct GuildDataCache;
|
||||
|
||||
impl TypeMapKey for GuildDataCache {
|
||||
type Value = Arc<DashMap<GuildId, Arc<RwLock<GuildData>>>>;
|
||||
}
|
||||
|
||||
struct SQLPool;
|
||||
@ -172,10 +175,14 @@ DELETE FROM channels WHERE channel = ?
|
||||
.cloned()
|
||||
.expect("Could not get SQLPool from data");
|
||||
|
||||
#[cfg(feature = "prefix-cache")]
|
||||
let prefix_cache = ctx.data.read().await.get::<PrefixCache>().cloned().unwrap();
|
||||
#[cfg(feature = "prefix-cache")]
|
||||
prefix_cache.remove(&guild.id);
|
||||
let guild_data_cache = ctx
|
||||
.data
|
||||
.read()
|
||||
.await
|
||||
.get::<GuildDataCache>()
|
||||
.cloned()
|
||||
.unwrap();
|
||||
guild_data_cache.remove(&guild.id);
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@ -274,8 +281,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
.expect("Error occurred creating client");
|
||||
|
||||
{
|
||||
#[cfg(feature = "prefix-cache")]
|
||||
let prefix_cache = dashmap::DashMap::new();
|
||||
let guild_data_cache = dashmap::DashMap::new();
|
||||
|
||||
let pool = MySqlPool::connect(
|
||||
&env::var("DATABASE_URL").expect("Missing DATABASE_URL from environment"),
|
||||
@ -302,8 +308,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
|
||||
let mut data = client.data.write().await;
|
||||
|
||||
#[cfg(feature = "prefix-cache")]
|
||||
data.insert::<PrefixCache>(Arc::new(prefix_cache));
|
||||
data.insert::<GuildDataCache>(Arc::new(guild_data_cache));
|
||||
|
||||
data.insert::<SQLPool>(pool);
|
||||
data.insert::<PopularTimezones>(Arc::new(popular_timezones));
|
||||
|
135
src/models.rs
135
src/models.rs
@ -1,4 +1,5 @@
|
||||
use serenity::{
|
||||
async_trait,
|
||||
http::CacheHttp,
|
||||
model::{
|
||||
channel::Channel,
|
||||
@ -6,6 +7,7 @@ use serenity::{
|
||||
id::{GuildId, UserId},
|
||||
user::User,
|
||||
},
|
||||
prelude::Context,
|
||||
};
|
||||
|
||||
use sqlx::MySqlPool;
|
||||
@ -15,13 +17,77 @@ use chrono_tz::Tz;
|
||||
|
||||
use log::error;
|
||||
|
||||
use crate::consts::{DEFAULT_PREFIX, LOCAL_LANGUAGE, LOCAL_TIMEZONE};
|
||||
use crate::{
|
||||
consts::{DEFAULT_PREFIX, LOCAL_LANGUAGE, LOCAL_TIMEZONE},
|
||||
GuildDataCache, SQLPool,
|
||||
};
|
||||
|
||||
#[cfg(feature = "prefix-cache")]
|
||||
use crate::PrefixCache;
|
||||
use crate::SQLPool;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use serenity::prelude::Context;
|
||||
#[async_trait]
|
||||
pub trait CtxGuildData {
|
||||
async fn guild_data<G: Into<GuildId> + Send + Sync>(
|
||||
&self,
|
||||
guild_id: G,
|
||||
) -> Result<Arc<RwLock<GuildData>>, sqlx::Error>;
|
||||
|
||||
async fn prefix<G: Into<GuildId> + Send + Sync>(&self, guild_id: Option<G>) -> String;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CtxGuildData for Context {
|
||||
async fn guild_data<G: Into<GuildId> + Send + Sync>(
|
||||
&self,
|
||||
guild_id: G,
|
||||
) -> Result<Arc<RwLock<GuildData>>, sqlx::Error> {
|
||||
let guild_id = guild_id.into();
|
||||
|
||||
let guild = guild_id.to_guild_cached(&self.cache).await.unwrap();
|
||||
|
||||
let guild_cache = self
|
||||
.data
|
||||
.read()
|
||||
.await
|
||||
.get::<GuildDataCache>()
|
||||
.cloned()
|
||||
.unwrap();
|
||||
|
||||
let x = if let Some(guild_data) = guild_cache.get(&guild_id) {
|
||||
Ok(guild_data.clone())
|
||||
} else {
|
||||
let pool = self.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
match GuildData::from_guild(guild, &pool).await {
|
||||
Ok(d) => {
|
||||
let lock = Arc::new(RwLock::new(d));
|
||||
|
||||
guild_cache.insert(guild_id, lock.clone());
|
||||
|
||||
Ok(lock)
|
||||
}
|
||||
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
};
|
||||
|
||||
x
|
||||
}
|
||||
|
||||
async fn prefix<G: Into<GuildId> + Send + Sync>(&self, guild_id: Option<G>) -> String {
|
||||
if let Some(guild_id) = guild_id {
|
||||
self.guild_data(guild_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.read()
|
||||
.await
|
||||
.prefix
|
||||
.clone()
|
||||
} else {
|
||||
DEFAULT_PREFIX.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GuildData {
|
||||
pub id: u32,
|
||||
@ -30,65 +96,6 @@ pub struct GuildData {
|
||||
}
|
||||
|
||||
impl GuildData {
|
||||
#[cfg(feature = "prefix-cache")]
|
||||
pub async fn prefix_from_id<T: Into<GuildId>>(
|
||||
guild_id_opt: Option<T>,
|
||||
ctx: &Context,
|
||||
) -> String {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
let prefix_cache = ctx.data.read().await.get::<PrefixCache>().cloned().unwrap();
|
||||
|
||||
if let Some(guild_id) = guild_id_opt {
|
||||
let guild_id = guild_id.into();
|
||||
|
||||
if let Some(prefix) = prefix_cache.get(&guild_id) {
|
||||
prefix.to_string()
|
||||
} else {
|
||||
let row = sqlx::query!(
|
||||
"
|
||||
SELECT prefix FROM guilds WHERE guild = ?
|
||||
",
|
||||
guild_id.as_u64().to_owned()
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await;
|
||||
|
||||
let prefix = row.map_or_else(|_| DEFAULT_PREFIX.clone(), |r| r.prefix);
|
||||
|
||||
prefix_cache.insert(guild_id, prefix.clone());
|
||||
|
||||
prefix
|
||||
}
|
||||
} else {
|
||||
DEFAULT_PREFIX.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "prefix-cache"))]
|
||||
pub async fn prefix_from_id<T: Into<GuildId>>(
|
||||
guild_id_opt: Option<T>,
|
||||
ctx: &Context,
|
||||
) -> String {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
if let Some(guild_id) = guild_id_opt {
|
||||
let guild_id = guild_id.into().as_u64().to_owned();
|
||||
|
||||
let row = sqlx::query!(
|
||||
"
|
||||
SELECT prefix FROM guilds WHERE guild = ?
|
||||
",
|
||||
guild_id
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await;
|
||||
|
||||
row.map_or_else(|_| DEFAULT_PREFIX.clone(), |r| r.prefix)
|
||||
} else {
|
||||
DEFAULT_PREFIX.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn from_guild(guild: Guild, pool: &MySqlPool) -> Result<Self, sqlx::Error> {
|
||||
let guild_id = guild.id.as_u64().to_owned();
|
||||
|
||||
|
Reference in New Issue
Block a user