diff --git a/coolq/bot.go b/coolq/bot.go index 228faf3..5ff9b36 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -14,8 +14,6 @@ import ( "sync" "time" - "github.com/gabriel-vasile/mimetype" - "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" @@ -70,29 +68,6 @@ func (e *Event) JSONString() string { return utils.B2S(e.buffer.Bytes()) } -// keep sync with /docs/file.md#MINE -var lawfulImageTypes = [...]string{ - "image/bmp", - "image/gif", - "image/jpeg", - "image/png", - "image/webp", -} - -var lawfulAudioTypes = [...]string{ - "audio/aac", - "audio/aiff", - "audio/amr", - "audio/ape", - "audio/flac", - "audio/midi", - "audio/mp4", - "audio/mpeg", - "audio/ogg", - "audio/wav", - "audio/x-m4a", -} - // NewQQBot 初始化一个QQBot实例 func NewQQBot(cli *client.QQClient, conf *config.Config) *CQBot { bot := &CQBot{ @@ -203,7 +178,7 @@ func (bot *CQBot) UploadLocalImageAsGroup(groupCode int64, img *LocalImageElemen defer func() { _ = f.Close() }() img.Stream = f } - if lawful, mime := IsLawfulImage(img.Stream); !lawful { + if lawful, mime := base.IsLawfulImage(img.Stream); !lawful { return nil, errors.New("image type error: " + mime) } i, err = bot.Client.UploadGroupImage(groupCode, img.Stream) @@ -238,7 +213,7 @@ func (bot *CQBot) UploadLocalImageAsPrivate(userID int64, img *LocalImageElement defer func() { _ = f.Close() }() img.Stream = f } - if lawful, mime := IsLawfulImage(img.Stream); !lawful { + if lawful, mime := base.IsLawfulImage(img.Stream); !lawful { return nil, errors.New("image type error: " + mime) } i, err = bot.Client.UploadPrivateImage(userID, img.Stream) @@ -609,25 +584,3 @@ func (bot *CQBot) uploadMedia(raw message.IMessageElement, target int64, group b } return nil, errors.New("unsupported message element type") } - -// IsLawfulImage 判断给定流是否为合法图片 -// 返回 是否合法, 实际Mime -// 判断后会自动将 Stream Seek 至 0 -func IsLawfulImage(r io.ReadSeeker) (bool, string) { - if base.SkipMimeScan { - return true, "" - } - _, _ = r.Seek(0, io.SeekStart) - defer func() { _, _ = r.Seek(0, io.SeekStart) }() - t, err := mimetype.DetectReader(r) - if err != nil { - log.Debugf("扫描 Mime 时出现问题: %v", err) - return false, "" - } - for _, lt := range lawfulImageTypes { - if t.Is(lt) { - return true, t.String() - } - } - return false, t.String() -} diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 55296a7..e0a6975 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -20,12 +20,10 @@ import ( "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" - "github.com/gabriel-vasile/mimetype" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/global/codec" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/param" ) @@ -715,7 +713,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte if err != nil { return nil, err } - return &message.VoiceElement{Data: codec.RecodeTo24K(data)}, nil + return &message.VoiceElement{Data: base.ResampleSilk(data)}, nil case "record": f := d["file"] data, err := global.FindFile(f, d["cache"], global.VoicePath) @@ -725,17 +723,10 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte if err != nil { return nil, err } - if !base.SkipMimeScan && !global.IsAMRorSILK(data) { - mt := mimetype.Detect(data) - lawful := false - for _, lt := range lawfulAudioTypes { - if mt.Is(lt) { - lawful = true - break - } - } + if !global.IsAMRorSILK(data) { + lawful, mt := base.IsLawfulAudio(bytes.NewReader(data)) if !lawful { - return nil, errors.New("audio type error: " + mt.String()) + return nil, errors.New("audio type error: " + mt) } } if !global.IsAMRorSILK(data) { diff --git a/global/codec.go b/global/codec.go index 9ff78cb..223c533 100644 --- a/global/codec.go +++ b/global/codec.go @@ -7,9 +7,9 @@ import ( "os/exec" "path" - "github.com/Mrs4s/go-cqhttp/global/codec" - "github.com/pkg/errors" + + "github.com/Mrs4s/go-cqhttp/internal/base" ) // EncoderSilk 将音频编码为Silk @@ -23,7 +23,7 @@ func EncoderSilk(data []byte) ([]byte, error) { if silkPath := path.Join("data/cache", tempName+".silk"); PathExists(silkPath) { return os.ReadFile(silkPath) } - slk, err := codec.EncodeToSilk(data, tempName, true) + slk, err := base.EncodeSilk(data, tempName) if err != nil { return nil, errors.Wrap(err, "encode silk failed") } diff --git a/global/codec/doc.go b/global/codec/doc.go deleted file mode 100644 index ec2c484..0000000 --- a/global/codec/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package codec Slik编码核心模块 -package codec diff --git a/internal/base/feature.go b/internal/base/feature.go new file mode 100644 index 0000000..f27c0a0 --- /dev/null +++ b/internal/base/feature.go @@ -0,0 +1,31 @@ +package base + +import ( + "io" + + "github.com/pkg/errors" +) + +// silk encode features +var ( + EncodeSilk = encodeSilk // 编码 SilkV3 音频 + ResampleSilk = resampleSilk // 将silk重新编码为 24000 bit rate +) + +func encodeSilk(_ []byte, _ string) ([]byte, error) { + return nil, errors.New("not supported now") +} + +func resampleSilk(data []byte) []byte { + return data +} + +// Mime scan feature +var ( + IsLawfulImage = nocheck // 检查图片MIME + IsLawfulAudio = nocheck // 检查音频MIME +) + +func nocheck(_ io.ReadSeeker) (bool, string) { + return true, "" +} diff --git a/main.go b/main.go index 6c939d0..d2205f6 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,9 @@ import ( "golang.org/x/crypto/pbkdf2" "golang.org/x/term" + _ "github.com/Mrs4s/go-cqhttp/modules/mime" // mime检查模块 + _ "github.com/Mrs4s/go-cqhttp/modules/silk" // silk编码模块 + "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/global/config" diff --git a/modules/mime/mime.go b/modules/mime/mime.go new file mode 100644 index 0000000..4563099 --- /dev/null +++ b/modules/mime/mime.go @@ -0,0 +1,70 @@ +// Package mime 提供MIME检查功能 +package mime + +import ( + "io" + + "github.com/gabriel-vasile/mimetype" + "github.com/sirupsen/logrus" + + "github.com/Mrs4s/go-cqhttp/internal/base" +) + +func init() { + base.IsLawfulAudio = checkImage + base.IsLawfulAudio = checkAudio +} + +// keep sync with /docs/file.md#MINE +var lawfulImage = [...]string{ + "image/bmp", + "image/gif", + "image/jpeg", + "image/png", + "image/webp", +} + +var lawfulAudio = [...]string{ + "audio/aac", + "audio/aiff", + "audio/amr", + "audio/ape", + "audio/flac", + "audio/midi", + "audio/mp4", + "audio/mpeg", + "audio/ogg", + "audio/wav", + "audio/x-m4a", +} + +func check(r io.ReadSeeker, list []string) (bool, string) { + if base.SkipMimeScan { + return true, "" + } + _, _ = r.Seek(0, io.SeekStart) + defer r.Seek(0, io.SeekStart) // nolint + t, err := mimetype.DetectReader(r) + if err != nil { + logrus.Debugf("扫描 Mime 时出现问题: %v", err) + return false, "" + } + for _, lt := range list { + if t.Is(lt) { + return true, t.String() + } + } + return false, t.String() +} + +// checkImage 判断给定流是否为合法图片 +// 返回 是否合法, 实际Mime +// 判断后会自动将 Stream Seek 至 0 +func checkImage(r io.ReadSeeker) (bool, string) { + return check(r, lawfulImage[:]) +} + +// checkImage 判断给定流是否为合法音频 +func checkAudio(r io.ReadSeeker) (bool, string) { + return check(r, lawfulAudio[:]) +} diff --git a/global/codec/codec.go b/modules/silk/codec.go similarity index 80% rename from global/codec/codec.go rename to modules/silk/codec.go index 7563518..19118c6 100644 --- a/global/codec/codec.go +++ b/modules/silk/codec.go @@ -4,7 +4,7 @@ // +build !race // +build !nosilk -package codec +package silk import ( "os" @@ -19,8 +19,8 @@ import ( const silkCachePath = "data/cache" -// EncodeToSilk 将音频编码为Silk -func EncodeToSilk(record []byte, tempName string, useCache bool) (silkWav []byte, err error) { +// encode 将音频编码为Silk +func encode(record []byte, tempName string) (silkWav []byte, err error) { // 1. 写入缓存文件 rawPath := path.Join(silkCachePath, tempName+".wav") err = os.WriteFile(rawPath, record, os.ModePerm) @@ -50,15 +50,13 @@ func EncodeToSilk(record []byte, tempName string, useCache bool) (silkWav []byte if err != nil { return nil, errors.Wrap(err, "silk encode error") } - if useCache { - silkPath := path.Join(silkCachePath, tempName+".silk") - err = os.WriteFile(silkPath, silkWav, 0o666) - } + silkPath := path.Join(silkCachePath, tempName+".silk") + err = os.WriteFile(silkPath, silkWav, 0o666) return } -// RecodeTo24K 将silk重新编码为 24000 bit rate -func RecodeTo24K(data []byte) []byte { +// resample 将silk重新编码为 24000 bit rate +func resample(data []byte) []byte { pcm, err := silk.DecodeSilkBuffToPcm(data, 24000) if err != nil { panic(err) diff --git a/global/codec/codec_unsupported.go b/modules/silk/codec_unsupported.go similarity index 58% rename from global/codec/codec_unsupported.go rename to modules/silk/codec_unsupported.go index 336b023..684dcc3 100644 --- a/global/codec/codec_unsupported.go +++ b/modules/silk/codec_unsupported.go @@ -1,16 +1,16 @@ //go:build (!arm && !arm64 && !amd64 && !386) || (!windows && !linux && !darwin) || (windows && arm) || (windows && arm64) || race || nosilk // +build !arm,!arm64,!amd64,!386 !windows,!linux,!darwin windows,arm windows,arm64 race nosilk -package codec +package silk import "errors" -// EncodeToSilk 将音频编码为Silk -func EncodeToSilk(record []byte, tempName string, useCache bool) ([]byte, error) { +// encode 将音频编码为Silk +func encode(record []byte, tempName string, useCache bool) ([]byte, error) { return nil, errors.New("not supported now") } -// RecodeTo24K 将silk重新编码为 24000 bit rate -func RecodeTo24K(data []byte) []byte { +// resample 将silk重新编码为 24000 bit rate +func resample(data []byte) []byte { return data } diff --git a/modules/silk/stubs.go b/modules/silk/stubs.go new file mode 100644 index 0000000..1b86c70 --- /dev/null +++ b/modules/silk/stubs.go @@ -0,0 +1,11 @@ +// Package silk Silk编码核心模块 +package silk + +import ( + "github.com/Mrs4s/go-cqhttp/internal/base" +) + +func init() { + base.EncodeSilk = encode + base.ResampleSilk = resample +}