import fs from 'fs'; import Sentry from '@sentry/node'; import Config from './config.js'; export function capture_sentry_exception(err) { if (Config.ENABLE_SENTRY) { Sentry.captureException(err); Sentry.configureScope((scope) => scope.clear()); } else { console.error(err); } } export function gen_url(mode, path) { const ruleset = ['osu', 'taiko', 'catch', 'mania'][mode || 0]; const scheme = Config.IS_PRODUCTION ? 'https://' : 'http://'; return `${scheme}${ruleset}.${Config.domain_name}${path}`; } export function random_from(arr) { return arr[Math.floor((Math.random() * arr.length))]; } const base = fs.readFileSync('views/main.eta', 'utf-8'); export const render_error = (error) => { return base.replace('<%~ it.body %>', error); }; // Returns the color of a given star rating, matching osu!web's color scheme. export function stars_to_color(sr) { if (sr <= 0.1) { return '#4290FB'; } else if (sr >= 9) { return '#000000'; } const star_levels = [0.1, 1.25, 2, 2.5, 3.3, 4.2, 4.9, 5.8, 6.7, 7.7, 9]; const star_colors = ['#4290FB', '#4FC0FF', '#4FFFD5', '#7CFF4F', '#F6F05C', '#FF8068', '#FF4E6F', '#C645B8', '#6563DE', '#18158E', '#000000']; for (const i in star_levels) { if (!star_levels.hasOwnProperty(i)) continue; if (star_levels[i] >= sr && star_levels[i-1] < sr) { const lower = star_levels[i - 1]; const upper = star_levels[i]; const ratio = (sr - lower) / (upper - lower); const r = parseInt(star_colors[i-1].substr(1, 2), 16) * (1 - ratio) + parseInt(star_colors[i].substr(1, 2), 16) * ratio; const g = parseInt(star_colors[i-1].substr(3, 2), 16) * (1 - ratio) + parseInt(star_colors[i].substr(3, 2), 16) * ratio; const b = parseInt(star_colors[i-1].substr(5, 2), 16) * (1 - ratio) + parseInt(star_colors[i].substr(5, 2), 16) * ratio; return '#' + Math.round(r).toString(16).padStart(2, '0') + Math.round(g).toString(16).padStart(2, '0') + Math.round(b).toString(16).padStart(2, '0'); } } } export function recover_sql_query_data(filter_query) { if (!filter_query || filter_query == 1) { return { key_counts: [], filters: [], }; } // We never stored the map filters/mania keycounts! // So, we recover them now from the SQL query ¯\_(ツ)_/¯ const mania_keycounts = []; const cleaned_filters = []; const criterion = filter_query.split(' AND '); criterion.shift(); // SQL query starts with "1 AND " for (let i = 0; i < criterion.length; i += 2) { // Map filters are " AND thing >= 0 AND thing <= 9" // Mania keycounts are " AND (0 OR cs = 4 OR cs = 8)" if (criterion[i].startsWith('(')) { const foo = criterion[i].split(' OR cs = '); foo.shift(); for (const cs of foo) { mania_keycounts.push(parseInt(cs, 10)); } continue; } const bar = criterion[i].split(' '); cleaned_filters.push({ name: bar[0], min: parseFloat(bar[2]), max: parseFloat(criterion[i+1].split(' ')[2]), }); } return { key_counts: mania_keycounts, filters: cleaned_filters, }; } export function escape_markdown(text) { text = text.replaceAll('\\', '\\\\'); text = text.replaceAll('_', '\\_'); text = text.replaceAll('~', '\\~'); text = text.replaceAll('`', '\\`'); text = text.replaceAll('|', '\\|'); text = text.replaceAll(/(https?:\/\/\S+)/g, '<$1>'); return text; }