Initial commit
This commit is contained in:
commit
9f0781bf02
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
env.dev
|
||||
env.prod
|
||||
.env
|
49
.eslintrc.json
Normal file
49
.eslintrc.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2021
|
||||
},
|
||||
"rules": {
|
||||
"arrow-spacing": ["warn", { "before": true, "after": true }],
|
||||
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"comma-spacing": "error",
|
||||
"comma-style": "error",
|
||||
"curly": ["error", "multi-line", "consistent"],
|
||||
"dot-location": ["error", "property"],
|
||||
"handle-callback-err": "off",
|
||||
"indent": ["error", "tab"],
|
||||
"keyword-spacing": "error",
|
||||
"max-nested-callbacks": ["error", { "max": 4 }],
|
||||
"max-statements-per-line": ["error", { "max": 2 }],
|
||||
"no-console": "off",
|
||||
"no-empty-function": "error",
|
||||
"no-floating-decimal": "error",
|
||||
"no-inline-comments": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-multi-spaces": "error",
|
||||
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
|
||||
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }],
|
||||
"no-trailing-spaces": ["error"],
|
||||
"no-var": "error",
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"prefer-const": "error",
|
||||
"quotes": ["error", "single"],
|
||||
"semi": ["error", "always"],
|
||||
"space-before-blocks": "error",
|
||||
"space-before-function-paren": ["error", {
|
||||
"anonymous": "never",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
}],
|
||||
"space-in-parens": "error",
|
||||
"space-infix-ops": "error",
|
||||
"space-unary-ops": "error",
|
||||
"spaced-comment": "error",
|
||||
"yoda": "error"
|
||||
}
|
||||
}
|
29
.github/workflows/docker-image.yml
vendored
Normal file
29
.github/workflows/docker-image.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Bot Dockerization
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
|
||||
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build the Docker image
|
||||
run: docker build . --file Dockerfile --tag v0idf1sh/tag
|
||||
- name: Log into Docker Hub
|
||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||
- name: Push image to Docker Hub
|
||||
run: docker push v0idf1sh/tag
|
||||
- name: Set up a skeleton .env file
|
||||
run: echo "TOKEN=${{secrets.TOKEN}}" > .env && echo "BOTID=${{ secrets.BOTID }}" >> .env
|
||||
- name: Install modules
|
||||
run: npm i
|
||||
- name: Refresh commands with Discord
|
||||
run: node modules/_deploy-global.js
|
118
.gitignore
vendored
Normal file
118
.gitignore
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
# IDE Config Files
|
||||
.vscode
|
||||
package-lock.json
|
||||
.VSCodeCounter/
|
||||
env.dev
|
||||
env.prod
|
||||
.DS_Store
|
||||
|
||||
# Custom folders
|
||||
# gifs/*
|
||||
# pastas/*
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
.env.dev
|
||||
.env.prod
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
8
Dockerfile
Normal file
8
Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM node:16
|
||||
RUN mkdir -p /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
CMD [ "node", "main.js" ]
|
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Discord Bot Template
|
||||
This is a very basic Discord.js v14 bot template. This is meant to be an easy jumping-off point for quick bot creation without having to set up the basics every time.
|
||||
|
||||
# Dependencies
|
||||
* `dotenv`
|
||||
* `discord.js`
|
||||
* `fs` (built in)
|
4
data/config.json
Normal file
4
data/config.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"guildId": "",
|
||||
"validCommands": []
|
||||
}
|
24
data/strings.json
Normal file
24
data/strings.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"help": {
|
||||
"title": "Title",
|
||||
"content": "Some help content",
|
||||
"footer": "Witty Text Here"
|
||||
},
|
||||
"embeds": {
|
||||
"footer": "github/voidf1sh/discord-bot-template",
|
||||
"color": "0x55FF55",
|
||||
"errorTitle": "Error",
|
||||
"errorColor": "0xFF0000",
|
||||
"infoTitle": "Information",
|
||||
"infoColor": "0x8888FF"
|
||||
},
|
||||
"emoji": {
|
||||
"next": "⏭️",
|
||||
"previous": "⏮️",
|
||||
"confirm": "☑️",
|
||||
"cancel": "❌"
|
||||
},
|
||||
"errors": {
|
||||
"generalCommand": "Sorry, there was an error running that command."
|
||||
}
|
||||
}
|
48
main.js
Normal file
48
main.js
Normal file
@ -0,0 +1,48 @@
|
||||
/* eslint-disable no-case-declarations */
|
||||
/* eslint-disable indent */
|
||||
// dotenv for handling environment variables
|
||||
const dotenv = require('dotenv');
|
||||
dotenv.config();
|
||||
const token = process.env.TOKEN;;
|
||||
|
||||
// Discord.JS
|
||||
const { Client, GatewayIntentBits } = require('discord.js');
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds
|
||||
]
|
||||
});
|
||||
|
||||
// Various imports
|
||||
const fn = require('./modules/functions.js');
|
||||
const strings = require('./data/strings.json');
|
||||
const debugMode = process.env.DEBUG;
|
||||
const statusChannelId = process.env.STATUSCHANNELID
|
||||
|
||||
client.once('ready', () => {
|
||||
fn.collectionBuilders.slashCommands(client);
|
||||
console.log('Ready!');
|
||||
// client.channels.fetch(statusChannelId).then(channel => {
|
||||
// channel.send(`${new Date().toISOString()} -- Ready`).catch(e => console.error(e));
|
||||
// });
|
||||
});
|
||||
|
||||
// slash-commands
|
||||
client.on('interactionCreate', async interaction => {
|
||||
if (interaction.isCommand()) {
|
||||
const { commandName } = interaction;
|
||||
|
||||
if (client.slashCommands.has(commandName)) {
|
||||
client.slashCommands.get(commandName).execute(interaction).catch(e => console.error(e));
|
||||
} else {
|
||||
interaction.reply('Sorry, I don\'t have access to that command.').catch(e => console.error(e));
|
||||
console.error('Slash command attempted to run but not found: /' + commandName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
process.on('uncaughtException', err => {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
client.login(token);
|
28
modules/_clear-commands.js
Normal file
28
modules/_clear-commands.js
Normal file
@ -0,0 +1,28 @@
|
||||
// dotenv for handling environment variables
|
||||
const dotenv = require('dotenv');
|
||||
dotenv.config();
|
||||
|
||||
const { REST, Routes } = require('discord.js');
|
||||
const botId = process.env.BOTID;
|
||||
const token = process.env.TOKEN;
|
||||
const fs = require('fs');
|
||||
|
||||
console.log(`Token: ...${token.slice(-5)} | Bot ID: ${botId}`);
|
||||
|
||||
const rest = new REST({ version: '10' }).setToken(token);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
console.log('Started clearing global application (/) commands.');
|
||||
|
||||
await rest.put(
|
||||
Routes.applicationCommands(botId),
|
||||
{ body: '' },
|
||||
);
|
||||
|
||||
console.log('Successfully cleared global application (/) commands.');
|
||||
process.exit();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
})();
|
38
modules/_deploy-global.js
Normal file
38
modules/_deploy-global.js
Normal file
@ -0,0 +1,38 @@
|
||||
// dotenv for handling environment variables
|
||||
const dotenv = require('dotenv');
|
||||
dotenv.config();
|
||||
|
||||
const { REST, Routes } = require('discord.js');
|
||||
const botId = process.env.BOTID;
|
||||
const token = process.env.TOKEN;
|
||||
const fs = require('fs');
|
||||
|
||||
const commands = [];
|
||||
const commandFiles = fs.readdirSync('./slash-commands').filter(file => file.endsWith('.js'));
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const command = require(`../slash-commands/${file}`);
|
||||
if (command.data != undefined) {
|
||||
commands.push(command.data.toJSON());
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Token: ...${token.slice(-5)} | Bot ID: ${botId}`);
|
||||
|
||||
const rest = new REST({ version: '10' }).setToken(token);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
console.log('Started refreshing global application (/) commands.');
|
||||
|
||||
await rest.put(
|
||||
Routes.applicationCommands(botId),
|
||||
{ body: commands },
|
||||
);
|
||||
|
||||
console.log('Successfully reloaded global application (/) commands.');
|
||||
process.exit();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
})();
|
18
modules/_sanitizeInput.js
Normal file
18
modules/_sanitizeInput.js
Normal file
@ -0,0 +1,18 @@
|
||||
const replaceAll = require('string.prototype.replaceall');
|
||||
const fs = require('fs');
|
||||
const path = "./input.txt";
|
||||
const input = fs.readFileSync(path);
|
||||
let output = "";
|
||||
|
||||
console.log(input);
|
||||
|
||||
if (input.includes("\r\n")) {
|
||||
output = replaceAll(input, "\r\n", "\\n");
|
||||
} else {
|
||||
output = replaceAll(input, "\n", "\\n");
|
||||
}
|
||||
|
||||
output = replaceAll(output, "`", "``");
|
||||
|
||||
console.log(output);
|
||||
fs.writeFileSync(path, output);
|
80
modules/functions.js
Normal file
80
modules/functions.js
Normal file
@ -0,0 +1,80 @@
|
||||
// dotenv for importing environment variables
|
||||
const dotenv = require('dotenv');
|
||||
const fs = require('fs');
|
||||
// Configure Environment Variables
|
||||
dotenv.config();
|
||||
|
||||
// Discord.js
|
||||
const Discord = require('discord.js');
|
||||
const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = Discord;
|
||||
|
||||
// Various imports from other files
|
||||
const debugMode = process.env.DEBUG;
|
||||
const config = require('../data/config.json');
|
||||
const strings = require('../data/strings.json');
|
||||
const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js'));
|
||||
|
||||
const functions = {
|
||||
// Functions for managing and creating Collections
|
||||
collectionBuilders: {
|
||||
// Create the collection of slash commands
|
||||
slashCommands(client) {
|
||||
if (!client.slashCommands) client.slashCommands = new Discord.Collection();
|
||||
client.slashCommands.clear();
|
||||
for (const file of slashCommandFiles) {
|
||||
const slashCommand = require(`../slash-commands/${file}`);
|
||||
if (slashCommand.data != undefined) {
|
||||
client.slashCommands.set(slashCommand.data.name, slashCommand);
|
||||
}
|
||||
}
|
||||
if (debugMode) console.log('Slash Commands Collection Built');
|
||||
}
|
||||
},
|
||||
builders: {
|
||||
actionRows: {
|
||||
example() {
|
||||
// Create the button to go in the Action Row
|
||||
const exampleButton = this.buttons.exampleButton();
|
||||
// Create the Action Row with the Button in it, to be sent with the Embed
|
||||
return new ActionRowBuilder()
|
||||
.addComponents(exampleButton);
|
||||
},
|
||||
buttons: {
|
||||
exampleButton() {
|
||||
return new ButtonBuilder()
|
||||
.setCustomId('id')
|
||||
.setLabel('Label')
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
}
|
||||
}
|
||||
},
|
||||
embeds: {
|
||||
help(private) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(strings.embeds.color)
|
||||
.setTitle(strings.help.title)
|
||||
.setDescription(strings.help.content)
|
||||
.setFooter({ text: strings.help.footer });
|
||||
return { embeds: [embed] };
|
||||
},
|
||||
error(content) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(strings.error.color)
|
||||
.setTitle(strings.error.title)
|
||||
.setDescription(content)
|
||||
.setFooter({ text: strings.embeds.footer });
|
||||
return { embeds: [embed], ephemeral: true };
|
||||
},
|
||||
info(content) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(strings.embeds.infoColor)
|
||||
.setTitle(strings.embeds.infoTitle)
|
||||
.setDescription(content)
|
||||
.setFooter({ text: strings.embeds.footer });
|
||||
return { embeds: [embed], ephemeral: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = functions;
|
0
modules/input.txt
Normal file
0
modules/input.txt
Normal file
7
package.json
Normal file
7
package.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"discord.js": "^14.7.1",
|
||||
"dotenv": "^16.0.3",
|
||||
"string.prototype.replaceall": "^1.0.7"
|
||||
}
|
||||
}
|
37
slash-commands/commands.js
Normal file
37
slash-commands/commands.js
Normal file
@ -0,0 +1,37 @@
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
const fn = require('../modules/functions.js');
|
||||
const strings = require('../data/strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("commands")
|
||||
.setDescription("View all of the bot's commands")
|
||||
.addBooleanOption(o =>
|
||||
o.setName("private")
|
||||
.setDescription("Should the reply be visible only to you?")
|
||||
.setRequired(false)
|
||||
),
|
||||
id: "", // The command ID, used to generate clickable commands
|
||||
about: "[meta] View all of the bot's commands", // A description of the command to be used with /commands
|
||||
async execute(interaction) {
|
||||
let private = interaction.options.getBoolean('private');
|
||||
if (private == undefined) {
|
||||
private = true;
|
||||
}
|
||||
// Defer the reply so we have time to do things
|
||||
await interaction.deferReply({ ephemeral: private }).catch(e => console.error(e));
|
||||
try {
|
||||
// Code here...
|
||||
let commandsParts = ["These are all of the bot's commands, and some information about them:"];
|
||||
interaction.client.slashCommands.forEach(slashCommand => {
|
||||
commandsParts.push(`</${slashCommand.data.name}:${(slashCommand.id == "" ? "0" : slashCommand.id)}> - ${slashCommand.about}`);
|
||||
});
|
||||
let commandsString = commandsParts.join("\n");
|
||||
await interaction.editReply(fn.builders.embeds.info(commandsString)).catch(e => console.error(e));
|
||||
} catch(err) {
|
||||
// In case of error, log it and let the user know something went wrong
|
||||
console.error(err);
|
||||
await interaction.editReply(strings.errors.generalCommand).catch(e => console.error(e));
|
||||
}
|
||||
},
|
||||
};
|
30
slash-commands/help.js
Normal file
30
slash-commands/help.js
Normal file
@ -0,0 +1,30 @@
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
const fn = require('../modules/functions.js');
|
||||
const strings = require('../data/strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("help")
|
||||
.setDescription("Get some help using the bot")
|
||||
.addBooleanOption(o =>
|
||||
o.setName("private")
|
||||
.setDescription("Should the reply be visible only to you?")
|
||||
.setRequired(false)
|
||||
),
|
||||
id: "", // The command ID, used to generate clickable commands
|
||||
about: "Get some help using the bot", // A description of the command to be used with /commands
|
||||
async execute(interaction) {
|
||||
let private = interaction.options.getBoolean('private');
|
||||
if (private == undefined) {
|
||||
private = true;
|
||||
}
|
||||
await interaction.deferReply({ ephemeral: private }).catch(e => console.error(e));
|
||||
try {
|
||||
await interaction.editReply(fn.builders.embeds.help()).catch(e => console.error(e));
|
||||
} catch(err) {
|
||||
// In case of error, log it and let the user know something went wrong
|
||||
console.error(err);
|
||||
await interaction.editReply(strings.errors.generalCommand).catch(e => console.error(e));
|
||||
}
|
||||
},
|
||||
};
|
31
slash-commands/template
Normal file
31
slash-commands/template
Normal file
@ -0,0 +1,31 @@
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
const fn = require('../modules/functions.js');
|
||||
const strings = require('../data/strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("")
|
||||
.setDescription("")
|
||||
.addBooleanOption(o =>
|
||||
o.setName("private")
|
||||
.setDescription("Should the reply be visible only to you?")
|
||||
.setRequired(false)
|
||||
),
|
||||
id: "", // The command ID, used to generate clickable commands
|
||||
about: "", // A description of the command to be used with /commands
|
||||
async execute(interaction) {
|
||||
let private = interaction.options.getBoolean('private');
|
||||
if (private == undefined) {
|
||||
private = true;
|
||||
}
|
||||
// Defer the reply so we have time to do things
|
||||
await interaction.deferReply({ ephemeral: private }).catch(e => console.error(e));
|
||||
try {
|
||||
// Code here...
|
||||
} catch(err) {
|
||||
// In case of error, log it and let the user know something went wrong
|
||||
console.error(err);
|
||||
await interaction.editReply(strings.errors.generalCommand).catch(e => console.error(e));
|
||||
}
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue
Block a user