* feat: improved command handler

This commit is contained in:
Jon Deaves
2020-08-11 23:36:19 +01:00
parent 99116d2c65
commit 73a2af412f
11 changed files with 157 additions and 8 deletions

View File

@@ -1,3 +1,23 @@
# Team Codemonkey Discord Bot
# Venom Discord Bot
This is the Discord bot for Team Codemonkey
This is the Discord bot for Creation Asylum.
## Development
- Requires [NodeJS](https://nodejs.org/), recommend at least the latest LTS version.
- Run `yarn` or `npm install` to install dependencies
### Environment Variables
At a minimum you need to provide the Discord bots Token, which can be found on the Bot tab of a Discord application. See table below for possible values.
| key | description | example |
|-------------------|-------------|---------|
| BOT_TRIGGER | Prefix of message to let bot know you are speaking to it
| DISCORD_BOT_TOKEN | Discord bots Token
| ENVIRONMENT | What environment the bot is running in | `production`, `development` or `test` |
| LOG_LEVEL | What level of logs should be displayed in console | `error`, `warn`, `info`, `verbose`, `debug` or `silly` |
### Bot commands
To add a command you create a Typescript file in `src/bot/commands/[filename].ts` and ensure it implements the \src/bot/commands/ICommand.ts` interface. You can see the other files in this directory for implementation examples. Also ensure you export this file in `src/bot/commands/index.ts`.

View File

@@ -1,10 +1,10 @@
{
"name": "teamcodemonkey-bot",
"name": "venom",
"version": "0.0.1",
"description": "A bot for the Team Codemonkey Discord",
"description": "A bot for the Creation Asylum Discord",
"main": "index.js",
"repository": "https://github.com/jondeaves/tcm-bot",
"bugs": "https://github.com/jondeaves/jondeaves/issues",
"repository": "https://github.com/jondeaves/venom",
"bugs": "https://github.com/jondeaves/venom/issues",
"author": "Jon Deaves <hello@jondeaves.me> https://jondeaves.me",
"contributors": [
"Jon Deaves <hello@jondeaves.me> https://jondeaves.me"
@@ -35,4 +35,4 @@
"tslint-react": "~5.0.0",
"typescript": "~3.9.7"
}
}
}

4
src/.env.template Normal file
View File

@@ -0,0 +1,4 @@
BOT_TRIGGER=!
DISCORD_BOT_TOKEN=[replace with own token]
ENVIRONMENT=production
LOG_LEVEL=error

View File

@@ -1,10 +1,13 @@
import { exit } from 'process';
import Discord from 'discord.js';
import container from './inversity.config';
import ConfigService from './core/services/config.service';
import LoggerService from './core/services/logger.service';
import { exit } from 'process';
import ICommand from './bot/commands/ICommand';
import rawCommands from './bot/commands';
export default class App {
private _configService: ConfigService = container.resolve<ConfigService>(ConfigService);
@@ -14,11 +17,40 @@ export default class App {
public async init(): Promise<void> {
this._discordClient = new Discord.Client();
const commandList = new Discord.Collection<string, ICommand>();
rawCommands.forEach(rawCommand => {
commandList.set(rawCommand.name, rawCommand);
});
// Triggers once after connecting to server
this._discordClient.once('ready', () => {
this._loggerService.log('info', "The Bot is connected to Discord server");
});
// Triggers on every message the bot can see
this._discordClient.on('message', async message => {
const prefix = this._configService.get('BOT_TRIGGER');
// If the message either doesn't start with the prefix or was sent by a bot, exit early.
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/);
const commandName = args.shift().toLowerCase();
const command = commandList.get(commandName) || commandList.find(cmd => cmd.aliases && cmd.aliases.includes(commandName));
if (!command) {
return message.reply('Monkey no understand that command yet!');
};
try {
await command.execute(message, args, prefix, commandList);
} catch (error) {
console.error(error);
message.reply('there was an error trying to execute that command!');
}
});
this._discordClient.login(this._configService.get('DISCORD_BOT_TOKEN'))
.catch((reason) => {
this._loggerService.log('error', `Cannot initialise Discord client. Check the token: ${this._configService.get('DISCORD_BOT_TOKEN')}`);

View File

@@ -0,0 +1,8 @@
import Discord, { Collection } from 'discord.js';
export default interface ICommand {
name: string;
aliases?: string[];
description: string;
execute: (message: Discord.Message, args: string[], prefix?: string, commands?: Collection<string, ICommand>) => Promise<void>,
}

47
src/bot/commands/help.ts Normal file
View File

@@ -0,0 +1,47 @@
import Discord, { Collection } from 'discord.js';
import ICommand from './ICommand';
const command: ICommand = {
name: 'help',
aliases: ['commands'],
description: 'Lists available commands!',
async execute(message: Discord.Message, args: string[], prefix: string, commands: Collection<string, ICommand>) {
const data = [];
if (!args.length) {
// Get for all commands
data.push('Here\'s a list of all my commands:\n');
data.push(commands.map(command => command.name).join(', '));
data.push(`\nYou can send \`${prefix}help [command name]\` to get info on a specific command!`);
} else {
// Get description of single command
const name = args[0].toLowerCase();
const command = commands.get(name) || commands.find(c => c.aliases && c.aliases.includes(name));
if (!command) {
message.reply('that\'s not a valid command!');
} else {
data.push(`**Name:** ${command.name}`);
if (command.aliases) data.push(`**Aliases:** ${command.aliases.join(', ')}`);
if (command.description) data.push(`**Description:** ${command.description}`);
}
}
try {
await message.author.send(data, { split: true });
if (message.channel.type === 'dm')
return;
message.reply('I\'ve sent you a DM with all my commands!');
}
catch (error) {
console.error(`Could not send help DM to ${message.author.tag}.\n`, error);
message.reply('it seems like I can\'t DM you! Do you have DMs disabled?');
}
},
};
export default command;

View File

@@ -0,0 +1,9 @@
import help from './help';
import ping from './ping';
import see from './see';
export default [
help,
ping,
see
]

14
src/bot/commands/ping.ts Normal file
View File

@@ -0,0 +1,14 @@
import Discord from 'discord.js';
import ICommand from './ICommand';
const command: ICommand = {
name: 'ping',
aliases: ['hello'],
description: 'Ping!',
async execute(message: Discord.Message, args: string[]) {
message.reply('Pong.');
},
};
export default command;

13
src/bot/commands/see.ts Normal file
View File

@@ -0,0 +1,13 @@
import Discord from 'discord.js';
import ICommand from './ICommand';
const command: ICommand = {
name: 'see',
description: 'See!',
async execute(message: Discord.Message, args: string[]) {
message.reply(`Server: ${message.guild.name}\nYour username: ${message.author.username}\nYour ID: ${message.author.id}`);
},
};
export default command;

View File

@@ -31,6 +31,7 @@ export default class ConfigService {
private setup(): void {
this.config = {
BOT_TRIGGER: process.env.BOT_TRIGGER || '!',
DISCORD_BOT_TOKEN: process.env.DISCORD_BOT_TOKEN,
ENVIRONMENT: (process.env.NODE_ENV as Environment) || 'development',
LOG_LEVEL: (process.env.LOG_LEVEL as LogLevel) || 'info',

View File

@@ -2,6 +2,7 @@ import LogLevel from "./LogLevel";
import Environment from "./Environment";
export default interface Config {
BOT_TRIGGER: string;
DISCORD_BOT_TOKEN: string;
ENVIRONMENT: Environment;
LOG_LEVEL: LogLevel;