From 23a3bb8f66e6a78ac9447386d8901f1e727e200c Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 10 Mar 2021 17:58:53 -0500 Subject: [PATCH 1/2] rename to Message and Reader; remove Event --- ircevent/README.markdown | 10 ++-- ircevent/examples/simple.go | 9 ++-- ircevent/examples/stress.go | 7 +-- ircevent/irc.go | 6 +-- ircevent/irc_callback.go | 52 ++++++++++---------- ircevent/irc_ctcp.go | 24 ++++++---- ircevent/irc_labeledresponse_test.go | 6 ++- ircevent/irc_parse_test.go | 10 ++-- ircevent/irc_sasl.go | 14 +++--- ircevent/irc_sasl_test.go | 8 ++-- ircevent/irc_struct.go | 72 ++++++++-------------------- ircevent/irc_test.go | 50 +++++++++---------- ircmsg/message.go | 52 ++++++++++---------- ircmsg/message_test.go | 10 ++-- ircmsg/tags.go | 2 +- ircreader/ircreader.go | 18 +++---- ircreader/ircreader_test.go | 2 +- 17 files changed, 164 insertions(+), 188 deletions(-) diff --git a/ircevent/README.markdown b/ircevent/README.markdown index 03de795..ea91af6 100644 --- a/ircevent/README.markdown +++ b/ircevent/README.markdown @@ -23,12 +23,12 @@ irc := ircevent.Connection{ RequestCaps: []string{"server-time", "message-tags"}, } -irc.AddCallback("001", func(e ircevent.Event) { irc.Join("#ircevent-test") }) +irc.AddCallback("001", func(e ircmsg.Message) { irc.Join("#ircevent-test") }) -irc.AddCallback("PRIVMSG", func(event ircevent.Event) { - //event.Message() contains the message - //event.Nick() Contains the sender - //event.Params[0] Contains the channel +irc.AddCallback("PRIVMSG", func(event ircmsg.Message) { + // event.Prefix is the source; + // event.Params[0] is the target (the channel or nickname the message was sent to) + // and event.Params[1] is the message itself }); err := irc.Connect() diff --git a/ircevent/examples/simple.go b/ircevent/examples/simple.go index a6f3cfc..3fe9de6 100644 --- a/ircevent/examples/simple.go +++ b/ircevent/examples/simple.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/goshuirc/irc-go/ircevent" + "github.com/goshuirc/irc-go/ircmsg" ) func getenv(key, defaultValue string) (value string) { @@ -36,15 +37,15 @@ func main() { SASLPassword: saslPassword, } - irc.AddConnectCallback(func(e ircevent.Event) { + irc.AddConnectCallback(func(e ircmsg.Message) { // attempt to set the BOT mode on ourself: if botMode := irc.ISupport()["BOT"]; botMode != "" { irc.Send("MODE", irc.CurrentNick(), "+"+botMode) } irc.Join(channel) }) - irc.AddCallback("JOIN", func(e ircevent.Event) {}) // TODO try to rejoin if we *don't* get this - irc.AddCallback("PRIVMSG", func(e ircevent.Event) { + irc.AddCallback("JOIN", func(e ircmsg.Message) {}) // TODO try to rejoin if we *don't* get this + irc.AddCallback("PRIVMSG", func(e ircmsg.Message) { if len(e.Params) < 2 { return } @@ -65,7 +66,7 @@ func main() { // example client-to-client extension via message-tags: // have the bot maintain a running sum of integers var sum int64 // doesn't need synchronization as long as it's only visible from a single callback - irc.AddCallback("TAGMSG", func(e ircevent.Event) { + irc.AddCallback("TAGMSG", func(e ircmsg.Message) { _, tv := e.GetTag("+summand") if v, err := strconv.ParseInt(tv, 10, 64); err == nil { sum += v diff --git a/ircevent/examples/stress.go b/ircevent/examples/stress.go index 4e3769e..8e52a4b 100644 --- a/ircevent/examples/stress.go +++ b/ircevent/examples/stress.go @@ -9,6 +9,7 @@ import ( _ "net/http/pprof" "github.com/goshuirc/irc-go/ircevent" + "github.com/goshuirc/irc-go/ircmsg" ) /* @@ -48,11 +49,11 @@ func main() { RequestCaps: []string{"server-time", "echo-message"}, } - irc.AddCallback("001", func(e ircevent.Event) { irc.Join(channel) }) - irc.AddCallback("JOIN", func(e ircevent.Event) { irc.Privmsg(channel, "hi there friend!") }) + irc.AddCallback("001", func(e ircmsg.Message) { irc.Join(channel) }) + irc.AddCallback("JOIN", func(e ircmsg.Message) { irc.Privmsg(channel, "hi there friend!") }) // echo whatever we get back count := 0 - irc.AddCallback("PRIVMSG", func(e ircevent.Event) { + irc.AddCallback("PRIVMSG", func(e ircmsg.Message) { if limit != 0 && count >= limit { irc.Quit() } else { diff --git a/ircevent/irc.go b/ircevent/irc.go index 6bd80c1..63fbfac 100644 --- a/ircevent/irc.go +++ b/ircevent/irc.go @@ -142,7 +142,7 @@ func (irc *Connection) readLoop() { } func readMsgLoop(socket net.Conn, maxLineLen int, msgChan chan string, errChan chan error, end chan empty) { - var reader ircreader.IRCReader + var reader ircreader.Reader reader.Initialize(socket, 1024, maxLineLen+maxlenTags) for { msgBytes, err := reader.ReadLine() @@ -348,8 +348,8 @@ func (irc *Connection) sendInternal(b []byte) (err error) { } } -// Send a built ircmsg.IRCMessage. -func (irc *Connection) SendIRCMessage(msg ircmsg.IRCMessage) error { +// Send a built ircmsg.Message. +func (irc *Connection) SendIRCMessage(msg ircmsg.Message) error { b, err := msg.LineBytesStrict(true, irc.MaxLineLen) if err != nil { if irc.Debug { diff --git a/ircevent/irc_callback.go b/ircevent/irc_callback.go index da72e6c..d9e4f5a 100644 --- a/ircevent/irc_callback.go +++ b/ircevent/irc_callback.go @@ -23,10 +23,10 @@ type CallbackID struct { } // Register a callback to a connection and event code. A callback is a function -// which takes only an Event object as parameter. Valid event codes are all +// which takes only an ircmsg.Message object as parameter. Valid event codes are all // IRC/CTCP commands and error/response codes. This function returns the ID of the // registered callback for later management. -func (irc *Connection) AddCallback(eventCode string, callback func(Event)) CallbackID { +func (irc *Connection) AddCallback(eventCode string, callback func(ircmsg.Message)) CallbackID { return irc.addCallback(eventCode, Callback(callback), false, 0) } @@ -103,7 +103,7 @@ func (irc *Connection) ClearCallback(eventcode string) { } // Replace callback i (ID) associated with a given event code with a new callback function. -func (irc *Connection) ReplaceCallback(id CallbackID, callback func(Event)) bool { +func (irc *Connection) ReplaceCallback(id CallbackID, callback func(ircmsg.Message)) bool { irc.eventsMutex.Lock() defer irc.eventsMutex.Unlock() @@ -153,7 +153,7 @@ func (irc *Connection) removeBatchCallbackNoMutex(idNum uint64) { // Convenience function to add a callback that will be called once the // connection is completed (this is traditionally referred to as "connection // registration"). -func (irc *Connection) AddConnectCallback(callback func(Event)) (id CallbackID) { +func (irc *Connection) AddConnectCallback(callback func(ircmsg.Message)) (id CallbackID) { // XXX: forcibly use the same ID number for both copies of the callback id376 := irc.AddCallback(RPL_ENDOFMOTD, callback) irc.addCallback(ERR_NOMOTD, callback, false, id376.id) @@ -184,7 +184,7 @@ var ( errorUnknownLabel = errors.New("received labeled response from server, but we don't recognize the label") ) -func (irc *Connection) handleBatchCommand(msg ircmsg.IRCMessage) { +func (irc *Connection) handleBatchCommand(msg ircmsg.Message) { if len(msg.Params) < 1 || len(msg.Params[0]) < 2 { irc.Log.Printf("Invalid BATCH command from server\n") return @@ -214,7 +214,7 @@ func (irc *Connection) handleBatchCommand(msg ircmsg.IRCMessage) { return } batchObj := new(Batch) - batchObj.IRCMessage = msg + batchObj.Message = msg irc.batches[batchID] = batchInProgress{ createdAt: time.Now(), batch: batchObj, @@ -295,14 +295,14 @@ func (irc *Connection) HandleBatch(batch *Batch) { // recursively "flatten" the nested batch; process every command individually func (irc *Connection) handleBatchNaively(batch *Batch) { if batch.Command != "BATCH" { - irc.HandleEvent(Event{IRCMessage: batch.IRCMessage}) + irc.HandleMessage(batch.Message) } for _, item := range batch.Items { irc.handleBatchNaively(item) } } -func (irc *Connection) handleBatchedCommand(msg ircmsg.IRCMessage, batchID string) { +func (irc *Connection) handleBatchedCommand(msg ircmsg.Message, batchID string) { irc.batchMutex.Lock() defer irc.batchMutex.Unlock() @@ -311,11 +311,11 @@ func (irc *Connection) handleBatchedCommand(msg ircmsg.IRCMessage, batchID strin irc.Log.Printf("ignoring command with unknown batch ID %s\n", batchID) return } - bip.batch.Items = append(bip.batch.Items, &Batch{IRCMessage: msg}) + bip.batch.Items = append(bip.batch.Items, &Batch{Message: msg}) } // Execute all callbacks associated with a given event. -func (irc *Connection) runCallbacks(msg ircmsg.IRCMessage) { +func (irc *Connection) runCallbacks(msg ircmsg.Message) { if !irc.AllowPanic { defer func() { if r := recover(); r != nil { @@ -347,7 +347,7 @@ func (irc *Connection) runCallbacks(msg ircmsg.IRCMessage) { return } else { labelCallback(&Batch{ - IRCMessage: msg, + Message: msg, }) } return @@ -355,12 +355,12 @@ func (irc *Connection) runCallbacks(msg ircmsg.IRCMessage) { } // OK, it's a normal IRC command - irc.HandleEvent(Event{IRCMessage: msg}) + irc.HandleMessage(msg) } -// HandleEvent handles an IRC line using the available handlers. This can be +// HandleMessage handles an IRC line using the available handlers. This can be // used in a batch or labeled-response callback to process an individual line. -func (irc *Connection) HandleEvent(event Event) { +func (irc *Connection) HandleMessage(event ircmsg.Message) { if irc.EnableCTCP { eventRewriteCTCP(&event) } @@ -386,12 +386,10 @@ func (irc *Connection) setupCallbacks() { } // PING: we must respond with the correct PONG - irc.AddCallback("PING", func(e Event) { irc.Send("PONG", e.Message()) }) + irc.AddCallback("PING", func(e ircmsg.Message) { irc.Send("PONG", lastParam(&e)) }) // PONG: record time to make sure the server is responding to us - irc.AddCallback("PONG", func(e Event) { - irc.recordPong(e.Message()) - }) + irc.AddCallback("PONG", func(e ircmsg.Message) { irc.recordPong(lastParam(&e)) }) // 433: ERR_NICKNAMEINUSE " :Nickname is already in use" // 437: ERR_UNAVAILRESOURCE " :Nick/channel is temporarily unavailable" @@ -406,13 +404,13 @@ func (irc *Connection) setupCallbacks() { irc.AddCallback(RPL_ISUPPORT, irc.handleISupport) // respond to NICK from the server (in response to our own NICK, or sent unprompted) - irc.AddCallback("NICK", func(e Event) { - if e.Nick() == irc.CurrentNick() && len(e.Params) > 0 { + irc.AddCallback("NICK", func(e ircmsg.Message) { + if ExtractNick(e.Prefix) == irc.CurrentNick() && len(e.Params) > 0 { irc.setCurrentNick(e.Params[0]) } }) - irc.AddCallback("ERROR", func(e Event) { + irc.AddCallback("ERROR", func(e ircmsg.Message) { if !irc.isQuitting() { irc.Log.Printf("ERROR received from server: %s", strings.Join(e.Params, " ")) } @@ -438,7 +436,7 @@ func (irc *Connection) setupCallbacks() { irc.AddCallback("NOTE", irc.handleStandardReplies) } -func (irc *Connection) handleRplWelcome(e Event) { +func (irc *Connection) handleRplWelcome(e ircmsg.Message) { irc.stateMutex.Lock() defer irc.stateMutex.Unlock() @@ -448,7 +446,7 @@ func (irc *Connection) handleRplWelcome(e Event) { } } -func (irc *Connection) handleRegistration(e Event) { +func (irc *Connection) handleRegistration(e ircmsg.Message) { // wake up Connect() if applicable defer func() { select { @@ -471,7 +469,7 @@ func (irc *Connection) handleRegistration(e Event) { } -func (irc *Connection) handleUnavailableNick(e Event) { +func (irc *Connection) handleUnavailableNick(e ircmsg.Message) { // only try to change the nick if we're not registered yet, // otherwise we'll change in response to pingLoop unsuccessfully // trying to restore the intended nick (swapping one undesired nick @@ -489,7 +487,7 @@ func (irc *Connection) handleUnavailableNick(e Event) { } } -func (irc *Connection) handleISupport(e Event) { +func (irc *Connection) handleISupport(e ircmsg.Message) { irc.stateMutex.Lock() defer irc.stateMutex.Unlock() @@ -530,7 +528,7 @@ func unescapeISupportValue(in string) (out string) { return buf.String() } -func (irc *Connection) handleCAP(e Event) { +func (irc *Connection) handleCAP(e ircmsg.Message) { if len(e.Params) < 3 { return } @@ -666,7 +664,7 @@ func splitCAPToken(token string) (name, value string) { } } -func (irc *Connection) handleStandardReplies(e Event) { +func (irc *Connection) handleStandardReplies(e ircmsg.Message) { // unconditionally print messages for FAIL and WARN; // re. NOTE, if debug is enabled, we print the raw line anyway switch e.Command { diff --git a/ircevent/irc_ctcp.go b/ircevent/irc_ctcp.go index 363c069..d7a3f7c 100644 --- a/ircevent/irc_ctcp.go +++ b/ircevent/irc_ctcp.go @@ -4,9 +4,11 @@ import ( "fmt" "strings" "time" + + "github.com/goshuirc/irc-go/ircmsg" ) -func eventRewriteCTCP(event *Event) { +func eventRewriteCTCP(event *ircmsg.Message) { // XXX rewrite event.Command for CTCP if !(event.Command == "PRIVMSG" && len(event.Params) == 2 && strings.HasPrefix(event.Params[1], "\x01")) { return @@ -44,21 +46,23 @@ func eventRewriteCTCP(event *Event) { } func (irc *Connection) setupCTCPCallbacks() { - irc.AddCallback("CTCP_VERSION", func(e Event) { - irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01VERSION %s\x01", e.Nick(), irc.Version)) + irc.AddCallback("CTCP_VERSION", func(e ircmsg.Message) { + irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01VERSION %s\x01", ExtractNick(e.Prefix), irc.Version)) }) - irc.AddCallback("CTCP_USERINFO", func(e Event) { - irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01USERINFO %s\x01", e.Nick(), irc.User)) + irc.AddCallback("CTCP_USERINFO", func(e ircmsg.Message) { + irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01USERINFO %s\x01", ExtractNick(e.Prefix), irc.User)) }) - irc.AddCallback("CTCP_CLIENTINFO", func(e Event) { - irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01CLIENTINFO PING VERSION TIME USERINFO CLIENTINFO\x01", e.Nick())) + irc.AddCallback("CTCP_CLIENTINFO", func(e ircmsg.Message) { + irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01CLIENTINFO PING VERSION TIME USERINFO CLIENTINFO\x01", ExtractNick(e.Prefix))) }) - irc.AddCallback("CTCP_TIME", func(e Event) { - irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01TIME %s\x01", e.Nick(), time.Now().UTC().Format(time.RFC1123))) + irc.AddCallback("CTCP_TIME", func(e ircmsg.Message) { + irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01TIME %s\x01", ExtractNick(e.Prefix), time.Now().UTC().Format(time.RFC1123))) }) - irc.AddCallback("CTCP_PING", func(e Event) { irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01%s\x01", e.Nick(), e.Message())) }) + irc.AddCallback("CTCP_PING", func(e ircmsg.Message) { + irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01%s\x01", ExtractNick(e.Prefix), e.Params[1])) + }) } diff --git a/ircevent/irc_labeledresponse_test.go b/ircevent/irc_labeledresponse_test.go index ca6b3f5..000de9a 100644 --- a/ircevent/irc_labeledresponse_test.go +++ b/ircevent/irc_labeledresponse_test.go @@ -6,6 +6,8 @@ import ( "encoding/hex" "fmt" "testing" + + "github.com/goshuirc/irc-go/ircmsg" ) const ( @@ -21,7 +23,7 @@ func TestLabeledResponse(t *testing.T) { irccon.RequestCaps = []string{"message-tags", "batch", "labeled-response"} irccon.RealName = "ecf61da38b58" results := make(map[string]string) - irccon.AddConnectCallback(func(e Event) { + irccon.AddConnectCallback(func(e ircmsg.Message) { irccon.SendWithLabel(func(batch *Batch) { if batch == nil { return @@ -192,7 +194,7 @@ func TestBatchHandlers(t *testing.T) { } return false }) - alice.AddCallback("PRIVMSG", func(e Event) { + alice.AddCallback("PRIVMSG", func(e ircmsg.Message) { alicePrivmsgCount++ }) diff --git a/ircevent/irc_parse_test.go b/ircevent/irc_parse_test.go index 2b9036e..d3ee72a 100644 --- a/ircevent/irc_parse_test.go +++ b/ircevent/irc_parse_test.go @@ -7,16 +7,16 @@ import ( ) func TestParse(t *testing.T) { - event := new(Event) - event.Prefix = "nick!~user@host" + source := "nick!~user@host" + nick, user, host := SplitNUH(source) - if event.Nick() != "nick" { + if nick != "nick" { t.Fatal("Parse failed: nick") } - if event.User() != "~user" { + if user != "~user" { t.Fatal("Parse failed: user") } - if event.Host() != "host" { + if host != "host" { t.Fatal("Parse failed: host") } } diff --git a/ircevent/irc_sasl.go b/ircevent/irc_sasl.go index 23074fb..236aa39 100644 --- a/ircevent/irc_sasl.go +++ b/ircevent/irc_sasl.go @@ -4,6 +4,8 @@ import ( "encoding/base64" "errors" "fmt" + + "github.com/goshuirc/irc-go/ircmsg" ) type saslResult struct { @@ -28,35 +30,35 @@ func (irc *Connection) submitSASLResult(r saslResult) { } func (irc *Connection) setupSASLCallbacks() { - irc.AddCallback("AUTHENTICATE", func(e Event) { + irc.AddCallback("AUTHENTICATE", func(e ircmsg.Message) { str := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s\x00%s\x00%s", irc.SASLLogin, irc.SASLLogin, irc.SASLPassword))) irc.Send("AUTHENTICATE", str) }) - irc.AddCallback(RPL_LOGGEDOUT, func(e Event) { + irc.AddCallback(RPL_LOGGEDOUT, func(e ircmsg.Message) { irc.SendRaw("CAP END") irc.SendRaw("QUIT") irc.submitSASLResult(saslResult{true, errors.New(e.Params[1])}) }) - irc.AddCallback(ERR_NICKLOCKED, func(e Event) { + irc.AddCallback(ERR_NICKLOCKED, func(e ircmsg.Message) { irc.SendRaw("CAP END") irc.SendRaw("QUIT") irc.submitSASLResult(saslResult{true, errors.New(e.Params[1])}) }) - irc.AddCallback(RPL_SASLSUCCESS, func(e Event) { + irc.AddCallback(RPL_SASLSUCCESS, func(e ircmsg.Message) { irc.submitSASLResult(saslResult{false, nil}) }) - irc.AddCallback(ERR_SASLFAIL, func(e Event) { + irc.AddCallback(ERR_SASLFAIL, func(e ircmsg.Message) { irc.SendRaw("CAP END") irc.SendRaw("QUIT") irc.submitSASLResult(saslResult{true, errors.New(e.Params[1])}) }) // this could potentially happen with auto-login via certfp? - irc.AddCallback(ERR_SASLALREADY, func(e Event) { + irc.AddCallback(ERR_SASLALREADY, func(e ircmsg.Message) { irc.submitSASLResult(saslResult{false, nil}) }) } diff --git a/ircevent/irc_sasl_test.go b/ircevent/irc_sasl_test.go index 3ad8997..a252dfc 100644 --- a/ircevent/irc_sasl_test.go +++ b/ircevent/irc_sasl_test.go @@ -5,6 +5,8 @@ import ( "fmt" "os" "testing" + + "github.com/goshuirc/irc-go/ircmsg" ) const ( @@ -48,9 +50,9 @@ func runCAPTest(caps []string, useSASL bool, t *testing.T) { } irccon.RequestCaps = caps irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true} - irccon.AddCallback("001", func(e Event) { irccon.Join("#go-eventirc") }) + irccon.AddCallback("001", func(e ircmsg.Message) { irccon.Join("#go-eventirc") }) - irccon.AddCallback("366", func(e Event) { + irccon.AddCallback("366", func(e ircmsg.Message) { irccon.Privmsg("#go-eventirc", "Test Message SASL") irccon.Quit() }) @@ -96,7 +98,7 @@ func TestSASLFail(t *testing.T) { irccon.UseTLS = true setSaslTestCreds(irccon, t) irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true} - irccon.AddCallback("001", func(e Event) { irccon.Join("#go-eventirc") }) + irccon.AddCallback("001", func(e ircmsg.Message) { irccon.Join("#go-eventirc") }) // intentionally break the password irccon.SASLPassword = irccon.SASLPassword + "_" err := irccon.Connect() diff --git a/ircevent/irc_struct.go b/ircevent/irc_struct.go index 10ec2e9..3b2859b 100644 --- a/ircevent/irc_struct.go +++ b/ircevent/irc_struct.go @@ -17,7 +17,7 @@ import ( type empty struct{} -type Callback func(Event) +type Callback func(ircmsg.Message) type callbackPair struct { id uint64 @@ -65,7 +65,7 @@ type Connection struct { // networking and synchronization stateMutex sync.Mutex // innermost mutex: don't block while holding this - end chan empty // closing this causes the goroutines to exit (think threading.Event) + end chan empty // closing this causes the goroutines to exit pwrite chan []byte // receives IRC lines to be sent to the socket wg sync.WaitGroup // after closing end, wait on this for all the goroutines to stop socket net.Conn @@ -123,74 +123,40 @@ type pendingLabel struct { callback LabelCallback } -// Event represents an individual IRC line. -type Event struct { - ircmsg.IRCMessage -} - // Batch represents an IRCv3 batch, or a line within one. There are // two cases: // 1. (Batch).Command == "BATCH". This indicates the start of an IRCv3 -// batch; the embedded IRCMessage is the initial BATCH command, which +// batch; the embedded Message is the initial BATCH command, which // may contain tags that pertain to the batch as a whole. (Batch).Items // contains zero or more *Batch elements, pointing to the contents of // the batch in order. // 2. (Batch).Command != "BATCH". This is an ordinary IRC line; its // tags, command, and parameters are available as members of the embedded -// IRCMessage. +// Message. // In the context of labeled-response, there is a third case: a `nil` // value of *Batch indicates that the server failed to respond in time. type Batch struct { - ircmsg.IRCMessage + ircmsg.Message Items []*Batch } -// Retrieve the last message from Event arguments. -// This function leaves the arguments untouched and -// returns an empty string if there are none. -func (e *Event) Message() string { - if len(e.Params) == 0 { - return "" - } - return e.Params[len(e.Params)-1] -} - -/* -// https://stackoverflow.com/a/10567935/6754440 -// Regex of IRC formatting. -var ircFormat = regexp.MustCompile(`[\x02\x1F\x0F\x16\x1D\x1E]|\x03(\d\d?(,\d\d?)?)?`) - -// Retrieve the last message from Event arguments, but without IRC formatting (color. -// This function leaves the arguments untouched and -// returns an empty string if there are none. -func (e *Event) MessageWithoutFormat() string { - if len(e.Arguments) == 0 { - return "" - } - return ircFormat.ReplaceAllString(e.Arguments[len(e.Arguments)-1], "") -} -*/ - -func (e *Event) Nick() string { - nick, _, _ := e.splitNUH() +func ExtractNick(source string) string { + nick, _, _ := SplitNUH(source) return nick } -func (e *Event) User() string { - _, user, _ := e.splitNUH() - return user -} - -func (e *Event) Host() string { - _, _, host := e.splitNUH() - return host -} - -func (event *Event) splitNUH() (nick, user, host string) { - if i, j := strings.Index(event.Prefix, "!"), strings.Index(event.Prefix, "@"); i > -1 && j > -1 && i < j { - nick = event.Prefix[0:i] - user = event.Prefix[i+1 : j] - host = event.Prefix[j+1:] +func SplitNUH(source string) (nick, user, host string) { + if i, j := strings.Index(source, "!"), strings.Index(source, "@"); i > -1 && j > -1 && i < j { + nick = source[0:i] + user = source[i+1 : j] + host = source[j+1:] + } + return +} + +func lastParam(msg *ircmsg.Message) (result string) { + if 0 < len(msg.Params) { + return msg.Params[len(msg.Params)-1] } return } diff --git a/ircevent/irc_test.go b/ircevent/irc_test.go index 8ad3208..b6e93aa 100644 --- a/ircevent/irc_test.go +++ b/ircevent/irc_test.go @@ -27,7 +27,7 @@ func connForTesting(nick, user string, tls bool) *Connection { return irc } -func mockEvent(command string) ircmsg.IRCMessage { +func mockEvent(command string) ircmsg.Message { return ircmsg.MakeMessage(nil, ":server.name", command) } @@ -37,9 +37,9 @@ func TestRemoveCallback(t *testing.T) { done := make(chan int, 10) - irccon.AddCallback("TEST", func(e Event) { done <- 1 }) - id := irccon.AddCallback("TEST", func(e Event) { done <- 2 }) - irccon.AddCallback("TEST", func(e Event) { done <- 3 }) + irccon.AddCallback("TEST", func(e ircmsg.Message) { done <- 1 }) + id := irccon.AddCallback("TEST", func(e ircmsg.Message) { done <- 2 }) + irccon.AddCallback("TEST", func(e ircmsg.Message) { done <- 3 }) // Should remove callback at index 1 irccon.RemoveCallback(id) @@ -62,11 +62,11 @@ func TestClearCallback(t *testing.T) { done := make(chan int, 10) - irccon.AddCallback("TEST", func(e Event) { done <- 0 }) - irccon.AddCallback("TEST", func(e Event) { done <- 1 }) + irccon.AddCallback("TEST", func(e ircmsg.Message) { done <- 0 }) + irccon.AddCallback("TEST", func(e ircmsg.Message) { done <- 1 }) irccon.ClearCallback("TEST") - irccon.AddCallback("TEST", func(e Event) { done <- 2 }) - irccon.AddCallback("TEST", func(e Event) { done <- 3 }) + irccon.AddCallback("TEST", func(e ircmsg.Message) { done <- 2 }) + irccon.AddCallback("TEST", func(e ircmsg.Message) { done <- 3 }) irccon.runCallbacks(mockEvent("TEST")) @@ -106,10 +106,10 @@ func TestConnection(t *testing.T) { teststr := randStr(20) testmsgok := make(chan bool, 1) - irccon1.AddCallback("001", func(e Event) { irccon1.Join(channel) }) - irccon2.AddCallback("001", func(e Event) { irccon2.Join(channel) }) - irccon1.AddCallback("366", func(e Event) { - go func(e Event) { + irccon1.AddCallback("001", func(e ircmsg.Message) { irccon1.Join(channel) }) + irccon2.AddCallback("001", func(e ircmsg.Message) { irccon2.Join(channel) }) + irccon1.AddCallback("366", func(e ircmsg.Message) { + go func(e ircmsg.Message) { tick := time.NewTicker(1 * time.Second) i := 10 for { @@ -131,14 +131,14 @@ func TestConnection(t *testing.T) { }(e) }) - irccon2.AddCallback("366", func(e Event) { + irccon2.AddCallback("366", func(e ircmsg.Message) { ircnick2 = randStr(8) irccon2.SetNick(ircnick2) }) - irccon2.AddCallback("PRIVMSG", func(e Event) { - if e.Message() == teststr { - if e.Nick() == ircnick1 { + irccon2.AddCallback("PRIVMSG", func(e ircmsg.Message) { + if e.Params[1] == teststr { + if ExtractNick(e.Prefix) == ircnick1 { testmsgok <- true irccon2.Quit() } else { @@ -150,8 +150,8 @@ func TestConnection(t *testing.T) { } }) - irccon2.AddCallback("NICK", func(e Event) { - if !(e.Nick() == ircnick2orig && e.Message() == ircnick2) { + irccon2.AddCallback("NICK", func(e ircmsg.Message) { + if !(ExtractNick(e.Prefix) == ircnick2orig && e.Params[0] == ircnick2) { t.Errorf("Nick change did not work!") } }) @@ -181,9 +181,9 @@ func runReconnectTest(useSASL bool, t *testing.T) { debugTest(irccon) connects := 0 - irccon.AddCallback("001", func(e Event) { irccon.Join(channel) }) + irccon.AddCallback("001", func(e ircmsg.Message) { irccon.Join(channel) }) - irccon.AddCallback("366", func(e Event) { + irccon.AddCallback("366", func(e ircmsg.Message) { connects += 1 if connects > 2 { irccon.Privmsgf(channel, "Connection nr %d (test done)", connects) @@ -226,9 +226,9 @@ func TestConnectionSSL(t *testing.T) { debugTest(irccon) irccon.UseTLS = true irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true} - irccon.AddCallback("001", func(e Event) { irccon.Join(channel) }) + irccon.AddCallback("001", func(e ircmsg.Message) { irccon.Join(channel) }) - irccon.AddCallback("366", func(e Event) { + irccon.AddCallback("366", func(e ircmsg.Message) { irccon.Privmsg(channel, "Test Message from SSL") irccon.Quit() }) @@ -284,8 +284,8 @@ func TestConnectionNickInUse(t *testing.T) { n2 := make(chan string, 1) // check the actual nick after 001 is processed - irccon1.AddCallback("002", func(e Event) { n1 <- irccon1.CurrentNick() }) - irccon2.AddCallback("002", func(e Event) { n2 <- irccon2.CurrentNick() }) + irccon1.AddCallback("002", func(e ircmsg.Message) { n1 <- irccon1.CurrentNick() }) + irccon2.AddCallback("002", func(e ircmsg.Message) { n2 <- irccon2.CurrentNick() }) err := irccon1.Connect() if err != nil { @@ -318,7 +318,7 @@ func TestConnectionCallbacks(t *testing.T) { irccon1 := connForTesting(ircnick, "IRCTest1", false) debugTest(irccon1) resultChan := make(chan map[string]string, 1) - irccon1.AddConnectCallback(func(e Event) { + irccon1.AddConnectCallback(func(e ircmsg.Message) { resultChan <- irccon1.ISupport() }) err := irccon1.Connect() diff --git a/ircmsg/message.go b/ircmsg/message.go index a1d8f4b..b80dedb 100644 --- a/ircmsg/message.go +++ b/ircmsg/message.go @@ -61,10 +61,10 @@ var ( ErrorBadParam = errors.New("Cannot have an empty param, a param with spaces, or a param that starts with ':' before the last parameter") ) -// IRCMessage represents an IRC message, as defined by the RFCs and as +// Message represents an IRC message, as defined by the RFCs and as // extended by the IRCv3 Message Tags specification with the introduction // of message tags. -type IRCMessage struct { +type Message struct { Prefix string Command string Params []string @@ -77,12 +77,12 @@ type IRCMessage struct { // will be encoded as a "trailing parameter" (preceded by a colon). This is // almost never necessary and should not be used except when having to interact // with broken implementations that don't correctly interpret IRC messages. -func (msg *IRCMessage) ForceTrailing() { +func (msg *Message) ForceTrailing() { msg.forceTrailing = true } // GetTag returns whether a tag is present, and if so, what its value is. -func (msg *IRCMessage) GetTag(tagName string) (present bool, value string) { +func (msg *Message) GetTag(tagName string) (present bool, value string) { if len(tagName) == 0 { return } else if tagName[0] == '+' { @@ -95,13 +95,13 @@ func (msg *IRCMessage) GetTag(tagName string) (present bool, value string) { } // HasTag returns whether a tag is present. -func (msg *IRCMessage) HasTag(tagName string) (present bool) { +func (msg *Message) HasTag(tagName string) (present bool) { present, _ = msg.GetTag(tagName) return } // SetTag sets a tag. -func (msg *IRCMessage) SetTag(tagName, tagValue string) { +func (msg *Message) SetTag(tagName, tagValue string) { if len(tagName) == 0 { return } else if tagName[0] == '+' { @@ -118,7 +118,7 @@ func (msg *IRCMessage) SetTag(tagName, tagValue string) { } // DeleteTag deletes a tag. -func (msg *IRCMessage) DeleteTag(tagName string) { +func (msg *Message) DeleteTag(tagName string) { if len(tagName) == 0 { return } else if tagName[0] == '+' { @@ -129,14 +129,14 @@ func (msg *IRCMessage) DeleteTag(tagName string) { } // UpdateTags is a convenience to set multiple tags at once. -func (msg *IRCMessage) UpdateTags(tags map[string]string) { +func (msg *Message) UpdateTags(tags map[string]string) { for name, value := range tags { msg.SetTag(name, value) } } // AllTags returns all tags as a single map. -func (msg *IRCMessage) AllTags() (result map[string]string) { +func (msg *Message) AllTags() (result map[string]string) { result = make(map[string]string, len(msg.tags)+len(msg.clientOnlyTags)) for name, value := range msg.tags { result[name] = value @@ -148,23 +148,23 @@ func (msg *IRCMessage) AllTags() (result map[string]string) { } // ClientOnlyTags returns the client-only tags (the tags with the + prefix). -// The returned map may be internal storage of the IRCMessage object and +// The returned map may be internal storage of the Message object and // should not be modified. -func (msg *IRCMessage) ClientOnlyTags() map[string]string { +func (msg *Message) ClientOnlyTags() map[string]string { return msg.clientOnlyTags } // ParseLine creates and returns a message from the given IRC line. -func ParseLine(line string) (ircmsg IRCMessage, err error) { +func ParseLine(line string) (ircmsg Message, err error) { return parseLine(line, 0, 0) } -// ParseLineStrict creates and returns an IRCMessage from the given IRC line, +// ParseLineStrict creates and returns an Message from the given IRC line, // taking the maximum length into account and truncating the message as appropriate. // If fromClient is true, it enforces the client limit on tag data length (4094 bytes), // allowing the server to return ERR_INPUTTOOLONG as appropriate. If truncateLen is // nonzero, it is the length at which the non-tag portion of the message is truncated. -func ParseLineStrict(line string, fromClient bool, truncateLen int) (ircmsg IRCMessage, err error) { +func ParseLineStrict(line string, fromClient bool, truncateLen int) (ircmsg Message, err error) { maxTagDataLength := MaxlenTagData if fromClient { maxTagDataLength = MaxlenClientTagData @@ -180,7 +180,7 @@ func trimInitialSpaces(str string) string { return str[i:] } -func parseLine(line string, maxTagDataLength int, truncateLen int) (ircmsg IRCMessage, err error) { +func parseLine(line string, maxTagDataLength int, truncateLen int) (ircmsg Message, err error) { // remove either \n or \r\n from the end of the line: line = strings.TrimSuffix(line, "\n") line = strings.TrimSuffix(line, "\r") @@ -279,7 +279,7 @@ func parseLine(line string, maxTagDataLength int, truncateLen int) (ircmsg IRCMe } // helper to parse tags -func (ircmsg *IRCMessage) parseTags(tags string) (err error) { +func (ircmsg *Message) parseTags(tags string) (err error) { for 0 < len(tags) { tagEnd := strings.IndexByte(tags, ';') endPos := tagEnd @@ -311,8 +311,8 @@ func (ircmsg *IRCMessage) parseTags(tags string) (err error) { return nil } -// MakeMessage provides a simple way to create a new IRCMessage. -func MakeMessage(tags map[string]string, prefix string, command string, params ...string) (ircmsg IRCMessage) { +// MakeMessage provides a simple way to create a new Message. +func MakeMessage(tags map[string]string, prefix string, command string, params ...string) (ircmsg Message) { ircmsg.Prefix = prefix ircmsg.Command = command ircmsg.Params = params @@ -320,8 +320,8 @@ func MakeMessage(tags map[string]string, prefix string, command string, params . return ircmsg } -// Line returns a sendable line created from an IRCMessage. -func (ircmsg *IRCMessage) Line() (result string, err error) { +// Line returns a sendable line created from an Message. +func (ircmsg *Message) Line() (result string, err error) { bytes, err := ircmsg.line(0, 0, 0, 0) if err == nil { result = string(bytes) @@ -329,17 +329,17 @@ func (ircmsg *IRCMessage) Line() (result string, err error) { return } -// LineBytes returns a sendable line created from an IRCMessage. -func (ircmsg *IRCMessage) LineBytes() (result []byte, err error) { +// LineBytes returns a sendable line created from an Message. +func (ircmsg *Message) LineBytes() (result []byte, err error) { result, err = ircmsg.line(0, 0, 0, 0) return } -// LineBytesStrict returns a sendable line, as a []byte, created from an IRCMessage. +// LineBytesStrict returns a sendable line, as a []byte, created from an Message. // fromClient controls whether the server-side or client-side tag length limit // is enforced. If truncateLen is nonzero, it is the length at which the // non-tag portion of the message is truncated. -func (ircmsg *IRCMessage) LineBytesStrict(fromClient bool, truncateLen int) ([]byte, error) { +func (ircmsg *Message) LineBytesStrict(fromClient bool, truncateLen int) ([]byte, error) { var tagLimit, clientOnlyTagDataLimit, serverAddedTagDataLimit int if fromClient { // enforce client max tags: @@ -359,8 +359,8 @@ func paramRequiresTrailing(param string) bool { return len(param) == 0 || strings.IndexByte(param, ' ') != -1 || param[0] == ':' } -// line returns a sendable line created from an IRCMessage. -func (ircmsg *IRCMessage) line(tagLimit, clientOnlyTagDataLimit, serverAddedTagDataLimit, truncateLen int) (result []byte, err error) { +// line returns a sendable line created from an Message. +func (ircmsg *Message) line(tagLimit, clientOnlyTagDataLimit, serverAddedTagDataLimit, truncateLen int) (result []byte, err error) { if len(ircmsg.Command) == 0 { return nil, ErrorCommandMissing } diff --git a/ircmsg/message_test.go b/ircmsg/message_test.go index 19e6603..dbd7d29 100644 --- a/ircmsg/message_test.go +++ b/ircmsg/message_test.go @@ -11,12 +11,12 @@ import ( type testcode struct { raw string - message IRCMessage + message Message } type testcodewithlen struct { raw string length int - message IRCMessage + message Message truncateExpected bool } @@ -256,7 +256,7 @@ func TestEncodeErrors(t *testing.T) { } } -var testMessages = []IRCMessage{ +var testMessages = []Message{ { tags: map[string]string{"time": "2019-02-27T04:38:57.489Z", "account": "dan-"}, clientOnlyTags: map[string]string{"+status": "typing"}, @@ -329,7 +329,7 @@ func TestEncodeDecode(t *testing.T) { } func TestForceTrailing(t *testing.T) { - message := IRCMessage{ + message := Message{ Prefix: "shivaram", Command: "PRIVMSG", Params: []string{"#darwin", "nice"}, @@ -352,7 +352,7 @@ func TestForceTrailing(t *testing.T) { } func TestErrorLineTooLongGeneration(t *testing.T) { - message := IRCMessage{ + message := Message{ tags: map[string]string{"draft/msgid": "SAXV5OYJUr18CNJzdWa1qQ"}, Prefix: "shivaram", Command: "PRIVMSG", diff --git a/ircmsg/tags.go b/ircmsg/tags.go index 6d57b00..94e8a66 100644 --- a/ircmsg/tags.go +++ b/ircmsg/tags.go @@ -30,7 +30,7 @@ func init() { // EscapeTagValue takes a value, and returns an escaped message tag value. // // This function is automatically used when lines are created from an -// IRCMessage, so you don't need to call it yourself before creating a line. +// Message, so you don't need to call it yourself before creating a line. func EscapeTagValue(inString string) string { return valtoescape.Replace(inString) } diff --git a/ircreader/ircreader.go b/ircreader/ircreader.go index 204345d..b84fd9a 100644 --- a/ircreader/ircreader.go +++ b/ircreader/ircreader.go @@ -10,7 +10,7 @@ import ( ) /* -IRCReader is an optimized line reader for IRC lines containing tags; +Reader is an optimized line reader for IRC lines containing tags; most IRC lines will not approach the maximum line length (8191 bytes of tag data, plus 512 bytes of message data), so we want a buffered reader that can start with a smaller buffer and expand if necessary, @@ -21,7 +21,7 @@ var ( ErrReadQ = errors.New("readQ exceeded (read too many bytes without terminating newline)") ) -type IRCReader struct { +type Reader struct { conn io.Reader initialSize int @@ -34,17 +34,17 @@ type IRCReader struct { eof bool } -// Returns a new *IRCReader with sane buffer size limits. -func NewIRCReader(conn io.Reader) *IRCReader { - var reader IRCReader +// Returns a new *Reader with sane buffer size limits. +func NewIRCReader(conn io.Reader) *Reader { + var reader Reader reader.Initialize(conn, 512, 8192+1024) return &reader } -// "Placement new" for an IRCReader; initializes it with custom buffer size +// "Placement new" for a Reader; initializes it with custom buffer size // limits. -func (cc *IRCReader) Initialize(conn io.Reader, initialSize, maxSize int) { - *cc = IRCReader{} +func (cc *Reader) Initialize(conn io.Reader, initialSize, maxSize int) { + *cc = Reader{} cc.conn = conn cc.initialSize = initialSize cc.maxSize = maxSize @@ -54,7 +54,7 @@ func (cc *IRCReader) Initialize(conn io.Reader, initialSize, maxSize int) { // or \r\n as the line terminator (but not \r in isolation). Passes through // errors from the underlying connection. Returns ErrReadQ if the buffer limit // was exceeded without a terminating \n. -func (cc *IRCReader) ReadLine() ([]byte, error) { +func (cc *Reader) ReadLine() ([]byte, error) { for { // try to find a terminated line in the buffered data already read nlidx := bytes.IndexByte(cc.buf[cc.searchFrom:cc.end], '\n') diff --git a/ircreader/ircreader_test.go b/ircreader/ircreader_test.go index 1c23005..4615f77 100644 --- a/ircreader/ircreader_test.go +++ b/ircreader/ircreader_test.go @@ -158,7 +158,7 @@ func TestRegression(t *testing.T) { // this terminates the previous read, within the acceptable limit: c.reads = append(c.reads, makeLine(500, true)) - var cc IRCReader + var cc Reader cc.Initialize(&c, 512, 4096+512) line, err := cc.ReadLine() From 5dda03b554a9618f837c23bf65036be4dcefe5eb Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 10 Mar 2021 19:15:58 -0500 Subject: [PATCH 2/2] clean up some event-related language --- ircevent/irc_callback.go | 42 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/ircevent/irc_callback.go b/ircevent/irc_callback.go index d9e4f5a..21a547b 100644 --- a/ircevent/irc_callback.go +++ b/ircevent/irc_callback.go @@ -18,21 +18,21 @@ const ( // Tuple type for uniquely identifying callbacks type CallbackID struct { - eventCode string - id uint64 + command string + id uint64 } -// Register a callback to a connection and event code. A callback is a function -// which takes only an ircmsg.Message object as parameter. Valid event codes are all -// IRC/CTCP commands and error/response codes. This function returns the ID of the +// Register a callback to handle an IRC command (or numeric). A callback is a +// function which takes only an ircmsg.Message object as parameter. Valid commands +// are all IRC commands, including numerics. This function returns the ID of the // registered callback for later management. -func (irc *Connection) AddCallback(eventCode string, callback func(ircmsg.Message)) CallbackID { - return irc.addCallback(eventCode, Callback(callback), false, 0) +func (irc *Connection) AddCallback(command string, callback func(ircmsg.Message)) CallbackID { + return irc.addCallback(command, Callback(callback), false, 0) } -func (irc *Connection) addCallback(eventCode string, callback Callback, prepend bool, idNum uint64) CallbackID { - eventCode = strings.ToUpper(eventCode) - if eventCode == "" || strings.HasPrefix(eventCode, "*") || eventCode == "BATCH" { +func (irc *Connection) addCallback(command string, callback Callback, prepend bool, idNum uint64) CallbackID { + command = strings.ToUpper(command) + if command == "" || strings.HasPrefix(command, "*") || command == "BATCH" { return CallbackID{} } @@ -47,9 +47,9 @@ func (irc *Connection) addCallback(eventCode string, callback Callback, prepend idNum = irc.callbackCounter irc.callbackCounter++ } - id := CallbackID{eventCode: eventCode, id: idNum} + id := CallbackID{command: command, id: idNum} newPair := callbackPair{id: id.id, callback: callback} - current := irc.events[eventCode] + current := irc.events[command] newList := make([]callbackPair, len(current)+1) start := 0 if prepend { @@ -60,7 +60,7 @@ func (irc *Connection) addCallback(eventCode string, callback Callback, prepend if !prepend { newList[len(newList)-1] = newPair } - irc.events[eventCode] = newList + irc.events[command] = newList return id } @@ -68,14 +68,14 @@ func (irc *Connection) addCallback(eventCode string, callback Callback, prepend func (irc *Connection) RemoveCallback(id CallbackID) { irc.eventsMutex.Lock() defer irc.eventsMutex.Unlock() - switch id.eventCode { + switch id.command { case registrationEvent: irc.removeCallbackNoMutex(RPL_ENDOFMOTD, id.id) irc.removeCallbackNoMutex(ERR_NOMOTD, id.id) case "BATCH": irc.removeBatchCallbackNoMutex(id.id) default: - irc.removeCallbackNoMutex(id.eventCode, id.id) + irc.removeCallbackNoMutex(id.command, id.id) } } @@ -94,12 +94,12 @@ func (irc *Connection) removeCallbackNoMutex(code string, id uint64) { } // Remove all callbacks from a given event code. -func (irc *Connection) ClearCallback(eventcode string) { - eventcode = strings.ToUpper(eventcode) +func (irc *Connection) ClearCallback(command string) { + command = strings.ToUpper(command) irc.eventsMutex.Lock() defer irc.eventsMutex.Unlock() - delete(irc.events, eventcode) + delete(irc.events, command) } // Replace callback i (ID) associated with a given event code with a new callback function. @@ -107,7 +107,7 @@ func (irc *Connection) ReplaceCallback(id CallbackID, callback func(ircmsg.Messa irc.eventsMutex.Lock() defer irc.eventsMutex.Unlock() - list := irc.events[id.eventCode] + list := irc.events[id.command] for i, p := range list { if p.id == id.id { list[i] = callbackPair{id: id.id, callback: callback} @@ -133,7 +133,7 @@ func (irc *Connection) AddBatchCallback(callback func(*Batch) bool) CallbackID { copy(nbc, irc.batchCallbacks) nbc[len(nbc)-1] = batchCallbackPair{id: idNum, callback: callback} irc.batchCallbacks = nbc - return CallbackID{eventCode: "BATCH", id: idNum} + return CallbackID{command: "BATCH", id: idNum} } func (irc *Connection) removeBatchCallbackNoMutex(idNum uint64) { @@ -157,7 +157,7 @@ func (irc *Connection) AddConnectCallback(callback func(ircmsg.Message)) (id Cal // XXX: forcibly use the same ID number for both copies of the callback id376 := irc.AddCallback(RPL_ENDOFMOTD, callback) irc.addCallback(ERR_NOMOTD, callback, false, id376.id) - return CallbackID{eventCode: registrationEvent, id: id376.id} + return CallbackID{command: registrationEvent, id: id376.id} } func (irc *Connection) getCallbacks(code string) (result []callbackPair) {