0

I'm making a bot for the mattermost server of my company (like a discord bot), and for my bot commands, I created one file per command.

The idea is that to send a command, the user must send a message like "!giveChannelPerms arg1 arg 2 ...". The bot will parse the message to identify the command (in this case !giveChannelPerms) and execute the code related to the command.

The problem is that for each command, I have to require() the file and make an if {} else if {} else if {} ... to find the command, as you can see with the code below.

const giveChannelPerms = require('../cmd/giveChannelPerms');
const removeChannelPerms = require('../cmd/removeChannelPerms');

[...]

if (cmd == "!giveChannelPerms") {
    giveChannelPerms.giveChannelPerms(post, args, db, obj);
} else if (cmd == "!removeChannelPerms") {
    removeChannelPerms.removeChannelPerms(post, args, db, obj);
}

This code is good if we only have 2 commands for our bot, but the more commands I create, the more require() and if {} else if {} will be big.

Isn't there a more "optimized" way to do what I'm trying to do? I had thought of doing something like C function pointers but I have no idea how to do it.

antonyftp
  • 60
  • 10

4 Answers4

2

If you want less require and reduce if elses, I recommend you to create a file importing your commands and returning an associated map

const { giveChannelPerms } = require('../cmd/giveChannelPerms');
const { removeChannelPerms } = require('../cmd/removeChannelPerms');

const cmdMap = new Map();

cmdMap.set('!giveChannelPerms', giveChannelPerms)
cmdMap.set('!removeChannelPerms', removeChannelPerms)

export default cmdMap

Then you will be able to import it only once and use it without conditions in your file :

// Imported multiples functions in one require
const commands = require('../cmd/commands');

// Will execute function associated to cmd string without conditions
commands.get(cmd)(post, args, db, obj);
Mze
  • 197
  • 9
  • It didn't work because node told me that commands.get(...) is not a function. Also i put the entire json object into the map and not only the function, so i don't know how to acces the function :/ – antonyftp Sep 17 '20 at 14:51
  • 1
    Don't say it didn't work if you modified the example I gave. You should not place the entire object into the map value because you will need to call the object function which has a different name and that will result into non a non generic solution. Either you call "giveChannelPerms" and "removeChannelPerms" the same like "commandFunction" and it will be generic but I don't recommend it since it doesn't fit your problem, either you only add the fonction as value to the map as stated in the answer and you won't have to explicitly call object function. – Mze Sep 17 '20 at 15:06
  • Yes excuse me i haven't explicitly explain my problem. Instead of doing each require manually, i've decided to do a for each loop who's require all of my commands into the map. The fact is that it import all of the object into the map and not only the command. – antonyftp Sep 17 '20 at 15:45
  • If the exported function is the only one in your file I strongly recommend to export it as default, like this you wont have to deconstruct it ;) – Mze Sep 17 '20 at 16:20
1

Borrowing from https://stackoverflow.com/a/38397640/932256 try:

const commandFiles = {
    giveChannelPerms: require('../cmd/giveChannelPerms'),
    removeChannelPerms: require('../cmd/removeChannelPerms')
};

const commandList = Object.keys(commandFiles);

if (cmd.match(new RegExp('^!(' + commandList.join('|') + ')$'))) {
    let baseCommand = cmd.replace('!', '');
    commandFiles[baseCommand][baseCommand](post, args, db, obj);
}

Now all you need to do is add your commands to the commandFiles object.

nzn
  • 1,014
  • 11
  • 20
  • Good solution, I like the way you gather indexes, but using a Regexp on the fly with it is confusing thought. – Mze Sep 17 '20 at 13:28
  • 1
    @Mizibi - Thanks. I could have easily checked the command removing the "!" and checking against commandList by object index but on purpose I didn't because I don't know where it's coming from and just removing the ! and running could be risky as "doSomething" and "!doSomething" would call the same command and RegExp was the easiest way to verify. – nzn Sep 17 '20 at 13:38
0

Maybe something like this would work

[...]
require('../cmd/giveChannelPerms').then(res => { 
    if (res) {
        res.giveChannelPerms(post, args, db, obj);
    } else {
        require('../cmd/removeChannelPerms').then(rslt =>  {
             if (rslt) rslt.removeChannelPerms(post, args, db, obj);
        })
    }
});
  
StPaulis
  • 2,844
  • 1
  • 14
  • 24
  • That's better cause you won't add the second file if the first exists. But the code isn't pretty at all :/ – StPaulis Sep 17 '20 at 12:50
  • Yeah thanks but that didn't solve my problem because the more command i have, the more require() i need to do :/ – antonyftp Sep 17 '20 at 12:53
0

I think you are looking for below two solution, hope this helps

  1. More elegant way to manage your imports - For this you can create a index file (index.js) which will import all your commands from different file and export all commands from one place which is index.js
  2. Simplify your if else condition - from your code snippet, it seems like you need to evaluate against each command as the code then executed is different for each command. As there is no escape from, switch-case will provide you better code readability than if-else
const Cmds = require('../cmd/index');
// or you use import as below
//const {giveChannelPerms, removeChannelPerms} = require('../cmd/index')

switch(cmd)
case: '!giveChannelPerms'
   Cmds.giveChannelPerms.giveChannelPerms(post, args, db, obj);
case: '!removeChannelPerms' 
    Cmds.removeChannelPerms.removeChannelPerms(post, args, db, obj);
defualt:
     // do-somthing
}
vikash vik
  • 686
  • 5
  • 10
  • 1
    Thanks man ! But i always got the same problem because i need to do another case for each command. I found out that the best way to do it is to create a map who contain all of my command functions with my command name in key. That's what @Mizibi explained right above :) – antonyftp Sep 17 '20 at 13:28