Skip to content

Instantly share code, notes, and snippets.

@nuadaria
Last active February 2, 2024 01:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nuadaria/9ea8c1074a3cef7d97eb32ffcbaaceb3 to your computer and use it in GitHub Desktop.
Save nuadaria/9ea8c1074a3cef7d97eb32ffcbaaceb3 to your computer and use it in GitHub Desktop.
Various JavaScript functions to support an Obsidian Portal integration with discord webhooks
function buildOnClickDiscordString(source){
var startingMessage = "sendMessage(";
switch (source) {
case "character":
var title = document.getElementsByTagName("title")[0].innerHTML;
var position = title.indexOf("|")-1;
var charName = title.substring(0, position);
var imgSource = document.querySelector("[title='"+charName+"']").src;
var charParent = document.getElementsByClassName('description')[0];
var charBody = charParent.querySelectorAll('p')[0].innerHTML;
return startingMessage + "'" + imgSource + " " + charName + " | " + charBody + "');";
break;
case "monster":
var monsterName = document.getElementsByTagName('h1')[1].innerHTML;
var allImages = document.getElementsByTagName('img');
var monsterImg = '';
for (var i = 0; i < allImages.length; i++) {
if (allImages[i].className == 'media-item-align-center') {
monsterImg = allImages[i].src;
}
}
var monsterFirstSentence = document.getElementsByClassName('grid-item')[0].querySelectorAll('p')[0].innerHTML.split('.')[0];
return startingMessage + "'" + monsterImg + " " + monsterName + " " + monsterFirstSentence + "');";
break;
case "city":
case "faction":
var descriptionBlurb = document.getElementsByClassName('grid-item')[0].querySelectorAll('p')[0].innerHTML.replace(/(<([^>]+)>)/gi, "");
if (descriptionBlurb.length > 1800) {
descriptionBlurb = descriptionBlurb.substr(0,1800).trimEnd() + '...';
}
var allImages = document.getElementsByTagName('img');
var factionImg = '';
for (var i = 0; i < allImages.length; i++) {
if (allImages[i].classList.contains('media-item-align-center') || allImages[i].classList.contains('media-item-align-none')) {
factionImg = allImages[i].src;
}
}
return startingMessage + "'" + window.location.href + " " + factionImg + " " + descriptionBlurb + "');";
case "holiday":
case "location":
var allParagraphs = document.getElementsByTagName('p');
var descriptionBlurb = '';
for (var i = 0; i < allParagraphs.length -1; i++) {
descriptionBlurb += allParagraphs[i].innerHTML.replace(/(<([^>]+)>)/gi, "");
}
return startingMessage + "'" + window.location.href + " " + descriptionBlurb + "...');";
case "wiki":
return startingMessage + "'" + window.location.href + "');";
break;
case "log":
var logSubtitle = '';
if (document.getElementsByClassName('adventure-log-post-subtitle').length > 0) {
logSubtitle = document.getElementsByClassName('adventure-log-post-subtitle')[0].innerText;
}
return startingMessage + "'" + window.location.href + " " + logSubtitle + "');";
break;
case "item":
var itemName = document.getElementsByClassName('item-name')[0].innerText;
var itemParent = document.getElementsByClassName('description')[0];
var imgSource = document.querySelector("[title='"+itemName+"']").src;
var itemBody = itemParent.querySelectorAll('p')[0].innerHTML;
return startingMessage + "'" + imgSource + " " + itemName + " | " + itemBody + "');";
break;
default:
return startingMessage + "'ERROR');";
console.log('no source specified');
}
}
if (document.getElementsByClassName('character-name').length > 0
&& !document.getElementById("charDiscordButton")) {
var charDiscordButton= document.createElement("button");
charDiscordButton.textContent = 'Send to Discord';
onClickString = buildOnClickDiscordString('character');
charDiscordButton.setAttribute("onClick", onClickString);
charDiscordButton.setAttribute("id", "charDiscordButton");
document.getElementsByClassName("character-name")[0].appendChild(charDiscordButton);
document.getElementById("charDiscordButton").className += " button";
}
if (document.getElementsByClassName('tag-link').length > 0
&& !document.getElementById("wikiDiscordButton")) {
var wikiDiscordButton= document.createElement("button");
wikiDiscordButton.textContent = 'Send to Discord';
for (var i = 0; i < document.getElementsByClassName('tag-link').length; i++) {
var tag = document.getElementsByClassName('tag-link')[i].innerHTML;
switch (tag) {
case "monsters":
wikiType = "monster";
break;
case "city":
wikiType = "city";
break;
case "faction":
case "military":
wikiType = "faction";
break;
case "holiday":
wikiType = "holiday";
break;
case "location":
wikiType = "location";
break;
default:
wikiType = "wiki";
}
}
onClickString = buildOnClickDiscordString(wikiType);
wikiDiscordButton.setAttribute("onClick", onClickString);
wikiDiscordButton.setAttribute("id", "wikiDiscordButton");
var referenceNode = document.getElementsByClassName("see-all-wiki-pages-button button")[0];
referenceNode.parentNode.insertBefore(wikiDiscordButton, referenceNode.nextSibling);
document.getElementById("wikiDiscordButton").className += " button";
}
if (document.getElementsByClassName('adventure-log-post-title').length > 0
&& !document.getElementById("logDiscordButton")) {
var logDiscordButton= document.createElement("button");
logDiscordButton.textContent = 'Send to Discord';
onClickString = buildOnClickDiscordString('log');
logDiscordButton.setAttribute("onClick", onClickString);
logDiscordButton.setAttribute("id", "logDiscordButton");
document.getElementsByClassName("adventure-log-post-title")[0].appendChild(logDiscordButton);
document.getElementById("logDiscordButton").className += " button";
}
if (window.location.pathname.indexOf('item') > -1
&& !document.getElementById("itemDiscordButton")) {
var itemDiscordButton= document.createElement("button");
itemDiscordButton.textContent = 'Send to Discord';
onClickString = buildOnClickDiscordString('item');
itemDiscordButton.setAttribute("onClick", onClickString);
itemDiscordButton.setAttribute("id", "itemDiscordButton");
document.getElementsByClassName("item-name")[0].appendChild(itemDiscordButton);
document.getElementById("itemDiscordButton").className += " button";
}
function sendMessage(messageText) {
var data = JSON.stringify({
//"avatar_url": avatarURL,
"content": messageText
});
var xhr = new XMLHttpRequest();
var url = '<replace with your complete webhook URL from discord>';
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(data);
}
@nuadaria
Copy link
Author

nuadaria commented Mar 17, 2023

The various DOM scraping is very specific to what made sense for my specific campaign so you will have to adjust these to support your needs. Also VERY WORTH NOTE, you will have to make a webhook URL for your discord server PUBLIC. Please proceed with caution and the complete understanding of what this means. This is no longer true as there is now a Campaign Member Only Javascript field and it's contents are not even loaded for non members viewing your game.

@nuadaria
Copy link
Author

nuadaria commented Feb 1, 2024

Overview
The buildOnClickDiscordString function is designed to generate a string that can be used as the onClick attribute for various buttons in an Obsidian Portal site. When the user clicks on these buttons, a message is sent to Discord containing relevant information about characters, monsters, cities, factions, holidays, locations, wikis, logs, or items, depending on the context.

Function Signature
function buildOnClickDiscordString(source)

Parameters
source (String): Specifies the type of content for which the Discord message should be generated. Valid values include:

  • "character"
  • "monster"
  • "city"
  • "faction"

These can and should all be customized to your needs/your site

Return Value
A string representing the Discord message containing relevant information based on the specified source.
Example Sources

  • Character
    Discord message includes character name, image source, and character body.
  • Monster
    Discord message includes monster name, image source, and the first sentence from the description.
  • City / Faction
    Discord message includes the webpage URL, image source, and a truncated description (if longer than 1800 characters).
  • Holiday / Location
    Discord message includes the webpage URL and a concatenated description from paragraphs on the page.
  • Wiki
    Discord message includes the webpage URL.
  • Log
    Discord message includes the webpage URL and the subtitle of the adventure log post.
  • Item
    Discord message includes item name, image source, and item body.

Button Creation and Event Handling
Buttons are dynamically created based on the context of the web page. The buttons are designed to send the corresponding Discord message when clicked.

  • Character Button
    Created for pages with a character name and no existing character Discord button.
  • Wiki Button
    Created for pages with tag links (e.g., "monsters," "city") and no existing wiki Discord button.
  • Log Button
    Created for pages with an adventure log post title and no existing log Discord button.
  • Item Button
    Created for pages with "item" in the URL path and no existing item Discord button.

sendMessage Function
The sendMessage function is responsible for sending a JSON-formatted message to Discord using an XMLHttpRequest. The message includes the content to be sent to Discord.

Parameters
messageText (String): The content of the message to be sent to Discord.
Important Note
Replace in the sendMessage function with the actual Discord webhook URL to enable message posting to Discord. Failure to do so will result in an error.

Example Usage

Copy code

// Example usage on a character page
if (document.getElementsByClassName('character-name').length > 0 && !document.getElementById("charDiscordButton")) {
    var charDiscordButton = document.createElement("button");
    charDiscordButton.textContent = 'Send to Discord';
    var onClickString = buildOnClickDiscordString('character');
    charDiscordButton.setAttribute("onClick", onClickString);
    charDiscordButton.setAttribute("id", "charDiscordButton");
    document.getElementsByClassName("character-name")[0].appendChild(charDiscordButton);
    document.getElementById("charDiscordButton").className += " button";
}

Simplified Example

function buildOnClickDiscordString(source){
	var startingMessage = "sendMessage(" + source + ");";
}

//this window.location could be "wikis", "characters", "maps" or one of the other types of selectors from the larger example above
if (window.location.pathname.indexOf('item') > -1 
   && !document.getElementById("discordButton")) {
    var discordButton= document.createElement("button");
    discordButton.textContent = 'Send to Discord';
	onClickString = buildOnClickDiscordString('Hello World');
	discordButton.setAttribute("onClick", onClickString);
	discordButton.setAttribute("id", "itemDiscordButton");

	document.getElementsByClassName("item-name")[0].appendChild(discordButton);
	document.getElementById("discordButton").className += " button";
}

function sendMessage(messageText) {
    var data = JSON.stringify({
        //"avatar_url": avatarURL,
        "content": messageText
    });

    var xhr = new XMLHttpRequest();
    var url = '<replace with your complete webhook URL from discord>';

    xhr.open('POST', url, true);
    xhr.setRequestHeader("Content-Type", "application/json");

    xhr.send(data);
}

Conclusion
This documentation provides an overview of the various Discord integration functions and usage in generating Discord messages based on different content sources. Ensure to replace the placeholder in the sendMessage function with the actual Discord webhook URL for proper functionality.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment