diff --git a/control/rule.go b/control/rule.go index b311260d..1d0507a9 100644 --- a/control/rule.go +++ b/control/rule.go @@ -5,12 +5,13 @@ import ( "crypto/md5" "encoding/binary" "fmt" + "github.com/FloatTech/ZeroBot-Plugin/utils/txt2img" "os" "strconv" "strings" "sync" - "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" zero "github.com/wdvxdr1123/ZeroBot" "github.com/wdvxdr1123/ZeroBot/extension" "github.com/wdvxdr1123/ZeroBot/message" @@ -73,7 +74,7 @@ func (m *Control) Enable(groupID int64) { err = db.Insert(m.service, &c) m.Unlock() if err != nil { - logrus.Errorf("[control] %v", err) + log.Errorf("[control] %v", err) } } @@ -92,7 +93,7 @@ func (m *Control) Disable(groupID int64) { err = db.Insert(m.service, &c) m.Unlock() if err != nil { - logrus.Errorf("[control] %v", err) + log.Errorf("[control] %v", err) } } @@ -104,7 +105,7 @@ func (m *Control) Reset(groupID int64) { err := db.Del(m.service, "WHERE gid = "+strconv.FormatInt(groupID, 10)) m.Unlock() if err != nil { - logrus.Errorf("[control] %v", err) + log.Errorf("[control] %v", err) } } } @@ -113,13 +114,13 @@ func (m *Control) Reset(groupID int64) { func (m *Control) IsEnabledIn(gid int64) bool { var c grpcfg var err error - logrus.Debugln("[control] IsEnabledIn recv gid =", gid) + log.Debugln("[control] IsEnabledIn recv gid =", gid) if gid != 0 { m.RLock() err = db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(gid, 10)) m.RUnlock() if err == nil && gid == c.GroupID { - logrus.Debugf("[control] plugin %s of grp %d : %d", m.service, c.GroupID, c.Disable&1) + log.Debugf("[control] plugin %s of grp %d : %d", m.service, c.GroupID, c.Disable&1) return c.Disable&1 == 0 } } @@ -127,7 +128,7 @@ func (m *Control) IsEnabledIn(gid int64) bool { err = db.Find(m.service, &c, "WHERE gid = 0") m.RUnlock() if err == nil && c.GroupID == 0 { - logrus.Debugf("[control] plugin %s of all : %d", m.service, c.Disable&1) + log.Debugf("[control] plugin %s of all : %d", m.service, c.Disable&1) return c.Disable&1 == 0 } return !m.options.DisableOnDefault @@ -137,14 +138,14 @@ func (m *Control) IsEnabledIn(gid int64) bool { func (m *Control) Ban(uid, gid int64) { var err error var digest [16]byte - logrus.Debugln("[control] Ban recv gid =", gid, "uid =", uid) + log.Debugln("[control] Ban recv gid =", gid, "uid =", uid) if gid != 0 { // 特定群 digest = md5.Sum(helper.StringToBytes(fmt.Sprintf("%d_%d", uid, gid))) m.RLock() err = db.Insert(m.service+"ban", &ban{ID: int64(binary.LittleEndian.Uint64(digest[:8])), UserID: uid, GroupID: gid}) m.RUnlock() if err == nil { - logrus.Debugf("[control] plugin %s is banned in grp %d for usr %d.", m.service, gid, uid) + log.Debugf("[control] plugin %s is banned in grp %d for usr %d.", m.service, gid, uid) return } } @@ -154,20 +155,20 @@ func (m *Control) Ban(uid, gid int64) { err = db.Insert(m.service+"ban", &ban{ID: int64(binary.LittleEndian.Uint64(digest[:8])), UserID: uid, GroupID: 0}) m.RUnlock() if err == nil { - logrus.Debugf("[control] plugin %s is banned in all grp for usr %d.", m.service, uid) + log.Debugf("[control] plugin %s is banned in all grp for usr %d.", m.service, uid) } } // Permit 允许某人在某群使用本插件 func (m *Control) Permit(uid, gid int64) { var digest [16]byte - logrus.Debugln("[control] Permit recv gid =", gid, "uid =", uid) + log.Debugln("[control] Permit recv gid =", gid, "uid =", uid) if gid != 0 { // 特定群 digest = md5.Sum(helper.StringToBytes(fmt.Sprintf("%d_%d", uid, gid))) m.RLock() _ = db.Del(m.service+"ban", "WHERE id = "+strconv.FormatInt(int64(binary.LittleEndian.Uint64(digest[:8])), 10)) m.RUnlock() - logrus.Debugf("[control] plugin %s is permitted in grp %d for usr %d.", m.service, gid, uid) + log.Debugf("[control] plugin %s is permitted in grp %d for usr %d.", m.service, gid, uid) return } // 所有群 @@ -175,7 +176,7 @@ func (m *Control) Permit(uid, gid int64) { m.RLock() _ = db.Del(m.service+"ban", "WHERE id = "+strconv.FormatInt(int64(binary.LittleEndian.Uint64(digest[:8])), 10)) m.RUnlock() - logrus.Debugf("[control] plugin %s is permitted in all grp for usr %d.", m.service, uid) + log.Debugf("[control] plugin %s is permitted in all grp for usr %d.", m.service, uid) } // IsBannedIn 某人是否在某群被 ban @@ -183,14 +184,14 @@ func (m *Control) IsBannedIn(uid, gid int64) bool { var b ban var err error var digest [16]byte - logrus.Debugln("[control] IsBannedIn recv gid =", gid, "uid =", uid) + log.Debugln("[control] IsBannedIn recv gid =", gid, "uid =", uid) if gid != 0 { digest = md5.Sum(helper.StringToBytes(fmt.Sprintf("%d_%d", uid, gid))) m.RLock() err = db.Find(m.service+"ban", &b, "WHERE id = "+strconv.FormatInt(int64(binary.LittleEndian.Uint64(digest[:8])), 10)) m.RUnlock() if err == nil && gid == b.GroupID && uid == b.UserID { - logrus.Debugf("[control] plugin %s is banned in grp %d for usr %d.", m.service, b.GroupID, b.UserID) + log.Debugf("[control] plugin %s is banned in grp %d for usr %d.", m.service, b.GroupID, b.UserID) return true } } @@ -199,7 +200,7 @@ func (m *Control) IsBannedIn(uid, gid int64) bool { err = db.Find(m.service+"ban", &b, "WHERE id = "+strconv.FormatInt(int64(binary.LittleEndian.Uint64(digest[:8])), 10)) m.RUnlock() if err == nil && b.GroupID == 0 && uid == b.UserID { - logrus.Debugf("[control] plugin %s is banned in all grp for usr %d.", m.service, b.UserID) + log.Debugf("[control] plugin %s is banned in all grp for usr %d.", m.service, b.UserID) return true } return false @@ -209,13 +210,13 @@ func (m *Control) IsBannedIn(uid, gid int64) bool { func (m *Control) GetData(gid int64) int64 { var c grpcfg var err error - logrus.Debugln("[control] IsEnabledIn recv gid =", gid) + log.Debugln("[control] IsEnabledIn recv gid =", gid) if gid != 0 { m.RLock() err = db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(gid, 10)) m.RUnlock() if err == nil && gid == c.GroupID { - logrus.Debugf("[control] plugin %s of grp %d : %x", m.service, c.GroupID, c.Disable>>1) + log.Debugf("[control] plugin %s of grp %d : %x", m.service, c.GroupID, c.Disable>>1) return c.Disable >> 1 } } @@ -223,7 +224,7 @@ func (m *Control) GetData(gid int64) int64 { err = db.Find(m.service, &c, "WHERE gid = 0") m.RUnlock() if err == nil && c.GroupID == 0 { - logrus.Debugf("[control] plugin %s of all : %x", m.service, c.Disable>>1) + log.Debugf("[control] plugin %s of all : %x", m.service, c.Disable>>1) return c.Disable >> 1 } return 0 @@ -242,12 +243,12 @@ func (m *Control) SetData(groupID int64, data int64) error { } } c.Disable |= data << 1 - logrus.Debugf("[control] set plugin %s of all : %x", m.service, data) + log.Debugf("[control] set plugin %s of all : %x", m.service, data) m.Lock() err = db.Insert(m.service, &c) m.Unlock() if err != nil { - logrus.Errorf("[control] %v", err) + log.Errorf("[control] %v", err) } return err } @@ -260,7 +261,7 @@ func (m *Control) Handler(ctx *zero.Ctx) bool { // 个人用户 return m.IsEnabledIn(-ctx.Event.UserID) } - logrus.Debugln("[control] handler get gid =", grp) + log.Debugln("[control] handler get gid =", grp) return m.IsEnabledIn(grp) && !m.IsBannedIn(ctx.Event.UserID, grp) } @@ -312,7 +313,7 @@ func init() { zero.OnCommandGroup([]string{ "启用", "enable", "禁用", "disable", "全局启用", "enableall", "全局禁用", "disableall", - }, userOrGrpAdmin).Handle(func(ctx *zero.Ctx) { + }, userOrGrpAdmin).SetBlock(true).FirstPriority().Handle(func(ctx *zero.Ctx) { model := extension.CommandModel{} _ = ctx.Parse(&model) service, ok := Lookup(model.Args) @@ -337,7 +338,7 @@ func init() { } }) - zero.OnCommandGroup([]string{"还原", "reset"}, userOrGrpAdmin).Handle(func(ctx *zero.Ctx) { + zero.OnCommandGroup([]string{"还原", "reset"}, userOrGrpAdmin).SetBlock(true).FirstPriority().Handle(func(ctx *zero.Ctx) { model := extension.CommandModel{} _ = ctx.Parse(&model) service, ok := Lookup(model.Args) @@ -357,7 +358,7 @@ func init() { zero.OnCommandGroup([]string{ "禁止", "ban", "允许", "permit", "全局禁止", "banall", "全局允许", "permitall", - }, zero.OnlyGroup, zero.AdminPermission).Handle(func(ctx *zero.Ctx) { + }, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).FirstPriority().Handle(func(ctx *zero.Ctx) { model := extension.CommandModel{} _ = ctx.Parse(&model) args := strings.Split(model.Args, " ") @@ -395,7 +396,7 @@ func init() { ctx.SendChain(message.Text("参数错误!")) }) - zero.OnCommandGroup([]string{"用法", "usage"}, userOrGrpAdmin). + zero.OnCommandGroup([]string{"用法", "usage"}, userOrGrpAdmin).SetBlock(true).FirstPriority(). Handle(func(ctx *zero.Ctx) { model := extension.CommandModel{} _ = ctx.Parse(&model) @@ -411,7 +412,7 @@ func init() { } }) - zero.OnCommandGroup([]string{"服务列表", "service_list"}, userOrGrpAdmin). + zero.OnCommandGroup([]string{"服务列表", "service_list"}, userOrGrpAdmin).SetBlock(true).FirstPriority(). Handle(func(ctx *zero.Ctx) { msg := "--------服务列表--------\n发送\"/用法 name\"查看详情" i := 0 @@ -429,15 +430,9 @@ func init() { ctx.SendChain(message.Text(msg)) }) - zero.OnCommandGroup([]string{"服务详情", "service_detail"}, userOrGrpAdmin, zero.OnlyGroup). + zero.OnCommandGroup([]string{"服务详情", "service_detail"}, userOrGrpAdmin).SetBlock(true).FirstPriority(). Handle(func(ctx *zero.Ctx) { - var m message.Message - m = append(m, - message.CustomNode( - zero.BotConfig.NickName[0], - ctx.Event.SelfID, - "---服务详情---", - )) + text := "---服务详情---\n" i := 0 ForEach(func(key string, manager *Control) bool { service, _ := Lookup(key) @@ -450,19 +445,14 @@ func init() { msg += "○" + key } msg += "\n" + help - m = append(m, - message.CustomNode( - zero.BotConfig.NickName[0], - ctx.Event.SelfID, - msg, - )) + text += msg + "\n\n" return true }) - - if id := ctx.SendGroupForwardMessage( - ctx.Event.GroupID, - m, - ).Get("message_id").Int(); id == 0 { + data, err := txt2img.RenderToBase64(text, 40, 20) + if err != nil { + log.Errorf("[control] %v", err) + } + if id := ctx.SendChain(message.Image("base64://" + helper.BytesToString(data))); id == 0 { ctx.SendChain(message.Text("ERROR: 可能被风控了")) } }) diff --git a/go.mod b/go.mod index 4b375117..44c1a108 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/jinzhu/gorm v1.9.16 github.com/logoove/sqlite v1.13.0 + github.com/mattn/go-runewidth v0.0.13 github.com/mroth/weightedrand v0.4.1 github.com/shirou/gopsutil/v3 v3.21.11 github.com/sirupsen/logrus v1.8.1 @@ -52,6 +53,7 @@ require ( github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tklauser/go-sysconf v0.3.9 // indirect diff --git a/go.sum b/go.sum index 793fd38d..e587bdcf 100644 --- a/go.sum +++ b/go.sum @@ -107,6 +107,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -130,6 +132,8 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/shirou/gopsutil v3.21.8+incompatible h1:sh0foI8tMRlCidUJR+KzqWYWxrkuuPIGiO6Vp+KXdCU= github.com/shirou/gopsutil v3.21.8+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= diff --git a/main.go b/main.go index b280eee9..03d67160 100644 --- a/main.go +++ b/main.go @@ -41,6 +41,7 @@ import ( _ "github.com/FloatTech/ZeroBot-Plugin/plugin_hs" // 炉石 _ "github.com/FloatTech/ZeroBot-Plugin/plugin_juejuezi" // 绝绝子生成器 _ "github.com/FloatTech/ZeroBot-Plugin/plugin_minecraft" // MCSManager + _ "github.com/FloatTech/ZeroBot-Plugin/plugin_mocking_bird" // 拟声鸟 _ "github.com/FloatTech/ZeroBot-Plugin/plugin_moyu" // 摸鱼 _ "github.com/FloatTech/ZeroBot-Plugin/plugin_music" // 点歌 _ "github.com/FloatTech/ZeroBot-Plugin/plugin_novel" // 铅笔小说网搜索 @@ -141,7 +142,7 @@ func getKanban() string { } func main() { - printBanner() + // printBanner() // 帮助 zero.OnFullMatchGroup([]string{"/help", ".help", "菜单"}, zero.OnlyToMe).SetBlock(true).FirstPriority(). Handle(func(ctx *zero.Ctx) { diff --git a/plugin_book_review/book_review.go b/plugin_book_review/book_review.go index 1624c21e..a1bc89c2 100644 --- a/plugin_book_review/book_review.go +++ b/plugin_book_review/book_review.go @@ -2,8 +2,11 @@ package bookreview import ( + "github.com/FloatTech/ZeroBot-Plugin/utils/txt2img" + log "github.com/sirupsen/logrus" zero "github.com/wdvxdr1123/ZeroBot" "github.com/wdvxdr1123/ZeroBot/message" + "github.com/wdvxdr1123/ZeroBot/utils/helper" "github.com/FloatTech/ZeroBot-Plugin/control" ) @@ -18,12 +21,24 @@ func init() { engine.OnRegex("^书评([\u4E00-\u9FA5A-Za-z0-9]{1,25})$").SetBlock(true). Handle(func(ctx *zero.Ctx) { b := getBookReviewByKeyword(ctx.State["regex_matched"].([]string)[1]) - ctx.SendChain(message.Text(b.BookReview)) + data, err := txt2img.RenderToBase64(b.BookReview, 40, 20) + if err != nil { + log.Println("err:", err) + } + if id := ctx.SendChain(message.Image("base64://" + helper.BytesToString(data))); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控了")) + } }) engine.OnFullMatch("随机书评").SetBlock(true). Handle(func(ctx *zero.Ctx) { br := getRandomBookReview() - ctx.SendChain(message.Text(br.BookReview)) + data, err := txt2img.RenderToBase64(br.BookReview, 40, 20) + if err != nil { + log.Println("err:", err) + } + if id := ctx.SendChain(message.Image("base64://" + helper.BytesToString(data))); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控了")) + } }) } diff --git a/plugin_chouxianghua/chouxianghua.go b/plugin_chouxianghua/chouxianghua.go index 6fa98b51..59727ef5 100644 --- a/plugin_chouxianghua/chouxianghua.go +++ b/plugin_chouxianghua/chouxianghua.go @@ -1,3 +1,4 @@ +// Package chouxianghua 抽象话转化 package chouxianghua import ( @@ -12,7 +13,7 @@ const prio = 10 func init() { control.Register("chouxianghua", &control.Options{ DisableOnDefault: false, - Help: "抽象话\n- 抽象翻译xxx\n", + Help: "抽象话\n- 抽象翻译xxx", }).OnRegex("^抽象翻译((\\s|[\\r\\n]|[\\p{Han}\\p{P}A-Za-z0-9])+)$").SetBlock(true).SetPriority(prio). Handle(func(ctx *zero.Ctx) { r := cx(ctx.State["regex_matched"].([]string)[1]) diff --git a/plugin_chouxianghua/model.go b/plugin_chouxianghua/model.go index b99bc46c..c12f2e07 100644 --- a/plugin_chouxianghua/model.go +++ b/plugin_chouxianghua/model.go @@ -11,7 +11,7 @@ type emoji struct { func getPinyinByWord(word string) string { var p pinyin - db.Find("pinyin", &p, "where word = '"+word+"'") + _ = db.Find("pinyin", &p, "where word = '"+word+"'") return p.Pronun } @@ -21,6 +21,6 @@ func getPronunByDWord(w0, w1 rune) string { func getEmojiByPronun(pronun string) string { var e emoji - db.Find("emoji", &e, "where pronunciation = '"+pronun+"'") + _ = db.Find("emoji", &e, "where pronunciation = '"+pronun+"'") return e.Emoji } diff --git a/plugin_coser/coser.go b/plugin_coser/coser.go index e981765f..02eb02d9 100644 --- a/plugin_coser/coser.go +++ b/plugin_coser/coser.go @@ -19,7 +19,7 @@ import ( var ( engine = control.Register("coser", &control.Options{ DisableOnDefault: false, - Help: "三次元小姐姐\n- coser\n", + Help: "三次元小姐姐\n- coser", }) prio = 20 ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36" diff --git a/plugin_cpstory/cpstory.go b/plugin_cpstory/cpstory.go index dd4866df..a546a6f2 100644 --- a/plugin_cpstory/cpstory.go +++ b/plugin_cpstory/cpstory.go @@ -1,3 +1,4 @@ +// Package cpstory cp短打 package cpstory import ( diff --git a/plugin_funny/laugh.go b/plugin_funny/laugh.go index 4ba38c80..3831493f 100644 --- a/plugin_funny/laugh.go +++ b/plugin_funny/laugh.go @@ -1,3 +1,4 @@ +// Package funny 冷笑话 package funny import ( @@ -17,7 +18,7 @@ var ( engine = control.Register("funny", &control.Options{ DisableOnDefault: false, Help: "讲个笑话\n" + - "- 讲个笑话[@xxx]|讲个笑话[qq号]\n", + "- 讲个笑话[@xxx]|讲个笑话[qq号]", }) limit = rate.NewManager(time.Minute, 20) db = &sql.Sqlite{DBPath: dbfile} diff --git a/plugin_mocking_bird/data.go b/plugin_mocking_bird/data.go new file mode 100644 index 00000000..8d4005f2 --- /dev/null +++ b/plugin_mocking_bird/data.go @@ -0,0 +1,23 @@ +package mockingbird + +import ( + "github.com/FloatTech/ZeroBot-Plugin/utils/file" + "github.com/FloatTech/ZeroBot-Plugin/utils/process" + "github.com/sirupsen/logrus" + "os" +) + +// 加载数据库 +func init() { + go func() { + process.SleepAbout1sTo2s() + _ = os.MkdirAll(dbpath, 0755) + os.RemoveAll(cachePath) + _ = os.MkdirAll(cachePath, 0755) + _, err := file.GetLazyData(dbfile, false, true) + if err != nil { + panic(err) + } + logrus.Infoln("[mockingbird]加载实例音频") + }() +} diff --git a/plugin_mocking_bird/mocking_bird.go b/plugin_mocking_bird/mocking_bird.go new file mode 100644 index 00000000..b22c66e6 --- /dev/null +++ b/plugin_mocking_bird/mocking_bird.go @@ -0,0 +1,139 @@ +// Package mockingbird 拟声鸟 +package mockingbird + +import ( + "bytes" + "github.com/FloatTech/ZeroBot-Plugin/control" + "github.com/FloatTech/ZeroBot-Plugin/plugin_qingyunke" + fileutil "github.com/FloatTech/ZeroBot-Plugin/utils/file" + "github.com/FloatTech/ZeroBot-Plugin/utils/web" + log "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/extension/rate" + "github.com/wdvxdr1123/ZeroBot/message" + "github.com/wdvxdr1123/ZeroBot/utils/helper" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "os" + "strconv" + "time" +) + +const ( + prio = 250 + dbpath = "data/MockingBird/" + cachePath = dbpath + "cache/" + dbfile = dbpath + "降噪3.wav" + baseURL = "http://aaquatri.com/sound/" + synthesizersURL = baseURL + "api/synthesizers/" + synthesizeURL = baseURL + "api/synthesize" +) + +var ( + engine = control.Register("mockingbird", &control.Options{ + DisableOnDefault: false, + Help: "拟声鸟\n- @Bot 任意文本(任意一句话回复)", + }) + limit = rate.NewManager(time.Second*10, 1) + vocoderList = []string{"WaveRNN", "HifiGAN"} +) + +func init() { + engine.OnMessage(zero.OnlyToMe, getAcquire).SetBlock(true).SetPriority(prio). + Handle(func(ctx *zero.Ctx) { + msg := ctx.ExtractPlainText() + // 调用青云客接口 + reply, err := qingyunke.GetMessage(msg) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + // 挑出 face 表情 + textReply, _ := qingyunke.DealReply(reply) + // 拟声器生成音频 + syntPath := getSyntPath() + fileName := getWav(textReply, syntPath, vocoderList[1], ctx.Event.UserID) + // 回复 + ctx.SendChain(message.Record("file:///" + fileutil.BOTPATH + "/" + cachePath + fileName)) + + }) +} + +func getSyntPath() (syntPath string) { + data, err := web.ReqWith(synthesizersURL, "GET", "", "") + if err != nil { + log.Errorln("[mockingbird]:", err) + } + syntPath = gjson.Get(helper.BytesToString(data), "0.path").String() + return +} + +func getWav(text, syntPath, vocoder string, uid int64) (fileName string) { + fileName = strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + ".wav" + var b bytes.Buffer + w := multipart.NewWriter(&b) + // Add your file + f, err := os.Open(dbfile) + if err != nil { + log.Errorln("[mockingbird]:", err) + } + defer f.Close() + fw, err := w.CreateFormFile("file", dbfile) + if err != nil { + log.Errorln("[mockingbird]:", err) + } + if _, err = io.Copy(fw, f); err != nil { + log.Errorln("[mockingbird]:", err) + } + if fw, err = w.CreateFormField("text"); err != nil { + log.Errorln("[mockingbird]:", err) + } + if _, err = fw.Write([]byte(text)); err != nil { + log.Errorln("[mockingbird]:", err) + } + if fw, err = w.CreateFormField("synt_path"); err != nil { + log.Errorln("[mockingbird]:", err) + } + if _, err = fw.Write([]byte(syntPath)); err != nil { + log.Errorln("[mockingbird]:", err) + } + if fw, err = w.CreateFormField("vocoder"); err != nil { + log.Errorln("[mockingbird]:", err) + } + if _, err = fw.Write([]byte(vocoder)); err != nil { + log.Errorln("[mockingbird]:", err) + } + w.Close() + // Now that you have a form, you can submit it to your handler. + req, err := http.NewRequest("POST", synthesizeURL, &b) + if err != nil { + log.Errorln("[mockingbird]:", err) + } + // Don't forget to set the content type, this will contain the boundary. + req.Header.Set("Content-Type", w.FormDataContentType()) + + // Submit the request + client := &http.Client{} + res, err := client.Do(req) + if err != nil { + log.Errorln("[mockingbird]:", err) + } + // Check the response + if res.StatusCode != http.StatusOK { + log.Errorf("[mockingbird]bad status: %s", res.Status) + } + defer res.Body.Close() + data, _ := ioutil.ReadAll(res.Body) + err = ioutil.WriteFile(cachePath+fileName, data, 0666) + if err != nil { + log.Errorln("[mockingbird]:", err) + } + return +} + +func getAcquire(ctx *zero.Ctx) bool { + return limit.Load(ctx.Event.UserID).Acquire() +} diff --git a/plugin_nativewife/main.go b/plugin_nativewife/main.go index 145b5963..7117df5a 100644 --- a/plugin_nativewife/main.go +++ b/plugin_nativewife/main.go @@ -1,3 +1,4 @@ +// Package nativewife 本地老婆 package nativewife import ( diff --git a/plugin_novel/qianbi.go b/plugin_novel/qianbi.go index 2d7438f4..16f87343 100644 --- a/plugin_novel/qianbi.go +++ b/plugin_novel/qianbi.go @@ -3,6 +3,7 @@ package novel import ( "fmt" + "github.com/FloatTech/ZeroBot-Plugin/utils/txt2img" "io/ioutil" "net/http" "net/http/cookiejar" @@ -55,7 +56,6 @@ func init() { login(username, password) searchKey := ctx.State["regex_matched"].([]string)[1] searchHTML := search(searchKey) - var m message.Message doc, err := htmlquery.Parse(strings.NewReader(searchHTML)) if err != nil { log.Errorln("[novel]", err) @@ -68,6 +68,7 @@ func init() { log.Errorln("[novel]", err) } if len(list) != 0 { + text := "" for _, v := range list { bookName := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[1]/h3/a[1]")) category := htmlquery.InnerText(htmlquery.FindOne(v, "/dt/span[1]")) @@ -83,17 +84,13 @@ func init() { webpageURL := websiteURL + "/book/" + id + "/" downloadURL := websiteURL + "/modules/article/txtarticle.php?id=" + id - text := fmt.Sprintf("书名:%s\n类型:%s\n作者:%s\n状态:%s\n字数:%s\n简介:%s\n更新时间:%s\n最新章节:%s\n网页链接:%s\n下载地址:%s\n", bookName, category, author, status, wordNumbers, description, updateTime, latestChapter, webpageURL, downloadURL) - m = append(m, - message.CustomNode( - zero.BotConfig.NickName[0], - ctx.Event.SelfID, - text), - ) + text += fmt.Sprintf("书名:%s\n类型:%s\n作者:%s\n状态:%s\n字数:%s\n简介:%s\n更新时间:%s\n最新章节:%s\n网页链接:%s\n下载地址:%s\n\n", bookName, category, author, status, wordNumbers, description, updateTime, latestChapter, webpageURL, downloadURL) } - if id := ctx.SendGroupForwardMessage( - ctx.Event.GroupID, - m).Get("message_id").Int(); id == 0 { + data, err := txt2img.RenderToBase64(text, 40, 20) + if err != nil { + log.Println("err:", err) + } + if id := ctx.SendChain(message.Image("base64://" + helper.BytesToString(data))); id == 0 { ctx.SendChain(message.Text("ERROR: 可能被风控了")) } } else { @@ -121,7 +118,13 @@ func init() { webpageURL := websiteURL + "/book/" + id + "/" downloadURL := websiteURL + "/modules/article/txtarticle.php?id=" + id text := fmt.Sprintf("书名:%s\n类型:%s\n作者:%s\n状态:%s\n简介:%s\n更新时间:%s\n最新章节:%s\n网页链接:%s\n下载地址:%s\n", bookName, category, author, status, description, updateTime, latestChapter, webpageURL, downloadURL) - ctx.SendChain(message.Text(text)) + data, err := txt2img.RenderToBase64(text, 40, 20) + if err != nil { + log.Println("err:", err) + } + if id := ctx.SendChain(message.Image("base64://" + helper.BytesToString(data))); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控了")) + } } }) } diff --git a/plugin_omikuji/sensou.go b/plugin_omikuji/sensou.go index 53ff8ca7..f1fd6f08 100644 --- a/plugin_omikuji/sensou.go +++ b/plugin_omikuji/sensou.go @@ -3,6 +3,8 @@ package omikuji import ( "fmt" + "github.com/FloatTech/ZeroBot-Plugin/utils/txt2img" + "github.com/wdvxdr1123/ZeroBot/utils/helper" "math/rand" "strconv" "time" @@ -39,10 +41,13 @@ func init() { // 插件主体 }) engine.OnFullMatchGroup([]string{"解签"}).SetPriority(10).SetBlock(true). Handle(func(ctx *zero.Ctx) { - ctx.SendChain( - message.At(ctx.Event.UserID), - message.Text(getKujiByBango(bangoToday(ctx.Event.UserID))), - ) + kujiBytes, err := txt2img.RenderToBase64(getKujiByBango(bangoToday(ctx.Event.UserID)), 40, 20) + if err != nil { + log.Errorln("[omikuji]:", err) + } + if id := ctx.SendChain(message.At(ctx.Event.UserID), message.Image("base64://"+helper.BytesToString(kujiBytes))); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控了")) + } }) } diff --git a/plugin_qingyunke/qingyunke.go b/plugin_qingyunke/qingyunke.go index 947a7e77..a75011b6 100644 --- a/plugin_qingyunke/qingyunke.go +++ b/plugin_qingyunke/qingyunke.go @@ -41,23 +41,13 @@ func init() { // 插件主体 } msg := ctx.ExtractPlainText() // 调用青云客接口 - reply, err := getMessage(msg) + reply, err := GetMessage(msg) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } // 挑出 face 表情 - reg := regexp.MustCompile(`\{face:(\d+)\}(.*)`) - faceReply := -1 - var textReply string - if reg.MatchString(reply) { - faceReply, _ = strconv.Atoi(reg.FindStringSubmatch(reply)[1]) - textReply = reg.FindStringSubmatch(reply)[2] - } else { - textReply = reply - } - textReply = strings.ReplaceAll(textReply, "菲菲", zero.BotConfig.NickName[0]) - textReply = strings.ReplaceAll(textReply, "{br}", "\n") + textReply, faceReply := DealReply(reply) // 回复 time.Sleep(time.Second * 1) if ctx.Event.MessageType == "group" { @@ -103,8 +93,8 @@ const ( appid = "0" ) -// 青云客取消息 -func getMessage(msg string) (string, error) { +// GetMessage 青云客取消息 +func GetMessage(msg string) (string, error) { u := fmt.Sprintf(qykURL+"?key=%s&appid=%s&msg=%s", key, appid, url.QueryEscape(msg)) client := &http.Client{} req, err := http.NewRequest("GET", u, nil) @@ -133,6 +123,21 @@ func getMessage(msg string) (string, error) { return dataQYK.Content, nil } +// DealReply 处理青云客返回文本 +func DealReply(reply string) (textReply string, faceReply int) { + reg := regexp.MustCompile(`\{face:(\d+)\}(.*)`) + faceReply = -1 + if reg.MatchString(reply) { + faceReply, _ = strconv.Atoi(reg.FindStringSubmatch(reply)[1]) + textReply = reg.FindStringSubmatch(reply)[2] + } else { + textReply = reply + } + textReply = strings.ReplaceAll(textReply, "菲菲", zero.BotConfig.NickName[0]) + textReply = strings.ReplaceAll(textReply, "{br}", "\n") + return +} + func getAgent() string { agent := [...]string{ "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0", diff --git a/plugin_shadiao/yduanzi.go b/plugin_shadiao/yduanzi.go index ac4beb58..26589a97 100644 --- a/plugin_shadiao/yduanzi.go +++ b/plugin_shadiao/yduanzi.go @@ -22,7 +22,7 @@ func init() { return } text := gjson.Get(helper.BytesToString(data), "duanzi").String() - text = strings.Replace(text, "
", "\n", -1) + text = strings.ReplaceAll(text, "
", "\n") ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(text)) }) } diff --git a/plugin_shindan/shindan.go b/plugin_shindan/shindan.go index 4ba5b5d5..0462700b 100644 --- a/plugin_shindan/shindan.go +++ b/plugin_shindan/shindan.go @@ -2,6 +2,9 @@ package shindan import ( + "github.com/FloatTech/ZeroBot-Plugin/utils/txt2img" + log "github.com/sirupsen/logrus" + "github.com/wdvxdr1123/ZeroBot/utils/helper" "time" "github.com/FloatTech/AnimeAPI/shindanmaker" @@ -47,7 +50,18 @@ func handle(ctx *zero.Ctx) { ctx.SendChain(message.Text("ERROR: ", err)) } // TODO: 可注入 - ctx.Send(text) + switch ctx.State["id"].(int64) { + case 587874, 162207: + data, err := txt2img.RenderToBase64(text, 40, 20) + if err != nil { + log.Errorln("[shindan]:", err) + } + if id := ctx.SendChain(message.Image("base64://" + helper.BytesToString(data))); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控了")) + } + default: + ctx.Send(text) + } } // 传入 shindanmaker id diff --git a/plugin_vtb_quotation/model/model.go b/plugin_vtb_quotation/model/model.go index e1edef1b..0e62b7db 100644 --- a/plugin_vtb_quotation/model/model.go +++ b/plugin_vtb_quotation/model/model.go @@ -1,3 +1,4 @@ +// Package model vtb数据库操作 package model import ( @@ -11,7 +12,7 @@ import ( "github.com/jinzhu/gorm" _ "github.com/logoove/sqlite" // import sql - "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" ) @@ -42,9 +43,8 @@ func Open(dbpath string) (*VtbDB, error) { db, err := gorm.Open("sqlite3", dbpath) if err != nil { return nil, err - } else { - return (*VtbDB)(db), nil } + return (*VtbDB)(db), nil } // FirstCategory 第一品类 @@ -52,7 +52,7 @@ type FirstCategory struct { gorm.Model FirstCategoryIndex int64 `gorm:"column:first_category_index"` FirstCategoryName string `gorm:"column:first_category_name"` - FirstCategoryUid string `gorm:"column:first_category_uid"` + FirstCategoryUID string `gorm:"column:first_category_uid"` FirstCategoryDescription string `gorm:"column:first_category_description;type:varchar(1024)"` FirstCategoryIconPath string `gorm:"column:first_category_icon_path"` } @@ -66,7 +66,7 @@ func (FirstCategory) TableName() string { type SecondCategory struct { gorm.Model SecondCategoryIndex int64 `gorm:"column:second_category_index"` - FirstCategoryUid string `gorm:"column:first_category_uid;association_foreignkey:first_category_uid"` + FirstCategoryUID string `gorm:"column:first_category_uid;association_foreignkey:first_category_uid"` SecondCategoryName string `gorm:"column:second_category_name"` SecondCategoryAuthor string `gorm:"column:second_category_author"` SecondCategoryDescription string `gorm:"column:second_category_description"` @@ -82,7 +82,7 @@ type ThirdCategory struct { gorm.Model ThirdCategoryIndex int64 `gorm:"column:third_category_index"` SecondCategoryIndex int64 `gorm:"column:second_category_index"` - FirstCategoryUid string `gorm:"column:first_category_uid"` + FirstCategoryUID string `gorm:"column:first_category_uid"` ThirdCategoryName string `gorm:"column:third_category_name"` ThirdCategoryPath string `gorm:"column:third_category_path"` ThirdCategoryAuthor string `gorm:"column:third_category_author"` @@ -98,24 +98,14 @@ func (ThirdCategory) TableName() string { func (vdb *VtbDB) GetAllFirstCategoryMessage() string { db := (*gorm.DB)(vdb) firstStepMessage := "请选择一个vtb并发送序号:\n" - var fc FirstCategory - rows, err := db.Model(&FirstCategory{}).Rows() + var fcl []FirstCategory + err := db.Debug().Model(&FirstCategory{}).Find(&fcl).Error if err != nil { - logrus.Errorln("[vtb/model]数据库读取错误", err) + log.Errorln("[vtb/model]数据库读取错误", err) return "" } - if rows == nil { - return "" - } - defer rows.Close() - for rows.Next() { - err = db.ScanRows(rows, &fc) - if err != nil { - logrus.Errorln("[vtb/model]数据库读取错误", err) - return "" - } - // logrus.Println(fc) - firstStepMessage = firstStepMessage + strconv.FormatInt(fc.FirstCategoryIndex, 10) + ". " + fc.FirstCategoryName + "\n" + for _, v := range fcl { + firstStepMessage += strconv.FormatInt(v.FirstCategoryIndex, 10) + ". " + v.FirstCategoryName + "\n" } return firstStepMessage } @@ -123,50 +113,39 @@ func (vdb *VtbDB) GetAllFirstCategoryMessage() string { // GetAllSecondCategoryMessageByFirstIndex 取得同一个vtb所有语录类别 func (vdb *VtbDB) GetAllSecondCategoryMessageByFirstIndex(firstIndex int) string { db := (*gorm.DB)(vdb) - SecondStepMessage := "请选择一个语录类别并发送序号:\n" - var sc SecondCategory + secondStepMessage := "请选择一个语录类别并发送序号:\n" + var scl []SecondCategory var count int var fc FirstCategory db.Model(FirstCategory{}).Where("first_category_index = ?", firstIndex).First(&fc) - db.Model(&SecondCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).Count(&count) - if count == 0 { + err := db.Debug().Model(&SecondCategory{}).Find(&scl, "first_category_uid = ?", fc.FirstCategoryUID).Count(&count).Error + if err != nil || count == 0 { + log.Errorln("[vtb/model]数据库读取错误", err) return "" } - rows, err := db.Model(&SecondCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).Rows() - if err != nil { - logrus.Errorln("[vtb/model]数据库读取错误", err) + for _, v := range scl { + secondStepMessage += strconv.FormatInt(v.SecondCategoryIndex, 10) + ". " + v.SecondCategoryName + "\n" } - - for rows.Next() { - db.ScanRows(rows, &sc) - // logrus.Println(sc) - SecondStepMessage = SecondStepMessage + strconv.FormatInt(sc.SecondCategoryIndex, 10) + ". " + sc.SecondCategoryName + "\n" - } - return SecondStepMessage + return secondStepMessage } // GetAllThirdCategoryMessageByFirstIndexAndSecondIndex 取得同一个vtb同个类别的所有语录 func (vdb *VtbDB) GetAllThirdCategoryMessageByFirstIndexAndSecondIndex(firstIndex, secondIndex int) string { db := (*gorm.DB)(vdb) - ThirdStepMessage := "请选择一个语录并发送序号:\n" + thirdStepMessage := "请选择一个语录并发送序号:\n" var fc FirstCategory db.Model(FirstCategory{}).Where("first_category_index = ?", firstIndex).First(&fc) var count int - db.Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ?", fc.FirstCategoryUid, secondIndex).Count(&count) - if count == 0 { + var tcl []ThirdCategory + err := db.Debug().Model(&ThirdCategory{}).Find(&tcl, "first_category_uid = ? and second_category_index = ?", fc.FirstCategoryUID, secondIndex).Count(&count).Error + if err != nil || count == 0 { + log.Errorln("[vtb/model]数据库读取错误", err) return "" } - var tc ThirdCategory - rows, err := db.Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ?", fc.FirstCategoryUid, secondIndex).Rows() - if err != nil { - logrus.Errorln("[vtb/model]数据库读取错误", err) + for _, v := range tcl { + thirdStepMessage = thirdStepMessage + strconv.FormatInt(v.ThirdCategoryIndex, 10) + ". " + v.ThirdCategoryName + "\n" } - for rows.Next() { - db.ScanRows(rows, &tc) - // logrus.Println(tc) - ThirdStepMessage = ThirdStepMessage + strconv.FormatInt(tc.ThirdCategoryIndex, 10) + ". " + tc.ThirdCategoryName + "\n" - } - return ThirdStepMessage + return thirdStepMessage } // GetThirdCategory ... @@ -175,7 +154,7 @@ func (vdb *VtbDB) GetThirdCategory(firstIndex, secondIndex, thirdIndex int) Thir var fc FirstCategory db.Model(FirstCategory{}).Where("first_category_index = ?", firstIndex).First(&fc) var tc ThirdCategory - db.Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ? and third_category_index = ?", fc.FirstCategoryUid, secondIndex, thirdIndex).Take(&tc) + db.Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ? and third_category_index = ?", fc.FirstCategoryUID, secondIndex, thirdIndex).Take(&tc) return tc } @@ -184,20 +163,16 @@ func (vdb *VtbDB) RandomVtb() ThirdCategory { db := (*gorm.DB)(vdb) rand.Seed(time.Now().UnixNano()) var count int - db.Model(&ThirdCategory{}).Count(&count) - // logrus.Info("一共有", count, "个") var tc ThirdCategory - db.Model(&ThirdCategory{}).Offset(rand.Intn(count)).Take(&tc) - // logrus.Info(tc) + db.Model(&ThirdCategory{}).Count(&count).Offset(rand.Intn(count)).Take(&tc) return tc } -// GetFirstCategoryByFirstUid ... -func (vdb *VtbDB) GetFirstCategoryByFirstUid(firstUid string) FirstCategory { +// GetFirstCategoryByFirstUID ... +func (vdb *VtbDB) GetFirstCategoryByFirstUID(firstUID string) FirstCategory { db := (*gorm.DB)(vdb) var fc FirstCategory - db.Model(FirstCategory{}).Where("first_category_uid = ?", firstUid).Take(&fc) - // logrus.Info(fc) + db.Model(FirstCategory{}).Where("first_category_uid = ?", firstUID).Take(&fc) return fc } @@ -207,57 +182,57 @@ func (vdb *VtbDB) Close() error { return db.Close() } -const vtbUrl = "https://vtbkeyboard.moe/api/get_vtb_list" +const vtbURL = "https://vtbkeyboard.moe/api/get_vtb_list" // GetVtbList ... func (vdb *VtbDB) GetVtbList() (uidList []string) { db := (*gorm.DB)(vdb) client := &http.Client{} - req, err := http.NewRequest("GET", vtbUrl, nil) + req, err := http.NewRequest("GET", vtbURL, nil) if err != nil { - logrus.Errorln(err) + log.Errorln(err) return } // 自定义Header req.Header.Set("User-Agent", randua()) resp, err := client.Do(req) if err != nil { - logrus.Errorln(err) + log.Errorln(err) return } defer resp.Body.Close() bytes, err := ioutil.ReadAll(resp.Body) if err != nil { - logrus.Errorln(err) + log.Errorln(err) return } - vtbListStr, err := strconv.Unquote(strings.Replace(strconv.Quote(string(bytes)), `\\u`, `\u`, -1)) + vtbListStr, err := strconv.Unquote(strings.ReplaceAll(strconv.Quote(string(bytes)), `\\u`, `\u`)) if err != nil { - logrus.Errorln(err) + log.Errorln(err) return } count := gjson.Get(vtbListStr, "#").Int() for i := int64(0); i < count; i++ { item := gjson.Get(vtbListStr, strconv.FormatInt(i, 10)) - logrus.Println(item) + log.Println(item) fc := FirstCategory{ FirstCategoryIndex: i, FirstCategoryName: item.Get("name").String(), FirstCategoryDescription: item.Get("description").String(), FirstCategoryIconPath: item.Get("icon_path").String(), - FirstCategoryUid: item.Get("uid").String(), + FirstCategoryUID: item.Get("uid").String(), } - logrus.Println(fc) + log.Println(fc) - if err := db.Debug().Model(&FirstCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).First(&fc).Error; err != nil { + if err := db.Debug().Model(&FirstCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUID).First(&fc).Error; err != nil { if gorm.IsRecordNotFoundError(err) { db.Debug().Model(&FirstCategory{}).Create(&fc) // newUser not user } } else { - db.Debug().Model(&FirstCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).Update( + db.Debug().Model(&FirstCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUID).Update( map[string]interface{}{ "first_category_index": i, "first_category_name": item.Get("name").String(), @@ -265,7 +240,7 @@ func (vdb *VtbDB) GetVtbList() (uidList []string) { "first_category_icon_path": item.Get("icon_path").String(), }) } - uidList = append(uidList, fc.FirstCategoryUid) + uidList = append(uidList, fc.FirstCategoryUID) } return uidList @@ -274,45 +249,45 @@ func (vdb *VtbDB) GetVtbList() (uidList []string) { // StoreVtb ... func (vdb *VtbDB) StoreVtb(uid string) { db := (*gorm.DB)(vdb) - vtbUrl := "https://vtbkeyboard.moe/api/get_vtb_page?uid=" + uid + vtbURL := "https://vtbkeyboard.moe/api/get_vtb_page?uid=" + uid client := &http.Client{} - req, err := http.NewRequest("GET", vtbUrl, nil) + req, err := http.NewRequest("GET", vtbURL, nil) if err != nil { - logrus.Errorln(err) + log.Errorln(err) return } // 自定义Header req.Header.Set("User-Agent", randua()) resp, err := client.Do(req) if err != nil { - logrus.Errorln(err) + log.Errorln(err) return } defer resp.Body.Close() bytes, err := ioutil.ReadAll(resp.Body) if err != nil { - logrus.Errorln(err) + log.Errorln(err) return } - vtbStr, err := strconv.Unquote(strings.Replace(strconv.Quote(string(bytes)), `\\u`, `\u`, -1)) + vtbStr, err := strconv.Unquote(strings.ReplaceAll(strconv.Quote(string(bytes)), `\\u`, `\u`)) if err != nil { - logrus.Errorln(err) + log.Errorln(err) return } secondCount := gjson.Get(vtbStr, "data.voices.#").Int() - logrus.Println("二级品类一共有", secondCount) + log.Println("二级品类一共有", secondCount) for secondIndex := int64(0); secondIndex < secondCount; secondIndex++ { secondItem := gjson.Get(vtbStr, "data.voices."+strconv.FormatInt(secondIndex, 10)) - logrus.Println(secondItem) + log.Println(secondItem) sc := SecondCategory{ SecondCategoryName: secondItem.Get("categoryName").String(), SecondCategoryIndex: secondIndex, SecondCategoryAuthor: secondItem.Get("author").String(), SecondCategoryDescription: secondItem.Get("categoryDescription.zh-CN").String(), - FirstCategoryUid: uid, + FirstCategoryUID: uid, } if err := db.Debug().Model(&SecondCategory{}).Where("first_category_uid = ? and second_category_index = ?", uid, secondIndex).First(&sc).Error; err != nil { @@ -329,20 +304,20 @@ func (vdb *VtbDB) StoreVtb(uid string) { }) } thirdCount := secondItem.Get("voiceList.#").Int() - logrus.Println("三级品类一共有", thirdCount) + log.Println("三级品类一共有", thirdCount) for thirdIndex := int64(0); thirdIndex < thirdCount; thirdIndex++ { thirdItem := secondItem.Get("voiceList." + strconv.FormatInt(thirdIndex, 10)) - logrus.Println(thirdItem) + log.Println(thirdItem) tc := ThirdCategory{ ThirdCategoryName: thirdItem.Get("name").String(), ThirdCategoryIndex: thirdIndex, ThirdCategoryDescription: thirdItem.Get("description.zh-CN").String(), - FirstCategoryUid: uid, + FirstCategoryUID: uid, SecondCategoryIndex: secondIndex, ThirdCategoryPath: thirdItem.Get("path").String(), ThirdCategoryAuthor: thirdItem.Get("author").String(), } - logrus.Println(tc) + log.Println(tc) if err := db.Debug().Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ? and third_category_index = ?", uid, secondIndex, thirdIndex).First(&tc).Error; err != nil { diff --git a/plugin_vtb_quotation/vtb_quotation.go b/plugin_vtb_quotation/vtb_quotation.go index 2bc18617..6c274cff 100644 --- a/plugin_vtb_quotation/vtb_quotation.go +++ b/plugin_vtb_quotation/vtb_quotation.go @@ -1,3 +1,4 @@ +// Package vtbquotation vtb经典语录 package vtbquotation import ( @@ -8,17 +9,21 @@ import ( "time" _ "github.com/logoove/sqlite" // use sql - "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" zero "github.com/wdvxdr1123/ZeroBot" "github.com/wdvxdr1123/ZeroBot/message" + "github.com/wdvxdr1123/ZeroBot/utils/helper" "github.com/FloatTech/ZeroBot-Plugin/control" "github.com/FloatTech/ZeroBot-Plugin/plugin_vtb_quotation/model" + "github.com/FloatTech/ZeroBot-Plugin/utils/txt2img" ) -const regStr = ".*/(.*)" -const dbpath = "data/VtbQuotation/" -const dbfile = dbpath + "vtb.db" +const ( + regStr = ".*/(.*)" + dbpath = "data/VtbQuotation/" + dbfile = dbpath + "vtb.db" +) var engine = control.Register("vtbquotation", &control.Options{ DisableOnDefault: false, @@ -36,13 +41,16 @@ func init() { Repeat() // 不断监听复读 db, err := model.Open(dbfile) if err != nil { - logrus.Errorln(err) + log.Errorln("[vtb]:", err) return } defer db.Close() defer cancel() - firstStepMessage := db.GetAllFirstCategoryMessage() - if id := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(firstStepMessage)); id == 0 { + firstStepImageBytes, err := txt2img.RenderToBase64(db.GetAllFirstCategoryMessage(), 40, 20) + if err != nil { + log.Errorln("[vtb]:", err) + } + if id := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Image("base64://"+helper.BytesToString(firstStepImageBytes))); id == 0 { ctx.SendChain(message.Text("ERROR: 可能被风控了")) } // 步骤0,1,2,依次选择3个类别 @@ -66,14 +74,26 @@ func init() { ctx.SendChain(message.Reply(e.MessageID), message.Text("请输入正确的序号,三次输入错误,指令可退出重输")) errorCount++ } else { - SecondStepMessage := db.GetAllSecondCategoryMessageByFirstIndex(firstIndex) - // log.Println(SecondStepMessage) - if SecondStepMessage == "" { + secondStepMessage := db.GetAllSecondCategoryMessageByFirstIndex(firstIndex) + // log.Println(secondStepMessage) + if secondStepMessage == "" { ctx.SendChain(message.Reply(e.MessageID), message.Text("你选择的序号没有内容,请重新选择,三次输入错误,指令可退出重输")) - ctx.SendChain(message.Reply(e.MessageID), message.Text(db.GetAllFirstCategoryMessage())) + firstStepImageBytes, err := txt2img.RenderToBase64(db.GetAllFirstCategoryMessage(), 40, 20) + if err != nil { + log.Errorln("[vtb]:", err) + } + if id := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Image("base64://"+helper.BytesToString(firstStepImageBytes))); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控了")) + } errorCount++ } else { - ctx.SendChain(message.Reply(e.MessageID), message.Text(SecondStepMessage)) + secondStepMessageBytes, err := txt2img.RenderToBase64(secondStepMessage, 40, 20) + if err != nil { + log.Errorln("[vtb]:", err) + } + if id := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Image("base64://"+helper.BytesToString(secondStepMessageBytes))); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控了")) + } step++ } } @@ -85,14 +105,26 @@ func init() { ctx.SendChain(message.Reply(e.MessageID), message.Text("请输入正确的序号,三次输入错误,指令可退出重输")) errorCount++ } else { - ThirdStepMessage := db.GetAllThirdCategoryMessageByFirstIndexAndSecondIndex(firstIndex, secondIndex) - // log.Println(ThirdStepMessage) - if ThirdStepMessage == "" { + thirdStepMessage := db.GetAllThirdCategoryMessageByFirstIndexAndSecondIndex(firstIndex, secondIndex) + // log.Println(thirdStepMessage) + if thirdStepMessage == "" { ctx.SendChain(message.Reply(e.MessageID), message.Text("你选择的序号没有内容,请重新选择,三次输入错误,指令可退出重输")) - ctx.SendChain(message.Reply(e.MessageID), message.Text(db.GetAllSecondCategoryMessageByFirstIndex(firstIndex))) + secondStepMessageBytes, err := txt2img.RenderToBase64(db.GetAllSecondCategoryMessageByFirstIndex(firstIndex), 40, 20) + if err != nil { + log.Errorln("[vtb]:", err) + } + if id := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Image("base64://"+helper.BytesToString(secondStepMessageBytes))); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控了")) + } errorCount++ } else { - ctx.SendChain(message.Reply(e.MessageID), message.Text(ThirdStepMessage)) + thirdStepMessageBytes, err := txt2img.RenderToBase64(thirdStepMessage, 40, 20) + if err != nil { + log.Errorln("[vtb]:", err) + } + if id := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Image("base64://"+helper.BytesToString(thirdStepMessageBytes))); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控了")) + } step++ } } @@ -109,7 +141,13 @@ func init() { recURL := tc.ThirdCategoryPath if recURL == "" { ctx.SendChain(message.Reply(e.MessageID), message.Text("没有内容请重新选择,三次输入错误,指令可退出重输")) - ctx.SendChain(message.Reply(e.MessageID), message.Text(db.GetAllFirstCategoryMessage())) + firstStepImageBytes, err := txt2img.RenderToBase64(db.GetAllFirstCategoryMessage(), 40, 20) + if err != nil { + log.Errorln("[vtb]:", err) + } + if id := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Image("base64://"+helper.BytesToString(firstStepImageBytes))); id == 0 { + ctx.SendChain(message.Text("ERROR: 可能被风控了")) + } errorCount++ step = 1 } else { @@ -138,20 +176,17 @@ func init() { Handle(func(ctx *zero.Ctx) { db, err := model.Open(dbfile) if err != nil { - logrus.Errorln(err) + log.Errorln("[vtb]:", err) return } tc := db.RandomVtb() - fc := db.GetFirstCategoryByFirstUid(tc.FirstCategoryUid) + fc := db.GetFirstCategoryByFirstUID(tc.FirstCategoryUID) if (tc != model.ThirdCategory{}) && (fc != model.FirstCategory{}) { reg := regexp.MustCompile(regStr) recURL := tc.ThirdCategoryPath if reg.MatchString(recURL) { - // log.Println(reg.FindStringSubmatch(recordUrl)[1]) - // log.Println(url.QueryEscape(reg.FindStringSubmatch(recordUrl)[1])) recURL = strings.ReplaceAll(recURL, reg.FindStringSubmatch(recURL)[1], url.QueryEscape(reg.FindStringSubmatch(recURL)[1])) recURL = strings.ReplaceAll(recURL, "+", "%20") - // log.Println(recordUrl) } ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("请欣赏"+fc.FirstCategoryName+"的《"+tc.ThirdCategoryName+"》")) ctx.SendChain(message.Record(recURL)) diff --git a/utils/txt2img/txt2img.go b/utils/txt2img/txt2img.go new file mode 100644 index 00000000..d344d92d --- /dev/null +++ b/utils/txt2img/txt2img.go @@ -0,0 +1,89 @@ +// Package txt2img 文字转图片 +package txt2img + +import ( + "bytes" + "encoding/base64" + "github.com/FloatTech/ZeroBot-Plugin/utils/file" + "github.com/FloatTech/ZeroBot-Plugin/utils/process" + "github.com/fogleman/gg" + "github.com/mattn/go-runewidth" + log "github.com/sirupsen/logrus" + "image/jpeg" + "os" + "strings" +) + +const ( + whitespace = "\t\n\r\x0b\x0c" + fontpath = "data/Font/" + fontfile = fontpath + "regular.ttf" +) + +// 加载数据库 +func init() { + go func() { + process.SleepAbout1sTo2s() + _ = os.MkdirAll(fontpath, 0755) + _, _ = file.GetLazyData(fontfile, false, true) + }() +} + +// RenderToBase64 文字转base64 +func RenderToBase64(text string, width, fontSize int) (base64Bytes []byte, err error) { + canvas, err := Render(text, width, fontSize) + if err != nil { + log.Println("err:", err) + return nil, err + } + // 转成 base64 + buffer := new(bytes.Buffer) + encoder := base64.NewEncoder(base64.StdEncoding, buffer) + var opt jpeg.Options + opt.Quality = 70 + if err = jpeg.Encode(encoder, canvas.Image(), &opt); err != nil { + return nil, err + } + encoder.Close() + base64Bytes = buffer.Bytes() + return +} + +// Render 文字转图片 +func Render(text string, width, fontSize int) (canvas *gg.Context, err error) { + buff := make([]string, 0) + line := "" + count := 0 + for _, v := range text { + c := string(v) + if strings.Contains(whitespace, c) { + buff = append(buff, strings.TrimSpace(line)) + count = 0 + line = "" + continue + } + if count <= width { + line += c + count += runewidth.StringWidth(c) + } else { + buff = append(buff, line) + line = c + count = runewidth.StringWidth(c) + } + } + + canvas = gg.NewContext((fontSize+3)*width/2, (len(buff)+2)*fontSize) + canvas.SetRGB(1, 1, 1) + canvas.Clear() + canvas.SetRGB(0, 0, 0) + if err = canvas.LoadFontFace(fontfile, float64(fontSize)); err != nil { + log.Println("err:", err) + return nil, err + } + for i, v := range buff { + if v != "" { + canvas.DrawString(v, float64(width/2), float64((i+2)*fontSize)) + } + } + return +}