Created
October 24, 2025 00:49
-
-
Save keithcurtis1/4632a3dedeca0f779a4f5664cfe1dbb3 to your computer and use it in GitHub Desktop.
Key Capture for Roll20, userscript written by the Aaron
This file contains hidden or 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
| // ==UserScript== | |
| // @name Key Capture | |
| // @namespace Violentmonkey Scripts | |
| // @match https://app.roll20.net/editor/ | |
| // @grant unsafeWindow | |
| // @version 1.0 | |
| // @author - | |
| // @description 6/9/2021, 2:13:30 PM | |
| // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js | |
| // ==/UserScript== | |
| /* global $ Window Node unsafeWindow console */ | |
| /* eslint-disable no-console */ | |
| (()=>{ | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // Scriptlets Here //////////////////////////////////////////////////////////// | |
| /////////////////////////////////////////////////////////////////////////////// | |
| const scriptlets = { | |
| ['ctrl-Slash']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Hotkeys}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['ctrl-KeyC']: ()=>{ | |
| $('#textchat-input textarea').val('%{selected|check}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['ctrl-KeyS']: ()=>{ | |
| $('#textchat-input textarea').val('%{selected|save}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['ctrl-KeyG']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Group-Check}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['Home']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Find-TokenBy-Name}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['End']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Target-Selected-All-Players}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['NumLock']: ()=>{ | |
| $('#textchat-input textarea').val('!bump'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['PageDown']: ()=>{ | |
| $('#textchat-input textarea').val('!gmnote'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['shift-PageDown']: ()=>{ | |
| $('#textchat-input textarea').val('!gmnote --bio'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['shift-End']: ()=>{ | |
| $('#textchat-input textarea').val('!gmnote --charnote'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['alt-PageDown']: ()=>{ | |
| $('#textchat-input textarea').val('!gmnote --image'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['PageUp']: ()=>{ | |
| $('#textchat-input textarea').val('!group-init'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['shift-PageUp']: ()=>{ | |
| $('#textchat-input textarea').val('#Statblock'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F1']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|Report-PCs-Compact}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['shift-F1']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|PCs-Detail}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['alt-F1']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|Report-PCs}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F2']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|NPCs-Compact}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['shift-F2']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|%{Reports|Report-NPCs}}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F3']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|Hommelton-Report-Compact}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['shift-F3']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|Hommelton-Brief}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['alt-F3']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|Hommelton-Report}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F4']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|Search-Labels}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F5']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|Map-Keys}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F12']: ()=>{ | |
| $('#textchat-input textarea').val('!token-fate'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F13']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Game}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F14']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Utility}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F15']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Rules}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F16']: ()=>{ | |
| $('#textchat-input textarea').val('!cmaster --turn,next'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['shift-F16']: ()=>{ | |
| $('#textchat-input textarea').val('!cmaster --turn,previous'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['alt-F16']: ()=>{ | |
| $('#textchat-input textarea').val('!group-init --sort'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F17']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Condition}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['shift-F17']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Clear-All-Conditions}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['alt-F17']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|CMReport}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F18']: ()=>{ | |
| $('#textchat-input textarea').val('%{Reports|Report}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['shift-F18']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Move-token}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['F19']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Modify-HP}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['shift-F19']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Dead}'); | |
| $('#textchat-input button').click(); | |
| }, | |
| ['alt-F19']: ()=>{ | |
| $('#textchat-input textarea').val('%{Macros|Revive}'); | |
| $('#textchat-input button').click(); | |
| } | |
| }; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| /////////////////////////////////////////////////////////////////////////////// | |
| /////////////////////////////////////////////////////////////////////////////// | |
| let loggingMode = false; | |
| let toggleEventCounter = 0; | |
| let toggleEventClearHandle; | |
| const s = { | |
| banner: `color:#90f; font-size:30px; font-weight: bold; -webkit-text-stroke: 1px black;`, | |
| normal: `line-height:1.2em;`, | |
| code: `padding: 2px 4px;font-size: 90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px;border: 1px solid #c7254e;line-height:1.2em;display:inline-block;`, | |
| msgTag: `padding: 4px 4px;background-color:#330066;color:white;font-weight:bold;font-variant:small-caps;border-radius:4px;border:1px solid #9900ff;line-height:1.2em;display:inline-block;text-align:middle;`, | |
| msgBody: `` | |
| }; | |
| const t = { | |
| banner: ()=>{ | |
| let extraStyles = []; | |
| let extra = Object.keys(scriptlets).reduce((m,k)=>{ | |
| extraStyles = [...extraStyles,s.normal,s.code,s.normal]; | |
| return `${m}\n%c•%c${k}%c`; | |
| },''); | |
| console.log(`%cThe Aaron's Key Handler\n%cPress %calt-ctrl-shift%c three times rapidly to enable Logging mode.\n\nInstalled Shortcuts:\n${extra}\n\n\n`,s.banner,s.normal,s.code,s.normal,...extraStyles); | |
| }, | |
| msg: (text,...styles)=>{ | |
| if(loggingMode){ | |
| console.log(`%cKeyHandler%c ${text}`,s.msgTag,s.msgBody,...styles); | |
| } | |
| } | |
| }; | |
| const mod = { | |
| alt: (e) => e.altKey ? 'alt-' :'', | |
| ctrl: (e) => e.ctrlKey ? 'ctrl-' : '', | |
| shift: (e) => e.shiftKey ? 'shift-' : '' | |
| }; | |
| const isAltCtrlShift = (e) => { | |
| return( | |
| (e.altKey && e.ctrlKey && 16 === e.keyCode) || | |
| (e.altKey && 17 === e.keyCode && e.shiftKey) || | |
| (18 === e.keyCode && e.ctrlKey && e.shiftKey) | |
| ); | |
| }; | |
| const LookupFromEvent = (e) => { | |
| return `${mod.alt(e)}${mod.ctrl(e)}${mod.shift(e)}${e.code}`; | |
| }; | |
| const toggleHandler = (e) => { | |
| if(isAltCtrlShift(e)){ | |
| clearTimeout(toggleEventClearHandle); | |
| ++toggleEventCounter; | |
| if(toggleEventCounter>=3){ | |
| toggleEventCounter = 0; | |
| loggingMode = !loggingMode; | |
| t.msg(`Logging Mode: %c${loggingMode ? 'Enabled' : 'Disabled'}`,s.code); | |
| } else { | |
| setTimeout(()=>toggleEventCounter=0,1000); | |
| } | |
| } | |
| }; | |
| const stringifyEvent = (e) => { // eslint-disable-line no-unused-vars | |
| const obj = {}; | |
| for (let k in e) { | |
| obj[k] = e[k]; | |
| } | |
| return JSON.stringify(obj, (k, v) => { | |
| if (v instanceof Node) return 'Node'; | |
| if (v instanceof Window) return 'Window'; | |
| return v; | |
| }, 2); | |
| }; | |
| const keyHandler = (e) => { | |
| let key = LookupFromEvent(e); | |
| if(['AltLeft','AltRight','ControlLeft','ControlRight','ShiftLeft','ShiftRight','OSLeft','OSRight'].includes(e.code)){ | |
| return; | |
| } | |
| if(scriptlets.hasOwnProperty(key)){ | |
| e.preventDefault(); | |
| scriptlets[key](e); | |
| } else { | |
| t.msg(`No scriptlet for shortcut: %c${key}`,s.code); | |
| //t.msg(`No scriptlet for shortcut: ${key}\n%c${stringifyEvent(e)}`,s.code); | |
| } | |
| }; | |
| unsafeWindow.document.addEventListener('keydown',keyHandler,true); | |
| unsafeWindow.document.addEventListener('keyup',toggleHandler,true); | |
| setTimeout(t.banner,0);//3000); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment