Skip to content
GitHub

Cooldown

function in meocord/decorator Since 4.1.0

Cooldown(
  options: CooldownOptions & { by?: undefined },
): ClassDecorator & MethodDecorator

Cooldown<P extends object = Record<string, unknown>>(
  options: CooldownOptions<P> & { by: NonNullable<CooldownOptions<P>['by']> },
): CooldownByDecorator<P>

Limits how often a handler runs, counted per user, server, channel or for everyone.

Use it to rate-limit a command, a component or a message command: an interaction over the limit is answered only to its caller, with how long to wait, and a message command's is ignored, or told in a direct message with @MeoCord({ messages: { dmOnCooldown: true } }). For a check that is not about how often, use a Guard.

Where it runs

  • Cooldown check — for a cooldown without by, before a message's params are fetched
  • Cooldowns — after validation and pipes

Parameters

NameTypeDefaultDescription
optionsCooldownOptions & { by?: undefined }

The limit, whose calls count together, and how to exempt or tell calls apart.

options.secondsnumber

The window's length, in seconds: from 0.001 (a millisecond) to 4320000000000, counted in whole milliseconds, rounded. @Cooldown refuses anything else where it applies.

options.uses?number1

Calls allowed within the window. A deploy that changes it keeps the calls counted so far, held to the new number; one that changes seconds starts the count again. A handler's cooldowns with the same seconds, per, by and bypass (the same function, or none) count the same calls, so they share one count, held to the smallest uses. The exception is two cooldowns over the same seconds and per, both with by or both without, whose by or bypass functions differ (two inline functions differ even when written alike): uses tells them apart, so changing it, or adding or removing another such cooldown, starts their counts again, and reordering two with the same uses swaps their counts.

options.per?CooldownScope'user'

Whose calls are counted together: 'user', 'guild', 'channel' or 'global'. Outside a server, 'guild' and 'channel' count per user.

options.bypass?( context: ExecutionContext, ) => boolean | Promise<boolean>

Exempts a call, such as one from an owner, without counting it.

options.by?undefined

Counts calls apart by a value of the call, such as the account a button acts on, within the scope per names. It receives the handler's params as the handler does, after validation and pipes, and returns undefined to count the call as though there were no by. Declare the params it reads, and the handler's are checked against them; an error it throws goes to the exception filters.

Returns

Returns a decorator for a class or a method.

ClassDecorator & MethodDecorator

Throws

  • Error when seconds is not from 0.001 to 4320000000000, uses is not a whole number of at least 1, per is not a scope or by is not a function, as the decorator applies; CooldownError to a call over the limit.

Examples

TypeScript
@Command('daily', CommandType.SLASH)
@Cooldown({ seconds: 3 })
@Cooldown({ uses: 5, seconds: 60 })
async daily(interaction: ChatInputCommandInteraction) {
  await respond(interaction).send('Here are your coins.')
}

@Command('check-in/{uid}', CommandType.BUTTON)
@Cooldown({ seconds: 3600, by: (_context, { uid }: { uid: string }) => uid })
async checkIn(interaction: ButtonInteraction, { uid }: { uid: string }) {
  await respond(interaction).send(`Checked in ${uid}.`)
}

Parameters

NameTypeDefaultDescription
optionsCooldownOptions<P> & { by: NonNullable<CooldownOptions<P>['by']> }

The limit, whose calls count together, and by; the handler's params are checked against what by reads.

options.secondsnumber

The window's length, in seconds: from 0.001 (a millisecond) to 4320000000000, counted in whole milliseconds, rounded. @Cooldown refuses anything else where it applies.

options.uses?number1

Calls allowed within the window. A deploy that changes it keeps the calls counted so far, held to the new number; one that changes seconds starts the count again. A handler's cooldowns with the same seconds, per, by and bypass (the same function, or none) count the same calls, so they share one count, held to the smallest uses. The exception is two cooldowns over the same seconds and per, both with by or both without, whose by or bypass functions differ (two inline functions differ even when written alike): uses tells them apart, so changing it, or adding or removing another such cooldown, starts their counts again, and reordering two with the same uses swaps their counts.

options.per?CooldownScope'user'

Whose calls are counted together: 'user', 'guild', 'channel' or 'global'. Outside a server, 'guild' and 'channel' count per user.

options.bypass?( context: ExecutionContext, ) => boolean | Promise<boolean>

Exempts a call, such as one from an owner, without counting it.

options.by( context: ExecutionContext, params: P, ) => | CooldownKey | undefined | Promise<CooldownKey | undefined>

Counts calls apart by a value of the call, such as the account a button acts on, within the scope per names. It receives the handler's params as the handler does, after validation and pipes, and returns undefined to count the call as though there were no by. Declare the params it reads, and the handler's are checked against them; an error it throws goes to the exception filters.

Returns

CooldownByDecorator<P>