Skip to content

Commit

Permalink
Merge pull request #38 from granstel/youtube
Browse files Browse the repository at this point in the history
Youtube
  • Loading branch information
granstel authored Feb 26, 2024
2 parents 30461b9 + f2e5a07 commit 8f3192e
Show file tree
Hide file tree
Showing 29 changed files with 296 additions and 121 deletions.
3 changes: 1 addition & 2 deletions Dodo1000Bot.Api/DependencyConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ internal static void Configure(IServiceCollection services, IConfiguration appCo

services.AddSingleton(configuration);
services.AddSingleton(configuration.HttpLog);
services.AddSingleton(configuration.Redis);
services.AddSingleton(configuration.Dialogflow);
services.AddSingleton(configuration.PushNotifications);
services.AddSingleton(configuration.UnitsJob);
services.AddSingleton(configuration.StatisticsJob);
services.AddSingleton(configuration.Management);
services.AddSingleton(configuration.YouTube);

services.AddInternalServices();
services.AddJobs(configuration);
Expand Down
54 changes: 3 additions & 51 deletions Dodo1000Bot.Api/DependencyModules/ExternalServicesRegistration.cs
Original file line number Diff line number Diff line change
@@ -1,71 +1,23 @@
using System;
using Dodo1000Bot.Api.Extensions;
using Dodo1000Bot.Api.Extensions;
using Dodo1000Bot.Services;
using Dodo1000Bot.Services.Clients;
using Google.Apis.Auth.OAuth2;
using Google.Cloud.Dialogflow.V2;
using Dodo1000Bot.Services.Configuration;
using GranSteL.Helpers.Redis;
using Grpc.Auth;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;

namespace Dodo1000Bot.Api.DependencyModules
{
internal static class ExternalServicesRegistration
{
internal static void AddExternalServices(this IServiceCollection services, AppConfiguration configuration)
{
services.AddSingleton<SessionsClient>(RegisterDialogflowSessionsClient);

services.AddSingleton<IDatabase>(RegisterRedisClient);

services.AddSingleton<IRedisCacheService>(RegisterCacheService);

services.AddHttpClient<IGlobalApiClient, GlobalApiClient>(configuration.GlobalApiEndpoint,
nameof(configuration.GlobalApiEndpoint));
services.AddHttpClient<IRealtimeBoardApiClient, RealtimeBoardApiClient>(configuration.RealtimeBoardApiClientEndpoint,
nameof(configuration.RealtimeBoardApiClientEndpoint));
services.AddHttpClient<IRestcountriesApiClient, RestcountriesApiClient>(configuration.RestcountriesApiClientEndpoint,
nameof(configuration.RestcountriesApiClientEndpoint));
}

private static SessionsClient RegisterDialogflowSessionsClient(IServiceProvider provider)
{
var configuration = provider.GetService<DialogflowConfiguration>();

var credential = GoogleCredential.FromFile(configuration.JsonPath).CreateScoped(SessionsClient.DefaultScopes);

var clientBuilder = new SessionsClientBuilder
{
ChannelCredentials = credential.ToChannelCredentials()
};

var client = clientBuilder.Build();

return client;
}

private static IDatabase RegisterRedisClient(IServiceProvider provider)
{
var configuration = provider.GetService<RedisConfiguration>();

var redisClient = ConnectionMultiplexer.Connect(configuration.ConnectionString);

var dataBase = redisClient.GetDatabase();

return dataBase;
}

private static RedisCacheService RegisterCacheService(IServiceProvider provider)
{
var configuration = provider.GetService<RedisConfiguration>();

var db = provider.GetService<IDatabase>();

var service = new RedisCacheService(db, configuration.KeyPrefix);

return service;
services.AddHttpClient<IYouTubeClient, YouTubeClient>(configuration.YouTube.Endpoint,
nameof(configuration.YouTube));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ internal static void AddInternalServices(this IServiceCollection services)
services.AddTransient<INotificationsService, NotificationsService>();
services.AddTransient<UnitsService>();
services.AddTransient<StatisticsService>();
services.AddTransient<YoutubeService>();
services.AddTransient<ICountriesService, CountriesService>();
services.AddTransient<IUsersService, UsersService>();

Expand Down
1 change: 1 addition & 0 deletions Dodo1000Bot.Api/DependencyModules/JobsRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ internal static void AddJobs(this IServiceCollection services, AppConfiguration
services.AddHostedService<PushNotificationsJob>();
services.AddHostedService<UnitsCheckAndNotifyJob>();
services.AddHostedService<StatisticsCheckAndNotifyJob>();
services.AddHostedService<YoutubeCheckAndNotifyJob>();
}
}
2 changes: 1 addition & 1 deletion Dodo1000Bot.Api/Dodo1000Bot.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>1.25.0</Version>
<Version>1.26.0</Version>
<UserSecretsId>f449de95-800a-40ef-8716-6e80b7f0977d</UserSecretsId>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion Dodo1000Bot.Api/Jobs/CheckAndNotifyJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class CheckAndNotifyJob<TService, TConfiguration> : RepeatableJob
{
private readonly IServiceProvider _provider;

public CheckAndNotifyJob(ILogger<CheckAndNotifyJob<TService, TConfiguration>> log,
protected CheckAndNotifyJob(ILogger<CheckAndNotifyJob<TService, TConfiguration>> log,
IServiceProvider provider,
TConfiguration configuration) : base(log, configuration.RefreshEveryTime)
{
Expand Down
21 changes: 10 additions & 11 deletions Dodo1000Bot.Api/Jobs/FirstRunJob.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
using System;
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
using Dodo1000Bot.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Dodo1000Bot.Api.Jobs;

public class FirstRunJob: IHostedService
{
private readonly IServiceProvider _provider;
private readonly UnitsService _unitsService;
private readonly YoutubeService _youtubeService;

public FirstRunJob(IServiceProvider provider, UnitsService unitsService)
public FirstRunJob(UnitsService unitsService, YoutubeService youtubeService)
{
_provider = provider;
_unitsService = unitsService;
_youtubeService = youtubeService;
}

public async Task StartAsync(CancellationToken cancellationToken)
{
await using var scope = _provider.CreateAsyncScope();
var unitsService = scope.ServiceProvider.GetRequiredService<UnitsService>();

await unitsService.CreateUnitsCountSnapshotIfNotExists(cancellationToken);
await unitsService.CreateUnitsSnapshotIfNotExists(cancellationToken);
await _unitsService.CreateUnitsCountSnapshotIfNotExists(cancellationToken);
await _unitsService.CreateUnitsSnapshotIfNotExists(cancellationToken);

await _youtubeService.CreateVideosSnapshotIfNotExists(cancellationToken);
}

public Task StopAsync(CancellationToken cancellationToken)
Expand Down
2 changes: 1 addition & 1 deletion Dodo1000Bot.Api/Jobs/RepeatableJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public abstract class RepeatableJob : BackgroundService
private readonly TimeSpan _repeatEveryTime;
private readonly string _jobName;

protected ILogger Log { get; }
private ILogger Log { get; }

protected RepeatableJob(ILogger log, TimeSpan repeatEveryTime)
{
Expand Down
16 changes: 16 additions & 0 deletions Dodo1000Bot.Api/Jobs/YoutubeCheckAndNotifyJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using Dodo1000Bot.Services;
using Dodo1000Bot.Services.Configuration;
using Microsoft.Extensions.Logging;

namespace Dodo1000Bot.Api.Jobs;

public class YoutubeCheckAndNotifyJob: CheckAndNotifyJob<YoutubeService, YoutubeConfiguration>
{
public YoutubeCheckAndNotifyJob(
ILogger<YoutubeCheckAndNotifyJob> log,
IServiceProvider provider,
YoutubeConfiguration configuration) : base(log, provider, configuration)
{
}
}
18 changes: 8 additions & 10 deletions Dodo1000Bot.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,6 @@
"ExcludeBodiesWithWords": [ "ping", "pong" ],
"IncludeEndpoints": ["dialogflow", "events"]
},
"Dialogflow": {
"ProjectId": "",
"LanguageCode": "",
"JsonPath": "",
"LogQuery": false
},
"Redis": {
"ConnectionString": "",
"KeyPrefix": ""
},
"PushNotifications": {
"EveryTime": "0:01:00"
},
Expand All @@ -32,6 +22,14 @@
"StatisticsJob": {
"RefreshEveryTime": "0:10:00"
},
"Youtube": {
"RefreshEveryTime": "0:10:00",
"ApiKey": "",
"Endpoint": "https://youtube.googleapis.com/youtube/v3/search",
"Channels": [
"UCkPTVsNw2R5lwQ_VxF6Wuow"
]
},
"GlobalApiEndpoint": "https://globalapi.dodois.io/api/v2/",
"RealtimeBoardApiClientEndpoint": "",
"RestcountriesApiClientEndpoint": "https://restcountries.com/v3.1/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,41 @@ public async Task NotifyAbout_AnyNotificationsAndUsers_SentAllNotificationsToAll
Assert.AreEqual(notification.Id, pushedNotification.NotificationId);
Assert.AreEqual(user.Id, pushedNotification.UserId);
}

[Test]
public async Task NotifyAbout_AdminNotifications_SentOnlyToAdminUsers()
{
var payload = _fixture.Build<NotificationPayload>()
.With(n => n.Text)
.Create();
var notification = _fixture.Build<Notification>()
.With(n => n.Payload, payload)
.With(n => n.Type, NotificationType.Admin)
.Create();
var ordinaryUser = _fixture.Build<Domain.User>()
.With(u => u.Id)
.With(u => u.MessengerUserId, _fixture.Create<long>().ToString)
.Create();
var adminUser = _fixture.Build<Domain.User>()
.With(u => u.Id)
.With(u => u.MessengerUserId, _fixture.Create<long>().ToString)
.With(u => u.IsAdmin, true)
.Create();

var ct = CancellationToken.None;

_usersRepositoryMock.Setup(r => r.GetUsers(Source.Telegram, ct)).ReturnsAsync(new []{ adminUser, ordinaryUser });
_clientMock.Setup(c => c.SendTextMessageAsync(adminUser.MessengerUserId, notification.Payload.Text,
It.IsAny<ParseMode>(), It.IsAny<IEnumerable<MessageEntity>>(),
It.IsAny<bool>(), It.IsAny<bool>(),
It.IsAny<int>(), It.IsAny<bool>(),
It.IsAny<IReplyMarkup>(), ct)).ReturnsAsync(() => null);

var pushedNotifications = (await _target.NotifyAbout(new []{notification}, ct)).ToArray();

Assert.IsNotEmpty(pushedNotifications);

Assert.True(pushedNotifications.All(n => n.UserId == adminUser.Id));
}
}
}
10 changes: 7 additions & 3 deletions Dodo1000Bot.Messengers.Telegram/TelegramNotifyService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Dodo1000Bot.Services;
using Microsoft.Extensions.Logging;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Types.Enums;

Expand Down Expand Up @@ -38,7 +37,7 @@ public async Task<IEnumerable<PushedNotification>> NotifyAbout(IList<Notificatio
return pushedNotifications;
}

IList<Models.Domain.User> users = await _usersRepository.GetUsers(Source.Telegram, cancellationToken);
IList<User> users = await _usersRepository.GetUsers(Source.Telegram, cancellationToken);

if (users?.Any() != true)
{
Expand All @@ -55,12 +54,17 @@ public async Task<IEnumerable<PushedNotification>> NotifyAbout(IList<Notificatio
return pushedNotifications;
}

private async Task<IList<PushedNotification>> PushNotificationsToUser(IList<Notification> notifications, Models.Domain.User user, CancellationToken cancellationToken)
private async Task<IList<PushedNotification>> PushNotificationsToUser(IList<Notification> notifications, User user, CancellationToken cancellationToken)
{
var pushedNotifications = new List<PushedNotification>();

foreach (var notification in notifications)
{
if (notification.Type is NotificationType.Admin && user is not { IsAdmin : true })
{
continue;
}

var messengerUserId = user.MessengerUserId;
try
{
Expand Down
4 changes: 2 additions & 2 deletions Dodo1000Bot.Models/Domain/Notification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ public Notification(NotificationType type)
}
public int Id { get; set; }

public NotificationType Type { get; }
public NotificationType Type { get; init; }

public NotificationPayload Payload { get; set; }

public string Distinction => $"{Payload}";
public string Distinction => Payload.ToString();
}
}
2 changes: 2 additions & 0 deletions Dodo1000Bot.Models/Domain/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public class User
public string MessengerUserId { get; set; }

public Source MessengerType { get; set; }

public bool IsAdmin { get; set; }
}
}
25 changes: 25 additions & 0 deletions Dodo1000Bot.Models/Youtube/SearchResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Dodo1000Bot.Models.Youtube;

public class SearchResponse
{
public Video[] Items { get; set; }
}

public class Video
{
public Id Id { get; set; }
public Snippet Snippet { get; set; }
}

public class Id
{
public string VideoId { get; set; }
}

public class Snippet
{
public string PublishedAt { get; set; }
public string LiveBroadcastContent { get; set; }
public string PublishTime { get; set; }
public string Title { get; set; }
}
4 changes: 2 additions & 2 deletions Dodo1000Bot.Services/CheckAndNotifyService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

namespace Dodo1000Bot.Services
{
public abstract class CheckAndNotifyService
public interface CheckAndNotifyService
{
public abstract Task CheckAndNotify(CancellationToken cancellationToken);
public Task CheckAndNotify(CancellationToken cancellationToken);
}
}
6 changes: 3 additions & 3 deletions Dodo1000Bot.Services/Clients/GlobalApiClient.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Dodo1000Bot.Models;
using Dodo1000Bot.Models.GlobalApi;
using Dodo1000Bot.Services.Extensions;

namespace Dodo1000Bot.Services.Clients
{
Expand All @@ -23,7 +23,7 @@ public async Task<BrandListTotalUnitCountListModel> UnitsCount(CancellationToken
{
const string url = "units/count";

var unitsCount = await _httpClient.GetAsync<BrandListTotalUnitCountListModel>(url, _serializerOptions, cancellationToken);
var unitsCount = await _httpClient.GetFromJsonAsync<BrandListTotalUnitCountListModel>(url, _serializerOptions, cancellationToken);

return unitsCount;
}
Expand All @@ -32,7 +32,7 @@ public async Task<BrandData<UnitListModel>> UnitsOfBrandAtCountry(Brands brand,
{
var url = $"{brand}/units/all/{countryId}";

var unitsCount = await _httpClient.GetAsync<BrandData<UnitListModel>>(url, _serializerOptions, cancellationToken);
var unitsCount = await _httpClient.GetFromJsonAsync<BrandData<UnitListModel>>(url, _serializerOptions, cancellationToken);

return unitsCount;
}
Expand Down
Loading

0 comments on commit 8f3192e

Please sign in to comment.