diff --git a/README.md b/README.md index 9dfa28e..8ec6e7b 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,11 @@ At the moment the bot can... - [x] ...log stuff in a database - [ ] ...transform into a cow +Presences +--- + +You can add presences to the bot either by owner command `addpresence` or by providing a presences.txt file in the data directory. Each line represents a presence.

When all lines are loaded by the bot, the file gets deleted.

+ Ideas --- - command replies saved in file (server specific file and global file) diff --git a/bot.js b/bot.js index d8eee22..761ad8f 100644 --- a/bot.js +++ b/bot.js @@ -7,44 +7,59 @@ const Discord = require("discord.js"), config = require('./config.json'), client = new Discord.Client(), args = require('args-parser')(process.argv), + sqlite3 = require('sqlite3'), authToken = args.token || config.api.botToken, prefix = args.prefix || config.prefix || '~', gamepresence = args.game || config.presence; let presences = [], // loaded from presences.txt file if the file exists - rotator = null; // an interval id to stop presence duration if needed + rotator = null, // an interval id to stop presence duration if needed + maindb = null; function main() { + logger.verbose('Registering cleanup function'); + utils.Cleanup(() => { guilding.destroyAll(); client.destroy(); }); cmd.setLogger(logger); + logger.verbose('Verifying config'); + let configVerifyer = new utils.ConfigVerifyer(config, [ "api.botToken", "api.youTubeApiKey" ]); - if (configVerifyer.verifyConfig(logger)) { + if (!configVerifyer.verifyConfig(logger)) { if (!args.i) { + logger.info('Invalid config. Exiting'); process.exit(1); } } guilding.setLogger(logger); cmd.init(prefix); + logger.verbose('Registering commands'); registerCommands(); + logger.debug('Checking for ./data/ existence') utils.dirExistence('./data', () => { - fs.exists('./data/presences.txt', (exist) => { - if (exist) { - logger.debug('Loading presences from file...'); - let lineReader = require('readline').createInterface({ - input: require('fs').createReadStream('./data/presences.txt') - }); - lineReader.on('line', (line) => { - presences.push(line); + logger.verbose('Connecting to main database'); + maindb = new sqlite3.Database('./data/main.db', (err) => { + if (err) { + logger.error(err.message); + } else { + maindb.run(`${utils.sql.tableExistCreate} presences ( + ${utils.sql.pkIdSerial}, + text VARCHAR(255) UNIQUE NOT NULL + )`, (err) => { + if (err) { + logger.error(err.message); + } else { + logger.debug('Loading presences'); + loadPresences(); + } }); - rotator = client.setInterval(() => rotatePresence(), config.presence_duration); } - }) + }); }); registerCallbacks(); @@ -53,6 +68,55 @@ function main() { }); } +/** + * If a data/presences.txt exists, it is read and each line is put into the presences array. + * Each line is also stored in the main.db database. After the file is completely read, it get's deleted. + * Then the data is read from the database and if the presence doesn't exist in the presences array, it get's + * pushed in there. If the presences.txt file does not exist, the data is just read from the database. In the end + * a rotator is created that rotates the presence every configured duration. + */ +function loadPresences() { + if(fs.existsSync('./data/presences.txt')) { + let lineReader = require('readline').createInterface({ + input: require('fs').createReadStream('./data/presences.txt') + }); + lineReader.on('line', (line) => { + maindb.run('INSERT INTO presences (text) VALUES (?)', [line], (err) => { + if(err) { + logger.warn(err.message); + } + }); + presences.push(line); + }); + rotator = client.setInterval(() => rotatePresence(), config.presence_duration || 360000); + fs.unlink('./data/presences.txt', (err) => { + if (err) + logger.warn(err.message); + }); + maindb.all('SELECT text FROM presences', (err, rows) => { + if (err) { + logger.warn(err.message); + } else { + for(let row of rows) { + if (!row[0] in presences) + presences.push(row.text); + } + } + }) + } else { + maindb.all('SELECT text FROM presences', (err, rows) => { + if (err) { + logger.warn(err.message); + } else { + for(let row of rows) { + presences.push(row.text); + } + } + rotator = client.setInterval(() => rotatePresence(), config.presence_duration || 360000); + }) + } +} + /** * registeres global commands */ @@ -66,15 +130,20 @@ function registerCommands() { cmd.createGlobalCommand(prefix + 'addpresence', (msg, argv, args) => { let p = args.join(' '); presences.push(p); - fs.writeFile('./data/presences.txt', presences.join('\n'), (err) => { + + maindb.run('INSERT INTO presences (text) VALUES (?)', [p], (err) => { + if (err) + logger.warn(err.message); }); return `Added Presence \`${p}\``; }, [], "Adds a presence to the rotation.", 'owner'); // shuts down the bot after destroying the client cmd.createGlobalCommand(prefix + 'shutdown', (msg) => { + msg.reply('Shutting down...').finally(() => { logger.debug('Destroying client...'); + client.destroy().finally(() => { logger.debug(`Exiting Process...`); process.exit(0); diff --git a/lib/guilding.js b/lib/guilding.js index 048400b..76568b2 100644 --- a/lib/guilding.js +++ b/lib/guilding.js @@ -1,6 +1,6 @@ const cmd = require('./cmd'), music = require('./music'), - utils = require('./utils.js'), + utils = require('./utils'), config = require('../config.json'), servercmd = require('../commands/servercommands'), sqlite3 = require('sqlite3'), @@ -60,17 +60,15 @@ exports.GuildHandler = class { * playlists - save playlists to play them later */ createTables() { - let createCmd = 'CREATE TABLE IF NOT EXISTS'; - let autoIdPK = 'id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL'; - this.db.run(`${createCmd} messages ( - ${autoIdPK}, + 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.db.run(`${createCmd} playlists ( - ${autoIdPK}, + this.db.run(`${utils.sql.tableExistCreate} playlists ( + ${utils.sql.pkIdSerial}, name VARCHAR(32) UNIQUE NOT NULL, url VARCHAR(255) NOT NULL )`); diff --git a/lib/utils.js b/lib/utils.js index 5653ce9..a691ed3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -199,7 +199,7 @@ exports.ConfigVerifyer = class { } /** - * @param promtMissing {Boolean} true - everything is fine; false - missing attributes + * @param logger set the logger to log to */ verifyConfig(logger) { let missing = []; @@ -221,4 +221,9 @@ exports.ConfigVerifyer = class { logger.error(`Missing required Attributes ${this.missingAttributes.join(', ')}`); } } +}; + +exports.sql = { + tableExistCreate: 'CREATE TABLE IF NOT EXISTS', + pkIdSerial: 'id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL' }; \ No newline at end of file diff --git a/test/test.js b/test/test.js index 1149922..fd080d1 100644 --- a/test/test.js +++ b/test/test.js @@ -358,10 +358,13 @@ describe('lib/cmd', function() { describe('lib/guilding', function() { const guilding = rewire('../lib/guilding'); const servercommands = require('../commands/servercommands'); + const utils = require('../lib/utils'); guilding.__set__("sqlite3", null); guilding.__set__("utils", { dirExistence: (file, callback) => { - } + }, + sql: utils.sql, + YouTube: utils.YouTube }); guilding.setLogger(mockobjects.mockLogger);