✏️ 修正 sql

This commit is contained in:
fumiama 2021-11-29 20:25:14 +08:00
parent 89121cfc57
commit f3f7e2d3c1
7 changed files with 141 additions and 54 deletions

View File

@ -123,7 +123,7 @@ zerobot -h -t token -u url [-d|w] [-g 监听地址:端口] qq1 qq2 qq3 ...
- [x] 删除[涩图/二次元/风景/车万][P站图片ID] - [x] 删除[涩图/二次元/风景/车万][P站图片ID]
- [x] > setu status - [x] > setu status
- **本地涩图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_nativesetu"` - **本地涩图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_nativesetu"`
- [x] 来份本地[xxx] - [x] 本地[xxx]
- [x] 刷新本地[xxx] - [x] 刷新本地[xxx]
- [x] 设置本地setu绝对路径[xxx] - [x] 设置本地setu绝对路径[xxx]
- [x] 刷新所有本地setu - [x] 刷新所有本地setu

1
go.mod
View File

@ -23,4 +23,5 @@ require (
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
github.com/tidwall/gjson v1.11.0 github.com/tidwall/gjson v1.11.0
github.com/wdvxdr1123/ZeroBot v1.4.1 github.com/wdvxdr1123/ZeroBot v1.4.1
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
) )

View File

@ -1,7 +1,9 @@
package nativesetu package nativesetu
import ( import (
"bytes"
"image" "image"
"io"
"io/fs" "io/fs"
"os" "os"
"sync" "sync"
@ -9,6 +11,7 @@ import (
"github.com/corona10/goimagehash" "github.com/corona10/goimagehash"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/wdvxdr1123/ZeroBot/utils/helper" "github.com/wdvxdr1123/ZeroBot/utils/helper"
"golang.org/x/image/webp"
"github.com/FloatTech/ZeroBot-Plugin/utils/file" "github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/process" "github.com/FloatTech/ZeroBot-Plugin/utils/process"
@ -17,8 +20,9 @@ import (
// setuclass holds setus in a folder, which is the class name. // setuclass holds setus in a folder, which is the class name.
type setuclass struct { type setuclass struct {
ImgID uint64 `db:"imgid"` // ImgID 图片唯一 id (dhash) ImgID int64 `db:"imgid"` // ImgID 图片唯一 id (dhash)
Name string `db:"name"` // Name 图片名 Name string `db:"name"` // Name 图片名
Path string `db:"path"` // Path 图片路径
} }
var ( var (
@ -41,45 +45,71 @@ func init() {
logrus.Println("[nsetu] set setu dir to", setupath) logrus.Println("[nsetu] set setu dir to", setupath)
} }
} }
if file.IsExist(dbfile) {
err := db.Open()
if err == nil {
setuclasses, err = db.ListTables()
}
if err != nil {
logrus.Errorln("[nsetu]", err)
}
}
}() }()
} }
func scanall(path string) error { func scanall(path string) error {
setuclasses = setuclasses[:0] setuclasses = nil
model := &setuclass{} model := &setuclass{}
root := os.DirFS(path) root := os.DirFS(path)
return fs.WalkDir(root, "./", func(path string, d fs.DirEntry, err error) error { _ = db.Close()
_ = os.Remove(dbfile)
return fs.WalkDir(root, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil { if err != nil {
return err return err
} }
if d.IsDir() { if d.IsDir() {
clsn := d.Name() clsn := d.Name()
if clsn != "." {
mu.Lock() mu.Lock()
err = db.Create(clsn, model) err = db.Create(clsn, model)
setuclasses = append(setuclasses, clsn) setuclasses = append(setuclasses, clsn)
mu.Unlock() mu.Unlock()
if err == nil { if err == nil {
err = scanclass(root, clsn) err = scanclass(root, path, clsn)
if err != nil { if err != nil {
logrus.Errorln("[nsetu]", err)
return err return err
} }
} }
} }
return err }
return nil
}) })
} }
func scanclass(root fs.FS, clsn string) error { func scanclass(root fs.FS, path, clsn string) error {
return fs.WalkDir(root, clsn, func(path string, d fs.DirEntry, err error) error { ds, err := fs.ReadDir(root, path)
if err != nil { if err != nil {
return err return err
} }
mu.Lock()
_ = db.Truncate(clsn)
mu.Unlock()
for _, d := range ds {
if !d.IsDir() { if !d.IsDir() {
f, e := os.Open(path) relpath := path + "/" + d.Name()
fullpath := setupath + "/" + relpath
logrus.Debugln("[nsetu] read", fullpath)
f, e := os.ReadFile(fullpath)
if e != nil { if e != nil {
return e return e
} }
img, _, e := image.Decode(f) b := bytes.NewReader(f)
img, _, e := image.Decode(b)
if e != nil {
b.Seek(0, io.SeekStart)
img, e = webp.Decode(b)
}
if e != nil { if e != nil {
return e return e
} }
@ -87,10 +117,15 @@ func scanclass(root fs.FS, clsn string) error {
if e != nil { if e != nil {
return e return e
} }
dhi := int64(dh.GetHash())
logrus.Debugln("[nsetu] insert", d.Name(), "with id", dhi, "into", clsn)
mu.Lock() mu.Lock()
err = db.Insert(clsn, &setuclass{ImgID: dh.GetHash(), Name: d.Name()}) err = db.Insert(clsn, &setuclass{ImgID: dhi, Name: d.Name(), Path: relpath})
mu.Unlock() mu.Unlock()
} if err != nil {
return err return err
}) }
}
}
return nil
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/FloatTech/ZeroBot-Plugin/control" "github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/utils/rule" "github.com/FloatTech/ZeroBot-Plugin/utils/rule"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot" zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper" "github.com/wdvxdr1123/ZeroBot/utils/helper"
@ -25,13 +26,13 @@ func init() {
engine := control.Register("nativesetu", &control.Options{ engine := control.Register("nativesetu", &control.Options{
DisableOnDefault: false, DisableOnDefault: false,
Help: "本地涩图\n" + Help: "本地涩图\n" +
"- 来份本地[xxx]\n" + "- 本地[xxx]\n" +
"- 刷新本地[xxx]\n" + "- 刷新本地[xxx]\n" +
"- 设置本地setu绝对路径[xxx]\n" + "- 设置本地setu绝对路径[xxx]\n" +
"- 刷新所有本地setu\n" + "- 刷新所有本地setu\n" +
"- 所有本地setu分类", "- 所有本地setu分类",
}) })
engine.OnRegex(`^来份本地(.*)$`, rule.FirstValueInList(setuclasses)).SetBlock(true).SetPriority(20). engine.OnRegex(`^本地(.*)$`, func(ctx *zero.Ctx) bool { return rule.FirstValueInList(setuclasses)(ctx) }).SetBlock(true).SetPriority(36).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
imgtype := ctx.State["regex_matched"].([]string)[1] imgtype := ctx.State["regex_matched"].([]string)[1]
sc := new(setuclass) sc := new(setuclass)
@ -41,36 +42,40 @@ func init() {
if err != nil { if err != nil {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
} else { } else {
p := "file:///" + setupath + "/" + imgtype + "/" + sc.Name p := "file:///" + setupath + "/" + sc.Path
ctx.SendChain(message.Text(imgtype, ": ", sc.Name, "\n"), message.Image(p)) ctx.SendChain(message.Text(imgtype, ": ", sc.Name, "\n"), message.Image(p))
} }
}) })
engine.OnRegex(`^刷新本地(.*)$`, rule.FirstValueInList(setuclasses), zero.SuperUserPermission).SetBlock(true).SetPriority(20). engine.OnRegex(`^刷新本地(.*)$`, func(ctx *zero.Ctx) bool { return rule.FirstValueInList(setuclasses)(ctx) }, zero.SuperUserPermission).SetBlock(true).SetPriority(36).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
imgtype := ctx.State["regex_matched"].([]string)[1] imgtype := ctx.State["regex_matched"].([]string)[1]
err := scanclass(os.DirFS(setupath), imgtype) err := scanclass(os.DirFS(setupath), imgtype, imgtype)
if err == nil { if err == nil {
ctx.SendChain(message.Text("成功!")) ctx.SendChain(message.Text("成功!"))
} else { } else {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
} }
}) })
engine.OnRegex(`^设置本地setu绝对路径(.*)$`, zero.SuperUserPermission).SetBlock(true).SetPriority(20). engine.OnRegex(`^设置本地setu绝对路径(.*)$`, zero.SuperUserPermission).SetBlock(true).SetPriority(36).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
setupath = ctx.State["regex_matched"].([]string)[1] setupath = ctx.State["regex_matched"].([]string)[1]
err := os.WriteFile(cfgfile, helper.StringToBytes(setupath), 0644) err := os.WriteFile(cfgfile, helper.StringToBytes(setupath), 0644)
if err != nil { if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
} }
}) })
engine.OnFullMatch("刷新所有本地setu", zero.SuperUserPermission).SetBlock(true).SetPriority(20). engine.OnFullMatch("刷新所有本地setu", zero.SuperUserPermission).SetBlock(true).SetPriority(36).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
err := scanall(setupath) err := scanall(setupath)
if err != nil { if err == nil {
ctx.SendChain(message.Text("成功!"))
} else {
ctx.SendChain(message.Text("ERROR: ", err)) ctx.SendChain(message.Text("ERROR: ", err))
} }
}) })
engine.OnFullMatch("所有本地setu分类").SetBlock(true).SetPriority(20). engine.OnFullMatch("所有本地setu分类").SetBlock(true).SetPriority(36).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
msg := "所有本地setu分类" msg := "所有本地setu分类"
mu.RLock() mu.RLock()
@ -80,8 +85,10 @@ func init() {
msg += fmt.Sprintf("\n%02d. %s(%d)", i, c, n) msg += fmt.Sprintf("\n%02d. %s(%d)", i, c, n)
} else { } else {
msg += fmt.Sprintf("\n%02d. %s(error)", i, c) msg += fmt.Sprintf("\n%02d. %s(error)", i, c)
logrus.Errorln("[nsetu]", err)
} }
} }
mu.RUnlock() mu.RUnlock()
ctx.SendChain(message.Text(msg))
}) })
} }

View File

@ -21,7 +21,7 @@ import (
) )
var ( var (
prio = 100 prio = 256
bucket = rate.NewManager(time.Minute, 20) // 青云客接口回复 bucket = rate.NewManager(time.Minute, 20) // 青云客接口回复
engine *zero.Engine engine *zero.Engine
) )

View File

@ -6,8 +6,8 @@ import zero "github.com/wdvxdr1123/ZeroBot"
func FirstValueInList(list []string) zero.Rule { func FirstValueInList(list []string) zero.Rule {
return func(ctx *zero.Ctx) bool { return func(ctx *zero.Ctx) bool {
first := ctx.State["regex_matched"].([]string)[1] first := ctx.State["regex_matched"].([]string)[1]
for i := range list { for _, v := range list {
if first == list[i] { if first == v {
return true return true
} }
} }

View File

@ -16,6 +16,27 @@ type Sqlite struct {
DBPath string DBPath string
} }
// Open 打开数据库
func (db *Sqlite) Open() (err error) {
if db.DB == nil {
database, err := sql.Open("sqlite3", db.DBPath)
if err != nil {
return err
}
db.DB = database
}
return
}
// Close 关闭数据库
func (db *Sqlite) Close() (err error) {
if db.DB != nil {
err = db.DB.Close()
db.DB = nil
}
return
}
// Create 生成数据库 // Create 生成数据库
// 默认结构体的第一个元素为主键 // 默认结构体的第一个元素为主键
// 返回错误 // 返回错误
@ -57,7 +78,7 @@ func (db *Sqlite) Create(table string, objptr interface{}) (err error) {
// 默认结构体的第一个元素为主键 // 默认结构体的第一个元素为主键
// 返回错误 // 返回错误
func (db *Sqlite) Insert(table string, objptr interface{}) error { func (db *Sqlite) Insert(table string, objptr interface{}) error {
rows, err := db.DB.Query("SELECT * FROM " + table + ";") rows, err := db.DB.Query("SELECT * FROM " + table + " limit 1;")
if err != nil { if err != nil {
return err return err
} }
@ -67,7 +88,7 @@ func (db *Sqlite) Insert(table string, objptr interface{}) error {
tags, _ := rows.Columns() tags, _ := rows.Columns()
rows.Close() rows.Close()
var ( var (
values = values(objptr) vals = values(objptr)
top = len(tags) - 1 top = len(tags) - 1
cmd = []string{} cmd = []string{}
) )
@ -105,7 +126,7 @@ func (db *Sqlite) Insert(table string, objptr interface{}) error {
if err != nil { if err != nil {
return err return err
} }
_, err = stmt.Exec(values...) _, err = stmt.Exec(vals...)
if err != nil { if err != nil {
return err return err
} }
@ -173,7 +194,7 @@ func (db *Sqlite) ListTables() (s []string, err error) {
return return
} }
// Del 删除数据库 // Del 删除数据库表项
// condition 可为"WHERE id = 0" // condition 可为"WHERE id = 0"
// 返回错误 // 返回错误
func (db *Sqlite) Del(table string, condition string) error { func (db *Sqlite) Del(table string, condition string) error {
@ -192,11 +213,27 @@ func (db *Sqlite) Del(table string, condition string) error {
return stmt.Close() return stmt.Close()
} }
// Truncate 清空数据库表
func (db *Sqlite) Truncate(table string) error {
var cmd = []string{}
cmd = append(cmd, "TRUNCATE TABLE")
cmd = append(cmd, table)
stmt, err := db.DB.Prepare(strings.Join(cmd, " ") + ";")
if err != nil {
return err
}
_, err = stmt.Exec()
if err != nil {
return err
}
return stmt.Close()
}
// Count 查询数据库行数 // Count 查询数据库行数
// 返回行数以及错误 // 返回行数以及错误
func (db *Sqlite) Count(table string) (num int, err error) { func (db *Sqlite) Count(table string) (num int, err error) {
var cmd = []string{} var cmd = []string{}
cmd = append(cmd, "SELECT * FROM") cmd = append(cmd, "SELECT COUNT(1) FROM")
cmd = append(cmd, table) cmd = append(cmd, table)
rows, err := db.DB.Query(strings.Join(cmd, " ") + ";") rows, err := db.DB.Query(strings.Join(cmd, " ") + ";")
if err != nil { if err != nil {
@ -205,8 +242,8 @@ func (db *Sqlite) Count(table string) (num int, err error) {
if rows.Err() != nil { if rows.Err() != nil {
return num, rows.Err() return num, rows.Err()
} }
for rows.Next() { if rows.Next() {
num++ rows.Scan(&num)
} }
rows.Close() rows.Close()
return num, nil return num, nil
@ -236,8 +273,22 @@ func kinds(objptr interface{}) []string {
} }
for i, flen := 0, elem.Type().NumField(); i < flen; i++ { for i, flen := 0, elem.Type().NumField(); i < flen; i++ {
switch elem.Field(i).Type().String() { switch elem.Field(i).Type().String() {
case "int64": case "int8":
kinds = append(kinds, "TINYINT")
case "uint8", "byte":
kinds = append(kinds, "UNSIGNED TINYINT")
case "int16":
kinds = append(kinds, "SMALLINT")
case "uint16":
kinds = append(kinds, "UNSIGNED SMALLINT")
case "int32":
kinds = append(kinds, "INT") kinds = append(kinds, "INT")
case "uint32":
kinds = append(kinds, "UNSIGNED INT")
case "int64":
kinds = append(kinds, "BIGINT")
case "uint64":
kinds = append(kinds, "UNSIGNED BIGINT")
case "string": case "string":
kinds = append(kinds, "TEXT") kinds = append(kinds, "TEXT")
default: default:
@ -256,14 +307,7 @@ func values(objptr interface{}) []interface{} {
elem = elem.Field(0) elem = elem.Field(0)
} }
for i, flen := 0, elem.Type().NumField(); i < flen; i++ { for i, flen := 0, elem.Type().NumField(); i < flen; i++ {
switch elem.Field(i).Type().String() { values = append(values, elem.Field(i).Interface())
case "int64":
values = append(values, elem.Field(i).Int())
case "string":
values = append(values, elem.Field(i).String())
default:
values = append(values, elem.Field(i).String())
}
} }
return values return values
} }