1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2025-11-15 00:25:56 +00:00

Bot: Added automatic Intents detection from handlers

This commit adds automatic Intents detection into package bot. When the
Start function is used, the intents will be OR'd after running the
options callback.

This commit also breaks the old "AddIntent" methods to rename them to
"AddIntents" for correctness.
This commit is contained in:
diamondburned 2020-10-28 22:49:18 -07:00
parent ef48d686cd
commit 9899f6073b
6 changed files with 96 additions and 9 deletions

View file

@ -2,8 +2,24 @@ package bot
import ( import (
"reflect" "reflect"
"github.com/diamondburned/arikawa/v2/gateway"
) )
// eventIntents maps event pointer types to intents.
var eventIntents = map[reflect.Type]gateway.Intents{}
func init() {
for event, intent := range gateway.EventIntents {
fn, ok := gateway.EventCreator[event]
if !ok {
continue
}
eventIntents[reflect.TypeOf(fn())] = intent
}
}
type command struct { type command struct {
value reflect.Value // Func value reflect.Value // Func
event reflect.Type event reflect.Type
@ -26,6 +42,15 @@ func (c *command) call(arg0 interface{}, argv ...reflect.Value) (interface{}, er
return callWith(c.value, arg0, argv...) return callWith(c.value, arg0, argv...)
} }
// intents returns the command's intents from the event.
func (c *command) intents() gateway.Intents {
intents, ok := eventIntents[c.event]
if !ok {
return 0
}
return intents
}
func callWith(caller reflect.Value, arg0 interface{}, argv ...reflect.Value) (interface{}, error) { func callWith(caller reflect.Value, arg0 interface{}, argv ...reflect.Value) (interface{}, error) {
var callargs = make([]reflect.Value, 0, 1+len(argv)) var callargs = make([]reflect.Value, 0, 1+len(argv))

View file

@ -165,6 +165,8 @@ func Start(
} }
} }
s.Gateway.AddIntents(c.DeriveIntents())
cancel := c.Start() cancel := c.Start()
if err := s.Open(); err != nil { if err := s.Open(); err != nil {
@ -229,10 +231,10 @@ func New(s *state.State, cmd interface{}) (*Context, error) {
return ctx, nil return ctx, nil
} }
// AddIntent adds the given Gateway Intent into the Gateway. This is a // AddIntents adds the given Gateway Intent into the Gateway. This is a
// convenient function that calls Gateway's AddIntent. // convenient function that calls Gateway's AddIntent.
func (ctx *Context) AddIntent(i gateway.Intents) { func (ctx *Context) AddIntents(i gateway.Intents) {
ctx.Gateway.AddIntent(i) ctx.Gateway.AddIntents(i)
} }
// Subcommands returns the slice of subcommands. To add subcommands, use // Subcommands returns the slice of subcommands. To add subcommands, use
@ -444,3 +446,13 @@ func IndentLines(input string) string {
} }
return strings.Join(lines, "\n") return strings.Join(lines, "\n")
} }
// DeriveIntents derives all possible gateway intents from this context and all
// its subcommands' method handlers and middlewares.
func (ctx *Context) DeriveIntents() gateway.Intents {
var intents = ctx.Subcommand.DeriveIntents()
for _, subcmd := range ctx.subcommands {
intents |= subcmd.DeriveIntents()
}
return intents
}

View file

@ -149,6 +149,21 @@ func TestContext(t *testing.T) {
} }
}) })
t.Run("derive intents", func(t *testing.T) {
intents := ctx.DeriveIntents()
assertIntents := func(target gateway.Intents, name string) {
if !intents.Has(target) {
t.Error("Derived intents do not have", name)
}
}
assertIntents(gateway.IntentGuildMessages, "guild messages")
assertIntents(gateway.IntentDirectMessages, "direct messages")
assertIntents(gateway.IntentGuildMessageTyping, "guild typing")
assertIntents(gateway.IntentDirectMessageTyping, "direct message typing")
})
t.Run("typing event", func(t *testing.T) { t.Run("typing event", func(t *testing.T) {
typing := &gateway.TypingStartEvent{} typing := &gateway.TypingStartEvent{}

View file

@ -143,6 +143,10 @@ func (sub *Subcommand) NeedsName() {
sub.Command = lowerFirstLetter(sub.StructName) sub.Command = lowerFirstLetter(sub.StructName)
} }
func lowerFirstLetter(name string) string {
return strings.ToLower(string(name[0])) + name[1:]
}
// FindCommand finds the MethodContext. It panics if methodName is not found. // FindCommand finds the MethodContext. It panics if methodName is not found.
func (sub *Subcommand) FindCommand(methodName string) *MethodContext { func (sub *Subcommand) FindCommand(methodName string) *MethodContext {
for _, c := range sub.Commands { for _, c := range sub.Commands {
@ -413,6 +417,23 @@ func (sub *Subcommand) AddAliases(commandName string, aliases ...string) {
command.Aliases = append(command.Aliases, aliases...) command.Aliases = append(command.Aliases, aliases...)
} }
func lowerFirstLetter(name string) string { // DeriveIntents derives all possible gateway intents from the method handlers
return strings.ToLower(string(name[0])) + name[1:] // and middlewares.
func (sub *Subcommand) DeriveIntents() gateway.Intents {
var intents gateway.Intents
for _, event := range sub.Events {
intents |= event.intents()
}
for _, command := range sub.Commands {
intents |= command.intents()
}
if sub.plumbed != nil {
intents |= sub.plumbed.intents()
}
for _, middleware := range sub.globalmws {
intents |= middleware.intents()
}
return intents
} }

View file

@ -114,7 +114,7 @@ func NewGatewayWithIntents(token string, intents ...Intents) (*Gateway, error) {
} }
for _, intent := range intents { for _, intent := range intents {
g.AddIntent(intent) g.AddIntents(intent)
} }
return g, nil return g, nil
@ -154,9 +154,9 @@ func NewCustomGateway(gatewayURL, token string) *Gateway {
} }
} }
// AddIntent adds a Gateway Intent before connecting to the Gateway. As // AddIntents adds a Gateway Intent before connecting to the Gateway. As such,
// such, this function will only work before Open() is called. // this function will only work before Open() is called.
func (g *Gateway) AddIntent(i Intents) { func (g *Gateway) AddIntents(i Intents) {
g.Identifier.Intents |= i g.Identifier.Intents |= i
} }

View file

@ -1,5 +1,7 @@
package gateway package gateway
import "github.com/diamondburned/arikawa/v2/discord"
// Intents for the new Discord API feature, documented at // Intents for the new Discord API feature, documented at
// https://discordapp.com/developers/docs/topics/gateway#gateway-intents. // https://discordapp.com/developers/docs/topics/gateway#gateway-intents.
type Intents uint32 type Intents uint32
@ -28,3 +30,15 @@ var PrivilegedIntents = []Intents{
IntentGuildPresences, IntentGuildPresences,
IntentGuildMembers, IntentGuildMembers,
} }
// Has returns true if i has the given intents.
func (i Intents) Has(intents Intents) bool {
return discord.HasFlag(uint64(i), uint64(intents))
}
// IsPrivileged returns true for each of the boolean that indicates the type of
// the privilege.
func (i Intents) IsPrivileged() (presences, member bool) {
// Keep this in sync with PrivilegedIntents.
return i.Has(IntentGuildPresences), i.Has(IntentGuildMembers)
}