refactor: change dependency to use dotnet runtime instead of aspnet

This commit is contained in:
Guillermo Marcel 2025-02-18 13:06:49 -03:00
parent ba51677d98
commit ea02a726ef
4 changed files with 143 additions and 194 deletions

View File

@ -1,89 +1,81 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Telegram.Bot; using Telegram.Bots;
using Telegram.Bot.Polling; using Telegram.Bots.Extensions.Polling;
using Telegram.Bot.Types; using Telegram.Bots.Requests.Usernames;
using Telegram.Bot.Types.Enums; using Telegram.Bots.Types;
using Telegram.Bot.Types.ReplyMarkups; using File = System.IO.File;
namespace CasaBotApp; namespace CasaBotApp;
public class BotHandler public class BotHandler : IUpdateHandler
{ {
private readonly ILogger<BotHandler> _logger; private readonly ILogger<BotHandler> _logger;
private readonly TelegramOptions _telegramOptions; private readonly TelegramOptions _telegramOptions;
private readonly List<ChatId> _subscribers = []; private readonly List<Chat> _subscribers = [];
private TelegramBotClient? _bot; private readonly IBotClient _bot;
public BotHandler(IBotClient bot, IOptions<TelegramOptions> telegramConfiguration, ILogger<BotHandler> logger)
public BotHandler(IOptions<TelegramOptions> telegramConfiguration, ILogger<BotHandler> logger)
{ {
_logger = logger; _logger = logger;
if (string.IsNullOrEmpty(telegramConfiguration.Value.BotToken))
{
_logger.LogError("Bot token is not provided");
throw new ArgumentException("Bot token is required", nameof(telegramConfiguration));
}
_telegramOptions = telegramConfiguration.Value; _telegramOptions = telegramConfiguration.Value;
_bot = bot;
} }
public void Start(CancellationToken cancellationToken) public void Start(CancellationToken cancellationToken)
{ {
_logger.LogInformation("Starting bot..."); foreach (var subs in _telegramOptions.SubscribedChatIds)
if (string.IsNullOrEmpty(_telegramOptions.BotToken))
{ {
_logger.LogError("Bot token is not provided"); Subscribe(subs);
return;
} }
_bot = new TelegramBotClient(_telegramOptions.BotToken, cancellationToken: cancellationToken);
_bot.OnError += OnError;
_bot.OnMessage += OnMessage;
_bot.OnUpdate += OnUpdate;
} }
public void Update(string message) public void Subscribe(long id)
{ {
if (_bot is null) if (_subscribers.Any(x => x.Id == id))
{ {
_logger.LogWarning("Bot is not initialized yet"); _logger.LogWarning("User {Id} is already subscribed", id);
return; 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) if (_subscribers.Count == 0)
{ {
_logger.LogWarning("No subscribers to send message to"); _logger.LogWarning("No subscribers to send message to");
return; return;
} }
var replacement = new List<(Chat from, Chat to)>();
foreach (var subscriber in _subscribers) foreach (var subscriber in _subscribers)
{ {
_bot.SendMessage(subscriber, message); var response = await SndTxt(subscriber, message);
if (subscriber.FirstName is null)
{
replacement.Add((subscriber, response.Result.Chat));
} }
} }
public void Subscribe(long id) foreach (var rep in replacement)
{ {
if (_subscribers.Any(s => s.Identifier == id)) _subscribers.Remove(rep.from);
{ _subscribers.Add(rep.to);
_logger.LogWarning("User {Id} is already subscribed", id); }
return;
} }
_subscribers.Add(new ChatId(id));
_logger.LogInformation("User {Id} subscribed to receive messages", id);
}
public async Task UpdatePhoto(string path) public async Task UpdatePhoto(string path)
{ {
if (_bot is null)
{
_logger.LogWarning("Bot is not initialized yet");
return;
}
if (_subscribers.Count == 0) if (_subscribers.Count == 0)
{ {
@ -94,84 +86,85 @@ public class BotHandler
foreach (var subscriber in _subscribers) foreach (var subscriber in _subscribers)
{ {
await using var stream = File.OpenRead(path); await using var stream = File.OpenRead(path);
var inputFile = InputFile.FromStream(stream, Path.GetFileName(path)); await SndPhoto(subscriber, stream);
await _bot.SendPhoto(subscriber, inputFile, "Detected object");
} }
} }
private async Task SendImageTest(long id) private async Task SendImageTest(long id)
{ {
if (_bot is null)
{
_logger.LogWarning("Bot is not initialized yet");
return;
}
await using var stream = File.OpenRead(@"C:\Users\GuillermoMarcel\Pictures\prueba.jpeg"); await using var stream = File.OpenRead(@"C:\Users\GuillermoMarcel\Pictures\prueba.jpeg");
var inputFile = InputFile.FromStream(stream, "gorda.jpeg");
await _bot.SendPhoto(new ChatId(id), inputFile, "mi gorda"); var send = new SendPhotoFile(id.ToString(), stream);
} await _bot.HandleAsync(send);
// method to handle errors in polling or in your OnMessage/OnUpdate code
private Task OnError(Exception exception, HandleErrorSource source)
{
_logger.LogError(exception, "Error in {Source}", source);
return Task.CompletedTask;
} }
private async Task OnMessage(Message msg, UpdateType type)
private async Task OnMessage(TextMessage msg)
{ {
if (_bot is null)
{
_logger.LogWarning("Bot is not initialized yet");
return;
}
switch (msg.Text) switch (msg.Text)
{ {
case "/start":
await _bot.SendMessage(msg.Chat, "Welcome! Pick one direction",
replyMarkup: new InlineKeyboardMarkup().AddButtons("Left", "Right"));
return;
case "/register": case "/register":
if (_subscribers.Any(s => s.Identifier == msg.Chat.Id)) if (_subscribers.Any(c => c.Id == msg.Chat.Id))
{ {
await _bot.SendMessage(msg.Chat, "You are already registered to receive messages"); await Respond(msg, "You are already registered to receive messages");
return; return;
} }
_subscribers.Add(msg.Chat); _subscribers.Add(msg.Chat);
_logger.LogInformation("User {User} registered to receive messages", msg.Chat); _logger.LogInformation("User {User} ({id}) registered to receive messages", msg.Chat.FirstName, msg.Chat.Id);
await _bot.SendMessage(msg.Chat, "You are registered to receive messages every minute"); await Respond(msg, "You are registered to receive messages every minute");
return; return;
case "/photo": case "/photo":
await SendImageTest(msg.Chat.Id); await SendImageTest(msg.Chat.Id);
return; return;
case "/soyandre":
await Respond(msg, "Hola vida, te amo mucho ❤️");
return;
default: default:
_logger.LogInformation("Received {Type} '{Text}' in {Chat}", type, msg.Text, msg.Chat); _logger.LogInformation("Received '{Text}' in {Chat}", msg.Text, msg.Chat);
await _bot.SendMessage(msg.Chat, "Commands: \n/start to start over \n/register to get messages every minute \n/photo to get a photo");
// await _bot.SendMessage(msg.Chat, "Hola vida te amo mucho ❤️"); const string commands =
"Commands: \n/help to show the commands \n/register to get messages every minute \n/photo to get a photo \n/soyandre por si sos andre";
await Respond(msg, commands);
break; break;
} }
} }
// method that handle other types of updates received by the bot:
private async Task OnUpdate(Update update) 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)
{ {
if (_bot is null) try
{ {
_logger.LogWarning("Bot is not initialized yet"); return update switch
return; {
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;
}
} }
if (update is { CallbackQuery: { } query }) // non-null CallbackQuery
{
await _bot.AnswerCallbackQuery(query.Id, $"You picked {query.Data}");
await _bot.SendMessage(query.Message!.Chat, $"User {query.From} clicked on {query.Data}");
}
}
} }

View File

@ -16,7 +16,18 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Telegram.Bot" Version="22.3.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Telegram.Bots" Version="5.9.0" />
<PackageReference Include="Telegram.Bots.Extensions.Polling" Version="5.9.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,43 +1,6 @@
#FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/runtime:9.0 AS base # Learn about building .NET container images:
#ARG TARGETARCH
#USER root
#WORKDIR /app
#
#FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
#ARG BUILD_CONFIGURATION=Release
#WORKDIR /src
#COPY ["CasaBotApp/CasaBotApp.csproj", "CasaBotApp/"]
#COPY ["AutoScan/AutoScan.csproj", "AutoScan/"]
#RUN dotnet restore "CasaBotApp/CasaBotApp.csproj" -a $TARGETARCH
#COPY . .
#WORKDIR "/src/CasaBotApp"
#RUN dotnet build "CasaBotApp.csproj" -c $BUILD_CONFIGURATION -o /app/build
#
#FROM build AS publish
#ARG BUILD_CONFIGURATION=Release
#RUN dotnet publish "CasaBotApp.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
#
#FROM base AS final
#
## Install pyhon and run "pipx install dvr-scan[opencv]" to install dependency
##RUN apt-get update && apt-get install -y python3 python3-pip python3-venv
##RUN python3 -m pip install dvr-scan[opencv] --break-system-packages
##RUN pip3 install pipx --break-system-packages
##RUN pipx install dvr-scan[opencv-headless]
#
#WORKDIR /app
#COPY --from=publish /app/publish .
##ENTRYPOINT ["dotnet", "CasaBotApp.dll"]
#
## interactive shell
#CMD ["/bin/bash"]
# Learn about building .NET container images:
# https://github.com/dotnet/dotnet-docker/blob/main/samples/README.md # https://github.com/dotnet/dotnet-docker/blob/main/samples/README.md
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0-noble AS build FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG TARGETARCH ARG TARGETARCH
WORKDIR /source WORKDIR /source
@ -53,8 +16,7 @@ RUN dotnet publish "CasaBotApp/CasaBotApp.csproj" -a $TARGETARCH --no-restore -o
# Runtime stage # Runtime stage
#FROM mcr.microsoft.com/dotnet/runtime:9.0-noble FROM mcr.microsoft.com/dotnet/runtime:9.0
FROM mcr.microsoft.com/dotnet/aspnet:9.0-noble
WORKDIR /app WORKDIR /app
COPY --link --from=build /app . COPY --link --from=build /app .
USER $APP_UID USER $APP_UID

View File

@ -3,19 +3,21 @@ using AutoScan.Options;
using CasaBotApp; using CasaBotApp;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Telegram.Bots;
using Telegram.Bots.Extensions.Polling;
// See https://aka.ms/new-console-template for more information var environment = Environment.GetEnvironmentVariable("CASABOT_ENVIRONMENT");
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
IConfigurationRoot configuration = new ConfigurationBuilder() IConfigurationRoot configuration = new ConfigurationBuilder()
.AddJsonFile($"appsettings.json", true, true) .AddJsonFile($"appsettings.json", true, true)
.AddJsonFile($"appsettings.{environment}.json", true, true) .AddJsonFile($"appsettings.{environment}.json", true, true)
.AddEnvironmentVariables() .AddEnvironmentVariables()
.Build(); .Build();
var services = new ServiceCollection(); var hostBuilder = new HostBuilder();
hostBuilder.ConfigureServices((_, services) =>
{
services.AddSingleton(configuration); services.AddSingleton(configuration);
services.AddLogging(builder => services.AddLogging(builder =>
{ {
@ -35,15 +37,23 @@ services.Configure<ShinobiOptions>(configuration.GetSection("Shinobi"));
services.AddSingleton<BotHandler>(); services.AddSingleton<BotHandler>();
var token = configuration["Telegram:BotToken"] ?? "";
services.AddBotClient(token);
services.AddPolling<BotHandler>();
services.AddSingleton<IUpdateHandler>(sp => sp.GetService<BotHandler>()!);
services.AddAutoScan(); services.AddAutoScan();
services.AddHttpClient(); services.AddHttpClient();
});
var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<Program>>()!; var host = hostBuilder.Build();
var botHandler = serviceProvider.GetService<BotHandler>()!;
var autoScanApp = serviceProvider.GetService<AutoScanApp>()!; var logger = host.Services.GetService<ILogger<Program>>()!;
var botHandler = host.Services.GetService<BotHandler>()!;
var autoScanApp = host.Services.GetService<AutoScanApp>()!;
using var cts = new CancellationTokenSource(); using var cts = new CancellationTokenSource();
@ -52,40 +62,12 @@ botHandler.Start(cts.Token);
_ = SendMessageToSubscribers(cts.Token); _ = SendMessageToSubscribers(cts.Token);
// var videoFileName = configuration.GetValue<string>("VideoFileName") ?? "video.mp4"; _ = host.RunAsync(cts.Token);
// if (configuration.GetValue<bool>("Fetch"))
// {
// var shinobiConnector = serviceProvider.GetService<ShinobiConnector>()!;
// await shinobiConnector.FetchLastVideo(videoFileName);
// }
//
// if (configuration.GetValue<bool>("Scan"))
// {
// var dvrScanner = serviceProvider.GetService<DVRScanner>()!;
// await dvrScanner.ScanVideos(cts.Token);
// }
//
// if(configuration.GetValue<bool>("Screenshot"))
// {
// var detected = "2025-02-12T07-00-02.DSME_0001.avi";
// var ffmpegWrapper = serviceProvider.GetService<FfmpegWrapper>()!;
// var duration = await ffmpegWrapper.GetVideoDuration($@".\media\detected\{detected}");
// logger.LogInformation("Video duration: {Duration}", duration);
// var middleTime = (duration / 2).Add(TimeSpan.FromSeconds(0.5));
//
// //Extract frame at middle time
// var screenshotPath = $@".\media\detected\{detected}-ss.png";
// await ffmpegWrapper.ExtractFrame($@".\media\detected\{detected}", screenshotPath, middleTime);
// logger.LogInformation("Screenshot extracted at {MiddleTime}", middleTime);
//
// //botHandler.Subscribe(115151151);
// await botHandler.UpdatePhoto(screenshotPath);
// }
logger.LogInformation("Bot started"); logger.LogInformation("Bot started");
logger.LogInformation("Press any key to stop the bot..."); logger.LogInformation("Press any key to stop the bot...");
Console.ReadLine(); Console.ReadLine();
cts.Cancel(); // stop the bot await cts.CancelAsync(); // stop the bot
return; return;
@ -94,8 +76,9 @@ async Task SendMessageToSubscribers(CancellationToken cancellationToken)
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken); await Task.Delay(TimeSpan.FromSeconds(30), cancellationToken);
botHandler.Update("Hello from CasaBot! at " + DateTime.Now); logger.LogInformation("Sending message to subscribers");
await botHandler.Update($"Hello from CasaBot! at {DateTime.Now}");
} }
} }