支持森空岛签到

This commit is contained in:
qwerdvd 2023-09-01 00:59:30 +08:00
parent 9705a2bc38
commit 97148306c2
6 changed files with 449 additions and 42 deletions

View File

@ -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(
'插件命令前缀(确认无冲突再修改)',
'用于本插件的前缀设定',

View 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每日全部签到]群聊推送完成')

View 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

View File

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

View File

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

View File

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