discord-net / Discord.Net

An unofficial .Net wrapper for the Discord API (https://discord.com/)
https://discordnet.dev
MIT License
3.35k stars 732 forks source link

ReceivedGatewayEvent #2868

Open wickedmachinator opened 9 months ago

wickedmachinator commented 9 months ago

Hello!

It would be awesome to be able to access DiscordSocketClient.ApiClient, or at least expose an event which gets all events currently handled by DiscordSocketClient.ProcessMessageAsync.

Because everything is internal instead of being protected, I cannot solve this issue without forking Discord.Net.

I will try to add my own ReceivedGatewayEvent eventhandler with reflection to the internal ApiClient property, but even if it works, well, you know... not the best solution. Even the GatewayOpCode enum is internal...

PS: this project is a gemstone of github, congratulations!

Misha-133 commented 9 months ago

Hello! Thanks for the feedback

Could you better describe your usecase? Most likely we won't alter the library structure this way in v3 but we are rewriting the library for v4 and we could probably have your usecase addressed there

wickedmachinator commented 9 months ago

Thank you for the quick answer!

I want to catch all incoming messages from the gateway for debug purposes, better understand of the API, and other stuff.

currently this is not possible:

var client = new DiscordSocketClient(config);
client.ApiClient.ReceivedGatewayEvent += ProcessMessageAsync;

I created a solution to my problem with reflection to be able to add my own message processor to ReceivedGatewayEvent:

    private void AddClientReceivedGatewayEventHandler(DiscordSocketClient client)
    {
        var clientType = client.GetType();
        var apiClientProperty = clientType.GetProperty("ApiClient", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly);
        var apiClient = apiClientProperty.GetValue(_client);
        var apiClientType = apiClient.GetType();
        var receivedGatewayEvent = apiClientType.GetEvent("ReceivedGatewayEvent");

        var del = Delegate.CreateDelegate(receivedGatewayEvent.EventHandlerType, this, GetType().GetMethod(nameof(ProcessMessageAsync), System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic));

        receivedGatewayEvent.AddEventHandler(apiClient, del);
    }

    private async Task ProcessMessageAsync(GatewayOpCode opCode, int? sequence, string type, object payload)
    {
        var payloadAsToken = payload as JToken;

        Console.WriteLine(opCode.ToString() + "\t" + (sequence?.ToString() ?? "no sequence") + "\t" + type + "\t" + payloadAsToken?.ToString());
    }

    private enum GatewayOpCode : byte
    {
        .....
    }

You could easily expose a simple event on DiscordSocketClient, with the following parameters (GatewayOpCode opCode, int? sequence, string type, JToken payload) or (GatewayOpCode opCode, int? sequence, string type, object payload)

and invoke this event in DiscordSocketClient.ProcessMessageAsync.

Misha-133 commented 9 months ago

Hm Thanks for the explanation I think this is something we can include in v4

wickedmachinator commented 9 months ago

one more comment: in Discord.Net.Rest, all model classes are internal. That would be extremely helpful to change them to be public. If you are worried for "noise", then adding this will solve it: [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

example:

using Newtonsoft.Json; using Newtonsoft.Json.Linq;

namespace Discord.API
{
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    public class AuditLogChange
    {
        [JsonProperty("key")]
        public string ChangedProperty { get; set; }

        [JsonProperty("new_value")]
        public JToken NewValue { get; set; }

        [JsonProperty("old_value")]
        public JToken OldValue { get; set; }
    }
}

In this case, we could add low-level gateway message processing via the endpoint requested above, serializing the JToken to the corresponding model, similarly to what DiscordSocketClient.ProcessMessageAsync

I know these are edge-cases, but I can only repeat myself, they would be very useful for advanced processing.

Thank in advance!

Misha-133 commented 9 months ago

That would be extremely helpful to change them to be public.

Yeah, that is planned in v4. All json models will be public