Skip to content

Commit

Permalink
Merge pull request #37 from H5YR/develop
Browse files Browse the repository at this point in the history
[merge] Dev to main
  • Loading branch information
OwainWilliams authored Oct 9, 2023
2 parents d6dfdd8 + 3687af8 commit c908b73
Show file tree
Hide file tree
Showing 26 changed files with 694 additions and 20 deletions.
2 changes: 2 additions & 0 deletions Controllers/LoadMoreTweetsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public LoadMoreTweetsController(IOptions<TwitterSettings> twitterSettings, ILogg
public IActionResult GetTweets()
{
var tweetsToSkip = Convert.ToInt32(HttpContext.Session.GetInt32("NumberOfTweetsDisplayed"));
tweetsToSkip = tweetsToSkip == 0 ? 12 : tweetsToSkip;

List<TweetModel> tweets = new();

if (_apiSettings.Value.Offline == null || _apiSettings.Value.Offline.ToLowerInvariant() != "true")
Expand Down
57 changes: 57 additions & 0 deletions Core/Composers/TweetCounterStoreComposer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using h5yr.Data.Migrations;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Migrations;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Migrations;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;

namespace h5yr.Core.Composers
{
public class TweetCounterStoreComposer : ComponentComposer<TweetCounterStoreComponent>, IComposer
{
}

public class TweetCounterStoreComponent : IComponent
{
private readonly ICoreScopeProvider _coreScopeProvider;
private readonly IMigrationPlanExecutor _migrationPlanExecutor;
private readonly IKeyValueService _keyValueService;
private readonly ILogger<TweetCounterStoreComponent> _logger;
private readonly IRuntimeState _runtimeState;

public TweetCounterStoreComponent(ICoreScopeProvider coreScopeProvider,
IMigrationPlanExecutor migrationPlanExecutor, IKeyValueService keyValueService,
ILogger<TweetCounterStoreComponent> logger, IRuntimeState runtimeState)
{
_coreScopeProvider = coreScopeProvider ?? throw new ArgumentNullException(nameof(coreScopeProvider));
_migrationPlanExecutor = migrationPlanExecutor ?? throw new ArgumentNullException(nameof(migrationPlanExecutor));
_keyValueService = keyValueService ?? throw new ArgumentNullException(nameof(keyValueService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState));
}

public void Initialize()
{
if (_runtimeState.Level < RuntimeLevel.Run)
{
return;
}

var migrationPlan = new MigrationPlan("TweetCounterCreateTableMigrationPlan1.2");

migrationPlan = migrationPlan.From(string.Empty)
.To<TweetCounterCreateTableMigration>(nameof(TweetCounterCreateTableMigration));

var upgrader = new Upgrader(migrationPlan);

upgrader.Execute(_migrationPlanExecutor, _coreScopeProvider, _keyValueService);
}

public void Terminate()
{
}
}
}
30 changes: 30 additions & 0 deletions Core/Composers/WebComposer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using h5yr.Data.Interfaces;
using h5yr.Data.Stores;
using Microsoft.Data.SqlClient;
using NPoco;
using Umbraco.Cms.Core.Composing;

namespace h5yr.Core.Composers
{
public class WebComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.Services.AddSingleton<ITweetCounterStore, TweetCounterStore>();

const string umbracoDbDSN = Umbraco.Cms.Core.Constants.System.UmbracoConnectionName;

var dbBuilder = WebApplication.CreateBuilder();
var connectionString = dbBuilder.Configuration.GetConnectionString(umbracoDbDSN);

builder.Services.AddSingleton<IDatabase>(x =>
{
return new NPoco.Database(
connectionString,
DatabaseType.SqlServer2012,
SqlClientFactory.Instance);
});

}
}
}
13 changes: 13 additions & 0 deletions Core/Services/IMastodonService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Skybrud.Social.Mastodon.Models.Statuses;

namespace h5yr.Core.Services
{

public interface IMastodonService
{

IReadOnlyList<MastodonStatus> GetStatuses(int limit, string? maxId = null);

}

}
58 changes: 58 additions & 0 deletions Core/Services/MastodonService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using Skybrud.Social.Mastodon;
using Skybrud.Social.Mastodon.Models.Statuses;
using Skybrud.Social.Mastodon.Options.Timeline;
using Skybrud.Social.Mastodon.Responses.Statuses;

namespace h5yr.Core.Services {

public class MastodonService : IMastodonService
{

private readonly ILogger<MastodonService> _logger;

public MastodonService(ILogger<MastodonService> logger)
{
_logger = logger;
}

public IReadOnlyList<MastodonStatus> GetStatuses(int limit, string? maxId = null)
{

// Initialize a new HTTP service (basically the API wrapper)
MastodonHttpService mastodon = MastodonHttpService
.CreateFromDomain("umbracocommunity.social");

// Initialize the options for the request to the API
MastodonGetHashtagTimelineOptions options = new()
{
Hashtag = "h5yr",
Limit = limit
};

try
{

// Make the request to the API
MastodonStatusListResponse response = mastodon
.Timelines
.GetHashtagTimeline(options);

// Return the statuses
return response.Body;

}
catch (Exception ex)
{

_logger.LogError(ex, "Failed fetching statuses from the Mastodon API.");

}


return Array.Empty<MastodonStatus>();

}

}

}
79 changes: 79 additions & 0 deletions Core/Services/TwitterAPICountHostedService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using System.Threading.Tasks;
using h5yr.Data.Entities;
using h5yr.Data.Interfaces;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Logging;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Infrastructure.HostedServices;

namespace H5YR.Core.Services
{
public class TwitterAPICountHostedService : RecurringHostedServiceBase
{
private readonly IRuntimeState _runtimeState;
private readonly IContentService _contentService;
private readonly IServerRoleAccessor _serverRoleAccessor;
private readonly IProfilingLogger _profilingLogger;
private readonly ILogger<TwitterAPICountHostedService> _logger;
private readonly ICoreScopeProvider _scopeProvider;
private readonly ITwitterHelper _twitterHelper;
private readonly ITweetCounterStore _tweetCounterStore;

private static TimeSpan HowOftenWeRepeat => TimeSpan.FromMinutes(30);
private static TimeSpan DelayBeforeWeStart => TimeSpan.FromMinutes(1);

public TwitterAPICountHostedService(
IRuntimeState runtimeState,
IContentService contentService,
IServerRoleAccessor serverRoleAccessor,
IProfilingLogger profilingLogger,
ILogger<TwitterAPICountHostedService> logger,
ICoreScopeProvider scopeProvider,
ITwitterHelper twitterHelper,
ITweetCounterStore tweetCounterStore)
: base(logger, HowOftenWeRepeat, DelayBeforeWeStart)
{
_runtimeState = runtimeState;
_contentService = contentService;
_serverRoleAccessor = serverRoleAccessor;
_profilingLogger = profilingLogger;
_logger = logger;
_scopeProvider = scopeProvider;
_twitterHelper = twitterHelper;
_tweetCounterStore = tweetCounterStore;
}

public override Task PerformExecuteAsync(object state)
{
// Don't do anything if the site is not running.
if (_runtimeState.Level != RuntimeLevel.Run)
{
return Task.CompletedTask;
}

// Wrap the three content service calls in a scope to do it all in one transaction.
using ICoreScope scope = _scopeProvider.CreateCoreScope();

var tweetCount = _twitterHelper.GetTweetCount();
var date = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
_logger.LogInformation("Retrieving tweet count for " + DateTime.Now.ToString("dd/MM/yyyy") + ". " + tweetCount + " tweets found");

var tweetCountModel = new TweetCounter()
{
Date = date,
Quantity = tweetCount,
};

_tweetCounterStore.Update(tweetCountModel);

// Remember to complete the scope when done.
scope.Complete();

return Task.CompletedTask;
}
}
}
21 changes: 21 additions & 0 deletions Core/Services/TwitterHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
using Microsoft.Extensions.Options;
using Tweetinvi;
using Tweetinvi.Models;
using System.Linq;

namespace H5YR.Core.Services
{
public interface ITwitterHelper
{
List<TweetModel> GetAllTweets(int tweetsToSkip, int tweetsToReturn);
int GetTweetCount();
}
public class TwitterHelper : ITwitterHelper
{
Expand Down Expand Up @@ -56,5 +58,24 @@ public List<TweetModel> GetAllTweets(int tweetsToSkip, int tweetsToReturn)
return Tweets;
}

public int GetTweetCount()
{
if (apiSettings.Value.Offline == null || apiSettings.Value?.Offline?.ToLowerInvariant() == "true") { return 0; }

var creds = new TwitterCredentials(settings.Value.ConsumerKey, settings.Value.ConsumerSecret, settings.Value.AccessToken, settings.Value.AccessTokenSecret);
var userClient = new TwitterClient(creds);

var searchResults = userClient.Search.SearchTweetsAsync("#h5yr");

if (searchResults.Result?.Any() ?? false)
{
var dateTo = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
var recentTweets = searchResults.Result.Where(x => x.CreatedAt.Ticks <= dateTo.AddDays(1).Ticks && x.CreatedAt.Ticks >= dateTo.Ticks);

return recentTweets.Count();
}

return 0;
}
}
}
17 changes: 17 additions & 0 deletions Data/Constants/TweetCounterSchemaConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using h5yr.Data.Entities;

namespace h5yr.Data.Constants
{
public static class TweetCounterSchemaConstants
{
public const string TableName = "TweetCounter";

public const string PrimaryKey = Id;

public const string Id = nameof(TweetCounter.Id);

public const string Date = nameof(TweetCounter.Date);

public const string Quantity = nameof(TweetCounter.Quantity);
}
}
23 changes: 23 additions & 0 deletions Data/Entities/TweetCounter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using h5yr.Data.Constants;
using NPoco;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;

namespace h5yr.Data.Entities
{
[TableName(TweetCounterSchemaConstants.TableName)]
[PrimaryKey(TweetCounterSchemaConstants.PrimaryKey, AutoIncrement = true)]
[ExplicitColumns]
public class TweetCounter
{
[PrimaryKeyColumn(AutoIncrement = true)]
[Column(TweetCounterSchemaConstants.Id)]
public int Id { get; set; }

[Column(TweetCounterSchemaConstants.Date)]
public DateTime Date { get; set; }

[Column(TweetCounterSchemaConstants.Quantity)]
public int Quantity { get; set; }
}
}
14 changes: 14 additions & 0 deletions Data/Interfaces/ITweetCounterStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using h5yr.Data.Entities;

namespace h5yr.Data.Interfaces
{
public interface ITweetCounterStore
{
IEnumerable<TweetCounter> GetAll();
IEnumerable<TweetCounter> GetTweetCount(DateTime date);
int GetTweetsCountTotalByDate(DateTime dateFrom, DateTime dateTo);
void Save(TweetCounter poco);
void Update(TweetCounter poco);
void Delete(TweetCounter poco);
}
}
40 changes: 40 additions & 0 deletions Data/Migrations/TweetCounterCreateTableMigration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using h5yr.Data.Constants;
using h5yr.Data.Entities;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Infrastructure.Migrations;

namespace h5yr.Data.Migrations
{
public class TweetCounterCreateTableMigration : MigrationBase
{
private ILogger<TweetCounterCreateTableMigration> _logger;
private ICoreScopeProvider _scopeProvider;

public TweetCounterCreateTableMigration(IMigrationContext context, ILogger<TweetCounterCreateTableMigration> logger, ICoreScopeProvider scopeProvider) : base(context)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_scopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider));
}

protected override void Migrate()
{
_logger.LogDebug("Running migration.");

using (var scope = _scopeProvider.CreateCoreScope())
{
if (!TableExists(TweetCounterSchemaConstants.TableName))
{
Create.Table<TweetCounter>().Do();
scope.Complete();
return;
}
scope.Complete();
}

_logger.LogDebug(
$"The database table {TweetCounterSchemaConstants.TableName} already exists, skipping.");

}
}
}
Loading

0 comments on commit c908b73

Please sign in to comment.