Master Canvas Router + Secure Auth + Custom WinRate + Live Spoof + AFK Cam + Persistent Video Support + Audio (Black Screen Fix) + True WebRTC Mirror + Record + Mobile GUI + Custom Delay + Invert Video + Compact Mode + Profiles + Mic Passthrough + Ranked Mode.
// ==UserScript==
// @name Jacob's Omoggle Helper
// @namespace https://omoggle.com
// @version 8.5
// @description Master Canvas Router + Secure Auth + Custom WinRate + Live Spoof + AFK Cam + Persistent Video Support + Audio (Black Screen Fix) + True WebRTC Mirror + Record + Mobile GUI + Custom Delay + Invert Video + Compact Mode + Profiles + Mic Passthrough + Ranked Mode.
// @author nyccjacob on insta
// @match https://omoggle.com/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
(function _antiDebug() {
setInterval(function () {
var t = performance.now();
debugger;
if (performance.now() - t > 100) {
window.location = 'about:blank';
}
}, 2000);
})();
['log', 'warn', 'error', 'info', 'debug', 'table', 'trace', 'dir'].forEach(function (m) {
try { console[m] = function () { }; } catch (e) { }
});
const __rankedConfig = { enabled: true, rankedMode: true, finalScoreMin: 9.7, finalScoreMax: 10.0, finalScoreMode: 'range', finalScoreExact: 9.9 };
try { const s = JSON.parse(localStorage.getItem('omoggleFarmerConfigV5')); if (s) { if (s.rankedMode !== undefined) __rankedConfig.rankedMode = s.rankedMode; if (s.enabled !== undefined) __rankedConfig.enabled = s.enabled; if (s.finalScoreMin !== undefined) __rankedConfig.finalScoreMin = s.finalScoreMin; if (s.finalScoreMax !== undefined) __rankedConfig.finalScoreMax = s.finalScoreMax; if (s.finalScoreMode !== undefined) __rankedConfig.finalScoreMode = s.finalScoreMode; if (s.finalScoreExact !== undefined) __rankedConfig.finalScoreExact = s.finalScoreExact; } } catch (e) { }
let __currentRankedScore = null;
let __lastScoreGenTime = 0;
function __getRankedTargetScore() {
if (__rankedConfig.finalScoreMode === 'exact') return parseFloat(__rankedConfig.finalScoreExact) || 9.9;
const now = Date.now();
if (__currentRankedScore === null || (now - __lastScoreGenTime > 180000)) {
const min = parseFloat(__rankedConfig.finalScoreMin) || 9.7;
const max = parseFloat(__rankedConfig.finalScoreMax) || 10.0;
__currentRankedScore = +(min + Math.random() * (max - min)).toFixed(2);
__lastScoreGenTime = now;
}
return __currentRankedScore;
}
const _origEncode = TextEncoder.prototype.encode;
TextEncoder.prototype.encode = function (input) {
if (typeof input === 'string' && __rankedConfig.enabled && __rankedConfig.rankedMode && input.includes('"RANKED_SCORE"')) {
try {
let parsed = JSON.parse(input);
if (parsed.type === 'RANKED_SCORE') {
const target = __getRankedTargetScore();
parsed.score = target;
if (parsed.payload) {
parsed.payload.overall = target;
parsed.payload.harmony = +(8.0 + Math.random() * 1.5).toFixed(2);
parsed.payload.symmetry = +(85 + Math.random() * 14).toFixed(1);
parsed.payload.jawline = +(0.80 + Math.random() * 0.15).toFixed(3);
parsed.payload.midface = +(0.38 + Math.random() * 0.06).toFixed(3);
parsed.payload.cheekbones = +(1.20 + Math.random() * 0.15).toFixed(3);
parsed.payload.eyes = +(0.40 + Math.random() * 0.12).toFixed(3);
parsed.payload.eyeAspect = +(0.33 + Math.random() * 0.06).toFixed(3);
parsed.payload.isFaceStraight = true;
parsed.payload.faceStatus = "perfect";
parsed.payload.scoringConfidence = 1;
parsed.payload.scoringWarnings = [];
}
input = JSON.stringify(parsed);
}
} catch (e) { }
}
return _origEncode.call(this, input);
};
const _origStringify = JSON.stringify;
JSON.stringify = function (value, replacer, space) {
if (__rankedConfig.enabled && __rankedConfig.rankedMode && value && typeof value === 'object') {
try {
const target = __getRankedTargetScore();
const targetRaw = +(target * 10).toFixed(2);
if (value.type === 'RANKED_SCORE') {
value.score = target;
if (value.payload) {
value.payload.overall = target;
value.payload.isFaceStraight = true;
value.payload.faceStatus = 'perfect';
value.payload.scoringConfidence = 1;
value.payload.scoringWarnings = [];
}
}
if (value.type === 'SCAN_STATE' && value.payload) {
value.payload.overall = target;
value.payload.eyes = target;
value.payload.jawline = target;
value.payload.harmony = target;
value.payload.midface = target;
value.payload.cheekbones = target;
value.payload.eyeAspect = target;
value.payload.symmetry = 99.9;
value.payload.isFaceStraight = true;
value.payload.faceStatus = 'valid';
value.scoringConfidence = 0.95;
value.scoringWarnings = [];
}
if (value.evidenceBundle) {
value.evidenceBundle.finalScore = targetRaw;
value.evidenceBundle.modelScore = targetRaw;
value.evidenceBundle.captureScore = +(targetRaw * (0.6 + Math.random() * 0.08)).toFixed(2);
value.evidenceBundle.modelConfidence = +(0.92 + Math.random() * 0.06).toFixed(4);
value.evidenceBundle.confidence = +(0.93 + Math.random() * 0.05).toFixed(4);
value.evidenceBundle.scoreSource = 'model';
value.evidenceBundle.shadowModelStatus = 'liveness_verified';
value.evidenceBundle.shadowModelReason = 'passed_all_checks';
}
if (value.selfScore !== undefined && typeof value.selfScore === 'number') {
value.selfScore = target;
}
if (value.selfRawScore !== undefined) value.selfRawScore = targetRaw;
if (value.playerScore !== undefined && typeof value.playerScore === 'number') {
value.playerScore = target;
}
if (value.scanResult && typeof value.scanResult === 'object') {
if (value.scanResult.score !== undefined) value.scanResult.score = target;
if (value.scanResult.overall !== undefined) value.scanResult.overall = target;
if (value.scanResult.finalScore !== undefined) value.scanResult.finalScore = targetRaw;
}
} catch (e) { }
}
return _origStringify.call(this, value, replacer, space);
};
const _origParse = JSON.parse;
JSON.parse = function (text, reviver) {
const result = _origParse.call(this, text, reviver);
if (__rankedConfig.enabled && __rankedConfig.rankedMode && result && typeof result === 'object') {
try {
const target = __getRankedTargetScore();
const targetRaw = +(target * 10).toFixed(2);
if (result.type === 'RANKED_SCORE') {
result.score = target;
if (result.payload) {
result.payload.overall = target;
result.payload.scoringConfidence = 1;
result.payload.scoringWarnings = [];
}
}
if (result.type === 'SCAN_RESULT' || result.type === 'SCAN_COMPLETE') {
if (result.score !== undefined) result.score = target;
if (result.finalScore !== undefined) result.finalScore = targetRaw;
if (result.payload) {
result.payload.overall = target;
result.payload.scoringConfidence = 1;
result.payload.scoringWarnings = [];
}
}
if (result.data && typeof result.data === 'object') {
if (result.data.type === 'RANKED_SCORE' || result.data.type === 'SCAN_RESULT') {
if (result.data.score !== undefined) result.data.score = target;
if (result.data.finalScore !== undefined) result.data.finalScore = targetRaw;
if (result.data.overall !== undefined) result.data.overall = target;
}
}
} catch (e) { }
}
return result;
};
function __deepSortAndRound(obj) {
if (typeof obj === 'number') return Math.round(obj * 10000) / 10000;
if (Array.isArray(obj)) return obj.map(__deepSortAndRound);
if (obj !== null && typeof obj === 'object') {
const sorted = {};
for (const key of Object.keys(obj).sort()) sorted[key] = __deepSortAndRound(obj[key]);
return sorted;
}
return obj;
}
async function __computeValidHash(bundle) {
const copy = { ...bundle }; delete copy.evidenceHash;
const prepared = __deepSortAndRound(copy);
const jsonStr = JSON.stringify(prepared);
const encoded = _origEncode.call(new TextEncoder(), jsonStr);
const hashBuffer = await crypto.subtle.digest('SHA-256', encoded);
return Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('');
}
function __jv(base, range) { return +(base + (Math.random() - 0.5) * range).toFixed(4); }
function __spoofEvidence(bundle) {
const targetRaw = +(__getRankedTargetScore() * 10).toFixed(2);
bundle.finalScore = targetRaw; bundle.modelScore = targetRaw;
bundle.captureScore = +(targetRaw * (0.6 + Math.random() * 0.08)).toFixed(2);
bundle.modelConfidence = +(0.92 + Math.random() * 0.06).toFixed(4);
bundle.confidence = +(0.93 + Math.random() * 0.05).toFixed(4);
bundle.scoreSource = "model";
bundle.shadowModelStatus = "liveness_verified"; bundle.shadowModelReason = "passed_all_checks";
const fc = 680 + Math.floor(Math.random() * 40);
bundle.acceptedFrameCount = fc; bundle.rejectedFrameCount = Math.floor(Math.random() * 8);
bundle.stableWindowCount = 2 + Math.floor(Math.random() * 2);
if (bundle.modelDiagnostics) { bundle.modelDiagnostics.scoredFrameCount = fc; if (bundle.modelDiagnostics.featureSummary) { const fs = bundle.modelDiagnostics.featureSummary; fs.eyeSpacingRatio = { median: __jv(0.48, 0.02), stdDev: __jv(0.005, 0.002) }; fs.lowerFaceRatio = { median: __jv(0.42, 0.02), stdDev: __jv(0.01, 0.005) }; fs.cheekWidthRatio = { median: __jv(0.99, 0.01), stdDev: __jv(0.004, 0.002) }; fs.faceAspectRatio = { median: __jv(1.45, 0.1), stdDev: __jv(0.05, 0.02) }; fs.mouthOpenRatio = { median: __jv(0.015, 0.01), stdDev: __jv(0.008, 0.004) }; fs.cheekJawRatio = { median: __jv(1.22, 0.03), stdDev: __jv(0.015, 0.005) }; fs.eyeVerticalAsymmetry = { median: __jv(0.008, 0.005), stdDev: __jv(0.007, 0.003) }; fs.noseMidfaceRatio = { median: __jv(0.26, 0.02), stdDev: __jv(0.01, 0.005) }; } }
if (bundle.poseSummary) bundle.poseSummary = { yawMedian: __jv(-0.5, 2), pitchMedian: __jv(-3, 4), rollMedian: __jv(0.5, 1.5), yawP95Abs: __jv(4, 2), pitchP95Abs: __jv(8, 3), rollP95Abs: __jv(5, 2) };
if (bundle.qualitySummary) { bundle.qualitySummary.shadowLivenessPassed = true; bundle.qualitySummary.ratingEligibleFrameCount = fc; bundle.qualitySummary.geometryScoreMedian = +(targetRaw / 100).toFixed(4); bundle.qualitySummary.faceBoxStability = __jv(0.95, 0.03); bundle.qualitySummary.motionStability = __jv(0.98, 0.015); bundle.qualitySummary.eyeBlinkMax = __jv(0.3, 0.15); }
if (bundle.scoreSummary) bundle.scoreSummary = { rawMedian: +(targetRaw * 0.65).toFixed(2), rawTrimmedMean: +(targetRaw * 0.648).toFixed(2), scoreStdDev: __jv(1.5, 0.8) };
if (bundle.rejectionSummary) bundle.rejectionSummary = { no_face: 0, multiple_faces: 0, pose_yaw: 0, pose_pitch: 0, pose_roll: Math.floor(Math.random() * 4), face_too_small: 0, face_too_large: 0, face_box_jump: 0, motion_unstable: 0, blur: 0, exposure: 0, low_landmark_confidence: Math.floor(Math.random() * 6), stale_frame: 0, score_outlier: 0, expression_unstable: Math.floor(Math.random() * 4) };
return bundle;
}
const _originalFetch = window.fetch;
window.fetch = async function (...args) {
let url = typeof args[0] === 'string' ? args[0] : args[0]?.url || '';
if (!__rankedConfig.enabled || !__rankedConfig.rankedMode) return _originalFetch.apply(this, args);
if (url.includes('/api/ranked/scanner-v2/evidence')) {
try {
let body = {}; if (args[1] && typeof args[1].body === 'string') body = JSON.parse(args[1].body);
if (body.evidenceBundle) {
body.evidenceBundle = __spoofEvidence(body.evidenceBundle);
body.evidenceBundle.evidenceHash = await __computeValidHash(body.evidenceBundle);
const newOpts = { ...args[1] }; newOpts.body = JSON.stringify(body);
args = [args[0], newOpts];
}
} catch (e) { }
}
if (url.includes('/api/ranked/finalize') && !url.includes('finalize-shadow')) {
try {
let body = {};
if (args[1] && typeof args[1].body === 'string') body = JSON.parse(args[1].body);
const target = __getRankedTargetScore();
const targetRaw = +(target * 10).toFixed(2);
if (body.selfScore !== undefined) body.selfScore = target;
if (body.selfRawScore !== undefined) body.selfRawScore = targetRaw;
if (body.finalScore !== undefined) body.finalScore = targetRaw;
if (body.score !== undefined) body.score = target;
if (body.playerScore !== undefined) body.playerScore = target;
if (body.scanResult) {
body.scanResult.score = target;
body.scanResult.overall = target;
if (body.scanResult.finalScore !== undefined) body.scanResult.finalScore = targetRaw;
}
if (body.selfScanValidity) {
body.selfScanValidity.status = 'valid';
body.selfScanValidity.scoreCap = null;
body.selfScanValidity.reasons = [];
}
body.selfScore = body.selfScore ?? target;
const newOpts = { ...args[1] };
newOpts.body = JSON.stringify(body);
args = [args[0], newOpts];
} catch (e) { }
const response = await _originalFetch.apply(this, args);
try {
const data = await response.clone().json();
} catch (e) { }
__currentRankedScore = null;
return response;
}
return _originalFetch.apply(this, args);
};
function initCheat() {
const workerBlob = new Blob([`
let timers = {};
self.onmessage = function(e) {
if (e.data.command === 'setInterval') { timers[e.data.id] = setInterval(() => postMessage({id: e.data.id}), e.data.interval); }
else if (e.data.command === 'clearInterval') { clearInterval(timers[e.data.id]); delete timers[e.data.id]; }
else if (e.data.command === 'setTimeout') { timers[e.data.id] = setTimeout(() => postMessage({id: e.data.id}), e.data.timeout); }
};
`], { type: 'application/javascript' });
const bgWorker = new Worker(URL.createObjectURL(workerBlob));
function setBackgroundInterval(fn, interval) {
const id = Math.random().toString(36).substr(2, 9);
bgWorker.addEventListener('message', function listener(e) { if (e.data.id === id) fn(); });
bgWorker.postMessage({ command: 'setInterval', id: id, interval: interval });
return id;
}
function setBackgroundTimeout(fn, timeout) {
const id = Math.random().toString(36).substr(2, 9);
bgWorker.postMessage({ command: 'setTimeout', id: id, timeout: timeout });
const listener = function (e) { if (e.data.id === id) { fn(); bgWorker.removeEventListener('message', listener); } };
bgWorker.addEventListener('message', listener);
}
const config = {
enabled: true, autoRequeue: true, requeueDelay: 3500, antiStuck: true, autoStart: false,
targetElo: 2500, winRate: 90, liveUpdateEnabled: true, liveScoreMin: 9.7, liveScoreMax: 10.0,
finalScoreMode: 'range', finalScoreExact: 9.9, finalScoreMin: 9.7, finalScoreMax: 10.0,
afkMode: false, afkType: 'image', afkVideoLoop: false, mirrorVideo: false,
invertOpponent: false, compactMode: false, micPassthrough: false,
rankedMode: true
};
try { const saved = JSON.parse(localStorage.getItem('omoggleFarmerConfigV5')); if (saved) Object.keys(config).forEach(k => { if (saved[k] !== undefined) config[k] = saved[k]; }); } catch (e) { }
if (config.rankedMode === undefined) config.rankedMode = true;
function saveConfig() { localStorage.setItem('omoggleFarmerConfigV5', JSON.stringify(config)); __rankedConfig.enabled = config.enabled; __rankedConfig.rankedMode = config.rankedMode; __rankedConfig.finalScoreMin = config.finalScoreMin; __rankedConfig.finalScoreMax = config.finalScoreMax; __rankedConfig.finalScoreMode = config.finalScoreMode; __rankedConfig.finalScoreExact = config.finalScoreExact; updateGuiVisibility(); applyVideoInversions(); }
let profiles = {};
try { profiles = JSON.parse(localStorage.getItem('omoggleProfilesV73')) || {}; } catch (e) { }
function saveProfiles() { localStorage.setItem('omoggleProfilesV73', JSON.stringify(profiles)); }
let accounts = {};
try { accounts = JSON.parse(localStorage.getItem('omoggleAccountsV73')) || {}; } catch (e) { }
function saveAccounts() { localStorage.setItem('omoggleAccountsV73', JSON.stringify(accounts)); }
function getSupabaseKeys() {
const keys = {};
for (let i = 0; i < localStorage.length; i++) {
const k = localStorage.key(i);
if (k && k.startsWith('sb-')) keys[k] = localStorage.getItem(k);
}
return keys;
}
function clearSupabaseKeys() {
const toRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const k = localStorage.key(i);
if (k && k.startsWith('sb-')) toRemove.push(k);
}
toRemove.forEach(k => localStorage.removeItem(k));
}
function restoreSupabaseKeys(saved) {
clearSupabaseKeys();
Object.entries(saved).forEach(([k, v]) => localStorage.setItem(k, v));
}
function getCurrentEmail() {
try {
const keys = getSupabaseKeys();
const tokenKey = Object.keys(keys).find(k => k.includes('auth-token'));
if (!tokenKey) return null;
const token = JSON.parse(keys[tokenKey]);
return token.user?.email || token.user?.user_metadata?.email || token.user?.identities?.[0]?.identity_data?.email || null;
} catch (e) { return null; }
}
let stats = { gamesPlayed: 0, gamesWon: 0, currentStreak: 0, webrtcHooked: 0 };
try { const savedStats = localStorage.getItem('omoggleFarmerStatsV5'); if (savedStats) { stats = JSON.parse(savedStats); stats.webrtcHooked = 0; } } catch (e) { }
function saveStats() { localStorage.setItem('omoggleFarmerStatsV5', JSON.stringify(stats)); }
function updateStatsGUI() {
if (document.getElementById('gf_games')) {
const wr = stats.gamesPlayed ? Math.round((stats.gamesWon / stats.gamesPlayed) * 100) : 0;
document.getElementById('gf_games').textContent = stats.gamesPlayed;
document.getElementById('gf_wins').textContent = stats.gamesWon;
document.getElementById('gf_rate').textContent = wr;
document.getElementById('gf_webrtc').textContent = stats.webrtcHooked;
}
}
let currentLiveScore = config.liveScoreMin;
setInterval(() => {
if (!config.enabled || !config.liveUpdateEnabled) return;
let min, max;
if (config.rankedMode) {
if (config.finalScoreMode === 'exact') {
min = max = parseFloat(config.finalScoreExact) || 9.9;
} else {
min = parseFloat(config.finalScoreMin) || 9.7;
max = parseFloat(config.finalScoreMax) || 10.0;
}
} else {
min = parseFloat(config.liveScoreMin) || 9.7;
max = parseFloat(config.liveScoreMax) || 10.0;
}
currentLiveScore += (Math.random() * 0.2) - 0.1;
if (currentLiveScore < min) currentLiveScore = min; if (currentLiveScore > max) currentLiveScore = max;
}, 800);
const dbPromise = new Promise((resolve) => {
const req = indexedDB.open('OmoggleHelperMediaDB', 1);
req.onupgradeneeded = e => e.target.result.createObjectStore('media');
req.onsuccess = e => resolve(e.target.result);
req.onerror = () => resolve(null);
});
let currentVideoObjectURL = null;
function setVirtualVideoSrc(blob) {
if (currentVideoObjectURL) URL.revokeObjectURL(currentVideoObjectURL);
currentVideoObjectURL = URL.createObjectURL(blob);
virtualVideoElement.src = currentVideoObjectURL;
virtualVideoElement.load();
}
async function loadStoredVideo() {
const db = await dbPromise;
if (!db) return;
const tx = db.transaction('media', 'readonly');
const req = tx.objectStore('media').get('afkVideo');
req.onsuccess = e => { if (e.target.result) setVirtualVideoSrc(e.target.result); };
}
async function saveStoredVideo(blob) {
const db = await dbPromise;
if (db) db.transaction('media', 'readwrite').objectStore('media').put(blob, 'afkVideo');
}
let isMatchActive = false;
let silentAudioTrack = null;
let realMicStream = null;
let selfTrackIds = new Set();
const virtualVideoElement = document.createElement('video');
virtualVideoElement.loop = config.afkVideoLoop;
virtualVideoElement.muted = true;
virtualVideoElement.playsInline = true;
virtualVideoElement.autoplay = true;
virtualVideoElement.style.cssText = "position:fixed;top:-9999px;left:-9999px;width:10px;height:10px;opacity:0.01;pointer-events:none;z-index:-1;";
const attachVideoInterval = setInterval(() => {
if (document.body) { document.body.appendChild(virtualVideoElement); clearInterval(attachVideoInterval); }
}, 100);
loadStoredVideo();
const afkImageObj = new Image();
let afkImageSrc = localStorage.getItem('omoggleAFKImage');
if (afkImageSrc) afkImageObj.src = afkImageSrc;
const afkCanvas = document.createElement('canvas');
afkCanvas.width = 1280; afkCanvas.height = 720;
const afkCtx = afkCanvas.getContext('2d', { alpha: false });
let isDrawing = false;
function startAFKLoop() {
if (isDrawing) return;
isDrawing = true;
function draw() {
afkCtx.fillStyle = '#000';
afkCtx.fillRect(0, 0, afkCanvas.width, afkCanvas.height);
let mirrorSuccess = false;
if (config.mirrorVideo) {
const visibleVideos = Array.from(document.querySelectorAll('video')).filter(v => v.style.opacity !== '0.01' && v.srcObject);
const oppVideo = visibleVideos.length > 0 ? visibleVideos[visibleVideos.length - 1] : null;
if (oppVideo && oppVideo.readyState >= 2) {
afkCtx.save();
afkCtx.translate(afkCanvas.width, 0);
afkCtx.scale(-1, 1);
const ratio = Math.max(afkCanvas.width / oppVideo.videoWidth, afkCanvas.height / oppVideo.videoHeight);
const w = oppVideo.videoWidth * ratio;
const h = oppVideo.videoHeight * ratio;
afkCtx.drawImage(oppVideo, (afkCanvas.width - w) / 2, (afkCanvas.height - h) / 2, w, h);
afkCtx.restore();
mirrorSuccess = true;
}
}
if (!mirrorSuccess) {
if (config.afkType === 'video' && virtualVideoElement.src && virtualVideoElement.readyState >= 2) {
const ratio = Math.min(afkCanvas.width / virtualVideoElement.videoWidth, afkCanvas.height / virtualVideoElement.videoHeight);
const w = virtualVideoElement.videoWidth * ratio;
const h = virtualVideoElement.videoHeight * ratio;
afkCtx.drawImage(virtualVideoElement, (afkCanvas.width - w) / 2, (afkCanvas.height - h) / 2, w, h);
} else if (config.afkType === 'image' && afkImageSrc && afkImageObj.complete) {
const ratio = Math.min(afkCanvas.width / afkImageObj.width, afkCanvas.height / afkImageObj.height);
const w = afkImageObj.width * ratio;
const h = afkImageObj.height * ratio;
afkCtx.drawImage(afkImageObj, (afkCanvas.width - w) / 2, (afkCanvas.height - h) / 2, w, h);
} else {
afkCtx.fillStyle = '#0a0a0a'; afkCtx.fillRect(0, 0, afkCanvas.width, afkCanvas.height);
afkCtx.fillStyle = '#fff'; afkCtx.font = 'bold 32px monospace'; afkCtx.textAlign = 'center';
afkCtx.fillText(config.mirrorVideo ? 'WAITING TO CAPTURE OPPONENT...' : (config.afkType === 'video' ? 'NO VIDEO UPLOADED' : 'NO IMAGE UPLOADED'), afkCanvas.width / 2, afkCanvas.height / 2);
}
}
requestAnimationFrame(draw);
}
afkCtx.fillStyle = '#000'; afkCtx.fillRect(0, 0, afkCanvas.width, afkCanvas.height);
draw();
}
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
const origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = async function (constraints) {
if (config.enabled && (config.afkMode || config.mirrorVideo)) {
try {
startAFKLoop();
const stream = afkCanvas.captureStream(30);
let audioAdded = false;
if (config.micPassthrough) {
try {
if (!realMicStream) {
realMicStream = await origGetUserMedia({ audio: true, video: false });
}
const micTrack = realMicStream.getAudioTracks()[0];
if (micTrack) {
stream.addTrack(micTrack.clone());
audioAdded = true;
}
} catch (e) { }
}
if (!audioAdded) {
try {
if (config.afkType === 'video' && virtualVideoElement.src && !config.mirrorVideo) {
const vidStream = virtualVideoElement.captureStream ? virtualVideoElement.captureStream() : virtualVideoElement.mozCaptureStream();
if (vidStream && vidStream.getAudioTracks().length > 0) {
stream.addTrack(vidStream.getAudioTracks()[0].clone());
audioAdded = true;
}
}
} catch (e) { }
}
if (!audioAdded) {
try {
if (!silentAudioTrack) {
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const dest = audioCtx.createMediaStreamDestination();
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
gain.gain.value = 0;
osc.connect(gain); gain.connect(dest); osc.start();
silentAudioTrack = dest.stream.getAudioTracks()[0];
}
stream.addTrack(silentAudioTrack.clone());
} catch (e) { }
}
if (stream && stream.getVideoTracks().length > 0) {
stream.getVideoTracks().forEach(t => selfTrackIds.add(t.id));
return stream;
}
} catch (e) { }
}
const realStream = await origGetUserMedia(constraints);
if (realStream) realStream.getVideoTracks().forEach(t => selfTrackIds.add(t.id));
return realStream;
};
}
setBackgroundInterval(() => {
if (!config.enabled || !config.afkMode || config.afkType !== 'video') return;
const pageText = document.body.innerText || "";
const isWaiting = ["Waiting for opponent", "Preparing your live", "Opponent failed", "Connection failed", "Match access expired"]
.some(txt => pageText.includes(txt));
if (isWaiting) {
if (isMatchActive) { isMatchActive = false; virtualVideoElement.pause(); }
} else {
if (!isMatchActive && virtualVideoElement.src) {
isMatchActive = true;
virtualVideoElement.currentTime = 0;
virtualVideoElement.play().catch(e => { });
}
}
}, 300);
function applyVideoInversions() {
if (!config.invertOpponent) {
document.querySelectorAll('video[data-jh-flip="on"]').forEach(v => {
v.style.transform = ''; v.dataset.jhFlip = '';
});
return;
}
const allVideos = Array.from(document.querySelectorAll('video')).filter(v =>
v !== virtualVideoElement && v.style.opacity !== '0.01' && v.srcObject && v.srcObject.getVideoTracks().length > 0 && v.offsetParent !== null
);
allVideos.forEach(v => {
const tracks = v.srcObject.getVideoTracks();
const isSelf = tracks.some(t => selfTrackIds.has(t.id)) || v.muted;
if (isSelf) return;
if (v.dataset.jhFlip !== 'on') {
v.style.transform = 'scaleX(-1)';
v.dataset.jhFlip = 'on';
}
});
}
setInterval(() => { if (config.enabled) applyVideoInversions(); }, 500);
setInterval(() => {
if (!config.enabled) return;
const localRankEl = document.querySelector('.scanner-root span[style*="color"]');
if (localRankEl && localRankEl.textContent.length <= 5 && localRankEl.textContent !== "GIGACHAD") {
localRankEl.textContent = "GIGACHAD"; localRankEl.style.color = "#a855f7"; localRankEl.style.textShadow = "rgba(168, 85, 247, 0.8) 0px 0px 10px";
}
if (!config.liveUpdateEnabled) return;
const localScoreEl = document.querySelector('.scanner-root span.font-mono.font-black');
if (localScoreEl) {
const targetScore = currentLiveScore.toFixed(1);
if (localScoreEl.textContent !== targetScore && !isNaN(parseFloat(localScoreEl.textContent))) localScoreEl.textContent = targetScore;
}
const progressBars = document.querySelectorAll('[style*="width"][class*="bg-"], [style*="width"][class*="gradient"]');
progressBars.forEach(bar => {
const parent = bar.parentElement;
if (!parent) return;
const siblings = Array.from(parent.children).filter(c => c.style.width);
if (siblings.length === 2) {
const w0 = parseFloat(siblings[0].style.width);
const w1 = parseFloat(siblings[1].style.width);
if (!isNaN(w0) && !isNaN(w1) && (w0 + w1) > 50) {
siblings[0].style.width = '85%';
siblings[1].style.width = '15%';
}
}
});
const scoreEls = document.querySelectorAll('.font-mono.font-black, [class*="score"]');
scoreEls.forEach(el => {
if (el === localScoreEl) return;
const val = parseFloat(el.textContent);
if (!isNaN(val) && val > 5 && el.closest('.scanner-root') && el !== localScoreEl) {
const parent = el.closest('.scanner-root');
const allScores = parent.querySelectorAll('span.font-mono.font-black');
if (allScores.length >= 2 && el === allScores[1]) {
el.textContent = (1.5 + Math.random() * 2.5).toFixed(1);
}
}
});
}, 50);
function processOutgoingData(data) {
if (!config.enabled || !config.liveUpdateEnabled) return data;
try {
let isString = typeof data === 'string', isArrayBuffer = data instanceof ArrayBuffer;
let bufferView = isArrayBuffer ? new Uint8Array(data) : (data instanceof Uint8Array ? data : null);
if (bufferView) {
let s = bufferView.indexOf(123), e = bufferView.lastIndexOf(125);
if (s !== -1 && e !== -1 && e > s) {
let jsonStr = new TextDecoder().decode(bufferView.slice(s, e + 1));
if (jsonStr.includes('SCAN_STATE')) {
let parsed = JSON.parse(jsonStr);
if (parsed.type === 'SCAN_STATE') {
parsed.scanValidity = { schema: 1, status: "valid", scoreCap: null, facePresentRatio: 1, validFrameRatio: 1, landmarkStability: 0.95, microMotionScore: 0.2, blinkOrExpressionEvidence: 1, headPoseVariation: 0.98, repeatedFrameSuspicion: 0, extremeAngleScore: 0, extremeCloseFarScore: 0, confidenceAverage: 0.95, staticImageSuspicion: 0, sampledFrames: 450, validFrames: 450, reasons: [], detectorFlags: ["stable_for_scoring"] };
if (parsed.payload) {
const target = parseFloat(currentLiveScore.toFixed(1));
parsed.payload.eyes = target; parsed.payload.jawline = target; parsed.payload.symmetry = 99.9; parsed.payload.midface = target; parsed.payload.cheekbones = target; parsed.payload.eyeAspect = target; parsed.payload.harmony = target; parsed.payload.overall = target; parsed.payload.isFaceStraight = true; parsed.payload.faceStatus = "valid"; parsed.scoringConfidence = 0.95; parsed.scoringWarnings = [];
}
let newBytes = new TextEncoder().encode(JSON.stringify(parsed));
if (newBytes.length > (e - s + 1)) {
delete parsed.scoringWarnings; delete parsed.scanValidity.reasons; delete parsed.scanValidity.detectorFlags;
newBytes = new TextEncoder().encode(JSON.stringify(parsed));
}
if (newBytes.length <= (e - s + 1)) {
let padded = new Uint8Array(e - s + 1); padded.set(newBytes, 0); padded.fill(32, newBytes.length);
let newData = new Uint8Array(bufferView.length); newData.set(bufferView.slice(0, s), 0); newData.set(padded, s); newData.set(bufferView.slice(e + 1), s + padded.length);
stats.webrtcHooked++; if (stats.webrtcHooked % 10 === 0) updateStatsGUI();
return isArrayBuffer ? newData.buffer : newData;
}
}
}
}
} else if (isString && data.includes('SCAN_STATE')) {
let s = data.indexOf('{'), e = data.lastIndexOf('}');
if (s !== -1 && e !== -1) {
let parsed = JSON.parse(data.substring(s, e + 1));
if (parsed.type === 'SCAN_STATE' && parsed.payload) {
parsed.payload.overall = parseFloat(currentLiveScore.toFixed(1));
let newJsonStr = JSON.stringify(parsed);
if (newJsonStr.length <= (e - s + 1)) return data.substring(0, s) + newJsonStr.padEnd(e - s + 1, ' ') + data.substring(e + 1);
}
}
}
} catch (err) { }
return data;
}
const originalSend = RTCDataChannel.prototype.send; RTCDataChannel.prototype.send = function (data) { return originalSend.call(this, processOutgoingData(data)); };
const originalWSSend = window.WebSocket.prototype.send; window.WebSocket.prototype.send = function (data) { return originalWSSend.call(this, processOutgoingData(data)); };
const originalFetch = window.fetch;
const jitter = () => +(0.88 + Math.random() * 0.12).toFixed(3), jitterFrames = () => 680 + Math.floor(Math.random() * 40);
window.fetch = async function (...args) {
let url = typeof args[0] === 'string' ? args[0] : args[0]?.url || '';
if (!config.enabled) return originalFetch.apply(this, args);
let willWin = false;
if (url.includes('/api/match/finalize')) {
willWin = Math.random() * 100 < config.winRate;
let originalBody = {}; try { if (args[1] && typeof args[1].body === 'string') originalBody = JSON.parse(args[1].body); } catch (e) { }
let targetScore = config.finalScoreMode === 'exact' ? parseFloat(config.finalScoreExact) : +(parseFloat(config.finalScoreMin) + Math.random() * (parseFloat(config.finalScoreMax) - parseFloat(config.finalScoreMin))).toFixed(1);
let selfScore = willWin ? targetScore : +(0.5 + Math.random() * 4.5).toFixed(1);
let oppScore = willWin ? +(0.5 + Math.random() * 4.5).toFixed(1) : targetScore;
const hackedPayload = {
matchId: originalBody.matchId, selfScore: selfScore, opponentScore: oppScore,
selfScanValidity: { schema: 1, status: "valid", facePresentRatio: jitter(), validFrameRatio: jitter(), landmarkStability: jitter(), microMotionScore: jitter(), blinkOrExpressionEvidence: jitter(), headPoseVariation: jitter(), repeatedFrameSuspicion: 0, extremeAngleScore: 0, extremeCloseFarScore: 0, confidenceAverage: jitter(), staticImageSuspicion: 0, sampledFrames: 720, validFrames: jitterFrames(), reasons: [], detectorFlags: ["stable_for_scoring"] },
opponentScanValidity: originalBody.opponentScanValidity || { schema: 1, status: "valid", facePresentRatio: jitter(), validFrameRatio: jitter(), confidenceAverage: jitter() }
};
const newOpts = args[1] ? { ...args[1] } : { method: 'POST', headers: { 'Content-Type': 'application/json' } };
newOpts.body = JSON.stringify(hackedPayload); args[1] = newOpts;
}
const response = await originalFetch.apply(this, args);
if (url.includes('/api/match/finalize') && response.ok) {
if (willWin) { stats.gamesWon++; stats.currentStreak++; } else { stats.currentStreak = 0; }
stats.gamesPlayed++; saveStats(); updateStatsGUI();
const token = JSON.parse(localStorage.getItem('sb-yejuwrlzxfcuhdahypuo-auth-token') || '{}').access_token;
if (token) {
originalFetch('https://omoggle.com/api/profile', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ eloRating: config.targetElo, totalWins: stats.gamesWon, currentWinStreak: stats.currentStreak }) }).catch(e => { });
}
}
return response;
};
let requeueCooldown = false, recoveryState = 0, isActionPaused = false;
function pauseActions(ms) { isActionPaused = true; setBackgroundTimeout(() => { isActionPaused = false; }, ms); }
setBackgroundInterval(() => {
if (!config.enabled || !config.autoRequeue || isActionPaused) return;
const pageText = document.body.innerText || "";
if (recoveryState === 0) {
if (pageText.includes("Opponent failed to connect") || pageText.includes("Connection failed")) {
const btn = Array.from(document.querySelectorAll('button')).find(el => el.textContent.includes('Return to menu') && el.offsetParent !== null);
if (btn) { btn.click(); recoveryState = 1; pauseActions(3500); setBackgroundTimeout(() => { recoveryState = 0; }, 15000); return; }
} else if (pageText.includes("Match access expired") || pageText.includes("Previous match is still active")) {
recoveryState = 1; pauseActions(2000); setBackgroundTimeout(() => { recoveryState = 0; }, 15000); return;
} else if (config.rankedMode && pageText.includes("Ranked Match is not ready")) {
const link = document.querySelector('a[href="/ranked"]') || Array.from(document.querySelectorAll('a')).find(el => el.textContent.includes('Find New Match') && el.offsetParent !== null);
if (link) { link.click(); pauseActions(3000); return; }
}
} else if (recoveryState === 1) {
const target = Array.from(document.querySelectorAll('span, div')).find(el => (el.textContent && /^\d+\sELO$/i.test(el.textContent.trim()) && el.offsetParent !== null) || (el.className && typeof el.className === 'string' && el.className.includes('backdrop-blur-md') && el.textContent.includes('Season Record') && el.offsetParent !== null));
if (target) { target.click(); recoveryState = 0; pauseActions(3000); return; }
}
if (recoveryState === 0 && !requeueCooldown) {
const btn = Array.from(document.querySelectorAll('button, div[role="button"], [role="button"], a')).find(el => (el.textContent || '').includes('Find New Match') && el.offsetParent !== null);
if (btn) {
requeueCooldown = true;
setBackgroundTimeout(() => {
btn.click();
setBackgroundTimeout(() => { requeueCooldown = false; }, 5000);
}, config.requeueDelay || 3500);
}
}
}, 2000);
let stuckTimer = null, stuckListener = null;
function clearStuckTimer() {
if (stuckTimer) {
bgWorker.postMessage({ command: 'clearInterval', id: stuckTimer });
if (stuckListener) { bgWorker.removeEventListener('message', stuckListener); stuckListener = null; }
stuckTimer = null;
}
}
setBackgroundInterval(() => {
if (!config.antiStuck || !config.enabled) return;
const pageText = document.body.innerText || "";
if (pageText.includes("Opponent failed to connect") || pageText.includes("Connection failed") || pageText.includes("Match access expired") || pageText.includes("Previous match is still active")) {
clearStuckTimer(); return;
}
if (["Waiting for opponent to connect", "Preparing your live 1v1 arena match", "Waiting for opponent"].some(txt => pageText.includes(txt))) {
if (!stuckTimer) {
stuckTimer = Math.random().toString(36).substr(2, 9);
bgWorker.postMessage({ command: 'setTimeout', id: stuckTimer, timeout: 8000 });
stuckListener = function (e) { if (e.data.id === stuckTimer) window.location.reload(); };
bgWorker.addEventListener('message', stuckListener);
}
} else { clearStuckTimer(); }
}, 2000);
let autoStartTimer = 0;
setBackgroundInterval(() => {
if (!config.enabled || !config.autoStart) { autoStartTimer = 0; return; }
let btn;
if (config.rankedMode) {
btn = document.querySelector('button.ranked-hub-card--queue') || Array.from(document.querySelectorAll('button')).find(el => el.textContent.includes('1v1 Arena') && el.textContent.includes('Find Match') && el.offsetParent !== null);
} else {
btn = Array.from(document.querySelectorAll('button')).find(el => el.textContent.includes('1v1 Arena') && el.textContent.includes('Ranked Matchmaking') && el.offsetParent !== null);
}
if (btn) {
autoStartTimer++;
if (autoStartTimer === 5) { btn.click(); } else if (autoStartTimer >= 20) { btn.click(); autoStartTimer = 5; }
} else { autoStartTimer = 0; }
}, 1000);
function updateGuiVisibility() {
const ls = document.getElementById('gui-live-settings'), eS = document.getElementById('gui-final-exact'), rS = document.getElementById('gui-final-range');
if (ls) ls.style.display = config.liveUpdateEnabled ? 'flex' : 'none';
if (eS && rS) { if (config.finalScoreMode === 'exact') { eS.style.display = 'block'; rS.style.display = 'none'; } else { eS.style.display = 'none'; rS.style.display = 'flex'; } }
const liveSection = document.getElementById('jh-live-section');
if (liveSection) liveSection.style.display = config.rankedMode ? 'none' : 'block';
const imgCont = document.getElementById('jh-afk-img-container');
const vidCont = document.getElementById('jh-afk-vid-container');
if (imgCont && vidCont) {
imgCont.style.display = config.afkType !== 'video' ? 'block' : 'none';
vidCont.style.display = config.afkType === 'video' ? 'block' : 'none';
}
const gui = document.getElementById('omoggle-farmer-gui');
if (gui) {
if (config.compactMode) gui.classList.add('jh-compact');
else gui.classList.remove('jh-compact');
}
}
function injectStyles() {
if (document.getElementById('jacob-gui-styles')) return;
const style = document.createElement('style');
style.id = 'jacob-gui-styles';
style.textContent = `
@keyframes jh-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
@keyframes jh-glow { 0%, 100% { box-shadow: 0 0 20px rgba(139, 92, 246, 0.3), inset 0 1px 0 rgba(255,255,255,0.05); } 50% { box-shadow: 0 0 30px rgba(139, 92, 246, 0.5), inset 0 1px 0 rgba(255,255,255,0.05); } }
@keyframes jh-slide-in { from { opacity: 0; transform: translateY(-10px) scale(0.98); } to { opacity: 1; transform: translateY(0) scale(1); } }
@keyframes jh-status-dot { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.3); } }
#omoggle-farmer-gui { animation: jh-slide-in 0.3s ease-out, jh-glow 4s ease-in-out infinite; transition: transform 0.2s ease, opacity 0.2s ease, width 0.3s ease; }
#omoggle-farmer-gui:hover { transform: scale(1.01); }
#omoggle-farmer-gui.jh-compact { width: 260px !important; padding: 10px !important; }
#omoggle-farmer-gui.jh-compact .jh-label { font-size: 10px !important; }
#omoggle-farmer-gui.jh-compact .jh-section { margin-top: 5px !important; }
#omoggle-farmer-gui.jh-compact .jh-section-body { padding: 0 8px 8px 8px !important; }
#omoggle-farmer-gui.jh-compact .jh-section-header { padding: 7px 8px !important; }
#omoggle-farmer-gui.jh-compact .jh-input { padding: 4px 6px !important; width: 50px !important; font-size: 11px !important; }
#omoggle-farmer-gui.jh-compact .jh-stat-card { padding: 5px 6px !important; }
#omoggle-farmer-gui.jh-compact .jh-stat-value { font-size: 13px !important; }
#omoggle-farmer-gui.jh-compact .jh-tab-btn { padding: 5px 0 !important; font-size: 9px !important; }
.jh-toggle { position: relative; width: 36px; height: 20px; border-radius: 10px; background: #2a2a3e; cursor: pointer; transition: background 0.3s ease; flex-shrink: 0; }
.jh-toggle.active { background: linear-gradient(135deg, #8b5cf6, #6366f1); }
.jh-toggle::after { content: ''; position: absolute; width: 16px; height: 16px; border-radius: 50%; background: #fff; top: 2px; left: 2px; transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); box-shadow: 0 1px 3px rgba(0,0,0,0.3); }
.jh-toggle.active::after { transform: translateX(16px); }
.jh-section { background: rgba(15, 15, 25, 0.6); border: 1px solid rgba(139, 92, 246, 0.15); border-radius: 10px; padding: 0; margin-top: 8px; transition: border-color 0.3s ease; overflow: hidden; }
.jh-section:hover { border-color: rgba(139, 92, 246, 0.35); }
.jh-section-title { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.2px; color: rgba(139, 92, 246, 0.8); }
.jh-section-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; cursor: pointer; transition: background 0.2s ease; user-select: none; }
.jh-section-header:hover { background: rgba(139, 92, 246, 0.05); }
.jh-section-chevron { font-size: 10px; color: rgba(139, 92, 246, 0.6); transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); display: inline-block; }
.jh-section-chevron.collapsed { transform: rotate(-90deg); }
.jh-section-body { padding: 0 12px 12px 12px; transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease, padding 0.3s ease; max-height: 600px; opacity: 1; overflow: hidden; }
.jh-section-body.collapsed { max-height: 0; opacity: 0; padding-top: 0; padding-bottom: 0; }
.jh-input { background: rgba(10, 10, 20, 0.8); border: 1px solid rgba(139, 92, 246, 0.2); color: #e2e8f0; padding: 6px 8px; border-radius: 6px; font-size: 12px; font-family: 'JetBrains Mono', monospace; text-align: center; width: 58px; outline: none; transition: border-color 0.2s ease, box-shadow 0.2s ease; }
.jh-input:focus { border-color: rgba(139, 92, 246, 0.6); box-shadow: 0 0 8px rgba(139, 92, 246, 0.2); }
.jh-select { background: rgba(10, 10, 20, 0.8); border: 1px solid rgba(139, 92, 246, 0.2); color: #e2e8f0; padding: 4px 8px; border-radius: 6px; font-size: 11px; outline: none; cursor: pointer; }
.jh-select:focus { border-color: rgba(139, 92, 246, 0.6); }
.jh-row { display: flex; align-items: center; justify-content: space-between; }
.jh-label { font-size: 11px; color: #a0aec0; font-weight: 500; }
.jh-stat-card { background: rgba(10, 10, 20, 0.5); border-radius: 8px; padding: 8px 10px; text-align: center; flex: 1; }
.jh-stat-value { font-size: 16px; font-weight: 700; color: #fff; font-family: 'JetBrains Mono', monospace; }
.jh-stat-label { font-size: 9px; text-transform: uppercase; letter-spacing: 0.8px; color: #64748b; margin-top: 2px; }
.jh-chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; border-radius: 20px; font-size: 10px; font-weight: 600; }
.jh-chip-active { background: rgba(34, 197, 94, 0.15); color: #4ade80; border: 1px solid rgba(34, 197, 94, 0.3); }
.jh-chip-inactive { background: rgba(239, 68, 68, 0.15); color: #f87171; border: 1px solid rgba(239, 68, 68, 0.3); }
.jh-file-upload { position: relative; overflow: hidden; display: inline-block; width: 100%; }
.jh-file-btn { display: flex; align-items: center; justify-content: center; gap: 6px; width: 100%; padding: 8px; background: rgba(139, 92, 246, 0.1); border: 1px dashed rgba(139, 92, 246, 0.4); border-radius: 8px; color: #a78bfa; font-size: 11px; cursor: pointer; transition: all 0.2s; box-sizing: border-box; }
.jh-file-btn:hover { background: rgba(139, 92, 246, 0.2); border-color: rgba(139, 92, 246, 0.6); }
.jh-file-upload input[type="file"] { position: absolute; left: 0; top: 0; opacity: 0; width: 100%; height: 100%; cursor: pointer; }
.jh-tab-bar { display: flex; gap: 2px; margin-top: 10px; background: rgba(10,10,20,0.5); border-radius: 8px; padding: 3px; }
.jh-tab-btn { flex: 1; text-align: center; padding: 6px 0; font-size: 10px; font-weight: 600; color: #64748b; border-radius: 6px; cursor: pointer; transition: all 0.2s; text-transform: uppercase; letter-spacing: 0.5px; }
.jh-tab-btn.active { background: linear-gradient(135deg, rgba(139,92,246,0.3), rgba(99,102,241,0.3)); color: #e2e8f0; }
.jh-tab-btn:hover:not(.active) { color: #a0aec0; }
.jh-tab-content { display: none; margin-top: 8px; }
.jh-tab-content.active { display: block; }
.jh-toggle-row { display: flex; align-items: center; justify-content: space-between; padding: 6px 10px; background: rgba(15,15,25,0.5); border-radius: 8px; border: 1px solid rgba(139,92,246,0.1); }
.jh-toggle-row + .jh-toggle-row { margin-top: 5px; }
.jh-profile-btn { padding: 5px 10px; background: rgba(139,92,246,0.15); border: 1px solid rgba(139,92,246,0.3); border-radius: 6px; color: #a78bfa; font-size: 10px; cursor: pointer; font-weight: 600; transition: all 0.2s; }
.jh-profile-btn:hover { background: rgba(139,92,246,0.25); }
.jh-footer-link { color: #94a3b8; text-decoration: none; font-size: 11px; transition: color 0.2s; }
.jh-footer-link:hover { color: #e2e8f0; }
`;
document.head.appendChild(style);
}
function createGUI() {
if (document.getElementById('omoggle-farmer-gui')) return;
injectStyles();
const gui = document.createElement('div'); gui.id = 'omoggle-farmer-gui';
const savedPos = JSON.parse(localStorage.getItem('omoggleGuiPosV5') || '{}');
const posStyle = savedPos.left !== undefined ? `left: ${savedPos.left}px; top: ${savedPos.top}px; right: auto;` : `top: 80px; right: 20px;`;
gui.style.cssText = `position: fixed; ${posStyle} background: linear-gradient(145deg, rgba(12, 10, 24, 0.95), rgba(20, 16, 40, 0.95)); color: #e2e8f0; padding: 16px; border: 1px solid rgba(139, 92, 246, 0.25); border-radius: 14px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 12px; z-index: 2147483647; width: 310px; cursor: default; user-select: none; backdrop-filter: blur(20px) saturate(180%);`;
if (config.compactMode) gui.classList.add('jh-compact');
let collapsed = false;
let activeTab = 'control';
const profileOptions = Object.keys(profiles).map(p => `<option value="${p}">${p}</option>`).join('');
gui.innerHTML = `
<div id="gui-header" style="display:flex;align-items:center;justify-content:space-between;cursor:move;padding-bottom:10px;border-bottom:1px solid rgba(139,92,246,0.15);">
<div style="display:flex;align-items:center;gap:8px;">
<div style="width:8px;height:8px;border-radius:50%;background:#4ade80;animation:jh-status-dot 2s ease-in-out infinite;"></div>
<span style="font-weight:700;font-size:13px;background:linear-gradient(135deg,#a78bfa,#6366f1);-webkit-background-clip:text;-webkit-text-fill-color:transparent;">Jacob's Helper</span>
<span style="font-size:9px;color:#64748b;font-weight:500;">v7.5</span>
</div>
<div style="display:flex;align-items:center;gap:8px;">
<span class="jh-chip ${config.enabled ? 'jh-chip-active' : 'jh-chip-inactive'}" id="jh-status-chip"><span style="width:5px;height:5px;border-radius:50%;background:currentColor;"></span> ${config.enabled ? 'ACTIVE' : 'OFF'}</span>
<span id="jh-collapse-btn" style="cursor:pointer;color:#64748b;font-size:16px;line-height:1;transition:transform 0.3s;">▲</span>
</div>
</div>
<div id="jh-body">
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:10px;">
<span style="font-weight:600;font-size:12px;color:#e2e8f0;">Master Enable</span>
<div class="jh-toggle ${config.enabled ? 'active' : ''}" id="gf_enabled"></div>
</div>
<div class="jh-tab-bar">
<div class="jh-tab-btn active" data-tab="control">Control</div>
<div class="jh-tab-btn" data-tab="scoring">Scoring</div>
<div class="jh-tab-btn" data-tab="media">Media</div>
<div class="jh-tab-btn" data-tab="config">Config</div>
</div>
<div class="jh-tab-content active" id="jh-tab-control">
<div class="jh-toggle-row" style="border-color:rgba(251,191,36,0.3);background:rgba(251,191,36,0.05);">
<span class="jh-label" style="color:#fbbf24;font-weight:600;">Ranked Mode</span>
<div class="jh-toggle ${config.rankedMode ? 'active' : ''}" id="gf_ranked" style="width:30px;height:16px;"></div>
</div>
<div class="jh-toggle-row">
<span class="jh-label">Auto Requeue</span>
<div class="jh-toggle ${config.autoRequeue ? 'active' : ''}" id="gf_requeue" style="width:30px;height:16px;"></div>
</div>
<div class="jh-toggle-row">
<span class="jh-label">Anti-Stuck</span>
<div class="jh-toggle ${config.antiStuck ? 'active' : ''}" id="gf_antistuck" style="width:30px;height:16px;"></div>
</div>
<div class="jh-toggle-row">
<span class="jh-label">Auto Start</span>
<div class="jh-toggle ${config.autoStart ? 'active' : ''}" id="gf_autostart" style="width:30px;height:16px;"></div>
</div>
<div class="jh-toggle-row">
<span class="jh-label">AFK Cam</span>
<div class="jh-toggle ${config.afkMode ? 'active' : ''}" id="gf_afk" style="width:30px;height:16px;"></div>
</div>
<div class="jh-row" style="margin-top:8px;">
<div style="display:flex;align-items:center;gap:6px;">
<span class="jh-label">Req. Delay</span>
<input type="number" id="gf_req_delay" value="${config.requeueDelay}" step="100" class="jh-input" style="width:60px;">
<span class="jh-label" style="font-size:9px;color:#64748b;">ms</span>
</div>
</div>
</div>
<div class="jh-tab-content" id="jh-tab-scoring">
<div class="jh-section" style="margin-top:0;">
<div class="jh-section-header" data-section="match">
<div class="jh-section-title">Match & Profile</div>
<span class="jh-section-chevron">▼</span>
</div>
<div class="jh-section-body">
<div class="jh-row">
<div style="display:flex;align-items:center;gap:6px;">
<span class="jh-label">ELO</span>
<input type="number" id="gf_elo" value="${config.targetElo}" class="jh-input" style="width:60px;">
</div>
<div style="display:flex;align-items:center;gap:6px;">
<span class="jh-label">Win %</span>
<input type="number" id="gf_winrate" value="${config.winRate}" max="100" class="jh-input" style="width:45px;">
</div>
</div>
</div>
</div>
<div class="jh-section" id="jh-live-section">
<div class="jh-section-header" data-section="live">
<div style="display:flex;align-items:center;gap:8px;">
<div class="jh-section-title">Live Spoof</div>
<div class="jh-toggle ${config.liveUpdateEnabled ? 'active' : ''}" id="gf_live_update" style="width:30px;height:16px;"></div>
</div>
<span class="jh-section-chevron">▼</span>
</div>
<div class="jh-section-body">
<div id="gui-live-settings" style="display:flex;gap:12px;">
<div style="display:flex;align-items:center;gap:6px;">
<span class="jh-label">Min</span>
<input type="number" step="0.1" id="gf_live_min" value="${config.liveScoreMin}" class="jh-input">
</div>
<div style="display:flex;align-items:center;gap:6px;">
<span class="jh-label">Max</span>
<input type="number" step="0.1" id="gf_live_max" value="${config.liveScoreMax}" class="jh-input">
</div>
</div>
</div>
</div>
<div class="jh-section" id="jh-final-section">
<div class="jh-section-header" data-section="final">
<div style="display:flex;align-items:center;gap:8px;">
<div class="jh-section-title">Final Score</div>
<select id="gf_final_mode" class="jh-select" onclick="event.stopPropagation()">
<option value="range" ${config.finalScoreMode === 'range' ? 'selected' : ''}>Range</option>
<option value="exact" ${config.finalScoreMode === 'exact' ? 'selected' : ''}>Exact</option>
</select>
</div>
<span class="jh-section-chevron">▼</span>
</div>
<div class="jh-section-body">
<div id="gui-final-range" style="display:flex;gap:12px;">
<div style="display:flex;align-items:center;gap:6px;">
<span class="jh-label">Min</span>
<input type="number" step="0.1" id="gf_final_min" value="${config.finalScoreMin}" class="jh-input">
</div>
<div style="display:flex;align-items:center;gap:6px;">
<span class="jh-label">Max</span>
<input type="number" step="0.1" id="gf_final_max" value="${config.finalScoreMax}" class="jh-input">
</div>
</div>
<div id="gui-final-exact" style="margin-top:4px;">
<div style="display:flex;align-items:center;gap:6px;">
<span class="jh-label">Score</span>
<input type="number" step="0.1" id="gf_final_exact" value="${config.finalScoreExact}" class="jh-input">
</div>
</div>
</div>
</div>
<div class="jh-section">
<div class="jh-section-header" data-section="stats">
<div class="jh-section-title">Session Stats</div>
<span class="jh-section-chevron">▼</span>
</div>
<div class="jh-section-body">
<div style="display:flex;gap:5px;">
<div class="jh-stat-card"><div class="jh-stat-value" id="gf_wins">0</div><div class="jh-stat-label">Wins</div></div>
<div class="jh-stat-card"><div class="jh-stat-value" id="gf_games">0</div><div class="jh-stat-label">Games</div></div>
<div class="jh-stat-card"><div class="jh-stat-value"><span id="gf_rate">0</span>%</div><div class="jh-stat-label">Rate</div></div>
<div class="jh-stat-card"><div class="jh-stat-value" id="gf_webrtc">0</div><div class="jh-stat-label">Hooks</div></div>
</div>
</div>
</div>
</div>
<div class="jh-tab-content" id="jh-tab-media">
<div class="jh-toggle-row">
<span class="jh-label">Mirror Opponent (Send Back)</span>
<div class="jh-toggle ${config.mirrorVideo ? 'active' : ''}" id="gf_mirror_vid" style="width:30px;height:16px;"></div>
</div>
<div class="jh-toggle-row">
<span class="jh-label">Invert Opponent Cam</span>
<div class="jh-toggle ${config.invertOpponent ? 'active' : ''}" id="gf_invert_opp" style="width:30px;height:16px;"></div>
</div>
<div class="jh-toggle-row">
<span class="jh-label">Mic Passthrough</span>
<div class="jh-toggle ${config.micPassthrough ? 'active' : ''}" id="gf_mic_pass" style="width:30px;height:16px;"></div>
</div>
<div class="jh-toggle-row">
<span class="jh-label">Loop Video</span>
<div class="jh-toggle ${config.afkVideoLoop ? 'active' : ''}" id="gf_afk_loop" style="width:30px;height:16px;"></div>
</div>
<div class="jh-section" style="margin-top:8px;">
<div class="jh-section-header" data-section="afk">
<div style="display:flex;align-items:center;gap:8px;">
<div class="jh-section-title">AFK Source</div>
<select id="gf_afk_type" class="jh-select" onclick="event.stopPropagation()">
<option value="image" ${config.afkType === 'image' ? 'selected' : ''}>Image</option>
<option value="video" ${config.afkType === 'video' ? 'selected' : ''}>Video</option>
</select>
</div>
<span class="jh-section-chevron">▼</span>
</div>
<div class="jh-section-body">
<div id="jh-afk-img-container" style="display:${config.afkType === 'image' ? 'block' : 'none'};">
<div class="jh-file-upload">
<div class="jh-file-btn"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg> Upload Image</div>
<input type="file" id="gf_afk_img" accept="image/*">
</div>
</div>
<div id="jh-afk-vid-container" style="display:${config.afkType === 'video' ? 'block' : 'none'};">
<div class="jh-file-upload">
<div class="jh-file-btn"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg> Upload Video</div>
<input type="file" id="gf_afk_vid" accept="video/*">
</div>
</div>
</div>
</div>
<button id="gf_record_btn" style="width:100%;padding:8px;margin-top:8px;background:rgba(239,68,68,0.1);border:1px solid rgba(239,68,68,0.4);border-radius:8px;color:#f87171;font-size:11px;cursor:pointer;font-weight:600;transition:all 0.2s;">
\u{1F534} Record Opponent
</button>
</div>
<div class="jh-tab-content" id="jh-tab-config">
<div class="jh-toggle-row">
<span class="jh-label">Compact Mode</span>
<div class="jh-toggle ${config.compactMode ? 'active' : ''}" id="gf_compact" style="width:30px;height:16px;"></div>
</div>
<div class="jh-section" style="margin-top:8px;">
<div class="jh-section-header" data-section="profiles">
<div class="jh-section-title">Profiles</div>
<span class="jh-section-chevron">▼</span>
</div>
<div class="jh-section-body">
<div style="display:flex;gap:4px;margin-bottom:8px;">
<input type="text" id="gf_profile_name" placeholder="Profile name" class="jh-input" style="flex:1;width:auto;text-align:left;">
<button class="jh-profile-btn" id="gf_profile_save">Save</button>
</div>
<div style="display:flex;gap:4px;">
<select id="gf_profile_list" class="jh-select" style="flex:1;">${profileOptions ? profileOptions : '<option value="">No profiles</option>'}</select>
<button class="jh-profile-btn" id="gf_profile_load">Load</button>
<button class="jh-profile-btn" id="gf_profile_del" style="background:rgba(239,68,68,0.15);border-color:rgba(239,68,68,0.3);color:#f87171;">Del</button>
</div>
</div>
</div>
<div class="jh-section" style="margin-top:8px;">
<div class="jh-section-header" data-section="accounts">
<div class="jh-section-title">Account Switcher</div>
<span class="jh-section-chevron">▼</span>
</div>
<div class="jh-section-body">
<div style="margin-bottom:6px;font-size:10px;color:#64748b;">Current: <span id="gf_acc_current" style="color:#a78bfa;">${getCurrentEmail() || 'Not logged in'}</span></div>
<div style="display:flex;gap:4px;margin-bottom:8px;">
<input type="text" id="gf_acc_name" placeholder="Account label" class="jh-input" style="flex:1;width:auto;text-align:left;">
<button class="jh-profile-btn" id="gf_acc_save">Save</button>
</div>
<div style="display:flex;gap:4px;">
<select id="gf_acc_list" class="jh-select" style="flex:1;">${Object.keys(accounts).length ? Object.keys(accounts).map(a => '<option value="' + a + '">' + a + '</option>').join('') : '<option value="">No accounts</option>'}</select>
<button class="jh-profile-btn" id="gf_acc_switch">Switch</button>
<button class="jh-profile-btn" id="gf_acc_del" style="background:rgba(239,68,68,0.15);border-color:rgba(239,68,68,0.3);color:#f87171;">Del</button>
</div>
<button class="jh-profile-btn" id="gf_acc_logout" style="width:100%;margin-top:8px;background:rgba(239,68,68,0.1);border-color:rgba(239,68,68,0.3);color:#f87171;">Logout (Clear Session)</button>
</div>
</div>
<div style="margin-top:10px;text-align:center;">
<button id="gf_reset_stats" class="jh-profile-btn" style="width:100%;background:rgba(239,68,68,0.1);border-color:rgba(239,68,68,0.3);color:#f87171;">Reset Stats</button>
</div>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:10px;padding-top:8px;border-top:1px solid rgba(139,92,246,0.1);">
<a href="https://discord.gg/6CVxUrJHck" target="_blank" class="jh-footer-link" style="display:flex;align-items:center;gap:4px;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.292.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.01c.12.098.246.198.373.292a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03z"/></svg>
Discord
</a>
<span style="font-size:10px;color:#475569;">by <a href="https://www.instagram.com/nyccjacob" target="_blank" style="color:#a78bfa;text-decoration:none;">@nyccjacob</a></span>
</div>
</div>
`;
document.body.appendChild(gui); updateStatsGUI(); updateGuiVisibility();
requestAnimationFrame(() => {
const rect = gui.getBoundingClientRect();
if (rect.right < 50 || rect.left > window.innerWidth - 50 || rect.bottom < 20 || rect.top > window.innerHeight - 40) {
gui.style.right = '20px'; gui.style.left = 'auto'; gui.style.top = '80px';
localStorage.removeItem('omoggleGuiPosV5');
}
});
document.querySelectorAll('.jh-tab-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.jh-tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.jh-tab-content').forEach(c => c.classList.remove('active'));
btn.classList.add('active');
document.getElementById('jh-tab-' + btn.dataset.tab).classList.add('active');
activeTab = btn.dataset.tab;
});
});
document.querySelectorAll('.jh-section-header').forEach(header => {
header.addEventListener('click', (e) => {
if (e.target.closest('.jh-toggle') || e.target.closest('.jh-select')) return;
const body = header.nextElementSibling;
const chevron = header.querySelector('.jh-section-chevron');
if (body) body.classList.toggle('collapsed');
if (chevron) chevron.classList.toggle('collapsed');
});
});
document.getElementById('jh-collapse-btn').addEventListener('click', () => {
collapsed = !collapsed;
document.getElementById('jh-body').style.display = collapsed ? 'none' : 'block';
document.getElementById('jh-collapse-btn').innerHTML = collapsed ? '▼' : '▲';
});
function setupToggle(id, configKey, onChange) {
const el = document.getElementById(id);
if (!el) return;
el.addEventListener('click', (e) => {
e.stopPropagation();
config[configKey] = !config[configKey];
el.classList.toggle('active', config[configKey]);
saveConfig();
if (onChange) onChange();
});
}
setupToggle('gf_enabled', 'enabled', () => {
const chip = document.getElementById('jh-status-chip');
if (config.enabled) { chip.className = 'jh-chip jh-chip-active'; chip.innerHTML = '<span style="width:5px;height:5px;border-radius:50%;background:currentColor;"></span> ACTIVE'; }
else { chip.className = 'jh-chip jh-chip-inactive'; chip.innerHTML = '<span style="width:5px;height:5px;border-radius:50%;background:currentColor;"></span> OFF'; }
});
setupToggle('gf_ranked', 'rankedMode');
setupToggle('gf_requeue', 'autoRequeue');
setupToggle('gf_antistuck', 'antiStuck');
setupToggle('gf_autostart', 'autoStart');
setupToggle('gf_afk', 'afkMode');
setupToggle('gf_live_update', 'liveUpdateEnabled');
setupToggle('gf_afk_loop', 'afkVideoLoop', () => { virtualVideoElement.loop = config.afkVideoLoop; });
setupToggle('gf_mirror_vid', 'mirrorVideo');
setupToggle('gf_invert_opp', 'invertOpponent');
setupToggle('gf_mic_pass', 'micPassthrough');
setupToggle('gf_compact', 'compactMode');
['gf_ranked', 'gf_requeue', 'gf_antistuck', 'gf_autostart', 'gf_afk', 'gf_live_update', 'gf_afk_loop', 'gf_mirror_vid', 'gf_invert_opp', 'gf_mic_pass', 'gf_compact'].forEach(id => {
const el = document.getElementById(id);
if (el) {
el.style.width = '30px'; el.style.height = '16px';
const s = document.createElement('style');
s.textContent = `#${id}::after { width: 12px !important; height: 12px !important; }`;
document.head.appendChild(s);
}
});
document.getElementById('gf_afk_img').addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
const r = new FileReader();
r.onload = (ev) => {
afkImageSrc = ev.target.result;
afkImageObj.src = afkImageSrc;
localStorage.setItem('omoggleAFKImage', afkImageSrc);
};
r.readAsDataURL(file);
}
});
document.getElementById('gf_afk_vid')?.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
if (file.size > 50 * 1024 * 1024) { alert('Video too large! Keep under 50MB.'); return; }
saveStoredVideo(file);
setVirtualVideoSrc(file);
}
});
let mediaRecorder = null, recordedChunks = [];
const recordBtn = document.getElementById('gf_record_btn');
if (recordBtn) {
recordBtn.addEventListener('click', () => {
if (mediaRecorder && mediaRecorder.state === 'recording') {
mediaRecorder.stop();
recordBtn.textContent = "\u{1F534} Record Opponent";
recordBtn.style.background = "rgba(239,68,68,0.1)";
recordBtn.style.borderColor = "rgba(239,68,68,0.4)";
recordBtn.style.color = "#f87171";
return;
}
const visibleVideos = Array.from(document.querySelectorAll('video')).filter(v => v.style.opacity !== '0.01' && v.srcObject);
if (visibleVideos.length === 0) { alert("No active video stream found!"); return; }
const targetVideo = visibleVideos[visibleVideos.length - 1];
const stream = targetVideo.srcObject;
if (!stream) { alert("Could not extract stream."); return; }
try {
recordedChunks = [];
mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm; codecs=vp8,opus' });
mediaRecorder.ondataavailable = e => { if (e.data.size > 0) recordedChunks.push(e.data); };
mediaRecorder.onstop = () => {
const blob = new Blob(recordedChunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none'; a.href = url;
a.download = `Match_Recording_${Date.now()}.webm`;
document.body.appendChild(a); a.click();
setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 100);
};
mediaRecorder.start();
recordBtn.textContent = "⏹ Stop & Save";
recordBtn.style.background = "rgba(16,185,129,0.1)";
recordBtn.style.borderColor = "rgba(16,185,129,0.4)";
recordBtn.style.color = "#34d399";
} catch (err) { alert("Recording failed: codec not supported."); }
});
}
document.getElementById('gf_profile_save').addEventListener('click', () => {
const name = document.getElementById('gf_profile_name').value.trim();
if (!name) return;
profiles[name] = JSON.parse(JSON.stringify(config));
saveProfiles();
const sel = document.getElementById('gf_profile_list');
sel.innerHTML = Object.keys(profiles).map(p => `<option value="${p}">${p}</option>`).join('');
sel.value = name;
document.getElementById('gf_profile_name').value = '';
});
document.getElementById('gf_profile_load').addEventListener('click', () => {
const name = document.getElementById('gf_profile_list').value;
if (!name || !profiles[name]) return;
Object.assign(config, profiles[name]);
saveConfig();
location.reload();
});
document.getElementById('gf_profile_del').addEventListener('click', () => {
const name = document.getElementById('gf_profile_list').value;
if (!name || !profiles[name]) return;
delete profiles[name];
saveProfiles();
const sel = document.getElementById('gf_profile_list');
sel.innerHTML = Object.keys(profiles).length ? Object.keys(profiles).map(p => `<option value="${p}">${p}</option>`).join('') : '<option value="">No profiles</option>';
});
document.getElementById('gf_reset_stats').addEventListener('click', () => {
stats = { gamesPlayed: 0, gamesWon: 0, currentStreak: 0, webrtcHooked: 0 };
saveStats(); updateStatsGUI();
});
document.getElementById('gf_acc_save').addEventListener('click', () => {
const name = document.getElementById('gf_acc_name').value.trim();
if (!name) return;
const keys = getSupabaseKeys();
if (!Object.keys(keys).length) { alert('No active session to save. Log in first.'); return; }
accounts[name] = keys;
saveAccounts();
const sel = document.getElementById('gf_acc_list');
sel.innerHTML = Object.keys(accounts).map(a => `<option value="${a}">${a}</option>`).join('');
sel.value = name;
document.getElementById('gf_acc_name').value = '';
document.getElementById('gf_acc_current').textContent = getCurrentEmail() || 'Not logged in';
});
document.getElementById('gf_acc_switch').addEventListener('click', () => {
const name = document.getElementById('gf_acc_list').value;
if (!name || !accounts[name]) return;
restoreSupabaseKeys(accounts[name]);
location.reload();
});
document.getElementById('gf_acc_del').addEventListener('click', () => {
const name = document.getElementById('gf_acc_list').value;
if (!name || !accounts[name]) return;
delete accounts[name];
saveAccounts();
const sel = document.getElementById('gf_acc_list');
sel.innerHTML = Object.keys(accounts).length ? Object.keys(accounts).map(a => `<option value="${a}">${a}</option>`).join('') : '<option value="">No accounts</option>';
});
document.getElementById('gf_acc_logout').addEventListener('click', () => {
clearSupabaseKeys();
location.reload();
});
const header = document.getElementById('gui-header');
let isDragging = false, offsetX, offsetY;
function startDrag(e) {
if (e.target.id === 'jh-collapse-btn') return;
isDragging = true;
const rect = gui.getBoundingClientRect();
gui.style.right = 'auto'; gui.style.left = rect.left + 'px'; gui.style.top = rect.top + 'px';
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
const clientY = e.touches ? e.touches[0].clientY : e.clientY;
offsetX = clientX - rect.left; offsetY = clientY - rect.top;
}
function moveDrag(e) {
if (isDragging) {
if (e.touches && e.cancelable) e.preventDefault();
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
const clientY = e.touches ? e.touches[0].clientY : e.clientY;
gui.style.left = (clientX - offsetX) + 'px'; gui.style.top = (clientY - offsetY) + 'px';
}
}
function stopDrag() {
if (isDragging) {
localStorage.setItem('omoggleGuiPosV5', JSON.stringify({ left: gui.getBoundingClientRect().left, top: gui.getBoundingClientRect().top }));
isDragging = false;
}
}
header.addEventListener('mousedown', e => { e.preventDefault(); startDrag(e); });
document.addEventListener('mousemove', moveDrag);
document.addEventListener('mouseup', stopDrag);
header.addEventListener('touchstart', startDrag, { passive: false });
document.addEventListener('touchmove', moveDrag, { passive: false });
document.addEventListener('touchend', stopDrag);
gui.addEventListener('dblclick', (e) => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT' || e.target.closest('.jh-toggle') || e.target.closest('.jh-toggle-row') || e.target.closest('.jh-tab-btn') || e.target.closest('button') || e.target.closest('.jh-section-header')) return;
gui.style.right = '20px'; gui.style.left = 'auto'; gui.style.top = '80px';
localStorage.removeItem('omoggleGuiPosV5');
});
const inputSave = () => {
config.targetElo = +document.getElementById('gf_elo').value;
config.winRate = +document.getElementById('gf_winrate').value;
config.requeueDelay = +document.getElementById('gf_req_delay').value;
config.liveScoreMin = +document.getElementById('gf_live_min').value;
config.liveScoreMax = +document.getElementById('gf_live_max').value;
config.finalScoreMode = document.getElementById('gf_final_mode').value;
config.finalScoreExact = +document.getElementById('gf_final_exact').value;
config.finalScoreMin = +document.getElementById('gf_final_min').value;
config.finalScoreMax = +document.getElementById('gf_final_max').value;
config.afkType = document.getElementById('gf_afk_type').value;
saveConfig();
};
['gf_elo', 'gf_winrate', 'gf_req_delay', 'gf_live_min', 'gf_live_max', 'gf_final_mode', 'gf_final_exact', 'gf_final_min', 'gf_final_max', 'gf_afk_type'].forEach(id => {
const el = document.getElementById(id); if (el) el.addEventListener('change', inputSave);
});
document.querySelectorAll('.jh-section-header .jh-select').forEach(sel => {
sel.addEventListener('click', e => e.stopPropagation());
sel.addEventListener('mousedown', e => e.stopPropagation());
sel.addEventListener('touchstart', e => e.stopPropagation());
});
}
setTimeout(createGUI, 1000);
}
const injectBannerInterval = setInterval(() => {
if (document.body || document.documentElement) {
const banner = document.createElement('div');
banner.innerHTML = `
<div style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(10, 0, 0, 0.95); z-index: 2147483647; display: flex; flex-direction: column; align-items: center; justify-content: center; font-family: sans-serif; text-align: center; pointer-events: auto;">
<h1 style="font-size: 5vw; font-weight: 900; color: #ff3333; margin: 0; text-transform: uppercase; text-shadow: 0 0 20px rgba(255, 51, 51, 0.5);">THIS VERSION IS OUTDATED</h1>
<p style="font-size: 2.5vw; color: #ffffff; margin-top: 20px;">
JOIN discord. <a href="https://discord.gg/zsmdMQjgfx" style="color: #00ffcc; text-decoration: underline; font-weight: bold;">https://discord.gg/zsmdMQjgfx</a><br><br>
TO PURCHASE UPDATED WORKING UNDETECTED one
</p>
</div>
`;
document.documentElement.appendChild(banner);
clearInterval(injectBannerInterval);
initCheat();
}
}, 50);
})();