<commit_before>// Copyright 2009 <NAME> <<EMAIL>>  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
This package provides an event based IRC client library. It allows to
register callbacks for the events you need to handle. Its features
include handling standard CTCP, reconnecting on errors and detecting
stones servers.
Details of the IRC protocol can be found in the following RFCs:
https://tools.ietf.org/html/rfc1459
https://tools.ietf.org/html/rfc2810
https://tools.ietf.org/html/rfc2811
https://tools.ietf.org/html/rfc2812
https://tools.ietf.org/html/rfc2813
The details of the client-to-client protocol (CTCP) can be found here: http://www.irchelp.org/irchelp/rfc/ctcpspec.html
*/

package irc

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"

	"golang.org/x/text/encoding"
)

const (
	VERSION = "go-ircevent v2.1"
)

const CAP_TIMEOUT = time.Second * 15

var ErrDisconnected = errors.New("Disconnect Called")

// Read data from a connection. To be used as a goroutine.
func (irc *Connection) readLoop() {
	defer irc.Done()
	r := irc.Encoding.NewDecoder().Reader(irc.socket)
	br := bufio.NewReaderSize(r, 512)

	errChan := irc.ErrorChan()

	for {
		select {
		case <-irc.end:
			return
		default:
			// Set a read deadline based on the combined timeout and ping frequency
			// We should ALWAYS have received a response from the server within the timeout
			// after our own pings
			if irc.socket != nil {
				irc.socket.SetReadDeadline(time.Now().Add(irc.Timeout + irc.PingFreq))
			}

			msg, err := br.ReadString('\n')

			// We got past our blocking read, so bin timeout
			if irc.socket != nil {
				var zero time.Time
				irc.socket.SetReadDeadline(zero)
			}

			if err != nil {
				errChan <- err
				return
			}

			if irc.Debug {
				irc.Log.Printf("<-- %s\n", strings.TrimSpace(msg))
			}

			irc.lastMessageMutex.Lock()
			irc.lastMessage = time.Now()
			irc.lastMessageMutex.Unlock()
			event, err := parseToEvent(msg)
			if err == nil {
				event.Connection = irc
				irc.RunCallbacks(event)
			}
		}
	}
}

// Unescape tag values as defined in the IRCv3.2 message tags spec
// http://ircv3.net/specs/core/message-tags-3.2.html
func unescapeTagValue(value string) string {
	value = strings.Replace(value, "\\:", ";", -1)
	value = strings.Replace(value, "\\s", " ", -1)
	value = strings.Replace(value, "\\\\", "\\", -1)
	value = strings.Replace(value, "\\r", "\r", -1)
	value = strings.Replace(value, "\\n", "\n", -1)
	return value
}

//Parse raw irc messages
func parseToEvent(msg string) (*Event, error) {
	msg = strings.TrimSuffix(msg, "\n") //Remove \r\n
	msg = strings.TrimSuffix(msg, "\r")
	event := &Event{Raw: msg}
	if len(msg) < 5 {
		return nil, errors.New("Malformed msg from server")
	}

	if msg[0] == '@' {
		// IRCv3 Message Tags
		if i := strings.Index(msg, " "); i > -1 {
			event.Tags = make(map[string]string)
			tags := strings.Split(msg[1:i], ";")
			for _, data := range tags {
				parts := strings.SplitN(data, "=", 2)
				if len(parts) == 1 {
					event.Tags[parts[0]] = ""
				} else {
					event.Tags[parts[0]] = unescapeTagValue(parts[1])
				}
			}
			msg = msg[i+1:]
		} else {
			return nil, errors.New("Malformed msg from server")
		}
	}

	if msg[0] == ':' {
		if i := strings.Index(msg, " "); i > -1 {
			event.Source = msg[1:i]
			msg = msg[i+1:]

		} else {
			return nil, errors.New("Malformed msg from server")
		}

		if i, j := strings.Index(event.Source, "!"), strings.Index(event.Source, "@"); i > -1 && j > -1 && i < j {
			event.Nick = event.Source[0:i]
			event.User = event.Source[i+1 : j]
			event.Host = event.Source[j+1 : len(event.Source)]
		}
	}

	split := strings.SplitN(msg, " :", 2)
	args := strings.Split(split[0], " ")
	event.Code = strings.ToUpper(args[0])
	event.Arguments = args[1:]
	if len(split) > 1 {
		event.Arguments = append(event.Arguments, split[1])
	}
	return event, nil

}

// Loop to write to a connection. To be used as a goroutine.
func (irc *Connection) writeLoop() {
	defer irc.Done()
	w := irc.Encoding.NewEncoder().Writer(irc.socket)
	errChan := irc.ErrorChan()
	for {
		select {<commit_msg>`go mod vendor`<commit_after>// Copyright 2009 <NAME> <<EMAIL>>  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
This package provides an event based IRC client library. It allows to
register callbacks for the events you need to handle. Its features
include handling standard CTCP, reconnecting on errors and detecting
stones servers.
Details of the IRC protocol can be found in the following RFCs:
https://tools.ietf.org/html/rfc1459
https://tools.ietf.org/html/rfc2810
https://tools.ietf.org/html/rfc2811
https://tools.ietf.org/html/rfc2812
https://tools.ietf.org/html/rfc2813
The details of the client-to-client protocol (CTCP) can be found here: http://www.irchelp.org/irchelp/rfc/ctcpspec.html
*/

package irc

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"

	"golang.org/x/text/encoding"
)

const (
	VERSION = "go-ircevent v2.1"
)

const CAP_TIMEOUT = time.Second * 15

var ErrDisconnected = errors.New("Disconnect Called")

// Read data from a connection. To be used as a goroutine.
func (irc *Connection) readLoop() {
	defer irc.Done()
	r := irc.Encoding.NewDecoder().Reader(irc.socket)
	br := bufio.NewReaderSize(r, 512)

	errChan := irc.ErrorChan()

	for {
		select {
		case <-irc.end:
			return
		default:
			// Set a read deadline based on the combined timeout and ping frequency
			// We should ALWAYS have received a response from the server within the timeout
			// after our own pings
			if irc.socket != nil {
				irc.socket.SetReadDeadline(time.Now().Add(irc.Timeout + irc.PingFreq))
			}

			msg, err := br.ReadString('\n')

			// We got past our blocking read, so bin timeout
			if irc.socket != nil {
				var zero time.Time
				irc.socket.SetReadDeadline(zero)
			}

			if err != nil {
				errChan <- err
				return
			}

			if irc.Debug {
				irc.Log.Printf("<-- %s\n", strings.TrimSpace(msg))
			}

			irc.lastMessageMutex.Lock()
			irc.lastMessage = time.Now()
			irc.lastMessageMutex.Unlock()
			event, err := parseToEvent(msg)
			if err == nil {
				event.Connection = irc
				irc.RunCallbacks(event)
			}
		}
	}
}

// Unescape tag values as defined in the IRCv3.2 message tags spec
// http://ircv3.net/specs/core/message-tags-3.2.html
func unescapeTagValue(value string) string {
	value = strings.Replace(value, "\\:", ";", -1)
	value = strings.Replace(value, "\\s", " ", -1)
	value = strings.Replace(value, "\\\\", "\\", -1)
	value = strings.Replace(value, "\\r", "\r", -1)
	value = strings.Replace(value, "\\n", "\n", -1)
	return value
}

//Parse raw irc messages
func parseToEvent(msg string) (*Event, error) {
	msg = strings.TrimSuffix(msg, "\n") //Remove \r\n
	msg = strings.TrimSuffix(msg, "\r")
	event := &Event{Raw: msg}
	if len(msg) < 5 {
		return nil, errors.New("Malformed msg from server")
	}

	if msg[0] == '@' {
		// IRCv3 Message Tags
		if i := strings.Index(msg, " "); i > -1 {
			event.Tags = make(map[string]string)
			tags := strings.Split(msg[1:i], ";")
			for _, data := range tags {
				parts := strings.SplitN(data, "=", 2)
				if len(parts) == 1 {
					event.Tags[parts[0]] = ""
				} else {
					event.Tags[parts[0]] = unescapeTagValue(parts[1])
				}
			}
			msg = msg[i+1 : len(msg)]
		} else {
			return nil, errors.New("Malformed msg from server")
		}
	}

	if msg[0] == ':' {
		if i := strings.Index(msg, " "); i > -1 {
			event.Source = msg[1:i]
			msg = msg[i+1 : len(msg)]

		} else {
			return nil, errors.New("Malformed msg from server")
		}

		if i, j := strings.Index(event.Source, "!"), strings.Index(event.Source, "@"); i > -1 && j > -1 && i < j {
			event.Nick = event.Source[0:i]
			event.User = event.Source[i+1 : j]
			event.Host = event.Source[j+1 : len(event.Source)]
		}
	}

	split := strings.SplitN(msg, " :", 2)
	args := strings.Split(split[0], " ")
	event.Code = strings.ToUpper(args[0])
	event.Arguments = args[1:]
	if len(split) > 1 {
		event.Arguments = append(event.Arguments, split[1])
	}
	return event, nil

}

// Loop to write to a connection. To be used as a goroutine.
func (irc *Connection) writeLoop() {
	defer irc.Done()
	w := irc.Encoding.NewEncoder().Writer(irc.socket)
	errChan := irc.ErrorChan()
	for {
		select {
{'fraction_non_alphanumeric': 0.16033057851239668, 'fraction_numerical': 0.015820543093270367, 'mean_word_length': 3.1463534018600097, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 19, 'https://': 10, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4657594', 'n_tokens_mistral': 3459, 'n_tokens_neox': 3047, 'n_words': 818}