""" Contains utility functions used by parts of the bot. *Functions* ----------- sanitize(data: str, lower_case_value: bool = false) -> dict get_options() -> dict get_credentials() -> dict long_strings() -> dict get_params() -> dict log_this(messages: Union[str, list], channel: str = "", level: int = 20) cap(s: str) -> str make_files() replace_multiple(main_string: str, to_be_replaced: list, new_string: str) -> str emoji_to_command(emoji: str) -> str """ import string import json # Used by long_strings(), get_params() and make_files() import logging # Used for logging import os # Used by make_files() to check if files exist import sys # Used to specify printing for logging import imdb # Used to disable logging for the module BASE_37 = ":" + string.digits + string.ascii_uppercase BASE_128 = list( string.digits + string.ascii_letters + "!#$€£¢¥¤&%()*+,-./;:<=>?@[]_{|}~ `¦§©®«»±µ·¿əʒ" + "ÆØÅÐÉÈÊÇÑÖ" + "æøåðéèêçñö" ) # All of this is logging configuration FORMAT = " %(asctime)s | %(name)-16s | %(levelname)-8s | %(message)s" PRINTFORMAT = "%(asctime)s - %(message)s" DATEFORMAT = "%Y-%m-%d %H:%M:%S" logging.addLevelName(25, "PRINT") loggingConfigParams = { "format": FORMAT, "datefmt": DATEFORMAT, "level": logging.INFO, "filename": "gwendolyn.log" } logging.basicConfig(**loggingConfigParams) logger = logging.getLogger("Gwendolyn") printer = logging.getLogger("printer") handler = logging.StreamHandler(sys.stdout) handler.setFormatter(logging.Formatter(fmt=PRINTFORMAT, datefmt=DATEFORMAT)) printer.addHandler(handler) printer.propagate = False imdb._logging.setLevel("CRITICAL") # pylint: disable=protected-access # Basically disables imdbpy logging, since it's being printed to the # terminal. def sanitize(data: str, lower_case_value: bool = False): """ Sanitize and create a dictionary from a string. Each element is created from a line with a : in it. The key is left of the :, the value is right of it. *Parameters* ------------ data: str The string to create a dict from. lower_case_value: bool = False Whether the value of each element should be lowercase. *Returns* --------- dct: dict The sanitized dictionary of elements. """ data = data.splitlines() dct = {} for line in data: if line[0] != "#" and ":" in line: line_values = line.split(":") line_values[0] = line_values[0].lower() line_values[1] = line_values[1].replace(" ", "") if lower_case_value: line_values[1] = line_values[1].lower() if line_values[0] in ["testing guild ids", "admins"]: line_values[1] = line_values[1].split(",") if all(i.isnumeric() for i in line_values[1]): line_values[1] = [int(i) for i in line_values[1]] if any(i == line_values[1] for i in ["true", "false"]): line_values[1] = (line_values[1] == "true") dct[line_values[0]] = line_values[1] return dct def get_options(): """ Get the bot options as dict. *Returns* --------- options: dict The options of the bot. """ with open("options.txt", "r") as file_pointer: data = sanitize(file_pointer.read(), True) options = {} options["testing"] = data["testing"] options["guild_ids"] = data["testing guild ids"] options["admins"] = data["admins"] return options def get_credentials(): """ Returns the credentials used by the bot as a dict. *Returns* --------- credentials: dict The credentials used by the bot. """ with open("credentials.txt", "r") as file_pointer: data = sanitize(file_pointer.read()) credentials = {} credentials["token"] = data["bot token"] credentials["finnhub_key"] = data["finnhub api key"] credentials["wordnik_key"] = data["wordnik api key"] credentials["mongo_db_user"] = data["mongodb user"] credentials["mongo_db_password"] = data["mongodb password"] credentials["wolfram_alpha_key"] = data["wolframalpha appid"] credentials["radarr_key"] = data["radarr api key"] credentials["sonarr_key"] = data["sonarr api key"] credentials["qbittorrent_username"] = data["qbittorrent username"] credentials["qbittorrent_password"] = data["qbittorrent password"] return credentials def long_strings(): """ Get the data from gwendolyn/resources/long_strings.json. *Returns* --------- data: dict The long strings and their keys. """ with open("gwendolyn/resources/long_strings.json", "r") as file_pointer: data = json.load(file_pointer) return data def get_params(): """ Get the slash command parameters. *Returns* --------- params: dict The parameters for every slash command. """ with open("gwendolyn/resources/slash_parameters.json", "r") as file_pointer: slash_parameters = json.load(file_pointer) options = get_options() if options["testing"]: for parameter in slash_parameters: slash_parameters[parameter]["guild_ids"] = options["guild_ids"] return slash_parameters def log_this(messages, channel: str = "", level: int = 20): """ Log something in Gwendolyn's logs. *Parameters* ------------ messages: Union[str, list] A string or list of strings to be logged. If there are multiple strings and the level is PRINT (25) or higher, only the first string will be printed. channel: str = "" The channel the event to be logged occurred in. Will be logged along with the message(s). level: int = 20 The level to log the message(s) at. If PRINT (25) or higher, the first message will be printed to the console. """ channel = channel.replace("Direct Message with ", "") if isinstance(messages, str): messages = [messages] print_message = messages[0] for i, message in enumerate(messages): if channel != "": messages[i] = f"{message} - ({channel})" # Adds channel ID # to log messages if len(messages) > 1: # Tells user to check the log if there are # more messages there print_message += " (details in log)" if level >= 25: printer.log(level, print_message) for log_message in messages: logger.log(level, log_message) def cap(input_string: str): """ Capitalize a string like a movie title. That means "of" and "the" are not capitalized. *Parameters* ------------ input_string: str The string to capitalized. *Returns* --------- return_string: str The capitalized string. """ no_caps_list = ["of", "the"] word_number = 0 string_list = input_string.split() return_string = '' for word in string_list: word_number += 1 if word not in no_caps_list or word_number == 1: word = word.capitalize() return_string += word+" " return_string = return_string[:-1] return return_string def make_files(): """Create all the files and directories needed by Gwendolyn.""" def make_json_file(path, content): """Create json file if it doesn't exist.""" if not os.path.isfile(path): log_this(path.split("/")[-1]+" didn't exist. Making it now.") with open(path, "w") as file_pointer: json.dump(content, file_pointer, indent=4) def make_txt_file(path, content): """Create txt file if it doesn't exist.""" if not os.path.isfile(path): log_this(path.split("/")[-1]+" didn't exist. Making it now.") with open(path, "w") as file_pointer: file_pointer.write(content) def directory(path): """Create directory if it doesn't exist.""" if not os.path.isdir(path): os.makedirs(path) log_this("The "+path.split("/")[-1]+" directory didn't exist") with open("gwendolyn/resources/starting_files.json") as file_pointer: data = json.load(file_pointer) for path, content in data["json"].items(): make_json_file(path, content) for path, content in data["txt"].items(): make_txt_file(path, content) for path in data["folder"]: directory(path) def replace_multiple(main_string: str, to_be_replaced: list, new_string: str): """ Replace multiple substrings in a string with the same substring. *Parameters* ------------ main_string: str The string to replace substrings in. to_be_replaced: list The substrings to replace. new_string: str The string to replace the substrings with. *Returns* --------- main_string: str The string with the substrings replaced. """ # Iterate over the strings to be replaced for elem in to_be_replaced: # Check if string is in the main string if elem in main_string: # Replace the string main_string = main_string.replace(elem, new_string) return main_string def emoji_to_command(emoji: str): """ Convert emoji to text. *Parameters* ------------ emoji: str The emoji to decipher. *Returns* --------- : str The deciphered string. """ if emoji == "1️⃣": return_value = 1 elif emoji == "2️⃣": return_value = 2 elif emoji == "3️⃣": return_value = 3 elif emoji == "4️⃣": return_value = 4 elif emoji == "5️⃣": return_value = 5 elif emoji == "6️⃣": return_value = 6 elif emoji == "7️⃣": return_value = 7 elif emoji == "🎲": return_value = "roll" elif emoji == "❌": return_value = "none" elif emoji == "✔️": return_value = 1 else: return_value = "" return return_value def encode_id(info: list): letters = list(":".join(info)) dec = 0 for i, letter in enumerate(letters): try: dec += (37**i) * BASE_37.index(letter.upper()) except ValueError: log_this(f"Could not encode letter {letter}", level=30) custom_id = [] while dec: custom_id.append(BASE_128[dec % 128]) dec = dec // 128 custom_id = ''.join(custom_id) log_this(f"Encoded {info} to {custom_id}") return custom_id def decode_id(custom_id: str): letters = list(custom_id) dec = 0 for i, letter in enumerate(letters): dec += (128**i) * BASE_128.index(letter) info_string = [] while dec: info_string.append(BASE_37[dec % 37]) dec = dec // 37 info = ''.join(info_string).split(':') log_this(f"Decoded {custom_id} to be {info}") return ''.join(info_string).split(':')