mirror of
https://github.com/PaiGramTeam/PaiGram.git
synced 2024-11-25 09:37:30 +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
|
||||||
test_**.html
|
test_**.html
|
||||||
logs/
|
logs/
|
||||||
|
report/
|
||||||
/resources/*/*/test/
|
/resources/*/*/test/
|
||||||
|
|
||||||
### DotEnv ###
|
### DotEnv ###
|
||||||
|
@ -249,3 +249,123 @@ class YuanShen:
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
await self.client.aclose()
|
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.error import UserNotFoundError
|
||||||
from core.user.models import User
|
from core.user.models import User
|
||||||
from core.user.services import UserService
|
from core.user.services import UserService
|
||||||
|
from modules.apihelper.hyperion import YuanShen
|
||||||
from utils.decorators.error import error_callable
|
from utils.decorators.error import error_callable
|
||||||
from utils.decorators.restricts import restricts
|
from utils.decorators.restricts import restricts
|
||||||
from utils.log import logger
|
from utils.log import logger
|
||||||
@ -27,9 +28,11 @@ class AddUserCommandData(TelegramObject):
|
|||||||
region: RegionEnum = RegionEnum.NULL
|
region: RegionEnum = RegionEnum.NULL
|
||||||
cookies: dict = {}
|
cookies: dict = {}
|
||||||
game_uid: int = 0
|
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):
|
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))
|
await message.reply_markdown_v2(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
|
||||||
return CHECK_SERVER
|
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)
|
@conversation.state(state=CHECK_SERVER)
|
||||||
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True)
|
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True)
|
||||||
@error_callable
|
@error_callable
|
||||||
@ -109,13 +128,83 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
|||||||
f"2、复制下方的代码,并将其粘贴在地址栏中,点击右侧箭头\n" \
|
f"2、复制下方的代码,并将其粘贴在地址栏中,点击右侧箭头\n" \
|
||||||
f"`{escape_markdown(javascript_android, version=2, entity_type='code')}`"
|
f"`{escape_markdown(javascript_android, version=2, entity_type='code')}`"
|
||||||
await message.reply_markdown_v2(help_message, disable_web_page_preview=True)
|
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)
|
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True)
|
||||||
@error_callable
|
@error_callable
|
||||||
async def check_cookies(self, update: Update, context: CallbackContext) -> int:
|
async def check_phone(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:
|
||||||
|
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
|
message = update.effective_message
|
||||||
add_user_command_data: AddUserCommandData = context.chat_data.get("add_user_command_data")
|
add_user_command_data: AddUserCommandData = context.chat_data.get("add_user_command_data")
|
||||||
if message.text == "退出":
|
if message.text == "退出":
|
||||||
@ -135,6 +224,15 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
|||||||
if not cookies:
|
if not cookies:
|
||||||
await message.reply_text("Cookies格式有误,请检查", reply_markup=ReplyKeyboardRemove())
|
await message.reply_text("Cookies格式有误,请检查", reply_markup=ReplyKeyboardRemove())
|
||||||
return ConversationHandler.END
|
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:
|
if add_user_command_data.region == RegionEnum.HYPERION:
|
||||||
client = genshin.ChineseClient(cookies=cookies)
|
client = genshin.ChineseClient(cookies=cookies)
|
||||||
elif add_user_command_data.region == RegionEnum.HOYOLAB:
|
elif add_user_command_data.region == RegionEnum.HOYOLAB:
|
||||||
@ -157,7 +255,6 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
|
|||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
await message.reply_text("Cookies错误,请检查是否正确", reply_markup=ReplyKeyboardRemove())
|
await message.reply_text("Cookies错误,请检查是否正确", reply_markup=ReplyKeyboardRemove())
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
add_user_command_data.cookies = cookies
|
|
||||||
add_user_command_data.game_uid = user_info.uid
|
add_user_command_data.game_uid = user_info.uid
|
||||||
reply_keyboard = [['确认', '退出']]
|
reply_keyboard = [['确认', '退出']]
|
||||||
await message.reply_text("获取角色基础信息成功,请检查是否正确!")
|
await message.reply_text("获取角色基础信息成功,请检查是否正确!")
|
||||||
|
Loading…
Reference in New Issue
Block a user