adding Context

- converting messages and methods into one context per event
This commit is contained in:
2026-02-24 18:45:00 -05:00
parent 5297a480b8
commit 01dd3633ef
5 changed files with 134 additions and 128 deletions

View File

@@ -73,7 +73,7 @@ import (
func main() { func main() {
b, err := bolt.New(bolt.WithLogLevel(bolt.LogLevelAll), b, err := bolt.New(bolt.WithLogLevel(bolt.LogLevelAll),
bolt.WithMaxGoroutines(50), bolt.WithMaxGoroutines(150),
bolt.WithPermissions(bolt.MessagePermissions, bolt.AdminPermissions)) bolt.WithPermissions(bolt.MessagePermissions, bolt.AdminPermissions))
if err != nil { if err != nil {

19
bolt.go
View File

@@ -33,8 +33,7 @@ type bolt struct {
pool chan struct{} pool chan struct{}
maxRoutines int maxRoutines int
msgHandlerf Payload msgHandlerf Payload
admin bool // admin bool
tools AdminToolBox
} }
type Bolt interface { type Bolt interface {
@@ -69,7 +68,7 @@ func New(opts ...Option) (Bolt, error) {
logLvl: LogLevelAll, logLvl: LogLevelAll,
indicator: DEFAULT_INDICATOR, indicator: DEFAULT_INDICATOR,
wg: sync.WaitGroup{}, wg: sync.WaitGroup{},
admin: false, // admin: false,
maxRoutines: DEFAULT_MAX_GOROUTINES, maxRoutines: DEFAULT_MAX_GOROUTINES,
} }
@@ -80,7 +79,7 @@ func New(opts ...Option) (Bolt, error) {
//options can change these fields so we must create post-opts //options can change these fields so we must create post-opts
b.pool = make(chan struct{}, b.maxRoutines) b.pool = make(chan struct{}, b.maxRoutines)
b.tools = NewToolbox(b) // b.tools = NewToolbox(b)
return b, nil return b, nil
} }
@@ -173,7 +172,7 @@ func (b *bolt) msgEventHandler(s *dg.Session, msg *dg.MessageCreate) {
ChannelID: channel.ID, ChannelID: channel.ID,
Server: server.Name, Server: server.Name,
ServerID: server.ID, ServerID: server.ID,
sesh: b, // sesh: b,
} }
w := strings.Fields(msg.Content) w := strings.Fields(msg.Content)
@@ -240,7 +239,10 @@ func (b *bolt) msgEventHandler(s *dg.Session, msg *dg.MessageCreate) {
func (b *bolt) handleMessage(event *Message) error { func (b *bolt) handleMessage(event *Message) error {
if b.msgHandlerf != nil { if b.msgHandlerf != nil {
return b.msgHandlerf(event, b.tools) return b.msgHandlerf(&Context{
Message: event,
bolt: b,
})
} }
return nil return nil
@@ -277,7 +279,10 @@ func (b *bolt) handleCommand(msg *Message, lg int) error {
} }
} }
err = run.Payload(msg, b.tools) err = run.Payload(&Context{
Message: msg,
bolt: b,
})
if err != nil { if err != nil {
return fmt.Errorf("encountered an error while handling command (%s): %e", msg.Words[0], err) return fmt.Errorf("encountered an error while handling command (%s): %e", msg.Words[0], err)
} }

View File

@@ -13,37 +13,34 @@ type Command struct {
Roles []string //roles that can use command, if none are set anyone can run the command Roles []string //roles that can use command, if none are set anyone can run the command
} }
// command payload functions, any strings returned are sent as a response to the command // type adminToolbox struct {
type Payload func(msg *Message, admin AdminToolBox) error // *bolt
// }
// type AdminToolBox interface {
// Timeout(userId, serverId string, duration time.Time) error
// ClearTimeout(userId, serverId string) error
// Mute(userId, serverId string) error
// Unmute(userId, serverId string) error
// }
type adminToolbox struct { // func NewToolbox(b *bolt) AdminToolBox {
*bolt // return &adminToolbox{
} // bolt: b,
type AdminToolBox interface { // }
Timeout(userId, serverId string, duration time.Time) error // }
ClearTimeout(userId, serverId string) error
Mute(userId, serverId string) error
Unmute(userId, serverId string) error
}
func NewToolbox(b *bolt) AdminToolBox { // func (a *adminToolbox) Timeout(userId, serverId string, duration time.Time) error {
return &adminToolbox{ // return a.GuildMemberTimeout(serverId, userId, &duration)
bolt: b, // }
}
}
func (a *adminToolbox) Timeout(userId, serverId string, duration time.Time) error { // func (a *adminToolbox) ClearTimeout(userId, serverId string) error {
return a.GuildMemberTimeout(serverId, userId, &duration) // return a.GuildMemberTimeout(serverId, userId, nil)
} // }
func (a *adminToolbox) ClearTimeout(userId, serverId string) error { // func (a *adminToolbox) Mute(userId, serverId string) error {
return a.GuildMemberTimeout(serverId, userId, nil) // return a.GuildMemberMute(serverId, userId, true)
} // }
func (a *adminToolbox) Mute(userId, serverId string) error { // func (a *adminToolbox) Unmute(userId, serverId string) error {
return a.GuildMemberMute(serverId, userId, true) // return a.GuildMemberMute(serverId, userId, false)
} // }
func (a *adminToolbox) Unmute(userId, serverId string) error {
return a.GuildMemberMute(serverId, userId, false)
}

View File

@@ -2,6 +2,7 @@ package bolt
import ( import (
"fmt" "fmt"
"time"
dg "github.com/bwmarrin/discordgo" dg "github.com/bwmarrin/discordgo"
) )
@@ -11,89 +12,8 @@ const (
MSG_MAX_LENGTH = 2000 MSG_MAX_LENGTH = 2000
) )
// Message contains basic information about the messages received and provides a few methods // command payload functions, any strings returned are sent as a response to the command
// for handling replies, bans, timeouts, reaction, and deletion. All Discord utilities will use type Payload func(c *Context) error
// a timeout to prevent hanging for too long, this timeout can be customized with the WithTimeout
// option.
type Message struct {
Author Author
ID string //message ID
Words []string //message data split on whitespaces
Content string //entire message data string
Channel string //name of channel message was sent in
ChannelID string //ID of channel message was sent in
Server string //name of guild message was sent in
ServerID string //ID of guild message was sent in
Attachments []MessageAttachment //any attachments bound to the message
Mentions []*dg.User
sesh *bolt
}
type Author struct {
Name string
ID string
Roles []string
}
// React applies reaction to the message
func (m *Message) React(emoji Reaction) error {
return m.sesh.MessageReactionAdd(m.ChannelID, m.ID, fmt.Sprint(emoji))
}
// Respond sends a response to the message, handling chunking if the message exceeds max length
func (m *Message) Respond(res string) error {
if len(res) > MSG_MAX_LENGTH {
for len(res) > 0 {
//send full chunk size allowed by discord
sc := res[:MSG_MAX_LENGTH]
rep := m.sesh.createReply(sc, m.ID, m.ChannelID, m.ServerID)
_, err := m.sesh.ChannelMessageSendComplex(m.ChannelID, rep)
if err != nil {
return err
}
res = res[MSG_MAX_LENGTH:]
//if we have left than a full chunk send the rest and break the loop
if len(res) < MSG_MAX_LENGTH {
final := m.sesh.createReply(res, m.ID, m.ChannelID, m.ServerID)
_, err := m.sesh.ChannelMessageSendComplex(m.ChannelID, final)
if err != nil {
return err
}
break
}
}
return nil
}
//short enough message to send in one go
rep := m.sesh.createReply(res, m.ID, m.ChannelID, m.ServerID)
_, err := m.sesh.ChannelMessageSendComplex(m.ChannelID, rep)
return err
}
// Delete removes the message from the current channel
func (m *Message) Delete() error {
return m.sesh.ChannelMessageDelete(m.ChannelID, m.ID, nil)
}
// func (m *Message) Timeout(userID string, duration time.Time) error {
// return m.sesh.GuildMemberTimeout(m.serverID, userID, &duration)
// }
// func (m *Message) ClearTimeout(userID string) error {
// return m.sesh.GuildMemberTimeout(m.serverID, userID, nil)
// }
// func (m *Message) Mute(userID string) error {
// return m.sesh.GuildMemberMute(m.serverID, userID, true)
// }
// func (m *Author) Unmute(userID string) error {
// return m.sesh.GuildMemberMute(m.serverID, userID, false)
// }
// message attachment details // message attachment details
type MessageAttachment struct { type MessageAttachment struct {
@@ -107,3 +27,87 @@ type MessageAttachment struct {
Size int Size int
DurationSecs float64 DurationSecs float64
} }
type Author struct {
Name string
ID string
Roles []string
}
type Message struct {
Author Author
ID string //message ID
Words []string //message data split on whitespaces
Content string //entire message data string
Channel string //name of channel message was sent in
ChannelID string //ID of channel message was sent in
Server string //name of guild message was sent in
ServerID string //ID of guild message was sent in
Attachments []MessageAttachment //any attachments bound to the message
Mentions []*dg.User
}
type Context struct {
Message *Message
bolt *bolt
}
// React applies reaction to the message
func (c *Context) React(emoji Reaction) error {
return c.bolt.MessageReactionAdd(c.Message.ChannelID, c.Message.ID, fmt.Sprint(emoji))
}
// Respond sends a response to the message, handling chunking if the message exceeds max length
func (c *Context) Respond(res string) error {
if len(res) > MSG_MAX_LENGTH {
for len(res) > 0 {
//send full chunk size allowed by discord
sc := res[:MSG_MAX_LENGTH]
rep := c.bolt.createReply(sc, c.Message.ID, c.Message.ChannelID, c.Message.ServerID)
_, err := c.bolt.ChannelMessageSendComplex(c.Message.ChannelID, rep)
if err != nil {
return err
}
res = res[MSG_MAX_LENGTH:]
//if we have left than a full chunk send the rest and break the loop
if len(res) < MSG_MAX_LENGTH {
final := c.bolt.createReply(res, c.Message.ID, c.Message.ChannelID, c.Message.ServerID)
_, err := c.bolt.ChannelMessageSendComplex(c.Message.ChannelID, final)
if err != nil {
return err
}
break
}
}
return nil
}
//short enough message to send in one go
rep := c.bolt.createReply(res, c.Message.ID, c.Message.ChannelID, c.Message.ServerID)
_, err := c.bolt.ChannelMessageSendComplex(c.Message.ChannelID, rep)
return err
}
// Delete removes the message from the current channel
func (c *Context) Delete() error {
return c.bolt.ChannelMessageDelete(c.Message.ChannelID, c.Message.ID, nil)
}
func (c *Context) Timeout(userId string, duration time.Time) error {
return c.bolt.GuildMemberTimeout(c.Message.ServerID, userId, &duration)
}
func (c *Context) ClearTimeout(userId string) error {
return c.bolt.GuildMemberTimeout(c.Message.ServerID, userId, nil)
}
func (c *Context) Mute(userId string) error {
return c.bolt.GuildMemberMute(c.Message.ServerID, userId, true)
}
func (c *Context) Unmute(userId string) error {
return c.bolt.GuildMemberMute(c.Message.ServerID, userId, false)
}

View File

@@ -34,9 +34,9 @@ func WithPermissions(perms ...Permission) Option {
return func(b *bolt) { return func(b *bolt) {
var fullPerms dg.Intent var fullPerms dg.Intent
for _, p := range perms { for _, p := range perms {
if p == AdminPermissions { // if p == AdminPermissions {
b.admin = true // b.admin = true
} // }
fullPerms |= dg.Intent(p) fullPerms |= dg.Intent(p)
} }