Add custom shortcut support

This commit is contained in:
Max Isom 2020-03-16 19:37:54 -05:00
parent 5eb1389a6f
commit 32cb3ca4ae
7 changed files with 152 additions and 23 deletions

View file

@ -1,9 +1,7 @@
import makeDir from 'make-dir';
import {Client, Message, Collection} from 'discord.js'; import {Client, Message, Collection} from 'discord.js';
import {inject, injectable} from 'inversify'; import {inject, injectable} from 'inversify';
import {TYPES} from './types'; import {TYPES} from './types';
import {Settings} from './models'; import {Settings, Shortcut} from './models';
import {sequelize} from './utils/db';
import handleGuildCreate from './events/guild-create'; import handleGuildCreate from './events/guild-create';
import container from './inversify.config'; import container from './inversify.config';
import Command from './commands'; import Command from './commands';
@ -13,16 +11,12 @@ export default class {
private readonly client: Client; private readonly client: Client;
private readonly token: string; private readonly token: string;
private readonly clientId: string; private readonly clientId: string;
private readonly dataDir: string;
private readonly cacheDir: string;
private readonly commands!: Collection<string, Command>; private readonly commands!: Collection<string, Command>;
constructor(@inject(TYPES.Client) client: Client, @inject(TYPES.Config.DISCORD_TOKEN) token: string, @inject(TYPES.Config.DISCORD_CLIENT_ID) clientId: string, @inject(TYPES.Config.DATA_DIR) dataDir: string, @inject(TYPES.Config.CACHE_DIR) cacheDir: string) { constructor(@inject(TYPES.Client) client: Client, @inject(TYPES.Config.DISCORD_TOKEN) token: string, @inject(TYPES.Config.DISCORD_CLIENT_ID) clientId: string) {
this.client = client; this.client = client;
this.token = token; this.token = token;
this.clientId = clientId; this.clientId = clientId;
this.dataDir = dataDir;
this.cacheDir = cacheDir;
this.commands = new Collection(); this.commands = new Collection();
} }
@ -58,17 +52,31 @@ export default class {
return; return;
} }
const args = msg.content.slice(prefix.length).split(/ +/); let args = msg.content.slice(prefix.length).split(/ +/);
const command = args.shift()!.toLowerCase(); const command = args.shift()!.toLowerCase();
if (!this.commands.has(command)) { // Get possible shortcut
const shortcut = await Shortcut.findOne({where: {guildId: msg.guild.id, shortcut: command}});
let handler: Command;
if (this.commands.has(command)) {
handler = this.commands.get(command) as Command;
} else if (shortcut) {
const possibleHandler = this.commands.get(shortcut.command.split(' ')[0]);
if (possibleHandler) {
handler = possibleHandler;
args = shortcut.command.split(/ +/).slice(1);
} else {
return;
}
} else {
return; return;
} }
try { try {
const handler = this.commands.get(command); handler.execute(msg, args);
handler!.execute(msg, args);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
msg.reply('there was an error trying to execute that command!'); msg.reply('there was an error trying to execute that command!');
@ -76,15 +84,13 @@ export default class {
}); });
this.client.on('ready', async () => { this.client.on('ready', async () => {
// Create directory if necessary
await makeDir(this.dataDir);
await makeDir(this.cacheDir);
await sequelize.sync({});
console.log(`Ready! Invite the bot with https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&scope=bot`); console.log(`Ready! Invite the bot with https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&scope=bot`);
}); });
this.client.on('error', error => {
console.error(error);
});
// Register event handlers // Register event handlers
this.client.on('guildCreate', handleGuildCreate); this.client.on('guildCreate', handleGuildCreate);

88
src/commands/shortcuts.ts Normal file
View file

@ -0,0 +1,88 @@
import {Message} from 'discord.js';
import {injectable} from 'inversify';
import {Shortcut, Settings} from '../models';
import Command from '.';
@injectable()
export default class implements Command {
public name = 'shortcuts';
public description = 'edit shortcuts';
public async execute(msg: Message, args: string []): Promise<void> {
if (args.length === 0) {
// Get shortcuts for guild
const shortcuts = await Shortcut.findAll({where: {guildId: msg.guild!.id}});
if (shortcuts.length === 0) {
await msg.channel.send('no shortcuts exist');
return;
}
// Get prefix for guild
const {prefix} = await Settings.findOne({where: {guildId: msg.guild!.id}}) as Settings;
const res = shortcuts.reduce((accum, shortcut) => {
accum += `${prefix}${shortcut.shortcut}: ${shortcut.command}\n`;
return accum;
}, '');
await msg.channel.send(res);
} else {
const action = args[0];
const shortcutName = args[1];
switch (action) {
case 'set': {
const shortcut = await Shortcut.findOne({where: {guildId: msg.guild!.id, shortcut: shortcutName}});
const command = args.slice(2).join(' ');
const newShortcut = {shortcut: shortcutName, command, guildId: msg.guild!.id, authorId: msg.author.id};
if (shortcut) {
if (shortcut.authorId !== msg.author.id && msg.author.id !== msg.guild!.owner!.id) {
await msg.channel.send('error: you do not have permission to do that');
return;
}
await shortcut.update(newShortcut);
await msg.channel.send('shortcut updated');
} else {
await Shortcut.create(newShortcut);
await msg.channel.send('shortcut created');
}
break;
}
case 'delete': {
// Check if shortcut exists
const shortcut = await Shortcut.findOne({where: {guildId: msg.guild!.id, shortcut: shortcutName}});
if (!shortcut) {
await msg.channel.send('error: shortcut does not exist');
return;
}
// Check permissions
if (shortcut.authorId !== msg.author.id && msg.author.id !== msg.guild!.owner!.id) {
await msg.channel.send('error: you do not have permission to do that');
return;
}
await shortcut.destroy();
await msg.channel.send('shortcut deleted');
break;
}
default: {
await msg.channel.send('error: unknown command');
}
}
}
}
}

View file

@ -1,7 +1,9 @@
import container from './inversify.config';
import Spotify from 'spotify-web-api-node'; import Spotify from 'spotify-web-api-node';
import makeDir from 'make-dir';
import container from './inversify.config';
import {TYPES} from './types'; import {TYPES} from './types';
import Bot from './bot'; import Bot from './bot';
import {sequelize} from './utils/db';
let bot = container.get<Bot>(TYPES.Bot); let bot = container.get<Bot>(TYPES.Bot);
const spotify = container.get<Spotify>(TYPES.Lib.Spotify); const spotify = container.get<Spotify>(TYPES.Lib.Spotify);
@ -11,5 +13,11 @@ const spotify = container.get<Spotify>(TYPES.Lib.Spotify);
spotify.setAccessToken(auth.body.access_token); spotify.setAccessToken(auth.body.access_token);
// Create data directories if necessary
await makeDir(container.get(TYPES.Config.DATA_DIR));
await makeDir(container.get(TYPES.Config.CACHE_DIR));
await sequelize.sync({});
bot.listen(); bot.listen();
})(); })();

View file

@ -28,6 +28,7 @@ import Pause from './commands/pause';
import Play from './commands/play'; import Play from './commands/play';
import QueueCommad from './commands/queue'; import QueueCommad from './commands/queue';
import Seek from './commands/seek'; import Seek from './commands/seek';
import Shortcuts from './commands/shortcuts';
import Shuffle from './commands/shuffle'; import Shuffle from './commands/shuffle';
import Skip from './commands/skip'; import Skip from './commands/skip';
import Unskip from './commands/unskip'; import Unskip from './commands/unskip';
@ -50,6 +51,7 @@ container.bind<Command>(TYPES.Command).to(Pause).inSingletonScope();
container.bind<Command>(TYPES.Command).to(Play).inSingletonScope(); container.bind<Command>(TYPES.Command).to(Play).inSingletonScope();
container.bind<Command>(TYPES.Command).to(QueueCommad).inSingletonScope(); container.bind<Command>(TYPES.Command).to(QueueCommad).inSingletonScope();
container.bind<Command>(TYPES.Command).to(Seek).inSingletonScope(); container.bind<Command>(TYPES.Command).to(Seek).inSingletonScope();
container.bind<Command>(TYPES.Command).to(Shortcuts).inSingletonScope();
container.bind<Command>(TYPES.Command).to(Shuffle).inSingletonScope(); container.bind<Command>(TYPES.Command).to(Shuffle).inSingletonScope();
container.bind<Command>(TYPES.Command).to(Skip).inSingletonScope(); container.bind<Command>(TYPES.Command).to(Skip).inSingletonScope();
container.bind<Command>(TYPES.Command).to(Unskip).inSingletonScope(); container.bind<Command>(TYPES.Command).to(Unskip).inSingletonScope();

View file

@ -1,5 +1,7 @@
import Settings from './settings'; import Settings from './settings';
import Shortcut from './shortcut';
export { export {
Settings Settings,
Shortcut
}; };

23
src/models/shortcut.ts Normal file
View file

@ -0,0 +1,23 @@
import {Table, Column, PrimaryKey, Model, AutoIncrement, Index} from 'sequelize-typescript';
@Table
export default class Shortcut extends Model<Shortcut> {
@PrimaryKey
@AutoIncrement
@Column
id!: number;
@Column
@Index
guildId!: string;
@Column
authorId!: string;
@Column
@Index
shortcut!: string;
@Column
command!: string;
}

View file

@ -1,11 +1,11 @@
import {Sequelize} from 'sequelize-typescript'; import {Sequelize} from 'sequelize-typescript';
import path from 'path'; import path from 'path';
import {DATA_DIR} from '../utils/config'; import {DATA_DIR} from '../utils/config';
import {Settings} from '../models'; import {Settings, Shortcut} from '../models';
export const sequelize = new Sequelize({ export const sequelize = new Sequelize({
dialect: 'sqlite', dialect: 'sqlite',
database: 'muse', database: 'muse',
storage: path.join(DATA_DIR, 'db.sqlite'), storage: path.join(DATA_DIR, 'db.sqlite'),
models: [Settings] models: [Settings, Shortcut]
}); });