From 8e0f2bd58bc68a3fd8da4171d53442dd6aa52415 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Sun, 26 Apr 2026 15:40:46 -0400 Subject: [PATCH 01/18] 238-Feature add version bump check workflow --- .github/workflows/version-check.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/version-check.yml diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml new file mode 100644 index 0000000..9368755 --- /dev/null +++ b/.github/workflows/version-check.yml @@ -0,0 +1,26 @@ +name: Version Check + +on: + pull_request: + branches: [main] + +jobs: + version-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Fetch main branch + run: git fetch origin main + + - name: Check version bump + run: | + PR_VERSION=$(grep -m1 '^version' pyproject.toml | sed 's/.*"\(.*\)"/\1/') + MAIN_VERSION=$(git show origin/main:pyproject.toml | grep -m1 '^version' | sed 's/.*"\(.*\)"/\1/') + echo "PR version: $PR_VERSION" + echo "Main version: $MAIN_VERSION" + if [ "$PR_VERSION" = "$MAIN_VERSION" ]; then + echo "::error::Version in pyproject.toml ($PR_VERSION) has not been bumped. Please update the version before merging to main." + exit 1 + fi + echo "Version bumped: $MAIN_VERSION -> $PR_VERSION" From f1c7eb49ac45797ba9ea5f6800e36253161b5593 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Mon, 27 Apr 2026 12:51:02 -0400 Subject: [PATCH 02/18] 190-Enhancement paginate /hacked-list with nav buttons and cleaner layout --- database/mongo.py | 2 +- features/security.py | 70 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/database/mongo.py b/database/mongo.py index 99c4f22..4b152a1 100644 --- a/database/mongo.py +++ b/database/mongo.py @@ -229,7 +229,7 @@ async def add_hacked_user(user_id: str, reason: str = "Compromised Account"): async def get_hacked_users(): """Retrieves all currently hacked users.""" cursor = db.hacked_users.find({"status": "hacked"}) - return await cursor.to_list(length=100) + return await cursor.to_list(length=None) async def remove_hacked_user(user_id: str): diff --git a/features/security.py b/features/security.py index bdfff11..75dc160 100644 --- a/features/security.py +++ b/features/security.py @@ -2,6 +2,7 @@ from discord import app_commands from discord.ext import commands from datetime import timedelta, datetime +from math import ceil import asyncio from database.mongo import add_hacked_user, get_hacked_users, remove_hacked_user @@ -229,24 +230,71 @@ async def hackedlist(self, interaction: discord.Interaction): ) return - embed = discord.Embed( - title="🚨 Hacked Users List", color=discord.Color.dark_red() + view = HackedListView(users, interaction.user) + await interaction.followup.send(embed=view.create_embed(), view=view) + + +class HackedListView(discord.ui.View): + def __init__(self, users: list, author: discord.User): + super().__init__(timeout=300) + self.users = sorted( + users, key=lambda u: u.get("timestamp", datetime.min), reverse=True ) - description_lines = [] - for u in users: + self.author = author + self.per_page = 10 + self.current_page = 0 + self.total_pages = ceil(len(users) / self.per_page) + self.update_buttons() + + def create_embed(self) -> discord.Embed: + start = self.current_page * self.per_page + end = start + self.per_page + page_users = self.users[start:end] + + entries = [] + for u in page_users: user_id = u["_id"] reason = u.get("reason", "No reason provided") time_str = u.get("timestamp", datetime.utcnow()).strftime("%Y-%m-%d") - description_lines.append( - f"• <@{user_id}> (`{user_id}`)\n Reason: *{reason}* ({time_str})" + entries.append( + f"<@{user_id}> (`{user_id}`)\nReason: *{reason}* ({time_str})" ) - full_text = "\n".join(description_lines) - if len(full_text) > 4000: - full_text = full_text[:3900] + "..." + embed = discord.Embed( + title="🚨 Hacked Users List", + description="\n\n".join(entries), + color=discord.Color.dark_red(), + ) + embed.set_footer(text=f"Page {self.current_page + 1}/{self.total_pages}") + return embed + + def update_buttons(self): + self.prev_button.disabled = self.current_page == 0 + self.next_button.disabled = self.current_page == self.total_pages - 1 - embed.description = full_text - await interaction.followup.send(embed=embed) + @discord.ui.button( + label="◀ Previous", style=discord.ButtonStyle.blurple, disabled=True + ) + async def prev_button( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id != self.author.id: + await interaction.response.defer() + return + self.current_page -= 1 + self.update_buttons() + await interaction.response.edit_message(embed=self.create_embed(), view=self) + + @discord.ui.button(label="Next ▶", style=discord.ButtonStyle.blurple) + async def next_button( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id != self.author.id: + await interaction.response.defer() + return + self.current_page += 1 + self.update_buttons() + await interaction.response.edit_message(embed=self.create_embed(), view=self) async def setup(bot): From 17ac2445b7f731a12b362ef516197c9ad7394746 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Tue, 12 May 2026 15:25:26 +1000 Subject: [PATCH 03/18] 254-Enhancement bump version to v1.10.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 88638e4..87599a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "remaining7-discord-bot" -version = "1.9.0" +version = "1.10.0" [tool.pytest.ini_options] asyncio_mode = "auto" From c02971609825a33b9d92b61b1f5ee6baa2fc1c9d Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Tue, 12 May 2026 15:31:51 +1000 Subject: [PATCH 04/18] 191-Enhancement update hacked message purge to last 1 hour instead of last 7 days --- features/security.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/features/security.py b/features/security.py index 75dc160..d68d0ba 100644 --- a/features/security.py +++ b/features/security.py @@ -27,9 +27,7 @@ async def has_security_permission(self, source): return False # --- CORE LOGIC: The shared hacked/purge process --- - async def _execute_hacked_action( - self, guild, target_user, moderator, days_to_clean=7 - ): + async def _execute_hacked_action(self, guild, target_user, moderator): """ Shared logic that performs the timeout, DB update, and message purge. """ @@ -69,7 +67,7 @@ async def _execute_hacked_action( print(f"Failed to DM hacked user {target_user.id}: {e}") # 5. Global Message Purge - cutoff_date = datetime.utcnow() - timedelta(days=days_to_clean) + cutoff_date = datetime.utcnow() - timedelta(hours=1) total_deleted = 0 channels_checked = 0 @@ -108,7 +106,7 @@ async def _execute_hacked_action( embed.add_field(name="Status", value=timeout_status, inline=False) embed.add_field( name="Cleanup Stats", - value=f"🗑️ Deleted **{total_deleted} messages** across **{channels_checked} channels** (Past {days_to_clean} days).", + value=f"🗑️ Deleted **{total_deleted} messages** across **{channels_checked} channels** (Past 1 hour).", inline=False, ) embed.add_field( @@ -131,14 +129,11 @@ async def _send_security_logs(self, embed): name="hacked", description="MOD/ADMIN: Flag user as hacked, timeout them, and delete messages.", ) - @app_commands.describe( - user="The hacked user", days_to_clean="Days of messages to delete (default 7)" - ) + @app_commands.describe(user="The hacked user") async def hacked_slash( self, interaction: discord.Interaction, user: discord.Member, - days_to_clean: int = 7, ): if not await self.has_security_permission(interaction): await interaction.response.send_message( @@ -148,7 +143,7 @@ async def hacked_slash( await interaction.response.defer() result_embed = await self._execute_hacked_action( - interaction.guild, user, interaction.user, days_to_clean + interaction.guild, user, interaction.user ) await interaction.followup.send(embed=result_embed) From 5ad7f37406dcf21ccce24a95b8429a8560c9d914 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Tue, 12 May 2026 15:55:08 +1000 Subject: [PATCH 05/18] 258-Bug fix !hacked triggering on messages with trailing text --- features/security.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/features/security.py b/features/security.py index d68d0ba..5a21da5 100644 --- a/features/security.py +++ b/features/security.py @@ -159,6 +159,9 @@ async def hacked_text(self, ctx): if not await self.has_security_permission(ctx): return + if ctx.message.content.strip() != "!hacked": + return + if not ctx.message.reference: await ctx.send("❌ Reply to a message with `!hacked` to flag that user.") return From 3888d6029204ed71cf28d51d3f1d45401b058b2e Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Tue, 12 May 2026 16:11:52 +1000 Subject: [PATCH 06/18] 260-Feature add PR issue reference check workflow --- .../workflows/pr-issue-reference-check.yml | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/pr-issue-reference-check.yml diff --git a/.github/workflows/pr-issue-reference-check.yml b/.github/workflows/pr-issue-reference-check.yml new file mode 100644 index 0000000..d477a78 --- /dev/null +++ b/.github/workflows/pr-issue-reference-check.yml @@ -0,0 +1,43 @@ +name: PR Issue Reference Check + +on: + pull_request: + branches: [dev] + types: [opened, edited, synchronize, reopened] + +jobs: + pr-issue-reference-check: + runs-on: ubuntu-latest + steps: + - name: Check issue number is consistent across branch, title, and body + env: + BRANCH: ${{ github.head_ref }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BODY: ${{ github.event.pull_request.body }} + run: | + # Extract leading issue number from branch name + if [[ "$BRANCH" =~ ^([0-9]+)- ]]; then + ISSUE_NUM="${BASH_REMATCH[1]}" + echo "Issue number: $ISSUE_NUM" + else + echo "ERROR: Branch name '$BRANCH' does not start with an issue number (e.g. 258-Bug)." + exit 1 + fi + + # Assert PR body contains "Closes #" (case-insensitive) + if echo "$PR_BODY" | grep -iqE "closes #${ISSUE_NUM}([^0-9]|$)"; then + echo "PR body contains 'Closes #${ISSUE_NUM}'. OK." + else + echo "ERROR: PR body must contain 'Closes #${ISSUE_NUM}'." + exit 1 + fi + + # Assert PR title references the issue number as a whole word + if echo "$PR_TITLE" | grep -qwE "${ISSUE_NUM}"; then + echo "PR title references issue #${ISSUE_NUM}. OK." + else + echo "ERROR: PR title must reference issue number ${ISSUE_NUM}." + exit 1 + fi + + echo "All issue reference checks passed." From c82a529b35ca094ea14c07fc9be52291dff1615d Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Tue, 12 May 2026 16:18:21 +1000 Subject: [PATCH 07/18] 250-Feature add MIT License --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..79e7b08 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Shiven Ajwaliya + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From fca0206cfa7eee3733acb1bd7b20da7df4160009 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Tue, 12 May 2026 16:24:33 +1000 Subject: [PATCH 08/18] 192-Enhancement apply 60s slow mode to general on tourney start/end --- features/tourney/tourney_commands.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/features/tourney/tourney_commands.py b/features/tourney/tourney_commands.py index 7631395..309f5de 100644 --- a/features/tourney/tourney_commands.py +++ b/features/tourney/tourney_commands.py @@ -1508,6 +1508,14 @@ async def start_tourney_command(ctx: commands.Context, region: str = None): } await dashboard_cog.start_dashboard() + # Apply 60s slow mode to general channel during tourney. + general_channel = guild.get_channel(GENERAL_CHANNEL_ID) + if isinstance(general_channel, discord.TextChannel): + try: + await general_channel.edit(slowmode_delay=60) + except Exception as e: + print(f"Failed to set slow mode on general channel: {e}") + @bot.command(name="endtourney") async def end_tourney_command(ctx: commands.Context): """ @@ -1691,6 +1699,14 @@ async def _retry_winner_post(): await unlock_command(ctx) + # Remove slow mode from general channel now that tourney is over. + general_channel = guild.get_channel(GENERAL_CHANNEL_ID) + if isinstance(general_channel, discord.TextChannel): + try: + await general_channel.edit(slowmode_delay=0) + except Exception as e: + print(f"Failed to remove slow mode on general channel: {e}") + from features.config import SPANISH_CHANNEL_ID guild = ctx.guild From f370ee1c0a8a4784ed1e67cbe81cf7617833cf90 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Tue, 12 May 2026 21:51:00 +1000 Subject: [PATCH 09/18] 192-Enhancement send confirmation messages of slowmode --- features/tourney/tourney_commands.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/features/tourney/tourney_commands.py b/features/tourney/tourney_commands.py index 309f5de..0ea56fb 100644 --- a/features/tourney/tourney_commands.py +++ b/features/tourney/tourney_commands.py @@ -1513,6 +1513,7 @@ async def start_tourney_command(ctx: commands.Context, region: str = None): if isinstance(general_channel, discord.TextChannel): try: await general_channel.edit(slowmode_delay=60) + await ctx.send(f"🐢 Slow mode (60s) has been enabled in {general_channel.mention}.") except Exception as e: print(f"Failed to set slow mode on general channel: {e}") @@ -1704,6 +1705,7 @@ async def _retry_winner_post(): if isinstance(general_channel, discord.TextChannel): try: await general_channel.edit(slowmode_delay=0) + await ctx.send(f"🐇 Slow mode has been removed from {general_channel.mention}.") except Exception as e: print(f"Failed to remove slow mode on general channel: {e}") From 325aed066afa991d05cf334035053a8b4512b137 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Tue, 12 May 2026 21:52:24 +1000 Subject: [PATCH 10/18] 192-Enhancement fix linter errors --- features/tourney/tourney_commands.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/features/tourney/tourney_commands.py b/features/tourney/tourney_commands.py index 0ea56fb..fc4762c 100644 --- a/features/tourney/tourney_commands.py +++ b/features/tourney/tourney_commands.py @@ -1513,7 +1513,9 @@ async def start_tourney_command(ctx: commands.Context, region: str = None): if isinstance(general_channel, discord.TextChannel): try: await general_channel.edit(slowmode_delay=60) - await ctx.send(f"🐢 Slow mode (60s) has been enabled in {general_channel.mention}.") + await ctx.send( + f"🐢 Slow mode (60s) has been enabled in {general_channel.mention}." + ) except Exception as e: print(f"Failed to set slow mode on general channel: {e}") @@ -1705,7 +1707,9 @@ async def _retry_winner_post(): if isinstance(general_channel, discord.TextChannel): try: await general_channel.edit(slowmode_delay=0) - await ctx.send(f"🐇 Slow mode has been removed from {general_channel.mention}.") + await ctx.send( + f"🐇 Slow mode has been removed from {general_channel.mention}." + ) except Exception as e: print(f"Failed to remove slow mode on general channel: {e}") From 62018c09cd32d241afce4ff9b3e1d031c98627c3 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Tue, 12 May 2026 22:15:09 +1000 Subject: [PATCH 11/18] 195-Enhancement include item price and balance before/after in redeem ticket embed --- features/economy.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/features/economy.py b/features/economy.py index 2eb8518..c160b0f 100644 --- a/features/economy.py +++ b/features/economy.py @@ -883,12 +883,14 @@ async def redeem(self, interaction: discord.Interaction, item: str): await interaction.response.send_message(embed=embed, ephemeral=True) return + await interaction.response.defer() + try: if ( not isinstance(REDEMPTION_TICKET_CATEGORY_ID, int) or REDEMPTION_TICKET_CATEGORY_ID <= 0 ): - await interaction.response.send_message( + await interaction.followup.send( "❌ Redemption category is not configured.", ephemeral=True, ) @@ -896,12 +898,14 @@ async def redeem(self, interaction: discord.Interaction, item: str): category = interaction.guild.get_channel(REDEMPTION_TICKET_CATEGORY_ID) if not isinstance(category, discord.CategoryChannel): - await interaction.response.send_message( + await interaction.followup.send( "❌ Configured redemption category channel was not found.", ephemeral=True, ) return + balance_before = await get_user_balance(user_id) + await remove_item_token(user_id, item) tracking_keys = { @@ -964,17 +968,23 @@ async def redeem(self, interaction: discord.Interaction, item: str): description=f"A ticket has been created in {ch.mention}.\nPlease provide the following details to redeem your **{item_info['display']}**:\n{instructions}", color=discord.Color.green(), ) - await interaction.response.send_message(embed=embed) + await interaction.followup.send(embed=embed) + item_price = item_info["price"] + balance_after = balance_before + balance_display_before = balance_before + item_price ticket_embed = discord.Embed( title=f"🎫 **{item.title()} Redemption Ticket**", description=f"{interaction.user.mention}, please provide the following details in this ticket channel:\n\n{instructions}", color=discord.Color.blue(), ) + ticket_embed.add_field(name="Item Price", value=f"{item_price:,} R7 tokens", inline=True) + ticket_embed.add_field(name="Balance Before", value=f"{balance_display_before:,} R7 tokens", inline=True) + ticket_embed.add_field(name="Balance After", value=f"{balance_after:,} R7 tokens", inline=True) await ch.send(embed=ticket_embed) except Exception as e: await add_item_token(user_id, item, quantity=1) - await interaction.response.send_message( + await interaction.followup.send( f"❌ **Error** Failed to create ticket: {e}", ephemeral=True ) From f6df726b581e6caf94759b6dd4d83814a601e577 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Tue, 12 May 2026 22:17:22 +1000 Subject: [PATCH 12/18] 195-Enhancement fix linter errors --- features/economy.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/features/economy.py b/features/economy.py index c160b0f..2a3dd7d 100644 --- a/features/economy.py +++ b/features/economy.py @@ -977,9 +977,17 @@ async def redeem(self, interaction: discord.Interaction, item: str): description=f"{interaction.user.mention}, please provide the following details in this ticket channel:\n\n{instructions}", color=discord.Color.blue(), ) - ticket_embed.add_field(name="Item Price", value=f"{item_price:,} R7 tokens", inline=True) - ticket_embed.add_field(name="Balance Before", value=f"{balance_display_before:,} R7 tokens", inline=True) - ticket_embed.add_field(name="Balance After", value=f"{balance_after:,} R7 tokens", inline=True) + ticket_embed.add_field( + name="Item Price", value=f"{item_price:,} R7 tokens", inline=True + ) + ticket_embed.add_field( + name="Balance Before", + value=f"{balance_display_before:,} R7 tokens", + inline=True, + ) + ticket_embed.add_field( + name="Balance After", value=f"{balance_after:,} R7 tokens", inline=True + ) await ch.send(embed=ticket_embed) except Exception as e: From 5c658843d163b4ae42ff3243f52fd703db346e85 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Thu, 14 May 2026 22:54:30 +1000 Subject: [PATCH 13/18] 194-Enhancement add admin role rename on tourney start and restore on end --- features/tourney/tourney_commands.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/features/tourney/tourney_commands.py b/features/tourney/tourney_commands.py index fc4762c..c685ca1 100644 --- a/features/tourney/tourney_commands.py +++ b/features/tourney/tourney_commands.py @@ -1161,6 +1161,7 @@ async def blacklist_list(self, interaction: discord.Interaction): def setup_tourney_commands(bot: commands.Bot): sticky_redirect_state = {"enabled": False, "region": None} + admin_role_original_name: list[str | None] = [None] # mutable container for closure @bot.command(name="close", aliases=["c"]) async def close_command(ctx: commands.Context): @@ -1486,6 +1487,19 @@ async def start_tourney_command(ctx: commands.Context, region: str = None): except Exception as e: print(f"Failed to grant timeout permission to Tourney Admin role: {e}") + # Rename Admin role to indicate it is not a Tourney Admin. + admin_role = guild.get_role(ADMIN_ROLE_ID) + if admin_role: + admin_role_original_name[0] = admin_role.name + + async def _rename_admin_role(): + try: + await admin_role.edit(name="[NOT TOURNEY ADMIN] Admin") + except Exception as e: + print(f"Failed to rename Admin role: {e}") + + asyncio.create_task(_rename_admin_role()) + # START THE DASHBOARD dashboard_cog = bot.get_cog("QueueDashboard") if dashboard_cog: @@ -1626,6 +1640,19 @@ async def _retry_winner_post(): f"Failed to revoke timeout permission from Tourney Admin role: {e}" ) + # Restore Admin role name. + admin_role = guild.get_role(ADMIN_ROLE_ID) + if admin_role: + original_name = admin_role_original_name[0] or "Admin" + + async def _restore_admin_role(): + try: + await admin_role.edit(name=original_name) + except Exception as e: + print(f"Failed to restore Admin role name: {e}") + + asyncio.create_task(_restore_admin_role()) + session = await get_active_tourney_session() if session: # 1. Calculate Duration From ace024d9424fa460695399f3a6c56738fe8f312e Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Thu, 14 May 2026 23:05:17 +1000 Subject: [PATCH 14/18] 194-Enhancement add confirmation messages for admin role rename --- features/tourney/tourney_commands.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/features/tourney/tourney_commands.py b/features/tourney/tourney_commands.py index c685ca1..ebfd4f6 100644 --- a/features/tourney/tourney_commands.py +++ b/features/tourney/tourney_commands.py @@ -1495,6 +1495,9 @@ async def start_tourney_command(ctx: commands.Context, region: str = None): async def _rename_admin_role(): try: await admin_role.edit(name="[NOT TOURNEY ADMIN] Admin") + await ctx.send( + f"✏️ **{admin_role_original_name[0]}** has been renamed to **[NOT TOURNEY ADMIN] Admin**." + ) except Exception as e: print(f"Failed to rename Admin role: {e}") @@ -1640,19 +1643,6 @@ async def _retry_winner_post(): f"Failed to revoke timeout permission from Tourney Admin role: {e}" ) - # Restore Admin role name. - admin_role = guild.get_role(ADMIN_ROLE_ID) - if admin_role: - original_name = admin_role_original_name[0] or "Admin" - - async def _restore_admin_role(): - try: - await admin_role.edit(name=original_name) - except Exception as e: - print(f"Failed to restore Admin role name: {e}") - - asyncio.create_task(_restore_admin_role()) - session = await get_active_tourney_session() if session: # 1. Calculate Duration @@ -1729,6 +1719,18 @@ async def _restore_admin_role(): await unlock_command(ctx) + # Restore Admin role name. + admin_role = guild.get_role(ADMIN_ROLE_ID) + if admin_role: + original_name = admin_role_original_name[0] or "Admin" + try: + await admin_role.edit(name=original_name) + await ctx.send( + f"✏️ Admin role has been restored to **{original_name}**." + ) + except Exception as e: + print(f"Failed to restore Admin role name: {e}") + # Remove slow mode from general channel now that tourney is over. general_channel = guild.get_channel(GENERAL_CHANNEL_ID) if isinstance(general_channel, discord.TextChannel): From b00269545350f257c076c13509f98b94ea087c8d Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Fri, 15 May 2026 09:24:22 +1000 Subject: [PATCH 15/18] 269-Enhancement update server booster token gain from 2% to 5% --- features/economy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/economy.py b/features/economy.py index 2a3dd7d..a9b6482 100644 --- a/features/economy.py +++ b/features/economy.py @@ -682,12 +682,12 @@ async def on_message(self, message: discord.Message): if should_award_tokens: earned_tokens = random.randint(2, 5) - # Booster Bonus: 7% Chance (Avg 2% increase) + # Booster Bonus: 17.5% Chance (Avg 5% increase) SERVER_BOOSTER_ROLE_ID = 647685778255642626 if message.guild: booster_role = message.guild.get_role(SERVER_BOOSTER_ROLE_ID) if booster_role and booster_role in message.author.roles: - if random.random() < 0.07: + if random.random() < 0.175: earned_tokens += 1 current_balance = await get_user_balance(user_id) @@ -1342,7 +1342,7 @@ async def economy_help(self, interaction: discord.Interaction): "*Requires 5 messages sent since your last `/daily` claim.*\n" f"🪂 **Supply Drops:** Random crates appear in {general_ch}! Click the button to claim.\n" f"🏆 **Events:** Earn massive token rewards in {event_ch}.\n" - "🚀 **Booster Bonus:** Server Boosters receive a **2% increase** in coins on average." + "🚀 **Booster Bonus:** Server Boosters receive a **5% increase** in coins on average." ) earn_embed.add_field(name="📈 Earning Methods", value=earn_text, inline=False) earn_embed.set_thumbnail(url=self.bot.user.display_avatar.url) From d19bc9518c9379c25d9f89c7b631155e61293795 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Thu, 21 May 2026 10:16:23 +1000 Subject: [PATCH 16/18] 257-Enhancement skip token rewards for messages in BOTS category channel --- features/config.py | 2 ++ features/economy.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/features/config.py b/features/config.py index 52a62e5..c947357 100644 --- a/features/config.py +++ b/features/config.py @@ -36,6 +36,7 @@ SUPPORT_STAFF_APPS_CATEGORY_ID = 1330243733177897030 SUPPORT_PARTNERSHIP_CATEGORY_ID = 1330243884911169657 REDEMPTION_TICKET_CATEGORY_ID = 1481456156080738346 + BOTS_CATEGORY_ID = 767612682357964810 SUPPORT_TRANSCRIPT_LOG_CHANNEL_ID = 1481117793222004746 SUPPORT_STAFF_APPS_INFO_CHANNEL_ID = 1261020578790248459 @@ -212,6 +213,7 @@ SUPPORT_STAFF_APPS_CATEGORY_ID = 1480728684704043098 SUPPORT_PARTNERSHIP_CATEGORY_ID = 1480728776919879721 REDEMPTION_TICKET_CATEGORY_ID = 1481455542236086293 + BOTS_CATEGORY_ID = 1506812222704451677 SUPPORT_TRANSCRIPT_LOG_CHANNEL_ID = 1480735066924781711 SUPPORT_STAFF_APPS_INFO_CHANNEL_ID = 1481083131342618754 diff --git a/features/economy.py b/features/economy.py index a9b6482..ce22784 100644 --- a/features/economy.py +++ b/features/economy.py @@ -27,6 +27,7 @@ # --- CONFIGURATION --- from features.config import ( ADMIN_ROLE_ID, + BOTS_CATEGORY_ID, GENERAL_CHANNEL_ID, EVENT_ANNOUNCEMENTS_CHANNEL_ID, SHOP_DATA, @@ -640,6 +641,10 @@ async def on_message(self, message: discord.Message): if message.content.startswith("!"): return + # Skip token rewards for messages in the 'BOTS' category + if message.channel.category and message.channel.category.id == BOTS_CATEGORY_ID: + return + user_id = str(message.author.id) current_timestamp = time.time() datetime.utcnow().strftime("%Y-%m-%d") From 6268fe925d1aedbe25404fb6f171bba0a499e511 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Thu, 21 May 2026 10:34:37 +1000 Subject: [PATCH 17/18] 191-Enhancement-12hr update message delete to the last 12 hours --- features/security.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/security.py b/features/security.py index 5a21da5..f74c215 100644 --- a/features/security.py +++ b/features/security.py @@ -67,7 +67,7 @@ async def _execute_hacked_action(self, guild, target_user, moderator): print(f"Failed to DM hacked user {target_user.id}: {e}") # 5. Global Message Purge - cutoff_date = datetime.utcnow() - timedelta(hours=1) + cutoff_date = datetime.utcnow() - timedelta(hours=12) total_deleted = 0 channels_checked = 0 @@ -106,7 +106,7 @@ async def _execute_hacked_action(self, guild, target_user, moderator): embed.add_field(name="Status", value=timeout_status, inline=False) embed.add_field( name="Cleanup Stats", - value=f"🗑️ Deleted **{total_deleted} messages** across **{channels_checked} channels** (Past 1 hour).", + value=f"🗑️ Deleted **{total_deleted} messages** across **{channels_checked} channels** (Past 12 hours).", inline=False, ) embed.add_field( From 8cf562570bcd8554c544aa2eaef2d6087cdbee02 Mon Sep 17 00:00:00 2001 From: Shiven Ajwaliya Date: Thu, 21 May 2026 12:14:51 +1000 Subject: [PATCH 18/18] 274-Enhancement bump down bot version to v1.9.1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 87599a8..e3dbc45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "remaining7-discord-bot" -version = "1.10.0" +version = "1.9.1" [tool.pytest.ini_options] asyncio_mode = "auto"