// ==UserScript==
// @name Decode nothing strings on Voz
// @namespace Decode nothing strings on Voz
// @version 2.1
// @icon https://www.google.com/s2/favicons?sz=64&domain=voz.vn
// @match https://voz.vn/t/*
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
const validCharsRegex = /^[\w\s.,!?@#$%^&*()_+=[\]{}|;:'",<>\-\/`~]*$/;
// Escape special characters in regex
const escapeRegExp = string => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
// Decode Base64 and validate
const decodeBase64 = base64String => {
try {
const decodedString = atob(base64String);
if (validCharsRegex.test(decodedString)) {
return decodedString;
} else {
console.log(`Base64 decoded string contains invalid characters: ${decodedString}`);
return base64String;
}
} catch (e) {
console.error(`Error decoding Base64 string: ${base64String}`, e);
return base64String;
}
};
// Decode Hex and validate
const decodeHex = hexString => {
try {
const cleanHexString = hexString.replace(/\s+/g, '');
let decodedString = '';
for (let i = 0; i < cleanHexString.length; i += 2) {
const charCode = parseInt(cleanHexString.substr(i, 2), 16);
if (isNaN(charCode)) {
console.error(`Invalid hex byte: ${cleanHexString.substr(i, 2)}`);
return hexString;
}
decodedString += String.fromCharCode(charCode);
}
if (validCharsRegex.test(decodedString)) {
return decodedString;
} else {
console.log(`Hex decoded string contains invalid characters: ${decodedString}`);
return hexString;
}
} catch (e) {
console.error(`Error decoding Hex string: ${hexString}`, e);
return hexString;
}
};
// Decode content with given regex and decode function
const decodeContent = (elements, regex, decodeFunc) => {
elements.forEach(element => {
if (element.classList.contains('decoded') || element.closest('.bbCodeCode')) {
return;
}
let content = element.innerHTML;
let newContent = content;
const matches = content.match(regex);
if (matches) {
let hasChanges = false;
matches.forEach(match => {
const decoded = decodeFunc(match);
if (decoded !== match) {
newContent = newContent.replace(new RegExp(escapeRegExp(match), 'g'), decoded);
hasChanges = true;
}
});
if (hasChanges) {
element.innerHTML = newContent;
console.log(`Updated content for element:`, element);
}
}
});
};
// Main function to perform decoding
const main = () => {
console.log('Starting decoding process...');
const elements = document.querySelectorAll('.bbWrapper');
decodeContent(elements, /[A-Za-z0-9+/]{16,}={0,2}/g, decodeBase64);
decodeContent(elements, /(?:[0-9A-Fa-f]{2}\s*){4,}/g, decodeHex);
elements.forEach(element => element.classList.add('decoded'));
console.log('Decoding process completed.');
};
// Execute main function on script load
main();
// Create a MutationObserver to call main on DOM changes
const observer = new MutationObserver(mutationsList => {
let hasChanges = false;
for (const mutation of mutationsList) {
if (mutation.type === 'childList' && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)) {
hasChanges = true;
break;
}
}
if (hasChanges) {
console.log('DOM changes detected. Re-running main function...');
main(); // Call main function if there are added or removed nodes
}
});
// Observe changes in the entire document
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
console.log('MutationObserver is set up to monitor DOM changes.');
})();