Skip to content

Commit 4578077

Browse files
committed
feat: Enhance warn command with confirmation, reset option, and balancing logic; Update database schema with comments
1 parent 6f1e5df commit 4578077

File tree

6 files changed

+931
-187
lines changed

6 files changed

+931
-187
lines changed

README.md

Lines changed: 67 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ This bot offers a wide range of functionalities to manage and enhance Discord se
1414
**Moderation & Administration:**
1515

1616
- **Anti-Spam System:** Automatically detects and takes action (mute, kick, ban) against spamming users based on configurable thresholds.
17-
- **Warnings (`/warn`):** Records warnings for users in the database.
17+
- **Warnings (`/warn`):** Records warnings for users in the database, with a confirmation box before execution and an option to reset warnings.
1818
- **Kick (`/kick`):** Kicks a user from the server.
1919
- **Ban (`/ban`):** Bans a user from the server.
2020
- **Timeout (`/timeout`):** Temporarily mutes a user.
21-
- **Nuke (`/nuke`):** (Extreme caution\!) Deletes all channels and roles in the server (except the command channel).
2221
- **Setup FSU (`/setupfsu`):** Creates a basic set of FSU-related roles, categories, and channels for quick server setup.
2322
- **Admin Tasks (`/addtask`, `/listtasks`, `/completetask`):** Manage administrative to-do items.
2423
- **Verified Users List (`/gotverified`):** Displays a list of verified users with their real names and college email addresses (Admin/Moderator only).
2524

2625
**Community Engagement & Information:**
2726

27+
- **Leveling/XP System:** Awards experience points (XP) for messages sent and voice chat activity, allowing users to level up and gain recognition.
2828
- **Suggestions (`/suggest`, `/approvesuggestion`, `/denysuggestion`, `/listsuggestions`):** Allows members to submit suggestions and administrators to review them.
2929
- **Reaction Roles (`/setreactionrole`, `/removereactionrole`):** Enables users to assign themselves roles by reacting to specific messages.
3030
- **FAQs (`/addfaq`, `/getfaq`, `/removefaq`):** Create and retrieve frequently asked questions.
@@ -44,64 +44,65 @@ This bot offers a wide range of functionalities to manage and enhance Discord se
4444
## 📂 Project Structure
4545

4646
```
47+
4748
.
4849
├── .env.example (Environment variables for configuration)
4950
├── package.json (Project metadata and dependencies)
5051
├── package-lock.json (Generated by npm, locks dependency versions)
5152
├── deploy-commands.js (Script to register Discord slash commands)
5253
├── Dockerfile (Instructions for building the Docker image)
5354
├── docker-compose.yml (Configuration for running the bot with Docker Compose)
54-
├── LICENSE (Information about the project's license)
55+
├── https://www.google.com/search?q=LICENSE (Information about the project's license)
5556
└── src/ (Main source code directory)
56-
├── bot.js (Main bot file: initializes client, loads commands, sets up events, schedules scraper)
57-
├── database.js (Handles SQLite DB connection and table creation)
58-
├── services/ (External service integrations)
59-
│ ├── emailService.js (Google Gmail API integration for OTP)
60-
│ └── scraper.js (Core web scraping logic for FSU notices/holidays)
61-
├── commands/ (Bot commands, categorized by type)
62-
│ └── slash/ (For Discord Slash Commands)
63-
│ ├── addFaq.js
64-
│ ├── addTask.js
65-
│ ├── allRoles.js
66-
│ ├── approveSuggestion.js
67-
│ ├── assignRole.js
68-
│ ├── ban.js
69-
│ ├── completeTask.js
70-
│ ├── confirmotp.js
71-
│ ├── denySuggestion.js
72-
│ ├── getFaq.js
73-
│ ├── gotVerified.js
74-
│ ├── help.js
75-
│ ├── holidays.js
76-
│ ├── kick.js
77-
│ ├── links.js
78-
│ ├── listSuggestions.js
79-
│ ├── listTasks.js
80-
│ ├── myStats.js
81-
│ ├── news.js
82-
│ ├── nuke.js
83-
│ ├── removeBirthday.js
84-
│ ├── removeFaq.js
85-
│ ├── removeReactionRole.js
86-
│ ├── removeRole.js
87-
│ ├── roles.js
88-
│ ├── setAntiSpam.js
89-
│ ├── setBirthday.js
90-
│ ├── setReactionRole.js
91-
│ ├── setupFSU.js
92-
│ ├── setWelcome.js
93-
│ ├── suggest.js
94-
│ ├── timeout.js
95-
│ ├── topChatters.js
96-
│ ├── topVoice.js
97-
├── viewAntiSpam.js
98-
| ── verify.js
99-
└── warn.js
100-
└── utils/
101-
├── Command.js (Base class for prefix commands)
102-
── CommandHandler.js (Manages prefix command execution, includes verified role check)
103-
└── otpGenerator.js (Utility for generating One-Time Passwords)
104-
```
57+
├── bot.js (Main bot file: initializes client, loads commands, sets up events, schedules scraper)
58+
├── database.js (Handles SQLite DB connection and table creation)
59+
├── services/ (External service integrations)
60+
│ ├── emailService.js (Google Gmail API integration for OTP)
61+
│ └── scraper.js (Core web scraping logic for FSU notices/holidays)
62+
├── commands/ (Bot commands, categorized by type)
63+
│ └── slash/ (For Discord Slash Commands)
64+
│ ├── addFaq.js
65+
│ ├── addTask.js
66+
│ ├── allRoles.js
67+
│ ├── approveSuggestion.js
68+
│ ├── assignRole.
69+
│ ├── ban.js
70+
│ ├── completeTask.js
71+
│ ├── confirmotp.js
72+
│ ├── denySuggestion.js
73+
│ ├── getFaq.js
74+
│ ├── gotVerified.js
75+
│ ├── help.js
76+
│ ├── holidays.js
77+
│ ├── kick.js
78+
│ ├── links.js
79+
│ ├── listSuggestions.js
80+
│ ├── listTasks.js
81+
│ ├── myStats.js
82+
│ ├── news.js
83+
│ ├── removeBirthday.js
84+
│ ├── removeFaq.js
85+
│ ├── removeReactionRole.js
86+
│ ├── removeRole.js
87+
│ ├── roles.js
88+
│ ├── setAntiSpam.js
89+
│ ├── setBirthday.js
90+
│ ├── setReactionRole.js
91+
│ ├── setupFSU.js
92+
│ ├── setWelcome.js
93+
│ ├── suggest.js
94+
│ ├── timeout.js
95+
│ ├── topChatters.js
96+
│ ├── topVoice.js
97+
│ ├── viewAntiSpam.js
98+
| ├── verify.js
99+
── warn.js
100+
└── utils/
101+
├── Command.js (Base class for prefix commands)
102+
├── CommandHandler.js (Manages prefix command execution, includes verified role check)
103+
── otpGenerator.js (Utility for generating One-Time Passwords)
104+
105+
````
105106
106107
## 🚀 Getting Started
107108
@@ -147,17 +148,17 @@ Follow these steps to get Pulchowk Discord Bot up and running.
147148
4. **Get Client ID:**
148149
- Go to the "General Information" tab. Copy the "Application ID". This is `CLIENT_ID`.
149150
5. **Get Guild ID (for testing):**
150-
- In Discord server, enable "Developer Mode" (User Settings -\> Advanced).
151+
- In Discord server, enable "Developer Mode" (User Settings -> Advanced).
151152
- Right-click on server icon in Discord and select "Copy ID". This is `GUILD_ID`.
152153
6. **Invite the Bot to Server:**
153-
- Go to the "OAuth2" -\> "URL Generator" tab.
154+
- Go to the "OAuth2" -> "URL Generator" tab.
154155
- Select `bot` and `applications.commands` scopes.
155156
- Under "Bot Permissions", select the following:
156157
- `Administrator` (simplest for full functionality, but grant specific permissions for production if you prefer)
157158
- Alternatively, grant specific permissions: `Manage Roles`, `Kick Members`, `Ban Members`, `Moderate Members`, `Manage Channels`, `Read Messages/View Channels`, `Send Messages`, `Embed Links`, `Attach Files`, `Add Reactions`, `Use External Emojis`, `Read Message History`, `Connect`, `Speak`, `Mute Members`, `Deafen Members`, `Move Members`.
158159
- Copy the generated URL and paste it into browser to invite the bot.
159160
7. **Create a "Verified" Role:**
160-
- In Discord server, go to Server Settings -\> Roles.
161+
- In Discord server, go to Server Settings -> Roles.
161162
- Create a new role named "Verified" (or anything you prefer).
162163
- **Copy its ID:** Right-click the role and select "Copy ID". This is `VERIFIED_ROLE_ID`. Ensure this role is positioned **below** bot's role in the server's role hierarchy so the bot can assign it.
163164
@@ -169,19 +170,19 @@ This bot uses Google APIs for email verification and holiday announcements.
169170
- Go to the [Google Cloud Console](https://console.cloud.google.com/).
170171
- Create a new project or select an existing one.
171172
2. **Enable APIs:**
172-
- In project, navigate to "APIs & Services" -\> "Enabled APIs & Services".
173+
- In project, navigate to "APIs & Services" -> "Enabled APIs & Services".
173174
- Click "+ ENABLE APIS AND SERVICES".
174175
- Search for and enable:
175176
- **Gmail API** (for sending OTP emails)
176177
- **Google Calendar API** (for `/holidays` command)
177178
3. **Create OAuth Consent Screen:**
178-
- Go to "APIs & Services" -\> "OAuth consent screen".
179+
- Go to "APIs & Services" -> "OAuth consent screen".
179180
- Configure it (choose "External" for personal use, fill in required info).
180181
- Add `https://www.googleapis.com/auth/gmail.send` as a scope.
181182
- Add `https://www.googleapis.com/auth/calendar.readonly` as a scope.
182183
- Add email as a test user.
183184
4. **Create OAuth 2.0 Client ID (for Gmail API):**
184-
- Go to "APIs & Services" -\> "Credentials".
185+
- Go to "APIs & Services" -> "Credentials".
185186
- Click "+ CREATE CREDENTIALS" and choose "OAuth client ID".
186187
- Select "Desktop app" as the application type.
187188
- Copy `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`.
@@ -195,12 +196,12 @@ This bot uses Google APIs for email verification and holiday announcements.
195196
- Set `REDIRECT_URI` in `.env` to `https://developers.google.com/oauthplayground` (or custom URI if you set one up).
196197
- Set `SENDER_EMAIL` in `.env` to the email address you want the OTPs to be sent from (must be associated with Google Workspace account).
197198
5. **Create Service Account Key (for Google Calendar API - Optional but Recommended):**
198-
- Go to "APIs & Services" -\> "Credentials".
199+
- Go to "APIs & Services" -> "Credentials".
199200
- Click "+ CREATE CREDENTIALS" and choose "Service Account".
200201
- Follow the steps to create a new service account.
201202
- Grant it the "Calendar Viewer" role (or a custom role with `calendar.events.list` permission).
202203
- After creation, click on the service account email.
203-
- Go to the "Keys" tab and click "ADD KEY" -\> "Create new key".
204+
- Go to the "Keys" tab and click "ADD KEY" -> "Create new key".
204205
- Select "JSON" and click "CREATE". A JSON file will download.
205206
- **Rename this file to `service_account_key.json`** and place it in the **root directory of bot project**.
206207
- Set `GOOGLE_SERVICE_ACCOUNT_KEY_PATH="./service_account_key.json"` in `.env`.
@@ -226,7 +227,7 @@ GOOGLE_CLIENT_ID="_GOOGLE_CLIENT_ID_HERE"
226227
# GOOGLE_CLIENT_SECRET (from Google Cloud Console -> APIs & Services -> Credentials -> OAuth 2.0 Client IDs)
227228
GOOGLE_CLIENT_SECRET="_GOOGLE_CLIENT_SECRET_HERE"
228229
# Redirect URI used during OAuth2 consent screen setup (e.g., [https://developers.google.com/oauthplayground](https://developers.google.com/oauthplayground))
229-
REDIRECT_URI="https://developers.google.com/oauthplayground" # Or custom redirect URI
230+
REDIRECT_URI="[https://developers.google.com/oauthplayground](https://developers.google.com/oauthplayground)" # Or custom redirect URI
230231
# Refresh Token generated from OAuth2 Playground with [https://www.googleapis.com/auth/gmail.send](https://www.googleapis.com/auth/gmail.send) scope
231232
REFRESH_TOKEN="_GOOGLE_REFRESH_TOKEN_HERE"
232233
# The email address from college Workspace that will send the OTP emails
@@ -257,7 +258,7 @@ BIRTHDAY_ANNOUNCEMENT_CHANNEL_ID="_BIRTHDAY_ANNOUNCEMENT_CHANNEL_ID_HERE"
257258
258259
# --- Bot Prefix for traditional commands (e.g., !help) ---
259260
BOT_PREFIX="!"
260-
```
261+
````
261262
262263
### 4\. Installation
263264
@@ -329,7 +330,6 @@ npm start
329330
| `/kick @user [reason]` | Kicks a user from the server (Moderator). | `/kick @User Rule break` |
330331
| `/timeout @user <duration> [reason]` | Times out a user (Moderator). | `/timeout @User 5m Misbehaving` |
331332
| `/warn @user [reason]` | Issues a warning to a user (Moderator). | `/warn @User Off-topic` |
332-
| `/nuke` | **(EXTREME CAUTION\!)** Deletes all channels and roles. Requires confirmation. (Server Owner Only) | `/nuke` |
333333
| `/setantispam [setting <value>] ...` | Configures anti-spam settings (Admin). | `/setantispam message_limit 7 time_window_seconds 10` |
334334
| `/viewantispam` | Views current anti-spam settings (Admin). | `/viewantispam` |
335335
| `/gotverified` | Displays a list of verified users with their real names and college email addresses (Admin/Moderator).
@@ -434,7 +434,7 @@ To host your Pulchowk Discord Bot on a local server using Docker, follow these s
434434
VERIFIED_ROLE_ID="_VERIFIED_ROLE_ID_HERE"
435435
GOOGLE_CLIENT_ID="_GOOGLE_CLIENT_ID_HERE"
436436
GOOGLE_CLIENT_SECRET="_GOOGLE_CLIENT_SECRET_HERE"
437-
REDIRECT_URI="https://developers.google.com/oauthplayground"
437+
REDIRECT_URI="[https://developers.google.com/oauthplayground](https://developers.google.com/oauthplayground)"
438438
REFRESH_TOKEN="_GOOGLE_REFRESH_TOKEN_HERE"
439439
SENDER_EMAIL="-college-email@pulchowk.edu.np"
440440
GOOGLE_SERVICE_ACCOUNT_KEY_PATH="./service_account_key.json"
@@ -560,5 +560,6 @@ Contributions are welcome\! If you have suggestions for improvements or new feat
560560
- Open a Pull Request.
561561

562562
## 📄 License
563-
564-
This project is licensed under the [No Redistribution License](LICENSE).
563+
```
564+
This project is licensed under the [No Redistribution License](https://www.google.com/search?q=LICENSE).
565+
```

src/commands/slash/clean.js

Lines changed: 67 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,51 @@ import {
88
function parseDuration(duration) {
99
const match = duration.match(/^(\d+)([smhd])$/);
1010
if (!match) return null;
11-
const [_, num, unit] = match;
12-
const multiplier = { s: 1000, m: 60000, h: 3600000, d: 86400000 }[unit];
13-
return parseInt(num) * multiplier;
14-
}
11+
const [_, num, unit] = match;
12+
const multiplier = { s: 1000, m: 60000, h: 3600000, d: 86400000 }[unit];
13+
return parseInt(num) * multiplier;
14+
}
1515

16-
export const data = new SlashCommandBuilder()
17-
.setName('clean')
18-
.setDescription('Deletes messages within a specific time frame from a channel.')
19-
.addStringOption(option =>
20-
option.setName('duration')
21-
.setDescription('Duration like 10m, 1h, 2d (up to 14d)')
22-
.setRequired(true))
23-
.addStringOption(option =>
24-
option.setName('reason')
25-
.setDescription('Reason for message deletion')
26-
.setRequired(false))
27-
.addChannelOption(option =>
28-
option.setName('channel')
29-
.setDescription('Channel to clean (default is current)')
30-
.addChannelTypes(ChannelType.GuildText)
31-
.setRequired(false))
32-
.setDefaultMemberPermissions(PermissionsBitField.Flags.ManageGuild);
16+
export const data = new SlashCommandBuilder()
17+
.setName('clean')
18+
.setDescription('Deletes messages within a specific time frame from a channel.')
19+
.addStringOption(option =>
20+
option.setName('duration')
21+
.setDescription('Duration like 10m, 1h, 2d (up to 14d)')
22+
.setRequired(true))
23+
.addIntegerOption(option =>
24+
option.setName('count')
25+
.setDescription('Number of messages to delete (up to 100)')
26+
.setRequired(false)
27+
.setMinValue(1)
28+
.setMaxValue(100))
29+
.addUserOption(option =>
30+
option.setName('target_user')
31+
.setDescription('Only delete messages from a specific user')
32+
.setRequired(false))
33+
.addStringOption(option =>
34+
option.setName('reason')
35+
.setDescription('Reason for message deletion')
36+
.setRequired(false))
37+
.addChannelOption(option =>
38+
option.setName('channel')
39+
.setDescription('Channel to clean (default is current)')
40+
.addChannelTypes(ChannelType.GuildText)
41+
.setRequired(false))
42+
.setDefaultMemberPermissions(PermissionsBitField.Flags.ManageGuild);
3343

34-
export async function execute(interaction) {
44+
export async function execute(interaction) {
3545
const durationInput = interaction.options.getString('duration');
46+
const count = interaction.options.getInteger('count');
47+
const targetUser = interaction.options.getUser('target_user');
3648
const reason = interaction.options.getString('reason') || 'No reason provided.';
3749
const targetChannel = interaction.options.getChannel('channel') || interaction.channel;
3850

3951
const durationMs = parseDuration(durationInput);
4052
if (!durationMs || durationMs > 14 * 24 * 60 * 60 * 1000) {
4153
return interaction.reply({
42-
content: '❌ Invalid duration. Use `s`, `m`, `h`, or `d` (up to 14 days). Example: `30m`, `2h`',
43-
ephemeral: true
54+
content: '❌ Invalid duration. Use `s`, `m`, `h`, or `d` (up to 14 days). Example: `30m`, `2h`',
55+
ephemeral: true
4456
});
4557
}
4658

@@ -51,34 +63,50 @@ function parseDuration(duration) {
5163
await interaction.deferReply({ ephemeral: true });
5264

5365
const messages = await targetChannel.messages.fetch({ limit: 100 });
54-
const toDelete = messages.filter(msg =>
55-
msg.createdTimestamp >= threshold &&
56-
msg.deletable
66+
let toDelete = messages.filter(msg =>
67+
msg.createdTimestamp >= threshold &&
68+
msg.deletable
5769
);
5870

71+
if (targetUser) {
72+
toDelete = toDelete.filter(msg => msg.author.id === targetUser.id);
73+
}
74+
75+
if (count) {
76+
toDelete = toDelete.first(count);
77+
}
78+
5979
const deleteResults = [];
6080
for (const msg of toDelete.values()) {
61-
deleteResults.push(msg.delete().catch(() => null));
81+
deleteResults.push(msg.delete().catch(() => null));
6282
}
6383
const results = await Promise.all(deleteResults);
6484
const deletedCount = results.filter(Boolean).length;
6585

6686
const embed = new EmbedBuilder()
67-
.setColor('#00ffff')
68-
.setTitle('🧹 Clean Complete')
69-
.addFields(
70-
{ name: '🕒 Duration', value: durationInput, inline: true },
71-
{ name: '📄 Reason', value: reason, inline: true },
72-
{ name: '📺 Channel', value: `${targetChannel}`, inline: true },
73-
{ name: '🧮 Deleted', value: `${deletedCount} messages`, inline: true }
74-
)
75-
.setTimestamp();
87+
.setColor('#00ffff')
88+
.setTitle('🧹 Clean Complete')
89+
.addFields(
90+
{ name: '🕒 Duration', value: durationInput, inline: true },
91+
{ name: '📄 Reason', value: reason, inline: true },
92+
{ name: '📺 Channel', value: `${targetChannel}`, inline: true },
93+
{ name: '🧮 Deleted', value: `${deletedCount} messages`, inline: true }
94+
);
95+
96+
if (targetUser) {
97+
embed.addFields({ name: '👤 Targeted User', value: `${targetUser}`, inline: true });
98+
}
99+
if (count) {
100+
embed.addFields({ name: '🔢 Messages to Target', value: `${count}`, inline: true });
101+
}
102+
103+
embed.setTimestamp();
76104

77105
await interaction.editReply({ embeds: [embed] });
78106
} catch (err) {
79107
console.error('Error during clean:', err);
80108
await interaction.editReply({
81-
content: '❌ Failed to clean messages. Check my permissions.',
109+
content: '❌ Failed to clean messages. Check my permissions.',
82110
});
83111
}
84-
}
112+
}

0 commit comments

Comments
 (0)