From 9b500bc5f7649c5c082451edd8fd5494f4c4a40c Mon Sep 17 00:00:00 2001 From: Wim Date: Fri, 20 Oct 2017 22:22:13 +0200 Subject: [PATCH] Replace sorcix/irc and go-ircevent with girc --- bridge/irc/irc.go | 209 +++++++++++++++++++++++----------------------- 1 file changed, 104 insertions(+), 105 deletions(-) diff --git a/bridge/irc/irc.go b/bridge/irc/irc.go index e4fc0680..9109e284 100644 --- a/bridge/irc/irc.go +++ b/bridge/irc/irc.go @@ -4,15 +4,15 @@ import ( "bytes" "crypto/tls" "fmt" - "github.com/42wim/go-ircevent" "github.com/42wim/matterbridge/bridge/config" log "github.com/Sirupsen/logrus" + "github.com/lrstanley/girc" "github.com/paulrosania/go-charset/charset" _ "github.com/paulrosania/go-charset/data" "github.com/saintfish/chardet" - ircm "github.com/sorcix/irc" "io" "io/ioutil" + "net" "regexp" "sort" "strconv" @@ -21,7 +21,7 @@ import ( ) type Birc struct { - i *irc.Connection + i *girc.Client Nick string names map[string][]string Config *config.Protocol @@ -63,9 +63,9 @@ func New(cfg config.Protocol, account string, c chan config.Message) *Birc { func (b *Birc) Command(msg *config.Message) string { switch msg.Text { case "!users": - b.i.AddCallback(ircm.RPL_NAMREPLY, b.storeNames) - b.i.AddCallback(ircm.RPL_ENDOFNAMES, b.endNames) - b.i.SendRaw("NAMES " + msg.Channel) + b.i.Handlers.Add(girc.RPL_NAMREPLY, b.storeNames) + b.i.Handlers.Add(girc.RPL_ENDOFNAMES, b.endNames) + b.i.Cmd.SendRaw("NAMES " + msg.Channel) } return "" } @@ -73,26 +73,50 @@ func (b *Birc) Command(msg *config.Message) string { func (b *Birc) Connect() error { b.Local = make(chan config.Message, b.Config.MessageQueue+10) flog.Infof("Connecting %s", b.Config.Server) - i := irc.IRC(b.Config.Nick, b.Config.Nick) - if log.GetLevel() == log.DebugLevel { - i.Debug = true - } - i.UseTLS = b.Config.UseTLS - i.UseSASL = b.Config.UseSASL - i.SASLLogin = b.Config.NickServNick - i.SASLPassword = b.Config.NickServPassword - i.TLSConfig = &tls.Config{InsecureSkipVerify: b.Config.SkipTLSVerify} - i.KeepAlive = time.Minute - i.PingFreq = time.Minute - if b.Config.Password != "" { - i.Password = b.Config.Password - } - i.AddCallback(ircm.RPL_WELCOME, b.handleNewConnection) - i.AddCallback(ircm.RPL_ENDOFMOTD, b.handleOtherAuth) - err := i.Connect(b.Config.Server) + server, portstr, err := net.SplitHostPort(b.Config.Server) if err != nil { return err } + port, err := strconv.Atoi(portstr) + if err != nil { + return err + } + i := girc.New(girc.Config{ + Server: server, + ServerPass: b.Config.Password, + Port: port, + Nick: b.Config.Nick, + User: b.Config.Nick, + Name: b.Config.Nick, + SSL: b.Config.UseTLS, + TLSConfig: &tls.Config{InsecureSkipVerify: b.Config.SkipTLSVerify}, + PingDelay: time.Minute, + }) + + if b.Config.UseSASL { + i.Config.SASL = &girc.SASLPlain{b.Config.NickServNick, b.Config.NickServPassword} + } + + i.Handlers.Add(girc.RPL_WELCOME, b.handleNewConnection) + i.Handlers.Add(girc.RPL_ENDOFMOTD, b.handleOtherAuth) + i.Handlers.Add("*", b.handleOther) + go func() { + for { + if err := i.Connect(); err != nil { + flog.Errorf("error: %s", err) + flog.Info("reconnecting in 30 seconds...") + time.Sleep(30 * time.Second) + i.Handlers.Clear(girc.RPL_WELCOME) + i.Handlers.Add(girc.RPL_WELCOME, func(client *girc.Client, event girc.Event) { + b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS} + // set our correct nick on reconnect if necessary + b.Nick = event.Source.Name + }) + } else { + return + } + } + }() b.i = i select { case <-b.connected: @@ -100,15 +124,8 @@ func (b *Birc) Connect() error { case <-time.After(time.Second * 30): return fmt.Errorf("connection timed out") } - i.Debug = false - // clear on reconnects - i.ClearCallback(ircm.RPL_WELCOME) - i.AddCallback(ircm.RPL_WELCOME, func(event *irc.Event) { - b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS} - // set our correct nick on reconnect if necessary - b.Nick = event.Nick - }) - go i.Loop() + //i.Debug = false + i.Handlers.Clear("*") go b.doSend() return nil } @@ -122,9 +139,9 @@ func (b *Birc) Disconnect() error { func (b *Birc) JoinChannel(channel config.ChannelInfo) error { if channel.Options.Key != "" { flog.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name) - b.i.Join(channel.Name + " " + channel.Options.Key) + b.i.Cmd.JoinKey(channel.Name, channel.Options.Key) } else { - b.i.Join(channel.Name) + b.i.Cmd.Join(channel.Name) } return nil } @@ -173,15 +190,15 @@ func (b *Birc) doSend() { for msg := range b.Local { <-throttle.C if msg.Event == config.EVENT_USER_ACTION { - b.i.Action(msg.Channel, msg.Username+msg.Text) + b.i.Cmd.Action(msg.Channel, msg.Username+msg.Text) } else { - b.i.Privmsg(msg.Channel, msg.Username+msg.Text) + b.i.Cmd.Message(msg.Channel, msg.Username+msg.Text) } } } -func (b *Birc) endNames(event *irc.Event) { - channel := event.Arguments[1] +func (b *Birc) endNames(client *girc.Client, event girc.Event) { + channel := event.Params[1] sort.Strings(b.names[channel]) maxNamesPerPost := (300 / b.nicksPerRow()) * b.nicksPerRow() continued := false @@ -194,99 +211,95 @@ func (b *Birc) endNames(event *irc.Event) { b.Remote <- config.Message{Username: b.Nick, Text: b.formatnicks(b.names[channel], continued), Channel: channel, Account: b.Account} b.names[channel] = nil - b.i.ClearCallback(ircm.RPL_NAMREPLY) - b.i.ClearCallback(ircm.RPL_ENDOFNAMES) + b.i.Handlers.Clear(girc.RPL_NAMREPLY) + b.i.Handlers.Clear(girc.RPL_ENDOFNAMES) } -func (b *Birc) handleNewConnection(event *irc.Event) { +func (b *Birc) handleNewConnection(client *girc.Client, event girc.Event) { flog.Debug("Registering callbacks") i := b.i - b.Nick = event.Arguments[0] - i.AddCallback("PRIVMSG", b.handlePrivMsg) - i.AddCallback("CTCP_ACTION", b.handlePrivMsg) - i.AddCallback(ircm.RPL_TOPICWHOTIME, b.handleTopicWhoTime) - i.AddCallback(ircm.NOTICE, b.handleNotice) - //i.AddCallback(ircm.RPL_MYINFO, func(e *irc.Event) { flog.Infof("%s: %s", e.Code, strings.Join(e.Arguments[1:], " ")) }) - i.AddCallback("PING", func(e *irc.Event) { - i.SendRaw("PONG :" + e.Message()) - flog.Debugf("PING/PONG") - }) - i.AddCallback("JOIN", b.handleJoinPart) - i.AddCallback("PART", b.handleJoinPart) - i.AddCallback("QUIT", b.handleJoinPart) - i.AddCallback("KICK", b.handleJoinPart) - i.AddCallback("*", b.handleOther) + b.Nick = event.Params[0] + + i.Handlers.Add(girc.RPL_ENDOFMOTD, b.handleOtherAuth) + i.Handlers.Add("PRIVMSG", b.handlePrivMsg) + i.Handlers.Add("CTCP_ACTION", b.handlePrivMsg) + i.Handlers.Add(girc.RPL_TOPICWHOTIME, b.handleTopicWhoTime) + i.Handlers.Add(girc.NOTICE, b.handleNotice) + i.Handlers.Add("JOIN", b.handleJoinPart) + i.Handlers.Add("PART", b.handleJoinPart) + i.Handlers.Add("QUIT", b.handleJoinPart) + i.Handlers.Add("KICK", b.handleJoinPart) // we are now fully connected b.connected <- struct{}{} } -func (b *Birc) handleJoinPart(event *irc.Event) { - channel := event.Arguments[0] - if event.Code == "KICK" { - flog.Infof("Got kicked from %s by %s", channel, event.Nick) +func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) { + channel := event.Params[0] + if event.Command == "KICK" { + flog.Infof("Got kicked from %s by %s", channel, event.Source.Name) b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS} return } - if event.Code == "QUIT" { - if event.Nick == b.Nick && strings.Contains(event.Raw, "Ping timeout") { + if event.Command == "QUIT" { + if event.Source.Name == b.Nick && strings.Contains(event.Trailing, "Ping timeout") { flog.Infof("%s reconnecting ..", b.Account) b.Remote <- config.Message{Username: "system", Text: "reconnect", Channel: channel, Account: b.Account, Event: config.EVENT_FAILURE} return } } - if event.Nick != b.Nick { + if event.Source.Name != b.Nick { flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account) - b.Remote <- config.Message{Username: "system", Text: event.Nick + " " + strings.ToLower(event.Code) + "s", Channel: channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE} + b.Remote <- config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE} return } flog.Debugf("handle %#v", event) } -func (b *Birc) handleNotice(event *irc.Event) { - if strings.Contains(event.Message(), "This nickname is registered") && event.Nick == b.Config.NickServNick { - b.i.Privmsg(b.Config.NickServNick, "IDENTIFY "+b.Config.NickServPassword) +func (b *Birc) handleNotice(client *girc.Client, event girc.Event) { + if strings.Contains(event.String(), "This nickname is registered") && event.Source.Name == b.Config.NickServNick { + b.i.Cmd.Message(b.Config.NickServNick, "IDENTIFY "+b.Config.NickServPassword) } else { - b.handlePrivMsg(event) + b.handlePrivMsg(client, event) } } -func (b *Birc) handleOther(event *irc.Event) { - switch event.Code { +func (b *Birc) handleOther(client *girc.Client, event girc.Event) { + switch event.Command { case "372", "375", "376", "250", "251", "252", "253", "254", "255", "265", "266", "002", "003", "004", "005": return } - flog.Debugf("%#v", event.Raw) + flog.Debugf("%#v", event.String()) } -func (b *Birc) handleOtherAuth(event *irc.Event) { +func (b *Birc) handleOtherAuth(client *girc.Client, event girc.Event) { if strings.EqualFold(b.Config.NickServNick, "Q@CServe.quakenet.org") { flog.Debugf("Authenticating %s against %s", b.Config.NickServUsername, b.Config.NickServNick) - b.i.Privmsg(b.Config.NickServNick, "AUTH "+b.Config.NickServUsername+" "+b.Config.NickServPassword) + b.i.Cmd.Message(b.Config.NickServNick, "AUTH "+b.Config.NickServUsername+" "+b.Config.NickServPassword) } } -func (b *Birc) handlePrivMsg(event *irc.Event) { +func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { b.Nick = b.i.GetNick() // freenode doesn't send 001 as first reply - if event.Code == "NOTICE" { + if event.Command == "NOTICE" { return } // don't forward queries to the bot - if event.Arguments[0] == b.Nick { + if event.Params[0] == b.Nick { return } // don't forward message from ourself - if event.Nick == b.Nick { + if event.Source.Name == b.Nick { return } - rmsg := config.Message{Username: event.Nick, Channel: event.Arguments[0], Account: b.Account, UserID: event.User + "@" + event.Host} - flog.Debugf("handlePrivMsg() %s %s %#v", event.Nick, event.Message(), event) + rmsg := config.Message{Username: event.Source.Name, Channel: event.Params[0], Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host} + flog.Debugf("handlePrivMsg() %s %s %#v", event.Source.Name, event.Trailing, event) msg := "" - if event.Code == "CTCP_ACTION" { - // msg = event.Nick + " " + if event.Command == "CTCP_ACTION" { + // msg = event.Source.Name + " " rmsg.Event = config.EVENT_USER_ACTION } - msg += event.Message() + msg += event.Trailing // strip IRC colors re := regexp.MustCompile(`[[:cntrl:]](?:\d{1,2}(?:,\d{1,2})?)?`) msg = re.ReplaceAllString(msg, "") @@ -317,49 +330,35 @@ func (b *Birc) handlePrivMsg(event *irc.Event) { output, _ := ioutil.ReadAll(r) msg = string(output) - flog.Debugf("Sending message from %s on %s to gateway", event.Arguments[0], b.Account) + flog.Debugf("Sending message from %s on %s to gateway", event.Params[0], b.Account) rmsg.Text = msg b.Remote <- rmsg } -func (b *Birc) handleTopicWhoTime(event *irc.Event) { - parts := strings.Split(event.Arguments[2], "!") - t, err := strconv.ParseInt(event.Arguments[3], 10, 64) +func (b *Birc) handleTopicWhoTime(client *girc.Client, event girc.Event) { + parts := strings.Split(event.Params[2], "!") + t, err := strconv.ParseInt(event.Params[3], 10, 64) if err != nil { - flog.Errorf("Invalid time stamp: %s", event.Arguments[3]) + flog.Errorf("Invalid time stamp: %s", event.Params[3]) } user := parts[0] if len(parts) > 1 { user += " [" + parts[1] + "]" } - flog.Debugf("%s: Topic set by %s [%s]", event.Code, user, time.Unix(t, 0)) + flog.Debugf("%s: Topic set by %s [%s]", event.Command, user, time.Unix(t, 0)) } func (b *Birc) nicksPerRow() int { return 4 - /* - if b.Config.Mattermost.NicksPerRow < 1 { - return 4 - } - return b.Config.Mattermost.NicksPerRow - */ } -func (b *Birc) storeNames(event *irc.Event) { - channel := event.Arguments[2] +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.Message()), " ")...) + strings.Split(strings.TrimSpace(event.Trailing), " ")...) } func (b *Birc) formatnicks(nicks []string, continued bool) string { return plainformatter(nicks, b.nicksPerRow()) - /* - switch b.Config.Mattermost.NickFormatter { - case "table": - return tableformatter(nicks, b.nicksPerRow(), continued) - default: - return plainformatter(nicks, b.nicksPerRow()) - } - */ }