When the bot accepts an admin INVITE, the channel is stored in bot.state under chanmgmt/autojoin:<channel>. On reconnect, persisted channels are rejoined alongside configured ones. If the bot is kicked, the channel is removed from the auto-rejoin list. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
130 lines
4.2 KiB
Python
130 lines
4.2 KiB
Python
"""Channel management: kick, ban, unban, topic, mode, invite-join."""
|
|
|
|
import logging
|
|
|
|
from derp.plugin import command, event
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def _require_channel(message):
|
|
"""Return True if the message originated in a channel."""
|
|
return message.is_channel
|
|
|
|
|
|
@command("kick", help="Kick a user: !kick <nick> [reason]", admin=True)
|
|
async def cmd_kick(bot, message):
|
|
"""Kick a user from the current channel."""
|
|
if not _require_channel(message):
|
|
await bot.reply(message, "Must be used in a channel")
|
|
return
|
|
parts = message.text.split(None, 2)
|
|
if len(parts) < 2:
|
|
await bot.reply(message, "Usage: !kick <nick> [reason]")
|
|
return
|
|
nick = parts[1]
|
|
reason = parts[2] if len(parts) > 2 else ""
|
|
await bot.kick(message.target, nick, reason)
|
|
|
|
|
|
@command("ban", help="Ban a hostmask: !ban <mask>", admin=True)
|
|
async def cmd_ban(bot, message):
|
|
"""Set +b on a hostmask in the current channel."""
|
|
if not _require_channel(message):
|
|
await bot.reply(message, "Must be used in a channel")
|
|
return
|
|
parts = message.text.split(None, 1)
|
|
if len(parts) < 2:
|
|
await bot.reply(message, "Usage: !ban <mask>")
|
|
return
|
|
mask = parts[1]
|
|
await bot.mode(message.target, "+b", mask)
|
|
|
|
|
|
@command("unban", help="Remove a ban: !unban <mask>", admin=True)
|
|
async def cmd_unban(bot, message):
|
|
"""Remove +b from a hostmask in the current channel."""
|
|
if not _require_channel(message):
|
|
await bot.reply(message, "Must be used in a channel")
|
|
return
|
|
parts = message.text.split(None, 1)
|
|
if len(parts) < 2:
|
|
await bot.reply(message, "Usage: !unban <mask>")
|
|
return
|
|
mask = parts[1]
|
|
await bot.mode(message.target, "-b", mask)
|
|
|
|
|
|
@command("topic", help="Set or query channel topic: !topic [text]", admin=True)
|
|
async def cmd_topic(bot, message):
|
|
"""Set or query the channel topic."""
|
|
if not _require_channel(message):
|
|
await bot.reply(message, "Must be used in a channel")
|
|
return
|
|
parts = message.text.split(None, 1)
|
|
if len(parts) < 2:
|
|
# Query current topic
|
|
from derp.irc import format_msg
|
|
await bot.conn.send(format_msg("TOPIC", message.target))
|
|
else:
|
|
await bot.set_topic(message.target, parts[1])
|
|
|
|
|
|
@command("mode", help="Set channel mode: !mode <mode> [args]", admin=True)
|
|
async def cmd_mode(bot, message):
|
|
"""Set a mode on the current channel."""
|
|
if not _require_channel(message):
|
|
await bot.reply(message, "Must be used in a channel")
|
|
return
|
|
parts = message.text.split(None)
|
|
if len(parts) < 2:
|
|
await bot.reply(message, "Usage: !mode <mode> [args]")
|
|
return
|
|
mode_str = parts[1]
|
|
args = parts[2:]
|
|
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."""
|
|
if not bot._is_admin(message):
|
|
log.info("ignoring invite from non-admin %s", message.nick)
|
|
return
|
|
channel = message.params[1] if len(message.params) > 1 else None
|
|
if not channel:
|
|
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)
|