diff --git a/Employee.cs b/Employee.cs new file mode 100644 index 0000000..71046c5 --- /dev/null +++ b/Employee.cs @@ -0,0 +1,22 @@ +// Модель данных сотрудника + +namespace nsszcontactbot; + +public class Employee +{ + public string Id { get; set; } = string.Empty; // Уникальный идентификатор сотрудника (GUID) + public string FullName { get; set; } = string.Empty; // Полное имя сотрудника + public string Department { get; set; } = string.Empty; // Название отдела + public string DepartmentCode { get; set; } = string.Empty; // Код отдела + public string Position { get; set; } = string.Empty; // Должность + public string? Phone { get; set; } // Опциональное поле для телефона + public string? Mobile { get; set; } // Опциональное поле для хранения номера мобильного телефона + public string? Mail { get; set; } // Опциональное поле для электронной почты + public string? IP { get; set; } // Опциональное поле для IP-адреса + public int Category { get; set; } // Категория для статистического учета + + // Поля события + 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 new file mode 100644 index 0000000..186dc1a --- /dev/null +++ b/EmployeeContact.cs @@ -0,0 +1,67 @@ +namespace nsszcontactbot; + +public class EmployeeContact +{ + public static string GeneralPhoneNumber => "+78123352577"; + public EmployeeContact(Employee employee) + { + FullName = employee.FullName; + var nameParts = FullName.Split(' '); + if (nameParts.Length > 1) + { + LastName = nameParts[0]; + FirstName = nameParts[1]; + } + else + { + FirstName = FullName; + LastName = string.Empty; + } + Department = employee.Department; + Position = employee.Position; + ExtensionNumber = employee.Phone ?? null; //string.IsNullOrEmpty(employee.Phone) ? null : employee.Phone; + Phone = string.IsNullOrEmpty(ExtensionNumber) ? GeneralPhoneNumber : $"{GeneralPhoneNumber},{ExtensionNumber}"; + if (!string.IsNullOrEmpty(employee.Mobile)) + { + Mobile = employee.Mobile; + if (!Mobile.StartsWith('+')) + Mobile = "+" + Mobile; + } + Mail = employee.Mail; + } + + public string VCard + { + 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"; + if (!string.IsNullOrEmpty(Mobile)) + text += $"TEL;CELL:{Mobile}\n"; + + if (!string.IsNullOrEmpty(Phone)) + text += $"TEL;WORK:{Phone}\n"; + + if (!string.IsNullOrEmpty(Mail)) + text += $"EMAIL;WORK:{Mail}\n"; + + text += "PRODID:nssz-contact-bot\n"; + text += "END:VCARD\n"; + return text; + } + } + public string FullName { get; set; } // Полное имя сотрудника + public string FirstName { get; set; } + public string LastName { get; set; } + public string Department { get; set; } // Название отдела + public string Position { get; set; } // Должность + public string Phone { get; set; } + public string? ExtensionNumber { get; set; } + public string? Mobile { get; set; } // Опциональное поле для хранения номера мобильного телефона + public string? Mail { get; set; } // Опциональное поле для электронной почты +} diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..ee3c3f6 --- /dev/null +++ b/Program.cs @@ -0,0 +1,321 @@ +using Newtonsoft.Json; +using Telegram.Bot; +using Telegram.Bot.Polling; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.ReplyMarkups; + +namespace nsszcontactbot; + +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; // Количество подразделений на странице + + public static async Task Main(string[] args) + { + Bot = new TelegramBotClient("6237449447:AAEGMmpx-1hUEApFZZS_ySAMZErseKJ8dHo"); // Укажите ваш токен + List cmds = new(); + await Bot.SetMyCommandsAsync(cmds); + LoadEmployees(); + var me = await Bot.GetMeAsync(); + Console.WriteLine($"Бот запущен: @{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); + + Console.WriteLine("Нажмите Enter для выхода..."); + Console.ReadLine(); + } + + private static async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) + { + if (update.Type == UpdateType.Message && update.Message?.Text != null) + { + var message = update.Message; + + if (message.Text.StartsWith("/start")) + { + await StartCommand(message); + } + else if (message.Text.StartsWith("/choose_department")) + { + await ChooseDepartment(message); + } + else if (message.ReplyToMessage != null && message.ReplyToMessage.Text!.StartsWith("Выберите подразделение")) + { + await HandleDepartmentSelection(message); + } + else + { + await HandleSearchInput(message); + } + } + else if (update.Type == UpdateType.CallbackQuery) + { + var message = update.CallbackQuery.Message; + string data = update.CallbackQuery.Data ?? string.Empty; + string[] dataParts = data.Split('/'); + if (dataParts.Length > 0) + { + switch (dataParts[0]) + { + case "addcontact": + { + await HandleAddContact(update.CallbackQuery, dataParts[1]); + break; + } + default: + break; + } + } + + } + } + + private static async Task StartCommand(Message message) + { + string text = "Привет! Я бот для поиска контактов сотрудников. Введите запрос.\n"; + //text += "/choose_department - Выбрать подразделение\n"; + await Bot.SendTextMessageAsync(message.Chat.Id, text); + } + + 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 = "Выберите подразделение:\n"; + text += string.Join("\n", departmentsToShow.Select((d, i) => $"{i + 1 + CurrentPage * PageSize}. {d}")); + + // Создание кнопок + var inlineKeyboard = new List>(); + if (CurrentPage > 0) + { + inlineKeyboard.Add(new List + { + InlineKeyboardButton.WithCallbackData("◀️ Назад", "prev") + }); + } + if (CurrentPage < totalPages - 1) + { + inlineKeyboard.Add(new List + { + InlineKeyboardButton.WithCallbackData("Вперед ▶️", "next") + }); + } + + var keyboardMarkup = new InlineKeyboardMarkup(inlineKeyboard); + + await Bot.SendTextMessageAsync(chatId, text, replyMarkup: keyboardMarkup); + } + + private static async Task HandleSearchInput(Message message) + { + if (!string.IsNullOrEmpty(message.Text)) + { + string query = message.Text.Trim().ToLower(); + if (query.Length < 3) + { + await Bot.SendTextMessageAsync(message.Chat.Id, "Минимальная длина строки поиска - 3 символа."); + 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(); + + // Отправка результатов поиска + await ShowEmployeeResults(message.Chat.Id, results); + } + } + + private static async Task HandleAddContact(CallbackQuery callbackQuery, string id) + { + if (callbackQuery.Message != null) + { + var employee = Employees.Where(e => e.Id == id).First(); + 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); + await Task.Delay(999); + } + } + await Bot.AnswerCallbackQueryAsync(callbackQuery.Id); + } + + private static async Task HandleDepartmentSelection(Message message) + { + if (message.Text.StartsWith("◀️ Назад")) + { + if (CurrentPage > 0) + { + 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, "Некорректный выбор."); + } + } + } + + private static async Task ShowEmployeeResults(long chatId, List results) + { + if (results.Any()) + { + foreach (var employee in results) + { + 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}")); + + var card = CreateEmployeeCard(employee); + string photoPath = $"data/photos/{employee.FullName}.jpg"; + try + { + 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); + } + } + } + else + { + await Bot.SendTextMessageAsync(chatId, "Сотрудники не найдены."); + } + } + + static string GetEventStatus(Employee employee) + { + if (employee.StartDate == null || employee.EndDate == null) + { + return string.Empty; + } + DateTime today = DateTime.Now; + TimeSpan oneWeekBeforeEvent = (DateTime)employee.StartDate - today; + + if (employee.StartDate <= today && today <= employee.EndDate) + { + // Сотрудник участвует в событии + return $"🟥 {employee.State} с {employee.StartDate:dd.MM.yyyy} по {employee.EndDate:dd.MM.yyyy}"; + } + else if (oneWeekBeforeEvent.TotalDays <= 7 && oneWeekBeforeEvent.TotalDays > 0) + { + // Событие начнется через неделю + return $"🟩 {employee.State} с {employee.StartDate:dd.MM.yyyy} по {employee.EndDate:dd.MM.yyyy}"; + } + + // Если событие не указано или неактуально + return string.Empty; + } + + private static string CreateEmployeeCard(Employee employee) + { + EmployeeContact contact = new(employee); + string card = $"👤 {contact.FullName}\n" + + $"🏢 {contact.Department}\n" + + $"🎓 {contact.Position}\n"; + + if (!string.IsNullOrEmpty(contact.Mail)) + card += $"✉️ {contact.Mail}\n"; + + if (!string.IsNullOrEmpty(contact.Mobile)) + card += $"📱 {contact.Mobile}\n"; + if (!string.IsNullOrEmpty(contact.ExtensionNumber)) + card += $"📞 {contact.ExtensionNumber}\n"; + + //if (!string.IsNullOrEmpty(employee.IP)) + // card += $"🌐 {employee.IP}\n"; + + card += GetEventStatus(employee); + + return card; + } + + private static string CreateEmployeeCard2(Employee employee) + { + 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"; // Добавление ссылки на фото + + if (!string.IsNullOrEmpty(employee.Mail)) + card += $"✉️ Почта: {employee.Mail}\n"; + if (!string.IsNullOrEmpty(employee.Phone)) + card += $"📞 Телефон: {employee.Phone}\n"; + if (!string.IsNullOrEmpty(employee.Mobile)) + card += $"📱 Мобильный: +{employee.Mobile}\n"; + if (!string.IsNullOrEmpty(employee.IP)) + card += $"🌐 IP: {employee.IP}\n"; + + card += GetEventStatus(employee); + + return card; + } + + private static void LoadEmployees() + { + // Загрузка данных сотрудников из JSON файла + var json = System.IO.File.ReadAllText("data/contacts.json"); + Employees = JsonConvert.DeserializeObject>(json)!; + } + + private static Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken) + { + Console.WriteLine($"Произошла ошибка: {exception.Message}"); + return Task.CompletedTask; + } +} diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..1258e0b --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "nsszcontactbot": { + "commandName": "Project", + "workingDirectory": "$(ProjectDir)" + } + } +} \ No newline at end of file diff --git a/nsszcontactbot.csproj b/nsszcontactbot.csproj new file mode 100644 index 0000000..49a15d5 --- /dev/null +++ b/nsszcontactbot.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/nsszcontactbot.sln b/nsszcontactbot.sln new file mode 100644 index 0000000..5f3bb8b --- /dev/null +++ b/nsszcontactbot.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nsszcontactbot", "nsszcontactbot.csproj", "{B7AD2DE2-039F-410C-BFD0-E87C9887F4C9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B7AD2DE2-039F-410C-BFD0-E87C9887F4C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7AD2DE2-039F-410C-BFD0-E87C9887F4C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7AD2DE2-039F-410C-BFD0-E87C9887F4C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7AD2DE2-039F-410C-BFD0-E87C9887F4C9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {71E7013F-F654-4F53-ADF2-CB968889042E} + EndGlobalSection +EndGlobal diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..25dc8ed --- /dev/null +++ b/nuget.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file