irc

package module
v4.0.0 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2022 License: MIT Imports: 11 Imported by: 11

README

go-irc

GoDoc Build Status Coverage Status

This package was originally created to only handle message parsing, but has since been expanded to include small abstractions around connections and a very general client type with some small conveniences.

This library is not designed to hide any of the IRC elements from you. If you just want to build a simple chat bot and don't want to deal with IRC in particular, there are a number of other libraries which provide a more full featured client if that's what you're looking for.

This library is meant to stay as simple as possible so it can be a building block for other packages.

This library aims for API compatibility whenever possible. New functions and other additions will not result in a major version increase unless they break the API. This library aims to follow the semver recommendations mentioned on gopkg.in.

This packages uses newer error handling APIs so, only go 1.13+ is officially supported.

Import Paths

All development happens on the master branch and when features are considered stable enough, a new release will be tagged.

  • gopkg.in/irc.v4 should be used to develop against the commits tagged as stable

Development

In order to run the tests, make sure all submodules are up to date. If you are just using this library, these are not needed.

Notes on Unstable APIs

Currently the ISupport and Tracker APIs are considered unstable - these may be broken or removed with minor version changes, so use them at your own risk.

Major Version Changes

v4
  • Added initial ISupport and Tracker support as unstable APIs
  • Drop the separate TagValue type
  • Drop Tags.GetTag
v3
  • Import path changed back to gopkg.in/irc.v3 without the version suffix.
v2
  • CTCP messages will no longer be rewritten. The decision was made that this library should pass through all messages without mangling them.
  • Remove Message.FromChannel as this is not always accurate, while Client.FromChannel should always be accurate.
v1

Initial release

Documentation

Overview

nolint

Index

Constants

View Source
const (
	// RFC1459
	RPL_TRACELINK         = "200"
	RPL_TRACECONNECTING   = "201"
	RPL_TRACEHANDSHAKE    = "202"
	RPL_TRACEUNKNOWN      = "203"
	RPL_TRACEOPERATOR     = "204"
	RPL_TRACEUSER         = "205"
	RPL_TRACESERVER       = "206"
	RPL_TRACENEWTYPE      = "208"
	RPL_STATSLINKINFO     = "211"
	RPL_STATSCOMMANDS     = "212"
	RPL_STATSCLINE        = "213"
	RPL_STATSNLINE        = "214"
	RPL_STATSILINE        = "215"
	RPL_STATSKLINE        = "216"
	RPL_STATSQLINE        = "217"
	RPL_STATSYLINE        = "218"
	RPL_ENDOFSTATS        = "219"
	RPL_UMODEIS           = "221"
	RPL_STATSLLINE        = "241"
	RPL_STATSUPTIME       = "242"
	RPL_STATSOLINE        = "243"
	RPL_STATSHLINE        = "244"
	RPL_LUSERCLIENT       = "251"
	RPL_LUSEROP           = "252"
	RPL_LUSERUNKNOWN      = "253"
	RPL_LUSERCHANNELS     = "254"
	RPL_LUSERME           = "255"
	RPL_ADMINME           = "256"
	RPL_ADMINLOC1         = "257"
	RPL_ADMINLOC2         = "258"
	RPL_ADMINEMAIL        = "259"
	RPL_TRACELOG          = "261"
	RPL_NONE              = "300"
	RPL_AWAY              = "301"
	RPL_USERHOST          = "302"
	RPL_ISON              = "303"
	RPL_UNAWAY            = "305"
	RPL_NOWAWAY           = "306"
	RPL_WHOISUSER         = "311"
	RPL_WHOISSERVER       = "312"
	RPL_WHOISOPERATOR     = "313"
	RPL_WHOWASUSER        = "314"
	RPL_ENDOFWHO          = "315"
	RPL_WHOISIDLE         = "317"
	RPL_ENDOFWHOIS        = "318"
	RPL_WHOISCHANNELS     = "319"
	RPL_LIST              = "322"
	RPL_LISTEND           = "323"
	RPL_CHANNELMODEIS     = "324"
	RPL_NOTOPIC           = "331"
	RPL_TOPIC             = "332"
	RPL_INVITING          = "341"
	RPL_VERSION           = "351"
	RPL_WHOREPLY          = "352"
	RPL_NAMREPLY          = "353"
	RPL_LINKS             = "364"
	RPL_ENDOFLINKS        = "365"
	RPL_ENDOFNAMES        = "366"
	RPL_BANLIST           = "367"
	RPL_ENDOFBANLIST      = "368"
	RPL_ENDOFWHOWAS       = "369"
	RPL_INFO              = "371"
	RPL_MOTD              = "372"
	RPL_ENDOFINFO         = "374"
	RPL_MOTDSTART         = "375"
	RPL_ENDOFMOTD         = "376"
	RPL_YOUREOPER         = "381"
	RPL_REHASHING         = "382"
	RPL_TIME              = "391"
	RPL_USERSSTART        = "392"
	RPL_USERS             = "393"
	RPL_ENDOFUSERS        = "394"
	RPL_NOUSERS           = "395"
	ERR_NOSUCHNICK        = "401"
	ERR_NOSUCHSERVER      = "402"
	ERR_NOSUCHCHANNEL     = "403"
	ERR_CANNOTSENDTOCHAN  = "404"
	ERR_TOOMANYCHANNELS   = "405"
	ERR_WASNOSUCHNICK     = "406"
	ERR_TOOMANYTARGETS    = "407"
	ERR_NOORIGIN          = "409"
	ERR_NORECIPIENT       = "411"
	ERR_NOTEXTTOSEND      = "412"
	ERR_NOTOPLEVEL        = "413"
	ERR_WILDTOPLEVEL      = "414"
	ERR_UNKNOWNCOMMAND    = "421"
	ERR_NOMOTD            = "422"
	ERR_NOADMININFO       = "423"
	ERR_FILEERROR         = "424"
	ERR_NONICKNAMEGIVEN   = "431"
	ERR_ERRONEUSNICKNAME  = "432"
	ERR_NICKNAMEINUSE     = "433"
	ERR_NICKCOLLISION     = "436"
	ERR_USERNOTINCHANNEL  = "441"
	ERR_NOTONCHANNEL      = "442"
	ERR_USERONCHANNEL     = "443"
	ERR_NOLOGIN           = "444"
	ERR_SUMMONDISABLED    = "445"
	ERR_USERSDISABLED     = "446"
	ERR_NOTREGISTERED     = "451"
	ERR_NEEDMOREPARAMS    = "461"
	ERR_ALREADYREGISTERED = "462"
	ERR_NOPERMFORHOST     = "463"
	ERR_PASSWDMISMATCH    = "464"
	ERR_YOUREBANNEDCREEP  = "465"
	ERR_KEYSET            = "467"
	ERR_CHANNELISFULL     = "471"
	ERR_UNKNOWNMODE       = "472"
	ERR_INVITEONLYCHAN    = "473"
	ERR_BANNEDFROMCHAN    = "474"
	ERR_BADCHANNELKEY     = "475"
	ERR_NOPRIVILEGES      = "481"
	ERR_CHANOPRIVSNEEDED  = "482"
	ERR_CANTKILLSERVER    = "483"
	ERR_NOOPERHOST        = "491"
	ERR_UMODEUNKNOWNFLAG  = "501"
	ERR_USERSDONTMATCH    = "502"

	// RFC1459 (Obsolete)
	RPL_SERVICEINFO     = "231"
	RPL_ENDOFSERVICES   = "232"
	RPL_SERVICE         = "233"
	RPL_WHOISCHANOP     = "316"
	RPL_LISTSTART       = "321"
	RPL_SUMMONING       = "342"
	RPL_KILLDONE        = "361"
	RPL_CLOSING         = "362"
	RPL_CLOSEEND        = "363"
	RPL_INFOSTART       = "373"
	RPL_MYPORTIS        = "384"
	ERR_YOUWILLBEBANNED = "466"
	ERR_NOSERVICEHOST   = "492"

	// RFC2812
	RPL_WELCOME          = "001"
	RPL_YOURHOST         = "002"
	RPL_CREATED          = "003"
	RPL_MYINFO           = "004"
	RPL_TRACESERVICE     = "207"
	RPL_TRACECLASS       = "209"
	RPL_SERVLIST         = "234"
	RPL_SERVLISTEND      = "235"
	RPL_STATSVLINE       = "240"
	RPL_STATSBLINE       = "247"
	RPL_STATSDLINE       = "250"
	RPL_TRACEEND         = "262"
	RPL_TRYAGAIN         = "263"
	RPL_UNIQOPIS         = "325"
	RPL_INVITELIST       = "346"
	RPL_ENDOFINVITELIST  = "347"
	RPL_EXCEPTLIST       = "348"
	RPL_ENDOFEXCEPTLIST  = "349"
	RPL_YOURESERVICE     = "383"
	ERR_NOSUCHSERVICE    = "408"
	ERR_BADMASK          = "415"
	ERR_UNAVAILRESOURCE  = "437"
	ERR_BADCHANMASK      = "476"
	ERR_NOCHANMODES      = "477"
	ERR_BANLISTFULL      = "478"
	ERR_RESTRICTED       = "484"
	ERR_UNIQOPRIVSNEEDED = "485"

	// RFC2812 (Obsolete)
	RPL_BOUNCE         = "005"
	RPL_TRACERECONNECT = "210"
	RPL_STATSPING      = "246"

	// IRCv3
	ERR_INVALIDCAPCMD   = "410" // Undernet?
	RPL_STARTTLS        = "670"
	ERR_STARTTLS        = "691"
	RPL_MONONLINE       = "730" // RatBox
	RPL_MONOFFLINE      = "731" // RatBox
	RPL_MONLIST         = "732" // RatBox
	RPL_ENDOFMONLIST    = "733" // RatBox
	ERR_MONLISTFULL     = "734" // RatBox
	RPL_WHOISKEYVALUE   = "760"
	RPL_KEYVALUE        = "761"
	RPL_METADATAEND     = "762"
	ERR_METADATALIMIT   = "764"
	ERR_TARGETINVALID   = "765"
	ERR_NOMATCHINGKEY   = "766"
	ERR_KEYINVALID      = "767"
	ERR_KEYNOTSET       = "768"
	ERR_KEYNOPERMISSION = "769"
	RPL_LOGGEDIN        = "900" // Charybdis/Atheme, IRCv3
	RPL_LOGGEDOUT       = "901" // Charybdis/Atheme, IRCv3
	ERR_NICKLOCKED      = "902" // Charybdis/Atheme, IRCv3
	RPL_SASLSUCCESS     = "903" // Charybdis/Atheme, IRCv3
	ERR_SASLFAIL        = "904" // Charybdis/Atheme, IRCv3
	ERR_SASLTOOLONG     = "905" // Charybdis/Atheme, IRCv3
	ERR_SASLABORTED     = "906" // Charybdis/Atheme, IRCv3
	ERR_SASLALREADY     = "907" // Charybdis/Atheme, IRCv3
	RPL_SASLMECHS       = "908" // Charybdis/Atheme, IRCv3

	// Other
	RPL_ISUPPORT = "005"
)

Variables

View Source
var (
	// ErrZeroLengthMessage is returned when parsing if the input is
	// zero-length.
	ErrZeroLengthMessage = errors.New("irc: cannot parse zero-length message")

	// ErrMissingDataAfterPrefix is returned when parsing if there is
	// no message data after the prefix.
	ErrMissingDataAfterPrefix = errors.New("irc: no message data after prefix")

	// ErrMissingDataAfterTags is returned when parsing if there is no
	// message data after the tags.
	ErrMissingDataAfterTags = errors.New("irc: no message data after tags")

	// ErrMissingCommand is returned when parsing if there is no
	// command in the parsed message.
	ErrMissingCommand = errors.New("irc: missing message command")
)

Functions

func EncodeTagValue

func EncodeTagValue(v string) string

EncodeTagValue converts a raw string to the format in the connection.

func MaskToRegex

func MaskToRegex(rawMask string) (*regexp.Regexp, error)

MaskToRegex converts an irc mask to a go Regexp for more convenient use. This should never return an error, but we have this here just in case.

func ParseTagValue

func ParseTagValue(v string) string

ParseTagValue parses an encoded tag value as a string. If you need to set a tag, you probably want to just set the string itself, so it will be encoded properly.

Types

type ChannelState

type ChannelState struct {
	Name  string
	Topic string
	Users map[string]struct{}
}

ChannelState represents the current state of a channel, including the name, topic, and all users in it.

type Client

type Client struct {
	*Conn

	ISupport *ISupportTracker
	Tracker  *Tracker
	// contains filtered or unexported fields
}

Client is a wrapper around irc.Conn which is designed to make common operations much simpler. It is safe for concurrent use.

func NewClient

func NewClient(rwc io.ReadWriteCloser, config ClientConfig) *Client

NewClient creates a client given an io stream and a client config.

func (*Client) CapAvailable

func (c *Client) CapAvailable(capName string) bool

CapAvailable allows you to check if a CAP is available on this server. Note that it will not be populated until after the CAP handshake is done, so it is recommended to wait to check this until after a message like 001.

func (*Client) CapEnabled

func (c *Client) CapEnabled(capName string) bool

CapEnabled allows you to check if a CAP is enabled for this connection. Note that it will not be populated until after the CAP handshake is done, so it is recommended to wait to check this until after a message like 001.

func (*Client) CapRequest

func (c *Client) CapRequest(capName string, required bool)

CapRequest allows you to request IRCv3 capabilities from the server during the handshake. The behavior is undefined if this is called before the handshake completes so it is recommended that this be called before Run. If the CAP is marked as required, the client will exit if that CAP could not be negotiated during the handshake.

func (*Client) CurrentNick

func (c *Client) CurrentNick() string

CurrentNick returns what the nick of the client is known to be at this point in time.

func (*Client) FromChannel

func (c *Client) FromChannel(m *Message) bool

FromChannel takes a Message representing a PRIVMSG and returns if that message came from a channel or directly from a user.

func (*Client) Run

func (c *Client) Run() error

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.

func (*Client) RunContext

func (c *Client) RunContext(ctx context.Context) error

RunContext is the same as Run but a context.Context can be passed in for cancelation.

type ClientConfig

type ClientConfig struct {
	// General connection information.
	Nick string
	Pass string
	User string
	Name string

	// If this is set to true, the ISupport value on the client struct will be
	// non-nil.
	EnableISupport bool

	// If this is set to true, the Tracker value on the client struct will be
	// non-nil.
	EnableTracker bool

	// Connection settings
	PingFrequency time.Duration
	PingTimeout   time.Duration

	// SendLimit is how frequent messages can be sent. If this is zero,
	// there will be no limit.
	SendLimit time.Duration

	// SendBurst is the number of messages which can be sent in a burst.
	SendBurst int

	// Handler is used for message dispatching.
	Handler Handler
}

ClientConfig is a structure used to configure a Client.

type Conn

type Conn struct {
	*Reader
	*Writer
}

Conn represents a simple IRC client. It embeds an irc.Reader and an irc.Writer.

func NewConn

func NewConn(rw io.ReadWriter) *Conn

NewConn creates a new Conn.

type Handler

type Handler interface {
	Handle(*Client, *Message)
}

Handler is a simple interface meant for dispatching a message from a Client connection.

type HandlerFunc

type HandlerFunc func(*Client, *Message)

HandlerFunc is a simple wrapper around a function which allows it to be used as a Handler.

func (HandlerFunc) Handle

func (f HandlerFunc) Handle(c *Client, m *Message)

Handle calls f(c, m).

type ISupportTracker

type ISupportTracker struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

ISupportTracker tracks the ISUPPORT values returned by servers and provides a convenient way to access them.

From http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt

005 RPL_ISUPPORT.

func NewISupportTracker

func NewISupportTracker() *ISupportTracker

NewISupportTracker creates a new tracker instance with a set of sane defaults if the server is missing them.

func (*ISupportTracker) GetList

func (t *ISupportTracker) GetList(key string) ([]string, bool)

GetList will check for list ISupport values.

func (*ISupportTracker) GetMap

func (t *ISupportTracker) GetMap(key string) (map[string]string, bool)

GetMap will check for map ISupport values.

func (*ISupportTracker) GetPrefixMap

func (t *ISupportTracker) GetPrefixMap() (map[rune]rune, bool)

GetPrefixMap gets the mapping of mode to symbol for the PREFIX value. Unfortunately, this is fairly specific, so it can only be used with PREFIX.

func (*ISupportTracker) GetRaw

func (t *ISupportTracker) GetRaw(key string) (string, bool)

GetRaw will get the raw ISupport values.

func (*ISupportTracker) Handle

func (t *ISupportTracker) Handle(msg *Message) error

Handle needs to be called for all 005 IRC messages. All other messages will be ignored.

func (*ISupportTracker) IsEnabled

func (t *ISupportTracker) IsEnabled(key string) bool

IsEnabled will check for boolean ISupport values. Note that for ISupport boolean true simply means the value exists.

type Message

type Message struct {
	// Each message can have IRCv3 tags
	Tags

	// Each message can have a Prefix
	*Prefix

	// Command is which command is being called.
	Command string

	// Params are all the arguments for the command.
	Params []string
}

Message represents a line parsed from the server.

func MustParseMessage

func MustParseMessage(line string) *Message

MustParseMessage calls ParseMessage and either returns the message or panics if an error is returned.

func ParseMessage

func ParseMessage(line string) (*Message, error)

ParseMessage takes a message string (usually a whole line) and parses it into a Message struct. This will return nil in the case of invalid messages.

func (*Message) Copy

func (m *Message) Copy() *Message

Copy will create a new copy of an message.

func (*Message) Param

func (m *Message) Param(i int) string

Param returns the i'th argument in the Message or an empty string if the requested arg does not exist.

func (*Message) String

func (m *Message) String() string

String ensures this is stringable.

func (*Message) Trailing

func (m *Message) Trailing() string

Trailing returns the last argument in the Message or an empty string if there are no args.

type Prefix

type Prefix struct {
	// Name will contain the nick of who sent the message, the
	// server who sent the message, or a blank string
	Name string

	// User will either contain the user who sent the message or a blank string
	User string

	// Host will either contain the host of who sent the message or a blank string
	Host string
}

Prefix represents the prefix of a message, generally the user who sent it.

func ParsePrefix

func ParsePrefix(line string) *Prefix

ParsePrefix takes an identity string and parses it into an identity struct. It will always return an Prefix struct and never nil.

func (*Prefix) Copy

func (p *Prefix) Copy() *Prefix

Copy will create a new copy of an Prefix.

func (*Prefix) String

func (p *Prefix) String() string

String ensures this is stringable.

type Reader

type Reader struct {
	// DebugCallback is called for each incoming message. The name of this may
	// not be stable.
	DebugCallback func(string)
	// contains filtered or unexported fields
}

Reader is the incoming side of a connection. The data will be buffered, so do not re-use the io.Reader used to create the irc.Reader.

func NewReader

func NewReader(r io.Reader) *Reader

NewReader creates an irc.Reader from an io.Reader. Note that once a reader is passed into this function, you should no longer use it as it is being used inside a bufio.Reader so you cannot rely on only the amount of data for a Message being read when you call ReadMessage.

func (*Reader) ReadMessage

func (r *Reader) ReadMessage() (*Message, error)

ReadMessage returns the next message from the stream or an error. It ignores empty messages.

type Tags

type Tags map[string]string

Tags represents the IRCv3 message tags.

func ParseTags

func ParseTags(line string) Tags

ParseTags takes a tag string and parses it into a tag map. It will always return a tag map, even if there are no valid tags.

func (Tags) Copy

func (t Tags) Copy() Tags

Copy will create a new copy of all IRC tags attached to this message.

func (Tags) String

func (t Tags) String() string

String ensures this is stringable.

type Tracker

type Tracker struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

Tracker provides a convenient interface to track users, the channels they are in, and what modes they have in those channels.

func NewTracker

func NewTracker(isupport *ISupportTracker) *Tracker

NewTracker creates a new tracker instance.

func (*Tracker) GetChannel

func (t *Tracker) GetChannel(name string) *ChannelState

GetChannel will look up the ChannelState for a given channel name. It will return nil if the channel is unknown.

func (*Tracker) Handle

func (t *Tracker) Handle(msg *Message) error

Handle needs to be called for all 001, 332, 353, JOIN, TOPIC, PART, KICK, QUIT, and NICK messages. All other messages will be ignored. Note that this will not handle calling the underlying ISupportTracker's Handle method.

func (*Tracker) ListChannels

func (t *Tracker) ListChannels() []string

ListChannels will list the names of all known channels.

type Writer

type Writer struct {
	// DebugCallback is called for each outgoing message. The name of this may
	// not be stable.
	DebugCallback func(line string)

	// WriteCallback is called for each outgoing message. It needs to write the
	// message to the connection. Note that this API is not a part of the semver
	// stability guarantee.
	WriteCallback func(w *Writer, line string) error
	// contains filtered or unexported fields
}

Writer is the outgoing side of a connection.

func NewWriter

func NewWriter(w io.Writer) *Writer

NewWriter creates an irc.Writer from an io.Writer.

func (*Writer) RawWrite

func (w *Writer) RawWrite(data []byte) (int, error)

RawWrite will write the given data to the underlying connection, skipping the WriteCallback. This is meant to be used by implementations of the WriteCallback to write data directly to the stream. Otherwise, it is recommended to avoid this function and use one of the other helpers. Also note that it will not append \r\n to the end of the line.

func (*Writer) Write

func (w *Writer) Write(line string) error

Write is a simple function which will write the given line to the underlying connection.

func (*Writer) WriteMessage

func (w *Writer) WriteMessage(m *Message) error

WriteMessage writes the given message to the stream.

func (*Writer) Writef

func (w *Writer) Writef(format string, args ...interface{}) error

Writef is a wrapper around the connection's Write method and fmt.Sprintf. Simply use it to send a message as you would normally use fmt.Printf.

Jump to

Keyboard shortcuts

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