diff --git a/PepperBot/Application/Interfaces/ISubscriptionRepository.cs b/PepperBot/Application/Interfaces/ISubscriptionRepository.cs
new file mode 100644
index 0000000..3dae2e6
--- /dev/null
+++ b/PepperBot/Application/Interfaces/ISubscriptionRepository.cs
@@ -0,0 +1,30 @@
+using PepperBot.Domain;
+
+namespace PepperBot.Application.Interfaces;
+
+public interface ISubscriptionRepository
+{
+ Task InitializeAsync(CancellationToken cancellationToken);
+
+ ///
+ /// Получить все активные подписки.
+ ///
+ Task> GetAllActiveAsync(CancellationToken cancellationToken);
+
+ ///
+ /// Добавить новое правило подписки для указанного чата.
+ /// Одно правило = один кейворд (но у одного чата может быть много правил).
+ ///
+ Task AddSubscriptionAsync(string chatId, string keyword, CancellationToken cancellationToken);
+
+ ///
+ /// Получить активные подписки для конкретного чата.
+ ///
+ Task> GetByChatAsync(string chatId, CancellationToken cancellationToken);
+
+ ///
+ /// Удалить (деактивировать) подписку по идентификатору.
+ ///
+ Task DeleteSubscriptionAsync(long id, CancellationToken cancellationToken);
+}
+
diff --git a/PepperBot/Application/Interfaces/ITelegramNotifier.cs b/PepperBot/Application/Interfaces/ITelegramNotifier.cs
index ef698ed..2b88485 100644
--- a/PepperBot/Application/Interfaces/ITelegramNotifier.cs
+++ b/PepperBot/Application/Interfaces/ITelegramNotifier.cs
@@ -4,6 +4,19 @@ namespace PepperBot.Application.Interfaces;
public interface ITelegramNotifier
{
+ ///
+ /// Уведомление о новой скидке в общий чат (из переменной окружения TELEGRAM_CHAT_ID).
+ ///
Task NotifyNewDealAsync(Deal deal, CancellationToken cancellationToken);
+
+ ///
+ /// Уведомление о скидке в конкретный Telegram-чат (личная/персональная рассылка).
+ ///
+ Task NotifyDealToChatAsync(Deal deal, string chatId, CancellationToken cancellationToken);
+
+ ///
+ /// Запустить обработку входящих сообщений Telegram и регистрацию подписок.
+ ///
+ Task StartAsync(CancellationToken cancellationToken);
}
diff --git a/PepperBot/Application/Services/DealService.cs b/PepperBot/Application/Services/DealService.cs
index 45103be..69ea153 100644
--- a/PepperBot/Application/Services/DealService.cs
+++ b/PepperBot/Application/Services/DealService.cs
@@ -8,6 +8,7 @@ public class DealService
{
private readonly IPepperClient _pepperClient;
private readonly IDealRepository _repository;
+ private readonly ISubscriptionRepository _subscriptionRepository;
private readonly ITelegramNotifier _notifier;
private readonly ILogger _logger;
private readonly IHealthMonitor _healthMonitor;
@@ -15,12 +16,14 @@ public class DealService
public DealService(
IPepperClient pepperClient,
IDealRepository repository,
+ ISubscriptionRepository subscriptionRepository,
ITelegramNotifier notifier,
ILogger logger,
IHealthMonitor healthMonitor)
{
_pepperClient = pepperClient;
_repository = repository;
+ _subscriptionRepository = subscriptionRepository;
_notifier = notifier;
_logger = logger;
_healthMonitor = healthMonitor;
@@ -36,6 +39,9 @@ public class DealService
_healthMonitor.ReportSuccess(DateTimeOffset.UtcNow);
}
+ // Загружаем активные подписки один раз на проход
+ var subscriptions = await _subscriptionRepository.GetAllActiveAsync(cancellationToken);
+
foreach (var deal in deals)
{
if (await _repository.ExistsAsync(deal.Id, cancellationToken))
@@ -46,8 +52,63 @@ public class DealService
_logger.LogInformation("Новая скидка: {Title} ({Id})", deal.Title, deal.Id);
await _repository.AddAsync(deal, cancellationToken);
+
+ // Общая рассылка в основной чат (как было раньше)
await _notifier.NotifyNewDealAsync(deal, cancellationToken);
+
+ if (subscriptions.Count == 0)
+ {
+ continue;
+ }
+
+ var searchableText = $"{deal.Title} {deal.StoreName}";
+
+ foreach (var subscription in subscriptions)
+ {
+ if (!subscription.IsActive)
+ {
+ continue;
+ }
+
+ var keywords = ParseKeywords(subscription.Keywords);
+ if (keywords.Count == 0)
+ {
+ continue;
+ }
+
+ if (IsMatch(searchableText, keywords))
+ {
+ await _notifier.NotifyDealToChatAsync(deal, subscription.ChatId, cancellationToken);
+ }
+ }
}
}
+
+ private static List ParseKeywords(string raw)
+ {
+ return raw
+ .Split(new[] { '-',',', ';', '\n', '\r', '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(k => k.Trim())
+ .Where(k => k.Length > 0)
+ .ToList();
+ }
+
+ private static bool IsMatch(string text, List keywords)
+ {
+ if (keywords.Count == 0)
+ {
+ return false;
+ }
+
+ foreach (var keyword in keywords)
+ {
+ if (text.Contains(keyword, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/PepperBot/Domain/Subscription.cs b/PepperBot/Domain/Subscription.cs
new file mode 100644
index 0000000..31f3d82
--- /dev/null
+++ b/PepperBot/Domain/Subscription.cs
@@ -0,0 +1,19 @@
+namespace PepperBot.Domain;
+
+public class Subscription
+{
+ public long Id { get; set; }
+
+ ///
+ /// Telegram chat id пользователя (может быть user id или id группы).
+ ///
+ public string ChatId { get; set; } = string.Empty;
+
+ ///
+ /// Строка с ключевыми словами, разделёнными запятыми/пробелами (например: "PLA, ABS, PETG").
+ ///
+ public string Keywords { get; set; } = string.Empty;
+
+ public bool IsActive { get; set; } = true;
+}
+
diff --git a/PepperBot/Infrastructure/Data/SqliteSubscriptionRepository.cs b/PepperBot/Infrastructure/Data/SqliteSubscriptionRepository.cs
new file mode 100644
index 0000000..fc518f5
--- /dev/null
+++ b/PepperBot/Infrastructure/Data/SqliteSubscriptionRepository.cs
@@ -0,0 +1,144 @@
+using Microsoft.Data.Sqlite;
+using PepperBot.Application.Interfaces;
+using PepperBot.Domain;
+
+namespace PepperBot.Infrastructure.Data;
+
+public class SqliteSubscriptionRepository : ISubscriptionRepository
+{
+ private const string DatabaseFileName = "pepper_bot.db";
+ private const string DataDirectory = "data";
+
+ private string ConnectionString =>
+ new SqliteConnectionStringBuilder
+ {
+ DataSource = Path.Combine(DataDirectory, DatabaseFileName)
+ }.ToString();
+
+ public async Task InitializeAsync(CancellationToken cancellationToken)
+ {
+ if (!Directory.Exists(DataDirectory))
+ {
+ Directory.CreateDirectory(DataDirectory);
+ }
+
+ await using var connection = new SqliteConnection(ConnectionString);
+ await connection.OpenAsync(cancellationToken);
+
+ var command = connection.CreateCommand();
+ command.CommandText =
+ """
+ CREATE TABLE IF NOT EXISTS Subscriptions (
+ Id INTEGER PRIMARY KEY AUTOINCREMENT,
+ ChatId TEXT NOT NULL,
+ Keywords TEXT NOT NULL,
+ IsActive INTEGER NOT NULL DEFAULT 1
+ );
+ """;
+
+ await command.ExecuteNonQueryAsync(cancellationToken);
+ }
+
+ public async Task> GetAllActiveAsync(CancellationToken cancellationToken)
+ {
+ await using var connection = new SqliteConnection(ConnectionString);
+ await connection.OpenAsync(cancellationToken);
+
+ var command = connection.CreateCommand();
+ command.CommandText =
+ """
+ SELECT Id, ChatId, Keywords, IsActive
+ FROM Subscriptions
+ WHERE IsActive = 1;
+ """;
+
+ var result = new List();
+
+ await using var reader = await command.ExecuteReaderAsync(cancellationToken);
+ while (await reader.ReadAsync(cancellationToken))
+ {
+ var subscription = new Subscription
+ {
+ Id = reader.GetInt64(0),
+ ChatId = reader.GetString(1),
+ Keywords = reader.GetString(2),
+ IsActive = reader.GetInt32(3) == 1
+ };
+
+ result.Add(subscription);
+ }
+
+ return result;
+ }
+
+ public async Task AddSubscriptionAsync(string chatId, string keyword, CancellationToken cancellationToken)
+ {
+ await using var connection = new SqliteConnection(ConnectionString);
+ await connection.OpenAsync(cancellationToken);
+
+ var command = connection.CreateCommand();
+ command.CommandText =
+ """
+ INSERT INTO Subscriptions (ChatId, Keywords, IsActive)
+ VALUES ($chatId, $keywords, 1);
+ """;
+
+ command.Parameters.AddWithValue("$chatId", chatId);
+ command.Parameters.AddWithValue("$keywords", keyword);
+
+ await command.ExecuteNonQueryAsync(cancellationToken);
+ }
+
+ public async Task> GetByChatAsync(string chatId, CancellationToken cancellationToken)
+ {
+ await using var connection = new SqliteConnection(ConnectionString);
+ await connection.OpenAsync(cancellationToken);
+
+ var command = connection.CreateCommand();
+ command.CommandText =
+ """
+ SELECT Id, ChatId, Keywords, IsActive
+ FROM Subscriptions
+ WHERE ChatId = $chatId AND IsActive = 1;
+ """;
+
+ command.Parameters.AddWithValue("$chatId", chatId);
+
+ var result = new List();
+
+ await using var reader = await command.ExecuteReaderAsync(cancellationToken);
+ while (await reader.ReadAsync(cancellationToken))
+ {
+ var subscription = new Subscription
+ {
+ Id = reader.GetInt64(0),
+ ChatId = reader.GetString(1),
+ Keywords = reader.GetString(2),
+ IsActive = reader.GetInt32(3) == 1
+ };
+
+ result.Add(subscription);
+ }
+
+ return result;
+ }
+
+ public async Task DeleteSubscriptionAsync(long id, CancellationToken cancellationToken)
+ {
+ await using var connection = new SqliteConnection(ConnectionString);
+ await connection.OpenAsync(cancellationToken);
+
+ var command = connection.CreateCommand();
+ command.CommandText =
+ """
+ UPDATE Subscriptions
+ SET IsActive = 0
+ WHERE Id = $id;
+ """;
+
+ command.Parameters.AddWithValue("$id", id);
+
+ await command.ExecuteNonQueryAsync(cancellationToken);
+ }
+}
+
diff --git a/PepperBot/Infrastructure/Telegram/TelegramNotifier.cs b/PepperBot/Infrastructure/Telegram/TelegramNotifier.cs
index 7db40f6..4db5f84 100644
--- a/PepperBot/Infrastructure/Telegram/TelegramNotifier.cs
+++ b/PepperBot/Infrastructure/Telegram/TelegramNotifier.cs
@@ -1,43 +1,266 @@
-using System.Net.Http.Json;
+using System.Globalization;
using System.Text;
using Microsoft.Extensions.Logging;
using PepperBot.Application.Interfaces;
using PepperBot.Domain;
+using Telegram.Bot;
+using Telegram.Bot.Exceptions;
+using Telegram.Bot.Polling;
+using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
+using Telegram.Bot.Types.ReplyMarkups;
namespace PepperBot.Infrastructure.Telegram;
public class TelegramNotifier : ITelegramNotifier
{
- private readonly HttpClient _httpClient;
+ private readonly ITelegramBotClient _botClient;
+ private readonly ISubscriptionRepository _subscriptionRepository;
private readonly ILogger _logger;
- private readonly string _botToken;
- private readonly string _chatId;
+ private readonly string _broadcastChatId;
+ private bool _started;
- public TelegramNotifier(ILogger logger)
+ private enum ConversationState
{
- _logger = logger;
- _botToken = Environment.GetEnvironmentVariable("TELEGRAM_BOT_TOKEN") ?? string.Empty;
- _chatId = Environment.GetEnvironmentVariable("TELEGRAM_CHAT_ID") ?? string.Empty;
-
- if (string.IsNullOrWhiteSpace(_botToken) || string.IsNullOrWhiteSpace(_chatId))
- {
- _logger.LogWarning("TELEGRAM_BOT_TOKEN или TELEGRAM_CHAT_ID не заданы. Отправка сообщений отключена.");
- }
-
- _httpClient = new HttpClient();
+ None = 0,
+ AwaitingAddKeyword = 1
}
- public async Task NotifyNewDealAsync(Deal deal, CancellationToken cancellationToken)
+ private readonly Dictionary _chatStates = new();
+
+ public TelegramNotifier(
+ ITelegramBotClient botClient,
+ ISubscriptionRepository subscriptionRepository,
+ ILogger logger)
{
- if (string.IsNullOrWhiteSpace(_botToken) || string.IsNullOrWhiteSpace(_chatId))
+ _botClient = botClient;
+ _subscriptionRepository = subscriptionRepository;
+ _logger = logger;
+ _broadcastChatId = Environment.GetEnvironmentVariable("TELEGRAM_CHAT_ID") ?? string.Empty;
+
+ if (string.IsNullOrWhiteSpace(_broadcastChatId))
+ {
+ _logger.LogInformation("TELEGRAM_CHAT_ID не задан. Общая рассылка по скидкам будет отключена.");
+ }
+ }
+
+ public Task NotifyNewDealAsync(Deal deal, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrWhiteSpace(_broadcastChatId))
+ {
+ return Task.CompletedTask;
+ }
+
+ return SendDealAsync(deal, _broadcastChatId, cancellationToken);
+ }
+
+ public Task NotifyDealToChatAsync(Deal deal, string chatId, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrWhiteSpace(chatId))
+ {
+ return Task.CompletedTask;
+ }
+
+ return SendDealAsync(deal, chatId, cancellationToken);
+ }
+
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ if (_started)
+ {
+ return Task.CompletedTask;
+ }
+
+ var receiverOptions = new ReceiverOptions
+ {
+ AllowedUpdates = Array.Empty()
+ };
+
+ _botClient.StartReceiving(
+ HandleUpdateAsync,
+ HandleErrorAsync,
+ receiverOptions,
+ cancellationToken);
+
+ _started = true;
+ _logger.LogInformation("Запущена обработка входящих сообщений Telegram.");
+
+ return Task.CompletedTask;
+ }
+
+ private async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
+ {
+ if (update.Type == UpdateType.CallbackQuery)
+ {
+ await HandleCallbackQueryAsync(botClient, update.CallbackQuery!, cancellationToken);
+ {
+ return;
+ }
+ }
+
+ if (update.Type != UpdateType.Message)
{
return;
}
- var url = $"https://api.telegram.org/bot{_botToken}/sendMessage";
+ var message = update.Message;
+ if (message is null)
+ {
+ return;
+ }
+ if (message.Type != MessageType.Text)
+ {
+ return;
+ }
+
+ // Нас интересуют только личные сообщения боту
+ if (message.Chat.Type != ChatType.Private)
+ {
+ return;
+ }
+
+ var chatId = message.Chat.Id.ToString(CultureInfo.InvariantCulture);
+ var text = message.Text?.Trim();
+ if (string.IsNullOrWhiteSpace(text))
+ {
+ return;
+ }
+
+ // Команда для вызова меню
+ if (text.Equals("/menu", StringComparison.OrdinalIgnoreCase))
+ {
+ await SendMainMenuAsync(message.Chat.Id, cancellationToken);
+ _chatStates[chatId] = ConversationState.None;
+ return;
+ }
+
+ // Обработка выбора пунктов меню
+ if (text.Equals("Добавить подписку", StringComparison.OrdinalIgnoreCase))
+ {
+ await botClient.SendMessage(
+ chatId: message.Chat.Id,
+ text: "Отправьте одно или несколько ключевых слов для подписки (через пробел, запятую или с новой строки).",
+ cancellationToken: cancellationToken);
+
+ _chatStates[chatId] = ConversationState.AwaitingAddKeyword;
+ return;
+ }
+
+ if (text.Equals("Удалить подписку", StringComparison.OrdinalIgnoreCase))
+ {
+ await ShowDeleteMenuAsync(message.Chat.Id, chatId, cancellationToken);
+ _chatStates[chatId] = ConversationState.None;
+ return;
+ }
+
+ if (text.Equals("Список подписок", StringComparison.OrdinalIgnoreCase))
+ {
+ await ShowSubscriptionsListAsync(message.Chat.Id, chatId, cancellationToken);
+ _chatStates[chatId] = ConversationState.None;
+ return;
+ }
+
+ // Обработка состояний диалога
+ _chatStates.TryGetValue(chatId, out var state);
+
+ if (state == ConversationState.AwaitingAddKeyword)
+ {
+ var separators = new[] { ',', ';', '\n', '\r', '\t', ' ' };
+ var keywords = text
+ .Split(separators, StringSplitOptions.RemoveEmptyEntries)
+ .Select(k => k.Trim())
+ .Where(k => k.Length > 0)
+ .ToList();
+
+ if (keywords.Count == 0)
+ {
+ await botClient.SendMessage(
+ chatId: message.Chat.Id,
+ text: "Не нашёл ни одного ключевого слова. Попробуйте ещё раз или нажмите /menu.",
+ cancellationToken: cancellationToken);
+ return;
+ }
+
+ foreach (var keyword in keywords)
+ {
+ await _subscriptionRepository.AddSubscriptionAsync(chatId, keyword, cancellationToken);
+ }
+
+ _chatStates[chatId] = ConversationState.None;
+
+ var confirmation = new StringBuilder();
+ confirmation.AppendLine("Добавлены правила подписки по ключевым словам:");
+ confirmation.AppendLine(string.Join(", ", keywords));
+
+ await botClient.SendMessage(
+ chatId: message.Chat.Id,
+ text: confirmation.ToString(),
+ cancellationToken: cancellationToken);
+
+ _logger.LogInformation(
+ "Для чата {ChatId} добавлены правила подписки по ключевым словам: {Keywords}",
+ chatId,
+ string.Join(", ", keywords));
+
+ return;
+ }
+
+ // Если состояние не распознано — просто напоминаем про меню
+ await botClient.SendMessage(
+ chatId: message.Chat.Id,
+ text: "Используйте команду /menu для управления подписками.",
+ cancellationToken: cancellationToken);
+ }
+
+ private async Task HandleCallbackQueryAsync(ITelegramBotClient botClient, CallbackQuery callbackQuery, CancellationToken cancellationToken)
+ {
+ if (callbackQuery.Data is null)
+ {
+ return;
+ }
+
+ if (callbackQuery.Data.StartsWith("del:", StringComparison.Ordinal))
+ {
+ var idPart = callbackQuery.Data["del:".Length..];
+ if (!long.TryParse(idPart, CultureInfo.InvariantCulture, out var id))
+ {
+ return;
+ }
+
+ await _subscriptionRepository.DeleteSubscriptionAsync(id, cancellationToken);
+
+ await botClient.AnswerCallbackQuery(
+ callbackQueryId: callbackQuery.Id,
+ text: "Подписка удалена.",
+ cancellationToken: cancellationToken);
+
+ if (callbackQuery.Message is not null)
+ {
+ var chatId = callbackQuery.Message.Chat.Id;
+ var chatIdString = chatId.ToString(CultureInfo.InvariantCulture);
+ await ShowDeleteMenuAsync(chatId, chatIdString, cancellationToken);
+ }
+ }
+ }
+
+ private Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
+ {
+ var errorMessage = exception switch
+ {
+ ApiRequestException apiRequestException =>
+ $"Ошибка Telegram API: [{apiRequestException.ErrorCode}] {apiRequestException.Message}",
+ _ => exception.ToString()
+ };
+
+ _logger.LogError("Ошибка Telegram-бота: {Error}", errorMessage);
+ return Task.CompletedTask;
+ }
+
+ private async Task SendDealAsync(Deal deal, string chatId, CancellationToken cancellationToken)
+ {
var textBuilder = new StringBuilder();
- textBuilder.AppendLine($"🔥 *{Escape(deal.Title)}*");
+ textBuilder.AppendLine($"🔥 {deal.Title}");
if (deal.CurrentPrice is not null)
{
@@ -55,33 +278,102 @@ public class TelegramNotifier : ITelegramNotifier
}
textBuilder.AppendLine();
- textBuilder.AppendLine($"Магазин: {Escape(deal.StoreName)}");
+ textBuilder.AppendLine($"Магазин: {deal.StoreName}");
textBuilder.AppendLine();
- textBuilder.AppendLine($"[Открыть на Pepper.ru]({Escape(deal.DealUrl)})");
+ textBuilder.AppendLine($"Открыть на Pepper.ru: {deal.DealUrl}");
- var payload = new
+ try
{
- chat_id = _chatId,
- text = textBuilder.ToString(),
- parse_mode = "Markdown"
- };
-
- var response = await _httpClient.PostAsJsonAsync(url, payload, cancellationToken);
-
- if (!response.IsSuccessStatusCode)
+ await _botClient.SendMessage(
+ chatId: chatId,
+ text: textBuilder.ToString(),
+ cancellationToken: cancellationToken);
+ }
+ catch (ApiRequestException ex)
{
- var body = await response.Content.ReadAsStringAsync(cancellationToken);
- _logger.LogWarning("Не удалось отправить сообщение в Telegram: {Status} {Body}", response.StatusCode, body);
+ _logger.LogWarning(
+ ex,
+ "Не удалось отправить сообщение в Telegram (чат {ChatId}): [{Code}] {Message}",
+ chatId,
+ ex.ErrorCode,
+ ex.Message);
}
}
- private static string Escape(string value)
+ private Task SendMainMenuAsync(ChatId chatId, CancellationToken cancellationToken)
{
- return value
- .Replace("_", "\\_")
- .Replace("*", "\\*")
- .Replace("[", "\\[")
- .Replace("`", "\\`");
+ var keyboard = new ReplyKeyboardMarkup(new[]
+ {
+ new[] { new KeyboardButton("Добавить подписку") },
+ new[] { new KeyboardButton("Удалить подписку") },
+ new[] { new KeyboardButton("Список подписок") }
+ })
+ {
+ ResizeKeyboard = true,
+ OneTimeKeyboard = false
+ };
+
+ return _botClient.SendMessage(
+ chatId: chatId,
+ text: "Меню управления подписками:",
+ replyMarkup: keyboard,
+ cancellationToken: cancellationToken);
+ }
+
+ private async Task ShowSubscriptionsListAsync(ChatId chatId, string chatIdString, CancellationToken cancellationToken)
+ {
+ var subs = await _subscriptionRepository.GetByChatAsync(chatIdString, cancellationToken);
+
+ if (subs.Count == 0)
+ {
+ await _botClient.SendMessage(
+ chatId: chatId,
+ text: "У вас пока нет подписок.",
+ cancellationToken: cancellationToken);
+ return;
+ }
+
+ var sb = new StringBuilder();
+ sb.AppendLine("Ваши подписки:");
+ foreach (var sub in subs)
+ {
+ sb.AppendLine($"• [{sub.Id}] {sub.Keywords}");
+ }
+
+ await _botClient.SendMessage(
+ chatId: chatId,
+ text: sb.ToString(),
+ cancellationToken: cancellationToken);
+ }
+
+ private async Task ShowDeleteMenuAsync(ChatId chatId, string chatIdString, CancellationToken cancellationToken)
+ {
+ var subs = await _subscriptionRepository.GetByChatAsync(chatIdString, cancellationToken);
+
+ if (subs.Count == 0)
+ {
+ await _botClient.SendMessage(
+ chatId: chatId,
+ text: "Подписок для удаления не найдено.",
+ cancellationToken: cancellationToken);
+ return;
+ }
+
+ var buttons = subs
+ .Select(s => InlineKeyboardButton.WithCallbackData(
+ text: s.Keywords,
+ callbackData: $"del:{s.Id}"))
+ .Chunk(2)
+ .Select(chunk => chunk.ToArray())
+ .ToArray();
+
+ var keyboard = new InlineKeyboardMarkup(buttons);
+
+ await _botClient.SendMessage(
+ chatId: chatId,
+ text: "Выберите подписку для удаления:",
+ replyMarkup: keyboard,
+ cancellationToken: cancellationToken);
}
}
diff --git a/PepperBot/PepperBot.csproj b/PepperBot/PepperBot.csproj
index a52748c..fab46d5 100644
--- a/PepperBot/PepperBot.csproj
+++ b/PepperBot/PepperBot.csproj
@@ -15,6 +15,7 @@
+
diff --git a/PepperBot/Presentation/BotWorker.cs b/PepperBot/Presentation/BotWorker.cs
index 6a7b170..64fae49 100644
--- a/PepperBot/Presentation/BotWorker.cs
+++ b/PepperBot/Presentation/BotWorker.cs
@@ -9,23 +9,33 @@ public class BotWorker : BackgroundService
{
private readonly DealService _dealService;
private readonly IDealRepository _repository;
+ private readonly ISubscriptionRepository _subscriptionRepository;
+ private readonly ITelegramNotifier _telegramNotifier;
private readonly ILogger _logger;
public BotWorker(
DealService dealService,
IDealRepository repository,
+ ISubscriptionRepository subscriptionRepository,
+ ITelegramNotifier telegramNotifier,
ILogger logger)
{
_dealService = dealService;
_repository = repository;
+ _subscriptionRepository = subscriptionRepository;
+ _telegramNotifier = telegramNotifier;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await _repository.InitializeAsync(stoppingToken);
+ await _subscriptionRepository.InitializeAsync(stoppingToken);
- _logger.LogInformation("PepperBot запущен. Опрос каждые 30 секунд.");
+ // Вся работа с Telegram вынесена в TelegramNotifier
+ await _telegramNotifier.StartAsync(stoppingToken);
+
+ _logger.LogInformation("PepperBot запущен. Опрос скидок каждые 30 секунд.");
while (!stoppingToken.IsCancellationRequested)
{
@@ -42,4 +52,3 @@ public class BotWorker : BackgroundService
}
}
}
-
diff --git a/PepperBot/Program.cs b/PepperBot/Program.cs
index aaa6502..029ddbb 100644
--- a/PepperBot/Program.cs
+++ b/PepperBot/Program.cs
@@ -9,6 +9,7 @@ using PepperBot.Infrastructure.Data;
using PepperBot.Infrastructure.Health;
using PepperBot.Infrastructure.Pepper;
using PepperBot.Infrastructure.Telegram;
+using Telegram.Bot;
var builder = WebApplication.CreateBuilder(args);
@@ -19,11 +20,19 @@ builder.Services.AddLogging(logging =>
});
builder.Services.AddSingleton();
+builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
+// Клиент Telegram-бота для приёма личных сообщений
+builder.Services.AddSingleton(_ =>
+{
+ var token = Environment.GetEnvironmentVariable("TELEGRAM_BOT_TOKEN") ?? string.Empty;
+ return new TelegramBotClient(token);
+});
+
builder.Services.AddHostedService();
var app = builder.Build();