@@ -0,0 +1,72 @@
|
||||
// Package volcsubtitle implements the native RTS subtitle callback documented at
|
||||
// https://docs.volcengine.com/docs/6348/1337284 (verified 2026-09-06).
|
||||
package volcsubtitle
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var ErrInvalid = errors.New("invalid native subtitle packet")
|
||||
|
||||
type Envelope struct {
|
||||
Message string `json:"message"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
type Segment struct {
|
||||
UserID string `json:"userId"`
|
||||
RoundID *int64 `json:"roundId"`
|
||||
Sequence *int64 `json:"sequence"`
|
||||
Text string `json:"text"`
|
||||
Definite bool `json:"definite"`
|
||||
Paragraph bool `json:"paragraph"`
|
||||
}
|
||||
type Packet struct {
|
||||
Type string `json:"type"`
|
||||
Data []Segment `json:"data"`
|
||||
DeviceName string `json:"deviceName,omitempty"`
|
||||
ProductKey string `json:"productKey,omitempty"`
|
||||
}
|
||||
|
||||
// Bind authentication to the platform conversation. A delayed callback can
|
||||
// never be reassigned by looking up the device's newest active conversation.
|
||||
func Signature(secret []byte, conversationID string) string {
|
||||
mac := hmac.New(sha256.New, secret)
|
||||
mac.Write([]byte("volc-subtitle-v1\n" + conversationID))
|
||||
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
|
||||
func Decode(message string) (Packet, error) {
|
||||
var p Packet
|
||||
if len(message) > 350000 {
|
||||
return p, ErrInvalid
|
||||
}
|
||||
data, err := base64.StdEncoding.DecodeString(message)
|
||||
if err != nil || len(data) == 0 {
|
||||
return p, ErrInvalid
|
||||
}
|
||||
if data[0] != '{' {
|
||||
if len(data) < 8 || string(data[:4]) != "subv" || uint64(binary.BigEndian.Uint32(data[4:8])) != uint64(len(data)-8) {
|
||||
return p, ErrInvalid
|
||||
}
|
||||
data = data[8:]
|
||||
}
|
||||
if !utf8.Valid(data) {
|
||||
return p, ErrInvalid
|
||||
}
|
||||
if json.Unmarshal(data, &p) != nil || p.Type != "subtitle" || len(p.Data) == 0 || len(p.Data) > 100 || len(p.DeviceName) > 128 || len(p.ProductKey) > 200 {
|
||||
return Packet{}, ErrInvalid
|
||||
}
|
||||
for _, s := range p.Data {
|
||||
if s.UserID == "" || len(s.UserID) > 200 || s.RoundID == nil || s.Sequence == nil ||
|
||||
*s.RoundID < 0 || *s.Sequence < 0 || len([]rune(s.Text)) > 20000 {
|
||||
return Packet{}, ErrInvalid
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
Reference in New Issue
Block a user