mirror of
https://github.com/baiqwerdvd/ArknightsUID.git
synced 2025-05-07 12:43:29 +08:00
✨ 支持森空岛签到
This commit is contained in:
parent
9705a2bc38
commit
97148306c2
@ -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(
|
||||
'插件命令前缀(确认无冲突再修改)',
|
||||
'用于本插件的前缀设定',
|
||||
|
102
ArknightsUID/arknightsuid_signin/__init__.py
Normal file
102
ArknightsUID/arknightsuid_signin/__init__.py
Normal file
@ -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每日全部签到]群聊推送完成')
|
149
ArknightsUID/arknightsuid_signin/sign.py
Normal file
149
ArknightsUID/arknightsuid_signin/sign.py
Normal file
@ -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
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
################
|
||||
|
Loading…
x
Reference in New Issue
Block a user