Skip to main content
Your bot removes and restricts members through a small set of server-scoped methods on the Client. Each one names the server and the target member. Each returns a Promise<void> that resolves when the server accepts the action, and rejects with a CloakActionError when the server denies it. The server re-checks every action, so its verdict is the authoritative one even when your own can() check passed.

Every moderation call needs a server

Message.serverId is string | null. It is null for a direct message, and messageCreate delivers direct messages on the same event as channel messages. Moderation is a server concept, so guard before you touch msg.serverId.
TypeScript flags every unguarded use. Add the guard once at the top of the handler and the rest of the body type-checks.

Three shared constraints

kick(), ban(), and timeout() are gated the same way. All three require:
  • The matching permission: member_kick, member_ban, or member_timeout.
  • A role rank above the target. Equal rank is not enough.
  • A target that is neither the server owner nor the bot itself.
Failing any of them rejects with a CloakActionError carrying the server’s own code and message. err.permission is set only when the denial maps to a named permission, so a rank failure or a self-target failure leaves it undefined.

Kick a member

kick() removes a member from the server. They can rejoin with a new invite.
The acknowledgement uses msg.channel.send(), a plain send into the same channel. msg.reply() posts a true reply: it renders a quote header and fires a mute-piercing “replied to you” notification on the person who ran the command. Use reply() when the quote is the point, and channel.send() when the bot is just answering.

Ban a member

ban() kicks the member and records a ban, so the account cannot rejoin until you unban it.
The reason argument is optional. The server caps it at 256 characters and rejects a longer one outright, so truncate before you send.
You can omit it entirely:
The reason is stored with the ban and comes back later through fetchBans().

Unban a member

unban() clears a recorded ban so the account can rejoin. It requires the member_ban permission.
On some older Cloak servers unban() additionally requires owner or admin standing, and answers with a distinct code when the bot has neither. On an up-to-date server the member_ban permission is enough.

Time out a member

timeout() mutes a member for a duration you set in seconds. While timed out, the member cannot send messages or add reactions in that server. Passing 0 lifts the timeout.
The duration is floored to a whole number of seconds and clamped at zero, so fractional and negative values never reach the wire. removeTimeout() lifts a member’s timeout. It calls timeout() with 0 seconds.
timeout() needs a current Cloak server. An older server drops the frame instead of answering it, so the promise neither resolves nor rejects: it hangs until the transport timeout fires. A try/catch does not shorten that wait. Confirm the server supports timeouts before you build a command around it.

Read the ban list

fetchBans() returns the server’s recorded bans as a BanInfo[]. It requires the member_ban permission.
Each BanInfo carries the fields below.
string
The banned account’s id.
string | null
The account’s name, resolved server-side, or null when the account no longer resolves.
Date | null
When the ban was recorded, where known.
string
The reason passed to ban(), or an empty string if none was given.
On some older Cloak servers fetchBans() also requires owner or admin standing, in addition to the member_ban permission.

Pre-gate with can()

Before firing a moderation action, check can() to skip work the server would only reject. Use the permission the action requires: member_kick for kick(), and member_ban for ban(), unban(), and fetchBans().
can() is advisory. It reflects the last permission verdict the server sent, and it returns false when no verdict has arrived yet. The server re-checks every action and remains the authority. Never treat a passing can() as a guarantee, and always handle a CloakActionError from the action itself.
can() also cannot see the two constraints that are not permissions. Role rank and owner status are knowable only to the server, so a rank failure surfaces as a rejection that no pre-gate can predict.

Read the denial

When an action is denied, the rejected error is a CloakActionError. It carries the opcode, the raw server code, and a readable message. If the denial maps to a named permission, .permission tells you which one was missing.
A denial for insufficient rank, a self-target, or an owner target leaves .permission undefined. Read .message in that case.

Use the guild() handle

Every moderation method is also available on the guild() handle, which binds a serverId once so you do not repeat it on each call.
The handle changes nothing about permissions, return types, or errors. It only removes the repeated first argument.

Next

Permissions

What decides whether an action is allowed, and why can() is advisory.

Errors

Catch and read a CloakActionError.

Members and roles

Read the roles you are ranking against.

Moderation bot

A runnable bot that uses every method on this page.