Files
gott/Endpoints/TelegramEndpoint.cs
T
Meleki ec73ccd71e Telegram Bots als eigenständige Entität mit eigener Page
- Neue Tabelle 'telegram_bots' (bot_id, name, bot_token, webhook_secret, is_public)
- Automatische Migration bestehender Session-Telegram-Configs in telegram_bots
- Sessions verweisen via telegram_bot_id auf Bot (mehrere Sessions pro Bot möglich)
- Webhook-URL: /api/telegram/webhook/{botId} statt {sessionId}
- Eigene Telegram-Bot-Page (wie Agent/Cronjob/Session) statt Modal
- Bot-Page: Name, Token, Secret, Webhook-Verwaltung (registrieren/status/entfernen), Löschen
- Dashboard: Telegram Bots Section mit Cards (Name, maskierter Token, Session-Count)
- Session-Page: Bot-Picker (wie Agent-Picker) statt Token-Input, Chat-ID readonly + Unlink-Button
- Public-Flag: Bot kann von anderen Usern in ihren Sessions ausgewählt werden
- Vereinfacht: 1 Session = max. 1 Telegram Chat, allowed_chat_ids entfällt
- /start zeigt Chat-ID an, /unlink entfernt (Unlink nur über Web-UI)
- Nachrichten-Routing: Bot + Chat-ID → Session Lookup
2026-03-08 19:27:05 +01:00

315 lines
11 KiB
C#
Raw 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 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 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)
{
var sessionCount = await db.GetTelegramBotSessionCount(bot.BotId);
result.Add(new
{
botId = bot.BotId,
name = bot.Name,
botToken = bot.BotToken,
webhookSecret = bot.WebhookSecret,
isPublic = bot.IsPublic,
sessionCount,
createdAt = bot.CreatedAt,
updatedAt = bot.UpdatedAt
});
}
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;
}
var sessionCount = await db.GetTelegramBotSessionCount(bot.BotId);
await res.WriteAsJsonAsync(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 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;
result.Add(new
{
botId = bot.BotId,
name = bot.Name,
isOwner,
isPublic = bot.IsPublic,
createdAt = bot.CreatedAt,
// Token nur für eigene Bots anzeigen
botToken = isOwner ? bot.BotToken : null
});
}
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 });
}
}