/* Senobar Studio Analytics */ /* Script: Traffic Recorder */ var currentScript = document.currentScript; var token; if (currentScript && currentScript.src) { var src = currentScript.src; var tokenMatch = src.match(/token=([^&]+)/); token = tokenMatch[1]; } if (token) (async function () { // === CONFIG === const ENDPOINT_URL = "https://analytics-api.senobarstudio.ir/api/Metric/traffic/" + token; // 🔹 replace with your API endpoint const IP_API_URL = "https://ipapi.co/json/"; const SEND_TIMEOUT_MS = 10000; // === HELPERS === const timeout = (ms) => new Promise((_, rej) => setTimeout(() => rej("timeout"), ms)); function getDeviceType(ua = navigator.userAgent) { if (/android/i.test(ua)) return "Android"; if (/iPad|iPhone|iPod/.test(ua)) return "iOS"; if (/Windows/i.test(ua)) return "Windows PC"; if (/Macintosh|Mac OS X/.test(ua)) return "Mac"; if (/Linux/i.test(ua)) return "Linux"; return "Unknown"; } function getBrowserName(ua = navigator.userAgent) { if (ua.includes("Edg/") || ua.includes("EdgA/") || ua.includes("EdgI/")) return "Edge"; if (ua.includes("OPR/") || ua.includes("Opera")) return "Opera"; if (ua.includes("Chrome/") && !ua.includes("Edg/") && !ua.includes("OPR/")) return "Chrome"; if (ua.includes("Safari/") && !ua.includes("Chrome")) return "Safari"; if (ua.includes("Firefox/")) return "Firefox"; return "Unknown"; } function getConnectionInfo() { const c = navigator.connection || navigator.mozConnection || navigator.webkitConnection || {}; return { networkType: c.effectiveType || null, networkDownlinkMbps: typeof c.downlink === "number" ? c.downlink : null, networkRttMs: typeof c.rtt === "number" ? c.rtt : null, networkSaveData: !!c.saveData, }; } async function fetchIpInfo() { try { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 7000); const res = await fetch(IP_API_URL, { signal: controller.signal }); clearTimeout(timer); if (!res.ok) return {}; const json = await res.json(); return { ip: json.ip || null, ipCity: json.city || null, ipRegion: json.region || null, ipCountry: json.country_name || json.country || null, ipPostal: json.postal || null, ipOrg: json.org || json.company || null, ipLatitude: json.latitude || json.lat || null, ipLongitude: json.longitude || json.lon || null, ipTimezone: json.timezone || null, }; } catch { return {}; } } function getGeolocation(timeoutMs = 8000) { return new Promise((resolve) => { if (!("geolocation" in navigator)) return resolve({ geoAllowed: false }); let resolved = false; const onSuccess = (pos) => { if (resolved) return; resolved = true; resolve({ geoAllowed: true, geoLatitude: pos.coords.latitude, geoLongitude: pos.coords.longitude, geoAccuracyMeters: pos.coords.accuracy, geoTimestamp: new Date(pos.timestamp).toISOString(), }); }; const onError = () => { if (resolved) return; resolved = true; resolve({ geoAllowed: false }); }; navigator.geolocation.getCurrentPosition(onSuccess, onError, { enableHighAccuracy: true, timeout: timeoutMs, maximumAge: 0, }); setTimeout(() => { if (!resolved) { resolved = true; resolve({ geoAllowed: false }); } }, timeoutMs + 1000); }); } function getPath() { return document.location.pathname + document.location.search + document.location.hash; } // === COLLECT DATA === async function collectData() { const ua = navigator.userAgent || ""; const base = { collectedAt: new Date().toISOString(), browserName: getBrowserName(ua), browserUserAgent: ua, browserAppName: navigator.appName || null, browserAppVersion: navigator.appVersion || null, browserProduct: navigator.product || null, systemPlatform: navigator.platform || null, systemDeviceType: getDeviceType(ua), systemDeviceMemoryGB: navigator.deviceMemory || null, systemHardwareConcurrency: navigator.hardwareConcurrency || null, systemTouchPoints: navigator.maxTouchPoints || 0, systemCookieEnabled: navigator.cookieEnabled, systemOnline: navigator.onLine, screenWidth: screen.width, screenHeight: screen.height, screenAvailWidth: screen.availWidth, screenAvailHeight: screen.availHeight, screenColorDepth: screen.colorDepth, screenPixelDepth: screen.pixelDepth, screenOrientation: screen.orientation?.type || null, screenDevicePixelRatio: window.devicePixelRatio || 1, ...getConnectionInfo(), featureServiceWorker: 'serviceWorker' in navigator, featureLocalStorage: typeof localStorage !== "undefined", featureSessionStorage: typeof sessionStorage !== "undefined", featureIndexedDB: typeof indexedDB !== "undefined", featureWebRTC: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia), featureClipboard: !!navigator.clipboard, featureBluetooth: !!navigator.bluetooth, language: navigator.language || null, languages: navigator.languages || null, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || null, timezoneOffsetMinutes: new Date().getTimezoneOffset(), }; const [ipData, geoData] = await Promise.all([fetchIpInfo()]); const clientHints = navigator.userAgentData ? { clientHintMobile: navigator.userAgentData.mobile, clientHintPlatform: navigator.userAgentData.platform, clientHintBrands: (navigator.userAgentData.brands || []) .map((b) => `${b.brand};v=${b.version}`) .join(", "), } : {}; const path = getPath(); return { ...base, ...ipData, ...geoData, ...clientHints, path }; } // === SEND DATA === async function sendData(payload) { try { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), SEND_TIMEOUT_MS); const res = await fetch(ENDPOINT_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), signal: controller.signal, }); clearTimeout(timer); console.log("✅ Data sent:", res.status); } catch (err) { console.warn("⚠️ Failed to send data:", err); } } // === RUN === try { const data = await collectData(); console.log("Collected data:", data); await sendData(data); } catch (err) { console.error("Unexpected error:", err); } })(); /* Script: Time Spend */ (function () { const API_URL = "https://your-api.com/api/page-activity"; // Helper to generate UUID function uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } // Cookie utilities function setCookie(name, value, days) { const expires = new Date(Date.now() + days * 864e5).toUTCString(); document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Lax`; } function getCookie(name) { return document.cookie.split('; ').find(row => row.startsWith(name + '='))?.split('=')[1]; } // Ensure unique user ID let userId = getCookie('user_id'); if (!userId) { userId = uuidv4(); setCookie('user_id', userId, 365); // 1 year } // Track entry time const entryTime = new Date(); function sendData() { const exitTime = new Date(); const timeSpentSeconds = Math.round((exitTime - entryTime) / 1000); const data = { collectedAt: new Date().toISOString(), sessionId: uuidv4(), userId, pageUrl: window.location.href, pageTitle: document.title, referrerUrl: document.referrer || null, entryTimestamp: entryTime.toISOString(), exitTimestamp: exitTime.toISOString(), timeSpentSeconds, userAgent: navigator.userAgent, screenWidth: window.screen.width, screenHeight: window.screen.height, language: navigator.language || null }; navigator.sendBeacon(API_URL, JSON.stringify(data)); } window.addEventListener('beforeunload', sendData); })(); /* Script: Errors Log */ (function () { const API_URL = "https://your-api.com/api/error-log"; // Helper: UUID generator function uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } // Cookie utilities function setCookie(name, value, days) { const expires = new Date(Date.now() + days * 864e5).toUTCString(); document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Lax`; } function getCookie(name) { return document.cookie.split('; ').find(row => row.startsWith(name + '='))?.split('=')[1]; } // Ensure unique user ID cookie let userId = getCookie('user_id'); if (!userId) { userId = uuidv4(); setCookie('user_id', userId, 365); } function sendError(errorData) { const payload = { collectedAt: new Date().toISOString(), userId, sessionId: uuidv4(), pageUrl: window.location.href, pageTitle: document.title, referrerUrl: document.referrer || null, userAgent: navigator.userAgent, screenWidth: window.screen.width, screenHeight: window.screen.height, language: navigator.language || null, ...errorData }; navigator.sendBeacon(API_URL, JSON.stringify(payload)); } // Catch runtime JS errors window.addEventListener("error", (event) => { sendError({ errorMessage: event.message, errorFile: event.filename, errorLine: event.lineno, errorColumn: event.colno, errorStack: event.error?.stack || null, errorType: event.error?.name || "Error" }); }); // Catch unhandled promise rejections window.addEventListener("unhandledrejection", (event) => { sendError({ errorMessage: event.reason?.message || String(event.reason), errorStack: event.reason?.stack || null, errorType: "UnhandledPromiseRejection" }); }); // Optional: catch fetch/XHR errors const originalFetch = window.fetch; window.fetch = async function (...args) { try { const res = await originalFetch(...args); if (!res.ok) { sendError({ errorMessage: `Fetch failed with status ${res.status}`, errorFile: args[0], errorType: "FetchError" }); } return res; } catch (err) { sendError({ errorMessage: err.message, errorStack: err.stack, errorType: "NetworkError" }); throw err; } }; })();