Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Nick tracker #79

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Add Nick tracker #79

wants to merge 5 commits into from

Conversation

thoj
Copy link
Owner

@thoj thoj commented Jul 28, 2016

Please test
Usage (Prints Nick list every 30 seconds):

package main

import (
        "fmt"
        "github.com/thoja/go-ircevent"
        "sort"
        "time"
)

const channel = "#whatever"
const serverssl = "irc.whatever.net:6667"

func main() {
        ircnick1 := "blatibalt1"
        irccon := irc.IRC(ircnick1, "blatiblat")
        irccon.VerboseCallbackHandler = true
        irccon.Debug = true
        irccon.AddCallback("001", func(e *irc.Event) { irccon.Join(channel) })
        irccon.AddCallback("366", func(e *irc.Event) {})
        irccon.SetupNickTrack()
        err := irccon.Connect(serverssl)
        if err != nil {
                fmt.Printf("Err %s", err)
                return
        }
        go func() {
                t := time.NewTicker(30 * time.Second)
                for {
                        <-t.C
                        var keys []string
                        if _, ok := irccon.Channels[channel]; ok == true {
                                for k, _ := range irccon.Channels[channel].Users {
                                        keys = append(keys, k)
                                }
                                sort.Strings(keys)
                                fmt.Printf("%d: ", len(keys))
                                for _, k := range keys {
                                        fmt.Printf("(%s)%s ", irccon.Channels[channel].Users[k].Mode, k)
                                }
                                fmt.Printf("\n")
                        }
                }
        }()
        irccon.Loop()
}

irc.SetupNickTrack() Adds nessesarry callbacks
irc.Channels map Populated by said callbacks
irc.Channels["#chan"].Users[nickname]
@belak
Copy link
Contributor

belak commented Jul 28, 2016

Nice! Definitely a great start.

There are a few subtleties with this:

  • If a user has +o+v and this gets a MODE -o for that user, I believe they'll be listed as not having any channel modes at that point. This can be worked around with CAP multi-prefix
  • Channel modes other than o, v, and h can be defined by the server. These are usually sent in an ISUPPORT (005) message. This might be overkill for this PR, but it's worth keeping in mind.
  • ISUPPORT also has a concept called casemapping which defines a way of allowing users and channels with different casings to still match.

I have an implementation of some of the ISUPPORT stuff here: https://github.com/belak/python-seabird/blob/master/seabird/modules/isupport.py

There's also an implementation of nick tracking here: https://github.com/belak/python-seabird/blob/master/seabird/modules/track.py

And I have some more notes here: https://gist.github.com/belak/09edcc4f5e51056bf5bc728647659d81

@thoj
Copy link
Owner Author

thoj commented Jul 28, 2016

Thanks a ton! I keep on efnet where stuff is simple and nice. hehe. I'll look into this. I'm not to worried about getting the mode stuff right. Maybe just keep an array of modes instead of just a string is cleaner.
Notes:

2016/07/28 21:07:18 005 (0) >> &irc.Event{Code:"005", Raw:":irc.homelien.no 005 blatibalt CHANTYPES=&# EXCEPTS INVEX CHANMODES=eIb,k,l,imnpstS CHANLIMIT=&#:50 PREFIX=(ov)@+ MAXLIST=beI:100 MODES=4 NETWORK=EFNet KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server", Nick:"", Host:"", Source:"irc.homelien.no", User:"", Arguments:[]string{"blatibalt", "CHANTYPES=&#", "EXCEPTS", "INVEX", "CHANMODES=eIb,k,l,imnpstS", "CHANLIMIT=&#:50", "PREFIX=(ov)@+", "MAXLIST=beI:100", "MODES=4", "NETWORK=EFNet", "KNOCK", "STATUSMSG=@+", "CALLERID=g", "are supported by this server"}, Connection:(*irc.Connection)(0xc82000c840)}
2016/07/28 21:07:18 005 (0) >> &irc.Event{Code:"005", Raw:":irc.homelien.no 005 blatibalt SAFELIST ELIST=U CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=9 CHANNELLEN=50 TOPICLEN=160 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=60 :are supported by this server", Nick:"", Host:"", Source:"irc.homelien.no", User:"", Arguments:[]string{"blatibalt", "SAFELIST", "ELIST=U", "CASEMAPPING=rfc1459", "CHARSET=ascii", "NICKLEN=9", "CHANNELLEN=50", "TOPICLEN=160", "ETRACE", "CPRIVMSG", "CNOTICE", "DEAF=D", "MONITOR=60", "are supported by this server"}, Connection:(*irc.Connection)(0xc82000c840)}
2016/07/28 21:07:18 005 (0) >> &irc.Event{Code:"005", Raw:":irc.homelien.no 005 blatibalt FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server", Nick:"", Host:"", Source:"irc.homelien.no", User:"", Arguments:[]string{"blatibalt", "FNC", "TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR:", "are supported by this server"}, Connection:(*irc.Connection)(0xc82000c840)}

@belak
Copy link
Contributor

belak commented Jul 28, 2016

Yeah, writing this sort of thing properly without multi-prefix is a pain, but it can be done.

@carstenhag
Copy link

Is there any update on this? Seems like the PR just got forgotten :(

@belak
Copy link
Contributor

belak commented Aug 18, 2017

I ended up writing my own earlier this year which does its best to cover all the bases without needing any special IRCv3 features... unfortunately it's pretty integrated into my irc bot, but the code might be helpful.

https://github.com/belak/go-seabird/blob/master/plugins/channel_track.go

It's technically licensed under MIT, but I'm the sole person who worked on it, and as far as I'm concerned, it's totally find to use as a reference.

@qaisjp
Copy link
Contributor

qaisjp commented Jan 3, 2018

I'll run this on my copy of this repo (it has #96 included) and give feedback how well this runs.

@belak What was the reasoning for not using this code? Does this code use ircv3?

Mode string
}

var mode_split = regexp.MustCompile("([%@+]{0,1})(.+)") //Half-Op, //Op, //Voice
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fairly limited and will break on any servers that support other modes (I've seen & used for admin). This should be pulled from the PREFIX isupport value which comes in the format like this: (qaohv)~&@%+

if _, ok := irc.Channels[channelName]; ok != true {
irc.Channels[channelName] = Channel{Users: make(map[string]User)}
}
if _, ok := irc.Channels[channelName].Users[e.Arguments[2]]; ok != true {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only seems to handle setting all modes, however MODE responses don't always contain all modes of a user. Because this code seems to set mode directly, it looks like the Mode won't be accurate if MODE is called more than once for a user or if a user has a mode set and MODE is called.

Additionally, this will probably end up being the most complicated portion. Either IRCv3 multi-prefix needs to be enabled (and handled) or you will need to query for the user's modes in a channel using NAMES or WHO (rather, WHOX) when a mode is removed. As an example, a user can have +v+o in a channel, but in most cases, only the highest mode will be returned (in this case +o). When +o is removed, they still have +v, so there should be a way to determine that.

})

irc.AddCallback("PART", func(e *Event) {
channelName := e.Arguments[0]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the bot leaves a channel, the whole channel should be removed as it won't be tracked any more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try and work on this, it's something that will probably break things for me.

nickandmode := mode_split.FindStringSubmatch(modenick)
u := User{}
if len(nickandmode) == 3 {
if nickandmode[1] == "@" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above, these should probably be pulled from isupport, if possible, rather than hard coded.

@belak
Copy link
Contributor

belak commented Jan 3, 2018

@qaisjp

I'll run this on my copy of this repo (it has #96 included) and give feedback how well this runs.

It's fairly buggy, but should handle the basic cases relatively well.

@belak What was the reasoning for not using this code? Does this code use ircv3?

This might sound like overkill, but I actually wrote my own IRC library a while back, so using this branch wouldn't really be an option.

Anyway, this code needs a bit of love before it would be production ready. I left a few comments about bugs I noticed off the top of my head (EDIT: I actually noticed I left similar comments above - this may make it easier to see where there may be trouble though).

It doesn't use IRCv3. This sort of tracking can be done without IRCv3 but it takes a bit of extra code. Essentially, IRCv3 would let you handle some of the edge cases when MODE responses remove modes.

@qaisjp
Copy link
Contributor

qaisjp commented Jan 3, 2018

Thanks for the info @belak. Does storing modes in a slice sound good to you?

Might be worth calling it "TrackState", creating a State struct, and then storing content in there (nicknames aren't the only things being tracked, channels are too)

@belak
Copy link
Contributor

belak commented Jan 3, 2018

Yep, storing modes in a slice would probably be good enough. I ended up using a map[rune]bool in my implementation, but the idea is similar.

https://github.com/belak/go-seabird/blob/master/plugins/channel_track.go#L31

The other main difference in my implementation is that it assigns a unique identifier to each user and allows you to track nick changes using the user struct and/or that ID.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants