Reorganize packages
This commit is contained in:
37
bot/handler/ping.go
Normal file
37
bot/handler/ping.go
Normal 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
259
bot/handler/random.go
Normal 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)
|
||||
}
|
@ -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
64
bot/handler/time.go
Normal 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
49
bot/handler/version.go
Normal 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
161
bot/handler/weather.go
Normal 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(),
|
||||
))
|
||||
}
|
Reference in New Issue
Block a user