Initial commit: SREBOT website (Express/EJS + i18n) - extracted from SREBOT monorepo

This commit is contained in:
clxud
2026-07-02 02:35:56 +00:00
commit 7f2ab08adc
145 changed files with 148257 additions and 0 deletions
+332
View File
@@ -0,0 +1,332 @@
// Seasons Filter Utility
// Parses and manages season/week data from the seasons constant file
class SeasonsFilter {
constructor() {
this.seasons = [];
this.loaded = false;
}
// Parse seasons text content into structured data
parseSeasons(content) {
const seasons = [];
const lines = content.split('\n');
let currentSeason = null;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (!line) continue;
// Check if it's a season header (e.g., "2025-V")
const seasonMatch = line.match(/^(\d{4}-[IVX]+)$/);
if (seasonMatch) {
if (currentSeason) {
seasons.push(currentSeason);
}
currentSeason = {
name: seasonMatch[1],
weeks: []
};
continue;
}
// Check if it's a week line
// Format: "week 1 (01.09 07.09) <t:1756735246:R>: max BR 14.0"
// or: "until eos (27.10 31.10) <t:1761487200:R>: max BR 5.0"
const weekMatch = line.match(/(?:week\s+(\d+)|until eos)\s+\((\d{2})\.(\d{2})\s*[–—]\s*(\d{2})\.(\d{2})\)\s*<t:(\d+):R>/);
if (weekMatch && currentSeason) {
const weekNum = weekMatch[1] ? parseInt(weekMatch[1]) : null;
const startDay = parseInt(weekMatch[2]);
const startMonth = parseInt(weekMatch[3]);
const endDay = parseInt(weekMatch[4]);
const endMonth = parseInt(weekMatch[5]);
const timestamp = parseInt(weekMatch[6]);
// Extract max BR if present (preserve .0 formatting)
const brMatch = line.match(/max BR ([\d.]+)/);
const maxBR = brMatch ? brMatch[1] : null; // Keep as string to preserve ".0"
// Use the Unix timestamp directly from the file (it's already correct!)
// The timestamp is the START of the week
const startDate = new Date(timestamp * 1000); // Convert to milliseconds
// Calculate end date: 7 days from start (or until end of season for "until eos")
// For "until eos" entries, we'll need to calculate based on the actual end day
let endDate;
// Determine year from season name (e.g., "2025-V" -> 2025)
const yearMatch = currentSeason.name.match(/^(\d{4})/);
const year = yearMatch ? parseInt(yearMatch[1]) : new Date().getFullYear();
// Build end date from the parsed end day/month
// Need to handle year rollover (e.g., December -> January)
let endYear = year;
if (endMonth < startMonth) {
endYear = year + 1; // Crossed into next year
}
endDate = new Date(endYear, endMonth - 1, endDay, 23, 59, 59, 999);
// Build display name with max BR (preserve decimal formatting like 14.0)
let displayName;
if (weekNum) {
displayName = `Week ${weekNum} (${weekMatch[2]}.${weekMatch[3]} ${weekMatch[4]}.${weekMatch[5]})`;
if (maxBR) displayName += ` - BR ${maxBR}`;
} else {
displayName = `Until EOS (${weekMatch[2]}.${weekMatch[3]} ${weekMatch[4]}.${weekMatch[5]})`;
if (maxBR) displayName += ` - BR ${maxBR}`;
}
currentSeason.weeks.push({
weekNumber: weekNum,
name: weekNum ? `Week ${weekNum}` : 'Until EOS',
startDate: startDate,
endDate: endDate,
startTimestamp: timestamp, // Store original Unix timestamp (seconds)
maxBR: maxBR,
displayName: displayName
});
}
}
// Add the last season
if (currentSeason) {
seasons.push(currentSeason);
}
// Post-process: set each week's endDate to the next week's startDate - 1ms
// This uses the exact Unix timestamps instead of the approximate text-parsed dates
seasons.forEach(season => {
season.weeks.forEach((week, i) => {
if (i + 1 < season.weeks.length) {
week.endDate = new Date(season.weeks[i + 1].startDate.getTime() - 1);
}
// Last week of each season keeps its text-parsed endDate
});
});
return seasons;
}
// Load seasons data from the constant file
async loadSeasons() {
if (this.loaded) {
return this.seasons;
}
try {
const response = await fetch('/constants/seasons');
if (!response.ok) {
throw new Error(`Failed to load seasons: ${response.status}`);
}
const content = await response.text();
this.seasons = this.parseSeasons(content);
this.loaded = true;
console.log('[Seasons Filter] Loaded seasons:', this.seasons);
return this.seasons;
} catch (error) {
console.error('[Seasons Filter] Error loading seasons:', error);
this.seasons = [];
this.loaded = false;
return [];
}
}
// Get all seasons
getSeasons() {
return this.seasons;
}
// Get a specific season by name
getSeason(seasonName) {
return this.seasons.find(s => s.name === seasonName);
}
// Get current season (based on current date)
getCurrentSeason() {
const now = new Date();
for (const season of this.seasons) {
for (const week of season.weeks) {
if (now >= week.startDate && now <= week.endDate) {
return season;
}
}
}
// If no current season found, return the most recent one
return this.seasons.length > 0 ? this.seasons[this.seasons.length - 1] : null;
}
// Get current week (based on current date)
getCurrentWeek() {
const now = new Date();
for (const season of this.seasons) {
for (const week of season.weeks) {
if (now >= week.startDate && now <= week.endDate) {
return { season, week };
}
}
}
return null;
}
// Get date range for a specific season
getSeasonDateRange(seasonName) {
const season = this.getSeason(seasonName);
if (!season || season.weeks.length === 0) {
return null;
}
return {
startDate: season.weeks[0].startDate,
endDate: season.weeks[season.weeks.length - 1].endDate,
season: season
};
}
// Get date range for a specific week in a season
getWeekDateRange(seasonName, weekNumber) {
const season = this.getSeason(seasonName);
if (!season) {
return null;
}
const week = season.weeks.find(w => w.weekNumber === weekNumber);
if (!week) {
return null;
}
return {
startDate: week.startDate,
endDate: week.endDate,
week: week
};
}
// Get all unique BR values across all seasons (returns array of strings, preserving ".0")
getAllBRValues() {
const brSet = new Set();
this.seasons.forEach(season => {
season.weeks.forEach(week => {
if (week.maxBR) {
brSet.add(week.maxBR);
}
});
});
// Sort descending (convert to float for comparison, but keep as strings)
return Array.from(brSet).sort((a, b) => parseFloat(b) - parseFloat(a));
}
// Get all weeks that match a specific BR
getWeeksByBR(maxBR) {
const matchingWeeks = [];
this.seasons.forEach(season => {
season.weeks.forEach(week => {
if (week.maxBR === maxBR) {
matchingWeeks.push({
season: season,
week: week,
displayName: `${season.name} - ${week.name}`
});
}
});
});
return matchingWeeks;
}
// Get combined date range for all weeks with a specific BR (for filtering multiple weeks)
getDateRangeForBR(maxBR) {
const weeks = this.getWeeksByBR(maxBR);
if (weeks.length === 0) {
return null;
}
// Return array of date ranges (one for each week with this BR)
return weeks.map(({ season, week }) => ({
startDate: week.startDate,
endDate: week.endDate,
seasonName: season.name,
weekName: week.name,
displayName: `${season.name} - ${week.displayName}`
}));
}
// Populate a select element with season options
populateSeasonSelect(selectElement, includeAllOption = true) {
selectElement.innerHTML = '';
if (includeAllOption) {
const allOption = document.createElement('option');
allOption.value = 'all';
allOption.textContent = window.__t ? window.__t('leaderboard.allSeasons') : 'All Seasons';
selectElement.appendChild(allOption);
}
// Add seasons in reverse order (most recent first)
for (let i = this.seasons.length - 1; i >= 0; i--) {
const season = this.seasons[i];
const option = document.createElement('option');
option.value = season.name;
option.textContent = season.name;
selectElement.appendChild(option);
}
}
// Populate a select element with BR options
populateBRSelect(selectElement, includeAllOption = true) {
selectElement.innerHTML = '';
if (includeAllOption) {
const allOption = document.createElement('option');
allOption.value = '';
allOption.textContent = window.__t ? window.__t('leaderboard.allBR') : 'All BR';
selectElement.appendChild(allOption);
}
const brValues = this.getAllBRValues();
brValues.forEach(br => {
const option = document.createElement('option');
option.value = br;
option.textContent = `BR ${br}`;
selectElement.appendChild(option);
});
}
// Populate a select element with week options for a given season
populateWeekSelect(selectElement, seasonName, includeAllOption = true) {
selectElement.innerHTML = '';
if (includeAllOption) {
const allOption = document.createElement('option');
allOption.value = 'all';
allOption.textContent = window.__t ? window.__t('leaderboard.allWeeks') : 'All Weeks';
selectElement.appendChild(allOption);
}
const season = this.getSeason(seasonName);
if (!season) {
return;
}
season.weeks.forEach(week => {
const option = document.createElement('option');
option.value = week.weekNumber !== null ? week.weekNumber.toString() : 'final';
option.textContent = week.displayName;
selectElement.appendChild(option);
});
}
}
// Global instance
window.seasonsFilter = window.seasonsFilter || new SeasonsFilter();