2016-11-13 08:30:43 +00:00
|
|
|
package girc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TODO: conntime, uptime
|
|
|
|
|
|
|
|
// State represents the actively-changing variables within the client runtime
|
|
|
|
type State struct {
|
|
|
|
m sync.RWMutex // lock, primarily used for writing things in state
|
|
|
|
connected bool // if we're connected to the server or not
|
|
|
|
nick string // internal tracker for our nickname
|
|
|
|
channels map[string]*Channel // map of channels that the client is in
|
|
|
|
}
|
|
|
|
|
|
|
|
// User represents an IRC user and the state attached to them
|
|
|
|
type User struct {
|
|
|
|
Nick string // nickname of the user
|
|
|
|
Ident string // ident (often referred to as "user") of the user
|
|
|
|
Host string // host that server is providing for the user, may not always be accurate
|
|
|
|
FirstSeen time.Time // the first time they were seen by the client
|
|
|
|
}
|
|
|
|
|
|
|
|
// Channel represents an IRC channel and the state attached to it
|
|
|
|
type Channel struct {
|
|
|
|
// TODO: users needs to be exposed
|
|
|
|
Name string // name of the channel, always lowercase
|
|
|
|
users map[string]*User
|
|
|
|
Joined time.Time // when the channel was joined
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewState returns a clean state
|
|
|
|
func NewState() *State {
|
|
|
|
s := &State{}
|
|
|
|
|
|
|
|
s.channels = make(map[string]*Channel)
|
|
|
|
s.connected = false
|
|
|
|
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
// createChanIfNotExists creates the channel in state, if not already done
|
|
|
|
func (s *State) createChanIfNotExists(channel string) {
|
2016-11-13 10:46:44 +00:00
|
|
|
channel = strings.ToLower(channel)
|
2016-11-13 08:30:43 +00:00
|
|
|
|
|
|
|
// not a valid channel
|
|
|
|
if !IsValidChannel(channel) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-11-13 10:46:44 +00:00
|
|
|
s.m.Lock()
|
2016-11-13 08:30:43 +00:00
|
|
|
if _, ok := s.channels[channel]; !ok {
|
|
|
|
s.channels[channel] = &Channel{
|
2016-11-13 10:46:44 +00:00
|
|
|
Name: channel,
|
2016-11-13 08:30:43 +00:00
|
|
|
users: make(map[string]*User),
|
|
|
|
Joined: time.Now(),
|
|
|
|
}
|
|
|
|
}
|
2016-11-13 10:46:44 +00:00
|
|
|
s.m.Unlock()
|
2016-11-13 08:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// deleteChannel removes the channel from state, if not already done
|
|
|
|
func (s *State) deleteChannel(channel string) {
|
2016-11-13 10:46:44 +00:00
|
|
|
channel = strings.ToLower(channel)
|
2016-11-13 08:30:43 +00:00
|
|
|
s.createChanIfNotExists(channel)
|
|
|
|
|
2016-11-13 10:46:44 +00:00
|
|
|
s.m.Lock()
|
2016-11-13 08:30:43 +00:00
|
|
|
if _, ok := s.channels[channel]; ok {
|
|
|
|
delete(s.channels, channel)
|
|
|
|
}
|
2016-11-13 10:46:44 +00:00
|
|
|
s.m.Unlock()
|
2016-11-13 08:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// createUserIfNotExists creates the channel and user in state,
|
|
|
|
// if not already done
|
|
|
|
func (s *State) createUserIfNotExists(channel, nick, ident, host string) {
|
2016-11-13 10:46:44 +00:00
|
|
|
channel = strings.ToLower(channel)
|
2016-11-13 08:30:43 +00:00
|
|
|
s.createChanIfNotExists(channel)
|
|
|
|
|
2016-11-13 10:46:44 +00:00
|
|
|
s.m.Lock()
|
2016-11-13 08:30:43 +00:00
|
|
|
if _, ok := s.channels[channel].users[nick]; !ok {
|
|
|
|
s.channels[channel].users[nick] = &User{
|
|
|
|
Nick: nick,
|
|
|
|
Ident: ident,
|
|
|
|
Host: host,
|
|
|
|
FirstSeen: time.Now(),
|
|
|
|
}
|
|
|
|
}
|
2016-11-13 10:46:44 +00:00
|
|
|
s.m.Unlock()
|
2016-11-13 08:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// deleteUser removes the user from channel state
|
|
|
|
func (s *State) deleteUser(channel, nick string) {
|
2016-11-13 10:46:44 +00:00
|
|
|
channel = strings.ToLower(channel)
|
2016-11-13 08:30:43 +00:00
|
|
|
s.createChanIfNotExists(channel)
|
|
|
|
|
2016-11-13 10:46:44 +00:00
|
|
|
s.m.Lock()
|
2016-11-13 08:30:43 +00:00
|
|
|
if _, ok := s.channels[channel].users[nick]; ok {
|
|
|
|
delete(s.channels[channel].users, nick)
|
|
|
|
}
|
2016-11-13 10:46:44 +00:00
|
|
|
s.m.Unlock()
|
2016-11-13 08:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// renameUser renames the user in state, in all locations where
|
|
|
|
// relevant
|
|
|
|
func (s *State) renameUser(from, to string) {
|
|
|
|
s.m.Lock()
|
|
|
|
defer s.m.Unlock()
|
|
|
|
|
|
|
|
for k := range s.channels {
|
|
|
|
// check to see if they're in this channel
|
|
|
|
if _, ok := s.channels[k].users[from]; !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// take the actual reference to the pointer
|
|
|
|
source := *s.channels[k].users[from]
|
|
|
|
|
|
|
|
// update the nick field (as we not only have a key, but a
|
|
|
|
// matching struct field)
|
|
|
|
source.Nick = to
|
|
|
|
|
|
|
|
// delete the old
|
|
|
|
delete(s.channels[k].users, from)
|
|
|
|
|
|
|
|
// in with the new
|
|
|
|
s.channels[k].users[to] = &source
|
|
|
|
}
|
|
|
|
}
|