Skip to content

Commit 604780d

Browse files
handler improvements (#240)
* added not found handler to mux * add HandleNotFound into interface * removed redundant handle prefixes in mux * fix panic * remove NotFound method from Router & export Mux type * add not found handler to handler example * add docs --------- Co-authored-by: TopiSenpai <[email protected]>
1 parent 12bf0cc commit 604780d

File tree

4 files changed

+97
-43
lines changed

4 files changed

+97
-43
lines changed

_examples/handler/example.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/disgoorg/disgo"
1313
"github.com/disgoorg/disgo/bot"
1414
"github.com/disgoorg/disgo/discord"
15+
"github.com/disgoorg/disgo/events"
1516
"github.com/disgoorg/disgo/handler"
1617
"github.com/disgoorg/disgo/handler/middleware"
1718
)
@@ -72,18 +73,19 @@ func main() {
7273
r.Group(func(r handler.Router) {
7374
r.Use(middleware.Print("group1"))
7475
r.Route("/test", func(r handler.Router) {
75-
r.HandleCommand("/sub2", handleContent("/test/sub2"))
76+
r.Command("/sub2", handleContent("/test/sub2"))
7677
r.Route("/{group}", func(r handler.Router) {
77-
r.HandleCommand("/sub", handleVariableContent)
78+
r.Command("/sub", handleVariableContent)
7879
})
7980
})
8081
})
8182
r.Group(func(r handler.Router) {
8283
r.Use(middleware.Print("group2"))
83-
r.HandleCommand("/ping", handlePing)
84-
r.HandleCommand("/ping2", handleContent("pong2"))
85-
r.HandleComponent("button1/{data}", handleComponent)
84+
r.Command("/ping", handlePing)
85+
r.Command("/ping2", handleContent("pong2"))
86+
r.Component("button1/{data}", handleComponent)
8687
})
88+
r.NotFound(handleNotFound)
8789

8890
client, err := disgo.New(token,
8991
bot.WithDefaultGateway(),
@@ -111,17 +113,17 @@ func main() {
111113
}
112114

113115
func handleContent(content string) handler.CommandHandler {
114-
return func(client bot.Client, event *handler.CommandEvent) error {
116+
return func(event *handler.CommandEvent) error {
115117
return event.CreateMessage(discord.MessageCreate{Content: content})
116118
}
117119
}
118120

119-
func handleVariableContent(client bot.Client, event *handler.CommandEvent) error {
121+
func handleVariableContent(event *handler.CommandEvent) error {
120122
group := event.Variables["group"]
121123
return event.CreateMessage(discord.MessageCreate{Content: "group: " + group})
122124
}
123125

124-
func handlePing(client bot.Client, event *handler.CommandEvent) error {
126+
func handlePing(event *handler.CommandEvent) error {
125127
return event.CreateMessage(discord.MessageCreate{
126128
Content: "pong",
127129
Components: []discord.ContainerComponent{
@@ -132,7 +134,11 @@ func handlePing(client bot.Client, event *handler.CommandEvent) error {
132134
})
133135
}
134136

135-
func handleComponent(client bot.Client, event *handler.ComponentEvent) error {
137+
func handleComponent(event *handler.ComponentEvent) error {
136138
data := event.Variables["data"]
137139
return event.CreateMessage(discord.MessageCreate{Content: "component: " + data})
138140
}
141+
142+
func handleNotFound(event *events.InteractionCreate) error {
143+
return event.Respond(discord.InteractionResponseTypeCreateMessage, discord.MessageCreate{Content: "not found"})
144+
}

handler/handler.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
// Package handler provides a way to handle interactions like application commands, autocomplete, buttons, select menus & modals with a simple interface.
2+
//
3+
// The handler package is inspired by the go-chi/chi http router.
4+
// Each interaction has a path which is either the command name (starting with /) or the custom id. According to this path all interactions are routed to the correct handler.
5+
// Slash Commands can have subcommands, which are nested paths. For example /test/subcommand1 or /test/subcommandgroup/subcommand.
6+
//
7+
// The handler also supports variables in its path which is especially useful for subcommands, components and modals.
8+
// Variables are defined by curly braces like {variable} and can be accessed in the handler via the Variables map.
9+
//
10+
// You can also register middlewares, which are executed before the handler is called. Middlewares can be used to check permissions, validate input or do other things.
11+
// Middlewares can also be attached to sub-routers, which is useful if you want to have a middleware for all subcommands of a command as an example.
12+
// A middleware does not care which interaction type it is, it is just executed before the handler and has the following signature:
13+
// type Middleware func(next func(e *events.InteractionCreate)) func(e *events.InteractionCreate)
14+
//
15+
// The handler iterates over all routes until it finds the fist matching route. If no route matches, the handler will call the NotFoundHandler.
16+
// The NotFoundHandler can be set via the `NotFound` method on the *Mux. If no NotFoundHandler is set nothing will happen.
17+
118
package handler
219

320
import (
@@ -25,7 +42,7 @@ func (h *handlerHolder[T]) Match(path string, t discord.InteractionType) bool {
2542
if strings.HasPrefix(part, "{") && strings.HasSuffix(part, "}") {
2643
continue
2744
}
28-
if part != parts[i] {
45+
if len(parts) <= i || part != parts[i] {
2946
return false
3047
}
3148
}

handler/mux.go

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,29 @@ import (
88
"github.com/disgoorg/disgo/events"
99
)
1010

11-
func New() Router {
12-
return &mux{}
11+
// New returns a new Router.
12+
func New() *Mux {
13+
return &Mux{}
1314
}
1415

15-
func newRouter(pattern string, middlewares []Middleware, routes []Route) *mux {
16-
return &mux{
16+
func newRouter(pattern string, middlewares []Middleware, routes []Route) *Mux {
17+
return &Mux{
1718
pattern: pattern,
1819
middlewares: middlewares,
1920
routes: routes,
2021
}
2122
}
2223

23-
type mux struct {
24-
pattern string
25-
middlewares []Middleware
26-
routes []Route
24+
// Mux is a basic Router implementation.
25+
type Mux struct {
26+
pattern string
27+
middlewares []Middleware
28+
routes []Route
29+
notFoundHandler NotFoundHandler
2730
}
2831

29-
func (r *mux) OnEvent(event bot.Event) {
32+
// OnEvent is called when a new event is received.
33+
func (r *Mux) OnEvent(event bot.Event) {
3034
e, ok := event.(*events.InteractionCreate)
3135
if !ok {
3236
return
@@ -53,7 +57,8 @@ func (r *mux) OnEvent(event bot.Event) {
5357
}
5458
}
5559

56-
func (r *mux) Match(path string, t discord.InteractionType) bool {
60+
// Match returns true if the given path matches the Route.
61+
func (r *Mux) Match(path string, t discord.InteractionType) bool {
5762
if r.pattern != "" {
5863
parts := splitPath(path)
5964
patternParts := splitPath(r.pattern)
@@ -63,7 +68,7 @@ func (r *mux) Match(path string, t discord.InteractionType) bool {
6368
if strings.HasPrefix(part, "{") && strings.HasSuffix(part, "}") {
6469
continue
6570
}
66-
if part != parts[i] {
71+
if len(parts) <= i || part != parts[i] {
6772
return false
6873
}
6974
}
@@ -77,7 +82,8 @@ func (r *mux) Match(path string, t discord.InteractionType) bool {
7782
return false
7883
}
7984

80-
func (r *mux) Handle(path string, variables map[string]string, e *events.InteractionCreate) error {
85+
// Handle handles the given interaction event.
86+
func (r *Mux) Handle(path string, variables map[string]string, e *events.InteractionCreate) error {
8187
path = parseVariables(path, r.pattern, variables)
8288
middlewares := func(event *events.InteractionCreate) {}
8389
for i := len(r.middlewares) - 1; i >= 0; i-- {
@@ -90,44 +96,53 @@ func (r *mux) Handle(path string, variables map[string]string, e *events.Interac
9096
return route.Handle(path, variables, e)
9197
}
9298
}
99+
if r.notFoundHandler != nil {
100+
return r.notFoundHandler(e)
101+
}
93102
return nil
94103
}
95104

96-
func (r *mux) Use(middlewares ...Middleware) {
105+
// Use adds the given middlewares to the current Router.
106+
func (r *Mux) Use(middlewares ...Middleware) {
97107
r.middlewares = append(r.middlewares, middlewares...)
98108
}
99109

100-
func (r *mux) With(middlewares ...Middleware) Router {
110+
// With returns a new Router with the given middlewares.
111+
func (r *Mux) With(middlewares ...Middleware) Router {
101112
return newRouter("", middlewares, nil)
102113
}
103114

104-
func (r *mux) Group(fn func(router Router)) {
115+
// Group creates a new Router and adds it to the current Router.
116+
func (r *Mux) Group(fn func(router Router)) {
105117
router := New()
106118
fn(router)
107119
r.handle(router)
108120
}
109121

110-
func (r *mux) Route(pattern string, fn func(r Router)) Router {
122+
// Route creates a new sub-router with the given pattern and adds it to the current Router.
123+
func (r *Mux) Route(pattern string, fn func(r Router)) Router {
111124
checkPattern(pattern)
112125
router := newRouter(pattern, nil, nil)
113126
fn(router)
114127
r.handle(router)
115128
return router
116129
}
117130

118-
func (r *mux) Mount(pattern string, router Router) {
131+
// Mount mounts the given router with the given pattern to the current Router.
132+
func (r *Mux) Mount(pattern string, router Router) {
119133
if pattern == "" {
120134
r.handle(router)
121135
return
122136
}
123137
r.handle(newRouter(pattern, nil, []Route{router}))
124138
}
125139

126-
func (r *mux) handle(route Route) {
140+
func (r *Mux) handle(route Route) {
127141
r.routes = append(r.routes, route)
128142
}
129143

130-
func (r *mux) HandleCommand(pattern string, h CommandHandler) {
144+
// Command registers the given CommandHandler to the current Router.
145+
func (r *Mux) Command(pattern string, h CommandHandler) {
131146
checkPattern(pattern)
132147
r.handle(&handlerHolder[CommandHandler]{
133148
pattern: pattern,
@@ -136,7 +151,8 @@ func (r *mux) HandleCommand(pattern string, h CommandHandler) {
136151
})
137152
}
138153

139-
func (r *mux) HandleAutocomplete(pattern string, h AutocompleteHandler) {
154+
// Autocomplete registers the given AutocompleteHandler to the current Router.
155+
func (r *Mux) Autocomplete(pattern string, h AutocompleteHandler) {
140156
checkPattern(pattern)
141157
r.handle(&handlerHolder[AutocompleteHandler]{
142158
pattern: pattern,
@@ -145,7 +161,8 @@ func (r *mux) HandleAutocomplete(pattern string, h AutocompleteHandler) {
145161
})
146162
}
147163

148-
func (r *mux) HandleComponent(pattern string, h ComponentHandler) {
164+
// Component registers the given ComponentHandler to the current Router.
165+
func (r *Mux) Component(pattern string, h ComponentHandler) {
149166
checkPatternEmpty(pattern)
150167
r.handle(&handlerHolder[ComponentHandler]{
151168
pattern: pattern,
@@ -154,7 +171,8 @@ func (r *mux) HandleComponent(pattern string, h ComponentHandler) {
154171
})
155172
}
156173

157-
func (r *mux) HandleModal(pattern string, h ModalHandler) {
174+
// Modal registers the given ModalHandler to the current Router.
175+
func (r *Mux) Modal(pattern string, h ModalHandler) {
158176
checkPatternEmpty(pattern)
159177
r.handle(&handlerHolder[ModalHandler]{
160178
pattern: pattern,
@@ -163,6 +181,12 @@ func (r *mux) HandleModal(pattern string, h ModalHandler) {
163181
})
164182
}
165183

184+
// NotFound sets the NotFoundHandler for this router.
185+
// This handler only works for the root router and will be ignored for sub routers.
186+
func (r *Mux) NotFound(h NotFoundHandler) {
187+
r.notFoundHandler = h
188+
}
189+
166190
func checkPatternEmpty(pattern string) {
167191
if pattern == "" {
168192
panic("pattern must not be empty")

handler/router.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,18 @@ type (
1111
AutocompleteHandler func(e *AutocompleteEvent) error
1212
ComponentHandler func(e *ComponentEvent) error
1313
ModalHandler func(e *ModalEvent) error
14+
NotFoundHandler func(event *events.InteractionCreate) error
1415
)
1516

1617
var (
17-
_ Route = (*mux)(nil)
18+
_ Route = (*Mux)(nil)
1819
_ Route = (*handlerHolder[CommandHandler])(nil)
20+
_ Route = (*handlerHolder[AutocompleteHandler])(nil)
21+
_ Route = (*handlerHolder[ComponentHandler])(nil)
22+
_ Route = (*handlerHolder[ModalHandler])(nil)
1923
)
2024

25+
// Route is a basic interface for a route in a Router.
2126
type Route interface {
2227
// Match returns true if the given path matches the Route.
2328
Match(path string, t discord.InteractionType) bool
@@ -26,14 +31,16 @@ type Route interface {
2631
Handle(path string, variables map[string]string, e *events.InteractionCreate) error
2732
}
2833

34+
// Router provides with the core routing functionality.
35+
// It is used to register handlers and middlewares and sub-routers.
2936
type Router interface {
3037
bot.EventListener
3138
Route
3239

33-
// Use adds the given middlewares to the current Router
40+
// Use adds the given middlewares to the current Router.
3441
Use(middlewares ...Middleware)
3542

36-
// With returns a new Router with the given middlewares
43+
// With returns a new Router with the given middlewares.
3744
With(middlewares ...Middleware) Router
3845

3946
// Group creates a new Router and adds it to the current Router.
@@ -45,15 +52,15 @@ type Router interface {
4552
// Mount mounts the given router with the given pattern to the current Router.
4653
Mount(pattern string, r Router)
4754

48-
// HandleCommand registers the given CommandHandler to the current Router.
49-
HandleCommand(pattern string, h CommandHandler)
55+
// Command registers the given CommandHandler to the current Router.
56+
Command(pattern string, h CommandHandler)
5057

51-
// HandleAutocomplete registers the given AutocompleteHandler to the current Router.
52-
HandleAutocomplete(pattern string, h AutocompleteHandler)
58+
// Autocomplete registers the given AutocompleteHandler to the current Router.
59+
Autocomplete(pattern string, h AutocompleteHandler)
5360

54-
// HandleComponent registers the given ComponentHandler to the current Router.
55-
HandleComponent(pattern string, h ComponentHandler)
61+
// Component registers the given ComponentHandler to the current Router.
62+
Component(pattern string, h ComponentHandler)
5663

57-
// HandleModal registers the given ModalHandler to the current Router.
58-
HandleModal(pattern string, h ModalHandler)
64+
// Modal registers the given ModalHandler to the current Router.
65+
Modal(pattern string, h ModalHandler)
5966
}

0 commit comments

Comments
 (0)