Compare commits

..

6 Commits

Author SHA1 Message Date
fumiama
4781bc8eb7 sync 2022-02-27 19:34:47 +08:00
fumiama
b233c4b638 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-02-27 19:32:29 +08:00
fumiama
0a0cc807dc fix #139 2022-02-27 19:31:53 +08:00
github-actions[bot]
feeeb63e52 🎨 改进代码样式 2022-02-27 10:59:03 +00:00
himawari
5c620d6268 feat:添加月幕galgame网站图 (#138)
* feat:添加月幕galgame网站图

* fix:修lint

* fix:不重复更新

* fix:增加命令提醒语

* fix:增加日语匹配

* fix:换位置加锁

* fix:隐式声明

* fix:copy数组

* Update ai_tts.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-02-27 18:57:52 +08:00
fumiama
a469000d7a 🔖 v1.3.0 2022-02-27 15:04:09 +08:00
20 changed files with 446 additions and 41 deletions

View File

@@ -287,6 +287,12 @@ zerobot [-h] [-t token] [-u url] [-n nickname] [-p prefix] [-d|w] [-g 监听地
- **煎蛋网无聊图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan"`
- [x] 来份屌图
- [x] 更新屌图
- **月幕galgame图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal"`
- [x] 随机galCG
- [x] 随机gal表情包
- [x] galCG[xxx]
- [x] gal表情包[xxx]
- [x] 更新gal
- **TODO...**
## 使用方法

2
data

Submodule data updated: f8067d8790...7ba7dfb0f3

6
go.mod
View File

@@ -3,9 +3,9 @@ module github.com/FloatTech/ZeroBot-Plugin
go 1.17
require (
github.com/FloatTech/AnimeAPI v1.3.0-beta8.0.20220227060429-ae266747b1df
github.com/FloatTech/AnimeAPI v1.3.1-0.20220227112758-da30b39dd7a7
github.com/FloatTech/sqlite v0.2.0
github.com/FloatTech/zbputils v1.3.0-beta8.0.20220227042832-376a84a3ea87
github.com/FloatTech/zbputils v1.3.1-0.20220227112438-2ccf42cc85d3
github.com/antchfx/htmlquery v1.2.4
github.com/corona10/goimagehash v1.0.3
github.com/fogleman/gg v1.3.0
@@ -21,7 +21,7 @@ require (
github.com/shirou/gopsutil/v3 v3.21.12
github.com/sirupsen/logrus v1.8.1
github.com/tidwall/gjson v1.13.0
github.com/wdvxdr1123/ZeroBot v1.4.2-0.20220223090418-8c5d8eccaf3a
github.com/wdvxdr1123/ZeroBot v1.4.2-0.20220227065946-629493e7d2bf
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
)

12
go.sum
View File

@@ -1,10 +1,10 @@
github.com/FloatTech/AnimeAPI v1.3.0-beta8.0.20220227060429-ae266747b1df h1:pz6mcz4pC8hEbyygM/QX/mX7pgvQOIpYF8H940/4Z8Q=
github.com/FloatTech/AnimeAPI v1.3.0-beta8.0.20220227060429-ae266747b1df/go.mod h1:uMe7TzdyGnyRn6AzMmkvDXp+GfTTWV/vBlv6mhhP8kg=
github.com/FloatTech/AnimeAPI v1.3.1-0.20220227112758-da30b39dd7a7 h1:JOmelUrxBef/Sg/15PgkBC3IV4BpORQwiSWfqV1nCD0=
github.com/FloatTech/AnimeAPI v1.3.1-0.20220227112758-da30b39dd7a7/go.mod h1:AuFa+9NWdJujJc2PvCoO2NqLOwwwFXKy69X0zexsZS0=
github.com/FloatTech/bot-manager v1.0.0/go.mod h1:8YYRJ16oroGHQGD2En0oVnmcKJkxR9O/jd5BPSfWfOQ=
github.com/FloatTech/sqlite v0.2.0 h1:x3uls/hExXH1+bbaNLkvilce6ATtWlDx4IqoxBW/bv8=
github.com/FloatTech/sqlite v0.2.0/go.mod h1:xIDWIvpOFl8AXmZm0FC8t3PZjiR6ZutytCpBv2EWCns=
github.com/FloatTech/zbputils v1.3.0-beta8.0.20220227042832-376a84a3ea87 h1:nyUTZLGUAJP/kLXx48j2qdxQBPVqaZgypjjIqpIRqwk=
github.com/FloatTech/zbputils v1.3.0-beta8.0.20220227042832-376a84a3ea87/go.mod h1:Jn5LmnwqYivr64qQJBdhDwm2gRut8Hhq5gPBydV0Gvg=
github.com/FloatTech/zbputils v1.3.1-0.20220227112438-2ccf42cc85d3 h1:BA9mbIE2D2hhZnGaFjgIbiUrwG4h6VjVYf4UoDnBVn8=
github.com/FloatTech/zbputils v1.3.1-0.20220227112438-2ccf42cc85d3/go.mod h1:NXv73hWyC4Q4S4wg01DEwLzznSHSmde0o03PnsS1DtY=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0=
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0=
@@ -156,8 +156,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/wdvxdr1123/ZeroBot v1.4.2-0.20220223090418-8c5d8eccaf3a h1:Mw7850zyEgIr8VO9e8iofo78maed53FclPzRiMa7aeY=
github.com/wdvxdr1123/ZeroBot v1.4.2-0.20220223090418-8c5d8eccaf3a/go.mod h1:NwXIp7PgjV+kUALMXJ4v4/3QcsRSOodtjhLekuPXFog=
github.com/wdvxdr1123/ZeroBot v1.4.2-0.20220227065946-629493e7d2bf h1:oUAVOzsRw5vrrZgBUEgG1BJ3hvjCUXykiVFqj0nfQ6o=
github.com/wdvxdr1123/ZeroBot v1.4.2-0.20220227065946-629493e7d2bf/go.mod h1:NwXIp7PgjV+kUALMXJ4v4/3QcsRSOodtjhLekuPXFog=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=

View File

@@ -100,6 +100,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtb_quotation" // vtb语录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wangyiyun" // 网易云音乐热评
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wtf" // 鬼东西
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili_push" // b站推送

View File

@@ -4,6 +4,7 @@ import (
"errors"
"regexp"
"strconv"
"sync"
"github.com/pkumza/numcn"
log "github.com/sirupsen/logrus"
@@ -38,18 +39,26 @@ var (
)
type ttsInstances struct {
sync.RWMutex
m map[string]tts.TTS
l []string
}
func (t *ttsInstances) List() []string {
return t.l
t.RLock()
cl := make([]string, len(t.l))
_ = copy(cl, t.l)
t.RUnlock()
return cl
}
func init() {
engine := control.Register(ttsServiceName, order.AcquirePrio(), &control.Options{
DisableOnDefault: false,
Help: "语音回复(包括拟声鸟和百度)\n- @Bot 任意文本(任意一句话回复)\n- 设置语音模式拟声鸟阿梓 | 设置语音模式拟声鸟药水哥 | 设置语音模式百度女声 | 设置语音模式百度男声| 设置语音模式百度度逍遥 | 设置语音模式百度度丫丫",
DisableOnDefault: true,
Help: "语音回复(包括拟声鸟和百度)\n" +
"- @Bot 任意文本(任意一句话回复)\n" +
"- 设置语音模式[拟声鸟阿梓 | 拟声鸟药水哥 | 百度女声 | 百度男声| 百度度逍遥 | 百度度丫丫]\n" +
"- 设置默认语音模式[拟声鸟阿梓 | 拟声鸟药水哥 | 百度女声 | 百度男声| 百度度逍遥 | 百度度丫丫]\n",
})
engine.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
@@ -78,7 +87,13 @@ func init() {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
return
}
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功"))
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,当前模式为", param))
})
engine.OnRegex(`^设置默认语音模式(.*)$`, ctxext.FirstValueInList(t)).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
param := ctx.State["regex_matched"].([]string)[1]
t.setDefaultSoundMode(param)
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,默认模式为", param))
})
}
@@ -93,12 +108,14 @@ func (t *ttsInstances) setSoundMode(ctx *zero.Ctx, name string) error {
gid = -ctx.Event.UserID
}
var index int64
t.RLock()
for i, s := range t.l {
if s == name {
index = int64(i)
break
}
}
t.RUnlock()
m, ok := control.Lookup(ttsServiceName)
if !ok {
return errors.New("no such plugin")
@@ -113,6 +130,8 @@ func (t *ttsInstances) getSoundMode(ctx *zero.Ctx) (name string) {
}
m, ok := control.Lookup(ttsServiceName)
if ok {
t.RLock()
defer t.RUnlock()
index := m.GetData(gid)
if int(index) < len(t.l) {
return t.l[index]
@@ -120,3 +139,18 @@ func (t *ttsInstances) getSoundMode(ctx *zero.Ctx) (name string) {
}
return "拟声鸟阿梓"
}
func (t *ttsInstances) setDefaultSoundMode(name string) {
var index int
t.RLock()
for _, s := range t.l {
if s == name {
break
}
index++
}
t.RUnlock()
t.Lock()
t.l[0], t.l[index] = t.l[index], t.l[0]
t.Unlock()
}

View File

@@ -232,7 +232,7 @@ func bilibiliPushDaily() {
}
func checkBuid(buid int64) (status int, name string) {
data, err := web.ReqWith(fmt.Sprintf(infoURL, buid), "GET", referer, ua)
data, err := web.GetDataWith(web.NewDefaultClient(), fmt.Sprintf(infoURL, buid), "GET", referer, ua)
if err != nil {
log.Errorln("[bilibilipush]:", err)
}
@@ -290,7 +290,7 @@ func unsubscribeLive(buid, groupid int64) (err error) {
}
func getUserDynamicCard(buid int64) (cardList []gjson.Result) {
data, err := web.ReqWith(fmt.Sprintf(userDynamicURL, buid), "GET", referer, ua)
data, err := web.GetDataWith(web.NewDefaultClient(), fmt.Sprintf(userDynamicURL, buid), "GET", referer, ua)
if err != nil {
log.Errorln("[bilibilipush]:", err)
}

View File

@@ -28,7 +28,7 @@ func init() {
}).ApplySingle(ctxext.DefaultSingle).OnFullMatch("coser", zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("少女祈祷中......"))
data, err := web.ReqWith(coserURL, "GET", "", ua)
data, err := web.GetDataWith(web.NewDefaultClient(), coserURL, "GET", "", ua)
if err != nil {
log.Println("err为:", err)
}

View File

@@ -66,7 +66,7 @@ func init() {
cid := gjson.Get(g, `list.`+strconv.Itoa(i)+`.CardID`).String()
cachefile := cachedir + cid
if file.IsNotExist(cachefile) {
data, err := web.ReqWith(
data, err := web.GetDataWith(web.NewDefaultClient(),
`https://res.fbigame.com/hs/v13/`+cid+`.png?auth_key=`+
gjson.Get(g, `list.`+strconv.Itoa(i)+`.auth_key`).String(),
reqconf[0], reqconf[1], reqconf[2])
@@ -105,10 +105,10 @@ func init() {
}
func sh(s string) string {
data, err := web.ReqWith("https://hs.fbigame.com", reqconf[0], reqconf[1], reqconf[2])
data, err := web.GetDataWith(web.NewDefaultClient(), "https://hs.fbigame.com", reqconf[0], reqconf[1], reqconf[2])
if err == nil {
url := hs + para + "&hash=" + strings.SplitN(strings.SplitN(helper.BytesToString(data), `var hash = "`, 2)[1], `"`, 2)[0] + "&search=" + s
r, err := web.ReqWith(url, reqconf[0], reqconf[1], reqconf[2])
r, err := web.GetDataWith(web.NewDefaultClient(), url, reqconf[0], reqconf[1], reqconf[2])
if err == nil {
return helper.BytesToString(r)
}
@@ -117,10 +117,10 @@ func sh(s string) string {
}
func kz(s string) string {
data, err := web.ReqWith("https://hs.fbigame.com", reqconf[0], reqconf[1], reqconf[2])
data, err := web.GetDataWith(web.NewDefaultClient(), "https://hs.fbigame.com", reqconf[0], reqconf[1], reqconf[2])
if err == nil {
url := hs + para + "mod=general_deck_image&deck_code=" + s + "&deck_text=&hash=" + strings.SplitN(strings.SplitN(helper.BytesToString(data), `var hash = "`, 2)[1], `"`, 2)[0] + "&search=" + s
r, err := web.ReqWith(url, reqconf[0], reqconf[1], reqconf[2])
r, err := web.GetDataWith(web.NewDefaultClient(), url, reqconf[0], reqconf[1], reqconf[2])
if err == nil {
return "base64://" + gjson.Get(helper.BytesToString(r), "img").String()
}

View File

@@ -70,7 +70,7 @@ func init() {
// soutuapi 请求api
func soutuapi(keyword string) (r resultjson, err error) {
url := "https://api.pixivel.moe/v2/pixiv/illust/search/" + keyword + "?page=0"
data, err := web.ReqWith(url, "GET", "https://pixivel.moe/", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36")
data, err := web.GetDataWith(web.NewTLS12Client(), url, "GET", "https://pixivel.moe/", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36")
if err != nil {
return
}

View File

@@ -204,12 +204,12 @@ func getLevel(count int) int {
func initPic(picFile string) {
if file.IsNotExist(picFile) {
data, err := web.ReqWith(backgroundURL, "GET", referer, ua)
data, err := web.GetDataWith(web.NewDefaultClient(), backgroundURL, "GET", referer, ua)
if err != nil {
log.Errorln("[score]", err)
}
picURL := gjson.Get(string(data), "pic").String()
data, err = web.ReqWith(picURL, "GET", "", ua)
data, err = web.GetDataWith(web.NewDefaultClient(), picURL, "GET", "", ua)
if err != nil {
log.Errorln("[score]", err)
}

View File

@@ -11,7 +11,7 @@ import (
func init() {
engine.OnFullMatch("哄我").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
data, err := web.ReqWith(chpURL, "GET", chpReferer, ua)
data, err := web.GetDataWith(web.NewDefaultClient(), chpURL, "GET", chpReferer, ua)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return

View File

@@ -11,7 +11,7 @@ import (
func init() {
engine.OnFullMatch("来碗毒鸡汤").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
data, err := web.ReqWith(duURL, "GET", duReferer, ua)
data, err := web.GetDataWith(web.NewDefaultClient(), duURL, "GET", duReferer, ua)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return

View File

@@ -11,7 +11,7 @@ import (
func init() {
engine.OnFullMatch("发个朋友圈").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
data, err := web.ReqWith(pyqURL, "GET", pyqReferer, ua)
data, err := web.GetDataWith(web.NewDefaultClient(), pyqURL, "GET", pyqReferer, ua)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return

View File

@@ -12,7 +12,7 @@ import (
func init() {
engine.OnFullMatch("来碗绿茶").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
data, err := web.ReqWith(chayiURL, "GET", loveliveReferer, ua)
data, err := web.GetDataWith(web.NewDefaultClient(), chayiURL, "GET", loveliveReferer, ua)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
@@ -22,7 +22,7 @@ func init() {
})
engine.OnFullMatch("渣我").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
data, err := web.ReqWith(ganhaiURL, "GET", loveliveReferer, ua)
data, err := web.GetDataWith(web.NewDefaultClient(), ganhaiURL, "GET", loveliveReferer, ua)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return

View File

@@ -14,7 +14,7 @@ import (
func init() {
engine.OnFullMatch("讲个段子").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
data, err := web.ReqWith(yduanziURL, "POST", yduanziReferer, ua)
data, err := web.GetDataWith(web.NewDefaultClient(), yduanziURL, "POST", yduanziReferer, ua)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return

View File

@@ -2,7 +2,6 @@
package vtbquotation
import (
"crypto/tls"
"fmt"
"io"
"net/http"
@@ -22,6 +21,7 @@ import (
control "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/img/text"
"github.com/FloatTech/zbputils/web"
"github.com/FloatTech/zbputils/control/order"
@@ -255,14 +255,7 @@ func init() {
func initRecord(recordFile, recordURL string) {
if file.IsNotExist(recordFile) {
transport := http.Transport{
TLSClientConfig: &tls.Config{
MaxVersion: tls.VersionTLS12,
},
}
client := &http.Client{
Transport: &transport,
}
client := web.NewTLS12Client()
req, _ := http.NewRequest("GET", recordURL, nil)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0")

View File

@@ -24,7 +24,7 @@ func init() {
Help: "wangyiyun \n- 来份网易云热评",
}).OnFullMatch("来份网易云热评").SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
data, err := web.ReqWith(wangyiyunURL, "GET", wangyiyunReferer, ua)
data, err := web.GetDataWith(web.NewDefaultClient(), wangyiyunURL, "GET", wangyiyunReferer, ua)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return

275
plugin/ymgal/model.go Normal file
View File

@@ -0,0 +1,275 @@
package ymgal
import (
"fmt"
"math/rand"
"net/url"
"os"
"regexp"
"strconv"
"sync"
"time"
"github.com/antchfx/htmlquery"
_ "github.com/fumiama/sqlite3" // import sql
"github.com/jinzhu/gorm"
log "github.com/sirupsen/logrus"
)
// gdb 得分数据库
var gdb *ymgaldb
// ymgaldb galgame图片数据库
type ymgaldb gorm.DB
var mu sync.RWMutex
// ymgal gal图片储存结构体
type ymgal struct {
ID int64 `gorm:"column:id" `
Title string `gorm:"column:title" `
PictureType string `gorm:"column:picture_type" `
PictureDescription string `gorm:"column:picture_description;type:varchar(1024)" `
PictureList string `gorm:"column:picture_list;type:varchar(20000)" `
}
// TableName ...
func (ymgal) TableName() string {
return "ymgal"
}
// initialize 初始化ymgaldb数据库
func initialize(dbpath string) *ymgaldb {
var err error
if _, err = os.Stat(dbpath); err != nil || os.IsNotExist(err) {
// 生成文件
f, err := os.Create(dbpath)
if err != nil {
return nil
}
defer f.Close()
}
gdb, err := gorm.Open("sqlite3", dbpath)
if err != nil {
panic(err)
}
gdb.AutoMigrate(&ymgal{})
return (*ymgaldb)(gdb)
}
func (gdb *ymgaldb) insertOrUpdateYmgalByID(id int64, title, pictureType, pictureDescription, pictureList string) (err error) {
db := (*gorm.DB)(gdb)
y := ymgal{
ID: id,
Title: title,
PictureType: pictureType,
PictureDescription: pictureDescription,
PictureList: pictureList,
}
if err = db.Debug().Model(&ymgal{}).First(&y, "id = ? ", id).Error; err != nil {
if gorm.IsRecordNotFoundError(err) {
err = db.Debug().Model(&ymgal{}).Create(&y).Error // newUser not user
}
} else {
err = db.Debug().Model(&ymgal{}).Where("id = ? ", id).Update(map[string]interface{}{
"title": title,
"picture_type": pictureType,
"picture_description": pictureDescription,
"picture_list": pictureList,
}).Error
}
return
}
func (gdb *ymgaldb) getYmgalByID(id string) (y ymgal) {
db := (*gorm.DB)(gdb)
db.Debug().Model(&ymgal{}).Where("id = ?", id).Take(&y)
return
}
func (gdb *ymgaldb) randomYmgal(pictureType string) (y ymgal) {
db := (*gorm.DB)(gdb)
var count int
s := db.Debug().Model(&ymgal{}).Where("picture_type = ?", pictureType).Count(&count)
if count == 0 {
return
}
s.Offset(rand.Intn(count)).Take(&y)
return
}
func (gdb *ymgaldb) getYmgalByKey(pictureType, key string) (y ymgal) {
db := (*gorm.DB)(gdb)
var count int
s := db.Debug().Model(&ymgal{}).Where("picture_type = ? and (picture_description like ? or title like ?) ", pictureType, "%"+key+"%", "%"+key+"%").Count(&count)
if count == 0 {
return
}
s.Offset(rand.Intn(count)).Take(&y)
return
}
const (
webURL = "https://www.ymgal.com"
cgType = "Gal CG"
emoticonType = "其他"
webPicURL = webURL + "/co/picset/"
reNumber = `\d+`
)
var (
cgURL = webURL + "/search?type=picset&sort=default&category=" + url.QueryEscape(cgType) + "&page="
emoticonURL = webURL + "/search?type=picset&sort=default&category=" + url.QueryEscape(emoticonType) + "&page="
commonPageNumberExpr = "//*[@id='pager-box']/div/a[@class='icon item pager-next']/preceding-sibling::a[1]/text()"
cgIDList []string
emoticonIDList []string
)
func initPageNumber() (maxCgPageNumber, maxEmoticonPageNumber int) {
doc, err := htmlquery.LoadURL(cgURL + "1")
if err != nil {
log.Errorln("[ymgal]:", err)
}
maxCgPageNumber, err = strconv.Atoi(htmlquery.FindOne(doc, commonPageNumberExpr).Data)
if err != nil {
log.Errorln("[ymgal]:", err)
}
doc, err = htmlquery.LoadURL(emoticonURL + "1")
if err != nil {
log.Errorln("[ymgal]:", err)
}
maxEmoticonPageNumber, err = strconv.Atoi(htmlquery.FindOne(doc, commonPageNumberExpr).Data)
if err != nil {
log.Errorln("[ymgal]:", err)
}
return
}
func getPicID(pageNumber int, pictureType string) {
var picURL string
if pictureType == cgType {
picURL = cgURL + strconv.Itoa(pageNumber)
} else if pictureType == emoticonType {
picURL = emoticonURL + strconv.Itoa(pageNumber)
}
doc, err := htmlquery.LoadURL(picURL)
if err != nil {
log.Errorln("[ymgal]:", err)
}
list := htmlquery.Find(doc, "//*[@id='picset-result-list']/ul/div/div[1]/a")
for i := 0; i < len(list); i++ {
re := regexp.MustCompile(reNumber)
picID := re.FindString(list[i].Attr[0].Val)
if pictureType == cgType {
cgIDList = append(cgIDList, picID)
} else if pictureType == emoticonType {
emoticonIDList = append(emoticonIDList, picID)
}
}
}
func updatePic() {
maxCgPageNumber, maxEmoticonPageNumber := initPageNumber()
for i := 1; i <= maxCgPageNumber; i++ {
getPicID(i, cgType)
time.Sleep(time.Millisecond * 500)
}
for i := 1; i <= maxEmoticonPageNumber; i++ {
getPicID(i, emoticonType)
time.Sleep(time.Millisecond * 500)
}
CGLOOP:
for i := len(cgIDList) - 1; i >= 0; i-- {
mu.RLock()
y := gdb.getYmgalByID(cgIDList[i])
mu.RUnlock()
if y.PictureList == "" {
mu.Lock()
storeCgPic(cgIDList[i])
mu.Unlock()
} else {
break CGLOOP
}
time.Sleep(time.Millisecond * 500)
}
EMOTICONLOOP:
for i := len(emoticonIDList) - 1; i >= 0; i-- {
mu.RLock()
y := gdb.getYmgalByID(emoticonIDList[i])
mu.RUnlock()
if y.PictureList == "" {
mu.Lock()
storeEmoticonPic(emoticonIDList[i])
mu.Unlock()
} else {
break EMOTICONLOOP
}
time.Sleep(time.Millisecond * 500)
}
}
func storeCgPic(picIDStr string) {
picID, err := strconv.ParseInt(picIDStr, 10, 64)
if err != nil {
log.Errorln("[ymgal]:", err)
}
pictureType := cgType
doc, err := htmlquery.LoadURL(webPicURL + picIDStr)
if err != nil {
log.Errorln("[ymgal]:", err)
}
title := htmlquery.FindOne(doc, "//meta[@name='name']").Attr[1].Val
pictureDescription := htmlquery.FindOne(doc, "//meta[@name='description']").Attr[1].Val
pictureNumberStr := htmlquery.FindOne(doc, "//div[@class='meta-info']/div[@class='meta-right']/span[2]/text()").Data
re := regexp.MustCompile(reNumber)
pictureNumber, err := strconv.Atoi(re.FindString(pictureNumberStr))
if err != nil {
log.Errorln("[ymgal]:", err)
}
pictureList := ""
for i := 1; i <= pictureNumber; i++ {
picURL := htmlquery.FindOne(doc, fmt.Sprintf("//*[@id='main-picset-warp']/div/div[2]/div/div[@class='swiper-wrapper']/div[%d]", i)).Attr[1].Val
if i == 1 {
pictureList += picURL
} else {
pictureList += "," + picURL
}
}
err = gdb.insertOrUpdateYmgalByID(picID, title, pictureType, pictureDescription, pictureList)
if err != nil {
log.Errorln("[ymgal]:", err)
}
}
func storeEmoticonPic(picIDStr string) {
picID, err := strconv.ParseInt(picIDStr, 10, 64)
if err != nil {
log.Errorln("[ymgal]:", err)
}
pictureType := emoticonType
doc, err := htmlquery.LoadURL(webPicURL + picIDStr)
if err != nil {
log.Errorln("[ymgal]:", err)
}
title := htmlquery.FindOne(doc, "//meta[@name='name']").Attr[1].Val
pictureDescription := htmlquery.FindOne(doc, "//meta[@name='description']").Attr[1].Val
pictureNumberStr := htmlquery.FindOne(doc, "//div[@class='meta-info']/div[@class='meta-right']/span[2]/text()").Data
re := regexp.MustCompile(reNumber)
pictureNumber, err := strconv.Atoi(re.FindString(pictureNumberStr))
if err != nil {
log.Errorln("[ymgal]:", err)
}
pictureList := ""
for i := 1; i <= pictureNumber; i++ {
picURL := htmlquery.FindOne(doc, fmt.Sprintf("//*[@id='main-picset-warp']/div/div[@class='stream-list']/div[%d]/img", i)).Attr[1].Val
if i == 1 {
pictureList += picURL
} else {
pictureList += "," + picURL
}
}
err = gdb.insertOrUpdateYmgalByID(picID, title, pictureType, pictureDescription, pictureList)
if err != nil {
log.Errorln("[ymgal]:", err)
}
}

96
plugin/ymgal/ymgal.go Normal file
View File

@@ -0,0 +1,96 @@
// Package ymgal 月幕galgame
package ymgal
import (
"log"
"strings"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/control/order"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/file"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
engine := control.Register("ymgal", order.AcquirePrio(), &control.Options{
DisableOnDefault: false,
Help: "月幕galgame\n- 随机galCG\n- 随机gal表情包\n- galCG[xxx]\n- gal表情包[xxx]\n- 更新gal\n",
PublicDataFolder: "Ymgal",
})
dbfile := engine.DataFolder() + "ymgal.db"
go func() {
defer order.DoneOnExit()()
_, _ = file.GetLazyData(dbfile, false, false)
gdb = initialize(dbfile)
log.Println("[ymgal]加载月幕gal数据库")
}()
engine.OnRegex("^随机gal(CG|表情包)$").Limit(ctxext.LimitByUser).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.Send("少女祈祷中......")
pictureType := ctx.State["regex_matched"].([]string)[1]
var y ymgal
if pictureType == "表情包" {
y = gdb.randomYmgal(emoticonType)
} else {
y = gdb.randomYmgal(cgType)
}
sendYmgal(y, ctx)
})
engine.OnRegex("^gal(CG|表情包)([一-龥ぁ-んァ-ヶA-Za-z0-9]{1,25})$").Limit(ctxext.LimitByUser).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.Send("少女祈祷中......")
pictureType := ctx.State["regex_matched"].([]string)[1]
key := ctx.State["regex_matched"].([]string)[2]
var y ymgal
if pictureType == "CG" {
y = gdb.getYmgalByKey(cgType, key)
} else {
y = gdb.getYmgalByKey(emoticonType, key)
}
sendYmgal(y, ctx)
})
engine.OnFullMatch("更新gal", zero.SuperUserPermission).SetBlock(true).Handle(
func(ctx *zero.Ctx) {
ctx.Send("少女祈祷中......")
updatePic()
ctx.Send("ymgal数据库已更新")
})
}
func sendYmgal(y ymgal, ctx *zero.Ctx) {
if y.PictureList == "" {
ctx.SendChain(message.Text(zero.BotConfig.NickName[0] + "暂时没有这样的图呢"))
return
}
m := message.Message{
message.CustomNode(
ctx.Event.Sender.NickName,
ctx.Event.UserID,
y.Title,
)}
if y.PictureDescription != "" {
m = append(m,
message.CustomNode(
ctx.Event.Sender.NickName,
ctx.Event.UserID,
y.PictureDescription,
))
}
for _, v := range strings.Split(y.PictureList, ",") {
m = append(m,
message.CustomNode(
ctx.Event.Sender.NickName,
ctx.Event.UserID,
[]message.MessageSegment{
message.Image(v),
}),
)
}
if id := ctx.SendGroupForwardMessage(
ctx.Event.GroupID,
m).Get("message_id").Int(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
}