feat: 添加扫码登录,cookies复写,修正错误提示,添加成长值显示 (#246)

* feat: 添加扫码登录,cookies复写,修正错误提示

* On branch qr_login

* chore: 遵守代码规范

* chore: not invalid-name

* chore: 添加成长值显示

* imp: 更新版本号

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: 0-8-4 <ljd69154@liangjundi.cn>
This commit is contained in:
Night-stars-1 2023-12-18 19:58:12 +08:00 committed by GitHub
parent 5044bcc744
commit 3991a86a41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 277 additions and 53 deletions

3
.gitignore vendored
View File

@ -142,5 +142,4 @@ dmypy.json
logs logs
data data
.pdm-python .pdm-python
test.py test*.py
test2.py

View File

@ -1,13 +1,15 @@
''' '''
Date: 2023-11-13 20:29:19 Date: 2023-11-13 20:29:19
LastEditors: Night-stars-1 nujj1042633805@gmail.com LastEditors: Night-stars-1 nujj1042633805@gmail.com
LastEditTime: 2023-12-03 01:53:48 LastEditTime: 2023-12-18 19:21:36
''' '''
# new Env("MIUI-Auto-Task") # pylint: disable=missing-module-docstring # new Env("MIUI-Auto-Task") # pylint: disable=missing-module-docstring
# cron 30 8 * * * miuitask.py # cron 30 8 * * * miuitask.py
import asyncio import asyncio
from tenacity import RetryError, Retrying, stop_after_attempt
from utils.api.login import Login from utils.api.login import Login
from utils.api.sign import BaseSign, CheckIn from utils.api.sign import BaseSign, CheckIn
from utils.config import ConfigManager from utils.config import ConfigManager
@ -23,24 +25,33 @@ async def main():
"""启动签到""" """启动签到"""
print_info() print_info()
for account in _conf.accounts: for account in _conf.accounts:
try:
for attempt in Retrying(stop=stop_after_attempt(2)):
with attempt:
login_obj = Login(account) login_obj = Login(account)
if cookies := await login_obj.login(): if cookies := await login_obj.login():
sign_obj = BaseSign(cookies) sign_obj = BaseSign(cookies, account.user_agent)
daily_tasks = await sign_obj.check_daily_tasks() daily_tasks = await sign_obj.check_daily_tasks()
sign_task_obj = sign_obj.AVAILABLE_SIGNS # 签到任务对象合集 sign_task_obj = sign_obj.AVAILABLE_SIGNS # 签到任务对象合集
for task in daily_tasks: for task in daily_tasks:
if not task.showType:
log.info(f"开始执行{task.name}任务") log.info(f"开始执行{task.name}任务")
if task_obj := sign_task_obj.get(task.name): # 签到任务对象 if task.showType:
if getattr(account, task_obj.__name__):
token = await get_token(cookies["cUserId"]) if task_obj == CheckIn else None
await task_obj(cookies, token).sign()
else:
log.info(f"任务{task.name}被禁用")
else:
log.error(f"未找到{task.name}任务")
else:
log.info(f"{task.name}任务已完成") log.info(f"{task.name}任务已完成")
continue
if not (task_obj := sign_task_obj.get(task.name)): # 签到任务对象
log.error(f"未找到{task.name}任务")
continue
if not getattr(account, task_obj.__name__):
log.info(f"任务{task.name}被禁用")
continue
token = await get_token(cookies["cUserId"]) if task_obj == CheckIn else None
status, reason = await task_obj(cookies, token).sign()
if not status and reason == "cookie":
raise ValueError("Cookie失效")
user_info = await sign_obj.user_info()
log.info(f"{user_info.title} 成长值: {user_info.point}")
except RetryError:
...
notify_me(InterceptHandler.message) notify_me(InterceptHandler.message)

View File

@ -100,11 +100,8 @@ dependencies = [
[[package]] [[package]]
name = "onepush" name = "onepush"
version = "1.2.0" version = "1.3.0"
requires_python = ">=3.6" requires_python = ">=3.6"
git = "https://github.com/y1ndan/onepush.git"
ref = "main"
revision = "8b09e62330ad74ba3221bfc2b080d1732a1dcc55"
summary = "A Python library to send notifications to your iPhone, Discord, Telegram, WeChat, QQ and DingTalk." summary = "A Python library to send notifications to your iPhone, Discord, Telegram, WeChat, QQ and DingTalk."
dependencies = [ dependencies = [
"pycryptodome", "pycryptodome",
@ -149,12 +146,28 @@ dependencies = [
"typing-extensions!=4.7.0,>=4.6.0", "typing-extensions!=4.7.0,>=4.6.0",
] ]
[[package]]
name = "pypng"
version = "0.20220715.0"
summary = "Pure Python library for saving and loading PNG images"
[[package]] [[package]]
name = "pyyaml" name = "pyyaml"
version = "6.0.1" version = "6.0.1"
requires_python = ">=3.6" requires_python = ">=3.6"
summary = "YAML parser and emitter for Python" summary = "YAML parser and emitter for Python"
[[package]]
name = "qrcode"
version = "7.4.2"
requires_python = ">=3.7"
summary = "QR Code image generator"
dependencies = [
"colorama; platform_system == \"Windows\"",
"pypng",
"typing-extensions",
]
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.31.0" version = "2.31.0"
@ -207,7 +220,7 @@ summary = "A small Python utility to set file creation time on Windows"
lock_version = "4.2" lock_version = "4.2"
cross_platform = true cross_platform = true
groups = ["default"] groups = ["default"]
content_hash = "sha256:7c922391569ae98b5aba9438d7da9b0ab1b413b23a1e333df2e61bfab038c0ac" content_hash = "sha256:fb7c34d8c74d9043647bf7d200afa5a64602e32c03b558cc9d2fb1e81ad1c1e5"
[metadata.files] [metadata.files]
"annotated-types 0.6.0" = [ "annotated-types 0.6.0" = [
@ -417,6 +430,9 @@ content_hash = "sha256:7c922391569ae98b5aba9438d7da9b0ab1b413b23a1e333df2e61bfab
{url = "https://files.pythonhosted.org/packages/03/0a/4f6fed21aa246c6b49b561ca55facacc2a44b87d65b8b92362a8e99ba202/loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, {url = "https://files.pythonhosted.org/packages/03/0a/4f6fed21aa246c6b49b561ca55facacc2a44b87d65b8b92362a8e99ba202/loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"},
{url = "https://files.pythonhosted.org/packages/9e/30/d87a423766b24db416a46e9335b9602b054a72b96a88a241f2b09b560fa8/loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, {url = "https://files.pythonhosted.org/packages/9e/30/d87a423766b24db416a46e9335b9602b054a72b96a88a241f2b09b560fa8/loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"},
] ]
"onepush 1.3.0" = [
{url = "https://files.pythonhosted.org/packages/e1/a2/1d65907128f1b7980d7da0e68b134298ced50d38df8ddf5133e32c8e9f99/onepush-1.3.0-py3-none-any.whl", hash = "sha256:196af2e147a54381b5ffa32565f5f3955a0647328cc283812ce2f87f4bc09f47"},
]
"orjson 3.9.10" = [ "orjson 3.9.10" = [
{url = "https://files.pythonhosted.org/packages/03/96/4fd0da4f4a5a450054e69439875b4e856654dcbbfea6907d7753b827c937/orjson-3.9.10-cp312-none-win_amd64.whl", hash = "sha256:3e892621434392199efb54e69edfff9f699f6cc36dd9553c5bf796058b14b20d"}, {url = "https://files.pythonhosted.org/packages/03/96/4fd0da4f4a5a450054e69439875b4e856654dcbbfea6907d7753b827c937/orjson-3.9.10-cp312-none-win_amd64.whl", hash = "sha256:3e892621434392199efb54e69edfff9f699f6cc36dd9553c5bf796058b14b20d"},
{url = "https://files.pythonhosted.org/packages/09/33/d090754faab1a63ecf80b1df220d6787605caefd570331c757a3553afbf2/orjson-3.9.10-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:92af0d00091e744587221e79f68d617b432425a7e59328ca4c496f774a356071"}, {url = "https://files.pythonhosted.org/packages/09/33/d090754faab1a63ecf80b1df220d6787605caefd570331c757a3553afbf2/orjson-3.9.10-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:92af0d00091e744587221e79f68d617b432425a7e59328ca4c496f774a356071"},
@ -618,6 +634,10 @@ content_hash = "sha256:7c922391569ae98b5aba9438d7da9b0ab1b413b23a1e333df2e61bfab
{url = "https://files.pythonhosted.org/packages/fd/81/7bcde32ca3434ec80861205d5f949fe28247af9712dd7da940f08445c45d/pydantic_core-2.14.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27828f0227b54804aac6fb077b6bb48e640b5435fdd7fbf0c274093a7b78b69c"}, {url = "https://files.pythonhosted.org/packages/fd/81/7bcde32ca3434ec80861205d5f949fe28247af9712dd7da940f08445c45d/pydantic_core-2.14.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27828f0227b54804aac6fb077b6bb48e640b5435fdd7fbf0c274093a7b78b69c"},
{url = "https://files.pythonhosted.org/packages/ff/1b/937923cfa9df349c632ddead46e57c996103f9bfe904f5ff6cc87472df6e/pydantic_core-2.14.3-cp39-none-win32.whl", hash = "sha256:caa94726791e316f0f63049ee00dff3b34a629b0d099f3b594770f7d0d8f1f56"}, {url = "https://files.pythonhosted.org/packages/ff/1b/937923cfa9df349c632ddead46e57c996103f9bfe904f5ff6cc87472df6e/pydantic_core-2.14.3-cp39-none-win32.whl", hash = "sha256:caa94726791e316f0f63049ee00dff3b34a629b0d099f3b594770f7d0d8f1f56"},
] ]
"pypng 0.20220715.0" = [
{url = "https://files.pythonhosted.org/packages/3e/b9/3766cc361d93edb2ce81e2e1f87dd98f314d7d513877a342d31b30741680/pypng-0.20220715.0-py3-none-any.whl", hash = "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c"},
{url = "https://files.pythonhosted.org/packages/93/cd/112f092ec27cca83e0516de0a3368dbd9128c187fb6b52aaaa7cde39c96d/pypng-0.20220715.0.tar.gz", hash = "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"},
]
"pyyaml 6.0.1" = [ "pyyaml 6.0.1" = [
{url = "https://files.pythonhosted.org/packages/02/74/b2320ebe006b6a521cf929c78f12a220b9db319b38165023623ed195654b/PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {url = "https://files.pythonhosted.org/packages/02/74/b2320ebe006b6a521cf929c78f12a220b9db319b38165023623ed195654b/PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{url = "https://files.pythonhosted.org/packages/03/5c/c4671451b2f1d76ebe352c0945d4cd13500adb5d05f5a51ee296d80152f7/PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {url = "https://files.pythonhosted.org/packages/03/5c/c4671451b2f1d76ebe352c0945d4cd13500adb5d05f5a51ee296d80152f7/PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
@ -670,6 +690,10 @@ content_hash = "sha256:7c922391569ae98b5aba9438d7da9b0ab1b413b23a1e333df2e61bfab
{url = "https://files.pythonhosted.org/packages/f1/26/55e4f21db1f72eaef092015d9017c11510e7e6301c62a6cfee91295d13c6/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {url = "https://files.pythonhosted.org/packages/f1/26/55e4f21db1f72eaef092015d9017c11510e7e6301c62a6cfee91295d13c6/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{url = "https://files.pythonhosted.org/packages/fe/88/def2e57fe740544f2eefb1645f1d6e0094f56c00f4eade708140b6137ead/PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, {url = "https://files.pythonhosted.org/packages/fe/88/def2e57fe740544f2eefb1645f1d6e0094f56c00f4eade708140b6137ead/PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
] ]
"qrcode 7.4.2" = [
{url = "https://files.pythonhosted.org/packages/24/79/aaf0c1c7214f2632badb2771d770b1500d3d7cbdf2590ae62e721ec50584/qrcode-7.4.2-py3-none-any.whl", hash = "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a"},
{url = "https://files.pythonhosted.org/packages/30/35/ad6d4c5a547fe9a5baf85a9edbafff93fc6394b014fab30595877305fa59/qrcode-7.4.2.tar.gz", hash = "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845"},
]
"requests 2.31.0" = [ "requests 2.31.0" = [
{url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
{url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, {url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},

View File

@ -13,6 +13,7 @@ dependencies = [
"tenacity>=8.2.3", "tenacity>=8.2.3",
"tzdata>=2023.3", "tzdata>=2023.3",
"onepush>=1.3.0", "onepush>=1.3.0",
"qrcode>=7.4.2",
] ]
requires-python = ">=3.11" requires-python = ">=3.11"
license = {text = "MIT"} license = {text = "MIT"}

View File

@ -3,8 +3,9 @@ Date: 2023-11-12 14:05:06
LastEditors: Night-stars-1 nujj1042633805@gmail.com LastEditors: Night-stars-1 nujj1042633805@gmail.com
LastEditTime: 2023-11-13 12:32:26 LastEditTime: 2023-11-13 12:32:26
""" """
import time
from os import getenv from os import getenv
from typing import Dict, Union from typing import Dict, Optional, Tuple, Union
import orjson import orjson
@ -13,6 +14,8 @@ from ..data_model import LoginResultHandler
from ..logger import log from ..logger import log
from ..request import get, post from ..request import get, post
from .sign import BaseSign from .sign import BaseSign
from ..utils import generate_qrcode
class Login: class Login:
"""登录类""" """登录类"""
@ -24,7 +27,7 @@ class Login:
self.password = account.password self.password = account.password
self.cookies = account.cookies self.cookies = account.cookies
async def login(self) -> Union[Dict[str, str], bool]: async def login(self) -> Union[Dict[str, str], bool]: # pylint: disable=too-many-return-statements
"""登录小米账号""" """登录小米账号"""
headers = { headers = {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
@ -68,9 +71,17 @@ class Login:
repo_owner = getenv('GITHUB_REPOSITORY_OWNER') repo_owner = getenv('GITHUB_REPOSITORY_OWNER')
if repo_owner not in [None, "0-8-4"]: if repo_owner not in [None, "0-8-4"]:
return False return False
if self.cookies != {} and await BaseSign(self.cookies).check_daily_tasks(nolog=True) != []: if self.cookies != {} and await BaseSign(self.cookies, self.user_agent).check_daily_tasks(nolog=True) != []:
log.info("Cookie有效跳过登录") log.info("Cookie有效跳过登录")
return self.cookies return self.cookies
elif self.cookies.get("passToken") and \
(cookies := await self.get_cookies_by_passtk(user_id=self.uid,
pass_token=self.cookies["passToken"])):
log.info("Cookie无效重新复写")
self.cookies.update(cookies)
self.account.cookies = self.cookies
write_plugin_data()
return cookies
response = await post('https://account.xiaomi.com/pass/serviceLoginAuth2', headers=headers, data=data) response = await post('https://account.xiaomi.com/pass/serviceLoginAuth2', headers=headers, data=data)
log.debug(response.text) log.debug(response.text)
result = response.text.lstrip('&').lstrip('START').lstrip('&') result = response.text.lstrip('&').lstrip('START').lstrip('&')
@ -83,12 +94,19 @@ class Login:
self.account.cookies = cookies self.account.cookies = cookies
write_plugin_data() write_plugin_data()
return cookies return cookies
elif not api_data.pwd_wrong: elif api_data.pwd_wrong:
log.error(f'小米账号登录失败:{api_data.message}') log.error('小米账号登录失败:用户名或密码不正确')
check_url = await self.qr_login()
userid, cookies = await self.check_login(check_url)
self.cookies.update(cookies)
self.account.cookies = self.cookies
self.account.uid = userid
write_plugin_data()
return cookies
elif api_data.need_captcha: elif api_data.need_captcha:
log.error('当前账号需要短信验证码, 请尝试修改UA或设备ID') log.error('当前账号需要短信验证码, 请尝试修改UA或设备ID')
else: else:
log.error('小米账号登录失败:用户名或密码不正确') log.error(f'小米账号登录失败:{api_data.message}')
return False return False
except Exception: # pylint: disable=broad-exception-caught except Exception: # pylint: disable=broad-exception-caught
log.exception("登录小米账号出错") log.exception("登录小米账号出错")
@ -104,3 +122,103 @@ class Login:
log.exception("社区获取 Cookie 失败") log.exception("社区获取 Cookie 失败")
return False return False
async def get_cookies_by_passtk(self, user_id: str, pass_token: str) -> Union[Dict[str, str], bool]:
"""使用passToken获取签到cookies"""
try:
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Pragma': 'no-cache',
'Referer': 'https://web.vip.miui.com/',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-site',
'Upgrade-Insecure-Requests': '1',
'User-Agent': self.user_agent,
'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
params = {
'destUrl': 'https://web.vip.miui.com/page/info/mio/mio/checkIn?app_version=dev.230904',
'time': round(time.time() * 1000),
}
cookies = {
"userId": user_id,
"passToken": pass_token
}
response = await get('https://api.vip.miui.com/page/login', params=params, headers=headers)
url = response.headers.get("location")
response = await get(url, cookies=cookies, headers=headers)
url = response.headers.get("location")
response = await get(url, cookies=cookies, headers=headers)
return dict(response.cookies)
except Exception: # pylint: disable=broad-exception-caught
log.exception("从passToken获取 Cookie 失败")
return {}
async def qr_login(self) -> Tuple[str, bytes]:
"""二维码登录"""
headers = {
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Pragma': 'no-cache',
'Referer': 'https://account.xiaomi.com/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0',
'X-Requested-With': 'XMLHttpRequest',
'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
response = await get(
'https://account.xiaomi.com/longPolling/loginUrl?_group=DEFAULT&_qrsize=240&qs=%253Fcallback%253Dhttps%25253A%25252F%25252Faccount.xiaomi.com%25252Fsts%25253Fsign%25253DZvAtJIzsDsFe60LdaPa76nNNP58%2525253D%252526followup%25253Dhttps%2525253A%2525252F%2525252Faccount.xiaomi.com%2525252Fpass%2525252Fauth%2525252Fsecurity%2525252Fhome%252526sid%25253Dpassport%2526sid%253Dpassport%2526_group%253DDEFAULT&bizDeviceType=&callback=https:%2F%2Faccount.xiaomi.com%2Fsts%3Fsign%3DZvAtJIzsDsFe60LdaPa76nNNP58%253D%26followup%3Dhttps%253A%252F%252Faccount.xiaomi.com%252Fpass%252Fauth%252Fsecurity%252Fhome%26sid%3Dpassport&theme=&sid=passport&needTheme=false&showActiveX=false&serviceParam=%7B%22checkSafePhone%22:false,%22checkSafeAddress%22:false,%22lsrp_score%22:0.0%7D&_locale=zh_CN&_sign=2%26V1_passport%26BUcblfwZ4tX84axhVUaw8t6yi2E%3D&_dc=1702105962382', # pylint: disable=line-too-long
headers=headers,
)
result = response.text.replace("&&&START&&&", "")
data = orjson.loads(result) # pylint: disable=no-member
login_url = data["loginUrl"]
check_url = data["lp"]
generate_qrcode(login_url)
return check_url
async def check_login(self, url: str) -> Tuple[Optional[int], Optional[dict]]:
"""检查扫码登录状态"""
try:
headers = {
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Pragma': 'no-cache',
'Referer': 'https://account.xiaomi.com/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0',
'X-Requested-With': 'XMLHttpRequest',
'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
response = await get(url, headers=headers)
result = response.text.replace("&&&START&&&", "")
data = orjson.loads(result) # pylint: disable=no-member
pass_token = data["passToken"]
user_id = str(data["userId"])
cookies = await self.get_cookies_by_passtk(user_id=user_id, pass_token=pass_token)
cookies.update({
"passToken": pass_token
})
return user_id, cookies
except Exception: # pylint: disable=broad-exception-caught
return None, None

View File

@ -2,10 +2,10 @@
import time import time
from typing import Dict, List, Optional, Type, Union, Any from typing import Dict, List, Optional, Type, Union, Any, Tuple
from tenacity import RetryError, Retrying, stop_after_attempt from tenacity import RetryError, Retrying, stop_after_attempt
from ..data_model import ApiResultHandler, DailyTasksResult, SignResultHandler from ..data_model import ApiResultHandler, DailyTasksResult, SignResultHandler, UserInfoResult
from ..request import get, post from ..request import get, post
from ..logger import log from ..logger import log
from ..utils import is_incorrect_return from ..utils import is_incorrect_return
@ -30,9 +30,10 @@ class BaseSign:
AVAILABLE_SIGNS: Dict[str, Type["BaseSign"]] = {} AVAILABLE_SIGNS: Dict[str, Type["BaseSign"]] = {}
"""可用的子类""" """可用的子类"""
def __init__(self, cookie: Dict, token: Optional[str] = None): def __init__(self, cookies: Dict, user_agent: str, token: Optional[str] = None):
self.cookie = cookie self.cookies = cookies
self.token = token self.token = token
self.user_agent = user_agent
self.headers = { self.headers = {
} }
@ -42,7 +43,7 @@ class BaseSign:
for attempt in Retrying(stop=stop_after_attempt(3)): for attempt in Retrying(stop=stop_after_attempt(3)):
with attempt: with attempt:
response = await get('https://api.vip.miui.com/mtop/planet/vip/member/getCheckinPageCakeList', response = await get('https://api.vip.miui.com/mtop/planet/vip/member/getCheckinPageCakeList',
cookies=self.cookie) cookies=self.cookies)
log.debug(response.text) log.debug(response.text)
result = response.json() result = response.json()
api_data = ApiResultHandler(result) api_data = ApiResultHandler(result)
@ -68,7 +69,7 @@ class BaseSign:
log.exception("获取每日任务异常") log.exception("获取每日任务异常")
return [] return []
async def sign(self) -> bool: async def sign(self) -> Tuple[bool, str]:
""" """
每日任务处理器 每日任务处理器
""" """
@ -76,19 +77,19 @@ class BaseSign:
for attempt in Retrying(stop=stop_after_attempt(3)): for attempt in Retrying(stop=stop_after_attempt(3)):
with attempt: with attempt:
params = self.PARAMS.copy() params = self.PARAMS.copy()
params['miui_vip_ph'] = self.cookie['miui_vip_ph'] if 'miui_vip_ph' in self.cookie else params params['miui_vip_ph'] = self.cookies['miui_vip_ph'] if 'miui_vip_ph' in self.cookies else params
params['token'] = self.token if 'token' in params else params params['token'] = self.token if 'token' in params else params
data = self.DATA.copy() data = self.DATA.copy()
data['miui_vip_ph'] = self.cookie['miui_vip_ph'] if 'miui_vip_ph' in self.cookie else data data['miui_vip_ph'] = self.cookies['miui_vip_ph'] if 'miui_vip_ph' in self.cookies else data
if 'token' in data: if 'token' in data:
if self.token: if self.token:
data['token'] = self.token data['token'] = self.token
else: else:
log.info(f"未获取到token, 跳过{self.NAME}") log.info(f"未获取到token, 跳过{self.NAME}")
return False return False, "None"
response = await post(self.URL_SIGN, response = await post(self.URL_SIGN,
params=params, data=data, params=params, data=data,
cookies=self.cookie, headers=self.headers) cookies=self.cookies, headers=self.headers)
log.debug(response.text) log.debug(response.text)
result = response.json() result = response.json()
api_data = SignResultHandler(result) api_data = SignResultHandler(result)
@ -97,20 +98,53 @@ class BaseSign:
log.success(f"{self.NAME}结果: 成长值+{api_data.growth}") log.success(f"{self.NAME}结果: 成长值+{api_data.growth}")
else: else:
log.success(f"{self.NAME}结果: {api_data.message}") log.success(f"{self.NAME}结果: {api_data.message}")
return True return True, "None"
elif api_data.ck_invalid: elif api_data.ck_invalid:
log.error(f"{self.NAME}失败: Cookie无效") log.error(f"{self.NAME}失败: Cookie无效")
return False return False, "cookie"
else: else:
log.error(f"{self.NAME}失败:{api_data.message}") log.error(f"{self.NAME}失败:{api_data.message}")
return False return False, "None"
except RetryError as error: except RetryError as error:
if is_incorrect_return(error): if is_incorrect_return(error):
log.exception(f"{self.NAME} - 服务器没有正确返回 {response.text}") log.exception(f"{self.NAME} - 服务器没有正确返回 {response.text}")
else: else:
log.exception("{self.NAME}出错") log.exception("{self.NAME}出错")
return False return False, "None"
async def user_info(self) -> UserInfoResult:
"""获取用户信息"""
try:
for attempt in Retrying(stop=stop_after_attempt(3)):
with attempt:
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': self.user_agent,
'Request-Container-Mark': 'android',
'Host': 'api.vip.miui.com',
'Connection': 'Keep-Alive',
}
response = await get(
'https://api.vip.miui.com/mtop/planet/vip/homepage/mineInfo',
cookies=self.cookies,
headers=headers,
)
log.debug(response.text)
result = response.json()
api_data = ApiResultHandler(result)
if api_data.success:
return UserInfoResult.model_validate(api_data.data)
else:
log.error(f"获取用户信息失败:{api_data.message}")
return UserInfoResult()
except RetryError as error:
if is_incorrect_return(error):
log.exception(f"用户信息 - 服务器没有正确返回 {response.text}")
else:
log.exception("获取用户信息异常")
return UserInfoResult()
#pylint: disable=trailing-whitespace
class CheckIn(BaseSign): class CheckIn(BaseSign):
""" """
每日签到 每日签到

View File

@ -8,7 +8,7 @@ from typing import Dict, List, Optional, Union
import orjson import orjson
import yaml # pylint: disable=wrong-import-order import yaml # pylint: disable=wrong-import-order
from pydantic import BaseModel, ValidationError, field_validator from pydantic import BaseModel, ValidationError, field_validator # pylint: disable=no-name-in-module
from .logger import log from .logger import log

View File

@ -1,6 +1,6 @@
"""数据处理模型""" """数据处理模型"""
from typing import (Any, Dict, NamedTuple, Optional) from typing import (Any, Dict, NamedTuple, Optional)
from pydantic import BaseModel from pydantic import BaseModel # pylint: disable=no-name-in-module
class ApiResultHandler(BaseModel): class ApiResultHandler(BaseModel):
@ -42,7 +42,7 @@ class ApiResultHandler(BaseModel):
""" """
是否成功 是否成功
""" """
return self.status in [0, 200] or self.message in ["成功", "OK", "success"] return (self.status in [0, 200] or self.message in ["成功", "OK", "success"]) and not self.content.get("notificationUrl")
class LoginResultHandler(ApiResultHandler): class LoginResultHandler(ApiResultHandler):
@ -65,7 +65,7 @@ class LoginResultHandler(ApiResultHandler):
""" """
是否需要验证码 是否需要验证码
""" """
return self.status == 87001 or "验证码" in self.message return self.status == 87001 or "验证码" in self.message or self.content.get("notificationUrl")
@property @property
def pwd_wrong(self): def pwd_wrong(self):
@ -146,3 +146,17 @@ class GeetestResult(NamedTuple):
"""人机验证结果数据""" """人机验证结果数据"""
validate: str validate: str
challenge: str challenge: str
class UserInfoResult(BaseModel):
"""用户信息数据"""
title: str = "未知"
"""等级名称"""
point: int = 0
"""积分"""
def __init__(self, **kwargs):
if isinstance(kwargs, dict) and kwargs:
kwargs = kwargs.get("userInfo", {}).get("userGrowLevelInfo")
super().__init__(**kwargs)
else:
super().__init__()

View File

@ -10,7 +10,7 @@ from utils.logger import log
def print_info(): def print_info():
"""打印系统信息""" """打印系统信息"""
log.info("MIUI-AUTO-TASK v1.7.3") log.info("MIUI-AUTO-TASK v1.7.4")
log.info('---------- 系统信息 -------------') log.info('---------- 系统信息 -------------')
system_info() system_info()
log.info('---------- 项目信息 -------------') log.info('---------- 项目信息 -------------')

View File

@ -2,9 +2,11 @@
import base64 import base64
import random import random
import time import time
from io import BytesIO
from typing import Type from typing import Type
from urllib.parse import parse_qsl, urlparse from urllib.parse import parse_qsl, urlparse
import qrcode
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding, serialization from cryptography.hazmat.primitives import padding, serialization
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
@ -239,3 +241,24 @@ async def get_token(uid: str) -> str | bool:
else: else:
log.exception("获取TOKEN异常") log.exception("获取TOKEN异常")
return False return False
def generate_qrcode(url):
"""生成二维码"""
qr = qrcode.QRCode(version=1, # pylint: disable=invalid-name
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)
# 获取二维码的模块 (module) 列表
qr_modules = qr.get_matrix()
chaes = [" ", "██"]
# 在控制台中打印二维码
for row in qr_modules:
line = "".join(chaes[pixel] for pixel in row)
print(line)
log.debug(line)