diff --git a/code/ai_recommendations.py b/code/ai_recommendations.py index 3aa2da5..9f73976 100644 --- a/code/ai_recommendations.py +++ b/code/ai_recommendations.py @@ -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): diff --git a/code/bot.py b/code/bot.py index 983e92f..2e3123f 100644 --- a/code/bot.py +++ b/code/bot.py @@ -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) diff --git a/code/cogs/autoplay.py b/code/cogs/autoplay.py index cc5de91..4f5dc7a 100644 --- a/code/cogs/autoplay.py +++ b/code/cogs/autoplay.py @@ -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): diff --git a/code/cogs/bug.py b/code/cogs/bug.py index 67ae258..4f8606d 100644 --- a/code/cogs/bug.py +++ b/code/cogs/bug.py @@ -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"): diff --git a/code/cogs/clear.py b/code/cogs/clear.py index 8711701..79b2f0f 100644 --- a/code/cogs/clear.py +++ b/code/cogs/clear.py @@ -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): diff --git a/code/cogs/feedback.py b/code/cogs/feedback.py index e04eecf..149b112 100644 --- a/code/cogs/feedback.py +++ b/code/cogs/feedback.py @@ -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"): diff --git a/code/cogs/help.py b/code/cogs/help.py index ee1ad19..2cf0f54 100644 --- a/code/cogs/help.py +++ b/code/cogs/help.py @@ -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": { diff --git a/code/cogs/music.py b/code/cogs/music.py index e62c9e0..68870e1 100644 --- a/code/cogs/music.py +++ b/code/cogs/music.py @@ -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( { diff --git a/code/cogs/nowplaying.py b/code/cogs/nowplaying.py index 84898cd..928ac51 100644 --- a/code/cogs/nowplaying.py +++ b/code/cogs/nowplaying.py @@ -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): diff --git a/code/cogs/owner/stats.py b/code/cogs/owner/stats.py index d81ec13..ab0f0ad 100644 --- a/code/cogs/owner/stats.py +++ b/code/cogs/owner/stats.py @@ -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): diff --git a/code/cogs/pause.py b/code/cogs/pause.py index 522a376..656075c 100644 --- a/code/cogs/pause.py +++ b/code/cogs/pause.py @@ -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): diff --git a/code/cogs/play.py b/code/cogs/play.py index 855d645..67c3e04 100644 --- a/code/cogs/play.py +++ b/code/cogs/play.py @@ -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 diff --git a/code/cogs/queue.py b/code/cogs/queue.py index 1b26991..d356b54 100644 --- a/code/cogs/queue.py +++ b/code/cogs/queue.py @@ -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): diff --git a/code/cogs/remove.py b/code/cogs/remove.py index 6234d67..2188331 100644 --- a/code/cogs/remove.py +++ b/code/cogs/remove.py @@ -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): diff --git a/code/cogs/repeat.py b/code/cogs/repeat.py index 95f430b..4424b72 100644 --- a/code/cogs/repeat.py +++ b/code/cogs/repeat.py @@ -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"): diff --git a/code/cogs/resume.py b/code/cogs/resume.py index 51d3348..4d95682 100644 --- a/code/cogs/resume.py +++ b/code/cogs/resume.py @@ -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): diff --git a/code/cogs/shuffle.py b/code/cogs/shuffle.py index ba78957..7ba9cf9 100644 --- a/code/cogs/shuffle.py +++ b/code/cogs/shuffle.py @@ -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"): diff --git a/code/cogs/skip.py b/code/cogs/skip.py index 26c6639..c1692c7 100644 --- a/code/cogs/skip.py +++ b/code/cogs/skip.py @@ -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 diff --git a/code/cogs/slash_handlers.py b/code/cogs/slash_handlers.py index ebe4a81..019ee45 100644 --- a/code/cogs/slash_handlers.py +++ b/code/cogs/slash_handlers.py @@ -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 diff --git a/code/cogs/stop.py b/code/cogs/stop.py index f0641d1..fd7bb5d 100644 --- a/code/cogs/stop.py +++ b/code/cogs/stop.py @@ -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): diff --git a/code/config.py b/code/config.py new file mode 100644 index 0000000..5817cbd --- /dev/null +++ b/code/config.py @@ -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." + ) + ) \ No newline at end of file diff --git a/code/global_variables.py b/code/global_variables.py deleted file mode 100644 index 8f3bb9a..0000000 --- a/code/global_variables.py +++ /dev/null @@ -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__() diff --git a/code/validate_config.py b/code/validate_config.py deleted file mode 100644 index c0c38c5..0000000 --- a/code/validate_config.py +++ /dev/null @@ -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()