Implemented Telegram bot functionality, including settings management, subscriber tracking, and link generation for user interaction. Updated the backend to support new Telegram-related services and database entities. Enhanced the frontend to display Telegram options and allow users to open a chat with the bot. Localization strings were added for both English and Russian to support the new features. This integration aims to improve user engagement through Telegram notifications and interactions.
148 lines
5.4 KiB
C#
148 lines
5.4 KiB
C#
using NSubstitute;
|
|
using TeleWave.Application.Common.Interfaces;
|
|
using TeleWave.Application.Notifications;
|
|
using TeleWave.Application.Tests.Support;
|
|
using TeleWave.Domain.Broadcast;
|
|
using TeleWave.Domain.Library;
|
|
using TeleWave.Domain.Media;
|
|
using TeleWave.Domain.Notifications;
|
|
using Xunit;
|
|
|
|
namespace TeleWave.Application.Tests.Notifications;
|
|
|
|
/// <summary>
|
|
/// Рассылка по ленте. Проверяется то, из-за чего бот становится спамом: оповещение шлётся
|
|
/// на смену шоу, а не на каждую серию, и только тем, кто подписан на этот канал.
|
|
/// </summary>
|
|
public class TelegramNotifierTests
|
|
{
|
|
private static readonly DateTimeOffset Now = new(2026, 8, 1, 20, 0, 0, TimeSpan.Zero);
|
|
|
|
private static TelegramSettings Settings()
|
|
{
|
|
var settings = TelegramSettings.CreateDefault();
|
|
settings.Update(
|
|
new TelegramSettingsUpdate(
|
|
true,
|
|
TelegramTransport.Polling,
|
|
"123:ABC",
|
|
null,
|
|
TelegramProxyKind.None,
|
|
null,
|
|
null,
|
|
null,
|
|
null
|
|
)
|
|
);
|
|
return settings;
|
|
}
|
|
|
|
private static ScheduleEntry Program(
|
|
Guid channelId,
|
|
Guid showId,
|
|
DateTimeOffset start,
|
|
int minutes
|
|
) =>
|
|
ScheduleEntry.FromSlot(
|
|
channelId,
|
|
Guid.NewGuid(),
|
|
ScheduleEntryKind.Program,
|
|
start,
|
|
start.AddMinutes(minutes),
|
|
new ScheduleEntryOrigin(showId, 0, null, null, null)
|
|
);
|
|
|
|
[Fact]
|
|
public async Task Notifies_OnlySubscribersOfTheChannel_AndOnlyOnShowChange()
|
|
{
|
|
var fixture = new TestDb();
|
|
var channel = Channel.Create("Мультреалити", "mult", Now);
|
|
var other = Channel.Create("Второй", "second", Now);
|
|
var show = Show.Create("Футурама", ShowKind.Series);
|
|
var another = Show.Create("Симпсоны", ShowKind.Series);
|
|
|
|
var subscriber = TelegramSubscriber.Create(100, Guid.NewGuid(), "viewer", Now);
|
|
subscriber.Toggle(channel.Id, TelegramNotificationKind.ShowStart);
|
|
var stranger = TelegramSubscriber.Create(200, Guid.NewGuid(), "other", Now);
|
|
stranger.Toggle(other.Id, TelegramNotificationKind.ShowStart);
|
|
|
|
await using (var seed = fixture.New())
|
|
{
|
|
seed.Channels.AddRange(channel, other);
|
|
seed.Shows.AddRange(show, another);
|
|
seed.TelegramSubscribers.AddRange(subscriber, stranger);
|
|
// Предыдущая программа того же шоу: следующая серия сменой не считается.
|
|
seed.ScheduleEntries.Add(Program(channel.Id, show.Id, Now.AddMinutes(-30), 25));
|
|
seed.ScheduleEntries.Add(Program(channel.Id, show.Id, Now.AddMinutes(-2), 25));
|
|
await seed.SaveChangesAsync(CancellationToken.None);
|
|
}
|
|
|
|
var api = Substitute.For<ITelegramApi>();
|
|
var settings = Settings();
|
|
settings.MarkNotifiedUntil(Now.AddMinutes(-5));
|
|
|
|
await using var db = fixture.New();
|
|
var sent = await new TelegramNotifier(db, api).RunAsync(
|
|
settings,
|
|
Now,
|
|
CancellationToken.None
|
|
);
|
|
|
|
Assert.Equal(0, sent);
|
|
await api.DidNotReceive()
|
|
.SendMessageAsync(
|
|
Arg.Any<TelegramSettings>(),
|
|
Arg.Any<long>(),
|
|
Arg.Any<string>(),
|
|
Arg.Any<IReadOnlyList<IReadOnlyList<TelegramButton>>?>(),
|
|
Arg.Any<CancellationToken>()
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Notifies_WhenShowChanges()
|
|
{
|
|
var fixture = new TestDb();
|
|
var channel = Channel.Create("Мультреалити", "mult", Now);
|
|
var previous = Show.Create("Симпсоны", ShowKind.Series);
|
|
var started = Show.Create("Футурама", ShowKind.Series);
|
|
|
|
var subscriber = TelegramSubscriber.Create(100, Guid.NewGuid(), "viewer", Now);
|
|
subscriber.Toggle(channel.Id, TelegramNotificationKind.ShowStart);
|
|
|
|
await using (var seed = fixture.New())
|
|
{
|
|
seed.Channels.Add(channel);
|
|
seed.Shows.AddRange(previous, started);
|
|
seed.TelegramSubscribers.Add(subscriber);
|
|
seed.ScheduleEntries.Add(Program(channel.Id, previous.Id, Now.AddMinutes(-30), 25));
|
|
seed.ScheduleEntries.Add(Program(channel.Id, started.Id, Now.AddMinutes(-1), 25));
|
|
await seed.SaveChangesAsync(CancellationToken.None);
|
|
}
|
|
|
|
var api = Substitute.For<ITelegramApi>();
|
|
var settings = Settings();
|
|
settings.MarkNotifiedUntil(Now.AddMinutes(-5));
|
|
|
|
await using var db = fixture.New();
|
|
var sent = await new TelegramNotifier(db, api).RunAsync(
|
|
settings,
|
|
Now,
|
|
CancellationToken.None
|
|
);
|
|
|
|
Assert.Equal(1, sent);
|
|
await api.Received(1)
|
|
.SendMessageAsync(
|
|
Arg.Any<TelegramSettings>(),
|
|
100,
|
|
Arg.Is<string>(s => s.Contains("Футурама")),
|
|
null,
|
|
Arg.Any<CancellationToken>()
|
|
);
|
|
|
|
// Граница разобранного двигается всегда — иначе следующий тик оповестил бы о том же.
|
|
Assert.Equal(Now, settings.NotifiedUntil);
|
|
}
|
|
}
|