diff --git a/examples/nicktrack/nicktrack.go b/examples/nicktrack/nicktrack.go new file mode 100644 index 0000000..e8904be --- /dev/null +++ b/examples/nicktrack/nicktrack.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" + "github.com/thoja/go-ircevent" + "sort" + "time" +) + +const channel = "#ggnet" +const serverssl = "irc.homelien.no: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() +} diff --git a/irc.go b/irc.go index 3b99bbd..4d06559 100644 --- a/irc.go +++ b/irc.go @@ -477,6 +477,7 @@ func IRC(nick, user string) *Connection { PingFreq: 15 * time.Minute, SASLMech: "PLAIN", QuitMessage: "", + Channels: make(map[string]Channel), } irc.setupCallbacks() return irc diff --git a/irc_nicktrack.go b/irc_nicktrack.go new file mode 100644 index 0000000..65d3d70 --- /dev/null +++ b/irc_nicktrack.go @@ -0,0 +1,103 @@ +package irc + +import ( + "regexp" + "strings" +) + +//Struct to store Channel Info +type Channel struct { + Topic string + Mode string + Users map[string]User +} + +type User struct { + Host string + Mode string +} + +var mode_split = regexp.MustCompile("([%@+]{0,1})(.+)") //Half-Op, //Op, //Voice + +func (irc *Connection) SetupNickTrack() { + // 353: RPL_NAMEREPLY per RFC1459 + // will typically receive this on channel joins and when NAMES is + // called via GetNicksOnCHan + irc.AddCallback("353", func(e *Event) { + // get chan + channelName := e.Arguments[2] + // check if chan exists in map + _, ok := irc.Channels[channelName] + + // if not make one + if ok != true { + irc.Channels[channelName] = Channel{Users: make(map[string]User)} + } + // split the datat into a slice + for _, modenick := range strings.Split(e.Message(), " ") { + nickandmode := mode_split.FindStringSubmatch(modenick) + u := User{} + if len(nickandmode) == 3 { + if nickandmode[1] == "@" { + u.Mode = "+o" // Ooof should be mode struct? + } else if nickandmode[1] == "+" { + u.Mode = "+v" // Ooof should be mode struct? + } else if nickandmode[1] == "%" { + u.Mode = "+h" + } + irc.Channels[channelName].Users[nickandmode[2]] = u + } else { + irc.Channels[channelName].Users[modenick] = u + } + } + }) + + irc.AddCallback("MODE", func(e *Event) { + channelName := e.Arguments[0] + if len(e.Arguments) == 3 { // 3 == for channel 2 == for user on server + 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 { + irc.Channels[channelName].Users[e.Arguments[2]] = User{Mode: e.Arguments[1]} + } else { + u := irc.Channels[channelName].Users[e.Arguments[2]] + u.Mode = e.Arguments[1] + irc.Channels[channelName].Users[e.Arguments[2]] = u + } + } + }) + + //Really hacky since the message from the server does not include the channel + irc.AddCallback("NICK", func(e *Event) { + if len(e.Arguments) == 1 { // Sanity check + for k, _ := range irc.Channels { + if _, ok := irc.Channels[k].Users[e.Nick]; ok { + u := irc.Channels[k].Users[e.Nick] + u.Host = e.Host + irc.Channels[k].Users[e.Arguments[0]] = u //New nick + delete(irc.Channels[k].Users, e.Nick) //Delete old + } + } + } + }) + + irc.AddCallback("JOIN", func(e *Event) { + channelName := e.Arguments[0] + if _, ok := irc.Channels[channelName]; ok != true { + irc.Channels[channelName] = Channel{Users: make(map[string]User)} + } + irc.Channels[channelName].Users[e.Nick] = User{Host: e.Source} + }) + + irc.AddCallback("PART", func(e *Event) { + channelName := e.Arguments[0] + delete(irc.Channels[channelName].Users, e.Nick) + }) + + irc.AddCallback("QUIT", func(e *Event) { + for k, _ := range irc.Channels { + delete(irc.Channels[k].Users, e.Nick) + } + }) +} diff --git a/irc_struct.go b/irc_struct.go index a188d9d..61c0517 100644 --- a/irc_struct.go +++ b/irc_struct.go @@ -47,7 +47,9 @@ type Connection struct { Log *log.Logger stopped bool - quit bool //User called Quit, do not reconnect. + quit bool + + Channels map[string]Channel } // A struct to represent an event. diff --git a/irc_test.go b/irc_test.go index 4ef2a2b..4f3c90c 100644 --- a/irc_test.go +++ b/irc_test.go @@ -200,6 +200,9 @@ func TestConnection(t *testing.T) { teststr := randStr(20) testmsgok := make(chan bool, 1) + irccon1.SetupNickTrack() + irccon2.SetupNickTrack() + irccon1.AddCallback("001", func(e *Event) { irccon1.Join(channel) }) irccon2.AddCallback("001", func(e *Event) { irccon2.Join(channel) }) irccon1.AddCallback("366", func(e *Event) { @@ -233,6 +236,15 @@ func TestConnection(t *testing.T) { irccon2.AddCallback("PRIVMSG", func(e *Event) { if e.Message() == teststr { if e.Nick == ircnick1 { + if _, ok := irccon2.Channels[channel]; !ok { + t.Error("Nick tracker did not create channel in map") + } + if _, ok := irccon2.Channels[channel].Users[ircnick1]; !ok { + t.Errorf("Nick tracker did not track other bot nick. List: %v", irccon2.Channels[channel].Users) + } + if _, ok := irccon2.Channels[channel].Users[ircnick2]; !ok { + t.Errorf("Nick tracker did not track own nick. List: %v", irccon2.Channels[channel].Users) + } testmsgok <- true irccon2.Quit() } else {