diff --git a/README.md b/README.md index 2af9ecf..72786dc 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,17 @@ You can build the test client also with: make ./gobot -This will connect to freenode and join #go-lang by default, so be careful ;-) +This will connect to freenode and join `#go-lang` by default, so be careful ;-) ### Using the framework The test client provides a good (if basic) example of how to use the framework. -Reading irc/handlers.go gives a more in-depth look at how handlers can be +Reading `irc/handlers.go` gives a more in-depth look at how handlers can be written. Commands to be sent to the server (e.g. PRIVMSG) are methods of the -main \*irc.Conn object, and can be found in irc/commands.go (not all of the +main `*irc.Conn` object, and can be found in `irc/commands.go` (not all of the possible IRC commands are implemented yet). Events are produced directly from the messages from the IRC server, so you have to handle e.g. "332" for -RPL\_TOPIC to get the topic for a channel. +`RPL_TOPIC` to get the topic for a channel. The vast majority of handlers implemented within the framework implement state tracking of all nicks in channels that the client is also present in. It's diff --git a/client.go b/client.go index a88399c..ae4f3b5 100644 --- a/client.go +++ b/client.go @@ -1,73 +1,70 @@ package main import ( - "./irc/_obj/irc"; - "fmt"; - "os"; - "bufio"; - "strings"; + "./irc/_obj/irc" + "fmt" + "os" + "bufio" + "strings" ) func main() { // create new IRC connection - c := irc.New("GoTest", "gotest", "GoBot"); + c := irc.New("GoTest", "gotest", "GoBot") c.AddHandler("connected", - func(conn *irc.Conn, line *irc.Line) { - conn.Join("#go-lang"); - } - ); + func(conn *irc.Conn, line *irc.Line) { conn.Join("#go-nuts") }) // connect to server if err := c.Connect("irc.freenode.net", ""); err != nil { - fmt.Printf("Connection error: %s\n", err); - return; + fmt.Printf("Connection error: %s\n", err) + return } // set up a goroutine to read commands from stdin - in := make(chan string, 4); - reallyquit := false; + in := make(chan string, 4) + reallyquit := false go func() { - con := bufio.NewReader(os.Stdin); + con := bufio.NewReader(os.Stdin) for { - s, err := con.ReadString('\n'); + s, err := con.ReadString('\n') if err != nil { // wha?, maybe ctrl-D... - close(in); - break; + close(in) + break } // no point in sending empty lines down the channel if len(s) > 2 { in <- s[0:len(s)-1] } } - }(); + }() // set up a goroutine to do parsey things with the stuff from stdin go func() { for { if closed(in) { - break; + break } - cmd := <-in; + cmd := <-in if cmd[0] == ':' { switch idx := strings.Index(cmd, " "); { - case idx == -1: - continue; - case cmd[1] == 'q': - reallyquit = true; - c.Quit(cmd[idx+1:len(cmd)]); - case cmd[1] == 'j': - c.Join(cmd[idx+1:len(cmd)]); - case cmd[1] == 'p': - c.Part(cmd[idx+1:len(cmd)]); - case cmd[1] == 'd': - fmt.Printf(c.String()); + case idx == -1: + continue + case cmd[1] == 'q': + reallyquit = true + c.Quit(cmd[idx+1 : len(cmd)]) + case cmd[1] == 'j': + c.Join(cmd[idx+1 : len(cmd)]) + case cmd[1] == 'p': + c.Part(cmd[idx+1 : len(cmd)]) + case cmd[1] == 'd': + fmt.Printf(c.String()) } } else { c.Raw(cmd) } } - }(); + }() // stall here waiting for asplode on error channel for { @@ -76,17 +73,17 @@ func main() { // server for some reason (e.g. quit, kill or ping timeout) // if we don't really want to quit, reconnect! if !reallyquit { - fmt.Println("Reconnecting..."); + fmt.Println("Reconnecting...") if err := c.Connect("irc.freenode.net", ""); err != nil { - fmt.Printf("Connection error: %s\n", err); - break; + fmt.Printf("Connection error: %s\n", err) + break } - continue; + continue } - break; + break } if err := <-c.Err; err != nil { - fmt.Printf("goirc error: %s\n", err); + fmt.Printf("goirc error: %s\n", err) } } } diff --git a/irc/commands.go b/irc/commands.go index f06fd8d..708915b 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -4,123 +4,101 @@ package irc // send to the server using an Conn connection import ( -// "fmt"; - "reflect"; + // "fmt"; + "reflect" ) // This could be a lot less ugly with the ability to manipulate // the symbol table and add methods/functions on the fly -// [ CMD, FMT, FMTARGS ] etc. +// [ CMD, FMT, FMTARGS ] etc. // send a raw line to the server for debugging etc -func (conn *Conn) Raw(s string) { - conn.out <- s -} +func (conn *Conn) Raw(s string) { conn.out <- s } // send a PASS command to the server -func (conn *Conn) Pass(p string) { - conn.out <- "PASS " + p -} +func (conn *Conn) Pass(p string) { conn.out <- "PASS "+p } // send a NICK command to the server -func (conn *Conn) Nick(n string) { - conn.out <- "NICK " + n -} +func (conn *Conn) Nick(n string) { conn.out <- "NICK "+n } // send a USER command to the server -func (conn *Conn) User(u, n string) { - conn.out <- "USER " + u + " 12 * :" + n -} +func (conn *Conn) User(u, n string) { conn.out <- "USER "+u+" 12 * :"+n } // send a JOIN command to the server -func (conn *Conn) Join(c string) { - conn.out <- "JOIN " + c -} +func (conn *Conn) Join(c string) { conn.out <- "JOIN "+c } // send a PART command to the server func (conn *Conn) Part(c string, a ...) { - msg := getStringMsg(a); + msg := getStringMsg(a) if msg != "" { msg = " :" + msg } - conn.out <- "PART " + c + msg + conn.out <- "PART "+c+msg } // send a QUIT command to the server func (conn *Conn) Quit(a ...) { - msg := getStringMsg(a); + msg := getStringMsg(a) if msg == "" { msg = "GoBye!" } - conn.out <- "QUIT :" + msg + conn.out <- "QUIT :"+msg } // send a WHOIS command to the server -func (conn *Conn) Whois(t string) { - conn.out <- "WHOIS " + t -} +func (conn *Conn) Whois(t string) { conn.out <- "WHOIS "+t } // send a WHO command to the server -func (conn *Conn) Who(t string) { - conn.out <- "WHO " + t -} +func (conn *Conn) Who(t string) { conn.out <- "WHO "+t } // send a PRIVMSG to the target t -func (conn *Conn) Privmsg(t, msg string) { - conn.out <- "PRIVMSG " + t + " :" + msg -} +func (conn *Conn) Privmsg(t, msg string) { conn.out <- "PRIVMSG "+t+" :"+msg } // send a NOTICE to the target t -func (conn *Conn) Notice(t, msg string) { - conn.out <- "NOTICE " + t + " :" +msg -} +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); + msg := getStringMsg(a) if msg != "" { msg = " " + msg } - conn.Privmsg(t, "\001" + ctcp + msg + "\001") + 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); + msg := getStringMsg(a) if msg != "" { msg = " " + msg } - conn.Notice(t, "\001" + ctcp + msg + "\001") + conn.Notice(t, "\001"+ctcp+msg+"\001") } // send a CTCP "VERSION" to the target t -func (conn *Conn) Version(t string) { - conn.Ctcp(t, "VERSION") -} +func (conn *Conn) Version(t string) { conn.Ctcp(t, "VERSION") } // send a CTCP "ACTION" to the target t -- /me does stuff! -func (conn *Conn) Action(t, msg string) { - conn.Ctcp(t, "ACTION", msg) -} +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); + topic := getStringMsg(a) if topic != "" { topic = " :" + topic } - conn.out <- "TOPIC " + c + topic + conn.out <- "TOPIC "+c+topic } // 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); + mode := getStringMsg(a) if mode != "" { mode = " " + mode } - conn.out <- "MODE " + t + mode + conn.out <- "MODE "+t+mode } func getStringMsg(a ...) (msg string) { @@ -128,7 +106,7 @@ func getStringMsg(a ...) (msg string) { // the below stolen and munged from fmt/print.go func getString() if v := reflect.NewValue(a).(*reflect.StructValue); v.NumField() > 0 { if s, ok := v.Field(0).(*reflect.StringValue); ok { - return s.Get(); + return s.Get() } if b, ok := v.Interface().([]byte); ok { return string(b) diff --git a/irc/connection.go b/irc/connection.go index c5305a5..4d4c644 100644 --- a/irc/connection.go +++ b/irc/connection.go @@ -1,76 +1,76 @@ package irc import ( - "bufio"; - "os"; - "net"; - "fmt"; - "strings"; + "bufio" + "os" + "net" + "fmt" + "strings" ) // the IRC connection object type Conn struct { // Hostname, Nickname, etc. - Host string; - Me *Nick; + Host string + Me *Nick // I/O stuff to server - sock *net.TCPConn; - io *bufio.ReadWriter; - in chan *Line; - out chan string; - connected bool; + sock *net.TCPConn + io *bufio.ReadWriter + in chan *Line + out chan string + connected bool // Error channel to transmit any fail back to the user - Err chan os.Error; + Err chan os.Error // Event handler mapping - events map[string] []func (*Conn, *Line); + events map[string][]func(*Conn, *Line) // Map of channels we're on - chans map[string] *Channel; + chans map[string]*Channel // Map of nicks we know about - nicks map[string] *Nick; + nicks map[string]*Nick } // We'll parse an incoming line into this struct // raw =~ ":nick!user@host cmd args[] :text" // src == "nick!user@host" type Line struct { - Nick, Ident, Host, Src string; - Cmd, Text, Raw string; - Args []string; + Nick, Ident, Host, Src string + Cmd, Text, Raw string + Args []string } // construct a new IRC Connection object func New(nick, user, name string) *Conn { - conn := new(Conn); - conn.initialise(); - conn.Me = conn.NewNick(nick, user, name, ""); - conn.setupEvents(); - return conn; + conn := new(Conn) + conn.initialise() + conn.Me = conn.NewNick(nick, user, name, "") + conn.setupEvents() + return conn } func (conn *Conn) initialise() { // allocate meh some memoraaaahh - fmt.Println("irc.initialise(): initialising..."); - conn.nicks = make(map[string] *Nick); - conn.chans = make(map[string] *Channel); - conn.in = make(chan *Line, 32); - conn.out = make(chan string, 32); - conn.Err = make(chan os.Error, 4); - conn.io = nil; - conn.sock = nil; + fmt.Println("irc.initialise(): initialising...") + conn.nicks = make(map[string]*Nick) + conn.chans = make(map[string]*Channel) + conn.in = make(chan *Line, 32) + conn.out = make(chan string, 32) + conn.Err = make(chan os.Error, 4) + conn.io = nil + conn.sock = nil } // connect the IRC connection object to a host func (conn *Conn) Connect(host, pass string) os.Error { if conn.connected { - return os.NewError(fmt.Sprintf("irc.Connect(): already connected to %s, cannot connect to %s", conn.Host, host)); + return os.NewError(fmt.Sprintf("irc.Connect(): already connected to %s, cannot connect to %s", conn.Host, host)) } if !hasPort(host) { - host += ":6667"; + host += ":6667" } if addr, err := net.ResolveTCPAddr(host); err != nil { @@ -78,99 +78,94 @@ func (conn *Conn) Connect(host, pass string) os.Error { } else if conn.sock, err = net.DialTCP("tcp", nil, addr); err != nil { return err } - fmt.Println("irc.Connect(): connected happily..."); - conn.Host = host; + fmt.Println("irc.Connect(): connected happily...") + conn.Host = host conn.io = bufio.NewReadWriter( bufio.NewReader(conn.sock), - bufio.NewWriter(conn.sock), - ); - go conn.send(); - go conn.recv(); + bufio.NewWriter(conn.sock)) + go conn.send() + go conn.recv() if pass != "" { conn.Pass(pass) } - conn.Nick(conn.Me.Nick); - conn.User(conn.Me.Ident, conn.Me.Name); + conn.Nick(conn.Me.Nick) + conn.User(conn.Me.Ident, conn.Me.Name) - go conn.runLoop(); - fmt.Println("irc.Connect(): launched runLoop() goroutine."); - return nil; + go conn.runLoop() + fmt.Println("irc.Connect(): launched runLoop() goroutine.") + return nil } // dispatch a nicely formatted os.Error to the error channel -func (conn *Conn) error(s string, a ...) { - conn.Err <- os.NewError(fmt.Sprintf(s, a)); -} +func (conn *Conn) error(s string, a ...) { conn.Err <- os.NewError(fmt.Sprintf(s, a)) } // copied from http.client for great justice -func hasPort(s string) bool { - return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") -} +func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } // dispatch input from channel as \r\n terminated line to peer func (conn *Conn) send() { for { - line := <-conn.out; + line := <-conn.out if closed(conn.out) { - break; + break } if err := conn.io.WriteString(line + "\r\n"); err != nil { - conn.error("irc.send(): %s", err.String()); - conn.shutdown(); - break; + conn.error("irc.send(): %s", err.String()) + conn.shutdown() + break } - conn.io.Flush(); - fmt.Println("-> " + line); + conn.io.Flush() + fmt.Println("-> " + line) } } // receive one \r\n terminated line from peer, parse and dispatch it func (conn *Conn) recv() { for { - s, err := conn.io.ReadString('\n'); + s, err := conn.io.ReadString('\n') if err != nil { - conn.error("irc.recv(): %s", err.String()); - conn.shutdown(); - break; + conn.error("irc.recv(): %s", err.String()) + conn.shutdown() + break } // chop off \r\n - s = s[0:len(s)-2]; - fmt.Println("<- " + s); + s = s[0 : len(s)-2] + fmt.Println("<- " + s) - line := &Line{Raw: s}; + line := &Line{Raw: s} 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:len(s)]; + line.Src, s = s[1:idx], s[idx+1:len(s)] } else { // pretty sure we shouldn't get here ... - line.Src = s[1:len(s)]; - conn.in <- line; - continue; + line.Src = s[1:len(s)] + conn.in <- line + continue } // 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, "@"); + line.Host = line.Src + nidx, uidx := strings.Index(line.Src, "!"), strings.Index(line.Src, "@") if uidx != -1 && nidx != -1 { - line.Nick = line.Src[0:nidx]; - line.Ident = line.Src[nidx+1:uidx]; - line.Host = line.Src[uidx+1:len(line.Src)]; + line.Nick = line.Src[0:nidx] + line.Ident = line.Src[nidx+1 : uidx] + line.Host = line.Src[uidx+1 : len(line.Src)] } } // now we're here, we've parsed a :nick!user@host or :server off // s should contain "cmd args[] :text" - args := strings.Split(s, " :", 2); + args := strings.Split(s, " :", 2) if len(args) > 1 { - line.Text = args[1]; + line.Text = args[1] } - args = strings.Split(args[0], " ", 0); - line.Cmd = strings.ToUpper(args[0]); + args = strings.Split(args[0], " ", 0) + line.Cmd = strings.ToUpper(args[0]) if len(args) > 1 { - line.Args = args[1:len(args)]; + line.Args = args[1:len(args)] } conn.in <- line } @@ -179,50 +174,49 @@ func (conn *Conn) recv() { func (conn *Conn) runLoop() { for { if closed(conn.in) { - break; + break } select { - case line := <-conn.in: - conn.dispatchEvent(line); + case line := <-conn.in: + conn.dispatchEvent(line) } } - fmt.Println("irc.runLoop(): Exited runloop..."); + fmt.Println("irc.runLoop(): Exited runloop...") // if we fall off the end here due to shutdown, // reinit everything once the runloop is done // so that Connect() can be called again. - conn.initialise(); + conn.initialise() } func (conn *Conn) shutdown() { - close(conn.in); - close(conn.out); - close(conn.Err); - conn.connected = false; - conn.sock.Close(); - fmt.Println("irc.shutdown(): shut down sockets and channels!"); + close(conn.in) + close(conn.out) + close(conn.Err) + conn.connected = false + conn.sock.Close() + fmt.Println("irc.shutdown(): shut down sockets and channels!") } func (conn *Conn) String() string { - str := "GoIRC Connection\n"; - str += "----------------\n\n"; + str := "GoIRC Connection\n" + str += "----------------\n\n" if conn.connected { str += "Connected to " + conn.Host + "\n\n" } else { - str += "Not currently connected!\n\n"; + str += "Not currently connected!\n\n" } - str += conn.Me.String() + "\n"; - str += "GoIRC Channels\n"; - str += "--------------\n\n"; + str += conn.Me.String() + "\n" + str += "GoIRC Channels\n" + str += "--------------\n\n" for _, ch := range conn.chans { str += ch.String() + "\n" } - str += "GoIRC NickNames\n"; - str += "---------------\n\n"; + str += "GoIRC NickNames\n" + str += "---------------\n\n" for _, n := range conn.nicks { if n != conn.Me { str += n.String() + "\n" } } - return str; + return str } - diff --git a/irc/handlers.go b/irc/handlers.go index 77aac0d..29ed521 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -4,28 +4,28 @@ package irc // to manage tracking an irc connection etc. import ( - "strings"; - "strconv"; + "strings" + "strconv" ) // Add an event handler for a specific IRC command -func (conn *Conn) AddHandler(name string, f func (*Conn, *Line)) { - n := strings.ToUpper(name); +func (conn *Conn) AddHandler(name string, f func(*Conn, *Line)) { + n := strings.ToUpper(name) if e, ok := conn.events[n]; ok { if len(e) == cap(e) { // crap, we're full. expand e by another 10 handler slots - ne := make([]func (*Conn, *Line), len(e), len(e)+10); - for i := 0; i 2 && line.Text[0] == '\001' && line.Text[len(line.Text)-1] == '\001' { // WOO, it's a CTCP message - t := strings.Split(line.Text[1:len(line.Text)-1], " ", 2); + t := strings.Split(line.Text[1:len(line.Text)-1], " ", 2) if c := strings.ToUpper(t[0]); c == "ACTION" { // make a CTCP ACTION it's own event a-la PRIVMSG - line.Cmd = c; + line.Cmd = c } else { // otherwise, dispatch a generic CTCP event that // contains the type of CTCP in line.Args[0] - line.Cmd = "CTCP"; - a := make([]string, len(line.Args)+1); - a[0] = c; - for i:=0; i 1 { - // for some CTCP messages this could make more sense + // for some CTCP messages this could make more sense // in line.Args[], but meh. MEH, I say. - line.Text = t[1]; + line.Text = t[1] } } if funcs, ok := conn.events[line.Cmd]; ok { @@ -75,7 +75,7 @@ func (conn *Conn) dispatchEvent(line *Line) { // sets up the internal event handlers to do useful things with lines // XXX: is there a better way of doing this? // Turns out there may be but it's not actually implemented in the language yet -// according to the people on freenode/#go-nuts ... :-( +// according to the people on freenode/#go-nuts ... :-( // see: http://golang.org/doc/go_spec.html#Method_expressions for details // I think this means we should be able to do something along the lines of: // conn.AddHandler("event", (*Conn).h_handler); @@ -83,144 +83,150 @@ func (conn *Conn) dispatchEvent(line *Line) { // func (conn *Conn) h_handler(line *Line) {} // in the future, but for now the compiler throws a hissy fit. func (conn *Conn) setupEvents() { - conn.events = make(map[string] []func(*Conn, *Line)); + conn.events = make(map[string][]func(*Conn, *Line)) // Basic ping/pong handler - conn.AddHandler("PING", func(conn *Conn, line *Line) { - conn.Raw("PONG :" + line.Text); - }); + conn.AddHandler("PING", func(conn *Conn, line *Line) { conn.Raw("PONG :" + line.Text) }) // Handler to trigger a "CONNECTED" event on receipt of numeric 001 conn.AddHandler("001", func(conn *Conn, line *Line) { // we're connected! - conn.connected = true; - conn.dispatchEvent(&Line{Cmd: "CONNECTED"}); + conn.connected = true + conn.dispatchEvent(&Line{Cmd: "CONNECTED"}) // and we're being given our hostname (from the server's perspective) if ridx := strings.LastIndex(line.Text, " "); ridx != -1 { - h := line.Text[ridx+1:len(line.Text)]; + h := line.Text[ridx+1 : len(line.Text)] if idx := strings.Index(h, "@"); idx != -1 { - conn.Me.Host = h[idx+1:len(h)]; + conn.Me.Host = h[idx+1 : len(h)] } } - }); + }) // 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 + :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" conn.AddHandler("433", func(conn *Conn, line *Line) { // Args[1] is the new nick we were attempting to acquire - conn.Nick(line.Args[1] + "_"); - }); + conn.Nick(line.Args[1] + "_") + }) // Handler NICK messages to inform us about nick changes conn.AddHandler("NICK", func(conn *Conn, line *Line) { // all nicks should be handled the same way, our own included if n := conn.GetNick(line.Nick); n != nil { - n.ReNick(line.Text); + n.ReNick(line.Text) } else { - conn.error("irc.NICK(): buh? unknown nick %s.", line.Nick); + conn.error("irc.NICK(): buh? unknown nick %s.", line.Nick) } - }); + }) // Handle VERSION requests and CTCP PING conn.AddHandler("CTCP", func(conn *Conn, line *Line) { if line.Args[0] == "VERSION" { - conn.CtcpReply(line.Nick, "VERSION", "powered by goirc..."); + conn.CtcpReply(line.Nick, "VERSION", "powered by goirc...") } else if line.Args[0] == "PING" { - conn.CtcpReply(line.Nick, "PING", line.Text); + conn.CtcpReply(line.Nick, "PING", line.Text) } - }); + }) // Handle JOINs to channels to maintain state conn.AddHandler("JOIN", func(conn *Conn, line *Line) { - ch := conn.GetChannel(line.Text); - n := conn.GetNick(line.Nick); + ch := conn.GetChannel(line.Text) + n := conn.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 n == nil && ch == nil if n != conn.Me { - conn.error("irc.JOIN(): buh? JOIN to unknown channel %s recieved from (non-me) nick %s", line.Text, line.Nick); - return; + conn.error("irc.JOIN(): buh? JOIN to unknown channel %s recieved from (non-me) nick %s", line.Text, line.Nick) + return } - ch = conn.NewChannel(line.Text); + ch = conn.NewChannel(line.Text) // 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(ch.Name); + conn.Mode(ch.Name) } if n == nil { // this is the first we've seen of this nick - n = conn.NewNick(line.Nick, line.Ident, "", line.Host); + n = conn.NewNick(line.Nick, line.Ident, "", line.Host) // since we don't know much about this nick, ask server for info - conn.Whois(n.Nick); + conn.Whois(n.Nick) } // this takes care of both nick and channel linking \o/ - ch.AddNick(n); - }); + ch.AddNick(n) + }) // Handle PARTs from channels to maintain state conn.AddHandler("PART", func(conn *Conn, line *Line) { - ch := conn.GetChannel(line.Args[0]); - n := conn.GetNick(line.Nick); + ch := conn.GetChannel(line.Args[0]) + n := conn.GetNick(line.Nick) if ch != nil && n != nil { - ch.DelNick(n); + ch.DelNick(n) } else { - conn.error("irc.PART(): buh? PART of channel %s by nick %s", line.Args[0], line.Nick); + conn.error("irc.PART(): buh? PART of channel %s by nick %s", line.Args[0], line.Nick) } - }); + }) // Handle KICKs from channels to maintain state conn.AddHandler("KICK", func(conn *Conn, line *Line) { // XXX: this won't handle autorejoining channels on KICK // it's trivial to do this in a seperate handler... - ch := conn.GetChannel(line.Args[0]); - n := conn.GetNick(line.Args[1]); + ch := conn.GetChannel(line.Args[0]) + n := conn.GetNick(line.Args[1]) if ch != nil && n != nil { - ch.DelNick(n); + ch.DelNick(n) } else { - conn.error("irc.KICK(): buh? KICK from channel %s of nick %s", line.Args[0], line.Args[1]); + conn.error("irc.KICK(): buh? KICK from channel %s of nick %s", line.Args[0], line.Args[1]) } - }); + }) // Handle other people's QUITs conn.AddHandler("QUIT", func(conn *Conn, line *Line) { if n := conn.GetNick(line.Nick); n != nil { - n.Delete(); + n.Delete() } else { - conn.error("irc.QUIT(): buh? QUIT from unknown nick %s", line.Nick); + conn.error("irc.QUIT(): buh? QUIT from unknown nick %s", line.Nick) } - }); + }) // Handle MODE changes for channels we know about (and our nick personally) // this is moderately ugly. suggestions for improvement welcome conn.AddHandler("MODE", func(conn *Conn, line *Line) { // channel modes first if ch := conn.GetChannel(line.Args[0]); ch != nil { - modeargs := line.Args[2:len(line.Args)]; - var modeop bool; // true => add mode, false => remove mode - var modestr string; + modeargs := line.Args[2:len(line.Args)] + var modeop bool // true => add mode, false => remove mode + var modestr string for i := 0; i < len(line.Args[1]); i++ { switch m := line.Args[1][i]; m { case '+': - modeop = true; - modestr = string(m); + modeop = true + modestr = string(m) case '-': - modeop = false; - modestr = string(m); - case 'i': ch.Modes.InviteOnly = modeop; - case 'm': ch.Modes.Moderated = modeop; - case 'n': ch.Modes.NoExternalMsg = modeop; - case 'p': ch.Modes.Private = modeop; - case 's': ch.Modes.Secret = modeop; - case 't': ch.Modes.ProtectedTopic = modeop; - case 'z': ch.Modes.SSLOnly = modeop; - case 'O': ch.Modes.OperOnly = modeop; + modeop = false + modestr = string(m) + case 'i': + ch.Modes.InviteOnly = modeop + case 'm': + ch.Modes.Moderated = modeop + case 'n': + ch.Modes.NoExternalMsg = modeop + case 'p': + ch.Modes.Private = modeop + case 's': + ch.Modes.Secret = modeop + case 't': + ch.Modes.ProtectedTopic = modeop + case 'z': + ch.Modes.SSLOnly = modeop + case 'O': + ch.Modes.OperOnly = modeop case 'k': if len(modeargs) != 0 { ch.Modes.Key, modeargs = modeargs[0], modeargs[1:len(modeargs)] @@ -229,99 +235,117 @@ func (conn *Conn) setupEvents() { } case 'l': if len(modeargs) != 0 { - ch.Modes.Limit, _ = strconv.Atoi(modeargs[0]); - modeargs = modeargs[1:len(modeargs)]; + ch.Modes.Limit, _ = strconv.Atoi(modeargs[0]) + modeargs = modeargs[1:len(modeargs)] } else { conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m) } case 'q', 'a', 'o', 'h', 'v': if len(modeargs) != 0 { - n := conn.GetNick(modeargs[0]); + n := conn.GetNick(modeargs[0]) if p, ok := ch.Nicks[n]; ok && n != nil { switch m { - case 'q': p.Owner = modeop; - case 'a': p.Admin = modeop; - case 'o': p.Op = modeop; - case 'h': p.HalfOp = modeop; - case 'v': p.Voice = modeop; + case 'q': + p.Owner = modeop + case 'a': + p.Admin = modeop + case 'o': + p.Op = modeop + case 'h': + p.HalfOp = modeop + case 'v': + p.Voice = modeop } - modeargs = modeargs[1:len(modeargs)]; + modeargs = modeargs[1:len(modeargs)] } else { - conn.error("irc.MODE(): MODE %s %s%s %s: buh? state tracking failure.", ch.Name, modestr, m, modeargs[0]); + conn.error("irc.MODE(): MODE %s %s%s %s: buh? state tracking failure.", ch.Name, modestr, m, modeargs[0]) } } else { - conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m); + conn.error("irc.MODE(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m) } } } } else if n := conn.GetNick(line.Args[0]); n != nil { // nick mode change, should be us if n != conn.Me { - conn.error("irc.MODE(): buh? recieved MODE %s for (non-me) nick %s", line.Args[1], n.Nick); - return; + conn.error("irc.MODE(): buh? recieved MODE %s for (non-me) nick %s", line.Args[1], n.Nick) + return } - var modeop bool; // true => add mode, false => remove mode + var modeop bool // true => add mode, false => remove mode for i := 0; i < len(line.Text); i++ { switch m := line.Text[i]; m { case '+': - modeop = true; + modeop = true case '-': - modeop = false; - case 'i': n.Modes.Invisible = modeop; - case 'o': n.Modes.Oper = modeop; - case 'w': n.Modes.WallOps = modeop; - case 'x': n.Modes.HiddenHost = modeop; - case 'z': n.Modes.SSL = modeop; + modeop = false + case 'i': + n.Modes.Invisible = modeop + case 'o': + n.Modes.Oper = modeop + case 'w': + n.Modes.WallOps = modeop + case 'x': + n.Modes.HiddenHost = modeop + case 'z': + n.Modes.SSL = modeop } } } else { - conn.error("irc.MODE(): buh? not sure what to do with MODE %s %s", line.Args[0], line.Args[1]); + conn.error("irc.MODE(): buh? not sure what to do with MODE %s %s", line.Args[0], line.Args[1]) } - }); + }) // Handle TOPIC changes for channels conn.AddHandler("TOPIC", func(conn *Conn, line *Line) { if ch := conn.GetChannel(line.Args[0]); ch != nil { - ch.Topic = line.Text; + ch.Topic = line.Text } else { - conn.error("irc.TOPIC(): buh? topic change on unknown channel %s", line.Args[0]); + conn.error("irc.TOPIC(): buh? topic change on unknown channel %s", line.Args[0]) } - }); + }) // Handle 311 whois reply conn.AddHandler("311", func(conn *Conn, line *Line) { if n := conn.GetNick(line.Args[1]); n != nil { - n.Ident = line.Args[2]; - n.Host = line.Args[3]; - n.Name = line.Text; + n.Ident = line.Args[2] + n.Host = line.Args[3] + n.Name = line.Text } else { - conn.error("irc.311(): buh? received WHOIS info for unknown nick %s", line.Args[1]); + conn.error("irc.311(): buh? received WHOIS info for unknown nick %s", line.Args[1]) } - }); + }) // Handle 324 mode reply conn.AddHandler("324", func(conn *Conn, line *Line) { // XXX: copypasta from MODE, needs tidying. if ch := conn.GetChannel(line.Args[1]); ch != nil { - modeargs := line.Args[3:len(line.Args)]; - var modeop bool; // true => add mode, false => remove mode - var modestr string; + modeargs := line.Args[3:len(line.Args)] + var modeop bool // true => add mode, false => remove mode + var modestr string for i := 0; i < len(line.Args[2]); i++ { switch m := line.Args[2][i]; m { case '+': - modeop = true; - modestr = string(m); + modeop = true + modestr = string(m) case '-': - modeop = false; - modestr = string(m); - case 'i': ch.Modes.InviteOnly = modeop; - case 'm': ch.Modes.Moderated = modeop; - case 'n': ch.Modes.NoExternalMsg = modeop; - case 'p': ch.Modes.Private = modeop; - case 's': ch.Modes.Secret = modeop; - case 't': ch.Modes.ProtectedTopic = modeop; - case 'z': ch.Modes.SSLOnly = modeop; - case 'O': ch.Modes.OperOnly = modeop; + modeop = false + modestr = string(m) + case 'i': + ch.Modes.InviteOnly = modeop + case 'm': + ch.Modes.Moderated = modeop + case 'n': + ch.Modes.NoExternalMsg = modeop + case 'p': + ch.Modes.Private = modeop + case 's': + ch.Modes.Secret = modeop + case 't': + ch.Modes.ProtectedTopic = modeop + case 'z': + ch.Modes.SSLOnly = modeop + case 'O': + ch.Modes.OperOnly = modeop case 'k': if len(modeargs) != 0 { ch.Modes.Key, modeargs = modeargs[0], modeargs[1:len(modeargs)] @@ -330,31 +354,31 @@ func (conn *Conn) setupEvents() { } case 'l': if len(modeargs) != 0 { - ch.Modes.Limit, _ = strconv.Atoi(modeargs[0]); - modeargs = modeargs[1:len(modeargs)]; + ch.Modes.Limit, _ = strconv.Atoi(modeargs[0]) + modeargs = modeargs[1:len(modeargs)] } else { conn.error("irc.324(): buh? not enough arguments to process MODE %s %s%s", ch.Name, modestr, m) } } } } else { - conn.error("irc.324(): buh? received MODE settings for unknown channel %s", line.Args[1]); + conn.error("irc.324(): buh? received MODE settings for unknown channel %s", line.Args[1]) } - }); + }) // Handle 332 topic reply on join to channel conn.AddHandler("332", func(conn *Conn, line *Line) { if ch := conn.GetChannel(line.Args[1]); ch != nil { - ch.Topic = line.Text; + ch.Topic = line.Text } else { - conn.error("irc.332(): buh? received TOPIC value for unknown channel %s", line.Args[1]); + conn.error("irc.332(): buh? received TOPIC value for unknown channel %s", line.Args[1]) } - }); + }) // Handle 353 names reply conn.AddHandler("353", func(conn *Conn, line *Line) { if ch := conn.GetChannel(line.Args[2]); ch != nil { - nicks := strings.Split(line.Text, " ", 0); + nicks := strings.Split(line.Text, " ", 0) for _, nick := range nicks { // UnrealIRCd's coders are lazy and leave a trailing space if nick == "" { @@ -362,41 +386,46 @@ func (conn *Conn) setupEvents() { } switch c := nick[0]; c { case '~', '&', '@', '%', '+': - nick = nick[1:len(nick)]; - fallthrough; + nick = nick[1:len(nick)] + fallthrough default: - n := conn.GetNick(nick); + n := conn.GetNick(nick) if n == nil { // we don't know this nick yet! - n = conn.NewNick(nick, "", "", ""); - conn.Whois(nick); + n = conn.NewNick(nick, "", "", "") + conn.Whois(nick) } if n != conn.Me { // we will be in the names list, but should also be in // the channel's nick list from the JOIN handler above - ch.AddNick(n); + ch.AddNick(n) } - p := ch.Nicks[n]; + p := ch.Nicks[n] switch c { - case '~': p.Owner = true; - case '&': p.Admin = true; - case '@': p.Op = true; - case '%': p.HalfOp = true; - case '+': p.Voice = true; + case '~': + p.Owner = true + case '&': + p.Admin = true + case '@': + p.Op = true + case '%': + p.HalfOp = true + case '+': + p.Voice = true } } } } else { - conn.error("irc.353(): buh? received NAMES list for unknown channel %s", line.Args[2]); + conn.error("irc.353(): buh? received NAMES list for unknown channel %s", line.Args[2]) } - }); + }) // Handle 671 whois reply (nick connected via SSL) conn.AddHandler("671", func(conn *Conn, line *Line) { if n := conn.GetNick(line.Args[1]); n != nil { - n.Modes.SSL = true; + n.Modes.SSL = true } else { - conn.error("irc.671(): buh? received WHOIS SSL info for unknown nick %s", line.Args[1]); + conn.error("irc.671(): buh? received WHOIS SSL info for unknown nick %s", line.Args[1]) } - }); + }) } diff --git a/irc/nickchan.go b/irc/nickchan.go index 230b9f5..7562cc9 100644 --- a/irc/nickchan.go +++ b/irc/nickchan.go @@ -4,25 +4,24 @@ package irc // as well as the internal state maintenance code for the handlers import ( - "fmt"; - "reflect"; + "fmt" + "reflect" ) // A struct representing an IRC channel type Channel struct { - Name, Topic string; - Modes *ChanMode; - // MODE +q, +a, +o, +h, +v - Nicks map[*Nick] *ChanPrivs; - conn *Conn; + Name, Topic string + Modes *ChanMode + Nicks map[*Nick]*ChanPrivs + conn *Conn } // A struct representing an IRC nick type Nick struct { - Nick, Ident, Host, Name string; - Modes *NickMode; - Channels map[*Channel] *ChanPrivs; - conn *Conn; + Nick, Ident, Host, Name string + Modes *NickMode + Channels map[*Channel]*ChanPrivs + conn *Conn } // A struct representing the modes of an IRC Channel @@ -30,13 +29,13 @@ type Nick struct { // see the MODE handler in setupEvents() for details type ChanMode struct { // MODE +p, +s, +t, +n, +m - Private, Secret, ProtectedTopic, NoExternalMsg, Moderated bool; + Private, Secret, ProtectedTopic, NoExternalMsg, Moderated bool // MODE +i, +O, +z - InviteOnly, OperOnly, SSLOnly bool; + InviteOnly, OperOnly, SSLOnly bool // MODE +k - Key string; + Key string // MODE +l - Limit int; + Limit int } // A struct representing the modes of an IRC Nick (User Modes) @@ -45,23 +44,23 @@ type ChanMode struct { // without IRC operator privileges (and even then only on some IRCd's). type NickMode struct { // MODE +i, +o, +w, +x, +z - Invisible, Oper, WallOps, HiddenHost, SSL bool; + Invisible, Oper, WallOps, HiddenHost, SSL bool } // A struct representing the modes a Nick can have on a Channel type ChanPrivs struct { // MODE +q, +a, +o, +h, +v - Owner, Admin, Op, HalfOp, Voice bool; + Owner, Admin, Op, HalfOp, Voice bool } /******************************************************************************\ * Conn methods to create/look up nicks/channels \******************************************************************************/ func (conn *Conn) NewNick(nick, ident, name, host string) *Nick { - n := &Nick{Nick: nick, Ident: ident, Name: name, Host: host, conn: conn}; - n.initialise(); - conn.nicks[n.Nick] = n; - return n; + n := &Nick{Nick: nick, Ident: ident, Name: name, Host: host, conn: conn} + n.initialise() + conn.nicks[n.Nick] = n + return n } func (conn *Conn) GetNick(n string) *Nick { @@ -72,9 +71,9 @@ func (conn *Conn) GetNick(n string) *Nick { } func (conn *Conn) NewChannel(c string) *Channel { - ch := &Channel{Name: c, conn: conn}; - ch.initialise(); - conn.chans[ch.Name] = ch; + ch := &Channel{Name: c, conn: conn} + ch.initialise() + conn.chans[ch.Name] = ch return ch } @@ -89,28 +88,28 @@ 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); + ch.Modes = new(ChanMode) + ch.Nicks = make(map[*Nick]*ChanPrivs) } func (ch *Channel) AddNick(n *Nick) { if _, ok := ch.Nicks[n]; !ok { - ch.Nicks[n] = new(ChanPrivs); - n.Channels[ch] = ch.Nicks[n]; + ch.Nicks[n] = new(ChanPrivs) + n.Channels[ch] = ch.Nicks[n] } else { - ch.conn.error("irc.Channel.AddNick() warning: trying to add already-present nick %s to channel %s", n.Nick, ch.Name); + ch.conn.error("irc.Channel.AddNick() warning: trying to add already-present nick %s to channel %s", n.Nick, ch.Name) } } 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); + fmt.Printf("irc.Channel.DelNick(): deleting %s from %s\n", n.Nick, ch.Name) if n == n.conn.Me { // we're leaving the channel, so remove all state we have about it - ch.Delete(); + ch.Delete() } else { - ch.Nicks[n] = nil, false; - n.DelChannel(ch); + ch.Nicks[n] = nil, false + n.DelChannel(ch) } } // no else here ... // we call Channel.DelNick() and Nick.DelChan() from each other to ensure @@ -118,57 +117,57 @@ func (ch *Channel) DelNick(n *Nick) { } func (ch *Channel) Delete() { - fmt.Printf("irc.Channel.Delete(): deleting %s\n", ch.Name); + fmt.Printf("irc.Channel.Delete(): deleting %s\n", ch.Name) for n, _ := range ch.Nicks { - n.DelChannel(ch); + n.DelChannel(ch) } - ch.conn.chans[ch.Name] = nil, false; + ch.conn.chans[ch.Name] = nil, false } /******************************************************************************\ * Nick methods for state management \******************************************************************************/ func (n *Nick) initialise() { - n.Modes = new(NickMode); - n.Channels = make(map[*Channel] *ChanPrivs); + n.Modes = new(NickMode) + n.Channels = make(map[*Channel]*ChanPrivs) } // very slightly different to Channel.AddNick() ... func (n *Nick) AddChannel(ch *Channel) { if _, ok := n.Channels[ch]; !ok { - ch.Nicks[n] = new(ChanPrivs); - n.Channels[ch] = ch.Nicks[n]; + ch.Nicks[n] = new(ChanPrivs) + n.Channels[ch] = ch.Nicks[n] } else { - n.conn.error("irc.Nick.AddChannel() warning: trying to add already-present channel %s to nick %s", ch.Name, n.Nick); + n.conn.error("irc.Nick.AddChannel() warning: trying to add already-present channel %s to nick %s", ch.Name, n.Nick) } } 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); - n.Channels[ch] = nil, false; - ch.DelNick(n); + fmt.Printf("irc.Nick.DelChannel(): deleting %s from %s\n", n.Nick, ch.Name) + n.Channels[ch] = nil, false + ch.DelNick(n) if len(n.Channels) == 0 { // nick is no longer in any channels we inhabit, stop tracking it - n.Delete(); + n.Delete() } } } func (n *Nick) ReNick(neu string) { - n.conn.nicks[n.Nick] = nil, false; - n.Nick = neu; - n.conn.nicks[n.Nick] = n; + n.conn.nicks[n.Nick] = nil, false + n.Nick = neu + n.conn.nicks[n.Nick] = n } func (n *Nick) Delete() { // we don't ever want to remove *our* nick from conn.nicks... if n != n.conn.Me { - fmt.Printf("irc.Nick.Delete(): deleting %s\n", n.Nick); + fmt.Printf("irc.Nick.Delete(): deleting %s\n", n.Nick) for ch, _ := range n.Channels { - ch.DelNick(n); + ch.DelNick(n) } - n.conn.nicks[n.Nick] = nil, false; + n.conn.nicks[n.Nick] = nil, false } } @@ -176,152 +175,152 @@ func (n *Nick) Delete() { * String() methods for all structs in this file for ease of debugging. \******************************************************************************/ var ChanModeToString = map[string]string{ - "Private":"p", - "Secret":"s", - "ProtectedTopic":"t", - "NoExternalMsg":"n", - "Moderated":"m", - "InviteOnly":"i", - "OperOnly":"O", - "SSLOnly":"z", - "Key":"k", - "Limit":"l", -}; + "Private": "p", + "Secret": "s", + "ProtectedTopic": "t", + "NoExternalMsg": "n", + "Moderated": "m", + "InviteOnly": "i", + "OperOnly": "O", + "SSLOnly": "z", + "Key": "k", + "Limit": "l", +} var NickModeToString = map[string]string{ - "Invisible":"i", - "Oper":"o", - "WallOps":"w", - "HiddenHost":"x", - "SSL":"z", -}; + "Invisible": "i", + "Oper": "o", + "WallOps": "w", + "HiddenHost": "x", + "SSL": "z", +} var ChanPrivToString = map[string]string{ - "Owner":"q", - "Admin":"a", - "Op":"o", - "HalfOp":"h", - "Voice":"v", -}; + "Owner": "q", + "Admin": "a", + "Op": "o", + "HalfOp": "h", + "Voice": "v", +} var ChanPrivToModeChar = map[string]byte{ - "Owner":'~', - "Admin":'&', - "Op":'@', - "HalfOp":'%', - "Voice":'+', -}; -var StringToChanMode, StringToNickMode, StringToChanPriv map[string]string; -var ModeCharToChanPriv map[byte]string; + "Owner": '~', + "Admin": '&', + "Op": '@', + "HalfOp": '%', + "Voice": '+', +} +var StringToChanMode, StringToNickMode, StringToChanPriv map[string]string +var ModeCharToChanPriv map[byte]string // Init function to fill in reverse mappings for *toString constants etc. func init() { - StringToChanMode = make(map[string]string); - for k,v := range ChanModeToString { - StringToChanMode[v] = k; + StringToChanMode = make(map[string]string) + for k, v := range ChanModeToString { + StringToChanMode[v] = k } - StringToNickMode = make(map[string]string); - for k,v := range NickModeToString { - StringToNickMode[v] = k; + StringToNickMode = make(map[string]string) + for k, v := range NickModeToString { + StringToNickMode[v] = k } - StringToChanPriv = make(map[string]string); - for k,v := range ChanPrivToString { - StringToChanPriv[v] = k; + StringToChanPriv = make(map[string]string) + for k, v := range ChanPrivToString { + StringToChanPriv[v] = k } - ModeCharToChanPriv = make(map[byte]string); - for k,v := range ChanPrivToModeChar { - ModeCharToChanPriv[v] = k; + ModeCharToChanPriv = make(map[byte]string) + for k, v := range ChanPrivToModeChar { + ModeCharToChanPriv[v] = k } } func (ch *Channel) String() string { - str := "Channel: " + ch.Name + "\n\t"; - str += "Topic: " + ch.Topic + "\n\t"; - str += "Modes: " + ch.Modes.String() + "\n\t"; - str += "Nicks: \n"; + str := "Channel: " + ch.Name + "\n\t" + str += "Topic: " + ch.Topic + "\n\t" + str += "Modes: " + ch.Modes.String() + "\n\t" + str += "Nicks: \n" for n, p := range ch.Nicks { - str += "\t\t" + n.Nick + ": " + p.String() + "\n"; + str += "\t\t" + n.Nick + ": " + p.String() + "\n" } - return str; + return str } func (n *Nick) String() string { - str := "Nick: "+ n.Nick + "\n\t"; - str += "Hostmask: " + n.Ident + "@" + n.Host + "\n\t"; - str += "Real Name: " + n.Name + "\n\t"; - str += "Modes: " + n.Modes.String() + "\n\t"; - str += "Channels: \n"; + str := "Nick: " + n.Nick + "\n\t" + str += "Hostmask: " + n.Ident + "@" + n.Host + "\n\t" + str += "Real Name: " + n.Name + "\n\t" + str += "Modes: " + n.Modes.String() + "\n\t" + str += "Channels: \n" for ch, p := range n.Channels { - str += "\t\t" + ch.Name + ": " + p.String() + "\n"; + str += "\t\t" + ch.Name + ": " + p.String() + "\n" } - return str; + return str } func (cm *ChanMode) String() string { - str := "+"; - a := make([]string, 2); - v := reflect.Indirect(reflect.NewValue(cm)).(*reflect.StructValue); - t := v.Type().(*reflect.StructType); + str := "+" + a := make([]string, 2) + v := reflect.Indirect(reflect.NewValue(cm)).(*reflect.StructValue) + t := v.Type().(*reflect.StructType) for i := 0; i < v.NumField(); i++ { switch f := v.Field(i).(type) { case *reflect.BoolValue: if f.Get() { - str += ChanModeToString[t.Field(i).Name]; + str += ChanModeToString[t.Field(i).Name] } case *reflect.StringValue: if f.Get() != "" { - str += ChanModeToString[t.Field(i).Name]; - a[0] = f.Get(); + str += ChanModeToString[t.Field(i).Name] + a[0] = f.Get() } case *reflect.IntValue: if f.Get() != 0 { - str += ChanModeToString[t.Field(i).Name]; - a[1] = fmt.Sprintf("%d", cm.Limit); + str += ChanModeToString[t.Field(i).Name] + a[1] = fmt.Sprintf("%d", cm.Limit) } } } for _, s := range a { if s != "" { - str += " " + s; + str += " " + s } } if str == "+" { - str = "No modes set"; + str = "No modes set" } - return str; + return str } func (nm *NickMode) String() string { - str := "+"; - v := reflect.Indirect(reflect.NewValue(nm)).(*reflect.StructValue); - t := v.Type().(*reflect.StructType); + str := "+" + v := reflect.Indirect(reflect.NewValue(nm)).(*reflect.StructValue) + t := v.Type().(*reflect.StructType) for i := 0; i < v.NumField(); i++ { switch f := v.Field(i).(type) { // only bools here at the mo! case *reflect.BoolValue: if f.Get() { - str += NickModeToString[t.Field(i).Name]; + str += NickModeToString[t.Field(i).Name] } } } if str == "+" { - str = "No modes set"; + str = "No modes set" } - return str; + return str } func (p *ChanPrivs) String() string { - str := "+"; - v := reflect.Indirect(reflect.NewValue(p)).(*reflect.StructValue); - t := v.Type().(*reflect.StructType); + str := "+" + v := reflect.Indirect(reflect.NewValue(p)).(*reflect.StructValue) + t := v.Type().(*reflect.StructType) for i := 0; i < v.NumField(); i++ { switch f := v.Field(i).(type) { // only bools here at the mo too! case *reflect.BoolValue: if f.Get() { - str += ChanPrivToString[t.Field(i).Name]; + str += ChanPrivToString[t.Field(i).Name] } } } if str == "+" { - str = "No modes set"; + str = "No modes set" } - return str; + return str }