New custom discord webhook client (#7)

This commit is contained in:
James Tombleson 2023-03-05 20:12:59 -08:00 committed by GitHub
parent 521940ca4f
commit bb832ed441
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 337 additions and 3 deletions

View File

@ -17,5 +17,7 @@
"password": "postgres"
}
],
"editor.formatOnType": true
"editor.formatOnType": true,
"csharp.inlayHints.parameters.forObjectCreationParameters": true,
"omnisharp.organizeImportsOnFormat": true
}

1
DiscordWebhookClient Submodule

@ -0,0 +1 @@
Subproject commit d84949a6b735b8e5a079d1d298726da6ca0220e9

View File

@ -0,0 +1,8 @@
using Newsbot.Collector.Domain.Models;
namespace Newsbot.Collector.Domain.Interfaces;
public interface IDiscordNotificatioClient
{
void SendMessage(DiscordMessage payload);
}

View File

@ -6,7 +6,7 @@ public class ArticlesModel
public Guid SourceID { get; set; }
public string Tags { get; set; } = "";
public string Title { get; set; } = "";
public string URL { get; set; } = "";
public string? URL { get; set; }
public DateTime PubDate { get; set; } = DateTime.Now;
public string Video { get; set; } = "";
public int VideoHeight { get; set; } = 0;
@ -14,7 +14,7 @@ public class ArticlesModel
public string Thumbnail { get; set; } = "";
public string Description { get; set; } = "";
public string AuthorName { get; set; } = "";
public string AuthorImage { get; set; } = "";
public string? AuthorImage { get; set; }
}
public class AuthorModel

View File

@ -0,0 +1,65 @@
using Newtonsoft.Json;
namespace Newsbot.Collector.Domain.Models;
public class DiscordMessage
{
[JsonProperty("content")]
public string? Content { get; set; }
[JsonProperty("username")]
public string? Username { get; set; }
[JsonProperty("avatar_url")]
public string? AvatarUrl { get; set; }
//[JsonProperty("thread_name")]
//public string ThreadName { get; set; } = "";
[JsonProperty("embeds")]
public DiscordMessageEmbed[]? Embeds { get; set; }
}
public class DiscordMessageEmbedAuthor
{
[JsonProperty("name")]
public string? Name { get; set; }
[JsonProperty("url")]
public string? Url { get; set; }
[JsonProperty("icon_url")]
public string? IconUrl { get; set; }
}
public class DiscordMessageEmbedField
{
[JsonProperty("name")]
public string? Name { get; set; }
[JsonProperty("value")]
public string? Value { get; set; }
[JsonProperty("inline")]
public bool Inline { get; set; }
}
public class DiscordMessageEmbedImage
{
[JsonProperty("url")]
public string? Url { get; set; }
}
public class DiscordMessageEmbedThumbnail
{
[JsonProperty("url")]
public string? Url { get; set; }
}
public class DiscordMessageEmbedFooter
{
[JsonProperty("text")]
public string? Text { get; set; }
[JsonProperty("icon_url")]
public string? IconUrl { get; set; }
}

View File

@ -0,0 +1,37 @@
using Newtonsoft.Json;
namespace Newsbot.Collector.Domain.Models;
public class DiscordMessageEmbed
{
[JsonProperty("title")]
public string? Title { get; set; }
[JsonProperty("description")]
public string? Description { get; set; }
[JsonProperty("url")]
public string? Url { get; set; }
[JsonProperty("color")]
public int? Color { get; set; }
//2023-03-31T07:00:00.000Z
//[JsonProperty("timestamp")]
//public DateTime TimeStamp { get; set; }
[JsonProperty("author")]
public DiscordMessageEmbedAuthor? Author { get; set; }
[JsonProperty("fields")]
public DiscordMessageEmbedField[]? Fields { get; set; }
[JsonProperty("image")]
public DiscordMessageEmbedImage? Image { get; set; }
[JsonProperty("thumbnail")]
public DiscordMessageEmbedThumbnail? Thumbnail { get; set; }
[JsonProperty("footer")]
public DiscordMessageEmbedFooter? Footer { get; set; }
}

View File

@ -0,0 +1,8 @@
namespace Newsbot.Collector.Domain.Models;
public class DiscordMessageEmbedColors
{
public const int Black = 0;
public const int Red = 16711680;
public const int DarkBlue = 655615;
}

View File

@ -8,6 +8,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,109 @@
using Newsbot.Collector.Database.Repositories;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Domain.Models;
using Newsbot.Collector.Services.Notifications.Discord;
namespace Newsbot.Collector.Services.Jobs;
public class DiscordNotificationJobOptions
{
}
public class DiscordNotifificationJob
{
private IDiscordQueueRepository _queue;
private IArticlesRepository _article;
private IDiscordWebHooksRepository _webhook;
private ISourcesRepository _sources;
private ISubscriptionRepository _subs;
private IDiscordNotificatioClient _webhookClient;
public DiscordNotifificationJob()
{
_queue = new DiscordQueueTable("");
_article = new ArticlesTable("");
_webhook = new DiscordWebhooksTable("");
_sources = new SourcesTable("");
_subs = new SubscriptionsTable("");
_webhookClient = new DiscordWebhookClient("");
}
public void InitAndExecute()
{
}
private void Execute()
{
//collect all the new requests
var requests = _queue.List(25);
foreach (var request in requests)
{
// Get all details on the article in the queue
var articleDetails = _article.GetById(request.ArticleID);
// Get the deatils of the source
var sourceDetails = _sources.GetByID(articleDetails.SourceID);
// Find all the subscriptions for that source
var allSubscriptions = _subs.ListBySourceID(sourceDetails.ID);
foreach (var sub in allSubscriptions)
{
// find the discord webhooks we need to post to
var discordDetails = _webhook.GetByID(sub.DiscordWebHookID);
var client = new DiscordWebhookClient(discordDetails.Url);
client.SendMessage(GenerateDiscordMessage(sourceDetails, articleDetails));
}
}
}
public DiscordMessage GenerateDiscordMessage(SourceModel source, ArticlesModel article)
{
var embed = new DiscordMessageEmbed
{
Title = article.Title,
Color = DiscordMessageEmbedColors.Red,
Description = article.Description,
Author = new DiscordMessageEmbedAuthor
{
Name = article.AuthorName,
},
Footer = new DiscordMessageEmbedFooter
{
Text = "Brought to you by Newsbot",
}
};
if (article.URL is not null && article.URL != "")
{
embed.Url = article.URL;
}
if (article.Thumbnail is not null && 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 DiscordMessageEmbed[]
{
embed
}
};
}
}

View File

@ -131,6 +131,12 @@ public class RssWatcherJob : IHangfireJob
{
foreach (var item in items)
{
if (item.URL is null)
{
Log.Warning($"RSS Watcher collected a blank url and was skipped.");
continue;
}
if (IsThisUrlKnown(item.URL) == true)
{
continue;

View File

@ -0,0 +1,47 @@
using System.Net;
using System.Text;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Domain.Models;
using Newtonsoft.Json;
namespace Newsbot.Collector.Services.Notifications.Discord;
public class DiscordWebhookClient : IDiscordNotificatioClient
{
private string[] _webhooks;
public DiscordWebhookClient(string webhook)
{
_webhooks = new string[] { webhook };
}
public DiscordWebhookClient(string[] webhooks)
{
_webhooks = webhooks;
}
public void SendMessage(DiscordMessage payload)
{
if (payload.Embeds is not null)
{
MessageValidation.IsEmbedFooterValid(payload.Embeds);
}
foreach (var webhook in _webhooks)
{
var jsonRaw = JsonConvert.SerializeObject(payload, Newtonsoft.Json.Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
using StringContent jsonContent = new(jsonRaw, Encoding.UTF8, "application/json");
using var client = new HttpClient();
var resp = client.PostAsync(webhook, jsonContent);
resp.Wait();
if (resp.Result.StatusCode != HttpStatusCode.NoContent)
{
throw new Exception("Message was not accepted by the sever.");
}
}
}
}

View File

@ -0,0 +1,50 @@
using Newsbot.Collector.Domain.Models;
namespace Newsbot.Collector.Services.Notifications.Discord;
public class MessageValidation
{
public void IsMessageValid(DiscordMessage payload)
{
if (payload.Embeds is null)
{
}
}
public static bool IsEmbedFooterValid(DiscordMessageEmbed[] embeds)
{
if (embeds.Count() == 0)
{
return true;
}
foreach (var embed in embeds)
{
if (embed.Footer is null)
{
return true;
}
if (embed.Footer.IconUrl is null)
{
return true;
}
var containsHttp = embed.Footer.IconUrl.Contains("http://");
var containsHttps = embed.Footer.IconUrl.Contains("https://");
if (containsHttp == false || containsHttps == false)
{
throw new Exception("Footer.IconUrl does not contain http:// or https://");
}
}
return true;
}
public static bool IsEmbedAuthorValid(DiscordMessageEmbedAuthor author)
{
return true;
}
}