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"; // Простой пароль для администратора private static Dictionary adminFullNames = new Dictionary(); private static HashSet superAdmins = new HashSet(); // Хранение списка суперпользователей private static string superAdminPassword = "superadmin123"; // Пароль для суперпользователя 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(); await CreateUserSettingsTableIfNotExists(); // Загрузка списка администраторов из базы данных await LoadAdminsFromDatabase(); Log.Information($"Загружено {admins.Count} администраторов из базы данных"); 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 MonitorReportStatus(long reportId, CancellationToken token) { // Ждем 1 час перед первой проверкой await Task.Delay(TimeSpan.FromHours(1), token); while (!token.IsCancellationRequested) { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = @" SELECT Status FROM Reports WHERE Id = @id"; command.Parameters.AddWithValue("@id", reportId); var status = (string?)await command.ExecuteScalarAsync(); // Проверяем, существует ли заявка if (status == null) { // Если заявка была удалена, прекращаем мониторинг Log.Information($"Заявка #{reportId} была удалена, прекращаем мониторинг."); return; } if (status == "в работе" || status == "закрыта") { // Если статус изменился на "в работе" или "закрыта", прекращаем мониторинг return; } var getAdminsCommand = connection.CreateCommand(); getAdminsCommand.CommandText = "SELECT ChatId, NotifyOnPendingReports FROM Admins"; using (var reader = await getAdminsCommand.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { long adminId = reader.GetInt64(0); bool notifyOnPendingReports = reader.GetInt32(1) == 1; if (notifyOnPendingReports) { await _botClient.SendMessage( chatId: adminId, text: $"⚠️ Заявка #{reportId} остается в статусе 'Ожидает'." ); } } } } } catch (Exception ex) { Log.Error($"Ошибка при мониторинге статуса заявки #{reportId}: {ex.Message}"); } // Ждем 1 час перед следующей проверкой await Task.Delay(TimeSpan.FromHours(1), token); } } private static async Task DeletePreviousMessage(ITelegramBotClient botClient, long chatId, int messageId) { try { await botClient.DeleteMessage(chatId, messageId); } catch (Exception ex) { Log.Error($"Ошибка при удалении сообщения {messageId} в чате {chatId}: {ex.Message}"); } } private static async Task CreateUserSettingsTableIfNotExists() { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = @" CREATE TABLE IF NOT EXISTS UserSettings ( UserId INTEGER PRIMARY KEY, NotificationsEnabled INTEGER NOT NULL DEFAULT 1 );"; await command.ExecuteNonQueryAsync(); Log.Information("Таблица UserSettings успешно создана (если её не было)."); } } catch (Exception ex) { Log.Error($"Ошибка при создании таблицы UserSettings: {ex.Message}"); } } private static async Task ViewUserReports(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, Priority FROM Reports WHERE ChatId = @chatId"; command.Parameters.AddWithValue("@chatId", chatId); 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); string priority = reader.GetString(3); string statusEmoji = GetStatusEmoji(status); string priorityMarker = priority.ToLower() == "высокий" ? "⚠️ " : ""; buttons.Add(new[] { InlineKeyboardButton.WithCallbackData( $"{priorityMarker}#{id} - {statusEmoji} {status} - {description}...", $"user_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 ShowUserSettings(ITelegramBotClient botClient, long chatId) { try { // Получаем текущие настройки пользователя bool notificationsEnabled = true; // По умолчанию уведомления включены using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "SELECT NotificationsEnabled FROM UserSettings WHERE UserId = @userId"; command.Parameters.AddWithValue("@userId", chatId); var result = await command.ExecuteScalarAsync(); // Отладочная информация Log.Information($"[Отладка] Для пользователя {chatId} проверяем настройки уведомлений. Результат из БД: {result}"); if (result != null && result != DBNull.Value) { int dbValue = Convert.ToInt32(result); notificationsEnabled = dbValue == 1; Log.Information($"[Отладка] Для пользователя {chatId} настройка уведомлений из БД: {dbValue} => notificationsEnabled={notificationsEnabled}"); } else { // Если запись для пользователя отсутствует, создаем ее var insertCommand = connection.CreateCommand(); insertCommand.CommandText = "INSERT INTO UserSettings (UserId, NotificationsEnabled) VALUES (@userId, 1)"; insertCommand.Parameters.AddWithValue("@userId", chatId); await insertCommand.ExecuteNonQueryAsync(); Log.Information($"[Отладка] Создали запись для пользователя {chatId} с notificationsEnabled=true"); } } // Создаем клавиатуру с настройками string notificationStatus = notificationsEnabled ? "✅ Включены" : "❌ Отключены"; string toggleAction = notificationsEnabled ? "toggle_off" : "toggle_on"; Log.Information($"[Отладка] Для пользователя {chatId} показываем статус: {notificationStatus}, toggleAction: {toggleAction}"); var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData($"Уведомления: {notificationStatus}", $"notifications_{toggleAction}") }, new[] { InlineKeyboardButton.WithCallbackData("🔙 Назад в меню", "main_menu") } }); await botClient.SendMessage( chatId: chatId, text: "⚙️ Настройки пользователя\n\n" + "Здесь вы можете настроить параметры бота:\n\n" + "Уведомления включают:\n" + "- Уведомления о смене статуса ваших заявок\n", parseMode: ParseMode.Html, replyMarkup: keyboard ); } catch (Exception ex) { Log.Error($"Ошибка при отображении настроек пользователя: {ex.Message}"); Log.Error($"StackTrace: {ex.StackTrace}"); await botClient.SendMessage(chatId, "Произошла ошибка при загрузке настроек."); } } 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(); var priorityKeyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("Низкий", "priority_low"), InlineKeyboardButton.WithCallbackData("Средний", "priority_medium"), InlineKeyboardButton.WithCallbackData("Высокий", "priority_high") } }); await botClient.SendMessage(chatId, "Пожалуйста, выберите приоритет:", replyMarkup: priorityKeyboard); Log.Information($"Пользователь {chatId} начал создание заявки"); } else if (data == "admin_settings") { if (admins.Contains(chatId)) { await ShowAdminSettings(botClient, chatId); } else { await botClient.SendMessage(chatId, "⛔ У вас нет прав для доступа к настройкам администраторов!"); await Task.Delay(2000); await SendMainMenu(botClient, chatId); } } else if (data == "view_admins") { if (admins.Contains(chatId)) { await ShowAdminsList(botClient, chatId); } else { await botClient.SendMessage(chatId, "⛔ У вас нет прав для просмотра администраторов!"); await Task.Delay(2000); await SendMainMenu(botClient, chatId); } } else if (data != null && data.StartsWith("removeadmin_")) { long adminIdToRemove = long.Parse(data.Substring(11).Replace("_", "")); // Проверяем, что пользователь является администратором if (admins.Contains(chatId)) { // Проверяем, не пытается ли пользователь удалить себя if (adminIdToRemove == chatId) { var warningMessage = await botClient.SendMessage( chatId: chatId, text: "⚠️ Вы не можете удалить себя из администраторов!" ); await Task.Delay(2000); await botClient.DeleteMessage(chatId, warningMessage.MessageId); await ShowAdminsList(botClient, chatId); } else { await RemoveAdminFromDatabase(adminIdToRemove); string username = "администратор"; try { var user = await botClient.GetChat(adminIdToRemove); username = user.Username ?? user.FirstName ?? "администратор"; } catch { } await botClient.SendMessage( chatId: chatId, text: $"✅ Пользователь {username} (ID: {adminIdToRemove}) удален из администраторов." ); await Task.Delay(2000); await ShowAdminsList(botClient, chatId); } } else { await botClient.SendMessage(chatId, "⛔ У вас нет прав для удаления администраторов!"); await Task.Delay(2000); await SendMainMenu(botClient, chatId); } } else if (data != null && data.StartsWith("enable_notifications_")) { long adminId = long.Parse(data.Substring("enable_notifications_".Length)); await ToggleAdminNotifications(adminId, true); await ShowAdminSettings(botClient, chatId); } else if (data != null && data.StartsWith("disable_notifications_")) { long adminId = long.Parse(data.Substring("disable_notifications_".Length)); await ToggleAdminNotifications(adminId, false); await ShowAdminSettings(botClient, chatId); } else if (data == "user_reports") { await ViewUserReports(botClient, chatId); } else if (data != null && data.StartsWith("user_report_")) { long reportId = long.Parse(data.Substring("user_report_".Length)); if (callbackQuery?.Message?.MessageId != null) { int messageId = callbackQuery.Message.MessageId; 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 == "leave_admin") { bool isSuperUser = superAdmins.Contains(chatId); await RemoveSelfFromAdmins(chatId); var message = await botClient.SendMessage( chatId: chatId, text: isSuperUser ? "✅ Вы вышли из режима суперпользователя." : "✅ Вы вышли из режима администратора." ); await Task.Delay(2000); await botClient.DeleteMessage(chatId, message.MessageId); await SendMainMenu(botClient, chatId); } else if (data != null && data.StartsWith("removeadmin_")) { long adminIdToRemove = long.Parse(data.Substring(11).Replace("_", "")); // Проверяем, что пользователь является суперпользователем if (superAdmins.Contains(chatId)) { // Проверяем, не пытается ли пользователь удалить себя if (adminIdToRemove == chatId) { var warningMessage = await botClient.SendMessage( chatId: chatId, text: "⚠️ Вы не можете удалить себя из администраторов через эту функцию. Используйте 'Выйти из режима администратора'!" ); await Task.Delay(2000); await botClient.DeleteMessage(chatId, warningMessage.MessageId); await ShowAdminsList(botClient, chatId); } else { await RemoveAdminFromDatabase(adminIdToRemove); string username = "администратор"; try { var user = await botClient.GetChat(adminIdToRemove); username = user.Username ?? user.FirstName ?? "администратор"; } catch { } await botClient.SendMessage( chatId: chatId, text: $"✅ Пользователь {username} (ID: {adminIdToRemove}) удален из администраторов." ); await Task.Delay(2000); await ShowAdminsList(botClient, chatId); } } else { await botClient.SendMessage(chatId, "⛔ У вас нет прав для удаления администраторов! Только суперпользователи могут управлять списком администраторов."); await Task.Delay(2000); await SendMainMenu(botClient, chatId); } } // Также добавим обработку команды /removeadmin в секцию обработки текстовых сообщений: // В блоке для обработки текстовых сообщений (после if (message.Text.StartsWith("/admin"))) else if (update.Type == UpdateType.Message && update.Message?.Text != null) { var message = update.Message; Log.Information($"Получено сообщение от {message.Chat.Id}: {message.Text}"); if (message.Text.StartsWith("/admin")) { string[] parts = message.Text.Split(' '); if (parts.Length == 2 && parts[1] == adminPassword) { admins.Add(message.Chat.Id); // Сохраняем администратора в базу данных await SaveAdminToDatabase(message.Chat.Id, ""); // Добавляем пустую строку для fullName var authMessage = await botClient.SendMessage(message.Chat.Id, "✅ Вы авторизованы как администратор!"); Log.Information($"Новый администратор: {message.Chat.Id}"); await Task.Delay(2000); await botClient.DeleteMessage(message.Chat.Id, authMessage.MessageId); await SendMainMenu(botClient, message.Chat.Id); } else { var authMessage = await botClient.SendMessage(message.Chat.Id, "❌ Неверный пароль!"); await Task.Delay(2000); await botClient.DeleteMessage(message.Chat.Id, authMessage.MessageId); await SendMainMenu(botClient, message.Chat.Id); } return; } } else if (data == "admin_panel") { if (admins.Contains(chatId)) { await SendAdminPanel(botClient, chatId); } else { var authMessage = await botClient.SendMessage(chatId, "⛔ Вы не являетесь администратором!"); Log.Information($"Неавторизованный доступ к админ-панели от {chatId}"); await Task.Delay(2000); await botClient.DeleteMessage(chatId, authMessage.MessageId); await SendMainMenu(botClient, chatId); } } else if (data == "view_reports") { if (admins.Contains(chatId)) { await ViewReports(botClient, chatId); } else { var authMessage = await botClient.SendMessage(chatId, "⛔ Вы не являетесь администратором!"); Log.Information($"Неавторизованный доступ к заявкам от {chatId}"); await Task.Delay(2000); await botClient.DeleteMessage(chatId, authMessage.MessageId); await SendMainMenu(botClient, chatId); } } else if (data == "view_archived_reports") { if (admins.Contains(chatId)) { await ViewArchivedReports(botClient, chatId); } else { var authMessage = await botClient.SendMessage(chatId, "⛔ Вы не являетесь администратором!"); Log.Information($"Неавторизованный доступ к архиву заявок от {chatId}"); await Task.Delay(2000); await botClient.DeleteMessage(chatId, authMessage.MessageId); await SendMainMenu(botClient, chatId); } } else if (data == "delete_all_reports") { if (superAdmins.Contains(chatId)) { var confirmKeyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("✅ Подтвердить", "confirm_delete_all_reports"), InlineKeyboardButton.WithCallbackData("❌ Отмена", "admin_panel") } }); await botClient.SendMessage( chatId: chatId, text: "Вы уверены, что хотите удалить все заявки? Это действие необратимо.", replyMarkup: confirmKeyboard ); } else { await botClient.SendMessage(chatId, "⛔ У вас нет прав для удаления всех заявок!"); await Task.Delay(2000); await SendMainMenu(botClient, chatId); } } else if (data == "confirm_delete_all_reports") { if (admins.Contains(chatId)) { await DeleteAllReports(botClient, chatId); } else { await botClient.SendMessage(chatId, "⛔ У вас нет прав для удаления всех заявок!"); await Task.Delay(2000); await SendMainMenu(botClient, 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, chatId); 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 == "back_to_admin_panel") { await SendAdminPanel(botClient, chatId); } else if (data == "main_menu") { await SendMainMenu(botClient, chatId); } else if (data == "user_settings") { await ShowUserSettings(botClient, chatId); } else if (data != null && data.StartsWith("notifications_toggle_")) { string action = data.Substring("notifications_toggle_".Length); // "on" или "off" await ToggleNotifications(botClient, chatId, action); } else if (data != null && data.StartsWith("priority_")) { string priority = data.Substring(9); userReports[chatId].Priority = priority; userReportSteps[chatId] = 2; await botClient.SendMessage(chatId, "Пожалуйста, укажите кабинет."); } } } // Обработка текстовых сообщений if (update.Type == UpdateType.Message && update.Message?.Text != null) { var message = update.Message; // Обработка текстовых сообщений if (update.Type == UpdateType.Message && update.Message?.Text != null) { var receivedMessage = update.Message; Log.Information($"Получено сообщение от {receivedMessage.Chat.Id}: {receivedMessage.Text}"); if (receivedMessage.Text.StartsWith("/superadm")) { string[] parts = receivedMessage.Text.Split(new[] { ' ' }, 3); // Разделяем на 3 части: команду, пароль и остаток как ФИО if (parts.Length >= 3 && parts[1] == superAdminPassword) { string fullName = parts[2]; // Вся оставшаяся строка - это ФИО admins.Add(receivedMessage.Chat.Id); superAdmins.Add(receivedMessage.Chat.Id); adminFullNames[receivedMessage.Chat.Id] = fullName; // Сохраняем суперпользователя в базу данных await SaveSuperAdminToDatabase(receivedMessage.Chat.Id, fullName); var authMessage = await botClient.SendMessage(receivedMessage.Chat.Id, $"✅ Вы авторизованы как суперпользователь, {fullName}!"); Log.Information($"Новый суперпользователь: {receivedMessage.Chat.Id}, ФИО: {fullName}"); await Task.Delay(2000); await botClient.DeleteMessage(receivedMessage.Chat.Id, authMessage.MessageId); await SendMainMenu(botClient, receivedMessage.Chat.Id); } else { var authMessage = await botClient.SendMessage(receivedMessage.Chat.Id, "❌ Неверный формат команды или пароль! Используйте: /superadm пароль Фамилия Имя Отчество"); await Task.Delay(2000); await botClient.DeleteMessage(receivedMessage.Chat.Id, authMessage.MessageId); await SendMainMenu(botClient, receivedMessage.Chat.Id); } return; } // Обработка обычной команды администратора else if (receivedMessage.Text.StartsWith("/admin")) { string[] parts = receivedMessage.Text.Split(new[] { ' ' }, 3); // Разделяем на 3 части: команду, пароль и остаток как ФИО if (parts.Length >= 3 && parts[1] == adminPassword) { string fullName = parts[2]; // Вся оставшаяся строка - это ФИО admins.Add(receivedMessage.Chat.Id); adminFullNames[receivedMessage.Chat.Id] = fullName; // Сохраняем администратора в базу данных, убедившись что он не суперпользователь await SaveAdminToDatabase(receivedMessage.Chat.Id, fullName); var authMessage = await botClient.SendMessage(receivedMessage.Chat.Id, $"✅ Вы авторизованы как администратор, {fullName}!"); Log.Information($"Новый администратор: {receivedMessage.Chat.Id}, ФИО: {fullName}"); await Task.Delay(2000); await botClient.DeleteMessage(receivedMessage.Chat.Id, authMessage.MessageId); await SendMainMenu(botClient, receivedMessage.Chat.Id); } else { var authMessage = await botClient.SendMessage(receivedMessage.Chat.Id, "❌ Неверный формат команды или пароль! Используйте: /admin пароль Фамилия Имя Отчество"); await Task.Delay(2000); await botClient.DeleteMessage(receivedMessage.Chat.Id, authMessage.MessageId); await SendMainMenu(botClient, receivedMessage.Chat.Id); } return; } // В блоке для обработки текстовых сообщений else if (message.Text == "/admins") { if (admins.Contains(message.Chat.Id)) { await ShowAdminsList(botClient, message.Chat.Id); } else { var authMessage = await botClient.SendMessage( chatId: message.Chat.Id, text: "⛔ У вас нет прав для просмотра списка администраторов!" ); await Task.Delay(2000); await botClient.DeleteMessage(message.Chat.Id, authMessage.MessageId); await SendMainMenu(botClient, message.Chat.Id); } } if (message.Text == "/start") { await SendMainMenu(botClient, message.Chat.Id); 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 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]); var mainMenuKeyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("Главное меню", "main_menu") } }); await botClient.SendMessage( message.Chat.Id, "✅ Спасибо за заявку! Мы обработаем её в ближайшее время.", replyMarkup: mainMenuKeyboard ); 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 DeleteAllReports(ITelegramBotClient botClient, long chatId) { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "DELETE FROM Reports"; await command.ExecuteNonQueryAsync(); var deletionMessage = await botClient.SendMessage(chatId, "Все заявки успешно удалены."); Log.Information($"Все заявки удалены пользователем {chatId}."); // Ждем 2 секунды await Task.Delay(2000); // Удаляем сообщение об удалении await botClient.DeleteMessage(chatId, deletionMessage.MessageId); // Возвращаемся к панели администратора await SendAdminPanel(botClient, chatId); } } catch (Exception ex) { Log.Error($"Ошибка при удалении всех заявок: {ex.Message}"); await botClient.SendMessage(chatId, "Ошибка при удалении всех заявок."); } } 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 = "SELECT ChatId FROM Reports WHERE Id = @id"; command.Parameters.AddWithValue("@id", reportId); var ownerChatId = (long?)await command.ExecuteScalarAsync(); if (ownerChatId == null) { await botClient.SendMessage(chatId, $"Заявка #{reportId} не найдена."); return; } if (ownerChatId != chatId && !admins.Contains(chatId)) { await botClient.SendMessage(chatId, "⛔ Вы не можете удалить эту заявку, так как она не принадлежит вам."); return; } command.CommandText = "DELETE FROM Reports WHERE Id = @id"; 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 ViewUserReports(botClient, chatId); } } catch (Exception ex) { Log.Error($"Ошибка при удалении заявки #{reportId}: {ex.Message}"); await botClient.SendMessage(chatId, $"Ошибка при удалении заявки #{reportId}."); } } private static async Task NotifyUserAboutStatusChange(long chatId, long reportId, string newStatus) { try { // Проверяем настройки пользователя bool notificationsEnabled = true; // По умолчанию уведомления включены using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "SELECT NotificationsEnabled FROM UserSettings WHERE UserId = @userId"; command.Parameters.AddWithValue("@userId", chatId); var result = await command.ExecuteScalarAsync(); if (result != null && result != DBNull.Value) { notificationsEnabled = Convert.ToInt32(result) == 1; } } // Если уведомления отключены, не отправляем сообщение if (!notificationsEnabled) { Log.Information($"Уведомление о смене статуса заявки #{reportId} не отправлено пользователю {chatId} (уведомления отключены)"); return; } string statusEmoji = GetStatusEmoji(newStatus); await _botClient.SendMessage( chatId: chatId, text: $"{statusEmoji} Статус вашей заявки #{reportId} был изменен на: {newStatus}", parseMode: ParseMode.Html ); Log.Information($"Уведомление о смене статуса заявки #{reportId} отправлено пользователю {chatId}"); } catch (Exception ex) { Log.Error($"Ошибка при отправке уведомления пользователю о смене статуса: {ex.Message}"); } } private static async Task ToggleNotifications(ITelegramBotClient botClient, long chatId, string action) { try { // Проверка текущего состояния перед изменением bool currentState = true; using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var checkCommand = connection.CreateCommand(); checkCommand.CommandText = "SELECT NotificationsEnabled FROM UserSettings WHERE UserId = @userId"; checkCommand.Parameters.AddWithValue("@userId", chatId); var result = await checkCommand.ExecuteScalarAsync(); if (result != null && result != DBNull.Value) { currentState = Convert.ToInt32(result) == 1; } } Log.Information($"[Отладка] Переключение уведомлений для пользователя {chatId}: текущее состояние={currentState}, action={action}"); // Если action = "toggle_on", то нужно ВКЛЮЧИТЬ уведомления (установить в БД 1) // Если action = "toggle_off", то нужно ВЫКЛЮЧИТЬ уведомления (установить в БД 0) bool newValue = action == "on"; int dbValue = newValue ? 1 : 0; Log.Information($"[Отладка] Переключение уведомлений для пользователя {chatId}: action={action}, новое значение={newValue}, значение в БД={dbValue}"); using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = @" INSERT INTO UserSettings (UserId, NotificationsEnabled) VALUES (@userId, @value) ON CONFLICT(UserId) DO UPDATE SET NotificationsEnabled = @value"; command.Parameters.AddWithValue("@userId", chatId); command.Parameters.AddWithValue("@value", dbValue); int rowsAffected = await command.ExecuteNonQueryAsync(); Log.Information($"[Отладка] Запрос выполнен, затронуто {rowsAffected} строк"); } // Проверка после изменения using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var checkCommand = connection.CreateCommand(); checkCommand.CommandText = "SELECT NotificationsEnabled FROM UserSettings WHERE UserId = @userId"; checkCommand.Parameters.AddWithValue("@userId", chatId); var result = await checkCommand.ExecuteScalarAsync(); bool updatedState = false; if (result != null && result != DBNull.Value) { updatedState = Convert.ToInt32(result) == 1; } Log.Information($"[Отладка] После обновления: значение в БД для пользователя {chatId} = {result}, обновленное состояние={updatedState}"); } // Показываем обновленные настройки await ShowUserSettings(botClient, chatId); string statusText = newValue ? "включены" : "отключены"; Log.Information($"Пользователь {chatId}: уведомления {statusText}"); } catch (Exception ex) { Log.Error($"Ошибка при изменении настроек уведомлений: {ex.Message}"); Log.Error($"StackTrace: {ex.StackTrace}"); await botClient.SendMessage(chatId, "Произошла ошибка при обновлении настроек."); } } private static async Task SendMainMenu(ITelegramBotClient botClient, long chatId) { var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("📝 Подать заявку", "report"), InlineKeyboardButton.WithCallbackData("⚙️ Настройки", "user_settings") }, new[] { InlineKeyboardButton.WithCallbackData("📋 Мои заявки", "user_reports") }, new[] { InlineKeyboardButton.WithCallbackData("🔐 Панель администратора", "admin_panel") } }); await botClient.SendMessage( chatId: chatId, text: "Главное меню:", replyMarkup: keyboard ); } private static async Task SendAdminPanel(ITelegramBotClient botClient, long chatId) { var buttonsRows = new List { new[] { InlineKeyboardButton.WithCallbackData("📋 Менеджер заявок", "view_reports"), InlineKeyboardButton.WithCallbackData("🗃️ Архив заявок", "view_archived_reports") }, new[] { InlineKeyboardButton.WithCallbackData("⚙️ Личный кабинет", "admin_settings") } }; // Добавлять кнопку "Удалить все заявки" только для суперадминов if (superAdmins.Contains(chatId)) { buttonsRows.Add(new[] { InlineKeyboardButton.WithCallbackData("❌ Удалить все заявки", "delete_all_reports") }); } // Кнопка возврата в главное меню buttonsRows.Add(new[] { InlineKeyboardButton.WithCallbackData("🏠 Главное меню", "main_menu") }); var keyboard = new InlineKeyboardMarkup(buttonsRows); await botClient.SendMessage( chatId: chatId, text: "🔐 Панель администраторов:", parseMode: ParseMode.Html, 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, Priority 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); string priority = reader.GetString(3); string statusEmoji = GetStatusEmoji(status); string priorityMarker = priority.ToLower() == "высокий" ? "⚠️ " : ""; buttons.Add(new[] { InlineKeyboardButton.WithCallbackData( $"{priorityMarker}#{id} - {statusEmoji} {status} - {description}...", $"report_{id}") }); } } // Добавляем кнопки навигации buttons.Add(new[] { InlineKeyboardButton.WithCallbackData("🔙 Назад", "back_to_admin_panel"), 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, Priority 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); string priority = reader.GetString(3); string statusEmoji = GetStatusEmoji(status); string priorityMarker = priority.ToLower() == "высокий" ? "⚠️ " : ""; buttons.Add(new[] { InlineKeyboardButton.WithCallbackData( $"{priorityMarker}#{id} - {statusEmoji} {status} - {description}...", $"report_{id}") }); } } // Добавляем кнопки навигации buttons.Add(new[] { InlineKeyboardButton.WithCallbackData("🔙 Назад", "back_to_admin_panel"), 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 Priority, Room, Description, ReporterName, Status, DateCreated, AdminId, ChatId FROM Reports WHERE Id = @id"; command.Parameters.AddWithValue("@id", reportId); using (var reader = await command.ExecuteReaderAsync()) { if (await reader.ReadAsync()) { string priority = reader.GetString(0); string room = reader.GetString(1); string description = reader.GetString(2); string reporterName = reader.GetString(3); string status = reader.GetString(4); string dateCreated = reader.GetDateTime(5).ToString("yyyy-MM-dd HH:mm:ss"); long ownerChatId = reader.GetInt64(7); // Проверяем существование столбца AdminId long adminId = 0; try { if (!reader.IsDBNull(6)) adminId = reader.GetInt64(6); } catch { Log.Warning($"Столбец AdminId для заявки {reportId} не найден или содержит NULL"); } string adminFullName = adminId > 0 && adminFullNames.ContainsKey(adminId) ? adminFullNames[adminId] : "Не назначен"; string priorityEmoji = GetPriorityEmoji(priority); string statusEmoji = GetStatusEmoji(status); InlineKeyboardMarkup keyboard; // Разные клавиатуры для админов и обычных пользователей if (admins.Contains(chatId)) { // Клавиатура для администраторов с возможностью изменения статуса keyboard = 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") } }); } else { // Клавиатура для обычных пользователей - только удаление своих заявок keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("❌ Удалить заявку", $"delete_{reportId}") }, new[] { InlineKeyboardButton.WithCallbackData("🔙 Назад к списку", "user_reports"), InlineKeyboardButton.WithCallbackData("🏠 Главное меню", "main_menu") } }); } string newText = $"Заявка #{reportId}\n\n" + $"Приоритет: {priorityEmoji} {priority}\n" + $"Кабинет: {room}\n" + $"Описание: {description}\n" + $"ФИО: {reporterName}\n" + $"Статус: {statusEmoji} {status}\n" + $"Дата создания: {dateCreated}\n" + $"Администратор: {adminFullName}"; await botClient.SendMessage( chatId: chatId, text: newText, replyMarkup: keyboard ); } else { // Если заявка не найдена await botClient.SendMessage( chatId: chatId, text: $"⚠️ Заявка #{reportId} не найдена!" ); Log.Warning($"Заявка #{reportId} не найдена при попытке просмотра деталей."); await Task.Delay(2000); // Возвращаем к нужному списку в зависимости от роли пользователя if (admins.Contains(chatId)) { await ViewReports(botClient, chatId); } else { await ViewUserReports(botClient, chatId); } } } } } catch (Exception ex) { Log.Error($"Ошибка при отображении деталей заявки #{reportId}: {ex.Message}"); await botClient.SendMessage( chatId: chatId, text: $"Произошла ошибка при загрузке заявки #{reportId}." ); } } // Метод для удаления администратора из базы данных private static async Task RemoveAdminFromDatabase(long chatId) { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "DELETE FROM Admins WHERE ChatId = @chatId"; command.Parameters.AddWithValue("@chatId", chatId); int rowsAffected = await command.ExecuteNonQueryAsync(); if (rowsAffected > 0) { admins.Remove(chatId); Log.Information($"Администратор {chatId} удален из базы данных"); } else { Log.Information($"Администратор {chatId} не найден в базе данных"); } } } catch (Exception ex) { Log.Error($"Ошибка при удалении администратора из базы данных: {ex.Message}"); } } // Добавим метод для отображения раздела настроек администраторов private static async Task ShowAdminSettings(ITelegramBotClient botClient, long chatId) { try { // Проверяем, что запрашивающий пользователь - администратор if (!admins.Contains(chatId)) { await botClient.SendMessage( chatId: chatId, text: "⛔ У вас нет прав для доступа к настройкам администраторов." ); await Task.Delay(2000); await SendMainMenu(botClient, chatId); return; } bool isSuperAdmin = superAdmins.Contains(chatId); var buttons = new List(); // Разные опции в зависимости от типа пользователя if (isSuperAdmin) { buttons.Add(new[] { InlineKeyboardButton.WithCallbackData("👥 Управление администраторами", "view_admins") }); } // Настройки уведомлений для всех типов администраторов bool notifyOnPendingReports = true; using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "SELECT NotifyOnPendingReports FROM Admins WHERE ChatId = @chatId"; command.Parameters.AddWithValue("@chatId", chatId); var result = await command.ExecuteScalarAsync(); if (result != null && result != DBNull.Value) { notifyOnPendingReports = Convert.ToInt32(result) == 1; } } string notificationStatus = notifyOnPendingReports ? "✅ Включены" : "❌ Отключены"; string toggleAction = notifyOnPendingReports ? "disable_notifications" : "enable_notifications"; buttons.Add(new[] { InlineKeyboardButton.WithCallbackData($"Уведомления о просрочке: {notificationStatus}", $"{toggleAction}_{chatId}") }); // Кнопка выхода из режима администратора или суперпользователя (текст зависит от роли) buttons.Add(new[] { InlineKeyboardButton.WithCallbackData( isSuperAdmin ? "🚪 Выйти из режима суперпользователя" : "🚪 Выйти из режима администратора", "leave_admin") }); buttons.Add(new[] { InlineKeyboardButton.WithCallbackData("🔙 Назад к панели администратора", "admin_panel"), InlineKeyboardButton.WithCallbackData("🏠 Главное меню", "main_menu") }); var keyboard = new InlineKeyboardMarkup(buttons); string roleName = isSuperAdmin ? "суперпользователя" : "администратора"; await botClient.SendMessage( chatId: chatId, text: $"⚙️ Настройки {roleName}\n\n" + $"Ваша роль: {(isSuperAdmin ? "👑 Суперпользователь" : "👤 Администратор")}\n" + $"Уведомления о просрочках: {notificationStatus}\n\n" + $"Выберите действие:", parseMode: ParseMode.Html, replyMarkup: keyboard ); } catch (Exception ex) { Log.Error($"Ошибка при отображении настроек администратора: {ex.Message}"); } } private static async Task ToggleAdminNotifications(long adminId, bool enable) { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "UPDATE Admins SET NotifyOnPendingReports = @notify WHERE ChatId = @chatId"; command.Parameters.AddWithValue("@notify", enable ? 1 : 0); command.Parameters.AddWithValue("@chatId", adminId); await command.ExecuteNonQueryAsync(); Log.Information($"Уведомления для администратора {adminId} {(enable ? "включены" : "отключены")}"); } } catch (Exception ex) { Log.Error($"Ошибка при переключении уведомлений для администратора {adminId}: {ex.Message}"); } } private static async Task NotifyAdminsAboutWorkAssignment(long reportId, long changerId) { try { // Получаем данные о заявке string description = ""; string priority = ""; string room = ""; string changerFullName = adminFullNames[changerId]; using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "SELECT Description, Priority, Room FROM Reports WHERE Id = @id"; command.Parameters.AddWithValue("@id", reportId); using (var reader = await command.ExecuteReaderAsync()) { if (await reader.ReadAsync()) { description = reader.GetString(0); priority = reader.GetString(1); room = reader.GetString(2); } } } foreach (var adminId in admins) { // Не уведомляем админа, который сам взял заявку в работу if (adminId == changerId) continue; string priorityEmoji = GetPriorityEmoji(priority); var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("📝 Посмотреть детали", $"report_{reportId}") } }); await _botClient.SendMessage( chatId: adminId, text: $"{priorityEmoji} Заявка #{reportId} взята в работу администратором {changerFullName}\n\n" + $"Приоритет: {priority}\n" + $"Кабинет: {room}\n" + $"Фрагмент описания: {description.Substring(0, Math.Min(50, description.Length))}...", parseMode: ParseMode.Html, replyMarkup: keyboard ); Log.Information($"Уведомление о взятии в работу заявки #{reportId} отправлено администратору {adminId}"); } } catch (Exception ex) { Log.Error($"Ошибка при отправке уведомлений о взятии в работу: {ex.Message}"); } } private static async Task UpdateReportStatus(long reportId, string newStatus, long changerId) { try { if (!admins.Contains(changerId)) { Log.Warning($"Попытка изменения статуса пользователем без прав администратора: {changerId}"); return; // Прерываем выполнение, если пользователь не администратор } string oldStatus = ""; long userChatId = 0; // Сначала получаем текущий статус и chatId пользователя using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var getStatusCommand = connection.CreateCommand(); getStatusCommand.CommandText = "SELECT Status, ChatId FROM Reports WHERE Id = @id"; getStatusCommand.Parameters.AddWithValue("@id", reportId); using (var reader = await getStatusCommand.ExecuteReaderAsync()) { if (await reader.ReadAsync()) { oldStatus = reader.GetString(0); userChatId = reader.GetInt64(1); } } } // Если статус не изменился, прерываем выполнение if (oldStatus == newStatus) return; // Обновляем статус и сохраняем ID администратора using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "UPDATE Reports SET Status = @status, AdminId = @adminId WHERE Id = @id"; command.Parameters.AddWithValue("@status", newStatus); command.Parameters.AddWithValue("@adminId", changerId); command.Parameters.AddWithValue("@id", reportId); await command.ExecuteNonQueryAsync(); // Уведомляем администраторов об изменении статуса await NotifyAdminsAboutStatusChange(reportId, newStatus, oldStatus, changerId); // Уведомляем пользователя об изменении статуса await NotifyUserAboutStatusChange(userChatId, reportId, newStatus); // Уведомляем администраторов, если заявка взята в работу if (newStatus == "в работе") { await NotifyAdminsAboutWorkAssignment(reportId, changerId); } // Перезапускаем мониторинг, если статус изменился обратно на "ожидает" if (newStatus == "ожидает") { var cts = new CancellationTokenSource(); _ = Task.Run(() => MonitorReportStatus(reportId, cts.Token)); } } } 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, 'ожидает'); SELECT last_insert_rowid(); "; 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); long reportId = Convert.ToInt64(await insertCommand.ExecuteScalarAsync()); Log.Information($"Заявка от пользователя {chatId} успешно сохранена с ID {reportId}."); // Уведомляем администраторов о новой заявке await NotifyAdminsAboutNewReport(reportId, report); // Запускаем мониторинг статуса заявки var cts = new CancellationTokenSource(); _ = Task.Run(() => MonitorReportStatus(reportId, cts.Token)); } } catch (Exception ex) { Log.Error($"Ошибка при сохранении заявки в базу данных: {ex.Message}"); } } // Обновляем метод CreateDatabaseIfNotExists 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(); var requiredColumns = new HashSet { "Id", "ChatId", "Priority", "Room", "Description", "ReporterName", "DateCreated", "Status", "AdminId" }; var existingColumns = new HashSet(); while (await tableInfo.ReadAsync()) { existingColumns.Add(tableInfo["name"]?.ToString() ?? string.Empty); } foreach (var column in requiredColumns) { if (!existingColumns.Contains(column)) { var alterTableCommand = connection.CreateCommand(); switch (column) { case "Priority": alterTableCommand.CommandText = "ALTER TABLE Reports ADD COLUMN Priority TEXT NOT NULL DEFAULT 'низкий';"; break; case "Room": alterTableCommand.CommandText = "ALTER TABLE Reports ADD COLUMN Room TEXT NOT NULL DEFAULT '';"; break; case "Description": alterTableCommand.CommandText = "ALTER TABLE Reports ADD COLUMN Description TEXT NOT NULL DEFAULT '';"; break; case "ReporterName": alterTableCommand.CommandText = "ALTER TABLE Reports ADD COLUMN ReporterName TEXT NOT NULL DEFAULT '';"; break; case "DateCreated": alterTableCommand.CommandText = "ALTER TABLE Reports ADD COLUMN DateCreated DATETIME DEFAULT CURRENT_TIMESTAMP;"; break; case "Status": alterTableCommand.CommandText = "ALTER TABLE Reports ADD COLUMN Status TEXT DEFAULT 'ожидает';"; break; case "AdminId": alterTableCommand.CommandText = "ALTER TABLE Reports ADD COLUMN AdminId INTEGER DEFAULT 0;"; break; } await alterTableCommand.ExecuteNonQueryAsync(); Log.Information($"Столбец {column} добавлен в таблицу Reports."); } } // Создаем таблицу 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 'ожидает', AdminId INTEGER DEFAULT 0 ); "; await createTableCommand.ExecuteNonQueryAsync(); Log.Information("Таблица Reports успешно создана (если её не было)."); // Создаем таблицу администраторов await CreateAdminsTableIfNotExists(); } } 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; } // Добавим метод для создания таблицы администраторов private static async Task CreateAdminsTableIfNotExists() { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = @" CREATE TABLE IF NOT EXISTS Admins ( Id INTEGER PRIMARY KEY AUTOINCREMENT, ChatId INTEGER NOT NULL UNIQUE, FullName TEXT NOT NULL, NotifyOnPendingReports INTEGER NOT NULL DEFAULT 1, IsSuperAdmin INTEGER NOT NULL DEFAULT 0 );"; await command.ExecuteNonQueryAsync(); Log.Information("Таблица Admins успешно создана (если её не было)."); // Проверяем наличие столбца NotifyOnPendingReports command.CommandText = "PRAGMA table_info(Admins);"; var tableInfo = await command.ExecuteReaderAsync(); bool notifyColumnExists = false; bool superAdminColumnExists = false; while (await tableInfo.ReadAsync()) { if (tableInfo["name"].ToString() == "NotifyOnPendingReports") { notifyColumnExists = true; } if (tableInfo["name"].ToString() == "IsSuperAdmin") { superAdminColumnExists = true; } } await tableInfo.CloseAsync(); // Закрываем DataReader перед изменением CommandText if (!notifyColumnExists) { command.CommandText = "ALTER TABLE Admins ADD COLUMN NotifyOnPendingReports INTEGER NOT NULL DEFAULT 1;"; await command.ExecuteNonQueryAsync(); Log.Information("Столбец NotifyOnPendingReports добавлен в таблицу Admins."); } // Добавляем столбец IsSuperAdmin, если его нет if (!superAdminColumnExists) { command.CommandText = "ALTER TABLE Admins ADD COLUMN IsSuperAdmin INTEGER NOT NULL DEFAULT 0;"; await command.ExecuteNonQueryAsync(); Log.Information("Столбец IsSuperAdmin добавлен в таблицу Admins."); } } } catch (Exception ex) { Log.Error($"Ошибка при создании таблицы Admins: {ex.Message}"); } } // Метод для сохранения администратора в базу данных private static async Task SaveAdminToDatabase(long chatId, string fullName) { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); // Проверяем, существует ли уже такой админ var checkCommand = connection.CreateCommand(); checkCommand.CommandText = "SELECT COUNT(*) FROM Admins WHERE ChatId = @chatId"; checkCommand.Parameters.AddWithValue("@chatId", chatId); int count = Convert.ToInt32(await checkCommand.ExecuteScalarAsync()); if (count == 0) { // Добавляем нового админа (явно указываем IsSuperAdmin = 0) var insertCommand = connection.CreateCommand(); insertCommand.CommandText = "INSERT INTO Admins (ChatId, FullName, IsSuperAdmin) VALUES (@chatId, @fullName, 0)"; insertCommand.Parameters.AddWithValue("@chatId", chatId); insertCommand.Parameters.AddWithValue("@fullName", fullName); await insertCommand.ExecuteNonQueryAsync(); Log.Information($"Администратор {chatId} добавлен в базу данных"); } else { // Обновляем данные, если пользователь уже существует var updateCommand = connection.CreateCommand(); updateCommand.CommandText = "UPDATE Admins SET FullName = @fullName WHERE ChatId = @chatId"; updateCommand.Parameters.AddWithValue("@chatId", chatId); updateCommand.Parameters.AddWithValue("@fullName", fullName); await updateCommand.ExecuteNonQueryAsync(); Log.Information($"Данные администратора {chatId} обновлены"); } } } catch (Exception ex) { Log.Error($"Ошибка при сохранении администратора в базу данных: {ex.Message}"); } } // Метод для загрузки администраторов из базы данных private static async Task LoadAdminsFromDatabase() { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "SELECT ChatId, FullName, IsSuperAdmin FROM Admins"; using (var reader = await command.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { long adminId = reader.GetInt64(0); string fullName = reader.GetString(1); bool isSuperAdmin = reader.GetInt32(2) == 1; admins.Add(adminId); adminFullNames[adminId] = fullName; // Добавляем в список суперпользователей, если это суперадмин if (isSuperAdmin) { superAdmins.Add(adminId); } Log.Information($"Загружен {(isSuperAdmin ? "суперпользователь" : "администратор")} с ID: {adminId}, ФИО: {fullName}"); } } } Log.Information($"Загружено {admins.Count} администраторов из базы данных, из них {superAdmins.Count} суперпользователей"); } catch (Exception ex) { Log.Error($"Ошибка при загрузке администраторов из базы данных: {ex.Message}"); } } private static async Task SaveSuperAdminToDatabase(long chatId, string fullName) { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); // Проверяем, существует ли уже такой админ var checkCommand = connection.CreateCommand(); checkCommand.CommandText = "SELECT COUNT(*) FROM Admins WHERE ChatId = @chatId"; checkCommand.Parameters.AddWithValue("@chatId", chatId); int count = Convert.ToInt32(await checkCommand.ExecuteScalarAsync()); if (count == 0) { // Добавляем нового суперадмина var insertCommand = connection.CreateCommand(); insertCommand.CommandText = "INSERT INTO Admins (ChatId, FullName, IsSuperAdmin) VALUES (@chatId, @fullName, 1)"; insertCommand.Parameters.AddWithValue("@chatId", chatId); insertCommand.Parameters.AddWithValue("@fullName", fullName); await insertCommand.ExecuteNonQueryAsync(); Log.Information($"Суперпользователь {chatId} добавлен в базу данных"); } else { // Обновляем существующего пользователя до суперадмина var updateCommand = connection.CreateCommand(); updateCommand.CommandText = "UPDATE Admins SET IsSuperAdmin = 1, FullName = @fullName WHERE ChatId = @chatId"; updateCommand.Parameters.AddWithValue("@chatId", chatId); updateCommand.Parameters.AddWithValue("@fullName", fullName); await updateCommand.ExecuteNonQueryAsync(); Log.Information($"Пользователь {chatId} повышен до суперпользователя"); } } } catch (Exception ex) { Log.Error($"Ошибка при сохранении суперпользователя в базу данных: {ex.Message}"); } } private static async Task RemoveSelfFromAdmins(long chatId) { try { using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "DELETE FROM Admins WHERE ChatId = @chatId"; command.Parameters.AddWithValue("@chatId", chatId); int rowsAffected = await command.ExecuteNonQueryAsync(); if (rowsAffected > 0) { admins.Remove(chatId); superAdmins.Remove(chatId); if (adminFullNames.ContainsKey(chatId)) { adminFullNames.Remove(chatId); } Log.Information($"Пользователь {chatId} удалил себя из администраторов"); } else { Log.Information($"Администратор {chatId} не найден в базе данных"); } } } catch (Exception ex) { Log.Error($"Ошибка при удалении себя из администраторов: {ex.Message}"); } } private static async Task ShowAdminsList(ITelegramBotClient botClient, long chatId) { try { // Проверяем, что запрашивающий пользователь - администратор if (!admins.Contains(chatId)) { await botClient.SendMessage( chatId: chatId, text: "⛔ У вас нет прав для просмотра списка администраторов." ); return; } // Получаем список всех администраторов из базы данных var adminsList = new List<(long chatId, string username, bool isSuperAdmin)>(); using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "SELECT ChatId, FullName, IsSuperAdmin FROM Admins"; using (var reader = await command.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { long adminId = reader.GetInt64(0); string fullName = reader.GetString(1); bool isSuperAdmin = reader.GetInt32(2) == 1; adminsList.Add((adminId, fullName, isSuperAdmin)); } } } // Формируем сообщение if (adminsList.Count == 0) { await botClient.SendMessage( chatId: chatId, text: "⚠️ В системе нет зарегистрированных администраторов." ); return; } var buttons = new List(); var messageText = new System.Text.StringBuilder(); messageText.AppendLine("📋 Список администраторов:\n"); bool isSuperUser = superAdmins.Contains(chatId); for (int i = 0; i < adminsList.Count; i++) { var (adminId, username, adminIsSuperAdmin) = adminsList[i]; string role = adminIsSuperAdmin ? "👑 Суперпользователь" : "👤 Администратор"; messageText.AppendLine($"{i + 1}. {username} (ID: {adminId}) - {role}"); // Кнопка удаления доступна только суперпользователям и только для других администраторов if (isSuperUser && adminId != chatId) { buttons.Add(new[] { InlineKeyboardButton.WithCallbackData($"❌ Удалить {username}", $"removeadmin_{adminId}") }); } } // Кнопка "Назад" всегда доступна buttons.Add(new[] { InlineKeyboardButton.WithCallbackData("🔙 Назад", "admin_settings") }); var keyboard = new InlineKeyboardMarkup(buttons); await botClient.SendMessage( chatId: chatId, text: messageText.ToString(), parseMode: ParseMode.Html, replyMarkup: keyboard ); } catch (Exception ex) { Log.Error($"Ошибка при отображении списка администраторов: {ex.Message}"); await botClient.SendMessage( chatId: chatId, text: "❌ Произошла ошибка при отображении списка администраторов." ); } } // Метод для уведомления администраторов о новой заявке private static async Task NotifyAdminsAboutNewReport(long reportId, Report report) { try { foreach (var adminId in admins) { var statusEmoji = GetPriorityEmoji(report.Priority); var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("📝 Посмотреть детали", $"report_{reportId}"), InlineKeyboardButton.WithCallbackData("📋 Все заявки", "view_reports") } }); await _botClient.SendMessage( chatId: adminId, text: $"{statusEmoji} Новая заявка #{reportId}\n\n" + $"Приоритет: {report.Priority}\n" + $"Кабинет: {report.Room}\n" + $"Описание: {report.Description}\n" + $"От: {report.ReporterName}", parseMode: ParseMode.Html, replyMarkup: keyboard ); Log.Information($"Уведомление о новой заявке #{reportId} отправлено администратору {adminId}"); } } catch (Exception ex) { Log.Error($"Ошибка при отправке уведомлений администраторам о новой заявке: {ex.Message}"); } } // Метод для уведомления администраторов об изменении статуса заявки private static async Task NotifyAdminsAboutStatusChange(long reportId, string newStatus, string oldStatus, long changerId) { try { // Получаем данные о заявке string description = ""; string priority = ""; string room = ""; using (var connection = new SqliteConnection("Data Source=bot.db")) { await connection.OpenAsync(); var command = connection.CreateCommand(); command.CommandText = "SELECT Description, Priority, Room FROM Reports WHERE Id = @id"; command.Parameters.AddWithValue("@id", reportId); using (var reader = await command.ExecuteReaderAsync()) { if (await reader.ReadAsync()) { description = reader.GetString(0); priority = reader.GetString(1); room = reader.GetString(2); } } } foreach (var adminId in admins) { // Не уведомляем админа, который сам изменил статус if (adminId == changerId) continue; string statusEmoji = GetStatusEmoji(newStatus); var keyboard = new InlineKeyboardMarkup(new[] { new[] { InlineKeyboardButton.WithCallbackData("📝 Посмотреть детали", $"report_{reportId}") } }); await _botClient.SendMessage( chatId: adminId, text: $"{statusEmoji} Обновление статуса заявки #{reportId}\n\n" + $"Приоритет: {priority}\n" + $"Кабинет: {room}\n" + $"Статус изменен: {oldStatus} ➡️ {newStatus}\n\n" + $"Фрагмент описания: {description.Substring(0, Math.Min(50, description.Length))}...", parseMode: ParseMode.Html, replyMarkup: keyboard ); Log.Information($"Уведомление об изменении статуса заявки #{reportId} отправлено администратору {adminId}"); } } catch (Exception ex) { Log.Error($"Ошибка при отправке уведомлений об изменении статуса: {ex.Message}"); } } // Вспомогательный метод для получения emoji в зависимости от приоритета private static string GetPriorityEmoji(string priority) { return priority.ToLower() switch { "high" or "высокий" => "🔴", "medium" or "средний" => "🟠", "low" or "низкий" => "🟢", _ => "ℹ️" }; } // Вспомогательный метод для получения emoji в зависимости от статуса private static string GetStatusEmoji(string status) { return status.ToLower() switch { "ожидает" => "🟡", "в работе" => "🔵", "закрыта" => "🟢", _ => "ℹ️" }; } }