Consolidate config validation and variables to config.py

This commit is contained in:
Parker M. 2024-06-26 17:43:12 -05:00
parent e418e06ce6
commit cf7e9c0bc3
No known key found for this signature in database
GPG Key ID: 95CD2E0C7E329F2A
23 changed files with 221 additions and 230 deletions

View File

@ -1,6 +1,6 @@
from lavalink import LoadType
from global_variables import CLIENT
from config import CLIENT
async def add_song_recommendations(bot_user, player, number, inputs, retries: int = 1):

View File

@ -3,8 +3,7 @@ from discord.ext import commands, tasks
import os
import requests
from validate_config import create_config
from global_variables import LOG, BOT_TOKEN, SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET
import config
class MyBot(commands.Bot):
@ -16,7 +15,6 @@ class MyBot(commands.Bot):
)
async def setup_hook(self):
create_config()
get_access_token.start()
for ext in os.listdir("./code/cogs"):
if ext.endswith(".py"):
@ -34,7 +32,7 @@ bot.autoplay = [] # guild_id, guild_id, etc.
@bot.event
async def on_ready():
LOG.info(f"{bot.user} has connected to Discord.")
config.LOG.info(f"{bot.user} has connected to Discord.")
@tasks.loop(minutes=45)
@ -42,8 +40,8 @@ async def get_access_token():
auth_url = "https://accounts.spotify.com/api/token"
data = {
"grant_type": "client_credentials",
"client_id": SPOTIFY_CLIENT_ID,
"client_secret": SPOTIFY_CLIENT_SECRET,
"client_id": config.SPOTIFY_CLIENT_ID,
"client_secret": config.SPOTIFY_CLIENT_SECRET,
}
response = requests.post(auth_url, data=data)
access_token = response.json()["access_token"]
@ -51,4 +49,6 @@ async def get_access_token():
if __name__ == "__main__":
bot.run(BOT_TOKEN)
config_contents = config.load_config()
config.validate_config(config_contents)
bot.run(config.TOKEN)

View File

@ -6,7 +6,7 @@ from cogs.music import Music
from typing import Literal
from ai_recommendations import add_song_recommendations
from global_variables import BOT_COLOR
from config import BOT_COLOR
class Autoplay(commands.Cog):

View File

@ -2,7 +2,7 @@ import discord
from discord import app_commands
from discord.ext import commands
from global_variables import BOT_COLOR, BUG_CHANNEL_ID
from config import BOT_COLOR, BUG_CHANNEL_ID
class BugReport(discord.ui.Modal, title="Report a bug"):

View File

@ -4,7 +4,7 @@ from discord import app_commands
from discord.ext import commands
from cogs.music import Music
from global_variables import BOT_COLOR
from config import BOT_COLOR
class Clear(commands.Cog):

View File

@ -2,7 +2,7 @@ import discord
from discord import app_commands
from discord.ext import commands
from global_variables import BOT_COLOR, FEEDBACK_CHANNEL_ID
from config import BOT_COLOR, FEEDBACK_CHANNEL_ID
class FeedbackForm(discord.ui.Modal, title="Give feedback about the bot"):

View File

@ -2,7 +2,7 @@ import discord
from discord.ext import commands
from discord import app_commands
from global_variables import BOT_COLOR, BOT_INVITE_LINK
from config import BOT_COLOR, BOT_INVITE_LINK
commands_and_descriptions = {
"play": {

View File

@ -3,16 +3,21 @@ from discord.ext import commands
import lavalink
from lavalink import errors
from global_variables import (
from config import (
LAVALINK_HOST,
LAVALINK_PASSWORD,
LAVALINK_PORT,
LOG,
CheckPlayerError,
)
from ai_recommendations import add_song_recommendations
class CheckPlayerError(discord.app_commands.AppCommandError):
def __init__(self, info) -> None:
self.info = info
super().__init__()
class LavalinkVoiceClient(discord.VoiceProtocol):
"""
This is the preferred way to handle external voice sending
@ -104,13 +109,15 @@ class Music(commands.Cog):
host=LAVALINK_HOST,
port=LAVALINK_PORT,
password=LAVALINK_PASSWORD,
region='us-central',
connect=False
region="us-central",
connect=False,
) # Host, Port, Password, Region, Connect
try:
await node.get_version()
except lavalink.errors.ClientError:
LOG.error("Authentication to lavalink node failed. Check your login credentials.")
LOG.error(
"Authentication to lavalink node failed. Check your login credentials."
)
else:
await node.connect()
LOG.info(f"Connected to lavalink node {node.name}")
@ -125,7 +132,9 @@ class Music(commands.Cog):
async def create_player(interaction: discord.Interaction):
"""Create a player for the guild associated with the interaction, or raise an error"""
try:
player = interaction.client.lavalink.player_manager.create(interaction.guild.id)
player = interaction.client.lavalink.player_manager.create(
interaction.guild.id
)
except errors.ClientError:
raise CheckPlayerError(
{

View File

@ -5,7 +5,7 @@ from discord.ext import commands
from cogs.music import Music
import lavalink
from global_variables import BOT_COLOR
from config import BOT_COLOR
class NowPlaying(commands.Cog):

View File

@ -3,7 +3,7 @@ from discord import app_commands
import sqlite3
import discord
from global_variables import BOT_COLOR
from config import BOT_COLOR
class Stats(commands.Cog):

View File

@ -4,7 +4,7 @@ from discord import app_commands
from discord.ext import commands
from cogs.music import Music
from global_variables import BOT_COLOR
from config import BOT_COLOR
class Pause(commands.Cog):

View File

@ -8,7 +8,7 @@ import re
from cogs.music import LavalinkVoiceClient
import requests
from global_variables import BOT_COLOR
from config import BOT_COLOR
from custom_source import CustomSource

View File

@ -6,7 +6,7 @@ from cogs.music import Music
import math
import lavalink
from global_variables import BOT_COLOR
from config import BOT_COLOR
class Queue(commands.Cog):

View File

@ -4,7 +4,7 @@ from discord import app_commands
from discord.ext import commands
from cogs.music import Music
from global_variables import BOT_COLOR
from config import BOT_COLOR
class Remove(commands.Cog):

View File

@ -4,7 +4,7 @@ from discord import app_commands
from discord.ext import commands
from cogs.music import Music
from global_variables import BOT_COLOR
from config import BOT_COLOR
class Repeat(commands.GroupCog, name="repeat"):

View File

@ -4,7 +4,7 @@ from discord import app_commands
from discord.ext import commands
from cogs.music import Music
from global_variables import BOT_COLOR
from config import BOT_COLOR
class Resume(commands.Cog):

View File

@ -4,7 +4,7 @@ from discord import app_commands
from discord.ext import commands
from cogs.music import Music
from global_variables import BOT_COLOR
from config import BOT_COLOR
class Shuffle(commands.GroupCog, name="shuffle"):

View File

@ -5,7 +5,7 @@ from discord.ext import commands
from cogs.music import Music
import asyncio
from global_variables import BOT_COLOR
from config import BOT_COLOR
from custom_source import LoadError

View File

@ -3,9 +3,9 @@ from discord.ext import commands
from discord import app_commands
from discord.ext.commands.errors import *
import datetime
from lavalink import errors
from global_variables import BOT_COLOR, CheckPlayerError
from cogs.music import CheckPlayerError
from config import BOT_COLOR
from custom_source import LoadError

View File

@ -4,7 +4,7 @@ from discord import app_commands
from discord.ext import commands
from cogs.music import Music
from global_variables import BOT_COLOR
from config import BOT_COLOR
class Stop(commands.Cog):

180
code/config.py Normal file
View File

@ -0,0 +1,180 @@
import configparser
import re
import validators
import sys
import discord
import openai
import logging
from colorlog import ColoredFormatter
log_level = logging.DEBUG
log_format = (
" %(log_color)s%(levelname)-8s%(reset)s | %(log_color)s%(message)s%(reset)s"
)
logging.root.setLevel(log_level)
formatter = ColoredFormatter(log_format)
stream = logging.StreamHandler()
stream.setLevel(log_level)
stream.setFormatter(formatter)
LOG = logging.getLogger("pythonConfig")
LOG.setLevel(log_level)
LOG.addHandler(stream)
TOKEN = None
BOT_COLOR = None
BOT_INVITE_LINK = None
FEEDBACK_CHANNEL_ID = None
BUG_CHANNEL_ID = None
SPOTIFY_CLIENT_ID = None
SPOTIFY_CLIENT_SECRET = None
CLIENT = None
LAVALINK_HOST = None
LAVALINK_PORT = None
LAVALINK_PASSWORD = None
"""
Load the config.ini file and return the contents for validation or
create a new templated config.ini file if it doesn't exist.
"""
def load_config():
try:
with open("config.ini", "r") as f:
file_contents = f.read()
return file_contents
except FileNotFoundError:
config = configparser.ConfigParser()
config["BOT_INFO"] = {
"TOKEN": "",
"BOT_COLOR": "",
"BOT_INVITE_LINK": "",
"FEEDBACK_CHANNEL_ID": "",
"BUG_CHANNEL_ID": "",
}
config["SPOTIFY"] = {
"SPOTIFY_CLIENT_ID": "",
"SPOTIFY_CLIENT_SECRET": "",
}
config["OPENAI"] = {
"OPENAI_API_KEY": "",
}
config["LAVALINK"] = {
"HOST": "",
"PORT": "",
"PASSWORD": "",
}
with open("config.ini", "w") as configfile:
config.write(configfile)
sys.exit(
LOG.critical(
"Configuration file `config.ini` has been generated. Please fill out all of the necessary information. Refer to the docs for information on what a specific configuration option is."
)
)
"""
Validate all of the options in the config.ini file.
"""
def validate_config(file_contents):
global TOKEN, BOT_COLOR, BOT_INVITE_LINK, FEEDBACK_CHANNEL_ID, BUG_CHANNEL_ID, SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, CLIENT, LAVALINK_HOST, LAVALINK_PORT, LAVALINK_PASSWORD
config = configparser.ConfigParser()
config.read_string(file_contents)
hex_pattern_one = "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
hex_pattern_two = "^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
errors = 0
# Make sure all sections are present
if ["BOT_INFO", "SPOTIFY", "OPENAI", "LAVALINK"] != config.sections():
sys.exit(
LOG.critical(
"Missing sections in config.ini file. Delete the file and re-run the bot to generate a blank config.ini file."
)
)
if ["token","bot_color","bot_invite_link", "feedback_channel_id","bug_channel_id",] != config.options("BOT_INFO"):
sys.exit(
LOG.critical(
"Missing options in BOT_INFO section of config.ini file. Delete the file and re-run the bot to generate a blank config.ini file."
)
)
if ["spotify_client_id", "spotify_client_secret"] != config.options("SPOTIFY"):
sys.exit(
LOG.critical(
"Missing options in SPOTIFY section of config.ini file. Delete the file and re-run the bot to generate a blank config.ini file."
)
)
if ["openai_api_key"] != config.options("OPENAI"):
sys.exit(
LOG.critical(
"Missing options in OPENAI section of config.ini file. Delete the file and re-run the bot to generate a blank config.ini file."
)
)
if ["host", "port", "password"] != config.options("LAVALINK"):
sys.exit(
LOG.critical(
"Missing options in LAVALINK section of config.ini file. Delete the file and re-run the bot to generate a blank config.ini file."
)
)
# Make sure BOT_COLOR is a valid hex color
if not bool(re.match(hex_pattern_one, config["BOT_INFO"]["BOT_COLOR"])) and not bool(re.match(hex_pattern_two, config["BOT_INFO"]["BOT_COLOR"])):
LOG.error("BOT_COLOR is not a valid hex color.")
errors += 1
else:
BOT_COLOR = discord.Color(int((config["BOT_INFO"]["BOT_COLOR"]).replace("#", ""), 16))
# Make sure BOT_INVITE_LINK is a valid URL
if not validators.url(config["BOT_INFO"]["BOT_INVITE_LINK"]):
LOG.error("BOT_INVITE_LINK is not a valid URL.")
errors += 1
else:
BOT_INVITE_LINK = config["BOT_INFO"]["BOT_INVITE_LINK"]
# Make sure FEEDBACK_CHANNEL_ID is either exactly 0 or 19 characters long
if len(config["BOT_INFO"]["FEEDBACK_CHANNEL_ID"]) != 0:
if len(config["BOT_INFO"]["FEEDBACK_CHANNEL_ID"]) != 19:
LOG.error("FEEDBACK_CHANNEL_ID is not a valid Discord channel ID.")
errors += 1
else:
FEEDBACK_CHANNEL_ID = int(config["BOT_INFO"]["FEEDBACK_CHANNEL_ID"])
# Make sure BUG_CHANNEL_ID is either exactly 0 or 19 characters long
if len(config["BOT_INFO"]["BUG_CHANNEL_ID"]) != 0:
if len(config["BOT_INFO"]["BUG_CHANNEL_ID"]) != 19:
LOG.error("BUG_CHANNEL_ID is not a valid Discord channel ID.")
errors += 1
else:
BUG_CHANNEL_ID = int(config["BOT_INFO"]["BUG_CHANNEL_ID"])
# Assign the rest of the variables
TOKEN = config["BOT_INFO"]["TOKEN"]
SPOTIFY_CLIENT_ID = config["SPOTIFY"]["SPOTIFY_CLIENT_ID"]
SPOTIFY_CLIENT_SECRET = config["SPOTIFY"]["SPOTIFY_CLIENT_SECRET"]
CLIENT = openai.OpenAI(api_key=config["OPENAI"]["OPENAI_API_KEY"])
LAVALINK_HOST = config["LAVALINK"]["HOST"]
LAVALINK_PORT = config["LAVALINK"]["PORT"]
LAVALINK_PASSWORD = config["LAVALINK"]["PASSWORD"]
if errors > 0:
sys.exit(
LOG.critical(
f"Found {errors} error(s) in the config.ini file. Please fix them and try again."
)
)

View File

@ -1,71 +0,0 @@
import configparser
import logging
from colorlog import ColoredFormatter
import discord
import openai
log_level = logging.DEBUG
log_format = (
" %(log_color)s%(levelname)-8s%(reset)s | %(log_color)s%(message)s%(reset)s"
)
logging.root.setLevel(log_level)
formatter = ColoredFormatter(log_format)
stream = logging.StreamHandler()
stream.setLevel(log_level)
stream.setFormatter(formatter)
LOG = logging.getLogger("pythonConfig")
LOG.setLevel(log_level)
LOG.addHandler(stream)
try:
with open("config.ini", "r") as f:
file_contents = f.read()
except FileNotFoundError:
config = configparser.ConfigParser()
config["BOT_INFO"] = {
"TOKEN": "",
"BOT_COLOR": "",
"FEEDBACK_CHANNEL_ID": "",
"SPOTIFY_CLIENT_ID": "",
"SPOTIFY_CLIENT_SECRET": "",
"OPENAI_API_KEY": "",
"BUG_CHANNEL_ID": "",
"BOT_INVITE_LINK": ""
}
config["LAVALINK"] = {"HOST": "", "PORT": "", "PASSWORD": ""}
with open("config.ini", "w") as configfile:
config.write(configfile)
LOG.error(
"Configuration file `config.ini` has been generated. Please fill out all of the necessary information. Refer to the docs for information on what a specific configuration option is."
)
exit()
config = configparser.ConfigParser()
config.read_string(file_contents)
BOT_TOKEN = config["BOT_INFO"]["TOKEN"]
BOT_COLOR = discord.Color(int((config["BOT_INFO"]["BOT_COLOR"]).replace("#", ""), 16))
FEEDBACK_CHANNEL_ID = int(config["BOT_INFO"]["FEEDBACK_CHANNEL_ID"])
SPOTIFY_CLIENT_ID = config["BOT_INFO"]["SPOTIFY_CLIENT_ID"]
SPOTIFY_CLIENT_SECRET = config["BOT_INFO"]["SPOTIFY_CLIENT_SECRET"]
CLIENT = openai.OpenAI(api_key=config["BOT_INFO"]["OPENAI_API_KEY"])
BUG_CHANNEL_ID = int(config["BOT_INFO"]["BUG_CHANNEL_ID"])
BOT_INVITE_LINK = config["BOT_INFO"]["BOT_INVITE_LINK"]
LAVALINK_HOST = config["LAVALINK"]["HOST"]
LAVALINK_PORT = config["LAVALINK"]["PORT"]
LAVALINK_PASSWORD = config["LAVALINK"]["PASSWORD"]
class CheckPlayerError(discord.app_commands.AppCommandError):
def __init__(self, info) -> None:
self.info = info
super().__init__()

View File

@ -1,127 +0,0 @@
import configparser
import re
import validators
from global_variables import LOG
pattern_1 = "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
pattern_2 = "^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
def validate_config(file_contents):
config = configparser.ConfigParser()
config.read_string(file_contents)
errors = 0
try:
# Validate TOKEN
if not config["BOT_INFO"]["TOKEN"]:
LOG.critical("TOKEN has not been set.")
errors += 1
# Validate BOT_COLOR
if not config["BOT_INFO"]["BOT_COLOR"]:
LOG.critical("BOT_COLOR has not been set.")
errors += 1
elif not bool(
re.match(pattern_1, config["BOT_INFO"]["BOT_COLOR"])
) and not bool(re.match(pattern_2, config["BOT_INFO"]["BOT_COLOR"])):
LOG.critical("BOT_COLOR is not a valid hex color.")
errors += 1
# Validate FEEDBACK_CHANNEL_ID
if not config["BOT_INFO"]["FEEDBACK_CHANNEL_ID"]:
LOG.critical("FEEDBACK_CHANNEL_ID has not been set.")
errors += 1
elif len(config["BOT_INFO"]["FEEDBACK_CHANNEL_ID"]) != 19:
LOG.critical("FEEDBACK_CHANNEL_ID is not a valid Discord channel ID.")
errors += 1
# Validate SPOTIFY_CLIENT_ID
if not config["BOT_INFO"]["SPOTIFY_CLIENT_ID"]:
LOG.critical("SPOTIFY_CLIENT_ID has not been set.")
errors += 1
# Validate SPOTIFY_CLIENT_SECRET
if not config["BOT_INFO"]["SPOTIFY_CLIENT_SECRET"]:
LOG.critical("SPOTIFY_CLIENT_SECRET has not been set.")
errors += 1
# Validate OPENAI_API_KEY
if not config["BOT_INFO"]["OPENAI_API_KEY"]:
LOG.critical("OPENAI_API_KEY has not bee set.")
errors += 1
# Validate BUG_CHANNEL_ID
if not config["BOT_INFO"]["BUG_CHANNEL_ID"]:
LOG.critical("BUG_CHANNEL_ID has not been set.")
errors += 1
elif len(config["BOT_INFO"]["BUG_CHANNEL_ID"]) != 19:
LOG.critical("BUG_CHANNEL_ID is not a valid Discord channel ID.")
errors += 1
# Validate BOT_INVITE_LINK
if not config["BOT_INFO"]["BOT_INVITE_LINK"]:
LOG.critical("BOT_INVITE_LINK has not been set.")
errors += 1
elif not validators.url(config["BOT_INFO"]["BOT_INVITE_LINK"]):
LOG.critical("BOT_INVITE_LINK is not a valid URL.")
errors += 1
# Validate LAVALINK
# Validate HOST
if not config["LAVALINK"]["HOST"]:
LOG.critical("HOST has not been set.")
errors += 1
# Validate PORT
if not config["LAVALINK"]["PORT"]:
LOG.critical("PORT has not been set.")
errors += 1
# Validate PASSWORD
if not config["LAVALINK"]["PASSWORD"]:
LOG.critical("HOST has not been set.")
errors += 1
if errors > 0:
LOG.critical(
"Configuration checks failed. Correct your config.ini file and run again."
)
exit()
else:
LOG.info("Configuration checks passed. Starting bot.")
except KeyError:
LOG.critical(
"You are missing at least one of the configuration options from your config.ini file. In order to regenerate this file with all of the proper options, please delete it and re-run the `bot.py` file."
)
exit()
def create_config():
try:
with open("config.ini", "r") as f:
file_contents = f.read()
validate_config(file_contents)
except FileNotFoundError:
config = configparser.ConfigParser()
config["BOT_INFO"] = {
"TOKEN": "",
"BOT_COLOR": "",
"FEEDBACK_CHANNEL_ID": "",
"SPOTIFY_CLIENT_ID": "",
"SPOTIFY_CLIENT_SECRET": "",
"OPENAI_API_KEY": "",
"BUG_CHANNEL_ID": "",
"BOT_INVITE_LINK": ""
}
config["LAVALINK"] = {"HOST": "", "PORT": "", "PASSWORD": ""}
with open("config.ini", "w") as configfile:
config.write(configfile)
LOG.error(
"Configuration file `config.ini` has been generated. Please fill out all of the necessary information. Refer to the docs for information on what a specific configuration option is."
)
exit()