feat: Telegram Bot Integration

- TelegramService: Non-Streaming Claude Chat mit Tool-Loop,
  Commands (/start, /link, /session, /new, /clear, /status, /unlink),
  Nachrichten-Splitting bei >4096 Zeichen, Typing-Indicator,
  History-Aufbau aus DB, Token-Usage-Tracking
- TelegramEndpoint: Webhook (POST, no-auth), Link-Code-Generierung
  (POST, auth), Webhook-Registrierung + Info (Admin)
- DatabaseService: telegram_links Tabelle mit Link-Code-Flow
  (6-stelliger Code, 5 Min Ablauf), GetUserSessions
- AuthMiddleware: /api/telegram/webhook von Auth ausgenommen
- Session-Verknüpfung via Link-Code (Web-UI generiert Code,
  Telegram /link CODE löst ein)
- TelegramLinkRecord Model
This commit is contained in:
Meleki committed 2026-03-03 06:30:05 +01:00
1 parent 81714b1f4c
commit 0dd7c38751
7 files changed
+1108 -5

No files matched your search

+131
View File
@@ -0,0 +1,131 @@
using System;
using System.Text.Json;
using System.Threading.Tasks;
using Gott.Extensions;
using Gott.Services;
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 (von Telegram aufgerufen – keine Cookie-Auth!)
app.MapPost("/api/telegram/webhook", HandleWebhook);
// Link-Code generieren (aus Web-UI, braucht Auth)
app.MapPost("/api/telegram/link", HandleCreateLink);
// Webhook registrieren (Admin)
app.MapPost("/api/telegram/register-webhook", HandleRegisterWebhook);
// Webhook-Info (Admin)
app.MapGet("/api/telegram/webhook-info", HandleWebhookInfo);
}
private static async Task HandleWebhook(HttpRequest req, HttpResponse res, TelegramService telegram)
{
if (!telegram.IsConfigured)
{
res.StatusCode = 404;
return;
}
// Secret-Token prüfen
var secret = req.Headers["X-Telegram-Bot-Api-Secret-Token"].ToString();
if (secret != telegram.WebhookSecret)
{
res.StatusCode = 403;
return;
}
try
{
var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
// Fire-and-forget – Telegram erwartet schnelle 200 OK Antwort
_ = Task.Run(async () =>
{
try { await telegram.HandleUpdate(body); }
catch (Exception ex) { Console.Error.WriteLine($"[Telegram] Fehler: {ex.Message}"); }
});
}
catch
{
// Malformed JSON – ignorieren
}
res.StatusCode = 200;
}
private static async Task HandleCreateLink(HttpRequest req, HttpResponse res, DatabaseService db, TelegramService telegram)
{
if (!telegram.IsConfigured)
{
res.StatusCode = 400;
await res.WriteAsJsonAsync(new { error = "Telegram Bot nicht konfiguriert" });
return;
}
var userId = req.HttpContext.GetUserId();
var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
if (!body.TryGetProperty("sessionId", out var sidEl))
{
res.StatusCode = 400;
await res.WriteAsJsonAsync(new { error = "sessionId required" });
return;
}
var sessionId = sidEl.GetString();
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 agentId = session.AgentId;
var code = await db.CreateTelegramLinkCode(userId, sessionId, agentId);
await res.WriteAsJsonAsync(new { code, expiresInSeconds = 300 });
}
private static async Task HandleRegisterWebhook(HttpRequest req, HttpResponse res, TelegramService telegram)
{
if (!req.HttpContext.IsAdmin())
{
res.StatusCode = 403;
await res.WriteAsJsonAsync(new { error = "Admin required" });
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());
await res.WriteAsJsonAsync(new { result });
}
private static async Task HandleWebhookInfo(HttpRequest req, HttpResponse res, TelegramService telegram)
{
if (!req.HttpContext.IsAdmin())
{
res.StatusCode = 403;
await res.WriteAsJsonAsync(new { error = "Admin required" });
return;
}
var result = await telegram.GetWebhookInfo();
res.ContentType = "application/json";
await res.WriteAsync(result);
}
}