🔒️ Impore sign in group security

* 🔒️ 提高SIGN功能在群组使用的安全性
This commit is contained in:
omg-xtao 2022-10-30 19:37:57 +08:00 committed by GitHub
parent 08f2e636ef
commit 53f30a8f85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 12 deletions

View File

@ -9,7 +9,7 @@ from genshin import Game, GenshinException, AlreadyClaimed, Client
from httpx import AsyncClient, TimeoutException from httpx import AsyncClient, TimeoutException
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction from telegram.constants import ChatAction
from telegram.ext import CommandHandler, CallbackContext from telegram.ext import CommandHandler, CallbackContext, CallbackQueryHandler
from telegram.ext import MessageHandler, filters from telegram.ext import MessageHandler, filters
from core.admin.services import BotAdminService from core.admin.services import BotAdminService
@ -36,12 +36,16 @@ class SignRedis:
qname = "plugin:sign:" qname = "plugin:sign:"
@staticmethod @staticmethod
async def get(uid: int) -> Optional[bytes]: async def get(uid: int) -> Tuple[Optional[str], Optional[str]]:
return await SignRedis.client.get(f"{SignRedis.qname}{uid}") data = await SignRedis.client.get(f"{SignRedis.qname}{uid}")
if not data:
return None, None
data = data.decode("utf-8").split("|")
return data[0], data[1]
@staticmethod @staticmethod
async def set(uid: int, challenge: str): async def set(uid: int, gt: str, challenge: str):
await SignRedis.client.set(f"{SignRedis.qname}{uid}", challenge) await SignRedis.client.set(f"{SignRedis.qname}{uid}", f"{gt}|{challenge}")
await SignRedis.client.expire(f"{SignRedis.qname}{uid}", 10 * 60) await SignRedis.client.expire(f"{SignRedis.qname}{uid}", 10 * 60)
@ -170,25 +174,33 @@ class Sign(Plugin, BasePlugin):
@staticmethod @staticmethod
async def gen_challenge_header(uid: int, validate: str) -> Optional[Dict]: async def gen_challenge_header(uid: int, validate: str) -> Optional[Dict]:
challenge = await SignRedis.get(uid) _, challenge = await SignRedis.get(uid)
if not challenge or not validate: if not challenge or not validate:
return return
return { return {
"x-rpc-challenge": challenge.decode("utf-8"), "x-rpc-challenge": challenge,
"x-rpc-validate": validate, "x-rpc-validate": validate,
"x-rpc-seccode": f"{validate}|jordan", "x-rpc-seccode": f"{validate}|jordan",
} }
@staticmethod @staticmethod
async def gen_challenge_button(uid: int, gt: str, challenge: str): async def gen_challenge_button(uid: int, user_id: int, gt: str = None, challenge: str = None):
if not config.pass_challenge_user_web: if not config.pass_challenge_user_web:
return None return None
await SignRedis.set(uid, challenge) if gt and challenge:
await SignRedis.set(uid, gt, challenge)
data = f"sign|{user_id}|{uid}"
return InlineKeyboardMarkup([[InlineKeyboardButton("请尽快点我进行手动验证", callback_data=data)]])
gt, challenge = await SignRedis.get(uid)
if not gt or not challenge:
return
url = f"{config.pass_challenge_user_web}?username={bot.app.bot.username}&gt={gt}&challenge={challenge}" url = f"{config.pass_challenge_user_web}?username={bot.app.bot.username}&gt={gt}&challenge={challenge}"
return InlineKeyboardMarkup([[InlineKeyboardButton("请尽快点我进行手动验证", url=url)]]) return InlineKeyboardMarkup([[InlineKeyboardButton("请尽快点我进行手动验证", url=url)]])
@staticmethod @staticmethod
async def start_sign(client: Client, headers: Dict = None) -> Tuple[str, Optional[InlineKeyboardMarkup]]: async def start_sign(
client: Client, user_id: int, headers: Dict = None
) -> Tuple[str, Optional[InlineKeyboardMarkup]]:
try: try:
rewards = await client.get_monthly_rewards(game=Game.GENSHIN, lang="zh-cn") rewards = await client.get_monthly_rewards(game=Game.GENSHIN, lang="zh-cn")
except GenshinException as error: except GenshinException as error:
@ -223,6 +235,7 @@ class Sign(Plugin, BasePlugin):
if request_daily_reward and request_daily_reward.get("success", 0) == 1: if request_daily_reward and request_daily_reward.get("success", 0) == 1:
button = await Sign.gen_challenge_button( button = await Sign.gen_challenge_button(
client.uid, client.uid,
user_id,
request_daily_reward.get("gt", ""), request_daily_reward.get("gt", ""),
request_daily_reward.get("challenge", ""), request_daily_reward.get("challenge", ""),
) )
@ -324,7 +337,7 @@ class Sign(Plugin, BasePlugin):
client = await get_genshin_client(user.id) client = await get_genshin_client(user.id)
headers = await Sign.gen_challenge_header(client.uid, validate) headers = await Sign.gen_challenge_header(client.uid, validate)
await message.reply_chat_action(ChatAction.TYPING) await message.reply_chat_action(ChatAction.TYPING)
sign_text, button = await self.start_sign(client, headers) sign_text, button = await self.start_sign(client, user.id, headers)
reply_message = await message.reply_text(sign_text, allow_sending_without_reply=True, reply_markup=button) reply_message = await message.reply_text(sign_text, allow_sending_without_reply=True, reply_markup=button)
if filters.ChatType.GROUPS.filter(reply_message): if filters.ChatType.GROUPS.filter(reply_message):
self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id) self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id)
@ -339,3 +352,28 @@ class Sign(Plugin, BasePlugin):
self._add_delete_message_job(context, message.chat_id, message.message_id, 30) self._add_delete_message_job(context, message.chat_id, message.message_id, 30)
else: else:
await message.reply_text("未查询到您所绑定的账号信息,请先绑定账号", reply_markup=InlineKeyboardMarkup(buttons)) await message.reply_text("未查询到您所绑定的账号信息,请先绑定账号", reply_markup=InlineKeyboardMarkup(buttons))
@handler(CallbackQueryHandler, pattern=r"^sign\|", block=False)
@restricts(restricts_time_of_groups=20, without_overlapping=True)
@error_callable
async def sign_gen_link(self, update: Update, _: CallbackContext) -> None:
callback_query = update.callback_query
user = callback_query.from_user
async def get_sign_callback(callback_query_data: str) -> Tuple[int, int]:
_data = callback_query_data.split("|")
_user_id = int(_data[1])
_uid = int(_data[2])
logger.debug(f"callback_query_data 函数返回 user_id[{_user_id}] uid[{_uid}]")
return _user_id, _uid
user_id, uid = await get_sign_callback(callback_query.data)
if user.id != user_id:
await callback_query.answer(text="这不是你的按钮!\n再乱点再按我叫西风骑士团、千岩军、天领奉行和教令院了!", show_alert=True)
return
challenge = await SignRedis.get(uid)
if not challenge:
await callback_query.answer(text="验证请求已经过期,请重新发起签到!", show_alert=True)
return
url = f"t.me/{bot.app.bot.username}?start=sign"
await callback_query.answer(url=url)

View File

@ -36,6 +36,8 @@ class StartPlugin(Plugin):
f"你好 {user.mention_markdown_v2()} {escape_markdown('!我是派蒙 ')}\n" f"你好 {user.mention_markdown_v2()} {escape_markdown('!我是派蒙 ')}\n"
f"{escape_markdown('发送 /setuid 或 /setcookie 命令进入绑定账号流程')}" f"{escape_markdown('发送 /setuid 或 /setcookie 命令进入绑定账号流程')}"
) )
elif args[0] == "sign":
await StartPlugin.gen_sign_button(update)
elif args[0].startswith("challenge_"): elif args[0].startswith("challenge_"):
await StartPlugin.process_sign_validate(update, args[0][10:]) await StartPlugin.process_sign_validate(update, args[0][10:])
else: else:
@ -43,6 +45,19 @@ class StartPlugin(Plugin):
return return
await message.reply_markdown_v2(f"你好 {user.mention_markdown_v2()} {escape_markdown('!我是派蒙 ')}") await message.reply_markdown_v2(f"你好 {user.mention_markdown_v2()} {escape_markdown('!我是派蒙 ')}")
@staticmethod
async def gen_sign_button(update: Update):
with contextlib.suppress(UserNotFoundError, CookiesNotFoundError):
client = await get_genshin_client(update.effective_user.id)
await update.effective_message.reply_chat_action(ChatAction.TYPING)
button = await Sign.gen_challenge_button(client.uid, update.effective_user.id)
if not button:
await update.effective_message.reply_text("验证请求已过期。", allow_sending_without_reply=True)
return
await update.effective_message.reply_text(
"请尽快点击下方按钮进行验证。", allow_sending_without_reply=True, reply_markup=button
)
@staticmethod @staticmethod
async def process_sign_validate(update: Update, validate: str): async def process_sign_validate(update: Update, validate: str):
with contextlib.suppress(UserNotFoundError, CookiesNotFoundError): with contextlib.suppress(UserNotFoundError, CookiesNotFoundError):
@ -52,7 +67,7 @@ class StartPlugin(Plugin):
if not headers: if not headers:
await update.effective_message.reply_text("验证请求已过期。", allow_sending_without_reply=True) await update.effective_message.reply_text("验证请求已过期。", allow_sending_without_reply=True)
return return
sign_text, button = await Sign.start_sign(client, headers) sign_text, button = await Sign.start_sign(client, update.effective_user.id, headers)
await update.effective_message.reply_text(sign_text, allow_sending_without_reply=True, reply_markup=button) await update.effective_message.reply_text(sign_text, allow_sending_without_reply=True, reply_markup=button)
@staticmethod @staticmethod