Явные реализации интерфейсов в проде

TenantContext, LocalTelegramGateway, DefaultPasswordHasher и
WTelegramSessionClient переведены на явные реализации; типы в тестах
приведены к ITenantContext.
This commit is contained in:
2026-09-13 14:15:23 +03:00
parent a2da86ced7
commit 0d2204219a
8 changed files with 33 additions and 87 deletions
@@ -3,22 +3,17 @@ using Deal.SharedKernel.Tenants.Models;
namespace Deal.Infrastructure.Data; namespace Deal.Infrastructure.Data;
/// <summary>
/// Контекст тенанта на AsyncLocal
/// </summary>
public sealed class TenantContext : ITenantContext public sealed class TenantContext : ITenantContext
{ {
private static readonly AsyncLocal<TenantId?> Current = new(); private static readonly AsyncLocal<TenantId?> Current = new();
public TenantId? TenantId => Current.Value; TenantId? ITenantContext.TenantId => Current.Value;
public bool HasTenant => Current.Value is not null; bool ITenantContext.HasTenant => Current.Value is not null;
public string? SchemaName => Current.Value?.SchemaName; string? ITenantContext.SchemaName => Current.Value?.SchemaName;
/// <inheritdoc />
void ITenantContext.SetTenant(TenantId tenantId) => Current.Value = tenantId; void ITenantContext.SetTenant(TenantId tenantId) => Current.Value = tenantId;
/// <inheritdoc />
void ITenantContext.Reset() => Current.Value = null; void ITenantContext.Reset() => Current.Value = null;
} }
@@ -3,21 +3,16 @@ using Deal.Contracts.Integrations.Models;
namespace Deal.Infrastructure.Integrations.Services; namespace Deal.Infrastructure.Integrations.Services;
/// <summary>
/// Локальная заглушка <see cref="ITelegramGateway"/> без telegram-service.
/// </summary>
public sealed class LocalTelegramGateway : ITelegramGateway public sealed class LocalTelegramGateway : ITelegramGateway
{ {
// Фаза idle-формы (аккаунт не подключён — сервиса нет). // Фаза idle-формы (аккаунт не подключён — сервиса нет).
private const string IdlePhase = "idle"; private const string IdlePhase = "idle";
/// <inheritdoc />
Task<TelegramAccountStatusDto> ITelegramGateway.StatusAsync(CancellationToken ct) Task<TelegramAccountStatusDto> ITelegramGateway.StatusAsync(CancellationToken ct)
{ {
return Task.FromResult(new TelegramAccountStatusDto(IdlePhase, false, false, string.Empty, null, null)); return Task.FromResult(new TelegramAccountStatusDto(IdlePhase, false, false, string.Empty, null, null));
} }
/// <inheritdoc />
Task<TelegramAuthResultDto> ITelegramGateway.StartPhoneAsync( Task<TelegramAuthResultDto> ITelegramGateway.StartPhoneAsync(
string phone, string phone,
int apiId, int apiId,
@@ -25,76 +20,61 @@ public sealed class LocalTelegramGateway : ITelegramGateway
CancellationToken ct) CancellationToken ct)
=> Task.FromResult(new TelegramAuthResultDto(IdlePhase, null)); => Task.FromResult(new TelegramAuthResultDto(IdlePhase, null));
/// <inheritdoc />
Task<TelegramAuthResultDto> ITelegramGateway.StartQrAsync( Task<TelegramAuthResultDto> ITelegramGateway.StartQrAsync(
int apiId, int apiId,
string apiHash, string apiHash,
CancellationToken ct) CancellationToken ct)
=> Task.FromResult(new TelegramAuthResultDto(IdlePhase, null)); => Task.FromResult(new TelegramAuthResultDto(IdlePhase, null));
/// <inheritdoc /> Task<string> ITelegramGateway.SendCodeAsync(string code, CancellationToken ct) => Task.FromResult(IdlePhase);
public Task<string> SendCodeAsync(string code, CancellationToken ct) => Task.FromResult(IdlePhase);
/// <inheritdoc /> Task<string> ITelegramGateway.SendPasswordAsync(string password, CancellationToken ct) => Task.FromResult(IdlePhase);
public Task<string> SendPasswordAsync(string password, CancellationToken ct) => Task.FromResult(IdlePhase);
/// <inheritdoc />
Task ITelegramGateway.LogoutAsync(CancellationToken ct) => Task.CompletedTask; Task ITelegramGateway.LogoutAsync(CancellationToken ct) => Task.CompletedTask;
/// <inheritdoc />
Task<IReadOnlyList<TelegramDialogEntryDto>> ITelegramGateway.RefreshDialogsAsync(CancellationToken ct) Task<IReadOnlyList<TelegramDialogEntryDto>> ITelegramGateway.RefreshDialogsAsync(CancellationToken ct)
=> Task.FromResult<IReadOnlyList<TelegramDialogEntryDto>>([]); => Task.FromResult<IReadOnlyList<TelegramDialogEntryDto>>([]);
/// <inheritdoc />
Task ITelegramGateway.SetMonitorAsync( Task ITelegramGateway.SetMonitorAsync(
string dialogId, string dialogId,
bool enabled, bool enabled,
CancellationToken ct) => Task.CompletedTask; CancellationToken ct) => Task.CompletedTask;
/// <inheritdoc />
Task ITelegramGateway.SetMonitorAllAsync(bool enabled, CancellationToken ct) => Task.CompletedTask; Task ITelegramGateway.SetMonitorAllAsync(bool enabled, CancellationToken ct) => Task.CompletedTask;
/// <inheritdoc /> Task<int> ITelegramGateway.BackfillAsync(
public Task<int> BackfillAsync(
string dialogId, string dialogId,
bool force, bool force,
CancellationToken ct) => Task.FromResult(0); CancellationToken ct) => Task.FromResult(0);
/// <inheritdoc />
Task<IReadOnlyList<TelegramRecentMessageDto>> ITelegramGateway.ReadRecentAsync( Task<IReadOnlyList<TelegramRecentMessageDto>> ITelegramGateway.ReadRecentAsync(
string dialogId, string dialogId,
int limit, int limit,
CancellationToken ct) CancellationToken ct)
=> Task.FromResult<IReadOnlyList<TelegramRecentMessageDto>>([]); => Task.FromResult<IReadOnlyList<TelegramRecentMessageDto>>([]);
/// <inheritdoc />
Task<TelegramSourceContentDto> ITelegramGateway.ReadSourceAsync( Task<TelegramSourceContentDto> ITelegramGateway.ReadSourceAsync(
string dialogId, string dialogId,
long msgId, long msgId,
CancellationToken ct) CancellationToken ct)
=> Task.FromResult(new TelegramSourceContentDto(false, null, null)); => Task.FromResult(new TelegramSourceContentDto(false, null, null));
/// <inheritdoc />
Task<IReadOnlyList<TelegramDialogEntryDto>> ITelegramGateway.SearchAsync( Task<IReadOnlyList<TelegramDialogEntryDto>> ITelegramGateway.SearchAsync(
string query, string query,
int limit, int limit,
CancellationToken ct) CancellationToken ct)
=> Task.FromResult<IReadOnlyList<TelegramDialogEntryDto>>([]); => Task.FromResult<IReadOnlyList<TelegramDialogEntryDto>>([]);
/// <inheritdoc />
Task<TelegramChannelInfoDto> ITelegramGateway.InfoAsync(string dialogId, CancellationToken ct) Task<TelegramChannelInfoDto> ITelegramGateway.InfoAsync(string dialogId, CancellationToken ct)
=> Task.FromResult(new TelegramChannelInfoDto(dialogId, string.Empty, string.Empty, string.Empty, SourceDefaults.DefaultHue, null, false)); => Task.FromResult(new TelegramChannelInfoDto(dialogId, string.Empty, string.Empty, string.Empty, SourceDefaults.DefaultHue, null, false));
/// <inheritdoc />
Task<TelegramEvalReadDto> ITelegramGateway.ReadForEvalAsync( Task<TelegramEvalReadDto> ITelegramGateway.ReadForEvalAsync(
string dialogId, string dialogId,
int limit, int limit,
CancellationToken ct) CancellationToken ct)
=> Task.FromResult(new TelegramEvalReadDto(false, "no_history", [])); => Task.FromResult(new TelegramEvalReadDto(false, "no_history", []));
/// <inheritdoc />
Task ITelegramGateway.JoinAsync(string username, CancellationToken ct) => Task.CompletedTask; Task ITelegramGateway.JoinAsync(string username, CancellationToken ct) => Task.CompletedTask;
/// <inheritdoc />
Task ITelegramGateway.LeaveAsync(string dialogId, CancellationToken ct) => Task.CompletedTask; Task ITelegramGateway.LeaveAsync(string dialogId, CancellationToken ct) => Task.CompletedTask;
} }
@@ -3,14 +3,9 @@ using Isopoh.Cryptography.Argon2;
namespace Deal.Modules.Tenants.Application.Services; namespace Deal.Modules.Tenants.Application.Services;
/// <summary>
/// Реализация <see cref="IPasswordHasher"/> на Argon2id.
/// </summary>
public sealed class DefaultPasswordHasher : IPasswordHasher public sealed class DefaultPasswordHasher : IPasswordHasher
{ {
/// <inheritdoc /> string IPasswordHasher.Hash(string password) => Argon2.Hash(password);
public string Hash(string password) => Argon2.Hash(password);
/// <inheritdoc /> bool IPasswordHasher.Verify(string password, string encodedHash) => Argon2.Verify(encodedHash, password);
public bool Verify(string password, string encodedHash) => Argon2.Verify(encodedHash, password);
} }
@@ -39,7 +39,7 @@ public sealed class DiscoveryWorkerSchedulerTests
TestDiscoveryStore StoreB, TestDiscoveryStore StoreB,
TestDiscoveryGateway GatewayA, TestDiscoveryGateway GatewayA,
TestDiscoveryGateway GatewayB, TestDiscoveryGateway GatewayB,
TenantContext TenantContext, ITenantContext TenantContext,
ListLogger Logs); ListLogger Logs);
[Fact] [Fact]
@@ -91,7 +91,7 @@ public sealed class DiscoveryWorkerSchedulerTests
private static Context CreateContext() private static Context CreateContext()
{ {
var tenants = new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)); var tenants = new TestTenantRepository(Tenant(TenantA), Tenant(TenantB));
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
var storeA = new TestDiscoveryStore(); var storeA = new TestDiscoveryStore();
var storeB = new TestDiscoveryStore(); var storeB = new TestDiscoveryStore();
var settingsA = new TestSettingsStore(); var settingsA = new TestSettingsStore();
@@ -41,7 +41,7 @@ public sealed class MlOutboxFlushSchedulerTests
{ {
var store = new TestMlLearningStore(); var store = new TestMlLearningStore();
SeedRows(store, count: 25, prefix: "a"); SeedRows(store, count: 25, prefix: "a");
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
await using ServiceProvider provider = BuildProvider( await using ServiceProvider provider = BuildProvider(
port, port,
new TestTenantRepository(Tenant(TenantA)), new TestTenantRepository(Tenant(TenantA)),
@@ -67,7 +67,7 @@ public sealed class MlOutboxFlushSchedulerTests
service.TrainUnavailable = true; service.TrainUnavailable = true;
var store = new TestMlLearningStore(); var store = new TestMlLearningStore();
SeedRows(store, count: 5, prefix: "a"); SeedRows(store, count: 5, prefix: "a");
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
await using ServiceProvider provider = BuildProvider( await using ServiceProvider provider = BuildProvider(
port, port,
new TestTenantRepository(Tenant(TenantA)), new TestTenantRepository(Tenant(TenantA)),
@@ -95,7 +95,7 @@ public sealed class MlOutboxFlushSchedulerTests
SeedRows(storeA, count: 12, prefix: "a"); SeedRows(storeA, count: 12, prefix: "a");
var storeB = new TestMlLearningStore(); var storeB = new TestMlLearningStore();
SeedRows(storeB, count: 3, prefix: "b"); SeedRows(storeB, count: 3, prefix: "b");
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
await using ServiceProvider provider = BuildProvider( await using ServiceProvider provider = BuildProvider(
port, port,
new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)), new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)),
@@ -121,7 +121,7 @@ public sealed class MlOutboxFlushSchedulerTests
{ {
var store = new TestMlLearningStore(); var store = new TestMlLearningStore();
SeedRows(store, count: 105, prefix: "a"); SeedRows(store, count: 105, prefix: "a");
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
await using ServiceProvider provider = BuildProvider( await using ServiceProvider provider = BuildProvider(
port, port,
new TestTenantRepository(Tenant(TenantA)), new TestTenantRepository(Tenant(TenantA)),
@@ -148,7 +148,7 @@ public sealed class MlOutboxFlushSchedulerTests
private static ServiceProvider BuildProvider( private static ServiceProvider BuildProvider(
int port, int port,
TestTenantRepository tenants, TestTenantRepository tenants,
TenantContext tenantContext, ITenantContext tenantContext,
Dictionary<Guid, TestMlLearningStore> storesByTenant) Dictionary<Guid, TestMlLearningStore> storesByTenant)
{ {
var services = new ServiceCollection(); var services = new ServiceCollection();
@@ -47,7 +47,7 @@ public sealed class PipelineWorkerSchedulerTests
TestKanjStore KanjB, TestKanjStore KanjB,
SseSubscription SubscriptionB, SseSubscription SubscriptionB,
PipelinePumpGate PumpGate, PipelinePumpGate PumpGate,
TenantContext TenantContext, ITenantContext TenantContext,
ListLogger Logs); ListLogger Logs);
// ─── Цикл: pump каждого тенанта в собственном scope + new_card ───────── // ─── Цикл: pump каждого тенанта в собственном scope + new_card ─────────
@@ -149,7 +149,7 @@ public sealed class PipelineWorkerSchedulerTests
private static Context CreateContext(bool withThrowingQueueReadA = false) private static Context CreateContext(bool withThrowingQueueReadA = false)
{ {
var tenants = new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)); var tenants = new TestTenantRepository(Tenant(TenantA), Tenant(TenantB));
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
var pipelineA = new TestPipelineStore(throwOnList: withThrowingQueueReadA); var pipelineA = new TestPipelineStore(throwOnList: withThrowingQueueReadA);
var pipelineB = new TestPipelineStore(); var pipelineB = new TestPipelineStore();
var kanjA = new TestKanjStore(); var kanjA = new TestKanjStore();
@@ -46,7 +46,7 @@ public sealed class StorageTickSchedulerTests
TestKanjStore storeB = StoreWithExpiredInboxCard("l_b_old"); TestKanjStore storeB = StoreWithExpiredInboxCard("l_b_old");
var settings = new TestSettingsStore(); var settings = new TestSettingsStore();
var tenants = new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)); var tenants = new TestTenantRepository(Tenant(TenantA), Tenant(TenantB));
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
await using ServiceProvider provider = BuildProvider(tenants, tenantContext, storeA, storeB, settings); await using ServiceProvider provider = BuildProvider(tenants, tenantContext, storeA, storeB, settings);
SseBroker broker = provider.GetRequiredService<SseBroker>(); SseBroker broker = provider.GetRequiredService<SseBroker>();
@@ -74,7 +74,7 @@ public sealed class StorageTickSchedulerTests
var storeB = new TestKanjStore(); var storeB = new TestKanjStore();
storeB.SeedCard(Card("l_b_fresh", KanbanColumns.Inbox, ReceivedAtMsAgo(TimeSpan.FromHours(1)))); storeB.SeedCard(Card("l_b_fresh", KanbanColumns.Inbox, ReceivedAtMsAgo(TimeSpan.FromHours(1))));
var settings = new TestSettingsStore(); var settings = new TestSettingsStore();
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
await using ServiceProvider provider = BuildProvider( await using ServiceProvider provider = BuildProvider(
new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)), new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)),
tenantContext, tenantContext,
@@ -100,7 +100,7 @@ public sealed class StorageTickSchedulerTests
{ {
// У тенанта A настройки падают (имитация сбоя схемы/БД) — тик A логирует ошибку, B обрабатывается. // У тенанта A настройки падают (имитация сбоя схемы/БД) — тик A логирует ошибку, B обрабатывается.
TestKanjStore storeB = StoreWithExpiredInboxCard("l_b_old"); TestKanjStore storeB = StoreWithExpiredInboxCard("l_b_old");
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
var settingsByTenant = new Dictionary<Guid, ISettingsStore> var settingsByTenant = new Dictionary<Guid, ISettingsStore>
{ {
[TenantA] = new ThrowingSettingsStore(), [TenantA] = new ThrowingSettingsStore(),
@@ -133,7 +133,7 @@ public sealed class StorageTickSchedulerTests
[Fact] [Fact]
public async Task RunCycle_TenantListFailure_DoesNotThrow() public async Task RunCycle_TenantListFailure_DoesNotThrow()
{ {
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
await using ServiceProvider provider = BuildProvider( await using ServiceProvider provider = BuildProvider(
new ThrowingTenantRepository(), new ThrowingTenantRepository(),
tenantContext, tenantContext,
@@ -152,7 +152,7 @@ public sealed class StorageTickSchedulerTests
{ {
var kanjStore = new TestKanjStore(); var kanjStore = new TestKanjStore();
var settings = new TestSettingsStore(); var settings = new TestSettingsStore();
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
var pipelineStoreA = new TestPipelineStore(); var pipelineStoreA = new TestPipelineStore();
long nowMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); long nowMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
pipelineStoreA.SeedRejected(Rejected("r_old_a", (long)(nowMs - TimeSpan.FromDays(4).TotalMilliseconds))); pipelineStoreA.SeedRejected(Rejected("r_old_a", (long)(nowMs - TimeSpan.FromDays(4).TotalMilliseconds)));
@@ -186,7 +186,7 @@ public sealed class StorageTickSchedulerTests
cardStoreA.SeedCard(HoldCard("c_a_future", title: "Будущий", reminderAtMs: NowMs() + 60_000)); cardStoreA.SeedCard(HoldCard("c_a_future", title: "Будущий", reminderAtMs: NowMs() + 60_000));
cardStoreA.SeedCard(HoldCard("c_a_work", stage: "work", title: "В работе", reminderAtMs: NowMs() - 60_000)); cardStoreA.SeedCard(HoldCard("c_a_work", stage: "work", title: "В работе", reminderAtMs: NowMs() - 60_000));
var cardStoreB = new TestKanjStore(); var cardStoreB = new TestKanjStore();
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
await using ServiceProvider provider = BuildProvider( await using ServiceProvider provider = BuildProvider(
new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)).Repository, new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)).Repository,
tenantContext, tenantContext,
@@ -221,7 +221,7 @@ public sealed class StorageTickSchedulerTests
cardStoreA.SeedCard(HoldCard("c_past", title: "Старое", reminderAtMs: NowMs() - 60_000)); cardStoreA.SeedCard(HoldCard("c_past", title: "Старое", reminderAtMs: NowMs() - 60_000));
var settingsA = new TestSettingsStore(); var settingsA = new TestSettingsStore();
settingsA.Preload(SettingsKeys.RemindersEnabled, "false"); settingsA.Preload(SettingsKeys.RemindersEnabled, "false");
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
await using ServiceProvider provider = BuildProvider( await using ServiceProvider provider = BuildProvider(
new TestTenantRepository(Tenant(TenantA)).Repository, new TestTenantRepository(Tenant(TenantA)).Repository,
tenantContext, tenantContext,
@@ -247,7 +247,7 @@ public sealed class StorageTickSchedulerTests
// и тик A завершается, тенант B обрабатывается (автоархив + тост) — проход жив. // и тик A завершается, тенант B обрабатывается (автоархив + тост) — проход жив.
var cardStoreA = new TestKanjStore(throwOnDueReminders: true); var cardStoreA = new TestKanjStore(throwOnDueReminders: true);
TestKanjStore storeB = StoreWithExpiredInboxCard("l_b_old"); TestKanjStore storeB = StoreWithExpiredInboxCard("l_b_old");
var tenantContext = new TenantContext(); ITenantContext tenantContext = new TenantContext();
await using ServiceProvider provider = BuildProvider( await using ServiceProvider provider = BuildProvider(
new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)).Repository, new TestTenantRepository(Tenant(TenantA), Tenant(TenantB)).Repository,
tenantContext, tenantContext,
@@ -280,7 +280,7 @@ public sealed class StorageTickSchedulerTests
// Возвращает: Провайдер с зарегистрированными сервисами теста. // Возвращает: Провайдер с зарегистрированными сервисами теста.
private static ServiceProvider BuildProvider( private static ServiceProvider BuildProvider(
TestTenantRepository tenants, TestTenantRepository tenants,
TenantContext tenantContext, ITenantContext tenantContext,
TestKanjStore storeA, TestKanjStore storeA,
TestKanjStore storeB, TestKanjStore storeB,
TestSettingsStore settings) TestSettingsStore settings)
@@ -301,7 +301,7 @@ public sealed class StorageTickSchedulerTests
// Возвращает: Провайдер с зарегистрированными сервисами теста. // Возвращает: Провайдер с зарегистрированными сервисами теста.
private static ServiceProvider BuildProvider( private static ServiceProvider BuildProvider(
ITenantRepository tenants, ITenantRepository tenants,
TenantContext tenantContext, ITenantContext tenantContext,
Dictionary<Guid, TestKanjStore> storesByTenant, Dictionary<Guid, TestKanjStore> storesByTenant,
Dictionary<Guid, ISettingsStore> settingsByTenant, Dictionary<Guid, ISettingsStore> settingsByTenant,
Dictionary<Guid, TestPipelineStore>? pipelineStoresByTenant = null) Dictionary<Guid, TestPipelineStore>? pipelineStoresByTenant = null)
@@ -11,9 +11,6 @@ namespace Deal.Telegram.Telegram;
#pragma warning disable CS0618 // Auth_SendCode/Auth_SignIn используются осознанно: ручной веб-вход 1:1 с прототипом #pragma warning disable CS0618 // Auth_SendCode/Auth_SignIn используются осознанно: ручной веб-вход 1:1 с прототипом
/// <summary>
/// Реальная реализация <see cref="ISessionClient"/> поверх WTelegramClient.
/// </summary>
public sealed class WTelegramSessionClient : ISessionClient public sealed class WTelegramSessionClient : ISessionClient
{ {
private readonly Client _client; private readonly Client _client;
@@ -67,25 +64,19 @@ public sealed class WTelegramSessionClient : ISessionClient
_updateManager = new UpdateManager(_client, OnSingleUpdateAsync); _updateManager = new UpdateManager(_client, OnSingleUpdateAsync);
} }
/// <inheritdoc /> bool ISessionClient.IsAuthorized => _client.UserId != 0;
public bool IsAuthorized => _client.UserId != 0;
/// <inheritdoc /> bool ISessionClient.IsConnected => _connected && !_client.Disconnected;
public bool IsConnected => _connected && !_client.Disconnected;
/// <inheritdoc /> int ISessionClient.ApiId => _apiId;
public int ApiId => _apiId;
/// <inheritdoc /> string ISessionClient.ApiHash => _apiHash;
public string ApiHash => _apiHash;
/// <inheritdoc /> byte[]? ISessionClient.SessionBytes => Volatile.Read(ref _latestSessionBytes);
public byte[]? SessionBytes => Volatile.Read(ref _latestSessionBytes);
/// <inheritdoc />
async Task ISessionClient.ConnectAsync(CancellationToken cancellationToken) async Task ISessionClient.ConnectAsync(CancellationToken cancellationToken)
{ {
if (IsConnected) if (((ISessionClient)this).IsConnected)
{ {
return; return;
} }
@@ -94,7 +85,6 @@ public sealed class WTelegramSessionClient : ISessionClient
_connected = true; _connected = true;
} }
/// <inheritdoc />
async Task ISessionClient.RequestCodeAsync(string phone, CancellationToken cancellationToken) async Task ISessionClient.RequestCodeAsync(string phone, CancellationToken cancellationToken)
{ {
_phone = phone; _phone = phone;
@@ -118,7 +108,6 @@ public sealed class WTelegramSessionClient : ISessionClient
} }
} }
/// <inheritdoc />
async Task<string?> ISessionClient.SubmitCodeAsync(string code, CancellationToken cancellationToken) async Task<string?> ISessionClient.SubmitCodeAsync(string code, CancellationToken cancellationToken)
{ {
if (_phoneAlreadyAuthorized) if (_phoneAlreadyAuthorized)
@@ -158,7 +147,6 @@ public sealed class WTelegramSessionClient : ISessionClient
return null; return null;
} }
/// <inheritdoc />
async Task ISessionClient.SubmitPasswordAsync(string password, CancellationToken cancellationToken) async Task ISessionClient.SubmitPasswordAsync(string password, CancellationToken cancellationToken)
{ {
try try
@@ -178,7 +166,6 @@ public sealed class WTelegramSessionClient : ISessionClient
} }
} }
/// <inheritdoc />
async Task ISessionClient.StartQrAsync(Action<string> onQrUrl, CancellationToken cancellationToken) async Task ISessionClient.StartQrAsync(Action<string> onQrUrl, CancellationToken cancellationToken)
{ {
try try
@@ -196,13 +183,11 @@ public sealed class WTelegramSessionClient : ISessionClient
} }
} }
/// <inheritdoc />
async Task ISessionClient.LogOutAsync(CancellationToken cancellationToken) async Task ISessionClient.LogOutAsync(CancellationToken cancellationToken)
{ {
await _client.Auth_LogOut().WaitAsync(cancellationToken).ConfigureAwait(false); await _client.Auth_LogOut().WaitAsync(cancellationToken).ConfigureAwait(false);
} }
/// <inheritdoc />
async Task<string> ISessionClient.GetAccountAsync(CancellationToken cancellationToken) async Task<string> ISessionClient.GetAccountAsync(CancellationToken cancellationToken)
{ {
UserBase[] users = await _client.Users_GetUsers(InputUser.Self).WaitAsync(cancellationToken).ConfigureAwait(false); UserBase[] users = await _client.Users_GetUsers(InputUser.Self).WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -221,7 +206,6 @@ public sealed class WTelegramSessionClient : ISessionClient
/// <inheritdoc /> /// <inheritdoc />
public event Func<TelegramMessage, Task>? MessageReceived; public event Func<TelegramMessage, Task>? MessageReceived;
/// <inheritdoc />
async Task<IReadOnlyList<TelegramDialog>> ISessionClient.GetDialogsAsync(int limit, CancellationToken cancellationToken) async Task<IReadOnlyList<TelegramDialog>> ISessionClient.GetDialogsAsync(int limit, CancellationToken cancellationToken)
{ {
Messages_DialogsBase result = await RunTlCallAsync(() => _client.Messages_GetDialogs(limit: limit), cancellationToken).ConfigureAwait(false); Messages_DialogsBase result = await RunTlCallAsync(() => _client.Messages_GetDialogs(limit: limit), cancellationToken).ConfigureAwait(false);
@@ -241,7 +225,6 @@ public sealed class WTelegramSessionClient : ISessionClient
return items; return items;
} }
/// <inheritdoc />
async Task<IReadOnlyList<TelegramMessage>> ISessionClient.GetMessagesAsync( async Task<IReadOnlyList<TelegramMessage>> ISessionClient.GetMessagesAsync(
string dialogId, string dialogId,
int limit, int limit,
@@ -265,7 +248,6 @@ public sealed class WTelegramSessionClient : ISessionClient
return items; return items;
} }
/// <inheritdoc />
async Task<TelegramMessage?> ISessionClient.GetMessageAsync( async Task<TelegramMessage?> ISessionClient.GetMessageAsync(
string dialogId, string dialogId,
long msgId, long msgId,
@@ -295,7 +277,6 @@ public sealed class WTelegramSessionClient : ISessionClient
return null; return null;
} }
/// <inheritdoc />
async Task ISessionClient.MarkReadAsync(string dialogId, CancellationToken cancellationToken) async Task ISessionClient.MarkReadAsync(string dialogId, CancellationToken cancellationToken)
{ {
InputPeer peer = await ResolvePeerAsync(dialogId, cancellationToken).ConfigureAwait(false); InputPeer peer = await ResolvePeerAsync(dialogId, cancellationToken).ConfigureAwait(false);
@@ -303,7 +284,6 @@ public sealed class WTelegramSessionClient : ISessionClient
} }
/// <inheritdoc />
async Task<IReadOnlyList<TelegramDialog>> ISessionClient.SearchAsync( async Task<IReadOnlyList<TelegramDialog>> ISessionClient.SearchAsync(
string query, string query,
int limit, int limit,
@@ -327,7 +307,6 @@ public sealed class WTelegramSessionClient : ISessionClient
return items; return items;
} }
/// <inheritdoc />
async Task<TelegramSourceInfo> ISessionClient.GetInfoAsync(string dialogId, CancellationToken cancellationToken) async Task<TelegramSourceInfo> ISessionClient.GetInfoAsync(string dialogId, CancellationToken cancellationToken)
{ {
TelegramSourceInfo unknown = DefaultSourceInfo(dialogId); TelegramSourceInfo unknown = DefaultSourceInfo(dialogId);
@@ -361,7 +340,6 @@ public sealed class WTelegramSessionClient : ISessionClient
return unknown; return unknown;
} }
/// <inheritdoc />
async Task<DiscoveryReadResult> ISessionClient.ReadForEvalAsync( async Task<DiscoveryReadResult> ISessionClient.ReadForEvalAsync(
string dialogId, string dialogId,
int limit, int limit,
@@ -410,7 +388,6 @@ public sealed class WTelegramSessionClient : ISessionClient
} }
} }
/// <inheritdoc />
async Task ISessionClient.JoinAsync(string username, CancellationToken cancellationToken) async Task ISessionClient.JoinAsync(string username, CancellationToken cancellationToken)
{ {
Contacts_ResolvedPeer resolved = await RunTlCallAsync(() => _client.Contacts_ResolveUsername(username), cancellationToken).ConfigureAwait(false); Contacts_ResolvedPeer resolved = await RunTlCallAsync(() => _client.Contacts_ResolveUsername(username), cancellationToken).ConfigureAwait(false);
@@ -425,7 +402,6 @@ public sealed class WTelegramSessionClient : ISessionClient
await RunTlCallAsync(() => _client.Channels_JoinChannel(new InputChannel(channel.id, channel.access_hash)), cancellationToken).ConfigureAwait(false); await RunTlCallAsync(() => _client.Channels_JoinChannel(new InputChannel(channel.id, channel.access_hash)), cancellationToken).ConfigureAwait(false);
} }
/// <inheritdoc />
async Task ISessionClient.LeaveAsync(string dialogId, CancellationToken cancellationToken) async Task ISessionClient.LeaveAsync(string dialogId, CancellationToken cancellationToken)
{ {
if (!TryParseSignedId(dialogId, out bool isChannel, out _, out _, out long rawId) || !isChannel) if (!TryParseSignedId(dialogId, out bool isChannel, out _, out _, out long rawId) || !isChannel)