The small but powerful Discord bot

This little guy has been occupying me for quite some time now.

The purpose of ISAC is to provide a chat bot for a Discord Server. That was his main focus point until a little while ago where I modified him (he is my creation) to support multiple servers. Initially ISAC was programmed to avoid having one 2000 line long file, so he was split over multiple files such as numerous command files based upon their type and a few config files.

module.exports = {  
    command: function(e, client, message, msgarray, user, channel, guild, mods, functions, config, currentlyPlaying, callback) {
        switch(msgarray[0]) {
            case config['prefix'] + 'play':
                if(!currentlyPlaying) {
                    if(client.VoiceConnections.length == 0)
                        return functions.SendMessage(channel, ':thumbsdown: `Not connected to a voice channel`');
                    var info = client.VoiceConnections.getForGuild(guild);
          , function() {

That's the basis of all the command files. A command function within it which then depending on the command that was executed by the user, it'll sort through the switch statement and execute the proper code block.
After the command function, there are other more common functions available for future use if more than one command had to execute the common code, that code would then be it's own function.

ISAC v3.0 started the transition to using databases to start holding mass amounts of data, such as user IDs, when a user joined the guild, when a user created their account, user nicknames and when a user last sent a message and to which channel that was. The beginning of this involved many crashes and quick fixes on my end to get ISAC back online since 10000+ users rely'd on him to be working for commands and information.

ISAC v4.0 was the beginning of continuing the move to using databases and force ISAC to be multi-server supportive. So instead of crashing every time a user sent a message from a guild he was in other than the one he was setup for, the message simply does nothing unless that message contains some sort of logic that ISAC can see and reply with appropriately.

ISAC is currently 5000 rows strong. Storing over 2700 users, 300 different nicknames and last seen data of 2400+ users.

A small hickup

During v4 of ISAC, I hit a snag. Since I was storing all the commands in a different format (see below), there was an issue going about the method of executing them since I ditched the command function method.

module.exports = {  
    eval: (data, db, functions, config, client) => {
        if( == '121094532598071300') {
            var message = data.message.content.substring(data.message_array[0].length + 1, data.message.content.length);
            try {':ok_hand:\n```{0}```'.format(eval('require("util").inspect(' + message + ')'))); }
            catch(error) {':exclamation:\n ```{0}```'.format(error)); }
    //Remove the last X messages
    purge: (data, functions, config, client) => {
        if(functions.Allowed(config.guilds[], data.member, data.guild)) {
            if(data.message_array.length == 2 && functions.IsInt(data.message_array[1])) {...

Then I did a lot of research into executing a function if you have the name of the function as a string, turns out that's quite easy. But the real problem was that each function didn't have the same amount of arguments, I could have added every argument needed for each one and called it a day but that was bad practise in my opinion.

That is until the GOD of JavaScript programming ( revealed the .apply() method for functions. This magical method allowed me to easily apply arguments to the function.
But the issue now was getting the arguments of the function, turns out a simple node plugin was the fix get-parameter-names. Using all these ideas together the code below was created and works flawlessly!

if(data.message.content.charAt(0) == '!') {  
    try {
        var params = GetParamNames(config.guilds['137719638556540929'].commands[data.message_array[0].substring(1)]);
        var args = [];
        for(var param in params) args.push(eval(params[param]));
            config.guilds['137719638556540929'].commands[data.message_array[0].substring(1)].apply(null, args);
    catch(error) { if(error != "TypeError: Cannot read property 'toString' of undefined") console.log(error); }

The Try Catch check is there because custom commands are in the database rather than in the commands file, and the error because of the above issue results in... TypeError: Cannot read property 'toString' of undefined. Probably not a good idea to do this, but I'm working on replacing it with a better check (one that actually doesn't need a catch).

The history of ISAC, faster...

ISAC was originally a modular bot which had all of it's commands and config stored in files which were then loaded by the main process. This was done for a more easier programmer experience since certain functions were spread over multiple files which grouped common ones together.

Since then, ISAC has moved to a more database oriented bot. This allows for much easier editing and debugging as all it takes to tweak it is a row change in a table, then contacting the database again for the newly updated data. Before hand it would have required a file change and restart of the bot.
ISAC has gotten an update recently (November 1st), which allowed him to handle multiple guilds in a more safe manor, one which that doesn't crash every time a new message is sent from a guild other than The Division. All ISAC requires for an additional guild is a new row in a table, two files (config & commands), and optionally a logic file which will contain some extra logic aside from chat commands.

Tom Lynn

Read more posts by this author.