folx-tv/server/horoscope-generator.ts
2026-02-28 20:36:50 +00:00

114 lines
4.2 KiB
TypeScript

import OpenAI from "openai";
import { db } from "./db";
import { dailyHoroscopes } from "@shared/schema";
import { eq, and } from "drizzle-orm";
const openai = new OpenAI({
apiKey: process.env.AI_INTEGRATIONS_OPENAI_API_KEY,
baseURL: process.env.AI_INTEGRATIONS_OPENAI_BASE_URL,
});
const SIGN_NAMES = [
"Widder", "Stier", "Zwillinge", "Krebs", "Löwe", "Jungfrau",
"Waage", "Skorpion", "Schütze", "Steinbock", "Wassermann", "Fische"
];
function getTodayStr(): string {
const d = new Date();
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
}
export async function getHoroscopesForToday(): Promise<any[]> {
const today = getTodayStr();
const existing = await db.select().from(dailyHoroscopes).where(eq(dailyHoroscopes.dateStr, today));
if (existing.length === 12) return existing;
return [];
}
export async function generateDailyHoroscopes(): Promise<void> {
const today = getTodayStr();
const existing = await db.select().from(dailyHoroscopes).where(eq(dailyHoroscopes.dateStr, today));
if (existing.length >= 12) {
console.log(`Horoscopes for ${today} already exist.`);
return;
}
console.log(`Generating horoscopes for ${today}...`);
for (let i = 0; i < SIGN_NAMES.length; i++) {
const signName = SIGN_NAMES[i];
const alreadyExists = existing.find(h => h.signIndex === i);
if (alreadyExists) continue;
try {
const response = await openai.chat.completions.create({
model: "gpt-5-mini",
messages: [
{
role: "system",
content: `Du bist ein erfahrener Astrologe, der tägliche Horoskope für eine deutschsprachige Volksmusik- und Schlager-Nachrichtenwebsite schreibt. Dein Stil ist warm, ermutigend und poetisch. Du beziehst manchmal Musik, Natur und alpine Kultur in deine Texte ein. Schreibe immer auf Deutsch. Das heutige Datum ist ${today}.`
},
{
role: "user",
content: `Erstelle ein ausführliches Tageshoroskop für das Sternzeichen ${signName} für heute (${today}).
Antworte NUR mit einem JSON-Objekt in diesem exakten Format (kein Markdown, keine Erklärung):
{
"general": "Ausführlicher allgemeiner Tagestext, mindestens 4-5 Sätze über die allgemeine Energie, Stimmung und Möglichkeiten des Tages.",
"love": "Ausführlicher Text über Liebe und Partnerschaft, mindestens 3-4 Sätze mit konkreten Ratschlägen für Singles und Paare.",
"career": "Ausführlicher Text über Beruf und Finanzen, mindestens 3-4 Sätze mit konkreten Tipps.",
"health": "Ausführlicher Text über Gesundheit und Wohlbefinden, mindestens 3-4 Sätze.",
"tip": "Ein konkreter, umsetzbarer Tipp des Tages in 1-2 Sätzen.",
"weekly": "Ausführliche Wochenvorschau, mindestens 4-5 Sätze mit Hinweisen für jeden Wochentag.",
"monthly": "Ausführliche Monatsvorschau, mindestens 4-5 Sätze über die wichtigsten Themen des Monats."
}`
}
],
temperature: 0.9,
max_tokens: 2000,
});
const content = response.choices[0]?.message?.content || "";
const jsonMatch = content.match(/\{[\s\S]*\}/);
if (!jsonMatch) {
console.error(`Failed to parse horoscope for ${signName}`);
continue;
}
const parsed = JSON.parse(jsonMatch[0]);
await db.insert(dailyHoroscopes).values({
signIndex: i,
signName,
dateStr: today,
general: parsed.general || "",
love: parsed.love || "",
career: parsed.career || "",
health: parsed.health || "",
tip: parsed.tip || "",
weekly: parsed.weekly || "",
monthly: parsed.monthly || "",
});
console.log(`Generated horoscope for ${signName}`);
} catch (err: any) {
console.error(`Error generating horoscope for ${signName}:`, err.message);
}
}
console.log(`Horoscope generation complete for ${today}.`);
}
export async function getOrGenerateHoroscope(signIndex: number): Promise<any | null> {
const today = getTodayStr();
const [existing] = await db.select().from(dailyHoroscopes)
.where(and(eq(dailyHoroscopes.dateStr, today), eq(dailyHoroscopes.signIndex, signIndex)));
if (existing) return existing;
return null;
}