Files
nssz-contact-bot/Program.cs
T

602 lines
22 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Newtonsoft.Json;
using Telegram.Bot;
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;
public class Program
{
private static ITelegramBotClient Bot;
private static List<Employee> Employees = new();
private static List<string> Departments = new();
private static IConfigurationRoot config;
private static FavoriteContactsManager FavoritesManager;
//private static ILogger<Program> Logger;
public static async Task<int> Main(string[] args)
{
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<BotCommand> cmds = new();
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<UpdateType>() // 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
)
{
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.EndsWith("Список подразделений"))
{
await ChooseDepartment(message);
}
else if (message.Text.EndsWith("Избранные контакты"))
{
await HandleShowFavorites(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;
}
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";
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();
await ShowDepartments(message.Chat.Id);
}
private static async Task ShowDepartments(long chatId)
{
string text = "Выберите подразделение:";
var keyboardMarkup = new InlineKeyboardMarkup();
var departmentIndex = 0;
foreach (var department in Departments)
{
keyboardMarkup
.AddNewRow()
.AddButton($"{department}", $"showdepartment/{departmentIndex++}");
}
//keyboardMarkup.AddCloseButton();
await Bot.SendMessage(chatId, text, replyMarkup: keyboardMarkup);
await Task.Delay(999);
}
private static async Task HandleSearchInput(Message message)
{
if (!string.IsNullOrEmpty(message.Text))
{
string query = message.Text.Trim().ToLower();
if (query.Length < 4)
{
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();
// Отправка результатов поиска
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);
}
}
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 = $"<b>{contact.FullName}</b>"
};
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.AnswerCallbackQuery(callbackQuery.Id);
}
private static async Task HandleAddFavorite(CallbackQuery callbackQuery, string contactId)
{
if (callbackQuery.Message != null)
{
var employee = Employees.Where(e => e.Id == contactId).First();
if (employee != null)
{
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 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<Employee> results, bool? isFavorite = false)
{
if (results.Any())
{
if (results.Count < 10)
{
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();
foreach (var employee in results)
{
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.SendMessage(message.Chat.Id, "🔍 Извините, мы ничего не нашли.");
await Task.Delay(999);
}
}
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 =
$"👤 <b>{contact.FullName}</b>\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 =
$"👤 <b>{employee.FullName}</b>\n"
+ $"🏢 Отдел: {employee.Department}\n"
+ $"🎓 Должность: {employee.Position}\n"
+ $"📸 Фото: <a href='{photoUrl}'>Посмотреть фото</a>\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 Timer? updateTimer = null;
private static void StartUpdatingEmployeesData(int intervalMs = 1 * 60 * 60 * 1000)
{
if (updateTimer != null) return;
updateTimer = new Timer(intervalMs);
updateTimer.Elapsed += UpdateTimer_Elapsed;
updateTimer.AutoReset = true; // Запускать снова после завершения
updateTimer.Enabled = true;
}
private static void UpdateTimer_Elapsed(object? sender, ElapsedEventArgs e)
{
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<List<Employee>>(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;
}
}