126 lines
4.7 KiB
TypeScript
126 lines
4.7 KiB
TypeScript
import { createPost } from "../../db/community";
|
||
|
||
export const LYRA_TOPICS = [
|
||
"motivation",
|
||
"tipp",
|
||
"zitat",
|
||
"witzig",
|
||
"news",
|
||
"feature",
|
||
] as const;
|
||
|
||
export type LyraTopic = (typeof LYRA_TOPICS)[number];
|
||
|
||
export const TOPIC_HINTS: Record<LyraTopic, string> = {
|
||
motivation:
|
||
"Schreibe einen kurzen, stillen Gedanken für Menschen die heute kämpfen. Nicht übertrieben – eher ruhig stark.",
|
||
tipp: "Teile einen kleinen, konkreten Trick aus der Verhaltensforschung oder CBT gegen Spieldrang. Praktisch und direkt.",
|
||
zitat:
|
||
"Teile ein tiefgründiges Zitat aus Psychologie, Stoizismus oder Verhaltensforschung – ohne das Wort 'Sucht' zu verwenden. Kurz kommentiert.",
|
||
witzig:
|
||
"Schreibe einen witzigen, selbstironischen Post über das Thema Ablenkung, Impulskontrolle oder Gewohnheiten – leicht und menschlich, nicht flach.",
|
||
news: "Beschreibe kurz eine typische Taktik der Glücksspielindustrie (z.B. Push-Notifications, Bonusangebote) – sachlich und als Warnung formuliert.",
|
||
feature:
|
||
"Weise freundlich auf ein ReBreak-Feature hin (Blocker, Streak, Mail-Agent, Lyra-Chat, SOS-Atemübung) – wähle eines zufällig oder nutze den gegebenen Kontext.",
|
||
};
|
||
|
||
const LYRA_SYSTEM_PROMPT = `Du bist Lyra – Recovery-Coach und Begleiterin der ReBreak-Community. Du hast tiefes Wissen in CBT, Verhaltenspsychologie und dem Alltag von Menschen mit Spielsucht.
|
||
|
||
Du schreibst kurze Community-Beiträge. Deine Stimme:
|
||
- Direkt und persönlich – du sprichst die Person an ("du")
|
||
- Warmherzig, geerdet, wie jemand der wirklich zuhört
|
||
- Niemals klischeehafte Motivationsfloskeln ("Du schaffst das!!!")
|
||
- Kein KI-Sprech, keine Listen, keine Aufzählungen
|
||
- Keine Casino-Werbung, keine Links, keine medizinischen Diagnosen
|
||
- Auf Deutsch, max. 3–4 Sätze
|
||
|
||
Antworte NUR mit dem Post-Text. Kein "Lyra:" Prefix, keine Anführungszeichen.`;
|
||
|
||
const REBREAK_SYSTEM_PROMPT = `Du bist der offizielle ReBreak Account. ReBreak ist eine App zur Überwindung von Glücksspielsucht.
|
||
|
||
Du postest Neuigkeiten, Updates und Community-Ankündigungen. Deine Tonalität:
|
||
- Offiziell aber nahbar – wie ein Team-Update, nicht wie Werbung
|
||
- Kurz (max. 3–4 Sätze)
|
||
- Sachlich und informativ, gelegentlich motivierend
|
||
- Keine medizinischen Ratschläge, keine Links
|
||
- Auf Deutsch
|
||
|
||
Antworte NUR mit dem Post-Text. Kein "ReBreak:" Prefix, keine Anführungszeichen.`;
|
||
|
||
/** POST /api/admin/lyra-post — manueller Bot-Post vom Admin-Dashboard */
|
||
export default defineEventHandler(async (event) => {
|
||
const config = useRuntimeConfig();
|
||
const adminSecret = getHeader(event, "x-admin-secret");
|
||
|
||
if (!config.adminSecret || adminSecret !== config.adminSecret) {
|
||
throw createError({ statusCode: 401, message: "Unauthorized" });
|
||
}
|
||
|
||
const body = await readBody(event);
|
||
const author: "lyra" | "rebreak" =
|
||
body?.author === "rebreak" ? "rebreak" : "lyra";
|
||
|
||
const botUserId =
|
||
author === "rebreak" ? config.rebreakBotUserId : config.lyraBotUserId;
|
||
|
||
if (!botUserId) {
|
||
throw createError({
|
||
statusCode: 500,
|
||
message: `${author === "rebreak" ? "REBREAK_BOT_USER_ID" : "LYRA_BOT_USER_ID"} nicht konfiguriert`,
|
||
});
|
||
}
|
||
|
||
let content: string;
|
||
|
||
if (body?.customContent?.trim()) {
|
||
// Admin hat Text direkt eingegeben – kein LLM-Call nötig
|
||
content = body.customContent.trim();
|
||
} else {
|
||
if (!config.groqApiKey) {
|
||
throw createError({
|
||
statusCode: 500,
|
||
message: "Groq API Key fehlt",
|
||
});
|
||
}
|
||
|
||
const topic: LyraTopic = LYRA_TOPICS.includes(body?.topic)
|
||
? body.topic
|
||
: LYRA_TOPICS[Math.floor(Math.random() * LYRA_TOPICS.length)];
|
||
const context: string | undefined = body?.context?.trim() || undefined;
|
||
|
||
const userPrompt = context
|
||
? `${TOPIC_HINTS[topic]}\n\nZusätzlicher Kontext für diesen Post: ${context}`
|
||
: TOPIC_HINTS[topic];
|
||
|
||
const systemPrompt =
|
||
author === "rebreak" ? REBREAK_SYSTEM_PROMPT : LYRA_SYSTEM_PROMPT;
|
||
|
||
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.3-70b-versatile",
|
||
max_tokens: 200,
|
||
messages: [
|
||
{ role: "system", content: systemPrompt },
|
||
{ role: "user", content: userPrompt },
|
||
],
|
||
},
|
||
});
|
||
|
||
content = response.choices?.[0]?.message?.content?.trim() ?? "";
|
||
if (!content) {
|
||
throw createError({ statusCode: 500, message: "Keine Antwort von LLM" });
|
||
}
|
||
}
|
||
|
||
const post = await createPost(botUserId, "community", content);
|
||
|
||
return { success: true, postId: post.id, author, content };
|
||
});
|