Add Telegram bot integration and enhance user management features
- Introduced Telegram.Bot package for bot functionality. - Updated user management to include Telegram linking and blocking features. - Enhanced activation request handling with notifications via Telegram. - Added new database entities for Telegram link tokens and login requests. - Implemented traffic synchronization for client stats in the XuiPanelGateway. - Updated application structure to support new test projects and improved dependency injection for Telegram services.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PnvPanel.Application.Common.Interfaces;
|
||||
using PnvPanel.Application.Common.Models;
|
||||
@@ -149,6 +150,67 @@ internal sealed class XuiPanelGateway(
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<IReadOnlyDictionary<string, ClientTrafficInfo>>> GetClientTrafficAsync(
|
||||
Node node, string inboundRemoteId, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = GetClient(node);
|
||||
var remoteInbound = await client.GetInboundAsync(inboundRemoteId, cancellationToken);
|
||||
if (remoteInbound is null)
|
||||
{
|
||||
return Result.Failure<IReadOnlyDictionary<string, ClientTrafficInfo>>(
|
||||
Error.Failure("Xui.InboundNotFound", "Inbound не найден на панели."));
|
||||
}
|
||||
|
||||
return Result.Success(ParseClientStats(remoteInbound.RawInboundJson));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<IReadOnlyDictionary<string, ClientTrafficInfo>>(
|
||||
Error.Failure("Xui.TrafficFetchFailed", $"Не удалось получить трафик: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ThreeXui.Net типизированно не отдаёт трафик по клиентам — достаём его из сырого JSON инбаунда:
|
||||
/// стандартное поле 3x-ui API "clientStats": [{ "email": "...", "up": N, "down": N }, ...].
|
||||
/// Формат форка может отличаться — при ошибке парсинга просто возвращаем пусто, не валим синхронизацию.
|
||||
/// </summary>
|
||||
private static IReadOnlyDictionary<string, ClientTrafficInfo> ParseClientStats(string? rawInboundJson)
|
||||
{
|
||||
var result = new Dictionary<string, ClientTrafficInfo>();
|
||||
if (string.IsNullOrWhiteSpace(rawInboundJson))
|
||||
return result;
|
||||
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(rawInboundJson);
|
||||
if (!doc.RootElement.TryGetProperty("clientStats", out var clientStats) || clientStats.ValueKind != JsonValueKind.Array)
|
||||
return result;
|
||||
|
||||
foreach (var stat in clientStats.EnumerateArray())
|
||||
{
|
||||
if (!stat.TryGetProperty("email", out var emailProp) || emailProp.ValueKind != JsonValueKind.String)
|
||||
continue;
|
||||
|
||||
var email = emailProp.GetString();
|
||||
if (string.IsNullOrEmpty(email))
|
||||
continue;
|
||||
|
||||
var up = stat.TryGetProperty("up", out var upProp) ? upProp.GetInt64() : 0;
|
||||
var down = stat.TryGetProperty("down", out var downProp) ? downProp.GetInt64() : 0;
|
||||
result[email] = new ClientTrafficInfo(up, down);
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// Не удалось распарсить — возвращаем пусто, вызывающий код просто пропустит синк для этого инбаунда.
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void InvalidateClient(Guid nodeId)
|
||||
{
|
||||
if (_clients.TryRemove(nodeId, out var lazy) && lazy.IsValueCreated)
|
||||
|
||||
Reference in New Issue
Block a user