You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: store.go
    path of file: ./repos/ZeroBot-Plugin/plugin/mcfish
    
    the code of the file until where you have to start completion: // Package mcfish 钓鱼模拟器
package mcfish

import (
	"image"
	"image/color"
	"strconv"
	"strings"
	"time"

	"github.com/FloatTech/AnimeAPI/wallet"
	"github.com/FloatTech/floatbox/file"
	"github.com/FloatTech/floatbox/math"
	"github.com/FloatTech/gg"
	"github.com/FloatTech/imgfactory"
	"github.com/FloatTech/zbputils/control"
	"github.com/FloatTech/zbputils/img/text"
	"github.com/sirupsen/logrus"
	zero "github.com/wdvxdr1123/ZeroBot"
	"github.com/wdvxdr1123/ZeroBot/extension/rate"
	"github.com/wdvxdr1123/ZeroBot/message"
)

var (
	storeLimiter = rate.NewManager[int64](time.Second*3, 1)
	refresh      = false
	timeNow      = 0
	refreshFish  = func(ctx *zero.Ctx) bool {
		if refresh && timeNow == time.Now().Day() {
			return true
		}
		refresh, err := dbdata.refreshStroeInfo()
		if err != nil {
			ctx.SendChain(message.Text("[ERROR at store.go.1]:", err))
			return refresh
		}
		timeNow = time.Now().Day()
		return refresh
	}
)

func limitSet(ctx *zero.Ctx) *rate.Limiter {
	return storeLimiter.Load(ctx.Event.UserID)
}

func init() {
	engine.OnFullMatchGroup([]string{"钓鱼看板", "钓鱼商店"}, getdb, refreshFish).SetBlock(true).Limit(limitSet).Handle(func(ctx *zero.Ctx) {
		infos, err := dbdata.getStoreInfo()
		if err != nil {
			ctx.SendChain(message.Text("[ERROR at store.go.2]:", err))
			return
		}
		var picImage image.Image
		if len(infos) == 0 {
			picImage, err = drawStroeEmptyImage()
		} else {
			picImage, err = drawStroeInfoImage(infos)
		}
		if err != nil {
			ctx.SendChain(message.Text("[ERROR at store.go.3]:", err))
			return
		}
		pic, err := imgfactory.ToBytes(picImage)
		if err != nil {
			ctx.SendChain(message.Text("[ERROR at store.go.4]:", err))
			return
		}
		ctx.SendChain(message.ImageBytes(pic))
	})
	engine.OnRegex(`^出售(`+strings.Join(thingList, "|")+`)\s*(\d*)$`, getdb, refreshFish).SetBlock(true).Limit(limitSet).Handle(func(ctx *zero.Ctx) {
		uid := ctx.Event.UserID
		thingName := ctx.State["regex_matched"].([]string)[1]
		if strings.Contains(thingName, "竿") {
			times, err := dbdata.checkCanSalesFor(uid, true)
			if err != nil {
				ctx.SendChain(message.Text("[ERROR at store.go.75]:", err))
				return
			}
			if times <= 0 {
				ctx.SendChain(message.Text("出售次数已达到上限,明天再来售卖吧"))
				return
			}
		}
		number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
		if number == 0 || strings.Contains(thingName, "竿") {
			number = 1
		}
		articles, err := dbdata.getUserThingInfo(uid, thingName)
		if err != nil {
			ctx.SendChain(message.Text("[ERROR at store.go.5]:", err))
			return
		}
		if len(articles) == 0 {
			ctx.SendChain(message.Text("你的背包不存在该物品"))
			return
		}
	
    
func drawStroeInfoImage(stroeInfo []store) (picImage image.Image, err error) {
	fontdata, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true)
	if err != nil {
		return nil, err
	}
	canvas := gg.NewContext(1, 1)
	err = canvas.ParseFontFace(fontdata, 100)
	if err != nil {
		return nil, err
	}
	titleW, titleH := canvas.MeasureString("价格信息")

	err = canvas.ParseFontFace(fontdata, 50)
	if err != nil {
		return nil, err
	}
	_, textH := canvas.MeasureString("高度")
	nameW, _ := canvas.MeasureString("下界合金竿(100/100/0/0)")
	numberW, _ := canvas.MeasureString("10000")
	priceW, _ := canvas.MeasureString("10000")

	bolckW := int(10 + nameW + 50 + numberW + 50 + priceW + 10)
	backY := 10 + int(titleH*2+10)*2 + 10 + (len(stroeInfo)+len(discountList)/2+2)*int(textH*2) + 10
	canvas = gg.NewContext(bolckW, math.Max(backY, 500))
	// 画底色
	canvas.DrawRectangle(0, 0, float64(bolckW), float64(backY))
	canvas.SetRGBA255(150, 150, 150, 255)
	canvas.Fill()

	// 放字
	canvas.SetColor(color.Black)
	err = canvas.ParseFontFace(fontdata, 100)
	if err != nil {
		return nil, err
	}
	canvas.DrawString("今日波动", 10, 10+titleH*1.2)
	canvas.DrawLine(10, titleH*1.6, titleW, titleH*1.6)
	canvas.SetLineWidth(3)
	canvas.SetRGBA255(0, 0, 0, 255)
	canvas.Stroke()

	textDy := 10 + titleH*1.7
	if err = canvas.ParseFontFace(fontdata, 35); err != nil {
		return nil, err
	}
	textDx, textDh := canvas.MeasureString("下界合金竿(均价1000)")
	valueDx, _ := canvas.MeasureString("+100%")
	i := 0
	for _, name := range thingList {
		text := name + "(均价" + strconv.Itoa(priceList[name]) + ") "

		if i == 2 {
			i = 0
			textDy += textDh * 2
		}
		canvas.SetColor(color.Black)
		canvas.DrawStringAnchored(text, 20+(textDx+valueDx+10)*float64(i)+10, textDy+textDh/2, 0, 0.5)
		if discountList[name]-100 > 0 {
			canvas.SetRGBA255(200, 50, 50, 255)
			text = "+" + strconv.Itoa(discountList[name]-100) + "%"
		} else {
			canvas.SetRGBA255(63, 133, 55, 255)
			text = strconv.Itoa(discountList[name]-100) + "%"
		}
		canvas.DrawStringAnchored(text, 20+(textDx+valueDx+10)*float64(i)+10+textDx+10, textDy+textDh/2, 0, 0.5)
		i++
	}
	canvas.SetColor(color.Black)
	textDy += textDh * 2
	canvas.DrawStringAnchored("注:出售商品将会额外扣除20%的税收,附魔鱼竿请按实际价格", 10, textDy+10+textDh/2, 0, 0.5)

	textDy += textH * 2
	err = canvas.ParseFontFace(fontdata, 100)
	if err != nil {
		return nil, err
	}
	canvas.DrawString("上架内容", 10, textDy+titleH*1.2)
	canvas.DrawLine(10, textDy+titleH*1.6, titleW, textDy+titleH*1.6)
	canvas.SetLineWidth(3)
	canvas.SetRGBA255(0, 0, 0, 255)
	canvas.Stroke()

	textDy += 10 + titleH*1.7
	if err = canvas.ParseFontFace(fontdata, 50); err != nil {
		return nil, err
	}

	canvas.DrawStringAnchored("名称", 10+nameW/2, textDy+textH/2, 0.5, 0.5)
	canvas.DrawStringAnchored("数量", 10+nameW+10+numberW/2, textDy+textH/2, 0.5, 0.5)
	canvas.DrawStringAnchored("价格", 10+nameW+10+numberW+50+priceW/2, textDy+textH/2, 0.5, 0.5)

	for _, info := range stroeInfo {
		textDy += textH * 2
		name := info.Name
		if info.Other != "" && info.Name != "美西螈" {
			name += "(" + info.Other + ")"
		}
		numberStr := strconv.Itoa(info.Number)
		pice := 0
		if strings.Contains(name, "竿") {
			poleInfo := strings.Split(info.Other, "/")
			durable, _ := strconv.Atoi(poleInfo[0])
			maintenance, _ := strconv.Atoi(poleInfo[1])
			induceLevel, _ := strconv.Atoi(poleInfo[2])
			favorLevel, _ := strconv.Atoi(poleInfo[3])
			pice = (priceList[info.Name] - (durationList[info.Name] - durable) - maintenance*2 + induceLevel*600 + favorLevel*1800) * discountList[info.Name] / 100
		} else {
			pice = priceList[info.Name] * discountList[info.Name] / 100
		}

		canvas.DrawStringAnchored(name, 10+nameW/2, textDy+textH/2, 0.5, 0.5)
		canvas.DrawStringAnchored(numberStr, 10+nameW+10+numberW/2, textDy+textH/2, 0.5, 0.5)
		canvas.DrawStringAnchored(strconv.Itoa(pice), 10+nameW+10+numberW+50+priceW/2, textDy+textH/2, 0.5, 0.5)
	}
	return canvas.Image(), nil
}