Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: precondition guides #91

Merged
merged 34 commits into from
Dec 10, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
41444dd
feat: precondition guides
Lioness100 Nov 14, 2021
c19f2aa
fix line highlighting
Lioness100 Nov 14, 2021
7809b3a
fix links
Lioness100 Nov 14, 2021
3e801b1
refactors and fixes
Lioness100 Nov 15, 2021
1185820
style: formatting
favna Nov 20, 2021
b6d0889
implement favna's fixes
Lioness100 Nov 25, 2021
60bee92
links
Lioness100 Nov 27, 2021
a6bfe36
fixes
Lioness100 Nov 28, 2021
cfc0f4b
test
Lioness100 Nov 28, 2021
33f5512
prefer enums
Lioness100 Nov 28, 2021
02e7df0
add "what are pre" page
Lioness100 Nov 28, 2021
b2ece16
feat: precondition guides
Lioness100 Nov 14, 2021
dbba8dc
fix line highlighting
Lioness100 Nov 14, 2021
897af5b
fix links
Lioness100 Nov 14, 2021
2b31056
refactors and fixes
Lioness100 Nov 15, 2021
40a75b8
style: formatting
favna Nov 20, 2021
ba0428c
implement favna's fixes
Lioness100 Nov 25, 2021
7409dec
fixes
Lioness100 Nov 28, 2021
659cf9a
prefer enums
Lioness100 Nov 28, 2021
bf980ac
add "what are pre" page
Lioness100 Nov 28, 2021
f64466e
chore: insert commit message here
favna Nov 28, 2021
4939401
Update docs/Guide/preconditions/creating-your-own-preconditions.mdx
favna Nov 28, 2021
c4fc58f
Update docs/Guide/preconditions/handling-permissions.mdx
favna Nov 28, 2021
bce7094
chore: should work i guess...
favna Nov 28, 2021
c917c1a
Convert tabs to plugin
Lioness100 Dec 6, 2021
728a386
Merge branch 'feat/preconditionGuides' of https://github.com/Lioness1…
Lioness100 Dec 6, 2021
87cd8d7
Merge branch 'feat/preconditionGuides' of https://github.com/Lioness1…
Lioness100 Dec 6, 2021
04f1646
Merge branch 'feat/preconditionGuides' of https://github.com/Lioness1…
Lioness100 Dec 6, 2021
b3c131c
conflicts
Lioness100 Dec 6, 2021
814c6b0
vladdy fixes
Lioness100 Dec 6, 2021
997b983
Apply suggestions from favna
Lioness100 Dec 7, 2021
b294935
convert from tabs & prettify
Lioness100 Dec 7, 2021
527fed0
Merge branch 'feat/preconditionGuides' of https://github.com/Lioness1…
Lioness100 Dec 7, 2021
4e67b82
chore: formatting
favna Dec 7, 2021
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
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"**/projects/": true,
},
"cSpell.words": [
"favna"
"favna",
"nsfw"
vladfrangu marked this conversation as resolved.
Show resolved Hide resolved
]
}
79 changes: 77 additions & 2 deletions docs/Guide/preconditions/channel-types.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,81 @@
---
sidebar_position: 3
sidebar_position: 4
title: Setting the types of channel a command can run in
---

## TODO
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

The [`runIn`][runin] command option can be used to specify the types of channels a command can run in. This can be
useful if you're developing a command that, for example, displays the roles of a user. In that scenario, you'll want to
make sure that the command can only be run in guild channels.

:::info

You can view the valid `runIn` values [here][runintypes].

:::

<Tabs groupId="language-choice">
<TabItem value="javascript" label="JavaScript" default>

```javascript {7}
const { Command, CommandOptionsRunTypeEnum } = require('@sapphire/framework');

module.exports = class RolesCommand extends Command {
constructor(context) {
super(context, {
// ...
runIn: CommandOptionsRunTypeEnum.GuildAny // Only run in guild channels
});
}
};
```

</TabItem>
<TabItem value="ESM" label="ESM">

```javascript {7}
import { Command, CommandOptionsRunTypeEnum } from '@sapphire/framework';

export class PingCommand extends Command {
constructor(context) {
super(context, {
// ...
runIn: CommandOptionsRunTypeEnum.GuildAny // Only run in guild channels
});
}
}
```

</TabItem>
<TabItem value="typescript" label="TypeScript">

```typescript {7}
import { Command, CommandOptionsRunTypeEnum } from '@sapphire/framework';

export class PingCommand extends Command {
public constructor(context: Command.Context) {
super(context, {
// ...
runIn: CommandOptionsRunTypeEnum.GuildAny // Only run in guild channels
});
}
}
```

</TabItem>
</Tabs>

If you try to run a command in direct messages, you'll now find that nothing happens.

:::tip

To learn how to send a message to the command executor when a precondition fails, see [Reporting Precondition
Failure][reporting-precondition-failure].

:::

[runin]: ../../Documentation/api-framework/interfaces/CommandOptions#runin
[runintypes]: ../../Documentation/api-framework/enums/CommandOptionsRunTypeEnum
[reporting-precondition-failure]: ./reporting-precondition-failure
282 changes: 280 additions & 2 deletions docs/Guide/preconditions/command-cooldown.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,284 @@
---
sidebar_position: 0
sidebar_position: 3
title: Configuring command cooldowns
---

## TODO
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

Cooldowns are of vital importance for many bots to avoid excessive command usage, API ratelimits, and so on. Luckily,
Sapphire makes it easy to integrate them into your commands! At its simplest level, cooldowns can be used in specific
commands via the [`cooldownDelay`][cooldowndelay] property in the command's options. This value is amount of
milliseconds that a user will have to wait after using a command to use it again. Here's a basic example:

<Tabs groupId="language-choice">
<TabItem value="javascript" label="JavaScript" default>

```javascript {7}
const { Command } = require('@sapphire/framework');

module.exports = class PingCommand extends Command {
constructor(context) {
super(context, {
// ...
cooldownDelay: 10_000 // 10_000 milliseconds
});
}
};
```

</TabItem>
<TabItem value="ESM" label="ESM">

```javascript {7}
import { Command } from '@sapphire/framework';

export class PingCommand extends Command {
constructor(context) {
super(context, {
// ...
cooldownDelay: 10_000 // 10_000 milliseconds
});
}
}
```

</TabItem>
<TabItem value="typescript" label="TypeScript">

```typescript {7}
import { Command } from '@sapphire/framework';

export class PingCommand extends Command {
public constructor(context: Command.Context) {
super(context, {
// ...
cooldownDelay: 10_000 // 10_000 milliseconds
Lioness100 marked this conversation as resolved.
Show resolved Hide resolved
});
}
}
```

</TabItem>
</Tabs>

If you now try to run this command, and then run it again within 10 seconds, the command won't execute, and an error
Lioness100 marked this conversation as resolved.
Show resolved Hide resolved
will be thrown. You can learn how to process that error [here][reporting-precondition-failure].

:::info

`cooldownDelay` only accepts a value of milliseconds, which is not known to be the easiest to read or calculate. To
help, you can use [@sapphire/time-utilities][timeutils], which includes utilities for time transformers.

<Tabs groupId="language-choice">
Lioness100 marked this conversation as resolved.
Show resolved Hide resolved
<TabItem value="javascript" label="JavaScript" default>

```javascript {7}
const { Command } = require('@sapphire/framework');
const { Time } = require('@sapphire/time-utilities');

module.exports = class PingCommand extends Command {
constructor(context) {
super(context, {
// ...
cooldownDelay: Time.Second * 10; // Much easier for humans to read
});
}
};
```

</TabItem>
<TabItem value="ESM" label="ESM">

```javascript {7}
import { Command } from '@sapphire/framework';
import { Time } from '@sapphire/time-utilities';

export class PingCommand extends Command {
constructor(context) {
super(context, {
// ...
cooldownDelay: Time.Second * 10; // Much easier for humans to read
}
}
```

</TabItem>
<TabItem value="typescript" label="TypeScript">

```typescript {6}
import { Command } from '@sapphire/framework';
import { Time } from '@sapphire/time-utilities';

export class PingCommand extends Command {
public constructor(context: Command.Context) {
super(context, {
// ...
cooldownDelay: Time.Second * 10 // Much easier for humans to read
});
}
}
```

</TabItem>
</Tabs>

You can view the docs [here][timeenum].

:::

## User Exceptions

It's very common to not want cooldowns to apply to certain people, for example, the bot owner. This can be achieved by
adding [`cooldownFilteredUsers`][cooldownfilteredusers] to the options. This option should be an array of users ID that
favna marked this conversation as resolved.
Show resolved Hide resolved
the bot can ignore when calculating cooldowns.

<Tabs groupId="language-choice">
<TabItem value="javascript" label="JavaScript" default>

```javascript {8}
const { Command } = require('@sapphire/framework');

module.exports = class PingCommand extends Command {
constructor(context) {
super(context, {
// ...
cooldownDelay: 10_000, // 10_000 milliseconds
cooldownFilteredUsers: ['YOUR_ID'] // Ignore the bot owner
});
}
};
```

</TabItem>
<TabItem value="ESM" label="ESM">

```javascript {8}
import { Command } from '@sapphire/framework';

export class PingCommand extends Command {
constructor(context) {
super(context, {
// ...
cooldownDelay: 10_000, // 10_000 milliseconds
cooldownFilteredUsers: ['YOUR_ID'] // Ignore the bot owner
});
}
}
```

</TabItem>
<TabItem value="typescript" label="TypeScript">

```typescript {7}
import { Command } from '@sapphire/framework';

export class PingCommand extends Command {
public constructor(context: Command.Context) {
super(context, {
// ...
cooldownDelay: 10_000 // 10_000 milliseconds
Lioness100 marked this conversation as resolved.
Show resolved Hide resolved
cooldownFilteredUsers: ['YOUR_ID'] // Ignore the bot owner
});
}
}
```

</TabItem>
</Tabs>

## Advanced Usage

Accompanying `cooldownDelay`, you also have access to the options [`cooldownLimit`][cooldownlimit] and
[`cooldownScope`][cooldownscope].
favna marked this conversation as resolved.
Show resolved Hide resolved

`cooldownLimit` will define how many times a command can be used before a cooldown is put into affect. This value is set
to `1` by default. For example, a `cooldownDelay` of `10_000` milliseconds and a `cooldownLimit` of 2 will effectively
mean that you'd be able to use the command twice every 10 seconds.

Another useful option is `cooldownScope`, which will define the scope of the cooldown. This is useful if you want to
have a cooldown that applies per guild, for example, instead of per user. Valid scopes can be found [here][scopes].

## Client-wide Cooldowns

Sometimes you'll find a use case where you want specific cooldown options to apply to all commands in your client. This
can be achieved by adding [`defaultCooldown`][defaultcooldown] to your [`SapphireClient`][sapphire] options. You can use
any of the properties shown above with this option.

<Tabs groupId="language-choice">
<TabItem value="javascript" label="JavaScript" default>

```javascript {5-10}
const { SapphireClient, BucketScope } = require('@sapphire/framework');

const client = new SapphireClient({
intents: ['GUILDS', 'GUILD_MESSAGES'],
defaultCooldown: {
cooldownDelay: 10_000, // 10_000 milliseconds
cooldownFilteredUsers: ['YOUR_ID'], // Ignore the bot owner
cooldownLimit: 2, // Allow 2 uses before ratelimiting
cooldownScope: BucketScope.Channel // Scope cooldown to channel
}
});

void client.login('your-token-goes-here');
```

</TabItem>
<TabItem value="ESM" label="ESM">

```javascript {5-10}
import { SapphireClient, BucketScope } from '@sapphire/framework';

const client = new SapphireClient({
intents: ['GUILDS', 'GUILD_MESSAGES'],
defaultCooldown: {
cooldownDelay: 10_000, // 10_000 milliseconds
cooldownFilteredUsers: ['YOUR_ID'], // Ignore the bot owner
cooldownLimit: 2, // Allow 2 uses before ratelimiting
cooldownScope: BucketScope.Channel // Scope cooldown to channel
}
});

void client.login('your-token-goes-here');
```

</TabItem>
<TabItem value="typescript" label="TypeScript">

```typescript {5-10}
import { SapphireClient, BucketScope } from '@sapphire/framework';

const client = new SapphireClient({
intents: ['GUILDS', 'GUILD_MESSAGES'],
defaultCooldown: {
cooldownDelay: 10_000, // 10_000 milliseconds
cooldownFilteredUsers: ['YOUR_ID'], // Ignore the bot owner
cooldownLimit: 2, // Allow 2 uses before ratelimiting
cooldownScope: BucketScope.Channel // Scope cooldown to channel
}
});

void client.login('your-token-goes-here');
```

</TabItem>
</Tabs>

:::tip

To learn how to send a message to the command executor when a precondition fails, see [Reporting Precondition
Failure][reporting-precondition-failure].

:::

[cooldowndelay]: ../../Documentation/api-framework/interfaces/CommandOptions#cooldowndelay
[cooldownfilteredusers]: ../../Documentation/api-framework/interfaces/CommandOptions#cooldownfilteredusers
[cooldownlimit]: ../../Documentation/api-framework/interfaces/CommandOptions#cooldownlimit
[cooldownscope]: ../../Documentation/api-framework/interfaces/CommandOptions#cooldownscope
[defaultcooldown]: ../../Documentation/api-framework/interfaces/SapphireClientOptions#defaultcooldown
[sapphire]: ../../Documentation/api-framework/classes/SapphireClient
[scopes]: ../../Documentation/api-framework/enums/BucketScope
[reporting-precondition-failure]: ./reporting-precondition-failure
[timeutils]: ../../Documentation/api-utilities/modules/sapphire_time_utilities
[timeenum]: ../../Documentation/api-utilities/enums/sapphire_time_utilities.Time
Loading