diff --git a/src/background/background.js b/src/background/background.js index dc02c51..d124292 100644 --- a/src/background/background.js +++ b/src/background/background.js @@ -1,4 +1,5 @@ import { SETTINGS_CONFIG } from '../content/core/settings/settingConfig.js'; +import { log, logLevel} from '../content/core/logging.js'; // --- Constants & State --- @@ -66,9 +67,9 @@ function initializeSettings(reason) { if (needsUpdate) { chrome.storage.local.set(settingsToUpdate, () => { if (chrome.runtime.lastError) { - console.error('RoValra: Failed to sync settings.', chrome.runtime.lastError); + log(logLevel.ERROR, 'RoValra: Failed to sync settings.', chrome.runtime.lastError); } else { - console.log(`RoValra: Synced/Fixed ${Object.keys(settingsToUpdate).length} settings (Trigger: ${reason}).`); + log(logLevel.DEBUG, `RoValra: Synced/Fixed ${Object.keys(settingsToUpdate).length} settings (Trigger: ${reason}).`); } }); } @@ -205,7 +206,7 @@ async function getUniverseIdFromPlaceId(placeId) { } return null; } catch (e) { - console.error('RoValra: Error fetching universe ID from place ID', e); + log(logLevel.ERROR, 'RoValra: Error fetching universe ID from place ID', e); return null; } } @@ -265,7 +266,7 @@ async function wearOutfit(outfitData) { try { const outfitId = (typeof outfitData === 'object' && outfitData !== null) ? outfitData.itemId : outfitData; if (!outfitId) { - console.error('RoValra: wearOutfit called with invalid outfitData', outfitData); + log(logLevel.ERROR, 'RoValra: wearOutfit called with invalid outfitData', outfitData); return { ok: false }; } @@ -288,7 +289,7 @@ async function wearOutfit(outfitData) { const results = await Promise.all(promises); return { ok: results.every(r => r && r.ok) }; } catch (e) { - console.error('RoValra: Error wearing outfit', e); + log(logLevel.ERROR, 'RoValra: Error wearing outfit', e); return { ok: false }; } } diff --git a/src/content/core/api.js b/src/content/core/api.js index 7cd0dd5..8a6e430 100644 --- a/src/content/core/api.js +++ b/src/content/core/api.js @@ -3,6 +3,7 @@ import { getCsrfToken } from './utils.js'; import { getAuthenticatedUserId } from './user.js'; import { getValidAccessToken } from './oauth/oauth.js'; +import { log, logLevel } from './logging.js'; import { updateUserLocationIfChanged } from './utils/location.js'; const activeRequests = new Map(); @@ -244,7 +245,7 @@ export async function callRobloxApi(options) { let storedVerification = allVerifications[authedUserId]; if (storedVerification) { - console.log("RoValra API: New token received from header. Updating storage."); + log(logLevel.DEBUG, "RoValra API: New token received from header. Updating storage."); storedVerification.accessToken = newAccessToken; storedVerification.timestamp = Date.now(); @@ -260,13 +261,13 @@ export async function callRobloxApi(options) { } } } catch (e) { - console.error("RoValra API: Failed to update new access token.", e); + log(logLevel.ERROR, "RoValra API: Failed to update new access token.", e); } } if (lastResponse.status === 401 && endpoint && endpoint.includes('/v1/auth') && !skipAutoAuth && !authRetried) { - console.log("RoValra API: 401 Unauthorized, attempting token refresh..."); + log(logLevel.ERROR, "RoValra API: 401 Unauthorized, attempting token refresh..."); authRetried = true; const newToken = await getValidAccessToken(true); if (newToken) { @@ -282,7 +283,7 @@ export async function callRobloxApi(options) { if (endpoint && endpoint.includes('/v1/auth')) break; } catch (error) { if (attempt === 3 || (endpoint && endpoint.includes('/v1/auth'))) { - console.error(`RoValra API: Request to ${fullUrl} failed${attempt === 3 ? ' after multiple retries' : ''}.`, error); + log(logLevel.ERROR, `RoValra API: Request to ${fullUrl} failed${attempt === 3 ? ' after multiple retries' : ''}.`, error); throw error; } } @@ -291,7 +292,7 @@ export async function callRobloxApi(options) { } } if (!lastResponse.ok) { - console.error(`RoValra API: Request to ${fullUrl} failed with status ${lastResponse.status} after multiple retries.`); + log(logLevel.ERROR, `RoValra API: Request to ${fullUrl} failed with status ${lastResponse.status} after multiple retries.`); } return lastResponse; } @@ -330,7 +331,7 @@ export async function callRobloxApi(options) { } if (!response.ok) { - console.error(`RoValra API: Request to ${fullUrl} failed with status ${response.status}.`); + log(logLevel.ERROR, `RoValra API: Request to ${fullUrl} failed with status ${response.status}.`); } return response; diff --git a/src/content/core/configs/userIds.js b/src/content/core/configs/userIds.js index ce2234b..033b0d8 100644 --- a/src/content/core/configs/userIds.js +++ b/src/content/core/configs/userIds.js @@ -10,7 +10,8 @@ export const CONTRIBUTOR_USER_IDS = [ '546872490', //Kanibal02 '48255812', //aliceenight '7982684834', //qborder - '126448532' //steinann + '126448532', //steinann + '1564574922', // cornusandu ]; export const RAT_BADGE_USER_ID = '477516666'; // rat diff --git a/src/content/core/games/servers/serverstats.js b/src/content/core/games/servers/serverstats.js index 0736772..63d6645 100644 --- a/src/content/core/games/servers/serverstats.js +++ b/src/content/core/games/servers/serverstats.js @@ -4,6 +4,7 @@ import { callRobloxApi } from '../../api.js'; import { addTooltip } from '../../ui/tooltip.js'; import DOMPurify from 'dompurify'; import { observeElement, startObserving } from '../../observer.js'; +import { log, logLevel } from '../../logging.js'; let versionDataCache = null; @@ -40,7 +41,7 @@ async function fetchLatestPlaceVersion(placeId) { return null; } catch (error) { - console.error('RoValra Server Stats: Failed to fetch latest place version from Roblox.', error); + log(logLevel.ERROR, 'RoValra Server Stats: Failed to fetch latest place version from Roblox.', error); return null; } } @@ -78,7 +79,7 @@ export async function fetchServerStats(placeId) { return data.counts; } catch (error) { - console.error('RoValra Server Stats: Failed to fetch server statistics.', error); + log(logLevel.ERROR, 'RoValra Server Stats: Failed to fetch server statistics.', error); return null; } } @@ -129,7 +130,7 @@ async function createStatsBarUI(serverListContainer) { }); }); } catch (error) { - console.error('RoValra Server Stats: Failed to fetch settings.', error); + log(logLevel.ERROR, 'RoValra Server Stats: Failed to fetch settings.', error); return; } @@ -238,7 +239,7 @@ export async function initGlobalStatsBar() { } } } catch (error) { - console.error('RoValra Server Stats: Failed to initialize stats bar.', error); + log(logLevel.ERROR, 'RoValra Server Stats: Failed to initialize stats bar.', error); } } diff --git a/src/content/core/logging.js b/src/content/core/logging.js new file mode 100644 index 0000000..bac49d3 --- /dev/null +++ b/src/content/core/logging.js @@ -0,0 +1,85 @@ +export const logLevel = Object.freeze({ + DEBUG: 0, + INFO: 1, + WARNING: 2, + ERROR: 3, + CRITICAL: 4 +}); + +const logLevelStr = { + [logLevel.DEBUG]: 'DEBUG', + [logLevel.INFO]: 'INFO', + [logLevel.WARNING]: 'WARNING', + [logLevel.ERROR]: 'ERROR', + [logLevel.CRITICAL]: 'CRITICAL' +}; + +const maxLogLevelLength = 8; + +// Minimum Log Level +const minLogLevel = logLevel.DEBUG; + +// Whether to call alert() on logLevel.CRITICAL +const criticalAsAlert = true; + +// Logging functions +const logfn = { + [logLevel.DEBUG]: () => console.debug, + [logLevel.INFO]: () => console.info, + [logLevel.WARNING]: () => console.warn, + [logLevel.ERROR]: () => console.error, + [logLevel.CRITICAL]:() => (criticalAsAlert ? formatAndAlert : console.error) +}; + + +function format(msg, ...args) { + let i = 0; + + const str = String(msg).replace(/%[sdihf%]/g, token => { + if (token === "%%") return "%"; + const arg = args[i++]; + + switch (token) { + case "%s": return String(arg); + case "%d": + case "%i": return Number.parseInt(arg, 10); + case "%h": return "0x" + Number.parseInt(arg, 10).toString(16); + case "%f": return Number.parseFloat(arg); + default: + return token; + } + }); + + if (i < args.length) { + return str + " " + args.slice(i).join(" "); // concatenate remaining arguments to the formatted string (if any; seperated by spaces) + } + + return str; +} + +function formatAndAlert(msg, ...args) { + const formatted = format(msg, ...args); + log(logLevel.ERROR, msg, ...args); // for debugging purposes + alert(formatted); +} + +function timestamp() { + const now = new Date(); + return `${String(now.getHours()).padStart(2,'0')}:` + + `${String(now.getMinutes()).padStart(2,'0')}:` + + `${String(now.getSeconds()).padStart(2,'0')}.` + + `${String(now.getMilliseconds()).padStart(3,'0')}`; +} + +function padLevel(msg) { + return msg.padEnd(maxLogLevelLength); +} + +export function log(level, message, ...args) { + if (level < minLogLevel) { + return; + } + + const msg = `${timestamp()} [${padLevel(logLevelStr[level])}] ${message}`; + logfn[level]()(msg, ...args); +} diff --git a/src/content/core/oauth/oauth.js b/src/content/core/oauth/oauth.js index 812fefb..ca9d48c 100644 --- a/src/content/core/oauth/oauth.js +++ b/src/content/core/oauth/oauth.js @@ -1,9 +1,8 @@ -// TODO Remove console logs - const STORAGE_KEY = "rovalra_oauth_verification"; const TOKEN_EXPIRATION_BUFFER_MS = 5 * 60 * 1000; import { callRobloxApi } from '../api.js'; import { getAuthenticatedUserId } from '../user.js'; +import { log, logLevel } from '../logging.js'; const existenceCache = new Map(); const prefixCache = new Map(); @@ -11,18 +10,18 @@ let activeOAuthPromise = null; export async function init() { try { - console.log("RoValra: Script loaded. Syncing session..."); + log(logLevel.DEBUG, "RoValra: Script loaded. Syncing session..."); const token = await getValidAccessToken(true); if (token) { - console.log("RoValra: Session synchronized successfully."); + log(logLevel.INFO, "RoValra: Session synchronized successfully."); } else { - console.log("RoValra: No active session or re-auth required."); + log(logLevel.INFO, "RoValra: No active session or re-auth required."); } } catch (error) { - console.error("RoValra: Error during script initialization", error); + log(logLevel.ERROR, "RoValra: Error during script initialization", error); } } export async function getValidAccessToken(forceRefresh = false) { @@ -82,7 +81,7 @@ export async function getValidAccessToken(forceRefresh = false) { return updatedStorage[STORAGE_KEY]?.[userId]?.accessToken || storedVerification.accessToken; } catch (error) { - console.error("RoValra: Network error during token sync:", error); + log(logLevel.ERROR, "RoValra: Network error during token sync:", error); return storedVerification.accessToken; } } @@ -130,7 +129,7 @@ async function checkUserExistence(userId) { return exists; } } catch (error) { - console.error("RoValra: Error checking user existence", error); + log(logLevel.ERROR, "RoValra: Error checking user existence", error); } return false; } @@ -167,7 +166,7 @@ async function startOAuthFlow(silent = false) { } if (age < 13) { - console.log("RoValra: User is under 13. Skipping OAuth."); + log(logLevel.WARNING, "RoValra: User is under 13. Skipping OAuth."); return false; } } @@ -181,7 +180,7 @@ async function startOAuthFlow(silent = false) { } try { - console.log("RoValra: Attempting direct OAuth authorization POST request..."); + log(logLevel.DEBUG, "RoValra: Attempting direct OAuth authorization POST request..."); const response = await callRobloxApi({ subdomain: 'apis', @@ -207,11 +206,11 @@ async function startOAuthFlow(silent = false) { const locationUrl = authResponse.location; if (!locationUrl) { - console.error("RoValra: OAuth authorization response did not contain a location URL.", authResponse); + log(logLevel.ERROR, "RoValra: OAuth authorization response did not contain a location URL.", authResponse); return false; } - console.log("RoValra: Got authorization code. Fetching token from callback URL..."); + log(logLevel.INFO, "RoValra: Got authorization code. Fetching token from callback URL..."); const tokenResponse = await callRobloxApi({ fullUrl: locationUrl, @@ -220,14 +219,14 @@ async function startOAuthFlow(silent = false) { }); if (!tokenResponse.ok) { - console.error("RoValra: Failed to get token from callback URL.", await tokenResponse.text()); + log(logLevel.ERROR, "RoValra: Failed to get token from callback URL.", await tokenResponse.text()); return false; } const tokenData = await tokenResponse.json(); if (tokenData.status === 'success' && tokenData.access_token && tokenData.user_id && tokenData.username) { - console.log("RoValra: OAuth Successful!", tokenData); + log(logLevel.INFO, "RoValra: OAuth Successful!", tokenData); const expiresAt = tokenData.expires_at ? tokenData.expires_at * 1000 : null; @@ -246,16 +245,16 @@ async function startOAuthFlow(silent = false) { await chrome.storage.local.set({ [STORAGE_KEY]: allVerifications }); return true; } else { - console.error("RoValra: Invalid token data received from backend.", tokenData); + log(logLevel.ERROR, "RoValra: Invalid token data received from backend.", tokenData); return false; } } else { - console.error("RoValra: OAuth authorization POST request failed with status " + response.status, await response.text()); + log(logLevel.ERROR, "RoValra: OAuth authorization POST request failed with status " + response.status, await response.text()); return false; } } catch (error) { - console.error("RoValra: Error during direct OAuth authorization request.", error); + log(logLevel.ERROR, "RoValra: Error during direct OAuth authorization request.", error); return false; } })(); diff --git a/src/content/core/regionFinder/ClosestServer.js b/src/content/core/regionFinder/ClosestServer.js index 102faa2..ad8bf7f 100644 --- a/src/content/core/regionFinder/ClosestServer.js +++ b/src/content/core/regionFinder/ClosestServer.js @@ -4,6 +4,7 @@ import { launchGame } from '../utils/launcher.js'; import { showReviewPopup } from '../review/review.js'; import { hideLoadingOverlay } from '../ui/startModal/gamelaunchmodal.js'; import { getStateCodeFromRegion } from '../regions.js'; +import { log, logLevel } from '../logging.js'; export let REGIONS = {}; export let serverIpMap = {}; @@ -293,9 +294,9 @@ async function getRankedRegions(placeId, preferredRegionId) { } if (FINDER_CONFIG.logScores) { - console.log(`[RoValra] Region Scores for Place ${placeId}:`); + log(logLevel.DEBUG, `[RoValra] Region Scores for Place ${placeId}:`); ranked.forEach(r => { - console.log(` - ${getRegionName(r.region.id)}: Score ${r.score} (${Math.round(r.distance)} km)`); + log(logLevel.DEBUG, ` - ${getRegionName(r.region.id)}: Score ${r.score} (${Math.round(r.distance)} km)`); }); } diff --git a/src/content/core/regions.js b/src/content/core/regions.js index 8043bb7..1e938ad 100644 --- a/src/content/core/regions.js +++ b/src/content/core/regions.js @@ -2,6 +2,7 @@ import { callRobloxApi } from './api.js'; import { getAssets } from './assets.js'; +import { log, logLevel } from './logging.js'; const API_ENDPOINT_DATACENTERS_LIST = '/v1/datacenters/list'; const STORAGE_KEY_DATACENTERS = 'rovalraDatacenters'; @@ -64,7 +65,7 @@ export async function loadDatacenterMap() { processDataIntoMap(currentData); } } catch (e) { - console.error("RoValra: Error reading datacenter map from storage.", e); + log(logLevel.ERROR, "RoValra: Error reading datacenter map from storage.", e); } if (!currentData) { @@ -78,7 +79,7 @@ export async function loadDatacenterMap() { await chrome.storage.local.set({ [STORAGE_KEY_DATACENTERS]: localData }); processDataIntoMap(localData); } catch (e) { - console.error("RoValra: Could not load local fallback JSON.", e); + log(logLevel.ERROR, "RoValra: Could not load local fallback JSON.", e); serverIpMap = {}; } } @@ -131,7 +132,7 @@ async function fetchAndProcessRegions() { if (!response.ok) throw new Error(`Status ${response.status}`); data = await response.json(); } catch (fallbackError) { - console.error("RoValra Critical: Could not load region data.", fallbackError); + log(logLevel.ERROR, "RoValra Critical: Could not load region data.", fallbackError); return { regions: newRegions, continents: newContinents }; } } diff --git a/src/content/core/settings/handlesettings.js b/src/content/core/settings/handlesettings.js index eb887f0..baa6106 100644 --- a/src/content/core/settings/handlesettings.js +++ b/src/content/core/settings/handlesettings.js @@ -8,6 +8,7 @@ import { updateUserDescription, } from '../profile/descriptionhandler.js'; import { createAndShowPopup } from '../../features/catalog/40method.js'; +import { log, logLevel } from '../../core/logging.js'; export const loadSettings = async () => { return new Promise((resolve, reject) => { @@ -34,10 +35,9 @@ export const loadSettings = async () => { chrome.storage.local.get(defaultSettings, (settings) => { if (chrome.runtime.lastError) { - console.error( + log(logLevel.ERROR, 'Failed to load settings:', - chrome.runtime.lastError, - ); + chrome.runtime.lastError); reject(chrome.runtime.lastError); } else { resolve(settings); @@ -48,7 +48,7 @@ export const loadSettings = async () => { export const handleSaveSettings = async (settingName, value) => { if (!settingName) { - console.error('No setting name provided'); + log(logLevel.ERROR, 'No setting name provided'); return Promise.reject(new Error('No setting name provided')); } @@ -164,11 +164,10 @@ export const handleSaveSettings = async (settingName, value) => { return new Promise((resolve, reject) => { chrome.storage.local.set(settings, () => { if (chrome.runtime.lastError) { - console.error( + log(logLevel.ERROR, 'Failed to save setting:', settingName, - chrome.runtime.lastError, - ); + chrome.runtime.lastError); reject(chrome.runtime.lastError); } else { syncToSettingsKey(settingName, sanitizedValue); @@ -177,7 +176,7 @@ export const handleSaveSettings = async (settingName, value) => { }); }); } catch (error) { - console.error(`Error saving setting ${settingName}:`, error); + log(logLevel.ERROR, `Error saving setting ${settingName}:`, error); return Promise.reject(error); } }; @@ -210,28 +209,24 @@ export const buildSettingsKey = async () => { chrome.storage.local.get(allSettingKeys, (currentSettings) => { chrome.storage.local.set( - { rovalra_settings: currentSettings }, - () => { - if (chrome.runtime.lastError) { - console.error( - 'Failed to build settings key:', - chrome.runtime.lastError, - ); - } else { - console.log('RoValra: Settings key initialized'); - } - resolve(); - }, - ); + { rovalra_settings: currentSettings }, + () => { + if (chrome.runtime.lastError) { + log(logLevel.ERROR, + 'Failed to build settings key:', + chrome.runtime.lastError); + } else { + log(logLevel.DEBUG, 'RoValra: Settings key initialized'); + } + resolve(); + }); }); }); }; export const initSettings = async (settingsContent) => { if (!settingsContent) { - console.error( - 'settingsContent is null in initSettings! Check HTML structure.', - ); + log(logLevel.ERROR, "settingsContent is null in initSettings! Check HTML structure."); return; } const settings = await loadSettings(); @@ -257,8 +252,8 @@ export const initSettings = async (settingsContent) => { } if (missingPerms) { - console.log( - `RoValra: Disabling '${settingName}' because required permissions are missing.`, + log(logLevel.WARNING, + `RoValra: Disabling '${settingName}' because required permissions are missing.` ); await handleSaveSettings(settingName, false); settings[settingName] = false; @@ -547,20 +542,16 @@ export function updateConditionalSettingsVisibility( } async function hasPermission(permission) { - return new Promise((resolve) => { + return new Promise(resolve => { chrome.runtime.sendMessage( - { action: 'checkPermission', permission: permission }, - (response) => { - if (chrome.runtime.lastError) { - console.error( - 'RoValra: Error checking permission:', - chrome.runtime.lastError.message, - ); - resolve(false); - } - resolve(response?.granted || false); - }, - ); + { action: 'checkPermission', permission: permission }, + (response) => { + if (chrome.runtime.lastError) { + log(logLevel.ERROR, "RoValra: Error checking permission:", chrome.runtime.lastError.message); + resolve(false); + } + resolve(response?.granted || false); + }); }); } @@ -583,20 +574,18 @@ async function requestPermission(permission) { } async function revokePermission(permission) { - return new Promise((resolve) => { + return new Promise(resolve => { chrome.runtime.sendMessage( - { action: 'revokePermission', permission: permission }, - (response) => { - if (chrome.runtime.lastError) { - console.error( - `RoValra: Failed to revoke '${permission}' permission:`, - chrome.runtime.lastError.message, - ); - resolve(false); - } - resolve(response?.revoked || false); - }, - ); + { action: 'revokePermission', permission: permission }, + (response) => { + if (chrome.runtime.lastError) { + log(logLevel.ERROR, + `RoValra: Failed to revoke '${permission}' permission:`, + chrome.runtime.lastError.message); + resolve(false); + } + resolve(response?.revoked || false); + }); }); } @@ -628,9 +617,7 @@ export async function updateAllPermissionToggles() { } if (missingPerms) { - console.log( - `RoValra: Disabling '${settingName}' because required permissions are missing.`, - ); + log(logLevel.WARNING, `RoValra: Disabling '${settingName}' because required permissions are missing.`); await handleSaveSettings(settingName, false); settings[settingName] = false; @@ -769,11 +756,8 @@ export function initializeSettingsEventListeners() { ]; } - console.log( - 'Generated Environment JSON:\n' + - JSON.stringify(envConfig, null, 2), - ); - alert('Environment JSON has been printed to the console (F12).'); + log(logLevel.INFO, "Generated Environment JSON:\n" + JSON.stringify(envConfig, null, 2)); + log(logLevel.CRITICAL, "Environment JSON has been printed to the console (F12)."); }); chrome.runtime.onMessage.addListener((request) => { @@ -840,10 +824,8 @@ export function initializeSettingsEventListeners() { if (!granted) { target.checked = false; - console.log( - `RoValra: Permission denied for ${settingName}`, - ); - return; + log(logLevel.WARNING, `RoValra: Permission denied for ${settingName}`); + return; } } } @@ -946,10 +928,10 @@ export function initializeSettingsEventListeners() { }); } } - }) - .catch((error) => { - console.error('Error saving one or more settings:', error); - }); + } + }).catch(error => { + log(logLevel.ERROR, "Error saving one or more settings:", error); + }); }); document.addEventListener('click', (event) => { diff --git a/src/content/core/settings/portSettings.js b/src/content/core/settings/portSettings.js index a3fe91b..b09a464 100644 --- a/src/content/core/settings/portSettings.js +++ b/src/content/core/settings/portSettings.js @@ -1,6 +1,7 @@ import { createButton } from '../../core/ui/buttons.js'; import { sanitizeSettings } from '../utils/sanitize.js'; import { SETTINGS_CONFIG } from './settingConfig.js'; +import { log, logLevel } from '../../core/logging.js'; const ROVALRA_SETTINGS_UUID = 'a1b2c3d4-e5f6-7890-1234-567890abcdef'; @@ -9,8 +10,8 @@ export async function exportSettings() { try { chrome.storage.local.get('rovalra_settings', (result) => { if (chrome.runtime.lastError) { - console.error('Failed to export settings:', chrome.runtime.lastError); - alert('Error exporting settings. Check the console for details.'); + log(logLevel.ERROR, 'Failed to export settings:', chrome.runtime.lastError); + log(logLevel.CRITICAL, 'Error exporting settings. Check the console for details.'); return; } @@ -20,8 +21,8 @@ export async function exportSettings() { try { sanitizedSettings = sanitizeSettings(allSettings, SETTINGS_CONFIG); } catch (error) { - console.error('Failed to sanitize settings for export:', error); - alert('Error sanitizing settings for export. Check the console for details.'); + log(logLevel.ERROR, 'Failed to sanitize settings for export:', error); + log(logLevel.CRITICAL, 'Error sanitizing settings for export. Check the console for details.'); return; } @@ -41,8 +42,8 @@ export async function exportSettings() { URL.revokeObjectURL(url); }); } catch (error) { - console.error('Error in exportSettings:', error); - alert('An unexpected error occurred during export.'); + log(logLevel.ERROR, 'Error in exportSettings:', error); + log(logLevel.CRITICAL, 'An unexpected error occurred during export.'); } } @@ -66,7 +67,7 @@ export async function importSettings() { const importedData = JSON.parse(content); if (importedData.rovalra_uuid !== ROVALRA_SETTINGS_UUID) { - alert('This does not appear to be a valid RoValra settings file.'); + log(logLevel.CRITICAL, 'This does not appear to be a valid RoValra settings file.'); return; } @@ -75,21 +76,21 @@ export async function importSettings() { try { sanitizedSettings = sanitizeSettings(importedData.settings, SETTINGS_CONFIG); } catch (error) { - console.error('Failed to sanitize imported settings:', error); - alert('Error: The imported settings file contains invalid or potentially dangerous data.'); + log(logLevel.ERROR, 'Failed to sanitize imported settings:', error); + log(logLevel.CRITICAL, 'Error: The imported settings file contains invalid or potentially dangerous data.'); return; } const settingsSize = JSON.stringify(sanitizedSettings).length; if (settingsSize > 1024 * 1024) { - alert('Error: Settings file is too large. Maximum size is 1MB.'); + log(logLevel.CRITICAL, 'Error: Settings file is too large. Maximum size is 1MB.'); return; } chrome.storage.local.set(sanitizedSettings, () => { if (chrome.runtime.lastError) { - console.error('Failed to import settings:', chrome.runtime.lastError); - alert('Error importing settings. Check the console for details.'); + log(logLevel.ERROR, 'Failed to import settings:', chrome.runtime.lastError); + log(logLevel.CRITICAL, 'Error importing settings. Check the console for details.'); } else { chrome.storage.local.set({ rovalra_settings: sanitizedSettings }, () => { location.reload(); @@ -97,11 +98,11 @@ export async function importSettings() { } }); } else { - alert('The settings file is malformed.'); + log(logLevel.CRITICAL, 'The settings file is malformed.'); } } catch (error) { - console.error('Error parsing or processing settings file:', error); - alert('Could not read the settings file. It might be corrupted or in the wrong format.'); + log(logLevel.ERROR, 'Error parsing or processing settings file:', error); + log(logLevel.CRITICAL, 'Could not read the settings file. It might be corrupted or in the wrong format.'); } }; reader.readAsText(file); @@ -109,8 +110,8 @@ export async function importSettings() { input.click(); } catch (error) { - console.error('Error in importSettings:', error); - alert('An unexpected error occurred during import.'); + log(logLevel.ERROR, 'Error in importSettings:', error); + log(logLevel.CRITICAL, 'An unexpected error occurred during import.'); } } diff --git a/src/content/core/utils/assetStreamer.js b/src/content/core/utils/assetStreamer.js index 7e26ed4..f27b1ad 100644 --- a/src/content/core/utils/assetStreamer.js +++ b/src/content/core/utils/assetStreamer.js @@ -1,6 +1,7 @@ import { parseRbxm } from './rbxm.js'; import { callRobloxApi } from '../api.js'; +import { log, logLevel } from '../logging.js'; const RBXM_SIGNATURE_BYTES = [60, 114, 111, 98, 108, 111, 120, 33]; @@ -85,7 +86,7 @@ export async function checkAssetsInBatch(assetIds) { }); if (!batchApiResponse.ok) { - console.error(`[Rovalra Asset Parser] AssetDelivery batch API failed: ${batchApiResponse.status}`); + log(logLevel.ERROR, `[Rovalra Asset Parser] AssetDelivery batch API failed: ${batchApiResponse.status}`); return assetIds.map(id => createDefaultResult(id)); } @@ -133,7 +134,7 @@ export async function checkAssetsInBatch(assetIds) { }; } catch (error) { - console.error(`[Rovalra Asset Parser] Error parsing asset ${id}:`, error); + log(logLevel.ERROR, `[Rovalra Asset Parser] Error parsing asset ${id}:`, error); return createDefaultResult(id); } }); @@ -141,7 +142,7 @@ export async function checkAssetsInBatch(assetIds) { return Promise.all(processingPromises); } catch (error) { - console.error('[Rovalra Asset Parser] Critical error:', error); + log(logLevel.ERROR, '[Rovalra Asset Parser] Critical error:', error); return assetIds.map(id => createDefaultResult(id)); } } \ No newline at end of file diff --git a/src/content/core/utils/launcher.js b/src/content/core/utils/launcher.js index 733c1c4..e7282d1 100644 --- a/src/content/core/utils/launcher.js +++ b/src/content/core/utils/launcher.js @@ -1,11 +1,12 @@ // This script should always be used to start up the Roblox client import { callRobloxApiJson } from '../api.js'; +import { log, logLevel } from '../logging.js'; function executeLaunchScript(codeToInject) { if (typeof chrome !== 'undefined' && chrome.runtime) { chrome.runtime.sendMessage({ action: "injectScript", codeToInject }); } else { - console.error("RoValra Launcher: Chrome runtime is not available to inject the script."); + log(logLevel.ERROR, "RoValra Launcher: Chrome runtime is not available to inject the script."); } } @@ -65,7 +66,7 @@ export async function launchStudioForGame(placeId) { throw new Error(`Could not retrieve universeId for placeId ${placeId}`); } } catch (error) { - console.error('RoValra Launcher: Failed to launch studio with universeId, falling back.', error); + log(logLevel.ERROR, 'RoValra Launcher: Failed to launch studio with universeId, falling back.', error); const uri = `roblox-studio:launchmode:edit+task:EditPlace+placeId:${placeId}`; const codeToInject = `window.location.href = '${uri}';`; executeLaunchScript(codeToInject); diff --git a/src/content/core/utils/location.js b/src/content/core/utils/location.js index d1b8676..71fa1c3 100644 --- a/src/content/core/utils/location.js +++ b/src/content/core/utils/location.js @@ -1,4 +1,5 @@ import { callRobloxApi } from '../api.js'; +import { log, logLevel} from '../logging.js'; const LOCATION_STORAGE_KEY = 'robloxUserLocationCache'; const CACHE_DURATION_MS = 1000 * 60 * 60 * 24; @@ -27,7 +28,7 @@ async function resolveGeoNames(lat, lon) { countryCode: locationInfo ? locationInfo.code : "??", }; } catch (e) { - console.error("Location Util: Static API lookup failed", e); + log(logLevel.ERROR, "Location Util: Static API lookup failed", e); return { country: "Unknown", continent: "Unknown", countryCode: "??" }; } } @@ -49,11 +50,11 @@ export async function getUserLocation(placeId, forceRefresh = false) { return storedData; } } catch (e) { - console.error("Location Util: Error reading storage", e); + log(logLevel.ERROR, "Location Util: Error reading storage", e); } } - console.log('Location Util: Fetching fresh user location via Roblox API...'); + log(logLevel.DEBUG, 'Location Util: Fetching fresh user location via Roblox API...'); try { const serverListRes = await callRobloxApi({ @@ -88,7 +89,7 @@ export async function getUserLocation(placeId, forceRefresh = false) { } } } catch (error) { - console.error('Location Util: Critical Error', error); + log(logLevel.ERROR, 'Location Util: Critical Error', error); } return null; } @@ -140,6 +141,6 @@ export async function updateUserLocationIfChanged(freshCoords) { await chrome.storage.local.set({ [LOCATION_STORAGE_KEY]: cacheObject }); } } catch (e) { - console.error("Location Util: Error in update", e); + log(logLevel.ERROR, "Location Util: Error in update", e); } } \ No newline at end of file diff --git a/src/content/core/utils/trackers/friendslist.js b/src/content/core/utils/trackers/friendslist.js index ac8fc2d..ae36431 100644 --- a/src/content/core/utils/trackers/friendslist.js +++ b/src/content/core/utils/trackers/friendslist.js @@ -1,5 +1,6 @@ import { callRobloxApiJson } from "../../api"; import { getAuthenticatedUserId } from "../../user"; +import { log, logLevel } from "../../logging"; const FRIENDS_DATA_KEY = 'rovalra_friends_data'; @@ -18,7 +19,7 @@ async function fetchFriendsPage(userId, cursor = null) { endpoint: endpoint, }); } catch (error) { - console.error('RoValra: Failed to fetch friends list page', error); + log(logLevel.ERROR, 'RoValra: Failed to fetch friends list page', error); return null; } } @@ -35,7 +36,7 @@ async function fetchUserProfileData(userIds) { } }); } catch (error) { - console.error('RoValra: Failed to fetch user profile data', error); + log(logLevel.ERROR, 'RoValra: Failed to fetch user profile data', error); return null; } } @@ -52,7 +53,7 @@ async function fetchTrustedFriendsStatus(userId, friendIds) { }); return new Set(data?.trustedFriendsId || []); } catch (error) { - console.error('RoValra: Failed to fetch trusted friends status', error); + log(logLevel.ERROR, 'RoValra: Failed to fetch trusted friends status', error); return new Set(); } } @@ -106,11 +107,11 @@ export async function updateFriendsList(userId) { }; await new Promise(resolve => chrome.storage.local.set({ [FRIENDS_DATA_KEY]: allUsersFriendsData }, resolve)); - console.log('RoValra: Friends list and timestamp updated in local storage for user', userId); + log(logLevel.DEBUG, 'RoValra: Friends list and timestamp updated in local storage for user', userId); return fullFriendsList; } catch (error) { - console.error('RoValra: Failed to update friends list', error); + log(logLevel.ERROR, 'RoValra: Failed to update friends list', error); return []; } } diff --git a/src/content/features/avatar/avatarRotator.js b/src/content/features/avatar/avatarRotator.js index ff229de..3487748 100644 --- a/src/content/features/avatar/avatarRotator.js +++ b/src/content/features/avatar/avatarRotator.js @@ -5,6 +5,7 @@ import { callRobloxApiJson } from '../../core/api.js'; import { getBatchThumbnails, createThumbnailElement } from '../../core/thumbnail/thumbnails.js'; import { createRadioButton } from '../../core/ui/general/radio.js'; import { createStyledInput } from '../../core/ui/catalog/input.js'; +import { log, logLevel } from '../../core/logging.js'; export function init() { if (!window.location.pathname.includes('/my/avatar')) return; @@ -159,7 +160,7 @@ export function init() { nextPageCursor = response.nextPageToken || null; } catch (error) { - console.error('RoValra: Failed to fetch avatars', error); + log(logLevel.ERROR, 'RoValra: Failed to fetch avatars', error); } finally { isLoading = false; if (loadBtn) { diff --git a/src/content/features/avatar/filters.js b/src/content/features/avatar/filters.js index ce38fb7..24b9b7c 100644 --- a/src/content/features/avatar/filters.js +++ b/src/content/features/avatar/filters.js @@ -1,6 +1,7 @@ import { checkAssetsInBatch } from '../../core/utils/assetStreamer.js'; import { observeElement, observeAttributes } from '../../core/observer.js'; import { createAvatarFilterUI } from '../../core/ui/FiltersUI.js'; +import { log, logLevel } from '../../core/logging.js'; export function init() { if (!window.location.pathname.includes('/my/avatar')) return; @@ -362,7 +363,7 @@ export function init() { await processItemIds(Array.from(itemIdsToRecheck), currentSession); } } catch(e) { - console.error("Filter process error", e); + log(logLevel.ERROR, "Filter process error", e); } finally { if (currentSession === scanSessionId) { triggerDomUpdate(); diff --git a/src/content/features/catalog/40method.js b/src/content/features/catalog/40method.js index a6d8f71..4ffb9ad 100644 --- a/src/content/features/catalog/40method.js +++ b/src/content/features/catalog/40method.js @@ -13,6 +13,8 @@ import { fetchThumbnails } from '../../core/thumbnail/thumbnails.js'; import DOMPurify from 'dompurify'; import { getPlaceIdFromUrl } from '../../core/idExtractor.js'; +import { log, logLevel } from '../../core/logging.js'; + const ROVALRA_PLACE_ID = '107845747621646'; @@ -93,7 +95,7 @@ async function publishTemplateToPlace(targetPlaceId) { return true; } catch (error) { - console.error('RoValra: Auto-publish failed', error); + log(logLevel.ERROR, 'RoValra: Auto-publish failed', error); throw error; } } @@ -410,7 +412,7 @@ const detectAndAddSaveButton = () => { export const createAndShowPopup = (onSave, initialState = null) => { const currentUserId = getCurrentUserId(); if (!currentUserId) { - alert("Could not identify your user ID. Please make sure you are logged in."); + log(logLevel.CRITICAL, "Could not identify your user ID. Please make sure you are logged in."); return; } @@ -660,15 +662,15 @@ export const createAndShowPopup = (onSave, initialState = null) => { useRoValraGroup: useGroup }, () => { if (chrome.runtime.lastError) { - console.error('RoValra: Storage save error:', chrome.runtime.lastError); - alert('Failed to save settings: ' + chrome.runtime.lastError.message); + log(logLevel.ERROR, 'RoValra: Storage save error:', chrome.runtime.lastError); + log(logLevel.CRITICAL, 'Failed to save settings: ' + chrome.runtime.lastError.message); } else { if (onSuccess) onSuccess(); } }); } else { - console.error('RoValra: Storage API unavailable.'); - alert('Failed to save settings. Storage API unavailable.'); + log(logLevel.ERROR, 'RoValra: Storage API unavailable.'); + log(logLevel.CRITICAL, 'Failed to save settings. Storage API unavailable.'); } }; @@ -730,7 +732,7 @@ export const createAndShowPopup = (onSave, initialState = null) => { if (vResp && vResp.results && vResp.results.length > 0) { initialUserPlaceVersion = vResp.results[0].versionNumber; } - } catch (e) { console.error("RoValra: Failed to fetch initial place version", e); } + } catch (e) { log(logLevel.ERROR, "RoValra: Failed to fetch initial place version", e); } viewUpdateInstructions.classList.remove('sr-hidden'); return; } @@ -768,9 +770,9 @@ export const createAndShowPopup = (onSave, initialState = null) => { viewNonOwnerAck.classList.remove('sr-hidden'); } } catch (error) { - console.error("Failed to fetch group details:", error); + log(logLevel.ERROR, "Failed to fetch group details:", error); close(); - alert("Could not check group ownership. Please try again."); + log(logLevel.CRITICAL, "Could not check group ownership. Please try again."); } }; @@ -811,7 +813,7 @@ export const createAndShowPopup = (onSave, initialState = null) => { } } catch {} } catch (error) { - console.error('RoValra: Failed to fetch groups:', error); + log(logLevel.ERROR, 'RoValra: Failed to fetch groups:', error); groupDropdownContainer.innerHTML = DOMPurify.sanitize('