mirror of https://github.com/fluffle/goirc
update README and comments so godoc creates nice helpful html documentation
This commit is contained in:
parent
07ff350dd0
commit
e5131515b8
|
@ -52,5 +52,11 @@ likely that this state tracking will become optional in the near future.
|
|||
|
||||
Sorry the documentation is crap. Use the source, Luke.
|
||||
|
||||
[Feedback](mailto:a.bramley@gmail.com) on design decisions is welcome. I am
|
||||
indebted to Matt Gruen for his work on
|
||||
[go-bot](http://code.google.com/p/go-bot/source/browse/irc.go) which inspired
|
||||
the re-organisation and channel-based communication structure of `*Conn.send()`
|
||||
and `*Conn.recv()`. I'm sure things could be more asynchronous, still.
|
||||
|
||||
This code is (c) 2009 Alex Bramley, and released under the same licence terms
|
||||
as Go itself.
|
||||
|
|
|
@ -4,7 +4,6 @@ package irc
|
|||
// send to the server using an Conn connection
|
||||
|
||||
import (
|
||||
// "fmt";
|
||||
"reflect"
|
||||
)
|
||||
|
||||
|
@ -12,89 +11,100 @@ import (
|
|||
// the symbol table and add methods/functions on the fly
|
||||
// [ CMD, FMT, FMTARGS ] etc.
|
||||
|
||||
// send a raw line to the server for debugging etc
|
||||
func (conn *Conn) Raw(s string) { conn.out <- s }
|
||||
// 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) { conn.out <- rawline }
|
||||
|
||||
// send a PASS command to the server
|
||||
func (conn *Conn) Pass(p string) { conn.out <- "PASS "+p }
|
||||
// Pass() sends a PASS command to the server
|
||||
func (conn *Conn) Pass(password string) { conn.out <- "PASS "+password }
|
||||
|
||||
// send a NICK command to the server
|
||||
func (conn *Conn) Nick(n string) { conn.out <- "NICK "+n }
|
||||
// Nick() sends a NICK command to the server
|
||||
func (conn *Conn) Nick(nick string) { conn.out <- "NICK "+nick }
|
||||
|
||||
// send a USER command to the server
|
||||
func (conn *Conn) User(u, n string) { conn.out <- "USER "+u+" 12 * :"+n }
|
||||
// User() sends a USER command to the server
|
||||
func (conn *Conn) User(ident, name string) {
|
||||
conn.out <- "USER "+ident+" 12 * :"+name
|
||||
}
|
||||
|
||||
// send a JOIN command to the server
|
||||
func (conn *Conn) Join(c string) { conn.out <- "JOIN "+c }
|
||||
// Join() sends a JOIN command to the server
|
||||
func (conn *Conn) Join(channel string) { conn.out <- "JOIN "+channel }
|
||||
|
||||
// send a PART command to the server
|
||||
func (conn *Conn) Part(c string, a ...) {
|
||||
msg := getStringMsg(a)
|
||||
// Part() sends a PART command to the server with an optional part message
|
||||
func (conn *Conn) Part(channel string, message ...) {
|
||||
msg := getStringMsg(message)
|
||||
if msg != "" {
|
||||
msg = " :" + msg
|
||||
}
|
||||
conn.out <- "PART "+c+msg
|
||||
conn.out <- "PART "+channel+msg
|
||||
}
|
||||
|
||||
// send a QUIT command to the server
|
||||
func (conn *Conn) Quit(a ...) {
|
||||
msg := getStringMsg(a)
|
||||
// Quit() sends a QUIT command to the server with an optional quit message
|
||||
func (conn *Conn) Quit(message ...) {
|
||||
msg := getStringMsg(message)
|
||||
if msg == "" {
|
||||
msg = "GoBye!"
|
||||
}
|
||||
conn.out <- "QUIT :"+msg
|
||||
}
|
||||
|
||||
// send a WHOIS command to the server
|
||||
func (conn *Conn) Whois(t string) { conn.out <- "WHOIS "+t }
|
||||
// Whois() sends a WHOIS command to the server
|
||||
func (conn *Conn) Whois(nick string) { conn.out <- "WHOIS "+nick }
|
||||
|
||||
// send a WHO command to the server
|
||||
func (conn *Conn) Who(t string) { conn.out <- "WHO "+t }
|
||||
//Who() sends a WHO command to the server
|
||||
func (conn *Conn) Who(nick string) { conn.out <- "WHO "+nick }
|
||||
|
||||
// send a PRIVMSG to the target t
|
||||
// Privmsg() sends a PRIVMSG to the target t
|
||||
func (conn *Conn) Privmsg(t, msg string) { conn.out <- "PRIVMSG "+t+" :"+msg }
|
||||
|
||||
// send a NOTICE to the target t
|
||||
// Notice() sends a NOTICE to the target t
|
||||
func (conn *Conn) Notice(t, msg string) { conn.out <- "NOTICE "+t+" :"+msg }
|
||||
|
||||
// send a (generic) CTCP to the target t
|
||||
func (conn *Conn) Ctcp(t, ctcp string, a ...) {
|
||||
msg := getStringMsg(a)
|
||||
// Ctcp() sends a (generic) CTCP message to the target t
|
||||
// with an optional argument
|
||||
func (conn *Conn) Ctcp(t, ctcp string, arg ...) {
|
||||
msg := getStringMsg(arg)
|
||||
if msg != "" {
|
||||
msg = " " + msg
|
||||
}
|
||||
conn.Privmsg(t, "\001"+ctcp+msg+"\001")
|
||||
}
|
||||
|
||||
// send a generic CTCP reply to the target t
|
||||
func (conn *Conn) CtcpReply(t, ctcp string, a ...) {
|
||||
msg := getStringMsg(a)
|
||||
// CtcpReply() sends a generic CTCP reply to the target t
|
||||
// with an optional argument
|
||||
func (conn *Conn) CtcpReply(t, ctcp string, arg ...) {
|
||||
msg := getStringMsg(arg)
|
||||
if msg != "" {
|
||||
msg = " " + msg
|
||||
}
|
||||
conn.Notice(t, "\001"+ctcp+msg+"\001")
|
||||
}
|
||||
|
||||
// send a CTCP "VERSION" to the target t
|
||||
// Version() sends a CTCP "VERSION" to the target t
|
||||
func (conn *Conn) Version(t string) { conn.Ctcp(t, "VERSION") }
|
||||
|
||||
// send a CTCP "ACTION" to the target t -- /me does stuff!
|
||||
// Action() sends a CTCP "ACTION" to the target t
|
||||
func (conn *Conn) Action(t, msg string) { conn.Ctcp(t, "ACTION", msg) }
|
||||
|
||||
// send a TOPIC command to the channel c
|
||||
func (conn *Conn) Topic(c string, a ...) {
|
||||
topic := getStringMsg(a)
|
||||
if topic != "" {
|
||||
topic = " :" + topic
|
||||
// Topic() sends a TOPIC command to the channel
|
||||
// Topic(channel) retrieves the current channel topic (see "332" handler)
|
||||
// Topic(channel, topic) sets the topic for the channel
|
||||
func (conn *Conn) Topic(channel string, topic ...) {
|
||||
t := getStringMsg(topic)
|
||||
if t != "" {
|
||||
t = " :" + t
|
||||
}
|
||||
conn.out <- "TOPIC "+c+topic
|
||||
conn.out <- "TOPIC "+channel+t
|
||||
}
|
||||
|
||||
// send a MODE command (this one gets complicated)
|
||||
// send a MODE command to the server. This one can get complicated if we try
|
||||
// to be too clever, so it's deliberately simple:
|
||||
// Mode(t) retrieves the user or channel modes for target t
|
||||
// Mode(t, "string"
|
||||
func (conn *Conn) Mode(t string, a ...) {
|
||||
mode := getStringMsg(a)
|
||||
// Mode(t, "modestring") sets user or channel modes for target t, where...
|
||||
// modestring == e.g. "+o <nick>" or "+ntk <key>" or "-is"
|
||||
// This means you'll need to do your own mode work. It may be linked in with
|
||||
// the state tracking and ChanMode/NickMode/ChanPrivs objects later...
|
||||
func (conn *Conn) Mode(t string, modestring ...) {
|
||||
mode := getStringMsg(modestring)
|
||||
if mode != "" {
|
||||
mode = " " + mode
|
||||
}
|
||||
|
|
|
@ -8,9 +8,10 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// the IRC connection object
|
||||
// An IRC connection is represented by this struct. Once connected, any errors
|
||||
// encountered are piped down *Conn.Err; this channel is closed on disconnect.
|
||||
type Conn struct {
|
||||
// Hostname, Nickname, etc.
|
||||
// Connection Hostname and Nickname
|
||||
Host string
|
||||
Me *Nick
|
||||
|
||||
|
@ -34,16 +35,19 @@ type Conn struct {
|
|||
nicks map[string]*Nick
|
||||
}
|
||||
|
||||
// We'll parse an incoming line into this struct
|
||||
// raw =~ ":nick!user@host cmd args[] :text"
|
||||
// src == "nick!user@host"
|
||||
// We parse an incoming line into this struct. Line.Cmd is used as the trigger
|
||||
// name for incoming event handlers, see *Conn.recv() for details.
|
||||
// Raw =~ ":nick!user@host cmd args[] :text"
|
||||
// Src == "nick!user@host"
|
||||
// Cmd == e.g. PRIVMSG, 332
|
||||
type Line struct {
|
||||
Nick, Ident, Host, Src string
|
||||
Cmd, Text, Raw string
|
||||
Args []string
|
||||
}
|
||||
|
||||
// construct a new IRC Connection object
|
||||
// Creates a new IRC connection object, but doesn't connect to anything so
|
||||
// that you can add event handlers to it. See AddHandler() for details.
|
||||
func New(nick, user, name string) *Conn {
|
||||
conn := new(Conn)
|
||||
conn.initialise()
|
||||
|
@ -64,8 +68,10 @@ func (conn *Conn) initialise() {
|
|||
conn.sock = nil
|
||||
}
|
||||
|
||||
// connect the IRC connection object to a host
|
||||
func (conn *Conn) Connect(host, pass string) os.Error {
|
||||
// Connect the IRC connection object to "host[:port]" which should be either
|
||||
// a hostname or an IP address, with an optional port defaulting to 6667.
|
||||
// You can also provide an optional connect password.
|
||||
func (conn *Conn) Connect(host string, pass ...) os.Error {
|
||||
if conn.connected {
|
||||
return os.NewError(fmt.Sprintf("irc.Connect(): already connected to %s, cannot connect to %s", conn.Host, host))
|
||||
}
|
||||
|
@ -87,8 +93,9 @@ func (conn *Conn) Connect(host, pass string) os.Error {
|
|||
go conn.send()
|
||||
go conn.recv()
|
||||
|
||||
if pass != "" {
|
||||
conn.Pass(pass)
|
||||
// see getStringMsg() in commands.go for what this does
|
||||
if p := getStringMsg(pass); p != "" {
|
||||
conn.Pass(p)
|
||||
}
|
||||
conn.Nick(conn.Me.Nick)
|
||||
conn.User(conn.Me.Ident, conn.Me.Name)
|
||||
|
@ -197,6 +204,8 @@ func (conn *Conn) shutdown() {
|
|||
fmt.Println("irc.shutdown(): shut down sockets and channels!")
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
|
|
@ -8,7 +8,19 @@ import (
|
|||
"strconv"
|
||||
)
|
||||
|
||||
// Add an event handler for a specific IRC command
|
||||
// AddHandler() adds an event handler for a specific IRC command.
|
||||
//
|
||||
// Handlers take the form of an anonymous function (currently):
|
||||
// func(conn *irc.Conn, line *irc.Line) {
|
||||
// // handler code here
|
||||
// }
|
||||
//
|
||||
// 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).
|
||||
func (conn *Conn) AddHandler(name string, f func(*Conn, *Line)) {
|
||||
n := strings.ToUpper(name)
|
||||
if e, ok := conn.events[n]; ok {
|
||||
|
|
|
@ -25,21 +25,26 @@ type Nick struct {
|
|||
}
|
||||
|
||||
// A struct representing the modes of an IRC Channel
|
||||
// (the ones we care about, at least)
|
||||
// see the MODE handler in setupEvents() for details
|
||||
// (the ones we care about, at least).
|
||||
//
|
||||
// See the MODE handler in setupEvents() for details of how this is maintained.
|
||||
type ChanMode struct {
|
||||
// MODE +p, +s, +t, +n, +m
|
||||
Private, Secret, ProtectedTopic, NoExternalMsg, Moderated bool
|
||||
|
||||
// MODE +i, +O, +z
|
||||
InviteOnly, OperOnly, SSLOnly bool
|
||||
|
||||
// MODE +k
|
||||
Key string
|
||||
|
||||
// MODE +l
|
||||
Limit int
|
||||
}
|
||||
|
||||
// A struct representing the modes of an IRC Nick (User Modes)
|
||||
// (again, only the ones we care about)
|
||||
//
|
||||
// This is only really useful for conn.Me, as we can't see other people's modes
|
||||
// without IRC operator privileges (and even then only on some IRCd's).
|
||||
type NickMode struct {
|
||||
|
@ -56,6 +61,9 @@ type ChanPrivs struct {
|
|||
/******************************************************************************\
|
||||
* Conn methods to create/look up nicks/channels
|
||||
\******************************************************************************/
|
||||
|
||||
// Creates a new *irc.Nick, initialises it, and stores it in *irc.Conn so it
|
||||
// can be properly tracked for state management purposes.
|
||||
func (conn *Conn) NewNick(nick, ident, name, host string) *Nick {
|
||||
n := &Nick{Nick: nick, Ident: ident, Name: name, Host: host, conn: conn}
|
||||
n.initialise()
|
||||
|
@ -63,6 +71,7 @@ func (conn *Conn) NewNick(nick, ident, name, host string) *Nick {
|
|||
return n
|
||||
}
|
||||
|
||||
// Returns an *irc.Nick for the nick n, if we're tracking it.
|
||||
func (conn *Conn) GetNick(n string) *Nick {
|
||||
if nick, ok := conn.nicks[n]; ok {
|
||||
return nick
|
||||
|
@ -70,6 +79,8 @@ func (conn *Conn) GetNick(n string) *Nick {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Creates a new *irc.Channel, initialises it, and stores it in *irc.Conn so it
|
||||
// can be properly tracked for state management purposes.
|
||||
func (conn *Conn) NewChannel(c string) *Channel {
|
||||
ch := &Channel{Name: c, conn: conn}
|
||||
ch.initialise()
|
||||
|
@ -77,6 +88,7 @@ func (conn *Conn) NewChannel(c string) *Channel {
|
|||
return ch
|
||||
}
|
||||
|
||||
// Returns an *irc.Channel for the channel c, if we're tracking it.
|
||||
func (conn *Conn) GetChannel(c string) *Channel {
|
||||
if ch, ok := conn.chans[c]; ok {
|
||||
return ch
|
||||
|
@ -87,11 +99,13 @@ func (conn *Conn) GetChannel(c string) *Channel {
|
|||
/******************************************************************************\
|
||||
* Channel methods for state management
|
||||
\******************************************************************************/
|
||||
|
||||
func (ch *Channel) initialise() {
|
||||
ch.Modes = new(ChanMode)
|
||||
ch.Nicks = make(map[*Nick]*ChanPrivs)
|
||||
}
|
||||
|
||||
// Associates an *irc.Nick with an *irc.Channel using a shared *irc.ChanPrivs
|
||||
func (ch *Channel) AddNick(n *Nick) {
|
||||
if _, ok := ch.Nicks[n]; !ok {
|
||||
ch.Nicks[n] = new(ChanPrivs)
|
||||
|
@ -101,6 +115,9 @@ func (ch *Channel) AddNick(n *Nick) {
|
|||
}
|
||||
}
|
||||
|
||||
// Disassociates an *irc.Nick from an *irc.Channel. Will call ch.Delete() if
|
||||
// the *irc.Nick being removed is the connection's nick. Will also call
|
||||
// n.DelChannel(ch) to remove the association from the perspective of *irc.Nick.
|
||||
func (ch *Channel) DelNick(n *Nick) {
|
||||
if _, ok := ch.Nicks[n]; ok {
|
||||
fmt.Printf("irc.Channel.DelNick(): deleting %s from %s\n", n.Nick, ch.Name)
|
||||
|
@ -116,6 +133,8 @@ func (ch *Channel) DelNick(n *Nick) {
|
|||
// consistency, and this would mean spewing an error message every delete
|
||||
}
|
||||
|
||||
// Stops the channel from being tracked by state tracking handlers. Also calls
|
||||
// n.DelChannel(ch) for all nicks that are associated with the channel.
|
||||
func (ch *Channel) Delete() {
|
||||
fmt.Printf("irc.Channel.Delete(): deleting %s\n", ch.Name)
|
||||
for n, _ := range ch.Nicks {
|
||||
|
@ -132,7 +151,11 @@ func (n *Nick) initialise() {
|
|||
n.Channels = make(map[*Channel]*ChanPrivs)
|
||||
}
|
||||
|
||||
// very slightly different to Channel.AddNick() ...
|
||||
// Associates an *irc.Channel with an *irc.Nick using a shared *irc.ChanPrivs
|
||||
//
|
||||
// Very slightly different to irc.Channel.AddNick() in that it tests for a
|
||||
// pre-existing association within the *irc.Nick object rather than the
|
||||
// *irc.Channel object before associating the two.
|
||||
func (n *Nick) AddChannel(ch *Channel) {
|
||||
if _, ok := n.Channels[ch]; !ok {
|
||||
ch.Nicks[n] = new(ChanPrivs)
|
||||
|
@ -142,6 +165,9 @@ func (n *Nick) AddChannel(ch *Channel) {
|
|||
}
|
||||
}
|
||||
|
||||
// Disassociates an *irc.Channel from an *irc.Nick. Will call n.Delete() if
|
||||
// the *irc.Nick is no longer on any channels we are tracking. Will also call
|
||||
// ch.DelNick(n) to remove the association from the perspective of *irc.Channel.
|
||||
func (n *Nick) DelChannel(ch *Channel) {
|
||||
if _, ok := n.Channels[ch]; ok {
|
||||
fmt.Printf("irc.Nick.DelChannel(): deleting %s from %s\n", n.Nick, ch.Name)
|
||||
|
@ -154,12 +180,16 @@ func (n *Nick) DelChannel(ch *Channel) {
|
|||
}
|
||||
}
|
||||
|
||||
// Signals to the tracking code that the *irc.Nick object should be tracked
|
||||
// under a "neu" nick rather than the old one.
|
||||
func (n *Nick) ReNick(neu string) {
|
||||
n.conn.nicks[n.Nick] = nil, false
|
||||
n.Nick = neu
|
||||
n.conn.nicks[n.Nick] = n
|
||||
}
|
||||
|
||||
// Stops the nick from being tracked by state tracking handlers. Also calls
|
||||
// ch.DelNick(n) for all nicks that are associated with the channel.
|
||||
func (n *Nick) Delete() {
|
||||
// we don't ever want to remove *our* nick from conn.nicks...
|
||||
if n != n.conn.Me {
|
||||
|
@ -174,6 +204,8 @@ func (n *Nick) Delete() {
|
|||
/******************************************************************************\
|
||||
* String() methods for all structs in this file for ease of debugging.
|
||||
\******************************************************************************/
|
||||
|
||||
// Map *irc.ChanMode fields to IRC mode characters
|
||||
var ChanModeToString = map[string]string{
|
||||
"Private": "p",
|
||||
"Secret": "s",
|
||||
|
@ -186,6 +218,8 @@ var ChanModeToString = map[string]string{
|
|||
"Key": "k",
|
||||
"Limit": "l",
|
||||
}
|
||||
|
||||
// Map *irc.NickMode fields to IRC mode characters
|
||||
var NickModeToString = map[string]string{
|
||||
"Invisible": "i",
|
||||
"Oper": "o",
|
||||
|
@ -193,6 +227,8 @@ var NickModeToString = map[string]string{
|
|||
"HiddenHost": "x",
|
||||
"SSL": "z",
|
||||
}
|
||||
|
||||
// Map *irc.ChanPrivs fields to IRC mode characters
|
||||
var ChanPrivToString = map[string]string{
|
||||
"Owner": "q",
|
||||
"Admin": "a",
|
||||
|
@ -200,6 +236,9 @@ var ChanPrivToString = map[string]string{
|
|||
"HalfOp": "h",
|
||||
"Voice": "v",
|
||||
}
|
||||
|
||||
// Map *irc.ChanPrivs fields to the symbols used to represent these modes
|
||||
// in NAMES and WHOIS responses
|
||||
var ChanPrivToModeChar = map[string]byte{
|
||||
"Owner": '~',
|
||||
"Admin": '&',
|
||||
|
@ -207,6 +246,8 @@ var ChanPrivToModeChar = map[string]byte{
|
|||
"HalfOp": '%',
|
||||
"Voice": '+',
|
||||
}
|
||||
|
||||
// Reverse mappings of the above datastructures
|
||||
var StringToChanMode, StringToNickMode, StringToChanPriv map[string]string
|
||||
var ModeCharToChanPriv map[byte]string
|
||||
|
||||
|
@ -230,6 +271,13 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns a string representing the channel. Looks like:
|
||||
// Channel: <channel name> e.g. #moo
|
||||
// Topic: <channel topic> e.g. Discussing the merits of cows!
|
||||
// Mode: <channel modes> e.g. +nsti
|
||||
// Nicks:
|
||||
// <nick>: <privs> e.g. CowMaster: +o
|
||||
// ...
|
||||
func (ch *Channel) String() string {
|
||||
str := "Channel: " + ch.Name + "\n\t"
|
||||
str += "Topic: " + ch.Topic + "\n\t"
|
||||
|
@ -241,6 +289,14 @@ func (ch *Channel) String() string {
|
|||
return str
|
||||
}
|
||||
|
||||
// Returns a string representing the nick. Looks like:
|
||||
// Nick: <nick name> e.g. CowMaster
|
||||
// Hostmask: <ident@host> e.g. moo@cows.org
|
||||
// Real Name: <real name> e.g. Steve "CowMaster" Bush
|
||||
// Modes: <nick modes> e.g. +z
|
||||
// Channels:
|
||||
// <channel>: <privs> e.g. #moo: +o
|
||||
// ...
|
||||
func (n *Nick) String() string {
|
||||
str := "Nick: " + n.Nick + "\n\t"
|
||||
str += "Hostmask: " + n.Ident + "@" + n.Host + "\n\t"
|
||||
|
@ -253,6 +309,8 @@ func (n *Nick) String() string {
|
|||
return str
|
||||
}
|
||||
|
||||
// Returns a string representing the channel modes. Looks like:
|
||||
// +npk key
|
||||
func (cm *ChanMode) String() string {
|
||||
str := "+"
|
||||
a := make([]string, 2)
|
||||
|
@ -287,6 +345,8 @@ func (cm *ChanMode) String() string {
|
|||
return str
|
||||
}
|
||||
|
||||
// Returns a string representing the nick modes. Looks like:
|
||||
// +iwx
|
||||
func (nm *NickMode) String() string {
|
||||
str := "+"
|
||||
v := reflect.Indirect(reflect.NewValue(nm)).(*reflect.StructValue)
|
||||
|
@ -306,6 +366,8 @@ func (nm *NickMode) String() string {
|
|||
return str
|
||||
}
|
||||
|
||||
// Returns a string representing the channel privileges. Looks like:
|
||||
// +o
|
||||
func (p *ChanPrivs) String() string {
|
||||
str := "+"
|
||||
v := reflect.Indirect(reflect.NewValue(p)).(*reflect.StructValue)
|
||||
|
|
Loading…
Reference in New Issue