Newsbot.Collector/Newsbot.Collector.Services/Jobs/DiscordNotificationJob.cs

206 lines
7.3 KiB
C#

using Newsbot.Collector.Database;
using Newsbot.Collector.Database.Repositories;
using Newsbot.Collector.Domain.Entities;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Domain.Models;
using Newsbot.Collector.Services.Notifications.Discord;
using Serilog;
namespace Newsbot.Collector.Services.Jobs;
public class MessageTypeNotRequestedException : Exception
{
public MessageTypeNotRequestedException()
{
}
public MessageTypeNotRequestedException(string message) : base(message)
{
}
public MessageTypeNotRequestedException(string message, Exception inner) : base(message, inner)
{
}
}
public class DiscordNotificationJobOptions
{
public string? ConnectionString { get; init; }
public string? OpenTelemetry { get; init; }
public bool IsEnabled { get; init; }
}
public class DiscordNotificationJob
{
private const string JobName = "DiscordNotifications";
private ILogger _logger;
//private DatabaseContext _databaseContext;
private IArticlesRepository _article;
private IAuthorTable _author;
private IIconsRepository _icons;
private IDiscordQueueRepository _queue;
private ISourcesRepository _sources;
private IDiscordNotificationRepository _subs;
private IDiscordWebHooksRepository _webhook;
public DiscordNotificationJob()
{
_queue = new DiscordQueueTable("");
_article = new ArticlesTable("");
_author = new AuthorsTable("");
_webhook = new DiscordWebhooksTable("");
_sources = new SourcesTable("");
_subs = new DiscordNotificationTable("");
_icons = new IconsTable("");
_logger = JobLogger.GetLogger("", JobName);
}
public void InitAndExecute(DiscordNotificationJobOptions options)
{
//_databaseContext = new DatabaseContext(options.ConnectionString ?? "");
_queue = new DiscordQueueTable(options.ConnectionString ?? "");
_article = new ArticlesTable(options.ConnectionString ?? "");
_webhook = new DiscordWebhooksTable(options.ConnectionString ?? "");
_sources = new SourcesTable(options.ConnectionString ?? "");
_subs = new DiscordNotificationTable(options.ConnectionString ?? "");
_icons = new IconsTable(options.ConnectionString ?? "");
_logger = JobLogger.GetLogger(options.OpenTelemetry ?? "", JobName);
if (!options.IsEnabled)
{
_logger.Warning($"{JobName} - Going to exit because feature flag is off.");
return;
}
_logger.Information($"{JobName} - Starting up the job.");
Execute();
}
private void Execute()
{
//collect all the new requests
var requests = _queue.List(100);
_logger.Debug($"{JobName} - Collected {requests.Count} items to send");
foreach (var request in requests) ProcessQueueItem(request);
_logger.Information($"{JobName} - Loop has been completed.");
}
public void ProcessQueueItem(DiscordQueueEntity request)
{
_logger.Debug($"{JobName} - Processing {request.Id}");
// Get all details on the article in the queue
var articleDetails = _article.GetById(request.ArticleId);
// Get the details of the source
var sourceDetails = _sources.GetById(articleDetails.SourceId);
if (sourceDetails.Id == Guid.Empty)
{
_logger.Error(
$"{JobName} - Article ({articleDetails.Id}) was linked to a empty Source ID. Removing from the queue.");
_queue.Delete(request.Id);
return;
}
var author = _author.GetBySourceIdAndNameAsync(sourceDetails.Id, sourceDetails.Name);
author.Wait();
var sourceIcon = new IconEntity();
try
{
sourceIcon = _icons.GetBySourceId(sourceDetails.Id);
}
catch
{
_logger.Warning("{JobName} - Source ID \'{SourceDetailsId}\' is missing an icon", JobName,
sourceDetails.Id);
}
// Find all the subscriptions for that source
var allSubscriptions = _subs.ListBySourceId(sourceDetails.Id);
foreach (var sub in allSubscriptions)
SendSubscriptionNotification(request.Id, articleDetails, sourceDetails, sourceIcon, sub, author.Result ?? new AuthorEntity());
_logger.Debug("{JobName} - Removing {RequestId} from the queue", JobName, request.Id);
_queue.Delete(request.Id);
}
public void SendSubscriptionNotification(Guid requestId, ArticlesEntity articleDetails, SourceEntity sourceDetails, IconEntity sourceIcon, DiscordNotificationEntity sub, AuthorEntity authorEntity)
{
// Check if the subscription code flags
// If the article is a code commit and the subscription does not want them, skip.
if (articleDetails.CodeIsCommit && !sub.CodeAllowCommits) throw new MessageTypeNotRequestedException("Message was a code commit and was not requested by the subscription.");
// same for releases
if (articleDetails.CodeIsRelease && !sub.CodeAllowReleases) throw new MessageTypeNotRequestedException("Message was a code release and was not requested by the subscription");
// find the discord webhooks we need to post to
var discordDetails = _webhook.GetById(sub.DiscordWebHookId);
if (discordDetails.Enabled == false) return;
var client = new DiscordClient(discordDetails.Url);
try
{
client.SendMessage(GenerateDiscordMessage(sourceDetails, articleDetails, sourceIcon, authorEntity));
}
catch (Exception e)
{
_logger.Error("Failed to post message to Discord. {ErrorMessage}", e.Message);
_logger.Debug("Queue Record: {RequestId}", requestId);
_logger.Debug("Article: {ArticleDetailsId}", articleDetails.Id);
_logger.Debug("Source: {SourceDetailsId}", sourceDetails.Id);
_logger.Debug("Subscription: {SubId}", sub.Id);
}
Thread.Sleep(3000);
}
public DiscordMessage GenerateDiscordMessage(SourceEntity source, ArticlesEntity article, IconEntity icon, AuthorEntity author)
{
var embed = new DiscordMessageEmbed
{
Title = article.Title,
Color = DiscordMessageEmbedColors.Red,
Description = MessageValidation.ConvertHtmlCodes(article.Description),
Author = new DiscordMessageEmbedAuthor
{
Name = author.Name,
IconUrl = icon.FileName
},
Footer = new DiscordMessageEmbedFooter
{
Text = "Brought to you by Newsbot"
},
Fields = new DiscordMessageEmbedField[]
{
new()
{
Name = "Link",
Value = article.Url,
Inline = false
}
}
};
if (article.Url is not null && article.Url != "") embed.Url = article.Url;
if (article.Thumbnail != "")
embed.Image = new DiscordMessageEmbedImage
{
Url = article.Thumbnail
};
embed.Author.IconUrl = author.Image ?? "";
return new DiscordMessage
{
Embeds = new[]
{
embed
}
};
}
}