Skip to main content
Righteous

Trap channel

Set up a channel nobody should ever post in, and punish the self-bots and spam accounts that do.

A trap channel is a channel with a standing warning in it and nothing else. Real members read the warning and leave it alone. Automated accounts that post into every channel the moment they join do not, and get punished immediately. It costs nothing to run and catches the kind of account that arrives faster than a moderator can react. Every trap command needs the Manage Guild permission.

Set the trap

Create it

Run ,trap setup to have the bot make a channel for you, or name one you already have. Creating one needs Manage Channels.

Syntax

,trap setup [#channel]

Example

,trap setup #do-not-post

Choose the punishment

Run ,trap action. The default is softban.

Syntax

,trap action <softban|ban|kick|timeout|mute|jail> [duration]

Example

,trap action timeout 2h

Whitelist anyone who must be able to post there

Run ,trap whitelist add.

Syntax

,trap whitelist <add|remove|list> [@role|@user]

Example

,trap whitelist add @Staff

Check it

Run ,trap settings to see the whole configuration in one embed.

Syntax

,trap settings

Leave the channel visible. A trap nobody can see is a trap nothing walks into. The bot posts a standing embed in it saying not to send messages, so a member who wanders in has been told.

Threads inside a trap channel count as part of the trap. A message posted in one is treated the same as a message in the channel itself.

Choose what happens to whoever posts

,trap action sets the punishment, and a duration with it: ,trap action timeout 2h.

ActionBot needsEffect
softbanBan MembersBans with a message purge, then immediately unbans. The default
banBan MembersPermanent
kickKick MembersThey can rejoin
timeoutModerate MembersReversible. Takes an optional duration, one hour by default
muteManage RolesNeeds a mute role set with ,config muterole
jailManage RolesNeeds a jail role set with ,config jailrole

Every catch is deleted before anything else happens, and the caught message never reaches your snipe cache or your delete and edit logs. One trap embed goes to the log channel from ,config logchannel.

Staff are not immune

Only the server owner and whitelisted users and roles are immune. An Administrator who is not whitelisted gets caught and punished like anybody else. That is deliberate: permission-based immunity made the trap a silent no-op for every moderator who tried to test it.

It also means that unless you are the server owner or whitelisted, your own ,trap commands are deleted inside a trap channel before they can be read as commands. Configure it from somewhere else, or whitelist your staff role.

Punish repeat offenders harder

Escalate a second catch

,trap escalation applies a harsher punishment to anybody caught more than once. It is off by default and escalates to ban unless you name something else.

Syntax

,trap escalation <on|off> [action]

Example

,trap escalation on ban

Offence counts come from a member's punishment history, so clearing their history with ,punishmenthistory clear resets their count.

Treat a new account's first catch as a second

,trap joingate applies the escalation action on a member's first catch when their account or their membership is newer than the number of days you set. It accepts 1 to 90 days and defaults to 7, and it is off by default.

Syntax

,trap joingate <on|off> [days]

Example

,trap joingate on 7

The join gate uses the escalation action whether or not escalation itself is on, and that action is ban until you change it. ,trap escalation sets it either way, so ,trap escalation off timeout gives the gate a gentler action while leaving repeat escalation switched off.

Manage the warning embed

The standing embed in a trap channel is controlled by two separate commands, and they do different things.

Show or hide it with trap counter

,trap counter controls whether the embed exists at all and what number it shows.

Syntax

,trap counter <on|off|reset|set> [count]

Example

,trap counter set 42
ArgumentDoes
on / offShows or hides the embed. off gives you a silent trap with nothing posted in it
resetSets the displayed count back to zero
set <count>Corrects the displayed count

Restyle it with trap embed

,trap embed controls what it looks like.

Syntax

,trap embed <view|set|remove|resend> [code]

Example

,trap embed set {title: keep out}$p{field: Caught && **{trap.counter}** && inline}
ArgumentDoes
viewReturns the current embed code, or the default one if you have not set anything
set <code>Replaces it
removeGoes back to the default
resendDeletes the current embed and posts a fresh one

Turning the counter off does not delete your custom code, and setting a code does not turn the counter on.

Start from the code the bot already posts

The default warning embed is itself an embed code, and ,trap embed view hands back the one your server is running. Here it is, ready to paste and edit:

Syntax

,trap embed set <code>

Example

,trap embed set {title: 🪤 DO NOT SEND MESSAGES IN THIS CHANNEL}$p{color: #FF4747}$p{description: This channel is a trap for spam bots. Anyone who posts here is punished automatically — no warning.}$p{field: ⚖️ Punishment && **{trap.action}** && inline}$p{field: 🤖 Caught && **{trap.counter}** && inline}$p{footer: {guild.name} • whitelisted roles are exempt && {guild.icon}}

A shorter one that says the same thing in a description instead of fields:

Syntax

,trap embed set <code>

Example

,trap embed set {color: #FF4747}$p{title: 🪤 Do not post here}$p{description: This channel is a trap. Anyone who posts is punished automatically — **{trap.action}**, no warning. Caught so far: **{trap.counter}**.}$p{footer: {guild.name} && {guild.icon}}

`{user}` is the bot on this embed

Nobody triggered the warning embed (it just stands there), so there is no member for {user}, {user.mention} or {user.avatar} to mean, and the bot fills in for all three. A code that greets a member here greets the bot. Save those tokens for the catch DM, which has a real person.

Two more things worth copying out of those codes:

  • Add the escalation field only when escalation is on. {trap.escalation} becomes an empty string while ,trap escalation is off, and the field is posted anyway. {field: 🔁 If repeated && **{trap.escalation}** && inline} therefore leaves the bold markers with nothing between them: a field whose value is four literal asterisks, ****. Without the bold it is quieter and no better: an empty value is padded out to an invisible character, so you get a heading with nothing under it. The default code adds this field only once escalation is on, which is why you never see either.
  • The counter is an embed and only an embed. Every embed tag works: {title:}, {description:}, {field:}, {color:}, {footer:}, {author:}, {url:}, {image:}, {thumbnail:} and {timestamp:}. What is missing is everything that renders outside an embed: {v2:} is refused when you save, and {content:}, {button:}, {gallery:}, {section:} and {separator:} produce nothing at all. Every code is rendered once before it saves, so one that produces no embed is rejected rather than quietly leaving the channel unwarned.

The four trap-only tokens, in full. Everything else in a counter code is an ordinary variable:

TokenBecomes
{trap.counter}how many accounts the trap has caught
{trap.action}the punishment, written out
{trap.channel}the trap channel's name, written #name
{trap.escalation}the escalation punishment, or nothing when escalation is off

A standing warning has no audience until someone walks into it, so write it for the member who wandered in, not for the bot that stands in for them.

Tell the person what happened

,trap dm controls the message a caught account receives. It is on by default.

Syntax

,trap dm <on|off> | invite <on|off> | embed <code|check|reset>

Example

,trap dm invite on
ArgumentDoes
on / offSends or does not send a DM
invite <on/off>Adds a single-use rejoin invite, valid for a day, to a channel that is not the trap (in softban and kick DMs only)
embed <code>Replaces the default DM with your own embed code
embed checkShows the code currently set
embed resetGoes back to the default DM

The DM is sent before a ban or kick, which is the only moment it can still be delivered.

Write the DM they actually get

Syntax

,trap dm embed <code>

Example

,trap dm embed {color: #FFA500}$p{title: 🪤 You posted in a trap channel}$p{description: {user} — you sent a message in {trap.channel} on {guild.name}, so you were **{action}**. If you are a real person, your account may be compromised: change your password and turn on 2FA.}$p{footer: {guild.name} && {guild.icon}}$p{button: link && Appeal && https://discord.com}

The bot saves the code and posts the rendered result back. Three things worth copying out of it:

  • {user} is the caught account here. This is the one trap surface with a real person behind it, so {user} renders their tag, the opposite of the warning embed above. One feature, two surfaces, one token meaning two different things.
  • {action} is a whole phrase, not one word. A softban expands to "softbanned (banned and immediately unbanned, your recent messages were removed)", so write you were **{action}** and let it finish the sentence. It also names the punishment that landed, not the one you configured: a catch that escalated says "banned".
  • The counter tokens do not reach a DM. Only {action} and {trap.channel} are filled in here; {trap.counter}, {trap.action} and {trap.escalation} belong to the warning embed. An unresolved token does not go blank: {trap.counter} in a DM posts the literal text trap.counter.

The preview is not the DM

The message the bot posts back when you save proves your code parses. It does not show you what a caught account receives, because it fills the same tokens from where you are standing:

  • {action} previews as the bare word from ,trap action: softban. The real DM uses the full phrase, and names the action actually applied, so an escalated catch says banned where your preview said softban.
  • {trap.channel} previews as your primary trap channel, always. The real DM names the channel they actually posted in, and a thread resolves to the channel above it.
  • {user} previews as you, the moderator who ran the command. The real DM has the caught account.
  • The rejoin invite is not in the preview at all. It is built at catch time and appended then.

Write the sentence so it reads correctly for the longest of those, not the shortest. Only a real catch renders the real DM.

Unlike the warning embed, a DM code may carry plain text, buttons and Components V2 as well as an embed. ++delete is accepted and then ignored: deleting a DM the member may never have opened takes away the only record of why they were punished.

The rejoin invite reaches `softban` and `kick` DMs only

Those are the two punishments that put somebody outside the server and let them walk back in, so they are the only two the invite is built for. Under ban, timeout, mute or jail, ,trap dm invite on saves happily and does nothing: no invite is created and nothing is added to the DM. The bot also needs somewhere to point it (a channel that is not a trap, where it may create an invite), so a softban DM can go out without a line even with the setting on.

When it does appear it is appended to whatever your code rendered, so never write one yourself.

The DM is the only part of the trap a caught account can still read once it is gone, so tell them what happened and where to appeal.

Run more than one trap

Add more trap channels

,trap channels takes add, remove and list, up to five channels. Each gets its own warning embed. The channel you named during setup is the primary one and cannot be removed: re-run ,trap setup to change it, which resets the list.

Syntax

,trap channels <add|remove|list> [#channel]

Example

,trap channels add #do-not-post-2

The catch count is shared across the whole server, so a catch anywhere updates every trap channel's embed.

Rename them on a schedule

,trap randomize periodically renames your trap channels so a fixed name cannot be learned and skipped. It accepts 1 to 168 hours, defaults to 24, is off by default, and needs Manage Channels. Channels the bot cannot rename are left alone.

Syntax

,trap randomize <on|off> [hours]

Example

,trap randomize on 24

Review and remove

,trap settings prints the whole configuration. ,trap stats reports what the trap has actually caught: totals by action, unique accounts, how many were repeats, and the busiest days. ,trap disable removes the trap.

Syntax

,trap stats

Syntax

,trap disable

Common issues

A moderator got banned by the trap. Whitelist them or their role with ,trap whitelist add. Permissions never grant immunity.

My ,trap command did nothing. You ran it inside a trap channel without being the server owner or whitelisted, so the message was deleted before it could be read as a command. Run it somewhere else, or whitelist yourself.

Someone posted in the trap and nothing happened. The bot could not apply the punishment: usually a missing permission, a member above it in the role hierarchy, or a mute or jail role that is not set. It reports the failure rather than recording a catch that never happened, but it reports it to your log channel, so with no log channel set, a blocked catch looks exactly like nothing happening at all. The message is still deleted either way. Set a log channel first, then re-test.

Nothing is logged. Set a log channel with ,config logchannel. With nowhere to deliver to, the bot does not warn you that a report had no destination. It simply posts nothing, which is what makes the entry above so easy to misread. Set this before you test anything.

The warning embed disappeared. Run ,trap embed resend.

My custom embed was refused. A counter embed has to be an actual embed. A code containing only text or only buttons cannot be posted as a standing message.

The tags, variables and buttons themselves are one language shared by every surface that takes a code. Embed codes is where it is explained.