update README and comments so godoc creates nice helpful html documentation

This commit is contained in:
Alex Bramley 2009-12-18 22:39:22 +00:00
parent 07ff350dd0
commit e5131515b8
5 changed files with 158 additions and 59 deletions

View File

@ -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.

View File

@ -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)
// Mode(t) retrieves the user or channel modes for target t
// Mode(t, "string"
func (conn *Conn) Mode(t string, a ...) {
mode := getStringMsg(a)
// 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, "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
}

View File

@ -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"

View File

@ -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 {

View File

@ -12,8 +12,8 @@ import (
type Channel struct {
Name, Topic string
Modes *ChanMode
Nicks map[*Nick]*ChanPrivs
conn *Conn
Nicks map[*Nick]*ChanPrivs
conn *Conn
}
// A struct representing an IRC nick
@ -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)