Skip to content

Commit eb50ded

Browse files
authored
Merge pull request #1 from MonashDeepNeuron/dev
Dev
2 parents da83d7a + 512c0e8 commit eb50ded

File tree

4 files changed

+154
-3
lines changed

4 files changed

+154
-3
lines changed

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
11
# discord-bot
22
Discord bot for MDN discord server.
3-
Requires node v18+, run npm install to get required modules
3+
Requires node v18+, run `npm install` to get required modules.
4+
5+
Run `node ./index.js` to start the application.
6+
7+
If a slash command is added/removed or any command **data** is modified (i.e. name, description, parameters), then run `node ./deploy_commands.js` to sync this information with the development server on Discord. Use the `-global` flag to deploy these commands globally (to every Discord server the bot is in).
8+
9+
## Config File
10+
The bot will need a *config.json* file in the root of the folder, in the following form:
11+
```json
12+
{
13+
"token": "insert-token-here",
14+
"clientId": "insert-client-id",
15+
"guildId": "insert-guild-id"
16+
}
17+
```
18+
See [Configuration files](https://discordjs.guide/creating-your-bot/#configuration-files) for information on getting the Bot **token**.
19+
Also see [Guild commands](https://discordjs.guide/creating-your-bot/command-deployment.html#guild-commands) for where to find the **clientId** and **guildId**, where the guild is the development server of the bot.
20+
21+
## Bot Permissions
22+
The bot will require the `bot` and `application.commands` scopes to function properly (see [here](https://discordjs.guide/preparations/adding-your-bot-to-servers.html#creating-and-using-your-invite-link) for more info), as well as the following permissions in the Discord server:
23+
- Manage Channels
24+
- Manage Roles
25+
- Manage Nicknames
26+
- Mention Everyone
27+
- Use Application Commands

commands/utility/archive-channel.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const { SlashCommandBuilder, PermissionFlagsBits, PermissionsBitField } = require('discord.js');
2+
const { archiveId, clientId } = require('../../config.json')
3+
4+
module.exports = {
5+
data: new SlashCommandBuilder()
6+
.setName('archive-channel')
7+
.setDescription('Archives a channel')
8+
.setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels)
9+
10+
// Get the channel
11+
.addChannelOption(option =>
12+
option.setName('channel')
13+
.setDescription('Channel to archive')
14+
.setRequired(true)),
15+
16+
17+
async execute(interaction) {
18+
const channel = interaction.options.getChannel('channel');
19+
20+
try {
21+
// Update permissions to only allow bot to see
22+
await channel.permissionOverwrites.set([
23+
{
24+
id: interaction.guild.id,
25+
deny: [PermissionsBitField.Flags.ViewChannel],
26+
},
27+
{
28+
id: clientId,
29+
allow: [PermissionsBitField.Flags.ViewChannel],
30+
}
31+
]);
32+
33+
await channel.setParent(archiveId); // Move channel
34+
// Remove bot's permissions
35+
// await channel.permissionOverwrites.edit(clientId, { ViewChannel: false });
36+
37+
await interaction.reply(`<#${channel.id}> was archived successfully`);
38+
39+
} catch (error) {
40+
console.error(error);
41+
await interaction.reply( {content: 'There was an error archiving this channel.', ephemeral: true } );
42+
}
43+
}
44+
}

commands/utility/start-project.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
const { SlashCommandBuilder, PermissionFlagsBits, ChannelType, PermissionsBitField } = require("discord.js");
2+
3+
module.exports = {
4+
data: new SlashCommandBuilder()
5+
.setName('start-project')
6+
.setDescription('Create a new channel and role for a project.')
7+
.setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels | PermissionFlagsBits.ManageRoles)
8+
9+
// Get the channel name
10+
.addStringOption(option =>
11+
option.setName('project-name')
12+
.setDescription('Name of the new channel')
13+
.setRequired(true)
14+
.setMaxLength(30) // Max channel name length that can be displayed
15+
)
16+
17+
// Get the colour of the role
18+
.addStringOption(option =>
19+
option.setName('colour')
20+
.setDescription('Hex color code (e.g: #3E99C6) for the role')
21+
.setRequired(false)
22+
.setMinLength(7)
23+
.setMaxLength(7)
24+
),
25+
26+
async execute(interaction) {
27+
// Get user arguments
28+
const name = interaction.options.getString('project-name');
29+
const color = interaction.options.getString('colour') ?? '#FFFFFF';
30+
const guild = interaction.guild;
31+
32+
// Validate color input using regular expression
33+
if (!/^#([0-9A-F]{3}){1,2}$/i.test(color)) {
34+
return interaction.reply({ content: 'Invalid colour code. Please provide a valid hex colour code (e.g., #FF5733).', ephemeral: true });
35+
}
36+
37+
try {
38+
// Create role
39+
const createdRole = await guild.roles.create({
40+
name,
41+
permissions: [PermissionsBitField.Flags.SendMessages],
42+
mentionable: true,
43+
color,
44+
})
45+
46+
// Create object with channel data
47+
const newChannel = {
48+
name,
49+
type: ChannelType.GuildText,
50+
permissionOverwrites: [
51+
{
52+
id: guild.id,
53+
deny: [PermissionsBitField.Flags.ViewChannel],
54+
},
55+
{
56+
id: createdRole.id,
57+
allow: [PermissionsBitField.Flags.ViewChannel],
58+
}
59+
],
60+
};
61+
62+
// Create the channel
63+
const createdChannel = !interaction.channel.parent ?
64+
await interaction.guild.channels.create(newChannel) : // If command was run in a stray channel
65+
await interaction.channel.parent.children.create(newChannel); // Otherwise create the channel in the same category
66+
67+
await interaction.reply(`The project **${name}** has been created, ` +
68+
`with private channel <#${createdChannel.id}> ` +
69+
`and role <@&${createdRole.id}>!`);
70+
71+
} catch(error) {
72+
console.error(error);
73+
await interaction.reply( {content: 'There was an error creating this project.', ephemeral: true } );
74+
}
75+
}
76+
}

deploy_commands.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,19 @@ const { clientId, guildId, token } = require('./config.json');
33
const fs = require('node:fs');
44
const path = require('node:path');
55

6+
// Check for flags
7+
const global = process.argv.includes('-global');
8+
const testing = process.argv.includes('-testing')
9+
610
const commands = [];
711
// Grab all the command folders from the commands directory
812
const foldersPath = path.join(__dirname, 'commands');
913
const commandFolders = fs.readdirSync(foldersPath);
1014

1115
for (const folder of commandFolders) {
16+
// Don't deploy the commands in 'testing/' if -testing flag isn't used
17+
if (!testing && folder === 'testing') continue;
18+
1219
// Grab all the command files from the commands directory
1320
const commandsPath = path.join(foldersPath, folder);
1421
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
@@ -34,8 +41,8 @@ const rest = new REST().setToken(token);
3441

3542
// The put method is used to fully refresh all commands in the guild with the current set
3643
const data = await rest.put(
37-
Routes.applicationGuildCommands(clientId, guildId),
38-
// change above to Routes.applicationCommands(clientId) to make commands global
44+
// Deploy commands either to globally or to dev server
45+
global ? Routes.applicationCommands(clientId) : Routes.applicationGuildCommands(clientId, guildId),
3946
{ body: commands },
4047
);
4148

0 commit comments

Comments
 (0)