NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Interceptador JSON Profissional v5.1
// @namespace http://tampermonkey.net/
// @version 5.1
// @description Intercepta requisições, edita payloads, aplica regras e envia automaticamente. UI moderna com painel e botões de teste/cópia/envio.
// @author Você
// @match *://*/*
// @grant none
// @license 0BSD
// ==/UserScript==
(function () {
'use strict';
/************** Utils **************/
const host = location.host;
const LS_KEYS = {
IGNORED: 'ri:ignoredSites',
RULES: (h) => `ri:rules:${h}`,
AUTO_ORIG: (h) => `ri:autoOriginal:${h}`,
AUTO_EDIT: (h) => `ri:autoEdited:${h}`,
PANEL: 'ri:panelState'
};
const getJSON = (k, df) => { try { const v = localStorage.getItem(k); return v ? JSON.parse(v) : df; } catch { return df; } };
const setJSON = (k, v) => localStorage.setItem(k, JSON.stringify(v));
let ignoredSites = getJSON(LS_KEYS.IGNORED, []);
let rules = getJSON(LS_KEYS.RULES(host), []);
let autoOriginal = !!getJSON(LS_KEYS.AUTO_ORIG(host), false);
let autoEdited = !!getJSON(LS_KEYS.AUTO_EDIT(host), false);
let panelState = getJSON(LS_KEYS.PANEL, { minimized: false });
const queue = [];
let active = null;
function enqueue(item) { queue.push(item); updateBadge(); if (!active) nextItem(); }
function nextItem() {
active = queue.shift() || null;
updateBadge();
if (!active) return;
if (isIgnored()) { active.cancel(); nextItem(); return; }
if (autoEdited) { const processed = applyRules(active.rawBody); active.apply(processed); nextItem(); return; }
if (autoOriginal) { active.cancel(); nextItem(); return; }
showEditor(active);
}
function isIgnored() { return ignoredSites.includes(host); }
function parseMaybeRegex(pattern, flags) {
const m = pattern.match(/^\/(.+)\/([a-z]*)$/i);
if (m) { try { return new RegExp(m[1], m[2] || flags || 'g'); } catch {} }
return new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), flags || 'g');
}
function applyRules(str) {
let out = String(str);
for (const r of rules) {
try {
const re = r.isRegex ? new RegExp(r.find, r.flags || 'g') : parseMaybeRegex(r.find, r.flags || 'g');
out = out.replace(re, r.replace);
} catch {}
}
return out;
}
function detectBody(body) {
if (body == null) return { type: 'none', raw: '' };
if (typeof body === 'string') return { type: isLikelyJSON(body) ? 'json-string' : 'text', raw: body };
if (body instanceof FormData) { const obj = {}; body.forEach((v, k) => obj[k] = v); return { type: 'formdata', raw: JSON.stringify(obj, null, 2), original: body }; }
if (body instanceof URLSearchParams) return { type: 'urlencoded', raw: body.toString(), original: body };
try { return { type: 'json-object', raw: JSON.stringify(body, null, 2), original: body }; } catch { return { type: 'text', raw: String(body) }; }
}
function isLikelyJSON(s) { const t = s.trim(); return (t.startsWith('{') && t.endsWith('}')) || (t.startsWith('[') && t.endsWith(']')); }
function buildBodyFromRaw(type, raw, original) {
switch(type) {
case 'json-string':
case 'text': return raw;
case 'json-object': try { return JSON.parse(raw); } catch { return original ?? raw; }
case 'formdata': try { const obj = JSON.parse(raw); const fd = new FormData(); Object.entries(obj||{}).forEach(([k,v])=>fd.append(k,v)); return fd; } catch { return original; }
case 'urlencoded': try { return new URLSearchParams(raw); } catch { return original ?? raw; }
default: return raw;
}
}
function formatJSON(s) { try { return JSON.stringify(JSON.parse(s), null, 2); } catch { return s; } }
/************** Painel **************/
const panel = document.createElement('div');
panel.style.cssText = `
position:fixed; right:12px; bottom:12px; width:420px; height:360px;
background:#111827; color:#e5e7eb; border:1px solid #1f2937; border-radius:10px;
box-shadow:0 12px 30px rgba(0,0,0,.45); font:13px/1.4 Inter,Segoe UI,Roboto,Arial;
z-index:999999; display:flex; flex-direction:column; overflow:hidden;
`;
document.documentElement.appendChild(panel);
const header = document.createElement('div');
header.style.cssText = `background:linear-gradient(90deg,#1f2937,#111827); padding:8px 12px; display:flex; align-items:center; gap:8px; color:#f9fafb; font-weight:600;`;
const title = document.createElement('div'); title.textContent='Interceptador v5.1';
const badge = document.createElement('span'); badge.style.cssText='margin-left:auto; font-size:12px; color:#9ca3af;';
const minimizeBtn = document.createElement('button'); minimizeBtn.textContent = panelState.minimized?'▣':'▢'; minimizeBtn.style.cssText='background:none;border:none;color:#9ca3af;font-size:16px;cursor:pointer;';
header.append(title, badge, minimizeBtn);
panel.appendChild(header);
const toolbar = document.createElement('div'); toolbar.style.cssText='padding:6px 12px; display:flex; gap:10px; border-bottom:1px solid #1f2937; background:#0f131b;';
const ignoreCb = mkCheckbox('Ignorar site', isIgnored());
const autoOrigCb = mkCheckbox('Auto original', autoOriginal);
const autoEditCb = mkCheckbox('Auto editado', autoEdited);
toolbar.append(ignoreCb.label, autoOrigCb.label, autoEditCb.label);
panel.appendChild(toolbar);
const infoLine = document.createElement('div'); infoLine.style.cssText='padding:6px 12px; font-size:12px; color:#9ca3af; border-bottom:1px solid #1f2937;'; infoLine.textContent='Aguardando requisição…';
panel.appendChild(infoLine);
const main = document.createElement('div'); main.style.cssText='display:flex; flex-direction:column; gap:6px; padding:8px 12px; flex:1; overflow:hidden;';
const payloadBox = document.createElement('textarea'); payloadBox.placeholder='Payload aparecerá aqui...'; payloadBox.style.cssText='flex:1; width:100%; background:#0c0f16; color:#d1fae5; border:1px solid #1f2937; border-radius:8px; padding:8px; font-family:monospace; font-size:12px; resize:none; outline:none;';
main.appendChild(payloadBox);
const findReplaceWrap = document.createElement('div'); findReplaceWrap.style.cssText='display:grid; grid-template-columns: 1fr 1fr auto auto; gap:6px;';
const findInput = mkInput('Procurar'); const replaceInput = mkInput('Substituir');
const regexToggle = document.createElement('input'); regexToggle.type='checkbox';
const regexLabel = document.createElement('label'); regexLabel.append(regexToggle,' Regex'); regexLabel.style.color='#cbd5e1';
const addRuleBtn = mkBtn('➕','#2563eb');
findReplaceWrap.append(findInput, replaceInput, regexLabel, addRuleBtn);
main.appendChild(findReplaceWrap);
const rulesList = document.createElement('div'); rulesList.style.cssText='flex:0 0 70px; overflow:auto; border:1px dashed #1f2937; border-radius:8px; padding:4px;';
main.appendChild(rulesList);
const footer = document.createElement('div'); footer.style.cssText='padding:6px 12px; display:flex; gap:6px; border-top:1px solid #1f2937; background:#0f131b;';
const btnApplyRulesPreview = mkBtn('🧪 Regras','#7c3aed'); const btnCopy = mkBtn('📋 Copiar','#0369a1'); const btnSend = mkBtn('✅ Enviar editado','#16a34a'); const btnSendOrig = mkBtn('↩️ Original','#6b7280');
footer.append(btnApplyRulesPreview, btnCopy, btnSend, btnSendOrig);
panel.appendChild(main); panel.appendChild(footer);
// --- Funções UI ---
function applyMinimized(){if(panelState.minimized){panel.style.height='40px'; main.style.display='none'; toolbar.style.display='none'; infoLine.style.display='none'; footer.style.display='none'; minimizeBtn.textContent='▣';}else{panel.style.height='360px'; main.style.display=''; toolbar.style.display=''; infoLine.style.display=''; footer.style.display=''; minimizeBtn.textContent='▢';}}
applyMinimized(); minimizeBtn.onclick=()=>{panelState.minimized=!panelState.minimized; setJSON(LS_KEYS.PANEL,panelState); applyMinimized();};
ignoreCb.input.onchange=()=>{if(ignoreCb.input.checked&&!ignoredSites.includes(host))ignoredSites.push(host);else ignoredSites=ignoredSites.filter(h=>h!==host); setJSON(LS_KEYS.IGNORED,ignoredSites);};
autoOrigCb.input.onchange=()=>{autoOriginal=autoOrigCb.input.checked; setJSON(LS_KEYS.AUTO_ORIG(host),autoOriginal);};
autoEditCb.input.onchange=()=>{autoEdited=autoEditCb.input.checked; setJSON(LS_KEYS.AUTO_EDIT(host),autoEdited);};
function renderRules(){rulesList.innerHTML=''; if(!rules.length){rulesList.innerHTML='<div style="color:#94a3b8;font-size:12px;">Sem regras</div>';return;} rules.forEach((r,i)=>{const row=document.createElement('div'); row.style.cssText='display:flex;justify-content:space-between;font-size:12px;'; row.textContent=`${r.isRegex?`/${r.find}/`:`"${r.find}"`} → "${r.replace}"`; const del=mkBtn('🗑️','#ef4444'); del.style.padding='2px 6px'; del.onclick=()=>{rules.splice(i,1);setJSON(LS_KEYS.RULES(host),rules);renderRules();}; row.appendChild(del); rulesList.appendChild(row);}); }
renderRules();
addRuleBtn.onclick=()=>{if(!findInput.value)return alert('Informe o que procurar.'); rules.push({find:findInput.value,replace:replaceInput.value||'',flags:'g',isRegex:regexToggle.checked}); setJSON(LS_KEYS.RULES(host),rules); findInput.value=''; replaceInput.value=''; regexToggle.checked=false; renderRules();};
btnApplyRulesPreview.onclick=()=>{payloadBox.value=applyRules(payloadBox.value);};
btnCopy.onclick=()=>{navigator.clipboard.writeText(payloadBox.value).then(()=>alert('Copiado!'));};
btnSend.onclick=()=>{if(active){active.apply(payloadBox.value); clearEditor(); nextItem();}};
btnSendOrig.onclick=()=>{if(active){active.cancel(); clearEditor(); nextItem();}};
function clearEditor(){payloadBox.value=''; infoLine.textContent='Aguardando requisição…';}
function updateBadge(){const n=queue.length+(active?1:0); badge.textContent=n?n+' pend.':'';}
function showEditor(item){panelState.minimized=false; setJSON(LS_KEYS.PANEL,panelState); applyMinimized(); infoLine.textContent=`[${item.method}] ${item.url}`; payloadBox.value=formatJSON(item.rawBody);}
function mkBtn(label,color){const b=document.createElement('button'); b.textContent=label; b.style.cssText=`background:${color};color:#fff;border:none;padding:6px 8px;border-radius:6px;cursor:pointer;font-size:12px;`; return b; }
function mkInput(ph){const i=document.createElement('input'); i.placeholder=ph; i.style.cssText='background:#0c0f16;color:#e5e7eb;border:1px solid #1f2937;border-radius:6px;padding:6px;font-size:12px;'; return i; }
function mkCheckbox(label,checked){const input=document.createElement('input'); input.type='checkbox'; input.checked=checked; const lbl=document.createElement('label'); lbl.append(input, ` ${label}`); lbl.style.cssText='color:#cbd5e1;font-size:12px;display:flex;gap:4px;align-items:center;'; return {input,label:lbl};}
/************** Interceptores **************/
const originalFetch = window.fetch;
window.fetch = function(...args){
try{
const url = typeof args[0]==='string'?args[0]:(args[0]?.url||'');
const method=(args[1]?.method||'GET').toUpperCase();
const hasBody=!!args[1]?.body;
if(!hasBody)return originalFetch.apply(this,args);
const det=detectBody(args[1].body);
enqueue({
kind:'fetch', url, method, rawBody:det.raw,
apply:(newRaw)=>{const processed=applyRules(newRaw); args[1].body=buildBodyFromRaw(det.type,processed,det.original); return originalFetch.apply(this,args);},
cancel:()=>originalFetch.apply(this,args)
});
return new Promise((resolve,reject)=>{
const interval=setInterval(()=>{
if(!active)return;
if(active.url===url&&active.method===method&&active.rawBody===det.raw){
const oldApply=active.apply, oldCancel=active.cancel;
active.apply=(newRaw)=>{try{args[1].body=buildBodyFromRaw(det.type,applyRules(newRaw),det.original); originalFetch.apply(this,args).then(resolve).catch(reject);}catch(e){reject(e);}};
active.cancel=()=>{try{originalFetch.apply(this,args).then(resolve).catch(reject);}catch(e){reject(e);}};
clearInterval(interval);
}
},10);
});
}catch{return originalFetch.apply(this,args);}
};
const originalXHROpen = XMLHttpRequest.prototype.open;
const originalXHRSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open=function(method,url,...rest){this.__ri=this.__ri||{}; this.__ri.method=(method||'GET').toUpperCase(); this.__ri.url=url||''; return originalXHROpen.apply(this,[method,url,...rest]);};
XMLHttpRequest.prototype.send=function(body){
try{
const method=this.__ri?.method||'POST'; const url=this.__ri?.url||location.href;
if(body==null)return originalXHRSend.call(this,body);
const det=detectBody(body);
enqueue({
kind:'xhr', url, method, rawBody:det.raw,
apply:(newRaw)=>{const processed=applyRules(newRaw); originalXHRSend.call(this,buildBodyFromRaw(det.type,processed,det.original));},
cancel:()=>originalXHRSend.call(this,body)
});
return;
}catch{return originalXHRSend.call(this,body);}
};
updateBadge();
})();