func (c *Client) SetIRCToken(ircToken string) {
	c.ircToken = ircToken
}
SetIRCToken updates the oauth token for this client used for authentication
This will not cause a reconnect, but is meant more for "on next connect, use this new token" in case the old token has expired
package twitch
import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/textproto"
	"strings"
	"sync"
	"time"
)
const (
	ircTwitchTLS = "irc.chat.twitch.tv:6697"
	ircTwitch    = "irc.chat.twitch.tv:6667"
	pingSignature = "go-twitch-irc"
	pingMessage   = "PING :" + pingSignature
	TagsCapability = "twitch.tv/tags"
	CommandsCapability = "twitch.tv/commands"
	MembershipCapability = "twitch.tv/membership"
)
var (
	ErrClientDisconnected = errors.New("client called Disconnect()")
	ErrLoginAuthenticationFailed = errors.New("login authentication failed")
	ErrConnectionIsNotOpen = errors.New("connection is not open")
	WriteBufferSize = 512
	ReadBufferSize = 64
	DefaultCapabilities = []string{TagsCapability, CommandsCapability, MembershipCapability}
)
var (
	errReconnect = errors.New("reconnect")
)
type User struct {
	ID          string
	Name        string
	DisplayName string
	Color       string
	Badges      map[string]int
}
type Message interface {
	GetType() MessageType
}
type RawMessage struct {
	Raw     string
	Type    MessageType
	RawType string
	Tags    map[string]string
	Message string
}
func (msg *RawMessage) GetType() MessageType {
	return msg.Type
}
type WhisperMessage struct {
	User User
	Raw       string
	Type      MessageType
	RawType   string
	Tags      map[string]string
	Message   string
	Target    string
	MessageID string
	ThreadID  string
	Emotes    []*Emote
	Action    bool
}
func (msg *WhisperMessage) GetType() MessageType {
	return msg.Type
}
type PrivateMessage struct {
	User User
	Raw     string
	Type    MessageType
	RawType string
	Tags    map[string]string
	Message string
	Channel string
	RoomID  string
	ID      string
	Time    time.Time
	Emotes  []*Emote
	Bits    int
	Action  bool
}
func (msg *PrivateMessage) GetType() MessageType {
	return msg.Type
}
type ClearChatMessage struct {
	Raw            string
	Type           MessageType
	RawType        string
	Tags           map[string]string
	Message        string
	Channel        string
	RoomID         string
	Time           time.Time
	BanDuration    int
	TargetUserID   string
	TargetUsername string
}
func (msg *ClearChatMessage) GetType() MessageType {
	return msg.Type
}
type ClearMessage struct {
	Raw         string
	Type        MessageType
	RawType     string
	Tags        map[string]string
	Message     string
	Channel     string
	Login       string
	TargetMsgID string
}
func (msg *ClearMessage) GetType() MessageType {
	return msg.Type
}
type RoomStateMessage struct {
	Raw     string
	Type    MessageType
	RawType string
	Tags    map[string]string
	Message string
	Channel string
	RoomID  string
	State   map[string]int
}
func (msg *RoomStateMessage) GetType() MessageType {
	return msg.Type
}
type UserNoticeMessage struct {
	User User
	Raw       string
	Type      MessageType
	RawType   string
	Tags      map[string]string
	Message   string
	Channel   string
	RoomID    string
	ID        string
	Time      time.Time
	Emotes    []*Emote
	MsgID     string
	MsgParams map[string]string
	SystemMsg string
}
func (msg *UserNoticeMessage) GetType() MessageType {
	return msg.Type
}
type UserStateMessage struct {
	User User
	Raw       string
	Type      MessageType
	RawType   string
	Tags      map[string]string
	Message   string
	Channel   string
	EmoteSets []string
}
func (msg *UserStateMessage) GetType() MessageType {
	return msg.Type
}
type NoticeMessage struct {
	Raw     string
	Type    MessageType
	RawType string
	Tags    map[string]string
	Message string
	Channel string
	MsgID   string
}
func (msg *NoticeMessage) GetType() MessageType {
	return msg.Type
}
type UserJoinMessage struct {
	Raw     string
	Type    MessageType
	RawType string
	Channel string
	User string
}
func (msg *UserJoinMessage) GetType() MessageType {
	return msg.Type
}
type UserPartMessage struct {
	Raw     string
	Type    MessageType
	RawType string
	Channel string
	User string
}
func (msg *UserPartMessage) GetType() MessageType {
	return msg.Type
}
type GlobalUserStateMessage struct {
	User User
	Raw       string
	Type      MessageType
	RawType   string
	Tags      map[string]string
	EmoteSets []string
}
func (msg *GlobalUserStateMessage) GetType() MessageType {
	return msg.Type
}
type ReconnectMessage struct {
	Raw     string
	Type    MessageType
	RawType string
}
func (msg *ReconnectMessage) GetType() MessageType {
	return msg.Type
}
type NamesMessage struct {
	Raw     string
	Type    MessageType
	RawType string
	Channel string
	Users []string
}
func (msg *NamesMessage) GetType() MessageType {
	return msg.Type
}
type PingMessage struct {
	Raw     string
	Type    MessageType
	RawType string
	Message string
}
func (msg *PingMessage) GetType() MessageType {
	return msg.Type
}
type PongMessage struct {
	Raw     string
	Type    MessageType
	RawType string
	Message string
}
func (msg *PongMessage) GetType() MessageType {
	return msg.Type
}
type Client struct {
	IrcAddress               string
	ircUser                  string
	ircToken                 string
	TLS                      bool
	connActive               tAtomBool
	channels                 map[string]bool
	channelUserlistMutex     *sync.RWMutex
	channelUserlist          map[string]map[string]bool
	channelsMtx              *sync.RWMutex
	onConnect                func()
	onWhisperMessage         func(message WhisperMessage)
	onPrivateMessage         func(message PrivateMessage)
	onClearChatMessage       func(message ClearChatMessage)
	onRoomStateMessage       func(message RoomStateMessage)
	onClearMessage           func(message ClearMessage)
	onUserNoticeMessage      func(message UserNoticeMessage)
	onUserStateMessage       func(message UserStateMessage)
	onGlobalUserStateMessage func(message GlobalUserStateMessage)
	onNoticeMessage          func(message NoticeMessage)
	onUserJoinMessage        func(message UserJoinMessage)
	onUserPartMessage        func(message UserPartMessage)
	onReconnectMessage       func(message ReconnectMessage)
	onNamesMessage           func(message NamesMessage)
	onPingMessage            func(message PingMessage)
	onPongMessage            func(message PongMessage)
	onUnsetMessage           func(message RawMessage)
	onPingSent func()
	read chan string
	write chan string
	clientReconnect chanCloser
	userDisconnect chanCloser
	pongReceived chan bool
	messageReceived chan bool
	SendPings bool
	IdlePingInterval time.Duration
	PongTimeout time.Duration
	SetupCmd string
	Capabilities []string
	rateLimiter *RateLimiter
}
func NewClient(username, oauth string) *Client {
	return &Client{
		ircUser:         username,
		ircToken:        oauth,
		TLS:             true,
		channels:        map[string]bool{},
		channelUserlist: map[string]map[string]bool{},
		channelsMtx:     &sync.RWMutex{},
		messageReceived: make(chan bool),
		read:  make(chan string, ReadBufferSize),
		write: make(chan string, WriteBufferSize),
		SendPings:        true,
		IdlePingInterval: time.Second * 15,
		PongTimeout:      time.Second * 5,
		channelUserlistMutex: &sync.RWMutex{},
		Capabilities: DefaultCapabilities,
		rateLimiter: CreateDefaultRateLimiter(),
	}
}
func NewAnonymousClient() *Client {
	return NewClient("justinfan123123", "oauth:59301")
}
func (c *Client) OnConnect(callback func()) {
	c.onConnect = callback
}
func (c *Client) OnWhisperMessage(callback func(message WhisperMessage)) {
	c.onWhisperMessage = callback
}
func (c *Client) OnPrivateMessage(callback func(message PrivateMessage)) {
	c.onPrivateMessage = callback
}
func (c *Client) OnClearChatMessage(callback func(message ClearChatMessage)) {
	c.onClearChatMessage = callback
}
func (c *Client) OnClearMessage(callback func(message ClearMessage)) {
	c.onClearMessage = callback
}
func (c *Client) OnRoomStateMessage(callback func(message RoomStateMessage)) {
	c.onRoomStateMessage = callback
}
func (c *Client) OnUserNoticeMessage(callback func(message UserNoticeMessage)) {
	c.onUserNoticeMessage = callback
}
func (c *Client) OnUserStateMessage(callback func(message UserStateMessage)) {
	c.onUserStateMessage = callback
}
func (c *Client) OnGlobalUserStateMessage(callback func(message GlobalUserStateMessage)) {
	c.onGlobalUserStateMessage = callback
}
func (c *Client) OnNoticeMessage(callback func(message NoticeMessage)) {
	c.onNoticeMessage = callback
}
func (c *Client) OnUserJoinMessage(callback func(message UserJoinMessage)) {
	c.onUserJoinMessage = callback
}
func (c *Client) OnUserPartMessage(callback func(message UserPartMessage)) {
	c.onUserPartMessage = callback
}
func (c *Client) OnReconnectMessage(callback func(message ReconnectMessage)) {
	c.onReconnectMessage = callback
}
func (c *Client) OnNamesMessage(callback func(message NamesMessage)) {
	c.onNamesMessage = callback
}
func (c *Client) OnPingMessage(callback func(message PingMessage)) {
	c.onPingMessage = callback
}
func (c *Client) OnPongMessage(callback func(message PongMessage)) {
	c.onPongMessage = callback
}
func (c *Client) OnUnsetMessage(callback func(message RawMessage)) {
	c.onUnsetMessage = callback
}
func (c *Client) OnPingSent(callback func()) {
	c.onPingSent = callback
}
func (c *Client) Say(channel, text string) {
	channel = strings.ToLower(channel)
	c.send(fmt.Sprintf("PRIVMSG #%s :%s", channel, text))
}
func (c *Client) Whisper(username, text string) {
	c.send(fmt.Sprintf("PRIVMSG #%s :/w %s %s", c.ircUser, username, text))
}
func (c *Client) Join(channels ...string) {
	messages, joined := createJoinMessages(c.channels, channels...)
	c.channelsMtx.Lock()
	for _, message := range messages {
		if c.connActive.get() {
			c.send(message)
		}
	}
	for _, channel := range joined {
		c.channels[channel] = c.connActive.get()
		c.channelUserlistMutex.Lock()
		c.channelUserlist[channel] = map[string]bool{}
		c.channelUserlistMutex.Unlock()
	}
	c.channelsMtx.Unlock()
}
func (c *Client) FollowersOn(channel, duration string) {
	c.Say(channel, "/followers "+duration)
}
func (c *Client) FollowersOff(channel string) {
	c.Say(channel, "/followersoff")
}
func createJoinMessages(joinedChannels map[string]bool, channels ...string) ([]string, []string) {
	baseMessage := "JOIN"
	joinMessages := []string{}
	joined := []string{}
	if channels == nil || len(channels) < 1 {
		return joinMessages, joined
	}
	sb := strings.Builder{}
	sb.WriteString(baseMessage)
	for _, channel := range channels {
		channel = strings.ToLower(channel)
		if joinedChannels[channel] {
			continue
		}
		if sb.Len()+len(channel)+2 > maxMessageLength {
			joinMessages = append(joinMessages, sb.String())
			sb.Reset()
			sb.WriteString(baseMessage)
		}
		if sb.Len() == len(baseMessage) {
			sb.WriteString(" #" + channel)
		} else {
			sb.WriteString(",#" + channel)
		}
		joined = append(joined, channel)
	}
	joinMessages = append(joinMessages, sb.String())
	return joinMessages, joined
}
func (c *Client) Depart(channel string) {
	if c.connActive.get() {
		c.send(fmt.Sprintf("PART #%s", channel))
	}
	c.channelsMtx.Lock()
	delete(c.channels, channel)
	c.channelUserlistMutex.Lock()
	delete(c.channelUserlist, channel)
	c.channelUserlistMutex.Unlock()
	c.channelsMtx.Unlock()
}
func (c *Client) Disconnect() error {
	if !c.connActive.get() {
		return ErrConnectionIsNotOpen
	}
	c.userDisconnect.Close()
	return nil
}
func (c *Client) Connect() error {
	if c.IrcAddress == "" && c.TLS {
		c.IrcAddress = ircTwitchTLS
	} else if c.IrcAddress == "" && !c.TLS {
		c.IrcAddress = ircTwitch
	}
	dialer := &net.Dialer{
		KeepAlive: time.Second * 10,
	}
	var conf *tls.Config
	if strings.HasPrefix(c.IrcAddress, "127.0.0.1:") {
		conf = &tls.Config{
			InsecureSkipVerify: true,
		}
	} else {
		conf = &tls.Config{}
	}
	for {
		err := c.makeConnection(dialer, conf)
		switch err {
		case errReconnect:
			continue
		default:
			return err
		}
	}
}
func (c *Client) makeConnection(dialer *net.Dialer, conf *tls.Config) (err error) {
	c.connActive.set(false)
	var conn net.Conn
	if c.TLS {
		conn, err = tls.DialWithDialer(dialer, "tcp", c.IrcAddress, conf)
	} else {
		conn, err = dialer.Dial("tcp", c.IrcAddress)
	}
	if err != nil {
		return
	}
	wg := sync.WaitGroup{}
	c.clientReconnect.Reset()
	c.userDisconnect.Reset()
	wg.Add(1)
	go c.startReader(conn, &wg)
	go c.rateLimiter.Start()
	if c.SendPings {
		wg.Add(1)
		c.startPinger(conn, &wg)
	}
	c.setupConnection(conn)
	wg.Add(1)
	go c.startWriter(conn, &wg)
	err = c.startParser()
	conn.Close()
	c.clientReconnect.Close()
	wg.Wait()
	return
}
func (c *Client) Userlist(channel string) ([]string, error) {
	c.channelUserlistMutex.RLock()
	defer c.channelUserlistMutex.RUnlock()
	usermap, ok := c.channelUserlist[channel]
	if !ok || usermap == nil {
		return nil, fmt.Errorf("could not find userlist for channel '%s' in client", channel)
	}
	userlist := make([]string, len(usermap))
	i := 0
	for key := range usermap {
		userlist[i] = key
		i++
	}
	return userlist, nil
}