router.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. package mux
  2. import (
  3. "errors"
  4. "strings"
  5. "git.mgmcomp.net/thisnthat/discordgo"
  6. )
  7. // RouteOptions holds information about a specific message route handler
  8. type RouteOptions struct {
  9. Name string // match name that should trigger this route handler
  10. Description string // short description of this route
  11. Usage string // How to use the route
  12. Category string // The category to put the route under
  13. Type RouteType // The type of route this is
  14. Callback Handler // route handler function to call
  15. Hidden bool // The route is hidden from the help menu'
  16. Admin bool // The route is only visible to admins
  17. }
  18. type route struct {
  19. Name string
  20. Description string
  21. Usage string
  22. CategoryID int
  23. Type RouteType
  24. Run Handler
  25. Hidden bool
  26. Admin bool
  27. }
  28. type category struct {
  29. Name string
  30. Description string
  31. ID int
  32. }
  33. // RouteType is the type of route being created
  34. type RouteType int
  35. const (
  36. // MessageRoute is a route for handling OnMessageCreate events
  37. MessageRoute RouteType = 1001
  38. )
  39. // Handler is the function signature required for a message route handler.
  40. type Handler func(*discordgo.Session, *discordgo.Message, *Context)
  41. type IsAdminRouteHandler func(*discordgo.Session, *discordgo.Message, *Context) bool
  42. // Context holds a bit of extra data we pass along to route handlers
  43. // This way processing some of this only needs to happen once.
  44. type Context struct {
  45. Fields []string
  46. Args []string
  47. Content string
  48. Method Method
  49. }
  50. // Method is the method a command was received
  51. type Method int
  52. const (
  53. // DirectMethod Discord DM to the bot
  54. DirectMethod Method = 1001
  55. // MentionMethod is a message started with a @mention to the bot
  56. MentionMethod Method = 1002
  57. // PrefixMethod is a message started with a command prefix
  58. PrefixMethod Method = 1003
  59. )
  60. // Router is the main struct for all mux methods.
  61. type Router struct {
  62. routes []*route
  63. helpRoute *route
  64. prefix string
  65. categories []*category
  66. isAdminRoute IsAdminRouteHandler // the admin check handler func to call
  67. }
  68. // New returns a new Discord message route mux
  69. func New() *Router {
  70. r := &Router{}
  71. r.isAdminRoute = isAdminRouteDefault
  72. helpRoute := &route{}
  73. helpRoute.Name = "help"
  74. helpRoute.Usage = "help"
  75. helpRoute.Description = "Get Help"
  76. helpRoute.Run = r.helpCommandHandler
  77. r.helpRoute = helpRoute
  78. return r
  79. }
  80. // SetCommandPrefix will set the prefix used for message commands
  81. func (r *Router) SetCommandPrefix(cmdPrefix string) {
  82. r.prefix = cmdPrefix + " "
  83. }
  84. func (r *Router) SetAdminRouteCallback(callback IsAdminRouteHandler) {
  85. r.isAdminRoute = callback
  86. }
  87. // ErrRegisterRouteNameRequired is returned when registering a new route without a name
  88. var ErrRegisterRouteNameRequired = errors.New("all routes must have a name")
  89. // ErrRegisterInvalidRouteType is returned when registering a new route with an invalid type
  90. var ErrRegisterInvalidRouteType = errors.New("invalid route type")
  91. // ErrRegisterCallbackRequired is returned when registering a new route without a callback
  92. var ErrRegisterCallbackRequired = errors.New("a valid callback must be provided")
  93. // Register allows you to register a route
  94. //func (r *Router) Register(routeType RouteType, name, desc string, callback Handler) (*Route, error) {
  95. func (r *Router) Register(name string, options RouteOptions) error {
  96. if name == "" {
  97. return ErrRegisterRouteNameRequired
  98. }
  99. if !isValidRouteType(options.Type) {
  100. return ErrRegisterInvalidRouteType
  101. }
  102. if options.Callback == nil {
  103. return ErrRegisterCallbackRequired
  104. }
  105. route := route{}
  106. route.Type = options.Type
  107. route.Name = name
  108. route.Description = options.Description
  109. route.Usage = options.Usage
  110. route.Run = options.Callback
  111. route.Hidden = options.Hidden
  112. route.Admin = options.Admin
  113. if options.Category != "" {
  114. for _, category := range r.categories {
  115. if category.Name == options.Category {
  116. route.CategoryID = category.ID
  117. break
  118. }
  119. }
  120. }
  121. r.routes = append(r.routes, &route)
  122. return nil
  123. }
  124. // ErrAddCategoryNameRequired is returned when registering a new route without a name
  125. var ErrAddCategoryNameRequired = errors.New("all categories must have a name")
  126. // AddCategory allows you to a a router category
  127. func (r *Router) AddCategory(name, description string) error {
  128. if name == "" {
  129. return ErrAddCategoryNameRequired
  130. }
  131. category := category{}
  132. category.Name = name
  133. category.Description = description
  134. category.ID = len(r.categories) + 1
  135. r.categories = append(r.categories, &category)
  136. return nil
  137. }
  138. func isValidRouteType(routeType RouteType) bool {
  139. switch routeType {
  140. case MessageRoute:
  141. return true
  142. }
  143. return false
  144. }
  145. func (r *Router) findRoute(searchString string, routeType RouteType) (*route, []string, []string) {
  146. fields := strings.Fields(searchString)
  147. if len(fields) == 0 {
  148. return nil, nil, nil
  149. }
  150. commandKey := fields[0]
  151. for _, route := range r.routes {
  152. if route.Type != routeType {
  153. continue
  154. }
  155. if strings.EqualFold(commandKey, route.Name) {
  156. return route, fields[0:], fields[1:]
  157. }
  158. }
  159. return nil, nil, nil
  160. }
  161. // Default callback for determining if the caller is an admin
  162. func isAdminRouteDefault(s *discordgo.Session, m *discordgo.Message, ctx *Context) bool {
  163. channelPermissions, err := s.UserChannelPermissions(m.Author.ID, m.ChannelID)
  164. if err != nil {
  165. return false
  166. }
  167. return (channelPermissions&discordgo.PermissionAdministrator > 0)
  168. }