Compare commits
	
		
			39 Commits
		
	
	
		
			4a024e98f2
			...
			develop
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a0fc17fd89 | |||
| 7a0c90a5f7 | |||
| b8b994157e | |||
| 6139db889d | |||
| 12372522e9 | |||
| a3137e5276 | |||
| 7b3368eea4 | |||
| 5a141be534 | |||
| 49000133d8 | |||
| 756aaf2379 | |||
| 6bd4744745 | |||
| 211f963b87 | |||
| 04aef2f0e4 | |||
| 9221a218b9 | |||
| a551a10e59 | |||
| d8a28fb211 | |||
| 2ac0df3494 | |||
| a151b08142 | |||
| 7ff6e74148 | |||
| b419cfde69 | |||
| 534b3e5fcd | |||
| 446ac616bf | |||
| e2032942ca | |||
| 31cf6f6c9a | |||
| 5651df37ef | |||
| 8606ed0200 | |||
| 3efa3fb5a2 | |||
| 139b32094e | |||
| 4068a4ff06 | |||
| cb9d8e194f | |||
| f3145d8c1d | |||
| 4c5849daae | |||
| c59c95c47a | |||
| 0345b1cba1 | |||
| a1d612abc0 | |||
| 0f25b75fe4 | |||
| d3b95c693b | |||
| 043738668b | |||
| 579921a975 | 
							
								
								
									
										4
									
								
								.golangci.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.golangci.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | --- | ||||||
|  | linters: | ||||||
|  |   disable: | ||||||
|  |     - errcheck | ||||||
| @@ -6,4 +6,6 @@ RUN CGO_ENABLED=0 go build ./cmd/bb | |||||||
| FROM scratch AS bin | FROM scratch AS bin | ||||||
| COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ | ||||||
| COPY --from=build /src/bb / | COPY --from=build /src/bb / | ||||||
|  | ADD https://raw.githubusercontent.com/golang/go/master/lib/time/zoneinfo.zip /zoneinfo.zip | ||||||
|  | ENV ZONEINFO /zoneinfo.zip | ||||||
| ENTRYPOINT ["/bb"] | ENTRYPOINT ["/bb"] | ||||||
|   | |||||||
							
								
								
									
										239
									
								
								bot/bot.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								bot/bot.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,239 @@ | |||||||
|  | package bot | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"os/signal" | ||||||
|  | 	"syscall" | ||||||
|  |  | ||||||
|  | 	"git.kill0.net/chill9/beepboop/command" | ||||||
|  | 	"git.kill0.net/chill9/beepboop/config" | ||||||
|  | 	"git.kill0.net/chill9/beepboop/handler" | ||||||
|  | 	"git.kill0.net/chill9/beepboop/lib" | ||||||
|  | 	"github.com/bwmarrin/discordgo" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
|  | 	"github.com/spf13/pflag" | ||||||
|  | 	"github.com/spf13/viper" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var C *config.Config | ||||||
|  |  | ||||||
|  | type ( | ||||||
|  | 	Bot struct { | ||||||
|  | 		config   *config.Config | ||||||
|  | 		session  *discordgo.Session | ||||||
|  | 		commands map[string]*Command | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	Command struct { | ||||||
|  | 		Name  string | ||||||
|  | 		Func  CommandFunc | ||||||
|  | 		NArgs int | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	CommandFunc func(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error | ||||||
|  |  | ||||||
|  | 	MessageHandler func(s *discordgo.Session, m *discordgo.MessageCreate) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | 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{ | ||||||
|  | 		Name:  "rps", | ||||||
|  | 		Func:  ch.Rps, | ||||||
|  | 		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, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Run() error { | ||||||
|  | 	initConfig() | ||||||
|  | 	go reloadConfig() | ||||||
|  |  | ||||||
|  | 	if err := lib.SeedMathRand(); err != nil { | ||||||
|  | 		log.Warn(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if C.DiscordToken == "" { | ||||||
|  | 		return errors.New("discord token not set") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dg, err := discordgo.New(fmt.Sprintf("Bot %s", C.DiscordToken)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("error creating discord session: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b := NewBot(C, dg) | ||||||
|  | 	b.Init(handler.NewHandlers(C), command.NewHandlers(C)) | ||||||
|  |  | ||||||
|  | 	dg.Identify.Intents = discordgo.IntentsGuildMessages | discordgo.IntentsDirectMessages | ||||||
|  |  | ||||||
|  | 	if err = dg.Open(); err != nil { | ||||||
|  | 		return fmt.Errorf("error opening connection: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Info("The bot is now running. Press CTRL-C to exit.") | ||||||
|  |  | ||||||
|  | 	defer dg.Close() | ||||||
|  |  | ||||||
|  | 	sc := make(chan os.Signal, 1) | ||||||
|  | 	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM) | ||||||
|  | 	<-sc | ||||||
|  |  | ||||||
|  | 	log.Info("Shutting down") | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func initConfig() { | ||||||
|  | 	C = config.NewConfig() | ||||||
|  |  | ||||||
|  | 	viper.SetEnvPrefix("BEEPBOOP") | ||||||
|  | 	viper.AutomaticEnv() | ||||||
|  |  | ||||||
|  | 	viper.SetConfigName("config") | ||||||
|  | 	viper.SetConfigType("toml") | ||||||
|  | 	viper.AddConfigPath(".") | ||||||
|  |  | ||||||
|  | 	viper.SetDefault("debug", false) | ||||||
|  |  | ||||||
|  | 	viper.BindEnv("DISCORD_TOKEN") | ||||||
|  | 	viper.BindEnv("OPEN_WEATHER_MAP_TOKEN") | ||||||
|  |  | ||||||
|  | 	loadConfig() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func loadConfig() { | ||||||
|  | 	if err := viper.ReadInConfig(); err != nil { | ||||||
|  | 		if _, ok := err.(viper.ConfigFileNotFoundError); !ok { | ||||||
|  | 			log.Fatalf("fatal error config file: %v", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.WithField("filename", viper.ConfigFileUsed()).Info( | ||||||
|  | 		"loaded configuration file", | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	err := viper.Unmarshal(&C) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("unable to decode into struct: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if viper.GetBool("debug") { | ||||||
|  | 		log.SetLevel(log.DebugLevel) | ||||||
|  | 	} else { | ||||||
|  | 		log.SetLevel(log.InfoLevel) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func reloadConfig() { | ||||||
|  | 	sc := make(chan os.Signal, 1) | ||||||
|  | 	signal.Notify(sc, syscall.SIGHUP) | ||||||
|  | 	for { | ||||||
|  | 		<-sc | ||||||
|  |  | ||||||
|  | 		loadConfig() | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,42 +0,0 @@ | |||||||
| package bot |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	defaultPrefix = "!" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	defaultReactions []string = []string{"👍", "🌶️", "🤣", "😂", "🍆", "🍑", "❤️", "💦", "😍", "💩", "🔥", "🍒", "🎉", "🥳", "🎊"} |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type ( |  | ||||||
| 	Config struct { |  | ||||||
| 		Debug               bool          `mapstructure:"debug"` |  | ||||||
| 		Handler             HandlerConfig `mapstructure:"handler"` |  | ||||||
| 		Prefix              string        `mapstructure:"prefix"` |  | ||||||
| 		DiscordToken        string        `mapstructure:"discord_token"` |  | ||||||
| 		OpenWeatherMapToken string        `mapstructure:"open_weather_map_token"` |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	HandlerConfig struct { |  | ||||||
| 		Reaction ReactionConfig `mapstructure:"reaction"` |  | ||||||
| 		Weather  WeatherConfig  `mapstructure:"weather"` |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ReactionConfig struct { |  | ||||||
| 		Emojis   []string |  | ||||||
| 		Channels []string |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	WeatherConfig struct { |  | ||||||
| 		Token string `mapstructure:"token"` |  | ||||||
| 	} |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func NewConfig() Config { |  | ||||||
| 	var c Config |  | ||||||
|  |  | ||||||
| 	c.Prefix = defaultPrefix |  | ||||||
| 	c.Handler.Reaction.Emojis = defaultReactions |  | ||||||
|  |  | ||||||
| 	return c |  | ||||||
| } |  | ||||||
| @@ -1,52 +0,0 @@ | |||||||
| package handler |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"git.kill0.net/chill9/beepboop/bot" |  | ||||||
| 	"git.kill0.net/chill9/beepboop/lib" |  | ||||||
| 	"github.com/bwmarrin/discordgo" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type ( |  | ||||||
| 	Coin bool |  | ||||||
|  |  | ||||||
| 	CoinHandler struct { |  | ||||||
| 		config bot.Config |  | ||||||
| 		Name   string |  | ||||||
| 	} |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func (c *Coin) Flip() bool { |  | ||||||
| 	*c = Coin(lib.Itob(lib.RandInt(0, 1))) |  | ||||||
| 	return bool(*c) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewCoinHandler(s string) *CoinHandler { |  | ||||||
| 	return &CoinHandler{Name: s} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *CoinHandler) SetConfig(config bot.Config) { |  | ||||||
| 	h.config = config |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *CoinHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { |  | ||||||
| 	var ( |  | ||||||
| 		c   Coin |  | ||||||
| 		msg string |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	if m.Author.ID == s.State.User.ID { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if !lib.HasCommand(m.Content, h.config.Prefix, h.Name) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if c.Flip() { |  | ||||||
| 		msg = "heads" |  | ||||||
| 	} else { |  | ||||||
| 		msg = "tails" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	s.ChannelMessageSend(m.ChannelID, msg) |  | ||||||
| } |  | ||||||
| @@ -1,37 +0,0 @@ | |||||||
| package handler |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"git.kill0.net/chill9/beepboop/bot" |  | ||||||
| 	"git.kill0.net/chill9/beepboop/lib" |  | ||||||
| 	"github.com/bwmarrin/discordgo" |  | ||||||
| 	log "github.com/sirupsen/logrus" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type ( |  | ||||||
| 	PingHandler struct { |  | ||||||
| 		config bot.Config |  | ||||||
| 		Name   string |  | ||||||
| 	} |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func NewPingHandler(s string) *PingHandler { |  | ||||||
| 	return &PingHandler{Name: s} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *PingHandler) SetConfig(config bot.Config) { |  | ||||||
| 	h.config = config |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *PingHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { |  | ||||||
| 	if m.Author.ID == s.State.User.ID { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if !lib.HasCommand(m.Content, h.config.Prefix, h.Name) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	log.Debug("received ping") |  | ||||||
|  |  | ||||||
| 	s.ChannelMessageSend(m.ChannelID, "pong") |  | ||||||
| } |  | ||||||
| @@ -1,65 +0,0 @@ | |||||||
| package handler |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"git.kill0.net/chill9/beepboop/bot" |  | ||||||
| 	"github.com/bwmarrin/discordgo" |  | ||||||
| 	log "github.com/sirupsen/logrus" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type ( |  | ||||||
| 	TimeHandler struct { |  | ||||||
| 		config bot.Config |  | ||||||
| 		Name   string |  | ||||||
| 	} |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func NewTimeHandler(s string) *TimeHandler { |  | ||||||
| 	return &TimeHandler{Name: s} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *TimeHandler) SetConfig(config bot.Config) { |  | ||||||
| 	h.config = config |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *TimeHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { |  | ||||||
| 	var ( |  | ||||||
| 		t  time.Time |  | ||||||
| 		tz string |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	if m.Author.ID == s.State.User.ID { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if !strings.HasPrefix(m.Content, "!time") { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	x := strings.SplitN(m.Content, " ", 2) |  | ||||||
|  |  | ||||||
| 	if len(x) > 2 { |  | ||||||
| 		s.ChannelMessageSend(m.ChannelID, "help: `!time TIMEZONE`") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	now := time.Now() |  | ||||||
|  |  | ||||||
| 	if len(x) == 2 { |  | ||||||
| 		tz = x[1] |  | ||||||
| 		loc, err := time.LoadLocation(tz) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Warnf("failed to load location: %s", err) |  | ||||||
| 			s.ChannelMessageSend(m.ChannelID, err.Error()) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		t = now.In(loc) |  | ||||||
| 	} else { |  | ||||||
| 		t = now |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	s.ChannelMessageSend(m.ChannelID, fmt.Sprint(t)) |  | ||||||
| } |  | ||||||
| @@ -1,47 +0,0 @@ | |||||||
| package handler |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"runtime" |  | ||||||
|  |  | ||||||
| 	"git.kill0.net/chill9/beepboop/bot" |  | ||||||
| 	"git.kill0.net/chill9/beepboop/lib" |  | ||||||
| 	"github.com/bwmarrin/discordgo" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	SourceURI = "https://git.kill0.net/chill9/bb" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type ( |  | ||||||
| 	VersionHandler struct { |  | ||||||
| 		config bot.Config |  | ||||||
| 		Name   string |  | ||||||
| 	} |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func NewVersionHandler(s string) *VersionHandler { |  | ||||||
| 	return &VersionHandler{Name: s} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *VersionHandler) SetConfig(config bot.Config) { |  | ||||||
| 	h.config = config |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *VersionHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { |  | ||||||
| 	if m.Author.ID == s.State.User.ID { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if !lib.HasCommand(m.Content, h.config.Prefix, h.Name) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	s.ChannelMessageSend(m.ChannelID, fmt.Sprintf( |  | ||||||
| 		"go version: %s\nplatform: %s\nos: %s\nsource: %s\n", |  | ||||||
| 		runtime.Version(), |  | ||||||
| 		runtime.GOARCH, |  | ||||||
| 		runtime.GOOS, |  | ||||||
| 		SourceURI, |  | ||||||
| 	)) |  | ||||||
| } |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| package bot |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/bwmarrin/discordgo" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type MessageCreateHandler interface { |  | ||||||
| 	Handle(*discordgo.Session, *discordgo.MessageCreate) |  | ||||||
| 	SetConfig(Config) |  | ||||||
| } |  | ||||||
							
								
								
									
										102
									
								
								cmd/bb/main.go
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								cmd/bb/main.go
									
									
									
									
									
								
							| @@ -1,111 +1,13 @@ | |||||||
| package main | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"flag" |  | ||||||
| 	"fmt" |  | ||||||
|  |  | ||||||
| 	//"log" |  | ||||||
|  |  | ||||||
| 	"os" |  | ||||||
| 	"os/signal" |  | ||||||
| 	"syscall" |  | ||||||
|  |  | ||||||
| 	"git.kill0.net/chill9/beepboop/bot" | 	"git.kill0.net/chill9/beepboop/bot" | ||||||
| 	"git.kill0.net/chill9/beepboop/bot/handler" |  | ||||||
| 	"git.kill0.net/chill9/beepboop/lib" |  | ||||||
|  |  | ||||||
| 	"github.com/bwmarrin/discordgo" |  | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
| 	"github.com/spf13/pflag" |  | ||||||
| 	"github.com/spf13/viper" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	C bot.Config |  | ||||||
|  |  | ||||||
| 	handlers []bot.MessageCreateHandler = []bot.MessageCreateHandler{ |  | ||||||
| 		handler.NewCoinHandler("coin"), |  | ||||||
| 		handler.NewPingHandler("ping"), |  | ||||||
| 		handler.NewRollHandler("roll"), |  | ||||||
| 		handler.NewRouletteHandler("roulette"), |  | ||||||
| 		handler.NewTimeHandler("time"), |  | ||||||
| 		handler.NewVersionHandler("version"), |  | ||||||
| 		handler.NewWeatherHandler("weather"), |  | ||||||
| 		handler.NewReactionHandler(), |  | ||||||
| 		handler.NewDealHandler("deal"), |  | ||||||
| 	} |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| 	setupConfig() | 	if err := bot.Run(); err != nil { | ||||||
|  | 		log.Fatal(err) | ||||||
| 	lib.SeedMathRand() |  | ||||||
|  |  | ||||||
| 	if C.DiscordToken == "" { |  | ||||||
| 		log.Fatalf("Discord token is not set") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	dg, err := discordgo.New(fmt.Sprintf("Bot %s", C.DiscordToken)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalf("error creating Discord session: %v\n", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, h := range handlers { |  | ||||||
| 		h.SetConfig(C) |  | ||||||
| 		dg.AddHandler(h.Handle) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	dg.Identify.Intents = discordgo.IntentsGuildMessages | discordgo.IntentsDirectMessages |  | ||||||
|  |  | ||||||
| 	err = dg.Open() |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalf("error opening connection: %v\n", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	log.Info("The bot is now running. Press CTRL-C to exit.") |  | ||||||
|  |  | ||||||
| 	defer dg.Close() |  | ||||||
|  |  | ||||||
| 	sc := make(chan os.Signal, 1) |  | ||||||
| 	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) |  | ||||||
| 	<-sc |  | ||||||
|  |  | ||||||
| 	log.Info("Shutting down") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func setupConfig() { |  | ||||||
| 	var err error |  | ||||||
|  |  | ||||||
| 	C = bot.NewConfig() |  | ||||||
|  |  | ||||||
| 	flag.Bool("debug", false, "enable debug logging") |  | ||||||
| 	pflag.CommandLine.AddGoFlagSet(flag.CommandLine) |  | ||||||
| 	pflag.Parse() |  | ||||||
| 	viper.BindPFlags(pflag.CommandLine) |  | ||||||
|  |  | ||||||
| 	viper.SetEnvPrefix("BEEPBOOP") |  | ||||||
| 	viper.AutomaticEnv() |  | ||||||
|  |  | ||||||
| 	viper.SetConfigName("config") |  | ||||||
| 	viper.SetConfigType("toml") |  | ||||||
| 	viper.AddConfigPath(".") |  | ||||||
|  |  | ||||||
| 	err = viper.ReadInConfig() |  | ||||||
|  |  | ||||||
| 	viper.BindEnv("DEBUG") |  | ||||||
| 	viper.BindEnv("DISCORD_TOKEN") |  | ||||||
| 	viper.BindEnv("OPEN_WEATHER_MAP_TOKEN") |  | ||||||
|  |  | ||||||
| 	if _, ok := err.(viper.ConfigFileNotFoundError); !ok { |  | ||||||
| 		log.Fatalf("fatal error config file: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = viper.Unmarshal(&C) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalf("unable to decode into struct: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if viper.GetBool("debug") { |  | ||||||
| 		log.SetLevel(log.DebugLevel) |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								command/coin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								command/coin.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | package command | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"git.kill0.net/chill9/beepboop/lib" | ||||||
|  | 	"github.com/bwmarrin/discordgo" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Coin bool | ||||||
|  |  | ||||||
|  | func (c *Coin) Flip() bool { | ||||||
|  | 	*c = Coin(lib.Itob(lib.RandInt(0, 1))) | ||||||
|  | 	return bool(*c) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (h *Handlers) Coin(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error { | ||||||
|  | 	var ( | ||||||
|  | 		c   Coin | ||||||
|  | 		msg string | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	if c.Flip() { | ||||||
|  | 		msg = "heads" | ||||||
|  | 	} else { | ||||||
|  | 		msg = "tails" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.ChannelMessageSend(m.ChannelID, msg) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								command/commands.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								command/commands.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | package command | ||||||
|  |  | ||||||
|  | import "git.kill0.net/chill9/beepboop/config" | ||||||
|  |  | ||||||
|  | type Handlers struct { | ||||||
|  | 	config *config.Config | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewHandlers(config *config.Config) *Handlers { | ||||||
|  | 	return &Handlers{config: config} | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package handler | package command | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| @@ -7,18 +7,11 @@ import ( | |||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"git.kill0.net/chill9/beepboop/bot" |  | ||||||
| 	"git.kill0.net/chill9/beepboop/lib" |  | ||||||
| 	"github.com/bwmarrin/discordgo" | 	"github.com/bwmarrin/discordgo" | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type ( | type ( | ||||||
| 	DealHandler struct { |  | ||||||
| 		config bot.Config |  | ||||||
| 		Name   string |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	Card string | 	Card string | ||||||
| 
 | 
 | ||||||
| 	Deck [52]Card | 	Deck [52]Card | ||||||
| @@ -52,36 +45,16 @@ func (d *Deck) Deal(n int) ([]Card, error) { | |||||||
| 	return hand, err | 	return hand, err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewDealHandler(s string) *DealHandler { | func (h *Handlers) Deal(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error { | ||||||
| 	h := new(DealHandler) |  | ||||||
| 	h.Name = s |  | ||||||
| 	return h |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (h *DealHandler) SetConfig(config bot.Config) { |  | ||||||
| 	h.config = config |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (h *DealHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { |  | ||||||
| 	if m.Author.ID == s.State.User.ID { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if !lib.HasCommand(m.Content, h.config.Prefix, h.Name) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	rand.Shuffle(len(deck), func(i, j int) { | 	rand.Shuffle(len(deck), func(i, j int) { | ||||||
| 		deck[i], deck[j] = deck[j], deck[i] | 		deck[i], deck[j] = deck[j], deck[i] | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	log.Debugf("%+v", deck) | 	log.Debugf("%+v", deck) | ||||||
| 
 | 
 | ||||||
| 	_, args := lib.SplitCommandAndArgs(m.Content, h.config.Prefix) |  | ||||||
| 
 |  | ||||||
| 	if len(args) != 1 { | 	if len(args) != 1 { | ||||||
| 		s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("help: `!%s <n>`", h.Name)) | 		s.ChannelMessageSend(m.ChannelID, "help: `!deal <n>`") | ||||||
| 		return | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	n, err := strconv.Atoi(args[0]) | 	n, err := strconv.Atoi(args[0]) | ||||||
| @@ -92,16 +65,15 @@ func (h *DealHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { | |||||||
| 	hand, err := deck.Deal(n) | 	hand, err := deck.Deal(n) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("error: %s\n", err)) | 		s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("error: %s\n", err)) | ||||||
| 		return | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	s.ChannelMessageSend(m.ChannelID, JoinCards(hand, " ")) | 	s.ChannelMessageSend(m.ChannelID, JoinCards(hand, " ")) | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func JoinCards(h []Card, sep string) string { | func JoinCards(h []Card, sep string) string { | ||||||
| 	var b []string | 	b := make([]string, len(h)) | ||||||
| 
 |  | ||||||
| 	b = make([]string, len(h)) |  | ||||||
| 
 | 
 | ||||||
| 	for i, v := range h { | 	for i, v := range h { | ||||||
| 		b[i] = string(v) | 		b[i] = string(v) | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package handler | package command | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| @@ -7,10 +7,9 @@ import ( | |||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"git.kill0.net/chill9/beepboop/bot" |  | ||||||
| 	"git.kill0.net/chill9/beepboop/lib" | 	"git.kill0.net/chill9/beepboop/lib" | ||||||
| 	"github.com/bwmarrin/discordgo" |  | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/bwmarrin/discordgo" | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @@ -25,11 +24,6 @@ type ( | |||||||
| 		Rolls     []int | 		Rolls     []int | ||||||
| 		S         string | 		S         string | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	RollHandler struct { |  | ||||||
| 		config bot.Config |  | ||||||
| 		Name   string |  | ||||||
| 	} |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func NewRoll(n, d int) *Roll { | func NewRoll(n, d int) *Roll { | ||||||
| @@ -84,42 +78,19 @@ func (r *Roll) RollDice() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewRollHandler(s string) *RollHandler { | func (h *Handlers) Roll(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error { | ||||||
| 	return &RollHandler{Name: s} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (h *RollHandler) SetConfig(config bot.Config) { |  | ||||||
| 	h.config = config |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (h *RollHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { |  | ||||||
| 	var ( | 	var ( | ||||||
| 		err       error | 		err       error | ||||||
| 		msg, roll string | 		msg, roll string | ||||||
| 		r         *Roll | 		r         *Roll | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	if m.Author.ID == s.State.User.ID { | 	roll = args[0] | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if !lib.HasCommand(m.Content, h.config.Prefix, h.Name) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	x := strings.Split(m.Content, " ") |  | ||||||
| 
 |  | ||||||
| 	if len(x) != 2 { |  | ||||||
| 		s.ChannelMessageSend(m.ChannelID, "help: `!roll <n>d<s>`") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	roll = x[1] |  | ||||||
| 
 | 
 | ||||||
| 	r, err = ParseRoll(roll) | 	r, err = ParseRoll(roll) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		s.ChannelMessageSend(m.ChannelID, err.Error()) | 		s.ChannelMessageSend(m.ChannelID, err.Error()) | ||||||
| 		return | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	r.RollDice() | 	r.RollDice() | ||||||
| @@ -128,4 +99,5 @@ func (h *RollHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { | |||||||
| 	msg = fmt.Sprintf("🎲 %s = %d", lib.JoinInt(r.Rolls, " + "), r.Sum) | 	msg = fmt.Sprintf("🎲 %s = %d", lib.JoinInt(r.Rolls, " + "), r.Sum) | ||||||
| 
 | 
 | ||||||
| 	s.ChannelMessageSend(m.ChannelID, msg) | 	s.ChannelMessageSend(m.ChannelID, msg) | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
							
								
								
									
										10
									
								
								command/ping.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								command/ping.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | package command | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/bwmarrin/discordgo" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (h *Handlers) Ping(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error { | ||||||
|  | 	s.ChannelMessageSend(m.ChannelID, "pong") | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @@ -1,12 +1,9 @@ | |||||||
| package handler | package command | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"git.kill0.net/chill9/beepboop/bot" |  | ||||||
| 	"git.kill0.net/chill9/beepboop/lib" | 	"git.kill0.net/chill9/beepboop/lib" | ||||||
| 	"github.com/bwmarrin/discordgo" |  | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/bwmarrin/discordgo" | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @@ -21,11 +18,6 @@ type ( | |||||||
| 		C [6]bool | 		C [6]bool | ||||||
| 		N int | 		N int | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	RouletteHandler struct { |  | ||||||
| 		config bot.Config |  | ||||||
| 		Name   string |  | ||||||
| 	} |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| @@ -44,7 +36,7 @@ func (g *Gun) Load(n int) { | |||||||
| 	g.N = 0 | 	g.N = 0 | ||||||
| 	for i := 1; i <= n; { | 	for i := 1; i <= n; { | ||||||
| 		x := lib.RandInt(0, len(g.C)-1) | 		x := lib.RandInt(0, len(g.C)-1) | ||||||
| 		if g.C[x] == false { | 		if !g.C[x] { | ||||||
| 			g.C[x] = true | 			g.C[x] = true | ||||||
| 			i++ | 			i++ | ||||||
| 		} else { | 		} else { | ||||||
| @@ -66,7 +58,7 @@ func (g *Gun) Fire() bool { | |||||||
| 
 | 
 | ||||||
| func (g *Gun) IsEmpty() bool { | func (g *Gun) IsEmpty() bool { | ||||||
| 	for _, v := range g.C { | 	for _, v := range g.C { | ||||||
| 		if v == true { | 		if v { | ||||||
| 			return false | 			return false | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -74,23 +66,7 @@ func (g *Gun) IsEmpty() bool { | |||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewRouletteHandler(s string) *RouletteHandler { | func (h *Handlers) Roulette(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error { | ||||||
| 	return &RouletteHandler{Name: s} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (h *RouletteHandler) SetConfig(config bot.Config) { |  | ||||||
| 	h.config = config |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (h *RouletteHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { |  | ||||||
| 	if m.Author.ID == s.State.User.ID { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if !strings.HasPrefix(m.Content, "!roulette") { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if gun.IsEmpty() { | 	if gun.IsEmpty() { | ||||||
| 		gun.Load(Bullets) | 		gun.Load(Bullets) | ||||||
| 		log.Debugf("reloading gun: %+v\n", gun) | 		log.Debugf("reloading gun: %+v\n", gun) | ||||||
| @@ -102,4 +78,5 @@ func (h *RouletteHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreat | |||||||
| 	} else { | 	} else { | ||||||
| 		s.ChannelMessageSend(m.ChannelID, GunClickMessage) | 		s.ChannelMessageSend(m.ChannelID, GunClickMessage) | ||||||
| 	} | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
							
								
								
									
										34
									
								
								command/rps.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								command/rps.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | package command | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"git.kill0.net/chill9/beepboop/lib/rps" | ||||||
|  | 	"github.com/bwmarrin/discordgo" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (h *Handlers) Rps(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error { | ||||||
|  | 	if len(args) != 1 { | ||||||
|  | 		s.ChannelMessageSend( | ||||||
|  | 			m.ChannelID, "help: `!rps (rock | paper | scissors)`", | ||||||
|  | 		) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pc := strings.ToLower(args[0]) // player's choice | ||||||
|  |  | ||||||
|  | 	g := rps.NewGame(rps.RulesRps, rps.EmojiMapRps) | ||||||
|  |  | ||||||
|  | 	bc := g.Rand() // bot's choice | ||||||
|  |  | ||||||
|  | 	out, err := g.Play(bc, pc) | ||||||
|  | 	if _, ok := err.(rps.InvalidChoiceError); ok { | ||||||
|  | 		s.ChannelMessageSend( | ||||||
|  | 			m.ChannelID, "help: `!rps (rock | paper | scissors)`", | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.ChannelMessageSend(m.ChannelID, out) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								command/rpsls.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								command/rpsls.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | package command | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"git.kill0.net/chill9/beepboop/lib/rps" | ||||||
|  | 	"github.com/bwmarrin/discordgo" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (h *Handlers) Rpsls(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error { | ||||||
|  | 	if len(args) != 1 { | ||||||
|  | 		s.ChannelMessageSend( | ||||||
|  | 			m.ChannelID, "help: `!rps (rock | paper | scissors | lizard | spock)`", | ||||||
|  | 		) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pc := strings.ToLower(args[0]) // player's choice | ||||||
|  |  | ||||||
|  | 	g := rps.NewGame(rps.RulesRpsls, rps.EmojiMapRpsls) | ||||||
|  |  | ||||||
|  | 	bc := g.Rand() // bot's choice | ||||||
|  |  | ||||||
|  | 	out, err := g.Play(bc, pc) | ||||||
|  | 	if _, ok := err.(rps.InvalidChoiceError); ok { | ||||||
|  | 		s.ChannelMessageSend( | ||||||
|  | 			m.ChannelID, "help: `!rps (rock | paper | scissors | lizard | spock)`", | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.ChannelMessageSend(m.ChannelID, out) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								command/time.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								command/time.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | package command | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/bwmarrin/discordgo" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (h *Handlers) Time(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error { | ||||||
|  | 	var ( | ||||||
|  | 		t  time.Time | ||||||
|  | 		tz string | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	now := time.Now() | ||||||
|  |  | ||||||
|  | 	if len(args) == 1 { | ||||||
|  | 		tz = args[0] | ||||||
|  | 		loc, err := time.LoadLocation(tz) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Warnf("failed to load location: %s", err) | ||||||
|  | 			s.ChannelMessageSend(m.ChannelID, err.Error()) | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 		t = now.In(loc) | ||||||
|  | 	} else { | ||||||
|  | 		t = now | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.ChannelMessageSend(m.ChannelID, fmt.Sprint(t)) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								command/version.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								command/version.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | package command | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"runtime" | ||||||
|  |  | ||||||
|  | 	"github.com/bwmarrin/discordgo" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	SourceURI = "https://git.kill0.net/chill9/bb" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (h *Handlers) Version(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error { | ||||||
|  | 	s.ChannelMessageSend(m.ChannelID, fmt.Sprintf( | ||||||
|  | 		"go version: %s\nplatform: %s\nos: %s\nsource: %s\n", | ||||||
|  | 		runtime.Version(), | ||||||
|  | 		runtime.GOARCH, | ||||||
|  | 		runtime.GOOS, | ||||||
|  | 		SourceURI, | ||||||
|  | 	)) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @@ -1,66 +1,41 @@ | |||||||
| package handler | package command | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strings" |  | ||||||
| 
 | 
 | ||||||
| 	"git.kill0.net/chill9/beepboop/bot" |  | ||||||
| 	"git.kill0.net/chill9/beepboop/lib/weather" | 	"git.kill0.net/chill9/beepboop/lib/weather" | ||||||
| 	"github.com/bwmarrin/discordgo" |  | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/bwmarrin/discordgo" | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type WeatherHandler struct { | func (h *Handlers) Weather(args []string, s *discordgo.Session, m *discordgo.MessageCreate) error { | ||||||
| 	Config bot.Config |  | ||||||
| 	Name   string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func NewWeatherHandler(s string) *WeatherHandler { |  | ||||||
| 	return &WeatherHandler{Name: s} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (h *WeatherHandler) SetConfig(config bot.Config) { |  | ||||||
| 	h.Config = config |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (h *WeatherHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { |  | ||||||
| 	var ( | 	var ( | ||||||
| 		err error | 		err error | ||||||
| 		loc string | 		loc string | ||||||
| 		w   weather.Weather | 		w   weather.Weather | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	if m.Author.ID == s.State.User.ID { | 	if len(args) != 1 { | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if !strings.HasPrefix(m.Content, "!weather") { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	x := strings.SplitN(m.Content, " ", 2) |  | ||||||
| 
 |  | ||||||
| 	if len(x) != 2 { |  | ||||||
| 		s.ChannelMessageSend(m.ChannelID, "help: `!weather <CITY>,<STATE>,<COUNTRY>`") | 		s.ChannelMessageSend(m.ChannelID, "help: `!weather <CITY>,<STATE>,<COUNTRY>`") | ||||||
| 		return | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	loc = x[1] | 	loc = args[0] | ||||||
| 
 | 
 | ||||||
| 	if h.Config.OpenWeatherMapToken == "" { | 	if h.config.OpenWeatherMapToken == "" { | ||||||
| 		log.Error("OpenWeather token is not set") | 		log.Error("OpenWeather token is not set") | ||||||
| 		return | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	wc := weather.NewClient(h.Config.OpenWeatherMapToken) | 	wc := weather.NewClient(h.config.OpenWeatherMapToken) | ||||||
| 
 | 
 | ||||||
| 	log.Debugf("weather requested for '%s'", loc) | 	log.Debugf("weather requested for '%s'", loc) | ||||||
| 
 | 
 | ||||||
| 	w, err = wc.Get(loc) | 	w, err = wc.Get(loc) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Errorf("weather client error: %v", err) | 		log.Errorf("weather client error: %v", err) | ||||||
| 		return | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	log.Debugf("weather returned for '%s': %+v", loc, w) | 	log.Debugf("weather returned for '%s': %+v", loc, w) | ||||||
| @@ -74,4 +49,6 @@ func (h *WeatherHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate | |||||||
| 		w.Main.Temp.Fahrenheit(), | 		w.Main.Temp.Fahrenheit(), | ||||||
| 		w.Main.Temp.Kelvin(), | 		w.Main.Temp.Kelvin(), | ||||||
| 	)) | 	)) | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
							
								
								
									
										58
									
								
								config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								config/config.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | package config | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	defaultPrefix = "!" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	defaultReactions []string = []string{ | ||||||
|  | 		"👍", "🌶️", "🤣", "😂", "🍆", "🍑", "❤️", "💦", "😍", "💩", | ||||||
|  | 		"🔥", "🍒", "🎉", "🥳", "🎊", "📉", "📈", "💀", "☠️", | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type ( | ||||||
|  | 	Config struct { | ||||||
|  | 		Debug               bool           `mapstructure:"debug"` | ||||||
|  | 		Handler             HandlerConfig  `mapstructure:"handler"` | ||||||
|  | 		Prefix              string         `mapstructure:"prefix"` | ||||||
|  | 		DiscordToken        string         `mapstructure:"discord_token"` | ||||||
|  | 		OpenWeatherMapToken string         `mapstructure:"open_weather_map_token"` | ||||||
|  | 		Mongo               MongoConfig    `mapstructure:"mongo"` | ||||||
|  | 		Redis               RedisConfig    `mapstructure:"redis"` | ||||||
|  | 		Postgres            PostgresConfig `mapstructure:"postgres"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	HandlerConfig struct { | ||||||
|  | 		Reaction ReactionConfig `mapstructure:"reaction"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ReactionConfig struct { | ||||||
|  | 		Emojis   []string | ||||||
|  | 		Channels []string | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	MongoConfig struct { | ||||||
|  | 		Uri      string `mapstructure:"uri"` | ||||||
|  | 		Database string `mapstructure:"database"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	RedisConfig struct { | ||||||
|  | 		Addr     string `mapstructure:"addr"` | ||||||
|  | 		Password string `mapstructure:"password"` | ||||||
|  | 		DB       int    `mapstructure:"database"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	PostgresConfig struct { | ||||||
|  | 		Uri string `mapstructure:"uri"` | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func NewConfig() *Config { | ||||||
|  | 	var c *Config = &Config{} | ||||||
|  |  | ||||||
|  | 	c.Prefix = defaultPrefix | ||||||
|  | 	c.Handler.Reaction.Emojis = defaultReactions | ||||||
|  |  | ||||||
|  | 	return c | ||||||
|  | } | ||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | --- | ||||||
| version: "3.9" | version: "3.9" | ||||||
| services: | services: | ||||||
|   bot: |   bot: | ||||||
| @@ -6,3 +7,5 @@ services: | |||||||
|       - BEEPBOOP_DISCORD_TOKEN |       - BEEPBOOP_DISCORD_TOKEN | ||||||
|       - BEEPBOOP_OPEN_WEATHER_MAP_TOKEN |       - BEEPBOOP_OPEN_WEATHER_MAP_TOKEN | ||||||
|       - BEEPBOOP_DEBUG=true |       - BEEPBOOP_DEBUG=true | ||||||
|  |     volumes: | ||||||
|  |       - ./config.toml:/config.toml | ||||||
							
								
								
									
										20
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,28 +3,28 @@ module git.kill0.net/chill9/beepboop | |||||||
| go 1.18 | go 1.18 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/bwmarrin/discordgo v0.25.0 | 	github.com/bwmarrin/discordgo v0.26.1 | ||||||
| 	github.com/sirupsen/logrus v1.9.0 | 	github.com/sirupsen/logrus v1.9.0 | ||||||
| 	github.com/spf13/pflag v1.0.5 | 	github.com/spf13/pflag v1.0.5 | ||||||
| 	github.com/spf13/viper v1.12.0 | 	github.com/spf13/viper v1.13.0 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/fsnotify/fsnotify v1.5.4 // indirect | 	github.com/fsnotify/fsnotify v1.5.4 // indirect | ||||||
| 	github.com/gorilla/websocket v1.4.2 // indirect | 	github.com/gorilla/websocket v1.5.0 // indirect | ||||||
| 	github.com/hashicorp/hcl v1.0.0 // indirect | 	github.com/hashicorp/hcl v1.0.0 // indirect | ||||||
| 	github.com/magiconair/properties v1.8.6 // indirect | 	github.com/magiconair/properties v1.8.6 // indirect | ||||||
| 	github.com/mitchellh/mapstructure v1.5.0 // indirect | 	github.com/mitchellh/mapstructure v1.5.0 // indirect | ||||||
| 	github.com/pelletier/go-toml v1.9.5 // indirect | 	github.com/pelletier/go-toml v1.9.5 // indirect | ||||||
| 	github.com/pelletier/go-toml/v2 v2.0.1 // indirect | 	github.com/pelletier/go-toml/v2 v2.0.5 // indirect | ||||||
| 	github.com/spf13/afero v1.8.2 // indirect | 	github.com/spf13/afero v1.9.2 // indirect | ||||||
| 	github.com/spf13/cast v1.5.0 // indirect | 	github.com/spf13/cast v1.5.0 // indirect | ||||||
| 	github.com/spf13/jwalterweatherman v1.1.0 // indirect | 	github.com/spf13/jwalterweatherman v1.1.0 // indirect | ||||||
| 	github.com/subosito/gotenv v1.3.0 // indirect | 	github.com/subosito/gotenv v1.4.1 // indirect | ||||||
| 	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect | 	golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect | ||||||
| 	golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect | 	golang.org/x/sys v0.0.0-20220907062415-87db552b00fd // indirect | ||||||
| 	golang.org/x/text v0.3.7 // indirect | 	golang.org/x/text v0.3.7 // indirect | ||||||
| 	gopkg.in/ini.v1 v1.66.4 // indirect | 	gopkg.in/ini.v1 v1.67.0 // indirect | ||||||
| 	gopkg.in/yaml.v2 v2.4.0 // indirect | 	gopkg.in/yaml.v2 v2.4.0 // indirect | ||||||
| 	gopkg.in/yaml.v3 v3.0.0 // indirect | 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								go.sum
									
									
									
									
									
								
							| @@ -38,8 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f | |||||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||||
| github.com/bwmarrin/discordgo v0.25.0 h1:NXhdfHRNxtwso6FPdzW2i3uBvvU7UIQTghmV2T4nqAs= | github.com/bwmarrin/discordgo v0.26.1 h1:AIrM+g3cl+iYBr4yBxCBp9tD9jR3K7upEjl0d89FRkE= | ||||||
| github.com/bwmarrin/discordgo v0.25.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= | github.com/bwmarrin/discordgo v0.26.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= | ||||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||||
| github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | ||||||
| github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | ||||||
| @@ -118,8 +118,9 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ | |||||||
| github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | ||||||
| github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | ||||||
| github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= | ||||||
| github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= |  | ||||||
| github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||||
|  | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= | ||||||
|  | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||||
| github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||||
| github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||||
| github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | ||||||
| @@ -141,8 +142,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua | |||||||
| github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | ||||||
| github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= | ||||||
| github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= | ||||||
| github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= | github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= | ||||||
| github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= | github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= | ||||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||||
| github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= | ||||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||||
| @@ -152,25 +153,27 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR | |||||||
| github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= | github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= | ||||||
| github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= | ||||||
| github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= | ||||||
| github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= | github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= | ||||||
| github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= | github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= | ||||||
| github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= | github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= | ||||||
| github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= | ||||||
| github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= | ||||||
| github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= | ||||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||||||
| github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||||
| github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= | github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= | ||||||
| github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= | github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= | ||||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
|  | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||||
| github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||||
| github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= |  | ||||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||||
| github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= | ||||||
| github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||||
|  | github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= | ||||||
|  | github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= | ||||||
| github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||||
| github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||||
| github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||||
| @@ -188,8 +191,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U | |||||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
| golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= | ||||||
| golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||||
| golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= | golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= | ||||||
| golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||||
| golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | ||||||
| @@ -308,8 +311,9 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w | |||||||
| golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= |  | ||||||
| golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
|  | golang.org/x/sys v0.0.0-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U= | ||||||
|  | golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||||
| golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
| @@ -465,14 +469,14 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 | |||||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= | ||||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
| gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | ||||||
| gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= | ||||||
| gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | ||||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | ||||||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
| gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||||
| gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||||
| honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||||
| honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								handler/handlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								handler/handlers.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | package handler | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"git.kill0.net/chill9/beepboop/config" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Handlers struct { | ||||||
|  | 	config *config.Config | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewHandlers(config *config.Config) *Handlers { | ||||||
|  | 	return &Handlers{ | ||||||
|  | 		config: config, | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -4,34 +4,19 @@ import ( | |||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"git.kill0.net/chill9/beepboop/bot" |  | ||||||
| 	"git.kill0.net/chill9/beepboop/lib" | 	"git.kill0.net/chill9/beepboop/lib" | ||||||
| 
 | 
 | ||||||
| 	"github.com/bwmarrin/discordgo" | 	"github.com/bwmarrin/discordgo" | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type ( | func (h *Handlers) Reaction(s *discordgo.Session, m *discordgo.MessageCreate) { | ||||||
| 	ReactionHandler struct { |  | ||||||
| 		Config bot.Config |  | ||||||
| 	} |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func NewReactionHandler() *ReactionHandler { |  | ||||||
| 	return new(ReactionHandler) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (h *ReactionHandler) SetConfig(config bot.Config) { |  | ||||||
| 	h.Config = config |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (h *ReactionHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { |  | ||||||
| 	if m.Author.ID == s.State.User.ID { | 	if m.Author.ID == s.State.User.ID { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	emojis := h.Config.Handler.Reaction.Emojis | 	emojis := h.config.Handler.Reaction.Emojis | ||||||
| 	channels := h.Config.Handler.Reaction.Channels | 	channels := h.config.Handler.Reaction.Channels | ||||||
| 
 | 
 | ||||||
| 	if len(emojis) == 0 { | 	if len(emojis) == 0 { | ||||||
| 		log.Warning("emoji list is empty") | 		log.Warning("emoji list is empty") | ||||||
							
								
								
									
										108
									
								
								lib/common.go
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								lib/common.go
									
									
									
									
									
								
							| @@ -17,9 +17,7 @@ func Contains[T comparable](s []T, v T) bool { | |||||||
| } | } | ||||||
|  |  | ||||||
| func JoinInt(a []int, sep string) string { | func JoinInt(a []int, sep string) string { | ||||||
| 	var b []string | 	b := make([]string, len(a)) | ||||||
|  |  | ||||||
| 	b = make([]string, len(a)) |  | ||||||
|  |  | ||||||
| 	for i, v := range a { | 	for i, v := range a { | ||||||
| 		b[i] = strconv.Itoa(v) | 		b[i] = strconv.Itoa(v) | ||||||
| @@ -37,11 +35,7 @@ func SumInt(a []int) int { | |||||||
| } | } | ||||||
|  |  | ||||||
| func Itob(v int) bool { | func Itob(v int) bool { | ||||||
| 	if v == 1 { | 	return v == 1 | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return false |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func BuildURI(rawuri, rawpath string) string { | func BuildURI(rawuri, rawpath string) string { | ||||||
| @@ -50,7 +44,34 @@ func BuildURI(rawuri, rawpath string) string { | |||||||
| 	return u.String() | 	return u.String() | ||||||
| } | } | ||||||
|  |  | ||||||
| func HasCommand(s, prefix, cmd string) bool { | func HasCommand(s, prefix string) bool { | ||||||
|  | 	s = strings.TrimSpace(s) | ||||||
|  |  | ||||||
|  | 	if len(s) == 0 || len(prefix) == 0 { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !strings.HasPrefix(s, prefix) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// remove the command prefix | ||||||
|  | 	s = s[len(prefix):] | ||||||
|  |  | ||||||
|  | 	// multiple assignment trick | ||||||
|  | 	cmd, _ := func() (string, string) { | ||||||
|  | 		x := strings.SplitN(s, " ", 2) | ||||||
|  | 		if len(x) > 1 { | ||||||
|  | 			return x[0], x[1] | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return x[0], "" | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	return len(cmd) > 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ContainsCommand(s, prefix, cmd string) bool { | ||||||
| 	s = strings.TrimSpace(s) | 	s = strings.TrimSpace(s) | ||||||
|  |  | ||||||
| 	args := strings.Split(s, " ") | 	args := strings.Split(s, " ") | ||||||
| @@ -72,20 +93,69 @@ func HasCommand(s, prefix, cmd string) bool { | |||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
| func SplitCommandAndArgs(s, prefix string) (cmd string, args []string) { | func SplitCommandAndArg(s, prefix string) (cmd string, arg string) { | ||||||
| 	s = strings.TrimSpace(s) | 	s = strings.TrimSpace(s) | ||||||
|  |  | ||||||
| 	x := strings.Split(s, " ") | 	if !strings.HasPrefix(s, prefix) { | ||||||
|  | 		return | ||||||
| 	if len(x) > 1 { |  | ||||||
| 		args = x[1:] |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	cmd = x[0] | 	// remove the command prefix | ||||||
|  | 	s = s[len(prefix):] | ||||||
|  |  | ||||||
| 	if strings.Index(s, prefix) == 0 { | 	// multiple assignment trick | ||||||
| 		cmd = cmd[len(prefix):] | 	cmd, arg = func() (string, string) { | ||||||
| 	} | 		x := strings.SplitN(s, " ", 2) | ||||||
|  | 		if len(x) > 1 { | ||||||
|  | 			return x[0], x[1] | ||||||
|  | 		} | ||||||
|  | 		return x[0], "" | ||||||
|  | 	}() | ||||||
|  |  | ||||||
| 	return cmd, args | 	return cmd, arg | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func SplitCommandAndArgs(s, prefix string, n int) (cmd string, args []string) { | ||||||
|  | 	cmd, arg := SplitCommandAndArg(s, prefix) | ||||||
|  |  | ||||||
|  | 	if arg == "" { | ||||||
|  | 		return cmd, []string{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if n == 0 { | ||||||
|  | 		return cmd, strings.Split(arg, " ") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return cmd, strings.SplitN(arg, " ", n) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func SplitArgs(s string, n int) (args []string) { | ||||||
|  | 	if s == "" { | ||||||
|  | 		return []string{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if n > 0 { | ||||||
|  | 		args = strings.SplitN(s, " ", n) | ||||||
|  | 	} else { | ||||||
|  | 		args = strings.Split(s, " ") | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func MapKeys[K comparable, V any](m map[K]V) []K { | ||||||
|  | 	keys := make([]K, 0, len(m)) | ||||||
|  | 	for k := range m { | ||||||
|  | 		keys = append(keys, k) | ||||||
|  | 	} | ||||||
|  | 	return keys | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func MapKey[K comparable, V comparable](m map[K]V, v V) K { | ||||||
|  | 	var r K | ||||||
|  | 	for k := range m { | ||||||
|  | 		if m[k] == v { | ||||||
|  | 			return k | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return r | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,8 +1,11 @@ | |||||||
| package lib | package lib | ||||||
|  |  | ||||||
| import "testing" | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
| func TestHasCommand(t *testing.T) { | func TestContainsCommand(t *testing.T) { | ||||||
| 	tables := []struct { | 	tables := []struct { | ||||||
| 		s      string | 		s      string | ||||||
| 		prefix string | 		prefix string | ||||||
| @@ -18,11 +21,139 @@ func TestHasCommand(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, table := range tables { | 	for _, table := range tables { | ||||||
| 		r := HasCommand(table.s, table.prefix, table.cmd) | 		r := ContainsCommand(table.s, table.prefix, table.cmd) | ||||||
| 		if r != table.r { | 		if r != table.r { | ||||||
| 			t.Errorf("HasCommand(%q, %q, %q), got: %t, want: %t", | 			t.Errorf("ContainsCommand(%q, %q, %q), got: %t, want: %t", | ||||||
| 				table.s, table.prefix, table.cmd, r, table.r, | 				table.s, table.prefix, table.cmd, r, table.r, | ||||||
| 			) | 			) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestHasCommandCommand(t *testing.T) { | ||||||
|  | 	tables := []struct { | ||||||
|  | 		s      string | ||||||
|  | 		prefix string | ||||||
|  | 		want   bool | ||||||
|  | 	}{ | ||||||
|  | 		{"!command", "!", true}, | ||||||
|  | 		{"!command x y", "!", true}, | ||||||
|  | 		{"!c x y", "!", true}, | ||||||
|  | 		{"! x y", "!", false}, | ||||||
|  | 		{"hey guy", "!", false}, | ||||||
|  | 		{"hey", "!", false}, | ||||||
|  | 		{"hey", "", false}, | ||||||
|  | 		{"", "!", false}, | ||||||
|  | 		{"", "", false}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, table := range tables { | ||||||
|  | 		if got, want := HasCommand(table.s, table.prefix), table.want; got != want { | ||||||
|  | 			t.Errorf( | ||||||
|  | 				"s: %s, prefix: %s, got: %t, want: %t", | ||||||
|  | 				table.s, table.prefix, got, want, | ||||||
|  | 			) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestSplitCommandAndArg(t *testing.T) { | ||||||
|  | 	tables := []struct { | ||||||
|  | 		s       string | ||||||
|  | 		prefix  string | ||||||
|  | 		wantCmd string | ||||||
|  | 		wantArg string | ||||||
|  | 	}{ | ||||||
|  | 		{"!command x y", "!", "command", "x y"}, | ||||||
|  | 		{"!command", "!", "command", ""}, | ||||||
|  | 		{"hey man", "!", "", ""}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, table := range tables { | ||||||
|  | 		gotCmd, gotArg := SplitCommandAndArg(table.s, table.prefix) | ||||||
|  | 		if gotCmd != table.wantCmd { | ||||||
|  | 			t.Errorf("got: %s, want: %s", gotCmd, table.wantCmd) | ||||||
|  | 		} | ||||||
|  | 		if gotArg != table.wantArg { | ||||||
|  | 			t.Errorf("got: %+v, want: %+v", gotArg, table.wantArg) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestSplitCommandAndArgs(t *testing.T) { | ||||||
|  | 	tables := []struct { | ||||||
|  | 		s        string | ||||||
|  | 		prefix   string | ||||||
|  | 		n        int | ||||||
|  | 		wantCmd  string | ||||||
|  | 		wantArgs []string | ||||||
|  | 	}{ | ||||||
|  | 		{"!command x y", "!", 2, "command", []string{"x", "y"}}, | ||||||
|  | 		{"!command x y z", "!", 2, "command", []string{"x", "y z"}}, | ||||||
|  | 		{"!command", "!", 1, "command", []string{}}, | ||||||
|  | 		{"hey man", "!", 1, "", []string{}}, | ||||||
|  | 	} | ||||||
|  | 	for _, table := range tables { | ||||||
|  | 		gotCmd, gotArgs := SplitCommandAndArgs(table.s, table.prefix, table.n) | ||||||
|  | 		if gotCmd != table.wantCmd { | ||||||
|  | 			t.Errorf("got: %s, want: %s", gotCmd, table.wantCmd) | ||||||
|  | 		} | ||||||
|  | 		if !reflect.DeepEqual(gotArgs, table.wantArgs) { | ||||||
|  | 			t.Errorf("got: %#v, want: %#v", gotArgs, table.wantArgs) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestSplitArgs(t *testing.T) { | ||||||
|  | 	tables := []struct { | ||||||
|  | 		s    string | ||||||
|  | 		n    int | ||||||
|  | 		want []string | ||||||
|  | 	}{ | ||||||
|  | 		{"a b c", 0, []string{"a", "b", "c"}}, | ||||||
|  | 		{"a b c", 1, []string{"a b c"}}, | ||||||
|  | 		{"a b c", 2, []string{"a", "b c"}}, | ||||||
|  | 		{"a b c", 3, []string{"a", "b", "c"}}, | ||||||
|  | 		{"a b c", 4, []string{"a", "b", "c"}}, | ||||||
|  | 		{"", 0, []string{}}, | ||||||
|  | 	} | ||||||
|  | 	for _, table := range tables { | ||||||
|  | 		if got, want := SplitArgs(table.s, table.n), table.want; !reflect.DeepEqual(got, want) { | ||||||
|  | 			t.Errorf("got: %#v, want: %#v", got, want) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMapKeys(t *testing.T) { | ||||||
|  | 	tables := []struct { | ||||||
|  | 		m    map[string]int | ||||||
|  | 		want []string | ||||||
|  | 	}{ | ||||||
|  | 		{map[string]int{"a": 0, "b": 1, "c": 3}, []string{"a", "b", "c"}}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, table := range tables { | ||||||
|  | 		if got, want := MapKeys(table.m), table.want; !reflect.DeepEqual(got, want) { | ||||||
|  | 			t.Errorf("got: %#v, want: %#v", got, want) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMapKey(t *testing.T) { | ||||||
|  | 	tables := []struct { | ||||||
|  | 		m    map[string]int | ||||||
|  | 		n    int | ||||||
|  | 		want string | ||||||
|  | 	}{ | ||||||
|  | 		{map[string]int{"a": 0, "b": 1, "c": 2}, 0, "a"}, | ||||||
|  | 		{map[string]int{"a": 0, "b": 1, "c": 2}, 1, "b"}, | ||||||
|  | 		{map[string]int{"a": 0, "b": 1, "c": 2}, 2, "c"}, | ||||||
|  | 		{map[string]int{"a": 0, "b": 1, "c": 2, "d": 0}, 0, "a"}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, table := range tables { | ||||||
|  | 		if got, want := MapKey(table.m, table.n), table.want; got != want { | ||||||
|  | 			t.Errorf("got: %#v, want: %#v", got, want) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								lib/rand.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								lib/rand.go
									
									
									
									
									
								
							| @@ -37,3 +37,21 @@ func SeedMathRand() error { | |||||||
| func RandInt(min int, max int) int { | func RandInt(min int, max int) int { | ||||||
| 	return rand.Intn(max-min+1) + min | 	return rand.Intn(max-min+1) + min | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func MapRand[K comparable, V any](m map[K]V) V { | ||||||
|  | 	n := rand.Intn(len(m)) | ||||||
|  | 	i := 0 | ||||||
|  | 	for _, v := range m { | ||||||
|  | 		if i == n { | ||||||
|  | 			return v | ||||||
|  | 		} | ||||||
|  | 		i++ | ||||||
|  | 	} | ||||||
|  | 	panic("unreachable") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func MapRandKey[K comparable, V any](m map[K]V) K { | ||||||
|  | 	keys := MapKeys(m) | ||||||
|  | 	n := rand.Intn(len(m)) | ||||||
|  | 	return keys[n] | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										118
									
								
								lib/rps/rps.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								lib/rps/rps.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | |||||||
|  | package rps | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"git.kill0.net/chill9/beepboop/lib" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type ( | ||||||
|  | 	Game struct { | ||||||
|  | 		rules    [][]string | ||||||
|  | 		emojiMap map[string]string | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type InvalidChoiceError struct { | ||||||
|  | 	s string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e InvalidChoiceError) Error() string { | ||||||
|  | 	return fmt.Sprintf("%q is an invalid choice", e.s) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	RulesRps [][]string = [][]string{ | ||||||
|  | 		{"rock", "scissors", "crushes"}, | ||||||
|  | 		{"paper", "rock", "covers"}, | ||||||
|  | 		{"scissors", "paper", "cuts"}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	EmojiMapRps map[string]string = map[string]string{ | ||||||
|  | 		"rock":     "🪨️", | ||||||
|  | 		"paper":    "📝", | ||||||
|  | 		"scissors": "✂️", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	RulesRpsls [][]string = [][]string{ | ||||||
|  | 		{"rock", "scissors", "crushes"}, | ||||||
|  | 		{"rock", "lizard", "crushes"}, | ||||||
|  | 		{"paper", "rock", "covers"}, | ||||||
|  | 		{"paper", "spock", "disproves"}, | ||||||
|  | 		{"scissors", "paper", "cuts"}, | ||||||
|  | 		{"scissors", "lizard", "decapitates"}, | ||||||
|  | 		{"lizard", "paper", "eats"}, | ||||||
|  | 		{"lizard", "spock", "poisons"}, | ||||||
|  | 		{"spock", "scissors", "smashes"}, | ||||||
|  | 		{"spock", "rock", "vaporizes"}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	EmojiMapRpsls map[string]string = map[string]string{ | ||||||
|  | 		"rock":     "🪨️", | ||||||
|  | 		"paper":    "📝", | ||||||
|  | 		"scissors": "✂️", | ||||||
|  | 		"lizard":   "🦎", | ||||||
|  | 		"spock":    "🖖", | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func NewGame(rules [][]string, emojiMap map[string]string) *Game { | ||||||
|  | 	return &Game{rules: rules, emojiMap: emojiMap} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *Game) Rand() string { | ||||||
|  | 	return lib.MapRandKey(g.emojiMap) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *Game) Play(c1, c2 string) (string, error) { | ||||||
|  | 	var b strings.Builder | ||||||
|  |  | ||||||
|  | 	if !g.Valid(c1) { | ||||||
|  | 		return "", InvalidChoiceError{s: c1} | ||||||
|  | 	} | ||||||
|  | 	if !g.Valid(c2) { | ||||||
|  | 		return "", InvalidChoiceError{s: c2} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fmt.Fprintf(&b, "%s v %s: ", g.emojiMap[c1], g.emojiMap[c2]) | ||||||
|  |  | ||||||
|  | 	for _, rule := range g.rules { | ||||||
|  | 		verb := rule[2] | ||||||
|  |  | ||||||
|  | 		if c1 == c2 { | ||||||
|  | 			fmt.Fprintf(&b, "draw") | ||||||
|  | 			return b.String(), nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if c1 == rule[0] && c2 == rule[1] { | ||||||
|  | 			fmt.Fprintf(&b, "%s %s %s", c1, verb, c2) | ||||||
|  | 			return b.String(), nil | ||||||
|  | 		} else if c2 == rule[0] && c1 == rule[1] { | ||||||
|  | 			fmt.Fprintf(&b, "%s %s %s", c2, verb, c1) | ||||||
|  | 			return b.String(), nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return b.String(), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *Game) Valid(c string) bool { | ||||||
|  | 	_, ok := g.emojiMap[c] | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Play(rules [][]string, c1, c2 string) string { | ||||||
|  | 	for _, rule := range rules { | ||||||
|  | 		if c1 == c2 { | ||||||
|  | 			return "draw" | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if c1 == rule[0] && c2 == rule[1] { | ||||||
|  | 			return fmt.Sprintf("%s %s %s", c1, rule[2], c2) | ||||||
|  | 		} else if c2 == rule[0] && c1 == rule[1] { | ||||||
|  | 			return fmt.Sprintf("%s %s %s", c2, rule[2], c1) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
| @@ -22,6 +22,12 @@ type ( | |||||||
| 			H1 float32 `json:"1h"` | 			H1 float32 `json:"1h"` | ||||||
| 			H3 float32 `json:"3h"` | 			H3 float32 `json:"3h"` | ||||||
| 		} `json:"rain"` | 		} `json:"rain"` | ||||||
|  |  | ||||||
|  | 		Weather []struct { | ||||||
|  | 			Main        string `json:"main"` | ||||||
|  | 			Description string `json:"description"` | ||||||
|  | 			Icon        string `json:"icon"` | ||||||
|  | 		} `json:"weather"` | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	WeatherError struct { | 	WeatherError struct { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user