Decode base64-encoded links in some pastebins and make URLs clickable
// ==UserScript==
// @name FMHY Base64 Auto Decoder
// @namespace http://tampermonkey.net/
// @version 3.0
// @description Decode base64-encoded links in some pastebins and make URLs clickable
// @author Mumukshu D.C
// @license MIT
// @match *://rentry.co/*
// @match *://rentry.org/*
// @match *://pastes.fmhy.net/*
// @match *://bin.disroot.org/?*#*
// @match *://privatebin.net/?*#*
// @match *://textbin.xyz/?*#*
// @match *://bin.idrix.fr/?*#*
// @match *://privatebin.rinuploads.org/?*#*
// @match *://pastebin.com/*
// @grant none
// @icon https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://fmhy.net&size=64
// ==/UserScript==
(function() {
'use strict';
const Utils = {
base64Regex: /^[A-Za-z0-9+/]+={0,2}$/,
urlRegex: /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i,
isBase64(str) {
return this.base64Regex.test(str);
},
decode(str) {
try {
return atob(str);
} catch (e) {
return null;
}
},
isUrl(str) {
return this.urlRegex.test(str);
},
escapeHTML(str) {
return str.replace(/[&<>"']/g, m => ({
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
})[m]);
},
linkify(text, style = '', target = '_blank') {
const escapedText = this.escapeHTML(text);
const urlPattern = /(https?:\/\/[^\s]+)/g;
return escapedText.replace(urlPattern, (url) => {
return `<a href="${url}" target="${target}" style="${style}">${url}</a>`;
});
}
};
const HANDLERS = [
{
name: 'Pastebin',
match: /^https:\/\/pastebin\.com\/.*/,
selector: '.de1',
process: (el) => {
let text = el.textContent.trim();
if (text.startsWith('aHR0')) {
const decoded = Utils.decode(text);
if (decoded && !decoded.toLowerCase().startsWith('javascript:')) {
const originalColor = window.getComputedStyle(el).color;
el.innerHTML = Utils.linkify(decoded, `color: ${originalColor};`).replace(/\n/g, '<br>');
}
}
}
},
{
name: 'Rentry/FMHY',
match: /rentry\.(co|org)|pastes\.fmhy\.net/,
selector: (url) => /^https:\/\/rentry\.(co|org)\/fmhybase64/i.test(url) ? 'code' : 'code, p',
process: (el) => {
const content = el.textContent.trim();
if (Utils.isBase64(content)) {
const decoded = Utils.decode(content)?.trim();
if (decoded && (Utils.isUrl(decoded) || decoded.includes('http'))) {
// Prevent javascript: URLs
if (decoded.toLowerCase().startsWith('javascript:')) return;
if (!decoded.includes('\n')) {
const escaped = Utils.escapeHTML(decoded);
el.innerHTML = `<a href="${escaped}" target="_self">${escaped}</a>`;
} else {
el.innerHTML = decoded.split('\n')
.map(line => {
const trimmed = line.trim();
if (Utils.isUrl(trimmed) && !trimmed.toLowerCase().startsWith('javascript:')) {
const escaped = Utils.escapeHTML(trimmed);
return `<a href="${escaped}">${escaped}</a>`;
}
return Utils.escapeHTML(line);
})
.join('<br>');
}
}
}
}
},
{
name: 'PrivateBin',
match: /bin\.disroot\.org|privatebin\.net|textbin\.xyz|bin\.idrix\.fr|privatebin\.rinuploads\.org/,
selector: '#prettyprint',
process: (el) => {
let content = el.innerHTML.trim();
const lines = content.split('\n');
let modified = false;
const processedLines = lines.map(line => {
let target = line;
if (line.startsWith('`') && line.endsWith('`')) {
target = line.slice(1, -1);
}
if (Utils.isBase64(target)) {
const decoded = Utils.decode(target)?.trim();
if (decoded && Utils.isUrl(decoded) && !decoded.toLowerCase().startsWith('javascript:')) {
modified = true;
const escaped = Utils.escapeHTML(decoded);
return `<a href="${escaped}">${escaped}</a>`;
}
}
return line;
});
if (modified) {
el.innerHTML = processedLines.join('\n');
}
}
}
];
const Engine = {
activeHandler: null,
init() {
const url = window.location.href;
this.activeHandler = HANDLERS.find(h =>
typeof h.match === 'function' ? h.match(url) : h.match.test(url)
);
if (!this.activeHandler) return;
this.observe();
this.processAll();
},
processAll() {
const selector = typeof this.activeHandler.selector === 'function'
? this.activeHandler.selector(window.location.href)
: this.activeHandler.selector;
document.querySelectorAll(selector).forEach(el => this.processElement(el));
},
processElement(el) {
if (el.dataset.fmhyDecoded) return;
this.activeHandler.process(el);
el.dataset.fmhyDecoded = 'true';
},
observe() {
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
const selector = typeof this.activeHandler.selector === 'function'
? this.activeHandler.selector(window.location.href)
: this.activeHandler.selector;
if (node.matches(selector)) {
this.processElement(node);
}
node.querySelectorAll(selector).forEach(el => this.processElement(el));
}
});
});
});
observer.observe(document.body, { childList: true, subtree: true });
}
};
Engine.init();
})();