tuxbot-bot/tuxbot/setup.py

377 lines
9 KiB
Python
Raw Normal View History

2020-06-03 16:24:38 +00:00
import json
import logging
import re
import sys
from pathlib import Path
2020-06-03 17:41:30 +00:00
from typing import NoReturn, Union, List, Set
2020-06-03 16:24:38 +00:00
import click
from colorama import Fore, Style, init
2020-06-03 17:41:30 +00:00
from tuxbot.core.data_manager import config_dir, app_dir
2020-06-03 16:24:38 +00:00
2020-06-03 17:41:30 +00:00
init()
2020-06-03 16:24:38 +00:00
try:
config_dir.mkdir(parents=True, exist_ok=True)
except PermissionError:
print(f"mkdir: cannot create directory '{config_dir}': Permission denied")
sys.exit(1)
config_file = config_dir / "config.json"
def load_existing_config() -> dict:
2020-06-03 22:46:53 +00:00
"""Loading and returning configs.
Returns
-------
dict
a dict containing all configurations.
"""
2020-06-03 16:24:38 +00:00
if not config_file.exists():
return {}
with config_file.open() as fs:
return json.load(fs)
instances_data = load_existing_config()
if not instances_data:
instances_list = []
else:
instances_list = list(instances_data.keys())
2020-06-03 22:46:53 +00:00
def save_config(name: str, data: dict, delete=False) -> NoReturn:
"""save data in config file.
Parameters
----------
name:str
name of instance.
data:dict
settings for `name` instance.
delete:bool
delete or no data.
"""
2020-06-03 16:24:38 +00:00
_config = load_existing_config()
if delete and name in _config:
_config.pop(name)
else:
_config[name] = data
with config_file.open("w") as fs:
json.dump(_config, fs, indent=4)
def get_name() -> str:
2020-06-03 22:46:53 +00:00
"""Get instance name via input.
Returns
-------
str
The instance name choose by user.
"""
2020-06-03 16:24:38 +00:00
name = ""
while not name:
print(
"What name do you want to give this instance?\n"
"(valid characters: A-Z, a-z, 0-9, _, -)"
)
name = input("> ")
if re.fullmatch(r"[a-zA-Z0-9_\-]*", name) is None:
print()
print(
Fore.RED
+ "ERROR: Invalid characters provided"
+ Style.RESET_ALL
)
name = ""
return name
def get_data_dir(instance_name: str) -> Path:
2020-06-03 22:46:53 +00:00
"""Returning data path.
Parameters
----------
instance_name:str
Instance name.
Returns
-------
Path
The data config path corresponding to the instance.
"""
2020-06-03 16:24:38 +00:00
data_path = Path(app_dir.user_data_dir) / "data" / instance_name
data_path_input = ""
print()
def make_data_dir(path: Path) -> Union[Path, str]:
try:
path.mkdir(parents=True, exist_ok=True)
except OSError:
print()
print(
Fore.RED
+ f"mkdir: cannot create directory '{path}':"
f" Permission denied"
+ Style.RESET_ALL
)
path = ""
return path
while not data_path_input:
print(
"where do you want to save the configurations?\n"
"Press [enter] to keep the default path"
)
print()
print(f"Default: {data_path}")
data_path_input = input("> ")
if data_path_input != '':
data_path_input = Path(data_path_input)
try:
exists = data_path_input.exists()
except OSError:
print()
print(
Fore.RED
+ "Impossible to verify the validity of the path, "
"make sure it does not contain any invalid characters."
+ Style.RESET_ALL
)
data_path_input = ""
exists = False
if data_path_input and not exists:
data_path_input = make_data_dir(data_path_input)
else:
data_path_input = make_data_dir(data_path)
print()
print(
f"You have chosen {data_path_input} to be your config directory for "
f"`{instance_name}` instance"
)
if not click.confirm("Please confirm", default=True):
print("Rerun the process to redo this configuration.")
sys.exit(0)
(data_path_input / 'core').mkdir(parents=True, exist_ok=True)
(data_path_input / 'cogs').mkdir(parents=True, exist_ok=True)
2020-06-03 17:41:30 +00:00
(data_path_input / 'logs').mkdir(parents=True, exist_ok=True)
2020-06-03 16:24:38 +00:00
return data_path_input
def get_token() -> str:
2020-06-03 22:46:53 +00:00
"""Get token via input.
Returns
-------
str
The token choose by user.
"""
2020-06-03 16:24:38 +00:00
token = ""
while not token:
print(
"Please enter the bot token\n"
"(you can find it at https://discord.com/developers/applications)"
)
token = input("> ")
if re.fullmatch(r"([a-zA-Z0-9]{24}\.[a-zA-Z0-9_]{6}\.[a-zA-Z0-9_\-]{27}|mfa\.[a-zA-Z0-9_\-]{84})", token) is None:
print(
Fore.RED
+ "ERROR: Invalid token provided"
+ Style.RESET_ALL
)
token = ""
return token
2020-06-03 17:41:30 +00:00
def get_multiple(question: str, confirmation: str, value_type: type)\
-> List[Union[str, int]]:
2020-06-03 22:46:53 +00:00
"""Give possibility to user to fill multiple value.
Parameters
----------
question:str
First question.
confirmation:str
Asking text if user want to add another.
value_type:type
The type of values inside the list.
Returns
-------
List[Union[str, int]]
List containing user filled values.
"""
2020-06-03 17:41:30 +00:00
print(question)
user_input = input('> ')
if not user_input:
return []
values = [user_input]
2020-06-03 16:24:38 +00:00
2020-06-03 17:41:30 +00:00
while click.confirm(confirmation, default=False):
values.append(value_type(input('> ')))
2020-06-03 16:24:38 +00:00
return values
2020-06-03 16:24:38 +00:00
def additional_config() -> dict:
2020-06-03 22:46:53 +00:00
"""Asking for additional configs in cogs.
Returns
-------
dict:
Dict with cog name as key and configs as value.
"""
2020-06-03 16:24:38 +00:00
p = Path(r'tuxbot/cogs').glob('**/additional_config.json')
datas = {}
for file in p:
print()
cog_name = str(file.parent).split('/')[-1]
datas[cog_name] = {}
with file.open('r') as f:
data = json.load(f)
print(f"\n==Configuration for `{cog_name}` module==")
for key, value in data.items():
print()
print(value['description'])
datas[cog_name][key] = input('> ')
return datas
def finish_setup(data_dir: Path) -> NoReturn:
2020-06-03 22:46:53 +00:00
"""Configs who directly refer to the bot.
Parameters
----------
data_dir:Path
Where to save configs.
"""
2020-06-03 16:24:38 +00:00
print("Now, it's time to finish this setup by giving bot informations\n")
token = get_token()
print()
2020-06-03 17:41:30 +00:00
prefixes = get_multiple(
"Choice a (or multiple) prefix for the bot",
"Add another prefix ?",
str
)
mentionable = click.confirm(
"Does the bot answer if it's mentioned?",
default=True
)
owners_id = get_multiple(
"Give the owner id of this bot",
"Add another owner ?",
int
)
2020-06-03 16:24:38 +00:00
cogs_config = additional_config()
core_file = data_dir / 'core' / 'settings.json'
core = {
'token': token,
'prefixes': prefixes,
'mentionable': mentionable,
2020-06-03 17:41:30 +00:00
'owners_id': owners_id,
2020-06-04 22:29:14 +00:00
'locale': "en-US"
2020-06-03 16:24:38 +00:00
}
with core_file.open("w") as fs:
json.dump(core, fs, indent=4)
for cog, data in cogs_config.items():
data_cog_dir = data_dir / 'cogs' / cog
data_cog_dir.mkdir(parents=True, exist_ok=True)
data_cog_file = data_cog_dir / 'settings.json'
with data_cog_file.open("w") as fs:
json.dump(data, fs, indent=4)
def basic_setup() -> NoReturn:
2020-06-03 22:46:53 +00:00
"""Configs who refer to instances.
"""
2020-06-03 16:24:38 +00:00
print("Hi ! it's time for you to give me informations about you instance")
name = get_name()
data_dir = get_data_dir(name)
configs = load_existing_config()
instance_config = configs[name] if name in instances_list else {}
instance_config["DATA_PATH"] = str(data_dir.resolve())
instance_config["IS_RUNNING"] = False
if name in instances_list:
print()
print(
Fore.RED
+ f"WARNING: An instance named `{name}` already exists "
f"Continuing will overwrite this instance configs."
+ Style.RESET_ALL
)
if not click.confirm("Are you sure you want to continue?",
default=False):
print("Abandon...")
sys.exit(0)
save_config(name, instance_config)
print("\n"*4)
finish_setup(data_dir)
print()
print(
f"Instance successfully created! "
f"You can now run `tuxbot {name}` to launch this instance"
)
2020-06-03 22:46:53 +00:00
def setup() -> NoReturn:
2020-06-03 16:24:38 +00:00
try:
"""Create a new instance."""
level = logging.DEBUG
2020-06-03 17:41:30 +00:00
base_logger = logging.getLogger("tuxbot")
2020-06-03 16:24:38 +00:00
base_logger.setLevel(level)
formatter = logging.Formatter(
"[{asctime}] [{levelname}] {name}: {message}",
datefmt="%Y-%m-%d %H:%M:%S", style="{"
)
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(formatter)
base_logger.addHandler(stdout_handler)
basic_setup()
except KeyboardInterrupt:
print("Exiting...")
if __name__ == "__main__":
setup()