From e552ba7552514dd5553ff7a90fe1e8a07221d214 Mon Sep 17 00:00:00 2001 From: Unstable Kitsune Date: Thu, 11 Sep 2025 19:46:27 -0400 Subject: [PATCH] feat: implement configurable welcomer cog with settings Refactors simple welcomer to support custom messages, channels, and toggles. Adds command group for admins to manage server-specific settings, placeholders for personalization, and built-in safety checks for permissions and channel validity. Enhances event handling with better error management and logging. --- welcomer/README.md | 83 +++++++++++++++-- welcomer/__init__.py | 1 + welcomer/welcomer.py | 207 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 260 insertions(+), 31 deletions(-) diff --git a/welcomer/README.md b/welcomer/README.md index 5b7409e..9cde89e 100644 --- a/welcomer/README.md +++ b/welcomer/README.md @@ -1,9 +1,82 @@ -# Welcomer Cog +# Welcomer Cog - ver-1.0.0 +A configurable cog to automatically welcome new users when they join your server. -A simple cog to welcome new users to a server with a customizable message. +# Features +- Fully Configurable: Set a custom welcome message and channel for each server. +- Enable/Disable: Easily toggle the welcomer on or off without losing your settings. +- Placeholder Support: Personalize your welcome message with user and server details. +- Easy Setup: A simple command group for admins to manage all settings. +- Permissions: All settings commands require `Manage Server` permissions to use. -**Features:** -- Greets new members in a designated channel. -- Uses custom formatting provided by the server admin. +# Commands +All configuration is handled through the `[p]welcomeset` command group. + +[p]welcomeset channel <#channel> +Sets the channel where welcome messages will be sent. + +Alias: chnl + +Example: [p]welcomeset channel #welcome + +[p]welcomeset message +Sets the custom welcome message. See the "Placeholders" section below for available variables. + +Alias: msg + +Example: [p]welcomeset message Welcome {user.mention} to {server_name}! We're glad you're here. + +[p]welcomeset toggle +Toggles the welcomer system on or off for the server. + +Aliases: on, off + +Example: [p]welcomeset toggle + +[p]welcomeset settings +Displays the current settings for the welcomer in an embed. + +Aliases: show, status + +Example: [p]welcomeset settings + +[p]welcomeset test +Sends a preview of the current welcome message to the channel where the command is run. + +Example: [p]welcomeset test + +[p]welcomeset reset +Resets all welcomer settings for the server to their default values. + +Example: [p]welcomeset reset + +Quick Setup Guide +Load the Cog: + +[p]load welcomer + +Set the Welcome Channel: + +[p]welcomeset channel #your-welcome-channel + +Set Your Custom Message: + +[p]welcomeset message Welcome, {user.mention}! Enjoy your stay in {server_name}! + +Enable the System: + +[p]welcomeset toggle + +The bot will now welcome new members in the channel you specified. You can use [p]welcomeset settings at any time to check your configuration. + +Placeholders for the Welcome Message +You can use the following placeholders in your custom welcome message. They will be automatically replaced with the correct information when a new user joins. + +{user}: The user object itself. + +{user.mention}: Pings the new user (e.g., @UnstableKitsune). + +{user_name}: The new user's name (e.g., UnstableKitsune). + +{server_name}: The name of the server they joined. For full documentation, please visit the [repository wiki](https://git.kitsunic.org/kitsunicWorks/unstable-cogs/wiki). \ No newline at end of file diff --git a/welcomer/__init__.py b/welcomer/__init__.py index e69de29..0df88cb 100644 --- a/welcomer/__init__.py +++ b/welcomer/__init__.py @@ -0,0 +1 @@ +from .welcomer import setup \ No newline at end of file diff --git a/welcomer/welcomer.py b/welcomer/welcomer.py index 5b0c6ee..93e5670 100644 --- a/welcomer/welcomer.py +++ b/welcomer/welcomer.py @@ -1,40 +1,195 @@ # welcomer.py -from redbot.core import commands -from redbot.core.bot import Red import discord +from redbot.core import commands, Config +from redbot.core.bot import Red class Welcomer(commands.Cog): - """A simple cog to welcome new users.""" + """ + A configurable cog to welcome new users to a server. + """ def __init__(self, bot: Red): self.bot = bot + # Initialize Red's Config system. This will store our settings. + # The "GUILD" identifier means settings will be saved on a per-server basis. + self.config = Config.get_conf(self, identifier=1234567890, force_registration=True) + + # Define the default settings for each server. + default_guild = { + "welcome_channel": None, # The ID of the channel to send welcomes to + "welcome_message": "Welcome to the server, {user.mention}!", # Default message + "enabled": False # Whether the system is on or off + } + + # Register the default settings. + self.config.register_guild(**default_guild) @commands.Cog.listener() - async def on_member_join(self, member): - """Greets a new member when they join the server.""" - + async def on_member_join(self, member: discord.Member): + """ + The event listener that runs when a new member joins. + """ guild = member.guild - while True: - # Try to find a channel named 'welcome' - channel = discord.utils.get(guild.text_channels, name='welcome') - if channel is None: - # If not found, try to find a channel named 'general' - channel = discord.utils.get(guild.text_channels, name='general') - if channel is None: - # If still not found, use the first text channel available - if guild.text_channels: - channel = guild.text_channels[0] - else: - # If no text channels exist, exit the function - return - try: - await channel.send(f"Welcome to the server, {member.mention}!") - break # Exit the loop after successfully sending the message - except discord.Forbidden: - # If we don't have permission to send messages in this channel, try again - continue + + # Check if the welcomer is enabled for this server. + if not await self.config.guild(guild).enabled(): + return -# This function allows Red to load the cog + # Get the welcome channel ID from our saved settings. + channel_id = await self.config.guild(guild).welcome_channel() + if not channel_id: + return # If no channel is set, do nothing. + + # Try to find the channel object in the server. + channel = guild.get_channel(channel_id) + if not channel: + # The channel might have been deleted. + return + + # Explicitly check if the channel is a TextChannel before trying to send a message. + if not isinstance(channel, discord.TextChannel): + print(f"Welcomer: Configured channel '{channel.name}' in {guild.name} is not a text channel.") + return + + # Get the custom welcome message from our settings. + message_template = await self.config.guild(guild).welcome_message() + + # Format the message with the new member's info. + # .format() is a safe way to replace placeholders. + formatted_message = message_template.format( + user=member, + user_mention=member.mention, + user_name=member.name, + server_name=guild.name + ) + + # Check if we have permission to send messages in the channel. + if not channel.permissions_for(guild.me).send_messages: + # We can't send a message, so we'll just log this internally. + print(f"Welcomer: No permission to send messages in {channel.name} in {guild.name}.") + return + + try: + await channel.send(formatted_message) + except discord.Forbidden: + # This is a final safety check in case permissions change suddenly. + pass + except discord.HTTPException as e: + # This can happen if the message is too long or there's a Discord API error. + print(f"Welcomer: Failed to send welcome message in {guild.name}: {e}") + + # Create a command group for all our settings commands. + @commands.group(aliases=["wset"]) # type: ignore + @commands.guild_only() # Ensures this command and its subcommands can only be run in a server + @commands.admin_or_permissions(manage_guild=True) + async def welcomeset(self, ctx: commands.Context): + """ + Configure the welcomer settings for this server. + """ + pass + + @welcomeset.command(name="channel", aliases=["chnl"]) + async def welcomeset_channel(self, ctx: commands.Context, channel: discord.TextChannel): + """ + Set the channel where welcome messages will be sent. + + Example: + [p]welcomeset channel #welcome + """ + if not ctx.guild: + return # This check satisfies the type checker + await self.config.guild(ctx.guild).welcome_channel.set(channel.id) + await ctx.send(f"The welcome channel has been set to {channel.mention}.") + + @welcomeset.command(name="message", aliases=["msg"]) + async def welcomeset_message(self, ctx: commands.Context, *, message: str): + """ + Set the custom welcome message. + + You can use these placeholders: + {user} - The user object. + {user_mention} - Pings the user. + {user_name} - The user's name. + {server_name} - The name of this server. + + Example: + [p]welcomeset message Hello {user_mention}, welcome to {server_name}! + """ + if not ctx.guild: + return + await self.config.guild(ctx.guild).welcome_message.set(message) + await ctx.send(f"The welcome message has been updated.") + # Send a preview of the new message. + preview = message.format( + user=ctx.author, + user_mention=ctx.author.mention, + user_name=ctx.author.name, + server_name=ctx.guild.name + ) + await ctx.send(f"**Preview:**\n{preview}") + + @welcomeset.command(name="toggle", aliases=["on", "off"]) + async def welcomeset_toggle(self, ctx: commands.Context): + """ + Enable or disable the welcomer system on this server. + """ + if not ctx.guild: + return + current_status = await self.config.guild(ctx.guild).enabled() + new_status = not current_status + await self.config.guild(ctx.guild).enabled.set(new_status) + status_text = "enabled" if new_status else "disabled" + await ctx.send(f"The welcomer system has been {status_text}.") + + @welcomeset.command(name="settings", aliases=["show", "status"]) + async def welcomeset_settings(self, ctx: commands.Context): + """ + Show the current welcomer settings for this server. + """ + if not ctx.guild: + return + settings = await self.config.guild(ctx.guild).all() + channel_id = settings['welcome_channel'] + channel = ctx.guild.get_channel(channel_id) if channel_id else None + message = settings['welcome_message'] + enabled = "Enabled" if settings['enabled'] else "Disabled" + + embed = discord.Embed(title="Welcomer Settings", color=await ctx.embed_color()) + embed.add_field(name="Status", value=enabled, inline=False) + embed.add_field(name="Channel", value=channel.mention if isinstance(channel, discord.TextChannel) else "Not Set", inline=False) + embed.add_field(name="Message", value=f"```{message}```", inline=False) + + await ctx.send(embed=embed) + + @welcomeset.command(name="test") + async def welcomeset_test(self, ctx: commands.Context): + """ + Test the welcome message by sending a preview to this channel. + """ + if not ctx.guild: + return + message_template = await self.config.guild(ctx.guild).welcome_message() + preview = message_template.format( + user=ctx.author, + user_mention=ctx.author.mention, + user_name=ctx.author.name, + server_name=ctx.guild.name + ) + await ctx.send(f"**Welcome Message Preview**:\n{preview}") + + @welcomeset.command(name="reset") + async def welcomeset_reset(self, ctx: commands.Context): + """ + Reset all welcomer settings to their defaults. + """ + if not ctx.guild: + return + await self.config.guild(ctx.guild).clear() + await ctx.send("The welcomer settings have been reset to their defaults.") + + +# This function allows Red to load the cog. +# It is required in every cog file. async def setup(bot: Red): await bot.add_cog(Welcomer(bot)) \ No newline at end of file -- 2.43.0