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:
1 parent
81714b1f4c
commit
0dd7c38751
7 files changed
+1108
-5
No files matched your search
@@ -183,6 +183,22 @@ public class DatabaseService
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
");
|
||||
|
||||
conn.Execute(@"
|
||||
CREATE TABLE IF NOT EXISTS telegram_links (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
telegram_chat_id INTEGER NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
session_id TEXT NOT NULL,
|
||||
agent_id TEXT,
|
||||
link_code TEXT,
|
||||
expires_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
UNIQUE(telegram_chat_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_telegram_links_code ON telegram_links(link_code);
|
||||
");
|
||||
}
|
||||
|
||||
private void TryAddColumn(SqliteConnection conn, string table, string column, string type)
|
||||
@@ -742,4 +758,87 @@ public class DatabaseService
|
||||
"UPDATE users SET display_name = @displayName, persona = @persona, updated_at = @now WHERE user_id = @userId",
|
||||
new { userId, displayName, persona, now = DateTime.UtcNow.ToString("o") });
|
||||
}
|
||||
|
||||
// ── Telegram ──
|
||||
|
||||
public async Task<string> CreateTelegramLinkCode(string userId, string sessionId, string agentId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
var code = Guid.NewGuid().ToString("N")[..6].ToUpperInvariant();
|
||||
var now = DateTime.UtcNow.ToString("o");
|
||||
var expires = DateTime.UtcNow.AddMinutes(5).ToString("o");
|
||||
|
||||
// Alten Code für diese Session löschen
|
||||
await conn.ExecuteAsync(
|
||||
"DELETE FROM telegram_links WHERE link_code IS NOT NULL AND user_id = @userId AND session_id = @sessionId",
|
||||
new { userId, sessionId });
|
||||
|
||||
// Neuen Pending-Link-Code erstellen (noch ohne telegram_chat_id)
|
||||
await conn.ExecuteAsync(@"
|
||||
INSERT INTO telegram_links (telegram_chat_id, user_id, session_id, agent_id, link_code, expires_at, created_at, updated_at)
|
||||
VALUES (0, @userId, @sessionId, @agentId, @code, @expires, @now, @now)",
|
||||
new { userId, sessionId, agentId, code, expires, now });
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
public async Task<TelegramLinkRecord> RedeemTelegramLinkCode(string code, long chatId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
var now = DateTime.UtcNow.ToString("o");
|
||||
|
||||
var link = await conn.QuerySingleOrDefaultAsync<TelegramLinkRecord>(
|
||||
"SELECT * FROM telegram_links WHERE link_code = @code AND telegram_chat_id = 0",
|
||||
new { code });
|
||||
|
||||
if (link == null) return null;
|
||||
if (string.Compare(link.ExpiresAt, now, StringComparison.Ordinal) < 0) return null;
|
||||
|
||||
// Bestehende Verknüpfung für diese chatId entfernen
|
||||
await conn.ExecuteAsync(
|
||||
"DELETE FROM telegram_links WHERE telegram_chat_id = @chatId",
|
||||
new { chatId });
|
||||
|
||||
// Pending-Eintrag aktivieren
|
||||
await conn.ExecuteAsync(@"
|
||||
UPDATE telegram_links SET telegram_chat_id = @chatId, link_code = NULL, expires_at = NULL, updated_at = @now
|
||||
WHERE id = @id",
|
||||
new { chatId, now, id = link.Id });
|
||||
|
||||
link.TelegramChatId = chatId;
|
||||
link.LinkCode = null;
|
||||
return link;
|
||||
}
|
||||
|
||||
public async Task<TelegramLinkRecord> GetTelegramLink(long chatId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
return await conn.QuerySingleOrDefaultAsync<TelegramLinkRecord>(
|
||||
"SELECT * FROM telegram_links WHERE telegram_chat_id = @chatId",
|
||||
new { chatId });
|
||||
}
|
||||
|
||||
public async Task UpdateTelegramSession(long chatId, string sessionId, string agentId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
await conn.ExecuteAsync(
|
||||
"UPDATE telegram_links SET session_id = @sessionId, agent_id = @agentId, updated_at = @now WHERE telegram_chat_id = @chatId",
|
||||
new { chatId, sessionId, agentId, now = DateTime.UtcNow.ToString("o") });
|
||||
}
|
||||
|
||||
public async Task DeleteTelegramLink(long chatId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
await conn.ExecuteAsync(
|
||||
"DELETE FROM telegram_links WHERE telegram_chat_id = @chatId",
|
||||
new { chatId });
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SessionRecord>> GetUserSessions(string userId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
return await conn.QueryAsync<SessionRecord>(
|
||||
"SELECT * FROM sessions WHERE user_id = @userId AND is_deleted = 0 ORDER BY updated_at DESC",
|
||||
new { userId });
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user