Skip to content

Commit a4e812a

Browse files
committed
gelbooru: New plugin
1 parent fe262a0 commit a4e812a

File tree

10 files changed

+644
-0
lines changed

10 files changed

+644
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ result
66
.direnv/
77
http-client.private.env.json
88
files/
9+
gobot

bot/bot.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/Brawl345/gobot/plugin/delmsg"
2626
"github.com/Brawl345/gobot/plugin/echo"
2727
"github.com/Brawl345/gobot/plugin/expand"
28+
"github.com/Brawl345/gobot/plugin/gelbooru"
2829
"github.com/Brawl345/gobot/plugin/gemini"
2930
"github.com/Brawl345/gobot/plugin/getfile"
3031
"github.com/Brawl345/gobot/plugin/google_images"
@@ -109,6 +110,8 @@ func New(db *sqlx.DB) (*Gobot, error) {
109110
geminiService := sql.NewGeminiService(db)
110111
googleImagesService := sql.NewGoogleImagesService(db)
111112
googleImagesCleanupService := sql.NewGoogleImagesCleanupService(db)
113+
gelbooruService := sql.NewGelbooruService(db)
114+
gelbooruCleanupService := sql.NewGelbooruCleanupService(db)
112115
homeService := sql.NewHomeService(db)
113116
notifyService := sql.NewNotifyService(db)
114117
quoteService := sql.NewQuoteService(db)
@@ -130,6 +133,7 @@ func New(db *sqlx.DB) (*Gobot, error) {
130133
delmsg.New(),
131134
echo.New(),
132135
expand.New(),
136+
gelbooru.New(credentialService, gelbooruService, gelbooruCleanupService),
133137
gemini.New(credentialService, geminiService),
134138
getfile.New(credentialService, fileService),
135139
google_images.New(credentialService, googleImagesService, googleImagesCleanupService),

model/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ import "errors"
55
var (
66
ErrAlreadyExists = errors.New("record already exists")
77
ErrNotFound = errors.New("record not found")
8+
ErrQueryNotFound = errors.New("query not found")
89
)

model/gelbooru.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package model
2+
3+
type GelbooruService interface {
4+
GetQuery(queryID int64) (string, error)
5+
SaveQuery(query string) (int64, error)
6+
}

model/sql/gelbooru.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package sql
2+
3+
import (
4+
"database/sql"
5+
"strings"
6+
7+
"github.com/Brawl345/gobot/logger"
8+
"github.com/Brawl345/gobot/model"
9+
"github.com/jmoiron/sqlx"
10+
)
11+
12+
type gelbooruService struct {
13+
*sqlx.DB
14+
log *logger.Logger
15+
}
16+
17+
func NewGelbooruService(db *sqlx.DB) *gelbooruService {
18+
return &gelbooruService{
19+
DB: db,
20+
log: logger.New("gelbooruService"),
21+
}
22+
}
23+
24+
func (db *gelbooruService) GetQuery(queryID int64) (string, error) {
25+
const selectQuery = `SELECT query FROM gelbooru_queries WHERE id = ?`
26+
var query string
27+
err := db.Get(&query, selectQuery, queryID)
28+
if err != nil {
29+
if err == sql.ErrNoRows {
30+
return "", model.ErrQueryNotFound
31+
}
32+
return "", err
33+
}
34+
return query, nil
35+
}
36+
37+
func (db *gelbooruService) SaveQuery(query string) (int64, error) {
38+
query = strings.ToLower(query)
39+
40+
const selectQuery = `SELECT id FROM gelbooru_queries WHERE query = ?`
41+
var existingID int64
42+
err := db.Get(&existingID, selectQuery, query)
43+
if err == nil {
44+
return existingID, nil
45+
}
46+
if err != sql.ErrNoRows {
47+
return 0, err
48+
}
49+
50+
const insertQuery = `INSERT INTO gelbooru_queries (query) VALUES (?)`
51+
res, err := db.Exec(insertQuery, query)
52+
if err != nil {
53+
return 0, err
54+
}
55+
56+
lastInsertID, err := res.LastInsertId()
57+
if err != nil {
58+
return 0, err
59+
}
60+
61+
return lastInsertID, nil
62+
}

model/sql/gelbooru_cleanup.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package sql
2+
3+
import (
4+
"github.com/Brawl345/gobot/logger"
5+
"github.com/jmoiron/sqlx"
6+
)
7+
8+
type gelbooruCleanupService struct {
9+
*sqlx.DB
10+
log *logger.Logger
11+
}
12+
13+
func NewGelbooruCleanupService(db *sqlx.DB) *gelbooruCleanupService {
14+
return &gelbooruCleanupService{
15+
DB: db,
16+
log: logger.New("gelbooruCleanupService"),
17+
}
18+
}
19+
20+
func (db *gelbooruCleanupService) Cleanup() error {
21+
const query = `DELETE FROM gelbooru_queries WHERE created_at < NOW() - INTERVAL 7 DAY`
22+
_, err := db.Exec(query)
23+
return err
24+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- +migrate Up
2+
3+
CREATE TABLE `gelbooru_queries`
4+
(
5+
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
6+
`created_at` DATETIME NOT NULL DEFAULT current_timestamp(),
7+
`query` VARCHAR(2048) NOT NULL,
8+
INDEX `query` (`query`)
9+
) COLLATE = 'utf8mb4_general_ci'
10+
ENGINE = InnoDB;

plugin/gelbooru/api.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package gelbooru
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
const (
9+
PostURL = "https://gelbooru.com/index.php?page=post&s=view&id=%d"
10+
)
11+
12+
type (
13+
Post struct {
14+
Id int `json:"id"`
15+
CreatedAt string `json:"created_at"`
16+
Score int `json:"score"`
17+
Width int `json:"width"`
18+
Height int `json:"height"`
19+
Md5 string `json:"md5"`
20+
Directory string `json:"directory"`
21+
Image string `json:"image"`
22+
Rating string `json:"rating"`
23+
Source string `json:"source"`
24+
Change int `json:"change"`
25+
Owner string `json:"owner"`
26+
CreatorId int `json:"creator_id"`
27+
ParentId int `json:"parent_id"`
28+
Sample int `json:"sample"`
29+
PreviewHeight int `json:"preview_height"`
30+
PreviewWidth int `json:"preview_width"`
31+
Tags string `json:"tags"`
32+
Title string `json:"title"`
33+
HasNotes string `json:"has_notes"`
34+
HasComments string `json:"has_comments"`
35+
FileUrl string `json:"file_url"`
36+
PreviewUrl string `json:"preview_url"`
37+
SampleUrl string `json:"sample_url"`
38+
SampleHeight int `json:"sample_height"`
39+
SampleWidth int `json:"sample_width"`
40+
Status string `json:"status"`
41+
PostLocked int `json:"post_locked"`
42+
HasChildren string `json:"has_children"`
43+
}
44+
45+
Response struct {
46+
Attributes struct {
47+
Limit int `json:"limit"`
48+
Offset int `json:"offset"`
49+
Count int `json:"count"`
50+
} `json:"@attributes"`
51+
Post []Post `json:"post"`
52+
}
53+
)
54+
55+
func (p *Post) FileURL() string {
56+
if p.SampleUrl != "" {
57+
return p.SampleUrl
58+
}
59+
return p.FileUrl
60+
}
61+
62+
func (p *Post) DirectURL() string {
63+
if p.FileUrl != "" {
64+
return p.FileUrl
65+
}
66+
return p.SampleUrl
67+
}
68+
69+
func (p *Post) IsNSFW() bool {
70+
if p.Rating == "questionable" || p.Rating == "explicit" {
71+
return true
72+
}
73+
return false
74+
}
75+
76+
func (p *Post) Caption() string {
77+
var sb strings.Builder
78+
79+
sb.WriteString(fmt.Sprintf("🔗 <a href=\"%s\">Post #%d</a> - ", p.PostURL(), p.Id))
80+
sb.WriteString(fmt.Sprintf("🖼️ <a href=\"%s\">Direktlink</a>", p.DirectURL()))
81+
if p.Source != "" {
82+
sb.WriteString(fmt.Sprintf(" - 🌐 <a href=\"%s\">Quelle</a>\n", p.Source))
83+
}
84+
85+
return sb.String()
86+
}
87+
88+
// AltCaption is used when the media is too big or invalid type
89+
func (p *Post) AltCaption() string {
90+
var sb strings.Builder
91+
92+
sb.WriteString(fmt.Sprintf("%s\n", p.DirectURL()))
93+
if p.IsNSFW() {
94+
sb.WriteString("️🔞 <b>NSFW</b> - ")
95+
}
96+
sb.WriteString(fmt.Sprintf("🔗 <a href=\"%s\">Post #%d</a>", p.PostURL(), p.Id))
97+
if p.Source != "" {
98+
sb.WriteString(fmt.Sprintf(" - 🌐 <a href=\"%s\">Quelle</a>\n", p.Source))
99+
}
100+
101+
return sb.String()
102+
}
103+
104+
func (p *Post) PostURL() string {
105+
return fmt.Sprintf(PostURL, p.Id)
106+
}
107+
108+
func (p *Post) IsImage() bool {
109+
if strings.HasSuffix(p.FileURL(), ".jpg") ||
110+
strings.HasSuffix(p.FileURL(), ".jpeg") ||
111+
strings.HasSuffix(p.FileURL(), ".png") ||
112+
strings.HasSuffix(p.FileURL(), ".webp") {
113+
return true
114+
}
115+
return false
116+
}
117+
118+
func (p *Post) IsVideo() bool {
119+
if strings.HasSuffix(p.FileURL(), ".mp4") ||
120+
strings.HasSuffix(p.FileURL(), ".webm") {
121+
return true
122+
}
123+
return false
124+
}
125+
126+
func (p *Post) IsGIF() bool {
127+
if strings.HasSuffix(p.FileURL(), ".gif") {
128+
return true
129+
}
130+
return false
131+
}

0 commit comments

Comments
 (0)