kone
sub_icon

소미소프트

코네 base64 오토 디코더

2026-06-03 10:36:34
유틸
조회 30335 · 좋아요 22

클로드는 신이야!

1

딱 코네에서 base64 디코딩만하고 url만 하이퍼링크화 시켜주는 게 필요해서

클로드 한발 뽑아주니 원큐에 되더라

tampermonkey나 viloetmonkey에 추가해주면 됨

// ==UserScript==
// @name         Base64 Auto Decoder
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  영역의 base64 인코딩 텍스트만 자동 디코딩(최대 5회 중첩)하여 해당 부분만 대체하고, 디코딩된 텍스트의 URL은 하이퍼링크 처리
// @author       you
// @match        *://*/*
// @run-at       document-idle
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  /* ============================== 설정 ============================== */
  // CSS 셀렉터로 지정 → id면 '#post-article', class면 '.post-comment' 처럼 쓰면 됨
  // 댓글이 여러 개로 반복되는 구조여도 querySelectorAll 로 모두 처리됨
  const TARGET_SELECTORS = ['#post-article', '#post-comment', '.article-body', '.title'];
  const MAX_DECODE = 5;   // 중첩 디코딩 최대 반복 횟수
  const MIN_B64_LEN = 8;  // base64 후보로 볼 최소 길이 (짧을수록 오탐↑)

  // 텍스트 안에서 base64 "후보"를 찾아내는 정규식
  const B64_CANDIDATE = new RegExp('[A-Za-z0-9+/]{' + MIN_B64_LEN + ',}={0,2}', 'g');
  // URL 추출용 정규식 (http/https/www)
  const URL_REGEX = /((?:https?:\/\/|www\.)[^\s<>"'()]+)/gi;

  /* ============================== 유틸 ============================== */

  // 문자열이 base64 형태인지 1차 판별 (문자셋 + 길이). 패딩 유무 모두 허용.
  // base64 길이는 %4가 1일 수 없음(0/2/3만 유효) → 패딩 없는 글도 통과시킴
  function looksLikeBase64(str) {
    return (
      str.length >= MIN_B64_LEN &&
      str.length % 4 !== 1 &&
      /^[A-Za-z0-9+/]+={0,2}$/.test(str)
    );
  }

  // 디코딩 결과가 "사람이 읽을 수 있는 텍스트"인지 판별 (제어문자 비율로 오탐 제거)
  function isProbablyText(str) {
    if (!str) return false;
    let bad = 0;
    for (const ch of str) {
      const c = ch.codePointAt(0);
      if (c < 0x09 || (c > 0x0d && c < 0x20) || c === 0x7f) bad++;
    }
    return bad / str.length < 0.1;
  }

  // base64 -> UTF-8 문자열 1회 디코딩 (실패 시 null)
  function decodeOnce(str) {
    if (!looksLikeBase64(str)) return null;
    try {
      // 패딩이 없으면 4의 배수가 되도록 '=' 보정
      let s = str;
      const rem = s.length % 4;
      if (rem === 2) s += '==';
      else if (rem === 3) s += '=';
      const binary = atob(s);
      const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
      // fatal:true -> 유효하지 않은 UTF-8이면 예외 발생 → 오탐 차단
      return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
    } catch (e) {
      return null;
    }
  }

  // 중첩 디코딩 (최대 MAX_DECODE회). 더 이상 base64가 아니거나 깨진 데이터면 중단
  function fullyDecode(str) {
    let current = str;
    let best = null;
    for (let i = 0; i < MAX_DECODE; i++) {
      const decoded = decodeOnce(current);
      if (decoded === null) break;          // 더 이상 base64 아님 → 종료
      if (!isProbablyText(decoded)) break;  // 깨진 데이터 → 직전 결과 유지
      best = decoded;
      current = decoded;                    // 한 번 더 디코딩 시도
    }
    return best;
  }

  /* ============================== DOM 조립 ============================== */

  // 디코딩된 텍스트를 받아 URL을 <a>로 분리한 노드 배열 반환
  function buildDecodedNodes(text) {
    const nodes = [];
    let last = 0;
    let m;
    URL_REGEX.lastIndex = 0;
    while ((m = URL_REGEX.exec(text)) !== null) {
      let url = m[0];
      // URL 끝에 붙은 문장부호는 링크에서 제외
      const trailMatch = url.match(/[.,;:!?)\]}'"]+$/);
      let trail = '';
      if (trailMatch) {
        trail = trailMatch[0];
        url = url.slice(0, url.length - trail.length);
      }
      const start = m.index;
      if (start > last) nodes.push(document.createTextNode(text.slice(last, start)));

      const a = document.createElement('a');
      a.href = url.toLowerCase().startsWith('http') ? url : 'https://' + url;
      a.textContent = url;
      a.target = '_blank';
      a.rel = 'noopener noreferrer';
      nodes.push(a);

      if (trail) nodes.push(document.createTextNode(trail));
      last = start + m[0].length;
    }
    if (last < text.length) nodes.push(document.createTextNode(text.slice(last)));
    return nodes;
  }

  // 텍스트 노드 하나를 처리: base64 부분만 디코딩해서 그 자리에만 대체
  function processTextNode(node) {
    const text = node.nodeValue;
    if (!text) return;

    let match;
    let last = 0;
    let changed = false;
    const frag = document.createDocumentFragment();
    B64_CANDIDATE.lastIndex = 0;

    while ((match = B64_CANDIDATE.exec(text)) !== null) {
      const candidate = match[0];
      const decoded = fullyDecode(candidate);
      if (decoded === null) continue; // 실제 base64가 아니면 건드리지 않음

      changed = true;
      // base64 앞쪽의 일반 텍스트는 원본 그대로 유지
      if (match.index > last) {
        frag.appendChild(document.createTextNode(text.slice(last, match.index)));
      }
      // 디코딩된 부분만 (URL 링크 포함) 삽입
      for (const n of buildDecodedNodes(decoded)) frag.appendChild(n);
      last = match.index + candidate.length;
    }

    if (!changed) return; // 디코딩 대상 없음 → 노드 유지
    if (last < text.length) frag.appendChild(document.createTextNode(text.slice(last)));
    node.parentNode.replaceChild(frag, node);
  }

  // 컨테이너 내부의 모든 텍스트 노드 처리
  function processContainer(container) {
    const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, {
      acceptNode(node) {
        const p = node.parentNode;
        if (!p) return NodeFilter.FILTER_REJECT;
        const tag = p.nodeName;
        // 스크립트/스타일/이미 링크/입력영역은 건너뜀
        if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'A' || tag === 'TEXTAREA') {
          return NodeFilter.FILTER_REJECT;
        }
        if (!node.nodeValue || !node.nodeValue.trim()) return NodeFilter.FILTER_REJECT;
        return NodeFilter.FILTER_ACCEPT;
      },
    });
    const targets = [];
    let n;
    while ((n = walker.nextNode())) targets.push(n);
    targets.forEach(processTextNode);
  }

  /* ====================== 실행 / 동적 콘텐츠 감시 ====================== */

  let observer = null;
  let scheduled = false;

  function runOnce() {
    TARGET_SELECTORS.forEach((sel) => {
      document.querySelectorAll(sel).forEach((el) => processContainer(el));
    });
  }

  function startObserve() {
    if (!observer) observer = new MutationObserver(() => scheduleRun());
    const root = document.body || document.documentElement;
    if (root) observer.observe(root, { childList: true, subtree: true, characterData: true });
  }

  function scheduleRun() {
    if (scheduled) return;
    scheduled = true;
    // 디코딩으로 인한 DOM 변경이 옵저버를 다시 트리거하지 않도록
    // 일시 중지(disconnect) → 처리 → 재연결(reconnect)
    requestAnimationFrame(() => {
      scheduled = false;
      if (observer) observer.disconnect();
      runOnce();
      startObserve();
    });
  }

  runOnce();
  startObserve();
})();

22
댓글 5개
댓글 쓰기
ㅁㅊ 너무 편하고
06.03
딱 원하는 기능하고 id나 클래스만 지정해주니 알잘딱해줌
ㄳㄳ 근데 가끔 복호화된 결과물이 두번 연속 나오는 경우가 있긴한데 왜 그런거임?
06.03
난 그런거 못봤는데
06.03
두번 되는 게시물 링크좀
[요청복구]~탁란+사정 관리에 의한 극한 NTR 생활~ 알파메일에 의한 정자 제공으로 마음까지 네토라레 당하는 사랑하는 아내: 치사 편
소리
키도
06.03 53064 37
용량주의) 대마인 시리즈 드라마CD 모음 49GB/169개
소리
키도
06.03 48263 47
(복구)[한글자막]다키마쿠라 여친 -Pretty Holic- ~꼬~옥 밀착하며 곁잠 섹스해 주는 느슨하고 귀여운 후배~
소리
키리안
06.03 49098 46
씨이이이이발 이거거든ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
야짤
stealthmania
06.03 41273 19
[복구] 레이블(니케) 영상 복구
영상
Bremerton
06.03 125676 63
Naminori Kamome-눈 내리고 사랑이 쌓이네
동인
k8625
06.03 119247 53
[한글패치] 니토-오염격리구역에 떨어진 토끼- (패치파일만)
번역
jaaxmaster
06.03 413190 213
Henrybird-변기 성녀 블랑카
동인
k8625
06.03 138896 83
ai번역 간락02 유카편
동인
1Q2W3E4R
06.03 125788 50
Hotate-chan 섹스마려워 안달난 아내
동인
k8625
06.03 154055 180
장리 (명조)
영상
Bremerton
06.03 115650 97
[요청복구][RJ01576867]다키마쿠라 여친 -Pretty Holic- ~꼬~옥 밀착하며 곁잠 섹스해 주는 느슨하고 귀여운 후배~
소리
vhskorea
06.03 41245 39
【한국어 자막판】 집애감금(執愛監禁)
소리
puella
06.03 50076 72
이세계 NTR여관 후기 (털게가 가야했던길?)
후기 및 공략
koreno1
06.03 49321 23
karoti 메스가키 리나짱 1 2 3
동인
posan
06.03 145417 116
(AI번역,미검수)(후타나리, 펨보이, 후타나리 NTR) Futa Familly 0.246
번역
Onko
06.03 399255 119
RJ01561117 구마령 아가씨 사야 2탄 [돚거해옴]
미번
gloneofist
06.03 84602 54
RJ01586101 에투알 마리아주 v1.0.1
미번
ECiLA
06.03 84117 38
[요청복구] 디스토피아의 약탈자
복구
arquivity
06.03 201665 55
호신술도장 서클 신작 6월 5일 발매 확정
정보
아이스뱅
06.03 50201 43
[요청복구] 시코르스키 서클 모음 シコルスキー大佐
복구
arquivity
06.03 218494 115
한글, 렌파이) Confined and Horny 0.27
번역
화앨
06.03 363668 132
코네 base64 오토 디코더
유틸
apache
06.03 30335 22
[수정] 음락의 무녀 ~음탕회권~, 음락의 무녀 ~치옥담~, 음락의 무녀 (원본)
미번
Ed
06.03 78095 40
[구매보급]【한국어 자막판】 길 잃은 숲에서 폭유 엘프 자매에게 구조되었지만... 결국 잡아먹혔습니다♪
소리
이름뭐하지
06.03 58813 64
[AI번역][VJ007656] 음락의 무녀 (패치파일만) - 260604본편추가
번역
호빵할배
06.03 349476 78
[버전업] Base64 자동 링크 변환 크롬 확장 프로그램
유틸
qqwe1234
06.03 22482 18
[기번][AI번역][미검수][RJ01292204]레즈세계의 네토라레 사정
번역
kukuruyake
06.03 365876 181
Milimon-아비게일
동인
k8625
06.03 179819 191
Milimon-바니걸 니키
동인
k8625
06.03 144251 97
1 4546474849 100