// ==UserScript==
// @name iR Forum user stats
// @namespace http://tampermonkey.net/
// @version 2.2_2026-03-17
// @description Show user stats in the iRacing forum
// @author Max (refactored MR ver
// @match https://forums.iracing.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=iracing.com
// @downloadURL https://raw.githubusercontent.com/exenza/iracing_forum_browser_addon_drivers_stats/refs/heads/main/tampermonkey/ifbads_source.js
// @updateURL https://raw.githubusercontent.com/exenza/iracing_forum_browser_addon_drivers_stats/refs/heads/main/tampermonkey/ifbads_source.js
// ==/UserScript==
'use strict';
// Start User Configuration
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
const RECENT_EVENTS_COUNT = 2; // Number of recent events to display
const RECENT_EVENTS_TYPES = ['RACE']; // Event types to display: 'RACE', 'PRACTICE', 'HOSTED', 'LEAGUE'
// End user configuration
const API_ENDPOINT = 'https://lrm4on1eih.execute-api.eu-west-2.amazonaws.com/prod';
const cache = new Map();
// Simple API fetch for single driver
async function fetchSingleDriver(name) {
if (!name || !name.trim()) {
console.log('No valid name provided to fetchSingleDriver');
return null;
}
const url = `${API_ENDPOINT}/drivers?names=${encodeURIComponent(name.trim())}`;
console.log('Fetching driver:', name);
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return data[name] || null;
} catch (error) {
console.error('API Error for', name, ':', error);
return null;
}
}
// Cache helpers
function getCached(name) {
const cached = cache.get(name);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
cache.delete(name);
return null;
}
function setCached(name, data) {
cache.set(name, { data, timestamp: Date.now() });
}// SVG icons for license categories
const svgIcons = {
oval: ' viewBox="-2 -1.55 28 18">',
sports_car: ' viewBox="-2 -2 28 18">',
formula_car: ' viewBox="-2 -1 28 18">',
sports_car: ' viewBox="-2 -2 28 18">',
formula_car: ' viewBox="-2 -1 28 18">',
dirt_oval: ' viewBox="-2 0 28 18">',
dirt_road: ' viewBox="-2 0 28 18">',
undefined: ' viewBox="0 0 1 18">'
};
// Simple category detection from car name
function getCarCategory(carName) {
const name = carName.toLowerCase();
if (name.includes('formula') || name.includes('f1') || name.includes('f2') || name.includes('f3') ||
name.includes('indy') || name.includes('dallara') || name.includes('skip barber') ||
name.includes('lotus 79') || name.includes('williams') || name.includes('mclaren mp4')) {
return 'formula_car';
}
if (name.includes('dirt') && (name.includes('oval') || name.includes('sprint') || name.includes('late model') ||
name.includes('modified') || name.includes('midget') || name.includes('street stock'))) {
return 'dirt_oval';
}
if (name.includes('dirt') || name.includes('rally') || name.includes('beetle') || name.includes('fiesta') ||
name.includes('wrx') || name.includes('pro 2') || name.includes('pro 4')) {
return 'dirt_road';
}
if (name.includes('nascar') || name.includes('oval') || name.includes('legends') || name.includes('modified') ||
name.includes('sprint car') || name.includes('silver crown') || name.includes('street stock') ||
name.includes('late model') || name.includes('truck')) {
return 'oval';
}
return 'sports_car'; // Default for GT3, GTE, sports cars, etc.
}
// Helper functions
function yearsSince(dateStr) {
const years = (Date.now() - new Date(dateStr)) / (1000 * 60 * 60 * 24 * 365.25);
return Math.floor(years);
}
// Generate license display
function renderLicenses(driver) {
if (!driver.member_info?.licenses) return '';
const licenses = driver.member_info.licenses.map(lic => {
const className = lic.group_name.replace('Class ', '').replace('Rookie', 'R').replace('Pro', 'P');
const category = lic.category || 'undefined';
return `