feat(irc): support optional bot mode (#1246)

* feat(irc): set bot mode when the server supports it

See https://ircv3.net/specs/extensions/bot-mode.

* feat(irc): add a config option per network for bot mode
This commit is contained in:
Kuredant 2023-11-23 03:57:16 +09:00 committed by GitHub
parent c6c74c7f3b
commit f89a25d645
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 67 additions and 10 deletions

View file

@ -85,6 +85,8 @@ type Handler struct {
connectionErrors []string
failedNickServAttempts int
botModeChar string
authenticated bool
saslauthed bool
}
@ -201,6 +203,9 @@ func (h *Handler) Run() error {
h.client.AddDisconnectCallback(h.onDisconnect)
h.client.AddCallback("MODE", h.handleMode)
if h.network.BotMode {
h.client.AddCallback("501", h.handleModeUnknownFlag)
}
h.client.AddCallback("INVITE", h.handleInvite)
h.client.AddCallback("366", h.handleJoined)
h.client.AddCallback("PART", h.handlePart)
@ -383,8 +388,13 @@ func (h *Handler) onConnect(m ircmsg.Message) {
time.Sleep(1 * time.Second)
h.authenticate()
if h.network.BotMode && h.botModeSupported() {
// if we set Bot Mode, we'll try to authenticate after the MODE response
h.setBotMode()
return
}
h.authenticate()
}
// onDisconnect is the disconnect callback
@ -492,6 +502,20 @@ func (h *Handler) handleNickServ(msg ircmsg.Message) {
}
}
// botModeSupported checks if IRCv3 Bot Mode is supported by the server
// See https://ircv3.net/specs/extensions/bot-mode
func (h *Handler) botModeSupported() bool {
h.botModeChar = h.client.ISupport()["BOT"]
return h.botModeChar != ""
}
// setBotMode attempts to set Bot Mode on ourself
// See https://ircv3.net/specs/extensions/bot-mode
func (h *Handler) setBotMode() {
h.client.Send("MODE", h.CurrentNick(), "+"+h.botModeChar)
}
// authenticate sends NickServIdentify if not authenticated
func (h *Handler) authenticate() bool {
h.m.RLock()
@ -869,7 +893,15 @@ func (h *Handler) handleMode(msg ircmsg.Message) {
return
}
return
if h.network.BotMode && h.botModeChar != "" && h.isOurCurrentNick(msg.Params[0]) && strings.Contains(msg.Params[1], "+"+h.botModeChar) {
h.authenticate()
}
}
// listens for ERR_UMODEUNKNOWNFLAG events
func (h *Handler) handleModeUnknownFlag(msg ircmsg.Message) {
// if Bot Mode setting failed, still try to authenticate
h.authenticate()
}
func (h *Handler) SendMsg(channel, msg string) error {

View file

@ -211,6 +211,10 @@ func (s *service) checkIfNetworkRestartNeeded(network *domain.IrcNetwork) error
restartNeeded = true
fieldsChanged = append(fieldsChanged, "bouncer addr")
}
if handler.BotMode != network.BotMode {
restartNeeded = true
fieldsChanged = append(fieldsChanged, "bot mode")
}
if handler.Auth.Mechanism != network.Auth.Mechanism {
restartNeeded = true
fieldsChanged = append(fieldsChanged, "auth mechanism")
@ -447,6 +451,7 @@ func (s *service) GetNetworksWithHealth(ctx context.Context) ([]domain.IrcNetwor
InviteCommand: n.InviteCommand,
BouncerAddr: n.BouncerAddr,
UseBouncer: n.UseBouncer,
BotMode: n.BotMode,
Connected: false,
Channels: []domain.ChannelWithHealth{},
ConnectionErrors: []string{},