diff --git a/src/bot.ts b/src/bot.ts index 91fd71b..d6d6903 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,9 +1,7 @@ -import makeDir from 'make-dir'; import {Client, Message, Collection} from 'discord.js'; import {inject, injectable} from 'inversify'; import {TYPES} from './types'; -import {Settings} from './models'; -import {sequelize} from './utils/db'; +import {Settings, Shortcut} from './models'; import handleGuildCreate from './events/guild-create'; import container from './inversify.config'; import Command from './commands'; @@ -13,16 +11,12 @@ export default class { private readonly client: Client; private readonly token: string; private readonly clientId: string; - private readonly dataDir: string; - private readonly cacheDir: string; private readonly commands!: Collection; - 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.token = token; this.clientId = clientId; - this.dataDir = dataDir; - this.cacheDir = cacheDir; this.commands = new Collection(); } @@ -58,17 +52,31 @@ export default class { return; } - const args = msg.content.slice(prefix.length).split(/ +/); + let args = msg.content.slice(prefix.length).split(/ +/); 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; } try { - const handler = this.commands.get(command); - - handler!.execute(msg, args); + handler.execute(msg, args); } catch (error) { console.error(error); msg.reply('there was an error trying to execute that command!'); @@ -76,15 +84,13 @@ export default class { }); 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`); }); + this.client.on('error', error => { + console.error(error); + }); + // Register event handlers this.client.on('guildCreate', handleGuildCreate); diff --git a/src/commands/shortcuts.ts b/src/commands/shortcuts.ts new file mode 100644 index 0000000..2d62370 --- /dev/null +++ b/src/commands/shortcuts.ts @@ -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 { + 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'); + } + } + } + } +} diff --git a/src/index.ts b/src/index.ts index c4ce13e..2d1ea66 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,9 @@ -import container from './inversify.config'; import Spotify from 'spotify-web-api-node'; +import makeDir from 'make-dir'; +import container from './inversify.config'; import {TYPES} from './types'; import Bot from './bot'; +import {sequelize} from './utils/db'; let bot = container.get(TYPES.Bot); const spotify = container.get(TYPES.Lib.Spotify); @@ -11,5 +13,11 @@ const spotify = container.get(TYPES.Lib.Spotify); 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(); })(); diff --git a/src/inversify.config.ts b/src/inversify.config.ts index afdd5b8..5c17572 100644 --- a/src/inversify.config.ts +++ b/src/inversify.config.ts @@ -28,6 +28,7 @@ import Pause from './commands/pause'; import Play from './commands/play'; import QueueCommad from './commands/queue'; import Seek from './commands/seek'; +import Shortcuts from './commands/shortcuts'; import Shuffle from './commands/shuffle'; import Skip from './commands/skip'; import Unskip from './commands/unskip'; @@ -50,6 +51,7 @@ container.bind(TYPES.Command).to(Pause).inSingletonScope(); container.bind(TYPES.Command).to(Play).inSingletonScope(); container.bind(TYPES.Command).to(QueueCommad).inSingletonScope(); container.bind(TYPES.Command).to(Seek).inSingletonScope(); +container.bind(TYPES.Command).to(Shortcuts).inSingletonScope(); container.bind(TYPES.Command).to(Shuffle).inSingletonScope(); container.bind(TYPES.Command).to(Skip).inSingletonScope(); container.bind(TYPES.Command).to(Unskip).inSingletonScope(); diff --git a/src/models/index.ts b/src/models/index.ts index 6898b0e..2971df5 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,5 +1,7 @@ import Settings from './settings'; +import Shortcut from './shortcut'; export { - Settings + Settings, + Shortcut }; diff --git a/src/models/shortcut.ts b/src/models/shortcut.ts new file mode 100644 index 0000000..7ce1177 --- /dev/null +++ b/src/models/shortcut.ts @@ -0,0 +1,23 @@ +import {Table, Column, PrimaryKey, Model, AutoIncrement, Index} from 'sequelize-typescript'; + +@Table +export default class Shortcut extends Model { + @PrimaryKey + @AutoIncrement + @Column + id!: number; + + @Column + @Index + guildId!: string; + + @Column + authorId!: string; + + @Column + @Index + shortcut!: string; + + @Column + command!: string; +} diff --git a/src/utils/db.ts b/src/utils/db.ts index 6010df4..aeb0614 100644 --- a/src/utils/db.ts +++ b/src/utils/db.ts @@ -1,11 +1,11 @@ import {Sequelize} from 'sequelize-typescript'; import path from 'path'; import {DATA_DIR} from '../utils/config'; -import {Settings} from '../models'; +import {Settings, Shortcut} from '../models'; export const sequelize = new Sequelize({ dialect: 'sqlite', database: 'muse', storage: path.join(DATA_DIR, 'db.sqlite'), - models: [Settings] + models: [Settings, Shortcut] });