diff --git a/docs/CHEATSHEET.md b/docs/CHEATSHEET.md index 9c05838..838f447 100644 --- a/docs/CHEATSHEET.md +++ b/docs/CHEATSHEET.md @@ -103,7 +103,8 @@ IRC operators are auto-detected via WHO on connect and on user JOIN !mode +o nick # Give ops ``` -Auto-joins channels when invited by an admin/ircop. +Auto-joins channels when invited by an admin/ircop. Persists across restarts. +Removed from auto-rejoin list if bot is kicked. ## State Store (admin) diff --git a/docs/USAGE.md b/docs/USAGE.md index bfd7457..836d08a 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -302,7 +302,10 @@ The bot must have channel operator status for these commands to take effect. ### Invite Auto-Join When an admin or IRC operator sends an `INVITE`, the bot automatically joins -the target channel. Non-admin invites are silently ignored. +the target channel and persists it for auto-rejoin on reconnect. Non-admin +invites are silently ignored. If the bot is kicked from a persisted channel, +it is removed from the auto-rejoin list. Channels already in the config +`[bot] channels` are skipped (they rejoin via the normal path). ## Plugin State Persistence diff --git a/plugins/chanmgmt.py b/plugins/chanmgmt.py index 3155411..bfd96f3 100644 --- a/plugins/chanmgmt.py +++ b/plugins/chanmgmt.py @@ -85,6 +85,23 @@ async def cmd_mode(bot, message): await bot.mode(message.target, mode_str, *args) +@event("001") +async def on_connect(bot, message): + """Rejoin channels persisted from previous invite-joins.""" + from derp.irc import format_msg + + configured = set(bot.config["bot"]["channels"]) + for key in bot.state.keys("chanmgmt"): + if not key.startswith("autojoin:"): + continue + channel = key[len("autojoin:"):] + if channel in configured: + continue + log.info("rejoining %s (autojoin)", channel) + await bot.join(channel) + await bot.conn.send(format_msg("WHO", channel)) + + @event("INVITE") async def on_invite(bot, message): """Join a channel when invited by an admin or IRC operator.""" @@ -96,3 +113,17 @@ async def on_invite(bot, message): return log.info("accepting invite to %s from %s", channel, message.nick) await bot.join(channel) + bot.state.set("chanmgmt", f"autojoin:{channel}", message.nick) + + +@event("KICK") +async def on_kick(bot, message): + """Remove autojoin if the bot is kicked from a channel.""" + if len(message.params) < 2: + return + kicked = message.params[1] + if kicked != bot.nick: + return + channel = message.target + if bot.state.delete("chanmgmt", f"autojoin:{channel}"): + log.info("removed autojoin for %s (kicked)", channel)