Добавлены функции резервного копирования контактов, улучшена обработка фотографий сотрудников и обновлены зависимости
Gitea Actions Demo / Explore-Gitea-Actions (push) Canceled after 0s
Gitea Actions Demo / Explore-Gitea-Actions (push) Canceled after 0s
This commit is contained in:
@@ -438,3 +438,4 @@ FodyWeavers.xsd
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
|
||||
unique_contacts_backup/*
|
||||
@@ -4,10 +4,10 @@ namespace nsszcontactbot
|
||||
{
|
||||
internal static class InlineKeyboardMarkupExtensions
|
||||
{
|
||||
internal static InlineKeyboardMarkup AddCloseButton(this InlineKeyboardMarkup markup, bool newRow = true)
|
||||
internal static InlineKeyboardMarkup AddCloseButton(this InlineKeyboardMarkup markup, string text = "❌ Закрыть", bool newRow = true)
|
||||
{
|
||||
var buttonRow = newRow ? markup.AddNewRow() : markup;
|
||||
return buttonRow.AddButton("❌ Закрыть", "close");
|
||||
return buttonRow.AddButton(text, "close");
|
||||
}
|
||||
|
||||
internal static InlineKeyboardMarkup AddPrevPageButton(this InlineKeyboardMarkup markup, int currentPage, string context = "", string text = "⬅️ Назад")
|
||||
|
||||
@@ -13,9 +13,11 @@ public class PhotosManager(ConnectionMultiplexer redis, string PhotoBasePath)
|
||||
public async Task CacheEmployeePhotoAsync(Employee employee, string fileId, TimeSpan ttl = default)
|
||||
{
|
||||
string entryName = GetEntryName(employee);
|
||||
#pragma warning disable CS4014 // Так как этот вызов не ожидается, выполнение существующего метода продолжается до тех пор, пока вызов не будет завершен
|
||||
_redisDb.HashSetAsync(Key, [new(entryName, fileId)]).ConfigureAwait(false);
|
||||
ttl = ttl == default ? TimeSpan.FromDays(30) : ttl;
|
||||
_redisDb.HashFieldExpireAsync(Key, [new(entryName)], ttl).ConfigureAwait(false);
|
||||
#pragma warning restore CS4014 // Так как этот вызов не ожидается, выполнение существующего метода продолжается до тех пор, пока вызов не будет завершен
|
||||
}
|
||||
|
||||
public async Task<InputFile?> GetEmployeePhotoAsync(Employee employee)
|
||||
|
||||
+49
-14
@@ -2,6 +2,8 @@
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using StackExchange.Redis;
|
||||
using System.IO.Hashing;
|
||||
using System.Text;
|
||||
using System.Timers;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Polling;
|
||||
@@ -14,12 +16,12 @@ namespace nsszcontactbot;
|
||||
|
||||
public class Program
|
||||
{
|
||||
private static ITelegramBotClient Bot;
|
||||
private static ITelegramBotClient Bot = default!;
|
||||
private static List<Employee> Employees = [];
|
||||
private static List<string> Departments = [];
|
||||
private static IConfigurationRoot config;
|
||||
private static FavoriteContactsManager FavoritesManager;
|
||||
private static PhotosManager PhotosManager;
|
||||
private static IConfigurationRoot config = default!;
|
||||
private static FavoriteContactsManager FavoritesManager = default!;
|
||||
private static PhotosManager PhotosManager = default!;
|
||||
//private static ILogger<Program> Logger;
|
||||
|
||||
public static async Task<int> Main(string[] args)
|
||||
@@ -57,7 +59,7 @@ public class Program
|
||||
|
||||
var me = await Bot.GetMe();
|
||||
|
||||
Log.Information($"Бот запущен: @{me.Username}");
|
||||
Log.Information("Бот запущен: @{Username}", me.Username);
|
||||
|
||||
// Запуск обработки обновлений
|
||||
var cancellationToken = new CancellationTokenSource().Token;
|
||||
@@ -89,7 +91,7 @@ public class Program
|
||||
if (update.Type == UpdateType.Message && update.Message?.Text != null)
|
||||
{
|
||||
var message = update.Message;
|
||||
Log.Debug($"INPUT: from={message.From?.Username} text={message.Text}");
|
||||
Log.Debug("Входящий текст (From=\"{Username}\", MessageId=\"{MessageId}\"): {Text}", message.From?.ToString() ?? "<!--message.From is null-->", message.Id, message.Text);
|
||||
|
||||
if (message.Text.StartsWith("/start"))
|
||||
{
|
||||
@@ -97,7 +99,7 @@ public class Program
|
||||
}
|
||||
else if (message.Text.EndsWith("Подразделения"))
|
||||
{
|
||||
await ChooseDepartment(message);
|
||||
await HandleShowDepartments(message);
|
||||
}
|
||||
else if (message.Text.EndsWith("Избранные"))
|
||||
{
|
||||
@@ -111,6 +113,8 @@ public class Program
|
||||
else if (update.Type == UpdateType.CallbackQuery)
|
||||
{
|
||||
string data = update.CallbackQuery!.Data ?? string.Empty;
|
||||
var callbackQuery = update.CallbackQuery!;
|
||||
Log.Debug("Нажатие кнопки (From=\"{Username}\", CallbackQueryId=\"{CallbackQueryId}\"): {Data}", callbackQuery.From?.ToString() ?? "<!--cbquery.From is null-->", callbackQuery.Id, data);
|
||||
string[] dataParts = data.Split('/');
|
||||
if (dataParts.Length > 0)
|
||||
{
|
||||
@@ -185,7 +189,7 @@ public class Program
|
||||
await Bot.SendMessage(message.Chat.Id, text, replyMarkup: new string[] { "❤️ Избранные", "🏢 Подразделения" });
|
||||
}
|
||||
|
||||
private static async Task ChooseDepartment(Message message)
|
||||
private static async Task HandleShowDepartments(Message message)
|
||||
{
|
||||
Departments = [.. Employees.OrderBy(e => e.DepartmentCode).Select(e => e.Department).Distinct()];
|
||||
await ShowDepartments(message.Chat.Id);
|
||||
@@ -382,10 +386,10 @@ public class Program
|
||||
if (employee != null)
|
||||
{
|
||||
await FavoritesManager.RemoveFavoriteAsync(callbackQuery.From.Id, contactId);
|
||||
foreach (var row in callbackQuery.Message.ReplyMarkup.InlineKeyboard)
|
||||
foreach (var row in callbackQuery.Message.ReplyMarkup!.InlineKeyboard)
|
||||
foreach (var cell in row)
|
||||
{
|
||||
if (cell.CallbackData.Contains("delfavorite"))
|
||||
if (cell.CallbackData!.Contains("delfavorite"))
|
||||
{
|
||||
cell.CallbackData = $"addfavorite/{contactId}";
|
||||
cell.Text = "❤️ В избранные";
|
||||
@@ -533,7 +537,7 @@ public class Program
|
||||
|
||||
private static async Task ShowEmployeeResults(Message message, List<Employee> results, bool? isFavorite = false, int page = 1, int pageSize = 10)
|
||||
{
|
||||
if (results.Any())
|
||||
if (results.Count != 0)
|
||||
{
|
||||
if (results.Count <= pageSize)
|
||||
{
|
||||
@@ -670,7 +674,7 @@ public class Program
|
||||
card += $"📞 {contact.ExtensionNumber}\n";
|
||||
|
||||
if (!string.IsNullOrEmpty(employee.IP))
|
||||
card += $"🌐 {employee.IP}\n";
|
||||
card += $"🖥 {employee.IP}\n";
|
||||
|
||||
card += GetEventStatus(employee);
|
||||
|
||||
@@ -692,7 +696,7 @@ public class Program
|
||||
LoadEmployees();
|
||||
}
|
||||
|
||||
private static int prevHash = 0;
|
||||
private static string prevHash = string.Empty;
|
||||
private static bool LoadEmployees()
|
||||
{
|
||||
try
|
||||
@@ -713,13 +717,19 @@ public class Program
|
||||
json = File.ReadAllText(filePath);
|
||||
}
|
||||
// Загрузка данных сотрудников из JSON файла при наличии изменений
|
||||
int newHash = json.GetHashCode();
|
||||
var newHash = CalculateHash(json);
|
||||
if (prevHash == newHash)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
prevHash = newHash;
|
||||
Employees = JsonConvert.DeserializeObject<List<Employee>>(json)!;
|
||||
|
||||
var backupFilename = BackupContactsJSON(json);
|
||||
if (!string.IsNullOrEmpty(backupFilename))
|
||||
{
|
||||
Log.Debug("Данные обновлены, создана резервная копия: {BackupFilename}", backupFilename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -748,6 +758,31 @@ public class Program
|
||||
}
|
||||
}
|
||||
|
||||
private static string CalculateHash(string data)
|
||||
{
|
||||
byte[] hashBytes = Crc32.Hash(Encoding.UTF8.GetBytes(data));
|
||||
return Convert.ToHexString(hashBytes);
|
||||
}
|
||||
private static string BackupContactsJSON(string json)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(json, nameof(json));
|
||||
var backupDirectory = config["Paths:ContactsBackupDirectory"] ?? string.Empty;
|
||||
if (!string.IsNullOrEmpty(backupDirectory))
|
||||
{
|
||||
if (!Directory.Exists(backupDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(backupDirectory);
|
||||
}
|
||||
|
||||
var hashString = CalculateHash(json).ToUpperInvariant();
|
||||
|
||||
var filename = Path.GetFullPath(Path.Combine(backupDirectory, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{Employees.Count}_contacts_{hashString}.json"));
|
||||
File.WriteAllText(filename, json);
|
||||
return filename;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static void StartBirthdayNotifications()
|
||||
{
|
||||
var timer = new Timer(TimeSpan.FromHours(24).TotalMilliseconds);
|
||||
|
||||
+2
-1
@@ -2,7 +2,8 @@
|
||||
"Paths": {
|
||||
"ContactsJsonPath": "http://contacts.nssz.local/data/contacts.json",
|
||||
"PersistentContactsJsonPath": "PersistentContacts.json",
|
||||
"PhotoBasePath": "photos"
|
||||
"PhotoBasePath": "photos",
|
||||
"ContactsBackupDirectory": "unique_contacts_backup"
|
||||
},
|
||||
"Telegram": {
|
||||
"BotToken": "8155021946:AAGbfJnLa0sTWxqFB9yjbXhbTedBw4wvcQE"
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.10" />
|
||||
<PackageReference Include="Telegram.Bot" Version="22.4.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user