diff --git a/src/classes/bot.py b/src/classes/bot.py index c0b32eb..fc3b6f2 100644 --- a/src/classes/bot.py +++ b/src/classes/bot.py @@ -1,23 +1,44 @@ -from disnake.ext import commands +import os import disnake import dotenv -import os +from disnake.ext import commands dotenv.load_dotenv() -class MyBot(commands.InteractionBot): - def __init__(self): - super().__init__(intents=disnake.Intents.none()) # temporary - async def on_message(self, message: disnake.Message): - if "rewrite" in message.content.lower() and "rust" in message.content.lower() and message.guild.id == int(os.environ["FUN_GUILD"]): +class MyBot(commands.InteractionBot): + def __init__(self) -> None: + super().__init__( + intents=disnake.Intents( + messages=True, + members=True, + guilds=True, + ) + ) + self.cached_guild: disnake.Guild + self.guild_member_role: disnake.Role | None = None + self.guild_welcome_channel: disnake.TextChannel | None = None + + async def on_message(self, message: disnake.Message) -> None: + assert message.guild is not None + if ( + "rewrite" in message.content.lower() + and "rust" in message.content.lower() + and message.guild.id == self.cached_guild.id + ): await message.channel.send("never, stop asking") - async def on_ready(self): - print(f"logged in as {str(self.user)}") + async def on_ready(self) -> None: + self.cached_guild = await self.fetch_guild(int(os.environ["FUN_GUILD"])) - async def on_member_join(self, member: disnake.Member): - if member.guild.id == int(os.environ["FUN_GUILD"]): - role = member.guild.get_role(1275732314696978473) - await member.add_roles(role) \ No newline at end of file + if env_welcome_role := os.getenv("MEMBER_ROLE"): + self.guild_member_role = await self.cached_guild.fetch_role( + int(env_welcome_role) + ) + + if env_welcome_channel := os.getenv("WELCOME_CHANNEL"): + self.guild_welcome_channel = await self.cached_guild.fetch_channel( # pyright: ignore[reportAttributeAccessIssue] + int(env_welcome_channel) + ) + print(f"logged in as {str(self.user)}") diff --git a/src/plugins/member_join_management.py b/src/plugins/member_join_management.py new file mode 100644 index 0000000..42b6d62 --- /dev/null +++ b/src/plugins/member_join_management.py @@ -0,0 +1,93 @@ +from datetime import UTC, datetime, timedelta + +import disnake_plugins as plugins +from disnake import Event, HTTPException, Member, Message +from disnake.ext import tasks +from disnake.utils import format_dt + +from src.classes.bot import MyBot + +plugin = plugins.Plugin[MyBot]() + +MIN_JOIN_MINUTES = 15 +MIN_JOIN_DAYS = 14 + +last_message_store: dict[int, int] = {} + + +@plugin.listener(Event.member_join) +async def member_join_store(member: Member) -> None: + if not plugin.bot.guild_welcome_channel: + return + + welcome_message = await plugin.bot.guild_welcome_channel.send( + f"{member.mention}, welcome to soteria! do you want music??" + ) + last_message_store[member.id] = welcome_message.id + + +@plugin.listener(Event.message) +async def last_sent_message_store(message: Message) -> None: + last_message_store[message.author.id] = message.id + + +new_member_store: dict[Member, datetime] = {} + + +@plugin.listener(Event.member_join) +async def new_member_role_assign(member: Member) -> None: + if not member.joined_at: + return + + new_member_store[member] = member.joined_at + + +@plugin.listener(Event.member_remove) +async def member_leave_react(member: Member) -> None: + + cached_last_sent_message = last_message_store.get(member.id) + if not cached_last_sent_message: + return + + last_sent_message = plugin.bot.get_message(cached_last_sent_message) + if not last_sent_message: + return + + try: + await last_sent_message.add_reaction("") + except HTTPException: + await last_sent_message.add_reaction("\U0001f6aa") # door emoji + last_message_store.pop(last_message_store[member.id], None) + new_member_store.pop(member, None) + + +@plugin.register_loop(wait_until_ready=True) +@tasks.loop(minutes=1) +async def new_member_role_assign_timer() -> None: + if not plugin.bot.guild_member_role: + return + + for member, joined_at in new_member_store.copy().items(): + if datetime.now(UTC) > joined_at + timedelta(minutes=MIN_JOIN_MINUTES): + await member.add_roles( + plugin.bot.guild_member_role, + reason="member has been in the guild for 15 minutes", + ) + new_member_store.pop(member, None) + + +@plugin.listener(Event.member_join) +async def member_created_at_checker(member: Member) -> None: + since_creation = datetime.now(tz=UTC) - member.created_at + days_since_creation = since_creation.days + if days_since_creation < MIN_JOIN_DAYS: + await member.send( + f"you have been kicked from **{member.guild.name}** as your account is not at least {MIN_JOIN_DAYS} days old. " + f"to minimise the potential for raids or spam, we will not let you join until {format_dt(member.created_at + timedelta(days=MIN_JOIN_DAYS))}." + ) + await member.kick( + reason=f"account too young. only {days_since_creation} days old" + ) + + +setup, teardown = plugin.create_extension_handlers()