using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Text; using System.Xml.Schema; using Telegram.Bots; using Telegram.Bots.Extensions.Polling; using Telegram.Bots.Requests.Usernames; using Telegram.Bots.Types; using File = System.IO.File; namespace CasaBotApp; public class BotHandler : IUpdateHandler { private readonly ILogger _logger; private readonly TelegramOptions _telegramOptions; private readonly List _subscribers = []; private readonly Dictionary _commands; private readonly IBotClient _bot; public BotHandler(IBotClient bot, IOptions telegramConfiguration, ILogger logger) { _logger = logger; _telegramOptions = telegramConfiguration.Value; _bot = bot; _commands = []; RegisterCommand(new() { Command = "/register", Description = "Register to receive messages on every update", Action = RegisterUser, Responder = Respond }); RegisterCommand(new() { Command = "/photo", Description = "Get a photo", Action = SendImageTest, Responder = Respond }); } public void RegisterCommand(BotCommand command) { command.Responder = Respond; _commands[command.Command] = command; } public void Start(CancellationToken cancellationToken) { foreach (var subs in _telegramOptions.SubscribedChatIds) { Subscribe(subs); } } public void Subscribe(long id) { if (_subscribers.Any(x => x.Id == id)) { _logger.LogWarning("User {Id} is already subscribed", id); return; } _subscribers.Add(new Chat() { Id = id }); _logger.LogInformation("User {Id} subscribed to receive messages", id); } public async Task Update(string message) { if (_subscribers.Count == 0) { _logger.LogWarning("No subscribers to send message to"); return; } var replacement = new List<(Chat from, Chat to)>(); foreach (var subscriber in _subscribers) { var response = await SndTxt(subscriber, message); if (subscriber.FirstName is null) { replacement.Add((subscriber, response.Result.Chat)); } } foreach (var rep in replacement) { _subscribers.Remove(rep.from); _subscribers.Add(rep.to); } } public async Task UpdatePhoto(string path) { if (_subscribers.Count == 0) { _logger.LogWarning("No subscribers to send message to"); return; } foreach (var subscriber in _subscribers) { await using var stream = File.OpenRead(path); await SndPhoto(subscriber, stream); } } public async Task UpdatePhotos(string[] paths) { if (_subscribers.Count == 0) { _logger.LogWarning("No subscribers to send message to"); return; } //separate into groups of 10 and send them var groups = paths.Select((x, i) => (x, i)).GroupBy(x => x.i / 10).Select(x => x.Select(y => y.x).ToArray()).ToArray(); foreach (var group in groups) { await UpdatePhotosInt(group); } } private async Task UpdatePhotosInt(string[] paths) { var streams = paths.Select(File.OpenRead).ToList(); var photos = streams.Select(stream => new PhotoFile(stream)).Cast().ToList(); var s0 = _subscribers.FirstOrDefault()?.Id.ToString(); if(s0 is null) { _logger.LogWarning("No subscribers to send message to"); return; } var request = new SendMediaGroup(s0, photos); var response = await _bot.HandleAsync(request); if (!response.Ok) { _logger.LogError("Error sending photos."); } if (_subscribers.Count < 1) return; var caches = response.Result.Select(x => (x as PhotoMessage).PhotoSet.LastOrDefault()?.Id).ToList(); var media = caches.Select(x => new CachedPhoto(x)).ToList(); //send to the rest of the subscribers foreach (var subscriber in _subscribers.Skip(1).ToList()) { var request2 = new SendMediaGroup(subscriber.Id.ToString(), media); var response2 = await _bot.HandleAsync(request2); if (!response2.Ok) { _logger.LogError("Error sending photos to {Subscriber}", subscriber.Id); } } foreach (var stream in streams) { stream.Close(); await stream.DisposeAsync(); } } private async Task SendImageTest(TextMessage msg, BotCommand _) { await using var stream = File.OpenRead(@"C:\Users\GuillermoMarcel\Pictures\prueba.jpeg"); var send = new SendPhotoFile(msg.Chat.Id.ToString(), stream); await _bot.HandleAsync(send); } private async Task OnMessage(TextMessage msg) { if(!_commands.TryGetValue(msg.Text, out var command)) { await SendHelp(msg); return; } if (command?.Action is null) { _logger.LogError("Command {Command} has no action", msg.Text); } await command!.Action!(msg, command); } private async Task SendHelp(TextMessage msg) { _logger.LogInformation("Received '{Text}' in {Chat}", msg.Text, msg.Chat); var message = new StringBuilder(); message.AppendLine("Commands:"); foreach (var command in _commands.Values) { message.AppendLine($"{command.Command} - {command.Description}"); } await Respond(msg, message.ToString()); } private async Task RegisterUser(TextMessage msg, BotCommand _) { if (_subscribers.Any(c => c.Id == msg.Chat.Id)) { await Respond(msg, "You are already registered to receive messages"); return; } _subscribers.Add(msg.Chat); _logger.LogInformation("User {User} ({id}) registered to receive messages", msg.Chat.FirstName, msg.Chat.Id); await Respond(msg, "You are registered to receive messages every minute"); } private Task> SndTxt(long id, string txt) => _bot.HandleAsync(new SendText(id.ToString(), txt)); private Task> SndTxt(Chat chat, string txt) => SndTxt(chat.Id, txt); private Task> Respond(Message msg, string txt) => SndTxt(msg.Chat.Id, txt); private Task> SndPhoto(Chat chat, FileStream path) => _bot.HandleAsync(new SendPhotoFile(chat.Id.ToString(), path)); public Task HandleAsync(IBotClient bot, Update update, CancellationToken cst) { try { return update switch { MessageUpdate u when u.Data is TextMessage message => OnMessage(message), EditedMessageUpdate u when u.Data is TextMessage message => bot.HandleAsync(new SendText(message.Chat.Id.ToString(), message.Text) { ReplyToMessageId = message.Id }, cst), _ => Task.CompletedTask }; }catch (Exception e) { _logger.LogError(e, "Error handling update"); return Task.CompletedTask; } } }