From 97148306c2d51d0b2a2f9e7479c3615b61a3ca0a Mon Sep 17 00:00:00 2001 From: qwerdvd <105906879+qwerdvd@users.noreply.github.com> Date: Fri, 1 Sep 2023 00:59:30 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E6=94=AF=E6=8C=81=E6=A3=AE?= =?UTF-8?q?=E7=A9=BA=E5=B2=9B=E7=AD=BE=E5=88=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../arknightsuid_config/config_default.py | 12 ++ ArknightsUID/arknightsuid_signin/__init__.py | 102 ++++++++++ ArknightsUID/arknightsuid_signin/sign.py | 149 +++++++++++++++ ArknightsUID/utils/api/skd/api.py | 2 + ArknightsUID/utils/api/skd/request.py | 176 +++++++++++++----- ArknightsUID/utils/models/skland/models.py | 50 +++++ 6 files changed, 449 insertions(+), 42 deletions(-) create mode 100644 ArknightsUID/arknightsuid_signin/__init__.py create mode 100644 ArknightsUID/arknightsuid_signin/sign.py diff --git a/ArknightsUID/arknightsuid_config/config_default.py b/ArknightsUID/arknightsuid_config/config_default.py index c6dba3a..1302853 100644 --- a/ArknightsUID/arknightsuid_config/config_default.py +++ b/ArknightsUID/arknightsuid_config/config_default.py @@ -1,10 +1,22 @@ from gsuid_core.utils.plugins_config.models import ( GSC, GsBoolConfig, + GsListStrConfig, GsStrConfig, ) CONIFG_DEFAULT: dict[str, GSC] = { + 'SignTime': GsListStrConfig('每晚签到时间设置', '每晚森空岛签到时间设置(时,分)', ['0', '38']), + 'SignReportSimple': GsBoolConfig( + '简洁签到报告', + '开启后可以大大减少每日签到报告字数', + True, + ), + 'SchedSignin': GsBoolConfig( + '定时签到', + '开启后每晚00:30将开始自动签到任务', + True, + ), 'ArknightsPrefix': GsStrConfig( '插件命令前缀(确认无冲突再修改)', '用于本插件的前缀设定', diff --git a/ArknightsUID/arknightsuid_signin/__init__.py b/ArknightsUID/arknightsuid_signin/__init__.py new file mode 100644 index 0000000..ffdb175 --- /dev/null +++ b/ArknightsUID/arknightsuid_signin/__init__.py @@ -0,0 +1,102 @@ +import asyncio +import random + +from gsuid_core.aps import scheduler +from gsuid_core.bot import Bot +from gsuid_core.gss import gss +from gsuid_core.logger import logger +from gsuid_core.models import Event +from gsuid_core.sv import SV +from gsuid_core.utils.database.api import get_uid + +from ..arknightsuid_config.ark_config import arkconfig +from ..utils.ark_prefix import PREFIX +from ..utils.database.models import ArknightsBind +from .sign import daily_sign, sign_in + +SIGN_TIME = arkconfig.get_config('SignTime').data +UID_HINT = '添加失败, 请先绑定明日方舟UID' + +sv_sign = SV('森空岛签到') +sv_sign_config = SV('森空岛管理', pm=2) + + +# 每日零点半执行森空岛签到 +@scheduler.scheduled_job('cron', hour=SIGN_TIME[0], minute=SIGN_TIME[1]) +async def sr_sign_at_night(): + if arkconfig.get_config('SchedSignin').data: + await send_daily_sign() + + +# 群聊内 签到 功能 +@sv_sign.on_fullmatch(f'{PREFIX}签到') +async def get_sign_func(bot: Bot, ev: Event): + await bot.logger.info(f'[ARK签到]QQ号: {ev.user_id}') + ark_uid = await get_uid(bot, ev, bind_model=ArknightsBind) + if ark_uid is None: + return '你还没有绑定UID噢,请使用[ark绑定uid123]完成绑定!' + await bot.logger.info(f'[ARK签到]UID: {ark_uid}') + await bot.send(await sign_in(ark_uid)) + return None + + +@sv_sign_config.on_fullmatch(f'{PREFIX}全部重签') +async def recheck(bot: Bot, ev: Event): + await bot.logger.info('开始执行[ARK全部重签]') + await bot.send('已开始执行') + await send_daily_sign() + await bot.send('执行完成') + + +async def send_daily_sign(): + logger.info('开始执行[ARK每日全部签到]') + # 执行签到 并获得推送消息 + result = await daily_sign() + private_msg_list = result['private_msg_list'] + group_msg_list = result['group_msg_list'] + logger.info('[ARK每日全部签到]完成') + + # 执行私聊推送 + for qid in private_msg_list: + try: + for bot_id in gss.active_bot: + for single in private_msg_list[qid]: + await gss.active_bot[bot_id].target_send( + single['msg'], 'direct', qid, single['bot_id'], '', '' + ) + except Exception as e: + logger.warning(f'[ARK每日全部签到] QQ {qid} 私聊推送失败!错误信息:{e}') + await asyncio.sleep(0.5) + logger.info('[ARK每日全部签到]私聊推送完成') + + # 执行群聊推送 + for gid in group_msg_list: + # 根据succee数判断是否为简洁推送 + if group_msg_list[gid]['success'] >= 0: + report = ( + '以下为签到失败报告:{}'.format(group_msg_list[gid]['push_message']) + if group_msg_list[gid]['push_message'] != '' + else '' + ) + msg_title = '森空岛今日自动签到已完成!\n本群共签到成功{}人,共签到失败{}人。{}'.format( + group_msg_list[gid]['success'], + group_msg_list[gid]['failed'], + report, + ) + else: + msg_title = group_msg_list[gid]['push_message'] + # 发送群消息 + try: + for bot_id in gss.active_bot: + await gss.active_bot[bot_id].target_send( + msg_title, + 'group', + gid, + group_msg_list[gid]['bot_id'], + '', + '', + ) + except Exception as e: + logger.warning(f'[ARK每日全部签到]群 {gid} 推送失败!错误信息:{e}') + await asyncio.sleep(0.5 + random.randint(1, 3)) + logger.info('[ARK每日全部签到]群聊推送完成') diff --git a/ArknightsUID/arknightsuid_signin/sign.py b/ArknightsUID/arknightsuid_signin/sign.py new file mode 100644 index 0000000..2eb2b42 --- /dev/null +++ b/ArknightsUID/arknightsuid_signin/sign.py @@ -0,0 +1,149 @@ +import asyncio +import random +from copy import deepcopy +from datetime import datetime + +from gsuid_core.gss import gss +from gsuid_core.logger import logger + +from ..arknightsuid_config.ark_config import arkconfig +from ..utils.ark_api import ark_skd_api +from ..utils.database.models import ArknightsUser + +private_msg_list = {} +group_msg_list = {} +already = 0 + + +# 签到函数 +async def sign_in(ark_uid: str) -> str: + logger.info(f'[ARK签到] {ark_uid} 开始执行签到') + # 获得签到信息 + sign_info = await ark_skd_api.get_sign_info(ark_uid) + # 初步校验数据 + if isinstance(sign_info, int): + logger.warning(f'[ARK签到] {ark_uid} 出错, 请检查森空岛Cred是否过期!') + return '签到失败...请检查森空岛Cred是否过期!' + # 检测是否已签到 + for calendar in sign_info.calendar: + if calendar.available: + break + else: + logger.info(f'[ARK签到] {ark_uid} 该用户今日已签到,跳过...') + global already # noqa: PLW0603 + already += 1 + # 获取今天和月初的日期,计算漏签次数 + day_of_month = datetime.now().day + special_count = 0 + count = 0 + for calendar in sign_info.calendar: + special_count += 1 if calendar.type_ == 'first' else 0 + done = calendar.done + if done is True: + count += 1 + # signed_count = sign_info + sign_missed = day_of_month - count + special_count + return f'今日已签到!本月漏签次数:{sign_missed}' + + # 进行一次签到 + sign_data = await ark_skd_api.skd_sign(uid=ark_uid) + # 检测数据 + if isinstance(sign_data, int): + logger.warning(f'[ARK签到] {ark_uid} 出错, 请检查森空岛Cred是否过期!') + return 'ark签到失败...请检查森空岛Cred是否过期!' + # 获取签到奖励物品,拿旧的总签到天数 + 1 为新的签到天数,再 -1 即为今日奖励物品的下标 + getitem = sign_data.awards + get_im = '' + for award in getitem: + get_im = f'本次ark签到获得{award.resource.name}x{award.count}' + # 签到后计算漏签次数 + new_sign_info = await ark_skd_api.get_sign_info(ark_uid) + # 校验数据 + if isinstance(new_sign_info, int): + logger.warning(f'[ARK签到] {ark_uid} 出错, 请检查森空岛Cred是否过期!') + return '签到失败...请检查森空岛Cred是否过期!' + # 获取今天和月初的日期,计算漏签次数 + day_of_month = datetime.now().day + special_count = 0 + count = 0 + for calendar in new_sign_info.calendar: + special_count += 1 if calendar.type_ == 'first' else 0 + done = calendar.done + if done is True: + count += 1 + # signed_count = sign_info + sign_missed = day_of_month - count + special_count + im = f'ark签到成功!\n{get_im}\n本月漏签次数:{sign_missed}' + logger.info(f'[ARK签到] {ark_uid} 签到完成, 结果: ark签到成功, 漏签次数: {sign_missed}') + return im + + +async def single_daily_sign(bot_id: str, ark_uid: str, gid: str, qid: str): + im = await sign_in(ark_uid) + if gid == 'on': + if qid not in private_msg_list: + private_msg_list[qid] = [] + private_msg_list[qid].append( + {'bot_id': bot_id, 'uid': ark_uid, 'msg': im} + ) + else: + # 向群消息推送列表添加这个群 + if gid not in group_msg_list: + group_msg_list[gid] = { + 'bot_id': bot_id, + 'success': 0, + 'failed': 0, + 'push_message': '', + } + # 检查是否开启简洁签到 + if arkconfig.get_config('SignReportSimple').data: + # 如果失败, 则添加到推送列表 + if im.startswith(('ark签到失败', '网络有点忙', 'OK', 'ok')): + message = f'[CQ:at,qq={qid}] {im}' + group_msg_list[gid]['failed'] += 1 + group_msg_list[gid]['push_message'] += '\n' + message + else: + group_msg_list[gid]['success'] += 1 + # 没有开启简洁签到, 则每条消息都要携带@信息 + else: + # 不用MessageSegment.at(row[2]),因为不方便移植 + message = f'[CQ:at,qq={qid}] {im}' + group_msg_list[gid]['push_message'] += '\n' + message + group_msg_list[gid]['success'] -= 1 + + +async def daily_sign(): + global already # noqa: PLW0603 + tasks = [] + for _ in gss.active_bot: + user_list = await ArknightsUser.get_all_user() + for user in user_list: + if user.sign_switch != 'off' and user.ark_uid is not None: + tasks.append( + single_daily_sign( + user.bot_id, + user.ark_uid, + user.sign_switch, + user.user_id, + ) + ) + if len(tasks) >= 1: + await asyncio.gather(*tasks) + if already >= 1: + delay = 1 + else: + delay = 50 + random.randint(3, 45) + logger.info(f'[ARK签到] 已签到{len(tasks)}个用户, 等待{delay}秒进行下一次签到') + tasks.clear() + already = 0 + await asyncio.sleep(delay) + await asyncio.gather(*tasks) + tasks.clear() + result = { + 'private_msg_list': deepcopy(private_msg_list), + 'group_msg_list': deepcopy(group_msg_list), + } + private_msg_list.clear() + group_msg_list.clear() + logger.info(result) + return result diff --git a/ArknightsUID/utils/api/skd/api.py b/ArknightsUID/utils/api/skd/api.py index 549ef12..20d351c 100644 --- a/ArknightsUID/utils/api/skd/api.py +++ b/ArknightsUID/utils/api/skd/api.py @@ -2,3 +2,5 @@ ARK_USER_ME = 'https://zonai.skland.com/api/v1/user/me' ARK_PLAYER_INFO = 'https://zonai.skland.com/api/v1/game/player/info' ARK_GEN_CRED_BY_CODE = 'https://zonai.skland.com/api/v1/user/auth/generate_cred_by_code' + +ARK_SKD_SIGN = 'https://zonai.skland.com/api/v1/game/attendance' diff --git a/ArknightsUID/utils/api/skd/request.py b/ArknightsUID/utils/api/skd/request.py index 88c58f3..db1668e 100644 --- a/ArknightsUID/utils/api/skd/request.py +++ b/ArknightsUID/utils/api/skd/request.py @@ -4,14 +4,23 @@ from typing import Any, ClassVar, Literal import msgspec from aiohttp import ClientSession, ContentTypeError, TCPConnector from gsuid_core.logger import logger +from gsuid_core.utils.plugins_config.gs_config import core_plugins_config from ...database.models import ArknightsUser -from ...models.skland.models import ArknightsPlayerInfoModel, ArknightsUserMeModel -from .api import ARK_PLAYER_INFO, ARK_USER_ME +from ...models.skland.models import ( + ArknightsAttendanceCalendarModel, + ArknightsAttendanceModel, + ArknightsPlayerInfoModel, + ArknightsUserMeModel, +) +from .api import ARK_PLAYER_INFO, ARK_SKD_SIGN, ARK_USER_ME + +proxy_url = core_plugins_config.get_config('proxy').data +ssl_verify = core_plugins_config.get_config('MhySSLVerify').data class BaseArkApi: - ssl_verify = True + proxy_url: str | None = proxy_url if proxy_url else None _HEADER: ClassVar[dict[str, str]] = { 'Host': 'zonai.skland.com', 'Origin': 'https://www.skland.com', @@ -21,8 +30,25 @@ class BaseArkApi: AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36', } + async def _pass(self, gt: str, ch: str) -> tuple[str | None, str | None]: + _pass_api = core_plugins_config.get_config('_pass_API').data + if _pass_api: + data = await self._ark_request( + url=f'{_pass_api}>={gt}&challenge={ch}', + method='GET', + ) + if isinstance(data, int): + return None, None + else: + validate = data['data']['validate'] + ch = data['data']['challenge'] + else: + validate = None + + return validate, ch + async def get_game_player_info(self, uid: str) -> int | ArknightsPlayerInfoModel: - cred = await ArknightsUser.get_user_attr_by_uid(uid=uid, attr='cred') + cred: str | None = await ArknightsUser.get_user_attr_by_uid(uid=uid, attr='cred') if cred is None: return -61 is_vaild = await self.check_cred_valid(cred) @@ -42,6 +68,54 @@ class BaseArkApi: else: return msgspec.convert(unpack_data, type=ArknightsPlayerInfoModel) + async def skd_sign(self, uid: str) -> int | ArknightsAttendanceModel: + cred: str | None = await ArknightsUser.get_user_attr_by_uid(uid=uid, attr='cred') + if cred is None: + return -61 + is_vaild = await self.check_cred_valid(cred) + if isinstance(is_vaild, bool): + await ArknightsUser.delete_user_data_by_uid(uid) + return -61 + header = deepcopy(self._HEADER) + header['Cred'] = cred + raw_data = await self._ark_request( + url=ARK_SKD_SIGN, + method='POST', + data={ + 'uid': uid, + 'gameId': 1 + }, + ) + unpack_data = self.unpack(raw_data) + if isinstance(unpack_data, int): + return unpack_data + else: + return msgspec.convert(unpack_data, ArknightsAttendanceModel) + + async def get_sign_info(self, uid: str) -> int | ArknightsAttendanceCalendarModel: + cred: str | None = await ArknightsUser.get_user_attr_by_uid(uid=uid, attr='cred') + if cred is None: + return -61 + is_vaild = await self.check_cred_valid(cred) + if isinstance(is_vaild, bool): + await ArknightsUser.delete_user_data_by_uid(uid) + return -61 + header = deepcopy(self._HEADER) + header['Cred'] = cred + raw_data = await self._ark_request( + url=ARK_SKD_SIGN, + method='GET', + params={ + 'uid': uid, + 'gameId': 1 + }, + ) + unpack_data = self.unpack(raw_data) + if isinstance(unpack_data, int): + return unpack_data + else: + return msgspec.convert(unpack_data, ArknightsAttendanceCalendarModel) + async def check_cred_valid(self, Cred: str) -> bool | ArknightsUserMeModel: header = deepcopy(self._HEADER) header['Cred'] = Cred @@ -65,44 +139,62 @@ class BaseArkApi: header: dict[str, Any] = _HEADER, params: dict[str, Any] | None = None, data: dict[str, Any] | None = None, + use_proxy: bool | None = False, ) -> dict | int: - if 'Cred' not in header: - target_user_id = ( - data['friendUserId'] - if data and 'friendUserId' in data - else None - ) - Cred: str | None = await ArknightsUser.get_random_cookie( - target_user_id if target_user_id else '18888888' - ) - if Cred is None: - return -61 - arkUser = await ArknightsUser.base_select_data(Cred=Cred) - if arkUser is None: - return -61 - header['Cred'] = Cred - async with ClientSession( - connector=TCPConnector(verify_ssl=self.ssl_verify) + connector=TCPConnector(verify_ssl=ssl_verify) ) as client: - async with client.request( - method, - url=url, - headers=header, - params=params, - json=data, - timeout=300, - ) as resp: - try: - raw_data = await resp.json() - except ContentTypeError: - _raw_data = await resp.text() - raw_data = {'code': -999, 'data': _raw_data} - if ( - raw_data - and 'code' in raw_data - and raw_data['code'] != 0 - ): - return raw_data['code'] - logger.debug(raw_data) - return raw_data + raw_data = {} + if 'Cred' not in header: + target_user_id = ( + data['friendUserId'] + if data and 'friendUserId' in data + else None + ) + Cred: str | None = await ArknightsUser.get_random_cookie( + target_user_id if target_user_id else '18888888' + ) + if Cred is None: + return -61 + arkUser = await ArknightsUser.base_select_data(Cred=Cred) + if arkUser is None: + return -61 + header['Cred'] = Cred + + for _ in range(3): + async with client.request( + method, + url=url, + headers=header, + params=params, + json=data, + proxy=self.proxy_url if use_proxy else None, + timeout=300, + ) as resp: + try: + raw_data = await resp.json() + except ContentTypeError: + _raw_data = await resp.text() + raw_data = {'retcode': -999, 'data': _raw_data} + logger.debug(raw_data) + + # 判断status + if 'status' in raw_data and 'msg' in raw_data: + retcode = 1 + else: + retcode = 0 + + if retcode == 1 and data: + vl, ch = await self._pass( + gt=raw_data['data']['captcha']['gt'], + ch=raw_data['data']['captcha']['challenge'] + ) + data['captcha'] = {} + data['captcha']['geetest_challenge'] = ch + data['captcha']['geetest_validate'] = vl + data['captcha']['geetest_seccode'] = f'{vl}|jordan' + elif retcode != 0: + return retcode + else: + return raw_data + return -999 diff --git a/ArknightsUID/utils/models/skland/models.py b/ArknightsUID/utils/models/skland/models.py index 20ab8b9..85e0706 100644 --- a/ArknightsUID/utils/models/skland/models.py +++ b/ArknightsUID/utils/models/skland/models.py @@ -1,5 +1,55 @@ from msgspec import Struct, field + +################ +# ArknightsAttendanceCalendar Start +################ +class ArknightsAttendanceAwardResource(Struct): + id_: str = field(name='id') + type_: str = field(name='type') + name: str + rarity: int + + +class ArknightsAttendanceRecord(Struct): + ts: int + resourceId: str + type_: str = field(name='type') + count: int + + +class ArknightsAttendanceCalendar(Struct): + resourceId: str + type_: str = field(name='type') + count: int + available: bool + done: bool + + +class ArknightsAttendanceCalendarModel(Struct): + currentTs: int + calendar: list[ArknightsAttendanceCalendar] + records: list[ArknightsAttendanceRecord | None] + resourceInfoMap: dict[str, ArknightsAttendanceAwardResource] + + +################ +# ArknightsAttendance Start +################ +class ArknightsAttendanceAward(Struct): + resource: ArknightsAttendanceAwardResource + count: int + type_: str = field(name='type') + +class ArknightsAttendanceModel(Struct): + ts: str + awards: list[ArknightsAttendanceAward] +################ +# ArknightsAttendance End +################ + + + ################ # ArknightsUserMeModel Start ################