Merge pull request #2 from lyx0/rewrite

This commit is contained in:
lyx0 2022-08-07 15:31:04 +00:00 committed by GitHub
commit 6fed6fc265
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
117 changed files with 559 additions and 3475 deletions

View file

@ -1,25 +0,0 @@
TWITCH_USER=forsen
TWITCH_PASS=oauth:asdhjadsjksajkhdjhksadjkias
BOT_USER_ID=133769
MONGO_URI=mongodb+srv://coolurihere
BOT_USERNAME=forsen
BOT_USER_ID=133769
BOT_PASS=oauth:cool123oauthstringhere
# Administrator is the super user for which all commands should work for.
# Put the name you login with as the ADMIN_LOGIN and the Twitch UserID for that account as the ADMIN_USERID
# If you don't know your Twitch UserID follow this:
# go to https://twitch.tv/nourybot and write "()uid [username]" in the chat and my bot should
# respond with the UserID
ADMIN_LOGIN=pajlada
ADMIN_USERID=11148817
# Create new application here https://dev.twitch.tv/console/apps and
# fill in the Client ID and Client Secret you get.
CLIENT_ID=hashdashjudsadhashd
CLIENT_SECRET=asdoaijsdiouasioudasiuo

3
.gitignore vendored
View file

@ -15,5 +15,4 @@
# vendor/ # vendor/
.env .env
Nourybot Nourybot
nourybot

View file

@ -1,12 +1,11 @@
build: build:
cd cmd && go build -o Nourybot cd cmd/bot && go build -o Nourybot
run: run:
cd cmd && ./Nourybot cd cmd/bot && ./Nourybot
dev: xd:
cd cmd && go build -o Nourybot && ./Nourybot -mode dev cd cmd/bot && go build -o Nourybot && ./Nourybot
prod:
cd cmd && go build -o Nourybot && ./Nourybot -mode production
jq:
cd cmd/bot && go build -o Nourybot && ./Nourybot | jq

View file

@ -1,78 +0,0 @@
package bot
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
// banphraseResponse is the data we receive back from
// the banphrase API
type banphraseResponse struct {
Banned bool `json:"banned"`
InputMessage string `json:"input_message"`
BanphraseData banphraseData `json:"banphrase_data"`
}
// banphraseData contains details about why a message
// was banphrased.
type banphraseData struct {
Id int `json:"id"`
Name string `json:"name"`
Phrase string `json:"phrase"`
Length int `json:"length"`
Permanent bool `json:"permanent"`
}
var (
banPhraseUrl = "https://pajlada.pajbot.com/api/v1/banphrases/test"
)
// CheckMessage checks a message against a banphrase api..
// returns false, "okay" if a message was allowed
// returns true, "[banphrased] monkaS" and the reason if not.
// More information:
// https://gist.github.com/pajlada/57464e519ba8d195a97ddcd0755f9715
func CheckMessage(text string) (bool, string) {
// log.Info("fn CheckMessage")
// {"message": "AHAHAHAHA LUL"}
reqBody, err := json.Marshal(map[string]string{
"message": text,
})
if err != nil {
log.Error(err)
}
resp, err := http.Post(banPhraseUrl, "application/json", bytes.NewBuffer(reqBody))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
}
var responseObject banphraseResponse
json.Unmarshal(body, &responseObject)
// Bad Message
//
// {"phrase": "No gyazo allowed"}
// reason := responseObject.BanphraseData.Name
if responseObject.Banned {
return true, "[Banphrased]"
} else if !responseObject.Banned {
// Good message
return false, "okay"
}
// Couldn't contact api so assume it was a bad message
return true, "Banphrase API couldn't be reached monkaS"
}

View file

@ -1,30 +0,0 @@
package bot
import "testing"
func TestBanphrase(t *testing.T) {
t.Run("tests the banphrase api", func(t *testing.T) {
// Banned message
request, _ := CheckMessage("https://gyazo.com/asda")
response := true
got := request
want := response
if got != want {
t.Errorf("got %v, want %v", got, want)
}
// Okay message
request, _ = CheckMessage("LUL")
response = false
got = request
want = response
if got != want {
t.Errorf("got %v, want %v", got, want)
}
})
}

View file

@ -1,57 +0,0 @@
package bot
import (
"time"
twitch "github.com/gempir/go-twitch-irc/v2"
"go.mongodb.org/mongo-driver/mongo"
)
type Bot struct {
TwitchClient *twitch.Client
MongoClient *mongo.Client
Uptime time.Time
}
// Send checks the message against a banphrase api
// and also splits the message into two if the message
// is too long for a single twitch chat message.
func (b *Bot) Send(target, text string) {
if len(text) == 0 {
return
}
if text[0] == '.' || text[0] == '/' || text[0] == '!' {
text = ":tf: " + text
}
// If a message is too long for a single twitch
// message, split it into two messages.
if len(text) > 500 {
firstMessage := text[0:499]
secondMessage := text[499:]
b.TwitchClient.Say(target, firstMessage)
b.TwitchClient.Say(target, secondMessage)
return
}
// check the message for bad words before we say it
messageBanned, banReason := CheckMessage(text)
if messageBanned {
// Bad message, replace message with a small
// notice on why it's banned.
b.TwitchClient.Say(target, banReason)
return
} else {
// Message was okay.
b.TwitchClient.Say(target, text)
return
}
}
// SkipChecking sends the message without banphrase check
func (b *Bot) SkipChecking(target, text string) {
b.TwitchClient.Say(target, text)
}

80
cmd/bot/command.go Normal file
View file

@ -0,0 +1,80 @@
package main
import (
"strings"
"github.com/gempir/go-twitch-irc/v3"
"github.com/lyx0/nourybot/pkg/commands"
"github.com/lyx0/nourybot/pkg/common"
"go.uber.org/zap"
)
func handleCommand(message twitch.PrivateMessage, tc *twitch.Client) {
sugar := zap.NewExample().Sugar()
defer sugar.Sync()
// Increments the counter how many commands were used.
common.CommandUsed()
// commandName is the actual name of the command without the prefix.
// e.g. `()ping` would be `ping`.
commandName := strings.ToLower(strings.SplitN(message.Message, " ", 3)[0][2:])
// cmdParams are additional command parameters.
// e.g. `()weather san antonio`
// cmdParam[0] is `san` and cmdParam[1] = `antonio`.
//
// Since Twitch messages are at most 500 characters I use a
// maximum count of 500+10 just to be safe.
// https://discuss.dev.twitch.tv/t/missing-client-side-message-length-check/21316
cmdParams := strings.SplitN(message.Message, " ", 500)
_ = cmdParams
// msgLen is the amount of words in a message without the prefix.
// Useful to check if enough cmdParams are provided.
msgLen := len(strings.SplitN(message.Message, " ", -2))
// target is the channelname the message originated from and
// where we are responding.
target := message.Channel
sugar.Infow("Command received",
// "message", message,
"message.Channel", target,
"message.Message", message.Message,
"commandName", commandName,
"cmdParams", cmdParams,
"msgLen", msgLen,
)
switch commandName {
case "":
if msgLen == 1 {
common.Send(target, "xd", tc)
return
}
case "currency":
if msgLen < 4 {
common.Send(target, "Not enough arguments provided. Usage: ()currency 10 USD to EUR", tc)
return
}
commands.Currency(target, cmdParams[1], cmdParams[2], cmdParams[4], tc)
case "echo":
if msgLen < 2 {
common.Send(target, "Not enough arguments provided.", tc)
return
} else {
commands.Echo(target, message.Message[7:len(message.Message)], tc)
return
}
case "tweet":
if msgLen < 2 {
common.Send(target, "Not enough arguments provided. Usage: ()tweet forsen", tc)
return
} else {
commands.Tweet(target, cmdParams[1], tc)
}
case "ping":
commands.Ping(target, tc)
}
}

57
cmd/bot/main.go Normal file
View file

@ -0,0 +1,57 @@
package main
import (
"github.com/gempir/go-twitch-irc/v3"
"github.com/lyx0/nourybot/internal/config"
"github.com/lyx0/nourybot/pkg/common"
"go.uber.org/zap"
)
type Application struct {
TwitchClient *twitch.Client
Logger *zap.SugaredLogger
}
func main() {
cfg := config.New()
tc := twitch.NewClient(cfg.TwitchUsername, cfg.TwitchOauth)
sugar := zap.NewExample().Sugar()
defer sugar.Sync()
common.StartTime()
app := &Application{
TwitchClient: tc,
Logger: sugar,
}
// Received a PrivateMessage (normal chat message), pass it to
// the handler who checks for further action.
app.TwitchClient.OnPrivateMessage(func(message twitch.PrivateMessage) {
app.handlePrivateMessage(message)
})
// Received a WhisperMessage (Twitch DM), pass it to
// the handler who checks for further action.
app.TwitchClient.OnWhisperMessage(func(message twitch.WhisperMessage) {
app.handleWhisperMessage(message)
})
// Successfully connected to Twitch so we log a message with the
// mode we are currently running in..
app.TwitchClient.OnConnect(func() {
app.Logger.Infow("Successfully connected to Twitch Servers",
"Bot username", cfg.TwitchUsername,
"Environment", cfg.Environment,
)
common.Send("nourylul", "xd", app.TwitchClient)
})
app.TwitchClient.Join("nourylul")
err := app.TwitchClient.Connect()
if err != nil {
panic(err)
}
}

40
cmd/bot/privatemessage.go Normal file
View file

@ -0,0 +1,40 @@
package main
import (
"github.com/gempir/go-twitch-irc/v3"
"go.uber.org/zap"
)
func (app *Application) handlePrivateMessage(message twitch.PrivateMessage) {
sugar := zap.NewExample().Sugar()
defer sugar.Sync()
// roomId is the Twitch UserID of the channel the message originated from.
// If there is no roomId something went really wrong.
roomId := message.Tags["room-id"]
if roomId == "" {
app.Logger.Errorw("Missing room-id in message tag",
"roomId", roomId,
)
return
}
// Message was shorter than our prefix is therefore it's irrelevant for us.
if len(message.Message) >= 2 {
// Strip the `()` prefix
if message.Message[:2] == "()" {
handleCommand(message, app.TwitchClient)
return
}
}
// Message was no command so we just print it.
app.Logger.Infow("Private Message received",
// "message", message,
"message.Channel", message.Channel,
"message.User", message.User.DisplayName,
"message.Message", message.Message,
)
}

13
cmd/bot/whispermessage.go Normal file
View file

@ -0,0 +1,13 @@
package main
import "github.com/gempir/go-twitch-irc/v3"
func (app *Application) handleWhisperMessage(message twitch.WhisperMessage) {
// Print the whisper message for now.
// TODO: Implement a basic whisper handler.
app.Logger.Infow("Whisper Message received",
"message", message,
"message.User.DisplayName", message.User.DisplayName,
"message.Message", message.Message,
)
}

View file

@ -1,66 +0,0 @@
package main
import (
"flag"
"fmt"
"time"
"github.com/gempir/go-twitch-irc/v2"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/config"
"github.com/lyx0/nourybot/pkg/db"
"github.com/lyx0/nourybot/pkg/handlers"
log "github.com/sirupsen/logrus"
)
var nb *bot.Bot
func main() {
// runMode is either dev or production so that we don't join every channel
// everytime we do a small test.
runMode := flag.String("mode", "production", "Mode in which to run. (dev/production")
flag.Parse()
conf := config.LoadConfig()
nb = &bot.Bot{
TwitchClient: twitch.NewClient(conf.Username, conf.Oauth),
MongoClient: db.Connect(conf),
Uptime: time.Now(),
}
nb.TwitchClient.OnPrivateMessage(func(message twitch.PrivateMessage) {
// If channelID is missing something must have gone wrong.
channelID := message.Tags["room-id"]
if channelID == "" {
fmt.Printf("Missing room-id tag in message")
return
}
// Don't act on bots own messages.
if message.Tags["user-id"] == conf.BotUserId {
return
}
// Forward the message for further processing.
handlers.PrivateMessage(message, nb)
})
// Depending on the mode we run in, join different channel.
if *runMode == "production" {
log.Info("[PRODUCTION]: Joining every channel.")
// Production, joining all regular channels
db.InitialJoin(nb)
} else if *runMode == "dev" {
log.Info("[DEV]: Joining whereismymnd and nourybot.")
// Development, only join my two channels
nb.TwitchClient.Join("whereismymnd", "nourybot")
nb.Send("nourybot", "[DEV] Badabing Badaboom Pepepains")
}
nb.TwitchClient.Connect()
}

View file

@ -1,18 +0,0 @@
package main
import "testing"
func TestMain(t *testing.T) {
t.Run("main package test placeholder", func(t *testing.T) {
request := 1
response := 1
got := request
want := response
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,135 +0,0 @@
nourybot commands
##### 8ball:
Ask the magic 8ball for guidance
#### Bot/Botinfo/Nourybot/Help:
Returns information about the bot
#### Botstatus:
Returns the status of a verified/known/normal bots and its ratelimits
API used: https://customapi.aidenwallis.co.uk/docs/twitch/bot-status
#### Bttv:
Returns a bttv search link for a given emote name
#### Bttvemotes:
Returns all Bttv emotes from the current channel
API used: https://customapi.aidenwallis.co.uk/docs/emotes/bttv
#### Commands:
Returns this page 4Head
#### Coinflip:
Head or Tails
#### Ffz:
Return sa ffz search link for a given emote name
Usage: ()ffz [emotename]
#### Ffzemotes:
Returns all Ffz emotes from the current channel
API used: https://customapi.aidenwallis.co.uk/docs/emotes/ffz
#### Fill:
Repeats an emote for the maximum size of a single message and "fills" the message with it.
Only usable for vips/moderators/broadcaster.
#### Firstline/fl:
Returns the first line a user has written in a channel.
Usage: ()fl [channel] [user]
API used: https://api.ivr.fi/logs/firstmessage/
#### Followage:
Returns the date a user has been following a channel.
Usage: ()followage [channel] [user]
API used: https://api.ivr.fi/twitch/subage/
#### Game:
Returns the game of the current channel if no parameters are given, otherwise for the given channel
Usage: ()game [channel]
#### Number/num:
Returns a fact about a given number, or if none is given a random number.
API used: http://numbersapi.com/
#### Godoc/Godocs:
Returns the godocs.io search for a given word
#### Ping:
Returns a Pong
#### Profilepicture/pfp:
Returns a link to a users profile picture.
API used: https://api.ivr.fi/twitch/resolve/
#### Pingme:
Pings you in chat
#### Pyramid:
Builds a pyramid of a given emote, only usable by vips/moderators/broadcaster in the channel. Max size 20
#### RandomCat/cat:
Returns a random cat image
API used: https://aws.random.cat/meow
#### RandomCat/dog:
Returns a random dog image
API used: https://random.dog/woof.json
#### Randomduck/duck:
Returns a random duck image
API used: https://random-d.uk/
#### RandomFox/fox:
Returns a random fox image
API used: https://randomfox.ca/floof
#### Randomxkcd/rxkcd:
Returns a random Xkcd comic
#### Randomquote/rq:
Returns a random quote from a user in a channel.
Usage: ()rq [channel] [user]
API used: https://api.ivr.fi/logs/rq/
### Robohash/robo:
Returns a link to the robohash image of your Twitch Message Id.
API Used: https://robohash.org
#### Subage:
Returns the months someone has been subscribed to a channel.
Usage: ()subage [channel] [user]
API used: https://api.ivr.fi/twitch/subage/
### Thumb/Preview:
Returns a screenshot of a given live channel.
Usage: ()thumb [channel]
#### Title:
Returns the title of the current channel if no parameters are given, otherwise for the given channel
#### Mycolor/color:
Returns the hexcode of your Twitch color
#### Uptime:
Returns the uptime of the current channel if no parameters are given, otherwise for the given channel
Usage: ()uptime [channel]
#### userid/uid:
Returns the Twitch User ID of a given user, otherwise the senders userid.
Usage: ()uid [name]
#### Uid:
Returns the Twitch userid of a given username
API used: https://api.ivr.fi/twitch/resolve/
#### Weather:
Returns the weather for a given location
API used: https://customapi.aidenwallis.co.uk/api/v1/misc/weather/
Usage: ()weather [location]
#### Xkcd:
Returns a link to the current Xkcd comic

21
go.mod
View file

@ -1,26 +1,15 @@
module github.com/lyx0/nourybot module github.com/lyx0/nourybot
go 1.17 go 1.19
require ( require (
github.com/dustin/go-humanize v1.0.0 github.com/dustin/go-humanize v1.0.0
github.com/gempir/go-twitch-irc/v2 v2.5.0 github.com/gempir/go-twitch-irc/v3 v3.2.0
github.com/joho/godotenv v1.4.0 github.com/joho/godotenv v1.4.0
github.com/sirupsen/logrus v1.8.1 go.uber.org/zap v1.21.0
go.mongodb.org/mongo-driver v1.7.3
) )
require ( require (
github.com/go-stack/stack v1.8.0 // indirect go.uber.org/atomic v1.9.0 // indirect
github.com/golang/snappy v0.0.1 // indirect go.uber.org/multierr v1.8.0 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.0.2 // indirect
github.com/xdg-go/stringprep v1.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
golang.org/x/text v0.3.5 // indirect
) )

137
go.sum
View file

@ -1,126 +1,65 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
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=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/gempir/go-twitch-irc/v2 v2.5.0 h1:aybXNoyDNQaa4vHhXb0UpIDmspqutQUmXIYUFsjgecU= github.com/gempir/go-twitch-irc/v3 v3.2.0 h1:ENhsa7RgBE1GMmDqe0iMkvcSYfgw6ZsXilt+sAg32/U=
github.com/gempir/go-twitch-irc/v2 v2.5.0/go.mod h1:120d2SdlRYg8tRnZwsyNPeS+mWPn+YmNEzB7Bv/CDGE= github.com/gempir/go-twitch-irc/v3 v3.2.0/go.mod h1:/W9KZIiyizVecp4PEb7kc4AlIyXKiCmvlXrzlpPUytU=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
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/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.mongodb.org/mongo-driver v1.7.3 h1:G4l/eYY9VrQAK/AUgkV0koQKzQnyddnWxrd/Etf0jIs= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

26
internal/config/config.go Normal file
View file

@ -0,0 +1,26 @@
package config
import (
"log"
"os"
"github.com/joho/godotenv"
)
type Config struct {
TwitchUsername string
TwitchOauth string
Environment string
}
func New() *Config {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
twitchUsername := os.Getenv("TWITCH_USERNAME")
twitchOauth := os.Getenv("TWITCH_OAUTH")
environment := "Development"
return &Config{twitchUsername, twitchOauth, environment}
}

View file

@ -1,32 +0,0 @@
package aiden
import (
"fmt"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
var (
basePath = "https://customapi.aidenwallis.co.uk/"
)
// ApiCall calls https://customapi.aidenwallis.co.uk/ with
// a given uri and returns the result and an error.
func ApiCall(uri string) (string, error) {
resp, err := http.Get(fmt.Sprint(basePath + uri))
if err != nil {
log.Error(err)
return "Something went wrong FeelsBadMan", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
return "Something went wrong FeelsBadMan", err
}
return string(body), nil
}

View file

@ -1,21 +0,0 @@
package aiden
import (
"testing"
)
// TestAidenBotRatelimits calls aidens bot ratelimits api and checks
// the results against each other with my bot and another verified bot.
func TestAidenBotRatelimits(t *testing.T) {
t.Run("returns a twitch accounts ratelimits", func(t *testing.T) {
requestPajbot, _ := ApiCall("api/v1/twitch/botStatus/pajbot?includeLimits=1")
requestNourybot, _ := ApiCall("api/v1/twitch/botStatus/nourybot?includeLimits=1")
got := requestPajbot
want := requestNourybot
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,19 +0,0 @@
package api
import "testing"
// Idk how to test this really since almost everything in here is random
func TestApi(t *testing.T) {
t.Run("api package test placeholder", func(t *testing.T) {
request := 1
response := 1
got := request
want := response
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,48 +0,0 @@
package api
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
log "github.com/sirupsen/logrus"
)
type currencyResponse struct {
Amount int `json:"amount"`
Base string `json:"base"`
Rates map[string]float32 `json:"rates"`
}
// Currency returns the exchange rate for a given given amount to another.
func Currency(currAmount, currFrom, currTo string) (string, error) {
baseUrl := "https://api.frankfurter.app"
currFromUpper := strings.ToUpper(currFrom)
currToUpper := strings.ToUpper(currTo)
// https://api.frankfurter.app/latest?amount=10&from=gbp&to=usd
response, err := http.Get(fmt.Sprintf("%s/latest?amount=%s&from=%s&to=%s", baseUrl, currAmount, currFromUpper, currToUpper))
if err != nil {
log.Error(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error(err)
}
var responseObject currencyResponse
json.Unmarshal(responseData, &responseObject)
// log.Info(responseObject.Amount)
// log.Info(responseObject.Base)
// log.Info(responseObject.Rates[strings.ToUpper(currTo)])
if responseObject.Rates[currToUpper] != 0 {
return fmt.Sprintf("%s %s = %.2f %s", currAmount, currFromUpper, responseObject.Rates[currToUpper], currToUpper), nil
}
return "Something went wrong FeelsBadMan", nil
}

View file

@ -1,41 +0,0 @@
package ivr
import "testing"
func TestEmoteLookup(t *testing.T) {
t.Run("returns the channel a twitch emote is from and its tier", func(t *testing.T) {
// Tier 1
request, _ := EmoteLookup("forsenE")
response := "forsenE is a Tier 1 emote to channel forsen."
got := request
want := response
if got != want {
t.Errorf("got %q, want %q", got, want)
}
// Tier 2
request, _ = EmoteLookup("nanZ")
response = "nanZ is a Tier 2 emote to channel nani."
got = request
want = response
if got != want {
t.Errorf("got %q, want %q", got, want)
}
// Tier 3
request, _ = EmoteLookup("pajaCORAL")
response = "pajaCORAL is a Tier 3 emote to channel pajlada."
got = request
want = response
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,47 +0,0 @@
package ivr
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
type emoteLookupResponse struct {
Channel string `json:"channel"`
EmoteCode string `json:"emotecode"`
Tier string `json:"tier"`
Emote string `json:"emote"`
Error string `json:"error"`
}
// ProfilePicture returns a link to a given users profilepicture.
func EmoteLookup(emote string) (string, error) {
baseUrl := "https://api.ivr.fi/twitch/emotes"
resp, err := http.Get(fmt.Sprintf("%s/%s", baseUrl, emote))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
}
var responseObject emoteLookupResponse
json.Unmarshal(body, &responseObject)
// log.Info(responseObject.Tier)
// Emote not found
if responseObject.Error != "" {
return fmt.Sprintf(responseObject.Error + " FeelsBadMan"), nil
} else {
return fmt.Sprintf("%s is a Tier %v emote to channel %s.", responseObject.EmoteCode, responseObject.Tier, responseObject.Channel), nil
}
}

View file

@ -1,47 +0,0 @@
package ivr
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
type firstLineApiResponse struct {
User string `json:"user"`
Message string `json:"message"`
Time string `json:"time"`
Error string `json:"error"`
}
// FirstLine returns the first line a given user has sent in a
// given channel.
func FirstLine(channel, username string) (string, error) {
baseUrl := "https://api.ivr.fi/logs/firstmessage"
resp, err := http.Get(fmt.Sprintf("%s/%s/%s", baseUrl, channel, username))
if err != nil {
log.Error(err)
return "Something went wrong FeelsBadMan", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
}
var responseObject firstLineApiResponse
json.Unmarshal(body, &responseObject)
// User or channel was not found
if responseObject.Error != "" {
return fmt.Sprintf(responseObject.Error + " FeelsBadMan"), nil
} else {
return fmt.Sprintf(username + ": " + responseObject.Message + " (" + responseObject.Time + " ago)."), nil
}
}

View file

@ -1,16 +0,0 @@
package ivr
// func TestFirstLine(t *testing.T) {
// t.Run("returns a users first message in a channel", func(t *testing.T) {
// request, _ := FirstLine("forsen", "forsen")
// response := "forsen: EZ or Ezy (3y, 9mo, 16d ago)."
// got := request
// want := response
// if got != want {
// t.Errorf("got %q, want %q", got, want)
// }
// })
// }

View file

@ -1,50 +0,0 @@
package ivr
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
// https://api.ivr.fi
type followageApiResponse struct {
User string `json:"user"`
UserID string `json:"userid"`
Channel string `json:"channel"`
ChannelId string `json:"channelid"`
FollowedAt string `json:"followedAt"`
Error string `json:"error"`
}
// Followage returns the time since a given user followed a given streamer
func Followage(streamer, username string) (string, error) {
baseUrl := "https://api.ivr.fi/twitch/subage"
resp, err := http.Get(fmt.Sprintf("%s/%s/%s", baseUrl, username, streamer))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
}
var responseObject followageApiResponse
json.Unmarshal(body, &responseObject)
// User or channel was not found
if responseObject.Error != "" {
return fmt.Sprintf(responseObject.Error + " FeelsBadMan"), nil
} else if responseObject.FollowedAt == "" {
return fmt.Sprintf(username + " is not following " + streamer), nil
} else {
d := responseObject.FollowedAt[:10]
return fmt.Sprintf(username + " has been following " + streamer + " since " + d + "."), nil
}
}

View file

@ -1,18 +0,0 @@
package ivr
import "testing"
func TestFollowage(t *testing.T) {
t.Run("returns a users date they followed a channel", func(t *testing.T) {
request, _ := Followage("forsen", "pajlada")
response := "pajlada has been following forsen since 2015-03-09."
got := request
want := response
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,43 +0,0 @@
package ivr
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
// https://api.ivr.fi
type pfpApiResponse struct {
Logo string `json:"logo"`
Error string `json:"error"`
}
// ProfilePicture returns a link to a given users profilepicture.
func ProfilePicture(username string) (string, error) {
baseUrl := "https://api.ivr.fi/twitch/resolve"
resp, err := http.Get(fmt.Sprintf("%s/%s", baseUrl, username))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
}
var responseObject pfpApiResponse
json.Unmarshal(body, &responseObject)
// User not found
if responseObject.Error != "" {
return fmt.Sprintf(responseObject.Error + " FeelsBadMan"), nil
} else {
return fmt.Sprintf(responseObject.Logo), nil
}
}

View file

@ -1,18 +0,0 @@
package ivr
import "testing"
func TestProfilePicture(t *testing.T) {
t.Run("returns a link to a users profile picture", func(t *testing.T) {
request, _ := ProfilePicture("forsen")
response := "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-600x600.png"
got := request
want := response
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,47 +0,0 @@
package ivr
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
type randomQuoteApiResponse struct {
User string `json:"user"`
Message string `json:"message"`
Time string `json:"time"`
Error string `json:"error"`
}
// FirstLine returns the first line a given user has sent in a
// given channel.
func RandomQuote(channel, username string) (string, error) {
baseUrl := "https://api.ivr.fi/logs/rq"
resp, err := http.Get(fmt.Sprintf("%s/%s/%s", baseUrl, channel, username))
if err != nil {
log.Error(err)
return "Something went wrong FeelsBadMan", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
}
var responseObject randomQuoteApiResponse
json.Unmarshal(body, &responseObject)
// User or channel was not found
if responseObject.Error != "" {
return fmt.Sprintf(responseObject.Error + " FeelsBadMan"), nil
} else {
return fmt.Sprintf(username + ": " + responseObject.Message + " (" + responseObject.Time + " ago)."), nil
}
}

View file

@ -1,69 +0,0 @@
package ivr
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
type subageApiResponse struct {
User string `json:"user"`
UserID string `json:"userId"`
Channel string `json:"channel"`
ChannelId string `json:"channelid"`
SubageHidden bool `json:"hidden"`
Subscribed bool `json:"subscribed"`
FollowedAt string `json:"followedAt"`
Cumulative cumulative `json:"cumulative"`
Streak subStreak `json:"streak"`
Error string `json:"error"`
}
type cumulative struct {
Months int `json:"months"`
}
type subStreak struct {
Months int `json:"months"`
}
// Subage returns the length a given user has been subscribed to a given channel.
func Subage(username, channel string) (string, error) {
baseUrl := "https://api.ivr.fi/twitch/subage"
resp, err := http.Get(fmt.Sprintf("%s/%s/%s", baseUrl, username, channel))
if err != nil {
log.Error(err)
return "Something went wrong FeelsBadMan", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
}
var responseObject subageApiResponse
json.Unmarshal(body, &responseObject)
// User or channel was not found
if responseObject.Error != "" {
reply := fmt.Sprintf(responseObject.Error + " FeelsBadMan")
// client.Say(channel, fmt.Sprintf(responseObject.Error+" FeelsBadMan"))
return reply, nil
}
if responseObject.SubageHidden {
reply := fmt.Sprintf(username + " has their subscription status hidden. FeelsBadMan")
return reply, nil
} else {
months := fmt.Sprint(responseObject.Cumulative.Months)
reply := fmt.Sprintf(username + " has been subscribed to " + channel + " for " + months + " months.")
return reply, nil
}
}

View file

@ -1,43 +0,0 @@
package ivr
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
// https://api.ivr.fi
type uidApiResponse struct {
Id string `json:"id"`
Error string `json:"error"`
}
// Userid returns the userID of a given user
func Userid(username string) string {
baseUrl := "https://api.ivr.fi/twitch/resolve"
resp, err := http.Get(fmt.Sprintf("%s/%s", baseUrl, username))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
}
var responseObject uidApiResponse
json.Unmarshal(body, &responseObject)
// User not found
if responseObject.Error != "" {
return fmt.Sprintf(responseObject.Error + " FeelsBadMan")
} else {
return fmt.Sprintf(responseObject.Id)
}
}

View file

@ -1,18 +0,0 @@
package ivr
import "testing"
func TestUserid(t *testing.T) {
t.Run("returns a link to a users profile picture", func(t *testing.T) {
request := Userid("whereismymnd")
response := "31437432"
got := request
want := response
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,77 +0,0 @@
package ivr
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
log "github.com/sirupsen/logrus"
)
// https://api.ivr.fi
type whoisApiResponse struct {
Id string `json:"id"`
DisplayName string `json:"displayName"`
Login string `json:"login"`
Bio string `json:"bio"`
ChatColor string `json:"chatColor"`
Partner bool `json:"partner"`
// Affiliate bool `json:"affiliate"`
Bot bool `json:"bot"`
CreatedAt string `json:"createdAt"`
Roles roles
Error string `json:"error"`
}
type roles struct {
IsAffiliate bool `json:"isAffiliate"`
IsPartner bool `json:"isPartner"`
IsSiteAdmin bool `json:"isSiteAdmin"`
IsStaff bool `json:"isStaff"`
}
// Userid returns the userID of a given user
func Whois(username string) string {
baseUrl := "https://api.ivr.fi/twitch/resolve"
resp, err := http.Get(fmt.Sprintf("%s/%s", baseUrl, username))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
}
var responseObject whoisApiResponse
json.Unmarshal(body, &responseObject)
// time string format 2011-05-19T00:28:28.310449Z
// discard everything after T
created := strings.Split(responseObject.CreatedAt, "T")
reply := fmt.Sprintf("User: %s, ID: %s, Created on: %s, Color: %s, Affiliate: %v, Partner: %v, Staff: %v, Admin: %v, Bot: %v, Bio: %v",
responseObject.DisplayName,
responseObject.Id,
created[0],
responseObject.ChatColor,
responseObject.Roles.IsAffiliate,
responseObject.Roles.IsPartner,
responseObject.Roles.IsStaff,
responseObject.Roles.IsSiteAdmin,
responseObject.Bot,
responseObject.Bio,
)
// User not found
if responseObject.Error != "" {
return fmt.Sprintf(responseObject.Error + " FeelsBadMan")
} else {
return reply
}
}

View file

@ -1,16 +0,0 @@
package ivr
import "testing"
func TestWhois(t *testing.T) {
t.Run("returns a link to a users profile picture", func(t *testing.T) {
request := Whois("forsen")
got := request
want := Whois("forsen")
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,42 +0,0 @@
package api
import (
"fmt"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
// RandomNumber returns a string containg fun facts about a random number.
// API used: http://numbersapi.com
func RandomNumber() string {
response, err := http.Get("http://numbersapi.com/random/trivia")
if err != nil {
log.Error(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error(err)
}
return string(responseData)
}
// Number returns a string containing fun facts about a given number.
// API used: http://numbersapi.com
func Number(number string) string {
response, err := http.Get(fmt.Sprint("http://numbersapi.com/" + string(number)))
if err != nil {
log.Error(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error(err)
}
return string(responseData)
}

View file

@ -1,32 +0,0 @@
package api
import (
"encoding/json"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
type randomCatResponse struct {
File string `json:"file"`
}
// RandomCat returns a link to a random cat picture.
// API used: https://aws.random.cat/meow
func RandomCat() string {
response, err := http.Get("https://aws.random.cat/meow")
if err != nil {
log.Error(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error(err)
}
var responseObject randomCatResponse
json.Unmarshal(responseData, &responseObject)
return string(responseObject.File)
}

View file

@ -1,32 +0,0 @@
package api
import (
"encoding/json"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
type randomDogResponse struct {
Url string `json:"url"`
}
// RandomDog returns a link to a random dog picture.
// API used: https://random.dog/woof.json
func RandomDog() string {
response, err := http.Get("https://random.dog/woof.json")
if err != nil {
log.Error(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error(err)
}
var responseObject randomDogResponse
json.Unmarshal(responseData, &responseObject)
return string(responseObject.Url)
}

View file

@ -1,32 +0,0 @@
package api
import (
"encoding/json"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
type randomDuckResponse struct {
Url string `json:"url"`
}
// RandomDuck returns a link to a random duck picture.
// API used: https://random-d.uk/api/random
func RandomDuck() string {
response, err := http.Get("https://random-d.uk/api/random")
if err != nil {
log.Error(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error(err)
}
var responseObject randomDuckResponse
json.Unmarshal(responseData, &responseObject)
return string(responseObject.Url)
}

View file

@ -1,33 +0,0 @@
package api
import (
"encoding/json"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
type randomFoxResponse struct {
Image string `json:"image"`
Link string `json:"link"`
}
// RandomFox returns a link to a random fox picture.
// API used: https://randomfox.ca/floof/
func RandomFox() string {
response, err := http.Get("https://randomfox.ca/floof/")
if err != nil {
log.Error(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error(err)
}
var responseObject randomFoxResponse
json.Unmarshal(responseData, &responseObject)
return string(responseObject.Image)
}

View file

@ -1,36 +0,0 @@
package api
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/lyx0/nourybot/pkg/utils"
log "github.com/sirupsen/logrus"
)
type XkcdResponse struct {
Num int `json:"num"`
SafeTitle string `json:"safe_title"`
Img string `json:"img"`
}
// RandomXkcd returns a link to a random Xkcd comic.
func RandomXkcd() string {
comicNum := fmt.Sprint(utils.GenerateRandomNumber(2468))
response, err := http.Get(fmt.Sprint("http://xkcd.com/" + comicNum + "/info.0.json"))
if err != nil {
log.Error(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error(err)
}
var responseObject XkcdResponse
json.Unmarshal(responseData, &responseObject)
reply := fmt.Sprint("Random Xkcd #", responseObject.Num, " Title: ", responseObject.SafeTitle, " ", responseObject.Img)
return reply
}

View file

@ -1,29 +0,0 @@
package api
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
log "github.com/sirupsen/logrus"
)
// Xkcd returns a link to the latest Xkcd comic.
// API used: https://xkcd.com/info.0.json
func Xkcd() string {
response, err := http.Get("https://xkcd.com/info.0.json")
if err != nil {
log.Error(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error(err)
}
var responseObject XkcdResponse
json.Unmarshal(responseData, &responseObject)
reply := fmt.Sprint("Current Xkcd #", responseObject.Num, " Title: ", responseObject.SafeTitle, " ", responseObject.Img)
return reply
}

View file

@ -1,22 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/aiden"
log "github.com/sirupsen/logrus"
)
// Botstatus responds with if a given bot is verified/known and its ratelimits.
func BotStatus(channel, name string, nb *bot.Bot) {
resp, err := aiden.ApiCall(fmt.Sprintf("api/v1/twitch/botStatus/%s?includeLimits=1", name))
if err != nil {
nb.Send(channel, "Something went wrong FeelsBadMan")
log.Error(err)
return
}
nb.Send(channel, resp)
}

View file

@ -1,14 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
)
// Bttv responds with a search for a given bttv emote.
func Bttv(target, emote string, nb *bot.Bot) {
reply := fmt.Sprintf("https://betterttv.com/emotes/shared/search?query=%s", emote)
nb.Send(target, reply)
}

View file

@ -1,20 +0,0 @@
package commands
// import (
// "fmt"
// "github.com/lyx0/nourybot/cmd/bot"
// "github.com/lyx0/nourybot/pkg/api/aiden"
// log "github.com/sirupsen/logrus"
// )
// func BttvEmotes(channel string, nb *bot.Bot) {
// resp, err := aiden.ApiCall(fmt.Sprintf("api/v1/emotes/%s/bttv", channel))
// if err != nil {
// log.Error(err)
// nb.Send(channel, "Something went wrong FeelsBadMan")
// }
// nb.Send(channel, string(resp))
// }

View file

@ -1,11 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/db"
)
func ChannelList(target string, nb *bot.Bot) {
db.ListChannel(nb)
nb.Send(target, "Whispered you the list of channel.")
}

View file

@ -1,17 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/utils"
)
// Coinflip responds with Heads or Tails.
func Coinflip(channel string, nb *bot.Bot) {
result := utils.GenerateRandomNumber(2)
if result == 1 {
nb.Send(channel, "Heads")
} else {
nb.Send(channel, "Tails")
}
}

View file

@ -1,53 +0,0 @@
package commands
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/lyx0/nourybot/cmd/bot"
log "github.com/sirupsen/logrus"
)
// https://api.ivr.fi
type colorApiResponse struct {
Id string `json:"id"`
DisplayName string `json:"displayName"`
ChatColor string `json:"chatColor"`
Error string `json:"error"`
}
// Userid returns the userID of a given user
func Color(username, target string, nb *bot.Bot) {
baseUrl := "https://api.ivr.fi/twitch/resolve"
resp, err := http.Get(fmt.Sprintf("%s/%s", baseUrl, username))
if err != nil {
log.Error(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
}
var responseObject colorApiResponse
json.Unmarshal(body, &responseObject)
// time string format 2011-05-19T00:28:28.310449Z
// discard everything after T
reply := fmt.Sprintf("%s color is %s",
responseObject.DisplayName,
responseObject.ChatColor,
)
// User not found
if responseObject.Error != "" {
nb.Send(target, "Something went wrong... FeelsBadMan")
}
nb.Send(target, reply)
}

View file

@ -1,18 +0,0 @@
package commands
import "testing"
func TestCommands(t *testing.T) {
t.Run("commands package test placeholder", func(t *testing.T) {
request := 1
response := 1
got := request
want := response
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,10 +0,0 @@
package commands
import "github.com/lyx0/nourybot/cmd/bot"
// Commandslist responds with a link to the list of commands.
func CommandsList(target string, nb *bot.Bot) {
reply := "https://gist.github.com/lyx0/6e326451ed9602157fcf6b5e40fb1dfd"
nb.Send(target, reply)
}

View file

@ -1,18 +1,21 @@
package commands package commands
import ( import (
"github.com/lyx0/nourybot/cmd/bot" "github.com/gempir/go-twitch-irc/v3"
"github.com/lyx0/nourybot/pkg/api" "github.com/lyx0/nourybot/pkg/common"
"github.com/sirupsen/logrus" "github.com/lyx0/nourybot/pkg/decapi"
"go.uber.org/zap"
) )
// Currency responds with the conversion rate for two given currencies. // ()currency 10 USD to EUR
// Example: ()currency 10 usd to eur func Currency(target, currAmount, currFrom, currTo string, tc *twitch.Client) {
func Currency(target, currAmount, currFrom, currTo string, nb *bot.Bot) { sugar := zap.NewExample().Sugar()
reply, err := api.Currency(currAmount, currFrom, currTo) defer sugar.Sync()
resp, err := decapi.Currency(currAmount, currFrom, currTo)
if err != nil { if err != nil {
logrus.Info(err) sugar.Error(err)
} }
nb.Send(target, reply) common.Send(target, resp, tc)
} }

View file

@ -1,9 +1,10 @@
package commands package commands
import "github.com/lyx0/nourybot/cmd/bot" import (
"github.com/gempir/go-twitch-irc/v3"
"github.com/lyx0/nourybot/pkg/common"
)
// Echo responds with the given message. func Echo(target, message string, tc *twitch.Client) {
func Echo(channel, message string, nb *bot.Bot) { common.Send(target, message, tc)
nb.Send(channel, message)
} }

View file

@ -1,19 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/aiden"
log "github.com/sirupsen/logrus"
)
// Eightball asks the magic 8ball for guidance.
func EightBall(channel string, nb *bot.Bot) {
resp, err := aiden.ApiCall("api/v1/misc/8ball")
if err != nil {
log.Error(err)
nb.Send(channel, "Something went wrong FeelsBadMan")
}
nb.Send(channel, string(resp))
}

View file

@ -1,21 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/ivr"
)
// EmoteLookup looks up a given emote and responds with the channel it is
// associated with and its tier.
func EmoteLookup(channel, emote string, nb *bot.Bot) {
reply, err := ivr.EmoteLookup(emote)
if err != nil {
nb.Send(channel, fmt.Sprint(err))
return
}
nb.Send(channel, reply)
}

View file

@ -1,14 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
)
// Ffz responds with a search link for a given ffz emote.
func Ffz(target, emote string, nb *bot.Bot) {
reply := fmt.Sprintf("https://www.frankerfacez.com/emoticons/?q=%s", emote)
nb.Send(target, reply)
}

View file

@ -1,20 +0,0 @@
package commands
// import (
// "fmt"
// "github.com/lyx0/nourybot/cmd/bot"
// "github.com/lyx0/nourybot/pkg/api/aiden"
// log "github.com/sirupsen/logrus"
// )
// func FfzEmotes(channel string, nb *bot.Bot) {
// resp, err := aiden.ApiCall(fmt.Sprintf("api/v1/emotes/%s/ffz", channel))
// if err != nil {
// log.Error(err)
// nb.Send(channel, "Something went wrong FeelsBadMan")
// }
// nb.Send(channel, string(resp))
// }

View file

@ -1,27 +0,0 @@
package commands
import (
"fmt"
"strings"
"github.com/lyx0/nourybot/cmd/bot"
)
// Fill repeats a given emote until the whole twitch message
// is filled up with the emote and then sends it.
func Fill(channel, emote string, nb *bot.Bot) {
if emote[0] == '.' || emote[0] == '/' {
nb.Send(channel, ":tf:")
return
}
// Get the length of the emote
emoteLength := (len(emote) + 1)
// Check how often the emote fits into a single message
repeatCount := (499 / emoteLength)
reply := strings.Repeat(fmt.Sprintf(emote+" "), repeatCount)
nb.Send(channel, reply)
}

View file

@ -1,20 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/ivr"
)
// Firstline responds a given users first message in a given channel.
func Firstline(channel, streamer, username string, nb *bot.Bot) {
ivrResponse, err := ivr.FirstLine(streamer, username)
if err != nil {
nb.Send(channel, fmt.Sprint(err))
return
}
nb.Send(channel, ivrResponse)
}

View file

@ -1,20 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/ivr"
)
// Followage responds with a given users time since he followed a streamer.
func Followage(channel, streamer, username string, nb *bot.Bot) {
ivrResponse, err := ivr.Followage(streamer, username)
if err != nil {
nb.Send(channel, fmt.Sprint(err))
return
}
nb.Send(channel, ivrResponse)
}

View file

@ -1,23 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/aiden"
log "github.com/sirupsen/logrus"
)
// Game responds with the current game category a given stream is set to.
func Game(channel, name string, nb *bot.Bot) {
game, err := aiden.ApiCall(fmt.Sprintf("api/v1/twitch/channel/%s/game", name))
if err != nil {
nb.Send(channel, "Something went wrong FeelsBadMan")
log.Error(err)
return
}
reply := fmt.Sprintf("@%s was last seen playing: %s", name, game)
nb.Send(channel, reply)
}

View file

@ -1,14 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
)
// Godocs responds with a search for a given term on godocs.io
func Godocs(channel, searchTerm string, nb *bot.Bot) {
resp := fmt.Sprintf("https://godocs.io/?q=%s", searchTerm)
nb.Send(channel, resp)
}

View file

@ -1,10 +0,0 @@
package commands
import "github.com/lyx0/nourybot/cmd/bot"
// Help responds with basic information about the bot.
func Help(target string, nb *bot.Bot) {
reply := "Bot made in Go by @Nouryqt, Prefix: (), Commands: https://gist.github.com/lyx0/6e326451ed9602157fcf6b5e40fb1dfd"
nb.Send(target, reply)
}

View file

@ -1,20 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api"
)
// RandomNumber calls the numbers api with a random number.
func RandomNumber(channel string, nb *bot.Bot) {
reply := api.RandomNumber()
nb.Send(channel, string(reply))
}
// Number calls the numbers api with a given number.
func Number(channel, number string, nb *bot.Bot) {
reply := api.Number(number)
nb.Send(channel, string(reply))
}

View file

@ -1,15 +0,0 @@
package commands
import (
"fmt"
"net/url"
"github.com/lyx0/nourybot/cmd/bot"
)
// Osrs responds with a link to a given osrs wiki search.
func Osrs(target, term string, nb *bot.Bot) {
reply := fmt.Sprint("https://oldschool.runescape.wiki/?search=" + url.QueryEscape(term))
nb.Send(target, reply)
}

View file

@ -1,17 +0,0 @@
package personal
import "github.com/lyx0/nourybot/cmd/bot"
// Arch responds with a meme about Arch linux
func Arch(target string, nb *bot.Bot) {
reply := "Your friend isn't wrong. Being on the actual latest up to date software, having a single unified community repository for out of repo software (AUR) instead of a bunch of scattered broken PPAs for extra software, not having so many hard dependencies that removing GNOME removes basic system utilities, broader customization support and other things is indeed, pretty nice."
nb.Send(target, reply)
}
// ArchTwo responds with a meme about Arch linux
func ArchTwo(target string, nb *bot.Bot) {
reply := "One time I was ordering coffee and suddenly realised the barista didn't know I use Arch. Needless to say, I stopped mid-order to inform her that I do indeed use Arch. I must have spoken louder than I intended because the whole café instantly erupted into a prolonged applause. I walked outside with my head held high. I never did finish my order that day, but just knowing that everyone around me was aware that I use Arch was more energising than a simple cup of coffee could ever be."
nb.Send(target, reply)
}

View file

@ -1,9 +0,0 @@
package personal
import "github.com/lyx0/nourybot/cmd/bot"
// Farm responds with osrs wiki farming guide
func Farm(target string, nb *bot.Bot) {
nb.Send(target, "Trees: https://oldschool.runescape.wiki/w/Crop_running#Example_tree_run_sequence")
nb.Send(target, "Herbs: https://oldschool.runescape.wiki/w/Crop_running#Example_allotment,_flower_and_herb_run_sequence")
}

View file

@ -1,10 +0,0 @@
package personal
import "github.com/lyx0/nourybot/cmd/bot"
// Justinfan responds with the Jusinfan user chatterino has used before.
func Justinfan(target string, nb *bot.Bot) {
reply := "64537"
nb.Send(target, reply)
}

View file

@ -1,18 +0,0 @@
package personal
import "testing"
func TestCommands(t *testing.T) {
t.Run("personal package test placeholder", func(t *testing.T) {
request := 1
response := 1
got := request
want := response
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,13 +0,0 @@
package personal
import "github.com/lyx0/nourybot/cmd/bot"
// Rave returns youtube music playlists.
func Rave(target string, nb *bot.Bot) {
reply := `https://www.youtube.com/playlist?list=PLY9LTYa8xnQKrug3VvgkPWqmpmXSKAYPe`
reply2 := `https://www.youtube.com/playlist?list=PLWwCeXamjNuZ2ZNJiNwvdmVT9TtULsHc6`
nb.Send(target, reply)
nb.Send(target, reply2)
}

View file

@ -1,10 +0,0 @@
package personal
import "github.com/lyx0/nourybot/cmd/bot"
// Streamlink returns the streamlink config.
func Streamlink(target string, nb *bot.Bot) {
reply := `https://haste.zneix.eu/udajirixep put this in ~/.config/streamlink/config on Linux (or %appdata%\streamlink\streamlinkrc on Windows)`
nb.Send(target, reply)
}

View file

@ -1,10 +0,0 @@
package personal
import "github.com/lyx0/nourybot/cmd/bot"
// Xset returns the keyboard repeat rate command.
func Xset(target string, nb *bot.Bot) {
reply := "xset r rate 175 50"
nb.Send(target, reply)
}

View file

@ -1,10 +0,0 @@
package personal
import "github.com/lyx0/nourybot/cmd/bot"
// Zneix returns a meme about Zneix leaking stuff.
func Zneix(target string, nb *bot.Bot) {
reply := "ᵃᵈ⁴³ oh frick ⁵²⁴ᵈ ⁸⁹ᵈˢ ⁷⁵⁴⁹ ᶠᵈ³⁴ ᶦᵒ⁶⁸ frick sorry guys ᶠ⁷⁸ᶠ ᵈ⁹⁸⁹ ᵍ⁸²³ ᵍ⁹⁰ˣ ⁹ᵍᶠᵖ sorry im dropping ⁸⁹⁸⁴ ⁰⁹⁰ᶠ my api keys all over the ⁵³²ᵈ place ⁸⁷ᶠᵈ ⁹⁸⁴ᶠ ⁰⁹¹ᵃ sorry zneixApu zneixLeak"
nb.Send(target, reply)
}

View file

@ -3,17 +3,15 @@ package commands
import ( import (
"fmt" "fmt"
"github.com/lyx0/nourybot/cmd/bot" "github.com/gempir/go-twitch-irc/v3"
"github.com/lyx0/nourybot/pkg/common"
"github.com/lyx0/nourybot/pkg/humanize" "github.com/lyx0/nourybot/pkg/humanize"
"github.com/lyx0/nourybot/pkg/utils"
) )
// Ping responds with Pong and basic information about the bot. func Ping(target string, tc *twitch.Client) {
func Ping(target string, nb *bot.Bot) { botUptime := humanize.Time(common.GetUptime())
commandCount := fmt.Sprint(utils.GetCommandsUsed()) commandsUsed := common.GetCommandsUsed()
botUptime := humanize.Time(nb.Uptime)
reply := fmt.Sprintf("Pong! :) Commands used: %v, Last restart: %v", commandCount, botUptime) reply := fmt.Sprintf("Pong! :) Commands used: %v, Last restart: %v", commandsUsed, botUptime)
common.Send(target, reply, tc)
nb.Send(target, reply)
} }

View file

@ -1,14 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
)
// Pingme pings a user.
func Pingme(channel, user string, nb *bot.Bot) {
response := fmt.Sprintf("🔔 @%s 🔔", user)
nb.Send(channel, response)
}

View file

@ -1,20 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/ivr"
)
// ProfilePicture responds with a link to a given users Twitch Profilepicture.
func ProfilePicture(channel, target string, nb *bot.Bot) {
reply, err := ivr.ProfilePicture(target)
if err != nil {
nb.Send(channel, fmt.Sprint(err))
return
}
nb.Send(channel, reply)
}

View file

@ -1,52 +0,0 @@
package commands
// import (
// "fmt"
// "strconv"
// "strings"
// "github.com/lyx0/nourybot/cmd/bot"
// )
// func Pyramid(channel, size, emote string, nb *bot.Bot) {
// if size[0] == '.' || size[0] == '/' {
// nb.Send(channel, ":tf:")
// return
// }
// if emote[0] == '.' || emote[0] == '/' {
// nb.Send(channel, ":tf:")
// return
// }
// pyramidSize, err := strconv.Atoi(size)
// pyramidEmote := fmt.Sprint(emote + " ")
// if err != nil {
// nb.Send(channel, "Something went wrong")
// }
// if pyramidSize == 1 {
// nb.Send(channel, fmt.Sprint(pyramidEmote+"..."))
// return
// }
// if pyramidSize > 3 {
// nb.Send(channel, "Max pyramid size is 3")
// return
// }
// for i := 0; i <= pyramidSize; i++ {
// pyramidMessageAsc := strings.Repeat(pyramidEmote, i)
// // fmt.Println(pyramidMessageAsc)
// nb.Send(channel, pyramidMessageAsc)
// }
// for j := pyramidSize - 1; j >= 0; j-- {
// pyramidMessageDesc := strings.Repeat(pyramidEmote, j)
// // fmt.Println(pyramidMessageDesc)
// nb.Send(channel, pyramidMessageDesc)
// }
// }

View file

@ -1,14 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api"
)
// RandomCat calls the RandomCat api and responds with a link for a
// random cat image.
func RandomCat(channel string, nb *bot.Bot) {
reply := api.RandomCat()
nb.Send(channel, reply)
}

View file

@ -1,14 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api"
)
// RandomDog calls the RandomDog api and responds with a link for a
// random dog image.
func RandomDog(channel string, nb *bot.Bot) {
reply := api.RandomDog()
nb.Send(channel, reply)
}

View file

@ -1,14 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api"
)
// RandomDuck calls the RandomDuck api and responds with a link for a
// random duck image.
func RandomDuck(channel string, nb *bot.Bot) {
reply := api.RandomDuck()
nb.Send(channel, reply)
}

View file

@ -1,14 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api"
)
// RandomFox calls the RandomFox api and responds with a link for a
// random fox image.
func RandomFox(channel string, nb *bot.Bot) {
reply := api.RandomFox()
nb.Send(channel, reply)
}

View file

@ -1,21 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/ivr"
)
// RandomQuote calls the RandomQuote api and responds with a link for a
// random quote image.
func RandomQuote(channel, target, username string, nb *bot.Bot) {
ivrResponse, err := ivr.RandomQuote(target, username)
if err != nil {
nb.Send(channel, fmt.Sprint(err))
return
}
nb.Send(channel, ivrResponse)
}

View file

@ -1,14 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api"
)
// RandomXkcd calls the RandomXkcd api and responds with a link to a
// random xkcd comic.
func RandomXkcd(channel string, nb *bot.Bot) {
reply := api.RandomXkcd()
nb.Send(channel, reply)
}

View file

@ -1,16 +0,0 @@
package commands
import (
"fmt"
"github.com/gempir/go-twitch-irc/v2"
"github.com/lyx0/nourybot/cmd/bot"
)
// Robohash takes the message ID from the message and responds
// with the robohash image link for the message id.
func RoboHash(target string, message twitch.PrivateMessage, nb *bot.Bot) {
reply := fmt.Sprintf("https://robohash.org/%s", message.ID)
nb.Send(target, reply)
}

View file

@ -1,14 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
)
// SevenTV responds with a link to the 7tv search for a given emote.
func SevenTV(target, emote string, nb *bot.Bot) {
reply := fmt.Sprintf("https://7tv.app/emotes?query=%s", emote)
nb.Send(target, reply)
}

View file

@ -1,21 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/ivr"
)
// Subage responds with the time a given user has been subscribed
// to a given channel.
func Subage(channel, username, streamer string, nb *bot.Bot) {
ivrResponse, err := ivr.Subage(username, streamer)
if err != nil {
nb.Send(channel, fmt.Sprint(err))
return
}
nb.Send(channel, ivrResponse)
}

View file

@ -1,18 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/utils"
)
// Thumbnail responds with the current preview image for a given channel.
func Thumbnail(channel, target string, nb *bot.Bot) {
imageHeight := utils.GenerateRandomNumberRange(1040, 1080)
imageWidth := utils.GenerateRandomNumberRange(1890, 1920)
response := fmt.Sprintf("https://static-cdn.jtvnw.net/previews-ttv/live_user_%v-%vx%v.jpg", target, imageWidth, imageHeight)
nb.Send(channel, response)
}

View file

@ -1,23 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/aiden"
log "github.com/sirupsen/logrus"
)
// Title responds with the stream title for a given channel.
func Title(channel, target string, nb *bot.Bot) {
title, err := aiden.ApiCall(fmt.Sprintf("api/v1/twitch/channel/%s/title", target))
if err != nil {
nb.Send(channel, "Something went wrong FeelsBadMan")
log.Error(err)
return
}
reply := fmt.Sprintf("%s title is: %s", target, title)
nb.Send(channel, reply)
}

20
pkg/commands/tweet.go Normal file
View file

@ -0,0 +1,20 @@
package commands
import (
"github.com/gempir/go-twitch-irc/v3"
"github.com/lyx0/nourybot/pkg/common"
"github.com/lyx0/nourybot/pkg/decapi"
"go.uber.org/zap"
)
func Tweet(target, username string, tc *twitch.Client) {
sugar := zap.NewExample().Sugar()
defer sugar.Sync()
resp, err := decapi.Tweet(username)
if err != nil {
sugar.Error(err)
}
common.Send(target, resp, tc)
}

View file

@ -1,22 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/aiden"
log "github.com/sirupsen/logrus"
)
// Uptime responds with the time a given channel has been live.
func Uptime(channel, name string, nb *bot.Bot) {
resp, err := aiden.ApiCall(fmt.Sprintf("api/v1/twitch/channel/%s/uptime", name))
if err != nil {
nb.Send(channel, "Something went wrong FeelsBadMan")
log.Error(err)
return
}
nb.Send(channel, resp)
}

View file

@ -1,13 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/ivr"
)
// Userid responds with the userid for a given user.
func Userid(channel, target string, nb *bot.Bot) {
reply := ivr.Userid(target)
nb.Send(channel, reply)
}

View file

@ -1,23 +0,0 @@
package commands
import (
"fmt"
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/aiden"
log "github.com/sirupsen/logrus"
)
// Weather calls the weather api for a given location and responds
// with the current weather data.
func Weather(channel, location string, nb *bot.Bot) {
reply, err := aiden.ApiCall(fmt.Sprintf("api/v1/misc/weather/%s", location))
if err != nil {
nb.Send(channel, "Something went wrong FeelsBadMan")
log.Error(err)
return
}
nb.Send(channel, reply)
}

View file

@ -1,13 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api/ivr"
)
// Whois responds with information about a given users Twitch account.
func Whois(target, user string, nb *bot.Bot) {
reply := ivr.Whois(user)
nb.Send(target, reply)
}

View file

@ -1,8 +0,0 @@
package commands
import "github.com/lyx0/nourybot/cmd/bot"
// Xd responds with a xd
func Xd(channel string, nb *bot.Bot) {
nb.Send(channel, "xd")
}

View file

@ -1,14 +0,0 @@
package commands
import (
"github.com/lyx0/nourybot/cmd/bot"
"github.com/lyx0/nourybot/pkg/api"
)
// Xkcd responds with a link to the current xkcd comic and information about i.
func Xkcd(channel string, nb *bot.Bot) {
reply := api.Xkcd()
nb.Send(channel, reply)
}

16
pkg/common/counter.go Normal file
View file

@ -0,0 +1,16 @@
package common
var (
tempCommands = 0
)
// CommandUsed is called on every command incremenenting tempCommands.
func CommandUsed() {
tempCommands++
}
// GetCommandsUsed returns the amount of commands that have been used
// since the last restart.
func GetCommandsUsed() int {
return tempCommands
}

134
pkg/common/send.go Normal file
View file

@ -0,0 +1,134 @@
package common
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"github.com/gempir/go-twitch-irc/v3"
"go.uber.org/zap"
)
// banphraseResponse is the data we receive back from
// the banphrase API
type banphraseResponse struct {
Banned bool `json:"banned"`
InputMessage string `json:"input_message"`
BanphraseData banphraseData `json:"banphrase_data"`
}
// banphraseData contains details about why a message
// was banphrased.
type banphraseData struct {
Id int `json:"id"`
Name string `json:"name"`
Phrase string `json:"phrase"`
Length int `json:"length"`
Permanent bool `json:"permanent"`
}
var (
banPhraseUrl = "https://pajlada.pajbot.com/api/v1/banphrases/test"
)
// CheckMessage checks a given message against the banphrase api.
// returns false, "okay" if a message is allowed
// returns true and a string with the reason if it was banned.
// More information:
// https://gist.github.com/pajlada/57464e519ba8d195a97ddcd0755f9715
func checkMessage(text string) (bool, string) {
sugar := zap.NewExample().Sugar()
defer sugar.Sync()
// {"message": "AHAHAHAHA LUL"}
reqBody, err := json.Marshal(map[string]string{
"message": text,
})
if err != nil {
log.Fatal(err)
}
resp, err := http.Post(banPhraseUrl, "application/json", bytes.NewBuffer(reqBody))
if err != nil {
log.Panic(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Panic(err)
}
var responseObject banphraseResponse
json.Unmarshal(body, &responseObject)
// Bad Message
//
// {"phrase": "No gyazo allowed"}
reason := responseObject.BanphraseData.Name
if responseObject.Banned {
return true, fmt.Sprint(reason)
} else if !responseObject.Banned {
// Good message
return false, "okay"
}
// Couldn't contact api so assume it was a bad message
return true, "Banphrase API couldn't be reached monkaS"
}
// Send is used to send twitch replies and contains the necessary
// safeguards and logic for that.
func Send(target, message string, tc *twitch.Client) {
sugar := zap.NewExample().Sugar()
defer sugar.Sync()
// Message we are trying to send is empty.
if len(message) == 0 {
return
}
// Since messages starting with `.` or `/` are used for special actions
// (ban, whisper, timeout) and so on, we place an emote infront of it so
// the actions wouldn't execute. `!` and `$` are common bot prefixes so we
// don't allow them either.
if message[0] == '.' || message[0] == '/' || message[0] == '!' || message[0] == '$' {
message = ":tf: " + message
}
// check the message for bad words before we say it
messageBanned, banReason := checkMessage(message)
if messageBanned {
// Bad message, replace message and log it.
tc.Say(target, "[BANPHRASED] monkaS")
sugar.Infow("banned message detected",
"target channel", target,
"message", message,
"ban reason", banReason,
)
return
} else {
// In case the message we are trying to send is longer than the
// maximum allowed message length on twitch we split the message in two parts.
// Twitch has a maximum length for messages of 510 characters so to be safe
// we split and check at 500 characters.
// https://discuss.dev.twitch.tv/t/missing-client-side-message-length-check/21316
if len(message) > 500 {
firstMessage := message[0:499]
secondMessage := message[499:]
tc.Say(target, firstMessage)
tc.Say(target, secondMessage)
return
}
// Message was fine.
tc.Say(target, message)
return
}
}

15
pkg/common/uptime.go Normal file
View file

@ -0,0 +1,15 @@
package common
import "time"
var (
uptime time.Time
)
func StartTime() {
uptime = time.Now()
}
func GetUptime() time.Time {
return uptime
}

View file

@ -1,40 +0,0 @@
package config
import (
"os"
"github.com/joho/godotenv"
log "github.com/sirupsen/logrus"
)
type Config struct {
Username string
Oauth string
BotUserId string
MongoURI string
}
func LoadConfig() *Config {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env")
}
cfg := &Config{
Username: os.Getenv("TWITCH_USER"),
Oauth: os.Getenv("TWITCH_PASS"),
BotUserId: os.Getenv("BOT_USER_ID"),
MongoURI: os.Getenv("MONGO_URI"),
}
log.Info("Config loaded succesfully")
return cfg
}
// Only for tests
func LoadConfigTest() {
os.Setenv("TEST_VALUE", "xDLUL420")
// defer os.Unsetenv("TEST_VALUE")
}

View file

@ -1,20 +0,0 @@
package config
import (
"os"
"testing"
)
func TestLoadConfig(t *testing.T) {
t.Run("loads a testing value from the .env file", func(t *testing.T) {
LoadConfigTest()
got := os.Getenv("TEST_VALUE")
want := "xDLUL420"
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View file

@ -1,32 +0,0 @@
package db
import (
"context"
"fmt"
"time"
"github.com/lyx0/nourybot/cmd/bot"
log "github.com/sirupsen/logrus"
)
// AddChannel adds a given channel name to the database.
func AddChannel(target, channelName string, nb *bot.Bot) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
collection := nb.MongoClient.Database("nourybot").Collection("channels")
// Channel
// {{"name": string}, {"connect": bool}}
chnl := Channel{channelName, true}
_, insertErr := collection.InsertOne(ctx, chnl)
if insertErr != nil {
nb.Send(target, fmt.Sprintf("Error trying to join %s", channelName))
log.Error(insertErr)
return
}
nb.TwitchClient.Join(channelName)
nb.Send(target, fmt.Sprintf("Joined %s", channelName))
}

View file

@ -1,46 +0,0 @@
package db
import (
"context"
"time"
"github.com/lyx0/nourybot/pkg/config"
log "github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// Connect connects the the MongoDB database through a supplied cfg
// and returns a *mongo.Client
func Connect(cfg *config.Config) *mongo.Client {
client, err := mongo.NewClient(options.Client().ApplyURI(cfg.MongoURI))
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err = client.Connect(ctx)
if err != nil {
log.Fatal(err)
}
// defer client.Disconnect(ctx)
err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
log.Info("Connected to MongoDB!")
databases, err := client.ListDatabaseNames(ctx, bson.M{})
if err != nil {
log.Fatal(err)
}
_ = databases
// log.Info(databases)
return client
}

Some files were not shown because too many files have changed in this diff Show more