优化Wiki命令,支持命令前缀以及更简洁的命令,如钟离图鉴

This commit is contained in:
CMHopeSunshine 2022-12-17 17:08:59 +08:00
parent 72dbeb79cf
commit 8ad13992e0
12 changed files with 289 additions and 299 deletions

View File

@ -47,7 +47,7 @@ class ConfigModel(BaseModel):
command_alias_enable: bool = Field(True, alias='启用命令别名') command_alias_enable: bool = Field(True, alias='启用命令别名')
github_proxy: str = Field('https://ghproxy.com/', alias='github资源地址') github_proxy: str = Field('https://github.cherishmoon.fun/', alias='github资源地址')
@property @property
def alias_dict(self): def alias_dict(self):

View File

@ -32,6 +32,14 @@ class PluginManager:
if file.is_file() and file.name.endswith('.yml'): if file.is_file() and file.name.endswith('.yml'):
data = load_yaml(file) data = load_yaml(file)
plugins[file.name.replace('.yml', '')] = PluginInfo.parse_obj(data) plugins[file.name.replace('.yml', '')] = PluginInfo.parse_obj(data)
# ------临时删除------
if plugins.get('Paimon_Wiki'):
plugins['Paimon_Wiki'].matchers = [matcher for matcher in plugins['Paimon_Wiki'].matchers
if matcher.pm_name not in {'圣遗物图鉴', '每日材料', '原魔图鉴', '参考面板',
'武器图鉴',
'收益曲线', '角色材料', '角色攻略',
'七圣召唤图鉴',
'角色图鉴'}]
@classmethod @classmethod
def save(cls): def save(cls):
@ -73,15 +81,14 @@ class PluginManager:
}) })
else: else:
cls.plugins[plugin.name] = PluginInfo(name=plugin.name, module_name=plugin.name) cls.plugins[plugin.name] = PluginInfo(name=plugin.name, module_name=plugin.name)
if cls.plugins[plugin.name].matchers is None:
cls.plugins[plugin.name].matchers = []
matchers = plugin.matcher matchers = plugin.matcher
for matcher in matchers: for matcher in matchers:
if matcher._default_state: if matcher._default_state:
with contextlib.suppress(Exception):
matcher_info = MatcherInfo.parse_obj(matcher._default_state) matcher_info = MatcherInfo.parse_obj(matcher._default_state)
if cls.plugins[plugin.name].matchers is not None and matcher_info.pm_name not in [m.pm_name for if matcher_info.pm_name not in [m.pm_name for m in cls.plugins[plugin.name].matchers]:
m
in
cls.plugins[
plugin.name].matchers]:
cls.plugins[plugin.name].matchers.append(matcher_info) cls.plugins[plugin.name].matchers.append(matcher_info)
cls.save() cls.save()
logger.success('插件管理器', '<g>初始化完成</g>') logger.success('插件管理器', '<g>初始化完成</g>')

View File

@ -77,7 +77,7 @@ async def _(event: Union[GroupMessageEvent, PrivateMessageEvent], lang=CommandLa
voice = await GenshinVoice.get_or_none(id=int(msg)) voice = await GenshinVoice.get_or_none(id=int(msg))
await get_voice.finish(MessageBuild.Record(voice.voice_url) if voice else MessageBuild.Text(f'没有{msg}号原神语音')) await get_voice.finish(MessageBuild.Record(voice.voice_url) if voice else MessageBuild.Text(f'没有{msg}号原神语音'))
else: else:
if chara := get_match_alias(msg, '角色', True): if chara := get_match_alias(msg, ['角色'], True):
chara = list(chara.keys())[0] chara = list(chara.keys())[0]
else: else:
await get_voice.finish(MessageBuild.Text(f'没有叫{chara}的角色')) await get_voice.finish(MessageBuild.Text(f'没有叫{chara}的角色'))

View File

@ -1,10 +1,11 @@
import datetime import datetime
import re
from nonebot import on_regex, on_command from nonebot import on_regex, on_command
from nonebot.adapters.onebot.v11 import MessageEvent, Message, MessageSegment, GroupMessageEvent, PrivateMessageEvent, \ from nonebot.adapters.onebot.v11 import MessageEvent, Message, MessageSegment, GroupMessageEvent, PrivateMessageEvent, \
Bot Bot
from nonebot.adapters.onebot.v11.exception import ActionFailed from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot.adapters.onebot.v11.helpers import HandleCancellation from nonebot.adapters.onebot.v11.helpers import HandleCancellation, convert_chinese_to_bool
from nonebot.params import RegexDict, ArgPlainText, CommandArg, Arg from nonebot.params import RegexDict, ArgPlainText, CommandArg, Arg
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.plugin import PluginMetadata from nonebot.plugin import PluginMetadata
@ -17,29 +18,18 @@ from LittlePaimon.utils.alias import get_match_alias
from LittlePaimon.utils.message import MessageBuild, fullmatch_rule from LittlePaimon.utils.message import MessageBuild, fullmatch_rule
from LittlePaimon.utils.path import RESOURCE_BASE_PATH from LittlePaimon.utils.path import RESOURCE_BASE_PATH
from LittlePaimon.utils.tool import freq_limiter from LittlePaimon.utils.tool import freq_limiter
from LittlePaimon.utils.typing import COMMAND_START_RE
from .draw_daily_material import draw_material from .draw_daily_material import draw_material
from .draw_map import init_map, draw_map, get_full_map from .draw_map import init_map, draw_map, get_full_map
from .SereniteaPot import draw_pot_materials from .SereniteaPot import draw_pot_materials
from .card import get_match_card, CARD_API, get_card_resources from .card import get_match_card, get_card_resources
from .wiki_api import API
__paimon_help__ = {
'type': '原神Wiki',
'range': ['private', 'group', 'guild']
}
help_msg = """"1.[xx角色攻略]查看西风驿站出品的角色一图流攻略\n"
"2.[xx角色材料]查看惜月出品的角色材料统计\n"
"3.[xx参考面板]查看blue菌hehe出品的参考面板攻略\n"
"4.[xx收益曲线]查看blue菌hehe出品的收益曲线攻略\n"
"5.[今日/明日/周x材料]查看每日角色天赋材料和武器突破材料表\n"
"6.[xx武器攻略]查看武器攻略\n"
"7.[xx原魔图鉴]查看原魔图鉴\n"
"""
__plugin_meta__ = PluginMetadata( __plugin_meta__ = PluginMetadata(
name='原神Wiki', name='原神Wiki',
description='原神WIKI百科', description='原神WIKI百科',
usage=help_msg, usage='',
extra={ extra={
'author': '惜月', 'author': '惜月',
'version': '3.0', 'version': '3.0',
@ -47,74 +37,52 @@ __plugin_meta__ = PluginMetadata(
} }
) )
daily_material = on_regex(r'^(?P<day>现在|(今|明|后)(天|日)|周(一|二|三|四|五|六|日))(天赋|角色|武器)?材料$', cancel = [HandleCancellation(f'好吧,有需要再找{NICKNAME}')]
priority=11, block=True, state={
'pm_name': '每日材料', total_wiki = on_regex(
'pm_description': '查看某日开放材料刷取的角色和武器', COMMAND_START_RE + r'(?P<name1>\w{0,7})(?P<type>(材料|图鉴|攻略|参考面板|收益曲线))(?P<name2>\w{0,7})',
'pm_usage': '<今天|周几>材料', priority=12,
'pm_priority': 8 block=True,
state={
'pm_name': '原神WIKI',
'pm_description': '支持查询:角色的图鉴、攻略、材料、参考面板、收益曲线,武器、圣遗物、原魔、七圣召唤图鉴,今日|周几材料'
'\n示例:钟离攻略、护摩图鉴、今日材料',
'pm_usage': '<对象名><图鉴|攻略|材料>',
'pm_priority': 1
}) })
material_map = on_command('材料图鉴', priority=11, block=True, state={ material_map = on_command('材料图鉴', priority=11, block=True, state={
'pm_name': '材料图鉴', 'pm_name': '材料图鉴',
'pm_description': '查看某个材料的介绍和采集点。', 'pm_description': '查看某个材料的介绍和采集点。',
'pm_usage': '材料图鉴<材料名>[地图]', 'pm_usage': '材料图鉴<材料名>[地图]',
'pm_priority': 9 'pm_priority': 2
}) })
material_map_full = on_command('材料地图', priority=11, block=True, state={ material_map_full = on_command('材料地图', priority=11, block=True, state={
'pm_name': '材料地图', 'pm_name': '材料地图',
'pm_description': '查看多个材料大地图采集点。\n示例:材料地图 鸣草 鬼兜虫 提瓦特', 'pm_description': '查看多个材料大地图采集点。\n示例:材料地图 鸣草 鬼兜虫 提瓦特',
'pm_usage': '材料地图<材料名列表>[地图]', 'pm_usage': '材料地图<材料名列表>[地图]',
'pm_priority': 10 'pm_priority': 3
}) })
generate_map = on_command('生成地图', priority=1, block=True, permission=SUPERUSER, state={ generate_map = on_command('生成地图', priority=1, block=True, permission=SUPERUSER, state={
'pm_name': '生成地图', 'pm_name': '生成地图',
'pm_description': '生成材料图鉴等所需要的地图资源,仅超级用户可用。', 'pm_description': '生成材料图鉴等所需要的地图资源,仅超级用户可用。',
'pm_usage': '生成地图', 'pm_usage': '生成地图',
'pm_priority': 11 'pm_priority': 4
}) })
pot_material = on_command('尘歌壶摹本', aliases={'摹本材料', '尘歌壶材料', '尘歌壶摹本材料'}, priority=11, block=True, pot_material = on_command('尘歌壶摹本', aliases={'摹本材料', '尘歌壶材料', '尘歌壶摹本材料'}, priority=11, block=True,
state={ state={
'pm_name': '尘歌壶摹本材料', 'pm_name': '尘歌壶摹本材料',
'pm_description': '查看尘歌壶摹本所需要的材料总览', 'pm_description': '查看尘歌壶摹本所需要的材料总览',
'pm_usage': '尘歌壶材料<摹数>', 'pm_usage': '尘歌壶材料<摹数>',
'pm_priority': 12 'pm_priority': 5
})
card_wiki = on_command('七圣召唤图鉴', aliases={'原牌图鉴', '原石传说图鉴', '原牌'}, priority=11, block=True, state={
'pm_name': '七圣召唤图鉴',
'pm_description': '查看七圣召唤图鉴,支持模糊查询',
'pm_usage': '七圣召唤图鉴<卡牌名>',
'pm_priority': 13
}) })
card_wiki_list = on_command('七圣召唤列表', aliases={'七圣召唤卡牌列表', '原牌列表', '原石传说列表'}, priority=11, card_wiki_list = on_command('七圣召唤列表', aliases={'七圣召唤卡牌列表', '原牌列表', '原石传说列表'}, priority=11,
rule=fullmatch_rule, block=True, state={ rule=fullmatch_rule, block=True, state={
'pm_name': '七圣召唤图鉴列表', 'pm_name': '七圣召唤图鉴列表',
'pm_description': '查看七圣召唤卡牌图鉴列表', 'pm_description': '查看已支持查询的七圣召唤卡牌图鉴列表',
'pm_usage': '原牌列表', 'pm_usage': '原牌列表',
'pm_priority': 14 'pm_priority': 6
}) })
week_str = ['周一', '周二', '周三', '周四', '周五', '周六']
@daily_material.handle()
async def _(event: MessageEvent, regex_dict: dict = RegexDict()):
if regex_dict['day'] in {'今日', '今天', '现在'}:
day = datetime.datetime.now().weekday()
elif regex_dict['day'] in {'明日', '明天'}:
day = (datetime.datetime.now() + datetime.timedelta(days=1)).weekday()
elif regex_dict['day'] in {'后日', '后天'}:
day = (datetime.datetime.now() + datetime.timedelta(days=2)).weekday()
elif regex_dict['day'] == '周日':
await daily_material.finish('周日所有材料都可以刷哦!', at_sender=True)
elif regex_dict['day'].startswith(''):
await daily_material.send('开始获取每日材料,请稍候...')
await daily_material.finish(await draw_material(str(event.user_id), regex_dict['day']))
if day == 6:
await daily_material.finish('周日所有材料都可以刷哦!', at_sender=True)
else:
await daily_material.send('开始获取每日材料,请稍候...')
await daily_material.finish(await draw_material(str(event.user_id), week_str[day]), at_sender=True)
@material_map.handle() @material_map.handle()
async def _(event: MessageEvent, state: T_State, msg: Message = CommandArg()): async def _(event: MessageEvent, state: T_State, msg: Message = CommandArg()):
@ -131,7 +99,7 @@ async def _(event: MessageEvent, state: T_State, msg: Message = CommandArg()):
@material_map.got('map', prompt='地图名称有误,请在【提瓦特、层岩巨渊、渊下宫】中选择,或回答【取消】退出', @material_map.got('map', prompt='地图名称有误,请在【提瓦特、层岩巨渊、渊下宫】中选择,或回答【取消】退出',
parameterless=[HandleCancellation(f'好吧,有需要再找{NICKNAME}')]) parameterless=cancel)
async def _(event: MessageEvent, state: T_State, map_: str = ArgPlainText('map')): async def _(event: MessageEvent, state: T_State, map_: str = ArgPlainText('map')):
if map_ not in {'提瓦特', '层岩巨渊', '渊下宫'}: if map_ not in {'提瓦特', '层岩巨渊', '渊下宫'}:
await material_map.reject('地图名称有误,请在【提瓦特、层岩巨渊、渊下宫】中选择') await material_map.reject('地图名称有误,请在【提瓦特、层岩巨渊、渊下宫】中选择')
@ -140,7 +108,7 @@ async def _(event: MessageEvent, state: T_State, map_: str = ArgPlainText('map')
@material_map.got('name', prompt='请输入要查询的材料名称,或回答【取消】退出', @material_map.got('name', prompt='请输入要查询的材料名称,或回答【取消】退出',
parameterless=[HandleCancellation(f'好吧,有需要再找{NICKNAME}')]) parameterless=cancel)
async def _(event: MessageEvent, map_: str = ArgPlainText('map'), name: str = ArgPlainText('name')): 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(): 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) await material_map.finish(MessageSegment.image(file_path), at_sender=True)
@ -163,7 +131,7 @@ async def _(event: MessageEvent, state: T_State, msg: Message = CommandArg()):
@material_map_full.got('names', prompt='请输入要查询的材料名称,或回答【取消】退出', @material_map_full.got('names', prompt='请输入要查询的材料名称,或回答【取消】退出',
parameterless=[HandleCancellation(f'好吧,有需要再找{NICKNAME}')]) parameterless=cancel)
async def _(event: MessageEvent, map_: str = Arg('map'), names=Arg('names')): async def _(event: MessageEvent, map_: str = Arg('map'), names=Arg('names')):
if isinstance(names, Message): if isinstance(names, Message):
names = names.extract_plain_text().split(' ') names = names.extract_plain_text().split(' ')
@ -179,11 +147,15 @@ async def _(event: MessageEvent, map_: str = Arg('map'), names=Arg('names')):
await material_map_full.finish(result, at_sender=True) await material_map_full.finish(result, at_sender=True)
@generate_map.handle() @generate_map.got('confirm',
prompt=f'生成材料地图资源要求大量内存,如果内存不足,可能会导致{NICKNAME}崩溃,请确认你的机器内存充足,回答【是|否】继续执行!')
async def _(event: MessageEvent): async def _(event: MessageEvent):
await generate_map.send('开始生成地图资源,这可能需要较长时间。') if convert_chinese_to_bool(event.message):
await generate_map.send('开始生成地图资源,这可能需要较长时间...')
result = await init_map() result = await init_map()
await generate_map.finish(result) await generate_map.finish(result)
else:
await generate_map.finish('取消生成地图资源!')
@pot_material.handle() @pot_material.handle()
@ -200,173 +172,160 @@ async def _(event: MessageEvent, msg: Message = CommandArg()):
await pot_material.finish(result, at_sender=True) await pot_material.finish(result, at_sender=True)
def create_wiki_matcher(pattern: str, help_fun: str, help_name: str): @total_wiki.handle()
maps = on_regex(pattern, priority=11, block=True, state={ async def _(state: T_State, regex_dict: dict = RegexDict()):
'pm_name': help_fun,
'pm_description': f"查看该{help_name}{help_fun}",
'pm_usage': f'<{help_name}名> {help_fun}',
'pm_priority': 5
})
maps.plugin_name = 'Paimon_Wiki'
@maps.handle()
async def _(event: MessageEvent, state: T_State, regex_dict: dict = RegexDict()):
if regex_dict['name1'] and regex_dict['name2']: if regex_dict['name1'] and regex_dict['name2']:
await maps.finish() await total_wiki.finish()
name = regex_dict['name1'] or regex_dict['name2'] name = regex_dict['name1'] or regex_dict['name2'] or ''
state['type'] = regex_dict['type'] type = regex_dict.get('type')
if '武器' in state['type']: if type == '材料':
state['type'] = '武器' if name.endswith(('角色', '天赋', '培养')):
# state['img_url'] = 'https://static.cherishmoon.fun/LittlePaimon/WeaponMaps/{}.jpg' state['type'] = '角色材料'
state['img_url'] = '{}https://raw.githubusercontent.com/Nwflower/genshin-atlas/master/weapon/{}.png' name = name[:-2]
elif '圣遗物' in state['type']: elif name.endswith('武器'):
state['type'] = '圣遗物' state['type'] = '武器图鉴'
state['img_url'] = 'https://static.cherishmoon.fun/LittlePaimon/ArtifactMaps/{}.jpg' name = name[:-2]
elif '怪物' in state['type'] or '原魔' in state['type']: elif re.match(r'现在|[今明后][天日]|周[一二三四五六日]', name):
state['type'] = '原魔' state['type'] = '每日材料'
state['img_url'] = 'https://static.cherishmoon.fun/LittlePaimon/MonsterMaps/{}.jpg'
elif state['type'] == '角色攻略':
state['type'] = '角色'
state['img_url'] = 'https://static.cherishmoon.fun/LittlePaimon/XFGuide/{}.jpg'
elif state['type'] == '角色材料':
state['type'] = '角色'
# state['img_url'] = 'https://static.cherishmoon.fun/LittlePaimon/RoleMaterials/{}材料.jpg'
state[
'img_url'] = '{}https://raw.githubusercontent.com/Nwflower/genshin-atlas/master/material%20for%20role/{}.png'
elif state['type'] == '角色图鉴':
state['type'] = '角色'
state[
'img_url'] = '{}https://raw.githubusercontent.com/CMHopeSunshine/GenshinWikiMap/master/results/character_map/{}.jpg'
elif state['type'] == '收益曲线':
state['type'] = '角色'
state['img_url'] = 'https://static.cherishmoon.fun/LittlePaimon/blue/{}.jpg'
elif state['type'] == '参考面板':
state['type'] = '角色'
state['img_url'] = 'https://static.cherishmoon.fun/LittlePaimon/blueRefer/{}.jpg'
if name:
state['name'] = name
@maps.got('name', prompt=Message.template('请提供要查询的{type}'),
parameterless=[HandleCancellation(f'好吧,有需要再找{NICKNAME}')])
async def _(event: MessageEvent, state: T_State):
name = state['name']
if isinstance(name, Message):
name = name.extract_plain_text().strip()
if state['type'] == '角色' and (
match_alias := await PlayerAlias.get_or_none(user_id=str(event.user_id), alias=name)):
a = '1'
try:
await maps.finish(
MessageSegment.image(state['img_url'].format(config.github_proxy, match_alias.character) if
not state['img_url'].startswith('http') else state['img_url'].format(
match_alias.character)))
except ActionFailed:
await maps.finish(MessageBuild.Text(f'没有找到{name}的图鉴'))
match_alias = get_match_alias(name, state['type'])
true_name = match_alias[0] if (
isinstance(match_alias, list) and len(match_alias) == 1) else match_alias if isinstance(match_alias,
str) else None
if true_name:
try:
await maps.finish(MessageSegment.image(state['img_url'].format(config.github_proxy, match_alias)
if not state['img_url'].startswith('http') else state[
'img_url'].format(match_alias)))
except ActionFailed:
await maps.finish(MessageBuild.Text(f'没有找到{name}的图鉴'))
elif match_alias:
if isinstance(match_alias, dict):
match_alias = list(match_alias.keys())
if 'choice' not in state:
msg = f'你要查询的{state["type"]}是:\n'
msg += '\n'.join([f'{int(i) + 1}. {name}' for i, name in enumerate(match_alias)])
await maps.send(msg + '\n回答\"取消\"来取消查询', at_sender=True)
state['match_alias'] = match_alias
else: else:
# await maps.finish(MessageBuild.Text(f'没有找到{name}的图鉴')) state['type'] = '材料'
await maps.finish() elif type in {'图鉴', '攻略'}:
if name.endswith(('角色', '天赋', '命座', '技能')):
state['type'] = f'角色{type}'
name = name[:-2]
elif name.endswith('武器'):
state['type'] = '武器图鉴'
name = name[:-2]
elif name.endswith(('原魔', '怪物')):
state['type'] = '原魔图鉴'
name = name[:-2]
elif name.endswith('圣遗物'):
state['type'] = '圣遗物图鉴'
name = name[:-3]
elif name.endswith(('七圣召唤', '原牌', '卡牌')):
state['type'] = '七圣召唤图鉴'
name = name.replace('七圣召唤', '').replace('原牌', '').replace('卡牌', '')
else:
state['type'] = type
elif type in {'参考面板', '收益曲线'}:
state['type'] = type
if name:
state['name'] = Message(name)
state['times'] = 1
@maps.got('choice', parameterless=[HandleCancellation(f'好吧,有需要再找{NICKNAME}')])
async def _(event: MessageEvent, state: T_State, choice: str = ArgPlainText('choice')): week_str = ['周一', '周二', '周三', '周四', '周五', '周六']
match_alias = state['match_alias']
if choice.isdigit() and (1 <= int(choice) <= len(match_alias)):
@total_wiki.got('name', prompt=Message.template('你要查询谁的{type}呢?'), parameterless=cancel)
async def _(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:
if type.startswith('角色') or type in {'参考面板', '收益曲线'}:
if alias := await PlayerAlias.get_or_none(user_id=str(event.user_id), alias=name):
final_name = alias.character
matches = {}
try: try:
await maps.finish(MessageSegment.image( await total_wiki.finish(
state['img_url'].format(match_alias[int(choice) - 1]) if state['img_url'].startswith('http') MessageSegment.image(API[type].format(proxy=config.github_proxy, name=final_name)))
else state['img_url'].format(config.github_proxy, match_alias[int(choice) - 1])))
except ActionFailed: except ActionFailed:
await maps.finish(MessageBuild.Text(f'没有找到{match_alias[int(choice) - 1]}的图鉴')) await total_wiki.finish(
if choice not in match_alias: MessageBuild.Text(f'{final_name}{type}发送失败,可能是网络问题或者不存在该资源'))
state['times'] = state['times'] + 1 if 'times' in state else 1 else:
if state['times'] == 1: matches = get_match_alias(name, ['角色'])
await maps.reject(f'请旅行者从上面的{state["type"]}中选一个问{NICKNAME}\n回答\"取消\"可以取消查询') elif type.startswith(('武器', '原魔')):
matches = get_match_alias(name, [type[:2]])
elif state['times'] == 2: elif type.startswith('圣遗物'):
await maps.reject(f'别调戏{NICKNAME}啦,快选一个吧,不想问了请回答\"取消\"') matches = get_match_alias(name, ['圣遗物'])
elif state['times'] >= 3: elif type.startswith('七圣召唤'):
await maps.finish( matches = await get_match_card(name)
MessageSegment.text(f'看来旅行者您有点神志不清哦(,下次再问{NICKNAME}') + MessageSegment.face(146)) else:
matches = get_match_alias(name, ['角色', '武器', '原魔', '圣遗物'])
if m := await get_match_card(name):
matches['七圣召唤'] = m
if not matches:
await total_wiki.finish()
elif len(matches) == 1 and len(list(matches.values())[0]) == 1:
final_name = list(matches.values())[0][0]
if type in {'材料', '攻略', '图鉴'}:
type = list(matches.keys())[0] + type
if type.endswith(('攻略', '图鉴')) and type.startswith(('原魔', '圣遗物', '武器', '七圣召唤')):
type = f'{type[-2:]}图鉴'
try: try:
await maps.finish(MessageSegment.image(state['img_url'].format( await total_wiki.finish(
state['img_url'].format(config.github_proxy, choice) if not state['img_url'].startswith( MessageSegment.image(API[type].format(proxy=config.github_proxy, name=final_name)))
'http') else state['img_url'].format(choice))))
except ActionFailed: except ActionFailed:
await maps.finish(MessageBuild.Text(f'没有找到{choice}的图鉴')) await total_wiki.finish(
MessageBuild.Text(f'{final_name}{type}发送失败,可能是网络问题或者不存在该资源'))
else:
create_wiki_matcher(r'(?P<name1>\w{0,7})(?P<type>(原魔|怪物)(图鉴|攻略))(?P<name2>\w{0,7})', '原魔图鉴', '原魔') msg = f'你要查询的{type}是:\n'
create_wiki_matcher(r'(?P<name1>\w{0,7})(?P<type>武器(图鉴|攻略))(?P<name2>\w{0,7})', '武器图鉴', '武器') index = 1
create_wiki_matcher(r'(?P<name1>\w{0,7})(?P<type>圣遗物(图鉴|攻略))(?P<name2>\w{0,7})', '圣遗物图鉴', '圣遗物') for key, value in matches.items():
create_wiki_matcher(r'(?P<name1>\w{0,7})(?P<type>角色攻略)(?P<name2>\w{0,7})', '角色攻略', '角色') for v in value:
create_wiki_matcher(r'(?P<name1>\w{0,7})(?P<type>角色材料)(?P<name2>\w{0,7})', '角色材料', '角色') msg += f'{index}.{v}({key})\n' if len(matches) > 1 else f'{index}.{v}\n'
create_wiki_matcher(r'(?P<name1>\w{0,7})(?P<type>角色图鉴)(?P<name2>\w{0,7})', '角色图鉴', '角色') index += 1
create_wiki_matcher(r'(?P<name1>\w{0,7})(?P<type>收益曲线)(?P<name2>\w{0,7})', '收益曲线', '角色') # msg += '\n'.join([f'{i}({key})' if len(matches) > 1 else f'{i}' for i in value]) + '\n'
create_wiki_matcher(r'(?P<name1>\w{0,7})(?P<type>参考面板)(?P<name2>\w{0,7})', '参考面板', '角色') msg += '回答\"序号\"查询或回答\"取消\"取消查询'
await total_wiki.send(msg)
@card_wiki.handle()
async def _(state: T_State, msg: Message = CommandArg()):
state['name'] = msg
@card_wiki.got('name', prompt='你想查询哪张卡牌的图鉴呢?')
async def _(state: T_State, name: str = ArgPlainText('name')):
if not (matches := await get_match_card(name)):
await card_wiki.finish(MessageBuild.Text(f'暂时没有{name}的卡牌图鉴'))
if name in matches:
await card_wiki.finish(MessageSegment.image(CARD_API.format(config.github_proxy, name)))
if len(matches) == 1:
await card_wiki.finish(MessageSegment.image(CARD_API.format(config.github_proxy, matches[0])))
if 'choice' not in state:
msg = f'你要查询的卡牌是:\n'
msg += '\n'.join([f'{int(i) + 1}. {name}' for i, name in enumerate(matches)])
await card_wiki.send(msg + '\n回答\"取消\"来取消查询', at_sender=True)
state['matches'] = matches state['matches'] = matches
@card_wiki.got('choice', parameterless=[HandleCancellation(f'好吧,有需要再找{NICKNAME}')]) @total_wiki.got('choice', parameterless=cancel)
async def _(state: T_State, choice: str = ArgPlainText('choice')): async def _(state: T_State, matches: dict = Arg('matches'), choice: str = ArgPlainText('choice'),
matches = state['matches'] type: str = Arg('type')):
if choice.isdigit() and (1 <= int(choice) <= len(matches)): 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: try:
await card_wiki.finish(MessageSegment.image(CARD_API.format(config.github_proxy, matches[int(choice) - 1]))) await total_wiki.finish(
MessageSegment.image(API[type].format(proxy=config.github_proxy, name=final_name)))
except ActionFailed: except ActionFailed:
await card_wiki.finish( await total_wiki.finish(
MessageBuild.Text(f'获取{matches[int(choice) - 1]}的卡牌图鉴失败,请检查网络或更换资源地址')) MessageBuild.Text(f'{final_name}{type}发送失败,可能是网络问题或者不存在该资源'))
if choice not in matches:
state['times'] = state['times'] + 1 if 'times' in state else 1
if state['times'] == 1:
await card_wiki.reject(f'请旅行者从上面的卡牌中选一个问{NICKNAME}\n回答\"取消\"可以取消查询')
elif state['times'] == 2: elif state['times'] == 2:
await card_wiki.reject(f'别调戏{NICKNAME}啦,快选一个吧,不想问了请回答\"取消\"') await total_wiki.finish(f'旅行者似乎不太能理解,下次再问我吧{MessageSegment.face(146)}')
elif state['times'] >= 3: else:
await card_wiki.finish( state['times'] = state['times'] + 1
MessageSegment.text(f'看来旅行者您有点神志不清哦(,下次再问{NICKNAME}') + MessageSegment.face(146)) await total_wiki.reject(f'请旅行者从上面的{type}中选一个问{NICKNAME}或回答\"取消\"可以取消查询', at_sender=True)
try:
await card_wiki.finish(MessageSegment.image(CARD_API.format(config.github_proxy, choice)))
except ActionFailed:
await card_wiki.finish(MessageBuild.Text(f'获取{choice}的卡牌图鉴失败,请检查网络或更换资源地址'))
@card_wiki_list.handle() @card_wiki_list.handle()

View File

@ -0,0 +1 @@
from .api import get_card_resources, get_match_card, CARD_API

View File

@ -6,13 +6,13 @@ from ruamel import yaml
from LittlePaimon.config import config from LittlePaimon.config import config
from LittlePaimon.utils.requests import aiorequests from LittlePaimon.utils.requests import aiorequests
CARD_RESOURCES_API = '{}https://raw.githubusercontent.com/Nwflower/Atlas/master/resource/text/card.yaml' CARD_RESOURCES_API = '{proxy}https://raw.githubusercontent.com/Nwflower/Atlas/master/resource/text/card.yaml'
CARD_API = '{}https://raw.githubusercontent.com/Nwflower/genshin-atlas/master/card/{}.png' CARD_API = '{proxy}https://raw.githubusercontent.com/Nwflower/genshin-atlas/master/card/{name}.png'
async def get_card_resources() -> Optional[dict]: async def get_card_resources() -> Optional[dict]:
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
resp = await aiorequests.get(CARD_RESOURCES_API.format(config.github_proxy)) resp = await aiorequests.get(CARD_RESOURCES_API.format(proxy=config.github_proxy))
data = yaml.load(resp.content, Loader=yaml.Loader) data = yaml.load(resp.content, Loader=yaml.Loader)
data.pop('召唤') data.pop('召唤')
return data return data

View File

@ -111,8 +111,10 @@ async def draw_map(name: str, map_: str):
if 'source' in info: if 'source' in info:
des += '\n推荐采集地点:' + ''.join(info['source']).replace('推荐:', '') des += '\n推荐采集地点:' + ''.join(info['source']).replace('推荐:', '')
if des: if des:
await total_img.text_box(des.replace('\n', '^'), (482, 1010), (281, 520), fm.get('SourceHanSansCN-Bold.otf', 30), '#3c3c3c') await total_img.text_box(des.replace('\n', '^'), (482, 1010), (281, 520),
await total_img.text('CREATED BY LITTLEPAIMON', (0, total_img.width), total_img.height - 45, fm.get('bahnschrift_bold', 36, 'Bold'), '#3c3c3c', align='center') fm.get('SourceHanSansCN-Bold.otf', 30), '#3c3c3c')
await total_img.text('CREATED BY LITTLEPAIMON', (0, total_img.width), total_img.height - 45,
fm.get('bahnschrift_bold', 36, 'Bold'), '#3c3c3c', align='center')
total_img.save(RESOURCE_BASE_PATH / 'genshin_map' / 'results' / f'{map_}_{name}.png') total_img.save(RESOURCE_BASE_PATH / 'genshin_map' / 'results' / f'{map_}_{name}.png')
return MessageBuild.Image(total_img, mode='RGB', quality=85) return MessageBuild.Image(total_img, mode='RGB', quality=85)
@ -144,20 +146,19 @@ async def get_full_map(names: List[str], map_: str):
return MessageBuild.Text(f'{map_}未查找到材料{"".join(names)},请尝试其他地图') return MessageBuild.Text(f'{map_}未查找到材料{"".join(names)},请尝试其他地图')
map_img = (await load_image(RESOURCE_BASE_PATH / 'genshin_map' / 'results' / f'{map_id.name}.png')).copy() map_img = (await load_image(RESOURCE_BASE_PATH / 'genshin_map' / 'results' / f'{map_id.name}.png')).copy()
box_icon = await load_image(RESOURCE_BASE_PATH / 'genshin_map' / 'point_box.png') box_icon = await load_image(RESOURCE_BASE_PATH / 'genshin_map' / 'point_box.png')
i = 0
max_point = XYPoint(x=0, y=0) max_point = XYPoint(x=0, y=0)
min_point = XYPoint(x=16384, y=12288) min_point = XYPoint(x=map_img.width, y=map_img.height)
for points in resources_points: for i, points in enumerate(resources_points):
resource_icon = box_icon.copy() resource_icon = box_icon.copy()
resource_icon.alpha_composite(await aiorequests.get_img(resources[i].icon, size=(90, 90)), (28, 15)) resource_icon.alpha_composite(await aiorequests.get_img(resources[i].icon, size=(90, 90)), (28, 15))
resource_icon = resource_icon.resize((48, 48), Image.ANTIALIAS) resource_icon = resource_icon.resize((48, 48), Image.ANTIALIAS)
if len(points) >= 3: if len(points) >= 3:
group_point = img.k_means_points(points, 16000) group_point = img.k_means_points(points, 2000)
else: else:
x1_temp = int(points[0].x) - 16000 x1_temp = int(points[0].x) - 2000
x2_temp = int(points[0].x) + 16000 x2_temp = int(points[0].x) + 2000
y1_temp = int(points[0].y) - 16000 y1_temp = int(points[0].y) - 2000
y2_temp = int(points[0].y) + 16000 y2_temp = int(points[0].y) + 2000
group_point = [( group_point = [(
models.XYPoint(x1_temp, y1_temp), models.XYPoint(x1_temp, y1_temp),
models.XYPoint(x2_temp, y2_temp), models.XYPoint(x2_temp, y2_temp),
@ -169,13 +170,9 @@ async def get_full_map(names: List[str], map_: str):
for point in group_point[0][2]: for point in group_point[0][2]:
point_trans = (int(point.x), int(point.y)) point_trans = (int(point.x), int(point.y))
map_img.paste(resource_icon, (point_trans[0] - 24, point_trans[1] - 48), resource_icon) map_img.paste(resource_icon, (point_trans[0] - 24, point_trans[1] - 48), resource_icon)
i += 1
map_img = map_img.crop((int(min_point.x) - 50, int(min_point.y) - 50, int(max_point.x) + 50, int(max_point.y) + 50)) map_img = map_img.crop((int(min_point.x) - 50, int(min_point.y) - 50, int(max_point.x) + 50, int(max_point.y) + 50))
if resources_not: if resources_not:
return MessageBuild.Text(f'{map_}未找到材料{"".join(resources_not)},请尝试其他地图\n') + MessageBuild.Image(map_img) return MessageBuild.Text(f'{map_}未找到材料{"".join(resources_not)},请尝试其他地图\n') + MessageBuild.Image(
map_img)
else: else:
return MessageBuild.Image(map_img) return MessageBuild.Image(map_img)

View File

@ -0,0 +1,14 @@
from typing import Dict
from .card import CARD_API
API: Dict[str, str] = {
'角色图鉴': '{proxy}https://raw.githubusercontent.com/CMHopeSunshine/GenshinWikiMap/master/results/character_map/{name}.jpg',
'角色攻略': 'https://static.cherishmoon.fun/LittlePaimon/XFGuide/{name}.jpg',
'角色材料': '{proxy}https://raw.githubusercontent.com/Nwflower/genshin-atlas/master/material%20for%20role/{name}.png',
'收益曲线': 'https://static.cherishmoon.fun/LittlePaimon/blue/{name}.jpg',
'参考面板': 'https://static.cherishmoon.fun/LittlePaimon/blueRefer/{name}.jpg',
'武器图鉴': '{proxy}https://raw.githubusercontent.com/Nwflower/genshin-atlas/master/weapon/{name}.png',
'圣遗物图鉴': 'https://static.cherishmoon.fun/LittlePaimon/ArtifactMaps/{name}.jpg',
'原魔图鉴': 'https://static.cherishmoon.fun/LittlePaimon/MonsterMaps/{name}.jpg',
'七圣召唤图鉴': CARD_API
}

View File

@ -4,7 +4,7 @@ from nonebot import get_driver
from .logger import logger from .logger import logger
from .scheduler import scheduler from .scheduler import scheduler
__version__ = '3.0.0rc6' __version__ = '3.0.0rc7'
DRIVER = get_driver() DRIVER = get_driver()
try: try:

View File

@ -1,5 +1,5 @@
import difflib from difflib import get_close_matches
from typing import Union, Literal, List, Optional from typing import Union, Literal, List, Optional, Dict
from .files import load_json from .files import load_json
from .path import JSON_DATA from .path import JSON_DATA
@ -43,42 +43,49 @@ def get_alias_by_name(name: str) -> Optional[List[str]]:
return next((r for r in name_list.values() if name in r), None) return next((r for r in name_list.values() if name in r), None)
def get_match_alias(msg: str, type: Literal['角色', '武器', '原魔', '圣遗物'] = '角色', single_to_dict: bool = False) -> Union[ def get_match_alias(name: str, types: List[Literal['角色', '武器', '原魔', '圣遗物']] = None,
str, list, dict]: one_to_list: bool = False) -> Union[
Dict[str, List[str]], List[str]]:
""" """
根据字符串消息获取与之相似或匹配的角色武器原魔名 根据字符串消息获取与之相似或匹配的角色武器原魔名
:param msg: 消息 :param name: 名称
:param type: 匹配类型有rolesweaponsmonsters :param types: 匹配类型'角色', '武器', '原魔', '圣遗物'
:param single_to_dict: 是否将角色单结果也转换成{角色:id}字典 :param one_to_list: 只有一种匹配结果时是否直接返回其列表
:return: 匹配的字符串列表或字典 :return: 匹配结果
""" """
if types is None:
types = ['角色']
matches = {}
for type in types:
alias_list = alias_file[type] alias_list = alias_file[type]
if msg in {'风主', '岩主', '雷主', '草主'}: matches[type] = []
return msg if type == '角色':
elif type == '角色': if name.startswith(('', '', '', '', '', '', '')) and name.endswith(
possible = {} ('', '主角', '', '', '旅行者')):
for role_id, alias in alias_list.items(): matches[type].append(name if name.endswith(('', '')) else f'{name[0]}')
match_list = difflib.get_close_matches(msg, alias, cutoff=0.6, n=3) continue
if msg in match_list: for alias in alias_list.values():
return {alias[0]: role_id} if single_to_dict else alias[0] if name in alias:
elif match_list: matches[type].append(alias[0])
possible[alias[0]] = role_id break
if len(possible) == 1: if get_close_matches(name, alias, cutoff=0.6, n=3):
return {list(possible.keys())[0]: possible[list(possible.keys())[0]]} if single_to_dict else \ matches[type].append(alias[0])
list(possible.keys())[0]
return possible
elif type in {'武器', '圣遗物'}: elif type in {'武器', '圣遗物'}:
possible = [] for raw_name, alias in alias_list.items():
for name, alias in alias_list.items(): if name in alias:
match_list = difflib.get_close_matches(msg, alias, cutoff=0.4, n=3) matches[type].append(raw_name)
if msg in match_list: break
return name else:
elif match_list: if get_close_matches(name, alias, cutoff=0.6, n=3):
possible.append(name) matches[type].append(raw_name)
return possible
elif type == '原魔': elif type == '原魔':
match_list = difflib.get_close_matches(msg, alias_list, cutoff=0.4, n=5) matches[type] = get_close_matches(name, alias_list, cutoff=0.4, n=5)
return match_list[0] if len(match_list) == 1 else match_list if not matches[type]:
del matches[type]
if one_to_list and len(matches) == 1:
return list(matches.values())[0]
else:
return matches
def get_chara_icon(name: Optional[str] = None, chara_id: Optional[int] = None, def get_chara_icon(name: Optional[str] = None, chara_id: Optional[int] = None,

View File

@ -210,7 +210,7 @@ def CommandCharacter(limit: int = 3) -> List[str]:
character_list.append(random.choice(FEMALE_CHARACTERS)) character_list.append(random.choice(FEMALE_CHARACTERS))
msg.replace(character_name, '') msg.replace(character_name, '')
# 如果有匹配别名 # 如果有匹配别名
elif character_match := get_match_alias(character_name, '角色', True): elif character_match := get_match_alias(character_name, ['角色'], True):
character_list.append(list(character_match.keys())[0]) character_list.append(list(character_match.keys())[0])
msg.replace(character_name, '') msg.replace(character_name, '')
# 没有匹配到角色时,结束事件 # 没有匹配到角色时,结束事件

View File

@ -7,6 +7,11 @@ try:
except ImportError: except ImportError:
import json import json
from . import DRIVER
command_start = list(DRIVER.config.command_start)
COMMAND_START_RE = '^' + '|'.join(command_start) if command_start else '^'
ElementType = Literal['', '', '', '', '', '', '', '物理'] ElementType = Literal['', '', '', '', '', '', '', '物理']
WeaponType = Literal['单手剑', '双手剑', '长柄武器', '', '法器'] WeaponType = Literal['单手剑', '双手剑', '长柄武器', '', '法器']