Upload image of board on mention

This commit is contained in:
ekzyis 2024-09-23 02:55:55 +02:00
parent e6b7582ae9
commit 7eea9d932f
8 changed files with 253 additions and 10 deletions

3
.env.sample Normal file
View File

@ -0,0 +1,3 @@
SN_BASE_URL=
SN_API_TOKEN=
SN_MEDIA_URL=

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
*.png
!assets/*.png
.env
*.sqlite3

View File

@ -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 {

66
db/db.go Normal file
View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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)
}
}

64
sn/sn.go Normal file
View File

@ -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)
}
}