DJS Commandsv2 docs
API Reference@djs-commands/core

Cooldowns

CooldownConfig, CooldownType, CooldownActor, and the CacheAdapter contract.

See Concepts → Cooldowns for the narrative.

CooldownConfig

interface CooldownConfig {
	type: CooldownType;
	/** Duration in milliseconds */
	duration: number;
}

Attach to a command via the cooldown field.

defineCommand({
	name: "vote",
	description: "Vote on the current poll",
	cooldown: { type: "perUser", duration: 30_000 },
	run: async ({ reply }) => { await reply("voted"); },
});

CooldownType

type CooldownType = "perUser" | "perGuild" | "perUserPerGuild" | "global";
TypeKey shape
perUsercd:<cmd>:user:<userId>
perGuildcd:<cmd>:guild:<guildId>
perUserPerGuildcd:<cmd>:user:<userId>:guild:<guildId>
globalcd:<cmd>:global

In DMs, <guildId> is the literal "dm" so perGuild and perUserPerGuild keys remain stable.

CooldownActor

interface CooldownActor {
	userId: string;
	guildId: string | null;
}

Identifies a single rate-limit subject. The dispatcher derives this automatically; you only construct it yourself if you're using CooldownEngine directly to gate non-command flows.

CacheAdapter

interface CacheAdapter {
	get(key: string): Promise<number | null>;
	set(key: string, expiresAt: number, ttlMs: number): Promise<void>;
	delete(key: string): Promise<void>;
}

TTL-native KV contract for distributed cooldowns. expiresAt is an absolute UNIX millisecond timestamp; ttlMs is the same value as a TTL hint for stores that prefer it (Redis PX, Memcached, etc).

get should return null when the key is missing or already expired. Backends that auto-expire keys (Redis with PX) and backends that don't (a SQL cooldowns table) both work — the engine cleans up expired entries on read for the latter.

@djs-commands/adapter-redis implements this contract — see Adapter Cookbook → Redis.

CooldownEngine

class CooldownEngine {
	constructor(cache?: CacheAdapter);
	check(command: AnyCommand, actor: CooldownActor): Promise<number | null>;
	start(command: AnyCommand, actor: CooldownActor): Promise<void>;
}

Construct one yourself to gate non-command flows (autocomplete handlers, button clicks, modal submits) using the same shared cache.

import { CooldownEngine } from "@djs-commands/core";

const engine = new CooldownEngine(redisCacheAdapter(redis));

const remaining = await engine.check(myCommand, { userId, guildId });
if (remaining !== null) {
	// on cooldown — `remaining` ms left
}

await engine.start(myCommand, { userId, guildId });

Last updated on

On this page