diff --git a/.gitmodules b/.gitmodules
index 7ca35a9..28319d5 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,6 @@
[submodule "src/Mmcc.Bot.Polychat/Protos"]
path = src/Mmcc.Bot.Polychat/Protos
url = https://github.com/ModdedMinecraftClub/protos
+[submodule "src/porbeagle"]
+ path = src/porbeagle
+ url = https://github.com/TraceLD/porbeagle
diff --git a/src/Mmcc.Bot.Caching/ButtonHandlerRepository.cs b/src/Mmcc.Bot.Caching/ButtonHandlerRepository.cs
deleted file mode 100644
index 3a3ba4c..0000000
--- a/src/Mmcc.Bot.Caching/ButtonHandlerRepository.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using System;
-using Microsoft.Extensions.Caching.Memory;
-using Mmcc.Bot.Caching.Entities;
-
-namespace Mmcc.Bot.Caching;
-
-///
-/// Represents a button handler repository.
-///
-public interface IButtonHandlerRepository
-{
- ///
- /// Registers a new button handler from a object with the repository.
- ///
- /// object containing the handler.
- void Register(HandleableButton handleableButton);
-
- ///
- /// Registers a button handler with the repository.
- ///
- /// The ID the button handler corresponds to.
- /// The .
- void Register(ulong buttonId, ButtonHandler handler);
-
- ///
- /// De-registers a button handler from the repository.
- ///
- /// The ID of the button to deregister.
- void Deregister(ulong buttonId);
-
- ///
- /// Gets a button handler for a button with a given ID or default value of if not found.
- ///
- /// The ID of the button for which to get a button handler.
- /// The button handler corresponding to a button with a given ID or default value of if not found.
- ButtonHandler? GetOrDefault(ulong buttonId);
-}
-
-///
-public class ButtonHandlerRepository : IButtonHandlerRepository
-{
- private readonly IMemoryCache _cache;
-
- ///
- /// Sliding button handler cache expiration in minutes.
- ///
- private const int SlidingExpirationInMinutes = 5;
-
- ///
- /// Absolute button handler cache expiration in minutes.
- ///
- ///
- /// 15 minutes is how an interaction lasts by default in the Discord API.
- private const int AbsoluteExpirationInMinutes = 15;
-
- ///
- /// Instantiates a new instance of .
- ///
- /// The memory cache.
- public ButtonHandlerRepository(IMemoryCache cache)
- {
- _cache = cache;
- }
-
- ///
- public void Register(HandleableButton handleableButton) =>
- Register(handleableButton.Id.Value, handleableButton.Handler);
-
- ///
- public void Register(ulong buttonId, ButtonHandler handler) =>
- _cache.Set(buttonId, handler, new MemoryCacheEntryOptions
- {
- SlidingExpiration = TimeSpan.FromMinutes(SlidingExpirationInMinutes),
- AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(AbsoluteExpirationInMinutes)
- });
-
- ///
- public void Deregister(ulong buttonId) =>
- _cache.Remove(buttonId);
-
- ///
- public ButtonHandler? GetOrDefault(ulong buttonId) =>
- _cache.Get(buttonId);
-}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Caching/CachingSetup.cs b/src/Mmcc.Bot.Caching/CachingSetup.cs
deleted file mode 100644
index 99292c0..0000000
--- a/src/Mmcc.Bot.Caching/CachingSetup.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Mmcc.Bot.Caching;
-
-///
-/// Extension methods that register Mmcc.Bot.Caching with the service collection.
-///
-public static class CachingSetup
-{
- ///
- /// Registers Mmcc.Bot.Caching classes with the service collection.
- ///
- /// The .
- /// The .
- public static IServiceCollection AddMmccCaching(this IServiceCollection services) =>
- services.AddSingleton();
-}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Caching/Entities/ButtonHandler.cs b/src/Mmcc.Bot.Caching/Entities/ButtonHandler.cs
deleted file mode 100644
index c115a8d..0000000
--- a/src/Mmcc.Bot.Caching/Entities/ButtonHandler.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-using Remora.Discord.API.Abstractions.Objects;
-using Remora.Rest.Core;
-
-namespace Mmcc.Bot.Caching.Entities;
-
-public record ButtonHandler(
- Type HandlerCommandType,
- Type ContextType,
- object Context,
- Optional RequiredPermission = new()
-);
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Caching/Entities/HandleableButton.cs b/src/Mmcc.Bot.Caching/Entities/HandleableButton.cs
deleted file mode 100644
index 150d7c2..0000000
--- a/src/Mmcc.Bot.Caching/Entities/HandleableButton.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using MediatR;
-using Remora.Discord.API.Abstractions.Objects;
-using Remora.Rest.Core;
-using Remora.Results;
-
-namespace Mmcc.Bot.Caching.Entities;
-
-public record HandleableButton
-{
- public Snowflake Id { get; init; }
- public IButtonComponent Component { get; init; }
- public ButtonHandler Handler { get; init; }
-
- private HandleableButton(Snowflake id, IButtonComponent component, ButtonHandler handler)
- {
- Id = id;
- Component = component;
- Handler = handler;
- }
-
- public static HandleableButton Create(
- Snowflake id,
- IButtonComponent component,
- TContext context,
- Optional requiredPermission = new()
- )
- where THandlerCommand : IRequest
- where TContext : class
- => new(id, component, new(typeof(THandlerCommand), typeof(TContext), context, requiredPermission));
-
- public void Deconstruct(out Snowflake id, out IButtonComponent component, out ButtonHandler handler)
- {
- id = Id;
- component = Component;
- handler = Handler;
- }
-}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.Extensions/Caching/ButtonExtensions.cs b/src/Mmcc.Bot.Common.Extensions/Caching/ButtonExtensions.cs
deleted file mode 100644
index 869120d..0000000
--- a/src/Mmcc.Bot.Common.Extensions/Caching/ButtonExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Mmcc.Bot.Caching;
-using Mmcc.Bot.Caching.Entities;
-
-namespace Mmcc.Bot.Common.Extensions.Caching
-{
- public static class ButtonExtensions
- {
- public static HandleableButton RegisterWith(this HandleableButton handleableButton, IButtonHandlerRepository repository)
- {
- repository.Register(handleableButton);
- return handleableButton;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.Extensions/Database/Entities/MemberApplicationExtensions.cs b/src/Mmcc.Bot.Common.Extensions/Database/Entities/MemberApplicationExtensions.cs
deleted file mode 100644
index 62977bd..0000000
--- a/src/Mmcc.Bot.Common.Extensions/Database/Entities/MemberApplicationExtensions.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Mmcc.Bot.Common.Models.Colours;
-using Mmcc.Bot.Database.Entities;
-using Mmcc.Bot.RemoraAbstractions.Timestamps;
-using Remora.Discord.API.Objects;
-
-namespace Mmcc.Bot.Common.Extensions.Database.Entities
-{
- ///
- /// Extensions for .
- ///
- public static class MemberApplicationExtensions
- {
- ///
- /// Get an embed representation of a member application.
- ///
- /// Member application.
- /// Colour palette for the embed to use.
- /// Embed representing the member application.
- public static Embed GetEmbed(this MemberApplication memberApplication, IColourPalette colourPalette)
- {
- var statusStr = memberApplication.AppStatus.ToString();
- var embedConditionalAttributes = memberApplication.AppStatus switch
- {
- ApplicationStatus.Pending => new
- {
- Colour = colourPalette.Blue,
- StatusFieldValue = $":clock1: {statusStr}"
- },
- ApplicationStatus.Approved => new
- {
- Colour = colourPalette.Green,
- StatusFieldValue = $":white_check_mark: {statusStr}"
- },
- ApplicationStatus.Rejected => new
- {
- Colour = colourPalette.Red,
- StatusFieldValue = $":no_entry: {statusStr}"
- },
- _ => throw new ArgumentOutOfRangeException(nameof(memberApplication))
- };
- return new Embed
- {
- Title = $"Member Application #{memberApplication.MemberApplicationId}",
- Description =
- $"Submitted at {new DiscordTimestamp(memberApplication.AppTime).AsStyled(DiscordTimestampStyle.ShortDateTime)}.",
- Fields = new List
- {
- new("Author", $"{memberApplication.AuthorDiscordName} (ID: `{memberApplication.AuthorDiscordId}`)",
- false),
- new("Status", embedConditionalAttributes.StatusFieldValue, false),
- new(
- "Provided details",
- $"{memberApplication.MessageContent}\n" +
- $"**[Original message (click here)](https://discord.com/channels/{memberApplication.GuildId}/{memberApplication.ChannelId}/{memberApplication.MessageId})**",
- false
- )
- },
- Colour = embedConditionalAttributes.Colour,
- Thumbnail = new EmbedThumbnail(memberApplication.ImageUrl)
- };
- }
-
- ///
- /// Gets an enumerable of formatted embed fields that represent the member applications.
- ///
- /// Enumerable of member applications.
- /// Enumerable of formatted embed fields that represent the member applications.
- public static IEnumerable GetEmbedFields(this IEnumerable memberApplications) =>
- memberApplications.Select(app => new EmbedField
- (
- $"[{app.MemberApplicationId}] {app.AuthorDiscordName}",
- $"*Submitted at:* {new DiscordTimestamp(app.AppTime).AsStyled(DiscordTimestampStyle.ShortDateTime)}.",
- false
- ));
- }
-}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.Extensions/Database/Entities/ModerationActionExtensions.cs b/src/Mmcc.Bot.Common.Extensions/Database/Entities/ModerationActionExtensions.cs
index c26e845..d3c16dc 100644
--- a/src/Mmcc.Bot.Common.Extensions/Database/Entities/ModerationActionExtensions.cs
+++ b/src/Mmcc.Bot.Common.Extensions/Database/Entities/ModerationActionExtensions.cs
@@ -29,6 +29,22 @@ public static IEnumerable GetEmbedFields(this IEnumerable ma switch
+ {
+ { UserDiscordId: { } dId, UserIgn: { } ign } =>
+ $$"""
+ Discord user: <@{{dId}}>
+ IGN: `{{ign}}`
+ """,
+
+ { UserDiscordId: { } dId } => $"Discord user: <@{dId}>",
+
+ { UserIgn: { } ign } => $"IGN: `{ign}`",
+
+ _ => "No Discord ID/IGN data."
+ };
+
private static EmbedField GetEmbedFieldForActionsOfType(this IEnumerable moderationActions, ModerationActionType type, bool showAssociatedDiscord, bool showAssociatedIgn)
{
var list = moderationActions.Where(ma => ma.ModerationActionType == type).ToList();
diff --git a/src/Mmcc.Bot.Common.Extensions/FluentValidation/Results/ValidationFailuresExtensions.cs b/src/Mmcc.Bot.Common.Extensions/FluentValidation/Results/ValidationFailuresExtensions.cs
deleted file mode 100644
index 73851f6..0000000
--- a/src/Mmcc.Bot.Common.Extensions/FluentValidation/Results/ValidationFailuresExtensions.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using FluentValidation.Results;
-using Remora.Discord.API.Objects;
-
-namespace Mmcc.Bot.Common.Extensions.FluentValidation.Results
-{
- ///
- /// Extensions for .
- ///
- public static class ValidationFailuresExtensions
- {
- ///
- /// Gets containing details of failures.
- ///
- /// Validation failures.
- /// Whether the should be inline. Defaults to false.
- /// containing details of failures.
- public static EmbedField ToEmbedField(this IEnumerable validationFailures, bool inline = false)
- {
- var validationFailuresList = validationFailures.ToList();
-
- if (!validationFailuresList.Any())
- {
- return new("Failures", "No description.");
- }
-
- var descriptionSb = string.Join("\n",
- validationFailuresList
- .Select((vf, i) => $"{i + 1}) {vf.ToString().Replace('\'', '`')}"));
- return new("Reason(s)", descriptionSb, inline);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.Extensions/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs b/src/Mmcc.Bot.Common.Extensions/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs
index 7c2afcb..9806af0 100644
--- a/src/Mmcc.Bot.Common.Extensions/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs
+++ b/src/Mmcc.Bot.Common.Extensions/Microsoft/Extensions/DependencyInjection/ServiceCollectionExtensions.cs
@@ -1,4 +1,5 @@
-using FluentValidation;
+using System;
+using FluentValidation;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@@ -33,6 +34,11 @@ IConfigurationSection configurationSection
where TValidator : AbstractValidator, new()
{
var config = configurationSection.Get();
+ if (config is null)
+ {
+ throw new Exception($"Could not match a configuration section to {typeof(TConfig)}");
+ }
+
var validator = new TValidator();
validator.ValidateAndThrow(config);
diff --git a/src/Mmcc.Bot.Common.Extensions/Mmcc.Bot.Common.Extensions.csproj b/src/Mmcc.Bot.Common.Extensions/Mmcc.Bot.Common.Extensions.csproj
index 98bcc18..662737d 100644
--- a/src/Mmcc.Bot.Common.Extensions/Mmcc.Bot.Common.Extensions.csproj
+++ b/src/Mmcc.Bot.Common.Extensions/Mmcc.Bot.Common.Extensions.csproj
@@ -1,22 +1,20 @@
- net6.0
+ net7.0
enable
- 10
+ 11
-
-
-
-
+
+
diff --git a/src/Mmcc.Bot.Common.Extensions/Remora/Discord/API/Abstractions/Rest/DiscordRestGuildApiExtensions.cs b/src/Mmcc.Bot.Common.Extensions/Remora/Discord/API/Abstractions/Rest/DiscordRestGuildApiExtensions.cs
index c348bf3..e400161 100644
--- a/src/Mmcc.Bot.Common.Extensions/Remora/Discord/API/Abstractions/Rest/DiscordRestGuildApiExtensions.cs
+++ b/src/Mmcc.Bot.Common.Extensions/Remora/Discord/API/Abstractions/Rest/DiscordRestGuildApiExtensions.cs
@@ -33,7 +33,7 @@ public static async Task> FindGuildChannelByName(this IDiscordR
var guildChannels = getGuildChannelsResult.Entity;
var channel = guildChannels
.Where(c => c.Name.HasValue)
- .FirstOrDefault(c => c.Name.Value.Equals(channelName));
+ .FirstOrDefault(c => c.Name.Value!.Equals(channelName));
if (channel is null)
{
return new NotFoundError(
diff --git a/src/Mmcc.Bot.Common.Extensions/System/KeyValuePairDiscordExtensions.cs b/src/Mmcc.Bot.Common.Extensions/System/KeyValuePairDiscordExtensions.cs
new file mode 100644
index 0000000..1533c4b
--- /dev/null
+++ b/src/Mmcc.Bot.Common.Extensions/System/KeyValuePairDiscordExtensions.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+using System.Linq;
+using Remora.Discord.API.Abstractions.Objects;
+using Remora.Discord.API.Objects;
+
+namespace Mmcc.Bot.Common.Extensions.System;
+
+public static class KeyValuePairDiscordExtensions
+{
+ public static IEnumerable ToEmbedFields(this IEnumerable> kvEnumerable)
+ => kvEnumerable.Select(x => new EmbedField(x.Key, x.Value, false));
+
+ public static EmbedField ToEmbedField(this KeyValuePair kv)
+ => new(kv.Key, kv.Value, false);
+}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.Extensions/System/String.cs b/src/Mmcc.Bot.Common.Extensions/System/StringExtensions.cs
similarity index 69%
rename from src/Mmcc.Bot.Common.Extensions/System/String.cs
rename to src/Mmcc.Bot.Common.Extensions/System/StringExtensions.cs
index cbc0b17..3e0ea4a 100644
--- a/src/Mmcc.Bot.Common.Extensions/System/String.cs
+++ b/src/Mmcc.Bot.Common.Extensions/System/StringExtensions.cs
@@ -2,12 +2,14 @@
namespace Mmcc.Bot.Common.Extensions.System
{
- public static class String
+ public static class StringExtensions
{
public static string[] SplitByNewLine(this string s) =>
s.Split(
new[] {"\r\n", "\r", "\n"},
StringSplitOptions.None
);
+
+ public static string DoubleQuotes(this string s) => $"\"{s}\"";
}
}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.UI/Buttons/DonateButton.cs b/src/Mmcc.Bot.Common.UI/Buttons/DonateButton.cs
new file mode 100644
index 0000000..7e2087e
--- /dev/null
+++ b/src/Mmcc.Bot.Common.UI/Buttons/DonateButton.cs
@@ -0,0 +1,12 @@
+using Mmcc.Bot.Common.Statics;
+using Remora.Discord.API.Abstractions.Objects;
+using Remora.Discord.API.Objects;
+
+namespace Mmcc.Bot.Common.UI.Buttons;
+
+public record DonateButton() : ButtonComponent(
+ Style: ButtonComponentStyle.Link,
+ Label: "Donate",
+ Emoji: new PartialEmoji(Name: "❤️"),
+ URL: MmccUrls.Donations
+);
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.UI/Buttons/ForumButton.cs b/src/Mmcc.Bot.Common.UI/Buttons/ForumButton.cs
new file mode 100644
index 0000000..f1ea7cb
--- /dev/null
+++ b/src/Mmcc.Bot.Common.UI/Buttons/ForumButton.cs
@@ -0,0 +1,12 @@
+using Mmcc.Bot.Common.Statics;
+using Remora.Discord.API.Abstractions.Objects;
+using Remora.Discord.API.Objects;
+
+namespace Mmcc.Bot.Common.UI.Buttons;
+
+public record ForumButton() : ButtonComponent(
+ ButtonComponentStyle.Link,
+ "Forum",
+ new PartialEmoji(Name: "🗣️"),
+ URL: MmccUrls.Forum
+);
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.UI/Buttons/MmccGithubOrgButton.cs b/src/Mmcc.Bot.Common.UI/Buttons/MmccGithubOrgButton.cs
new file mode 100644
index 0000000..597d929
--- /dev/null
+++ b/src/Mmcc.Bot.Common.UI/Buttons/MmccGithubOrgButton.cs
@@ -0,0 +1,13 @@
+using Mmcc.Bot.Common.Statics;
+using Remora.Discord.API.Abstractions.Objects;
+using Remora.Discord.API.Objects;
+using Remora.Rest.Core;
+
+namespace Mmcc.Bot.Common.UI.Buttons;
+
+public record MmccGithubOrgButton() : ButtonComponent(
+ ButtonComponentStyle.Link,
+ "GitHub",
+ new PartialEmoji(new Snowflake(453413238638641163)),
+ URL: MmccUrls.GitHub
+);
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.UI/Buttons/MmccWebsiteButton.cs b/src/Mmcc.Bot.Common.UI/Buttons/MmccWebsiteButton.cs
new file mode 100644
index 0000000..af159f8
--- /dev/null
+++ b/src/Mmcc.Bot.Common.UI/Buttons/MmccWebsiteButton.cs
@@ -0,0 +1,13 @@
+using Mmcc.Bot.Common.Statics;
+using Remora.Discord.API.Abstractions.Objects;
+using Remora.Discord.API.Objects;
+using Remora.Rest.Core;
+
+namespace Mmcc.Bot.Common.UI.Buttons;
+
+public record MmccWebsiteButton() : ButtonComponent(
+ ButtonComponentStyle.Link,
+ "Website",
+ new PartialEmoji(new Snowflake(863798570602856469)),
+ URL: MmccUrls.Website
+);
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.UI/Buttons/WikiButton.cs b/src/Mmcc.Bot.Common.UI/Buttons/WikiButton.cs
new file mode 100644
index 0000000..77cdcc5
--- /dev/null
+++ b/src/Mmcc.Bot.Common.UI/Buttons/WikiButton.cs
@@ -0,0 +1,12 @@
+using Mmcc.Bot.Common.Statics;
+using Remora.Discord.API.Abstractions.Objects;
+using Remora.Discord.API.Objects;
+
+namespace Mmcc.Bot.Common.UI.Buttons;
+
+public record WikiButton() : ButtonComponent(
+ ButtonComponentStyle.Link,
+ "Wiki",
+ new PartialEmoji(Name: "📖"),
+ URL: MmccUrls.Wiki
+);
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.UI/Embeds/NotificationEmbed.cs b/src/Mmcc.Bot.Common.UI/Embeds/NotificationEmbed.cs
new file mode 100644
index 0000000..7f5a65e
--- /dev/null
+++ b/src/Mmcc.Bot.Common.UI/Embeds/NotificationEmbed.cs
@@ -0,0 +1,19 @@
+using Mmcc.Bot.Common.Extensions.System;
+using Mmcc.Bot.Common.Models;
+using Remora.Discord.API.Abstractions.Objects;
+using Remora.Discord.API.Objects;
+using Remora.Rest.Core;
+
+namespace Mmcc.Bot.Common.UI.Embeds;
+
+public record NotificationEmbed : Embed
+{
+ public NotificationEmbed(IMmccNotification context) : base(
+ Title: context.Title,
+ Description: context.Description ?? new Optional(),
+ Timestamp: context.Timestamp ?? new Optional(),
+ Fields: context.CustomProperties?.ToEmbedFields().ToList() ?? new Optional>()
+ )
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common.UI/Mmcc.Bot.Common.UI.csproj b/src/Mmcc.Bot.Common.UI/Mmcc.Bot.Common.UI.csproj
new file mode 100644
index 0000000..363e860
--- /dev/null
+++ b/src/Mmcc.Bot.Common.UI/Mmcc.Bot.Common.UI.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/src/Mmcc.Bot.Common/Errors/InteractionExpiredError.cs b/src/Mmcc.Bot.Common/Errors/InteractionExpiredError.cs
new file mode 100644
index 0000000..553cb23
--- /dev/null
+++ b/src/Mmcc.Bot.Common/Errors/InteractionExpiredError.cs
@@ -0,0 +1,9 @@
+using Remora.Commands.Trees.Nodes;
+using Remora.Results;
+
+namespace Mmcc.Bot.Common.Errors;
+
+///
+/// Represents a failure resulting from an interaction having expired in the corresponding store.
+///
+public record InteractionExpiredError(string Message, IChildNode? Node = default) : ResultError(Message);
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common/ExcludeFromMediatrAssemblyScanAttribute.cs b/src/Mmcc.Bot.Common/ExcludeFromMediatrAssemblyScanAttribute.cs
new file mode 100644
index 0000000..89a3b44
--- /dev/null
+++ b/src/Mmcc.Bot.Common/ExcludeFromMediatrAssemblyScanAttribute.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace Mmcc.Bot.Common;
+
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
+public class ExcludeFromMediatrAssemblyScanAttribute : Attribute
+{
+}
diff --git a/src/Mmcc.Bot.Common/Mmcc.Bot.Common.csproj b/src/Mmcc.Bot.Common/Mmcc.Bot.Common.csproj
index c8d753c..2d0da02 100644
--- a/src/Mmcc.Bot.Common/Mmcc.Bot.Common.csproj
+++ b/src/Mmcc.Bot.Common/Mmcc.Bot.Common.csproj
@@ -1,15 +1,20 @@
- net6.0
+ net7.0
enable
- 10
+ 11
-
-
-
+
+
+
+
+
+
+
+
diff --git a/src/Mmcc.Bot.Common/Models/Colours/ColourPalette.cs b/src/Mmcc.Bot.Common/Models/Colours/ColourPalette.cs
new file mode 100644
index 0000000..448a4fb
--- /dev/null
+++ b/src/Mmcc.Bot.Common/Models/Colours/ColourPalette.cs
@@ -0,0 +1,17 @@
+using System.Drawing;
+
+namespace Mmcc.Bot.Common.Models.Colours;
+
+// TODO: Remove all usages of IColourPalette;
+public static class ColourPalette
+{
+ public static Color Black => ColorTranslator.FromHtml("#262626");
+ public static Color Gray => ColorTranslator.FromHtml("#6B7280");
+ public static Color Red => ColorTranslator.FromHtml("#EF4444");
+ public static Color Yellow => ColorTranslator.FromHtml("#F59E0B");
+ public static Color Green => ColorTranslator.FromHtml("#10B981");
+ public static Color Blue => ColorTranslator.FromHtml("#3B82F6");
+ public static Color Indigo => ColorTranslator.FromHtml("#6366F1");
+ public static Color Purple => ColorTranslator.FromHtml("#8B5CF6");
+ public static Color Pink => ColorTranslator.FromHtml("#EC4899");
+}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common/Models/Colours/IColourPalette.cs b/src/Mmcc.Bot.Common/Models/Colours/IColourPalette.cs
index 11e5427..fa94cee 100644
--- a/src/Mmcc.Bot.Common/Models/Colours/IColourPalette.cs
+++ b/src/Mmcc.Bot.Common/Models/Colours/IColourPalette.cs
@@ -2,6 +2,8 @@
namespace Mmcc.Bot.Common.Models.Colours
{
+ // TODO: REMOVE THIS CLASS;
+
///
/// Colour palette for embeds;
///
@@ -17,4 +19,17 @@ public interface IColourPalette
public Color Purple { get; }
public Color Pink { get; }
}
+
+ public class TailwindColourPalette : IColourPalette
+ {
+ public Color Black => ColorTranslator.FromHtml("#262626");
+ public Color Gray => ColorTranslator.FromHtml("#6B7280");
+ public Color Red => ColorTranslator.FromHtml("#EF4444");
+ public Color Yellow => ColorTranslator.FromHtml("#F59E0B");
+ public Color Green => ColorTranslator.FromHtml("#10B981");
+ public Color Blue => ColorTranslator.FromHtml("#3B82F6");
+ public Color Indigo => ColorTranslator.FromHtml("#6366F1");
+ public Color Purple => ColorTranslator.FromHtml("#8B5CF6");
+ public Color Pink => ColorTranslator.FromHtml("#EC4899");
+ }
}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common/Models/Colours/TailwindColourPalette.cs b/src/Mmcc.Bot.Common/Models/Colours/TailwindColourPalette.cs
deleted file mode 100644
index baf2a5c..0000000
--- a/src/Mmcc.Bot.Common/Models/Colours/TailwindColourPalette.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Drawing;
-
-namespace Mmcc.Bot.Common.Models.Colours
-{
- ///
- public class TailwindColourPalette : IColourPalette
- {
- public Color Black => ColorTranslator.FromHtml("#262626");
- public Color Gray => ColorTranslator.FromHtml("#6B7280");
- public Color Red => ColorTranslator.FromHtml("#EF4444");
- public Color Yellow => ColorTranslator.FromHtml("#F59E0B");
- public Color Green => ColorTranslator.FromHtml("#10B981");
- public Color Blue => ColorTranslator.FromHtml("#3B82F6");
- public Color Indigo => ColorTranslator.FromHtml("#6366F1");
- public Color Purple => ColorTranslator.FromHtml("#8B5CF6");
- public Color Pink => ColorTranslator.FromHtml("#EC4899");
- }
-}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common/Models/IDiscordNotifiable.cs b/src/Mmcc.Bot.Common/Models/IDiscordNotifiable.cs
new file mode 100644
index 0000000..d72cccf
--- /dev/null
+++ b/src/Mmcc.Bot.Common/Models/IDiscordNotifiable.cs
@@ -0,0 +1,8 @@
+using Remora.Rest.Core;
+
+namespace Mmcc.Bot.Common.Models;
+
+public interface IDiscordNotifiable
+{
+ public Snowflake TargetGuildId { get; }
+}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Common/Models/IMmccNotification.cs b/src/Mmcc.Bot.Common/Models/IMmccNotification.cs
new file mode 100644
index 0000000..aca2049
--- /dev/null
+++ b/src/Mmcc.Bot.Common/Models/IMmccNotification.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using MediatR;
+
+namespace Mmcc.Bot.Common.Models;
+
+public interface IMmccNotification : INotification
+{
+ public string Title { get; }
+ public string? Description { get; }
+ public DateTimeOffset? Timestamp { get; }
+ IReadOnlyList>? CustomProperties { get; }
+}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Database/DesignTimeBotContextFactory.cs b/src/Mmcc.Bot.Database/DesignTimeBotContextFactory.cs
index ef7fab6..3013698 100644
--- a/src/Mmcc.Bot.Database/DesignTimeBotContextFactory.cs
+++ b/src/Mmcc.Bot.Database/DesignTimeBotContextFactory.cs
@@ -21,7 +21,7 @@ public BotContext CreateDbContext(string[] args)
var optionsBuilder = new DbContextOptionsBuilder();
var boundConfig = config.GetSection("MySql").Get();
var connString =
- $"Server={boundConfig.ServerIp};Port={boundConfig.Port};Database={boundConfig.DatabaseName};Uid={boundConfig.Username};Pwd={boundConfig.Password};Allow User Variables=True";
+ $"Server={boundConfig!.ServerIp};Port={boundConfig.Port};Database={boundConfig.DatabaseName};Uid={boundConfig.Username};Pwd={boundConfig.Password};Allow User Variables=True";
optionsBuilder.UseMySql(
connString,
ServerVersion.Parse("10.4.11-mariadb"),
diff --git a/src/Mmcc.Bot.Database/Mmcc.Bot.Database.csproj b/src/Mmcc.Bot.Database/Mmcc.Bot.Database.csproj
index 0bcd71e..eb2a63e 100644
--- a/src/Mmcc.Bot.Database/Mmcc.Bot.Database.csproj
+++ b/src/Mmcc.Bot.Database/Mmcc.Bot.Database.csproj
@@ -1,22 +1,21 @@
- net6.0
+ net7.0
enable
- 10
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
+
+
+
+
+
diff --git a/src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.PolychatRequestResolverGenerator/Mmcc.Bot.Generators.PolychatRequestResolverGenerator.csproj b/src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.PolychatRequestResolverGenerator/Mmcc.Bot.Generators.PolychatRequestResolverGenerator.csproj
new file mode 100644
index 0000000..e4fac13
--- /dev/null
+++ b/src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.PolychatRequestResolverGenerator/Mmcc.Bot.Generators.PolychatRequestResolverGenerator.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netstandard2.0
+ enable
+ enable
+ 11
+
+
+
+
+
+
diff --git a/src/Mmcc.Bot.Generators/RequestResolverGenerator.cs b/src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.PolychatRequestResolverGenerator/RequestResolverGenerator.cs
similarity index 82%
rename from src/Mmcc.Bot.Generators/RequestResolverGenerator.cs
rename to src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.PolychatRequestResolverGenerator/RequestResolverGenerator.cs
index 98e70e3..d6c8f4a 100644
--- a/src/Mmcc.Bot.Generators/RequestResolverGenerator.cs
+++ b/src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.PolychatRequestResolverGenerator/RequestResolverGenerator.cs
@@ -1,10 +1,8 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-namespace Mmcc.Bot.Generators
+namespace Mmcc.Bot.Generators.PolychatRequestResolverGenerator
{
[Generator]
public class RequestResolverGenerator : ISourceGenerator
@@ -79,20 +77,21 @@ List polychatMessageClasses
private string AnnotateMsgType(string type) => $"global::{MSG_NAMESPACE}.{type}";
- protected override string FillInStub(string generatedFiller)
- => $@"// auto-generated
-namespace {GENERATED_NAMESPACE};
-
-#pragma warning disable CS0612 // type is obsolete
-public partial class {GENERATED_CLASS}
-{{
- public {AnnotateTypeWithGlobal(_polychatRequestInterfaceSymbol)}? Resolve()
- {{
-{string.Join("\n", generatedFiller.Split('\n').Select(s => Indent(s, 2)))}
- }}
-}}
-#pragma warning restore CS0612
-";
+ protected override string FillInStub(string generatedFiller) =>
+ $$"""
+ // auto-generated
+ namespace {{GENERATED_NAMESPACE}};
+
+ #pragma warning disable CS0612 // type is obsolete
+ public partial class {{GENERATED_CLASS}}
+ {
+ public {{AnnotateTypeWithGlobal(_polychatRequestInterfaceSymbol)}}? Resolve()
+ {
+ {{string.Join("\n", generatedFiller.Split('\n').Select(s => Indent(s, 2)))}}
+ }
+ }
+ #pragma warning restore CS0612
+ """;
protected override string GenerateFiller()
{
@@ -115,13 +114,13 @@ protected override string GenerateFiller()
return sb.ToString();
}
- private static string GenerateNoClassesStub()
- => new StringBuilder()
- .AppendLine("// no Polychat classes found;")
- .AppendLine("// once classes are added to the Polychat project generated code will go here;")
- .AppendLine()
- .AppendLine("return null;")
- .ToString();
+ private static string GenerateNoClassesStub() =>
+ """
+ // no Polychat classes found;
+ // once classes are added to the Polychat project generated code will go here;
+
+ return null;
+ """;
private string GenerateIfForMsgClass(string messageType)
=> new StringBuilder()
diff --git a/src/Mmcc.Bot.Generators/TemplateGeneratorBase.cs b/src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.PolychatRequestResolverGenerator/TemplateGeneratorBase.cs
similarity index 95%
rename from src/Mmcc.Bot.Generators/TemplateGeneratorBase.cs
rename to src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.PolychatRequestResolverGenerator/TemplateGeneratorBase.cs
index 4761ff5..ab65194 100644
--- a/src/Mmcc.Bot.Generators/TemplateGeneratorBase.cs
+++ b/src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.PolychatRequestResolverGenerator/TemplateGeneratorBase.cs
@@ -1,7 +1,6 @@
-using System.Linq;
-using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis;
-namespace Mmcc.Bot.Generators
+namespace Mmcc.Bot.Generators.PolychatRequestResolverGenerator
{
///
/// Represents a base class to be inherited by template generators.
diff --git a/src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.csproj b/src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.csproj
deleted file mode 100644
index 2449501..0000000
--- a/src/Mmcc.Bot.Generators/Mmcc.Bot.Generators.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- netstandard2.0
- 9.0
- enable
-
-
-
-
-
-
diff --git a/src/Mmcc.Bot.Caching/Mmcc.Bot.Caching.csproj b/src/Mmcc.Bot.InMemoryStore/Mmcc.Bot.InMemoryStore.csproj
similarity index 58%
rename from src/Mmcc.Bot.Caching/Mmcc.Bot.Caching.csproj
rename to src/Mmcc.Bot.InMemoryStore/Mmcc.Bot.InMemoryStore.csproj
index 4227cc0..9702445 100644
--- a/src/Mmcc.Bot.Caching/Mmcc.Bot.Caching.csproj
+++ b/src/Mmcc.Bot.InMemoryStore/Mmcc.Bot.InMemoryStore.csproj
@@ -2,14 +2,13 @@
net6.0
+ enable
enable
- 10
+ 11
-
-
-
+
diff --git a/src/Mmcc.Bot.InMemoryStore/Stores/MessageMemberAppContextStore.cs b/src/Mmcc.Bot.InMemoryStore/Stores/MessageMemberAppContextStore.cs
new file mode 100644
index 0000000..a9c42df
--- /dev/null
+++ b/src/Mmcc.Bot.InMemoryStore/Stores/MessageMemberAppContextStore.cs
@@ -0,0 +1,34 @@
+using Microsoft.Extensions.Caching.Memory;
+
+namespace Mmcc.Bot.InMemoryStore.Stores;
+
+public interface IMessageMemberAppContextStore
+{
+ void Add(ulong messageId, int memberAppId);
+ void Remove(ulong messageId);
+ int? GetOrDefault(ulong key);
+}
+
+public class MessageMemberAppContextStore : IMessageMemberAppContextStore
+{
+ private readonly IMemoryCache _memCache;
+
+ private readonly TimeSpan _slidingExpiration = TimeSpan.FromMinutes(15);
+ public readonly TimeSpan _absoluteExpiration = TimeSpan.FromHours(1);
+
+ public MessageMemberAppContextStore(IMemoryCache memCache)
+ => _memCache = memCache;
+
+ public void Add(ulong messageId, int memberAppId)
+ => _memCache.Set(messageId, memberAppId, new MemoryCacheEntryOptions
+ {
+ SlidingExpiration = _slidingExpiration,
+ AbsoluteExpirationRelativeToNow = _absoluteExpiration
+ });
+
+ public void Remove(ulong messageId)
+ => _memCache.Remove(messageId);
+
+ public int? GetOrDefault(ulong key)
+ => _memCache.Get(key);
+}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.InMemoryStore/Stores/StoresSetup.cs b/src/Mmcc.Bot.InMemoryStore/Stores/StoresSetup.cs
new file mode 100644
index 0000000..908265c
--- /dev/null
+++ b/src/Mmcc.Bot.InMemoryStore/Stores/StoresSetup.cs
@@ -0,0 +1,17 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Mmcc.Bot.InMemoryStore.Stores;
+
+///
+/// Extension methods that register stores in Mmcc.Bot.InMemoryStore.Stores with the service collection.
+///
+public static class StoresSetup
+{
+ ///
+ /// Registers stores in Mmcc.Bot.InMemoryStore.Store classes with the service collection.
+ ///
+ /// The .
+ /// The .
+ public static IServiceCollection AddInMemoryStores(this IServiceCollection services) =>
+ services.AddSingleton();
+}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.Mojang/Mmcc.Bot.Mojang.csproj b/src/Mmcc.Bot.Mojang/Mmcc.Bot.Mojang.csproj
index 587c0bb..658e8c5 100644
--- a/src/Mmcc.Bot.Mojang/Mmcc.Bot.Mojang.csproj
+++ b/src/Mmcc.Bot.Mojang/Mmcc.Bot.Mojang.csproj
@@ -1,14 +1,13 @@
- net6.0
+ net7.0
enable
- 10
-
-
+
+
diff --git a/src/Mmcc.Bot.Polychat/Mmcc.Bot.Polychat.csproj b/src/Mmcc.Bot.Polychat/Mmcc.Bot.Polychat.csproj
index fa074f4..63a278a 100644
--- a/src/Mmcc.Bot.Polychat/Mmcc.Bot.Polychat.csproj
+++ b/src/Mmcc.Bot.Polychat/Mmcc.Bot.Polychat.csproj
@@ -1,26 +1,25 @@
- net6.0
+ net7.0
enable
- 10
-
-
-
-
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+
-
+
@@ -30,7 +29,10 @@
-
+
diff --git a/src/Mmcc.Bot.Polychat/Services/DiscordSanitiserService.cs b/src/Mmcc.Bot.Polychat/Services/DiscordSanitiserService.cs
index 754f5bd..c11c3d7 100644
--- a/src/Mmcc.Bot.Polychat/Services/DiscordSanitiserService.cs
+++ b/src/Mmcc.Bot.Polychat/Services/DiscordSanitiserService.cs
@@ -3,6 +3,7 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Mmcc.Bot.Polychat.Abstractions;
+using Remora.Discord.API.Abstractions.Gateway.Events;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.API.Abstractions.Rest;
using Remora.Rest.Core;
@@ -64,10 +65,14 @@ public DiscordSanitiserService(IDiscordRestGuildAPI guildApi)
public async Task SanitiseMessageContent(IMessage message)
{
var s = message.Content;
-
- s = SanitiseUsernameAndNicknameMentions(s, message.Mentions);
- s = await SanitiseChannelMentions(s, message.GuildID);
- s = await SanitiseRoleMentions(s, message.GuildID);
+
+ if (message is IMessageCreate msgCreateEv)
+ {
+ s = SanitiseUsernameAndNicknameMentions(s, msgCreateEv.Mentions);
+ s = await SanitiseChannelMentions(s, msgCreateEv.GuildID);
+ s = await SanitiseRoleMentions(s, msgCreateEv.GuildID);
+ }
+
s = SanitiseStandardEmoji(s);
s = SanitiseCustomEmoji(s);
s = s.Replace("️", " ");
diff --git a/src/Mmcc.Bot.RemoraAbstractions/AbstractionsSetup.cs b/src/Mmcc.Bot.RemoraAbstractions/AbstractionsSetup.cs
index 7c99c2a..2b2fbd1 100644
--- a/src/Mmcc.Bot.RemoraAbstractions/AbstractionsSetup.cs
+++ b/src/Mmcc.Bot.RemoraAbstractions/AbstractionsSetup.cs
@@ -1,7 +1,9 @@
using Microsoft.Extensions.DependencyInjection;
-using Mmcc.Bot.RemoraAbstractions.Conditions;
+using Mmcc.Bot.RemoraAbstractions.Conditions.CommandSpecific;
+using Mmcc.Bot.RemoraAbstractions.Conditions.InteractionSpecific;
using Mmcc.Bot.RemoraAbstractions.Parsers;
using Mmcc.Bot.RemoraAbstractions.Services;
+using Mmcc.Bot.RemoraAbstractions.Services.MessageResponders;
using Remora.Commands.Extensions;
namespace Mmcc.Bot.RemoraAbstractions;
@@ -21,12 +23,19 @@ public static IServiceCollection AddRemoraAbstractions(this IServiceCollection s
services.AddScoped();
services.AddScoped();
services.AddScoped();
- services.AddScoped();
- services.AddScoped();
-
+ services.AddScoped();
+ services.AddScoped();
+
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
services.AddCondition();
services.AddCondition();
+ services.AddCondition();
+ services.AddCondition();
+
services.AddParser();
services.AddParser();
diff --git a/src/Mmcc.Bot.RemoraAbstractions/CommandTreeWalker.cs b/src/Mmcc.Bot.RemoraAbstractions/CommandTreeWalker.cs
new file mode 100644
index 0000000..802f163
--- /dev/null
+++ b/src/Mmcc.Bot.RemoraAbstractions/CommandTreeWalker.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Remora.Commands.Trees;
+using Remora.Commands.Trees.Nodes;
+
+namespace Mmcc.Bot.RemoraAbstractions;
+
+public class CommandTreeWalker
+{
+ private readonly CommandTree _commandTree;
+
+ public CommandTreeWalker(CommandTree commandTree)
+ => _commandTree = commandTree;
+
+ public void PreOrderTraverseParentNodes(Action onParentNode)
+ => PreOrderTraverseParentNodes(_commandTree.Root, onParentNode);
+
+ public GroupNode? GetGroupNodeByPath(List path)
+ {
+ var root = _commandTree.Root;
+
+ var currPathIndex = 0;
+ var children = root.Children.OfType().ToList();
+ while (true)
+ {
+ var matchedChild = children.FirstOrDefault(c => c.Key.Equals(path[currPathIndex]));
+
+ if (matchedChild is null)
+ return null;
+
+ if (currPathIndex == path.Count - 1)
+ return matchedChild;
+
+ currPathIndex++;
+ children = matchedChild.Children.OfType().ToList();
+ }
+ }
+
+ public List CollectPath(GroupNode node)
+ {
+ IEnumerable res = new List { node.Key };
+ var parent = node.Parent;
+ while (parent is GroupNode groupNode)
+ {
+ res = res.Prepend(groupNode.Key);
+ parent = groupNode.Parent;
+ }
+
+ return res.ToList();
+ }
+
+ private void PreOrderTraverseParentNodes(IParentNode parentNode, Action onNode)
+ {
+ onNode(parentNode);
+
+ foreach (var childNode in parentNode.Children.OfType())
+ {
+ PreOrderTraverseParentNodes(childNode, onNode);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.RemoraAbstractions/Conditions/Attributes/RequireGuildAttribute.cs b/src/Mmcc.Bot.RemoraAbstractions/Conditions/Attributes/RequireGuildAttribute.cs
deleted file mode 100644
index 6dd643e..0000000
--- a/src/Mmcc.Bot.RemoraAbstractions/Conditions/Attributes/RequireGuildAttribute.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Remora.Commands.Conditions;
-
-namespace Mmcc.Bot.RemoraAbstractions.Conditions.Attributes;
-
-///
-/// Marks a command as requiring to be executed within a guild.
-///
-public class RequireGuildAttribute : ConditionAttribute
-{
-}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.RemoraAbstractions/Conditions/Attributes/RequireUserGuildPermissionAttribute.cs b/src/Mmcc.Bot.RemoraAbstractions/Conditions/Attributes/RequireUserGuildPermissionAttribute.cs
deleted file mode 100644
index 6ae8828..0000000
--- a/src/Mmcc.Bot.RemoraAbstractions/Conditions/Attributes/RequireUserGuildPermissionAttribute.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Remora.Commands.Conditions;
-using Remora.Discord.API.Abstractions.Objects;
-
-namespace Mmcc.Bot.RemoraAbstractions.Conditions.Attributes;
-
-///
-/// Marks a command as requiring the requesting user to have a particular permission within the guild.
-///
-public class RequireUserGuildPermissionAttribute : ConditionAttribute
-{
- ///
- /// Gets the permission.
- ///
- public DiscordPermission Permission { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The permission.
- public RequireUserGuildPermissionAttribute(DiscordPermission permission)
- {
- Permission = permission;
- }
-}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.RemoraAbstractions/Conditions/RequireGuildCondition.cs b/src/Mmcc.Bot.RemoraAbstractions/Conditions/CommandSpecific/RequireGuildCondition.cs
similarity index 66%
rename from src/Mmcc.Bot.RemoraAbstractions/Conditions/RequireGuildCondition.cs
rename to src/Mmcc.Bot.RemoraAbstractions/Conditions/CommandSpecific/RequireGuildCondition.cs
index 65c4a55..32d317d 100644
--- a/src/Mmcc.Bot.RemoraAbstractions/Conditions/RequireGuildCondition.cs
+++ b/src/Mmcc.Bot.RemoraAbstractions/Conditions/CommandSpecific/RequireGuildCondition.cs
@@ -1,12 +1,17 @@
using System.Threading;
using System.Threading.Tasks;
-using Mmcc.Bot.RemoraAbstractions.Conditions.Attributes;
using Remora.Commands.Conditions;
-using Remora.Commands.Results;
using Remora.Discord.Commands.Contexts;
using Remora.Results;
-namespace Mmcc.Bot.RemoraAbstractions.Conditions;
+namespace Mmcc.Bot.RemoraAbstractions.Conditions.CommandSpecific;
+
+///
+/// Marks a command as requiring to be executed within a guild.
+///
+public class RequireGuildAttribute : ConditionAttribute
+{
+}
///
/// Checks if the command was executed within a guild before allowing execution.
@@ -14,23 +19,20 @@ namespace Mmcc.Bot.RemoraAbstractions.Conditions;
public class RequireGuildCondition : ICondition
{
private readonly MessageContext _context;
-
+
///
/// Instantiates a new instance of the class.
///
/// The message context.
public RequireGuildCondition(MessageContext context)
- {
- _context = context;
- }
-
+ => _context = context;
+
///
public ValueTask CheckAsync(RequireGuildAttribute attribute, CancellationToken ct)
{
- var guild = _context.Message.GuildID;
+ var guild = _context.GuildID;
return new(!guild.HasValue
- ? new ConditionNotSatisfiedError(
- "Command that requires to be executed within a guild was executed outside of one")
+ ? new InvalidOperationError("Command that requires to be executed within a guild was executed outside of one")
: Result.FromSuccess());
}
}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.RemoraAbstractions/Conditions/RequireUserGuildPermissionCondition.cs b/src/Mmcc.Bot.RemoraAbstractions/Conditions/CommandSpecific/RequireUserGuildPermissionCondition.cs
similarity index 59%
rename from src/Mmcc.Bot.RemoraAbstractions/Conditions/RequireUserGuildPermissionCondition.cs
rename to src/Mmcc.Bot.RemoraAbstractions/Conditions/CommandSpecific/RequireUserGuildPermissionCondition.cs
index bf6cff4..cdc5858 100644
--- a/src/Mmcc.Bot.RemoraAbstractions/Conditions/RequireUserGuildPermissionCondition.cs
+++ b/src/Mmcc.Bot.RemoraAbstractions/Conditions/CommandSpecific/RequireUserGuildPermissionCondition.cs
@@ -1,12 +1,32 @@
using System.Threading;
using System.Threading.Tasks;
-using Mmcc.Bot.RemoraAbstractions.Conditions.Attributes;
using Mmcc.Bot.RemoraAbstractions.Services;
using Remora.Commands.Conditions;
+using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.Commands.Contexts;
using Remora.Results;
-namespace Mmcc.Bot.RemoraAbstractions.Conditions;
+namespace Mmcc.Bot.RemoraAbstractions.Conditions.CommandSpecific;
+
+///
+/// Marks a command as requiring the requesting user to have a particular permission within the guild.
+///
+public class RequireUserGuildPermissionAttribute : ConditionAttribute
+{
+ ///
+ /// Gets the permission.
+ ///
+ public DiscordPermission Permission { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The permission.
+ public RequireUserGuildPermissionAttribute(DiscordPermission permission)
+ {
+ Permission = permission;
+ }
+}
///
/// Checks required Guild permissions before allowing execution.
@@ -30,8 +50,8 @@ public RequireUserGuildPermissionCondition(MessageContext context, IDiscordPermi
}
///
- public async ValueTask CheckAsync(RequireUserGuildPermissionAttribute attribute, CancellationToken ct) =>
- await _permissionsService.CheckHasRequiredPermission(
+ public async ValueTask CheckAsync(RequireUserGuildPermissionAttribute attribute, CancellationToken ct)
+ => await _permissionsService.CheckHasRequiredPermission(
attribute.Permission,
_context.ChannelID,
_context.User, ct
diff --git a/src/Mmcc.Bot.RemoraAbstractions/Conditions/InteractionSpecific/InteractionRequireGuildCondition.cs b/src/Mmcc.Bot.RemoraAbstractions/Conditions/InteractionSpecific/InteractionRequireGuildCondition.cs
new file mode 100644
index 0000000..d98993d
--- /dev/null
+++ b/src/Mmcc.Bot.RemoraAbstractions/Conditions/InteractionSpecific/InteractionRequireGuildCondition.cs
@@ -0,0 +1,27 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Remora.Commands.Conditions;
+using Remora.Discord.Commands.Contexts;
+using Remora.Results;
+
+namespace Mmcc.Bot.RemoraAbstractions.Conditions.InteractionSpecific;
+
+public class InteractionRequireGuildAttribute : ConditionAttribute
+{
+}
+
+public class InteractionRequireGuildCondition : ICondition
+{
+ private readonly InteractionContext _context;
+
+ public InteractionRequireGuildCondition(InteractionContext context)
+ => _context = context;
+
+ public ValueTask CheckAsync(InteractionRequireGuildAttribute attribute, CancellationToken ct = new CancellationToken())
+ {
+ var guild = _context.GuildID;
+ return new(!guild.HasValue
+ ? new InvalidOperationError("Command that requires to be executed within a guild was executed outside of one")
+ : Result.FromSuccess());
+ }
+}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.RemoraAbstractions/Conditions/InteractionSpecific/InteractionRequireUserGuildPermission.cs b/src/Mmcc.Bot.RemoraAbstractions/Conditions/InteractionSpecific/InteractionRequireUserGuildPermission.cs
new file mode 100644
index 0000000..3947bb4
--- /dev/null
+++ b/src/Mmcc.Bot.RemoraAbstractions/Conditions/InteractionSpecific/InteractionRequireUserGuildPermission.cs
@@ -0,0 +1,45 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Mmcc.Bot.RemoraAbstractions.Conditions.CommandSpecific;
+using Mmcc.Bot.RemoraAbstractions.Services;
+using Remora.Commands.Conditions;
+using Remora.Discord.API.Abstractions.Objects;
+using Remora.Discord.Commands.Contexts;
+using Remora.Results;
+
+namespace Mmcc.Bot.RemoraAbstractions.Conditions.InteractionSpecific;
+
+public class InteractionRequireUserGuildPermissionAttribute : ConditionAttribute
+{
+ ///
+ /// Gets the permission.
+ ///
+ public DiscordPermission Permission { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The permission.
+ public InteractionRequireUserGuildPermissionAttribute(DiscordPermission permission)
+ => Permission = permission;
+}
+
+public class InteractionRequireUserGuildPermissionCondition : ICondition
+{
+ private readonly InteractionContext _context;
+ private readonly IDiscordPermissionsService _permissionsService;
+
+
+ public InteractionRequireUserGuildPermissionCondition(InteractionContext context, IDiscordPermissionsService permissionsService)
+ {
+ _context = context;
+ _permissionsService = permissionsService;
+ }
+
+ public async ValueTask CheckAsync(InteractionRequireUserGuildPermissionAttribute attribute, CancellationToken ct)
+ => await _permissionsService.CheckHasRequiredPermission(
+ attribute.Permission,
+ _context.ChannelID,
+ _context.User, ct
+ );
+}
\ No newline at end of file
diff --git a/src/Mmcc.Bot.RemoraAbstractions/Mmcc.Bot.RemoraAbstractions.csproj b/src/Mmcc.Bot.RemoraAbstractions/Mmcc.Bot.RemoraAbstractions.csproj
index a06779b..13c2ac8 100644
--- a/src/Mmcc.Bot.RemoraAbstractions/Mmcc.Bot.RemoraAbstractions.csproj
+++ b/src/Mmcc.Bot.RemoraAbstractions/Mmcc.Bot.RemoraAbstractions.csproj
@@ -1,17 +1,17 @@
- net6.0
+ net7.0
enable
- 10
-
+
+
-
+
diff --git a/src/Mmcc.Bot.RemoraAbstractions/Services/CommandResponder.cs b/src/Mmcc.Bot.RemoraAbstractions/Services/CommandResponder.cs
deleted file mode 100644
index dc282a9..0000000
--- a/src/Mmcc.Bot.RemoraAbstractions/Services/CommandResponder.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Remora.Discord.API.Abstractions.Objects;
-using Remora.Discord.API.Abstractions.Rest;
-using Remora.Discord.API.Objects;
-using Remora.Discord.Commands.Contexts;
-using Remora.Rest.Core;
-using Remora.Results;
-
-namespace Mmcc.Bot.RemoraAbstractions.Services;
-
-///
-/// Responds to the message the command was invoked by.
-///
-public interface ICommandResponder
-{
- ///
- /// Responds to the message the command was invoked by with a message with content.
- ///
- /// Content of the response.
- /// Result of the asynchronous operation.
- Task Respond(string message);
-
- ///
- /// Responds to the message the command was invoked by with a message with s content.
- ///
- /// Content of the response.
- /// Result of the asynchronous operation.
- Task Respond(params Embed[] embeds);
-
- ///
- /// Responds to the message the command was invoked by with a message with s content.
- ///
- /// Content of the response.
- /// Result of the asynchronous operation.
- Task Respond(List