--- /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
+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
+}
 
        // twitch specific IRC messages. These are all receive only
        ClearChat       = "CLEARCHAT"
        ClearMsg        = "CLEARMSG"
-       GlobalUserStare = "GLOBALUSERSTATE"
+       GlobalUserState = "GLOBALUSERSTATE"
        HostTarget      = "HOSTTARGET"
        Reconnect       = "RECONNECT"
        RoomState       = "ROOMSTATE"
 
--- /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
+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
+       }
+}
 
        }
        return nil
 }
+
+func (m Message) Channel() string {
+       if len(m.Params.Params) == 0 {
+               return ""
+       }
+       if m.Params.Params[0] != "#" {
+               return ""
+       }
+       return m.Params.Params[0][1:]
+}
 
--- /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"
+)
 
        "twitchchat/twitch/core/messages"
 )
 
-type PrivMsg messages.Message
-
 //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) GetTag(key PrivMsgTag) *irc.Tag {
-       return messages.Message(p).GetTag(string(key))
+       return p.Message.GetTag(string(key))
 }
 
--- /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
+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
+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
+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"
+)
 
        rmax := NewRunningMax(50)
        for m := range msgs {
                if privmsg.IsPrivateMessage(m) {
-                       priv := privmsg.PrivMsg(m)
+                       priv := privmsg.PrivMsg{m}
                        u := priv.User()
                        mx := rmax.Push(len(u))
                        format = fmt.Sprintf("%%%ds: %%s\n", mx)
 
                return
        }
 
-       f, err := os.OpenFile(*outFile, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0755)
+       f, err := os.OpenFile(*outFile, os.O_WRONLY|os.O_CREATE, 0755)
        if err != nil {
                fmt.Fprintln(os.Stderr, err)
        }