updated tech stack for newer versions of python, removed pyaudio. refactored configuration into .env file and integration inside logic, removed repeat config reads
This commit is contained in:
297
config.py
297
config.py
@@ -1,115 +1,198 @@
|
||||
# config.py
|
||||
# This file should parse all configurations within the bot
|
||||
# Modern configuration management using environment variables
|
||||
|
||||
import os
|
||||
import discord
|
||||
from discord import Color
|
||||
import json
|
||||
from dotenv import load_dotenv
|
||||
from typing import Optional
|
||||
|
||||
# Read data from JSON file in ./data/config.json
|
||||
def read_data():
|
||||
with open("./data/config.json", "r") as file:
|
||||
return json.load(file)
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
raise Exception("Could not load config data")
|
||||
|
||||
|
||||
def get_spotify_creds():
|
||||
data = read_data()
|
||||
data = data.get("spotify")
|
||||
|
||||
SCID = data.get("SCID")
|
||||
secret = data.get("SECRET")
|
||||
|
||||
return SCID, secret
|
||||
|
||||
|
||||
# Reading prefix
|
||||
def get_prefix():
|
||||
data = read_data()
|
||||
|
||||
prefix = data.get('prefix')
|
||||
if prefix:
|
||||
return prefix
|
||||
|
||||
raise Exception("Missing config data: prefix")
|
||||
|
||||
|
||||
# Fetch the bot secret token
|
||||
def get_login(bot):
|
||||
data = read_data()
|
||||
if data is False or data.get(f"{bot}bot") is False:
|
||||
raise Exception(f"Missing config data: {bot}bot")
|
||||
|
||||
data = data.get(f"{bot}bot")
|
||||
return data.get("secret")
|
||||
|
||||
|
||||
# Read the status and text data
|
||||
def get_status():
|
||||
data = read_data()
|
||||
|
||||
if data is False or data.get('status') is False:
|
||||
raise Exception("Missing config data: status")
|
||||
|
||||
# Find type
|
||||
data = data.get('status')
|
||||
return translate_status(
|
||||
data.get('type'),
|
||||
data.get('text'),
|
||||
data.get('link')
|
||||
)
|
||||
|
||||
# Get colors from colorscheme
|
||||
def get_color(color):
|
||||
data = read_data()
|
||||
|
||||
if data is False or data.get('status') is False:
|
||||
raise Exception("Missing config data: color")
|
||||
|
||||
# Grab color
|
||||
string_value = data.get("colorscheme").get(color)
|
||||
hex_value = Color.from_str(string_value)
|
||||
return hex_value
|
||||
|
||||
|
||||
# Taking JSON variables and converting them into a presence
|
||||
# Use None url incase not provided
|
||||
def translate_status(status_type, status_text, status_url=None):
|
||||
if status_type == "playing":
|
||||
return discord.Activity(
|
||||
type=discord.ActivityType.playing,
|
||||
name=status_text
|
||||
)
|
||||
|
||||
|
||||
elif status_type == "streaming":
|
||||
return discord.Activity(
|
||||
type=discord.ActivityType.streaming,
|
||||
name=status_text,
|
||||
url=status_url
|
||||
)
|
||||
|
||||
elif status_type == "listening":
|
||||
return discord.Activity(
|
||||
type=discord.ActivityType.listening,
|
||||
name=status_text
|
||||
)
|
||||
|
||||
|
||||
elif status_type == "watching":
|
||||
return discord.Activity(
|
||||
type=discord.ActivityType.watching,
|
||||
name=status_text
|
||||
)
|
||||
|
||||
elif status_type == "competing":
|
||||
return discord.Activity(
|
||||
type=discord.ActivityType.competing,
|
||||
name=status_text
|
||||
)
|
||||
|
||||
#TODO
|
||||
# Implement custom status type
|
||||
# ===================================
|
||||
# Environment Detection
|
||||
# ===================================
|
||||
ENVIRONMENT = os.getenv("ENVIRONMENT", "dev").lower()
|
||||
IS_PRODUCTION = ENVIRONMENT == "live"
|
||||
IS_DEVELOPMENT = ENVIRONMENT == "dev"
|
||||
|
||||
# ===================================
|
||||
# Discord Configuration
|
||||
# ===================================
|
||||
def get_discord_token() -> str:
|
||||
"""Get the appropriate Discord token based on environment"""
|
||||
if IS_PRODUCTION:
|
||||
token = os.getenv("DISCORD_TOKEN_LIVE")
|
||||
if not token:
|
||||
raise ValueError("DISCORD_TOKEN_LIVE not found in environment!")
|
||||
return token
|
||||
else:
|
||||
raise Exception(f"Invalid status type: {status_type}")
|
||||
token = os.getenv("DISCORD_TOKEN_DEV")
|
||||
if not token:
|
||||
raise ValueError("DISCORD_TOKEN_DEV not found in environment!")
|
||||
return token
|
||||
|
||||
|
||||
def get_prefix() -> str:
|
||||
"""Get command prefix (default: =)"""
|
||||
return os.getenv("DISCORD_PREFIX", "=")
|
||||
|
||||
|
||||
# ===================================
|
||||
# Spotify Configuration
|
||||
# ===================================
|
||||
def get_spotify_creds() -> tuple[str, str]:
|
||||
"""Get Spotify API credentials"""
|
||||
client_id = os.getenv("SPOTIFY_CLIENT_ID")
|
||||
client_secret = os.getenv("SPOTIFY_CLIENT_SECRET")
|
||||
|
||||
if not client_id or not client_secret:
|
||||
raise ValueError("Spotify credentials not found in environment!")
|
||||
|
||||
return client_id, client_secret
|
||||
|
||||
|
||||
# ===================================
|
||||
# Database Configuration
|
||||
# ===================================
|
||||
def get_db_path() -> str:
|
||||
"""Get SQLite database path"""
|
||||
return os.getenv("DB_PATH", "./data/music.db")
|
||||
|
||||
|
||||
# Future PostgreSQL config
|
||||
def get_postgres_url() -> Optional[str]:
|
||||
"""Get PostgreSQL connection URL (for future migration)"""
|
||||
host = os.getenv("DB_HOST")
|
||||
port = os.getenv("DB_PORT", "5432")
|
||||
name = os.getenv("DB_NAME")
|
||||
user = os.getenv("DB_USER")
|
||||
password = os.getenv("DB_PASSWORD")
|
||||
|
||||
if all([host, name, user, password]):
|
||||
return f"postgresql://{user}:{password}@{host}:{port}/{name}"
|
||||
return None
|
||||
|
||||
|
||||
# ===================================
|
||||
# Bot Status/Presence
|
||||
# ===================================
|
||||
def get_status() -> discord.Activity:
|
||||
"""Get bot status/presence"""
|
||||
status_type = os.getenv("STATUS_TYPE", "listening").lower()
|
||||
status_text = os.getenv("STATUS_TEXT", "Zilean's Theme")
|
||||
status_url = os.getenv("STATUS_URL")
|
||||
|
||||
return translate_status(status_type, status_text, status_url)
|
||||
|
||||
|
||||
def translate_status(
|
||||
status_type: str,
|
||||
status_text: str,
|
||||
status_url: Optional[str] = None
|
||||
) -> discord.Activity:
|
||||
"""Convert status type string to Discord Activity"""
|
||||
|
||||
status_map = {
|
||||
"playing": discord.ActivityType.playing,
|
||||
"streaming": discord.ActivityType.streaming,
|
||||
"listening": discord.ActivityType.listening,
|
||||
"watching": discord.ActivityType.watching,
|
||||
"competing": discord.ActivityType.competing,
|
||||
}
|
||||
|
||||
activity_type = status_map.get(status_type)
|
||||
if not activity_type:
|
||||
raise ValueError(f"Invalid status type: {status_type}")
|
||||
|
||||
# Streaming requires URL
|
||||
if status_type == "streaming":
|
||||
if not status_url:
|
||||
raise ValueError("Streaming status requires STATUS_URL")
|
||||
return discord.Activity(
|
||||
type=activity_type,
|
||||
name=status_text,
|
||||
url=status_url
|
||||
)
|
||||
|
||||
return discord.Activity(type=activity_type, name=status_text)
|
||||
|
||||
|
||||
# ===================================
|
||||
# Color Scheme
|
||||
# ===================================
|
||||
def get_color(color_name: str) -> Color:
|
||||
"""Get color from environment (hex format)"""
|
||||
color_map = {
|
||||
"primary": os.getenv("COLOR_PRIMARY", "#7289DA"),
|
||||
"success": os.getenv("COLOR_SUCCESS", "#43B581"),
|
||||
"error": os.getenv("COLOR_ERROR", "#F04747"),
|
||||
"warning": os.getenv("COLOR_WARNING", "#FAA61A"),
|
||||
}
|
||||
|
||||
hex_value = color_map.get(color_name.lower())
|
||||
if not hex_value:
|
||||
# Default to Discord blurple
|
||||
hex_value = "#7289DA"
|
||||
|
||||
return Color.from_str(hex_value)
|
||||
|
||||
|
||||
# ===================================
|
||||
# Logging Configuration
|
||||
# ===================================
|
||||
def get_log_level() -> str:
|
||||
"""Get logging level from environment"""
|
||||
return os.getenv("LOG_LEVEL", "INFO").upper()
|
||||
|
||||
|
||||
# ===================================
|
||||
# Legacy Support (for backward compatibility)
|
||||
# ===================================
|
||||
def get_login(bot: str) -> str:
|
||||
"""Legacy function - maps to new get_discord_token()"""
|
||||
# Ignore the 'bot' parameter, use ENVIRONMENT instead
|
||||
return get_discord_token()
|
||||
|
||||
|
||||
# ===================================
|
||||
# Validation
|
||||
# ===================================
|
||||
def validate_config():
|
||||
"""Validate that all required config is present"""
|
||||
errors = []
|
||||
|
||||
# Check Discord token
|
||||
try:
|
||||
get_discord_token()
|
||||
except ValueError as e:
|
||||
errors.append(str(e))
|
||||
|
||||
# Check Spotify creds
|
||||
try:
|
||||
get_spotify_creds()
|
||||
except ValueError as e:
|
||||
errors.append(str(e))
|
||||
|
||||
if errors:
|
||||
error_msg = "\n".join(errors)
|
||||
raise ValueError(f"Configuration errors:\n{error_msg}")
|
||||
|
||||
print(f"✅ Configuration validated (Environment: {ENVIRONMENT})")
|
||||
|
||||
|
||||
# ===================================
|
||||
# Startup Info
|
||||
# ===================================
|
||||
def print_config_info():
|
||||
"""Print configuration summary (without secrets!)"""
|
||||
print("=" * 50)
|
||||
print("🎵 Groovy-Zilean Configuration")
|
||||
print("=" * 50)
|
||||
print(f"Environment: {ENVIRONMENT.upper()}")
|
||||
print(f"Prefix: {get_prefix()}")
|
||||
print(f"Database: {get_db_path()}")
|
||||
print(f"Log Level: {get_log_level()}")
|
||||
print(f"Spotify: {'Configured ✅' if os.getenv('SPOTIFY_CLIENT_ID') else 'Not configured ❌'}")
|
||||
print("=" * 50)
|
||||
|
||||
Reference in New Issue
Block a user