Files
OnePieceDle/src/lib/server/daily-fruit.ts
T
2026-07-10 19:40:17 +02:00

138 lines
4.0 KiB
TypeScript

import { db } from '$lib/server/db';
import { character, devilFruit, devilFruitHistory, type DevilFruit, type Character, type DevilFruitType } from '$lib/server/db/schema';
import { eq, and } from 'drizzle-orm';
const characterWithFruitSelect = {
id: character.id,
name: character.name,
frName: character.frName,
devilFruitId: character.devilFruitId,
devilFruitName: devilFruit.name,
devilFruitType: devilFruit.type,
// include url if available from devilFruit join
url: devilFruit.url
};
export type CharacterWithFruit = Pick<Character, 'id' | 'name' | 'devilFruitId'> & {
devilFruitName: string | null;
devilFruitType: DevilFruitType | null;
frName: string | null;
url: string | null;
};
export function getDateKey(date: Date): number {
return normalizeDay(date).getTime();
}
export function normalizeDay(date: Date = new Date()): Date {
const normalized = new Date(date);
normalized.setHours(1, 0, 0, 0);
return normalized;
}
function pickDailyFruit(fruits: DevilFruit[]): DevilFruit {
const randomIndex = Math.floor(Math.random() * fruits.length);
return fruits[randomIndex];
}
export async function getCharactersWithDevilFruit(): Promise<CharacterWithFruit[]> {
const rows = await db
.select(characterWithFruitSelect)
.from(character)
.leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id))
.where(eq(character.isInDailyMode, true))
.all();
return rows.filter((r): r is CharacterWithFruit => Boolean(r.devilFruitId));
}
export async function getAllDevilFruitsFromCharacters(): Promise<DevilFruit[]> {
const characters = await db
.select(characterWithFruitSelect)
.from(character)
.leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id))
.where(eq(character.isInDailyMode, true))
.all();
const map = new Map<string, DevilFruit>();
for (const row of characters) {
if (row.devilFruitId && row.devilFruitName) {
if (!map.has(row.devilFruitId)) {
map.set(row.devilFruitId, {
id: row.devilFruitId,
name: row.devilFruitName,
type: row.devilFruitType ?? 'Unknown',
url: row.url ?? null
} as DevilFruit);
}
}
}
return Array.from(map.values());
}
export async function getFruitById(fruitId: string): Promise<DevilFruit | null> {
const [found] = await db.select().from(devilFruit).where(eq(devilFruit.id, fruitId)).limit(1);
return (found as DevilFruit) ?? null;
}
export async function getOrCreateTodayFruit(date: Date = new Date()): Promise<DevilFruit | null> {
const today = normalizeDay(date);
const todayDate = getDateKey(today);
const [existingEntry] = await db
.select()
.from(devilFruitHistory)
.where(eq(devilFruitHistory.date, todayDate))
.limit(1);
if (existingEntry?.devilFruitId) {
return getFruitById(existingEntry.devilFruitId);
}
const allFruits = await getAllDevilFruitsFromCharacters();
if (allFruits.length === 0) return null;
const chosen = pickDailyFruit(allFruits);
try {
await db.insert(devilFruitHistory).values({
devilFruitId: chosen.id,
date: todayDate,
createdAt: Date.now(),
updatedAt: Date.now()
});
} catch (error) {
console.error('Failed to record daily fruit:', error);
}
return chosen;
}
export async function getYesterdayFruit(date: Date = new Date()): Promise<DevilFruit | null> {
const yesterday = new Date(date);
yesterday.setDate(yesterday.getDate() - 1);
const yesterdayDate = getDateKey(yesterday);
const [yesterdayEntry] = await db
.select()
.from(devilFruitHistory)
.where(eq(devilFruitHistory.date, yesterdayDate))
.limit(1);
if (!yesterdayEntry?.devilFruitId) return null;
return getFruitById(yesterdayEntry.devilFruitId);
}
export async function getTodayFruitWinsCount(fruitId: string, date: Date = new Date()): Promise<number> {
const today = normalizeDay(date);
const todayDate = getDateKey(today);
const [result] = await db
.select({ won: devilFruitHistory.won })
.from(devilFruitHistory)
.where(and(eq(devilFruitHistory.devilFruitId, fruitId), eq(devilFruitHistory.date, todayDate)));
return result?.won ?? 0;
}