bb/bot/bot.go

240 lines
4.5 KiB
Go
Raw Normal View History

package bot
2022-09-07 02:48:29 +00:00
import (
2022-09-09 15:21:07 +00:00
"errors"
2022-09-07 02:48:29 +00:00
"fmt"
"os"
"os/signal"
"syscall"
"git.kill0.net/chill9/beepboop/command"
2022-09-23 03:53:40 +00:00
"git.kill0.net/chill9/beepboop/config"
"git.kill0.net/chill9/beepboop/handler"
2022-09-07 02:48:29 +00:00
"git.kill0.net/chill9/beepboop/lib"
"github.com/bwmarrin/discordgo"
log "github.com/sirupsen/logrus"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
2022-09-23 03:53:40 +00:00
var C *config.Config
2022-09-07 02:48:29 +00:00
2022-09-07 05:29:28 +00:00
type (
Bot struct {
config *config.Config
session *discordgo.Session
commands map[string]*Command
2022-09-07 02:48:29 +00:00
}
Command struct {
Name string
Func CommandFunc
NArgs int
}
CommandFunc func(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error
2022-09-07 05:29:28 +00:00
MessageHandler func(s *discordgo.Session, m *discordgo.MessageCreate)
)
2022-09-08 06:45:16 +00:00
func init() {
pflag.Bool("debug", false, "enable debug mode")
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
}
func NewBot(config *config.Config, s *discordgo.Session) *Bot {
return &Bot{
session: s,
commands: make(map[string]*Command),
}
}
func (b *Bot) AddHandler(handler interface{}) func() {
return b.session.AddHandler(handler)
}
func (b *Bot) AddCommand(cmd *Command) {
b.commands[cmd.Name] = cmd
}
func (b *Bot) GetCommand(name string) (*Command, bool) {
cmd, ok := b.commands[name]
return cmd, ok
}
func (b *Bot) CommandHandler(s *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.ID == s.State.User.ID {
return
}
if !lib.HasCommand(m.Content, b.config.Prefix) {
return
}
cmdName, arg := lib.SplitCommandAndArg(m.Content, b.config.Prefix)
cmd, ok := b.GetCommand(cmdName)
if !ok {
return
}
args := lib.SplitArgs(arg, cmd.NArgs)
if ok {
log.Debugf("command: %v, args: %v, nargs: %d", cmd.Name, args, len(args))
if err := cmd.Func(args, s, m); err != nil {
log.Errorf("failed to execute command: %s", err)
}
return
}
log.Warnf("unknown command: %v, args: %v, nargs: %d", cmdName, args, len(args))
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("unknown command: %s", cmdName))
}
func (b *Bot) Init(h *handler.Handlers, ch *command.Handlers) {
// Register handlers
b.AddHandler(h.Reaction)
// Register commands
b.AddCommand(&Command{
Name: "coin",
Func: ch.Coin,
})
b.AddCommand(&Command{
Name: "deal",
Func: ch.Deal,
NArgs: 1,
})
b.AddCommand(&Command{
Name: "ping",
Func: ch.Ping,
})
b.AddCommand(&Command{
Name: "roll",
Func: ch.Roll,
NArgs: 1,
})
b.AddCommand(&Command{
Name: "roulette",
Func: ch.Roulette,
})
b.AddCommand(&Command{
2022-09-09 15:23:03 +00:00
Name: "rps",
Func: ch.Rps,
2022-09-09 15:23:03 +00:00
NArgs: 1,
})
b.AddCommand(&Command{
Name: "rpsls",
Func: ch.Rpsls,
NArgs: 1,
})
b.AddCommand(&Command{
Name: "time",
Func: ch.Time,
NArgs: 1,
})
b.AddCommand(&Command{
Name: "version",
Func: ch.Version,
})
b.AddCommand(&Command{
Name: "weather",
Func: ch.Weather,
NArgs: 1,
})
}
2022-09-07 02:48:29 +00:00
func Run() error {
initConfig()
2022-09-08 07:35:39 +00:00
go reloadConfig()
2022-09-07 02:48:29 +00:00
2022-09-07 05:54:58 +00:00
if err := lib.SeedMathRand(); err != nil {
log.Warn(err)
}
2022-09-07 02:48:29 +00:00
if C.DiscordToken == "" {
2022-09-09 15:21:07 +00:00
return errors.New("discord token not set")
2022-09-07 02:48:29 +00:00
}
dg, err := discordgo.New(fmt.Sprintf("Bot %s", C.DiscordToken))
if err != nil {
2022-09-09 15:21:07 +00:00
return fmt.Errorf("error creating discord session: %v", err)
2022-09-07 02:48:29 +00:00
}
b := NewBot(C, dg)
b.Init(handler.NewHandlers(C), command.NewHandlers(C))
2022-09-07 02:48:29 +00:00
dg.Identify.Intents = discordgo.IntentsGuildMessages | discordgo.IntentsDirectMessages
2022-09-08 07:28:58 +00:00
if err = dg.Open(); err != nil {
2022-09-09 15:21:07 +00:00
return fmt.Errorf("error opening connection: %v", err)
2022-09-07 02:48:29 +00:00
}
log.Info("The bot is now running. Press CTRL-C to exit.")
defer dg.Close()
sc := make(chan os.Signal, 1)
2022-09-07 05:54:58 +00:00
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM)
2022-09-07 02:48:29 +00:00
<-sc
log.Info("Shutting down")
return nil
}
func initConfig() {
2022-09-23 03:53:40 +00:00
C = config.NewConfig()
2022-09-07 02:48:29 +00:00
viper.SetEnvPrefix("BEEPBOOP")
viper.AutomaticEnv()
viper.SetConfigName("config")
viper.SetConfigType("toml")
viper.AddConfigPath(".")
viper.SetDefault("debug", false)
2022-09-07 02:48:29 +00:00
viper.BindEnv("DISCORD_TOKEN")
viper.BindEnv("OPEN_WEATHER_MAP_TOKEN")
2022-09-08 07:26:47 +00:00
loadConfig()
}
func loadConfig() {
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
log.Fatalf("fatal error config file: %v", err)
}
2022-09-07 02:48:29 +00:00
}
2022-09-08 07:35:01 +00:00
log.WithField("filename", viper.ConfigFileUsed()).Info(
"loaded configuration file",
)
2022-09-08 07:26:47 +00:00
err := viper.Unmarshal(&C)
2022-09-07 02:48:29 +00:00
if err != nil {
log.Fatalf("unable to decode into struct: %v", err)
}
if viper.GetBool("debug") {
log.SetLevel(log.DebugLevel)
2022-09-08 07:35:39 +00:00
} else {
log.SetLevel(log.InfoLevel)
}
}
func reloadConfig() {
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGHUP)
for {
<-sc
loadConfig()
2022-09-07 02:48:29 +00:00
}
}