Last active
February 23, 2023 19:07
-
-
Save mattroseman/80e3113db46d385183fd0e062c28dc58 to your computer and use it in GitHub Desktop.
DGG Scripts: A catchall Tampermonkey script for messing around with adding stuff to destiny.gg chat
This file contains 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 DGG Scripts | |
// @namespace http://tampermonkey.net/ | |
// @version 0.1 | |
// @description Random scripts modifying DGG and DGG chat | |
// @author Matthew Roseman | |
// @match *://*.destiny.gg/embed/chat* | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=destiny.gg | |
// @grant none | |
// @run-at document-start | |
// ==/UserScript== | |
/* | |
================================================ | |
Shared Observers | |
================================================ | |
*/ | |
/** | |
* handleNewMessage is a shared handler for new DGG message elements being added to the chat | |
* it passes the new message to the various functions used by different parts of this general script | |
*/ | |
function handleNewMessage(newMessage) { | |
addNewUserFlairToMessage(newMessage); | |
showRestrictedEmotes(newMessage); | |
} | |
document.addEventListener("DOMContentLoaded", function(event) { | |
const messageObserver = new MutationObserver((mutationList, observer) => { | |
for (const mutation of mutationList) { | |
if (mutation.type === 'childList') { | |
for (const node of mutation.addedNodes) { | |
if (node.className?.includes('msg-user')) { | |
handleNewMessage(node); | |
} | |
} | |
} | |
} | |
}); | |
// messageObserver should trigger for any new message added to the chat-lines div | |
messageObserver.observe(document.getElementsByClassName('chat-lines')[0], {childList: true}); | |
}); | |
/* | |
================================================ | |
Show All Emotes, Regardless of Flair | |
================================================ | |
*/ | |
const RESTRICTED_EMOTES = [ | |
'Aware', | |
]; | |
function showRestrictedEmotes(newMessage) { | |
const messageText = newMessage.getElementsByClassName("text")[0]; | |
let messageTextHTML = messageText.innerHTML; | |
for (const emote of RESTRICTED_EMOTES) { | |
messageTextHTML = messageTextHTML.replace(new RegExp(`(^|\\s)${emote}($|\\s)`, "gm"), `$1<div title='${emote}' class='emote ${emote}'>${emote}</div>$2`); | |
} | |
messageText.innerHTML = messageTextHTML; | |
} | |
/* | |
================================================ | |
Show New-User Badge | |
================================================ | |
*/ | |
// code that will be added, inline, to the page's HTML in a <script> tag | |
const INJECTED_CODE = ` | |
let OrigWebSocket = window.WebSocket; // store the original WebSocket for later reference | |
let wsAddListener = OrigWebSocket.prototype.addEventListener; | |
wsAddListener = wsAddListener.call.bind(wsAddListener); // rebind to the call function, as it gets lost when reassigned to a new variable | |
window.WebSocket = function WebSocket(url, protocols) { | |
var ws; | |
// mock the various construction situations | |
if (!(this instanceof WebSocket)) { | |
ws = OrigWebSocket.apply.bind(OrigWebSocket)(this, arguments); | |
} else if (arguments.length === 1) { | |
ws = new OrigWebSocket(url); | |
} else if (arguments.length >= 2) { | |
ws = new OrigWebSocket(url, protocols); | |
} else { // No arguments (browsers will throw an error) | |
ws = new OrigWebSocket(); | |
} | |
// add a listener to 'message' events which will store the incoming messages | |
wsAddListener(ws, 'message', function(event) { | |
const users = JSON.parse(sessionStorage.getItem('users') || '{}'); | |
const type = event.data.split(' ', 1)[0]; | |
const data = event.data.split(' ').slice(1).join(' '); | |
if (type === 'NAMES') { | |
message = JSON.parse(data); | |
for (const user of message.users) { | |
users[user.nick.toLowerCase()] = user.createdDate; | |
} | |
} else if (type === 'JOIN') { | |
message = JSON.parse(data); | |
users[message.nick.toLowerCase()] = message.createdDate; | |
} | |
sessionStorage.setItem('users', JSON.stringify(users)); | |
}); | |
return ws; | |
}.bind(); | |
window.WebSocket.prototype = OrigWebSocket.prototype; | |
window.WebSocket.prototype.constructor = window.WebSocket; | |
`; | |
// create a <script> tag, add the above websocket eavesdropping code, and insert in the page's HTML | |
const injectedScript = document.createElement('script'); | |
injectedScript.textContent = INJECTED_CODE; | |
(document.head || document.documentElement).appendChild(injectedScript); | |
const LEAF_ICON_SVG = '' + | |
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="rgb(34, 197, 94)" aria-hidden="true" class="h-4 w-4 text-green-500" height="18px" width="18px">' + | |
'<path fill-rule="evenodd" d="M5 2a1 1 0 011 1v1h1a1 1 0 010 2H6v1a1 1 0 01-2 0V6H3a1 1 0 010-2h1V3a1 1 0 011-1zm0 10a1 1 0 011 1v1h1a1 1 0 110 2H6v1a1 1 0 11-2 0v-1H3a1 1 0 110-2h1v-1a1 1 0 011-1zM12 2a1 1 0 01.967.744L14.146 7.2 17.5 9.134a1 1 0 010 1.732l-3.354 1.935-1.18 4.455a1 1 0 01-1.933 0L9.854 12.8 6.5 10.866a1 1 0 010-1.732l3.354-1.935 1.18-4.455A1 1 0 0112 2z" clip-rule="evenodd">' + | |
'</path>' + | |
'</svg>'; | |
function addNewUserFlairToMessage(newMessage) { | |
// construct the leaf icon in preperation if this message is from a new user's account | |
const leafIcon = document.createElement('i'); | |
leafIcon.className += 'flair new-user-flair'; | |
leafIcon.style.height = '18px'; | |
leafIcon.innerHTML = LEAF_ICON_SVG; | |
const username = newMessage.dataset.username; | |
// if username isn't in the current dictionary of users, re-read it from session storage to be sure | |
if (!(username in users)) { | |
updateUsers(); | |
} | |
if (username in users) { | |
const userAge = (new Date() - new Date(users[username])) / 1000 / 60 / 60 / 24; // user's account's age in days | |
// console.log(`${username} is ${userAge} days old`); | |
if (userAge <= 7) { | |
// if the features span isn't in the HTML, no features span is added), add it | |
if (newMessage.getElementsByClassName('features').length === 0) { | |
const features = document.createElement('span'); | |
features.className += 'features' | |
newMessage.insertBefore(features, newMessage.getElementsByClassName('user')[0]) | |
} | |
// add the leaf icon before the username | |
newMessage.getElementsByClassName('features')[0].prepend(leafIcon); | |
} | |
} | |
} | |
function handleNewUser(newUser) { | |
if (newUser.getElementsByClassName('new-user-flair').length > 0) { | |
return; | |
} | |
// construct the leaf icon in preperation if this message is from a new user's account | |
const leafIcon = document.createElement('i'); | |
leafIcon.className += 'flair new-user-flair'; | |
leafIcon.style.height = '18px'; | |
leafIcon.innerHTML = LEAF_ICON_SVG; | |
const username = newUser.dataset.username; | |
// if username isn't in the current dictionary of users, re-read it from session storage to be sure | |
if (!(username in users)) { | |
updateUsers(); | |
} | |
if (username in users) { | |
const userAge = (new Date() - new Date(users[username])) / 1000 / 60 / 60 / 24; // user's account's age in days | |
if (userAge <= 7) { | |
newUser.prepend(leafIcon); | |
} | |
} | |
} | |
let users = {}; | |
function updateUsers() { | |
users = JSON.parse(sessionStorage.getItem('users') || '{}'); | |
} | |
document.addEventListener("DOMContentLoaded", function(event) { | |
updateUsers(); | |
const userListObserver = new MutationObserver((mutationList, observer) => { | |
for (const mutation of mutationList) { | |
for (const node of mutation.addedNodes) { | |
if (node.className?.includes('user-entry')) { | |
handleNewUser(node); | |
} | |
} | |
} | |
}); | |
userListObserver.observe(document.getElementById('chat-user-list'), {childList: true, subtree: true}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment