Table of contents
Triggers — passive auto-responses ("tags")
GrumpyTriggers lets admins define keywords the bot automatically reacts to as soon as someone writes them in a normal chat message — no command required. This pattern is well known from Carl-bot/YAGPDB, where it's often called "tags".
The key difference from Custom Commands
This trips people up at first, so let's be very clear:
Trigger (/trigger) |
Custom Command (/cc / /cmd) |
|
|---|---|---|
| Activation | Automatic/passive — fires as soon as the keyword appears in a normal message | Active — the user has to type /cmd <name> themselves |
| Example | User writes "what's the server ip?" in chat → bot replies immediately |
User types /cmd ip → bot replies |
| Response types | text, embed |
text, embed, role-toggle |
| Cooldown | Yes, per trigger (see below) | No |
In short: a trigger "listens" to chat and fires on its own once it recognizes a keyword. A custom command does nothing at all until someone actively calls it via a slash command. Details on custom commands are in GrumpyCommands-EN.
Activation
In configs/config.yml:
addons:
triggers: true
No YAML config file — triggers live entirely in the database (TextTrigger table). /trigger add/remove/list/info are the only interface.
Step-by-step example (beginner-friendly)
Goal: Whenever someone writes the word "serverip" in chat, the bot should automatically post the Minecraft server IP — without anyone having to type a command.
1. Start the slash command:
/trigger add
Discord opens a form with four required fields. Fill them in like this:
| Field | Input | Explanation |
|---|---|---|
keyword |
serverip |
The keyword to watch for. Automatically lowercased and trimmed. |
match-type |
contains |
The trigger should also fire if "serverip" appears anywhere in the message (e.g. "what's the serverip?"). With exact, the entire message would have to be exactly "serverip" — too strict here. |
type |
text |
We want a simple text reply, not an embed. |
response |
The server IP is: play.example.com (version 1.21.x) |
The text the bot posts. |
2. Submit. The full invocation looks like this:
/trigger add keyword: serverip match-type: contains type: text response: "The server IP is: play.example.com (version 1.21.x)"
3. The bot confirms (visible only to you, ephemeral): ✅ Trigger "serverip" created.
4. Test it: Write any regular message in a channel, e.g.:
Hey, what's the serverip here anyway?
The bot replies automatically (as a reply to that message):
The server IP is: play.example.com (version 1.21.x)
Nobody had to type /cmd or any other command — the keyword in normal chat was enough. That's the whole point of a trigger.
5. Mind the cooldown: If someone writes "serverip" again right after, the bot stays silent for 15 seconds (see the Cooldown section below) — this stops the chat from getting spammed with repeated replies.
Match types: contains vs. exact
| Match type | Behavior | When to use |
|---|---|---|
contains |
Fires if the keyword appears anywhere in the (lowercased) message | The default choice — for keywords, questions, casual mentions |
exact |
Only fires if the entire message (after trim + lowercase) exactly equals the keyword | For short, unambiguous command words that shouldn't accidentally match inside longer sentences |
Examples:
Keyword: "serverip", match-type: contains
→ "what's the serverip?" → fires ✅
→ "SERVERIP please" → fires ✅ (case doesn't matter)
→ "serverip" → fires ✅
Keyword: "hi", match-type: exact
→ "hi" → fires ✅
→ "hi everyone" → does NOT fire ❌ (not the whole message)
→ "historically speaking..." → does NOT fire ❌
Keyword: "hi", match-type: contains
→ "historically speaking..." → fires ✅ (contains "hi"!) — usually NOT what you want for
short keywords, so use exact instead
Rule of thumb: Short, generic words (2-3 characters like "hi", "ok") → exact, otherwise you risk unwanted matches buried inside other words. Longer, more specific keywords (like "serverip", "discord-link") → contains, so they're also recognized inside full sentences.
Response types
text
Plain-text reply with placeholder support (same placeholders as custom commands, e.g. %user_mention%, %user_name%).
/trigger add keyword: rules match-type: contains type: text response: "%user_mention%, please check out the rules in #rules!"
embed
JSON-defined embed — just like custom commands, only title, description and color are taken from the JSON, all other fields are ignored (security constraint).
/trigger add keyword: discord match-type: exact type: embed response: '{"title":"Discord","description":"We are here: https://discord.gg/example","color":"#5865F2"}'
If the JSON is invalid or not an object, /trigger add rejects the creation immediately with an error message.
Why there is NO role-toggle response type
Custom Commands (/cc) have role-toggle; triggers deliberately do not. Reason: a trigger fires passively, triggered by plain chat text — nobody has to actively invoke anything. If a trigger could automatically grant a role, virtually any message that happened to contain the keyword could unintentionally hand out or remove roles. That's a classic privilege-escalation hole: unlike /cmd, where the user consciously and deliberately runs a command, with triggers the user often has no intention of "triggering" anything at all — they're just writing a normal message. That's why /trigger add only offers text and embed as response types; for self-assign roles, /cc add type: role-toggle remains the correct approach (see GrumpyCommands-EN).
Cooldown
Every trigger has a 15-second cooldown (default value, not currently configurable per trigger). While the cooldown is active, the trigger will not fire again — even if the keyword appears multiple times in the meantime. This prevents a busy channel from getting spammed with bot replies just by repeating the keyword.
The cooldown applies per trigger, server-wide, not per user — once trigger A fires, it stays silent for 15 seconds for everyone, regardless of who mentions the keyword next.
Server limit
Each server allows a maximum of 50 triggers (MAX_TRIGGERS_PER_GUILD in the code). Once the limit is reached, /trigger add rejects further creations with an error message until an existing trigger is deleted.
Commands
For all users
| Command | Function |
|---|---|
/trigger list |
Shows all triggers on the server (keyword, match type, response type, use counter) as an embed. Long lists are truncated with …and N more appended. |
/trigger info <keyword> |
Details for a trigger: match type, response type, uses, creator, response content. Autocomplete suggests existing keywords. |
For admins (Manage Guild)
| Command | Function |
|---|---|
/trigger add <keyword> <match-type> <type> <response> |
Create a new trigger |
/trigger remove <keyword> |
Delete a trigger (autocomplete for keyword) |
Example invocations:
/trigger add keyword: serverip match-type: contains type: text response: "play.example.com"
/trigger remove keyword: serverip
/trigger list
/trigger info keyword: serverip
Permissions: add and remove require Manage Guild. Without this permission the bot replies with 🚫 You need the "Manage Server" permission. list and info are usable by all server members.
Miscellaneous
- Keyword normalization: On creation, the keyword is automatically trimmed, lowercased, and capped at 200 characters.
- Unique per server: A duplicate keyword is rejected with
❌ A trigger for "<keyword>" already exists. - Mention safety: All trigger replies use
allowedMentions: { parse: [] }— no@everyone/@here, even if included in the response text. - In-memory cache: Triggers are reloaded into an internal cache on every change so matching stays fast on every incoming message, without querying the database per message.
- Bot messages ignored: Triggers never fire on messages from bots (including the bot's own).
GrumpyCore Wiki
⚙️ Setup
🔧 Core-Module
🆕 Neue Module
🔔 Benachrichtigungen & Utility
💬 Commands
👥 Staff
GrumpyCore Wiki (English)
⚙️ Setup
🔧 Core Modules
🆕 New Modules
🔔 Notifications & Utility
💬 Commands
👥 Staff