diff --git a/Commands/ApplicationStart/StartInstar.sh b/Commands/ApplicationStart/StartInstar.sh
index b7086be..33b211a 100644
--- a/Commands/ApplicationStart/StartInstar.sh
+++ b/Commands/ApplicationStart/StartInstar.sh
@@ -12,7 +12,7 @@ startInstar() {
local INSTAR_CONF="/Instar/bin/Config/$INSTAR_CONF_FILE"
- /Instar/bin/InstarBot --config-path "$INSTAR_CONF" > /dev/null 2> /dev/null < /dev/null &
+ /Instar/bin/InstarBot --config-path "$INSTAR_CONF" --level Debug > /dev/null 2> /dev/null < /dev/null &
}
startInstar
\ No newline at end of file
diff --git a/InstarBot.Tests.Common/EmbedVerifier.cs b/InstarBot.Tests.Common/EmbedVerifier.cs
index f22f65c..0ed5863 100644
--- a/InstarBot.Tests.Common/EmbedVerifier.cs
+++ b/InstarBot.Tests.Common/EmbedVerifier.cs
@@ -1,6 +1,7 @@
using System.Collections.Immutable;
using Discord;
using Serilog;
+using Xunit;
namespace InstarBot.Tests;
diff --git a/InstarBot.Tests.Common/InstarBot.Tests.Common.csproj b/InstarBot.Tests.Common/InstarBot.Tests.Common.csproj
index 6e8fa45..fb541f3 100644
--- a/InstarBot.Tests.Common/InstarBot.Tests.Common.csproj
+++ b/InstarBot.Tests.Common/InstarBot.Tests.Common.csproj
@@ -7,20 +7,20 @@
InstarBot.Tests
false
false
+ Exe
+
-
-
-
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/InstarBot.Tests.Integration/Assembly.cs b/InstarBot.Tests.Integration/Assembly.cs
deleted file mode 100644
index 4941ec7..0000000
--- a/InstarBot.Tests.Integration/Assembly.cs
+++ /dev/null
@@ -1 +0,0 @@
-[assembly: Parallelize]
\ No newline at end of file
diff --git a/InstarBot.Tests.Integration/InstarBot.Tests.Integration.csproj b/InstarBot.Tests.Integration/InstarBot.Tests.Integration.csproj
index 3021c4c..7a51cf5 100644
--- a/InstarBot.Tests.Integration/InstarBot.Tests.Integration.csproj
+++ b/InstarBot.Tests.Integration/InstarBot.Tests.Integration.csproj
@@ -4,20 +4,20 @@
net10.0
enable
enable
+ Exe
+
-
-
-
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/InstarBot.Tests.Integration/Interactions/AutoMemberSystemCommandTests.cs b/InstarBot.Tests.Integration/Interactions/AutoMemberSystemCommandTests.cs
index 0c5ef3e..4cb0fd8 100644
--- a/InstarBot.Tests.Integration/Interactions/AutoMemberSystemCommandTests.cs
+++ b/InstarBot.Tests.Integration/Interactions/AutoMemberSystemCommandTests.cs
@@ -149,7 +149,7 @@ await tds.CreateNotificationAsync(new Notification
// There is a potential asynchronous delay here, so let's keep waiting for this condition for 5 seconds.
await Task.WhenAny(
- Task.Delay(5000),
+ Task.Delay(5000, TestContext.Current.CancellationToken),
Task.Factory.StartNew(async () =>
{
while (true)
@@ -160,7 +160,7 @@ await Task.WhenAny(
// only poll once every 50ms
await Task.Delay(50);
}
- }));
+ }, TestContext.Current.CancellationToken));
}
[Fact]
diff --git a/InstarBot.Tests.Integration/Interactions/PageCommandTests.cs b/InstarBot.Tests.Integration/Interactions/PageCommandTests.cs
index 6550506..96319f0 100644
--- a/InstarBot.Tests.Integration/Interactions/PageCommandTests.cs
+++ b/InstarBot.Tests.Integration/Interactions/PageCommandTests.cs
@@ -14,7 +14,7 @@ public static class PageCommandTests
{
private const string TestReason = "Test reason for paging";
- private static async Task SetupOrchestrator(PageCommandTestContext context)
+ private static async Task SetupOrchestrator(PageCommandTestContext context)
{
var orchestrator = TestOrchestrator.Default;
diff --git a/InstarBot.Tests.Integration/Interactions/PingCommandTests.cs b/InstarBot.Tests.Integration/Interactions/PingCommandTests.cs
index de207af..542116b 100644
--- a/InstarBot.Tests.Integration/Interactions/PingCommandTests.cs
+++ b/InstarBot.Tests.Integration/Interactions/PingCommandTests.cs
@@ -6,13 +6,13 @@ namespace InstarBot.Tests.Integration.Interactions;
public static class PingCommandTests
{
- ///
- /// Tests that the ping command emits an ephemeral "Pong!" response.
- ///
- /// This test verifies that calling the ping command results in the
- /// expected ephemeral "Pong!" response.
- ///
- [Fact(DisplayName = "User should be able to issue the Ping command.")]
+ ///
+ /// Tests that the ping command emits an ephemeral "Pong!" response.
+ ///
+ /// This test verifies that calling the ping command results in the
+ /// expected ephemeral "Pong!" response.
+ ///
+ [Fact(DisplayName = "User should be able to issue the Ping command.")]
public static async Task PingCommand_Send_ShouldEmitEphemeralPong()
{
// Arrange
diff --git a/InstarBot.Tests.Integration/Services/BirthdaySystemTests.cs b/InstarBot.Tests.Integration/Services/BirthdaySystemTests.cs
index 6bb96e4..0ac5765 100644
--- a/InstarBot.Tests.Integration/Services/BirthdaySystemTests.cs
+++ b/InstarBot.Tests.Integration/Services/BirthdaySystemTests.cs
@@ -4,6 +4,7 @@
using Moq;
using PaxAndromeda.Instar;
using PaxAndromeda.Instar.Services;
+using Serilog;
using Xunit;
using Metric = PaxAndromeda.Instar.Metrics.Metric;
@@ -68,7 +69,7 @@ public static async Task BirthdaySystem_WhenUserBirthday_ShouldGrantRole()
var channel = await orchestrator.Discord.GetChannel(orchestrator.Configuration.BirthdayConfig.BirthdayAnnounceChannel) as TestChannel;
channel.Should().NotBeNull();
- var messages = await channel.GetMessagesAsync().SelectMany(n => n).ToListAsync();
+ var messages = await channel.GetMessagesAsync().SelectMany(n => n).ToListAsync(TestContext.Current.CancellationToken);
messages.Count.Should().BeGreaterThan(0);
TestUtilities.MatchesFormat(Strings.Birthday_Announcement, messages[0].Content);
@@ -153,7 +154,7 @@ public static async Task BirthdaySystem_WhenNoBirthdays_ShouldDoNothing()
var channel = await orchestrator.Discord.GetChannel(orchestrator.Configuration.BirthdayConfig.BirthdayAnnounceChannel) as TestChannel;
channel.Should().NotBeNull();
- var messages = await channel.GetMessagesAsync().SelectMany(n => n).ToListAsync();
+ var messages = await channel.GetMessagesAsync().SelectMany(n => n).ToListAsync(TestContext.Current.CancellationToken);
messages.Count.Should().Be(0);
}
@@ -210,8 +211,8 @@ public static async Task BirthdaySystem_WithBirthdayRoleButNoBirthdayRecord_Shou
public static async Task BirthdaySystem_WithUserBirthdayStill_ShouldKeepBirthdayRoles()
{
// Arrange
- var birthday = DateTime.Parse("2000-02-13T12:00:00Z");
- var currentTime = DateTime.Parse("2025-02-14T00:00:00Z");
+ var birthday = DateTimeOffset.Parse("2000-02-13T00:00:00-08:00");
+ var currentTime = DateTimeOffset.Parse("2025-02-14T00:00:00Z");
var orchestrator = await SetupOrchestrator(currentTime, birthday);
var system = orchestrator.GetService();
diff --git a/InstarBot.Tests.Orchestrator/InstarBot.Test.Framework.csproj b/InstarBot.Tests.Orchestrator/InstarBot.Test.Framework.csproj
index 76bb766..d61f75d 100644
--- a/InstarBot.Tests.Orchestrator/InstarBot.Test.Framework.csproj
+++ b/InstarBot.Tests.Orchestrator/InstarBot.Test.Framework.csproj
@@ -4,8 +4,15 @@
net10.0
enable
enable
+ Exe
+
+
+
+
+
+
diff --git a/InstarBot.Tests.Orchestrator/TestOrchestrator.cs b/InstarBot.Tests.Orchestrator/TestOrchestrator.cs
index 5db5933..9e59670 100644
--- a/InstarBot.Tests.Orchestrator/TestOrchestrator.cs
+++ b/InstarBot.Tests.Orchestrator/TestOrchestrator.cs
@@ -12,6 +12,8 @@
using Serilog.Events;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
+using Serilog.Sinks.XUnit3;
+using Xunit;
using InvalidOperationException = Amazon.CloudWatchLogs.Model.InvalidOperationException;
namespace InstarBot.Test.Framework
@@ -109,12 +111,13 @@ private void InitializeActor()
tdbs.CreateUserAsync(InstarUserData.CreateFrom(user)).Wait();
}
- private static void SetupLogging()
+ public static void SetupLogging()
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.MinimumLevel.Is(LogEventLevel.Verbose)
.WriteTo.Console()
+ .WriteTo.XUnit3TestOutput()
.CreateLogger();
Log.Warning("Logging is enabled for this unit test.");
}
diff --git a/InstarBot.Tests.Unit/Assembly.cs b/InstarBot.Tests.Unit/Assembly.cs
deleted file mode 100644
index 5d68f78..0000000
--- a/InstarBot.Tests.Unit/Assembly.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-[assembly: Parallelize]
\ No newline at end of file
diff --git a/InstarBot.Tests.Unit/AsyncAutoResetEventTests.cs b/InstarBot.Tests.Unit/AsyncAutoResetEventTests.cs
index de0ac0c..933ee7d 100644
--- a/InstarBot.Tests.Unit/AsyncAutoResetEventTests.cs
+++ b/InstarBot.Tests.Unit/AsyncAutoResetEventTests.cs
@@ -15,7 +15,7 @@ public void WaitAsync_CompletesImmediately_WhenInitiallySignaled()
var ev = new AsyncAutoResetEvent(true);
// Act
- var task = ev.WaitAsync();
+ var task = ev.WaitAsync(TestContext.Current.CancellationToken);
// Assert
task.IsCompleted.Should().BeTrue();
@@ -26,8 +26,8 @@ public void Set_ReleasesSingleWaiter()
{
var ev = new AsyncAutoResetEvent(false);
- var waiter1 = ev.WaitAsync();
- var waiter2 = ev.WaitAsync();
+ var waiter1 = ev.WaitAsync(TestContext.Current.CancellationToken);
+ var waiter2 = ev.WaitAsync(TestContext.Current.CancellationToken);
// First Set should release only one waiter.
ev.Set();
@@ -49,13 +49,13 @@ public void Set_MarksEventSignaled_WhenNoWaiters()
// No waiters now — Set should mark the event signaled so the next WaitAsync completes immediately.
ev.Set();
- var immediate = ev.WaitAsync();
+ var immediate = ev.WaitAsync(TestContext.Current.CancellationToken);
// WaitAsync should complete immediately after Set() when there were no waiters
immediate.IsCompleted.Should().BeTrue();
// That consumption should reset the event; a subsequent waiter should block.
- var next = ev.WaitAsync();
+ var next = ev.WaitAsync(TestContext.Current.CancellationToken);
next.IsCompleted.Should().BeFalse();
}
diff --git a/InstarBot.Tests.Unit/BirthdayTests.cs b/InstarBot.Tests.Unit/BirthdayTests.cs
index b173e57..3b33abf 100644
--- a/InstarBot.Tests.Unit/BirthdayTests.cs
+++ b/InstarBot.Tests.Unit/BirthdayTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using FluentAssertions;
using Moq;
using PaxAndromeda.Instar;
diff --git a/InstarBot.Tests.Unit/InstarBot.Tests.Unit.csproj b/InstarBot.Tests.Unit/InstarBot.Tests.Unit.csproj
index 4b438cd..92bf113 100644
--- a/InstarBot.Tests.Unit/InstarBot.Tests.Unit.csproj
+++ b/InstarBot.Tests.Unit/InstarBot.Tests.Unit.csproj
@@ -7,16 +7,15 @@
false
InstarBot.Tests
+ Exe
+
-
-
-
runtime; build; native; contentfiles; analyzers; buildtransitive
all
@@ -25,6 +24,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
+
diff --git a/InstarBot/Birthday.cs b/InstarBot/Birthday.cs
index fa86da9..163b7df 100644
--- a/InstarBot/Birthday.cs
+++ b/InstarBot/Birthday.cs
@@ -1,4 +1,5 @@
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
+using Serilog;
namespace PaxAndromeda.Instar;
@@ -94,7 +95,7 @@ public bool IsToday
var currentLocalTime = dtNow.ToOffset(utcOffset);
var localTimeToday = new DateTimeOffset(currentLocalTime.Date, currentLocalTime.Offset);
- var localTimeTomorrow = localTimeToday.Date.AddDays(1);
+ var localTimeTomorrow = localTimeToday.AddDays(1);
return Observed >= localTimeToday && Observed < localTimeTomorrow;
}
diff --git a/InstarBot/Commands/SetBirthdayCommand.cs b/InstarBot/Commands/SetBirthdayCommand.cs
index 0d34ca5..072ceda 100644
--- a/InstarBot/Commands/SetBirthdayCommand.cs
+++ b/InstarBot/Commands/SetBirthdayCommand.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
diff --git a/InstarBot/ConfigModels/AutoMemberConfig.cs b/InstarBot/ConfigModels/AutoMemberConfig.cs
index fa94058..8be803f 100644
--- a/InstarBot/ConfigModels/AutoMemberConfig.cs
+++ b/InstarBot/ConfigModels/AutoMemberConfig.cs
@@ -14,7 +14,8 @@ public sealed class AutoMemberConfig
public int MinimumMessages { get; init; }
public int MinimumMessageTime { get; init; }
public List RequiredRoles { get; init; } = null!;
- public bool EnableGaiusCheck { get; init; }
+ public bool EnableGaiusCheck { get; init; }
+ public List AllowedPunishmentReasons { get; init; } = [];
}
[UsedImplicitly]
diff --git a/InstarBot/InstarBot.csproj b/InstarBot/InstarBot.csproj
index 19d97bf..603c8c3 100644
--- a/InstarBot/InstarBot.csproj
+++ b/InstarBot/InstarBot.csproj
@@ -22,6 +22,7 @@
+
diff --git a/InstarBot/Modals/UserUpdatedEventArgs.cs b/InstarBot/Modals/UserUpdatedEventArgs.cs
index 0c0a2e1..dd462f7 100644
--- a/InstarBot/Modals/UserUpdatedEventArgs.cs
+++ b/InstarBot/Modals/UserUpdatedEventArgs.cs
@@ -2,18 +2,18 @@
namespace PaxAndromeda.Instar.Modals;
-public class UserUpdatedEventArgs(Snowflake id, IGuildUser before, IGuildUser after)
+public class UserUpdatedEventArgs(Snowflake id, IUser before, IUser after)
{
public Snowflake ID { get; } = id;
- public IGuildUser Before { get; } = before;
+ public IUser Before { get; } = before;
- public IGuildUser After { get; } = after;
+ public IUser After { get; } = after;
// TODO: add additional parts to this for the data we care about
///
/// A flag indicating whether data we care about (e.g. nicknames, usernames) has changed.
///
- public bool HasUpdated => Before.Username != After.Username || Before.Nickname != After.Nickname;
+ public bool HasUpdated => Before.Username != After.Username || (Before is IGuildUser gBefore && After is IGuildUser gAfter && gBefore.Nickname != gAfter.Nickname);
}
\ No newline at end of file
diff --git a/InstarBot/Services/AutoMemberSystem.cs b/InstarBot/Services/AutoMemberSystem.cs
index a367fa5..a094ce1 100644
--- a/InstarBot/Services/AutoMemberSystem.cs
+++ b/InstarBot/Services/AutoMemberSystem.cs
@@ -64,22 +64,23 @@ internal override async Task Initialize()
await PreloadGaiusPunishments();
}
- ///
- /// Filters warnings and caselogs to only focus on the ones we care about. For example, we don't
- /// want to withhold membership from someone who was kicked for having a too new Discord account.
- ///
- /// A collection of warnings retrieved from the Gaius API.
- /// A collection of caselogs retrieved from the Gaius API.
- /// A tuple containing the filtered warnings and caselogs, respectively.
- /// If or is null.
- private static (IEnumerable, IEnumerable) FilterPunishments(IEnumerable warnings, IEnumerable caselogs)
+ ///
+ /// Filters warnings and caselogs to only focus on the ones we care about. For example, we don't
+ /// want to withhold membership from someone who was kicked for having a too new Discord account.
+ ///
+ /// The current configuration version in use.
+ /// A collection of warnings retrieved from the Gaius API.
+ /// A collection of caselogs retrieved from the Gaius API.
+ /// A tuple containing the filtered warnings and caselogs, respectively.
+ /// If or is null.
+ private static (IEnumerable, IEnumerable) FilterPunishments(InstarDynamicConfiguration cfg, IEnumerable warnings, IEnumerable caselogs)
{
if (warnings is null)
throw new ArgumentNullException(nameof(warnings));
if (caselogs is null)
throw new ArgumentNullException(nameof(caselogs));
-
- var filteredCaselogs = caselogs.Where(n => n is not { Type: CaselogType.Kick, Reason: "Join age punishment" });
+
+ var filteredCaselogs = caselogs.Where(n => n.Type != CaselogType.Kick && !cfg.AutoMemberConfig.AllowedPunishmentReasons.Contains(n.Reason));
return (warnings, filteredCaselogs);
}
@@ -92,9 +93,11 @@ private async Task UpdateGaiusPunishments()
// bias for an hour and a half ago.
var afterTime = _timeProvider.GetUtcNow().UtcDateTime - TimeSpan.FromHours(1.5);
+ var cfg = await _dynamicConfig.GetConfig();
+
try
{
- var (warnings, caselogs) = FilterPunishments(
+ var (warnings, caselogs) = FilterPunishments(cfg,
await _gaiusApiService.GetWarningsAfter(afterTime),
await _gaiusApiService.GetCaselogsAfter(afterTime));
@@ -192,13 +195,17 @@ private async Task HandleUserUpdated(UserUpdatedEventArgs arg)
if (!arg.HasUpdated)
return;
+ if (arg.After is not IGuildUser gAfter)
+ return;
+
var user = await _ddbService.GetUserAsync(arg.ID);
if (user is null)
{
+
// new user for the database, create from the latest data and return
try
{
- await _ddbService.CreateUserAsync(InstarUserData.CreateFrom(arg.After));
+ await _ddbService.CreateUserAsync(InstarUserData.CreateFrom(gAfter));
Log.Information("Created new user {Username} (user ID {UserID})", arg.After.Username, arg.ID);
}
catch (Exception ex)
@@ -217,10 +224,13 @@ private async Task HandleUserUpdated(UserUpdatedEventArgs arg)
user.Data.Username = arg.After.Username;
changed = true;
}
+
+ if (arg.Before is not IGuildUser gBefore)
+ return;
- if (arg.Before.Nickname != arg.After.Nickname)
+ if (gBefore.Nickname != gAfter.Nickname)
{
- user.Data.Nicknames?.Add(new InstarUserDataHistoricalEntry(_timeProvider.GetUtcNow().UtcDateTime, arg.After.Nickname));
+ user.Data.Nicknames?.Add(new InstarUserDataHistoricalEntry(_timeProvider.GetUtcNow().UtcDateTime, gAfter.Nickname));
changed = true;
}
@@ -230,7 +240,7 @@ private async Task HandleUserUpdated(UserUpdatedEventArgs arg)
await user.CommitAsync();
}
- await HandleAutoKickRoles(arg.After);
+ await HandleAutoKickRoles(gAfter);
}
private async Task HandleAutoKickRoles(IGuildUser user, InstarDynamicConfiguration? cfg = null)
@@ -473,9 +483,11 @@ private Dictionary GetMessagesSent()
private async Task PreloadGaiusPunishments()
{
+ var cfg = await _dynamicConfig.GetConfig();
+
try
{
- var (warnings, caselogs) = FilterPunishments(
+ var (warnings, caselogs) = FilterPunishments(cfg,
await _gaiusApiService.GetAllWarnings(),
await _gaiusApiService.GetAllCaselogs());
diff --git a/InstarBot/Services/BirthdaySystem.cs b/InstarBot/Services/BirthdaySystem.cs
index 8895989..6d030ef 100644
--- a/InstarBot/Services/BirthdaySystem.cs
+++ b/InstarBot/Services/BirthdaySystem.cs
@@ -36,6 +36,8 @@ public override async Task RunAsync()
var cfg = await dynamicConfig.GetConfig();
var currentTime = _timeProvider.GetUtcNow().UtcDateTime;
+ await discord.SyncUsers();
+
await RemoveBirthdays(cfg, currentTime);
var successfulAdds = await GrantBirthdays(cfg, currentTime);
@@ -97,21 +99,6 @@ private async Task RemoveBirthdays(InstarDynamicConfiguration cfg, DateTime curr
toRemove.Add(user.Data.UserID!);
continue;
}
-
- var birthDate = user.Data.Birthday.Birthdate;
-
- var thisYearBirthday = new DateTime(
- currentTime.Year,
- birthDate.Month, birthDate.Day, birthDate.Hour, birthDate.Minute, 0, DateTimeKind.Utc);
-
- if (thisYearBirthday > currentTime)
- thisYearBirthday = thisYearBirthday.AddYears(-1);
-
- if (currentTime - thisYearBirthday >= TimeSpan.FromDays(1))
- {
- Log.Information("Removing birthday role from {UserID} due to their birthday not being today. CurrentTime={CurrentTime}, ThisYearBirthday={ThisYearBirthday}, Diff={TimeDiff}", user.Data.UserID, currentTime, thisYearBirthday, currentTime - thisYearBirthday);
- toRemove.Add(user.Data.UserID!);
- }
}
foreach (var snowflake in toRemove)
diff --git a/InstarBot/Services/DiscordService.cs b/InstarBot/Services/DiscordService.cs
index 3854682..e3f19f0 100644
--- a/InstarBot/Services/DiscordService.cs
+++ b/InstarBot/Services/DiscordService.cs
@@ -87,6 +87,7 @@ public DiscordService(IServiceProvider provider, IConfiguration config, IDynamic
_socketClient.MessageDeleted += async (msgCache, _) => await _messageDeletedEvent.Invoke(msgCache.Id);
_socketClient.UserJoined += async user => await _userJoinedEvent.Invoke(user);
_socketClient.UserLeft += async (_, user) => await _userLeftEvent.Invoke(user);
+ _socketClient.UserUpdated += HandleUserUpdate;
_socketClient.GuildMemberUpdated += HandleUserUpdate;
_interactionService.Log += HandleDiscordLog;
@@ -97,19 +98,29 @@ public DiscordService(IServiceProvider provider, IConfiguration config, IDynamic
// Validate
if (_guild == 0)
throw new ConfigurationException("TargetGuild is not set");
- }
+ }
- private async Task HandleUserUpdate(Cacheable before, SocketGuildUser after)
+ private Task HandleUserUpdate(Cacheable before, SocketGuildUser after)
{
// Can't do anything if we don't have a before state.
// In the case of a user join, that is handled in UserJoined event.
if (!before.HasValue)
- return;
+ return Task.CompletedTask;
+
+ return HandleUserUpdate(before.Value, after);
+ }
- await _userUpdatedEvent.Invoke(new UserUpdatedEventArgs(after.Id, before.Value, after));
+ private async Task HandleUserUpdate(SocketGuildUser before, SocketGuildUser after)
+ {
+ await _userUpdatedEvent.Invoke(new UserUpdatedEventArgs(after.Id, before, after));
+ }
+
+ private async Task HandleUserUpdate(SocketUser before, SocketUser after)
+ {
+ await _userUpdatedEvent.Invoke(new UserUpdatedEventArgs(after.Id, before, after));
}
- private async Task HandleMessageCommand(SocketMessageCommand arg)
+ private async Task HandleMessageCommand(SocketMessageCommand arg)
{
Log.Information("Message command: {CommandName}", arg.CommandName);
diff --git a/InstarBot/Services/NotificationService.cs b/InstarBot/Services/NotificationService.cs
index e438c6e..8a6a45a 100644
--- a/InstarBot/Services/NotificationService.cs
+++ b/InstarBot/Services/NotificationService.cs
@@ -64,6 +64,17 @@ private async Task ProcessNotification(InstarDatabaseEntry n
var actor = discordService.GetUser(notification.Data.Actor);
+ // Don't post a notification if the reference user (if set) is not on the server.
+ if (notification.Data.ReferenceUser is not null)
+ {
+ var referenceUser = discordService.GetUser(notification.Data.ReferenceUser);
+ if (referenceUser is null)
+ {
+ Log.Warning("Reference user {ReferenceUserId} for notification dated {NotificationDate} not found on the server; skipping notification", notification.Data.ReferenceUser, notification.Data.Date);
+ return true;
+ }
+ }
+
Log.Debug("Actor ID {UserId} name: {Username}", notification.Data.Actor.ID, actor?.Username ?? "");
var embed = new NotificationEmbed(notification.Data, actor, cfg);