ci / build-test (pull_request) Failing after 1m58s
tgStart ждёт старт QR до показа картинки (устранена гонка с 404). Вкладка Telegram опрашивает статус и обновляет QR по таймеру, не останавливаясь до авторизации. Статус: account берётся из живого статуса сервиса, connected = phase ready (авторизация), а не транспорт.
95 lines
4.0 KiB
C#
95 lines
4.0 KiB
C#
using System.Text.Json;
|
|
using Deal.Contracts.Integrations.Abstractions;
|
|
using Deal.Contracts.Integrations.Models;
|
|
using Deal.Modules.Settings.Application.Abstractions;
|
|
using Deal.Modules.Settings.Application.Models;
|
|
using Deal.Modules.Telegram.Application;
|
|
using Deal.Modules.Telegram.Application.Models;
|
|
|
|
namespace Deal.Api.Telegram;
|
|
|
|
/// <summary>
|
|
/// Сборка статуса вкладки Telegram — GET /api/tg/status.
|
|
/// </summary>
|
|
/// <param name="gateway">Порт-гейт telegram-service (живой статус аккаунта).</param>
|
|
/// <param name="dialogs">Сервис каталога диалогов ядра (счётчик мониторящихся).</param>
|
|
/// <param name="settings">KV-хранилище настроек тенанта (tgAccount).</param>
|
|
/// <param name="keys">Глобальные ключи приложения Telegram (keysSet).</param>
|
|
public sealed class TgStatusService(
|
|
ITelegramGateway gateway,
|
|
DialogsService dialogs,
|
|
ISettingsStore settings,
|
|
TelegramKeysService keys)
|
|
{
|
|
private const string IdlePhase = "idle";
|
|
private const string ReadyPhase = "ready";
|
|
|
|
// Опции JSON KV-значений статуса: camelCase (как пишет ингресс) + терпимость регистра.
|
|
private static readonly JsonSerializerOptions KvJsonOptions = new()
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
PropertyNameCaseInsensitive = true,
|
|
};
|
|
|
|
/// <summary>
|
|
/// Форма GET /api/tg/status текущего тенанта.
|
|
/// </summary>
|
|
/// <returns>Полный статус вкладки Telegram.</returns>
|
|
public async Task<TgStatusDto> GetAsync(CancellationToken ct)
|
|
{
|
|
TelegramAccountStatusDto live = await ReadLiveAsync(ct).ConfigureAwait(false);
|
|
// «Подключён» для UI = авторизован (phase ready). Транспортный connected сервиса
|
|
// означает лишь живость соединения и не гарантирует вход — в UI он даёт «зависание».
|
|
bool authorized = string.Equals(live.Phase, ReadyPhase, StringComparison.Ordinal);
|
|
// Живой account (имя из Telegram) приоритетнее KV: при QR-входе KV ещё не заполнен.
|
|
string account = string.IsNullOrEmpty(live.Account)
|
|
? await ReadAccountAsync(ct).ConfigureAwait(false)
|
|
: live.Account;
|
|
int monitored = (await dialogs.ListMonitoredIdsAsync(ct).ConfigureAwait(false)).Count;
|
|
TgKeysSnapshot snapshot = await keys.GetAsync(ct).ConfigureAwait(false);
|
|
|
|
return new TgStatusDto(
|
|
Phase: live.Phase,
|
|
Connected: authorized,
|
|
Listener: live.Listener,
|
|
Account: account,
|
|
Monitored: monitored,
|
|
KeysSet: snapshot.KeysSet,
|
|
Error: live.Error,
|
|
QrUrl: live.QrUrl);
|
|
}
|
|
|
|
private async Task<TelegramAccountStatusDto> ReadLiveAsync(CancellationToken ct)
|
|
{
|
|
try
|
|
{
|
|
return await gateway.StatusAsync(ct).ConfigureAwait(false);
|
|
}
|
|
catch (Exception exception) when (exception is not OperationCanceledException || !ct.IsCancellationRequested)
|
|
{
|
|
return new TelegramAccountStatusDto(IdlePhase, Connected: false, Listener: false, string.Empty, null, null);
|
|
}
|
|
}
|
|
|
|
private async Task<string> ReadAccountAsync(CancellationToken ct)
|
|
{
|
|
SettingValue? row = await settings.GetAsync(SettingsKeys.TgAccount, ct).ConfigureAwait(false);
|
|
if (row is null)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
try
|
|
{
|
|
using JsonDocument document = JsonDocument.Parse(row.ValueJson);
|
|
return document.RootElement.ValueKind == JsonValueKind.String
|
|
? document.RootElement.GetString() ?? string.Empty
|
|
: string.Empty;
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
}
|
|
}
|