Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/background/background.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SETTINGS_CONFIG } from '../content/core/settings/settingConfig.js';
import { log, logLevel} from '../content/core/logging.js';

// --- Constants & State ---

Expand Down Expand Up @@ -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}).`);
}
});
}
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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 };
}

Expand All @@ -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 };
}
}
Expand Down
13 changes: 7 additions & 6 deletions src/content/core/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();

Expand All @@ -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) {
Expand All @@ -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;
}
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/content/core/configs/userIds.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions src/content/core/games/servers/serverstats.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
}
}

Expand Down
85 changes: 85 additions & 0 deletions src/content/core/logging.js
Original file line number Diff line number Diff line change
@@ -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);
}
33 changes: 16 additions & 17 deletions src/content/core/oauth/oauth.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
// 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();
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) {
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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',
Expand All @@ -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,
Expand All @@ -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;

Expand All @@ -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;
}
})();
Expand Down
5 changes: 3 additions & 2 deletions src/content/core/regionFinder/ClosestServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down Expand Up @@ -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)`);
});
}

Expand Down
Loading