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.
309 lines
11 KiB
C#
309 lines
11 KiB
C#
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 });
|
||
}
|
||
}
|