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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions examples/nicktrack/nicktrack.go
Original file line number Diff line number Diff line change
@@ -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()
}
1 change: 1 addition & 0 deletions irc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
103 changes: 103 additions & 0 deletions irc_nicktrack.go
Original file line number Diff line number Diff line change
@@ -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
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)~&@%+


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] == "@" {
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.

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 {
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.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]
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.

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)
}
})
}
4 changes: 3 additions & 1 deletion irc_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
12 changes: 12 additions & 0 deletions irc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down