Arca base64 autodecoder

auto decode Base64 encoded link in Arca.live

< Arca base64 autodecoder 피드백으로 돌아가기

리뷰: 좋음 - 스크립트가 잘 작동함

§
게시: 2026-04-02

스크립트를 아카라이브가 아닌 'kone'라는 커뮤니티에서 사용할 수 있도록 수정했는데, 해당 커뮤니티에 공유해도 되는지 알고 싶습니다. 수정된 코드는 아래와 같습니다.
----------------------
// ==UserScript==
// @name Kone base64 autodecoder
// @name:ko 코네 Base64 자동 디코더
// @version 1.225
// @author Laria, OMNI7
// @match https://kone.gg/*
// @description auto decode Base64 encoded link in Kone.gg
// @description:ko 코네 내 Base64로 인코딩된 링크를 자동으로 복호화합니다.
// @icon https://www.google.com/s2/favicons?sz=64&domain=kone.gg
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
// @license MIT
// @encoding utf-8
// @run-at document-end
// @supportURL https://greasyfork.org/ko/scripts/482577
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.deleteValue
// @grant GM.registerMenuCommand
// @grant GM.unregisterMenuCommand
// @grant GM.setClipboard
// ==/UserScript==

const regexEncodedPrefixDef = [
/(aHR0cDovL|aHR0cHM6Ly|bWVnYS5ue|a2lvLmFj)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
/(YUhSMGNEb3ZM|YUhSMGNITTZMe|YldWbllTVX|YTIsdmJtR)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
/(WVVoU01HTkViM1pN|WVVoU01HTklUVFpNZ|V1d4V1JteF|V1RJeHNXWn)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
/(V1ZWb1UwMUhUa1ZpTTFwT|V1ZWb1UwMUhUa2xVVkZwTl|VmpkNFYxSn|VjFSSmVIT)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
/(VjFaV2IxVXdNVWhVYTFacFRURndU|VjFaV2IxVXdNVWhVYTJ4VlZrWndUb)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
/(VmpGYVYySXhWWGROVldoVllURmFjRlJVUm5kV|VmpGYVYySXhWWGROVldoVllUSjRWbFpyV25kVW)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
/(Vm1wR1lWWXlTWGhXV0dST1ZsZG9WbGxVUm1GalJsSlZVbTVrV|Vm1wR1lWWXlTWGhXV0dST1ZsZG9WbGxVU2pSV2JGcHlWMjVrVl)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
/(Vm0xd1IxbFdXWGxUV0doWFYwZFNUMVpzWkc5V2JHeFZVbTFHYWxKc1NsWlZiVFZyV|Vm0xd1IxbFdXWGxUV0doWFYwZFNUMVpzWkc5V2JHeFZVMnBTVjJKR2NIbFdNalZyVm)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
/(Vm0weGQxSXhiRmRYV0d4VVYwZG9XRll3WkZOVU1WcHpXa2M1VjJKSGVGWlZiVEZIWVd4S2MxTnNXbFppVkZaeV|Vm0weGQxSXhiRmRYV0d4VVYwZG9XRll3WkZOVU1WcHpXa2M1VjJKSGVGWlZNbkJUVmpKS1IyTkliRmROYWxaeVZt)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
/(Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFppVkVaSVdWZDRTMk14VG5OWGJGcHBWa1phZ|Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZad)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
/(Vm0wd2QyVkhVWGhUV0docFVtMVNXVll3WkRSV1ZsbDNXa2M1V0ZKc2JETlhhMXBQVmxVeFYyTkljRmhoTWsweFZtcEtTMU5IVmtkWGJGcHBWa1ZhU1ZkV1pEUlRNazE0Vkc1T1dHSkdjSEJXYTFwaF|Vm0wd2QyVkhVWGhUV0docFVtMVNXVll3WkRSV1ZsbDNXa2M1V0ZKc2JETlhhMXBQVmxVeFYyTkljRmhoTWsweFZtcEtTMU5IVmtkWGJGcE9ZbXRLVlZadGNFdFRNVWw1Vkd0c2FWSnRVazlaVjNoaFpWWmFk)([\w\-+\/=]*)(?=[^\+=\w\/]|$)/g,
];

let abadInternalDB = {
encodedLink: {}, decodedLink: {}, decodedList: [], hostnameSetRaw: new Set(), hostnameSet: [],
internalDB: { autoDecodingMaximum: 11, totlaDecodedCount: 0, dragDecodingEnable: false, swal2Enable: false },
};

const abadConstDB = {
regInvalid: /[^\w\+\/=]/,
logPrompt: { default: '['+GM.info.script.name+']', decodeManager: '['+GM.info.script.name+'-DEC]', paramManager: '['+GM.info.script.name+'-PAR]' },
SWAL2Title: `${('name:ko' in GM.info.script)?GM.info.script['name:ko']:GM.info.script.name} V ${GM.info.script.version}`,
};

let hindex = 0;
let lastSelected = document;
let lastSelectedTime = Date.now();
let lastUrl = location.href;

let localParameter = {
'basedepth': { 'param_name': 'basedepth', 'value': 3, 'def_value': 3 },
'enclinkhide': { 'param_name': 'enclinkhide', 'value': false, 'def_value': false },
'draggable': { 'param_name': 'draggable', 'value': false, 'def_value': false },
'expandmenu': { 'param_name': 'expandmenu', 'value': true, 'def_value': true },
};

let menuStructure = {
'basedepth': { 'param_name': localParameter.basedepth, 'name': '🎛 base64 깊이 조절하기', 'desc': '자동 base64 디코딩 깊이를 조절할 수 있습니다.', 'id': -1, 'func': menuFunctionBasedepth, 'visible': true },
'enclinkhide': { 'param_name': localParameter.enclinkhide, 'name': '🔗 인코딩된 링크 [보이기/숨기기]', 'desc': '', 'id': -1, 'func': menuFunctionEnchide, 'visible': true },
'draggable': { 'param_name': localParameter.draggable, 'name': '🖱 드래그 시 자동 디코딩 [켜기/끄기]', 'desc': '', 'id': -1, 'func': menuFunctionDraggable, 'visible': true },
'resetdefaults': { 'param_name': null, 'name': '🛠 스크립트 기본값 초기화', 'desc': '', 'id': -1, 'func': menuFunctionRstDefaults, 'visible': true },
'expandmenu': { 'param_name': localParameter.expandmenu, 'name': '⚙️ 스크립트 메뉴 [축소/확장]', 'desc': '', 'id': -1, 'func': menuFunctionChangeExpandMode, 'visible': true },
};

let _eventHandlers = {};
const addListener = (node, event, handler, capture = false) => {
if (!(event in _eventHandlers)) _eventHandlers[event] = [];
_eventHandlers[event].push({ node: node, handler: handler, capture: capture });
node.addEventListener(event, handler, capture);
};

function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }
function getLocation(href) {
var match = href.toString().match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/);
return match && { href: href, hostname: match[3] };
}
function createElemID() { return 'abad_'+self.crypto.randomUUID(); }
function base64AddPadding(str) { return str + Array((4 - str.length % 4) % 4 + 1).join('='); }

const Base64 = {
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
decode : function (input) {
let output = ""; let chr1, chr2, chr3; let enc1, enc2, enc3, enc4; let i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++)); enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++)); enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) output = output + String.fromCharCode(chr2);
if (enc4 != 64) output = output + String.fromCharCode(chr3);
}
return this._utf8_decode(output);
},
_utf8_decode : function (utftext) {
let string = ""; let i = 0; let c = 0, c2 = 0, c3 = 0;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) { string += String.fromCharCode(c); i++; }
else if ((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i+1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; }
else { c2 = utftext.charCodeAt(i+1); c3 = utftext.charCodeAt(i+2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; }
}
return string;
}
};

const scrollToTarget = function(id_tmp, target) {
let targetElem = document.getElementById(id_tmp);
if (!targetElem) {
const pc = document.querySelector("main #post_content");
if (pc && pc.shadowRoot) targetElem = pc.shadowRoot.getElementById(id_tmp);
}
if (!targetElem) return;

if (abadInternalDB.internalDB.swal2Enable) {
Swal.close();
Swal.fire({
icon: 'success', title: abadConstDB.SWAL2Title,
html: `${(target==undefined)?'해당':target} 위치로 이동했습니다.`, toast: true, position: 'top-end', timer: 3500, timerProgressBar: true, showConfirmButton: false
});
}
targetElem.style.background = '#06ff004f';
window.scrollTo({top:window.pageYOffset + targetElem.getBoundingClientRect().top - (window.innerHeight / 2), behavior:'smooth'});
sleep(2750).then(() => { targetElem.style.background = null; targetElem.style.transition = "all 1s"; });
};

function copyToClipboard(target, cont) {
let msgHeader = '';
if (cont != undefined) msgHeader = `${cont}이(가) `;
if (target == undefined) {
console.warn('Copy target does not exist');
} else {
try {
GM.setClipboard(target);
if (abadInternalDB.internalDB.swal2Enable) {
let timerInterval;
Swal.fire({
title: abadConstDB.SWAL2Title,
html: `${msgHeader}클립보드로 복사되었습니다.

또는 아래 코드를 복사:
${target}

`,
confirmButtonText: '확인',
icon: 'success',
timer: 3000,
timerProgressBar: true,
footer: ` `,
didOpen: (modal) => {
let autoClose = true;
modal.onmouseenter = () => {
autoClose = false;
Swal.stopTimer();
modal.querySelector("#footer").innerHTML = `창에서 마우스를 떼면 일정시간 후 자동으로 닫힙니다.`;
};
modal.onmouseleave = () => {
autoClose = true;
Swal.resumeTimer();
};
timerInterval = setInterval(() => {
if (autoClose) {
let timeLeft = Swal.getTimerLeft();
let seconds = isNaN(Math.floor(timeLeft / 1000)) ? '0' : Math.floor(timeLeft / 1000);
modal.querySelector("#footer").innerHTML = `약 ${seconds}초 후 창이 자동으로 닫힙니다.`;
}
}, 100);
},
willClose: () => { clearInterval(timerInterval); },
});
} else {
window.alert(msgHeader + '클립보드로 복사되었습니다.');
}
} catch (e) {
console.warn('Copy failed:', e);
}
}
}

function showEncodedLink(event) {
event.preventDefault();
event.stopPropagation();

const self = event.currentTarget;
if (abadInternalDB.encodedLink.hasOwnProperty(self.id)) {
const rawLink = abadInternalDB.encodedLink[self.id].raw;
if (!abadInternalDB.encodedLink[self.id].isEnabled) {
self.innerHTML = rawLink;
self.style.color = '#4758bc';
self.style.textDecoration = 'none';
self.title = '디코딩 전 인코딩된 링크입니다. 클릭 시 내용이 복사됩니다.';
abadInternalDB.encodedLink[self.id].isEnabled = true;
} else { copyToClipboard(rawLink, '인코딩된 코드'); }
}
}

function showDecodeSummary(event) {
if (abadInternalDB.internalDB.swal2Enable) {
const decodedLinkListWrapper = createElemID();
Swal.fire({
title: abadConstDB.SWAL2Title,
html: `이 페이지에서 디코딩된 링크 목록

불러오는중...

`,
confirmButtonText: '닫기',
didOpen: (modal) => {
Swal.showLoading();
sleep(100).then(() => {
modal.querySelector('#'+decodedLinkListWrapper).innerHTML = '';
Object.keys(abadInternalDB.decodedLink).forEach(function(targetRaw) {
const target = abadInternalDB.decodedLink[targetRaw];
let cont = document.createElement("p");
cont.style.marginBottom = '0.3rem'; cont.style.whiteSpace = 'nowrap';

const elemGotoLocation = document.createElement("a");
elemGotoLocation.id = createElemID(); elemGotoLocation.innerHTML = `[클릭 시 해당 위치로 이동]`; elemGotoLocation.href = "javascript:void(0);";

const contLink = document.createElement("a");
contLink.href = target.href; contLink.target = "_blank"; contLink.innerHTML = `> ${target.no}번째 링크 (${target.hostname})`;

cont.appendChild(contLink); cont.appendChild(document.createTextNode(" - ")); cont.appendChild(elemGotoLocation);
modal.querySelector('#'+decodedLinkListWrapper).appendChild(cont);
addListener(elemGotoLocation, 'click', function() { scrollToTarget(target.id, `${target.no}번째 링크`); });
});
Swal.hideLoading();
});
}
});
}
}

function createEncodedLink(src) { return `[ ${src.toString()} ]`; }
function createMaskEncodedLink(src, genMode, uuid) {
abadInternalDB.encodedLink[uuid] = { type: genMode, raw: src, isShown: false };
return `클릭 시 인코딩된 코드 보기`;
}

function createLink(src, index, url, depth, genMode, uuid, parentuuid, hidelink = false) {
abadInternalDB.hostnameSetRaw.add(url.hostname);
return `${index.toString()}번째 링크 (base64 깊이: ${depth.toString()}) (${url.hostname}) ${(hidelink?createEncodedLink(createMaskEncodedLink(src, genMode, parentuuid)):createEncodedLink(src))}`;
}

function replacerGen(numIter, genMode) {
return function(source) {
try {
let rstring = "";
let converted = Base64.decode(base64AddPadding(source));
for (let i=0; i {
let trimmed = item.trim();
if (trimmed.startsWith('mega.nz') || trimmed.startsWith('kio.ac')) return 'https://' + trimmed;
return trimmed;
});

const registerDecodedLink = function(_target, _uuid, _parentuuid) {
abadInternalDB.decodedLink[_uuid] = { id: _uuid, no: hindex, type: genMode, hostname: _target.hostname, title: _target.href, href: _target.href, srcid: _parentuuid };
abadInternalDB.decodedList.push(_uuid);
};

if (converted.length == 1) {
const uuid = createElemID(); const parentuuid = createElemID();
const url_t = getLocation(converted[0]);
if(!url_t) throw new Error("Invalid URL");
registerDecodedLink(url_t, uuid, parentuuid);
rstring += createLink(source, hindex, url_t, numIter+1, genMode, uuid, parentuuid, !localParameter.enclinkhide.value);
} else {
const parentuuid = createElemID();
rstring += createEncodedLink(localParameter.enclinkhide.value?source.toString():createMaskEncodedLink(source.toString(), genMode, parentuuid));
let nindex = 1; const hindexPrev = hindex;
converted.forEach(function(i) {
if (i != '') {
const uuid = createElemID(); const url_t = getLocation(i);
if(url_t) {
registerDecodedLink(url_t, uuid, parentuuid);
rstring += `
${createLink(`링크 자동 분할 : ${nindex.toString()}번째`, hindex, url_t, numIter+1, genMode, uuid, parentuuid)}`;
hindex++; nindex++;
}
}
});
hindex--; nindex--;
rstring = `분할된 링크 총 ${nindex.toString()}개 ${rstring}`;
}
return rstring;
} catch(e) { return `[ base64 변환 실패: ${source.toString()}]`; }
};
}

function selClicked(event) {
const sel = document.getSelection().toString();
if (!sel.match(abadConstDB.regInvalid) && sel.length >= 10 && lastSelectedTime + 200 < Date.now()) {
try {
let converted = decodeURI(encodeURI(Base64.decode(base64AddPadding(sel))).replaceAll('%00', ''));
converted = converted.trim();
if (converted.startsWith('mega.nz') || converted.startsWith('kio.ac')) converted = 'https://' + converted;
this.innerHTML = `${this.innerHTML.replace(sel, converted)}`;
} catch (e) {} finally { this.removeEventListener('click', selClicked); }
}
}

function activateDragDecoding() {
if (abadInternalDB.internalDB.dragDecodingEnable) return;
abadInternalDB.internalDB.dragDecodingEnable = true;
document.addEventListener('selectionchange', function() {
let sel = document.getSelection().anchorNode;
if (sel) {
sel = sel.parentElement;
if (sel != lastSelected) {
lastSelected.removeEventListener('click', selClicked);
sel.addEventListener('click', selClicked);
lastSelected = sel; lastSelectedTime = Date.now();
}
}
});
}

function menuStructureUpdate(fistRun = false) {
localParameter.basedepth.value = localParameter.basedepth.value > abadInternalDB.internalDB.autoDecodingMaximum ? abadInternalDB.internalDB.autoDecodingMaximum : localParameter.basedepth.value;

menuStructure.basedepth.name = '🎛 base64 깊이 조절하기 - 현재 값 : '+localParameter.basedepth.value+'회';
menuStructure.enclinkhide.name = '🔗 인코딩된 링크 '+(localParameter.enclinkhide.value?'숨기기':'보이기');
menuStructure.draggable.name = '🖱 드래그 시 자동 디코딩 '+(localParameter.draggable.value?'끄기':'켜기');
menuStructure.expandmenu.name = '⚙️ 스크립트 메뉴 '+(localParameter.expandmenu.value?'축소':'확장');

if (!fistRun) {
Object.keys(menuStructure).forEach(function(i) {
try { GM.unregisterMenuCommand(menuStructure[i].id); } catch(_) {}
});
}

try {
if(localParameter.expandmenu.value) {
Object.keys(menuStructure).forEach(function(i) {
if (menuStructure[i].visible) {
menuStructure[i].id = GM.registerMenuCommand(menuStructure[i].name, menuStructure[i].func, {title:menuStructure[i].desc});
}
});
} else {
menuStructure.expandmenu.id = GM.registerMenuCommand(menuStructure.expandmenu.name, menuStructure.expandmenu.func, {title:menuStructure.expandmenu.desc});
}
} catch(e) {}
}

function menuFuncSubPageReload(showmsg) {
if (abadInternalDB.internalDB.swal2Enable) {
Swal.fire({
title: abadConstDB.SWAL2Title,
html: `${showmsg}

> 반영을 위해 사이트 새로고침이 필요합니다.
사이트를 새로고침할까요?
`,
icon: 'info',
showCancelButton: true,
confirmButtonColor: '#3085d6',
confirmButtonText: '새로고침',
cancelButtonText: '취소',
}).then((result) => {
if (result.isConfirmed) window.location.reload(true);
});
}
}

function menuFunctionBasedepth() {
menuStructureUpdate();
const previousValue = localParameter.basedepth.value;
const str_common_1 = ' ( 지정 가능한 범위: 1~'+abadInternalDB.internalDB.autoDecodingMaximum.toString()+' )';

if (abadInternalDB.internalDB.swal2Enable) {
const slideHandler = function(event) {
const target = Swal.getPopup().querySelector("#footer");
if (event.target.value > 7) {
target.style.display = 'block';
target.innerHTML = `(값을 너무 크게 지정하면 브라우저 성능에 영향을 줄 수 있습니다.)`;
} else { target.style.display = 'none'; }
};
Swal.fire({
title: abadConstDB.SWAL2Title,
icon: "question",
input: "range",
html: `Base64 자동 디코딩 중첩 횟수를 얼마로 지정할까요?

(인코딩을 인코딩한 것을 여러번 반복한 것을 자동으로 풀어냅니다.)
현재 값: ${previousValue.toString()}회,${(previousValue == 3 ? '' : ' 기본값: 3회,')}${str_common_1}`,
inputAttributes: { min: "1", max: abadInternalDB.internalDB.autoDecodingMaximum.toString(), step: "1" },
footer: `${(previousValue > 7)?'(값을 너무 크게 지정하면 브라우저 성능에 영향을 줄 수 있습니다.)':''}`,
inputValue: previousValue,
showCancelButton: true,
confirmButtonColor: '#3085d6',
confirmButtonText: '변경',
cancelButtonText: '취소',
inputValidator: (value) => {
return new Promise((resolve) => {
if (value == previousValue) { resolve(`기존값과 동일합니다, 현재 값: ${previousValue}회`); } else { resolve(); }
});
},
didOpen: (modal) => { modal.querySelector(".swal2-range").firstChild.addEventListener('input', slideHandler, false); },
willClose: (modal) => { modal.querySelector(".swal2-range").firstChild.removeEventListener('input', slideHandler); },
}).then((result) => {
if (result.isConfirmed) {
const targetValue = parseInt(result.value);
localParameter.basedepth.value = targetValue;
try {
GM.setValue(localParameter.basedepth.param_name, targetValue);
menuFuncSubPageReload('자동 디코딩 중첩 횟수가 '+previousValue.toString()+'에서 '+targetValue.toString()+'(으)로
변경이 완료되었습니다.');
} catch(e) { localParameter.basedepth.value = previousValue; }
finally { menuStructureUpdate(); }
}
});
}
}

function menuFunctionEnchide() {
menuStructureUpdate();
const currentState = localParameter.enclinkhide.value;
if (abadInternalDB.internalDB.swal2Enable) {
Swal.fire({
title: abadConstDB.SWAL2Title,
html: `디코딩 시 인코딩된 링크를 ${(currentState?'숨기시':'표시하')}겠습니까?

(앞으로 디코딩 전 인코딩된 링크를
"${(currentState?'클릭 시 기존링크 보기':'aHR0cHM6Ly9hcmNhLmx..')}"와 같은 형태로
보여줍니다.)
`,
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: (currentState?'숨기기':'표시하기'),
cancelButtonText: '취소',
}).then((result) => {
if (result.isConfirmed) {
const targetState = !currentState;
localParameter.enclinkhide.value = targetState;
try {
GM.setValue(localParameter.enclinkhide.param_name, targetState);
menuFuncSubPageReload('앞으로 인코딩된 링크를 '+(targetState?'표시합':'숨깁')+'니다.');
} catch(e) { localParameter.enclinkhide.value = currentState; }
finally { menuStructureUpdate(); }
}
});
}
}

function menuFunctionDraggable() {
menuStructureUpdate();
const currentState = localParameter.draggable.value;
if (abadInternalDB.internalDB.swal2Enable) {
Swal.fire({
title: abadConstDB.SWAL2Title,
html: `드래그 시 자동 디코딩을 ${(currentState?'비':'')}활성화 하시겠습니까?

(앞으로 인코딩된 부분을 드래그${(currentState?'해도
자동으로 디코딩되지 않습':' 시 Base64로 인코딩된
것으로 판단 되면 자동으로 디코딩을 시도합')}니다.)
${(currentState?'':'

(이 기능은 작동이 불안정할 수 있습니다.)')}`,
icon: 'question',
showDenyButton: true,
showCancelButton: !currentState,
confirmButtonColor: '#3085d6',
confirmButtonText: (currentState?'비활성화':'활성화'),
denyButtonText: '취소',
cancelButtonText: '이번에만 활성화',
}).then((result) => {
if (result.isConfirmed) {
const targetState = !currentState;
localParameter.draggable.value = targetState;
try {
GM.setValue(localParameter.draggable.param_name, targetState);
if (targetState) {
activateDragDecoding();
Swal.fire({ icon: 'success', title: abadConstDB.SWAL2Title, html: `앞으로 드래그 시 자동 디코딩을 진행합니다.`, toast: true, position: 'center', timer: 2000, timerProgressBar: true, showConfirmButton: false });
} else { menuFuncSubPageReload('앞으로 드래그 해도 반응하지 않습니다.'); }
} catch(e) { localParameter.draggable.value = currentState; }
finally { menuStructureUpdate(); }
} else if (result.isDismissed && result.dismiss === Swal.DismissReason.cancel) {
try {
activateDragDecoding();
Swal.fire({ icon: 'success', title: abadConstDB.SWAL2Title, html: `드래그 시 자동 디코딩이 활성화되었습니다.
새로고침 시 자동으로 비활성화됩니다.`, toast: true, position: 'center', timer: 2000, timerProgressBar: true, showConfirmButton: false });
} catch(e) {}
}
});
}
}

function menuFunctionChangeExpandMode() {
menuStructureUpdate();
const currentState = localParameter.expandmenu.value;
if (abadInternalDB.internalDB.swal2Enable) {
Swal.fire({
title: abadConstDB.SWAL2Title,
html: `메뉴에 나타나는 항목을 ${(currentState?'줄일':'늘릴')}까요?

(앞으로 세부설정 메뉴가 ${(currentState?'숨겨':'보여')}집니다.)`,
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: (currentState?'줄이기':'표시하기'),
cancelButtonText: '취소',
}).then((result) => {
if (result.isConfirmed) {
const targetState = !currentState;
localParameter.expandmenu.value = targetState;
try {
GM.setValue(localParameter.expandmenu.param_name, targetState);
Swal.fire({ icon: 'success', title: abadConstDB.SWAL2Title, html: `앞으로 세부설정 메뉴가 ${(targetState?'보여':'숨겨')}집니다.`, toast: true, position: 'center', timer: 2000, timerProgressBar: true, showConfirmButton: false });
} catch(e) { localParameter.expandmenu.value = currentState; }
finally { menuStructureUpdate(); }
}
});
}
}

function menuFunctionRstDefaults() {
menuStructureUpdate();
if (abadInternalDB.internalDB.swal2Enable) {
Swal.fire({
title: abadConstDB.SWAL2Title,
html: `정말 스크립트 설정을 기본값으로 초기화하시겠습니까?

(초기화 완료 후 자동으로 새로고침됩니다.)`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
focusCancel: true,
confirmButtonText: '초기화 진행',
cancelButtonText: '취소',
showLoaderOnConfirm: true,
timer: 10000,
timerProgressBar: true,
didOpen: (modal) => {
modal.onmouseenter = Swal.stopTimer;
modal.onmouseleave = Swal.resumeTimer;
},
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: abadConstDB.SWAL2Title,
html: `설정값을 제거중입니다, 잠시만 기다려주세요..`,
footer: `1분 이내로 이 창이 사라지지 않으면 수동으로 새로고침해주세요.`,
didOpen: () => { Swal.showLoading(); },
showConfirmButton: false, allowOutsideClick: false, allowEscapeKey: false, allowEnterKey: false,
});
try {
Object.keys(menuStructure).forEach(function(i) { try { GM.unregisterMenuCommand(menuStructure[i].id); } catch(_) {} });
for (const i of Object.keys(localParameter)) { GM.deleteValue(localParameter[i].param_name); }
sleep(250).then(() => {
Swal.fire({
title: abadConstDB.SWAL2Title,
html: `설정값이 모두 제거되었습니다.

(확인 후 현재 창이 자동으로 새로고침됩니다.)`,
footer: `비정상적으로 동작 시 스크립트를 재설치해주세요.`,
icon: 'success',
confirmButtonColor: '#3085d6',
confirmButtonText: '확인',
didOpen: () => { Swal.hideLoading(); },
}).then(() => { window.location.reload(true); });
});
} catch(e) { Swal.close(); }
}
});
}
}

function processTextNode(node) {
let text = node.textContent;
if (!text || !text.trim()) return false;

for (let i = 0; i < localParameter.basedepth.value; i++) {
let regex = new RegExp(regexEncodedPrefixDef[i].source, regexEncodedPrefixDef[i].flags);
let match = regex.exec(text);
if (match) {
let sourceStr = match[0];
let start = match.index;

let afterStart = node.splitText(start);
let afterEnd = afterStart.splitText(sourceStr.length);

let htmlString = replacerGen(i, 'article')(sourceStr);

let wrapper = document.createElement('span');
wrapper.className = 'abad-generated';
wrapper.innerHTML = htmlString;

afterStart.replaceWith(wrapper);

wrapper.querySelectorAll('[id^="abad_"]').forEach(el => {
if (abadInternalDB.encodedLink.hasOwnProperty(el.id)) {
el.addEventListener('click', showEncodedLink);
}
});

processTextNode(afterEnd);
return true;
}
}
return false;
}

function hasSummaryBox() {
if (document.getElementById('abad_summary_box')) return true;
const pc = document.querySelector("main #post_content");
if (pc && pc.shadowRoot && pc.shadowRoot.getElementById('abad_summary_box')) return true;
return false;
}

function executeDecoder() {
let containers = [];
let pc = document.querySelector("main #post_content");
if (pc && pc.shadowRoot) containers.push(pc.shadowRoot);
else if (pc) containers.push(pc);
document.querySelectorAll('main .grow.flex .grow.grid, main .md\\:items-center').forEach(e => containers.push(e));

let processedAny = false;

containers.forEach(container => {
const textTagNames = 'p, span, div, a, li, h1, h2, h3, h4, h5, h6, em, strong, td, code, pre, blockquote';
const elements = Array.from(container.querySelectorAll(textTagNames));
elements.push(container);

elements.forEach(el => {
if (typeof el.closest === 'function' && el.closest('.abad-generated')) return;

const textNodes = Array.from(el.childNodes).filter(n => n.nodeType === Node.TEXT_NODE);
textNodes.forEach(node => {
if (processTextNode(node)) processedAny = true;
});
});
});

if (processedAny && hindex > 0 && !hasSummaryBox()) {
abadInternalDB.hostnameSet = Array.from(abadInternalDB.hostnameSetRaw).sort();

let result = document.createElement("div");
result.id = 'abad_summary_box';
result.style.margin = '10px 0';
result.style.paddingTop = '7px';

let result_box = document.createElement("span");
result_box.style.cursor = 'pointer';
let result_in = '
';
result_box.title = '클릭 시 디코딩된 링크를 한번에 볼 수 있습니다.';
result_in += `총 ${hindex}개의 링크가 자동 디코딩되었습니다.
( 감지된 사이트 종류: ${abadInternalDB.hostnameSet.length}개 )`;
result_in += `

(여기를 클릭하여 전체 목록 보기)

`;
result_in += '
';
result_box.innerHTML = result_in;
result_box.addEventListener('click', showDecodeSummary);

result.appendChild(result_box);
result.appendChild(document.createElement("hr"));

let insertTarget = document.querySelector("main #post_content");
if (insertTarget && insertTarget.shadowRoot) {
insertTarget.shadowRoot.insertBefore(result, insertTarget.shadowRoot.firstChild);
} else if (insertTarget && insertTarget.parentNode) {
insertTarget.parentNode.insertBefore(result, insertTarget);
} else {
let fallbackTarget = document.querySelector('main .grow.flex .grow.grid') || document.querySelector('main');
if (fallbackTarget && fallbackTarget.parentNode) {
fallbackTarget.parentNode.insertBefore(result, fallbackTarget);
}
}
}
}

function debounce(func, delay) {
let timer;
return function() { clearTimeout(timer); timer = setTimeout(func, delay); };
}

(async () => {
if (window.Swal) {
const styleSA2 = document.createElement('style');
styleSA2.textContent = '.swal2-container { z-index: 2400; }';
document.head.appendChild(styleSA2);
abadInternalDB.internalDB.swal2Enable = true;
}

for (const i of Object.keys(localParameter)) {
localParameter[i].value = await GM.getValue(localParameter[i].param_name, localParameter[i].def_value);
}
menuStructureUpdate(true);
if (localParameter.draggable.value) activateDragDecoding();

executeDecoder();

const debouncedExecute = debounce(() => {
if (lastUrl !== location.href) {
lastUrl = location.href;
hindex = 0;
abadInternalDB.decodedLink = {};
abadInternalDB.encodedLink = {};
abadInternalDB.hostnameSetRaw.clear();
const pc = document.querySelector("main #post_content");
let oldBox = document.getElementById('abad_summary_box');
if (!oldBox && pc && pc.shadowRoot) oldBox = pc.shadowRoot.getElementById('abad_summary_box');
if (oldBox) oldBox.remove();
}
executeDecoder();
}, 400);

const observer = new MutationObserver(debouncedExecute);
observer.observe(document.body, { childList: true, subtree: true });
})();

§
게시: 2026-04-02

개인 메세지로 보내려고 했는데 글자 수가 10000자 제한이라서 여기에서 여쭈어 봅니다.

답글 게시

답글을 게시하려면 로그인하세요.