/
d.alekseev
/
SmartMenuBot
Обзор
Документация
Войти
/
d.alekseev
/
SmartMenuBot
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ConsoleBot/Program.cs
205 строк
11 KB
d.alekseev
feat: нотификации и фоновые задачи доставки уведомлений
26 фев 2026, 10:55
26 фев 2026, 10:55
4338916
Код
Авторство
О чём код?
using Microsoft.Extensions.DependencyInjection; using Telegram.Bot; using Telegram.Bot.Polling; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using SmartMenuBot.Core.DataAccess; using SmartMenuBot.Core.Services.Domain; using SmartMenuBot.Core.Services.Infrastructure; using SmartMenuBot.Core.Services.Interfaces; using SmartMenuBot.Core.Services.Managers; using SmartMenuBot.BackgroundTasks; using SmartMenuBot.Infrastructure.DataAccess; using SmartMenuBot.Infrastructure.Services; using SmartMenuBot.TelegramBot; using SmartMenuBot.TelegramBot.Callbacks.Implementations; using SmartMenuBot.TelegramBot.Callbacks.Interfaces; using SmartMenuBot.TelegramBot.Commands.Implementations; using SmartMenuBot.TelegramBot.Commands.Interfaces; using SmartMenuBot.TelegramBot.Processors.Implementations; using SmartMenuBot.TelegramBot.Processors.Interfaces; using SmartMenuBot.TelegramBot.Scenarios.Implementations; using SmartMenuBot.TelegramBot.Scenarios.Interfaces; namespace SmartMenuBot { internal class Program { static async Task Main() { try { var botToken = Environment.GetEnvironmentVariable("SMART_MENU_BOT_TOKEN") ?? throw new InvalidOperationException( "Переменная окружения SMART_MENU_BOT_TOKEN не установлена. " + "Установите её командой: export SMART_MENU_BOT_TOKEN=\"ваш_токен\""); var connectionString = Environment.GetEnvironmentVariable("SMART_MENU_DB_CONNECTION_STRING") ?? throw new InvalidOperationException( "Переменная окружения SMART_MENU_DB_CONNECTION_STRING не установлена. " + "Установите её командой: export SMART_MENU_DB_CONNECTION_STRING=\"Host=localhost;Port=5432;Database=ToDoList;Username=postgres;Password=postgres\""); using var cts = new CancellationTokenSource(); var services = new ServiceCollection(); services.AddSingleton<ITelegramBotClient>(_ => new TelegramBotClient(botToken)); services.AddSingleton<IDataContextFactory<ToDoDataContext>>(_ => new DataContextFactory(connectionString)); services.AddSingleton<IUserRepository, SqlUserRepository>(); services.AddSingleton<IToDoRepository, SqlToDoRepository>(); services.AddSingleton<IToDoListRepository, SqlToDoListRepository>(); services.AddSingleton<IUserService, UserService>(); services.AddSingleton<IToDoService, ToDoService>(); services.AddSingleton<IToDoListService, ToDoListService>(); services.AddSingleton<IToDoReportService, ToDoReportService>(); services.AddSingleton<INotificationService, NotificationService>(); services.AddSingleton<ILimitsNotifier, DefaultLimitsNotifier>(); services.AddSingleton<ITaskLimitsManager, TaskLimitsManager>(); services.AddSingleton<IBotCommand, StartCommand>(); services.AddSingleton<IBotCommand, SetLimitsCommand>(); services.AddSingleton<IBotCommand, HelpCommand>(); services.AddSingleton<IBotCommand, AddTaskCommand>(); services.AddSingleton<IBotCommand, CancelCommand>(); services.AddSingleton<IBotCommand, ShowTasksCommand>(); services.AddSingleton<IBotCommand, ReportCommand>(); services.AddSingleton<IBotCommand, FindCommand>(); services.AddSingleton<IBotCommand, InfoCommand>(); services.AddSingleton<IBotCommand, UnknownCommand>(); services.AddSingleton<ICallbackCommand, AddListCallbackCommand>(); services.AddSingleton<ICallbackCommand, DeleteListCallbackCommand>(); services.AddSingleton<ICallbackCommand, ShowCallbackCommand>(); services.AddSingleton<ICallbackCommand, ShowCompletedCallbackCommand>(); services.AddSingleton<ICallbackCommand, ShowTaskCallbackCommand>(); services.AddSingleton<ICallbackCommand, CompleteTaskCallbackCommand>(); services.AddSingleton<ICallbackCommand, DeleteTaskCallbackCommand>(); services.AddSingleton<IScenarioContextRepository, InMemoryScenarioContextRepository>(); services.AddSingleton<IScenario, AddTaskScenario>(); services.AddSingleton<IScenario, AddListScenario>(); services.AddSingleton<IScenario, DeleteListScenario>(); services.AddSingleton<IScenario, DeleteTaskScenario>(); services.AddSingleton<IScenarioCoordinator, ScenarioCoordinator>(); services.AddSingleton<IMessageUpdateProcessor, MessageUpdateProcessor>(); services.AddSingleton<ICallbackUpdateProcessor, CallbackUpdateProcessor>(); services.AddSingleton<UpdateHandler>(); var serviceProvider = services.BuildServiceProvider(); var botClient = serviceProvider.GetRequiredService<ITelegramBotClient>(); var handler = serviceProvider.GetRequiredService<UpdateHandler>(); var scenarioContextRepository = serviceProvider.GetRequiredService<IScenarioContextRepository>(); var notificationService = serviceProvider.GetRequiredService<INotificationService>(); var userRepository = serviceProvider.GetRequiredService<IUserRepository>(); var toDoRepository = serviceProvider.GetRequiredService<IToDoRepository>(); using var backgroundTaskRunner = new BackgroundTaskRunner(); // Подписка на события MessageEventHandler onStarted = message => Console.WriteLine($"Началась обработка сообщения '{message}'"); MessageEventHandler onCompleted = message => Console.WriteLine($"Закончилась обработка сообщения '{message}'"); handler.OnHandleUpdateStarted += onStarted; handler.OnHandleUpdateCompleted += onCompleted; backgroundTaskRunner.AddTask( new ResetScenarioBackgroundTask( TimeSpan.FromHours(1), scenarioContextRepository, botClient)); backgroundTaskRunner.AddTask( new NotificationBackgroundTask( notificationService, botClient)); backgroundTaskRunner.AddTask( new DeadlineBackgroundTask( notificationService, userRepository, toDoRepository)); backgroundTaskRunner.AddTask( new TodayBackgroundTask( notificationService, userRepository, toDoRepository)); try { // Регистрация команд в Menu await botClient.SetMyCommands( [ new BotCommand { Command = "start", Description = "Начало работы с ботом" }, new BotCommand { Command = "setlimits", Description = "Установить лимиты задач" }, new BotCommand { Command = "help", Description = "Справка по командам" }, new BotCommand { Command = "addtask", Description = "Добавить задачу" }, new BotCommand { Command = "cancel", Description = "Отменить текущий сценарий" }, new BotCommand { Command = "show", Description = "Показать задачи по списку" }, new BotCommand { Command = "report", Description = "Отчёт по задачам" }, new BotCommand { Command = "find", Description = "Поиск задач" }, new BotCommand { Command = "info", Description = "Информация о версии" }, ], cancellationToken: cts.Token); backgroundTaskRunner.StartTasks(cts.Token); // Запуск polling (неблокирующий) var receiverOptions = new ReceiverOptions { AllowedUpdates = [UpdateType.Message, UpdateType.CallbackQuery], DropPendingUpdates = true }; botClient.StartReceiving( updateHandler: handler.HandleUpdateAsync, errorHandler: handler.HandleErrorAsync, receiverOptions: receiverOptions, cancellationToken: cts.Token ); // Информация о боте кэшируется для избежания повторных API-запросов в цикле var me = await botClient.GetMe(cts.Token); Console.WriteLine($"{me.FirstName} (@{me.Username}) запущен!"); Console.WriteLine("Нажмите клавишу A для выхода"); // Цикл ожидания: A — выход, иначе — вывод информации о боте while (!cts.Token.IsCancellationRequested) { var key = Console.ReadKey(intercept: true); if (key.Key == ConsoleKey.A) { break; } else { Console.WriteLine($"\nИнформация о боте: {me.FirstName} (@{me.Username}), ID: {me.Id}"); } } Console.WriteLine("\nБот остановлен."); } finally { await backgroundTaskRunner.StopTasks(CancellationToken.None); await cts.CancelAsync(); handler.OnHandleUpdateStarted -= onStarted; handler.OnHandleUpdateCompleted -= onCompleted; } } catch (Exception ex) { Console.WriteLine("В приложении произошла непредвиденная ошибка:"); Console.WriteLine($"Тип: {ex.GetType()}"); Console.WriteLine($"Сообщение: {ex.Message}"); Console.WriteLine($"StackTrace: {ex.StackTrace}"); if (ex.InnerException != null) Console.WriteLine($"InnerException: {ex.InnerException}"); } } } }