refactor: extract controllers
This commit is contained in:
parent
064595c3e3
commit
77b4c21571
41
src/CasaBot/CasaBotApp/Controllers/AutoScanController.cs
Normal file
41
src/CasaBot/CasaBotApp/Controllers/AutoScanController.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using AutoScan;
|
||||
using CasaBotApp.TelegramBot;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CasaBotApp.Controllers;
|
||||
|
||||
public class AutoScanController : IController
|
||||
{
|
||||
private readonly BotHandler _botHandler;
|
||||
private readonly AutoScanApp _autoScanApp;
|
||||
private readonly ILogger<AutoScanController> _logger;
|
||||
|
||||
public AutoScanController(BotHandler botHandler, AutoScanApp autoScanApp, ILogger<AutoScanController> logger)
|
||||
{
|
||||
_botHandler = botHandler;
|
||||
_autoScanApp = autoScanApp;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Register()
|
||||
{
|
||||
_autoScanApp.OnScanCompleted = async () =>
|
||||
{
|
||||
_logger.LogInformation("Scan completed at {At}", DateTime.Now);
|
||||
try
|
||||
{
|
||||
var images = _autoScanApp.GetLastScanPictures();
|
||||
if (images.Length == 0)
|
||||
{
|
||||
await _botHandler.UpdateText("No images found");
|
||||
return;
|
||||
}
|
||||
await _botHandler.UpdateText($"Scan completed, found {images.Length} images");
|
||||
await _botHandler.UpdatePhotos(images);
|
||||
}catch(Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error while sending message");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
143
src/CasaBot/CasaBotApp/Controllers/BotController.cs
Normal file
143
src/CasaBot/CasaBotApp/Controllers/BotController.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using AutoScan;
|
||||
using AutoScan.Interfaces;
|
||||
using CasaBotApp.TelegramBot;
|
||||
using ControlServer;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics;
|
||||
using Telegram.Bots.Types;
|
||||
using BotCommand = CasaBotApp.TelegramBot.BotCommand;
|
||||
|
||||
namespace CasaBotApp.Controllers;
|
||||
|
||||
public class BotController : IController
|
||||
{
|
||||
private readonly BotHandler _botHandler;
|
||||
private readonly ILogger<BotController> _logger;
|
||||
private readonly AutoScanApp _autoScanApp;
|
||||
private readonly ISnapshoter _snapshoter;
|
||||
private readonly IControlServer _controlServer;
|
||||
|
||||
|
||||
public BotController(BotHandler botHandler, ILogger<BotController> logger, AutoScanApp autoScanApp, ISnapshoter snapshoter, IControlServer controlServer)
|
||||
{
|
||||
_botHandler = botHandler;
|
||||
_logger = logger;
|
||||
_autoScanApp = autoScanApp;
|
||||
_snapshoter = snapshoter;
|
||||
_controlServer = controlServer;
|
||||
}
|
||||
|
||||
public void Register()
|
||||
{
|
||||
_logger.LogInformation("Registering bot commands");
|
||||
var methods = GetType().GetMethods()
|
||||
.Where(m => m.GetCustomAttributes(typeof(BotCommandAttribute), false).Length > 0)
|
||||
.ToArray();
|
||||
foreach (var method in methods)
|
||||
{
|
||||
try
|
||||
{
|
||||
var attribute = (BotCommandAttribute)method.GetCustomAttributes(typeof(BotCommandAttribute), false)[0];
|
||||
var command = new BotCommand
|
||||
{
|
||||
Command = attribute.Command,
|
||||
Description = attribute.Description,
|
||||
Action = method.CreateDelegate<Func<TextMessage, BotCommand, Task>>(this),
|
||||
};
|
||||
_botHandler.RegisterCommand(command);
|
||||
|
||||
_logger.LogInformation("Registered command: {Command} - {Description}", command.Command, command.Description);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error registering command {Command}", method.Name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
HandleReply();
|
||||
}
|
||||
|
||||
[BotCommand("/soyandre", "Soy Andre")]
|
||||
public async Task HiAndre(TextMessage msg, BotCommand ctx)
|
||||
{
|
||||
_logger.LogInformation("Andre stoped by");
|
||||
await ctx.Responder(msg, "Hola vida, te amo mucho ❤️");
|
||||
}
|
||||
|
||||
[BotCommand("/startScan", "Start a scan of last night images")]
|
||||
public async Task StartScan(TextMessage msg, BotCommand ctx)
|
||||
{
|
||||
await ctx.Responder(msg, "Starting scan 🔍📼");
|
||||
await _autoScanApp.StartNewScan();
|
||||
}
|
||||
|
||||
[BotCommand("/lastscan", "Send the images from the last scan")]
|
||||
public async Task LastScan(TextMessage msg, BotCommand ctx)
|
||||
{
|
||||
var images = _autoScanApp.GetLastScanPictures();
|
||||
if (images.Length == 0)
|
||||
{
|
||||
await ctx.Responder(msg, "No images found");
|
||||
return;
|
||||
}
|
||||
await _botHandler.SendPhotos(msg.Chat.Id, images);
|
||||
|
||||
}
|
||||
|
||||
[BotCommand("/now", "Get the current snapshot")]
|
||||
public async Task CurrentSnapshot(TextMessage msg, BotCommand ctx)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
stopwatch.Start();
|
||||
var outputPath = await _snapshoter.TakeSnapshot();
|
||||
stopwatch.Stop();
|
||||
if (string.IsNullOrEmpty(outputPath))
|
||||
{
|
||||
await ctx.Responder(msg, "Error taking snapshot, try later");
|
||||
return;
|
||||
}
|
||||
_ = _botHandler.SendPhoto(msg.Chat.Id, outputPath, $"Snapshot: {DateTime.Now:g} ({stopwatch.ElapsedMilliseconds} ms)");
|
||||
}
|
||||
|
||||
[BotCommand("/disarm", "Disarm the Door Sensor")]
|
||||
public async Task Disarm(TextMessage msg, BotCommand ctx)
|
||||
{
|
||||
await ctx.Responder(msg, "Disarming the door sensor");
|
||||
_controlServer.RequestDisarm();
|
||||
}
|
||||
|
||||
private void HandleReply()
|
||||
{
|
||||
_botHandler.OnReply = async msg =>
|
||||
{
|
||||
var originalMsg = msg.ReplyToMessage;
|
||||
|
||||
// Check if the original message is a photo and has a caption
|
||||
if (originalMsg is not PhotoMessage photoMessage || photoMessage.Caption is null)
|
||||
return;
|
||||
|
||||
var videoPath = _autoScanApp.GetVideoPath(photoMessage.Caption);
|
||||
if (string.IsNullOrEmpty(videoPath))
|
||||
{
|
||||
await _botHandler.SendText(msg.Chat.Id, "No video found for this image");
|
||||
return;
|
||||
}
|
||||
|
||||
await _botHandler.SendVideo(msg.Chat.Id, videoPath);
|
||||
};
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
private class BotCommandAttribute : Attribute
|
||||
{
|
||||
public string Command { get; }
|
||||
public string Description { get; }
|
||||
|
||||
public BotCommandAttribute(string command, string description)
|
||||
{
|
||||
Command = command;
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
}
|
19
src/CasaBot/CasaBotApp/Controllers/CasaBotOrchestrator.cs
Normal file
19
src/CasaBot/CasaBotApp/Controllers/CasaBotOrchestrator.cs
Normal file
@ -0,0 +1,19 @@
|
||||
namespace CasaBotApp.Controllers;
|
||||
|
||||
public class CasaBotOrchestrator
|
||||
{
|
||||
private readonly IEnumerable<IController> _controllers;
|
||||
|
||||
public CasaBotOrchestrator(IEnumerable<IController> controllers)
|
||||
{
|
||||
_controllers = controllers;
|
||||
}
|
||||
|
||||
public void RegisterControllers()
|
||||
{
|
||||
foreach (var controller in _controllers)
|
||||
{
|
||||
controller.Register();
|
||||
}
|
||||
}
|
||||
}
|
6
src/CasaBot/CasaBotApp/Controllers/IController.cs
Normal file
6
src/CasaBot/CasaBotApp/Controllers/IController.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace CasaBotApp.Controllers;
|
||||
|
||||
public interface IController
|
||||
{
|
||||
void Register();
|
||||
}
|
56
src/CasaBot/CasaBotApp/Controllers/ServerController.cs
Normal file
56
src/CasaBot/CasaBotApp/Controllers/ServerController.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using AutoScan.Interfaces;
|
||||
using CasaBotApp.TelegramBot;
|
||||
using ControlServer;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CasaBotApp.Controllers;
|
||||
|
||||
public class ServerController : IController
|
||||
{
|
||||
private readonly IControlServer _controlServer;
|
||||
private readonly BotHandler _botHandler;
|
||||
private readonly ISnapshoter _snapshoter;
|
||||
private readonly ILogger<ServerController> _logger;
|
||||
private readonly IShinobiLinkFactory _shinobiLinkFactory;
|
||||
|
||||
public ServerController(IControlServer controlServer, ISnapshoter snapshoter, ILogger<ServerController> logger, IShinobiLinkFactory shinobiLinkFactory, BotHandler botHandler)
|
||||
{
|
||||
_controlServer = controlServer;
|
||||
_snapshoter = snapshoter;
|
||||
_logger = logger;
|
||||
_shinobiLinkFactory = shinobiLinkFactory;
|
||||
_botHandler = botHandler;
|
||||
}
|
||||
|
||||
public void Register()
|
||||
{
|
||||
_controlServer.OnEvent(async sensorEvent =>
|
||||
{
|
||||
var mediaPath = await _snapshoter.TakeSnapshot();
|
||||
if (string.IsNullOrEmpty(mediaPath))
|
||||
{
|
||||
await _botHandler.AlertText("Unauthorized access detected 🚨🚨🚨, but no media available");
|
||||
return;
|
||||
}
|
||||
if (sensorEvent.Type == EventType.Fired)
|
||||
{
|
||||
await _botHandler.AlertPhoto(mediaPath,
|
||||
"Unauthorized access detected 🚨 🚨 🚨",
|
||||
[
|
||||
new(OptionType.Url, "Camera Feed", _shinobiLinkFactory.BuildFeedLink()),
|
||||
new(OptionType.Action, "Authorize", $"authorize-{sensorEvent.EventId}", (_, _ ) =>
|
||||
{
|
||||
_logger.LogWarning("Authorizing event {EventId}", sensorEvent.EventId);
|
||||
_controlServer.AuthorizeEvent(sensorEvent.EventId);
|
||||
return Task.FromResult("Entrance authorized");
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
if (sensorEvent.Type == EventType.DisarmedEntrance)
|
||||
{
|
||||
await _botHandler.UpdateText("Authorize access");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
17
src/CasaBot/CasaBotApp/Extensions/AlarmBotOrchestration.cs
Normal file
17
src/CasaBot/CasaBotApp/Extensions/AlarmBotOrchestration.cs
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
using CasaBotApp.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CasaBotApp.Extensions;
|
||||
|
||||
public static class AlarmBotOrchestration
|
||||
{
|
||||
public static void AddCasaBotOrchestration(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<CasaBotOrchestrator>();
|
||||
services.AddTransient<IController, AutoScanController>();
|
||||
services.AddTransient<IController, BotController>();
|
||||
services.AddTransient<IController, ServerController>();
|
||||
}
|
||||
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
using AutoScan;
|
||||
using AutoScan.Interfaces;
|
||||
using CasaBotApp.TelegramBot;
|
||||
using ControlServer;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics;
|
||||
using Telegram.Bots.Types;
|
||||
using BotCommand = CasaBotApp.TelegramBot.BotCommand;
|
||||
|
||||
namespace CasaBotApp.Extensions;
|
||||
|
||||
public class AlarmBotOrquestrator
|
||||
{
|
||||
private readonly ILogger<AlarmBotOrquestrator> _logger;
|
||||
private readonly BotHandler _botHandler;
|
||||
private readonly AutoScanApp _autoScanApp;
|
||||
private readonly IControlServer _controlServer;
|
||||
private readonly IShinobiLinkFactory _shinobiLinkFactory;
|
||||
private ISnapshoter _snapshoter;
|
||||
|
||||
public AlarmBotOrquestrator(ILogger<AlarmBotOrquestrator> logger, BotHandler botHandler, AutoScanApp autoScanApp,
|
||||
IControlServer controlServer, IShinobiLinkFactory shinobiLinkFactory, ISnapshoter snapshoter)
|
||||
{
|
||||
_logger = logger;
|
||||
_botHandler = botHandler;
|
||||
_autoScanApp = autoScanApp;
|
||||
_controlServer = controlServer;
|
||||
_shinobiLinkFactory = shinobiLinkFactory;
|
||||
_snapshoter = snapshoter;
|
||||
}
|
||||
|
||||
public void RegisterCommands()
|
||||
{
|
||||
_botHandler.RegisterCommand(new BotCommand
|
||||
{
|
||||
Command = "/soyandre",
|
||||
Description = "Soy Andre",
|
||||
Action = async (message, ctx) =>
|
||||
{
|
||||
await ctx.Responder(message, "Hola vida, te amo mucho ❤️");
|
||||
}
|
||||
});
|
||||
_botHandler.RegisterCommand(new BotCommand
|
||||
{
|
||||
Command = "/startScan",
|
||||
Description = "Start a scan of last night images",
|
||||
Action = async (message, ctx) =>
|
||||
{
|
||||
await ctx.Responder(message, "Starting scan 🔍📼");
|
||||
await _autoScanApp.StartNewScan();
|
||||
}
|
||||
});
|
||||
_botHandler.RegisterCommand(new BotCommand
|
||||
{
|
||||
Command = "/lastscan",
|
||||
Description = "Send the images from the last scan",
|
||||
Action = async (message, ctx) =>
|
||||
{
|
||||
var images = _autoScanApp.GetLastScanPictures();
|
||||
if (images.Length == 0)
|
||||
{
|
||||
await ctx.Responder(message, "No images found");
|
||||
return;
|
||||
}
|
||||
await _botHandler.SendPhotos(message.Chat.Id, images);
|
||||
}
|
||||
});
|
||||
|
||||
_botHandler.RegisterCommand(new BotCommand()
|
||||
{
|
||||
Command = "/now",
|
||||
Description = "Send the current snapshot",
|
||||
Action = async (msg, ctx) =>
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
stopwatch.Start();
|
||||
var outputPath = await _snapshoter.TakeSnapshot();
|
||||
stopwatch.Stop();
|
||||
if (string.IsNullOrEmpty(outputPath))
|
||||
{
|
||||
await ctx.Responder(msg, "Error taking snapshot");
|
||||
return;
|
||||
}
|
||||
_ = _botHandler.SendPhoto(msg.Chat.Id, outputPath, "Current snapshot");
|
||||
_ = _botHandler.SendText(msg.Chat.Id, $"It took {stopwatch.ElapsedMilliseconds} ms to take the picture");
|
||||
}
|
||||
});
|
||||
_botHandler.RegisterCommand(new BotCommand()
|
||||
{
|
||||
Command = "/disarm",
|
||||
Description = "Disarm the Door Sensor",
|
||||
Action = async (msg, ctx) =>
|
||||
{
|
||||
await ctx.Responder(msg, "Disarming the door sensor");
|
||||
_controlServer.RequestDisarm();
|
||||
}
|
||||
});
|
||||
|
||||
_botHandler.OnReply = async msg =>
|
||||
{
|
||||
var originalMsg = msg.ReplyToMessage;
|
||||
|
||||
// Check if the original message is a photo and has a caption
|
||||
if (originalMsg is not PhotoMessage photoMessage || photoMessage.Caption is null)
|
||||
return;
|
||||
|
||||
var videoPath = _autoScanApp.GetVideoPath(photoMessage.Caption);
|
||||
if (string.IsNullOrEmpty(videoPath))
|
||||
{
|
||||
await _botHandler.SendText(msg.Chat.Id, "No video found for this image");
|
||||
return;
|
||||
}
|
||||
|
||||
await _botHandler.SendVideo(msg.Chat.Id, videoPath);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public void RegisterAutoScanApp()
|
||||
{
|
||||
_autoScanApp.OnScanCompleted = async () =>
|
||||
{
|
||||
_logger.LogInformation("Scan completed at {At}", DateTime.Now);
|
||||
try
|
||||
{
|
||||
var images = _autoScanApp.GetLastScanPictures();
|
||||
if (images.Length == 0)
|
||||
{
|
||||
await _botHandler.UpdateText("No images found");
|
||||
return;
|
||||
}
|
||||
await _botHandler.UpdateText($"Scan completed, found {images.Length} images");
|
||||
await _botHandler.UpdatePhotos(images);
|
||||
}catch(Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error while sending message");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void RegisterControlServer()
|
||||
{
|
||||
_controlServer.OnEvent(async sensorEvent =>
|
||||
{
|
||||
var mediaPath = await _snapshoter.TakeSnapshot();
|
||||
if (string.IsNullOrEmpty(mediaPath))
|
||||
{
|
||||
await _botHandler.AlertText("Unauthorized access detected 🚨🚨🚨, but no media available");
|
||||
return;
|
||||
}
|
||||
if (sensorEvent.Type == EventType.Fired)
|
||||
{
|
||||
await _botHandler.AlertPhoto(mediaPath,
|
||||
"Unauthorized access detected 🚨 🚨 🚨",
|
||||
[
|
||||
new(OptionType.Url, "Camera Feed", _shinobiLinkFactory.BuildFeedLink()),
|
||||
new(OptionType.Action, "Authorize", $"authorize-{sensorEvent.EventId}", (_, _ ) =>
|
||||
{
|
||||
_logger.LogWarning("Authorizing event {EventId}", sensorEvent.EventId);
|
||||
_controlServer.AuthorizeEvent(sensorEvent.EventId);
|
||||
return Task.FromResult("Entrance authorized");
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
if (sensorEvent.Type == EventType.DisarmedEntrance)
|
||||
{
|
||||
await _botHandler.UpdateText("Authorize access");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using AutoScan;
|
||||
using AutoScan.Options;
|
||||
using CasaBotApp.Controllers;
|
||||
using CasaBotApp.Extensions;
|
||||
using CasaBotApp.TelegramBot;
|
||||
using ControlServer;
|
||||
@ -40,7 +41,7 @@ hostBuilder.ConfigureServices((_, services) =>
|
||||
services.AddPolling<BotHandler>();
|
||||
services.AddSingleton<IUpdateHandler>(sp => sp.GetService<BotHandler>()!);
|
||||
|
||||
services.AddTransient<AlarmBotOrquestrator>();
|
||||
services.AddCasaBotOrchestration();
|
||||
|
||||
// To get notifications when a retry is performed
|
||||
|
||||
@ -65,16 +66,14 @@ var host = hostBuilder.Build();
|
||||
var logger = host.Services.GetService<ILogger<Program>>()!;
|
||||
var autoScanApp = host.Services.GetService<AutoScanApp>()!;
|
||||
|
||||
var commandRegister = host.Services.GetRequiredService<AlarmBotOrquestrator>();
|
||||
var orchestrator = host.Services.GetRequiredService<CasaBotOrchestrator>();
|
||||
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
|
||||
_ = autoScanApp.Run(cts.Token);
|
||||
|
||||
commandRegister.RegisterAutoScanApp();
|
||||
commandRegister.RegisterCommands();
|
||||
commandRegister.RegisterControlServer();
|
||||
orchestrator.RegisterControllers();
|
||||
|
||||
logger.LogInformation("Bot started");
|
||||
await host.RunAsync(cts.Token);
|
||||
|
Loading…
Reference in New Issue
Block a user