Refactor project structure and update README; remove unused dependencies from requirements.txt and enhance database management in sql_commands.py.

This commit is contained in:
2026-05-31 12:56:55 +00:00
parent 5fdb37f7f8
commit 3e6410d112
4 changed files with 107 additions and 106 deletions
+30 -18
View File
@@ -56,25 +56,37 @@ Heres an outline of the directory and file structure for this project:
``` ```
discord-bot/ discord-bot/
├── bot.py # Main bot file for initializing the bot and loading cogs ├── bot.py
├── requirements.txt # List of required Python packages ├── bot_development.py
├── .env # Environment file containing sensitive data (e.g., bot token) ├── main.py
├── dockerfile
├── requirements.txt
├── README.md
├── Fixes.md
├── cogs/ # Folder containing individual cogs for separate functionalities ├── cogs/
│ ├── admin.py # Admin commands such as ban, kick, etc. │ ├── admin.py
│ ├── economy.py # Economy-related commands │ ├── customCommands.py
│ ├── fun.py # Fun commands for user engagement │ ├── economy.py
── roles.py # Commands for managing server roles ── gamble.py
│ ├── informational.py
│ ├── listeners.py
│ ├── mail.py
│ ├── npc.py
│ ├── xp.py
├── utils/ # Folder containing utility files for common functionality ├── utils/
│ ├── bank_functions.py # Economy and balance management functions │ ├── bank_functions.py
── sql_Commands.py # Database helper functions for handling SQL commands ── npc_data.py
│ ├── npc_handler.py
│ ├── npc_memory.py
│ ├── sql_commands.py
├── extras/ # Experimental or additional features (optional) ├── web/
── example.py ── __init__.py
├── app.py
└── docs/ # Documentation files │ ├── static/
── CheckList.txt # Planning and improvement list (optional) ── 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. Store sensitive information like your bot token in the `.env` file. Avoid committing this file to version control.
### Database ### 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 ## 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.
--- ---
+54 -58
View File
@@ -1,49 +1,39 @@
import logging import logging
import discord import discord
from discord.ext import commands from discord.ext import commands
from utils.bank_functions import * from datetime import datetime, timedelta, timezone
from discord.ext import commands
from datetime import datetime, timedelta
from random import randint from random import randint
from typing import Literal 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( def validate_transfer(
ctx: commands.Context, payer_balance: int, author_id: int, receiver_id: int, amount: int
payer_balance: int, ) -> tuple[bool, str]:
receiver_balance: int, if amount <= 0:
member: discord.Member, return False, "Please enter an amount greater than 0."
amount: int,
) -> bool:
"""Check if a transfer is valid.
Args: if payer_balance is None:
- ctx (commands.Context): The context of the invoked command. return False, "Your account does not exist."
- 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.
Returns: if author_id == receiver_id:
- bool: Whether the transfer is valid. return False, "You cannot give yourself money."
"""
if payer_balance is None or receiver_balance is None: if payer_balance < amount:
await ctx.reply("Bank account doesn't exist.") return False, (
return False f"You do not have {amount:,}<:flooney:1194943899765051473>. "
if payer_balance >= amount > 0: f"You have {payer_balance:,}<:flooney:1194943899765051473>."
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>."
) )
return False
return True, ""
class Economy(commands.Cog): class Economy(commands.Cog):
@@ -213,7 +203,12 @@ class Economy(commands.Cog):
payer_wallet = int((await bank_data(ctx.author)).get("WALLET", 0)) payer_wallet = int((await bank_data(ctx.author)).get("WALLET", 0))
receiver_wallet = int((await bank_data(target)).get("WALLET", 0)) receiver_wallet = int((await bank_data(target)).get("WALLET", 0))
if await check_transfer(ctx, payer_wallet, receiver_wallet, target, 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 update_money(target, wallet=amount) await update_money(target, wallet=amount)
await update_money(ctx.author, wallet=-amount) await update_money(ctx.author, wallet=-amount)
@@ -229,7 +224,12 @@ class Economy(commands.Cog):
payer_bank = int((await bank_data(ctx.author)).get("BANK", 0)) payer_bank = int((await bank_data(ctx.author)).get("BANK", 0))
receiver_bank = int((await bank_data(target)).get("BANK", 0)) receiver_bank = int((await bank_data(target)).get("BANK", 0))
if await check_transfer(ctx, payer_bank, receiver_bank, target, 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 update_money(target, bank=amount) await update_money(target, bank=amount)
await update_money(ctx.author, bank=-amount) await update_money(ctx.author, bank=-amount)
@@ -248,7 +248,7 @@ class Economy(commands.Cog):
user_data = await bank_data(ctx.author) user_data = await bank_data(ctx.author)
if user_data is None: if user_data is None:
await create_account(ctx) await create_account(ctx.author)
user_data = await bank_data(ctx.author) user_data = await bank_data(ctx.author)
wallet_balance = int(user_data.get("WALLET", 0)) wallet_balance = int(user_data.get("WALLET", 0))
@@ -268,7 +268,7 @@ class Economy(commands.Cog):
user_data = await bank_data(ctx.author) user_data = await bank_data(ctx.author)
if user_data is None: if user_data is None:
await create_account(ctx) await create_account(ctx.author)
user_data = await bank_data(ctx.author) user_data = await bank_data(ctx.author)
bank_balance = int(user_data.get("BANK", 0)) 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", title=f"Top {len(leaderboard_entries)} Richest Users - Leaderboard",
description="\n".join(leaderboard_entries), description="\n".join(leaderboard_entries),
color=discord.Color(0x00FF00), color=discord.Color(0x00FF00),
timestamp=datetime.datetime.utcnow(), timestamp=datetime.utcnow(),
) )
embed.set_footer(text=f"GLOBAL - {ctx.guild.name}") embed.set_footer(text=f"GLOBAL - {ctx.guild.name}")
await ctx.reply(embed=embed, mention_author=False) await ctx.reply(embed=embed, mention_author=False)
@@ -353,36 +353,32 @@ class Economy(commands.Cog):
try: try:
user_data = await bank_data(ctx.author) user_data = await bank_data(ctx.author)
last_claim = user_data.get("DAILY") 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 last_claim:
if isinstance(last_claim, (int, float)): if isinstance(last_claim, (int, float)):
last_claim_dt = datetime.datetime.fromtimestamp( last_claim_dt = datetime.fromtimestamp(last_claim, tz=timezone.utc)
last_claim, tz=datetime.timezone.utc
)
elif isinstance(last_claim, str): elif isinstance(last_claim, str):
try: try:
last_claim_dt = datetime.datetime.fromtimestamp( last_claim_dt = datetime.fromtimestamp(
float(last_claim), tz=datetime.timezone.utc float(last_claim), tz=timezone.utc
) )
except Exception: except ValueError:
last_claim_dt = None last_claim_dt = None
elif isinstance(last_claim, datetime.datetime): elif isinstance(last_claim, datetime):
# If it's naive, make it aware last_claim_dt = (
if last_claim.tzinfo is None: last_claim.replace(tzinfo=timezone.utc)
last_claim_dt = last_claim.replace(tzinfo=datetime.timezone.utc) if last_claim.tzinfo is None
else: else last_claim
last_claim_dt = last_claim )
else: else:
last_claim_dt = None last_claim_dt = None
else: else:
last_claim_dt = None 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) daily_reward = randint(200, 1000)
await update_money(ctx.author, daily_reward) await update_money(ctx.author, wallet=daily_reward)
# Save the new timestamp as a float (UNIX time)
await update_daily_timestamp(ctx.author, now) await update_daily_timestamp(ctx.author, now)
await ctx.reply( await ctx.reply(
f"Your daily pocket money is {daily_reward:,}<:flooney:1194943899765051473>", f"Your daily pocket money is {daily_reward:,}<:flooney:1194943899765051473>",
-2
View File
@@ -16,8 +16,6 @@ Jinja2==3.1.6
MarkupSafe==3.0.3 MarkupSafe==3.0.3
multidict==6.1.0 multidict==6.1.0
mysql-connector==2.2.9 mysql-connector==2.2.9
numpy==2.1.3
pandas==2.2.3
pillow==12.1.1 pillow==12.1.1
propcache==0.2.0 propcache==0.2.0
python-dateutil==2.9.0.post0 python-dateutil==2.9.0.post0
+13 -18
View File
@@ -7,7 +7,6 @@ import re
import time import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
import logging import logging
import pandas as pd
# Configure logging # Configure logging
@@ -45,8 +44,9 @@ class DatabaseManager:
"user": os.getenv("SQLUSER", "root"), "user": os.getenv("SQLUSER", "root"),
"password": os.getenv("SQLPASS", ""), "password": os.getenv("SQLPASS", ""),
"database": os.getenv("SQLDB", "testdb"), "database": os.getenv("SQLDB", "testdb"),
"pool_reset_session": os.getenv("POOL_RESET_SESSION", "false").lower() "pool_reset_session": self._parse_bool(
in ("true", "1", "yes"), os.getenv("POOL_RESET_SESSION", "false")
),
} }
self.pool = pooling.MySQLConnectionPool( self.pool = pooling.MySQLConnectionPool(
@@ -234,6 +234,10 @@ class DatabaseManager:
raise ValueError(f"Invalid SQL identifier: {identifier}") raise ValueError(f"Invalid SQL identifier: {identifier}")
return 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]: def _parse_insert_columns(self, query: str) -> list[str]:
match = re.search( match = re.search(
r"INSERT\s+INTO\s+\S+\s*\(([^)]+)\)\s*VALUES", r"INSERT\s+INTO\s+\S+\s*\(([^)]+)\)\s*VALUES",
@@ -365,22 +369,13 @@ class DatabaseManager:
connection.close() connection.close()
def fetch_as_dataframe(self, query, params=None): def fetch_as_dataframe(self, query, params=None):
connection = None results = self.fetch_all(query, params)
cursor = None
try: try:
connection = self.get_connection() import pandas as pd
cursor = connection.cursor(dictionary=True, buffered=True) except ImportError as err:
cursor.execute(query, params or ()) logger.error("Pandas is not installed; fetch_as_dataframe cannot return a DataFrame.")
if cursor.with_rows: raise
results = cursor.fetchall() return pd.DataFrame(results)
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()
def create_table_if_not_exists(self, table_name, schema): def create_table_if_not_exists(self, table_name, schema):
table_name = self._sanitize_identifier(table_name) table_name = self._sanitize_identifier(table_name)