Compare commits

..

1 Commits
main ... staff

Author SHA1 Message Date
Etzelia f10f83e3b9
Add ServerAPI, clean up help, Twitter
Signed-off-by: Etzelia <etzelia@hotmail.com>
2021-02-21 23:36:06 -06:00
40 changed files with 333 additions and 1249 deletions

View File

@ -7,13 +7,13 @@ trigger:
steps: steps:
- name: build - name: build
pull: always pull: always
image: golang:1.16 image: golang:1.15
commands: commands:
- make test - make test
- make build - make build-all
- name: check - name: check
pull: always pull: always
image: golang:1.16 image: golang:1.15
commands: commands:
- make vet - make vet
@ -28,15 +28,15 @@ trigger:
steps: steps:
- name: build - name: build
pull: always pull: always
image: golang:1.16 image: golang:1.15
commands: commands:
- make build - make build-all
- name: gitea-release - name: gitea-release
pull: always pull: always
image: jolheiser/drone-gitea-main:latest image: jolheiser/drone-gitea-main:latest
settings: settings:
token: token:
from_secret: gitea_token from_secret: gitea_token
base: https://git.canopymc.net base: https://git.etztech.xyz
files: files:
- "canopeas" - "sedbot"

6
.gitignore vendored
View File

@ -1,6 +1,6 @@
# GoLand # GoLand
.idea/ .idea/
# canopeas # sedbot
canopeas* sedbot*
!config/canopeas.example.toml !config/sedbot.example.toml

31
Makefile 100644
View File

@ -0,0 +1,31 @@
GO ?= go
.PHONY: fmt
fmt:
$(GO) fmt ./...
.PHONY: imp
imp:
imp -w
.PHONY: generate
generate:
$(GO) generate ./...
.PHONY: test
test:
$(GO) test -race ./...
.PHONY: vet
vet:
$(GO) vet ./...
.PHONY: build
build:
$(GO) build
.PHONY: build-all
build-all: generate build
.PHONY: check
check: generate imp fmt test vet build

View File

@ -1,6 +1,6 @@
# canopeas # SedBot
Canopy Discord generi-bot. BirbMC Discord generi-bot.
## Commands ## Commands
@ -20,6 +20,12 @@ Canopy Discord generi-bot.
* `clear [<@user>] <number>` - Clear <number> messages (optionally only by @user) * `clear [<@user>] <number>` - Clear <number> messages (optionally only by @user)
## Building
```text
make build-all
```
## License ## License
[MIT](LICENSE) [MIT](LICENSE)

View File

@ -8,7 +8,7 @@ import (
"github.com/pelletier/go-toml" "github.com/pelletier/go-toml"
) )
//go:embed canopeas.example.toml //go:embed sedbot.example.toml
var defaultConfig []byte var defaultConfig []byte
type Config struct { type Config struct {
@ -35,15 +35,12 @@ type Config struct {
AccessToken string `toml:"access_token"` AccessToken string `toml:"access_token"`
AccessSecret string `toml:"access_secret"` AccessSecret string `toml:"access_secret"`
} `toml:"twitter"` } `toml:"twitter"`
StaffRoles []string `toml:"staff_roles"` StaffRoles []string `toml:"staff_roles"`
Echoes []Echo `toml:"echoes"` Echoes []Echo `toml:"echoes"`
Albums []Album `toml:"albums"`
MessageRoles []MessageRole `toml:"message_roles"` MessageRoles []MessageRole `toml:"message_roles"`
Register struct { RegisterRole string `toml:"register_role"`
URL string `toml:"url"` RegisteredChannel string `toml:"registered_channel"`
Role string `toml:"role"`
WelcomeChannel string `toml:"welcome_channel"`
} `toml:"register"`
LeaveChannel string `toml:"leave_channel"` LeaveChannel string `toml:"leave_channel"`
FiredRole string `toml:"fired_role"` FiredRole string `toml:"fired_role"`
MemeRate string `toml:"meme_rate"` MemeRate string `toml:"meme_rate"`
@ -58,22 +55,13 @@ type Config struct {
Nouns []string `toml:"nouns"` Nouns []string `toml:"nouns"`
MinorThings []string `toml:"minor_things"` MinorThings []string `toml:"minor_things"`
} `toml:"compliment"` } `toml:"compliment"`
Welcome struct {
Channel string `toml:"channel"`
Message string `toml:"message"`
DM string `toml:"dm"`
} `toml:"welcome"`
} }
type MessageRole struct { type MessageRole struct {
ChannelID string `toml:"channel_id"` ChannelID string `toml:"channel_id"`
MessageID string `toml:"message_id"` MessageID string `toml:"message_id"`
Reactions []MessageReaction `toml:"reactions"`
}
type MessageReaction struct {
Emoji string `toml:"emoji"`
RoleID string `toml:"role_id"` RoleID string `toml:"role_id"`
Emoji string `toml:"emoji"`
} }
type Echo struct { type Echo struct {
@ -83,18 +71,11 @@ type Echo struct {
Help string `toml:"help"` Help string `toml:"help"`
} }
type Album struct {
Name string `toml:"name"`
Aliases []string `toml:"aliases"`
AlbumID string `toml:"album_id"`
Help string `toml:"help"`
}
func Load(configPath string) (*Config, error) { func Load(configPath string) (*Config, error) {
var err error var err error
var configContent []byte var configContent []byte
if len(configPath) == 0 { if len(configPath) == 0 {
configPath = "canopeas.toml" configPath = "sedbot.toml"
} }
configContent, err = ioutil.ReadFile(configPath) configContent, err = ioutil.ReadFile(configPath)

View File

@ -5,7 +5,7 @@ token = ""
prefix = "!" prefix = "!"
# db_path is the path to the database (default is next to binary) # db_path is the path to the database (default is next to binary)
db_path = "canopeas.db" db_path = "sedbot.db"
# mc_path is the path to the root directory of the minecraft server # mc_path is the path to the root directory of the minecraft server
mc_path = "/home/minecraft/server/" mc_path = "/home/minecraft/server/"
@ -13,6 +13,12 @@ mc_path = "/home/minecraft/server/"
# fired_role is to check how many time Carolyn has been fired # fired_role is to check how many time Carolyn has been fired
fired_role = "0" fired_role = "0"
# register_role is the role to assign to a user after registering
register_role = "0"
# registered_channel is the channel to message to welcome the newly registered user
registered_channel = "0"
# leave_channel is the channel to post leave messages to # leave_channel is the channel to post leave messages to
leave_channel = "0" leave_channel = "0"
@ -25,44 +31,6 @@ meme_rate = "0"
# Imgur Client ID # Imgur Client ID
imgur_client_id = "" imgur_client_id = ""
# Welcome new users
[welcome]
# Channel ID of the welcome channel
channel = "0"
# Message to send in welcome channel
message = """\
**Hey ${user}, welcome to The Canopy 🎉 !**
If you are new, please read #rules and fill out an application here: https://canopymc.net/apply.
You will need to join our creative server at `creative.canopymc.net` to be verified.
**After** applying and joining the server, please run the `!register <MC username>` (without the brackets) command in this channel to join the Discord!
"""
# DM to send to user
dm = """\
**Hey ${user}, welcome to The Canopy 🎉 !**
If you are new, please read #rules and fill out an application here: https://canopymc.net/apply.
You will need to join our survival server at `creative.canopymc.net` to be verified.
After doing that please run the `!register <MC username>` (without the brackets) command in the welcome channel to join the Discord!
"""
[register]
# role is the role to assign to a user after registering
role = "0"
# url is the URL to show to new users
url = "https://google.com"
# welcome_channel is the channel to message to welcome the newly registered user
welcome_channel = "0"
# ServerAPI options # ServerAPI options
[serverapi] [serverapi]
# API endpoint # API endpoint
@ -110,12 +78,6 @@ verbs = []
nouns = [] nouns = []
minor_things = [] minor_things = []
[[albums]]
name = "jupiter"
aliases = ["jup", "jupjup"]
album_id = ""
help = "Images of Jupiter"
# echoes are any basic command -> message # echoes are any basic command -> message
[[echoes]] [[echoes]]
name = "discord" name = "discord"
@ -127,9 +89,5 @@ help = "Get the invite link"
[[message_roles]] [[message_roles]]
channel_id = "0" channel_id = "0"
message_id = "0" message_id = "0"
[[message_roles.reactions]]
role_id = "0" role_id = "0"
emoji = "👍" emoji = "👍"
[[message_roles.reactions]]
role_id = "0"
emoji = "👎"

View File

@ -19,7 +19,7 @@ type Database struct {
} }
func Load(dbPath string) (*Database, error) { func Load(dbPath string) (*Database, error) {
db, err := bbolt.Open(dbPath, 0o600, bbolt.DefaultOptions) db, err := bbolt.Open(dbPath, 0600, bbolt.DefaultOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -6,13 +6,12 @@ import (
"strings" "strings"
"time" "time"
"git.jojodev.com/Minecraft/go-serverapi" "go.etztech.xyz/go-serverapi"
) )
func init() { func init() {
commands = append(commands, &command{ commands["ban"] = command{
staffOnly: true, staffOnly: true,
name: "ban",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles) return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles)
}, },
@ -50,5 +49,5 @@ func init() {
return fmt.Sprintf("%s was banned by %s", target, cmd.message.Author.Username), nil return fmt.Sprintf("%s was banned by %s", target, cmd.message.Author.Username), nil
}, },
help: "Ban a player", help: "Ban a player",
}) }
} }

View File

@ -6,12 +6,11 @@ import (
"strconv" "strconv"
"strings" "strings"
"git.jojodev.com/Minecraft/canopeas/falseknees" "go.etztech.xyz/falseknees"
) )
func init() { func init() {
commands = append(commands, &command{ commands["birb"] = command{
name: "birb",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
@ -43,5 +42,5 @@ func init() {
return fmt.Sprintf("%d: %s\n%s", comic.Num, comic.Title, comic.Img), nil return fmt.Sprintf("%d: %s\n%s", comic.Num, comic.Title, comic.Img), nil
}, },
help: "Get a FalseKnees comic", help: "Get a FalseKnees comic",
}) }
} }

View File

@ -5,13 +5,12 @@ import (
"net/http" "net/http"
"strings" "strings"
"git.jojodev.com/Minecraft/go-serverapi" "go.etztech.xyz/go-serverapi"
) )
func init() { func init() {
commands = append(commands, &command{ commands["broadcast"] = command{
staffOnly: true, staffOnly: true,
name: "broadcast",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles) return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles)
}, },
@ -38,5 +37,5 @@ func init() {
return "Broadcast sent!", nil return "Broadcast sent!", nil
}, },
help: "Send an in-game broadcast", help: "Send an in-game broadcast",
}) }
} }

View File

@ -8,9 +8,7 @@ import (
const clearMax = 20 const clearMax = 20
func init() { func init() {
commands = append(commands, &command{ commands["clear"] = command{
name: "clear",
staffOnly: true,
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles) return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles)
}, },
@ -66,5 +64,5 @@ func init() {
return "", cmd.session.ChannelMessagesBulkDelete(cmd.message.ChannelID, batch) return "", cmd.session.ChannelMessagesBulkDelete(cmd.message.ChannelID, batch)
}, },
help: "Clear messages", help: "Clear messages",
}) }
} }

View File

@ -6,8 +6,7 @@ import (
) )
func init() { func init() {
commands = append(commands, &command{ commands["compliment"] = command{
name: "compliment",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
@ -39,5 +38,5 @@ func init() {
return "", nil return "", nil
}, },
help: "Compliment someone!", help: "Compliment someone!",
}) }
} }

View File

@ -2,11 +2,8 @@ package discord
import ( import (
"encoding/json" "encoding/json"
"errors"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"github.com/rs/zerolog/log"
) )
const ( const (
@ -21,8 +18,7 @@ type dadJoke struct {
} }
func init() { func init() {
commands = append(commands, &command{ commands["dad"] = command{
name: "dad",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
@ -31,48 +27,38 @@ func init() {
return "", nil return "", nil
} }
dj, err := newDadJoke() req, err := http.NewRequest(http.MethodGet, dadJokeAPI, nil)
if err != nil { if err != nil {
log.Warn().Msgf("error getting new dad joke: %v", err) return "", err
}
req.Header.Add("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
if resp.StatusCode != http.StatusOK {
return dadJokeErr, nil
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var dj dadJoke
if err := json.Unmarshal(body, &dj); err != nil {
return "", nil
}
// Check status again, in case API returned an error
if dj.Status != http.StatusOK {
return dadJokeErr, nil return dadJokeErr, nil
} }
return dj.Joke, nil return dj.Joke, nil
}, },
help: "Get a random Dad joke", help: "Get a random Dad joke",
})
} }
func newDadJoke() (*dadJoke, error) {
req, err := http.NewRequest(http.MethodGet, dadJokeAPI, nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New("non-ok status")
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var dj *dadJoke
if err := json.Unmarshal(body, &dj); err != nil {
return nil, errors.New("could not unmarshal")
}
// Check status again, in case API returned an error
if dj.Status != http.StatusOK {
return nil, errors.New("API error")
}
return dj, nil
} }

View File

@ -2,30 +2,26 @@ package discord
import ( import (
"fmt" "fmt"
"os"
"strings" "strings"
"time" "time"
"github.com/rs/zerolog/log" "go.etztech.xyz/sedbot/config"
"go.etztech.xyz/sedbot/database"
"git.jojodev.com/Minecraft/go-serverapi" "go.etztech.xyz/sedbot/imgur"
"git.jojodev.com/Minecraft/canopeas/config"
"git.jojodev.com/Minecraft/canopeas/database"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
"github.com/dghubble/go-twitter/twitter" "github.com/dghubble/go-twitter/twitter"
"github.com/dghubble/oauth1" "github.com/dghubble/oauth1"
"go.etztech.xyz/go-serverapi"
"go.jolheiser.com/beaver"
) )
// Register commands to this map // Register commands to this map
var ( var (
commands = make([]*command, 0) commands = make(map[string]command)
commandMap = make(map[string]*command)
messageRoleMap = make(map[string]map[string]string) messageRoleMap = make(map[string]map[string]string)
memeRateLimit *rateLimit memeRateLimit *rateLimit
embedColor = 0x007D96
) )
type commandInit struct { type commandInit struct {
@ -39,13 +35,9 @@ type commandInit struct {
type command struct { type command struct {
staffOnly bool staffOnly bool
deleteInvocation bool
child bool
// TODO Does this really need to exist separately? // TODO Does this really need to exist separately?
validate func(cmd commandInit) bool validate func(cmd commandInit) bool
run func(cmd commandInit) (string, error) run func(cmd commandInit) (string, error)
name string
aliases []string
help string help string
} }
@ -55,8 +47,11 @@ func Bot(cfg *config.Config, db *database.Database) (*discordgo.Session, error)
return nil, err return nil, err
} }
// Init rand // Init Jupiter images
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
if err := imgur.Init(cfg.ImgurClientID); err != nil {
return nil, err
}
// Init ServerAPI // Init ServerAPI
sapi := serverapi.NewClient(cfg.ServerAPI.Endpoint, serverapi.WithToken(cfg.ServerAPI.Token)) sapi := serverapi.NewClient(cfg.ServerAPI.Endpoint, serverapi.WithToken(cfg.ServerAPI.Token))
@ -78,30 +73,12 @@ func Bot(cfg *config.Config, db *database.Database) (*discordgo.Session, error)
if messageRoleMap[messageRole.MessageID] == nil { if messageRoleMap[messageRole.MessageID] == nil {
messageRoleMap[messageRole.MessageID] = make(map[string]string) messageRoleMap[messageRole.MessageID] = make(map[string]string)
} }
for _, reaction := range messageRole.Reactions { _ = bot.MessageReactionAdd(messageRole.ChannelID, messageRole.MessageID, messageRole.Emoji)
_ = bot.MessageReactionAdd(messageRole.ChannelID, messageRole.MessageID, reaction.Emoji) messageRoleMap[messageRole.MessageID][messageRole.Emoji] = messageRole.RoleID
messageRoleMap[messageRole.MessageID][reaction.Emoji] = reaction.RoleID
}
} }
// Init commandMap
if err := Album(cfg); err != nil {
return nil, err
}
Echo(cfg) Echo(cfg)
for _, c := range commands {
if c.name == "" {
log.Error().Msgf("command is missing a name: %s", c.help)
continue
}
commandMap[c.name] = c
for _, a := range c.aliases {
commandMap[a] = c
}
}
bot.AddHandler(readyHandler()) bot.AddHandler(readyHandler())
bot.AddHandler(joinHandler(cfg))
bot.AddHandler(leaveHandler(cfg)) bot.AddHandler(leaveHandler(cfg))
bot.AddHandler(commandHandler(cfg, db, sapi, twitterClient)) bot.AddHandler(commandHandler(cfg, db, sapi, twitterClient))
bot.AddHandler(messageHandler(cfg, db)) bot.AddHandler(messageHandler(cfg, db))
@ -123,7 +100,7 @@ func Bot(cfg *config.Config, db *database.Database) (*discordgo.Session, error)
func sendTyping(s *discordgo.Session, channelID string) { func sendTyping(s *discordgo.Session, channelID string) {
if err := s.ChannelTyping(channelID); err != nil { if err := s.ChannelTyping(channelID); err != nil {
log.Error().Msgf("could not send typing status: %v", err) beaver.Errorf("could not send typing status: %v", err)
} }
} }
@ -139,17 +116,16 @@ func sendMessage(s *discordgo.Session, channelID, content string, scrub bool) *d
msg, err = s.ChannelMessageSend(channelID, content) msg, err = s.ChannelMessageSend(channelID, content)
} }
if err != nil { if err != nil {
log.Error().Msgf("could not send message: %v", err) beaver.Errorf("could not send message: %v", err)
return nil return nil
} }
return msg return msg
} }
func sendEmbed(s *discordgo.Session, channelID string, embed *discordgo.MessageEmbed) *discordgo.Message { func sendEmbed(s *discordgo.Session, channelID string, embed *discordgo.MessageEmbed) *discordgo.Message {
embed.Color = embedColor
msg, err := s.ChannelMessageSendEmbed(channelID, embed) msg, err := s.ChannelMessageSendEmbed(channelID, embed)
if err != nil { if err != nil {
log.Error().Msgf("could not send embed: %v", err) beaver.Errorf("could not send embed: %v", err)
return nil return nil
} }
return msg return msg
@ -168,10 +144,7 @@ func isStaff(authorRoleIDs, staffRoleIDs []string) bool {
func readyHandler() func(s *discordgo.Session, m *discordgo.Ready) { func readyHandler() func(s *discordgo.Session, m *discordgo.Ready) {
return func(s *discordgo.Session, r *discordgo.Ready) { return func(s *discordgo.Session, r *discordgo.Ready) {
log.Info().Msgf("https://discord.com/api/oauth2/authorize?client_id=%s&permissions=0&redirect_uri=https://birbmc.com&scope=bot", r.User.ID) beaver.Infof("https://discord.com/api/oauth2/authorize?client_id=%s&permissions=0&redirect_uri=https://birbmc.com&scope=bot", r.User.ID)
// Init status changer
go updateStatus(s)
} }
} }
@ -195,7 +168,7 @@ func commandHandler(cfg *config.Config, db *database.Database, sapi *serverapi.C
cmdArg := strings.ToLower(args[0]) cmdArg := strings.ToLower(args[0])
cmd, ok := commandMap[cmdArg] cmd, ok := commands[cmdArg]
if !ok { if !ok {
return return
} }
@ -216,15 +189,10 @@ func commandHandler(cfg *config.Config, db *database.Database, sapi *serverapi.C
sendMessage(s, m.ChannelID, "You cannot run this command.", false) sendMessage(s, m.ChannelID, "You cannot run this command.", false)
return return
} }
if cmd.deleteInvocation {
if err := s.ChannelMessageDelete(m.Message.ChannelID, m.Message.ID); err != nil {
log.Warn().Msgf("could not remove invocation for %s: %v", m.Content, err)
}
}
feedback, err := cmd.run(cmdInit) feedback, err := cmd.run(cmdInit)
if err != nil { if err != nil {
feedback = "Internal error" feedback = "Internal error"
log.Error().Msgf("error while running %s: %v", cmdArg, err) beaver.Errorf("error while running %s: %v", cmdArg, err)
} }
if len(feedback) > 0 { if len(feedback) > 0 {
sendMessage(s, m.ChannelID, feedback, false) sendMessage(s, m.ChannelID, feedback, false)
@ -243,7 +211,7 @@ func messageHandler(cfg *config.Config, db *database.Database) func(s *discordgo
for _, role := range m.MentionRoles { for _, role := range m.MentionRoles {
if cfg.FiredRole == role { if cfg.FiredRole == role {
if err := db.IncrementPing(cfg.FiredRole); err != nil { if err := db.IncrementPing(cfg.FiredRole); err != nil {
log.Error().Msgf("could not increment ping for %s: %v", cfg.FiredRole, err) beaver.Errorf("could not increment ping for %s: %v", cfg.FiredRole, err)
} }
} }
} }
@ -265,14 +233,14 @@ func reactionRemoveHandler() func(s *discordgo.Session, m *discordgo.MessageReac
func reactionHandler(add bool, s *discordgo.Session, m *discordgo.MessageReaction) { func reactionHandler(add bool, s *discordgo.Session, m *discordgo.MessageReaction) {
if _, ok := messageRoleMap[m.MessageID]; ok { if _, ok := messageRoleMap[m.MessageID]; ok {
if r, ok := messageRoleMap[m.MessageID][m.Emoji.APIName()]; ok { if r, ok := messageRoleMap[m.MessageID][m.Emoji.APIName()]; ok {
var roleCmd func(guildID, userID, roleID string, opts ...discordgo.RequestOption) (err error) var roleCmd func(guildID, userID, roleID string) (err error)
if add { if add {
roleCmd = s.GuildMemberRoleAdd roleCmd = s.GuildMemberRoleAdd
} else { } else {
roleCmd = s.GuildMemberRoleRemove roleCmd = s.GuildMemberRoleRemove
} }
if err := roleCmd(m.GuildID, m.UserID, r); err != nil { if err := roleCmd(m.GuildID, m.UserID, r); err != nil {
log.Error().Msgf("could not modify role %s for user %s", r, m.UserID) beaver.Errorf("could not modify role %s for user %s", r, m.UserID)
} }
} }
} }
@ -283,45 +251,3 @@ func leaveHandler(cfg *config.Config) func(s *discordgo.Session, m *discordgo.Gu
sendMessage(s, cfg.LeaveChannel, fmt.Sprintf("%s (%s) left the server. :sob:", m.Mention(), m.User.String()), true) sendMessage(s, cfg.LeaveChannel, fmt.Sprintf("%s (%s) left the server. :sob:", m.Mention(), m.User.String()), true)
} }
} }
func updateStatus(s *discordgo.Session) {
ticker := time.NewTicker(time.Minute * 30)
for {
dj, err := newDadJoke()
if err != nil {
log.Warn().Msgf("could not get new dad joke: %v", err)
} else if err := s.UpdateGameStatus(0, dj.Joke); err != nil {
log.Warn().Msgf("could not update status: %v", err)
}
<-ticker.C
}
}
func joinHandler(cfg *config.Config) func(s *discordgo.Session, m *discordgo.GuildMemberAdd) {
return func(s *discordgo.Session, m *discordgo.GuildMemberAdd) {
sendMessage(s, cfg.Welcome.Channel, os.Expand(cfg.Welcome.Message, func(s string) string {
switch strings.ToLower(s) {
case "user":
return m.Mention()
default:
return s
}
}), false)
if cfg.Welcome.DM != "" {
dm, err := s.UserChannelCreate(m.User.ID)
if err != nil {
log.Err(err).Msgf("could not create DM with %s", m.User.Username)
return
}
sendMessage(s, dm.ID, os.Expand(cfg.Welcome.DM, func(s string) string {
switch strings.ToLower(s) {
case "user":
return m.Mention()
default:
return s
}
}), false)
}
}
}

View File

@ -4,59 +4,45 @@ import (
"fmt" "fmt"
"strings" "strings"
"git.jojodev.com/Minecraft/canopeas/config" "go.etztech.xyz/sedbot/config"
"github.com/bwmarrin/discordgo"
) )
func Echo(cfg *config.Config) { func Echo(cfg *config.Config) {
for _, echo := range cfg.Echoes { echoes := make([]string, 0)
e := echo // Closure for _, e := range cfg.Echoes {
commands = append(commands, &command{ echo := e
name: e.Name, commands[echo.Name] = command{
aliases: e.Aliases,
child: true,
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
run: func(cmd commandInit) (string, error) { run: func(cmd commandInit) (string, error) {
return e.Message, nil return echo.Message, nil
}, },
help: e.Help, help: echo.Help,
})
} }
for _, a := range echo.Aliases {
commands = append(commands, &command{ alias := a
deleteInvocation: true, commands[alias] = command{
name: "echoes",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
run: func(cmd commandInit) (string, error) { run: func(cmd commandInit) (string, error) {
embed := &discordgo.MessageEmbed{ return echo.Message, nil
Title: "Echo Commands", },
Fields: make([]*discordgo.MessageEmbedField, len(cfg.Echoes)), help: echo.Help,
} }
for i, echo := range cfg.Echoes {
name := echo.Name
if len(echo.Aliases) > 0 {
name += fmt.Sprintf(" (%s)", strings.Join(echo.Aliases, ", "))
}
embed.Fields[i] = &discordgo.MessageEmbedField{
Name: name,
Value: echo.Help,
Inline: true,
} }
combined := append([]string{echo.Name}, echo.Aliases...)
echoes = append(echoes, fmt.Sprintf("**%s**: %s", strings.Join(combined, ", "), echo.Help))
} }
channel, err := cmd.session.UserChannelCreate(cmd.message.Author.ID) commands["echoes"] = command{
if err != nil { validate: func(cmd commandInit) bool {
return "", err return true
} },
run: func(cmd commandInit) (string, error) {
sendEmbed(cmd.session, channel.ID, embed) return strings.Join(echoes, "\n"), nil
return "", nil
}, },
help: "Get all dynamic messages", help: "Get all dynamic messages",
}) }
} }

View File

@ -5,8 +5,7 @@ import (
) )
func init() { func init() {
commands = append(commands, &command{ commands["fired"] = command{
name: "fired",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
@ -15,5 +14,5 @@ func init() {
cmd.database.CheckPing(cmd.config.FiredRole)), nil cmd.database.CheckPing(cmd.config.FiredRole)), nil
}, },
help: "Check how many times Carolyn has been fired.", help: "Check how many times Carolyn has been fired.",
}) }
} }

View File

@ -1,16 +1,13 @@
package discord package discord
import ( import (
"fmt"
"strings" "strings"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
) )
func init() { func init() {
commands = append(commands, &command{ commands["help"] = command{
deleteInvocation: true,
name: "help",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
@ -33,15 +30,14 @@ func init() {
return "", nil return "", nil
}, },
help: "HELP! HEEEEEEEEEELP!", help: "HELP! HEEEEEEEEEELP!",
}) }
} }
func singleHelp(cmd commandInit, arg string) *discordgo.MessageEmbed { func singleHelp(cmd commandInit, arg string) *discordgo.MessageEmbed {
embed := &discordgo.MessageEmbed{ embed := &discordgo.MessageEmbed{
Title: "Unkown Command", Title: "Unkown Command",
Color: 0x007D96,
} }
c, ok := commandMap[arg] c, ok := commands[arg]
if !ok { if !ok {
return embed return embed
} }
@ -50,7 +46,7 @@ func singleHelp(cmd commandInit, arg string) *discordgo.MessageEmbed {
return embed return embed
} }
embed.Title = c.name embed.Title = arg
embed.Description = c.help embed.Description = c.help
return embed return embed
} }
@ -64,17 +60,10 @@ func allHelp(cmd commandInit) *discordgo.MessageEmbed {
if staff { if staff {
embed.Description = "Commands with an asterisk (*) are staff-only" embed.Description = "Commands with an asterisk (*) are staff-only"
} }
for _, c := range commands { for n, c := range commands {
if c.child { cmdName := n
continue
}
cmdName := c.name
if len(c.aliases) > 0 {
cmdName += fmt.Sprintf(" (%s)", strings.Join(c.aliases, ", "))
}
if c.staffOnly { if c.staffOnly {
cmdName = fmt.Sprintf("*%s", cmdName) cmdName += "*"
} }
if !c.staffOnly || staff { if !c.staffOnly || staff {
embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{ embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{

View File

@ -5,17 +5,14 @@ import (
"strings" "strings"
"time" "time"
"github.com/rs/zerolog/log"
"gitea.com/jolheiser/gojang"
"gitea.com/jolheiser/gojang/rate"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
"go.jolheiser.com/beaver"
"go.jolheiser.com/gojang"
"go.jolheiser.com/gojang/rate"
) )
func init() { func init() {
commands = append(commands, &command{ cmd := command{
name: "history",
aliases: []string{"names"},
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
@ -45,13 +42,13 @@ func init() {
if rate.IsRateLimitExceededError(err) { if rate.IsRateLimitExceededError(err) {
return "Rate limited by Mojang, slow down!", nil return "Rate limited by Mojang, slow down!", nil
} }
log.Error().Msgf("Profile: %v", err) beaver.Errorf("Profile: %v", err)
return "Could not contact the Mojang API.", nil return "Could not contact the Mojang API.", nil
} }
names, err := client.UUIDToNameHistory(profile.UUID) names, err := client.UUIDToNameHistory(profile.UUID)
if err != nil { if err != nil {
log.Error().Msgf("UUIDToNameHistory: %v", err) beaver.Errorf("UUIDToNameHistory: %v", err)
return "Could not contact the Mojang API.", nil return "Could not contact the Mojang API.", nil
} }
@ -82,5 +79,8 @@ func init() {
return "", nil return "", nil
}, },
help: "Minecraft name history", help: "Minecraft name history",
}) }
commands["history"] = cmd
commands["names"] = cmd
} }

View File

@ -1,71 +0,0 @@
package discord
import (
"fmt"
"strings"
"git.jojodev.com/Minecraft/canopeas/config"
"git.jojodev.com/Minecraft/canopeas/imgur"
"github.com/bwmarrin/discordgo"
)
func Album(cfg *config.Config) error {
for _, a := range cfg.Albums {
images, err := imgur.Get(cfg.ImgurClientID, a.AlbumID)
if err != nil {
return err
}
commands = append(commands, &command{
name: a.Name,
aliases: a.Aliases,
child: true,
validate: func(cmd commandInit) bool {
return true
},
run: func(cmd commandInit) (string, error) {
if !memeRateLimit.Try() {
return "", nil
}
img := images[rand.Intn(len(images))-1]
return img.Link, nil
},
help: a.Help,
})
}
commands = append(commands, &command{
deleteInvocation: true,
name: "albums",
validate: func(cmd commandInit) bool {
return true
},
run: func(cmd commandInit) (string, error) {
embed := &discordgo.MessageEmbed{
Title: "Album Commands",
Fields: make([]*discordgo.MessageEmbedField, len(cfg.Albums)),
}
for i, a := range cfg.Albums {
name := a.Name
if len(a.Aliases) > 0 {
name += fmt.Sprintf(" (%s)", strings.Join(a.Aliases, ", "))
}
embed.Fields[i] = &discordgo.MessageEmbedField{
Name: name,
Value: a.Help,
Inline: true,
}
}
channel, err := cmd.session.UserChannelCreate(cmd.message.Author.ID)
if err != nil {
return "", err
}
sendEmbed(cmd.session, channel.ID, embed)
return "", nil
},
help: "Get all imgur albums",
})
return nil
}

View File

@ -1,10 +1,9 @@
package discord package discord
import "git.jojodev.com/Minecraft/canopeas/inspiro" import "go.etztech.xyz/inspiro"
func init() { func init() {
commands = append(commands, &command{ commands["inspire"] = command{
name: "inspire",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
@ -23,5 +22,5 @@ func init() {
return img, nil return img, nil
}, },
help: "Get inspired!", help: "Get inspired!",
}) }
} }

View File

@ -6,8 +6,7 @@ import (
) )
func init() { func init() {
commands = append(commands, &command{ commands["insult"] = command{
name: "insult",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
@ -40,5 +39,5 @@ func init() {
return "", nil return "", nil
}, },
help: "Insult someone!", help: "Insult someone!",
}) }
} }

23
discord/jupiter.go 100644
View File

@ -0,0 +1,23 @@
package discord
import "go.etztech.xyz/sedbot/imgur"
func init() {
cmd := command{
validate: func(cmd commandInit) bool {
return true
},
run: func(cmd commandInit) (string, error) {
if !memeRateLimit.Try() {
return "", nil
}
img := imgur.Images[rand.Intn(len(imgur.Images))-1]
return img.Link, nil
},
help: "Get a Jupiter image",
}
commands["jupiter"] = cmd
commands["jup"] = cmd
commands["jupjup"] = cmd
}

View File

@ -7,17 +7,16 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"git.jojodev.com/Minecraft/go-mcm" "go.etztech.xyz/sedbot/config"
"git.jojodev.com/Minecraft/go-mcm/model/django"
"git.jojodev.com/Minecraft/canopeas/config" "go.etztech.xyz/go-mcm"
"go.etztech.xyz/go-mcm/model/django"
) )
const bannedPlayersFile = "banned-players.json" const bannedPlayersFile = "banned-players.json"
func init() { func init() {
commands = append(commands, &command{ commands["register"] = command{
name: "register",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return len(cmd.message.Member.Roles) == 0 return len(cmd.message.Member.Roles) == 0
}, },
@ -67,49 +66,35 @@ func init() {
return "No application found for that player", nil return "No application found for that player", nil
} }
} }
} else if len(apps) > 0 {
if apps[0].Accepted != nil && *apps[0].Accepted {
return "Please join the server and then re-try this command", nil
} }
if len(apps) == 0 { } else {
return "No player or applications found for that username", nil return "No player or applications found for that username", nil
} }
accepted := apps[0].Accepted accepted := apps[0].Accepted
if accepted == nil { if accepted == nil {
return "Your application is still being reviewed, hang tight", nil return "Your application is still being reviewed, hang tight", nil
} else if !*accepted { } else if !*accepted {
return "Your application was denied, good luck finding a new server", nil return "Your application was denied, good luck finding a new server", nil
} }
if nickname != "" { // Accepted
// Accepted, check for dupe user
guild, err := cmd.session.State.Guild(cmd.message.GuildID)
if err != nil {
return "", err
}
for _, member := range guild.Members {
nick := member.Nick
if nick == "" {
nick = member.User.Username
}
if strings.EqualFold(nickname, nick) && cmd.message.Author.ID != member.User.ID {
return "A member with that name already exists in this Discord. Please contact staff.", nil
}
}
if err := cmd.session.GuildMemberNickname(cmd.message.GuildID, cmd.message.Author.ID, nickname); err != nil { if err := cmd.session.GuildMemberNickname(cmd.message.GuildID, cmd.message.Author.ID, nickname); err != nil {
return "", err return "", err
} }
if err := cmd.session.GuildMemberRoleAdd(cmd.message.GuildID, cmd.message.Author.ID, cmd.config.RegisterRole); err != nil {
}
if err := cmd.session.GuildMemberRoleAdd(cmd.message.GuildID, cmd.message.Author.ID, cmd.config.Register.Role); err != nil {
return "", err return "", err
} }
// Don't return feedback because this goes in a different channel // Don't return feedback because this goes in a different channel
sendMessage(cmd.session, cmd.config.Register.WelcomeChannel, fmt.Sprintf("Welcome, **%s**!", cmd.message.Author.Mention()), false) sendMessage(cmd.session, cmd.config.RegisteredChannel, fmt.Sprintf("Welcome, **%s**!", cmd.message.Author.Mention()), false)
return "", nil return "", nil
}, },
help: "Register yourself with the Discord", help: "Register yourself with the Discord",
}) }
} }
type Ban struct { type Ban struct {

View File

@ -4,26 +4,25 @@ import (
"fmt" "fmt"
"strings" "strings"
"gitea.com/jolheiser/gojang/query"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
"go.jolheiser.com/gojang/query"
) )
func init() { func init() {
commands = append(commands, &command{ cmd := command{
name: "status",
aliases: []string{"version", "online"},
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
run: func(cmd commandInit) (string, error) { run: func(cmd commandInit) (string, error) {
server := query.NewServer(cmd.config.Server.Address, cmd.config.Server.Port) server := query.NewServer(cmd.config.Server.Address, cmd.config.Server.Port)
q, err := server.Query(query.DefaultTimeout, query.DefaultDeadline) q, err := server.Query(query.DefaultTimeout)
if err != nil { if err != nil {
return "", err return "", err
} }
embed := &discordgo.MessageEmbed{ embed := &discordgo.MessageEmbed{
Color: 0x007D96,
Title: fmt.Sprintf("Server Status for `%s`", cmd.config.Server.Address), Title: fmt.Sprintf("Server Status for `%s`", cmd.config.Server.Address),
Description: q.MOTD, Description: q.MOTD,
Fields: []*discordgo.MessageEmbedField{ Fields: []*discordgo.MessageEmbedField{
@ -47,5 +46,9 @@ func init() {
return "", nil return "", nil
}, },
help: "Get the server status", help: "Get the server status",
}) }
commands["status"] = cmd
commands["version"] = cmd
commands["online"] = cmd
} }

View File

@ -6,9 +6,8 @@ import (
) )
func init() { func init() {
commands = append(commands, &command{ commands["tweet"] = command{
staffOnly: true, staffOnly: true,
name: "tweet",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles) return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles)
}, },
@ -25,10 +24,11 @@ func init() {
return "", err return "", err
} }
if resp.StatusCode%100 != 2 { if resp.StatusCode%100 != 2 {
} }
return fmt.Sprintf("https://twitter.com/%d/status/%d", tweet.User.ID, tweet.ID), nil return fmt.Sprintf("https://twitter.com/%d/status/%d", tweet.User.ID, tweet.ID), nil
}, },
help: "Send a tweet from the BirbMC Twitter", help: "Send a tweet from the BirbMC Twitter",
}) }
} }

View File

@ -6,13 +6,12 @@ import (
"strings" "strings"
"time" "time"
"git.jojodev.com/Minecraft/canopeas/database" "go.etztech.xyz/sedbot/database"
) )
func init() { func init() {
commands = append(commands, &command{ commands["unban"] = command{
staffOnly: true, staffOnly: true,
name: "unban",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles) return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles)
}, },
@ -37,5 +36,5 @@ func init() {
cmd.database.AddUnban(record) cmd.database.AddUnban(record)
}, },
help: "Unban a player", help: "Unban a player",
}) }
} }

View File

@ -5,9 +5,8 @@ import (
) )
func init() { func init() {
commands = append(commands, &command{ commands["unbans"] = command{
staffOnly: true, staffOnly: true,
name: "unbans",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles) return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles)
}, },
@ -31,6 +30,6 @@ func init() {
sendEmbed(cmd.session, cmd.message.ChannelID, embed) sendEmbed(cmd.session, cmd.message.ChannelID, embed)
return "", nil return "", nil
}, },
help: "Check the unban scheduler", help: "Unban a player",
}) }
} }

View File

@ -5,11 +5,10 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/rs/zerolog/log" "go.etztech.xyz/sedbot/database"
"git.jojodev.com/Minecraft/go-serverapi" "go.etztech.xyz/go-serverapi"
"go.jolheiser.com/beaver"
"git.jojodev.com/Minecraft/canopeas/database"
) )
type rateLimit struct { type rateLimit struct {
@ -61,23 +60,23 @@ func (u *unbanSchedule) Run() {
} }
func (u *unbanSchedule) check() { func (u *unbanSchedule) check() {
log.Debug().Msg("Running unban schedule") beaver.Debug("Running unban schedule")
now := time.Now() now := time.Now()
for _, record := range u.db.ListUnbans() { for _, record := range u.db.ListUnbans() {
if now.After(record.Expiration) { if now.After(record.Expiration) {
log.Info().Msgf("Unbanning %s", record.Username) beaver.Infof("Unbanning %s", record.Username)
unban := serverapi.Unban{ unban := serverapi.Unban{
Target: record.Username, Target: record.Username,
} }
status, err := u.sapi.Unban(unban) status, err := u.sapi.Unban(unban)
if err != nil { if err != nil {
log.Err(err).Msg("") beaver.Error(err)
} }
if status != http.StatusOK { if status != http.StatusOK {
log.Error().Msgf("ServerAPI returned status %d when trying to ban %s", status, record.Username) beaver.Errorf("ServerAPI returned status %d when trying to ban %s", status, record.Username)
} }
if err := u.db.RemoveUnban(record.Username); err != nil { if err := u.db.RemoveUnban(record.Username); err != nil {
log.Error().Msgf("could not remove unban for %s in database", record.Username) beaver.Errorf("could not remove unban for %s in database", record.Username)
} }
} }
} }

View File

@ -11,7 +11,7 @@ func TestRateLimit(t *testing.T) {
} }
now() now()
limit := NewRateLimit(time.Second) limit := NewRateLimit(time.Second * 2)
if ok := limit.Try(); !ok { if ok := limit.Try(); !ok {
now() now()
@ -25,7 +25,7 @@ func TestRateLimit(t *testing.T) {
t.FailNow() t.FailNow()
} }
time.Sleep(time.Millisecond * 500) time.Sleep(time.Second)
if ok := limit.Try(); ok { if ok := limit.Try(); ok {
now() now()
@ -33,7 +33,7 @@ func TestRateLimit(t *testing.T) {
t.FailNow() t.FailNow()
} }
time.Sleep(time.Millisecond * 500) time.Sleep(time.Second)
if ok := limit.Try(); !ok { if ok := limit.Try(); !ok {
now() now()

View File

@ -7,13 +7,15 @@ import (
) )
func init() { func init() {
commands = append(commands, &command{ commands["welcome"] = command{
deleteInvocation: true,
name: "welcome",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles) return isStaff(cmd.message.Member.Roles, cmd.config.StaffRoles)
}, },
run: func(cmd commandInit) (string, error) { run: func(cmd commandInit) (string, error) {
if err := cmd.session.ChannelMessageDelete(cmd.message.ChannelID, cmd.message.ID); err != nil {
return "", err
}
orphans := make([]*discordgo.Member, 0) orphans := make([]*discordgo.Member, 0)
members, err := cmd.session.GuildMembers(cmd.message.GuildID, "", 1000) members, err := cmd.session.GuildMembers(cmd.message.GuildID, "", 1000)
@ -47,5 +49,5 @@ func init() {
return "", nil return "", nil
}, },
help: "Get a list of people with no roles", help: "Get a list of people with no roles",
}) }
} }

View File

@ -2,17 +2,15 @@ package discord
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"gitea.com/jolheiser/xkcd" "go.jolheiser.com/xkcd"
) )
func init() { func init() {
commands = append(commands, &command{ commands["xkcd"] = command{
name: "xkcd",
validate: func(cmd commandInit) bool { validate: func(cmd commandInit) bool {
return true return true
}, },
@ -31,7 +29,7 @@ func init() {
} else { } else {
comicNum, err := strconv.Atoi(args[1]) comicNum, err := strconv.Atoi(args[1])
if err != nil { if err != nil {
return "", errors.New("this command only accepts a number or no arguments") return "", err
} }
comic, err = client.Comic(context.Background(), comicNum) comic, err = client.Comic(context.Background(), comicNum)
} }
@ -42,5 +40,5 @@ func init() {
return fmt.Sprintf("%d: %s\n%s\n%s", comic.Num, comic.SafeTitle, comic.Alt, comic.Img), nil return fmt.Sprintf("%d: %s\n%s\n%s", comic.Num, comic.SafeTitle, comic.Alt, comic.Img), nil
}, },
help: "Get an xkcd comic", help: "Get an xkcd comic",
}) }
} }

View File

@ -1,29 +0,0 @@
package falseknees
import "net/http"
// Client is a FalseKnees client
type Client struct {
http *http.Client
}
// New returns a new Client
func New(opts ...ClientOption) *Client {
c := &Client{
http: http.DefaultClient,
}
for _, opt := range opts {
opt(c)
}
return c
}
// ClientOption is options for a Client
type ClientOption func(*Client)
// WithHTTP is a ClientOption for using a different http.Client
func WithHTTP(client *http.Client) ClientOption {
return func(c *Client) {
c.http = client
}
}

View File

@ -1,130 +0,0 @@
package falseknees
import (
"context"
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"regexp"
"strconv"
"time"
)
var (
updateInterval = time.Minute * 30
baseURL = "https://www.falseknees.com/"
currentRe = regexp.MustCompile(`window\.location\.href.+"(.+)\.html"`)
imageRe = regexp.MustCompile(`src="(imgs.+\.png)".+title="(.+)"`)
current int
lastUpdate time.Time
)
// Comic is a FalseKnees comic
type Comic struct {
Num int
Title string
Img string
}
// Comic returns a specific Comic
func (c *Client) Comic(ctx context.Context, num int) (*Comic, error) {
return c.comic(ctx, num)
}
// Current returns the current Comic
func (c *Client) Current(ctx context.Context) (*Comic, error) {
if err := c.updateCurrent(ctx); err != nil {
return nil, err
}
return c.Comic(ctx, current)
}
// Random returns a random Comic
func (c *Client) Random(ctx context.Context) (*Comic, error) {
if err := c.updateCurrent(ctx); err != nil {
return nil, err
}
rand.Seed(time.Now().UnixNano())
return c.Comic(ctx, rand.Intn(current)+1)
}
func (c *Client) updateCurrent(ctx context.Context) error {
now := time.Now()
if !lastUpdate.IsZero() && lastUpdate.After(now.Add(-updateInterval)) {
return nil
}
lastUpdate = now
u := fmt.Sprintf("%sindex.html", baseURL)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
if err != nil {
return err
}
resp, err := c.http.Do(req)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("could not get page for index: %s", resp.Status)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
defer resp.Body.Close()
match := currentRe.FindStringSubmatch(string(body))
if len(match) == 0 {
return errors.New("could not find current comic")
}
curr, err := strconv.Atoi(match[1])
if err != nil {
return err
}
current = curr
return nil
}
func (c *Client) comic(ctx context.Context, num int) (*Comic, error) {
u := fmt.Sprintf("%s%d.html", baseURL, num)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
if err != nil {
return nil, err
}
resp, err := c.http.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("could not get page for comic %d: %s", num, resp.Status)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
defer resp.Body.Close()
match := imageRe.FindStringSubmatch(string(body))
if len(match) == 0 {
return nil, fmt.Errorf("could not find comic #%d", num)
}
return &Comic{
Num: num,
Title: match[2],
Img: fmt.Sprintf("%s%s", baseURL, match[1]),
}, nil
}

View File

@ -1,483 +0,0 @@
package falseknees
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
)
var (
server *httptest.Server
client *Client
)
func TestMain(m *testing.M) {
handler := func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/index.html" {
_, _ = w.Write(indexHTML)
return
}
if r.URL.Path == "/389.html" {
_, _ = w.Write(currentHTML)
return
}
if r.URL.Path == "/252.html" {
_, _ = w.Write(bunnyHTML)
return
}
w.WriteHeader(http.StatusNotFound)
}
server = httptest.NewServer(http.HandlerFunc(handler))
baseURL = server.URL + "/"
currentComic.Img = fmt.Sprintf("%simgs/389.png", baseURL)
bunnyComic.Img = fmt.Sprintf("%simgs/252.png", baseURL)
client = New()
os.Exit(m.Run())
}
func TestCurrent(t *testing.T) {
t.Parallel()
comic, err := client.Current(context.Background())
if err != nil {
t.Logf("could not get current comic: %v\n", err)
t.FailNow()
}
if *comic != currentComic {
t.Log("comic does not match test data")
t.FailNow()
}
}
func TestComic(t *testing.T) {
t.Parallel()
comic, err := client.Comic(context.Background(), 252)
if err != nil {
t.Logf("could not get comic 252: %v\n", err)
t.FailNow()
}
if *comic != bunnyComic {
t.Log("comic does not match test data")
t.FailNow()
}
}
var (
currentComic = Comic{
Num: 389,
Title: "that's the good stuff",
}
bunnyComic = Comic{
Num: 252,
Title: "Spring is the fucking greatest shit",
}
indexHTML = []byte(`<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="description"
content="False Knees is a webcomic written by Joshua Barkman. All silly nonsense is my own." />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="refresh" content="0; URL=389.html" />
<script type="text/javascript">
window.location.href = "389.html"
</script>
<!-- Favicon -->
<link rel="icon" type="image/png" href="imgs/favicon.png" sizes="96x96">
<!-- Facebook Meta tags -->
<meta property="og:title" content="False Knees" />
<meta property="og:type" content="blog" />
<meta property="og:url" content="http://www.falseknees.com/index.html" />
<meta property="og:image" content="http://www.falseknees.com/imgs/389.png" />
<meta property="og:site_name" content="False Knees" />
<meta property="fb:admins" content="1646220005" />
<link rel="image_src" href="imgs/389.png" />
<link type="text/css" rel="stylesheet" href="stylesheet.css" />
<title>Page Redirection</title>
<!-- Google Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
var pluginUrl =
'//www.google-analytics.com/plugins/ga/inpage_linkid.js';
_gaq.push(['_require', 'inpage_linkid', pluginUrl]);
_gaq.push(['_setAccount', 'UA-37345913-1']);
_gaq.push(['_setDomainName', 'falseknees.com']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<div id="fb-root"></div>
<script>
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.0";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
<!-- Title Image -->
<table align="center">
<tr>
<td>
<div id="title">
<a href="index.html"><img src="imgs/falseknees.png" width="800" /></a>
</div>
</td>
</tr>
</table>
<!-- New Button Placement -->
<div align="center">
<p>
<table>
<tr>
<td><a href="about.html"
title=""><img src="imgs/aboutoff.png" height="50" alt="About" onmouseover="this.src='imgs/abouton.png'" onmouseout="this.src='imgs/aboutoff.png'" /></a>
</td>
<td><a title=""><img src="imgs/stara.png" height="50" alt="" /></a></td>
<td><a href="https://false-knees.myshopify.com/"
title=""><img src="imgs/store.png" height="50" alt="Store" onmouseover="this.src='imgs/storeon.png'" onmouseout="this.src='imgs/store.png'" /></a>
</td>
<td><a title=""><img src="imgs/starb.png" height="50" alt="" /></a></td>
<td><a href="https://www.patreon.com/falseknees?ty=h"><img src="imgs/patron.png" height="50" onmouseover="this.src='imgs/patroff.png'" onmouseout="this.src='imgs/patron.png'"/></a>
</td>
<td><a title=""><img src="imgs/starc.png" height="50" alt="" /></a></td>
<td><a href="book.html"
title=""><img src="imgs/book.png" height="40" alt="About" onmouseover="this.src='imgs/bookon.png'" onmouseout="this.src='imgs/book.png'" /></a>
</td>
</tr>
</table>
</p>
</div>
<!-- Comic -->
<div>
<img src="imgs/389.png" width="600" title="that's the good stuff" />
</div>
<!-- Descriptive Text Box -->
<!-- Descriptive Text Box -->
<div align="center">
</div>
<!-- Navigation Buttons -->
<div align="center">
<p>
<table>
<tr>
<td><a href="1.html"
title=""><img src="imgs/first.png" height="60" alt="First" onmouseover="this.src='imgs/firston.png'" onmouseout="this.src='imgs/first.png'" /></a>
</td>
<td><a href="388.html"
title=""><img src="imgs/previous.png" height="60" alt="Previous" onmouseover="this.src='imgs/previouson.png'" onmouseout="this.src='imgs/previous.png'" /></a>
</td>
<td><a href="archive.html"
title=""><img src="imgs/archive.png" height="60" alt="Archive" onmouseover="this.src='imgs/archive2.png'" onmouseout="this.src='imgs/archive.png'" /></a>
</td>
<td><a href="index.html"
title=""><img src="imgs/next.png" height="60" alt="Next" onmouseover="this.src='imgs/nexton.png'" onmouseout="this.src='imgs/next.png'" /></a>
</td>
<td><a href="index.html"
title=""><img src="imgs/last.png" height="60" alt="Last" onmouseover="this.src='imgs/laston.png'" onmouseout="this.src='imgs/last.png'" /></a>
</td>
</tr>
</table>
</p>
</div>
<!-- Social Media -->
<table class="social" align="center">
<tr>
<td><a href="http://falseknees.tumblr.com/"><img src="imgs/TumblrButton.png" width="60" onmouseover="this.src='imgs/TumblrButtonOn.png'" onmouseout="this.src='imgs/TumblrButton.png'"/></a>
</td>
<td><a href="https://instagram.com/FalseKnees"><img src="imgs/instagram.png" width="60" onmouseover="this.src='imgs/instagramon.png'" onmouseout="this.src='imgs/instagram.png'"/></a>
</td>
<td><a href="https://www.facebook.com/FalseKnees?ref=hl"><img src="imgs/FacebookButton.png" width="60" onmouseover="this.src='imgs/FacebookButtonOn.png'" onmouseout="this.src='imgs/FacebookButton.png'"/></a>
</td>
<td><a href="http://www.webtoons.com/en/challenge/false-knees/list?title_no=79544"><img src="imgs/webtooff.png" width="60" onmouseover="this.src='imgs/webtoon.png'" onmouseout="this.src='imgs/webtooff.png'"/></a>
</td>
<td><a href="https://tapas.io/series/FalseKnees"><img src="imgs/tap.png" width="60" onmouseover="this.src='imgs/tapon.png'" onmouseout="this.src='imgs/tap.png'"/></a>
</td>
</tr>
</table>
<!-- Copyright -->
<div>
<p>False Knees &copy 2013-whenever Joshua Barkman</p>
</div>
</body>
</html>`)
currentHTML = []byte(`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="description" content="False Knees is a webcomic written by Joshua Barkman. All silly nonsense is my own." />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- Favicon -->
<link rel="icon" type="image/png" href="imgs/favicon.png" sizes="96x96">
<!-- Facebook Meta tags -->
<meta property="og:title" content="False Knees" />
<meta property="og:type" content="blog" />
<meta property="og:url" content="http://www.falseknees.com/389.html" />
<meta property="og:image" content="http://www.falseknees.com/imgs/389.png" />
<meta property="og:site_name" content="False Knees" />
<meta property="fb:admins" content="1646220005" />
<link rel="image_src" href="imgs/389.png" />
<link type="text/css" rel="stylesheet" href="stylesheet.css" />
<title>False Knees</title>
<!-- Google Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
var pluginUrl =
'//www.google-analytics.com/plugins/ga/inpage_linkid.js';
_gaq.push(['_require', 'inpage_linkid', pluginUrl]);
_gaq.push(['_setAccount', 'UA-37345913-1']);
_gaq.push(['_setDomainName', 'falseknees.com']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<div id="fb-root"></div>
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.0";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<!-- Title Image -->
<table align="center">
<tr>
<td><div id="title">
<a href="index.html"><img src="imgs/falseknees.png" width="800" /></a>
</div></td>
</tr>
</table>
<!-- New Button Placement -->
<div align="center">
<p>
<table>
<tr>
<td><a href="about.html" title=""><img src="imgs/aboutoff.png" height="50" alt="About" onmouseover="this.src='imgs/abouton.png'" onmouseout="this.src='imgs/aboutoff.png'" /></a></td>
<td><a title=""><img src="imgs/stara.png" height="50" alt="" /></a></td>
<td><a href="https://false-knees.myshopify.com/" title=""><img src="imgs/store.png" height="50" alt="Store" onmouseover="this.src='imgs/storeon.png'" onmouseout="this.src='imgs/store.png'" /></a></td>
<td><a title=""><img src="imgs/starb.png" height="50" alt="" /></a></td>
<td><a href="https://www.patreon.com/falseknees?ty=h"><img src="imgs/patron.png" height="50" onmouseover="this.src='imgs/patroff.png'" onmouseout="this.src='imgs/patron.png'"/></a></td>
<td><a title=""><img src="imgs/starc.png" height="50" alt="" /></a></td>
<td><a href="book.html" title=""><img src="imgs/book.png" height="40" alt="About" onmouseover="this.src='imgs/bookon.png'" onmouseout="this.src='imgs/book.png'" /></a></td>
</tr>
</table></p>
</div>
<!-- Comic -->
<div>
<img src="imgs/389.png" width="600" title="that's the good stuff" />
</div>
<!-- Descriptive Text Box -->
<!-- Descriptive Text Box -->
<div align="center">
</div>
<!-- Navigation Buttons -->
<div align="center">
<p>
<table>
<tr>
<td><a href="1.html" title=""><img src="imgs/first.png" height="60" alt="First" onmouseover="this.src='imgs/firston.png'" onmouseout="this.src='imgs/first.png'" /></a></td>
<td><a href="388.html" title=""><img src="imgs/previous.png" height="60" alt="Previous" onmouseover="this.src='imgs/previouson.png'" onmouseout="this.src='imgs/previous.png'" /></a></td>
<td><a href="archive.html" title=""><img src="imgs/archive.png" height="60" alt="Archive" onmouseover="this.src='imgs/archive2.png'" onmouseout="this.src='imgs/archive.png'" /></a></td>
<td><a href="389.html" title=""><img src="imgs/next.png" height="60" alt="Next" onmouseover="this.src='imgs/nexton.png'" onmouseout="this.src='imgs/next.png'" /></a></td>
<td><a href="index.html" title=""><img src="imgs/last.png" height="60" alt="Last" onmouseover="this.src='imgs/laston.png'" onmouseout="this.src='imgs/last.png'" /></a></td>
</tr>
</table></p>
</div>
<!-- Social Media -->
<table class="social" align="center">
<tr>
<td><a href="http://falseknees.tumblr.com/"><img src="imgs/TumblrButton.png" width="60" onmouseover="this.src='imgs/TumblrButtonOn.png'" onmouseout="this.src='imgs/TumblrButton.png'"/></a>
</td>
<td><a href="https://instagram.com/FalseKnees"><img src="imgs/instagram.png" width="60" onmouseover="this.src='imgs/instagramon.png'" onmouseout="this.src='imgs/instagram.png'"/></a>
</td>
<td><a href="https://www.facebook.com/FalseKnees?ref=hl"><img src="imgs/FacebookButton.png" width="60" onmouseover="this.src='imgs/FacebookButtonOn.png'" onmouseout="this.src='imgs/FacebookButton.png'"/></a>
</td>
<td><a href="http://www.webtoons.com/en/challenge/false-knees/list?title_no=79544"><img src="imgs/webtooff.png" width="60" onmouseover="this.src='imgs/webtoon.png'" onmouseout="this.src='imgs/webtooff.png'"/></a>
</td>
<td><a href="https://tapas.io/series/FalseKnees"><img src="imgs/tap.png" width="60" onmouseover="this.src='imgs/tapon.png'" onmouseout="this.src='imgs/tap.png'"/></a>
</td>
</tr>
</table>
<!-- Copyright -->
<div>
<p>False Knees &copy 2013-whenever Joshua Barkman</p>
</div>
</body>
</html>
`)
bunnyHTML = []byte(`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="description" content="False Knees is a webcomic written by Joshua Barkman. All silly nonsense is my own." />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- Favicon -->
<link rel="icon" type="image/png" href="imgs/favicon.png" sizes="96x96">
<!-- Facebook Meta tags -->
<meta property="og:title" content="False Knees" />
<meta property="og:type" content="blog" />
<meta property="og:url" content="http://www.falseknees.com/252.html" />
<meta property="og:image" content="http://www.falseknees.com/imgs/252.png" />
<meta property="og:site_name" content="False Knees" />
<meta property="fb:admins" content="1646220005" />
<link rel="image_src" href="imgs/252.png" />
<link type="text/css" rel="stylesheet" href="stylesheet.css" />
<title>False Knees</title>
<!-- Google Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
var pluginUrl =
'//www.google-analytics.com/plugins/ga/inpage_linkid.js';
_gaq.push(['_require', 'inpage_linkid', pluginUrl]);
_gaq.push(['_setAccount', 'UA-37345913-1']);
_gaq.push(['_setDomainName', 'falseknees.com']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<div id="fb-root"></div>
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.0";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<!-- Title Image -->
<table align="center">
<tr>
<td><div id="title">
<a href="index.html"><img src="imgs/falseknees.png" width="800" /></a>
</div></td>
</tr>
</table>
<!-- New Button Placement -->
<div align="center">
<p>
<table>
<tr>
<td><a href="about.html" title=""><img src="imgs/aboutoff.png" height="50" alt="About" onmouseover="this.src='imgs/abouton.png'" onmouseout="this.src='imgs/aboutoff.png'" /></a></td>
<td><a title=""><img src="imgs/stara.png" height="50" alt="" /></a></td>
<td><a href="https://false-knees.myshopify.com/collections/comic-prints" title=""><img src="imgs/store.png" height="50" alt="Store" onmouseover="this.src='imgs/storeon.png'" onmouseout="this.src='imgs/store.png'" /></a></td>
<td><a title=""><img src="imgs/starb.png" height="50" alt="" /></a></td>
<td><a href="https://www.patreon.com/falseknees?ty=h"><img src="imgs/patron.png" height="50" onmouseover="this.src='imgs/patroff.png'" onmouseout="this.src='imgs/patron.png'"/></a></td>
<td><a title=""><img src="imgs/starc.png" height="50" alt="" /></a></td>
<td><a href="book.html" title=""><img src="imgs/book.png" height="40" alt="About" onmouseover="this.src='imgs/bookon.png'" onmouseout="this.src='imgs/book.png'" /></a></td>
</tr>
</table></p>
</div>
<!-- Comic -->
<div>
<img src="imgs/252.png" width="600" title="Spring is the fucking greatest shit" />
</div>
<!-- Descriptive Text Box -->
<!-- Descriptive Text Box -->
<div align="center">
</div>
<!-- Navigation Buttons -->
<div align="center">
<p>
<table>
<tr>
<td><a href="1.html" title=""><img src="imgs/first.png" height="60" alt="First" onmouseover="this.src='imgs/firston.png'" onmouseout="this.src='imgs/first.png'" /></a></td>
<td><a href="251.html" title=""><img src="imgs/previous.png" height="60" alt="Previous" onmouseover="this.src='imgs/previouson.png'" onmouseout="this.src='imgs/previous.png'" /></a></td>
<td><a href="archive.html" title=""><img src="imgs/archive.png" height="60" alt="Archive" onmouseover="this.src='imgs/archive2.png'" onmouseout="this.src='imgs/archive.png'" /></a></td>
<td><a href="253.html" title=""><img src="imgs/next.png" height="60" alt="Next" onmouseover="this.src='imgs/nexton.png'" onmouseout="this.src='imgs/next.png'" /></a></td>
<td><a href="index.html" title=""><img src="imgs/last.png" height="60" alt="Last" onmouseover="this.src='imgs/laston.png'" onmouseout="this.src='imgs/last.png'" /></a></td>
</tr>
</table></p>
</div>
<!-- Social Media -->
<table class="social" align="center">
<tr>
<td><a href="http://falseknees.tumblr.com/"><img src="imgs/TumblrButton.png" width="60" onmouseover="this.src='imgs/TumblrButtonOn.png'" onmouseout="this.src='imgs/TumblrButton.png'"/></a>
</td>
<td><a href="https://instagram.com/FalseKnees"><img src="imgs/instagram.png" width="60" onmouseover="this.src='imgs/instagramon.png'" onmouseout="this.src='imgs/instagram.png'"/></a>
</td>
<td><a href="https://www.facebook.com/FalseKnees?ref=hl"><img src="imgs/FacebookButton.png" width="60" onmouseover="this.src='imgs/FacebookButtonOn.png'" onmouseout="this.src='imgs/FacebookButton.png'"/></a>
</td>
<td><a href="http://www.webtoons.com/en/challenge/false-knees/list?title_no=79544"><img src="imgs/webtooff.png" width="60" onmouseover="this.src='imgs/webtoon.png'" onmouseout="this.src='imgs/webtooff.png'"/></a>
</td>
<td><a href="https://tapas.io/series/FalseKnees"><img src="imgs/tap.png" width="60" onmouseover="this.src='imgs/tapon.png'" onmouseout="this.src='imgs/tap.png'"/></a>
</td>
</tr>
</table>
<!-- Copyright -->
<div>
<p>False Knees &copy 2013-whenever Joshua Barkman</p>
</div>
</body>
</html>
`)
)

21
go.mod
View File

@ -1,18 +1,21 @@
module git.jojodev.com/Minecraft/canopeas module go.etztech.xyz/sedbot
go 1.16 go 1.16
require ( require (
git.jojodev.com/Minecraft/go-mcm v0.0.1 github.com/bwmarrin/discordgo v0.22.0
git.jojodev.com/Minecraft/go-serverapi v0.0.1
gitea.com/jolheiser/gojang v0.0.7
gitea.com/jolheiser/xkcd v0.0.2
github.com/bwmarrin/discordgo v0.27.1
github.com/dghubble/go-twitter v0.0.0-20201011215211-4b180d0cc78d github.com/dghubble/go-twitter v0.0.0-20201011215211-4b180d0cc78d
github.com/dghubble/oauth1 v0.7.0 github.com/dghubble/oauth1 v0.7.0
github.com/gorilla/websocket v1.5.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect
github.com/pelletier/go-toml v1.8.1 github.com/pelletier/go-toml v1.8.1
github.com/rs/zerolog v1.26.1
go.etcd.io/bbolt v1.3.4 go.etcd.io/bbolt v1.3.4
golang.org/x/crypto v0.8.0 // indirect go.etztech.xyz/falseknees v0.0.1
go.etztech.xyz/go-mcm v1.3.1
go.etztech.xyz/go-serverapi v0.0.3
go.etztech.xyz/inspiro v0.0.0-20200606185551-edfdf9da2359
go.jolheiser.com/beaver v1.0.2
go.jolheiser.com/gojang v0.0.3
go.jolheiser.com/xkcd v0.0.1
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect
golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5 // indirect
) )

89
go.sum
View File

@ -1,16 +1,7 @@
git.jojodev.com/Minecraft/go-mcm v0.0.1 h1:3nfjCz3wA4l44QWYMRC7DLwU4GneWC5e1CRub7Tt1S0= github.com/bwmarrin/discordgo v0.22.0 h1:uBxY1HmlVCsW1IuaPjpCGT6A2DBwRn0nvOguQIxDdFM=
git.jojodev.com/Minecraft/go-mcm v0.0.1/go.mod h1:0VAA1b5ZgM1leeYOqMhBnEfdonZccdMdeQrsF+50s04= github.com/bwmarrin/discordgo v0.22.0/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
git.jojodev.com/Minecraft/go-serverapi v0.0.1 h1:sn594cScthq0W/ntLVhWfMfNcPXG9IyyqMatcEjcYIQ=
git.jojodev.com/Minecraft/go-serverapi v0.0.1/go.mod h1:a3e4OnMNJnd2OzwRG62Fe/k70SelNDoAxYW9SYmkDf4=
gitea.com/jolheiser/gojang v0.0.7 h1:Q4cG7QYiKQsJtUWgXXiolAH9DCLRoaQ4olaO9OV628U=
gitea.com/jolheiser/gojang v0.0.7/go.mod h1:r9kj2wv/21Da7VpWz+qmxLexH85o2BAM4NMxeYgQlcY=
gitea.com/jolheiser/xkcd v0.0.2 h1:HJP83YwSKxSYcoNfpb1ZpAfBvkUAnN+YgeukraXtfrc=
gitea.com/jolheiser/xkcd v0.0.2/go.mod h1:aDa2vX54wLaX8Ra5CGN2GWBX13UWAGJKGGddzHl/hks=
github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY=
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -20,77 +11,45 @@ github.com/dghubble/oauth1 v0.7.0 h1:AlpZdbRiJM4XGHIlQ8BuJ/wlpGwFEJNnB4Mc+78tA/w
github.com/dghubble/oauth1 v0.7.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4Tk1ujxk= github.com/dghubble/oauth1 v0.7.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4Tk1ujxk=
github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU= github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU=
github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY= github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
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/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
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/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etztech.xyz/falseknees v0.0.1 h1:XjlBqMyBUH5b7e/9oedEwZ9pnz3sqMhXQv6vnQocxeM=
go.etztech.xyz/falseknees v0.0.1/go.mod h1:Acn1AwrvAArQEqhMBDlak5BvCZ3jgV8vdL8Pe5ZldRE=
go.etztech.xyz/go-mcm v1.3.1 h1:RLdOQrMgw0eP7bsfRRbXLW8c5/RaJ5Mg7i/ESFaUwvY=
go.etztech.xyz/go-mcm v1.3.1/go.mod h1:Hz2YULB3sN/aQA8cPSm2d6LM3E3qTMspzfRCIAD/1dc=
go.etztech.xyz/go-serverapi v0.0.3 h1:h2Zww0x5E61cH4jXB97x3oUnbryEuLkuq+RTgIi5/U4=
go.etztech.xyz/go-serverapi v0.0.3/go.mod h1:tq4J5zxVnAwzOiv79iLUzpfNAd7IoNirOfb0gt3/IEY=
go.etztech.xyz/inspiro v0.0.0-20200606185551-edfdf9da2359 h1:j/ZeoAj185wHfCSYD52Kt/69i3Bzk1MXN4Qh1yP6+P4=
go.etztech.xyz/inspiro v0.0.0-20200606185551-edfdf9da2359/go.mod h1:+fC1WzJm/oS4UEgqr1jPouWerxBys52lTTDA94/5bf8=
go.jolheiser.com/beaver v1.0.2 h1:KA2D6iO8MQhZi1nZYi/Chak/f1Cxfrs6b1XO623+Khk=
go.jolheiser.com/beaver v1.0.2/go.mod h1:7X4F5+XOGSC3LejTShoBdqtRCnPWcnRgmYGmG3EKW8g=
go.jolheiser.com/gojang v0.0.3 h1:EWDLMo6X3f67DK2p/mSB680H43t8SDrDYNZnXvY7PLg=
go.jolheiser.com/gojang v0.0.3/go.mod h1:hUBULFDoampNM97E1IaYUhkLBJ30sb7iGsoFOdDU76I=
go.jolheiser.com/gql v0.0.1 h1:y3LGHcJUZI9otTCcMn8TVdF3aEzNX0FW6m0YUamlLto= go.jolheiser.com/gql v0.0.1 h1:y3LGHcJUZI9otTCcMn8TVdF3aEzNX0FW6m0YUamlLto=
go.jolheiser.com/gql v0.0.1/go.mod h1:74eYqVRIxsOFxtVl0RYGKNyYQgJYQaxOCgar7LP71Hw= go.jolheiser.com/gql v0.0.1/go.mod h1:74eYqVRIxsOFxtVl0RYGKNyYQgJYQaxOCgar7LP71Hw=
go.jolheiser.com/xkcd v0.0.1 h1:pRNY2BXxUS+NMtKlm/ENumOr7g3k4VWY/QoDri9YSNU=
go.jolheiser.com/xkcd v0.0.1/go.mod h1:AzWPrZToCLfpazsZBkeu/nPuIvurMqfCOptiydz7Dvk=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5 h1:iCaAy5bMeEvwANu3YnJfWwI0kWAGkEa2RXPdweI/ysk=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
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=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -6,6 +6,10 @@ import (
"net/http" "net/http"
) )
// TODO Make a client for this in a separate module
var Images []*Image
type Response struct { type Response struct {
Images []*Image `json:"data"` Images []*Image `json:"data"`
Success bool `json:"success"` Success bool `json:"success"`
@ -29,19 +33,23 @@ type Image struct {
Link string `json:"link"` Link string `json:"link"`
} }
func Get(clientID, albumID string) ([]*Image, error) { func Init(clientID string) error {
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://api.imgur.com/3/album/%s/images", albumID), nil)
req, err := http.NewRequest(http.MethodGet, "https://api.imgur.com/3/album/TaJVIdQ/images", nil)
if err != nil { if err != nil {
return nil, err return err
} }
req.Header.Set("Authorization", fmt.Sprintf("Client-ID %s", clientID)) req.Header.Set("Authorization", fmt.Sprintf("Client-ID %s", clientID))
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return nil, err return err
} }
defer resp.Body.Close()
var imgurResp Response var imgurResp Response
return imgurResp.Images, json.NewDecoder(resp.Body).Decode(&imgurResp) if err := json.NewDecoder(resp.Body).Decode(&imgurResp); err != nil {
return err
}
Images = imgurResp.Images
return resp.Body.Close()
} }

View File

@ -1,23 +0,0 @@
package inspiro
import (
"io/ioutil"
"net/http"
)
const api = "https://inspirobot.me/api?generate=true"
func Generate() (string, error) {
resp, err := http.Get(api)
if err != nil {
return "", err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
defer resp.Body.Close()
return string(body), nil
}

View File

@ -1,17 +0,0 @@
package inspiro
import (
"os"
"testing"
)
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func TestGenerate(t *testing.T) {
if _, err := Generate(); err != nil {
t.Logf("could not generate image: %v", err)
t.Fail()
}
}

33
main.go
View File

@ -6,49 +6,54 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
"github.com/rs/zerolog" "go.etztech.xyz/sedbot/config"
"github.com/rs/zerolog/log" "go.etztech.xyz/sedbot/database"
"go.etztech.xyz/sedbot/discord"
"git.jojodev.com/Minecraft/canopeas/config" "go.jolheiser.com/beaver"
"git.jojodev.com/Minecraft/canopeas/database"
"git.jojodev.com/Minecraft/canopeas/discord"
) )
var configFlag string var configFlag string
func main() { func main() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) flag.StringVar(&configFlag, "config", "sedbot.toml", "Set config path")
flag.StringVar(&configFlag, "config", "canopeas.toml", "Set config path")
flag.Parse() flag.Parse()
beaver.Console.Format = beaver.FormatOptions{
TimePrefix: true,
StackPrefix: true,
StackLimit: 15,
LevelPrefix: true,
LevelColor: true,
}
cfg, err := config.Load(configFlag) cfg, err := config.Load(configFlag)
if err != nil { if err != nil {
log.Fatal().Msgf("could not load config: %v", err) beaver.Fatalf("could not load config: %v", err)
} }
db, err := database.Load(cfg.DBPath) db, err := database.Load(cfg.DBPath)
if err != nil { if err != nil {
log.Fatal().Msgf("could not load database: %v", err) beaver.Fatalf("could not load database: %v", err)
} }
bot, err := discord.Bot(cfg, db) bot, err := discord.Bot(cfg, db)
if err != nil { if err != nil {
log.Fatal().Msgf("could not start Discord bot: %v", err) beaver.Fatalf("could not start Discord bot: %v", err)
} }
go func() { go func() {
if err := bot.Open(); err != nil { if err := bot.Open(); err != nil {
log.Error().Msgf("error running Discord bot: %v", err) beaver.Errorf("error running Discord bot: %v", err)
} }
}() }()
log.Info().Msg("Bot is now running. Press CTRL-C to exit.") beaver.Info("Bot is now running. Press CTRL-C to exit.")
sc := make(chan os.Signal, 1) sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-sc <-sc
if err := bot.Close(); err != nil { if err := bot.Close(); err != nil {
log.Error().Msgf("error closing Discord bot: %v", err) beaver.Errorf("error closing Discord bot: %v", err)
} }
} }