modified: .env.example
modified: Dockerfile modified: app.py modified: requirements.txt
This commit is contained in:
11
.env.example
11
.env.example
@@ -1,2 +1,13 @@
|
|||||||
# Discord Bot Token (wird in Coolify als Umgebungsvariable gesetzt)
|
# Discord Bot Token (wird in Coolify als Umgebungsvariable gesetzt)
|
||||||
DISCORD_TOKEN=your_discord_bot_token_here
|
DISCORD_TOKEN=your_discord_bot_token_here
|
||||||
|
|
||||||
|
# Database Connection - Option 1: Full URL (PostgreSQL/MySQL)
|
||||||
|
DATABASE_URL=postgresql://username:password@host:port/database_name
|
||||||
|
# DATABASE_URL=mysql://username:password@host:port/database_name
|
||||||
|
|
||||||
|
# Database Connection - Option 2: Individual Variables
|
||||||
|
DB_HOST=localhost
|
||||||
|
DB_PORT=5432
|
||||||
|
DB_NAME=hoi4_elo
|
||||||
|
DB_USER=username
|
||||||
|
DB_PASSWORD=password
|
||||||
@@ -14,6 +14,13 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Environment variables from Coolify
|
||||||
ENV DISCORD_TOKEN=$DISCORD_TOKEN
|
ENV DISCORD_TOKEN=$DISCORD_TOKEN
|
||||||
|
ENV DATABASE_URL=$DATABASE_URL
|
||||||
|
ENV DB_HOST=$DB_HOST
|
||||||
|
ENV DB_PORT=$DB_PORT
|
||||||
|
ENV DB_NAME=$DB_NAME
|
||||||
|
ENV DB_USER=$DB_USER
|
||||||
|
ENV DB_PASSWORD=$DB_PASSWORD
|
||||||
|
|
||||||
CMD ["python", "app.py"]
|
CMD ["python", "app.py"]
|
||||||
|
|||||||
432
app.py
432
app.py
@@ -4,6 +4,10 @@ import os
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
import asyncpg
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional, List, Dict
|
||||||
|
|
||||||
# Load environment variables
|
# Load environment variables
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
@@ -11,6 +15,12 @@ load_dotenv()
|
|||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
# Database configuration
|
||||||
|
DATABASE_URL = os.getenv('DATABASE_URL')
|
||||||
|
|
||||||
|
# Global database connection pool
|
||||||
|
db_pool = None
|
||||||
|
|
||||||
# Bot configuration
|
# Bot configuration
|
||||||
intents = discord.Intents.default()
|
intents = discord.Intents.default()
|
||||||
intents.message_content = True
|
intents.message_content = True
|
||||||
@@ -27,9 +37,12 @@ async def on_ready():
|
|||||||
print(f'Discord.py Version: {discord.__version__}')
|
print(f'Discord.py Version: {discord.__version__}')
|
||||||
print('------')
|
print('------')
|
||||||
|
|
||||||
|
# Initialize database
|
||||||
|
await init_database()
|
||||||
|
|
||||||
# Set bot status
|
# Set bot status
|
||||||
await bot.change_presence(
|
await bot.change_presence(
|
||||||
activity=discord.Game(name="Hearts of Iron IV"),
|
activity=discord.Game(name="Hearts of Iron IV ELO"),
|
||||||
status=discord.Status.online
|
status=discord.Status.online
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,6 +66,107 @@ async def on_guild_remove(guild):
|
|||||||
# Owner Configuration
|
# Owner Configuration
|
||||||
OWNER_ID = 253922739709018114
|
OWNER_ID = 253922739709018114
|
||||||
|
|
||||||
|
# ELO Configuration
|
||||||
|
STARTING_ELO = 800
|
||||||
|
K_FACTOR = 32
|
||||||
|
T_LEVEL_MULTIPLIERS = {
|
||||||
|
1: 0.8, # T1 countries get less points
|
||||||
|
2: 1.0, # T2 countries get normal points
|
||||||
|
3: 1.2 # T3 countries get more points
|
||||||
|
}
|
||||||
|
|
||||||
|
# Database Functions
|
||||||
|
async def init_database():
|
||||||
|
"""Initialize database connection and create tables"""
|
||||||
|
global db_pool
|
||||||
|
try:
|
||||||
|
db_pool = await asyncpg.create_pool(DATABASE_URL)
|
||||||
|
|
||||||
|
async with db_pool.acquire() as conn:
|
||||||
|
# Create players table
|
||||||
|
await conn.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS players (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
discord_id BIGINT UNIQUE NOT NULL,
|
||||||
|
username VARCHAR(255) NOT NULL,
|
||||||
|
standard_elo INTEGER DEFAULT 800,
|
||||||
|
competitive_elo INTEGER DEFAULT 800,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Create games table
|
||||||
|
await conn.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS games (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
game_name VARCHAR(255) NOT NULL,
|
||||||
|
game_type VARCHAR(50) NOT NULL,
|
||||||
|
status VARCHAR(50) DEFAULT 'setup',
|
||||||
|
players JSONB NOT NULL DEFAULT '[]',
|
||||||
|
winner_team VARCHAR(255),
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
finished_at TIMESTAMP
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Create game_results table for detailed match history
|
||||||
|
await conn.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS game_results (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
game_id INTEGER REFERENCES games(id),
|
||||||
|
discord_id BIGINT NOT NULL,
|
||||||
|
team_name VARCHAR(255) NOT NULL,
|
||||||
|
t_level INTEGER NOT NULL,
|
||||||
|
old_elo INTEGER NOT NULL,
|
||||||
|
new_elo INTEGER NOT NULL,
|
||||||
|
elo_change INTEGER NOT NULL,
|
||||||
|
won BOOLEAN NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
print("✅ Database initialized successfully")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Database initialization failed: {e}")
|
||||||
|
|
||||||
|
async def get_or_create_player(discord_id: int, username: str) -> Dict:
|
||||||
|
"""Get or create a player in the database"""
|
||||||
|
async with db_pool.acquire() as conn:
|
||||||
|
# Try to get existing player
|
||||||
|
player = await conn.fetchrow(
|
||||||
|
"SELECT * FROM players WHERE discord_id = $1", discord_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not player:
|
||||||
|
# Create new player
|
||||||
|
await conn.execute(
|
||||||
|
"INSERT INTO players (discord_id, username) VALUES ($1, $2)",
|
||||||
|
discord_id, username
|
||||||
|
)
|
||||||
|
player = await conn.fetchrow(
|
||||||
|
"SELECT * FROM players WHERE discord_id = $1", discord_id
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Update username if changed
|
||||||
|
await conn.execute(
|
||||||
|
"UPDATE players SET username = $1, updated_at = CURRENT_TIMESTAMP WHERE discord_id = $2",
|
||||||
|
username, discord_id
|
||||||
|
)
|
||||||
|
|
||||||
|
return dict(player)
|
||||||
|
|
||||||
|
def calculate_elo_change(player_elo: int, opponent_avg_elo: int, won: bool, t_level: int) -> int:
|
||||||
|
"""Calculate ELO change using standard ELO formula with T-level multiplier"""
|
||||||
|
expected_score = 1 / (1 + 10 ** ((opponent_avg_elo - player_elo) / 400))
|
||||||
|
actual_score = 1 if won else 0
|
||||||
|
|
||||||
|
base_change = K_FACTOR * (actual_score - expected_score)
|
||||||
|
t_multiplier = T_LEVEL_MULTIPLIERS.get(t_level, 1.0)
|
||||||
|
|
||||||
|
return round(base_change * t_multiplier)
|
||||||
|
|
||||||
# Owner only decorator
|
# Owner only decorator
|
||||||
def is_owner():
|
def is_owner():
|
||||||
def predicate(ctx):
|
def predicate(ctx):
|
||||||
@@ -96,6 +210,314 @@ async def reload_bot(ctx):
|
|||||||
)
|
)
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
# HOI4 ELO Commands
|
||||||
|
@bot.hybrid_command(name='hoi4create', description='Create a new HOI4 game')
|
||||||
|
async def hoi4create(ctx, game_type: str, game_name: str):
|
||||||
|
"""Create a new HOI4 game"""
|
||||||
|
if game_type.lower() not in ['standard', 'competitive']:
|
||||||
|
await ctx.send("❌ Game type must be either 'standard' or 'competitive'")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with db_pool.acquire() as conn:
|
||||||
|
# Check if game name already exists and is active
|
||||||
|
existing_game = await conn.fetchrow(
|
||||||
|
"SELECT * FROM games WHERE game_name = $1 AND status = 'setup'",
|
||||||
|
game_name
|
||||||
|
)
|
||||||
|
|
||||||
|
if existing_game:
|
||||||
|
await ctx.send(f"❌ A game with name '{game_name}' is already in setup phase!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create new game
|
||||||
|
await conn.execute(
|
||||||
|
"INSERT INTO games (game_name, game_type, status) VALUES ($1, $2, 'setup')",
|
||||||
|
game_name, game_type.lower()
|
||||||
|
)
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="🎮 Game Created",
|
||||||
|
description=f"HOI4 {game_type.title()} game '{game_name}' has been created!",
|
||||||
|
color=discord.Color.green()
|
||||||
|
)
|
||||||
|
embed.add_field(name="Game Name", value=game_name, inline=True)
|
||||||
|
embed.add_field(name="Type", value=game_type.title(), inline=True)
|
||||||
|
embed.add_field(name="Status", value="Setup Phase", inline=True)
|
||||||
|
embed.set_footer(text="Use /hoi4setup to add players to this game")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await ctx.send(f"❌ Error creating game: {str(e)}")
|
||||||
|
|
||||||
|
@bot.hybrid_command(name='hoi4setup', description='Add a player to an existing game')
|
||||||
|
async def hoi4setup(ctx, game_name: str, user: discord.Member, team_name: str, t_level: int):
|
||||||
|
"""Add a player to an existing game"""
|
||||||
|
if t_level not in [1, 2, 3]:
|
||||||
|
await ctx.send("❌ T-Level must be 1, 2, or 3")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with db_pool.acquire() as conn:
|
||||||
|
# Get the game
|
||||||
|
game = await conn.fetchrow(
|
||||||
|
"SELECT * FROM games WHERE game_name = $1 AND status = 'setup'",
|
||||||
|
game_name
|
||||||
|
)
|
||||||
|
|
||||||
|
if not game:
|
||||||
|
await ctx.send(f"❌ No game found with name '{game_name}' in setup phase!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get or create player
|
||||||
|
player = await get_or_create_player(user.id, user.display_name)
|
||||||
|
|
||||||
|
# Parse existing players
|
||||||
|
players = json.loads(game['players']) if game['players'] else []
|
||||||
|
|
||||||
|
# Check if player already in game
|
||||||
|
for p in players:
|
||||||
|
if p['discord_id'] == user.id:
|
||||||
|
await ctx.send(f"❌ {user.display_name} is already in this game!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Add player to game
|
||||||
|
player_data = {
|
||||||
|
'discord_id': user.id,
|
||||||
|
'username': user.display_name,
|
||||||
|
'team_name': team_name,
|
||||||
|
't_level': t_level,
|
||||||
|
'current_elo': player[f"{game['game_type']}_elo"]
|
||||||
|
}
|
||||||
|
players.append(player_data)
|
||||||
|
|
||||||
|
# Update game
|
||||||
|
await conn.execute(
|
||||||
|
"UPDATE games SET players = $1 WHERE id = $2",
|
||||||
|
json.dumps(players), game['id']
|
||||||
|
)
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="✅ Player Added",
|
||||||
|
description=f"{user.display_name} has been added to '{game_name}'!",
|
||||||
|
color=discord.Color.green()
|
||||||
|
)
|
||||||
|
embed.add_field(name="Player", value=user.display_name, inline=True)
|
||||||
|
embed.add_field(name="Team", value=team_name, inline=True)
|
||||||
|
embed.add_field(name="T-Level", value=f"T{t_level}", inline=True)
|
||||||
|
embed.add_field(name="Current ELO", value=player[f"{game['game_type']}_elo"], inline=True)
|
||||||
|
embed.add_field(name="Players in Game", value=len(players), inline=True)
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await ctx.send(f"❌ Error adding player: {str(e)}")
|
||||||
|
|
||||||
|
@bot.hybrid_command(name='hoi4end', description='End a game and calculate ELO changes')
|
||||||
|
async def hoi4end(ctx, game_name: str, winner_team: str):
|
||||||
|
"""End a game and calculate ELO changes"""
|
||||||
|
try:
|
||||||
|
async with db_pool.acquire() as conn:
|
||||||
|
# Get the game
|
||||||
|
game = await conn.fetchrow(
|
||||||
|
"SELECT * FROM games WHERE game_name = $1 AND status = 'setup'",
|
||||||
|
game_name
|
||||||
|
)
|
||||||
|
|
||||||
|
if not game:
|
||||||
|
await ctx.send(f"❌ No active game found with name '{game_name}'!")
|
||||||
|
return
|
||||||
|
|
||||||
|
players = json.loads(game['players']) if game['players'] else []
|
||||||
|
|
||||||
|
if len(players) < 2:
|
||||||
|
await ctx.send("❌ Game needs at least 2 players to end!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if winner team exists
|
||||||
|
teams = {p['team_name'] for p in players}
|
||||||
|
if winner_team not in teams:
|
||||||
|
await ctx.send(f"❌ Team '{winner_team}' not found in game! Available teams: {', '.join(teams)}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Calculate team averages
|
||||||
|
team_elos = {}
|
||||||
|
team_players = {}
|
||||||
|
|
||||||
|
for player in players:
|
||||||
|
team = player['team_name']
|
||||||
|
if team not in team_elos:
|
||||||
|
team_elos[team] = []
|
||||||
|
team_players[team] = []
|
||||||
|
|
||||||
|
team_elos[team].append(player['current_elo'])
|
||||||
|
team_players[team].append(player)
|
||||||
|
|
||||||
|
# Calculate average ELOs for each team
|
||||||
|
team_averages = {team: sum(elos) / len(elos) for team, elos in team_elos.items()}
|
||||||
|
|
||||||
|
elo_changes = []
|
||||||
|
|
||||||
|
# Calculate ELO changes for each player
|
||||||
|
for player in players:
|
||||||
|
team = player['team_name']
|
||||||
|
won = team == winner_team
|
||||||
|
|
||||||
|
# Calculate opponent average (average of all other teams)
|
||||||
|
opponent_elos = []
|
||||||
|
for other_team, elos in team_elos.items():
|
||||||
|
if other_team != team:
|
||||||
|
opponent_elos.extend(elos)
|
||||||
|
|
||||||
|
opponent_avg = sum(opponent_elos) / len(opponent_elos) if opponent_elos else player['current_elo']
|
||||||
|
|
||||||
|
elo_change = calculate_elo_change(
|
||||||
|
player['current_elo'],
|
||||||
|
opponent_avg,
|
||||||
|
won,
|
||||||
|
player['t_level']
|
||||||
|
)
|
||||||
|
|
||||||
|
new_elo = max(0, player['current_elo'] + elo_change) # Prevent negative ELO
|
||||||
|
|
||||||
|
elo_changes.append({
|
||||||
|
'discord_id': player['discord_id'],
|
||||||
|
'username': player['username'],
|
||||||
|
'team_name': team,
|
||||||
|
't_level': player['t_level'],
|
||||||
|
'old_elo': player['current_elo'],
|
||||||
|
'new_elo': new_elo,
|
||||||
|
'elo_change': elo_change,
|
||||||
|
'won': won
|
||||||
|
})
|
||||||
|
|
||||||
|
# Update player ELOs and save game results
|
||||||
|
for change in elo_changes:
|
||||||
|
# Update player ELO
|
||||||
|
elo_field = f"{game['game_type']}_elo"
|
||||||
|
await conn.execute(
|
||||||
|
f"UPDATE players SET {elo_field} = $1, updated_at = CURRENT_TIMESTAMP WHERE discord_id = $2",
|
||||||
|
change['new_elo'], change['discord_id']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Save game result
|
||||||
|
await conn.execute(
|
||||||
|
"""INSERT INTO game_results
|
||||||
|
(game_id, discord_id, team_name, t_level, old_elo, new_elo, elo_change, won)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)""",
|
||||||
|
game['id'], change['discord_id'], change['team_name'],
|
||||||
|
change['t_level'], change['old_elo'], change['new_elo'],
|
||||||
|
change['elo_change'], change['won']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mark game as finished
|
||||||
|
await conn.execute(
|
||||||
|
"UPDATE games SET status = 'finished', winner_team = $1, finished_at = CURRENT_TIMESTAMP WHERE id = $2",
|
||||||
|
winner_team, game['id']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create result embed
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="🏆 Game Finished!",
|
||||||
|
description=f"Game '{game_name}' has ended!\n**Winner: {winner_team}**",
|
||||||
|
color=discord.Color.gold()
|
||||||
|
)
|
||||||
|
|
||||||
|
# Group results by team
|
||||||
|
teams_results = {}
|
||||||
|
for change in elo_changes:
|
||||||
|
team = change['team_name']
|
||||||
|
if team not in teams_results:
|
||||||
|
teams_results[team] = []
|
||||||
|
teams_results[team].append(change)
|
||||||
|
|
||||||
|
for team, team_changes in teams_results.items():
|
||||||
|
team_text = ""
|
||||||
|
for change in team_changes:
|
||||||
|
emoji = "📈" if change['elo_change'] > 0 else "📉" if change['elo_change'] < 0 else "➡️"
|
||||||
|
team_text += f"{change['username']}: {change['old_elo']} → {change['new_elo']} ({change['elo_change']:+d}) {emoji}\n"
|
||||||
|
|
||||||
|
embed.add_field(
|
||||||
|
name=f"{'🏆 ' if team == winner_team else ''}Team {team}",
|
||||||
|
value=team_text,
|
||||||
|
inline=False
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.set_footer(text=f"Game Type: {game['game_type'].title()}")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await ctx.send(f"❌ Error ending game: {str(e)}")
|
||||||
|
|
||||||
|
@bot.hybrid_command(name='hoi4stats', description='Show your HOI4 ELO statistics')
|
||||||
|
async def hoi4stats(ctx, user: Optional[discord.Member] = None):
|
||||||
|
"""Show HOI4 ELO statistics for a user"""
|
||||||
|
target_user = user or ctx.author
|
||||||
|
|
||||||
|
try:
|
||||||
|
player = await get_or_create_player(target_user.id, target_user.display_name)
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title=f"📊 HOI4 ELO Stats - {target_user.display_name}",
|
||||||
|
color=discord.Color.blue()
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.add_field(name="Standard ELO", value=f"🎯 {player['standard_elo']}", inline=True)
|
||||||
|
embed.add_field(name="Competitive ELO", value=f"🏆 {player['competitive_elo']}", inline=True)
|
||||||
|
embed.add_field(name="Player Since", value=player['created_at'].strftime("%m/%d/%Y"), inline=True)
|
||||||
|
|
||||||
|
if target_user.avatar:
|
||||||
|
embed.set_thumbnail(url=target_user.avatar.url)
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await ctx.send(f"❌ Error getting stats: {str(e)}")
|
||||||
|
|
||||||
|
@bot.hybrid_command(name='hoi4games', description='Show active games')
|
||||||
|
async def hoi4games(ctx):
|
||||||
|
"""Show all active games"""
|
||||||
|
try:
|
||||||
|
async with db_pool.acquire() as conn:
|
||||||
|
games = await conn.fetch(
|
||||||
|
"SELECT * FROM games WHERE status = 'setup' ORDER BY created_at DESC"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not games:
|
||||||
|
await ctx.send("📝 No active games found. Use `/hoi4create` to create a new game!")
|
||||||
|
return
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="🎮 Active HOI4 Games",
|
||||||
|
color=discord.Color.green()
|
||||||
|
)
|
||||||
|
|
||||||
|
for game in games:
|
||||||
|
players = json.loads(game['players']) if game['players'] else []
|
||||||
|
player_count = len(players)
|
||||||
|
|
||||||
|
teams = {}
|
||||||
|
for player in players:
|
||||||
|
team = player['team_name']
|
||||||
|
if team not in teams:
|
||||||
|
teams[team] = 0
|
||||||
|
teams[team] += 1
|
||||||
|
|
||||||
|
team_info = ", ".join([f"{team} ({count})" for team, count in teams.items()]) if teams else "No players yet"
|
||||||
|
|
||||||
|
embed.add_field(
|
||||||
|
name=f"{game['game_name']} ({game['game_type'].title()})",
|
||||||
|
value=f"Players: {player_count}\nTeams: {team_info}",
|
||||||
|
inline=False
|
||||||
|
)
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await ctx.send(f"❌ Error getting games: {str(e)}")
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_command_error(ctx, error):
|
async def on_command_error(ctx, error):
|
||||||
"""Handles command errors"""
|
"""Handles command errors"""
|
||||||
@@ -122,6 +544,11 @@ async def main():
|
|||||||
print("Please set the DISCORD_TOKEN variable in Coolify or create a .env file")
|
print("Please set the DISCORD_TOKEN variable in Coolify or create a .env file")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not DATABASE_URL:
|
||||||
|
print("❌ DATABASE_URL environment variable not found!")
|
||||||
|
print("Please set the DATABASE_URL variable in Coolify")
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print("🚀 Starting bot...")
|
print("🚀 Starting bot...")
|
||||||
await bot.start(token)
|
await bot.start(token)
|
||||||
@@ -129,6 +556,9 @@ async def main():
|
|||||||
print("❌ Invalid Discord token!")
|
print("❌ Invalid Discord token!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Error starting bot: {e}")
|
print(f"❌ Error starting bot: {e}")
|
||||||
|
finally:
|
||||||
|
if db_pool:
|
||||||
|
await db_pool.close()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
discord.py==2.3.2
|
discord.py==2.3.2
|
||||||
python-dotenv==1.0.0
|
python-dotenv==1.0.0
|
||||||
aiohttp==3.9.1
|
aiohttp==3.9.1
|
||||||
|
asyncpg==0.29.0
|
||||||
|
psycopg2-binary==2.9.9
|
||||||
|
aiomysql==0.2.0
|
||||||
|
PyMySQL==1.1.0
|
||||||
Reference in New Issue
Block a user