Automatically decodes Base64 content in mod pages and converts valid URLs into clickable links on arca.live.
// ==UserScript==
// @name arca.live Auto Base64 Decoder
// @description Automatically decodes Base64 content in mod pages and converts valid URLs into clickable links on arca.live.
// @description 모드 페이지의 Base64 콘텐츠를 자동으로 디코딩하고 유효한 URL을 arca.live에서 클릭 가능한 링크로 변환합니다.
// @description 自动解码 MOD 页面中的 Base64 内容,并将有效 URL 转换为 arca.live 上的可点击链接。
// @version 0.4
// @author
// @match *://arca.live/b/*/*
// @grant none
// @run-at document-end
// @license MIT
// @namespace https://greasyfork.org/users/1350640
// ==/UserScript==
function decodeBase64(str) {
try {
let padding = str.length % 4;
if (padding > 0) {
str += '='.repeat(4 - padding);
}
return decodeURIComponent(escape(window.atob(str)));
} catch {
return str;
}
}
function isValidUrl(string) {
try {
new URL(string);
return true;
} catch (_) {
return false;
}
}
function processTextNode(node) {
if (node.nodeType === Node.TEXT_NODE) {
let text = node.textContent;
const base64Pattern = /[A-Za-z0-9+/]{4,}={0,2}/g;
let updatedText = text.replace(base64Pattern, (match) => {
let decoded = decodeBase64(match);
decoded = decoded.replace(/=+$/, ''); // Remove any remaining padding
if (decoded !== match) {
if (isValidUrl(decoded)) {
return `<a href="${decoded}" target="_blank">${decoded}</a>`;
}
return decoded;
}
return match; // Return the original match if no valid decoding occurs
});
if (updatedText !== text) {
// Safely update the text content without altering the HTML structure
let tempElement = document.createElement('span');
tempElement.innerHTML = updatedText;
node.parentNode.replaceChild(tempElement, node);
}
}
}
function traverseNodes(node) {
if (node.nodeType === Node.ELEMENT_NODE) {
// Process child nodes within elements
node.childNodes.forEach(traverseNodes);
} else {
processTextNode(node);
}
}
// Check if the current page is a thread page
function isThreadPage() {
return window.location.href.match(/https:\/\/arca\.live\/b\/[^\/]+\/\d+/);
}
(function() {
'use strict';
if (isThreadPage()) {
// Select elements with both "article-body" and "message" classes
let elements = document.querySelectorAll('.article-body, .message');
elements.forEach(element => {
traverseNodes(element);
});
}
})();