From 3e6410d11276f84c60d96fd760ac5f128d533c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20L=27abb=C3=A9?= Date: Sun, 31 May 2026 12:56:55 +0000 Subject: [PATCH] Refactor project structure and update README; remove unused dependencies from requirements.txt and enhance database management in sql_commands.py. --- README.md | 48 +++++++++------ cogs/economy.py | 132 ++++++++++++++++++++---------------------- requirements.txt | 2 - utils/sql_commands.py | 31 +++++----- 4 files changed, 107 insertions(+), 106 deletions(-) diff --git a/README.md b/README.md index cb75f84..22aecb2 100755 --- a/README.md +++ b/README.md @@ -56,25 +56,37 @@ Here’s an outline of the directory and file structure for this project: ``` discord-bot/ │ -├── bot.py # Main bot file for initializing the bot and loading cogs -├── requirements.txt # List of required Python packages -├── .env # Environment file containing sensitive data (e.g., bot token) +├── bot.py +├── bot_development.py +├── main.py +├── dockerfile +├── requirements.txt +├── README.md +├── Fixes.md │ -├── cogs/ # Folder containing individual cogs for separate functionalities -│ ├── admin.py # Admin commands such as ban, kick, etc. -│ ├── economy.py # Economy-related commands -│ ├── fun.py # Fun commands for user engagement -│ └── roles.py # Commands for managing server roles +├── cogs/ +│ ├── admin.py +│ ├── customCommands.py +│ ├── economy.py +│ ├── gamble.py +│ ├── informational.py +│ ├── listeners.py +│ ├── mail.py +│ ├── npc.py +│ ├── xp.py │ -├── utils/ # Folder containing utility files for common functionality -│ ├── bank_functions.py # Economy and balance management functions -│ └── sql_Commands.py # Database helper functions for handling SQL commands +├── utils/ +│ ├── bank_functions.py +│ ├── npc_data.py +│ ├── npc_handler.py +│ ├── npc_memory.py +│ ├── sql_commands.py │ -├── extras/ # Experimental or additional features (optional) -│ └── example.py -│ -└── docs/ # Documentation files - └── CheckList.txt # Planning and improvement list (optional) +├── web/ +│ ├── __init__.py +│ ├── app.py +│ ├── static/ +│ ├── templates/ ``` --- @@ -117,7 +129,7 @@ Make sure your bot has the appropriate permissions for each command. You can man Store sensitive information like your bot token in the `.env` file. Avoid committing this file to version control. ### Database -The bot currently uses SQLite for user and balance data. You can update `sql_Commands.py` and `bank_functions.py` in the `utils` folder to manage other data or switch to another database if necessary. +The bot currently uses MySQL for user and balance data through `utils/sql_commands.py` and `utils/bank_functions.py`. You can update these modules if you want to change the database engine or schema. --- @@ -136,7 +148,7 @@ Make sure to include docstrings for any new functions and comments for complex l ## License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. +No license file is included in this repository. Add a `LICENSE` file if you want to make the license explicit. --- diff --git a/cogs/economy.py b/cogs/economy.py index 3468f16..1e7be87 100755 --- a/cogs/economy.py +++ b/cogs/economy.py @@ -1,49 +1,39 @@ import logging import discord from discord.ext import commands -from utils.bank_functions import * -from discord.ext import commands -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from random import randint from typing import Literal -import datetime + +from utils.bank_functions import ( + bank_data, + create_account, + reset_bank, + update_daily_timestamp, + update_money, +) +from utils.sql_commands import DatabaseManager -async def check_transfer( - ctx: commands.Context, - payer_balance: int, - receiver_balance: int, - member: discord.Member, - amount: int, -) -> bool: - """Check if a transfer is valid. +def validate_transfer( + payer_balance: int, author_id: int, receiver_id: int, amount: int +) -> tuple[bool, str]: + if amount <= 0: + return False, "Please enter an amount greater than 0." - Args: - - ctx (commands.Context): The context of the invoked command. - - payer_balance (int): The balance of the payer. - - receiver_balance (int): The balance of the receiver. - - member (discord.Member): The member to check against. - - amount (int): The amount to transfer. + if payer_balance is None: + return False, "Your account does not exist." - Returns: - - bool: Whether the transfer is valid. - """ - if payer_balance is None or receiver_balance is None: - await ctx.reply("Bank account doesn't exist.") - return False - if payer_balance >= amount > 0: - payer_balance = payer_balance - amount - receiver_balance = receiver_balance + amount - if ctx.author.id != member.id: - return True - else: - await ctx.reply("You cannot give yourself money.") - return False - else: - await ctx.reply( - f"You do not have {int(amount):,}<:flooney:1194943899765051473>.\nYou have {int(payer_balance):,}<:flooney:1194943899765051473>." + if author_id == receiver_id: + return False, "You cannot give yourself money." + + if payer_balance < amount: + return False, ( + f"You do not have {amount:,}<:flooney:1194943899765051473>. " + f"You have {payer_balance:,}<:flooney:1194943899765051473>." ) - return False + + return True, "" class Economy(commands.Cog): @@ -213,13 +203,18 @@ class Economy(commands.Cog): payer_wallet = int((await bank_data(ctx.author)).get("WALLET", 0)) receiver_wallet = int((await bank_data(target)).get("WALLET", 0)) - if await check_transfer(ctx, payer_wallet, receiver_wallet, target, amount): - await update_money(target, wallet=amount) - await update_money(ctx.author, wallet=-amount) + valid, reason = validate_transfer( + payer_wallet, ctx.author.id, target.id, amount + ) + if not valid: + return await ctx.reply(reason, mention_author=False) - await ctx.reply( - f"You gave {target} {amount:,} flooneys.", mention_author=False - ) + await update_money(target, wallet=amount) + await update_money(ctx.author, wallet=-amount) + + await ctx.reply( + f"You gave {target} {amount:,} flooneys.", mention_author=False + ) @commands.command(name="transfer") async def _transfer(self, ctx, target: discord.Member, amount: int): @@ -229,13 +224,18 @@ class Economy(commands.Cog): payer_bank = int((await bank_data(ctx.author)).get("BANK", 0)) receiver_bank = int((await bank_data(target)).get("BANK", 0)) - if await check_transfer(ctx, payer_bank, receiver_bank, target, amount): - await update_money(target, bank=amount) - await update_money(ctx.author, bank=-amount) + valid, reason = validate_transfer( + payer_bank, ctx.author.id, target.id, amount + ) + if not valid: + return await ctx.reply(reason, mention_author=False) - await ctx.reply( - f"Transferred {amount:,} flooneys to {target}.", mention_author=False - ) + await update_money(target, bank=amount) + await update_money(ctx.author, bank=-amount) + + await ctx.reply( + f"Transferred {amount:,} flooneys to {target}.", mention_author=False + ) @commands.command( name="deposit", @@ -248,7 +248,7 @@ class Economy(commands.Cog): user_data = await bank_data(ctx.author) if user_data is None: - await create_account(ctx) + await create_account(ctx.author) user_data = await bank_data(ctx.author) wallet_balance = int(user_data.get("WALLET", 0)) @@ -268,7 +268,7 @@ class Economy(commands.Cog): user_data = await bank_data(ctx.author) if user_data is None: - await create_account(ctx) + await create_account(ctx.author) user_data = await bank_data(ctx.author) bank_balance = int(user_data.get("BANK", 0)) @@ -328,7 +328,7 @@ class Economy(commands.Cog): title=f"Top {len(leaderboard_entries)} Richest Users - Leaderboard", description="\n".join(leaderboard_entries), color=discord.Color(0x00FF00), - timestamp=datetime.datetime.utcnow(), + timestamp=datetime.utcnow(), ) embed.set_footer(text=f"GLOBAL - {ctx.guild.name}") await ctx.reply(embed=embed, mention_author=False) @@ -353,36 +353,32 @@ class Economy(commands.Cog): try: user_data = await bank_data(ctx.author) last_claim = user_data.get("DAILY") - now = discord.utils.utcnow() # Modern, timezone-aware + now = discord.utils.utcnow() - # Convert last_claim to datetime if it's a timestamp (int or float) if last_claim: if isinstance(last_claim, (int, float)): - last_claim_dt = datetime.datetime.fromtimestamp( - last_claim, tz=datetime.timezone.utc - ) + last_claim_dt = datetime.fromtimestamp(last_claim, tz=timezone.utc) elif isinstance(last_claim, str): try: - last_claim_dt = datetime.datetime.fromtimestamp( - float(last_claim), tz=datetime.timezone.utc + last_claim_dt = datetime.fromtimestamp( + float(last_claim), tz=timezone.utc ) - except Exception: + except ValueError: last_claim_dt = None - elif isinstance(last_claim, datetime.datetime): - # If it's naive, make it aware - if last_claim.tzinfo is None: - last_claim_dt = last_claim.replace(tzinfo=datetime.timezone.utc) - else: - last_claim_dt = last_claim + elif isinstance(last_claim, datetime): + last_claim_dt = ( + last_claim.replace(tzinfo=timezone.utc) + if last_claim.tzinfo is None + else last_claim + ) else: last_claim_dt = None else: last_claim_dt = None - if not last_claim_dt or (now - last_claim_dt).total_seconds() >= 86400: + if not last_claim_dt or (now - last_claim_dt) >= timedelta(days=1): daily_reward = randint(200, 1000) - await update_money(ctx.author, daily_reward) - # Save the new timestamp as a float (UNIX time) + await update_money(ctx.author, wallet=daily_reward) await update_daily_timestamp(ctx.author, now) await ctx.reply( f"Your daily pocket money is {daily_reward:,}<:flooney:1194943899765051473>", diff --git a/requirements.txt b/requirements.txt index 453ec5b..aa56b62 100755 --- a/requirements.txt +++ b/requirements.txt @@ -16,8 +16,6 @@ Jinja2==3.1.6 MarkupSafe==3.0.3 multidict==6.1.0 mysql-connector==2.2.9 -numpy==2.1.3 -pandas==2.2.3 pillow==12.1.1 propcache==0.2.0 python-dateutil==2.9.0.post0 diff --git a/utils/sql_commands.py b/utils/sql_commands.py index 6aa6d82..988f64d 100755 --- a/utils/sql_commands.py +++ b/utils/sql_commands.py @@ -7,7 +7,6 @@ import re import time from datetime import datetime, timedelta import logging -import pandas as pd # Configure logging @@ -45,8 +44,9 @@ class DatabaseManager: "user": os.getenv("SQLUSER", "root"), "password": os.getenv("SQLPASS", ""), "database": os.getenv("SQLDB", "testdb"), - "pool_reset_session": os.getenv("POOL_RESET_SESSION", "false").lower() - in ("true", "1", "yes"), + "pool_reset_session": self._parse_bool( + os.getenv("POOL_RESET_SESSION", "false") + ), } self.pool = pooling.MySQLConnectionPool( @@ -234,6 +234,10 @@ class DatabaseManager: raise ValueError(f"Invalid SQL identifier: {identifier}") return identifier + @staticmethod + def _parse_bool(value: str) -> bool: + return str(value).strip().lower() in {"true", "1", "yes", "y"} + def _parse_insert_columns(self, query: str) -> list[str]: match = re.search( r"INSERT\s+INTO\s+\S+\s*\(([^)]+)\)\s*VALUES", @@ -365,22 +369,13 @@ class DatabaseManager: connection.close() def fetch_as_dataframe(self, query, params=None): - connection = None - cursor = None + results = self.fetch_all(query, params) try: - connection = self.get_connection() - cursor = connection.cursor(dictionary=True, buffered=True) - cursor.execute(query, params or ()) - if cursor.with_rows: - results = cursor.fetchall() - return pd.DataFrame(results) if results else pd.DataFrame() - logger.warning("No result set to fetch from.") - return pd.DataFrame() - finally: - if cursor: - cursor.close() - if connection: - connection.close() + import pandas as pd + except ImportError as err: + logger.error("Pandas is not installed; fetch_as_dataframe cannot return a DataFrame.") + raise + return pd.DataFrame(results) def create_table_if_not_exists(self, table_name, schema): table_name = self._sanitize_identifier(table_name)