From 584cd648ff71db17ad024275b81ee9ea5fb45f49 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Sun, 23 Apr 2023 02:59:06 +0700 Subject: [PATCH] style: format code with autopep8 and isort (#19) Format code with autopep8 and isort This commit fixes the style issues introduced in 7b2bb9c according to the output from autopep8 and isort. Details: https://app.deepsource.com/gh/nattadasu/ryuuRyuusei/transform/48839f57-c48c-4cbf-ac0f-80d7eb97da88/ Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> Co-authored-by: Sultan Iskandar Maulana --- classes/anilist.py | 52 +++-- classes/animeapi.py | 68 +++--- classes/database.py | 82 ++++--- classes/excepts.py | 6 +- classes/jikan.py | 19 +- classes/kitsu.py | 17 +- classes/lastfm.py | 11 +- classes/myanimelist.py | 22 +- classes/nekomimidb.py | 6 +- classes/randomorg.py | 64 ++--- classes/rawg.py | 26 ++- classes/simkl.py | 84 ++++--- classes/thecolorapi.py | 18 +- classes/tmdb.py | 5 +- classes/trakt.py | 16 +- dev.py | 17 +- extensions/anime.py | 119 +++++----- extensions/commons.py | 122 +++++----- extensions/profile.py | 77 +++--- extensions/random.py | 98 +++++--- extensions/serversettings.py | 18 +- extensions/usersettings.py | 16 +- extensions/utilities.py | 358 +++++++++++++--------------- firstRun.py | 29 ++- main.py | 17 +- modules/anilist.py | 14 +- modules/commons.py | 96 ++++---- modules/const.py | 186 +++++++++------ modules/database.py | 4 +- modules/i18n.py | 63 ++--- modules/jikan.py | 2 +- modules/myanimelist.py | 441 +++++++++++++++++++---------------- modules/nekomimidb.py | 26 +-- modules/oobe/commons.py | 33 +-- modules/oobe/i18nBuild.py | 48 ++-- modules/oobe/jikan.py | 10 +- modules/oobe/malIndexer.py | 2 +- modules/platforms.py | 248 +++++++++++--------- 38 files changed, 1390 insertions(+), 1150 deletions(-) diff --git a/classes/anilist.py b/classes/anilist.py index 36c0558fd..cecc20ea5 100644 --- a/classes/anilist.py +++ b/classes/anilist.py @@ -16,7 +16,7 @@ def __init__(self): """Initialize the AniList API Wrapper""" self.base_url = "https://graphql.anilist.co" self.session = None - self.cache_directory = 'cache/anilist' + self.cache_directory = "cache/anilist" self.cache_expiration_time = 86400 # 1 day in seconds async def __aenter__(self): @@ -34,10 +34,13 @@ async def close(self) -> None: class MediaType(Enum): """Media type enum for AniList""" + ANIME = "ANIME" MANGA = "MANGA" - async def nsfwCheck(self, media_id: int, media_type: str | MediaType = MediaType.ANIME) -> bool: + async def nsfwCheck( + self, media_id: int, media_type: str | MediaType = MediaType.ANIME + ) -> bool: """Check if the media is NSFW Args: @@ -54,7 +57,8 @@ async def nsfwCheck(self, media_id: int, media_type: str | MediaType = MediaType if isinstance(media_type, self.MediaType): media_type = media_type.value cache_file_path = self.get_cache_file_path( - f'nsfw/{media_type.lower()}/{id}.json') + f"nsfw/{media_type.lower()}/{id}.json" + ) cached_data = self.read_cached_data(cache_file_path) if cached_data is not None: return cached_data @@ -70,7 +74,8 @@ async def nsfwCheck(self, media_id: int, media_type: str | MediaType = MediaType if response.status == 200: data = await response.json() self.write_data_to_cache( - data["data"]["Media"]["isAdult"], cache_file_path) + data["data"]["Media"]["isAdult"], cache_file_path + ) return data["data"]["Media"]["isAdult"] error_message = await response.text() raise ProviderHttpError(error_message, response.status) @@ -87,7 +92,7 @@ async def anime(self, media_id: int) -> dict: Returns: dict: The anime information """ - cache_file_path = self.get_cache_file_path(f'anime/{media_id}.json') + cache_file_path = self.get_cache_file_path(f"anime/{media_id}.json") cached_data = self.read_cached_data(cache_file_path) if cached_data is not None: return cached_data @@ -140,11 +145,12 @@ async def anime(self, media_id: int) -> dict: }} }} }}""" - async with self.session.post(self.base_url, json={"query": gqlquery}) as response: + async with self.session.post( + self.base_url, json={"query": gqlquery} + ) as response: if response.status == 200: data = await response.json() - self.write_data_to_cache( - data["data"]["Media"], cache_file_path) + self.write_data_to_cache(data["data"]["Media"], cache_file_path) return data["data"]["Media"] error_message = await response.text() raise ProviderHttpError(error_message, response.status) @@ -158,7 +164,7 @@ async def manga(self, media_id: int) -> dict: Returns: dict: The manga information """ - cache_file_path = self.get_cache_file_path(f'manga/{media_id}.json') + cache_file_path = self.get_cache_file_path(f"manga/{media_id}.json") cached_data = self.read_cached_data(cache_file_path) if cached_data is not None: return cached_data @@ -211,16 +217,19 @@ async def manga(self, media_id: int) -> dict: }} }} }}""" - async with self.session.post(self.base_url, json={"query": gqlquery}) as response: + async with self.session.post( + self.base_url, json={"query": gqlquery} + ) as response: if response.status == 200: data = await response.json() - self.write_data_to_cache( - data["data"]["Media"], cache_file_path) + self.write_data_to_cache(data["data"]["Media"], cache_file_path) return data["data"]["Media"] error_message = await response.text() raise ProviderHttpError(error_message, response.status) - async def search_media(self, query: str, limit: int = 10, media_type: str | MediaType = MediaType.MANGA) -> list[dict]: + async def search_media( + self, query: str, limit: int = 10, media_type: str | MediaType = MediaType.MANGA + ) -> list[dict]: """Search anime by its title Args: @@ -236,8 +245,7 @@ async def search_media(self, query: str, limit: int = 10, media_type: str | Medi list[dict]: The search results """ if limit > 10: - raise ProviderTypeError( - "limit must be less than or equal to 10", "int") + raise ProviderTypeError("limit must be less than or equal to 10", "int") if isinstance(media_type, self.MediaType): media_type = media_type.value gqlquery = f"""query ($search: String, $mediaType: MediaType, $limit: Int) {{ @@ -264,7 +272,9 @@ async def search_media(self, query: str, limit: int = 10, media_type: str | Medi "mediaType": media_type, "limit": limit, } - async with self.session.post(self.base_url, json={"query": gqlquery, "variables": variables}) as response: + async with self.session.post( + self.base_url, json={"query": gqlquery, "variables": variables} + ) as response: if response.status == 200: data = await response.json() return data["data"]["Page"]["results"] @@ -293,11 +303,11 @@ def read_cached_data(self, cache_file_path: str) -> dict | None: None: If cache file does not exist """ if os.path.exists(cache_file_path): - with open(cache_file_path, 'r') as cache_file: + with open(cache_file_path, "r") as cache_file: cache_data = json.load(cache_file) - cache_age = time.time() - cache_data['timestamp'] + cache_age = time.time() - cache_data["timestamp"] if cache_age < self.cache_expiration_time: - return cache_data['data'] + return cache_data["data"] return None def write_data_to_cache(self, data, cache_file_path: str) -> None: @@ -310,9 +320,9 @@ def write_data_to_cache(self, data, cache_file_path: str) -> None: Returns: None """ - cache_data = {'timestamp': time.time(), 'data': data} + cache_data = {"timestamp": time.time(), "data": data} os.makedirs(os.path.dirname(cache_file_path), exist_ok=True) - with open(cache_file_path, 'w') as cache_file: + with open(cache_file_path, "w") as cache_file: json.dump(cache_data, cache_file) diff --git a/classes/animeapi.py b/classes/animeapi.py index fa18a673b..f0a1fc2f3 100644 --- a/classes/animeapi.py +++ b/classes/animeapi.py @@ -32,21 +32,22 @@ async def close(self) -> None: class AnimeApiPlatforms(Enum): """Anime API supported platforms enum""" - ANI_SEARCH = ANISEARCH = AS = 'anisearch' - ANIDB = 'anidb' - ANILIST = AL = 'anilist' - ANIME_PLANET = ANIMEPLANET = AP = 'animeplanet' - ANNICT = 'annict' - KAIZE = 'kaize' - KITSU = 'kitsu' - LIVECHART = LC = 'livechart' - MYANIMELIST = MAL = 'myanimelist' - NOTIFY = 'notify' - OTAKOTAKU = 'otakotaku' - SHIKIMORI = SHIKI = 'shikimori' - SHOBOI = SYOBOI = 'shoboi' - SILVERYASHA = 'silveryasha' - TRAKT = 'trakt' + + ANI_SEARCH = ANISEARCH = AS = "anisearch" + ANIDB = "anidb" + ANILIST = AL = "anilist" + ANIME_PLANET = ANIMEPLANET = AP = "animeplanet" + ANNICT = "annict" + KAIZE = "kaize" + KITSU = "kitsu" + LIVECHART = LC = "livechart" + MYANIMELIST = MAL = "myanimelist" + NOTIFY = "notify" + OTAKOTAKU = "otakotaku" + SHIKIMORI = SHIKI = "shikimori" + SHOBOI = SYOBOI = "shoboi" + SILVERYASHA = "silveryasha" + TRAKT = "trakt" async def get_update_time(self) -> dt: """Get the last update time of AniAPI's database @@ -54,25 +55,26 @@ async def get_update_time(self) -> dt: Returns: datetime: The last update time of AniAPI's database """ - cache_file_path = self.get_cache_file_path('updated') + cache_file_path = self.get_cache_file_path("updated") cached_data = self.read_cached_data(cache_file_path) if cached_data is not None: - cached_data = dt.fromtimestamp(cached_data['timestamp']) + cached_data = dt.fromtimestamp(cached_data["timestamp"]) return cached_data try: - async with self.session.get(f'{self.base_url}/updated') as resp: + async with self.session.get(f"{self.base_url}/updated") as resp: text = await resp.text() # format: Updated on %m/%d/%Y %H:%M:%S UTC - text = text.replace('Updated on ', '') - text = text.replace(' UTC', '+00:00') - final = dt.strptime(text, '%m/%d/%Y %H:%M:%S%z').timestamp() - self.write_data_to_cache( - cache_file_path, {'timestamp': final}) + text = text.replace("Updated on ", "") + text = text.replace(" UTC", "+00:00") + final = dt.strptime(text, "%m/%d/%Y %H:%M:%S%z").timestamp() + self.write_data_to_cache(cache_file_path, {"timestamp": final}) return dt.fromtimestamp(final) except BaseException: return dt.now() - async def get_relation(self, id: str | int, platform: AnimeApiPlatforms | str) -> dict: + async def get_relation( + self, id: str | int, platform: AnimeApiPlatforms | str + ) -> dict: """Get a relation between anime and other platform via Natsu's AniAPI Args: @@ -84,12 +86,14 @@ async def get_relation(self, id: str | int, platform: AnimeApiPlatforms | str) - """ if isinstance(platform, self.AnimeApiPlatforms): platform = platform.value - cache_file_path = self.get_cache_file_path(f'{platform}/{id}.json') + cache_file_path = self.get_cache_file_path(f"{platform}/{id}.json") cached_data = self.read_cached_data(cache_file_path) if cached_data is not None: return cached_data try: - async with self.session.get(f'https://aniapi.nattadasu.my.id/{platform}/{id}') as resp: + async with self.session.get( + f"https://aniapi.nattadasu.my.id/{platform}/{id}" + ) as resp: jsonText = await resp.text() jsonText = json.loads(jsonText) self.write_data_to_cache(jsonText, cache_file_path) @@ -120,11 +124,11 @@ def read_cached_data(self, cache_file_path: str) -> dict | None: None: If cache file does not exist """ if os.path.exists(cache_file_path): - with open(cache_file_path, 'r') as cache_file: + with open(cache_file_path, "r") as cache_file: cache_data = json.load(cache_file) - cache_age = time.time() - cache_data['timestamp'] + cache_age = time.time() - cache_data["timestamp"] if cache_age < self.cache_expiration_time: - return cache_data['data'] + return cache_data["data"] return None def write_data_to_cache(self, data, cache_file_path: str): @@ -134,10 +138,10 @@ def write_data_to_cache(self, data, cache_file_path: str): data (any): Data to write to cache cache_file_name (str): Cache file name """ - cache_data = {'timestamp': time.time(), 'data': data} + cache_data = {"timestamp": time.time(), "data": data} os.makedirs(os.path.dirname(cache_file_path), exist_ok=True) - with open(cache_file_path, 'w') as cache_file: + with open(cache_file_path, "w") as cache_file: json.dump(cache_data, cache_file) -__all__ = ['AnimeApi'] +__all__ = ["AnimeApi"] diff --git a/classes/database.py b/classes/database.py index 9f21bbb92..98d312bbc 100644 --- a/classes/database.py +++ b/classes/database.py @@ -11,7 +11,8 @@ def __init__(self, database_path: str = database): """Initialize the database Args: - database_path (str, optional): Path to the database. Defaults to database.""" + database_path (str, optional): Path to the database. Defaults to database. + """ self.database_path = database_path async def __aenter__(self): @@ -36,11 +37,21 @@ async def check_if_registered(self, discord_id: int) -> bool: bool: True if user is registered, False if not """ df = pd.read_csv(self.database_path, sep="\t", dtype=str) - return str(discord_id) in df['discordId'].values - - async def save_to_database(self, discord_id: int, discord_username: str, discord_joined: int, - mal_username: str, mal_id: int, mal_joined: int, registered_at: int, - registered_guild: int, registered_by: int, guild_name: str): + return str(discord_id) in df["discordId"].values + + async def save_to_database( + self, + discord_id: int, + discord_username: str, + discord_joined: int, + mal_username: str, + mal_id: int, + mal_joined: int, + registered_at: int, + registered_guild: int, + registered_by: int, + guild_name: str, + ): """Save information regarding to user with their consent Args: @@ -56,16 +67,16 @@ async def save_to_database(self, discord_id: int, discord_username: str, discord guild_name (str): Guild name where the user registered """ data = { - 'discordId': str(discord_id), - 'discordUsername': discord_username, - 'discordJoined': str(discord_joined), - 'malUsername': mal_username, - 'malId': str(mal_id), - 'malJoined': str(mal_joined), - 'registeredAt': str(registered_at), - 'registeredGuild': str(registered_guild), - 'registeredBy': str(registered_by), - 'guildName': guild_name, + "discordId": str(discord_id), + "discordUsername": discord_username, + "discordJoined": str(discord_joined), + "malUsername": mal_username, + "malId": str(mal_id), + "malJoined": str(mal_joined), + "registeredAt": str(registered_at), + "registeredGuild": str(registered_guild), + "registeredBy": str(registered_by), + "guildName": guild_name, } df = pd.DataFrame(data, index=[0]) df.to_csv( @@ -73,7 +84,8 @@ async def save_to_database(self, discord_id: int, discord_username: str, discord sep="\t", index=False, header=not self._database_exists(), - mode='a') + mode="a", + ) async def drop_user(self, discord_id: int) -> bool: """Drop a user from the database @@ -85,7 +97,7 @@ async def drop_user(self, discord_id: int) -> bool: bool: True if user is dropped, False if not """ df = pd.read_csv(self.database_path, sep="\t", dtype=str) - df.drop(df[df['discordId'] == str(discord_id)].index, inplace=True) + df.drop(df[df["discordId"] == str(discord_id)].index, inplace=True) df.to_csv(self.database_path, sep="\t", index=False) return True @@ -99,30 +111,33 @@ async def verify_user(self, discord_id: int) -> bool: bool: True if user is verified, False if not """ df = pd.read_csv(self.database_path, sep="\t", dtype=str) - row = df[df['discordId'] == str(discord_id)] + row = df[df["discordId"] == str(discord_id)] if row.empty: raise DatabaseException( - f"{EMOJI_UNEXPECTED_ERROR} User may not be registered to the bot, or there's unknown error") + f"{EMOJI_UNEXPECTED_ERROR} User may not be registered to the bot, or there's unknown error" + ) - username = row.iloc[0]['malUsername'] + username = row.iloc[0]["malUsername"] clubs = await checkClubMembership(username) verified = False for club in clubs: - if str(club['mal_id']) == str(CLUB_ID): + if str(club["mal_id"]) == str(CLUB_ID): verified = True break else: raise DatabaseException( - f"{EMOJI_UNEXPECTED_ERROR} User is not a member of the club") + f"{EMOJI_UNEXPECTED_ERROR} User is not a member of the club" + ) return verified async def export_user_data(self, user_id: int) -> str: df = pd.read_csv(self.database_path, sep="\t", dtype=str) - row = df[df['discordId'] == str(user_id)] + row = df[df["discordId"] == str(user_id)] if row.empty: raise DatabaseException( - f"{EMOJI_UNEXPECTED_ERROR} User may not be registered to the bot, or there's unknown error") - data = row.to_dict(orient='records')[0] + f"{EMOJI_UNEXPECTED_ERROR} User may not be registered to the bot, or there's unknown error" + ) + data = row.to_dict(orient="records")[0] for key, value in data.items(): if value.isdigit(): data[key] = int(value) @@ -145,16 +160,17 @@ def _database_exists(self): return __all__ = [ - 'check_if_registered', - 'save_to_database', - 'drop_user', - 'verify_user', - 'export_user_data', - 'close'] + "check_if_registered", + "save_to_database", + "drop_user", + "verify_user", + "export_user_data", + "close", + ] class DatabaseException(Exception): pass -__all__ = ['UserDatabase', 'DatabaseException'] +__all__ = ["UserDatabase", "DatabaseException"] diff --git a/classes/excepts.py b/classes/excepts.py index df3c7ce06..faf2f3efe 100644 --- a/classes/excepts.py +++ b/classes/excepts.py @@ -25,8 +25,4 @@ def __str__(self): return f"SimklTypeError: {self.message} (expected {self.expected_type})" -__all__ = [ - "ProviderHttpError", - "ProviderTypeError", - "SimklTypeError" -] +__all__ = ["ProviderHttpError", "ProviderTypeError", "SimklTypeError"] diff --git a/classes/jikan.py b/classes/jikan.py index 12dd05eeb..004d3d16e 100644 --- a/classes/jikan.py +++ b/classes/jikan.py @@ -14,10 +14,10 @@ def __str__(self): def defineJikanException(errmsg: str) -> JikanException: - e = str(errmsg).split('=') + e = str(errmsg).split("=") etype = 400 try: - etype = int(e[1].split(',')[0]) + etype = int(e[1].split(",")[0]) errm = e[3].strip() if len(e) >= 4: for i in range(4, len(e)): @@ -60,18 +60,17 @@ async def get_member_clubs(self, username: str) -> list[dict]: """ try: async with AioJikan() as jikan: - res = await jikan.users(username=username, extension='clubs') - data: list = res['data'] - if res['pagination']['last_visible_page'] > 1: - for i in range( - 2, res['pagination']['last_visible_page'] + 1): + res = await jikan.users(username=username, extension="clubs") + data: list = res["data"] + if res["pagination"]["last_visible_page"] > 1: + for i in range(2, res["pagination"]["last_visible_page"] + 1): res = await jikan.users( username=username, - extension='clubs', + extension="clubs", page=i, ) await asyncio.sleep(1) - data.extend(res['data']) + data.extend(res["data"]) return data except Exception as e: defineJikanException(e) @@ -88,7 +87,7 @@ async def get_user_data(self, username: str) -> dict: try: async with AioJikan() as jikan: res = await jikan.users(username=username, extension="full") - return res['data'] + return res["data"] except Exception as e: defineJikanException(e) diff --git a/classes/kitsu.py b/classes/kitsu.py index 48d180f16..78b258d8d 100644 --- a/classes/kitsu.py +++ b/classes/kitsu.py @@ -17,7 +17,7 @@ def __init__(self): self.base_url = "https://kitsu.io/api/edge/" self.params = { # public client ID provided by Kitsu themselves - 'client_id': 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd' + "client_id": "dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd" } self.content_type = "application/vnd.api+json" self.cache_directory = "cache/kitsu" @@ -37,10 +37,13 @@ def close(self): class MediaType(Enum): """Media type enum""" + ANIME = "anime" MANGA = "manga" - async def get_anime(self, anime_id: int, media_type: MediaType | str = MediaType.ANIME) -> dict: + async def get_anime( + self, anime_id: int, media_type: MediaType | str = MediaType.ANIME + ) -> dict: """Get anime data Args: @@ -52,8 +55,7 @@ async def get_anime(self, anime_id: int, media_type: MediaType | str = MediaType """ if isinstance(media_type, str): media_type = self.MediaType(media_type) - cache_file_path = self.get_cache_path( - f"{media_type.value}/{anime_id}.json") + cache_file_path = self.get_cache_path(f"{media_type.value}/{anime_id}.json") cached_data = self.read_cache(cache_file_path) if cached_data is not None: return cached_data @@ -66,7 +68,9 @@ async def get_anime(self, anime_id: int, media_type: MediaType | str = MediaType self.write_cache(cache_file_path, jsonFinal) return jsonFinal - async def resolve_slug(self, slug: str, media_type: MediaType | str = MediaType.ANIME) -> dict: + async def resolve_slug( + self, slug: str, media_type: MediaType | str = MediaType.ANIME + ) -> dict: """Resolve slug to anime ID Args: @@ -78,8 +82,7 @@ async def resolve_slug(self, slug: str, media_type: MediaType | str = MediaType. """ if isinstance(media_type, str): media_type = self.MediaType(media_type) - cache_file_path = self.get_cache_path( - f"{media_type.value}/slug/{slug}.json") + cache_file_path = self.get_cache_path(f"{media_type.value}/slug/{slug}.json") cached_data = self.read_cache(cache_file_path) if cached_data is not None: return cached_data diff --git a/classes/lastfm.py b/classes/lastfm.py index 0479844b8..9e2109389 100644 --- a/classes/lastfm.py +++ b/classes/lastfm.py @@ -53,14 +53,17 @@ async def get_user_info(self, username: str) -> dict: async with self.session.get(self.base_url, params=params) as resp: if resp.status == 404: raise ProviderHttpError( - "User can not be found on Last.fm. Check the name or register?") + "User can not be found on Last.fm. Check the name or register?" + ) else: jsonText = await resp.text() jsonFinal = loads(jsonText) - ud = jsonFinal['user'] + ud = jsonFinal["user"] return ud - async def get_user_recent_tracks(self, username: str, maximum: int = 9) -> list[dict]: + async def get_user_recent_tracks( + self, username: str, maximum: int = 9 + ) -> list[dict]: """Get recent tracks Args: @@ -79,5 +82,5 @@ async def get_user_recent_tracks(self, username: str, maximum: int = 9) -> list[ async with self.session.get(self.base_url, params=params) as resp: jsonText = await resp.text() jsonFinal = loads(jsonText) - scb = jsonFinal['recenttracks']['track'] + scb = jsonFinal["recenttracks"]["track"] return scb diff --git a/classes/myanimelist.py b/classes/myanimelist.py index a80d81760..bc0b472fa 100644 --- a/classes/myanimelist.py +++ b/classes/myanimelist.py @@ -33,7 +33,8 @@ def __init__(self, client_id, nsfw: bool = False): self.client_id = client_id if client_id is None: raise ProviderHttpError( - "Unauthorized, please fill Client ID before using this module", 401) + "Unauthorized, please fill Client ID before using this module", 401 + ) self.base_url = "https://api.myanimelist.net/v2" self.headers = {"X-MAL-CLIENT-ID": self.client_id} self.nsfw = nsfw @@ -67,14 +68,22 @@ async def anime(self, anime_id: int, fields: str | None = None) -> dict: params["fields"] = fields if self.nsfw: params["nsfw"] = "true" - async with self.session.get(f"{self.base_url}/anime/{anime_id}", params=params) as response: + async with self.session.get( + f"{self.base_url}/anime/{anime_id}", params=params + ) as response: if response.status == 200: data = await response.json() return data error_message = await response.text() raise ProviderHttpError(error_message, response.status) - async def search(self, query: str, limit: int = 10, offset: int | None = None, fields: str | None = None): + async def search( + self, + query: str, + limit: int = 10, + offset: int | None = None, + fields: str | None = None, + ): """Search anime by its title Args: @@ -87,14 +96,15 @@ async def search(self, query: str, limit: int = 10, offset: int | None = None, f dict: Search results """ if limit > 100: - raise ProviderTypeError( - "limit must be less than or equal to 100", "int") + raise ProviderTypeError("limit must be less than or equal to 100", "int") params = {"q": query, "limit": limit} if offset: params["offset"] = offset if fields: params["fields"] = fields - async with self.session.get(f"{self.base_url}/anime", params=params) as response: + async with self.session.get( + f"{self.base_url}/anime", params=params + ) as response: if response.status == 200: data = await response.json() return data diff --git a/classes/nekomimidb.py b/classes/nekomimidb.py index 4fd7d5823..c63211d42 100644 --- a/classes/nekomimidb.py +++ b/classes/nekomimidb.py @@ -20,8 +20,10 @@ class NekomimiDb: embed = nm.get_random_nekomimi() await ctx.send(embed=embed) ```""" + class Gender(Enum): """Supported Gender Enum""" + BOY = "boy" GIRL = "girl" UNKNOWN = NONBINARY = NB = "nb" @@ -35,7 +37,7 @@ def __init__(self, gender: Gender | None = None): """ self.gender = gender self.seed = getRandom() - self.nmDb = pd.read_csv("database/nekomimiDb.tsv", sep="\t").fillna('') + self.nmDb = pd.read_csv("database/nekomimiDb.tsv", sep="\t").fillna("") def get_random_nekomimi(self) -> pd.Series: """Get a random nekomimi image from the database @@ -44,7 +46,7 @@ def get_random_nekomimi(self) -> pd.Series: Series: a random row from the database """ if self.gender is not None: - query = self.nmDb[self.nmDb['girlOrBoy'] == self.gender.value] + query = self.nmDb[self.nmDb["girlOrBoy"] == self.gender.value] else: query = self.nmDb # get a random row from the query diff --git a/classes/randomorg.py b/classes/randomorg.py index a21ad452d..5eb473ec5 100644 --- a/classes/randomorg.py +++ b/classes/randomorg.py @@ -47,10 +47,7 @@ def __init__(self): """Initialize the Random.org True Random Number Generator API Wrapper""" self.base_url = "https://www.random.org" self.session = None - self.params = { - "format": "plain", - "rnd": "new" - } + self.params = {"format": "plain", "rnd": "new"} async def __aenter__(self): """Enter the async context manager""" @@ -67,6 +64,7 @@ async def close(self) -> None: class OnOff(Enum): """OnOff enum""" + ON = "on" OFF = "off" @@ -85,18 +83,18 @@ async def integers(self, num: int, min: int, max: int, base: int = 10) -> list[i if base not in [2, 8, 10, 16]: raise ProviderTypeError("Base must be 2, 8, 10, or 16", "base") if num > 10000 or num < 1: - raise ProviderTypeError( - "Number must be between 1 and 10000", "num") + raise ProviderTypeError("Number must be between 1 and 10000", "num") if min > max: - raise ProviderTypeError( - "Min must be less than or equal to max", "minmax") + raise ProviderTypeError("Min must be less than or equal to max", "minmax") params = self.params.copy() params["num"] = num params["min"] = min params["max"] = max params["col"] = 1 params["base"] = base - async with self.session.get(f"{self.base_url}/integers", params=params) as response: + async with self.session.get( + f"{self.base_url}/integers", params=params + ) as response: if response.status == 200: data = await response.text() data = data.splitlines() @@ -116,12 +114,13 @@ async def sequences(self, min: int, max: int) -> list[int]: list[int]: List of random sequences """ if min > max: - raise ProviderTypeError( - "Min must be less than or equal to max", "minmax") + raise ProviderTypeError("Min must be less than or equal to max", "minmax") params = self.params.copy() params["min"] = min params["max"] = max - async with self.session.get(f"{self.base_url}/sequences", params=params) as response: + async with self.session.get( + f"{self.base_url}/sequences", params=params + ) as response: if response.status == 200: data = await response.text() data = data.splitlines() @@ -130,7 +129,15 @@ async def sequences(self, min: int, max: int) -> list[int]: error_message = await response.text() raise ProviderHttpError(error_message, response.status) - async def strings(self, num: int, length: int = 10, digits: str | OnOff = "on", upperalpha: str | OnOff = "on", loweralpha: str | OnOff = "on", unique: str | OnOff = "on") -> list[str]: + async def strings( + self, + num: int, + length: int = 10, + digits: str | OnOff = "on", + upperalpha: str | OnOff = "on", + loweralpha: str | OnOff = "on", + unique: str | OnOff = "on", + ) -> list[str]: """Generate random strings Args: @@ -149,11 +156,9 @@ async def strings(self, num: int, length: int = 10, digits: str | OnOff = "on", list[str]: List of random strings """ if length > 20: - raise ProviderTypeError( - "Length must be less than or equal to 20", "length") + raise ProviderTypeError("Length must be less than or equal to 20", "length") if num > 10000 or num < 1: - raise ProviderTypeError( - "Number must be between 1 and 10000", "num") + raise ProviderTypeError("Number must be between 1 and 10000", "num") if isinstance(digits, self.OnOff): digits = digits.value if isinstance(upperalpha, self.OnOff): @@ -162,23 +167,18 @@ async def strings(self, num: int, length: int = 10, digits: str | OnOff = "on", loweralpha = loweralpha.value if isinstance(unique, self.OnOff): unique = unique.value - if isinstance( - digits, - str) or isinstance( - upperalpha, - str) or isinstance( - loweralpha, - str) or isinstance( - unique, - str): + if ( + isinstance(digits, str) + or isinstance(upperalpha, str) + or isinstance(loweralpha, str) + or isinstance(unique, str) + ): if digits not in ["on", "off"]: raise ProviderTypeError("Digits must be on or off", "digits") elif upperalpha not in ["on", "off"]: - raise ProviderTypeError( - "Upperalpha must be on or off", "upperalpha") + raise ProviderTypeError("Upperalpha must be on or off", "upperalpha") elif loweralpha not in ["on", "off"]: - raise ProviderTypeError( - "Loweralpha must be on or off", "loweralpha") + raise ProviderTypeError("Loweralpha must be on or off", "loweralpha") elif unique not in ["on", "off"]: raise ProviderTypeError("Unique must be on or off", "unique") params = self.params.copy() @@ -188,7 +188,9 @@ async def strings(self, num: int, length: int = 10, digits: str | OnOff = "on", params["upperalpha"] = upperalpha params["loweralpha"] = loweralpha params["unique"] = unique - async with self.session.get(f"{self.base_url}/strings", params=params) as response: + async with self.session.get( + f"{self.base_url}/strings", params=params + ) as response: if response.status == 200: data = await response.text() data = data.splitlines() diff --git a/classes/rawg.py b/classes/rawg.py index 4e66e4876..a772bf03d 100644 --- a/classes/rawg.py +++ b/classes/rawg.py @@ -1,6 +1,7 @@ import json import os import time + # deep copy the params from copy import deepcopy @@ -22,7 +23,7 @@ def __init__(self, key: str = RAWG_API_KEY): self.base_url = "https://api.rawg.io/api" self.params = {"key": key} self.session = None - self.cache_directory = 'cache/rawg' + self.cache_directory = "cache/rawg" self.cache_expiration_time = 86400 # 1 day in seconds async def __aenter__(self): @@ -55,7 +56,8 @@ async def search(self, query: str) -> list[dict]: rawgRes = await resp.json() return rawgRes["results"] raise ProviderHttpError( - f"RAWG API returned {resp.status}. Reason: {resp.text()}", resp.status) + f"RAWG API returned {resp.status}. Reason: {resp.text()}", resp.status + ) async def get_data(self, slug: str) -> dict: """Get information of a title in RAWG @@ -70,16 +72,20 @@ async def get_data(self, slug: str) -> dict: Returns: dict: Game data """ - cache_file_path = self.get_cache_file_path(f'{slug}.json') + cache_file_path = self.get_cache_file_path(f"{slug}.json") cached_data = self.read_cached_data(cache_file_path) if cached_data is not None: return cached_data - async with self.session.get(f"https://api.rawg.io/api/games/{slug}", params=self.params) as resp: + async with self.session.get( + f"https://api.rawg.io/api/games/{slug}", params=self.params + ) as resp: if resp.status == 200: rawgRes = await resp.json() else: raise ProviderHttpError( - f"RAWG API returned {resp.status}. Reason: {resp.text()}", resp.status) + f"RAWG API returned {resp.status}. Reason: {resp.text()}", + resp.status, + ) if len(rawgRes) == 0: raise ProviderTypeError("**No results found!**", dict) self.write_data_to_cache(rawgRes, cache_file_path) @@ -107,11 +113,11 @@ def read_cached_data(self, cache_file_path: str) -> dict | None: None: If cache file does not exist """ if os.path.exists(cache_file_path): - with open(cache_file_path, 'r') as cache_file: + with open(cache_file_path, "r") as cache_file: cache_data = json.load(cache_file) - cache_age = time.time() - cache_data['timestamp'] + cache_age = time.time() - cache_data["timestamp"] if cache_age < self.cache_expiration_time: - return cache_data['data'] + return cache_data["data"] return None def write_data_to_cache(self, data, cache_file_path: str): @@ -121,9 +127,9 @@ def write_data_to_cache(self, data, cache_file_path: str): data (any): Data to write cache_file_name (str): Cache file name """ - cache_data = {'timestamp': time.time(), 'data': data} + cache_data = {"timestamp": time.time(), "data": data} os.makedirs(os.path.dirname(cache_file_path), exist_ok=True) - with open(cache_file_path, 'w') as cache_file: + with open(cache_file_path, "w") as cache_file: json.dump(cache_data, cache_file) async def close(self): diff --git a/classes/simkl.py b/classes/simkl.py index a72253834..e41125783 100644 --- a/classes/simkl.py +++ b/classes/simkl.py @@ -145,6 +145,7 @@ class SimklAnimeGenre(Enum): class SimklMediaTypes(Enum): """Media types supported by Simkl API""" + ANIME = "anime" MOVIE = "movie" SHOW = TV = "tv" @@ -153,7 +154,8 @@ class SimklMediaTypes(Enum): class Simkl: """Simkl API wrapper - This module is a wrapper for Simkl API, which is used to search for anime, shows, and movies.""" + This module is a wrapper for Simkl API, which is used to search for anime, shows, and movies. + """ def __init__(self, client_id: str = SIMKL_CLIENT_ID): """Initialize the Simkl API wrapper @@ -164,11 +166,12 @@ def __init__(self, client_id: str = SIMKL_CLIENT_ID): self.client_id = client_id if client_id is None: raise ProviderHttpError( - "Unauthorized, please fill Client ID before using this module", 401) + "Unauthorized, please fill Client ID before using this module", 401 + ) self.base_url = "https://api.simkl.com" self.params = {"client_id": self.client_id} self.session = None - self.cache_directory = 'cache/simkl' + self.cache_directory = "cache/simkl" self.cache_expiration_time = 86400 # 1 day in seconds async def __aenter__(self): @@ -186,6 +189,7 @@ async def close(self) -> None: class Provider(Enum): """Providers supported by Simkl API""" + ANIDB = "anidb" ANILIST = AL = "anilist" ANIMEPLANET = AP = "animeplanet" @@ -202,10 +206,16 @@ class Provider(Enum): class TmdbMediaTypes(Enum): """Media types required to reverse lookup from TMDB ID""" + SHOW = "show" MOVIE = "movie" - async def search_by_id(self, provider: Provider | str, id: int, media_type: TmdbMediaTypes | str | None = None) -> dict: + async def search_by_id( + self, + provider: Provider | str, + id: int, + media_type: TmdbMediaTypes | str | None = None, + ) -> dict: """Search by ID Args: @@ -226,9 +236,12 @@ async def search_by_id(self, provider: Provider | str, id: int, media_type: Tmdb if provider == self.Provider.TMDB and not media_type: raise SimklTypeError( "MediaType is required when using TMDB provider", - "TmdbMediaTypeRequired") + "TmdbMediaTypeRequired", + ) params[f"{provider}"] = id - async with self.session.get(f"{self.base_url}/search/id", params=params) as response: + async with self.session.get( + f"{self.base_url}/search/id", params=params + ) as response: if response.status == 200: data = await response.json() return data @@ -264,7 +277,9 @@ async def search_by_title( params["extended"] = "full" params["page"] = page params["limit"] = limit - async with self.session.get(f"{self.base_url}/search/{media_type}", params=params) as response: + async with self.session.get( + f"{self.base_url}/search/{media_type}", params=params + ) as response: if response.status == 200: data = await response.json() return data @@ -284,13 +299,15 @@ async def get_show(self, id: int) -> dict: Returns: dict: Response from Simkl API """ - cache_file_path = self.get_cache_file_path(f'show/{id}.json') + cache_file_path = self.get_cache_file_path(f"show/{id}.json") cached_data = self.read_cached_data(cache_file_path) if cached_data is not None: return cached_data params = deepcopy(self.params) params["extended"] = "full" - async with self.session.get(f"{self.base_url}/tv/{id}", params=params) as response: + async with self.session.get( + f"{self.base_url}/tv/{id}", params=params + ) as response: if response.status == 200: data = await response.json() self.write_data_to_cache(data, cache_file_path) @@ -311,13 +328,15 @@ async def get_movie(self, id: int) -> dict: Returns: dict: Response from Simkl API """ - cache_file_path = self.get_cache_file_path(f'movie/{id}.json') + cache_file_path = self.get_cache_file_path(f"movie/{id}.json") cached_data = self.read_cached_data(cache_file_path) if cached_data is not None: return cached_data params = deepcopy(self.params) params["extended"] = "full" - async with self.session.get(f"{self.base_url}/movies/{id}", params=params) as response: + async with self.session.get( + f"{self.base_url}/movies/{id}", params=params + ) as response: if response.status == 200: data = await response.json() self.write_data_to_cache(data, cache_file_path) @@ -338,13 +357,15 @@ async def get_anime(self, id: int) -> dict: Returns: dict: Response from Simkl API """ - cache_file_path = self.get_cache_file_path(f'anime/{id}.json') + cache_file_path = self.get_cache_file_path(f"anime/{id}.json") cached_data = self.read_cached_data(cache_file_path) if cached_data is not None: return cached_data params = deepcopy(self.params) params["extended"] = "full" - async with self.session.get(f"{self.base_url}/anime/{id}", params=params) as response: + async with self.session.get( + f"{self.base_url}/anime/{id}", params=params + ) as response: if response.status == 200: data = await response.json() self.write_data_to_cache(data, cache_file_path) @@ -355,7 +376,12 @@ async def get_anime(self, id: int) -> dict: async def get_random_title( self, media_type: SimklMediaTypes | str, - genre: SimklMediaGenre | SimklMovieGenre | SimklTvGenre | SimklAnimeGenre | str | None = None, + genre: SimklMediaGenre + | SimklMovieGenre + | SimklTvGenre + | SimklAnimeGenre + | str + | None = None, year_from: int | None = None, year_to: int | None = None, rating_limit: int | None = None, @@ -405,7 +431,9 @@ async def get_random_title( params["rating_from"] = rating_from if rating_to: params["rating_to"] = rating_to - async with self.session.get(f"{self.base_url}/search/random/", params=params) as response: + async with self.session.get( + f"{self.base_url}/search/random/", params=params + ) as response: if response.status == 200: data = await response.json() return data @@ -425,8 +453,7 @@ async def get_title_ids(self, id: int, media_type: SimklMediaTypes | str) -> dic Returns: dict: Response from Simkl API """ - cache_file_path = self.get_cache_file_path( - f'ids/{media_type}/{id}.json') + cache_file_path = self.get_cache_file_path(f"ids/{media_type}/{id}.json") cached_data = self.read_cached_data(cache_file_path) if cached_data is not None: return cached_data @@ -452,16 +479,15 @@ async def get_title_ids(self, id: int, media_type: SimklMediaTypes | str) -> dic "instagram", "twitter", "wikien", - "wikijp"]: + "wikijp", + ]: continue if isinstance(v, str) and v.isdigit(): mids[k] = int(v) - keys = [ - 'title', 'poster', 'fanart', 'anime_type', 'type' - ] + keys = ["title", "poster", "fanart", "anime_type", "type"] for k in keys: - if k == 'anime_type': - mids['anitype'] = data.get(k, None) + if k == "anime_type": + mids["anitype"] = data.get(k, None) continue mids[k] = data.get(k, None) self.write_data_to_cache(mids, cache_file_path) @@ -489,11 +515,11 @@ def read_cached_data(self, cache_file_path: str) -> dict | None: None: If cache file does not exist """ if os.path.exists(cache_file_path): - with open(cache_file_path, 'r') as cache_file: + with open(cache_file_path, "r") as cache_file: cache_data = json.load(cache_file) - cache_age = time.time() - cache_data['timestamp'] + cache_age = time.time() - cache_data["timestamp"] if cache_age < self.cache_expiration_time: - return cache_data['data'] + return cache_data["data"] return None def write_data_to_cache(self, data, cache_file_path: str): @@ -503,10 +529,10 @@ def write_data_to_cache(self, data, cache_file_path: str): data (any): Data to write to cache cache_file_name (str): Cache file name """ - cache_data = {'timestamp': time.time(), 'data': data} + cache_data = {"timestamp": time.time(), "data": data} os.makedirs(os.path.dirname(cache_file_path), exist_ok=True) - with open(cache_file_path, 'w') as cache_file: + with open(cache_file_path, "w") as cache_file: json.dump(cache_data, cache_file) -__all__ = ['Simkl'] +__all__ = ["Simkl"] diff --git a/classes/thecolorapi.py b/classes/thecolorapi.py index 5f87e92fe..de6edc9c7 100644 --- a/classes/thecolorapi.py +++ b/classes/thecolorapi.py @@ -10,13 +10,14 @@ class TheColorApi: """The Color API wrapper - This module is a wrapper for The Color API, which is used to get color information from hex, rgb, hsl, hsv, and cmyk values.""" + This module is a wrapper for The Color API, which is used to get color information from hex, rgb, hsl, hsv, and cmyk values. + """ def __init__(self): """Initialize the class""" self.base_url = "https://www.thecolorapi.com" self.session = None - self.cache_directory = 'cache/thecolorapi' + self.cache_directory = "cache/thecolorapi" self.cache_expiration_time = 604800 # 1 week in seconds async def __aenter__(self): @@ -69,8 +70,7 @@ def get_cache_file_path(self, color_params): Args: color_params (dict): Color parameters """ - filename = '-'.join([f"{k}_{v}" for k, - v in color_params.items()]) + '.json' + filename = "-".join([f"{k}_{v}" for k, v in color_params.items()]) + ".json" return os.path.join(self.cache_directory, filename) def read_cached_data(self, cache_file_path) -> dict | None: @@ -84,11 +84,11 @@ def read_cached_data(self, cache_file_path) -> dict | None: None: If cache file does not exist or cache is expired """ if os.path.exists(cache_file_path): - with open(cache_file_path, 'r') as cache_file: + with open(cache_file_path, "r") as cache_file: cache_data = json.load(cache_file) - cache_age = time.time() - cache_data['timestamp'] + cache_age = time.time() - cache_data["timestamp"] if cache_age < self.cache_expiration_time: - return cache_data['data'] + return cache_data["data"] return None def write_data_to_cache(self, data, cache_file_path: str): @@ -98,7 +98,7 @@ def write_data_to_cache(self, data, cache_file_path: str): data (any): Data to write to cache cache_file_path (str): Cache file path """ - cache_data = {'timestamp': time.time(), 'data': data} + cache_data = {"timestamp": time.time(), "data": data} os.makedirs(os.path.dirname(cache_file_path), exist_ok=True) - with open(cache_file_path, 'w') as cache_file: + with open(cache_file_path, "w") as cache_file: json.dump(cache_data, cache_file) diff --git a/classes/tmdb.py b/classes/tmdb.py index ca266b3bf..0b3e19ed9 100644 --- a/classes/tmdb.py +++ b/classes/tmdb.py @@ -39,10 +39,13 @@ async def close(self): class MediaType(Enum): """Media type enum""" + TV = SHOW = "tv" MOVIE = "movie" - async def get_nsfw_status(self, id: int, media_type: MediaType | str = MediaType.TV) -> bool: + async def get_nsfw_status( + self, id: int, media_type: MediaType | str = MediaType.TV + ) -> bool: """Get the NSFW status of a TV show or movie Args: diff --git a/classes/trakt.py b/classes/trakt.py index d2f53913e..10d618043 100644 --- a/classes/trakt.py +++ b/classes/trakt.py @@ -38,15 +38,22 @@ async def close(self): class Platform(Enum): """Supported Platform Enum""" + IMDB = "imdb" TMDB = "tmdb" class MediaType(Enum): """Media type enum""" + TV = SHOW = ONA = "show" MOVIE = "movie" - async def lookup(self, id: int | str, platform: Platform | str = Platform.IMDB, media_type: MediaType | str = MediaType.TV) -> dict: + async def lookup( + self, + id: int | str, + platform: Platform | str = Platform.IMDB, + media_type: MediaType | str = MediaType.TV, + ) -> dict: """Lookup a TV show or movie by ID on a supported platform Args: @@ -69,7 +76,8 @@ async def lookup(self, id: int | str, platform: Platform | str = Platform.IMDB, raise ProviderTypeError("TMDB requires a media type", "MediaType") self.cache_time = 2592000 cache_file_path = self.get_cache_path( - f"lookup/{platform.value}/{media_type.value}/{id}.json") + f"lookup/{platform.value}/{media_type.value}/{id}.json" + ) cached_data = self.read_cache(cache_file_path) if cached_data is not None: return cached_data @@ -106,9 +114,7 @@ async def get_title_data(self, id: int | str, media_type: MediaType | str) -> di if cached_data is not None: return cached_data url = f"{self.base_url}{media_type.value}/{id}" - param = { - "extended": "full" - } + param = {"extended": "full"} async with self.session.get(url, params=param) as resp: if resp.status != 200: raise ProviderHttpError(resp.text(), resp.status) diff --git a/dev.py b/dev.py index c02746c70..c3fc2cb15 100644 --- a/dev.py +++ b/dev.py @@ -16,19 +16,19 @@ def print_file(path_name: str) -> None: Returns: None """ - print(f'Formatting file: {path_name}') + print(f"Formatting file: {path_name}") return None # for each py files exclude from specific folders, sort import -excluded_folders = ['jikanpy', 'venv', '__pycache__'] +excluded_folders = ["jikanpy", "venv", "__pycache__"] -for root, dirs, files in os.walk('.'): +for root, dirs, files in os.walk("."): # exclude the folders in the excluded_folders list dirs[:] = [d for d in dirs if d not in excluded_folders] for file in files: - if file.endswith('.py'): + if file.endswith(".py"): print_file(file) file_path = os.path.join(root, file) @@ -36,13 +36,14 @@ def print_file(path_name: str) -> None: isort.file(file_path) # format the file using autopep8 - subprocess.run(['autopep8', '--in-place', - '--aggressive', '--aggressive', file_path]) + subprocess.run( + ["autopep8", "--in-place", "--aggressive", "--aggressive", file_path] + ) - elif file.endswith('.json'): + elif file.endswith(".json"): print_file(file) file_path = os.path.join(root, file) - with open(file_path, 'r+') as f: + with open(file_path, "r+") as f: data = json.load(f) f.seek(0) json.dump(data, f, indent=2) diff --git a/extensions/anime.py b/extensions/anime.py index e940f9544..a47cd6034 100644 --- a/extensions/anime.py +++ b/extensions/anime.py @@ -25,7 +25,7 @@ async def anime(self, ctx: ipy.SlashContext): name="query", description="The anime title to search for", type=ipy.OptionType.STRING, - required=True + required=True, ), ipy.SlashCommandOption( name="provider", @@ -33,29 +33,27 @@ async def anime(self, ctx: ipy.SlashContext): type=ipy.OptionType.STRING, required=False, choices=[ - ipy.SlashCommandChoice( - name="AniList (Default)", - value="anilist" - ), - ipy.SlashCommandChoice( - name="MyAnimeList", - value="mal" - ), - ] - ) - ] + ipy.SlashCommandChoice(name="AniList (Default)", value="anilist"), + ipy.SlashCommandChoice(name="MyAnimeList", value="mal"), + ], + ), + ], ) - async def anime_search(self, ctx: ipy.SlashContext, query: str, provider: str = "anilist"): + async def anime_search( + self, ctx: ipy.SlashContext, query: str, provider: str = "anilist" + ): await ctx.defer() ul = readUserLang(ctx) l_ = lang(ul, useRaw=True) - send = await ctx.send(embed=ipy.Embed( - title=l_['commons']['search']['init_title'], - description=l_['commons']['search']['init'].format( - QUERY=query, - PLATFORM="MyAnimeList" if provider == "mal" else "AniList", - ), - )) + send = await ctx.send( + embed=ipy.Embed( + title=l_["commons"]["search"]["init_title"], + description=l_["commons"]["search"]["init"].format( + QUERY=query, + PLATFORM="MyAnimeList" if provider == "mal" else "AniList", + ), + ) + ) f = [] so = [] try: @@ -68,23 +66,30 @@ async def anime_search(self, ctx: ipy.SlashContext, query: str, provider: str = if res is None or len(res) == 0: raise Exception("No result") for a in res: - a = a['node'] - if a['start_season'] is None: - a['start_season'] = {'season': 'Unknown', 'year': 'Year'} - media_type: str = a['media_type'].lower() + a = a["node"] + if a["start_season"] is None: + a["start_season"] = {"season": "Unknown", "year": "Year"} + media_type: str = a["media_type"].lower() try: - media_type = l_['commons']['media_formats'][media_type] + media_type = l_["commons"]["media_formats"][media_type] except KeyError: - media_type = l_['commons']['unknown'] - season: str = a['start_season']['season'] if a['start_season']['season'] else 'unknown' - season = l_['commons']['season'][season] - year = a['start_season']['year'] if a['start_season']['year'] else l_[ - 'commons']['year']['unknown'] - title = a['title'] + media_type = l_["commons"]["unknown"] + season: str = ( + a["start_season"]["season"] + if a["start_season"]["season"] + else "unknown" + ) + season = l_["commons"]["season"][season] + year = ( + a["start_season"]["year"] + if a["start_season"]["year"] + else l_["commons"]["year"]["unknown"] + ) + title = a["title"] mdTitle = sanitizeMarkdown(title) - alt = a['alternative_titles'] - if alt is not None and alt['ja'] is not None: - native = sanitizeMarkdown(alt['ja']) + alt = a["alternative_titles"] + if alt is not None and alt["ja"] is not None: + native = sanitizeMarkdown(alt["ja"]) native += "\n" else: native = "" @@ -92,20 +97,19 @@ async def anime_search(self, ctx: ipy.SlashContext, query: str, provider: str = ipy.EmbedField( name=mdTitle[:253] + ("..." if len(mdTitle) > 253 else ""), value=f"{native}`{a['id']}`, {media_type}, {season} {year}", - inline=False + inline=False, ) ] so += [ ipy.StringSelectOption( # trim to 80 chars in total label=title[:77] + ("..." if len(title) > 77 else ""), - value=a['id'], - description=f"{media_type}, {season} {year}" + value=a["id"], + description=f"{media_type}, {season} {year}", ) ] if len(f) >= 1: - title = l_['commons']['search']['result_title'].format( - QUERY=query) + title = l_["commons"]["search"]["result_title"].format(QUERY=query) if provider == "anilist": title += " (AniList)" result = generateSearchSelections( @@ -124,30 +128,29 @@ async def anime_search(self, ctx: ipy.SlashContext, query: str, provider: str = embed=result, components=ipy.ActionRow( ipy.StringSelectMenu( - *so, - placeholder="Choose an anime", - custom_id="mal_search" + *so, placeholder="Choose an anime", custom_id="mal_search" ) - ) + ), ) await asyncio.sleep(60) await send.edit(components=[]) except Exception: - l_ = l_['strings']['anime']['search']['exception'] + l_ = l_["strings"]["anime"]["search"]["exception"] emoji = rSub(r"(<:.*:)(\d+)(>)", r"\2", EMOJI_UNEXPECTED_ERROR) - await send.edit(content="", embed=ipy.Embed( - title=l_['title'], - description=l_['text'].format( - QUERY=query, - ), - color=0xFF0000, - footer=ipy.EmbedFooter( - text=l_['footer'] - ), - thumbnail=ipy.EmbedAttachment( - url=f"https://cdn.discordapp.com/emojis/{emoji}.png?v=1" + await send.edit( + content="", + embed=ipy.Embed( + title=l_["title"], + description=l_["text"].format( + QUERY=query, + ), + color=0xFF0000, + footer=ipy.EmbedFooter(text=l_["footer"]), + thumbnail=ipy.EmbedAttachment( + url=f"https://cdn.discordapp.com/emojis/{emoji}.png?v=1" + ), ), - )) + ) @ipy.component_callback("mal_search") async def anime_search_data(self, ctx: ipy.ComponentContext) -> None: @@ -163,9 +166,9 @@ async def anime_search_data(self, ctx: ipy.ComponentContext) -> None: name="id", description="The anime ID to get information from", type=ipy.OptionType.INTEGER, - required=True + required=True, ), - ] + ], ) async def anime_info(self, ctx: ipy.SlashContext, id: int): await ctx.defer() diff --git a/extensions/commons.py b/extensions/commons.py index 898d70a57..878745b2a 100644 --- a/extensions/commons.py +++ b/extensions/commons.py @@ -5,8 +5,15 @@ import interactions as ipy -from modules.const import (AUTHOR_USERNAME, BOT_CLIENT_ID, BOT_SUPPORT_SERVER, - database, gittyHash, gtHsh, ownerUserUrl) +from modules.const import ( + AUTHOR_USERNAME, + BOT_CLIENT_ID, + BOT_SUPPORT_SERVER, + database, + gittyHash, + gtHsh, + ownerUserUrl, +) from modules.i18n import lang, readUserLang @@ -15,11 +22,10 @@ def __init__(self, bot: ipy.Client, now: dtime = dtime.now(tz=tz.utc)): self.bot = bot self.now = now - @ipy.slash_command(name="about", - description="Get information about the bot") + @ipy.slash_command(name="about", description="Get information about the bot") async def about(self, ctx: ipy.SlashContext): ul = readUserLang(ctx) - l_ = lang(ul)['about'] + l_ = lang(ul)["about"] embed = ipy.Embed( title=l_["header"], description=l_["text"].format( @@ -28,7 +34,7 @@ async def about(self, ctx: ipy.SlashContext): ownerUserUrl=ownerUserUrl, BOT_SUPPORT_SERVER=BOT_SUPPORT_SERVER, gtHsh=gtHsh, - gittyHash=gittyHash + gittyHash=gittyHash, ), color=0x996422, ) @@ -44,17 +50,18 @@ async def about(self, ctx: ipy.SlashContext): async def ping(self, ctx: ipy.SlashContext, defer: bool = False): start = pc() ul = readUserLang(ctx) - l_ = lang(ul)['ping'] + l_ = lang(ul)["ping"] langEnd = pc() if defer: await ctx.defer() # to make sure if benchmark reflects other commands with .defer() send = await ctx.send( "", embed=ipy.Embed( - title=l_['ping']['title'], - description=l_['ping']['text'], + title=l_["ping"]["title"], + description=l_["ping"]["text"], color=0xDD2288, - )) + ), + ) ping = send.created_at.timestamp() pnow = dtime.now(tz=tz.utc).timestamp() end = pc() @@ -64,14 +71,12 @@ async def ping(self, ctx: ipy.SlashContext, defer: bool = False): duration = abs(duration) fields = [ ipy.EmbedField( - name="🤝 " + - l_['websocket']['title'], + name="🤝 " + l_["websocket"]["title"], value=f"`{self.bot.latency * 1000:.2f}`ms\n> *{l_['websocket']['text']}*", inline=True, ), ipy.EmbedField( - name="🤖 " + - l_['bot']['title'], + name="🤖 " + l_["bot"]["title"], value=f"`{duration:.2f}`ms\n> *{l_['bot']['text']}*", inline=True, ), @@ -83,98 +88,103 @@ async def ping(self, ctx: ipy.SlashContext, defer: bool = False): readLat_end = pc() fields += [ ipy.EmbedField( - name="🔎 " + l_['dbRead']['title'], + name="🔎 " + l_["dbRead"]["title"], value=f"`{(readLat_end - readLat_start) * 1000:.2f}`ms\n> *{l_['dbRead']['text']}*", inline=True, ), ipy.EmbedField( - name="🌐 " + l_['langLoad']['title'], + name="🌐 " + l_["langLoad"]["title"], value=f"`{langPerfCount:.2f}`ms\n> *{l_['langLoad']['text']}*", inline=True, ), ipy.EmbedField( - name="🐍 " + l_['pyTime']['title'], + name="🐍 " + l_["pyTime"]["title"], value=f"`{pyPerfCount:.2f}`ms\n> *{l_['pyTime']['text']}*", inline=True, ), ipy.EmbedField( - name="📅 " + l_['uptime']['title'], - value=l_['uptime']['text'].format( - TIMESTAMP=f""), + name="📅 " + l_["uptime"]["title"], + value=l_["uptime"]["text"].format( + TIMESTAMP=f"" + ), inline=True, - )] - await send.edit(content="", embed=ipy.Embed( - title=l_['pong']['title'], - description=l_['pong']['text'], - color=0x996422, - thumbnail=ipy.EmbedAttachment( - url="https://cdn.discordapp.com/attachments/1078005713349115964/1095771964783734874/main.png" ), - fields=fields, - footer=ipy.EmbedFooter( - text=l_['pong']['footer'] - ) - )) + ] + await send.edit( + content="", + embed=ipy.Embed( + title=l_["pong"]["title"], + description=l_["pong"]["text"], + color=0x996422, + thumbnail=ipy.EmbedAttachment( + url="https://cdn.discordapp.com/attachments/1078005713349115964/1095771964783734874/main.png" + ), + fields=fields, + footer=ipy.EmbedFooter(text=l_["pong"]["footer"]), + ), + ) @ipy.slash_command(name="invite", description="Get the bot invite link") async def invite(self, ctx: ipy.SlashContext): ul = readUserLang(ctx) - l_ = lang(ul)['invite'] + l_ = lang(ul)["invite"] invLink = f"https://discord.com/api/oauth2/authorize?client_id={BOT_CLIENT_ID}&permissions=274878221376&scope=bot%20applications.commands" dcEm = ipy.Embed( - title=l_['title'], - description=l_['text'].format(INVBUTTON=l_['buttons']['invite']), + title=l_["title"], + description=l_["text"].format(INVBUTTON=l_["buttons"]["invite"]), color=0x996422, fields=[ ipy.EmbedField( - name=l_['fields']['acc']['title'], - value=l_['fields']['acc']['value'], - inline=True + name=l_["fields"]["acc"]["title"], + value=l_["fields"]["acc"]["value"], + inline=True, ), ipy.EmbedField( - name=l_['fields']['scope']['title'], - value=l_['fields']['scope']['value'], - inline=True + name=l_["fields"]["scope"]["title"], + value=l_["fields"]["scope"]["value"], + inline=True, ), - ] + ], ) invButton = ipy.Button( - label=l_['buttons']['invite'], + label=l_["buttons"]["invite"], url=invLink, style=ipy.ButtonStyle.URL, ) serverButton = ipy.Button( - label=l_['buttons']['support'], + label=l_["buttons"]["support"], url=BOT_SUPPORT_SERVER, style=ipy.ButtonStyle.URL, ) await ctx.send(embed=dcEm, components=[ipy.ActionRow(invButton, serverButton)]) - @ipy.slash_command(name="privacy", - description="Get the bot's tl;dr version of privacy policy") + @ipy.slash_command( + name="privacy", description="Get the bot's tl;dr version of privacy policy" + ) async def privacy(self, ctx: ipy.SlashContext): ul = readUserLang(ctx) - l_ = lang(ul)['privacy'] + l_ = lang(ul)["privacy"] butt = ipy.Button( - label=l_['read'], + label=l_["read"], url="https://github.com/nattadasu/ryuuRyuusei/blob/main/PRIVACY.md", style=ipy.ButtonStyle.URL, ) em = ipy.Embed( - title=l_['title'], - description=l_['text'], + title=l_["title"], + description=l_["text"], color=0x996422, ) await ctx.send(embed=em, components=[ipy.ActionRow(butt)]) - @ipy.slash_command(name="support", - description="Give (financial) support to the bot") + @ipy.slash_command( + name="support", description="Give (financial) support to the bot" + ) async def support(self, ctx: ipy.SlashContext): ul = readUserLang(ctx) - l_ = lang(ul)['support'] - txt: str = l_['text'] + l_ = lang(ul)["support"] + txt: str = l_["text"] em = ipy.Embed( - title=l_['title'], + title=l_["title"], description=txt.format( KOFI="https://ko-fi.com/nattadasu", PAYPAL="https://paypal.me/nattadasu", @@ -183,7 +193,7 @@ async def support(self, ctx: ipy.SlashContext): SAWERIA="https://saweria.co/nattadasu", TRAKTEER="https://trakteer.id/nattadasu", GHREPO="https://github.com/nattadasu/ryuuRyuusei", - SUPPORT=BOT_SUPPORT_SERVER + SUPPORT=BOT_SUPPORT_SERVER, ), color=0x996422, ) diff --git a/extensions/profile.py b/extensions/profile.py index 83f3b3b47..7d35e8a80 100644 --- a/extensions/profile.py +++ b/extensions/profile.py @@ -28,13 +28,13 @@ async def profile(self, ctx: ipy.SlashContext): type=ipy.OptionType.USER, required=False, ) - ] + ], ) async def profile_discord(self, ctx: ipy.SlashContext, user: ipy.User = None): await ctx.defer() ul = readUserLang(ctx) l_ = lang(ul, useRaw=True) - lp = l_['strings']['profile'] + lp = l_["strings"]["profile"] try: if user is None: userId = ctx.author.id @@ -51,17 +51,17 @@ async def profile_discord(self, ctx: ipy.SlashContext, user: ipy.User = None): color = 0x000000 fields = [ ipy.EmbedField( - name=lp['commons']['username'], + name=lp["commons"]["username"], value=data.username + "#" + str(data.discriminator), inline=True, ), ipy.EmbedField( - name=lp['discord']['snowflake'], + name=lp["discord"]["snowflake"], value=f"`{userId}`", inline=True, ), ipy.EmbedField( - name=lp['discord']['joined_discord'], + name=lp["discord"]["joined_discord"], value=f"", inline=True, ), @@ -69,48 +69,48 @@ async def profile_discord(self, ctx: ipy.SlashContext, user: ipy.User = None): avatar = data.avatar.url # if user is on a server, show server-specific info if ctx.guild: - if servData['avatar']: + if servData["avatar"]: avatar = f"https://cdn.discordapp.com/guilds/{ctx.guild.id}/users/{userId}/avatars/{servData['avatar']}" # if avatar is animated, add .gif extension - if servData['avatar'].startswith("a_"): + if servData["avatar"].startswith("a_"): avatar += ".gif" else: avatar += ".png" avatar += "?size=4096" - if servData['nick'] is not None: - nick = sanitizeMarkdown(servData['nick']) + if servData["nick"] is not None: + nick = sanitizeMarkdown(servData["nick"]) else: nick = sanitizeMarkdown(data.username) - nick += " " + lp['commons']['default'] - joined = dtime.strptime( - servData['joined_at'], "%Y-%m-%dT%H:%M:%S.%f%z") + nick += " " + lp["commons"]["default"] + joined = dtime.strptime(servData["joined_at"], "%Y-%m-%dT%H:%M:%S.%f%z") joined = int(joined.timestamp()) joined = f"" - if servData['premium_since']: + if servData["premium_since"]: premium = dtime.strptime( - servData['premium_since'], "%Y-%m-%dT%H:%M:%S.%f%z") + servData["premium_since"], "%Y-%m-%dT%H:%M:%S.%f%z" + ) premium: int = int(premium.timestamp()) - premium = lp['discord']['boost_since'].format( + premium = lp["discord"]["boost_since"].format( TIMESTAMP=f"" ) else: - premium = lp['discord']['not_boosting'] + premium = lp["discord"]["not_boosting"] fields += [ ipy.EmbedField( - name=lp['discord']['joined_server'], + name=lp["discord"]["joined_server"], value=joined, inline=True, ), ipy.EmbedField( - name=lp['commons']['nickname'], + name=lp["commons"]["nickname"], value=nick, inline=True, ), ipy.EmbedField( - name=lp['discord']['boost_status'], + name=lp["discord"]["boost_status"], value=premium, inline=True, - ) + ), ] if data.banner is not None: banner = data.banner.url @@ -119,33 +119,28 @@ async def profile_discord(self, ctx: ipy.SlashContext, user: ipy.User = None): botStatus = "" regStatus = "" if data.bot: - botStatus = "\n🤖 " + lp['commons']['bot'] + botStatus = "\n🤖 " + lp["commons"]["bot"] async with UserDatabase() as db: reg = await db.check_if_registered(discord_id=userId) if reg is True: - regStatus = "\n✅ " + lp['discord']['registered'] + regStatus = "\n✅ " + lp["discord"]["registered"] embed = ipy.Embed( - title=lp['discord']['title'], - description=lp['commons']['about'].format( + title=lp["discord"]["title"], + description=lp["commons"]["about"].format( USER=data.mention, - ) + botStatus + regStatus, - thumbnail=ipy.EmbedAttachment( - url=avatar - ), + ) + + botStatus + + regStatus, + thumbnail=ipy.EmbedAttachment(url=avatar), color=color, fields=fields, - images=[ - ipy.EmbedAttachment( - url=banner - ) - ] + images=[ipy.EmbedAttachment(url=banner)], ) await ctx.send(embed=embed) except Exception as e: embed = generalExceptionEmbed( - description=l_[ - 'strings']['profile']['commons']['exception']['general'], + description=l_["strings"]["profile"]["commons"]["exception"]["general"], error=e, lang_dict=l_, ) @@ -166,12 +161,18 @@ async def profile_discord(self, ctx: ipy.SlashContext, user: ipy.User = None): description="Username on MyAnimeList to get profile information of", type=ipy.OptionType.STRING, required=False, - )]) - async def profile_myanimelist(self, ctx: ipy.SlashContext, user: ipy.User = None, mal_username: str = None): + ), + ], + ) + async def profile_myanimelist( + self, ctx: ipy.SlashContext, user: ipy.User = None, mal_username: str = None + ): # await ctx.defer() # ul = readUserLang(ctx) # l_ = lang(ul, useRaw=True) - await ctx.send("This command is currently disabled as it not have been implemented yet.") + await ctx.send( + "This command is currently disabled as it not have been implemented yet." + ) def setup(bot): diff --git a/extensions/random.py b/extensions/random.py index 88ccc6dfb..87e1aa0ae 100644 --- a/extensions/random.py +++ b/extensions/random.py @@ -3,7 +3,7 @@ from classes.nekomimidb import NekomimiDb as neko from classes.randomorg import RandomOrg from modules.i18n import lang, readUserLang -from modules.myanimelist import (lookupRandomAnime, malSubmit) +from modules.myanimelist import lookupRandomAnime, malSubmit from modules.nekomimidb import nekomimiSubmit gender = neko.Gender @@ -23,23 +23,27 @@ async def random(self, ctx: ipy.SlashContext): ) async def random_anime(self, ctx: ipy.SlashContext): await ctx.defer() - send = await ctx.send(embed=ipy.Embed( - title="Random Anime", - description="Getting a random anime...", - color=0x213498, - footer=ipy.EmbedFooter( - text="This may take a while...", - ), - )) + send = await ctx.send( + embed=ipy.Embed( + title="Random Anime", + description="Getting a random anime...", + color=0x213498, + footer=ipy.EmbedFooter( + text="This may take a while...", + ), + ) + ) anime = lookupRandomAnime() - await send.edit(embed=ipy.Embed( - title="Random Anime", - description=f"We've found MAL ID [`{anime}`](https://myanimelist.net/anime/{anime}). Fetching info...", - color=0x213498, - footer=ipy.EmbedFooter( - text="This may take a while...", - ), - )) + await send.edit( + embed=ipy.Embed( + title="Random Anime", + description=f"We've found MAL ID [`{anime}`](https://myanimelist.net/anime/{anime}). Fetching info...", + color=0x213498, + footer=ipy.EmbedFooter( + text="This may take a while...", + ), + ) + ) await malSubmit(ctx, anime) @random.subcommand( @@ -51,7 +55,7 @@ async def random_anime(self, ctx: ipy.SlashContext): async def random_nekomimi_boy(self, ctx: ipy.SlashContext): await ctx.defer() ul = readUserLang(ctx) - l_ = lang(ul)['random']['nekomimi'] + l_ = lang(ul)["random"]["nekomimi"] await nekomimiSubmit(ctx=ctx, gender=gender.BOY, lang=l_) @random.subcommand( @@ -63,7 +67,7 @@ async def random_nekomimi_boy(self, ctx: ipy.SlashContext): async def random_nekomimi_girl(self, ctx: ipy.SlashContext): await ctx.defer() ul = readUserLang(ctx) - l_ = lang(ul)['random']['nekomimi'] + l_ = lang(ul)["random"]["nekomimi"] await nekomimiSubmit(ctx=ctx, gender=gender.GIRL, lang=l_) @random.subcommand( @@ -75,7 +79,7 @@ async def random_nekomimi_girl(self, ctx: ipy.SlashContext): async def random_nekomimi_randomize(self, ctx: ipy.SlashContext): await ctx.defer() ul = readUserLang(ctx) - l_ = lang(ul)['random']['nekomimi'] + l_ = lang(ul)["random"]["nekomimi"] await nekomimiSubmit(ctx=ctx, lang=l_) @random.subcommand( @@ -123,20 +127,29 @@ async def random_nekomimi_randomize(self, ctx: ipy.SlashContext): name="Hexadecimal", value=16, ), - ] + ], ), - ] + ], ) - async def random_number(self, ctx: ipy.SlashContext, numbers: int = 1, min: int = 1, max: int = 10, base: int = 10): + async def random_number( + self, + ctx: ipy.SlashContext, + numbers: int = 1, + min: int = 1, + max: int = 10, + base: int = 10, + ): await ctx.defer() async with RandomOrg() as rand: numbers = await rand.integers(num=numbers, min=min, max=max, base=base) # convert arrays of int to arrays of str numbers = [str(i) for i in numbers] - await ctx.send(embed=ipy.Embed( - description=f"```py\n{', '.join(numbers)}\n```", - color=0x1F1F1F, - )) + await ctx.send( + embed=ipy.Embed( + description=f"```py\n{', '.join(numbers)}\n```", + color=0x1F1F1F, + ) + ) @random.subcommand( sub_cmd_name="string", @@ -174,19 +187,36 @@ async def random_number(self, ctx: ipy.SlashContext, numbers: int = 1, min: int required=False, type=ipy.OptionType.BOOLEAN, ), - ] + ], ) - async def random_string(self, ctx: ipy.SlashContext, length: int = 10, amount: int = 1, use_uppercase: bool = True, use_lowercase: bool = True, use_digits: bool = True): + async def random_string( + self, + ctx: ipy.SlashContext, + length: int = 10, + amount: int = 1, + use_uppercase: bool = True, + use_lowercase: bool = True, + use_digits: bool = True, + ): upper = "off" if not use_uppercase else "on" lower = "off" if not use_lowercase else "on" digits = "off" if not use_digits else "on" await ctx.defer() async with RandomOrg() as rand: - strings = await rand.strings(length=length, num=amount, upperalpha=upper, loweralpha=lower, digits=digits, unique="on") - await ctx.send(embed=ipy.Embed( - description=f"```py\n{', '.join(strings)}\n```", - color=0x1F1F1F, - )) + strings = await rand.strings( + length=length, + num=amount, + upperalpha=upper, + loweralpha=lower, + digits=digits, + unique="on", + ) + await ctx.send( + embed=ipy.Embed( + description=f"```py\n{', '.join(strings)}\n```", + color=0x1F1F1F, + ) + ) def setup(bot): diff --git a/extensions/serversettings.py b/extensions/serversettings.py index 6e59eda00..7efa89983 100644 --- a/extensions/serversettings.py +++ b/extensions/serversettings.py @@ -43,15 +43,17 @@ async def code_autocomplete(self, ctx: ipy.AutocompleteContext): final = [] for di in data: try: - if di['name'] == 'Serbian': - di['dialect'] = 'Serbia' - flag = di['dialect'].replace(' ', '_') + if di["name"] == "Serbian": + di["dialect"] = "Serbia" + flag = di["dialect"].replace(" ", "_") flag = emojize(f":{flag}:", language="alias") - final.append({ - # 'name': flag + " " + di['name'] + " (" + di['native'] + ", " + di['dialect'] + ")", - 'name': f"{flag} {di['name']} ({di['native']}, {di['dialect']})", - 'value': di['code'] - }) + final.append( + { + # 'name': flag + " " + di['name'] + " (" + di['native'] + ", " + di['dialect'] + ")", + "name": f"{flag} {di['name']} ({di['native']}, {di['dialect']})", + "value": di["code"], + } + ) except BaseException: break await ctx.send(choices=final) diff --git a/extensions/usersettings.py b/extensions/usersettings.py index d91357c8f..dd1dabde7 100644 --- a/extensions/usersettings.py +++ b/extensions/usersettings.py @@ -50,14 +50,16 @@ async def code_autocomplete(self, ctx: ipy.AutocompleteContext): final = [] for di in data: try: - if di['name'] == 'Serbian': - di['dialect'] = 'Serbia' - flag = di['dialect'].replace(' ', '_') + if di["name"] == "Serbian": + di["dialect"] = "Serbia" + flag = di["dialect"].replace(" ", "_") flag = emojize(f":{flag}:", language="alias") - final.append({ - 'name': f"{flag} {di['name']} ({di['native']}, {di['dialect']})", - 'value': di['code'] - }) + final.append( + { + "name": f"{flag} {di['name']} ({di['native']}, {di['dialect']})", + "value": di["code"], + } + ) except BaseException: break await ctx.send(choices=final) diff --git a/extensions/utilities.py b/extensions/utilities.py index da59b34e3..e92775007 100644 --- a/extensions/utilities.py +++ b/extensions/utilities.py @@ -26,40 +26,44 @@ async def utilities(self, ctx: ipy.SlashContext): name="expression", description="The expression to evaluate", type=ipy.OptionType.STRING, - required=True + required=True, ) - ] + ], ) async def utilities_math(self, ctx: ipy.SlashContext, expression: str): await ctx.defer() ul = readUserLang(ctx) - l_ = lang(ul)['utilities'] + l_ = lang(ul)["utilities"] try: exp = BAP().evaluate(expression) - await ctx.send(embed=ipy.Embed( - title=l_['commons']['result'], - color=0x996422, - fields=[ - ipy.EmbedField( - name=l_['math']['expression'], - value=f"```py\n{expression}```", - inline=False - ), - ipy.EmbedField( - name=l_['commons']['result'], - value=f"```py\n{exp}```", - inline=False - ) - ] - )) + await ctx.send( + embed=ipy.Embed( + title=l_["commons"]["result"], + color=0x996422, + fields=[ + ipy.EmbedField( + name=l_["math"]["expression"], + value=f"```py\n{expression}```", + inline=False, + ), + ipy.EmbedField( + name=l_["commons"]["result"], + value=f"```py\n{exp}```", + inline=False, + ), + ], + ) + ) except Exception as e: - await ctx.send(embed=utilitiesExceptionEmbed( - language=ul, - description=l_['math']['exception'], - field_name=l_['math']['expression'], - field_value=f"```py\n{expression}```", - error=e - )) + await ctx.send( + embed=utilitiesExceptionEmbed( + language=ul, + description=l_["math"]["exception"], + field_name=l_["math"]["expression"], + field_value=f"```py\n{expression}```", + error=e, + ) + ) @utilities.subcommand( sub_cmd_name="base64", @@ -71,28 +75,22 @@ async def utilities_math(self, ctx: ipy.SlashContext, expression: str): type=ipy.OptionType.STRING, required=True, choices=[ - ipy.SlashCommandChoice( - name="Encode", - value="encode" - ), - ipy.SlashCommandChoice( - name="Decode", - value="decode" - ) - ] + ipy.SlashCommandChoice(name="Encode", value="encode"), + ipy.SlashCommandChoice(name="Decode", value="decode"), + ], ), ipy.SlashCommandOption( name="string", description="The string to encode/decode", type=ipy.OptionType.STRING, - required=True - ) - ] + required=True, + ), + ], ) async def utilities_base64(self, ctx: ipy.SlashContext, mode: str, string: str): await ctx.defer() ul = readUserLang(ctx) - l_ = lang(ul)['utilities'] + l_ = lang(ul)["utilities"] try: if mode == "encode": res = b64encode(string.encode()).decode() @@ -106,30 +104,30 @@ async def utilities_base64(self, ctx: ipy.SlashContext, mode: str, string: str): resVal = f"""```md {res} ```""" - await ctx.send(embed=ipy.Embed( - title=l_['commons']['result'], - color=0x996422, - fields=[ - ipy.EmbedField( - name=l_['base64']['string'], - value=strVal, - inline=False - ), - ipy.EmbedField( - name=l_['commons']['result'], - value=resVal, - inline=False - ) - ] - )) + await ctx.send( + embed=ipy.Embed( + title=l_["commons"]["result"], + color=0x996422, + fields=[ + ipy.EmbedField( + name=l_["base64"]["string"], value=strVal, inline=False + ), + ipy.EmbedField( + name=l_["commons"]["result"], value=resVal, inline=False + ), + ], + ) + ) except Exception as e: - await ctx.send(embed=utilitiesExceptionEmbed( - language=ul, - description=l_['base64']['exception'], - field_name=l_['commons']['string'], - field_value=f"```{string}```", - error=e - )) + await ctx.send( + embed=utilitiesExceptionEmbed( + language=ul, + description=l_["base64"]["exception"], + field_name=l_["commons"]["string"], + field_value=f"```{string}```", + error=e, + ) + ) @utilities.subcommand( sub_cmd_name="color", @@ -141,40 +139,30 @@ async def utilities_base64(self, ctx: ipy.SlashContext, mode: str, string: str): type=ipy.OptionType.STRING, required=True, choices=[ - ipy.SlashCommandChoice( - name="Hex", - value="hex" - ), - ipy.SlashCommandChoice( - name="RGB", - value="rgb" - ), - ipy.SlashCommandChoice( - name="HSL", - value="hsl" - ), - ipy.SlashCommandChoice( - name="CMYK", - value="cmyk" - ), + ipy.SlashCommandChoice(name="Hex", value="hex"), + ipy.SlashCommandChoice(name="RGB", value="rgb"), + ipy.SlashCommandChoice(name="HSL", value="hsl"), + ipy.SlashCommandChoice(name="CMYK", value="cmyk"), ], ), ipy.SlashCommandOption( name="color", description="The color to get information about", type=ipy.OptionType.STRING, - required=True + required=True, ), - ] + ], ) async def utilities_color(self, ctx: ipy.SlashContext, format: str, color: str): await ctx.defer() ul = readUserLang(ctx) - l_ = lang(ul)['utilities'] + l_ = lang(ul)["utilities"] res: dict = {} try: - if format == "hex" and re.match( - r"^#?(?:[0-9a-fA-F]{3}){1,2}$", color) is None: + if ( + format == "hex" + and re.match(r"^#?(?:[0-9a-fA-F]{3}){1,2}$", color) is None + ): raise ValueError("Invalid hex color") elif format == "hex" and re.match(r"^#", color) is None: color = f"#{color}" @@ -189,45 +177,45 @@ async def utilities_color(self, ctx: ipy.SlashContext, format: str, color: str): case "cmyk": res = await tca.color(cmyk=color) # await tca.close() - rgb: dict = res['rgb'] - col: int = (rgb['r'] << 16) + (rgb['g'] << 8) + rgb['b'] + rgb: dict = res["rgb"] + col: int = (rgb["r"] << 16) + (rgb["g"] << 8) + rgb["b"] fields = [ ipy.EmbedField( - name=l_['color']['name'], + name=l_["color"]["name"], value=f"{res['name']['value']}", - inline=False + inline=False, ), ] - for f in ['hex', 'rgb', 'hsl', 'cmyk', 'hsv']: - fields.append(ipy.EmbedField( - name=f"{f.upper()}", - value=f"```css\n{res[f]['value']}\n```", - inline=True - )) - fields.append(ipy.EmbedField( - name="DEC", - value=f"```py\n{col}\n```", - inline=True - )) - await ctx.send(embed=ipy.Embed( - title=l_['commons']['result'], - color=col, - fields=fields, - thumbnail=ipy.EmbedAttachment( - url=res['image']['bare'] - ), - footer=ipy.EmbedFooter( - text=l_['color']['powered'] + for f in ["hex", "rgb", "hsl", "cmyk", "hsv"]: + fields.append( + ipy.EmbedField( + name=f"{f.upper()}", + value=f"```css\n{res[f]['value']}\n```", + inline=True, + ) + ) + fields.append( + ipy.EmbedField(name="DEC", value=f"```py\n{col}\n```", inline=True) + ) + await ctx.send( + embed=ipy.Embed( + title=l_["commons"]["result"], + color=col, + fields=fields, + thumbnail=ipy.EmbedAttachment(url=res["image"]["bare"]), + footer=ipy.EmbedFooter(text=l_["color"]["powered"]), ) - )) + ) except Exception as e: - await ctx.send(embed=utilitiesExceptionEmbed( - language=ul, - description=l_['color']['exception'], - field_name=l_['color']['color'], - field_value=f"```{color}```", - error=e, - )) + await ctx.send( + embed=utilitiesExceptionEmbed( + language=ul, + description=l_["color"]["exception"], + field_name=l_["color"]["color"], + field_value=f"```{color}```", + error=e, + ) + ) @utilities.subcommand( sub_cmd_name="qrcode", @@ -237,7 +225,7 @@ async def utilities_color(self, ctx: ipy.SlashContext, format: str, color: str): name="string", description="The string to encode", type=ipy.OptionType.STRING, - required=True + required=True, ), ipy.SlashCommandOption( name="error_correction", @@ -245,30 +233,20 @@ async def utilities_color(self, ctx: ipy.SlashContext, format: str, color: str): type=ipy.OptionType.STRING, required=False, choices=[ - ipy.SlashCommandChoice( - name="Low (~7%, default)", - value="L" - ), - ipy.SlashCommandChoice( - name="Medium (~15%)", - value="M" - ), - ipy.SlashCommandChoice( - name="Quality (~25%)", - value="Q" - ), - ipy.SlashCommandChoice( - name="High (~30%)", - value="H" - ), + ipy.SlashCommandChoice(name="Low (~7%, default)", value="L"), + ipy.SlashCommandChoice(name="Medium (~15%)", value="M"), + ipy.SlashCommandChoice(name="Quality (~25%)", value="Q"), + ipy.SlashCommandChoice(name="High (~30%)", value="H"), ], ), - ] + ], ) - async def utilities_qrcode(self, ctx: ipy.SlashContext, string: str, error_correction: str = "L"): + async def utilities_qrcode( + self, ctx: ipy.SlashContext, string: str, error_correction: str = "L" + ): await ctx.defer() ul = readUserLang(ctx) - l_ = lang(ul)['utilities'] + l_ = lang(ul)["utilities"] try: params = { "data": string, @@ -278,31 +256,35 @@ async def utilities_qrcode(self, ctx: ipy.SlashContext, string: str, error_corre } # convert params object to string params = urlenc(params) - await ctx.send(embed=ipy.Embed( - title=l_['commons']['result'], - color=0x000000, - fields=[ - ipy.EmbedField( - name=l_['commons']['string'], - value=f"```{string}```", - inline=False - ), - ], - images=[ipy.EmbedAttachment( - url=f"https://api.qrserver.com/v1/create-qr-code/?{params}" - )], - footer=ipy.EmbedFooter( - text=l_['qrcode']['powered'] + await ctx.send( + embed=ipy.Embed( + title=l_["commons"]["result"], + color=0x000000, + fields=[ + ipy.EmbedField( + name=l_["commons"]["string"], + value=f"```{string}```", + inline=False, + ), + ], + images=[ + ipy.EmbedAttachment( + url=f"https://api.qrserver.com/v1/create-qr-code/?{params}" + ) + ], + footer=ipy.EmbedFooter(text=l_["qrcode"]["powered"]), ) - )) + ) except Exception as e: - await ctx.send(embed=utilitiesExceptionEmbed( - language=ul, - description=l_['qrcode']['exception'], - field_name=l_['commons']['string'], - field_value=f"```{string}```", - error=e, - )) + await ctx.send( + embed=utilitiesExceptionEmbed( + language=ul, + description=l_["qrcode"]["exception"], + field_name=l_["commons"]["string"], + field_value=f"```{string}```", + error=e, + ) + ) @utilities.subcommand( sub_cmd_name="snowflake", @@ -314,46 +296,38 @@ async def utilities_qrcode(self, ctx: ipy.SlashContext, string: str, error_corre required=True, type=ipy.OptionType.STRING, ) - ] + ], ) async def utilities_snowflake(self, ctx: ipy.SlashContext, snowflake: str): """Convert a Discord Snowflake to a timestamp""" await ctx.defer() ul = readUserLang(ctx) - l_ = lang(ul)['utilities']['snowflake'] + l_ = lang(ul)["utilities"]["snowflake"] tmsp = int(snowflake_to_datetime(int(snowflake))) - await ctx.send(embed=ipy.Embed( - title=l_['title'], - description=l_['text'], - color=0x1F1F1F, - fields=[ - ipy.EmbedField( - name=l_['snowflake'], - value=f"```py\n{snowflake}\n```", - inline=False - ), - ipy.EmbedField( - name=l_['timestamp'], - value=f"```py\n{tmsp}\n```", - inline=False - ), - ipy.EmbedField( - name=l_['date'], - value=f"", - inline=True - ), - ipy.EmbedField( - name=l_['full_date'], - value=f"", - inline=True - ), - ipy.EmbedField( - name=l_['relative'], - value=f"", - inline=True - ), - ] - )) + await ctx.send( + embed=ipy.Embed( + title=l_["title"], + description=l_["text"], + color=0x1F1F1F, + fields=[ + ipy.EmbedField( + name=l_["snowflake"], + value=f"```py\n{snowflake}\n```", + inline=False, + ), + ipy.EmbedField( + name=l_["timestamp"], value=f"```py\n{tmsp}\n```", inline=False + ), + ipy.EmbedField(name=l_["date"], value=f"", inline=True), + ipy.EmbedField( + name=l_["full_date"], value=f"", inline=True + ), + ipy.EmbedField( + name=l_["relative"], value=f"", inline=True + ), + ], + ) + ) def setup(bot): diff --git a/firstRun.py b/firstRun.py index a227d4067..1b45eabf5 100644 --- a/firstRun.py +++ b/firstRun.py @@ -13,8 +13,7 @@ import shlex import subprocess -from modules.oobe.commons import (checkTermux, current_os, prepare_database, - py_bin_path) +from modules.oobe.commons import checkTermux, current_os, prepare_database, py_bin_path from modules.oobe.getNekomimi import nk_run from modules.oobe.i18nBuild import convert_langs_to_json from modules.oobe.jikan import install_jikanpy, update_jikanpy @@ -37,10 +36,11 @@ def first_run(py_bin: str = py_bin_path()): if not os.path.exists("requirements.txt"): raise Exception("Please run the script from the root directory.") # Check if Termux is used - env = "MATHLAB=\"m\" " if checkTermux() else "" + env = 'MATHLAB="m" ' if checkTermux() else "" # Check if jikanpy is installed and up-to-date try: from jikanpy import AioJikan + print("Checking for jikanpy updates...") os.chdir("jikanpy") os.system("git pull") @@ -51,12 +51,29 @@ def first_run(py_bin: str = py_bin_path()): except ImportError: install_jikanpy() # Install dependencies - print("Installing and upgrading dependencies for the next step and the bot itself...") + print( + "Installing and upgrading dependencies for the next step and the bot itself..." + ) # os.system(f"{env}{py_bin} -m pip install -U -r requirements.txt") if current_os() == "Windows": - subprocess.run([env + py_bin, "-m", "pip", "install", "-U", "-r", "requirements.txt"], shell=False) + subprocess.run( + [env + py_bin, "-m", "pip", "install", "-U", "-r", "requirements.txt"], + shell=False, + ) else: - os.system(shlex.join([env + shlex.quote(py_bin), "-m", "pip", "install", "-U", "-r", "requirements.txt"])) + os.system( + shlex.join( + [ + env + shlex.quote(py_bin), + "-m", + "pip", + "install", + "-U", + "-r", + "requirements.txt", + ] + ) + ) # Prepare the database print("Preparing the database as database.csv in tabbed format...") prepare_database() diff --git a/main.py b/main.py index 8e9c8d274..0385bd308 100644 --- a/main.py +++ b/main.py @@ -21,7 +21,7 @@ activity=ipy.Activity( name="Kagamine Len's Live Concert", type=ipy.ActivityType.WATCHING, - ) + ), ) @@ -33,8 +33,7 @@ async def on_ready(): """ guilds = len(bot.guilds) print("[Sys] Bot is ready!") - print(" Logged in as: " + bot.user.display_name + - "#" + bot.user.discriminator) + print(" Logged in as: " + bot.user.display_name + "#" + bot.user.discriminator) print(" User ID: " + str(bot.user.id)) print(" Guilds: " + str(guilds)) @@ -45,8 +44,7 @@ async def main(): This function will be run before the bot starts. """ if SENTRY_DSN: - bot.load_extension('interactions.ext.sentry', - token=SENTRY_DSN) + bot.load_extension("interactions.ext.sentry", token=SENTRY_DSN) bot.load_extension("interactions.ext.jurigged") bot.del_unused_app_cmd = True bot.sync_interactions = True @@ -88,5 +86,10 @@ async def main(): print("[Sys] Bot stopped by user.") now: dtime = dtime.now(tz=tz.utc) print("[Bcm] Date: " + now.strftime("%d/%m/%Y %H:%M:%S")) - print(" Uptime: " + str(int(bot_stop - bot_run)) + - "s, or around " + str(int((bot_stop - bot_run) / 60)) + "m") + print( + " Uptime: " + + str(int(bot_stop - bot_run)) + + "s, or around " + + str(int((bot_stop - bot_run) / 60)) + + "m" + ) diff --git a/modules/anilist.py b/modules/anilist.py index a43694dc0..ffadc4364 100644 --- a/modules/anilist.py +++ b/modules/anilist.py @@ -11,7 +11,9 @@ async def searchAniListAnime(title: str) -> dict: dict: The formatted data """ async with AniList() as anilist: - data = await anilist.search_media(title, limit=5, media_type=anilist.MediaType.ANIME) + data = await anilist.search_media( + title, limit=5, media_type=anilist.MediaType.ANIME + ) # Create an empty list to store the formatted data formatted_data = [] @@ -25,11 +27,15 @@ async def searchAniListAnime(title: str) -> dict: "title": item["title"]["romaji"], "alternative_titles": { "en": item["title"]["english"] if item["title"]["english"] else "", - "ja": item["title"]["native"]}, + "ja": item["title"]["native"], + }, "start_season": { "year": item["startDate"]["year"], - "season": item["season"].lower() if item["season"] else None}, - "media_type": item["format"].lower() if item["format"] else None}} + "season": item["season"].lower() if item["season"] else None, + }, + "media_type": item["format"].lower() if item["format"] else None, + } + } # Append the formatted data to the list formatted_data.append(formatted_item) diff --git a/modules/commons.py b/modules/commons.py index 5d8c82173..147928288 100644 --- a/modules/commons.py +++ b/modules/commons.py @@ -5,9 +5,16 @@ from re import sub as rSub from uuid import uuid4 as id4 -from interactions import (Button, ButtonStyle, Client, Embed, EmbedAttachment, - EmbedAuthor, EmbedField, - PartialEmoji) +from interactions import ( + Button, + ButtonStyle, + Client, + Embed, + EmbedAttachment, + EmbedAuthor, + EmbedField, + PartialEmoji, +) from modules.const import BOT_TOKEN from modules.const import EMOJI_UNEXPECTED_ERROR as EUNER @@ -69,19 +76,21 @@ def sanitize_markdown(text: str) -> str: Returns: str: The sanitized string of text. """ - text = (text.replace("*", "\\*") - .replace("_", "\\_") - .replace("`", "\\`") - .replace("~", "\\~") - .replace("|", "\\|") - .replace(">", "\\>") - .replace("<", "\\<") - .replace("[", "\\[") - .replace("]", "\\]") - .replace("(", "\\(") - .replace(")", "\\)") - .replace("/", "\\/") - .replace("@", "\\@")) + text = ( + text.replace("*", "\\*") + .replace("_", "\\_") + .replace("`", "\\`") + .replace("~", "\\~") + .replace("|", "\\|") + .replace(">", "\\>") + .replace("<", "\\<") + .replace("[", "\\[") + .replace("]", "\\]") + .replace("(", "\\(") + .replace(")", "\\)") + .replace("/", "\\/") + .replace("@", "\\@") + ) return text @@ -139,24 +148,13 @@ def generateSearchSelections( case 2: count = l_["quantities"][f"{mediaType}"]["two"] case _: - count = l_["quantities"][f"{mediaType}"]["many"].format( - count=len(results) - ) + count = l_["quantities"][f"{mediaType}"]["many"].format(count=len(results)) dcEm = Embed( - author=EmbedAuthor( - name=platform, - url=homepage, - icon_url=icon - ), - thumbnail=EmbedAttachment( - url=icon - ), + author=EmbedAuthor(name=platform, url=homepage, icon_url=icon), + thumbnail=EmbedAttachment(url=icon), color=color, title=title, - description=l_['commons']['search']['result'].format( - COUNT=count, - QUERY=query - ), + description=l_["commons"]["search"]["result"].format(COUNT=count, QUERY=query), fields=results, ) @@ -193,19 +191,13 @@ def utilitiesExceptionEmbed( emoji = rSub(r"(<:.*:)(\d+)(>)", r"\2", EUNER) dcEm = Embed( color=color, - title=l_['commons']['error'], + title=l_["commons"]["error"], description=description, fields=[ + EmbedField(name=field_name, value=field_value, inline=False), EmbedField( - name=field_name, - value=field_value, - inline=False + name=l_["commons"]["reason"], value=f"```md\n{error}\n```", inline=False ), - EmbedField( - name=l_['commons']['reason'], - value=f"```md\n{error}\n```", - inline=False - ) ], thumbnail=EmbedAttachment( url=f"https://cdn.discordapp.com/emojis/{emoji}.png?v=1" @@ -242,13 +234,11 @@ def generalExceptionEmbed( emoji = rSub(r"(<:.*:)(\d+)(>)", r"\2", EUNER) dcEm = Embed( color=color, - title=l_['commons']['error'], + title=l_["commons"]["error"], description=description, fields=[ EmbedField( - name=l_['commons']['reason'], - value=f"```md\n{error}\n```", - inline=False + name=l_["commons"]["reason"], value=f"```md\n{error}\n```", inline=False ) ], thumbnail=EmbedAttachment( @@ -260,9 +250,8 @@ def generalExceptionEmbed( def generate_trailer( - data: dict, - is_mal: bool = False, - is_simkl: bool = False) -> Button: + data: dict, is_mal: bool = False, is_simkl: bool = False +) -> Button: """ Generate a button for playing the trailer of a given anime. @@ -280,19 +269,16 @@ def generate_trailer( """ if is_mal: - ytid = data['youtube_id'] + ytid = data["youtube_id"] elif is_simkl: - ytid = data['youtube'] + ytid = data["youtube"] else: - ytid = data['id'] + ytid = data["id"] final = Button( label="PV/CM on YouTube", style=ButtonStyle.LINK, url=f"https://www.youtube.com/watch?v={ytid}", - emoji=PartialEmoji( - id=975564205228965918, - name="Youtube" - ) + emoji=PartialEmoji(id=975564205228965918, name="Youtube"), ) return final @@ -316,4 +302,4 @@ async def get_parent_nsfw_status(snowflake: int) -> bool: bot_http = Client(token=BOT_TOKEN).http guild = await bot_http.get_channel(channel_id=snowflake) # close the connection - return guild.get('nsfw', False) + return guild.get("nsfw", False) diff --git a/modules/const.py b/modules/const.py index 2dd72a003..c6aeeb56d 100644 --- a/modules/const.py +++ b/modules/const.py @@ -19,32 +19,32 @@ database = r"database/database.csv" -AUTHOR_USERID: Final[int] = ge('AUTHOR_USERID') -AUTHOR_USERNAME: Final[str] = ge('AUTHOR_USERNAME') -BOT_CLIENT_ID: Final[int] = ge('BOT_CLIENT_ID') -BOT_SUPPORT_SERVER: Final[str] = ge('BOT_SUPPORT_SERVER') -BOT_TOKEN: Final[str] = ge('BOT_TOKEN') -CLUB_ID: Final[int] = ge('CLUB_ID') -LASTFM_API_KEY: Final[str] = ge('LASTFM_API_KEY') -MYANIMELIST_CLIENT_ID: Final[str] = ge('MYANIMELIST_CLIENT_ID') -RAWG_API_KEY: Final[str] = ge('RAWG_API_KEY') -SENTRY_DSN: Final[str] = ge('SENTRY_DSN') -SIMKL_CLIENT_ID: Final[str] = ge('SIMKL_CLIENT_ID') -TMDB_API_KEY: Final[str] = ge('TMDB_API_KEY') -TMDB_API_VERSION: Final[int] = ge('TMDB_API_VERSION') -TRAKT_API_VERSION: Final[int] = ge('TRAKT_API_VERSION') -TRAKT_CLIENT_ID: Final[str] = ge('TRAKT_CLIENT_ID') -VERIFICATION_SERVER: Final[int] = ge('VERIFICATION_SERVER') -VERIFIED_ROLE: Final[int] = ge('VERIFIED_ROLE') - -EMOJI_ATTENTIVE: Final[str] = ge('EMOJI_ATTENTIVE') -EMOJI_DOUBTING: Final[str] = ge('EMOJI_DOUBTING') -EMOJI_FORBIDDEN: Final[str] = ge('EMOJI_FORBIDDEN') -EMOJI_SUCCESS: Final[str] = ge('EMOJI_SUCCESS') -EMOJI_UNEXPECTED_ERROR: Final[str] = ge('EMOJI_UNEXPECTED_ERROR') -EMOJI_USER_ERROR: Final[str] = ge('EMOJI_USER_ERROR') - -LANGUAGE_CODE: Final[str] = ge('LANGUAGE_CODE') +AUTHOR_USERID: Final[int] = ge("AUTHOR_USERID") +AUTHOR_USERNAME: Final[str] = ge("AUTHOR_USERNAME") +BOT_CLIENT_ID: Final[int] = ge("BOT_CLIENT_ID") +BOT_SUPPORT_SERVER: Final[str] = ge("BOT_SUPPORT_SERVER") +BOT_TOKEN: Final[str] = ge("BOT_TOKEN") +CLUB_ID: Final[int] = ge("CLUB_ID") +LASTFM_API_KEY: Final[str] = ge("LASTFM_API_KEY") +MYANIMELIST_CLIENT_ID: Final[str] = ge("MYANIMELIST_CLIENT_ID") +RAWG_API_KEY: Final[str] = ge("RAWG_API_KEY") +SENTRY_DSN: Final[str] = ge("SENTRY_DSN") +SIMKL_CLIENT_ID: Final[str] = ge("SIMKL_CLIENT_ID") +TMDB_API_KEY: Final[str] = ge("TMDB_API_KEY") +TMDB_API_VERSION: Final[int] = ge("TMDB_API_VERSION") +TRAKT_API_VERSION: Final[int] = ge("TRAKT_API_VERSION") +TRAKT_CLIENT_ID: Final[str] = ge("TRAKT_CLIENT_ID") +VERIFICATION_SERVER: Final[int] = ge("VERIFICATION_SERVER") +VERIFIED_ROLE: Final[int] = ge("VERIFIED_ROLE") + +EMOJI_ATTENTIVE: Final[str] = ge("EMOJI_ATTENTIVE") +EMOJI_DOUBTING: Final[str] = ge("EMOJI_DOUBTING") +EMOJI_FORBIDDEN: Final[str] = ge("EMOJI_FORBIDDEN") +EMOJI_SUCCESS: Final[str] = ge("EMOJI_SUCCESS") +EMOJI_UNEXPECTED_ERROR: Final[str] = ge("EMOJI_UNEXPECTED_ERROR") +EMOJI_USER_ERROR: Final[str] = ge("EMOJI_USER_ERROR") + +LANGUAGE_CODE: Final[str] = ge("LANGUAGE_CODE") def get_git_revision_hash() -> str: @@ -53,16 +53,15 @@ def get_git_revision_hash() -> str: Returns: str: The current git revision hash """ - return chout(['git', 'rev-parse', 'HEAD']).decode('ascii').strip() + return chout(["git", "rev-parse", "HEAD"]).decode("ascii").strip() def get_git_revision_short_hash() -> str: """Get the current git revision short hash Returns: - """ - return chout(['git', 'rev-parse', '--short', 'HEAD'] - ).decode('ascii').strip() + """ + return chout(["git", "rev-parse", "--short", "HEAD"]).decode("ascii").strip() gittyHash = get_git_revision_hash() @@ -71,12 +70,14 @@ def get_git_revision_short_hash() -> str: # ============================================================================= # About Bot -ownerUserUrl = f'https://discord.com/users/{AUTHOR_USERID}' +ownerUserUrl = f"https://discord.com/users/{AUTHOR_USERID}" # ============================================================================= # Declined GDPR notice -DECLINED_GDPR: Final[str] = '''**You have not accepted the GDPR/CCPA/CPRA Privacy Consent!** +DECLINED_GDPR: Final[ + str +] = """**You have not accepted the GDPR/CCPA/CPRA Privacy Consent!** Unfortunately, we cannot register you without your consent. However, you can still use the bot albeit limited. Allowed commands: @@ -92,27 +93,36 @@ def get_git_revision_short_hash() -> str: ***We respect your privacy.*** For more info what do we collect and use, use `/privacy`. -''' +""" # ============================================================================= # Common errors and messages -MESSAGE_MEMBER_REG_PROFILE: Final[str] = f"{EMOJI_DOUBTING} **You are looking at your own profile!**\nYou can also use without any arguments to get your own profile!" +MESSAGE_MEMBER_REG_PROFILE: Final[ + str +] = f"{EMOJI_DOUBTING} **You are looking at your own profile!**\nYou can also use without any arguments to get your own profile!" -MESSAGE_INVITE: Final[str] = "To invite me, simply press \"**Invite me!**\" button below!\nFor any questions, please join my support server!" +MESSAGE_INVITE: Final[ + str +] = 'To invite me, simply press "**Invite me!**" button below!\nFor any questions, please join my support server!' -MESSAGE_SELECT_TIMEOUT: Final[str] = "*Selection menu has reached timeout, please try again if you didn't pick the option!*" +MESSAGE_SELECT_TIMEOUT: Final[ + str +] = "*Selection menu has reached timeout, please try again if you didn't pick the option!*" -MESSAGE_WARN_CONTENTS: Final[str] = """ +MESSAGE_WARN_CONTENTS: Final[ + str +] = """ If you invoked this command outside (public or private) forum thread channel or regular text channel and **Age Restriction** is enabled, please contact developer of this bot as the feature only tested in forum thread and text channel. You can simply access it on `/support`""" ERR_KAIZE_SLUG_MODDED: Final[ - str] = '''We've tried to search for the anime using the slug (and even fix the slug itself), but it seems that the anime is not found on Kaize via AnimeApi. -Please send a message to AnimeApi maintainer, nattadasu (he is also a developer of this bot)''' + str +] = """We've tried to search for the anime using the slug (and even fix the slug itself), but it seems that the anime is not found on Kaize via AnimeApi. +Please send a message to AnimeApi maintainer, nattadasu (he is also a developer of this bot)""" # ============================================================================= # Aliases @@ -120,42 +130,74 @@ def get_git_revision_short_hash() -> str: warnThreadCW = MESSAGE_WARN_CONTENTS bannedTags = [ - 'Amputation', 'Anal Sex', 'Ashikoki', 'Asphyxiation', - 'Blackmail', 'Bondage', 'Boobjob', 'Cumflation', - 'Cunnilingus', 'Deepthroat', 'DILF', 'Fellatio', - 'Femdom', 'Futanari', 'Group Sex', 'Handjob', - 'Human Pet', 'Incest', 'Inseki', 'Irrumatio', - 'Lactation', 'Masochism', 'Masturbation', 'MILF', - 'Nakadashi', 'Pregnant', 'Prostitution', 'Public Sex', - 'Rape', 'Rimjob', 'Sadism', 'Scat', - 'Scissoring', 'Sex Toys', 'Squirting', 'Sumata', - 'Sweat', 'Tentacles', 'Threesome', 'Vore', - 'Voyeur', 'Watersports', 'Omegaverse' + "Amputation", + "Anal Sex", + "Ashikoki", + "Asphyxiation", + "Blackmail", + "Bondage", + "Boobjob", + "Cumflation", + "Cunnilingus", + "Deepthroat", + "DILF", + "Fellatio", + "Femdom", + "Futanari", + "Group Sex", + "Handjob", + "Human Pet", + "Incest", + "Inseki", + "Irrumatio", + "Lactation", + "Masochism", + "Masturbation", + "MILF", + "Nakadashi", + "Pregnant", + "Prostitution", + "Public Sex", + "Rape", + "Rimjob", + "Sadism", + "Scat", + "Scissoring", + "Sex Toys", + "Squirting", + "Sumata", + "Sweat", + "Tentacles", + "Threesome", + "Vore", + "Voyeur", + "Watersports", + "Omegaverse", ] invAa = { - 'title': None, - 'anidb': None, - 'anilist': None, - 'animeplanet': None, - 'anisearch': None, - 'annict': None, - 'kaize': None, - 'kitsu': None, - 'livechart': None, - 'myanimelist': None, - 'notify': None, - 'otakotaku': None, - 'shikimori': None, - 'shoboi': None, - 'silveryasha': None, - 'trakt': None, - 'trakt_type': None, - 'trakt_season': None + "title": None, + "anidb": None, + "anilist": None, + "animeplanet": None, + "anisearch": None, + "annict": None, + "kaize": None, + "kitsu": None, + "livechart": None, + "myanimelist": None, + "notify": None, + "otakotaku": None, + "shikimori": None, + "shoboi": None, + "silveryasha": None, + "trakt": None, + "trakt_type": None, + "trakt_season": None, } simkl0rels = { - 'title': None, + "title": None, "simkl": None, "slug": None, "poster": None, @@ -184,7 +226,7 @@ def get_git_revision_short_hash() -> str: } traktHeader = { - 'Content-Type': 'applications/json', - 'trakt-api-key': TRAKT_CLIENT_ID, - 'trakt-api-version': TRAKT_API_VERSION + "Content-Type": "applications/json", + "trakt-api-key": TRAKT_CLIENT_ID, + "trakt-api-version": TRAKT_API_VERSION, } diff --git a/modules/database.py b/modules/database.py index d5d7cee73..eb1a4f55a 100644 --- a/modules/database.py +++ b/modules/database.py @@ -12,7 +12,7 @@ def checkIfRegistered(discordId: int) -> bool: Returns: bool: True if user is registered, False if not """ - df = pd.read_csv(database, delimiter="\t", dtype={'discordId': str}) - if str(discordId) in df['discordId'].values: + df = pd.read_csv(database, delimiter="\t", dtype={"discordId": str}) + if str(discordId) in df["discordId"].values: return True return False diff --git a/modules/i18n.py b/modules/i18n.py index a7f2dcd8c..30312cd17 100644 --- a/modules/i18n.py +++ b/modules/i18n.py @@ -9,8 +9,7 @@ import pandas as pd from fuzzywuzzy import fuzz -from interactions import (BaseContext, Client, Embed, EmbedField, - InteractionContext) +from interactions import BaseContext, Client, Embed, EmbedField, InteractionContext from interactions.ext.paginators import Paginator from modules.const import LANGUAGE_CODE @@ -31,7 +30,7 @@ def lang(code: str, useRaw: bool = False) -> dict: data = jlo(f.read()) if useRaw: return data - return data['strings'] + return data["strings"] except FileNotFoundError: return lang(LANGUAGE_CODE) @@ -82,25 +81,28 @@ async def paginateLanguage(bot: Client, ctx: InteractionContext) -> None: pages = [] for i in range(0, len(langs), 15): paged = [] - for lang in langs[i:i + 15]: - flag = lang['code'].split('_')[1].lower() + for lang in langs[i : i + 15]: + flag = lang["code"].split("_")[1].lower() match flag: case "sp": flag = "rs" case _: flag = flag - paged += [EmbedField( - name=f":flag_{flag}: `{lang['code']}` - {lang['name']}", - value=f"{lang['native']}", - inline=True - )] + paged += [ + EmbedField( + name=f":flag_{flag}: `{lang['code']}` - {lang['name']}", + value=f"{lang['native']}", + inline=True, + ) + ] pages += [ Embed( title="Languages", description="List of all available languages.\nUse `/usersettings language set code:` by replacing `` with the language code (for example `en_US`) to change your language.\n\nIf you want to contribute, visit [Crowdin page](https://crowdin.com/project/ryuuRyuusei).", color=0x996422, fields=paged, - )] + ) + ] pagin = Paginator.create_from_embeds(bot, *pages, timeout=60) await pagin.send(ctx) @@ -114,13 +116,13 @@ def searchLanguage(query: str) -> list[dict]: Returns: list[dict]: The list of languages that match the query """ - with open('i18n/_index.json') as f: + with open("i18n/_index.json") as f: data = load(f) results = [] for item in data: - name_ratio = fuzz.token_set_ratio(query, item['name']) - native_ratio = fuzz.token_set_ratio(query, item['native']) - dialect_ratio = fuzz.token_set_ratio(query, item['dialect']) + name_ratio = fuzz.token_set_ratio(query, item["name"]) + native_ratio = fuzz.token_set_ratio(query, item["native"]) + dialect_ratio = fuzz.token_set_ratio(query, item["dialect"]) max_ratio = max(name_ratio, native_ratio, dialect_ratio) if max_ratio >= 70: # minimum similarity threshold of 70% results.append(item) @@ -144,7 +146,9 @@ def checkLangExist(code: str) -> bool: return False -async def setLanguage(code: str, ctx: InteractionContext, isGuild: bool = False) -> None: +async def setLanguage( + code: str, ctx: InteractionContext, isGuild: bool = False +) -> None: """Set the user's/guild's language preference Args: @@ -154,14 +158,14 @@ async def setLanguage(code: str, ctx: InteractionContext, isGuild: bool = False) """ if isGuild is True: if checkLangExist(code) is False: - raise Exception( - "Language not found, recheck the spelling and try again") + raise Exception("Language not found, recheck the spelling and try again") # check if guild is already in database try: df = pd.read_csv("database/server.csv", sep="\t") if df.query(f"serverId == {ctx.guild.id}").empty: - df = pd.DataFrame([[ctx.guild.id, code]], columns=[ - "serverId", "language"]) + df = pd.DataFrame( + [[ctx.guild.id, code]], columns=["serverId", "language"] + ) df = df.append(df, ignore_index=True) df.to_csv("database/server.csv", sep="\t", index=False) else: @@ -176,22 +180,25 @@ async def setLanguage(code: str, ctx: InteractionContext, isGuild: bool = False) writer.writerow([ctx.guild.id, code]) else: if checkLangExist(code) is False: - raise Exception( - "Language not found, recheck the spelling and try again") + raise Exception("Language not found, recheck the spelling and try again") try: - df = pd.read_csv("database/member.csv", sep="\t", - encoding="utf-8", dtype={"discordId": str}) + df = pd.read_csv( + "database/member.csv", + sep="\t", + encoding="utf-8", + dtype={"discordId": str}, + ) if df.query(f"discordId == '{str(ctx.author.id)}'").empty: - df = pd.DataFrame([[str(ctx.author.id), code]], columns=[ - "discordId", "language"]) + df = pd.DataFrame( + [[str(ctx.author.id), code]], columns=["discordId", "language"] + ) df = df.append(df, ignore_index=True) df.to_csv("database/member.csv", sep="\t", index=False) else: # if it is, update it - df.loc[df["discordId"] == str( - ctx.author.id), "language"] = f"{code}" + df.loc[df["discordId"] == str(ctx.author.id), "language"] = f"{code}" df.to_csv("database/member.csv", sep="\t", index=False) except BaseException: # if the database doesn't exist, create it diff --git a/modules/jikan.py b/modules/jikan.py index ef5c37ee8..d2cd8b3b2 100644 --- a/modules/jikan.py +++ b/modules/jikan.py @@ -33,7 +33,7 @@ async def check_if_club_in_list(clubs: list[dict]) -> bool: bool: True if the club is in the list, False if not """ for club in clubs: - if str(club['mal_id']) == str(CLUB_ID): + if str(club["mal_id"]) == str(CLUB_ID): return True return False diff --git a/modules/myanimelist.py b/modules/myanimelist.py index c51bd713f..be4c5d013 100644 --- a/modules/myanimelist.py +++ b/modules/myanimelist.py @@ -10,9 +10,14 @@ from zoneinfo import ZoneInfo import pandas as pd -from interactions import ( Embed, - EmbedAttachment, EmbedAuthor, EmbedField, - EmbedFooter, SlashContext) +from interactions import ( + Embed, + EmbedAttachment, + EmbedAuthor, + EmbedField, + EmbedFooter, + SlashContext, +) from classes.anilist import AniList from classes.animeapi import AnimeApi @@ -20,14 +25,24 @@ from classes.kitsu import Kitsu from classes.myanimelist import MyAnimeList from classes.simkl import Simkl -from modules.commons import (generateTrailer, getParentNsfwStatus, getRandom, - sanitizeMarkdown, trimCyno) -from modules.const import (EMOJI_FORBIDDEN, EMOJI_UNEXPECTED_ERROR, - EMOJI_USER_ERROR, MYANIMELIST_CLIENT_ID, - SIMKL_CLIENT_ID, simkl0rels, warnThreadCW) +from modules.commons import ( + generateTrailer, + getParentNsfwStatus, + getRandom, + sanitizeMarkdown, + trimCyno, +) +from modules.const import ( + EMOJI_FORBIDDEN, + EMOJI_UNEXPECTED_ERROR, + EMOJI_USER_ERROR, + MYANIMELIST_CLIENT_ID, + SIMKL_CLIENT_ID, + simkl0rels, + warnThreadCW, +) from modules.i18n import readUserLang - def lookupRandomAnime() -> int: """Lookup random anime from MAL @@ -43,7 +58,7 @@ def lookupRandomAnime() -> int: # get random anime randomAnime = df.sample(n=1, random_state=seed) # get anime id - randomAnimeId: int = randomAnime['mal_id'].values[0] + randomAnimeId: int = randomAnime["mal_id"].values[0] return randomAnimeId @@ -66,29 +81,27 @@ async def searchMalAnime(title: str) -> dict | list: fields = ",".join(fields) async with MyAnimeList(MYANIMELIST_CLIENT_ID) as mal: data = await mal.search(title, limit=5, fields=fields) - for d in data['data']: + for d in data["data"]: # drop main_picture - d['node'].pop("main_picture", None) + d["node"].pop("main_picture", None) # only keep english and japanese alternative titles - d['node']['alternative_titles'] = { - k: v for k, - v in d['node']['alternative_titles'].items() if k in [ - "en", - "ja"]} + d["node"]["alternative_titles"] = { + k: v + for k, v in d["node"]["alternative_titles"].items() + if k in ["en", "ja"] + } # if d['node']['start_season'] is not in the dict, then force add as # None: - if 'start_season' not in d['node']: - d['node']['start_season'] = { - "year": None, - "season": None - } - if 'media_type' not in d['node']: - d['node']['media_type'] = "unknown" - return data['data'] + if "start_season" not in d["node"]: + d["node"]["start_season"] = {"year": None, "season": None} + if "media_type" not in d["node"]: + d["node"]["media_type"] = "unknown" + return data["data"] class MalErrType(Enum): """MyAnimeList Error Type""" + USER = EMOJI_USER_ERROR NSFW = EMOJI_FORBIDDEN SYSTEM = EMOJI_UNEXPECTED_ERROR @@ -119,15 +132,9 @@ def malExceptionEmbed( emoji = re.sub(r"(<:.*:)(\d+)(>)", r"\2", error_type) dcEm = Embed( color=color, - title=l_['commons']['error'], + title=l_["commons"]["error"], description=description, - fields=[ - EmbedField( - name=l_['commons']['reason'], - value=error, - inline=False - ) - ], + fields=[EmbedField(name=l_["commons"]["reason"], value=error, inline=False)], thumbnail=EmbedAttachment( url=f"https://cdn.discordapp.com/emojis/{emoji}.png?v=1" ), @@ -138,12 +145,19 @@ def malExceptionEmbed( class MediaIsNsfw(Exception): """Media is NSFW exception""" + pass # old code taken from ipy/v4.3.4 #! TODO: respect ipy/v5.0.0 and introduce locale -async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: dict = None, animeApi: dict = None) -> Embed: +async def generateMal( + entry_id: int, + code: str, + isNsfw: bool = False, + alDict: dict = None, + animeApi: dict = None, +) -> Embed: """Generate an embed for /anime with MAL via Jikan Args: @@ -170,26 +184,29 @@ async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: di if isNsfw is None: msgForThread = warnThreadCW else: - msgForThread = '' + msgForThread = "" if isNsfw is not True: - for g in j['genres']: - gn = g['name'] + for g in j["genres"]: + gn = g["name"] if "Hentai" in gn: raise MediaIsNsfw( - f'{EMOJI_FORBIDDEN} **NSFW is not allowed!**\nOnly NSFW channels are allowed to search NSFW content.{msgForThread}') + f"{EMOJI_FORBIDDEN} **NSFW is not allowed!**\nOnly NSFW channels are allowed to search NSFW content.{msgForThread}" + ) - m = j['mal_id'] + m = j["mal_id"] - if j['synopsis'] is not None: + if j["synopsis"] is not None: # remove \n\n[Written by MAL Rewrite] - jdata = j['synopsis'].replace("\n\n[Written by MAL Rewrite]", "") + jdata = j["synopsis"].replace("\n\n[Written by MAL Rewrite]", "") # try to decode ampersands jdata = html.unescape(jdata) j_spl = jdata.split("\n") synl = len(j_spl) cynoin = j_spl[0] - cynmo = f"\n> \n> Read more on [MyAnimeList]()" + cynmo = ( + f"\n> \n> Read more on [MyAnimeList]()" + ) if len(str(cynoin)) <= 150: cyno = sanitizeMarkdown(cynoin) @@ -203,65 +220,95 @@ async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: di else: cyno = sanitizeMarkdown(cynoin) - if (cyno[-3:] == "...") or ((len(str(cynoin)) >= 150) - and (synl > 3)) or ((len(str(cynoin)) >= 1000) and (synsl > 1)): + if ( + (cyno[-3:] == "...") + or ((len(str(cynoin)) >= 150) and (synl > 3)) + or ((len(str(cynoin)) >= 1000) and (synsl > 1)) + ): cyno += cynmo else: cyno = "*None*" - jJpg = j['images']['jpg'] + jJpg = j["images"]["jpg"] note = "Images from " if al: pass else: - al = { - 'bannerImage': None, - 'coverImage': { - 'extraLarge': None - } - } + al = {"bannerImage": None, "coverImage": {"extraLarge": None}} - alPost = al['coverImage']['extraLarge'] - alBg = al['bannerImage'] + alPost = al["coverImage"]["extraLarge"] + alBg = al["bannerImage"] try: async with Simkl(SIMKL_CLIENT_ID) as sim: smId = await sim.search_by_id(sim.Provider.MYANIMELIST, m) - smk = await sim.get_anime(smId[0]['ids']['simkl']) + smk = await sim.get_anime(smId[0]["ids"]["simkl"]) except Exception: smk = simkl0rels - smkPost = smk.get('poster', None) - smkBg = smk.get('fanart', None) - smkPost = f"https://simkl.in/posters/{smkPost}_m.webp" if smkPost is not None else None + smkPost = smk.get("poster", None) + smkBg = smk.get("fanart", None) + smkPost = ( + f"https://simkl.in/posters/{smkPost}_m.webp" if smkPost is not None else None + ) smkBg = f"https://simkl.in/fanart/{smkBg}_w.webp" if smkBg is not None else None - if (animeApi['kitsu'] is not None) and (((alPost is None) and ( - alBg is None)) or ((smkPost is None) and (smkBg is None))): - kts = await Kitsu().get_anime(animeApi['kitsu']) + if (animeApi["kitsu"] is not None) and ( + ((alPost is None) and (alBg is None)) or ((smkPost is None) and (smkBg is None)) + ): + kts = await Kitsu().get_anime(animeApi["kitsu"]) else: - kts = { - "data": { - "attributes": { - "posterImage": None, - "coverImage": None - } - } - } + kts = {"data": {"attributes": {"posterImage": None, "coverImage": None}}} - ktsPost = kts['data']['attributes'].get('posterImage', None) - ktsPost = ktsPost.get('original', None) if ktsPost is not None else None - ktsBg = kts['data']['attributes'].get('coverImage', None) - ktsBg = ktsBg.get('original', None) if ktsBg is not None else None + ktsPost = kts["data"]["attributes"].get("posterImage", None) + ktsPost = ktsPost.get("original", None) if ktsPost is not None else None + ktsBg = kts["data"]["attributes"].get("coverImage", None) + ktsBg = ktsBg.get("original", None) if ktsBg is not None else None - malPost = jJpg['large_image_url'] if jJpg['large_image_url'] is not None else j['image_url'] + malPost = ( + jJpg["large_image_url"] + if jJpg["large_image_url"] is not None + else j["image_url"] + ) malBg = "" - poster = alPost if alPost is not None else smkPost if smkPost is not None else ktsPost if ktsPost is not None else malPost - postNote = "AniList" if alPost is not None else "SIMKL" if smkPost is not None else "Kitsu" if ktsPost is not None else "MyAnimeList" - background = alBg if alBg is not None else smkBg if smkBg is not None else ktsBg if ktsBg is not None else malBg - bgNote = "AniList" if alBg is not None else "SIMKL" if smkBg is not None else "Kitsu" if ktsBg is not None else "MyAnimeList" + poster = ( + alPost + if alPost is not None + else smkPost + if smkPost is not None + else ktsPost + if ktsPost is not None + else malPost + ) + postNote = ( + "AniList" + if alPost is not None + else "SIMKL" + if smkPost is not None + else "Kitsu" + if ktsPost is not None + else "MyAnimeList" + ) + background = ( + alBg + if alBg is not None + else smkBg + if smkBg is not None + else ktsBg + if ktsBg is not None + else malBg + ) + bgNote = ( + "AniList" + if alBg is not None + else "SIMKL" + if smkBg is not None + else "Kitsu" + if ktsBg is not None + else "MyAnimeList" + ) if postNote == bgNote: note += f"{postNote} for poster and background." @@ -291,20 +338,20 @@ async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: di tgs = ", ".join(tgs) else: tgs = "*None*" - year = j['aired']['prop']['from']['year'] + year = j["aired"]["prop"]["from"]["year"] if (year == 0) or (year is None): - year = 'year?' - astn = j['aired']['from'] - aenn = j['aired']['to'] - astr = j['aired']['string'] - ssonn = j['season'] + year = "year?" + astn = j["aired"]["from"] + aenn = j["aired"]["to"] + astr = j["aired"]["string"] + ssonn = j["season"] daten = datetime(1970, 1, 1, tzinfo=timezone.utc) - bcast = j['broadcast'] + bcast = j["broadcast"] # Grab studio names on j['studios'][n]['name'] stdio = [] - for s in j['studios']: - stdio += [s['name']] + for s in j["studios"]: + stdio += [s["name"]] if len(stdio) > 0: stdio = ", ".join(stdio) else: @@ -313,31 +360,36 @@ async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: di # start date logic if astn is not None: # Check if title is airing/aired or TBA by checking astr in regex - if re.match( - r'^([\d]{4})', - astr) or re.match( - r'^([a-zA-Z]{3} [\d]{4})', - astr): + if re.match(r"^([\d]{4})", astr) or re.match(r"^([a-zA-Z]{3} [\d]{4})", astr): ast = astr.split(" to ")[0] tsa = "" - elif re.match(r'^([a-zA-Z]{3} [\d]{1,2}, [\d]{4})', astr): - if (bcast['string'] == "Unknown") or ( - bcast['string'] is None) or (bcast['time'] is None): - astn = astn.replace('+00:00', '+0000') - ast = (datetime.strptime( - astn, '%Y-%m-%dT%H:%M:%S%z') - daten).total_seconds() + elif re.match(r"^([a-zA-Z]{3} [\d]{1,2}, [\d]{4})", astr): + if ( + (bcast["string"] == "Unknown") + or (bcast["string"] is None) + or (bcast["time"] is None) + ): + astn = astn.replace("+00:00", "+0000") + ast = ( + datetime.strptime(astn, "%Y-%m-%dT%H:%M:%S%z") - daten + ).total_seconds() else: # Split bcast.time into hours and minutes - bct = bcast['time'].split(":") - prop = j['aired']['prop']['from'] + bct = bcast["time"].split(":") + prop = j["aired"]["prop"]["from"] # Convert bct to datetime ast = ( datetime( - prop['year'], prop['month'], prop['day'], int( - bct[0]), int( - bct[1]), tzinfo=ZoneInfo( - bcast['timezone'])) - daten).total_seconds() - ast = str(ast).removesuffix('.0') + prop["year"], + prop["month"], + prop["day"], + int(bct[0]), + int(bct[1]), + tzinfo=ZoneInfo(bcast["timezone"]), + ) + - daten + ).total_seconds() + ast = str(ast).removesuffix(".0") tsa = "()" ast = "" else: @@ -346,30 +398,39 @@ async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: di # end date logic if aenn is not None: - if re.match(r'^([a-zA-Z]{3} [\d]{1,2}, [\d]{4})', astr): - if (bcast['string'] == "Unknown") or ( - bcast['string'] is None) or (bcast['time'] is None): - aenn = aenn.replace('+00:00', '+0000') - aen = (datetime.strptime( - aenn, '%Y-%m-%dT%H:%M:%S%z') - daten).total_seconds() + if re.match(r"^([a-zA-Z]{3} [\d]{1,2}, [\d]{4})", astr): + if ( + (bcast["string"] == "Unknown") + or (bcast["string"] is None) + or (bcast["time"] is None) + ): + aenn = aenn.replace("+00:00", "+0000") + aen = ( + datetime.strptime(aenn, "%Y-%m-%dT%H:%M:%S%z") - daten + ).total_seconds() else: # Split bcast.time into hours and minutes - bct = bcast['time'].split(":") - prop = j['aired']['prop']['to'] + bct = bcast["time"].split(":") + prop = j["aired"]["prop"]["to"] # Convert bct to datetime aen = ( datetime( - prop['year'], prop['month'], prop['day'], int( - bct[0]), int( - bct[1]), tzinfo=ZoneInfo( - bcast['timezone'])) - daten).total_seconds() - aen = str(aen).removesuffix('.0') + prop["year"], + prop["month"], + prop["day"], + int(bct[0]), + int(bct[1]), + tzinfo=ZoneInfo(bcast["timezone"]), + ) + - daten + ).total_seconds() + aen = str(aen).removesuffix(".0") aen = "" - elif re.match(r'^([a-zA-Z]{3} [\d]{4})', astr): + elif re.match(r"^([a-zA-Z]{3} [\d]{4})", astr): aen = astr.split(" to ")[1] - elif j['status'] == 'Currently Airing': + elif j["status"] == "Currently Airing": aen = "Ongoing" - elif (j['status'] == 'Not yet aired') or (astr == 'Not available'): + elif (j["status"] == "Not yet aired") or (astr == "Not available"): aen = "TBA" else: aen = ast @@ -389,35 +450,34 @@ async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: di sson = str(ssonn).capitalize() elif re.match("^[0-9]{4}$", ast): sson = "Unknown" - elif j['aired']['prop']['from']['month'] is not None: + elif j["aired"]["prop"]["from"]["month"] is not None: astn = astn.replace("+00:00", "+0000") - astn = datetime.strptime( - astn, '%Y-%m-%dT%H:%M:%S%z') - sson = astn.strftime('%m') - if sson in ['01', '02', '03']: - sson = 'Winter' - elif sson in ['04', '05', '06']: - sson = 'Spring' - elif sson in ['07', '08', '09']: - sson = 'Summer' - elif sson in ['10', '11', '12']: - sson = 'Fall' + astn = datetime.strptime(astn, "%Y-%m-%dT%H:%M:%S%z") + sson = astn.strftime("%m") + if sson in ["01", "02", "03"]: + sson = "Winter" + elif sson in ["04", "05", "06"]: + sson = "Spring" + elif sson in ["07", "08", "09"]: + sson = "Summer" + elif sson in ["10", "11", "12"]: + sson = "Fall" else: sson = "Unknown" - rot = j['title'] + rot = j["title"] - nat = j['title_japanese'] - if (nat == '') or (nat is None): + nat = j["title_japanese"] + if (nat == "") or (nat is None): nat = "*None*" - ent = j['title_english'] + ent = j["title_english"] # create a synonyms list syns = [] - for s in j['titles']: - if s['type'] not in ['Default', 'English']: - syns += [s['title']] - if (ent is None) or (ent == ''): + for s in j["titles"]: + if s["type"] not in ["Default", "English"]: + syns += [s["title"]] + if (ent is None) or (ent == ""): # for each s in syns, check if the s is in ASCII using regex # if it is, then set ent to s # if not, then set ent to rot @@ -430,10 +490,10 @@ async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: di ent = rot else: ent = rot - enChkMark = '\\*' + enChkMark = "\\*" chkMsg = "\n* Data might be inaccurate due to bot rules/config, please check source for more information." else: - enChkMark = '' + enChkMark = "" chkMsg = "" note += chkMsg @@ -456,7 +516,7 @@ async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: di syns = "*None*" # format people voted for to be more readable (1,000 instead of 1000) - pvd = j['scored_by'] + pvd = j["scored_by"] if pvd is None: pvd = "0 person voted" elif pvd > 1: @@ -464,27 +524,28 @@ async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: di elif pvd == 1: pvd = "1 person voted" - eps = j['episodes'] + eps = j["episodes"] if (eps == 0) or (eps is None): - eps = '*??*' + eps = "*??*" - stat = j['status'] - if (stat == '') or (stat is None): + stat = j["status"] + if (stat == "") or (stat is None): stat = "*Unknown*" - dur = j['duration'] - if (dur == '') or (dur is None): + dur = j["duration"] + if (dur == "") or (dur is None): dur = "*Unknown*" - scr = j['score'] - if (scr == '') or (scr is None): + scr = j["score"] + if (scr == "") or (scr is None): scr = "0" embed = Embed( author=EmbedAuthor( name="MyAnimeList Anime", url="https://myanimelist.net", - icon_url="https://cdn.myanimelist.net/img/sp/icon/apple-touch-icon-256.png"), + icon_url="https://cdn.myanimelist.net/img/sp/icon/apple-touch-icon-256.png", + ), title=rot, url=f"https://myanimelist.net/anime/{m}", description=f"""*`{m}`, {j['type']}, {sson} {year}, ⭐ {scr}/10 by {pvd}* @@ -492,43 +553,20 @@ async def generateMal(entry_id: int, code: str, isNsfw: bool = False, alDict: di > {cyno} """, color=0x2E51A2, - thumbnail=EmbedAttachment( - url=poster), + thumbnail=EmbedAttachment(url=poster), fields=[ - EmbedField( - name=f"English Title{enChkMark}", - value=ent, - inline=True), - EmbedField( - name="Native Title", - value=nat, - inline=True), - EmbedField( - name="Synonyms", - value=syns), - EmbedField( - name="Genres and Themes", - value=tgs), - EmbedField( - name="Eps/Duration", - value=f"{eps} ({dur})", - inline=True), - EmbedField( - name="Status", - value=stat, - inline=True), - EmbedField( - name="Studio", - value=stdio, - inline=True), - EmbedField( - name="Aired", - value=date)], - images=[ - EmbedAttachment( - url=background)], - footer=EmbedFooter( - text=note)) + EmbedField(name=f"English Title{enChkMark}", value=ent, inline=True), + EmbedField(name="Native Title", value=nat, inline=True), + EmbedField(name="Synonyms", value=syns), + EmbedField(name="Genres and Themes", value=tgs), + EmbedField(name="Eps/Duration", value=f"{eps} ({dur})", inline=True), + EmbedField(name="Status", value=stat, inline=True), + EmbedField(name="Studio", value=stdio, inline=True), + EmbedField(name="Aired", value=date), + ], + images=[EmbedAttachment(url=background)], + footer=EmbedFooter(text=note), + ) return embed @@ -554,24 +592,27 @@ async def malSubmit(ctx: SlashContext, ani_id: int) -> None: trailer = None try: async with AnimeApi() as aniapi: - aniApi = await aniapi.get_relation(id=ani_id, platform=aniapi.AnimeApiPlatforms.MYANIMELIST) - if aniApi['anilist'] is not None: + aniApi = await aniapi.get_relation( + id=ani_id, platform=aniapi.AnimeApiPlatforms.MYANIMELIST + ) + if aniApi["anilist"] is not None: async with AniList() as al: - alData = await al.anime(media_id=aniApi['anilist']) - if (alData['trailer'] is not None) and ( - alData['trailer']['site'] == "youtube"): - trailer = generateTrailer( - data=alData['trailer'], isMal=False) + alData = await al.anime(media_id=aniApi["anilist"]) + if (alData["trailer"] is not None) and ( + alData["trailer"]["site"] == "youtube" + ): + trailer = generateTrailer(data=alData["trailer"], isMal=False) trailer = [trailer] else: trailer = [] - dcEm = await generateMal(ani_id, isNsfw=nsfwBool, code=ul, alDict=alData, animeApi=aniApi) + dcEm = await generateMal( + ani_id, isNsfw=nsfwBool, code=ul, alDict=alData, animeApi=aniApi + ) await ctx.send("", embeds=dcEm, components=trailer) except MediaIsNsfw as e: - await ctx.send( - f"**{e}**\n") + await ctx.send(f"**{e}**\n") except Exception: #! TODO: Implement the embed raise NotImplementedError( - "This error embed is not yet implemented.\n" + - traceback.format_exc()) + "This error embed is not yet implemented.\n" + traceback.format_exc() + ) diff --git a/modules/nekomimidb.py b/modules/nekomimidb.py index 2379e0a4d..731710ead 100644 --- a/modules/nekomimidb.py +++ b/modules/nekomimidb.py @@ -19,14 +19,14 @@ def generateNekomimi(row: dict, lang: dict) -> Embed: Returns: Embed: The generated nekomimi embed. """ - img = row['imageUrl'].values[0] - mediaSource = row['mediaSource'].values[0] - if mediaSource == '': - mediaSource = 'Original Character' - artist = row['artist'].values[0] - artistUrl = row['artistUrl'].values[0] - imageSourceUrl = row['imageSourceUrl'].values[0] - col = getPlatformColor(row['platform'].values[0]) + img = row["imageUrl"].values[0] + mediaSource = row["mediaSource"].values[0] + if mediaSource == "": + mediaSource = "Original Character" + artist = row["artist"].values[0] + artistUrl = row["artistUrl"].values[0] + imageSourceUrl = row["imageSourceUrl"].values[0] + col = getPlatformColor(row["platform"].values[0]) # Send the image url to the user dcEm = Embed( title=f"{mediaSource}", @@ -37,20 +37,18 @@ def generateNekomimi(row: dict, lang: dict) -> Embed: ], color=col, author=EmbedAuthor( - name=lang['author'], + name=lang["author"], url="https://github.com/nattadasu/nekomimiDb", icon_url="https://cdn.discordapp.com/avatars/1080049635621609513/6f79d106de439f917179b7ef052a6ca8.png", ), fields=[ EmbedField( - name=lang['source']['title'], + name=lang["source"]["title"], value=f"[{lang['source']['value']}]({imageSourceUrl})", - inline=True + inline=True, ), EmbedField( - name=lang['artist'], - value=f"[{artist}]({artistUrl})", - inline=True + name=lang["artist"], value=f"[{artist}]({artistUrl})", inline=True ), ], ) diff --git a/modules/oobe/commons.py b/modules/oobe/commons.py index 74d0372c5..720a496d8 100644 --- a/modules/oobe/commons.py +++ b/modules/oobe/commons.py @@ -51,15 +51,13 @@ def ask_python(shell_output: str) -> bool: if current_os() == "Windows": try: - py = subprocess.check_output("python --version", - shell=True).decode("utf-8") + py = subprocess.check_output("python --version", shell=True).decode("utf-8") if ask_python(py): return "python" raise subprocess.CalledProcessError() except subprocess.CalledProcessError: - paths = subprocess.check_output( - "where python", shell=True).decode("utf-8") - paths = paths.replace('\r', '').split("\n") + paths = subprocess.check_output("where python", shell=True).decode("utf-8") + paths = paths.replace("\r", "").split("\n") # reverse the list, so we can get the up to date python version paths.reverse() # if index 0 is '', drop it @@ -74,14 +72,14 @@ def ask_python(shell_output: str) -> bool: return "python" else: try: - py3 = subprocess.check_output("python3 --version", - shell=True).decode("utf-8") + py3 = subprocess.check_output("python3 --version", shell=True).decode( + "utf-8" + ) if ask_python(py3): return "python3" raise subprocess.CalledProcessError() except subprocess.CalledProcessError: - py = subprocess.check_output("python --version", - shell=True).decode("utf-8") + py = subprocess.check_output("python --version", shell=True).decode("utf-8") if ask_python(py): return "python" raise Exception("Python version is too old") @@ -99,7 +97,8 @@ def checkTermux() -> bool: bool: True if running on Termux, False otherwise. """ return current_os() == "Linux" and os.path.exists( - "/data/data/com.termux/files/usr/bin") + "/data/data/com.termux/files/usr/bin" + ) def prepare_database(): @@ -112,12 +111,14 @@ def prepare_database(): Returns: None """ - files = [{"path": "database/database.csv", - "header": "discordId\tdiscordUsername\tdiscordJoined\tmalUsername\tmalId\tmalJoined\tregisteredAt\tregisteredGuild\tregisteredBy"}, - {"path": "database/member.csv", - "header": "discordId\tlanguage"}, - {"path": "database/server.csv", - "header": "guildId\tlanguage"}] + files = [ + { + "path": "database/database.csv", + "header": "discordId\tdiscordUsername\tdiscordJoined\tmalUsername\tmalId\tmalJoined\tregisteredAt\tregisteredGuild\tregisteredBy", + }, + {"path": "database/member.csv", "header": "discordId\tlanguage"}, + {"path": "database/server.csv", "header": "guildId\tlanguage"}, + ] for file in files: if not os.path.exists(file["path"]): diff --git a/modules/oobe/i18nBuild.py b/modules/oobe/i18nBuild.py index f2ae9a14f..1e85bde4f 100644 --- a/modules/oobe/i18nBuild.py +++ b/modules/oobe/i18nBuild.py @@ -24,54 +24,54 @@ def convert_langs_to_json() -> None: indexed: List[dict] = [] files = [f for f in i18n_dir.glob("*.yaml") if f.name != "_index.yaml"] for file in files: - with file.open("r", encoding='utf-8') as f: + with file.open("r", encoding="utf-8") as f: data = y.safe_load(f) print(f"Converting {file.name} to JSON", end="") code = file.stem match code: case "lol_US": drip = { - 'code': code, - 'name': 'English Lolcat', - 'native': 'LOLCAT', - 'dialect': 'United States', + "code": code, + "name": "English Lolcat", + "native": "LOLCAT", + "dialect": "United States", } case "ace_ID": drip = { - 'code': code, - 'name': 'Achinese', - 'native': 'Acèh', - 'dialect': 'Indonesia', + "code": code, + "name": "Achinese", + "native": "Acèh", + "dialect": "Indonesia", } case "sr_SP": drip = { - 'code': code, - 'name': 'Serbian Cyrillic', - 'native': 'Српски', - 'dialect': 'Serbia', + "code": code, + "name": "Serbian Cyrillic", + "native": "Српски", + "dialect": "Serbia", } case _: drip = { - 'code': code, - 'name': lang.get(code).language_name(), - 'native': lang.get(code).language_name( - lang.get(code).language), - 'dialect': lang.get(code).territory_name(), + "code": code, + "name": lang.get(code).language_name(), + "native": lang.get(code).language_name(lang.get(code).language), + "dialect": lang.get(code).territory_name(), } - data['meta'] = drip + data["meta"] = drip indexed.append(drip) print( f"\rConverted {file.name}: {indexed[-1]['name']} ({indexed[-1]['dialect']}) to JSON", - end="\n") - with file.with_suffix(".json").open("w", encoding='utf-8', newline='\n') as f: + end="\n", + ) + with file.with_suffix(".json").open("w", encoding="utf-8", newline="\n") as f: j.dump(data, f, indent=2, ensure_ascii=False) # Create index of languages print("Indexing languages") - indexed.sort(key=lambda x: x['code'].lower()) - with (i18n_dir / "_index.json").open("w", encoding='utf-8', newline='\n') as f: + indexed.sort(key=lambda x: x["code"].lower()) + with (i18n_dir / "_index.json").open("w", encoding="utf-8", newline="\n") as f: j.dump(indexed, f, indent=2, ensure_ascii=False) - with (i18n_dir / "_index.yaml").open("w", encoding='utf-8', newline='\n') as f: + with (i18n_dir / "_index.yaml").open("w", encoding="utf-8", newline="\n") as f: y.dump(indexed, f, indent=2, allow_unicode=True) diff --git a/modules/oobe/jikan.py b/modules/oobe/jikan.py index 3b2e31ee8..1a98e2a2a 100644 --- a/modules/oobe/jikan.py +++ b/modules/oobe/jikan.py @@ -58,9 +58,15 @@ def update_jikanpy(pf: str = pf): with open("requirements.txt", "w") as f: f.write(reqs) if os.name == "nt": - subprocess.run([pf, "-m", "pip", "install", "-r", "requirements.txt"], shell=False) + subprocess.run( + [pf, "-m", "pip", "install", "-r", "requirements.txt"], shell=False + ) else: - os.system(shlex.join([shlex.quote(pf), "-m", "pip", "install", "-r", "requirements.txt"])) + os.system( + shlex.join( + [shlex.quote(pf), "-m", "pip", "install", "-r", "requirements.txt"] + ) + ) # os.system(f"{pf} setup.py install") os.system(shlex.join([shlex.quote(pf), "setup.py", "install"])) revert_reqs() diff --git a/modules/oobe/malIndexer.py b/modules/oobe/malIndexer.py index 0b20c03f4..d17df6663 100644 --- a/modules/oobe/malIndexer.py +++ b/modules/oobe/malIndexer.py @@ -8,7 +8,7 @@ import pandas as pd import requests as r -MAIN_SITE = 'https://aniapi.nattadasu.my.id/myanimelist%28%29.json' +MAIN_SITE = "https://aniapi.nattadasu.my.id/myanimelist%28%29.json" def mal_get_data() -> None: diff --git a/modules/platforms.py b/modules/platforms.py index aa4837e5c..d9174f0a9 100644 --- a/modules/platforms.py +++ b/modules/platforms.py @@ -46,6 +46,7 @@ class Platform(Enum): TRAKT (str): Trakt.tv. TVTIME (str): TV Time. """ + # SNS ARTSTATION = "artstation" DEVIANTART = DA = "deviantart" @@ -99,49 +100,52 @@ def getPlatformColor(pf: str | Platform) -> hex: """ if isinstance(pf, str): pf = Platform(pf) - pfDict = defaultdict(lambda: 0x000000, { - # SNS - "artstation": 0x0F0F0F, - "deviantart": 0x05CC47, - "discord": 0x7289DA, - "facebook": 0x3B5998, - "flickr": 0xFF0084, - "hoyolab": 0x1B75BB, - "instagram": 0x833AB4, - "lofter": 0x335F60, - "patreon": 0xF96854, - "pixiv": 0x0096FA, - "reddit": 0xFF4500, - "seiga": 0xEDA715, - "tumblr": 0x35465C, - "twitter": 0x15202B, - "weibo": 0xE6162D, - # Media tracking - "allcin": 0xEC0A0A, - "anidb": 0x2A2F46, - "anilist": 0x2F80ED, - "animeplanet": 0xE75448, - "anisearch": 0xFDA37C, - "ann": 0x2D50A7, - "annict": 0xF65B73, - "imdb": 0xF5C518, - "kaize": 0x692FC2, - "kitsu": 0xF85235, - "lastfm": 0xD51007, - "livechart": 0x67A427, - "myanimelist": 0x2F51A3, - "notify": 0xDEA99E, - "otakotaku": 0xBE2222, - "shikimori": 0x2E2E2E, - "shoboi": 0xE3F0FD, - "silveryasha": 0x0172BB, - "simkl": 0x0B0F10, - "syoboi": 0xE3F0FD, - "tmdb": 0x09B4E2, - "trakt": 0xED1C24, - "tvdb": 0x6CD491, - "tvtime": 0xFBD737, - }) + pfDict = defaultdict( + lambda: 0x000000, + { + # SNS + "artstation": 0x0F0F0F, + "deviantart": 0x05CC47, + "discord": 0x7289DA, + "facebook": 0x3B5998, + "flickr": 0xFF0084, + "hoyolab": 0x1B75BB, + "instagram": 0x833AB4, + "lofter": 0x335F60, + "patreon": 0xF96854, + "pixiv": 0x0096FA, + "reddit": 0xFF4500, + "seiga": 0xEDA715, + "tumblr": 0x35465C, + "twitter": 0x15202B, + "weibo": 0xE6162D, + # Media tracking + "allcin": 0xEC0A0A, + "anidb": 0x2A2F46, + "anilist": 0x2F80ED, + "animeplanet": 0xE75448, + "anisearch": 0xFDA37C, + "ann": 0x2D50A7, + "annict": 0xF65B73, + "imdb": 0xF5C518, + "kaize": 0x692FC2, + "kitsu": 0xF85235, + "lastfm": 0xD51007, + "livechart": 0x67A427, + "myanimelist": 0x2F51A3, + "notify": 0xDEA99E, + "otakotaku": 0xBE2222, + "shikimori": 0x2E2E2E, + "shoboi": 0xE3F0FD, + "silveryasha": 0x0172BB, + "simkl": 0x0B0F10, + "syoboi": 0xE3F0FD, + "tmdb": 0x09B4E2, + "trakt": 0xED1C24, + "tvdb": 0x6CD491, + "tvtime": 0xFBD737, + }, + ) return pfDict[pf.value] @@ -203,8 +207,9 @@ def getPlatformName(pf: str | Platform) -> str: return pfDict.get(pf, "Unknown") -def mediaIdToPlatform(media_id: str, platform: str | Platform, - simklType: Union[str, None] = None) -> dict: +def mediaIdToPlatform( + media_id: str, platform: str | Platform, simklType: Union[str, None] = None +) -> dict: """Convert a media ID to a platform-specific ID Args: @@ -221,77 +226,96 @@ def mediaIdToPlatform(media_id: str, platform: str | Platform, if isinstance(platform, str): platform = Platform(platform) platform_dict = { - 'anidb': { - 'uid': f'https://anidb.net/anime/{media_id}', - 'emoid': '1073439145067806801'}, - 'anilist': { - 'uid': f'https://anilist.co/anime/{media_id}', - 'emoid': '1073445700689465374'}, - 'animeplanet': { - 'uid': f'https://www.anime-planet.com/anime/{media_id}', - 'emoid': '1073446927447891998'}, - 'anisearch': { - 'uid': f'https://anisearch.com/anime/{media_id}', - 'emoid': '1073439148100300810'}, - 'annict': { - 'uid': f'https://en.annict.com/works/{media_id}', - 'emoid': '1088801941469012050'}, - 'imdb': { - 'uid': f"https://www.imdb.com/title/{media_id}", - 'emoid': '1079376998880784464'}, - 'kaize': { - 'uid': f'https://kaize.io/anime/{media_id}', - 'emoid': '1073441859910774784'}, - 'kitsu': { - 'uid': f'https://kitsu.io/anime/{media_id}', - 'emoid': '1073439152462368950'}, - 'myanimelist': { - 'uid': f'https://myanimelist.net/anime/{media_id}', - 'emoid': '1073442204921643048'}, - 'shikimori': { - 'uid': f'https://shikimori.one/animes/{media_id}', - 'emoid': '1073441855645155468'}, - 'livechart': { - 'uid': f'https://livechart.me/anime/{media_id}', - 'emoid': '1073439158883844106'}, - 'notify': { - 'uid': f'https://notify.moe/anime/{media_id}', - 'emoid': '1073439161194905690'}, - 'otakotaku': { - 'uid': f'https://otakotaku.com/anime/view/{media_id}', - 'emoid': '1088801946313429013'}, - 'simkl': { - 'uid': f'https://simkl.com/{simklType}/{media_id}', - 'emoid': '1073630754275348631'}, - 'shoboi': { - 'uid': f'https://cal.syoboi.jp/tid/{media_id}', - 'emoid': '1088801950751015005'}, - 'tmdb': { - 'uid': f"https://www.themoviedb.org/{media_id}", - 'emoid': '1079379319920529418'}, - 'silveryasha': { - 'uid': f"https://db.silveryasha.web.id/anime/{media_id}", - 'emoid': "1079380182059733052"}, - 'trakt': { - 'uid': f"https://trakt.tv/{media_id}", - 'emoid': '1081612822175305788'}, - 'tvdb': { - 'uid': media_id, - 'emoid': '1079378495064510504'}, - 'allcin': { - 'uid': f"https://www.allcinema.net/prog/show_c.php?num_c={media_id}", - 'emoid': '1079493870326403123'}, - 'ann': { - 'uid': f"https://www.animenewsnetwork.com/encyclopedia/anime.php?id={media_id}", - 'emoid': '1079377192951230534'}, - 'tvtime': { - 'uid': f"https://tvtime.com/en/{media_id}", - 'emoid': '1091550459023605790'}, + "anidb": { + "uid": f"https://anidb.net/anime/{media_id}", + "emoid": "1073439145067806801", + }, + "anilist": { + "uid": f"https://anilist.co/anime/{media_id}", + "emoid": "1073445700689465374", + }, + "animeplanet": { + "uid": f"https://www.anime-planet.com/anime/{media_id}", + "emoid": "1073446927447891998", + }, + "anisearch": { + "uid": f"https://anisearch.com/anime/{media_id}", + "emoid": "1073439148100300810", + }, + "annict": { + "uid": f"https://en.annict.com/works/{media_id}", + "emoid": "1088801941469012050", + }, + "imdb": { + "uid": f"https://www.imdb.com/title/{media_id}", + "emoid": "1079376998880784464", + }, + "kaize": { + "uid": f"https://kaize.io/anime/{media_id}", + "emoid": "1073441859910774784", + }, + "kitsu": { + "uid": f"https://kitsu.io/anime/{media_id}", + "emoid": "1073439152462368950", + }, + "myanimelist": { + "uid": f"https://myanimelist.net/anime/{media_id}", + "emoid": "1073442204921643048", + }, + "shikimori": { + "uid": f"https://shikimori.one/animes/{media_id}", + "emoid": "1073441855645155468", + }, + "livechart": { + "uid": f"https://livechart.me/anime/{media_id}", + "emoid": "1073439158883844106", + }, + "notify": { + "uid": f"https://notify.moe/anime/{media_id}", + "emoid": "1073439161194905690", + }, + "otakotaku": { + "uid": f"https://otakotaku.com/anime/view/{media_id}", + "emoid": "1088801946313429013", + }, + "simkl": { + "uid": f"https://simkl.com/{simklType}/{media_id}", + "emoid": "1073630754275348631", + }, + "shoboi": { + "uid": f"https://cal.syoboi.jp/tid/{media_id}", + "emoid": "1088801950751015005", + }, + "tmdb": { + "uid": f"https://www.themoviedb.org/{media_id}", + "emoid": "1079379319920529418", + }, + "silveryasha": { + "uid": f"https://db.silveryasha.web.id/anime/{media_id}", + "emoid": "1079380182059733052", + }, + "trakt": { + "uid": f"https://trakt.tv/{media_id}", + "emoid": "1081612822175305788", + }, + "tvdb": {"uid": media_id, "emoid": "1079378495064510504"}, + "allcin": { + "uid": f"https://www.allcinema.net/prog/show_c.php?num_c={media_id}", + "emoid": "1079493870326403123", + }, + "ann": { + "uid": f"https://www.animenewsnetwork.com/encyclopedia/anime.php?id={media_id}", + "emoid": "1079377192951230534", + }, + "tvtime": { + "uid": f"https://tvtime.com/en/{media_id}", + "emoid": "1091550459023605790", + }, } try: data = platform_dict[platform] - data['pf'] = getPlatformName(platform) + data["pf"] = getPlatformName(platform) return data except KeyError: