using Luna.Channels.Abstractions; using Luna.Configuration; using Luna.Shared; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Telegram.Bot; using Telegram.Bot.Polling; using Telegram.Bot.Types; namespace Luna.Channels.Telegram; public class TelegramAdapter( IChannelManager channelManager, ITelegramBotClient botClient, TelegramOptions options, ILogger adapterLogger, ILogger channelLogger) : IHostedService, IUpdateHandler { private readonly Dictionary channels = new(); private CancellationTokenSource? cts; public Task StartAsync(CancellationToken cancellationToken) { cts = new CancellationTokenSource(); botClient.StartReceiving(this, new ReceiverOptions(), cts.Token); adapterLogger.LogInformation("Telegram adapter started polling"); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { cts?.Cancel(); cts?.Dispose(); foreach (var (_, channel) in channels) { channelManager.UnregisterChannel(channel.ChannelId); channel.Dispose(); } channels.Clear(); adapterLogger.LogInformation("Telegram adapter stopped"); return Task.CompletedTask; } public async Task HandleUpdateAsync(ITelegramBotClient client, Update update, CancellationToken cancellationToken) { if (update.Message is not { } message) return; if (message.Text is null or "") return; var chat = message.Chat; var channel = GetOrCreateChannel(chat.Id); if (message.Text.StartsWith('/')) { await HandleCommandAsync(channel, message, cancellationToken); return; } var channelMessage = new ChannelMessage( MessageId: message.Id.ToString(), ConversationId: chat.Id.ToString(), SenderId: message.From?.Id.ToString() ?? "unknown", SenderName: message.From?.FirstName ?? "Unknown", Content: message.Text, Timestamp: new DateTimeOffset(message.Date, TimeSpan.Zero)); channel.RaiseMessageReceived(channelMessage); } public Task HandleErrorAsync(ITelegramBotClient client, Exception exception, HandleErrorSource source, CancellationToken cancellationToken) { adapterLogger.LogError(exception, "Telegram polling error from {Source}", source); return Task.CompletedTask; } private TelegramChannel GetOrCreateChannel(long chatId) { if (channels.TryGetValue(chatId, out var existing)) return existing; var channel = new TelegramChannel( chatId.ToString(), botClient, channelLogger, options); channels[chatId] = channel; channelManager.RegisterChannel(channel); adapterLogger.LogInformation("Created TelegramChannel for chat {ChatId}", chatId); return channel; } private async Task HandleCommandAsync(TelegramChannel channel, Message message, CancellationToken cancellationToken) { var command = message.Text!.Split(' ')[0].ToLowerInvariant(); var responseText = command switch { "/start" => "Welcome to Luna AI Assistant! Send me a message and I'll do my best to help.", "/help" => "Luna AI Assistant\n\nCommands:\n/start - Start the bot\n/help - Show this help message\n\nJust send a message to chat with Luna!", _ => null }; if (responseText is null) return; var responseMessage = new ChannelMessage( MessageId: Guid.NewGuid().ToString(), ConversationId: message.Chat.Id.ToString(), SenderId: "luna", SenderName: "Luna", Content: responseText, Timestamp: DateTimeOffset.UtcNow); await channel.SendMessageAsync(responseMessage, cancellationToken); } }