mirror of
https://github.com/PaiGramTeam/PaiGram.git
synced 2024-11-16 04:35:49 +00:00
✨ Use qrcode to add cookies only CN users
This commit is contained in:
parent
343c893a5c
commit
c927b7b86b
@ -1,5 +1,12 @@
|
||||
import json
|
||||
import random
|
||||
from asyncio import sleep
|
||||
from io import BytesIO
|
||||
from string import ascii_letters, digits
|
||||
from typing import Dict
|
||||
|
||||
import qrcode
|
||||
from genshin.utility import generate_cn_dynamic_secret
|
||||
from httpx import AsyncClient
|
||||
|
||||
from ...utility.helpers import get_device_id
|
||||
@ -8,69 +15,25 @@ __all__ = ("SignIn",)
|
||||
|
||||
|
||||
class SignIn:
|
||||
LOGIN_URL = "https://webapi.account.mihoyo.com/Api/login_by_mobilecaptcha"
|
||||
S_TOKEN_URL = (
|
||||
"https://api-takumi.mihoyo.com/auth/api/getMultiTokenByLoginTicket?login_ticket={0}&token_types=3&uid={1}"
|
||||
)
|
||||
BBS_URL = "https://api-takumi.mihoyo.com/account/auth/api/webLoginByMobile"
|
||||
USER_AGENT = (
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
|
||||
"AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15"
|
||||
)
|
||||
HEADERS = {
|
||||
"Host": "webapi.account.mihoyo.com",
|
||||
"Connection": "keep-alive",
|
||||
"sec-ch-ua": '".Not/A)Brand";v="99", "Microsoft Edge";v="103", "Chromium";v="103"',
|
||||
"DNT": "1",
|
||||
"x-rpc-device_model": "OS X 10.15.7",
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"User-Agent": USER_AGENT,
|
||||
"x-rpc-device_id": get_device_id(USER_AGENT),
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"x-rpc-device_name": "Microsoft Edge 103.0.1264.62",
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
"x-rpc-client_type": "4",
|
||||
"sec-ch-ua-platform": '"macOS"',
|
||||
"Origin": "https://user.mihoyo.com",
|
||||
"Sec-Fetch-Site": "same-site",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Referer": "https://user.mihoyo.com/",
|
||||
"Accept-Encoding": "gzip, deflate, br",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||||
}
|
||||
BBS_HEADERS = {
|
||||
"Host": "api-takumi.mihoyo.com",
|
||||
"Content-Type": "application/json;charset=utf-8",
|
||||
"Origin": "https://bbs.mihoyo.com",
|
||||
"Accept-Encoding": "gzip, deflate, br",
|
||||
"Connection": "keep-alive",
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"User-Agent": USER_AGENT,
|
||||
"Referer": "https://bbs.mihoyo.com/",
|
||||
"Accept-Language": "zh-CN,zh-Hans;q=0.9",
|
||||
}
|
||||
AUTHKEY_API = "https://api-takumi.mihoyo.com/binding/api/genAuthKey"
|
||||
USER_INFO_API = "https://bbs-api.mihoyo.com/user/wapi/getUserFullInfo"
|
||||
GACHA_HEADERS = {
|
||||
"User-Agent": "okhttp/4.8.0",
|
||||
"x-rpc-app_version": "2.28.1",
|
||||
"x-rpc-sys_version": "12",
|
||||
"x-rpc-client_type": "5",
|
||||
"x-rpc-channel": "mihoyo",
|
||||
"x-rpc-device_id": get_device_id(USER_AGENT),
|
||||
"x-rpc-device_name": "Mi 10",
|
||||
"x-rpc-device_model": "Mi 10",
|
||||
"Referer": "https://app.mihoyo.com",
|
||||
"Host": "api-takumi.mihoyo.com",
|
||||
}
|
||||
QRCODE_GEN_API = "https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/fetch"
|
||||
QRCODE_GET_API = "https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/query"
|
||||
GAME_TOKEN_API = "https://api-takumi.mihoyo.com/auth/api/getCookieAccountInfoByGameToken"
|
||||
GAME_LTOKEN_API = "https://passport-api.mihoyo.com/account/ma-cn-session/app/getTokenByGameToken"
|
||||
|
||||
def __init__(self, phone: int = 0, uid: int = 0, cookie: Dict = None):
|
||||
self.phone = phone
|
||||
def __init__(self, uid: int = 0, cookie: Dict = None):
|
||||
self.client = AsyncClient()
|
||||
self.uid = uid
|
||||
self.cookie = cookie if cookie is not None else {}
|
||||
self.parse_uid()
|
||||
self.ticket = None
|
||||
self.device_id = None
|
||||
|
||||
def parse_uid(self):
|
||||
"""
|
||||
@ -87,34 +50,6 @@ class SignIn:
|
||||
for item in ["login_uid", "stuid", "ltuid", "account_id"]:
|
||||
self.cookie[item] = self.uid
|
||||
|
||||
@staticmethod
|
||||
def check_error(data: dict) -> bool:
|
||||
"""
|
||||
检查是否有错误
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
res_data = data.get("data", {})
|
||||
return res_data.get("msg") == "验证码错误" or res_data.get("info") == "Captcha not match Err"
|
||||
|
||||
async def login(self, captcha: int) -> bool:
|
||||
data = await self.client.post(
|
||||
self.LOGIN_URL,
|
||||
data={"mobile": str(self.phone), "mobile_captcha": str(captcha), "source": "user.mihoyo.com"},
|
||||
headers=self.HEADERS,
|
||||
)
|
||||
res_json = data.json()
|
||||
if self.check_error(res_json):
|
||||
return False
|
||||
|
||||
for k, v in data.cookies.items():
|
||||
self.cookie[k] = v
|
||||
|
||||
if "login_ticket" not in self.cookie:
|
||||
return False
|
||||
self.parse_uid()
|
||||
return bool(self.uid)
|
||||
|
||||
async def get_s_token(self):
|
||||
if not self.cookie.get("login_ticket") or not self.uid:
|
||||
return
|
||||
@ -127,23 +62,85 @@ class SignIn:
|
||||
if i.get("name") and i.get("token"):
|
||||
self.cookie[i.get("name")] = i.get("token")
|
||||
|
||||
async def get_token(self, captcha: int) -> bool:
|
||||
data = await self.client.post(
|
||||
self.BBS_URL,
|
||||
headers=self.BBS_HEADERS,
|
||||
json={
|
||||
"is_bh2": False,
|
||||
"mobile": str(self.phone),
|
||||
"captcha": str(captcha),
|
||||
"action_type": "login",
|
||||
"token_type": 6,
|
||||
},
|
||||
async def get_ltoken_by_game_token(self, game_token: str):
|
||||
data = {"account_id": self.uid, "game_token": game_token}
|
||||
headers = {
|
||||
"x-rpc-app_version": "2.41.2",
|
||||
"DS": generate_cn_dynamic_secret(body=data, salt="osgT0DljLarYxgebPPHJFjdaxPfoiHGt"),
|
||||
"x-rpc-aigis": "",
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"x-rpc-game_biz": "bbs_cn",
|
||||
"x-rpc-sys_version": "11",
|
||||
"x-rpc-device_id": get_device_id(self.USER_AGENT),
|
||||
"x-rpc-device_fp": "".join(random.choices((ascii_letters + digits), k=13)),
|
||||
"x-rpc-device_name": "Chrome 108.0.0.0",
|
||||
"x-rpc-device_model": "Windows 10 64-bit",
|
||||
"x-rpc-app_id": "bll8iq97cem8",
|
||||
"x-rpc-client_type": "4",
|
||||
"User-Agent": "okhttp/4.8.0",
|
||||
}
|
||||
res = await self.client.post(
|
||||
self.GAME_LTOKEN_API,
|
||||
headers=headers,
|
||||
json={"account_id": self.uid, "game_token": game_token},
|
||||
)
|
||||
res_json = data.json()
|
||||
if self.check_error(res_json):
|
||||
return res.json()
|
||||
|
||||
async def create_login_data(self) -> str:
|
||||
self.device_id = get_device_id("".join(random.choices((ascii_letters + digits), k=64)))
|
||||
data = {"app_id": "4", "device": self.device_id}
|
||||
res = await self.client.post(self.QRCODE_GEN_API, json=data)
|
||||
res_json = res.json()
|
||||
url = res_json.get("data", {}).get("url", "")
|
||||
if not url:
|
||||
return ""
|
||||
self.ticket = url.split("ticket=")[1]
|
||||
return url
|
||||
|
||||
async def get_cookie_token_data(self, game_token: str) -> Dict:
|
||||
res = await self.client.get(
|
||||
self.GAME_TOKEN_API,
|
||||
params={"game_token": game_token, "account_id": self.uid},
|
||||
)
|
||||
return res.json()
|
||||
|
||||
async def set_cookie(self, data: Dict) -> bool:
|
||||
self.cookie = {}
|
||||
game_token = json.loads(data.get("payload", {}).get("raw", "{}"))
|
||||
if not game_token:
|
||||
return False
|
||||
self.uid = int(game_token["uid"])
|
||||
for item in ["login_uid", "stuid", "ltuid", "account_id"]:
|
||||
self.cookie[item] = str(self.uid)
|
||||
cookie_token_data = await self.get_cookie_token_data(game_token["token"])
|
||||
ltoken_data = await self.get_ltoken_by_game_token(game_token["token"])
|
||||
self.cookie["cookie_token"] = cookie_token_data["data"]["cookie_token"]
|
||||
for item in ["account_mid_v2", "ltmid_v2"]:
|
||||
self.cookie[item] = ltoken_data["data"]["user_info"]["mid"]
|
||||
self.cookie["ltoken_v2"] = ltoken_data["data"]["token"]["token"]
|
||||
return True
|
||||
|
||||
for k, v in data.cookies.items():
|
||||
self.cookie[k] = v
|
||||
async def check_login(self):
|
||||
data = {"app_id": "4", "ticket": self.ticket, "device": self.device_id}
|
||||
for _ in range(20):
|
||||
await sleep(10)
|
||||
res = await self.client.post(self.QRCODE_GET_API, json=data)
|
||||
res_json = res.json()
|
||||
ret_code = res_json.get("retcode", 1)
|
||||
if ret_code != 0:
|
||||
print(res_json)
|
||||
return False
|
||||
data = res_json.get("data", {})
|
||||
if data.get("stat", "") == "Confirmed":
|
||||
return await self.set_cookie(res_json.get("data", {}))
|
||||
|
||||
return "cookie_token" in self.cookie or "cookie_token_v2" in self.cookie
|
||||
@staticmethod
|
||||
def generate_qrcode(url: str) -> bytes:
|
||||
qr = qrcode.QRCode(version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4)
|
||||
qr.add_data(url)
|
||||
qr.make(fit=True)
|
||||
img = qr.make_image(fill_color="black", back_color="white")
|
||||
bio = BytesIO()
|
||||
img.save(bio, format="PNG")
|
||||
return bio.getvalue()
|
||||
|
@ -6,6 +6,7 @@ from arkowrapper import ArkoWrapper
|
||||
from genshin import DataNotPublic, GenshinException, InvalidCookies, types
|
||||
from genshin.models import GenshinAccount
|
||||
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, TelegramObject, Update
|
||||
from telegram.constants import ParseMode
|
||||
from telegram.ext import CallbackContext, ConversationHandler, filters
|
||||
from telegram.helpers import escape_markdown
|
||||
|
||||
@ -34,7 +35,7 @@ class AddUserCommandData(TelegramObject):
|
||||
sign_in_client: Optional[SignIn] = None
|
||||
|
||||
|
||||
CHECK_SERVER, CHECK_PHONE, CHECK_CAPTCHA, INPUT_COOKIES, COMMAND_RESULT = range(10100, 10105)
|
||||
CHECK_SERVER, INPUT_COOKIES, COMMAND_RESULT = range(10100, 10103)
|
||||
|
||||
|
||||
class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
||||
@ -85,20 +86,41 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
||||
return CHECK_SERVER
|
||||
|
||||
@conversation.entry_point
|
||||
@handler.command(command="mlogin", filters=filters.ChatType.PRIVATE, block=True)
|
||||
@handler.command("qlogin", filters=filters.ChatType.PRIVATE, block=True)
|
||||
@error_callable
|
||||
async def choose_method(self, update: Update, context: CallbackContext) -> int:
|
||||
async def qrcode_login(self, update: Update, context: CallbackContext):
|
||||
user = update.effective_user
|
||||
message = update.effective_message
|
||||
logger.info(f"用户 {user.full_name}[{user.id}] 绑定账号命令请求")
|
||||
logger.info("用户 %s[%s] 绑定账号命令请求", user.full_name, user.id)
|
||||
add_user_command_data: AddUserCommandData = context.chat_data.get("add_user_command_data")
|
||||
if add_user_command_data is None:
|
||||
cookies_command_data = AddUserCommandData()
|
||||
cookies_command_data.region = RegionEnum.HYPERION
|
||||
context.chat_data["add_user_command_data"] = cookies_command_data
|
||||
text = f'你好 {user.mention_markdown_v2()} {escape_markdown("!该绑定方法仅支持国服,请发送 11 位手机号码!或回复退出取消操作")}'
|
||||
await message.reply_markdown_v2(text)
|
||||
return CHECK_PHONE
|
||||
add_user_command_data = AddUserCommandData()
|
||||
add_user_command_data.region = RegionEnum.HYPERION
|
||||
context.chat_data["add_user_command_data"] = add_user_command_data
|
||||
try:
|
||||
user_info = await self.user_service.get_user_by_id(user.id)
|
||||
except UserNotFoundError:
|
||||
user_info = None
|
||||
if user_info is not None:
|
||||
try:
|
||||
cookies_database_data = await self.cookies_service.get_cookies(user.id, RegionEnum.HYPERION)
|
||||
add_user_command_data.cookies_database_data = cookies_database_data
|
||||
except CookiesNotFoundError:
|
||||
await message.reply_text("你已经绑定UID,如果继续操作会覆盖当前UID。")
|
||||
else:
|
||||
await message.reply_text("警告,你已经绑定Cookie,如果继续操作会覆盖当前Cookie。")
|
||||
add_user_command_data.user = user_info
|
||||
sign_in_client = SignIn()
|
||||
url = await sign_in_client.create_login_data()
|
||||
data = sign_in_client.generate_qrcode(url)
|
||||
text = f"你好 {user.mention_html()} !该绑定方法仅支持国服,请在3分钟内使用米游社扫码并确认进行绑定。"
|
||||
await message.reply_photo(data, caption=text, parse_mode=ParseMode.HTML)
|
||||
if await sign_in_client.check_login():
|
||||
add_user_command_data.cookies = sign_in_client.cookie
|
||||
return await self.check_cookies(update, context)
|
||||
else:
|
||||
await message.reply_markdown_v2("可能是验证码已过期或者你没有同意授权,请重新发送命令进行绑定。")
|
||||
return ConversationHandler.END
|
||||
|
||||
@conversation.state(state=CHECK_SERVER)
|
||||
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True)
|
||||
@ -177,79 +199,6 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
||||
await message.reply_html(help_message, disable_web_page_preview=True)
|
||||
return INPUT_COOKIES
|
||||
|
||||
@conversation.state(state=CHECK_PHONE)
|
||||
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True)
|
||||
@error_callable
|
||||
async def check_phone(self, update: Update, context: CallbackContext) -> int:
|
||||
message = update.effective_message
|
||||
if message.text == "退出":
|
||||
await message.reply_text("退出任务", reply_markup=ReplyKeyboardRemove())
|
||||
return ConversationHandler.END
|
||||
try:
|
||||
if not message.text.startswith("1"):
|
||||
raise ValueError
|
||||
phone = int(message.text)
|
||||
if len(str(phone)) != 11:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
await message.reply_text("手机号码输入错误,请重新输入!或回复退出取消操作")
|
||||
return CHECK_PHONE
|
||||
add_user_command_data: AddUserCommandData = context.chat_data.get("add_user_command_data")
|
||||
add_user_command_data.phone = phone
|
||||
await message.reply_text(
|
||||
"请打开 https://user.mihoyo.com/#/login/captcha ,输入手机号并获取验证码," "然后将收到的验证码发送给我(请不要在网页上进行登录)"
|
||||
)
|
||||
return CHECK_CAPTCHA
|
||||
|
||||
@conversation.state(state=CHECK_CAPTCHA)
|
||||
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True)
|
||||
@error_callable
|
||||
async def check_captcha(self, update: Update, context: CallbackContext) -> int:
|
||||
user = update.effective_user
|
||||
message = update.effective_message
|
||||
if message.text == "退出":
|
||||
await message.reply_text("退出任务", reply_markup=ReplyKeyboardRemove())
|
||||
return ConversationHandler.END
|
||||
try:
|
||||
captcha = int(message.text)
|
||||
if len(str(captcha)) != 6:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
await message.reply_text("验证码输入错误,请重新输入!或回复退出取消操作")
|
||||
return CHECK_CAPTCHA
|
||||
add_user_command_data: AddUserCommandData = context.chat_data.get("add_user_command_data")
|
||||
if not add_user_command_data.sign_in_client:
|
||||
phone = add_user_command_data.phone
|
||||
client = SignIn(phone)
|
||||
try:
|
||||
success = await client.login(captcha)
|
||||
if not success:
|
||||
await message.reply_text("登录失败:可能是验证码错误,注意不要在登录页面使用掉验证码,如果验证码已经使用,请重新获取验证码!")
|
||||
return ConversationHandler.END
|
||||
await client.get_s_token()
|
||||
except Exception as exc: # pylint: disable=W0703
|
||||
logger.error(f"用户 {user.full_name}[{user.id}] 登录失败 {repr(exc)}")
|
||||
await message.reply_text("登录失败:米游社返回了错误的数据,请稍后再试!")
|
||||
return ConversationHandler.END
|
||||
add_user_command_data.sign_in_client = client
|
||||
await message.reply_text(
|
||||
"请再次打开 https://user.mihoyo.com/#/login/captcha ,输入手机号并获取验证码(需要等待一分钟)," "然后将收到的验证码发送给我(请不要在网页上进行登录)"
|
||||
)
|
||||
return CHECK_CAPTCHA
|
||||
else:
|
||||
client = add_user_command_data.sign_in_client
|
||||
try:
|
||||
success = await client.get_token(captcha)
|
||||
if not success:
|
||||
await message.reply_text("登录失败:可能是验证码错误,注意不要在登录页面使用掉验证码,如果验证码已经使用,请重新获取验证码!")
|
||||
return ConversationHandler.END
|
||||
except Exception as exc: # pylint: disable=W0703
|
||||
logger.error(f"用户 {user.full_name}[{user.id}] 登录失败 {repr(exc)}")
|
||||
await message.reply_text("登录失败:米游社返回了错误的数据,请稍后再试!")
|
||||
return ConversationHandler.END
|
||||
add_user_command_data.cookies = client.cookie
|
||||
return await self.check_cookies(update, context)
|
||||
|
||||
@conversation.state(state=INPUT_COOKIES)
|
||||
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True)
|
||||
@error_callable
|
||||
@ -325,7 +274,7 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
||||
return ConversationHandler.END
|
||||
except (AttributeError, ValueError) as exc:
|
||||
logger.warning("用户 %s[%s] Cookies错误", user.full_name, user.id)
|
||||
logger.debug("用户 %s[%s] Cookies错误" % (user.full_name, user.id), exc_info=exc)
|
||||
logger.debug("用户 %s[%s] Cookies错误", user.full_name, user.id, exc_info=exc)
|
||||
await message.reply_text("Cookies错误,请检查是否正确", reply_markup=ReplyKeyboardRemove())
|
||||
return ConversationHandler.END
|
||||
with contextlib.suppress(Exception):
|
||||
@ -347,7 +296,7 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
||||
add_user_command_data.game_uid = user_info.uid
|
||||
reply_keyboard = [["确认", "退出"]]
|
||||
await message.reply_text("获取角色基础信息成功,请检查是否正确!")
|
||||
logger.info(f"用户 {user.full_name}[{user.id}] 获取账号 {user_info.nickname}[{user_info.uid}] 信息成功")
|
||||
logger.info("用户 %s[%s] 获取账号 {%s}[%s] 信息成功", user.full_name, user.id, user_info.nickname, user_info.uid)
|
||||
text = (
|
||||
f"*角色信息*\n"
|
||||
f"角色名称:{escape_markdown(user_info.nickname, version=2)}\n"
|
||||
|
55
poetry.lock
generated
55
poetry.lock
generated
@ -167,7 +167,7 @@ python-versions = ">=3.5"
|
||||
dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
|
||||
docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
|
||||
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
|
||||
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
|
||||
tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
|
||||
|
||||
[[package]]
|
||||
name = "backports.zoneinfo"
|
||||
@ -242,7 +242,7 @@ optional = false
|
||||
python-versions = ">=3.6.0"
|
||||
|
||||
[package.extras]
|
||||
unicode-backport = ["unicodedata2"]
|
||||
unicode_backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
@ -304,7 +304,7 @@ dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version
|
||||
|
||||
[[package]]
|
||||
name = "enkanetwork.py"
|
||||
version = "1.3.0"
|
||||
version = "1.2.10"
|
||||
description = "Library for fetching JSON data from site https://enka.network/"
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -392,8 +392,8 @@ python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "genshin"
|
||||
version = "1.4.0"
|
||||
description = ""
|
||||
version = "1.3.0"
|
||||
description = "An API wrapper for Genshin Impact."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
@ -413,7 +413,7 @@ geetest = ["rsa"]
|
||||
type = "git"
|
||||
url = "https://github.com/thesadru/genshin.py"
|
||||
reference = "HEAD"
|
||||
resolved_reference = "072c7c101b648f7a847ab27a4b1ae47c943ee316"
|
||||
resolved_reference = "6685886c4651e489c0fc145af4a61e826c7750ac"
|
||||
|
||||
[[package]]
|
||||
name = "gitdb"
|
||||
@ -890,6 +890,24 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "qrcode"
|
||||
version = "7.3.1"
|
||||
description = "QR Code image generator"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[package.extras]
|
||||
all = ["pillow", "pytest", "pytest", "pytest-cov", "tox", "zest.releaser[recommended]"]
|
||||
dev = ["pytest", "tox"]
|
||||
maintainer = ["zest.releaser[recommended]"]
|
||||
pil = ["pillow"]
|
||||
test = ["pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "4.3.4"
|
||||
@ -960,7 +978,7 @@ falcon = ["falcon (>=1.4)"]
|
||||
fastapi = ["fastapi (>=0.79.0)"]
|
||||
flask = ["blinker (>=1.1)", "flask (>=0.11)"]
|
||||
httpx = ["httpx (>=0.16.0)"]
|
||||
pure-eval = ["asttokens", "executing", "pure-eval"]
|
||||
pure_eval = ["asttokens", "executing", "pure-eval"]
|
||||
pymongo = ["pymongo (>=3.1)"]
|
||||
pyspark = ["pyspark (>=2.4.4)"]
|
||||
quart = ["blinker (>=1.1)", "quart (>=0.16.1)"]
|
||||
@ -1039,19 +1057,19 @@ aiomysql = ["aiomysql", "greenlet (!=0.4.17)"]
|
||||
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"]
|
||||
asyncio = ["greenlet (!=0.4.17)"]
|
||||
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"]
|
||||
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"]
|
||||
mariadb_connector = ["mariadb (>=1.0.1,!=1.1.2)"]
|
||||
mssql = ["pyodbc"]
|
||||
mssql-pymssql = ["pymssql"]
|
||||
mssql-pyodbc = ["pyodbc"]
|
||||
mssql_pymssql = ["pymssql"]
|
||||
mssql_pyodbc = ["pyodbc"]
|
||||
mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"]
|
||||
mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"]
|
||||
mysql-connector = ["mysql-connector-python"]
|
||||
mysql_connector = ["mysql-connector-python"]
|
||||
oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"]
|
||||
postgresql = ["psycopg2 (>=2.7)"]
|
||||
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
|
||||
postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
|
||||
postgresql-psycopg2binary = ["psycopg2-binary"]
|
||||
postgresql-psycopg2cffi = ["psycopg2cffi"]
|
||||
postgresql_asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
|
||||
postgresql_pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
|
||||
postgresql_psycopg2binary = ["psycopg2-binary"]
|
||||
postgresql_psycopg2cffi = ["psycopg2cffi"]
|
||||
pymysql = ["pymysql", "pymysql (<1)"]
|
||||
sqlcipher = ["sqlcipher3_binary"]
|
||||
|
||||
@ -1294,7 +1312,7 @@ test = ["pytest", "pytest-asyncio", "flaky"]
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "762203f9de83ba0b8003e9e7ba233477e701d234ac0c782635c4870537a4fe93"
|
||||
content-hash = "e1cf967bf3a56d440f3caadfa5bc93bf5b58a218c0a010d15f0fc3db1d8635f7"
|
||||
|
||||
[metadata.files]
|
||||
aiofiles = [
|
||||
@ -1761,6 +1779,7 @@ lxml = [
|
||||
{file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0"},
|
||||
{file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e"},
|
||||
{file = "lxml-4.9.2-cp35-cp35m-win32.whl", hash = "sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df"},
|
||||
{file = "lxml-4.9.2-cp35-cp35m-win_amd64.whl", hash = "sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5"},
|
||||
{file = "lxml-4.9.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53"},
|
||||
{file = "lxml-4.9.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7"},
|
||||
{file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe"},
|
||||
@ -1770,6 +1789,7 @@ lxml = [
|
||||
{file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74"},
|
||||
{file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38"},
|
||||
{file = "lxml-4.9.2-cp36-cp36m-win32.whl", hash = "sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5"},
|
||||
{file = "lxml-4.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3"},
|
||||
{file = "lxml-4.9.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03"},
|
||||
{file = "lxml-4.9.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941"},
|
||||
{file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726"},
|
||||
@ -2147,6 +2167,9 @@ PyYAML = [
|
||||
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
|
||||
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
|
||||
]
|
||||
qrcode = [
|
||||
{file = "qrcode-7.3.1.tar.gz", hash = "sha256:375a6ff240ca9bd41adc070428b5dfc1dcfbb0f2507f1ac848f6cded38956578"},
|
||||
]
|
||||
redis = [
|
||||
{file = "redis-4.3.4-py3-none-any.whl", hash = "sha256:a52d5694c9eb4292770084fa8c863f79367ca19884b329ab574d5cb2036b3e54"},
|
||||
{file = "redis-4.3.4.tar.gz", hash = "sha256:ddf27071df4adf3821c4f2ca59d67525c3a82e5f268bed97b813cb4fabf87880"},
|
||||
|
@ -41,6 +41,7 @@ GitPython = "^3.1.29"
|
||||
openpyxl = "^3.0.10"
|
||||
async-lru = "^1.0.3"
|
||||
thefuzz = "^0.19.0"
|
||||
qrcode = "^7.3.1"
|
||||
|
||||
[tool.poetry.extras]
|
||||
pyro = ["Pyrogram", "TgCrypto"]
|
||||
|
Loading…
Reference in New Issue
Block a user