commit
ce821ae088
@ -1,5 +1,5 @@
|
|||||||
/* template index.js. Doesn't implement actual commands */
|
/* template index.js. Doesn't implement actual commands */
|
||||||
const cmdLib = require('../../CommandLib'); // required for command objects
|
const cmdLib = require('../../lib/command'); // required for command objects
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A description what the command module includes and why. Doesn't need to list commands but explains
|
* A description what the command module includes and why. Doesn't need to list commands but explains
|
@ -1,6 +1,6 @@
|
|||||||
const cmdLib = require('../../CommandLib'),
|
const cmdLib = require('../../lib/command'),
|
||||||
fsx = require('fs-extra'),
|
fsx = require('fs-extra'),
|
||||||
utils = require('../../utils');
|
utils = require('../../lib/utils');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Info commands provide information about the bot. These informations are
|
* Info commands provide information about the bot. These informations are
|
@ -1,7 +1,7 @@
|
|||||||
const fetch = require('node-fetch'),
|
const fetch = require('node-fetch'),
|
||||||
fsx = require('fs-extra'),
|
fsx = require('fs-extra'),
|
||||||
yaml = require('js-yaml'),
|
yaml = require('js-yaml'),
|
||||||
queryPath = './lib/api/graphql/AnilistApi',
|
queryPath = __dirname + '/graphql',
|
||||||
alApiEndpoint = 'https://graphql.anilist.co';
|
alApiEndpoint = 'https://graphql.anilist.co';
|
||||||
|
|
||||||
async function getFragments() {
|
async function getFragments() {
|
@ -0,0 +1,175 @@
|
|||||||
|
const genericSql = require('../utils/genericSql'),
|
||||||
|
logging = require('../utils/logging'),
|
||||||
|
config = require('../../config.json');
|
||||||
|
|
||||||
|
class Database {
|
||||||
|
/**
|
||||||
|
* Creates a new database.
|
||||||
|
* @param name {String} - the name of the database.
|
||||||
|
*/
|
||||||
|
constructor(name) {
|
||||||
|
this.name = name;
|
||||||
|
this._logger = new logging.Logger(`Database@${name}`);
|
||||||
|
this._dbType = config.database? config.database : 'sqlite';
|
||||||
|
if (this._dbType === 'sqlite')
|
||||||
|
this.database = new (require('../utils/sqliteAsync')).Database(`./data/${this.name}.db`);
|
||||||
|
else if (this._dbType === 'postgresql')
|
||||||
|
this.database = new (require('pg')).Pool({
|
||||||
|
user: config.databaseConnection.user,
|
||||||
|
host: config.databaseConnection.host,
|
||||||
|
database: config.databaseConnection.database,
|
||||||
|
password: config.databaseConnection.password,
|
||||||
|
port: config.databaseConnection.port
|
||||||
|
});
|
||||||
|
this.sql = new genericSql.GenericSql(this._dbType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the database.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async initDatabase() {
|
||||||
|
if (this._dbType === 'sqlite') {
|
||||||
|
await this.database.init();
|
||||||
|
} else if (this._dbType === 'postgresql') {
|
||||||
|
await this.database.connect();
|
||||||
|
await this.begin();
|
||||||
|
await this.database.query(`CREATE SCHEMA IF NOT EXISTS ${this.name.replace(/\W/g, '')}`);
|
||||||
|
await this.database.query(`SET search_path TO ${this.name.replace(/\W/g, '')}`);
|
||||||
|
await this.commit();
|
||||||
|
}
|
||||||
|
this._logger.verbose(`Connected to ${this._dbType} database ${this.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a sql statement with seperate values and no return.
|
||||||
|
* Autocommit.
|
||||||
|
* @param sql {String}
|
||||||
|
* @param [values] {Array<String|Number>}
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
async run(sql, values) {
|
||||||
|
this._logger.debug(`Running SQL "${sql}" with values ${values}`);
|
||||||
|
if (this._dbType === 'sqlite')
|
||||||
|
await this.database.run(sql, values);
|
||||||
|
else if (this._dbType === 'postgresql')
|
||||||
|
try {
|
||||||
|
await this.begin();
|
||||||
|
await this.database.query(sql, values);
|
||||||
|
await this.commit();
|
||||||
|
} catch (err) {
|
||||||
|
this._logger.error(err.message);
|
||||||
|
this._logger.verbose(err.stack);
|
||||||
|
await this.rollback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begin. Part of Postgresqls BEGIN / COMMIT / ROLLBACK
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async begin() {
|
||||||
|
if (this._dbType === 'postgresql') {
|
||||||
|
await this.database.query('BEGIN');
|
||||||
|
await this.database.query(`SET search_path TO ${this.name.replace(/\W/g, '')};`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a query to the current changes. No autocommit (except on sqlite).
|
||||||
|
* @param sql
|
||||||
|
* @param values
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async query(sql, values) {
|
||||||
|
if (this._dbType === 'sqlite') {
|
||||||
|
await this.run(sql, values);
|
||||||
|
} else if (this._dbType === 'postgresql') {
|
||||||
|
await this.database.query(sql, values);
|
||||||
|
this._logger.debug(`Running SQL "${sql}" with values ${values}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit. Part of Postgresqls BEGIN / COMMIT / ROLLBACK.
|
||||||
|
* Writes data to the database, ROLLBACK on error. (has no effect on sqlite)
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async commit() {
|
||||||
|
if (this._dbType === 'postgresql')
|
||||||
|
try {
|
||||||
|
await this.database.query('COMMIT');
|
||||||
|
} catch (err) {
|
||||||
|
await this.database.query('ROLLBACK');
|
||||||
|
this._logger.error(err.message);
|
||||||
|
this._logger.verbose(err.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback. Part of Postgresqls BEGIN / COMMIT / ROLLBACK.
|
||||||
|
* Reverts changes done in the current commit. (has no effect on sqlite)
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async rollback() {
|
||||||
|
if (this._dbType === 'postgresql')
|
||||||
|
this.database.query('ROLLBACK');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a sql statement with seperate values and first result row as return.
|
||||||
|
* @param sql {String} - the sql statement with escaped values ($1, $2... for postgres, ? for sqlite)
|
||||||
|
* @param [values] {Array<String|Number>}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async get(sql, values) {
|
||||||
|
this._logger.debug(`Running SQL "${sql}" with values ${values}`);
|
||||||
|
let result = null;
|
||||||
|
if (this._dbType === 'sqlite') {
|
||||||
|
result = await this.database.get(sql, values);
|
||||||
|
} else if (this._dbType === 'postgresql') {
|
||||||
|
await this.database.query(`SET search_path TO ${this.name.replace(/\W/g, '')};`);
|
||||||
|
result = (await this.database.query({
|
||||||
|
text: sql,
|
||||||
|
values: values
|
||||||
|
})).rows;
|
||||||
|
}
|
||||||
|
if (result instanceof Array && result.length > 0)
|
||||||
|
return result[0];
|
||||||
|
else
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a sql statement with seperate values and all result rows as return.
|
||||||
|
* @param sql {String} - the sql statement with escaped values ($1, $2... for postgres, ? for sqlite)
|
||||||
|
* @param [values] {Array<String|Number>} - the seperate values
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
async all(sql, values) {
|
||||||
|
this._logger.debug(`Running SQL "${sql}" with values ${values}`);
|
||||||
|
if (this._dbType === 'sqlite') {
|
||||||
|
return await this.database.all(sql, values);
|
||||||
|
} else if (this._dbType === 'postgresql') {
|
||||||
|
await this.database.query(`SET search_path TO ${this.name.replace(/\W/g, '')};`);
|
||||||
|
return (await this.database.query({
|
||||||
|
text: sql,
|
||||||
|
values: values
|
||||||
|
})).rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the connection to the database.
|
||||||
|
*/
|
||||||
|
close() {
|
||||||
|
if (this._dbType === 'sqlite')
|
||||||
|
this.database.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(exports, {
|
||||||
|
Column: genericSql.Column,
|
||||||
|
Database: Database
|
||||||
|
});
|
@ -1,102 +0,0 @@
|
|||||||
const music = require('./MusicLib'),
|
|
||||||
utils = require('./utils'),
|
|
||||||
config = require('../config.json'),
|
|
||||||
sqliteAsync = require('./sqliteAsync'),
|
|
||||||
logging = require('./logging'),
|
|
||||||
fs = require('fs-extra'),
|
|
||||||
dataDir = config.dataPath || './data';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Guild Handler handles guild settings and data.
|
|
||||||
* @type {GuildHandler}
|
|
||||||
*/
|
|
||||||
class GuildHandler {
|
|
||||||
|
|
||||||
constructor(guild) {
|
|
||||||
this.guild = guild;
|
|
||||||
this._logger = new logging.Logger(`${this.constructor.name}@${this.guild}`);
|
|
||||||
this.musicPlayer = new music.MusicPlayer(null);
|
|
||||||
this._logger.silly('Initialized Guild Handler');
|
|
||||||
this._votes = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the database
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
async initDatabase() {
|
|
||||||
this._logger.silly('Initializing Database');
|
|
||||||
await fs.ensureDir(dataDir + '/gdb');
|
|
||||||
this.db = new sqliteAsync.Database(`${dataDir}/gdb/${this.guild}.db`);
|
|
||||||
await this.db.init();
|
|
||||||
this._logger.debug(`Connected to the database for ${this.guild}`);
|
|
||||||
this._logger.debug('Creating Databases');
|
|
||||||
await this._createTables();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys the guild handler
|
|
||||||
*/
|
|
||||||
destroy() {
|
|
||||||
this._logger.debug('Ending musicPlayer');
|
|
||||||
this.musicPlayer.stop();
|
|
||||||
this._logger.debug('Ending Database');
|
|
||||||
this.db.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates all tables needed in the Database.
|
|
||||||
* These are at the moment:
|
|
||||||
* messages - logs all messages send on the server
|
|
||||||
* playlists - save playlists to play them later
|
|
||||||
*/
|
|
||||||
async _createTables() {
|
|
||||||
await this.db.run(`${utils.sql.tableExistCreate} messages (
|
|
||||||
${utils.sql.pkIdSerial},
|
|
||||||
creation_timestamp DATETIME NOT NULL,
|
|
||||||
author VARCHAR(128) NOT NULL,
|
|
||||||
author_name VARCHAR(128),
|
|
||||||
content TEXT NOT NULL
|
|
||||||
)`);
|
|
||||||
this._logger.silly('Created Table messages');
|
|
||||||
await this.db.run(`${utils.sql.tableExistCreate} playlists (
|
|
||||||
${utils.sql.pkIdSerial},
|
|
||||||
name VARCHAR(32) UNIQUE NOT NULL,
|
|
||||||
url VARCHAR(255) NOT NULL
|
|
||||||
)`);
|
|
||||||
this._logger.silly('Created Table playlists');
|
|
||||||
await this.db.run(`${utils.sql.tableExistCreate} commands (
|
|
||||||
${utils.sql.pkIdSerial},
|
|
||||||
name VARCHAR(32) UNIQUE NOT NULL,
|
|
||||||
command VARCHAR(255) NOT NULL
|
|
||||||
)`);
|
|
||||||
this._logger.silly('Created Table commands');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the vote counter for a command up and adds the user.
|
|
||||||
* @param command {String}
|
|
||||||
* @param user {String}
|
|
||||||
*/
|
|
||||||
updateCommandVote(command, user) {
|
|
||||||
if (!this._votes[command])
|
|
||||||
this._votes[command] = {count: 0, users: []};
|
|
||||||
if (!this._votes[command].users.includes(user)) {
|
|
||||||
this._votes[command].count++;
|
|
||||||
this._votes[command].users.push(user);
|
|
||||||
}
|
|
||||||
return this._votes[command];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the vote counter and voted users for a command.
|
|
||||||
* @param command {String}
|
|
||||||
*/
|
|
||||||
resetCommandVote(command) {
|
|
||||||
this._votes[command] = {count: 0, users: []};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(exports, {
|
|
||||||
GuildHandler: GuildHandler
|
|
||||||
});
|
|
@ -0,0 +1,195 @@
|
|||||||
|
const music = require('../music'),
|
||||||
|
dblib = require('../database'),
|
||||||
|
logging = require('../utils/logging');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GuildDatabase class has abstraction for some sql statements.
|
||||||
|
*/
|
||||||
|
class GuildDatabase extends dblib.Database {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
constructor(name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates all tables needed in the guilds Database.
|
||||||
|
*/
|
||||||
|
async createTables() {
|
||||||
|
let sql = this.sql;
|
||||||
|
await this.run(sql.createTableIfNotExists('playlists', [
|
||||||
|
sql.templates.idcolumn,
|
||||||
|
new dblib.Column('name', sql.types.getVarchar(32), [sql.constraints.unique, sql.constraints.notNull]),
|
||||||
|
new dblib.Column('url', sql.types.getVarchar(255), [sql.constraints.notNull])
|
||||||
|
]));
|
||||||
|
this._logger.silly('Created Table playlists.');
|
||||||
|
await this.run(sql.createTableIfNotExists('commands', [
|
||||||
|
sql.templates.idcolumn,
|
||||||
|
new dblib.Column('name', sql.types.getVarchar(32), [sql.constraints.unique, sql.constraints.notNull]),
|
||||||
|
new dblib.Column('command', sql.types.getVarchar(255), [sql.constraints.notNull])
|
||||||
|
]));
|
||||||
|
this._logger.silly('Created Table commands.');
|
||||||
|
await this.run(sql.createTableIfNotExists('settings', [
|
||||||
|
sql.templates.idcolumn,
|
||||||
|
new dblib.Column('key', sql.types.getVarchar(32), [sql.constraints.unique, sql.constraints.notNull]),
|
||||||
|
new dblib.Column('value', sql.types.getVarchar(32), [])
|
||||||
|
]));
|
||||||
|
this._logger.silly('Created Table settings.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the column where the key has the value keyvalue
|
||||||
|
* @param table {String} - the table name
|
||||||
|
* @param column {String} - the name of the column
|
||||||
|
* @param keyname {String} - the name of the key
|
||||||
|
* @param keyvalue {*} - the value of the key
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
async getSingleValue(table, column, keyname, keyvalue) {
|
||||||
|
let result = await this.get(this.sql.select(table, false, column,
|
||||||
|
this.sql.where(this.sql.parameter(1), '=', this.sql.parameter(2))),
|
||||||
|
[keyname, keyvalue]);
|
||||||
|
if (result)
|
||||||
|
return result[column];
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns either the whole table or a limited version
|
||||||
|
* @param tablename
|
||||||
|
* @param limit
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async getTableContent(tablename, limit) {
|
||||||
|
if (limit)
|
||||||
|
return await this.all(this.sql.select(tablename, false, ['*'], [], [
|
||||||
|
this.sql.limit(limit)
|
||||||
|
]));
|
||||||
|
else
|
||||||
|
return await this.all(this.sql.select(tablename, false, ['*'], [], []));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of a setting
|
||||||
|
* @param name
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
async getSetting(name) {
|
||||||
|
let result = await this.get(this.sql.select('settings', false, 'value',
|
||||||
|
this.sql.where('key', '=', this.sql.parameter(1))), [name]);
|
||||||
|
if (result)
|
||||||
|
return result.value;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all settings as object.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async getSettings() {
|
||||||
|
let rows = await this.all(this.sql.select('settings', false, ['key', 'value'], [], []));
|
||||||
|
let retObj = {};
|
||||||
|
if (rows)
|
||||||
|
for (let row of rows)
|
||||||
|
retObj[row.key] = row.value;
|
||||||
|
return retObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert or update a setting parameter in the settings database.
|
||||||
|
* @param name
|
||||||
|
* @param value
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async setSetting(name, value) {
|
||||||
|
let row = await this.get(this.sql.select('settings', false, [this.sql.count('*')],
|
||||||
|
this.sql.where('key', '=', this.sql.parameter(1))), [name]);
|
||||||
|
if (!row || Number(row.count) === 0)
|
||||||
|
await this.run(this.sql.insert('settings', {key: this.sql.parameter(1), value: this.sql.parameter(2)}),
|
||||||
|
[name, value]);
|
||||||
|
else
|
||||||
|
await this.run(this.sql.update('settings', {value: this.sql.parameter(1)},
|
||||||
|
this.sql.where('key', '=', this.sql.parameter(2))), [value, name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Guild Handler handles guild settings and data.
|
||||||
|
* @type {GuildHandler}
|
||||||
|
*/
|
||||||
|
class GuildHandler {
|
||||||
|
|
||||||
|
constructor(guild) {
|
||||||
|
this.guild = guild;
|
||||||
|
this._logger = new logging.Logger(`${this.constructor.name}@${this.guild}`);
|
||||||
|
this.musicPlayer = new music.MusicPlayer(null);
|
||||||
|
this._logger.silly('Initialized Guild Handler');
|
||||||
|
this._votes = {};
|
||||||
|
this.settings = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the database
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async initDatabase() {
|
||||||
|
this._logger.silly('Initializing Database');
|
||||||
|
this.db = new GuildDatabase(`guild_${this.guild.name.replace(/\s/g, '_').replace(/\W/g, '')}`);
|
||||||
|
await this.db.initDatabase();
|
||||||
|
this._logger.debug(`Connected to the database for ${this.guild}`);
|
||||||
|
this._logger.debug('Creating Databases');
|
||||||
|
await this.db.createTables();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all relevant guild settings.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async applySettings() {
|
||||||
|
this.settings = await this.db.getSettings();
|
||||||
|
this.musicPlayer.setVolume(Number(this.settings.musicPlayerVolume) || 0.5);
|
||||||
|
this.musicPlayer.quality = this.settings.musicPlayerQuality || 'lowest';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the guild handler
|
||||||
|
*/
|
||||||
|
destroy() {
|
||||||
|
this._logger.debug('Ending musicPlayer');
|
||||||
|
this.musicPlayer.stop();
|
||||||
|
this._logger.debug('Ending Database');
|
||||||
|
this.db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the vote counter for a command up and adds the user.
|
||||||
|
* @param command {String}
|
||||||
|
* @param user {String}
|
||||||
|
*/
|
||||||
|
updateCommandVote(command, user) {
|
||||||
|
if (!this._votes[command])
|
||||||
|
this._votes[command] = {count: 0, users: []};
|
||||||
|
if (!this._votes[command].users.includes(user)) {
|
||||||
|
this._votes[command].count++;
|
||||||
|
this._votes[command].users.push(user);
|
||||||
|
}
|
||||||
|
return this._votes[command];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the vote counter and voted users for a command.
|
||||||
|
* @param command {String}
|
||||||
|
*/
|
||||||
|
resetCommandVote(command) {
|
||||||
|
this._votes[command] = {count: 0, users: []};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(exports, {
|
||||||
|
GuildHandler: GuildHandler
|
||||||
|
});
|
@ -0,0 +1,76 @@
|
|||||||
|
const logging = require('../utils/logging'),
|
||||||
|
EventEmitter = require('events');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends the event emitter with some useful features.
|
||||||
|
*/
|
||||||
|
class ExtendedEventEmitter extends EventEmitter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param [name] {String}
|
||||||
|
*/
|
||||||
|
constructor(name) {
|
||||||
|
super();
|
||||||
|
this._logger = new logging.Logger(`${name}-${this.constructor.name}`);
|
||||||
|
this._registerDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registeres the error event to the logger so it won't crash the bot.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_registerDefault() {
|
||||||
|
this.on('error', (err) => {
|
||||||
|
this._logger.error(err.message);
|
||||||
|
this._logger.debug(err.stack);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an object of events with listeners to the bot.
|
||||||
|
* @param eventListenerObject
|
||||||
|
* @returns {ExtendedEventEmitter}
|
||||||
|
*/
|
||||||
|
addListeners(eventListenerObject) {
|
||||||
|
for (let [event, listener] of Object.entries(eventListenerObject))
|
||||||
|
this.on(event, listener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all registered events.
|
||||||
|
* @returns {*|Array<string | symbol>|string[]}
|
||||||
|
*/
|
||||||
|
get events() {
|
||||||
|
return this.eventNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around getMaxListeners function
|
||||||
|
* @returns {*|number}
|
||||||
|
*/
|
||||||
|
get maxListeners() {
|
||||||
|
return this.getMaxListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around setMaxListeners function.
|
||||||
|
* @param n
|
||||||
|
* @returns {this | this | Cluster | *}
|
||||||
|
*/
|
||||||
|
set maxListeners(n) {
|
||||||
|
return this.setMaxListeners(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if the emitter has additional listeners apart from the error listener.
|
||||||
|
*/
|
||||||
|
get hasListeners() {
|
||||||
|
return this.events.count > 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(exports, {
|
||||||
|
ExtendedEventEmitter: ExtendedEventEmitter,
|
||||||
|
});
|
@ -0,0 +1,413 @@
|
|||||||
|
/**
|
||||||
|
* Returns types based on the database.
|
||||||
|
*/
|
||||||
|
class GenericTypes {
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param database {String}
|
||||||
|
*/
|
||||||
|
constructor(database) {
|
||||||
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
get null() {
|
||||||
|
switch(this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
default:
|
||||||
|
return 'NULL';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get integer() {
|
||||||
|
switch(this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
default:
|
||||||
|
return 'INTEGER';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get real() {
|
||||||
|
switch(this.database) {
|
||||||
|
case 'sqlite':
|
||||||
|
return 'REAL';
|
||||||
|
case 'postgresql':
|
||||||
|
default:
|
||||||
|
return 'FLOAT';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get text() {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
default:
|
||||||
|
return 'TEXT';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get varchar() {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
default:
|
||||||
|
return 'VARCHAR';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get date() {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
default:
|
||||||
|
return 'DATE';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get datetime() {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
return 'TIMESTAMP';
|
||||||
|
case 'sqlite':
|
||||||
|
default:
|
||||||
|
return 'DATETIME';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get serial() {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'sqlite':
|
||||||
|
return 'INTEGER AUTOINCREMENT NOT NULL';
|
||||||
|
case 'postgresql':
|
||||||
|
default:
|
||||||
|
return 'SERIAL';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get serialPK() {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'sqlite':
|
||||||
|
return 'INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL';
|
||||||
|
case 'postgresql':
|
||||||
|
default:
|
||||||
|
return 'SERIAL PRIMARY KEY UNIQUE';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the VARCHAR type with the specified length.
|
||||||
|
* @param length {Number}
|
||||||
|
*/
|
||||||
|
getVarchar(length) {
|
||||||
|
return `${this.varchar}(${length})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns sql statements based on the database.
|
||||||
|
*/
|
||||||
|
class GenericSql {
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param database {String}
|
||||||
|
*/
|
||||||
|
constructor(database) {
|
||||||
|
this.database = database;
|
||||||
|
this.types = new GenericTypes(database);
|
||||||
|
this.constraints = {
|
||||||
|
primaryKey: 'PRIMARY KEY',
|
||||||
|
notNull: 'NOT NULL',
|
||||||
|
unique: 'UNIQUE',
|
||||||
|
like: 'LIKE',
|
||||||
|
exists: 'EXISTS',
|
||||||
|
and: 'AND',
|
||||||
|
or: 'OR',
|
||||||
|
in: 'IN',
|
||||||
|
any: 'ANY',
|
||||||
|
all: 'ALL'
|
||||||
|
};
|
||||||
|
this.templates = {
|
||||||
|
idcolumn: new Column('id', this.types.serialPK, [])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a value placeholder for the specified number.
|
||||||
|
* @param number {Number} - the variables position.
|
||||||
|
*/
|
||||||
|
parameter(number) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
return `$${number}`;
|
||||||
|
case 'sqlite':
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sum selector - calculates the sum of all values of the column
|
||||||
|
* @param colname {String} - the name of the column where the sum is selected.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
sum(colname) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `SUM(${colname})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A avg selector - selects the average
|
||||||
|
* @param colname {String} - the name of the column where the avg value is selected.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
avg(colname) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `AVG(${colname})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A min selector - selects the minimum
|
||||||
|
* @param colname {String} - the name of the column where the min value is selected.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
min(colname) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `MIN(${colname})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A max selector - selects the maximum
|
||||||
|
* @param colname {String} - the name of the column where the max value is selected.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
max(colname) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `MAX(${colname})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A count selector - counts the results
|
||||||
|
* @param colname {String} - the name of the column to be counted.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
count(colname) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `COUNT(${colname}) count`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A default constraint
|
||||||
|
* @param expression {String} - the expression to generate the default value.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
default(expression) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `DEFAULT ${expression}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A where statement
|
||||||
|
* @param row {String} - the row
|
||||||
|
* @param operator {String} - the comparison operator
|
||||||
|
* @param comparator {String} the value or row to compare to
|
||||||
|
*/
|
||||||
|
and(row, operator, comparator) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `AND ${row} ${operator} ${comparator}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A or statement
|
||||||
|
* @param row {String} - the row
|
||||||
|
* @param operator {String} - the comparison operator
|
||||||
|
* @param comparator {String} the value or row to compare to
|
||||||
|
*/
|
||||||
|
or(row, operator, comparator) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `OR ${row} ${operator} ${comparator}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A where statement
|
||||||
|
* @param row {String} - the row
|
||||||
|
* @param operator {String} - the comparison operator
|
||||||
|
* @param comparator {String} the value or row to compare to
|
||||||
|
*/
|
||||||
|
where(row, operator, comparator) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `WHERE ${row} ${operator} ${comparator}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A limit statement.
|
||||||
|
* @param count {Number} - the number of rows to return
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
limit(count) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `LIMIT ${count}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Table statement
|
||||||
|
* @param table {String}
|
||||||
|
* @param rows {Array<Column>}
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
createTable(table, rows) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `CREATE TABLE ${table} (${rows.map(x => x.sql).join(',')})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Table if it doesn't exist statement
|
||||||
|
* @param table {String}
|
||||||
|
* @param columns {Array<Column>}
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
createTableIfNotExists(table, columns) {
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `CREATE TABLE IF NOT EXISTS ${table} (${columns.map(x => x.sql).join(',')})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert into the table.
|
||||||
|
* @param table {String} - the table name
|
||||||
|
* @param colValueObj {Object} - an object with keys as columnnames and values as columnvalues
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
insert(table, colValueObj) {
|
||||||
|
let rownames = Object.keys(colValueObj);
|
||||||
|
let values = Object.values(colValueObj);
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `INSERT INTO ${table} (${rownames.join(',')}) values (${values.join(',')})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the table with the rowValueObject.
|
||||||
|
* @param table {String} - the table name
|
||||||
|
* @param colValueObj {Object} - an object with keys as columnnames and values as columnvalues
|
||||||
|
* @param conditions {Array<String>|String} - conditions for the update row selection (WHERE ... [OR ...][AND ...]
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
update(table, colValueObj, conditions) {
|
||||||
|
if (!(conditions instanceof Array))
|
||||||
|
conditions = [conditions];
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `UPDATE ${table} SET ${Object.entries(colValueObj).map(x => `${x[0]} = ${x[1]}`).join(',')} ${conditions.join(' ')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects from a table
|
||||||
|
* @param table {String} - the tablename
|
||||||
|
* @param distinct {String|boolean} - should distinct values be selected? If yes provide distinct keyword.
|
||||||
|
* @param colnames {Array<String>|String} - the rows to select
|
||||||
|
* @param conditions {Array<String>|String} - conditions for the row selection (WHERE ... [OR ...][AND ...]
|
||||||
|
* @param operations {Array<String>|String} - operations on the selected rows
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
select(table, distinct, colnames, conditions, operations) {
|
||||||
|
if (!(colnames instanceof Array))
|
||||||
|
colnames = [colnames];
|
||||||
|
if (!(conditions instanceof Array))
|
||||||
|
conditions = [conditions];
|
||||||
|
if (!(operations instanceof Array))
|
||||||
|
operations = [operations];
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `SELECT${distinct? ' ' + distinct : ''} ${colnames.join(', ')} FROM ${table} ${conditions.join(' ')} ${operations.join(' ')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes from a table
|
||||||
|
* @param table {String} - the table name
|
||||||
|
* @param conditions {Array<String>|String} - conditions for the row selection (WHERE ... [OR ...][AND ...]
|
||||||
|
*/
|
||||||
|
delete(table, conditions) {
|
||||||
|
if (!(conditions instanceof Array))
|
||||||
|
conditions = [conditions];
|
||||||
|
switch (this.database) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'sqlite':
|
||||||
|
return `DELETE FROM ${table} ${conditions.join(' ')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Column {
|
||||||
|
/**
|
||||||
|
* Create a column for usage in the generic sql statements
|
||||||
|
* @param name {String}
|
||||||
|
* @param [type] {String}
|
||||||
|
* @param [constraints] {Array<String>}
|
||||||
|
*/
|
||||||
|
constructor(name, type, constraints) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
this.constraints = constraints || [];
|
||||||
|
if (!(constraints instanceof Array))
|
||||||
|
this.constraints = [constraints];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the datatype of the row.
|
||||||
|
* @param constraint {String}
|
||||||
|
*/
|
||||||
|
addConstraint(constraint) {
|
||||||
|
this.constraints.push(constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
get sql() {
|
||||||
|
return `${this.name} ${this.type} ${this.constraints.join(' ')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(exports, {
|
||||||
|
GenericSql: GenericSql,
|
||||||
|
GenericTypes: GenericTypes,
|
||||||
|
Column: Column
|
||||||
|
});
|
Loading…
Reference in New Issue