@@ -105,10 +105,14 @@
105 105 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
msgTotal atomic.Int64
106 106 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
107 107 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
joinCh chan string
108 108 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
client *girc.Client
109 109 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
onUserJoin func(channel, nick string) // optional callback when a non-bridge user joins
110 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
111 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // namesUsers is our own authoritative user list populated from RPL_NAMREPLY.
112 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // channel → nick → mode prefix ("@", "+", or "")
113 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ namesUsers map[string]map[string]string
110 114 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
111 115 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// RELAYMSG support detected from ISUPPORT.
112 116 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
relaySep string // separator (e.g. "/"), empty if unsupported
113 117 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
114 118 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
@@ -140,10 +144,11 @@
140 144 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
log: log,
141 145 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
buffers: make(map[string]*ringBuf),
142 146 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
subs: make(map[string]map[uint64]chan Message),
143 147 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
joined: make(map[string]bool),
144 148 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
joinCh: make(chan string, 32),
149 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ namesUsers: make(map[string]map[string]string),
145 150 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
146 151 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
147 152 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
148 153 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// SetWebUserTTL updates how long bridge-posted HTTP nicks remain visible in
149 154 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// the channel user list after their last post.
@@ -243,10 +248,39 @@
243 248 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
} else if b.onUserJoin != nil {
244 249 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Another user joined — fire callback for on-join instructions.
245 250 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
go b.onUserJoin(channel, nick)
246 251 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
247 252 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
})
253 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
254 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Parse RPL_NAMREPLY ourselves for a reliable user list.
255 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ c.Handlers.AddBg(girc.RPL_NAMREPLY, func(_ *girc.Client, e girc.Event) {
256 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Format: :server 353 bridge = #channel :@op +voice regular
257 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(e.Params) < 4 {
258 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return
259 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
260 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ channel := e.Params[2]
261 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ names := strings.Fields(e.Last())
262 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.mu.Lock()
263 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if b.namesUsers[channel] == nil {
264 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.namesUsers[channel] = make(map[string]string)
265 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
266 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for _, name := range names {
267 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ prefix := ""
268 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ nick := name
269 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if strings.HasPrefix(name, "@") {
270 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ prefix = "@"
271 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ nick = name[1:]
272 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ } else if strings.HasPrefix(name, "+") {
273 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ prefix = "+"
274 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ nick = name[1:]
275 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
276 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if nick != b.nick {
277 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.namesUsers[channel][nick] = prefix
278 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
279 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
280 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.mu.Unlock()
281 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ })
248 282 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
249 283 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) {
250 284 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if len(e.Params) < 1 || e.Source == nil {
251 285 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return
252 286 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
@@ -481,37 +515,38 @@
481 515 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
channels := make([]string, 0, len(b.joined))
482 516 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
for ch := range b.joined {
483 517 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
channels = append(channels, ch)
484 518 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
485 519 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
b.mu.RUnlock()
520 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Clear stale data before refresh.
521 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.mu.Lock()
522 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for _, ch := range channels {
523 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.namesUsers[ch] = make(map[string]string)
524 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
525 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.mu.Unlock()
486 526 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
for _, ch := range channels {
487 527 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
b.RefreshNames(ch)
488 528 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
489 529 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
490 530 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
491 531 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
492 532 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
493 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- // Users returns the current nick list for a channel — IRC connections plus
494 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- // web UI users who have posted recently within the configured TTL.
533 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Users returns the current nick list for a channel — from our NAMES cache
534 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // plus web UI users who have posted recently within the configured TTL.
495 535 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func (b *Bot) Users(channel string) []string {
496 536 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
seen := make(map[string]bool)
497 537 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
var nicks []string
498 538 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
499 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- // IRC-connected nicks from girc's state — exclude the bridge bot itself.
500 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if b.client != nil {
501 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if ch := b.client.LookupChannel(channel); ch != nil {
502 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- for _, u := range ch.Users(b.client) {
503 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if u.Nick == b.nick {
504 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- continue // skip the bridge bot
505 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
506 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if !seen[u.Nick] {
507 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- seen[u.Nick] = true
508 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- nicks = append(nicks, u.Nick)
509 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
510 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
539 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // IRC-connected nicks from our NAMES cache.
540 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.mu.RLock()
541 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for nick := range b.namesUsers[channel] {
542 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if !seen[nick] {
543 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ seen[nick] = true
544 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ nicks = append(nicks, nick)
511 545 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
512 546 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
547 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.mu.RUnlock()
513 548 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
514 549 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Web UI senders active within the configured TTL. Also prune expired nicks
515 550 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// so the bridge doesn't retain dead web-user entries forever.
516 551 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
now := time.Now()
517 552 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
b.mu.Lock()
@@ -540,44 +575,26 @@
540 575 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// UsersWithModes returns the current user list with mode info for a channel.
541 576 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func (b *Bot) UsersWithModes(channel string) []UserInfo {
542 577 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
seen := make(map[string]bool)
543 578 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
var users []UserInfo
544 579 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
545 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if b.client != nil {
546 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if ch := b.client.LookupChannel(channel); ch != nil {
547 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- for _, u := range ch.Users(b.client) {
548 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if u.Nick == b.nick {
549 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- continue
550 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
551 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if seen[u.Nick] {
552 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- continue
553 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
554 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- seen[u.Nick] = true
555 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- var modes []string
556 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if u.Perms != nil {
557 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if perms, ok := u.Perms.Lookup(channel); ok {
558 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if perms.Owner {
559 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- modes = append(modes, "q")
560 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
561 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if perms.Admin {
562 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- modes = append(modes, "a")
563 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
564 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if perms.Op {
565 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- modes = append(modes, "o")
566 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
567 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if perms.HalfOp {
568 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- modes = append(modes, "h")
569 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
570 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if perms.Voice {
571 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- modes = append(modes, "v")
572 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
573 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
574 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
575 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- users = append(users, UserInfo{Nick: u.Nick, Modes: modes})
576 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
577 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
578 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
580 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Use our NAMES cache for reliable user+mode data.
581 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.mu.RLock()
582 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for nick, prefix := range b.namesUsers[channel] {
583 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if seen[nick] {
584 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ continue
585 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
586 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ seen[nick] = true
587 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ var modes []string
588 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if prefix == "@" {
589 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ modes = append(modes, "o")
590 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ } else if prefix == "+" {
591 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ modes = append(modes, "v")
592 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
593 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ users = append(users, UserInfo{Nick: nick, Modes: modes})
594 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
595 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ b.mu.RUnlock()
579 596 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
580 597 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
now := time.Now()
581 598 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
b.mu.Lock()
582 599 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
cutoff := now.Add(-b.webUserTTL)
583 600 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
for nick, last := range b.webUsers[channel] {
584 601 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!