package afreeca import ( "bytes" "errors" "fmt" "git.beejay.kim/tool/service/ascii" "github.com/samber/lo" "strconv" "time" ) type kind []byte func newKind(category, index int) kind { return []byte( fmt.Sprintf("%04d%03d", category, index)) } type kindList struct { Ping kind Authorize kind Authenticate kind Authenticated kind Roster struct { List kind Change kind Status kind } Chat kind ChatEmoticon kind Donation kind } var ( Kind = &kindList{ Ping: newKind(0, 0), Authorize: newKind(1, 0), Authenticate: newKind(2, 0), Authenticated: newKind(94, 0), Chat: newKind(5, 0), ChatEmoticon: newKind(109, 0), Donation: newKind(18, 0), Roster: struct { List kind Change kind Status kind }{ List: newKind(4, 1), Change: newKind(4, 0), Status: newKind(13, 0), }, } ) type ChatMessage struct { raw []byte kind kind data []string at time.Time } func (m *ChatMessage) Unmarshall(buf []byte) error { m.at = time.Now() m.raw = buf if buf == nil || len(buf) < 14 { return errors.New("empty data") } if buf[0] != ascii.Escape || buf[1] != ascii.HorizontalTab || buf[14] != ascii.FormFeed { return errors.New("illegal data format") } m.data = []string{} m.kind = buf[2:9] var ( size = 0 data bytes.Buffer err error ) if size, err = strconv.Atoi(string(buf[9:12])); err != nil { return errors.New("illegal data size") } for i := range buf[15:] { last := i == size-2 b := buf[15+i : 16+i] if ascii.FormFeed == b[0] { m.data = append(m.data, data.String()) data.Reset() continue } data.Write(b) if last { m.data = append(m.data, data.String()) } } return nil } func (m *ChatMessage) Build() []byte { var ( buf bytes.Buffer size = lo.SumBy(m.data, func(item string) int { return len(item) }) ) buf.WriteByte(ascii.Escape) buf.WriteByte(ascii.HorizontalTab) buf.Write(m.kind) buf.WriteString(fmt.Sprintf("%03d00", size+len(m.data))) for i := range m.data { buf.WriteByte(ascii.FormFeed) buf.WriteString(m.data[i]) } return buf.Bytes() } func (m ChatMessage) Is(k kind) bool { return bytes.Compare(m.kind, k) == 0 } func (m ChatMessage) Kind() []byte { return m.kind } func (m ChatMessage) Data() []string { return m.data } func NewChatMessage(k kind, args ...string) *ChatMessage { return &ChatMessage{ kind: k, data: args, at: time.Now(), } } func NewChatAuthorizeMessage(token string, withQuickView bool) *ChatMessage { flag := "16" if withQuickView { flag = "524304" } return NewChatMessage(Kind.Authorize, token, "", flag, "") } func NewChatPingMessage() []byte { return NewChatMessage(Kind.Ping, "").Build() } func NewChatRosterRequestMessage() []byte { return NewChatMessage(Kind.Roster.Change, "").Build() }