diff --git a/DisCatSharp/Clients/DiscordClient.InteractionEventHandlers.cs b/DisCatSharp/Clients/DiscordClient.InteractionEventHandlers.cs
index 497a1c603..301c755ad 100644
--- a/DisCatSharp/Clients/DiscordClient.InteractionEventHandlers.cs
+++ b/DisCatSharp/Clients/DiscordClient.InteractionEventHandlers.cs
@@ -1,226 +1,239 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2023 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
namespace DisCatSharp;
///
/// A Discord API wrapper.
///
public sealed partial class DiscordClient
{
private readonly Dictionary<(object?, Type, bool), List<(EventInfo, Delegate, bool)[]>> _registrationInteractionToDelegate = new();
private readonly Dictionary> _typeInteractionToAnonymousHandlers = new();
///
/// Registers all methods annotated with from the given object.
///
/// The event handler object.
/// Whether to consider static methods.
public void RegisterInteractionHandler(object handler, bool registerStatic = false)
=> this.RegisterInteractionHandlerImpl(handler, handler.GetType(), registerStatic);
///
/// Registers all static methods annotated with from the given type.
///
/// The static event handler type.
public void RegisterStaticInteractionHandler(Type t)
=> this.RegisterInteractionHandlerImpl(null, t);
///
/// .
///
/// Type to register.
public void RegisterStaticInteractionHandler()
=> this.RegisterStaticInteractionHandler(typeof(T));
///
/// If abstract, registers all static methods of the type.
/// If non-abstract, tries to instantiate it, optionally using the provided
/// and registers all instance and static methods.
///
/// Type to register.
public void RegisterInteractionHandler(Type type)
{
if (type.IsAbstract)
this.RegisterStaticInteractionHandler(type);
else
{
var anon = ActivatorUtilities.CreateInstance(this.Configuration.ServiceProvider, type);
this._typeInteractionToAnonymousHandlers[type] = this._typeInteractionToAnonymousHandlers.TryGetValue(type, out var anonObjs) ? anonObjs : (anonObjs = new());
anonObjs.Add(anon);
this.RegisterInteractionHandlerImpl(anon, type);
}
}
///
/// .
///
/// Type to register.
public void RegisterInteractionHandler()
=> this.RegisterInteractionHandler(typeof(T));
///
/// Registers all types associated with the provided assembly that have the attribute.
///
/// The assembly from which to get the types.
public void RegisterInteractionHandlers(Assembly assembly)
{
+ this.Logger.LogError("Registering event handlers from assembly");
foreach (var t in GetInteractionHandlersFromAssembly(assembly))
this.RegisterInteractionHandler(t);
}
///
/// Perfectly mirrors .
///
/// The event handler object.
/// Whether it considered static methods.
public void UnregisterInteractionHandler(object handler, bool wasRegisteredWithStatic = false)
=> this.UnregisterInteractionHandlerImpl(handler, handler.GetType(), wasRegisteredWithStatic);
///
/// Perfectly mirrors .
///
/// Type to unregister.
public void UnregisterStaticInteractionHandler(Type t)
=> this.UnregisterInteractionHandlerImpl(null, t);
///
/// Perfectly mirrors .
///
/// Type to unregister.
public void UnregisterStaticInteractionHandler()
=> this.UnregisterInteractionHandler(typeof(T));
///
/// Perfectly mirrors .
///
/// Type to unregister.
public void UnregisterInteractionHandler(Type t)
{
if (t.IsAbstract)
this.UnregisterStaticInteractionHandler(t);
else
{
if (!this._typeInteractionToAnonymousHandlers.TryGetValue(t, out var anonObjs) || anonObjs.Count == 0)
return;
var anon = anonObjs[0];
anonObjs.RemoveAt(0);
if (anonObjs.Count == 0)
this._typeInteractionToAnonymousHandlers.Remove(t);
this.UnregisterInteractionHandlerImpl(anon, t);
}
}
///
/// Perfectly mirrors .
///
/// The type to unregister
public void UnregisterInteractionHandler()
=> this.UnregisterInteractionHandler(typeof(T));
///
/// Perfectly mirrors .
///
/// The assembly to unregister.
public void UnregisterInteractionHandlers(Assembly assembly)
{
foreach (var t in GetInteractionHandlersFromAssembly(assembly))
this.UnregisterInteractionHandler(t);
}
///
/// Gets the event handlers from the assembly.
///
/// The assembly to get the event handlers from.
private static IEnumerable GetInteractionHandlersFromAssembly(Assembly assembly)
- => assembly.GetTypes().Where(t => t.GetCustomAttribute() is not null);
+ => assembly.GetTypes().Where(t => t.GetCustomAttribute() is not null);
///
/// Unregisters event handler implementations.
///
/// The event handler object.
/// The type.
/// Whether it considereded static methods.
-
private void UnregisterInteractionHandlerImpl(object? handler, Type type, bool wasRegisteredWithStatic = true)
{
if (!this._registrationInteractionToDelegate.TryGetValue((handler, type, wasRegisteredWithStatic), out var delegateLists) || delegateLists.Count == 0)
return;
foreach (var (evnt, dlgt, prv) in delegateLists[0])
evnt.RemoveEventHandler(this, dlgt);
delegateLists.RemoveAt(0);
if (delegateLists.Count == 0)
this._registrationInteractionToDelegate.Remove((handler, type, wasRegisteredWithStatic));
}
///
/// Rregisters event handler implementations.
///
/// The event handler object.
/// The type.
/// Whether to consider static methods.
private void RegisterInteractionHandlerImpl(object? handler, Type type, bool registerStatic = true)
{
- var delegates = (
+ this.Logger.LogError("Trying to register event handlers");
+ try
+ {
+ var delegates = (
from method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
let attribute = method.GetCustomAttribute()
where attribute is not null && ((registerStatic && method.IsStatic) || handler is not null)
let eventName = attribute.EventName
let eventInfo = this.GetType().GetEvent(eventName)
?? throw new ArgumentException($"Tried to register handler to non-existent event \"{eventName}\"")
let eventHandlerType = eventInfo.EventHandlerType
let prefixed = attribute.IsPrefixed
let dlgt = (method.IsStatic
? Delegate.CreateDelegate(eventHandlerType, method, false)
: Delegate.CreateDelegate(eventHandlerType, handler, method, false))
?? throw new ArgumentException($"Method \"{method}\" does not adhere to event specification \"{eventHandlerType}\"")
select (eventInfo, dlgt, prefixed)
).ToArray();
this._registrationInteractionToDelegate[(handler, type, registerStatic)] = this._registrationInteractionToDelegate.TryGetValue((handler, type, registerStatic), out var delList) ? delList : (delList = new());
delList.Add(delegates);
foreach (var (evnt, dlgt, prv) in delegates)
if (!prv)
evnt.AddEventHandler(this, dlgt);
else
evnt.AddEventHandler(this, dlgt);
+
+ this.Logger.LogError("Registered {count} interaction event handlers", delegates.Length);
+ }
+ catch (Exception ex)
+ {
+ this.Logger.LogError(ex.Message);
+ this.Logger.LogError(ex.StackTrace);
+ throw ex;
+ }
}
}
diff --git a/DisCatSharp/Enums/Discord/DiscordEvent.cs b/DisCatSharp/Enums/Discord/DiscordEvent.cs
index 6f51a2e64..f083684b9 100644
--- a/DisCatSharp/Enums/Discord/DiscordEvent.cs
+++ b/DisCatSharp/Enums/Discord/DiscordEvent.cs
@@ -1,299 +1,305 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2023 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#nullable enable
using System;
namespace DisCatSharp.Enums;
///
/// Methods marked with this attribute will be registered as modal handling methods.
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class ModalInteractionAttribute : InteractionAttribute
{
///
/// The custom id to listen for.
///
internal readonly string CustomId;
///
/// Registers a method as modal handler.
///
/// The custom id of the modal to handle.
public ModalInteractionAttribute(string custom_id)
: base("InteractionCreated")
{
this.CustomId = custom_id;
}
}
///
/// Methods marked with this attribute will be registered as modal handling methods.
/// Modal custom ids will be checked with .
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class ModalInteractionWithPrefixAttribute : InteractionAttribute
{
///
/// The custom id to listen for.
///
internal readonly string CustomIdPrefix;
///
/// Registers a method as modal handler.
///
/// The custom id prefix of the modal to handle.
public ModalInteractionWithPrefixAttribute(string custom_id_prefix)
: base("InteractionCreated", true)
{
this.CustomIdPrefix = custom_id_prefix;
}
}
///
/// Methods marked with this attribute will be registered as button handling methods.
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class ButtonInteractionAttribute : InteractionAttribute
{
///
/// The custom id to listen for.
///
internal readonly string CustomId;
///
/// Registers a method as button handler.
///
/// The custom id of the button to handle.
public ButtonInteractionAttribute(string custom_id)
: base("ComponentInteractionCreated")
{
this.CustomId = custom_id;
}
}
///
/// Methods marked with this attribute will be registered as button handling methods.
/// Modal custom ids will be checked with .
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class ButtonInteractionWithPrefixAttribute : InteractionAttribute
{
///
/// The custom id to listen for.
///
internal readonly string CustomIdPrefix;
///
/// Registers a method as button handler.
///
/// The custom id prefix of the button to handle.
public ButtonInteractionWithPrefixAttribute(string custom_id_prefix)
: base("ComponentInteractionCreated", true)
{
this.CustomIdPrefix = custom_id_prefix;
}
}
///
/// Methods marked with this attribute will be registered as select menu handling methods.
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class SelectInteractionAttribute : InteractionAttribute
{
///
/// The custom id to listen for.
///
internal readonly string CustomId;
///
/// Registers a method as select menu handler.
///
/// The custom id of the select menu to handle.
public SelectInteractionAttribute(string custom_id)
: base("ComponentInteractionCreated")
{
this.CustomId = custom_id;
}
}
///
/// Methods marked with this attribute will be registered as select menu handling methods.
/// Modal custom ids will be checked with .
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class SelectMenuInteractionWithPrefixAttribute : InteractionAttribute
{
///
/// The custom id to listen for.
///
internal readonly string CustomIdPrefix;
///
/// Registers a method as select menu handler.
///
/// The custom id prefix of the select menu to handle.
public SelectMenuInteractionWithPrefixAttribute(string custom_id_prefix)
: base("ComponentInteractionCreated", true)
{
this.CustomIdPrefix = custom_id_prefix;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class InteractionAttribute : Attribute
{
internal readonly string EventName;
internal readonly bool IsPrefixed;
public InteractionAttribute(string event_name, bool prefixed = false)
{
this.EventName = event_name;
this.IsPrefixed = prefixed;
}
}
+///
+/// Classes marked with this attribute will be considered for event handler registration from an assembly.
+///
+[AttributeUsage(AttributeTargets.Class, Inherited = false)]
+public class InteractionHandlerAttribute : Attribute { }
+
///
/// Methods marked with this attribute will be registered as event handling methods
/// if the associated type / an associated instance is being registered.
///
[AttributeUsage(AttributeTargets.Method)]
public class EventAttribute : Attribute
{
///
/// The event name to listen for.
///
internal readonly string? EventName;
public EventAttribute() { }
///
/// Registers a method as event handler.
///
/// The name of the event.
/// The attributed method's name will be used if null.
public EventAttribute(DiscordEvent evtn)
{
this.EventName = evtn.ToString();
}
}
///
/// Classes marked with this attribute will be considered for event handler registration from an assembly.
///
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class EventHandlerAttribute : Attribute { }
///
/// All events available in for use with .
///
public enum DiscordEvent
{
ApplicationCommandCreated,
ApplicationCommandDeleted,
ApplicationCommandPermissionsUpdated,
ApplicationCommandUpdated,
ChannelCreated,
ChannelDeleted,
ChannelPinsUpdated,
ChannelUpdated,
ClientErrored,
ComponentInteractionCreated,
ContextMenuInteractionCreated,
DmChannelDeleted,
EmbeddedActivityUpdated,
GuildApplicationCommandCountUpdated,
GuildAvailable,
GuildBanAdded,
GuildBanRemoved,
GuildCreated,
GuildDeleted,
GuildDownloadCompleted,
GuildEmojisUpdated,
GuildIntegrationCreated,
GuildIntegrationDeleted,
GuildIntegrationsUpdated,
GuildIntegrationUpdated,
GuildMemberAdded,
GuildMemberRemoved,
GuildMembersChunked,
GuildMemberTimeoutAdded,
GuildMemberTimeoutChanged,
GuildMemberTimeoutRemoved,
GuildMemberUpdated,
GuildRoleCreated,
GuildRoleDeleted,
GuildRoleUpdated,
GuildScheduledEventCreated,
GuildScheduledEventDeleted,
GuildScheduledEventUpdated,
GuildScheduledEventUserAdded,
GuildScheduledEventUserRemoved,
GuildStickersUpdated,
GuildUnavailable,
GuildUpdated,
Heartbeated,
InteractionCreated,
InviteCreated,
InviteDeleted,
MessageAcknowledged,
MessageCreated,
MessageDeleted,
MessageReactionAdded,
MessageReactionRemoved,
MessageReactionRemovedEmoji,
MessageReactionsCleared,
MessagesBulkDeleted,
MessageUpdated,
PayloadReceived,
PresenceUpdated,
RateLimitHit,
Ready,
Resumed,
SocketClosed,
SocketErrored,
SocketOpened,
StageInstanceCreated,
StageInstanceDeleted,
StageInstanceUpdated,
ThreadCreated,
ThreadDeleted,
ThreadListSynced,
ThreadMembersUpdated,
ThreadMemberUpdated,
ThreadUpdated,
TypingStarted,
UnknownEvent,
UserSettingsUpdated,
UserUpdated,
VoiceServerUpdated,
VoiceStateUpdated,
WebhooksUpdated,
Zombied,
AutomodRuleCreated,
AutomodRuleUpdated,
AutomodRuleDeleted,
AutomodActionExecuted,
GuildAuditLogEntryCreated
}