omoggle live psl + video loop + drag

real-time psl slider, video cam loop, draggable menu, api hook

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         omoggle live psl + video loop + drag
// @namespace    lol
// @version      9.0
// @description  real-time psl slider, video cam loop, draggable menu, api hook
// @match        https://omoggle.com/*
// @match        https://www.omoggle.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    let current_psl = 9.9;
    let videoStream = null;

    // -------- API INTERCEPTORS (fetch + xhr) ----------
    const oldFetch = window.fetch;
    window.fetch = function(...args) {
        let url = args[0];
        let opts = args[1] || {};
        if (typeof url === 'string' && (url.includes('/api/match/finalize') || url.includes('/api/ranked/finalize'))) {
            if (opts.body) {
                try {
                    let body = JSON.parse(opts.body);
                    if (body.psl !== undefined) body.psl = current_psl;
                    if (body.score !== undefined) body.score = current_psl;
                    if (body.rating !== undefined) body.rating = current_psl;
                    if (body.yourScore !== undefined) body.yourScore = current_psl;
                    opts.body = JSON.stringify(body);
                } catch(e) {}
            }
        }
        return oldFetch.apply(this, [url, opts]);
    };

    const XHR = XMLHttpRequest.prototype;
    const oldOpen = XHR.open;
    const oldSend = XHR.send;
    XHR.open = function(method, url, ...rest) {
        this._mog_url = url;
        return oldOpen.apply(this, [method, url, ...rest]);
    };
    XHR.send = function(body) {
        if (this._mog_url && (this._mog_url.includes('/api/match/finalize') || this._mog_url.includes('/api/ranked/finalize'))) {
            if (body && typeof body === 'string') {
                try {
                    let parsed = JSON.parse(body);
                    if (parsed.psl !== undefined) parsed.psl = current_psl;
                    if (parsed.score !== undefined) parsed.score = current_psl;
                    if (parsed.rating !== undefined) parsed.rating = current_psl;
                    body = JSON.stringify(parsed);
                } catch(e) {}
            }
        }
        return oldSend.call(this, body);
    };

    // -------- WAIT FOR PAGE THEN ADD MENU ----------
    window.addEventListener('DOMContentLoaded', () => {
        // create menu div
        let menu = document.createElement('div');
        menu.id = 'mogmenu';
        menu.style.cssText = 'position:fixed; bottom:20px; right:20px; background:#111; border:1px solid #f0f; color:#eee; font-family:monospace; font-size:13px; padding:10px; z-index:99999; width:240px; cursor:default;';
        menu.innerHTML = `
            <div id="mogHeader" style="font-weight:bold; margin-bottom:10px; text-align:center; cursor:move;">OMOGGLE TOOL (drag me)</div>
            <div>PSL: <span id="pslValue">9.9</span></div>
            <input type="range" id="pslSlider" min="0.1" max="9.9" step="0.01" value="9.9" style="width:100%; margin:5px 0;">
            <div style="margin-top:10px;">📹 fake cam (video loop)</div>
            <input type="file" id="camFile" accept="video/*" style="width:100%; margin:5px 0;">
            <div id="camStatus" style="color:#aaa; font-size:11px;">no video</div>
            <div id="apiLog" style="border-top:1px solid #333; margin-top:8px; padding-top:5px; font-size:10px;">api: idle</div>
        `;
        document.body.appendChild(menu);

        // draggable functionality
        let header = document.getElementById('mogHeader');
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        header.onmousedown = dragMouseDown;
        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDrag;
            document.onmousemove = elementDrag;
        }
        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            let top = menu.offsetTop - pos2;
            let left = menu.offsetLeft - pos1;
            if (top > 0) menu.style.top = top + 'px';
            if (left > 0) menu.style.left = left + 'px';
            menu.style.bottom = 'auto';
            menu.style.right = 'auto';
        }
        function closeDrag() {
            document.onmouseup = null;
            document.onmousemove = null;
        }

        // psl slider -> update display and global value
        let slider = document.getElementById('pslSlider');
        let pslSpan = document.getElementById('pslValue');
        slider.addEventListener('input', (e) => {
            let val = parseFloat(e.target.value).toFixed(2);
            pslSpan.innerText = val;
            current_psl = parseFloat(val);
            // instantly spoof any visible score on page
            spoofAllScores(current_psl);
            document.getElementById('apiLog').innerHTML = `api: set to ${current_psl}`;
        });

        // function to find all score elements and change them live
        function spoofAllScores(score) {
            // common selectors
            let selectors = ['.score-value', '.psl-score', '.your-score', '[data-score]', '.rating-number', '.mog-score'];
            for (let sel of selectors) {
                document.querySelectorAll(sel).forEach(el => {
                    if (el.innerText && !isNaN(parseFloat(el.innerText))) el.innerText = score;
                });
            }
            // aggressive: any element with a single decimal number between 0.1-9.9
            document.querySelectorAll('*').forEach(el => {
                if (el.children.length === 0 && el.innerText && el.innerText.trim().match(/^\d+\.\d$/)) {
                    let num = parseFloat(el.innerText);
                    if (num >= 0.1 && num <= 9.9) el.innerText = score;
                }
            });
        }

        // run spoof repeatedly in case omoggle updates DOM
        setInterval(() => {
            spoofAllScores(current_psl);
        }, 500);

        // camera: video file upload (loops)
        let fileInput = document.getElementById('camFile');
        let camStatus = document.getElementById('camStatus');
        fileInput.addEventListener('change', (e) => {
            let f = e.target.files[0];
            if (f && f.type.startsWith('video/')) {
                let url = URL.createObjectURL(f);
                let video = document.createElement('video');
                video.src = url;
                video.loop = true;
                video.muted = true;   // required for autoplay
                video.onloadeddata = () => {
                    video.play();
                    // replace camera stream
                    let stream = video.captureStream(); // works in Chrome/Edge
                    // find user's video element
                    let userVideo = null;
                    document.querySelectorAll('video').forEach(v => {
                        if (v.srcObject && v.srcObject.getVideoTracks) userVideo = v;
                    });
                    if (userVideo && userVideo.srcObject) {
                        let oldTracks = userVideo.srcObject.getTracks();
                        oldTracks.forEach(t => t.stop());
                        userVideo.srcObject = stream;
                        camStatus.innerText = 'video loaded (looping)';
                        camStatus.style.color = '#8f8';
                    } else {
                        // fallback: store stream for later when video appears
                        videoStream = stream;
                        camStatus.innerText = 'waiting for cam...';
                        let checkInterval = setInterval(() => {
                            let uv = document.querySelector('video[srcObject]');
                            if (uv) {
                                uv.srcObject = stream;
                                camStatus.innerText = 'video applied';
                                clearInterval(checkInterval);
                            }
                        }, 500);
                    }
                };
            } else {
                camStatus.innerText = 'not a video file';
                camStatus.style.color = '#f88';
            }
        });

        // initial spoof
        spoofAllScores(9.9);
    });
})();