Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,23 @@ Can be used to repeat the same 'talk' command without typing it out again.

---

## Automatic Features

### AutoMod Response

The bot automatically monitors Discord's AutoMod system and responds when messages are blocked for profanity or custom keyword violations. When a user's message is deleted by AutoMod due to banned words, the bot will:

- Post a lighthearted "Tsk tsk" message mentioning the user
- Display a humorous image

This feature works automatically with your server's existing AutoMod rules - no configuration needed. It only responds to keyword-based message deletions (profanity filters and custom word lists).

---

### Uptime

The bot may be [configured](#configure-the-bot) to automatically ping an uptime monitor of your choosing every few minutes, to keep easier track of server downtime.

## Usage or Development

If you've read this far, and don't plan to run or develop on the bot yourself, or are not curious how to do so, you may leave now. This part is quite boring. But feel free to read on anyway!
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "csbot",
"version": "0.17.0",
"version": "0.19.0",
"private": true,
"description": "The One beneath the Supreme Overlord's rule. A bot to help manage the BYU CS Discord, successor to Ze Kaiser (https://github.com/arkenstorm/ze-kaiser)",
"keywords": [
Expand Down Expand Up @@ -88,4 +88,4 @@
"engines": {
"node": ">=20.6.0"
}
}
}
Binary file added src/assets/christian-server.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 61 additions & 0 deletions src/events/autoModerationActionExecution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { AttachmentBuilder, AutoModerationActionType, AutoModerationRuleTriggerType } from 'discord.js';
import { fileURLToPath } from 'node:url';
import path from 'node:path';

import { onEvent } from '../helpers/onEvent.js';
import { debug, error, info } from '../logger.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const assetsDir = path.resolve(__dirname, '..', 'assets');

/**
* The event handler for AutoMod action executions
* Sends an image when AutoMod deletes a message due to profanity or custom keywords
*/
export const autoModerationActionExecution = onEvent('autoModerationActionExecution', {
once: false,
async execute(actionExecution) {
try {
// Check if the action was blocking a message (deletion)
const isBlockMessage = actionExecution.action.type === AutoModerationActionType.BlockMessage;

if (!isBlockMessage) {
// Not a message deletion, ignore
return;
}

// Check if the trigger was keyword-based (profanity or custom word list)
const isKeywordTrigger =
actionExecution.ruleTriggerType === AutoModerationRuleTriggerType.Keyword ||
actionExecution.ruleTriggerType === AutoModerationRuleTriggerType.KeywordPreset;

if (!isKeywordTrigger) {
// Not a keyword trigger, ignore
debug('AutoMod action was not keyword-based, ignoring');
return;
}

// Get the channel where the infraction happened
const channel = actionExecution.channel;
if (!channel?.isTextBased()) {
debug('AutoMod action occurred in non-text channel, ignoring');
return;
}

info(
`AutoMod blocked message in ${channel.name} - Rule: ${actionExecution.autoModerationRule?.name ?? 'Unknown'}, Matched: "${actionExecution.matchedKeyword}"`
);

// Send a message mentioning the user with the Christian server image
const attachment = new AttachmentBuilder(path.join(assetsDir, 'christian-server.jpg'), {
name: 'christian-server.jpg',
});
await channel.send({
content: `Tsk tsk, <@${actionExecution.userId}>`,
files: [attachment],
});
} catch (error_) {
error('Failed to handle AutoMod action execution:', error_);
}
},
});
2 changes: 2 additions & 0 deletions src/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ export function registerEventHandlers(client: Client): void {
}

// Install event handlers
import { autoModerationActionExecution } from './autoModerationActionExecution.js';
import { error } from './error.js';
import { interactionCreate } from './interactionCreate.js';
import { messageReactionAdd } from './messageReactionAdd.js';
import { messageReactionRemove } from './messageReactionRemove.js';
import { ready } from './ready.js';

_add(autoModerationActionExecution as EventHandler);
_add(error as EventHandler);
_add(interactionCreate as EventHandler);
_add(messageReactionAdd as EventHandler);
Expand Down
1 change: 1 addition & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export async function _main(): Promise<void> {
GatewayIntentBits.GuildMessageTyping,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.MessageContent,
GatewayIntentBits.AutoModerationExecution,
],
partials: [Partials.Reaction, Partials.Channel, Partials.Message],
allowedMentions: {
Expand Down