"use strict";
// CommandHandler.ts - Command handler (noud02)
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const Eris = require("eris");
const minimist = require("minimist");
const Context_1 = require("./Context");
const Ratelimiter_1 = require("./Ratelimiter");
/**
* Command handler class
*
* @export
* @class CommandHandler
*/
class CommandHandler {
constructor(shard) {
this.shard = shard;
/**
* Map of buckets
*
* @type {Map<string, Ratelimiter>}
*/
this.buckets = new Map();
}
/**
* Initializes the command handler
*
* @returns {Promise<void>}
*/
init() {
// link the messageCreate event to checkMessage to check if the message is a command
this.shard.on("messageCreate", (msg) => this.checkMessage(msg).catch((e) => void (e))); // tslint:disable-line:no-unused-expression
return Promise.resolve();
}
/**
* Check if a message is a command
*
* @param {Eris.Message} msg Message
* @returns {Promise<void>}
*/
checkMessage(msg) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let guildPrefixes = [];
let usedPrefix = "";
let command = "";
let args;
if (msg.channel instanceof Eris.GuildChannel) {
const res = yield this.shard.pg.select("guilds", `id = '${msg.channel.guild.id}'`);
guildPrefixes = res.rows[0].prefixes;
}
if (msg.author.bot) {
return;
}
if (guildPrefixes.length > 0) {
for (const prefix of guildPrefixes) {
if (!msg.content.startsWith(prefix)) {
continue;
}
else {
usedPrefix = prefix;
break;
}
}
}
else {
for (const prefix of this.shard.hibikiOptions.hibiki.prefixes) {
if (!msg.content.startsWith(prefix)) {
continue;
}
else {
usedPrefix = prefix;
break;
}
}
}
if (!usedPrefix) {
return Promise.reject(new Error("Message not a command"));
}
command = msg.content.substring(usedPrefix.length).split(" ")[0];
args = minimist(msg.content.substring(usedPrefix.length).split(" ").slice(1), { strings: true });
return this.executeCommand(msg, command, args, usedPrefix);
});
}
/**
* Execute a command
*
* @param {Eris.Message} msg Message
* @param {string} command Command name
* @param {minimist.ParsedArgs} args Arguments
* @param {string} prefix Prefix used
* @returns {Promise<any>}
*/
executeCommand(msg, command, args, prefix) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const cmd = this.shard.ext.commands.get(command);
let bucket = this.buckets.get(msg.author.id);
let ok = false;
if (!cmd) {
return Promise.reject(new Error(`Command ${command} not found`));
}
const useBucket = () => {
if (bucket) {
switch (bucket.use()) {
default:
case "OK": {
ok = true;
break;
}
case "RATELIMITED": {
const waitTime = (bucket.lastUse + bucket.time / 2) - Date.now();
ok = false;
if (bucket.sentWarn) {
return;
}
else {
return msg.channel.createMessage(this.shard.lm.t("commands.cooldown", { username: msg.author.username, time: Math.round(waitTime / 1000) }))
.then(() => bucket ? bucket.sentWarn = true : true) // tslint:disable-line;no-unused-expression
.catch((e) => console.error(e)); // tslint:disable-line:no-unused-expression
}
}
}
}
return;
};
if (this.shard.hibikiOptions.hibiki.owners.indexOf(msg.author.id) === -1) {
if (bucket) {
useBucket();
}
else {
this.buckets.set(msg.author.id, new Ratelimiter_1.Ratelimiter());
bucket = this.buckets.get(msg.author.id);
useBucket();
}
}
else {
ok = true;
}
if (cmd.ownerOnly && this.shard.hibikiOptions.hibiki.owners.indexOf(msg.author.id) === -1) {
return Promise.reject(new Error("Command is owner only"));
}
let newArgs = {};
let newPerms = {};
let newBotPerms = {};
try {
newArgs = yield this.checkArguments(msg, args._, cmd.args || []);
}
catch (e) {
return msg.channel.createMessage(e);
}
try {
newPerms = yield this.checkPermissions(msg, cmd.perms || []);
}
catch (e) {
return msg.channel.createMessage(e);
}
try {
newBotPerms = yield this.checkBotPermissions(msg, cmd.botPerms || []);
}
catch (e) {
return msg.channel.createMessage(e);
}
const ctx = new Context_1.Context(this.shard, msg, prefix, command, args, newArgs);
if (ok) {
return cmd.run(ctx);
}
else {
return Promise.resolve();
}
});
}
/**
* Check the permissions
*
* @param {Eris.Message} msg Message
* @param {ICommandPermission[]} perms Array of permissions
* @returns {Promise<map>}
*/
checkPermissions(msg, perms) {
const newPerms = {};
if (!(msg.channel instanceof Eris.GuildChannel)) {
for (const perm of perms) {
newPerms[perm.name] = true;
}
return Promise.resolve(newPerms);
}
for (const perm of perms) {
if (!perm.optional && !msg.channel.permissionsOf(msg.author.id).has(perm.name)) {
return Promise.reject(this.shard.lm.t("permissions.user_lack_perms", {
permission: this.shard.lm.localizedPerm(perm.name),
username: msg.author.username,
}));
}
newPerms[perm.name] = msg.channel.permissionsOf(msg.author.id).has(perm.name);
}
return Promise.resolve(newPerms);
}
/**
* Check the bot permissions
*
* @param {Eris.Message} msg Message
* @param {ICommandPermission[]} perms Array of permissions
* @returns {Promise<map>}
*/
checkBotPermissions(msg, perms) {
const newPerms = {};
if (!(msg.channel instanceof Eris.GuildChannel)) {
for (const perm of perms) {
newPerms[perm.name] = true;
}
return Promise.resolve(newPerms);
}
for (const perm of perms) {
if (!perm.optional && !msg.channel.permissionsOf(this.shard.user.id).has(perm.name)) {
return Promise.reject(this.shard.lm.t("permissions.bot_lack_perms", {
permission: this.shard.lm.localizedPerm(perm.name),
username: msg.author.username,
}));
}
newPerms[perm.name] = msg.channel.permissionsOf(this.shard.user.id).has(perm.name);
}
return Promise.resolve(newPerms);
}
/**
* Check the arguments and return new args
*
* @todo add search things
* @todo add websockets and try to get the guild/user from another shard
*
* @param {Eris.Message} msg Message
* @param {string[]} given Array of given arguments
* @param {ICommandArg[]} args Array of command args
* @returns {Promise<map>}
*/
checkArguments(msg, given, args) {
const newArgs = {};
for (const arg of args) {
const i = args.indexOf(arg);
if (!given[i]) {
if (arg.optional) {
continue;
}
else {
return Promise.reject(this.shard.lm.t("commands.argument_not_specified", { username: msg.author.username, argument: arg.name }));
}
}
switch (arg.type) {
case "number": {
if (isNaN(given[i])) {
return Promise.reject(this.shard.lm.t("commands.invalid_argument_type", {
argument: arg.name,
type: arg.type,
username: msg.author.username,
}));
}
break;
}
case "user": {
const mention = /<@!?(\d+)>/i;
const userdisc = /.{0,32}#\d{4}/i;
const username = /.{0,32}/i;
const id = /\d+/i;
if (!mention.test(given[i]) && !userdisc.test(given[i]) && !username.test(given[i]) && !id.test(given[i])) {
return Promise.reject(this.shard.lm.t("commands.invalid_argument_type", {
argument: arg.name,
type: arg.type,
username: msg.author.username,
}));
}
// meme
if (given[i] === "me") {
newArgs[arg.name] = msg.author;
}
else if (mention.test(given[i])) {
const res = mention.exec(given[i]);
if (res) {
const user = this.shard.users.get(res[1]);
if (user) {
newArgs[arg.name] = user;
}
}
}
else if (userdisc.test(given[i])) {
if (!(msg.channel instanceof Eris.GuildChannel)) {
return Promise.reject(this.shard.lm.t("commands.guild_only", { username: msg.author.username }));
}
const user = msg.channel.guild.members.filter((member) => `${member.username}#${member.discriminator}` === given[i])[0];
if (!user) {
return Promise.reject(this.shard.lm.t("search.user_not_found", { username: msg.author.username }));
}
newArgs[arg.name] = user;
}
else if (id.test(given[i])) {
const user = this.shard.users.get(given[i]);
if (!user) {
return Promise.reject(this.shard.lm.t("search.user_not_found", { username: msg.author.username }));
}
newArgs[arg.name] = user;
}
else if (username.test(given[i])) {
if (!(msg.channel instanceof Eris.GuildChannel)) {
return Promise.reject(this.shard.lm.t("commands.guild_only", { username: msg.author.username }));
}
const user = msg.channel.guild.members.filter((member) => `${member.username}` === given[i])[0];
if (!user) {
return Promise.reject(this.shard.lm.t("search.user_not_found", { username: msg.author.username }));
}
newArgs[arg.name] = user;
}
break;
}
case "channel": {
const mention = /<#(\d+)>/i;
const name = /[^\s]{0,100}/i;
const id = /\d+/i;
if (!(msg.channel instanceof Eris.GuildChannel)) {
return Promise.reject(this.shard.lm.t("commands.guild_only", { username: msg.author.username }));
}
if (!mention.test(given[i]) && !name.test(given[i]) && !id.test(given[i])) {
return Promise.reject(this.shard.lm.t("commands.invalid_argument_type", {
argument: arg.name,
type: arg.type,
username: msg.author.username,
}));
}
if (given[i] === "this") {
newArgs[arg.name] = msg.channel;
}
else if (mention.test(given[i])) {
const res = mention.exec(given[i]);
if (!res) {
return Promise.reject(this.shard.lm.t("search.channel_not_found", { username: msg.author.username }));
}
const channel = msg.channel.guild.channels.filter((c) => c.type === 0 && c.id === res[0])[0];
if (!channel) {
return Promise.reject(this.shard.lm.t("search.channel_not_found", { username: msg.author.username }));
}
newArgs[arg.name] = channel;
}
else if (id.test(given[i])) {
const channel = msg.channel.guild.channels.filter((c) => c.type === 0 && c.id === given[i])[0];
if (!channel) {
return Promise.reject(this.shard.lm.t("search.channel_not_found", { username: msg.author.username }));
}
newArgs[arg.name] = channel;
}
else if (name.test(given[i])) {
const channel = msg.channel.guild.channels.filter((c) => c.type === 0 && c.name.indexOf(given[i]) > -1)[0];
if (!channel) {
return Promise.reject(this.shard.lm.t("search.channel_not_found", { username: msg.author.username }));
}
newArgs[arg.name] = channel;
}
break;
}
case "role": {
const mention = /<&\d+>/i;
const name = /.{0,100}/i;
const id = /\d+/i;
if (!(msg.channel instanceof Eris.GuildChannel)) {
return Promise.reject(this.shard.lm.t("commands.guild_only", { username: msg.author.username }));
}
if (!mention.test(given[i]) && !name.test(given[i]) && !id.test(given[i])) {
return Promise.reject(this.shard.lm.t("commands.invalid_argument_type", {
argument: arg.name,
type: arg.type,
username: msg.author.username,
}));
}
if (mention.test(given[i])) {
const res = mention.exec(given[i]);
if (!res) {
return Promise.reject(this.shard.lm.t("search.role_not_found", { username: msg.author.username }));
}
const role = msg.channel.guild.roles.get(res[0]);
if (!role) {
return Promise.reject(this.shard.lm.t("search.role_not_found", { username: msg.author.username }));
}
newArgs[arg.name] = role;
}
else if (name.test(given[i])) {
const role = msg.channel.guild.roles.filter((r) => r.name.indexOf(given[i]) > -1)[0];
if (!role) {
return Promise.reject(this.shard.lm.t("search.role_not_found", { username: msg.author.username }));
}
newArgs[arg.name] = role;
}
break;
}
case "guild": {
const name = /.{0,100}/i;
const id = /\d+/i;
if (!name.test(given[i]) && !id.test(given[i])) {
return Promise.reject(this.shard.lm.t("commands.invalid_argument_type", {
argument: arg.name,
type: arg.type,
username: msg.author.username,
}));
}
if (id.test(given[i])) {
const guild = this.shard.guilds.get(given[i]);
if (!guild) {
return Promise.reject(this.shard.lm.t("search.guild_not_found", { username: msg.author.username }));
}
newArgs[arg.name] = guild;
}
else if (name.test(given[i])) {
const guild = this.shard.guilds.filter((g) => g.name.indexOf(given[i]) > -1)[0];
if (!guild) {
return Promise.reject(this.shard.lm.t("search.guild_not_found", { username: msg.author.username }));
}
newArgs[arg.name] = guild;
}
break;
}
case "string":
default: {
newArgs[arg.name] = given[i];
return Promise.resolve(newArgs);
}
}
}
return Promise.resolve(newArgs);
}
}
exports.CommandHandler = CommandHandler;