client

package
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 23, 2023 License: BSD-3-Clause Imports: 17 Imported by: 229

Documentation

Overview

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

Index

Constants

View Source
const (
	REGISTER     = "REGISTER"
	CONNECTED    = "CONNECTED"
	DISCONNECTED = "DISCONNECTED"
	ACTION       = "ACTION"
	AUTHENTICATE = "AUTHENTICATE"
	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"
)
View Source
const (
	CAP_LS  = "LS"
	CAP_REQ = "REQ"
	CAP_ACK = "ACK"
	CAP_NAK = "NAK"
	CAP_END = "END"
)

Variables

This section is empty.

Functions

func DefaultNewNick added in v1.0.2

func DefaultNewNick(old string) string

Because networks limit nick lengths, the easy approach of appending an '_' to a nick that is already in use can cause problems. When the length limit is reached, the clients idea of what its nick is ends up being different from the server. Hilarity ensues. Thanks to github.com/purpleidea for the bug report! Thanks to 'man ascii' for

Types

type Config

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

	// Enable IRCv3 capability negotiation.
	EnableCapabilityNegotiation bool

	// A list of capabilities to request to the server during registration.
	Capabilites []string

	// SASL configuration to use to authenticate the connection.
	Sasl sasl.Client

	// 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
}

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.

func NewConfig

func NewConfig(nick string, args ...string) *Config

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.

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn encapsulates a connection to a single IRC server. Create one with Client or SimpleClient.

func Client

func Client(cfg *Config) *Conn

Client takes a Config struct and returns a new Conn ready to have handlers added and connect to a server.

func SimpleClient

func SimpleClient(nick string, args ...string) *Conn

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 (*Conn) Action

func (conn *Conn) Action(t, msg string)

Action sends a CTCP "ACTION" to the target nick or channel t.

func (*Conn) Authenticate added in v1.3.0

func (conn *Conn) Authenticate(message string)

Authenticate send an AUTHENTICATE command to the server.

func (*Conn) Away

func (conn *Conn) Away(message ...string)

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) Cap

func (conn *Conn) Cap(subcommmand string, capabilities ...string)

Cap sends a CAP command to the server.

CAP subcommand
CAP subcommand :message

func (*Conn) Close

func (conn *Conn) Close() error

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) Config

func (conn *Conn) Config() *Config

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) Connect

func (conn *Conn) Connect() error

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) ConnectContext added in v1.1.0

func (conn *Conn) ConnectContext(ctx context.Context) error

ConnectContext works like Connect but uses the provided context.

func (*Conn) ConnectTo

func (conn *Conn) ConnectTo(host string, pass ...string) error

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) ConnectToContext added in v1.1.0

func (conn *Conn) ConnectToContext(ctx context.Context, host string, pass ...string) error

ConnectToContext works like ConnectTo but uses the provided context.

func (*Conn) Connected

func (conn *Conn) Connected() bool

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) Ctcp

func (conn *Conn) Ctcp(t, ctcp string, arg ...string)

Ctcp sends a (generic) CTCP message to the target nick or channel t, with an optional argument.

PRIVMSG t :\001CTCP arg\001

func (*Conn) CtcpReply

func (conn *Conn) CtcpReply(t, ctcp string, arg ...string)

CtcpReply sends a (generic) CTCP reply to the target nick or channel t, with an optional argument.

NOTICE t :\001CTCP arg\001

func (*Conn) DisableStateTracking

func (conn *Conn) DisableStateTracking()

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) EnableStateTracking

func (conn *Conn) EnableStateTracking()

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) Handle

func (conn *Conn) Handle(name string, h Handler) Remover

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) HandleBG

func (conn *Conn) HandleBG(name string, h Handler) Remover

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) HandleFunc

func (conn *Conn) HandleFunc(name string, hf HandlerFunc) Remover

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) HasCapability added in v1.2.0

func (conn *Conn) HasCapability(cap string) bool

HasCapability returns true if the given capability has been acked by the server during negotiation.

func (*Conn) Invite

func (conn *Conn) Invite(nick, channel string)

Invite sends an INVITE command to the server.

INVITE nick channel

func (*Conn) Join

func (conn *Conn) Join(channel string, key ...string)

Join sends a JOIN command to the server with an optional key.

JOIN channel [key]

func (*Conn) Kick

func (conn *Conn) Kick(channel, nick string, message ...string)

Kick sends a KICK command to remove a nick from a channel.

KICK channel nick [:message]

func (*Conn) LogPanic

func (conn *Conn) LogPanic(line *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) Me

func (conn *Conn) Me() *state.Nick

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) Mode

func (conn *Conn) Mode(t string, modestring ...string)

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) Nick

func (conn *Conn) Nick(nick string)

Nick sends a NICK command to the server.

NICK nick

func (*Conn) Notice

func (conn *Conn) Notice(t, msg string)

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) Oper

func (conn *Conn) Oper(user, pass string)

Oper sends an OPER command to the server.

OPER user pass

func (*Conn) Part

func (conn *Conn) Part(channel string, message ...string)

Part sends a PART command to the server with an optional part message.

PART channel [:message]

func (*Conn) Pass

func (conn *Conn) Pass(password string)

Pass sends a PASS command to the server.

PASS password

func (*Conn) Ping

func (conn *Conn) Ping(message string)

Ping sends a PING command to the server, which should PONG.

PING :message

func (*Conn) Pong

func (conn *Conn) Pong(message string)

Pong sends a PONG command to the server.

PONG :message

func (*Conn) Privmsg

func (conn *Conn) Privmsg(t, msg string)

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) Privmsgf

func (conn *Conn) Privmsgf(t, format string, a ...interface{})

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) Privmsgln

func (conn *Conn) Privmsgln(t string, a ...interface{})

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) Quit

func (conn *Conn) Quit(message ...string)

Quit sends a QUIT command to the server with an optional quit message.

QUIT [:message]

func (*Conn) Raw

func (conn *Conn) Raw(rawline string)

Raw sends a raw line to the server, should really only be used for debugging purposes but may well come in handy.

func (*Conn) StateTracker

func (conn *Conn) StateTracker() state.Tracker

StateTracker returns the state tracker being used by the client, if tracking is enabled, and nil otherwise.

func (*Conn) String

func (conn *Conn) String() string

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) SupportsCapability added in v1.2.0

func (conn *Conn) SupportsCapability(cap string) bool

SupportsCapability returns true if the server supports the given capability.

func (*Conn) Topic

func (conn *Conn) Topic(channel string, topic ...string)

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) User

func (conn *Conn) User(ident, name string)

User sends a USER command to the server.

USER ident 12 * :name

func (*Conn) VHost

func (conn *Conn) VHost(user, pass string)

VHost sends a VHOST command to the server.

VHOST user pass

func (*Conn) Version

func (conn *Conn) Version(t string)

Version sends a CTCP "VERSION" to the target nick or channel t.

func (*Conn) Who

func (conn *Conn) Who(nick string)

Who sends a WHO command to the server.

WHO nick

func (*Conn) Whois

func (conn *Conn) Whois(nick string)

Whois sends a WHOIS command to the server.

WHOIS nick

type Handler

type Handler interface {
	Handle(*Conn, *Line)
}

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 HandlerFunc

type HandlerFunc func(*Conn, *Line)

HandlerFunc allows a bare function with this signature to implement the Handler interface. It is used by Conn.HandleFunc.

func (HandlerFunc) Handle

func (hf HandlerFunc) Handle(conn *Conn, line *Line)

type Line

type Line struct {
	Tags                   map[string]string
	Nick, Ident, Host, Src string
	Cmd, Raw               string
	Args                   []string
	Time                   time.Time
}

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

func ParseLine

func ParseLine(s string) *Line

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 (*Line) Copy

func (l *Line) Copy() *Line

Copy returns a deep copy of the Line.

func (*Line) Public

func (line *Line) Public() bool

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) Target

func (line *Line) Target() string

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) Text

func (line *Line) Text() string

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.

type Remover

type Remover interface {
	Remove()
}

Removers allow for a handler that has been previously added to the client to be removed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL