Initial commit
This commit is contained in:
commit
c222b0db1d
2
.env.example
Normal file
2
.env.example
Normal file
@ -0,0 +1,2 @@
|
||||
TOKEN=
|
||||
CLIENT=
|
11
.eslintignore
Normal file
11
.eslintignore
Normal file
@ -0,0 +1,11 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
12
.eslintrc.json
Normal file
12
.eslintrc.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "prettier"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {}
|
||||
}
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
11
.prettierignore
Normal file
11
.prettierignore
Normal file
@ -0,0 +1,11 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
8
.prettierrc
Normal file
8
.prettierrc
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": [],
|
||||
"overrides": []
|
||||
}
|
11
README.md
Normal file
11
README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# A DiscordJS Template
|
||||
|
||||
## Getting started
|
||||
|
||||
- Create a new repository from this template.
|
||||
- Remove example commands:
|
||||
- Either permanently delete directory `commands/examples/`
|
||||
- Or move `commands/examples/` elsewhere, commit deletion, add to [`.gitignore`](.gitignore) and move back in
|
||||
- Look through, copy and/or modify the [examples](commands/examples/).
|
||||
- Either read a [guide](https://discordjs.guide/)!
|
||||
- Or read the [docs](https://discord.js.org/docs)!
|
36
commands/examples/login.example.js
Normal file
36
commands/examples/login.example.js
Normal file
@ -0,0 +1,36 @@
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
ModalBuilder,
|
||||
SlashCommandBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle
|
||||
} from 'discord.js';
|
||||
|
||||
export const data = new SlashCommandBuilder()
|
||||
.setName('login')
|
||||
.setDescription('Opens a login pop-up.');
|
||||
export async function modalSubmit(interaction) {
|
||||
await interaction.reply({ content: 'Successfully submitted Form!', ephemeral: true });
|
||||
}
|
||||
export async function execute(interaction) {
|
||||
const modal = new ModalBuilder().setCustomId('login-modal').setTitle('Login Form');
|
||||
|
||||
const user = new ActionRowBuilder().addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('user')
|
||||
.setLabel('Enter username:')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setRequired(true)
|
||||
);
|
||||
const password = new ActionRowBuilder().addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('password')
|
||||
.setLabel('Enter password:')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setRequired(true)
|
||||
);
|
||||
|
||||
modal.addComponents(user, password);
|
||||
|
||||
await interaction.showModal(modal);
|
||||
}
|
6
commands/examples/ping.example.js
Normal file
6
commands/examples/ping.example.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { SlashCommandBuilder } from 'discord.js';
|
||||
|
||||
export const data = new SlashCommandBuilder().setName('ping').setDescription('Replies with Pong!');
|
||||
export async function execute(interaction) {
|
||||
await interaction.reply({ content: 'Pong!', ephemeral: true });
|
||||
}
|
42
commands/examples/role.example.js
Normal file
42
commands/examples/role.example.js
Normal file
@ -0,0 +1,42 @@
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
ComponentType,
|
||||
RoleSelectMenuBuilder,
|
||||
SlashCommandBuilder
|
||||
} from 'discord.js';
|
||||
|
||||
export const data = new SlashCommandBuilder()
|
||||
.setName('role')
|
||||
.setDMPermission(false)
|
||||
.setDescription('Provides a role selector.');
|
||||
export async function execute(interaction) {
|
||||
const roles = await interaction.guild.roles.fetch();
|
||||
const choices = roles.filter((r) => r.name.startsWith('test')).map((r) => r.id);
|
||||
|
||||
const button = new RoleSelectMenuBuilder()
|
||||
.setMinValues(1)
|
||||
.setMaxValues(25)
|
||||
.setCustomId('role')
|
||||
.setDefaultRoles(choices)
|
||||
.setPlaceholder('Select at least one role.');
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(button);
|
||||
|
||||
const response = await interaction.reply({
|
||||
components: [row],
|
||||
ephemeral: true
|
||||
});
|
||||
|
||||
const collector = response.createMessageComponentCollector({
|
||||
componentType: ComponentType.RoleSelect,
|
||||
time: 120_000
|
||||
});
|
||||
|
||||
collector.on('collect', async (i) => {
|
||||
const selection = roles
|
||||
.filter((r) => i.values.includes(r.id))
|
||||
.map((r) => r.name)
|
||||
.join(', ');
|
||||
await i.reply({ content: `You have selected: "${selection}".`, ephemeral: true });
|
||||
});
|
||||
}
|
71
deploy.js
Normal file
71
deploy.js
Normal file
@ -0,0 +1,71 @@
|
||||
import { REST, Routes } from 'discord.js';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { promisify } from 'util';
|
||||
import { config } from 'dotenv';
|
||||
import { readdir } from 'fs';
|
||||
|
||||
config();
|
||||
const readdirAsync = promisify(readdir);
|
||||
|
||||
const required = ['data', 'execute'];
|
||||
const optional = ['autocomplete', 'modalSubmit'];
|
||||
|
||||
// Construct and prepare an instance of the REST module
|
||||
const rest = new REST().setToken(process.env.TOKEN);
|
||||
|
||||
// and deploy your commands!
|
||||
const putCommands = async (commands) => {
|
||||
try {
|
||||
console.info(`[INFO] Started refreshing ${commands.length} application (/) commands.`);
|
||||
|
||||
// The put method is used to fully refresh all commands in the guild with the current set
|
||||
const data = await rest.put(Routes.applicationCommands(process.env.CLIENT), { body: commands });
|
||||
|
||||
console.info(`[INFO] Successfully reloaded ${data.length} application (/) commands.`);
|
||||
} catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
console.error('[ERROR] Uncaught error:');
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
// Register commands from sub-directories
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const mainPath = join(__dirname, 'commands');
|
||||
readdirAsync(mainPath)
|
||||
.then(
|
||||
async (categories) =>
|
||||
// For each category directory
|
||||
await Promise.all(
|
||||
categories.map(async (category) => {
|
||||
const catPath = join(mainPath, category);
|
||||
const files = (await readdirAsync(catPath)).filter((file) => file.endsWith('.js') && !file.endsWith('.example.js'));
|
||||
// For each command file
|
||||
return await Promise.all(
|
||||
files.map(async (current) => {
|
||||
const filePath = join(catPath, current);
|
||||
const command = await import(filePath);
|
||||
// Warn incomplete commands
|
||||
if (!required.every((name) => name in command)) {
|
||||
console.error(
|
||||
`[ERROR] The command at ${filePath} is missing a required "data" or "execute" property.`
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
const properties = optional.filter((name) => !(name in command));
|
||||
if (properties.length > 0)
|
||||
properties.forEach((name) =>
|
||||
console.warn(
|
||||
`[WARNING] The command at ${filePath} is missing a optional "${name}" property.`
|
||||
)
|
||||
);
|
||||
// Add command to collection
|
||||
return command.data.toJSON();
|
||||
})
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
.then((commands) => commands.reduce((a, i) => a.concat(i), []).filter((e) => e !== 0))
|
||||
.then(putCommands);
|
61
events/interactionCreate.js
Normal file
61
events/interactionCreate.js
Normal file
@ -0,0 +1,61 @@
|
||||
import { Events } from 'discord.js';
|
||||
|
||||
const chatInputCommand = async (interaction, command) => {
|
||||
// Try executing command
|
||||
try {
|
||||
console.info(`[INFO] Command ${interaction.commandName} was executed.`);
|
||||
await command.execute(interaction);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
// Follow up/reply with error message
|
||||
if (interaction.replied || interaction.deferred)
|
||||
await interaction.followUp({
|
||||
content: 'There was an error while executing this command!',
|
||||
ephemeral: true
|
||||
});
|
||||
else
|
||||
await interaction.reply({
|
||||
content: 'There was an error while executing this command!',
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const genericExecute = async (interaction, command, name, description) => {
|
||||
try {
|
||||
console.info(`[INFO] Command ${interaction.commandName} ${description ?? `used "${name}"`}.`);
|
||||
await command[name](interaction);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const name = Events.InteractionCreate;
|
||||
export async function execute(interaction) {
|
||||
let command = interaction.client.commands.get(interaction.commandName);
|
||||
|
||||
// Execute slash commands
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await chatInputCommand(interaction, command);
|
||||
return;
|
||||
}
|
||||
// Autocomplete input
|
||||
if (interaction.isAutocomplete() && 'autocomplete' in command) {
|
||||
await genericExecute(interaction, command, 'autocomplete');
|
||||
return;
|
||||
}
|
||||
// Modeal submit event
|
||||
if (interaction.isModalSubmit()) {
|
||||
const name = interaction.customId.split('-')[0];
|
||||
command = interaction.client.commands.get(name);
|
||||
|
||||
await genericExecute(interaction, command, 'modalSubmit', 'submitted a modal');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if command exists
|
||||
if (interaction.commandName && !command) {
|
||||
console.warn(`[WARNING] No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
}
|
||||
}
|
7
events/ready.js
Normal file
7
events/ready.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { Events } from 'discord.js';
|
||||
|
||||
export const name = Events.ClientReady;
|
||||
export const once = true;
|
||||
export function execute(client) {
|
||||
console.info(`[INFO] Ready! Logged in as ${client.user.tag}`);
|
||||
}
|
81
index.js
Normal file
81
index.js
Normal file
@ -0,0 +1,81 @@
|
||||
import { Client, Collection, GatewayIntentBits } from 'discord.js';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { promisify } from 'util';
|
||||
import { config } from 'dotenv';
|
||||
import { readdir } from 'fs';
|
||||
|
||||
config();
|
||||
const readdirAsync = promisify(readdir);
|
||||
|
||||
const required = ['data', 'execute'];
|
||||
const optional = ['autocomplete', 'modalSubmit'];
|
||||
|
||||
const runClient = (commands, events) => {
|
||||
// Create a new client instance
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
client.commands = new Collection();
|
||||
commands.forEach((c) => client.commands.set(c.data.name, c));
|
||||
events.forEach((e) =>
|
||||
e.once
|
||||
? client.once(e.name, (...args) => e.execute(...args))
|
||||
: client.on(e.name, (...args) => e.execute(...args))
|
||||
);
|
||||
|
||||
// Log in to Discord with your client's token
|
||||
client.login(process.env.TOKEN);
|
||||
};
|
||||
|
||||
// Register commands from sub-directories
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const cmdPath = join(__dirname, 'commands');
|
||||
const evtPath = join(__dirname, 'events');
|
||||
readdirAsync(cmdPath)
|
||||
.then(
|
||||
async (categories) =>
|
||||
// For each category directory
|
||||
await Promise.all(
|
||||
categories.map(async (category) => {
|
||||
const catPath = join(cmdPath, category);
|
||||
const content = await readdirAsync(catPath);
|
||||
const files = content.filter((file) => file.endsWith('.js') && !file.endsWith('.example.js'));
|
||||
// For each command file
|
||||
return await Promise.all(
|
||||
files.map(async (current) => {
|
||||
const filePath = join(catPath, current);
|
||||
const command = await import(filePath);
|
||||
// Warn incomplete commands
|
||||
if (!required.every((name) => name in command)) {
|
||||
console.error(
|
||||
`[ERROR] The command at ${filePath} is missing a required "data" or "execute" property.`
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
const properties = optional.filter((name) => !(name in command));
|
||||
if (properties.length > 0)
|
||||
properties.forEach((name) =>
|
||||
console.warn(
|
||||
`[WARNING] The command at ${filePath} is missing a optional "${name}" property.`
|
||||
)
|
||||
);
|
||||
// Add command to collection
|
||||
return command;
|
||||
})
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
.then((commands) => commands.reduce((a, i) => a.concat(i), []).filter((e) => e !== 0))
|
||||
.then(async (commands) => {
|
||||
const content = await readdirAsync(evtPath);
|
||||
const files = content.filter((file) => file.endsWith('.js'));
|
||||
const events = await Promise.all(
|
||||
files.map(async (current) => {
|
||||
const filePath = join(evtPath, current);
|
||||
const command = await import(filePath);
|
||||
// Add event to collection
|
||||
return command;
|
||||
})
|
||||
);
|
||||
runClient(commands, events);
|
||||
});
|
1711
package-lock.json
generated
Normal file
1711
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
package.json
Normal file
26
package.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "discord-bot",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node .",
|
||||
"deploy": "node deploy.js",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"discord.js": "^14.14.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"proxy-agent": "^6.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"prettier": "^3.1.1"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
Loading…
Reference in New Issue
Block a user