mirror of
https://github.com/xuthus83/LittlePaimon.git
synced 2025-04-12 23:29:37 +08:00
404 lines
20 KiB
Python
404 lines
20 KiB
Python
import datetime
|
||
import re
|
||
|
||
from nonebot import on_regex, on_command
|
||
from nonebot.adapters.onebot.v11 import MessageEvent, Message, MessageSegment, GroupMessageEvent, PrivateMessageEvent, \
|
||
Bot
|
||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||
from nonebot.adapters.onebot.v11.helpers import HandleCancellation, convert_chinese_to_bool
|
||
from nonebot.params import RegexDict, ArgPlainText, CommandArg, Arg
|
||
from nonebot.permission import SUPERUSER
|
||
from nonebot.plugin import PluginMetadata
|
||
from nonebot.typing import T_State
|
||
|
||
from LittlePaimon.database import PlayerAlias
|
||
from LittlePaimon.utils import NICKNAME
|
||
from LittlePaimon.config import config
|
||
from LittlePaimon.utils.alias import get_match_alias, WEAPON_TYPE_ALIAS, type_file, alias_file
|
||
from LittlePaimon.utils.message import MessageBuild, fullmatch_rule
|
||
from LittlePaimon.utils.tool import freq_limiter
|
||
from LittlePaimon.utils.typing import COMMAND_START_RE
|
||
from .draw_daily_material import draw_material
|
||
from .draw_map import init_map, draw_map, get_full_map
|
||
from .SereniteaPot import draw_pot_materials
|
||
from .Atlas import get_match_card, get_card_resources, get_match_specialty, get_all_specialty
|
||
from .wiki_api import API
|
||
|
||
__plugin_meta__ = PluginMetadata(
|
||
name='原神Wiki',
|
||
description='原神WIKI百科',
|
||
usage='',
|
||
extra={
|
||
'author': '惜月',
|
||
'version': '3.0',
|
||
'priority': 3,
|
||
}
|
||
)
|
||
|
||
cancel = [HandleCancellation(f'好吧,有需要再找{NICKNAME}')]
|
||
BASE_TYPE = ['角色', '天赋', '培养', '命座', '技能', '武器', '圣遗物', '原魔', '怪物', '七圣召唤', '原牌', '卡牌',
|
||
'特产', '材料']
|
||
BASE_TYPE_RE = '(' + '|'.join(BASE_TYPE) + ')'
|
||
IMG_TYPE = ['图鉴', '攻略', '材料', '参考面板', '收益曲线']
|
||
IMG_TYPE_RE = '(' + '|'.join(IMG_TYPE) + ')'
|
||
|
||
WIKI_RE = fr'{COMMAND_START_RE}(?P<name1>\w{{0,7}}?)(?P<type>{BASE_TYPE_RE}?{IMG_TYPE_RE})(?P<name2>\w{{0,7}})'
|
||
|
||
total_wiki = on_regex(
|
||
WIKI_RE,
|
||
priority=9,
|
||
block=True,
|
||
state={
|
||
'pm_name': '原神WIKI',
|
||
'pm_description': '支持查询:角色的图鉴、攻略、材料、参考面板、收益曲线,武器、圣遗物、原魔、七圣召唤图鉴,今日|周几材料'
|
||
'\n示例:钟离攻略、护摩图鉴、今日材料',
|
||
'pm_usage': '<对象名><图鉴|攻略|材料>',
|
||
'pm_priority': 1
|
||
})
|
||
# material_map = on_command('材料图鉴', priority=11, block=True, state={
|
||
# 'pm_name': '材料图鉴',
|
||
# 'pm_description': '查看某个材料的介绍和采集点。',
|
||
# 'pm_usage': '材料图鉴<材料名>[地图]',
|
||
# 'pm_priority': 2
|
||
# })
|
||
material_map_full = on_command('材料地图', priority=8, block=True, state={
|
||
'pm_name': '材料地图',
|
||
'pm_description': '查看多个材料大地图采集点。\n示例:材料地图 鸣草 鬼兜虫 提瓦特',
|
||
'pm_usage': '材料地图<材料名列表>[地图]',
|
||
'pm_priority': 3
|
||
})
|
||
generate_map = on_command('生成地图', priority=1, block=True, permission=SUPERUSER, state={
|
||
'pm_name': '生成地图',
|
||
'pm_description': '生成材料图鉴等所需要的地图资源,仅超级用户可用。',
|
||
'pm_usage': '生成地图',
|
||
'pm_priority': 4
|
||
})
|
||
pot_material = on_command('尘歌壶摹本', aliases={'摹本材料', '尘歌壶材料', '尘歌壶摹本材料'}, priority=11, block=True,
|
||
state={
|
||
'pm_name': '尘歌壶摹本材料',
|
||
'pm_description': '查看尘歌壶摹本所需要的材料总览',
|
||
'pm_usage': '尘歌壶材料<摹数>',
|
||
'pm_priority': 5
|
||
})
|
||
card_wiki_list = on_command('七圣召唤列表', aliases={'七圣召唤卡牌列表', '原牌列表', '原石传说列表'}, priority=11,
|
||
rule=fullmatch_rule, block=True, state={
|
||
'pm_name': '七圣召唤图鉴列表',
|
||
'pm_description': '查看已支持查询的七圣召唤卡牌图鉴列表',
|
||
'pm_usage': '原牌列表',
|
||
'pm_priority': 6
|
||
})
|
||
|
||
|
||
# @material_map.handle()
|
||
# async def _(event: MessageEvent, state: T_State, msg: Message = CommandArg()):
|
||
# if params := msg.extract_plain_text().strip():
|
||
# params = params.split(' ')
|
||
# state['name'] = Message(params[0])
|
||
# if len(params) > 1:
|
||
# if params[1] in {'提瓦特', '层岩巨渊', '渊下宫'}:
|
||
# state['map'] = params[1]
|
||
# else:
|
||
# state['map'] = Message('提瓦特')
|
||
# else:
|
||
# state['map'] = Message('提瓦特')
|
||
|
||
|
||
# @material_map.got('map', prompt='地图名称有误,请在【提瓦特、层岩巨渊、渊下宫】中选择,或回答【取消】退出',
|
||
# parameterless=cancel)
|
||
# async def _(event: MessageEvent, state: T_State, map_: str = ArgPlainText('map')):
|
||
# if map_ not in {'提瓦特', '层岩巨渊', '渊下宫'}:
|
||
# await material_map.reject('地图名称有误,请在【提瓦特、层岩巨渊、渊下宫】中选择')
|
||
# else:
|
||
# state['map'] = Message(map_)
|
||
#
|
||
#
|
||
# @material_map.got('name', prompt='请输入要查询的材料名称,或回答【取消】退出',
|
||
# parameterless=cancel)
|
||
# async def _(event: MessageEvent, map_: str = ArgPlainText('map'), name: str = ArgPlainText('name')):
|
||
# if (file_path := RESOURCE_BASE_PATH / 'genshin_map' / 'results' / f'{map_}_{name}.png').exists():
|
||
# await material_map.finish(MessageSegment.image(file_path), at_sender=True)
|
||
# else:
|
||
# await material_map.send(MessageBuild.Text(f'开始查找{name}的资源点,请稍候...'))
|
||
# result = await draw_map(name, map_)
|
||
# await material_map.finish(result, at_sender=True)
|
||
|
||
|
||
@material_map_full.handle()
|
||
async def _(event: MessageEvent, state: T_State, msg: Message = CommandArg()):
|
||
state['map'] = '提瓦特'
|
||
if params := msg.extract_plain_text().strip():
|
||
params = params.split(' ')
|
||
for p in params.copy():
|
||
if p in {'提瓦特', '层岩巨渊', '渊下宫'}:
|
||
params.remove(p)
|
||
state['map'] = p
|
||
state['names'] = params
|
||
|
||
|
||
@material_map_full.got('names', prompt='请输入要查询的材料名称,或回答【取消】退出',
|
||
parameterless=cancel)
|
||
async def _(event: MessageEvent, map_: str = Arg('map'), names=Arg('names')):
|
||
if isinstance(names, Message):
|
||
names = names.extract_plain_text().split(' ')
|
||
if not freq_limiter.check(f'材料地图_{event.group_id if isinstance(event, GroupMessageEvent) else event.user_id}'):
|
||
await material_map_full.finish(
|
||
f'材料地图查询冷却中,剩余{freq_limiter.left(f"材料地图_{event.group_id if isinstance(event, GroupMessageEvent) else event.user_id}")}秒',
|
||
at_sender=True)
|
||
freq_limiter.start(f'材料地图_{event.group_id if isinstance(event, GroupMessageEvent) else event.user_id}', 15)
|
||
if len(names) > 3:
|
||
names = names[:3]
|
||
await material_map_full.send(MessageBuild.Text(f'开始查找{"、".join(names)}的资源点,请稍候...'))
|
||
result = await get_full_map(names, map_)
|
||
await material_map_full.finish(result, at_sender=True)
|
||
|
||
|
||
@generate_map.got('confirm',
|
||
prompt=f'生成材料地图资源要求大量内存,如果内存不足,可能会导致{NICKNAME}崩溃,请确认你的机器内存充足,回答【是|否】继续执行!')
|
||
async def _(event: MessageEvent):
|
||
if convert_chinese_to_bool(event.message):
|
||
await generate_map.send('开始生成地图资源,这可能需要较长时间...')
|
||
result = await init_map()
|
||
await generate_map.finish(result)
|
||
else:
|
||
await generate_map.finish('取消生成地图资源!')
|
||
|
||
|
||
@pot_material.handle()
|
||
async def _(event: MessageEvent, msg: Message = CommandArg()):
|
||
msg = msg.extract_plain_text().strip()
|
||
if len(msg) != 10 and not msg.isdigit():
|
||
await pot_material.finish('这个尘歌壶摹数不对哦,必须是十位数的数字')
|
||
else:
|
||
try:
|
||
code = int(msg)
|
||
result = await draw_pot_materials(code, user_id=str(event.user_id))
|
||
except Exception as e:
|
||
result = f'绘制尘歌壶摹本材料失败,错误信息:{e}'
|
||
await pot_material.finish(result, at_sender=True)
|
||
|
||
|
||
@total_wiki.handle()
|
||
async def _(state: T_State, regex_dict: dict = RegexDict()):
|
||
if regex_dict['name1'] and regex_dict['name2']:
|
||
await total_wiki.finish()
|
||
name: str = regex_dict['name1'] or regex_dict['name2'] or ''
|
||
type: str = regex_dict.get('type')
|
||
if type.startswith(('角色', '天赋', '培养', '命座', '技能')):
|
||
type = f'角色{type[2:]}'
|
||
elif type.startswith('武器'):
|
||
type = '武器图鉴'
|
||
elif type.startswith(('材料', '特产')) and type != '材料':
|
||
type = '特产图鉴'
|
||
elif type.startswith(('原魔', '怪物')):
|
||
type = '原魔图鉴'
|
||
elif type.startswith(('七圣召唤', '原牌', '卡牌')):
|
||
type = '七圣召唤图鉴'
|
||
if type.endswith('材料') and re.match(r'现在|[今明后][天日]|周[一二三四五六日]', name):
|
||
type = '每日材料'
|
||
elif type.endswith(('参考面板', '收益曲线')):
|
||
type = type[-4:]
|
||
state['type'] = type
|
||
if name:
|
||
state['name'] = Message(name)
|
||
elif type not in {'图鉴', '攻略', '材料'}:
|
||
state['name'] = Message('全部')
|
||
state['times'] = 1
|
||
|
||
|
||
week_str = ['周一', '周二', '周三', '周四', '周五', '周六']
|
||
|
||
|
||
@total_wiki.got('name', prompt=Message.template('你要查询谁的{type}呢?'), parameterless=cancel)
|
||
async def _(bot: Bot, event: MessageEvent, state: T_State, type: str = Arg('type'), name: str = ArgPlainText('name')):
|
||
if not name:
|
||
if state['times'] == 2:
|
||
await total_wiki.finish('旅行者似乎不太能理解,下次再问我吧' + MessageSegment.face(146))
|
||
else:
|
||
state['times'] = state['times'] + 1
|
||
await total_wiki.reject(f'你要查询谁的{type}呢?', at_sender=True)
|
||
if type == '每日材料':
|
||
if name in {'今日', '今天', '现在'}:
|
||
day = datetime.datetime.now().weekday()
|
||
elif name in {'明日', '明天'}:
|
||
day = (datetime.datetime.now() + datetime.timedelta(days=1)).weekday()
|
||
elif name in {'后日', '后天'}:
|
||
day = (datetime.datetime.now() + datetime.timedelta(days=2)).weekday()
|
||
elif name == '周日':
|
||
await total_wiki.finish('周日所有材料都可以刷哦!', at_sender=True)
|
||
elif name.startswith('周'):
|
||
await total_wiki.send('开始获取每日材料,请稍候...')
|
||
await total_wiki.finish(await draw_material(str(event.user_id), name))
|
||
if day == 6:
|
||
await total_wiki.finish('周日所有材料都可以刷哦!', at_sender=True)
|
||
else:
|
||
await total_wiki.send('开始获取每日材料,请稍候...')
|
||
await total_wiki.finish(await draw_material(str(event.user_id), week_str[day]), at_sender=True)
|
||
else:
|
||
matches = {}
|
||
if type == '参考面板' and name in {'火', '水', '冰', '雷', '风', '岩', '草'}:
|
||
await total_wiki.finish(MessageSegment.image(API[type].format(proxy=config.github_proxy, name=name)))
|
||
elif type.startswith('角色') or type in {'参考面板', '收益曲线'}:
|
||
if name == '全部':
|
||
matches = type_file['角色']['元素类型']
|
||
elif re.match('^[火水冰雷风岩草](元素|属性|系)?$', name):
|
||
matches = {'角色': type_file['角色']['元素类型'][name[0]]}
|
||
elif re.match('^' + '|'.join(WEAPON_TYPE_ALIAS.keys()) + '$', name):
|
||
matches = {'角色': type_file['角色']['武器类型'][WEAPON_TYPE_ALIAS[name]]}
|
||
elif alias := await PlayerAlias.get_or_none(user_id=str(event.user_id), alias=name):
|
||
final_name = alias.character
|
||
matches = {}
|
||
try:
|
||
await total_wiki.finish(
|
||
MessageSegment.image(API[type].format(proxy=config.github_proxy, name=final_name)))
|
||
except ActionFailed:
|
||
await total_wiki.finish(
|
||
MessageBuild.Text(f'{final_name}的{type}发送失败,可能是网络问题或者不存在该资源'))
|
||
else:
|
||
matches = get_match_alias(name, '角色')
|
||
elif type.startswith('武器'):
|
||
if name == '全部':
|
||
matches = type_file['武器']
|
||
elif re.match('^' + '|'.join(WEAPON_TYPE_ALIAS.keys()) + '$', name):
|
||
matches = {'武器': type_file['武器'][WEAPON_TYPE_ALIAS[name]]}
|
||
else:
|
||
matches = get_match_alias(name, '武器')
|
||
elif type.startswith('原魔'):
|
||
matches = {'原魔': alias_file['原魔']} if name == '全部' else get_match_alias(name, '原魔')
|
||
elif type.startswith('圣遗物'):
|
||
matches = {'圣遗物': list(alias_file['圣遗物'].keys())} if name == '全部' else get_match_alias(name, '圣遗物')
|
||
elif type.startswith('七圣召唤'):
|
||
if name == '全部':
|
||
matches = await get_match_card(name)
|
||
else:
|
||
matches = {'七圣召唤': await get_match_card(name)}
|
||
elif type == '特产图鉴':
|
||
matches = {'特产': await get_match_specialty(name)}
|
||
else:
|
||
if re.match('^[火水冰雷风岩草](元素|属性|系)?$', name):
|
||
matches = {'角色': type_file['角色']['元素类型'][name[0]]}
|
||
elif re.match('^' + '|'.join(WEAPON_TYPE_ALIAS.keys()) + '$', name):
|
||
matches = {'角色': type_file['角色']['武器类型'][WEAPON_TYPE_ALIAS[name]],
|
||
'武器': type_file['武器'][WEAPON_TYPE_ALIAS[name]]}
|
||
elif alias := await PlayerAlias.get_or_none(user_id=str(event.user_id), alias=name):
|
||
final_name = alias.character
|
||
if type in {'材料', '攻略', '图鉴'}:
|
||
type = f'角色{type}'
|
||
try:
|
||
await total_wiki.finish(
|
||
MessageSegment.image(API[type].format(proxy=config.github_proxy, name=final_name)))
|
||
except ActionFailed:
|
||
await total_wiki.finish(f'{final_name}的{type}发送失败,可能是网络问题或者不存在该资源')
|
||
else:
|
||
matches = get_match_alias(name, ['角色', '武器', '原魔', '圣遗物'])
|
||
if m := await get_match_card(name):
|
||
matches['七圣召唤'] = m
|
||
if s := await get_match_specialty(name):
|
||
matches['特产'] = s
|
||
if not matches:
|
||
await total_wiki.finish(MessageBuild.Text(f'没有名为{name}的{type}哦,是不是打错了~'), at_sender=True)
|
||
elif len(matches) == 1 and len(list(matches.values())[0]) == 1:
|
||
final_name = list(matches.values())[0][0]
|
||
temp_type = list(matches.keys())[0]
|
||
if type in {'材料', '攻略', '图鉴'}:
|
||
type = f'{temp_type}图鉴' if temp_type != '角色' else f'{temp_type}{type}'
|
||
try:
|
||
await total_wiki.finish(
|
||
MessageSegment.image(API[type].format(proxy=config.github_proxy, name=final_name)))
|
||
except ActionFailed:
|
||
await total_wiki.finish(f'{final_name}的{type}发送失败,可能是网络问题或者不存在该资源')
|
||
else:
|
||
send_flag = False
|
||
if sum(len(value) for value in matches.values()) >= 15 and isinstance(event, (
|
||
PrivateMessageEvent, GroupMessageEvent)):
|
||
if len(matches) == 1:
|
||
matches = {i: matches[list(matches.keys())[0]][i:i + 5] for i in
|
||
range(0, len(matches[list(matches.keys())[0]]), 5)}
|
||
index = 1
|
||
msg = []
|
||
for key, value in matches.items():
|
||
temp_msg = f'{key}:\n' if not isinstance(key, int) else ''
|
||
for v in value:
|
||
temp_msg += f'{index}.{v}\n'
|
||
index += 1
|
||
msg.append({'type': 'node', 'data': {'name': NICKNAME, 'uin': event.self_id, 'content': temp_msg.strip()}})
|
||
msg.insert(0, {'type': 'node',
|
||
'data': {'name': NICKNAME, 'uin': event.self_id, 'content': f'你要查询哪个的{type}呢?'}})
|
||
msg.append({'type': 'node', 'data': {'name': NICKNAME, 'uin': event.self_id,
|
||
'content': '回答\"序号\"查询或回答\"取消\"取消查询'}})
|
||
try:
|
||
send_flag = True
|
||
if isinstance(event, GroupMessageEvent):
|
||
await bot.call_api('send_group_forward_msg', group_id=event.group_id, messages=msg)
|
||
elif isinstance(event, PrivateMessageEvent):
|
||
await bot.call_api('send_private_forward_msg', user_id=event.user_id, messages=msg)
|
||
except ActionFailed:
|
||
send_flag = False
|
||
if not send_flag:
|
||
index = 1
|
||
msg = f'你要查询的{type}是:\n'
|
||
for key, value in matches.items():
|
||
for v in value:
|
||
msg += f'{index}.{v}({key})\n' if len(matches) > 1 else f'{index}.{v}\n'
|
||
index += 1
|
||
msg += '回答\"序号\"查询或回答\"取消\"取消查询'
|
||
await total_wiki.send(msg)
|
||
state['matches'] = matches
|
||
|
||
|
||
@total_wiki.got('choice', parameterless=cancel)
|
||
async def _(state: T_State, matches: dict = Arg('matches'), choice: str = ArgPlainText('choice'),
|
||
type: str = Arg('type')):
|
||
if choice.isdigit() and 1 <= int(choice) <= sum(len(value) for value in matches.values()):
|
||
choice_num = int(choice)
|
||
else:
|
||
choice_num = None
|
||
final_name = None
|
||
for key, value in matches.items():
|
||
if choice_num:
|
||
if len(value) >= choice_num:
|
||
final_name = value[choice_num - 1]
|
||
if type in {'材料', '攻略', '图鉴'}:
|
||
type = f'{key}图鉴' if key != '角色' else f'{key}{type}'
|
||
break
|
||
else:
|
||
choice_num -= len(value)
|
||
elif choice in value:
|
||
final_name = choice
|
||
if type in {'材料', '攻略', '图鉴'}:
|
||
type = f'{key}图鉴' if key != '角色' else f'{key}{type}'
|
||
break
|
||
if final_name:
|
||
try:
|
||
await total_wiki.finish(
|
||
MessageSegment.image(API[type].format(proxy=config.github_proxy, name=final_name)))
|
||
except ActionFailed:
|
||
await total_wiki.finish(f'{final_name}的{type}发送失败,可能是网络问题或者不存在该资源')
|
||
elif state['times'] == 2:
|
||
await total_wiki.finish(f'旅行者似乎不太能理解,下次再问我吧{MessageSegment.face(146)}')
|
||
else:
|
||
state['times'] = state['times'] + 1
|
||
await total_wiki.reject(f'请旅行者从上面的{type}中选一个问{NICKNAME}或回答\"取消\"可以取消查询', at_sender=True)
|
||
|
||
|
||
@card_wiki_list.handle()
|
||
async def _(bot: Bot, event: MessageEvent):
|
||
result = await get_card_resources()
|
||
if not result:
|
||
await card_wiki_list.finish('读取七圣召唤卡牌列表失败')
|
||
msg = [
|
||
{'type': 'node', 'data': {'name': NICKNAME, 'uin': event.self_id, 'content': f'{type}:\n' + '\n'.join(cards)}}
|
||
for type, cards in result.items()]
|
||
msg.insert(0, {'type': 'node', 'data': {'name': NICKNAME, 'uin': event.self_id, 'content': '七圣召唤卡牌列表如下'}})
|
||
try:
|
||
if isinstance(event, GroupMessageEvent):
|
||
await bot.call_api('send_group_forward_msg', group_id=event.group_id, messages=msg)
|
||
elif isinstance(event, PrivateMessageEvent):
|
||
await bot.call_api('send_private_forward_msg', user_id=event.user_id, messages=msg)
|
||
else:
|
||
msg = '七圣召唤卡牌列表:\n'
|
||
for type, cards in result.items():
|
||
msg += f'{type}:\n' + '\n'.join([' '.join(cards[i:i + 3]) for i in range(0, len(cards), 3)]) + '\n'
|
||
await card_wiki_list.send(msg)
|
||
except ActionFailed:
|
||
await card_wiki_list.finish('七圣召唤卡牌列表发送失败,账号可能被风控')
|