2025-02-12 19:15:20 -03:00
|
|
|
using Microsoft.Extensions.Logging;
|
2025-02-14 20:32:04 -03:00
|
|
|
using Microsoft.Extensions.Options;
|
2025-03-26 15:41:39 -03:00
|
|
|
using System.Text;
|
2025-02-19 12:54:09 -03:00
|
|
|
using System.Xml.Schema;
|
2025-02-18 13:06:49 -03:00
|
|
|
using Telegram.Bots;
|
|
|
|
using Telegram.Bots.Extensions.Polling;
|
|
|
|
using Telegram.Bots.Requests.Usernames;
|
|
|
|
using Telegram.Bots.Types;
|
|
|
|
using File = System.IO.File;
|
2025-02-12 19:15:20 -03:00
|
|
|
|
|
|
|
namespace CasaBotApp;
|
|
|
|
|
2025-02-18 13:06:49 -03:00
|
|
|
public class BotHandler : IUpdateHandler
|
2025-02-12 19:15:20 -03:00
|
|
|
{
|
|
|
|
private readonly ILogger<BotHandler> _logger;
|
2025-02-14 20:32:04 -03:00
|
|
|
private readonly TelegramOptions _telegramOptions;
|
2025-02-18 13:06:49 -03:00
|
|
|
private readonly List<Chat> _subscribers = [];
|
2025-03-26 15:41:39 -03:00
|
|
|
private readonly Dictionary<string, BotCommand> _commands;
|
2025-03-28 22:44:53 -03:00
|
|
|
|
|
|
|
public Func<TextMessage, Task>? OnReply { get; set; } = null;
|
2025-02-12 19:15:20 -03:00
|
|
|
|
2025-02-18 13:06:49 -03:00
|
|
|
private readonly IBotClient _bot;
|
2025-02-12 19:15:20 -03:00
|
|
|
|
2025-02-18 13:06:49 -03:00
|
|
|
public BotHandler(IBotClient bot, IOptions<TelegramOptions> telegramConfiguration, ILogger<BotHandler> logger)
|
2025-02-12 19:15:20 -03:00
|
|
|
{
|
|
|
|
_logger = logger;
|
2025-02-14 20:32:04 -03:00
|
|
|
_telegramOptions = telegramConfiguration.Value;
|
2025-02-18 13:06:49 -03:00
|
|
|
_bot = bot;
|
2025-03-26 15:41:39 -03:00
|
|
|
_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;
|
2025-02-12 19:15:20 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Start(CancellationToken cancellationToken)
|
|
|
|
{
|
2025-02-18 13:06:49 -03:00
|
|
|
foreach (var subs in _telegramOptions.SubscribedChatIds)
|
2025-02-17 22:20:51 -03:00
|
|
|
{
|
2025-02-18 13:06:49 -03:00
|
|
|
Subscribe(subs);
|
2025-02-17 22:20:51 -03:00
|
|
|
}
|
2025-02-12 19:15:20 -03:00
|
|
|
}
|
2025-02-18 13:06:49 -03:00
|
|
|
|
|
|
|
public void Subscribe(long id)
|
2025-02-12 19:15:20 -03:00
|
|
|
{
|
2025-02-18 13:06:49 -03:00
|
|
|
if (_subscribers.Any(x => x.Id == id))
|
2025-02-12 19:15:20 -03:00
|
|
|
{
|
2025-02-18 13:06:49 -03:00
|
|
|
_logger.LogWarning("User {Id} is already subscribed", id);
|
2025-02-12 19:15:20 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-02-18 13:06:49 -03:00
|
|
|
_subscribers.Add(new Chat()
|
|
|
|
{
|
|
|
|
Id = id
|
|
|
|
});
|
|
|
|
_logger.LogInformation("User {Id} subscribed to receive messages", id);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task Update(string message)
|
|
|
|
{
|
2025-02-12 19:15:20 -03:00
|
|
|
if (_subscribers.Count == 0)
|
|
|
|
{
|
|
|
|
_logger.LogWarning("No subscribers to send message to");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-02-18 13:06:49 -03:00
|
|
|
var replacement = new List<(Chat from, Chat to)>();
|
2025-02-12 19:15:20 -03:00
|
|
|
foreach (var subscriber in _subscribers)
|
|
|
|
{
|
2025-02-18 13:06:49 -03:00
|
|
|
var response = await SndTxt(subscriber, message);
|
|
|
|
if (subscriber.FirstName is null)
|
|
|
|
{
|
|
|
|
replacement.Add((subscriber, response.Result.Chat));
|
|
|
|
}
|
2025-02-12 19:15:20 -03:00
|
|
|
}
|
2025-02-18 13:06:49 -03:00
|
|
|
|
|
|
|
foreach (var rep in replacement)
|
2025-02-13 19:12:46 -03:00
|
|
|
{
|
2025-02-18 13:06:49 -03:00
|
|
|
_subscribers.Remove(rep.from);
|
|
|
|
_subscribers.Add(rep.to);
|
2025-02-13 19:12:46 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-18 13:06:49 -03:00
|
|
|
|
|
|
|
|
2025-02-13 19:12:46 -03:00
|
|
|
public async Task UpdatePhoto(string path)
|
|
|
|
{
|
2025-02-18 13:06:49 -03:00
|
|
|
|
2025-02-13 19:12:46 -03:00
|
|
|
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);
|
2025-02-18 13:06:49 -03:00
|
|
|
await SndPhoto(subscriber, stream);
|
2025-02-13 19:12:46 -03:00
|
|
|
}
|
|
|
|
}
|
2025-03-28 22:44:53 -03:00
|
|
|
|
|
|
|
public async Task SendVideo(long chatId, string path)
|
|
|
|
{
|
|
|
|
_logger.LogInformation("Sending video to {ChatId}", chatId);
|
|
|
|
await using var stream = File.OpenRead(path);
|
|
|
|
var send = new SendVideoFile(chatId.ToString(), stream);
|
|
|
|
var response = await _bot.HandleAsync(send);
|
|
|
|
if (!response.Ok)
|
|
|
|
{
|
|
|
|
_logger.LogError("Error sending video.");
|
|
|
|
}
|
|
|
|
_logger.LogInformation("Video sent to {ChatId}", chatId);
|
|
|
|
stream.Close();
|
|
|
|
}
|
2025-02-18 19:21:19 -03:00
|
|
|
|
|
|
|
public async Task UpdatePhotos(string[] paths)
|
|
|
|
{
|
|
|
|
if (_subscribers.Count == 0)
|
|
|
|
{
|
|
|
|
_logger.LogWarning("No subscribers to send message to");
|
|
|
|
return;
|
|
|
|
}
|
2025-02-19 12:54:09 -03:00
|
|
|
|
|
|
|
//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);
|
|
|
|
}
|
|
|
|
}
|
2025-02-18 19:21:19 -03:00
|
|
|
|
2025-02-19 12:54:09 -03:00
|
|
|
private async Task UpdatePhotosInt(string[] paths)
|
|
|
|
{
|
2025-02-18 19:21:19 -03:00
|
|
|
var streams = paths.Select(File.OpenRead).ToList();
|
2025-03-28 22:44:53 -03:00
|
|
|
var photos = streams.Select(stream => new PhotoFile(stream)
|
|
|
|
{
|
|
|
|
Caption = Path.GetFileNameWithoutExtension(stream.Name)
|
|
|
|
}).Cast<IGroupableMedia>().ToList();
|
2025-02-18 19:21:19 -03:00
|
|
|
|
2025-02-19 12:54:09 -03:00
|
|
|
var s0 = _subscribers.FirstOrDefault()?.Id.ToString();
|
|
|
|
if(s0 is null)
|
2025-02-18 19:21:19 -03:00
|
|
|
{
|
2025-02-19 12:54:09 -03:00
|
|
|
_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);
|
|
|
|
}
|
2025-02-18 19:21:19 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var stream in streams)
|
|
|
|
{
|
|
|
|
stream.Close();
|
|
|
|
await stream.DisposeAsync();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-26 15:41:39 -03:00
|
|
|
private async Task SendImageTest(TextMessage msg, BotCommand _)
|
2025-02-12 19:15:20 -03:00
|
|
|
{
|
|
|
|
await using var stream = File.OpenRead(@"C:\Users\GuillermoMarcel\Pictures\prueba.jpeg");
|
2025-02-12 19:22:18 -03:00
|
|
|
|
2025-03-26 15:41:39 -03:00
|
|
|
var send = new SendPhotoFile(msg.Chat.Id.ToString(), stream);
|
2025-02-18 13:06:49 -03:00
|
|
|
await _bot.HandleAsync(send);
|
2025-02-12 19:15:20 -03:00
|
|
|
}
|
|
|
|
|
2025-03-28 22:44:53 -03:00
|
|
|
/// <summary>
|
|
|
|
/// Send photos to a specific chat
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="chatId"></param>
|
|
|
|
/// <param name="paths"></param>
|
|
|
|
public async Task SendPhotos(long chatId, string[] paths)
|
|
|
|
{
|
|
|
|
var streams = paths.Select(File.OpenRead).ToArray();
|
|
|
|
var photos = streams.Select(stream => new PhotoFile(stream)
|
|
|
|
{
|
|
|
|
Caption = Path.GetFileNameWithoutExtension(stream.Name)
|
|
|
|
}).Cast<IGroupableMedia>().ToArray();
|
|
|
|
|
|
|
|
var request = new SendMediaGroup(chatId.ToString(), photos);
|
|
|
|
var response = await _bot.HandleAsync(request);
|
|
|
|
if (!response.Ok)
|
|
|
|
{
|
|
|
|
_logger.LogError("Error sending photos.");
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var stream in streams)
|
|
|
|
{
|
|
|
|
stream.Close();
|
|
|
|
await stream.DisposeAsync();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-12 19:15:20 -03:00
|
|
|
|
2025-02-12 19:22:18 -03:00
|
|
|
|
2025-02-18 13:06:49 -03:00
|
|
|
private async Task OnMessage(TextMessage msg)
|
2025-02-12 19:15:20 -03:00
|
|
|
{
|
2025-03-26 15:41:39 -03:00
|
|
|
if(!_commands.TryGetValue(msg.Text, out var command))
|
|
|
|
{
|
2025-03-28 22:44:53 -03:00
|
|
|
if (msg.ReplyToMessage != null && OnReply is not null)
|
|
|
|
{
|
|
|
|
await OnReply(msg);
|
|
|
|
return;
|
|
|
|
}
|
2025-03-26 15:41:39 -03:00
|
|
|
await SendHelp(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (command?.Action is null)
|
|
|
|
{
|
|
|
|
_logger.LogError("Command {Command} has no action", msg.Text);
|
2025-02-12 19:15:20 -03:00
|
|
|
}
|
2025-03-26 15:41:39 -03:00
|
|
|
|
|
|
|
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");
|
2025-02-12 19:15:20 -03:00
|
|
|
}
|
|
|
|
|
2025-02-18 13:06:49 -03:00
|
|
|
|
|
|
|
private Task<Response<TextMessage>> SndTxt(long id, string txt) => _bot.HandleAsync(new SendText(id.ToString(), txt));
|
|
|
|
private Task<Response<TextMessage>> SndTxt(Chat chat, string txt) => SndTxt(chat.Id, txt);
|
|
|
|
private Task<Response<TextMessage>> Respond(Message msg, string txt) => SndTxt(msg.Chat.Id, txt);
|
|
|
|
private Task<Response<PhotoMessage>> SndPhoto(Chat chat, FileStream path) =>
|
|
|
|
_bot.HandleAsync(new SendPhotoFile(chat.Id.ToString(), path));
|
|
|
|
|
|
|
|
|
|
|
|
public Task HandleAsync(IBotClient bot, Update update, CancellationToken cst)
|
2025-02-12 19:15:20 -03:00
|
|
|
{
|
2025-02-18 13:06:49 -03:00
|
|
|
try
|
2025-02-12 19:15:20 -03:00
|
|
|
{
|
2025-02-18 13:06:49 -03:00
|
|
|
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)
|
2025-02-12 19:15:20 -03:00
|
|
|
{
|
2025-02-18 13:06:49 -03:00
|
|
|
_logger.LogError(e, "Error handling update");
|
|
|
|
return Task.CompletedTask;
|
|
|
|
}
|
2025-02-12 19:15:20 -03:00
|
|
|
}
|
2025-02-18 13:06:49 -03:00
|
|
|
|
2025-02-12 19:15:20 -03:00
|
|
|
}
|