summaryrefslogtreecommitdiff
path: root/vendor/github.com/fluffle/goirc/client
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/fluffle/goirc/client')
-rw-r--r--vendor/github.com/fluffle/goirc/client/commands.go304
-rw-r--r--vendor/github.com/fluffle/goirc/client/connection.go587
-rw-r--r--vendor/github.com/fluffle/goirc/client/dispatch.go202
-rw-r--r--vendor/github.com/fluffle/goirc/client/doc.go34
-rw-r--r--vendor/github.com/fluffle/goirc/client/handlers.go105
-rw-r--r--vendor/github.com/fluffle/goirc/client/line.go216
-rw-r--r--vendor/github.com/fluffle/goirc/client/state_handlers.go262
7 files changed, 0 insertions, 1710 deletions
diff --git a/vendor/github.com/fluffle/goirc/client/commands.go b/vendor/github.com/fluffle/goirc/client/commands.go
deleted file mode 100644
index 101c7d3..0000000
--- a/vendor/github.com/fluffle/goirc/client/commands.go
+++ /dev/null
@@ -1,304 +0,0 @@
-package client
-
-import (
- "fmt"
- "strings"
-)
-
-const (
- REGISTER = "REGISTER"
- CONNECTED = "CONNECTED"
- DISCONNECTED = "DISCONNECTED"
- ACTION = "ACTION"
- AWAY = "AWAY"
- CAP = "CAP"
- CTCP = "CTCP"
- CTCPREPLY = "CTCPREPLY"
- ERROR = "ERROR"
- INVITE = "INVITE"
- JOIN = "JOIN"
- KICK = "KICK"
- MODE = "MODE"
- NICK = "NICK"
- NOTICE = "NOTICE"
- OPER = "OPER"
- PART = "PART"
- PASS = "PASS"
- PING = "PING"
- PONG = "PONG"
- PRIVMSG = "PRIVMSG"
- QUIT = "QUIT"
- TOPIC = "TOPIC"
- USER = "USER"
- VERSION = "VERSION"
- VHOST = "VHOST"
- WHO = "WHO"
- WHOIS = "WHOIS"
- defaultSplit = 450
-)
-
-// cutNewLines() pares down a string to the part before the first "\r" or "\n".
-func cutNewLines(s string) string {
- r := strings.SplitN(s, "\r", 2)
- r = strings.SplitN(r[0], "\n", 2)
- return r[0]
-}
-
-// indexFragment looks for the last sentence split-point (defined as one of
-// the punctuation characters .:;,!?"' followed by a space) in the string s
-// and returns the index in the string after that split-point. If no split-
-// point is found it returns the index after the last space in s, or -1.
-func indexFragment(s string) int {
- max := -1
- for _, sep := range []string{". ", ": ", "; ", ", ", "! ", "? ", "\" ", "' "} {
- if idx := strings.LastIndex(s, sep); idx > max {
- max = idx
- }
- }
- if max > 0 {
- return max + 2
- }
- if idx := strings.LastIndex(s, " "); idx > 0 {
- return idx + 1
- }
- return -1
-}
-
-// splitMessage splits a message > splitLen chars at:
-// 1. the end of the last sentence fragment before splitLen
-// 2. the end of the last word before splitLen
-// 3. splitLen itself
-func splitMessage(msg string, splitLen int) (msgs []string) {
- // This is quite short ;-)
- if splitLen < 13 {
- splitLen = defaultSplit
- }
- for len(msg) > splitLen {
- idx := indexFragment(msg[:splitLen-3])
- if idx < 0 {
- idx = splitLen - 3
- }
- msgs = append(msgs, msg[:idx]+"...")
- msg = msg[idx:]
- }
- return append(msgs, msg)
-}
-
-// Raw sends a raw line to the server, should really only be used for
-// debugging purposes but may well come in handy.
-func (conn *Conn) Raw(rawline string) {
- // Avoid command injection by enforcing one command per line.
- conn.out <- cutNewLines(rawline)
-}
-
-// Pass sends a PASS command to the server.
-// PASS password
-func (conn *Conn) Pass(password string) { conn.Raw(PASS + " " + password) }
-
-// Nick sends a NICK command to the server.
-// NICK nick
-func (conn *Conn) Nick(nick string) { conn.Raw(NICK + " " + nick) }
-
-// User sends a USER command to the server.
-// USER ident 12 * :name
-func (conn *Conn) User(ident, name string) {
- conn.Raw(USER + " " + ident + " 12 * :" + name)
-}
-
-// Join sends a JOIN command to the server with an optional key.
-// JOIN channel [key]
-func (conn *Conn) Join(channel string, key ...string) {
- k := ""
- if len(key) > 0 {
- k = " " + key[0]
- }
- conn.Raw(JOIN + " " + channel + k)
-}
-
-// Part sends a PART command to the server with an optional part message.
-// PART channel [:message]
-func (conn *Conn) Part(channel string, message ...string) {
- msg := strings.Join(message, " ")
- if msg != "" {
- msg = " :" + msg
- }
- conn.Raw(PART + " " + channel + msg)
-}
-
-// Kick sends a KICK command to remove a nick from a channel.
-// KICK channel nick [:message]
-func (conn *Conn) Kick(channel, nick string, message ...string) {
- msg := strings.Join(message, " ")
- if msg != "" {
- msg = " :" + msg
- }
- conn.Raw(KICK + " " + channel + " " + nick + msg)
-}
-
-// Quit sends a QUIT command to the server with an optional quit message.
-// QUIT [:message]
-func (conn *Conn) Quit(message ...string) {
- msg := strings.Join(message, " ")
- if msg == "" {
- msg = conn.cfg.QuitMessage
- }
- conn.Raw(QUIT + " :" + msg)
-}
-
-// Whois sends a WHOIS command to the server.
-// WHOIS nick
-func (conn *Conn) Whois(nick string) { conn.Raw(WHOIS + " " + nick) }
-
-// Who sends a WHO command to the server.
-// WHO nick
-func (conn *Conn) Who(nick string) { conn.Raw(WHO + " " + nick) }
-
-// Privmsg sends a PRIVMSG to the target nick or channel t.
-// If msg is longer than Config.SplitLen characters, multiple PRIVMSGs
-// will be sent to the target containing sequential parts of msg.
-// PRIVMSG t :msg
-func (conn *Conn) Privmsg(t, msg string) {
- prefix := PRIVMSG + " " + t + " :"
- for _, s := range splitMessage(msg, conn.cfg.SplitLen) {
- conn.Raw(prefix + s)
- }
-}
-
-// Privmsgln is the variadic version of Privmsg that formats the message
-// that is sent to the target nick or channel t using the
-// fmt.Sprintln function.
-// Note: Privmsgln doesn't add the '\n' character at the end of the message.
-func (conn *Conn) Privmsgln(t string, a ...interface{}) {
- msg := fmt.Sprintln(a...)
- // trimming the new-line character added by the fmt.Sprintln function,
- // since it's irrelevant.
- msg = msg[:len(msg)-1]
- conn.Privmsg(t, msg)
-}
-
-// Privmsgf is the variadic version of Privmsg that formats the message
-// that is sent to the target nick or channel t using the
-// fmt.Sprintf function.
-func (conn *Conn) Privmsgf(t, format string, a ...interface{}) {
- msg := fmt.Sprintf(format, a...)
- conn.Privmsg(t, msg)
-}
-
-// Notice sends a NOTICE to the target nick or channel t.
-// If msg is longer than Config.SplitLen characters, multiple NOTICEs
-// will be sent to the target containing sequential parts of msg.
-// NOTICE t :msg
-func (conn *Conn) Notice(t, msg string) {
- for _, s := range splitMessage(msg, conn.cfg.SplitLen) {
- conn.Raw(NOTICE + " " + t + " :" + s)
- }
-}
-
-// Ctcp sends a (generic) CTCP message to the target nick
-// or channel t, with an optional argument.
-// PRIVMSG t :\001CTCP arg\001
-func (conn *Conn) Ctcp(t, ctcp string, arg ...string) {
- // We need to split again here to ensure
- for _, s := range splitMessage(strings.Join(arg, " "), conn.cfg.SplitLen) {
- if s != "" {
- s = " " + s
- }
- // Using Raw rather than PRIVMSG here to avoid double-split problems.
- conn.Raw(PRIVMSG + " " + t + " :\001" + strings.ToUpper(ctcp) + s + "\001")
- }
-}
-
-// CtcpReply sends a (generic) CTCP reply to the target nick
-// or channel t, with an optional argument.
-// NOTICE t :\001CTCP arg\001
-func (conn *Conn) CtcpReply(t, ctcp string, arg ...string) {
- for _, s := range splitMessage(strings.Join(arg, " "), conn.cfg.SplitLen) {
- if s != "" {
- s = " " + s
- }
- // Using Raw rather than NOTICE here to avoid double-split problems.
- conn.Raw(NOTICE + " " + t + " :\001" + strings.ToUpper(ctcp) + s + "\001")
- }
-}
-
-// Version sends a CTCP "VERSION" to the target nick or channel t.
-func (conn *Conn) Version(t string) { conn.Ctcp(t, VERSION) }
-
-// Action sends a CTCP "ACTION" to the target nick or channel t.
-func (conn *Conn) Action(t, msg string) { conn.Ctcp(t, ACTION, msg) }
-
-// Topic() sends a TOPIC command for a channel.
-// If no topic is provided this requests that a 332 response is sent by the
-// server for that channel, which can then be handled to retrieve the current
-// channel topic. If a topic is provided the channel's topic will be set.
-// TOPIC channel
-// TOPIC channel :topic
-func (conn *Conn) Topic(channel string, topic ...string) {
- t := strings.Join(topic, " ")
- if t != "" {
- t = " :" + t
- }
- conn.Raw(TOPIC + " " + channel + t)
-}
-
-// Mode sends a MODE command for a target nick or channel t.
-// If no mode strings are provided this requests that a 324 response is sent
-// by the server for the target. Otherwise the mode strings are concatenated
-// with spaces and sent to the server. This allows e.g.
-// conn.Mode("#channel", "+nsk", "mykey")
-//
-// MODE t
-// MODE t modestring
-func (conn *Conn) Mode(t string, modestring ...string) {
- mode := strings.Join(modestring, " ")
- if mode != "" {
- mode = " " + mode
- }
- conn.Raw(MODE + " " + t + mode)
-}
-
-// Away sends an AWAY command to the server.
-// If a message is provided it sets the client's away status with that message,
-// otherwise it resets the client's away status.
-// AWAY
-// AWAY :message
-func (conn *Conn) Away(message ...string) {
- msg := strings.Join(message, " ")
- if msg != "" {
- msg = " :" + msg
- }
- conn.Raw(AWAY + msg)
-}
-
-// Invite sends an INVITE command to the server.
-// INVITE nick channel
-func (conn *Conn) Invite(nick, channel string) {
- conn.Raw(INVITE + " " + nick + " " + channel)
-}
-
-// Oper sends an OPER command to the server.
-// OPER user pass
-func (conn *Conn) Oper(user, pass string) { conn.Raw(OPER + " " + user + " " + pass) }
-
-// VHost sends a VHOST command to the server.
-// VHOST user pass
-func (conn *Conn) VHost(user, pass string) { conn.Raw(VHOST + " " + user + " " + pass) }
-
-// Ping sends a PING command to the server, which should PONG.
-// PING :message
-func (conn *Conn) Ping(message string) { conn.Raw(PING + " :" + message) }
-
-// Pong sends a PONG command to the server.
-// PONG :message
-func (conn *Conn) Pong(message string) { conn.Raw(PONG + " :" + message) }
-
-// Cap sends a CAP command to the server.
-// CAP subcommand
-// CAP subcommand :message
-func (conn *Conn) Cap(subcommmand string, capabilities ...string) {
- if len(capabilities) == 0 {
- conn.Raw(CAP + " " + subcommmand)
- } else {
- conn.Raw(CAP + " " + subcommmand + " :" + strings.Join(capabilities, " "))
- }
-}
diff --git a/vendor/github.com/fluffle/goirc/client/connection.go b/vendor/github.com/fluffle/goirc/client/connection.go
deleted file mode 100644
index 12d0e59..0000000
--- a/vendor/github.com/fluffle/goirc/client/connection.go
+++ /dev/null
@@ -1,587 +0,0 @@
-package client
-
-import (
- "bufio"
- "crypto/tls"
- "fmt"
- "io"
- "net"
- "net/url"
- "strings"
- "sync"
- "time"
-
- "github.com/fluffle/goirc/logging"
- "github.com/fluffle/goirc/state"
- "golang.org/x/net/proxy"
-)
-
-// Conn encapsulates a connection to a single IRC server. Create
-// one with Client or SimpleClient.
-type Conn struct {
- // For preventing races on (dis)connect.
- mu sync.RWMutex
-
- // Contains parameters that people can tweak to change client behaviour.
- cfg *Config
-
- // Handlers
- intHandlers *hSet
- fgHandlers *hSet
- bgHandlers *hSet
-
- // State tracker for nicks and channels
- st state.Tracker
- stRemovers []Remover
-
- // I/O stuff to server
- dialer *net.Dialer
- proxyDialer proxy.Dialer
- sock net.Conn
- io *bufio.ReadWriter
- in chan *Line
- out chan string
- connected bool
-
- // Control channel and WaitGroup for goroutines
- die chan struct{}
- wg sync.WaitGroup
-
- // Internal counters for flood protection
- badness time.Duration
- lastsent time.Time
-}
-
-// Config contains options that can be passed to Client to change the
-// behaviour of the library during use. It is recommended that NewConfig
-// is used to create this struct rather than instantiating one directly.
-// Passing a Config with no Nick in the Me field to Client will result
-// in unflattering consequences.
-type Config struct {
- // Set this to provide the Nick, Ident and Name for the client to use.
- // It is recommended to call Conn.Me to get up-to-date information
- // about the current state of the client's IRC nick after connecting.
- Me *state.Nick
-
- // Hostname to connect to and optional connect password.
- // Changing these after connection will have no effect until the
- // client reconnects.
- Server, Pass string
-
- // Are we connecting via SSL? Do we care about certificate validity?
- // Changing these after connection will have no effect until the
- // client reconnects.
- SSL bool
- SSLConfig *tls.Config
-
- // To connect via proxy set the proxy url here.
- // Changing these after connection will have no effect until the
- // client reconnects.
- Proxy string
-
- // Local address to bind to when connecting to the server.
- LocalAddr string
-
- // To attempt RFC6555 parallel IPv4 and IPv6 connections if both
- // address families are returned for a hostname, set this to true.
- // Passed through to https://golang.org/pkg/net/#Dialer
- DualStack bool
-
- // Replaceable function to customise the 433 handler's new nick.
- // By default an underscore "_" is appended to the current nick.
- NewNick func(string) string
-
- // Client->server ping frequency, in seconds. Defaults to 3m.
- // Set to 0 to disable client-side pings.
- PingFreq time.Duration
-
- // The duration before a connection timeout is triggered. Defaults to 1m.
- // Set to 0 to wait indefinitely.
- Timeout time.Duration
-
- // Set this to true to disable flood protection and false to re-enable.
- Flood bool
-
- // Sent as the reply to a CTCP VERSION message.
- Version string
-
- // Sent as the default QUIT message if Quit is called with no args.
- QuitMessage string
-
- // Configurable panic recovery for all handlers.
- // Defaults to logging an error, see LogPanic.
- Recover func(*Conn, *Line)
-
- // Split PRIVMSGs, NOTICEs and CTCPs longer than SplitLen characters
- // over multiple lines. Default to 450 if not set.
- SplitLen int
-}
-
-// NewConfig creates a Config struct containing sensible defaults.
-// It takes one required argument: the nick to use for the client.
-// Subsequent string arguments set the client's ident and "real"
-// name, but these are optional.
-func NewConfig(nick string, args ...string) *Config {
- cfg := &Config{
- Me: &state.Nick{Nick: nick},
- PingFreq: 3 * time.Minute,
- NewNick: func(s string) string { return s + "_" },
- Recover: (*Conn).LogPanic, // in dispatch.go
- SplitLen: defaultSplit,
- Timeout: 60 * time.Second,
- }
- cfg.Me.Ident = "goirc"
- if len(args) > 0 && args[0] != "" {
- cfg.Me.Ident = args[0]
- }
- cfg.Me.Name = "Powered by GoIRC"
- if len(args) > 1 && args[1] != "" {
- cfg.Me.Name = args[1]
- }
- cfg.Version = "Powered by GoIRC"
- cfg.QuitMessage = "GoBye!"
- return cfg
-}
-
-// SimpleClient creates a new Conn, passing its arguments to NewConfig.
-// If you don't need to change any client options and just want to get
-// started quickly, this is a convenient shortcut.
-func SimpleClient(nick string, args ...string) *Conn {
- conn := Client(NewConfig(nick, args...))
- return conn
-}
-
-// Client takes a Config struct and returns a new Conn ready to have
-// handlers added and connect to a server.
-func Client(cfg *Config) *Conn {
- if cfg == nil {
- cfg = NewConfig("__idiot__")
- }
- if cfg.Me == nil || cfg.Me.Nick == "" || cfg.Me.Ident == "" {
- cfg.Me = &state.Nick{Nick: "__idiot__"}
- cfg.Me.Ident = "goirc"
- cfg.Me.Name = "Powered by GoIRC"
- }
-
- dialer := new(net.Dialer)
- dialer.Timeout = cfg.Timeout
- dialer.DualStack = cfg.DualStack
- if cfg.LocalAddr != "" {
- if !hasPort(cfg.LocalAddr) {
- cfg.LocalAddr += ":0"
- }
-
- local, err := net.ResolveTCPAddr("tcp", cfg.LocalAddr)
- if err == nil {
- dialer.LocalAddr = local
- } else {
- logging.Error("irc.Client(): Cannot resolve local address %s: %s", cfg.LocalAddr, err)
- }
- }
-
- conn := &Conn{
- cfg: cfg,
- dialer: dialer,
- intHandlers: handlerSet(),
- fgHandlers: handlerSet(),
- bgHandlers: handlerSet(),
- stRemovers: make([]Remover, 0, len(stHandlers)),
- lastsent: time.Now(),
- }
- conn.addIntHandlers()
- return conn
-}
-
-// Connected returns true if the client is successfully connected to
-// an IRC server. It becomes true when the TCP connection is established,
-// and false again when the connection is closed.
-func (conn *Conn) Connected() bool {
- conn.mu.RLock()
- defer conn.mu.RUnlock()
- return conn.connected
-}
-
-// Config returns a pointer to the Config struct used by the client.
-// Many of the elements of Config may be changed at any point to
-// affect client behaviour. To disable flood protection temporarily,
-// for example, a handler could do:
-//
-// conn.Config().Flood = true
-// // Send many lines to the IRC server, risking "excess flood"
-// conn.Config().Flood = false
-//
-func (conn *Conn) Config() *Config {
- return conn.cfg
-}
-
-// Me returns a state.Nick that reflects the client's IRC nick at the
-// time it is called. If state tracking is enabled, this comes from
-// the tracker, otherwise it is equivalent to conn.cfg.Me.
-func (conn *Conn) Me() *state.Nick {
- if conn.st != nil {
- conn.cfg.Me = conn.st.Me()
- }
- return conn.cfg.Me
-}
-
-// StateTracker returns the state tracker being used by the client,
-// if tracking is enabled, and nil otherwise.
-func (conn *Conn) StateTracker() state.Tracker {
- return conn.st
-}
-
-// EnableStateTracking causes the client to track information about
-// all channels it is joined to, and all the nicks in those channels.
-// This can be rather handy for a number of bot-writing tasks. See
-// the state package for more details.
-//
-// NOTE: Calling this while connected to an IRC server may cause the
-// state tracker to become very confused all over STDERR if logging
-// is enabled. State tracking should enabled before connecting or
-// at a pinch while the client is not joined to any channels.
-func (conn *Conn) EnableStateTracking() {
- conn.mu.Lock()
- defer conn.mu.Unlock()
- if conn.st == nil {
- n := conn.cfg.Me
- conn.st = state.NewTracker(n.Nick)
- conn.st.NickInfo(n.Nick, n.Ident, n.Host, n.Name)
- conn.cfg.Me = conn.st.Me()
- conn.addSTHandlers()
- }
-}
-
-// DisableStateTracking causes the client to stop tracking information
-// about the channels and nicks it knows of. It will also wipe current
-// state from the state tracker.
-func (conn *Conn) DisableStateTracking() {
- conn.mu.Lock()
- defer conn.mu.Unlock()
- if conn.st != nil {
- conn.cfg.Me = conn.st.Me()
- conn.delSTHandlers()
- conn.st.Wipe()
- conn.st = nil
- }
-}
-
-// Per-connection state initialisation.
-func (conn *Conn) initialise() {
- conn.io = nil
- conn.sock = nil
- conn.in = make(chan *Line, 32)
- conn.out = make(chan string, 32)
- conn.die = make(chan struct{})
- if conn.st != nil {
- conn.st.Wipe()
- }
-}
-
-// ConnectTo connects the IRC client to "host[:port]", which should be either
-// a hostname or an IP address, with an optional port. It sets the client's
-// Config.Server to host, Config.Pass to pass if one is provided, and then
-// calls Connect.
-func (conn *Conn) ConnectTo(host string, pass ...string) error {
- conn.cfg.Server = host
- if len(pass) > 0 {
- conn.cfg.Pass = pass[0]
- }
- return conn.Connect()
-}
-
-// Connect connects the IRC client to the server configured in Config.Server.
-// To enable explicit SSL on the connection to the IRC server, set Config.SSL
-// to true before calling Connect(). The port will default to 6697 if SSL is
-// enabled, and 6667 otherwise.
-// To enable connecting via a proxy server, set Config.Proxy to the proxy URL
-// (example socks5://localhost:9000) before calling Connect().
-//
-// Upon successful connection, Connected will return true and a REGISTER event
-// will be fired. This is mostly for internal use; it is suggested that a
-// handler for the CONNECTED event is used to perform any initial client work
-// like joining channels and sending messages.
-func (conn *Conn) Connect() error {
- // We don't want to hold conn.mu while firing the REGISTER event,
- // and it's much easier and less error prone to defer the unlock,
- // so the connect mechanics have been delegated to internalConnect.
- err := conn.internalConnect()
- if err == nil {
- conn.dispatch(&Line{Cmd: REGISTER, Time: time.Now()})
- }
- return err
-}
-
-// internalConnect handles the work of actually connecting to the server.
-func (conn *Conn) internalConnect() error {
- conn.mu.Lock()
- defer conn.mu.Unlock()
- conn.initialise()
-
- if conn.cfg.Server == "" {
- return fmt.Errorf("irc.Connect(): cfg.Server must be non-empty")
- }
- if conn.connected {
- return fmt.Errorf("irc.Connect(): Cannot connect to %s, already connected.", conn.cfg.Server)
- }
-
- if !hasPort(conn.cfg.Server) {
- if conn.cfg.SSL {
- conn.cfg.Server = net.JoinHostPort(conn.cfg.Server, "6697")
- } else {
- conn.cfg.Server = net.JoinHostPort(conn.cfg.Server, "6667")
- }
- }
-
- if conn.cfg.Proxy != "" {
- proxyURL, err := url.Parse(conn.cfg.Proxy)
- if err != nil {
- return err
- }
- conn.proxyDialer, err = proxy.FromURL(proxyURL, conn.dialer)
- if err != nil {
- return err
- }
-
- logging.Info("irc.Connect(): Connecting to %s.", conn.cfg.Server)
- if s, err := conn.proxyDialer.Dial("tcp", conn.cfg.Server); err == nil {
- conn.sock = s
- } else {
- return err
- }
- } else {
- logging.Info("irc.Connect(): Connecting to %s.", conn.cfg.Server)
- if s, err := conn.dialer.Dial("tcp", conn.cfg.Server); err == nil {
- conn.sock = s
- } else {
- return err
- }
- }
-
- if conn.cfg.SSL {
- logging.Info("irc.Connect(): Performing SSL handshake.")
- s := tls.Client(conn.sock, conn.cfg.SSLConfig)
- if err := s.Handshake(); err != nil {
- return err
- }
- conn.sock = s
- }
-
- conn.postConnect(true)
- conn.connected = true
- return nil
-}
-
-// postConnect performs post-connection setup, for ease of testing.
-func (conn *Conn) postConnect(start bool) {
- conn.io = bufio.NewReadWriter(
- bufio.NewReader(conn.sock),
- bufio.NewWriter(conn.sock))
- if start {
- conn.wg.Add(3)
- go conn.send()
- go conn.recv()
- go conn.runLoop()
- if conn.cfg.PingFreq > 0 {
- conn.wg.Add(1)
- go conn.ping()
- }
- }
-}
-
-// hasPort returns true if the string hostname has a :port suffix.
-// It was copied from net/http for great justice.
-func hasPort(s string) bool {
- return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
-}
-
-// send is started as a goroutine after a connection is established.
-// It shuttles data from the output channel to write(), and is killed
-// when Conn.die is closed.
-func (conn *Conn) send() {
- for {
- select {
- case line := <-conn.out:
- if err := conn.write(line); err != nil {
- logging.Error("irc.send(): %s", err.Error())
- // We can't defer this, because Close() waits for it.
- conn.wg.Done()
- conn.Close()
- return
- }
- case <-conn.die:
- // control channel closed, bail out
- conn.wg.Done()
- return
- }
- }
-}
-
-// recv is started as a goroutine after a connection is established.
-// It receives "\r\n" terminated lines from the server, parses them into
-// Lines, and sends them to the input channel.
-func (conn *Conn) recv() {
- for {
- s, err := conn.io.ReadString('\n')
- if err != nil {
- if err != io.EOF {
- logging.Error("irc.recv(): %s", err.Error())
- }
- // We can't defer this, because Close() waits for it.
- conn.wg.Done()
- conn.Close()
- return
- }
- s = strings.Trim(s, "\r\n")
- logging.Debug("<- %s", s)
-
- if line := ParseLine(s); line != nil {
- line.Time = time.Now()
- conn.in <- line
- } else {
- logging.Warn("irc.recv(): problems parsing line:\n %s", s)
- }
- }
-}
-
-// ping is started as a goroutine after a connection is established, as
-// long as Config.PingFreq >0. It pings the server every PingFreq seconds.
-func (conn *Conn) ping() {
- defer conn.wg.Done()
- tick := time.NewTicker(conn.cfg.PingFreq)
- for {
- select {
- case <-tick.C:
- conn.Ping(fmt.Sprintf("%d", time.Now().UnixNano()))
- case <-conn.die:
- // control channel closed, bail out
- tick.Stop()
- return
- }
- }
-}
-
-// runLoop is started as a goroutine after a connection is established.
-// It pulls Lines from the input channel and dispatches them to any
-// handlers that have been registered for that IRC verb.
-func (conn *Conn) runLoop() {
- defer conn.wg.Done()
- for {
- select {
- case line := <-conn.in:
- conn.dispatch(line)
- case <-conn.die:
- // control channel closed, bail out
- return
- }
- }
-}
-
-// write writes a \r\n terminated line of output to the connected server,
-// using Hybrid's algorithm to rate limit if conn.cfg.Flood is false.
-func (conn *Conn) write(line string) error {
- if !conn.cfg.Flood {
- if t := conn.rateLimit(len(line)); t != 0 {
- // sleep for the current line's time value before sending it
- logging.Info("irc.rateLimit(): Flood! Sleeping for %.2f secs.",
- t.Seconds())
- <-time.After(t)
- }
- }
-
- if _, err := conn.io.WriteString(line + "\r\n"); err != nil {
- return err
- }
- if err := conn.io.Flush(); err != nil {
- return err
- }
- if strings.HasPrefix(line, "PASS") {
- line = "PASS **************"
- }
- logging.Debug("-> %s", line)
- return nil
-}
-
-// rateLimit implements Hybrid's flood control algorithm for outgoing lines.
-func (conn *Conn) rateLimit(chars int) time.Duration {
- // Hybrid's algorithm allows for 2 seconds per line and an additional
- // 1/120 of a second per character on that line.
- linetime := 2*time.Second + time.Duration(chars)*time.Second/120
- elapsed := time.Now().Sub(conn.lastsent)
- if conn.badness += linetime - elapsed; conn.badness < 0 {
- // negative badness times are badness...
- conn.badness = 0
- }
- conn.lastsent = time.Now()
- // If we've sent more than 10 second's worth of lines according to the
- // calculation above, then we're at risk of "Excess Flood".
- if conn.badness > 10*time.Second {
- return linetime
- }
- return 0
-}
-
-// Close tears down all connection-related state. It is called when either
-// the sending or receiving goroutines encounter an error.
-// It may also be used to forcibly shut down the connection to the server.
-func (conn *Conn) Close() error {
- // Guard against double-call of Close() if we get an error in send()
- // as calling sock.Close() will cause recv() to receive EOF in readstring()
- conn.mu.Lock()
- if !conn.connected {
- conn.mu.Unlock()
- return nil
- }
- logging.Info("irc.Close(): Disconnected from server.")
- conn.connected = false
- err := conn.sock.Close()
- close(conn.die)
- // Drain both in and out channels to avoid a deadlock if the buffers
- // have filled. See TestSendDeadlockOnFullBuffer in connection_test.go.
- conn.drainIn()
- conn.drainOut()
- conn.wg.Wait()
- conn.mu.Unlock()
- // Dispatch after closing connection but before reinit
- // so event handlers can still access state information.
- conn.dispatch(&Line{Cmd: DISCONNECTED, Time: time.Now()})
- return err
-}
-
-// drainIn sends all data buffered in conn.in to /dev/null.
-func (conn *Conn) drainIn() {
- for {
- select {
- case <-conn.in:
- default:
- return
- }
- }
-}
-
-// drainOut does the same for conn.out. Generics!
-func (conn *Conn) drainOut() {
- for {
- select {
- case <-conn.out:
- default:
- return
- }
- }
-}
-
-// Dumps a load of information about the current state of the connection to a
-// string for debugging state tracking and other such things.
-func (conn *Conn) String() string {
- str := "GoIRC Connection\n"
- str += "----------------\n\n"
- if conn.Connected() {
- str += "Connected to " + conn.cfg.Server + "\n\n"
- } else {
- str += "Not currently connected!\n\n"
- }
- str += conn.Me().String() + "\n"
- if conn.st != nil {
- str += conn.st.String() + "\n"
- }
- return str
-}
diff --git a/vendor/github.com/fluffle/goirc/client/dispatch.go b/vendor/github.com/fluffle/goirc/client/dispatch.go
deleted file mode 100644
index 4a5deb9..0000000
--- a/vendor/github.com/fluffle/goirc/client/dispatch.go
+++ /dev/null
@@ -1,202 +0,0 @@
-package client
-
-import (
- "runtime"
- "strings"
- "sync"
-
- "github.com/fluffle/goirc/logging"
-)
-
-// Handlers are triggered on incoming Lines from the server, with the handler
-// "name" being equivalent to Line.Cmd. Read the RFCs for details on what
-// replies could come from the server. They'll generally be things like
-// "PRIVMSG", "JOIN", etc. but all the numeric replies are left as ascii
-// strings of digits like "332" (mainly because I really didn't feel like
-// putting massive constant tables in).
-//
-// Foreground handlers have a guarantee of protocol consistency: all the
-// handlers for one event will have finished before the handlers for the
-// next start processing. They are run in parallel but block the event
-// loop, so care should be taken to ensure these handlers are quick :-)
-//
-// Background handlers are run in parallel and do not block the event loop.
-// This is useful for things that may need to do significant work.
-type Handler interface {
- Handle(*Conn, *Line)
-}
-
-// Removers allow for a handler that has been previously added to the client
-// to be removed.
-type Remover interface {
- Remove()
-}
-
-// HandlerFunc allows a bare function with this signature to implement the
-// Handler interface. It is used by Conn.HandleFunc.
-type HandlerFunc func(*Conn, *Line)
-
-func (hf HandlerFunc) Handle(conn *Conn, line *Line) {
- hf(conn, line)
-}
-
-// Handlers are organised using a map of linked-lists, with each map
-// key representing an IRC verb or numeric, and the linked list values
-// being handlers that are executed in parallel when a Line from the
-// server with that verb or numeric arrives.
-type hSet struct {
- set map[string]*hList
- sync.RWMutex
-}
-
-type hList struct {
- start, end *hNode
-}
-
-// Storing the forward and backward links in the node allows O(1) removal.
-// This probably isn't strictly necessary but I think it's kinda nice.
-type hNode struct {
- next, prev *hNode
- set *hSet
- event string
- handler Handler
-}
-
-// A hNode implements both Handler (with configurable panic recovery)...
-func (hn *hNode) Handle(conn *Conn, line *Line) {
- defer conn.cfg.Recover(conn, line)
- hn.handler.Handle(conn, line)
-}
-
-// ... and Remover.
-func (hn *hNode) Remove() {
- hn.set.remove(hn)
-}
-
-func handlerSet() *hSet {
- return &hSet{set: make(map[string]*hList)}
-}
-
-// When a new Handler is added for an event, it is wrapped in a hNode and
-// returned as a Remover so the caller can remove it at a later time.
-func (hs *hSet) add(ev string, h Handler) Remover {
- hs.Lock()
- defer hs.Unlock()
- ev = strings.ToLower(ev)
- l, ok := hs.set[ev]
- if !ok {
- l = &hList{}
- }
- hn := &hNode{
- set: hs,
- event: ev,
- handler: h,
- }
- if !ok {
- l.start = hn
- } else {
- hn.prev = l.end
- l.end.next = hn
- }
- l.end = hn
- hs.set[ev] = l
- return hn
-}
-
-func (hs *hSet) remove(hn *hNode) {
- hs.Lock()
- defer hs.Unlock()
- l, ok := hs.set[hn.event]
- if !ok {
- logging.Error("Removing node for unknown event '%s'", hn.event)
- return
- }
- if hn.next == nil {
- l.end = hn.prev
- } else {
- hn.next.prev = hn.prev
- }
- if hn.prev == nil {
- l.start = hn.next
- } else {
- hn.prev.next = hn.next
- }
- hn.next = nil
- hn.prev = nil
- hn.set = nil
- if l.start == nil || l.end == nil {
- delete(hs.set, hn.event)
- }
-}
-
-func (hs *hSet) getHandlers(ev string) []*hNode {
- hs.RLock()
- defer hs.RUnlock()
- list, ok := hs.set[ev]
- if !ok {
- return nil
- }
- // Copy current list of handlers to a temporary slice under the lock.
- handlers := make([]*hNode, 0)
- for hn := list.start; hn != nil; hn = hn.next {
- handlers = append(handlers, hn)
- }
- return handlers
-}
-
-func (hs *hSet) dispatch(conn *Conn, line *Line) {
- ev := strings.ToLower(line.Cmd)
- wg := &sync.WaitGroup{}
- for _, hn := range hs.getHandlers(ev) {
- wg.Add(1)
- go func(hn *hNode) {
- hn.Handle(conn, line.Copy())
- wg.Done()
- }(hn)
- }
- wg.Wait()
-}
-
-// Handle adds the provided handler to the foreground set for the named event.
-// It will return a Remover that allows that handler to be removed again.
-func (conn *Conn) Handle(name string, h Handler) Remover {
- return conn.fgHandlers.add(name, h)
-}
-
-// HandleBG adds the provided handler to the background set for the named
-// event. It may go away in the future.
-// It will return a Remover that allows that handler to be removed again.
-func (conn *Conn) HandleBG(name string, h Handler) Remover {
- return conn.bgHandlers.add(name, h)
-}
-
-func (conn *Conn) handle(name string, h Handler) Remover {
- return conn.intHandlers.add(name, h)
-}
-
-// HandleFunc adds the provided function as a handler in the foreground set
-// for the named event.
-// It will return a Remover that allows that handler to be removed again.
-func (conn *Conn) HandleFunc(name string, hf HandlerFunc) Remover {
- return conn.Handle(name, hf)
-}
-
-func (conn *Conn) dispatch(line *Line) {
- // We run the internal handlers first, including all state tracking ones.
- // This ensures that user-supplied handlers that use the tracker have a
- // consistent view of the connection state in handlers that mutate it.
- conn.intHandlers.dispatch(conn, line)
- go conn.bgHandlers.dispatch(conn, line)
- conn.fgHandlers.dispatch(conn, line)
-}
-
-// LogPanic is used as the default panic catcher for the client. If, like me,
-// you are not good with computer, and you'd prefer your bot not to vanish into
-// the ether whenever you make unfortunate programming mistakes, you may find
-// this useful: it will recover panics from handler code and log the errors.
-func (conn *Conn) LogPanic(line *Line) {
- if err := recover(); err != nil {
- _, f, l, _ := runtime.Caller(2)
- logging.Error("%s:%d: panic: %v", f, l, err)
- }
-}
diff --git a/vendor/github.com/fluffle/goirc/client/doc.go b/vendor/github.com/fluffle/goirc/client/doc.go
deleted file mode 100644
index 08a79dd..0000000
--- a/vendor/github.com/fluffle/goirc/client/doc.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// Package client implements an IRC client. It handles protocol basics
-// such as initial connection and responding to server PINGs, and has
-// optional state tracking support which will keep tabs on every nick
-// present in the same channels as the client. Other features include
-// SSL support, automatic splitting of long lines, and panic recovery
-// for handlers.
-//
-// Incoming IRC messages are parsed into client.Line structs and trigger
-// events based on the IRC verb (e.g. PRIVMSG) of the message. Handlers
-// for these events conform to the client.Handler interface; a HandlerFunc
-// type to wrap bare functions is provided a-la the net/http package.
-//
-// Creating a client, adding a handler and connecting to a server looks
-// soemthing like this, for the simple case:
-//
-// // Create a new client, which will connect with the nick "myNick"
-// irc := client.SimpleClient("myNick")
-//
-// // Add a handler that waits for the "disconnected" event and
-// // closes a channel to signal everything is done.
-// disconnected := make(chan struct{})
-// c.HandleFunc("disconnected", func(c *client.Conn, l *client.Line) {
-// close(disconnected)
-// })
-//
-// // Connect to an IRC server.
-// if err := c.ConnectTo("irc.freenode.net"); err != nil {
-// log.Fatalf("Connection error: %v\n", err)
-// }
-//
-// // Wait for disconnection.
-// <-disconnected
-//
-package client
diff --git a/vendor/github.com/fluffle/goirc/client/handlers.go b/vendor/github.com/fluffle/goirc/client/handlers.go
deleted file mode 100644
index b538579..0000000
--- a/vendor/github.com/fluffle/goirc/client/handlers.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package client
-
-// this file contains the basic set of event handlers
-// to manage tracking an irc connection etc.
-
-import (
- "strings"
- "time"
-)
-
-// sets up the internal event handlers to do essential IRC protocol things
-var intHandlers = map[string]HandlerFunc{
- REGISTER: (*Conn).h_REGISTER,
- "001": (*Conn).h_001,
- "433": (*Conn).h_433,
- CTCP: (*Conn).h_CTCP,
- NICK: (*Conn).h_NICK,
- PING: (*Conn).h_PING,
-}
-
-func (conn *Conn) addIntHandlers() {
- for n, h := range intHandlers {
- // internal handlers are essential for the IRC client
- // to function, so we don't save their Removers here
- conn.handle(n, h)
- }
-}
-
-// Basic ping/pong handler
-func (conn *Conn) h_PING(line *Line) {
- conn.Pong(line.Args[0])
-}
-
-// Handler for initial registration with server once tcp connection is made.
-func (conn *Conn) h_REGISTER(line *Line) {
- if conn.cfg.Pass != "" {
- conn.Pass(conn.cfg.Pass)
- }
- conn.Nick(conn.cfg.Me.Nick)
- conn.User(conn.cfg.Me.Ident, conn.cfg.Me.Name)
-}
-
-// Handler to trigger a CONNECTED event on receipt of numeric 001
-func (conn *Conn) h_001(line *Line) {
- // we're connected!
- conn.dispatch(&Line{Cmd: CONNECTED, Time: time.Now()})
- // and we're being given our hostname (from the server's perspective)
- t := line.Args[len(line.Args)-1]
- if idx := strings.LastIndex(t, " "); idx != -1 {
- t = t[idx+1:]
- if idx = strings.Index(t, "@"); idx != -1 {
- if conn.st != nil {
- me := conn.Me()
- conn.st.NickInfo(me.Nick, me.Ident, t[idx+1:], me.Name)
- } else {
- conn.cfg.Me.Host = t[idx+1:]
- }
- }
- }
-}
-
-// XXX: do we need 005 protocol support message parsing here?
-// probably in the future, but I can't quite be arsed yet.
-/*
- :irc.pl0rt.org 005 GoTest CMDS=KNOCK,MAP,DCCALLOW,USERIP UHNAMES NAMESX SAFELIST HCN MAXCHANNELS=20 CHANLIMIT=#:20 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 :are supported by this server
- :irc.pl0rt.org 005 GoTest MAXTARGETS=20 WALLCHOPS WATCH=128 WATCHOPTS=A SILENCE=15 MODES=12 CHANTYPES=# PREFIX=(qaohv)~&@%+ CHANMODES=beI,kfL,lj,psmntirRcOAQKVCuzNSMT NETWORK=bb101.net CASEMAPPING=ascii EXTBAN=~,cqnr ELIST=MNUCT :are supported by this server
- :irc.pl0rt.org 005 GoTest STATUSMSG=~&@%+ EXCEPTS INVEX :are supported by this server
-*/
-
-// Handler to deal with "433 :Nickname already in use"
-func (conn *Conn) h_433(line *Line) {
- // Args[1] is the new nick we were attempting to acquire
- me := conn.Me()
- neu := conn.cfg.NewNick(line.Args[1])
- conn.Nick(neu)
- if !line.argslen(1) {
- return
- }
- // if this is happening before we're properly connected (i.e. the nick
- // we sent in the initial NICK command is in use) we will not receive
- // a NICK message to confirm our change of nick, so ReNick here...
- if line.Args[1] == me.Nick {
- if conn.st != nil {
- conn.cfg.Me = conn.st.ReNick(me.Nick, neu)
- } else {
- conn.cfg.Me.Nick = neu
- }
- }
-}
-
-// Handle VERSION requests and CTCP PING
-func (conn *Conn) h_CTCP(line *Line) {
- if line.Args[0] == VERSION {
- conn.CtcpReply(line.Nick, VERSION, conn.cfg.Version)
- } else if line.Args[0] == PING && line.argslen(2) {
- conn.CtcpReply(line.Nick, PING, line.Args[2])
- }
-}
-
-// Handle updating our own NICK if we're not using the state tracker
-func (conn *Conn) h_NICK(line *Line) {
- if conn.st == nil && line.Nick == conn.cfg.Me.Nick {
- conn.cfg.Me.Nick = line.Args[0]
- }
-}
diff --git a/vendor/github.com/fluffle/goirc/client/line.go b/vendor/github.com/fluffle/goirc/client/line.go
deleted file mode 100644
index bfa473a..0000000
--- a/vendor/github.com/fluffle/goirc/client/line.go
+++ /dev/null
@@ -1,216 +0,0 @@
-package client
-
-import (
- "runtime"
- "strings"
- "time"
-
- "github.com/fluffle/goirc/logging"
-)
-
-var tagsReplacer = strings.NewReplacer("\\:", ";", "\\s", " ", "\\r", "\r", "\\n", "\n")
-
-// We parse an incoming line into this struct. Line.Cmd is used as the trigger
-// name for incoming event handlers and is the IRC verb, the first sequence
-// of non-whitespace characters after ":nick!user@host", e.g. PRIVMSG.
-// Raw =~ ":nick!user@host cmd args[] :text"
-// Src == "nick!user@host"
-// Cmd == e.g. PRIVMSG, 332
-type Line struct {
- Tags map[string]string
- Nick, Ident, Host, Src string
- Cmd, Raw string
- Args []string
- Time time.Time
-}
-
-// Copy returns a deep copy of the Line.
-func (l *Line) Copy() *Line {
- nl := *l
- nl.Args = make([]string, len(l.Args))
- copy(nl.Args, l.Args)
- if l.Tags != nil {
- nl.Tags = make(map[string]string)
- for k, v := range l.Tags {
- nl.Tags[k] = v
- }
- }
- return &nl
-}
-
-// Text returns the contents of the text portion of a line. This only really
-// makes sense for lines with a :text part, but there are a lot of them.
-func (line *Line) Text() string {
- if len(line.Args) > 0 {
- return line.Args[len(line.Args)-1]
- }
- return ""
-}
-
-// Target returns the contextual target of the line, usually the first Arg
-// for the IRC verb. If the line was broadcast from a channel, the target
-// will be that channel. If the line was sent directly by a user, the target
-// will be that user.
-func (line *Line) Target() string {
- // TODO(fluffle): Add 005 CHANTYPES parsing for this?
- switch line.Cmd {
- case PRIVMSG, NOTICE, ACTION:
- if !line.Public() {
- return line.Nick
- }
- case CTCP, CTCPREPLY:
- if !line.Public() {
- return line.Nick
- }
- return line.Args[1]
- }
- if len(line.Args) > 0 {
- return line.Args[0]
- }
- return ""
-}
-
-// Public returns true if the line is the result of an IRC user sending
-// a message to a channel the client has joined instead of directly
-// to the client.
-//
-// NOTE: This is very permissive, allowing all 4 RFC channel types even if
-// your server doesn't technically support them.
-func (line *Line) Public() bool {
- switch line.Cmd {
- case PRIVMSG, NOTICE, ACTION:
- switch line.Args[0][0] {
- case '#', '&', '+', '!':
- return true
- }
- case CTCP, CTCPREPLY:
- // CTCP prepends the CTCP verb to line.Args, thus for the message
- // :nick!user@host PRIVMSG #foo :\001BAR baz\001
- // line.Args contains: []string{"BAR", "#foo", "baz"}
- // TODO(fluffle): Arguably this is broken, and we should have
- // line.Args containing: []string{"#foo", "BAR", "baz"}
- // ... OR change conn.Ctcp()'s argument order to be consistent.
- switch line.Args[1][0] {
- case '#', '&', '+', '!':
- return true
- }
- }
- return false
-}
-
-// ParseLine creates a Line from an incoming message from the IRC server.
-//
-// It contains special casing for CTCP messages, most notably CTCP ACTION.
-// All CTCP messages have the \001 bytes stripped from the message and the
-// CTCP command separated from any subsequent text. Then, CTCP ACTIONs are
-// rewritten such that Line.Cmd == ACTION. Other CTCP messages have Cmd
-// set to CTCP or CTCPREPLY, and the CTCP command prepended to line.Args.
-//
-// ParseLine also parses IRCv3 tags, if received. If a line does not have
-// the tags section, Line.Tags will be nil. Tags are optional, and will
-// only be included after the correct CAP command.
-//
-// http://ircv3.net/specs/core/capability-negotiation-3.1.html
-// http://ircv3.net/specs/core/message-tags-3.2.html
-func ParseLine(s string) *Line {
- line := &Line{Raw: s}
-
- if s == "" {
- return nil
- }
-
- if s[0] == '@' {
- var rawTags string
- line.Tags = make(map[string]string)
- if idx := strings.Index(s, " "); idx != -1 {
- rawTags, s = s[1:idx], s[idx+1:]
- } else {
- return nil
- }
-
- // ; is represented as \: in a tag, so it's safe to split on ;
- for _, tag := range strings.Split(rawTags, ";") {
- if tag == "" {
- continue
- }
-
- pair := strings.SplitN(tagsReplacer.Replace(tag), "=", 2)
- if len(pair) < 2 {
- line.Tags[tag] = ""
- } else {
- line.Tags[pair[0]] = pair[1]
- }
- }
- }
-
- if s[0] == ':' {
- // remove a source and parse it
- if idx := strings.Index(s, " "); idx != -1 {
- line.Src, s = s[1:idx], s[idx+1:]
- } else {
- // pretty sure we shouldn't get here ...
- return nil
- }
-
- // src can be the hostname of the irc server or a nick!user@host
- line.Host = line.Src
- nidx, uidx := strings.Index(line.Src, "!"), strings.Index(line.Src, "@")
- if uidx != -1 && nidx != -1 {
- line.Nick = line.Src[:nidx]
- line.Ident = line.Src[nidx+1 : uidx]
- line.Host = line.Src[uidx+1:]
- }
- }
-
- // now we're here, we've parsed a :nick!user@host or :server off
- // s should contain "cmd args[] :text"
- args := strings.SplitN(s, " :", 2)
- if len(args) > 1 {
- args = append(strings.Fields(args[0]), args[1])
- } else {
- args = strings.Fields(args[0])
- }
- line.Cmd = strings.ToUpper(args[0])
- if len(args) > 1 {
- line.Args = args[1:]
- }
-
- // So, I think CTCP and (in particular) CTCP ACTION are better handled as
- // separate events as opposed to forcing people to have gargantuan
- // handlers to cope with the possibilities.
- if (line.Cmd == PRIVMSG || line.Cmd == NOTICE) &&
- len(line.Args[1]) > 2 &&
- strings.HasPrefix(line.Args[1], "\001") &&
- strings.HasSuffix(line.Args[1], "\001") {
- // WOO, it's a CTCP message
- t := strings.SplitN(strings.Trim(line.Args[1], "\001"), " ", 2)
- if len(t) > 1 {
- // Replace the line with the unwrapped CTCP
- line.Args[1] = t[1]
- }
- if c := strings.ToUpper(t[0]); c == ACTION && line.Cmd == PRIVMSG {
- // make a CTCP ACTION it's own event a-la PRIVMSG
- line.Cmd = c
- } else {
- // otherwise, dispatch a generic CTCP/CTCPREPLY event that
- // contains the type of CTCP in line.Args[0]
- if line.Cmd == PRIVMSG {
- line.Cmd = CTCP
- } else {
- line.Cmd = CTCPREPLY
- }
- line.Args = append([]string{c}, line.Args...)
- }
- }
- return line
-}
-
-func (line *Line) argslen(minlen int) bool {
- pc, _, _, _ := runtime.Caller(1)
- fn := runtime.FuncForPC(pc)
- if len(line.Args) <= minlen {
- logging.Warn("%s: too few arguments: %s", fn.Name(), strings.Join(line.Args, " "))
- return false
- }
- return true
-}
diff --git a/vendor/github.com/fluffle/goirc/client/state_handlers.go b/vendor/github.com/fluffle/goirc/client/state_handlers.go
deleted file mode 100644
index 847679c..0000000
--- a/vendor/github.com/fluffle/goirc/client/state_handlers.go
+++ /dev/null
@@ -1,262 +0,0 @@
-package client
-
-// this file contains the extra set of event handlers
-// to manage tracking state for an IRC connection
-
-import (
- "strings"
-
- "github.com/fluffle/goirc/logging"
-)
-
-var stHandlers = map[string]HandlerFunc{
- "JOIN": (*Conn).h_JOIN,
- "KICK": (*Conn).h_KICK,
- "MODE": (*Conn).h_MODE,
- "NICK": (*Conn).h_STNICK,
- "PART": (*Conn).h_PART,
- "QUIT": (*Conn).h_QUIT,
- "TOPIC": (*Conn).h_TOPIC,
- "311": (*Conn).h_311,
- "324": (*Conn).h_324,
- "332": (*Conn).h_332,
- "352": (*Conn).h_352,
- "353": (*Conn).h_353,
- "671": (*Conn).h_671,
-}
-
-func (conn *Conn) addSTHandlers() {
- for n, h := range stHandlers {
- conn.stRemovers = append(conn.stRemovers, conn.handle(n, h))
- }
-}
-
-func (conn *Conn) delSTHandlers() {
- for _, h := range conn.stRemovers {
- h.Remove()
- }
- conn.stRemovers = conn.stRemovers[:0]
-}
-
-// Handle NICK messages that need to update the state tracker
-func (conn *Conn) h_STNICK(line *Line) {
- // all nicks should be handled the same way, our own included
- conn.st.ReNick(line.Nick, line.Args[0])
-}
-
-// Handle JOINs to channels to maintain state
-func (conn *Conn) h_JOIN(line *Line) {
- ch := conn.st.GetChannel(line.Args[0])
- nk := conn.st.GetNick(line.Nick)
- if ch == nil {
- // first we've seen of this channel, so should be us joining it
- // NOTE this will also take care of nk == nil && ch == nil
- if !conn.Me().Equals(nk) {
- logging.Warn("irc.JOIN(): JOIN to unknown channel %s received "+
- "from (non-me) nick %s", line.Args[0], line.Nick)
- return
- }
- conn.st.NewChannel(line.Args[0])
- // since we don't know much about this channel, ask server for info
- // we get the channel users automatically in 353 and the channel
- // topic in 332 on join, so we just need to get the modes
- conn.Mode(line.Args[0])
- // sending a WHO for the channel is MUCH more efficient than
- // triggering a WHOIS on every nick from the 353 handler
- conn.Who(line.Args[0])
- }
- if nk == nil {
- // this is the first we've seen of this nick
- conn.st.NewNick(line.Nick)
- conn.st.NickInfo(line.Nick, line.Ident, line.Host, "")
- // since we don't know much about this nick, ask server for info
- conn.Who(line.Nick)
- }
- // this takes care of both nick and channel linking \o/
- conn.st.Associate(line.Args[0], line.Nick)
-}
-
-// Handle PARTs from channels to maintain state
-func (conn *Conn) h_PART(line *Line) {
- conn.st.Dissociate(line.Args[0], line.Nick)
-}
-
-// Handle KICKs from channels to maintain state
-func (conn *Conn) h_KICK(line *Line) {
- if !line.argslen(1) {
- return
- }
- // XXX: this won't handle autorejoining channels on KICK
- // it's trivial to do this in a seperate handler...
- conn.st.Dissociate(line.Args[0], line.Args[1])
-}
-
-// Handle other people's QUITs
-func (conn *Conn) h_QUIT(line *Line) {
- conn.st.DelNick(line.Nick)
-}
-
-// Handle MODE changes for channels we know about (and our nick personally)
-func (conn *Conn) h_MODE(line *Line) {
- if !line.argslen(1) {
- return
- }
- if ch := conn.st.GetChannel(line.Args[0]); ch != nil {
- // channel modes first
- conn.st.ChannelModes(line.Args[0], line.Args[1], line.Args[2:]...)
- } else if nk := conn.st.GetNick(line.Args[0]); nk != nil {
- // nick mode change, should be us
- if !conn.Me().Equals(nk) {
- logging.Warn("irc.MODE(): recieved MODE %s for (non-me) nick %s",
- line.Args[1], line.Args[0])
- return
- }
- conn.st.NickModes(line.Args[0], line.Args[1])
- } else {
- logging.Warn("irc.MODE(): not sure what to do with MODE %s",
- strings.Join(line.Args, " "))
- }
-}
-
-// Handle TOPIC changes for channels
-func (conn *Conn) h_TOPIC(line *Line) {
- if !line.argslen(1) {
- return
- }
- if ch := conn.st.GetChannel(line.Args[0]); ch != nil {
- conn.st.Topic(line.Args[0], line.Args[1])
- } else {
- logging.Warn("irc.TOPIC(): topic change on unknown channel %s",
- line.Args[0])
- }
-}
-
-// Handle 311 whois reply
-func (conn *Conn) h_311(line *Line) {
- if !line.argslen(5) {
- return
- }
- if nk := conn.st.GetNick(line.Args[1]); (nk != nil) && !conn.Me().Equals(nk) {
- conn.st.NickInfo(line.Args[1], line.Args[2], line.Args[3], line.Args[5])
- } else {
- logging.Warn("irc.311(): received WHOIS info for unknown nick %s",
- line.Args[1])
- }
-}
-
-// Handle 324 mode reply
-func (conn *Conn) h_324(line *Line) {
- if !line.argslen(2) {
- return
- }
- if ch := conn.st.GetChannel(line.Args[1]); ch != nil {
- conn.st.ChannelModes(line.Args[1], line.Args[2], line.Args[3:]...)
- } else {
- logging.Warn("irc.324(): received MODE settings for unknown channel %s",
- line.Args[1])
- }
-}
-
-// Handle 332 topic reply on join to channel
-func (conn *Conn) h_332(line *Line) {
- if !line.argslen(2) {
- return
- }
- if ch := conn.st.GetChannel(line.Args[1]); ch != nil {
- conn.st.Topic(line.Args[1], line.Args[2])
- } else {
- logging.Warn("irc.332(): received TOPIC value for unknown channel %s",
- line.Args[1])
- }
-}
-
-// Handle 352 who reply
-func (conn *Conn) h_352(line *Line) {
- if !line.argslen(5) {
- return
- }
- nk := conn.st.GetNick(line.Args[5])
- if nk == nil {
- logging.Warn("irc.352(): received WHO reply for unknown nick %s",
- line.Args[5])
- return
- }
- if conn.Me().Equals(nk) {
- return
- }
- // XXX: do we care about the actual server the nick is on?
- // or the hop count to this server?
- // last arg contains "<hop count> <real name>"
- a := strings.SplitN(line.Args[len(line.Args)-1], " ", 2)
- conn.st.NickInfo(nk.Nick, line.Args[2], line.Args[3], a[1])
- if !line.argslen(6) {
- return
- }
- if idx := strings.Index(line.Args[6], "*"); idx != -1 {
- conn.st.NickModes(nk.Nick, "+o")
- }
- if idx := strings.Index(line.Args[6], "B"); idx != -1 {
- conn.st.NickModes(nk.Nick, "+B")
- }
- if idx := strings.Index(line.Args[6], "H"); idx != -1 {
- conn.st.NickModes(nk.Nick, "+i")
- }
-}
-
-// Handle 353 names reply
-func (conn *Conn) h_353(line *Line) {
- if !line.argslen(2) {
- return
- }
- if ch := conn.st.GetChannel(line.Args[2]); ch != nil {
- nicks := strings.Split(line.Args[len(line.Args)-1], " ")
- for _, nick := range nicks {
- // UnrealIRCd's coders are lazy and leave a trailing space
- if nick == "" {
- continue
- }
- switch c := nick[0]; c {
- case '~', '&', '@', '%', '+':
- nick = nick[1:]
- fallthrough
- default:
- if conn.st.GetNick(nick) == nil {
- // we don't know this nick yet!
- conn.st.NewNick(nick)
- }
- if _, ok := conn.st.IsOn(ch.Name, nick); !ok {
- // This nick isn't associated with this channel yet!
- conn.st.Associate(ch.Name, nick)
- }
- switch c {
- case '~':
- conn.st.ChannelModes(ch.Name, "+q", nick)
- case '&':
- conn.st.ChannelModes(ch.Name, "+a", nick)
- case '@':
- conn.st.ChannelModes(ch.Name, "+o", nick)
- case '%':
- conn.st.ChannelModes(ch.Name, "+h", nick)
- case '+':
- conn.st.ChannelModes(ch.Name, "+v", nick)
- }
- }
- }
- } else {
- logging.Warn("irc.353(): received NAMES list for unknown channel %s",
- line.Args[2])
- }
-}
-
-// Handle 671 whois reply (nick connected via SSL)
-func (conn *Conn) h_671(line *Line) {
- if !line.argslen(1) {
- return
- }
- if nk := conn.st.GetNick(line.Args[1]); nk != nil {
- conn.st.NickModes(nk.Nick, "+z")
- } else {
- logging.Warn("irc.671(): received WHOIS SSL info for unknown nick %s",
- line.Args[1])
- }
-}