feat: 优化喜报逻辑,支持图片

This commit is contained in:
lixiangwuxian 2025-08-08 17:54:01 +08:00
parent df86341677
commit 3299e0a3bf
7 changed files with 144 additions and 64 deletions

1
go.mod
View File

@ -15,7 +15,6 @@ require (
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/hertz-contrib/cors v0.1.0
github.com/jmoiron/sqlx v1.4.0
github.com/mattn/go-sqlite3 v1.14.23
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/sashabaranov/go-openai v1.36.1

9
go.sum
View File

@ -1,5 +1,3 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.lxtend.com/lixiangwuxian/imagedd v0.0.0-20250515173831-6585d9dd6665 h1:7BI2/KZmvmUEYHewAR4zgoWsl1dmzcGP9mqTqmQdyAg=
git.lxtend.com/lixiangwuxian/imagedd v0.0.0-20250515173831-6585d9dd6665/go.mod h1:+G/BR3iv5Yw0bIqZTRcBxpXwcv3bIso+XhN0MTfnjCY=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
@ -72,8 +70,6 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
@ -108,8 +104,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
@ -135,11 +129,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=

View File

@ -0,0 +1,17 @@
package auth
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
)
var tempTokens = make(map[string]string)
func AcquireTokenHandler(c context.Context, ctx *app.RequestContext) {
}
func LoginHandler(c context.Context, ctx *app.RequestContext) {
}

View File

@ -2,7 +2,7 @@ package xibao
import (
"fmt"
"regexp"
"strings"
"git.lxtend.com/lixiangwuxian/qqbot/constants"
"git.lxtend.com/lixiangwuxian/qqbot/handler"
@ -23,17 +23,19 @@ func init() {
func xiBao(msg model.Message) (reply *model.Reply) {
fileName := uuid.New().String()
filePath := util.GenTempFilePath(fmt.Sprintf("%s.png", fileName))
re := regexp.MustCompile(`\s+`)
tokens := re.Split(msg.RawMsg, 2)
if len(tokens) < 2 {
handler.RegisterLiveHandler(msg.GroupInfo.GroupId, msg.UserId, xiBaoTemp)
return &model.Reply{
ReplyMsg: "",
ReferOriginMsg: false,
FromMsg: msg,
if len(msg.StructuredMsg) == 1 {
if msg.StructuredMsg[0].GetMessageType() != qq_message.TypeText {
return nil
} else if msg.StructuredMsg[0].(*qq_message.TextMessage).Data.Text == "喜报" {
handler.RegisterLiveHandler(msg.GroupInfo.GroupId, msg.UserId, xiBaoTemp)
return nil
}
msg.StructuredMsg[0].(*qq_message.TextMessage).Data.Text = strings.TrimPrefix(msg.StructuredMsg[0].(*qq_message.TextMessage).Data.Text, "喜报 ")
xibao.GenerateCongratulationImageNew(msg.StructuredMsg[0], "./resource/xibao_background.png", filePath, true)
}
if len(msg.StructuredMsg) == 2 {
xibao.GenerateCongratulationImageNew(msg.StructuredMsg[1], "./resource/xibao_background.png", filePath, true)
}
xibao.GenerateCongratulationImageNew(tokens[1], "./resource/xibao_background.png", filePath, true)
imageMsg := qq_message.ImageMessage{
Type: "image",
Data: qq_message.ImageMessageData{
@ -41,7 +43,7 @@ func xiBao(msg model.Message) (reply *model.Reply) {
},
}
return &model.Reply{
ReplyMsg: imageMsg.ToCQString(),
ReplyMsg: imageMsg,
ReferOriginMsg: true,
FromMsg: msg,
}
@ -51,7 +53,13 @@ func xiBaoTemp(msg model.Message) (reply *model.Reply, isTrigger bool) {
handler.UnRegisterLiveHandler(msg.GroupInfo.GroupId, msg.UserId)
fileName := uuid.New().String()
filePath := util.GenTempFilePath(fmt.Sprintf("%s.png", fileName))
xibao.GenerateCongratulationImageNew(msg.RawMsg, "./resource/xibao_background.png", filePath, true)
var contentMsg qq_message.QQMessage
if len(msg.StructuredMsg) > 0 {
contentMsg = msg.StructuredMsg[0]
} else {
contentMsg = qq_message.NewTextMessage().ParseMessage(msg.RawMsg)
}
xibao.GenerateCongratulationImageNew(contentMsg, "./resource/xibao_background.png", filePath, true)
imageMsg := qq_message.ImageMessage{
Type: "image",
Data: qq_message.ImageMessageData{
@ -59,7 +67,7 @@ func xiBaoTemp(msg model.Message) (reply *model.Reply, isTrigger bool) {
},
}
return &model.Reply{
ReplyMsg: imageMsg.ToCQString(),
ReplyMsg: imageMsg,
ReferOriginMsg: true,
FromMsg: msg,
}, true
@ -68,17 +76,19 @@ func xiBaoTemp(msg model.Message) (reply *model.Reply, isTrigger bool) {
func beiBao(msg model.Message) (reply *model.Reply) {
fileName := uuid.New().String()
filePath := util.GenTempFilePath(fmt.Sprintf("%s.png", fileName))
re := regexp.MustCompile(`\s+`)
tokens := re.Split(msg.RawMsg, 2)
if len(tokens) < 2 {
handler.RegisterLiveHandler(msg.GroupInfo.GroupId, msg.UserId, beiBaoTemp)
return &model.Reply{
ReplyMsg: "",
ReferOriginMsg: false,
FromMsg: msg,
if len(msg.StructuredMsg) == 1 {
if msg.StructuredMsg[0].GetMessageType() != "text" {
return nil
} else if msg.StructuredMsg[0].(*qq_message.TextMessage).Data.Text == "悲报" {
handler.RegisterLiveHandler(msg.GroupInfo.GroupId, msg.UserId, beiBaoTemp)
return nil
}
msg.StructuredMsg[0].(*qq_message.TextMessage).Data.Text = strings.TrimPrefix(msg.StructuredMsg[0].(*qq_message.TextMessage).Data.Text, "悲报 ")
xibao.GenerateCongratulationImageNew(msg.StructuredMsg[0], "./resource/beibao_background.png", filePath, false)
}
if len(msg.StructuredMsg) == 2 {
xibao.GenerateCongratulationImageNew(msg.StructuredMsg[1], "./resource/beibao_background.png", filePath, false)
}
xibao.GenerateCongratulationImageNew(tokens[1], "./resource/beibao_background.png", filePath, false)
imageMsg := qq_message.ImageMessage{
Type: "image",
Data: qq_message.ImageMessageData{
@ -86,7 +96,7 @@ func beiBao(msg model.Message) (reply *model.Reply) {
},
}
return &model.Reply{
ReplyMsg: imageMsg.ToCQString(),
ReplyMsg: imageMsg,
ReferOriginMsg: true,
FromMsg: msg,
}
@ -96,7 +106,13 @@ func beiBaoTemp(msg model.Message) (reply *model.Reply, isTrigger bool) {
handler.UnRegisterLiveHandler(msg.GroupInfo.GroupId, msg.UserId)
fileName := uuid.New().String()
filePath := util.GenTempFilePath(fmt.Sprintf("%s.png", fileName))
xibao.GenerateCongratulationImageNew(msg.RawMsg, "./resource/beibao_background.png", filePath, false)
var contentMsg qq_message.QQMessage
if len(msg.StructuredMsg) > 0 {
contentMsg = msg.StructuredMsg[0]
} else {
contentMsg = qq_message.NewTextMessage().ParseMessage(msg.RawMsg)
}
xibao.GenerateCongratulationImageNew(contentMsg, "./resource/beibao_background.png", filePath, false)
imageMsg := qq_message.ImageMessage{
Type: "image",
Data: qq_message.ImageMessageData{
@ -104,7 +120,7 @@ func beiBaoTemp(msg model.Message) (reply *model.Reply, isTrigger bool) {
},
}
return &model.Reply{
ReplyMsg: imageMsg.ToCQString(),
ReplyMsg: imageMsg,
ReferOriginMsg: true,
FromMsg: msg,
}, true

View File

@ -157,7 +157,7 @@ func GenerateCongratulationImage(text string, inputFile, outputFile string, isGo
}
}
func GenerateCongratulationImageNew(text string, inputFile, outputFile string, isGood bool) {
func GenerateCongratulationImageNew(textOrImg qq_message.QQMessage, inputFile, outputFile string, isGood bool) {
baseboard := sprite.NewNamedSpriteBoard()
backgroundImageSprite, err := sprite.LoadSpriteFromFile("bg", inputFile)
backgroundImageSprite.Index = 0
@ -166,26 +166,49 @@ func GenerateCongratulationImageNew(text string, inputFile, outputFile string, i
return
}
baseboard.AddSprite(backgroundImageSprite)
var textColor color.RGBA
if isGood {
// 设置文本颜色为红色
textColor = color.RGBA{R: 255 * 0.8, G: 0, B: 0, A: 255}
} else {
textColor = color.RGBA{R: 0, G: 0, B: 255 * 0.8, A: 255}
if textOrImg.GetMessageType() == qq_message.TypeText {
text := textOrImg.(*qq_message.TextMessage).Data.Text
var textColor color.RGBA
if isGood {
// 设置文本颜色为红色
textColor = color.RGBA{R: 255 * 0.8, G: 0, B: 0, A: 255}
} else {
textColor = color.RGBA{R: 0, G: 0, B: 255 * 0.8, A: 255}
}
textImage, err := text2img.RenderTextToTrimmedImage(nil, text, 96, textColor, 0, 0)
if err != nil {
log.Print("无法渲染文本:", err)
return
}
textSprite := sprite.Sprite{
Name: "text",
Images: []image.Image{textImage},
Position: image.Point{X: backgroundImageSprite.GetCurrentImage().Bounds().Dx()/2 - textImage.Bounds().Dx()/2, Y: backgroundImageSprite.GetCurrentImage().Bounds().Dy()/2 - textImage.Bounds().Dy()/2},
Index: 1,
}
baseboard.AddSprite(&textSprite)
baseboard.SaveToApng(outputFile)
} else if textOrImg.GetMessageType() == qq_message.TypeImage {
imgMsg := textOrImg.(*qq_message.ImageMessage)
filePath, err := util.DownloadFile(imgMsg.Data.URL, "/tmp/qqbot", false)
if err != nil {
log.Print("无法下载图片:", err)
return
}
im, _, err := sprite.LoadImageFile(filePath)
if err != nil {
log.Print("无法加载图片:", err)
return
}
imgSprite := sprite.Sprite{
Name: "image",
Images: im,
Position: image.Point{X: backgroundImageSprite.GetCurrentImage().Bounds().Dx()/2 - im[0].Bounds().Dx()/2, Y: backgroundImageSprite.GetCurrentImage().Bounds().Dy()/2 - im[0].Bounds().Dy()/2},
Index: 1,
}
baseboard.AddSprite(&imgSprite)
baseboard.SaveToApng(outputFile)
}
textImage, err := text2img.RenderTextToTrimmedImage(nil, text, 96, textColor, 0, 0)
if err != nil {
log.Print("无法渲染文本:", err)
return
}
textSprite := sprite.Sprite{
Name: "text",
Images: []image.Image{textImage},
Position: image.Point{X: backgroundImageSprite.GetCurrentImage().Bounds().Dx()/2 - textImage.Bounds().Dx()/2, Y: backgroundImageSprite.GetCurrentImage().Bounds().Dy()/2 - textImage.Bounds().Dy()/2},
Index: 1,
}
baseboard.AddSprite(&textSprite)
baseboard.SaveToApng(outputFile)
}
func isImageCQ(text string) (string, bool) {

View File

@ -0,0 +1,13 @@
package middleware
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
)
func AuthMiddleware() app.HandlerFunc {
return func(c context.Context, ctx *app.RequestContext) {
ctx.Next(c)
}
}

View File

@ -3,10 +3,12 @@ package webserver
import (
"time"
"git.lxtend.com/lixiangwuxian/qqbot/handler/auth"
"git.lxtend.com/lixiangwuxian/qqbot/handler/restart"
"git.lxtend.com/lixiangwuxian/qqbot/handler/ticket"
"git.lxtend.com/lixiangwuxian/qqbot/health"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/route"
"github.com/hertz-contrib/cors"
)
@ -26,14 +28,33 @@ func StartRouter() {
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
healthEngine := herzServer.Group("/health")
healthEngine.GET("/ping", health.HealthHandler)
ticketEngine := herzServer.Group("/ticket")
ticketEngine.GET("", ticket.TicketHandler)
gitEngine := herzServer.Group("/git")
gitEngine.GET("/pull", restart.PullCodeHandler)
gitEngine.GET("/build", restart.BuildBotHandler)
gitEngine.GET("/restart", restart.RestartBotHandler)
gitEngine.GET("/all", restart.AllInOneHandler)
router(herzServer)
go herzServer.Run()
}
func router(s *server.Hertz) {
gitRouter(s.Group("/git"))
healthRouter(s.Group("/health"))
ticketRouter(s.Group("/ticket"))
authRouter(s.Group("/auth"))
}
func gitRouter(g *route.RouterGroup) {
g.GET("/pull", restart.PullCodeHandler)
g.GET("/build", restart.BuildBotHandler)
g.GET("/restart", restart.RestartBotHandler)
g.GET("/all", restart.AllInOneHandler)
}
func healthRouter(g *route.RouterGroup) {
g.GET("/ping", health.HealthHandler)
}
func ticketRouter(g *route.RouterGroup) {
g.GET("", ticket.TicketHandler)
}
func authRouter(g *route.RouterGroup) {
g.GET("/acquire_token", auth.AcquireTokenHandler)
g.GET("/login", auth.LoginHandler)
}