diff --git a/bridge/irc/handlers.go b/bridge/irc/handlers.go index c94ea892..ce4f0445 100644 --- a/bridge/irc/handlers.go +++ b/bridge/irc/handlers.go @@ -81,7 +81,7 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) { return } if event.Command == "QUIT" { - if event.Source.Name == b.Nick && strings.Contains(event.Trailing, "Ping timeout") { + if event.Source.Name == b.Nick && strings.Contains(event.Last(), "Ping timeout") { b.Log.Infof("%s reconnecting ..", b.Account) b.Remote <- config.Message{Username: "system", Text: "reconnect", Channel: channel, Account: b.Account, Event: config.EventFailure} return @@ -164,7 +164,7 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { return } rmsg := config.Message{Username: event.Source.Name, Channel: strings.ToLower(event.Params[0]), Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host} - b.Log.Debugf("== Receiving PRIVMSG: %s %s %#v", event.Source.Name, event.Trailing, event) + b.Log.Debugf("== Receiving PRIVMSG: %s %s %#v", event.Source.Name, event.Last(), event) // set action event if event.IsAction() { diff --git a/bridge/irc/irc.go b/bridge/irc/irc.go index 4e3d3ed4..4b7203c4 100644 --- a/bridge/irc/irc.go +++ b/bridge/irc/irc.go @@ -295,7 +295,7 @@ func (b *Birc) storeNames(client *girc.Client, event girc.Event) { channel := event.Params[2] b.names[channel] = append( b.names[channel], - strings.Split(strings.TrimSpace(event.Trailing), " ")...) + strings.Split(strings.TrimSpace(event.Last()), " ")...) } func (b *Birc) formatnicks(nicks []string) string { diff --git a/go.mod b/go.mod index f509a282..15fbb835 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect github.com/kr/pretty v0.1.0 // indirect github.com/labstack/echo/v4 v4.0.0 - github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488 + github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398 github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 // indirect github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d diff --git a/go.sum b/go.sum index ab184a08..4df46c75 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/labstack/echo/v4 v4.0.0 h1:q1GH+caIXPP7H2StPIdzy/ez9CO0EepqYeUg6vi9SW github.com/labstack/echo/v4 v4.0.0/go.mod h1:tZv7nai5buKSg5h/8E6zz4LsD/Dqh9/91Mvs7Z5Zyno= github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0= github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= -github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488 h1:dDEQN5oaa0WOzEiPDSbOugW/e2I/SWY98HYRdcwmGfY= -github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk= +github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398 h1:a40kRmhA1p2XFJ6gqXfCExSyuDDCp/U9LA8ZY27u2Lk= +github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk= github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU= github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5/go.mod h1:c2mYKRyMb1BPkO5St0c/ps62L4S0W2NAkaTXj9qEI+0= github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 h1:iOAVXzZyXtW408TMYejlUPo6BIn92HmOacWtIfNyYns= diff --git a/vendor/github.com/lrstanley/girc/builtin.go b/vendor/github.com/lrstanley/girc/builtin.go index 9dd02804..7f285fda 100644 --- a/vendor/github.com/lrstanley/girc/builtin.go +++ b/vendor/github.com/lrstanley/girc/builtin.go @@ -93,7 +93,7 @@ func handleConnect(c *Client, e Event) { } time.Sleep(2 * time.Second) - c.RunHandlers(&Event{Command: CONNECTED, Trailing: c.Server()}) + c.RunHandlers(&Event{Command: CONNECTED, Params: []string{c.Server()}}) } // nickCollisionHandler helps prevent the client from having conflicting @@ -109,7 +109,7 @@ func nickCollisionHandler(c *Client, e Event) { // handlePING helps respond to ping requests from the server. func handlePING(c *Client, e Event) { - c.Cmd.Pong(e.Trailing) + c.Cmd.Pong(e.Last()) } func handlePONG(c *Client, e Event) { @@ -120,16 +120,11 @@ func handlePONG(c *Client, e Event) { // handleJOIN ensures that the state has updated users and channels. func handleJOIN(c *Client, e Event) { - if e.Source == nil { + if e.Source == nil || len(e.Params) == 0 { return } - var channelName string - if len(e.Params) > 0 { - channelName = e.Params[0] - } else { - channelName = e.Trailing - } + channelName := e.Params[0] c.state.Lock() @@ -158,13 +153,13 @@ func handleJOIN(c *Client, e Event) { user.addChannel(channel.Name) // Assume extended-join (ircv3). - if len(e.Params) == 2 { + if len(e.Params) >= 2 { if e.Params[1] != "*" { user.Extras.Account = e.Params[1] } - if len(e.Trailing) > 0 { - user.Extras.Name = e.Trailing + if len(e.Params) > 2 { + user.Extras.Name = e.Params[2] } } c.state.Unlock() @@ -192,16 +187,13 @@ func handleJOIN(c *Client, e Event) { // handlePART ensures that the state is clean of old user and channel entries. func handlePART(c *Client, e Event) { - if e.Source == nil { + if e.Source == nil || len(e.Params) < 1 { return } - var channel string - if len(e.Params) > 0 { - channel = e.Params[0] - } else { - channel = e.Trailing - } + // TODO: does this work if it's not the bot? + + channel := e.Params[0] if channel == "" { return @@ -231,7 +223,7 @@ func handleTOPIC(c *Client, e Event) { case 1: name = e.Params[0] default: - name = e.Params[len(e.Params)-1] + name = e.Params[1] } c.state.Lock() @@ -241,7 +233,7 @@ func handleTOPIC(c *Client, e Event) { return } - channel.Topic = e.Trailing + channel.Topic = e.Last() c.state.Unlock() c.state.notify(c, UPDATE_STATE) } @@ -253,7 +245,7 @@ func handleWHO(c *Client, e Event) { // Assume WHOX related. if e.Command == RPL_WHOSPCRPL { - if len(e.Params) != 7 { + if len(e.Params) != 8 { // Assume there was some form of error or invalid WHOX response. return } @@ -266,12 +258,24 @@ func handleWHO(c *Client, e Event) { } ident, host, nick, account = e.Params[3], e.Params[4], e.Params[5], e.Params[6] - realname = e.Trailing + realname = e.Last() } else { // Assume RPL_WHOREPLY. - ident, host, nick = e.Params[2], e.Params[3], e.Params[5] - if len(e.Trailing) > 2 { - realname = e.Trailing[2:] + // format: " [*][@|+] : " + ident, host, nick, realname = e.Params[2], e.Params[3], e.Params[5], e.Last() + + // Strip the numbers from " " + for i := 0; i < len(realname); i++ { + // Check if it's not 0-9. + if realname[i] < 0x30 || i > 0x39 { + realname = strings.TrimLeft(realname[i+1:], " ") + break + } + + if i == len(realname)-1 { + // Assume it's only numbers? + realname = "" + } } } @@ -326,10 +330,8 @@ func handleNICK(c *Client, e Event) { c.state.Lock() // renameUser updates the LastActive time automatically. - if len(e.Params) == 1 { - c.state.renameUser(e.Source.ID(), e.Params[0]) - } else if len(e.Trailing) > 0 { - c.state.renameUser(e.Source.ID(), e.Trailing) + if len(e.Params) >= 1 { + c.state.renameUser(e.Source.ID(), e.Last()) } c.state.Unlock() c.state.notify(c, UPDATE_STATE) @@ -372,12 +374,10 @@ func handleMYINFO(c *Client, e Event) { // events. These commonly contain the server capabilities and limitations. // For example, things like max channel name length, or nickname length. func handleISUPPORT(c *Client, e Event) { - // Must be a ISUPPORT-based message. 005 is also used for server bounce - // related things, so this handler may be triggered during other - // situations. + // Must be a ISUPPORT-based message. // Also known as RPL_PROTOCTL. - if !strings.HasSuffix(e.Trailing, "this server") { + if !strings.HasSuffix(e.Last(), "this server") { return } @@ -387,8 +387,8 @@ func handleISUPPORT(c *Client, e Event) { } c.state.Lock() - // Skip the first parameter, as it's our nickname. - for i := 1; i < len(e.Params); i++ { + // Skip the first parameter, as it's our nickname, and the last, as it's the doc. + for i := 1; i < len(e.Params)-1; i++ { j := strings.IndexByte(e.Params[i], '=') if j < 1 || (j+1) == len(e.Params[i]) { @@ -421,10 +421,9 @@ func handleMOTD(c *Client, e Event) { // Otherwise, assume we're getting sent the MOTD line-by-line. if len(c.state.motd) != 0 { - e.Trailing = "\n" + e.Trailing + c.state.motd += "\n" } - - c.state.motd += e.Trailing + c.state.motd += e.Last() c.state.Unlock() } @@ -436,12 +435,12 @@ func handleNAMES(c *Client, e Event) { return } - channel := c.state.lookupChannel(e.Params[len(e.Params)-1]) + channel := c.state.lookupChannel(e.Params[2]) if channel == nil { return } - parts := strings.Split(e.Trailing, " ") + parts := strings.Split(e.Last(), " ") var modes, nick string var ok bool diff --git a/vendor/github.com/lrstanley/girc/cap.go b/vendor/github.com/lrstanley/girc/cap.go index 89146484..5995233f 100644 --- a/vendor/github.com/lrstanley/girc/cap.go +++ b/vendor/github.com/lrstanley/girc/cap.go @@ -94,7 +94,7 @@ func handleCAP(c *Client, e Event) { } // We can assume there was a failure attempting to enable a capability. - if len(e.Params) == 2 && e.Params[1] == CAP_NAK { + if len(e.Params) >= 2 && e.Params[1] == CAP_NAK { // Let the server know that we're done. c.write(&Event{Command: CAP, Params: []string{CAP_END}}) return @@ -102,10 +102,10 @@ func handleCAP(c *Client, e Event) { possible := possibleCapList(c) - if len(e.Params) >= 2 && e.Params[1] == CAP_LS { + if len(e.Params) >= 3 && e.Params[1] == CAP_LS { c.state.Lock() - caps := parseCap(e.Trailing) + caps := parseCap(e.Last()) for k := range caps { if _, ok := possible[k]; !ok { @@ -137,9 +137,9 @@ func handleCAP(c *Client, e Event) { } c.state.Unlock() - // Indicates if this is a multi-line LS. (2 args means it's the + // Indicates if this is a multi-line LS. (3 args means it's the // last LS). - if len(e.Params) == 2 { + if len(e.Params) == 3 { // If we support no caps, just ack the CAP message and END. if len(c.state.tmpCap) == 0 { c.write(&Event{Command: CAP, Params: []string{CAP_END}}) @@ -147,7 +147,7 @@ func handleCAP(c *Client, e Event) { } // Let them know which ones we'd like to enable. - c.write(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " "), EmptyTrailing: true}) + c.write(&Event{Command: CAP, Params: []string{CAP_REQ, strings.Join(c.state.tmpCap, " ")}}) // Re-initialize the tmpCap, so if we get multiple 'CAP LS' requests // due to cap-notify, we can re-evaluate what we can support. @@ -157,9 +157,9 @@ func handleCAP(c *Client, e Event) { } } - if len(e.Params) == 2 && len(e.Trailing) > 1 && e.Params[1] == CAP_ACK { + if len(e.Params) == 3 && e.Params[1] == CAP_ACK { c.state.Lock() - c.state.enabledCap = strings.Split(e.Trailing, " ") + c.state.enabledCap = strings.Split(e.Last(), " ") // Do we need to do sasl auth? wantsSASL := false @@ -208,7 +208,7 @@ func handleAWAY(c *Client, e Event) { c.state.Lock() user := c.state.lookupUser(e.Source.Name) if user != nil { - user.Extras.Away = e.Trailing + user.Extras.Away = e.Last() } c.state.Unlock() c.state.notify(c, UPDATE_STATE) diff --git a/vendor/github.com/lrstanley/girc/cap_sasl.go b/vendor/github.com/lrstanley/girc/cap_sasl.go index 6f86e101..9aecccf3 100644 --- a/vendor/github.com/lrstanley/girc/cap_sasl.go +++ b/vendor/github.com/lrstanley/girc/cap_sasl.go @@ -95,10 +95,9 @@ func handleSASL(c *Client, e Event) { // some reason. The SASL spec and IRCv3 spec do not define a clear // way to abort a SASL exchange, other than to disconnect, or proceed // with CAP END. - c.rx <- &Event{Command: ERROR, Trailing: fmt.Sprintf( - "closing connection: SASL %s failed: %s", - c.Config.SASL.Method(), e.Trailing, - )} + c.rx <- &Event{Command: ERROR, Params: []string{ + fmt.Sprintf("closing connection: SASL %s failed: %s", c.Config.SASL.Method(), e.Last()), + }} return } @@ -133,5 +132,5 @@ func handleSASLError(c *Client, e Event) { // Authentication failed. The SASL spec and IRCv3 spec do not define a // clear way to abort a SASL exchange, other than to disconnect, or // proceed with CAP END. - c.rx <- &Event{Command: ERROR, Trailing: "closing connection: " + e.Trailing} + c.rx <- &Event{Command: ERROR, Params: []string{"closing connection: " + e.Last()}} } diff --git a/vendor/github.com/lrstanley/girc/client.go b/vendor/github.com/lrstanley/girc/client.go index b8abb642..038cc6a7 100644 --- a/vendor/github.com/lrstanley/girc/client.go +++ b/vendor/github.com/lrstanley/girc/client.go @@ -335,7 +335,7 @@ func (e *ErrEvent) Error() string { return "unknown error occurred" } - return e.Event.Trailing + return e.Event.Last() } func (c *Client) execLoop(ctx context.Context, errs chan error, wg *sync.WaitGroup) { diff --git a/vendor/github.com/lrstanley/girc/commands.go b/vendor/github.com/lrstanley/girc/commands.go index 79287d52..9538cedc 100644 --- a/vendor/github.com/lrstanley/girc/commands.go +++ b/vendor/github.com/lrstanley/girc/commands.go @@ -63,7 +63,7 @@ func (cmd *Commands) Part(channels ...string) { // PartMessage leaves an IRC channel with a specified leave message. func (cmd *Commands) PartMessage(channel, message string) { - cmd.c.Send(&Event{Command: PART, Params: []string{channel}, Trailing: message, EmptyTrailing: true}) + cmd.c.Send(&Event{Command: PART, Params: []string{channel, message}}) } // SendCTCP sends a CTCP request to target. Note that this method uses @@ -105,7 +105,7 @@ func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) { // Message sends a PRIVMSG to target (either channel, service, or user). func (cmd *Commands) Message(target, message string) { - cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message, EmptyTrailing: true}) + cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target, message}}) } // Messagef sends a formated PRIVMSG to target (either channel, service, or @@ -171,9 +171,8 @@ func (cmd *Commands) ReplyTof(event Event, format string, a ...interface{}) { // or user). func (cmd *Commands) Action(target, message string) { cmd.c.Send(&Event{ - Command: PRIVMSG, - Params: []string{target}, - Trailing: fmt.Sprintf("\001ACTION %s\001", message), + Command: PRIVMSG, + Params: []string{target, fmt.Sprintf("\001ACTION %s\001", message)}, }) } @@ -185,7 +184,7 @@ func (cmd *Commands) Actionf(target, format string, a ...interface{}) { // Notice sends a NOTICE to target (either channel, service, or user). func (cmd *Commands) Notice(target, message string) { - cmd.c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message, EmptyTrailing: true}) + cmd.c.Send(&Event{Command: NOTICE, Params: []string{target, message}}) } // Noticef sends a formated NOTICE to target (either channel, service, or @@ -221,7 +220,7 @@ func (cmd *Commands) SendRawf(format string, a ...interface{}) error { // Topic sets the topic of channel to message. Does not verify the length // of the topic. func (cmd *Commands) Topic(channel, message string) { - cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message, EmptyTrailing: true}) + cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel, message}}) } // Who sends a WHO query to the server, which will attempt WHOX by default. @@ -266,7 +265,7 @@ func (cmd *Commands) Oper(user, pass string) { // server. func (cmd *Commands) Kick(channel, user, reason string) { if reason != "" { - cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user}, Trailing: reason, EmptyTrailing: true}) + cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user, reason}}) } cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user}}) diff --git a/vendor/github.com/lrstanley/girc/conn.go b/vendor/github.com/lrstanley/girc/conn.go index d0579815..d9ec6319 100644 --- a/vendor/github.com/lrstanley/girc/conn.go +++ b/vendor/github.com/lrstanley/girc/conn.go @@ -309,17 +309,17 @@ func (c *Client) internalConnect(mock net.Conn, dialer Dialer) error { c.Config.Name = c.Config.User } - c.write(&Event{Command: USER, Params: []string{c.Config.User, "*", "*"}, Trailing: c.Config.Name}) + c.write(&Event{Command: USER, Params: []string{c.Config.User, "*", "*", c.Config.Name}}) // Send a virtual event allowing hooks for successful socket connection. - c.RunHandlers(&Event{Command: INITIALIZED, Trailing: c.Server()}) + c.RunHandlers(&Event{Command: INITIALIZED, Params: []string{c.Server()}}) // Wait for the first error. var result error select { case <-ctx.Done(): c.debug.Print("received request to close, beginning clean up") - c.RunHandlers(&Event{Command: CLOSED, Trailing: c.Server()}) + c.RunHandlers(&Event{Command: CLOSED, Params: []string{c.Server()}}) case err := <-errs: c.debug.Print("received error, beginning clean up") result = err @@ -336,7 +336,7 @@ func (c *Client) internalConnect(mock net.Conn, dialer Dialer) error { c.conn.mu.Unlock() c.mu.RUnlock() - c.RunHandlers(&Event{Command: DISCONNECTED, Trailing: c.Server()}) + c.RunHandlers(&Event{Command: DISCONNECTED, Params: []string{c.Server()}}) // Once we have our error/result, let all other functions know we're done. c.debug.Print("waiting for all routines to finish") @@ -396,9 +396,9 @@ func (c *Client) Send(event *Event) { <-time.After(c.conn.rate(event.Len())) } - if c.Config.GlobalFormat && event.Trailing != "" && + if c.Config.GlobalFormat && len(event.Params) > 0 && event.Params[len(event.Params)-1] != "" && (event.Command == PRIVMSG || event.Command == TOPIC || event.Command == NOTICE) { - event.Trailing = Fmt(event.Trailing) + event.Params[len(event.Params)-1] = Fmt(event.Params[len(event.Params)-1]) } c.write(event) diff --git a/vendor/github.com/lrstanley/girc/ctcp.go b/vendor/github.com/lrstanley/girc/ctcp.go index 56f4c85c..637615ef 100644 --- a/vendor/github.com/lrstanley/girc/ctcp.go +++ b/vendor/github.com/lrstanley/girc/ctcp.go @@ -41,7 +41,7 @@ func DecodeCTCP(e *Event) *CTCPEvent { // Must be targeting a user/channel, AND trailing must have // DELIM+TAG+DELIM minimum (at least 3 chars). - if len(e.Params) != 1 || len(e.Trailing) < 3 { + if len(e.Params) != 2 || len(e.Params[1]) < 3 { return nil } @@ -49,12 +49,12 @@ func DecodeCTCP(e *Event) *CTCPEvent { return nil } - if e.Trailing[0] != ctcpDelim || e.Trailing[len(e.Trailing)-1] != ctcpDelim { + if e.Params[1][0] != ctcpDelim || e.Params[1][len(e.Params[1])-1] != ctcpDelim { return nil } // Strip delimiters. - text := e.Trailing[1 : len(e.Trailing)-1] + text := e.Params[1][1 : len(e.Params[1])-1] s := strings.IndexByte(text, eventSpace) diff --git a/vendor/github.com/lrstanley/girc/event.go b/vendor/github.com/lrstanley/girc/event.go index 69f82148..d9d22f26 100644 --- a/vendor/github.com/lrstanley/girc/event.go +++ b/vendor/github.com/lrstanley/girc/event.go @@ -21,46 +21,6 @@ func cutCRFunc(r rune) bool { return r == '\r' || r == '\n' } -// Event represents an IRC protocol message, see RFC1459 section 2.3.1 -// -// :: [':' ] -// :: | ['!' ] ['@' ] -// :: {} | -// :: ' '{' '} -// :: [':' | ] -// :: -// :: -// :: CR LF -type Event struct { - // Source is the origin of the event. - Source *Source `json:"source"` - // Tags are the IRCv3 style message tags for the given event. Only use - // if network supported. - Tags Tags `json:"tags"` - // Timestamp is the time the event was received. This could optionally be - // used for client-stored sent messages too. If the server supports the - // "server-time" capability, this is synced to the UTC time that the server - // specifies. - Timestamp time.Time `json:"timestamp"` - // Command that represents the event, e.g. JOIN, PRIVMSG, KILL. - Command string `json:"command"` - // Params (parameters/args) to the command. Commonly nickname, channel, etc. - Params []string `json:"params"` - // Trailing text. e.g. with a PRIVMSG, this is the message text (part - // after the colon.) - Trailing string `json:"trailing"` - // EmptyTrailing, if true, the text prefix (:) will be added even if - // Event.Trailing is empty. - EmptyTrailing bool `json:"empty_trailing"` - // Sensitive should be true if the message is sensitive (e.g. and should - // not be logged/shown in debugging output). - Sensitive bool `json:"sensitive"` - // If the event is an echo-message response. - Echo bool `json:"echo"` -} - // ParseEvent takes a string and attempts to create a Event struct. Returns // nil if the Event is invalid. func ParseEvent(raw string) (e *Event) { @@ -157,16 +117,55 @@ func ParseEvent(raw string) (e *Event) { e.Params = strings.Split(raw[j:i-1], string(eventSpace)) } - e.Trailing = raw[i+1:] - - // We need to re-encode the trailing argument even if it was empty. - if len(e.Trailing) <= 0 { - e.EmptyTrailing = true - } + e.Params = append(e.Params, raw[i+1:]) return e } +// Event represents an IRC protocol message, see RFC1459 section 2.3.1 +// +// :: [':' ] +// :: | ['!' ] ['@' ] +// :: {} | +// :: ' '{' '} +// :: [':' | ] +// :: +// :: +// :: CR LF +type Event struct { + // Source is the origin of the event. + Source *Source `json:"source"` + // Tags are the IRCv3 style message tags for the given event. Only use + // if network supported. + Tags Tags `json:"tags"` + // Timestamp is the time the event was received. This could optionally be + // used for client-stored sent messages too. If the server supports the + // "server-time" capability, this is synced to the UTC time that the server + // specifies. + Timestamp time.Time `json:"timestamp"` + // Command that represents the event, e.g. JOIN, PRIVMSG, KILL. + Command string `json:"command"` + // Params (parameters/args) to the command. Commonly nickname, channel, etc. + // The last item in the slice could potentially contain spaces (commonly + // referred to as the "trailing" parameter). + Params []string `json:"params"` + // Sensitive should be true if the message is sensitive (e.g. and should + // not be logged/shown in debugging output). + Sensitive bool `json:"sensitive"` + // If the event is an echo-message response. + Echo bool `json:"echo"` +} + +// Last returns the last parameter in Event.Params if it exists. +func (e *Event) Last() string { + if len(e.Params) >= 1 { + return e.Params[len(e.Params)-1] + } + return "" +} + // Copy makes a deep copy of a given event, for use with allowing untrusted // functions/handlers edit the event without causing potential issues with // other handlers. @@ -176,12 +175,10 @@ func (e *Event) Copy() *Event { } newEvent := &Event{ - Timestamp: e.Timestamp, - Command: e.Command, - Trailing: e.Trailing, - EmptyTrailing: e.EmptyTrailing, - Sensitive: e.Sensitive, - Echo: e.Echo, + Timestamp: e.Timestamp, + Command: e.Command, + Sensitive: e.Sensitive, + Echo: e.Echo, } // Copy Source field, as it's a pointer and needs to be dereferenced. @@ -208,7 +205,7 @@ func (e *Event) Copy() *Event { // Equals compares two Events for equality. func (e *Event) Equals(ev *Event) bool { - if e.Command != ev.Command || e.Trailing != ev.Trailing || len(e.Params) != len(ev.Params) { + if e.Command != ev.Command || len(e.Params) != len(ev.Params) { return false } @@ -242,16 +239,18 @@ func (e *Event) Len() (length int) { length += len(e.Command) if len(e.Params) > 0 { + // Spaces before each param. length += len(e.Params) for i := 0; i < len(e.Params); i++ { length += len(e.Params[i]) - } - } - if len(e.Trailing) > 0 || e.EmptyTrailing { - // Include prefix and space. - length += len(e.Trailing) + 2 + // If param contains a space or it's empty, it's trailing, so it should be + // prefixed with a colon (:). + if i == len(e.Params)-1 && (strings.Contains(e.Params[i], " ") || e.Params[i] == "") { + length++ + } + } } return @@ -283,14 +282,15 @@ func (e *Event) Bytes() []byte { // Space separated list of arguments. if len(e.Params) > 0 { - buffer.WriteByte(eventSpace) - buffer.WriteString(strings.Join(e.Params, string(eventSpace))) - } + // buffer.WriteByte(eventSpace) - if len(e.Trailing) > 0 || e.EmptyTrailing { - buffer.WriteByte(eventSpace) - buffer.WriteByte(messagePrefix) - buffer.WriteString(e.Trailing) + for i := 0; i < len(e.Params); i++ { + if i == len(e.Params)-1 && (strings.Contains(e.Params[i], " ") || e.Params[i] == "") { + buffer.WriteString(string(eventSpace) + string(messagePrefix) + e.Params[i]) + continue + } + buffer.WriteString(string(eventSpace) + e.Params[i]) + } } // We need the limit the buffer length. @@ -327,7 +327,7 @@ func (e *Event) Pretty() (out string, ok bool) { } if e.Command == ERROR { - return fmt.Sprintf("[*] an error occurred: %s", e.Trailing), true + return fmt.Sprintf("[*] an error occurred: %s", e.Last()), true } if e.Source == nil { @@ -335,23 +335,19 @@ func (e *Event) Pretty() (out string, ok bool) { return "", false } - if len(e.Params) > 0 && len(e.Trailing) > 0 { - return fmt.Sprintf("[>] writing %s [%s]: %s", strings.ToLower(e.Command), strings.Join(e.Params, ", "), e.Trailing), true - } else if len(e.Params) > 0 { - return fmt.Sprintf("[>] writing %s [%s]", strings.ToLower(e.Command), strings.Join(e.Params, ", ")), true - } else if len(e.Trailing) > 0 { - return fmt.Sprintf("[>] writing %s: %s", strings.ToLower(e.Command), e.Trailing), true + if len(e.Params) > 0 { + return fmt.Sprintf("[>] writing %s", e.String()), true } return "", false } if e.Command == INITIALIZED { - return fmt.Sprintf("[*] connection to %s initialized", e.Trailing), true + return fmt.Sprintf("[*] connection to %s initialized", e.Last()), true } if e.Command == CONNECTED { - return fmt.Sprintf("[*] successfully connected to %s", e.Trailing), true + return fmt.Sprintf("[*] successfully connected to %s", e.Last()), true } if (e.Command == PRIVMSG || e.Command == NOTICE) && len(e.Params) > 0 { @@ -361,18 +357,18 @@ func (e *Event) Pretty() (out string, ok bool) { } if ctcp.Command == CTCP_ACTION { - return fmt.Sprintf("[%s] **%s** %s", strings.Join(e.Params, ","), ctcp.Source.Name, ctcp.Text), true + return fmt.Sprintf("[%s] **%s** %s", strings.Join(e.Params[0:len(e.Params)-1], ","), ctcp.Source.Name, ctcp.Text), true } return fmt.Sprintf("[*] CTCP query from %s: %s%s", ctcp.Source.Name, ctcp.Command, " "+ctcp.Text), true } - return fmt.Sprintf("[%s] (%s) %s", strings.Join(e.Params, ","), e.Source.Name, e.Trailing), true + return fmt.Sprintf("[%s] (%s) %s", strings.Join(e.Params[0:len(e.Params)-1], ","), e.Source.Name, e.Last()), true } if e.Command == RPL_MOTD || e.Command == RPL_MOTDSTART || e.Command == RPL_WELCOME || e.Command == RPL_YOURHOST || e.Command == RPL_CREATED || e.Command == RPL_LUSERCLIENT { - return "[*] " + e.Trailing, true + return "[*] " + e.Last(), true } if e.Command == JOIN && len(e.Params) > 0 { @@ -380,41 +376,34 @@ func (e *Event) Pretty() (out string, ok bool) { } if e.Command == PART && len(e.Params) > 0 { - return fmt.Sprintf("[*] %s (%s) has left %s (%s)", e.Source.Name, e.Source.Host, e.Params[0], e.Trailing), true + return fmt.Sprintf("[*] %s (%s) has left %s (%s)", e.Source.Name, e.Source.Host, e.Params[0], e.Last()), true } if e.Command == QUIT { - return fmt.Sprintf("[*] %s has quit (%s)", e.Source.Name, e.Trailing), true + return fmt.Sprintf("[*] %s has quit (%s)", e.Source.Name, e.Last()), true } if e.Command == INVITE && len(e.Params) == 1 { - return fmt.Sprintf("[*] %s invited to %s by %s", e.Params[0], e.Trailing, e.Source.Name), true + return fmt.Sprintf("[*] %s invited to %s by %s", e.Params[0], e.Last(), e.Source.Name), true } if e.Command == KICK && len(e.Params) >= 2 { - if e.Trailing == "" && len(e.Params) == 3 { - e.Trailing = e.Params[2] - } - - return fmt.Sprintf("[%s] *** %s has kicked %s: %s", e.Params[0], e.Source.Name, e.Params[1], e.Trailing), true + return fmt.Sprintf("[%s] *** %s has kicked %s: %s", e.Params[0], e.Source.Name, e.Params[1], e.Last()), true } if e.Command == NICK { - // Workaround, see https://github.com/lrstanley/girc/pull/15#issuecomment-413845482 - var name string - if len(e.Params) == 1 { - name = e.Params[0] - } else if len(e.Trailing) > 0 { - name = e.Trailing - } - - if name != "" { - return fmt.Sprintf("[*] %s is now known as %s", e.Source.Name, name), true - } + return fmt.Sprintf("[*] %s is now known as %s", e.Source.Name, e.Last()), true } - if e.Command == TOPIC && len(e.Params) > 0 { - return fmt.Sprintf("[%s] *** %s has set the topic to: %s", e.Params[len(e.Params)-1], e.Source.Name, e.Trailing), true + if e.Command == TOPIC && len(e.Params) >= 2 { + return fmt.Sprintf("[%s] *** %s has set the topic to: %s", e.Params[0], e.Source.Name, e.Last()), true + } + + if e.Command == RPL_TOPIC && len(e.Params) > 0 { + if len(e.Params) >= 2 { + return fmt.Sprintf("[*] topic for %s is: %s", e.Params[1], e.Last()), true + } + return fmt.Sprintf("[*] topic for %s is: %s", e.Params[0], e.Last()), true } if e.Command == MODE && len(e.Params) > 2 { @@ -422,8 +411,8 @@ func (e *Event) Pretty() (out string, ok bool) { } if e.Command == CAP_AWAY { - if len(e.Trailing) > 0 { - return fmt.Sprintf("[*] %s is now away: %s", e.Source.Name, e.Trailing), true + if len(e.Params) > 0 { + return fmt.Sprintf("[*] %s is now away: %s", e.Source.Name, e.Last()), true } return fmt.Sprintf("[*] %s is no longer away", e.Source.Name), true @@ -441,12 +430,8 @@ func (e *Event) Pretty() (out string, ok bool) { return fmt.Sprintf("[*] %s has authenticated for account: %s", e.Source.Name, e.Params[0]), true } - if e.Command == RPL_TOPIC && len(e.Params) > 0 && len(e.Trailing) > 0 { - return fmt.Sprintf("[*] topic for %s is: %s", e.Params[len(e.Params)-1], e.Trailing), true - } - - if e.Command == CAP && len(e.Params) == 2 && len(e.Trailing) > 1 && e.Params[1] == CAP_ACK { - return "[*] enabling capabilities: " + e.Trailing, true + if e.Command == CAP && len(e.Params) >= 2 && e.Params[1] == CAP_ACK { + return "[*] enabling capabilities: " + e.Last(), true } return "", false @@ -501,10 +486,11 @@ func (e *Event) IsFromUser() bool { // PRIVMSG ACTION (/me). func (e *Event) StripAction() string { if !e.IsAction() { - return e.Trailing + return e.Last() } - return e.Trailing[8 : len(e.Trailing)-1] + msg := e.Last() + return msg[8 : len(msg)-1] } const ( diff --git a/vendor/github.com/lrstanley/girc/format.go b/vendor/github.com/lrstanley/girc/format.go index 762f602b..e1efb756 100644 --- a/vendor/github.com/lrstanley/girc/format.go +++ b/vendor/github.com/lrstanley/girc/format.go @@ -211,7 +211,7 @@ func IsValidChannel(channel string) bool { return true } -// IsValidNick validates an IRC nickame. Note that this does not validate +// IsValidNick validates an IRC nickname. Note that this does not validate // IRC nickname length. // // nickname = ( letter / special ) *8( letter / digit / special / "-" ) diff --git a/vendor/modules.txt b/vendor/modules.txt index 8c8160b6..d943944d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -66,7 +66,7 @@ github.com/labstack/gommon/color github.com/labstack/gommon/log github.com/labstack/gommon/bytes github.com/labstack/gommon/random -# github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488 +# github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398 github.com/lrstanley/girc # github.com/magiconair/properties v1.8.0 github.com/magiconair/properties