Universal Script Interceptor v1.5 i18n + UA logo + autoupdate

Перехват скриптов с виджетом, ZIP экспортом, сворачиванием, переводом, логотип UA и автообновлением

当前为 2025-09-26 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Universal Script Interceptor v1.5 i18n + UA logo + autoupdate
// @namespace    https://example.com/
// @version      1.5
// @description  Перехват скриптов с виджетом, ZIP экспортом, сворачиванием, переводом, логотип UA и автообновлением
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @connect      raw.githubusercontent.com
// @run-at       document-start
// ==/UserScript==

(function() {
'use strict';
if(window.__universalInterceptorActive) return;

// ---------- Настройки ----------
const CURRENT_VERSION = '1.5';
const UPDATE_URL = 'https://raw.githubusercontent.com/YOUR_USERNAME/YOUR_REPO/main/universal_interceptor.user.js';
const store = new Map();
let idCounter=1;
const nowISO=()=>new Date().toISOString();
const makeId=()=>idCounter.toString().padStart(4,'0');

// ---------- Языки ----------
const LANGS={
    ru:{title:'Script Interceptor', clear:'Очистить', export:'Экспорт ZIP', collapse:'[–]', expand:'[+]', noScripts:'Нет скриптов для экспорта', scriptLabel:'скрипт', bytes:'байт'},
    ua:{title:'Перехоплювач скриптів', clear:'Очистити', export:'Експорт ZIP', collapse:'[–]', expand:'[+]', noScripts:'Немає скриптів для експорту', scriptLabel:'скрипт', bytes:'байт'},
    en:{title:'Script Interceptor', clear:'Clear', export:'Export ZIP', collapse:'[–]', expand:'[+]', noScripts:'No scripts to export', scriptLabel:'script', bytes:'bytes'}
};
let lang=localStorage.getItem('__si_widgetLang')||'ru';
let collapsed=JSON.parse(localStorage.getItem('__si_widgetCollapsed'))||false;

// ---------- Виджет ----------
const widget=document.createElement('div');
widget.id='__scriptInterceptorWidget';
widget.style.position='fixed';
widget.style.right='10px';
widget.style.bottom='10px';
widget.style.width='340px';
widget.style.maxHeight='420px';
widget.style.overflowY='auto';
widget.style.background='rgba(0,0,0,0.9)';
widget.style.color='#0f0';
widget.style.fontSize='12px';
widget.style.padding='8px';
widget.style.zIndex=2147483647;
widget.style.borderRadius='8px';
widget.style.fontFamily='monospace';
widget.style.resize='both';
widget.style.overflow='auto';
widget.style.cursor='move';
widget.style.boxShadow='0 0 10px rgba(0,255,0,0.5)';
document.body.appendChild(widget);

// ---------- Функции ----------
function saveScript({url='(inline)', type='script', content='', origin=''}={}){
    for(const v of store.values()) if((v.url===url)&&(v.content===content)) return;
    const id = makeId(); idCounter++;
    store.set(id,{id,url,type,content,timestamp:nowISO(),size:content?content.length:0,origin});
    updateWidget();
}

function updateWidget(){
    widget.innerHTML='';
    const txt=LANGS[lang];

    // Header с логотипом и кнопками
    const header=document.createElement('div'); header.style.display='flex'; header.style.justifyContent='space-between'; header.style.alignItems='center';

    const title=document.createElement('b'); title.textContent=txt.title;
    header.appendChild(title);

    const logo=document.createElement('span');
    logo.textContent='🟦🟨 Зроблено в Україні';
    logo.style.marginLeft='6px';
    logo.style.fontSize='10px';
    logo.style.color='#FFD700';
    header.appendChild(logo);

    const btnContainer=document.createElement('div');

    const collapseBtn=document.createElement('button'); collapseBtn.textContent=collapsed?txt.expand:txt.collapse;
    collapseBtn.onclick=()=>{collapsed=!collapsed; localStorage.setItem('__si_widgetCollapsed',JSON.stringify(collapsed)); updateWidget();};
    btnContainer.appendChild(collapseBtn);

    const langSelect=document.createElement('select'); langSelect.style.marginLeft='4px';
    Object.keys(LANGS).forEach(k=>{ const opt=document.createElement('option'); opt.value=k; opt.text=k.toUpperCase(); if(k===lang) opt.selected=true; langSelect.appendChild(opt);});
    langSelect.onchange=()=>{lang=langSelect.value; localStorage.setItem('__si_widgetLang',lang); updateWidget();};
    btnContainer.appendChild(langSelect);

    header.appendChild(btnContainer);
    widget.appendChild(header);

    if(!collapsed){
        store.forEach(s=>{
            const link=document.createElement('a'); link.href='#';
            const label=s.url==='(inline)'?'(inline script)':`${txt.scriptLabel} ${s.url}`;
            link.textContent=`#${s.id} ${label} (${s.size} ${txt.bytes})`;
            link.style.color='#0f0'; link.style.display='block'; link.style.marginBottom='2px';
            link.onclick=e=>{e.preventDefault(); downloadSingle(s);};
            widget.appendChild(link);
        });
        const btns=document.createElement('div'); btns.style.marginTop='4px';
        const clearBtn=document.createElement('button'); clearBtn.textContent=txt.clear; clearBtn.onclick=()=>{store.clear(); updateWidget();};
        btns.appendChild(clearBtn);
        const zipBtn=document.createElement('button'); zipBtn.textContent=txt.export; zipBtn.style.marginLeft='4px'; zipBtn.onclick=exportZIP;
        btns.appendChild(zipBtn);
        widget.appendChild(btns);
    }
}

function downloadSingle(s){
    const blob=new Blob([s.content||''],{type:'application/javascript'});
    const url=URL.createObjectURL(blob);
    const a=document.createElement('a'); a.href=url; a.download=s.url.split('/').pop()||`script-${s.id}.js`;
    document.body.appendChild(a); a.click(); document.body.removeChild(a);
}

async function exportZIP(){
    const txt=LANGS[lang];
    if(!store.size){ alert(txt.noScripts); return; }
    const JSZipURL='https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js';
    if(!window.JSZip){
        await new Promise(resolve=>{const s=document.createElement('script'); s.src=JSZipURL; s.onload=resolve; document.head.appendChild(s);});
    }
    const zip=new JSZip();
    store.forEach(s=>{zip.file(s.url.split('/').pop()||`script-${s.id}.js`,s.content||'');});
    zip.generateAsync({type:'blob'}).then(content=>{
        const url=URL.createObjectURL(content);
        const a=document.createElement('a'); a.href=url; a.download='scripts.zip';
        document.body.appendChild(a); a.click(); document.body.removeChild(a);
    });
}

// Перетаскивание
(function makeDraggable(el){
    let posX=0,posY=0,mouseX=0,mouseY=0;
    const stored=JSON.parse(localStorage.getItem('__si_widgetPos')||'{}');
    if(stored.left) el.style.left=stored.left; if(stored.top) el.style.top=stored.top;
    el.style.right='auto'; el.style.bottom='auto';
    el.onmousedown=function(e){if(e.target.tagName==='BUTTON'||e.target.tagName==='SELECT'||e.target.tagName==='A') return; e.preventDefault(); mouseX=e.clientX; mouseY=e.clientY; document.onmousemove=drag; document.onmouseup=stopDrag;};
    function drag(e){ e.preventDefault(); posX=mouseX-e.clientX; posY=mouseY-e.clientY; mouseX=e.clientX; mouseY=e.clientY; el.style.top=(el.offsetTop-posY)+'px'; el.style.left=(el.offsetLeft-posX)+'px'; }
    function stopDrag(){document.onmousemove=null; document.onmouseup=null; localStorage.setItem('__si_widgetPos',JSON.stringify({top:el.style.top,left:el.style.left}));}
})(widget);

// Перехват fetch/XHR/script
const origFetch=window.fetch;
window.fetch=async function(resource,init){try{const res=await origFetch.apply(this,arguments); const ct=res.headers.get&&res.headers.get('content-type'); const isJS=ct&&/javascript|ecmascript|application\/x-javascript/.test(ct)||(typeof resource==='string'&&resource.match(/\.js(\?|$)/i)); if(isJS){try{const clone=res.clone(); const text=await clone.text(); saveScript({url:(typeof resource==='string'?resource:(resource&&resource.url))||'(fetch)',type:'fetch',content:text,origin:document.location.href});}catch(e){}} return res;}catch(err){throw err;}};

const origXHROpen=XMLHttpRequest.prototype.open;
const origXHRSend=XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open=function(method,url){this.__ti_url=url; return origXHROpen.apply(this,arguments);};
XMLHttpRequest.prototype.send=function(body){ const xhr=this; const url=xhr.__ti_url||'(xhr)'; const onReady=()=>{if(xhr.readyState===4){ const ct=xhr.getResponseHeader&&xhr.getResponseHeader('content-type'); const isJS=ct&&/javascript|ecmascript|application\/x-javascript/.test(ct)||(typeof url==='string'&&url.match(/\.js(\?|$)/i)); if(isJS&&typeof xhr.responseText==='string'){saveScript({url,type:'xhr',content:xhr.responseText,origin:document.location.href});}}}; this.addEventListener&&this.addEventListener('readystatechange',onReady); return origXHRSend.apply(this,arguments);};

const mo=new MutationObserver(mutations=>{for(const m of mutations){for(const n of m.addedNodes){if(n.tagName==='SCRIPT'){try{if(n.src){fetch(n.src).then(r=>r.text().then(t=>saveScript({url:n.src,type:'script[src]',content:t,origin:document.location.href}))).catch(()=>saveScript({url:n.src,type:'script[src]',content:'',origin:document.location.href}));}else{saveScript({url:'(inline script)',type:'script[inline]',content:n.textContent||'',origin:document.location.href});}}catch(e){}}}}});
mo.observe(document.documentElement||document,{childList:true,subtree:true});

updateWidget();

// ---------- Автообновление ----------
(function checkUpdate(){
    try{
        GM_xmlhttpRequest({
            method:'GET',
            url:UPDATE_URL,
            onload:function(res){
                const remoteVersionMatch=res.responseText.match(/@version\s+([0-9.]+)/);
                if(remoteVersionMatch && remoteVersionMatch[1]!==CURRENT_VERSION){
                    if(confirm(`New version ${remoteVersionMatch[1]} available. Reload and update?`)){
                        window.location.href=UPDATE_URL;
                    }
                }
            }
        });
    }catch(e){}
    setTimeout(checkUpdate,6*60*60*1000);
})();

window.__universalInterceptorActive=true;
})();