ScuttleBot
feat: wire command router into all 10 system bots (#86) Every bot now responds to HELP, !commands, and DMs via the shared cmdparse framework. 22 commands registered across 10 bots: - auditbot: QUERY - scribe: SEARCH, STATS - herald: STATUS, TEST - oracle: SUMMARIZE - warden: WARN, MUTE, KICK, STATUS - scroll: REPLAY, SEARCH - systembot: STATUS, WHO - snitch: STATUS, ACKNOWLEDGE - sentinel: REPORT, STATUS, DISMISS - steward: ACT, OVERRIDE, STATUS All handlers return 'not implemented yet' — individual bot issues (#73-#82) will implement the real logic.
Commit
520ad8ed949416b1f4ffe95180c7a876fcf1a13bb03c36e2acb7f5c31b272321
Parent
9460ec238954073…
10 files changed
+16
-2
+27
+15
-1
+22
-2
+21
-2
+29
-2
+21
+27
+27
+33
-1
~
internal/bots/auditbot/auditbot.go
~
internal/bots/herald/herald.go
~
internal/bots/oracle/oracle.go
~
internal/bots/scribe/scribe.go
~
internal/bots/scroll/scroll.go
~
internal/bots/sentinel/sentinel.go
~
internal/bots/snitch/snitch.go
~
internal/bots/steward/steward.go
~
internal/bots/systembot/systembot.go
~
internal/bots/warden/warden.go
+16
-2
| --- internal/bots/auditbot/auditbot.go | ||
| +++ internal/bots/auditbot/auditbot.go | ||
| @@ -20,10 +20,11 @@ | ||
| 20 | 20 | "strings" |
| 21 | 21 | "time" |
| 22 | 22 | |
| 23 | 23 | "github.com/lrstanley/girc" |
| 24 | 24 | |
| 25 | + "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" | |
| 25 | 26 | "github.com/conflicthq/scuttlebot/pkg/protocol" |
| 26 | 27 | ) |
| 27 | 28 | |
| 28 | 29 | const botNick = "auditbot" |
| 29 | 30 | |
| @@ -132,17 +133,30 @@ | ||
| 132 | 133 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 133 | 134 | cl.Cmd.Join(ch) |
| 134 | 135 | } |
| 135 | 136 | }) |
| 136 | 137 | |
| 137 | - c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) { | |
| 138 | + router := cmdparse.NewRouter(botNick) | |
| 139 | + router.Register(cmdparse.Command{ | |
| 140 | + Name: "query", | |
| 141 | + Usage: "QUERY <nick|#channel>", | |
| 142 | + Description: "show recent audit events for a nick or channel", | |
| 143 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 144 | + }) | |
| 145 | + | |
| 146 | + c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { | |
| 138 | 147 | if len(e.Params) < 1 { |
| 139 | 148 | return |
| 149 | + } | |
| 150 | + // Dispatch commands (DMs and channel messages). | |
| 151 | + if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { | |
| 152 | + cl.Cmd.Message(reply.Target, reply.Text) | |
| 153 | + return | |
| 140 | 154 | } |
| 141 | 155 | channel := e.Params[0] |
| 142 | 156 | if !strings.HasPrefix(channel, "#") { |
| 143 | - return // ignore DMs | |
| 157 | + return // non-command DMs ignored | |
| 144 | 158 | } |
| 145 | 159 | text := e.Last() |
| 146 | 160 | env, err := protocol.Unmarshal([]byte(text)) |
| 147 | 161 | if err != nil { |
| 148 | 162 | return // non-envelope PRIVMSG ignored |
| 149 | 163 |
| --- internal/bots/auditbot/auditbot.go | |
| +++ internal/bots/auditbot/auditbot.go | |
| @@ -20,10 +20,11 @@ | |
| 20 | "strings" |
| 21 | "time" |
| 22 | |
| 23 | "github.com/lrstanley/girc" |
| 24 | |
| 25 | "github.com/conflicthq/scuttlebot/pkg/protocol" |
| 26 | ) |
| 27 | |
| 28 | const botNick = "auditbot" |
| 29 | |
| @@ -132,17 +133,30 @@ | |
| 132 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 133 | cl.Cmd.Join(ch) |
| 134 | } |
| 135 | }) |
| 136 | |
| 137 | c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) { |
| 138 | if len(e.Params) < 1 { |
| 139 | return |
| 140 | } |
| 141 | channel := e.Params[0] |
| 142 | if !strings.HasPrefix(channel, "#") { |
| 143 | return // ignore DMs |
| 144 | } |
| 145 | text := e.Last() |
| 146 | env, err := protocol.Unmarshal([]byte(text)) |
| 147 | if err != nil { |
| 148 | return // non-envelope PRIVMSG ignored |
| 149 |
| --- internal/bots/auditbot/auditbot.go | |
| +++ internal/bots/auditbot/auditbot.go | |
| @@ -20,10 +20,11 @@ | |
| 20 | "strings" |
| 21 | "time" |
| 22 | |
| 23 | "github.com/lrstanley/girc" |
| 24 | |
| 25 | "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" |
| 26 | "github.com/conflicthq/scuttlebot/pkg/protocol" |
| 27 | ) |
| 28 | |
| 29 | const botNick = "auditbot" |
| 30 | |
| @@ -132,17 +133,30 @@ | |
| 133 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 134 | cl.Cmd.Join(ch) |
| 135 | } |
| 136 | }) |
| 137 | |
| 138 | router := cmdparse.NewRouter(botNick) |
| 139 | router.Register(cmdparse.Command{ |
| 140 | Name: "query", |
| 141 | Usage: "QUERY <nick|#channel>", |
| 142 | Description: "show recent audit events for a nick or channel", |
| 143 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 144 | }) |
| 145 | |
| 146 | c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { |
| 147 | if len(e.Params) < 1 { |
| 148 | return |
| 149 | } |
| 150 | // Dispatch commands (DMs and channel messages). |
| 151 | if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { |
| 152 | cl.Cmd.Message(reply.Target, reply.Text) |
| 153 | return |
| 154 | } |
| 155 | channel := e.Params[0] |
| 156 | if !strings.HasPrefix(channel, "#") { |
| 157 | return // non-command DMs ignored |
| 158 | } |
| 159 | text := e.Last() |
| 160 | env, err := protocol.Unmarshal([]byte(text)) |
| 161 | if err != nil { |
| 162 | return // non-envelope PRIVMSG ignored |
| 163 |
| --- internal/bots/herald/herald.go | ||
| +++ internal/bots/herald/herald.go | ||
| @@ -17,10 +17,12 @@ | ||
| 17 | 17 | "strings" |
| 18 | 18 | "sync" |
| 19 | 19 | "time" |
| 20 | 20 | |
| 21 | 21 | "github.com/lrstanley/girc" |
| 22 | + | |
| 23 | + "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" | |
| 22 | 24 | ) |
| 23 | 25 | |
| 24 | 26 | const botNick = "herald" |
| 25 | 27 | |
| 26 | 28 | // Event is a notification pushed to herald for delivery. |
| @@ -164,10 +166,35 @@ | ||
| 164 | 166 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 165 | 167 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 166 | 168 | cl.Cmd.Join(ch) |
| 167 | 169 | } |
| 168 | 170 | }) |
| 171 | + | |
| 172 | + router := cmdparse.NewRouter(botNick) | |
| 173 | + router.Register(cmdparse.Command{ | |
| 174 | + Name: "status", | |
| 175 | + Usage: "STATUS", | |
| 176 | + Description: "show webhook endpoint status and recent events", | |
| 177 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 178 | + }) | |
| 179 | + router.Register(cmdparse.Command{ | |
| 180 | + Name: "test", | |
| 181 | + Usage: "TEST #channel", | |
| 182 | + Description: "send a test event to a channel", | |
| 183 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 184 | + }) | |
| 185 | + | |
| 186 | + c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { | |
| 187 | + if len(e.Params) < 1 || e.Source == nil { | |
| 188 | + return | |
| 189 | + } | |
| 190 | + // Dispatch commands (DMs and channel messages). | |
| 191 | + if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { | |
| 192 | + cl.Cmd.Message(reply.Target, reply.Text) | |
| 193 | + return | |
| 194 | + } | |
| 195 | + }) | |
| 169 | 196 | |
| 170 | 197 | b.client = c |
| 171 | 198 | |
| 172 | 199 | errCh := make(chan error, 1) |
| 173 | 200 | go func() { |
| 174 | 201 |
| --- internal/bots/herald/herald.go | |
| +++ internal/bots/herald/herald.go | |
| @@ -17,10 +17,12 @@ | |
| 17 | "strings" |
| 18 | "sync" |
| 19 | "time" |
| 20 | |
| 21 | "github.com/lrstanley/girc" |
| 22 | ) |
| 23 | |
| 24 | const botNick = "herald" |
| 25 | |
| 26 | // Event is a notification pushed to herald for delivery. |
| @@ -164,10 +166,35 @@ | |
| 164 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 165 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 166 | cl.Cmd.Join(ch) |
| 167 | } |
| 168 | }) |
| 169 | |
| 170 | b.client = c |
| 171 | |
| 172 | errCh := make(chan error, 1) |
| 173 | go func() { |
| 174 |
| --- internal/bots/herald/herald.go | |
| +++ internal/bots/herald/herald.go | |
| @@ -17,10 +17,12 @@ | |
| 17 | "strings" |
| 18 | "sync" |
| 19 | "time" |
| 20 | |
| 21 | "github.com/lrstanley/girc" |
| 22 | |
| 23 | "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" |
| 24 | ) |
| 25 | |
| 26 | const botNick = "herald" |
| 27 | |
| 28 | // Event is a notification pushed to herald for delivery. |
| @@ -164,10 +166,35 @@ | |
| 166 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 167 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 168 | cl.Cmd.Join(ch) |
| 169 | } |
| 170 | }) |
| 171 | |
| 172 | router := cmdparse.NewRouter(botNick) |
| 173 | router.Register(cmdparse.Command{ |
| 174 | Name: "status", |
| 175 | Usage: "STATUS", |
| 176 | Description: "show webhook endpoint status and recent events", |
| 177 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 178 | }) |
| 179 | router.Register(cmdparse.Command{ |
| 180 | Name: "test", |
| 181 | Usage: "TEST #channel", |
| 182 | Description: "send a test event to a channel", |
| 183 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 184 | }) |
| 185 | |
| 186 | c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { |
| 187 | if len(e.Params) < 1 || e.Source == nil { |
| 188 | return |
| 189 | } |
| 190 | // Dispatch commands (DMs and channel messages). |
| 191 | if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { |
| 192 | cl.Cmd.Message(reply.Target, reply.Text) |
| 193 | return |
| 194 | } |
| 195 | }) |
| 196 | |
| 197 | b.client = c |
| 198 | |
| 199 | errCh := make(chan error, 1) |
| 200 | go func() { |
| 201 |
+15
-1
| --- internal/bots/oracle/oracle.go | ||
| +++ internal/bots/oracle/oracle.go | ||
| @@ -20,10 +20,12 @@ | ||
| 20 | 20 | "strings" |
| 21 | 21 | "sync" |
| 22 | 22 | "time" |
| 23 | 23 | |
| 24 | 24 | "github.com/lrstanley/girc" |
| 25 | + | |
| 26 | + "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" | |
| 25 | 27 | ) |
| 26 | 28 | |
| 27 | 29 | const ( |
| 28 | 30 | botNick = "oracle" |
| 29 | 31 | defaultLimit = 50 |
| @@ -176,14 +178,26 @@ | ||
| 176 | 178 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 177 | 179 | cl.Cmd.Join(ch) |
| 178 | 180 | } |
| 179 | 181 | }) |
| 180 | 182 | |
| 181 | - // Only handle DMs — oracle ignores channel messages. | |
| 183 | + router := cmdparse.NewRouter(botNick) | |
| 184 | + router.Register(cmdparse.Command{ | |
| 185 | + Name: "summarize", | |
| 186 | + Usage: "SUMMARIZE [#channel] [duration]", | |
| 187 | + Description: "summarize recent channel activity", | |
| 188 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 189 | + }) | |
| 190 | + | |
| 182 | 191 | c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { |
| 183 | 192 | if len(e.Params) < 1 || e.Source == nil { |
| 184 | 193 | return |
| 194 | + } | |
| 195 | + // Dispatch commands (DMs and channel messages). | |
| 196 | + if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { | |
| 197 | + cl.Cmd.Message(reply.Target, reply.Text) | |
| 198 | + return | |
| 185 | 199 | } |
| 186 | 200 | target := e.Params[0] |
| 187 | 201 | if strings.HasPrefix(target, "#") { |
| 188 | 202 | return // channel message — ignore |
| 189 | 203 | } |
| 190 | 204 |
| --- internal/bots/oracle/oracle.go | |
| +++ internal/bots/oracle/oracle.go | |
| @@ -20,10 +20,12 @@ | |
| 20 | "strings" |
| 21 | "sync" |
| 22 | "time" |
| 23 | |
| 24 | "github.com/lrstanley/girc" |
| 25 | ) |
| 26 | |
| 27 | const ( |
| 28 | botNick = "oracle" |
| 29 | defaultLimit = 50 |
| @@ -176,14 +178,26 @@ | |
| 176 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 177 | cl.Cmd.Join(ch) |
| 178 | } |
| 179 | }) |
| 180 | |
| 181 | // Only handle DMs — oracle ignores channel messages. |
| 182 | c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { |
| 183 | if len(e.Params) < 1 || e.Source == nil { |
| 184 | return |
| 185 | } |
| 186 | target := e.Params[0] |
| 187 | if strings.HasPrefix(target, "#") { |
| 188 | return // channel message — ignore |
| 189 | } |
| 190 |
| --- internal/bots/oracle/oracle.go | |
| +++ internal/bots/oracle/oracle.go | |
| @@ -20,10 +20,12 @@ | |
| 20 | "strings" |
| 21 | "sync" |
| 22 | "time" |
| 23 | |
| 24 | "github.com/lrstanley/girc" |
| 25 | |
| 26 | "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" |
| 27 | ) |
| 28 | |
| 29 | const ( |
| 30 | botNick = "oracle" |
| 31 | defaultLimit = 50 |
| @@ -176,14 +178,26 @@ | |
| 178 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 179 | cl.Cmd.Join(ch) |
| 180 | } |
| 181 | }) |
| 182 | |
| 183 | router := cmdparse.NewRouter(botNick) |
| 184 | router.Register(cmdparse.Command{ |
| 185 | Name: "summarize", |
| 186 | Usage: "SUMMARIZE [#channel] [duration]", |
| 187 | Description: "summarize recent channel activity", |
| 188 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 189 | }) |
| 190 | |
| 191 | c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { |
| 192 | if len(e.Params) < 1 || e.Source == nil { |
| 193 | return |
| 194 | } |
| 195 | // Dispatch commands (DMs and channel messages). |
| 196 | if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { |
| 197 | cl.Cmd.Message(reply.Target, reply.Text) |
| 198 | return |
| 199 | } |
| 200 | target := e.Params[0] |
| 201 | if strings.HasPrefix(target, "#") { |
| 202 | return // channel message — ignore |
| 203 | } |
| 204 |
+22
-2
| --- internal/bots/scribe/scribe.go | ||
| +++ internal/bots/scribe/scribe.go | ||
| @@ -15,10 +15,11 @@ | ||
| 15 | 15 | "strings" |
| 16 | 16 | "time" |
| 17 | 17 | |
| 18 | 18 | "github.com/lrstanley/girc" |
| 19 | 19 | |
| 20 | + "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" | |
| 20 | 21 | "github.com/conflicthq/scuttlebot/pkg/protocol" |
| 21 | 22 | ) |
| 22 | 23 | |
| 23 | 24 | const botNick = "scribe" |
| 24 | 25 | |
| @@ -75,19 +76,38 @@ | ||
| 75 | 76 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 76 | 77 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 77 | 78 | cl.Cmd.Join(ch) |
| 78 | 79 | } |
| 79 | 80 | }) |
| 81 | + | |
| 82 | + router := cmdparse.NewRouter(botNick) | |
| 83 | + router.Register(cmdparse.Command{ | |
| 84 | + Name: "search", | |
| 85 | + Usage: "SEARCH <term>", | |
| 86 | + Description: "search channel logs", | |
| 87 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 88 | + }) | |
| 89 | + router.Register(cmdparse.Command{ | |
| 90 | + Name: "stats", | |
| 91 | + Usage: "STATS", | |
| 92 | + Description: "show channel message statistics", | |
| 93 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 94 | + }) | |
| 80 | 95 | |
| 81 | 96 | // Log PRIVMSG — the agent message stream. |
| 82 | 97 | c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) { |
| 83 | - if len(e.Params) < 1 { | |
| 98 | + if len(e.Params) < 1 || e.Source == nil { | |
| 99 | + return | |
| 100 | + } | |
| 101 | + // Dispatch commands (DMs and channel messages). | |
| 102 | + if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { | |
| 103 | + client.Cmd.Message(reply.Target, reply.Text) | |
| 84 | 104 | return |
| 85 | 105 | } |
| 86 | 106 | channel := e.Params[0] |
| 87 | 107 | if !strings.HasPrefix(channel, "#") { |
| 88 | - return // ignore DMs to scribe itself | |
| 108 | + return // non-command DMs ignored | |
| 89 | 109 | } |
| 90 | 110 | text := e.Last() |
| 91 | 111 | nick := e.Source.Name |
| 92 | 112 | b.writeEntry(channel, nick, text) |
| 93 | 113 | }) |
| 94 | 114 |
| --- internal/bots/scribe/scribe.go | |
| +++ internal/bots/scribe/scribe.go | |
| @@ -15,10 +15,11 @@ | |
| 15 | "strings" |
| 16 | "time" |
| 17 | |
| 18 | "github.com/lrstanley/girc" |
| 19 | |
| 20 | "github.com/conflicthq/scuttlebot/pkg/protocol" |
| 21 | ) |
| 22 | |
| 23 | const botNick = "scribe" |
| 24 | |
| @@ -75,19 +76,38 @@ | |
| 75 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 76 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 77 | cl.Cmd.Join(ch) |
| 78 | } |
| 79 | }) |
| 80 | |
| 81 | // Log PRIVMSG — the agent message stream. |
| 82 | c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) { |
| 83 | if len(e.Params) < 1 { |
| 84 | return |
| 85 | } |
| 86 | channel := e.Params[0] |
| 87 | if !strings.HasPrefix(channel, "#") { |
| 88 | return // ignore DMs to scribe itself |
| 89 | } |
| 90 | text := e.Last() |
| 91 | nick := e.Source.Name |
| 92 | b.writeEntry(channel, nick, text) |
| 93 | }) |
| 94 |
| --- internal/bots/scribe/scribe.go | |
| +++ internal/bots/scribe/scribe.go | |
| @@ -15,10 +15,11 @@ | |
| 15 | "strings" |
| 16 | "time" |
| 17 | |
| 18 | "github.com/lrstanley/girc" |
| 19 | |
| 20 | "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" |
| 21 | "github.com/conflicthq/scuttlebot/pkg/protocol" |
| 22 | ) |
| 23 | |
| 24 | const botNick = "scribe" |
| 25 | |
| @@ -75,19 +76,38 @@ | |
| 76 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 77 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 78 | cl.Cmd.Join(ch) |
| 79 | } |
| 80 | }) |
| 81 | |
| 82 | router := cmdparse.NewRouter(botNick) |
| 83 | router.Register(cmdparse.Command{ |
| 84 | Name: "search", |
| 85 | Usage: "SEARCH <term>", |
| 86 | Description: "search channel logs", |
| 87 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 88 | }) |
| 89 | router.Register(cmdparse.Command{ |
| 90 | Name: "stats", |
| 91 | Usage: "STATS", |
| 92 | Description: "show channel message statistics", |
| 93 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 94 | }) |
| 95 | |
| 96 | // Log PRIVMSG — the agent message stream. |
| 97 | c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) { |
| 98 | if len(e.Params) < 1 || e.Source == nil { |
| 99 | return |
| 100 | } |
| 101 | // Dispatch commands (DMs and channel messages). |
| 102 | if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { |
| 103 | client.Cmd.Message(reply.Target, reply.Text) |
| 104 | return |
| 105 | } |
| 106 | channel := e.Params[0] |
| 107 | if !strings.HasPrefix(channel, "#") { |
| 108 | return // non-command DMs ignored |
| 109 | } |
| 110 | text := e.Last() |
| 111 | nick := e.Source.Name |
| 112 | b.writeEntry(channel, nick, text) |
| 113 | }) |
| 114 |
+21
-2
| --- internal/bots/scroll/scroll.go | ||
| +++ internal/bots/scroll/scroll.go | ||
| @@ -20,10 +20,11 @@ | ||
| 20 | 20 | "sync" |
| 21 | 21 | "time" |
| 22 | 22 | |
| 23 | 23 | "github.com/lrstanley/girc" |
| 24 | 24 | |
| 25 | + "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" | |
| 25 | 26 | "github.com/conflicthq/scuttlebot/internal/bots/scribe" |
| 26 | 27 | ) |
| 27 | 28 | |
| 28 | 29 | const ( |
| 29 | 30 | botNick = "scroll" |
| @@ -81,13 +82,31 @@ | ||
| 81 | 82 | cl.Cmd.Join(ch) |
| 82 | 83 | } |
| 83 | 84 | b.log.Info("scroll connected", "channels", b.channels) |
| 84 | 85 | }) |
| 85 | 86 | |
| 86 | - // Only respond to DMs — ignore anything in a channel. | |
| 87 | + router := cmdparse.NewRouter(botNick) | |
| 88 | + router.Register(cmdparse.Command{ | |
| 89 | + Name: "replay", | |
| 90 | + Usage: "REPLAY [#channel] [count]", | |
| 91 | + Description: "replay recent channel messages", | |
| 92 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 93 | + }) | |
| 94 | + router.Register(cmdparse.Command{ | |
| 95 | + Name: "search", | |
| 96 | + Usage: "SEARCH [#channel] <term>", | |
| 97 | + Description: "search channel history", | |
| 98 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 99 | + }) | |
| 100 | + | |
| 87 | 101 | c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) { |
| 88 | - if len(e.Params) < 1 { | |
| 102 | + if len(e.Params) < 1 || e.Source == nil { | |
| 103 | + return | |
| 104 | + } | |
| 105 | + // Dispatch commands (DMs and channel messages). | |
| 106 | + if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { | |
| 107 | + client.Cmd.Message(reply.Target, reply.Text) | |
| 89 | 108 | return |
| 90 | 109 | } |
| 91 | 110 | target := e.Params[0] |
| 92 | 111 | if strings.HasPrefix(target, "#") { |
| 93 | 112 | return // channel message, ignore |
| 94 | 113 |
| --- internal/bots/scroll/scroll.go | |
| +++ internal/bots/scroll/scroll.go | |
| @@ -20,10 +20,11 @@ | |
| 20 | "sync" |
| 21 | "time" |
| 22 | |
| 23 | "github.com/lrstanley/girc" |
| 24 | |
| 25 | "github.com/conflicthq/scuttlebot/internal/bots/scribe" |
| 26 | ) |
| 27 | |
| 28 | const ( |
| 29 | botNick = "scroll" |
| @@ -81,13 +82,31 @@ | |
| 81 | cl.Cmd.Join(ch) |
| 82 | } |
| 83 | b.log.Info("scroll connected", "channels", b.channels) |
| 84 | }) |
| 85 | |
| 86 | // Only respond to DMs — ignore anything in a channel. |
| 87 | c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) { |
| 88 | if len(e.Params) < 1 { |
| 89 | return |
| 90 | } |
| 91 | target := e.Params[0] |
| 92 | if strings.HasPrefix(target, "#") { |
| 93 | return // channel message, ignore |
| 94 |
| --- internal/bots/scroll/scroll.go | |
| +++ internal/bots/scroll/scroll.go | |
| @@ -20,10 +20,11 @@ | |
| 20 | "sync" |
| 21 | "time" |
| 22 | |
| 23 | "github.com/lrstanley/girc" |
| 24 | |
| 25 | "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" |
| 26 | "github.com/conflicthq/scuttlebot/internal/bots/scribe" |
| 27 | ) |
| 28 | |
| 29 | const ( |
| 30 | botNick = "scroll" |
| @@ -81,13 +82,31 @@ | |
| 82 | cl.Cmd.Join(ch) |
| 83 | } |
| 84 | b.log.Info("scroll connected", "channels", b.channels) |
| 85 | }) |
| 86 | |
| 87 | router := cmdparse.NewRouter(botNick) |
| 88 | router.Register(cmdparse.Command{ |
| 89 | Name: "replay", |
| 90 | Usage: "REPLAY [#channel] [count]", |
| 91 | Description: "replay recent channel messages", |
| 92 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 93 | }) |
| 94 | router.Register(cmdparse.Command{ |
| 95 | Name: "search", |
| 96 | Usage: "SEARCH [#channel] <term>", |
| 97 | Description: "search channel history", |
| 98 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 99 | }) |
| 100 | |
| 101 | c.Handlers.AddBg(girc.PRIVMSG, func(client *girc.Client, e girc.Event) { |
| 102 | if len(e.Params) < 1 || e.Source == nil { |
| 103 | return |
| 104 | } |
| 105 | // Dispatch commands (DMs and channel messages). |
| 106 | if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { |
| 107 | client.Cmd.Message(reply.Target, reply.Text) |
| 108 | return |
| 109 | } |
| 110 | target := e.Params[0] |
| 111 | if strings.HasPrefix(target, "#") { |
| 112 | return // channel message, ignore |
| 113 |
+29
-2
| --- internal/bots/sentinel/sentinel.go | ||
| +++ internal/bots/sentinel/sentinel.go | ||
| @@ -20,10 +20,12 @@ | ||
| 20 | 20 | "strings" |
| 21 | 21 | "sync" |
| 22 | 22 | "time" |
| 23 | 23 | |
| 24 | 24 | "github.com/lrstanley/girc" |
| 25 | + | |
| 26 | + "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" | |
| 25 | 27 | ) |
| 26 | 28 | |
| 27 | 29 | const defaultNick = "sentinel" |
| 28 | 30 | |
| 29 | 31 | // LLMProvider calls a language model to evaluate channel content. |
| @@ -161,17 +163,42 @@ | ||
| 161 | 163 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 162 | 164 | cl.Cmd.Join(ch) |
| 163 | 165 | } |
| 164 | 166 | }) |
| 165 | 167 | |
| 166 | - c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) { | |
| 168 | + router := cmdparse.NewRouter(b.cfg.Nick) | |
| 169 | + router.Register(cmdparse.Command{ | |
| 170 | + Name: "report", | |
| 171 | + Usage: "REPORT [#channel]", | |
| 172 | + Description: "on-demand policy review", | |
| 173 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 174 | + }) | |
| 175 | + router.Register(cmdparse.Command{ | |
| 176 | + Name: "status", | |
| 177 | + Usage: "STATUS", | |
| 178 | + Description: "show current incidents", | |
| 179 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 180 | + }) | |
| 181 | + router.Register(cmdparse.Command{ | |
| 182 | + Name: "dismiss", | |
| 183 | + Usage: "DISMISS <incident-id>", | |
| 184 | + Description: "dismiss a false positive", | |
| 185 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 186 | + }) | |
| 187 | + | |
| 188 | + c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { | |
| 167 | 189 | if len(e.Params) < 1 || e.Source == nil { |
| 168 | 190 | return |
| 191 | + } | |
| 192 | + // Dispatch commands (DMs and channel messages). | |
| 193 | + if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { | |
| 194 | + cl.Cmd.Message(reply.Target, reply.Text) | |
| 195 | + return | |
| 169 | 196 | } |
| 170 | 197 | channel := e.Params[0] |
| 171 | 198 | if !strings.HasPrefix(channel, "#") { |
| 172 | - return // ignore DMs | |
| 199 | + return // non-command DMs ignored | |
| 173 | 200 | } |
| 174 | 201 | if channel == b.cfg.ModChannel { |
| 175 | 202 | return // don't analyse the mod channel itself |
| 176 | 203 | } |
| 177 | 204 | nick := e.Source.Name |
| 178 | 205 |
| --- internal/bots/sentinel/sentinel.go | |
| +++ internal/bots/sentinel/sentinel.go | |
| @@ -20,10 +20,12 @@ | |
| 20 | "strings" |
| 21 | "sync" |
| 22 | "time" |
| 23 | |
| 24 | "github.com/lrstanley/girc" |
| 25 | ) |
| 26 | |
| 27 | const defaultNick = "sentinel" |
| 28 | |
| 29 | // LLMProvider calls a language model to evaluate channel content. |
| @@ -161,17 +163,42 @@ | |
| 161 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 162 | cl.Cmd.Join(ch) |
| 163 | } |
| 164 | }) |
| 165 | |
| 166 | c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) { |
| 167 | if len(e.Params) < 1 || e.Source == nil { |
| 168 | return |
| 169 | } |
| 170 | channel := e.Params[0] |
| 171 | if !strings.HasPrefix(channel, "#") { |
| 172 | return // ignore DMs |
| 173 | } |
| 174 | if channel == b.cfg.ModChannel { |
| 175 | return // don't analyse the mod channel itself |
| 176 | } |
| 177 | nick := e.Source.Name |
| 178 |
| --- internal/bots/sentinel/sentinel.go | |
| +++ internal/bots/sentinel/sentinel.go | |
| @@ -20,10 +20,12 @@ | |
| 20 | "strings" |
| 21 | "sync" |
| 22 | "time" |
| 23 | |
| 24 | "github.com/lrstanley/girc" |
| 25 | |
| 26 | "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" |
| 27 | ) |
| 28 | |
| 29 | const defaultNick = "sentinel" |
| 30 | |
| 31 | // LLMProvider calls a language model to evaluate channel content. |
| @@ -161,17 +163,42 @@ | |
| 163 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 164 | cl.Cmd.Join(ch) |
| 165 | } |
| 166 | }) |
| 167 | |
| 168 | router := cmdparse.NewRouter(b.cfg.Nick) |
| 169 | router.Register(cmdparse.Command{ |
| 170 | Name: "report", |
| 171 | Usage: "REPORT [#channel]", |
| 172 | Description: "on-demand policy review", |
| 173 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 174 | }) |
| 175 | router.Register(cmdparse.Command{ |
| 176 | Name: "status", |
| 177 | Usage: "STATUS", |
| 178 | Description: "show current incidents", |
| 179 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 180 | }) |
| 181 | router.Register(cmdparse.Command{ |
| 182 | Name: "dismiss", |
| 183 | Usage: "DISMISS <incident-id>", |
| 184 | Description: "dismiss a false positive", |
| 185 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 186 | }) |
| 187 | |
| 188 | c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { |
| 189 | if len(e.Params) < 1 || e.Source == nil { |
| 190 | return |
| 191 | } |
| 192 | // Dispatch commands (DMs and channel messages). |
| 193 | if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { |
| 194 | cl.Cmd.Message(reply.Target, reply.Text) |
| 195 | return |
| 196 | } |
| 197 | channel := e.Params[0] |
| 198 | if !strings.HasPrefix(channel, "#") { |
| 199 | return // non-command DMs ignored |
| 200 | } |
| 201 | if channel == b.cfg.ModChannel { |
| 202 | return // don't analyse the mod channel itself |
| 203 | } |
| 204 | nick := e.Source.Name |
| 205 |
| --- internal/bots/snitch/snitch.go | ||
| +++ internal/bots/snitch/snitch.go | ||
| @@ -17,10 +17,12 @@ | ||
| 17 | 17 | "strings" |
| 18 | 18 | "sync" |
| 19 | 19 | "time" |
| 20 | 20 | |
| 21 | 21 | "github.com/lrstanley/girc" |
| 22 | + | |
| 23 | + "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" | |
| 22 | 24 | ) |
| 23 | 25 | |
| 24 | 26 | const defaultNick = "snitch" |
| 25 | 27 | |
| 26 | 28 | // Config controls snitch's thresholds and alert destination. |
| @@ -165,14 +167,33 @@ | ||
| 165 | 167 | if len(e.Params) < 1 || e.Source == nil { |
| 166 | 168 | return |
| 167 | 169 | } |
| 168 | 170 | b.recordJoinPart(e.Params[0], e.Source.Name) |
| 169 | 171 | }) |
| 172 | + | |
| 173 | + router := cmdparse.NewRouter(b.cfg.Nick) | |
| 174 | + router.Register(cmdparse.Command{ | |
| 175 | + Name: "status", | |
| 176 | + Usage: "STATUS", | |
| 177 | + Description: "show current active alerts", | |
| 178 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 179 | + }) | |
| 180 | + router.Register(cmdparse.Command{ | |
| 181 | + Name: "acknowledge", | |
| 182 | + Usage: "ACKNOWLEDGE <alert-id>", | |
| 183 | + Description: "acknowledge an alert", | |
| 184 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 185 | + }) | |
| 170 | 186 | |
| 171 | 187 | c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) { |
| 172 | 188 | if len(e.Params) < 1 || e.Source == nil { |
| 173 | 189 | return |
| 190 | + } | |
| 191 | + // Dispatch commands (DMs and channel messages). | |
| 192 | + if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { | |
| 193 | + c.Cmd.Message(reply.Target, reply.Text) | |
| 194 | + return | |
| 174 | 195 | } |
| 175 | 196 | channel := e.Params[0] |
| 176 | 197 | nick := e.Source.Name |
| 177 | 198 | if nick == b.cfg.Nick { |
| 178 | 199 | return |
| 179 | 200 |
| --- internal/bots/snitch/snitch.go | |
| +++ internal/bots/snitch/snitch.go | |
| @@ -17,10 +17,12 @@ | |
| 17 | "strings" |
| 18 | "sync" |
| 19 | "time" |
| 20 | |
| 21 | "github.com/lrstanley/girc" |
| 22 | ) |
| 23 | |
| 24 | const defaultNick = "snitch" |
| 25 | |
| 26 | // Config controls snitch's thresholds and alert destination. |
| @@ -165,14 +167,33 @@ | |
| 165 | if len(e.Params) < 1 || e.Source == nil { |
| 166 | return |
| 167 | } |
| 168 | b.recordJoinPart(e.Params[0], e.Source.Name) |
| 169 | }) |
| 170 | |
| 171 | c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) { |
| 172 | if len(e.Params) < 1 || e.Source == nil { |
| 173 | return |
| 174 | } |
| 175 | channel := e.Params[0] |
| 176 | nick := e.Source.Name |
| 177 | if nick == b.cfg.Nick { |
| 178 | return |
| 179 |
| --- internal/bots/snitch/snitch.go | |
| +++ internal/bots/snitch/snitch.go | |
| @@ -17,10 +17,12 @@ | |
| 17 | "strings" |
| 18 | "sync" |
| 19 | "time" |
| 20 | |
| 21 | "github.com/lrstanley/girc" |
| 22 | |
| 23 | "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" |
| 24 | ) |
| 25 | |
| 26 | const defaultNick = "snitch" |
| 27 | |
| 28 | // Config controls snitch's thresholds and alert destination. |
| @@ -165,14 +167,33 @@ | |
| 167 | if len(e.Params) < 1 || e.Source == nil { |
| 168 | return |
| 169 | } |
| 170 | b.recordJoinPart(e.Params[0], e.Source.Name) |
| 171 | }) |
| 172 | |
| 173 | router := cmdparse.NewRouter(b.cfg.Nick) |
| 174 | router.Register(cmdparse.Command{ |
| 175 | Name: "status", |
| 176 | Usage: "STATUS", |
| 177 | Description: "show current active alerts", |
| 178 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 179 | }) |
| 180 | router.Register(cmdparse.Command{ |
| 181 | Name: "acknowledge", |
| 182 | Usage: "ACKNOWLEDGE <alert-id>", |
| 183 | Description: "acknowledge an alert", |
| 184 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 185 | }) |
| 186 | |
| 187 | c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) { |
| 188 | if len(e.Params) < 1 || e.Source == nil { |
| 189 | return |
| 190 | } |
| 191 | // Dispatch commands (DMs and channel messages). |
| 192 | if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { |
| 193 | c.Cmd.Message(reply.Target, reply.Text) |
| 194 | return |
| 195 | } |
| 196 | channel := e.Params[0] |
| 197 | nick := e.Source.Name |
| 198 | if nick == b.cfg.Nick { |
| 199 | return |
| 200 |
| --- internal/bots/steward/steward.go | ||
| +++ internal/bots/steward/steward.go | ||
| @@ -29,10 +29,12 @@ | ||
| 29 | 29 | "strings" |
| 30 | 30 | "sync" |
| 31 | 31 | "time" |
| 32 | 32 | |
| 33 | 33 | "github.com/lrstanley/girc" |
| 34 | + | |
| 35 | + "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" | |
| 34 | 36 | ) |
| 35 | 37 | |
| 36 | 38 | const defaultNick = "steward" |
| 37 | 39 | |
| 38 | 40 | // Config controls steward's behaviour. |
| @@ -144,14 +146,39 @@ | ||
| 144 | 146 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 145 | 147 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 146 | 148 | cl.Cmd.Join(ch) |
| 147 | 149 | } |
| 148 | 150 | }) |
| 151 | + | |
| 152 | + router := cmdparse.NewRouter(b.cfg.Nick) | |
| 153 | + router.Register(cmdparse.Command{ | |
| 154 | + Name: "act", | |
| 155 | + Usage: "ACT <incident-id>", | |
| 156 | + Description: "manually trigger action on incident", | |
| 157 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 158 | + }) | |
| 159 | + router.Register(cmdparse.Command{ | |
| 160 | + Name: "override", | |
| 161 | + Usage: "OVERRIDE <incident-id>", | |
| 162 | + Description: "override pending action", | |
| 163 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 164 | + }) | |
| 165 | + router.Register(cmdparse.Command{ | |
| 166 | + Name: "status", | |
| 167 | + Usage: "STATUS", | |
| 168 | + Description: "show current pending actions", | |
| 169 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 170 | + }) | |
| 149 | 171 | |
| 150 | 172 | c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) { |
| 151 | 173 | if len(e.Params) < 1 || e.Source == nil { |
| 152 | 174 | return |
| 175 | + } | |
| 176 | + // Dispatch commands (DMs and channel messages). | |
| 177 | + if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { | |
| 178 | + c.Cmd.Message(reply.Target, reply.Text) | |
| 179 | + return | |
| 153 | 180 | } |
| 154 | 181 | target := e.Params[0] |
| 155 | 182 | nick := e.Source.Name |
| 156 | 183 | text := strings.TrimSpace(e.Last()) |
| 157 | 184 | |
| 158 | 185 |
| --- internal/bots/steward/steward.go | |
| +++ internal/bots/steward/steward.go | |
| @@ -29,10 +29,12 @@ | |
| 29 | "strings" |
| 30 | "sync" |
| 31 | "time" |
| 32 | |
| 33 | "github.com/lrstanley/girc" |
| 34 | ) |
| 35 | |
| 36 | const defaultNick = "steward" |
| 37 | |
| 38 | // Config controls steward's behaviour. |
| @@ -144,14 +146,39 @@ | |
| 144 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 145 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 146 | cl.Cmd.Join(ch) |
| 147 | } |
| 148 | }) |
| 149 | |
| 150 | c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) { |
| 151 | if len(e.Params) < 1 || e.Source == nil { |
| 152 | return |
| 153 | } |
| 154 | target := e.Params[0] |
| 155 | nick := e.Source.Name |
| 156 | text := strings.TrimSpace(e.Last()) |
| 157 | |
| 158 |
| --- internal/bots/steward/steward.go | |
| +++ internal/bots/steward/steward.go | |
| @@ -29,10 +29,12 @@ | |
| 29 | "strings" |
| 30 | "sync" |
| 31 | "time" |
| 32 | |
| 33 | "github.com/lrstanley/girc" |
| 34 | |
| 35 | "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" |
| 36 | ) |
| 37 | |
| 38 | const defaultNick = "steward" |
| 39 | |
| 40 | // Config controls steward's behaviour. |
| @@ -144,14 +146,39 @@ | |
| 146 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 147 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 148 | cl.Cmd.Join(ch) |
| 149 | } |
| 150 | }) |
| 151 | |
| 152 | router := cmdparse.NewRouter(b.cfg.Nick) |
| 153 | router.Register(cmdparse.Command{ |
| 154 | Name: "act", |
| 155 | Usage: "ACT <incident-id>", |
| 156 | Description: "manually trigger action on incident", |
| 157 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 158 | }) |
| 159 | router.Register(cmdparse.Command{ |
| 160 | Name: "override", |
| 161 | Usage: "OVERRIDE <incident-id>", |
| 162 | Description: "override pending action", |
| 163 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 164 | }) |
| 165 | router.Register(cmdparse.Command{ |
| 166 | Name: "status", |
| 167 | Usage: "STATUS", |
| 168 | Description: "show current pending actions", |
| 169 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 170 | }) |
| 171 | |
| 172 | c.Handlers.AddBg(girc.PRIVMSG, func(_ *girc.Client, e girc.Event) { |
| 173 | if len(e.Params) < 1 || e.Source == nil { |
| 174 | return |
| 175 | } |
| 176 | // Dispatch commands (DMs and channel messages). |
| 177 | if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { |
| 178 | c.Cmd.Message(reply.Target, reply.Text) |
| 179 | return |
| 180 | } |
| 181 | target := e.Params[0] |
| 182 | nick := e.Source.Name |
| 183 | text := strings.TrimSpace(e.Last()) |
| 184 | |
| 185 |
| --- internal/bots/systembot/systembot.go | ||
| +++ internal/bots/systembot/systembot.go | ||
| @@ -17,10 +17,12 @@ | ||
| 17 | 17 | "strconv" |
| 18 | 18 | "strings" |
| 19 | 19 | "time" |
| 20 | 20 | |
| 21 | 21 | "github.com/lrstanley/girc" |
| 22 | + | |
| 23 | + "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" | |
| 22 | 24 | ) |
| 23 | 25 | |
| 24 | 26 | const botNick = "systembot" |
| 25 | 27 | |
| 26 | 28 | // EntryKind classifies a system event. |
| @@ -176,10 +178,35 @@ | ||
| 176 | 178 | if e.Source != nil { |
| 177 | 179 | nick = e.Source.Name |
| 178 | 180 | } |
| 179 | 181 | b.write(Entry{Kind: KindMode, Channel: channel, Nick: nick, Text: strings.Join(e.Params, " ")}) |
| 180 | 182 | }) |
| 183 | + | |
| 184 | + router := cmdparse.NewRouter(botNick) | |
| 185 | + router.Register(cmdparse.Command{ | |
| 186 | + Name: "status", | |
| 187 | + Usage: "STATUS", | |
| 188 | + Description: "show connected users and channel counts", | |
| 189 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 190 | + }) | |
| 191 | + router.Register(cmdparse.Command{ | |
| 192 | + Name: "who", | |
| 193 | + Usage: "WHO [#channel]", | |
| 194 | + Description: "show detailed user list", | |
| 195 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 196 | + }) | |
| 197 | + | |
| 198 | + c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { | |
| 199 | + if len(e.Params) < 1 || e.Source == nil { | |
| 200 | + return | |
| 201 | + } | |
| 202 | + // Dispatch commands (DMs and channel messages). | |
| 203 | + if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { | |
| 204 | + cl.Cmd.Message(reply.Target, reply.Text) | |
| 205 | + return | |
| 206 | + } | |
| 207 | + }) | |
| 181 | 208 | |
| 182 | 209 | b.client = c |
| 183 | 210 | |
| 184 | 211 | errCh := make(chan error, 1) |
| 185 | 212 | go func() { |
| 186 | 213 |
| --- internal/bots/systembot/systembot.go | |
| +++ internal/bots/systembot/systembot.go | |
| @@ -17,10 +17,12 @@ | |
| 17 | "strconv" |
| 18 | "strings" |
| 19 | "time" |
| 20 | |
| 21 | "github.com/lrstanley/girc" |
| 22 | ) |
| 23 | |
| 24 | const botNick = "systembot" |
| 25 | |
| 26 | // EntryKind classifies a system event. |
| @@ -176,10 +178,35 @@ | |
| 176 | if e.Source != nil { |
| 177 | nick = e.Source.Name |
| 178 | } |
| 179 | b.write(Entry{Kind: KindMode, Channel: channel, Nick: nick, Text: strings.Join(e.Params, " ")}) |
| 180 | }) |
| 181 | |
| 182 | b.client = c |
| 183 | |
| 184 | errCh := make(chan error, 1) |
| 185 | go func() { |
| 186 |
| --- internal/bots/systembot/systembot.go | |
| +++ internal/bots/systembot/systembot.go | |
| @@ -17,10 +17,12 @@ | |
| 17 | "strconv" |
| 18 | "strings" |
| 19 | "time" |
| 20 | |
| 21 | "github.com/lrstanley/girc" |
| 22 | |
| 23 | "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" |
| 24 | ) |
| 25 | |
| 26 | const botNick = "systembot" |
| 27 | |
| 28 | // EntryKind classifies a system event. |
| @@ -176,10 +178,35 @@ | |
| 178 | if e.Source != nil { |
| 179 | nick = e.Source.Name |
| 180 | } |
| 181 | b.write(Entry{Kind: KindMode, Channel: channel, Nick: nick, Text: strings.Join(e.Params, " ")}) |
| 182 | }) |
| 183 | |
| 184 | router := cmdparse.NewRouter(botNick) |
| 185 | router.Register(cmdparse.Command{ |
| 186 | Name: "status", |
| 187 | Usage: "STATUS", |
| 188 | Description: "show connected users and channel counts", |
| 189 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 190 | }) |
| 191 | router.Register(cmdparse.Command{ |
| 192 | Name: "who", |
| 193 | Usage: "WHO [#channel]", |
| 194 | Description: "show detailed user list", |
| 195 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 196 | }) |
| 197 | |
| 198 | c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { |
| 199 | if len(e.Params) < 1 || e.Source == nil { |
| 200 | return |
| 201 | } |
| 202 | // Dispatch commands (DMs and channel messages). |
| 203 | if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { |
| 204 | cl.Cmd.Message(reply.Target, reply.Text) |
| 205 | return |
| 206 | } |
| 207 | }) |
| 208 | |
| 209 | b.client = c |
| 210 | |
| 211 | errCh := make(chan error, 1) |
| 212 | go func() { |
| 213 |
+33
-1
| --- internal/bots/warden/warden.go | ||
| +++ internal/bots/warden/warden.go | ||
| @@ -18,10 +18,11 @@ | ||
| 18 | 18 | "sync" |
| 19 | 19 | "time" |
| 20 | 20 | |
| 21 | 21 | "github.com/lrstanley/girc" |
| 22 | 22 | |
| 23 | + "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" | |
| 23 | 24 | "github.com/conflicthq/scuttlebot/pkg/protocol" |
| 24 | 25 | ) |
| 25 | 26 | |
| 26 | 27 | const botNick = "warden" |
| 27 | 28 | |
| @@ -215,18 +216,49 @@ | ||
| 215 | 216 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 216 | 217 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 217 | 218 | cl.Cmd.Join(ch) |
| 218 | 219 | } |
| 219 | 220 | }) |
| 221 | + | |
| 222 | + router := cmdparse.NewRouter(botNick) | |
| 223 | + router.Register(cmdparse.Command{ | |
| 224 | + Name: "warn", | |
| 225 | + Usage: "WARN <nick> [reason]", | |
| 226 | + Description: "issue a warning to a user", | |
| 227 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 228 | + }) | |
| 229 | + router.Register(cmdparse.Command{ | |
| 230 | + Name: "mute", | |
| 231 | + Usage: "MUTE <nick> [duration]", | |
| 232 | + Description: "mute a user", | |
| 233 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 234 | + }) | |
| 235 | + router.Register(cmdparse.Command{ | |
| 236 | + Name: "kick", | |
| 237 | + Usage: "KICK <nick> [reason]", | |
| 238 | + Description: "kick a user from channel", | |
| 239 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 240 | + }) | |
| 241 | + router.Register(cmdparse.Command{ | |
| 242 | + Name: "status", | |
| 243 | + Usage: "STATUS", | |
| 244 | + Description: "show current warnings and mutes", | |
| 245 | + Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, | |
| 246 | + }) | |
| 220 | 247 | |
| 221 | 248 | c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { |
| 222 | 249 | if len(e.Params) < 1 || e.Source == nil { |
| 223 | 250 | return |
| 251 | + } | |
| 252 | + // Dispatch commands (DMs and channel messages). | |
| 253 | + if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { | |
| 254 | + cl.Cmd.Message(reply.Target, reply.Text) | |
| 255 | + return | |
| 224 | 256 | } |
| 225 | 257 | channel := e.Params[0] |
| 226 | 258 | if !strings.HasPrefix(channel, "#") { |
| 227 | - return | |
| 259 | + return // non-command DMs ignored | |
| 228 | 260 | } |
| 229 | 261 | nick := e.Source.Name |
| 230 | 262 | text := e.Last() |
| 231 | 263 | |
| 232 | 264 | cs := b.channelStateFor(channel) |
| 233 | 265 |
| --- internal/bots/warden/warden.go | |
| +++ internal/bots/warden/warden.go | |
| @@ -18,10 +18,11 @@ | |
| 18 | "sync" |
| 19 | "time" |
| 20 | |
| 21 | "github.com/lrstanley/girc" |
| 22 | |
| 23 | "github.com/conflicthq/scuttlebot/pkg/protocol" |
| 24 | ) |
| 25 | |
| 26 | const botNick = "warden" |
| 27 | |
| @@ -215,18 +216,49 @@ | |
| 215 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 216 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 217 | cl.Cmd.Join(ch) |
| 218 | } |
| 219 | }) |
| 220 | |
| 221 | c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { |
| 222 | if len(e.Params) < 1 || e.Source == nil { |
| 223 | return |
| 224 | } |
| 225 | channel := e.Params[0] |
| 226 | if !strings.HasPrefix(channel, "#") { |
| 227 | return |
| 228 | } |
| 229 | nick := e.Source.Name |
| 230 | text := e.Last() |
| 231 | |
| 232 | cs := b.channelStateFor(channel) |
| 233 |
| --- internal/bots/warden/warden.go | |
| +++ internal/bots/warden/warden.go | |
| @@ -18,10 +18,11 @@ | |
| 18 | "sync" |
| 19 | "time" |
| 20 | |
| 21 | "github.com/lrstanley/girc" |
| 22 | |
| 23 | "github.com/conflicthq/scuttlebot/internal/bots/cmdparse" |
| 24 | "github.com/conflicthq/scuttlebot/pkg/protocol" |
| 25 | ) |
| 26 | |
| 27 | const botNick = "warden" |
| 28 | |
| @@ -215,18 +216,49 @@ | |
| 216 | c.Handlers.AddBg(girc.INVITE, func(cl *girc.Client, e girc.Event) { |
| 217 | if ch := e.Last(); strings.HasPrefix(ch, "#") { |
| 218 | cl.Cmd.Join(ch) |
| 219 | } |
| 220 | }) |
| 221 | |
| 222 | router := cmdparse.NewRouter(botNick) |
| 223 | router.Register(cmdparse.Command{ |
| 224 | Name: "warn", |
| 225 | Usage: "WARN <nick> [reason]", |
| 226 | Description: "issue a warning to a user", |
| 227 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 228 | }) |
| 229 | router.Register(cmdparse.Command{ |
| 230 | Name: "mute", |
| 231 | Usage: "MUTE <nick> [duration]", |
| 232 | Description: "mute a user", |
| 233 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 234 | }) |
| 235 | router.Register(cmdparse.Command{ |
| 236 | Name: "kick", |
| 237 | Usage: "KICK <nick> [reason]", |
| 238 | Description: "kick a user from channel", |
| 239 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 240 | }) |
| 241 | router.Register(cmdparse.Command{ |
| 242 | Name: "status", |
| 243 | Usage: "STATUS", |
| 244 | Description: "show current warnings and mutes", |
| 245 | Handler: func(_ *cmdparse.Context, _ string) string { return "not implemented yet" }, |
| 246 | }) |
| 247 | |
| 248 | c.Handlers.AddBg(girc.PRIVMSG, func(cl *girc.Client, e girc.Event) { |
| 249 | if len(e.Params) < 1 || e.Source == nil { |
| 250 | return |
| 251 | } |
| 252 | // Dispatch commands (DMs and channel messages). |
| 253 | if reply := router.Dispatch(e.Source.Name, e.Params[0], e.Last()); reply != nil { |
| 254 | cl.Cmd.Message(reply.Target, reply.Text) |
| 255 | return |
| 256 | } |
| 257 | channel := e.Params[0] |
| 258 | if !strings.HasPrefix(channel, "#") { |
| 259 | return // non-command DMs ignored |
| 260 | } |
| 261 | nick := e.Source.Name |
| 262 | text := e.Last() |
| 263 | |
| 264 | cs := b.channelStateFor(channel) |
| 265 |