米游社支持直接通过手机号绑定

This commit is contained in:
omg-xtao 2022-09-17 22:58:54 +08:00 committed by GitHub
parent c6baac1659
commit babdb90db7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 225 additions and 7 deletions

1
.gitignore vendored
View File

@ -31,6 +31,7 @@ __pycache__/
**_test.html
test_**.html
logs/
report/
/resources/*/*/test/
### DotEnv ###

View File

@ -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

View File

@ -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("获取角色基础信息成功,请检查是否正确!")