حملتtampermonkey و أضفت البرنامج النصي بس ماطلعتلي القائمه
عالبركة و تستاهل كل خير, ياريت تسويلنا شرح بالجوال و بكتب موضوع بإسمك للفائدة.
اللي ما ضبط معه على بريف يجرب يغير خيار تبليك الكوكيز يحطه block all cookies بعدين رجعه على ماهو وحدث الصفحه، كل السكربتين شغاله ![]()
تسلم ماتقصر
فيه اكثر من طريقة لتشغيل السكربت على الجوال
اضافة tampermonkey ( مدفوعة )
Stay for safari بديلة
تحمل التطبيق وتتبع الشرح اللي داخل التطبيق لتشغيل الاضافة على سفاري ، بعدها تنسخ الكود بداخل الاضافة او تحمل الكود بصيغة .js منgithub كلها تنفع
تختار الاضافة اللي تبغى تفعلها ( يفضل الاصدار الثاني لتوافقة بشكل افل مع الجوال )
وهنا يكون تفعل السكربت
اتمنى الشرح سهل وكل التوفيق
انا ماضبط معي بالاول لين دخلت علي السكربت واضافة حرف S بعد http في السطر السابع
وصار
واشتغل معي ان شاء الله يضبط مع الجميع
ياليت يتعدل السكربت ويكتب الكود هذا
شكراً يا @ABO-SULTAN على ردك و شرحك المميز للمشكلة و حلها
نسخة إلى المبدع @0xfff للتعديل على Github و إختبار السكربت بعد التعديل.
يا سلام عليك
بارك الله في جهودكم تذكر فتشكر.
عندي استفسار .
عند البحث و تثبيت ال 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 :









