From a205f274b5d8e8ac6244fc442f55cb356ec5b019 Mon Sep 17 00:00:00 2001 From: V1RTUOZ Date: Wed, 30 Apr 2025 02:34:18 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B8=D0=BB=D1=8C=D0=BD=D0=BE=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F,=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=B8=D0=B7=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=BA=D0=BE=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D0=BA=D1=82=D1=8B,=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BA=D0=B0=20=D1=84=D0=BE=D1=82=D0=BE=20=D0=B8=20=D1=82?= =?UTF-8?q?.=D0=BF.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Employee.cs | 2 +- EmployeeContact.cs | 20 +- FavoriteContactsManager.cs | 56 ++++ InlineKeyboardMarkupExtensions.cs | 18 + Program.cs | 524 +++++++++++++++++++++++------- appsettings.json | 28 ++ nsszcontactbot.csproj | 13 +- 7 files changed, 529 insertions(+), 132 deletions(-) create mode 100644 FavoriteContactsManager.cs create mode 100644 InlineKeyboardMarkupExtensions.cs create mode 100644 appsettings.json diff --git a/Employee.cs b/Employee.cs index 71046c5..d780807 100644 --- a/Employee.cs +++ b/Employee.cs @@ -19,4 +19,4 @@ public class Employee public string? State { get; set; } // Состояние события (например, "В отпуске") public DateTime? StartDate { get; set; } // Дата начала события public DateTime? EndDate { get; set; } // Дата окончания события -} \ No newline at end of file +} diff --git a/EmployeeContact.cs b/EmployeeContact.cs index 186dc1a..f6b2dc5 100644 --- a/EmployeeContact.cs +++ b/EmployeeContact.cs @@ -3,6 +3,7 @@ public class EmployeeContact { public static string GeneralPhoneNumber => "+78123352577"; + public EmployeeContact(Employee employee) { FullName = employee.FullName; @@ -20,7 +21,9 @@ public class EmployeeContact Department = employee.Department; Position = employee.Position; ExtensionNumber = employee.Phone ?? null; //string.IsNullOrEmpty(employee.Phone) ? null : employee.Phone; - Phone = string.IsNullOrEmpty(ExtensionNumber) ? GeneralPhoneNumber : $"{GeneralPhoneNumber},{ExtensionNumber}"; + Phone = string.IsNullOrEmpty(ExtensionNumber) + ? GeneralPhoneNumber + : $"{GeneralPhoneNumber},{ExtensionNumber}"; if (!string.IsNullOrEmpty(employee.Mobile)) { Mobile = employee.Mobile; @@ -34,13 +37,14 @@ public class EmployeeContact { get { - string text = "BEGIN:VCARD\n" - + "VERSION:2.1\n" - + $"N:{LastName};{FirstName}\n" - + $"FN:{FullName}\n" - + "ORG:NSSZ\n" - + $"ROLE:{Department}\n" - + $"TITLE:{Position}\n"; + string text = + "BEGIN:VCARD\n" + + "VERSION:2.1\n" + + $"N:{LastName};{FirstName}\n" + + $"FN:{FullName}\n" + + "ORG:NSSZ\n" + + $"ROLE:{Department}\n" + + $"TITLE:{Position}\n"; if (!string.IsNullOrEmpty(Mobile)) text += $"TEL;CELL:{Mobile}\n"; diff --git a/FavoriteContactsManager.cs b/FavoriteContactsManager.cs new file mode 100644 index 0000000..1e8cf44 --- /dev/null +++ b/FavoriteContactsManager.cs @@ -0,0 +1,56 @@ +using StackExchange.Redis; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace nsszcontactbot; +public class FavoriteContactsManager +{ + private readonly IDatabase _redisDb; + private const string KeyPrefix = "user:favorites:"; + + public FavoriteContactsManager(ConnectionMultiplexer redis) + { + _redisDb = redis.GetDatabase(); + } + + /// + /// Добавить контакт в избранное. + /// + public async Task AddFavoriteAsync(long userId, string contactId) + { + string key = GetRedisKey(userId); + await _redisDb.SetAddAsync(key, contactId); + } + + /// + /// Удалить контакт из избранного. + /// + public async Task RemoveFavoriteAsync(long userId, string contactId) + { + string key = GetRedisKey(userId); + await _redisDb.SetRemoveAsync(key, contactId); + } + + /// + /// Проверить, является ли контакт избранным. + /// + public async Task IsFavoriteAsync(long userId, string contactId) + { + string key = GetRedisKey(userId); + return await _redisDb.SetContainsAsync(key, contactId); + } + + /// + /// Получить список всех избранных контактов пользователя. + /// + public async Task> GetAllFavoritesAsync(long userId) + { + string key = GetRedisKey(userId); + var members = await _redisDb.SetMembersAsync(key); + return members.Select(m => m.ToString()).ToList(); + } + + private string GetRedisKey(long userId) => $"{KeyPrefix}{userId}"; +} diff --git a/InlineKeyboardMarkupExtensions.cs b/InlineKeyboardMarkupExtensions.cs new file mode 100644 index 0000000..75ad520 --- /dev/null +++ b/InlineKeyboardMarkupExtensions.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Telegram.Bot.Types.ReplyMarkups; + +namespace nsszcontactbot +{ + internal static class InlineKeyboardMarkupExtensions + { + internal static InlineKeyboardMarkup AddCloseButton(this InlineKeyboardMarkup markup, bool newRow = true) + { + var buttonRow = newRow ? markup.AddNewRow() : markup; + return buttonRow.AddButton("❌ Закрыть", "close"); + } + } +} diff --git a/Program.cs b/Program.cs index ee3c3f6..78c0219 100644 --- a/Program.cs +++ b/Program.cs @@ -4,6 +4,16 @@ using Telegram.Bot.Polling; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.ReplyMarkups; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Serilog; +using System.Timers; +using Serilog.Core; +using NRedisStack; +using NRedisStack.RedisStackCommands; + +using Timer = System.Timers.Timer; +using StackExchange.Redis; namespace nsszcontactbot; @@ -12,48 +22,87 @@ public class Program private static ITelegramBotClient Bot; private static List Employees = new(); private static List Departments = new(); - private static int CurrentPage = 0; // Текущая страница для пагинации - private static int PageSize = 5; // Количество подразделений на странице + private static IConfigurationRoot config; + private static FavoriteContactsManager FavoritesManager; + //private static ILogger Logger; - public static async Task Main(string[] args) + public static async Task Main(string[] args) { - Bot = new TelegramBotClient("6237449447:AAEGMmpx-1hUEApFZZS_ySAMZErseKJ8dHo"); // Укажите ваш токен + config = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build(); + + Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(config).CreateLogger(); + + var botToken = config["Telegram:BotToken"]; + if (botToken == null) + { + Log.Error("Токен Telegram-бота не указан в конфигурации."); + return 1; + } + + ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(config["Redis:Configuration"]!, (conf) => + { + conf.User = config["Redis:User"]; + conf.Password = config["Redis:Password"]; + }); + + FavoritesManager = new FavoriteContactsManager(redis); + Bot = new TelegramBotClient(botToken); List cmds = new(); - await Bot.SetMyCommandsAsync(cmds); - LoadEmployees(); - var me = await Bot.GetMeAsync(); - Console.WriteLine($"Бот запущен: @{me.Username}"); + await Bot.SetMyCommands(cmds); + if(!LoadEmployees()) + { + return 1; + } + StartUpdatingEmployeesData(); + + var me = await Bot.GetMe(); + + Log.Information($"Бот запущен: @{me.Username}"); // Запуск обработки обновлений var cancellationToken = new CancellationTokenSource().Token; ReceiverOptions receiverOptions = - new() - { - AllowedUpdates = Array.Empty() // receive all update types except ChatMember related updates - }; - Bot.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions: receiverOptions, cancellationToken: cancellationToken); + new() + { + AllowedUpdates = Array.Empty() // receive all update types except ChatMember related updates + }; + Bot.StartReceiving( + HandleUpdateAsync, + HandleErrorAsync, + receiverOptions: receiverOptions, + cancellationToken: cancellationToken + ); Console.WriteLine("Нажмите Enter для выхода..."); Console.ReadLine(); + return 0; } - private static async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) + private static async Task HandleUpdateAsync( + ITelegramBotClient botClient, + Update update, + CancellationToken cancellationToken + ) { if (update.Type == UpdateType.Message && update.Message?.Text != null) { var message = update.Message; + Log.Debug($"INPUT: from={message.From?.Username} text={message.Text}"); if (message.Text.StartsWith("/start")) { await StartCommand(message); } - else if (message.Text.StartsWith("/choose_department")) + else if (message.Text.EndsWith("Список подразделений")) { await ChooseDepartment(message); } - else if (message.ReplyToMessage != null && message.ReplyToMessage.Text!.StartsWith("Выберите подразделение")) + else if (message.Text.EndsWith("Избранные контакты")) { - await HandleDepartmentSelection(message); + await HandleShowFavorites(message); } else { @@ -74,57 +123,82 @@ public class Program await HandleAddContact(update.CallbackQuery, dataParts[1]); break; } + + case "addfavorite": + { + if (dataParts.Length != 2) + break; + await HandleAddFavorite(update.CallbackQuery, dataParts[1]); + break; + } + + case "delfavorite": + { + if (dataParts.Length != 2) + break; + await HandleDelFavorite(update.CallbackQuery, dataParts[1]); + break; + } + + case "showdepartment": + { + if (dataParts.Length != 2) + break; + await HandleShowDepartment(update.CallbackQuery, int.Parse(dataParts[1])); + break; + } + + case "showemployee": + { + if (dataParts.Length != 2) + break; + await HandleShowEmployee(update.CallbackQuery, dataParts[1]); + break; + } + + case "close": + { + await HandleClose( + update.CallbackQuery, + dataParts.Length > 1 ? dataParts[1] : null + ); + break; + } + default: break; } } - } } private static async Task StartCommand(Message message) { - string text = "Привет! Я бот для поиска контактов сотрудников. Введите запрос.\n"; - //text += "/choose_department - Выбрать подразделение\n"; - await Bot.SendTextMessageAsync(message.Chat.Id, text); + string text = "Привет! Я бот для поиска контактов сотрудников. Введите критерии поиска или воспользуйтесь кнопками внизу.\n"; + await Bot.SendMessage(message.Chat.Id, text, replyMarkup: new string[] { "⭐️ Избранные контакты", "🏢 Список подразделений" }); } private static async Task ChooseDepartment(Message message) { Departments = Employees.Select(e => e.Department).Distinct().ToList(); - CurrentPage = 0; // Сброс страницы await ShowDepartments(message.Chat.Id); } private static async Task ShowDepartments(long chatId) { - var totalDepartments = Departments.Count; - var totalPages = (int)Math.Ceiling((double)totalDepartments / PageSize); - var departmentsToShow = Departments.Skip(CurrentPage * PageSize).Take(PageSize).ToList(); + string text = "Выберите подразделение:"; + var keyboardMarkup = new InlineKeyboardMarkup(); - string text = "Выберите подразделение:\n"; - text += string.Join("\n", departmentsToShow.Select((d, i) => $"{i + 1 + CurrentPage * PageSize}. {d}")); - - // Создание кнопок - var inlineKeyboard = new List>(); - if (CurrentPage > 0) + var departmentIndex = 0; + foreach (var department in Departments) { - inlineKeyboard.Add(new List - { - InlineKeyboardButton.WithCallbackData("◀️ Назад", "prev") - }); + keyboardMarkup + .AddNewRow() + .AddButton($"{department}", $"showdepartment/{departmentIndex++}"); } - if (CurrentPage < totalPages - 1) - { - inlineKeyboard.Add(new List - { - InlineKeyboardButton.WithCallbackData("Вперед ▶️", "next") - }); - } - - var keyboardMarkup = new InlineKeyboardMarkup(inlineKeyboard); - - await Bot.SendTextMessageAsync(chatId, text, replyMarkup: keyboardMarkup); + //keyboardMarkup.AddCloseButton(); + await Bot.SendMessage(chatId, text, replyMarkup: keyboardMarkup); + await Task.Delay(999); } private static async Task HandleSearchInput(Message message) @@ -132,23 +206,48 @@ public class Program if (!string.IsNullOrEmpty(message.Text)) { string query = message.Text.Trim().ToLower(); - if (query.Length < 3) + if (query.Length < 4) { - await Bot.SendTextMessageAsync(message.Chat.Id, "Минимальная длина строки поиска - 3 символа."); + await Bot.SendMessage( + message.Chat.Id, + "Минимальная длина строки поиска - 4 символа." + ); return; } // Поиск сотрудников по полям - var results = Employees.Where(e => - e.FullName.ToLower().Contains(query) || - (e.Mail != null && e.Mail.ToLower().Contains(query)) || - (e.Phone != null && e.Phone.Contains(query)) || - (e.Mobile != null && e.Mobile.Contains(query)) || - (e.IP != null && e.IP.Contains(query)) - ).OrderBy(e => e.FullName.ToLower().IndexOf(query)).ToList(); + var results = Employees + .Where( + e => + e.FullName.ToLower().Contains(query) + || (e.Mail != null && e.Mail.ToLower().Contains(query)) + || (e.Phone != null && e.Phone.Contains(query)) + || (e.Mobile != null && e.Mobile.Contains(query)) + || (e.IP != null && e.IP.Contains(query)) + ) + .OrderBy(e => e.FullName.ToLower().IndexOf(query)) + .ToList(); // Отправка результатов поиска - await ShowEmployeeResults(message.Chat.Id, results); + await ShowEmployeeResults(message, results, null); + } + } + + private static async Task HandleShowFavorites(Message message) + { + if (!string.IsNullOrEmpty(message.Text)) + { + var ids = await FavoritesManager.GetAllFavoritesAsync(message.From.Id); + // Поиск сотрудников по полям + var results = Employees + .Where( + e => ids.Contains(e.Id) + ) + .OrderBy(e => e.FullName) + .ToList(); + + // Отправка результатов поиска + await ShowEmployeeResults(message, results, true); } } @@ -160,79 +259,223 @@ public class Program if (employee != null) { EmployeeContact contact = new(employee); - string phoneNumber = !string.IsNullOrEmpty(contact.Mobile) ? contact.Mobile : contact.Phone; - var replyParameters = new ReplyParameters() { MessageId = callbackQuery.Message.MessageId, QuoteParseMode = ParseMode.Html, Quote = $"{contact.FullName}" }; - await Bot.SendContactAsync(callbackQuery.Message.Chat.Id, phoneNumber, contact.FirstName, lastName: contact.LastName, vcard: contact.VCard, replyParameters: replyParameters); + string phoneNumber = !string.IsNullOrEmpty(contact.Mobile) + ? contact.Mobile + : contact.Phone; + var replyParameters = new ReplyParameters() + { + MessageId = callbackQuery.Message.MessageId, + QuoteParseMode = ParseMode.Html, + Quote = $"{contact.FullName}" + }; + var keyboardMarkup = new InlineKeyboardMarkup() + //.AddNewRow() + .AddCloseButton(); + await Bot.SendContact( + callbackQuery.Message.Chat.Id, + phoneNumber, + contact.FirstName, + lastName: contact.LastName, + vcard: contact.VCard, + replyParameters: replyParameters, + replyMarkup: keyboardMarkup, + protectContent: true + ); await Task.Delay(999); } } - await Bot.AnswerCallbackQueryAsync(callbackQuery.Id); + await Bot.AnswerCallbackQuery(callbackQuery.Id); } - private static async Task HandleDepartmentSelection(Message message) + private static async Task HandleAddFavorite(CallbackQuery callbackQuery, string contactId) { - if (message.Text.StartsWith("◀️ Назад")) + if (callbackQuery.Message != null) { - if (CurrentPage > 0) + var employee = Employees.Where(e => e.Id == contactId).First(); + if (employee != null) { - CurrentPage--; - await ShowDepartments(message.Chat.Id); - } - } - else if (message.Text.StartsWith("Вперед ▶️")) - { - CurrentPage++; - await ShowDepartments(message.Chat.Id); - } - else - { - int selectedIndex = int.Parse(message.Text) - 1; - if (selectedIndex >= 0 && selectedIndex < Departments.Count) - { - string selectedDepartment = Departments[selectedIndex]; - var results = Employees.Where(e => e.Department == selectedDepartment).ToList(); - await ShowEmployeeResults(message.Chat.Id, results); - } - else - { - await Bot.SendTextMessageAsync(message.Chat.Id, "Некорректный выбор."); + await FavoritesManager.AddFavoriteAsync(callbackQuery.From.Id, contactId); + foreach (var row in callbackQuery.Message.ReplyMarkup.InlineKeyboard) + foreach (var cell in row) + { + if (cell.CallbackData.Contains("addfavorite")) + { + cell.CallbackData = $"delfavorite/{contactId}"; + cell.Text = "💔 Не избранный"; + } + + } + await Bot.EditMessageReplyMarkup(callbackQuery.Message.Chat.Id, callbackQuery.Message.MessageId, callbackQuery.Message.ReplyMarkup); + await Bot.AnswerCallbackQuery(callbackQuery.Id, "✔️ Контакт добавлен в избранные"); } } } - private static async Task ShowEmployeeResults(long chatId, List results) + private static async Task HandleDelFavorite(CallbackQuery callbackQuery, string contactId) + { + if (callbackQuery.Message != null) + { + var employee = Employees.Where(e => e.Id == contactId).First(); + if (employee != null) + { + await FavoritesManager.RemoveFavoriteAsync(callbackQuery.From.Id, contactId); + foreach (var row in callbackQuery.Message.ReplyMarkup.InlineKeyboard) + foreach (var cell in row) + { + if (cell.CallbackData.Contains("delfavorite")) + { + cell.CallbackData = $"addfavorite/{contactId}"; + cell.Text = "❤️ В избранные"; + } + + } + await Bot.EditMessageReplyMarkup(callbackQuery.Message.Chat.Id, callbackQuery.Message.MessageId, callbackQuery.Message.ReplyMarkup); + await Bot.AnswerCallbackQuery(callbackQuery.Id, "✔️ Контакт удален из избранных"); + } + } + } + + private static async Task HandleShowDepartment(CallbackQuery callbackQuery, int departmentIndex) + { + await Bot.AnswerCallbackQuery(callbackQuery.Id); + if (callbackQuery.Message != null) + { + await Bot.DeleteMessage( + callbackQuery.Message.Chat.Id, + callbackQuery.Message.MessageId + ); + + var employees = Employees + .Where(e => e.Department == Departments[departmentIndex]) + .OrderBy(e => e.FullName) + .ToList(); + if (employees != null) + { + await ShowEmployeeResults(callbackQuery.Message, employees, null); + } + } + //await Bot.AnswerCallbackQueryAsync(callbackQuery.Id); + } + + private static async Task HandleShowEmployee(CallbackQuery callbackQuery, string id) + { + if (callbackQuery.Message != null) + { + var employee = Employees.Where(e => e.Id == id).First(); + if (employee != null) + { + var isFavorite = await FavoritesManager.IsFavoriteAsync(callbackQuery.From.Id, id); + await ShowEmployeeCard(callbackQuery.Message.Chat.Id, employee, isFavorite, closeButton: true); + } + } + await Bot.AnswerCallbackQuery(callbackQuery.Id); + } + + + private static async Task HandleClose(CallbackQuery callbackQuery, string? nextAction) + { + if (callbackQuery.Message != null) + { + await Bot.DeleteMessage( + callbackQuery.Message.Chat.Id, + callbackQuery.Message.MessageId + ); + await Task.Delay(500); + } + await Bot.AnswerCallbackQuery(callbackQuery.Id); + } + + private static async Task ShowEmployeeCard(long chatId, Employee employee, bool isFavorite = false, bool contactButton = true, bool favoriteButton = true, bool closeButton = false) + { + { + var keyboardMarkup = new InlineKeyboardMarkup(); + var keyboardRow = keyboardMarkup.AddNewRow(); + if (contactButton) + keyboardRow.AddButton("👤 Контакт", $"addcontact/{employee.Id}"); + + if (favoriteButton) + { + if (isFavorite) + { + keyboardRow.AddButton("💔 Не избранный", $"delfavorite/{employee.Id}"); + } + else + { + keyboardRow.AddButton("❤️ В избранные", $"addfavorite/{employee.Id}"); + } + } + + if (closeButton) + keyboardRow.AddCloseButton(); + + var card = CreateEmployeeCard(employee); + + string photoPath = Path.Combine(config["Paths:PhotoBasePath"]!, $"{employee.FullName}.jpg"); + try + { + await using var fileStream = new FileStream( + photoPath, + FileMode.Open, + FileAccess.Read + ); + + await Bot.SendPhoto( + chatId, + fileStream, + caption: card, + parseMode: ParseMode.Html, + replyMarkup: keyboardMarkup + ); + } + catch (FileNotFoundException) + { + await Bot.SendMessage( + chatId, + card, + ParseMode.Html, + replyMarkup: keyboardMarkup, + protectContent: true + ); + } + finally + { + await Task.Delay(999); + } + } + } + + private static async Task ShowEmployeeResults(Message message, List results, bool? isFavorite = false) { if (results.Any()) { - foreach (var employee in results) + if (results.Count < 10) { - var keyboardMarkup = new InlineKeyboardMarkup() - //.AddNewRow("1.1", "1.2", "1.3") - .AddNewRow() - .AddButton("👤 Контакт", $"addcontact/{employee.Id}"); - //.AddButton("В избранные", $"addfavorite/{employee.Id}"); - //.AddButton(InlineKeyboardButton.WithUrl("Написать письмо", $"mailto:{employee.Mail}")); + foreach (var employee in results) + { + bool bFavorite = isFavorite.HasValue ? isFavorite == true : await FavoritesManager.IsFavoriteAsync(message.From.Id, employee.Id); + await ShowEmployeeCard(message.Chat.Id, employee, bFavorite); + } + } + else + { + string text = "Вот, что удалось найти:"; + var keyboardMarkup = new InlineKeyboardMarkup(); - var card = CreateEmployeeCard(employee); - string photoPath = $"data/photos/{employee.FullName}.jpg"; - try + foreach (var employee in results) { - await using var fileStream = new FileStream(photoPath, FileMode.Open, FileAccess.Read); - await Bot.SendPhotoAsync(chatId, fileStream, caption: card, parseMode: ParseMode.Html, replyMarkup: keyboardMarkup); - } - catch (FileNotFoundException) - { - await Bot.SendTextMessageAsync(chatId, card, null, ParseMode.Html, replyMarkup: keyboardMarkup); - } - finally - { - await Task.Delay(999); + keyboardMarkup + .AddNewRow() + .AddButton(employee.FullName, $"showemployee/{employee.Id}"); } + + await Bot.SendMessage(message.Chat.Id, text, replyMarkup: keyboardMarkup, protectContent: true); + await Task.Delay(999); } } else { - await Bot.SendTextMessageAsync(chatId, "Сотрудники не найдены."); + await Bot.SendMessage(message.Chat.Id, "🔍 Извините, мы ничего не нашли."); + await Task.Delay(999); } } @@ -263,9 +506,10 @@ public class Program private static string CreateEmployeeCard(Employee employee) { EmployeeContact contact = new(employee); - string card = $"👤 {contact.FullName}\n" + - $"🏢 {contact.Department}\n" + - $"🎓 {contact.Position}\n"; + string card = + $"👤 {contact.FullName}\n" + + $"🏢 {contact.Department}\n" + + $"🎓 {contact.Position}\n"; if (!string.IsNullOrEmpty(contact.Mail)) card += $"✉️ {contact.Mail}\n"; @@ -275,8 +519,8 @@ public class Program if (!string.IsNullOrEmpty(contact.ExtensionNumber)) card += $"📞 {contact.ExtensionNumber}\n"; - //if (!string.IsNullOrEmpty(employee.IP)) - // card += $"🌐 {employee.IP}\n"; + if (!string.IsNullOrEmpty(employee.IP)) + card += $"🌐 {employee.IP}\n"; card += GetEventStatus(employee); @@ -285,12 +529,14 @@ public class Program private static string CreateEmployeeCard2(Employee employee) { - string photoUrl = $"https://contacts.int.nssz.ru/data/photos/{employee.FullName.Replace(" ", "%20")}.jpg"; // Формирование пути к фотографии + string photoUrl = + $"https://contacts.int.nssz.ru/data/photos/{employee.FullName.Replace(" ", "%20")}.jpg"; // Формирование пути к фотографии - string card = $"👤 {employee.FullName}\n" + - $"🏢 Отдел: {employee.Department}\n" + - $"🎓 Должность: {employee.Position}\n" + - $"📸 Фото: Посмотреть фото\n"; // Добавление ссылки на фото + string card = + $"👤 {employee.FullName}\n" + + $"🏢 Отдел: {employee.Department}\n" + + $"🎓 Должность: {employee.Position}\n" + + $"📸 Фото: Посмотреть фото\n"; // Добавление ссылки на фото if (!string.IsNullOrEmpty(employee.Mail)) card += $"✉️ Почта: {employee.Mail}\n"; @@ -306,16 +552,50 @@ public class Program return card; } - private static void LoadEmployees() + private static Timer? updateTimer = null; + private static void StartUpdatingEmployeesData(int intervalMs = 1 * 60 * 60 * 1000) { - // Загрузка данных сотрудников из JSON файла - var json = System.IO.File.ReadAllText("data/contacts.json"); - Employees = JsonConvert.DeserializeObject>(json)!; + if (updateTimer != null) return; + updateTimer = new Timer(intervalMs); + updateTimer.Elapsed += UpdateTimer_Elapsed; + updateTimer.AutoReset = true; // Запускать снова после завершения + updateTimer.Enabled = true; } - private static Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken) + private static void UpdateTimer_Elapsed(object? sender, ElapsedEventArgs e) { - Console.WriteLine($"Произошла ошибка: {exception.Message}"); + LoadEmployees(); + } + + private static int prevHash = 0; + private static bool LoadEmployees() + { + try + { + // Загрузка данных сотрудников из JSON файла при наличии изменений + var json = System.IO.File.ReadAllText(config["Paths:ContactsJsonPath"]!); + int newHash = json.GetHashCode(); + if (prevHash == newHash) + { + return true; + } + prevHash = newHash; + Employees = JsonConvert.DeserializeObject>(json)!; + Log.Information("Загружены данные о {Count} сотрудниках", Employees.Count); + return true; + } catch (Exception ex) { + Log.Error(ex, "Ошибка при загрузке списка сотрудников"); + return false; + } + } + + private static Task HandleErrorAsync( + ITelegramBotClient botClient, + Exception exception, + CancellationToken cancellationToken + ) + { + Log.Error(exception, "Произошла ошибка"); return Task.CompletedTask; } } diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 0000000..ce0412f --- /dev/null +++ b/appsettings.json @@ -0,0 +1,28 @@ +{ + "Paths": { + "ContactsJsonPath": "data/contacts.json", + "PhotoBasePath": "data/photos" + }, + "Telegram": { + "BotToken": "8155021946:AAGbfJnLa0sTWxqFB9yjbXhbTedBw4wvcQE" + }, + "Redis": { + "Configuration": "redis-15840.crce198.eu-central-1-3.ec2.redns.redis-cloud.com:15840", + "User": "tgbot", + "Password": "0aCKMee9Ocq88KzfjWyC9AmB25tLiY93%" + }, + "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], + "MinimumLevel": "Debug", + "WriteTo": [ + { "Name": "Console" }, + { + "Name": "File", + "Args": { + "path": "nsszcontactbot.log", + "rollingInterval": "Day" + } + } + ] + } +} \ No newline at end of file diff --git a/nsszcontactbot.csproj b/nsszcontactbot.csproj index 49a15d5..10e6652 100644 --- a/nsszcontactbot.csproj +++ b/nsszcontactbot.csproj @@ -7,8 +7,19 @@ enable + - + + + + + + + + + + PreserveNewest +