Overhaul, breaking change in GetPerms
This commit is contained in:
parent
0d38e8f3d9
commit
633a5dea16
@ -18,9 +18,6 @@ import (
|
|||||||
func (c *Client) registerBuiltins() {
|
func (c *Client) registerBuiltins() {
|
||||||
c.debug.Print("registering built-in handlers")
|
c.debug.Print("registering built-in handlers")
|
||||||
|
|
||||||
c.Handlers.mu.Lock()
|
|
||||||
defer c.Handlers.mu.Unlock()
|
|
||||||
|
|
||||||
// Built-in things that should always be supported.
|
// Built-in things that should always be supported.
|
||||||
c.Handlers.register(true, true, RPL_WELCOME, HandlerFunc(handleConnect))
|
c.Handlers.register(true, true, RPL_WELCOME, HandlerFunc(handleConnect))
|
||||||
c.Handlers.register(true, false, PING, HandlerFunc(handlePING))
|
c.Handlers.register(true, false, PING, HandlerFunc(handlePING))
|
||||||
@ -219,9 +216,6 @@ func handlePART(c *Client, e Event) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.state.Lock()
|
|
||||||
defer c.state.Unlock()
|
|
||||||
|
|
||||||
c.debug.Println("handlePart")
|
c.debug.Println("handlePart")
|
||||||
defer c.debug.Println("handlePart done for " + e.Params[0])
|
defer c.debug.Println("handlePart done for " + e.Params[0])
|
||||||
|
|
||||||
@ -238,9 +232,7 @@ func handlePART(c *Client, e Event) {
|
|||||||
|
|
||||||
if chn := c.LookupChannel(channel); chn != nil {
|
if chn := c.LookupChannel(channel); chn != nil {
|
||||||
chn.UserList.Remove(e.Source.ID())
|
chn.UserList.Remove(e.Source.ID())
|
||||||
c.state.Unlock()
|
|
||||||
c.debug.Println(fmt.Sprintf("removed: %s, new count: %d", e.Source.ID(), chn.Len()))
|
c.debug.Println(fmt.Sprintf("removed: %s, new count: %d", e.Source.ID(), chn.Len()))
|
||||||
c.state.Lock()
|
|
||||||
} else {
|
} else {
|
||||||
c.debug.Println("failed to lookup channel: " + channel)
|
c.debug.Println("failed to lookup channel: " + channel)
|
||||||
}
|
}
|
||||||
@ -251,7 +243,6 @@ func handlePART(c *Client, e Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.state.deleteUser(channel, e.Source.ID())
|
c.state.deleteUser(channel, e.Source.ID())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleCREATIONTIME handles incoming TOPIC events and keeps channel tracking info
|
// handleCREATIONTIME handles incoming TOPIC events and keeps channel tracking info
|
||||||
|
13
cap.go
13
cap.go
@ -122,8 +122,7 @@ func handleCAP(c *Client, e Event) {
|
|||||||
if len(e.Params) >= 2 && e.Params[1] == CAP_DEL {
|
if len(e.Params) >= 2 && e.Params[1] == CAP_DEL {
|
||||||
caps := parseCap(e.Last())
|
caps := parseCap(e.Last())
|
||||||
for capab := range caps {
|
for capab := range caps {
|
||||||
// TODO: test the deletion.
|
c.state.enabledCap.Remove(capab)
|
||||||
delete(c.state.enabledCap, capab)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -194,10 +193,10 @@ func handleCAP(c *Client, e Event) {
|
|||||||
enabled := strings.Split(e.Last(), " ")
|
enabled := strings.Split(e.Last(), " ")
|
||||||
for _, capab := range enabled {
|
for _, capab := range enabled {
|
||||||
if val, ok := c.state.tmpCap[capab]; ok {
|
if val, ok := c.state.tmpCap[capab]; ok {
|
||||||
c.state.enabledCap[capab] = val
|
c.state.enabledCap.Set(capab, val)
|
||||||
} else {
|
continue
|
||||||
c.state.enabledCap[capab] = nil
|
|
||||||
}
|
}
|
||||||
|
c.state.enabledCap.Remove(capab)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anything client side that needs to be setup post-capability-acknowledgement,
|
// Anything client side that needs to be setup post-capability-acknowledgement,
|
||||||
@ -205,7 +204,7 @@ func handleCAP(c *Client, e Event) {
|
|||||||
|
|
||||||
// Handle STS, and only if it's something specifically we enabled (client
|
// Handle STS, and only if it's something specifically we enabled (client
|
||||||
// may choose to disable girc automatic STS, and do it themselves).
|
// may choose to disable girc automatic STS, and do it themselves).
|
||||||
if sts, sok := c.state.enabledCap["sts"]; sok && !c.Config.DisableSTS {
|
if sts, sok := c.state.enabledCap.Get("sts"); sok && !c.Config.DisableSTS {
|
||||||
var isError bool
|
var isError bool
|
||||||
// Some things are updated in the policy depending on if the current
|
// Some things are updated in the policy depending on if the current
|
||||||
// connection is over tls or not.
|
// connection is over tls or not.
|
||||||
@ -285,7 +284,7 @@ func handleCAP(c *Client, e Event) {
|
|||||||
// due to cap-notify, we can re-evaluate what we can support.
|
// due to cap-notify, we can re-evaluate what we can support.
|
||||||
c.state.tmpCap = make(map[string]map[string]string)
|
c.state.tmpCap = make(map[string]map[string]string)
|
||||||
|
|
||||||
if _, ok := c.state.enabledCap["sasl"]; ok && c.Config.SASL != nil {
|
if _, ok := c.state.enabledCap.Get("sasl"); ok && c.Config.SASL != nil {
|
||||||
c.write(&Event{Command: AUTHENTICATE, Params: []string{c.Config.SASL.Method()}})
|
c.write(&Event{Command: AUTHENTICATE, Params: []string{c.Config.SASL.Method()}})
|
||||||
// Don't "CAP END", since we want to authenticate.
|
// Don't "CAP END", since we want to authenticate.
|
||||||
return
|
return
|
||||||
|
36
client.go
36
client.go
@ -21,6 +21,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
cmap "github.com/orcaman/concurrent-map/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client contains all of the information necessary to run a single IRC
|
// Client contains all of the information necessary to run a single IRC
|
||||||
@ -340,7 +342,12 @@ func New(config Config) *Client {
|
|||||||
c.Handlers = newCaller(c, c.debug)
|
c.Handlers = newCaller(c, c.debug)
|
||||||
|
|
||||||
// Give ourselves a new state.
|
// Give ourselves a new state.
|
||||||
c.state = &state{}
|
c.state = &state{
|
||||||
|
channels: cmap.New[*Channel](),
|
||||||
|
users: cmap.New[*User](),
|
||||||
|
enabledCap: cmap.New[map[string]string](),
|
||||||
|
serverOptions: cmap.New[string](),
|
||||||
|
}
|
||||||
c.state.RWMutex = &sync.RWMutex{}
|
c.state.RWMutex = &sync.RWMutex{}
|
||||||
c.state.reset(true)
|
c.state.reset(true)
|
||||||
|
|
||||||
@ -600,7 +607,7 @@ func (c *Client) ChannelList() []string {
|
|||||||
|
|
||||||
channels := make([]string, 0, len(c.state.channels.Keys()))
|
channels := make([]string, 0, len(c.state.channels.Keys()))
|
||||||
for channel := range c.state.channels.IterBuffered() {
|
for channel := range c.state.channels.IterBuffered() {
|
||||||
chn := channel.Val.(*Channel)
|
chn := channel.Val
|
||||||
if !chn.UserIn(c.GetNick()) {
|
if !chn.UserIn(c.GetNick()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -616,9 +623,9 @@ func (c *Client) ChannelList() []string {
|
|||||||
func (c *Client) Channels() []*Channel {
|
func (c *Client) Channels() []*Channel {
|
||||||
c.panicIfNotTracking()
|
c.panicIfNotTracking()
|
||||||
|
|
||||||
channels := make([]*Channel, 0, len(c.state.channels))
|
channels := make([]*Channel, 0, c.state.channels.Count())
|
||||||
for channel := range c.state.channels.IterBuffered() {
|
for channel := range c.state.channels.IterBuffered() {
|
||||||
chn := channel.Val.(*Channel)
|
chn := channel.Val
|
||||||
channels = append(channels, chn.Copy())
|
channels = append(channels, chn.Copy())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -633,9 +640,9 @@ func (c *Client) Channels() []*Channel {
|
|||||||
func (c *Client) UserList() []string {
|
func (c *Client) UserList() []string {
|
||||||
c.panicIfNotTracking()
|
c.panicIfNotTracking()
|
||||||
|
|
||||||
users := make([]string, 0, len(c.state.users))
|
users := make([]string, 0, c.state.users.Count())
|
||||||
for user := range c.state.users.IterBuffered() {
|
for user := range c.state.users.IterBuffered() {
|
||||||
usr := user.Val.(*User)
|
usr := user.Val
|
||||||
if usr.Stale {
|
if usr.Stale {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -651,9 +658,9 @@ func (c *Client) UserList() []string {
|
|||||||
func (c *Client) Users() []*User {
|
func (c *Client) Users() []*User {
|
||||||
c.panicIfNotTracking()
|
c.panicIfNotTracking()
|
||||||
|
|
||||||
users := make([]*User, 0, len(c.state.users))
|
users := make([]*User, 0, c.state.users.Count())
|
||||||
for user := range c.state.users.IterBuffered() {
|
for user := range c.state.users.IterBuffered() {
|
||||||
usr := user.Val.(*User)
|
usr := user.Val
|
||||||
users = append(users, usr.Copy())
|
users = append(users, usr.Copy())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -703,17 +710,14 @@ func (c *Client) IsInChannel(channel string) (in bool) {
|
|||||||
// Will panic if used when tracking has been disabled. Examples of usage:
|
// Will panic if used when tracking has been disabled. Examples of usage:
|
||||||
//
|
//
|
||||||
// nickLen, success := GetServerOpt("MAXNICKLEN")
|
// nickLen, success := GetServerOpt("MAXNICKLEN")
|
||||||
//
|
|
||||||
func (c *Client) GetServerOpt(key string) (result string, ok bool) {
|
func (c *Client) GetServerOpt(key string) (result string, ok bool) {
|
||||||
c.panicIfNotTracking()
|
c.panicIfNotTracking()
|
||||||
|
|
||||||
oi, ok := c.state.serverOptions.Get(key)
|
result, ok = c.state.serverOptions.Get(key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", ok
|
return "", ok
|
||||||
}
|
}
|
||||||
|
|
||||||
result = oi.(string)
|
|
||||||
|
|
||||||
if len(result) > 0 {
|
if len(result) > 0 {
|
||||||
ok = true
|
ok = true
|
||||||
}
|
}
|
||||||
@ -726,7 +730,7 @@ func (c *Client) GetServerOpt(key string) (result string, ok bool) {
|
|||||||
func (c *Client) GetServerOptions() []byte {
|
func (c *Client) GetServerOptions() []byte {
|
||||||
o := make(map[string]string)
|
o := make(map[string]string)
|
||||||
for opt := range c.state.serverOptions.IterBuffered() {
|
for opt := range c.state.serverOptions.IterBuffered() {
|
||||||
o[opt.Key] = opt.Val.(string)
|
o[opt.Key] = opt.Val
|
||||||
}
|
}
|
||||||
jcytes, _ := json.Marshal(o)
|
jcytes, _ := json.Marshal(o)
|
||||||
return jcytes
|
return jcytes
|
||||||
@ -799,15 +803,13 @@ func (c *Client) HasCapability(name string) (has bool) {
|
|||||||
|
|
||||||
name = strings.ToLower(name)
|
name = strings.ToLower(name)
|
||||||
|
|
||||||
c.state.RLock()
|
for capab := range c.state.enabledCap.IterBuffered() {
|
||||||
for key := range c.state.enabledCap {
|
key := strings.ToLower(capab.Key)
|
||||||
key = strings.ToLower(key)
|
|
||||||
if key == name {
|
if key == name {
|
||||||
has = true
|
has = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.state.RUnlock()
|
|
||||||
|
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
6
conn.go
6
conn.go
@ -509,15 +509,13 @@ func (c *Client) sendLoop(ctx context.Context, errs chan error, working *int32)
|
|||||||
// Check if tags exist on the event. If they do, and message-tags
|
// Check if tags exist on the event. If they do, and message-tags
|
||||||
// isn't a supported capability, remove them from the event.
|
// isn't a supported capability, remove them from the event.
|
||||||
if event.Tags != nil {
|
if event.Tags != nil {
|
||||||
c.state.RLock()
|
|
||||||
var in bool
|
var in bool
|
||||||
for i := 0; i < len(c.state.enabledCap); i++ {
|
for i := 0; i < c.state.enabledCap.Count(); i++ {
|
||||||
if _, ok := c.state.enabledCap["message-tags"]; ok {
|
if _, ok := c.state.enabledCap.Get("message-tags"); ok {
|
||||||
in = true
|
in = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.state.RUnlock()
|
|
||||||
|
|
||||||
if !in {
|
if !in {
|
||||||
event.Tags = Tags{}
|
event.Tags = Tags{}
|
||||||
|
37
ctcp.go
37
ctcp.go
@ -9,6 +9,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
cmap "github.com/orcaman/concurrent-map/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ctcpDelim if the delimiter used for CTCP formatted events/messages.
|
// ctcpDelim if the delimiter used for CTCP formatted events/messages.
|
||||||
@ -122,12 +124,12 @@ type CTCP struct {
|
|||||||
// mu is the mutex that should be used when accessing any ctcp handlers.
|
// mu is the mutex that should be used when accessing any ctcp handlers.
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
// handlers is a map of CTCP message -> functions.
|
// handlers is a map of CTCP message -> functions.
|
||||||
handlers map[string]CTCPHandler
|
handlers cmap.ConcurrentMap[string, CTCPHandler]
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCTCP returns a new clean CTCP handler.
|
// newCTCP returns a new clean CTCP handler.
|
||||||
func newCTCP() *CTCP {
|
func newCTCP() *CTCP {
|
||||||
return &CTCP{handlers: map[string]CTCPHandler{}}
|
return &CTCP{handlers: cmap.New[CTCPHandler]()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// call executes the necessary CTCP handler for the incoming event/CTCP
|
// call executes the necessary CTCP handler for the incoming event/CTCP
|
||||||
@ -137,23 +139,16 @@ func (c *CTCP) call(client *Client, event *CTCPEvent) {
|
|||||||
if client.Config.RecoverFunc != nil && event.Origin != nil {
|
if client.Config.RecoverFunc != nil && event.Origin != nil {
|
||||||
defer recoverHandlerPanic(client, event.Origin, "ctcp-"+strings.ToLower(event.Command), 3)
|
defer recoverHandlerPanic(client, event.Origin, "ctcp-"+strings.ToLower(event.Command), 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support wildcard CTCP event handling. Gets executed first before
|
// Support wildcard CTCP event handling. Gets executed first before
|
||||||
// regular event handlers.
|
// regular event handlers.
|
||||||
if _, ok := c.handlers["*"]; ok {
|
if val, ok := c.handlers.Get("*"); ok && val != nil {
|
||||||
c.handlers["*"](client, *event)
|
val(client, *event)
|
||||||
}
|
}
|
||||||
|
val, ok := c.handlers.Get(event.Command)
|
||||||
if _, ok := c.handlers[event.Command]; !ok {
|
if !ok || val == nil || event.Command == CTCP_ACTION {
|
||||||
// If ACTION, don't do anything.
|
|
||||||
if event.Command == CTCP_ACTION {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
val(client, *event)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.handlers[event.Command](client, *event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseCMD parses a CTCP command/tag, ensuring it's valid. If not, an empty
|
// parseCMD parses a CTCP command/tag, ensuring it's valid. If not, an empty
|
||||||
@ -185,9 +180,7 @@ func (c *CTCP) Set(cmd string, handler func(client *Client, ctcp CTCPEvent)) {
|
|||||||
if cmd = c.parseCMD(cmd); cmd == "" {
|
if cmd = c.parseCMD(cmd); cmd == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.handlers.Set(cmd, handler)
|
||||||
c.handlers[cmd] = handler
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBg is much like Set, however the handler is executed in the background,
|
// SetBg is much like Set, however the handler is executed in the background,
|
||||||
@ -204,18 +197,12 @@ func (c *CTCP) Clear(cmd string) {
|
|||||||
if cmd = c.parseCMD(cmd); cmd == "" {
|
if cmd = c.parseCMD(cmd); cmd == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
c.handlers.Remove(cmd)
|
||||||
c.mu.Lock()
|
|
||||||
delete(c.handlers, cmd)
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearAll removes all currently setup and re-sets the default handlers.
|
// ClearAll removes all currently setup and re-sets the default handlers.
|
||||||
func (c *CTCP) ClearAll() {
|
func (c *CTCP) ClearAll() {
|
||||||
c.mu.Lock()
|
c.handlers = cmap.New[CTCPHandler]()
|
||||||
c.handlers = map[string]CTCPHandler{}
|
|
||||||
c.mu.Unlock()
|
|
||||||
|
|
||||||
// Register necessary handlers.
|
// Register necessary handlers.
|
||||||
c.addDefaultHandlers()
|
c.addDefaultHandlers()
|
||||||
}
|
}
|
||||||
|
10
ctcp_test.go
10
ctcp_test.go
@ -162,13 +162,13 @@ func TestSet(t *testing.T) {
|
|||||||
ctcp := newCTCP()
|
ctcp := newCTCP()
|
||||||
|
|
||||||
ctcp.Set("TEST-1", func(client *Client, event CTCPEvent) {})
|
ctcp.Set("TEST-1", func(client *Client, event CTCPEvent) {})
|
||||||
if _, ok := ctcp.handlers["TEST"]; ok {
|
if _, ok := ctcp.handlers.Get("TEST"); ok {
|
||||||
t.Fatal("Set('TEST') allowed invalid command")
|
t.Fatal("Set('TEST') allowed invalid command")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctcp.Set("TEST", func(client *Client, event CTCPEvent) {})
|
ctcp.Set("TEST", func(client *Client, event CTCPEvent) {})
|
||||||
// Make sure it's there.
|
// Make sure it's there.
|
||||||
if _, ok := ctcp.handlers["TEST"]; !ok {
|
if _, ok := ctcp.handlers.Get("TEST"); !ok {
|
||||||
t.Fatal("store: Set('TEST') didn't set")
|
t.Fatal("store: Set('TEST') didn't set")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,7 +179,7 @@ func TestClear(t *testing.T) {
|
|||||||
ctcp.Set("TEST", func(client *Client, event CTCPEvent) {})
|
ctcp.Set("TEST", func(client *Client, event CTCPEvent) {})
|
||||||
ctcp.Clear("TEST")
|
ctcp.Clear("TEST")
|
||||||
|
|
||||||
if _, ok := ctcp.handlers["TEST"]; ok {
|
if _, ok := ctcp.handlers.Get("TEST"); ok {
|
||||||
t.Fatal("ctcp.Clear('TEST') didn't remove handler")
|
t.Fatal("ctcp.Clear('TEST') didn't remove handler")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,8 +191,8 @@ func TestClearAll(t *testing.T) {
|
|||||||
ctcp.Set("TEST2", func(client *Client, event CTCPEvent) {})
|
ctcp.Set("TEST2", func(client *Client, event CTCPEvent) {})
|
||||||
ctcp.ClearAll()
|
ctcp.ClearAll()
|
||||||
|
|
||||||
_, first := ctcp.handlers["TEST1"]
|
_, first := ctcp.handlers.Get("TEST1")
|
||||||
_, second := ctcp.handlers["TEST2"]
|
_, second := ctcp.handlers.Get("TEST2")
|
||||||
|
|
||||||
if first || second {
|
if first || second {
|
||||||
t.Fatalf("ctcp.ClearAll() didn't remove all handlers: 1: %v 2: %v", first, second)
|
t.Fatalf("ctcp.ClearAll() didn't remove all handlers: 1: %v 2: %v", first, second)
|
||||||
|
6
go.mod
6
go.mod
@ -1,9 +1,9 @@
|
|||||||
module github.com/yunginnanet/girc-atomic
|
module github.com/yunginnanet/girc-atomic
|
||||||
|
|
||||||
go 1.19
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.tcp.direct/kayos/common v0.7.6
|
git.tcp.direct/kayos/common v0.8.1
|
||||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
|
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
|
||||||
github.com/orcaman/concurrent-map v1.0.0
|
github.com/orcaman/concurrent-map/v2 v2.0.1
|
||||||
)
|
)
|
||||||
|
8
go.sum
8
go.sum
@ -1,12 +1,12 @@
|
|||||||
git.tcp.direct/kayos/common v0.7.6 h1:RThBVa6xKF6ybRURBgzobEHsRi8nYoYp3Z1PE2qtKx8=
|
git.tcp.direct/kayos/common v0.8.1 h1:gxcCaa7QlQzkvBPzcwoVyP89mexrxKvnmlnvh4PGu4o=
|
||||||
git.tcp.direct/kayos/common v0.7.6/go.mod h1:jVbdX9prBrx9e3aTsNpu643brGVgpLvysl40/F5U2cE=
|
git.tcp.direct/kayos/common v0.8.1/go.mod h1:r7lZuKTQz0uf/jNm61sz1XaMgK/RYRr7wtqr/cNYd8o=
|
||||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
|
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
|
||||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY=
|
github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=
|
||||||
github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
|
github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
50
handler.go
50
handler.go
@ -15,7 +15,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/orcaman/concurrent-map"
|
cmap "github.com/orcaman/concurrent-map/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunHandlers manually runs handlers for a given event.
|
// RunHandlers manually runs handlers for a given event.
|
||||||
@ -81,7 +81,7 @@ func (f HandlerFunc) Execute(client *Client, event Event) {
|
|||||||
//
|
//
|
||||||
// command and cuid are both strings.
|
// command and cuid are both strings.
|
||||||
type nestedHandlers struct {
|
type nestedHandlers struct {
|
||||||
cm cmap.ConcurrentMap
|
cm cmap.ConcurrentMap[string, cmap.ConcurrentMap[string, Handler]]
|
||||||
}
|
}
|
||||||
|
|
||||||
type handlerTuple struct {
|
type handlerTuple struct {
|
||||||
@ -90,40 +90,37 @@ type handlerTuple struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newNestedHandlers() *nestedHandlers {
|
func newNestedHandlers() *nestedHandlers {
|
||||||
return &nestedHandlers{cm: cmap.New()}
|
return &nestedHandlers{cm: cmap.New[cmap.ConcurrentMap[string, Handler]]()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nest *nestedHandlers) len() (total int) {
|
func (nest *nestedHandlers) len() (total int) {
|
||||||
for hs := range nest.cm.IterBuffered() {
|
for hndlrs := range nest.cm.IterBuffered() {
|
||||||
hndlrs := hs.Val.(cmap.ConcurrentMap)
|
total += len(hndlrs.Val.Keys())
|
||||||
total += len(hndlrs.Keys())
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nest *nestedHandlers) lenFor(cmd string) (total int) {
|
func (nest *nestedHandlers) lenFor(cmd string) (total int) {
|
||||||
cmd = strings.ToUpper(cmd)
|
cmd = strings.ToUpper(cmd)
|
||||||
hs, ok := nest.cm.Get(cmd)
|
hndlrs, ok := nest.cm.Get(cmd)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
hndlrs := hs.(cmap.ConcurrentMap)
|
|
||||||
return hndlrs.Count()
|
return hndlrs.Count()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nest *nestedHandlers) getAllHandlersFor(s string) (handlers chan handlerTuple, ok bool) {
|
func (nest *nestedHandlers) getAllHandlersFor(s string) (handlers chan handlerTuple, ok bool) {
|
||||||
var h interface{}
|
var h cmap.ConcurrentMap[string, Handler]
|
||||||
h, ok = nest.cm.Get(s)
|
h, ok = nest.cm.Get(s)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hm := h.(cmap.ConcurrentMap)
|
|
||||||
handlers = make(chan handlerTuple)
|
handlers = make(chan handlerTuple)
|
||||||
go func() {
|
go func() {
|
||||||
for hi := range hm.IterBuffered() {
|
for hi := range h.IterBuffered() {
|
||||||
ht := handlerTuple{
|
ht := handlerTuple{
|
||||||
hi.Key,
|
hi.Key,
|
||||||
hi.Val.(Handler),
|
hi.Val,
|
||||||
}
|
}
|
||||||
handlers <- ht
|
handlers <- ht
|
||||||
}
|
}
|
||||||
@ -221,9 +218,8 @@ func (c *Caller) exec(command string, bg bool, client *Client, event *Event) {
|
|||||||
var stack []execStack
|
var stack []execStack
|
||||||
|
|
||||||
// Get internal handlers first.
|
// Get internal handlers first.
|
||||||
ihm, iok := c.internal.cm.Get(command)
|
hmap, iok := c.internal.cm.Get(command)
|
||||||
if iok {
|
if iok {
|
||||||
hmap := ihm.(cmap.ConcurrentMap)
|
|
||||||
for assigned := range hmap.IterBuffered() {
|
for assigned := range hmap.IterBuffered() {
|
||||||
cuid := assigned.Key
|
cuid := assigned.Key
|
||||||
if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
|
if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
|
||||||
@ -238,18 +234,13 @@ func (c *Caller) exec(command string, bg bool, client *Client, event *Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Then external handlers.
|
// Then external handlers.
|
||||||
ehm, eok := c.external.cm.Get(command)
|
hmap, eok := c.external.cm.Get(command)
|
||||||
if eok {
|
if eok {
|
||||||
hmap := ehm.(cmap.ConcurrentMap)
|
|
||||||
for _, cuid := range hmap.Keys() {
|
for _, cuid := range hmap.Keys() {
|
||||||
if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
|
if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
hi, _ := hmap.Get(cuid)
|
hndlr, _ := hmap.Get(cuid)
|
||||||
hndlr, ok := hi.(Handler)
|
|
||||||
if !ok {
|
|
||||||
panic("improper handler type in map")
|
|
||||||
}
|
|
||||||
stack = append(stack, execStack{hndlr, cuid})
|
stack = append(stack, execStack{hndlr, cuid})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,14 +328,12 @@ func (c *Caller) remove(cuid string) (ok bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the irc command/event has any handlers on it.
|
// Check if the irc command/event has any handlers on it.
|
||||||
var h interface{}
|
var hs cmap.ConcurrentMap[string, Handler]
|
||||||
h, ok = c.external.cm.Get(cmd)
|
hs, ok = c.external.cm.Get(cmd)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hs := h.(cmap.ConcurrentMap)
|
|
||||||
|
|
||||||
// Check to see if it's actually a registered handler.
|
// Check to see if it's actually a registered handler.
|
||||||
if _, ok = hs.Get(cuid); !ok {
|
if _, ok = hs.Get(cuid); !ok {
|
||||||
return
|
return
|
||||||
@ -381,8 +370,7 @@ func (c *Caller) register(internal, bg bool, cmd string, handler Handler) (cuid
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
parent *nestedHandlers
|
parent *nestedHandlers
|
||||||
chandlers cmap.ConcurrentMap
|
chandlers cmap.ConcurrentMap[string, Handler]
|
||||||
ei interface{}
|
|
||||||
ok bool
|
ok bool
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -392,12 +380,10 @@ func (c *Caller) register(internal, bg bool, cmd string, handler Handler) (cuid
|
|||||||
parent = c.external
|
parent = c.external
|
||||||
}
|
}
|
||||||
|
|
||||||
ei, ok = parent.cm.Get(cmd)
|
chandlers, ok = parent.cm.Get(cmd)
|
||||||
|
|
||||||
if ok {
|
if !ok {
|
||||||
chandlers = ei.(cmap.ConcurrentMap)
|
chandlers = cmap.New[Handler]()
|
||||||
} else {
|
|
||||||
chandlers = cmap.New()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chandlers.Set(uid, handler)
|
chandlers.Set(uid, handler)
|
||||||
|
31
modes.go
31
modes.go
@ -8,7 +8,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
cmap "github.com/orcaman/concurrent-map"
|
cmap "github.com/orcaman/concurrent-map/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CMode represents a single step of a given mode change.
|
// CMode represents a single step of a given mode change.
|
||||||
@ -370,13 +370,9 @@ func handleMODE(c *Client, e Event) {
|
|||||||
// chanModes returns the ISUPPORT list of server-supported channel modes,
|
// chanModes returns the ISUPPORT list of server-supported channel modes,
|
||||||
// alternatively falling back to ModeDefaults.
|
// alternatively falling back to ModeDefaults.
|
||||||
func (s *state) chanModes() string {
|
func (s *state) chanModes() string {
|
||||||
if validmodes, ok := s.serverOptions.Get("CHANMODES"); ok {
|
if validmodes, ok := s.serverOptions.Get("CHANMODES"); ok && IsValidChannelMode(validmodes) {
|
||||||
modes := validmodes.(string)
|
return validmodes
|
||||||
if IsValidChannelMode(modes) {
|
|
||||||
return modes
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return ModeDefaults
|
return ModeDefaults
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,26 +380,22 @@ func (s *state) chanModes() string {
|
|||||||
// This includes mode characters, as well as user prefix symbols. Falls back
|
// This includes mode characters, as well as user prefix symbols. Falls back
|
||||||
// to DefaultPrefixes if not server-supported.
|
// to DefaultPrefixes if not server-supported.
|
||||||
func (s *state) userPrefixes() string {
|
func (s *state) userPrefixes() string {
|
||||||
if pi, ok := s.serverOptions.Get("PREFIX"); ok {
|
if prefix, ok := s.serverOptions.Get("PREFIX"); ok && isValidUserPrefix(prefix) {
|
||||||
prefix := pi.(string)
|
|
||||||
if isValidUserPrefix(prefix) {
|
|
||||||
return prefix
|
return prefix
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return DefaultPrefixes
|
return DefaultPrefixes
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserPerms contains all of the permissions for each channel the user is
|
// UserPerms contains all of the permissions for each channel the user is
|
||||||
// in.
|
// in.
|
||||||
type UserPerms struct {
|
type UserPerms struct {
|
||||||
channels cmap.ConcurrentMap
|
channels cmap.ConcurrentMap[string, *Perms]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a deep copy of the channel permissions.
|
// Copy returns a deep copy of the channel permissions.
|
||||||
func (p *UserPerms) Copy() (perms *UserPerms) {
|
func (p *UserPerms) Copy() (perms *UserPerms) {
|
||||||
np := &UserPerms{
|
np := &UserPerms{
|
||||||
channels: cmap.New(),
|
channels: cmap.New[*Perms](),
|
||||||
}
|
}
|
||||||
for tuple := range p.channels.IterBuffered() {
|
for tuple := range p.channels.IterBuffered() {
|
||||||
np.channels.Set(tuple.Key, tuple.Val)
|
np.channels.Set(tuple.Key, tuple.Val)
|
||||||
@ -419,16 +411,11 @@ func (p *UserPerms) MarshalJSON() ([]byte, error) {
|
|||||||
|
|
||||||
// Lookup looks up the users permissions for a given channel. ok is false
|
// Lookup looks up the users permissions for a given channel. ok is false
|
||||||
// if the user is not in the given channel.
|
// if the user is not in the given channel.
|
||||||
func (p *UserPerms) Lookup(channel string) (perms Perms, ok bool) {
|
func (p *UserPerms) Lookup(channel string) (perms *Perms, ok bool) {
|
||||||
var permsi interface{}
|
return p.channels.Get(ToRFC1459(channel))
|
||||||
permsi, ok = p.channels.Get(ToRFC1459(channel))
|
|
||||||
if ok {
|
|
||||||
perms = permsi.(Perms)
|
|
||||||
}
|
|
||||||
return perms, ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *UserPerms) set(channel string, perms Perms) {
|
func (p *UserPerms) set(channel string, perms *Perms) {
|
||||||
p.channels.Set(ToRFC1459(channel), perms)
|
p.channels.Set(ToRFC1459(channel), perms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
105
state.go
105
state.go
@ -10,7 +10,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
cmap "github.com/orcaman/concurrent-map"
|
cmap "github.com/orcaman/concurrent-map/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// state represents the actively-changing variables within the client
|
// state represents the actively-changing variables within the client
|
||||||
@ -22,12 +22,13 @@ type state struct {
|
|||||||
nick, ident, host atomic.Value
|
nick, ident, host atomic.Value
|
||||||
// channels represents all channels we're active in.
|
// channels represents all channels we're active in.
|
||||||
// channels map[string]*Channel
|
// channels map[string]*Channel
|
||||||
channels cmap.ConcurrentMap
|
channels cmap.ConcurrentMap[string, *Channel]
|
||||||
// users represents all of users that we're tracking.
|
// users represents all of users that we're tracking.
|
||||||
// users map[string]*User
|
// users map[string]*User
|
||||||
users cmap.ConcurrentMap
|
users cmap.ConcurrentMap[string, *User]
|
||||||
// enabledCap are the capabilities which are enabled for this connection.
|
// enabledCap are the capabilities which are enabled for this connection.
|
||||||
enabledCap map[string]map[string]string
|
// enabledCap map[string]map[string]string
|
||||||
|
enabledCap cmap.ConcurrentMap[string, map[string]string]
|
||||||
// tmpCap are the capabilties which we share with the server during the
|
// tmpCap are the capabilties which we share with the server during the
|
||||||
// last capability check. These will get sent once we have received the
|
// last capability check. These will get sent once we have received the
|
||||||
// last capability list command from the server.
|
// last capability list command from the server.
|
||||||
@ -35,7 +36,8 @@ type state struct {
|
|||||||
// serverOptions are the standard capabilities and configurations
|
// serverOptions are the standard capabilities and configurations
|
||||||
// supported by the server at connection time. This also includes
|
// supported by the server at connection time. This also includes
|
||||||
// RPL_ISUPPORT entries.
|
// RPL_ISUPPORT entries.
|
||||||
serverOptions cmap.ConcurrentMap
|
// serverOptions map[string]string
|
||||||
|
serverOptions cmap.ConcurrentMap[string, string]
|
||||||
|
|
||||||
// network is an alternative way to store and retrieve the NETWORK server option.
|
// network is an alternative way to store and retrieve the NETWORK server option.
|
||||||
network atomic.Value
|
network atomic.Value
|
||||||
@ -54,22 +56,31 @@ type state struct {
|
|||||||
sts strictTransport
|
sts strictTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Clearer interface {
|
||||||
|
Clear()
|
||||||
|
}
|
||||||
|
|
||||||
// reset resets the state back to it's original form.
|
// reset resets the state back to it's original form.
|
||||||
func (s *state) reset(initial bool) {
|
func (s *state) reset(initial bool) {
|
||||||
s.nick.Store("")
|
s.nick.Store("")
|
||||||
s.ident.Store("")
|
s.ident.Store("")
|
||||||
s.host.Store("")
|
s.host.Store("")
|
||||||
s.network.Store("")
|
s.network.Store("")
|
||||||
var cmaps = []*cmap.ConcurrentMap{&s.channels, &s.users, &s.serverOptions}
|
var cmaps = []Clearer{&s.channels, &s.users, &s.serverOptions}
|
||||||
for _, cm := range cmaps {
|
for i, cm := range cmaps {
|
||||||
if initial {
|
switch {
|
||||||
*cm = cmap.New()
|
case i == 0 && initial:
|
||||||
} else {
|
cm = cmap.New[*Channel]()
|
||||||
|
case i == 1 && initial:
|
||||||
|
cm = cmap.New[*User]()
|
||||||
|
case i == 2 && initial:
|
||||||
|
cm = cmap.New[string]()
|
||||||
|
default:
|
||||||
cm.Clear()
|
cm.Clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.enabledCap = make(map[string]map[string]string)
|
s.enabledCap = cmap.New[map[string]string]()
|
||||||
s.tmpCap = make(map[string]map[string]string)
|
s.tmpCap = make(map[string]map[string]string)
|
||||||
s.motd = ""
|
s.motd = ""
|
||||||
|
|
||||||
@ -106,8 +117,8 @@ type User struct {
|
|||||||
//
|
//
|
||||||
// NOTE: If the ChannelList is empty for the user, then the user's info could be out of date.
|
// NOTE: If the ChannelList is empty for the user, then the user's info could be out of date.
|
||||||
// turns out Concurrent-Map implements json.Marhsal!
|
// turns out Concurrent-Map implements json.Marhsal!
|
||||||
// https://github.com/orcaman/concurrent-map/blob/893feb299719d9cbb2cfbe08b6dd4eb567d8039d/concurrent_map.go#L305
|
// https://github.com/orcaman/concurrent-map/v2/blob/893feb299719d9cbb2cfbe08b6dd4eb567d8039d/concurrent_map.go#L305
|
||||||
ChannelList cmap.ConcurrentMap `json:"channels"`
|
ChannelList cmap.ConcurrentMap[string, *Channel] `json:"channels"`
|
||||||
|
|
||||||
// FirstSeen represents the first time that the user was seen by the
|
// FirstSeen represents the first time that the user was seen by the
|
||||||
// client for the given channel. Only usable if from state, not in past.
|
// client for the given channel. Only usable if from state, not in past.
|
||||||
@ -152,8 +163,8 @@ func (u *User) Channels(c *Client) []*Channel {
|
|||||||
var channels []*Channel
|
var channels []*Channel
|
||||||
|
|
||||||
for listed := range u.ChannelList.IterBuffered() {
|
for listed := range u.ChannelList.IterBuffered() {
|
||||||
chn, chok := listed.Val.(*Channel)
|
chn := listed.Val
|
||||||
if chok {
|
if chn != nil {
|
||||||
channels = append(channels, chn)
|
channels = append(channels, chn)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -178,7 +189,9 @@ func (u *User) Copy() *User {
|
|||||||
*nu = *u
|
*nu = *u
|
||||||
|
|
||||||
nu.Perms = u.Perms.Copy()
|
nu.Perms = u.Perms.Copy()
|
||||||
_ = copy(nu.ChannelList, u.ChannelList)
|
for ch := range u.ChannelList.IterBuffered() {
|
||||||
|
nu.ChannelList.Set(ch.Key, ch.Val)
|
||||||
|
}
|
||||||
|
|
||||||
return nu
|
return nu
|
||||||
}
|
}
|
||||||
@ -197,7 +210,7 @@ func (u *User) addChannel(name string, chn *Channel) {
|
|||||||
|
|
||||||
u.ChannelList.Set(name, chn)
|
u.ChannelList.Set(name, chn)
|
||||||
|
|
||||||
u.Perms.set(name, Perms{})
|
u.Perms.set(name, &Perms{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteChannel removes an existing channel from the users channel list.
|
// deleteChannel removes an existing channel from the users channel list.
|
||||||
@ -246,7 +259,7 @@ type Channel struct {
|
|||||||
Created string `json:"created"`
|
Created string `json:"created"`
|
||||||
// UserList is a sorted list of all users we are currently tracking within
|
// UserList is a sorted list of all users we are currently tracking within
|
||||||
// the channel. Each is the1 nickname, and is rfc1459 compliant.
|
// the channel. Each is the1 nickname, and is rfc1459 compliant.
|
||||||
UserList cmap.ConcurrentMap `json:"user_list"`
|
UserList cmap.ConcurrentMap[string, *User] `json:"user_list"`
|
||||||
// Network is the name of the IRC network where this channel was found.
|
// Network is the name of the IRC network where this channel was found.
|
||||||
// This has been added for the purposes of girc being used in multi-client scenarios with data persistence.
|
// This has been added for the purposes of girc being used in multi-client scenarios with data persistence.
|
||||||
Network string `json:"network"`
|
Network string `json:"network"`
|
||||||
@ -312,19 +325,17 @@ func (ch *Channel) Admins(c *Client) []*User {
|
|||||||
|
|
||||||
for listed := range ch.UserList.IterBuffered() {
|
for listed := range ch.UserList.IterBuffered() {
|
||||||
ui := listed.Val
|
ui := listed.Val
|
||||||
user, usrok := ui.(*User)
|
|
||||||
if !usrok {
|
if ui == nil {
|
||||||
user = c.state.lookupUser(listed.Key)
|
if ui = c.state.lookupUser(listed.Key); ui == nil {
|
||||||
if user == nil {
|
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
ch.UserList.Set(listed.Key, user)
|
|
||||||
}
|
}
|
||||||
|
ch.UserList.Set(listed.Key, ui)
|
||||||
}
|
}
|
||||||
|
|
||||||
perms, ok := user.Perms.Lookup(ch.Name)
|
perms, ok := ui.Perms.Lookup(ch.Name)
|
||||||
if ok && perms.IsAdmin() {
|
if ok && perms.IsAdmin() {
|
||||||
users = append(users, user)
|
users = append(users, ui)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,7 +365,9 @@ func (ch *Channel) Copy() *Channel {
|
|||||||
nc := &Channel{}
|
nc := &Channel{}
|
||||||
*nc = *ch
|
*nc = *ch
|
||||||
|
|
||||||
_ = copy(nc.UserList, ch.UserList)
|
for v := range ch.UserList.IterBuffered() {
|
||||||
|
nc.UserList.Set(v.Val.Nick, v.Val)
|
||||||
|
}
|
||||||
|
|
||||||
// And modes.
|
// And modes.
|
||||||
nc.Modes = ch.Modes.Copy()
|
nc.Modes = ch.Modes.Copy()
|
||||||
@ -391,7 +404,7 @@ func (s *state) createChannel(name string) (ok bool) {
|
|||||||
|
|
||||||
s.channels.Set(ToRFC1459(name), &Channel{
|
s.channels.Set(ToRFC1459(name), &Channel{
|
||||||
Name: name,
|
Name: name,
|
||||||
UserList: cmap.New(),
|
UserList: cmap.New[*User](),
|
||||||
Joined: time.Now(),
|
Joined: time.Now(),
|
||||||
Network: s.client.NetworkName(),
|
Network: s.client.NetworkName(),
|
||||||
Modes: NewCModes(supported, prefixes),
|
Modes: NewCModes(supported, prefixes),
|
||||||
@ -404,17 +417,14 @@ func (s *state) createChannel(name string) (ok bool) {
|
|||||||
func (s *state) deleteChannel(name string) {
|
func (s *state) deleteChannel(name string) {
|
||||||
name = ToRFC1459(name)
|
name = ToRFC1459(name)
|
||||||
|
|
||||||
c, ok := s.channels.Get(name)
|
chn, ok := s.channels.Get(name)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chn := c.(*Channel)
|
|
||||||
|
|
||||||
for listed := range chn.UserList.IterBuffered() {
|
for listed := range chn.UserList.IterBuffered() {
|
||||||
ui, _ := s.users.Get(listed.Key)
|
usr, uok := s.users.Get(listed.Key)
|
||||||
usr, usrok := ui.(*User)
|
if uok {
|
||||||
if usrok {
|
|
||||||
usr.deleteChannel(name)
|
usr.deleteChannel(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -426,28 +436,26 @@ func (s *state) deleteChannel(name string) {
|
|||||||
// found.
|
// found.
|
||||||
func (s *state) lookupChannel(name string) *Channel {
|
func (s *state) lookupChannel(name string) *Channel {
|
||||||
ci, cok := s.channels.Get(ToRFC1459(name))
|
ci, cok := s.channels.Get(ToRFC1459(name))
|
||||||
chn, ok := ci.(*Channel)
|
if ci == nil || !cok {
|
||||||
if !ok || !cok {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return chn
|
return ci
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookupUser returns a reference to a user, nil returned if no results
|
// lookupUser returns a reference to a user, nil returned if no results
|
||||||
// found.
|
// found.
|
||||||
func (s *state) lookupUser(name string) *User {
|
func (s *state) lookupUser(name string) *User {
|
||||||
ui, uok := s.users.Get(ToRFC1459(name))
|
usr, uok := s.users.Get(ToRFC1459(name))
|
||||||
usr, ok := ui.(*User)
|
if usr == nil || !uok {
|
||||||
if !ok || !uok {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return usr
|
return usr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) createUser(src *Source) (u *User, ok bool) {
|
func (s *state) createUser(src *Source) (u *User, ok bool) {
|
||||||
if _, ok := s.users.Get(src.ID()); ok {
|
if u, ok = s.users.Get(src.ID()); ok {
|
||||||
// User already exists.
|
// User already exists.
|
||||||
return nil, false
|
return u, false
|
||||||
}
|
}
|
||||||
|
|
||||||
mask := strs.Get()
|
mask := strs.Get()
|
||||||
@ -462,11 +470,11 @@ func (s *state) createUser(src *Source) (u *User, ok bool) {
|
|||||||
Host: src.Host,
|
Host: src.Host,
|
||||||
Ident: src.Ident,
|
Ident: src.Ident,
|
||||||
Mask: mask.String(),
|
Mask: mask.String(),
|
||||||
ChannelList: cmap.New(),
|
ChannelList: cmap.New[*Channel](),
|
||||||
FirstSeen: time.Now(),
|
FirstSeen: time.Now(),
|
||||||
LastActive: time.Now(),
|
LastActive: time.Now(),
|
||||||
Network: s.client.NetworkName(),
|
Network: s.client.NetworkName(),
|
||||||
Perms: &UserPerms{channels: cmap.New()},
|
Perms: &UserPerms{channels: cmap.New[*Perms]()},
|
||||||
}
|
}
|
||||||
|
|
||||||
strs.MustPut(mask)
|
strs.MustPut(mask)
|
||||||
@ -521,7 +529,7 @@ func (s *state) renameUser(from, to string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if old != nil && user == nil {
|
if old != nil && user == nil {
|
||||||
user = old.(*User)
|
user = old
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Nick = to
|
user.Nick = to
|
||||||
@ -529,12 +537,11 @@ func (s *state) renameUser(from, to string) {
|
|||||||
s.users.Set(ToRFC1459(to), user)
|
s.users.Set(ToRFC1459(to), user)
|
||||||
|
|
||||||
for chanchan := range s.channels.IterBuffered() {
|
for chanchan := range s.channels.IterBuffered() {
|
||||||
chi := chanchan.Val
|
chn := chanchan.Val
|
||||||
chn, chok := chi.(*Channel)
|
if chn == nil {
|
||||||
if !chok {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if old, oldok := chn.UserList.Pop(from); oldok {
|
if old, oldok = chn.UserList.Pop(from); oldok {
|
||||||
chn.UserList.Set(to, old)
|
chn.UserList.Set(to, old)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,10 +284,9 @@ func TestState(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chi, chnok := user.ChannelList.Get("#channel")
|
chn, chnok := user.ChannelList.Get("#channel")
|
||||||
chn, chiok := chi.(*Channel)
|
|
||||||
|
|
||||||
if !chnok || !chiok {
|
if !chnok {
|
||||||
t.Errorf("should have been able to get a pointer by looking up #channel")
|
t.Errorf("should have been able to get a pointer by looking up #channel")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -297,8 +296,7 @@ func TestState(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chi2, _ := user.ChannelList.Get("#channel2")
|
chn2, _ := user.ChannelList.Get("#channel2")
|
||||||
chn2, _ := chi2.(*Channel)
|
|
||||||
|
|
||||||
if chn2.Len() != len([]string{"notjones"}) {
|
if chn2.Len() != len([]string{"notjones"}) {
|
||||||
t.Errorf("channel.UserList.Count() == %d, wanted %d",
|
t.Errorf("channel.UserList.Count() == %d, wanted %d",
|
||||||
|
Loading…
Reference in New Issue
Block a user