Upload image of board on mention
This commit is contained in:
parent
e6b7582ae9
commit
7eea9d932f
|
@ -0,0 +1,3 @@
|
|||
SN_BASE_URL=
|
||||
SN_API_TOKEN=
|
||||
SN_MEDIA_URL=
|
|
@ -1,2 +1,4 @@
|
|||
*.png
|
||||
!assets/*.png
|
||||
.env
|
||||
*.sqlite3
|
||||
|
|
|
@ -55,15 +55,20 @@ func NewBoard() *Board {
|
|||
return board
|
||||
}
|
||||
|
||||
func NewGame(move string) (*Board, error) {
|
||||
board := NewBoard()
|
||||
|
||||
if err := board.Move(move); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return board, nil
|
||||
}
|
||||
|
||||
func (b *Board) Save(filename string) error {
|
||||
var (
|
||||
file *os.File
|
||||
img *image.RGBA
|
||||
piece *Piece
|
||||
bg *image.Uniform
|
||||
rect image.Rectangle
|
||||
p = image.Point{0, 0}
|
||||
err error
|
||||
file *os.File
|
||||
err error
|
||||
)
|
||||
|
||||
if file, err = os.Create(filename); err != nil {
|
||||
|
@ -71,6 +76,18 @@ func (b *Board) Save(filename string) error {
|
|||
}
|
||||
defer file.Close()
|
||||
|
||||
return png.Encode(file, b.Image())
|
||||
}
|
||||
|
||||
func (b *Board) Image() *image.RGBA {
|
||||
var (
|
||||
img *image.RGBA
|
||||
piece *Piece
|
||||
bg *image.Uniform
|
||||
rect image.Rectangle
|
||||
p = image.Point{0, 0}
|
||||
)
|
||||
|
||||
img = image.NewRGBA(image.Rect(0, 0, 1024, 1024))
|
||||
|
||||
for yi := 0; yi < 8; yi++ {
|
||||
|
@ -86,7 +103,7 @@ func (b *Board) Save(filename string) error {
|
|||
}
|
||||
}
|
||||
|
||||
return png.Encode(file, img)
|
||||
return img
|
||||
}
|
||||
|
||||
func (b *Board) SetPiece(name PieceName, color Color, position string) error {
|
||||
|
@ -187,6 +204,8 @@ func (b *Board) Move(position string) error {
|
|||
default:
|
||||
err = fmt.Errorf("invalid move: %s", position)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("invalid move: %s", position)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
|
||||
sn "github.com/ekzyis/snappy"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
var (
|
||||
db = getDb()
|
||||
)
|
||||
|
||||
func getDb() *sql.DB {
|
||||
var (
|
||||
db *sql.DB
|
||||
err error
|
||||
)
|
||||
if db, err = sql.Open("sqlite3", "chessbot.sqlite3?_foreign_keys=on"); err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
if err = migrate(db); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func migrate(db *sql.DB) error {
|
||||
_, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS items (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
text TEXT NOT NULL,
|
||||
parent_id INTEGER REFERENCES items(id),
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func ItemHasReply(parentId int, userId int) (bool, error) {
|
||||
var (
|
||||
count int
|
||||
err error
|
||||
)
|
||||
|
||||
if err = db.QueryRow(`SELECT COUNT(1) FROM items WHERE parent_id = ? AND user_id = ?`, parentId, userId).Scan(&count); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func InsertItem(item *sn.Item) error {
|
||||
if _, err := db.Exec(
|
||||
`INSERT INTO items(id, user_id, text, parent_id) VALUES (?, ?, ?, NULLIF(?, 0))`,
|
||||
item.Id, item.User.Id, item.Text, item.ParentId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
3
go.mod
3
go.mod
|
@ -3,6 +3,8 @@ module github.com/ekzyis/chessbot
|
|||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/ekzyis/snappy v0.5.5-0.20240923014110-b880c1256e13
|
||||
github.com/mattn/go-sqlite3 v1.14.23
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/image v0.20.0
|
||||
)
|
||||
|
@ -10,5 +12,6 @@ require (
|
|||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
gopkg.in/guregu/null.v4 v4.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
6
go.sum
6
go.sum
|
@ -1,5 +1,9 @@
|
|||
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/ekzyis/snappy v0.5.5-0.20240923014110-b880c1256e13 h1:yxDqMo4HzCVzhxPNTBpST0KVVHVMwGouzwfXsb4WYw4=
|
||||
github.com/ekzyis/snappy v0.5.5-0.20240923014110-b880c1256e13/go.mod h1:UksYI0dU0+cnzz0LQjWB1P0QQP/ghx47e4atP99a5Lk=
|
||||
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
|
||||
github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
|
@ -8,5 +12,7 @@ golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
|
|||
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg=
|
||||
gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
84
main.go
84
main.go
|
@ -1,13 +1,93 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ekzyis/chessbot/chess"
|
||||
"github.com/ekzyis/chessbot/db"
|
||||
"github.com/ekzyis/chessbot/sn"
|
||||
)
|
||||
|
||||
var (
|
||||
c = sn.GetClient()
|
||||
// TODO: fetch our id from SN API
|
||||
meId = 21858
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
for {
|
||||
tickGameStart(c)
|
||||
// TODO: implement game progress
|
||||
// tickGameProgress(c)
|
||||
time.Sleep(1 * time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
func tickGameStart(c *sn.Client) {
|
||||
var (
|
||||
b = chess.NewBoard()
|
||||
mentions []sn.Notification
|
||||
err error
|
||||
)
|
||||
|
||||
b.Save("board.png")
|
||||
if mentions, err = c.Mentions(); err != nil {
|
||||
log.Printf("mentions error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("fetched %d mention(s)\n", len(mentions))
|
||||
|
||||
for _, n := range mentions {
|
||||
|
||||
if exists, err := db.ItemHasReply(n.Item.Id, meId); err != nil {
|
||||
log.Printf("error during reply check: %v\n", err)
|
||||
continue
|
||||
} else if exists {
|
||||
// TODO: check if move changed
|
||||
log.Printf("reply already exists: id=%d\n", n.Item.Id)
|
||||
continue
|
||||
}
|
||||
|
||||
move := strings.Trim(strings.ReplaceAll(n.Item.Text, "@chess", ""), " ")
|
||||
|
||||
var b *chess.Board
|
||||
if b, err = chess.NewGame(move); err != nil {
|
||||
log.Printf("error creating new game: %v: id=%d\n", err, n.Item.Id)
|
||||
continue
|
||||
}
|
||||
|
||||
img := b.Image()
|
||||
var imgUrl string
|
||||
if imgUrl, err = c.UploadImage(img); err != nil {
|
||||
log.Printf("error uploading image: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var cId int
|
||||
parentId := n.Item.Id
|
||||
if cId, err = c.CreateComment(parentId, imgUrl); err != nil {
|
||||
log.Printf("error creating reply: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
n.Item.ParentId = 0
|
||||
if err = db.InsertItem(&n.Item); err != nil {
|
||||
log.Printf("error inserting item into db: %v: id=%d\n", err, n.Item.Id)
|
||||
continue
|
||||
}
|
||||
|
||||
var item *sn.Item
|
||||
if item, err = c.Item(cId); err != nil {
|
||||
log.Printf("error fetching item: %v: id=%d\n", cId)
|
||||
continue
|
||||
}
|
||||
if err = db.InsertItem(item); err != nil {
|
||||
log.Printf("error inserting item into db: %v: id=%d\n", err, item.Id)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("started new game: id=%d\n", n.Item.Id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package sn
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
snappy "github.com/ekzyis/snappy"
|
||||
)
|
||||
|
||||
type Client = snappy.Client
|
||||
type Notification = snappy.Notification
|
||||
type Item = snappy.Item
|
||||
|
||||
var (
|
||||
c *Client
|
||||
)
|
||||
|
||||
func GetClient() *Client {
|
||||
loadEnv()
|
||||
|
||||
if c == nil {
|
||||
c = snappy.NewClient(
|
||||
snappy.WithBaseUrl(os.Getenv("SN_BASE_URL")),
|
||||
snappy.WithApiKey(os.Getenv("SN_API_KEY")),
|
||||
snappy.WithMediaUrl(os.Getenv("SN_MEDIA_URL")),
|
||||
)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func loadEnv() {
|
||||
var (
|
||||
f *os.File
|
||||
s *bufio.Scanner
|
||||
err error
|
||||
)
|
||||
|
||||
if f, err = os.Open(".env"); err != nil {
|
||||
log.Fatalf("error opening .env: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s = bufio.NewScanner(f)
|
||||
s.Split(bufio.ScanLines)
|
||||
for s.Scan() {
|
||||
line := s.Text()
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
|
||||
// Check if we have exactly 2 parts (key and value)
|
||||
if len(parts) == 2 {
|
||||
os.Setenv(parts[0], parts[1])
|
||||
} else {
|
||||
log.Fatalf(".env: invalid line: %s\n", line)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for errors during scanning
|
||||
if err = s.Err(); err != nil {
|
||||
fmt.Println("error scanning .env:", err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue