Here's the script that saves the state of the elements as they appear (saveMessage function), and then uses MutationObserver to monitor the changes in the className of an element and to restore it's saved state (resetMessage function). Actually it was working in Firefox until one of the recent patches (v.104 I believe) and wasn't working in Chrome for at last a year (the text of the elements with className 'chat-line__message--deleted-notice' won't be restored, just nothing happens). Now it just does seemingly nothing in FF, but because it was working in FF and not working in Chrome at the same time, I assume it must be some very small deviation from the standard or something like that. Any ideas what's wrong with it?
// ==UserScript==
// @name Twitch: <message deleted>
// @namespace https://greasyfork.org/users/221926
// @version 1.3
// @description show deleted messages in twitch chat
// @match https://www.twitch.tv/*
// @grant none
// @run-at document-end
// ==/UserScript==
(function () {
'use strict'
function saveMessage (el) {
if (el.initialChildNodes) return
el.initialChildNodes = Array.from(el.childNodes)
}
function resetMessage (el) {
while (el.initialChildNodes == null) { el = el.parentNode }
while (el.lastChild) { el.removeChild(el.lastChild) }
el.initialChildNodes.forEach(childNode => el.appendChild(childNode))
el = el.closest('.chat-line__message')
el.style.backgroundColor = 'rgba(255, 0, 0, .1)'
el.style.boxShadow = 'inset 4px 0 0 0 rgba(255, 0, 0, .4)'
}
new MutationObserver(mutationList => {
mutationList.forEach(mutation => {
Array.from(mutation.addedNodes).forEach(el => {
switch (el.className) {
case 'chat-line__message': saveMessage(el.querySelector('.chat-line__username-container').parentNode); break
case 'chat-line__message--deleted-notice': resetMessage(el); break
}
})
})
}).observe(document.body, { childList: true, subtree: true, characterData: true })
})()
It looks like Twitch now replaces the whole .chat-line__message
node, so you can't save the initial state in the node itself any longer. However, there is a solution: In this case replacing the element means that you get a mutation with a removal of a chat-line__message
node followed by an added chat-line__message
node.
The following code saves that removed node, then, when the new deleted message node is added, restores the old node before the new deleted message node and deletes that new node.
setting node.isrestored
prevents a loop, because our code als triggers the MutationObserver.
(function () {
'use strict'
var previousMutationRemoved;
function resetMessage (el) {
if (previousMutationRemoved == null) return;
previousMutationRemoved.style.backgroundColor = 'rgba(255, 0, 0, .1)';
previousMutationRemoved.style.boxShadow = 'inset 4px 0 0 0 rgba(255, 0, 0, .4)';
previousMutationRemoved.isrestored = 'true';
el.parentNode.insertBefore(previousMutationRemoved.cloneNode(true), el);
previousMutationRemoved = null;
el.isrestored = 'true';
el.parentNode.removeChild(el);
}
new MutationObserver(mutationList => {
mutationList.forEach(mutation => {
Array.from(mutation.removedNodes).forEach(node => {
if (node.nodeType == Node.ELEMENT_NODE) {
if (node.className == 'chat-line__message' && node.isrestored != 'true') {
previousMutationRemoved = node;
}
}
});
Array.from(mutation.addedNodes).forEach(node => {
if (node.nodeType == Node.ELEMENT_NODE) {
if (node.className == 'chat-line__message' && node.isrestored != 'true') {
if(node.querySelector(".chat-line__message--deleted-notice")) {
resetMessage(node);
}
}
}
})
});
previousMutationRemoved = null;
}).observe(document.body, { childList: true, subtree: true, characterData: true })
})()