- Added new models for leveling and economy functionalities in the Prisma schema, including LevelingConfig, MemberLevel, LevelReward, EconomyConfig, MemberEconomy, ShopItem, and InventoryItem. - Introduced commands for managing XP, economy transactions, and shop interactions, enhancing user engagement. - Updated job handling to include reminders and poll closing functionalities. - Enhanced localization support for new commands and features in both German and English. - Improved the bot's job processing capabilities with a dedicated reminder worker for scheduled tasks.
113 lines
3.2 KiB
TypeScript
113 lines
3.2 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
/** XP required to go from level L to L+1. */
|
|
export function xpForNextLevel(level: number): number {
|
|
return 5 * level * level + 50 * level + 100;
|
|
}
|
|
|
|
/** Total XP needed to reach a given level from 0. */
|
|
export function totalXpForLevel(level: number): number {
|
|
let total = 0;
|
|
for (let i = 0; i < level; i += 1) {
|
|
total += xpForNextLevel(i);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
/** Derive level from cumulative XP. */
|
|
export function levelFromXp(xp: number): number {
|
|
let level = 0;
|
|
let remaining = Math.max(0, xp);
|
|
while (remaining >= xpForNextLevel(level)) {
|
|
remaining -= xpForNextLevel(level);
|
|
level += 1;
|
|
if (level > 1000) {
|
|
break;
|
|
}
|
|
}
|
|
return level;
|
|
}
|
|
|
|
export function progressInLevel(xp: number): { level: number; intoLevel: number; needed: number } {
|
|
const level = levelFromXp(xp);
|
|
const base = totalXpForLevel(level);
|
|
const intoLevel = xp - base;
|
|
const needed = xpForNextLevel(level);
|
|
return { level, intoLevel, needed };
|
|
}
|
|
|
|
export function randomInt(min: number, max: number): number {
|
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
}
|
|
|
|
export const LevelUpModeSchema = z.enum(['CHANNEL', 'DM', 'OFF']);
|
|
export type LevelUpMode = z.infer<typeof LevelUpModeSchema>;
|
|
|
|
export const MultiplierMapSchema = z.record(z.string(), z.number().positive());
|
|
export type MultiplierMap = z.infer<typeof MultiplierMapSchema>;
|
|
|
|
export function parseMultiplierMap(raw: unknown): MultiplierMap {
|
|
const parsed = MultiplierMapSchema.safeParse(raw);
|
|
return parsed.success ? parsed.data : {};
|
|
}
|
|
|
|
export function formatCurrency(amount: number, symbol: string, name: string): string {
|
|
return `${symbol} ${amount.toLocaleString()} ${name}`;
|
|
}
|
|
|
|
export const EIGHT_BALL_ANSWERS = {
|
|
de: [
|
|
'Ja.',
|
|
'Nein.',
|
|
'Vielleicht.',
|
|
'Frag später nochmal.',
|
|
'Sieht gut aus.',
|
|
'Zweifelhaft.',
|
|
'Auf keinen Fall.',
|
|
'Absolut.'
|
|
],
|
|
en: [
|
|
'Yes.',
|
|
'No.',
|
|
'Maybe.',
|
|
'Ask again later.',
|
|
'Looks good.',
|
|
'Doubtful.',
|
|
'Absolutely not.',
|
|
'Absolutely.'
|
|
]
|
|
} as const;
|
|
|
|
export const TRIVIA_QUESTIONS = [
|
|
{
|
|
q: { de: 'Welche Farbe hat der Himmel an einem klaren Tag?', en: 'What color is the sky on a clear day?' },
|
|
options: { de: ['Blau', 'Grün', 'Rot', 'Gelb'], en: ['Blue', 'Green', 'Red', 'Yellow'] },
|
|
answer: 0
|
|
},
|
|
{
|
|
q: { de: 'Wie viele Kontinente gibt es?', en: 'How many continents are there?' },
|
|
options: { de: ['5', '6', '7', '8'], en: ['5', '6', '7', '8'] },
|
|
answer: 2
|
|
},
|
|
{
|
|
q: { de: 'Welches Tier ist ein Säugetier?', en: 'Which animal is a mammal?' },
|
|
options: { de: ['Hai', 'Delphin', 'Thunfisch', 'Lachs'], en: ['Shark', 'Dolphin', 'Tuna', 'Salmon'] },
|
|
answer: 1
|
|
},
|
|
{
|
|
q: { de: 'Was ist 2 + 2?', en: 'What is 2 + 2?' },
|
|
options: { de: ['3', '4', '5', '22'], en: ['3', '4', '5', '22'] },
|
|
answer: 1
|
|
},
|
|
{
|
|
q: { de: 'Hauptstadt von Frankreich?', en: 'Capital of France?' },
|
|
options: { de: ['Berlin', 'Madrid', 'Paris', 'Rom'], en: ['Berlin', 'Madrid', 'Paris', 'Rome'] },
|
|
answer: 2
|
|
}
|
|
] as const;
|
|
|
|
export const HANGMAN_WORDS = {
|
|
de: ['DISCORD', 'NEXUMI', 'SERVER', 'MODERATION', 'BOT', 'KANAL', 'ROLLE'],
|
|
en: ['DISCORD', 'NEXUMI', 'SERVER', 'MODERATION', 'BOT', 'CHANNEL', 'ROLE']
|
|
} as const;
|