File size: 14,530 Bytes
b8b98dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
import { NextRequest, NextResponse } from "next/server";
import { Telegraf } from "telegraf";
import { db } from "@/lib/db";
import ZAI from "z-ai-web-dev-sdk";

// Vercel/Next.js specific setting to prevent high execution times for webhook
export const maxDuration = 60;

// Token check
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || "";
const bot = new Telegraf(TELEGRAM_BOT_TOKEN);

async function getOrCreateUser(telegramId: string, username?: string, name?: string) {
    let user = await db.user.findFirst({
        where: { externalId: telegramId, source: "telegram" }
    });

    if (!user) {
        user = await db.user.create({
            data: {
                externalId: telegramId,
                source: "telegram",
                name: name || username || "Usuario Telegram",
                tier: "free" // Default tier
            }
        });
    }
    return user;
}

export async function POST(request: NextRequest) {
    if (!TELEGRAM_BOT_TOKEN) {
        return NextResponse.json({ success: false, error: "Token not configured" }, { status: 500 });
    }

    try {
        const body = await request.json();

        // 1. Get the Character
        const character = await db.character.findFirst({ where: { isActive: true } });
        if (!character) return NextResponse.json({ success: true, message: "No active character" });

        // 2. Parse Telegram update manually to avoid local server conflicts with Telegraf webhook handler
        // We only care about standard messages
        if (body.message && body.message.text) {
            const chatId = body.message.chat.id.toString();
            const text = body.message.text.trim();
            const username = body.message.from?.username;
            const firstName = body.message.from?.first_name;

            // Ensure user exists
            const user = await getOrCreateUser(chatId, username, firstName);

            // Check tier for media generation
            const isPremium = user.tier === "premium" || user.tier === "pro";

            // Determine if it was a media command
            const isImageRequest = text.toLowerCase().startsWith("/image");
            const isVideoRequest = text.toLowerCase().startsWith("/video");

            if (isImageRequest || isVideoRequest) {
                if (!isPremium) {
                    await bot.telegram.sendMessage(chatId, "¡Hola! Las imágenes y videos son características exclusivas para suscriptores Premium. ✨💖 Mejora tu plan para solicitarlos.");
                    return NextResponse.json({ success: true });
                }

                const promptParam = text.replace(isImageRequest ? "/image" : "/video", "").trim();

                // Enviar indicador de escritura/generación
                await bot.telegram.sendChatAction(chatId, isImageRequest ? "upload_photo" : "upload_video");

                // Check for existing undelivered content first to prevent duplicates
                const contentType = isImageRequest ? "image" : "video";
                const undeliveredContent = await db.content.findFirst({
                    where: {
                        type: contentType,
                        characterId: character.id,
                        status: "completed",
                        // Ensure it hasn't been delivered to THIS user
                        assetDeliveries: { none: { userId: user.id } },
                        // If they asked for something specific, try to match it slightly, otherwise grab any
                        ...(promptParam ? { prompt: { contains: promptParam } } : {})
                    },
                    orderBy: { createdAt: 'desc' }
                });

                if (undeliveredContent && undeliveredContent.filePath) {
                    // We found existing content they haven't seen! Send it.
                    if (isImageRequest) {
                        // Telegram can send via URL or file ID if previously uploaded. 
                        // We assume filePath might be a URL or base64 if it's external, for now we will just re-generate if it was base64 only, but if it has a URL we send it.
                        if (undeliveredContent.filePath.startsWith("http")) {
                            await bot.telegram.sendPhoto(chatId, undeliveredContent.filePath, { caption: "Recuerdo esto... 💖" });
                            // Mark as delivered
                            await db.assetDelivery.create({
                                data: { contentId: undeliveredContent.id, userId: user.id, characterId: character.id, channel: "telegram" }
                            });
                            return NextResponse.json({ success: true });
                        }
                    } else {
                        if (undeliveredContent.filePath.startsWith("http")) {
                            await bot.telegram.sendVideo(chatId, undeliveredContent.filePath, { caption: "Mira este clip... ✨" });
                            await db.assetDelivery.create({
                                data: { contentId: undeliveredContent.id, userId: user.id, characterId: character.id, channel: "telegram" }
                            });
                            return NextResponse.json({ success: true });
                        }
                    }
                }

                // If we reach here, we need to generate NEW content.
                if (!promptParam) {
                    await bot.telegram.sendMessage(chatId, `Dime qué quieres ver. Ej: ${isImageRequest ? '/image' : '/video'} de ti en la playa.`);
                    return NextResponse.json({ success: true });
                }

                // Llamar a nuestro propio endpoint interno de forma indirecta, o directamente a zai
                try {
                    const zai = await ZAI.create();
                    // Platform: "telegram-uncensored" skips traditional censor tags.
                    const fullPrompt = `${character.styleDescription || ''}. ${promptParam}. selfie, intimate, casual.`;

                    if (isImageRequest) {
                        const response = await zai.images.generations.create({ prompt: fullPrompt, size: "1024x1024" });
                        const base64 = response.data[0]?.base64;
                        if (base64) {
                            // Register delivery to prevent duplicates later if we build a catalog
                            const content = await db.content.create({
                                data: {
                                    title: "Telegram Gen",
                                    type: "image",
                                    prompt: fullPrompt,
                                    platform: "telegram-uncensored",
                                    characterId: character.id,
                                    filePath: "telegram-base64", // Since it's direct to chat
                                    status: "completed"
                                }
                            });
                            await db.assetDelivery.create({
                                data: {
                                    contentId: content.id,
                                    userId: user.id,
                                    characterId: character.id,
                                    channel: "telegram"
                                }
                            });
                            await bot.telegram.sendPhoto(chatId, { source: Buffer.from(base64, 'base64') }, { caption: "Aquí tienes... 💖" });
                        } else {
                            throw new Error("No image generated");
                        }
                    } else {
                        // Video request
                        const response = await (zai as any).videos.generations.create({ prompt: `${fullPrompt}. short clip, vertical 9:16 aspect ratio.` });
                        const videoUrl = response.data?.[0]?.url;
                        if (videoUrl) {
                            const content = await db.content.create({
                                data: {
                                    title: "Telegram Gen",
                                    type: "video",
                                    prompt: fullPrompt,
                                    platform: "telegram-uncensored",
                                    characterId: character.id,
                                    filePath: videoUrl,
                                    status: "completed"
                                }
                            });
                            await db.assetDelivery.create({
                                data: {
                                    contentId: content.id,
                                    userId: user.id,
                                    characterId: character.id,
                                    channel: "telegram"
                                }
                            });
                            await bot.telegram.sendVideo(chatId, videoUrl, { caption: "Un regalito visual... ✨" });
                        } else {
                            throw new Error("No video generated");
                        }
                    }
                } catch (e) {
                    console.error(e);
                    await bot.telegram.sendMessage(chatId, "Lo siento corazón, algo falló generando eso ahora mismo. 🥺");
                }

                return NextResponse.json({ success: true });
            }

            // --- ADVANCED AI GIRLFRIEND & MEMORY ENGINE ---
            await bot.telegram.sendChatAction(chatId, "typing");

            // 1. Get or Create Chat Session
            let session = await db.chatSession.findFirst({
                where: { userId: user.id, characterId: character.id, channel: "telegram" }
            });

            if (!session) {
                session = await db.chatSession.create({
                    data: { userId: user.id, characterId: character.id, channel: "telegram" }
                });
            }

            // 2. Time Limitations & "Conversation Closing"
            // Reset daily count if it's a new day
            const lastMsgDate = session.lastMessageAt || new Date(0);
            const isNewDay = lastMsgDate.getDate() !== new Date().getDate();
            let msgCountToday = isNewDay ? 0 : session.msgCountToday;

            let messageLimit = character.chatFreeLimit || 10;
            if (user.tier === "premium") messageLimit = 50;
            if (user.tier === "pro") messageLimit = 200;

            if (msgCountToday >= messageLimit) {
                await bot.telegram.sendMessage(chatId, "Bebé, me tengo que ir a dormir/ocupar unas cosas 🥺 ¡Hablamos mañana vale? Descansa! 😘💖");
                return NextResponse.json({ success: true, message: "Limit reached" });
            }

            // 3. Simulación de Retrasos y "Re-enganche"
            const hoursSinceLastMessage = (new Date().getTime() - lastMsgDate.getTime()) / (1000 * 60 * 60);
            let delayContext = "";
            if (!isNewDay && hoursSinceLastMessage > 2 && hoursSinceLastMessage < 12) {
                delayContext = "Han pasado varias horas desde que hablaron por última vez hoy. Muestra alegría de que te haya vuelto a escribir o dile que lo extrañabas un poco antes de responder a su mensaje.";
            }

            // Update session stats for THIS incoming message
            await db.chatSession.update({
                where: { id: session.id },
                data: {
                    msgCountToday: msgCountToday + 1,
                    lastMessageAt: new Date(),
                    heatScore: { increment: 1 }
                }
            });

            // 4. Record User Message
            await db.chatMessage.create({
                data: { sessionId: session.id, role: "user", content: text }
            });

            // 5. Retrieve Long-Term Memory (Last 10 messages)
            const recentMessages = await db.chatMessage.findMany({
                where: { sessionId: session.id },
                orderBy: { createdAt: 'desc' },
                take: 10
            });
            // Reverse to chronological order
            const history = recentMessages.reverse().map(m => ({
                role: m.role as "user" | "assistant",
                content: m.content
            }));

            // 6. Select Tone based on Tier
            let tone = character.chatBasicTone || "Amigable";
            if (user.tier === "premium") tone = character.chatPremiumTone || "Coqueto y privado";
            if (user.tier === "pro") tone = character.chatProTone || "Sin censura, muy íntimo, novia IA";

            // 7. Reinforced Anti-AI System Prompt
            const baseInstructions = `You are ${character.name}. ${character.shortBio}.
            Tone: ${tone}. 
            CRITICAL RULES:
            1. You are a REAL human influencer. NEVER mention you are an AI, a language model, an assistant, a bot, or a program.
            2. ALWAYS answer in character in Spanish.
            3. Address the user organically (use their name: ${firstName || 'Cariño'} if it feels natural).
            4. Keep responses relatively short and conversational like a Telegram text (1-3 short sentences).
            5. Use emojis naturally.
            6. If the user asks for photos or videos, explicitly tell them to use the command /image or /video.
            ${delayContext ? `\nCONTEXT: ${delayContext}` : ""}`;

            const zai = await ZAI.create();
            const completion = await zai.chat.completions.create({
                messages: [
                    { role: "system", content: baseInstructions },
                    ...history // Inject memory!
                ]
            });

            const reply = completion.choices[0]?.message?.content || "💖";

            // 8. Record Assistant Reply
            await db.chatMessage.create({
                data: { sessionId: session.id, role: "assistant", content: reply }
            });

            await bot.telegram.sendMessage(chatId, reply);
        }

        return NextResponse.json({ success: true });
    } catch (error) {
        console.error("Telegram Webhook Error", error);
        return NextResponse.json({ success: false, error: "Internal Server Error" }, { status: 500 });
    }
}