124 lines
3.6 KiB
TypeScript
124 lines
3.6 KiB
TypeScript
// Game-spezifischer Kontext für den Prompt
|
||
const GAME_VIBES: Record<string, string> = {
|
||
snake: "Snake – präzise, fließend, ein Moment der Kontrolle",
|
||
tetris: "Tetris – logisch, fokussiert, Gedanken ordnen statt Chaos",
|
||
memory: "Memory – Geduld, Konzentration, im Hier und Jetzt bleiben",
|
||
tictactoe: "Tic-Tac-Toe – ruhig, überlegt, kleine Strategie",
|
||
};
|
||
|
||
function getGameVibe(gameName: string): string {
|
||
const key = gameName.toLowerCase();
|
||
for (const [k, v] of Object.entries(GAME_VIBES)) {
|
||
if (key.includes(k)) return v;
|
||
}
|
||
return `${gameName} – ein Moment bewusster Ablenkung`;
|
||
}
|
||
|
||
function buildFallback(
|
||
isNewRecord: boolean,
|
||
myRank: number | null,
|
||
mode: string,
|
||
): string {
|
||
const parts: string[] = [];
|
||
if (isNewRecord) parts.push("Neuer persönlicher Rekord! 🏆");
|
||
else if (myRank) parts.push(`Rang #${myRank} in der Rangliste`);
|
||
const challenge =
|
||
mode === "impulse"
|
||
? "Impuls überwunden – kannst du länger standhalten? 💪"
|
||
: "Kannst du das schlagen? 👊";
|
||
parts.push(challenge);
|
||
return parts.join("\n");
|
||
}
|
||
|
||
const SYSTEM_PROMPT = `Du generierst kurze Share-Texte (max 2–3 Zeilen) für die ReBreak-App.
|
||
ReBreak hilft Menschen bei Glücksspielsucht, indem sie Mini-Games spielen statt zu gamble.
|
||
Der Text wird im Community-Feed unter dem Spielnamen gepostet – er soll den Vibe des jeweiligen Spiels widerspiegeln.
|
||
|
||
Regeln:
|
||
- Zeile 1 (optional): Nur wenn neuer Rekord ODER guter Rang, eine kurze Meldung dazu
|
||
- Letzte Zeile: Kurzer, kreativer Challenge-Satz passend zum Spiel auf Deutsch
|
||
- 1–2 Emojis, passend zum Spiel und Ton
|
||
- KEIN Spielname, KEINE Score-Zahl im Text
|
||
- Keine Hashtags, keine URLs
|
||
- Antworte NUR mit dem Text, kein Prefix, keine Anführungszeichen`;
|
||
|
||
/** POST /api/games/share-text */
|
||
export default defineEventHandler(async (event) => {
|
||
await requireUser(event);
|
||
|
||
const body = await readBody(event);
|
||
const {
|
||
gameName,
|
||
score,
|
||
scoreLabel = "Punkte",
|
||
bestScore,
|
||
myRank,
|
||
isNewRecord,
|
||
mode = "game",
|
||
} = body as {
|
||
gameName: string;
|
||
score: number;
|
||
scoreLabel?: string;
|
||
bestScore?: number;
|
||
myRank?: number | null;
|
||
isNewRecord?: boolean;
|
||
mode?: string;
|
||
};
|
||
|
||
if (!gameName || score == null) {
|
||
throw createError({
|
||
statusCode: 400,
|
||
message: "gameName und score erforderlich",
|
||
});
|
||
}
|
||
|
||
const config = useRuntimeConfig();
|
||
|
||
if (!config.groqApiKey) {
|
||
return {
|
||
text: buildFallback(!!isNewRecord, myRank ?? null, mode),
|
||
};
|
||
}
|
||
|
||
const gameVibe = getGameVibe(gameName);
|
||
const userPrompt = [
|
||
`Spiel-Vibe: ${gameVibe}`,
|
||
isNewRecord ? "NEUER PERSÖNLICHER REKORD!" : null,
|
||
myRank ? `Rang #${myRank} in der Rangliste` : null,
|
||
mode === "impulse"
|
||
? "Der Spieler hat einen Spielimpuls damit überwunden."
|
||
: null,
|
||
]
|
||
.filter(Boolean)
|
||
.join("\n");
|
||
|
||
try {
|
||
const response = await $fetch<{
|
||
choices: { message: { content: string } }[];
|
||
}>("https://api.groq.com/openai/v1/chat/completions", {
|
||
method: "POST",
|
||
headers: {
|
||
Authorization: `Bearer ${config.groqApiKey}`,
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: {
|
||
model: "llama-3.1-8b-instant",
|
||
max_tokens: 120,
|
||
messages: [
|
||
{ role: "system", content: SYSTEM_PROMPT },
|
||
{ role: "user", content: userPrompt },
|
||
],
|
||
},
|
||
});
|
||
|
||
const text = response.choices?.[0]?.message?.content?.trim();
|
||
if (!text) throw new Error("empty response");
|
||
|
||
return { text };
|
||
} catch {
|
||
return {
|
||
text: buildFallback(!!isNewRecord, myRank ?? null, mode),
|
||
};
|
||
}
|
||
});
|