"errors"
"twitchchat/twitch/core/commands"
"twitchchat/twitch/core/messages"
+ "twitchchat/twitch/core/notice"
)
// see https://dev.twitch.tv/docs/irc/authenticate-bot/#sending-the-pass-and-nick-messages.
authResp <- msg
close(authResp)
case commands.Notice:
- err := msg.ToNoticeError()
- if errors.Is(err, messages.LoginFailedError) ||
- errors.Is(err, messages.InvalidAuthError) {
+ err := notice.Notice{msg}.ToNoticeError()
+ if errors.Is(err, notice.LoginFailedError) ||
+ errors.Is(err, notice.InvalidAuthError) {
authResp <- msg
close(authResp)
} else {
c.WriteMessage(messages.Nick(nickname))
for m := range authResp {
- if messages.IsNotice(m) {
- return res, m.ToNoticeError()
+ if notice.IsNotice(m) {
+ return res, notice.Notice{m}.ToNoticeError()
}
}
+++ /dev/null
-package clearchat
-
-import (
- "twitchchat/irc"
- "twitchchat/twitch/core/commands"
- "twitchchat/twitch/core/messages"
-)
-
-//go:generate tagprototype -out clearchattags.go -package clearchat -prototype @ban-duration=<duration>;room-id=<room-id>;target-user-id=<user-id>;tmi-sent-ts=<timestamp> -type ClearChatTag
-
-type ClearChat struct {
- messages.Message
-}
-
-func IsClearChat(m messages.Message) bool {
- return string(m.Command) == commands.ClearChat
-}
-
-func (c ClearChat) GetTag(key ClearChatTag) *irc.Tag {
- return c.Message.GetTag(string(key))
-}
-
-func (c ClearChat) User() string {
- return c.Params.Trailing
-}
+++ /dev/null
-//Generated by twitch-chat/x/generate/tagprototype
-
-package clearchat
-
-type ClearChatTag string
-
-const (
- BanDuration ClearChatTag = "ban-duration"
- RoomId ClearChatTag = "room-id"
- TargetUserId ClearChatTag = "target-user-id"
- TmiSentTs ClearChatTag = "tmi-sent-ts"
-)
--- /dev/null
+// generated by message
+
+package clearchat
+
+import (
+ "strings"
+ "twitchchat/twitch/core/messages"
+)
+
+type Clearchat struct {
+ messages.Message
+}
+
+func IsClearchat(m messages.Message) bool { return string(m.Command) == "CLEARCHAT" }
+
+func (m Clearchat) Channel() string { return m.Params.Params[0][1:] }
+
+func (m Clearchat) User() string { return strings.Split(m.Params.Trailing, " ")[0][0:] }
+
+func (m Clearchat) Duration() string {
+ for _, t := range m.Tags {
+ if t.Key == "ban-duration" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Clearchat) RoomId() string {
+ for _, t := range m.Tags {
+ if t.Key == "room-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Clearchat) UserId() string {
+ for _, t := range m.Tags {
+ if t.Key == "target-user-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Clearchat) Timestamp() string {
+ for _, t := range m.Tags {
+ if t.Key == "tmi-sent-ts" {
+ return t.Value
+ }
+ }
+ return ""
+}
+++ /dev/null
-package clearmsg
-
-import (
- "twitchchat/twitch/core/commands"
- "twitchchat/twitch/core/messages"
-)
-
-type ClearMsg struct {
- messages.Message
-}
-
-func IsClearMsg(m messages.Message) bool {
- return string(m.Command) == commands.ClearMsg
-}
-
-func (c ClearMsg) ChatMessage() string {
- return c.Params.Trailing
-}
--- /dev/null
+// generated by message
+
+package clearmsg
+
+import (
+ "strings"
+ "twitchchat/twitch/core/messages"
+)
+
+type Clearmsg struct {
+ messages.Message
+}
+
+func IsClearmsg(m messages.Message) bool { return string(m.Command) == "CLEARMSG" }
+
+func (m Clearmsg) Channel() string { return m.Params.Params[0][1:] }
+
+func (m Clearmsg) UserMessage() string { return strings.Split(m.Params.Trailing, " ")[0][0:] }
+
+func (m Clearmsg) Login() string {
+ for _, t := range m.Tags {
+ if t.Key == "login" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Clearmsg) RoomId() string {
+ for _, t := range m.Tags {
+ if t.Key == "room-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Clearmsg) TargetMsgId() string {
+ for _, t := range m.Tags {
+ if t.Key == "target-msg-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Clearmsg) Timestamp() string {
+ for _, t := range m.Tags {
+ if t.Key == "tmi-sent-ts" {
+ return t.Value
+ }
+ }
+ return ""
+}
+++ /dev/null
-package globaluserstate
-
-import (
- "twitchchat/twitch/core/commands"
- "twitchchat/twitch/core/messages"
-)
-
-//go:generate tagprototype -out globaluserstatetags.go -package globaluserstate -type GlobalUserStateTag -prototype @badge-info=<badge-info>;badges=<badges>;color=<color>;display-name=<display-name>;emote-sets=<emote-sets>;turbo=<turbo>;user-id=<user-id>;user-type=<user-type>
-
-func IsGlobalUserState(m messages.Message) bool {
- return m.Command == commands.GlobalUserState
-}
+++ /dev/null
-//Generated by twitch-chat/x/generate/tagprototype
-
-package globaluserstate
-
-type GlobalUserStateTag string
-
-const (
- BadgeInfo GlobalUserStateTag = "badge-info"
- Badges GlobalUserStateTag = "badges"
- Color GlobalUserStateTag = "color"
- DisplayName GlobalUserStateTag = "display-name"
- EmoteSets GlobalUserStateTag = "emote-sets"
- Turbo GlobalUserStateTag = "turbo"
- UserId GlobalUserStateTag = "user-id"
- UserType GlobalUserStateTag = "user-type"
-)
--- /dev/null
+// generated by message
+
+package globaluserstate
+
+import "twitchchat/twitch/core/messages"
+
+type Globaluserstate struct {
+ messages.Message
+}
+
+func IsGlobaluserstate(m messages.Message) bool { return string(m.Command) == "GLOBALUSERSTATE" }
+
+func (m Globaluserstate) BadgeInfo() string {
+ for _, t := range m.Tags {
+ if t.Key == "badge-info" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Globaluserstate) Badges() string {
+ for _, t := range m.Tags {
+ if t.Key == "badges" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Globaluserstate) Color() string {
+ for _, t := range m.Tags {
+ if t.Key == "color" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Globaluserstate) DisplayName() string {
+ for _, t := range m.Tags {
+ if t.Key == "display-name" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Globaluserstate) EmoteSets() string {
+ for _, t := range m.Tags {
+ if t.Key == "emote-sets" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Globaluserstate) Turbo() string {
+ for _, t := range m.Tags {
+ if t.Key == "turbo" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Globaluserstate) UserId() string {
+ for _, t := range m.Tags {
+ if t.Key == "user-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Globaluserstate) UserType() string {
+ for _, t := range m.Tags {
+ if t.Key == "user-type" {
+ return t.Value
+ }
+ }
+ return ""
+}
+++ /dev/null
-package hosttarget
-
-import (
- "strconv"
- "strings"
- "twitchchat/twitch/core/commands"
- "twitchchat/twitch/core/messages"
-)
-
-type HostTarget struct {
- messages.Message
-}
-
-func IsHostTarget(m messages.Message) bool {
- return m.Command == commands.HostTarget
-}
-
-func (h HostTarget) ChannelTarget() string {
- parts := strings.Split(h.Params.Trailing, " ")
- if len(parts) != 2 {
- return ""
- }
- return parts[0]
-}
-
-func (h HostTarget) NumberOfViewers() int {
- parts := strings.Split(h.Params.Trailing, " ")
- if len(parts) != 2 {
- return 0
- }
- if i, err := strconv.Atoi(parts[1]); err != nil {
- return 0
- } else {
- return i
- }
-}
--- /dev/null
+// generated by message
+
+package hosttarget
+
+import (
+ "strings"
+ "twitchchat/twitch/core/messages"
+)
+
+type Hosttarget struct {
+ messages.Message
+}
+
+func IsHosttarget(m messages.Message) bool { return string(m.Command) == "HOSTTARGET" }
+
+func (m Hosttarget) HostingChannel() string { return m.Params.Params[0][1:] }
+
+func (m Hosttarget) Channel() string { return strings.Split(m.Params.Trailing, " ")[0][0:] }
+
+func (m Hosttarget) NumberOfViewers() string { return strings.Split(m.Params.Trailing, " ")[1][0:] }
import (
"fmt"
"twitchchat/twitch/core/messages"
+ "twitchchat/twitch/core/notice"
)
type ChannelMembers map[string][]string
resp := <-msgs
fmt.Fprintf(DebugLogger, "Reading channel members for %s\n", resp)
- if messages.IsNotice(resp) {
- return msgs, channelMembers, resp.ToNoticeError()
+ if notice.IsNotice(resp) {
+ return msgs, channelMembers, notice.Notice{resp}.ToNoticeError()
} else if !messages.IsJoin(resp) {
return msgs, channelMembers, UnexpectedAnswerError
}
--- /dev/null
+package core
+
+//go:generate message -proto "@badge-info=;badges=<badges>;client-nonce=<client-nonce>;color=<color>;display-name=<display-name>;emote-only=<emote-only>;emotes=<emotes>;first-msg=<first-msg>;flags=;id=<id>;mod=<mod>;room-id=<room-id>;subscriber=<subscriber>;tmi-sent-ts=<tmi-sent-ts>;turbo=<turbo>;user-id=<user-id>;user-type= :<user>!<user>@<user>.tmi.twitch.tv PRIVMSG #<channel> :<user-message>"
+
+//go:generate message -proto "@ban-duration=<duration>;room-id=<room-id>;target-user-id=<user-id>;tmi-sent-ts=<timestamp> :tmi.twitch.tv CLEARCHAT #<channel> :<user>"
+
+//go:generate message -proto "@login=<login>;room-id=<room-id>;target-msg-id=<target-msg-id>;tmi-sent-ts=<timestamp> :tmi.twitch.tv CLEARMSG #<channel> :<user-message>"
+
+//go:generate message -proto "@badge-info=<badge-info>;badges=<badges>;color=<color>;display-name=<display-name>;emote-sets=<emote-sets>;turbo=<turbo>;user-id=<user-id>;user-type=<user-type> :tmi.twitch.tv GLOBALUSERSTATE"
+
+//go:generate message -proto ":tmi.twitch.tv HOSTTARGET #<hosting-channel> :[-|<channel>] <number-of-viewers>"
+
+//go:generate message -proto "@msg-id=<msg-id>;target-user-id=<user-id> :tmi.twitch.tv NOTICE #<channel> :<notice-message>"
+
+//go:generate message -proto ":tmi.twitch.tv RECONNECT"
+
+//go:generate message -proto "@emote-only=<emote-only>;followers-only=<followers-only>;r9k=<r9k>;rituals=<rituals>;room-id=<room-id>;slow=<slow>;subs-only=<subs-only> :tmi.twitch.tv ROOMSTATE #<channel>"
+
+//go:generate message -proto "@badge-info=<badge-info>;badges=<badges>;color=<color>;display-name=<display-name>;emotes=<emotes>;id=<id-of-msg>;login=<user>;mod=<mod>;msg-id=<msg-id>;room-id=<room-id>;subscriber=<subscriber>;system-msg=<system-msg>;tmi-sent-ts=<timestamp>;turbo=<turbo>;user-id=<user-id>;user-type=<user-type> :tmi.twitch.tv USERNOTICE #<channel> :[<user-message>]"
+
+//go:generate message -proto "@badge-info=<badge-info>;badges=<badges>;color=<color>;display-name=<display-name>;emote-sets=<emote-sets>;id=<id>;mod=<mod>;subscriber=<subscriber>;turbo=<turbo>;user-type=<user-type> :tmi.twitch.tv USERSTATE #<channel>"
+
+//go:generate message -proto "@badges=<badges>;color=<color>;display-name=<display-name>;emotes=<emotes>;message-id=<msg-id>;thread-id=<thread-id>;turbo=<turbo>;user-id=<user-id>;user-type=<user-type> :<to-user>!<to-user>@<to-user>.tmi.twitch.tv WHISPER <from-user> :<user-message>"
b := strings.Builder{}
for n, c := range channels {
if n != 0 {
- b.WriteString(",")
+ b.WriteByte(',')
}
- b.WriteString("#")
+ b.WriteByte('#')
b.WriteString(c)
}
return Message{
-package messages
+package notice
-import (
- "errors"
- "twitchchat/twitch/core/commands"
-)
+import "errors"
var LoginFailedError = errors.New("login authentication failed")
var InvalidAuthError = errors.New("improperly formatted auth")
-func IsNotice(m Message) bool {
- return m.Command == commands.Notice
-}
-
-func (m Message) ToNoticeError() error {
- if !IsNotice(m) {
- return nil
- }
-
+func (m Notice) ToNoticeError() error {
// known errors by string value
switch m.Params.Trailing {
case "Login authentication failed":
--- /dev/null
+// generated by message
+
+package notice
+
+import (
+ "strings"
+ "twitchchat/twitch/core/messages"
+)
+
+type Notice struct {
+ messages.Message
+}
+
+func IsNotice(m messages.Message) bool { return string(m.Command) == "NOTICE" }
+
+func (m Notice) Channel() string { return m.Params.Params[0][1:] }
+
+func (m Notice) NoticeMessage() string { return strings.Split(m.Params.Trailing, " ")[0][0:] }
+
+func (m Notice) MsgId() string {
+ for _, t := range m.Tags {
+ if t.Key == "msg-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Notice) UserId() string {
+ for _, t := range m.Tags {
+ if t.Key == "target-user-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+++ /dev/null
-package notice
-
-import (
- "twitchchat/irc"
- "twitchchat/twitch/core/commands"
- "twitchchat/twitch/core/messages"
-)
-
-//go:generate tagprototype -out noticetags.go -package notice -type NoticeTag -prototype @msg-id=<msg-id>;target-user-id=<user-id>
-
-type Notice struct {
- messages.Message
-}
-
-func IsNotice(m messages.Message) bool {
- return m.Command == commands.Notice
-}
-
-func (n Notice) GetTag(tag NoticeTag) *irc.Tag {
- return n.Message.GetTag(string(tag))
-}
-
-func (n Notice) NoticeMessage() string {
- return n.Params.Trailing
-}
+++ /dev/null
-//Generated by twitch-chat/x/generate/tagprototype
-
-package notice
-
-type NoticeTag string
-
-const (
- MsgId NoticeTag = "msg-id"
- TargetUserId NoticeTag = "target-user-id"
-)
--- /dev/null
+// generated by message
+
+package privmsg
+
+import (
+ "strings"
+ "twitchchat/twitch/core/messages"
+)
+
+type Privmsg struct {
+ messages.Message
+}
+
+func IsPrivmsg(m messages.Message) bool { return string(m.Command) == "PRIVMSG" }
+
+func (m Privmsg) User() string { return m.Prefix.User }
+
+func (m Privmsg) Channel() string { return m.Params.Params[0][1:] }
+
+func (m Privmsg) UserMessage() string { return strings.Split(m.Params.Trailing, " ")[0][0:] }
+
+func (m Privmsg) Badges() string {
+ for _, t := range m.Tags {
+ if t.Key == "badges" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) ClientNonce() string {
+ for _, t := range m.Tags {
+ if t.Key == "client-nonce" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) Color() string {
+ for _, t := range m.Tags {
+ if t.Key == "color" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) DisplayName() string {
+ for _, t := range m.Tags {
+ if t.Key == "display-name" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) EmoteOnly() string {
+ for _, t := range m.Tags {
+ if t.Key == "emote-only" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) Emotes() string {
+ for _, t := range m.Tags {
+ if t.Key == "emotes" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) FirstMsg() string {
+ for _, t := range m.Tags {
+ if t.Key == "first-msg" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) Id() string {
+ for _, t := range m.Tags {
+ if t.Key == "id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) Mod() string {
+ for _, t := range m.Tags {
+ if t.Key == "mod" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) RoomId() string {
+ for _, t := range m.Tags {
+ if t.Key == "room-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) Subscriber() string {
+ for _, t := range m.Tags {
+ if t.Key == "subscriber" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) TmiSentTs() string {
+ for _, t := range m.Tags {
+ if t.Key == "tmi-sent-ts" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) Turbo() string {
+ for _, t := range m.Tags {
+ if t.Key == "turbo" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Privmsg) UserId() string {
+ for _, t := range m.Tags {
+ if t.Key == "user-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+++ /dev/null
-package privmsg
-
-import (
- "twitchchat/irc"
- "twitchchat/twitch/core/commands"
- "twitchchat/twitch/core/messages"
-)
-
-//go:generate tagprototype -out "privmsgtags.go" -type PrivMsgTag -prototype "@badge-info=<badge-info>;badges=<badges>;bits=<bits>client-nonce=<nonce>;color=<color>;display-name=<display-name>;emotes=<emotes>;first-msg=<first-msg>;flags=<flags>;id=<msg-id>;mod=<mod>;room-id=<room-id>;subscriber=<subscriber>;tmi-sent-ts=<timestamp>;turbo=<turbo>;user-id=<user-id>;user-type=<user-type>;reply-parent-msg-id=<reply-parent-msg-id>;reply-parent-user-id=<reply-parent-user-id>;reply-parent-user-login=<reply-parent-user-login>;reply-parent-display-name=<reply-parent-display-name>;reply-parent-msg-body=<reply-parent-msg-body>;reply-thread-parent-msg-id=<reply-thread-parent-msg-id>;reply-thread-parent-user-login=<reply-thread-parent-user-login>;vip=<vip>" -package privmsg
-
-type PrivMsg struct {
- messages.Message
-}
-
-func IsPrivateMessage(m messages.Message) bool {
- return string(m.Command) == commands.PrivMsg
-}
-
-func (p PrivMsg) User() string {
- return p.Prefix.Name
-}
-
-func (p PrivMsg) Text() string {
- return p.Params.Trailing
-}
-
-func (p PrivMsg) GetTag(key PrivMsgTag) *irc.Tag {
- return p.Message.GetTag(string(key))
-}
+++ /dev/null
-//Generated by twitch-chat/x/generate/tagprototype
-
-package privmsg
-
-type PrivMsgTag string
-
-const (
- BadgeInfo PrivMsgTag = "badge-info"
- Badges PrivMsgTag = "badges"
- Bits PrivMsgTag = "bits"
- Color PrivMsgTag = "color"
- DisplayName PrivMsgTag = "display-name"
- Emotes PrivMsgTag = "emotes"
- FirstMsg PrivMsgTag = "first-msg"
- Flags PrivMsgTag = "flags"
- Id PrivMsgTag = "id"
- Mod PrivMsgTag = "mod"
- RoomId PrivMsgTag = "room-id"
- Subscriber PrivMsgTag = "subscriber"
- TmiSentTs PrivMsgTag = "tmi-sent-ts"
- Turbo PrivMsgTag = "turbo"
- UserId PrivMsgTag = "user-id"
- UserType PrivMsgTag = "user-type"
- ReplyParentMsgId PrivMsgTag = "reply-parent-msg-id"
- ReplyParentUserId PrivMsgTag = "reply-parent-user-id"
- ReplyParentUserLogin PrivMsgTag = "reply-parent-user-login"
- ReplyParentDisplayName PrivMsgTag = "reply-parent-display-name"
- ReplyParentMsgBody PrivMsgTag = "reply-parent-msg-body"
- ReplyThreadParentMsgId PrivMsgTag = "reply-thread-parent-msg-id"
- ReplyThreadParentUserLogin PrivMsgTag = "reply-thread-parent-user-login"
- Vip PrivMsgTag = "vip"
-)
--- /dev/null
+// generated by message
+
+package reconnect
+
+import "twitchchat/twitch/core/messages"
+
+type Reconnect struct {
+ messages.Message
+}
+
+func IsReconnect(m messages.Message) bool { return string(m.Command) == "RECONNECT" }
--- /dev/null
+// generated by message
+
+package roomstate
+
+import "twitchchat/twitch/core/messages"
+
+type Roomstate struct {
+ messages.Message
+}
+
+func IsRoomstate(m messages.Message) bool { return string(m.Command) == "ROOMSTATE" }
+
+func (m Roomstate) Channel() string { return m.Params.Params[0][1:] }
+
+func (m Roomstate) EmoteOnly() string {
+ for _, t := range m.Tags {
+ if t.Key == "emote-only" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Roomstate) FollowersOnly() string {
+ for _, t := range m.Tags {
+ if t.Key == "followers-only" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Roomstate) R9k() string {
+ for _, t := range m.Tags {
+ if t.Key == "r9k" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Roomstate) Rituals() string {
+ for _, t := range m.Tags {
+ if t.Key == "rituals" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Roomstate) RoomId() string {
+ for _, t := range m.Tags {
+ if t.Key == "room-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Roomstate) Slow() string {
+ for _, t := range m.Tags {
+ if t.Key == "slow" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Roomstate) SubsOnly() string {
+ for _, t := range m.Tags {
+ if t.Key == "subs-only" {
+ return t.Value
+ }
+ }
+ return ""
+}
+++ /dev/null
-package roomstate
-
-import (
- "twitchchat/irc"
- "twitchchat/twitch/core/commands"
- "twitchchat/twitch/core/messages"
-)
-
-//go:generate tagprototype -out roomstatetags.go -package roomstate -type RoomStateTag -prototype @emote-only=<emote-only>;followers-only=<followers-only>;r9k=<r9k>;rituals=<rituals>;room-id=<room-id>;slow=<slow>;subs-only=<subs-only>
-
-type RoomState struct {
- messages.Message
-}
-
-func IsRoomState(m messages.Message) bool {
- return m.Command == commands.RoomState
-}
-
-func (r RoomState) GetTag(tag RoomStateTag) *irc.Tag {
- return r.Message.GetTag(string(tag))
-}
+++ /dev/null
-//Generated by twitch-chat/x/generate/tagprototype
-
-package roomstate
-
-type RoomStateTag string
-
-const (
- EmoteOnly RoomStateTag = "emote-only"
- FollowersOnly RoomStateTag = "followers-only"
- R9k RoomStateTag = "r9k"
- Rituals RoomStateTag = "rituals"
- RoomId RoomStateTag = "room-id"
- Slow RoomStateTag = "slow"
- SubsOnly RoomStateTag = "subs-only"
-)
--- /dev/null
+// generated by message
+
+package usernotice
+
+import (
+ "strings"
+ "twitchchat/twitch/core/messages"
+)
+
+type Usernotice struct {
+ messages.Message
+}
+
+func IsUsernotice(m messages.Message) bool { return string(m.Command) == "USERNOTICE" }
+
+func (m Usernotice) Channel() string { return m.Params.Params[0][1:] }
+
+func (m Usernotice) UserMessage() string { return strings.Split(m.Params.Trailing, " ")[0][0:] }
+
+func (m Usernotice) BadgeInfo() string {
+ for _, t := range m.Tags {
+ if t.Key == "badge-info" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) Badges() string {
+ for _, t := range m.Tags {
+ if t.Key == "badges" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) Color() string {
+ for _, t := range m.Tags {
+ if t.Key == "color" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) DisplayName() string {
+ for _, t := range m.Tags {
+ if t.Key == "display-name" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) Emotes() string {
+ for _, t := range m.Tags {
+ if t.Key == "emotes" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) IdOfMsg() string {
+ for _, t := range m.Tags {
+ if t.Key == "id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) User() string {
+ for _, t := range m.Tags {
+ if t.Key == "login" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) Mod() string {
+ for _, t := range m.Tags {
+ if t.Key == "mod" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) MsgId() string {
+ for _, t := range m.Tags {
+ if t.Key == "msg-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) RoomId() string {
+ for _, t := range m.Tags {
+ if t.Key == "room-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) Subscriber() string {
+ for _, t := range m.Tags {
+ if t.Key == "subscriber" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) SystemMsg() string {
+ for _, t := range m.Tags {
+ if t.Key == "system-msg" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) Timestamp() string {
+ for _, t := range m.Tags {
+ if t.Key == "tmi-sent-ts" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) Turbo() string {
+ for _, t := range m.Tags {
+ if t.Key == "turbo" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) UserId() string {
+ for _, t := range m.Tags {
+ if t.Key == "user-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Usernotice) UserType() string {
+ for _, t := range m.Tags {
+ if t.Key == "user-type" {
+ return t.Value
+ }
+ }
+ return ""
+}
+++ /dev/null
-package usernotice
-
-import (
- "twitchchat/twitch/core/commands"
- "twitchchat/twitch/core/messages"
-)
-
-//go:generate tagprototype -out usernoticetags.go -package usernotice -type UserNoticeTag -prototype @badge-info=<badge-info>;badges=<badges>;color=<color>;display-name=<display-name>;emotes=<emotes>;id=<id-of-msg>;login=<user>;mod=<mod>;msg-id=<msg-id>;room-id=<room-id>;subscriber=<subscriber>;system-msg=<system-msg>;tmi-sent-ts=<timestamp>;turbo=<turbo>;user-id=<user-id>;user-type=<user-type>
-
-type UserNotice struct {
- messages.Message
-}
-
-func IsUserNotice(m messages.Message) bool {
- return m.Command == commands.UserNotice
-}
-
-func (u UserNotice) UserMessage() string {
- return u.Params.Trailing
-}
+++ /dev/null
-//Generated by twitch-chat/x/generate/tagprototype
-
-package usernotice
-
-type UserNoticeTag string
-
-const (
- BadgeInfo UserNoticeTag = "badge-info"
- Badges UserNoticeTag = "badges"
- Color UserNoticeTag = "color"
- DisplayName UserNoticeTag = "display-name"
- Emotes UserNoticeTag = "emotes"
- Id UserNoticeTag = "id"
- Login UserNoticeTag = "login"
- Mod UserNoticeTag = "mod"
- MsgId UserNoticeTag = "msg-id"
- RoomId UserNoticeTag = "room-id"
- Subscriber UserNoticeTag = "subscriber"
- SystemMsg UserNoticeTag = "system-msg"
- TmiSentTs UserNoticeTag = "tmi-sent-ts"
- Turbo UserNoticeTag = "turbo"
- UserId UserNoticeTag = "user-id"
- UserType UserNoticeTag = "user-type"
-)
--- /dev/null
+// generated by message
+
+package userstate
+
+import "twitchchat/twitch/core/messages"
+
+type Userstate struct {
+ messages.Message
+}
+
+func IsUserstate(m messages.Message) bool { return string(m.Command) == "USERSTATE" }
+
+func (m Userstate) Channel() string { return m.Params.Params[0][1:] }
+
+func (m Userstate) BadgeInfo() string {
+ for _, t := range m.Tags {
+ if t.Key == "badge-info" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Userstate) Badges() string {
+ for _, t := range m.Tags {
+ if t.Key == "badges" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Userstate) Color() string {
+ for _, t := range m.Tags {
+ if t.Key == "color" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Userstate) DisplayName() string {
+ for _, t := range m.Tags {
+ if t.Key == "display-name" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Userstate) EmoteSets() string {
+ for _, t := range m.Tags {
+ if t.Key == "emote-sets" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Userstate) Id() string {
+ for _, t := range m.Tags {
+ if t.Key == "id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Userstate) Mod() string {
+ for _, t := range m.Tags {
+ if t.Key == "mod" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Userstate) Subscriber() string {
+ for _, t := range m.Tags {
+ if t.Key == "subscriber" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Userstate) Turbo() string {
+ for _, t := range m.Tags {
+ if t.Key == "turbo" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Userstate) UserType() string {
+ for _, t := range m.Tags {
+ if t.Key == "user-type" {
+ return t.Value
+ }
+ }
+ return ""
+}
+++ /dev/null
-package userstate
-
-import (
- "twitchchat/irc"
- "twitchchat/twitch/core/commands"
- "twitchchat/twitch/core/messages"
-)
-
-//go:generate tagprototype -out userstatetags.go -package userstate -type UserStateTag -prototype @badge-info=;badges=staff/1;color=#0D4200;display-name=ronni;emote-sets=0,33,50,237,793,2126,3517,4578,5569,9400,10337,12239;mod=1;subscriber=1;turbo=1;user-type=staff :tmi.twitch.tv USERSTATE #dallas
-
-type UserState struct {
- messages.Message
-}
-
-func IsUserState(m messages.Message) bool {
- return m.Command == commands.UserState
-}
-
-func (u UserState) GetTag(tag UserStateTag) *irc.Tag {
- return u.Message.GetTag(string(tag))
-}
+++ /dev/null
-//Generated by twitch-chat/x/generate/tagprototype
-
-package userstate
-
-type UserStateTag string
-
-const (
- BadgeInfo UserStateTag = "badge-info"
- Badges UserStateTag = "badges"
- Color UserStateTag = "color"
- DisplayName UserStateTag = "display-name"
- EmoteSets UserStateTag = "emote-sets"
- Mod UserStateTag = "mod"
- Subscriber UserStateTag = "subscriber"
- Turbo UserStateTag = "turbo"
- UserType UserStateTag = "user-type"
-)
--- /dev/null
+// generated by message
+
+package whisper
+
+import (
+ "strings"
+ "twitchchat/twitch/core/messages"
+)
+
+type Whisper struct {
+ messages.Message
+}
+
+func IsWhisper(m messages.Message) bool { return string(m.Command) == "WHISPER" }
+
+func (m Whisper) ToUser() string { return m.Prefix.User }
+
+func (m Whisper) FromUser() string { return m.Params.Params[0][0:] }
+
+func (m Whisper) UserMessage() string { return strings.Split(m.Params.Trailing, " ")[0][0:] }
+
+func (m Whisper) Badges() string {
+ for _, t := range m.Tags {
+ if t.Key == "badges" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Whisper) Color() string {
+ for _, t := range m.Tags {
+ if t.Key == "color" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Whisper) DisplayName() string {
+ for _, t := range m.Tags {
+ if t.Key == "display-name" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Whisper) Emotes() string {
+ for _, t := range m.Tags {
+ if t.Key == "emotes" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Whisper) MsgId() string {
+ for _, t := range m.Tags {
+ if t.Key == "message-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Whisper) ThreadId() string {
+ for _, t := range m.Tags {
+ if t.Key == "thread-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Whisper) Turbo() string {
+ for _, t := range m.Tags {
+ if t.Key == "turbo" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Whisper) UserId() string {
+ for _, t := range m.Tags {
+ if t.Key == "user-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Whisper) UserType() string {
+ for _, t := range m.Tags {
+ if t.Key == "user-type" {
+ return t.Value
+ }
+ }
+ return ""
+}
+++ /dev/null
-package whisper
-
-import (
- "twitchchat/twitch/core/commands"
- "twitchchat/twitch/core/messages"
-)
-
-//go:generate tagprototype -out whispertags.go -package whisper -type WhisperTag -prototype @badges=<badges>;color=<color>;display-name=<display-name>;emotes=<emotes>;message-id=<msg-id>;thread-id=<thread-id>;turbo=<turbo>;user-id=<user-id>;user-type=<user-type>
-
-type Whisper struct {
- messages.Message
-}
-
-func IsWhisper(m messages.Message) bool {
- return m.Command == commands.Whisper
-}
-
-func (w Whisper) ToUser() string {
- return w.Prefix.User
-}
-
-func (w Whisper) FromUser() string {
- if len(w.Params.Params) != 1 {
- return ""
- }
- return w.Params.Params[0]
-}
-
-func (w Whisper) WhisperMessage() string {
- return w.Params.Trailing
-}
+++ /dev/null
-//Generated by twitch-chat/x/generate/tagprototype
-
-package whisper
-
-type WhisperTag string
-
-const (
- Badges WhisperTag = "badges"
- Color WhisperTag = "color"
- DisplayName WhisperTag = "display-name"
- Emotes WhisperTag = "emotes"
- MessageId WhisperTag = "message-id"
- ThreadId WhisperTag = "thread-id"
- Turbo WhisperTag = "turbo"
- UserId WhisperTag = "user-id"
- UserType WhisperTag = "user-type"
-)
var format string
rmax := NewRunningMax(50)
for m := range msgs {
- if privmsg.IsPrivateMessage(m) {
- priv := privmsg.PrivMsg{m}
+ if privmsg.IsPrivmsg(m) {
+ priv := privmsg.Privmsg{m}
u := priv.User()
mx := rmax.Push(len(u))
format = fmt.Sprintf("%%%ds: %%s\n", mx)
--- /dev/null
+// generated by /tmp/go-build2868507172/b001/exe/message
+
+package clearchat
+
+import (
+ "strings"
+ "twitchchat/irc"
+)
+
+type Clearchat struct {
+ irc.Message
+}
+
+func IsClearchat(m irc.Message) bool { return string(m.Command) == "CLEARCHAT" }
+
+func (m Clearchat) Channel() string { return m.Params.Params[0][1:] }
+
+func (m Clearchat) User() string { return strings.Split(m.Params.Trailing, " ")[0][0:] }
+
+func (m Clearchat) Duration() string {
+ for _, t := range m.Tags {
+ if t.Key == "ban-duration" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Clearchat) RoomId() string {
+ for _, t := range m.Tags {
+ if t.Key == "room-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Clearchat) UserId() string {
+ for _, t := range m.Tags {
+ if t.Key == "target-user-id" {
+ return t.Value
+ }
+ }
+ return ""
+}
+
+func (m Clearchat) Timestamp() string {
+ for _, t := range m.Tags {
+ if t.Key == "tmi-sent-ts" {
+ return t.Value
+ }
+ }
+ return ""
+}
--- /dev/null
+package main
+
+import (
+ "strings"
+ "unicode"
+)
+
+func convertCommandToName(command string) string {
+ b := strings.Builder{}
+ upper := true
+ for _, c := range command {
+ if upper {
+ b.WriteRune(unicode.ToUpper(c))
+ upper = false
+ } else {
+ b.WriteRune(unicode.ToLower(c))
+ }
+ }
+ return b.String()
+}
+
+func convertCommandToDir(command string) string {
+ return strings.ToLower(command)
+}
+
+func convertCommandToPackage(command string) string {
+ return strings.ToLower(command)
+}
+
+func isParam(word string) bool {
+ left := strings.Index(word, "<")
+ right := strings.Index(word, ">")
+ return left != -1 && right != -1
+}
+
+func convertWordToName(word string) (prefix, name string) {
+ prefB := strings.Builder{}
+ nameB := strings.Builder{}
+
+ if !isParam(word) {
+ return word, ""
+ }
+
+ const (
+ s_init int = iota
+ s_optional
+ s_param
+ )
+
+ part := true
+ state := s_init
+ for _, c := range word {
+ switch state {
+
+ case s_init:
+ switch c {
+ case '[':
+ state = s_optional
+ case '<':
+ state = s_param
+ default:
+ prefB.WriteRune(c)
+ }
+
+ case s_optional:
+ switch c {
+ case '<':
+ state = s_param
+ }
+
+ case s_param:
+ if c == '>' || c == ']' {
+ continue
+ } else if part {
+ nameB.WriteRune(unicode.ToUpper(c))
+ part = false
+ } else if c == '-' {
+ part = true
+ } else {
+ nameB.WriteRune(c)
+ }
+
+ }
+ }
+ return prefB.String(), nameB.String()
+}
--- /dev/null
+package main
+
+import "testing"
+
+func TestConvertWordToName(t *testing.T) {
+ data := []struct {
+ in, prefix, name string
+ }{
+ {"<to-user>", "", "ToUser"},
+ {"#<channel>", "#", "Channel"},
+ {"#<hosting-channel>", "#", "HostingChannel"},
+ {"[-|<channel>]", "", "Channel"},
+ {"[<message>]", "", "Message"},
+ }
+ for _, d := range data {
+ prefix, name := convertWordToName(d.in)
+ if prefix != d.prefix || name != d.name {
+ t.Errorf("convertWordToName(%#v) = %#v, %#v but expected %#v, %#v.\n", d.in, prefix, name, d.prefix, d.name)
+ }
+ }
+}
--- /dev/null
+package main
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "twitchchat/irc"
+)
+
+func generateMessage(msg irc.Message, w io.Writer) (n int, err error) {
+ write := func(s string) {
+ if err == nil {
+ var m int
+ m, err = io.WriteString(w, s)
+ n += m
+ }
+ }
+
+ // header
+ write(fmt.Sprintf("// generated by %s\n", os.Args[0]))
+ write("\n")
+ write(fmt.Sprintf("package %s\n", convertCommandToPackage(string(msg.Command))))
+ write("\n")
+
+ // imports
+ if msg.Params.Trailing != "" {
+ write("import \"strings\"\n")
+ write("\n")
+ }
+
+ // Potential optimization is to have all properties from the message as strings
+ // in the type and lazy load them
+
+ var typeDef = convertCommandToName(string(msg.Command))
+ // type definition
+ write(fmt.Sprintf("type %s struct {\n", typeDef))
+ write(fmt.Sprintf("\tmessages.Message\n"))
+ write(fmt.Sprintf("}\n"))
+ write("\n")
+
+ // command checking
+
+ write(fmt.Sprintf("func Is%s(m messages.Message) bool { return string(m.Command) == %#v }\n", typeDef, msg.Command))
+ write("\n")
+
+ // from the user value
+ if isParam(msg.Prefix.User) {
+ // there shouldn't be a prefix here
+ _, n := convertWordToName(msg.Prefix.User)
+ write(fmt.Sprintf("func (m %s) %s() string { return m.Prefix.User }\n", typeDef, n))
+ write("\n")
+ }
+
+ // all params
+ for d, p := range msg.Params.Params {
+ p, n := convertWordToName(p)
+ write(fmt.Sprintf("func (m %s) %s() string { return m.Params.Params[%d][%d:] }\n", typeDef, n, d, len(p)))
+ write("\n")
+ }
+
+ // words in trailing
+ if msg.Params.Trailing != "" {
+ for d, p := range strings.Split(msg.Params.Trailing, " ") {
+ p, n := convertWordToName(p)
+ write(fmt.Sprintf("func (m %s) %s() string { return strings.Split(m.Params.Trailing, \" \")[%d][%d:] }\n", typeDef, n, d, len(p)))
+ write("\n")
+ }
+ }
+
+ // tags
+ for _, t := range msg.Tags {
+ // shouldn't have a prefx
+ if isParam(t.Value) {
+ _, n := convertWordToName(t.Value)
+ write(fmt.Sprintf("func (m %s) %s() string {\n", typeDef, n))
+ write(fmt.Sprintf("\tfor _, t := range m.Tags {\n"))
+ write(fmt.Sprintf("\t\tif t.Key == %#v { return t.Value }\n", t.Key))
+ write(fmt.Sprintf("\t}\n"))
+ write(fmt.Sprintf("\treturn \"\"\n"))
+ write(fmt.Sprintf("}\n"))
+ write("\n")
+ }
+ }
+
+ return n, err
+}
--- /dev/null
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "io/fs"
+ "os"
+ "path"
+ "twitchchat/irc"
+)
+
+var prototype = flag.String("proto", "", "The prototype of the tags + message as a valid IRC message")
+
+func flags() {
+ flag.Usage = func() {
+ fmt.Fprintf(flag.CommandLine.Output(), "%s: generate code from a twitch message prototype\n", os.Args[0])
+ fmt.Fprintln(flag.CommandLine.Output(), "Usage:")
+ flag.PrintDefaults()
+ }
+
+ flag.Parse()
+ if *prototype == "" {
+ flag.Usage()
+ os.Exit(-1)
+ }
+}
+
+func main() {
+ flags()
+
+ msg, err := irc.ParseBytes([]byte(*prototype))
+ if err != nil {
+ fmt.Printf("Invalid prototype message: %s\n", err)
+ os.Exit(-1)
+ }
+
+ dir := convertCommandToDir(string(msg.Command))
+
+ if err := os.Mkdir(dir, fs.ModeDir|0755); err != nil {
+ if !errors.Is(err, fs.ErrExist) {
+ fmt.Printf("Could now create a directory: %s\n", err)
+ os.Exit(-1)
+ }
+ }
+
+ file, err := os.Create(path.Join(dir, "message.go"))
+ if err != nil {
+ fmt.Printf("Could not write out: %s\n", err)
+ os.Exit(-1)
+ }
+
+ generateMessage(msg, file)
+ fmt.Printf("Generated %s\n", msg)
+}