diff --git a/.env.template b/.env.template index d6b08e0f..2b5b9091 100644 --- a/.env.template +++ b/.env.template @@ -7,8 +7,7 @@ SENTRY_URL= PRODUCTION_BOT_TOKEN= DEVELOPMENT_BOT_TOKEN= BLOXLINK_API_KEY= -# DO NOT CHANGE, EVEN IF YOU'RE SELF HOSTING -CUSTOM_GUILD_ID=0 + # If you want to use a custom DB name (instead of `erm`), uncomment this and enable it # DB_NAME= diff --git a/AGENTS.md b/AGENTS.md index 0eee7761..b5f3fd77 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -78,7 +78,6 @@ Per [documentation/coding-assistants.md](documentation/coding-assistants.md): ## Gotchas -- `CUSTOM_GUILD_ID` must remain `0` — do not change - `DB_NAME` defaults to `erm`; uncomment in `.env` only if needed - `GITHUB_TOKEN` is reserved for future use, not used by the bot - Database schema reference: [documentation/database-schema.md](documentation/database-schema.md) diff --git a/cogs/Search.py b/cogs/Search.py index ea381099..f8391a81 100644 --- a/cogs/Search.py +++ b/cogs/Search.py @@ -101,7 +101,7 @@ async def mywarnings( roblox_id=roblox_player.id ) - if member and bot.environment != "CUSTOM": + if member: try: discord_member = await guild.fetch_member(member.discord_id) except discord.NotFound: @@ -113,8 +113,7 @@ async def mywarnings( for role in discord_member.roles if role.id in magic_flags_reverse ) - elif member and bot.environment == "CUSTOM": - applied_flags.update(["ERM Staff"]) + applied_flags = list(applied_flags) if ( @@ -356,7 +355,7 @@ async def search(self, ctx, *, query): roblox_id=roblox_player.id ) - if member and bot.environment != "CUSTOM": + if member: try: discord_member = await guild.fetch_member(member.discord_id) except discord.NotFound: @@ -368,8 +367,7 @@ async def search(self, ctx, *, query): for role in discord_member.roles if role.id in magic_flags_reverse ) - elif member and bot.environment == "CUSTOM": - applied_flags.update(["ERM Staff"]) + applied_flags = list(applied_flags) if ( diff --git a/documentation/architecture.md b/documentation/architecture.md index 96d91ef1..4ad1531b 100644 --- a/documentation/architecture.md +++ b/documentation/architecture.md @@ -106,8 +106,6 @@ staggered 30-second delay between each to avoid startup load spikes. `REMINDERS_ENABLED` and `ACTIONS_ENABLED` respectively and will not start if those are set to `FALSE`. -`change_status` does not run when `ENVIRONMENT=CUSTOM`. - --- ## Data Layer diff --git a/erm.py b/erm.py index 4eb5bb8d..88a72769 100644 --- a/erm.py +++ b/erm.py @@ -94,7 +94,7 @@ async def rate_limited_fetch(coro, endpoint_type="default"): raise setup = False -accepted_envs = ["PRODUCTION", "DEVELOPMENT", "ALPHA", "CUSTOM"] +accepted_envs = ["PRODUCTION", "DEVELOPMENT", "ALPHA"] sentry_url = config("SENTRY_URL", "") @@ -141,9 +141,6 @@ async def is_owner(self, user: discord.User): # Else fall back to the original if user.id == 1394817794427846737: return True - - if environment != "CUSTOM": # let's not allow custom bot owners to use jishaku lol - return await super().is_owner(user) else: return False @@ -208,13 +205,6 @@ async def setup_hook(self) -> None: self.accounts = Accounts(self) - if environment == "CUSTOM": - doc = await self.whitelabel.db.find_one({"GuildID": config("CUSTOM_GUILD_ID", default="0")}) - if not doc: - raise Exception( - "Custom guild ID not found in the database. This means the whitelabel subscription is overdue." - ) - self.roblox = roblox.Client() self.prc_api = PRCApiClient( self, @@ -279,11 +269,7 @@ async def setup_hook(self) -> None: if environment == "DEVELOPMENT": pass # await bot.tree.sync(guild=discord.Object(id=987798554972143728)) - elif environment == "CUSTOM": - await self.tree.sync() - # Prevent auto syncing - # await bot.tree.sync() - # guild specific: leave blank if global (global registration can take 1-24 hours) + bot.is_synced = True self.saved_latencies = { "shards": [], @@ -310,9 +296,6 @@ async def setup_hook(self) -> None: -if config("ENVIRONMENT") == "CUSTOM": - Bot.__bases__ = (commands.Bot,) - bot = Bot( command_prefix=get_prefix, case_insensitive=True, @@ -342,38 +325,7 @@ def running(): @bot.before_invoke async def AutoDefer(ctx: commands.Context): - if ( - environment == "CUSTOM" - and config("CUSTOM_GUILD_ID", default="0") != "0" - and not getattr(ctx.bot, "whitelist_disabled", False) - ): - if ctx.guild.id != int(config("CUSTOM_GUILD_ID")): - if ctx.interaction: - await ctx.interaction.response.send_message( - embed=discord.Embed( - title="Not Permitted", - description="This bot is not permitted to be used in this server. You can change this in the **Whitelabel Bot Dashboard**.", - color=BLANK_COLOR, - ), - ephemeral=True, - ) - raise Exception(f"Guild not permitted to use this bot: {ctx.guild.id}") - guild_id = ctx.guild.id - if (environment != "CUSTOM" or int(config("CUSTOM_GUILD_ID", default="0")) != guild_id) and await has_whitelabel(bot, guild_id): - if "jishaku" in ctx.command.qualified_name: - return - if ctx.interaction: - await ctx.interaction.response.send_message( - embed=discord.Embed( - title="Not Permitted", - description="There is a whitelabel bot already in this server.", - color=BLANK_COLOR, - ), - ephemeral=True, - ) - raise Exception("Whitelabel bot already in use") - bot.internal_command_storage[ctx.message.id] = datetime.datetime.now(tz=pytz.UTC).timestamp() if ctx.command: if ctx.command.extras.get("ephemeral") is True: @@ -408,41 +360,6 @@ async def loggingCommandExecution(ctx: commands.Context): "Command could not be found in internal context storage. Please report." ) - -@bot.event -async def on_message( - message, -): # DO NOT COG - - if not message.guild: - return await bot.process_commands(message) - - if ( - environment == "CUSTOM" - and config("CUSTOM_GUILD_ID", default=None) != 0 - and not getattr(bot, "whitelist_disabled", False) - ): - if message.guild.id != int(config("CUSTOM_GUILD_ID")): - ctx = await bot.get_context(message) - if ctx.command is not None: - await message.reply( - embed=discord.Embed( - title="Not Permitted", - description="This bot is not permitted to be used in this server. You can change this in the **Whitelabel Bot Dashboard**.", - color=BLANK_COLOR, - ) - ) - return - - if environment == "PRODUCTION" and await bot.whitelabel.db.find_one({"GuildID": str(message.guild.id)}) is not None: - return - - await bot.process_commands(message) - - -client = roblox.Client() - - async def staff_check(bot_obj, guild, member): guild_settings = await bot_obj.settings.find_by_id(guild.id) member_role_ids = [r.id for r in member.roles] diff --git a/events/on_message.py b/events/on_message.py index e10a171c..10367957 100644 --- a/events/on_message.py +++ b/events/on_message.py @@ -56,8 +56,6 @@ async def on_message(self, message: discord.Message): if not message.guild: return - if await has_whitelabel(bot, message.guild.id) and (bot.environment != "CUSTOM" or int(config("CUSTOM_GUILD_ID", default="0")) != message.guild.id): - return if not hasattr(bot, "settings"): return diff --git a/tasks/check_reminders.py b/tasks/check_reminders.py index 1cc842a3..42a92c2f 100644 --- a/tasks/check_reminders.py +++ b/tasks/check_reminders.py @@ -120,8 +120,7 @@ async def process_reminder(bot, guild, item, guild_obj): async def iterate_reminder(bot, guild_obj): """Iterate through all reminders for a guild and process any that are due.""" - if await has_whitelabel(bot, guild_obj["_id"]): - return + guild = bot.get_guild(int(guild_obj["_id"])) if not guild: @@ -145,13 +144,6 @@ async def iterate_reminder(bot, guild_obj): @tasks.loop(minutes=1) async def check_reminders(bot): query = {} - if bot.environment != "PRODUCTION": - try: - query = {"_id": int(config("CUSTOM_GUILD_ID"))} - except Exception as e: - logging.warning(f"Reminder task failed: {e}") - return - try: for guild_obj in await bot.reminders.db.find(query).to_list(None): try: diff --git a/tasks/check_whitelabel.py b/tasks/check_whitelabel.py new file mode 100644 index 00000000..1d2f3586 --- /dev/null +++ b/tasks/check_whitelabel.py @@ -0,0 +1,45 @@ +import discord +from discord.ext import tasks +from erm import Bot +import datetime +import aiohttp + + + +@tasks.loop(hours=2) +async def check_whitelabel(bot: Bot): + async for item in bot.whitelabel.db.find({}): + try: + print(item) + if item["GuildID"] == 0: + await bot.whitelabel.delete(item["_id"]) + continue + try: + guild = await bot.fetch_guild(int(item["GuildID"])) # looking at the db, guild ids aren't always ints + except: + return + try: + owner = await guild.fetch_member(int(item["DiscordID"])) + except: + return + bot_member = guild.me or guild.get_member(bot.user.id) or await guild.fetch_member(bot.user.id) + + print(bot_member) + time = datetime.datetime.now(tz=datetime.UTC) + expiry = datetime.datetime.fromtimestamp(item["Expiry"], tz=datetime.UTC) + if expiry < time: + await owner.send(embed=discord.Embed( + title="Whitelabel Subscription Expired", + description="Your whitelabel subscription has expired, therefore, the avatar, banner, and bio will be reset. Please renew your subscription through the web dashboard or open a ticket if you need assistance." + )) + await bot_member.edit(avatar=None, banner=None, bio=None, reason="Whitelabel subscription expired") + return + + session = aiohttp.ClientSession() + av = await (await session.get(item["UserData"]["AvatarURL"])).read() + banner = await (await session.get(item["UserData"]["BannerURL"])).read() + await bot_member.edit(avatar=av, banner=banner, bio=item["UserData"]["Bio"]) + except Exception as e: + print(str(e)) + continue + diff --git a/tasks/iterate_ics.py b/tasks/iterate_ics.py index 6342dc91..aa0ac1a2 100644 --- a/tasks/iterate_ics.py +++ b/tasks/iterate_ics.py @@ -12,7 +12,7 @@ async def iterate_ics(bot): # This will aim to constantly update the Integration Command Storage # and the relevant storage data. - async for item in bot.ics.db.find({} if bot.environment in ["PRODUCTION", "ALPHA", "DEVELOPMENT"] else {"guild": config("CUSTOM_GUILD_ID")}): + async for item in bot.ics.db.find({}): guild = bot.get_guild(item["guild"]) if not guild: diff --git a/tasks/iterate_prc_logs.py b/tasks/iterate_prc_logs.py index 7f1f7c81..7efd36b9 100644 --- a/tasks/iterate_prc_logs.py +++ b/tasks/iterate_prc_logs.py @@ -45,10 +45,10 @@ count_aggregate = global_aggregate + [{"$count": "total"}] - -async def iterate_prc_logs_global(bot): +@tasks.loop(minutes=5, reconnect=True) +async def iterate_prc_logs(bot): try: - server_count_list = await (await bot.settings.db.aggregate(count_aggregate)).to_list(length=None) + server_count_list = [i async for i in await bot.settings.db.aggregate(count_aggregate)] server_count = server_count_list[0]["total"] if server_count_list else 0 logging.warning(f"[ITERATE] Starting iteration for {server_count} servers") @@ -78,16 +78,7 @@ async def iterate_prc_logs_global(bot): -async def iterate_prc_logs_custom(bot): - guild_id = config("CUSTOM_GUILD_ID") - if not guild_id: - logging.info("No custom guild ID provided for custom environment") - return - try: - await unprimitive_guild_process({"_id": int(guild_id)}, bot) - except Exception as e: - logging.warning(f"error processing guild: {e}") async def unprimitive_guild_process(items, bot): guild = bot.get_guild(items["_id"]) or await bot.fetch_guild( @@ -98,9 +89,6 @@ async def unprimitive_guild_process(items, bot): settings = await bot.settings.find_by_id(guild.id) erlc_settings = settings.get("ERLC", {}) - if await has_whitelabel(bot, guild.id) and not config("CUSTOM_GUILD_ID") == str(guild.id): - logging.warning("Not handling {} due to whitelabel instance existing") - return channels = { "kill_logs": erlc_settings.get("kill_logs"), @@ -209,6 +197,7 @@ async def unprimitive_guild_process(items, bot): await asyncio.gather(*subtasks, return_exceptions=True) await bot.log_tracker.save_guild(guild.id) + await flush_pending_unbans(bot, guild.id) async def process_guild(bot, items, semaphore): async with semaphore: @@ -221,14 +210,6 @@ async def process_guild(bot, items, semaphore): logging.warning(f"error processing guild: {e}") -@tasks.loop(minutes=7, reconnect=True) -async def iterate_prc_logs(bot): - if bot.environment == "PRODUCTION": - await iterate_prc_logs_global(bot) - else: - await iterate_prc_logs_custom( - bot - ) async def fetch_logs_with_retry(guild_id, bot, retries=3): @@ -535,6 +516,25 @@ async def process_player_logs(bot, settings, guild_id, player_logs, last_timesta return embeds, latest_timestamp +async def flush_pending_unbans(bot, guild_id: int): + now = int(datetime.datetime.now(tz=pytz.UTC).timestamp()) + pending = bot.punishments.db.find({ + "Guild": guild_id, + "CheckExecuted": {"$exists": False}, + "UntilEpoch": {"$lt": now}, + "Type": "Temporary Ban", + "Epoch": {"$gt": 1709164800}, + }) + async for item in pending: + try: + await bot.prc_api.unban_user(guild_id, item["UserID"]) + item["CheckExecuted"] = True + await bot.punishments.update_by_id(item) + await asyncio.sleep(1) + except Exception: + break + + async def is_username_found(username: str, members: list[discord.Member]) -> bool: pattern = re.compile(re.escape(username), re.IGNORECASE) member_found = False diff --git a/tasks/sync_weather.py b/tasks/sync_weather.py index 05e83899..5e59e699 100644 --- a/tasks/sync_weather.py +++ b/tasks/sync_weather.py @@ -83,17 +83,6 @@ async def fetch_weather(session: aiohttp.ClientSession, lat: float, lon: float, @tasks.loop(minutes=2, reconnect=True) async def sync_weather(bot): - chosen_filter = { - "CUSTOM": {"_id": int(config("CUSTOM_GUILD_ID", default=0))}, - "_": { - "_id": { - "$nin": [ - int(item["GuildID"] or 0) - async for item in bot.whitelabel.db.find({}) - ] - } - }, - }["CUSTOM" if config("ENVIRONMENT") == "CUSTOM" else "_"] try: logging.info("Starting weather sync task...") @@ -107,7 +96,6 @@ async def sync_weather(bot): {"ERLC.weather.sync_weather": True}, ], "ERLC.weather.location": {"$exists": True, "$ne": ""}, - **chosen_filter, } }, { @@ -143,9 +131,7 @@ async def sync_weather(bot): processed += 1 guild_id = guild_data["_id"] - if config("ENVIRONMENT") == "CUSTOM": - if guild_id != int(config("CUSTOM_GUILD_ID", default=0)): - continue + weather_settings = guild_data["ERLC"]["weather"] location = weather_settings["location"] diff --git a/utils/api.py b/utils/api.py index 2e9eefa7..2a674333 100644 --- a/utils/api.py +++ b/utils/api.py @@ -2144,8 +2144,7 @@ def __init__( async def __call__(self, request: Request, call_next): guild_id = "" try: - if config("ENVIRONMENT") == "CUSTOM": - raise Exception("We're already redirected.") + request_json = await request.json() guild_id = int( diff --git a/utils/utils.py b/utils/utils.py index 5381a7a6..517c84c6 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -81,7 +81,8 @@ async def generalised_interaction_check_failure( async def has_whitelabel(bot, guild_id: int) -> bool: - if (item := await bot.whitelabel.db.find_one({"GuildID": str(guild_id)})) is not None and config("ENVIRONMENT") not in ["ALPHA", "DEVELOPMENT"]: + item = await bot.whitelabel.db.find_one({"GuildID": str(guild_id)}) + if item: guild = bot.get_guild(guild_id) token = item.get("Token") b64_userid = token.split(".")[0]