mirror of
https://github.com/PaiGramTeam/MibooGram.git
synced 2024-11-16 04:45:27 +00:00
✨ 米游社支持直接通过手机号绑定
This commit is contained in:
parent
c6baac1659
commit
babdb90db7
1
.gitignore
vendored
1
.gitignore
vendored
@ -31,6 +31,7 @@ __pycache__/
|
||||
**_test.html
|
||||
test_**.html
|
||||
logs/
|
||||
report/
|
||||
/resources/*/*/test/
|
||||
|
||||
### DotEnv ###
|
||||
|
@ -249,3 +249,123 @@ class YuanShen:
|
||||
:return:
|
||||
"""
|
||||
await self.client.aclose()
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
def __init__(self, phone: int):
|
||||
self.phone = phone
|
||||
self.client = AsyncClient()
|
||||
self.uid = 0
|
||||
self.cookie = {}
|
||||
|
||||
def parse_uid(self):
|
||||
"""
|
||||
从cookie中获取uid
|
||||
:param self:
|
||||
:return:
|
||||
"""
|
||||
if "login_ticket" not in self.cookie:
|
||||
return
|
||||
for item in ["login_uid", "stuid", "ltuid", "account_id"]:
|
||||
if item in self.cookie:
|
||||
self.uid = self.cookie[item]
|
||||
break
|
||||
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
|
||||
|
||||
self.parse_uid()
|
||||
return bool(self.uid)
|
||||
|
||||
async def get_s_token(self):
|
||||
data = await self.client.get(
|
||||
self.S_TOKEN_URL.format(self.cookie["login_ticket"], self.uid),
|
||||
headers={"User-Agent": self.USER_AGENT}
|
||||
)
|
||||
res_json = data.json()
|
||||
res_data = res_json.get("data", {}).get("list", [])
|
||||
for i in res_data:
|
||||
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
|
||||
}
|
||||
)
|
||||
res_json = data.json()
|
||||
if self.check_error(res_json):
|
||||
return False
|
||||
|
||||
for k, v in data.cookies.items():
|
||||
self.cookie[k] = v
|
||||
|
||||
return "cookie_token" in self.cookie
|
||||
|
@ -15,6 +15,7 @@ from core.plugin import Plugin, handler, conversation
|
||||
from core.user.error import UserNotFoundError
|
||||
from core.user.models import User
|
||||
from core.user.services import UserService
|
||||
from modules.apihelper.hyperion import YuanShen
|
||||
from utils.decorators.error import error_callable
|
||||
from utils.decorators.restricts import restricts
|
||||
from utils.log import logger
|
||||
@ -27,9 +28,11 @@ class AddUserCommandData(TelegramObject):
|
||||
region: RegionEnum = RegionEnum.NULL
|
||||
cookies: dict = {}
|
||||
game_uid: int = 0
|
||||
phone: int = 0
|
||||
sign_in_client: Optional[YuanShen.SignIn] = None
|
||||
|
||||
|
||||
CHECK_SERVER, CHECK_COOKIES, COMMAND_RESULT = range(10100, 10103)
|
||||
CHECK_SERVER, CHECK_PHONE, CHECK_CAPTCHA, INPUT_COOKIES, COMMAND_RESULT = range(10100, 10106)
|
||||
|
||||
|
||||
class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
||||
@ -57,6 +60,22 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
||||
await message.reply_markdown_v2(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
|
||||
return CHECK_SERVER
|
||||
|
||||
@conversation.entry_point
|
||||
@handler.command(command='mlogin', filters=filters.ChatType.PRIVATE, block=True)
|
||||
@error_callable
|
||||
async def choose_method(self, update: Update, context: CallbackContext) -> int:
|
||||
user = update.effective_user
|
||||
message = update.effective_message
|
||||
logger.info(f"用户 {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
|
||||
|
||||
@conversation.state(state=CHECK_SERVER)
|
||||
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True)
|
||||
@error_callable
|
||||
@ -109,13 +128,83 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
||||
f"2、复制下方的代码,并将其粘贴在地址栏中,点击右侧箭头\n" \
|
||||
f"`{escape_markdown(javascript_android, version=2, entity_type='code')}`"
|
||||
await message.reply_markdown_v2(help_message, disable_web_page_preview=True)
|
||||
return CHECK_COOKIES
|
||||
return INPUT_COOKIES
|
||||
|
||||
@conversation.state(state=CHECK_COOKIES)
|
||||
@conversation.state(state=CHECK_PHONE)
|
||||
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True)
|
||||
@error_callable
|
||||
async def check_cookies(self, update: Update, context: CallbackContext) -> int:
|
||||
user = update.effective_user
|
||||
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:
|
||||
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 = YuanShen.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:
|
||||
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:
|
||||
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
|
||||
async def input_cookies(self, update: Update, context: CallbackContext) -> int:
|
||||
message = update.effective_message
|
||||
add_user_command_data: AddUserCommandData = context.chat_data.get("add_user_command_data")
|
||||
if message.text == "退出":
|
||||
@ -135,6 +224,15 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
||||
if not cookies:
|
||||
await message.reply_text("Cookies格式有误,请检查", reply_markup=ReplyKeyboardRemove())
|
||||
return ConversationHandler.END
|
||||
add_user_command_data.cookies = cookies
|
||||
return await self.check_cookies(update, context)
|
||||
|
||||
@staticmethod
|
||||
async def check_cookies(update: Update, context: CallbackContext) -> int:
|
||||
user = update.effective_user
|
||||
message = update.effective_message
|
||||
add_user_command_data: AddUserCommandData = context.chat_data.get("add_user_command_data")
|
||||
cookies = add_user_command_data.cookies
|
||||
if add_user_command_data.region == RegionEnum.HYPERION:
|
||||
client = genshin.ChineseClient(cookies=cookies)
|
||||
elif add_user_command_data.region == RegionEnum.HOYOLAB:
|
||||
@ -157,7 +255,6 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
||||
except (AttributeError, ValueError):
|
||||
await message.reply_text("Cookies错误,请检查是否正确", reply_markup=ReplyKeyboardRemove())
|
||||
return ConversationHandler.END
|
||||
add_user_command_data.cookies = cookies
|
||||
add_user_command_data.game_uid = user_info.uid
|
||||
reply_keyboard = [['确认', '退出']]
|
||||
await message.reply_text("获取角色基础信息成功,请检查是否正确!")
|
||||
|
Loading…
Reference in New Issue
Block a user