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

Beta38 #126

Merged
merged 4 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions handlers/message_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,17 @@ func parseMessageContent(paramsMessage callapi.ParamsContent) (string, map[strin
//mylog.Printf(messageText)
// 正则表达式部分
var localImagePattern *regexp.Regexp

var localRecordPattern *regexp.Regexp
if runtime.GOOS == "windows" {
localImagePattern = regexp.MustCompile(`\[CQ:image,file=file:///([^\]]+?)\]`)
} else {
localImagePattern = regexp.MustCompile(`\[CQ:image,file=file://([^\]]+?)\]`)
}

if runtime.GOOS == "windows" {
localRecordPattern = regexp.MustCompile(`\[CQ:record,file=file:///([^\]]+?)\]`)
} else {
localRecordPattern = regexp.MustCompile(`\[CQ:record,file=file://([^\]]+?)\]`)
}
urlImagePattern := regexp.MustCompile(`\[CQ:image,file=https?://(.+)\]`)
base64ImagePattern := regexp.MustCompile(`\[CQ:image,file=base64://(.+)\]`)
base64RecordPattern := regexp.MustCompile(`\[CQ:record,file=base64://(.+)\]`)
Expand All @@ -144,6 +148,7 @@ func parseMessageContent(paramsMessage callapi.ParamsContent) (string, map[strin
{"url_image", urlImagePattern},
{"base64_image", base64ImagePattern},
{"base64_record", base64RecordPattern},
{"local_record", localRecordPattern},
}

foundItems := make(map[string][]string)
Expand Down
56 changes: 56 additions & 0 deletions handlers/send_group_msg.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package handlers

import (
"bytes"
"context"
"encoding/base64"
"os"
Expand All @@ -13,6 +14,7 @@ import (
"github.com/hoshinonyaruko/gensokyo/idmap"
"github.com/hoshinonyaruko/gensokyo/images"
"github.com/hoshinonyaruko/gensokyo/mylog"
"github.com/hoshinonyaruko/gensokyo/silk"
"github.com/tencent-connect/botgo/dto"
"github.com/tencent-connect/botgo/openapi"
)
Expand Down Expand Up @@ -248,6 +250,47 @@ func generateGroupMessage(id string, foundItems map[string][]string, messageText
Content: "", // 这个字段文档没有了
SrvSendMsg: true,
}
} else if RecordURLs, ok := foundItems["local_record"]; ok && len(RecordURLs) > 0 {
// 从本地路径读取语音
RecordData, err := os.ReadFile(RecordURLs[0])
if err != nil {
// 读入文件失败
mylog.Printf("Error reading the record from path %s: %v", RecordURLs[0], err)
// 返回文本信息,提示语音文件不存在
return &dto.MessageToCreate{
Content: "错误: 语音文件不存在",
MsgID: id,
MsgSeq: msgseq,
MsgType: 0, // 默认文本类型
}
}
//判断并转码
if !silk.IsAMRorSILK(RecordData) {
mt, ok := silk.CheckAudio(bytes.NewReader(RecordData))
if !ok {
mylog.Errorf("voice type error: " + mt)
return nil
}
RecordData = silk.EncoderSilk(RecordData)
mylog.Errorf("音频转码ing")
if err != nil {
return nil
}
}
// 将解码的语音数据转换回base64格式并上传
imageURL, err := images.UploadBase64RecordToServer(base64.StdEncoding.EncodeToString(RecordData))
if err != nil {
mylog.Printf("failed to upload base64 record: %v", err)
return nil
}
// 创建RichMediaMessage并返回
return &dto.RichMediaMessage{
EventID: id,
FileType: 3, // 3代表语音
URL: imageURL,
Content: "", // 这个字段文档没有了
SrvSendMsg: true,
}
} else if imageURLs, ok := foundItems["url_image"]; ok && len(imageURLs) > 0 {
// 发链接图片
return &dto.RichMediaMessage{
Expand All @@ -266,6 +309,19 @@ func generateGroupMessage(id string, foundItems map[string][]string, messageText
mylog.Printf("failed to decode base64 record: %v", err)
return nil
}
//判断并转码
if !silk.IsAMRorSILK(fileRecordData) {
mt, ok := silk.CheckAudio(bytes.NewReader(fileRecordData))
if !ok {
mylog.Errorf("voice type error: " + mt)
return nil
}
fileRecordData = silk.EncoderSilk(fileRecordData)
mylog.Errorf("音频转码ing")
if err != nil {
return nil
}
}
// 将解码的语音数据转换回base64格式并上传
imageURL, err := images.UploadBase64RecordToServer(base64.StdEncoding.EncodeToString(fileRecordData))
if err != nil {
Expand Down
38 changes: 37 additions & 1 deletion silk/silk.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,32 @@
package silk

import (
"bytes"
"crypto/md5"
"encoding/hex"
"errors"
"io"
"net/http"
"os"
"os/exec"
"path"
"strings"

"github.com/hoshinonyaruko/gensokyo/mylog"
"github.com/wdvxdr1123/go-silk"
)

const (
// HeaderAmr AMR文件头
HeaderAmr = "#!AMR"
// HeaderSilk Silkv3文件头
HeaderSilk = "\x02#!SILK_V3"
)

const silkCachePath = "data/cache"

const limit = 4 * 1024

// EncoderSilk 将音频编码为Silk
func EncoderSilk(data []byte) []byte {
h := md5.New()
Expand All @@ -30,7 +43,6 @@ func EncoderSilk(data []byte) []byte {
}
tempName := hex.EncodeToString(h.Sum(nil))
slk := encode(data, tempName)

return slk
}

Expand Down Expand Up @@ -105,6 +117,30 @@ func encode(record []byte, tempName string) (silkWav []byte) {
return silkWav
}

// IsAMRorSILK 判断给定文件是否为Amr或Silk格式
func IsAMRorSILK(b []byte) bool {
return bytes.HasPrefix(b, []byte(HeaderAmr)) || bytes.HasPrefix(b, []byte(HeaderSilk))
}

// 扫描格式
func scan(r io.ReadSeeker) string {
_, _ = r.Seek(0, io.SeekStart)
defer r.Seek(0, io.SeekStart)
in := make([]byte, limit)
_, _ = r.Read(in)
return http.DetectContentType(in)
}

// CheckAudio 判断给定流是否为合法音频
func CheckAudio(r io.ReadSeeker) (string, bool) {
t := scan(r)
// std mime type detection is not full supported for audio
if strings.Contains(t, "text") || strings.Contains(t, "image") {
return t, false
}
return t, true
}

// // resample 将silk重新编码为 24000 bit rate
// func resample(data []byte) []byte {
// pcm, err := silk.DecodeSilkBuffToPcm(data, 24000)
Expand Down
42 changes: 19 additions & 23 deletions wsclient/ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,10 @@ func (c *WebSocketClient) handleIncomingMessages(ctx context.Context, cancel con
_, msg, err := c.conn.ReadMessage()
if err != nil {
mylog.Println("WebSocket connection closed:", err)
cancel() // 取消心跳 goroutine

if !c.isReconnecting {
go c.Reconnect()
}
// cancel() // 取消心跳 goroutine
// if !c.isReconnecting {
// go c.Reconnect()
// }
return // 退出循环,不再尝试读取消息
}

Expand Down Expand Up @@ -93,25 +92,22 @@ func (client *WebSocketClient) Reconnect() {
client.mutex.Unlock()
}()

for {
time.Sleep(5 * time.Second)
newClient, err := NewWebSocketClient(client.urlStr, client.botID, client.api, client.apiv2, 30)
if err == nil && newClient != nil {
client.mutex.Lock() // 在替换连接之前锁定
oldCancel := client.cancel // 保存旧的取消函数
client.conn = newClient.conn
client.api = newClient.api
client.apiv2 = newClient.apiv2
oldCancel() // 停止所有相关的旧协程
client.cancel = newClient.cancel // 更新取消函数
client.mutex.Unlock()
// 重发失败的消息
go newClient.processFailedMessages(oldSendFailures)
mylog.Println("Successfully reconnected to WebSocket.")
return
}
mylog.Println("Failed to reconnect to WebSocket. Retrying in 5 seconds...")
newClient, err := NewWebSocketClient(client.urlStr, client.botID, client.api, client.apiv2, 30)
if err == nil && newClient != nil {
client.mutex.Lock() // 在替换连接之前锁定
oldCancel := client.cancel // 保存旧的取消函数
client.conn = newClient.conn
client.api = newClient.api
client.apiv2 = newClient.apiv2
oldCancel() // 停止所有相关的旧协程
client.cancel = newClient.cancel // 更新取消函数
client.mutex.Unlock()
// 重发失败的消息
go newClient.processFailedMessages(oldSendFailures)
mylog.Println("Successfully reconnected to WebSocket.")
return
}

}

// 处理发送失败的消息
Expand Down