From 54ff18c2397bc6b8a362b1d28b584fe7b3e9a8c2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 21:05:32 +0000 Subject: [PATCH] feat: add welcome message cog for new member DMs - Add welcome.py cog that sends DMs to new members on join - Per-server configurable welcome message stored in database - Command to view current welcome text with edit button - Modal UI for editing the welcome message - Requires manage_guild permission to configure Co-Authored-By: Oliver Ni --- bmt_discord_bot/__init__.py | 1 + bmt_discord_bot/cogs/welcome.py | 122 ++++++++++++++++++ bmt_discord_bot/database.py | 1 + bmt_discord_bot/migrations/0003_welcome.sql | 4 + .../migrations/0003_welcome_down.sql | 1 + 5 files changed, 129 insertions(+) create mode 100644 bmt_discord_bot/cogs/welcome.py create mode 100644 bmt_discord_bot/migrations/0003_welcome.sql create mode 100644 bmt_discord_bot/migrations/0003_welcome_down.sql diff --git a/bmt_discord_bot/__init__.py b/bmt_discord_bot/__init__.py index c0092af..7304edd 100644 --- a/bmt_discord_bot/__init__.py +++ b/bmt_discord_bot/__init__.py @@ -15,6 +15,7 @@ class Bot(commands.Bot): "math", "reminders", "viraj", + "welcome", ] def __init__(self, database: Database): diff --git a/bmt_discord_bot/cogs/welcome.py b/bmt_discord_bot/cogs/welcome.py new file mode 100644 index 0000000..acf3f95 --- /dev/null +++ b/bmt_discord_bot/cogs/welcome.py @@ -0,0 +1,122 @@ +import discord +from discord.ext import commands + +from bmt_discord_bot import Bot, Context + + +class EditWelcomeModal(discord.ui.Modal, title="Edit Welcome Message"): + message = discord.ui.TextInput( + label="Welcome Message", + style=discord.TextStyle.paragraph, + placeholder="Enter the welcome message to send to new members...", + required=False, + max_length=2000, + ) + + def __init__(self, current_message: str | None, cog: "Welcome", guild_id: int): + super().__init__() + self.cog = cog + self.guild_id = guild_id + if current_message: + self.message.default = current_message + + async def on_submit(self, interaction: discord.Interaction): + new_message = self.message.value.strip() if self.message.value else None + + if new_message: + await self.cog.bot.database.pool.execute( + """ + INSERT INTO welcome_settings (guild_id, welcome_message) + VALUES ($1, $2) + ON CONFLICT (guild_id) DO UPDATE SET welcome_message = EXCLUDED.welcome_message + """, + self.guild_id, + new_message, + ) + await interaction.response.send_message( + "Welcome message updated successfully.", ephemeral=True + ) + else: + await self.cog.bot.database.pool.execute( + """ + DELETE FROM welcome_settings + WHERE guild_id = $1 + """, + self.guild_id, + ) + await interaction.response.send_message( + "Welcome message has been disabled.", ephemeral=True + ) + + +class WelcomeView(discord.ui.View): + def __init__(self, cog: "Welcome", guild_id: int, current_message: str | None): + super().__init__(timeout=180) + self.cog = cog + self.guild_id = guild_id + self.current_message = current_message + + @discord.ui.button(label="Edit Message", style=discord.ButtonStyle.primary) + async def edit_button(self, interaction: discord.Interaction, button: discord.ui.Button): + modal = EditWelcomeModal(self.current_message, self.cog, self.guild_id) + await interaction.response.send_modal(modal) + + +class Welcome(commands.Cog): + """Welcome new members with a direct message.""" + + def __init__(self, bot: Bot): + self.bot = bot + + async def get_welcome_message(self, guild_id: int) -> str | None: + return await self.bot.database.pool.fetchval( + """ + SELECT welcome_message + FROM welcome_settings + WHERE guild_id = $1 + """, + guild_id, + ) + + @commands.Cog.listener() + async def on_member_join(self, member: discord.Member): + if member.bot: + return + + welcome_message = await self.get_welcome_message(member.guild.id) + if not welcome_message: + return + + try: + await member.send(welcome_message) + except discord.Forbidden: + pass + + @commands.hybrid_command() + @commands.guild_only() + @commands.has_permissions(manage_guild=True) + async def welcome(self, ctx: Context): + """View and edit the welcome message for new members.""" + + assert ctx.guild is not None + current_message = await self.get_welcome_message(ctx.guild.id) + + if current_message: + embed = discord.Embed( + title="Current Welcome Message", + description=current_message, + color=discord.Color.blue(), + ) + else: + embed = discord.Embed( + title="Welcome Message", + description="No welcome message is currently set for this server.", + color=discord.Color.greyple(), + ) + + view = WelcomeView(self, ctx.guild.id, current_message) + await ctx.send(embed=embed, view=view) + + +async def setup(bot): + await bot.add_cog(Welcome(bot)) diff --git a/bmt_discord_bot/database.py b/bmt_discord_bot/database.py index 2cd1f4e..3595162 100644 --- a/bmt_discord_bot/database.py +++ b/bmt_discord_bot/database.py @@ -24,6 +24,7 @@ class Database: MIGRATIONS = [ Migration.from_files("0001_reminders"), Migration.from_files("0002_math"), + Migration.from_files("0003_welcome"), ] def __init__(self, pool: asyncpg.Pool): diff --git a/bmt_discord_bot/migrations/0003_welcome.sql b/bmt_discord_bot/migrations/0003_welcome.sql new file mode 100644 index 0000000..cf17fee --- /dev/null +++ b/bmt_discord_bot/migrations/0003_welcome.sql @@ -0,0 +1,4 @@ +CREATE TABLE welcome_settings ( + guild_id BIGINT PRIMARY KEY, + welcome_message TEXT +); diff --git a/bmt_discord_bot/migrations/0003_welcome_down.sql b/bmt_discord_bot/migrations/0003_welcome_down.sql new file mode 100644 index 0000000..f7e1258 --- /dev/null +++ b/bmt_discord_bot/migrations/0003_welcome_down.sql @@ -0,0 +1 @@ +DROP TABLE welcome_settings;