Initial Commit - Based on TheShadowEevee/Konpeki-Discord-Bot
This commit is contained in:
commit
0e027dc99b
19 changed files with 3633 additions and 0 deletions
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", 4, { "SwitchCase": 1 }],
|
||||
"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"
|
||||
}
|
||||
}
|
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: TheShadowEevee # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: TheShadowEevee # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
71
.github/workflows/codeql.yml
vendored
Normal file
71
.github/workflows/codeql.yml
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: '16 9 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
config.json
|
||||
presence.json
|
||||
|
||||
node_modules
|
||||
data
|
7
LICENSE
Normal file
7
LICENSE
Normal file
|
@ -0,0 +1,7 @@
|
|||
Copyright © 2022 TheShadowEevee and Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
75
README.md
Normal file
75
README.md
Normal file
|
@ -0,0 +1,75 @@
|
|||
[![GitHub](https://img.shields.io/github/license/TheShadowEevee/Konpeki-Discord-Bot)](https://github.com/TheShadowEevee/Konpeki-Discord-Bot/blob/master/LICENSE) [![GitHub repo size](https://img.shields.io/github/repo-size/TheShadowEevee/Konpeki-Discord-Bot)](https://github.com/TheShadowEevee/Konpeki-Discord-Bot) [![GitHub package.json dependency version (prod)](https://img.shields.io/github/package-json/dependency-version/TheShadowEevee/Konpeki-Discord-Bot/discord.js)](https://discord.js.org/) [![CodeFactor](https://www.codefactor.io/repository/github/theshadoweevee/konpeki-discord-bot/badge)](https://www.codefactor.io/repository/github/theshadoweevee/konpeki-discord-bot) [![Discord](https://img.shields.io/discord/1052844258173931540?label=discord)](https://discord.gg/Zt8zruXexJ)
|
||||
# Konpeki Discord Bot
|
||||
|
||||
Konpeki is a Discord Bot with varied functions. Written with discord.js, the Konpeki bot is designed to be easy to use and setup, as well as easy to change to work how you want it to. The name Konpeki comes from the Japenese word for Azure; there's no other reason for the name.
|
||||
|
||||
For general support, or to try out a live, public version of the bot, feel free to join the bot's [Discord Server](https://discord.gg/Zt8zruXexJ)!
|
||||
|
||||
The live version of the bot is `Konpeki Shiho#2603`, run by TheShadowEevee.
|
||||
|
||||
|
||||
## Adding to your server
|
||||
|
||||
You can add the public bot, Konpeki Shiho, to your server [here](https://discord.com/oauth2/authorize?client_id=812862868721631272&permissions=275146345472&scope=bot%20applications.commands)!
|
||||
- Please note that currently Konpeki Shiho is *not* a verified bot due to lack of server membership. You can help get the bot verified by adding it to your server! Konpeki Shiho runs and updates directly off of the code from this repository, while development is done primarily against the private bot Little Shiho.
|
||||
|
||||
If you are running your own version of the Konpeki bot, change the Client ID in the link to your bot's Client ID found in the Discord Developer portal. See [Running Locally](#Running-Locally) for more.
|
||||
## Running Locally
|
||||
|
||||
These instructions will get you setup with a basic setup for development.
|
||||
If you are running the bot as-is, it is recommended to use the [pm2](https://pm2.io/) process manager or a similar tool to keep the bot running unattended.
|
||||
|
||||
Before you begin, install these prerequisites:
|
||||
- [Node.js](https://nodejs.org/en/download/)
|
||||
- [Git](https://git-scm.com/downloads/)
|
||||
|
||||
#### ***Creating the bot on Discord***
|
||||
The first step to setup any Discord bot is creating one in the [Discord Developer Portal](https://discord.com/developers/).
|
||||
|
||||
- Go to the [Discord Developer Portal](https://discord.com/developers/)
|
||||
- Click New Application, name it, and press Create.
|
||||
- The Application ID on the page you are brought to is your Client ID. You will need this later.
|
||||
- Click the Bot tab on the left, then New Bot.
|
||||
- If you get an error saying too many users have this username, rename your application to something more unique.
|
||||
- Copy your bot's token somewhere *safe*.
|
||||
- Do not share this! This will give anyone access to your bot. Reset it immediatly if it becomes public.
|
||||
- If you lose the token, you will have to reset it and get a new one.
|
||||
|
||||
#### ***Configuring the bot***
|
||||
Once you have created a bot, you need to configure it.
|
||||
|
||||
- Clone the repositiory and move into the folder.
|
||||
```bash
|
||||
git clone https://github.com/TheShadowEevee/Konpeki-Discord-Bot
|
||||
cd Konpeki-Discord-Bot
|
||||
```
|
||||
- Copy `config.json.template` and name the new file `config.json`.
|
||||
- Fill in the `config.json` file with your bot's token, client id, and your username.
|
||||
|
||||
#### ***Running the bot***
|
||||
Once your bot is created, run the below commands to clone and run the bot.
|
||||
|
||||
```bash
|
||||
# Install NPM dependencies
|
||||
npm install
|
||||
|
||||
# Setup slash commands
|
||||
node deploy-commands.js
|
||||
|
||||
# Run the bot
|
||||
node index.js
|
||||
```
|
||||
|
||||
#### ***Updating the bot***
|
||||
Update the bot periodically to ensure your up-to-date.
|
||||
|
||||
If you're updating from Github:
|
||||
- Run `git pull` from the bot's folder
|
||||
- This will get the lastest updates from Github
|
||||
- Run `node delete-commands.js && node deploy-commands.js`
|
||||
- This will recreate all your slash commands to ensure they are up-to-date
|
||||
|
||||
If you're updating based on changes you made:
|
||||
- You only need to do this if you have created or deleted a file from the `commands` folder
|
||||
- Run `node delete-commands.js && node deploy-commands.js`
|
||||
- This will recreate all your slash commands to ensure they are up-to-date
|
15
TODO.md
Normal file
15
TODO.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
TODO List for feature completeness
|
||||
|
||||
P0:
|
||||
- Check for Media in a message
|
||||
- Regex Parse Git Commits
|
||||
- Github top priority, Gitea/Forgejo second, any others third
|
||||
- Check for reactions on a message
|
||||
- Start / End Dates
|
||||
|
||||
P1:
|
||||
- Check if in a thread
|
||||
- Allow reaction to be chosen in the command rather than hard coded
|
||||
|
||||
P2:
|
||||
- Calculate Days, Highest Streak
|
27
commands/commit-count.js
Normal file
27
commands/commit-count.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Commit Overflow Counting - Slash Command Definition File
|
||||
* commit-count.js - Gets an initial commit count for Commit Overflow
|
||||
*/
|
||||
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('commit-count')
|
||||
.setDescription('Counts Commits'),
|
||||
async execute(interaction) {
|
||||
|
||||
const exampleEmbed = new EmbedBuilder()
|
||||
.setColor(interaction.member.displayHexColor)
|
||||
.setTitle('Commits')
|
||||
.setDescription('By Day (Method)')
|
||||
.addFields(
|
||||
{ name: 'Valid Commits', value: 'December 20th (Media)\nDecember 21st (Commit URL)\nDecember 23rd (Reaction)' },
|
||||
|
||||
{ name: 'Missed Days', value: 'December 22nd' },
|
||||
{ name: 'Organizers:', value: 'Please react with :gh_green_square: to verify missing days!' },
|
||||
);
|
||||
|
||||
await interaction.reply({ embeds: [exampleEmbed], ephemeral: true });
|
||||
},
|
||||
};
|
6
config.json.template
Normal file
6
config.json.template
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"token": "Bot Token Here - DO NOT SHARE YOUR TOKEN OR YOUR CONFIG FILE",
|
||||
"clientId": "0123456789012345678",
|
||||
"botName": "Konpeki Bot",
|
||||
"botOwner": "discordusername"
|
||||
}
|
30
delete-commands.js
Normal file
30
delete-commands.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Konpeki Discord Bot - Slash Command Configuration File
|
||||
* delete-commands.js - Clears all slash commands from Discord to remove any old commands
|
||||
*
|
||||
* Code modified based on example from the discord.js guides
|
||||
* Run `node deploy-commands.js` after this to recreate your slash commands
|
||||
*/
|
||||
|
||||
const { REST, Routes } = require('discord.js');
|
||||
const { clientId, token } = require('./config.json');
|
||||
|
||||
// Construct and prepare an instance of the REST module
|
||||
const rest = new REST({ version: '10' }).setToken(token);
|
||||
|
||||
// and deploy your commands!
|
||||
(async () => {
|
||||
try {
|
||||
console.log('Started deleting all application (/) commands.');
|
||||
|
||||
// The put method is used to fully refresh all commands
|
||||
rest.put(Routes.applicationCommands(clientId), { body: [] })
|
||||
.then(() => console.log('Successfully deleted all application (/) commands.'))
|
||||
.catch(console.error);
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
console.error(error);
|
||||
}
|
||||
})();
|
111
deploy-commands.js
Normal file
111
deploy-commands.js
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Konpeki Discord Bot - Slash Command Configuration File
|
||||
* deploy-commands.js - Gathers all current .js files in the commands folder and publishes a list to Discord
|
||||
*
|
||||
* Code modified based on example from the discord.js guides
|
||||
*/
|
||||
|
||||
const { REST, Routes } = require('discord.js');
|
||||
const { clientId, token } = require('./config.json');
|
||||
const fs = require('node:fs');
|
||||
|
||||
const commands = [];
|
||||
const commandsHelp = [];
|
||||
|
||||
// Grab all the command files from the commands directory you created earlier
|
||||
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
|
||||
|
||||
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
|
||||
for (const file of commandFiles) {
|
||||
const command = require(`./commands/${file}`);
|
||||
commands.push(command.data.toJSON());
|
||||
commandsHelp.push(command.data);
|
||||
}
|
||||
|
||||
// Construct and prepare an instance of the REST module
|
||||
const rest = new REST({ version: '10' }).setToken(token);
|
||||
|
||||
// Generate help-text.json
|
||||
try {
|
||||
console.log(`Generating ${commands.length} help text entries.`);
|
||||
|
||||
let helpJSONString = '{\n';
|
||||
|
||||
for (let i = 0; i < commandsHelp.length; i++) {
|
||||
helpJSONString += ` "${commandsHelp[i].name}": {\n`;
|
||||
helpJSONString += ` "description": "${commandsHelp[i].description}",\n`;
|
||||
helpJSONString += ' "options": {\n';
|
||||
|
||||
for (let j = 0; j < commandsHelp[i].options.length; j++) {
|
||||
|
||||
helpJSONString += ` "${commandsHelp[i].options[j].name}": {\n`;
|
||||
helpJSONString += ` "description": "${commandsHelp[i].options[j].description}",\n`;
|
||||
helpJSONString += ` "required": "${commandsHelp[i].options[j].required}",\n`;
|
||||
helpJSONString += ' "choices": {\n';
|
||||
|
||||
if (typeof commandsHelp[i].options[j].choices !== 'undefined') {
|
||||
for (let k = 0; k < commandsHelp[i].options[j].choices.length; k++) {
|
||||
|
||||
helpJSONString += ` "${commandsHelp[i].options[j].choices[k].name}": {\n`;
|
||||
helpJSONString += ` "value": "${commandsHelp[i].options[j].choices[k].value}",\n`;
|
||||
helpJSONString += ' },\n';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
helpJSONString += ' },\n';
|
||||
helpJSONString += ' },\n';
|
||||
|
||||
}
|
||||
|
||||
helpJSONString += ' },\n';
|
||||
helpJSONString += ' },\n';
|
||||
}
|
||||
|
||||
helpJSONString += '}';
|
||||
|
||||
// Lazy way out of removing trailing commas
|
||||
// See https://stackoverflow.com/a/34347475
|
||||
const helpJSON = JSON.stringify(JSON.parse(helpJSONString.replace(/,(?!\s*?[{["'\w])/g, '')), null, 4);
|
||||
|
||||
// Create data folder if it doesn't exist
|
||||
if (!fs.existsSync('data')) {
|
||||
fs.mkdirSync('data');
|
||||
}
|
||||
|
||||
// Write file to disk
|
||||
fs.writeFile('./data/help-text.json', helpJSON, err => {
|
||||
if (err) {
|
||||
console.log(`Unable to write help-text.json: ${err}`);
|
||||
console.log('Stopping...');
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Successfully generated ${commandsHelp.length} help text entries.`);
|
||||
|
||||
console.log();
|
||||
|
||||
// Update slash commands on Discord's side
|
||||
(async () => {
|
||||
try {
|
||||
console.log(`Started refreshing ${commands.length} application (/) commands.`);
|
||||
|
||||
// The put method is used to fully refresh all commands
|
||||
const data = await rest.put(
|
||||
Routes.applicationCommands(clientId),
|
||||
{ body: commands },
|
||||
);
|
||||
|
||||
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
|
||||
}
|
||||
catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
console.error(error);
|
||||
}
|
||||
})();
|
||||
}
|
||||
catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
console.error(error);
|
||||
}
|
127
index.js
Normal file
127
index.js
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Konpeki Discord Bot - Main Bot File
|
||||
* All base level bot setup is done here
|
||||
*/
|
||||
|
||||
// Require filesystem libraries
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
// Add pm2 metrics - this should NEVER track ANYTHING identifiable. This is purely for basic metrics and bot performance tracking
|
||||
const metrics = require('./utils/pm2-metrics.js');
|
||||
|
||||
// Use a custom logging script
|
||||
const logger = require('./utils/logging.js');
|
||||
|
||||
// Listen for (Semi-)Permenant Interactions
|
||||
const interactionListener = require('./utils/interaction-trigger.js');
|
||||
|
||||
// Require the necessary discord.js classes
|
||||
const { Client, Collection, Events, GatewayIntentBits } = require('discord.js');
|
||||
const { token, botOwner } = require('./config.json');
|
||||
const { activity, status } = require('./presence.json');
|
||||
|
||||
// Create a new client instance
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
// Setup the commands collection
|
||||
client.commands = new Collection();
|
||||
|
||||
const commandsPath = path.join(__dirname, 'commands');
|
||||
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const filePath = path.join(commandsPath, file);
|
||||
const command = require(filePath);
|
||||
// Set a new item in the Collection with the key as the command name and the value as the exported module
|
||||
if ('data' in command && 'execute' in command) {
|
||||
client.commands.set(command.data.name, command);
|
||||
}
|
||||
else {
|
||||
logger.log(logger.logLevels.WARN, `The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
|
||||
// When the client is ready, run this code (only once)
|
||||
// We use 'c' for the event parameter to keep it separate from the already defined 'client'
|
||||
client.once(Events.ClientReady, c => {
|
||||
logger.log(logger.logLevels.INFO, `${logger.colorText('Ready!', logger.textColor.Green)} Logged in as ${logger.colorText(c.user.tag, logger.textColor.Blue)}`);
|
||||
client.user.setPresence({ activities: [{ name: activity }], status: status });
|
||||
|
||||
// Track websocket heartbeat with PM2 Histogram
|
||||
let latency = 0;
|
||||
setInterval(function() {
|
||||
latency = c.ws.ping;
|
||||
metrics.websocketHeartbeatHist.update(latency);
|
||||
}, 1000);
|
||||
|
||||
// Report current server count with PM2 (Servers counted anonymously)
|
||||
metrics.serverCount.set(c.guilds.cache.size);
|
||||
|
||||
});
|
||||
|
||||
// Client "on" Events
|
||||
// Someone used an interaction
|
||||
client.on(Events.InteractionCreate, async interaction => {
|
||||
if (interaction.isChatInputCommand()) {
|
||||
|
||||
const command = interaction.client.commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
logger.log(logger.logLevels.ERROR, `No command matching ${interaction.commandName} was found.`);
|
||||
await interaction.reply({ content: `This command no longer exists! Please contact ${botOwner} to report that this is happening!`, ephemeral: true });
|
||||
|
||||
// Report error to PM2 dashboard
|
||||
metrics.interactionErrors.inc();
|
||||
metrics.io.notifyError(new Error('Interaction doesn\'t exist'), {
|
||||
custom: {
|
||||
interactionCommand: interaction.commandName,
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
}
|
||||
catch (error) {
|
||||
logger.log(logger.logLevels.ERROR, error);
|
||||
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||
|
||||
// Report error to PM2 dashboard
|
||||
metrics.interactionErrors.inc();
|
||||
metrics.io.notifyError(new Error('Error executing interaction'), {
|
||||
custom: {
|
||||
interactionCommand: interaction.commandName,
|
||||
error: error,
|
||||
},
|
||||
});
|
||||
}
|
||||
// Successful Execution, report as a PM2 metric
|
||||
// If the bot gets a lot of use, consider removing this for performance
|
||||
metrics.interactionSuccess();
|
||||
|
||||
}
|
||||
else if (interaction.isButton()) {
|
||||
interactionListener.buttonInteraction(interaction);
|
||||
}
|
||||
else if (interaction.isStringSelectMenu()) {
|
||||
// respond to the select menu
|
||||
}
|
||||
});
|
||||
|
||||
// Joined a server
|
||||
client.on(Events.GuildCreate, guild => {
|
||||
// Report current server count with PM2 (Servers counted anonymously)
|
||||
metrics.serverCount.set(guild.client.guilds.cache.size);
|
||||
});
|
||||
|
||||
// Removed from a server
|
||||
client.on(Events.GuildDelete, guild => {
|
||||
// Report current server count with PM2 (Servers counted anonymously)
|
||||
metrics.serverCount.set(guild.client.guilds.cache.size);
|
||||
});
|
||||
|
||||
// Log in to Discord with your client's token
|
||||
client.login(token);
|
1703
package-lock.json
generated
Normal file
1703
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
33
package.json
Normal file
33
package.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"@pm2/io": "^5.0.0",
|
||||
"discord.js": "^14.15.3",
|
||||
"dotenv": "^16.0.3"
|
||||
},
|
||||
"name": "konpeki-discordbot",
|
||||
"version": "1.0.0",
|
||||
"description": "A Discord bot with varied functionalities",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"eslint": "^8.29.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/TheShadowEevee/Konpeki-Discord-Bot.git"
|
||||
},
|
||||
"keywords": [
|
||||
"RTD",
|
||||
"Discord",
|
||||
"Bot",
|
||||
"Konpeki"
|
||||
],
|
||||
"author": "TheShadowEevee",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/TheShadowEevee/Konpeki-Discord-Bot/issues"
|
||||
},
|
||||
"homepage": "https://github.com/TheShadowEevee/Konpeki-Discord-Bot#readme"
|
||||
}
|
1206
pnpm-lock.yaml
Normal file
1206
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load diff
4
presence.json.template
Normal file
4
presence.json.template
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"activity": "Put your status here!",
|
||||
"status": "online"
|
||||
}
|
39
utils/interaction-trigger.js
Normal file
39
utils/interaction-trigger.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
const { Events } = require('discord.js');
|
||||
|
||||
const buttonInteraction = function(interaction) {
|
||||
const splitInteraction = interaction.customId.split('-');
|
||||
|
||||
(async () => {
|
||||
if (splitInteraction[0] === 'role') {
|
||||
const client = interaction.client;
|
||||
const guild = await client.guilds.fetch(interaction.guildId);
|
||||
const member = interaction.member;
|
||||
const role = await guild.roles.fetch(splitInteraction[1]);
|
||||
|
||||
if (member.roles.cache.find(r => r.id === splitInteraction[1])) {
|
||||
try {
|
||||
member.roles.remove(splitInteraction[1]);
|
||||
await interaction.reply({ content: `Removed role ${role} from ${interaction.user}!`, ephemeral: true });
|
||||
}
|
||||
catch {
|
||||
await interaction.reply({ content: 'An error has occurred and the role was not removed. Likely I don\'t have the needed permissions!', ephemeral: true });
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
member.roles.add(splitInteraction[1]);
|
||||
await interaction.reply({ content: `Added role ${role} to ${interaction.user}!`, ephemeral: true });
|
||||
}
|
||||
catch {
|
||||
await interaction.reply({ content: 'An error has occurred and the role was not added. Likely I don\'t have the needed permissions!', ephemeral: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
return;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: Events.InteractionCreate,
|
||||
buttonInteraction,
|
||||
};
|
72
utils/logging.js
Normal file
72
utils/logging.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Konpeki Discord Bot - Utility Definition File
|
||||
* logging.js - A custom logging script to apply information to output
|
||||
*
|
||||
* Though console.error() and console.warn() exist, they don't exactly fit what is wanted here.
|
||||
*/
|
||||
|
||||
// Enum list of severity levels
|
||||
const logLevels = {
|
||||
DEBUG: 0,
|
||||
INFO: 1,
|
||||
WARN: 2,
|
||||
ERROR: 3,
|
||||
};
|
||||
|
||||
// Enum list of text colors
|
||||
const textColor = {
|
||||
White: '\x1b[97m',
|
||||
Gray: '\x1b[37m',
|
||||
Yellow: '\x1b[33m',
|
||||
Red: '\x1b[91m',
|
||||
Blue: '\x1b[96m',
|
||||
Green: '\x1b[92m',
|
||||
Reset: '\x1b[0m',
|
||||
};
|
||||
|
||||
// Get the current Date and Time
|
||||
const date_time = new Date(new Date().toUTCString());
|
||||
const datetime = '[' + ('0' + (date_time.getMonth() + 1)).slice(-2) + '/' + ('0' + date_time.getDate()).slice(-2) + '/' + date_time.getFullYear() + ' ' + ('0' + (date_time.getHours() + 1)).slice(-2) + ':' + ('0' + (date_time.getMinutes() + 1)).slice(-2) + ':' + ('0' + (date_time.getSeconds() + 1)).slice(-2) + ' UTC]';
|
||||
|
||||
// Add color to the text passed through - If a text manip util is made, move this there
|
||||
const colorText = function(message, color) {
|
||||
return color + message + textColor.Reset;
|
||||
};
|
||||
|
||||
// Print log message to console based on severity level
|
||||
const log = function(logLevel, message) {
|
||||
// Used in the event the message is an error: see case 3
|
||||
const lines = message.toString().split('\n');
|
||||
|
||||
switch (logLevel) {
|
||||
case 0:
|
||||
console.log(datetime + ' ' + colorText('[DEBUG]', textColor.Gray) + ' ' + message);
|
||||
break;
|
||||
case 1:
|
||||
console.log(datetime + ' ' + colorText('[INFO]', textColor.White) + ' ' + message);
|
||||
break;
|
||||
case 2:
|
||||
console.log(datetime + ' ' + colorText('[WARN]', textColor.Yellow) + ' ' + message);
|
||||
break;
|
||||
case 3:
|
||||
// Errors tend to have multiple lines, handle them specially to include the first two.
|
||||
console.log(datetime + ' ' + colorText('[ERROR]', textColor.Red) + ' ' + lines[0]);
|
||||
|
||||
// If the error only has 1 line, don't try to read a second one
|
||||
if (lines[1] != null) {
|
||||
console.log(datetime + ' ' + colorText('[ERROR]', textColor.Red) + ' | ' + lines[1].trim());
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
console.log(datetime + ' ' + colorText('[INFO]', textColor.White) + ' ' + message);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
logLevels,
|
||||
textColor,
|
||||
colorText,
|
||||
log,
|
||||
};
|
40
utils/pm2-metrics.js
Normal file
40
utils/pm2-metrics.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Konpeki Discord Bot - Utility Definition File
|
||||
* pm2-metrics.js - A place to keep all pm2 metrics and error definitions
|
||||
*/
|
||||
|
||||
const io = require('@pm2/io');
|
||||
|
||||
const interactionErrors = io.counter({
|
||||
name: 'Interaction Errors (Since last restart)',
|
||||
});
|
||||
|
||||
const interactionSuccessCounter = io.counter({
|
||||
name: 'Interaction Successful Runs (Since last restart)',
|
||||
});
|
||||
|
||||
const interactionSuccessMeter = io.meter({
|
||||
name: 'Successful Interactions per Second',
|
||||
});
|
||||
|
||||
const websocketHeartbeatHist = io.histogram({
|
||||
name: 'Avg. Websocket Heartbeat (ms) / 5 min.',
|
||||
measurement: 'mean',
|
||||
});
|
||||
|
||||
const serverCount = io.metric({
|
||||
name : 'Server Count',
|
||||
});
|
||||
|
||||
const interactionSuccess = function() {
|
||||
interactionSuccessCounter.inc();
|
||||
interactionSuccessMeter.mark();
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
io,
|
||||
interactionErrors,
|
||||
interactionSuccess,
|
||||
websocketHeartbeatHist,
|
||||
serverCount,
|
||||
};
|
Loading…
Reference in a new issue