starting comment mountain
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
TODO
|
||||
pre-1. Break up msg handler method its insane
|
||||
pre-2. Copy main into README
|
||||
1. Read through code and ensure I didn't miss anything
|
||||
2. do research on intents for 'admin' jobs
|
||||
3. comments and README updates, things have changed
|
||||
|
||||
69
bolt.go
69
bolt.go
@@ -15,25 +15,35 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
//Environment variable name for discord token, this is the only required variable
|
||||
//the name of the environment variable that should contain the token for the bot, it is
|
||||
//required for bolt to run
|
||||
TOKEN_ENV_VAR = "DISCORD_TOKEN"
|
||||
|
||||
//bot defaults
|
||||
DEFAULT_INDICATOR = "."
|
||||
DEFAULT_MAX_GOROUTINES = 50
|
||||
//bot default command indicator, if messages begin with this substring they are processed
|
||||
//through the command handler instead of the generic message handler
|
||||
DEFAULT_INDICATOR = "."
|
||||
//max amount of concurrent goroutines that bolt can use for events. A lower amount
|
||||
//may lower the resource usage of bolt but may cause a delay in event handling
|
||||
DEFAULT_MAX_GOROUTINES = 250
|
||||
)
|
||||
|
||||
// basic bot structure containing discordgo connection as well as the command map
|
||||
type bolt struct {
|
||||
*dg.Session //holds discordgo internals
|
||||
commands map[string]Command //maps trigger phrase to command struct for fast lookup
|
||||
indicator string //the indicator used to detect whether a message is a command
|
||||
logLvl LogLevel //determines how much the bot logs
|
||||
wg sync.WaitGroup
|
||||
//discordgo internals
|
||||
*dg.Session
|
||||
//maps trigger phrase to command struct for instant lookup
|
||||
commands map[string]Command
|
||||
//used to detect whether a message is a command
|
||||
indicator string
|
||||
//verbosity of logs bolt outputs
|
||||
logLvl LogLevel
|
||||
//waitgroup for event routines
|
||||
wg sync.WaitGroup
|
||||
//pool is a buffered channel used as a semaphore for event handler routines, it is limited to
|
||||
//only spawn maxRoutines to handle events
|
||||
pool chan struct{}
|
||||
maxRoutines int
|
||||
//generic message handler func
|
||||
msgHandlerf Payload
|
||||
// admin bool
|
||||
}
|
||||
|
||||
type Bolt interface {
|
||||
@@ -77,13 +87,15 @@ func New(opts ...Option) (Bolt, error) {
|
||||
opt(b)
|
||||
}
|
||||
|
||||
//options can change these fields so we must create post-opts
|
||||
//options can change max routine number, so create after
|
||||
b.pool = make(chan struct{}, b.maxRoutines)
|
||||
// b.tools = NewToolbox(b)
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Start applies the message event handler function to the bot and opens the initial websocket connection
|
||||
// with Discord. Start is a blocking call that also handles safe shutdown, on Interrupt, bolt will give
|
||||
// command routines a window to finish before closing the connection.
|
||||
func (b *bolt) Start() error {
|
||||
b.AddHandler(b.msgEventHandler)
|
||||
err := b.Open()
|
||||
@@ -117,21 +129,27 @@ func (b *bolt) Start() error {
|
||||
return b.stop()
|
||||
}
|
||||
|
||||
func (b *bolt) stop() error {
|
||||
return b.Close()
|
||||
}
|
||||
|
||||
// adds commands to bot command map for use
|
||||
// AddCommands registers command handlers, any messages that begin with the command indicator will be forwarded
|
||||
// to a handler if the command string matches a trigger
|
||||
func (b *bolt) AddCommands(cmd ...Command) {
|
||||
for _, c := range cmd {
|
||||
b.commands[c.Trigger] = c
|
||||
}
|
||||
}
|
||||
|
||||
// AddMessageHandler registers the generic message handler, any messages that are not commands will be forwarded
|
||||
// to the Payload
|
||||
func (b *bolt) AddMessageHandler(p Payload) {
|
||||
b.msgHandlerf = p
|
||||
}
|
||||
|
||||
// stop closes the websocket connection with Discord
|
||||
func (b *bolt) stop() error {
|
||||
return b.Close()
|
||||
}
|
||||
|
||||
// msgEventHandler is a beefy boy that handles message logging, command parsing, and executing payload functions. It needs cleanup then
|
||||
// i'll worry about this comment
|
||||
func (b *bolt) msgEventHandler(s *dg.Session, msg *dg.MessageCreate) {
|
||||
//get server information
|
||||
server, err := s.Guild(msg.GuildID)
|
||||
@@ -148,14 +166,12 @@ func (b *bolt) msgEventHandler(s *dg.Session, msg *dg.MessageCreate) {
|
||||
//the bot will ignore it's own messages to prevent command loops
|
||||
if msg.Author.ID == s.State.User.ID {
|
||||
if b.logLvl != LogLevelErr && b.logLvl != LogLevelNone {
|
||||
//log command responses
|
||||
log.Printf("< %s | %s | %s > %s\n", server.Name, channel.Name, msg.Author.Username, msg.Content)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if b.logLvl == LogLevelAll {
|
||||
//log message
|
||||
log.Printf("< %s | %s | %s > %s\n", server.Name, channel.Name, msg.Author.Username, msg.Content)
|
||||
}
|
||||
|
||||
@@ -172,7 +188,6 @@ func (b *bolt) msgEventHandler(s *dg.Session, msg *dg.MessageCreate) {
|
||||
ChannelID: channel.ID,
|
||||
Server: server.Name,
|
||||
ServerID: server.ID,
|
||||
// sesh: b,
|
||||
}
|
||||
|
||||
w := strings.Fields(msg.Content)
|
||||
@@ -203,21 +218,13 @@ func (b *bolt) msgEventHandler(s *dg.Session, msg *dg.MessageCreate) {
|
||||
m.Attachments = att
|
||||
}
|
||||
|
||||
//using a patter based on a stackoverflow comment I saw that mentioned the use of a buffered channel as a lock (semaphore)
|
||||
//to limit the amount of goroutines used at once
|
||||
|
||||
//could be an issue if the bot is used like a long-term calendar, not sure that is my concern we now have a timeout so it will only wait so long
|
||||
|
||||
lg := len(b.indicator)
|
||||
if msg.Content[:lg] == b.indicator {
|
||||
if b.logLvl == LogLevelCmd {
|
||||
//log commands
|
||||
log.Printf("< %s | %s | %s > %s\n", m.Server, m.Channel, m.Author.Name, m.Content)
|
||||
}
|
||||
|
||||
b.pool <- struct{}{} //'aquire' a routine
|
||||
|
||||
//handled in its own goroutine to allow for async commands
|
||||
b.wg.Go(func() {
|
||||
err := b.handleCommand(&m, lg)
|
||||
if err != nil {
|
||||
@@ -237,6 +244,7 @@ func (b *bolt) msgEventHandler(s *dg.Session, msg *dg.MessageCreate) {
|
||||
}
|
||||
}
|
||||
|
||||
// handleMessage forwards the message data to the handler function, if one was set
|
||||
func (b *bolt) handleMessage(event *Message) error {
|
||||
if b.msgHandlerf != nil {
|
||||
return b.msgHandlerf(&Context{
|
||||
@@ -248,6 +256,9 @@ func (b *bolt) handleMessage(event *Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleCommand maps the first word of the message to the command payload, if it exists. Checking the timeout
|
||||
// and role restrictions before forwarding the message to the Command Payload. If restrictions have not been met
|
||||
// a response is sent to the message
|
||||
func (b *bolt) handleCommand(msg *Message, lg int) error {
|
||||
run, ok := b.commands[msg.Words[0][lg:]]
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user