Skip to content

Commit

Permalink
Add CooldownContext struct to make Cooldowns usable in more situations (
Browse files Browse the repository at this point in the history
  • Loading branch information
scottbot95 authored and GnomedDev committed Nov 26, 2023
1 parent 56e2b74 commit bd73861
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 19 deletions.
4 changes: 2 additions & 2 deletions examples/advanced_cooldowns/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ async fn dynamic_cooldowns(ctx: Context<'_>) -> Result<(), Error> {
cooldown_durations.user = Some(std::time::Duration::from_secs(10));
}

match cooldown_tracker.remaining_cooldown(ctx, &cooldown_durations) {
match cooldown_tracker.remaining_cooldown(ctx.cooldown_context(), &cooldown_durations) {
Some(remaining) => {
return Err(format!("Please wait {} seconds", remaining.as_secs()).into())
}
None => cooldown_tracker.start_cooldown(ctx),
None => cooldown_tracker.start_cooldown(ctx.cooldown_context()),
}
};

Expand Down
2 changes: 1 addition & 1 deletion macros/src/command/prefix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub fn generate_prefix_action(inv: &Invocation) -> Result<proc_macro2::TokenStre
))?;

if !ctx.framework.options.manual_cooldowns {
ctx.command.cooldowns.lock().unwrap().start_cooldown(ctx.into());
ctx.command.cooldowns.lock().unwrap().start_cooldown(ctx.cooldown_context());
}

inner(ctx.into(), #( #param_idents, )* )
Expand Down
4 changes: 2 additions & 2 deletions macros/src/command/slash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ pub fn generate_slash_action(inv: &Invocation) -> Result<proc_macro2::TokenStrea
})?;

if !ctx.framework.options.manual_cooldowns {
ctx.command.cooldowns.lock().unwrap().start_cooldown(ctx.into());
ctx.command.cooldowns.lock().unwrap().start_cooldown(ctx.cooldown_context());
}

inner(ctx.into(), #( #param_identifiers, )*)
Expand Down Expand Up @@ -214,7 +214,7 @@ pub fn generate_context_menu_action(
<#param_type as ::poise::ContextMenuParameter<_, _>>::to_action(|ctx, value| {
Box::pin(async move {
if !ctx.framework.options.manual_cooldowns {
ctx.command.cooldowns.lock().unwrap().start_cooldown(ctx.into());
ctx.command.cooldowns.lock().unwrap().start_cooldown(ctx.cooldown_context());
}

inner(ctx.into(), value)
Expand Down
45 changes: 33 additions & 12 deletions src/cooldown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ use crate::serenity_prelude as serenity;
use crate::util::OrderedMap;
use std::time::{Duration, Instant};

/// Subset of [`crate::Context`] so that [`Cooldowns`] can be used without requiring a full [Context](`crate::Context`)
/// (ie from within an `event_handler`)
#[derive(Default, Clone, PartialEq, Eq, Debug, Hash)]
pub struct CooldownContext {
/// The user associated with this request
pub user_id: serenity::UserId,
/// The guild this request originated from or `None`
pub guild_id: Option<serenity::GuildId>,
/// The channel associated with this request
pub channel_id: serenity::ChannelId,
}

/// Configuration struct for [`Cooldowns`]
#[derive(Default, Clone, PartialEq, Eq, Debug, Hash)]
pub struct CooldownConfig {
Expand Down Expand Up @@ -57,32 +69,32 @@ impl CooldownTracker {

/// Queries the cooldown buckets and checks if all cooldowns have expired and command
/// execution may proceed. If not, Some is returned with the remaining cooldown
pub fn remaining_cooldown<U, E>(
pub fn remaining_cooldown(
&self,
ctx: crate::Context<'_, U, E>,
ctx: CooldownContext,
cooldown_durations: &CooldownConfig,
) -> Option<Duration> {
let mut cooldown_data = vec![
(cooldown_durations.global, self.global_invocation),
(
cooldown_durations.user,
self.user_invocations.get(&ctx.author().id).copied(),
self.user_invocations.get(&ctx.user_id).copied(),
),
(
cooldown_durations.channel,
self.channel_invocations.get(&ctx.channel_id()).copied(),
self.channel_invocations.get(&ctx.channel_id).copied(),
),
];

if let Some(guild_id) = ctx.guild_id() {
if let Some(guild_id) = ctx.guild_id {
cooldown_data.push((
cooldown_durations.guild,
self.guild_invocations.get(&guild_id).copied(),
));
cooldown_data.push((
cooldown_durations.member,
self.member_invocations
.get(&(ctx.author().id, guild_id))
.get(&(ctx.user_id, guild_id))
.copied(),
));
}
Expand All @@ -98,17 +110,26 @@ impl CooldownTracker {
}

/// Indicates that a command has been executed and all associated cooldowns should start running
pub fn start_cooldown<U, E>(&mut self, ctx: crate::Context<'_, U, E>) {
pub fn start_cooldown(&mut self, ctx: CooldownContext) {
let now = Instant::now();

self.global_invocation = Some(now);
self.user_invocations.insert(ctx.author().id, now);
self.channel_invocations.insert(ctx.channel_id(), now);
self.user_invocations.insert(ctx.user_id, now);
self.channel_invocations.insert(ctx.channel_id, now);

if let Some(guild_id) = ctx.guild_id() {
if let Some(guild_id) = ctx.guild_id {
self.guild_invocations.insert(guild_id, now);
self.member_invocations
.insert((ctx.author().id, guild_id), now);
self.member_invocations.insert((ctx.user_id, guild_id), now);
}
}
}

impl<'a> From<&'a serenity::Message> for CooldownContext {
fn from(message: &'a serenity::Message) -> Self {
Self {
user_id: message.author.id,
channel_id: message.channel_id,
guild_id: message.guild_id,
}
}
}
4 changes: 2 additions & 2 deletions src/dispatch/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ async fn check_permissions_and_cooldown_single<'a, U, E>(
}

if !ctx.framework().options().manual_cooldowns {
let cooldowns = &cmd.cooldowns;
let cooldowns = cmd.cooldowns.lock().unwrap();
let config = cmd.cooldown_config.read().unwrap();
let remaining_cooldown = cooldowns.lock().unwrap().remaining_cooldown(ctx, &config);
let remaining_cooldown = cooldowns.remaining_cooldown(ctx.cooldown_context(), &config);
if let Some(remaining_cooldown) = remaining_cooldown {
return Err(crate::FrameworkError::CooldownHit {
ctx,
Expand Down
10 changes: 10 additions & 0 deletions src/structs/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ context_methods! {
}
}

/// Create a [`crate::CooldownContext`] based off the underlying context type.
(cooldown_context self)
(pub fn cooldown_context(self) -> crate::CooldownContext) {
crate::CooldownContext {
user_id: self.author().id,
channel_id: self.channel_id(),
guild_id: self.guild_id()
}
}

/// See [`Self::serenity_context`].
#[deprecated = "poise::Context can now be passed directly into most serenity functions. Otherwise, use `.serenity_context()` now"]
#[allow(deprecated)]
Expand Down

0 comments on commit bd73861

Please sign in to comment.