Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ export {
ExtensionsManager
} from './src/commands/extension.ts'
export { ApplicationCommandsModule } from './src/interactions/commandModule.ts'
export { CommandClient, command, subcommand } from './src/commands/client.ts'
export {
CommandClient,
command,
subcommand,
CommandCooldownType
} from './src/commands/client.ts'
export type { CommandClientOptions } from './src/commands/client.ts'
export { BaseManager } from './src/managers/base.ts'
export { BaseChildManager } from './src/managers/baseChild.ts'
Expand Down
86 changes: 86 additions & 0 deletions src/commands/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ export interface CommandClientOptions extends ClientOptions {
allowDMs?: boolean
/** Whether Commands should be case-sensitive or not, not by default. */
caseSensitive?: boolean
/** Global command cooldown in MS */
globalCommandCooldown?: number
/** Global cooldown in MS */
globalCooldown?: number
}

export enum CommandCooldownType {
/** Cooldown for command for user */
USER_COMMAND,
/** Cooldown for any command for bot */
USER_GLOBAL,
/** Cooldown for command for bot */
BOT_COMMAND,
/** Cooldown for any command for bot */
BOT_GLOBAL
}

export type CommandContextMiddleware<T extends CommandContext> = (
Expand Down Expand Up @@ -81,6 +96,14 @@ export class CommandClient extends Client implements CommandClientOptions {

middlewares = new Array<CommandContextMiddleware<CommandContext>>()

globalCommandCooldown = 0
globalCooldown = 0

private readonly lastUsed = new Map<
string,
{ global: number; commands: { [key: string]: number } }
>()

constructor(options: CommandClientOptions) {
super(options)
this.prefix = options.prefix
Expand Down Expand Up @@ -125,6 +148,9 @@ export class CommandClient extends Client implements CommandClientOptions {
this.caseSensitive =
options.caseSensitive === undefined ? false : options.caseSensitive

this.globalCommandCooldown = options.globalCommandCooldown ?? 0
this.globalCooldown = options.globalCooldown ?? 0

const self = this as any
if (self._decoratedCommands !== undefined) {
Object.values(self._decoratedCommands).forEach((entry: any) => {
Expand Down Expand Up @@ -404,6 +430,58 @@ export class CommandClient extends Client implements CommandClientOptions {
}
}

const userCooldowns = this.lastUsed.get(msg.author.id) ?? {
global: 0,
commands: {}
}
const botCooldowns = this.lastUsed.get('bot') ?? {
global: 0,
commands: {}
}

const userCanUseCommandAt =
(userCooldowns.commands[command.name] ?? 0) +
((command.cooldown ?? 0) as number)
const userCanUseAnyCommandAt =
userCooldowns.global + this.globalCommandCooldown

const anyoneCanUseCommandAt =
(botCooldowns.commands[command.name] ?? 0) +
((command.globalCooldown ?? 0) as number)
const anyoneCanUseAnyCommandAt = botCooldowns.global + this.globalCooldown

if (
Date.now() < anyoneCanUseCommandAt ||
Date.now() < anyoneCanUseAnyCommandAt
) {
const forCommand = anyoneCanUseCommandAt > anyoneCanUseAnyCommandAt
return this.emit(
'commandOnCooldown',
ctx,
(forCommand ? anyoneCanUseCommandAt : anyoneCanUseAnyCommandAt) -
Date.now(),
forCommand
? CommandCooldownType.BOT_COMMAND
: CommandCooldownType.BOT_GLOBAL
)
}

if (
Date.now() < userCanUseCommandAt ||
Date.now() < userCanUseAnyCommandAt
) {
const forCommand = userCanUseCommandAt > userCanUseAnyCommandAt
return this.emit(
'commandOnCooldown',
ctx,
(forCommand ? userCanUseCommandAt : userCanUseAnyCommandAt) -
Date.now(),
forCommand
? CommandCooldownType.USER_COMMAND
: CommandCooldownType.USER_GLOBAL
)
}

const lastNext = async (): Promise<void> => {
try {
this.emit('commandUsed', ctx)
Expand All @@ -412,6 +490,14 @@ export class CommandClient extends Client implements CommandClientOptions {

const result = await command.execute(ctx)
await command.afterExecute(ctx, result)

userCooldowns.commands[command.name] = Date.now()
userCooldowns.global = Date.now()
botCooldowns.commands[command.name] = Date.now()
botCooldowns.global = Date.now()

this.lastUsed.set(msg.author.id, userCooldowns)
this.lastUsed.set('bot', botCooldowns)
} catch (e) {
try {
await command.onError(ctx, e as Error)
Expand Down
4 changes: 4 additions & 0 deletions src/commands/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ export class Command implements CommandOptions {
dmOnly?: boolean
ownerOnly?: boolean
subCommands?: Command[]
/** Cooldown in MS */
cooldown?: number
/** Global command cooldown in MS */
globalCooldown?: number

declare readonly _decoratedSubCommands?: Command[]

Expand Down
6 changes: 6 additions & 0 deletions src/gateway/handlers/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import { threadListSync } from './threadListSync.ts'
import { guildStickersUpdate } from './guildStickersUpdate.ts'
import { MessageSticker } from '../../structures/messageSticker.ts'
import { guildAuditLogEntryCreate } from './guildAuditLogEntryCreate.ts'
import { CommandCooldownType } from '../../commands/client.ts'

export const gatewayHandlers: {
[eventCode in GatewayEvents]: GatewayEventHandler | undefined
Expand Down Expand Up @@ -452,6 +453,11 @@ export type ClientEvents = {
commandUsed: [ctx: CommandContext]
commandError: [ctx: CommandContext, err: Error]
commandNotFound: [msg: Message, parsedCmd: ParsedCommand]
commandOnCooldown: [
ctx: CommandContext,
remaining: number,
type: CommandCooldownType
]
gatewayError: [err: ErrorEvent, shards: [number, number]]
error: [error: Error]

Expand Down