using System; using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Data.Sqlite; using Serilog; using Telegram.Bot; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.ReplyMarkups; class Program { private static string _botToken = string.Empty; private static TelegramBotClient _botClient = null!; private static Dictionary usersWaitingForReport = new Dictionary(); // Отслеживаем состояние пользователей private static HashSet admins = new HashSet(); // Хранение списка администраторов private static string adminPassword = "admin123"; // Простой пароль для администратора static async Task Main() { // Загружаем конфигурацию из appsettings.json try { Log.Information("Загрузка конфигурации из appsettings.json..."); var config = new ConfigurationBuilder() .SetBasePath(AppContext.BaseDirectory) // <-- Используем правильный путь .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Build(); _botToken = config["BotToken"] ?? throw new Exception("BotToken не найден в конфигурации!"); Log.Information("Конфигурация успешно загружена"); } catch (Exception ex) { Log.Error($"Ошибка при загрузке конфигурации: {ex.Message}"); throw; } Log.Logger = new LoggerConfiguration() .WriteTo.Console() .WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day) .CreateLogger(); Log.Information("Запуск Telegram-бота..."); try { _botClient = new TelegramBotClient(_botToken); var me = await _botClient.GetMe(); Log.Information($"Бот {me.FirstName} запущен! ID: {me.Id}"); } catch (Exception ex) { Log.Error($"Ошибка при подключении к Telegram API: {ex.Message}"); throw; } // Создание базы данных и таблицы, если они не существуют await CreateDatabaseIfNotExists(); var cts = new CancellationTokenSource(); Log.Information("Начало получения обновлений..."); try { // Применение StartReceiving для работы с задачами _botClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, cancellationToken: cts.Token); Log.Information("Получение обновлений успешно началось."); } catch (Exception ex) { Log.Error($"Ошибка при запуске получения обновлений: {ex.Message}"); throw; } // Создание TaskCompletionSource для удержания процесса бота var tcs = new TaskCompletionSource(); await tcs.Task; // Это заставит бота работать до тех пор, пока не будет отменен // Ожидаем отмены через token cts.Token.WaitHandle.WaitOne(); } private static async Task DeletePreviousMessage(ITelegramBotClient botClient, long chatId, int messageId) { try { await botClient.DeleteMessage(chatId, messageId); Log.Information($"Сообщение {messageId} в чате {chatId} удалено."); } catch (Exception ex) { Log.Error($"Ошибка при удалении сообщения {messageId} в чате {chatId}: {ex.Message}"); } } private static async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) { try { // Обработка нажатий на кнопки if (update.Type == UpdateType.CallbackQuery) { var callbackQuery = update.CallbackQuery; if (callbackQuery?.From != null) { long chatId = callbackQuery.From.Id; string? data = callbackQuery.Data; if (callbackQuery?.Message?.MessageId != null) { int messageId = callbackQuery.Message.MessageId; await DeletePreviousMessage(botClient, chatId, messageId); // Удаляем предыдущее сообщение } if (callbackQuery?.Id != null) { await botClient.AnswerCallbackQuery(callbackQuery.Id); // Убираем "часики" у кнопки } if (data == "report") { usersWaitingForReport[chatId] = true; userReportSteps[chatId] = 1; userReports[chatId] = new Report(); await botClient.SendMessage(chatId, "Пожалуйста, укажите приоритет (низкий, средний, высокий)."); Log.Information($"Пользователь {chatId} начал создание заявки"); } else if (data == "admin_panel") { if (admins.Contains(chatId)) { await SendAdminPanel(botClient, chatId); } else { var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("Главное меню", "main_menu") } }); await botClient.SendMessage(chatId, "⛔ Вы не являетесь администратором!", replyMarkup: keyboard); Log.Information($"Неавторизованный доступ к админ-панели от {chatId}"); } } else if (data == "view_reports") { if (admins.Contains(chatId)) { await ViewReports(botClient, chatId); } else { var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("Главное меню", "main_menu") } }); await botClient.SendMessage(chatId, "⛔ Вы не являетесь администратором!", replyMarkup: keyboard); Log.Information($"Неавторизованный доступ к заявкам от {chatId}"); } } else if (data == "view_archived_reports") { if (admins.Contains(chatId)) { await ViewArchivedReports(botClient, chatId); } else { var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("Главное меню", "main_menu") } }); await botClient.SendMessage(chatId, "⛔ Вы не являетесь администратором!", replyMarkup: keyboard); Log.Information($"Неавторизованный доступ к архиву заявок от {chatId}"); } } else if (data != null && data.StartsWith("report_")) { long reportId = long.Parse(data.Substring(7)); if (callbackQuery?.Message?.MessageId != null) { int messageId = callbackQuery.Message.MessageId; await ShowReportDetails(botClient, chatId, reportId, messageId); } } else if (data != null && data.StartsWith("status_")) { string[] parts = data.Split('_'); long reportId = long.Parse(parts[1]); string newStatus = parts[2]; if (callbackQuery?.Message?.MessageId != null) { int messageId = callbackQuery.Message.MessageId; await UpdateReportStatus(reportId, newStatus); await ShowReportDetails(botClient, chatId, reportId, messageId); } } else if (data != null && data.StartsWith("delete_")) { long reportId = long.Parse(data.Substring(7)); await DeleteReport(botClient, chatId, reportId); } else if (data == "back_to_list") { await ViewReports(botClient, chatId); } else if (data == "main_menu") { await SendMainMenu(botClient, chatId); } } } // Обработка текстовых сообщений if (update.Type == UpdateType.Message && update.Message?.Text != null) { var message = update.Message; Log.Information($"Получено сообщение от {message.Chat.Id}: {message.Text}"); // Обработка команды /admin для авторизации if (message.Text.StartsWith("/admin")) { string[] parts = message.Text.Split(' '); if (parts.Length == 2 && parts[1] == adminPassword) { admins.Add(message.Chat.Id); var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("Главное меню", "main_menu") } }); await botClient.SendMessage(message.Chat.Id, "✅ Вы авторизованы как администратор!", replyMarkup: keyboard); Log.Information($"Новый администратор: {message.Chat.Id}"); } else { var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("Главное меню", "main_menu") } }); await botClient.SendMessage(message.Chat.Id, "❌ Неверный пароль!", replyMarkup: keyboard); } return; } if (message.Text == "/start") { var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("Подать заявку", "report"), InlineKeyboardButton.WithCallbackData("Панель администратора", "admin_panel") } }); await botClient.SendMessage( chatId: message.Chat.Id, text: "Привет! Я бот для сбора заявок на ремонт оборудования.", replyMarkup: keyboard ); Log.Information($"Ответ на команду /start с кнопками отправлен."); } else if (usersWaitingForReport.TryGetValue(message.Chat.Id, out bool isWaiting) && isWaiting) { if (userReportSteps.TryGetValue(message.Chat.Id, out int step)) { switch (step) { case 1: userReports[message.Chat.Id].Priority = message.Text; userReportSteps[message.Chat.Id] = 2; await botClient.SendMessage(message.Chat.Id, "Пожалуйста, укажите кабинет."); break; case 2: userReports[message.Chat.Id].Room = message.Text; userReportSteps[message.Chat.Id] = 3; await botClient.SendMessage(message.Chat.Id, "Пожалуйста, опишите проблему."); break; case 3: userReports[message.Chat.Id].Description = message.Text; userReportSteps[message.Chat.Id] = 4; await botClient.SendMessage(message.Chat.Id, "Пожалуйста, укажите ваше ФИО."); break; case 4: userReports[message.Chat.Id].ReporterName = message.Text; await SaveReportToDatabase(message.Chat.Id, userReports[message.Chat.Id]); await botClient.SendMessage( message.Chat.Id, "✅ Спасибо за заявку! Мы обработаем её в ближайшее время." ); usersWaitingForReport[message.Chat.Id] = false; userReportSteps.Remove(message.Chat.Id); userReports.Remove(message.Chat.Id); Log.Information($"Заявка пользователя {message.Chat.Id} сохранена в базе данных."); break; } } } else { await botClient.SendMessage(message.Chat.Id, "ℹ️ Используйте команду /start для начала работы с ботом."); } } } catch (Exception ex) { Log.Error($"Ошибка при обработке обновлений: {ex.Message}"); Log.Error($"StackTrace: {ex.StackTrace}"); } } private static async Task DeleteReport(ITelegramBotClient botClient, long chatId, long reportId) { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "DELETE FROM Reports WHERE Id = @id"; command.Parameters.AddWithValue("@id", reportId); await command.ExecuteNonQueryAsync(); var deletionMessage = await botClient.SendMessage(chatId, $"Заявка #{reportId} успешно удалена."); Log.Information($"Заявка #{reportId} удалена пользователем {chatId}."); // Ждем 2 секунды await Task.Delay(2000); // Удаляем сообщение об удалении await botClient.DeleteMessage(chatId, deletionMessage.MessageId); // Отображаем список заявок await ViewReports(botClient, chatId); } } catch (Exception ex) { Log.Error($"Ошибка при удалении заявки #{reportId}: {ex.Message}"); await botClient.SendMessage(chatId, $"Ошибка при удалении заявки #{reportId}."); } } private static async Task SendMainMenu(ITelegramBotClient botClient, long chatId) { var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("Подать заявку", "report"), InlineKeyboardButton.WithCallbackData("Панель администратора", "admin_panel") } }); await botClient.SendMessage( chatId: chatId, text: "Главное меню:", replyMarkup: keyboard ); } private static async Task SendAdminPanel(ITelegramBotClient botClient, long chatId) { var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("Менеджер заявок", "view_reports"), InlineKeyboardButton.WithCallbackData("Архив заявок", "view_archived_reports") }, new[] { InlineKeyboardButton.WithCallbackData("Главное меню", "main_menu") } }); await botClient.SendMessage( chatId: chatId, text: "Панель администраторов:", replyMarkup: keyboard ); } private static async Task ViewReports(ITelegramBotClient botClient, long chatId) { string connectionString = "Data Source=bot.db"; try { using (var connection = new SqliteConnection(connectionString)) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "SELECT Id, Description, Status FROM Reports WHERE Status != 'закрыта'"; var buttons = new List(); using (var reader = await command.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { long id = reader.GetInt64(0); string description = reader.GetString(1).Substring(0, Math.Min(20, reader.GetString(1).Length)); string status = reader.GetString(2); buttons.Add(new[] { InlineKeyboardButton.WithCallbackData( $"#{id} - {status} - {description}...", $"report_{id}") }); } } // Добавляем кнопки навигации buttons.Add(new[] { InlineKeyboardButton.WithCallbackData("Главное меню", "main_menu") }); await botClient.SendMessage( chatId: chatId, text: "Список заявок:", replyMarkup: new InlineKeyboardMarkup(buttons) ); } } catch (Exception ex) { Log.Error($"Ошибка: {ex.Message}"); await botClient.SendMessage(chatId, "Ошибка при получении заявок"); } } private static async Task ViewArchivedReports(ITelegramBotClient botClient, long chatId) { string connectionString = "Data Source=bot.db"; try { using (var connection = new SqliteConnection(connectionString)) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "SELECT Id, Description, Status FROM Reports WHERE Status = 'закрыта'"; var buttons = new List(); using (var reader = await command.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { long id = reader.GetInt64(0); string description = reader.GetString(1).Substring(0, Math.Min(20, reader.GetString(1).Length)); string status = reader.GetString(2); buttons.Add(new[] { InlineKeyboardButton.WithCallbackData( $"#{id} - {status} - {description}...", $"report_{id}") }); } } // Добавляем кнопки навигации buttons.Add(new[] { InlineKeyboardButton.WithCallbackData("Главное меню", "main_menu") }); await botClient.SendMessage( chatId: chatId, text: "Архив заявок:", replyMarkup: new InlineKeyboardMarkup(buttons) ); } } catch (Exception ex) { Log.Error($"Ошибка: {ex.Message}"); await botClient.SendMessage(chatId, "Ошибка при получении архива заявок"); } } private static async Task ShowReportDetails(ITelegramBotClient botClient, long chatId, long reportId, int messageId) { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "SELECT Description, Status FROM Reports WHERE Id = @id"; command.Parameters.AddWithValue("@id", reportId); using (var reader = await command.ExecuteReaderAsync()) { if (await reader.ReadAsync()) { string description = reader.GetString(0); string status = reader.GetString(1); var statusButtons = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("🟡 Ожидает", $"status_{reportId}_ожидает"), InlineKeyboardButton.WithCallbackData("🔵 В работе", $"status_{reportId}_в работе") }, new[] { InlineKeyboardButton.WithCallbackData("🟢 Закрыта", $"status_{reportId}_закрыта") }, new[] { InlineKeyboardButton.WithCallbackData("❌ Удалить заявку", $"delete_{reportId}") }, new[] { InlineKeyboardButton.WithCallbackData("Назад", "back_to_list"), InlineKeyboardButton.WithCallbackData("Главное меню", "main_menu") } }); string newText = $"Заявка #{reportId}\n\nОписание: {description}\nСтатус: {status}"; await botClient.SendMessage( chatId: chatId, text: newText, replyMarkup: statusButtons ); } } } } catch (Exception ex) { Log.Error($"Ошибка: {ex.Message}"); } } private static async Task UpdateReportStatus(long reportId, string newStatus) { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "UPDATE Reports SET Status = @status WHERE Id = @id"; command.Parameters.AddWithValue("@status", newStatus); command.Parameters.AddWithValue("@id", reportId); await command.ExecuteNonQueryAsync(); } } catch (Exception ex) { Log.Error($"Ошибка: {ex.Message}"); } } private static async Task SaveReportToDatabase(long chatId, Report report) { string connectionString = "Data Source=bot.db"; // Используем SQLite try { using (var connection = new SqliteConnection(connectionString)) { await connection.OpenAsync(); var insertCommand = connection.CreateCommand(); insertCommand.CommandText = @" INSERT INTO Reports (ChatId, Priority, Room, Description, ReporterName, Status) VALUES (@ChatId, @Priority, @Room, @Description, @ReporterName, 'ожидает'); "; insertCommand.Parameters.AddWithValue("@ChatId", chatId); insertCommand.Parameters.AddWithValue("@Priority", report.Priority); insertCommand.Parameters.AddWithValue("@Room", report.Room); insertCommand.Parameters.AddWithValue("@Description", report.Description); insertCommand.Parameters.AddWithValue("@ReporterName", report.ReporterName); await insertCommand.ExecuteNonQueryAsync(); Log.Information($"Заявка от пользователя {chatId} успешно сохранена."); } } catch (Exception ex) { Log.Error($"Ошибка при сохранении заявки в базу данных: {ex.Message}"); } } private static async Task CreateDatabaseIfNotExists() { string connectionString = "Data Source=bot.db"; // Путь к вашей базе данных try { using (var connection = new SqliteConnection(connectionString)) { await connection.OpenAsync(); // Проверяем, существует ли таблица Reports var checkTableCommand = connection.CreateCommand(); checkTableCommand.CommandText = "PRAGMA table_info(Reports);"; var tableInfo = await checkTableCommand.ExecuteReaderAsync(); bool priorityColumnExists = false; while (await tableInfo.ReadAsync()) { if (tableInfo["name"].ToString() == "Priority") { priorityColumnExists = true; break; } } if (!priorityColumnExists) { // Добавляем столбец Priority, если его нет var alterTableCommand = connection.CreateCommand(); alterTableCommand.CommandText = "ALTER TABLE Reports ADD COLUMN Priority TEXT NOT NULL DEFAULT 'низкий';"; await alterTableCommand.ExecuteNonQueryAsync(); Log.Information("Столбец Priority добавлен в таблицу Reports."); } // Создаем таблицу, если её не существует var createTableCommand = connection.CreateCommand(); createTableCommand.CommandText = @" CREATE TABLE IF NOT EXISTS Reports ( Id INTEGER PRIMARY KEY AUTOINCREMENT, ChatId INTEGER NOT NULL, Priority TEXT NOT NULL, Room TEXT NOT NULL, Description TEXT NOT NULL, ReporterName TEXT NOT NULL, DateCreated DATETIME DEFAULT CURRENT_TIMESTAMP, Status TEXT DEFAULT 'ожидает' ); "; await createTableCommand.ExecuteNonQueryAsync(); Log.Information("Таблица Reports успешно создана (если её не было)."); } } catch (Exception ex) { Log.Error($"Ошибка при создании таблицы в базе данных: {ex.Message}"); } } private static Dictionary userReportSteps = new Dictionary(); private static Dictionary userReports = new Dictionary(); private class Report { public string Priority { get; set; } = string.Empty; public string Room { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string ReporterName { get; set; } = string.Empty; } private static Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken) { Log.Error($"Ошибка в процессе работы с ботом: {exception.Message}"); Log.Error($"StackTrace: {exception.StackTrace}"); return Task.CompletedTask; } }