From 868b2b7e32cc2681e2c490ca654b3931d42f2f2b Mon Sep 17 00:00:00 2001 From: Hash Borgir Date: Thu, 30 Apr 2026 01:07:29 -0500 Subject: [PATCH] Welcome plugin added. Refactored funguy.py a bit. --- funguy.py | 24 +++++++++ plugins/welcome.py | 119 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 plugins/welcome.py diff --git a/funguy.py b/funguy.py index 0384f90..99427da 100755 --- a/funguy.py +++ b/funguy.py @@ -90,6 +90,22 @@ class FunguyBot: except Exception as e: logging.error(f"Error loading plugin {plugin_name}: {e}") # Logging error if plugin loading fails + def setup_plugins(self): + """ + Method to call setup(bot) on any plugin that defines it. + + This must be called AFTER self.bot is created (i.e. inside run()), so + that plugins which register custom event listeners (e.g. on_custom_event + for RoomMemberEvent) receive a valid bot instance. + """ + for plugin_name, plugin_module in self.PLUGINS.items(): + if hasattr(plugin_module, "setup") and callable(plugin_module.setup): + try: + plugin_module.setup(self.bot) + logging.info(f"Setup called for plugin: {plugin_name}") + except Exception as e: + logging.error(f"Error during setup of plugin {plugin_name}: {e}") + def reload_plugins(self): """ Method to reload all plugins. @@ -100,6 +116,9 @@ class FunguyBot: if plugin_name.startswith(self.PLUGINS_DIR + "."): del sys.modules[plugin_name] # Deleting plugin module from system modules self.load_plugins() # Reloading plugins + # Re-run setup for any plugin that needs it (bot already exists at this point) + if self.bot is not None: + self.setup_plugins() def load_config(self): """ @@ -223,6 +242,11 @@ class FunguyBot: creds = botlib.Creds(MATRIX_URL, MATRIX_USER, MATRIX_PASS) # Creating credentials object self.bot = botlib.Bot(creds, self.config) # Creating bot instance + # Call setup() on any plugin that defines it, now that self.bot exists. + # This is what registers custom event listeners such as the join-event + # listener in the welcome plugin. + self.setup_plugins() + # Defining listener for message events @self.bot.listener.on_message_event async def wrapper_handle_commands(room, message): diff --git a/plugins/welcome.py b/plugins/welcome.py new file mode 100644 index 0000000..9ec043e --- /dev/null +++ b/plugins/welcome.py @@ -0,0 +1,119 @@ +""" +Plugin for welcoming new users to the room. + +Features: + * Automatically greets users when they join the target room. + * !welcome command – manually trigger the welcome message for yourself. + +Restricted to room: !NXdVjDXPxXowPkrJJY:matrix.org +""" + +import logging +import simplematrixbotlib as botlib +import nio + +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- + +ALLOWED_ROOM_ID = "!NXdVjDXPxXowPkrJJY:matrix.org" + +# --------------------------------------------------------------------------- +# Internal helpers +# --------------------------------------------------------------------------- + +def _build_welcome_message(display_name: str) -> str: + """Return the Markdown welcome message for a given display name.""" + return ( + f"Welcome to the room, **{display_name}**! 🎉\n\n" + "We're glad to have you here in " + "**Self‑hosting | Security | Sysadmin | Homelab | Programming**!\n\n" + "To help us get to know you better:\n" + "• 🔧 **What do you self‑host?** Got any cool services running?\n" + "• 🔐 **Are you into cybersecurity?** What areas interest you most?\n" + "• 💻 **What programming languages do you use?**\n" + "• 🏠 **Tell us about your homelab setup!**\n\n" + "Feel free to introduce yourself and jump into the conversation! 🍄" + ) + + +# --------------------------------------------------------------------------- +# Plugin setup - called by funguy.py's run() after the bot object is created +# --------------------------------------------------------------------------- + +def setup(bot): + """ + Register the member-join listener. + + funguy.py must call plugin_module.setup(self.bot) for each loaded plugin + (after self.bot is created) for this listener to fire. See the note in + funguy.py's run() method. + """ + + @bot.listener.on_custom_event(nio.RoomMemberEvent) + async def _on_member_event(room, event): + logging.debug( + "RoomMemberEvent received: room=%s, sender=%s, membership=%s", + room.room_id, event.sender, event.membership, + ) + + # Only the configured room + if room.room_id != ALLOWED_ROOM_ID: + return + + # Only on actual join (not invite / leave / ban) + if event.membership != "join": + return + + # Ignore the bot itself + if event.sender == bot.async_client.user_id: + logging.debug("Ignoring bot's own join event") + return + + # Derive a friendly display name from the Matrix ID (@name:server → name) + display_name = event.sender.split(":")[0].lstrip("@") + + logging.info( + "User %s joined room %s – sending welcome", event.sender, room.room_id + ) + try: + await bot.api.send_markdown_message( + room.room_id, _build_welcome_message(display_name) + ) + logging.info("Welcome message sent successfully to %s", display_name) + except Exception as exc: + logging.error("Failed to send welcome message: %s", exc) + + logging.info("Join listener registered for room %s", ALLOWED_ROOM_ID) + + +# --------------------------------------------------------------------------- +# Plugin entry point - called by FunguyBot for every message event +# --------------------------------------------------------------------------- + +async def handle_command(room, message, bot, prefix, config): + """Handle the !welcome command.""" + + match = botlib.MessageMatch(room, message, bot, prefix) + + if not ( + match.is_not_from_this_bot() + and match.prefix() + and match.command("welcome") + ): + return + + if room.room_id != ALLOWED_ROOM_ID: + await bot.api.send_text_message( + room.room_id, + "The !welcome command only works in the designated room.", + ) + return + + sender = str(message.sender) + display_name = sender.split(":")[0].lstrip("@") + + await bot.api.send_markdown_message( + room.room_id, _build_welcome_message(display_name) + ) + logging.info("Sent manual !welcome to %s", sender)