[油猴脚本]开发了一个一键解base64的油猴

b1xcy 2026-07-29 02:36 1

看到很多佬们都分享了自己的apikey,但很多都要多过一步base64 decode,感觉有点麻烦,就让ai跑了一个油猴一键解码并复制


// ==UserScript==
// @name linux.do 选中文本 Base64 解码
// @namespace https://linux.do/
// @version 1.0.0
// @description 在 linux.do 选中文本弹出的引用按钮栏中添加 Base64 解码按钮
// @author b1xcy
// @match https://linux.do/*
// @grant none
// @run-at document-idle
// ==/UserScript==

(function () {
"use strict";

const BUTTON_CLASS = "btn btn-icon-text btn-flat base64-decode";
const BUTTON_MARKER = "data-base64-decode-btn";

function getSelectedText() {
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) return "";
return selection.toString().trim();
}

function normalizeBase64(input) {
let text = input
.replace(/\s+/g, "")
.replace(/-/g, "+")
.replace(/_/g, "/");

// 去掉常见包装,如 data:...;base64,xxxx
const dataUrlMatch = text.match(/base64,(.+)$/i);
if (dataUrlMatch) text = dataUrlMatch[1];

// 去掉可能的引号
text = text.replace(/^["']|["']$/g, "");

// 补齐 padding
const pad = text.length % 4;
if (pad) text += "=".repeat(4 - pad);

return text;
}

function decodeBase64(input) {
const cleaned = normalizeBase64(input);
if (!cleaned) {
throw new Error("没有可解码的内容");
}

// atob 得到 binary string,再按 UTF-8 还原
const binary = atob(cleaned);
const bytes = Uint8Array.from(binary, (ch) => ch.charCodeAt(0));
try {
return new TextDecoder("utf-8", { fatal: false }).decode(bytes);
} catch (_) {
// 回退:直接返回 latin1 字符串
return binary;
}
}

async function copyText(text) {
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(text);
return true;
}
} catch (_) {
// fall through
}

const ta = document.createElement("textarea");
ta.value = text;
ta.setAttribute("readonly", "");
ta.style.cssText = "position:fixed;left:-9999px;top:-9999px;";
document.body.appendChild(ta);
ta.select();
let ok = false;
try {
ok = document.execCommand("copy");
} catch (_) {
ok = false;
}
ta.remove();
return ok;
}

function showResult(decoded, copied) {
// 简单 toast,不依赖站点 UI
let toast = document.getElementById("ld-base64-decode-toast");
if (!toast) {
toast = document.createElement("div");
toast.id = "ld-base64-decode-toast";
toast.style.cssText = [
"position:fixed",
"z-index:999999",
"left:50%",
"bottom:24px",
"transform:translateX(-50%)",
"max-width:min(720px,90vw)",
"max-height:40vh",
"overflow:auto",
"padding:12px 14px",
"border-radius:10px",
"background:#1d1c1d",
"color:#fff",
"box-shadow:0 8px 24px rgba(0,0,0,.28)",
"font:13px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif",
"white-space:pre-wrap",
"word-break:break-word",
"display:none",
].join(";");
document.body.appendChild(toast);
}

const tip = copied ? "已复制到剪贴板" : "解码成功(复制失败,请手动复制)";
toast.textContent = `${tip}\n\n${decoded}`;
toast.style.display = "block";

clearTimeout(showResult._timer);
showResult._timer = setTimeout(() => {
toast.style.display = "none";
}, 5000);
}

function showError(message) {
let toast = document.getElementById("ld-base64-decode-toast");
if (!toast) {
// 复用 showResult 的创建逻辑
showResult("", false);
toast = document.getElementById("ld-base64-decode-toast");
}
toast.textContent = `Base64 解码失败:${message}`;
toast.style.display = "block";
clearTimeout(showResult._timer);
showResult._timer = setTimeout(() => {
toast.style.display = "none";
}, 3000);
}

async function onDecodeClick(event) {
event.preventDefault();
event.stopPropagation();

const selected = getSelectedText();
if (!selected) {
showError("请先选中一段文本");
return;
}

try {
const decoded = decodeBase64(selected);
const copied = await copyText(decoded);
showResult(decoded, copied);
} catch (err) {
showError(err && err.message ? err.message : "无效的 Base64 内容");
}
}

function createDecodeButton() {
const button = document.createElement("button");
button.className = BUTTON_CLASS;
button.type = "button";
button.title = "Base64 解码";
button.setAttribute(BUTTON_MARKER, "1");
button.innerHTML = [
'<svg class="fa d-icon d-icon-code svg-icon fa-width-auto svg-string" width="1em" height="1em" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">',
'<use href="#code"></use>',
"</svg>",
' <span class="d-button-label">Base64 解码</span>',
].join("");
button.addEventListener("click", onDecodeClick);
return button;
}

function injectButton(container) {
if (!(container instanceof HTMLElement)) return;
if (!container.classList.contains("buttons")) return;
// 只处理包含引用相关按钮的弹出栏,避免误伤其它 .buttons
if (
!container.querySelector(".insert-quote, .copy-quote, .quote-button, button")
) {
return;
}
if (container.querySelector(`[${BUTTON_MARKER}]`)) return;

const btn = createDecodeButton();
// 插到「复制引用」后面;没有就追加到末尾
const copyBtn = container.querySelector(".copy-quote");
if (copyBtn && copyBtn.parentNode === container) {
copyBtn.insertAdjacentElement("afterend", btn);
} else {
container.appendChild(btn);
}
}

function scan(root = document) {
root.querySelectorAll?.(".buttons").forEach(injectButton);
if (root instanceof HTMLElement && root.matches?.(".buttons")) {
injectButton(root);
}
}

// 初次扫描
scan(document);

// Discourse 是动态渲染,监听弹层出现
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (!(node instanceof HTMLElement)) continue;
scan(node);
}
}
});

observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
})();


代码无任何外带操作,可以放心用

最新回复 (1)
  • 西红柿炒番茄 07-29 04:26
    1

    你们还要自己解码的吗,直接发给ai,让他测试,配置不就好了,我就负责重启

* 帖子来源Linux.do
返回