Files
gott/Endpoints/TelegramEndpoint.cs
Meleki dcedbaa074 refactor: Backend deduplizieren – 13 Dateien, -528 Zeilen netto
ChatContext: 3 identische Context-Klassen (PreparerContext, ReviewContext,
FinalizerContext) zu einer SubAgentContext-Klasse zusammengefasst.

ChatService: 3x40 Zeilen Sub-Agent-Setup durch BuildSubAgentContext() ersetzt.

AnthropicService: 5 Hilfsmethoden extrahiert – ExtractWebSearchSources(),
TruncateToolResult(), AddToolsToPayload(), ParseServerToolUse(), TrackUsage().

WorkspaceEndpoint: 5 Session/Agent-Endpoint-Paare durch generische Core-Handler
mit basePath-Parameter ersetzt. Ungenutzte rmdir-Endpoints entfernt.

AgentEndpoint: Doppeltes Agent-DTO-Mapping durch MapAgentList() ersetzt.

SessionEndpoint: Doppelte Session/Agent-Auflösung durch ResolvePromptContext() ersetzt.

DatabaseService: Toten Code entfernt (SetSessionTelegramChatId,
ClearSessionTelegramChatId, GetSessions, GetUserSessions). ConvertDataUrlToWebP
und ResizeDataUrlToWebP zu einer Methode zusammengefasst.

TelegramEndpoint: Bot-DTO-Mapping durch MapBotResponse() dedupliziert.

KreaEndpoint + SkillEndpoint: Thumbnail-Generierung in ImageService.GenerateThumbnail()
extrahiert, SixLabors-Imports aus Endpoints entfernt.

CronjobEndpoint: 7x ctx.Items[UserId] durch ctx.GetUserId() ersetzt.
2026-03-09 09:18:49 +01:00

309 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Text.Json;
using System.Threading.Tasks;
using Gott.Extensions;
using Gott.Services;
using Gott.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
namespace Gott.Endpoints;
public static class TelegramEndpoint
{
public static void MapTelegramEndpoints(this IEndpointRouteBuilder app)
{
// Webhook pro Bot (von Telegram aufgerufen – keine Cookie-Auth!)
app.MapPost("/api/telegram/webhook/{botId}", HandleWebhook);
// ── Telegram Bot CRUD ──
app.MapGet("/api/telegram/bots", HandleGetBots);
app.MapGet("/api/telegram/bots/available", HandleGetAvailableBots);
app.MapPost("/api/telegram/bots", HandleCreateBot);
app.MapGet("/api/telegram/bots/{botId}", HandleGetBot);
app.MapPut("/api/telegram/bots/{botId}", HandleUpdateBot);
app.MapDelete("/api/telegram/bots/{botId}", HandleDeleteBot);
// ── Webhook-Verwaltung (pro Bot) ──
app.MapPost("/api/telegram/bots/{botId}/register-webhook", HandleRegisterWebhook);
app.MapGet("/api/telegram/bots/{botId}/webhook-info", HandleWebhookInfo);
app.MapDelete("/api/telegram/bots/{botId}/webhook", HandleDeleteWebhook);
// ── Session Telegram-Config ──
app.MapGet("/api/sessions/{sessionId}/telegram", HandleGetSessionTelegram);
app.MapPut("/api/sessions/{sessionId}/telegram", HandleSaveSessionTelegram);
}
// ── Webhook Handler ──
private static async Task HandleWebhook(string botId, HttpRequest req, HttpResponse res, TelegramService telegram, DatabaseService db)
{
var bot = await db.GetTelegramBot(botId);
if (bot == null)
{
res.StatusCode = 404;
return;
}
// Secret-Token prüfen
var secret = req.Headers["X-Telegram-Bot-Api-Secret-Token"].ToString();
if (!string.IsNullOrEmpty(bot.WebhookSecret) && secret != bot.WebhookSecret)
{
res.StatusCode = 403;
return;
}
try
{
var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
_ = Task.Run(async () =>
{
try { await telegram.HandleUpdate(body, bot); }
catch (Exception ex) { Console.Error.WriteLine($"[Telegram] Fehler für Bot {bot.Name}: {ex.Message}"); }
});
}
catch
{
// Malformed JSON – ignorieren
}
res.StatusCode = 200;
}
// ── Bot CRUD ──
private static async Task<object> MapBotResponse(TelegramBotRecord bot, DatabaseService db)
{
var sessionCount = await db.GetTelegramBotSessionCount(bot.BotId);
return new
{
botId = bot.BotId,
name = bot.Name,
botToken = bot.BotToken,
webhookSecret = bot.WebhookSecret,
isPublic = bot.IsPublic,
sessionCount,
createdAt = bot.CreatedAt,
updatedAt = bot.UpdatedAt
};
}
private static async Task HandleGetBots(HttpRequest req, HttpResponse res, DatabaseService db)
{
var userId = req.HttpContext.GetUserId();
var bots = await db.GetTelegramBots(userId);
var result = new System.Collections.Generic.List<object>();
foreach (var bot in bots)
result.Add(await MapBotResponse(bot, db));
await res.WriteAsJsonAsync(result);
}
private static async Task HandleGetBot(string botId, HttpRequest req, HttpResponse res, DatabaseService db)
{
var userId = req.HttpContext.GetUserId();
var bot = await db.GetTelegramBot(botId);
if (bot == null || bot.UserId != userId)
{
res.StatusCode = 404;
await res.WriteAsJsonAsync(new { error = "Bot nicht gefunden" });
return;
}
await res.WriteAsJsonAsync(await MapBotResponse(bot, db));
}
private static async Task HandleCreateBot(HttpRequest req, HttpResponse res, DatabaseService db)
{
var userId = req.HttpContext.GetUserId();
var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
var name = body.TryGetProperty("name", out var n) ? n.GetString()?.Trim() : null;
var botToken = body.TryGetProperty("botToken", out var t) ? t.GetString()?.Trim() : null;
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(botToken))
{
res.StatusCode = 400;
await res.WriteAsJsonAsync(new { error = "Name und Bot-Token sind erforderlich" });
return;
}
var bot = await db.CreateTelegramBot(userId, name, botToken);
await res.WriteAsJsonAsync(new
{
botId = bot.BotId,
name = bot.Name,
botToken = bot.BotToken,
webhookSecret = bot.WebhookSecret,
createdAt = bot.CreatedAt
});
}
private static async Task HandleUpdateBot(string botId, HttpRequest req, HttpResponse res, DatabaseService db)
{
var userId = req.HttpContext.GetUserId();
var bot = await db.GetTelegramBot(botId);
if (bot == null || bot.UserId != userId)
{
res.StatusCode = 404;
await res.WriteAsJsonAsync(new { error = "Bot nicht gefunden" });
return;
}
var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
var name = body.TryGetProperty("name", out var n) ? n.GetString()?.Trim() : bot.Name;
var botToken = body.TryGetProperty("botToken", out var t) ? t.GetString()?.Trim() : bot.BotToken;
var isPublic = body.TryGetProperty("isPublic", out var p) ? p.GetBoolean() : bot.IsPublic;
await db.UpdateTelegramBot(botId, name, botToken, isPublic);
await res.WriteAsJsonAsync(new { ok = true });
}
private static async Task HandleDeleteBot(string botId, HttpRequest req, HttpResponse res, DatabaseService db, TelegramService telegram)
{
var userId = req.HttpContext.GetUserId();
var bot = await db.GetTelegramBot(botId);
if (bot == null || bot.UserId != userId)
{
res.StatusCode = 404;
await res.WriteAsJsonAsync(new { error = "Bot nicht gefunden" });
return;
}
// Webhook bei Telegram löschen
try { await telegram.DeleteWebhook(bot); } catch { /* ignore */ }
await db.DeleteTelegramBot(botId);
await res.WriteAsJsonAsync(new { ok = true });
}
// ── Webhook-Verwaltung ──
private static async Task HandleRegisterWebhook(string botId, HttpRequest req, HttpResponse res, DatabaseService db, TelegramService telegram)
{
var userId = req.HttpContext.GetUserId();
var bot = await db.GetTelegramBot(botId);
if (bot == null || bot.UserId != userId)
{
res.StatusCode = 404;
await res.WriteAsJsonAsync(new { error = "Bot nicht gefunden" });
return;
}
var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
if (!body.TryGetProperty("baseUrl", out var urlEl))
{
res.StatusCode = 400;
await res.WriteAsJsonAsync(new { error = "baseUrl required" });
return;
}
var result = await telegram.RegisterWebhook(urlEl.GetString(), bot);
res.ContentType = "application/json";
await res.WriteAsync(result);
}
private static async Task HandleWebhookInfo(string botId, HttpRequest req, HttpResponse res, DatabaseService db, TelegramService telegram)
{
var userId = req.HttpContext.GetUserId();
var bot = await db.GetTelegramBot(botId);
if (bot == null || bot.UserId != userId)
{
res.StatusCode = 404;
return;
}
var result = await telegram.GetWebhookInfo(bot);
res.ContentType = "application/json";
await res.WriteAsync(result);
}
private static async Task HandleDeleteWebhook(string botId, HttpRequest req, HttpResponse res, DatabaseService db, TelegramService telegram)
{
var userId = req.HttpContext.GetUserId();
var bot = await db.GetTelegramBot(botId);
if (bot == null || bot.UserId != userId)
{
res.StatusCode = 404;
return;
}
var result = await telegram.DeleteWebhook(bot);
res.ContentType = "application/json";
await res.WriteAsync(result);
}
// ── Verfügbare Bots (eigene + public) ──
private static async Task HandleGetAvailableBots(HttpRequest req, HttpResponse res, DatabaseService db)
{
var userId = req.HttpContext.GetUserId();
var bots = await db.GetAvailableTelegramBots(userId);
var result = new System.Collections.Generic.List<object>();
foreach (var bot in bots)
{
var isOwner = bot.UserId == userId;
var sessionCount = await db.GetTelegramBotSessionCount(bot.BotId);
result.Add(new
{
botId = bot.BotId,
name = bot.Name,
isOwner,
isPublic = bot.IsPublic,
sessionCount,
createdAt = bot.CreatedAt,
updatedAt = bot.UpdatedAt
});
}
await res.WriteAsJsonAsync(result);
}
// ── Session Telegram-Config ──
private static async Task HandleGetSessionTelegram(string sessionId, HttpRequest req, HttpResponse res, DatabaseService db)
{
var userId = req.HttpContext.GetUserId();
var session = await db.GetSession(sessionId);
if (session == null || session.UserId != userId)
{
res.StatusCode = 404;
await res.WriteAsJsonAsync(new { error = "Session nicht gefunden" });
return;
}
await res.WriteAsJsonAsync(new
{
telegramBotId = session.TelegramBotId ?? "",
chatId = session.TelegramChatId ?? "",
configured = !string.IsNullOrEmpty(session.TelegramBotId)
});
}
private static async Task HandleSaveSessionTelegram(string sessionId, HttpRequest req, HttpResponse res, DatabaseService db)
{
var userId = req.HttpContext.GetUserId();
var session = await db.GetSession(sessionId);
if (session == null || session.UserId != userId)
{
res.StatusCode = 404;
await res.WriteAsJsonAsync(new { error = "Session nicht gefunden" });
return;
}
var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
var botId = body.TryGetProperty("telegramBotId", out var b) ? b.GetString() : null;
var chatId = body.TryGetProperty("chatId", out var c) ? c.GetString() : session.TelegramChatId;
// Bot-Zuordnung setzen
if (botId != null)
{
await db.SetSessionTelegramBot(sessionId, string.IsNullOrEmpty(botId) ? null : botId);
}
// Chat-ID aktualisieren (leerer String → NULL)
await db.UpdateSessionTelegramChat(sessionId, string.IsNullOrEmpty(chatId) ? null : chatId);
await res.WriteAsJsonAsync(new { ok = true });
}
}