Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于fortune运势插件报错生成空文件的一个问题 #593

Closed
byx2020 opened this issue Feb 22, 2023 · 8 comments
Closed

关于fortune运势插件报错生成空文件的一个问题 #593

byx2020 opened this issue Feb 22, 2023 · 8 comments

Comments

@byx2020
Copy link

byx2020 commented Feb 22, 2023

昨天将zbp从1.6.0-beta1更新到1.6.2-beta4之后,
发现fortune运势插件在被通过指令触发生成图片时较大概率会出现如下所示的报错,

�[0m�[33m[WARNING] [file]文件 data/Fortune/全部混合.zip 存在, 已跳过md5检查
�[0m�[31m[ERROR] [bot] execute handler err: interface conversion: interface {} is nil, not
 []uint8
goroutine 98742 [running]:
runtime/debug.Stack()
        C:/Program Files/Go/src/runtime/debug/stack.go:24 +0x65
github.com/wdvxdr1123/ZeroBot.match.func2.1.1()
        C:/Users/Administrator/go/pkg/mod/github.com/wdvxdr1123/!zero!bot@v1.6.9/bot.go:24
9 +0x45
panic({0xcf2f20, 0xc001468030})
        C:/Program Files/Go/src/runtime/panic.go:884 +0x213
github.com/FloatTech/ZeroBot-Plugin/plugin/fortune.init.0.func3.1()
        C:/Software/ZeroBot/ZeroBot/plugin/fortune/fortune.go:151 +0x185
github.com/FloatTech/zbputils/img/pool.SendImageFromPool({0xc00141a880?, 0xdb70e0?}, {0xc0
0141a880, 0x33}, 0xc001441f38, 0xc0012403b0, 0xc001240470)
        C:/Users/Administrator/go/pkg/mod/github.com/!float!tech/zbputils@v1.6.2-0.2023021
5092613-4a7ebf458f16/img/pool/sender.go:20 +0x111
github.com/FloatTech/ZeroBot-Plugin/plugin/fortune.init.0.func3(0xc00127f680)
        C:/Software/ZeroBot/ZeroBot/plugin/fortune/fortune.go:146 +0x7e7
github.com/wdvxdr1123/ZeroBot.match.func2.1()
        C:/Users/Administrator/go/pkg/mod/github.com/wdvxdr1123/!zero!bot@v1.6.9/bot.go:25
2 +0x68
created by github.com/wdvxdr1123/ZeroBot.match.func2
        C:/Users/Administrator/go/pkg/mod/github.com/wdvxdr1123/!zero!bot@v1.6.9/bot.go:24
5 +0xaa

同时会在“data\fortune\cache”目录下面生成一个如下图所示的空文件

image

但有一个奇怪的点是,也不是所有的生成都会出错,偶尔还是有正常生成并发送的(比如上图空文件下面的就是正常生成的文件),

简单看了一下报错内容,似乎是接口转换出了问题,但我能力有限也不知道应该如何解决,

自行尝试了修改调试多次无果之后,只好来这里打扰一下大佬,导致这个问题可能的原因会是什么,应该如何进行修复处理?

(关于“data/Fortune/全部混合.zip”这个文件,就是简单将所有类型的运势底图全部打包到了一个zip里面,而且我也测试过其他单独类型的运势底图,也是会出现上面相同的报错,因此我个人初步排除掉了“data/Fortune/全部混合.zip”文件导致此问题的可能性,至于“plugin\fortune\fortune.go”文件,我仅仅只是增加了全部混合这一个底图类型并将其设置为默认类型,其他代码都没有任何改动,这两个操作在1.6.0-beta1版本里面是可以正常使用的,就是不知道在1.6.2-beta4上为什么出问题了)

@fumiama
Copy link
Member

fumiama commented Feb 22, 2023

你可以先将全部代码恢复到官方状态再试。这个转换在官方代码下是必然成功的,我不知道你到底是怎么改的,所以无法做出判断。
截屏2023-02-22 下午8 21 56

@byx2020
Copy link
Author

byx2020 commented Feb 22, 2023

@fumiama 非常感谢大佬能够抽时间回复指点,我收到回复后马上去测试了一下,将全部代码恢复到官方状态后确实恢复正常可以正常发送图片了,看起来确实应该是代码的问题,
不过奇怪的是,我以前也是这么修改的,但一直没出过问题(用了应该有一两年了),不知道为什么这次更新后就不行了,
下面我把修改过后的代码贴出来,想麻烦大佬有空的话可以帮忙看一下,非常感谢(我修改的的地方总共有三处,里面已经标注了出来)

// Package fortune 每日运势
package fortune

import (
	"archive/zip"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"image"
	"io"
	"os"
	"strconv"
	"github.com/FloatTech/gg" // 注册了 jpg png gif
	"github.com/FloatTech/imgfactory"
	"github.com/sirupsen/logrus"
	zero "github.com/wdvxdr1123/ZeroBot"
	"github.com/wdvxdr1123/ZeroBot/message"
	"github.com/wdvxdr1123/ZeroBot/utils/helper"
	fcext "github.com/FloatTech/floatbox/ctxext"
	"github.com/FloatTech/floatbox/file"
	"github.com/FloatTech/floatbox/math"
	ctrl "github.com/FloatTech/zbpctrl"
	"github.com/FloatTech/zbputils/control"
	"github.com/FloatTech/zbputils/ctxext"
	"github.com/FloatTech/zbputils/img/pool"
)

const (
	// 底图缓存位置
	images = "data/Fortune/"


	// 基础文件位置
	// 原版为:omikujson = "data/Fortune/text.json"
	omikujson = "data/Fortune/浅草百签.json"

	// 生成图缓存位置
	cache = images + "cache/"
)

var (

	// 底图类型列表
	// 原版为:table = [...]string{"全部混合", "车万", "DC4", "爱因斯坦", "星空列车", "樱云之恋", "富婆妹", "李清歌", "公主连结", "原神", "明日方舟", "碧蓝航线", "碧蓝幻想", "战双", "阴阳师", "赛马娘", "东方归言录", "奇异恩典", "夏日口袋", "ASoul", "Hololive"}
	table = [...]string{"全部混合", "车万", "DC4", "爱因斯坦", "星空列车", "樱云之恋", "富婆妹", "李清歌", "公主连结", "原神", "明日方舟", "碧蓝航线", "碧蓝幻想", "战双", "阴阳师", "赛马娘", "东方归言录", "奇异恩典", "夏日口袋", "ASoul", "Hololive"}

	// 映射底图与 index
	index = make(map[string]uint8)
	// 签文
	omikujis []map[string]string
)

func init() {
	// 插件主体
	en := control.Register("fortune", &ctrl.Options[*zero.Ctx]{
		DisableOnDefault: false,
		Brief:            "每日运势",
		Help: "- 运势 | 抽签\n" +
			"- 设置底图[车万 | DC4 | 爱因斯坦 | 星空列车 | 樱云之恋 | 富婆妹 | 李清歌 | 公主连结 | 原神 | 明日方舟 | 碧蓝航线 | 碧蓝幻想 | 战双 | 阴阳师 | 赛马娘 | 东方归言录 | 奇异恩典 | 夏日口袋 | ASoul | Hololive]",
		PublicDataFolder: "Fortune",
	})
	_ = os.RemoveAll(cache)
	err := os.MkdirAll(cache, 0755)
	if err != nil {
		panic(err)
	}
	for i, s := range table {
		index[s] = uint8(i)
	}
	en.OnRegex(`^设置底图\s?(.*)`).SetBlock(true).
		Handle(func(ctx *zero.Ctx) {
			gid := ctx.Event.GroupID
			if gid <= 0 {
				// 个人用户设为负数
				gid = -ctx.Event.UserID
			}
			i, ok := index[ctx.State["regex_matched"].([]string)[1]]
			if ok {
				c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
				if ok {
					err := c.SetData(gid, int64(i)&0xff)
					if err != nil {
						ctx.SendChain(message.Text("设置失败:", err))
						return
					}
					ctx.SendChain(message.Text("设置成功~"))
					return
				}
				ctx.SendChain(message.Text("设置失败: 找不到插件"))
				return
			}
			ctx.SendChain(message.Text("没有这个底图哦~"))
		})
	en.OnFullMatchGroup([]string{"运势", "抽签"}, fcext.DoOnceOnSuccess(
		func(ctx *zero.Ctx) bool {
			data, err := file.GetLazyData(omikujson, control.Md5File, false)
			if err != nil {
				ctx.SendChain(message.Text("ERROR: ", err))
				return false
			}
			err = json.Unmarshal(data, &omikujis)
			if err != nil {
				ctx.SendChain(message.Text("ERROR: ", err))
				return false
			}
			fontdata, err := file.GetLazyData("data/Font/sakura.ttf", control.Md5File, true)
			if err != nil {
				ctx.SendChain(message.Text("ERROR: ", err))
				return false
			}
			ctx.State["fontdata"] = fontdata
			return true
		},
	)).Limit(ctxext.LimitByGroup).SetBlock(true).
		Handle(func(ctx *zero.Ctx) {

			// 获取该群背景类型,默认车万
			// 原版为:kind := "车万"
			kind := "全部混合"
			
gid := ctx.Event.GroupID
			if gid <= 0 {
				// 个人用户设为负数
				gid = -ctx.Event.UserID
			}
			logrus.Debugln("[fortune]gid:", ctx.Event.GroupID, "uid:", ctx.Event.UserID)
			c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
			if ok {
				v := uint8(c.GetData(gid) & 0xff)
				if int(v) < len(table) {
					kind = table[v]
				}
			}
			// 检查背景图片是否存在
			zipfile := images + kind + ".zip"
			_, err := file.GetLazyData(zipfile, control.Md5File, false)
			if err != nil {
				ctx.SendChain(message.Text("ERROR: ", err))
				return
			}

			// 随机获取背景
			background, index, err := randimage(zipfile, ctx)
			if err != nil {
				ctx.SendChain(message.Text("ERROR: ", err))
				return
			}

			// 随机获取签文
			randtextindex := fcext.RandSenderPerDayN(ctx.Event.UserID, len(omikujis))
			title, text := omikujis[randtextindex]["title"], omikujis[randtextindex]["content"]
			digest := md5.Sum(helper.StringToBytes(zipfile + strconv.Itoa(index) + title + text))
			cachefile := cache + hex.EncodeToString(digest[:])

			err = pool.SendImageFromPool(cachefile, cachefile, func() error {
				f, err := os.Create(cachefile)
				if err != nil {
					return err
				}
				_, err = draw(background, ctx.State["fontdata"].([]byte), title, text, f)
				_ = f.Close()
				return err
			}, ctxext.Send(ctx), ctxext.GetMessage(ctx))
			if err != nil {
				ctx.SendChain(message.Text("ERROR: ", err))
				return
			}
		})
}

// @function randimage 随机选取zip内的文件
// @param path zip路径
// @param ctx *zero.Ctx
// @return 文件路径 & 错误信息
func randimage(path string, ctx *zero.Ctx) (im image.Image, index int, err error) {
	reader, err := zip.OpenReader(path)
	if err != nil {
		return
	}
	defer reader.Close()

	file := reader.File[fcext.RandSenderPerDayN(ctx.Event.UserID, len(reader.File))]
	f, err := file.Open()
	if err != nil {
		return
	}
	defer f.Close()

	im, _, err = image.Decode(f)
	return
}

// @function draw 绘制运势图
// @param background 背景图片路径
// @param seed 随机数种子
// @param title 签名
// @param text 签文
// @return 错误信息
func draw(back image.Image, fontdata []byte, title, txt string, f io.Writer) (int64, error) {
	canvas := gg.NewContext(back.Bounds().Size().Y, back.Bounds().Size().X)
	canvas.DrawImage(back, 0, 0)
	// 写标题
	canvas.SetRGB(1, 1, 1)
	if err := canvas.ParseFontFace(fontdata, 45); err != nil {
		return -1, err
	}
	sw, _ := canvas.MeasureString(title)
	canvas.DrawString(title, 140-sw/2, 112)
	// 写正文
	canvas.SetRGB(0, 0, 0)
	if err := canvas.ParseFontFace(fontdata, 23); err != nil {
		return -1, err
	}
	tw, th := canvas.MeasureString("测")
	tw, th = tw+10, th+10
	r := []rune(txt)
	xsum := rowsnum(len(r), 9)
	switch xsum {
	default:
		for i, o := range r {
			xnow := rowsnum(i+1, 9)
			ysum := math.Min(len(r)-(xnow-1)*9, 9)
			ynow := i%9 + 1
			canvas.DrawString(string(o), -offest(xsum, xnow, tw)+115, offest(ysum, ynow, th)+320.0)
		}
	case 2:
		div := rowsnum(len(r), 2)
		for i, o := range r {
			xnow := rowsnum(i+1, div)
			ysum := math.Min(len(r)-(xnow-1)*div, div)
			ynow := i%div + 1
			switch xnow {
			case 1:
				canvas.DrawString(string(o), -offest(xsum, xnow, tw)+115, offest(9, ynow, th)+320.0)
			case 2:
				canvas.DrawString(string(o), -offest(xsum, xnow, tw)+115, offest(9, ynow+(9-ysum), th)+320.0)
			}
		}
	}
	return imgfactory.WriteTo(canvas.Image(), f)
}

func offest(total, now int, distance float64) float64 {
	if total%2 == 0 {
		return (float64(now-total/2) - 1) * distance
	}
	return (float64(now-total/2) - 1.5) * distance
}

func rowsnum(total, div int) int {
	temp := total / div
	if total%div != 0 {
		temp++
	}
	return temp
}

改了半天,发现代码块内好像没有办法加粗特定部分的代码,
好吧,就是我修改了这三个地方,下面单独列出来

// 基础文件位置
// 原版为:omikujson = "data/Fortune/text.json"
omikujson = "data/Fortune/浅草百签.json"

// 底图类型列表
// 原版为:table = [...]string{"车万", "DC4", "爱因斯坦", "星空列车", "樱云之恋", "富婆妹", "李清歌", "公主连结", "原神", "明日方舟", "碧蓝航线", "碧蓝幻想", "战双", "阴阳师", "赛马娘", "东方归言录", "奇异恩典", "夏日口袋", "ASoul", "Hololive"}
table = [...]string{"全部混合", "车万", "DC4", "爱因斯坦", "星空列车", "樱云之恋", "富婆妹", "李清歌", "公主连结", "原神", "明日方舟", "碧蓝航线", "碧蓝幻想", "战双", "阴阳师", "赛马娘", "东方归言录", "奇异恩典", "夏日口袋", "ASoul", "Hololive"}

// 获取该群背景类型,默认车万
// 原版为:kind := "车万"
kind := "全部混合"

可以看到,修改的地方实际上很少,其他地方都没有修改,
这些修改按逻辑来说不应该出问题的,难道会是数据目录下的文件出了什么问题吗?

image

但这些文件有点大,如果大佬需要查看的话,可以让我再发出来

image
image

内容我看起来没什么问题,因为以前用的也是这些,都是可以正常使用的

@fumiama
Copy link
Member

fumiama commented Feb 23, 2023

如果你只改了这些,那恢复原版应该也会出问题的,你再观察几天。

@byx2020
Copy link
Author

byx2020 commented Feb 23, 2023

@fumiama 好的,那我先把运势底图类型都改回原版放几天看看效果

@byx2020
Copy link
Author

byx2020 commented Feb 23, 2023

@fumiama 刚刚我和几个群友测试了一下,所有代码和对应的签文文件都恢复了原版之后,确实也出现了这个问题,
本来想给大佬总结一下这个问题出现的规律,但试了几十次,也没发现什么明显的规律,
尽管这个问题出现的概率比较大,平均每10个使用运势指令的群友里面,有7-9个会出现这个问题,但也还是有能够正常生成并发送运势图片的结果,偶尔还会出现连续两个,就很随机,
不过我还是有了一点发现,

  1. 每次重启zbp之后,第一次使用运势指令必定能够成功正常生成并发送图片,不论当前设置的底图类型是什么。
  2. 对于一个在“车万”底图类型下已经获得了正常运势图片的群友,如果底图类型变成了“车万”以外的类型,那这个群友再次使用运势指令也会出现问题报错,除非将底图类型改回"车万",对于其他类型也是同样。
  3. 对于一个第一次使用运势指令就出现问题报错的群友,不论如何修改底图类型,都始终无法正常生成。

通过上面的测试,我感觉基本可以排除掉是由于修改运势插件代码、签文或底图所导致的问题了,会不会有可能是我的GO环境有问题
因为我在这次更新zbp之前,把GO更新到了最新版,难道在这个过程中,出现了什么我所没有察觉到的问题
image

@fumiama
Copy link
Member

fumiama commented Feb 24, 2023

试试最新master

@byx2020
Copy link
Author

byx2020 commented Feb 24, 2023

试试最新master

好的,我晚上再测试

@byx2020
Copy link
Author

byx2020 commented Feb 27, 2023

试试最新master

@fumiama 今天下午更新最新master之后到现在都没有出过问题了,非常感谢大佬能够抽时间修复。💐

@byx2020 byx2020 closed this as completed Feb 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants