import OpenAI from "openai"; import { db } from "./db"; import { dailyHoroscopes, cosmicEvents } 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 { 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 { 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." }` } ], max_completion_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 { 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; } const VALID_ICONS = ["mercury", "venus", "moon", "newmoon", "sun", "saturn", "jupiter", "mars"]; const VALID_TYPES = ["retrograde", "moon", "transit", "season"]; export async function getCosmicEventsForToday(): Promise { const today = getTodayStr(); const rows = await db.select().from(cosmicEvents) .where(eq(cosmicEvents.dateStr, today)); return rows .sort((a, b) => a.position - b.position) .map((r) => ({ title: r.title, description: r.description, dateRange: r.dateRange, icon: r.icon, type: r.type, affectedSigns: JSON.parse(r.affectedSigns || "[]"), })); } export async function generateCosmicEvents(): Promise { const today = getTodayStr(); const existing = await db.select().from(cosmicEvents) .where(eq(cosmicEvents.dateStr, today)); if (existing.length >= 6) { console.log(`Cosmic events for ${today} already exist.`); return; } console.log(`Generating cosmic events for ${today}...`); const d = new Date(); const monthName = d.toLocaleDateString("de-DE", { month: "long", year: "numeric" }); const eventSlots = [ "die aktuelle Tierkreis-Saison (Sonne im aktuellen Sternzeichen), type=season, icon=sun", "die aktuelle Mondphase (Vollmond oder Neumond, je nachdem was näher ist), type=moon, icon=moon oder newmoon", "ein aktueller Venus-Transit (Venus im aktuellen Zeichen), type=transit, icon=venus", "ein aktueller Mars- oder Merkur-Aspekt (inkl. eventueller Retrograde), type=transit oder retrograde, icon=mars oder mercury", "ein aktueller Saturn-Transit, type=transit, icon=saturn", "ein aktueller Jupiter-Transit, type=transit, icon=jupiter", ]; const results: any[] = []; for (let i = 0; i < eventSlots.length; i++) { try { const response = await openai.chat.completions.create({ model: "gpt-5-mini", messages: [ { role: "system", content: `Du bist ein erfahrener Astrologe für eine deutschsprachige Volksmusik- und Schlager-Website. Du kennst die realen astronomischen Daten. Heute ist der ${today} (${monthName}). Beschreibe nur Ereignisse, die rund um dieses Datum tatsächlich aktiv sind, mit realistischen Datumsangaben. Antworte ausschließlich auf Deutsch.`, }, { role: "user", content: `Beschreibe folgendes aktuelles kosmisches Ereignis für heute (${today}): ${eventSlots[i]}. Antworte NUR mit einem JSON-Objekt (kein Markdown, keine Erklärung) in diesem exakten Format: { "title": "Kurzer Titel, z.B. 'Vollmond im Skorpion' oder 'Zwillinge-Saison'", "description": "3-4 Sätze über die Bedeutung dieses Ereignisses und seinen Einfluss.", "dateRange": "Realistische Datumsangabe passend zum heutigen Datum, z.B. '21. Mai – 20. Juni 2026' oder '11. Juni 2026'", "icon": "EINER VON: mercury, venus, moon, newmoon, sun, saturn, jupiter, mars", "type": "EINER VON: retrograde, moon, transit, season", "affectedSigns": ["3-4 betroffene Sternzeichen auf Deutsch"] }`, }, ], max_completion_tokens: 3000, }); let content = response.choices[0]?.message?.content || ""; content = content.replace(/```json/gi, "").replace(/```/g, "").trim(); const jsonMatch = content.match(/\{[\s\S]*\}/); if (!jsonMatch) { console.error(`Failed to parse cosmic event ${i}. Raw:`, content.slice(0, 300)); continue; } const ev = JSON.parse(jsonMatch[0]); const icon = VALID_ICONS.includes(ev.icon) ? ev.icon : "moon"; const type = VALID_TYPES.includes(ev.type) ? ev.type : "transit"; const signs = Array.isArray(ev.affectedSigns) ? ev.affectedSigns : []; await db.insert(cosmicEvents).values({ dateStr: today, position: i, title: String(ev.title || "").slice(0, 120), description: String(ev.description || ""), dateRange: String(ev.dateRange || "").slice(0, 120), icon, type, affectedSigns: JSON.stringify(signs), }); results.push(ev.title); } catch (err: any) { console.error(`Error generating cosmic event ${i}:`, err.message); } } console.log(`Generated ${results.length} cosmic events for ${today}.`); }