@ -5,23 +5,30 @@ const Discord = require("discord.js"),
guilding = require ( './lib/guilding' ) ,
guilding = require ( './lib/guilding' ) ,
utils = require ( './lib/utils' ) ,
utils = require ( './lib/utils' ) ,
config = require ( './config.json' ) ,
config = require ( './config.json' ) ,
client = new Discord . Client ( ) ,
args = require ( 'args-parser' ) ( process . argv ) ,
args = require ( 'args-parser' ) ( process . argv ) ,
sqlite3 = require ( 'sqlite3' ) ,
sqlite3 = require ( 'sqlite3' ) ,
authToken = args . token || config . api . botToken ,
authToken = args . token || config . api . botToken ,
prefix = args . prefix || config . prefix || '~' ,
prefix = args . prefix || config . prefix || '~' ,
gamepresence = args . game || config . presence ;
gamepresence = args . game || config . presence ;
let presences = [ ] , // loaded from presences.txt file if the file exists
class Bot {
rotator = null , // an interval id to stop presence duration if needed
constructor ( ) {
maindb = null ;
this . client = new Discord . Client ( ) ;
this . mention = false ;
this . rotator = null ;
this . maindb = null ;
this . presences = [ ] ;
this . guildHandlers = [ ] ;
function main ( ) {
logger . verbose ( 'Registering cleanup function' ) ;
logger . verbose ( 'Registering cleanup function' ) ;
utils . Cleanup ( ( ) => {
utils . Cleanup ( ( ) => {
guilding . destroyAll ( ) ;
for ( let gh in Object . values ( this . guildHandlers ) ) {
client . destroy ( ) ;
if ( gh )
gh . destroy ( ) ;
}
this . client . destroy ( ) . then ( ( ) => {
logger . debug ( 'destroyed client' ) ;
} ) ;
} ) ;
} ) ;
cmd . setLogger ( logger ) ;
cmd . setLogger ( logger ) ;
logger . verbose ( 'Verifying config' ) ;
logger . verbose ( 'Verifying config' ) ;
@ -38,16 +45,15 @@ function main() {
guilding . setLogger ( logger ) ;
guilding . setLogger ( logger ) ;
cmd . init ( prefix ) ;
cmd . init ( prefix ) ;
logger . verbose ( 'Registering commands' ) ;
logger . verbose ( 'Registering commands' ) ;
registerCommands ( ) ;
this . registerCommands ( ) ;
logger . debug ( 'Checking for ./data/ existence' ) ;
logger . debug ( 'Checking for ./data/ existence' )
utils . dirExistence ( './data' , ( ) => {
utils . dirExistence ( './data' , ( ) => {
logger . verbose ( 'Connecting to main database' ) ;
logger . verbose ( 'Connecting to main database' ) ;
maindb = new sqlite3 . Database ( './data/main.db' , ( err ) => {
this . maindb = new sqlite3 . Database ( './data/main.db' , ( err ) => {
if ( err ) {
if ( err ) {
logger . error ( err . message ) ;
logger . error ( err . message ) ;
} else {
} else {
maindb . run ( ` ${ utils . sql . tableExistCreate } presences (
this . maindb . run ( ` ${ utils . sql . tableExistCreate } presences (
$ { utils . sql . pkIdSerial } ,
$ { utils . sql . pkIdSerial } ,
text VARCHAR ( 255 ) UNIQUE NOT NULL
text VARCHAR ( 255 ) UNIQUE NOT NULL
) ` , (err) => {
) ` , (err) => {
@ -55,72 +61,79 @@ function main() {
logger . error ( err . message ) ;
logger . error ( err . message ) ;
} else {
} else {
logger . debug ( 'Loading presences' ) ;
logger . debug ( 'Loading presences' ) ;
loadPresences ( ) ;
this . loadPresences ( ) ;
}
}
} ) ;
} ) ;
}
}
} ) ;
} ) ;
} ) ;
} ) ;
registerCallbacks ( ) ;
this . registerCallbacks ( ) ;
}
client . login ( authToken ) . then ( ( ) => {
start ( ) {
return new Promise ( ( resolve , reject ) => {
this . client . login ( authToken ) . then ( ( ) => {
logger . debug ( "Logged in" ) ;
logger . debug ( "Logged in" ) ;
resolve ( ) ;
} ) . catch ( ( err ) => {
reject ( err ) ;
} ) ;
} ) ;
}
} )
}
/ * *
/ * *
* If a data / presences . txt exists , it is read and each line is put into the presences array .
* 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 .
* 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
* 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
* 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 .
* a rotator is created that rotates the presence every configured duration .
* /
* /
function loadPresences ( ) {
loadPresences ( ) {
if ( fs . existsSync ( './data/presences.txt' ) ) {
if ( fs . existsSync ( './data/presences.txt' ) ) {
let lineReader = require ( 'readline' ) . createInterface ( {
let lineReader = require ( 'readline' ) . createInterface ( {
input : require ( 'fs' ) . createReadStream ( './data/presences.txt' )
input : require ( 'fs' ) . createReadStream ( './data/presences.txt' )
} ) ;
} ) ;
lineReader . on ( 'line' , ( line ) => {
lineReader . on ( 'line' , ( line ) => {
maindb . run ( 'INSERT INTO presences (text) VALUES (?)' , [ line ] , ( err ) => {
this . maindb . run ( 'INSERT INTO presences (text) VALUES (?)' , [ line ] , ( err ) => {
if ( err ) {
if ( err ) {
logger . warn ( err . message ) ;
logger . warn ( err . message ) ;
}
}
} ) ;
} ) ;
presences . push ( line ) ;
this . presences . push ( line ) ;
} ) ;
} ) ;
rotator = client . setInterval ( ( ) => rotatePresence ( ) , config . presence _duration || 360000 ) ;
this . rotator = this . client . setInterval ( ( ) => this . rotatePresence ( ) , config . presence _duration || 360000 ) ;
fs . unlink ( './data/presences.txt' , ( err ) => {
fs . unlink ( './data/presences.txt' , ( err ) => {
if ( err )
if ( err )
logger . warn ( err . message ) ;
logger . warn ( err . message ) ;
} ) ;
} ) ;
maindb . all ( 'SELECT text FROM presences' , ( err , rows ) => {
this . maindb . all ( 'SELECT text FROM presences' , ( err , rows ) => {
if ( err ) {
if ( err ) {
logger . warn ( err . message ) ;
logger . warn ( err . message ) ;
} else {
} else {
for ( let row of rows ) {
for ( let row of rows ) {
if ( ! row [ 0 ] in presences )
if ( ! row [ 0 ] in this . presences )
presences . push ( row . text ) ;
this . presences . push ( row . text ) ;
}
}
}
}
} )
} )
} else {
} else {
maindb . all ( 'SELECT text FROM presences' , ( err , rows ) => {
this . maindb . all ( 'SELECT text FROM presences' , ( err , rows ) => {
if ( err ) {
if ( err ) {
logger . warn ( err . message ) ;
logger . warn ( err . message ) ;
} else {
} else {
for ( let row of rows ) {
for ( let row of rows ) {
presences . push ( row . text ) ;
this . presences . push ( row . text ) ;
}
}
}
}
rotator = client . setInterval ( ( ) => rotatePresence ( ) , config . presence _duration || 360000 ) ;
this . rotator = this . client . setInterval ( ( ) => this . rotatePresence ( ) , config . presence _duration || 360000 ) ;
} )
} )
}
}
}
}
/ * *
/ * *
* registeres global commands
* registeres global commands
* /
* /
function registerCommands ( ) {
registerCommands ( ) {
// useless test command
// useless test command
cmd . createGlobalCommand ( prefix + 'repeatafterme' , ( msg , argv , args ) => {
cmd . createGlobalCommand ( prefix + 'repeatafterme' , ( msg , argv , args ) => {
return args . join ( ' ' ) ;
return args . join ( ' ' ) ;
@ -129,9 +142,9 @@ function registerCommands() {
// adds a presence that will be saved in the presence file and added to the rotation
// adds a presence that will be saved in the presence file and added to the rotation
cmd . createGlobalCommand ( prefix + 'addpresence' , ( msg , argv , args ) => {
cmd . createGlobalCommand ( prefix + 'addpresence' , ( msg , argv , args ) => {
let p = args . join ( ' ' ) ;
let p = args . join ( ' ' ) ;
presences . push ( p ) ;
this . presences . push ( p ) ;
maindb . run ( 'INSERT INTO presences (text) VALUES (?)' , [ p ] , ( err ) => {
this . maindb . run ( 'INSERT INTO presences (text) VALUES (?)' , [ p ] , ( err ) => {
if ( err )
if ( err )
logger . warn ( err . message ) ;
logger . warn ( err . message ) ;
} ) ;
} ) ;
@ -144,7 +157,7 @@ function registerCommands() {
msg . reply ( 'Shutting down...' ) . finally ( ( ) => {
msg . reply ( 'Shutting down...' ) . finally ( ( ) => {
logger . debug ( 'Destroying client...' ) ;
logger . debug ( 'Destroying client...' ) ;
client . destroy ( ) . finally ( ( ) => {
this . client . destroy ( ) . finally ( ( ) => {
logger . debug ( ` Exiting Process... ` ) ;
logger . debug ( ` Exiting Process... ` ) ;
process . exit ( 0 ) ;
process . exit ( 0 ) ;
} ) ;
} ) ;
@ -154,9 +167,9 @@ function registerCommands() {
// forces a presence rotation
// forces a presence rotation
cmd . createGlobalCommand ( prefix + 'rotate' , ( ) => {
cmd . createGlobalCommand ( prefix + 'rotate' , ( ) => {
try {
try {
client . clearInterval ( rotator ) ;
this . client . clearInterval ( this . rotator ) ;
rotatePresence ( ) ;
this . rotatePresence ( ) ;
rotator = client . setInterval ( ( ) => rotatePresence ( ) , config . presence _duration ) ;
this . rotator = this . client . setInterval ( ( ) => this . rotatePresence ( ) , config . presence _duration ) ;
} catch ( error ) {
} catch ( error ) {
logger . warn ( JSON . stringify ( error ) ) ;
logger . warn ( JSON . stringify ( error ) ) ;
}
}
@ -164,12 +177,12 @@ function registerCommands() {
// ping command that returns the ping attribute of the client
// ping command that returns the ping attribute of the client
cmd . createGlobalCommand ( prefix + 'ping' , ( ) => {
cmd . createGlobalCommand ( prefix + 'ping' , ( ) => {
return ` Current average ping: \` ${ client . ping } ms \` ` ;
return ` Current average ping: \` ${ this . client . ping } ms \` ` ;
} , [ ] , 'Returns the current average ping' , 'owner' ) ;
} , [ ] , 'Returns the current average ping' , 'owner' ) ;
// returns the time the bot is running
// returns the time the bot is running
cmd . createGlobalCommand ( prefix + 'uptime' , ( ) => {
cmd . createGlobalCommand ( prefix + 'uptime' , ( ) => {
let uptime = utils . getSplitDuration ( client . uptime ) ;
let uptime = utils . getSplitDuration ( this . client . uptime ) ;
return new Discord . RichEmbed ( ) . setDescription ( `
return new Discord . RichEmbed ( ) . setDescription ( `
* * $ { uptime . days } * * days
* * $ { uptime . days } * * days
* * $ { uptime . hours } * * hours
* * $ { uptime . hours } * * hours
@ -181,76 +194,98 @@ function registerCommands() {
// returns the numbe of guilds, the bot has joined
// returns the numbe of guilds, the bot has joined
cmd . createGlobalCommand ( prefix + 'guilds' , ( ) => {
cmd . createGlobalCommand ( prefix + 'guilds' , ( ) => {
return ` Number of guilds: \` ${ client . guilds . size } \` `
return ` Number of guilds: \` ${ this . client . guilds . size } \` `
} , [ ] , 'Returns the number of guilds the bot has joined' , 'owner' ) ;
} , [ ] , 'Returns the number of guilds the bot has joined' , 'owner' ) ;
}
}
/ * *
/ * *
* changes the presence of the bot by using one stored in the presences array
* changes the presence of the bot by using one stored in the presences array
* /
* /
function rotatePresence ( ) {
rotatePresence ( ) {
let pr = presences . shift ( ) ;
let pr = this . presences . shift ( ) ;
presences . push ( pr ) ;
this . presences . push ( pr ) ;
client . user . setPresence ( { game : { name : ` ${ gamepresence } | ${ pr } ` , type : "PLAYING" } , status : 'online' } ) ;
this . client . user . setPresence ( { game : { name : ` ${ gamepresence } | ${ pr } ` , type : "PLAYING" } , status : 'online' } ) ;
logger . debug ( ` Presence rotation to ${ pr } ` ) ;
logger . debug ( ` Presence rotation to ${ pr } ` ) ;
}
/ * *
* Sends the answer recieved from the commands callback .
* Handles the sending differently depending on the type of the callback return
* @ param msg
* @ param answer
* /
function answerMessage ( msg , answer ) {
if ( answer instanceof Promise || answer ) {
if ( answer instanceof Discord . RichEmbed ) {
( this . mention ) ? msg . reply ( '' , answer ) : msg . channel . send ( '' , answer ) ;
} else if ( answer instanceof Promise ) {
answer
. then ( ( answer ) => answerMessage ( msg , answer ) )
. catch ( ( error ) => answerMessage ( msg , error ) ) ;
} else {
( this . mention ) ? msg . reply ( answer ) : msg . channel . send ( answer ) ;
}
} else {
logger . warn ( ` Empty answer won't be send. ` ) ;
}
}
}
/ * *
/ * *
* Registeres callbacks for client events
* Registeres callbacks for client events
* /
* /
function registerCallbacks ( ) {
registerCallbacks ( ) {
client . on ( 'error' , ( err ) => {
this . client . on ( 'error' , ( err ) => {
logger . error ( err . message ) ;
logger . error ( err . message ) ;
} ) ;
} ) ;
client . on ( 'ready' , ( ) => {
this . client . on ( 'ready' , ( ) => {
logger . info ( ` logged in as ${ client . user . tag } ! ` ) ;
logger . info ( ` logged in as ${ this . client . user . tag } ! ` ) ;
client . user . setPresence ( { game : { name : gamepresence , type : "PLAYING" } , status : 'online' } ) ;
this . client . user . setPresence ( { game : { name : gamepresence , type : "PLAYING" } , status : 'online' } )
. catch ( ( err ) => {
if ( err )
logger . warn ( err . message ) ;
} ) ;
} ) ;
} ) ;
client . on ( 'message' , msg => {
this . client . on ( 'message' , msg => {
try {
try {
if ( msg . author === client . user ) {
if ( msg . author === this . client . user ) {
logger . verbose ( ` ME: ${ msg . content } ` ) ;
logger . verbose ( ` ME: ${ msg . content } ` ) ;
return ;
return ;
}
}
logger . verbose ( ` < ${ msg . author . tag } >: ${ msg . content } ` ) ;
logger . verbose ( ` < ${ msg . author . tag } >: ${ msg . content } ` ) ;
if ( ! msg . guild ) {
if ( ! msg . guild ) {
let reply = cmd . parseMessage ( msg ) ;
let reply = cmd . parseMessage ( msg ) ;
answerMessage ( msg , reply ) ;
this . answerMessage ( msg , reply ) ;
} else {
} else {
guilding . get Handler( msg . guild , prefix ) . handleMessage ( msg ) ;
this . getGuild Handler( msg . guild , prefix ) . handleMessage ( msg ) ;
}
}
} catch ( err ) {
} catch ( err ) {
logger . error ( err . stack ) ;
logger . error ( err . stack ) ;
}
}
} ) ;
} ) ;
}
/ * *
* Sends the answer recieved from the commands callback .
* Handles the sending differently depending on the type of the callback return
* @ param msg
* @ param answer
* /
answerMessage ( msg , answer ) {
if ( answer instanceof Promise || answer ) {
if ( answer instanceof Discord . RichEmbed ) {
( this . mention ) ? msg . reply ( '' , answer ) : msg . channel . send ( '' , answer ) ;
} else if ( answer instanceof Promise ) {
answer
. then ( ( answer ) => answerMessage ( msg , answer ) )
. catch ( ( error ) => answerMessage ( msg , error ) ) ;
} else {
( this . mention ) ? msg . reply ( answer ) : msg . channel . send ( answer ) ;
}
} else {
logger . warn ( ` Empty answer won't be send. ` ) ;
}
}
/ * *
* Returns the guild handler by id , creates one if it doesn ' t exist and returns it then
* @ param guild
* @ param prefix
* @ returns { * }
* /
getGuildHandler ( guild , prefix ) {
if ( ! this . guildHandlers [ guild . id ] )
this . guildHandlers [ guild . id ] = new guilding . GuildHandler ( guild , prefix ) ;
return this . guildHandlers [ guild . id ] ;
} ;
}
}
// Executing the main function
// Executing the main function
if ( typeof require !== 'undefined' && require . main === module ) {
if ( typeof require !== 'undefined' && require . main === module ) {
logger . info ( "Starting up... " ) ; // log the current date so that the logfile is better to read.
logger . info ( "Starting up... " ) ; // log the current date so that the logfile is better to read.
main ( ) ;
let discordBot = new Bot ( ) ;
discordBot . start ( ) . catch ( ( err ) => {
logger . error ( err . message ) ;
} ) ;
}
}