diff --git a/LittlePaimon/plugins/Learning_Chat/handler.py b/LittlePaimon/plugins/Learning_Chat/handler.py index a034a56..5bcd111 100644 --- a/LittlePaimon/plugins/Learning_Chat/handler.py +++ b/LittlePaimon/plugins/Learning_Chat/handler.py @@ -87,7 +87,7 @@ class LearningChat: return Result.SetEnable elif not await self._check_allow(self.data): # 本消息不合法,跳过 - logger.debug('群聊学习', f'➤消息未通过校验,跳过') + logger.debug('群聊学习', '➤消息未通过校验,跳过') return Result.Pass elif self.reply: # 如果是回复消息 @@ -150,41 +150,41 @@ class LearningChat: else: return [random.choice(DOUBT_WORDS)] elif result == Result.SetEnable: - # 开启/关闭学习 - if self.role in {'superuser', 'admin', 'owner'}: - # 检查权限 - if any(w in self.data.message for w in {'学说话', '快学', '开启学习'}): - self.config.update(enable=True) - config_manager.config.group_config[self.data.group_id] = self.config - config_manager.save() - return [random.choice(ENABLE_WORDS)] - else: - self.config.update(enable=False) - config_manager.config.group_config[self.data.group_id] = self.config - config_manager.save() - return [random.choice(DISABLE_WORDS)] - else: + if self.role not in {'superuser', 'admin', 'owner'}: return [random.choice(NO_PERMISSION_WORDS)] + # 检查权限 + if any(w in self.data.message for w in {'学说话', '快学', '开启学习'}): + self.config.update(enable=True) + config_manager.config.group_config[self.data.group_id] = self.config + config_manager.save() + return [random.choice(ENABLE_WORDS)] + else: + self.config.update(enable=False) + config_manager.config.group_config[self.data.group_id] = self.config + config_manager.save() + return [random.choice(DISABLE_WORDS)] elif result == Result.Pass: # 跳过 return None elif result == Result.Repeat: - query_set = ChatMessage.filter(group_id=self.data.group_id, time__gte=self.data.time - 3600) - if await query_set.limit(self.config.repeat_threshold + 5).filter( + if await ChatMessage.filter(group_id=self.data.group_id, + time__gte=self.data.time - 3600).limit( + self.config.repeat_threshold + 5).filter( user_id=self.bot_id, message=self.data.message).exists(): # 如果在阈值+5条消息内,bot已经回复过这句话,则跳过 - logger.debug('群聊学习', f'➤➤已经复读过了,跳过') + logger.debug('群聊学习', '➤➤已经复读过了,跳过') return None - if not (messages := await query_set.limit( - self.config.repeat_threshold + 5)): + if not (messages := await ChatMessage.filter( + group_id=self.data.group_id, + time__gte=self.data.time - 3600).limit(self.config.repeat_threshold)): return None # 如果达到阈值,且不是全都为同一个人在说,则进行复读 if len(messages) >= self.config.repeat_threshold and all( - message.message == self.data.message and message.user_id != self.bot_id - for message in messages) and not all( - message.user_id == messages[0].user_id for message in messages): + message.message == self.data.message + for message in messages) and all(message.user_id != messages[0].user_id + for message in messages): if random.random() < self.config.break_probability: - logger.debug('群聊学习', f'➤➤达到复读阈值,打断复读!') + logger.debug('群聊学习', '➤➤达到复读阈值,打断复读!') return [random.choice(BREAK_REPEAT_WORDS)] else: logger.debug('群聊学习', f'➤➤达到复读阈值,复读{messages[0].message}') @@ -258,17 +258,13 @@ class LearningChat: """屏蔽消息""" bot = get_bot() if message_id: - # 如果有指定消息ID,则屏蔽该消息 - if ( - message := await ChatMessage.filter( - message_id=message_id).first()) and message.message not in ALL_WORDS: - keywords = message.keywords - try: - await bot.delete_msg(message_id=message_id) - except ActionFailed: - logger.info('群聊学习', f'待禁用消息{message_id}尝试撤回失败') - else: + if not (message := await ChatMessage.filter(message_id=message_id).first()) or message.message in ALL_WORDS: return False + keywords = message.keywords + try: + await bot.delete_msg(message_id=message_id) + except ActionFailed: + logger.info('群聊学习', f'待禁用消息{message_id}尝试撤回失败') elif (last_reply := await ChatMessage.filter(group_id=self.data.group_id, user_id=self.bot_id).first()) and ( last_reply.message not in ALL_WORDS): # 没有指定消息ID,则屏蔽最后一条回复 @@ -328,7 +324,6 @@ class LearningChat: ban_word = ChatBlackList(keywords=data.keywords, ban_group_id=[data.group_id]) await ChatAnswer.filter(keywords=data.keywords, group_id=data.group_id).delete() else: - logger.info('群聊学习', f'学习词{data.keywords}将被全局禁用') ban_word = ChatBlackList(keywords=data.keywords, global_ban=True) await ChatAnswer.filter(keywords=data.keywords).delete() @@ -342,7 +337,7 @@ class LearningChat: today_time = time.mktime(datetime.date.today().timetuple()) # 获取两小时内消息超过10条的群列表 groups = await ChatMessage.filter(time__gte=today_time).annotate(count=Count('id')).group_by('group_id'). \ - filter(count__gte=10).values_list('group_id', flat=True) + filter(count__gte=10).values_list('group_id', flat=True) if not groups: return None total_messages = {} @@ -428,9 +423,8 @@ class LearningChat: continue if message.startswith('[') and message.endswith(']'): continue - if any(word in message for word in ban_words): - continue - speak_list.append(message) + if all(word not in message for word in ban_words): + speak_list.append(message) else: break else: @@ -438,8 +432,7 @@ class LearningChat: if speak_list: # logger.debug('群聊学习', f'主动发言:将向群{group_id}主动发言{" ".join(speak_list)}') if random.random() < config.speak_poke_probability: - last_speak_users = set( - message.user_id for message in messages[:5] if message.user_id != self_id) + last_speak_users = {message.user_id for message in messages[:5] if message.user_id != self_id} select_user = random.choice(list(last_speak_users)) speak_list.append(MessageSegment('poke', {'qq': select_user})) return group_id, speak_list diff --git a/LittlePaimon/plugins/Paimon_Info/damage_model.py b/LittlePaimon/plugins/Paimon_Info/damage_model.py index ce95ae1..feb1993 100644 --- a/LittlePaimon/plugins/Paimon_Info/damage_model.py +++ b/LittlePaimon/plugins/Paimon_Info/damage_model.py @@ -409,9 +409,12 @@ def weapon_common_fix(info: Character): info.prop.elemental_mastery += 5 * (24 + 8 * info.weapon.affix_level) for i in info.prop.dmg_bonus: info.prop.dmg_bonus[i] = info.prop.dmg_bonus[i] + 2 * (0.06 + 0.04 * info.weapon.affix_level) - info.damage_describe.append('千夜浮梦算1同2异') + info.damage_describe.append('武器算1同2异') elif info.weapon.name == '流浪的晚星': info.prop.extra_attack += info.prop.elemental_mastery * (0.18 + 0.06 * info.weapon.affix_level) + elif info.weapon.name == '魔导绪论': + for i in info.prop.dmg_bonus: + info.prop.dmg_bonus[i] = info.prop.dmg_bonus[i] + 0.09 + 0.03 * info.weapon.affix_level # 系列武器 elif info.weapon.name.startswith('千岩'): @@ -985,19 +988,18 @@ def get_damage_multipiler(info: Character) -> Optional[Dict[str, any]]: info.prop.elemental_mastery += 160 if len(info.constellation) >= 4 else 0 eb = skill_data['所闻遍计']['数值']['灭净三业伤害'][level_e].split('+') return { - 'B:l0-增伤-E': (float(skill_data['心景幻成']['数值'][f'火:伤害提升{c}'][level_q].replace('%', '')) / 100.0, f'大招计算{c}名火元素'), + 'B:l0-增伤-E': (float(skill_data['心景幻成']['数值'][f'火:伤害提升{c}'][level_q].replace('%', '')) / 100.0, f'大招计算{c}火'), 'B:l70-增伤-E': (0.8 if info.prop.elemental_mastery >= 1000 else ( 0.001 * (info.prop.elemental_mastery - 200)) if info.prop.elemental_mastery >= 200 else 0, ), 'B:l70-暴击率-E': (0.24 if info.prop.elemental_mastery >= 1000 else ( 0.0003 * (info.prop.elemental_mastery - 200)) if info.prop.elemental_mastery >= 200 else 0,), - 'B:c2-减防-*': (0.3, '二命减防触发'), + 'B:c2-减防-*': (0.3, '二命减防'), 'B:l0-额外倍率-E': (float(eb[1].replace('%元素精通', '')) / 100.0 * info.prop.elemental_mastery, ), 'E-e草:灭净三业': float(eb[0].replace('%攻击力', '')) / 100.0, 'E-e草-j蔓激化:灭净三业蔓激化': float(eb[0].replace('%攻击力', '')) / 100.0 } - async def draw_dmg_pic(dmg: Dict[str, Union[tuple, list]]) -> PMImage: """ 绘制伤害图片 diff --git a/LittlePaimon/plugins/Paimon_Wiki/__init__.py b/LittlePaimon/plugins/Paimon_Wiki/__init__.py index fda6502..10953ad 100644 --- a/LittlePaimon/plugins/Paimon_Wiki/__init__.py +++ b/LittlePaimon/plugins/Paimon_Wiki/__init__.py @@ -18,6 +18,7 @@ from LittlePaimon.utils.tool import freq_limiter 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 .card import get_match_card, CARD_API, get_card_list __paimon_help__ = { 'type': '原神Wiki', @@ -69,12 +70,26 @@ generate_map = on_command('生成地图', priority=1, block=True, permission=SUP 'pm_usage': '生成地图', 'pm_priority': 11 }) -pot_material = on_command('尘歌壶摹本', aliases={'摹本材料', '尘歌壶材料', '尘歌壶摹本材料'}, priority=11, block=True, state={ - 'pm_name': '尘歌壶摹本材料', - 'pm_description': '查看尘歌壶摹本所需要的材料总览', - 'pm_usage': '尘歌壶材料<摹数>', - 'pm_priority': 12 +pot_material = on_command('尘歌壶摹本', aliases={'摹本材料', '尘歌壶材料', '尘歌壶摹本材料'}, priority=11, block=True, + state={ + 'pm_name': '尘歌壶摹本材料', + 'pm_description': '查看尘歌壶摹本所需要的材料总览', + 'pm_usage': '尘歌壶材料<摹数>', + 'pm_priority': 12 + }) +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, + block=True, state={ + 'pm_name': '七圣召唤图鉴列表', + 'pm_description': '查看七圣召唤卡牌图鉴列表', + 'pm_usage': '原牌列表', + 'pm_priority': 14 + }) week_str = ['周一', '周二', '周三', '周四', '周五', '周六'] @@ -194,6 +209,8 @@ def create_wiki_matcher(pattern: str, help_fun: str, help_name: str): @maps.handle() async def _(event: MessageEvent, state: T_State, regex_dict: dict = RegexDict()): + if regex_dict['name1'] and regex_dict['name2']: + await maps.finish() name = regex_dict['name1'] or regex_dict['name2'] state['type'] = regex_dict['type'] if '武器' in state['type']: @@ -211,7 +228,8 @@ def create_wiki_matcher(pattern: str, help_fun: str, help_name: str): elif state['type'] == '角色材料': state['type'] = '角色' # state['img_url'] = 'https://static.cherishmoon.fun/LittlePaimon/RoleMaterials/{}材料.jpg' - state['img_url'] = 'https://ghproxy.com/https://raw.githubusercontent.com/Nwflower/genshin-atlas/master/material%20for%20role/{}.png' + state[ + 'img_url'] = 'https://ghproxy.com/https://raw.githubusercontent.com/Nwflower/genshin-atlas/master/material%20for%20role/{}.png' elif state['type'] == '收益曲线': state['type'] = '角色' state['img_url'] = 'https://static.cherishmoon.fun/LittlePaimon/blue/{}.jpg' @@ -251,7 +269,8 @@ def create_wiki_matcher(pattern: str, help_fun: str, help_name: str): await maps.send(msg + '\n回答\"取消\"来取消查询', at_sender=True) state['match_alias'] = match_alias else: - await maps.finish(MessageBuild.Text(f'没有找到{name}的图鉴')) + # await maps.finish(MessageBuild.Text(f'没有找到{name}的图鉴')) + await maps.finish() @maps.got('choice', parameterless=[HandleCancellation(f'好吧,有需要再找{NICKNAME}')]) async def _(event: MessageEvent, state: T_State, choice: str = ArgPlainText('choice')): @@ -264,24 +283,71 @@ def create_wiki_matcher(pattern: str, help_fun: str, help_name: str): if choice not in match_alias: state['times'] = state['times'] + 1 if 'times' in state else 1 if state['times'] == 1: - await maps.reject(f'请旅行者从上面的{state["type"]}中选一个问{NICKNAME}\n回答\"取消\"可以取消查询', - at_sender=True) + await maps.reject(f'请旅行者从上面的{state["type"]}中选一个问{NICKNAME}\n回答\"取消\"可以取消查询') elif state['times'] == 2: - await maps.reject(f'别调戏{NICKNAME}啦,快选一个吧,不想问了请回答\"取消\"!', at_sender=True) + await maps.reject(f'别调戏{NICKNAME}啦,快选一个吧,不想问了请回答\"取消\"!') elif state['times'] >= 3: - await maps.finish(f'看来旅行者您有点神志不清哦(,下次再问{NICKNAME}吧{MessageSegment.face(146)}', - at_sender=True) + await maps.finish( + MessageSegment.text(f'看来旅行者您有点神志不清哦(,下次再问{NICKNAME}吧') + MessageSegment.face(146)) try: await maps.finish(MessageSegment.image(state['img_url'].format(choice))) except ActionFailed: await maps.finish(MessageBuild.Text(f'没有找到{choice}的图鉴')) -create_wiki_matcher(r'(?P\w*)(?P(原魔|怪物)(图鉴|攻略))(?P\w*)', '原魔图鉴', '原魔') -create_wiki_matcher(r'(?P\w*)(?P武器(图鉴|攻略))(?P\w*)', '武器图鉴', '武器') -create_wiki_matcher(r'(?P\w*)(?P圣遗物(图鉴|攻略))(?P\w*)', '圣遗物图鉴', '圣遗物') -create_wiki_matcher(r'(?P\w*)(?P角色攻略)(?P\w*)', '角色攻略', '角色') -create_wiki_matcher(r'(?P\w*)(?P角色材料)(?P\w*)', '角色材料', '角色') -create_wiki_matcher(r'(?P\w*)(?P收益曲线)(?P\w*)', '收益曲线', '角色') -create_wiki_matcher(r'(?P\w*)(?P参考面板)(?P\w*)', '参考面板', '角色') +create_wiki_matcher(r'(?P\w{0,7})(?P(原魔|怪物)(图鉴|攻略))(?P\w{0,7})', '原魔图鉴', '原魔') +create_wiki_matcher(r'(?P\w{0,7})(?P武器(图鉴|攻略))(?P\w{0,7})', '武器图鉴', '武器') +create_wiki_matcher(r'(?P\w{0,7})(?P圣遗物(图鉴|攻略))(?P\w{0,7})', '圣遗物图鉴', '圣遗物') +create_wiki_matcher(r'(?P\w{0,7})(?P角色攻略)(?P\w{0,7})', '角色攻略', '角色') +create_wiki_matcher(r'(?P\w{0,7})(?P角色材料)(?P\w{0,7})', '角色材料', '角色') +create_wiki_matcher(r'(?P\w{0,7})(?P收益曲线)(?P\w{0,7})', '收益曲线', '角色') +create_wiki_matcher(r'(?P\w{0,7})(?P参考面板)(?P\w{0,7})', '参考面板', '角色') + + +@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(name))) + 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 + + +@card_wiki.got('choice', parameterless=[HandleCancellation(f'好吧,有需要再找{NICKNAME}')]) +async def _(state: T_State, choice: str = ArgPlainText('choice')): + matches = state['matches'] + if choice.isdigit() and (1 <= int(choice) <= len(matches)): + try: + await card_wiki.finish(MessageSegment.image(CARD_API.format(matches[int(choice) - 1]))) + except ActionFailed: + await card_wiki.finish(MessageBuild.Text(f'暂时没有{matches[int(choice) - 1]}的卡牌图鉴')) + 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: + await card_wiki.reject(f'别调戏{NICKNAME}啦,快选一个吧,不想问了请回答\"取消\"!') + elif state['times'] >= 3: + await card_wiki.finish( + MessageSegment.text(f'看来旅行者您有点神志不清哦(,下次再问{NICKNAME}吧') + MessageSegment.face(146)) + try: + await card_wiki.finish(MessageSegment.image(CARD_API.format(choice))) + except ActionFailed: + await card_wiki.finish(MessageBuild.Text(f'暂时没有{choice}的卡牌图鉴')) + + +@card_wiki_list.handle() +async def _(): + result = await get_card_list() + await card_wiki_list.finish(result or '暂时没有卡牌图鉴') diff --git a/LittlePaimon/plugins/Paimon_Wiki/card.py b/LittlePaimon/plugins/Paimon_Wiki/card.py new file mode 100644 index 0000000..a4bf659 --- /dev/null +++ b/LittlePaimon/plugins/Paimon_Wiki/card.py @@ -0,0 +1,33 @@ +import contextlib +import difflib +from LittlePaimon.utils import scheduler +from LittlePaimon.utils.requests import aiorequests + +card_list = [] +CARD_RESOURCES_API = 'https://api.github.com/repos/Nwflower/genshin-atlas/contents/card' +CARD_API = 'https://ghproxy.com/https://raw.githubusercontent.com/Nwflower/genshin-atlas/master/card/{}.png' + + +async def update_card_list(): + with contextlib.suppress(Exception): + resp = await aiorequests.get(CARD_RESOURCES_API) + if resp.status_code == 200: + card_list.extend([card['name'].split('.')[0] for card in resp.json()]) + return None + + +async def get_card_list(): + if not card_list: + await update_card_list() + return '七圣召唤原牌列表:\n' + '\n'.join([' '.join(card_list[i:i + 3]) for i in range(0, len(card_list), 3)]) + + +async def get_match_card(name: str): + if not card_list: + await update_card_list() + return difflib.get_close_matches(name, card_list, cutoff=0.6, n=10) if card_list else None + + +@scheduler.scheduled_job('cron', hour='*/2') +async def _(): + await update_card_list()