سكربت فايبر هوم لتغيير ip بضغطة زر والبحث عن ip معين مع اختبار السرعه

حملتtampermonkey و أضفت البرنامج النصي بس ماطلعتلي القائمه

الله يجزاك خير ضبط معي الاصدار الجديد وشغال تمام

إعجابَين (2)

عالبركة و تستاهل كل خير, ياريت تسويلنا شرح بالجوال و بكتب موضوع بإسمك للفائدة.

اللي ما ضبط معه على بريف يجرب يغير خيار تبليك الكوكيز يحطه block all cookies بعدين رجعه على ماهو وحدث الصفحه، كل السكربتين شغاله :+1:

إعجابَين (2)

تسلم ماتقصر

فيه اكثر من طريقة لتشغيل السكربت على الجوال

اضافة tampermonkey ( مدفوعة )

Stay for safari بديلة

تحمل التطبيق وتتبع الشرح اللي داخل التطبيق لتشغيل الاضافة على سفاري ، بعدها تنسخ الكود بداخل الاضافة او تحمل الكود بصيغة .js منgithub كلها تنفع

تختار الاضافة اللي تبغى تفعلها ( يفضل الاصدار الثاني لتوافقة بشكل افل مع الجوال )

وهنا يكون تفعل السكربت

اتمنى الشرح سهل وكل التوفيق

إعجاب واحد (1)

الحل

3 إعجابات

انا ماضبط معي بالاول لين دخلت علي السكربت واضافة حرف S بعد http في السطر السابع

وصار

واشتغل معي ان شاء الله يضبط مع الجميع

ياليت يتعدل السكربت ويكتب الكود هذا

شكراً يا @ABO-SULTAN على ردك و شرحك المميز للمشكلة و حلها

نسخة إلى المبدع @0xfff للتعديل على Github و إختبار السكربت بعد التعديل.

:+1: يا سلام عليك

بارك الله في جهودكم تذكر فتشكر.

عندي استفسار .

عند البحث و تثبيت ال IP الجديد هل يستمر حتى بعد إطفاء الجهاز وتشغيل الجهاز مرة أخرى؟

لا بيتغير معك ..

رفض ادخل على تطبيق tamperemonkey يعطيني إغلاق وفتح تفضيلات إضافات سفاري ويحولني للتطبيقات ويش الحل

++ تحديث جديد للسكربت لمن يعاني من مشكلة عدم ظهور WAN IP أو Public IP, حصلت لي شخصياً و قمت بالتعديل عليها :

// ==UserScript==
// @name         Advanced IP Kha
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Monitor IPs with automatic speed testing via Fast.com - Modem Interface Style
// @author       Fahad (Twitter: @kkoao1)
// @match        http://192.168.8.1/*
// @match        https://fast.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        window.close
// ==/UserScript==

let speedTestRetryCount = 0;
let lastKnownWAN = "";
let currentLang = GM_getValue('ipMonitorLang', 'en');
let ipPinningAttempts = 0;

// Language translations
const translations = {
    en: {
        pageTitle: "IP Tools & Network Monitor",
        mainTitle: "IP Tools",
        status: {
            connected: "Connected",
            disconnected: "Disconnected",
            connecting: "Connecting"
        },
        buttons: {
            changeIP: "Change IP",
            speedTest: "Speed Test",
            stopAuto: "Stop Auto",
            saveLog: "Save Log",
            close: "Close",
            clearLog: "Clear",
            statsTable: "Speed Table"
        },
        labels: {
            networkStatus: "Network Status",
            autoSpeedTest: "Auto Speed Test Mode",
            autoSpeedDesc: "Automatically test speed for each new IP",
            networkLog: "Network Log",
            ipPinning: "IP Pinning",
            ipPinDesc: "Auto-change IP until match found",
            startPinning: "Start Pinning",
            wanIP: "WAN IP",
            publicIP: "Public IP",
            speed: "Speed",
            upload: "Upload",
            ping: "Ping",
            time: "Time",
            status: "Status",
            currentNetwork: "Current Network Information",
            quickActions: "Quick Actions",
            advancedSettings: "Advanced Settings",
            statistics: "Statistics & Logs"
        },
        messages: {
            noLogs: "No logs yet...",
            openingFast: "Opening Fast.com for speed test...",
            waitingTest: "Waiting for speed test to complete...",
            networkError: "Network error during speed test, retrying in 3 seconds...",
            maxRetries: "Reached maximum retries for speed test. Please check your internet and try again.",
            waitingNext: "Waiting 3 seconds before next IP change...",
            testTimeout: "Speed test timeout - no results obtained",
            processRunning: "Process already running!",
            startingIPChange: "Starting IP change process...",
            loadingAirplane: "Loading airplane mode settings...",
            enablingAirplane: "Enabling airplane mode...",
            airplaneEnabled: "Airplane mode enabled",
            waiting: "Waiting",
            seconds: "seconds",
            disablingAirplane: "Disabling airplane mode...",
            airplaneDisabled: "Airplane mode disabled",
            waitingReconnect: "Waiting for reconnection...",
            ipChangeComplete: "IP change completed!",
            error: "Error",
            testCompleted: "Test completed! Closing...",
            speedTestResults: "Speed Test Results",
            testCount: "Test Count",
            ipFound: "Target IP found! Stopping...",
            searchingIP: "Searching for matching IP...",
            pinningActive: "IP Pinning Active"
        },
        statsTable: {
            title: "Speed Test Results",
            headers: {
                number: "#",
                wanIP: "WAN IP",
                publicIP: "Public IP",
                speed: "Speed",
                upload: "Upload",
                ping: "Ping"
            }
        }
    },
    ar: {
        pageTitle: "أدوات IP ومراقب الشبكة",
        mainTitle: "أدوات IP",
        status: {
            connected: "متصل",
            disconnected: "غير متصل",
            connecting: "جاري الاتصال"
        },
        buttons: {
            changeIP: "تغيير IP",
            speedTest: "اختبار السرعة",
            stopAuto: "إيقاف تلقائي",
            saveLog: "حفظ السجل",
            close: "إغلاق",
            clearLog: "مسح",
            statsTable: "جدول السرعات"
        },
        labels: {
            networkStatus: "حالة الشبكة",
            autoSpeedTest: "وضع اختبار السرعة التلقائي",
            autoSpeedDesc: "اختبار السرعة تلقائياً لكل IP جديد",
            networkLog: "سجل الشبكة",
            ipPinning: "تثبيت IP",
            ipPinDesc: "تغيير IP تلقائياً حتى العثور على التطابق",
            startPinning: "بدء التثبيت",
            wanIP: "IP الشبكة",
            publicIP: "IP العام",
            speed: "السرعة",
            upload: "الرفع",
            ping: "البنج",
            time: "الوقت",
            status: "الحالة",
            currentNetwork: "معلومات الشبكة الحالية",
            quickActions: "إجراءات سريعة",
            advancedSettings: "إعدادات متقدمة",
            statistics: "الإحصائيات والسجلات"
        },
        messages: {
            noLogs: "لا توجد سجلات بعد...",
            openingFast: "فتح Fast.com لاختبار السرعة...",
            waitingTest: "انتظار اكتمال اختبار السرعة...",
            networkError: "خطأ في الشبكة أثناء اختبار السرعة، إعادة المحاولة بعد 3 ثواني...",
            maxRetries: "تم الوصول للحد الأقصى من المحاولات. تحقق من الإنترنت وحاول مرة أخرى.",
            waitingNext: "انتظار 3 ثواني قبل تغيير IP التالي...",
            testTimeout: "انتهت مهلة اختبار السرعة - لم يتم الحصول على نتائج",
            processRunning: "العملية قيد التشغيل بالفعل!",
            startingIPChange: "بدء عملية تغيير IP...",
            loadingAirplane: "تحميل إعدادات وضع الطيران...",
            enablingAirplane: "تفعيل وضع الطيران...",
            airplaneEnabled: "تم تفعيل وضع الطيران",
            waiting: "انتظار",
            seconds: "ثواني",
            disablingAirplane: "إلغاء وضع الطيران...",
            airplaneDisabled: "تم إلغاء وضع الطيران",
            waitingReconnect: "انتظار إعادة الاتصال...",
            ipChangeComplete: "اكتملت عملية تغيير IP!",
            error: "خطأ",
            testCompleted: "اكتمل الاختبار! جاري الإغلاق...",
            speedTestResults: "نتائج اختبارات السرعة",
            testCount: "عدد الاختبارات",
            ipFound: "تم العثور على IP المستهدف! جاري الإيقاف...",
            searchingIP: "البحث عن IP مطابق...",
            pinningActive: "تثبيت IP نشط"
        },
        statsTable: {
            title: "نتائج اختبارات السرعة",
            headers: {
                number: "#",
                wanIP: "IP الشبكة",
                publicIP: "IP العام",
                speed: "السرعة",
                upload: "الرفع",
                ping: "البنج"
            }
        }
    }
};

// Get translation
function t(key) {
    const keys = key.split('.');
    let value = translations[currentLang];
    for (const k of keys) {
        value = value[k];
    }
    return value || key;
}

(function() {
    'use strict';

    // Check if we're on Fast.com
    if (window.location.hostname === 'fast.com') {
        handleFastCom();
        return;
    }

    // Check if we're on IP Tools page
    if (window.location.hash === '#/iptools') {
        setTimeout(createIPToolsPage, 100);
        return;
    }

function addSidebarBtn() {
    let sidebarMenu = document.querySelector('.el-menu');
    if (!sidebarMenu || document.getElementById('fh_router_world_group')) return false;

    let group = document.createElement('div');
    group.className = 'el-submenu';
    group.id = 'fh_router_world_group';
    group.style.marginTop = '10px';

    group.innerHTML = `
        <div class="el-submenu__title" style="cursor:pointer; display: flex; align-items: center; gap: 10px;">
            <i class="icon-fh-apps"></i>
            <span style="font-size: 20px; font-weight: bold; color: #155fa0; letter-spacing: 0.5px; font-family: 'Tajawal', 'Segoe UI', Arial, sans-serif;">
                routers.world
            </span>
            <i class="el-submenu__icon-arrow el-icon-arrow-down" style="margin-right:auto;"></i>
        </div>
        <ul class="el-menu el-menu--inline">
            <li id="fh_ip_tools_item" class="el-menu-item" tabindex="-1" style="padding-left: 40px; color: rgb(100,100,100); font-size:15px;">
                <i class="icon-fh-apps"></i>
                <span title="IP Tools">IP Tools</span>
            </li>
        </ul>
    `;

    sidebarMenu.appendChild(group);

    let submenuTitle = group.querySelector('.el-submenu__title');
    let submenuList = group.querySelector('.el-menu--inline');
    submenuList.style.display = 'none';
    submenuTitle.onclick = () => {
        submenuList.style.display = (submenuList.style.display === 'none') ? 'block' : 'none';
    };

    group.querySelector('#fh_ip_tools_item').onclick = function(e) {
        e.preventDefault();
        e.stopPropagation();
        showIPToolsPage && showIPToolsPage();
        document.querySelectorAll('.el-menu-item').forEach(item => item.classList.remove('is-active'));
        this.classList.add('is-active');
        submenuList.style.display = 'block';
        return false;
    };

    return true;
}



    // دالة لإظهار صفحة IP Tools
function showIPToolsPage() {
    let mainContent = document.querySelector('.el-main');
    if (!mainContent) return; // إذا الصفحة غير مناسبة، لا تسوي شي

    // حفظ المحتوى الأصلي إذا لم يكن محفوظاً
    if (!window._originalMainContent) {
        window._originalMainContent = mainContent.innerHTML;
    }

    // إنشاء صفحة IP Tools
    createIPToolsPage();
}



    // Try to add sidebar button
    if (!addSidebarBtn()) {
        let sidebarInterval = setInterval(()=>{
            if (addSidebarBtn()) clearInterval(sidebarInterval);
        }, 500);
    }

    // Watch for hash changes
    window.addEventListener('hashchange', function() {
        if (window.location.hash === '#/iptools') {
            setTimeout(createIPToolsPage, 100);
        }
    });

    // -- STATE --
    let logList = [];
    let interval = null;
    let watcherActive = false;
    let prev = {status:"", wan:"", pub:""};
    let airplaneProcess = {active: false};
    let speedTestEnabled = false;
    let speedTestCycle = false;
    let speedTestWindow = null;
    let speedTestInProgress = false;
    let ipPinningActive = false;
    let targetWAN = "";
    let targetPublic = "";

    // Create IP Tools Page




function createIPToolsPage() {
    let mainContent = document.querySelector('.el-main');
    if (!mainContent) return;

    let pageContent = document.getElementById('ip_tools_content');
    if (!pageContent) {
        // Clear existing content
        mainContent.innerHTML = '';

        pageContent = document.createElement('div');
        pageContent.id = 'ip_tools_content';
        // هنا لازم تضيفه للـ main
        mainContent.appendChild(pageContent);
    }

    pageContent.innerHTML = `
    <div>
        <div class="page_title" style="display: flex; align-items: center; justify-content: space-between;">
            <div style="display: flex; align-items: center; gap: 10px;">
                <div class="head_line" style="font-size: 1.5rem; font-weight: bold;">
                    ${t('pageTitle')}
                </div>
                <button id="fh_lang_toggle" style="margin-left: 15px; background: #409eff; color: #fff; border: none; border-radius: 5px; padding: 4px 15px; font-weight: bold; cursor: pointer;">
                    ${currentLang === 'ar' ? 'English' : 'العربية'}
                </button>
            </div>
            <img src="https://routers.world/uploads/default/original/1X/237b3e2cbe9195c703d242957c2d4af3061a78ac.svg"
                 alt="routers.world"
                 style="height: 40px; margin-left: 20px; filter: drop-shadow(0 1px 4px #ccc;" />
        </div>
        <div class="page_content">
            <!-- Network Status Bar -->
            <div style="background: #e3f2fd; padding: 15px; border-radius: 8px; margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center;">
                <div style="display: flex; gap: 30px; align-items: center;">
                    <div>
                        <span style="color: #666; font-size: 14px;">${t('labels.status')}:</span>
                        <span id="fh_network_status" style="font-weight: bold; margin-left: 10px;">-</span>
                    </div>
                    <div>
                        <span style="color: #666; font-size: 14px;">${t('labels.wanIP')}:</span>
                        <span id="fh_wan_ip" style="font-weight: bold; color: #409eff; margin-left: 10px;">-</span>
                    </div>
                    <div>
                        <span style="color: #666; font-size: 14px;">${t('labels.publicIP')}:</span>
                        <span id="fh_public_ip" style="font-weight: bold; color: #e6a23c; margin-left: 10px;">-</span>
                    </div>
                </div>
                <div id="fh_process_status" style="display: none; background: #e6a23c; color: #fff; padding: 5px 15px; border-radius: 20px; font-size: 14px; font-weight: bold;"></div>
            </div>

            <!-- Main Content Grid -->
            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
                <!-- Left Column -->
                <div>
                    <!-- Quick Actions -->
                    <div class="page_sec_head">
                        ${t('labels.quickActions')}
                    </div>
                    <div style="background: #f5f5f5; padding: 20px; border-radius: 8px; margin-top: 10px;">
                        <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px;">
                            <button id="fh_change_ip" class="el-button el-button--primary">
                                <span>✈️ ${t('buttons.changeIP')}</span>
                            </button>
                            <button id="fh_speed_test" class="el-button el-button--success">
                                <span>⚡ ${t('buttons.speedTest')}</span>
                            </button>
                            <button id="fh_stats_btn" class="el-button el-button--info">
                                <span>📊 ${t('buttons.statsTable')}</span>
                            </button>
                        </div>
                    </div>

                    <!-- Settings -->
                    <div class="page_sec_head" style="margin-top: 20px;">
                        ${t('labels.advancedSettings')}
                    </div>
                    <div style="background: #f5f5f5; padding: 20px; border-radius: 8px; margin-top: 10px;">
                        <!-- Auto Speed Test -->
                        <div style="display: flex; align-items: center; justify-content: space-between; padding: 10px; background: #fff; border-radius: 6px; margin-bottom: 10px;">
                            <label style="display: flex; align-items: center; gap: 10px; cursor: pointer; flex: 1;">
                                <input type="checkbox" id="fh_auto_speed" style="width: 18px; height: 18px;">
                                <div>
                                    <div style="font-weight: bold;">🚀 ${t('labels.autoSpeedTest')}</div>
                                    <div style="color: #666; font-size: 12px;">${t('labels.autoSpeedDesc')}</div>
                                </div>
                            </label>
                            <button id="fh_stop_auto" class="el-button el-button--danger el-button--small">
                                ${t('buttons.stopAuto')}
                            </button>
                        </div>

                        <!-- IP Pinning -->
                        <div style="padding: 10px; background: #fff; border-radius: 6px;">
                            <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
                                <div>
                                    <span style="font-weight: bold;">📌 ${t('labels.ipPinning')}</span>
                                    <span id="fh_pinning_counter" style="color: #e6a23c; font-weight: bold; margin-left: 10px; display: none;">
                                        (<span id="fh_pinning_count">0</span>)
                                    </span>
                                    <div style="color: #666; font-size: 12px;">${t('labels.ipPinDesc')}</div>
                                </div>
                                <button id="fh_start_pinning" class="el-button el-button--warning el-button--small">
                                    ${t('labels.startPinning')}
                                </button>
                            </div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
                                <input type="text" id="fh_target_wan" placeholder="WAN: 10.193.89" class="el-input__inner">
                                <input type="text" id="fh_target_public" placeholder="Public: 51.253.184" class="el-input__inner">
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Right Column - Network Log -->
                <div>
                    <div class="page_sec_head" style="display: flex; justify-content: space-between; align-items: center;">
                        <span>${t('labels.networkLog')}</span>
                        <div>
                            <button id="fh_clear_log" class="el-button el-button--text" style="padding: 5px 10px;">
                                ${t('buttons.clearLog')}
                            </button>
                            <button id="fh_save_log" class="el-button el-button--text" style="padding: 5px 10px;">
                                💾 ${t('buttons.saveLog')}
                            </button>
                        </div>
                    </div>
                    <div id="fh_ip_log" style="height: 350px; overflow-y: auto; background: #f5f5f5; padding: 15px; border-radius: 8px; margin-top: 10px; border: 1px solid #e4e7ed;">
                        <div style="color: #999; text-align: center; padding: 20px;">${t('messages.noLogs')}</div>
                    </div>
                </div>
            </div>

            <!-- Footer -->
            <div style="text-align: center; margin-top: 30px; padding: 15px; color: #666; border-top: 1px solid #e4e7ed;">
                <div style="font-size: 14px;">
                    Developed by Fahad |
                    <a href="https://routers.world/" target="_blank" style="color: #409EFF; text-decoration: none;">
                        routers.world
                    </a>
                </div>
            </div>
        </div>
    </div>
    `;

    // زر تبديل اللغة
    let langBtn = document.getElementById('fh_lang_toggle');
    if (langBtn) {
        langBtn.onclick = () => {
            currentLang = currentLang === 'ar' ? 'en' : 'ar';
            GM_setValue('ipMonitorLang', currentLang);
            createIPToolsPage();
        };
    }

    // (اختياري) اتجاه الصفحة عربي أو إنجليزي
    pageContent.dir = (currentLang === 'ar') ? 'rtl' : 'ltr';

    // Add event listeners
    document.getElementById('fh_change_ip').onclick = triggerAirplaneMode;
    document.getElementById('fh_speed_test').onclick = () => runSpeedTest();
    document.getElementById('fh_clear_log').onclick = () => { logList = []; updateLogDisplay(); };
    document.getElementById('fh_save_log').onclick = saveLogToFile;
    document.getElementById('fh_auto_speed').onchange = (e) => { speedTestEnabled = e.target.checked; };
    document.getElementById('fh_stats_btn').onclick = showStatsWindow;
    document.getElementById('fh_start_pinning').onclick = startIPPinning;
    document.getElementById('fh_stop_auto').onclick = () => {
        speedTestEnabled = false;
        ipPinningActive = false;
        let cb = document.getElementById('fh_auto_speed');
        if(cb) cb.checked = false;
        updateProcessStatus("");
        let counter = document.getElementById('fh_pinning_counter');
        if(counter) counter.style.display = 'none';
    };

    // Update sidebar active state
    updateSidebarActiveState();

    // Start monitoring
    updateInfo(true);
    if(interval) clearInterval(interval);
    interval = setInterval(() => updateInfo(), 2000);
}


    function updateSidebarActiveState() {
        // Remove active from all items
        document.querySelectorAll('.el-menu-item').forEach(item => {
            item.classList.remove('is-active');
            item.style.color = '#646464';
        });

        // Remove active from all links
        document.querySelectorAll('.el-menu a').forEach(link => {
            link.classList.remove('router-link-active', 'router-link-exact-active');
        });

        // Add active to IP Tools
        let ipToolsLink = document.querySelector('#fh_ip_monitor_link');
        if (ipToolsLink) {
            ipToolsLink.classList.add('router-link-active', 'router-link-exact-active');
            let menuItem = ipToolsLink.querySelector('.el-menu-item');
            if (menuItem) {
                menuItem.classList.add('is-active');
                menuItem.style.color = '#ff6225 !important';
            }
        }
    }

    // IP Pinning function
    function startIPPinning() {
        targetWAN = document.getElementById('fh_target_wan').value.trim();
        targetPublic = document.getElementById('fh_target_public').value.trim();

        if (!targetWAN && !targetPublic) {
            alert("Please enter at least one IP to search for!");
            return;
        }

        ipPinningActive = true;
        ipPinningAttempts = 0;
        speedTestEnabled = false;
        document.getElementById('fh_auto_speed').checked = false;

        logList.unshift(`<div style="color: #e6a23c; font-weight: bold; background: #fdf6ec; padding: 8px; border-radius: 4px; margin-bottom: 8px;">🔍 ${t('messages.searchingIP')}</div>`);
        updateLogDisplay();

        triggerAirplaneMode();
    }

    function checkIPMatch() {
        if (!ipPinningActive) return false;

        let wanMatch = false;
        let publicMatch = false;

        function checkIPPattern(ip, pattern) {
            if (!ip || ip === "Unknown" || ip === "Failed!") return false;

            const firstOctet = ip.split('.')[0];

            if (pattern.includes('x')) {
                const excludedOctets = pattern.split('x').map(num => num.trim()).filter(num => num);
                return !excludedOctets.includes(firstOctet);
            }
            else if (pattern.includes('-')) {
                const allowedOctets = pattern.split('-').map(num => num.trim());
                return allowedOctets.includes(firstOctet);
            } else {
                return ip.startsWith(pattern);
            }
        }

        if (targetWAN && prev.wan) {
            wanMatch = checkIPPattern(prev.wan, targetWAN);
        }

        if (targetPublic && prev.pub) {
            publicMatch = checkIPPattern(prev.pub, targetPublic);
        }

        if (targetWAN && targetPublic) {
            return wanMatch && publicMatch;
        }
        return wanMatch || publicMatch;
    }

    // Monitor & Log
    async function updateInfo(forceLog=false, retries=1) {
        let modem = await getModemInfo();
        if ((modem.wanip==="Unknown" || modem.status==="Unknown") && retries > 0) {
            await new Promise(r=>setTimeout(r,1200));
            return updateInfo(forceLog, retries-1);
        }

        let publicip = await fetchPublicIP();
        let status = /connected/i.test(modem.status) ? "Connected" :
                     /disconnected/i.test(modem.status) ? "Disconnected" :
                     /connecting/i.test(modem.status) ? "Connecting" :
                     modem.status;
        let wanip = modem.wanip;

        if (wanip && wanip !== "Unknown") lastKnownWAN = wanip;

        // Update status in table
        let statusElem = document.getElementById('fh_network_status');
        let wanElem = document.getElementById('fh_wan_ip');
        let pubElem = document.getElementById('fh_public_ip');

        if (statusElem) {
            statusElem.innerHTML = status === "Connected"
                ? `<span style="color: #67c23a;">📶 ${t('status.connected')}</span>`
                : status === "Disconnected"
                    ? `<span style="color: #f56c6c;">✈️ ${t('status.disconnected')}</span>`
                    : status === "Connecting"
                        ? `<span style="color: #e6a23c;">🔄 ${t('status.connecting')}...</span>`
                        : `<span style="color: #e6a23c;">${status}</span>`;
        }

        if (wanElem) wanElem.textContent = wanip;
        if (pubElem) pubElem.textContent = publicip;

        if (forceLog || prev.status!==status || prev.wan!==wanip || prev.pub!==publicip) {
            logStatus(status, wanip, publicip);
            prev = {status, wan:wanip, pub:publicip};

            if (ipPinningActive && status === "Connected" && checkIPMatch()) {
                ipPinningActive = false;
                updateProcessStatus("");
                document.getElementById('fh_pinning_counter').style.display = 'none';
                logList.unshift(`<div style="color: #67c23a; font-weight: bold; background: #f0f9ff; padding: 8px; border-radius: 4px; margin-bottom: 8px;">🎯 ${t('messages.ipFound')} (${currentLang === 'ar' ? 'بعد' : 'After'} ${ipPinningAttempts} ${currentLang === 'ar' ? 'محاولة' : 'attempts'})</div>`);
                updateLogDisplay();
                return;
            }

            if (ipPinningActive && status === "Connected" && !airplaneProcess.active) {
                ipPinningAttempts++;
                let counterElem = document.getElementById('fh_pinning_counter');
                let countElem = document.getElementById('fh_pinning_count');
                if (counterElem) counterElem.style.display = 'inline';
                if (countElem) countElem.textContent = ipPinningAttempts;

                setTimeout(()=>{
                    if (ipPinningActive && prev.status === "Connected" && !airplaneProcess.active) {
                        updateProcessStatus(`${t('messages.pinningActive')} - ${t('messages.searchingIP')}`);
                        triggerAirplaneMode();
                    }
                }, 3000);
            }

            if (speedTestEnabled && !speedTestInProgress && status === "Connected" &&
                publicip !== "Failed!" && publicip !== "Unknown" &&
                (prev.pub !== publicip || speedTestCycle)) {
                setTimeout(()=>{
                    if (prev.status === "Connected" && !speedTestInProgress) {
                        runSpeedTest(true);
                    }
                }, 3000);
            }
        }
    }

    function logStatus(status, wan, pub, speedData = null) {
        let time = new Date().toLocaleTimeString(currentLang === 'ar' ? 'ar-SA' : 'en-US');
        let entry = `<div style="background: #f5f5f5; padding: 12px; border-radius: 4px; margin-bottom: 8px; border: 1px solid #e4e7ed;">`;
        entry += `<span style="color: #e6a23c; font-weight: bold;">${time}</span> | `;
        entry += `<b>${t('labels.status')}:</b> <span style="color: ${status=="Connected"?'#67c23a':'#f56c6c'}; font-weight: bold">${t(`status.${status.toLowerCase()}`)}</span>`;
        entry += `<br><b>${t('labels.wanIP')}:</b> <span style="color: #409eff">${wan}</span>`;
        entry += `<br><b>${t('labels.publicIP')}:</b> <span style="color: #e6a23c">${pub}</span>`;
        if (speedData) {
            entry += `<br><b>${t('labels.speed')}:</b> <span style="color: #67c23a">${speedData.download} Mbps</span>`;
            entry += ` | <b>${t('labels.ping')}:</b> <span style="color: #e6a23c">${speedData.ping} ms</span>`;
            entry += ` | <b>${t('labels.upload')}:</b> <span style="color: #909399">${speedData.upload} Mbps</span>`;
        }
        entry += `</div>`;
        logList.unshift(entry);
        updateLogDisplay();
    }

    function updateLogDisplay() {
        let el = document.getElementById('fh_ip_log');
        if(el) el.innerHTML = logList.join("") || `<div style="color: #999; text-align: center; padding: 20px;">${t('messages.noLogs')}</div>`;
    }

    // GET WAN IP + STATUS
function getModemInfo() {
    return new Promise(resolve => {
        let ifr = document.getElementById('fh_net_iframe');
        if (ifr) ifr.remove();

        ifr = document.createElement('iframe');
        ifr.id = 'fh_net_iframe';
        ifr.style.display = 'none';
        ifr.src = "/main.html#/networkInfo"; // ✅ رابط صفحة المعلومات
        document.body.appendChild(ifr);

        setTimeout(() => {
            try {
                let doc = ifr.contentDocument || ifr.contentWindow.document;
                let netStatus = "Unknown", wanIP = "Unknown";

                doc.querySelectorAll("table tr").forEach(tr => {
                    let th = tr.querySelector("th");
                    let td = tr.querySelector("td");
                    if (!th || !td) return;

                    let label = th.textContent.trim();
                    let value = td.textContent.trim();

                    if (label === "WAN IP Address" || label === "عنوان IP للـ WAN") {
                        wanIP = value;
                    }

                    if (label === "Network Status" || label === "حالة الشبكة") {
                        netStatus = value;
                    }
                });

                if (wanIP === "" || wanIP === "-") wanIP = "Unknown";
                if (netStatus === "" || netStatus === "-") netStatus = "Unknown";

                resolve({ status: netStatus, wanip: wanIP });
            } catch (e) {
                resolve({ status: "Unknown", wanip: "Unknown" });
            }

            setTimeout(() => { ifr.remove(); }, 500);
        }, 1200);
    });
}

    // GET PUBLIC IP
    async function fetchPublicIP() {
        let sources = [
            async()=>{ let r=await fetch("https://api.ipify.org?format=json"); return (await r.json()).ip; },
            async()=>{ let r=await fetch("https://ipv4.icanhazip.com/"); return (await r.text()).trim(); },
            async()=>{ let r=await fetch("https://ipinfo.io/json"); return (await r.json()).ip; },
        ];
        for (let fn of sources) {
            try {
                let ip = await fn();
                if (/^\d+\.\d+\.\d+\.\d+$/.test(ip)) return ip;
            } catch(e){}
        }
        return "Failed!";
    }

    // SPEED TEST
    function runSpeedTest(auto = false) {
        if (speedTestInProgress) {
            console.log('Speed test already in progress, skipping...');
            return;
        }

        if (speedTestWindow && !speedTestWindow.closed) {
            speedTestWindow.close();
        }

        speedTestInProgress = true;
        updateProcessStatus(`🚀 ${t('messages.openingFast')}`);
        GM_setValue('speedTestRequested', true);
        GM_setValue('currentPublicIP', prev.pub);

        speedTestWindow = window.open('https://fast.com/', '_blank');

        setTimeout(()=>{
            if (speedTestInProgress) {
                updateProcessStatus(`⏳ ${t('messages.waitingTest')}`);
            }
        }, 3000);

        let timeout = null;

        let checkInterval = setInterval(()=>{
            if (GM_getValue('speedTestShouldRetry', false)) {
                GM_setValue('speedTestShouldRetry', false);
                speedTestInProgress = false;
                if (speedTestWindow && !speedTestWindow.closed) {
                    speedTestWindow.close();
                }
                speedTestWindow = null;
                speedTestRetryCount++;

                logList.unshift(`<div style="color: #f56c6c;">🔁 ${t('messages.networkError')} (Retry #${speedTestRetryCount})</div>`);
                updateLogDisplay();

                clearInterval(checkInterval);
                if (timeout) clearTimeout(timeout);

                if (speedTestRetryCount >= 5) {
                    logList.unshift(`<div style="color: #f56c6c;">❌ ${t('messages.maxRetries')}</div>`);
                    updateLogDisplay();
                    speedTestRetryCount = 0;
                    return;
                }
                setTimeout(()=>{ runSpeedTest(auto); }, 3000);
                return;
            }

            let result = GM_getValue('speedTestResult', null);
            if (result) {
                GM_setValue('speedTestResult', null);
                clearInterval(checkInterval);
                if (timeout) clearTimeout(timeout);

                speedTestInProgress = false;
                speedTestRetryCount = 0;

                logStatus("Connected", lastKnownWAN, prev.pub, result);
                updateProcessStatus("");

                if (speedTestWindow && !speedTestWindow.closed) {
                    speedTestWindow.close();
                }
                speedTestWindow = null;

                if (auto && speedTestCycle && speedTestEnabled) {
                    logList.unshift(`<div style="color: #e6a23c;">⏳ ${t('messages.waitingNext')}</div>`);
                    updateLogDisplay();
                    setTimeout(()=>{
                        if (!airplaneProcess.active) {
                            triggerAirplaneMode();
                        }
                    }, 3000);
                }
            }
        }, 1000);

        timeout = setTimeout(()=>{
            clearInterval(checkInterval);
            speedTestInProgress = false;
            updateProcessStatus("");

            if (speedTestWindow && !speedTestWindow.closed) {
                speedTestWindow.close();
            }
            speedTestWindow = null;

            GM_setValue('speedTestRequested', false);

            if (!auto) {
                alert("Speed test timeout! Please try again.");
            } else {
                logList.unshift(`<div style="color: #f56c6c;">⚠️ ${t('messages.testTimeout')}</div>`);
                updateLogDisplay();

                if (speedTestCycle && speedTestEnabled) {
                    logList.unshift(`<div style="color: #e6a23c;">⏳ ${t('messages.waitingNext')}</div>`);
                    updateLogDisplay();
                    setTimeout(()=>{
                        if (!airplaneProcess.active) {
                            triggerAirplaneMode();
                        }
                    }, 3000);
                }
            }
        }, 90000);
    }

    // AIRPLANE MODE
    function updateProcessStatus(msg) {
        let el = document.getElementById('fh_process_status');
        if (el) {
            el.style.display = msg ? 'block' : 'none';
            el.textContent = msg;
        }
    }

    function triggerAirplaneMode() {
        if (airplaneProcess.active) {
            alert(t('messages.processRunning'));
            return;
        }

        airplaneProcess.active = true;
        watcherActive = true;
        speedTestCycle = speedTestEnabled;

        let btn = document.getElementById('fh_change_ip');
        if (btn) {
            btn.disabled = true;
            btn.classList.add('is-disabled');
        }

        logList.unshift(`<div style="color: #409eff; font-weight: bold; background: #ecf5ff; padding: 8px; border-radius: 4px; margin-bottom: 8px;">🔄 ${t('messages.startingIPChange')}</div>`);
        updateLogDisplay();
        updateProcessStatus(`📡 ${t('messages.loadingAirplane')}`);

        let ifr = document.getElementById('fh_air_iframe');
        if (ifr) ifr.remove();
        ifr = document.createElement('iframe');
        ifr.id = 'fh_air_iframe';
        ifr.style.display = 'none';
        ifr.src = "/main.html#/mobileNetwork/networkSet";
        document.body.appendChild(ifr);

        executeAirplaneSequence(ifr);

        if(interval) clearInterval(interval);
        interval = setInterval(()=>updateInfo(), 2000);
    }

    async function executeAirplaneSequence(ifr) {
        try {
            await new Promise(r => setTimeout(r, 1500));

            updateProcessStatus(`✈️ ${t('messages.enablingAirplane')}`);
            let doc = ifr.contentDocument || ifr.contentWindow.document;
            let sw = doc.querySelector('input[type=checkbox].el-switch__input');

            if (sw && !sw.checked) {
                sw.click();
                logList.unshift(`<div style="color: #67c23a;">✅ ${t('messages.airplaneEnabled')}</div>`);
                updateLogDisplay();
            }

            for(let i = 8; i > 0; i--) {
                updateProcessStatus(`⏱️ ${t('messages.waiting')} ${i} ${t('messages.seconds')}...`);
                await new Promise(r => setTimeout(r, 1000));
            }

            updateProcessStatus(`📡 ${t('messages.disablingAirplane')}`);
            sw = doc.querySelector('input[type=checkbox].el-switch__input');
            if (sw && sw.checked) {
                sw.click();
                logList.unshift(`<div style="color: #67c23a;">✅ ${t('messages.airplaneDisabled')}</div>`);
                updateLogDisplay();
            }

            updateProcessStatus(`🔄 ${t('messages.waitingReconnect')}`);
            await new Promise(r => setTimeout(r, 3000));

            updateProcessStatus("");
            ifr.remove();
            logList.unshift(`<div style="color: #67c23a; font-weight: bold; background: #f0f9ff; padding: 8px; border-radius: 4px; margin-bottom: 8px;">✅ ${t('messages.ipChangeComplete')}</div>`);
            updateLogDisplay();

            setTimeout(()=>{
                // Speed test or IP pinning will be handled by updateInfo
            }, 1000);

        } catch(e) {
            logList.unshift(`<div style="color: #f56c6c;">❌ ${t('messages.error')}: ${e.message}</div>`);
            updateLogDisplay();
            updateProcessStatus("");
        } finally {
            airplaneProcess.active = false;
            watcherActive = false;
            let btn = document.getElementById('fh_change_ip');
            if (btn) {
                btn.disabled = false;
                btn.classList.remove('is-disabled');
            }
        }
    }

    // SAVE LOG
    function saveLogToFile() {
        let text = `${t('mainTitle')} - ${t('labels.networkLog')}\n`;
        text += "Generated: " + new Date().toLocaleString(currentLang === 'ar' ? 'ar-SA' : 'en-US') + "\n";
        text += "=====================================\n\n";

        let tempDiv = document.createElement('div');
        logList.forEach(log => {
            tempDiv.innerHTML = log;
            text += tempDiv.textContent.trim() + "\n\n";
        });

        let blob = new Blob([text], {type: 'text/plain'});
        let a = document.createElement('a');
        a.href = URL.createObjectURL(blob);
        a.download = `IP_Monitor_Log_${new Date().toISOString().slice(0,10)}.txt`;
        a.click();
    }

    // STATS WINDOW
    function showStatsWindow() {
        if (document.getElementById('fh_stats_window')) return;
        let rows = extractSpeedRows(logList);

        let sortBy = 'speed', sortDir = -1;

        function sortRows() {
            rows.sort((a, b) => {
                let va = a[sortBy], vb = b[sortBy];
                if (sortBy === "ping") return (parseFloat(va) - parseFloat(vb)) * sortDir;
                else return (parseFloat(vb) - parseFloat(va)) * sortDir;
            });
        }
        sortRows();

        let win = document.createElement('div');
        win.id = 'fh_stats_window';
        win.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: #fff;
            padding: 20px;
            border-radius: 8px;
            min-width: 700px;
            max-width: 900px;
            z-index: 2000;
            box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
            border: 1px solid #e4e7ed;
            direction: ${currentLang === 'ar' ? 'rtl' : 'ltr'};
        `;

        win.innerHTML = `
            <div style="margin-bottom: 20px; border-bottom: 1px solid #e4e7ed; padding-bottom: 15px;">
                <h2 style="margin: 0; color: #303133; font-size: 20px; display: inline-block;">
                    ${t('statsTable.title')}
                </h2>
                <button id="fh_stats_close" class="el-button el-button--default el-button--small" style="float: ${currentLang === 'ar' ? 'left' : 'right'};">
                    ${t('buttons.close')}
                </button>
            </div>
            <div style="text-align: center; margin-bottom: 20px;">
                <span style="background: #f4f4f5; color: #606266; padding: 8px 16px; border-radius: 4px; font-size: 14px;">
                    ${t('messages.testCount')}: <strong style="color: #409eff;">${rows.length}</strong>
                </span>
            </div>
            <div id="fh_stats_table_wrap" style="overflow-x: auto;"></div>
        `;
        document.body.appendChild(win);

        document.getElementById('fh_stats_close').onclick = () => { win.remove(); };

        function renderTable() {
            let getArrow = (key) => {
                if (sortBy !== key) return "";
                return sortDir === 1 ? " ▲" : " ▼";
            };
            let table = `
            <table class="el-table" style="width: 100%;">
                <thead>
                    <tr style="background: #f5f7fa;">
                        <th style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5;">${t('statsTable.headers.number')}</th>
                        <th style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5;">${t('statsTable.headers.wanIP')}</th>
                        <th style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5;">${t('statsTable.headers.publicIP')}</th>
                        <th id="sort_speed" style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; cursor: pointer; color: #409eff;">
                            ${t('statsTable.headers.speed')}${getArrow('speed')}
                        </th>
                        <th id="sort_upload" style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; cursor: pointer; color: #409eff;">
                            ${t('statsTable.headers.upload')}${getArrow('upload')}
                        </th>
                        <th id="sort_ping" style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; cursor: pointer; color: #409eff;">
                            ${t('statsTable.headers.ping')}${getArrow('ping')}
                        </th>
                    </tr>
                </thead>
                <tbody>
                    ${rows.map((r, i) => `
                    <tr style="background: ${i%2 ? '#fafafa' : '#fff'};">
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5;">${i+1}</td>
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; color: #409eff; font-family: monospace;">${r.wan}</td>
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; color: #e6a23c; font-family: monospace;">${r.pub}</td>
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; font-weight: bold; color: #67c23a;">${r.speed}</td>
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; color: #909399;">${r.upload}</td>
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; color: #e6a23c;">${r.ping}</td>
                    </tr>
                    `).join('')}
                </tbody>
            </table>
            `;
            document.getElementById('fh_stats_table_wrap').innerHTML = table;

            document.getElementById('sort_speed').onclick = () => {
                if (sortBy == 'speed') sortDir *= -1; else { sortBy = 'speed'; sortDir = -1; }
                sortRows(); renderTable();
            };
            document.getElementById('sort_upload').onclick = () => {
                if (sortBy == 'upload') sortDir *= -1; else { sortBy = 'upload'; sortDir = -1; }
                sortRows(); renderTable();
            };
            document.getElementById('sort_ping').onclick = () => {
                if (sortBy == 'ping') sortDir *= -1; else { sortBy = 'ping'; sortDir = 1; }
                sortRows(); renderTable();
            };
        }
        renderTable();
    }

    function extractSpeedRows(logArr) {
        let out = [];
        let tempDiv = document.createElement('div');
        for (let i = 0; i < logArr.length; i++) {
            tempDiv.innerHTML = logArr[i];
            let plain = tempDiv.textContent || tempDiv.innerText || "";
            let m = plain.match(
                /(?:WAN IP|IP الشبكة):\s*([0-9\.]+).*?(?:Public IP|IP العام):\s*([0-9\.]+).*?(?:Speed|السرعة):\s*(\d+)\s*Mbps\s*\|\s*(?:Ping|البنج):\s*(\d+)\s*ms\s*\|\s*(?:Upload|الرفع):\s*(\d+)\s*Mbps/i
            );
            if (m) {
                out.push({
                    wan: m[1].trim(),
                    pub: m[2].trim(),
                    speed: m[3].trim(),
                    ping: m[4].trim(),
                    upload: m[5].trim()
                });
            }
        }
        return out;
    }

    // FAST.COM HANDLER
    function handleFastCom() {
        if (!GM_getValue('speedTestRequested', false)) return;

        let resultFound = false, detailsLoaded = false, retryCount = 0;
        let startZeroTime = null;

        let checkInterval = setInterval(() => {
            let speedElem   = document.querySelector('#speed-value');
            let uploadElem  = document.querySelector('#upload-value');
            let latencyElem = document.querySelector('#latency-value');

            let download = speedElem   ? speedElem.textContent.trim()   : "0";
            let upload   = uploadElem  ? uploadElem.textContent.trim()  : "0";
            let ping     = latencyElem ? latencyElem.textContent.trim() : "0";

            let noConnElem = document.querySelector('div#error-results-msg[loc-str="no_connection"]');
            let noConnVisible = noConnElem && (noConnElem.offsetParent !== null || noConnElem.style.display === 'block' || noConnElem.style.display === '');

            if (noConnVisible && download === "0" && upload === "0" && ping === "0") {
                if (!startZeroTime) startZeroTime = Date.now();
                if (Date.now() - startZeroTime >= 5000) {
                    retryCount = (retryCount || 0) + 1;
                    console.log(`Fast.com: Real failure (all zero for 5+ sec), will close and retry. (Retry #${retryCount})`);
                    GM_setValue('speedTestRequested', false);
                    GM_setValue('speedTestShouldRetry', true);
                    setTimeout(() => { window.close(); }, 500);
                    clearInterval(checkInterval);
                    return;
                }
            } else {
                startZeroTime = null;
            }

            let progressIndicator = document.querySelector('#speed-progress-indicator');
            if (
                progressIndicator && progressIndicator.classList.contains('succeeded') &&
                !progressIndicator.classList.contains('in-progress') &&
                speedElem && download !== "0" &&
                !detailsLoaded
            ) {
                detailsLoaded = true;
                let moreLink = document.querySelector('#show-more-details-link');
                if (moreLink && moreLink.style.display !== 'none') {
                    moreLink.click();
                }

                let uploadSuccessCheck = setInterval(() => {
                    let uploadElemDone = document.querySelector('#upload-value.extra-measurement-value-container.succeeded');
                    if (uploadElemDone && uploadElemDone.textContent && uploadElemDone.textContent !== '0') {
                        clearInterval(uploadSuccessCheck);

                        setTimeout(() => {
                            let latencyElem = document.querySelector('#latency-value');
                            let speedValue  = document.querySelector('#speed-value');
                            let upload      = uploadElemDone.textContent;
                            let download    = speedValue ? speedValue.textContent : null;
                            let ping        = latencyElem ? latencyElem.textContent : null;

                            if (
                                upload && upload !== '0' &&
                                download && download !== '0' &&
                                ping && ping !== '0'
                            ) {
                                saveFastComResult({download, ping, upload});
                                clearInterval(checkInterval);
                                resultFound = true;
                            }
                        }, 3000);
                    }
                }, 500);
            }
        }, 800);

        setTimeout(() => {
            clearInterval(checkInterval);
            if (!resultFound) {
                console.log('Test timeout, closing window...');
                GM_setValue('speedTestRequested', false);
                window.close();
            }
        }, 90000);

        function saveFastComResult(result) {
            let data = {
                download: result.download,
                ping: result.ping,
                upload: result.upload,
                ip: GM_getValue('currentPublicIP', 'Unknown'),
                timestamp: new Date().toISOString()
            };
            GM_setValue('speedTestResult', data);
            GM_setValue('speedTestRequested', false);

            let body = document.body;
            let msg = document.createElement('div');
            msg.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#67c23a;color:#fff;padding:20px;border-radius:10px;font-size:20px;font-weight:bold;z-index:9999;';
            msg.textContent = `✅ ${t('messages.testCompleted')}`;
            body.appendChild(msg);

            setTimeout(() => { window.close(); }, 2000);
        }
    }

 // دالة تعيد الصفحة لحالتها الأصلية إذا طلع المستخدم
function restoreOriginalPageIfNeeded() {
    // إذا المستخدم خرج من صفحة الأدوات لأي صفحة ثانية
    if (window.location.hash !== "#/iptools") {
        window.location.reload();
    }
}


// راقب أي تغيير في الهاش (أي انتقال بين صفحات)
window.addEventListener('hashchange', restoreOriginalPageIfNeeded);

// أو حتى راقب كل ضغطة في القائمة (احتياط)
document.addEventListener('click', function(e) {
    let el = e.target.closest('.el-menu-item');
    if (el) {
        setTimeout(restoreOriginalPageIfNeeded, 200); // أعطيه مهلة قصيرة حتى تتغير الصفحة
    }
});


})();

++ تفعيل هذي الخيارات من إعدادات متصفح كروم لإضافة Tampermonkey :