Created
December 1, 2015 04:15
-
-
Save mattkauffman23/23eba36fb310da259a20 to your computer and use it in GitHub Desktop.
Roll 20 API script for inspecting tokens
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
'use strict'; | |
var help = '!inspect - Manage player visible descriptions of tokens. ' + | |
'Requires CommandShell. See script for more options and useful macros.'; | |
/* | |
Requires the following other scripts to be installed: | |
- CommandShell (https://wiki.roll20.net/Script:Command_Shell) | |
Useful Macros: | |
Players | |
inspect = !inspect @{target|token_id} | |
GMs | |
inspect-add = !inspect @{selected|token_id} add "?{Description}" | |
inspect-remove = !inspect @{selected|token_id} remove | |
*/ | |
// ---CONFIG START --- | |
// Chat sender for responses. | |
var sender = "Inspector"; | |
// Custom CSS for chat message. | |
var styles = ''; | |
// Response when a description hasn't been set up. | |
var defaultDescription = 'Nothing appears out of the ordinary.'; | |
// ---CONFIG END --- | |
// Module Setup | |
var name = '!inspect'; | |
var moduleState = state[name] = state[name] || {}; | |
// Register the command and associated event handlers | |
on('ready', function() { | |
// Validate dependencies | |
if (!Shell) { | |
throw new Error(formatOutput('Missing dependency. CommandShell is required: https://wiki.roll20.net/Script:Command_Shell')); | |
} | |
// Register with CommandShell | |
Shell.registerCommand(name, name + ' <tokenId> [-b | --broadcast]\n' + | |
name + ' <tokenId> add <description>\n' + | |
name + ' <tokenId> remove', | |
help, processCommand); | |
// Set command permissions to be globally accesible. | |
// Add, update and delete actions are restricted to GMs internally. | |
Shell.write('!shell-permission add ' + name); | |
// Clean up descriptions if associated token is destroyed. | |
on('destroy:graphic', function(obj) { | |
moduleState = delete moduleState[obj.id]; | |
}); | |
}); | |
// Interpret and validate command, execute and respond | |
function processCommand(input, msg) { | |
if (input.length < 1) { | |
Shell.write( | |
formatOutput('Command usage incorrect. Please check !help.'), | |
msg.who, styles, sender); | |
} | |
// HACK: Trim leading - from id to not trip up args parser | |
input[1] = input[1].replace('-', ''); | |
// Preprocess input | |
var argv = parseArgs(input.slice(1)); | |
var arity = argv._.length; | |
var tokenId = '-' + argv._[0]; | |
var to = msg.who; | |
var isGM = playerIsGM(msg.playerid); | |
var output = ''; | |
// Interpret command | |
if (!getObj('graphic', tokenId)) { // validate tokenId | |
output = formatOutput('Could not find token with id ' + tokenId); | |
} else if (arity === 1) { // get | |
output = get(tokenId); | |
to = argv.b || argv.broadcast ? undefined : to; | |
} else if (arity === 2 && argv._[1] === 'remove') { // remove | |
output = isGM ? remove(tokenId) : | |
formatOutput('Only GMs can remove descriptions.'); | |
} else if (arity === 3 && argv._[1] === 'add') { // add | |
output = isGM ? add(tokenId, argv._[2]) : | |
formatOutput('Only GMs can add descriptions.'); | |
} else { // error | |
output = formatOutput('Command usage incorrect. Please check !help.'); | |
} | |
// Write output | |
Shell.rawWrite(output, to, styles, sender); | |
} | |
// Actions | |
function get(tokenId) { | |
return moduleState[tokenId] ? moduleState[tokenId] : defaultDescription; | |
} | |
function add(tokenId, description) { | |
moduleState[tokenId] = description; | |
return formatOutput('Description added for token.'); | |
} | |
function remove(tokenId) { | |
delete moduleState[tokenId]; | |
return formatOutput('Deleted description for token.'); | |
} | |
// Helpers | |
function formatOutput(output) { | |
return name + ': ' + output; | |
} | |
// Libraries | |
/* | |
Project: minimist https://github.com/substack/minimist | |
Copyright (c) 2013 James Halliday (substack@gmail.com) | |
License (MIT) https://github.com/substack/minimist/blob/master/LICENSE | |
*/ | |
function parseArgs(args, opts) { | |
if (!opts) opts = {}; | |
var flags = { bools : {}, strings : {}, unknownFn: null }; | |
if (typeof opts['unknown'] === 'function') { | |
flags.unknownFn = opts['unknown']; | |
} | |
if (typeof opts['boolean'] === 'boolean' && opts['boolean']) { | |
flags.allBools = true; | |
} else { | |
[].concat(opts['boolean']).filter(Boolean).forEach(function (key) { | |
flags.bools[key] = true; | |
}); | |
} | |
var aliases = {}; | |
Object.keys(opts.alias || {}).forEach(function (key) { | |
aliases[key] = [].concat(opts.alias[key]); | |
aliases[key].forEach(function (x) { | |
aliases[x] = [key].concat(aliases[key].filter(function (y) { | |
return x !== y; | |
})); | |
}); | |
}); | |
[].concat(opts.string).filter(Boolean).forEach(function (key) { | |
flags.strings[key] = true; | |
if (aliases[key]) { | |
flags.strings[aliases[key]] = true; | |
} | |
}); | |
var defaults = opts['default'] || {}; | |
var argv = { _ : [] }; | |
Object.keys(flags.bools).forEach(function (key) { | |
setArg(key, defaults[key] === undefined ? false : defaults[key]); | |
}); | |
var notFlags = []; | |
if (args.indexOf('--') !== -1) { | |
notFlags = args.slice(args.indexOf('--')+1); | |
args = args.slice(0, args.indexOf('--')); | |
} | |
function argDefined(key, arg) { | |
return (flags.allBools && /^--[^=]+$/.test(arg)) || | |
flags.strings[key] || flags.bools[key] || aliases[key]; | |
} | |
function setArg (key, val, arg) { | |
if (arg && flags.unknownFn && !argDefined(key, arg)) { | |
if (flags.unknownFn(arg) === false) return; | |
} | |
var value = !flags.strings[key] && isNumber(val) | |
? Number(val) : val | |
; | |
setKey(argv, key.split('.'), value); | |
(aliases[key] || []).forEach(function (x) { | |
setKey(argv, x.split('.'), value); | |
}); | |
} | |
function setKey (obj, keys, value) { | |
var o = obj; | |
keys.slice(0,-1).forEach(function (key) { | |
if (o[key] === undefined) o[key] = {}; | |
o = o[key]; | |
}); | |
var key = keys[keys.length - 1]; | |
if (o[key] === undefined || flags.bools[key] || typeof o[key] === 'boolean') { | |
o[key] = value; | |
} | |
else if (Array.isArray(o[key])) { | |
o[key].push(value); | |
} | |
else { | |
o[key] = [ o[key], value ]; | |
} | |
} | |
function aliasIsBoolean(key) { | |
return aliases[key].some(function (x) { | |
return flags.bools[x]; | |
}); | |
} | |
for (var i = 0; i < args.length; i++) { | |
var arg = args[i]; | |
if (/^--.+=/.test(arg)) { | |
// Using [\s\S] instead of . because js doesn't support the | |
// 'dotall' regex modifier. See: | |
// http://stackoverflow.com/a/1068308/13216 | |
var m = arg.match(/^--([^=]+)=([\s\S]*)$/); | |
var key = m[1]; | |
var value = m[2]; | |
if (flags.bools[key]) { | |
value = value !== 'false'; | |
} | |
setArg(key, value, arg); | |
} | |
else if (/^--no-.+/.test(arg)) { | |
var key = arg.match(/^--no-(.+)/)[1]; | |
setArg(key, false, arg); | |
} | |
else if (/^--.+/.test(arg)) { | |
var key = arg.match(/^--(.+)/)[1]; | |
var next = args[i + 1]; | |
if (next !== undefined && !/^-/.test(next) | |
&& !flags.bools[key] | |
&& !flags.allBools | |
&& (aliases[key] ? !aliasIsBoolean(key) : true)) { | |
setArg(key, next, arg); | |
i++; | |
} | |
else if (/^(true|false)$/.test(next)) { | |
setArg(key, next === 'true', arg); | |
i++; | |
} | |
else { | |
setArg(key, flags.strings[key] ? '' : true, arg); | |
} | |
} | |
else if (/^-[^-]+/.test(arg)) { | |
var letters = arg.slice(1,-1).split(''); | |
var broken = false; | |
for (var j = 0; j < letters.length; j++) { | |
var next = arg.slice(j+2); | |
if (next === '-') { | |
setArg(letters[j], next, arg); | |
continue; | |
} | |
if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) { | |
setArg(letters[j], next.split('=')[1], arg); | |
broken = true; | |
break; | |
} | |
if (/[A-Za-z]/.test(letters[j]) | |
&& /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { | |
setArg(letters[j], next, arg); | |
broken = true; | |
break; | |
} | |
if (letters[j+1] && letters[j+1].match(/\W/)) { | |
setArg(letters[j], arg.slice(j+2), arg); | |
broken = true; | |
break; | |
} | |
else { | |
setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg); | |
} | |
} | |
var key = arg.slice(-1)[0]; | |
if (!broken && key !== '-') { | |
if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1]) | |
&& !flags.bools[key] | |
&& (aliases[key] ? !aliasIsBoolean(key) : true)) { | |
setArg(key, args[i+1], arg); | |
i++; | |
} | |
else if (args[i+1] && /true|false/.test(args[i+1])) { | |
setArg(key, args[i+1] === 'true', arg); | |
i++; | |
} | |
else { | |
setArg(key, flags.strings[key] ? '' : true, arg); | |
} | |
} | |
} | |
else { | |
if (!flags.unknownFn || flags.unknownFn(arg) !== false) { | |
argv._.push( | |
flags.strings['_'] || !isNumber(arg) ? arg : Number(arg) | |
); | |
} | |
if (opts.stopEarly) { | |
argv._.push.apply(argv._, args.slice(i + 1)); | |
break; | |
} | |
} | |
} | |
Object.keys(defaults).forEach(function (key) { | |
if (!hasKey(argv, key.split('.'))) { | |
setKey(argv, key.split('.'), defaults[key]); | |
(aliases[key] || []).forEach(function (x) { | |
setKey(argv, x.split('.'), defaults[key]); | |
}); | |
} | |
}); | |
if (opts['--']) { | |
argv['--'] = new Array(); | |
notFlags.forEach(function(key) { | |
argv['--'].push(key); | |
}); | |
} | |
else { | |
notFlags.forEach(function(key) { | |
argv._.push(key); | |
}); | |
} | |
return argv; | |
} | |
function hasKey (obj, keys) { | |
var o = obj; | |
keys.slice(0,-1).forEach(function (key) { | |
o = (o[key] || {}); | |
}); | |
var key = keys[keys.length - 1]; | |
return key in o; | |
} | |
function isNumber (x) { | |
if (typeof x === 'number') return true; | |
if (/^0x[0-9a-f]+$/i.test(x)) return true; | |
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment