ScuttleBot

feat(#38,#39): autojoin enforcement and bot INVITE handling - topology.Manager.ProvisionChannel calls Invite() after provisioning (#38) - All system bots (bridge, systembot, scribe, herald, oracle, sentinel, steward, auditbot, warden, snitch) now handle girc.INVITE by joining the channel (#39) - bridge routes INVITE through JoinChannel() to initialise its buffer map first - snitch needed a strings import to support the HasPrefix check

lmata 2026-04-02 13:24 trunk
Commit bd16e1f7ac804eea6aa5f8c14fdf3944fb353ae2f3ac39e65c4abdaa3273b66c
--- internal/bots/auditbot/auditbot.go
+++ internal/bots/auditbot/auditbot.go
@@ -115,10 +115,16 @@
115115
for _, ch := range b.channels {
116116
cl.Cmd.Join(ch)
117117
}
118118
b.log.Info("auditbot connected", "channels", b.channels, "audit_types", b.auditTypesList())
119119
})
120
+
121
+ c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
122
+ if ch := e.Last(); strings.HasPrefix(ch, "#") {
123
+ cl.Cmd.Join(ch)
124
+ }
125
+ })
120126
121127
c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) {
122128
if len(e.Params) < 1 {
123129
return
124130
}
125131
--- internal/bots/auditbot/auditbot.go
+++ internal/bots/auditbot/auditbot.go
@@ -115,10 +115,16 @@
115 for _, ch := range b.channels {
116 cl.Cmd.Join(ch)
117 }
118 b.log.Info("auditbot connected", "channels", b.channels, "audit_types", b.auditTypesList())
119 })
 
 
 
 
 
 
120
121 c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) {
122 if len(e.Params) < 1 {
123 return
124 }
125
--- internal/bots/auditbot/auditbot.go
+++ internal/bots/auditbot/auditbot.go
@@ -115,10 +115,16 @@
115 for _, ch := range b.channels {
116 cl.Cmd.Join(ch)
117 }
118 b.log.Info("auditbot connected", "channels", b.channels, "audit_types", b.auditTypesList())
119 })
120
121 c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
122 if ch := e.Last(); strings.HasPrefix(ch, "#") {
123 cl.Cmd.Join(ch)
124 }
125 })
126
127 c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) {
128 if len(e.Params) < 1 {
129 return
130 }
131
--- internal/bots/bridge/bridge.go
+++ internal/bots/bridge/bridge.go
@@ -162,10 +162,16 @@
162162
}
163163
for _, ch := range b.initChannels {
164164
cl.Cmd.Join(ch)
165165
}
166166
})
167
+
168
+ c.Handlers.AddBg(girc.INVITE, func(_ *girc.Client, e girc.Event) {
169
+ if ch := e.Last(); strings.HasPrefix(ch, "#") {
170
+ b.JoinChannel(ch)
171
+ }
172
+ })
167173
168174
c.Handlers.AddBg(girc.JOIN, func(_ *girc.Client, e girc.Event) {
169175
if len(e.Params) < 1 || e.Source == nil || e.Source.Name != b.nick {
170176
return
171177
}
172178
--- internal/bots/bridge/bridge.go
+++ internal/bots/bridge/bridge.go
@@ -162,10 +162,16 @@
162 }
163 for _, ch := range b.initChannels {
164 cl.Cmd.Join(ch)
165 }
166 })
 
 
 
 
 
 
167
168 c.Handlers.AddBg(girc.JOIN, func(_ *girc.Client, e girc.Event) {
169 if len(e.Params) < 1 || e.Source == nil || e.Source.Name != b.nick {
170 return
171 }
172
--- internal/bots/bridge/bridge.go
+++ internal/bots/bridge/bridge.go
@@ -162,10 +162,16 @@
162 }
163 for _, ch := range b.initChannels {
164 cl.Cmd.Join(ch)
165 }
166 })
167
168 c.Handlers.AddBg(girc.INVITE, func(_ *girc.Client, e girc.Event) {
169 if ch := e.Last(); strings.HasPrefix(ch, "#") {
170 b.JoinChannel(ch)
171 }
172 })
173
174 c.Handlers.AddBg(girc.JOIN, func(_ *girc.Client, e girc.Event) {
175 if len(e.Params) < 1 || e.Source == nil || e.Source.Name != b.nick {
176 return
177 }
178
--- internal/bots/herald/herald.go
+++ internal/bots/herald/herald.go
@@ -149,10 +149,16 @@
149149
c.Handlers.AddBg(girc.CONNECTED, func(_ *girc.Client, _ girc.Event) {
150150
if b.log != nil {
151151
b.log.Info("herald connected")
152152
}
153153
})
154
+
155
+ c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
156
+ if ch := e.Last(); strings.HasPrefix(ch, "#") {
157
+ cl.Cmd.Join(ch)
158
+ }
159
+ })
154160
155161
b.client = c
156162
157163
errCh := make(chan error, 1)
158164
go func() {
159165
--- internal/bots/herald/herald.go
+++ internal/bots/herald/herald.go
@@ -149,10 +149,16 @@
149 c.Handlers.AddBg(girc.CONNECTED, func(_ *girc.Client, _ girc.Event) {
150 if b.log != nil {
151 b.log.Info("herald connected")
152 }
153 })
 
 
 
 
 
 
154
155 b.client = c
156
157 errCh := make(chan error, 1)
158 go func() {
159
--- internal/bots/herald/herald.go
+++ internal/bots/herald/herald.go
@@ -149,10 +149,16 @@
149 c.Handlers.AddBg(girc.CONNECTED, func(_ *girc.Client, _ girc.Event) {
150 if b.log != nil {
151 b.log.Info("herald connected")
152 }
153 })
154
155 c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
156 if ch := e.Last(); strings.HasPrefix(ch, "#") {
157 cl.Cmd.Join(ch)
158 }
159 })
160
161 b.client = c
162
163 errCh := make(chan error, 1)
164 go func() {
165
--- internal/bots/oracle/oracle.go
+++ internal/bots/oracle/oracle.go
@@ -162,10 +162,16 @@
162162
c.Handlers.AddBg(girc.CONNECTED, func(_ *girc.Client, _ girc.Event) {
163163
if b.log != nil {
164164
b.log.Info("oracle connected")
165165
}
166166
})
167
+
168
+ c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
169
+ if ch := e.Last(); strings.HasPrefix(ch, "#") {
170
+ cl.Cmd.Join(ch)
171
+ }
172
+ })
167173
168174
// Only handle DMs — oracle ignores channel messages.
169175
c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) {
170176
if len(e.Params) < 1 || e.Source == nil {
171177
return
172178
--- internal/bots/oracle/oracle.go
+++ internal/bots/oracle/oracle.go
@@ -162,10 +162,16 @@
162 c.Handlers.AddBg(girc.CONNECTED, func(_ *girc.Client, _ girc.Event) {
163 if b.log != nil {
164 b.log.Info("oracle connected")
165 }
166 })
 
 
 
 
 
 
167
168 // Only handle DMs — oracle ignores channel messages.
169 c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) {
170 if len(e.Params) < 1 || e.Source == nil {
171 return
172
--- internal/bots/oracle/oracle.go
+++ internal/bots/oracle/oracle.go
@@ -162,10 +162,16 @@
162 c.Handlers.AddBg(girc.CONNECTED, func(_ *girc.Client, _ girc.Event) {
163 if b.log != nil {
164 b.log.Info("oracle connected")
165 }
166 })
167
168 c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
169 if ch := e.Last(); strings.HasPrefix(ch, "#") {
170 cl.Cmd.Join(ch)
171 }
172 })
173
174 // Only handle DMs — oracle ignores channel messages.
175 c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) {
176 if len(e.Params) < 1 || e.Source == nil {
177 return
178
--- internal/bots/scribe/scribe.go
+++ internal/bots/scribe/scribe.go
@@ -67,10 +67,16 @@
6767
for _, ch := range b.channels {
6868
client.Cmd.Join(ch)
6969
}
7070
b.log.Info("scribe connected and joined channels", "channels", b.channels)
7171
})
72
+
73
+ c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
74
+ if ch := e.Last(); strings.HasPrefix(ch, "#") {
75
+ cl.Cmd.Join(ch)
76
+ }
77
+ })
7278
7379
// Log PRIVMSG — the agent message stream.
7480
c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) {
7581
if len(e.Params) < 1 {
7682
return
7783
--- internal/bots/scribe/scribe.go
+++ internal/bots/scribe/scribe.go
@@ -67,10 +67,16 @@
67 for _, ch := range b.channels {
68 client.Cmd.Join(ch)
69 }
70 b.log.Info("scribe connected and joined channels", "channels", b.channels)
71 })
 
 
 
 
 
 
72
73 // Log PRIVMSG — the agent message stream.
74 c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) {
75 if len(e.Params) < 1 {
76 return
77
--- internal/bots/scribe/scribe.go
+++ internal/bots/scribe/scribe.go
@@ -67,10 +67,16 @@
67 for _, ch := range b.channels {
68 client.Cmd.Join(ch)
69 }
70 b.log.Info("scribe connected and joined channels", "channels", b.channels)
71 })
72
73 c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
74 if ch := e.Last(); strings.HasPrefix(ch, "#") {
75 cl.Cmd.Join(ch)
76 }
77 })
78
79 // Log PRIVMSG — the agent message stream.
80 c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) {
81 if len(e.Params) < 1 {
82 return
83
--- internal/bots/sentinel/sentinel.go
+++ internal/bots/sentinel/sentinel.go
@@ -146,10 +146,16 @@
146146
if b.log != nil {
147147
b.log.Info("sentinel connected")
148148
}
149149
cl.Cmd.Join(b.cfg.ModChannel)
150150
})
151
+
152
+ c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
153
+ if ch := e.Last(); strings.HasPrefix(ch, "#") {
154
+ cl.Cmd.Join(ch)
155
+ }
156
+ })
151157
152158
c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) {
153159
if len(e.Params) < 1 || e.Source == nil {
154160
return
155161
}
156162
--- internal/bots/sentinel/sentinel.go
+++ internal/bots/sentinel/sentinel.go
@@ -146,10 +146,16 @@
146 if b.log != nil {
147 b.log.Info("sentinel connected")
148 }
149 cl.Cmd.Join(b.cfg.ModChannel)
150 })
 
 
 
 
 
 
151
152 c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) {
153 if len(e.Params) < 1 || e.Source == nil {
154 return
155 }
156
--- internal/bots/sentinel/sentinel.go
+++ internal/bots/sentinel/sentinel.go
@@ -146,10 +146,16 @@
146 if b.log != nil {
147 b.log.Info("sentinel connected")
148 }
149 cl.Cmd.Join(b.cfg.ModChannel)
150 })
151
152 c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
153 if ch := e.Last(); strings.HasPrefix(ch, "#") {
154 cl.Cmd.Join(ch)
155 }
156 })
157
158 c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) {
159 if len(e.Params) < 1 || e.Source == nil {
160 return
161 }
162
--- internal/bots/snitch/snitch.go
+++ internal/bots/snitch/snitch.go
@@ -12,10 +12,11 @@
1212
"context"
1313
"fmt"
1414
"log/slog"
1515
"net"
1616
"strconv"
17
+ "strings"
1718
"sync"
1819
"time"
1920
2021
"github.com/lrstanley/girc"
2122
)
@@ -136,10 +137,16 @@
136137
}
137138
if b.cfg.AlertChannel != "" {
138139
cl.Cmd.Join(b.cfg.AlertChannel)
139140
}
140141
})
142
+
143
+ c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
144
+ if ch := e.Last(); strings.HasPrefix(ch, "#") {
145
+ cl.Cmd.Join(ch)
146
+ }
147
+ })
141148
142149
c.Handlers.AddBg(girc.JOIN, func(_ *girc.Client, e girc.Event) {
143150
if len(e.Params) < 1 || e.Source == nil || e.Source.Name == b.cfg.Nick {
144151
return
145152
}
146153
--- internal/bots/snitch/snitch.go
+++ internal/bots/snitch/snitch.go
@@ -12,10 +12,11 @@
12 "context"
13 "fmt"
14 "log/slog"
15 "net"
16 "strconv"
 
17 "sync"
18 "time"
19
20 "github.com/lrstanley/girc"
21 )
@@ -136,10 +137,16 @@
136 }
137 if b.cfg.AlertChannel != "" {
138 cl.Cmd.Join(b.cfg.AlertChannel)
139 }
140 })
 
 
 
 
 
 
141
142 c.Handlers.AddBg(girc.JOIN, func(_ *girc.Client, e girc.Event) {
143 if len(e.Params) < 1 || e.Source == nil || e.Source.Name == b.cfg.Nick {
144 return
145 }
146
--- internal/bots/snitch/snitch.go
+++ internal/bots/snitch/snitch.go
@@ -12,10 +12,11 @@
12 "context"
13 "fmt"
14 "log/slog"
15 "net"
16 "strconv"
17 "strings"
18 "sync"
19 "time"
20
21 "github.com/lrstanley/girc"
22 )
@@ -136,10 +137,16 @@
137 }
138 if b.cfg.AlertChannel != "" {
139 cl.Cmd.Join(b.cfg.AlertChannel)
140 }
141 })
142
143 c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
144 if ch := e.Last(); strings.HasPrefix(ch, "#") {
145 cl.Cmd.Join(ch)
146 }
147 })
148
149 c.Handlers.AddBg(girc.JOIN, func(_ *girc.Client, e girc.Event) {
150 if len(e.Params) < 1 || e.Source == nil || e.Source.Name == b.cfg.Nick {
151 return
152 }
153
--- internal/bots/steward/steward.go
+++ internal/bots/steward/steward.go
@@ -130,10 +130,16 @@
130130
if b.log != nil {
131131
b.log.Info("steward connected")
132132
}
133133
cl.Cmd.Join(b.cfg.ModChannel)
134134
})
135
+
136
+ c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
137
+ if ch := e.Last(); strings.HasPrefix(ch, "#") {
138
+ cl.Cmd.Join(ch)
139
+ }
140
+ })
135141
136142
c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) {
137143
if len(e.Params) < 1 || e.Source == nil {
138144
return
139145
}
140146
--- internal/bots/steward/steward.go
+++ internal/bots/steward/steward.go
@@ -130,10 +130,16 @@
130 if b.log != nil {
131 b.log.Info("steward connected")
132 }
133 cl.Cmd.Join(b.cfg.ModChannel)
134 })
 
 
 
 
 
 
135
136 c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) {
137 if len(e.Params) < 1 || e.Source == nil {
138 return
139 }
140
--- internal/bots/steward/steward.go
+++ internal/bots/steward/steward.go
@@ -130,10 +130,16 @@
130 if b.log != nil {
131 b.log.Info("steward connected")
132 }
133 cl.Cmd.Join(b.cfg.ModChannel)
134 })
135
136 c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
137 if ch := e.Last(); strings.HasPrefix(ch, "#") {
138 cl.Cmd.Join(ch)
139 }
140 })
141
142 c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) {
143 if len(e.Params) < 1 || e.Source == nil {
144 return
145 }
146
--- internal/bots/systembot/systembot.go
+++ internal/bots/systembot/systembot.go
@@ -92,10 +92,16 @@
9292
for _, ch := range b.channels {
9393
cl.Cmd.Join(ch)
9494
}
9595
b.log.Info("systembot connected", "channels", b.channels)
9696
})
97
+
98
+ c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
99
+ if ch := e.Last(); strings.HasPrefix(ch, "#") {
100
+ cl.Cmd.Join(ch)
101
+ }
102
+ })
97103
98104
// NOTICE — server announcements, NickServ/ChanServ responses.
99105
c.Handlers.AddBg(girc.NOTICE, func(_ *girc.Client, e girc.Event) {
100106
channel := ""
101107
if len(e.Params) > 0 && strings.HasPrefix(e.Params[0], "#") {
102108
--- internal/bots/systembot/systembot.go
+++ internal/bots/systembot/systembot.go
@@ -92,10 +92,16 @@
92 for _, ch := range b.channels {
93 cl.Cmd.Join(ch)
94 }
95 b.log.Info("systembot connected", "channels", b.channels)
96 })
 
 
 
 
 
 
97
98 // NOTICE — server announcements, NickServ/ChanServ responses.
99 c.Handlers.AddBg(girc.NOTICE, func(_ *girc.Client, e girc.Event) {
100 channel := ""
101 if len(e.Params) > 0 && strings.HasPrefix(e.Params[0], "#") {
102
--- internal/bots/systembot/systembot.go
+++ internal/bots/systembot/systembot.go
@@ -92,10 +92,16 @@
92 for _, ch := range b.channels {
93 cl.Cmd.Join(ch)
94 }
95 b.log.Info("systembot connected", "channels", b.channels)
96 })
97
98 c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
99 if ch := e.Last(); strings.HasPrefix(ch, "#") {
100 cl.Cmd.Join(ch)
101 }
102 })
103
104 // NOTICE — server announcements, NickServ/ChanServ responses.
105 c.Handlers.AddBg(girc.NOTICE, func(_ *girc.Client, e girc.Event) {
106 channel := ""
107 if len(e.Params) > 0 && strings.HasPrefix(e.Params[0], "#") {
108
--- internal/bots/warden/warden.go
+++ internal/bots/warden/warden.go
@@ -201,10 +201,16 @@
201201
}
202202
if b.log != nil {
203203
b.log.Info("warden connected")
204204
}
205205
})
206
+
207
+ c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
208
+ if ch := e.Last(); strings.HasPrefix(ch, "#") {
209
+ cl.Cmd.Join(ch)
210
+ }
211
+ })
206212
207213
c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) {
208214
if len(e.Params) < 1 || e.Source == nil {
209215
return
210216
}
211217
--- internal/bots/warden/warden.go
+++ internal/bots/warden/warden.go
@@ -201,10 +201,16 @@
201 }
202 if b.log != nil {
203 b.log.Info("warden connected")
204 }
205 })
 
 
 
 
 
 
206
207 c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) {
208 if len(e.Params) < 1 || e.Source == nil {
209 return
210 }
211
--- internal/bots/warden/warden.go
+++ internal/bots/warden/warden.go
@@ -201,10 +201,16 @@
201 }
202 if b.log != nil {
203 b.log.Info("warden connected")
204 }
205 })
206
207 c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) {
208 if ch := e.Last(); strings.HasPrefix(ch, "#") {
209 cl.Cmd.Join(ch)
210 }
211 })
212
213 c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) {
214 if len(e.Params) < 1 || e.Source == nil {
215 return
216 }
217

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button