NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @namespace https://openuserjs.org/users/Andrew-J-Kennedy
// @name DnDBtoDiscord
// @description DnD Beyond to Discord Integration
// @author Andrew-J-Kennedy
// @license LGPL-3.0-or-later; https://raw.githubusercontent.com/Andrew-J-Kennedy/DnDBtoDiscord/master/LICENSE
// @version 0.3.3
// @match https://www.dndbeyond.com/encounters/*
// @match https://www.dndbeyond.com/profile/*/characters/*
// @match https://www.dndbeyond.com/characters/*
// @require https://openuserjs.org/src/libs/sizzle/GM_config.js
// @grant GM_getValue
// @grant GM_setValue
// @require file:///opt/DnDBtoDiscord/DnDBtoDiscordPrivate.js
// @grant none
// ==/UserScript==
// ==OpenUserJS==
// @author Andrew-J-Kennedy
// ==/OpenUserJS==
/*
Comments are important
/**/
(function() {
'use strict';
////////////////////////////////////////////////////////////////////////////////
function sendMessage(content, DiscordIdOverride = null) {
if (! content) {return;}
var DiscordIdCurrent = DiscordIdDefault;
if (DiscordIdOverride) {DiscordIdCurrent = DiscordIdOverride;}
// Only embed alias DiscordId when sending to Avrae Bot and different from DiscordIdBot
var embed = ((content.substr(0,1) === pre) && (DiscordIdCurrent != DiscordIdBot));
// console.log(content.substr(0,1) + ' === ' + pre);
// console.log(DiscordIdCurrent + ' != ' + DiscordIdBot);
var request = new XMLHttpRequest();
request.open("POST", uri);
request.setRequestHeader('Content-type', 'application/json');
var params = {
username: username,
avatar_url: avatar_url,
content: content,
embeds: ( (embed) ? [{fields: [{name: 'alias',value: DiscordIdCurrent}]}] : null)
}
console.log(JSON.stringify(params))
request.send(JSON.stringify(params));
}
////////////////////////////////////////////////////////////////////////////////
function addNewEventListenerFinal(e,logMsg,func) {
if (logMsg) {console.log(logMsg);}
if (func) {func(e);}
}
////////////////////////////////////////////////////////////////////////////////
function sleep(ms,cb) {
var sleepms = setInterval(function() {
cb();
clearInterval(sleepms);
},ms);
}
////////////////////////////////////////////////////////////////////////////////
function addNewEventListener(e,logMsg,override,func) {
if (window.event.ctrlKey || ! override) {
if (override) {
event.stopImmediatePropagation();
addNewEventListenerFinal(e,logMsg,func);
} else {
// Wait for others to complete
sleep(100,function(){addNewEventListenerFinal(e,logMsg,func)});
}
}
}
////////////////////////////////////////////////////////////////////////////////
function addNewEventListenerDelegate(logMsg,override,func) {
return function(e){
addNewEventListener(e,logMsg,override,func)
}
}
////////////////////////////////////////////////////////////////////////////////
function selectMobs (e) {
console.log('selectMobs(e)')
var p = e.target.parentNode.parentNode;
var classname = 'is-selected';
if (p.classList.contains(classname)) {
p.classList.remove('is-selected');
} else {
p.classList.add('is-selected');
}
}
////////////////////////////////////////////////////////////////////////////////
function addMobs (e) {
console.log('addMobs(e)')
var elem = document.getElementsByClassName('encounter-details-monster is-selected');
for (var j = 0; j < elem.length; j++) {
var monster_name = elem[j].getElementsByClassName('encounter-details-monster__name')[0].innerText;
var monster_abrv = monster_name.replace(/[^A-Z]/g,'');
monster_abrv += monster_name.replace(/.*[A-Z]/,'');
monster_abrv = monster_abrv.substring(0,3).toUpperCase() + '#';
var sendMsgFinal = pre + 'init madd "' + monster_name + '" -name ' + monster_abrv;
console.log(monster_name + ':' + monster_abrv);
// sendToAvrea(e,sendMsgFinal);
sendToAvrea(e,sendMsgFinal,null,DiscordIdBot);
}
}
////////////////////////////////////////////////////////////////////////////////
function findRelevantParent (el,classname) {
while ( el.parentNode && el.parentNode.classList && ! el.classList.contains(classname)) {
el = el.parentNode;
}
el = (el.classList.contains(classname)) ? el : null;
return el;
}
////////////////////////////////////////////////////////////////////////////////
function rollSkill (e) {
console.log('rollSkill(e)')
var skill = e.target.innerText;
var sendMsg = '!check ' + skill;
if (window.event.shiftKey) {
sendMsg = sendMsg + ' ' + prompt("Please enter additional args:" );
}
sendToAvrea(e,sendMsg);
}
////////////////////////////////////////////////////////////////////////////////
function getSignedNumber (el) {
if (! el) {return '+0';}
if (el.getElementsByClassName(md.cp + '-signed-number').length != 1) {return '+0';}
var sign = el.getElementsByClassName(md.cp + '-signed-number__sign')[0].innerText;
var number = el.getElementsByClassName(md.cp + '-signed-number__number')[0].innerText;
return sign + number;
}
////////////////////////////////////////////////////////////////////////////////
// Casts Attack Spell as a pre-configured Attack. Use [ctrl]+[alt]+click to configure
function rollAttackSpell(e,classname) {
console.log('rollAttackSpell(e,classname)')
classname = classname.replace(/^\?\?/,md.cp);
var p = findRelevantParent(e.target,classname);
if (! p) {console.log('Classname not found: ' + classname);return}
var spell_name = p.getElementsByClassName(md.cp + '-spell-name')[0].innerText;
console.log('spell_name: ' + spell_name);
var sendMsg;
if (window.event.altKey) { // Configure Attack Spell as an attack
var to_hit = getSignedNumber(p.getElementsByClassName(md.cp + '-combat-attack__tohit')[0]);
console.log('to_hit: ' + to_hit);
var damage = p.getElementsByClassName(md.cp + '-damage__value')[0].innerText;
console.log('damage: ' + damage);
var dmg_type = p.getElementsByClassName(md.cp + '-damage__icon')[0].getElementsByClassName(md.cp + '-tooltip ')[0].getAttribute('data-original-title');
console.log('dmg_type: ' + dmg_type);
sendMsg = pre + 'attack add "' + spell_name + '" -b ' + to_hit + ' -d ' + damage + '[' + dmg_type + ']';
} else {
sendMsg = pre + 'attack "' + spell_name + '"';
if (init) {
var target = prompt("Enter target and additional args:" );
if (target){
sendMsg = sendMsg + ' -t ' + target;
} else {
sendMsg = null;
}
} else {
if (window.event.shiftKey) {
var args = prompt("Enter additional args:" );
sendMsg = sendMsg + ' ' + args;
}
}
}
console.log(sendMsg);
sendMessage(sendMsg);
}
////////////////////////////////////////////////////////////////////////////////
function rollAbility (e,classname) {
console.log('rollTool(e)')
classname = classname.replace(/^\?\?/,md.cp);
var p = findRelevantParent(e.target,classname);
if (! p) {console.log('Classname not found: ' + classname);return}
var stat_name = p.getElementsByClassName(md.cp + '-ability-summary__label')[0].innerText;
var stat = p.getElementsByClassName(md.cp + '-ability-summary__abbr')[0].innerText;
console.log(stat_name + ' ' + stat);
var common = {
command: 'check',
options: 'adv/dis -b [conditional bonus] -dc [dc] -mc [minimum roll] -rr [iterations]'
};
var sendMsg = pre + common.command + ' ' + stat_name;
if (window.event.shiftKey) {
var args = prompt("Enter options: " + common.options);
sendMsg = sendMsg + ' ' + args;
}
var prep = ( /^[AEIOU]/.test(stat_name)) ? 'an ' : 'a ';
sendMsg = sendMsg + ' -title "' + username + ' makes ' + prep + stat_name + ' check!"';
console.log(sendMsg);
sendMessage(sendMsg);
}
////////////////////////////////////////////////////////////////////////////////
function rollTool (e,classname) {
console.log('rollTool(e)')
classname = classname.replace(/^\?\?/,md.cp);
var p = findRelevantParent(e.target,classname);
if (! p) {console.log('Classname not found: ' + classname);return}
var tool_name = p.innerText.replace(/, $/,'');
console.log(tool_name);
var common = {
command: 'check',
options: 'adv/dis -b [conditional bonus] -dc [dc] -mc [minimum roll] -rr [iterations]'
};
var tools = [
{stat: 'dex',name: "Thieves' Tools"}
];
var tool;
for (let t of tools) {
if (t.name === tool_name) {
tool = t;
break;
}
}
if (! tool) {console.log('Tool not found: ' + tool_name);return}
var sendMsg = pre + common.command + ' ' + tool.stat;
if (window.event.shiftKey) {
var args = prompt("Enter options: " + common.options);
sendMsg = sendMsg + ' ' + args;
}
sendMsg = sendMsg + ' -b ' + prof_bonus + ' -title "' + username + ' uses ' +tool_name + '"';
console.log(sendMsg);
sendMessage(sendMsg);
}
////////////////////////////////////////////////////////////////////////////////
function rollSnippet (e,classname) {
console.log('rollSnippet(e)')
classname = classname.replace(/^\?\?/,md.cp);
var p = findRelevantParent(e.target,classname);
if (! p) {console.log('Classname not found: ' + classname);return}
var snippet_name = p.innerText;
var snippets = [
{name: 'Divine Smite (Special)',
command: 'smite',
options: '[-l #] [-i] [-t target] [crit] [fiend|undead]'
}
];
var snippet;
for (let s of snippets) {
if (s.name === snippet_name) {
snippet = s;
break;
}
}
if (! snippet) {console.log('Snippet not found: ' + snippet_name);return}
var sendMsg = pre + snippet.command;
if (window.event.shiftKey) {
var args = prompt("Enter options: " + snippet.options);
sendMsg = sendMsg + ' ' + args;
}
console.log(sendMsg);
sendMessage(sendMsg);
}
////////////////////////////////////////////////////////////////////////////////
function rollAttack (e,classname) {
console.log('rollAttack(e)')
classname = classname.replace(/^\?\?/,md.cp);
var p = findRelevantParent(e.target,classname);
if (! p) {console.log('Classname not found: ' + classname);return}
var attack_label = p.getElementsByClassName(md.cp + '-combat-attack__label')[0].innerText;
var sendMsg = (init) ? pre + 'init attack ' : pre + 'attack ';
if (window.event.altKey && p.getElementsByClassName(md.cp + '-damage--versatile').length > 0) {
attack_label = '2-Handed ' + attack_label
}
sendMsg = sendMsg + attack_label;
if (init) {
var target = prompt("Enter target and additional args:" );
if (target){
sendMsg = sendMsg + ' -t ' + target;
} else {
sendMsg = null;
}
} else {
if (window.event.shiftKey) {
var args = prompt("Enter additional args:" );
sendMsg = sendMsg + ' ' + args;
}
}
console.log(sendMsg);
sendMessage(sendMsg);
}
////////////////////////////////////////////////////////////////////////////////
function rollSave (e,classname) {
console.log('rollSave(e)')
classname = classname.replace(/^\?\?/,md.cp);
var p = findRelevantParent(e.target,classname);
if (! p) {console.log('Classname not found: ' + classname);return}
var save;
for (let cn of p.classList) {
if (cn.includes(classname + '--')) {
save = cn.replace(classname + '--','');
}
}
var sendMsg = '!save ' + save;
if (window.event.shiftKey) {
sendMsg = sendMsg + ' ' + prompt("Please enter additional args:" );
}
sendToAvrea(e,sendMsg);
}
////////////////////////////////////////////////////////////////////////////////
function rollInitative (e) {
var sendMsg = (init) ? '!init join' : '!check initiative';
sendToAvrea(e,sendMsg);
}
////////////////////////////////////////////////////////////////////////////////
function turnOver (e) {
var msg = (window.event.shiftKey) ? prompt("Please enter message:" ) : 'Done';
var sendMsg = (init) ? '!init next' : `!embed -thumb ${avatar_url} -desc '${msg}'`;
sendToAvrea(e,sendMsg);
}
////////////////////////////////////////////////////////////////////////////////
function freeText (e,DiscordIdOverride = null) {
var sendMsg = prompt("Please enter your message:" );
if (sendMsg) {sendMessage(sendMsg,DiscordIdOverride);}
}
////////////////////////////////////////////////////////////////////////////////
function sendToAvrea (e,sendMsg,Arg1_innerText = null,DiscordIdOverride = null) {
if (! sendMsg) {
sendMsg = prompt("Please enter your message:");
}
var sendMsgFinal = sendMsg;
// Replace first char (if non alphanumeric) with server Prefix
sendMsgFinal = sendMsgFinal.replace(/^[^"a-zA-Z0-9 ]/,pre);
if (Arg1_innerText) {
// console.log(Arg1_innerText);
// console.log(e.target);
var Arg1;
if (Arg1_innerText === 'self') {
Arg1 = e.target.innerText.replace(/\.$/,'')
} else {
Arg1 = e.target.getElementsByClassName(Arg1_innerText)[0].innerText;
}
sendMsgFinal = sendMsgFinal.replace('$1',Arg1);
}
if (sendMsg.includes('-t $2')) { // Target Required
if (init) {
var target = prompt("Please enter your target:" );
if (target){
sendMsgFinal = sendMsgFinal.replace('$2',target);
} else {
sendMsgFinal = null;
}
} else { // Remove targeting parameter if init system off
sendMsgFinal = sendMsgFinal.replace('-t $2','');
var args = prompt("Please enter any extra args:" );
if (target){
sendMsgFinal = sendMsgFinal + ' ' + args;
}
}
}
if (sendMsg.includes('init ') && ! init) { // Remove init argument if init system off
sendMsgFinal = sendMsgFinal.replace('init ','');
}
console.log(sendMsgFinal);
sendMessage(sendMsgFinal,DiscordIdOverride);
}
////////////////////////////////////////////////////////////////////////////////
function openConfig() {
if (window.event.altKey) {
GM_config.open();
} else {
gmcuid.open();
}
}
////////////////////////////////////////////////////////////////////////////////
function addNewEventListeners(start = 0) {
console.log('addNewEventListeners');
const data = {
encounters: [
//classname ,type ,logMessage ,override ,function
['qa-init_begin' ,'click' ,'start' ,true ,function(e){sendToAvrea(e,'!init begin',null,DiscordIdBot);}],
['qa-init_madd' ,'click' ,'add mob' ,true ,function(e){addMobs(e);}],
['qa-init_next' ,'click' ,'next' ,true ,function(e){sendToAvrea(e,'!init next',null,DiscordIdBot);}],
['qa-init_list' ,'click' ,'list' ,true ,function(e){sendToAvrea(e,'!init list',null,DiscordIdBot);}],
['qa-init_end' ,'click' ,'end' ,true ,function(e){sendToAvrea(e,'!init end -yes',null,DiscordIdBot);}],
['qa-chat' ,'click' ,'chat' ,true ,function(e){freeText(e,DiscordIdBot);}],
['qa-chat_bot' ,'click' ,'chat bot' ,true ,function(e){sendToAvrea(e,null,null,DiscordIdBot);}],
['encounter-details-monster' ,'click' ,'monster' ,true ,function(e){selectMobs(e);}]
]
,characters: [
//classname ,type ,logMessage ,override ,function
['??-tab-list__nav-item' ,'click' ,'refresh lstnr' ,false ,function(){addNewEventListeners(1)}],
['??-tab-options__header-heading' ,'click' ,'refresh lstnr' ,false ,function(){addNewEventListeners(2)}],
['ct-free_text' ,'click' ,'free text' ,true ,function(e){freeText(e);}],
['ct-config' ,'click' ,'config' ,true ,function(e){openConfig();}],
['??-character-tidbits__avatar' ,'click' ,'next' ,true ,function(e){turnOver(e);}],
['ct-quick-info__ability' ,'click' ,'check(ability)',true ,function(e){rollAbility(e,'ct-quick-info__ability');}],
['??-saving-throws-summary__ability','click' ,'save' ,true ,function(e){rollSave(e,'??-saving-throws-summary__ability');}],
['ct-initiative-box' ,'click' ,'initiative' ,true ,function(e){rollInitative(e);}],
['??-combat-attack--item' ,'click' ,'attack' ,true ,function(e){rollAttack(e,'??-combat-attack--item');}],
['??-combat-action-attack-weapon' ,'click' ,'attack' ,true ,function(e){rollAttack(e,'??-combat-action-attack-weapon');}],
['??-combat-attack--spell' ,'click' ,'cast' ,true ,function(e){rollAttackSpell(e,'??-combat-attack--spell');}],
['ct-skills__item' ,'click' ,'check(skill)' ,true ,function(e){rollSkill(e);}],
['??-tooltip' ,'click' ,'check(tool)' ,true ,function(e){rollTool(e,'??-tooltip');}],
['ct-feature-snippet__heading' ,'click' ,'snippet' ,true ,function(e){rollSnippet(e,'ct-feature-snippet__heading');}]
]
};
for (var i = start; i < data[page].length; i++) {
console.log(data[page][i]);
var classname = data[page][i][0];
var etype = data[page][i][1];
var logMsg = data[page][i][2];
var override = data[page][i][3];
var func = data[page][i][4];
classname = classname.replace(/^\?\?/,md.cp);
var els = document.getElementsByClassName(classname);
console.log(logMsg + ': ' + els.length);
for (var j = 0; j < els.length; j++) {
console.log(j + ':' + logMsg);
els[j].addEventListener(etype, addNewEventListenerDelegate(logMsg,override,func), false);
}
}
} // addNewEventListeners
////////////////////////////////////////////////////////////////////////////////
function containsObject(obj, list) {
for (var i = 0; i < list.length; i++) {
if (list[i] === obj) {
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
function addNewMutationObservers () {
console.log('addNewMutationObservers');
const data = {
encounters: [
{
classname: 'encounter-details__content',
type: 'click',
logMessage: 'attack',
override: true,
func: function(e){sendToAvrea(e,'!init attack $1 -t $2','self');},
cbn_cn0: 'encounter-details__content-section--monster-stat-block', // Mutation Node
cbn_cn1: 'mon-stat-block__description-block-content', // Relevent Node
cbn_tst_sib: {cn:'mon-stat-block__description-block-heading',text:'Actions'}, // Test sibling node
find_target: {type: 'tag', name: 'strong'}
}
],
characters: [
]
};
for (var i = 0; i < data[page].length; i++) {
console.log(data[page][i]);
var jd = data[page][i]; // Job Details
var targetNode = document.getElementsByClassName(jd.classname)[0];
var config = { attributes: false, childList: true, subtree: true };
var callback = function(mutationsList, observer) {
for(let mutation of mutationsList) {
if (mutation.addedNodes.length > 0) {
var node_f = []; // node found
for(let node_m of mutation.addedNodes) {
if (node_m instanceof HTMLElement) {
if (node_m.classList.contains(jd.cbn_cn0)) { //Top Down (Relevant Node is below the Added Node)
for(let node_r of node_m.getElementsByClassName(jd.cbn_cn1)) {
var tst_sib = (jd.cbn_tst_sib) ? node_r.parentNode.getElementsByClassName(jd.cbn_tst_sib.cn)[0] : null;
if (tst_sib && tst_sib.innerText === jd.cbn_tst_sib.text ) {
node_f.push(node_r);
}
}
} else { // Bottom up (Added Nodes are inside the Relevant Node)
var tst_sib = (jd.cbn_tst_sib) ? node_m.parentNode.parentNode.getElementsByClassName(jd.cbn_tst_sib.cn)[0] : null;
if (tst_sib && tst_sib.parentNode === node_m.parentNode.parentNode && tst_sib.innerText === jd.cbn_tst_sib.text ) {
if ( ! containsObject(node_m.parentNode, node_f)) {
node_f.push(node_m.parentNode);
}
}
}
}
}
for(let nf of node_f) {
// console.log('nf.classList: ' + nf.classList);
for(let e of nf.getElementsByTagName(jd.find_target.name)) {
// console.log('e.innerText: ' + e.innerText);
e.addEventListener(jd.type, addNewEventListenerDelegate(jd.logMessage,jd.override,jd.func), false);
}
}
}
}
};
var observer = new MutationObserver(callback);
observer.observe(targetNode, config);
}
}
////////////////////////////////////////////////////////////////////////////////
function addNewMutationObserverTest (searchTerm,e,cb) {
if (! e) {return;}
const config = { attributes: false, childList: true, subtree: true };
const callback = function(mutationsList, observer) {
for(let mutation of mutationsList) {
if (mutation.addedNodes.length > 0) {
for(let node of mutation.addedNodes) {
if (node instanceof HTMLDivElement && node.classList.contains('encounter-details__body')) {
console.log(searchTerm + ': ' + node.classList);
}
}
}
}
};
const observer = new MutationObserver(callback);
observer.observe(e, config);
}
////////////////////////////////////////////////////////////////////////////////
// Main Mutation Observer - waiting for the page to fully load
function addNewMutationObserver (e,classname,cb) {
if (! e) {console.log('undefined');return;}
console.log('classname: ' + classname);
const config = { attributes: false, childList: true, subtree: true };
const callback = function(mutationsList, observer) {
for(let mutation of mutationsList) {
if (mutation.addedNodes.length > 0) {
for(let node of mutation.addedNodes) {
// console.log(node);
if (node instanceof HTMLDivElement && (node.classList.contains(classname)||node.childNodes[0].classList.contains(classname))) {
// console.log('node: ' + node);
cb();
}
}
}
}
};
const observer = new MutationObserver(callback);
observer.observe(e, config);
}
////////////////////////////////////////////////////////////////////////////////
function configToGlobals () {
WebHook = gmcuid.get('WebHook');
DiscordId = gmcuid.get('DiscordId');
DiscordIdBot = WebHook.split('/')[0];
pre = gmcuid.get('ServerPrefix');
uri = 'https://discordapp.com/api/webhooks/' + WebHook;
init = (gmcuid.get('InitSystem')|| pg === 'enc') ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
function main(dt) {
md = dt; // main data
console.log('main: ' + md.cp);
configToGlobals();
console.log('uri: ' + uri);
// Username and Avatar details
switch (page) {
case 'characters':
DiscordIdDefault = DiscordId;
username = document.getElementsByClassName(md.cp + '-character-tidbits__name')[0].innerText;
avatar_url = document.getElementsByClassName(md.cp + '-character-tidbits__avatar')[0].style.backgroundImage.replace(/url\("([^?"]*).*/, '$1') ;
prof_bonus = parseInt(getSignedNumber(document.getElementsByClassName('ct-proficiency-bonus-box__value')[0]));
break;
case 'encounters':
DiscordIdDefault = DiscordIdBot;
username = 'DM';
avatar_url = 'https://i.imgur.com/kLfuisi.png';
break;
}
console.log('username: ' + username);
console.log('DiscordId: ' + DiscordId);
// Add any additional buttons
const data = {
encounters: {
parentNode: 'ddb-page-header__controls',
template: '<a class="ddbeb-button dndb2d_text-here"></a>',
buttons: [
//classname ,text
['qa-init_begin' ,'Start Fight'],
['qa-init_madd' ,'Add Mobs'],
['qa-init_next' ,'Next'],
['qa-init_list' ,'List'],
['qa-init_end' ,'End Fight'],
['qa-chat' ,'Chat'],
['qa-chat_bot' ,'Chat Bot']
]
},
characters: {
parentNode: 'ct-character-header-desktop',
template: '<div class="ct-character-header-desktop__group"><div class="ct-character-header-desktop__button"><span class="ct-character-header-desktop__button-label dndb2d_text-here"></span></div></div>',
buttons: [
//classname ,text
['ct-free_text' ,'Free Text'],
['ct-config' ,'Config']
]
}
};
if (data[page].buttons.length > 0) {
const parentNode = document.getElementsByClassName(data[page].parentNode)[0];
if (parentNode) {
var tpn = document.createElement('div');
tpn.innerHTML = data[page].template;
for (var i = 0; i < data[page].buttons.length; i++) {
var classname = data[page].buttons[i][0];
if (document.getElementsByClassName(classname).length == 0) {
console.log(data[page].buttons[i]);
var text = data[page].buttons[i][1];
var pn = tpn.cloneNode(true);
pn.childNodes[0].classList.add(classname);
pn.getElementsByClassName('dndb2d_text-here')[0].innerText = text
parentNode.appendChild(pn.childNodes[0]);
}
}
}
}
// Add EventListeners and MutationObservers
addNewEventListeners();
addNewMutationObservers();
return;
} // main
// Script-wide variables
var DiscordId;
var WebHook;
var DiscordIdBot;
var DiscordIdDefault;
var username;
var avatar_url;
var pre;
var uri;
var init;
var md; // main data
var prof_bonus;
const page = (window.location.href.match(/characters/)) ? 'characters' : 'encounters';
const pg = (page === 'characters') ? 'chr' : 'enc'; //Abbreviated form
console.log("DnD Beyond to Discord Integration: " + page);
// Setup Configuration: Location to place the Config div and Unique Identifier
const cfg = {
enc: {
cn: 'mm-navbar__container',
uid: window.location.href.replace(/[\s\S]*encounters\/([\s\S]*)$/,'$1')
},
chr: {
cn: 'site-bar__container',
uid: window.location.href.replace(/[\s\S]*characters\/([0-9]{8}[0-9]*)[\s\S]*$/,'$1')
}
};
console.log(cfg[pg].uid);
var site_bar = document.getElementsByClassName(cfg[pg].cn)[0];
var frame0 = document.createElement('div');
var frame1 = document.createElement('div');
site_bar.appendChild(frame0);
site_bar.appendChild(frame1);
var GMconfigId0 = 'DnDBeyond2DiscordDefaults-0.0.1';
var GMconfig0 = {
'id': GMconfigId0,
'title': 'DnD Beyond to Discord Default Config Settings',
'fields': {
'ServerPrefix': {
'label': 'Server Prefix',
'type': 'text',
'size': 1,
'default': '!'
},
'InitSystem': {
'label': 'Init System',
'type': 'checkbox',
'default': false
},
'WebHook': {
'label': 'Discord Server WebHook',
'type': 'text',
'size': 87,
'default': '00--WEBHOOK-URL-00/QWERTY------------------------RANDOM-KEY----------------------XCVBNM'
},
'DiscordId': {
'label': 'Discord User Id',
'type': 'text',
'size': 18,
'default': '000000000000000000'
}
},
'events': {
'init': function() {checkConfigDefaults();},
'open': function() {
var e = document.getElementById(GMconfigId0);
e.classList.add('mm-mega-menu');
e.classList.add('mm-nav-item__label');
},
'save': function() { setConfigDefaults(); },
},
'frame': frame0
};
function checkConfigDefaults () {
if (GMconfig0.fields.WebHook.default === GM_config.get('WebHook') || GMconfig0.fields.DiscordId.default === GM_config.get('DiscordId') ) {
GM_config.open();
} else {
setConfigDefaults();
}
}
function setConfigDefaults () {
GMconfig1 = {
'id': GMconfigId1,
'title': 'DnD Beyond to Discord Config Settings',
'fields': {
'ServerPrefix': {
'label': 'Server Prefix',
'type': 'text',
'size': 1,
'default': GM_config.get('ServerPrefix')
},
'InitSystem': {
'label': 'Init System',
'type': 'checkbox',
'default': GM_config.get('InitSystem')
},
'WebHook': {
'label': 'Discord Server WebHook',
'type': 'text',
'size': 87,
'default': GM_config.get('WebHook')
},
'DiscordId': {
'label': 'Discord User Id',
'type': 'text',
'size': 18,
'default': GM_config.get('DiscordId')
}
},
'events': {
'save': function() { configToGlobals(); },
},
'frame': frame1
};
gmcuid = new GM_configStruct(GMconfig1);
console.log('gmcuid DiscordId: ' + gmcuid.get('DiscordId'));
}
var GMconfigId1 = 'DnDBeyond2Discord-' + cfg[pg].uid;
var GMconfig1;
var gmcuid;
GM_config.init(GMconfig0);
// Wait for the Encounter/Character Sheet to load
const dt = {
enc: [{
pn: 'encounter-builder-root',
cn: 'encounter-details__body',
cp: 'enc' //placeholder
}],
chr: [{
pn: 'character-sheet-target',
cn: 'ct-character-sheet-desktop',
cp: 'ct' //classname prefix for some elements
},{
pn: 'character-tools-target',
cn: 'ct-character-sheet-desktop',
cp: 'ddbc' //classname prefix for some elements
}]
};
console.log('launch with MutationObserver')
for (let d of dt[pg]) {
addNewMutationObserver(document.getElementById(d.pn),d.cn,function(){main(d);});
}
})();
/*
/**/