@@ -28,31 +28,40 @@
28 28 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Ops is a list of nicks to grant +o (channel operator) status.
29 29 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
Ops []string
30 30 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
31 31 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Voice is a list of nicks to grant +v status.
32 32 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
Voice []string
33 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
34 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Autojoin is a list of bot nicks to invite after provisioning.
35 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ Autojoin []string
33 36 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
34 37 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
35 38 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Manager provisions and maintains IRC channel topology.
36 39 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
type Manager struct {
37 40 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
ircAddr string
38 41 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
nick string
39 42 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
password string
40 43 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
log *slog.Logger
44 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ policy *Policy
41 45 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
client *girc.Client
42 46 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
43 47 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
44 48 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// NewManager creates a topology Manager. nick and password are the Ergo
45 49 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// credentials of the scuttlebot oper account used to manage channels.
46 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- func NewManager(ircAddr, nick, password string, log *slog.Logger) *Manager {
50 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // policy may be nil if the caller only uses the manager for ad-hoc provisioning.
51 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func NewManager(ircAddr, nick, password string, policy *Policy, log *slog.Logger) *Manager {
47 52 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return &Manager{
48 53 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
ircAddr: ircAddr,
49 54 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
nick: nick,
50 55 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
password: password,
56 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ policy: policy,
51 57 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
log: log,
52 58 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
53 59 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
60 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
61 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Policy returns the policy attached to this manager, or nil.
62 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func (m *Manager) Policy() *Policy { return m.policy }
54 63 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
55 64 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Connect establishes the IRC connection used for channel management.
56 65 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Call before Provision.
57 66 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func (m *Manager) Connect(ctx context.Context) error {
58 67 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
host, port, err := splitHostPort(m.ircAddr)
@@ -142,15 +151,50 @@
142 151 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
143 152 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// DestroyEphemeral drops an ephemeral task channel.
144 153 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func (m *Manager) DestroyEphemeral(channel string) {
145 154 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
m.chanserv("DROP %s", channel)
146 155 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
156 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
157 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // ProvisionChannel provisions a single channel and invites its autojoin nicks.
158 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // It applies the manager's Policy if set; the caller may override autojoin via
159 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // the ChannelConfig directly.
160 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func (m *Manager) ProvisionChannel(ch ChannelConfig) error {
161 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if err := ValidateName(ch.Name); err != nil {
162 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return err
163 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
164 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if err := m.provision(ch); err != nil {
165 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return err
166 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
167 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(ch.Autojoin) > 0 {
168 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ m.Invite(ch.Name, ch.Autojoin)
169 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
170 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return nil
171 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
172 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
173 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Invite sends IRC INVITE to each nick in nicks for the given channel.
174 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Invite is best-effort: nicks that are not connected are silently skipped.
175 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func (m *Manager) Invite(channel string, nicks []string) {
176 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if m.client == nil {
177 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return
178 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
179 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for _, nick := range nicks {
180 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ m.client.Cmd.Invite(nick, channel)
181 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
182 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
147 183 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
148 184 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func (m *Manager) provision(ch ChannelConfig) error {
149 185 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Register with ChanServ (idempotent — fails silently if already registered).
150 186 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
m.chanserv("REGISTER %s", ch.Name)
151 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- time.Sleep(100 * time.Millisecond) // let ChanServ process
187 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Give ChanServ time to process the registration before issuing follow-up
188 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // commands. Retry the sleep up to 3 times so transient load doesn't cause
189 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // TOPIC/ACCESS commands to fire before registration completes.
190 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for range 3 {
191 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ time.Sleep(200 * time.Millisecond)
192 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if m.client.IsConnected() {
193 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ break
194 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
195 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
152 196 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
153 197 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if ch.Topic != "" {
154 198 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
m.chanserv("TOPIC %s %s", ch.Name, ch.Topic)
155 199 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
156 200 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
@@ -158,10 +202,14 @@
158 202 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
m.chanserv("ACCESS %s ADD %s OP", ch.Name, nick)
159 203 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
160 204 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
for _, nick := range ch.Voice {
161 205 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
m.chanserv("ACCESS %s ADD %s VOICE", ch.Name, nick)
162 206 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
207 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
208 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(ch.Autojoin) > 0 {
209 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ m.Invite(ch.Name, ch.Autojoin)
210 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
163 211 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
164 212 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
m.log.Info("provisioned channel", "channel", ch.Name)
165 213 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return nil
166 214 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
167 215 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
168 216 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!