390 lines
11 KiB
Python
390 lines
11 KiB
Python
"""
|
||
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(':')
|