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

[suggestion] a list of currently running scripts in chat, via an api command with permissions... I think...

Just an idea, and not entirely necessary, but would someone *cough-TheAaron* like to write a nice compact little script that outputted the scripts currently running in a game (and version, if applicable) to chat? I know it would require adding a line to each script that doesn't already add itself (or that the script itself doesn't look for, if it becomes complex enough to search for other scripts itself) but that's not particularly difficult. I currently add logs outputting to the API for every script tab for testing scripts, so that i can find the active ones easily enough, or figure out which are disabled without having to tab between them all, but to be able to do so from within a game would make things a whole lot more streamlined when looking for updates in the forums without having to load up your api, and so that players can get an idea of what scripts are running too, at least the ones you want them to know are running (Might I suggest tying it to something like, oh, i don't know, a script like IsGM, so that GM's can print out a full list, and players can get a condensed list of scripts running, via some sort of permissions based tag for each script?). I ask because I run several different games, in different rulesets, all with different scripts though more and more lately that is becoming different scripts written or edited by TheAaron; I only currently use three scripts from anyone else, as Aarons have proven robust in most cases and the degree of support is amazing to be quite frank about it. All Hail TheAaron. Sorry, got distracted. Where was I? Oh, thats right. A list of scripts currently running in the game chat, as that would make things much easier to check at the start of a game, and to remind yourself what options you have available while in game without having to open the api or tab out and miss vital player interaction while you're fluffing about in another tab. I also really need to learn to construct shorter sentences...
1414034817

Edited 1414035124
I can see a use for a standardized chat command system, in which a script registers itself (providing a basic command, maybe some basic help output, and a callback function) and only the harness script actually registers a "chat:message" handler. Then the harness could provide some overall help functionality, handle argument parsing so scripts just get an argv array and don't have to reinvent the wheel, and marshal commands as necessary. I'd be willing to throw something together for that. Maybe something along these lines (fellow script writers, please make suggestions; it's only going to be useful if it's used, so it'd be nice if it was something people liked enough to use): function registerChatCommand(command, signature, description, callback): Register "!"+command as a chat command (should be resilient enough to avoid prepending "!" if it's already there). Verify command not already registered. Error if any parameters different; ignore if all parameters same. General help output for this command will be signature+"\n\t"+description. function callback(argv, message): Called whenever command issued in chat. argv is array of arguments (handled like shell commands, so "!foo 'bar baz'" => ["!foo", "bar baz"]) message is copy of "chat:message" callback parameter, to give access to .who, etc. function unregisterChatCommand(command): Remove specified command. Error (ignore?) if command not registered. In addition to marshalling registered commands to the relevant callbacks, the "chat:message" handler would provide a "!help" command to list registered commands and their descriptions (or maybe "!help" to just list signatures, and "!Help" to list signatures and descriptions).
1414036738

Edited 1414038004
Lithl
Pro
Sheet Author
API Scripter
There is no means to automatically detect what script tabs are enabled, because all the scripts get combined together into one on the tabletop. However, it would be trivial enough to add a bit of information to an object in each tab, and iterate through that object to see what's present. Something like this should work: // Somewhere on each script tab var AllScripts = AllScripts ? (AllScripts['My Script Name'] = true, AllScripts) : { 'My Script Name': true }; // Optional: replace `true` with an object or array containing relevant information, such as version number // In its own script tab var AllScripts = AllScripts ? (AllScripts['Scripts Reporter'] = { version: 1, author: 'Brian' }, AllScripts) : { 'Scripts Reporter': { version: 1, author: 'Brian' } }; var AllScripts_OPTIONS = AllScripts_OPTIONS || {}; // Change this to false if you want to enable !schowscripts for all players AllScripts_OPTIONS.GM_ONLY = true; on('ready', function() { function logObjectKeys(obj, indent, options) { _.each(obj, function(value, key) { var logMessage = (options ? (options.whisper || '') : indent) + key; if (value) { if (!(_.isObject(value) || _.isArray(value) || _.isBoolean(value))) { // If `value` is neither an object, an array, nor `true`, add its value to the message logMessage += ' :: ' + value; } if (options && options.sendChat) { sendChat('SYSTEM', logMessage); } else { log(logMessage); } // Drill into objects/arrays. Note that `key` will be 0, 1, 2, etc. for an array if (_.isObject(value) || _.isArray(value)) { logObjectKeys(value, indent + (options && options.sendChat ? '>' : ' '), options); } } } } log('Scripts Reporter :: All enabled scripts at on:ready'); logObjectKeys(AllScripts, ' '); on('chat:message', function(msg) { var parts, command, index, title = 'Scripts Reporter :: All enabled scripts at ' + msg.content, indent = '>', options = {}; if (msg.type != 'api') return; parts = msg.content.split(' '); command = parts.shift().substring(1).toLowerCase(); if (command !== 'showscripts') return; if (AllScripts_OPTIONS.GM_ONLY && isGM && !isGM(msg.playerid)) return; if (parts.length === 0) { indent = ' '; } else { options.sendChat = true; } index = _.indexOf(parts, '-w'); if (index >= 0 && index < parts.length - 1) { options.whisper = '/w ' + parts[index + 1]; } if (options.sendChat) { sendChat('SYSTEM', (options.whisper || '') + title); } else { log(title); } logObjectKeys(AllScripts, indent, options); }); }); (Warning: Code is untested) This would log all enabled scripts (in the API log) when the sandbox spins up, and make a !showscripts command available. It you pass the command zero parameters, the scripts will be listed once again in the API log. If you pass any parameters, they'll be sent to the chat, and if you send -w name as parameters, the scripts will be whispered to name only. When listing scripts, you'll see the name of each script which has been added to AllScripts . If you put any information into the object associated with that script (eg, supply an object with a version number and an author's name), those will be output as well (and if those have additional objects within them, those will be listed, all the way down to the turtles). If the scripts are being output to the API console, each nested level of information will be indented further using spaces (listing things in the chat window will indent with the '>' character). If you're running Aaron's isGM authentication script and you leave the GM_ONLY option set as true, only the GM can make use of !showscripts . If isGM isn't present or you set GM_ONLY to false, anyone may use the command.
1414043356
The Aaron
Roll20 Production Team
API Scripter
This post gives me warm fuzzies, thanks! I've actually given quite a bit of thought to this topic, including writing some code in this direction, but haven't yet finished anything. (Too many other things to write!!) I started a script like Manveti suggests (called Arguments), but haven't progressed much beyond the basic framework. I was planning to make it my command line parsing system and help system, as much of my scripts repeats those things. The problem I ran into is that even in my own scripts there is a bunch of variation in the way I pass arguments. (And being the over-engineerer that I am, I want to support arbitrary schemas!). I still plan to work on this (argument parsing/configuration management/help display are most of what is holding up the new ferdions of my turn marker script and calendar script), and just need to find time. I've also thought about the instrumenting of scripts in the API panel like Brian is talking about. I wrote a bookmarklet that would insert log messages at the top of each script with it's name so I can see what order they start in. With the start order and length of all scripts, it is possible to calculste where most errors occur, which is really handy when developing a script. I think its a good thing to discuss, I'm always interested in API improvements!
This morning I realized that it's probably better to package this in an object rather than as global functions, especially as additional functionality (e.g. optparse and/or getopt functionality) is added. I'm thinking "Shell" would be a reasonable name, and the function names could be shortened a bit: Shell.registerCommand and Shell.unregisterCommand.
So I threw something together during some downtime at work today. I didn't have much time, so right now it's just arg parsing and command marshaling, but it's a start: <a href="https://github.com/manveti/roll20/blob/master/shell.js" rel="nofollow">https://github.com/manveti/roll20/blob/master/shell.js</a>
1414163538
The Aaron
Roll20 Production Team
API Scripter
Looks like your tokenize() function is breaking up the msg.content based on whitespace, but preserving strings in quotes? Try this... var tokens=_.map(msg.content.match(/\'([^']+)'|\"([^"]+)"|[^\s]+|([^\s]+)/g), function(t) { return t.replace(/'([^']+(?='))'/g,'$1').replace(/"([^"]+(?="))"/g,'$1'); }); =D This is not a bad start. Personally, I'd make this more of the Module pattern (coming from c++, I'm really into encapsulation... =D ) I'm looking forward to seeing what this develops into.
That's pretty slick, but there are a couple additional POSIX-shell-like behaviors that I wanted to include that aren't easily covered with a one-line solution: 1) Catching dangling quotes ('foo "bar baz' should yield an error, not ['foo', '"bar', 'baz']). This helps catch bad input (e.g. the user probably wanted ['foo', 'bar baz']). 2) Merging adjacent quoted sections ('foo"bar baz"' should yield ['foobar baz'], not ['foo"bar', 'baz"']). This allows us to handle arbitrary quotes without having to implement backslash-escaping. But I do grant that the desired end could probably be achieved in a simpler fashion.
1414184337
The Aaron
Roll20 Production Team
API Scripter
I suppose that goes to requirements, but is a fair point. I think it gets into diminishing returns though I agree that I'd rather support more functionality than less. I'm not sure I buy the adjacent quotes argument though. =D I'd probably ignore 2) and implement 1) with a validation step, possibly looking for unbalanced quotes specifically, or just validating that there are the right number of parameters (as unbalanced quotes would lead to too many, in theory).
So I think it's in a pretty reasonable state at this point (certainly usable). It's got command registration and listing, basic access control, and some simple output functions (albeit without documentation). It's pretty simple to retrofit a script to register its command instead of handling it itself: If the script calls off to a named chat message handler, replace the on("chat:message", handler) call with Shell.registerCommand("!somecommand", "!somecommand [some args]", "description of command", function(tokens, msg){ handler(msg); }); (where "!somecommand" is the command the script implements; call registerCommand once for each command handled by handler). If the script uses an anonymous message handler, replace on("chat:message", function(msg){ ... }) with Shell.registerCommand("!somecommand", "!somecommand [some args]", "description of command", function(tokens, msg){ ... }) In either case, the default will be for commands to be GM-only. If you want to make the commands world-accessible, you can call Shell.permissionCommand(["!shell-permission", "add", "!somecommand"], {"who": "GM"}) after registering the command or execute "!shell-permission add !somecommand" in the chat. I've updated some of my scripts to take advantage of the module when it's present, which you can use as examples: Initiative Tracker: <a href="https://github.com/manveti/roll20/blob/master/trac" rel="nofollow">https://github.com/manveti/roll20/blob/master/trac</a>... Dungeon Generator: <a href="https://github.com/manveti/dungen/blob/master/dun" rel="nofollow">https://github.com/manveti/dungen/blob/master/dun</a>... (in both of the above scripts, most of the relevant stuff is in the last 20ish lines of the respective file)
1414547989
The Aaron
Roll20 Production Team
API Scripter
Interesting. I saw your post on the dungeon generator. I'll have to have a look at some point.