From b398385e0f48ab1e06013158d2d7b7cb57a3b429 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Tue, 24 Jan 2023 02:57:30 +0800
Subject: [PATCH 01/51] Add genshinutils cog
---
genshinutils/README.md | 22 ++++++
genshinutils/__init__.py | 17 +++++
genshinutils/genshinutils.py | 140 +++++++++++++++++++++++++++++++++++
genshinutils/info.json | 17 +++++
throw/throw.py | 2 +-
5 files changed, 197 insertions(+), 1 deletion(-)
create mode 100644 genshinutils/README.md
create mode 100644 genshinutils/__init__.py
create mode 100644 genshinutils/genshinutils.py
create mode 100644 genshinutils/info.json
diff --git a/genshinutils/README.md b/genshinutils/README.md
new file mode 100644
index 0000000..6691559
--- /dev/null
+++ b/genshinutils/README.md
@@ -0,0 +1,22 @@
+
+Genshin
+
+
+Genshin - A Genshin Impact oriented cog.
+
+
+
+
+Installation
+
+```ini
+[p]load downloader
+[p]repo add raiden-cogs https://github.com/raidensakura/raiden-cogs/
+[p]cog install raiden-cogs genshin
+```
diff --git a/genshinutils/__init__.py b/genshinutils/__init__.py
new file mode 100644
index 0000000..c09efae
--- /dev/null
+++ b/genshinutils/__init__.py
@@ -0,0 +1,17 @@
+import json
+from pathlib import Path
+
+from redbot.core.bot import Red
+
+from .genshinutils import GenshinUtils
+
+with open(Path(__file__).parent / "info.json") as fp:
+ __red_end_user_data_statement__ = json.load(fp)["end_user_data_statement"]
+
+
+async def setup(bot: Red) -> None:
+ cog = GenshinUtils(bot)
+
+ r = bot.add_cog(cog)
+ if r is not None:
+ await r
diff --git a/genshinutils/genshinutils.py b/genshinutils/genshinutils.py
new file mode 100644
index 0000000..fc5aff0
--- /dev/null
+++ b/genshinutils/genshinutils.py
@@ -0,0 +1,140 @@
+import asyncio
+import logging
+from typing import Union
+
+import discord
+import genshin
+from redbot.core import Config, checks, commands
+from redbot.core.bot import Red
+from redbot.core.commands import Context
+
+log = logging.getLogger("red.raidensakura.genshinutils")
+
+
+class GenshinUtils(commands.Cog):
+ """
+ A Genshin Impact cog.
+ """
+
+ __author__ = ["raidensakura"]
+ __version__ = "1.0.0"
+
+ def __init__(self, bot):
+ self.bot = bot
+ self.config = Config.get_conf(self, 243316261264556032, force_registration=True)
+ default_global = {"schema_version": 1}
+ default_user = {"UID": 000000000}
+ self.config.register_user(**default_user)
+
+ def format_help_for_context(self, ctx: commands.Context) -> str:
+ """
+ Thanks Sinbad!
+ """
+ pre_processed = super().format_help_for_context(ctx)
+ s = "s" if len(self.__author__) > 1 else ""
+ return f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}\nCog Version: {self.__version__}"
+
+ async def red_delete_data_for_user(self, **kwargs) -> None:
+ """Nothing to delete"""
+ return
+
+ @checks.is_owner()
+ @commands.group()
+ async def genshinset(self, ctx: commands.Context):
+ """
+ Settings for GenshinUtils cog.
+ """
+
+ @genshinset.command()
+ async def ltoken(self, ctx: commands.Context):
+ """Instructions on how to set the `ltoken` secret."""
+ msg = f"Use `{ctx.prefix}set api genshinutils ltoken your_ltoken_here`."
+ await ctx.send(msg)
+
+ @genshinset.command()
+ async def ltuid(self, ctx: commands.Context):
+ """Instructions on how to set the `ltuid` secret."""
+ msg = f"Use `{ctx.prefix}set api genshinutils ltuid your_ltuid_here`."
+ await ctx.send(msg)
+
+ @commands.group()
+ async def genshin(self, ctx: commands.Context):
+ """
+ GenshinUtils main command.
+ """
+
+ @genshin.command()
+ @commands.guild_only()
+ @commands.bot_has_permissions(embed_links=True)
+ @commands.cooldown(2, 5, commands.BucketType.member)
+ async def profile(self, ctx: Context, *, member: Union[discord.Member, str, None]):
+ """
+ Display a Genshin Impact profile.
+ If a UID is provided, it will display data for that player.
+ If a Discord user is provided, it will display data for that user (if a Genshin UID is linked).
+ If no argument is provided, it will display data for your account (if a Genshin UID is linked).
+ """
+
+ api_keys = await self.bot.get_shared_api_tokens("hoyolab")
+ if api_keys.get("ltuid") is None or api_keys.get("ltoken") is None:
+ return await ctx.send(f"API keys not set.")
+
+ cookies = {"ltuid": api_keys.get("ltuid"), "ltoken": api_keys.get("ltoken")}
+
+ client = genshin.Client(cookies)
+
+ if not member:
+ log.debug("Fetch own UID from config")
+ uid = None
+ return await ctx.send("todo")
+
+ elif isinstance(member, discord.Member) and member.id != ctx.me.id:
+ log.debug("Fetch mentioned user's UID from config")
+ uid = None
+ return await ctx.send("todo")
+
+ elif isinstance(member, discord.Member) and member.id == ctx.me.id:
+ return await ctx.send(f"Sorry, but I do not play Genshin.")
+
+ elif isinstance(member, str) and len(member) == 9 and member.isdigit():
+ uid = member
+ try:
+ data = await client.get_genshin_user(uid)
+ except Exception as exc:
+ log.exception("Error trying to fetch data from API.", exc_info=exc)
+ return await ctx.send(
+ "Oops, I encountered an error while trying to fetch data from Hoyolab."
+ )
+
+ e = discord.Embed(
+ color=(await ctx.embed_colour()),
+ title=f"Data for {data.info.nickname} (AR{data.info.level})",
+ )
+ e.add_field(name="Achievements", value=data.stats.achievements, inline=True)
+ e.add_field(name="Days Active", value=data.stats.days_active, inline=True)
+ e.add_field(
+ name="Characters Unlocked", value=data.stats.characters, inline=True
+ )
+ e.add_field(
+ name="Highest Spiral Abyss Climb",
+ value=data.stats.spiral_abyss,
+ inline=True,
+ )
+ e.add_field(
+ name="Oculi Collected",
+ value=(
+ f"Anemoculi: {data.stats.anemoculi}\n"
+ f"Geoculi: {data.stats.geoculi}\n"
+ f"Electroculi: {data.stats.electroculi}\n"
+ f"Dendroculi: {data.stats.dendroculi}"
+ ),
+ inline=True,
+ )
+
+ try:
+ return await ctx.send(embed=e)
+ except Exception as exc:
+ log.exception("Error trying to send choose embed.", exc_info=exc)
+ return await ctx.send(
+ "Oops, I encountered an error while trying to send the embed."
+ )
diff --git a/genshinutils/info.json b/genshinutils/info.json
new file mode 100644
index 0000000..c1ac06a
--- /dev/null
+++ b/genshinutils/info.json
@@ -0,0 +1,17 @@
+{
+ "name": "GenshinUtils",
+ "short": "A Genshin Impact cog",
+ "description": "",
+ "install_msg": "",
+ "end_user_data_statement": "This cog does not persistently store any data about users.",
+ "author": ["raidensakura"],
+ "required_cogs": {},
+ "requirements": ["genshin"],
+ "tags": [
+ "genshin"
+ ],
+ "min_bot_version": "3.4.12",
+ "hidden": false,
+ "disabled": false,
+ "type": "COG"
+}
\ No newline at end of file
diff --git a/throw/throw.py b/throw/throw.py
index f01739b..0a56376 100644
--- a/throw/throw.py
+++ b/throw/throw.py
@@ -19,7 +19,7 @@ class Throw(commands.Cog):
def __init__(self, bot: Red):
self.bot = bot
- self.config = Config.get_conf(self, 180109040514130509, force_registration=True)
+ self.config = Config.get_conf(self, 243316261264556032, force_registration=True)
default_global = {"schema_version": 1}
self.possible_actions = ["THROW"]
default_user = {"ITEMS_THROWN": 0, "TIMES_HIT": 0}
From 164c5c5d489f787866ba540dce5e0cf6b8256d94 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Wed, 25 Jan 2023 01:06:55 +0800
Subject: [PATCH 02/51] Add verification for profile linking, restructure
commands
---
genshinutils/genshinutils.py | 123 ++++-------------------------------
genshinutils/profile.py | 116 +++++++++++++++++++++++++++++++++
genshinutils/settings.py | 69 ++++++++++++++++++++
3 files changed, 198 insertions(+), 110 deletions(-)
create mode 100644 genshinutils/profile.py
create mode 100644 genshinutils/settings.py
diff --git a/genshinutils/genshinutils.py b/genshinutils/genshinutils.py
index fc5aff0..3858ea1 100644
--- a/genshinutils/genshinutils.py
+++ b/genshinutils/genshinutils.py
@@ -1,20 +1,16 @@
-import asyncio
-import logging
-from typing import Union
+import logging, aiohttp
-import discord
-import genshin
from redbot.core import Config, checks, commands
from redbot.core.bot import Red
from redbot.core.commands import Context
+from .settings import GenshinSet
+from .profile import GenshinProfile
log = logging.getLogger("red.raidensakura.genshinutils")
-class GenshinUtils(commands.Cog):
- """
- A Genshin Impact cog.
- """
+class GenshinUtils(GenshinSet, GenshinProfile, commands.Cog):
+ """GenshinUtils commands."""
__author__ = ["raidensakura"]
__version__ = "1.0.0"
@@ -24,12 +20,15 @@ def __init__(self, bot):
self.config = Config.get_conf(self, 243316261264556032, force_registration=True)
default_global = {"schema_version": 1}
default_user = {"UID": 000000000}
+ self.config.register_global(**default_global)
self.config.register_user(**default_user)
+ self.session = aiohttp.ClientSession()
+
+ def cog_unload(self):
+ self.bot.loop.create_task(self.session.close())
def format_help_for_context(self, ctx: commands.Context) -> str:
- """
- Thanks Sinbad!
- """
+ """Thanks Sinbad!"""
pre_processed = super().format_help_for_context(ctx)
s = "s" if len(self.__author__) > 1 else ""
return f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}\nCog Version: {self.__version__}"
@@ -38,103 +37,7 @@ async def red_delete_data_for_user(self, **kwargs) -> None:
"""Nothing to delete"""
return
- @checks.is_owner()
- @commands.group()
- async def genshinset(self, ctx: commands.Context):
- """
- Settings for GenshinUtils cog.
- """
-
- @genshinset.command()
- async def ltoken(self, ctx: commands.Context):
- """Instructions on how to set the `ltoken` secret."""
- msg = f"Use `{ctx.prefix}set api genshinutils ltoken your_ltoken_here`."
- await ctx.send(msg)
-
- @genshinset.command()
- async def ltuid(self, ctx: commands.Context):
- """Instructions on how to set the `ltuid` secret."""
- msg = f"Use `{ctx.prefix}set api genshinutils ltuid your_ltuid_here`."
- await ctx.send(msg)
-
@commands.group()
- async def genshin(self, ctx: commands.Context):
- """
- GenshinUtils main command.
- """
-
- @genshin.command()
@commands.guild_only()
- @commands.bot_has_permissions(embed_links=True)
- @commands.cooldown(2, 5, commands.BucketType.member)
- async def profile(self, ctx: Context, *, member: Union[discord.Member, str, None]):
- """
- Display a Genshin Impact profile.
- If a UID is provided, it will display data for that player.
- If a Discord user is provided, it will display data for that user (if a Genshin UID is linked).
- If no argument is provided, it will display data for your account (if a Genshin UID is linked).
- """
-
- api_keys = await self.bot.get_shared_api_tokens("hoyolab")
- if api_keys.get("ltuid") is None or api_keys.get("ltoken") is None:
- return await ctx.send(f"API keys not set.")
-
- cookies = {"ltuid": api_keys.get("ltuid"), "ltoken": api_keys.get("ltoken")}
-
- client = genshin.Client(cookies)
-
- if not member:
- log.debug("Fetch own UID from config")
- uid = None
- return await ctx.send("todo")
-
- elif isinstance(member, discord.Member) and member.id != ctx.me.id:
- log.debug("Fetch mentioned user's UID from config")
- uid = None
- return await ctx.send("todo")
-
- elif isinstance(member, discord.Member) and member.id == ctx.me.id:
- return await ctx.send(f"Sorry, but I do not play Genshin.")
-
- elif isinstance(member, str) and len(member) == 9 and member.isdigit():
- uid = member
- try:
- data = await client.get_genshin_user(uid)
- except Exception as exc:
- log.exception("Error trying to fetch data from API.", exc_info=exc)
- return await ctx.send(
- "Oops, I encountered an error while trying to fetch data from Hoyolab."
- )
-
- e = discord.Embed(
- color=(await ctx.embed_colour()),
- title=f"Data for {data.info.nickname} (AR{data.info.level})",
- )
- e.add_field(name="Achievements", value=data.stats.achievements, inline=True)
- e.add_field(name="Days Active", value=data.stats.days_active, inline=True)
- e.add_field(
- name="Characters Unlocked", value=data.stats.characters, inline=True
- )
- e.add_field(
- name="Highest Spiral Abyss Climb",
- value=data.stats.spiral_abyss,
- inline=True,
- )
- e.add_field(
- name="Oculi Collected",
- value=(
- f"Anemoculi: {data.stats.anemoculi}\n"
- f"Geoculi: {data.stats.geoculi}\n"
- f"Electroculi: {data.stats.electroculi}\n"
- f"Dendroculi: {data.stats.dendroculi}"
- ),
- inline=True,
- )
-
- try:
- return await ctx.send(embed=e)
- except Exception as exc:
- log.exception("Error trying to send choose embed.", exc_info=exc)
- return await ctx.send(
- "Oops, I encountered an error while trying to send the embed."
- )
+ async def genshin(self, ctx: commands.Context):
+ """GenshinUtils main command."""
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
new file mode 100644
index 0000000..c379572
--- /dev/null
+++ b/genshinutils/profile.py
@@ -0,0 +1,116 @@
+import logging
+from typing import Union
+
+import discord
+import genshin as genshinpy
+from redbot.core import commands
+from redbot.core.commands import Context
+
+log = logging.getLogger("red.raidensakura.genshinutils")
+
+
+class GenshinProfile(commands.Cog):
+ """GenshinUtils profile commands."""
+
+ # This will get replaced by genshinutils.py's `genshin`
+ # Thanks Jojo#7791!
+ @commands.group()
+ @commands.guild_only()
+ async def genshin(self, ctx: commands.Context):
+ """GenshinUtils main command."""
+
+ @genshin.command()
+ @commands.guild_only()
+ @commands.bot_has_permissions(embed_links=True)
+ @commands.cooldown(2, 5, commands.BucketType.member)
+ async def profile(self, ctx: Context, *, user_or_uid: Union[discord.Member, str, None]):
+ """
+ Display a Genshin Impact profile.
+ If a UID is provided, it will display data for that UID.
+ If a Discord user is provided, it will display data for that user (if UID is linked).
+ If no argument is provided, it will display data for your account (if UID is linked).
+ """
+
+ api_keys = await self.bot.get_shared_api_tokens("hoyolab")
+ if api_keys.get("ltuid") is None or api_keys.get("ltoken") is None:
+ return await ctx.send(f"API keys not set.")
+
+ cookies = {"ltuid": api_keys.get("ltuid"), "ltoken": api_keys.get("ltoken")}
+
+ client = genshinpy.Client(cookies)
+
+ async def get_profile(uid):
+ try:
+ data = await client.get_genshin_user(uid)
+ except Exception as exc:
+ log.debug("Error trying to fetch profile data from Hoyolab API.")
+ return await ctx.send("No profile was found with that UID.")
+
+ e = discord.Embed(
+ color=(await ctx.embed_colour()),
+ description=(
+ f"```fix\n" \
+ f"✨ :: Profile for {data.info.nickname} [AR {data.info.level}]```"
+ ),
+ )
+ e.set_thumbnail(url=data.characters[0].icon)
+ e.add_field(name="Achievements", value=data.stats.achievements, inline=True)
+ e.add_field(name="Days Active", value=data.stats.days_active, inline=True)
+ e.add_field(
+ name="Characters Unlocked", value=data.stats.characters, inline=True
+ )
+ e.add_field(
+ name="Highest Spiral Abyss Climb",
+ value=data.stats.spiral_abyss,
+ inline=True,
+ )
+ e.add_field(
+ name="Total Oculi Collected",
+ value=(f"{data.stats.anemoculi + data.stats.geoculi + data.stats.electroculi + data.stats.dendroculi}"),
+ inline=True,
+ )
+ e.add_field(
+ name="Waypoints Unlocked",
+ value=(f"{data.stats.unlocked_waypoints}"),
+ inline=True
+ )
+ e.add_field(
+ name="Total Chests Opened",
+ value=(f"{data.stats.common_chests + data.stats.precious_chests + data.stats.exquisite_chests + data.stats.luxurious_chests + data.stats.remarkable_chests}"),
+ inline=True
+ )
+ e.add_field(
+ name="Domains Unlocked",
+ value=(f"{data.stats.unlocked_domains}"),
+ inline=True
+ )
+
+ try:
+ return await ctx.send(embed=e)
+ except Exception as exc:
+ log.exception("Error trying to send embed.", exc_info=exc)
+ return await ctx.send(
+ "Oops, I encountered an error while trying to send the embed."
+ )
+
+ if not user_or_uid:
+ uid = await self.config.user(ctx.author).get_raw("UID")
+ if not uid or uid == "000000000":
+ return await ctx.send("You don't have any UID linked.")
+ async with ctx.typing():
+ return await get_profile(uid)
+
+ elif isinstance(user_or_uid, discord.Member) and user_or_uid.id != ctx.me.id:
+ uid = await self.config.user(user_or_uid).get_raw("UID")
+ if not uid or uid == "000000000":
+ return await ctx.send("That user has not linked a UID yet.")
+ async with ctx.typing():
+ return await get_profile(uid)
+
+ elif isinstance(user_or_uid, discord.Member) and user_or_uid.id == ctx.me.id:
+ return await ctx.send(f"Sorry, but I do not play Genshin.")
+
+ elif isinstance(user_or_uid, str) and len(user_or_uid) == 9 and user_or_uid.isdigit():
+ uid = user_or_uid
+ async with ctx.typing():
+ return await get_profile(uid)
diff --git a/genshinutils/settings.py b/genshinutils/settings.py
new file mode 100644
index 0000000..e2a523c
--- /dev/null
+++ b/genshinutils/settings.py
@@ -0,0 +1,69 @@
+import logging, json
+
+from redbot.core import checks, commands
+from redbot.core.commands import Context
+
+log = logging.getLogger("red.raidensakura.genshinutils")
+
+
+class GenshinSet(commands.Cog):
+ """
+ Settings for GenshinUtils cog.
+ """
+
+ @commands.group()
+ async def genshinset(self, ctx):
+ """Settings for GenshinUtils cog."""
+
+ @checks.is_owner()
+ @genshinset.command()
+ async def ltoken(self, ctx: commands.Context):
+ """Instructions on how to set the `ltoken` secret."""
+ await ctx.send(f"Use `{ctx.prefix}set api hoyolab ltoken your_ltoken_here`.")
+
+ @checks.is_owner()
+ @genshinset.command()
+ async def ltuid(self, ctx: commands.Context):
+ """Instructions on how to set the `ltuid` secret."""
+ await ctx.send(f"Use `{ctx.prefix}set api hoyolab ltuid your_ltuid_here`.")
+
+ @genshinset.command(name="uid", usage="")
+ @commands.guild_only()
+ @commands.cooldown(2, 5, commands.BucketType.member)
+ async def set_uid(self, ctx: commands.Context, uid_or_remove: str):
+ """
+ Link or unlink a Genshin Impact UID to your Discord account.
+ For verification purpose, you will need to add your Discord tag to your in-game signature.
+ It can take up to 15 minutes for your signature to be refreshed.
+ """
+ if uid_or_remove.lower() == "remove" or uid_or_remove.lower() == "unlink":
+ await self.config.user(ctx.author).UID.clear()
+ return await ctx.send(f"Successfully removed UID for {ctx.author.name}")
+ else:
+ uid = uid_or_remove
+ if not len(uid) == 9 or not uid.isdigit():
+ return await ctx.send("Not a valid UID.")
+
+ try:
+ reqmethod = self.session.get
+ url = f"https://enka.network/u/{uid}/__data.json"
+ async with reqmethod(url, headers={}, data={}) as req:
+ data = await req.text()
+ status = req.status
+ try:
+ parsed = json.loads(data)
+ except json.JSONDecodeError:
+ parsed = data
+ except Exception as exc:
+ log.error(exc)
+ return await ctx.send("Error trying to fetch data from API [enka.network].")
+
+ author_discord_id = f"{ctx.author.name}#{ctx.author.discriminator}"
+ signature = parsed["playerInfo"]["signature"]
+ if author_discord_id in signature:
+ log.debug("UID and signature match")
+ await self.config.user(ctx.author).UID.set(uid)
+ return await ctx.send(f"Successfully set UID for {ctx.author.name} to {uid}")
+ else:
+ log.debug("UID and signature does not match")
+ return await ctx.send(f"Your signature does not contain your Discord tag.\nNote that may take up to 15 minutes for changes to be reflected.")
From 1953070a7f7bc6d69ff57cb9bade34fa142ec5f5 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Thu, 26 Jan 2023 22:44:00 +0800
Subject: [PATCH 03/51] Add dictionary
---
genshinutils/constants.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 genshinutils/constants.py
diff --git a/genshinutils/constants.py b/genshinutils/constants.py
new file mode 100644
index 0000000..cd38510
--- /dev/null
+++ b/genshinutils/constants.py
@@ -0,0 +1,13 @@
+"""
+List of commonly accepted character names
+Key name always need to be properly capitalized and match in-game character name
+First index always need to match formal name but in all lowercase
+This make sure the profile command accepts a variation of valid character names
+"""
+common_names = {
+ "Kamisato Ayato": ["kamisato ayato", "ayato"],
+ "Kamisato Ayaka": ["kamisato ayaka", "ayaka", "ayaya"],
+ "Raiden Shogun": ["raiden shogun", "raiden", "shogun", "ei", "beelzebul"],
+ "Arataki Itto": ["arataki itto", "itto", "arataki"],
+ # Add more stuff here
+}
From 15baef8de755212aed3db3d36aa7d1b0e1b5cfb5 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Thu, 26 Jan 2023 22:45:48 +0800
Subject: [PATCH 04/51] =?UTF-8?q?More=20stuff=20=E2=9C=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
genshinutils/genshinutils.py | 28 +++--
genshinutils/info.json | 10 +-
genshinutils/profile.py | 219 ++++++++++++++++++++++++-----------
genshinutils/settings.py | 76 +++++++-----
4 files changed, 219 insertions(+), 114 deletions(-)
diff --git a/genshinutils/genshinutils.py b/genshinutils/genshinutils.py
index 3858ea1..75bd2c5 100644
--- a/genshinutils/genshinutils.py
+++ b/genshinutils/genshinutils.py
@@ -1,10 +1,15 @@
-import logging, aiohttp
+import logging
+from typing import Literal
+from enkanetwork import EnkaNetworkAPI
from redbot.core import Config, checks, commands
from redbot.core.bot import Red
from redbot.core.commands import Context
-from .settings import GenshinSet
+
from .profile import GenshinProfile
+from .settings import GenshinSet
+
+enka_client = EnkaNetworkAPI()
log = logging.getLogger("red.raidensakura.genshinutils")
@@ -18,14 +23,15 @@ class GenshinUtils(GenshinSet, GenshinProfile, commands.Cog):
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, 243316261264556032, force_registration=True)
- default_global = {"schema_version": 1}
- default_user = {"UID": 000000000}
+ default_global = {"schema_version": 1, "verification": True}
+ default_user = {"UID": ""}
self.config.register_global(**default_global)
self.config.register_user(**default_user)
- self.session = aiohttp.ClientSession()
+ self.enka_client = enka_client
def cog_unload(self):
- self.bot.loop.create_task(self.session.close())
+ log.debug(f"[Cog Unload] Executing tasks.")
+ enka_client._close()
def format_help_for_context(self, ctx: commands.Context) -> str:
"""Thanks Sinbad!"""
@@ -33,9 +39,13 @@ def format_help_for_context(self, ctx: commands.Context) -> str:
s = "s" if len(self.__author__) > 1 else ""
return f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}\nCog Version: {self.__version__}"
- async def red_delete_data_for_user(self, **kwargs) -> None:
- """Nothing to delete"""
- return
+ async def red_delete_data_for_user(
+ self,
+ *,
+ requester: Literal["discord", "owner", "user", "user_strict"],
+ user_id: int,
+ ):
+ await self.config.user_from_id(user_id).clear()
@commands.group()
@commands.guild_only()
diff --git a/genshinutils/info.json b/genshinutils/info.json
index c1ac06a..bf9a05d 100644
--- a/genshinutils/info.json
+++ b/genshinutils/info.json
@@ -1,12 +1,12 @@
{
"name": "GenshinUtils",
- "short": "A Genshin Impact cog",
- "description": "",
- "install_msg": "",
- "end_user_data_statement": "This cog does not persistently store any data about users.",
+ "short": "A Genshin Impact utility cog",
+ "description": "Various useful utilities for Genshin Impact, such as retrieving profile data and many more.",
+ "install_msg": "Thank you for installing my cog. This is continuously being worked on, expect rapid changes.\nI test stuff in my Discord: https://dsc.gg/transience",
+ "end_user_data_statement": "This cog interact with a public API to store your linked game UID.",
"author": ["raidensakura"],
"required_cogs": {},
- "requirements": ["genshin"],
+ "requirements": ["genshin", "enkanetwork", "aioenkanetworkcard"],
"tags": [
"genshin"
],
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
index c379572..38db6d4 100644
--- a/genshinutils/profile.py
+++ b/genshinutils/profile.py
@@ -1,17 +1,22 @@
+import io
import logging
+import time
from typing import Union
import discord
-import genshin as genshinpy
+from aioenkanetworkcard import encbanner
from redbot.core import commands
from redbot.core.commands import Context
+from .constants import common_names
+
log = logging.getLogger("red.raidensakura.genshinutils")
class GenshinProfile(commands.Cog):
- """GenshinUtils profile commands."""
+ """GenshinUtils profile command class."""
+ # https://discord.com/channels/133049272517001216/160386989819035648/1067445744497348639
# This will get replaced by genshinutils.py's `genshin`
# Thanks Jojo#7791!
@commands.group()
@@ -22,95 +27,171 @@ async def genshin(self, ctx: commands.Context):
@genshin.command()
@commands.guild_only()
@commands.bot_has_permissions(embed_links=True)
- @commands.cooldown(2, 5, commands.BucketType.member)
- async def profile(self, ctx: Context, *, user_or_uid: Union[discord.Member, str, None]):
+ @commands.cooldown(2, 10, commands.BucketType.member)
+ async def profile(
+ self,
+ ctx: Context,
+ user_or_uid: Union[discord.Member, str, None],
+ *,
+ character: Union[str, None],
+ ):
"""
- Display a Genshin Impact profile.
- If a UID is provided, it will display data for that UID.
- If a Discord user is provided, it will display data for that user (if UID is linked).
- If no argument is provided, it will display data for your account (if UID is linked).
+ Display a Genshin Impact profile for a UID, Discord user or yourself.
+ If a character name is provided, it will display character infographic instead.
"""
- api_keys = await self.bot.get_shared_api_tokens("hoyolab")
- if api_keys.get("ltuid") is None or api_keys.get("ltoken") is None:
- return await ctx.send(f"API keys not set.")
-
- cookies = {"ltuid": api_keys.get("ltuid"), "ltoken": api_keys.get("ltoken")}
-
- client = genshinpy.Client(cookies)
-
async def get_profile(uid):
try:
- data = await client.get_genshin_user(uid)
+ data = await self.enka_client.fetch_user(uid)
except Exception as exc:
- log.debug("Error trying to fetch profile data from Hoyolab API.")
- return await ctx.send("No profile was found with that UID.")
+ return await ctx.send(
+ f"Unable to retrieve data from enka.network:\n`{exc}`"
+ )
e = discord.Embed(
color=(await ctx.embed_colour()),
description=(
- f"```fix\n" \
- f"✨ :: Profile for {data.info.nickname} [AR {data.info.level}]```"
- ),
- )
- e.set_thumbnail(url=data.characters[0].icon)
- e.add_field(name="Achievements", value=data.stats.achievements, inline=True)
- e.add_field(name="Days Active", value=data.stats.days_active, inline=True)
- e.add_field(
- name="Characters Unlocked", value=data.stats.characters, inline=True
- )
- e.add_field(
- name="Highest Spiral Abyss Climb",
- value=data.stats.spiral_abyss,
- inline=True,
- )
- e.add_field(
- name="Total Oculi Collected",
- value=(f"{data.stats.anemoculi + data.stats.geoculi + data.stats.electroculi + data.stats.dendroculi}"),
- inline=True,
- )
- e.add_field(
- name="Waypoints Unlocked",
- value=(f"{data.stats.unlocked_waypoints}"),
- inline=True
- )
- e.add_field(
- name="Total Chests Opened",
- value=(f"{data.stats.common_chests + data.stats.precious_chests + data.stats.exquisite_chests + data.stats.luxurious_chests + data.stats.remarkable_chests}"),
- inline=True
+ f"```fix\n"
+ f"✨ :: Profile for {data.player.nickname} [AR {data.player.level}]```\n"
+ ),
)
+ if data.player.characters_preview:
+ char_str = ""
+ for character in data.player.characters_preview:
+ if character.name == data.player.characters_preview[0].name:
+ char_str += f"{character.name}"
+ else:
+ char_str += f", {character.name}"
+ e.add_field(
+ name="Characters in Showcase",
+ value=(f"```fix\n" f"{char_str}" f"```"),
+ inline=False,
+ )
+ e.set_thumbnail(url=data.player.avatar.icon.url)
+ e.set_image(url=data.player.namecard.banner.url)
+ e.add_field(name="Signature", value=f"{data.player.signature}")
+ e.add_field(name="World Level", value=f"{data.player.world_level}")
+ e.add_field(name="Achievements", value=data.player.achievement)
e.add_field(
- name="Domains Unlocked",
- value=(f"{data.stats.unlocked_domains}"),
- inline=True
+ name="Current Spiral Abyss Floor",
+ value=f"{data.player.abyss_floor} - {data.player.abyss_room}",
)
try:
return await ctx.send(embed=e)
except Exception as exc:
- log.exception("Error trying to send embed.", exc_info=exc)
+ log.exception(
+ f"[{get_profile.__name__}] Error trying to send embed.",
+ exc_info=exc,
+ )
return await ctx.send(
"Oops, I encountered an error while trying to send the embed."
)
- if not user_or_uid:
- uid = await self.config.user(ctx.author).get_raw("UID")
- if not uid or uid == "000000000":
- return await ctx.send("You don't have any UID linked.")
- async with ctx.typing():
- return await get_profile(uid)
+ async def get_character_card(uid, char_name):
+ async with encbanner.ENC(
+ lang="en", splashArt=True, characterName=char_name
+ ) as encard:
+ ENCpy = await encard.enc(uids=uid)
+ return await encard.creat(ENCpy, 2)
+
+ async def validate_uid(u):
+ if isinstance(u, discord.Member):
+ uid = await self.config.user(u).get_raw("UID")
+ if uid:
+ exist = "exist"
+ else:
+ exist = "does not exist"
+ log.debug(f"[{validate_uid.__name__}] UID {exist} in config.")
+
+ elif isinstance(u, str) and len(u) == 9 and u.isdigit():
+ uid = u
+ log.debug(f"[{validate_uid.__name__}] This is a valid UID.")
+
+ else:
+ uid = None
+ log.debug(f"[{validate_uid.__name__}] This is not a valid UID.")
+
+ return uid
+
+ def validate_char_name(arg):
+ formal_name = {i for i in common_names if arg in common_names[i]}
+ if formal_name:
+ return str(formal_name).strip("{'\"}")
+
+ async def generate_char_info(uid, char_name):
+ """Generate character info"""
+ with io.BytesIO() as image_binary:
+ char_card = await get_character_card(uid, char_name)
+ if not char_card:
+ return await ctx.send(
+ "This user does not have that character featured."
+ )
+ temp_filename = str(time.time()).split(".")[0] + ".png"
+ log.debug(
+ f"[{generate_char_info.__name__}] Pillow object for character card:\n{char_card}"
+ )
+ char_card[uid][char_name].save(
+ image_binary, "PNG", optimize=True, quality=95
+ )
+ image_binary.seek(0)
+ return await ctx.send(
+ file=discord.File(fp=image_binary, filename=temp_filename)
+ )
- elif isinstance(user_or_uid, discord.Member) and user_or_uid.id != ctx.me.id:
- uid = await self.config.user(user_or_uid).get_raw("UID")
- if not uid or uid == "000000000":
- return await ctx.send("That user has not linked a UID yet.")
- async with ctx.typing():
- return await get_profile(uid)
+ log.debug(f"[Args] user_or_uid: {user_or_uid}")
+ log.debug(f"[Args] character: {character}")
- elif isinstance(user_or_uid, discord.Member) and user_or_uid.id == ctx.me.id:
- return await ctx.send(f"Sorry, but I do not play Genshin.")
+ """If nothing is passed at all, we assume user is trying to generate their own profile"""
+ if not user_or_uid and not character:
+ uid = await validate_uid(ctx.author)
+ if not uid:
+ return await ctx.send("You do not have a UID linked.")
- elif isinstance(user_or_uid, str) and len(user_or_uid) == 9 and user_or_uid.isdigit():
- uid = user_or_uid
- async with ctx.typing():
+ with ctx.typing():
return await get_profile(uid)
+
+ """
+ Since both args are optional: [user_or_uid] [character]
+ [character] could be passed as [user_or_uid]
+ We check and handle it appropriately
+ """
+ if user_or_uid and not character:
+ uid = await validate_uid(user_or_uid)
+ if uid:
+ with ctx.typing():
+ return await get_profile(uid)
+
+ log.debug(
+ f"[{get_profile.__name__}] Not a UID, assuming it's a character name..."
+ )
+ char = validate_char_name(user_or_uid)
+ if not char:
+ return await ctx.send(
+ "Not a valid UID or character name that's not in dictionary."
+ )
+
+ log.debug(
+ f"[{get_profile.__name__}] Valid character name found, trying to fetch author UID..."
+ )
+ uid = await validate_uid(ctx.author)
+ if not uid:
+ return await ctx.send("You do not have a UID linked.")
+
+ with ctx.typing():
+ return await generate_char_info(uid, char)
+
+ """This handles if both [user_or_uid] and [character] are appropriately passed"""
+ if user_or_uid and character:
+ uid = await validate_uid(user_or_uid)
+ if not uid:
+ return await ctx.send(
+ "Not a valid UID or user does not have a UID linked."
+ )
+
+ char = validate_char_name(character)
+ if not char:
+ return await ctx.send("Character name invalid or not in dictionary.")
+
+ with ctx.typing():
+ await generate_char_info(uid, char)
diff --git a/genshinutils/settings.py b/genshinutils/settings.py
index e2a523c..420e615 100644
--- a/genshinutils/settings.py
+++ b/genshinutils/settings.py
@@ -1,4 +1,4 @@
-import logging, json
+import logging
from redbot.core import checks, commands
from redbot.core.commands import Context
@@ -7,63 +7,77 @@
class GenshinSet(commands.Cog):
- """
- Settings for GenshinUtils cog.
- """
+ """GenshinUtils genshinset command class."""
@commands.group()
async def genshinset(self, ctx):
- """Settings for GenshinUtils cog."""
+ """Various settings for GenshinUtils cog."""
@checks.is_owner()
@genshinset.command()
async def ltoken(self, ctx: commands.Context):
- """Instructions on how to set the `ltoken` secret."""
+ """(Unused) Instructions on how to set the `ltoken` secret."""
await ctx.send(f"Use `{ctx.prefix}set api hoyolab ltoken your_ltoken_here`.")
@checks.is_owner()
@genshinset.command()
async def ltuid(self, ctx: commands.Context):
- """Instructions on how to set the `ltuid` secret."""
+ """(Unused) Instructions on how to set the `ltuid` secret."""
await ctx.send(f"Use `{ctx.prefix}set api hoyolab ltuid your_ltuid_here`.")
+ @checks.is_owner()
+ @genshinset.command()
+ async def verification(self, ctx: commands.Context, toggle: bool):
+ """Globally enable or disable UID verification for GenshinUtils cog."""
+ await self.config.verification.set(toggle)
+ status = "enabled" if toggle else "disabled"
+ return await ctx.send(f"Global UID verification has been {status}.")
+
@genshinset.command(name="uid", usage="")
@commands.guild_only()
@commands.cooldown(2, 5, commands.BucketType.member)
async def set_uid(self, ctx: commands.Context, uid_or_remove: str):
"""
Link or unlink a Genshin Impact UID to your Discord account.
- For verification purpose, you will need to add your Discord tag to your in-game signature.
+ If verification is enabled, you need to add your Discord tag to your in-game signature.
It can take up to 15 minutes for your signature to be refreshed.
"""
+
+ async def verification_enabled():
+ enabled = (
+ await self.config.verification()
+ # or await self.config.guild.verification()
+ )
+ return enabled
+
+ def pass_verification(discordtag, signature):
+ if discordtag == signature:
+ return True
+
if uid_or_remove.lower() == "remove" or uid_or_remove.lower() == "unlink":
await self.config.user(ctx.author).UID.clear()
- return await ctx.send(f"Successfully removed UID for {ctx.author.name}")
- else:
- uid = uid_or_remove
+ return await ctx.send(f"Successfully removed UID for {ctx.author.name}.")
+
+ uid = uid_or_remove
+
if not len(uid) == 9 or not uid.isdigit():
- return await ctx.send("Not a valid UID.")
+ return await ctx.send("Invalid UID provided, it must consist of 9 digits.")
try:
- reqmethod = self.session.get
- url = f"https://enka.network/u/{uid}/__data.json"
- async with reqmethod(url, headers={}, data={}) as req:
- data = await req.text()
- status = req.status
- try:
- parsed = json.loads(data)
- except json.JSONDecodeError:
- parsed = data
+ with ctx.typing():
+ data = await self.enka_client.fetch_user(uid)
except Exception as exc:
- log.error(exc)
- return await ctx.send("Error trying to fetch data from API [enka.network].")
+ return await ctx.send(
+ f"Unable to retrieve data from enka.network:\n`{exc}`"
+ )
author_discord_id = f"{ctx.author.name}#{ctx.author.discriminator}"
- signature = parsed["playerInfo"]["signature"]
- if author_discord_id in signature:
- log.debug("UID and signature match")
- await self.config.user(ctx.author).UID.set(uid)
- return await ctx.send(f"Successfully set UID for {ctx.author.name} to {uid}")
- else:
- log.debug("UID and signature does not match")
- return await ctx.send(f"Your signature does not contain your Discord tag.\nNote that may take up to 15 minutes for changes to be reflected.")
+
+ if await verification_enabled() and not pass_verification(
+ author_discord_id, data.player.signature
+ ):
+ return await ctx.send(
+ f"Your signature does not contain your Discord tag.\nNote that it may take up to 15 minutes for changes to be reflected."
+ )
+ await self.config.user(ctx.author).UID.set(uid)
+ return await ctx.send(f"Successfully set UID for {ctx.author.name} to {uid}.")
From 1df04d22796742c4eba4f27f962e8acd6c1b0e6e Mon Sep 17 00:00:00 2001
From: Raiden
Date: Sat, 28 Jan 2023 21:42:38 +0800
Subject: [PATCH 05/51] =?UTF-8?q?Too=20much=20stuff=20=E2=9C=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
genshinutils/genshinutils.py | 17 +--
genshinutils/info.json | 2 +-
genshinutils/profile.py | 80 +++----------
genshinutils/register.py | 218 +++++++++++++++++++++++++++++++++++
genshinutils/settings.py | 63 ++--------
genshinutils/utils.py | 80 +++++++++++++
6 files changed, 335 insertions(+), 125 deletions(-)
create mode 100644 genshinutils/register.py
create mode 100644 genshinutils/utils.py
diff --git a/genshinutils/genshinutils.py b/genshinutils/genshinutils.py
index 75bd2c5..86d30bd 100644
--- a/genshinutils/genshinutils.py
+++ b/genshinutils/genshinutils.py
@@ -3,10 +3,10 @@
from enkanetwork import EnkaNetworkAPI
from redbot.core import Config, checks, commands
-from redbot.core.bot import Red
from redbot.core.commands import Context
from .profile import GenshinProfile
+from .register import GenshinRegister
from .settings import GenshinSet
enka_client = EnkaNetworkAPI()
@@ -14,7 +14,7 @@
log = logging.getLogger("red.raidensakura.genshinutils")
-class GenshinUtils(GenshinSet, GenshinProfile, commands.Cog):
+class GenshinUtils(GenshinSet, GenshinRegister, GenshinProfile, commands.Cog):
"""GenshinUtils commands."""
__author__ = ["raidensakura"]
@@ -23,14 +23,17 @@ class GenshinUtils(GenshinSet, GenshinProfile, commands.Cog):
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, 243316261264556032, force_registration=True)
- default_global = {"schema_version": 1, "verification": True}
- default_user = {"UID": ""}
+ default_global = {
+ "schema_version": 1,
+ "verification": True,
+ "encryption_key": "",
+ }
+ default_user = {"UID": "", "ltuid": "", "ltoken": ""}
self.config.register_global(**default_global)
self.config.register_user(**default_user)
self.enka_client = enka_client
def cog_unload(self):
- log.debug(f"[Cog Unload] Executing tasks.")
enka_client._close()
def format_help_for_context(self, ctx: commands.Context) -> str:
@@ -42,12 +45,12 @@ def format_help_for_context(self, ctx: commands.Context) -> str:
async def red_delete_data_for_user(
self,
*,
- requester: Literal["discord", "owner", "user", "user_strict"],
+ requester: Literal["discord_deleted_user", "owner", "user_strict", "user"],
user_id: int,
):
await self.config.user_from_id(user_id).clear()
@commands.group()
- @commands.guild_only()
async def genshin(self, ctx: commands.Context):
"""GenshinUtils main command."""
+ # TODO: Embed explaining what this cog does and its info
diff --git a/genshinutils/info.json b/genshinutils/info.json
index bf9a05d..00b68d1 100644
--- a/genshinutils/info.json
+++ b/genshinutils/info.json
@@ -6,7 +6,7 @@
"end_user_data_statement": "This cog interact with a public API to store your linked game UID.",
"author": ["raidensakura"],
"required_cogs": {},
- "requirements": ["genshin", "enkanetwork", "aioenkanetworkcard"],
+ "requirements": ["genshin", "enkanetwork", "aioenkanetworkcard", "fernet"],
"tags": [
"genshin"
],
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
index 38db6d4..a133b9e 100644
--- a/genshinutils/profile.py
+++ b/genshinutils/profile.py
@@ -4,11 +4,9 @@
from typing import Union
import discord
-from aioenkanetworkcard import encbanner
-from redbot.core import commands
-from redbot.core.commands import Context
+from redbot.core import checks, commands
-from .constants import common_names
+from .utils import get_character_card, validate_char_name, validate_uid
log = logging.getLogger("red.raidensakura.genshinutils")
@@ -20,7 +18,6 @@ class GenshinProfile(commands.Cog):
# This will get replaced by genshinutils.py's `genshin`
# Thanks Jojo#7791!
@commands.group()
- @commands.guild_only()
async def genshin(self, ctx: commands.Context):
"""GenshinUtils main command."""
@@ -30,7 +27,7 @@ async def genshin(self, ctx: commands.Context):
@commands.cooldown(2, 10, commands.BucketType.member)
async def profile(
self,
- ctx: Context,
+ ctx: commands.Context,
user_or_uid: Union[discord.Member, str, None],
*,
character: Union[str, None],
@@ -40,7 +37,7 @@ async def profile(
If a character name is provided, it will display character infographic instead.
"""
- async def get_profile(uid):
+ async def generate_profile(uid):
try:
data = await self.enka_client.fetch_user(uid)
except Exception as exc:
@@ -77,50 +74,9 @@ async def get_profile(uid):
value=f"{data.player.abyss_floor} - {data.player.abyss_room}",
)
- try:
- return await ctx.send(embed=e)
- except Exception as exc:
- log.exception(
- f"[{get_profile.__name__}] Error trying to send embed.",
- exc_info=exc,
- )
- return await ctx.send(
- "Oops, I encountered an error while trying to send the embed."
- )
-
- async def get_character_card(uid, char_name):
- async with encbanner.ENC(
- lang="en", splashArt=True, characterName=char_name
- ) as encard:
- ENCpy = await encard.enc(uids=uid)
- return await encard.creat(ENCpy, 2)
-
- async def validate_uid(u):
- if isinstance(u, discord.Member):
- uid = await self.config.user(u).get_raw("UID")
- if uid:
- exist = "exist"
- else:
- exist = "does not exist"
- log.debug(f"[{validate_uid.__name__}] UID {exist} in config.")
-
- elif isinstance(u, str) and len(u) == 9 and u.isdigit():
- uid = u
- log.debug(f"[{validate_uid.__name__}] This is a valid UID.")
-
- else:
- uid = None
- log.debug(f"[{validate_uid.__name__}] This is not a valid UID.")
-
- return uid
-
- def validate_char_name(arg):
- formal_name = {i for i in common_names if arg in common_names[i]}
- if formal_name:
- return str(formal_name).strip("{'\"}")
+ return await ctx.send(embed=e)
async def generate_char_info(uid, char_name):
- """Generate character info"""
with io.BytesIO() as image_binary:
char_card = await get_character_card(uid, char_name)
if not char_card:
@@ -129,11 +85,11 @@ async def generate_char_info(uid, char_name):
)
temp_filename = str(time.time()).split(".")[0] + ".png"
log.debug(
- f"[{generate_char_info.__name__}] Pillow object for character card:\n{char_card}"
- )
- char_card[uid][char_name].save(
- image_binary, "PNG", optimize=True, quality=95
+ f"[generate_char_info] Pillow object for character card:\n{char_card}"
)
+ first_card = next(iter(char_card.values()))
+ card_object = next(iter(first_card.values()))
+ card_object.save(image_binary, "PNG", optimize=True, quality=95)
image_binary.seek(0)
return await ctx.send(
file=discord.File(fp=image_binary, filename=temp_filename)
@@ -144,12 +100,12 @@ async def generate_char_info(uid, char_name):
"""If nothing is passed at all, we assume user is trying to generate their own profile"""
if not user_or_uid and not character:
- uid = await validate_uid(ctx.author)
+ uid = await validate_uid(ctx.author, self)
if not uid:
return await ctx.send("You do not have a UID linked.")
with ctx.typing():
- return await get_profile(uid)
+ return await generate_profile(uid)
"""
Since both args are optional: [user_or_uid] [character]
@@ -157,13 +113,13 @@ async def generate_char_info(uid, char_name):
We check and handle it appropriately
"""
if user_or_uid and not character:
- uid = await validate_uid(user_or_uid)
+ uid = await validate_uid(user_or_uid, self)
if uid:
with ctx.typing():
- return await get_profile(uid)
+ return await generate_profile(uid)
log.debug(
- f"[{get_profile.__name__}] Not a UID, assuming it's a character name..."
+ f"[{ctx.command.name}] Not a UID, assuming it's a character name..."
)
char = validate_char_name(user_or_uid)
if not char:
@@ -172,9 +128,9 @@ async def generate_char_info(uid, char_name):
)
log.debug(
- f"[{get_profile.__name__}] Valid character name found, trying to fetch author UID..."
+ f"[{ctx.command.name}] Valid character name found, trying to fetch author UID..."
)
- uid = await validate_uid(ctx.author)
+ uid = await validate_uid(ctx.author, self)
if not uid:
return await ctx.send("You do not have a UID linked.")
@@ -183,7 +139,7 @@ async def generate_char_info(uid, char_name):
"""This handles if both [user_or_uid] and [character] are appropriately passed"""
if user_or_uid and character:
- uid = await validate_uid(user_or_uid)
+ uid = await validate_uid(user_or_uid, self)
if not uid:
return await ctx.send(
"Not a valid UID or user does not have a UID linked."
@@ -194,4 +150,4 @@ async def generate_char_info(uid, char_name):
return await ctx.send("Character name invalid or not in dictionary.")
with ctx.typing():
- await generate_char_info(uid, char)
+ return await generate_char_info(uid, char)
diff --git a/genshinutils/register.py b/genshinutils/register.py
new file mode 100644
index 0000000..e0360a7
--- /dev/null
+++ b/genshinutils/register.py
@@ -0,0 +1,218 @@
+import logging
+import re
+from operator import attrgetter
+from re import escape
+
+import genshin
+from discord import Embed
+from discord.channel import DMChannel
+from redbot.core import checks, commands
+
+from .utils import decrypt_config, encrypt_config
+
+log = logging.getLogger("red.raidensakura.genshinutils")
+
+
+class GenshinRegister(commands.Cog):
+ """GenshinUtils register command class."""
+
+ # https://discord.com/channels/133049272517001216/160386989819035648/1067445744497348639
+ # This will get replaced by genshinutils.py's `genshin`
+ # Thanks Jojo#7791!
+ @commands.group()
+ async def genshin(self, ctx: commands.Context):
+ """GenshinUtils main command."""
+
+ @genshin.group()
+ async def register(self, ctx: commands.Context):
+ """Registration commands for GenshinUtils cog."""
+
+ @register.command(name="uid", usage="")
+ @commands.cooldown(2, 5, commands.BucketType.member)
+ async def set_uid(self, ctx: commands.Context, uid_or_remove: str):
+ """
+ Link or unlink a Genshin Impact UID to your Discord account.
+ If verification is enabled, you need to add your Discord tag to your in-game signature.
+ It can take up to 15 minutes for your signature to be refreshed.
+ """
+
+ async def verification_enabled():
+ enabled = await self.config.verification()
+ return enabled
+
+ def pass_verification(discordtag, signature):
+ if discordtag == signature:
+ return True
+
+ if uid_or_remove.lower() == "remove" or uid_or_remove.lower() == "unlink":
+ await self.config.user(ctx.author).UID.clear()
+ return await ctx.send(f"Successfully removed UID for {ctx.author.name}.")
+
+ uid = uid_or_remove
+
+ if not len(uid) == 9 or not uid.isdigit():
+ return await ctx.send("Invalid UID provided, it must consist of 9 digits.")
+
+ try:
+ with ctx.typing():
+ data = await self.enka_client.fetch_user(uid)
+ except Exception as exc:
+ return await ctx.send(
+ f"Unable to retrieve data from enka.network:\n`{exc}`"
+ )
+
+ author_discord_id = f"{ctx.author.name}#{ctx.author.discriminator}"
+
+ if await verification_enabled() and not pass_verification(
+ author_discord_id, data.player.signature
+ ):
+ return await ctx.send(
+ f"Your signature does not contain your Discord tag.\nNote that it may take up to 15 minutes for changes to be reflected."
+ )
+ await self.config.user(ctx.author).UID.set(uid)
+ return await ctx.send(f"Successfully set UID for {ctx.author.name} to {uid}.")
+
+ """
+ Important Notes:
+ 1. Command has proprietary DM check since I want it to preface a a disclaimer when run in a server.
+ This command deals with sensitive information and I want it to be taken very seriously.
+ 2. I fully acknowledge storing the encryption key along with the encrypted data itself is terrible security practice.
+ Hoyolab account token can be used to perform destructive account actions, and potentially get your account banned for abuse.
+ Since the cog is open-source, the purpose of the encryption is to prevent bot owners from having plaintext access to them
+ in a way such that is require a bit of coding and encryption knowledge to access them on demand.
+ """
+
+ @register.command()
+ @commands.bot_has_permissions(embed_links=True)
+ @commands.cooldown(2, 10, commands.BucketType.member)
+ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
+ """Link or unlink a Hoyolab account token to your Discord account."""
+
+ if not isinstance(ctx.channel, DMChannel):
+
+ if cookie:
+ try:
+ await ctx.message.delete()
+ except:
+ pass
+
+ # Preface disclaimer
+ app_info = await self.bot.application_info()
+ if app_info.team:
+ owner = app_info.team.name
+ else:
+ owner = app_info.owner
+ desc = (
+ f"This command lets the bot perform Hoyolab account actions on your behalf, authenticated using your token. "
+ f"The token will then be linked to your Discord ID and stored encrypted in the bot's config, along with the encryption key. "
+ f"Make sure **you fully acknowledge the risks of sharing your account token online** before proceeding.\n\n"
+ f"For security reason, please run this command in a DM channel when setting token.\n\n"
+ f"Read on how to obtain your token [here](https://project-mei.xyz/genshinutils)."
+ )
+ e = Embed(
+ color=(await ctx.embed_colour()),
+ title="Important Disclaimer",
+ description=desc,
+ )
+ if app_info.bot_public:
+ public = "Can be invited by anyone."
+ else:
+ public = "Can only be invited by the owner."
+ e.add_field(name="Bot Owner", value=owner)
+ e.add_field(name="Invite Link Privacy", value=public)
+ if ctx.me.avatar_url:
+ e.set_thumbnail(url=ctx.me.avatar_url)
+ e.set_footer(text=f"Command invoked by {ctx.author}.")
+ return await ctx.send(embed=e)
+
+ if not cookie:
+ msg = (
+ f"**Provide a valid cookie to bind your Discord account to.**\n\n"
+ f"` » ` Instruction on how to obtain your Hoyolab cookie:\n\n\n"
+ f"` » ` For command help context: `{escape(ctx.prefix)}help genshin register {escape(ctx.command.name)}`\n\n"
+ f"` » ` To read disclaimers, this command again in any server."
+ )
+ return await ctx.send(msg)
+
+ # Captures 2 groups: "ltuid=" and "abcd1234"
+ re_uid = re.search(r"(ltuid=)([^;]*)", cookie)
+ re_ltoken = re.search(r"(ltoken=)([^;]*)", cookie)
+
+ if not re_uid:
+ return await ctx.send("Not a valid `ltuid`.")
+ if not re_ltoken:
+ return await ctx.send("Not a valid `ltoken`.")
+
+ ltuid = re_uid.group(2)
+ ltoken = re_ltoken.group(2)
+
+ # Verify if cookie is valid
+ async with ctx.typing():
+ try:
+ cookies = {"ltuid": ltuid, "ltoken": ltoken}
+ client = genshin.Client(cookies)
+ accounts = await client.get_game_accounts()
+ except Exception as exc:
+ return await ctx.send(f"Unable to retrieve data from Hoyolab API:\n`{exc}`")
+ """
+ Accounts: [ GenshinAccount(lang="", game_biz="", level=int...), GenshinAccount(...) ]
+ Recognized game_biz:
+ bh3_global: Honkai Impact 3 Global
+ hk4e_global: Genshin Impact
+ """
+
+ # Filter Genshin accounts only
+ genshin_acc_list = []
+ for account in accounts:
+ if account.game_biz == "hk4e_global":
+ genshin_acc_list.append(account)
+
+ if not genshin_acc_list:
+ return await ctx.send(
+ "Couldn't find a linked Genshin UID in your Hoyolab account."
+ )
+
+ # https://www.geeksforgeeks.org/python-get-the-object-with-the-max-attribute-value-in-a-list-of-objects/
+ # get genshin account with the highest level
+ highest_level_acc = max(genshin_acc_list, key=attrgetter("level"))
+ uid = highest_level_acc.uid
+
+ # Save cookie in config
+ encoded_ltuid = await encrypt_config(self.config, ltuid)
+ encoded_ltoken = await encrypt_config(self.config, ltoken)
+ await self.config.user(ctx.author).UID.set(uid)
+ await self.config.user(ctx.author).ltuid.set(encoded_ltuid)
+ await self.config.user(ctx.author).ltoken.set(encoded_ltoken)
+
+ # Send success embed
+ desc = f"Successfully bound a Genshin Impact account to your Discord account. Details are as follow."
+ e = Embed(
+ color=(await ctx.embed_colour()),
+ title="Account Binding Success",
+ description=desc,
+ )
+ e.add_field(name="UID", value=highest_level_acc.uid)
+ e.add_field(name="Nickname", value=highest_level_acc.nickname)
+ e.add_field(name="Server", value=highest_level_acc.server_name)
+ e.add_field(name="AR Level", value=highest_level_acc.level)
+ e.add_field(name="Language", value=highest_level_acc.lang)
+ e.set_thumbnail(url=ctx.message.author.avatar_url)
+
+ return await ctx.send(embed=e)
+
+ # Debugging stuff
+ log.debug(
+ f"[Register Hoyolab] Encrypted ltuid saved: {await self.config.user(ctx.author).ltuid()}"
+ )
+ log.debug(
+ f"[Register Hoyolab] Encrypted ltoken saved: {await self.config.user(ctx.author).ltoken()}"
+ )
+ log.debug(
+ f"[Register Hoyolab] Encryption key saved: {await self.config.encryption_key()}"
+ )
+
+ decoded_ltuid = await decrypt_config(self.config, encoded_ltuid)
+ decoded_ltoken = await decrypt_config(self.config, encoded_ltoken)
+
+ log.debug(f"[Register Hoyolab] Decoded ltuid: {decoded_ltuid}")
+ log.debug(f"[Register Hoyolab] Decoded ltoken: {decoded_ltoken}")
diff --git a/genshinutils/settings.py b/genshinutils/settings.py
index 420e615..96901e2 100644
--- a/genshinutils/settings.py
+++ b/genshinutils/settings.py
@@ -1,7 +1,6 @@
import logging
from redbot.core import checks, commands
-from redbot.core.commands import Context
log = logging.getLogger("red.raidensakura.genshinutils")
@@ -10,74 +9,28 @@ class GenshinSet(commands.Cog):
"""GenshinUtils genshinset command class."""
@commands.group()
- async def genshinset(self, ctx):
- """Various settings for GenshinUtils cog."""
+ async def genshinset(self, ctx: commands.Context):
+ """Various global settings for GenshinUtils cog."""
@checks.is_owner()
@genshinset.command()
async def ltoken(self, ctx: commands.Context):
- """(Unused) Instructions on how to set the `ltoken` secret."""
+ """Instructions on how to set global `ltoken` secret."""
await ctx.send(f"Use `{ctx.prefix}set api hoyolab ltoken your_ltoken_here`.")
@checks.is_owner()
@genshinset.command()
async def ltuid(self, ctx: commands.Context):
- """(Unused) Instructions on how to set the `ltuid` secret."""
+ """Instructions on how to set global `ltuid` secret."""
await ctx.send(f"Use `{ctx.prefix}set api hoyolab ltuid your_ltuid_here`.")
@checks.is_owner()
@genshinset.command()
async def verification(self, ctx: commands.Context, toggle: bool):
- """Globally enable or disable UID verification for GenshinUtils cog."""
+ """
+ Globally enable or disable UID verification for GenshinUtils cog.
+ Only applicable for account-linking via signature check.
+ """
await self.config.verification.set(toggle)
status = "enabled" if toggle else "disabled"
return await ctx.send(f"Global UID verification has been {status}.")
-
- @genshinset.command(name="uid", usage="")
- @commands.guild_only()
- @commands.cooldown(2, 5, commands.BucketType.member)
- async def set_uid(self, ctx: commands.Context, uid_or_remove: str):
- """
- Link or unlink a Genshin Impact UID to your Discord account.
- If verification is enabled, you need to add your Discord tag to your in-game signature.
- It can take up to 15 minutes for your signature to be refreshed.
- """
-
- async def verification_enabled():
- enabled = (
- await self.config.verification()
- # or await self.config.guild.verification()
- )
- return enabled
-
- def pass_verification(discordtag, signature):
- if discordtag == signature:
- return True
-
- if uid_or_remove.lower() == "remove" or uid_or_remove.lower() == "unlink":
- await self.config.user(ctx.author).UID.clear()
- return await ctx.send(f"Successfully removed UID for {ctx.author.name}.")
-
- uid = uid_or_remove
-
- if not len(uid) == 9 or not uid.isdigit():
- return await ctx.send("Invalid UID provided, it must consist of 9 digits.")
-
- try:
- with ctx.typing():
- data = await self.enka_client.fetch_user(uid)
- except Exception as exc:
- return await ctx.send(
- f"Unable to retrieve data from enka.network:\n`{exc}`"
- )
-
- author_discord_id = f"{ctx.author.name}#{ctx.author.discriminator}"
-
- if await verification_enabled() and not pass_verification(
- author_discord_id, data.player.signature
- ):
- return await ctx.send(
- f"Your signature does not contain your Discord tag.\nNote that it may take up to 15 minutes for changes to be reflected."
- )
- await self.config.user(ctx.author).UID.set(uid)
- return await ctx.send(f"Successfully set UID for {ctx.author.name} to {uid}.")
diff --git a/genshinutils/utils.py b/genshinutils/utils.py
new file mode 100644
index 0000000..74c5c12
--- /dev/null
+++ b/genshinutils/utils.py
@@ -0,0 +1,80 @@
+from __future__ import annotations
+
+import logging
+
+import discord
+from aioenkanetworkcard import encbanner
+from cryptography.fernet import Fernet
+
+from .constants import common_names
+
+log = logging.getLogger("red.raidensakura.genshinutils")
+
+
+def bytes_to_string(bytes):
+ str = bytes.decode()
+ return str
+
+
+def string_to_bytes(str):
+ bytes = str.encode()
+ return bytes
+
+
+# https://stackoverflow.com/questions/44432945/generating-own-key-with-python-fernet
+async def get_encryption_key(config):
+ key = string_to_bytes(await config.encryption_key())
+ if not key or key is None:
+ key = Fernet.generate_key()
+ await config.encryption_key.set(bytes_to_string(key))
+ return key
+
+
+async def decrypt_config(config, encoded):
+ to_decode = string_to_bytes(encoded)
+ cipher_suite = Fernet(await get_encryption_key(config))
+ decoded_bytes = cipher_suite.decrypt(to_decode)
+ decoded = bytes_to_string(decoded_bytes)
+ return decoded
+
+
+async def encrypt_config(config, decoded):
+ to_encode = string_to_bytes(decoded)
+ cipher_suite = Fernet(await get_encryption_key(config))
+ encoded_bytes = cipher_suite.encrypt(to_encode)
+ encoded = bytes_to_string(encoded_bytes)
+ return encoded
+
+
+async def validate_uid(u, self):
+ if isinstance(u, discord.Member):
+ uid = await self.config.user(u).get_raw("UID")
+ if uid:
+ exist = "exist"
+ else:
+ exist = "does not exist"
+ log.debug(f"[validate_uid] UID {exist} in config.")
+
+ elif isinstance(u, str) and len(u) == 9 and u.isdigit():
+ uid = u
+ log.debug(f"[validate_uid] This is a valid UID.")
+
+ else:
+ uid = None
+ log.debug(f"[validate_uid] This is not a valid UID.")
+
+ return uid
+
+
+def validate_char_name(arg):
+ formal_name = {i for i in common_names if arg in common_names[i]}
+ if formal_name:
+ return str(formal_name).strip("{'\"}")
+
+
+async def get_character_card(uid, char_name):
+ async with encbanner.ENC(
+ lang="en", splashArt=True, characterName=char_name
+ ) as encard:
+ ENCpy = await encard.enc(uids=uid)
+ return await encard.creat(ENCpy, 2)
From 46ee2e704269f2aecae97a672697bdac4df9212a Mon Sep 17 00:00:00 2001
From: Raiden
Date: Sat, 28 Jan 2023 22:21:41 +0800
Subject: [PATCH 06/51] Update README.md
---
genshinutils/README.md | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/genshinutils/README.md b/genshinutils/README.md
index 6691559..3be701e 100644
--- a/genshinutils/README.md
+++ b/genshinutils/README.md
@@ -1,5 +1,5 @@
-Genshin
+GenshinUtils
-Genshin - A Genshin Impact oriented cog.
-
+
GenshinUtils - Multipurpose Genshin Impact cog. For now, it's able to display in-game profile information and featured character build cards.
+
@@ -20,3 +20,11 @@
[p]repo add raiden-cogs https://github.com/raidensakura/raiden-cogs/
[p]cog install raiden-cogs genshin
```
+
+Obtaining Account Cookie (Hoyolab)
+
+ - Go to Hoyolab and log into your account.
+ - Press
F12
to open Developer Tools and click on Console
tab.
+ - In the terminal, next to a right arrow
>
type in document.cookie
and copy the output.
+ - Paste the cookie next to the registration command for the bot, example:
?genshin register hoyolab your_cookie
Make sure to replace ?
in the command with your bot prefix.
+
From 9d5d659baa41f301801ab9dd973975cf77b11618 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Sat, 28 Jan 2023 22:30:40 +0800
Subject: [PATCH 07/51] Update README.md
---
README.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/README.md b/README.md
index b0b4df9..792a550 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,11 @@
choose |
A cog to replace Red Bot's General cog choose command to something more intuitive. |
+
+
+ genshinutils |
+ Multipurpose Genshin Impact oriented cog. |
+
From ab53fc001f24d1f959aeb5c75dcd3c8571f65642 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Sat, 28 Jan 2023 22:31:28 +0800
Subject: [PATCH 08/51] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 792a550..cfb9a90 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@
genshinutils |
- Multipurpose Genshin Impact oriented cog. |
+ Multipurpose Genshin Impact oriented cog. Able to retrieve game profile data, character data and many more. |
From f5a94f09939ba9beeeb5f76db4d00b289b91a51b Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 01:46:51 +0800
Subject: [PATCH 09/51] Too many stuff again
---
genshinutils/assets/global/cog_icon.png | Bin 0 -> 9820 bytes
genshinutils/constants.py | 19 +++-
genshinutils/genshinutils.py | 6 +-
genshinutils/info.json | 2 +-
genshinutils/notes.py | 116 ++++++++++++++++++++++++
genshinutils/profile.py | 100 ++++++++++++++++----
genshinutils/register.py | 4 +-
genshinutils/utils.py | 67 +++++++++++++-
8 files changed, 283 insertions(+), 31 deletions(-)
create mode 100644 genshinutils/assets/global/cog_icon.png
create mode 100644 genshinutils/notes.py
diff --git a/genshinutils/assets/global/cog_icon.png b/genshinutils/assets/global/cog_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..d7487c0e28a8483338186cd0aa5c130aca3948b6
GIT binary patch
literal 9820
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
z
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",
+ )
+ if data.expeditions:
+ e.add_field(
+ name="🚩 Expedition Status",
+ value=f"You can deploy a maximum of **{data.max_expeditions} characters**.",
+ inline=False,
+ )
+ for expedition in data.expeditions:
+ e.add_field(
+ name=f"{expedition.character.name} ({expedition.status}) ",
+ value=f"Time left: **{expedition.remaining_time}**",
+ )
+
+ return await ctx.send(embed=e)
+
+ async def test_honkai():
+ try:
+ client = genshin.Client(cookie)
+ data = await client.get_full_honkai_user(20177789)
+ except Exception as exc:
+ return await ctx.send(
+ f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
+ )
+ return await log.debug(f"```{data}```")
+
+ uid = await validate_uid(ctx.author, self.config)
+ if not uid:
+ return await ctx.send("You do not have a UID linked.")
+
+ cookie = await get_user_cookie(self.config, ctx.author)
+ if not cookie:
+ return await ctx.send("No cookie.")
+
+ with ctx.typing():
+ # return await test_honkai()
+ return await generate_diary(uid)
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
index a133b9e..24e27d2 100644
--- a/genshinutils/profile.py
+++ b/genshinutils/profile.py
@@ -3,10 +3,19 @@
import time
from typing import Union
+import genshin
+
import discord
from redbot.core import checks, commands
+from .constants import character_namecards
+from .utils import generate_embed
-from .utils import get_character_card, validate_char_name, validate_uid
+from .utils import (
+ enka_get_character_card,
+ validate_char_name,
+ validate_uid,
+ get_user_cookie,
+)
log = logging.getLogger("red.raidensakura.genshinutils")
@@ -37,7 +46,7 @@ async def profile(
If a character name is provided, it will display character infographic instead.
"""
- async def generate_profile(uid):
+ async def enka_generate_profile(uid):
try:
data = await self.enka_client.fetch_user(uid)
except Exception as exc:
@@ -45,13 +54,7 @@ async def generate_profile(uid):
f"Unable to retrieve data from enka.network:\n`{exc}`"
)
- e = discord.Embed(
- color=(await ctx.embed_colour()),
- description=(
- f"```fix\n"
- f"✨ :: Profile for {data.player.nickname} [AR {data.player.level}]```\n"
- ),
- )
+ e = generate_embed(f"Profile for {data.player.nickname} [AR {data.player.level}]", await ctx.embed_color())
if data.player.characters_preview:
char_str = ""
for character in data.player.characters_preview:
@@ -71,14 +74,14 @@ async def generate_profile(uid):
e.add_field(name="Achievements", value=data.player.achievement)
e.add_field(
name="Current Spiral Abyss Floor",
- value=f"{data.player.abyss_floor} - {data.player.abyss_room}",
+ value=f"{data.player.abyss_floor}-{data.player.abyss_room}",
)
return await ctx.send(embed=e)
- async def generate_char_info(uid, char_name):
+ async def enka_generate_char_img(uid, char_name):
with io.BytesIO() as image_binary:
- char_card = await get_character_card(uid, char_name)
+ char_card = await enka_get_character_card(uid, char_name)
if not char_card:
return await ctx.send(
"This user does not have that character featured."
@@ -95,17 +98,74 @@ async def generate_char_info(uid, char_name):
file=discord.File(fp=image_binary, filename=temp_filename)
)
+ async def genshin_generate_profile(uid):
+ try:
+ client = genshin.Client(cookie)
+ data = await client.get_partial_genshin_user(uid)
+ except Exception as exc:
+ return await ctx.send(
+ f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
+ )
+
+ e = generate_embed(f"Profile for {data.info.nickname} [AR {data.info.level}]", await ctx.embed_color())
+ if data.characters:
+ e.set_thumbnail(url=data.characters[0].icon)
+ if character_namecards[data.characters[0].name.title()]:
+ namecard_url = character_namecards[data.characters[0].name.title()]
+ e.set_image(url=namecard_url)
+ e.add_field(name="Achievements", value=data.stats.achievements, inline=True)
+ e.add_field(name="Days Active", value=data.stats.days_active, inline=True)
+ e.add_field(
+ name="Characters Unlocked", value=data.stats.characters, inline=True
+ )
+ e.add_field(
+ name="Current Spiral Abyss Floor",
+ value=data.stats.spiral_abyss,
+ inline=True,
+ )
+ e.add_field(
+ name="Total Oculi Collected",
+ value=(
+ f"{data.stats.anemoculi + data.stats.geoculi + data.stats.electroculi + data.stats.dendroculi}"
+ ),
+ inline=True,
+ )
+ e.add_field(
+ name="Waypoints Unlocked",
+ value=(f"{data.stats.unlocked_waypoints}"),
+ inline=True,
+ )
+ e.add_field(
+ name="Total Chests Opened",
+ value=(
+ f"{data.stats.common_chests + data.stats.precious_chests + data.stats.exquisite_chests + data.stats.luxurious_chests + data.stats.remarkable_chests}"
+ ),
+ inline=True,
+ )
+ e.add_field(
+ name="Domains Unlocked",
+ value=(f"{data.stats.unlocked_domains}"),
+ inline=True,
+ )
+
+ return await ctx.send(embed=e)
+
log.debug(f"[Args] user_or_uid: {user_or_uid}")
log.debug(f"[Args] character: {character}")
"""If nothing is passed at all, we assume user is trying to generate their own profile"""
if not user_or_uid and not character:
- uid = await validate_uid(ctx.author, self)
+ uid = await validate_uid(ctx.author, self.config)
if not uid:
return await ctx.send("You do not have a UID linked.")
+ cookie = await get_user_cookie(self.config, ctx.author)
+
with ctx.typing():
- return await generate_profile(uid)
+ if not cookie:
+ return await enka_generate_profile(uid)
+
+ return await genshin_generate_profile(uid)
"""
Since both args are optional: [user_or_uid] [character]
@@ -113,10 +173,10 @@ async def generate_char_info(uid, char_name):
We check and handle it appropriately
"""
if user_or_uid and not character:
- uid = await validate_uid(user_or_uid, self)
+ uid = await validate_uid(user_or_uid, self.config)
if uid:
with ctx.typing():
- return await generate_profile(uid)
+ return await enka_generate_profile(uid)
log.debug(
f"[{ctx.command.name}] Not a UID, assuming it's a character name..."
@@ -130,16 +190,16 @@ async def generate_char_info(uid, char_name):
log.debug(
f"[{ctx.command.name}] Valid character name found, trying to fetch author UID..."
)
- uid = await validate_uid(ctx.author, self)
+ uid = await validate_uid(ctx.author, self.config)
if not uid:
return await ctx.send("You do not have a UID linked.")
with ctx.typing():
- return await generate_char_info(uid, char)
+ return await enka_generate_char_img(uid, char)
"""This handles if both [user_or_uid] and [character] are appropriately passed"""
if user_or_uid and character:
- uid = await validate_uid(user_or_uid, self)
+ uid = await validate_uid(user_or_uid, self.config)
if not uid:
return await ctx.send(
"Not a valid UID or user does not have a UID linked."
@@ -150,4 +210,4 @@ async def generate_char_info(uid, char_name):
return await ctx.send("Character name invalid or not in dictionary.")
with ctx.typing():
- return await generate_char_info(uid, char)
+ return await enka_generate_char_img(uid, char)
diff --git a/genshinutils/register.py b/genshinutils/register.py
index e0360a7..7904c31 100644
--- a/genshinutils/register.py
+++ b/genshinutils/register.py
@@ -153,7 +153,9 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
client = genshin.Client(cookies)
accounts = await client.get_game_accounts()
except Exception as exc:
- return await ctx.send(f"Unable to retrieve data from Hoyolab API:\n`{exc}`")
+ return await ctx.send(
+ f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
+ )
"""
Accounts: [ GenshinAccount(lang="", game_biz="", level=int...), GenshinAccount(...) ]
Recognized game_biz:
diff --git a/genshinutils/utils.py b/genshinutils/utils.py
index 74c5c12..a1d323d 100644
--- a/genshinutils/utils.py
+++ b/genshinutils/utils.py
@@ -10,17 +10,22 @@
log = logging.getLogger("red.raidensakura.genshinutils")
-
+# Used internally
def bytes_to_string(bytes):
str = bytes.decode()
return str
+# Used internally
def string_to_bytes(str):
bytes = str.encode()
return bytes
+"""
+Accepts: config
+Returns: str(encryption_key) or None
+"""
# https://stackoverflow.com/questions/44432945/generating-own-key-with-python-fernet
async def get_encryption_key(config):
key = string_to_bytes(await config.encryption_key())
@@ -30,6 +35,11 @@ async def get_encryption_key(config):
return key
+"""
+Accepts: config, str(encoded)
+Returns: str(decoded)
+"""
+# decrypt config
async def decrypt_config(config, encoded):
to_decode = string_to_bytes(encoded)
cipher_suite = Fernet(await get_encryption_key(config))
@@ -38,6 +48,11 @@ async def decrypt_config(config, encoded):
return decoded
+"""
+Accepts: config, str(decoded)
+Returns: str(encoded)
+"""
+# encrypt config
async def encrypt_config(config, decoded):
to_encode = string_to_bytes(decoded)
cipher_suite = Fernet(await get_encryption_key(config))
@@ -46,9 +61,14 @@ async def encrypt_config(config, decoded):
return encoded
-async def validate_uid(u, self):
+"""
+Accepts: ( str(uid) | discord.Member ), self.config
+Returns: str(uid) or None
+"""
+# validate uid
+async def validate_uid(u, config):
if isinstance(u, discord.Member):
- uid = await self.config.user(u).get_raw("UID")
+ uid = await config.user(u).UID()
if uid:
exist = "exist"
else:
@@ -66,15 +86,54 @@ async def validate_uid(u, self):
return uid
+"""
+Accepts: str(name_query)
+Returns: str(formal_name) or None
+"""
+# validate_char_name
def validate_char_name(arg):
formal_name = {i for i in common_names if arg in common_names[i]}
if formal_name:
return str(formal_name).strip("{'\"}")
-async def get_character_card(uid, char_name):
+"""
+Accepts: str(uid), formal_name
+Returns: { UID: { Character: } }
+"""
+# enka_get_character_card
+async def enka_get_character_card(uid, char_name):
async with encbanner.ENC(
lang="en", splashArt=True, characterName=char_name
) as encard:
ENCpy = await encard.enc(uids=uid)
return await encard.creat(ENCpy, 2)
+
+
+"""
+Accepts: config, discord.Member
+Returns:
+"""
+# get_user_cookie
+async def get_user_cookie(config, user):
+ ltuid_config = await config.user(user).ltuid()
+ ltoken_config = await config.user(user).ltoken()
+
+ if ltuid_config and ltoken_config:
+ ltuid = await decrypt_config(config, ltuid_config)
+ ltoken = await decrypt_config(config, ltoken_config)
+ cookie = {"ltuid": ltuid, "ltoken": ltoken}
+
+ return cookie
+
+
+"""
+Accepts: str(title), str(author), color
+Returns: discord.Embed
+"""
+# generate_embed
+def generate_embed(title, color):
+ cog_url = "https://project-mei.xyz/genshinutils"
+ e = discord.Embed(title=title, color=color, url=cog_url)
+ e.set_footer(text="genshinutils cog by raidensakura", icon_url="https://avatars.githubusercontent.com/u/120461773?s=64&v=4")
+ return e
From eeeec877f989782788ebbc91efbf15e6c632d4da Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 01:47:43 +0800
Subject: [PATCH 10/51] Cleanup
---
genshinutils/genshinutils.py | 2 +-
genshinutils/notes.py | 7 ++-----
genshinutils/profile.py | 24 ++++++++++++------------
genshinutils/register.py | 2 +-
genshinutils/utils.py | 7 ++++++-
5 files changed, 22 insertions(+), 20 deletions(-)
diff --git a/genshinutils/genshinutils.py b/genshinutils/genshinutils.py
index 1a11026..339a852 100644
--- a/genshinutils/genshinutils.py
+++ b/genshinutils/genshinutils.py
@@ -4,10 +4,10 @@
from enkanetwork import EnkaNetworkAPI
from redbot.core import Config, checks, commands
+from .notes import GenshinNotes
from .profile import GenshinProfile
from .register import GenshinRegister
from .settings import GenshinSet
-from .notes import GenshinNotes
enka_client = EnkaNetworkAPI()
diff --git a/genshinutils/notes.py b/genshinutils/notes.py
index 7f97fca..88b096b 100644
--- a/genshinutils/notes.py
+++ b/genshinutils/notes.py
@@ -1,13 +1,10 @@
import logging
-from typing import Union
+from time import mktime
import genshin
-
-import discord
-from time import mktime
from redbot.core import checks, commands
-from .utils import validate_uid, get_user_cookie, generate_embed
+from .utils import generate_embed, get_user_cookie, validate_uid
log = logging.getLogger("red.raidensakura.genshinutils")
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
index 24e27d2..f9ca086 100644
--- a/genshinutils/profile.py
+++ b/genshinutils/profile.py
@@ -3,19 +3,13 @@
import time
from typing import Union
-import genshin
-
import discord
+import genshin
from redbot.core import checks, commands
-from .constants import character_namecards
-from .utils import generate_embed
-from .utils import (
- enka_get_character_card,
- validate_char_name,
- validate_uid,
- get_user_cookie,
-)
+from .constants import character_namecards
+from .utils import (enka_get_character_card, generate_embed, get_user_cookie,
+ validate_char_name, validate_uid)
log = logging.getLogger("red.raidensakura.genshinutils")
@@ -54,7 +48,10 @@ async def enka_generate_profile(uid):
f"Unable to retrieve data from enka.network:\n`{exc}`"
)
- e = generate_embed(f"Profile for {data.player.nickname} [AR {data.player.level}]", await ctx.embed_color())
+ e = generate_embed(
+ f"Profile for {data.player.nickname} [AR {data.player.level}]",
+ await ctx.embed_color(),
+ )
if data.player.characters_preview:
char_str = ""
for character in data.player.characters_preview:
@@ -107,7 +104,10 @@ async def genshin_generate_profile(uid):
f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
)
- e = generate_embed(f"Profile for {data.info.nickname} [AR {data.info.level}]", await ctx.embed_color())
+ e = generate_embed(
+ f"Profile for {data.info.nickname} [AR {data.info.level}]",
+ await ctx.embed_color(),
+ )
if data.characters:
e.set_thumbnail(url=data.characters[0].icon)
if character_namecards[data.characters[0].name.title()]:
diff --git a/genshinutils/register.py b/genshinutils/register.py
index 7904c31..93eaaf4 100644
--- a/genshinutils/register.py
+++ b/genshinutils/register.py
@@ -67,7 +67,7 @@ def pass_verification(discordtag, signature):
author_discord_id, data.player.signature
):
return await ctx.send(
- f"Your signature does not contain your Discord tag.\nNote that it may take up to 15 minutes for changes to be reflected."
+ "Your signature does not contain your Discord tag.\nNote that it may take up to 15 minutes for changes to be reflected."
)
await self.config.user(ctx.author).UID.set(uid)
return await ctx.send(f"Successfully set UID for {ctx.author.name} to {uid}.")
diff --git a/genshinutils/utils.py b/genshinutils/utils.py
index a1d323d..2b1d124 100644
--- a/genshinutils/utils.py
+++ b/genshinutils/utils.py
@@ -10,12 +10,14 @@
log = logging.getLogger("red.raidensakura.genshinutils")
+"""I make it a function so it's more readable"""
# Used internally
def bytes_to_string(bytes):
str = bytes.decode()
return str
+"""I make it a function so it's more readable"""
# Used internally
def string_to_bytes(str):
bytes = str.encode()
@@ -135,5 +137,8 @@ async def get_user_cookie(config, user):
def generate_embed(title, color):
cog_url = "https://project-mei.xyz/genshinutils"
e = discord.Embed(title=title, color=color, url=cog_url)
- e.set_footer(text="genshinutils cog by raidensakura", icon_url="https://avatars.githubusercontent.com/u/120461773?s=64&v=4")
+ e.set_footer(
+ text="genshinutils cog by raidensakura",
+ icon_url="https://avatars.githubusercontent.com/u/120461773?s=64&v=4",
+ )
return e
From baf4c96ebbc2a101ffc5e5d32f33511a6bfa3a5a Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 18:01:57 +0800
Subject: [PATCH 11/51] Add daily command and flake8 cleanup
---
choose/__init__.py | 2 +-
choose/choose.py | 7 +-
genshinutils/assets/global/login_check.png | Bin 0 -> 6664 bytes
genshinutils/daily.py | 71 +++++++++++++++++++++
genshinutils/genshinutils.py | 10 ++-
genshinutils/notes.py | 5 +-
genshinutils/profile.py | 10 +--
genshinutils/register.py | 16 ++---
genshinutils/utils.py | 10 +--
throw/throw.py | 6 +-
10 files changed, 109 insertions(+), 28 deletions(-)
create mode 100644 genshinutils/assets/global/login_check.png
create mode 100644 genshinutils/daily.py
diff --git a/choose/__init__.py b/choose/__init__.py
index f2b247d..ae5db39 100644
--- a/choose/__init__.py
+++ b/choose/__init__.py
@@ -1,7 +1,7 @@
import json
from pathlib import Path
-from .choose import setup
+from .choose import setup # noqa: F401
with open(Path(__file__).parent / "info.json") as fp:
__red_end_user_data_statement__ = json.load(fp)["end_user_data_statement"]
diff --git a/choose/choose.py b/choose/choose.py
index 9e5e256..b7825d4 100644
--- a/choose/choose.py
+++ b/choose/choose.py
@@ -26,7 +26,7 @@ def cog_unload(self):
if old_choose:
try:
self.bot.remove_command("choose")
- except:
+ except Exception:
pass
self.bot.add_command(old_choose)
@@ -36,7 +36,10 @@ def format_help_for_context(self, ctx: commands.Context) -> str:
"""
pre_processed = super().format_help_for_context(ctx)
s = "s" if len(self.__author__) > 1 else ""
- return f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}\nCog Version: {self.__version__}"
+ return (
+ f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}\n"
+ f"Cog Version: {self.__version__}"
+ )
async def red_delete_data_for_user(self, **kwargs) -> None:
"""Nothing to delete"""
diff --git a/genshinutils/assets/global/login_check.png b/genshinutils/assets/global/login_check.png
new file mode 100644
index 0000000000000000000000000000000000000000..73f6cb89115e3f2fe2d997020fba5cf149b8d294
GIT binary patch
literal 6664
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
literal 0
HcmV?d00001
diff --git a/genshinutils/daily.py b/genshinutils/daily.py
new file mode 100644
index 0000000..4f42ff8
--- /dev/null
+++ b/genshinutils/daily.py
@@ -0,0 +1,71 @@
+import logging
+
+import genshin
+from redbot.core import commands
+
+from .utils import generate_embed, get_user_cookie, validate_uid
+
+log = logging.getLogger("red.raidensakura.genshinutils")
+
+
+class GenshinDaily(commands.Cog):
+ """GenshinUtils daily command class."""
+
+ # https://discord.com/channels/133049272517001216/160386989819035648/1067445744497348639
+ # This will get replaced by genshinutils.py's `genshin`
+ # Thanks Jojo#7791!
+ @commands.group()
+ async def genshin(self, ctx: commands.Context):
+ """GenshinUtils main command."""
+
+ @genshin.command()
+ @commands.guild_only()
+ @commands.bot_has_permissions(embed_links=True)
+ @commands.cooldown(2, 10, commands.BucketType.member)
+ async def daily(self, ctx: commands.Context):
+ """
+ Redeem your daily login reward from Hoyolab for Genshin Impact.
+ Command require cookie registration.
+ """
+
+ async def redeem_daily():
+ try:
+ client = genshin.Client(cookie)
+ client.default_game = genshin.Game.GENSHIN
+ signed_in, claimed_rewards = await client.get_reward_info()
+ reward = await client.claim_daily_reward()
+ except genshin.AlreadyClaimed:
+ e = generate_embed(
+ title="Genshin Impact Daily Login",
+ desc="Daily reward already claimed.",
+ color=await ctx.embed_color(),
+ )
+ e.add_field(name="Total Login", value=f"{claimed_rewards} days")
+ except Exception as exc:
+ return await ctx.send(
+ f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
+ )
+ else:
+ signed_in = "✅"
+ e = generate_embed(
+ title="Genshin Impact Daily Login",
+ desc="Daily reward successfully claimed.",
+ color=await ctx.embed_color(),
+ )
+ e.set_thumbnail(reward.icon)
+ e.add_field(name="Reward", value=f"{reward.name} x{reward.amount}")
+ e.add_field(
+ name="Total Login", value=f"{signed_in} {claimed_rewards + 1} days"
+ )
+ return await ctx.send(embed=e)
+
+ uid = await validate_uid(ctx.author, self.config)
+ if not uid:
+ return await ctx.send("You do not have a UID linked.")
+
+ cookie = await get_user_cookie(self.config, ctx.author)
+ if not cookie:
+ return await ctx.send("No cookie.")
+
+ with ctx.typing():
+ return await redeem_daily()
diff --git a/genshinutils/genshinutils.py b/genshinutils/genshinutils.py
index 339a852..109d3a8 100644
--- a/genshinutils/genshinutils.py
+++ b/genshinutils/genshinutils.py
@@ -2,8 +2,9 @@
from typing import Literal
from enkanetwork import EnkaNetworkAPI
-from redbot.core import Config, checks, commands
+from redbot.core import Config, commands
+from .daily import GenshinDaily
from .notes import GenshinNotes
from .profile import GenshinProfile
from .register import GenshinRegister
@@ -15,7 +16,12 @@
class GenshinUtils(
- GenshinSet, GenshinRegister, GenshinProfile, GenshinNotes, commands.Cog
+ GenshinSet,
+ GenshinRegister,
+ GenshinProfile,
+ GenshinNotes,
+ GenshinDaily,
+ commands.Cog,
):
"""GenshinUtils commands."""
diff --git a/genshinutils/notes.py b/genshinutils/notes.py
index 88b096b..ee5295e 100644
--- a/genshinutils/notes.py
+++ b/genshinutils/notes.py
@@ -2,7 +2,7 @@
from time import mktime
import genshin
-from redbot.core import checks, commands
+from redbot.core import commands
from .utils import generate_embed, get_user_cookie, validate_uid
@@ -38,7 +38,8 @@ async def generate_diary(uid):
f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
)
e = generate_embed(
- f"Game Notes for {ctx.author.display_name}", await ctx.embed_color()
+ title=f"Game Notes for {ctx.author.display_name}",
+ color=await ctx.embed_color(),
)
e.add_field(
name="🌙 Resin",
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
index f9ca086..42e5ac8 100644
--- a/genshinutils/profile.py
+++ b/genshinutils/profile.py
@@ -5,7 +5,7 @@
import discord
import genshin
-from redbot.core import checks, commands
+from redbot.core import commands
from .constants import character_namecards
from .utils import (enka_get_character_card, generate_embed, get_user_cookie,
@@ -49,8 +49,8 @@ async def enka_generate_profile(uid):
)
e = generate_embed(
- f"Profile for {data.player.nickname} [AR {data.player.level}]",
- await ctx.embed_color(),
+ title=f"Profile for {data.player.nickname} [AR {data.player.level}]",
+ color=await ctx.embed_color(),
)
if data.player.characters_preview:
char_str = ""
@@ -105,8 +105,8 @@ async def genshin_generate_profile(uid):
)
e = generate_embed(
- f"Profile for {data.info.nickname} [AR {data.info.level}]",
- await ctx.embed_color(),
+ title=f"Profile for {data.info.nickname} [AR {data.info.level}]",
+ color=await ctx.embed_color(),
)
if data.characters:
e.set_thumbnail(url=data.characters[0].icon)
diff --git a/genshinutils/register.py b/genshinutils/register.py
index 93eaaf4..813ae8b 100644
--- a/genshinutils/register.py
+++ b/genshinutils/register.py
@@ -6,7 +6,7 @@
import genshin
from discord import Embed
from discord.channel import DMChannel
-from redbot.core import checks, commands
+from redbot.core import commands
from .utils import decrypt_config, encrypt_config
@@ -93,7 +93,7 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
if cookie:
try:
await ctx.message.delete()
- except:
+ except Exception:
pass
# Preface disclaimer
@@ -103,11 +103,11 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
else:
owner = app_info.owner
desc = (
- f"This command lets the bot perform Hoyolab account actions on your behalf, authenticated using your token. "
- f"The token will then be linked to your Discord ID and stored encrypted in the bot's config, along with the encryption key. "
- f"Make sure **you fully acknowledge the risks of sharing your account token online** before proceeding.\n\n"
- f"For security reason, please run this command in a DM channel when setting token.\n\n"
- f"Read on how to obtain your token [here](https://project-mei.xyz/genshinutils)."
+ "This command lets the bot perform Hoyolab account actions on your behalf, authenticated using your token. "
+ "The token will then be linked to your Discord ID and stored encrypted in the bot's config, along with the encryption key. "
+ "Make sure **you fully acknowledge the risks of sharing your account token online** before proceeding.\n\n"
+ "For security reason, please run this command in a DM channel when setting token.\n\n"
+ "Read on how to obtain your token [here](https://project-mei.xyz/genshinutils)."
)
e = Embed(
color=(await ctx.embed_colour()),
@@ -187,7 +187,7 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
await self.config.user(ctx.author).ltoken.set(encoded_ltoken)
# Send success embed
- desc = f"Successfully bound a Genshin Impact account to your Discord account. Details are as follow."
+ desc = "Successfully bound a Genshin Impact account to your Discord account. Details are as follow."
e = Embed(
color=(await ctx.embed_colour()),
title="Account Binding Success",
diff --git a/genshinutils/utils.py b/genshinutils/utils.py
index 2b1d124..0daf89a 100644
--- a/genshinutils/utils.py
+++ b/genshinutils/utils.py
@@ -79,11 +79,11 @@ async def validate_uid(u, config):
elif isinstance(u, str) and len(u) == 9 and u.isdigit():
uid = u
- log.debug(f"[validate_uid] This is a valid UID.")
+ log.debug("[validate_uid] This is a valid UID.")
else:
uid = None
- log.debug(f"[validate_uid] This is not a valid UID.")
+ log.debug("[validate_uid] This is not a valid UID.")
return uid
@@ -130,13 +130,13 @@ async def get_user_cookie(config, user):
"""
-Accepts: str(title), str(author), color
+Accepts: str(title), str(desc), color
Returns: discord.Embed
"""
# generate_embed
-def generate_embed(title, color):
+def generate_embed(title="", desc="", color=""):
cog_url = "https://project-mei.xyz/genshinutils"
- e = discord.Embed(title=title, color=color, url=cog_url)
+ e = discord.Embed(title=title, description=desc, color=color, url=cog_url)
e.set_footer(
text="genshinutils cog by raidensakura",
icon_url="https://avatars.githubusercontent.com/u/120461773?s=64&v=4",
diff --git a/throw/throw.py b/throw/throw.py
index 0a56376..ddecc74 100644
--- a/throw/throw.py
+++ b/throw/throw.py
@@ -4,11 +4,11 @@
from redbot.core import Config, commands
from redbot.core.bot import Red
from redbot.core.commands import Context
-from redbot.core.utils.chat_formatting import bold, box, quote
+from redbot.core.utils.chat_formatting import box
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
from tabulate import tabulate
-from .constants import *
+from .constants import ITEMS, HIT, MISS
class Throw(commands.Cog):
@@ -119,7 +119,7 @@ async def throw_stats(self, ctx: Context, *, member: discord.Member = None):
def parse_actions(data, array, action: str):
for key, value in data.items():
if action in key:
- sent = str(data.get(f"ITEMS_THROWN", " ")).replace("0", " ")
+ sent = str(data.get("ITEMS_THROWN", " ")).replace("0", " ")
received = str(data.get(f"TIMES_HIT", " ")).replace("0", " ")
array.append([action.lower(), received, sent])
From 773dff0b01dde99a3f1aea0fdae681ab3a50b9b0 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 18:20:42 +0800
Subject: [PATCH 12/51] Add workflows
---
.github/workflows/checks.yml | 68 +++++++++++++++++++
.github/workflows/loadcheck.yml | 67 +++++++++++++++++++
.github/workflows/scripts/loadcheck.py | 92 ++++++++++++++++++++++++++
3 files changed, 227 insertions(+)
create mode 100644 .github/workflows/checks.yml
create mode 100644 .github/workflows/loadcheck.yml
create mode 100644 .github/workflows/scripts/loadcheck.py
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
new file mode 100644
index 0000000..fae08f6
--- /dev/null
+++ b/.github/workflows/checks.yml
@@ -0,0 +1,68 @@
+# https://github.com/Vexed01/Vex-Cogs/blob/master/.github/workflows/checks.yml
+name: Checks
+
+on:
+ push:
+ branches:
+ - master
+ - genshin
+ pull_request:
+
+# thanks red or wherever you got it from
+jobs:
+ tox:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python_version:
+ - "3.8"
+ - "3.9"
+ tox_env:
+ - style-black
+ - style-isort
+ - lint-flake8
+ # - type-pyright
+ - docs
+ - pytest
+ include:
+ - tox_env: style-black
+ friendly_name: Style (black)
+ - tox_env: style-isort
+ friendly_name: Style (isort)
+ - tox_env: lint-flake8
+ friendly_name: Lint (flake8)
+ # - tox_env: type-pyright
+ # friendly_name: Type check (pyright)
+ - tox_env: docs
+ friendly_name: Docs
+ - tox_env: pytest
+ friendly_name: Tests
+
+ fail-fast: false
+
+ name: Tox - ${{ matrix.python_version }} - ${{ matrix.friendly_name }}
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ ref: ${{ env.ref }}
+ - name: Set up Python ${{ matrix.python_version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python_version }}
+
+ # caching cuts down time for tox (for example black) from ~40 secs to 4
+ - name: Cache tox
+ uses: actions/cache@v2
+ with:
+ path: .tox
+ key: tox-${{ matrix.python_version }}-${{ matrix.tox_env }}-${{ hashFiles('tox.ini') }}
+
+ - name: Install tox
+ run: |
+ python -m pip install --upgrade pip
+ pip install tox
+ - name: "Run tox: ${{ matrix.friendly_name }}"
+ env:
+ TOXENV: ${{ matrix.tox_env }}
+ run: |
+ tox
\ No newline at end of file
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
new file mode 100644
index 0000000..e642228
--- /dev/null
+++ b/.github/workflows/loadcheck.yml
@@ -0,0 +1,67 @@
+name: "Cog load test"
+
+on:
+ push:
+ branches:
+ - master
+ - genshin
+
+jobs:
+ loadcheck:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python-version: ["3.8", "3.9"]
+ red-version:
+ # this workflow required pr #5453 commit d27dbde, which is in dev & pypi 3.4.15+
+ - "git+https://github.com/Cog-Creators/Red-DiscordBot@V3/develop#egg=Red-DiscordBot"
+ - "Red-DiscordBot==3.4.16"
+ include:
+ - red-version: "git+https://github.com/Cog-Creators/Red-DiscordBot@V3/develop#egg=Red-DiscordBot"
+ friendly-red: "Red (dev version)"
+ - red-version: "Red-DiscordBot==3.4.16"
+ friendly-red: "Red 3.4.16"
+ fail-fast: false
+
+ name: Cog load test - Python ${{ matrix.python-version }} & ${{ matrix.friendly-red }}
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Cache venv
+ id: cache-venv
+ uses: actions/cache@v2
+ with:
+ path: .venv
+ key: ${{ matrix.red-version }}-${{ matrix.python-version }}-${{ hashFiles('dev-requirements.txt') }}-${{ secrets.CACHE_V }}
+
+ - name: Maybe make venv
+ if: steps.cache-venv.outputs.cache-hit != 'true'
+ run: |
+ python3 -m venv .venv
+ source .venv/bin/activate
+ pip install setuptools wheel
+ pip install ${{ matrix.red-version }}
+ pip install -r dev-requirements.txt
+ pip install jsonrpc-websocket
+ - name: Prepare files
+ run: |
+ mkdir -p /home/runner/.config/Red-DiscordBot
+ echo '{"workflow": {"DATA_PATH": "/home/runner/work/Vex-Cogs/Vex-Cogs/.github/red_data", "COG_PATH_APPEND": "cogs", "CORE_PATH_APPEND": "core", "STORAGE_TYPE": "JSON", "STORAGE_DETAILS": {}}}' > /home/runner/.config/Red-DiscordBot/config.json
+ - name: Run script loadcheck.py
+ run: |
+ source .venv/bin/activate
+ python .github/workflows/scripts/loadcheck.py
+ env:
+ DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_TEST_BOT }}
+
+ - name: Save Red output as Artifact
+ if: always() # still run if prev step failed
+ uses: actions/upload-artifact@v2
+ with:
+ name: "Red log - Python ${{ matrix.python-version }} & ${{ matrix.friendly-red }}"
+ path: red.log
\ No newline at end of file
diff --git a/.github/workflows/scripts/loadcheck.py b/.github/workflows/scripts/loadcheck.py
new file mode 100644
index 0000000..6010f51
--- /dev/null
+++ b/.github/workflows/scripts/loadcheck.py
@@ -0,0 +1,92 @@
+# https://github.com/Vexed01/Vex-Cogs/blob/master/.github/workflows/scripts/loadcheck.py
+import asyncio
+import os
+import subprocess
+import sys
+import time
+from typing import Any, Dict, Tuple
+
+from dotenv import load_dotenv
+from jsonrpc_websocket import Server
+from redbot import __version__ as red_str_ver
+
+load_dotenv(".env")
+
+token = os.environ.get("DISCORD_BOT_TOKEN")
+
+python_version = subprocess.check_output(["python", "-V"]).decode("utf-8")
+
+print("=== Red's logs are available to view as an Artifact on the main matrix page ===\n")
+
+print(f"Starting Red {red_str_ver} with {python_version}")
+
+file = open("red.log", "w")
+proc = subprocess.Popen(
+ f"redbot workflow --no-prompt --token {token} --rpc --debug",
+ stdout=file,
+ stderr=subprocess.STDOUT,
+ shell=True,
+)
+
+# let Red boot up
+time.sleep(10)
+
+cogs = [
+ "choose",
+ "genshinutils",
+ "longcat",
+ "throw",
+]
+
+
+async def leswebsockets() -> Tuple[Dict[str, Any], Dict[str, Any]]:
+ print("Connecting to Red via RPC")
+
+ server = Server("ws://localhost:6133")
+ try:
+ await server.ws_connect()
+
+ print("Loading cogs")
+ load_results: Dict[str, Any] = await server.CORE__LOAD(cogs)
+ await asyncio.sleep(1)
+ print("Unloading cogs")
+ unload_results: Dict[str, Any] = await server.CORE__UNLOAD(cogs)
+ finally:
+ await server.close()
+
+ return load_results, unload_results
+
+
+load, unload = asyncio.run(leswebsockets())
+
+print("Stopping Red")
+
+proc.terminate()
+
+exit_code = 0
+
+fail_load = []
+for i in (
+ "failed_packages",
+ "invalid_pkg_names",
+ "notfound_packages",
+ "alreadyloaded_packages",
+ "failed_with_reason_packages",
+):
+ fail_load.extend(load[i])
+
+if fail_load:
+ exit_code = 1
+ print("\N{CROSS MARK} Failed to load cogs " + ", ".join(fail_load))
+ print("See the artifact on the main matrix page for more information")
+else:
+ print("\N{HEAVY CHECK MARK} Loaded all cogs successfully")
+
+if unload["notloaded_packages"]:
+ exit_code = 1
+ print("\N{CROSS MARK} Failed to unload cogs " + ", ".join(unload["notloaded_packages"]))
+ print("See the artifact on the main matrix page for more information")
+else:
+ print("\N{HEAVY CHECK MARK} Unloaded all cogs successfully")
+
+sys.exit(exit_code)
\ No newline at end of file
From 2b6b9d8411e0ae88aeff35529bf6cb5fe7b6fe57 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 18:27:53 +0800
Subject: [PATCH 13/51] More stuff...
---
.flake8 | 5 +++
.pre-commit-config.yaml | 33 +++++++++++++++++
dev-requirements.txt | 42 ++++++++++++++++++++++
pyproject.toml | 14 ++++++++
tox.ini | 80 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 174 insertions(+)
create mode 100644 .flake8
create mode 100644 .pre-commit-config.yaml
create mode 100644 dev-requirements.txt
create mode 100644 pyproject.toml
create mode 100644 tox.ini
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..33d2356
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,5 @@
+[flake8]
+ignore = W503,E203,E402
+per-file-ignores = __init__.py:F401,consts.py:E501
+max-line-length = 99
+exclude = .git,.venv,.tox,__pycache__,docs,vexutils
\ No newline at end of file
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..342b0e2
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,33 @@
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v2.3.0
+ hooks:
+ - id: check-ast
+ - id: check-json
+ - id: pretty-format-json
+ args: ["--autofix", "--indent", "4"]
+ - id: end-of-file-fixer
+ - id: mixed-line-ending
+ - repo: https://github.com/psf/black
+ rev: "22.3.0"
+ hooks:
+ - id: black
+ - repo: https://github.com/PyCQA/isort
+ rev: "5.8.0"
+ hooks:
+ - id: isort
+ - repo: https://github.com/myint/autoflake
+ rev: "v1.4"
+ hooks:
+ - id: autoflake
+ args:
+ [
+ "--in-place",
+ "--remove-unused-variables",
+ "--remove-all-unused-imports",
+ "--ignore-init-module-imports",
+ ]
+ - repo: https://github.com/pycqa/flake8
+ rev: "3.9.2"
+ hooks:
+ - id: flake8
\ No newline at end of file
diff --git a/dev-requirements.txt b/dev-requirements.txt
new file mode 100644
index 0000000..40161a1
--- /dev/null
+++ b/dev-requirements.txt
@@ -0,0 +1,42 @@
+# Red itself is also a requriement, however it is not listed here because
+# some people may have special setups (eg dev version installed)
+
+# Required for utils
+tabulate
+cachetools
+asyncache
+
+# Required for optional PandasSQLDriver in utils
+pandas
+
+# Cogs
+plotly # betteruptime, stattrack, googletrends
+kaleido # betteruptime, stattrack, googletrends
+gidgethub # ghissues, gitub
+pytrends # googletrends
+psutil # stattrack, system
+markdownify # status
+rapidfuzz # timechannel
+wakeonlan # wol
+expr.py # calc
+
+# checking tools
+pyright
+black
+isort
+flake8
+pytest
+tox
+pre-commit
+
+# docs
+sphinx
+sphinx-rtd-theme
+furo
+
+# checks
+python-dotenv
+
+# docs
+sphinx_rtd_theme
+furo
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..b3bb428
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,14 @@
+[tool.black]
+line-length = 99
+target-version = ["py38"]
+extend-exclude = "vexutils|.stubs"
+
+[tool.isort]
+profile = "black"
+line_length = 99
+extend_skip = "vexutils"
+
+[tool.pyright]
+stubPath = "./.stubs"
+exclude = ["*/vexutils", ".tox", "**/__pycache__", ".venv", ".github", ".stubs"]
+useLibraryCodeForTypes = true
\ No newline at end of file
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..75f172d
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,80 @@
+[tox]
+envlist = py38, style-black, style-isort, lint-flake8, type-pyright, docs, pytest
+skipsdist = true
+
+[testenv]
+description = Run style and static type checking.
+deps =
+ # style
+ black
+ isort
+
+ # lint
+ flake8
+ gidgethub
+ wakeonlan
+
+ # non-typeshed stubs
+ pandas-stubs
+
+ tabulate
+ asyncache
+ rapidfuzz
+ plotly
+ pytrends
+ pyjson5
+ expr.py
+
+ # docs
+ sphinx
+ sphinx-rtd-theme==1.0.0
+ furo
+
+ # pytest
+ pytest
+ red-discordbot==3.4.18
+ markdownify
+
+ # type
+ # (some are covered under below)
+ pyright
+ asyncache
+
+[testenv:style-black]
+description = Check the style conforms with black.
+envdir = {toxworkdir}/py38
+
+commands = black --check .
+
+[testenv:style-isort]
+description = Check imports conform with isort.
+envdir = {toxworkdir}/py38
+
+commands = isort --check .
+
+[testenv:lint-flake8]
+description = Lint with flake8.
+envdir = {toxworkdir}/py38
+
+commands = flake8 .
+
+; [testenv:type-pyright]
+; description = Type checking with pyright.
+; envdir = {toxworkdir}/py38
+
+; commands =
+; pip install --force-reinstall git+https://github.com/Rapptz/discord.py
+; pyright
+
+[testenv:docs]
+description = Try to build the docs (HTML)
+envdir = {toxworkdir}/py38
+
+commands = sphinx-build -d "{toxworkdir}/docs_doctree" docs "{toxworkdir}/docs_out" --keep-going
+
+[testenv:pytest]
+description = Run pytest
+envdir = {toxworkdir}/py38
+
+commands =
+ pytest tests
\ No newline at end of file
From 96ff5c2427ca5375a6089e57f42b7177bb18dc49 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 18:40:39 +0800
Subject: [PATCH 14/51] Tweak workflow
---
.github/workflows/checks.yml | 4 ---
.github/workflows/scripts/loadcheck.py | 2 +-
choose/__init__.py | 2 +-
choose/choose.py | 8 ++---
genshinutils/daily.py | 8 ++---
genshinutils/notes.py | 8 ++---
genshinutils/profile.py | 41 ++++++++++----------------
genshinutils/register.py | 16 +++-------
genshinutils/utils.py | 4 +--
throw/throw.py | 18 ++++-------
tox.ini | 19 ------------
11 files changed, 33 insertions(+), 97 deletions(-)
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index fae08f6..10d21bd 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -31,10 +31,6 @@ jobs:
friendly_name: Style (isort)
- tox_env: lint-flake8
friendly_name: Lint (flake8)
- # - tox_env: type-pyright
- # friendly_name: Type check (pyright)
- - tox_env: docs
- friendly_name: Docs
- tox_env: pytest
friendly_name: Tests
diff --git a/.github/workflows/scripts/loadcheck.py b/.github/workflows/scripts/loadcheck.py
index 6010f51..7c9c217 100644
--- a/.github/workflows/scripts/loadcheck.py
+++ b/.github/workflows/scripts/loadcheck.py
@@ -89,4 +89,4 @@ async def leswebsockets() -> Tuple[Dict[str, Any], Dict[str, Any]]:
else:
print("\N{HEAVY CHECK MARK} Unloaded all cogs successfully")
-sys.exit(exit_code)
\ No newline at end of file
+sys.exit(exit_code)
diff --git a/choose/__init__.py b/choose/__init__.py
index ae5db39..7bf1181 100644
--- a/choose/__init__.py
+++ b/choose/__init__.py
@@ -1,7 +1,7 @@
import json
from pathlib import Path
-from .choose import setup # noqa: F401
+from .choose import setup # noqa: F401
with open(Path(__file__).parent / "info.json") as fp:
__red_end_user_data_statement__ = json.load(fp)["end_user_data_statement"]
diff --git a/choose/choose.py b/choose/choose.py
index b7825d4..89f7cfe 100644
--- a/choose/choose.py
+++ b/choose/choose.py
@@ -58,9 +58,7 @@ async def choose(self, ctx, *, options):
choosearray = split(r";|,|\n|\||#", options)
if len(choosearray) > 1:
- e = discord.Embed(
- color=(await ctx.embed_colour()), title=random.choice(choosearray)
- )
+ e = discord.Embed(color=(await ctx.embed_colour()), title=random.choice(choosearray))
e.set_footer(
text=f"✨ Choosing for {ctx.author.display_name}, from a list of {len(choosearray)} options."
)
@@ -71,9 +69,7 @@ async def choose(self, ctx, *, options):
return await ctx.send(embed=e)
except Exception as exc:
log.exception("Error trying to send choose embed.", exc_info=exc)
- return await ctx.send(
- "Oops, I encountered an error while trying to send the embed."
- )
+ return await ctx.send("Oops, I encountered an error while trying to send the embed.")
async def setup(bot: Red) -> None:
diff --git a/genshinutils/daily.py b/genshinutils/daily.py
index 4f42ff8..ca6a365 100644
--- a/genshinutils/daily.py
+++ b/genshinutils/daily.py
@@ -42,9 +42,7 @@ async def redeem_daily():
)
e.add_field(name="Total Login", value=f"{claimed_rewards} days")
except Exception as exc:
- return await ctx.send(
- f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
- )
+ return await ctx.send(f"Unable to retrieve data from Hoyolab API:\n`{exc}`")
else:
signed_in = "✅"
e = generate_embed(
@@ -54,9 +52,7 @@ async def redeem_daily():
)
e.set_thumbnail(reward.icon)
e.add_field(name="Reward", value=f"{reward.name} x{reward.amount}")
- e.add_field(
- name="Total Login", value=f"{signed_in} {claimed_rewards + 1} days"
- )
+ e.add_field(name="Total Login", value=f"{signed_in} {claimed_rewards + 1} days")
return await ctx.send(embed=e)
uid = await validate_uid(ctx.author, self.config)
diff --git a/genshinutils/notes.py b/genshinutils/notes.py
index ee5295e..91d7892 100644
--- a/genshinutils/notes.py
+++ b/genshinutils/notes.py
@@ -34,9 +34,7 @@ async def generate_diary(uid):
client = genshin.Client(cookie)
data = await client.get_notes(uid)
except Exception as exc:
- return await ctx.send(
- f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
- )
+ return await ctx.send(f"Unable to retrieve data from Hoyolab API:\n`{exc}`")
e = generate_embed(
title=f"Game Notes for {ctx.author.display_name}",
color=await ctx.embed_color(),
@@ -96,9 +94,7 @@ async def test_honkai():
client = genshin.Client(cookie)
data = await client.get_full_honkai_user(20177789)
except Exception as exc:
- return await ctx.send(
- f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
- )
+ return await ctx.send(f"Unable to retrieve data from Hoyolab API:\n`{exc}`")
return await log.debug(f"```{data}```")
uid = await validate_uid(ctx.author, self.config)
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
index 42e5ac8..f67fcaa 100644
--- a/genshinutils/profile.py
+++ b/genshinutils/profile.py
@@ -8,8 +8,13 @@
from redbot.core import commands
from .constants import character_namecards
-from .utils import (enka_get_character_card, generate_embed, get_user_cookie,
- validate_char_name, validate_uid)
+from .utils import (
+ enka_get_character_card,
+ generate_embed,
+ get_user_cookie,
+ validate_char_name,
+ validate_uid,
+)
log = logging.getLogger("red.raidensakura.genshinutils")
@@ -44,9 +49,7 @@ async def enka_generate_profile(uid):
try:
data = await self.enka_client.fetch_user(uid)
except Exception as exc:
- return await ctx.send(
- f"Unable to retrieve data from enka.network:\n`{exc}`"
- )
+ return await ctx.send(f"Unable to retrieve data from enka.network:\n`{exc}`")
e = generate_embed(
title=f"Profile for {data.player.nickname} [AR {data.player.level}]",
@@ -80,29 +83,21 @@ async def enka_generate_char_img(uid, char_name):
with io.BytesIO() as image_binary:
char_card = await enka_get_character_card(uid, char_name)
if not char_card:
- return await ctx.send(
- "This user does not have that character featured."
- )
+ return await ctx.send("This user does not have that character featured.")
temp_filename = str(time.time()).split(".")[0] + ".png"
- log.debug(
- f"[generate_char_info] Pillow object for character card:\n{char_card}"
- )
+ log.debug(f"[generate_char_info] Pillow object for character card:\n{char_card}")
first_card = next(iter(char_card.values()))
card_object = next(iter(first_card.values()))
card_object.save(image_binary, "PNG", optimize=True, quality=95)
image_binary.seek(0)
- return await ctx.send(
- file=discord.File(fp=image_binary, filename=temp_filename)
- )
+ return await ctx.send(file=discord.File(fp=image_binary, filename=temp_filename))
async def genshin_generate_profile(uid):
try:
client = genshin.Client(cookie)
data = await client.get_partial_genshin_user(uid)
except Exception as exc:
- return await ctx.send(
- f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
- )
+ return await ctx.send(f"Unable to retrieve data from Hoyolab API:\n`{exc}`")
e = generate_embed(
title=f"Profile for {data.info.nickname} [AR {data.info.level}]",
@@ -115,9 +110,7 @@ async def genshin_generate_profile(uid):
e.set_image(url=namecard_url)
e.add_field(name="Achievements", value=data.stats.achievements, inline=True)
e.add_field(name="Days Active", value=data.stats.days_active, inline=True)
- e.add_field(
- name="Characters Unlocked", value=data.stats.characters, inline=True
- )
+ e.add_field(name="Characters Unlocked", value=data.stats.characters, inline=True)
e.add_field(
name="Current Spiral Abyss Floor",
value=data.stats.spiral_abyss,
@@ -178,9 +171,7 @@ async def genshin_generate_profile(uid):
with ctx.typing():
return await enka_generate_profile(uid)
- log.debug(
- f"[{ctx.command.name}] Not a UID, assuming it's a character name..."
- )
+ log.debug(f"[{ctx.command.name}] Not a UID, assuming it's a character name...")
char = validate_char_name(user_or_uid)
if not char:
return await ctx.send(
@@ -201,9 +192,7 @@ async def genshin_generate_profile(uid):
if user_or_uid and character:
uid = await validate_uid(user_or_uid, self.config)
if not uid:
- return await ctx.send(
- "Not a valid UID or user does not have a UID linked."
- )
+ return await ctx.send("Not a valid UID or user does not have a UID linked.")
char = validate_char_name(character)
if not char:
diff --git a/genshinutils/register.py b/genshinutils/register.py
index 813ae8b..e2c08eb 100644
--- a/genshinutils/register.py
+++ b/genshinutils/register.py
@@ -57,9 +57,7 @@ def pass_verification(discordtag, signature):
with ctx.typing():
data = await self.enka_client.fetch_user(uid)
except Exception as exc:
- return await ctx.send(
- f"Unable to retrieve data from enka.network:\n`{exc}`"
- )
+ return await ctx.send(f"Unable to retrieve data from enka.network:\n`{exc}`")
author_discord_id = f"{ctx.author.name}#{ctx.author.discriminator}"
@@ -153,9 +151,7 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
client = genshin.Client(cookies)
accounts = await client.get_game_accounts()
except Exception as exc:
- return await ctx.send(
- f"Unable to retrieve data from Hoyolab API:\n`{exc}`"
- )
+ return await ctx.send(f"Unable to retrieve data from Hoyolab API:\n`{exc}`")
"""
Accounts: [ GenshinAccount(lang="", game_biz="", level=int...), GenshinAccount(...) ]
Recognized game_biz:
@@ -170,9 +166,7 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
genshin_acc_list.append(account)
if not genshin_acc_list:
- return await ctx.send(
- "Couldn't find a linked Genshin UID in your Hoyolab account."
- )
+ return await ctx.send("Couldn't find a linked Genshin UID in your Hoyolab account.")
# https://www.geeksforgeeks.org/python-get-the-object-with-the-max-attribute-value-in-a-list-of-objects/
# get genshin account with the highest level
@@ -209,9 +203,7 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
log.debug(
f"[Register Hoyolab] Encrypted ltoken saved: {await self.config.user(ctx.author).ltoken()}"
)
- log.debug(
- f"[Register Hoyolab] Encryption key saved: {await self.config.encryption_key()}"
- )
+ log.debug(f"[Register Hoyolab] Encryption key saved: {await self.config.encryption_key()}")
decoded_ltuid = await decrypt_config(self.config, encoded_ltuid)
decoded_ltoken = await decrypt_config(self.config, encoded_ltoken)
diff --git a/genshinutils/utils.py b/genshinutils/utils.py
index 0daf89a..16a9060 100644
--- a/genshinutils/utils.py
+++ b/genshinutils/utils.py
@@ -105,9 +105,7 @@ def validate_char_name(arg):
"""
# enka_get_character_card
async def enka_get_character_card(uid, char_name):
- async with encbanner.ENC(
- lang="en", splashArt=True, characterName=char_name
- ) as encard:
+ async with encbanner.ENC(lang="en", splashArt=True, characterName=char_name) as encard:
ENCpy = await encard.enc(uids=uid)
return await encard.creat(ENCpy, 2)
diff --git a/throw/throw.py b/throw/throw.py
index ddecc74..d6efb80 100644
--- a/throw/throw.py
+++ b/throw/throw.py
@@ -8,7 +8,7 @@
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
from tabulate import tabulate
-from .constants import ITEMS, HIT, MISS
+from .constants import HIT, ITEMS, MISS
class Throw(commands.Cog):
@@ -132,9 +132,7 @@ def get_avatar(user):
return str(user.avatar_url)
pages = []
- dedupe_list_1 = [
- x for i, x in enumerate(people_with_no_creativity, 1) if i % 2 != 0
- ]
+ dedupe_list_1 = [x for i, x in enumerate(people_with_no_creativity, 1) if i % 2 != 0]
server_table = tabulate(
dedupe_list_1, headers=header, colalign=colalign, tablefmt="psql"
)
@@ -148,21 +146,15 @@ def get_avatar(user):
for action in self.possible_actions:
parse_actions(global_actions_data, global_actions_array, action)
- dedupe_list_2 = [
- x for i, x in enumerate(global_actions_array, 1) if i % 2 != 0
- ]
+ dedupe_list_2 = [x for i, x in enumerate(global_actions_array, 1) if i % 2 != 0]
global_table = tabulate(
dedupe_list_2, headers=header, colalign=colalign, tablefmt="psql"
)
embed = discord.Embed(
colour=await ctx.embed_colour(), description=box(global_table, "nim")
)
- embed.set_author(
- name=f"Global Throw Stats | {user.name}", icon_url=get_avatar(user)
- )
- embed.set_footer(
- text=f"Requester: {ctx.author}", icon_url=get_avatar(ctx.author)
- )
+ embed.set_author(name=f"Global Throw Stats | {user.name}", icon_url=get_avatar(user))
+ embed.set_footer(text=f"Requester: {ctx.author}", icon_url=get_avatar(ctx.author))
pages.append(embed)
await menu(ctx, pages, DEFAULT_CONTROLS, timeout=60.0)
diff --git a/tox.ini b/tox.ini
index 75f172d..f88e9ef 100644
--- a/tox.ini
+++ b/tox.ini
@@ -25,11 +25,6 @@ deps =
pyjson5
expr.py
- # docs
- sphinx
- sphinx-rtd-theme==1.0.0
- furo
-
# pytest
pytest
red-discordbot==3.4.18
@@ -58,20 +53,6 @@ envdir = {toxworkdir}/py38
commands = flake8 .
-; [testenv:type-pyright]
-; description = Type checking with pyright.
-; envdir = {toxworkdir}/py38
-
-; commands =
-; pip install --force-reinstall git+https://github.com/Rapptz/discord.py
-; pyright
-
-[testenv:docs]
-description = Try to build the docs (HTML)
-envdir = {toxworkdir}/py38
-
-commands = sphinx-build -d "{toxworkdir}/docs_doctree" docs "{toxworkdir}/docs_out" --keep-going
-
[testenv:pytest]
description = Run pytest
envdir = {toxworkdir}/py38
From dad5b42ab095f6bdd1454001e19e66410d8a2e42 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 19:08:59 +0800
Subject: [PATCH 15/51] Hmm
---
.github/workflows/checks.yml | 2 --
1 file changed, 2 deletions(-)
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index 10d21bd..2aa1776 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -21,8 +21,6 @@ jobs:
- style-black
- style-isort
- lint-flake8
- # - type-pyright
- - docs
- pytest
include:
- tox_env: style-black
From 93dacc3044d5fe38a390177ad0987552a5c572a4 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 19:15:50 +0800
Subject: [PATCH 16/51] Test again...
---
.github/red_data/cogs/CogManager/settings.json | 10 ++++++++++
.github/red_data/core/settings.json | 11 +++++++++++
.github/workflows/loadcheck.yml | 2 +-
3 files changed, 22 insertions(+), 1 deletion(-)
create mode 100644 .github/red_data/cogs/CogManager/settings.json
create mode 100644 .github/red_data/core/settings.json
diff --git a/.github/red_data/cogs/CogManager/settings.json b/.github/red_data/cogs/CogManager/settings.json
new file mode 100644
index 0000000..b841a53
--- /dev/null
+++ b/.github/red_data/cogs/CogManager/settings.json
@@ -0,0 +1,10 @@
+{
+ "2938473984732": {
+ "GLOBAL": {
+ "paths": [
+ "C:\\Data\\Git\\raiden-cogs",
+ "/home/runner/work/raiden-cogs/raiden-cogs"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/.github/red_data/core/settings.json b/.github/red_data/core/settings.json
new file mode 100644
index 0000000..e0217d2
--- /dev/null
+++ b/.github/red_data/core/settings.json
@@ -0,0 +1,11 @@
+{
+ "0": {
+ "CUSTOM_GROUPS": {},
+ "GLOBAL": {
+ "prefix": [
+ "!"
+ ],
+ "schema_version": 2
+ }
+ }
+}
\ No newline at end of file
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index e642228..9295809 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -51,7 +51,7 @@ jobs:
- name: Prepare files
run: |
mkdir -p /home/runner/.config/Red-DiscordBot
- echo '{"workflow": {"DATA_PATH": "/home/runner/work/Vex-Cogs/Vex-Cogs/.github/red_data", "COG_PATH_APPEND": "cogs", "CORE_PATH_APPEND": "core", "STORAGE_TYPE": "JSON", "STORAGE_DETAILS": {}}}' > /home/runner/.config/Red-DiscordBot/config.json
+ echo '{"workflow": {"DATA_PATH": "/home/runner/work/raiden-cogs/raiden-cogs/.github/red_data", "COG_PATH_APPEND": "cogs", "CORE_PATH_APPEND": "core", "STORAGE_TYPE": "JSON", "STORAGE_DETAILS": {}}}' > /home/runner/.config/Red-DiscordBot/config.json
- name: Run script loadcheck.py
run: |
source .venv/bin/activate
From 3443cf8e31bf85046fbde0798c72e43fd5a28db8 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 19:29:10 +0800
Subject: [PATCH 17/51] again, blegh
---
.flake8 | 2 +-
.github/workflows/loadcheck.yml | 12 ++++--------
pyproject.toml | 8 +-------
3 files changed, 6 insertions(+), 16 deletions(-)
diff --git a/.flake8 b/.flake8
index 33d2356..7cf6fb3 100644
--- a/.flake8
+++ b/.flake8
@@ -2,4 +2,4 @@
ignore = W503,E203,E402
per-file-ignores = __init__.py:F401,consts.py:E501
max-line-length = 99
-exclude = .git,.venv,.tox,__pycache__,docs,vexutils
\ No newline at end of file
+exclude = .git,.venv,.tox,__pycache__
\ No newline at end of file
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index 9295809..b3932a0 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -13,14 +13,10 @@ jobs:
matrix:
python-version: ["3.8", "3.9"]
red-version:
- # this workflow required pr #5453 commit d27dbde, which is in dev & pypi 3.4.15+
- - "git+https://github.com/Cog-Creators/Red-DiscordBot@V3/develop#egg=Red-DiscordBot"
- - "Red-DiscordBot==3.4.16"
+ - "Red-DiscordBot==3.4.18"
include:
- - red-version: "git+https://github.com/Cog-Creators/Red-DiscordBot@V3/develop#egg=Red-DiscordBot"
- friendly-red: "Red (dev version)"
- - red-version: "Red-DiscordBot==3.4.16"
- friendly-red: "Red 3.4.16"
+ - red-version: "Red-DiscordBot==3.4.18"
+ friendly-red: "Red 3.4.18"
fail-fast: false
name: Cog load test - Python ${{ matrix.python-version }} & ${{ matrix.friendly-red }}
@@ -44,7 +40,7 @@ jobs:
run: |
python3 -m venv .venv
source .venv/bin/activate
- pip install setuptools wheel
+ pip install setuptools wheel pillow
pip install ${{ matrix.red-version }}
pip install -r dev-requirements.txt
pip install jsonrpc-websocket
diff --git a/pyproject.toml b/pyproject.toml
index b3bb428..41ea6ae 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,14 +1,8 @@
[tool.black]
line-length = 99
target-version = ["py38"]
-extend-exclude = "vexutils|.stubs"
+extend-exclude = ".stubs"
[tool.isort]
profile = "black"
line_length = 99
-extend_skip = "vexutils"
-
-[tool.pyright]
-stubPath = "./.stubs"
-exclude = ["*/vexutils", ".tox", "**/__pycache__", ".venv", ".github", ".stubs"]
-useLibraryCodeForTypes = true
\ No newline at end of file
From fc4f31f224b7d3ff7738e1e91f84a3cb82123c84 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 19:32:04 +0800
Subject: [PATCH 18/51] I hope it work now
---
.github/workflows/loadcheck.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index b3932a0..5401235 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -40,10 +40,11 @@ jobs:
run: |
python3 -m venv .venv
source .venv/bin/activate
- pip install setuptools wheel pillow
+ pip install setuptools wheel
pip install ${{ matrix.red-version }}
pip install -r dev-requirements.txt
pip install jsonrpc-websocket
+ pip install pillow genshin enkanetwork aioenkanetworkcard fernet
- name: Prepare files
run: |
mkdir -p /home/runner/.config/Red-DiscordBot
From be9871fb00fd7a546c9af0ef7414ad84d04964bf Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 19:35:37 +0800
Subject: [PATCH 19/51] Cmon pip
---
.github/workflows/loadcheck.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index 5401235..cc19e91 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -40,6 +40,7 @@ jobs:
run: |
python3 -m venv .venv
source .venv/bin/activate
+ pip install --upgrade pip
pip install setuptools wheel
pip install ${{ matrix.red-version }}
pip install -r dev-requirements.txt
From fb6498e335937c54a8cc65f7e76708adc8bdd174 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 19:47:37 +0800
Subject: [PATCH 20/51] =?UTF-8?q?=F0=9F=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
dev-requirements.txt | 37 +++++++++++--------------------------
1 file changed, 11 insertions(+), 26 deletions(-)
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 40161a1..0ce7649 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,24 +1,18 @@
-# Red itself is also a requriement, however it is not listed here because
+# Red itself is also a requirement, however it is not listed here because
# some people may have special setups (eg dev version installed)
-# Required for utils
-tabulate
-cachetools
-asyncache
-
-# Required for optional PandasSQLDriver in utils
-pandas
-
# Cogs
-plotly # betteruptime, stattrack, googletrends
-kaleido # betteruptime, stattrack, googletrends
gidgethub # ghissues, gitub
-pytrends # googletrends
-psutil # stattrack, system
-markdownify # status
-rapidfuzz # timechannel
-wakeonlan # wol
-expr.py # calc
+pillow # genshinutils
+genshin # genshinutils
+enkanetwork # genshinutils
+aioenkanetworkcard # genshinutils
+fernet # genshinutils
+
+# Required by EnkaNetwork.py
+pydantic
+aiohttp
+cachetools
# checking tools
pyright
@@ -29,14 +23,5 @@ pytest
tox
pre-commit
-# docs
-sphinx
-sphinx-rtd-theme
-furo
-
# checks
python-dotenv
-
-# docs
-sphinx_rtd_theme
-furo
\ No newline at end of file
From 783c6260f1e5ad6d411c80fb2ebd2e3c7c73751c Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 19:48:33 +0800
Subject: [PATCH 21/51] :(
---
dev-requirements.txt | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 0ce7649..b3f76a7 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,6 +1,11 @@
# Red itself is also a requirement, however it is not listed here because
# some people may have special setups (eg dev version installed)
+# Required by EnkaNetwork.py
+pydantic
+aiohttp
+cachetools
+
# Cogs
gidgethub # ghissues, gitub
pillow # genshinutils
@@ -9,11 +14,6 @@ enkanetwork # genshinutils
aioenkanetworkcard # genshinutils
fernet # genshinutils
-# Required by EnkaNetwork.py
-pydantic
-aiohttp
-cachetools
-
# checking tools
pyright
black
From c1d40934bc59c313a648ffd29a4b1595f824e325 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 19:58:32 +0800
Subject: [PATCH 22/51] .
---
.github/workflows/loadcheck.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index cc19e91..3f08a59 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -45,7 +45,6 @@ jobs:
pip install ${{ matrix.red-version }}
pip install -r dev-requirements.txt
pip install jsonrpc-websocket
- pip install pillow genshin enkanetwork aioenkanetworkcard fernet
- name: Prepare files
run: |
mkdir -p /home/runner/.config/Red-DiscordBot
From 1f0d3d3c3d39f737a604be5c6b07db216a73bc50 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 20:06:58 +0800
Subject: [PATCH 23/51] TYPO OOF
---
dev-requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dev-requirements.txt b/dev-requirements.txt
index b3f76a7..28e037e 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -10,7 +10,7 @@ cachetools
gidgethub # ghissues, gitub
pillow # genshinutils
genshin # genshinutils
-enkanetwork # genshinutils
+enkanetwork.py # genshinutils
aioenkanetworkcard # genshinutils
fernet # genshinutils
From ef1764ad16144f63215a542611be342c88505d50 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 20:08:49 +0800
Subject: [PATCH 24/51] bruh
---
dev-requirements.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 28e037e..fa219ef 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -9,6 +9,7 @@ cachetools
# Cogs
gidgethub # ghissues, gitub
pillow # genshinutils
+tabulate # throw
genshin # genshinutils
enkanetwork.py # genshinutils
aioenkanetworkcard # genshinutils
From e54e5773380b0561bc4b7f45265faca6af8b2c21 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 20:10:23 +0800
Subject: [PATCH 25/51] Remove more stuff
---
dev-requirements.txt | 5 -----
1 file changed, 5 deletions(-)
diff --git a/dev-requirements.txt b/dev-requirements.txt
index fa219ef..9674a66 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,11 +1,6 @@
# Red itself is also a requirement, however it is not listed here because
# some people may have special setups (eg dev version installed)
-# Required by EnkaNetwork.py
-pydantic
-aiohttp
-cachetools
-
# Cogs
gidgethub # ghissues, gitub
pillow # genshinutils
From 9e27955bdc03c620baf797cd92be3ba052b9b8ba Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 21:15:36 +0800
Subject: [PATCH 26/51] Link & format
---
.flake8 | 8 ++++----
choose/choose.py | 5 ++++-
genshinutils/profile.py | 17 ++++++++--------
genshinutils/register.py | 42 ++++++++++++++++++++++------------------
throw/throw.py | 2 +-
5 files changed, 41 insertions(+), 33 deletions(-)
diff --git a/.flake8 b/.flake8
index 7cf6fb3..2f2d6c1 100644
--- a/.flake8
+++ b/.flake8
@@ -1,5 +1,5 @@
[flake8]
-ignore = W503,E203,E402
-per-file-ignores = __init__.py:F401,consts.py:E501
-max-line-length = 99
-exclude = .git,.venv,.tox,__pycache__
\ No newline at end of file
+ignore = E302
+per-file-ignores = __init__.py:F401, constants.py:E501
+max-line-length = 110
+exclude = .git, .venv, .tox, __pycache__
\ No newline at end of file
diff --git a/choose/choose.py b/choose/choose.py
index 89f7cfe..42da803 100644
--- a/choose/choose.py
+++ b/choose/choose.py
@@ -60,7 +60,10 @@ async def choose(self, ctx, *, options):
if len(choosearray) > 1:
e = discord.Embed(color=(await ctx.embed_colour()), title=random.choice(choosearray))
e.set_footer(
- text=f"✨ Choosing for {ctx.author.display_name}, from a list of {len(choosearray)} options."
+ text=(
+ f"✨ Choosing for {ctx.author.display_name}, "
+ f"from a list of {len(choosearray)} options."
+ )
)
else:
return await ctx.send("Not enough options to pick from.")
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
index f67fcaa..eb89d06 100644
--- a/genshinutils/profile.py
+++ b/genshinutils/profile.py
@@ -116,28 +116,29 @@ async def genshin_generate_profile(uid):
value=data.stats.spiral_abyss,
inline=True,
)
+ total_oculi = data.stats.anemoculi + data.stats.geoculi
+ + data.stats.electroculi + data.stats.dendroculi
e.add_field(
name="Total Oculi Collected",
- value=(
- f"{data.stats.anemoculi + data.stats.geoculi + data.stats.electroculi + data.stats.dendroculi}"
- ),
+ value=total_oculi,
inline=True,
)
e.add_field(
name="Waypoints Unlocked",
- value=(f"{data.stats.unlocked_waypoints}"),
+ value=data.stats.unlocked_waypoints,
inline=True,
)
+ total_chest = data.stats.common_chests + data.stats.precious_chests
+ + data.stats.exquisite_chests + data.stats.luxurious_chests
+ + data.stats.remarkable_chests
e.add_field(
name="Total Chests Opened",
- value=(
- f"{data.stats.common_chests + data.stats.precious_chests + data.stats.exquisite_chests + data.stats.luxurious_chests + data.stats.remarkable_chests}"
- ),
+ value=total_chest,
inline=True,
)
e.add_field(
name="Domains Unlocked",
- value=(f"{data.stats.unlocked_domains}"),
+ value=data.stats.unlocked_domains,
inline=True,
)
diff --git a/genshinutils/register.py b/genshinutils/register.py
index e2c08eb..75c7e7f 100644
--- a/genshinutils/register.py
+++ b/genshinutils/register.py
@@ -8,7 +8,7 @@
from discord.channel import DMChannel
from redbot.core import commands
-from .utils import decrypt_config, encrypt_config
+from .utils import decrypt_config, encrypt_config, generate_embed
log = logging.getLogger("red.raidensakura.genshinutils")
@@ -65,7 +65,10 @@ def pass_verification(discordtag, signature):
author_discord_id, data.player.signature
):
return await ctx.send(
- "Your signature does not contain your Discord tag.\nNote that it may take up to 15 minutes for changes to be reflected."
+ (
+ "Your signature does not contain your Discord tag.\n"
+ "Note that it may take up to 15 minutes for changes to be reflected."
+ )
)
await self.config.user(ctx.author).UID.set(uid)
return await ctx.send(f"Successfully set UID for {ctx.author.name} to {uid}.")
@@ -74,9 +77,9 @@ def pass_verification(discordtag, signature):
Important Notes:
1. Command has proprietary DM check since I want it to preface a a disclaimer when run in a server.
This command deals with sensitive information and I want it to be taken very seriously.
- 2. I fully acknowledge storing the encryption key along with the encrypted data itself is terrible security practice.
- Hoyolab account token can be used to perform destructive account actions, and potentially get your account banned for abuse.
- Since the cog is open-source, the purpose of the encryption is to prevent bot owners from having plaintext access to them
+ 2. I fully acknowledge storing the encryption key along with the encrypted data itself is bad practice.
+ Hoyolab account token can be used to perform potentially dangerous account actions.
+ Since the cog is OSS, the purpose is to prevent bot owners from having plaintext access to them
in a way such that is require a bit of coding and encryption knowledge to access them on demand.
"""
@@ -101,34 +104,35 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
else:
owner = app_info.owner
desc = (
- "This command lets the bot perform Hoyolab account actions on your behalf, authenticated using your token. "
- "The token will then be linked to your Discord ID and stored encrypted in the bot's config, along with the encryption key. "
- "Make sure **you fully acknowledge the risks of sharing your account token online** before proceeding.\n\n"
- "For security reason, please run this command in a DM channel when setting token.\n\n"
- "Read on how to obtain your token [here](https://project-mei.xyz/genshinutils)."
- )
- e = Embed(
- color=(await ctx.embed_colour()),
- title="Important Disclaimer",
- description=desc,
+ "This command links your Hoyolab account token to your Discord account. "
+ "Your account token allow the bot to perform various account actions on your behalf, "
+ "such as claiming daily login, fetching character data etc. "
+ "Your token will be stored in the bot's config, and bot owner will always have access to it. "
+ "Make sure **you fully understand the risk of sharing your token online** before proceeding."
+ "\n\nFor security reason, please run this command in a DM channel when setting token."
+ "\n\nRead on how to obtain your token [here](https://project-mei.xyz/genshinutils)."
)
+ e = generate_embed(title="Important Disclaimer", desc=desc, color=await ctx.embed_color())
if app_info.bot_public:
public = "Can be invited by anyone."
else:
public = "Can only be invited by the owner."
e.add_field(name="Bot Owner", value=owner)
- e.add_field(name="Invite Link Privacy", value=public)
+ e.add_field(name="Bot Invite Link Privacy", value=public)
if ctx.me.avatar_url:
e.set_thumbnail(url=ctx.me.avatar_url)
e.set_footer(text=f"Command invoked by {ctx.author}.")
return await ctx.send(embed=e)
if not cookie:
+ cog_url = "https://project-mei.xyz/genshinutils"
+ bot_prefix = f"{escape(ctx.prefix)}"
+ command_name = f"{escape(ctx.command.name)}"
msg = (
f"**Provide a valid cookie to bind your Discord account to.**\n\n"
- f"` » ` Instruction on how to obtain your Hoyolab cookie:\n\n\n"
- f"` » ` For command help context: `{escape(ctx.prefix)}help genshin register {escape(ctx.command.name)}`\n\n"
- f"` » ` To read disclaimers, this command again in any server."
+ f"` » ` Instruction on how to obtain your Hoyolab cookie:\n<{cog_url}>\n\n"
+ f"` » ` For command help context: `{bot_prefix}help genshin register {command_name}`\n\n"
+ f"` » ` To read disclaimers, type this command again in any server."
)
return await ctx.send(msg)
diff --git a/throw/throw.py b/throw/throw.py
index d6efb80..17146e8 100644
--- a/throw/throw.py
+++ b/throw/throw.py
@@ -120,7 +120,7 @@ def parse_actions(data, array, action: str):
for key, value in data.items():
if action in key:
sent = str(data.get("ITEMS_THROWN", " ")).replace("0", " ")
- received = str(data.get(f"TIMES_HIT", " ")).replace("0", " ")
+ received = str(data.get("TIMES_HIT", " ")).replace("0", " ")
array.append([action.lower(), received, sent])
for act in self.possible_actions:
From bc15cb25b290258d98f3332dd3abf4bd268256f4 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 21:18:58 +0800
Subject: [PATCH 27/51] Reformat again ugh
---
genshinutils/profile.py | 6 +++---
genshinutils/register.py | 4 +++-
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
index eb89d06..1b52743 100644
--- a/genshinutils/profile.py
+++ b/genshinutils/profile.py
@@ -117,7 +117,7 @@ async def genshin_generate_profile(uid):
inline=True,
)
total_oculi = data.stats.anemoculi + data.stats.geoculi
- + data.stats.electroculi + data.stats.dendroculi
+ +data.stats.electroculi + data.stats.dendroculi
e.add_field(
name="Total Oculi Collected",
value=total_oculi,
@@ -129,8 +129,8 @@ async def genshin_generate_profile(uid):
inline=True,
)
total_chest = data.stats.common_chests + data.stats.precious_chests
- + data.stats.exquisite_chests + data.stats.luxurious_chests
- + data.stats.remarkable_chests
+ +data.stats.exquisite_chests + data.stats.luxurious_chests
+ +data.stats.remarkable_chests
e.add_field(
name="Total Chests Opened",
value=total_chest,
diff --git a/genshinutils/register.py b/genshinutils/register.py
index 75c7e7f..14e8cd6 100644
--- a/genshinutils/register.py
+++ b/genshinutils/register.py
@@ -112,7 +112,9 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
"\n\nFor security reason, please run this command in a DM channel when setting token."
"\n\nRead on how to obtain your token [here](https://project-mei.xyz/genshinutils)."
)
- e = generate_embed(title="Important Disclaimer", desc=desc, color=await ctx.embed_color())
+ e = generate_embed(
+ title="Important Disclaimer", desc=desc, color=await ctx.embed_color()
+ )
if app_info.bot_public:
public = "Can be invited by anyone."
else:
From 543e635842f17e2072b07e4be2ffeafd4c5a914d Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 21:54:32 +0800
Subject: [PATCH 28/51] Bump Github Actions ver.
---
.github/workflows/checks.yml | 7 ++-----
tox.ini | 12 +-----------
2 files changed, 3 insertions(+), 16 deletions(-)
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index 2aa1776..0f33229 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -21,7 +21,6 @@ jobs:
- style-black
- style-isort
- lint-flake8
- - pytest
include:
- tox_env: style-black
friendly_name: Style (black)
@@ -29,14 +28,12 @@ jobs:
friendly_name: Style (isort)
- tox_env: lint-flake8
friendly_name: Lint (flake8)
- - tox_env: pytest
- friendly_name: Tests
fail-fast: false
name: Tox - ${{ matrix.python_version }} - ${{ matrix.friendly_name }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
ref: ${{ env.ref }}
- name: Set up Python ${{ matrix.python_version }}
@@ -46,7 +43,7 @@ jobs:
# caching cuts down time for tox (for example black) from ~40 secs to 4
- name: Cache tox
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: .tox
key: tox-${{ matrix.python_version }}-${{ matrix.tox_env }}-${{ hashFiles('tox.ini') }}
diff --git a/tox.ini b/tox.ini
index f88e9ef..cb47ac0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py38, style-black, style-isort, lint-flake8, type-pyright, docs, pytest
+envlist = py38, style-black, style-isort, lint-flake8
skipsdist = true
[testenv]
@@ -25,10 +25,7 @@ deps =
pyjson5
expr.py
- # pytest
- pytest
red-discordbot==3.4.18
- markdownify
# type
# (some are covered under below)
@@ -52,10 +49,3 @@ description = Lint with flake8.
envdir = {toxworkdir}/py38
commands = flake8 .
-
-[testenv:pytest]
-description = Run pytest
-envdir = {toxworkdir}/py38
-
-commands =
- pytest tests
\ No newline at end of file
From 8f74ad27205c73445ab1b2c473fbd3ad149fd81d Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 22:06:11 +0800
Subject: [PATCH 29/51] Update README.md
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index cfb9a90..55bd976 100644
--- a/README.md
+++ b/README.md
@@ -78,6 +78,7 @@ black . ; isort .
From 123a804bb7b2d4daf7cac2481188fb373655c2e4 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Mon, 30 Jan 2023 23:13:34 +0800
Subject: [PATCH 30/51] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 55bd976..afb7c01 100644
--- a/README.md
+++ b/README.md
@@ -56,8 +56,8 @@
To make sure things stay nice and clean.
```py
-pip install -U black isort
-black . ; isort .
+pip install -U black isort flake8
+black . ; isort . ; flake8 .
```
.vscode/settings.json
From f667cd213c901304a0a32f0419644e0613d9227e Mon Sep 17 00:00:00 2001
From: Raiden
Date: Tue, 31 Jan 2023 15:31:31 +0800
Subject: [PATCH 31/51] Bump pre-commit versions, docs, format json
---
.flake8 | 4 +-
.../red_data/cogs/CogManager/settings.json | 4 +-
.github/workflows/checks.yml | 3 +-
.github/workflows/loadcheck.yml | 10 +-
.gitignore | 1 -
.pre-commit-config.yaml | 12 +-
.vscode/settings.json | 6 +
choose/info.json | 18 +--
genshinutils/info.json | 25 ++--
genshinutils/utils.py | 113 +++++++++---------
longcat/info.json | 21 ++--
throw/info.json | 19 +--
12 files changed, 128 insertions(+), 108 deletions(-)
create mode 100644 .vscode/settings.json
diff --git a/.flake8 b/.flake8
index 2f2d6c1..7706534 100644
--- a/.flake8
+++ b/.flake8
@@ -1,5 +1,5 @@
[flake8]
-ignore = E302
+# ignore = E302
per-file-ignores = __init__.py:F401, constants.py:E501
max-line-length = 110
-exclude = .git, .venv, .tox, __pycache__
\ No newline at end of file
+exclude = .git, .venv, .tox, __pycache__
diff --git a/.github/red_data/cogs/CogManager/settings.json b/.github/red_data/cogs/CogManager/settings.json
index b841a53..9c68377 100644
--- a/.github/red_data/cogs/CogManager/settings.json
+++ b/.github/red_data/cogs/CogManager/settings.json
@@ -2,9 +2,9 @@
"2938473984732": {
"GLOBAL": {
"paths": [
- "C:\\Data\\Git\\raiden-cogs",
+ "C:\\Projects\\raiden-cogs",
"/home/runner/work/raiden-cogs/raiden-cogs"
]
}
}
-}
\ No newline at end of file
+}
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index 0f33229..5df7742 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -1,4 +1,3 @@
-# https://github.com/Vexed01/Vex-Cogs/blob/master/.github/workflows/checks.yml
name: Checks
on:
@@ -56,4 +55,4 @@ jobs:
env:
TOXENV: ${{ matrix.tox_env }}
run: |
- tox
\ No newline at end of file
+ tox
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index 3f08a59..4aa47ac 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -21,7 +21,7 @@ jobs:
name: Cog load test - Python ${{ matrix.python-version }} & ${{ matrix.friendly-red }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
@@ -30,12 +30,12 @@ jobs:
- name: Cache venv
id: cache-venv
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: .venv
key: ${{ matrix.red-version }}-${{ matrix.python-version }}-${{ hashFiles('dev-requirements.txt') }}-${{ secrets.CACHE_V }}
- - name: Maybe make venv
+ - name: Make venv if not cached
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
python3 -m venv .venv
@@ -58,7 +58,7 @@ jobs:
- name: Save Red output as Artifact
if: always() # still run if prev step failed
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: "Red log - Python ${{ matrix.python-version }} & ${{ matrix.friendly-red }}"
- path: red.log
\ No newline at end of file
+ path: red.log
diff --git a/.gitignore b/.gitignore
index f5aa38f..d161acc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-.vscode/
Pipfile
Pipfile.lock
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 342b0e2..347f5e2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v2.3.0
+ rev: v4.4.0
hooks:
- id: check-ast
- id: check-json
@@ -9,15 +9,15 @@ repos:
- id: end-of-file-fixer
- id: mixed-line-ending
- repo: https://github.com/psf/black
- rev: "22.3.0"
+ rev: "22.12.0"
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
- rev: "5.8.0"
+ rev: "5.12.0"
hooks:
- id: isort
- repo: https://github.com/myint/autoflake
- rev: "v1.4"
+ rev: "v2.0.0"
hooks:
- id: autoflake
args:
@@ -28,6 +28,6 @@ repos:
"--ignore-init-module-imports",
]
- repo: https://github.com/pycqa/flake8
- rev: "3.9.2"
+ rev: "6.0.0"
hooks:
- - id: flake8
\ No newline at end of file
+ - id: flake8
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..9ae86e4
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,6 @@
+{
+ "autoDocstring.docstringFormat": "one-line-sphinx",
+ "autoDocstring.guessTypes": true,
+ "python.terminal.activateEnvInCurrentTerminal": true,
+ "python.terminal.activateEnvironment": true
+}
diff --git a/choose/info.json b/choose/info.json
index 8848e40..9479fdf 100644
--- a/choose/info.json
+++ b/choose/info.json
@@ -1,17 +1,19 @@
{
- "name": "Choose",
- "short": "Choose between multiple options",
+ "author": [
+ "raidensakura"
+ ],
"description": "A better replacement for core `choose` command.",
- "install_msg": "Thanks for installing. This cog replaces core's `choose` command with a more intuitive one.",
+ "disabled": false,
"end_user_data_statement": "This cog does not persistently store any data about users.",
- "author": ["raidensakura"],
+ "hidden": false,
+ "install_msg": "Thanks for installing. This cog replaces core's `choose` command with a more intuitive one.",
+ "min_bot_version": "3.4.12",
+ "name": "Choose",
"required_cogs": {},
"requirements": [],
+ "short": "Choose between multiple options",
"tags": [
"choose"
],
- "min_bot_version": "3.4.12",
- "hidden": false,
- "disabled": false,
"type": "COG"
-}
\ No newline at end of file
+}
diff --git a/genshinutils/info.json b/genshinutils/info.json
index ba52f34..2bf2521 100644
--- a/genshinutils/info.json
+++ b/genshinutils/info.json
@@ -1,17 +1,24 @@
{
- "name": "GenshinUtils",
- "short": "A Genshin Impact utility cog",
+ "author": [
+ "raidensakura"
+ ],
"description": "Various useful utilities for Genshin Impact, such as retrieving profile data and many more.",
- "install_msg": "Thank you for installing my cog. This is continuously being worked on, expect rapid changes.\nI test stuff in my Discord: https://dsc.gg/transience",
+ "disabled": false,
"end_user_data_statement": "This cog stores your Genshin UID, Hoyolab ID and Hoyolab Account token if provided.",
- "author": ["raidensakura"],
+ "hidden": false,
+ "install_msg": "Thank you for installing my cog. This is continuously being worked on, expect rapid changes.\nI test stuff in my Discord: https://dsc.gg/transience",
+ "min_bot_version": "3.4.12",
+ "name": "GenshinUtils",
"required_cogs": {},
- "requirements": ["genshin", "enkanetwork", "aioenkanetworkcard", "fernet"],
+ "requirements": [
+ "genshin",
+ "enkanetwork",
+ "aioenkanetworkcard",
+ "fernet"
+ ],
+ "short": "A Genshin Impact utility cog",
"tags": [
"genshin"
],
- "min_bot_version": "3.4.12",
- "hidden": false,
- "disabled": false,
"type": "COG"
-}
\ No newline at end of file
+}
diff --git a/genshinutils/utils.py b/genshinutils/utils.py
index 16a9060..ab2be09 100644
--- a/genshinutils/utils.py
+++ b/genshinutils/utils.py
@@ -10,65 +10,58 @@
log = logging.getLogger("red.raidensakura.genshinutils")
-"""I make it a function so it's more readable"""
-# Used internally
-def bytes_to_string(bytes):
- str = bytes.decode()
- return str
-
-"""I make it a function so it's more readable"""
-# Used internally
-def string_to_bytes(str):
- bytes = str.encode()
- return bytes
-
-
-"""
-Accepts: config
-Returns: str(encryption_key) or None
-"""
# https://stackoverflow.com/questions/44432945/generating-own-key-with-python-fernet
async def get_encryption_key(config):
- key = string_to_bytes(await config.encryption_key())
+ """Fetch and convert encryption key from config
+
+ :param object config: Red V3 Config object
+ :return str: Plaintext encryption key
+ """
+ key = await config.encryption_key()
if not key or key is None:
key = Fernet.generate_key()
- await config.encryption_key.set(bytes_to_string(key))
+ await config.encryption_key.set(key.decode())
+ else:
+ key = key.encode()
return key
-"""
-Accepts: config, str(encoded)
-Returns: str(decoded)
-"""
-# decrypt config
async def decrypt_config(config, encoded):
- to_decode = string_to_bytes(encoded)
+ """Decrypt encrypted config data
+
+ :param object config: Red V3 Config object
+ :param str encoded: encoded data
+ :return str: decoded data
+ """
+ to_decode = encoded.encode()
cipher_suite = Fernet(await get_encryption_key(config))
decoded_bytes = cipher_suite.decrypt(to_decode)
- decoded = bytes_to_string(decoded_bytes)
+ decoded = decoded_bytes.decode()
return decoded
-"""
-Accepts: config, str(decoded)
-Returns: str(encoded)
-"""
-# encrypt config
async def encrypt_config(config, decoded):
- to_encode = string_to_bytes(decoded)
+ """Encrypt unencrypted data to store in config
+
+ :param object config: Red V3 Config object
+ :param str decoded: data to encrypt
+ :return str: encoded data
+ """
+ to_encode = decoded.encode()
cipher_suite = Fernet(await get_encryption_key(config))
encoded_bytes = cipher_suite.encrypt(to_encode)
- encoded = bytes_to_string(encoded_bytes)
+ encoded = encoded_bytes.decode()
return encoded
-"""
-Accepts: ( str(uid) | discord.Member ), self.config
-Returns: str(uid) or None
-"""
-# validate uid
async def validate_uid(u, config):
+ """Return user UID from config or check if UID is valid
+
+ :param discord.Member or str u: User or UID to check
+ :param object config: Red V3 Config object
+ :return str: UID of the user if exist or valid
+ """
if isinstance(u, discord.Member):
uid = await config.user(u).UID()
if uid:
@@ -88,34 +81,36 @@ async def validate_uid(u, config):
return uid
-"""
-Accepts: str(name_query)
-Returns: str(formal_name) or None
-"""
-# validate_char_name
def validate_char_name(arg):
+ """Validate character name against constants
+
+ :param str arg: name to check
+ :return str: Formal name of the character if exist
+ """
formal_name = {i for i in common_names if arg in common_names[i]}
if formal_name:
return str(formal_name).strip("{'\"}")
-"""
-Accepts: str(uid), formal_name
-Returns: { UID: { Character: } }
-"""
-# enka_get_character_card
async def enka_get_character_card(uid, char_name):
+ """Generate one or more character build image objects in a dict
+
+ :param str uid: UID of the player
+ :param str char_name: formal name of the character
+ :return dict: dict containing Pillow image object for the character
+ """
async with encbanner.ENC(lang="en", splashArt=True, characterName=char_name) as encard:
ENCpy = await encard.enc(uids=uid)
return await encard.creat(ENCpy, 2)
-"""
-Accepts: config, discord.Member
-Returns:
-"""
-# get_user_cookie
async def get_user_cookie(config, user):
+ """Retrieve user cookie from config
+
+ :param object config: Red V3 Config object
+ :param discord.Member user: Discord user to check for
+ :return cookie: Cookie object for the user
+ """
ltuid_config = await config.user(user).ltuid()
ltoken_config = await config.user(user).ltoken()
@@ -127,12 +122,14 @@ async def get_user_cookie(config, user):
return cookie
-"""
-Accepts: str(title), str(desc), color
-Returns: discord.Embed
-"""
-# generate_embed
def generate_embed(title="", desc="", color=""):
+ """Generate standardized Discord Embed usable for the whole cog
+
+ :param str title: Title of the embed, defaults to ""
+ :param str desc: Description of the embed, defaults to ""
+ :param str color: Color of the embed, defaults to ""
+ :return discord.Embed: Discord Embed object
+ """
cog_url = "https://project-mei.xyz/genshinutils"
e = discord.Embed(title=title, description=desc, color=color, url=cog_url)
e.set_footer(
diff --git a/longcat/info.json b/longcat/info.json
index d5812c2..8a1372f 100644
--- a/longcat/info.json
+++ b/longcat/info.json
@@ -1,17 +1,22 @@
{
- "name": "Longcat",
- "short": "All hail Longcat. Improved from Aioxas' Longcat cog.",
+ "author": [
+ "raidensakura",
+ "Aioxas"
+ ],
"description": "Send a looooongcat based on how long you typed the command.",
- "install_msg": "It's a looooooong loooooooong cat",
+ "disabled": false,
"end_user_data_statement": "This cog does not persistently store any data about users.",
- "author": ["raidensakura", "Aioxas"],
+ "hidden": false,
+ "install_msg": "It's a looooooong loooooooong cat",
+ "min_bot_version": "3.4.12",
+ "name": "Longcat",
"required_cogs": {},
- "requirements": ["Pillow"],
+ "requirements": [
+ "Pillow"
+ ],
+ "short": "All hail Longcat. Improved from Aioxas' Longcat cog.",
"tags": [
"fun"
],
- "min_bot_version": "3.4.12",
- "hidden": false,
- "disabled": false,
"type": "COG"
}
diff --git a/throw/info.json b/throw/info.json
index e13f47f..10f97e1 100644
--- a/throw/info.json
+++ b/throw/info.json
@@ -1,16 +1,21 @@
{
- "name": "Throw",
- "short": "Throw random stuff at your Discord friends",
+ "author": [
+ "raidensakura",
+ "ow0x"
+ ],
"description": "A cog to throw random things at your friends that may or may not upset them. Originally a roleplay cog by owo-cogs, modified by raidensakura.",
+ "disabled": false,
"end_user_data_statement": "This cog does not persistently store any PII data or metadata about users.",
- "author": ["raidensakura", "ow0x"],
+ "hidden": false,
+ "min_bot_version": "3.4.12",
+ "name": "Throw",
"required_cogs": {},
- "requirements": ["tabulate"],
+ "requirements": [
+ "tabulate"
+ ],
+ "short": "Throw random stuff at your Discord friends",
"tags": [
"throw"
],
- "min_bot_version": "3.4.12",
- "hidden": false,
- "disabled": false,
"type": "COG"
}
From 235d895882c5374653fe5aa8a96b84b762a2bc19 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Tue, 31 Jan 2023 15:53:54 +0800
Subject: [PATCH 32/51] Update README.md
---
README.md | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index afb7c01..1639359 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+
@@ -16,6 +17,7 @@
[p]repo add raiden-cogs https://github.com/raidensakura/raiden-cogs/
[p]cog install raiden-cogs
```
+[p] is your prefix
List of Cogs
@@ -53,23 +55,19 @@
Dev Stuff
Formatting
-To make sure things stay nice and clean.
+For manual formatting, this repo uses these three:
```py
pip install -U black isort flake8
black . ; isort . ; flake8 .
```
-.vscode/settings.json
-To make sure the venv always open when I work on cogs.
-
-```json
-{
- "python.terminal.activateEnvironment": true,
- "python.terminal.activateEnvInCurrentTerminal": true,
- "python.defaultInterpreterPath": "C:\\Users\\Raiden\\redenv\\Scripts\\python.exe"
-}
+Pre-commit hooks
+Optional but it keeps manual formatting work away from you.
+```py
+pip install pre-commit
+pre-commit install
```
Credits
@@ -78,7 +76,9 @@ black . ; isort . ; flake8 .
+
+(Back to top)
From b94b12211b17da019a4c3dbf8bf31d98b5e5e416 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Tue, 31 Jan 2023 16:02:48 +0800
Subject: [PATCH 33/51] Update README.md
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 1639359..73708e7 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,8 @@
-A collection of badly written cogs I made for fun in the process of learning Python.
+A collection of badly written homemade cogs I made for fun in the process of learning Python.
+Support: Join my Discord server or mention Raiden#5008
in Red Cog Support server.
Installation
From dd0d1dd15a4c2f6ac90f232a0f991f70bfb480ec Mon Sep 17 00:00:00 2001
From: Raiden
Date: Tue, 31 Jan 2023 22:35:31 +0800
Subject: [PATCH 34/51] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 73708e7..6b3225b 100644
--- a/README.md
+++ b/README.md
@@ -82,4 +82,4 @@ pre-commit install
-(Back to top)
+(Back to top)
From c839aa5bb000fd3271270a9c6dea08f344a1d5b6 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Wed, 1 Feb 2023 23:13:25 +0800
Subject: [PATCH 35/51] add task
---
.flake8 | 2 ++
.github/workflows/checks.yml | 5 ++---
.github/workflows/loadcheck.yml | 8 ++++----
genshinutils/genshinutils.py | 13 ++++++++++++-
4 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/.flake8 b/.flake8
index 7706534..e480388 100644
--- a/.flake8
+++ b/.flake8
@@ -1,5 +1,7 @@
[flake8]
# ignore = E302
per-file-ignores = __init__.py:F401, constants.py:E501
+# So flake8 won't whine when my comments are too long
+# Black and isort still set at 99
max-line-length = 110
exclude = .git, .venv, .tox, __pycache__
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index 5df7742..c8de40f 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -3,8 +3,7 @@ name: Checks
on:
push:
branches:
- - master
- - genshin
+ - main
pull_request:
# thanks red or wherever you got it from
@@ -36,7 +35,7 @@ jobs:
with:
ref: ${{ env.ref }}
- name: Set up Python ${{ matrix.python_version }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }}
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index 4aa47ac..aee3fdd 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -3,8 +3,8 @@ name: "Cog load test"
on:
push:
branches:
- - master
- - genshin
+ - main
+ pull_request:
jobs:
loadcheck:
@@ -19,12 +19,12 @@ jobs:
friendly-red: "Red 3.4.18"
fail-fast: false
- name: Cog load test - Python ${{ matrix.python-version }} & ${{ matrix.friendly-red }}
+ name: Python ${{ matrix.python-version }} & ${{ matrix.friendly-red }}
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
diff --git a/genshinutils/genshinutils.py b/genshinutils/genshinutils.py
index 109d3a8..9d2b34c 100644
--- a/genshinutils/genshinutils.py
+++ b/genshinutils/genshinutils.py
@@ -1,6 +1,7 @@
import logging
from typing import Literal
+from discord.ext import tasks
from enkanetwork import EnkaNetworkAPI
from redbot.core import Config, commands
@@ -40,9 +41,11 @@ def __init__(self, bot):
self.config.register_global(**default_global)
self.config.register_user(**default_user)
self.enka_client = enka_client
+ self.run_tasks.start()
def cog_unload(self):
- enka_client._close()
+ log.debug("Cog unload")
+ self.run_tasks.stop()
def format_help_for_context(self, ctx: commands.Context) -> str:
"""Thanks Sinbad!"""
@@ -62,3 +65,11 @@ async def red_delete_data_for_user(
async def genshin(self, ctx: commands.Context):
"""GenshinUtils main command."""
# TODO: Embed explaining what this cog does and its info
+
+ @tasks.loop(hours=24)
+ async def run_tasks(self):
+ """Schedule tasks to run based on a set loop"""
+
+ @run_tasks.before_loop
+ async def before_run_tasks(self):
+ await self.bot.wait_until_ready()
From 40607b7a72589338ec73b15961a2ba6b10e47a51 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Thu, 2 Feb 2023 20:56:53 +0800
Subject: [PATCH 36/51] Fix cog requirements
---
genshinutils/info.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/genshinutils/info.json b/genshinutils/info.json
index 2bf2521..988c49b 100644
--- a/genshinutils/info.json
+++ b/genshinutils/info.json
@@ -11,10 +11,10 @@
"name": "GenshinUtils",
"required_cogs": {},
"requirements": [
- "genshin",
- "enkanetwork",
+ "enkanetwork.py",
"aioenkanetworkcard",
- "fernet"
+ "cryptography",
+ "genshin"
],
"short": "A Genshin Impact utility cog",
"tags": [
From d59bbe4811b0d85db78bc4593eaf4bc8c799cd8c Mon Sep 17 00:00:00 2001
From: Raiden
Date: Thu, 2 Feb 2023 22:27:36 +0800
Subject: [PATCH 37/51] Change checks decorator to commands
---
genshinutils/settings.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/genshinutils/settings.py b/genshinutils/settings.py
index 96901e2..a23d05f 100644
--- a/genshinutils/settings.py
+++ b/genshinutils/settings.py
@@ -1,6 +1,6 @@
import logging
-from redbot.core import checks, commands
+from redbot.core import commands
log = logging.getLogger("red.raidensakura.genshinutils")
@@ -12,19 +12,19 @@ class GenshinSet(commands.Cog):
async def genshinset(self, ctx: commands.Context):
"""Various global settings for GenshinUtils cog."""
- @checks.is_owner()
+ @commands.is_owner()
@genshinset.command()
async def ltoken(self, ctx: commands.Context):
"""Instructions on how to set global `ltoken` secret."""
await ctx.send(f"Use `{ctx.prefix}set api hoyolab ltoken your_ltoken_here`.")
- @checks.is_owner()
+ @commands.is_owner()
@genshinset.command()
async def ltuid(self, ctx: commands.Context):
"""Instructions on how to set global `ltuid` secret."""
await ctx.send(f"Use `{ctx.prefix}set api hoyolab ltuid your_ltuid_here`.")
- @checks.is_owner()
+ @commands.is_owner()
@genshinset.command()
async def verification(self, ctx: commands.Context, toggle: bool):
"""
From 45c2c231e4e87ff7a965329e401d4a51bcc64d34 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Sun, 5 Feb 2023 21:18:29 +0800
Subject: [PATCH 38/51] Use async in typing
---
dev-requirements.txt | 6 ++----
genshinutils/assets/global/cog_icon.png | Bin 9820 -> 0 bytes
genshinutils/assets/global/login_check.png | Bin 6664 -> 0 bytes
genshinutils/daily.py | 2 +-
genshinutils/notes.py | 2 +-
genshinutils/profile.py | 8 ++++----
genshinutils/register.py | 2 +-
7 files changed, 9 insertions(+), 11 deletions(-)
delete mode 100644 genshinutils/assets/global/cog_icon.png
delete mode 100644 genshinutils/assets/global/login_check.png
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 9674a66..ba90fee 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -2,20 +2,18 @@
# some people may have special setups (eg dev version installed)
# Cogs
-gidgethub # ghissues, gitub
-pillow # genshinutils
tabulate # throw
+pillow # genshinutils
genshin # genshinutils
enkanetwork.py # genshinutils
aioenkanetworkcard # genshinutils
-fernet # genshinutils
+cryptography # genshinutils
# checking tools
pyright
black
isort
flake8
-pytest
tox
pre-commit
diff --git a/genshinutils/assets/global/cog_icon.png b/genshinutils/assets/global/cog_icon.png
deleted file mode 100644
index d7487c0e28a8483338186cd0aa5c130aca3948b6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 9820
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
z
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
diff --git a/genshinutils/daily.py b/genshinutils/daily.py
index ca6a365..0414cf5 100644
--- a/genshinutils/daily.py
+++ b/genshinutils/daily.py
@@ -63,5 +63,5 @@ async def redeem_daily():
if not cookie:
return await ctx.send("No cookie.")
- with ctx.typing():
+ async with ctx.typing():
return await redeem_daily()
diff --git a/genshinutils/notes.py b/genshinutils/notes.py
index 91d7892..d894396 100644
--- a/genshinutils/notes.py
+++ b/genshinutils/notes.py
@@ -105,6 +105,6 @@ async def test_honkai():
if not cookie:
return await ctx.send("No cookie.")
- with ctx.typing():
+ async with ctx.typing():
# return await test_honkai()
return await generate_diary(uid)
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
index 1b52743..72ce4a8 100644
--- a/genshinutils/profile.py
+++ b/genshinutils/profile.py
@@ -155,7 +155,7 @@ async def genshin_generate_profile(uid):
cookie = await get_user_cookie(self.config, ctx.author)
- with ctx.typing():
+ async with ctx.typing():
if not cookie:
return await enka_generate_profile(uid)
@@ -169,7 +169,7 @@ async def genshin_generate_profile(uid):
if user_or_uid and not character:
uid = await validate_uid(user_or_uid, self.config)
if uid:
- with ctx.typing():
+ async with ctx.typing():
return await enka_generate_profile(uid)
log.debug(f"[{ctx.command.name}] Not a UID, assuming it's a character name...")
@@ -186,7 +186,7 @@ async def genshin_generate_profile(uid):
if not uid:
return await ctx.send("You do not have a UID linked.")
- with ctx.typing():
+ async with ctx.typing():
return await enka_generate_char_img(uid, char)
"""This handles if both [user_or_uid] and [character] are appropriately passed"""
@@ -199,5 +199,5 @@ async def genshin_generate_profile(uid):
if not char:
return await ctx.send("Character name invalid or not in dictionary.")
- with ctx.typing():
+ async with ctx.typing():
return await enka_generate_char_img(uid, char)
diff --git a/genshinutils/register.py b/genshinutils/register.py
index 14e8cd6..108e1ad 100644
--- a/genshinutils/register.py
+++ b/genshinutils/register.py
@@ -54,7 +54,7 @@ def pass_verification(discordtag, signature):
return await ctx.send("Invalid UID provided, it must consist of 9 digits.")
try:
- with ctx.typing():
+ async with ctx.typing():
data = await self.enka_client.fetch_user(uid)
except Exception as exc:
return await ctx.send(f"Unable to retrieve data from enka.network:\n`{exc}`")
From c94c8d37c75bc4e42b5f1b12f816369fd1c8f437 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Sun, 5 Feb 2023 22:06:08 +0800
Subject: [PATCH 39/51] Add pillow into requirements
---
genshinutils/info.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/genshinutils/info.json b/genshinutils/info.json
index 988c49b..881422b 100644
--- a/genshinutils/info.json
+++ b/genshinutils/info.json
@@ -14,7 +14,8 @@
"enkanetwork.py",
"aioenkanetworkcard",
"cryptography",
- "genshin"
+ "genshin",
+ "pillow"
],
"short": "A Genshin Impact utility cog",
"tags": [
From 0c27fcf1997681382d9853f8ca85c74239e5b2fa Mon Sep 17 00:00:00 2001
From: Raiden
Date: Thu, 20 Apr 2023 22:32:00 +0800
Subject: [PATCH 40/51] Switch flake8 to ruff
---
.flake8 | 4 +--
.github/workflows/loadcheck.yml | 17 ++++++-----
.gitignore | 2 ++
.pre-commit-config.yaml | 21 ++-----------
dev-requirements.txt | 21 -------------
pyproject.toml | 53 +++++++++++++++++++++++++++++++++
6 files changed, 69 insertions(+), 49 deletions(-)
delete mode 100644 dev-requirements.txt
diff --git a/.flake8 b/.flake8
index e480388..d86e497 100644
--- a/.flake8
+++ b/.flake8
@@ -1,7 +1,5 @@
[flake8]
# ignore = E302
per-file-ignores = __init__.py:F401, constants.py:E501
-# So flake8 won't whine when my comments are too long
-# Black and isort still set at 99
-max-line-length = 110
+max-line-length = 99
exclude = .git, .venv, .tox, __pycache__
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index aee3fdd..3d188c8 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -4,7 +4,6 @@ on:
push:
branches:
- main
- pull_request:
jobs:
loadcheck:
@@ -13,13 +12,17 @@ jobs:
matrix:
python-version: ["3.8", "3.9"]
red-version:
- - "Red-DiscordBot==3.4.18"
+ # this workflow required pr #5453 commit d27dbde, which is in dev & pypi 3.4.15+
+ - "git+https://github.com/Cog-Creators/Red-DiscordBot@V3/develop#egg=Red-DiscordBot"
+ - "Red-DiscordBot"
include:
- - red-version: "Red-DiscordBot==3.4.18"
- friendly-red: "Red 3.4.18"
+ - red-version: "git+https://github.com/Cog-Creators/Red-DiscordBot@V3/develop#egg=Red-DiscordBot"
+ friendly-red: "Red (Latest)"
+ - red-version: "Red-DiscordBot"
+ friendly-red: "Red (Stable)"
fail-fast: false
- name: Python ${{ matrix.python-version }} & ${{ matrix.friendly-red }}
+ name: Cog load test - Python ${{ matrix.python-version }} & ${{ matrix.friendly-red }}
steps:
- uses: actions/checkout@v3
@@ -35,7 +38,7 @@ jobs:
path: .venv
key: ${{ matrix.red-version }}-${{ matrix.python-version }}-${{ hashFiles('dev-requirements.txt') }}-${{ secrets.CACHE_V }}
- - name: Make venv if not cached
+ - name: Maybe make venv
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
python3 -m venv .venv
@@ -43,7 +46,7 @@ jobs:
pip install --upgrade pip
pip install setuptools wheel
pip install ${{ matrix.red-version }}
- pip install -r dev-requirements.txt
+ pip install .[dev]
pip install jsonrpc-websocket
- name: Prepare files
run: |
diff --git a/.gitignore b/.gitignore
index d161acc..71ce0f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -153,3 +153,5 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
+
+.ruff_cache
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 347f5e2..3cfc03b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -12,22 +12,7 @@ repos:
rev: "22.12.0"
hooks:
- id: black
- - repo: https://github.com/PyCQA/isort
- rev: "5.12.0"
+ - repo: https://github.com/charliermarsh/ruff-pre-commit
+ rev: 'v0.0.261'
hooks:
- - id: isort
- - repo: https://github.com/myint/autoflake
- rev: "v2.0.0"
- hooks:
- - id: autoflake
- args:
- [
- "--in-place",
- "--remove-unused-variables",
- "--remove-all-unused-imports",
- "--ignore-init-module-imports",
- ]
- - repo: https://github.com/pycqa/flake8
- rev: "6.0.0"
- hooks:
- - id: flake8
+ - id: ruff
diff --git a/dev-requirements.txt b/dev-requirements.txt
deleted file mode 100644
index ba90fee..0000000
--- a/dev-requirements.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-# Red itself is also a requirement, however it is not listed here because
-# some people may have special setups (eg dev version installed)
-
-# Cogs
-tabulate # throw
-pillow # genshinutils
-genshin # genshinutils
-enkanetwork.py # genshinutils
-aioenkanetworkcard # genshinutils
-cryptography # genshinutils
-
-# checking tools
-pyright
-black
-isort
-flake8
-tox
-pre-commit
-
-# checks
-python-dotenv
diff --git a/pyproject.toml b/pyproject.toml
index 41ea6ae..019589e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,47 @@
+[project]
+name = "raiden-cogs"
+version = "1.0.0"
+description = "Cogs for Red bot made by raidensakura"
+readme = "README.md"
+authors = [
+ {name = "raidensakura"},
+]
+license = {file = "LICENSE"}
+requires-python = ">=3.8"
+
+[project.urls]
+"Issue Tracker" = "https://github.com/raidensakura/raiden-cogs/issues"
+"Source Code" = "https://github.com/raidensakura/raiden-cogs"
+
+[options]
+requires = [
+ # throw
+ "tabulate",
+ # genshinutils
+ "pillow",
+ "genshin",
+ "enkanetwork.py",
+ "aioenkanetworkcard",
+ "cryptography",
+]
+
+[project.optional-dependencies]
+dev = [
+ "black",
+ "ruff",
+ "tox",
+ "pre-commit",
+ "python-dotenv"
+]
+
+[tool.setuptools]
+py-modules = [
+ "choose",
+ "genshinutils",
+ "longcat",
+ "throw"
+]
+
[tool.black]
line-length = 99
target-version = ["py38"]
@@ -6,3 +50,12 @@ extend-exclude = ".stubs"
[tool.isort]
profile = "black"
line_length = 99
+
+[tool.ruff]
+target-version = "py38"
+line-length = 99
+select = ["C90", "E", "F", "I001", "PGH004", "RUF100"]
+fix = true
+fixable = ["I001"]
+isort.combine-as-imports = true
+force-exclude = true
From b9e1f2264f02abaec69a3ca4ddbbebf0e675af7b Mon Sep 17 00:00:00 2001
From: Raiden
Date: Thu, 20 Apr 2023 22:33:40 +0800
Subject: [PATCH 41/51] Include branch in load test
---
.github/workflows/loadcheck.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index 3d188c8..7c5d7fb 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -4,6 +4,7 @@ on:
push:
branches:
- main
+ - genshin
jobs:
loadcheck:
From 422e57ef5c97e74f2468b16ccb1798aca0366634 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Thu, 20 Apr 2023 22:40:04 +0800
Subject: [PATCH 42/51] Attempt fix loadcheck failing
---
.github/workflows/loadcheck.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index 7c5d7fb..257e599 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -47,7 +47,7 @@ jobs:
pip install --upgrade pip
pip install setuptools wheel
pip install ${{ matrix.red-version }}
- pip install .[dev]
+ pip install .
pip install jsonrpc-websocket
- name: Prepare files
run: |
From 0a90012e6813a92e2a13c59d155b76564e811bbf Mon Sep 17 00:00:00 2001
From: Raiden
Date: Thu, 20 Apr 2023 23:02:31 +0800
Subject: [PATCH 43/51] Fix deps installation
---
pyproject.toml | 20 ++++++++------------
1 file changed, 8 insertions(+), 12 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 019589e..5db5db2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,23 +8,19 @@ authors = [
]
license = {file = "LICENSE"}
requires-python = ">=3.8"
+dependencies = [
+ "tabulate",
+ "Pillow",
+ "genshin",
+ "enkanetwork.py",
+ "aioenkanetworkcard",
+ "cryptography"
+]
[project.urls]
"Issue Tracker" = "https://github.com/raidensakura/raiden-cogs/issues"
"Source Code" = "https://github.com/raidensakura/raiden-cogs"
-[options]
-requires = [
- # throw
- "tabulate",
- # genshinutils
- "pillow",
- "genshin",
- "enkanetwork.py",
- "aioenkanetworkcard",
- "cryptography",
-]
-
[project.optional-dependencies]
dev = [
"black",
From 0b5b136e39cc2267226a90c673f137ef83c46a65 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Thu, 20 Apr 2023 23:17:29 +0800
Subject: [PATCH 44/51] Attempt number 2
---
pyproject.toml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 5db5db2..50520a5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -14,7 +14,8 @@ dependencies = [
"genshin",
"enkanetwork.py",
"aioenkanetworkcard",
- "cryptography"
+ "cryptography",
+ "python-dotenv"
]
[project.urls]
@@ -26,8 +27,7 @@ dev = [
"black",
"ruff",
"tox",
- "pre-commit",
- "python-dotenv"
+ "pre-commit"
]
[tool.setuptools]
From 91c3efcba815b7a93de3a51ae5c1fb5433159c2d Mon Sep 17 00:00:00 2001
From: Raiden
Date: Thu, 20 Apr 2023 23:40:47 +0800
Subject: [PATCH 45/51] Update README.md
---
README.md | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 6b3225b..398deff 100644
--- a/README.md
+++ b/README.md
@@ -56,18 +56,17 @@
Dev Stuff
Formatting
-For manual formatting, this repo uses these three:
+For manual formatting, this repo uses these:
```py
-pip install -U black isort flake8
-black . ; isort . ; flake8 .
+pip install .[dev]
+black . ; ruff .
```
Pre-commit hooks
Optional but it keeps manual formatting work away from you.
```py
-pip install pre-commit
pre-commit install
```
From 707535769ea95b49503ab895db23cb029ff27853 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Fri, 21 Apr 2023 01:49:16 +0800
Subject: [PATCH 46/51] Implement ABC class
---
genshinutils/abc.py | 14 ++++++++++++++
genshinutils/genshinutils.py | 12 +++++++++++-
2 files changed, 25 insertions(+), 1 deletion(-)
create mode 100644 genshinutils/abc.py
diff --git a/genshinutils/abc.py b/genshinutils/abc.py
new file mode 100644
index 0000000..85cbd5d
--- /dev/null
+++ b/genshinutils/abc.py
@@ -0,0 +1,14 @@
+from abc import ABC
+
+from redbot.core import Config
+from redbot.core.bot import Red
+
+
+class MixinMeta(ABC):
+ """Base class for well behaved type hint detection with composite class.
+ Basically, to keep developers sane when not all attributes are defined in each mixin.
+ """
+
+ def __init__(self, *_args):
+ self.config: Config
+ self.bot: Red
diff --git a/genshinutils/genshinutils.py b/genshinutils/genshinutils.py
index 9d2b34c..f218ebd 100644
--- a/genshinutils/genshinutils.py
+++ b/genshinutils/genshinutils.py
@@ -1,4 +1,5 @@
import logging
+from abc import ABC
from typing import Literal
from discord.ext import tasks
@@ -16,6 +17,11 @@
log = logging.getLogger("red.raidensakura.genshinutils")
+class CompositeMetaClass(type(commands.Cog), type(ABC)):
+ """This allows the metaclass used for proper type detection to coexist with discord.py's
+ metaclass."""
+
+
class GenshinUtils(
GenshinSet,
GenshinRegister,
@@ -23,6 +29,7 @@ class GenshinUtils(
GenshinNotes,
GenshinDaily,
commands.Cog,
+ metaclass=CompositeMetaClass,
):
"""GenshinUtils commands."""
@@ -51,7 +58,10 @@ def format_help_for_context(self, ctx: commands.Context) -> str:
"""Thanks Sinbad!"""
pre_processed = super().format_help_for_context(ctx)
s = "s" if len(self.__author__) > 1 else ""
- return f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}\nCog Version: {self.__version__}"
+ return (
+ f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}"
+ "\nCog Version: {self.__version__}"
+ )
async def red_delete_data_for_user(
self,
From 22e0f744ac388e9afbbd68d0d357fee6738242e0 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Fri, 21 Apr 2023 14:21:30 +0800
Subject: [PATCH 47/51] Shorten long lines
---
genshinutils/profile.py | 9 +++----
genshinutils/register.py | 53 ++++++++++++++++++----------------------
genshinutils/utils.py | 6 ++---
longcat/longcat.py | 5 +++-
pyproject.toml | 4 +++
throw/throw.py | 5 +++-
6 files changed, 43 insertions(+), 39 deletions(-)
diff --git a/genshinutils/profile.py b/genshinutils/profile.py
index 72ce4a8..888fbe6 100644
--- a/genshinutils/profile.py
+++ b/genshinutils/profile.py
@@ -81,14 +81,13 @@ async def enka_generate_profile(uid):
async def enka_generate_char_img(uid, char_name):
with io.BytesIO() as image_binary:
- char_card = await enka_get_character_card(uid, char_name)
+ char_card = await enka_get_character_card(uid)
if not char_card:
- return await ctx.send("This user does not have that character featured.")
+ return await ctx.send("This user does not have any character featured.")
temp_filename = str(time.time()).split(".")[0] + ".png"
log.debug(f"[generate_char_info] Pillow object for character card:\n{char_card}")
- first_card = next(iter(char_card.values()))
- card_object = next(iter(first_card.values()))
- card_object.save(image_binary, "PNG", optimize=True, quality=95)
+ first_card = char_card.get("card").get("1-4")
+ first_card.save(image_binary, "PNG", optimize=True, quality=95)
image_binary.seek(0)
return await ctx.send(file=discord.File(fp=image_binary, filename=temp_filename))
diff --git a/genshinutils/register.py b/genshinutils/register.py
index 108e1ad..4598831 100644
--- a/genshinutils/register.py
+++ b/genshinutils/register.py
@@ -67,7 +67,7 @@ def pass_verification(discordtag, signature):
return await ctx.send(
(
"Your signature does not contain your Discord tag.\n"
- "Note that it may take up to 15 minutes for changes to be reflected."
+ "It may take up to 15 minutes for changes to be reflected."
)
)
await self.config.user(ctx.author).UID.set(uid)
@@ -75,12 +75,14 @@ def pass_verification(discordtag, signature):
"""
Important Notes:
- 1. Command has proprietary DM check since I want it to preface a a disclaimer when run in a server.
- This command deals with sensitive information and I want it to be taken very seriously.
- 2. I fully acknowledge storing the encryption key along with the encrypted data itself is bad practice.
- Hoyolab account token can be used to perform potentially dangerous account actions.
- Since the cog is OSS, the purpose is to prevent bot owners from having plaintext access to them
- in a way such that is require a bit of coding and encryption knowledge to access them on demand.
+ 1. This has proprietary DM check since to preface a disclaimer.
+ 2. I fully acknowledge storing the encryption key along
+ with the encrypted data itself is bad practice.
+ Hoyolab account token can be used to performpotentially
+ dangerous account actions. Since the cog is OSS, the purpose is
+ to prevent bot owners from having plaintext access to them
+ in a way such that is require a bit of coding and encryption
+ knowledge to access them on demand.
"""
@register.command()
@@ -90,7 +92,6 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
"""Link or unlink a Hoyolab account token to your Discord account."""
if not isinstance(ctx.channel, DMChannel):
-
if cookie:
try:
await ctx.message.delete()
@@ -105,11 +106,10 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
owner = app_info.owner
desc = (
"This command links your Hoyolab account token to your Discord account. "
- "Your account token allow the bot to perform various account actions on your behalf, "
+ "This allow the bot to perform various account actions on your behalf, "
"such as claiming daily login, fetching character data etc. "
- "Your token will be stored in the bot's config, and bot owner will always have access to it. "
- "Make sure **you fully understand the risk of sharing your token online** before proceeding."
- "\n\nFor security reason, please run this command in a DM channel when setting token."
+ "Make sure you understand the risk of sharing your token online before proceeding."
+ "\n\nPlease run this command in a DM channel when setting token."
"\n\nRead on how to obtain your token [here](https://project-mei.xyz/genshinutils)."
)
e = generate_embed(
@@ -133,7 +133,8 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
msg = (
f"**Provide a valid cookie to bind your Discord account to.**\n\n"
f"` » ` Instruction on how to obtain your Hoyolab cookie:\n<{cog_url}>\n\n"
- f"` » ` For command help context: `{bot_prefix}help genshin register {command_name}`\n\n"
+ f"` » ` For command help context: "
+ f"`{bot_prefix}help genshin register {command_name}`\n\n"
f"` » ` To read disclaimers, type this command again in any server."
)
return await ctx.send(msg)
@@ -186,8 +187,17 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
await self.config.user(ctx.author).ltuid.set(encoded_ltuid)
await self.config.user(ctx.author).ltoken.set(encoded_ltoken)
+ # Debugging stuff
+ log.debug(f"Encrypted credentials saved for {ctx.author}")
+
+ decoded_ltuid = await decrypt_config(self.config, encoded_ltuid)
+
+ log.debug(f"Decoded ltuid for {ctx.author}: {decoded_ltuid}")
+
# Send success embed
- desc = "Successfully bound a Genshin Impact account to your Discord account. Details are as follow."
+ desc = (
+ "Successfully bound Genshin account to your Discord account. " "Details are as follow."
+ )
e = Embed(
color=(await ctx.embed_colour()),
title="Account Binding Success",
@@ -201,18 +211,3 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
e.set_thumbnail(url=ctx.message.author.avatar_url)
return await ctx.send(embed=e)
-
- # Debugging stuff
- log.debug(
- f"[Register Hoyolab] Encrypted ltuid saved: {await self.config.user(ctx.author).ltuid()}"
- )
- log.debug(
- f"[Register Hoyolab] Encrypted ltoken saved: {await self.config.user(ctx.author).ltoken()}"
- )
- log.debug(f"[Register Hoyolab] Encryption key saved: {await self.config.encryption_key()}")
-
- decoded_ltuid = await decrypt_config(self.config, encoded_ltuid)
- decoded_ltoken = await decrypt_config(self.config, encoded_ltoken)
-
- log.debug(f"[Register Hoyolab] Decoded ltuid: {decoded_ltuid}")
- log.debug(f"[Register Hoyolab] Decoded ltoken: {decoded_ltoken}")
diff --git a/genshinutils/utils.py b/genshinutils/utils.py
index ab2be09..3d50abe 100644
--- a/genshinutils/utils.py
+++ b/genshinutils/utils.py
@@ -92,16 +92,16 @@ def validate_char_name(arg):
return str(formal_name).strip("{'\"}")
-async def enka_get_character_card(uid, char_name):
+async def enka_get_character_card(uid, char_name=None):
"""Generate one or more character build image objects in a dict
:param str uid: UID of the player
:param str char_name: formal name of the character
:return dict: dict containing Pillow image object for the character
"""
- async with encbanner.ENC(lang="en", splashArt=True, characterName=char_name) as encard:
+ async with encbanner.ENC(lang="en", splashArt=True, miniInfo=True) as encard:
ENCpy = await encard.enc(uids=uid)
- return await encard.creat(ENCpy, 2)
+ return await encard.creat(ENCpy, 4)
async def get_user_cookie(config, user):
diff --git a/longcat/longcat.py b/longcat/longcat.py
index ee8b9da..7c50045 100644
--- a/longcat/longcat.py
+++ b/longcat/longcat.py
@@ -27,7 +27,10 @@ def format_help_for_context(self, ctx: commands.Context) -> str:
"""
pre_processed = super().format_help_for_context(ctx)
s = "s" if len(self.__author__) > 1 else ""
- return f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}\nCog Version: {self.__version__}"
+ return (
+ f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}"
+ f"\nCog Version: {self.__version__}"
+ )
async def red_delete_data_for_user(self, **kwargs) -> None:
"""Nothing to delete"""
diff --git a/pyproject.toml b/pyproject.toml
index 50520a5..49547df 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -55,3 +55,7 @@ fix = true
fixable = ["I001"]
isort.combine-as-imports = true
force-exclude = true
+
+[tool.ruff.mccabe]
+# Unlike Flake8, default to a complexity level of 10.
+max-complexity = 25
diff --git a/throw/throw.py b/throw/throw.py
index 17146e8..8ae226d 100644
--- a/throw/throw.py
+++ b/throw/throw.py
@@ -33,7 +33,10 @@ def format_help_for_context(self, ctx: commands.Context) -> str:
"""
pre_processed = super().format_help_for_context(ctx)
s = "s" if len(self.__author__) > 1 else ""
- return f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}\nCog Version: {self.__version__}"
+ return (
+ f"{pre_processed}\n\nAuthor{s}: {', '.join(self.__author__)}"
+ f"\nCog Version: {self.__version__}"
+ )
# TODO: Delete user throw stats
async def red_delete_data_for_user(self, **kwargs):
From 6996b5c26ce56ce272b4cac0cbe252eb9ef58060 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Fri, 21 Apr 2023 14:27:54 +0800
Subject: [PATCH 48/51] pls flake8
---
genshinutils/register.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/genshinutils/register.py b/genshinutils/register.py
index 4598831..d5f834b 100644
--- a/genshinutils/register.py
+++ b/genshinutils/register.py
@@ -76,10 +76,10 @@ def pass_verification(discordtag, signature):
"""
Important Notes:
1. This has proprietary DM check since to preface a disclaimer.
- 2. I fully acknowledge storing the encryption key along
+ 2. I fully acknowledge storing the encryption key along
with the encrypted data itself is bad practice.
Hoyolab account token can be used to performpotentially
- dangerous account actions. Since the cog is OSS, the purpose is
+ dangerous account actions. Since the cog is OSS, the purpose is
to prevent bot owners from having plaintext access to them
in a way such that is require a bit of coding and encryption
knowledge to access them on demand.
@@ -110,7 +110,7 @@ async def hoyolab(self, ctx: commands.Context, *, cookie: str = None):
"such as claiming daily login, fetching character data etc. "
"Make sure you understand the risk of sharing your token online before proceeding."
"\n\nPlease run this command in a DM channel when setting token."
- "\n\nRead on how to obtain your token [here](https://project-mei.xyz/genshinutils)."
+ "\n\nRead how to obtain your token [here](https://project-mei.xyz/genshinutils)."
)
e = generate_embed(
title="Important Disclaimer", desc=desc, color=await ctx.embed_color()
From 54b81329184b5fcd1a174c42663ca4e37bcd1cbb Mon Sep 17 00:00:00 2001
From: Raiden Sakura
Date: Thu, 29 Jun 2023 00:52:46 +0800
Subject: [PATCH 49/51] Update workflow
---
.github/workflows/checks.yml | 12 +++---------
.github/workflows/loadcheck.yml | 6 +-----
tox.ini | 19 ++++++-------------
3 files changed, 10 insertions(+), 27 deletions(-)
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index c8de40f..2a09d39 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -2,8 +2,6 @@ name: Checks
on:
push:
- branches:
- - main
pull_request:
# thanks red or wherever you got it from
@@ -14,18 +12,14 @@ jobs:
matrix:
python_version:
- "3.8"
- - "3.9"
tox_env:
- style-black
- - style-isort
- - lint-flake8
+ - style-ruff
include:
- tox_env: style-black
friendly_name: Style (black)
- - tox_env: style-isort
- friendly_name: Style (isort)
- - tox_env: lint-flake8
- friendly_name: Lint (flake8)
+ - tox_env: style-ruff
+ friendly_name: Style (ruff)
fail-fast: false
diff --git a/.github/workflows/loadcheck.yml b/.github/workflows/loadcheck.yml
index 257e599..7abe9c5 100644
--- a/.github/workflows/loadcheck.yml
+++ b/.github/workflows/loadcheck.yml
@@ -2,18 +2,14 @@ name: "Cog load test"
on:
push:
- branches:
- - main
- - genshin
jobs:
loadcheck:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ["3.8", "3.9"]
+ python-version: ["3.8", "3.9", "3.10", "3.11"]
red-version:
- # this workflow required pr #5453 commit d27dbde, which is in dev & pypi 3.4.15+
- "git+https://github.com/Cog-Creators/Red-DiscordBot@V3/develop#egg=Red-DiscordBot"
- "Red-DiscordBot"
include:
diff --git a/tox.ini b/tox.ini
index cb47ac0..9d53708 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py38, style-black, style-isort, lint-flake8
+envlist = py38, style-black, style-ruff
skipsdist = true
[testenv]
@@ -7,10 +7,9 @@ description = Run style and static type checking.
deps =
# style
black
- isort
+ ruff
# lint
- flake8
gidgethub
wakeonlan
@@ -25,7 +24,7 @@ deps =
pyjson5
expr.py
- red-discordbot==3.4.18
+ red-discordbot
# type
# (some are covered under below)
@@ -38,14 +37,8 @@ envdir = {toxworkdir}/py38
commands = black --check .
-[testenv:style-isort]
-description = Check imports conform with isort.
+[testenv:style-ruff]
+description = Check style conform with ruff.
envdir = {toxworkdir}/py38
-commands = isort --check .
-
-[testenv:lint-flake8]
-description = Lint with flake8.
-envdir = {toxworkdir}/py38
-
-commands = flake8 .
+commands = ruff check .
From 15c55be9690d6fad274950e3ccd54bc9f70d2eb0 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Wed, 12 Jul 2023 00:55:09 +0800
Subject: [PATCH 50/51] Update README.md
---
genshinutils/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/genshinutils/README.md b/genshinutils/README.md
index 3be701e..0d76f40 100644
--- a/genshinutils/README.md
+++ b/genshinutils/README.md
@@ -5,7 +5,7 @@
-
+
GenshinUtils - Multipurpose Genshin Impact cog. For now, it's able to display in-game profile information and featured character build cards.
From a16c482f43f5c475ef8d343cd26459f12f2d04e8 Mon Sep 17 00:00:00 2001
From: Raiden
Date: Wed, 12 Jul 2023 01:01:21 +0800
Subject: [PATCH 51/51] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 398deff..fafe013 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
-
+
A collection of badly written homemade cogs I made for fun in the process of learning Python.