feat: 重构消息处理模块,引入统一的消息接口和类型安全的消息解析
This commit is contained in:
parent
e0637ab81f
commit
13ea5d7f98
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.lxtend.com/qqbot/message"
|
||||
"git.lxtend.com/qqbot/model"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
@ -31,7 +32,13 @@ func (am *actionManager) SendAction(action string) error {
|
||||
|
||||
func (am *actionManager) SendMsg(reply model.Reply) error {
|
||||
if reply.ReferOriginMsg {
|
||||
reply.ReplyMsg = fmt.Sprintf("%s%s", GenReplyCQ(reply.FromMsg.OriginMsgId, "", 0, 0, 0), reply.ReplyMsg)
|
||||
replyMsg := message.ReplyMessage{
|
||||
Type: "reply",
|
||||
Data: message.ReplyData{
|
||||
ID: int(reply.FromMsg.OriginMsgId),
|
||||
},
|
||||
}
|
||||
reply.ReplyMsg = fmt.Sprintf("%s%s", replyMsg.ToCQString(), reply.ReplyMsg)
|
||||
}
|
||||
sendPkg := model.GenSendPkg(reply.FromMsg.UserId, reply.FromMsg.GroupInfo.GroupId, reply.ReplyMsg, false)
|
||||
sendPkgJson, err := json.Marshal(sendPkg)
|
||||
|
1
go.mod
1
go.mod
@ -13,6 +13,7 @@ require (
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
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
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
|
||||
golang.org/x/image v0.21.0
|
||||
|
2
go.sum
2
go.sum
@ -123,6 +123,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"git.lxtend.com/qqbot/auth"
|
||||
"git.lxtend.com/qqbot/constants"
|
||||
"git.lxtend.com/qqbot/handler"
|
||||
"git.lxtend.com/qqbot/message"
|
||||
"git.lxtend.com/qqbot/model"
|
||||
)
|
||||
|
||||
@ -36,8 +37,9 @@ func setUserLevel(msg model.Message) (reply model.Reply) {
|
||||
}
|
||||
}
|
||||
userText := tokens[1]
|
||||
if userId, err := model.ParseAtMessage(userText); err == nil {
|
||||
userText = userId.Data.QQ
|
||||
atMsg := message.AtMessage{}
|
||||
if err := atMsg.ParseMessage(userText); err == nil {
|
||||
userText = atMsg.Data.QQ
|
||||
}
|
||||
log.Println(userText)
|
||||
user, err := strconv.Atoi(userText)
|
||||
|
@ -2,12 +2,16 @@ package beatleader
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"git.lxtend.com/qqbot/constants"
|
||||
"git.lxtend.com/qqbot/handler"
|
||||
"git.lxtend.com/qqbot/message"
|
||||
"git.lxtend.com/qqbot/model"
|
||||
"git.lxtend.com/qqbot/service/beatleader"
|
||||
"git.lxtend.com/qqbot/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -135,9 +139,31 @@ func getMyRecentScore(msg model.Message) (reply model.Reply) {
|
||||
FromMsg: msg,
|
||||
}
|
||||
}
|
||||
for _, v := range records {
|
||||
scoreMsg += v.ToString() + "\n\n"
|
||||
userName = v.Name
|
||||
// 单独线程下载封面图片
|
||||
coverImageMap := make(map[string]string)
|
||||
wg := sync.WaitGroup{}
|
||||
for _, record := range records {
|
||||
coverImageMap[record.SongHash] = record.CoverImage
|
||||
wg.Add(1)
|
||||
go func(songHash string) {
|
||||
defer wg.Done()
|
||||
//文件存在则跳过
|
||||
if _, err := os.Stat(util.GetResizedIamgePathByOrgPath(util.GenTempFilePath(songHash + ".png"))); err == nil {
|
||||
return
|
||||
}
|
||||
util.DownloadFile(coverImageMap[songHash], util.GenTempFilePath(songHash+".png"))
|
||||
newPath, err := util.ResizeImageByMaxHeight(util.GenTempFilePath(songHash+".png"), 20)
|
||||
os.Remove(util.GenTempFilePath(songHash + ".png"))
|
||||
if err != nil {
|
||||
log.Printf("缩放图片失败: %v", err)
|
||||
}
|
||||
coverImageMap[songHash] = newPath
|
||||
}(record.SongHash)
|
||||
}
|
||||
wg.Wait()
|
||||
for _, record := range records {
|
||||
scoreMsg += record.ToString() + "\n\n"
|
||||
userName = record.Name
|
||||
recordCount++
|
||||
}
|
||||
if len(scoreMsg) > 0 {
|
||||
@ -157,8 +183,14 @@ func getMyRecentScore(msg model.Message) (reply model.Reply) {
|
||||
}
|
||||
|
||||
func screenShotBL(msg model.Message) (reply model.Reply) {
|
||||
imageMsg := message.ImageMessage{
|
||||
Type: "image",
|
||||
Data: message.ImageMessageData{
|
||||
File: "file:///tmp/qqbot/" + beatleader.GetBLPicture(strconv.Itoa(int(msg.UserId))),
|
||||
},
|
||||
}
|
||||
return model.Reply{
|
||||
ReplyMsg: "[CQ:image,file=file:///tmp/qqbot/" + beatleader.GetBLPicture(strconv.Itoa(int(msg.UserId))) + "]",
|
||||
ReplyMsg: imageMsg.ToCQString(),
|
||||
ReferOriginMsg: true,
|
||||
FromMsg: msg,
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
package drawback
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"git.lxtend.com/qqbot/action"
|
||||
"git.lxtend.com/qqbot/constants"
|
||||
"git.lxtend.com/qqbot/handler"
|
||||
"git.lxtend.com/qqbot/message"
|
||||
"git.lxtend.com/qqbot/model"
|
||||
)
|
||||
|
||||
@ -15,9 +14,8 @@ func init() {
|
||||
}
|
||||
|
||||
func drawback(msg model.Message) model.Reply {
|
||||
msgIdToDrawback, err := model.ParseReplyData(msg.RawMsg)
|
||||
log.Printf("Drawback message %d", msgIdToDrawback.Data.ID)
|
||||
if err != nil {
|
||||
msgIdToDrawback := message.ReplyMessage{}
|
||||
if err := msgIdToDrawback.ParseMessage(msg.RawMsg); err != nil {
|
||||
return model.Reply{
|
||||
ReplyMsg: "",
|
||||
ReferOriginMsg: false,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package getweb
|
||||
|
||||
import (
|
||||
"git.lxtend.com/qqbot/message"
|
||||
"git.lxtend.com/qqbot/model"
|
||||
"git.lxtend.com/qqbot/util"
|
||||
)
|
||||
@ -24,8 +25,14 @@ func getweb(msg model.Message) (reply model.Reply) {
|
||||
FromMsg: msg,
|
||||
}
|
||||
}
|
||||
imageMsg := message.ImageMessage{
|
||||
Type: "image",
|
||||
Data: message.ImageMessageData{
|
||||
File: "file:///tmp/qqbot/getweb/url.png",
|
||||
},
|
||||
}
|
||||
return model.Reply{
|
||||
ReplyMsg: "[CQ:image,file=file:///tmp/qqbot/getweb/url.png]",
|
||||
ReplyMsg: imageMsg.ToCQString(),
|
||||
ReferOriginMsg: true,
|
||||
FromMsg: msg,
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"git.lxtend.com/qqbot/constants"
|
||||
"git.lxtend.com/qqbot/handler"
|
||||
"git.lxtend.com/qqbot/message"
|
||||
"git.lxtend.com/qqbot/model"
|
||||
"git.lxtend.com/qqbot/service/xibao"
|
||||
"git.lxtend.com/qqbot/util"
|
||||
@ -33,8 +34,14 @@ func xiBao(msg model.Message) (reply model.Reply) {
|
||||
}
|
||||
}
|
||||
xibao.GenerateCongratulationImage(tokens[1], "./resource/xibao_background.png", filePath, true)
|
||||
imageMsg := message.ImageMessage{
|
||||
Type: "image",
|
||||
Data: message.ImageMessageData{
|
||||
File: "file:///tmp/qqbot/" + fileName + ".png",
|
||||
},
|
||||
}
|
||||
return model.Reply{
|
||||
ReplyMsg: fmt.Sprintf("[CQ:image,file=file:///tmp/qqbot/%s]", fileName+".png"),
|
||||
ReplyMsg: imageMsg.ToCQString(),
|
||||
ReferOriginMsg: true,
|
||||
FromMsg: msg,
|
||||
}
|
||||
@ -45,8 +52,14 @@ func xiBaoTemp(msg model.Message) (reply model.Reply, isTrigger bool) {
|
||||
fileName := uuid.New().String()
|
||||
filePath := util.GenTempFilePath(fmt.Sprintf("%s.png", fileName))
|
||||
xibao.GenerateCongratulationImage(msg.RawMsg, "./resource/xibao_background.png", filePath, true)
|
||||
imageMsg := message.ImageMessage{
|
||||
Type: "image",
|
||||
Data: message.ImageMessageData{
|
||||
File: "file:///tmp/qqbot/" + fileName + ".png",
|
||||
},
|
||||
}
|
||||
return model.Reply{
|
||||
ReplyMsg: fmt.Sprintf("[CQ:image,file=file:///tmp/qqbot/%s]", fileName+".png"),
|
||||
ReplyMsg: imageMsg.ToCQString(),
|
||||
ReferOriginMsg: true,
|
||||
FromMsg: msg,
|
||||
}, true
|
||||
@ -66,8 +79,14 @@ func beiBao(msg model.Message) (reply model.Reply) {
|
||||
}
|
||||
}
|
||||
xibao.GenerateCongratulationImage(tokens[1], "./resource/beibao_background.png", filePath, false)
|
||||
imageMsg := message.ImageMessage{
|
||||
Type: "image",
|
||||
Data: message.ImageMessageData{
|
||||
File: "file:///tmp/qqbot/" + fileName + ".png",
|
||||
},
|
||||
}
|
||||
return model.Reply{
|
||||
ReplyMsg: fmt.Sprintf("[CQ:image,file=file:///tmp/qqbot/%s]", fileName+".png"),
|
||||
ReplyMsg: imageMsg.ToCQString(),
|
||||
ReferOriginMsg: true,
|
||||
FromMsg: msg,
|
||||
}
|
||||
@ -78,8 +97,14 @@ func beiBaoTemp(msg model.Message) (reply model.Reply, isTrigger bool) {
|
||||
fileName := uuid.New().String()
|
||||
filePath := util.GenTempFilePath(fmt.Sprintf("%s.png", fileName))
|
||||
xibao.GenerateCongratulationImage(msg.RawMsg, "./resource/beibao_background.png", filePath, false)
|
||||
imageMsg := message.ImageMessage{
|
||||
Type: "image",
|
||||
Data: message.ImageMessageData{
|
||||
File: "file:///tmp/qqbot/" + fileName + ".png",
|
||||
},
|
||||
}
|
||||
return model.Reply{
|
||||
ReplyMsg: fmt.Sprintf("[CQ:image,file=file:///tmp/qqbot/%s]", fileName+".png"),
|
||||
ReplyMsg: imageMsg.ToCQString(),
|
||||
ReferOriginMsg: true,
|
||||
FromMsg: msg,
|
||||
}, true
|
||||
|
36
message/at.go
Normal file
36
message/at.go
Normal file
@ -0,0 +1,36 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// @某个QQ用户的结构体
|
||||
type AtMessage struct {
|
||||
Type string `json:"type"`
|
||||
Data AtData `json:"data"`
|
||||
}
|
||||
|
||||
// @消息数据结构体
|
||||
type AtData struct {
|
||||
QQ string `json:"qq"`
|
||||
}
|
||||
|
||||
func (msg *AtMessage) ToCQString() string {
|
||||
return fmt.Sprintf(`\[CQ:at,qq=%s\]`, msg.Data.QQ)
|
||||
}
|
||||
|
||||
func (msg *AtMessage) ParseMessage(data string) error {
|
||||
// 使用正则表达式提取QQ号
|
||||
re := regexp.MustCompile(`\[CQ:at,qq=(\d+)\]`)
|
||||
matches := re.FindStringSubmatch(data)
|
||||
if len(matches) < 2 {
|
||||
return fmt.Errorf("数据格式不正确")
|
||||
}
|
||||
|
||||
// 返回解析后的结构体
|
||||
msg.Data = AtData{
|
||||
QQ: matches[1],
|
||||
}
|
||||
return nil
|
||||
}
|
6
message/cq_message.go
Normal file
6
message/cq_message.go
Normal file
@ -0,0 +1,6 @@
|
||||
package message
|
||||
|
||||
type CQMessage interface {
|
||||
ToCQString() string
|
||||
ParseMessage(data string) error
|
||||
}
|
69
message/image.go
Normal file
69
message/image.go
Normal file
@ -0,0 +1,69 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ImageMessage struct {
|
||||
Type string `json:"type"`
|
||||
Data ImageMessageData `json:"data"`
|
||||
}
|
||||
|
||||
type ImageMessageData struct {
|
||||
File string
|
||||
SubType string
|
||||
FileID string
|
||||
URL string
|
||||
FileSize int
|
||||
FileUnique string
|
||||
}
|
||||
|
||||
func (msg *ImageMessage) ToCQString() string {
|
||||
// URL 转义
|
||||
escapedURL := url.QueryEscape(msg.Data.URL)
|
||||
|
||||
// 构造 CQ:image 字符串
|
||||
cqString := fmt.Sprintf("[CQ:image,file=%s,sub_type=%s,file_id=%s,url=%s,file_size=%d,file_unique=%s]",
|
||||
msg.Data.File, msg.Data.SubType, msg.Data.FileID, escapedURL, msg.Data.FileSize, msg.Data.FileUnique)
|
||||
|
||||
return cqString
|
||||
}
|
||||
|
||||
func (msg *ImageMessage) ParseMessage(data string) error {
|
||||
// 使用正则表达式提取各个字段
|
||||
re := regexp.MustCompile(`\[CQ:image,file=(.*?),sub_type=(.*?),file_id=(.*?),url=(.*?),file_size=(\d+),file_unique=(.*?)\]`)
|
||||
matches := re.FindStringSubmatch(data)
|
||||
if len(matches) < 7 {
|
||||
return fmt.Errorf("数据格式不正确")
|
||||
}
|
||||
|
||||
// 转换file_size为整数
|
||||
fileSize, err := strconv.Atoi(matches[5])
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析 file_size 出错: %v", err)
|
||||
}
|
||||
|
||||
// 处理URL转义
|
||||
decodedURL, err := url.QueryUnescape(matches[4])
|
||||
|
||||
decodedURL = strings.ReplaceAll(decodedURL, ",", ",")
|
||||
decodedURL = strings.ReplaceAll(decodedURL, "[", "[")
|
||||
decodedURL = strings.ReplaceAll(decodedURL, "]", "]")
|
||||
decodedURL = strings.ReplaceAll(decodedURL, "&", "&")
|
||||
if err != nil {
|
||||
return fmt.Errorf("URL 转义失败: %v", err)
|
||||
}
|
||||
msg.Data = ImageMessageData{
|
||||
File: matches[1],
|
||||
SubType: matches[2],
|
||||
FileID: matches[3],
|
||||
URL: decodedURL,
|
||||
FileSize: fileSize,
|
||||
FileUnique: matches[6],
|
||||
}
|
||||
return nil
|
||||
}
|
39
message/reply.go
Normal file
39
message/reply.go
Normal file
@ -0,0 +1,39 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// 回复消息结构体
|
||||
type ReplyMessage struct {
|
||||
Type string `json:"type"`
|
||||
Data ReplyData `json:"data"`
|
||||
}
|
||||
|
||||
// 回复消息数据结构体
|
||||
type ReplyData struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
func (msg *ReplyMessage) ToCQString() string {
|
||||
return fmt.Sprintf(`\[CQ:reply,id=%d\]`, msg.Data.ID)
|
||||
}
|
||||
|
||||
func (msg *ReplyMessage) ParseMessage(data string) error {
|
||||
// 使用正则表达式提取ID
|
||||
re := regexp.MustCompile(`\[CQ:reply,id=(\d+)\]`)
|
||||
matches := re.FindStringSubmatch(data)
|
||||
if len(matches) < 2 {
|
||||
return fmt.Errorf("数据格式不正确")
|
||||
}
|
||||
|
||||
id, _ := strconv.Atoi(matches[1])
|
||||
|
||||
// 返回解析后的结构体
|
||||
msg.Data = ReplyData{
|
||||
ID: id,
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// @某个QQ用户的结构体
|
||||
type AtMessage struct {
|
||||
Type string `json:"type"`
|
||||
Data AtData `json:"data"`
|
||||
}
|
||||
|
||||
// @消息数据结构体
|
||||
type AtData struct {
|
||||
QQ string `json:"qq"`
|
||||
}
|
||||
|
||||
func (msg *AtMessage) ToCQString() string {
|
||||
return fmt.Sprintf(`\[CQ:at,qq=%s\]`, msg.Data.QQ)
|
||||
}
|
||||
|
||||
func ParseAtMessage(data string) (*AtMessage, error) {
|
||||
// 使用正则表达式提取QQ号
|
||||
re := regexp.MustCompile(`\[CQ:at,qq=(\d+)\]`)
|
||||
matches := re.FindStringSubmatch(data)
|
||||
if len(matches) < 2 {
|
||||
return nil, fmt.Errorf("数据格式不正确")
|
||||
}
|
||||
|
||||
// 返回解析后的结构体
|
||||
return &AtMessage{
|
||||
Type: "at",
|
||||
Data: AtData{
|
||||
QQ: matches[1],
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 回复消息结构体
|
||||
type ReplyMessage struct {
|
||||
Type string `json:"type"`
|
||||
Data ReplyData `json:"data"`
|
||||
}
|
||||
|
||||
// 回复消息数据结构体
|
||||
type ReplyData struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
func ParseReplyData(data string) (*ReplyMessage, error) {
|
||||
// 使用正则表达式提取ID
|
||||
re := regexp.MustCompile(`\[CQ:reply,id=(\d+)\]`)
|
||||
matches := re.FindStringSubmatch(data)
|
||||
if len(matches) < 2 {
|
||||
return nil, fmt.Errorf("数据格式不正确")
|
||||
}
|
||||
|
||||
id, _ := strconv.Atoi(matches[1])
|
||||
|
||||
// 返回解析后的结构体
|
||||
return &ReplyMessage{
|
||||
Type: "reply",
|
||||
Data: ReplyData{
|
||||
ID: id,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type ImageMessage struct {
|
||||
Type string `json:"type"`
|
||||
Data ImageMessageData `json:"data"`
|
||||
}
|
||||
|
||||
type ImageMessageData struct {
|
||||
File string
|
||||
SubType string
|
||||
FileID string
|
||||
URL string
|
||||
FileSize int
|
||||
FileUnique string
|
||||
}
|
||||
|
||||
func (msg *ImageMessage) ToCQString() (string, error) {
|
||||
// URL 转义
|
||||
escapedURL := url.QueryEscape(msg.Data.URL)
|
||||
|
||||
// 构造 CQ:image 字符串
|
||||
cqString := fmt.Sprintf("[CQ:image,file=%s,sub_type=%s,file_id=%s,url=%s,file_size=%d,file_unique=%s]",
|
||||
msg.Data.File, msg.Data.SubType, msg.Data.FileID, escapedURL, msg.Data.FileSize, msg.Data.FileUnique)
|
||||
|
||||
return cqString, nil
|
||||
}
|
||||
|
||||
func ParseCQImageMessage(data string) (*ImageMessage, error) {
|
||||
// 使用正则表达式提取各个字段
|
||||
re := regexp.MustCompile(`\[CQ:image,file=(.*?),sub_type=(.*?),file_id=(.*?),url=(.*?),file_size=(\d+),file_unique=(.*?)\]`)
|
||||
matches := re.FindStringSubmatch(data)
|
||||
if len(matches) < 7 {
|
||||
return nil, fmt.Errorf("数据格式不正确")
|
||||
}
|
||||
|
||||
// 转换file_size为整数
|
||||
fileSize, err := strconv.Atoi(matches[5])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析 file_size 出错: %v", err)
|
||||
}
|
||||
|
||||
// 处理URL转义
|
||||
decodedURL, err := url.QueryUnescape(matches[4])
|
||||
|
||||
decodedURL = strings.ReplaceAll(decodedURL, ",", ",")
|
||||
decodedURL = strings.ReplaceAll(decodedURL, "[", "[")
|
||||
decodedURL = strings.ReplaceAll(decodedURL, "]", "]")
|
||||
decodedURL = strings.ReplaceAll(decodedURL, "&", "&")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("URL 转义失败: %v", err)
|
||||
}
|
||||
|
||||
// 返回解析后的结构体
|
||||
// return &ImageMessageData{
|
||||
// File: matches[1],
|
||||
// SubType: matches[2],
|
||||
// FileID: matches[3],
|
||||
// URL: decodedURL,
|
||||
// FileSize: fileSize,
|
||||
// FileUnique: matches[6],
|
||||
// }, nil
|
||||
return &ImageMessage{
|
||||
Type: "image",
|
||||
Data: ImageMessageData{
|
||||
File: matches[1],
|
||||
SubType: matches[2],
|
||||
FileID: matches[3],
|
||||
URL: decodedURL,
|
||||
FileSize: fileSize,
|
||||
FileUnique: matches[6],
|
||||
},
|
||||
}, nil
|
||||
}
|
@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.lxtend.com/qqbot/model"
|
||||
"git.lxtend.com/qqbot/message"
|
||||
"git.lxtend.com/qqbot/util"
|
||||
"github.com/fogleman/gg"
|
||||
"github.com/google/uuid"
|
||||
@ -157,8 +157,9 @@ func GenerateCongratulationImage(text string, inputFile, outputFile string, isGo
|
||||
}
|
||||
|
||||
func isImageCQ(text string) (string, bool) {
|
||||
if img, err := model.ParseCQImageMessage(text); err == nil {
|
||||
return img.Data.URL, true
|
||||
imgMsg := message.ImageMessage{}
|
||||
if err := imgMsg.ParseMessage(text); err == nil {
|
||||
return imgMsg.Data.URL, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
166
util/picture.go
Normal file
166
util/picture.go
Normal file
@ -0,0 +1,166 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
)
|
||||
|
||||
func ResizeImage(imagePath string, width int, height int) (outputPath string, err error) {
|
||||
// 打开源图片文件
|
||||
file, err := os.Open(imagePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 解码图片
|
||||
var img image.Image
|
||||
var decodeErr error
|
||||
|
||||
ext := strings.ToLower(filepath.Ext(imagePath))
|
||||
switch ext {
|
||||
case ".jpg", ".jpeg":
|
||||
img, decodeErr = jpeg.Decode(file)
|
||||
case ".png":
|
||||
img, decodeErr = png.Decode(file)
|
||||
default:
|
||||
return "", errors.New("unsupported image format")
|
||||
}
|
||||
|
||||
if decodeErr != nil {
|
||||
return "", decodeErr
|
||||
}
|
||||
|
||||
// 调整图片大小
|
||||
resized := resize.Resize(uint(width), uint(height), img, resize.Lanczos3)
|
||||
|
||||
// 创建输出文件
|
||||
outputPath = strings.TrimSuffix(imagePath, ext) + "_resized" + ext
|
||||
out, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// 根据文件扩展名选择编码方式
|
||||
switch ext {
|
||||
case ".jpg", ".jpeg":
|
||||
return outputPath, jpeg.Encode(out, resized, nil)
|
||||
case ".png":
|
||||
return outputPath, png.Encode(out, resized)
|
||||
default:
|
||||
return "", errors.New("unsupported image format")
|
||||
}
|
||||
}
|
||||
|
||||
// ResizeImageByMaxWidth 按最大宽度缩放图片,保持宽高比
|
||||
func ResizeImageByMaxWidth(imagePath string, maxWidth uint) (outputPath string, err error) {
|
||||
// 打开源图片文件
|
||||
file, err := os.Open(imagePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 解码图片
|
||||
var img image.Image
|
||||
var decodeErr error
|
||||
|
||||
ext := strings.ToLower(filepath.Ext(imagePath))
|
||||
switch ext {
|
||||
case ".jpg", ".jpeg":
|
||||
img, decodeErr = jpeg.Decode(file)
|
||||
case ".png":
|
||||
img, decodeErr = png.Decode(file)
|
||||
default:
|
||||
return "", errors.New("unsupported image format")
|
||||
}
|
||||
|
||||
if decodeErr != nil {
|
||||
return "", decodeErr
|
||||
}
|
||||
|
||||
// 计算缩放后的尺寸,保持宽高比
|
||||
// 传入0作为高度,resize包会自动计算等比例的高度
|
||||
resized := resize.Resize(maxWidth, 0, img, resize.Lanczos3)
|
||||
|
||||
// 创建输出文件
|
||||
outputPath = strings.TrimSuffix(imagePath, ext) + "_resized" + ext
|
||||
out, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// 根据文件扩展名选择编码方式
|
||||
switch ext {
|
||||
case ".jpg", ".jpeg":
|
||||
return outputPath, jpeg.Encode(out, resized, nil)
|
||||
case ".png":
|
||||
return outputPath, png.Encode(out, resized)
|
||||
default:
|
||||
return "", errors.New("unsupported image format")
|
||||
}
|
||||
}
|
||||
|
||||
// ResizeImageByMaxHeight 按最大高度缩放图片,保持宽高比
|
||||
func ResizeImageByMaxHeight(imagePath string, maxHeight uint) (outputPath string, err error) {
|
||||
// 打开源图片文件
|
||||
file, err := os.Open(imagePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 解码图片
|
||||
var img image.Image
|
||||
var decodeErr error
|
||||
|
||||
ext := strings.ToLower(filepath.Ext(imagePath))
|
||||
switch ext {
|
||||
case ".jpg", ".jpeg":
|
||||
img, decodeErr = jpeg.Decode(file)
|
||||
case ".png":
|
||||
img, decodeErr = png.Decode(file)
|
||||
default:
|
||||
return "", errors.New("unsupported image format")
|
||||
}
|
||||
|
||||
if decodeErr != nil {
|
||||
return "", decodeErr
|
||||
}
|
||||
|
||||
// 计算缩放后的尺寸,保持宽高比
|
||||
// 传入0作为宽度,resize包会自动计算等比例的宽度
|
||||
resized := resize.Resize(0, maxHeight, img, resize.Lanczos3)
|
||||
|
||||
// 创建输出文件
|
||||
outputPath = strings.TrimSuffix(imagePath, ext) + "_resized" + ext
|
||||
out, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// 根据文件扩展名选择编码方式
|
||||
switch ext {
|
||||
case ".jpg", ".jpeg":
|
||||
return outputPath, jpeg.Encode(out, resized, nil)
|
||||
case ".png":
|
||||
return outputPath, png.Encode(out, resized)
|
||||
default:
|
||||
return "", errors.New("unsupported image format")
|
||||
}
|
||||
}
|
||||
|
||||
func GetResizedIamgePathByOrgPath(orgPath string) string {
|
||||
ext := strings.ToLower(filepath.Ext(orgPath))
|
||||
return strings.TrimSuffix(orgPath, ext) + "_resized" + ext
|
||||
}
|
@ -36,11 +36,10 @@ func normalizeURL(rawURL string) string {
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func DownloadFile(url string, filepath string) error {
|
||||
func DownloadFile(url string, filepath string) (err error) {
|
||||
// 发送 HTTP GET 请求
|
||||
// resp, err := http.Get(url)
|
||||
var resp *http.Response
|
||||
var err error
|
||||
var maxRetry = 100
|
||||
var retry = 0
|
||||
for resp, err = http.Get(url); err != nil && retry < maxRetry; resp, err = http.Get(url) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user