Skip to content

Instantly share code, notes, and snippets.

@keithcurtis1
Created October 24, 2025 00:49
Show Gist options
  • Select an option

  • Save keithcurtis1/4632a3dedeca0f779a4f5664cfe1dbb3 to your computer and use it in GitHub Desktop.

Select an option

Save keithcurtis1/4632a3dedeca0f779a4f5664cfe1dbb3 to your computer and use it in GitHub Desktop.
Key Capture for Roll20, userscript written by the Aaron
// ==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