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:
@@ -56,25 +56,37 @@ Here’s 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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+57
-61
@@ -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 (
|
||||||
async def check_transfer(
|
bank_data,
|
||||||
ctx: commands.Context,
|
create_account,
|
||||||
payer_balance: int,
|
reset_bank,
|
||||||
receiver_balance: int,
|
update_daily_timestamp,
|
||||||
member: discord.Member,
|
update_money,
|
||||||
amount: int,
|
|
||||||
) -> bool:
|
|
||||||
"""Check if a transfer is valid.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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>."
|
|
||||||
)
|
)
|
||||||
return False
|
from utils.sql_commands import DatabaseManager
|
||||||
|
|
||||||
|
|
||||||
|
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."
|
||||||
|
|
||||||
|
if payer_balance is None:
|
||||||
|
return False, "Your account does not exist."
|
||||||
|
|
||||||
|
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 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>",
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user