diff --git a/README.md b/README.md index 5f22864..ea53fd9 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,11 @@ Synopsis: // Add handlers to do things here! // e.g. join a channel on connect. - c.AddHandler("connected", + c.HandleFunc(irc.CONNECTED, func(conn *irc.Conn, line *irc.Line) { conn.Join("#channel") }) // And a signal on disconnect quit := make(chan bool) - c.AddHandler("disconnected", + c.HandleFunc(irc.DISCONNECTED, func(conn *irc.Conn, line *irc.Line) { quit <- true }) // Tell client to connect diff --git a/client.go b/client.go index cb8d02f..0ff9d18 100644 --- a/client.go +++ b/client.go @@ -1,11 +1,11 @@ package main import ( - irc "github.com/fluffle/goirc/client" + "bufio" "flag" "fmt" + irc "github.com/fluffle/goirc/client" "os" - "bufio" "strings" ) @@ -16,14 +16,14 @@ func main() { flag.Parse() // create new IRC connection - c := irc.SimpleClient("GoTest", "gotest") + c := irc.Client("GoTest", "gotest") c.EnableStateTracking() - c.AddHandler("connected", + c.HandleFunc(irc.CONNECTED, func(conn *irc.Conn, line *irc.Line) { conn.Join(*channel) }) // Set up a handler to notify of disconnect events. quit := make(chan bool) - c.AddHandler("disconnected", + c.HandleFunc(irc.DISCONNECTED, func(conn *irc.Conn, line *irc.Line) { quit <- true }) // set up a goroutine to read commands from stdin @@ -36,6 +36,8 @@ func main() { if err != nil { // wha?, maybe ctrl-D... close(in) + reallyquit = true + c.Quit("") break } // no point in sending empty lines down the channel diff --git a/client/commands.go b/client/commands.go index 870b6a1..0f58109 100644 --- a/client/commands.go +++ b/client/commands.go @@ -2,6 +2,12 @@ package client import "strings" +const ( + INIT = "init" + CONNECTED = "connected" + DISCONNECTED = "disconnected" +) + // this file contains the various commands you can // send to the server using an Conn connection diff --git a/client/connection.go b/client/connection.go index dce6c43..f66f3c5 100644 --- a/client/connection.go +++ b/client/connection.go @@ -15,9 +15,10 @@ import ( // An IRC connection is represented by this struct. type Conn struct { // Connection Hostname and Nickname - Host string - Me *state.Nick - Network string + Host string + Me *state.Nick + Network string + password string // Handlers and Commands handlers *hSet @@ -60,7 +61,7 @@ type Conn struct { Flood bool // Internal counters for flood protection - badness time.Duration + badness time.Duration lastsent time.Time } @@ -164,13 +165,12 @@ func (conn *Conn) Connect(host string, pass ...string) error { } conn.Host = host conn.Connected = true - conn.postConnect() - if len(pass) > 0 { - conn.Pass(pass[0]) + conn.password = pass[0] + } else { + conn.password = "" } - conn.Nick(conn.Me.Nick) - conn.User(conn.Me.Ident, conn.Me.Name) + conn.postConnect() return nil } @@ -188,6 +188,7 @@ func (conn *Conn) postConnect() { go func() { <-conn.cPing }() } go conn.runLoop() + conn.dispatch(&Line{Cmd: INIT}) } // copied from http.client for great justice @@ -305,7 +306,7 @@ func (conn *Conn) shutdown() { // as calling sock.Close() will cause recv() to recieve EOF in readstring() if conn.Connected { logging.Info("irc.shutdown(): Disconnected from server.") - conn.dispatch(&Line{Cmd: "disconnected"}) + conn.dispatch(&Line{Cmd: DISCONNECTED}) conn.Connected = false conn.sock.Close() conn.cSend <- true diff --git a/client/connection_test.go b/client/connection_test.go index 6386eb3..caf1389 100644 --- a/client/connection_test.go +++ b/client/connection_test.go @@ -3,8 +3,8 @@ package client import ( "bufio" "code.google.com/p/gomock/gomock" - "github.com/fluffle/golog/logging" "github.com/fluffle/goirc/state" + "github.com/fluffle/golog/logging" "strings" "testing" "time" @@ -57,7 +57,7 @@ func TestEOF(t *testing.T) { // Set up a handler to detect whether disconnected handlers are called dcon := false - c.HandleFunc("disconnected", func (conn *Conn, line *Line) { + c.HandleFunc(DISCONNECTED, func(conn *Conn, line *Line) { dcon = true }) @@ -356,12 +356,12 @@ func TestRunLoop(t *testing.T) { // Set up a handler to detect whether 001 handler is called h001 := false - c.HandleFunc("001", func (conn *Conn, line *Line) { + c.HandleFunc("001", func(conn *Conn, line *Line) { h001 = true }) // Set up a handler to detect whether 002 handler is called h002 := false - c.HandleFunc("002", func (conn *Conn, line *Line) { + c.HandleFunc("002", func(conn *Conn, line *Line) { h002 = true }) @@ -470,7 +470,7 @@ func TestRateLimit(t *testing.T) { // We'll be needing this later... abs := func(i time.Duration) time.Duration { - if (i < 0) { + if i < 0 { return -i } return i @@ -491,13 +491,13 @@ func TestRateLimit(t *testing.T) { // 2.5 seconds minus the delta between the two ratelimit calls. This should // be minimal but it's guaranteed that it won't be zero. Use 10us as a fuzz. if l := c.rateLimit(60); l != 0 || - abs(c.badness - 2500*time.Millisecond) > 10 * time.Microsecond { + abs(c.badness-2500*time.Millisecond) > 10*time.Microsecond { t.Errorf("Rate limit calculating badness incorrectly.") } // At this point, we can tip over the badness scale, with a bit of help. // 720 chars => +8 seconds of badness => 10.5 seconds => ratelimit - if l := c.rateLimit(720); l != 8 * time.Second || - abs(c.badness - 10500*time.Millisecond) > 10 * time.Microsecond { + if l := c.rateLimit(720); l != 8*time.Second || + abs(c.badness-10500*time.Millisecond) > 10*time.Microsecond { t.Errorf("Rate limit failed to return correct limiting values.") t.Errorf("l=%d, badness=%d", l, c.badness) } diff --git a/client/dispatch.go b/client/dispatch.go index f35a10f..dd73865 100644 --- a/client/dispatch.go +++ b/client/dispatch.go @@ -105,7 +105,9 @@ func (hs *hSet) dispatch(conn *Conn, line *Line) { defer hs.RUnlock() ev := strings.ToLower(line.Cmd) list, ok := hs.set[ev] - if !ok { return } + if !ok { + return + } for hn := list.start; hn != nil; hn = hn.next { go hn.Handle(conn, line) } diff --git a/client/dispatch_test.go b/client/dispatch_test.go index 427c69e..84b9e54 100644 --- a/client/dispatch_test.go +++ b/client/dispatch_test.go @@ -83,7 +83,7 @@ func TestHandlerSet(t *testing.T) { if callcount != 0 { t.Errorf("Something incremented call count before we were expecting it.") } - hs.dispatch(nil, &Line{Cmd:"One"}) + hs.dispatch(nil, &Line{Cmd: "One"}) <-time.After(time.Millisecond) if callcount != 4 { t.Errorf("Our handler wasn't called four times :-(") @@ -107,7 +107,7 @@ func TestHandlerSet(t *testing.T) { } // Dispatch should result in 3 additions. - hs.dispatch(nil, &Line{Cmd:"One"}) + hs.dispatch(nil, &Line{Cmd: "One"}) <-time.After(time.Millisecond) if callcount != 7 { t.Errorf("Our handler wasn't called three times :-(") @@ -129,7 +129,7 @@ func TestHandlerSet(t *testing.T) { } // Dispatch should result in 2 additions. - hs.dispatch(nil, &Line{Cmd:"One"}) + hs.dispatch(nil, &Line{Cmd: "One"}) <-time.After(time.Millisecond) if callcount != 9 { t.Errorf("Our handler wasn't called two times :-(") @@ -151,7 +151,7 @@ func TestHandlerSet(t *testing.T) { } // Dispatch should result in 1 addition. - hs.dispatch(nil, &Line{Cmd:"One"}) + hs.dispatch(nil, &Line{Cmd: "One"}) <-time.After(time.Millisecond) if callcount != 10 { t.Errorf("Our handler wasn't called once :-(") @@ -170,7 +170,7 @@ func TestHandlerSet(t *testing.T) { } // Dispatch should result in NO additions. - hs.dispatch(nil, &Line{Cmd:"One"}) + hs.dispatch(nil, &Line{Cmd: "One"}) <-time.After(time.Millisecond) if callcount != 10 { t.Errorf("Our handler was called?") @@ -184,7 +184,7 @@ func TestCommandSet(t *testing.T) { } c := &command{ - fn: func(c *Conn, l *Line) {}, + fn: func(c *Conn, l *Line) {}, help: "wtf?", } @@ -196,7 +196,7 @@ func TestCommandSet(t *testing.T) { if fail := cs.add("one", c); fail != nil { t.Errorf("Adding a second 'one' command did not fail as expected.") } - + cn2 := cs.add("One Two", c).(*cNode) if _, ok := cs.set["one two"]; !ok || cn2.set != cs || cn2.prefix != "one two" { t.Errorf("Command 'one two' not added to set correctly.") @@ -208,7 +208,7 @@ func TestCommandSet(t *testing.T) { if c, l := cs.match("one"); c.(*cNode) != cn1 || l != 3 { t.Errorf("Didn't match 'one' when we should have.") } - if c, l := cs.match ("one two three"); c.(*cNode) != cn2 || l != 7 { + if c, l := cs.match("one two three"); c.(*cNode) != cn2 || l != 7 { t.Errorf("Didn't match 'one two' when we should have.") } @@ -216,14 +216,14 @@ func TestCommandSet(t *testing.T) { if _, ok := cs.set["one two"]; ok || cn2.set != nil { t.Errorf("Command 'one two' not removed correctly.") } - if c, l := cs.match ("one two three"); c.(*cNode) != cn1 || l != 3 { + if c, l := cs.match("one two three"); c.(*cNode) != cn1 || l != 3 { t.Errorf("Didn't match 'one' when we should have.") } cn1.Remove() if _, ok := cs.set["one"]; ok || cn1.set != nil { t.Errorf("Command 'one' not removed correctly.") } - if c, l := cs.match ("one two three"); c != nil || l != 0 { + if c, l := cs.match("one two three"); c != nil || l != 0 { t.Errorf("Matched 'one' when we shouldn't have.") } } diff --git a/client/handlers.go b/client/handlers.go index dcc48f9..9e1c41b 100644 --- a/client/handlers.go +++ b/client/handlers.go @@ -9,8 +9,9 @@ import ( // sets up the internal event handlers to do essential IRC protocol things var intHandlers = map[string]HandlerFunc{ - "001": (*Conn).h_001, - "433": (*Conn).h_433, + INIT: (*Conn).h_init, + "001": (*Conn).h_001, + "433": (*Conn).h_433, "CTCP": (*Conn).h_CTCP, "NICK": (*Conn).h_NICK, "PING": (*Conn).h_PING, @@ -24,6 +25,15 @@ func (conn *Conn) addIntHandlers() { } } +// Password/User/Nick broadcast on connection. +func (conn *Conn) h_init(line *Line) { + if conn.password != "" { + conn.Pass(conn.password) + } + conn.Nick(conn.Me.Nick) + conn.User(conn.Me.Ident, conn.Me.Name) +} + // Basic ping/pong handler func (conn *Conn) h_PING(line *Line) { conn.Raw("PONG :" + line.Args[0]) @@ -32,7 +42,7 @@ func (conn *Conn) h_PING(line *Line) { // Handler to trigger a "CONNECTED" event on receipt of numeric 001 func (conn *Conn) h_001(line *Line) { // we're connected! - conn.dispatch(&Line{Cmd: "connected"}) + conn.dispatch(&Line{Cmd: CONNECTED}) // and we're being given our hostname (from the server's perspective) t := line.Args[len(line.Args)-1] if idx := strings.LastIndex(t, " "); idx != -1 { @@ -99,7 +109,9 @@ func (conn *Conn) h_PRIVMSG(line *Line) { } } cmd, l := conn.cmdMatch(txt) - if cmd == nil { return } + if cmd == nil { + return + } if conn.CommandStripPrefix { txt = strings.TrimSpace(txt[l:]) } diff --git a/client/handlers_test.go b/client/handlers_test.go index fcedbb2..1389967 100644 --- a/client/handlers_test.go +++ b/client/handlers_test.go @@ -27,7 +27,7 @@ func Test001(t *testing.T) { l := parseLine(":irc.server.org 001 test :Welcome to IRC test!ident@somehost.com") // Set up a handler to detect whether connected handler is called from 001 hcon := false - c.HandleFunc("connected", func (conn *Conn, line *Line) { + c.HandleFunc(CONNECTED, func(conn *Conn, line *Line) { hcon = true }) @@ -139,11 +139,11 @@ func TestCTCP(t *testing.T) { c.h_CTCP(parseLine(":blah!moo@cows.com PRIVMSG test :\001UNKNOWN ctcp\001")) } -func TestPRIVMSG(t *testing.T){ +func TestPRIVMSG(t *testing.T) { c, s := setUp(t) defer s.tearDown() - f := func (conn *Conn, line *Line) { + f := func(conn *Conn, line *Line) { conn.Privmsg(line.Args[0], line.Args[1]) } c.CommandFunc("prefix", f, "") @@ -188,7 +188,6 @@ func TestPRIVMSG(t *testing.T){ c.h_PRIVMSG(parseLine(":blah!moo@cows.com PRIVMSG #foo :test! prefix bar")) s.nc.ExpectNothing() - } // Test the handler for JOIN messages @@ -317,7 +316,6 @@ func TestMODE(t *testing.T) { t.Errorf("Channel.ParseModes() not called correctly.") } - // Send a nick mode line, returning Me gomock.InOrder( s.st.EXPECT().GetChannel("test").Return(nil), diff --git a/client/line_test.go b/client/line_test.go index 6b9af7e..37e43dd 100644 --- a/client/line_test.go +++ b/client/line_test.go @@ -7,14 +7,14 @@ import ( func TestCopy(t *testing.T) { l1 := &Line{ - Nick: "nick", + Nick: "nick", Ident: "ident", - Host: "host", - Src: "src", - Cmd: "cmd", - Raw: "raw", - Args: []string{"arg", "text"}, - Time: time.Now(), + Host: "host", + Src: "src", + Cmd: "cmd", + Raw: "raw", + Args: []string{"arg", "text"}, + Time: time.Now(), } l2 := l1.Copy() diff --git a/client/state_handlers.go b/client/state_handlers.go index ab2e60a..ffc9211 100644 --- a/client/state_handlers.go +++ b/client/state_handlers.go @@ -9,19 +9,19 @@ import ( ) var stHandlers = map[string]HandlerFunc{ - "JOIN": (*Conn).h_JOIN, - "KICK": (*Conn).h_KICK, - "MODE": (*Conn).h_MODE, - "NICK": (*Conn).h_STNICK, - "PART": (*Conn).h_PART, - "QUIT": (*Conn).h_QUIT, + "JOIN": (*Conn).h_JOIN, + "KICK": (*Conn).h_KICK, + "MODE": (*Conn).h_MODE, + "NICK": (*Conn).h_STNICK, + "PART": (*Conn).h_PART, + "QUIT": (*Conn).h_QUIT, "TOPIC": (*Conn).h_TOPIC, - "311": (*Conn).h_311, - "324": (*Conn).h_324, - "332": (*Conn).h_332, - "352": (*Conn).h_352, - "353": (*Conn).h_353, - "671": (*Conn).h_671, + "311": (*Conn).h_311, + "324": (*Conn).h_324, + "332": (*Conn).h_332, + "352": (*Conn).h_352, + "353": (*Conn).h_353, + "671": (*Conn).h_671, } func (conn *Conn) addSTHandlers() {