diff --git a/DisCatSharp/Clients/DiscordClient.InteractionEventHandlers.cs b/DisCatSharp/Clients/DiscordClient.InteractionEventHandlers.cs index eb8a5c580..497a1c603 100644 --- a/DisCatSharp/Clients/DiscordClient.InteractionEventHandlers.cs +++ b/DisCatSharp/Clients/DiscordClient.InteractionEventHandlers.cs @@ -1,222 +1,226 @@ // 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; namespace DisCatSharp; /// /// A Discord API wrapper. /// public sealed partial class DiscordClient { - private readonly Dictionary<(object?, Type, bool), List<(EventInfo, Delegate)[]>> _registrationInteractionToDelegate = new(); + 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) { 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); /// /// 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) in delegateLists[0]) + 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 = ( + var delegates = ( from method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) - let attribute = method.GetCustomAttribute() + let attribute = method.GetCustomAttribute() where attribute is not null && ((registerStatic && method.IsStatic) || handler is not null) - let eventName = attribute.EventName ?? method.Name + 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.InteractionHandlerType + 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) + 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) in delegates) - evnt.AddEventHandler(this, dlgt);*/ + foreach (var (evnt, dlgt, prv) in delegates) + if (!prv) + evnt.AddEventHandler(this, dlgt); + else + evnt.AddEventHandler(this, dlgt); } }