1
0
mirror of https://github.com/Mrs4s/go-cqhttp.git synced 2025-05-05 03:23:49 +08:00
go-cqhttp/coolq/cqcode/element.go
2023-01-31 21:20:48 +08:00

155 lines
3.0 KiB
Go

package cqcode
import (
"bytes"
"strings"
"unicode/utf8"
"github.com/Mrs4s/MiraiGo/binary"
)
// Element single message
type Element struct {
Type string
Data []Pair
}
// Pair key value pair
type Pair struct {
K string
V string
}
// CQCode convert element to cqcode
func (e *Element) CQCode() string {
buf := strings.Builder{}
e.WriteCQCodeTo(&buf)
return buf.String()
}
// WriteCQCodeTo write element's cqcode into sb
func (e *Element) WriteCQCodeTo(sb *strings.Builder) {
if e.Type == "text" {
sb.WriteString(EscapeText(e.Data[0].V)) // must be {"text": value}
return
}
sb.WriteString("[CQ:")
sb.WriteString(e.Type)
for _, data := range e.Data {
sb.WriteByte(',')
sb.WriteString(data.K)
sb.WriteByte('=')
sb.WriteString(EscapeValue(data.V))
}
sb.WriteByte(']')
}
// MarshalJSON see encoding/json.Marshaler
func (e *Element) MarshalJSON() ([]byte, error) {
return binary.NewWriterF(func(w *binary.Writer) {
buf := (*bytes.Buffer)(w)
// fmt.Fprintf(buf, `{"type":"%s","data":{`, e.Type)
buf.WriteString(`{"type":"`)
buf.WriteString(e.Type)
buf.WriteString(`","data":{`)
for i, data := range e.Data {
if i != 0 {
buf.WriteByte(',')
}
// fmt.Fprintf(buf, `"%s":%q`, data.K, data.V)
buf.WriteByte('"')
buf.WriteString(data.K)
buf.WriteString(`":`)
writeQuote(buf, data.V)
}
buf.WriteString(`}}`)
}), nil
}
const hex = "0123456789abcdef"
func writeQuote(b *bytes.Buffer, s string) {
i, j := 0, 0
b.WriteByte('"')
for j < len(s) {
c := s[j]
if c >= 0x20 && c <= 0x7f && c != '\\' && c != '"' {
// fast path: most of the time, printable ascii characters are used
j++
continue
}
switch c {
case '\\', '"', '\n', '\r', '\t':
b.WriteString(s[i:j])
b.WriteByte('\\')
switch c {
case '\n':
c = 'n'
case '\r':
c = 'r'
case '\t':
c = 't'
}
b.WriteByte(c)
j++
i = j
continue
case '<', '>', '&':
b.WriteString(s[i:j])
b.WriteString(`\u00`)
b.WriteByte(hex[c>>4])
b.WriteByte(hex[c&0xF])
j++
i = j
continue
}
// This encodes bytes < 0x20 except for \t, \n and \r.
if c < 0x20 {
b.WriteString(s[i:j])
b.WriteString(`\u00`)
b.WriteByte(hex[c>>4])
b.WriteByte(hex[c&0xF])
j++
i = j
continue
}
r, size := utf8.DecodeRuneInString(s[j:])
if r == utf8.RuneError && size == 1 {
b.WriteString(s[i:j])
b.WriteString(`\ufffd`)
j += size
i = j
continue
}
switch r {
case '\u2028', '\u2029':
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don't work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
b.WriteString(s[i:j])
b.WriteString(`\u202`)
b.WriteByte(hex[r&0xF])
j += size
i = j
continue
}
j += size
}
b.WriteString(s[i:])
b.WriteByte('"')
}