mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-15 05:41:51 +00:00
Switch from Knex to TypeORM. Update Knub.
This commit is contained in:
parent
e3ff4cef45
commit
f9c16263ae
49 changed files with 1192 additions and 1395 deletions
34
knexfile.js
34
knexfile.js
|
@ -1,34 +0,0 @@
|
||||||
require('dotenv').config();
|
|
||||||
|
|
||||||
const moment = require('moment-timezone');
|
|
||||||
moment.tz.setDefault('UTC');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
client: 'mysql2',
|
|
||||||
connection: {
|
|
||||||
host: process.env.DB_HOST,
|
|
||||||
user: process.env.DB_USER,
|
|
||||||
password: process.env.DB_PASSWORD,
|
|
||||||
database: process.env.DB_DATABASE,
|
|
||||||
charset: 'utf8mb4',
|
|
||||||
timezone: 'UTC',
|
|
||||||
supportBigNumbers: true,
|
|
||||||
bigNumberStrings: true,
|
|
||||||
dateStrings: true,
|
|
||||||
typeCast(field, next) {
|
|
||||||
if (field.type === 'DATETIME') {
|
|
||||||
const val = field.string();
|
|
||||||
return val != null ? moment(val).format('YYYY-MM-DD HH:mm:ss') : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
pool: {
|
|
||||||
afterCreate(conn, cb) {
|
|
||||||
conn.query('SET time_zone = "+00:00";', err => {
|
|
||||||
cb(err, conn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
112
migrations/1540519249973-CreatePreTypeORMTables.ts
Normal file
112
migrations/1540519249973-CreatePreTypeORMTables.ts
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class CreatePreTypeORMTables1540519249973 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS \`archives\` (
|
||||||
|
\`id\` VARCHAR(36) NOT NULL,
|
||||||
|
\`guild_id\` VARCHAR(20) NOT NULL,
|
||||||
|
\`body\` MEDIUMTEXT NOT NULL,
|
||||||
|
\`created_at\` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
\`expires_at\` DATETIME NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (\`id\`)
|
||||||
|
)
|
||||||
|
COLLATE='utf8mb4_general_ci'
|
||||||
|
`);
|
||||||
|
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS \`cases\` (
|
||||||
|
\`id\` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
\`guild_id\` BIGINT(20) UNSIGNED NOT NULL,
|
||||||
|
\`case_number\` INT(10) UNSIGNED NOT NULL,
|
||||||
|
\`user_id\` BIGINT(20) UNSIGNED NOT NULL,
|
||||||
|
\`user_name\` VARCHAR(128) NOT NULL,
|
||||||
|
\`mod_id\` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
|
||||||
|
\`mod_name\` VARCHAR(128) NULL DEFAULT NULL,
|
||||||
|
\`type\` INT(10) UNSIGNED NOT NULL,
|
||||||
|
\`audit_log_id\` BIGINT(20) NULL DEFAULT NULL,
|
||||||
|
\`created_at\` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (\`id\`),
|
||||||
|
UNIQUE INDEX \`mod_actions_guild_id_case_number_unique\` (\`guild_id\`, \`case_number\`),
|
||||||
|
UNIQUE INDEX \`mod_actions_audit_log_id_unique\` (\`audit_log_id\`),
|
||||||
|
INDEX \`mod_actions_user_id_index\` (\`user_id\`),
|
||||||
|
INDEX \`mod_actions_mod_id_index\` (\`mod_id\`),
|
||||||
|
INDEX \`mod_actions_created_at_index\` (\`created_at\`)
|
||||||
|
)
|
||||||
|
COLLATE = 'utf8mb4_general_ci'
|
||||||
|
`);
|
||||||
|
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS \`case_notes\` (
|
||||||
|
\`id\` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
\`case_id\` INT(10) UNSIGNED NOT NULL,
|
||||||
|
\`mod_id\` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
|
||||||
|
\`mod_name\` VARCHAR(128) NULL DEFAULT NULL,
|
||||||
|
\`body\` TEXT NOT NULL,
|
||||||
|
\`created_at\` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (\`id\`),
|
||||||
|
INDEX \`mod_action_notes_mod_action_id_index\` (\`case_id\`),
|
||||||
|
INDEX \`mod_action_notes_mod_id_index\` (\`mod_id\`),
|
||||||
|
INDEX \`mod_action_notes_created_at_index\` (\`created_at\`)
|
||||||
|
)
|
||||||
|
COLLATE = 'utf8mb4_general_ci'
|
||||||
|
`);
|
||||||
|
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS \`mutes\` (
|
||||||
|
\`guild_id\` BIGINT(20) UNSIGNED NOT NULL,
|
||||||
|
\`user_id\` BIGINT(20) UNSIGNED NOT NULL,
|
||||||
|
\`created_at\` DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
\`expires_at\` DATETIME NULL DEFAULT NULL,
|
||||||
|
\`case_id\` INT(10) UNSIGNED NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (\`guild_id\`, \`user_id\`),
|
||||||
|
INDEX \`mutes_expires_at_index\` (\`expires_at\`),
|
||||||
|
INDEX \`mutes_case_id_foreign\` (\`case_id\`),
|
||||||
|
CONSTRAINT \`mutes_case_id_foreign\` FOREIGN KEY (\`case_id\`) REFERENCES \`cases\` (\`id\`)
|
||||||
|
ON DELETE SET NULL
|
||||||
|
)
|
||||||
|
COLLATE = 'utf8mb4_general_ci'
|
||||||
|
`);
|
||||||
|
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS \`persisted_data\` (
|
||||||
|
\`guild_id\` VARCHAR(20) NOT NULL,
|
||||||
|
\`user_id\` VARCHAR(20) NOT NULL,
|
||||||
|
\`roles\` VARCHAR(1024) NULL DEFAULT NULL,
|
||||||
|
\`nickname\` VARCHAR(255) NULL DEFAULT NULL,
|
||||||
|
\`is_voice_muted\` INT(11) NOT NULL DEFAULT '0',
|
||||||
|
PRIMARY KEY (\`guild_id\`, \`user_id\`)
|
||||||
|
)
|
||||||
|
COLLATE = 'utf8mb4_general_ci'
|
||||||
|
`);
|
||||||
|
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS \`reaction_roles\` (
|
||||||
|
\`guild_id\` VARCHAR(20) NOT NULL,
|
||||||
|
\`channel_id\` VARCHAR(20) NOT NULL,
|
||||||
|
\`message_id\` VARCHAR(20) NOT NULL,
|
||||||
|
\`emoji\` VARCHAR(20) NOT NULL,
|
||||||
|
\`role_id\` VARCHAR(20) NOT NULL,
|
||||||
|
PRIMARY KEY (\`guild_id\`, \`channel_id\`, \`message_id\`, \`emoji\`),
|
||||||
|
INDEX \`reaction_roles_message_id_emoji_index\` (\`message_id\`, \`emoji\`)
|
||||||
|
)
|
||||||
|
COLLATE = 'utf8mb4_general_ci'
|
||||||
|
`);
|
||||||
|
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS \`tags\` (
|
||||||
|
\`guild_id\` BIGINT(20) UNSIGNED NOT NULL,
|
||||||
|
\`tag\` VARCHAR(64) NOT NULL,
|
||||||
|
\`user_id\` BIGINT(20) UNSIGNED NOT NULL,
|
||||||
|
\`body\` TEXT NOT NULL,
|
||||||
|
\`created_at\` DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (\`guild_id\`, \`tag\`)
|
||||||
|
)
|
||||||
|
COLLATE = 'utf8mb4_general_ci'
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
// No down function since we're migrating (hehe) from another migration system (knex)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +0,0 @@
|
||||||
exports.up = async function(knex) {
|
|
||||||
if (! await knex.schema.hasTable('mod_actions')) {
|
|
||||||
await knex.schema.createTable('mod_actions', table => {
|
|
||||||
table.increments('id');
|
|
||||||
table.bigInteger('guild_id').unsigned().notNullable();
|
|
||||||
table.integer('case_number').unsigned().notNullable();
|
|
||||||
table.bigInteger('user_id').index().unsigned().notNullable();
|
|
||||||
table.string('user_name', 128).notNullable();
|
|
||||||
table.bigInteger('mod_id').index().unsigned().nullable().defaultTo(null);
|
|
||||||
table.string('mod_name', 128).nullable().defaultTo(null);
|
|
||||||
table.integer('action_type').unsigned().notNullable();
|
|
||||||
table.bigInteger('audit_log_id').unique().nullable().defaultTo(null);
|
|
||||||
table.dateTime('created_at').index().defaultTo(knex.raw('NOW()')).notNullable();
|
|
||||||
|
|
||||||
table.unique(['guild_id', 'case_number']);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! await knex.schema.hasTable('mod_action_notes')) {
|
|
||||||
await knex.schema.createTable('mod_action_notes', table => {
|
|
||||||
table.increments('id');
|
|
||||||
table.integer('mod_action_id').unsigned().notNullable().index().references('id').inTable('mod_actions').onDelete('CASCADE');
|
|
||||||
table.bigInteger('mod_id').index().unsigned().nullable().defaultTo(null);
|
|
||||||
table.string('mod_name', 128).nullable().defaultTo(null);
|
|
||||||
table.text('body').notNullable();
|
|
||||||
table.dateTime('created_at').index().defaultTo(knex.raw('NOW()')).notNullable();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async function(knex) {
|
|
||||||
await knex.schema.dropTableIfExists('mod_action_notes');
|
|
||||||
await knex.schema.dropTableIfExists('mod_actions');
|
|
||||||
};
|
|
|
@ -1,17 +0,0 @@
|
||||||
exports.up = async function(knex) {
|
|
||||||
if (! await knex.schema.hasTable('mutes')) {
|
|
||||||
await knex.schema.createTable('mutes', table => {
|
|
||||||
table.bigInteger('guild_id').unsigned().notNullable();
|
|
||||||
table.bigInteger('user_id').unsigned().notNullable();
|
|
||||||
table.dateTime('created_at').defaultTo(knex.raw('NOW()'));
|
|
||||||
table.dateTime('expires_at').nullable().defaultTo(null);
|
|
||||||
|
|
||||||
table.primary(['guild_id', 'user_id']);
|
|
||||||
table.index(['expires_at']);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async function(knex) {
|
|
||||||
await knex.schema.dropTableIfExists('mutes');
|
|
||||||
};
|
|
|
@ -1,21 +0,0 @@
|
||||||
exports.up = async function(knex) {
|
|
||||||
await knex.schema.renameTable('mod_actions', 'cases');
|
|
||||||
await knex.schema.renameTable('mod_action_notes', 'case_notes');
|
|
||||||
await knex.schema.table('cases', table => {
|
|
||||||
table.renameColumn('action_type', 'type');
|
|
||||||
});
|
|
||||||
await knex.schema.table('case_notes', table => {
|
|
||||||
table.renameColumn('mod_action_id', 'case_id');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async function(knex) {
|
|
||||||
await knex.schema.table('cases', table => {
|
|
||||||
table.renameColumn('type', 'action_type');
|
|
||||||
});
|
|
||||||
await knex.schema.table('case_notes', table => {
|
|
||||||
table.renameColumn('case_id', 'mod_action_id');
|
|
||||||
});
|
|
||||||
await knex.schema.renameTable('cases', 'mod_actions');
|
|
||||||
await knex.schema.renameTable('case_notes', 'mod_action_notes');
|
|
||||||
};
|
|
|
@ -1,18 +0,0 @@
|
||||||
exports.up = async function(knex, Promise) {
|
|
||||||
if (! await knex.schema.hasTable('reaction_roles')) {
|
|
||||||
await knex.schema.createTable('reaction_roles', table => {
|
|
||||||
table.string('guild_id', 20).notNullable();
|
|
||||||
table.string('channel_id', 20).notNullable();
|
|
||||||
table.string('message_id', 20).notNullable();
|
|
||||||
table.string('emoji', 20).notNullable();
|
|
||||||
table.string('role_id', 20).notNullable();
|
|
||||||
|
|
||||||
table.primary(['guild_id', 'channel_id', 'message_id', 'emoji']);
|
|
||||||
table.index(['message_id', 'emoji']);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async function(knex, Promise) {
|
|
||||||
await knex.schema.dropTableIfExists('reaction_roles');
|
|
||||||
};
|
|
|
@ -1,17 +0,0 @@
|
||||||
exports.up = async function(knex, Promise) {
|
|
||||||
if (! await knex.schema.hasTable('persisted_data')) {
|
|
||||||
await knex.schema.createTable('persisted_data', table => {
|
|
||||||
table.string('guild_id', 20).notNullable();
|
|
||||||
table.string('user_id', 20).notNullable();
|
|
||||||
table.string('roles', 1024).nullable().defaultTo(null);
|
|
||||||
table.string('nickname', 255).nullable().defaultTo(null);
|
|
||||||
table.integer('is_voice_muted').notNullable().defaultTo(0);
|
|
||||||
|
|
||||||
table.primary(['guild_id', 'user_id']);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async function(knex, Promise) {
|
|
||||||
await knex.schema.dropTableIfExists('persisted_data');
|
|
||||||
};
|
|
|
@ -1,15 +0,0 @@
|
||||||
exports.up = async function(knex, Promise) {
|
|
||||||
if (! await knex.schema.hasTable('spam_logs')) {
|
|
||||||
await knex.schema.createTable('spam_logs', table => {
|
|
||||||
table.string('id', 36).notNullable().primary();
|
|
||||||
table.string('guild_id', 20).notNullable();
|
|
||||||
table.text('body', 'mediumtext').notNullable();
|
|
||||||
table.dateTime('created_at').defaultTo(knex.raw('NOW()')).notNullable();
|
|
||||||
table.dateTime('expires_at').nullable();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async function(knex, Promise) {
|
|
||||||
await knex.schema.dropTableIfExists('spam_logs');
|
|
||||||
};
|
|
|
@ -1,12 +0,0 @@
|
||||||
exports.up = async function(knex, Promise) {
|
|
||||||
await knex.schema.table('mutes', table => {
|
|
||||||
table.integer('case_id').unsigned().nullable().defaultTo(null).after('user_id').references('id').inTable('cases').onDelete('SET NULL');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async function(knex, Promise) {
|
|
||||||
await knex.schema.table('mutes', table => {
|
|
||||||
table.dropForeign('case_id');
|
|
||||||
table.dropColumn('case_id');
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,17 +0,0 @@
|
||||||
exports.up = async function(knex) {
|
|
||||||
if (! await knex.schema.hasTable('tags')) {
|
|
||||||
await knex.schema.createTable('tags', table => {
|
|
||||||
table.bigInteger('guild_id').unsigned().notNullable();
|
|
||||||
table.string('tag', 64).notNullable();
|
|
||||||
table.bigInteger('user_id').unsigned().notNullable();
|
|
||||||
table.text('body').notNullable();
|
|
||||||
table.dateTime('created_at').defaultTo(knex.raw('NOW()'));
|
|
||||||
|
|
||||||
table.primary(['guild_id', 'tag']);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async function(knex) {
|
|
||||||
await knex.schema.dropTableIfExists('tags');
|
|
||||||
};
|
|
|
@ -1,7 +0,0 @@
|
||||||
exports.up = async function(knex) {
|
|
||||||
await knex.schema.renameTable('spam_logs', 'archives');
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async function(knex) {
|
|
||||||
await knex.schema.renameTable('archives', 'spam_logs');
|
|
||||||
};
|
|
38
ormconfig.js
Normal file
38
ormconfig.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
const moment = require('moment-timezone');
|
||||||
|
moment.tz.setDefault('UTC');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
type: "mysql",
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
charset: 'utf8mb4',
|
||||||
|
supportBigNumbers: true,
|
||||||
|
bigNumberStrings: true,
|
||||||
|
dateStrings: true,
|
||||||
|
synchronize: false,
|
||||||
|
|
||||||
|
// Entities
|
||||||
|
entities: [`${__dirname}/src/data/entities/*.ts`],
|
||||||
|
|
||||||
|
// Pool options
|
||||||
|
extra: {
|
||||||
|
typeCast(field, next) {
|
||||||
|
if (field.type === 'DATETIME') {
|
||||||
|
const val = field.string();
|
||||||
|
return val != null ? moment(val).format('YYYY-MM-DD HH:mm:ss') : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Migrations
|
||||||
|
migrations: ["migrations/*.ts"],
|
||||||
|
cli: {
|
||||||
|
migrationsDir: "migrations"
|
||||||
|
},
|
||||||
|
};
|
1231
package-lock.json
generated
1231
package-lock.json
generated
File diff suppressed because it is too large
Load diff
22
package.json
22
package.json
|
@ -10,8 +10,7 @@
|
||||||
"precommit": "lint-staged",
|
"precommit": "lint-staged",
|
||||||
"postcommit": "git update-index --again",
|
"postcommit": "git update-index --again",
|
||||||
"format": "prettier --write \"./**/*.ts\"",
|
"format": "prettier --write \"./**/*.ts\"",
|
||||||
"db-migrate": "knex migrate:latest",
|
"typeorm": "ts-node ./node_modules/typeorm/cli.js"
|
||||||
"db-rollback": "knex migrate:rollback"
|
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.ts": [
|
"*.ts": [
|
||||||
|
@ -23,34 +22,35 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/knex": "0.0.64",
|
|
||||||
"@types/lodash.at": "^4.6.3",
|
"@types/lodash.at": "^4.6.3",
|
||||||
"@types/moment-timezone": "^0.5.6",
|
"@types/moment-timezone": "^0.5.6",
|
||||||
"@types/node": "^8.0.50",
|
|
||||||
"dotenv": "^4.0.0",
|
"dotenv": "^4.0.0",
|
||||||
"emoji-regex": "^7.0.0",
|
"emoji-regex": "^7.0.0",
|
||||||
"eris": "^0.8.6",
|
"eris": "^0.8.6",
|
||||||
"escape-string-regexp": "^1.0.5",
|
"escape-string-regexp": "^1.0.5",
|
||||||
"humanize-duration": "^3.15.0",
|
"humanize-duration": "^3.15.0",
|
||||||
"knex": "0.12.6",
|
"js-yaml": "^3.12.0",
|
||||||
"knub": "^10.1.0",
|
"knub": "^12.1.1",
|
||||||
"lodash.at": "^4.6.0",
|
"lodash.at": "^4.6.0",
|
||||||
"lodash.chunk": "^4.2.0",
|
"lodash.chunk": "^4.2.0",
|
||||||
"lodash.difference": "^4.5.0",
|
"lodash.difference": "^4.5.0",
|
||||||
"lodash.intersection": "^4.4.0",
|
"lodash.intersection": "^4.4.0",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"moment-timezone": "^0.5.21",
|
"moment-timezone": "^0.5.21",
|
||||||
"mysql2": "^1.6.0",
|
"mysql": "^2.16.0",
|
||||||
|
"reflect-metadata": "^0.1.12",
|
||||||
"tlds": "^1.203.1",
|
"tlds": "^1.203.1",
|
||||||
"ts-node": "^3.3.0",
|
"ts-node": "^3.3.0",
|
||||||
"typescript": "^2.9.2",
|
"typeorm": "^0.2.8",
|
||||||
|
"typescript": "^3.1.3",
|
||||||
"uuid": "^3.3.2"
|
"uuid": "^3.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^1.17.5",
|
"@types/node": "^10.12.0",
|
||||||
"lint-staged": "^7.2.0",
|
|
||||||
"prettier": "^1.8.2",
|
|
||||||
"husky": "^0.14.3",
|
"husky": "^0.14.3",
|
||||||
|
"lint-staged": "^7.2.0",
|
||||||
|
"nodemon": "^1.17.5",
|
||||||
|
"prettier": "^1.8.2",
|
||||||
"tslint": "^5.8.0",
|
"tslint": "^5.8.0",
|
||||||
"tslint-config-prettier": "^1.6.0"
|
"tslint-config-prettier": "^1.6.0"
|
||||||
}
|
}
|
||||||
|
|
13
src/SimpleError.ts
Normal file
13
src/SimpleError.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import util from "util";
|
||||||
|
|
||||||
|
export class SimpleError {
|
||||||
|
public message: string;
|
||||||
|
|
||||||
|
constructor(message: string) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
[util.inspect.custom](depth, options) {
|
||||||
|
return `Error: ${this.message}`;
|
||||||
|
}
|
||||||
|
}
|
50
src/data/BaseRepository.ts
Normal file
50
src/data/BaseRepository.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
export class BaseRepository {
|
||||||
|
private static guildInstances: Map<string, any>;
|
||||||
|
private nextRelations: string[];
|
||||||
|
|
||||||
|
protected guildId: string;
|
||||||
|
|
||||||
|
constructor(guildId: string) {
|
||||||
|
this.guildId = guildId;
|
||||||
|
this.nextRelations = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a cached instance of the inheriting class for the specified guildId,
|
||||||
|
* or creates a new instance if one doesn't exist yet
|
||||||
|
*/
|
||||||
|
public static getInstance<T extends typeof BaseRepository>(this: T, guildId: string): InstanceType<T> {
|
||||||
|
if (!this.guildInstances) {
|
||||||
|
this.guildInstances = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.guildInstances.has(guildId)) {
|
||||||
|
this.guildInstances.set(guildId, new this(guildId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.guildInstances.get(guildId) as InstanceType<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primes the specified relation(s) to be used in the next database operation.
|
||||||
|
* Can be chained.
|
||||||
|
*/
|
||||||
|
public with(relations: string | string[]): this {
|
||||||
|
if (Array.isArray(relations)) {
|
||||||
|
this.nextRelations.push(...relations);
|
||||||
|
} else {
|
||||||
|
this.nextRelations.push(relations);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets and resets the relations primed using with()
|
||||||
|
*/
|
||||||
|
protected getRelations(): string[] {
|
||||||
|
const relations = this.nextRelations || [];
|
||||||
|
this.nextRelations = [];
|
||||||
|
return relations;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
import { CaseType } from "./CaseType";
|
import { CaseTypes } from "./CaseTypes";
|
||||||
|
|
||||||
export const CaseTypeColors = {
|
export const CaseTypeColors = {
|
||||||
[CaseType.Note]: 0x3498db,
|
[CaseTypes.Note]: 0x3498db,
|
||||||
[CaseType.Warn]: 0xdae622,
|
[CaseTypes.Warn]: 0xdae622,
|
||||||
[CaseType.Mute]: 0xe6b122,
|
[CaseTypes.Mute]: 0xe6b122,
|
||||||
[CaseType.Unmute]: 0xa175b3,
|
[CaseTypes.Unmute]: 0xa175b3,
|
||||||
[CaseType.Kick]: 0xe67e22,
|
[CaseTypes.Kick]: 0xe67e22,
|
||||||
[CaseType.Softban]: 0xe67e22,
|
[CaseTypes.Softban]: 0xe67e22,
|
||||||
[CaseType.Ban]: 0xcb4314,
|
[CaseTypes.Ban]: 0xcb4314,
|
||||||
[CaseType.Unban]: 0x9b59b6
|
[CaseTypes.Unban]: 0x9b59b6
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export enum CaseType {
|
export enum CaseTypes {
|
||||||
Ban = 1,
|
Ban = 1,
|
||||||
Unban,
|
Unban,
|
||||||
Note,
|
Note,
|
|
@ -1,51 +1,52 @@
|
||||||
import uuid from "uuid/v4"; // tslint:disable-line
|
import uuid from "uuid/v4"; // tslint:disable-line
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import knex from "../knex";
|
import { ArchiveEntry } from "./entities/ArchiveEntry";
|
||||||
import SpamLog from "../models/SpamLog";
|
import { getRepository, Repository } from "typeorm";
|
||||||
|
import { BaseRepository } from "./BaseRepository";
|
||||||
|
|
||||||
const DEFAULT_EXPIRY_DAYS = 30;
|
const DEFAULT_EXPIRY_DAYS = 30;
|
||||||
|
|
||||||
function deleteExpiredArchives() {
|
export class GuildArchives extends BaseRepository {
|
||||||
knex("archives")
|
protected archives: Repository<ArchiveEntry>;
|
||||||
.where("expires_at", "<=", knex.raw("NOW()"))
|
|
||||||
.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteExpiredArchives();
|
|
||||||
setInterval(deleteExpiredArchives, 1000 * 60 * 60); // Clean expired archives every hour
|
|
||||||
|
|
||||||
export class GuildArchives {
|
|
||||||
protected guildId: string;
|
|
||||||
|
|
||||||
constructor(guildId) {
|
constructor(guildId) {
|
||||||
this.guildId = guildId;
|
super(guildId);
|
||||||
|
this.archives = getRepository(ArchiveEntry);
|
||||||
|
|
||||||
|
// Clean expired archives at start and then every hour
|
||||||
|
this.deleteExpiredArchives();
|
||||||
|
setInterval(() => this.deleteExpiredArchives(), 1000 * 60 * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateNewLogId() {
|
private deleteExpiredArchives() {
|
||||||
return uuid();
|
this.archives
|
||||||
|
.createQueryBuilder()
|
||||||
|
.where("expires_at <= NOW()")
|
||||||
|
.delete()
|
||||||
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async find(id: string): Promise<SpamLog> {
|
async find(id: string): Promise<ArchiveEntry> {
|
||||||
const result = await knex("archives")
|
return this.archives.findOne({
|
||||||
.where("id", id)
|
where: { id },
|
||||||
.first();
|
relations: this.getRelations()
|
||||||
|
});
|
||||||
return result ? new SpamLog(result) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(body: string, expiresAt: moment.Moment = null) {
|
/**
|
||||||
const id = this.generateNewLogId();
|
* @returns ID of the created entry
|
||||||
|
*/
|
||||||
|
async create(body: string, expiresAt: moment.Moment = null): Promise<string> {
|
||||||
if (!expiresAt) {
|
if (!expiresAt) {
|
||||||
expiresAt = moment().add(DEFAULT_EXPIRY_DAYS, "days");
|
expiresAt = moment().add(DEFAULT_EXPIRY_DAYS, "days");
|
||||||
}
|
}
|
||||||
|
|
||||||
await knex("archives").insert({
|
const result = await this.archives.insert({
|
||||||
id,
|
|
||||||
guild_id: this.guildId,
|
guild_id: this.guildId,
|
||||||
body,
|
body,
|
||||||
expires_at: expiresAt.format("YYYY-MM-DD HH:mm:ss")
|
expires_at: expiresAt.format("YYYY-MM-DD HH:mm:ss")
|
||||||
});
|
});
|
||||||
|
|
||||||
return id;
|
return result.identifiers[0].id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,93 +1,78 @@
|
||||||
import knex from "../knex";
|
import { Case } from "./entities/Case";
|
||||||
import Case from "../models/Case";
|
import { CaseNote } from "./entities/CaseNote";
|
||||||
import CaseNote from "../models/CaseNote";
|
import { BaseRepository } from "./BaseRepository";
|
||||||
|
import { getRepository, In, Repository } from "typeorm";
|
||||||
|
|
||||||
export class GuildCases {
|
export class GuildCases extends BaseRepository {
|
||||||
protected guildId: string;
|
private cases: Repository<Case>;
|
||||||
|
private caseNotes: Repository<CaseNote>;
|
||||||
|
|
||||||
constructor(guildId) {
|
constructor(guildId) {
|
||||||
this.guildId = guildId;
|
super(guildId);
|
||||||
|
this.cases = getRepository(Case);
|
||||||
|
this.caseNotes = getRepository(CaseNote);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(ids: number[]): Promise<Case[]> {
|
async get(ids: number[]): Promise<Case[]> {
|
||||||
const result = await knex("cases")
|
return this.cases.find({
|
||||||
.whereIn("id", ids)
|
relations: this.getRelations(),
|
||||||
.select();
|
where: {
|
||||||
|
guild_id: this.guildId,
|
||||||
return result.map(r => new Case(r));
|
id: In(ids)
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async find(id: number): Promise<Case> {
|
async find(id: number): Promise<Case> {
|
||||||
const result = await knex("cases")
|
return this.cases.findOne({
|
||||||
.where("guild_id", this.guildId)
|
relations: this.getRelations(),
|
||||||
.where("id", id)
|
where: {
|
||||||
.first();
|
guild_id: this.guildId,
|
||||||
|
id
|
||||||
return result ? new Case(result) : null;
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByCaseNumber(caseNumber: number): Promise<Case> {
|
async findByCaseNumber(caseNumber: number): Promise<Case> {
|
||||||
const result = await knex("cases")
|
return this.cases.findOne({
|
||||||
.where("guild_id", this.guildId)
|
relations: this.getRelations(),
|
||||||
.where("case_number", caseNumber)
|
where: {
|
||||||
.first();
|
guild_id: this.guildId,
|
||||||
|
case_number: caseNumber
|
||||||
return result ? new Case(result) : null;
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
async getCaseNotes(caseId: number): Promise<CaseNote[]> {
|
|
||||||
const results = await knex("case_notes")
|
|
||||||
.where("case_id", caseId)
|
|
||||||
.select();
|
|
||||||
|
|
||||||
return results.map(r => new CaseNote(r));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getByUserId(userId: string): Promise<Case[]> {
|
async getByUserId(userId: string): Promise<Case[]> {
|
||||||
const results = await knex("cases")
|
return this.cases.find({
|
||||||
.where("guild_id", this.guildId)
|
relations: this.getRelations(),
|
||||||
.where("user_id", userId)
|
where: {
|
||||||
.select();
|
guild_id: this.guildId,
|
||||||
|
user_id: userId
|
||||||
return results.map(r => new Case(r));
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
async findFirstCaseNote(caseId: number): Promise<CaseNote> {
|
|
||||||
const result = await knex("case_notes")
|
|
||||||
.where("case_id", caseId)
|
|
||||||
.first();
|
|
||||||
|
|
||||||
return result ? new CaseNote(result) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data): Promise<number> {
|
async create(data): Promise<number> {
|
||||||
return knex
|
const result = await this.cases.insert({
|
||||||
.insert({
|
...data,
|
||||||
...data,
|
guild_id: this.guildId,
|
||||||
guild_id: this.guildId,
|
case_number: () => `(SELECT IFNULL(MAX(case_number)+1, 1) FROM cases AS ma2 WHERE guild_id = ${this.guildId})`
|
||||||
case_number: knex.raw(
|
});
|
||||||
"(SELECT IFNULL(MAX(case_number)+1, 1) FROM cases AS ma2 WHERE guild_id = ?)",
|
|
||||||
this.guildId
|
return result.identifiers[0].id;
|
||||||
)
|
|
||||||
})
|
|
||||||
.returning("id")
|
|
||||||
.into("cases")
|
|
||||||
.then(ids => Number(ids[0]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(id, data) {
|
update(id, data) {
|
||||||
return knex("cases")
|
return this.cases.update(id, data);
|
||||||
.where("id", id)
|
|
||||||
.update(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createNote(caseId: number, data: any) {
|
async createNote(caseId: number, data: any): Promise<number> {
|
||||||
return knex
|
const result = await this.caseNotes.insert({
|
||||||
.insert({
|
...data,
|
||||||
...data,
|
case_id: caseId
|
||||||
case_id: caseId
|
});
|
||||||
})
|
|
||||||
.into("case_notes")
|
return result.identifiers[0].id;
|
||||||
.return();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,32 @@
|
||||||
import knex from "../knex";
|
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import Mute from "../models/Mute";
|
import { Mute } from "./entities/Mute";
|
||||||
|
import { BaseRepository } from "./BaseRepository";
|
||||||
|
import { getRepository, Repository, Brackets } from "typeorm";
|
||||||
|
|
||||||
export class GuildMutes {
|
export class GuildMutes extends BaseRepository {
|
||||||
protected guildId: string;
|
private mutes: Repository<Mute>;
|
||||||
|
|
||||||
constructor(guildId) {
|
constructor(guildId) {
|
||||||
this.guildId = guildId;
|
super(guildId);
|
||||||
|
this.mutes = getRepository(Mute);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getExpiredMutes(): Promise<Mute[]> {
|
async getExpiredMutes(): Promise<Mute[]> {
|
||||||
const result = await knex("mutes")
|
return this.mutes
|
||||||
.where("guild_id", this.guildId)
|
.createQueryBuilder("mutes")
|
||||||
.whereNotNull("expires_at")
|
.where("guild_id = :guild_id", { guild_id: this.guildId })
|
||||||
.whereRaw("expires_at <= NOW()")
|
.where("expires_at IS NOT NULL")
|
||||||
.select();
|
.where("expires_at <= NOW()")
|
||||||
|
.getMany();
|
||||||
return result.map(r => new Mute(r));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async findExistingMuteForUserId(userId: string): Promise<Mute> {
|
async findExistingMuteForUserId(userId: string): Promise<Mute> {
|
||||||
const result = await knex("mutes")
|
return this.mutes.findOne({
|
||||||
.where("guild_id", this.guildId)
|
where: {
|
||||||
.where("user_id", userId)
|
guild_id: this.guildId,
|
||||||
.first();
|
user_id: userId
|
||||||
|
}
|
||||||
return result ? new Mute(result) : null;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async addMute(userId, expiryTime) {
|
async addMute(userId, expiryTime) {
|
||||||
|
@ -35,13 +36,11 @@ export class GuildMutes {
|
||||||
.format("YYYY-MM-DD HH:mm:ss")
|
.format("YYYY-MM-DD HH:mm:ss")
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return knex
|
return this.mutes.insert({
|
||||||
.insert({
|
guild_id: this.guildId,
|
||||||
guild_id: this.guildId,
|
user_id: userId,
|
||||||
user_id: userId,
|
expires_at: expiresAt
|
||||||
expires_at: expiresAt
|
});
|
||||||
})
|
|
||||||
.into("mutes");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateExpiryTime(userId, newExpiryTime) {
|
async updateExpiryTime(userId, newExpiryTime) {
|
||||||
|
@ -51,12 +50,15 @@ export class GuildMutes {
|
||||||
.format("YYYY-MM-DD HH:mm:ss")
|
.format("YYYY-MM-DD HH:mm:ss")
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return knex("mutes")
|
return this.mutes.update(
|
||||||
.where("guild_id", this.guildId)
|
{
|
||||||
.where("user_id", userId)
|
guild_id: this.guildId,
|
||||||
.update({
|
user_id: userId
|
||||||
|
},
|
||||||
|
{
|
||||||
expires_at: expiresAt
|
expires_at: expiresAt
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addOrUpdateMute(userId, expiryTime) {
|
async addOrUpdateMute(userId, expiryTime) {
|
||||||
|
@ -70,25 +72,33 @@ export class GuildMutes {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getActiveMutes(): Promise<Mute[]> {
|
async getActiveMutes(): Promise<Mute[]> {
|
||||||
const result = await knex("mutes")
|
return this.mutes
|
||||||
.where("guild_id", this.guildId)
|
.createQueryBuilder("mutes")
|
||||||
.where(q => q.whereRaw("expires_at > NOW()").orWhereNull("expires_at"))
|
.where("guild_id = :guild_id", { guild_id: this.guildId })
|
||||||
.select();
|
.andWhere(
|
||||||
|
new Brackets(qb => {
|
||||||
return result.map(r => new Mute(r));
|
qb.where("expires_at > NOW()").orWhere("expires_at IS NULL");
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.getMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
async setCaseId(userId, caseId) {
|
async setCaseId(userId, caseId) {
|
||||||
await knex("mutes")
|
await this.mutes.update(
|
||||||
.where("guild_id", this.guildId)
|
{
|
||||||
.where("user_id", userId)
|
guild_id: this.guildId,
|
||||||
.update({ case_id: caseId });
|
user_id: userId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case_id: caseId
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async clear(userId) {
|
async clear(userId) {
|
||||||
return knex("mutes")
|
await this.mutes.delete({
|
||||||
.where("guild_id", this.guildId)
|
guild_id: this.guildId,
|
||||||
.where("user_id", userId)
|
user_id: userId
|
||||||
.delete();
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import knex from "../knex";
|
import { PersistedData } from "./entities/PersistedData";
|
||||||
import PersistedData from "../models/PersistedData";
|
import { BaseRepository } from "./BaseRepository";
|
||||||
|
import { getRepository, Repository } from "typeorm";
|
||||||
|
|
||||||
export interface IPartialPersistData {
|
export interface IPartialPersistData {
|
||||||
roles?: string[];
|
roles?: string[];
|
||||||
|
@ -7,20 +8,21 @@ export interface IPartialPersistData {
|
||||||
is_voice_muted?: boolean;
|
is_voice_muted?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GuildPersistedData {
|
export class GuildPersistedData extends BaseRepository {
|
||||||
protected guildId: string;
|
private persistedData: Repository<PersistedData>;
|
||||||
|
|
||||||
constructor(guildId) {
|
constructor(guildId) {
|
||||||
this.guildId = guildId;
|
super(guildId);
|
||||||
|
this.persistedData = getRepository(PersistedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
async find(userId: string) {
|
async find(userId: string) {
|
||||||
const result = await knex("persisted_data")
|
return this.persistedData.findOne({
|
||||||
.where("guild_id", this.guildId)
|
where: {
|
||||||
.where("user_id", userId)
|
guild_id: this.guildId,
|
||||||
.first();
|
user_id: userId
|
||||||
|
}
|
||||||
return result ? new PersistedData(result) : null;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async set(userId: string, data: IPartialPersistData = {}) {
|
async set(userId: string, data: IPartialPersistData = {}) {
|
||||||
|
@ -31,12 +33,15 @@ export class GuildPersistedData {
|
||||||
|
|
||||||
const existing = await this.find(userId);
|
const existing = await this.find(userId);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
await knex("persisted_data")
|
await this.persistedData.update(
|
||||||
.where("guild_id", this.guildId)
|
{
|
||||||
.where("user_id", userId)
|
guild_id: this.guildId,
|
||||||
.update(finalData);
|
user_id: userId
|
||||||
|
},
|
||||||
|
finalData
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await knex("persisted_data").insert({
|
await this.persistedData.insert({
|
||||||
...finalData,
|
...finalData,
|
||||||
guild_id: this.guildId,
|
guild_id: this.guildId,
|
||||||
user_id: userId
|
user_id: userId
|
||||||
|
@ -45,9 +50,9 @@ export class GuildPersistedData {
|
||||||
}
|
}
|
||||||
|
|
||||||
async clear(userId: string) {
|
async clear(userId: string) {
|
||||||
await knex("persisted_data")
|
await this.persistedData.delete({
|
||||||
.where("guild_id", this.guildId)
|
guild_id: this.guildId,
|
||||||
.where("user_id", userId)
|
user_id: userId
|
||||||
.delete();
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +1,57 @@
|
||||||
import knex from "../knex";
|
import { ReactionRole } from "./entities/ReactionRole";
|
||||||
import ReactionRole from "../models/ReactionRole";
|
import { BaseRepository } from "./BaseRepository";
|
||||||
|
import { getRepository, Repository } from "typeorm";
|
||||||
|
|
||||||
export class GuildReactionRoles {
|
export class GuildReactionRoles extends BaseRepository {
|
||||||
protected guildId: string;
|
private reactionRoles: Repository<ReactionRole>;
|
||||||
|
|
||||||
constructor(guildId) {
|
constructor(guildId) {
|
||||||
this.guildId = guildId;
|
super(guildId);
|
||||||
|
this.reactionRoles = getRepository(ReactionRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
async all(): Promise<ReactionRole[]> {
|
async all(): Promise<ReactionRole[]> {
|
||||||
const results = await knex("reaction_roles")
|
return this.reactionRoles.find({
|
||||||
.where("guild_id", this.guildId)
|
where: {
|
||||||
.select();
|
guild_id: this.guildId
|
||||||
|
}
|
||||||
return results.map(r => new ReactionRole(r));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getForMessage(messageId: string): Promise<ReactionRole[]> {
|
async getForMessage(messageId: string): Promise<ReactionRole[]> {
|
||||||
const results = await knex("reaction_roles")
|
return this.reactionRoles.find({
|
||||||
.where("guild_id", this.guildId)
|
where: {
|
||||||
.where("message_id", messageId)
|
guild_id: this.guildId,
|
||||||
.select();
|
message_id: messageId
|
||||||
|
}
|
||||||
return results.map(r => new ReactionRole(r));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getByMessageAndEmoji(messageId: string, emoji: string): Promise<ReactionRole> {
|
async getByMessageAndEmoji(messageId: string, emoji: string): Promise<ReactionRole> {
|
||||||
const result = await knex("reaction_roles")
|
return this.reactionRoles.findOne({
|
||||||
.where("guild_id", this.guildId)
|
where: {
|
||||||
.where("message_id", messageId)
|
guild_id: this.guildId,
|
||||||
.where("emoji", emoji)
|
message_id: messageId,
|
||||||
.first();
|
emoji
|
||||||
|
}
|
||||||
return result ? new ReactionRole(result) : null;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeFromMessage(messageId: string, emoji: string = null) {
|
async removeFromMessage(messageId: string, emoji: string = null) {
|
||||||
let query = knex("reaction_roles")
|
const criteria: any = {
|
||||||
.where("guild_id", this.guildId)
|
guild_id: this.guildId,
|
||||||
.where("message_id", messageId);
|
message_id: messageId
|
||||||
|
};
|
||||||
|
|
||||||
if (emoji) {
|
if (emoji) {
|
||||||
query = query.where("emoji", emoji);
|
criteria.emoji = emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
await query.delete();
|
await this.reactionRoles.delete(criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
async add(channelId: string, messageId: string, emoji: string, roleId: string) {
|
async add(channelId: string, messageId: string, emoji: string, roleId: string) {
|
||||||
await knex("reaction_roles").insert({
|
await this.reactionRoles.insert({
|
||||||
guild_id: this.guildId,
|
guild_id: this.guildId,
|
||||||
channel_id: channelId,
|
channel_id: channelId,
|
||||||
message_id: messageId,
|
message_id: messageId,
|
||||||
|
|
|
@ -1,36 +1,40 @@
|
||||||
import knex from "../knex";
|
import { Tag } from "./entities/Tag";
|
||||||
import moment from "moment-timezone";
|
import { getRepository, Repository } from "typeorm";
|
||||||
import Tag from "../models/Tag";
|
import { BaseRepository } from "./BaseRepository";
|
||||||
|
|
||||||
export class GuildTags {
|
export class GuildTags extends BaseRepository {
|
||||||
protected guildId: string;
|
private tags: Repository<Tag>;
|
||||||
|
|
||||||
constructor(guildId) {
|
constructor(guildId) {
|
||||||
this.guildId = guildId;
|
super(guildId);
|
||||||
|
this.tags = getRepository(Tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
async find(tag): Promise<Tag> {
|
async find(tag): Promise<Tag> {
|
||||||
const result = await knex("tags")
|
return this.tags.findOne({
|
||||||
.where("guild_id", this.guildId)
|
where: {
|
||||||
.where("tag", tag)
|
guild_id: this.guildId,
|
||||||
.first();
|
tag
|
||||||
|
}
|
||||||
return result ? new Tag(result) : null;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async createOrUpdate(tag, body, userId) {
|
async createOrUpdate(tag, body, userId) {
|
||||||
const existingTag = await this.find(tag);
|
const existingTag = await this.find(tag);
|
||||||
if (existingTag) {
|
if (existingTag) {
|
||||||
await knex("tags")
|
await this.tags
|
||||||
.where("guild_id", this.guildId)
|
.createQueryBuilder()
|
||||||
.where("tag", tag)
|
.update()
|
||||||
.update({
|
.set({
|
||||||
body,
|
body,
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
created_at: knex.raw("NOW()")
|
created_at: () => "NOW()"
|
||||||
});
|
})
|
||||||
|
.where("guild_id = :guildId", { guildId: this.guildId })
|
||||||
|
.where("tag = :tag", { tag })
|
||||||
|
.execute();
|
||||||
} else {
|
} else {
|
||||||
await knex("tags").insert({
|
await this.tags.insert({
|
||||||
guild_id: this.guildId,
|
guild_id: this.guildId,
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
tag,
|
tag,
|
||||||
|
@ -40,9 +44,9 @@ export class GuildTags {
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(tag) {
|
async delete(tag) {
|
||||||
await knex("tags")
|
await this.tags.delete({
|
||||||
.where("guild_id", this.guildId)
|
guild_id: this.guildId,
|
||||||
.where("tag", tag)
|
tag
|
||||||
.delete();
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
src/data/db.ts
Normal file
23
src/data/db.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { SimpleError } from "../SimpleError";
|
||||||
|
import { Connection, createConnection } from "typeorm";
|
||||||
|
|
||||||
|
let connectionPromise: Promise<Connection>;
|
||||||
|
|
||||||
|
export let connection: Connection;
|
||||||
|
|
||||||
|
export function connect() {
|
||||||
|
if (!connectionPromise) {
|
||||||
|
connectionPromise = createConnection().then(newConnection => {
|
||||||
|
return newConnection.query("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP) AS tz").then(r => {
|
||||||
|
if (r[0].tz !== "00:00:00") {
|
||||||
|
throw new SimpleError(`Database timezone must be UTC (detected ${r[0].tz})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection = newConnection;
|
||||||
|
return newConnection;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return connectionPromise;
|
||||||
|
}
|
16
src/data/entities/ArchiveEntry.ts
Normal file
16
src/data/entities/ArchiveEntry.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { Entity, Column, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity("archives")
|
||||||
|
export class ArchiveEntry {
|
||||||
|
@Column()
|
||||||
|
@PrimaryGeneratedColumn("uuid")
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column() guild_id: string;
|
||||||
|
|
||||||
|
@Column() body: string;
|
||||||
|
|
||||||
|
@Column() created_at: string;
|
||||||
|
|
||||||
|
@Column() expires_at: string;
|
||||||
|
}
|
28
src/data/entities/Case.ts
Normal file
28
src/data/entities/Case.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
|
||||||
|
import { CaseNote } from "./CaseNote";
|
||||||
|
|
||||||
|
@Entity("cases")
|
||||||
|
export class Case {
|
||||||
|
@PrimaryGeneratedColumn() id: number;
|
||||||
|
|
||||||
|
@Column() guild_id: string;
|
||||||
|
|
||||||
|
@Column() case_number: number;
|
||||||
|
|
||||||
|
@Column() user_id: string;
|
||||||
|
|
||||||
|
@Column() user_name: string;
|
||||||
|
|
||||||
|
@Column() mod_id: string;
|
||||||
|
|
||||||
|
@Column() mod_name: string;
|
||||||
|
|
||||||
|
@Column() type: number;
|
||||||
|
|
||||||
|
@Column() audit_log_id: string;
|
||||||
|
|
||||||
|
@Column() created_at: string;
|
||||||
|
|
||||||
|
@OneToMany(type => CaseNote, note => note.case)
|
||||||
|
notes: CaseNote[];
|
||||||
|
}
|
21
src/data/entities/CaseNote.ts
Normal file
21
src/data/entities/CaseNote.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from "typeorm";
|
||||||
|
import { Case } from "./Case";
|
||||||
|
|
||||||
|
@Entity("case_notes")
|
||||||
|
export class CaseNote {
|
||||||
|
@PrimaryGeneratedColumn() id: number;
|
||||||
|
|
||||||
|
@Column() case_id: number;
|
||||||
|
|
||||||
|
@Column() mod_id: string;
|
||||||
|
|
||||||
|
@Column() mod_name: string;
|
||||||
|
|
||||||
|
@Column() body: string;
|
||||||
|
|
||||||
|
@Column() created_at: string;
|
||||||
|
|
||||||
|
@ManyToOne(type => Case, theCase => theCase.notes)
|
||||||
|
@JoinColumn({ name: "case_id" })
|
||||||
|
case: Case;
|
||||||
|
}
|
18
src/data/entities/Mute.ts
Normal file
18
src/data/entities/Mute.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { Entity, Column, PrimaryColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity("mutes")
|
||||||
|
export class Mute {
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
guild_id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
user_id: string;
|
||||||
|
|
||||||
|
@Column() created_at: string;
|
||||||
|
|
||||||
|
@Column() expires_at: string;
|
||||||
|
|
||||||
|
@Column() case_id: number;
|
||||||
|
}
|
18
src/data/entities/PersistedData.ts
Normal file
18
src/data/entities/PersistedData.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { Entity, Column, PrimaryColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity("persisted_data")
|
||||||
|
export class PersistedData {
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
guild_id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
user_id: string;
|
||||||
|
|
||||||
|
@Column() roles: string;
|
||||||
|
|
||||||
|
@Column() nickname: string;
|
||||||
|
|
||||||
|
@Column() is_voice_muted: number;
|
||||||
|
}
|
22
src/data/entities/ReactionRole.ts
Normal file
22
src/data/entities/ReactionRole.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Entity, Column, PrimaryColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity("reaction_roles")
|
||||||
|
export class ReactionRole {
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
guild_id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
channel_id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
message_id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
emoji: string;
|
||||||
|
|
||||||
|
@Column() role_id: string;
|
||||||
|
}
|
18
src/data/entities/Tag.ts
Normal file
18
src/data/entities/Tag.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { Entity, Column, PrimaryColumn, CreateDateColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity("tags")
|
||||||
|
export class Tag {
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
guild_id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
tag: string;
|
||||||
|
|
||||||
|
@Column() user_id: string;
|
||||||
|
|
||||||
|
@Column() body: string;
|
||||||
|
|
||||||
|
@Column() created_at: string;
|
||||||
|
}
|
32
src/index.ts
32
src/index.ts
|
@ -1,3 +1,9 @@
|
||||||
|
import path from "path";
|
||||||
|
import yaml from "js-yaml";
|
||||||
|
|
||||||
|
import _fs from "fs";
|
||||||
|
const fs = _fs.promises;
|
||||||
|
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
|
|
||||||
process.on("unhandledRejection", (reason, p) => {
|
process.on("unhandledRejection", (reason, p) => {
|
||||||
|
@ -17,13 +23,12 @@ process.on("uncaughtException", err => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Always use UTC
|
// Always use UTC
|
||||||
// This is also set for the database in knexfile
|
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
moment.tz.setDefault("UTC");
|
moment.tz.setDefault("UTC");
|
||||||
|
|
||||||
import { Client } from "eris";
|
import { Client } from "eris";
|
||||||
import { Knub, logger } from "knub";
|
import { Knub, logger } from "knub";
|
||||||
import knex from "./knex";
|
import { connect } from "./data/db";
|
||||||
|
|
||||||
// Global plugins
|
// Global plugins
|
||||||
import { BotControlPlugin } from "./plugins/BotControl";
|
import { BotControlPlugin } from "./plugins/BotControl";
|
||||||
|
@ -42,7 +47,9 @@ import { TagsPlugin } from "./plugins/Tags";
|
||||||
|
|
||||||
// Run latest database migrations
|
// Run latest database migrations
|
||||||
logger.info("Running database migrations");
|
logger.info("Running database migrations");
|
||||||
knex.migrate.latest().then(() => {
|
connect().then(async conn => {
|
||||||
|
await conn.runMigrations();
|
||||||
|
|
||||||
const client = new Client(process.env.TOKEN, {
|
const client = new Client(process.env.TOKEN, {
|
||||||
getAllUsers: true
|
getAllUsers: true
|
||||||
});
|
});
|
||||||
|
@ -72,6 +79,25 @@ knex.migrate.latest().then(() => {
|
||||||
return keys.filter(pluginName => {
|
return keys.filter(pluginName => {
|
||||||
return plugins[pluginName] && plugins[pluginName].enabled !== false;
|
return plugins[pluginName] && plugins[pluginName].enabled !== false;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async getConfig(id) {
|
||||||
|
const configFile = id ? `${id}.yml` : "global.yml";
|
||||||
|
const configPath = path.join("config", configFile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.access(configPath);
|
||||||
|
} catch (e) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const yamlString = await fs.readFile(configPath, { encoding: "utf8" });
|
||||||
|
return yaml.safeLoad(yamlString);
|
||||||
|
},
|
||||||
|
|
||||||
|
logFn: (level, msg) => {
|
||||||
|
if (level === "debug") return;
|
||||||
|
console.log(`[${level.toUpperCase()}] ${msg}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
const knexfile = require("../knexfile");
|
|
||||||
import knex from "knex";
|
|
||||||
|
|
||||||
const db = knex(knexfile);
|
|
||||||
|
|
||||||
export default db;
|
|
|
@ -1,14 +0,0 @@
|
||||||
import Model from "./Model";
|
|
||||||
|
|
||||||
export default class Case extends Model {
|
|
||||||
public id: number;
|
|
||||||
public guild_id: string;
|
|
||||||
public case_number: number;
|
|
||||||
public user_id: string;
|
|
||||||
public user_name: string;
|
|
||||||
public mod_id: string;
|
|
||||||
public mod_name: string;
|
|
||||||
public type: number;
|
|
||||||
public audit_log_id: string;
|
|
||||||
public created_at: string;
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
import Model from "./Model";
|
|
||||||
|
|
||||||
export default class CaseNote extends Model {
|
|
||||||
public id: number;
|
|
||||||
public case_id: number;
|
|
||||||
public mod_id: string;
|
|
||||||
public mod_name: string;
|
|
||||||
public body: string;
|
|
||||||
public created_at: string;
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
export default class Model {
|
|
||||||
constructor(props) {
|
|
||||||
for (const key in props) {
|
|
||||||
this[key] = props[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import Model from "./Model";
|
|
||||||
|
|
||||||
export default class Mute extends Model {
|
|
||||||
public guild_id: string;
|
|
||||||
public user_id: string;
|
|
||||||
public case_id: number;
|
|
||||||
public created_at: string;
|
|
||||||
public expires_at: string;
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
import Model from "./Model";
|
|
||||||
|
|
||||||
export default class PersistedData extends Model {
|
|
||||||
private _roles;
|
|
||||||
private _isVoiceMuted;
|
|
||||||
|
|
||||||
public guild_id: string;
|
|
||||||
public user_id: string;
|
|
||||||
public nickname: string;
|
|
||||||
|
|
||||||
set roles(v) {
|
|
||||||
this._roles = v ? v.split(",") : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
get roles() {
|
|
||||||
return this._roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
set is_voice_muted(v) {
|
|
||||||
this._isVoiceMuted = v === 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
get is_voice_muted() {
|
|
||||||
return this._isVoiceMuted;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import Model from "./Model";
|
|
||||||
|
|
||||||
export default class ReactionRole extends Model {
|
|
||||||
public guild_id: string;
|
|
||||||
public channel_id: string;
|
|
||||||
public message_id: string;
|
|
||||||
public emoji: string;
|
|
||||||
public role_id: string;
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import Model from "./Model";
|
|
||||||
|
|
||||||
export default class SpamLog extends Model {
|
|
||||||
public id: string;
|
|
||||||
public guild_id: string;
|
|
||||||
public body: string;
|
|
||||||
public created_at: string;
|
|
||||||
public expires_at: string;
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import Model from "./Model";
|
|
||||||
|
|
||||||
export default class Tag extends Model {
|
|
||||||
public guild_id: string;
|
|
||||||
public tag: string;
|
|
||||||
public user_id: string;
|
|
||||||
public body: string;
|
|
||||||
public created_at: string;
|
|
||||||
}
|
|
|
@ -11,14 +11,13 @@ import {
|
||||||
errorMessage,
|
errorMessage,
|
||||||
findRelevantAuditLogEntry,
|
findRelevantAuditLogEntry,
|
||||||
formatTemplateString,
|
formatTemplateString,
|
||||||
sleep,
|
|
||||||
stripObjectToScalars,
|
stripObjectToScalars,
|
||||||
successMessage,
|
successMessage,
|
||||||
trimLines
|
trimLines
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
import { GuildMutes } from "../data/GuildMutes";
|
import { GuildMutes } from "../data/GuildMutes";
|
||||||
import Case from "../models/Case";
|
import { Case } from "../data/entities/Case";
|
||||||
import { CaseType } from "../data/CaseType";
|
import { CaseTypes } from "../data/CaseTypes";
|
||||||
import { GuildLogs } from "../data/GuildLogs";
|
import { GuildLogs } from "../data/GuildLogs";
|
||||||
import { LogType } from "../data/LogType";
|
import { LogType } from "../data/LogType";
|
||||||
import Timer = NodeJS.Timer;
|
import Timer = NodeJS.Timer;
|
||||||
|
@ -47,8 +46,8 @@ export class ModActionsPlugin extends Plugin {
|
||||||
protected ignoredEvents: IIgnoredEvent[];
|
protected ignoredEvents: IIgnoredEvent[];
|
||||||
|
|
||||||
async onLoad() {
|
async onLoad() {
|
||||||
this.cases = new GuildCases(this.guildId);
|
this.cases = GuildCases.getInstance(this.guildId);
|
||||||
this.mutes = new GuildMutes(this.guildId);
|
this.mutes = GuildMutes.getInstance(this.guildId);
|
||||||
this.serverLogs = new GuildLogs(this.guildId);
|
this.serverLogs = new GuildLogs(this.guildId);
|
||||||
|
|
||||||
this.ignoredEvents = [];
|
this.ignoredEvents = [];
|
||||||
|
@ -156,9 +155,9 @@ export class ModActionsPlugin extends Plugin {
|
||||||
const modId = relevantAuditLogEntry.user.id;
|
const modId = relevantAuditLogEntry.user.id;
|
||||||
const auditLogId = relevantAuditLogEntry.id;
|
const auditLogId = relevantAuditLogEntry.id;
|
||||||
|
|
||||||
await this.createCase(user.id, modId, CaseType.Ban, auditLogId, relevantAuditLogEntry.reason, true);
|
await this.createCase(user.id, modId, CaseTypes.Ban, auditLogId, relevantAuditLogEntry.reason, true);
|
||||||
} else {
|
} else {
|
||||||
await this.createCase(user.id, null, CaseType.Ban);
|
await this.createCase(user.id, null, CaseTypes.Ban);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,9 +182,9 @@ export class ModActionsPlugin extends Plugin {
|
||||||
const modId = relevantAuditLogEntry.user.id;
|
const modId = relevantAuditLogEntry.user.id;
|
||||||
const auditLogId = relevantAuditLogEntry.id;
|
const auditLogId = relevantAuditLogEntry.id;
|
||||||
|
|
||||||
await this.createCase(user.id, modId, CaseType.Unban, auditLogId, null, true);
|
await this.createCase(user.id, modId, CaseTypes.Unban, auditLogId, null, true);
|
||||||
} else {
|
} else {
|
||||||
await this.createCase(user.id, null, CaseType.Unban);
|
await this.createCase(user.id, null, CaseTypes.Unban);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +227,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
this.createCase(
|
this.createCase(
|
||||||
member.id,
|
member.id,
|
||||||
kickAuditLogEntry.user.id,
|
kickAuditLogEntry.user.id,
|
||||||
CaseType.Kick,
|
CaseTypes.Kick,
|
||||||
kickAuditLogEntry.id,
|
kickAuditLogEntry.id,
|
||||||
kickAuditLogEntry.reason,
|
kickAuditLogEntry.reason,
|
||||||
true
|
true
|
||||||
|
@ -274,12 +273,13 @@ export class ModActionsPlugin extends Plugin {
|
||||||
const user = await this.bot.users.get(args.userId);
|
const user = await this.bot.users.get(args.userId);
|
||||||
const userName = user ? `${user.username}#${user.discriminator}` : "member";
|
const userName = user ? `${user.username}#${user.discriminator}` : "member";
|
||||||
|
|
||||||
await this.createCase(args.userId, msg.author.id, CaseType.Note, null, args.note);
|
await this.createCase(args.userId, msg.author.id, CaseTypes.Note, null, args.note);
|
||||||
msg.channel.createMessage(successMessage(`Note added on ${userName}`));
|
msg.channel.createMessage(successMessage(`Note added on ${userName}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
@d.command("warn", "<member:Member> <reason:string$>")
|
@d.command("warn", "<member:Member> <reason:string$>")
|
||||||
@d.permission("warn")
|
@d.permission("warn")
|
||||||
|
@d.nonBlocking()
|
||||||
async warnCmd(msg: Message, args: any) {
|
async warnCmd(msg: Message, args: any) {
|
||||||
// Make sure we're allowed to warn this member
|
// Make sure we're allowed to warn this member
|
||||||
if (!this.canActOn(msg.member, args.member)) {
|
if (!this.canActOn(msg.member, args.member)) {
|
||||||
|
@ -307,7 +307,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.createCase(args.member.id, msg.author.id, CaseType.Warn, null, args.reason);
|
await this.createCase(args.member.id, msg.author.id, CaseTypes.Warn, null, args.reason);
|
||||||
|
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(
|
||||||
successMessage(`Warned **${args.member.user.username}#${args.member.user.discriminator}**`)
|
successMessage(`Warned **${args.member.user.username}#${args.member.user.discriminator}**`)
|
||||||
|
@ -363,7 +363,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create a case
|
// Create a case
|
||||||
const caseId = await this.createCase(args.member.id, msg.author.id, CaseType.Mute, null, args.reason);
|
const caseId = await this.createCase(args.member.id, msg.author.id, CaseTypes.Mute, null, args.reason);
|
||||||
await this.mutes.setCaseId(args.member.id, caseId);
|
await this.mutes.setCaseId(args.member.id, caseId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +458,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a case
|
// Create a case
|
||||||
await this.createCase(args.member.id, msg.author.id, CaseType.Unmute, null, args.reason);
|
await this.createCase(args.member.id, msg.author.id, CaseTypes.Unmute, null, args.reason);
|
||||||
|
|
||||||
// Log the action
|
// Log the action
|
||||||
this.serverLogs.log(LogType.MEMBER_UNMUTE, {
|
this.serverLogs.log(LogType.MEMBER_UNMUTE, {
|
||||||
|
@ -568,7 +568,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
args.member.kick(args.reason);
|
args.member.kick(args.reason);
|
||||||
|
|
||||||
// Create a case for this action
|
// Create a case for this action
|
||||||
await this.createCase(args.member.id, msg.author.id, CaseType.Kick, null, args.reason);
|
await this.createCase(args.member.id, msg.author.id, CaseTypes.Kick, null, args.reason);
|
||||||
|
|
||||||
// Confirm the action to the moderator
|
// Confirm the action to the moderator
|
||||||
let response = `Kicked **${args.member.user.username}#${args.member.user.discriminator}**`;
|
let response = `Kicked **${args.member.user.username}#${args.member.user.discriminator}**`;
|
||||||
|
@ -613,7 +613,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
args.member.ban(1, args.reason);
|
args.member.ban(1, args.reason);
|
||||||
|
|
||||||
// Create a case for this action
|
// Create a case for this action
|
||||||
await this.createCase(args.member.id, msg.author.id, CaseType.Ban, null, args.reason);
|
await this.createCase(args.member.id, msg.author.id, CaseTypes.Ban, null, args.reason);
|
||||||
|
|
||||||
// Confirm the action to the moderator
|
// Confirm the action to the moderator
|
||||||
let response = `Banned **${args.member.user.username}#${args.member.user.discriminator}**`;
|
let response = `Banned **${args.member.user.username}#${args.member.user.discriminator}**`;
|
||||||
|
@ -646,7 +646,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
await this.guild.unbanMember(args.member.id);
|
await this.guild.unbanMember(args.member.id);
|
||||||
|
|
||||||
// Create a case for this action
|
// Create a case for this action
|
||||||
await this.createCase(args.member.id, msg.author.id, CaseType.Softban, null, args.reason);
|
await this.createCase(args.member.id, msg.author.id, CaseTypes.Softban, null, args.reason);
|
||||||
|
|
||||||
// Confirm the action to the moderator
|
// Confirm the action to the moderator
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(
|
||||||
|
@ -677,7 +677,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
msg.channel.createMessage(successMessage("Member unbanned!"));
|
msg.channel.createMessage(successMessage("Member unbanned!"));
|
||||||
|
|
||||||
// Create a case
|
// Create a case
|
||||||
this.createCase(args.userId, msg.author.id, CaseType.Unban, null, args.reason);
|
this.createCase(args.userId, msg.author.id, CaseTypes.Unban, null, args.reason);
|
||||||
|
|
||||||
// Log the action
|
// Log the action
|
||||||
this.serverLogs.log(LogType.MEMBER_UNBAN, {
|
this.serverLogs.log(LogType.MEMBER_UNBAN, {
|
||||||
|
@ -710,7 +710,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
msg.channel.createMessage(successMessage("Member forcebanned!"));
|
msg.channel.createMessage(successMessage("Member forcebanned!"));
|
||||||
|
|
||||||
// Create a case
|
// Create a case
|
||||||
this.createCase(args.userId, msg.author.id, CaseType.Ban, null, args.reason);
|
this.createCase(args.userId, msg.author.id, CaseTypes.Ban, null, args.reason);
|
||||||
|
|
||||||
// Log the action
|
// Log the action
|
||||||
this.serverLogs.log(LogType.MEMBER_FORCEBAN, {
|
this.serverLogs.log(LogType.MEMBER_FORCEBAN, {
|
||||||
|
@ -721,6 +721,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
|
|
||||||
@d.command("massban", "<userIds:string...>")
|
@d.command("massban", "<userIds:string...>")
|
||||||
@d.permission("massban")
|
@d.permission("massban")
|
||||||
|
@d.nonBlocking()
|
||||||
async massbanCmd(msg: Message, args: { userIds: string[] }) {
|
async massbanCmd(msg: Message, args: { userIds: string[] }) {
|
||||||
// Limit to 100 users at once (arbitrary?)
|
// Limit to 100 users at once (arbitrary?)
|
||||||
if (args.userIds.length > 100) {
|
if (args.userIds.length > 100) {
|
||||||
|
@ -763,7 +764,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
for (const userId of args.userIds) {
|
for (const userId of args.userIds) {
|
||||||
try {
|
try {
|
||||||
await this.guild.banMember(userId);
|
await this.guild.banMember(userId);
|
||||||
await this.createCase(userId, msg.author.id, CaseType.Ban, null, `Mass ban: ${banReason}`, false, false);
|
await this.createCase(userId, msg.author.id, CaseTypes.Ban, null, `Mass ban: ${banReason}`, false, false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
failedBans.push(userId);
|
failedBans.push(userId);
|
||||||
}
|
}
|
||||||
|
@ -811,13 +812,13 @@ export class ModActionsPlugin extends Plugin {
|
||||||
|
|
||||||
// Verify the case type is valid
|
// Verify the case type is valid
|
||||||
const type: string = args.type[0].toUpperCase() + args.type.slice(1).toLowerCase();
|
const type: string = args.type[0].toUpperCase() + args.type.slice(1).toLowerCase();
|
||||||
if (!CaseType[type]) {
|
if (!CaseTypes[type]) {
|
||||||
msg.channel.createMessage(errorMessage("Cannot add case: invalid case type"));
|
msg.channel.createMessage(errorMessage("Cannot add case: invalid case type"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the case
|
// Create the case
|
||||||
const caseId = await this.createCase(args.target, msg.author.id, CaseType[type], null, args.reason);
|
const caseId = await this.createCase(args.target, msg.author.id, CaseTypes[type], null, args.reason);
|
||||||
const theCase = await this.cases.find(caseId);
|
const theCase = await this.cases.find(caseId);
|
||||||
|
|
||||||
// Log the action
|
// Log the action
|
||||||
|
@ -852,7 +853,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
@d.command(/cases|usercases/, "<userId:userId> [expanded:string]")
|
@d.command(/cases|usercases/, "<userId:userId> [expanded:string]")
|
||||||
@d.permission("view")
|
@d.permission("view")
|
||||||
async usercasesCmd(msg: Message, args: { userId: string; expanded?: string }) {
|
async usercasesCmd(msg: Message, args: { userId: string; expanded?: string }) {
|
||||||
const cases = await this.cases.getByUserId(args.userId);
|
const cases = await this.cases.with("notes").getByUserId(args.userId);
|
||||||
const user = this.bot.users.get(args.userId);
|
const user = this.bot.users.get(args.userId);
|
||||||
const userName = user ? `${user.username}#${user.discriminator}` : "Unknown#0000";
|
const userName = user ? `${user.username}#${user.discriminator}` : "Unknown#0000";
|
||||||
const prefix = this.knub.getGuildData(this.guildId).config.prefix;
|
const prefix = this.knub.getGuildData(this.guildId).config.prefix;
|
||||||
|
@ -869,7 +870,8 @@ export class ModActionsPlugin extends Plugin {
|
||||||
// Compact view (= regular message with a preview of each case)
|
// Compact view (= regular message with a preview of each case)
|
||||||
const lines = [];
|
const lines = [];
|
||||||
for (const theCase of cases) {
|
for (const theCase of cases) {
|
||||||
const firstNote = await this.cases.findFirstCaseNote(theCase.id);
|
theCase.notes.sort((a, b) => (a.created_at > b.created_at ? 1 : -1));
|
||||||
|
const firstNote = theCase.notes[0];
|
||||||
let reason = firstNote ? firstNote.body : "";
|
let reason = firstNote ? firstNote.body : "";
|
||||||
|
|
||||||
if (reason.length > CASE_LIST_REASON_MAX_LENGTH) {
|
if (reason.length > CASE_LIST_REASON_MAX_LENGTH) {
|
||||||
|
@ -882,7 +884,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
|
|
||||||
reason = disableLinkPreviews(reason);
|
reason = disableLinkPreviews(reason);
|
||||||
|
|
||||||
lines.push(`Case \`#${theCase.case_number}\` __${CaseType[theCase.type]}__ ${reason}`);
|
lines.push(`Case \`#${theCase.case_number}\` __${CaseTypes[theCase.type]}__ ${reason}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalMessage = trimLines(`
|
const finalMessage = trimLines(`
|
||||||
|
@ -945,7 +947,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
protected async displayCase(caseOrCaseId: Case | number, channelId: string) {
|
protected async displayCase(caseOrCaseId: Case | number, channelId: string) {
|
||||||
let theCase: Case;
|
let theCase: Case;
|
||||||
if (typeof caseOrCaseId === "number") {
|
if (typeof caseOrCaseId === "number") {
|
||||||
theCase = await this.cases.find(caseOrCaseId);
|
theCase = await this.cases.with("notes").find(caseOrCaseId);
|
||||||
} else {
|
} else {
|
||||||
theCase = caseOrCaseId;
|
theCase = caseOrCaseId;
|
||||||
}
|
}
|
||||||
|
@ -953,10 +955,8 @@ export class ModActionsPlugin extends Plugin {
|
||||||
if (!theCase) return;
|
if (!theCase) return;
|
||||||
if (!this.guild.channels.get(channelId)) return;
|
if (!this.guild.channels.get(channelId)) return;
|
||||||
|
|
||||||
const notes = await this.cases.getCaseNotes(theCase.id);
|
|
||||||
|
|
||||||
const createdAt = moment(theCase.created_at);
|
const createdAt = moment(theCase.created_at);
|
||||||
const actionTypeStr = CaseType[theCase.type].toUpperCase();
|
const actionTypeStr = CaseTypes[theCase.type].toUpperCase();
|
||||||
|
|
||||||
const embed: any = {
|
const embed: any = {
|
||||||
title: `${actionTypeStr} - Case #${theCase.case_number}`,
|
title: `${actionTypeStr} - Case #${theCase.case_number}`,
|
||||||
|
@ -981,8 +981,8 @@ export class ModActionsPlugin extends Plugin {
|
||||||
embed.color = CaseTypeColors[theCase.type];
|
embed.color = CaseTypeColors[theCase.type];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notes.length) {
|
if (theCase.notes.length) {
|
||||||
notes.forEach((note: any) => {
|
theCase.notes.forEach((note: any) => {
|
||||||
const noteDate = moment(note.created_at);
|
const noteDate = moment(note.created_at);
|
||||||
embed.fields.push({
|
embed.fields.push({
|
||||||
name: `${note.mod_name} at ${noteDate.format("YYYY-MM-DD [at] HH:mm")}:`,
|
name: `${note.mod_name} at ${noteDate.format("YYYY-MM-DD [at] HH:mm")}:`,
|
||||||
|
@ -1014,7 +1014,7 @@ export class ModActionsPlugin extends Plugin {
|
||||||
public async createCase(
|
public async createCase(
|
||||||
userId: string,
|
userId: string,
|
||||||
modId: string,
|
modId: string,
|
||||||
caseType: CaseType,
|
caseType: CaseTypes,
|
||||||
auditLogId: string = null,
|
auditLogId: string = null,
|
||||||
reason: string = null,
|
reason: string = null,
|
||||||
automatic = false,
|
automatic = false,
|
||||||
|
|
|
@ -21,7 +21,7 @@ export class PersistPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.persistedData = new GuildPersistedData(this.guildId);
|
this.persistedData = GuildPersistedData.getInstance(this.guildId);
|
||||||
this.logs = new GuildLogs(this.guildId);
|
this.logs = new GuildLogs(this.guildId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,20 +30,17 @@ export class ReactionRolesPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
async onLoad() {
|
async onLoad() {
|
||||||
this.reactionRoles = new GuildReactionRoles(this.guildId);
|
this.reactionRoles = GuildReactionRoles.getInstance(this.guildId);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Pre-fetch all messages with reaction roles so we get their events
|
// Pre-fetch all messages with reaction roles so we get their events
|
||||||
const reactionRoles = await this.reactionRoles.all();
|
const reactionRoles = await this.reactionRoles.all();
|
||||||
|
|
||||||
const channelMessages: Map<string, Set<string>> = reactionRoles.reduce(
|
const channelMessages: Map<string, Set<string>> = reactionRoles.reduce((map: Map<string, Set<string>>, row) => {
|
||||||
(map: Map<string, Set<string>>, row) => {
|
if (!map.has(row.channel_id)) map.set(row.channel_id, new Set());
|
||||||
if (!map.has(row.channel_id)) map.set(row.channel_id, new Set());
|
map.get(row.channel_id).add(row.message_id);
|
||||||
map.get(row.channel_id).add(row.message_id);
|
return map;
|
||||||
return map;
|
}, new Map());
|
||||||
},
|
|
||||||
new Map()
|
|
||||||
);
|
|
||||||
|
|
||||||
const msgLoadPromises = [];
|
const msgLoadPromises = [];
|
||||||
|
|
||||||
|
@ -62,10 +59,7 @@ export class ReactionRolesPlugin extends Plugin {
|
||||||
|
|
||||||
@d.command("reaction_roles", "<channel:channel> <messageId:string> <reactionRolePairs:string$>")
|
@d.command("reaction_roles", "<channel:channel> <messageId:string> <reactionRolePairs:string$>")
|
||||||
@d.permission("manage")
|
@d.permission("manage")
|
||||||
async reactionRolesCmd(
|
async reactionRolesCmd(msg: Message, args: { channel: Channel; messageId: string; reactionRolePairs: string }) {
|
||||||
msg: Message,
|
|
||||||
args: { channel: Channel; messageId: string; reactionRolePairs: string }
|
|
||||||
) {
|
|
||||||
if (!(args.channel instanceof TextChannel)) {
|
if (!(args.channel instanceof TextChannel)) {
|
||||||
msg.channel.createMessage(errorMessage("Channel must be a text channel!"));
|
msg.channel.createMessage(errorMessage("Channel must be a text channel!"));
|
||||||
return;
|
return;
|
||||||
|
@ -100,9 +94,7 @@ export class ReactionRolesPlugin extends Plugin {
|
||||||
// Verify the specified emojis and roles are valid
|
// Verify the specified emojis and roles are valid
|
||||||
for (const pair of newRolePairs) {
|
for (const pair of newRolePairs) {
|
||||||
if (isSnowflake(pair[0]) && !guildEmojiIds.includes(pair[0])) {
|
if (isSnowflake(pair[0]) && !guildEmojiIds.includes(pair[0])) {
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(errorMessage("I can only use regular emojis and custom emojis from this server"));
|
||||||
errorMessage("I can only use regular emojis and custom emojis from this server")
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,9 +105,7 @@ export class ReactionRolesPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldReactionRoles = await this.reactionRoles.getForMessage(targetMessage.id);
|
const oldReactionRoles = await this.reactionRoles.getForMessage(targetMessage.id);
|
||||||
const oldRolePairs: ReactionRolePair[] = oldReactionRoles.map(
|
const oldRolePairs: ReactionRolePair[] = oldReactionRoles.map(r => [r.emoji, r.role_id] as ReactionRolePair);
|
||||||
r => [r.emoji, r.role_id] as ReactionRolePair
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remove old reaction/role pairs that weren't included in the new pairs or were changed in some way
|
// Remove old reaction/role pairs that weren't included in the new pairs or were changed in some way
|
||||||
const toRemove = oldRolePairs.filter(
|
const toRemove = oldRolePairs.filter(
|
||||||
|
@ -154,10 +144,7 @@ export class ReactionRolesPlugin extends Plugin {
|
||||||
|
|
||||||
@d.event("messageReactionAdd")
|
@d.event("messageReactionAdd")
|
||||||
async onAddReaction(msg: Message, emoji: CustomEmoji, userId: string) {
|
async onAddReaction(msg: Message, emoji: CustomEmoji, userId: string) {
|
||||||
const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji(
|
const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji(msg.id, emoji.id || emoji.name);
|
||||||
msg.id,
|
|
||||||
emoji.id || emoji.name
|
|
||||||
);
|
|
||||||
if (!matchingReactionRole) return;
|
if (!matchingReactionRole) return;
|
||||||
|
|
||||||
const member = this.guild.members.get(userId);
|
const member = this.guild.members.get(userId);
|
||||||
|
@ -168,10 +155,7 @@ export class ReactionRolesPlugin extends Plugin {
|
||||||
|
|
||||||
@d.event("messageReactionRemove")
|
@d.event("messageReactionRemove")
|
||||||
async onRemoveReaction(msg: Message, emoji: CustomEmoji, userId: string) {
|
async onRemoveReaction(msg: Message, emoji: CustomEmoji, userId: string) {
|
||||||
const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji(
|
const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji(msg.id, emoji.id || emoji.name);
|
||||||
msg.id,
|
|
||||||
emoji.id || emoji.name
|
|
||||||
);
|
|
||||||
if (!matchingReactionRole) return;
|
if (!matchingReactionRole) return;
|
||||||
|
|
||||||
const member = this.guild.members.get(userId);
|
const member = this.guild.members.get(userId);
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
import { decorators as d, Plugin } from "knub";
|
import { decorators as d, Plugin } from "knub";
|
||||||
import { Channel, Message, TextChannel, User } from "eris";
|
import { Channel, Message, User } from "eris";
|
||||||
import {
|
import {
|
||||||
formatTemplateString,
|
formatTemplateString,
|
||||||
getEmojiInString,
|
getEmojiInString,
|
||||||
getRoleMentions,
|
getRoleMentions,
|
||||||
getUrlsInString,
|
getUrlsInString,
|
||||||
getUserMentions,
|
getUserMentions,
|
||||||
sleep,
|
|
||||||
stripObjectToScalars,
|
stripObjectToScalars,
|
||||||
trimLines
|
trimLines
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
import { LogType } from "../data/LogType";
|
import { LogType } from "../data/LogType";
|
||||||
import { GuildLogs } from "../data/GuildLogs";
|
import { GuildLogs } from "../data/GuildLogs";
|
||||||
import { ModActionsPlugin } from "./ModActions";
|
import { ModActionsPlugin } from "./ModActions";
|
||||||
import { CaseType } from "../data/CaseType";
|
import { CaseTypes } from "../data/CaseTypes";
|
||||||
import { GuildArchives } from "../data/GuildArchives";
|
import { GuildArchives } from "../data/GuildArchives";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
|
|
||||||
|
@ -23,7 +22,8 @@ enum RecentActionType {
|
||||||
Link,
|
Link,
|
||||||
Attachment,
|
Attachment,
|
||||||
Emoji,
|
Emoji,
|
||||||
Newline
|
Newline,
|
||||||
|
Censor
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRecentAction {
|
interface IRecentAction {
|
||||||
|
@ -96,7 +96,7 @@ export class SpamPlugin extends Plugin {
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.logs = new GuildLogs(this.guildId);
|
this.logs = new GuildLogs(this.guildId);
|
||||||
this.archives = new GuildArchives(this.guildId);
|
this.archives = GuildArchives.getInstance(this.guildId);
|
||||||
|
|
||||||
this.recentActions = [];
|
this.recentActions = [];
|
||||||
this.expiryInterval = setInterval(() => this.clearOldRecentActions(), 1000 * 60);
|
this.expiryInterval = setInterval(() => this.clearOldRecentActions(), 1000 * 60);
|
||||||
|
@ -260,7 +260,7 @@ export class SpamPlugin extends Plugin {
|
||||||
const logUrl = await this.saveSpamArchives(uniqueMessages, msg.channel, msg.author);
|
const logUrl = await this.saveSpamArchives(uniqueMessages, msg.channel, msg.author);
|
||||||
|
|
||||||
// Create a case and log the actions taken above
|
// Create a case and log the actions taken above
|
||||||
const caseType = spamConfig.mute ? CaseType.Mute : CaseType.Note;
|
const caseType = spamConfig.mute ? CaseTypes.Mute : CaseTypes.Note;
|
||||||
const caseText = trimLines(`
|
const caseText = trimLines(`
|
||||||
Automatic spam detection: ${description} (over ${spamConfig.count} in ${spamConfig.interval}s)
|
Automatic spam detection: ${description} (over ${spamConfig.count} in ${spamConfig.interval}s)
|
||||||
${logUrl}
|
${logUrl}
|
||||||
|
@ -297,6 +297,14 @@ export class SpamPlugin extends Plugin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For interoperability with the Censor plugin
|
||||||
|
async logCensor(msg: Message) {
|
||||||
|
const spamConfig = this.configValueForMsg(msg, "max_censor");
|
||||||
|
if (spamConfig) {
|
||||||
|
this.logAndDetectSpam(msg, RecentActionType.Censor, spamConfig, 1, "too many censored messages");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@d.event("messageCreate")
|
@d.event("messageCreate")
|
||||||
async onMessageCreate(msg: Message) {
|
async onMessageCreate(msg: Message) {
|
||||||
if (msg.author.bot) return;
|
if (msg.author.bot) return;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Plugin, decorators as d } from "knub";
|
import { Plugin, decorators as d } from "knub";
|
||||||
import { Channel, Message, TextChannel } from "eris";
|
import { Message } from "eris";
|
||||||
import { errorMessage, successMessage } from "../utils";
|
import { errorMessage, successMessage } from "../utils";
|
||||||
import { GuildTags } from "../data/GuildTags";
|
import { GuildTags } from "../data/GuildTags";
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export class TagsPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.tags = new GuildTags(this.guildId);
|
this.tags = GuildTags.getInstance(this.guildId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@d.command("tag", "<tag:string> <body:string$>")
|
@d.command("tag", "<tag:string> <body:string$>")
|
||||||
|
|
|
@ -1,19 +1,12 @@
|
||||||
import { Plugin, decorators as d, reply } from "knub";
|
import { Plugin, decorators as d, reply } from "knub";
|
||||||
import { Channel, EmbedOptions, Message, TextChannel, User, VoiceChannel } from "eris";
|
import { Channel, EmbedOptions, Message, TextChannel, User, VoiceChannel } from "eris";
|
||||||
import {
|
import { embedPadding, errorMessage, getMessages, stripObjectToScalars, successMessage, trimLines } from "../utils";
|
||||||
embedPadding,
|
|
||||||
errorMessage,
|
|
||||||
getMessages,
|
|
||||||
stripObjectToScalars,
|
|
||||||
successMessage,
|
|
||||||
trimLines
|
|
||||||
} from "../utils";
|
|
||||||
import { GuildLogs } from "../data/GuildLogs";
|
import { GuildLogs } from "../data/GuildLogs";
|
||||||
import { LogType } from "../data/LogType";
|
import { LogType } from "../data/LogType";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import humanizeDuration from "humanize-duration";
|
import humanizeDuration from "humanize-duration";
|
||||||
import { GuildCases } from "../data/GuildCases";
|
import { GuildCases } from "../data/GuildCases";
|
||||||
import { CaseType } from "../data/CaseType";
|
import { CaseTypes } from "../data/CaseTypes";
|
||||||
|
|
||||||
const MAX_SEARCH_RESULTS = 15;
|
const MAX_SEARCH_RESULTS = 15;
|
||||||
const MAX_CLEAN_COUNT = 50;
|
const MAX_CLEAN_COUNT = 50;
|
||||||
|
@ -54,7 +47,7 @@ export class UtilityPlugin extends Plugin {
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.logs = new GuildLogs(this.guildId);
|
this.logs = new GuildLogs(this.guildId);
|
||||||
this.cases = new GuildCases(this.guildId);
|
this.cases = GuildCases.getInstance(this.guildId);
|
||||||
|
|
||||||
if (activeReloads && activeReloads.has(this.guildId)) {
|
if (activeReloads && activeReloads.has(this.guildId)) {
|
||||||
activeReloads.get(this.guildId).createMessage(successMessage("Reloaded!"));
|
activeReloads.get(this.guildId).createMessage(successMessage("Reloaded!"));
|
||||||
|
@ -80,9 +73,7 @@ export class UtilityPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
const level = this.getMemberLevel(member);
|
const level = this.getMemberLevel(member);
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(`The permission level of ${member.username}#${member.discriminator} is **${level}**`);
|
||||||
`The permission level of ${member.username}#${member.discriminator} is **${level}**`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@d.command("search", "<query:string$>")
|
@d.command("search", "<query:string$>")
|
||||||
|
@ -126,9 +117,7 @@ export class UtilityPlugin extends Plugin {
|
||||||
});
|
});
|
||||||
lines = lines.slice(from, to);
|
lines = lines.slice(from, to);
|
||||||
|
|
||||||
const footer = paginated
|
const footer = paginated ? "Add a page number to the end of the command to browse results" : "";
|
||||||
? "Add a page number to the end of the command to browse results"
|
|
||||||
: "";
|
|
||||||
|
|
||||||
msg.channel.createMessage(`${header}\n\`\`\`${lines.join("\n")}\`\`\`${footer}`);
|
msg.channel.createMessage(`${header}\n\`\`\`${lines.join("\n")}\`\`\`${footer}`);
|
||||||
} else {
|
} else {
|
||||||
|
@ -152,25 +141,17 @@ export class UtilityPlugin extends Plugin {
|
||||||
@d.permission("clean")
|
@d.permission("clean")
|
||||||
async cleanAllCmd(msg: Message, args: { count: number }) {
|
async cleanAllCmd(msg: Message, args: { count: number }) {
|
||||||
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
|
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`));
|
||||||
errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`)
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const messagesToClean = await getMessages(
|
const messagesToClean = await getMessages(msg.channel as TextChannel, m => m.id !== msg.id, args.count);
|
||||||
msg.channel as TextChannel,
|
|
||||||
m => m.id !== msg.id,
|
|
||||||
args.count
|
|
||||||
);
|
|
||||||
if (messagesToClean.length > 0) {
|
if (messagesToClean.length > 0) {
|
||||||
await this.cleanMessages(msg.channel, messagesToClean.map(m => m.id), msg.author);
|
await this.cleanMessages(msg.channel, messagesToClean.map(m => m.id), msg.author);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(
|
||||||
successMessage(
|
successMessage(`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`)
|
||||||
`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,9 +159,7 @@ export class UtilityPlugin extends Plugin {
|
||||||
@d.permission("clean")
|
@d.permission("clean")
|
||||||
async cleanUserCmd(msg: Message, args: { userId: string; count: number }) {
|
async cleanUserCmd(msg: Message, args: { userId: string; count: number }) {
|
||||||
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
|
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`));
|
||||||
errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`)
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,9 +173,7 @@ export class UtilityPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(
|
||||||
successMessage(
|
successMessage(`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`)
|
||||||
`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,9 +181,7 @@ export class UtilityPlugin extends Plugin {
|
||||||
@d.permission("clean")
|
@d.permission("clean")
|
||||||
async cleanBotCmd(msg: Message, args: { count: number }) {
|
async cleanBotCmd(msg: Message, args: { count: number }) {
|
||||||
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
|
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`));
|
||||||
errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`)
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,9 +195,7 @@ export class UtilityPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.channel.createMessage(
|
msg.channel.createMessage(
|
||||||
successMessage(
|
successMessage(`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`)
|
||||||
`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +256,7 @@ export class UtilityPlugin extends Plugin {
|
||||||
});
|
});
|
||||||
|
|
||||||
const caseSummaries = cases.map(c => {
|
const caseSummaries = cases.map(c => {
|
||||||
return `${CaseType[c.type]} (#${c.case_number})`;
|
return `${CaseTypes[c.type]} (#${c.case_number})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
embed.fields.push({
|
embed.fields.push({
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"target": "ES2017",
|
"emitDecoratorMetadata": true,
|
||||||
|
"target": "ES6",
|
||||||
"lib": [
|
"lib": [
|
||||||
"es6",
|
"es6",
|
||||||
"es7",
|
"es7",
|
||||||
|
|
Loading…
Add table
Reference in a new issue