Reorganize packages

This commit is contained in:
2022-08-24 09:06:00 -05:00
parent 6c0000d409
commit ce689146de
11 changed files with 248 additions and 100 deletions

37
bot/handler/ping.go Normal file
View File

@ -0,0 +1,37 @@
package handler
import (
"strings"
"git.kill0.net/chill9/beepboop/bot"
"github.com/bwmarrin/discordgo"
log "github.com/sirupsen/logrus"
)
type (
PingHandler struct {
config bot.Config
}
)
func NewPingHandler() *PingHandler {
return new(PingHandler)
}
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 !strings.HasPrefix(m.Content, "!ping") {
return
}
log.Debug("received ping")
s.ChannelMessageSend(m.ChannelID, "pong")
}

259
bot/handler/random.go Normal file
View File

@ -0,0 +1,259 @@
package handler
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"git.kill0.net/chill9/beepboop/bot"
"git.kill0.net/chill9/beepboop/lib"
"github.com/bwmarrin/discordgo"
log "github.com/sirupsen/logrus"
)
const (
MaxDice = 100
MaxSides = 100
Bullets = 1
GunFireMessage = "💀🔫"
GunClickMessage = "😌🔫"
)
type (
Roll struct {
N, D, Sum int
Rolls []int
S string
}
Coin bool
Gun struct {
C [6]bool
N int
}
CoinHandler struct {
config bot.Config
}
RollHandler struct {
config bot.Config
}
RouletteHandler struct {
config bot.Config
}
)
var (
gun *Gun
)
func init() {
gun = NewGun()
}
func NewRoll(n, d int) *Roll {
r := new(Roll)
r.N = n
r.D = d
r.S = fmt.Sprintf("%dd%d", r.N, r.D)
return r
}
func ParseRoll(roll string) (*Roll, error) {
var (
dice []string
err error
n, d int
)
match, _ := regexp.MatchString(`^(?:\d+)?d\d+$`, roll)
if !match {
return nil, errors.New("invalid roll, use `<n>d<sides>` e.g. `4d6`")
}
dice = strings.Split(roll, "d")
if dice[0] == "" {
n = 1
} else {
n, err = strconv.Atoi(dice[0])
if err != nil {
return nil, err
}
}
d, err = strconv.Atoi(dice[1])
if err != nil {
return nil, err
}
if n > MaxDice || d > MaxSides {
return nil, fmt.Errorf("invalid roll, n must be <= %d and sides must be <= %d", MaxDice, MaxSides)
}
return NewRoll(n, d), nil
}
func (r *Roll) RollDice() {
for i := 1; i <= r.N; i++ {
roll := lib.RandInt(1, r.D)
r.Rolls = append(r.Rolls, roll)
r.Sum += roll
}
}
func (c *Coin) Flip() bool {
*c = Coin(lib.Itob(lib.RandInt(0, 1)))
return bool(*c)
}
func NewGun() *Gun {
return new(Gun)
}
func (g *Gun) Load(n int) {
g.N = 0
for i := 1; i <= n; {
x := lib.RandInt(0, len(g.C)-1)
if g.C[x] == false {
g.C[x] = true
i++
} else {
continue
}
}
}
func (g *Gun) Fire() bool {
if g.C[g.N] {
g.C[g.N] = false
g.N++
return true
}
g.N++
return false
}
func (g *Gun) IsEmpty() bool {
for _, v := range g.C {
if v == true {
return false
}
}
return true
}
func NewRollHandler() *RollHandler {
return new(RollHandler)
}
func (h *RollHandler) SetConfig(config bot.Config) {
h.config = config
}
func (h *RollHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) {
var (
err error
msg, roll string
r *Roll
)
if m.Author.ID == s.State.User.ID {
return
}
if !strings.HasPrefix(m.Content, "!roll") {
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)
if err != nil {
s.ChannelMessageSend(m.ChannelID, err.Error())
return
}
r.RollDice()
log.Debugf("rolled dice: %+v", r)
msg = fmt.Sprintf("🎲 %s = %d", lib.JoinInt(r.Rolls, " + "), r.Sum)
s.ChannelMessageSend(m.ChannelID, msg)
}
func NewRouletteHandler() *RouletteHandler {
return new(RouletteHandler)
}
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() {
gun.Load(Bullets)
log.Debugf("reloading gun: %+v\n", gun)
}
log.Debugf("firing gun: %+v\n", gun)
if gun.Fire() {
s.ChannelMessageSend(m.ChannelID, GunFireMessage)
} else {
s.ChannelMessageSend(m.ChannelID, GunClickMessage)
}
}
func NewCoinHandler() *CoinHandler {
return new(CoinHandler)
}
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 !strings.HasPrefix(m.Content, "!coin") {
return
}
if c.Flip() {
msg = "heads"
} else {
msg = "tails"
}
s.ChannelMessageSend(m.ChannelID, msg)
}

View File

@ -5,7 +5,6 @@ import (
"strings"
"git.kill0.net/chill9/beepboop/bot"
"git.kill0.net/chill9/beepboop/command"
"git.kill0.net/chill9/beepboop/lib"
"github.com/bwmarrin/discordgo"
@ -14,7 +13,7 @@ import (
type (
ReactionHandler struct {
config bot.Config
Config bot.Config
}
)
@ -23,7 +22,7 @@ func NewReactionHandler() *ReactionHandler {
}
func (h *ReactionHandler) SetConfig(config bot.Config) {
h.config = config
h.Config = config
}
func (h *ReactionHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) {
@ -31,8 +30,8 @@ func (h *ReactionHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreat
return
}
emojis := h.config.Handler.Reaction.Emojis
channels := h.config.Handler.Reaction.Channels
emojis := h.Config.Handler.Reaction.Emojis
channels := h.Config.Handler.Reaction.Channels
if len(emojis) == 0 {
log.Warning("emoji list is empty")
@ -50,7 +49,7 @@ func (h *ReactionHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreat
for _, a := range m.Attachments {
if strings.HasPrefix(a.ContentType, "image/") {
for i := 1; i <= command.RandInt(1, len(emojis)); i++ {
for i := 1; i <= lib.RandInt(1, len(emojis)); i++ {
r := emojis[rand.Intn(len(emojis))]
s.MessageReactionAdd(m.ChannelID, m.ID, r)
}
@ -58,7 +57,7 @@ func (h *ReactionHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreat
}
for range m.Embeds {
for i := 1; i <= command.RandInt(1, len(emojis)); i++ {
for i := 1; i <= lib.RandInt(1, len(emojis)); i++ {
r := emojis[rand.Intn(len(emojis))]
s.MessageReactionAdd(m.ChannelID, m.ID, r)
}

64
bot/handler/time.go Normal file
View File

@ -0,0 +1,64 @@
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
}
)
func NewTimeHandler() *TimeHandler {
return new(TimeHandler)
}
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))
}

49
bot/handler/version.go Normal file
View File

@ -0,0 +1,49 @@
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 {
h := new(VersionHandler)
h.Name = s
return h
}
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,
))
}

161
bot/handler/weather.go Normal file
View File

@ -0,0 +1,161 @@
package handler
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"git.kill0.net/chill9/beepboop/bot"
"git.kill0.net/chill9/beepboop/lib"
"github.com/bwmarrin/discordgo"
log "github.com/sirupsen/logrus"
)
const (
OpenWeatherMapURI = "https://api.openweathermap.org"
)
var (
EndpointWeather = lib.BuildURI(OpenWeatherMapURI, "/data/2.5/weather")
)
type (
WeatherHandler struct {
Config bot.Config
}
Temperature float32
Weather struct {
Main struct {
Temp Temperature `json:"temp"`
FeelsLike Temperature `json:"feels_like"`
TempMin Temperature `json:"temp_min"`
TempMax Temperature `json:"temp_max"`
Pressure float32 `json:"pressure"`
Humidity float32 `json:"humidity"`
} `json:"main"`
Coord struct {
Lon float32 `json:"lon"`
Lat float32 `json:"lat"`
} `json:"coord"`
Rain struct {
H1 float32 `json:"1h"`
H3 float32 `json:"3h"`
} `json:"rain"`
}
WeatherError struct {
Message string `json:"message"`
}
)
func (t *Temperature) Kelvin() float32 {
return float32(*t)
}
func (t *Temperature) Fahrenheit() float32 {
return ((float32(*t) - 273.15) * (9.0 / 5)) + 32
}
func (t *Temperature) Celcius() float32 {
return float32(*t) - 273.15
}
func NewWeatherHandler() *WeatherHandler {
return new(WeatherHandler)
}
func (h *WeatherHandler) SetConfig(config bot.Config) {
h.Config = config
}
func (h *WeatherHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) {
var (
loc string
w Weather
werr WeatherError
)
if m.Author.ID == s.State.User.ID {
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>`")
return
}
loc = x[1]
if h.Config.OpenWeatherMapToken == "" {
log.Error("OpenWeather token is not set")
return
}
req, err := http.NewRequest("GET", EndpointWeather, nil)
if err != nil {
log.Errorf("failed to create new request: %s", err)
return
}
q := req.URL.Query()
q.Add("q", loc)
q.Add("appid", h.Config.OpenWeatherMapToken)
req.URL.RawQuery = q.Encode()
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Errorf("HTTP request failed: %s", err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Errorf("reading HTTP response failed: %s", err)
return
}
if resp.StatusCode != 200 {
err = json.Unmarshal(body, &werr)
if err != nil {
log.Debugf("%s\n", body)
log.Errorf("unmarshaling JSON failed: %s", err)
return
}
log.Warnf("error: (%s) %s", resp.Status, werr.Message)
return
}
log.Debugf("weather requested for '%s'\n", loc)
err = json.Unmarshal(body, &w)
if err != nil {
log.Debugf("%s\n", body)
log.Errorf("unmarshaling JSON failed: %s", err)
return
}
log.Debugf("weather returned for '%s': %+v\n", loc, w)
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf(
"%s (%.1f, %.1f) — C:%.1f F:%.1f K:%.1f",
loc,
w.Coord.Lat,
w.Coord.Lon,
w.Main.Temp.Celcius(),
w.Main.Temp.Fahrenheit(),
w.Main.Temp.Kelvin(),
))
}