199 lines
6.8 KiB
C#
199 lines
6.8 KiB
C#
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 IArticlesRepository _article;
|
|
private IIconsRepository _icons;
|
|
private ILogger _logger;
|
|
|
|
private IDiscordQueueRepository _queue;
|
|
private ISourcesRepository _sources;
|
|
private ISubscriptionRepository _subs;
|
|
private IDiscordWebHooksRepository _webhook;
|
|
|
|
public DiscordNotificationJob()
|
|
{
|
|
_queue = new DiscordQueueTable("");
|
|
_article = new ArticlesTable("");
|
|
_webhook = new DiscordWebhooksTable("");
|
|
_sources = new SourcesTable("");
|
|
_subs = new SubscriptionsTable("");
|
|
_icons = new IconsTable("");
|
|
_logger = JobLogger.GetLogger("", JobName);
|
|
}
|
|
|
|
public void InitAndExecute(DiscordNotificationJobOptions options)
|
|
{
|
|
_queue = new DiscordQueueTable(options.ConnectionString ?? "");
|
|
_article = new ArticlesTable(options.ConnectionString ?? "");
|
|
_webhook = new DiscordWebhooksTable(options.ConnectionString ?? "");
|
|
_sources = new SourcesTable(options.ConnectionString ?? "");
|
|
_subs = new SubscriptionsTable(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 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);
|
|
|
|
_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, SubscriptionEntity sub)
|
|
{
|
|
// 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 DiscordWebhookClient(discordDetails.Url);
|
|
try
|
|
{
|
|
client.SendMessage(GenerateDiscordMessage(sourceDetails, articleDetails, sourceIcon));
|
|
}
|
|
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)
|
|
{
|
|
var embed = new DiscordMessageEmbed
|
|
{
|
|
Title = article.Title,
|
|
Color = DiscordMessageEmbedColors.Red,
|
|
Description = MessageValidation.ConvertHtmlCodes(article.Description),
|
|
Author = new DiscordMessageEmbedAuthor
|
|
{
|
|
Name = article.AuthorName,
|
|
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
|
|
};
|
|
|
|
if (article.AuthorImage is not null && article.AuthorImage != "") embed.Author.IconUrl = article.AuthorImage;
|
|
|
|
return new DiscordMessage
|
|
{
|
|
Embeds = new[]
|
|
{
|
|
embed
|
|
}
|
|
};
|
|
}
|
|
} |