Add a discord webhook for when users join for the first time. (#698)

* Add a discord webhook for when users join for the first time.

* Fix compilation error related to embed colors

* Make config migration backup use the version of the stored file

* Make DiscordConfiguration not halt startup

* Allow the registration message to be configured

* Clean up registration announcement and add userId variable

* Fix userid resolution and convert newline string to actual character
Also make WebhookHelper not fail if all destinations aren't configured
This commit is contained in:
Josh 2023-03-22 19:57:28 -05:00 committed by GitHub
parent b87c16ab7c
commit fa5ff0b490
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 90 additions and 26 deletions

View file

@ -139,6 +139,19 @@ public class LoginController : ControllerBase
user.LinkedPsnId = npTicket.Platform != Platform.RPCS3 ? npTicket.UserId : 0; user.LinkedPsnId = npTicket.Platform != Platform.RPCS3 ? npTicket.UserId : 0;
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
if (DiscordConfiguration.Instance.DiscordIntegrationEnabled)
{
string registrationAnnouncementMsg = DiscordConfiguration.Instance.RegistrationAnnouncement
.Replace("%user", username)
.Replace("%id", user.UserId.ToString())
.Replace("%instance", ServerConfiguration.Instance.Customization.ServerName)
.Replace("%platform", npTicket.Platform.ToString())
.Replace(@"\n", "\n");
await WebhookHelper.SendWebhook(title: "A new user has registered!",
description: registrationAnnouncementMsg,
dest: WebhookHelper.WebhookDestination.Registration);
}
Logger.Success($"Created new user for {username}, platform={npTicket.Platform}", LogArea.Login); Logger.Success($"Created new user for {username}, platform={npTicket.Platform}", LogArea.Login);
} }
// automatically change username if it doesn't match // automatically change username if it doesn't match

View file

@ -139,7 +139,7 @@ public class PhotosController : ControllerBase
Title = "New photo uploaded!", Title = "New photo uploaded!",
Description = $"{user.Username} uploaded a new photo.", Description = $"{user.Username} uploaded a new photo.",
ImageUrl = $"{ServerConfiguration.Instance.ExternalUrl}/gameAssets/{photo.LargeHash}", ImageUrl = $"{ServerConfiguration.Instance.ExternalUrl}/gameAssets/{photo.LargeHash}",
Color = WebhookHelper.UnionColor, Color = WebhookHelper.GetEmbedColor(),
} }
); );

View file

@ -117,7 +117,7 @@ public abstract class ConfigurationBase<T> where T : class, new()
{ {
int newVersion = GetVersion(); int newVersion = GetVersion();
Logger.Info($"Upgrading config file from version {storedConfig.ConfigVersion} to version {newVersion}", LogArea.Config); Logger.Info($"Upgrading config file from version {storedConfig.ConfigVersion} to version {newVersion}", LogArea.Config);
File.Copy(this.ConfigName, this.ConfigName + "." + GetVersion()); File.Copy(this.ConfigName, this.ConfigName + ".v" + storedConfig.ConfigVersion);
this.loadConfig(storedConfig); this.loadConfig(storedConfig);
this.ConfigVersion = newVersion; this.ConfigVersion = newVersion;
this.writeConfig(this.ConfigName); this.writeConfig(this.ConfigName);

View file

@ -1,13 +0,0 @@
#nullable enable
namespace LBPUnion.ProjectLighthouse.Configuration.ConfigurationCategories;
public class DiscordIntegrationConfiguration
{
//TODO: integrations should be modular/abstracted away
public bool DiscordIntegrationEnabled { get; set; }
public string Url { get; set; } = "";
public string ModerationUrl { get; set; } = "";
}

View file

@ -0,0 +1,34 @@
using YamlDotNet.Serialization;
namespace LBPUnion.ProjectLighthouse.Configuration;
public class DiscordConfiguration : ConfigurationBase<DiscordConfiguration>
{
// HEY, YOU!
// THIS VALUE MUST BE INCREMENTED FOR EVERY CONFIG CHANGE!
//
// This is so Lighthouse can properly identify outdated configurations and update them with newer settings accordingly.
// If you are modifying anything here, this value MUST be incremented.
// Thanks for listening~
public override int ConfigVersion { get; set; } = 1;
public override string ConfigName { get; set; } = "discord.yml";
public override bool NeedsConfiguration { get; set; } = false;
// TODO integrations should be more modular
public bool DiscordIntegrationEnabled { get; set; } = false;
public string EmbedColor { get; set; } = "#008CFF";
public string PublicUrl { get; set; } = "";
public string ModerationUrl { get; set; } = "";
public string RegistrationUrl { get; set; } = "";
public string RegistrationAnnouncement { get; set; } = "%user just connected to %instance for the first time using %platform!";
public override ConfigurationBase<DiscordConfiguration> Deserialize(IDeserializer deserializer, string text) => deserializer.Deserialize<DiscordConfiguration>(text);
}

View file

@ -11,7 +11,7 @@ public class ServerConfiguration : ConfigurationBase<ServerConfiguration>
// This is so Lighthouse can properly identify outdated configurations and update them with newer settings accordingly. // This is so Lighthouse can properly identify outdated configurations and update them with newer settings accordingly.
// If you are modifying anything here, this value MUST be incremented. // If you are modifying anything here, this value MUST be incremented.
// Thanks for listening~ // Thanks for listening~
public override int ConfigVersion { get; set; } = 18; public override int ConfigVersion { get; set; } = 19;
public override string ConfigName { get; set; } = "lighthouse.yml"; public override string ConfigName { get; set; } = "lighthouse.yml";
public string WebsiteListenUrl { get; set; } = "http://localhost:10060"; public string WebsiteListenUrl { get; set; } = "http://localhost:10060";
@ -34,7 +34,6 @@ public class ServerConfiguration : ConfigurationBase<ServerConfiguration>
public AuthenticationConfiguration Authentication { get; set; } = new(); public AuthenticationConfiguration Authentication { get; set; } = new();
public CaptchaConfiguration Captcha { get; set; } = new(); public CaptchaConfiguration Captcha { get; set; } = new();
public DigestKeyConfiguration DigestKey { get; set; } = new(); public DigestKeyConfiguration DigestKey { get; set; } = new();
public DiscordIntegrationConfiguration DiscordIntegration { get; set; } = new();
public GoogleAnalyticsConfiguration GoogleAnalytics { get; set; } = new(); public GoogleAnalyticsConfiguration GoogleAnalytics { get; set; } = new();
public MailConfiguration Mail { get; set; } = new(); public MailConfiguration Mail { get; set; } = new();
public UserGeneratedContentLimitConfiguration UserGeneratedContentLimits { get; set; } = new(); public UserGeneratedContentLimitConfiguration UserGeneratedContentLimits { get; set; } = new();

View file

@ -3,6 +3,9 @@ using System.Threading.Tasks;
using Discord; using Discord;
using Discord.Webhook; using Discord.Webhook;
using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Configuration;
using SixLabors.ImageSharp.PixelFormats;
using Color = SixLabors.ImageSharp.Color;
using DiscordColor = Discord.Color;
namespace LBPUnion.ProjectLighthouse.Helpers; namespace LBPUnion.ProjectLighthouse.Helpers;
@ -21,31 +24,52 @@ public static class WebhookHelper
/// A channel intended for moderators; where grief reports are sent. /// A channel intended for moderators; where grief reports are sent.
/// </summary> /// </summary>
Moderation, Moderation,
/// <summary>
/// A channel intended for public viewing; specifically for announcing user registrations
/// </summary>
Registration,
} }
private static readonly DiscordWebhookClient publicClient = ServerConfiguration.Instance.DiscordIntegration.DiscordIntegrationEnabled private static bool isDestinationValid(WebhookDestination dest)
? new DiscordWebhookClient(ServerConfiguration.Instance.DiscordIntegration.Url) {
if (!DiscordConfiguration.Instance.DiscordIntegrationEnabled) return false;
string url = dest switch
{
WebhookDestination.Public => DiscordConfiguration.Instance.PublicUrl,
WebhookDestination.Moderation => DiscordConfiguration.Instance.ModerationUrl,
WebhookDestination.Registration => DiscordConfiguration.Instance.RegistrationUrl,
_ => throw new ArgumentOutOfRangeException(nameof(dest), dest, null),
};
return !string.IsNullOrWhiteSpace(url);
}
private static readonly DiscordWebhookClient publicClient = isDestinationValid(WebhookDestination.Public)
? new DiscordWebhookClient(DiscordConfiguration.Instance.PublicUrl)
: null; : null;
private static readonly DiscordWebhookClient moderationClient = ServerConfiguration.Instance.DiscordIntegration.DiscordIntegrationEnabled private static readonly DiscordWebhookClient moderationClient = isDestinationValid(WebhookDestination.Moderation)
? new DiscordWebhookClient(ServerConfiguration.Instance.DiscordIntegration.ModerationUrl) ? new DiscordWebhookClient(DiscordConfiguration.Instance.ModerationUrl)
: null; : null;
public static readonly Color UnionColor = new(0, 140, 255); private static readonly DiscordWebhookClient registrationClient = isDestinationValid(WebhookDestination.Registration)
? new DiscordWebhookClient(DiscordConfiguration.Instance.RegistrationUrl)
: null;
public static Task SendWebhook(EmbedBuilder builder, WebhookDestination dest = WebhookDestination.Public) public static Task SendWebhook(EmbedBuilder builder, WebhookDestination dest = WebhookDestination.Public)
=> SendWebhook(builder.Build(), dest); => SendWebhook(builder.Build(), dest);
public static async Task SendWebhook(Embed embed, WebhookDestination dest = WebhookDestination.Public) public static async Task SendWebhook(Embed embed, WebhookDestination dest = WebhookDestination.Public)
{ {
if (!ServerConfiguration.Instance.DiscordIntegration.DiscordIntegrationEnabled) return; if (!DiscordConfiguration.Instance.DiscordIntegrationEnabled) return;
DiscordWebhookClient client = dest switch DiscordWebhookClient client = dest switch
{ {
WebhookDestination.Public => publicClient, WebhookDestination.Public => publicClient,
WebhookDestination.Moderation => moderationClient, WebhookDestination.Moderation => moderationClient,
WebhookDestination.Registration => registrationClient,
_ => throw new ArgumentOutOfRangeException(nameof(dest), dest, null), _ => throw new ArgumentOutOfRangeException(nameof(dest), dest, null),
}; };
if (client == null) return;
await client.SendMessageAsync await client.SendMessageAsync
( (
@ -56,6 +80,13 @@ public static class WebhookHelper
); );
} }
public static DiscordColor GetEmbedColor()
{
Color embedColor = Color.ParseHex(DiscordConfiguration.Instance.EmbedColor);
Rgb24 pixel = embedColor.ToPixel<Rgb24>();
return new DiscordColor(pixel.R, pixel.G, pixel.B);
}
public static Task SendWebhook(string title, string description, WebhookDestination dest = WebhookDestination.Public) public static Task SendWebhook(string title, string description, WebhookDestination dest = WebhookDestination.Public)
=> SendWebhook => SendWebhook
( (
@ -63,7 +94,7 @@ public static class WebhookHelper
{ {
Title = title, Title = title,
Description = description, Description = description,
Color = UnionColor, Color = GetEmbedColor(),
}, },
dest dest
); );