From ad133b60382f4ab252002df4e95451e3fbf36bea Mon Sep 17 00:00:00 2001 From: ar-cyber Date: Fri, 12 Jun 2026 08:39:35 +0930 Subject: [PATCH 1/7] remove useless whitelabel part --- erm.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erm.py b/erm.py index 4eb5bb8..fb3adfb 100644 --- a/erm.py +++ b/erm.py @@ -310,9 +310,6 @@ async def setup_hook(self) -> None: -if config("ENVIRONMENT") == "CUSTOM": - Bot.__bases__ = (commands.Bot,) - bot = Bot( command_prefix=get_prefix, case_insensitive=True, From c1e6ca346e05c1b53ea1e574050260029dfff40e Mon Sep 17 00:00:00 2001 From: ar-cyber Date: Fri, 12 Jun 2026 08:44:21 +0930 Subject: [PATCH 2/7] remove tons of whitelabel componentry from erm.py --- erm.py | 68 +--------------------------------------------------------- 1 file changed, 1 insertion(+), 67 deletions(-) diff --git a/erm.py b/erm.py index fb3adfb..9552b64 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", "") @@ -339,38 +339,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: @@ -405,41 +374,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] From 13f1d48529c1c74ea63ac3f9662c6155826c1239 Mon Sep 17 00:00:00 2001 From: ar-cyber Date: Fri, 12 Jun 2026 08:51:39 +0930 Subject: [PATCH 3/7] remove a fuck ton of whitelabel references --- .env.template | 3 +-- AGENTS.md | 1 - cogs/Search.py | 10 ++++------ documentation/architecture.md | 2 -- erm.py | 16 +--------------- events/on_message.py | 2 -- tasks/check_reminders.py | 7 ------- tasks/iterate_ics.py | 2 +- tasks/iterate_prc_logs.py | 25 +++---------------------- tasks/sync_weather.py | 7 ++----- utils/api.py | 3 +-- 11 files changed, 13 insertions(+), 65 deletions(-) diff --git a/.env.template b/.env.template index d6b08e0..2b5b909 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 0eee776..b5f3fd7 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 ea38109..f8391a8 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 96d91ef..4ad1531 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 9552b64..88a7276 100644 --- a/erm.py +++ b/erm.py @@ -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": [], diff --git a/events/on_message.py b/events/on_message.py index e10a171..1036795 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 1cc842a..73efb46 100644 --- a/tasks/check_reminders.py +++ b/tasks/check_reminders.py @@ -145,13 +145,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/iterate_ics.py b/tasks/iterate_ics.py index 6342dc9..aa0ac1a 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 7f1f7c8..5e12ca9 100644 --- a/tasks/iterate_prc_logs.py +++ b/tasks/iterate_prc_logs.py @@ -45,8 +45,8 @@ count_aggregate = global_aggregate + [{"$count": "total"}] - -async def iterate_prc_logs_global(bot): +@tasks.loop(minutes=7, 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 = server_count_list[0]["total"] if server_count_list else 0 @@ -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"), @@ -220,15 +208,8 @@ async def process_guild(bot, items, semaphore): except Exception as e: logging.warning(f"error processing guild: {e}") + await iterate_prc_logs(bot) -@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): diff --git a/tasks/sync_weather.py b/tasks/sync_weather.py index 05e8389..aa2c924 100644 --- a/tasks/sync_weather.py +++ b/tasks/sync_weather.py @@ -84,7 +84,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": [ @@ -93,7 +92,7 @@ async def sync_weather(bot): ] } }, - }["CUSTOM" if config("ENVIRONMENT") == "CUSTOM" else "_"] + } try: logging.info("Starting weather sync task...") @@ -143,9 +142,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 2e9eefa..2a67433 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( From dc637b2247d1f0d8dc393b3ecb4baf6934ca04c4 Mon Sep 17 00:00:00 2001 From: ar-cyber Date: Fri, 12 Jun 2026 09:00:09 +0930 Subject: [PATCH 4/7] more removal and fix on has_whitelabel --- tasks/check_reminders.py | 3 +-- tasks/sync_weather.py | 11 ----------- utils/utils.py | 3 ++- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/tasks/check_reminders.py b/tasks/check_reminders.py index 73efb46..42a92c2 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: diff --git a/tasks/sync_weather.py b/tasks/sync_weather.py index aa2c924..5e59e69 100644 --- a/tasks/sync_weather.py +++ b/tasks/sync_weather.py @@ -83,16 +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 = { - "_": { - "_id": { - "$nin": [ - int(item["GuildID"] or 0) - async for item in bot.whitelabel.db.find({}) - ] - } - }, - } try: logging.info("Starting weather sync task...") @@ -106,7 +96,6 @@ async def sync_weather(bot): {"ERLC.weather.sync_weather": True}, ], "ERLC.weather.location": {"$exists": True, "$ne": ""}, - **chosen_filter, } }, { diff --git a/utils/utils.py b/utils/utils.py index 5381a7a..517c84c 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] From 1cdb083187b55e3068694f64730079e3aed80f57 Mon Sep 17 00:00:00 2001 From: ar-cyber Date: Fri, 12 Jun 2026 21:37:20 +0930 Subject: [PATCH 5/7] Check whitelabel task --- tasks/check_whitelabel.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tasks/check_whitelabel.py diff --git a/tasks/check_whitelabel.py b/tasks/check_whitelabel.py new file mode 100644 index 0000000..2def9e0 --- /dev/null +++ b/tasks/check_whitelabel.py @@ -0,0 +1,35 @@ +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: + 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 + time = datetime.datetime.now(tz=datetime.UTC) + expiry = datetime.datetime.fromtimestamp(item["Expiry"]) + 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.post(item["UserData"]["AvatarURL"])).read() + banner = await (await session.post(item["UserData"]["BannerURL"])).read() + await bot_member.edit(avatar=av, banner=banner, bio=item["UserData"]["Bio"]) + From eb1f2eaf9042f7df4e2ed8cfdfe71bcfe8bf397a Mon Sep 17 00:00:00 2001 From: Andrew R Date: Sun, 21 Jun 2026 21:49:27 +0930 Subject: [PATCH 6/7] fix(tasks.check_whitelabel): fix task issues --- tasks/check_whitelabel.py | 54 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/tasks/check_whitelabel.py b/tasks/check_whitelabel.py index 2def9e0..1d2f358 100644 --- a/tasks/check_whitelabel.py +++ b/tasks/check_whitelabel.py @@ -10,26 +10,36 @@ async def check_whitelabel(bot: Bot): async for item in bot.whitelabel.db.find({}): 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 - time = datetime.datetime.now(tz=datetime.UTC) - expiry = datetime.datetime.fromtimestamp(item["Expiry"]) - 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.post(item["UserData"]["AvatarURL"])).read() - banner = await (await session.post(item["UserData"]["BannerURL"])).read() - await bot_member.edit(avatar=av, banner=banner, bio=item["UserData"]["Bio"]) + 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 From 38eeb8a3db6177ff671d4bfdf94bc9783e5aabf1 Mon Sep 17 00:00:00 2001 From: Andrew R Date: Sun, 21 Jun 2026 21:50:46 +0930 Subject: [PATCH 7/7] fix(iterate_prc_logs): fix a ratelimit issue caused by recursion --- tasks/iterate_prc_logs.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/tasks/iterate_prc_logs.py b/tasks/iterate_prc_logs.py index 5e12ca9..7efd36b 100644 --- a/tasks/iterate_prc_logs.py +++ b/tasks/iterate_prc_logs.py @@ -45,10 +45,10 @@ count_aggregate = global_aggregate + [{"$count": "total"}] -@tasks.loop(minutes=7, reconnect=True) +@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") @@ -197,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: @@ -208,7 +209,6 @@ async def process_guild(bot, items, semaphore): except Exception as e: logging.warning(f"error processing guild: {e}") - await iterate_prc_logs(bot) @@ -516,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