Here's a really rough "is this what you mean?" script. It just spits the info out rather than being prompted, but that's easy to change. It only listens for atkdmg and dmg templates... I'm assuming there's already enough info on Spellcard templates that you don't want those? on('ready', () => { on('chat:message', (msg) => { log(msg.content); if (msg.rolltemplate && msg.rolltemplate.match(/(dmg|atkdmg)/i) && msg.content.match(/spelllevel=(c|\d)/i)) { log('Spell detected.'); let charName = (msg.content.match(/charname=([^}0{]+)/)) ? msg.content.match(/charname=([^}0{]+)/)[1] : false; charName = (charName) ? charName : msg.who let char = findObjs({type: 'character', name: charName})[0]; log(char); if (!char) char = findObjs({type: 'character', controlledby: msg.playerid})[0]; if (!char) return log(`Cannot find character associated with ${msg.who}'s chat message`); //char = char[0]; let spellName = (msg.content.match(/rname=([^}]+)}/)) ? msg.content.match(/rname=([^}]+)}/)[1] : null if (!spellName) return log(`Couldn't find spell name.`); log(`spell name: ${spellName}`); let rxSpell = new RegExp(`${spellName}`, 'i'); log(`spell regex: ${rxSpell}`); let attrs = findObjs({type: 'attribute', characterid: char.id}); //&& a.get('current').match(rxSpell)) log(`Found spells: `); attrs = attrs.filter(a => a.get('name').match(/spellname/i)); attrs = attrs.find(a => a.get('current').match(rxSpell)); log(`Found spell: ${attrs}`); if (!attrs) return log(`Couldn't find ${spellName} on ${charName}.`) let spellRow = (attrs.get('name').match(/_([A-Z0-9a-z_\-]{20})_spellname/)) ? attrs.get('name').match(/_([A-Z0-9a-z_\-]{20})_spellname/)[1] : false; log(`got row ID: ${spellRow}`); if (!spellRow) return log(`Failed to find spellrow for ${spellName} on ${charName}`); let rxSpellDesc = new RegExp(`${spellRow}_spelldescription`); let spellDesc = findObjs({type: 'attribute', characterid: char.id}).find(a => a.get('name').match(rxSpellDesc)) if (!spellDesc) return log(`Couldn't find spell description`); spellDesc = spellDesc.get('current'); sendChat('SpellSpy', `&{template:npcaction} {{rname=${spellName} Info}} {{description=${spellDesc}<br>[Compendium Entry](<a href="https://app.roll20.net/compendium/dnd5e/${spellName})}}`" rel="nofollow">https://app.roll20.net/compendium/dnd5e/${spellName})}}`</a>); } }); }); So I guess the question is.... is this what you mean? Apart from changing it so it stores the template rather than sends it to chat, and waits for someone to request it. It has a few cracks at figuring out who cast the spell, but if it's not in the template (from a custom macro) and the speaker is using their player name and controls multiple characters, it can fail. Not much to be done about that. Also it won't work for NPC's if you have nameplates turned off. And there's probably 1000 ways to throw errors.