New custom discord webhook client (#7)
This commit is contained in:
parent
521940ca4f
commit
bb832ed441
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -17,5 +17,7 @@
|
||||
"password": "postgres"
|
||||
}
|
||||
],
|
||||
"editor.formatOnType": true
|
||||
"editor.formatOnType": true,
|
||||
"csharp.inlayHints.parameters.forObjectCreationParameters": true,
|
||||
"omnisharp.organizeImportsOnFormat": true
|
||||
}
|
1
DiscordWebhookClient
Submodule
1
DiscordWebhookClient
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit d84949a6b735b8e5a079d1d298726da6ca0220e9
|
@ -0,0 +1,8 @@
|
||||
using Newsbot.Collector.Domain.Models;
|
||||
|
||||
namespace Newsbot.Collector.Domain.Interfaces;
|
||||
|
||||
public interface IDiscordNotificatioClient
|
||||
{
|
||||
void SendMessage(DiscordMessage payload);
|
||||
}
|
@ -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
|
||||
|
65
Newsbot.Collector.Domain/Models/DiscordMessage.cs
Normal file
65
Newsbot.Collector.Domain/Models/DiscordMessage.cs
Normal 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; }
|
||||
}
|
37
Newsbot.Collector.Domain/Models/DiscordMessageEmbed.cs
Normal file
37
Newsbot.Collector.Domain/Models/DiscordMessageEmbed.cs
Normal 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; }
|
||||
}
|
@ -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;
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
109
Newsbot.Collector.Services/Jobs/DiscordNotificationJob.cs
Normal file
109
Newsbot.Collector.Services/Jobs/DiscordNotificationJob.cs
Normal 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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user