/** * xxxiii.io/fraud — inline results, never blank page on submit. */ (function () { var global = typeof window !== 'undefined' ? window : this; var SERVICE_DOWN = 'Analysis service temporarily unavailable — try again or visit blockchainfraud.org'; function levelFromScore(score) { if (score >= 75) return 'Critical'; if (score >= 55) return 'High'; if (score >= 30) return 'Medium'; return 'Low'; } function normalizeLevel(raw) { if (!raw) return null; var t = String(raw).trim().replace(/[^A-Za-z]/g, ''); if (!t) return null; var cap = t.charAt(0).toUpperCase() + t.slice(1).toLowerCase(); return ['Low', 'Medium', 'High', 'Critical'].indexOf(cap) >= 0 ? cap : null; } function engineDisplayName(header, metaEngine) { if (metaEngine) return metaEngine.replace(/-/g, ' ').replace(/v(\d)/, 'v$1'); if (!header) return 'GMIIE Fraud Engine v2.1.0'; var h = String(header).toLowerCase(); if (h.indexOf('grok') >= 0) return 'GMIIE Analysis'; if (h.indexOf('gmiie-fraud-engine') >= 0) return 'GMIIE Fraud Engine v2.1.0'; if (h === 'edge-fallback-v2' || h === 'edge-fallback') return 'GMIIE Fraud Engine v2.1.0'; if (h.indexOf('python') >= 0) return 'Python Pipeline'; return 'GMIIE Fraud Engine v2.1.0'; } function parseReportMeta(html) { var s = String(html); var score = null; var patterns = [ /data-fraud-score=["']?(\d+)/i, /name="fraud-score"\s+content="(\d+)"/i, /Risk score:\s*(\d+)\s*\/\s*100/i, /class="risk-score-display"[^>]*>\s*(\d+)\s*\/\s*100/i, /class="score"[^>]*>\s*(\d+)\s*\/\s*100/i, /Fraud Check[^0-9]*(\d+)\s*\/\s*100/i, ]; for (var i = 0; i < patterns.length; i++) { var m = s.match(patterns[i]); if (m) { score = parseInt(m[1], 10); break; } } var level = null; var levelPatterns = [ /data-fraud-level=["']([^"']+)["']/i, /name="fraud-level"\s+content="([^"]+)"/i, /Risk level:<\/strong>\s*([^<]+)/i, /class="risk-level-badge[^"]*">\s*([A-Za-z]+)/i, /<title>Fraud Check[^—]*—\s*([A-Za-z]+)\s*\(\d/i, ]; for (var j = 0; j < levelPatterns.length; j++) { var lm = s.match(levelPatterns[j]); level = normalizeLevel(lm && lm[1]); if (level) break; } var engine = null; var engineMatch = s.match(/data-fraud-engine=["']([^"']+)["']/i) || s.match(/name="fraud-engine"\s+content="([^"]+)"/i) || s.match(/Engine:\s*([^·<]+)/i); if (engineMatch) engine = String(engineMatch[1]).trim(); if (score != null && !level) level = levelFromScore(score); return { score: score, level: level, engine: engine }; } function parseJsonMeta(json) { if (!json || json.score == null) return { score: null, level: null, engine: null }; var score = parseInt(json.score, 10); return { score: score, level: normalizeLevel(json.risk_level) || levelFromScore(score), engine: json.engine || null, typology: json.typology || json.typology_id || null, scamFamily: json.scam_family || null, }; } function resolveReport(meta, engineHeader) { if (meta.score == null) { return { score: null, level: null, engineLabel: engineDisplayName(engineHeader, meta.engine), unavailable: true, }; } var score = Math.max(0, Math.min(100, meta.score)); return { score: score, level: levelFromScore(score), engineLabel: engineDisplayName(engineHeader, meta.engine), typology: meta.typology || null, scamFamily: meta.scamFamily || null, unavailable: false, }; } function formatSummaryLine(scored) { return ( 'Risk score: ' + scored.score + ' / 100 · ' + scored.level + ' · ' + scored.engineLabel ); } function reportSpeakSummary(scored) { return ( 'Fraud analysis report. ' + formatSummaryLine(scored) + '. GMIIE scores are heuristic estimates, not legal findings. If Critical or High, do not send further funds. Preserve transaction hashes and report to ic3.gov.' ); } function downloadPdfReport(form) { if (!form) return; var fd = new FormData(form); fetch('/api/fraud/report.pdf', { method: 'POST', body: fd }) .then(function (r) { if (!r.ok) throw new Error('PDF unavailable'); var disp = r.headers.get('Content-Disposition') || ''; var fn = 'GMIIE-Fraud-Report.pdf'; var fnm = disp.match(/filename="([^"]+)"/i); if (fnm) fn = fnm[1]; return r.blob().then(function (blob) { var a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = fn; a.click(); URL.revokeObjectURL(a.href); }); }) .catch(function () { alert('PDF download failed — try again or visit blockchainfraud.org for full report.'); }); } function recoveryCtaHtml(scored) { var desk = global.GMIIEVictimActionDesk; if (desk && desk.recoveryCtaHtml) { var stageInput = document.querySelector('#fraud-check-form [name=incident_stage]'); var stage = stageInput ? stageInput.value : 'funds-sent'; return desk.recoveryCtaHtml(scored.score, scored.level, stage); } if (['Critical', 'High'].indexOf(scored.level) < 0) return ''; return ( '<div class="vad-recovery-cta">' + '<p><strong>' + scored.level + ' risk — take recovery action now.</strong></p>' + '<a class="btn-secondary" href="#take-action">View recovery steps for your situation</a></div>' ); } function reportActionsHtml(scored, summaryText) { var typAttr = scored.typology ? ' data-fraud-typology="' + String(scored.typology).replace(/"/g, '"') + '"' : ''; var scamAttr = scored.scamFamily ? ' data-scam-family="' + String(scored.scamFamily).replace(/"/g, '"') + '"' : ''; return ( recoveryCtaHtml(scored) + '<div class="gvd-report-actions">' + '<button type="button" class="gvd-pdf-btn" data-gmiie-pdf="1">Download PDF Report</button>' + '<button type="button" class="gvd-listen-btn" data-gmiie-report="1" data-report-text="' + String(summaryText || formatSummaryLine(scored)).replace(/"/g, '"') + '" data-fraud-score="' + scored.score + '" data-fraud-level="' + scored.level + '"' + typAttr + scamAttr + '>Listen to this report</button>' + '<button type="button" class="gvd-ask-btn" data-gmiie-ask-score="1" data-fraud-score="' + scored.score + '" data-fraud-level="' + scored.level + '">Ask about this score</button>' + '</div>' ); } function bindPdfButton(container, form) { if (!container) return; var btn = container.querySelector('[data-gmiie-pdf="1"]'); if (btn && !btn._pdfBound) { btn._pdfBound = true; btn.addEventListener('click', function () { downloadPdfReport(form); }); } } function notifyVoiceDesk(scored) { if (global.GMIIEVoiceDesk && global.GMIIEVoiceDesk.setFraudReportContext) { global.GMIIEVoiceDesk.setFraudReportContext(scored.score, scored.level, scored.engineLabel, null, { typology: scored.typology, scamFamily: scored.scamFamily, }); } } function showHtmlReport(results, html, engineHeader, form) { results.classList.add('visible'); var meta = parseReportMeta(html); var scored = resolveReport(meta, engineHeader); if (scored.unavailable) { results.innerHTML = '<p class="check-status err">Report returned but score could not be parsed. ' + SERVICE_DOWN + '</p>'; return; } var blobUrl = URL.createObjectURL(new Blob([html], { type: 'text/html' })); var speakText = reportSpeakSummary(scored); notifyVoiceDesk(scored); results.innerHTML = '<p class="check-status ok">' + formatSummaryLine(scored) + '</p>' + reportActionsHtml(scored, speakText) + '<iframe title="Fraud analysis report" sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox" src="' + blobUrl + '"></iframe>'; bindPdfButton(results, form); results.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } function showJsonReport(results, json, engineHeader, form) { results.classList.add('visible'); var meta = parseJsonMeta(json); var scored = resolveReport(meta, engineHeader); if (scored.unavailable) { results.innerHTML = '<p class="check-status err">Analysis returned without a score. ' + SERVICE_DOWN + '</p>'; return; } notifyVoiceDesk(scored); results.innerHTML = '<p class="check-status ok">' + formatSummaryLine(scored) + '</p>' + reportActionsHtml(scored, reportSpeakSummary(scored)); bindPdfButton(results, form); results.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } var form = document.getElementById('fraud-check-form'); var results = document.getElementById('fraud-results'); var submitBtn = document.getElementById('fraud-submit-btn'); var statusEl = document.getElementById('proxy-status'); if (!form || !results) return; form.addEventListener('submit', function (ev) { ev.preventDefault(); var text = (document.getElementById('fraud-text') || {}).value || ''; if (!String(text).trim()) { alert('Paste the suspicious message first.'); return; } if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Analyzing…'; } if (statusEl) { statusEl.textContent = 'Running check…'; statusEl.classList.remove('ok', 'err'); } results.classList.add('visible'); results.innerHTML = '<p class="check-status">Analyzing… contacting fraud engine</p>'; fetch('/api/fraud/analyze', { method: 'POST', body: new FormData(form), headers: { Accept: 'text/html,application/json' }, }) .then(function (r) { var ct = (r.headers.get('content-type') || '').toLowerCase(); var engineHeader = r.headers.get('X-Fraud-Engine'); if (r.ok && ct.includes('text/html')) { return r.text().then(function (html) { if (!html || html.length < 80) throw new Error(SERVICE_DOWN); showHtmlReport(results, html, engineHeader, form); if (statusEl) { statusEl.textContent = 'Analysis complete'; statusEl.classList.add('ok'); } }); } if (r.ok && ct.includes('application/json')) { return r.json().then(function (json) { showJsonReport(results, json, engineHeader, form); if (statusEl) { statusEl.textContent = 'Analysis complete'; statusEl.classList.add('ok'); } }); } return r .json() .catch(function () { return {}; }) .then(function (j) { throw new Error(j.error || SERVICE_DOWN); }); }) .catch(function (err) { results.classList.add('visible'); var msg = String(err.message || err); if (!/unavailable|502|503|failed/i.test(msg)) { msg = msg + '. ' + SERVICE_DOWN; } results.innerHTML = '<p class="check-status err">' + msg + ' · <a href="https://blockchainfraud.org" target="_blank" rel="noopener">blockchainfraud.org</a></p>'; if (statusEl) { statusEl.textContent = 'Check failed — see message above'; statusEl.classList.add('err'); } }) .finally(function () { if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Run Fraud Check'; } }); }); })();