Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[Script] Chat Command Handler

1373541626

Edited 1378444894
Alex L.
Pro
Sheet Author
So I have noticed a problem, each script that uses chat commands has its own handler. "What's the problem with that?" I hear you ask, lets think what would happen if I am running 10 different scripts all made by different people and two or more had the command !help, It wouldn't be good that's for sure. So to solve this problem I have created a well rounded fully functional chat command handler. <a href="https://gist.github.com/pigalot/5974253/c8bc959e0fa0358eda218ce5a5b69f72cc169aab" rel="nofollow">https://gist.github.com/pigalot/5974253/c8bc959e0fa0358eda218ce5a5b69f72cc169aab</a> It uses the commands formatted "!commandName [strings] [-OptionName values]". if the string has a space you can enclose it with quotes to keep it together. You can add commands by using "community.command.add" and passing it the command name and a object containing (* = required): minArgs - The minimum number of arguments required. maxArgs - The maximum number of arguments permitted. gmOnly - You can set this to anything it just has to be there, if it exists the command will be only usable by someone with "(GM)" in there name. typeList - This is an array of types for static arguments you want to receive. currently the only types are str (a string) and key (an option) key is the default. options - A object with valid options for your command ie: { "lol":"this is a discription", "rofl":"another discription"} would be !test -lol something -rofl "something else" handle* - a function with the profile of (args, who, isGM) args is a array of objects containing a value and if the argument was a option a key as well, ie "!test something -option value" would give [{ value:"something"},{key:"option", value:"value"}]. who is msg.who and isGM is a boolean. syntax - is a string that will be shown after the command name in the help it should show the basic usage of the command it will be auto generated if not set. Here is an example: on("ready", function() { &nbsp; obj = {}; &nbsp; obj.gmOnly = ""; &nbsp; obj.minArgs = 1; &nbsp; obj.typeList = ["str"]; &nbsp; obj.options = { "lol":"This has to do something :P" }; &nbsp; obj.handle = function(args) { &nbsp; log("test"); &nbsp; log(JSON.stringify(args)); &nbsp; }; &nbsp; community.command.add("test", obj); }); this would make the command !test, it requires one argument that will be a string and i can optionaly have as many -lol options as the user wants. When called it will log test and log its args in a JSON string. The add command returns a boolean indicating if the command has been added. Commands are automatic added to a help command (!?). Let me know what you think, I know this adds the problem of needing dependencies but without this sort of thing we will start having more and more problems as users start using more scripts.
This is very cool, Alex. &nbsp;Its like namespacing the scripting calls through the chat. &nbsp;Ill definately be using it.
1374570288
Alex L.
Pro
Sheet Author
Aaron W. said: This is very cool, Alex. &nbsp;Its like namespacing the scripting calls through the chat. &nbsp;Ill definately be using it. It will be far more useful once Riley gets the script repo in place, at the moment you have to instruct your users to install this on a earlier script tab, which isn't exactly user friendly
Alex, I'm using this script and I have a question. One of my commands uses the selected property of the chat:message. Unfortunately the&nbsp;Chat Command Handler script does not pass along the chat:message object. Are there any work arounds? Thanks.
I changed my copy of your script to pass along the selected tokens. Thank you for creating this script.
1378359233
Alex L.
Pro
Sheet Author
Davout said: I changed my copy of your script to pass along the selected tokens. Thank you for creating this script. Updated for you, now passes msg.selected to handler.
1378363824

Edited 1378364973
Lithl
Pro
Sheet Author
API Scripter
Alex L. said: Davout said: I changed my copy of your script to pass along the selected tokens. Thank you for creating this script. Updated for you, now passes msg.selected to handler. You should pass msg.playerid and msg.inlinerolls, as well. msg.inlinerolls only exists if the command included an inline roll, so you need to check for its existence. The other properties of the message object are irrelevant because you're only processing API commands. Also: the comparison msg.content.indexOf("!") !== -1 on line 253 is useless. You're already checking whether the message type is api; if that portion of the conditional is true, the indexOf!=-1 will always be true. (msg.type=api is only true if the message began with an exclamation mark, and msg.content includes said exclamation mark.) If the first conditional is false, the indexOf!=-1 will be short-circuited and never be evaluated. PS: msg.who.indexOf("(GM)") is not a very good way to check for isGM. Any player can edit their display name to include "(GM)". While that's an easy thing to see and police, there's also the fact that the actual GMs only have "(GM)" appended to their display name when entering chat commands while they have themselves selected in the "As" dropdown box (as opposed to when they're speaking as an NPC). Until the API is expanded to discriminate between GMs and players, the best way to check for GM status is to: Get the player object -- var player = getObj('player', msg.playerid); Get the player's user id -- var userid = player.get('d20userid'); Compare the user id to a validated set of GM user ids -- var isGM = false; _.each(list_of_gm_ids, function(id) { if(id == userid) isGM = true; }); The above requires entering one or more user ids when installing the script, which is another step to take, but I feel that's a small price to pay. (If the campaign only has 1 GM, you can simply check var isGM = (gmID == userid); ) You can find your own personal user id by visiting the wiki and clicking on your name in the left-hand sidebar (this takes you to your userpage); you'll see "Home » User:#" below your name on your userpage, where # is your user id. You can find any user's id by visiting their website profile page (click their name in the forum of campaign page) and checking the url; the path is "/users/#/ name ", where # is your user id and name is your display name.
1378364694
Alex L.
Pro
Sheet Author
Brian said: Alex L. said: Davout said: I changed my copy of your script to pass along the selected tokens. Thank you for creating this script. Updated for you, now passes msg.selected to handler. You should pass msg.playerid and msg.inlinerolls, as well. msg.inlinerolls only exists if the command included an inline roll, so you need to check for its existence. The other properties of the message object are irrelevant because you're only processing API commands. Also: the comparison msg.content.indexOf("!") !== -1 on line 253 is useless. You're already checking whether the message type is api; if that portion of the conditional is true, the indexOf!=-1 will always be true. (msg.type=api is only true if the message began with an exclamation mark, and msg.content includes said exclamation mark.) If the first conditional is false, the indexOf!=-1 will be short-circuited and never be evaluated. Ok I have just passed the roll20 msg through in place of selected, that way you can use what ever gets added without updates to the script. The indexOf will stay for now it has no real performance impact and it makes sure that if there is ever a change where you can use something other than ! as type API the script keeps working as intended.
1378365177

Edited 1378365231
Lithl
Pro
Sheet Author
API Scripter
Edited while you were replying; see the PS in my previous post. =) (Also: the indexOf check you're using only checks if the string contains an exclamation mark, not whether it starts with one.)
1378366305

Edited 1378366469
Alex L.
Pro
Sheet Author
Brian said: Edited while you were replying; see the PS in my previous post. =) (Also: the indexOf check you're using only checks if the string contains an exclamation mark, not whether it starts with one.) I know, its just a very quick check, it doesn't hurt anything and I has a reason for being there. As for your edit I check for GM the way I do because if you can't trust a player to not add (GM) to there name then you should not be playing with them, I would prefer not to require any editing of this script to make it work so I wont be doing it that way.
1378367528

Edited 1378367620
Lithl
Pro
Sheet Author
API Scripter
Alex L. said: I check for GM the way I do because if you can't trust a player to not add (GM) to there name then you should not be playing with them, I would prefer not to require any editing of this script to make it work so I wont be doing it that way. Indeed, that's easy to police (although, isn't this script supposed to be a generalization intended for wide use once we've got a script database, and in such case shouldn't it be as robust as possible?). However, it's not the only problem with checking the display name for the GM string as an isGM check. If I want to speak as one of my NPCs and then use an API command that requires being the GM, I have to switch over to speaking as myself, use the command, and then switch back to the NPC. Worse, msg.who will return my name, not the NPC's name, if it's an API command that cares. (For example, a command which sends a message back to the chat as whoever sent the message.)
1378369939

Edited 1378370043
Alex L.
Pro
Sheet Author
Brian said: Alex L. said: I check for GM the way I do because if you can't trust a player to not add (GM) to there name then you should not be playing with them, I would prefer not to require any editing of this script to make it work so I wont be doing it that way. Indeed, that's easy to police (although, isn't this script supposed to be a generalization intended for wide use once we've got a script database, and in such case shouldn't it be as robust as possible?). However, it's not the only problem with checking the display name for the GM string as an isGM check. If I want to speak as one of my NPCs and then use an API command that requires being the GM, I have to switch over to speaking as myself, use the command, and then switch back to the NPC. Worse, msg.who will return my name, not the NPC's name, if it's an API command that cares. (For example, a command which sends a message back to the chat as whoever sent the message.) Updated it to use msg.playerid to get the displayname to get round NPCs, it will also check community.command.gmList for an array of gm ids, in all cases it falls back to msg.who.
1378379606
Lithl
Pro
Sheet Author
API Scripter
Line 143: I don't see an object named "msg" in the scope, and you're using roll20Object like a message object elsewhere in the function. Is this a typo? Line 145: you have a space at the end of _d20userid. I don't know whether the get function trims the string or not, but just pointing it out. Assuming the above two notes are fixed and/or already function correctly, that try/catch block will never catch an error. This also means that the else block at line 161 is pulling "who" from the player.displayname. I could be wrong, but I don't believe player.get('displayname') returns the "(GM)" suffix; I think the GM suffix only shows up in msg.who, which would mean isGM is always false (barring a player editing his or her display name) at ln161.
1378392853
Alex L.
Pro
Sheet Author
Brian said: Line 143: I don't see an object named "msg" in the scope, and you're using roll20Object like a message object elsewhere in the function. Is this a typo? Line 145: you have a space at the end of _d20userid. I don't know whether the get function trims the string or not, but just pointing it out. Assuming the above two notes are fixed and/or already function correctly, that try/catch block will never catch an error. This also means that the else block at line 161 is pulling "who" from the player.displayname. I could be wrong, but I don't believe player.get('displayname') returns the "(GM)" suffix; I think the GM suffix only shows up in msg.who, which would mean isGM is always false (barring a player editing his or her display name) at ln161. Just typos, fixed now. The reason for the try/catch is I know one of the ids in msg can be an array under some conditions, although it may just be for the /w stuff.
Awesome! I'll check it out, thanks.
1378419435
Lithl
Pro
Sheet Author
API Scripter
Alex L. said: Brian said: Line 143: I don't see an object named "msg" in the scope, and you're using roll20Object like a message object elsewhere in the function. Is this a typo? Line 145: you have a space at the end of _d20userid. I don't know whether the get function trims the string or not, but just pointing it out. Assuming the above two notes are fixed and/or already function correctly, that try/catch block will never catch an error. This also means that the else block at line 161 is pulling "who" from the player.displayname. I could be wrong, but I don't believe player.get('displayname') returns the "(GM)" suffix; I think the GM suffix only shows up in msg.who, which would mean isGM is always false (barring a player editing his or her display name) at ln161. Just typos, fixed now. The reason for the try/catch is I know one of the ids in msg can be an array under some conditions, although it may just be for the /w stuff. msg.playerid will always be a single ID. msg.target can be a comma-delimited list if you whisper to a character with multiple controlling players, but msg.target doesn't exist if you're making an API call. It might be possible for your try/catch to find an error, if the player who sent the command somehow manages to quit the campaign in the time between entering the API command and the script calling getObj, but I'm not even entirely certain that will cause it to fail. I had a friend join my script testing campaign once a while back, and even though he has since quit the campaign, I can still give him control of tokens and players. (Most likely a bug, but still.)
1378419666
Alex L.
Pro
Sheet Author
Brian said: Alex L. said: Brian said: Line 143: I don't see an object named "msg" in the scope, and you're using roll20Object like a message object elsewhere in the function. Is this a typo? Line 145: you have a space at the end of _d20userid. I don't know whether the get function trims the string or not, but just pointing it out. Assuming the above two notes are fixed and/or already function correctly, that try/catch block will never catch an error. This also means that the else block at line 161 is pulling "who" from the player.displayname. I could be wrong, but I don't believe player.get('displayname') returns the "(GM)" suffix; I think the GM suffix only shows up in msg.who, which would mean isGM is always false (barring a player editing his or her display name) at ln161. Just typos, fixed now. The reason for the try/catch is I know one of the ids in msg can be an array under some conditions, although it may just be for the /w stuff. msg.playerid will always be a single ID. msg.target can be a comma-delimited list if you whisper to a character with multiple controlling players, but msg.target doesn't exist if you're making an API call. It might be possible for your try/catch to find an error, if the player who sent the command somehow manages to quit the campaign in the time between entering the API command and the script calling getObj, but I'm not even entirely certain that will cause it to fail. I had a friend join my script testing campaign once a while back, and even though he has since quit the campaign, I can still give him control of tokens and players. (Most likely a bug, but still.) I knew there was one that had that problem but i wasnt sure, its not like try creates any performance problems unless it catches something.
1378423350
Lithl
Pro
Sheet Author
API Scripter
The problem isn't with the try/catch directly, but I believe that player.displayname won't give you the GM suffix, so if community.command.gmList doesn't exist, isGM will always be false. If I'm wrong about player.displayname, of course, then there's no issue. If I'm right, then who needs to not be redefined in the try block.
1378444937
Alex L.
Pro
Sheet Author
Well then I have updated it back to how it was.