Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 42 additions & 9 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
========================================

Copyright (c) 2025 OPPRO.NET Network
Version: 2.0.0
Version: 2.1.0
"""

# =============================================================================
Expand Down Expand Up @@ -50,7 +50,7 @@

# API Routes & Translation
from src.api.dashboard.routes import set_bot_instance, dashboard_main_router, router_public
from mx_handler import TranslationHandler
from mxmariadb import TranslationHandler, BlacklistDatabase

colorama_init(autoreset=True)

Expand Down Expand Up @@ -111,8 +111,8 @@ async def start_webserver():
log_level=BotConfig.api.log_level
)
server = Server(server_config)
await server.serve()
logger.success("API", f"FastAPI-Server läuft auf http://{BotConfig.api.host}:{BotConfig.api.port}")
await server.serve()

# =============================================================================
# MAIN EXECUTION
Expand Down Expand Up @@ -146,16 +146,18 @@ async def start_webserver():
dashboard = DashboardTask(bot, BASEDIR)
dashboard.register()

# Dashboard starten
dashboard.start()

# --- NEU: Webserver direkt beim Start in den Loop hängen ---
# Wir starten den Webserver, bevor der Bot den Loop blockiert
bot.loop.create_task(start_webserver())
logger.info("API", "Webserver-Task im Hintergrund gestartet")

@bot.event
async def on_ready():
logger.success("BOT", f"Logged in as {bot.user.name}")

# --- NEU: Status API & Webserver starten ---
bot.loop.create_task(start_webserver())

# Dashboard starten
dashboard.start()

# Bot-Status
if BotConfig.features.get('bot_status', True):
await bot.change_presence(
Expand All @@ -174,6 +176,37 @@ async def on_ready():
logger.info("LIMITS", f"Discord-API Slots belegt: {len(root_slots)} / 100")
# --- LIMIT CHECK ENDE ---

@bot.check
async def global_blacklist_check(ctx: discord.ApplicationContext):
"""Checks if the user is on the global blacklist."""
# Bot owners are always exempt
if ctx.author.id in BotConfig.security.bot_owners:
return True

try:
db = BlacklistDatabase()
await db.ensure_connection()
ban_info = await db.is_blacklisted(str(ctx.author.id))

if ban_info:
embed = discord.Embed(
title="🚫 Globaler Ausschluss",
description=(
f"Du wurdest von der Nutzung von **{bot.user.name}** ausgeschlossen.\n\n"
f"**Grund:** `{ban_info['reason']}`\n"
f"**Datum:** `{ban_info['created_at'].strftime('%d.%m.%Y')}`\n\n"
"Solltest du glauben, dass dies ein Fehler ist, wende dich bitte an unseren Support."
),
color=discord.Color.from_rgb(244, 63, 94) # Rose-500
)
embed.set_footer(text="System: Global Blacklist")
await ctx.respond(embed=embed, ephemeral=True)
return False
except Exception as e:
logger.error("BLACKLIST", f"Fehler beim Blacklist-Check: {e}")

return True

@bot.before_invoke
async def maintenance_check(ctx: discord.ApplicationContext):
"""Global check for maintenance mode."""
Expand Down
2 changes: 2 additions & 0 deletions mxmariadb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@
from .economy_db import EconomyDatabase
from .management_db import ManagementDatabase
from .cms_db import CMSDatabase
from .blacklist_db import BlacklistDatabase
from mx_handler import TranslationHandler
43 changes: 43 additions & 0 deletions mxmariadb/blacklist_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from .connector import MariaConnector
from typing import List, Dict, Any
import time

class BlacklistDatabase(MariaConnector):
async def init_db(self):
"""Initialisiert die Blacklist-Tabelle."""
query = """
CREATE TABLE IF NOT EXISTS global_blacklist (
user_id VARCHAR(25) PRIMARY KEY,
reason TEXT,
admin_id VARCHAR(25),
admin_name VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_VALUE
)
"""
# Hinweis: CURRENT_TIMESTAMP wird automatisch gesetzt
await self.execute_query("""
CREATE TABLE IF NOT EXISTS global_blacklist (
user_id VARCHAR(25) PRIMARY KEY,
reason TEXT,
admin_id VARCHAR(25),
admin_name VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")

async def add_to_blacklist(self, user_id: str, reason: str, admin_id: str, admin_name: str) -> bool:
query = "INSERT INTO global_blacklist (user_id, reason, admin_id, admin_name) VALUES (%s, %s, %s, %s) ON DUPLICATE KEY UPDATE reason=%s"
return await self.execute_query(query, (user_id, reason, admin_id, admin_name, reason))

async def remove_from_blacklist(self, user_id: str) -> bool:
query = "DELETE FROM global_blacklist WHERE user_id = %s"
return await self.execute_query(query, (user_id,))

async def is_blacklisted(self, user_id: str) -> Dict[str, Any]:
query = "SELECT * FROM global_blacklist WHERE user_id = %s"
result = await self.fetch_all(query, (user_id,))
return result[0] if result else None

async def get_all_blacklisted(self) -> List[Dict[str, Any]]:
query = "SELECT * FROM global_blacklist ORDER BY created_at DESC"
return await self.fetch_all(query)
77 changes: 77 additions & 0 deletions mxmariadb/cms_db.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) 2025 OPPRO.NET Network
import aiomysql
import logging
from typing import List, Dict, Any, Optional
from mxmariadb.connector import MariaConnector

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -49,6 +50,17 @@ async def init_db(self):
except Exception:
pass # Column already exists or unsupported syntax

# Tags table
await cur.execute("""
CREATE TABLE IF NOT EXISTS cms_tags (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL,
slug VARCHAR(60) UNIQUE NOT NULL,
color VARCHAR(20) DEFAULT '#3498db',
emoji VARCHAR(10) DEFAULT ''
)
""")

# Media/uploads table
await cur.execute("""
CREATE TABLE IF NOT EXISTS cms_media (
Expand Down Expand Up @@ -265,3 +277,68 @@ async def get_changelog(self, limit: int = 50):
LIMIT %s
""", (limit,))
return await cur.fetchall()

# ─────────────────────────────────────────
# TAGS
# ─────────────────────────────────────────

async def get_tags(self) -> List[Dict[str, Any]]:
await self.ensure_connection()
try:
async with self.pool.acquire() as conn:
async with conn.cursor(aiomysql.DictCursor) as cur:
await cur.execute("SELECT * FROM cms_tags ORDER BY name")
return await cur.fetchall()
except Exception as e:
logger.error(f"Error fetching tags: {e}")
return []

async def create_tag(self, name: str, slug: str, color: str = "#3498db", emoji: str = ""):
await self.ensure_connection()
try:
async with self.pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute(
"INSERT INTO cms_tags (name, slug, color, emoji) VALUES (%s, %s, %s, %s)",
(name, slug, color, emoji)
)
await conn.commit()
return True
except Exception as e:
logger.error(f"Error creating tag: {e}")
return False

async def update_tag(self, tag_id: int, **kwargs):
await self.ensure_connection()
if not kwargs: return False

fields = []
values = []
for k, v in kwargs.items():
fields.append(f"{k} = %s")
values.append(v)

values.append(tag_id)
query = f"UPDATE cms_tags SET {', '.join(fields)} WHERE id = %s"

try:
async with self.pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute(query, tuple(values))
await conn.commit()
return True
except Exception as e:
logger.error(f"Error updating tag: {e}")
return False

async def delete_tag(self, tag_id: int):
await self.ensure_connection()
try:
async with self.pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute("DELETE FROM cms_tags WHERE id = %s", (tag_id,))
await conn.commit()
return True
except Exception as e:
logger.error(f"Error deleting tag: {e}")
return False
1 change: 1 addition & 0 deletions mxmariadb/levelsystem_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ async def _ensure_initialized(self):

async def init_db(self):
"""Create tables and load caches."""
await self.ensure_connection()
async with self.pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute('''
Expand Down
43 changes: 43 additions & 0 deletions mxmariadb/stats_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ async def init_db(self):
icon VARCHAR(10) DEFAULT '🏆'
)
''')
await cur.execute('''
CREATE TABLE IF NOT EXISTS command_usage (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
guild_id BIGINT NOT NULL,
command_name VARCHAR(100) NOT NULL,
used_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_cmd (command_name),
INDEX idx_guild (guild_id)
)
''')

await cur.execute('''
CREATE TABLE IF NOT EXISTS active_voice_sessions (
user_id BIGINT PRIMARY KEY,
Expand Down Expand Up @@ -204,6 +215,38 @@ async def _end_voice_internal(self, cur, user_id: int):

await cur.execute('DELETE FROM active_voice_sessions WHERE user_id = %s', (user_id,))

async def log_command(self, guild_id: int, command_name: str):
await self.ensure_connection()
async with self.lock:
try:
async with self.pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute('''
INSERT INTO command_usage (guild_id, command_name)
VALUES (%s, %s)
''', (guild_id, command_name))
await conn.commit()
except Exception as e:
logger.error(f"log_command fehlgeschlagen: {e}")

async def get_top_commands(self, limit: int = 5) -> List[Dict]:
await self.ensure_connection()
async with self.lock:
try:
async with self.pool.acquire() as conn:
async with conn.cursor(aiomysql.DictCursor) as cur:
await cur.execute('''
SELECT command_name, COUNT(*) as usage_count
FROM command_usage
GROUP BY command_name
ORDER BY usage_count DESC
LIMIT %s
''', (limit,))
return await cur.fetchall()
except Exception as e:
logger.error(f"get_top_commands fehlgeschlagen: {e}")
return []

# ------------------------------------------------------------------
# XP (unverändert, nur ensure_connection nicht nötig — läuft intern)
# ------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions mxmariadb/welcome_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ async def set_welcome_message(self, guild_id: int, message: str) -> bool:
# --- Core CRUD ---

async def update_welcome_settings(self, guild_id: int, **kwargs) -> bool:
await self.ensure_connection()
try:
async with self.pool.acquire() as conn:
async with conn.cursor() as cur:
Expand Down Expand Up @@ -114,6 +115,7 @@ async def update_welcome_settings(self, guild_id: int, **kwargs) -> bool:
return False

async def get_welcome_settings(self, guild_id: int) -> Optional[Dict[str, Any]]:
await self.ensure_connection()
try:
async with self.pool.acquire() as conn:
async with conn.cursor(aiomysql.DictCursor) as cur:
Expand All @@ -127,6 +129,7 @@ async def get_welcome_settings(self, guild_id: int) -> Optional[Dict[str, Any]]:
return None

async def delete_welcome_settings(self, guild_id: int) -> bool:
await self.ensure_connection()
try:
async with self.pool.acquire() as conn:
async with conn.cursor() as cur:
Expand Down Expand Up @@ -155,6 +158,7 @@ async def toggle_welcome(self, guild_id: int) -> Optional[bool]:

async def update_welcome_stats(self, guild_id: int,
joins: int = 0, leaves: int = 0):
await self.ensure_connection()
try:
date = datetime.now().strftime('%Y-%m-%d')
async with self.pool.acquire() as conn:
Expand All @@ -171,6 +175,7 @@ async def update_welcome_stats(self, guild_id: int,
logger.error(f"Stats update error: {e}")

async def get_weekly_stats(self, guild_id: int) -> List[Dict]:
await self.ensure_connection()
try:
async with self.pool.acquire() as conn:
async with conn.cursor(aiomysql.DictCursor) as cur:
Expand Down
Loading
Loading