From c9bde46282ce9e31c6d1a1d38b848eb0e31ef725 Mon Sep 17 00:00:00 2001 From: topi314 Date: Wed, 13 Aug 2025 19:54:57 +0200 Subject: [PATCH 1/4] partial implementation of webhook events --- .../application_commands/http/example.go | 10 +- _examples/application_commands/http/go.mod | 4 +- _examples/application_commands/http/go.sum | 2 + bot/client.go | 18 +- bot/config.go | 59 +++--- bot/event_manager.go | 130 ++++++------- bot/event_manager_config.go | 24 ++- bot/handlers/all_handlers.go | 125 ------------- bot/handlers/gateway_handlers.go | 161 ++++++++++++++++ bot/handlers/http_gateway_handler.go | 72 +++++++ bot/handlers/http_interaction_handler.go | 31 +++ bot/handlers/interaction_create_handler.go | 6 +- .../interaction_create_http_handler.go | 28 --- discord/channel.go | 2 +- discord/component.go | 2 +- discord/embed_builder.go | 8 +- discord/guild_scheduled_event.go | 2 +- discord/invite.go | 2 +- discord/member.go | 10 +- discord/message.go | 2 +- discord/user.go | 12 +- disgo.go | 9 +- gateway/gateway.go | 33 +++- gateway/gateway_config.go | 2 +- {httpserver => httpgateway}/config.go | 45 +++-- httpgateway/event.go | 56 ++++++ httpgateway/http_gateway.go | 80 ++++++++ httpgateway/http_gateway_messages.go | 177 ++++++++++++++++++ .../handler.go => httpgateway/interaction.go | 64 +------ httpgateway/verify.go | 76 ++++++++ httpserver/README.md | 22 --- httpserver/ed25519.go | 27 --- httpserver/server.go | 68 ------- webhook/webhook_client.go | 4 +- webhook/webook_test.go | 6 +- 35 files changed, 872 insertions(+), 507 deletions(-) delete mode 100644 bot/handlers/all_handlers.go create mode 100644 bot/handlers/gateway_handlers.go create mode 100644 bot/handlers/http_gateway_handler.go create mode 100644 bot/handlers/http_interaction_handler.go delete mode 100644 bot/handlers/interaction_create_http_handler.go rename {httpserver => httpgateway}/config.go (64%) create mode 100644 httpgateway/event.go create mode 100644 httpgateway/http_gateway.go create mode 100644 httpgateway/http_gateway_messages.go rename httpserver/handler.go => httpgateway/interaction.go (74%) create mode 100644 httpgateway/verify.go delete mode 100644 httpserver/README.md delete mode 100644 httpserver/ed25519.go delete mode 100644 httpserver/server.go diff --git a/_examples/application_commands/http/example.go b/_examples/application_commands/http/example.go index a1fd11bdf..6c1d46fa5 100644 --- a/_examples/application_commands/http/example.go +++ b/_examples/application_commands/http/example.go @@ -44,7 +44,7 @@ var ( type customVerifier struct{} -func (customVerifier) Verify(publicKey httpserver.PublicKey, message, sig []byte) bool { +func (customVerifier) Verify(publicKey httpgateway.PublicKey, message, sig []byte) bool { return ed25519.Verify(publicKey, message, sig) } @@ -58,10 +58,10 @@ func main() { client, err := disgo.New(token, bot.WithHTTPServerConfigOpts(publicKey, - httpserver.WithURL("/interactions/callback"), - httpserver.WithAddress(":80"), + httpgateway.WithURL("/interactions/callback"), + httpgateway.WithAddress(":80"), // use custom ed25519 verify implementation - httpserver.WithVerifier(customVerifier{}), + httpgateway.WithVerifier(customVerifier{}), ), bot.WithEventListenerFunc(commandListener), ) @@ -75,7 +75,7 @@ func main() { panic("error while registering commands: " + err.Error()) } - if err = client.OpenHTTPServer(); err != nil { + if err = client.OpenHTTPGateway(); err != nil { panic("error while starting http server: " + err.Error()) } diff --git a/_examples/application_commands/http/go.mod b/_examples/application_commands/http/go.mod index bc4d3acb9..b3e2ba3cd 100644 --- a/_examples/application_commands/http/go.mod +++ b/_examples/application_commands/http/go.mod @@ -15,6 +15,6 @@ require ( github.com/disgoorg/omit v1.0.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/sys v0.32.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/sys v0.33.0 // indirect ) diff --git a/_examples/application_commands/http/go.sum b/_examples/application_commands/http/go.sum index 3a4812a17..a4a6b2bd3 100644 --- a/_examples/application_commands/http/go.sum +++ b/_examples/application_commands/http/go.sum @@ -18,7 +18,9 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/bot/client.go b/bot/client.go index dc6ea6969..16bc89a46 100644 --- a/bot/client.go +++ b/bot/client.go @@ -9,7 +9,7 @@ import ( "github.com/disgoorg/disgo/cache" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/gateway" - "github.com/disgoorg/disgo/httpserver" + "github.com/disgoorg/disgo/httpgateway" "github.com/disgoorg/disgo/rest" "github.com/disgoorg/disgo/sharding" "github.com/disgoorg/disgo/voice" @@ -26,7 +26,7 @@ type Client struct { EventManager EventManager ShardManager sharding.ShardManager Gateway gateway.Gateway - HTTPServer httpserver.Server + HTTPGateway httpgateway.Gateway VoiceManager voice.Manager Caches cache.Caches MemberChunkingManager MemberChunkingManager @@ -45,8 +45,8 @@ func (c *Client) Close(ctx context.Context) { if c.ShardManager != nil { c.ShardManager.Close(ctx) } - if c.HTTPServer != nil { - c.HTTPServer.Close(ctx) + if c.HTTPGateway != nil { + c.HTTPGateway.Close(ctx) } } @@ -168,16 +168,16 @@ func (c *Client) SetPresenceForShard(ctx context.Context, shardId int, opts ...g return shard.Send(ctx, gateway.OpcodePresenceUpdate, applyPresenceFromOpts(shard, opts...)) } -func (c *Client) OpenHTTPServer() error { - if c.HTTPServer == nil { +func (c *Client) OpenHTTPGateway() error { + if c.HTTPGateway == nil { return discord.ErrNoHTTPServer } - c.HTTPServer.Start() + c.HTTPGateway.Start() return nil } -func (c *Client) HasHTTPServer() bool { - return c.HTTPServer != nil +func (c *Client) HasHTTPGateway() bool { + return c.HTTPGateway != nil } func applyPresenceFromOpts(g gateway.Gateway, opts ...gateway.PresenceOpt) gateway.MessageDataPresenceUpdate { diff --git a/bot/config.go b/bot/config.go index 6ea58d352..b9d5b65de 100644 --- a/bot/config.go +++ b/bot/config.go @@ -7,18 +7,22 @@ import ( "github.com/disgoorg/disgo/cache" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/gateway" - "github.com/disgoorg/disgo/httpserver" + "github.com/disgoorg/disgo/httpgateway" "github.com/disgoorg/disgo/internal/tokenhelper" "github.com/disgoorg/disgo/rest" "github.com/disgoorg/disgo/sharding" "github.com/disgoorg/disgo/voice" ) -func defaultConfig(gatewayHandlers map[gateway.EventType]GatewayEventHandler, httpHandler HTTPServerEventHandler) config { +func defaultConfig(gatewayHandler GatewayEventHandler, httpInteractionHandler HTTPInteractionEventHandler, httpGatewayHandler HTTPGatewayEventHandler) config { return config{ - Logger: slog.Default(), - EventManagerConfigOpts: []EventManagerConfigOpt{WithGatewayHandlers(gatewayHandlers), WithHTTPServerHandler(httpHandler)}, - MemberChunkingFilter: MemberChunkingFilterNone, + Logger: slog.Default(), + EventManagerConfigOpts: []EventManagerConfigOpt{ + WithGatewayHandlers(gatewayHandler), + WithHTTPServerHandler(httpInteractionHandler), + WithHTTPGatewayHandler(httpGatewayHandler), + }, + MemberChunkingFilter: MemberChunkingFilterNone, } } @@ -41,9 +45,9 @@ type config struct { ShardManager sharding.ShardManager ShardManagerConfigOpts []sharding.ConfigOpt - HTTPServer httpserver.Server - PublicKey string - HTTPServerConfigOpts []httpserver.ConfigOpt + HTTPGateway httpgateway.Gateway + PublicKey string + HTTPGatewayConfigOpts []httpgateway.ConfigOpt Caches cache.Caches CacheConfigOpts []cache.ConfigOpt @@ -164,17 +168,17 @@ func WithShardManagerConfigOpts(opts ...sharding.ConfigOpt) ConfigOpt { } // WithHTTPServer lets you inject your own httpserver.Server. -func WithHTTPServer(httpServer httpserver.Server) ConfigOpt { +func WithHTTPServer(httpServer httpgateway.Gateway) ConfigOpt { return func(config *config) { - config.HTTPServer = httpServer + config.HTTPGateway = httpServer } } // WithHTTPServerConfigOpts lets you configure the default httpserver.Server. -func WithHTTPServerConfigOpts(publicKey string, opts ...httpserver.ConfigOpt) ConfigOpt { +func WithHTTPServerConfigOpts(publicKey string, opts ...httpgateway.ConfigOpt) ConfigOpt { return func(config *config) { config.PublicKey = publicKey - config.HTTPServerConfigOpts = append(config.HTTPServerConfigOpts, opts...) + config.HTTPGatewayConfigOpts = append(config.HTTPGatewayConfigOpts, opts...) } } @@ -206,20 +210,25 @@ func WithMemberChunkingFilter(memberChunkingFilter MemberChunkingFilter) ConfigO } } -func defaultHTTPServerEventHandlerFunc(client *Client) httpserver.EventHandlerFunc { - return client.EventManager.HandleHTTPEvent -} - func defaultGatewayEventHandlerFunc(client *Client) gateway.EventHandlerFunc { return client.EventManager.HandleGatewayEvent } +func defaultInteractionHandlerFunc(client *Client) httpgateway.InteractionHandlerFunc { + return client.EventManager.HandleHTTPInteractionEvent +} + +func defaultHTTPGatewayEventHandlerFunc(client *Client) httpgateway.EventHandlerFunc { + return client.EventManager.HandleHTTPGatewayEvent +} + // BuildClient creates a new Client instance with the given Token, config, Gateway handlers, http handlers os, name, github & version. func BuildClient( token string, otps []ConfigOpt, - gatewayHandlers map[gateway.EventType]GatewayEventHandler, - httpHandler HTTPServerEventHandler, + gatewayHandler GatewayEventHandler, + httpInteractionHandler HTTPInteractionEventHandler, + httpGatewayHandler HTTPGatewayEventHandler, os string, name string, github string, @@ -233,7 +242,7 @@ func BuildClient( return nil, fmt.Errorf("error while getting application id from Token: %w", err) } - cfg := defaultConfig(gatewayHandlers, httpHandler) + cfg := defaultConfig(gatewayHandler, httpInteractionHandler, httpGatewayHandler) cfg.apply(otps) client := &Client{ @@ -325,17 +334,17 @@ func BuildClient( } client.ShardManager = cfg.ShardManager - if cfg.HTTPServer == nil && cfg.PublicKey != "" { - cfg.HTTPServerConfigOpts = append([]httpserver.ConfigOpt{ - httpserver.WithLogger(cfg.Logger), - }, cfg.HTTPServerConfigOpts...) + if cfg.HTTPGateway == nil && cfg.PublicKey != "" { + cfg.HTTPGatewayConfigOpts = append([]httpgateway.ConfigOpt{ + httpgateway.WithLogger(cfg.Logger), + }, cfg.HTTPGatewayConfigOpts...) - cfg.HTTPServer, err = httpserver.New(cfg.PublicKey, defaultHTTPServerEventHandlerFunc(client), cfg.HTTPServerConfigOpts...) + cfg.HTTPGateway, err = httpgateway.New(cfg.PublicKey, defaultInteractionHandlerFunc(client), defaultHTTPGatewayEventHandlerFunc(client), cfg.HTTPGatewayConfigOpts...) if err != nil { return nil, fmt.Errorf("error while creating http server: %w", err) } } - client.HTTPServer = cfg.HTTPServer + client.HTTPGateway = cfg.HTTPGateway if cfg.MemberChunkingManager == nil { cfg.MemberChunkingManager = NewMemberChunkingManager(client, cfg.Logger, cfg.MemberChunkingFilter) diff --git a/bot/event_manager.go b/bot/event_manager.go index 5c49c8e00..601199623 100644 --- a/bot/event_manager.go +++ b/bot/event_manager.go @@ -6,44 +6,9 @@ import ( "sync" "github.com/disgoorg/disgo/gateway" - "github.com/disgoorg/disgo/httpserver" + "github.com/disgoorg/disgo/httpgateway" ) -var _ EventManager = (*eventManagerImpl)(nil) - -// NewEventManager returns a new EventManager with the EventManagerConfigOpt(s) applied. -func NewEventManager(client *Client, opts ...EventManagerConfigOpt) EventManager { - cfg := defaultEventManagerConfig() - cfg.apply(opts) - - return &eventManagerImpl{ - client: client, - logger: cfg.Logger, - eventListeners: cfg.EventListeners, - asyncEventsEnabled: cfg.AsyncEventsEnabled, - gatewayHandlers: cfg.GatewayHandlers, - httpServerHandler: cfg.HTTPServerHandler, - } -} - -// EventManager lets you listen for specific events triggered by raw Gateway events -type EventManager interface { - // AddEventListeners adds one or more EventListener(s) to the EventManager - AddEventListeners(eventListeners ...EventListener) - - // RemoveEventListeners removes one or more EventListener(s) from the EventManager - RemoveEventListeners(eventListeners ...EventListener) - - // HandleGatewayEvent calls the correct GatewayEventHandler for the payload - HandleGatewayEvent(gateway gateway.Gateway, eventType gateway.EventType, sequenceNumber int, event gateway.EventData) - - // HandleHTTPEvent calls the HTTPServerEventHandler for the payload - HandleHTTPEvent(respondFunc httpserver.RespondFunc, event httpserver.EventInteractionCreate) - - // DispatchEvent dispatches a new Event to the Client's EventListener(s) - DispatchEvent(event Event) -} - // EventListener is used to create new EventListener to listen to events type EventListener interface { OnEvent(event Event) @@ -87,61 +52,90 @@ type Event interface { // GatewayEventHandler is used to handle Gateway Event(s) type GatewayEventHandler interface { - EventType() gateway.EventType - HandleGatewayEvent(client *Client, sequenceNumber int, shardID int, event gateway.EventData) + HandleGatewayEvent(client *Client, message gateway.Message, shardID int) } -// NewGatewayEventHandler returns a new GatewayEventHandler for the given gateway.EventType and handler func -func NewGatewayEventHandler[T gateway.EventData](eventType gateway.EventType, handleFunc func(client *Client, sequenceNumber int, shardID int, event T)) GatewayEventHandler { - return &genericGatewayEventHandler[T]{eventType: eventType, handleFunc: handleFunc} +// HTTPInteractionEventHandler is used to handle HTTP Event(s) +type HTTPInteractionEventHandler interface { + HandleHTTPInteraction(client *Client, respond httpgateway.RespondFunc, event httpgateway.EventInteractionCreate) } -type genericGatewayEventHandler[T gateway.EventData] struct { - eventType gateway.EventType - handleFunc func(client *Client, sequenceNumber int, shardID int, event T) +type HTTPGatewayEventHandler interface { + HandleHTTPGatewayEvent(client *Client, ack func(), message httpgateway.Message) } -func (h *genericGatewayEventHandler[T]) EventType() gateway.EventType { - return h.eventType -} +// EventManager lets you listen for specific events triggered by raw Gateway events +type EventManager interface { + // AddEventListeners adds one or more EventListener(s) to the EventManager + AddEventListeners(eventListeners ...EventListener) -func (h *genericGatewayEventHandler[T]) HandleGatewayEvent(client *Client, sequenceNumber int, shardID int, event gateway.EventData) { - if e, ok := event.(T); ok { - h.handleFunc(client, sequenceNumber, shardID, e) - } + // RemoveEventListeners removes one or more EventListener(s) from the EventManager + RemoveEventListeners(eventListeners ...EventListener) + + // HandleGatewayEvent calls the correct GatewayEventHandler for the payload + HandleGatewayEvent(gateway gateway.Gateway, message gateway.Message) + + // HandleHTTPInteractionEvent calls the HTTPInteractionEventHandler for the payload + HandleHTTPInteractionEvent(respond httpgateway.RespondFunc, event httpgateway.EventInteractionCreate) + + // HandleHTTPGatewayEvent calls the HTTPInteractionEventHandler for the payload + HandleHTTPGatewayEvent(ack func(), message httpgateway.Message) + + // DispatchEvent dispatches a new Event to the Client's EventListener(s) + DispatchEvent(event Event) } -// HTTPServerEventHandler is used to handle HTTP Event(s) -type HTTPServerEventHandler interface { - HandleHTTPEvent(client *Client, respondFunc httpserver.RespondFunc, event httpserver.EventInteractionCreate) +var _ EventManager = (*eventManagerImpl)(nil) + +// NewEventManager returns a new EventManager with the EventManagerConfigOpt(s) applied. +func NewEventManager(client *Client, opts ...EventManagerConfigOpt) EventManager { + cfg := defaultEventManagerConfig() + cfg.apply(opts) + + return &eventManagerImpl{ + client: client, + logger: cfg.Logger, + asyncEventsEnabled: cfg.AsyncEventsEnabled, + eventListeners: cfg.EventListeners, + gatewayHandler: cfg.GatewayHandler, + httpInteractionHandler: cfg.HTTPInteractionHandler, + httpGatewayHandler: cfg.HTTPGatewayHandler, + } } type eventManagerImpl struct { - mu sync.Mutex + client *Client + logger *slog.Logger - client *Client - logger *slog.Logger + asyncEventsEnabled bool eventListenerMu sync.Mutex eventListeners []EventListener - asyncEventsEnabled bool - gatewayHandlers map[gateway.EventType]GatewayEventHandler - httpServerHandler HTTPServerEventHandler + + mu sync.Mutex + gatewayHandler GatewayEventHandler + httpInteractionHandler HTTPInteractionEventHandler + httpGatewayHandler HTTPGatewayEventHandler } -func (e *eventManagerImpl) HandleGatewayEvent(gateway gateway.Gateway, eventType gateway.EventType, sequenceNumber int, event gateway.EventData) { +func (e *eventManagerImpl) HandleGatewayEvent(gateway gateway.Gateway, message gateway.Message) { e.mu.Lock() defer e.mu.Unlock() - if handler, ok := e.gatewayHandlers[eventType]; ok { - handler.HandleGatewayEvent(e.client, sequenceNumber, gateway.ShardID(), event) - } else { - e.logger.Warn("no handler for Gateway event found", slog.Any("event_type", eventType)) - } + + e.gatewayHandler.HandleGatewayEvent(e.client, message, gateway.ShardID()) } -func (e *eventManagerImpl) HandleHTTPEvent(respondFunc httpserver.RespondFunc, event httpserver.EventInteractionCreate) { +func (e *eventManagerImpl) HandleHTTPInteractionEvent(respond httpgateway.RespondFunc, event httpgateway.EventInteractionCreate) { e.mu.Lock() defer e.mu.Unlock() - e.httpServerHandler.HandleHTTPEvent(e.client, respondFunc, event) + + e.httpInteractionHandler.HandleHTTPInteraction(e.client, respond, event) +} + +func (e *eventManagerImpl) HandleHTTPGatewayEvent(ack func(), message httpgateway.Message) { + e.mu.Lock() + defer e.mu.Unlock() + + e.httpGatewayHandler.HandleHTTPGatewayEvent(e.client, ack, message) } func (e *eventManagerImpl) DispatchEvent(event Event) { diff --git a/bot/event_manager_config.go b/bot/event_manager_config.go index ef8b790fb..815c3dba6 100644 --- a/bot/event_manager_config.go +++ b/bot/event_manager_config.go @@ -2,8 +2,6 @@ package bot import ( "log/slog" - - "github.com/disgoorg/disgo/gateway" ) func defaultEventManagerConfig() eventManagerConfig { @@ -17,8 +15,9 @@ type eventManagerConfig struct { EventListeners []EventListener AsyncEventsEnabled bool - GatewayHandlers map[gateway.EventType]GatewayEventHandler - HTTPServerHandler HTTPServerEventHandler + GatewayHandler GatewayEventHandler + HTTPInteractionHandler HTTPInteractionEventHandler + HTTPGatewayHandler HTTPGatewayEventHandler } // EventManagerConfigOpt is a functional option for configuring an EventManager. @@ -63,15 +62,22 @@ func WithAsyncEventsEnabled() EventManagerConfigOpt { } // WithGatewayHandlers overrides the default GatewayEventHandler(s) in the eventManagerConfig. -func WithGatewayHandlers(handlers map[gateway.EventType]GatewayEventHandler) EventManagerConfigOpt { +func WithGatewayHandlers(handler GatewayEventHandler) EventManagerConfigOpt { + return func(config *eventManagerConfig) { + config.GatewayHandler = handler + } +} + +// WithHTTPServerHandler overrides the given HTTPInteractionEventHandler in the eventManagerConfig. +func WithHTTPServerHandler(handler HTTPInteractionEventHandler) EventManagerConfigOpt { return func(config *eventManagerConfig) { - config.GatewayHandlers = handlers + config.HTTPInteractionHandler = handler } } -// WithHTTPServerHandler overrides the given HTTPServerEventHandler in the eventManagerConfig. -func WithHTTPServerHandler(handler HTTPServerEventHandler) EventManagerConfigOpt { +// WithHTTPGatewayHandler overrides the given HTTPGatewayEventHandler in the eventManagerConfig. +func WithHTTPGatewayHandler(handler HTTPGatewayEventHandler) EventManagerConfigOpt { return func(config *eventManagerConfig) { - config.HTTPServerHandler = handler + config.HTTPGatewayHandler = handler } } diff --git a/bot/handlers/all_handlers.go b/bot/handlers/all_handlers.go deleted file mode 100644 index ab2d27fc0..000000000 --- a/bot/handlers/all_handlers.go +++ /dev/null @@ -1,125 +0,0 @@ -package handlers - -import ( - "github.com/disgoorg/disgo/bot" - "github.com/disgoorg/disgo/gateway" -) - -// GetHTTPServerHandler returns the default httpserver.Server event handler for processing the raw payload which gets passed into the bot.EventManager -func GetHTTPServerHandler() bot.HTTPServerEventHandler { - return &httpserverHandlerInteractionCreate{} -} - -// GetGatewayHandlers returns the default gateway.Gateway event handlers for processing the raw payload which gets passed into the bot.EventManager -func GetGatewayHandlers() map[gateway.EventType]bot.GatewayEventHandler { - handlers := make(map[gateway.EventType]bot.GatewayEventHandler, len(allEventHandlers)) - for _, handler := range allEventHandlers { - handlers[handler.EventType()] = handler - } - return handlers -} - -var allEventHandlers = []bot.GatewayEventHandler{ - bot.NewGatewayEventHandler(gateway.EventTypeRaw, gatewayHandlerRaw), - bot.NewGatewayEventHandler(gateway.EventTypeHeartbeatAck, gatewayHandlerHeartbeatAck), - bot.NewGatewayEventHandler(gateway.EventTypeReady, gatewayHandlerReady), - bot.NewGatewayEventHandler(gateway.EventTypeResumed, gatewayHandlerResumed), - - bot.NewGatewayEventHandler(gateway.EventTypeApplicationCommandPermissionsUpdate, gatewayHandlerApplicationCommandPermissionsUpdate), - - bot.NewGatewayEventHandler(gateway.EventTypeAutoModerationRuleCreate, gatewayHandlerAutoModerationRuleCreate), - bot.NewGatewayEventHandler(gateway.EventTypeAutoModerationRuleUpdate, gatewayHandlerAutoModerationRuleUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeAutoModerationRuleDelete, gatewayHandlerAutoModerationRuleDelete), - bot.NewGatewayEventHandler(gateway.EventTypeAutoModerationActionExecution, gatewayHandlerAutoModerationActionExecution), - - bot.NewGatewayEventHandler(gateway.EventTypeChannelCreate, gatewayHandlerChannelCreate), - bot.NewGatewayEventHandler(gateway.EventTypeChannelUpdate, gatewayHandlerChannelUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeChannelDelete, gatewayHandlerChannelDelete), - bot.NewGatewayEventHandler(gateway.EventTypeChannelPinsUpdate, gatewayHandlerChannelPinsUpdate), - - bot.NewGatewayEventHandler(gateway.EventTypeEntitlementCreate, gatewayHandlerEntitlementCreate), - bot.NewGatewayEventHandler(gateway.EventTypeEntitlementUpdate, gatewayHandlerEntitlementUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeEntitlementDelete, gatewayHandlerEntitlementDelete), - - bot.NewGatewayEventHandler(gateway.EventTypeThreadCreate, gatewayHandlerThreadCreate), - bot.NewGatewayEventHandler(gateway.EventTypeThreadUpdate, gatewayHandlerThreadUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeThreadDelete, gatewayHandlerThreadDelete), - bot.NewGatewayEventHandler(gateway.EventTypeThreadListSync, gatewayHandlerThreadListSync), - bot.NewGatewayEventHandler(gateway.EventTypeThreadMemberUpdate, gatewayHandlerThreadMemberUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeThreadMembersUpdate, gatewayHandlerThreadMembersUpdate), - - bot.NewGatewayEventHandler(gateway.EventTypeGuildCreate, gatewayHandlerGuildCreate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildUpdate, gatewayHandlerGuildUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildDelete, gatewayHandlerGuildDelete), - - bot.NewGatewayEventHandler(gateway.EventTypeGuildAuditLogEntryCreate, gatewayHandlerGuildAuditLogEntryCreate), - - bot.NewGatewayEventHandler(gateway.EventTypeGuildBanAdd, gatewayHandlerGuildBanAdd), - bot.NewGatewayEventHandler(gateway.EventTypeGuildBanRemove, gatewayHandlerGuildBanRemove), - - bot.NewGatewayEventHandler(gateway.EventTypeGuildEmojisUpdate, gatewayHandlerGuildEmojisUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildStickersUpdate, gatewayHandlerGuildStickersUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildIntegrationsUpdate, gatewayHandlerGuildIntegrationsUpdate), - - bot.NewGatewayEventHandler(gateway.EventTypeGuildMemberAdd, gatewayHandlerGuildMemberAdd), - bot.NewGatewayEventHandler(gateway.EventTypeGuildMemberRemove, gatewayHandlerGuildMemberRemove), - bot.NewGatewayEventHandler(gateway.EventTypeGuildMemberUpdate, gatewayHandlerGuildMemberUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildMembersChunk, gatewayHandlerGuildMembersChunk), - - bot.NewGatewayEventHandler(gateway.EventTypeGuildRoleCreate, gatewayHandlerGuildRoleCreate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildRoleUpdate, gatewayHandlerGuildRoleUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildRoleDelete, gatewayHandlerGuildRoleDelete), - - bot.NewGatewayEventHandler(gateway.EventTypeGuildScheduledEventCreate, gatewayHandlerGuildScheduledEventCreate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildScheduledEventUpdate, gatewayHandlerGuildScheduledEventUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildScheduledEventDelete, gatewayHandlerGuildScheduledEventDelete), - bot.NewGatewayEventHandler(gateway.EventTypeGuildScheduledEventUserAdd, gatewayHandlerGuildScheduledEventUserAdd), - bot.NewGatewayEventHandler(gateway.EventTypeGuildScheduledEventUserRemove, gatewayHandlerGuildScheduledEventUserRemove), - - bot.NewGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundCreate, gatewayHandlerGuildSoundboardSoundCreate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundUpdate, gatewayHandlerGuildSoundboardSoundUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundDelete, gatewayHandlerGuildSoundboardSoundDelete), - bot.NewGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundsUpdate, gatewayHandlerGuildSoundboardSoundsUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeSoundboardSounds, gatewayHandlerSoundboardSounds), - - bot.NewGatewayEventHandler(gateway.EventTypeIntegrationCreate, gatewayHandlerIntegrationCreate), - bot.NewGatewayEventHandler(gateway.EventTypeIntegrationUpdate, gatewayHandlerIntegrationUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeIntegrationDelete, gatewayHandlerIntegrationDelete), - - bot.NewGatewayEventHandler(gateway.EventTypeInteractionCreate, gatewayHandlerInteractionCreate), - - bot.NewGatewayEventHandler(gateway.EventTypeInviteCreate, gatewayHandlerInviteCreate), - bot.NewGatewayEventHandler(gateway.EventTypeInviteDelete, gatewayHandlerInviteDelete), - - bot.NewGatewayEventHandler(gateway.EventTypeMessageCreate, gatewayHandlerMessageCreate), - bot.NewGatewayEventHandler(gateway.EventTypeMessageUpdate, gatewayHandlerMessageUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeMessageDelete, gatewayHandlerMessageDelete), - bot.NewGatewayEventHandler(gateway.EventTypeMessageDeleteBulk, gatewayHandlerMessageDeleteBulk), - - bot.NewGatewayEventHandler(gateway.EventTypeMessagePollVoteAdd, gatewayHandlerMessagePollVoteAdd), - bot.NewGatewayEventHandler(gateway.EventTypeMessagePollVoteRemove, gatewayHandlerMessagePollVoteRemove), - - bot.NewGatewayEventHandler(gateway.EventTypeMessageReactionAdd, gatewayHandlerMessageReactionAdd), - bot.NewGatewayEventHandler(gateway.EventTypeMessageReactionRemove, gatewayHandlerMessageReactionRemove), - bot.NewGatewayEventHandler(gateway.EventTypeMessageReactionRemoveAll, gatewayHandlerMessageReactionRemoveAll), - bot.NewGatewayEventHandler(gateway.EventTypeMessageReactionRemoveEmoji, gatewayHandlerMessageReactionRemoveEmoji), - - bot.NewGatewayEventHandler(gateway.EventTypePresenceUpdate, gatewayHandlerPresenceUpdate), - - bot.NewGatewayEventHandler(gateway.EventTypeStageInstanceCreate, gatewayHandlerStageInstanceCreate), - bot.NewGatewayEventHandler(gateway.EventTypeStageInstanceUpdate, gatewayHandlerStageInstanceUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeStageInstanceDelete, gatewayHandlerStageInstanceDelete), - - bot.NewGatewayEventHandler(gateway.EventTypeSubscriptionCreate, gatewayHandlerSubscriptionCreate), - bot.NewGatewayEventHandler(gateway.EventTypeSubscriptionUpdate, gatewayHandlerSubscriptionUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeSubscriptionDelete, gatewayHandlerSubscriptionDelete), - - bot.NewGatewayEventHandler(gateway.EventTypeTypingStart, gatewayHandlerTypingStart), - bot.NewGatewayEventHandler(gateway.EventTypeUserUpdate, gatewayHandlerUserUpdate), - - bot.NewGatewayEventHandler(gateway.EventTypeVoiceChannelEffectSend, gatewayHandlerVoiceChannelEffectSend), - bot.NewGatewayEventHandler(gateway.EventTypeVoiceStateUpdate, gatewayHandlerVoiceStateUpdate), - bot.NewGatewayEventHandler(gateway.EventTypeVoiceServerUpdate, gatewayHandlerVoiceServerUpdate), - - bot.NewGatewayEventHandler(gateway.EventTypeWebhooksUpdate, gatewayHandlerWebhooksUpdate), -} diff --git a/bot/handlers/gateway_handlers.go b/bot/handlers/gateway_handlers.go new file mode 100644 index 000000000..5fcecbd5e --- /dev/null +++ b/bot/handlers/gateway_handlers.go @@ -0,0 +1,161 @@ +package handlers + +import ( + "github.com/disgoorg/disgo/bot" + "github.com/disgoorg/disgo/gateway" +) + +type gatewayEventHandler interface { + bot.GatewayEventHandler + EventType() gateway.EventType +} + +// GetGatewayHandler returns the default gateway.Gateway event handlers for processing the raw payload which gets passed into the bot.EventManager +func GetGatewayHandler() bot.GatewayEventHandler { + handlers := make(map[gateway.EventType]gatewayEventHandler, len(allEventHandlers)) + for _, handler := range allEventHandlers { + handlers[handler.EventType()] = handler + } + return &gatewayHandler{ + handlers: handlers, + } +} + +type gatewayHandler struct { + handlers map[gateway.EventType]gatewayEventHandler +} + +func (g *gatewayHandler) HandleGatewayEvent(client *bot.Client, message gateway.Message, shardID int) { + if handler, ok := g.handlers[message.T]; ok { + handler.HandleGatewayEvent(client, message, shardID) + } else { + client.Logger.Warn("No handler found for gateway event", "event_type", message.T, "shard_id", shardID) + } +} + +func newGatewayEventHandler[T gateway.EventData](eventType gateway.EventType, handleFunc func(client *bot.Client, sequenceNumber int, shardID int, event T)) *genericGatewayEventHandler[T] { + return &genericGatewayEventHandler[T]{ + eventType: eventType, + handleFunc: handleFunc, + } +} + +type genericGatewayEventHandler[T gateway.EventData] struct { + eventType gateway.EventType + handleFunc func(client *bot.Client, sequenceNumber int, shardID int, event T) +} + +func (h *genericGatewayEventHandler[T]) EventType() gateway.EventType { + return h.eventType +} + +func (h *genericGatewayEventHandler[T]) HandleGatewayEvent(client *bot.Client, message gateway.Message, shardID int) { + if data, ok := message.D.(T); ok { + h.handleFunc(client, message.S, shardID, data) + } +} + +var allEventHandlers = []gatewayEventHandler{ + newGatewayEventHandler(gateway.EventTypeRaw, gatewayHandlerRaw), + newGatewayEventHandler(gateway.EventTypeHeartbeatAck, gatewayHandlerHeartbeatAck), + newGatewayEventHandler(gateway.EventTypeReady, gatewayHandlerReady), + newGatewayEventHandler(gateway.EventTypeResumed, gatewayHandlerResumed), + + newGatewayEventHandler(gateway.EventTypeApplicationCommandPermissionsUpdate, gatewayHandlerApplicationCommandPermissionsUpdate), + + newGatewayEventHandler(gateway.EventTypeAutoModerationRuleCreate, gatewayHandlerAutoModerationRuleCreate), + newGatewayEventHandler(gateway.EventTypeAutoModerationRuleUpdate, gatewayHandlerAutoModerationRuleUpdate), + newGatewayEventHandler(gateway.EventTypeAutoModerationRuleDelete, gatewayHandlerAutoModerationRuleDelete), + newGatewayEventHandler(gateway.EventTypeAutoModerationActionExecution, gatewayHandlerAutoModerationActionExecution), + + newGatewayEventHandler(gateway.EventTypeChannelCreate, gatewayHandlerChannelCreate), + newGatewayEventHandler(gateway.EventTypeChannelUpdate, gatewayHandlerChannelUpdate), + newGatewayEventHandler(gateway.EventTypeChannelDelete, gatewayHandlerChannelDelete), + newGatewayEventHandler(gateway.EventTypeChannelPinsUpdate, gatewayHandlerChannelPinsUpdate), + + newGatewayEventHandler(gateway.EventTypeEntitlementCreate, gatewayHandlerEntitlementCreate), + newGatewayEventHandler(gateway.EventTypeEntitlementUpdate, gatewayHandlerEntitlementUpdate), + newGatewayEventHandler(gateway.EventTypeEntitlementDelete, gatewayHandlerEntitlementDelete), + + newGatewayEventHandler(gateway.EventTypeThreadCreate, gatewayHandlerThreadCreate), + newGatewayEventHandler(gateway.EventTypeThreadUpdate, gatewayHandlerThreadUpdate), + newGatewayEventHandler(gateway.EventTypeThreadDelete, gatewayHandlerThreadDelete), + newGatewayEventHandler(gateway.EventTypeThreadListSync, gatewayHandlerThreadListSync), + newGatewayEventHandler(gateway.EventTypeThreadMemberUpdate, gatewayHandlerThreadMemberUpdate), + newGatewayEventHandler(gateway.EventTypeThreadMembersUpdate, gatewayHandlerThreadMembersUpdate), + + newGatewayEventHandler(gateway.EventTypeGuildCreate, gatewayHandlerGuildCreate), + newGatewayEventHandler(gateway.EventTypeGuildUpdate, gatewayHandlerGuildUpdate), + newGatewayEventHandler(gateway.EventTypeGuildDelete, gatewayHandlerGuildDelete), + + newGatewayEventHandler(gateway.EventTypeGuildAuditLogEntryCreate, gatewayHandlerGuildAuditLogEntryCreate), + + newGatewayEventHandler(gateway.EventTypeGuildBanAdd, gatewayHandlerGuildBanAdd), + newGatewayEventHandler(gateway.EventTypeGuildBanRemove, gatewayHandlerGuildBanRemove), + + newGatewayEventHandler(gateway.EventTypeGuildEmojisUpdate, gatewayHandlerGuildEmojisUpdate), + newGatewayEventHandler(gateway.EventTypeGuildStickersUpdate, gatewayHandlerGuildStickersUpdate), + newGatewayEventHandler(gateway.EventTypeGuildIntegrationsUpdate, gatewayHandlerGuildIntegrationsUpdate), + + newGatewayEventHandler(gateway.EventTypeGuildMemberAdd, gatewayHandlerGuildMemberAdd), + newGatewayEventHandler(gateway.EventTypeGuildMemberRemove, gatewayHandlerGuildMemberRemove), + newGatewayEventHandler(gateway.EventTypeGuildMemberUpdate, gatewayHandlerGuildMemberUpdate), + newGatewayEventHandler(gateway.EventTypeGuildMembersChunk, gatewayHandlerGuildMembersChunk), + + newGatewayEventHandler(gateway.EventTypeGuildRoleCreate, gatewayHandlerGuildRoleCreate), + newGatewayEventHandler(gateway.EventTypeGuildRoleUpdate, gatewayHandlerGuildRoleUpdate), + newGatewayEventHandler(gateway.EventTypeGuildRoleDelete, gatewayHandlerGuildRoleDelete), + + newGatewayEventHandler(gateway.EventTypeGuildScheduledEventCreate, gatewayHandlerGuildScheduledEventCreate), + newGatewayEventHandler(gateway.EventTypeGuildScheduledEventUpdate, gatewayHandlerGuildScheduledEventUpdate), + newGatewayEventHandler(gateway.EventTypeGuildScheduledEventDelete, gatewayHandlerGuildScheduledEventDelete), + newGatewayEventHandler(gateway.EventTypeGuildScheduledEventUserAdd, gatewayHandlerGuildScheduledEventUserAdd), + newGatewayEventHandler(gateway.EventTypeGuildScheduledEventUserRemove, gatewayHandlerGuildScheduledEventUserRemove), + + newGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundCreate, gatewayHandlerGuildSoundboardSoundCreate), + newGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundUpdate, gatewayHandlerGuildSoundboardSoundUpdate), + newGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundDelete, gatewayHandlerGuildSoundboardSoundDelete), + newGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundsUpdate, gatewayHandlerGuildSoundboardSoundsUpdate), + newGatewayEventHandler(gateway.EventTypeSoundboardSounds, gatewayHandlerSoundboardSounds), + + newGatewayEventHandler(gateway.EventTypeIntegrationCreate, gatewayHandlerIntegrationCreate), + newGatewayEventHandler(gateway.EventTypeIntegrationUpdate, gatewayHandlerIntegrationUpdate), + newGatewayEventHandler(gateway.EventTypeIntegrationDelete, gatewayHandlerIntegrationDelete), + + newGatewayEventHandler(gateway.EventTypeInteractionCreate, gatewayHandlerInteractionCreate), + + newGatewayEventHandler(gateway.EventTypeInviteCreate, gatewayHandlerInviteCreate), + newGatewayEventHandler(gateway.EventTypeInviteDelete, gatewayHandlerInviteDelete), + + newGatewayEventHandler(gateway.EventTypeMessageCreate, gatewayHandlerMessageCreate), + newGatewayEventHandler(gateway.EventTypeMessageUpdate, gatewayHandlerMessageUpdate), + newGatewayEventHandler(gateway.EventTypeMessageDelete, gatewayHandlerMessageDelete), + newGatewayEventHandler(gateway.EventTypeMessageDeleteBulk, gatewayHandlerMessageDeleteBulk), + + newGatewayEventHandler(gateway.EventTypeMessagePollVoteAdd, gatewayHandlerMessagePollVoteAdd), + newGatewayEventHandler(gateway.EventTypeMessagePollVoteRemove, gatewayHandlerMessagePollVoteRemove), + + newGatewayEventHandler(gateway.EventTypeMessageReactionAdd, gatewayHandlerMessageReactionAdd), + newGatewayEventHandler(gateway.EventTypeMessageReactionRemove, gatewayHandlerMessageReactionRemove), + newGatewayEventHandler(gateway.EventTypeMessageReactionRemoveAll, gatewayHandlerMessageReactionRemoveAll), + newGatewayEventHandler(gateway.EventTypeMessageReactionRemoveEmoji, gatewayHandlerMessageReactionRemoveEmoji), + + newGatewayEventHandler(gateway.EventTypePresenceUpdate, gatewayHandlerPresenceUpdate), + + newGatewayEventHandler(gateway.EventTypeStageInstanceCreate, gatewayHandlerStageInstanceCreate), + newGatewayEventHandler(gateway.EventTypeStageInstanceUpdate, gatewayHandlerStageInstanceUpdate), + newGatewayEventHandler(gateway.EventTypeStageInstanceDelete, gatewayHandlerStageInstanceDelete), + + newGatewayEventHandler(gateway.EventTypeSubscriptionCreate, gatewayHandlerSubscriptionCreate), + newGatewayEventHandler(gateway.EventTypeSubscriptionUpdate, gatewayHandlerSubscriptionUpdate), + newGatewayEventHandler(gateway.EventTypeSubscriptionDelete, gatewayHandlerSubscriptionDelete), + + newGatewayEventHandler(gateway.EventTypeTypingStart, gatewayHandlerTypingStart), + newGatewayEventHandler(gateway.EventTypeUserUpdate, gatewayHandlerUserUpdate), + + newGatewayEventHandler(gateway.EventTypeVoiceChannelEffectSend, gatewayHandlerVoiceChannelEffectSend), + newGatewayEventHandler(gateway.EventTypeVoiceStateUpdate, gatewayHandlerVoiceStateUpdate), + newGatewayEventHandler(gateway.EventTypeVoiceServerUpdate, gatewayHandlerVoiceServerUpdate), + + newGatewayEventHandler(gateway.EventTypeWebhooksUpdate, gatewayHandlerWebhooksUpdate), +} diff --git a/bot/handlers/http_gateway_handler.go b/bot/handlers/http_gateway_handler.go new file mode 100644 index 000000000..5d9e20872 --- /dev/null +++ b/bot/handlers/http_gateway_handler.go @@ -0,0 +1,72 @@ +package handlers + +import ( + "log/slog" + + "github.com/disgoorg/disgo/bot" + "github.com/disgoorg/disgo/httpgateway" +) + +func GetHTTPGatewayHandler() bot.HTTPGatewayEventHandler { + return &httpGatewayHandler{} +} + +type httpGatewayHandler struct{} + +func (h *httpGatewayHandler) HandleHTTPGatewayEvent(client *bot.Client, ack func(), message httpgateway.Message) { + switch event := message.Event.(type) { + case httpgateway.EventPing: + handleHTTPGatewayPing(client, ack, message) + case httpgateway.Event: + handleHTTPGatewayEvent(client, ack, message, event) + case httpgateway.EventUnknown: + handleHTTPGatewayEventUnknown(client, ack, message, event) + default: + client.Logger.Warn("received unknown http gateway event", slog.Any("event", event)) + } +} + +func handleHTTPGatewayEvent(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event) { + switch data := event.Data.(type) { + case httpgateway.EventDataApplicationAuthorized: + handleHTTPGatewayEventDataApplicationAuthorized(client, ack, message, event, data) + case httpgateway.EventDataApplicationDeauthorized: + handleHTTPGatewayEventDataApplicationDeauthorized(client, ack, message, event, data) + case httpgateway.EventDataEntitlementCreate: + handleHTTPGatewayEventDataEntitlementCreate(client, ack, message, event, data) + case httpgateway.EventDataQuestUserEnrollment: + handleHTTPGatewayEventDataQuestUserEnrollment(client, ack, message, event, data) + case httpgateway.EventDataUnknown: + handleHTTPGatewayEventDataUnknown(client, ack, message, event, data) + default: + client.Logger.Warn("received unknown http gateway event data", slog.Any("data", data)) + } +} + +func handleHTTPGatewayPing(client *bot.Client, ack func(), message httpgateway.Message) { + ack() + client.Logger.Debug("received http gateway ping", slog.String("application_id", message.ApplicationID.String()), slog.Int("version", message.Version)) +} + +func handleHTTPGatewayEventDataApplicationAuthorized(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event, data httpgateway.EventDataApplicationAuthorized) { +} + +func handleHTTPGatewayEventDataApplicationDeauthorized(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event, data httpgateway.EventDataApplicationDeauthorized) { + +} + +func handleHTTPGatewayEventDataEntitlementCreate(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event, data httpgateway.EventDataEntitlementCreate) { + +} + +func handleHTTPGatewayEventDataQuestUserEnrollment(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event, data httpgateway.EventDataQuestUserEnrollment) { + +} + +func handleHTTPGatewayEventDataUnknown(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event, data httpgateway.EventDataUnknown) { + +} + +func handleHTTPGatewayEventUnknown(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.EventUnknown) { + +} diff --git a/bot/handlers/http_interaction_handler.go b/bot/handlers/http_interaction_handler.go new file mode 100644 index 000000000..f946e6661 --- /dev/null +++ b/bot/handlers/http_interaction_handler.go @@ -0,0 +1,31 @@ +package handlers + +import ( + "log/slog" + + "github.com/disgoorg/disgo/bot" + "github.com/disgoorg/disgo/discord" + "github.com/disgoorg/disgo/httpgateway" +) + +// GetHTTPInteractionHandler returns the default httpserver.Server event handler for processing the raw payload which gets passed into the bot.EventManager +func GetHTTPInteractionHandler() bot.HTTPInteractionEventHandler { + return &httpInteractionHandler{} +} + +type httpInteractionHandler struct{} + +func (h *httpInteractionHandler) HandleHTTPInteraction(client *bot.Client, respond httpgateway.RespondFunc, event httpgateway.EventInteractionCreate) { + // we just want to pong all pings + // no need for any event + if event.Type() == discord.InteractionTypePing { + client.Logger.Debug("received http interaction ping. responding with pong") + if err := respond(discord.InteractionResponse{ + Type: discord.InteractionResponseTypePong, + }); err != nil { + client.Logger.Error("failed to respond to http interaction ping", slog.Any("err", err)) + } + return + } + handleInteraction(client, -1, -1, respond, event.Interaction) +} diff --git a/bot/handlers/interaction_create_handler.go b/bot/handlers/interaction_create_handler.go index 8c39bb4f7..b31a7bd74 100644 --- a/bot/handlers/interaction_create_handler.go +++ b/bot/handlers/interaction_create_handler.go @@ -8,7 +8,7 @@ import ( "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/gateway" - "github.com/disgoorg/disgo/httpserver" + "github.com/disgoorg/disgo/httpgateway" "github.com/disgoorg/disgo/rest" ) @@ -16,7 +16,7 @@ func gatewayHandlerInteractionCreate(client *bot.Client, sequenceNumber int, sha handleInteraction(client, sequenceNumber, shardID, nil, event.Interaction) } -func respond(client *bot.Client, respondFunc httpserver.RespondFunc, interaction discord.Interaction) events.InteractionResponderFunc { +func respond(client *bot.Client, respondFunc httpgateway.RespondFunc, interaction discord.Interaction) events.InteractionResponderFunc { return func(responseType discord.InteractionResponseType, data discord.InteractionResponseData, opts ...rest.RequestOpt) error { response := discord.InteractionResponse{ Type: responseType, @@ -29,7 +29,7 @@ func respond(client *bot.Client, respondFunc httpserver.RespondFunc, interaction } } -func handleInteraction(client *bot.Client, sequenceNumber int, shardID int, respondFunc httpserver.RespondFunc, interaction discord.Interaction) { +func handleInteraction(client *bot.Client, sequenceNumber int, shardID int, respondFunc httpgateway.RespondFunc, interaction discord.Interaction) { genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) client.EventManager.DispatchEvent(&events.InteractionCreate{ diff --git a/bot/handlers/interaction_create_http_handler.go b/bot/handlers/interaction_create_http_handler.go deleted file mode 100644 index 87ac11f49..000000000 --- a/bot/handlers/interaction_create_http_handler.go +++ /dev/null @@ -1,28 +0,0 @@ -package handlers - -import ( - "log/slog" - - "github.com/disgoorg/disgo/bot" - "github.com/disgoorg/disgo/discord" - "github.com/disgoorg/disgo/httpserver" -) - -var _ bot.HTTPServerEventHandler = (*httpserverHandlerInteractionCreate)(nil) - -type httpserverHandlerInteractionCreate struct{} - -func (h *httpserverHandlerInteractionCreate) HandleHTTPEvent(client *bot.Client, respondFunc httpserver.RespondFunc, event httpserver.EventInteractionCreate) { - // we just want to pong all pings - // no need for any event - if event.Type() == discord.InteractionTypePing { - client.Logger.Debug("received http interaction ping. responding with pong") - if err := respondFunc(discord.InteractionResponse{ - Type: discord.InteractionResponseTypePong, - }); err != nil { - client.Logger.Error("failed to respond to http interaction ping", slog.Any("err", err)) - } - return - } - handleInteraction(client, -1, -1, respondFunc, event.Interaction) -} diff --git a/discord/channel.go b/discord/channel.go index 69cee4fa3..a7d5209d3 100644 --- a/discord/channel.go +++ b/discord/channel.go @@ -502,7 +502,7 @@ func (c GroupDMChannel) CreatedAt() time.Time { return c.id.Time() } -// IconURL returns the icon URL of this group DM or nil if not set +// IconURL returns the icon InteractionURL of this group DM or nil if not set func (c GroupDMChannel) IconURL(opts ...CDNOpt) *string { if c.icon == nil { return nil diff --git a/discord/component.go b/discord/component.go index f61637041..1cd07cfaf 100644 --- a/discord/component.go +++ b/discord/component.go @@ -389,7 +389,7 @@ const ( ButtonStylePremium ) -// NewButton creates a new [ButtonComponent] with the provided parameters. Link ButtonComponent(s) need a URL and other ButtonComponent(s) need a customID +// NewButton creates a new [ButtonComponent] with the provided parameters. Link ButtonComponent(s) need a InteractionURL and other ButtonComponent(s) need a customID func NewButton(style ButtonStyle, label string, customID string, url string, skuID snowflake.ID) ButtonComponent { return ButtonComponent{ Style: style, diff --git a/discord/embed_builder.go b/discord/embed_builder.go index d8fb73ab5..03489e1ec 100644 --- a/discord/embed_builder.go +++ b/discord/embed_builder.go @@ -68,7 +68,7 @@ func (b *EmbedBuilder) SetAuthorNamef(name string, a ...any) *EmbedBuilder { return b.SetAuthorName(fmt.Sprintf(name, a...)) } -// SetAuthorURL sets the author URL of the EmbedBuilder +// SetAuthorURL sets the author InteractionURL of the EmbedBuilder func (b *EmbedBuilder) SetAuthorURL(url string) *EmbedBuilder { if b.Author == nil { b.Author = &EmbedAuthor{} @@ -77,7 +77,7 @@ func (b *EmbedBuilder) SetAuthorURL(url string) *EmbedBuilder { return b } -// SetAuthorURLf sets the author URL of the EmbedBuilder with format +// SetAuthorURLf sets the author InteractionURL of the EmbedBuilder with format func (b *EmbedBuilder) SetAuthorURLf(url string, a ...any) *EmbedBuilder { return b.SetAuthorURL(fmt.Sprintf(url, a...)) } @@ -174,13 +174,13 @@ func (b *EmbedBuilder) SetThumbnailf(url string, a ...any) *EmbedBuilder { return b.SetThumbnail(fmt.Sprintf(url, a...)) } -// SetURL sets the URL of the EmbedBuilder +// SetURL sets the InteractionURL of the EmbedBuilder func (b *EmbedBuilder) SetURL(url string) *EmbedBuilder { b.URL = url return b } -// SetURLf sets the URL of the EmbedBuilder with format +// SetURLf sets the InteractionURL of the EmbedBuilder with format func (b *EmbedBuilder) SetURLf(url string, a ...any) *EmbedBuilder { return b.SetURL(fmt.Sprintf(url, a...)) } diff --git a/discord/guild_scheduled_event.go b/discord/guild_scheduled_event.go index 94eda5507..3fa757bd5 100644 --- a/discord/guild_scheduled_event.go +++ b/discord/guild_scheduled_event.go @@ -32,7 +32,7 @@ func (e GuildScheduledEvent) CreatedAt() time.Time { return e.ID.Time() } -// CoverURL returns the cover URL if set or nil +// CoverURL returns the cover InteractionURL if set or nil func (e GuildScheduledEvent) CoverURL(opts ...CDNOpt) *string { if e.Image == nil { return nil diff --git a/discord/invite.go b/discord/invite.go index 95f4dc6e5..924999c47 100644 --- a/discord/invite.go +++ b/discord/invite.go @@ -72,7 +72,7 @@ type InviteChannel struct { Icon *string `json:"icon,omitempty"` } -// IconURL returns the Icon URL of this channel. +// IconURL returns the Icon InteractionURL of this channel. // This will be nil for every ChannelType except ChannelTypeGroupDM func (c InviteChannel) IconURL(opts ...CDNOpt) *string { if c.Icon == nil { diff --git a/discord/member.go b/discord/member.go index 51e6c7f27..c8fbb70bd 100644 --- a/discord/member.go +++ b/discord/member.go @@ -49,7 +49,7 @@ func (m Member) EffectiveName() string { return m.User.EffectiveName() } -// EffectiveAvatarURL returns the guild-specific avatar URL of the user if set, falling back to the effective avatar URL of the user +// EffectiveAvatarURL returns the guild-specific avatar InteractionURL of the user if set, falling back to the effective avatar InteractionURL of the user func (m Member) EffectiveAvatarURL(opts ...CDNOpt) string { if m.Avatar == nil { return m.User.EffectiveAvatarURL(opts...) @@ -57,7 +57,7 @@ func (m Member) EffectiveAvatarURL(opts ...CDNOpt) string { return formatAssetURL(MemberAvatar, opts, m.GuildID, m.User.ID, *m.Avatar) } -// AvatarURL returns the guild-specific avatar URL of the user if set or nil +// AvatarURL returns the guild-specific avatar InteractionURL of the user if set or nil func (m Member) AvatarURL(opts ...CDNOpt) *string { if m.Avatar == nil { return nil @@ -66,7 +66,7 @@ func (m Member) AvatarURL(opts ...CDNOpt) *string { return &url } -// EffectiveBannerURL returns the guild-specific banner URL of the user if set, falling back to the banner URL of the user +// EffectiveBannerURL returns the guild-specific banner InteractionURL of the user if set, falling back to the banner InteractionURL of the user func (m Member) EffectiveBannerURL(opts ...CDNOpt) string { if m.Banner == nil { if banner := m.User.BannerURL(opts...); banner != nil { @@ -77,7 +77,7 @@ func (m Member) EffectiveBannerURL(opts ...CDNOpt) string { return formatAssetURL(MemberBanner, opts, m.GuildID, m.User.ID, *m.Banner) } -// BannerURL returns the guild-specific banner URL of the user if set or nil +// BannerURL returns the guild-specific banner InteractionURL of the user if set or nil func (m Member) BannerURL(opts ...CDNOpt) *string { if m.Banner == nil { return nil @@ -86,7 +86,7 @@ func (m Member) BannerURL(opts ...CDNOpt) *string { return &url } -// AvatarDecorationURL returns the avatar decoration URL if set or nil +// AvatarDecorationURL returns the avatar decoration InteractionURL if set or nil func (m Member) AvatarDecorationURL(opts ...CDNOpt) *string { if m.AvatarDecorationData == nil { return nil diff --git a/discord/message.go b/discord/message.go index bf774688e..b995479d1 100644 --- a/discord/message.go +++ b/discord/message.go @@ -165,7 +165,7 @@ func (m Message) AllComponents() iter.Seq[Component] { return componentIter(m.Components) } -// JumpURL returns the URL which can be used to jump to the message in the discord client. +// JumpURL returns the InteractionURL which can be used to jump to the message in the discord client. func (m Message) JumpURL() string { guildID := "@me" if m.GuildID != nil { diff --git a/discord/user.go b/discord/user.go index 0fd531457..6d5b72a0a 100644 --- a/discord/user.go +++ b/discord/user.go @@ -103,7 +103,7 @@ func (u User) EffectiveName() string { return u.Username } -// EffectiveAvatarURL returns the avatar URL of the user if set, falling back to the default avatar URL +// EffectiveAvatarURL returns the avatar InteractionURL of the user if set, falling back to the default avatar InteractionURL func (u User) EffectiveAvatarURL(opts ...CDNOpt) string { if u.Avatar == nil { return u.DefaultAvatarURL(opts...) @@ -111,7 +111,7 @@ func (u User) EffectiveAvatarURL(opts ...CDNOpt) string { return formatAssetURL(UserAvatar, opts, u.ID, *u.Avatar) } -// AvatarURL returns the avatar URL of the user if set or nil +// AvatarURL returns the avatar InteractionURL of the user if set or nil func (u User) AvatarURL(opts ...CDNOpt) *string { if u.Avatar == nil { return nil @@ -120,7 +120,7 @@ func (u User) AvatarURL(opts ...CDNOpt) *string { return &url } -// DefaultAvatarURL calculates and returns the default avatar URL +// DefaultAvatarURL calculates and returns the default avatar InteractionURL func (u User) DefaultAvatarURL(opts ...CDNOpt) string { discriminator, err := strconv.Atoi(u.Discriminator) if err != nil { @@ -133,7 +133,7 @@ func (u User) DefaultAvatarURL(opts ...CDNOpt) string { return formatAssetURL(DefaultUserAvatar, opts, index) } -// BannerURL returns the banner URL if set or nil +// BannerURL returns the banner InteractionURL if set or nil func (u User) BannerURL(opts ...CDNOpt) *string { if u.Banner == nil { return nil @@ -142,7 +142,7 @@ func (u User) BannerURL(opts ...CDNOpt) *string { return &url } -// AvatarDecorationURL returns the avatar decoration URL if set or nil +// AvatarDecorationURL returns the avatar decoration InteractionURL if set or nil func (u User) AvatarDecorationURL(opts ...CDNOpt) *string { if u.AvatarDecorationData == nil { return nil @@ -151,7 +151,7 @@ func (u User) AvatarDecorationURL(opts ...CDNOpt) *string { return &url } -// GuildTagURL returns the server tag badge URL if the user has a primary discord.Guild or nil +// GuildTagURL returns the server tag badge InteractionURL if the user has a primary discord.Guild or nil func (u User) GuildTagURL(opts ...CDNOpt) *string { if u.PrimaryGuild == nil { return nil diff --git a/disgo.go b/disgo.go index 2f8f31778..c353c0132 100644 --- a/disgo.go +++ b/disgo.go @@ -6,7 +6,7 @@ // // # Bot // -// Package bot connects the Gateway/Sharding, HTTPServer, Cache, Rest & Events packages into a single high level client interface. +// Package bot connects the Gateway/Sharding, HTTPGateway, Cache, Rest & Events packages into a single high level client interface. // // # Gateway // @@ -20,7 +20,7 @@ // // Package cache provides a generic cache interface for Discord entities. // -// # HTTPServer +// # HTTPGateway // // Package httpserver is used to interact with the Discord outgoing webhooks for interactions. // @@ -85,8 +85,9 @@ func getVersion() string { func New(token string, opts ...bot.ConfigOpt) (*bot.Client, error) { return bot.BuildClient(token, opts, - handlers.GetGatewayHandlers(), - handlers.GetHTTPServerHandler(), + handlers.GetGatewayHandler(), + handlers.GetHTTPInteractionHandler(), + handlers.GetHTTPGatewayHandler(), runtime.GOOS, Name, GitHub, diff --git a/gateway/gateway.go b/gateway/gateway.go index 774e573bc..4ed67be53 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -89,7 +89,7 @@ const ( type ( // EventHandlerFunc is a function that is called when an event is received. - EventHandlerFunc func(gateway Gateway, eventType EventType, sequenceNumber int, event EventData) + EventHandlerFunc func(gateway Gateway, message Message) // CreateFunc is a type that is used to create a new Gateway(s). CreateFunc func(token string, eventHandlerFunc EventHandlerFunc, closeHandlerFUnc CloseHandlerFunc, opts ...ConfigOpt) Gateway @@ -579,17 +579,22 @@ loop: // push message to the command manager if g.config.EnableRawEvents { - g.eventHandlerFunc(g, EventTypeRaw, message.S, EventRaw{ - EventType: message.T, - Payload: bytes.NewReader(message.RawD), + g.eventHandlerFunc(g, Message{ + Op: OpcodeDispatch, + T: EventTypeRaw, + S: message.S, + D: EventRaw{ + EventType: message.T, + Payload: bytes.NewReader(message.RawD), + }, + RawD: message.RawD, }) } if unknownEvent, ok := eventData.(EventUnknown); ok { g.config.Logger.Debug("unknown event received", slog.String("event", string(message.T)), slog.String("data", string(unknownEvent))) - continue } - g.eventHandlerFunc(g, message.T, message.S, eventData) + g.eventHandlerFunc(g, message) case OpcodeHeartbeat: g.sendHeartbeat() @@ -622,9 +627,15 @@ loop: case OpcodeHeartbeatACK: newHeartbeat := time.Now().UTC() - g.eventHandlerFunc(g, EventTypeHeartbeatAck, message.S, EventHeartbeatAck{ - LastHeartbeat: g.lastHeartbeatReceived, - NewHeartbeat: newHeartbeat, + g.eventHandlerFunc(g, Message{ + Op: OpcodeDispatch, + T: EventTypeHeartbeatAck, + S: message.S, + D: EventHeartbeatAck{ + LastHeartbeat: g.lastHeartbeatReceived, + NewHeartbeat: newHeartbeat, + }, + RawD: message.RawD, }) g.lastHeartbeatReceived = newHeartbeat @@ -643,7 +654,9 @@ func (g *gatewayImpl) parseMessage(mt int, r io.Reader) (Message, error) { if err != nil { return Message{}, fmt.Errorf("failed to decompress zlib: %w", err) } - defer reader.Close() + defer func() { + _ = reader.Close() + }() r = reader } diff --git a/gateway/gateway_config.go b/gateway/gateway_config.go index 3c321904a..33d1ba5f5 100644 --- a/gateway/gateway_config.go +++ b/gateway/gateway_config.go @@ -121,7 +121,7 @@ func WithCompress(compress bool) ConfigOpt { } } -// WithURL sets the Gateway URL for the Gateway. +// WithURL sets the Gateway InteractionURL for the Gateway. func WithURL(url string) ConfigOpt { return func(config *config) { config.URL = url diff --git a/httpserver/config.go b/httpgateway/config.go similarity index 64% rename from httpserver/config.go rename to httpgateway/config.go index b3c532ad5..6e7e0e3c9 100644 --- a/httpserver/config.go +++ b/httpgateway/config.go @@ -1,4 +1,4 @@ -package httpserver +package httpgateway import ( "log/slog" @@ -7,24 +7,26 @@ import ( func defaultConfig() config { return config{ - Logger: slog.Default(), - HTTPServer: &http.Server{}, - ServeMux: http.NewServeMux(), - URL: "/interactions/callback", - Address: ":80", - Verifier: DefaultVerifier{}, + Logger: slog.Default(), + HTTPServer: &http.Server{}, + ServeMux: http.NewServeMux(), + InteractionURL: "/interactions/callback", + EventURL: "/events/callback", + Address: ":80", + Verifier: DefaultVerifier{}, } } type config struct { - Logger *slog.Logger - HTTPServer *http.Server - ServeMux *http.ServeMux - URL string - Address string - CertFile string - KeyFile string - Verifier Verifier + Logger *slog.Logger + HTTPServer *http.Server + ServeMux *http.ServeMux + InteractionURL string + EventURL string + Address string + CertFile string + KeyFile string + Verifier Verifier } // ConfigOpt is a type alias for a function that takes a config and is used to configure your Server. @@ -58,10 +60,17 @@ func WithServeMux(serveMux *http.ServeMux) ConfigOpt { } } -// WithURL sets the URL of the config. -func WithURL(url string) ConfigOpt { +// WithInteractionURL sets the InteractionURL of the config. +func WithInteractionURL(url string) ConfigOpt { return func(config *config) { - config.URL = url + config.InteractionURL = url + } +} + +// WithEventURL sets the EventURL of the config. +func WithEventURL(url string) ConfigOpt { + return func(config *config) { + config.EventURL = url } } diff --git a/httpgateway/event.go b/httpgateway/event.go new file mode 100644 index 000000000..10780278e --- /dev/null +++ b/httpgateway/event.go @@ -0,0 +1,56 @@ +package httpgateway + +import ( + "bytes" + "context" + "encoding/json" + "io" + "log/slog" + "net/http" + "sync" + "time" +) + +func HandleEvent(verifier Verifier, publicKey PublicKey, logger *slog.Logger, handleFunc EventHandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + defer func() { + _ = r.Body.Close() + }() + + if ok := VerifyRequest(verifier, r, publicKey); !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + data, _ := io.ReadAll(r.Body) + logger.Debug("received http interaction with invalid signature", slog.String("body", string(data))) + return + } + + buff := new(bytes.Buffer) + rqData, _ := io.ReadAll(io.TeeReader(r.Body, buff)) + logger.Debug("received http event", slog.String("body", string(rqData))) + + var v Message + if err := json.NewDecoder(buff).Decode(&v); err != nil { + logger.Error("error while decoding event", slog.Any("err", err)) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + ackChan := make(chan struct{}, 1) + defer close(ackChan) + + go handleFunc(sync.OnceFunc(func() { + ackChan <- struct{}{} + }), v) + + ctx, cancel := context.WithTimeout(context.Background(), 3100*time.Millisecond) + defer cancel() + + select { + case <-ackChan: + w.WriteHeader(http.StatusNoContent) + case <-ctx.Done(): + logger.Debug("event timed out") + http.Error(w, "Event Timed Out", http.StatusRequestTimeout) + } + } +} diff --git a/httpgateway/http_gateway.go b/httpgateway/http_gateway.go new file mode 100644 index 000000000..57ef91f9b --- /dev/null +++ b/httpgateway/http_gateway.go @@ -0,0 +1,80 @@ +package httpgateway + +import ( + "context" + "encoding/hex" + "errors" + "fmt" + "log/slog" + "net/http" + + "github.com/disgoorg/disgo/discord" +) + +type ( + EventHandlerFunc func(ack func(), message Message) + + // InteractionHandlerFunc is used to handle events from Discord's Outgoing Webhooks + InteractionHandlerFunc func(responseFunc RespondFunc, event EventInteractionCreate) + + // RespondFunc is used to respond to Discord's Outgoing Webhooks + RespondFunc func(response discord.InteractionResponse) error +) + +type Gateway interface { + Start() + + Close(ctx context.Context) +} + +var _ Gateway = (*gatewayImpl)(nil) + +// New creates a new Server with the given publicKey eventHandlerFunc and ConfigOpt(s) +func New(publicKey string, interactionHandler InteractionHandlerFunc, eventHandler EventHandlerFunc, opts ...ConfigOpt) (Gateway, error) { + cfg := defaultConfig() + cfg.apply(opts) + + hexDecodedKey, err := hex.DecodeString(publicKey) + if err != nil { + return nil, fmt.Errorf("error decoding public key: %w", err) + } + + return &gatewayImpl{ + config: cfg, + publicKey: hexDecodedKey, + interactionHandler: interactionHandler, + eventHandler: eventHandler, + verifier: cfg.Verifier, + }, nil +} + +type gatewayImpl struct { + config config + publicKey PublicKey + interactionHandler InteractionHandlerFunc + eventHandler EventHandlerFunc + verifier Verifier +} + +func (s *gatewayImpl) Start() { + s.config.ServeMux.Handle(s.config.InteractionURL, HandleInteraction(s.verifier, s.publicKey, s.config.Logger, s.interactionHandler)) + s.config.ServeMux.Handle(s.config.EventURL, HandleEvent(s.verifier, s.publicKey, s.config.Logger, s.eventHandler)) + s.config.HTTPServer.Addr = s.config.Address + s.config.HTTPServer.Handler = s.config.ServeMux + + go func() { + var err error + if s.config.CertFile != "" && s.config.KeyFile != "" { + err = s.config.HTTPServer.ListenAndServeTLS(s.config.CertFile, s.config.KeyFile) + } else { + err = s.config.HTTPServer.ListenAndServe() + } + if !errors.Is(err, http.ErrServerClosed) { + s.config.Logger.Error("error while running http server", slog.Any("err", err)) + } + }() +} + +func (s *gatewayImpl) Close(ctx context.Context) { + _ = s.config.HTTPServer.Shutdown(ctx) +} diff --git a/httpgateway/http_gateway_messages.go b/httpgateway/http_gateway_messages.go new file mode 100644 index 000000000..020ca06b5 --- /dev/null +++ b/httpgateway/http_gateway_messages.go @@ -0,0 +1,177 @@ +package httpgateway + +import ( + "fmt" + "time" + + "github.com/disgoorg/json/v2" + "github.com/disgoorg/snowflake/v2" + + "github.com/disgoorg/disgo/discord" +) + +type MessageType int + +const ( + MessageTypePing MessageType = iota + MessageTypeEvent +) + +type Message struct { + Version int `json:"version"` + ApplicationID snowflake.ID `json:"application_id"` + Type MessageType `json:"type"` + Event MessageEvent `json:"event,omitempty"` + RawEvent json.RawMessage `json:"-"` +} + +func (m *Message) UnmarshalJSON(data []byte) error { + var v struct { + Version int `json:"version"` + ApplicationID snowflake.ID `json:"application_id"` + Type MessageType `json:"type"` + Event json.RawMessage `json:"event,omitempty"` + } + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + var ( + messageEvent MessageEvent + err error + ) + + switch v.Type { + case MessageTypePing: + messageEvent = EventPing{} + case MessageTypeEvent: + var d Event + err = json.Unmarshal(data, &v.Event) + messageEvent = d + default: + var d EventUnknown + err = json.Unmarshal(data, &v.Event) + messageEvent = d + } + if err != nil { + return fmt.Errorf("failed to unmarshal message data: %s: %w", string(data), err) + } + m.Version = v.Version + m.ApplicationID = v.ApplicationID + m.Type = v.Type + m.Event = messageEvent + m.RawEvent = v.Event + return nil +} + +type MessageEvent interface { + messageEvent() +} + +type EventPing struct{} + +func (EventPing) messageEvent() {} + +type EventUnknown json.RawMessage + +func (EventUnknown) messageEvent() {} + +type EventType string + +const ( + EventTypeApplicationAuthorized EventType = "APPLICATION_AUTHORIZED" + EventTypeApplicationDeauthorized EventType = "APPLICATION_DEAUTHORIZED" + EventTypeEntitlementCreate EventType = "ENTITLEMENT_CREATE" + EventTypeQuestUserEnrollment EventType = "QUEST_USER_ENROLLMENT" +) + +type Event struct { + Type EventType `json:"type"` + Timestamp time.Time `json:"timestamp"` + Data EventData `json:"data"` + RawData json.RawMessage `json:"-"` +} + +func (Event) messageEvent() {} + +func (m *Event) UnmarshalJSON(data []byte) error { + var v struct { + Type EventType `json:"type"` + Timestamp time.Time `json:"timestamp"` + Data json.RawMessage `json:"data"` + } + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + var ( + eventData EventData + err error + ) + + switch v.Type { + case EventTypeApplicationAuthorized: + var d EventDataApplicationAuthorized + err = json.Unmarshal(data, &v.Data) + eventData = d + case EventTypeApplicationDeauthorized: + var d EventDataApplicationDeauthorized + err = json.Unmarshal(data, &v.Data) + eventData = d + case EventTypeEntitlementCreate: + var d EventDataEntitlementCreate + err = json.Unmarshal(data, &v.Data) + eventData = d + case EventTypeQuestUserEnrollment: + var d EventDataQuestUserEnrollment + err = json.Unmarshal(data, &v.Data) + eventData = d + default: + var d EventDataUnknown + err = json.Unmarshal(data, &v.Data) + eventData = d + } + if err != nil { + return fmt.Errorf("failed to unmarshal message data: %s: %w", string(data), err) + } + m.Type = v.Type + m.Timestamp = v.Timestamp + m.Data = eventData + m.RawData = v.Data + return nil +} + +type EventData interface { + eventData() +} + +type EventDataApplicationAuthorized struct { + IntegrationType discord.IntegrationType `json:"integration_type"` + User discord.User `json:"user"` + Scopes []discord.OAuth2Scope `json:"scopes"` + Guild discord.Guild `json:"guild"` +} + +func (EventDataApplicationAuthorized) eventData() {} + +type EventDataApplicationDeauthorized struct { + User discord.User `json:"user"` +} + +func (EventDataApplicationDeauthorized) eventData() {} + +type EventDataEntitlementCreate struct { + discord.Entitlement +} + +func (EventDataEntitlementCreate) eventData() {} + +type EventDataQuestUserEnrollment struct { + // This event cannot be received by apps at this time. It's documented because it appears on the Webhooks settings page. +} + +func (EventDataQuestUserEnrollment) eventData() {} + +type EventDataUnknown json.RawMessage + +func (EventDataUnknown) eventData() {} diff --git a/httpserver/handler.go b/httpgateway/interaction.go similarity index 74% rename from httpserver/handler.go rename to httpgateway/interaction.go index 9704e6a1e..8ca45235e 100644 --- a/httpserver/handler.go +++ b/httpgateway/interaction.go @@ -1,9 +1,8 @@ -package httpserver +package httpgateway import ( "bytes" "context" - "encoding/hex" "io" "log/slog" "net/http" @@ -15,14 +14,6 @@ import ( "github.com/disgoorg/disgo/discord" ) -type ( - // EventHandlerFunc is used to handle events from Discord's Outgoing Webhooks - EventHandlerFunc func(responseFunc RespondFunc, event EventInteractionCreate) - - // RespondFunc is used to respond to Discord's Outgoing Webhooks - RespondFunc func(response discord.InteractionResponse) error -) - // EventInteractionCreate is the event payload when an interaction is created via Discord's Outgoing Webhooks type EventInteractionCreate struct { discord.Interaction @@ -41,49 +32,6 @@ func (e EventInteractionCreate) MarshalJSON() ([]byte, error) { return json.Marshal(e.Interaction) } -// VerifyRequest implements the verification side of the discord interactions api signing algorithm, as documented here: https://discord.com/developers/docs/interactions/slash-commands#security-and-authorization -// Credit: https://github.com/bsdlp/discord-interactions-go/blob/main/interactions/verify.go -func VerifyRequest(verifier Verifier, r *http.Request, key PublicKey) bool { - var msg bytes.Buffer - - signature := r.Header.Get("X-Signature-Ed25519") - if signature == "" { - return false - } - - sig, err := hex.DecodeString(signature) - if err != nil { - return false - } - - if len(sig) != verifier.SignatureSize() || sig[63]&224 != 0 { - return false - } - - timestamp := r.Header.Get("X-Signature-Timestamp") - if timestamp == "" { - return false - } - - msg.WriteString(timestamp) - - defer func() { - _ = r.Body.Close() - }() - var body bytes.Buffer - - defer func() { - r.Body = io.NopCloser(&body) - }() - - _, err = io.Copy(&msg, io.TeeReader(r.Body, &body)) - if err != nil { - return false - } - - return verifier.Verify(key, msg.Bytes(), sig) -} - type replyStatus int const ( @@ -93,8 +41,12 @@ const ( ) // HandleInteraction handles an interaction from Discord's Outgoing Webhooks. It verifies and parses the interaction and then calls the passed EventHandlerFunc. -func HandleInteraction(verifier Verifier, publicKey PublicKey, logger *slog.Logger, handleFunc EventHandlerFunc) http.HandlerFunc { +func HandleInteraction(verifier Verifier, publicKey PublicKey, logger *slog.Logger, handleFunc InteractionHandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + defer func() { + _ = r.Body.Close() + }() + if ok := VerifyRequest(verifier, r, publicKey); !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) data, _ := io.ReadAll(r.Body) @@ -102,10 +54,6 @@ func HandleInteraction(verifier Verifier, publicKey PublicKey, logger *slog.Logg return } - defer func() { - _ = r.Body.Close() - }() - buff := new(bytes.Buffer) rqData, _ := io.ReadAll(io.TeeReader(r.Body, buff)) logger.Debug("received http interaction", slog.String("body", string(rqData))) diff --git a/httpgateway/verify.go b/httpgateway/verify.go new file mode 100644 index 000000000..dcc7c196f --- /dev/null +++ b/httpgateway/verify.go @@ -0,0 +1,76 @@ +package httpgateway + +import ( + "bytes" + "crypto/ed25519" + "encoding/hex" + "io" + "net/http" +) + +// PublicKey is the type of Ed25519 public keys. +type PublicKey = []byte + +// Verifier is used to verify Ed25519 signatures. +type Verifier interface { + // Verify verifies the signature of the message using the public key. + // It returns true if the signature is valid, false otherwise. + Verify(publicKey PublicKey, message []byte, sig []byte) bool + + // SignatureSize is the size, in bytes, of signatures generated and verified by this package. + SignatureSize() int +} + +// DefaultVerifier is the default implementation of the Verifier interface. +type DefaultVerifier struct{} + +func (DefaultVerifier) Verify(publicKey PublicKey, message []byte, sig []byte) bool { + return ed25519.Verify(publicKey, message, sig) +} + +func (DefaultVerifier) SignatureSize() int { + return ed25519.SignatureSize +} + +// VerifyRequest implements the verification side of the discord interactions api signing algorithm, as documented here: https://discord.com/developers/docs/interactions/slash-commands#security-and-authorization +// Credit: https://github.com/bsdlp/discord-interactions-go/blob/main/interactions/verify.go +func VerifyRequest(verifier Verifier, r *http.Request, key PublicKey) bool { + var msg bytes.Buffer + + signature := r.Header.Get("X-Signature-Ed25519") + if signature == "" { + return false + } + + sig, err := hex.DecodeString(signature) + if err != nil { + return false + } + + if len(sig) != verifier.SignatureSize() || sig[63]&224 != 0 { + return false + } + + timestamp := r.Header.Get("X-Signature-Timestamp") + if timestamp == "" { + return false + } + + msg.WriteString(timestamp) + + defer func() { + _ = r.Body.Close() + }() + var body bytes.Buffer + + defer func() { + r.Body = io.NopCloser(&body) + }() + + _, err = io.Copy(&msg, io.TeeReader(r.Body, &body)) + if err != nil { + return false + } + + return verifier.Verify(key, msg.Bytes(), sig) +} diff --git a/httpserver/README.md b/httpserver/README.md deleted file mode 100644 index 0cc025ac0..000000000 --- a/httpserver/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# httpserver - -HTTPServer uses `crypto/ed25519` by default for signing verification. You can inject your own implementation by setting the `Verify` in this package. - -## Example - -For a simple command [`crypto/ed25519`](https://pkg.go.dev/crypto/ed25519) takes around 0.54ms on my machine and [`github.com/oasisprotocol/curve25519-voi`](https://pkg.go.dev/github.com/oasisprotocol/curve25519-voi) takes about 0.13ms. - -```go -package main - -import ( - "github.com/oasisprotocol/curve25519-voi/primitives/ed25519" - "github.com/disgoorg/disgo/httpserver" -) -func main() { - httpserver.Verify = func(publicKey httpserver.PublicKey, message, sig []byte) bool { - return ed25519.Verify(publicKey, message, sig) - } -} - -``` diff --git a/httpserver/ed25519.go b/httpserver/ed25519.go deleted file mode 100644 index 556df6508..000000000 --- a/httpserver/ed25519.go +++ /dev/null @@ -1,27 +0,0 @@ -package httpserver - -import "crypto/ed25519" - -// PublicKey is the type of Ed25519 public keys. -type PublicKey = []byte - -// Verifier is used to verify Ed25519 signatures. -type Verifier interface { - // Verify verifies the signature of the message using the public key. - // It returns true if the signature is valid, false otherwise. - Verify(publicKey PublicKey, message []byte, sig []byte) bool - - // SignatureSize is the size, in bytes, of signatures generated and verified by this package. - SignatureSize() int -} - -// DefaultVerifier is the default implementation of the Verifier interface. -type DefaultVerifier struct{} - -func (DefaultVerifier) Verify(publicKey PublicKey, message []byte, sig []byte) bool { - return ed25519.Verify(publicKey, message, sig) -} - -func (DefaultVerifier) SignatureSize() int { - return ed25519.SignatureSize -} diff --git a/httpserver/server.go b/httpserver/server.go deleted file mode 100644 index 00586e3bb..000000000 --- a/httpserver/server.go +++ /dev/null @@ -1,68 +0,0 @@ -package httpserver - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "log/slog" - "net/http" -) - -// Server is used for receiving Discord's interactions via Outgoing Webhooks -type Server interface { - // Start starts the Server - Start() - - // Close closes the Server - Close(ctx context.Context) -} - -var _ Server = (*serverImpl)(nil) - -// New creates a new Server with the given publicKey eventHandlerFunc and ConfigOpt(s) -func New(publicKey string, eventHandlerFunc EventHandlerFunc, opts ...ConfigOpt) (Server, error) { - cfg := defaultConfig() - cfg.apply(opts) - - hexDecodedKey, err := hex.DecodeString(publicKey) - if err != nil { - return nil, fmt.Errorf("error decoding public key: %w", err) - } - - return &serverImpl{ - config: cfg, - publicKey: hexDecodedKey, - eventHandlerFunc: eventHandlerFunc, - verifier: cfg.Verifier, - }, nil -} - -type serverImpl struct { - config config - publicKey PublicKey - eventHandlerFunc EventHandlerFunc - verifier Verifier -} - -func (s *serverImpl) Start() { - s.config.ServeMux.Handle(s.config.URL, HandleInteraction(s.verifier, s.publicKey, s.config.Logger, s.eventHandlerFunc)) - s.config.HTTPServer.Addr = s.config.Address - s.config.HTTPServer.Handler = s.config.ServeMux - - go func() { - var err error - if s.config.CertFile != "" && s.config.KeyFile != "" { - err = s.config.HTTPServer.ListenAndServeTLS(s.config.CertFile, s.config.KeyFile) - } else { - err = s.config.HTTPServer.ListenAndServe() - } - if !errors.Is(err, http.ErrServerClosed) { - s.config.Logger.Error("error while running http server", slog.Any("err", err)) - } - }() -} - -func (s *serverImpl) Close(ctx context.Context) { - _ = s.config.HTTPServer.Shutdown(ctx) -} diff --git a/webhook/webhook_client.go b/webhook/webhook_client.go index f17d044ed..30ae1c0c3 100644 --- a/webhook/webhook_client.go +++ b/webhook/webhook_client.go @@ -14,7 +14,7 @@ import ( "github.com/disgoorg/disgo/rest" ) -var ErrInvalidWebhookURL = errors.New("invalid webhook URL") +var ErrInvalidWebhookURL = errors.New("invalid webhook InteractionURL") // New creates a new Client with the given ID, Token and ConfigOpt(s). func New(id snowflake.ID, token string, opts ...ConfigOpt) *Client { @@ -34,7 +34,7 @@ func New(id snowflake.ID, token string, opts ...ConfigOpt) *Client { func NewWithURL(webhookURL string, opts ...ConfigOpt) (*Client, error) { u, err := url.Parse(webhookURL) if err != nil { - return nil, fmt.Errorf("invalid webhook URL: %w", err) + return nil, fmt.Errorf("invalid webhook InteractionURL: %w", err) } parts := strings.FieldsFunc(u.Path, func(r rune) bool { return r == '/' }) diff --git a/webhook/webook_test.go b/webhook/webook_test.go index 2c69a353d..c9d693f0c 100644 --- a/webhook/webook_test.go +++ b/webhook/webook_test.go @@ -44,11 +44,11 @@ func TestParseURL(t *testing.T) { t.Run(tc.URL, func(t *testing.T) { c, err := NewWithURL(tc.URL) if tc.Err { - assert.Error(t, err, "URL parsing should have resulted in an error") + assert.Error(t, err, "InteractionURL parsing should have resulted in an error") return } - assert.Equal(t, tc.ID, c.ID, "URL ID should match") - assert.Equal(t, tc.Token, c.Token, "URL Token should match") + assert.Equal(t, tc.ID, c.ID, "InteractionURL ID should match") + assert.Equal(t, tc.Token, c.Token, "InteractionURL Token should match") }) } } From a00b48157b3862c7394aeca15a6a750c517747c6 Mon Sep 17 00:00:00 2001 From: topi314 Date: Fri, 15 Aug 2025 21:32:19 +0200 Subject: [PATCH 2/4] refactor: event handling between http interactions & webhook events --- .../application_commands/http/example.go | 14 +- bot/client.go | 23 ++- bot/config.go | 102 ++++++++-- bot/event_manager.go | 43 +++-- ...tion_command_permissions_update_handler.go | 3 +- bot/handlers/channel_handler.go | 18 +- bot/handlers/entitlement_handlers.go | 16 +- bot/handlers/gateway_handlers.go | 161 ---------------- .../guild_auto_moderation_rule_handler.go | 12 +- bot/handlers/guild_ban_handlers.go | 6 +- bot/handlers/guild_emojis_update_handler.go | 12 +- bot/handlers/guild_handlers.go | 23 ++- .../guild_integrations_update_handler.go | 3 +- bot/handlers/guild_member_handlers.go | 9 +- bot/handlers/guild_role_handlers.go | 9 +- .../guild_scheduled_event_handlers.go | 15 +- bot/handlers/guild_soundboard_handlers.go | 15 +- bot/handlers/guild_stickers_update_handler.go | 12 +- bot/handlers/handler_gateway.go | 176 ++++++++++++++++++ bot/handlers/handler_http_gateway.go | 95 ++++++++++ ...handler.go => handler_http_interaction.go} | 8 +- bot/handlers/http_gateway_handler.go | 72 ------- bot/handlers/integration_handlers.go | 9 +- bot/handlers/interaction_create_handler.go | 37 ++-- bot/handlers/invite_handlers.go | 6 +- bot/handlers/message_handler.go | 31 +-- bot/handlers/message_poll_handler.go | 22 ++- bot/handlers/message_reaction_handler.go | 44 +++-- bot/handlers/presence_update_handler.go | 14 +- bot/handlers/stage_instance_handler.go | 9 +- bot/handlers/status_handler.go | 14 +- bot/handlers/subscription_handlers.go | 9 +- bot/handlers/thread_handler.go | 20 +- bot/handlers/typing_start_handler.go | 9 +- bot/handlers/user_update_handler.go | 3 +- bot/handlers/voice_handlers.go | 9 +- bot/handlers/webhooks_update_handler.go | 3 +- events/application.go | 20 ++ events/dm_channel_events.go | 6 +- events/dm_message_event_events.go | 3 +- events/dm_message_poll_events.go | 3 +- events/dm_message_reaction_events.go | 9 +- events/entitlement_events.go | 12 +- events/gateway_status_events.go | 6 +- events/generic_event.go | 67 +++++-- events/guild_auto_moderation_events.go | 6 +- events/guild_channel_events.go | 6 +- events/guild_emoji_events.go | 6 +- events/guild_events.go | 6 +- events/guild_integration_events.go | 12 +- events/guild_invite_events.go | 6 +- events/guild_member_events.go | 9 +- events/guild_message_events.go | 3 +- events/guild_message_poll_events.go | 3 +- events/guild_message_reaction_events.go | 9 +- events/guild_role_events.go | 3 +- events/guild_scheduled_events_events.go | 6 +- events/guild_soundboard_events.go | 12 +- events/guild_stage_instance_events.go | 3 +- events/guild_sticker_events.go | 6 +- events/guild_thread_events.go | 6 +- events/guild_voice_events.go | 9 +- events/guild_webhooks_update_events.go | 3 +- events/heartbeat_ack.go | 8 - events/interaction_events.go | 15 +- events/listener_adapter.go | 11 +- events/message_events.go | 3 +- events/message_poll_events.go | 3 +- events/message_reaction_events.go | 9 +- events/other_events.go | 29 +++ events/raw_event.go | 8 - events/self_update_events.go | 3 +- events/subscription_events.go | 3 +- events/user_activity_events.go | 6 +- events/user_events.go | 6 +- events/user_status_events.go | 6 +- gateway/gateway.go | 1 - gateway/gateway_event_type.go | 6 +- handler/handler.go | 30 ++- handler/mux_test.go | 12 +- httpgateway/config.go | 97 ---------- httpgateway/event.go | 56 ------ httpgateway/http_gateway.go | 80 -------- httpinteraction/config.go | 55 ++++++ .../handler.go | 37 ++-- httpinteraction/interaction.go | 25 +++ {httpgateway => httpinteraction}/verify.go | 10 +- webhook/webhook_client.go | 4 +- webhook/webook_test.go | 6 +- webhookevent/config.go | 57 ++++++ webhookevent/handler.go | 104 +++++++++++ .../messages.go | 10 +- 92 files changed, 1216 insertions(+), 810 deletions(-) delete mode 100644 bot/handlers/gateway_handlers.go create mode 100644 bot/handlers/handler_gateway.go create mode 100644 bot/handlers/handler_http_gateway.go rename bot/handlers/{http_interaction_handler.go => handler_http_interaction.go} (81%) delete mode 100644 bot/handlers/http_gateway_handler.go create mode 100644 events/application.go delete mode 100644 events/heartbeat_ack.go create mode 100644 events/other_events.go delete mode 100644 events/raw_event.go delete mode 100644 httpgateway/config.go delete mode 100644 httpgateway/event.go delete mode 100644 httpgateway/http_gateway.go create mode 100644 httpinteraction/config.go rename httpgateway/interaction.go => httpinteraction/handler.go (77%) create mode 100644 httpinteraction/interaction.go rename {httpgateway => httpinteraction}/verify.go (86%) create mode 100644 webhookevent/config.go create mode 100644 webhookevent/handler.go rename httpgateway/http_gateway_messages.go => webhookevent/messages.go (96%) diff --git a/_examples/application_commands/http/example.go b/_examples/application_commands/http/example.go index 6c1d46fa5..449e3e388 100644 --- a/_examples/application_commands/http/example.go +++ b/_examples/application_commands/http/example.go @@ -14,7 +14,7 @@ import ( "github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/events" - "github.com/disgoorg/disgo/httpserver" + "github.com/disgoorg/disgo/httpinteraction" ) var ( @@ -44,7 +44,7 @@ var ( type customVerifier struct{} -func (customVerifier) Verify(publicKey httpgateway.PublicKey, message, sig []byte) bool { +func (customVerifier) Verify(publicKey httpinteraction.PublicKey, message, sig []byte) bool { return ed25519.Verify(publicKey, message, sig) } @@ -57,11 +57,11 @@ func main() { slog.Info("disgo version", slog.String("version", disgo.Version)) client, err := disgo.New(token, - bot.WithHTTPServerConfigOpts(publicKey, - httpgateway.WithURL("/interactions/callback"), - httpgateway.WithAddress(":80"), + bot.WithAddress(":80"), + bot.WithHTTPInteractionConfigOpts(publicKey, + httpinteraction.WithEndpoint("/interactions/callback"), // use custom ed25519 verify implementation - httpgateway.WithVerifier(customVerifier{}), + httpinteraction.WithVerifier(customVerifier{}), ), bot.WithEventListenerFunc(commandListener), ) @@ -75,7 +75,7 @@ func main() { panic("error while registering commands: " + err.Error()) } - if err = client.OpenHTTPGateway(); err != nil { + if err = client.OpenHTTPServer(); err != nil { panic("error while starting http server: " + err.Error()) } diff --git a/bot/client.go b/bot/client.go index 16bc89a46..62a4ff0d1 100644 --- a/bot/client.go +++ b/bot/client.go @@ -2,14 +2,15 @@ package bot import ( "context" + "errors" "log/slog" + "net/http" "github.com/disgoorg/snowflake/v2" "github.com/disgoorg/disgo/cache" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/gateway" - "github.com/disgoorg/disgo/httpgateway" "github.com/disgoorg/disgo/rest" "github.com/disgoorg/disgo/sharding" "github.com/disgoorg/disgo/voice" @@ -26,7 +27,7 @@ type Client struct { EventManager EventManager ShardManager sharding.ShardManager Gateway gateway.Gateway - HTTPGateway httpgateway.Gateway + HTTPServer *http.Server VoiceManager voice.Manager Caches cache.Caches MemberChunkingManager MemberChunkingManager @@ -45,8 +46,8 @@ func (c *Client) Close(ctx context.Context) { if c.ShardManager != nil { c.ShardManager.Close(ctx) } - if c.HTTPGateway != nil { - c.HTTPGateway.Close(ctx) + if c.HTTPServer != nil { + _ = c.HTTPServer.Close() } } @@ -168,16 +169,20 @@ func (c *Client) SetPresenceForShard(ctx context.Context, shardId int, opts ...g return shard.Send(ctx, gateway.OpcodePresenceUpdate, applyPresenceFromOpts(shard, opts...)) } -func (c *Client) OpenHTTPGateway() error { - if c.HTTPGateway == nil { +func (c *Client) OpenHTTPServer() error { + if c.HTTPServer == nil { return discord.ErrNoHTTPServer } - c.HTTPGateway.Start() + go func() { + if err := c.HTTPServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + c.Logger.Error("HTTP server failed to run", slog.Any("err", err)) + } + }() return nil } -func (c *Client) HasHTTPGateway() bool { - return c.HTTPGateway != nil +func (c *Client) HasHTTPServer() bool { + return c.HTTPServer != nil } func applyPresenceFromOpts(g gateway.Gateway, opts ...gateway.PresenceOpt) gateway.MessageDataPresenceUpdate { diff --git a/bot/config.go b/bot/config.go index b9d5b65de..1bae15813 100644 --- a/bot/config.go +++ b/bot/config.go @@ -3,15 +3,17 @@ package bot import ( "fmt" "log/slog" + "net/http" "github.com/disgoorg/disgo/cache" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/gateway" - "github.com/disgoorg/disgo/httpgateway" + "github.com/disgoorg/disgo/httpinteraction" "github.com/disgoorg/disgo/internal/tokenhelper" "github.com/disgoorg/disgo/rest" "github.com/disgoorg/disgo/sharding" "github.com/disgoorg/disgo/voice" + "github.com/disgoorg/disgo/webhookevent" ) func defaultConfig(gatewayHandler GatewayEventHandler, httpInteractionHandler HTTPInteractionEventHandler, httpGatewayHandler HTTPGatewayEventHandler) config { @@ -23,6 +25,9 @@ func defaultConfig(gatewayHandler GatewayEventHandler, httpInteractionHandler HT WithHTTPGatewayHandler(httpGatewayHandler), }, MemberChunkingFilter: MemberChunkingFilterNone, + HTTPServer: &http.Server{}, + ServeMux: http.NewServeMux(), + Address: ":80", } } @@ -45,9 +50,14 @@ type config struct { ShardManager sharding.ShardManager ShardManagerConfigOpts []sharding.ConfigOpt - HTTPGateway httpgateway.Gateway - PublicKey string - HTTPGatewayConfigOpts []httpgateway.ConfigOpt + HTTPServer *http.Server + ServeMux *http.ServeMux + Address string + PublicKey string + CertFile string + KeyFile string + HTTPInteractionConfigOpts []httpinteraction.ConfigOpt + WebhookEventConfigOpts []webhookevent.ConfigOpt Caches cache.Caches CacheConfigOpts []cache.ConfigOpt @@ -167,18 +177,58 @@ func WithShardManagerConfigOpts(opts ...sharding.ConfigOpt) ConfigOpt { } } -// WithHTTPServer lets you inject your own httpserver.Server. -func WithHTTPServer(httpServer httpgateway.Gateway) ConfigOpt { +func WithHTTPServer(httpServer *http.Server) ConfigOpt { return func(config *config) { - config.HTTPGateway = httpServer + config.HTTPServer = httpServer } } -// WithHTTPServerConfigOpts lets you configure the default httpserver.Server. -func WithHTTPServerConfigOpts(publicKey string, opts ...httpgateway.ConfigOpt) ConfigOpt { +func WithServeMux(serveMux *http.ServeMux) ConfigOpt { + return func(config *config) { + config.ServeMux = serveMux + } +} + +// WithAddress sets the address the HTTP server will listen on. +func WithAddress(address string) ConfigOpt { + return func(config *config) { + config.Address = address + } +} + +// WithCert lets you set the certificate and key files for the HTTP server. +func WithCert(certFile string, keyFile string) ConfigOpt { + return func(config *config) { + config.CertFile = certFile + config.KeyFile = keyFile + } +} + +func WithHTTPInteractionConfigOpts(publicKey string, opts ...httpinteraction.ConfigOpt) ConfigOpt { + return func(config *config) { + config.PublicKey = publicKey + config.HTTPInteractionConfigOpts = append(config.HTTPInteractionConfigOpts, opts...) + } +} + +func WithDefaultHTTPInteractions(publicKey string) ConfigOpt { return func(config *config) { config.PublicKey = publicKey - config.HTTPGatewayConfigOpts = append(config.HTTPGatewayConfigOpts, opts...) + config.HTTPInteractionConfigOpts = append(config.HTTPInteractionConfigOpts, httpinteraction.WithDefault()) + } +} + +func WithWebhookEventConfigOpts(publicKey string, opts ...webhookevent.ConfigOpt) ConfigOpt { + return func(config *config) { + config.PublicKey = publicKey + config.WebhookEventConfigOpts = append(config.WebhookEventConfigOpts, opts...) + } +} + +func WithDefaultWebhookEvents(publicKey string) ConfigOpt { + return func(config *config) { + config.PublicKey = publicKey + config.WebhookEventConfigOpts = append(config.WebhookEventConfigOpts, webhookevent.WithDefault()) } } @@ -214,11 +264,11 @@ func defaultGatewayEventHandlerFunc(client *Client) gateway.EventHandlerFunc { return client.EventManager.HandleGatewayEvent } -func defaultInteractionHandlerFunc(client *Client) httpgateway.InteractionHandlerFunc { +func defaultInteractionHandlerFunc(client *Client) httpinteraction.InteractionHandlerFunc { return client.EventManager.HandleHTTPInteractionEvent } -func defaultHTTPGatewayEventHandlerFunc(client *Client) httpgateway.EventHandlerFunc { +func defaultHTTPGatewayEventHandlerFunc(client *Client) webhookevent.EventHandlerFunc { return client.EventManager.HandleHTTPGatewayEvent } @@ -334,17 +384,29 @@ func BuildClient( } client.ShardManager = cfg.ShardManager - if cfg.HTTPGateway == nil && cfg.PublicKey != "" { - cfg.HTTPGatewayConfigOpts = append([]httpgateway.ConfigOpt{ - httpgateway.WithLogger(cfg.Logger), - }, cfg.HTTPGatewayConfigOpts...) + if len(cfg.HTTPInteractionConfigOpts) > 0 || len(cfg.WebhookEventConfigOpts) > 0 { + cfg.HTTPInteractionConfigOpts = append([]httpinteraction.ConfigOpt{ + httpinteraction.WithLogger(cfg.Logger), + }, cfg.HTTPInteractionConfigOpts...) - cfg.HTTPGateway, err = httpgateway.New(cfg.PublicKey, defaultInteractionHandlerFunc(client), defaultHTTPGatewayEventHandlerFunc(client), cfg.HTTPGatewayConfigOpts...) - if err != nil { - return nil, fmt.Errorf("error while creating http server: %w", err) + if err = httpinteraction.New(cfg.ServeMux, cfg.PublicKey, defaultInteractionHandlerFunc(client), cfg.HTTPInteractionConfigOpts...); err != nil { + return nil, fmt.Errorf("error while initializing http interaction handler: %w", err) + } + + cfg.WebhookEventConfigOpts = append([]webhookevent.ConfigOpt{ + webhookevent.WithLogger(cfg.Logger), + }, cfg.WebhookEventConfigOpts...) + + if err = webhookevent.New(cfg.ServeMux, cfg.PublicKey, defaultHTTPGatewayEventHandlerFunc(client), cfg.WebhookEventConfigOpts...); err != nil { + return nil, fmt.Errorf("error while initializing webhook event handler: %w", err) + } + + if cfg.HTTPServer.Addr == "" { + cfg.HTTPServer.Addr = cfg.Address } + cfg.HTTPServer.Handler = cfg.ServeMux } - client.HTTPGateway = cfg.HTTPGateway + client.HTTPServer = cfg.HTTPServer if cfg.MemberChunkingManager == nil { cfg.MemberChunkingManager = NewMemberChunkingManager(client, cfg.Logger, cfg.MemberChunkingFilter) diff --git a/bot/event_manager.go b/bot/event_manager.go index 601199623..815cc7bf6 100644 --- a/bot/event_manager.go +++ b/bot/event_manager.go @@ -4,9 +4,13 @@ import ( "log/slog" "runtime/debug" "sync" + "time" + + "github.com/disgoorg/snowflake/v2" "github.com/disgoorg/disgo/gateway" - "github.com/disgoorg/disgo/httpgateway" + "github.com/disgoorg/disgo/httpinteraction" + "github.com/disgoorg/disgo/webhookevent" ) // EventListener is used to create new EventListener to listen to events @@ -44,24 +48,40 @@ func (l *listenerChan[E]) OnEvent(e Event) { } } -// Event the basic interface each event implement +// Event is the basic interface each event implement type Event interface { Client() *Client +} + +// GatewayEvent contains the basic information of an event which originates from the [gateway.Gateway]. +type GatewayEvent interface { + Event + SequenceNumber() int + ShardID() int +} + +// WebhookEvent contains the basic information of an event which originates from the [httpgateway.Gateway]. +type WebhookEvent interface { + Event + + Version() int + ApplicationID() snowflake.ID + Timestamp() time.Time } // GatewayEventHandler is used to handle Gateway Event(s) type GatewayEventHandler interface { - HandleGatewayEvent(client *Client, message gateway.Message, shardID int) + HandleGatewayEvent(client *Client, shardID int, message gateway.Message) } // HTTPInteractionEventHandler is used to handle HTTP Event(s) type HTTPInteractionEventHandler interface { - HandleHTTPInteraction(client *Client, respond httpgateway.RespondFunc, event httpgateway.EventInteractionCreate) + HandleHTTPInteraction(client *Client, respond httpinteraction.RespondFunc, event httpinteraction.EventInteractionCreate) } type HTTPGatewayEventHandler interface { - HandleHTTPGatewayEvent(client *Client, ack func(), message httpgateway.Message) + HandleHTTPGatewayEvent(client *Client, message webhookevent.Message) } // EventManager lets you listen for specific events triggered by raw Gateway events @@ -76,10 +96,10 @@ type EventManager interface { HandleGatewayEvent(gateway gateway.Gateway, message gateway.Message) // HandleHTTPInteractionEvent calls the HTTPInteractionEventHandler for the payload - HandleHTTPInteractionEvent(respond httpgateway.RespondFunc, event httpgateway.EventInteractionCreate) + HandleHTTPInteractionEvent(respond httpinteraction.RespondFunc, event httpinteraction.EventInteractionCreate) // HandleHTTPGatewayEvent calls the HTTPInteractionEventHandler for the payload - HandleHTTPGatewayEvent(ack func(), message httpgateway.Message) + HandleHTTPGatewayEvent(ack func(), message webhookevent.Message) // DispatchEvent dispatches a new Event to the Client's EventListener(s) DispatchEvent(event Event) @@ -121,21 +141,22 @@ func (e *eventManagerImpl) HandleGatewayEvent(gateway gateway.Gateway, message g e.mu.Lock() defer e.mu.Unlock() - e.gatewayHandler.HandleGatewayEvent(e.client, message, gateway.ShardID()) + e.gatewayHandler.HandleGatewayEvent(e.client, gateway.ShardID(), message) } -func (e *eventManagerImpl) HandleHTTPInteractionEvent(respond httpgateway.RespondFunc, event httpgateway.EventInteractionCreate) { +func (e *eventManagerImpl) HandleHTTPInteractionEvent(respond httpinteraction.RespondFunc, event httpinteraction.EventInteractionCreate) { e.mu.Lock() defer e.mu.Unlock() e.httpInteractionHandler.HandleHTTPInteraction(e.client, respond, event) } -func (e *eventManagerImpl) HandleHTTPGatewayEvent(ack func(), message httpgateway.Message) { +func (e *eventManagerImpl) HandleHTTPGatewayEvent(ack func(), message webhookevent.Message) { + ack() e.mu.Lock() defer e.mu.Unlock() - e.httpGatewayHandler.HandleHTTPGatewayEvent(e.client, ack, message) + e.httpGatewayHandler.HandleHTTPGatewayEvent(e.client, message) } func (e *eventManagerImpl) DispatchEvent(event Event) { diff --git a/bot/handlers/application_command_permissions_update_handler.go b/bot/handlers/application_command_permissions_update_handler.go index c4c31ecad..2c899c829 100644 --- a/bot/handlers/application_command_permissions_update_handler.go +++ b/bot/handlers/application_command_permissions_update_handler.go @@ -8,7 +8,8 @@ import ( func gatewayHandlerApplicationCommandPermissionsUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventApplicationCommandPermissionsUpdate) { client.EventManager.DispatchEvent(&events.GuildApplicationCommandPermissionsUpdate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), Permissions: event.ApplicationCommandPermissions, }) } diff --git a/bot/handlers/channel_handler.go b/bot/handlers/channel_handler.go index 3ab4327f4..d352cce34 100644 --- a/bot/handlers/channel_handler.go +++ b/bot/handlers/channel_handler.go @@ -14,7 +14,8 @@ func gatewayHandlerChannelCreate(client *bot.Client, sequenceNumber int, shardID client.EventManager.DispatchEvent(&events.GuildChannelCreate{ GenericGuildChannel: &events.GenericGuildChannel{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ChannelID: event.ID(), Channel: event.GuildChannel, GuildID: event.GuildChannel.GuildID(), @@ -28,7 +29,8 @@ func gatewayHandlerChannelUpdate(client *bot.Client, sequenceNumber int, shardID client.EventManager.DispatchEvent(&events.GuildChannelUpdate{ GenericGuildChannel: &events.GenericGuildChannel{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ChannelID: event.ID(), Channel: event.GuildChannel, GuildID: event.GuildChannel.GuildID(), @@ -44,7 +46,8 @@ func gatewayHandlerChannelUpdate(client *bot.Client, sequenceNumber int, shardID client.Caches.RemoveChannel(guildThread.ID()) client.EventManager.DispatchEvent(&events.ThreadHide{ GenericThread: &events.GenericThread{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), Thread: guildThread, ThreadID: guildThread.ID(), GuildID: guildThread.GuildID(), @@ -62,7 +65,8 @@ func gatewayHandlerChannelDelete(client *bot.Client, sequenceNumber int, shardID client.EventManager.DispatchEvent(&events.GuildChannelDelete{ GenericGuildChannel: &events.GenericGuildChannel{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ChannelID: event.ID(), Channel: event.GuildChannel, GuildID: event.GuildChannel.GuildID(), @@ -73,7 +77,8 @@ func gatewayHandlerChannelDelete(client *bot.Client, sequenceNumber int, shardID func gatewayHandlerChannelPinsUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventChannelPinsUpdate) { if event.GuildID == nil { client.EventManager.DispatchEvent(&events.DMChannelPinsUpdate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ChannelID: event.ChannelID, NewLastPinTimestamp: event.LastPinTimestamp, }) @@ -88,7 +93,8 @@ func gatewayHandlerChannelPinsUpdate(client *bot.Client, sequenceNumber int, sha } client.EventManager.DispatchEvent(&events.GuildChannelPinsUpdate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: *event.GuildID, ChannelID: event.ChannelID, OldLastPinTimestamp: oldTime, diff --git a/bot/handlers/entitlement_handlers.go b/bot/handlers/entitlement_handlers.go index 888edaa39..304749858 100644 --- a/bot/handlers/entitlement_handlers.go +++ b/bot/handlers/entitlement_handlers.go @@ -8,18 +8,19 @@ import ( func gatewayHandlerEntitlementCreate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventEntitlementCreate) { client.EventManager.DispatchEvent(&events.EntitlementCreate{ - GenericEntitlementEvent: &events.GenericEntitlementEvent{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), - Entitlement: event.Entitlement, - }, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), + Entitlement: event.Entitlement, }) } func gatewayHandlerEntitlementUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventEntitlementUpdate) { client.EventManager.DispatchEvent(&events.EntitlementUpdate{ GenericEntitlementEvent: &events.GenericEntitlementEvent{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), - Entitlement: event.Entitlement, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), + + Entitlement: event.Entitlement, }, }) } @@ -27,7 +28,8 @@ func gatewayHandlerEntitlementUpdate(client *bot.Client, sequenceNumber int, sha func gatewayHandlerEntitlementDelete(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventEntitlementDelete) { client.EventManager.DispatchEvent(&events.EntitlementDelete{ GenericEntitlementEvent: &events.GenericEntitlementEvent{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), Entitlement: event.Entitlement, }, }) diff --git a/bot/handlers/gateway_handlers.go b/bot/handlers/gateway_handlers.go deleted file mode 100644 index 5fcecbd5e..000000000 --- a/bot/handlers/gateway_handlers.go +++ /dev/null @@ -1,161 +0,0 @@ -package handlers - -import ( - "github.com/disgoorg/disgo/bot" - "github.com/disgoorg/disgo/gateway" -) - -type gatewayEventHandler interface { - bot.GatewayEventHandler - EventType() gateway.EventType -} - -// GetGatewayHandler returns the default gateway.Gateway event handlers for processing the raw payload which gets passed into the bot.EventManager -func GetGatewayHandler() bot.GatewayEventHandler { - handlers := make(map[gateway.EventType]gatewayEventHandler, len(allEventHandlers)) - for _, handler := range allEventHandlers { - handlers[handler.EventType()] = handler - } - return &gatewayHandler{ - handlers: handlers, - } -} - -type gatewayHandler struct { - handlers map[gateway.EventType]gatewayEventHandler -} - -func (g *gatewayHandler) HandleGatewayEvent(client *bot.Client, message gateway.Message, shardID int) { - if handler, ok := g.handlers[message.T]; ok { - handler.HandleGatewayEvent(client, message, shardID) - } else { - client.Logger.Warn("No handler found for gateway event", "event_type", message.T, "shard_id", shardID) - } -} - -func newGatewayEventHandler[T gateway.EventData](eventType gateway.EventType, handleFunc func(client *bot.Client, sequenceNumber int, shardID int, event T)) *genericGatewayEventHandler[T] { - return &genericGatewayEventHandler[T]{ - eventType: eventType, - handleFunc: handleFunc, - } -} - -type genericGatewayEventHandler[T gateway.EventData] struct { - eventType gateway.EventType - handleFunc func(client *bot.Client, sequenceNumber int, shardID int, event T) -} - -func (h *genericGatewayEventHandler[T]) EventType() gateway.EventType { - return h.eventType -} - -func (h *genericGatewayEventHandler[T]) HandleGatewayEvent(client *bot.Client, message gateway.Message, shardID int) { - if data, ok := message.D.(T); ok { - h.handleFunc(client, message.S, shardID, data) - } -} - -var allEventHandlers = []gatewayEventHandler{ - newGatewayEventHandler(gateway.EventTypeRaw, gatewayHandlerRaw), - newGatewayEventHandler(gateway.EventTypeHeartbeatAck, gatewayHandlerHeartbeatAck), - newGatewayEventHandler(gateway.EventTypeReady, gatewayHandlerReady), - newGatewayEventHandler(gateway.EventTypeResumed, gatewayHandlerResumed), - - newGatewayEventHandler(gateway.EventTypeApplicationCommandPermissionsUpdate, gatewayHandlerApplicationCommandPermissionsUpdate), - - newGatewayEventHandler(gateway.EventTypeAutoModerationRuleCreate, gatewayHandlerAutoModerationRuleCreate), - newGatewayEventHandler(gateway.EventTypeAutoModerationRuleUpdate, gatewayHandlerAutoModerationRuleUpdate), - newGatewayEventHandler(gateway.EventTypeAutoModerationRuleDelete, gatewayHandlerAutoModerationRuleDelete), - newGatewayEventHandler(gateway.EventTypeAutoModerationActionExecution, gatewayHandlerAutoModerationActionExecution), - - newGatewayEventHandler(gateway.EventTypeChannelCreate, gatewayHandlerChannelCreate), - newGatewayEventHandler(gateway.EventTypeChannelUpdate, gatewayHandlerChannelUpdate), - newGatewayEventHandler(gateway.EventTypeChannelDelete, gatewayHandlerChannelDelete), - newGatewayEventHandler(gateway.EventTypeChannelPinsUpdate, gatewayHandlerChannelPinsUpdate), - - newGatewayEventHandler(gateway.EventTypeEntitlementCreate, gatewayHandlerEntitlementCreate), - newGatewayEventHandler(gateway.EventTypeEntitlementUpdate, gatewayHandlerEntitlementUpdate), - newGatewayEventHandler(gateway.EventTypeEntitlementDelete, gatewayHandlerEntitlementDelete), - - newGatewayEventHandler(gateway.EventTypeThreadCreate, gatewayHandlerThreadCreate), - newGatewayEventHandler(gateway.EventTypeThreadUpdate, gatewayHandlerThreadUpdate), - newGatewayEventHandler(gateway.EventTypeThreadDelete, gatewayHandlerThreadDelete), - newGatewayEventHandler(gateway.EventTypeThreadListSync, gatewayHandlerThreadListSync), - newGatewayEventHandler(gateway.EventTypeThreadMemberUpdate, gatewayHandlerThreadMemberUpdate), - newGatewayEventHandler(gateway.EventTypeThreadMembersUpdate, gatewayHandlerThreadMembersUpdate), - - newGatewayEventHandler(gateway.EventTypeGuildCreate, gatewayHandlerGuildCreate), - newGatewayEventHandler(gateway.EventTypeGuildUpdate, gatewayHandlerGuildUpdate), - newGatewayEventHandler(gateway.EventTypeGuildDelete, gatewayHandlerGuildDelete), - - newGatewayEventHandler(gateway.EventTypeGuildAuditLogEntryCreate, gatewayHandlerGuildAuditLogEntryCreate), - - newGatewayEventHandler(gateway.EventTypeGuildBanAdd, gatewayHandlerGuildBanAdd), - newGatewayEventHandler(gateway.EventTypeGuildBanRemove, gatewayHandlerGuildBanRemove), - - newGatewayEventHandler(gateway.EventTypeGuildEmojisUpdate, gatewayHandlerGuildEmojisUpdate), - newGatewayEventHandler(gateway.EventTypeGuildStickersUpdate, gatewayHandlerGuildStickersUpdate), - newGatewayEventHandler(gateway.EventTypeGuildIntegrationsUpdate, gatewayHandlerGuildIntegrationsUpdate), - - newGatewayEventHandler(gateway.EventTypeGuildMemberAdd, gatewayHandlerGuildMemberAdd), - newGatewayEventHandler(gateway.EventTypeGuildMemberRemove, gatewayHandlerGuildMemberRemove), - newGatewayEventHandler(gateway.EventTypeGuildMemberUpdate, gatewayHandlerGuildMemberUpdate), - newGatewayEventHandler(gateway.EventTypeGuildMembersChunk, gatewayHandlerGuildMembersChunk), - - newGatewayEventHandler(gateway.EventTypeGuildRoleCreate, gatewayHandlerGuildRoleCreate), - newGatewayEventHandler(gateway.EventTypeGuildRoleUpdate, gatewayHandlerGuildRoleUpdate), - newGatewayEventHandler(gateway.EventTypeGuildRoleDelete, gatewayHandlerGuildRoleDelete), - - newGatewayEventHandler(gateway.EventTypeGuildScheduledEventCreate, gatewayHandlerGuildScheduledEventCreate), - newGatewayEventHandler(gateway.EventTypeGuildScheduledEventUpdate, gatewayHandlerGuildScheduledEventUpdate), - newGatewayEventHandler(gateway.EventTypeGuildScheduledEventDelete, gatewayHandlerGuildScheduledEventDelete), - newGatewayEventHandler(gateway.EventTypeGuildScheduledEventUserAdd, gatewayHandlerGuildScheduledEventUserAdd), - newGatewayEventHandler(gateway.EventTypeGuildScheduledEventUserRemove, gatewayHandlerGuildScheduledEventUserRemove), - - newGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundCreate, gatewayHandlerGuildSoundboardSoundCreate), - newGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundUpdate, gatewayHandlerGuildSoundboardSoundUpdate), - newGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundDelete, gatewayHandlerGuildSoundboardSoundDelete), - newGatewayEventHandler(gateway.EventTypeGuildSoundboardSoundsUpdate, gatewayHandlerGuildSoundboardSoundsUpdate), - newGatewayEventHandler(gateway.EventTypeSoundboardSounds, gatewayHandlerSoundboardSounds), - - newGatewayEventHandler(gateway.EventTypeIntegrationCreate, gatewayHandlerIntegrationCreate), - newGatewayEventHandler(gateway.EventTypeIntegrationUpdate, gatewayHandlerIntegrationUpdate), - newGatewayEventHandler(gateway.EventTypeIntegrationDelete, gatewayHandlerIntegrationDelete), - - newGatewayEventHandler(gateway.EventTypeInteractionCreate, gatewayHandlerInteractionCreate), - - newGatewayEventHandler(gateway.EventTypeInviteCreate, gatewayHandlerInviteCreate), - newGatewayEventHandler(gateway.EventTypeInviteDelete, gatewayHandlerInviteDelete), - - newGatewayEventHandler(gateway.EventTypeMessageCreate, gatewayHandlerMessageCreate), - newGatewayEventHandler(gateway.EventTypeMessageUpdate, gatewayHandlerMessageUpdate), - newGatewayEventHandler(gateway.EventTypeMessageDelete, gatewayHandlerMessageDelete), - newGatewayEventHandler(gateway.EventTypeMessageDeleteBulk, gatewayHandlerMessageDeleteBulk), - - newGatewayEventHandler(gateway.EventTypeMessagePollVoteAdd, gatewayHandlerMessagePollVoteAdd), - newGatewayEventHandler(gateway.EventTypeMessagePollVoteRemove, gatewayHandlerMessagePollVoteRemove), - - newGatewayEventHandler(gateway.EventTypeMessageReactionAdd, gatewayHandlerMessageReactionAdd), - newGatewayEventHandler(gateway.EventTypeMessageReactionRemove, gatewayHandlerMessageReactionRemove), - newGatewayEventHandler(gateway.EventTypeMessageReactionRemoveAll, gatewayHandlerMessageReactionRemoveAll), - newGatewayEventHandler(gateway.EventTypeMessageReactionRemoveEmoji, gatewayHandlerMessageReactionRemoveEmoji), - - newGatewayEventHandler(gateway.EventTypePresenceUpdate, gatewayHandlerPresenceUpdate), - - newGatewayEventHandler(gateway.EventTypeStageInstanceCreate, gatewayHandlerStageInstanceCreate), - newGatewayEventHandler(gateway.EventTypeStageInstanceUpdate, gatewayHandlerStageInstanceUpdate), - newGatewayEventHandler(gateway.EventTypeStageInstanceDelete, gatewayHandlerStageInstanceDelete), - - newGatewayEventHandler(gateway.EventTypeSubscriptionCreate, gatewayHandlerSubscriptionCreate), - newGatewayEventHandler(gateway.EventTypeSubscriptionUpdate, gatewayHandlerSubscriptionUpdate), - newGatewayEventHandler(gateway.EventTypeSubscriptionDelete, gatewayHandlerSubscriptionDelete), - - newGatewayEventHandler(gateway.EventTypeTypingStart, gatewayHandlerTypingStart), - newGatewayEventHandler(gateway.EventTypeUserUpdate, gatewayHandlerUserUpdate), - - newGatewayEventHandler(gateway.EventTypeVoiceChannelEffectSend, gatewayHandlerVoiceChannelEffectSend), - newGatewayEventHandler(gateway.EventTypeVoiceStateUpdate, gatewayHandlerVoiceStateUpdate), - newGatewayEventHandler(gateway.EventTypeVoiceServerUpdate, gatewayHandlerVoiceServerUpdate), - - newGatewayEventHandler(gateway.EventTypeWebhooksUpdate, gatewayHandlerWebhooksUpdate), -} diff --git a/bot/handlers/guild_auto_moderation_rule_handler.go b/bot/handlers/guild_auto_moderation_rule_handler.go index 42cc8448c..c7e601df2 100644 --- a/bot/handlers/guild_auto_moderation_rule_handler.go +++ b/bot/handlers/guild_auto_moderation_rule_handler.go @@ -9,7 +9,8 @@ import ( func gatewayHandlerAutoModerationRuleCreate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventAutoModerationRuleCreate) { client.EventManager.DispatchEvent(&events.AutoModerationRuleCreate{ GenericAutoModerationRule: &events.GenericAutoModerationRule{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), AutoModerationRule: event.AutoModerationRule, }, }) @@ -18,7 +19,8 @@ func gatewayHandlerAutoModerationRuleCreate(client *bot.Client, sequenceNumber i func gatewayHandlerAutoModerationRuleUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventAutoModerationRuleUpdate) { client.EventManager.DispatchEvent(&events.AutoModerationRuleUpdate{ GenericAutoModerationRule: &events.GenericAutoModerationRule{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), AutoModerationRule: event.AutoModerationRule, }, }) @@ -27,7 +29,8 @@ func gatewayHandlerAutoModerationRuleUpdate(client *bot.Client, sequenceNumber i func gatewayHandlerAutoModerationRuleDelete(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventAutoModerationRuleDelete) { client.EventManager.DispatchEvent(&events.AutoModerationRuleDelete{ GenericAutoModerationRule: &events.GenericAutoModerationRule{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), AutoModerationRule: event.AutoModerationRule, }, }) @@ -35,7 +38,8 @@ func gatewayHandlerAutoModerationRuleDelete(client *bot.Client, sequenceNumber i func gatewayHandlerAutoModerationActionExecution(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventAutoModerationActionExecution) { client.EventManager.DispatchEvent(&events.AutoModerationActionExecution{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), EventAutoModerationActionExecution: event, }) } diff --git a/bot/handlers/guild_ban_handlers.go b/bot/handlers/guild_ban_handlers.go index d79299aca..f9a5bcde0 100644 --- a/bot/handlers/guild_ban_handlers.go +++ b/bot/handlers/guild_ban_handlers.go @@ -8,7 +8,8 @@ import ( func gatewayHandlerGuildBanAdd(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventGuildBanAdd) { genericGuildEvent := &events.GenericGuild{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, } @@ -20,7 +21,8 @@ func gatewayHandlerGuildBanAdd(client *bot.Client, sequenceNumber int, shardID i func gatewayHandlerGuildBanRemove(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventGuildBanRemove) { genericGuildEvent := &events.GenericGuild{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, } diff --git a/bot/handlers/guild_emojis_update_handler.go b/bot/handlers/guild_emojis_update_handler.go index 1d1f5eb5b..d3a2f5692 100644 --- a/bot/handlers/guild_emojis_update_handler.go +++ b/bot/handlers/guild_emojis_update_handler.go @@ -19,7 +19,8 @@ type updatedEmoji struct { func gatewayHandlerGuildEmojisUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventGuildEmojisUpdate) { client.EventManager.DispatchEvent(&events.EmojisUpdate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), EventGuildEmojisUpdate: event, }) @@ -51,7 +52,8 @@ func gatewayHandlerGuildEmojisUpdate(client *bot.Client, sequenceNumber int, sha client.Caches.AddEmoji(emoji) client.EventManager.DispatchEvent(&events.EmojiCreate{ GenericEmoji: &events.GenericEmoji{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, Emoji: emoji, }, @@ -62,7 +64,8 @@ func gatewayHandlerGuildEmojisUpdate(client *bot.Client, sequenceNumber int, sha client.Caches.AddEmoji(emoji.new) client.EventManager.DispatchEvent(&events.EmojiUpdate{ GenericEmoji: &events.GenericEmoji{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, Emoji: emoji.new, }, @@ -74,7 +77,8 @@ func gatewayHandlerGuildEmojisUpdate(client *bot.Client, sequenceNumber int, sha client.Caches.RemoveEmoji(event.GuildID, emoji.ID) client.EventManager.DispatchEvent(&events.EmojiDelete{ GenericEmoji: &events.GenericEmoji{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, Emoji: emoji, }, diff --git a/bot/handlers/guild_handlers.go b/bot/handlers/guild_handlers.go index 8261dbb8f..5b28f0bc1 100644 --- a/bot/handlers/guild_handlers.go +++ b/bot/handlers/guild_handlers.go @@ -69,7 +69,8 @@ func gatewayHandlerGuildCreate(client *bot.Client, sequenceNumber int, shardID i } genericGuildEvent := &events.GenericGuild{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.ID, } @@ -81,7 +82,8 @@ func gatewayHandlerGuildCreate(client *bot.Client, sequenceNumber int, shardID i }) if len(client.Caches.UnreadyGuildIDs()) == 0 { client.EventManager.DispatchEvent(&events.GuildsReady{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), }) } if client.MemberChunkingManager.MemberChunkingFilter()(event.ID) { @@ -114,7 +116,8 @@ func gatewayHandlerGuildUpdate(client *bot.Client, sequenceNumber int, shardID i client.EventManager.DispatchEvent(&events.GuildUpdate{ GenericGuild: &events.GenericGuild{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.ID, }, Guild: event.Guild, @@ -147,7 +150,8 @@ func gatewayHandlerGuildDelete(client *bot.Client, sequenceNumber int, shardID i client.Caches.RemoveMessagesByGuildID(event.ID) genericGuildEvent := &events.GenericGuild{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.ID, } @@ -165,13 +169,12 @@ func gatewayHandlerGuildDelete(client *bot.Client, sequenceNumber int, shardID i } func gatewayHandlerGuildAuditLogEntryCreate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventGuildAuditLogEntryCreate) { - genericGuildEvent := &events.GenericGuild{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), - GuildID: event.GuildID, - } - client.EventManager.DispatchEvent(&events.GuildAuditLogEntryCreate{ - GenericGuild: genericGuildEvent, + GenericGuild: &events.GenericGuild{ + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), + GuildID: event.GuildID, + }, AuditLogEntry: event.AuditLogEntry, }) } diff --git a/bot/handlers/guild_integrations_update_handler.go b/bot/handlers/guild_integrations_update_handler.go index d5e8ab96f..d0c22609a 100644 --- a/bot/handlers/guild_integrations_update_handler.go +++ b/bot/handlers/guild_integrations_update_handler.go @@ -8,7 +8,8 @@ import ( func gatewayHandlerGuildIntegrationsUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventGuildIntegrationsUpdate) { client.EventManager.DispatchEvent(&events.GuildIntegrationsUpdate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, }) } diff --git a/bot/handlers/guild_member_handlers.go b/bot/handlers/guild_member_handlers.go index 9bd6337e0..2e5f3b607 100644 --- a/bot/handlers/guild_member_handlers.go +++ b/bot/handlers/guild_member_handlers.go @@ -16,7 +16,8 @@ func gatewayHandlerGuildMemberAdd(client *bot.Client, sequenceNumber int, shardI client.EventManager.DispatchEvent(&events.GuildMemberJoin{ GenericGuildMember: &events.GenericGuildMember{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, Member: event.Member, }, @@ -29,7 +30,8 @@ func gatewayHandlerGuildMemberUpdate(client *bot.Client, sequenceNumber int, sha client.EventManager.DispatchEvent(&events.GuildMemberUpdate{ GenericGuildMember: &events.GenericGuildMember{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, Member: event.Member, }, @@ -46,7 +48,8 @@ func gatewayHandlerGuildMemberRemove(client *bot.Client, sequenceNumber int, sha member, _ := client.Caches.RemoveMember(event.GuildID, event.User.ID) client.EventManager.DispatchEvent(&events.GuildMemberLeave{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, User: event.User, Member: member, diff --git a/bot/handlers/guild_role_handlers.go b/bot/handlers/guild_role_handlers.go index b0b4f2afb..93720764b 100644 --- a/bot/handlers/guild_role_handlers.go +++ b/bot/handlers/guild_role_handlers.go @@ -11,7 +11,8 @@ func gatewayHandlerGuildRoleCreate(client *bot.Client, sequenceNumber int, shard client.EventManager.DispatchEvent(&events.RoleCreate{ GenericRole: &events.GenericRole{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, RoleID: event.Role.ID, Role: event.Role, @@ -25,7 +26,8 @@ func gatewayHandlerGuildRoleUpdate(client *bot.Client, sequenceNumber int, shard client.EventManager.DispatchEvent(&events.RoleUpdate{ GenericRole: &events.GenericRole{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, RoleID: event.Role.ID, Role: event.Role, @@ -39,7 +41,8 @@ func gatewayHandlerGuildRoleDelete(client *bot.Client, sequenceNumber int, shard client.EventManager.DispatchEvent(&events.RoleDelete{ GenericRole: &events.GenericRole{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, RoleID: event.RoleID, Role: role, diff --git a/bot/handlers/guild_scheduled_event_handlers.go b/bot/handlers/guild_scheduled_event_handlers.go index 3d14cbfad..e8ab70460 100644 --- a/bot/handlers/guild_scheduled_event_handlers.go +++ b/bot/handlers/guild_scheduled_event_handlers.go @@ -11,7 +11,8 @@ func gatewayHandlerGuildScheduledEventCreate(client *bot.Client, sequenceNumber client.EventManager.DispatchEvent(&events.GuildScheduledEventCreate{ GenericGuildScheduledEvent: &events.GenericGuildScheduledEvent{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildScheduled: event.GuildScheduledEvent, }, }) @@ -23,7 +24,8 @@ func gatewayHandlerGuildScheduledEventUpdate(client *bot.Client, sequenceNumber client.EventManager.DispatchEvent(&events.GuildScheduledEventUpdate{ GenericGuildScheduledEvent: &events.GenericGuildScheduledEvent{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildScheduled: event.GuildScheduledEvent, }, OldGuildScheduled: oldGuildScheduledEvent, @@ -35,7 +37,8 @@ func gatewayHandlerGuildScheduledEventDelete(client *bot.Client, sequenceNumber client.EventManager.DispatchEvent(&events.GuildScheduledEventDelete{ GenericGuildScheduledEvent: &events.GenericGuildScheduledEvent{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildScheduled: event.GuildScheduledEvent, }, }) @@ -44,7 +47,8 @@ func gatewayHandlerGuildScheduledEventDelete(client *bot.Client, sequenceNumber func gatewayHandlerGuildScheduledEventUserAdd(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventGuildScheduledEventUserAdd) { client.EventManager.DispatchEvent(&events.GuildScheduledEventUserAdd{ GenericGuildScheduledEventUser: &events.GenericGuildScheduledEventUser{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildScheduledEventID: event.GuildScheduledEventID, UserID: event.UserID, GuildID: event.GuildID, @@ -55,7 +59,8 @@ func gatewayHandlerGuildScheduledEventUserAdd(client *bot.Client, sequenceNumber func gatewayHandlerGuildScheduledEventUserRemove(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventGuildScheduledEventUserRemove) { client.EventManager.DispatchEvent(&events.GuildScheduledEventUserRemove{ GenericGuildScheduledEventUser: &events.GenericGuildScheduledEventUser{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildScheduledEventID: event.GuildScheduledEventID, UserID: event.UserID, GuildID: event.GuildID, diff --git a/bot/handlers/guild_soundboard_handlers.go b/bot/handlers/guild_soundboard_handlers.go index 85e1387d4..32c330ea7 100644 --- a/bot/handlers/guild_soundboard_handlers.go +++ b/bot/handlers/guild_soundboard_handlers.go @@ -11,7 +11,8 @@ func gatewayHandlerGuildSoundboardSoundCreate(client *bot.Client, sequenceNumber client.EventManager.DispatchEvent(&events.GuildSoundboardSoundCreate{ GenericGuildSoundboardSound: &events.GenericGuildSoundboardSound{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), SoundboardSound: event.SoundboardSound, }, }) @@ -23,7 +24,8 @@ func gatewayHandlerGuildSoundboardSoundUpdate(client *bot.Client, sequenceNumber client.EventManager.DispatchEvent(&events.GuildSoundboardSoundUpdate{ GenericGuildSoundboardSound: &events.GenericGuildSoundboardSound{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), SoundboardSound: event.SoundboardSound, }, OldGuildSoundboardSound: oldSound, @@ -34,7 +36,8 @@ func gatewayHandlerGuildSoundboardSoundDelete(client *bot.Client, sequenceNumber client.Caches.RemoveGuildSoundboardSound(event.GuildID, event.SoundID) client.EventManager.DispatchEvent(&events.GuildSoundboardSoundDelete{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), SoundID: event.SoundID, GuildID: event.GuildID, }) @@ -46,7 +49,8 @@ func gatewayHandlerGuildSoundboardSoundsUpdate(client *bot.Client, sequenceNumbe } client.EventManager.DispatchEvent(&events.GuildSoundboardSoundsUpdate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), SoundboardSounds: event.SoundboardSounds, GuildID: event.GuildID, }) @@ -54,7 +58,8 @@ func gatewayHandlerGuildSoundboardSoundsUpdate(client *bot.Client, sequenceNumbe func gatewayHandlerSoundboardSounds(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventSoundboardSounds) { client.EventManager.DispatchEvent(&events.SoundboardSounds{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), SoundboardSounds: event.SoundboardSounds, GuildID: event.GuildID, }) diff --git a/bot/handlers/guild_stickers_update_handler.go b/bot/handlers/guild_stickers_update_handler.go index e5756ed3e..8ff6948a8 100644 --- a/bot/handlers/guild_stickers_update_handler.go +++ b/bot/handlers/guild_stickers_update_handler.go @@ -17,7 +17,8 @@ type updatedSticker struct { func gatewayHandlerGuildStickersUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventGuildStickersUpdate) { client.EventManager.DispatchEvent(&events.StickersUpdate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), EventGuildStickersUpdate: event, }) @@ -48,7 +49,8 @@ func gatewayHandlerGuildStickersUpdate(client *bot.Client, sequenceNumber int, s for _, emoji := range createdStickers { client.EventManager.DispatchEvent(&events.StickerCreate{ GenericSticker: &events.GenericSticker{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, Sticker: emoji, }, @@ -58,7 +60,8 @@ func gatewayHandlerGuildStickersUpdate(client *bot.Client, sequenceNumber int, s for _, emoji := range updatedStickers { client.EventManager.DispatchEvent(&events.StickerUpdate{ GenericSticker: &events.GenericSticker{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, Sticker: emoji.new, }, @@ -69,7 +72,8 @@ func gatewayHandlerGuildStickersUpdate(client *bot.Client, sequenceNumber int, s for _, emoji := range deletedStickers { client.EventManager.DispatchEvent(&events.StickerDelete{ GenericSticker: &events.GenericSticker{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, Sticker: emoji, }, diff --git a/bot/handlers/handler_gateway.go b/bot/handlers/handler_gateway.go new file mode 100644 index 000000000..333f3c2c9 --- /dev/null +++ b/bot/handlers/handler_gateway.go @@ -0,0 +1,176 @@ +package handlers + +import ( + "github.com/disgoorg/disgo/bot" + "github.com/disgoorg/disgo/gateway" +) + +// GetGatewayHandler returns the default gateway.Gateway event handlers for processing the raw payload which gets passed into the bot.EventManager +func GetGatewayHandler() bot.GatewayEventHandler { + return &gatewayHandler{} +} + +type gatewayHandler struct{} + +func (g *gatewayHandler) HandleGatewayEvent(client *bot.Client, shardID int, message gateway.Message) { + switch event := message.D.(type) { + case gateway.EventRaw: + gatewayHandlerRaw(client, message.S, shardID, event) + case gateway.EventHeartbeatAck: + gatewayHandlerHeartbeatAck(client, message.S, shardID, event) + case gateway.EventReady: + gatewayHandlerReady(client, message.S, shardID, event) + case gateway.EventResumed: + gatewayHandlerResumed(client, message.S, shardID, event) + case gateway.EventApplicationCommandPermissionsUpdate: + gatewayHandlerApplicationCommandPermissionsUpdate(client, message.S, shardID, event) + case gateway.EventAutoModerationRuleCreate: + gatewayHandlerAutoModerationRuleCreate(client, message.S, shardID, event) + case gateway.EventAutoModerationRuleUpdate: + gatewayHandlerAutoModerationRuleUpdate(client, message.S, shardID, event) + case gateway.EventAutoModerationRuleDelete: + gatewayHandlerAutoModerationRuleDelete(client, message.S, shardID, event) + case gateway.EventAutoModerationActionExecution: + gatewayHandlerAutoModerationActionExecution(client, message.S, shardID, event) + case gateway.EventChannelCreate: + gatewayHandlerChannelCreate(client, message.S, shardID, event) + case gateway.EventChannelUpdate: + gatewayHandlerChannelUpdate(client, message.S, shardID, event) + case gateway.EventChannelDelete: + gatewayHandlerChannelDelete(client, message.S, shardID, event) + case gateway.EventChannelPinsUpdate: + gatewayHandlerChannelPinsUpdate(client, message.S, shardID, event) + case gateway.EventEntitlementCreate: + gatewayHandlerEntitlementCreate(client, message.S, shardID, event) + case gateway.EventEntitlementUpdate: + gatewayHandlerEntitlementUpdate(client, message.S, shardID, event) + case gateway.EventEntitlementDelete: + gatewayHandlerEntitlementDelete(client, message.S, shardID, event) + case gateway.EventThreadCreate: + gatewayHandlerThreadCreate(client, message.S, shardID, event) + case gateway.EventThreadUpdate: + gatewayHandlerThreadUpdate(client, message.S, shardID, event) + case gateway.EventThreadDelete: + gatewayHandlerThreadDelete(client, message.S, shardID, event) + case gateway.EventThreadListSync: + gatewayHandlerThreadListSync(client, message.S, shardID, event) + case gateway.EventThreadMemberUpdate: + gatewayHandlerThreadMemberUpdate(client, message.S, shardID, event) + case gateway.EventThreadMembersUpdate: + gatewayHandlerThreadMembersUpdate(client, message.S, shardID, event) + case gateway.EventGuildCreate: + gatewayHandlerGuildCreate(client, message.S, shardID, event) + case gateway.EventGuildUpdate: + gatewayHandlerGuildUpdate(client, message.S, shardID, event) + case gateway.EventGuildDelete: + gatewayHandlerGuildDelete(client, message.S, shardID, event) + case gateway.EventGuildAuditLogEntryCreate: + gatewayHandlerGuildAuditLogEntryCreate(client, message.S, shardID, event) + case gateway.EventGuildBanAdd: + gatewayHandlerGuildBanAdd(client, message.S, shardID, event) + case gateway.EventGuildBanRemove: + gatewayHandlerGuildBanRemove(client, message.S, shardID, event) + case gateway.EventGuildEmojisUpdate: + gatewayHandlerGuildEmojisUpdate(client, message.S, shardID, event) + case gateway.EventGuildStickersUpdate: + gatewayHandlerGuildStickersUpdate(client, message.S, shardID, event) + case gateway.EventGuildIntegrationsUpdate: + gatewayHandlerGuildIntegrationsUpdate(client, message.S, shardID, event) + case gateway.EventGuildMemberAdd: + gatewayHandlerGuildMemberAdd(client, message.S, shardID, event) + case gateway.EventGuildMemberRemove: + gatewayHandlerGuildMemberRemove(client, message.S, shardID, event) + case gateway.EventGuildMemberUpdate: + gatewayHandlerGuildMemberUpdate(client, message.S, shardID, event) + case gateway.EventGuildMembersChunk: + gatewayHandlerGuildMembersChunk(client, message.S, shardID, event) + case gateway.EventGuildRoleCreate: + gatewayHandlerGuildRoleCreate(client, message.S, shardID, event) + case gateway.EventGuildRoleUpdate: + gatewayHandlerGuildRoleUpdate(client, message.S, shardID, event) + case gateway.EventGuildRoleDelete: + gatewayHandlerGuildRoleDelete(client, message.S, shardID, event) + case gateway.EventGuildScheduledEventCreate: + gatewayHandlerGuildScheduledEventCreate(client, message.S, shardID, event) + case gateway.EventGuildScheduledEventUpdate: + gatewayHandlerGuildScheduledEventUpdate(client, message.S, shardID, event) + case gateway.EventGuildScheduledEventDelete: + gatewayHandlerGuildScheduledEventDelete(client, message.S, shardID, event) + case gateway.EventGuildScheduledEventUserAdd: + gatewayHandlerGuildScheduledEventUserAdd(client, message.S, shardID, event) + case gateway.EventGuildScheduledEventUserRemove: + gatewayHandlerGuildScheduledEventUserRemove(client, message.S, shardID, event) + case gateway.EventGuildSoundboardSoundCreate: + gatewayHandlerGuildSoundboardSoundCreate(client, message.S, shardID, event) + case gateway.EventGuildSoundboardSoundUpdate: + gatewayHandlerGuildSoundboardSoundUpdate(client, message.S, shardID, event) + case gateway.EventGuildSoundboardSoundDelete: + gatewayHandlerGuildSoundboardSoundDelete(client, message.S, shardID, event) + case gateway.EventGuildSoundboardSoundsUpdate: + gatewayHandlerGuildSoundboardSoundsUpdate(client, message.S, shardID, event) + case gateway.EventSoundboardSounds: + gatewayHandlerSoundboardSounds(client, message.S, shardID, event) + case gateway.EventIntegrationCreate: + gatewayHandlerIntegrationCreate(client, message.S, shardID, event) + case gateway.EventIntegrationUpdate: + gatewayHandlerIntegrationUpdate(client, message.S, shardID, event) + case gateway.EventIntegrationDelete: + gatewayHandlerIntegrationDelete(client, message.S, shardID, event) + case gateway.EventInteractionCreate: + gatewayHandlerInteractionCreate(client, message.S, shardID, event) + case gateway.EventInviteCreate: + gatewayHandlerInviteCreate(client, message.S, shardID, event) + case gateway.EventInviteDelete: + gatewayHandlerInviteDelete(client, message.S, shardID, event) + case gateway.EventMessageCreate: + gatewayHandlerMessageCreate(client, message.S, shardID, event) + case gateway.EventMessageUpdate: + gatewayHandlerMessageUpdate(client, message.S, shardID, event) + case gateway.EventMessageDelete: + gatewayHandlerMessageDelete(client, message.S, shardID, event) + case gateway.EventMessageDeleteBulk: + gatewayHandlerMessageDeleteBulk(client, message.S, shardID, event) + case gateway.EventMessagePollVoteAdd: + gatewayHandlerMessagePollVoteAdd(client, message.S, shardID, event) + case gateway.EventMessagePollVoteRemove: + gatewayHandlerMessagePollVoteRemove(client, message.S, shardID, event) + case gateway.EventMessageReactionAdd: + gatewayHandlerMessageReactionAdd(client, message.S, shardID, event) + case gateway.EventMessageReactionRemove: + gatewayHandlerMessageReactionRemove(client, message.S, shardID, event) + case gateway.EventMessageReactionRemoveAll: + gatewayHandlerMessageReactionRemoveAll(client, message.S, shardID, event) + case gateway.EventMessageReactionRemoveEmoji: + gatewayHandlerMessageReactionRemoveEmoji(client, message.S, shardID, event) + case gateway.EventPresenceUpdate: + gatewayHandlerPresenceUpdate(client, message.S, shardID, event) + case gateway.EventStageInstanceCreate: + gatewayHandlerStageInstanceCreate(client, message.S, shardID, event) + case gateway.EventStageInstanceUpdate: + gatewayHandlerStageInstanceUpdate(client, message.S, shardID, event) + case gateway.EventStageInstanceDelete: + gatewayHandlerStageInstanceDelete(client, message.S, shardID, event) + case gateway.EventSubscriptionCreate: + gatewayHandlerSubscriptionCreate(client, message.S, shardID, event) + case gateway.EventSubscriptionUpdate: + gatewayHandlerSubscriptionUpdate(client, message.S, shardID, event) + case gateway.EventSubscriptionDelete: + gatewayHandlerSubscriptionDelete(client, message.S, shardID, event) + case gateway.EventTypingStart: + gatewayHandlerTypingStart(client, message.S, shardID, event) + case gateway.EventUserUpdate: + gatewayHandlerUserUpdate(client, message.S, shardID, event) + case gateway.EventVoiceChannelEffectSend: + gatewayHandlerVoiceChannelEffectSend(client, message.S, shardID, event) + case gateway.EventVoiceStateUpdate: + gatewayHandlerVoiceStateUpdate(client, message.S, shardID, event) + case gateway.EventVoiceServerUpdate: + gatewayHandlerVoiceServerUpdate(client, message.S, shardID, event) + case gateway.EventWebhooksUpdate: + gatewayHandlerWebhooksUpdate(client, message.S, shardID, event) + case gateway.EventUnknown: + // nothing to do + default: + client.Logger.Warn("Unknown gateway event type", "event_type", message.T, "shard_id", shardID) + } +} diff --git a/bot/handlers/handler_http_gateway.go b/bot/handlers/handler_http_gateway.go new file mode 100644 index 000000000..6ddb23273 --- /dev/null +++ b/bot/handlers/handler_http_gateway.go @@ -0,0 +1,95 @@ +package handlers + +import ( + "log/slog" + "time" + + "github.com/disgoorg/disgo/bot" + "github.com/disgoorg/disgo/events" + "github.com/disgoorg/disgo/webhookevent" +) + +func GetHTTPGatewayHandler() bot.HTTPGatewayEventHandler { + return &httpGatewayHandler{} +} + +type httpGatewayHandler struct{} + +func (h *httpGatewayHandler) HandleHTTPGatewayEvent(client *bot.Client, message webhookevent.Message) { + switch event := message.Event.(type) { + case webhookevent.EventPing: + handleHTTPGatewayPing(client, message) + case webhookevent.Event: + handleHTTPGatewayEvent(client, message, event) + case webhookevent.EventUnknown: + // nothing to do + default: + client.Logger.Warn("received unknown http gateway event", slog.Any("event", event)) + } +} + +func handleHTTPGatewayEvent(client *bot.Client, message webhookevent.Message, event webhookevent.Event) { + switch data := event.Data.(type) { + case webhookevent.EventDataApplicationAuthorized: + handleHTTPGatewayEventDataApplicationAuthorized(client, message, event, data) + case webhookevent.EventDataApplicationDeauthorized: + handleHTTPGatewayEventDataApplicationDeauthorized(client, message, event, data) + case webhookevent.EventDataEntitlementCreate: + handleHTTPGatewayEventDataEntitlementCreate(client, message, event, data) + case webhookevent.EventDataQuestUserEnrollment: + handleHTTPGatewayEventDataQuestUserEnrollment(client, message, event, data) + case webhookevent.EventDataRaw: + handleHTTPGatewayEventDataRaw(client, message, event, data) + case webhookevent.EventDataUnknown: + // nothing to do + default: + client.Logger.Warn("received unknown http gateway event data", slog.Any("data", data)) + } +} + +func handleHTTPGatewayPing(client *bot.Client, message webhookevent.Message) { + client.EventManager.DispatchEvent(&events.WebhookPing{ + Event: events.NewEvent(client), + WebhookEvent: events.NewWebhookEvent(message.Version, message.ApplicationID, time.Now()), + }) +} + +func handleHTTPGatewayEventDataApplicationAuthorized(client *bot.Client, message webhookevent.Message, event webhookevent.Event, data webhookevent.EventDataApplicationAuthorized) { + client.EventManager.DispatchEvent(&events.ApplicationAuthorized{ + Event: events.NewEvent(client), + WebhookEvent: events.NewWebhookEvent(message.Version, message.ApplicationID, event.Timestamp), + IntegrationType: data.IntegrationType, + User: data.User, + Scopes: data.Scopes, + Guild: data.Guild, + }) +} + +func handleHTTPGatewayEventDataApplicationDeauthorized(client *bot.Client, message webhookevent.Message, event webhookevent.Event, data webhookevent.EventDataApplicationDeauthorized) { + client.EventManager.DispatchEvent(&events.ApplicationDeauthorized{ + Event: events.NewEvent(client), + WebhookEvent: events.NewWebhookEvent(message.Version, message.ApplicationID, event.Timestamp), + User: data.User, + }) +} + +func handleHTTPGatewayEventDataEntitlementCreate(client *bot.Client, message webhookevent.Message, event webhookevent.Event, data webhookevent.EventDataEntitlementCreate) { + client.EventManager.DispatchEvent(&events.EntitlementCreate{ + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(-1, -1), + WebhookEvent: events.NewWebhookEvent(message.Version, message.ApplicationID, event.Timestamp), + Entitlement: data.Entitlement, + }) +} + +func handleHTTPGatewayEventDataRaw(client *bot.Client, message webhookevent.Message, event webhookevent.Event, data webhookevent.EventDataRaw) { + client.EventManager.DispatchEvent(&events.WebhookRaw{ + Event: events.NewEvent(client), + WebhookEvent: events.NewWebhookEvent(message.Version, message.ApplicationID, event.Timestamp), + EventDataRaw: data, + }) +} + +func handleHTTPGatewayEventDataQuestUserEnrollment(client *bot.Client, message webhookevent.Message, event webhookevent.Event, data webhookevent.EventDataQuestUserEnrollment) { + // This event cannot be received by apps at this time. It's documented because it appears on the Webhooks settings page. +} diff --git a/bot/handlers/http_interaction_handler.go b/bot/handlers/handler_http_interaction.go similarity index 81% rename from bot/handlers/http_interaction_handler.go rename to bot/handlers/handler_http_interaction.go index f946e6661..51bd300f8 100644 --- a/bot/handlers/http_interaction_handler.go +++ b/bot/handlers/handler_http_interaction.go @@ -5,7 +5,7 @@ import ( "github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/discord" - "github.com/disgoorg/disgo/httpgateway" + "github.com/disgoorg/disgo/httpinteraction" ) // GetHTTPInteractionHandler returns the default httpserver.Server event handler for processing the raw payload which gets passed into the bot.EventManager @@ -15,9 +15,8 @@ func GetHTTPInteractionHandler() bot.HTTPInteractionEventHandler { type httpInteractionHandler struct{} -func (h *httpInteractionHandler) HandleHTTPInteraction(client *bot.Client, respond httpgateway.RespondFunc, event httpgateway.EventInteractionCreate) { - // we just want to pong all pings - // no need for any event +func (h *httpInteractionHandler) HandleHTTPInteraction(client *bot.Client, respond httpinteraction.RespondFunc, event httpinteraction.EventInteractionCreate) { + // we just want to pong all pings automatically if event.Type() == discord.InteractionTypePing { client.Logger.Debug("received http interaction ping. responding with pong") if err := respond(discord.InteractionResponse{ @@ -25,7 +24,6 @@ func (h *httpInteractionHandler) HandleHTTPInteraction(client *bot.Client, respo }); err != nil { client.Logger.Error("failed to respond to http interaction ping", slog.Any("err", err)) } - return } handleInteraction(client, -1, -1, respond, event.Interaction) } diff --git a/bot/handlers/http_gateway_handler.go b/bot/handlers/http_gateway_handler.go deleted file mode 100644 index 5d9e20872..000000000 --- a/bot/handlers/http_gateway_handler.go +++ /dev/null @@ -1,72 +0,0 @@ -package handlers - -import ( - "log/slog" - - "github.com/disgoorg/disgo/bot" - "github.com/disgoorg/disgo/httpgateway" -) - -func GetHTTPGatewayHandler() bot.HTTPGatewayEventHandler { - return &httpGatewayHandler{} -} - -type httpGatewayHandler struct{} - -func (h *httpGatewayHandler) HandleHTTPGatewayEvent(client *bot.Client, ack func(), message httpgateway.Message) { - switch event := message.Event.(type) { - case httpgateway.EventPing: - handleHTTPGatewayPing(client, ack, message) - case httpgateway.Event: - handleHTTPGatewayEvent(client, ack, message, event) - case httpgateway.EventUnknown: - handleHTTPGatewayEventUnknown(client, ack, message, event) - default: - client.Logger.Warn("received unknown http gateway event", slog.Any("event", event)) - } -} - -func handleHTTPGatewayEvent(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event) { - switch data := event.Data.(type) { - case httpgateway.EventDataApplicationAuthorized: - handleHTTPGatewayEventDataApplicationAuthorized(client, ack, message, event, data) - case httpgateway.EventDataApplicationDeauthorized: - handleHTTPGatewayEventDataApplicationDeauthorized(client, ack, message, event, data) - case httpgateway.EventDataEntitlementCreate: - handleHTTPGatewayEventDataEntitlementCreate(client, ack, message, event, data) - case httpgateway.EventDataQuestUserEnrollment: - handleHTTPGatewayEventDataQuestUserEnrollment(client, ack, message, event, data) - case httpgateway.EventDataUnknown: - handleHTTPGatewayEventDataUnknown(client, ack, message, event, data) - default: - client.Logger.Warn("received unknown http gateway event data", slog.Any("data", data)) - } -} - -func handleHTTPGatewayPing(client *bot.Client, ack func(), message httpgateway.Message) { - ack() - client.Logger.Debug("received http gateway ping", slog.String("application_id", message.ApplicationID.String()), slog.Int("version", message.Version)) -} - -func handleHTTPGatewayEventDataApplicationAuthorized(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event, data httpgateway.EventDataApplicationAuthorized) { -} - -func handleHTTPGatewayEventDataApplicationDeauthorized(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event, data httpgateway.EventDataApplicationDeauthorized) { - -} - -func handleHTTPGatewayEventDataEntitlementCreate(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event, data httpgateway.EventDataEntitlementCreate) { - -} - -func handleHTTPGatewayEventDataQuestUserEnrollment(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event, data httpgateway.EventDataQuestUserEnrollment) { - -} - -func handleHTTPGatewayEventDataUnknown(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.Event, data httpgateway.EventDataUnknown) { - -} - -func handleHTTPGatewayEventUnknown(client *bot.Client, ack func(), message httpgateway.Message, event httpgateway.EventUnknown) { - -} diff --git a/bot/handlers/integration_handlers.go b/bot/handlers/integration_handlers.go index 22e5014bf..02f1b9557 100644 --- a/bot/handlers/integration_handlers.go +++ b/bot/handlers/integration_handlers.go @@ -9,7 +9,8 @@ import ( func gatewayHandlerIntegrationCreate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventIntegrationCreate) { client.EventManager.DispatchEvent(&events.IntegrationCreate{ GenericIntegration: &events.GenericIntegration{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, Integration: event.Integration, }, @@ -19,7 +20,8 @@ func gatewayHandlerIntegrationCreate(client *bot.Client, sequenceNumber int, sha func gatewayHandlerIntegrationUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventIntegrationUpdate) { client.EventManager.DispatchEvent(&events.IntegrationUpdate{ GenericIntegration: &events.GenericIntegration{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, Integration: event.Integration, }, @@ -28,7 +30,8 @@ func gatewayHandlerIntegrationUpdate(client *bot.Client, sequenceNumber int, sha func gatewayHandlerIntegrationDelete(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventIntegrationDelete) { client.EventManager.DispatchEvent(&events.IntegrationDelete{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, ID: event.ID, ApplicationID: event.ApplicationID, diff --git a/bot/handlers/interaction_create_handler.go b/bot/handlers/interaction_create_handler.go index b31a7bd74..7b4911870 100644 --- a/bot/handlers/interaction_create_handler.go +++ b/bot/handlers/interaction_create_handler.go @@ -8,7 +8,7 @@ import ( "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/gateway" - "github.com/disgoorg/disgo/httpgateway" + "github.com/disgoorg/disgo/httpinteraction" "github.com/disgoorg/disgo/rest" ) @@ -16,55 +16,58 @@ func gatewayHandlerInteractionCreate(client *bot.Client, sequenceNumber int, sha handleInteraction(client, sequenceNumber, shardID, nil, event.Interaction) } -func respond(client *bot.Client, respondFunc httpgateway.RespondFunc, interaction discord.Interaction) events.InteractionResponderFunc { +func respondFunc(client *bot.Client, respond httpinteraction.RespondFunc, interaction discord.Interaction) events.InteractionResponderFunc { return func(responseType discord.InteractionResponseType, data discord.InteractionResponseData, opts ...rest.RequestOpt) error { response := discord.InteractionResponse{ Type: responseType, Data: data, } - if respondFunc != nil { - return respondFunc(response) + if respond != nil { + return respond(response) } return client.Rest.CreateInteractionResponse(interaction.ID(), interaction.Token(), response, opts...) } } -func handleInteraction(client *bot.Client, sequenceNumber int, shardID int, respondFunc httpgateway.RespondFunc, interaction discord.Interaction) { - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) - +func handleInteraction(client *bot.Client, sequenceNumber int, shardID int, respond httpinteraction.RespondFunc, interaction discord.Interaction) { client.EventManager.DispatchEvent(&events.InteractionCreate{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), Interaction: interaction, - Respond: respond(client, respondFunc, interaction), + Respond: respondFunc(client, respond, interaction), }) switch i := interaction.(type) { case discord.ApplicationCommandInteraction: client.EventManager.DispatchEvent(&events.ApplicationCommandInteractionCreate{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ApplicationCommandInteraction: i, - Respond: respond(client, respondFunc, interaction), + Respond: respondFunc(client, respond, interaction), }) case discord.ComponentInteraction: client.EventManager.DispatchEvent(&events.ComponentInteractionCreate{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ComponentInteraction: i, - Respond: respond(client, respondFunc, interaction), + Respond: respondFunc(client, respond, interaction), }) case discord.AutocompleteInteraction: client.EventManager.DispatchEvent(&events.AutocompleteInteractionCreate{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), AutocompleteInteraction: i, - Respond: respond(client, respondFunc, interaction), + Respond: respondFunc(client, respond, interaction), }) case discord.ModalSubmitInteraction: client.EventManager.DispatchEvent(&events.ModalSubmitInteractionCreate{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ModalSubmitInteraction: i, - Respond: respond(client, respondFunc, interaction), + Respond: respondFunc(client, respond, interaction), }) default: diff --git a/bot/handlers/invite_handlers.go b/bot/handlers/invite_handlers.go index e5ff506ad..8df58ccdb 100644 --- a/bot/handlers/invite_handlers.go +++ b/bot/handlers/invite_handlers.go @@ -8,14 +8,16 @@ import ( func gatewayHandlerInviteCreate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventInviteCreate) { client.EventManager.DispatchEvent(&events.InviteCreate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), EventInviteCreate: event, }) } func gatewayHandlerInviteDelete(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventInviteDelete) { client.EventManager.DispatchEvent(&events.InviteDelete{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, ChannelID: event.ChannelID, Code: event.Code, diff --git a/bot/handlers/message_handler.go b/bot/handlers/message_handler.go index 3d282c66f..7847d8797 100644 --- a/bot/handlers/message_handler.go +++ b/bot/handlers/message_handler.go @@ -31,10 +31,10 @@ func gatewayHandlerMessageCreate(client *bot.Client, sequenceNumber int, shardID client.Caches.AddChannel(channel) } - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) client.EventManager.DispatchEvent(&events.MessageCreate{ GenericMessage: &events.GenericMessage{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.ID, Message: event.Message, ChannelID: event.ChannelID, @@ -45,7 +45,8 @@ func gatewayHandlerMessageCreate(client *bot.Client, sequenceNumber int, shardID if event.GuildID == nil { client.EventManager.DispatchEvent(&events.DMMessageCreate{ GenericDMMessage: &events.GenericDMMessage{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.ID, Message: event.Message, ChannelID: event.ChannelID, @@ -54,7 +55,8 @@ func gatewayHandlerMessageCreate(client *bot.Client, sequenceNumber int, shardID } else { client.EventManager.DispatchEvent(&events.GuildMessageCreate{ GenericGuildMessage: &events.GenericGuildMessage{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.ID, Message: event.Message, ChannelID: event.ChannelID, @@ -68,10 +70,10 @@ func gatewayHandlerMessageUpdate(client *bot.Client, sequenceNumber int, shardID oldMessage, _ := client.Caches.Message(event.ChannelID, event.ID) client.Caches.AddMessage(event.Message) - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) client.EventManager.DispatchEvent(&events.MessageUpdate{ GenericMessage: &events.GenericMessage{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.ID, Message: event.Message, ChannelID: event.ChannelID, @@ -83,7 +85,8 @@ func gatewayHandlerMessageUpdate(client *bot.Client, sequenceNumber int, shardID if event.GuildID == nil { client.EventManager.DispatchEvent(&events.DMMessageUpdate{ GenericDMMessage: &events.GenericDMMessage{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.ID, Message: event.Message, ChannelID: event.ChannelID, @@ -93,7 +96,8 @@ func gatewayHandlerMessageUpdate(client *bot.Client, sequenceNumber int, shardID } else { client.EventManager.DispatchEvent(&events.GuildMessageUpdate{ GenericGuildMessage: &events.GenericGuildMessage{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.ID, Message: event.Message, ChannelID: event.ChannelID, @@ -115,8 +119,6 @@ func gatewayHandlerMessageDeleteBulk(client *bot.Client, sequenceNumber int, sha } func handleMessageDelete(client *bot.Client, sequenceNumber int, shardID int, messageID snowflake.ID, channelID snowflake.ID, guildID *snowflake.ID) { - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) - message, _ := client.Caches.RemoveMessage(channelID, messageID) if channel, ok := client.Caches.GuildThread(channelID); ok { @@ -128,7 +130,8 @@ func handleMessageDelete(client *bot.Client, sequenceNumber int, shardID int, me client.EventManager.DispatchEvent(&events.MessageDelete{ GenericMessage: &events.GenericMessage{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: messageID, Message: message, ChannelID: channelID, @@ -139,7 +142,8 @@ func handleMessageDelete(client *bot.Client, sequenceNumber int, shardID int, me if guildID == nil { client.EventManager.DispatchEvent(&events.DMMessageDelete{ GenericDMMessage: &events.GenericDMMessage{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: messageID, Message: message, ChannelID: channelID, @@ -148,7 +152,8 @@ func handleMessageDelete(client *bot.Client, sequenceNumber int, shardID int, me } else { client.EventManager.DispatchEvent(&events.GuildMessageDelete{ GenericGuildMessage: &events.GenericGuildMessage{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: messageID, Message: message, ChannelID: channelID, diff --git a/bot/handlers/message_poll_handler.go b/bot/handlers/message_poll_handler.go index 96f2dbb57..9b098bbad 100644 --- a/bot/handlers/message_poll_handler.go +++ b/bot/handlers/message_poll_handler.go @@ -7,11 +7,10 @@ import ( ) func gatewayHandlerMessagePollVoteAdd(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventMessagePollVoteAdd) { - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) - client.EventManager.DispatchEvent(&events.MessagePollVoteAdd{ GenericMessagePollVote: &events.GenericMessagePollVote{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), UserID: event.UserID, ChannelID: event.ChannelID, MessageID: event.MessageID, @@ -23,7 +22,8 @@ func gatewayHandlerMessagePollVoteAdd(client *bot.Client, sequenceNumber int, sh if event.GuildID == nil { client.EventManager.DispatchEvent(&events.DMMessagePollVoteAdd{ GenericDMMessagePollVote: &events.GenericDMMessagePollVote{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), UserID: event.UserID, ChannelID: event.ChannelID, MessageID: event.MessageID, @@ -33,7 +33,8 @@ func gatewayHandlerMessagePollVoteAdd(client *bot.Client, sequenceNumber int, sh } else { client.EventManager.DispatchEvent(&events.GuildMessagePollVoteAdd{ GenericGuildMessagePollVote: &events.GenericGuildMessagePollVote{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), UserID: event.UserID, ChannelID: event.ChannelID, MessageID: event.MessageID, @@ -45,11 +46,10 @@ func gatewayHandlerMessagePollVoteAdd(client *bot.Client, sequenceNumber int, sh } func gatewayHandlerMessagePollVoteRemove(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventMessagePollVoteRemove) { - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) - client.EventManager.DispatchEvent(&events.MessagePollVoteRemove{ GenericMessagePollVote: &events.GenericMessagePollVote{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), UserID: event.UserID, ChannelID: event.ChannelID, MessageID: event.MessageID, @@ -61,7 +61,8 @@ func gatewayHandlerMessagePollVoteRemove(client *bot.Client, sequenceNumber int, if event.GuildID == nil { client.EventManager.DispatchEvent(&events.DMMessagePollVoteRemove{ GenericDMMessagePollVote: &events.GenericDMMessagePollVote{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), UserID: event.UserID, ChannelID: event.ChannelID, MessageID: event.MessageID, @@ -71,7 +72,8 @@ func gatewayHandlerMessagePollVoteRemove(client *bot.Client, sequenceNumber int, } else { client.EventManager.DispatchEvent(&events.GuildMessagePollVoteRemove{ GenericGuildMessagePollVote: &events.GenericGuildMessagePollVote{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), UserID: event.UserID, ChannelID: event.ChannelID, MessageID: event.MessageID, diff --git a/bot/handlers/message_reaction_handler.go b/bot/handlers/message_reaction_handler.go index 7f52840e8..c4ae6daf6 100644 --- a/bot/handlers/message_reaction_handler.go +++ b/bot/handlers/message_reaction_handler.go @@ -8,11 +8,10 @@ import ( ) func gatewayHandlerMessageReactionAdd(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventMessageReactionAdd) { - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) - client.EventManager.DispatchEvent(&events.MessageReactionAdd{ GenericReaction: &events.GenericReaction{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, GuildID: event.GuildID, @@ -27,7 +26,8 @@ func gatewayHandlerMessageReactionAdd(client *bot.Client, sequenceNumber int, sh if event.GuildID == nil { client.EventManager.DispatchEvent(&events.DMMessageReactionAdd{ GenericDMMessageReaction: &events.GenericDMMessageReaction{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, UserID: event.UserID, @@ -45,7 +45,8 @@ func gatewayHandlerMessageReactionAdd(client *bot.Client, sequenceNumber int, sh } client.EventManager.DispatchEvent(&events.GuildMessageReactionAdd{ GenericGuildMessageReaction: &events.GenericGuildMessageReaction{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, GuildID: *event.GuildID, @@ -61,11 +62,10 @@ func gatewayHandlerMessageReactionAdd(client *bot.Client, sequenceNumber int, sh } func gatewayHandlerMessageReactionRemove(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventMessageReactionRemove) { - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) - client.EventManager.DispatchEvent(&events.MessageReactionRemove{ GenericReaction: &events.GenericReaction{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, GuildID: event.GuildID, @@ -79,7 +79,8 @@ func gatewayHandlerMessageReactionRemove(client *bot.Client, sequenceNumber int, if event.GuildID == nil { client.EventManager.DispatchEvent(&events.DMMessageReactionRemove{ GenericDMMessageReaction: &events.GenericDMMessageReaction{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, UserID: event.UserID, @@ -91,7 +92,8 @@ func gatewayHandlerMessageReactionRemove(client *bot.Client, sequenceNumber int, } else { client.EventManager.DispatchEvent(&events.GuildMessageReactionRemove{ GenericGuildMessageReaction: &events.GenericGuildMessageReaction{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, GuildID: *event.GuildID, @@ -105,10 +107,9 @@ func gatewayHandlerMessageReactionRemove(client *bot.Client, sequenceNumber int, } func gatewayHandlerMessageReactionRemoveAll(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventMessageReactionRemoveAll) { - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) - client.EventManager.DispatchEvent(&events.MessageReactionRemoveAll{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, GuildID: event.GuildID, @@ -116,13 +117,15 @@ func gatewayHandlerMessageReactionRemoveAll(client *bot.Client, sequenceNumber i if event.GuildID == nil { client.EventManager.DispatchEvent(&events.DMMessageReactionRemoveAll{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, }) } else { client.EventManager.DispatchEvent(&events.GuildMessageReactionRemoveAll{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, GuildID: *event.GuildID, @@ -131,10 +134,9 @@ func gatewayHandlerMessageReactionRemoveAll(client *bot.Client, sequenceNumber i } func gatewayHandlerMessageReactionRemoveEmoji(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventMessageReactionRemoveEmoji) { - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) - client.EventManager.DispatchEvent(&events.MessageReactionRemoveEmoji{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, GuildID: event.GuildID, @@ -143,14 +145,16 @@ func gatewayHandlerMessageReactionRemoveEmoji(client *bot.Client, sequenceNumber if event.GuildID == nil { client.EventManager.DispatchEvent(&events.DMMessageReactionRemoveEmoji{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, Emoji: event.Emoji, }) } else { client.EventManager.DispatchEvent(&events.GuildMessageReactionRemoveEmoji{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), MessageID: event.MessageID, ChannelID: event.ChannelID, GuildID: *event.GuildID, diff --git a/bot/handlers/presence_update_handler.go b/bot/handlers/presence_update_handler.go index c641a8317..3de499ce5 100644 --- a/bot/handlers/presence_update_handler.go +++ b/bot/handlers/presence_update_handler.go @@ -13,10 +13,9 @@ import ( ) func gatewayHandlerPresenceUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventPresenceUpdate) { - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) - client.EventManager.DispatchEvent(&events.PresenceUpdate{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), EventPresenceUpdate: event, }) @@ -40,7 +39,8 @@ func gatewayHandlerPresenceUpdate(client *bot.Client, sequenceNumber int, shardI if oldStatus != event.Status { client.EventManager.DispatchEvent(&events.UserStatusUpdate{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), UserID: event.PresenceUser.ID, OldStatus: oldStatus, Status: event.Status, @@ -49,7 +49,8 @@ func gatewayHandlerPresenceUpdate(client *bot.Client, sequenceNumber int, shardI if oldClientStatus.Desktop != event.ClientStatus.Desktop || oldClientStatus.Mobile != event.ClientStatus.Mobile || oldClientStatus.Web != event.ClientStatus.Web { client.EventManager.DispatchEvent(&events.UserClientStatusUpdate{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), UserID: event.PresenceUser.ID, OldClientStatus: oldClientStatus, ClientStatus: event.ClientStatus, @@ -57,7 +58,8 @@ func gatewayHandlerPresenceUpdate(client *bot.Client, sequenceNumber int, shardI } genericUserActivityEvent := events.GenericUserActivity{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), UserID: event.PresenceUser.ID, GuildID: event.GuildID, } diff --git a/bot/handlers/stage_instance_handler.go b/bot/handlers/stage_instance_handler.go index 8879b4eb0..7abde79af 100644 --- a/bot/handlers/stage_instance_handler.go +++ b/bot/handlers/stage_instance_handler.go @@ -11,7 +11,8 @@ func gatewayHandlerStageInstanceCreate(client *bot.Client, sequenceNumber int, s client.EventManager.DispatchEvent(&events.StageInstanceCreate{ GenericStageInstance: &events.GenericStageInstance{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), StageInstanceID: event.ID, StageInstance: event.StageInstance, }, @@ -24,7 +25,8 @@ func gatewayHandlerStageInstanceUpdate(client *bot.Client, sequenceNumber int, s client.EventManager.DispatchEvent(&events.StageInstanceUpdate{ GenericStageInstance: &events.GenericStageInstance{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), StageInstanceID: event.ID, StageInstance: event.StageInstance, }, @@ -37,7 +39,8 @@ func gatewayHandlerStageInstanceDelete(client *bot.Client, sequenceNumber int, s client.EventManager.DispatchEvent(&events.StageInstanceDelete{ GenericStageInstance: &events.GenericStageInstance{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), StageInstanceID: event.ID, StageInstance: event.StageInstance, }, diff --git a/bot/handlers/status_handler.go b/bot/handlers/status_handler.go index 2359e5d48..331deec62 100644 --- a/bot/handlers/status_handler.go +++ b/bot/handlers/status_handler.go @@ -7,15 +7,17 @@ import ( ) func gatewayHandlerRaw(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventRaw) { - client.EventManager.DispatchEvent(&events.Raw{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + client.EventManager.DispatchEvent(&events.GatewayRaw{ + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), EventRaw: event, }) } func gatewayHandlerHeartbeatAck(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventHeartbeatAck) { client.EventManager.DispatchEvent(&events.HeartbeatAck{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), EventHeartbeatAck: event, }) } @@ -28,13 +30,15 @@ func gatewayHandlerReady(client *bot.Client, sequenceNumber int, shardID int, ev } client.EventManager.DispatchEvent(&events.Ready{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), EventReady: event, }) } func gatewayHandlerResumed(client *bot.Client, sequenceNumber int, shardID int, _ gateway.EventData) { client.EventManager.DispatchEvent(&events.Resumed{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), }) } diff --git a/bot/handlers/subscription_handlers.go b/bot/handlers/subscription_handlers.go index 012d5a077..f4c142102 100644 --- a/bot/handlers/subscription_handlers.go +++ b/bot/handlers/subscription_handlers.go @@ -9,7 +9,8 @@ import ( func gatewayHandlerSubscriptionCreate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventSubscriptionCreate) { client.EventManager.DispatchEvent(&events.SubscriptionCreate{ GenericSubscriptionEvent: &events.GenericSubscriptionEvent{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), Subscription: event.Subscription, }, }) @@ -18,7 +19,8 @@ func gatewayHandlerSubscriptionCreate(client *bot.Client, sequenceNumber int, sh func gatewayHandlerSubscriptionUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventSubscriptionUpdate) { client.EventManager.DispatchEvent(&events.SubscriptionUpdate{ GenericSubscriptionEvent: &events.GenericSubscriptionEvent{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), Subscription: event.Subscription, }, }) @@ -27,7 +29,8 @@ func gatewayHandlerSubscriptionUpdate(client *bot.Client, sequenceNumber int, sh func gatewayHandlerSubscriptionDelete(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventSubscriptionDelete) { client.EventManager.DispatchEvent(&events.SubscriptionDelete{ GenericSubscriptionEvent: &events.GenericSubscriptionEvent{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), Subscription: event.Subscription, }, }) diff --git a/bot/handlers/thread_handler.go b/bot/handlers/thread_handler.go index 0549f0443..ae1ec82cf 100644 --- a/bot/handlers/thread_handler.go +++ b/bot/handlers/thread_handler.go @@ -13,7 +13,8 @@ func gatewayHandlerThreadCreate(client *bot.Client, sequenceNumber int, shardID client.EventManager.DispatchEvent(&events.ThreadCreate{ GenericThread: &events.GenericThread{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ThreadID: event.ID(), GuildID: event.GuildID(), Thread: event.GuildThread, @@ -29,7 +30,8 @@ func gatewayHandlerThreadUpdate(client *bot.Client, sequenceNumber int, shardID client.EventManager.DispatchEvent(&events.ThreadUpdate{ GenericThread: &events.GenericThread{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), Thread: event.GuildThread, ThreadID: event.ID(), GuildID: event.GuildID(), @@ -48,7 +50,8 @@ func gatewayHandlerThreadDelete(client *bot.Client, sequenceNumber int, shardID client.EventManager.DispatchEvent(&events.ThreadDelete{ GenericThread: &events.GenericThread{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ThreadID: event.ID, GuildID: event.GuildID, ParentID: event.ParentID, @@ -62,7 +65,8 @@ func gatewayHandlerThreadListSync(client *bot.Client, sequenceNumber int, shardI client.Caches.AddChannel(thread) client.EventManager.DispatchEvent(&events.ThreadShow{ GenericThread: &events.GenericThread{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), Thread: thread, ThreadID: thread.ID(), GuildID: event.GuildID, @@ -76,8 +80,6 @@ func gatewayHandlerThreadMemberUpdate(_ *bot.Client, _ int, _ int, _ gateway.Eve } func gatewayHandlerThreadMembersUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventThreadMembersUpdate) { - genericEvent := events.NewGenericEvent(client, sequenceNumber, shardID) - if thread, ok := client.Caches.GuildThread(event.ID); ok { thread.MemberCount = event.MemberCount client.Caches.AddChannel(thread) @@ -94,7 +96,8 @@ func gatewayHandlerThreadMembersUpdate(client *bot.Client, sequenceNumber int, s client.EventManager.DispatchEvent(&events.ThreadMemberAdd{ GenericThreadMember: &events.GenericThreadMember{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, ThreadID: event.ID, ThreadMemberID: addedMember.UserID, @@ -110,7 +113,8 @@ func gatewayHandlerThreadMembersUpdate(client *bot.Client, sequenceNumber int, s client.EventManager.DispatchEvent(&events.ThreadMemberRemove{ GenericThreadMember: &events.GenericThreadMember{ - GenericEvent: genericEvent, + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildID: event.GuildID, ThreadID: event.ID, ThreadMemberID: removedMemberID, diff --git a/bot/handlers/typing_start_handler.go b/bot/handlers/typing_start_handler.go index b4303f01f..769a9fcd1 100644 --- a/bot/handlers/typing_start_handler.go +++ b/bot/handlers/typing_start_handler.go @@ -9,7 +9,8 @@ import ( func gatewayHandlerTypingStart(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventTypingStart) { client.EventManager.DispatchEvent(&events.UserTypingStart{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ChannelID: event.ChannelID, GuildID: event.GuildID, UserID: event.UserID, @@ -18,7 +19,8 @@ func gatewayHandlerTypingStart(client *bot.Client, sequenceNumber int, shardID i if event.GuildID == nil { client.EventManager.DispatchEvent(&events.DMUserTypingStart{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ChannelID: event.ChannelID, UserID: event.UserID, Timestamp: event.Timestamp, @@ -29,7 +31,8 @@ func gatewayHandlerTypingStart(client *bot.Client, sequenceNumber int, shardID i member = *event.Member } client.EventManager.DispatchEvent(&events.GuildMemberTypingStart{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), ChannelID: event.ChannelID, UserID: event.UserID, GuildID: *event.GuildID, diff --git a/bot/handlers/user_update_handler.go b/bot/handlers/user_update_handler.go index 0b9c0e924..605010306 100644 --- a/bot/handlers/user_update_handler.go +++ b/bot/handlers/user_update_handler.go @@ -11,7 +11,8 @@ func gatewayHandlerUserUpdate(client *bot.Client, sequenceNumber int, shardID in client.Caches.SetSelfUser(event.OAuth2User) client.EventManager.DispatchEvent(&events.SelfUpdate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), SelfUser: event.OAuth2User, OldSelfUser: oldUser, }) diff --git a/bot/handlers/voice_handlers.go b/bot/handlers/voice_handlers.go index 25ed566dc..c5d5faf58 100644 --- a/bot/handlers/voice_handlers.go +++ b/bot/handlers/voice_handlers.go @@ -8,7 +8,8 @@ import ( func gatewayHandlerVoiceChannelEffectSend(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventVoiceChannelEffectSend) { client.EventManager.DispatchEvent(&events.GuildVoiceChannelEffectSend{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), EventVoiceChannelEffectSend: event, }) } @@ -29,7 +30,8 @@ func gatewayHandlerVoiceStateUpdate(client *bot.Client, sequenceNumber int, shar } genericGuildVoiceEvent := &events.GenericGuildVoiceState{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), VoiceState: event.VoiceState, Member: member, } @@ -64,7 +66,8 @@ func gatewayHandlerVoiceServerUpdate(client *bot.Client, sequenceNumber int, sha } client.EventManager.DispatchEvent(&events.VoiceServerUpdate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), EventVoiceServerUpdate: event, }) } diff --git a/bot/handlers/webhooks_update_handler.go b/bot/handlers/webhooks_update_handler.go index 69619d3bc..5b2e2b051 100644 --- a/bot/handlers/webhooks_update_handler.go +++ b/bot/handlers/webhooks_update_handler.go @@ -8,7 +8,8 @@ import ( func gatewayHandlerWebhooksUpdate(client *bot.Client, sequenceNumber int, shardID int, event gateway.EventWebhooksUpdate) { client.EventManager.DispatchEvent(&events.WebhooksUpdate{ - GenericEvent: events.NewGenericEvent(client, sequenceNumber, shardID), + Event: events.NewEvent(client), + GatewayEvent: events.NewGatewayEvent(sequenceNumber, shardID), GuildId: event.GuildID, ChannelID: event.ChannelID, }) diff --git a/events/application.go b/events/application.go new file mode 100644 index 000000000..6bc42aadf --- /dev/null +++ b/events/application.go @@ -0,0 +1,20 @@ +package events + +import ( + "github.com/disgoorg/disgo/discord" +) + +type ApplicationAuthorized struct { + *Event + *WebhookEvent + IntegrationType discord.IntegrationType + User discord.User + Scopes []discord.OAuth2Scope + Guild discord.Guild +} + +type ApplicationDeauthorized struct { + *Event + *WebhookEvent + User discord.User +} diff --git a/events/dm_channel_events.go b/events/dm_channel_events.go index 11711b7bf..e69c57ce8 100644 --- a/events/dm_channel_events.go +++ b/events/dm_channel_events.go @@ -8,14 +8,16 @@ import ( // DMChannelPinsUpdate indicates that a discord.Message got pinned or unpinned. type DMChannelPinsUpdate struct { - *GenericEvent + *Event + *GatewayEvent ChannelID snowflake.ID NewLastPinTimestamp *time.Time } // DMUserTypingStart indicates that a discord.User started typing in a discord.DMChannel(requires gateway.IntentDirectMessageTyping). type DMUserTypingStart struct { - *GenericEvent + *Event + *GatewayEvent ChannelID snowflake.ID UserID snowflake.ID Timestamp time.Time diff --git a/events/dm_message_event_events.go b/events/dm_message_event_events.go index b37d29a37..01160940a 100644 --- a/events/dm_message_event_events.go +++ b/events/dm_message_event_events.go @@ -8,7 +8,8 @@ import ( // GenericDMMessage is called upon receiving DMMessageCreate , DMMessageUpdate , DMMessageDelete , GenericDMMessageReaction , DMMessageReactionAdd , DMMessageReactionRemove , DMMessageReactionRemoveEmoji or DMMessageReactionRemoveAll (requires gateway.IntentsDirectMessage) type GenericDMMessage struct { - *GenericEvent + *Event + *GatewayEvent MessageID snowflake.ID Message discord.Message ChannelID snowflake.ID diff --git a/events/dm_message_poll_events.go b/events/dm_message_poll_events.go index a80db4a6a..d6b406c7e 100644 --- a/events/dm_message_poll_events.go +++ b/events/dm_message_poll_events.go @@ -6,7 +6,8 @@ import ( // GenericDMMessagePollVote is called upon receiving DMMessagePollVoteAdd or DMMessagePollVoteRemove (requires gateway.IntentDirectMessagePolls) type GenericDMMessagePollVote struct { - *GenericEvent + *Event + *GatewayEvent UserID snowflake.ID ChannelID snowflake.ID MessageID snowflake.ID diff --git a/events/dm_message_reaction_events.go b/events/dm_message_reaction_events.go index 20de1db55..d9f240460 100644 --- a/events/dm_message_reaction_events.go +++ b/events/dm_message_reaction_events.go @@ -8,7 +8,8 @@ import ( // GenericDMMessageReaction is called upon receiving DMMessageReactionAdd or DMMessageReactionRemove (requires the gateway.IntentDirectMessageReactions) type GenericDMMessageReaction struct { - *GenericEvent + *Event + *GatewayEvent UserID snowflake.ID ChannelID snowflake.ID MessageID snowflake.ID @@ -30,7 +31,8 @@ type DMMessageReactionRemove struct { // DMMessageReactionRemoveEmoji indicates someone removed all discord.MessageReaction(s) of a specific discord.Emoji from a discord.Message in a Channel (requires the gateway.IntentDirectMessageReactions) type DMMessageReactionRemoveEmoji struct { - *GenericEvent + *Event + *GatewayEvent ChannelID snowflake.ID MessageID snowflake.ID Emoji discord.PartialEmoji @@ -38,7 +40,8 @@ type DMMessageReactionRemoveEmoji struct { // DMMessageReactionRemoveAll indicates someone removed all discord.MessageReaction(s) from a discord.Message in a Channel (requires the gateway.IntentDirectMessageReactions) type DMMessageReactionRemoveAll struct { - *GenericEvent + *Event + *GatewayEvent ChannelID snowflake.ID MessageID snowflake.ID } diff --git a/events/entitlement_events.go b/events/entitlement_events.go index 68d79931b..a019efd59 100644 --- a/events/entitlement_events.go +++ b/events/entitlement_events.go @@ -1,14 +1,20 @@ package events -import "github.com/disgoorg/disgo/discord" +import ( + "github.com/disgoorg/disgo/discord" +) type GenericEntitlementEvent struct { - *GenericEvent + *Event + *GatewayEvent discord.Entitlement } type EntitlementCreate struct { - *GenericEntitlementEvent + *Event + *GatewayEvent + *WebhookEvent + discord.Entitlement } type EntitlementUpdate struct { diff --git a/events/gateway_status_events.go b/events/gateway_status_events.go index 06896537c..0464649d1 100644 --- a/events/gateway_status_events.go +++ b/events/gateway_status_events.go @@ -4,11 +4,13 @@ import "github.com/disgoorg/disgo/gateway" // Ready indicates we received the Ready from the gateway.Gateway type Ready struct { - *GenericEvent + *Event + *GatewayEvent gateway.EventReady } // Resumed indicates disgo resumed the gateway.Gateway type Resumed struct { - *GenericEvent + *Event + *GatewayEvent } diff --git a/events/generic_event.go b/events/generic_event.go index 42ba6a732..524955bed 100644 --- a/events/generic_event.go +++ b/events/generic_event.go @@ -1,32 +1,77 @@ package events import ( + "time" + + "github.com/disgoorg/snowflake/v2" + "github.com/disgoorg/disgo/bot" ) -// NewGenericEvent constructs a new GenericEvent with the provided Client instance -func NewGenericEvent(client *bot.Client, sequenceNumber int, shardID int) *GenericEvent { - return &GenericEvent{client: client, sequenceNumber: sequenceNumber, shardID: shardID} +// NewEvent constructs a new event with the provided Client instance +func NewEvent(client *bot.Client) *Event { + return &Event{client: client} } -// GenericEvent the base event structure -type GenericEvent struct { - client *bot.Client - sequenceNumber int - shardID int +// event the base event structure +type Event struct { + client *bot.Client } // Client returns the bot.Client instance that dispatched the event -func (e *GenericEvent) Client() *bot.Client { +func (e *Event) Client() *bot.Client { return e.client } +func NewGatewayEvent(sequenceNumber int, shardID int) *GatewayEvent { + return &GatewayEvent{ + sequenceNumber: sequenceNumber, + shardID: shardID, + } +} + +type GatewayEvent struct { + sequenceNumber int + shardID int +} + // SequenceNumber returns the sequence number of the gateway event -func (e *GenericEvent) SequenceNumber() int { +func (e *GatewayEvent) SequenceNumber() int { return e.sequenceNumber } // ShardID returns the shard ID the event was dispatched from -func (e *GenericEvent) ShardID() int { +func (e *GatewayEvent) ShardID() int { return e.shardID } + +// NewWebhookEvent constructs a new WebhookEvent with the provided Client instance and timestamp +func NewWebhookEvent(version int, applicationID snowflake.ID, timestamp time.Time) *WebhookEvent { + return &WebhookEvent{ + version: version, + applicationID: applicationID, + timestamp: timestamp, + } +} + +// WebhookEvent represents an event received from a webhook +type WebhookEvent struct { + version int + applicationID snowflake.ID + timestamp time.Time +} + +// Version returns the version of the webhook event +func (e *WebhookEvent) Version() int { + return e.version +} + +// ApplicationID returns the application ID associated with the webhook event +func (e *WebhookEvent) ApplicationID() snowflake.ID { + return e.applicationID +} + +// Timestamp returns the timestamp of the webhook event +func (e *WebhookEvent) Timestamp() time.Time { + return e.timestamp +} diff --git a/events/guild_auto_moderation_events.go b/events/guild_auto_moderation_events.go index 07e7708a3..c2072d8cf 100644 --- a/events/guild_auto_moderation_events.go +++ b/events/guild_auto_moderation_events.go @@ -6,7 +6,8 @@ import ( ) type GenericAutoModerationRule struct { - *GenericEvent + *Event + *GatewayEvent discord.AutoModerationRule } @@ -29,7 +30,8 @@ type AutoModerationRuleDelete struct { } type AutoModerationActionExecution struct { - *GenericEvent + *Event + *GatewayEvent gateway.EventAutoModerationActionExecution } diff --git a/events/guild_channel_events.go b/events/guild_channel_events.go index 0b117cb84..67be06c28 100644 --- a/events/guild_channel_events.go +++ b/events/guild_channel_events.go @@ -10,7 +10,8 @@ import ( // GenericGuildChannel is called upon receiving GuildChannelCreate , GuildChannelUpdate or GuildChannelDelete type GenericGuildChannel struct { - *GenericEvent + *Event + *GatewayEvent ChannelID snowflake.ID Channel discord.GuildChannel GuildID snowflake.ID @@ -40,7 +41,8 @@ type GuildChannelDelete struct { // GuildChannelPinsUpdate indicates a discord.Message got pinned or unpinned in a discord.GuildMessageChannel type GuildChannelPinsUpdate struct { - *GenericEvent + *Event + *GatewayEvent GuildID snowflake.ID ChannelID snowflake.ID NewLastPinTimestamp *time.Time diff --git a/events/guild_emoji_events.go b/events/guild_emoji_events.go index 80d97235b..f5e9a349d 100644 --- a/events/guild_emoji_events.go +++ b/events/guild_emoji_events.go @@ -10,13 +10,15 @@ import ( // EmojisUpdate is dispatched when a guild's emojis are updated. // This event does not depend on a cache like EmojiCreate, EmojiUpdate or EmojiDelete. type EmojisUpdate struct { - *GenericEvent + *Event + *GatewayEvent gateway.EventGuildEmojisUpdate } // GenericEmoji is called upon receiving EmojiCreate , EmojiUpdate or EmojiDelete (requires gateway.IntentGuildExpressions) type GenericEmoji struct { - *GenericEvent + *Event + *GatewayEvent GuildID snowflake.ID Emoji discord.Emoji } diff --git a/events/guild_events.go b/events/guild_events.go index d55123d97..8a93fa7a2 100644 --- a/events/guild_events.go +++ b/events/guild_events.go @@ -8,7 +8,8 @@ import ( // GenericGuild represents a generic guild event type GenericGuild struct { - *GenericEvent + *Event + *GatewayEvent GuildID snowflake.ID } @@ -51,7 +52,8 @@ type GuildReady struct { // GuildsReady is called when all discord.Guild(s) are loaded after logging in type GuildsReady struct { - *GenericEvent + *Event + *GatewayEvent } // GuildBan is called when a discord.Member/discord.User is banned from the discord.Guild diff --git a/events/guild_integration_events.go b/events/guild_integration_events.go index 98e419774..a2cedd7ce 100644 --- a/events/guild_integration_events.go +++ b/events/guild_integration_events.go @@ -8,7 +8,8 @@ import ( // GenericIntegration is called upon receiving IntegrationCreate, IntegrationUpdate or IntegrationDelete(requires the gateway.IntentGuildIntegrations) type GenericIntegration struct { - *GenericEvent + *Event + *GatewayEvent GuildID snowflake.ID Integration discord.Integration } @@ -31,7 +32,8 @@ type IntegrationUpdate struct { // IntegrationDelete indicates that an Integration was deleted from a Guild type IntegrationDelete struct { - *GenericEvent + *Event + *GatewayEvent ID snowflake.ID GuildID snowflake.ID ApplicationID *snowflake.ID @@ -39,12 +41,14 @@ type IntegrationDelete struct { // GuildIntegrationsUpdate indicates that a Guild's integrations were updated type GuildIntegrationsUpdate struct { - *GenericEvent + *Event + *GatewayEvent GuildID snowflake.ID } // GuildApplicationCommandPermissionsUpdate indicates that a Guild's application's permissions were updated type GuildApplicationCommandPermissionsUpdate struct { - *GenericEvent + *Event + *GatewayEvent Permissions discord.ApplicationCommandPermissions } diff --git a/events/guild_invite_events.go b/events/guild_invite_events.go index 60e6dd9df..3274a5318 100644 --- a/events/guild_invite_events.go +++ b/events/guild_invite_events.go @@ -9,7 +9,8 @@ import ( // InviteCreate is called upon creation of a new discord.Invite (requires gateway.IntentGuildInvites) type InviteCreate struct { - *GenericEvent + *Event + *GatewayEvent gateway.EventInviteCreate } @@ -28,7 +29,8 @@ func (e *InviteCreate) Guild() (discord.Guild, bool) { // InviteDelete is called upon deletion of a discord.Invite (requires gateway.IntentGuildInvites) type InviteDelete struct { - *GenericEvent + *Event + *GatewayEvent GuildID *snowflake.ID ChannelID snowflake.ID diff --git a/events/guild_member_events.go b/events/guild_member_events.go index 532419c3b..cc9e8f863 100644 --- a/events/guild_member_events.go +++ b/events/guild_member_events.go @@ -10,7 +10,8 @@ import ( // GenericGuildMember generic discord.Member event type GenericGuildMember struct { - *GenericEvent + *Event + *GatewayEvent GuildID snowflake.ID Member discord.Member } @@ -28,7 +29,8 @@ type GuildMemberUpdate struct { // GuildMemberLeave indicates that a discord.Member left the discord.Guild type GuildMemberLeave struct { - *GenericEvent + *Event + *GatewayEvent GuildID snowflake.ID User discord.User Member discord.Member @@ -39,7 +41,8 @@ type GuildMemberLeave struct { // // [Clyde bot]: https://support.discord.com/hc/en-us/articles/13066317497239-Clyde-Discord-s-AI-Chatbot type GuildMemberTypingStart struct { - *GenericEvent + *Event + *GatewayEvent ChannelID snowflake.ID UserID snowflake.ID GuildID snowflake.ID diff --git a/events/guild_message_events.go b/events/guild_message_events.go index dd1553356..2ca898d92 100644 --- a/events/guild_message_events.go +++ b/events/guild_message_events.go @@ -8,7 +8,8 @@ import ( // GenericGuildMessage is called upon receiving GuildMessageCreate , GuildMessageUpdate or GuildMessageDelete type GenericGuildMessage struct { - *GenericEvent + *Event + *GatewayEvent MessageID snowflake.ID Message discord.Message ChannelID snowflake.ID diff --git a/events/guild_message_poll_events.go b/events/guild_message_poll_events.go index 7be8a4419..c22cf31be 100644 --- a/events/guild_message_poll_events.go +++ b/events/guild_message_poll_events.go @@ -8,7 +8,8 @@ import ( // GenericGuildMessagePollVote is called upon receiving GuildMessagePollVoteAdd or GuildMessagePollVoteRemove (requires gateway.IntentGuildMessagePolls) type GenericGuildMessagePollVote struct { - *GenericEvent + *Event + *GatewayEvent UserID snowflake.ID ChannelID snowflake.ID MessageID snowflake.ID diff --git a/events/guild_message_reaction_events.go b/events/guild_message_reaction_events.go index a83ea4cce..483549730 100644 --- a/events/guild_message_reaction_events.go +++ b/events/guild_message_reaction_events.go @@ -8,7 +8,8 @@ import ( // GenericGuildMessageReaction is called upon receiving GuildMessageReactionAdd or GuildMessageReactionRemove type GenericGuildMessageReaction struct { - *GenericEvent + *Event + *GatewayEvent UserID snowflake.ID ChannelID snowflake.ID MessageID snowflake.ID @@ -37,7 +38,8 @@ type GuildMessageReactionRemove struct { // GuildMessageReactionRemoveEmoji indicates someone removed all discord.MessageReaction of a specific discord.Emoji from a discord.Message in a Channel (requires the gateway.IntentGuildMessageReactions) type GuildMessageReactionRemoveEmoji struct { - *GenericEvent + *Event + *GatewayEvent ChannelID snowflake.ID MessageID snowflake.ID GuildID snowflake.ID @@ -46,7 +48,8 @@ type GuildMessageReactionRemoveEmoji struct { // GuildMessageReactionRemoveAll indicates someone removed all discord.MessageReaction(s) from a discord.Message in a Channel (requires the gateway.IntentGuildMessageReactions) type GuildMessageReactionRemoveAll struct { - *GenericEvent + *Event + *GatewayEvent ChannelID snowflake.ID MessageID snowflake.ID GuildID snowflake.ID diff --git a/events/guild_role_events.go b/events/guild_role_events.go index 454874643..a306c8a15 100644 --- a/events/guild_role_events.go +++ b/events/guild_role_events.go @@ -8,7 +8,8 @@ import ( // GenericRole generic discord.Role event type GenericRole struct { - *GenericEvent + *Event + *GatewayEvent GuildID snowflake.ID RoleID snowflake.ID Role discord.Role diff --git a/events/guild_scheduled_events_events.go b/events/guild_scheduled_events_events.go index afea7e294..d41323617 100644 --- a/events/guild_scheduled_events_events.go +++ b/events/guild_scheduled_events_events.go @@ -8,7 +8,8 @@ import ( // GenericGuildScheduledEvent is the base struct for all GuildScheduledEvents events. type GenericGuildScheduledEvent struct { - *GenericEvent + *Event + *GatewayEvent GuildScheduled discord.GuildScheduledEvent } @@ -30,7 +31,8 @@ type GuildScheduledEventDelete struct { // GenericGuildScheduledEventUser is the base struct for all GuildScheduledEventUser events. type GenericGuildScheduledEventUser struct { - *GenericEvent + *Event + *GatewayEvent GuildScheduledEventID snowflake.ID UserID snowflake.ID GuildID snowflake.ID diff --git a/events/guild_soundboard_events.go b/events/guild_soundboard_events.go index c8244253d..27951d71f 100644 --- a/events/guild_soundboard_events.go +++ b/events/guild_soundboard_events.go @@ -8,7 +8,8 @@ import ( // GenericGuildSoundboardSound is called upon receiving GuildSoundboardSoundCreate and GuildSoundboardSoundUpdate (requires gateway.IntentGuildExpressions) type GenericGuildSoundboardSound struct { - *GenericEvent + *Event + *GatewayEvent discord.SoundboardSound } @@ -25,21 +26,24 @@ type GuildSoundboardSoundUpdate struct { // GuildSoundboardSoundDelete indicates that a discord.SoundboardSound was deleted in a discord.Guild (requires gateway.IntentGuildExpressions) type GuildSoundboardSoundDelete struct { - *GenericEvent + *Event + *GatewayEvent SoundID snowflake.ID GuildID snowflake.ID } // GuildSoundboardSoundsUpdate indicates when multiple discord.Guild soundboard sounds were updated (requires gateway.IntentGuildExpressions) type GuildSoundboardSoundsUpdate struct { - *GenericEvent + *Event + *GatewayEvent SoundboardSounds []discord.SoundboardSound GuildID snowflake.ID } // SoundboardSounds is a response to gateway.OpcodeRequestSoundboardSounds type SoundboardSounds struct { - *GenericEvent + *Event + *GatewayEvent SoundboardSounds []discord.SoundboardSound GuildID snowflake.ID } diff --git a/events/guild_stage_instance_events.go b/events/guild_stage_instance_events.go index bd3121a65..48dc8a908 100644 --- a/events/guild_stage_instance_events.go +++ b/events/guild_stage_instance_events.go @@ -8,7 +8,8 @@ import ( // GenericStageInstance generic StageInstance event type GenericStageInstance struct { - *GenericEvent + *Event + *GatewayEvent StageInstanceID snowflake.ID StageInstance discord.StageInstance } diff --git a/events/guild_sticker_events.go b/events/guild_sticker_events.go index 9984f5318..0c1c4a149 100644 --- a/events/guild_sticker_events.go +++ b/events/guild_sticker_events.go @@ -10,13 +10,15 @@ import ( // StickersUpdate is dispatched when a guild's stickers are updated. // This event does not depend on a cache like StickerCreate, StickerUpdate or StickerDelete. type StickersUpdate struct { - *GenericEvent + *Event + *GatewayEvent gateway.EventGuildStickersUpdate } // GenericSticker is called upon receiving StickerCreate , StickerUpdate or StickerDelete (requires gateway.IntentGuildExpressions) type GenericSticker struct { - *GenericEvent + *Event + *GatewayEvent GuildID snowflake.ID Sticker discord.Sticker } diff --git a/events/guild_thread_events.go b/events/guild_thread_events.go index 60be737b9..b888620b7 100644 --- a/events/guild_thread_events.go +++ b/events/guild_thread_events.go @@ -8,7 +8,8 @@ import ( // GenericThread is the base struct for all Thread events. type GenericThread struct { - *GenericEvent + *Event + *GatewayEvent Thread discord.GuildThread ThreadID snowflake.ID GuildID snowflake.ID @@ -45,7 +46,8 @@ type ThreadHide struct { // GenericThreadMember is the base struct for all ThreadMember events. type GenericThreadMember struct { - *GenericEvent + *Event + *GatewayEvent GuildID snowflake.ID ThreadID snowflake.ID ThreadMemberID snowflake.ID diff --git a/events/guild_voice_events.go b/events/guild_voice_events.go index 3f8d4593f..d321749c8 100644 --- a/events/guild_voice_events.go +++ b/events/guild_voice_events.go @@ -7,13 +7,15 @@ import ( // GuildVoiceChannelEffectSend indicates that a discord.Member sent an effect in a discord.GuildVoiceChannel (requires gateway.IntentGuildVoiceStates) type GuildVoiceChannelEffectSend struct { - *GenericEvent + *Event + *GatewayEvent gateway.EventVoiceChannelEffectSend } // GenericGuildVoiceState is called upon receiving GuildVoiceJoin, GuildVoiceMove and GuildVoiceLeave type GenericGuildVoiceState struct { - *GenericEvent + *Event + *GatewayEvent VoiceState discord.VoiceState Member discord.Member } @@ -43,6 +45,7 @@ type GuildVoiceLeave struct { // VoiceServerUpdate indicates that a voice server the bot is connected to has been changed type VoiceServerUpdate struct { - *GenericEvent + *Event + *GatewayEvent gateway.EventVoiceServerUpdate } diff --git a/events/guild_webhooks_update_events.go b/events/guild_webhooks_update_events.go index bb6be0020..fb4185ec3 100644 --- a/events/guild_webhooks_update_events.go +++ b/events/guild_webhooks_update_events.go @@ -8,7 +8,8 @@ import ( // WebhooksUpdate indicates that a guilds webhooks were updated. type WebhooksUpdate struct { - *GenericEvent + *Event + *GatewayEvent GuildId snowflake.ID ChannelID snowflake.ID } diff --git a/events/heartbeat_ack.go b/events/heartbeat_ack.go deleted file mode 100644 index f54aa32df..000000000 --- a/events/heartbeat_ack.go +++ /dev/null @@ -1,8 +0,0 @@ -package events - -import "github.com/disgoorg/disgo/gateway" - -type HeartbeatAck struct { - *GenericEvent - gateway.EventHeartbeatAck -} diff --git a/events/interaction_events.go b/events/interaction_events.go index c1c62849f..274e5b1e7 100644 --- a/events/interaction_events.go +++ b/events/interaction_events.go @@ -10,7 +10,8 @@ type InteractionResponderFunc func(responseType discord.InteractionResponseType, // InteractionCreate indicates that a new interaction has been created. type InteractionCreate struct { - *GenericEvent + *Event + *GatewayEvent discord.Interaction Respond InteractionResponderFunc } @@ -27,7 +28,8 @@ func (e *InteractionCreate) Guild() (discord.Guild, bool) { // ApplicationCommandInteractionCreate is the base struct for all application command interaction create events. type ApplicationCommandInteractionCreate struct { - *GenericEvent + *Event + *GatewayEvent discord.ApplicationCommandInteraction Respond InteractionResponderFunc } @@ -87,7 +89,8 @@ func (e *ApplicationCommandInteractionCreate) LaunchActivity(opts ...rest.Reques // ComponentInteractionCreate indicates that a new component interaction has been created. type ComponentInteractionCreate struct { - *GenericEvent + *Event + *GatewayEvent discord.ComponentInteraction Respond InteractionResponderFunc } @@ -157,7 +160,8 @@ func (e *ComponentInteractionCreate) LaunchActivity(opts ...rest.RequestOpt) err // AutocompleteInteractionCreate indicates that a new autocomplete interaction has been created. type AutocompleteInteractionCreate struct { - *GenericEvent + *Event + *GatewayEvent discord.AutocompleteInteraction Respond InteractionResponderFunc } @@ -198,7 +202,8 @@ func (e *AutocompleteInteractionCreate) AutocompleteResult(choices []discord.Aut // ModalSubmitInteractionCreate indicates that a new modal submit interaction has been created. type ModalSubmitInteractionCreate struct { - *GenericEvent + *Event + *GatewayEvent discord.ModalSubmitInteraction Respond InteractionResponderFunc } diff --git a/events/listener_adapter.go b/events/listener_adapter.go index 28564a3a8..99aba935b 100644 --- a/events/listener_adapter.go +++ b/events/listener_adapter.go @@ -12,7 +12,8 @@ var _ bot.EventListener = (*ListenerAdapter)(nil) // ListenerAdapter lets you override the handles for receiving events type ListenerAdapter struct { // raw event - OnRaw func(event *Raw) + OnGatewayRaw func(event *GatewayRaw) + OnWebhookRaw func(event *WebhookRaw) // heartbeat ack event OnHeartbeatAck func(event *HeartbeatAck) @@ -208,8 +209,12 @@ type ListenerAdapter struct { // OnEvent is getting called everytime we receive an event func (l *ListenerAdapter) OnEvent(event bot.Event) { switch e := event.(type) { - case *Raw: - if listener := l.OnRaw; listener != nil { + case *GatewayRaw: + if listener := l.OnGatewayRaw; listener != nil { + listener(e) + } + case *WebhookRaw: + if listener := l.OnWebhookRaw; listener != nil { listener(e) } diff --git a/events/message_events.go b/events/message_events.go index d568ef3f2..12c995095 100644 --- a/events/message_events.go +++ b/events/message_events.go @@ -8,7 +8,8 @@ import ( // GenericMessage generic discord.Message event type GenericMessage struct { - *GenericEvent + *Event + *GatewayEvent MessageID snowflake.ID Message discord.Message ChannelID snowflake.ID diff --git a/events/message_poll_events.go b/events/message_poll_events.go index db35f4d26..637587a94 100644 --- a/events/message_poll_events.go +++ b/events/message_poll_events.go @@ -8,7 +8,8 @@ import ( // GenericMessagePollVote is a generic poll vote event (requires gateway.IntentGuildMessagePolls and/or gateway.IntentDirectMessagePolls) type GenericMessagePollVote struct { - *GenericEvent + *Event + *GatewayEvent UserID snowflake.ID ChannelID snowflake.ID MessageID snowflake.ID diff --git a/events/message_reaction_events.go b/events/message_reaction_events.go index 6e0070169..e2da818dc 100644 --- a/events/message_reaction_events.go +++ b/events/message_reaction_events.go @@ -8,7 +8,8 @@ import ( // GenericReaction is called upon receiving MessageReactionAdd or MessageReactionRemove type GenericReaction struct { - *GenericEvent + *GatewayEvent + *Event UserID snowflake.ID ChannelID snowflake.ID MessageID snowflake.ID @@ -31,7 +32,8 @@ type MessageReactionRemove struct { // MessageReactionRemoveEmoji indicates someone removed all discord.MessageReaction of a specific discord.Emoji from a discord.Message in a discord.Channel(requires the gateway.IntentGuildMessageReactions and/or gateway.IntentDirectMessageReactions) type MessageReactionRemoveEmoji struct { - *GenericEvent + *GatewayEvent + *Event ChannelID snowflake.ID MessageID snowflake.ID GuildID *snowflake.ID @@ -40,7 +42,8 @@ type MessageReactionRemoveEmoji struct { // MessageReactionRemoveAll indicates someone removed all discord.MessageReaction(s) from a discord.Message in a discord.Channel(requires the gateway.IntentGuildMessageReactions and/or gateway.IntentDirectMessageReactions) type MessageReactionRemoveAll struct { - *GenericEvent + *GatewayEvent + *Event ChannelID snowflake.ID MessageID snowflake.ID GuildID *snowflake.ID diff --git a/events/other_events.go b/events/other_events.go new file mode 100644 index 000000000..879471aea --- /dev/null +++ b/events/other_events.go @@ -0,0 +1,29 @@ +package events + +import ( + "github.com/disgoorg/disgo/gateway" + "github.com/disgoorg/disgo/webhookevent" +) + +type HeartbeatAck struct { + *Event + *GatewayEvent + gateway.EventHeartbeatAck +} + +type WebhookPing struct { + *Event + *WebhookEvent +} + +type GatewayRaw struct { + *Event + *GatewayEvent + gateway.EventRaw +} + +type WebhookRaw struct { + *Event + *WebhookEvent + webhookevent.EventDataRaw +} diff --git a/events/raw_event.go b/events/raw_event.go deleted file mode 100644 index 89e71598f..000000000 --- a/events/raw_event.go +++ /dev/null @@ -1,8 +0,0 @@ -package events - -import "github.com/disgoorg/disgo/gateway" - -type Raw struct { - *GenericEvent - gateway.EventRaw -} diff --git a/events/self_update_events.go b/events/self_update_events.go index 4fc603f18..b94a408d0 100644 --- a/events/self_update_events.go +++ b/events/self_update_events.go @@ -6,7 +6,8 @@ import ( // SelfUpdate is called when something about this discord.User updates type SelfUpdate struct { - *GenericEvent + *Event + *GatewayEvent SelfUser discord.OAuth2User OldSelfUser discord.OAuth2User } diff --git a/events/subscription_events.go b/events/subscription_events.go index 884fb90c0..d15e86a88 100644 --- a/events/subscription_events.go +++ b/events/subscription_events.go @@ -3,7 +3,8 @@ package events import "github.com/disgoorg/disgo/discord" type GenericSubscriptionEvent struct { - *GenericEvent + *Event + *GatewayEvent discord.Subscription } diff --git a/events/user_activity_events.go b/events/user_activity_events.go index 3c35013e5..36b0442b0 100644 --- a/events/user_activity_events.go +++ b/events/user_activity_events.go @@ -8,13 +8,15 @@ import ( ) type PresenceUpdate struct { - *GenericEvent + *Event + *GatewayEvent gateway.EventPresenceUpdate } // GenericUserActivity generic Activity event type GenericUserActivity struct { - *GenericEvent + *Event + *GatewayEvent UserID snowflake.ID GuildID snowflake.ID Activity discord.Activity diff --git a/events/user_events.go b/events/user_events.go index e6e500aa8..1d1e2f2eb 100644 --- a/events/user_events.go +++ b/events/user_events.go @@ -10,7 +10,8 @@ import ( // GenericUser is called upon receiving UserUpdate or UserTypingStart type GenericUser struct { - *GenericEvent + *Event + *GatewayEvent UserID snowflake.ID User discord.User } @@ -23,7 +24,8 @@ type UserUpdate struct { // UserTypingStart indicates that a discord.User started typing in a discord.DMChannel or discord.MessageChanel(requires the gateway.IntentDirectMessageTyping and/or gateway.IntentGuildMessageTyping) type UserTypingStart struct { - *GenericEvent + *Event + *GatewayEvent ChannelID snowflake.ID GuildID *snowflake.ID UserID snowflake.ID diff --git a/events/user_status_events.go b/events/user_status_events.go index f470db3c0..2791ab294 100644 --- a/events/user_status_events.go +++ b/events/user_status_events.go @@ -8,7 +8,8 @@ import ( // UserStatusUpdate generic Status event type UserStatusUpdate struct { - *GenericEvent + *Event + *GatewayEvent UserID snowflake.ID OldStatus discord.OnlineStatus Status discord.OnlineStatus @@ -16,7 +17,8 @@ type UserStatusUpdate struct { // UserClientStatusUpdate generic client-specific Status event type UserClientStatusUpdate struct { - *GenericEvent + *Event + *GatewayEvent UserID snowflake.ID OldClientStatus discord.ClientStatus ClientStatus discord.ClientStatus diff --git a/gateway/gateway.go b/gateway/gateway.go index 4ed67be53..1e67ed6c9 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -640,7 +640,6 @@ loop: g.lastHeartbeatReceived = newHeartbeat default: - g.config.Logger.Debug("unknown opcode received", slog.Int("opcode", int(message.Op)), slog.String("data", fmt.Sprintf("%s", message.D))) } } diff --git a/gateway/gateway_event_type.go b/gateway/gateway_event_type.go index ac4843a13..b6755415f 100644 --- a/gateway/gateway_event_type.go +++ b/gateway/gateway_event_type.go @@ -6,8 +6,10 @@ type EventType string // Constants for the gateway events const ( // EventTypeRaw is not a real event type, but is used to pass raw payloads to the bot.EventManager - EventTypeRaw EventType = "__RAW__" - EventTypeHeartbeatAck EventType = "__HEARTBEAT_ACK__" + EventTypeRaw EventType = "__RAW__" + // EventTypeHeartbeatAck is not a real event type, but is used to pass heartbeat acknowledgments to the bot.EventManager + EventTypeHeartbeatAck EventType = "__HEARTBEAT_ACK__" + EventTypeReady EventType = "READY" EventTypeResumed EventType = "RESUMED" EventTypeApplicationCommandPermissionsUpdate EventType = "APPLICATION_COMMAND_PERMISSIONS_UPDATE" diff --git a/handler/handler.go b/handler/handler.go index 42003734b..6c88f270a 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -83,7 +83,8 @@ func (h *handlerHolder[T]) Handle(path string, event *InteractionEvent) error { case CommandHandler: return handler(&CommandEvent{ ApplicationCommandInteractionCreate: &events.ApplicationCommandInteractionCreate{ - GenericEvent: event.GenericEvent, + Event: event.Event, + GatewayEvent: event.GatewayEvent, ApplicationCommandInteraction: event.Interaction.(discord.ApplicationCommandInteraction), Respond: event.Respond, }, @@ -94,7 +95,8 @@ func (h *handlerHolder[T]) Handle(path string, event *InteractionEvent) error { commandInteraction := event.Interaction.(discord.ApplicationCommandInteraction) return handler(commandInteraction.Data.(discord.SlashCommandInteractionData), &CommandEvent{ ApplicationCommandInteractionCreate: &events.ApplicationCommandInteractionCreate{ - GenericEvent: event.GenericEvent, + Event: event.Event, + GatewayEvent: event.GatewayEvent, ApplicationCommandInteraction: commandInteraction, Respond: event.Respond, }, @@ -105,7 +107,8 @@ func (h *handlerHolder[T]) Handle(path string, event *InteractionEvent) error { commandInteraction := event.Interaction.(discord.ApplicationCommandInteraction) return handler(commandInteraction.Data.(discord.UserCommandInteractionData), &CommandEvent{ ApplicationCommandInteractionCreate: &events.ApplicationCommandInteractionCreate{ - GenericEvent: event.GenericEvent, + Event: event.Event, + GatewayEvent: event.GatewayEvent, ApplicationCommandInteraction: commandInteraction, Respond: event.Respond, }, @@ -116,7 +119,8 @@ func (h *handlerHolder[T]) Handle(path string, event *InteractionEvent) error { commandInteraction := event.Interaction.(discord.ApplicationCommandInteraction) return handler(commandInteraction.Data.(discord.MessageCommandInteractionData), &CommandEvent{ ApplicationCommandInteractionCreate: &events.ApplicationCommandInteractionCreate{ - GenericEvent: event.GenericEvent, + Event: event.Event, + GatewayEvent: event.GatewayEvent, ApplicationCommandInteraction: commandInteraction, Respond: event.Respond, }, @@ -127,7 +131,8 @@ func (h *handlerHolder[T]) Handle(path string, event *InteractionEvent) error { commandInteraction := event.Interaction.(discord.ApplicationCommandInteraction) return handler(commandInteraction.Data.(discord.EntryPointCommandInteractionData), &CommandEvent{ ApplicationCommandInteractionCreate: &events.ApplicationCommandInteractionCreate{ - GenericEvent: event.GenericEvent, + Event: event.Event, + GatewayEvent: event.GatewayEvent, ApplicationCommandInteraction: commandInteraction, Respond: event.Respond, }, @@ -137,7 +142,8 @@ func (h *handlerHolder[T]) Handle(path string, event *InteractionEvent) error { case AutocompleteHandler: return handler(&AutocompleteEvent{ AutocompleteInteractionCreate: &events.AutocompleteInteractionCreate{ - GenericEvent: event.GenericEvent, + Event: event.Event, + GatewayEvent: event.GatewayEvent, AutocompleteInteraction: event.Interaction.(discord.AutocompleteInteraction), Respond: event.Respond, }, @@ -147,7 +153,8 @@ func (h *handlerHolder[T]) Handle(path string, event *InteractionEvent) error { case ComponentHandler: return handler(&ComponentEvent{ ComponentInteractionCreate: &events.ComponentInteractionCreate{ - GenericEvent: event.GenericEvent, + Event: event.Event, + GatewayEvent: event.GatewayEvent, ComponentInteraction: event.Interaction.(discord.ComponentInteraction), Respond: event.Respond, }, @@ -158,7 +165,8 @@ func (h *handlerHolder[T]) Handle(path string, event *InteractionEvent) error { componentInteraction := event.Interaction.(discord.ComponentInteraction) return handler(componentInteraction.Data.(discord.ButtonInteractionData), &ComponentEvent{ ComponentInteractionCreate: &events.ComponentInteractionCreate{ - GenericEvent: event.GenericEvent, + Event: event.Event, + GatewayEvent: event.GatewayEvent, ComponentInteraction: componentInteraction, Respond: event.Respond, }, @@ -169,7 +177,8 @@ func (h *handlerHolder[T]) Handle(path string, event *InteractionEvent) error { componentInteraction := event.Interaction.(discord.ComponentInteraction) return handler(componentInteraction.Data.(discord.SelectMenuInteractionData), &ComponentEvent{ ComponentInteractionCreate: &events.ComponentInteractionCreate{ - GenericEvent: event.GenericEvent, + Event: event.Event, + GatewayEvent: event.GatewayEvent, ComponentInteraction: componentInteraction, Respond: event.Respond, }, @@ -179,7 +188,8 @@ func (h *handlerHolder[T]) Handle(path string, event *InteractionEvent) error { case ModalHandler: return handler(&ModalEvent{ ModalSubmitInteractionCreate: &events.ModalSubmitInteractionCreate{ - GenericEvent: event.GenericEvent, + Event: event.Event, + GatewayEvent: event.GatewayEvent, ModalSubmitInteraction: event.Interaction.(discord.ModalSubmitInteraction), Respond: event.Respond, }, diff --git a/handler/mux_test.go b/handler/mux_test.go index 69053ae12..6386c2622 100644 --- a/handler/mux_test.go +++ b/handler/mux_test.go @@ -111,7 +111,8 @@ func TestCommandMux(t *testing.T) { recorder := NewRecorder() mux.OnEvent(&events.InteractionCreate{ - GenericEvent: events.NewGenericEvent(nil, 0, 0), + Event: events.NewEvent(nil), + GatewayEvent: events.NewGatewayEvent(0, 0), Interaction: interaction, Respond: recorder.Respond, }) @@ -168,7 +169,8 @@ func TestComponentMux(t *testing.T) { recorder := NewRecorder() mux.OnEvent(&events.InteractionCreate{ - GenericEvent: events.NewGenericEvent(nil, 0, 0), + Event: events.NewEvent(nil), + GatewayEvent: events.NewGatewayEvent(0, 0), Interaction: interaction, Respond: recorder.Respond, }) @@ -235,7 +237,8 @@ func TestMiddlewareMux(t *testing.T) { recorder := NewRecorder() mux.OnEvent(&events.InteractionCreate{ - GenericEvent: events.NewGenericEvent(nil, 0, 0), + Event: events.NewEvent(nil), + GatewayEvent: events.NewGatewayEvent(0, 0), Interaction: interaction, Respond: recorder.Respond, }) @@ -297,7 +300,8 @@ func TestMux(t *testing.T) { recorder := NewRecorder() mux.OnEvent(&events.InteractionCreate{ - GenericEvent: events.NewGenericEvent(nil, 0, 0), + Event: events.NewEvent(nil), + GatewayEvent: events.NewGatewayEvent(0, 0), Interaction: interaction, Respond: recorder.Respond, }) diff --git a/httpgateway/config.go b/httpgateway/config.go deleted file mode 100644 index 6e7e0e3c9..000000000 --- a/httpgateway/config.go +++ /dev/null @@ -1,97 +0,0 @@ -package httpgateway - -import ( - "log/slog" - "net/http" -) - -func defaultConfig() config { - return config{ - Logger: slog.Default(), - HTTPServer: &http.Server{}, - ServeMux: http.NewServeMux(), - InteractionURL: "/interactions/callback", - EventURL: "/events/callback", - Address: ":80", - Verifier: DefaultVerifier{}, - } -} - -type config struct { - Logger *slog.Logger - HTTPServer *http.Server - ServeMux *http.ServeMux - InteractionURL string - EventURL string - Address string - CertFile string - KeyFile string - Verifier Verifier -} - -// ConfigOpt is a type alias for a function that takes a config and is used to configure your Server. -type ConfigOpt func(config *config) - -func (c *config) apply(opts []ConfigOpt) { - for _, opt := range opts { - opt(c) - } - c.Logger = c.Logger.With(slog.String("name", "httpserver")) -} - -// WithLogger sets the Logger of the config. -func WithLogger(logger *slog.Logger) ConfigOpt { - return func(config *config) { - config.Logger = logger - } -} - -// WithHTTPServer sets the http.Server of the config. -func WithHTTPServer(httpServer *http.Server) ConfigOpt { - return func(config *config) { - config.HTTPServer = httpServer - } -} - -// WithServeMux sets the http.ServeMux of the config. -func WithServeMux(serveMux *http.ServeMux) ConfigOpt { - return func(config *config) { - config.ServeMux = serveMux - } -} - -// WithInteractionURL sets the InteractionURL of the config. -func WithInteractionURL(url string) ConfigOpt { - return func(config *config) { - config.InteractionURL = url - } -} - -// WithEventURL sets the EventURL of the config. -func WithEventURL(url string) ConfigOpt { - return func(config *config) { - config.EventURL = url - } -} - -// WithAddress sets the Address of the config. -func WithAddress(address string) ConfigOpt { - return func(config *config) { - config.Address = address - } -} - -// WithTLS sets the CertFile of the config. -func WithTLS(certFile string, keyFile string) ConfigOpt { - return func(config *config) { - config.CertFile = certFile - config.KeyFile = keyFile - } -} - -// WithVerifier sets the Verifier of the config. -func WithVerifier(verifier Verifier) ConfigOpt { - return func(config *config) { - config.Verifier = verifier - } -} diff --git a/httpgateway/event.go b/httpgateway/event.go deleted file mode 100644 index 10780278e..000000000 --- a/httpgateway/event.go +++ /dev/null @@ -1,56 +0,0 @@ -package httpgateway - -import ( - "bytes" - "context" - "encoding/json" - "io" - "log/slog" - "net/http" - "sync" - "time" -) - -func HandleEvent(verifier Verifier, publicKey PublicKey, logger *slog.Logger, handleFunc EventHandlerFunc) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - defer func() { - _ = r.Body.Close() - }() - - if ok := VerifyRequest(verifier, r, publicKey); !ok { - http.Error(w, "Unauthorized", http.StatusUnauthorized) - data, _ := io.ReadAll(r.Body) - logger.Debug("received http interaction with invalid signature", slog.String("body", string(data))) - return - } - - buff := new(bytes.Buffer) - rqData, _ := io.ReadAll(io.TeeReader(r.Body, buff)) - logger.Debug("received http event", slog.String("body", string(rqData))) - - var v Message - if err := json.NewDecoder(buff).Decode(&v); err != nil { - logger.Error("error while decoding event", slog.Any("err", err)) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - - ackChan := make(chan struct{}, 1) - defer close(ackChan) - - go handleFunc(sync.OnceFunc(func() { - ackChan <- struct{}{} - }), v) - - ctx, cancel := context.WithTimeout(context.Background(), 3100*time.Millisecond) - defer cancel() - - select { - case <-ackChan: - w.WriteHeader(http.StatusNoContent) - case <-ctx.Done(): - logger.Debug("event timed out") - http.Error(w, "Event Timed Out", http.StatusRequestTimeout) - } - } -} diff --git a/httpgateway/http_gateway.go b/httpgateway/http_gateway.go deleted file mode 100644 index 57ef91f9b..000000000 --- a/httpgateway/http_gateway.go +++ /dev/null @@ -1,80 +0,0 @@ -package httpgateway - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "log/slog" - "net/http" - - "github.com/disgoorg/disgo/discord" -) - -type ( - EventHandlerFunc func(ack func(), message Message) - - // InteractionHandlerFunc is used to handle events from Discord's Outgoing Webhooks - InteractionHandlerFunc func(responseFunc RespondFunc, event EventInteractionCreate) - - // RespondFunc is used to respond to Discord's Outgoing Webhooks - RespondFunc func(response discord.InteractionResponse) error -) - -type Gateway interface { - Start() - - Close(ctx context.Context) -} - -var _ Gateway = (*gatewayImpl)(nil) - -// New creates a new Server with the given publicKey eventHandlerFunc and ConfigOpt(s) -func New(publicKey string, interactionHandler InteractionHandlerFunc, eventHandler EventHandlerFunc, opts ...ConfigOpt) (Gateway, error) { - cfg := defaultConfig() - cfg.apply(opts) - - hexDecodedKey, err := hex.DecodeString(publicKey) - if err != nil { - return nil, fmt.Errorf("error decoding public key: %w", err) - } - - return &gatewayImpl{ - config: cfg, - publicKey: hexDecodedKey, - interactionHandler: interactionHandler, - eventHandler: eventHandler, - verifier: cfg.Verifier, - }, nil -} - -type gatewayImpl struct { - config config - publicKey PublicKey - interactionHandler InteractionHandlerFunc - eventHandler EventHandlerFunc - verifier Verifier -} - -func (s *gatewayImpl) Start() { - s.config.ServeMux.Handle(s.config.InteractionURL, HandleInteraction(s.verifier, s.publicKey, s.config.Logger, s.interactionHandler)) - s.config.ServeMux.Handle(s.config.EventURL, HandleEvent(s.verifier, s.publicKey, s.config.Logger, s.eventHandler)) - s.config.HTTPServer.Addr = s.config.Address - s.config.HTTPServer.Handler = s.config.ServeMux - - go func() { - var err error - if s.config.CertFile != "" && s.config.KeyFile != "" { - err = s.config.HTTPServer.ListenAndServeTLS(s.config.CertFile, s.config.KeyFile) - } else { - err = s.config.HTTPServer.ListenAndServe() - } - if !errors.Is(err, http.ErrServerClosed) { - s.config.Logger.Error("error while running http server", slog.Any("err", err)) - } - }() -} - -func (s *gatewayImpl) Close(ctx context.Context) { - _ = s.config.HTTPServer.Shutdown(ctx) -} diff --git a/httpinteraction/config.go b/httpinteraction/config.go new file mode 100644 index 000000000..35c4eb212 --- /dev/null +++ b/httpinteraction/config.go @@ -0,0 +1,55 @@ +package httpinteraction + +import ( + "log/slog" +) + +func defaultConfig() config { + return config{ + Logger: slog.Default(), + Endpoint: "/interactions/callback", + Verifier: DefaultVerifier{}, + } +} + +type config struct { + Logger *slog.Logger + Endpoint string + Verifier KeyVerifier + EnableRawEvents bool +} + +// ConfigOpt is a type alias for a function that takes a config and is used to configure your Server. +type ConfigOpt func(config *config) + +func (c *config) apply(opts []ConfigOpt) { + for _, opt := range opts { + opt(c) + } + c.Logger = c.Logger.With(slog.String("name", "httpserver")) +} + +func WithDefault() ConfigOpt { + return func(config *config) {} +} + +// WithLogger sets the Logger of the config. +func WithLogger(logger *slog.Logger) ConfigOpt { + return func(config *config) { + config.Logger = logger + } +} + +// WithEndpoint sets the endpoint discord will send interactions to. +func WithEndpoint(endpoint string) ConfigOpt { + return func(config *config) { + config.Endpoint = endpoint + } +} + +// WithVerifier sets the KeyVerifier of the config. +func WithVerifier(verifier KeyVerifier) ConfigOpt { + return func(config *config) { + config.Verifier = verifier + } +} diff --git a/httpgateway/interaction.go b/httpinteraction/handler.go similarity index 77% rename from httpgateway/interaction.go rename to httpinteraction/handler.go index 8ca45235e..c2827b24e 100644 --- a/httpgateway/interaction.go +++ b/httpinteraction/handler.go @@ -1,8 +1,10 @@ -package httpgateway +package httpinteraction import ( "bytes" "context" + "encoding/hex" + "fmt" "io" "log/slog" "net/http" @@ -14,22 +16,31 @@ import ( "github.com/disgoorg/disgo/discord" ) -// EventInteractionCreate is the event payload when an interaction is created via Discord's Outgoing Webhooks -type EventInteractionCreate struct { - discord.Interaction +type ( + // InteractionHandlerFunc is used to handle events from Discord's Outgoing Webhooks + InteractionHandlerFunc func(respond RespondFunc, event EventInteractionCreate) + + // RespondFunc is used to respond to Discord's Outgoing Webhooks + RespondFunc func(response discord.InteractionResponse) error +) + +type HTTPHandler interface { + Handle(pattern string, handler http.Handler) } -func (e *EventInteractionCreate) UnmarshalJSON(data []byte) error { - interaction, err := discord.UnmarshalInteraction(data) +// New creates a new Server with the given publicKey eventHandlerFunc and ConfigOpt(s) +func New(handler HTTPHandler, publicKey string, interactionHandler InteractionHandlerFunc, opts ...ConfigOpt) error { + cfg := defaultConfig() + cfg.apply(opts) + + hexDecodedKey, err := hex.DecodeString(publicKey) if err != nil { - return err + return fmt.Errorf("error decoding public key: %w", err) } - e.Interaction = interaction - return nil -} -func (e EventInteractionCreate) MarshalJSON() ([]byte, error) { - return json.Marshal(e.Interaction) + handler.Handle(cfg.Endpoint, HandleInteraction(cfg.Verifier, hexDecodedKey, cfg.Logger, interactionHandler)) + + return nil } type replyStatus int @@ -41,7 +52,7 @@ const ( ) // HandleInteraction handles an interaction from Discord's Outgoing Webhooks. It verifies and parses the interaction and then calls the passed EventHandlerFunc. -func HandleInteraction(verifier Verifier, publicKey PublicKey, logger *slog.Logger, handleFunc InteractionHandlerFunc) http.HandlerFunc { +func HandleInteraction(verifier KeyVerifier, publicKey PublicKey, logger *slog.Logger, handleFunc InteractionHandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { defer func() { _ = r.Body.Close() diff --git a/httpinteraction/interaction.go b/httpinteraction/interaction.go new file mode 100644 index 000000000..b5162568a --- /dev/null +++ b/httpinteraction/interaction.go @@ -0,0 +1,25 @@ +package httpinteraction + +import ( + "github.com/disgoorg/json/v2" + + "github.com/disgoorg/disgo/discord" +) + +// EventInteractionCreate is the event payload when an interaction is created via Discord's Outgoing Webhooks +type EventInteractionCreate struct { + discord.Interaction +} + +func (e *EventInteractionCreate) UnmarshalJSON(data []byte) error { + interaction, err := discord.UnmarshalInteraction(data) + if err != nil { + return err + } + e.Interaction = interaction + return nil +} + +func (e EventInteractionCreate) MarshalJSON() ([]byte, error) { + return json.Marshal(e.Interaction) +} diff --git a/httpgateway/verify.go b/httpinteraction/verify.go similarity index 86% rename from httpgateway/verify.go rename to httpinteraction/verify.go index dcc7c196f..03e2f49ca 100644 --- a/httpgateway/verify.go +++ b/httpinteraction/verify.go @@ -1,4 +1,4 @@ -package httpgateway +package httpinteraction import ( "bytes" @@ -11,8 +11,8 @@ import ( // PublicKey is the type of Ed25519 public keys. type PublicKey = []byte -// Verifier is used to verify Ed25519 signatures. -type Verifier interface { +// KeyVerifier is used to verify Ed25519 signatures. +type KeyVerifier interface { // Verify verifies the signature of the message using the public key. // It returns true if the signature is valid, false otherwise. Verify(publicKey PublicKey, message []byte, sig []byte) bool @@ -21,7 +21,7 @@ type Verifier interface { SignatureSize() int } -// DefaultVerifier is the default implementation of the Verifier interface. +// DefaultVerifier is the default implementation of the KeyVerifier interface. type DefaultVerifier struct{} func (DefaultVerifier) Verify(publicKey PublicKey, message []byte, sig []byte) bool { @@ -34,7 +34,7 @@ func (DefaultVerifier) SignatureSize() int { // VerifyRequest implements the verification side of the discord interactions api signing algorithm, as documented here: https://discord.com/developers/docs/interactions/slash-commands#security-and-authorization // Credit: https://github.com/bsdlp/discord-interactions-go/blob/main/interactions/verify.go -func VerifyRequest(verifier Verifier, r *http.Request, key PublicKey) bool { +func VerifyRequest(verifier KeyVerifier, r *http.Request, key PublicKey) bool { var msg bytes.Buffer signature := r.Header.Get("X-Signature-Ed25519") diff --git a/webhook/webhook_client.go b/webhook/webhook_client.go index 30ae1c0c3..f17d044ed 100644 --- a/webhook/webhook_client.go +++ b/webhook/webhook_client.go @@ -14,7 +14,7 @@ import ( "github.com/disgoorg/disgo/rest" ) -var ErrInvalidWebhookURL = errors.New("invalid webhook InteractionURL") +var ErrInvalidWebhookURL = errors.New("invalid webhook URL") // New creates a new Client with the given ID, Token and ConfigOpt(s). func New(id snowflake.ID, token string, opts ...ConfigOpt) *Client { @@ -34,7 +34,7 @@ func New(id snowflake.ID, token string, opts ...ConfigOpt) *Client { func NewWithURL(webhookURL string, opts ...ConfigOpt) (*Client, error) { u, err := url.Parse(webhookURL) if err != nil { - return nil, fmt.Errorf("invalid webhook InteractionURL: %w", err) + return nil, fmt.Errorf("invalid webhook URL: %w", err) } parts := strings.FieldsFunc(u.Path, func(r rune) bool { return r == '/' }) diff --git a/webhook/webook_test.go b/webhook/webook_test.go index c9d693f0c..2c69a353d 100644 --- a/webhook/webook_test.go +++ b/webhook/webook_test.go @@ -44,11 +44,11 @@ func TestParseURL(t *testing.T) { t.Run(tc.URL, func(t *testing.T) { c, err := NewWithURL(tc.URL) if tc.Err { - assert.Error(t, err, "InteractionURL parsing should have resulted in an error") + assert.Error(t, err, "URL parsing should have resulted in an error") return } - assert.Equal(t, tc.ID, c.ID, "InteractionURL ID should match") - assert.Equal(t, tc.Token, c.Token, "InteractionURL Token should match") + assert.Equal(t, tc.ID, c.ID, "URL ID should match") + assert.Equal(t, tc.Token, c.Token, "URL Token should match") }) } } diff --git a/webhookevent/config.go b/webhookevent/config.go new file mode 100644 index 000000000..489afe09e --- /dev/null +++ b/webhookevent/config.go @@ -0,0 +1,57 @@ +package webhookevent + +import ( + "log/slog" + + "github.com/disgoorg/disgo/httpinteraction" +) + +func defaultConfig() config { + return config{ + Logger: slog.Default(), + Endpoint: "/events/callback", + Verifier: httpinteraction.DefaultVerifier{}, + } +} + +type config struct { + Logger *slog.Logger + Endpoint string + Verifier httpinteraction.KeyVerifier + EnableRawEvents bool +} + +// ConfigOpt is a type alias for a function that takes a config and is used to configure your Server. +type ConfigOpt func(config *config) + +func (c *config) apply(opts []ConfigOpt) { + for _, opt := range opts { + opt(c) + } + c.Logger = c.Logger.With(slog.String("name", "httpserver")) +} + +func WithDefault() ConfigOpt { + return func(config *config) {} +} + +// WithLogger sets the Logger of the config. +func WithLogger(logger *slog.Logger) ConfigOpt { + return func(config *config) { + config.Logger = logger + } +} + +// WithEndpoint sets the endpoint discord will send webhook events to. +func WithEndpoint(endpoint string) ConfigOpt { + return func(config *config) { + config.Endpoint = endpoint + } +} + +// WithVerifier sets the KeyVerifier of the config. +func WithVerifier(verifier httpinteraction.KeyVerifier) ConfigOpt { + return func(config *config) { + config.Verifier = verifier + } +} diff --git a/webhookevent/handler.go b/webhookevent/handler.go new file mode 100644 index 000000000..922c9661e --- /dev/null +++ b/webhookevent/handler.go @@ -0,0 +1,104 @@ +package webhookevent + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "io" + "log/slog" + "net/http" + "sync" + "time" + + "github.com/disgoorg/json/v2" + + "github.com/disgoorg/disgo/httpinteraction" +) + +type EventHandlerFunc func(ack func(), message Message) + +type HTTPHandler interface { + Handle(pattern string, handler http.Handler) +} + +// New creates a new Server with the given publicKey eventHandlerFunc and ConfigOpt(s) +func New(handler HTTPHandler, publicKey string, eventHandler EventHandlerFunc, opts ...ConfigOpt) error { + cfg := defaultConfig() + cfg.apply(opts) + + hexDecodedKey, err := hex.DecodeString(publicKey) + if err != nil { + return fmt.Errorf("error decoding public key: %w", err) + } + + handler.Handle(cfg.Endpoint, HandleEvent(cfg.Verifier, hexDecodedKey, cfg.Logger, cfg.EnableRawEvents, eventHandler)) + + return nil +} + +func HandleEvent(verifier httpinteraction.KeyVerifier, publicKey httpinteraction.PublicKey, logger *slog.Logger, raw bool, handle EventHandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + defer func() { + _ = r.Body.Close() + }() + + if ok := httpinteraction.VerifyRequest(verifier, r, publicKey); !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + data, _ := io.ReadAll(r.Body) + logger.Debug("received http interaction with invalid signature", slog.String("body", string(data))) + return + } + + buff := new(bytes.Buffer) + rqData, _ := io.ReadAll(io.TeeReader(r.Body, buff)) + logger.Debug("received http event", slog.String("body", string(rqData))) + + var message Message + if err := json.NewDecoder(buff).Decode(&message); err != nil { + logger.Error("error while decoding event", slog.Any("err", err)) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + ackChan := make(chan struct{}, 1) + defer close(ackChan) + + ack := sync.OnceFunc(func() { + ackChan <- struct{}{} + }) + + if raw && message.Type == MessageTypeEvent { + event := message.Event.(Event) + + go handle(ack, Message{ + Version: message.Version, + ApplicationID: message.ApplicationID, + Type: message.Type, + Event: Event{ + Type: event.Type, + Timestamp: event.Timestamp, + Data: EventDataRaw{ + EventType: event.Type, + Payload: bytes.NewReader(event.RawData), + }, + RawData: event.RawData, + }, + RawEvent: message.RawEvent, + }) + } + + go handle(ack, message) + + ctx, cancel := context.WithTimeout(context.Background(), 3100*time.Millisecond) + defer cancel() + + select { + case <-ackChan: + w.WriteHeader(http.StatusNoContent) + case <-ctx.Done(): + logger.Debug("event timed out") + http.Error(w, "event Timed Out", http.StatusRequestTimeout) + } + } +} diff --git a/httpgateway/http_gateway_messages.go b/webhookevent/messages.go similarity index 96% rename from httpgateway/http_gateway_messages.go rename to webhookevent/messages.go index 020ca06b5..2c32701c5 100644 --- a/httpgateway/http_gateway_messages.go +++ b/webhookevent/messages.go @@ -1,7 +1,8 @@ -package httpgateway +package webhookevent import ( "fmt" + "io" "time" "github.com/disgoorg/json/v2" @@ -175,3 +176,10 @@ func (EventDataQuestUserEnrollment) eventData() {} type EventDataUnknown json.RawMessage func (EventDataUnknown) eventData() {} + +type EventDataRaw struct { + EventType EventType + Payload io.Reader +} + +func (EventDataRaw) eventData() {} From f817eac214e54339734613f2d33281395d79ede7 Mon Sep 17 00:00:00 2001 From: topi314 Date: Fri, 15 Aug 2025 21:58:01 +0200 Subject: [PATCH 3/4] add new labels --- .github/labeler.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index 988ea2779..4674e7f6b 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -15,6 +15,12 @@ "t:gateway": - changed-files: - any-glob-to-any-file: [ 'gateway/*' ] +"t:http interaction": + - changed-files: + - any-glob-to-any-file: [ 'httpinteraction/*' ] +"t:webhook event": + - changed-files: + - any-glob-to-any-file: [ 'webhookevent/*' ] "t:handler": - changed-files: - any-glob-to-any-file: [ 'handler/*' ] From 62b3477f76a3cd609d2cd62514e30a949e12e319 Mon Sep 17 00:00:00 2001 From: topi314 Date: Fri, 15 Aug 2025 22:02:46 +0200 Subject: [PATCH 4/4] refactor: update JSON unmarshalling for event data handling --- webhookevent/messages.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/webhookevent/messages.go b/webhookevent/messages.go index 2c32701c5..72f16797c 100644 --- a/webhookevent/messages.go +++ b/webhookevent/messages.go @@ -47,11 +47,11 @@ func (m *Message) UnmarshalJSON(data []byte) error { messageEvent = EventPing{} case MessageTypeEvent: var d Event - err = json.Unmarshal(data, &v.Event) + err = json.Unmarshal(v.Event, &d) messageEvent = d default: var d EventUnknown - err = json.Unmarshal(data, &v.Event) + err = json.Unmarshal(v.Event, &d) messageEvent = d } if err != nil { @@ -113,23 +113,23 @@ func (m *Event) UnmarshalJSON(data []byte) error { switch v.Type { case EventTypeApplicationAuthorized: var d EventDataApplicationAuthorized - err = json.Unmarshal(data, &v.Data) + err = json.Unmarshal(v.Data, &d) eventData = d case EventTypeApplicationDeauthorized: var d EventDataApplicationDeauthorized - err = json.Unmarshal(data, &v.Data) + err = json.Unmarshal(v.Data, &d) eventData = d case EventTypeEntitlementCreate: var d EventDataEntitlementCreate - err = json.Unmarshal(data, &v.Data) + err = json.Unmarshal(v.Data, &d) eventData = d case EventTypeQuestUserEnrollment: var d EventDataQuestUserEnrollment - err = json.Unmarshal(data, &v.Data) + err = json.Unmarshal(v.Data, &d) eventData = d default: var d EventDataUnknown - err = json.Unmarshal(data, &v.Data) + err = json.Unmarshal(v.Data, &d) eventData = d } if err != nil {