Initial commit

Signed-off-by: Etzelia <etzelia@hotmail.com>
main
Etzelia 2021-07-12 16:27:30 -05:00
commit 6091f30610
No known key found for this signature in database
GPG Key ID: 708511AE7ABC5314
10 changed files with 297 additions and 0 deletions

5
.gitignore vendored 100644
View File

@ -0,0 +1,5 @@
# GoLand
.idea/
# Binaries
stonebot*

7
LICENSE 100644
View File

@ -0,0 +1,7 @@
Copyright 2021 Etzelia
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

7
README.md 100644
View File

@ -0,0 +1,7 @@
# stonebot
ROCKS
## License
[MIT](LICENSE)

6
assets/assets.go 100644
View File

@ -0,0 +1,6 @@
package assets
import _ "embed"
//go:embed rocks.gif
var RocksGif []byte

BIN
assets/rocks.gif 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@ -0,0 +1,69 @@
package database
import (
"errors"
"os"
"strconv"
"go.etcd.io/bbolt"
)
var (
stonelandBucket = []byte("stoneland")
stonesKey = []byte("stones")
)
type Database struct {
db *bbolt.DB
}
func New(dbPath string) (*Database, error) {
db, err := bbolt.Open(dbPath, os.ModePerm, bbolt.DefaultOptions)
if err != nil {
return nil, err
}
return &Database{
db: db,
}, db.Update(func(tx *bbolt.Tx) error {
_, err := tx.CreateBucketIfNotExists(stonelandBucket)
return err
})
}
func (db *Database) Close() error {
return db.db.Close()
}
func (db *Database) Rock(num int) error {
return db.db.Update(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stonelandBucket)
if bucket == nil {
return errors.New("bucket for stones was not created")
}
stones, err := db.rocks(tx)
if err != nil {
return err
}
stones += num
return bucket.Put(stonesKey, []byte(strconv.Itoa(stones)))
})
}
func (db *Database) Rocks() (count int, err error) {
return count, db.db.View(func(tx *bbolt.Tx) error {
count, err = db.rocks(tx)
return err
})
}
func (db *Database) rocks(tx *bbolt.Tx) (int, error) {
bucket := tx.Bucket(stonelandBucket)
if bucket == nil {
return -1, errors.New("bucket for stones was not created")
}
stones := bucket.Get(stonesKey)
if stones == nil {
return 0, nil
}
return strconv.Atoi(string(stones))
}

88
discord/discord.go 100644
View File

@ -0,0 +1,88 @@
package discord
import (
"bytes"
"fmt"
"strings"
"git.canopymc.net/Etzelia/stonebot/assets"
"git.canopymc.net/Etzelia/stonebot/database"
"github.com/bwmarrin/discordgo"
"go.jolheiser.com/beaver"
)
func New(token, prefix string, db *database.Database) (*discordgo.Session, error) {
bot, err := discordgo.New("Bot " + token)
if err != nil {
return nil, err
}
bot.Identify.Intents = discordgo.IntentsAllWithoutPrivileged
bot.AddHandler(CommandHandler(prefix, db))
bot.AddHandler(MessageHandler(prefix, db))
return bot, nil
}
func CommandHandler(prefix string, db *database.Database) func(session *discordgo.Session, message *discordgo.MessageCreate) {
return func(session *discordgo.Session, message *discordgo.MessageCreate) {
// Ignore non-prefix
if !strings.HasPrefix(message.Content, prefix) {
return
}
// Ignore bots
if message.Author.Bot {
return
}
args := strings.Fields(message.Content[1:])
switch strings.ToLower(args[0]) {
case "rocks", "stones":
rocks, err := db.Rocks()
if err != nil {
beaver.Errorf("could not count rocks: %v", err)
return
}
if _, err := session.ChannelMessageSend(message.ChannelID, fmt.Sprintf("%d ROCKS", rocks)); err != nil {
beaver.Errorf("could not send message: %v", err)
return
}
}
}
}
func MessageHandler(prefix string, db *database.Database) func(session *discordgo.Session, message *discordgo.MessageCreate) {
return func(session *discordgo.Session, message *discordgo.MessageCreate) {
// Ignore prefix
if strings.HasPrefix(message.Content, prefix) {
return
}
// Ignore bots
if message.Author.Bot {
return
}
rocks := strings.Count(message.Content, "rock")
rocks += strings.Count(message.Content, "stone")
if err := db.Rock(rocks); err != nil {
beaver.Errorf("could not add rocks: %v", err)
}
if rocks > 0 {
if _, err := session.ChannelMessageSendComplex(message.ChannelID, &discordgo.MessageSend{
Files: []*discordgo.File{
{
Name: "rocks.gif",
ContentType: "multipart/form-data",
Reader: bytes.NewReader(assets.RocksGif),
},
},
}); err != nil {
beaver.Errorf("could not send rocks gif: %v", err)
}
}
}
}

13
go.mod 100644
View File

@ -0,0 +1,13 @@
module git.canopymc.net/Etzelia/stonebot
go 1.16
require (
github.com/bwmarrin/discordgo v0.23.2
github.com/gorilla/websocket v1.4.2 // indirect
github.com/peterbourgon/ff/v3 v3.1.0
go.etcd.io/bbolt v1.3.6
go.jolheiser.com/beaver v1.1.2
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 // indirect
)

32
go.sum 100644
View File

@ -0,0 +1,32 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
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/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/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
github.com/peterbourgon/ff/v3 v3.1.0 h1:5JAeDK5j/zhKFjyHEZQXwXBoDijERaos10RE+xamOsY=
github.com/peterbourgon/ff/v3 v3.1.0/go.mod h1:XNJLY8EIl6MjMVjBS4F0+G0LYoAqs0DTa4rmHHukKDE=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.jolheiser.com/beaver v1.1.2 h1:X8voMSTy+8QUFzHlvG89EUVZY/xPQe6fQLVjUPjTvWY=
go.jolheiser.com/beaver v1.1.2/go.mod h1:7X4F5+XOGSC3LejTShoBdqtRCnPWcnRgmYGmG3EKW8g=
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-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 h1:5jaG59Zhd+8ZXe8C+lgiAGqkOaZBruqrWclLkgAww34=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

70
main.go 100644
View File

@ -0,0 +1,70 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"git.canopymc.net/Etzelia/stonebot/database"
"git.canopymc.net/Etzelia/stonebot/discord"
"github.com/peterbourgon/ff/v3"
"github.com/peterbourgon/ff/v3/fftoml"
"go.jolheiser.com/beaver"
"go.jolheiser.com/beaver/color"
)
func main() {
fs := flag.NewFlagSet("stonebot", flag.ExitOnError)
fs.String("config", "stonebot.toml", "Path to config file")
verboseFlag := fs.Bool("verbose", false, "Verbose logging")
tokenFlag := fs.String("token", "", "Discord bot token")
prefixFlag := fs.String("prefix", "!", "Discord bot prefix")
dbFlag := fs.String("db", "stonebot.db", "Path to DB")
if err := ff.Parse(fs, os.Args[1:],
ff.WithEnvVarPrefix("STONEBOT"),
ff.WithConfigFileFlag("config"),
ff.WithConfigFileParser(fftoml.Parser),
ff.WithAllowMissingConfigFile(true),
); err != nil {
beaver.Fatal(err)
}
beaver.Console.Format = beaver.FormatOptions{
TimePrefix: true,
StackPrefix: true,
StackLimit: 15,
LevelPrefix: true,
LevelColor: true,
}
color.Fatal = color.Error
if *tokenFlag == "" {
beaver.Fatal("discord token is required")
}
if *verboseFlag {
beaver.Console.Level = beaver.DEBUG
}
db, err := database.New(*dbFlag)
if err != nil {
beaver.Fatalf("could not connect to database: %v", err)
}
defer db.Close()
bot, err := discord.New(*tokenFlag, *prefixFlag, db)
if err != nil {
beaver.Fatalf("could not run bot: %v", err)
}
defer bot.Close()
if err := bot.Open(); err != nil {
beaver.Fatalf("could not open bot connection: %v", err)
}
beaver.Info("Bot is now running. Press CTRL-C to exit.")
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-sc
}