func (c *Client) Run() error {
	return c.RunContext(context.TODO())
}
Run starts the main loop for this IRC connection. Note that it may break in
strange and unexpected ways if it is called again before the first connection
exits.
package irc
import (
	"context"
	"errors"
	"fmt"
	"io"
	"sync"
	"time"
)
type ClientConfig struct {
	Nick string
	Pass string
	User string
	Name string
	PingFrequency time.Duration
	PingTimeout   time.Duration
	SendLimit time.Duration
	SendBurst int
	Handler Handler
}
type cap struct {
	Requested bool
	Required bool
	Enabled bool
	Available bool
}
type Client struct {
	*Conn
	rwc    io.ReadWriteCloser
	config ClientConfig
	currentNick           string
	limiter               chan struct{}
	incomingPongChan      chan string
	errChan               chan error
	caps                  map[string]cap
	remainingCapResponses int
	connected             bool
}
func NewClient(rwc io.ReadWriteCloser, config ClientConfig) *Client {
	c := &Client{
		Conn:    NewConn(rwc),
		rwc:     rwc,
		config:  config,
		errChan: make(chan error, 1),
		caps:    make(map[string]cap),
	}
	c.Conn.Writer.writeCallback = c.writeCallback
	return c
}
func (c *Client) writeCallback(w *Writer, line string) error {
	if c.limiter != nil {
		<-c.limiter
	}
	_, err := w.writer.Write([]byte(line + "\r\n"))
	if err != nil {
		c.sendError(err)
	}
	return err
}
func (c *Client) maybeStartLimiter(wg *sync.WaitGroup, exiting chan struct{}) {
	if c.config.SendLimit == 0 {
		return
	}
	wg.Add(1)
	c.limiter = make(chan struct{}, c.config.SendBurst)
	limitTick := time.NewTicker(c.config.SendLimit)
	go func() {
		defer wg.Done()
		var done bool
		for !done {
			select {
			case <-limitTick.C:
				select {
				case c.limiter <- struct{}{}:
				default:
				}
			case <-exiting:
				done = true
			}
		}
		limitTick.Stop()
		close(c.limiter)
		c.limiter = nil
	}()
}
func (c *Client) maybeStartPingLoop(wg *sync.WaitGroup, exiting chan struct{}) {
	if c.config.PingFrequency <= 0 {
		return
	}
	wg.Add(1)
	c.incomingPongChan = make(chan string, 5)
	go func() {
		defer wg.Done()
		pingHandlers := make(map[string]chan struct{})
		ticker := time.NewTicker(c.config.PingFrequency)
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				timestamp := time.Now().Unix()
				pongChan := make(chan struct{}, 1)
				pingHandlers[fmt.Sprintf("%d", timestamp)] = pongChan
				wg.Add(1)
				go c.handlePing(timestamp, pongChan, wg, exiting)
			case data := <-c.incomingPongChan:
				c := pingHandlers[data]
				delete(pingHandlers, data)
				if c != nil {
					c <- struct{}{}
				}
			case <-exiting:
				return
			}
		}
	}()
}
func (c *Client) handlePing(timestamp int64, pongChan chan struct{}, wg *sync.WaitGroup, exiting chan struct{}) {
	defer wg.Done()
	c.Writef("PING :%d", timestamp)
	timer := time.NewTimer(c.config.PingTimeout)
	defer timer.Stop()
	select {
	case <-timer.C:
		c.sendError(errors.New("ping timeout"))
	case <-pongChan:
		return
	case <-exiting:
		return
	}
}
func (c *Client) maybeStartCapHandshake() {
	if len(c.caps) == 0 {
		return
	}
	c.Write("CAP LS")
	c.remainingCapResponses = 1  
	for key, cap := range c.caps {
		if cap.Requested {
			c.Writef("CAP REQ :%s", key)
			c.remainingCapResponses++
		}
	}
}
func (c *Client) CapRequest(capName string, required bool) {
	cap := c.caps[capName]
	cap.Requested = true
	cap.Required = cap.Required || required
	c.caps[capName] = cap
}
func (c *Client) CapEnabled(capName string) bool {
	return c.caps[capName].Enabled
}
func (c *Client) CapAvailable(capName string) bool {
	return c.caps[capName].Available
}
func (c *Client) sendError(err error) {
	select {
	case c.errChan <- err:
	default:
	}
}
func (c *Client) startReadLoop(wg *sync.WaitGroup, exiting chan struct{}) {
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-exiting:
				return
			default:
				m, err := c.ReadMessage()
				if err != nil {
					c.sendError(err)
					break
				}
				if f, ok := clientFilters[m.Command]; ok {
					f(c, m)
				}
				if c.config.Handler != nil {
					c.config.Handler.Handle(c, m)
				}
			}
		}
	}()
}