def on_keyword(self, keywords: Iterable, *, normalize=True, event='group',can_private=False) -> Callable: if isinstance(keywords, str): keywords = (keywords, ) if normalize: keywords = tuple(util.normalize_str(kw) for kw in keywords) if can_private: event=None def deco(func: Callable[[NoneBot, Dict], Any]) -> Callable: @wraps(func) async def wrapper(ctx): ctx['can_private']=can_private if self._check_all(ctx): plain_text = ctx['message'].extract_plain_text() if normalize: plain_text = util.normalize_str(plain_text) ctx['plain_text'] = plain_text for kw in keywords: if kw in plain_text: try: await func(self.bot, ctx) self.logger.info( f'Message {ctx["message_id"]} is handled by {func.__name__}, triggered by keyword.') except Exception as e: self.logger.exception(e) self.logger.error( f'{type(e)} occured when {func.__name__} handling message {ctx["message_id"]}.') return return self.bot.on_message(event)(wrapper) return deco
async def set_yocool_themes(session): uid = session.event.user_id if uid not in hoshino.config.SUPERUSERS: return if not os.path.exists(current_info_path): await session.send('没有找到YoCool信息配置文件,请发送【安装YoCool】后再试') name = util.normalize_str(session.current_arg_text) if not name: await session.finish(THEMES_NAME_TIP, at_sender=True) elif name in ('公主冒险', 'PrincessAdventure', '1'): select = 1 elif name in ('轻酷', 'CoolLite', '2'): select = 2 else: await session.finish(f'没有找到主题{name},请检查输入后再试') with open(current_info_path, 'r', encoding='utf-8') as f: current_updata_json = json.load(f) current_updata_json['Themes'] = select with open(current_info_path, 'w+', encoding='utf-8') as f: json.dump(current_updata_json, f, indent=4, ensure_ascii=False) themes = get_yocool_themes(select) hoshino.logger.info(f'设置YoCool主题为{themes}') await session.send('开始切换主题,需要一定时间,请耐心等待……') shutil.rmtree(yobot_themes_path) await asyncio.sleep(5) await update_yocool(force=True) await session.finish(f'YoCool主题已切换为{themes}')
def on_keyword(self, keywords: Iterable, normalize=False, event=None): if isinstance(keywords, str): keywords = (keywords, ) normalized_keywords = tuple(util.normalize_str(kw) for kw in keywords) def deco(func): @wraps(func) async def wrapper(ctx): if await self.check_permission(ctx): plain_text = ctx['message'].extract_plain_text() if normalize: plain_text = util.normalize_str(plain_text) ctx['plain_text'] = plain_text for kw in normalized_keywords: if plain_text.find(kw) >= 0: try: await func(self.bot, ctx) self.logger.info( f'Message {ctx["message_id"]} is handled by {func.__name__}, triggered by keyword.' ) except Exception as e: self.logger.exception(e) self.logger.error( f'{type(e)} occured when {func.__name__} handling message {ctx["message_id"]}.' ) return return self.bot.on_message(event)(wrapper) return deco
async def update_pcrdata(): ''' 对比本地和远程的_pcr_data.py, 自动补充本地没有的角色信息, 已有角色信息进行补全 ''' online_pcrdata = await get_online_pcrdata() hoshino.logger.info('开始对比角色数据') if online_pcrdata == {}: return -1 for id in online_pcrdata: # 增加对只有一个key的名字的检查,从而更新之前检查所添加的没有别称的新角色 if (id not in _pcr_data.CHARA_NAME or len(_pcr_data.CHARA_NAME[id]) == 1) and id != 9401: hoshino.logger.info(f'已开始更新角色{id}的数据和图标') # 由于返回数据可能出现全半角重复, 做一定程度的兼容性处理, 会将所有全角替换为半角, 并移除重复别称 for i, name in enumerate(online_pcrdata[id]): name_format = name.replace('(', '(') name_format = name_format.replace(')', ')') name_format = util.normalize_str(name_format) online_pcrdata[id][i] = name_format n = online_pcrdata[id][0] group = {f'{n}'} # 转集合再转列表, 移除重复元素, 按日文原名优先顺序排列 m = list(set(online_pcrdata[id])) sort_priority(m, group) _pcr_data.chara_master.add_chara(id, m) download_chara_icon(id, 6) download_chara_icon(id, 3) download_chara_icon(id, 1) # 重载花名册(不会引起全局reload) roster.update()
async def _clanbattle_bus(bot:NoneBot, ctx): # check prefix start = '' for m in ctx['message']: if m.type == 'text': start = m.data.get('text', '').lstrip() break if not start or start[0] not in '!!': return # find cmd plain_text = ctx['message'].extract_plain_text() cmd, *args = plain_text.split() cmd = util.normalize_str(cmd[1:]) if cmd in _registry: func, parser = _registry[cmd] try: sv.logger.info(f'Message {ctx["message_id"]} is a clanbattle command, start to process by {func.__name__}.') args = parser.parse(args, ctx['message']) await func(bot, ctx, args) sv.logger.info(f'Message {ctx["message_id"]} is a clanbattle command, handled by {func.__name__}.') except DatabaseError as e: await bot.send(ctx, f'DatabaseError: {e.message}\n{SORRY}\n※请及时联系维护组', at_sender=True) except ClanBattleError as e: await bot.send(ctx, e.message, at_sender=True) except Exception as e: sv.logger.exception(e) sv.logger.error(f'{type(e)} occured when {func.__name__} handling message {ctx["message_id"]}.') await bot.send(ctx, f'Error: 机器人出现未预料的错误\n{SORRY}\n※请及时联系维护组', at_sender=True)
async def set_pool(bot, ev: CQEvent): if not priv.check_priv(ev, priv.ADMIN): await bot.finish(ev, '只有群管理才能切换卡池', at_sender=True) name = util.normalize_str(ev.message.extract_plain_text()) if not name: await bot.finish(ev, POOL_NAME_TIP, at_sender=True) elif name in ('国', '国服', 'cn'): await bot.finish(ev, '请选择以下卡池\n> 选择卡池 b服\n> 选择卡池 台服') elif name in ('b', 'b服', 'bl', 'bilibili'): name = 'BL' elif name in ('台', '台服', 'tw', 'sonet'): name = 'TW' elif name in ('日', '日服', 'jp', 'cy', 'cygames'): name = 'JP' elif name in ('混', '混合', 'mix'): name = 'MIX' elif name in ('阴间', '整活', 'funny'): name = '阴间' elif name in ('怪兽', '怪物', 'monster'): name = '怪物' else: await bot.finish(ev, f'未知服务器地区 {POOL_NAME_TIP}', at_sender=True) gid = str(ev.group_id) _group_pool[gid] = name dump_pool_config() await bot.send(ev, f'卡池已切换为{name}池', at_sender=True) await gacha_info(bot, ev)
async def one_key_yocool(session): uid = session.event.user_id if uid not in hoshino.config.SUPERUSERS: return if os.path.exists(backup_themes_path): await session.finish('您已经安装过了,如需更新请发送【更新YoCool】') name = util.normalize_str(session.current_arg_text) if not name: select = 1 else: if f"{name}" in themes_list: select = name else: await session.finish(f'没有找到主题{name},请检查输入后再试') hoshino.logger.info('正在进行安装前初始化') if os.path.exists(current_info_path): for infile in glob.glob(os.path.join(path, '*.json')): os.remove(infile) yocool_info_json = await get_api_url(yocool_info_url) yocool_info_json['Themes'] = select with open(current_info_path, 'w+', encoding='utf-8') as f: json.dump(yocool_info_json, f, indent=4, ensure_ascii=False) themes = await get_yocool_themes(select) await session.send(f'YoCool初始化完成,准备使用{themes}主题进行安装,安装需要一定时间,请耐心等待……') status, version, updatenote = await update_yocool(force=True) if status == 0: for infile in glob.glob(os.path.join(path, '*.json')): os.remove(infile) await session.finish('本地版本信息异常!请重新发送指令再试!') elif status < 1000: await session.finish(f'发生错误{status},请在控制台日志中查看详细') else: await session.finish( f'一键安装已完成!\n当前YoCool版本:YoCool-{version}\n使用主题:{themes}\n更新日志:\n{updatenote}' )
async def set_pool(session: CommandSession): if not sv.check_priv(session.ctx, required_priv=Priv.ADMIN): session.finish('只有群管理才能切换卡池', at_sender=True) name = util.normalize_str(session.current_arg_text) if not name: session.finish(POOL_NAME_TIP, at_sender=True) elif name in ('国', '国服', 'cn'): session.finish('请选择以下卡池\n> 选择卡池 b服\n> 选择卡池 台服\n> ') elif name in ('b', 'b服', 'bl', 'bilibili'): name = 'BL' elif name in ('台', '台服', 'tw', 'sonet'): name = 'TW' elif name in ('日', '日服', 'jp', 'cy', 'cygames'): name = 'JP' elif name in ('混', '混合', 'mix'): name = 'MIX' elif name in ('群', '群员', '苍蓝星', '狗托'): name = 'QUN' else: session.finish(f'未知服务器地区 {POOL_NAME_TIP}', at_sender=True) gid = str(session.ctx['group_id']) _group_pool[gid] = name dump_pool_config() await session.send(f'卡池已切换为{name}池', at_sender=True) await gacha_info(session)
def update(self): self._roster.clear() for idx, names in cfg.chara_info.items(): for n in names: n = util.normalize_str(n) if n not in self._roster: self._roster[n] = int(idx) self._all_name_list = self._roster.keys()
def damage_int(x:str) -> int: x = util.normalize_str(x) m = _rex_dint.match(x) if m: x = int(m.group(1)) * _unit_rate[m.group(2).lower()] if x < 100000000: return x raise ParseError('伤害值不合法 伤害值应为小于一亿的非负整数')
async def set_yocool_themes(session): uid = session.event.user_id if uid not in hoshino.config.SUPERUSERS: return if not os.path.exists(current_info_path): await session.send('没有找到YoCool信息配置文件,请安装YoCool后再试') ins = get_install_state() if ins == 0: await session.finish('当前未安装YoCool,无法切换主题') name = util.normalize_str(session.current_arg_text) themes_list = await get_api_url(yocool_themes_url) themes_tip = '请选择需要切换的主题:' for num in themes_list: name_list = themes_list[f"{num}"] themes_name = name_list.split(',') themes_tip += f"\n{num}.{themes_name[0]}-{themes_name[1]}" themes_tip += '\n*回复“切换主题 序号”即可' if not name: await session.finish(themes_tip, at_sender=True) else: if f"{name}" in themes_list: select = name else: await session.finish(f'没有找到主题{name},请检查输入后再试') with open(current_info_path, 'r', encoding='utf-8') as f: current_updata_json = json.load(f) if current_updata_json['Themes'] == select: await session.finish(f'当前已在使用该主题,请选择其他主题') else: current_updata_json['Themes'] = select with open(current_info_path, 'w+', encoding='utf-8') as f: json.dump(current_updata_json, f, indent=4, ensure_ascii=False) themes = await get_yocool_themes(select) hoshino.logger.info(f'设置YoCool主题为{themes}') await session.send('开始切换主题,需要一定时间,请耐心等待……') yocode = await uninstall_yocool() if yocode == 0: await session.finish('尚未安装YoCool,无法切换主题') elif yocode == 1: await session.finish('YoCool完整性验证未通过,没有找到YoCool文件或备份文件夹') elif yocode == 2: await session.finish('移除YoCool文件出错,请手动卸载') elif yocode == 3: await session.finish('恢复原生主题出错,请手动恢复') elif yocode == 4: await session.finish('清理残留出错,请手动删除backup文件夹') else: hoshino.logger.info(f'原生主题已恢复') try: status, version, updatenote = await update_yocool(force=True) except: await session.send('主题切换异常中断,请排查问题后再次尝试') if status == 0: await session.finish(f'状态{status}') elif status < 1000: await session.finish(f'发生错误{status},请在控制台日志中查看详细') else: await session.finish(f'YoCool主题已切换为{themes}')
def server_code(x: str) -> int: x = util.normalize_str(x) if x in ('jp', '日', '日服'): return BattleMaster.SERVER_JP elif x in ('tw', '台', '台服'): return BattleMaster.SERVER_TW elif x in ('cn', '国', '国服', 'b', 'b服'): return BattleMaster.SERVER_CN raise ParseError('未知服务器地区 请用jp/tw/cn')
def add(self, keyword: str, sf: "ServiceFunc"): if sf.normalize_text: keyword = util.normalize_str(keyword) if keyword in self.allkw: other = self.allkw[keyword] hoshino.logger.warning(f'Failed to add keyword trigger `{keyword}`: Conflicts between {sf.__name__} and {other.__name__}') return self.allkw[keyword] = sf hoshino.logger.debug(f'Succeeded in adding keyword trigger `{keyword}`')
async def log_dit(bot, ev: CQEvent): num = util.normalize_str(ev.message.extract_plain_text()) logs = list() if not num: logs = ditter.dit_log(str(ev.group_id)) else: logs = ditter.dit_log(str(ev.group_id), num=int(num)) await bot.send(ev, get_format(logs))
def add(self, keyword: str, sf: "ServiceFunc"): if sf.normalize_text: keyword = util.normalize_str(keyword) if keyword in self.allkw: self.allkw[keyword].append(sf) hoshino.logger.warning(f"Keyword trigger `{keyword}` added multi handler: `{sf.__name__}`") else: self.allkw[keyword] = [sf] hoshino.logger.debug(f"Succeed to add keyword trigger `{keyword}`")
def server_code(x: str) -> int: x = util.normalize_str(x.strip()) if x in ("jp", "日", "日服"): return SERVER.JP elif x in ("tw", "台", "台服"): return SERVER.TW elif x in ("cn", "国", "国服", "b", "b服"): return SERVER.BL raise ParseError("未知服务器地区 请用jp/tw/b")
def boss_code(x:str) -> int: x = util.normalize_str(x) m = _rex1_bcode.match(x) if m: return int(m.group(1)) elif m : m = _rex2_bcode.match(x) if m: return '零一二三四五'.find(m.group(1)) raise ParseError('Boss编号不合法 应为1-5的整数')
def update(self): importlib.reload(_pcr_data) self._roster.clear() for idx, names in _pcr_data.CHARA_NAME.items(): for n in names: n = util.normalize_str(n) if n not in self._roster: self._roster[n] = idx else: logger.warning(f'priconne.chara.Roster: 出现重名{n}于id{idx}与id{self._roster[n]}') self._all_name_list = self._roster.keys()
async def poll_videos_by_mid(bot, ev: CQEvent, sv: Service, spider: BiliVideoSpider, max_num=3): mid = util.normalize_str(ev.message.extract_plain_text()) if mid not in _video_cache: _video_cache[mid] = ItemsCache() await spider.get_update(mid, _video_cache[mid]) sv.logger.info(f'拉取了UP主{mid}的视频') videos = _video_cache[mid].item_cache[0:max_num] await bot.send(ev, get_format(mid, videos), at_sender=True)
async def set_time(bot, ev: CQEvent): if not priv.check_priv(ev, priv.ADMIN): await bot.finish(ev, '只有群管理才能更改推送时间', at_sender=True) time = util.normalize_str(ev.message.extract_plain_text()) try: datetime.datetime.strptime(time, '%H:%M') except ValueError: await bot.finish(ev, f'时间{time}格式不正确,示例:切换日程时间08:00', at_sender=True) gid = str(ev.group_id) _group_calendar[gid].update({"time": time, "enable":True}) dump_calendar_config() await bot.send(ev, f'日程表推送时间已更改为每日{time}', at_sender=True)
async def add_dit(bot, ev: CQEvent): msg = util.normalize_str(ev.message.extract_plain_text()) sender = ev.sender["nickname"] if ev.sender["card"] != "": sender = ev.sender["card"] time = datetime.now(tz).isoformat() ditter.dit_add(str(ev.group_id), sender, time, msg) try: await bot.send(ev, f'{ditter.dit_last(str(ev.group_id))["id"]}', at_sender=True) except Exception as e : sv.logger.error(f'群{ev.group_id}添加dit条目失败') sv.logger.exception(e) await bot.send(ev, '出错了亲~,请再试一次', at_sender=True)
async def poll_live_by_rid(bot, ev: CQEvent, sv: Service, spider: BiliLiveSpider): rid = util.normalize_str(ev.message.extract_plain_text()) if rid not in _live_cache: _live_cache[rid] = Item() await spider.get_update(rid, _live_cache[rid]) sv.logger.info(f'拉取了直播间{rid}消息') msg = "" if _live_cache[rid].live_status == 1: msg = get_format(rid, _live_cache[rid], True) else: msg = f'主播{_live_cache[rid].author}的直播间{rid}尚未开播' await bot.send(ev, msg, at_sender=True)
def parse_team(self, namestr): """@return: List[ids], unknown_namestr""" namestr = util.normalize_str(namestr) team = [] unknown = [] while namestr: item = self._roster.longest_prefix(namestr) if not item: unknown.append(namestr[0]) namestr = namestr[1:].lstrip() else: team.append(item.value) namestr = namestr[len(item.key):].lstrip() return team, ''.join(unknown)
def cb_cmd(name, parser:ArgParser) -> Callable: if isinstance(name, str): name = (name, ) if not isinstance(name, Iterable): raise ValueError('`name` of cb_cmd must be `str` or `Iterable[str]`') names = map(lambda x: util.normalize_str(x), name) def deco(func) -> Callable: for n in names: if n in _registry: sv.logger.warning(f'出现重名命令:{func.__name__} 与 {_registry[n].__name__}命令名冲突') else: _registry[n] = (func, parser) return func return deco
async def switch_subscribe(bot, ev: CQEvent, sub: bool): action_tip = "订阅" if sub else "退订" if not priv.check_priv(ev, priv.ADMIN): await bot.finish(ev, f'只有管理员才能进行{action_tip}', at_sender=True) mid = util.normalize_str(ev.message.extract_plain_text()) if not mid: await bot.finish(ev, f'请在指令后加上要{action_tip}UP主的UID', at_sender=True) else: ss.add_subscription(mid, ev.group_id) if sub else ss.del_subscription( mid, ev.group_id) sv.logger.info(f'群{ev.group_id}{action_tip}了UP主{mid}') await bot.send(ev, f'已为群{ev.group_id}{action_tip}了UP主{mid}', at_sender=True)
async def clanbattle_query_clanname(session): uid = session.ctx['user_id'] if not _lmt.check(uid): await session.send('查询得太快了哦,请稍等一会儿~', at_sender=True) return _lmt.start_cd(uid) clan_name = util.normalize_str(session.current_arg_text) url = "https://service-kjcbcnmw-1254119946.gz.apigw.tencentcs.com//name/0" name = json.dumps({"clanName": clan_name}) headers = { 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Custom-Source': 'PurinBot', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36', 'Content-Type': 'application/json', 'Sec-Fetch-Site': 'cross-site', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Dest': 'empty', 'Host': 'service-kjcbcnmw-1254119946.gz.apigw.tencentcs.com', 'Content-Length': '33', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Origin': 'https://kyouka.kengxxiao.com', 'Referer': 'https://kyouka.kengxxiao.com/' } r = requests.post(url, data=name, headers=headers, timeout=10) if r.status_code == requests.codes.ok: r_dec = json.loads(r.text) if not r_dec['data']: msg = f"您所查询的公会尚未参加公会战或排名超出统计范围OxO" await session.send(msg, at_sender=True) return else: full = r_dec['full'] enter = '=================' if full > 10: msg = [f'\n下次查询将在5分钟后可用~\n共为您查询到{full}条结果,最多显示10条,请缩小搜索范围哦:'] else: msg = [f'\n下次查询将在5分钟后可用~\n共为您查询到{full}条结果:'] for data in r_dec['data'][0:10]: msg.append( f'行会名:{data["clan_name"]}\n行会人数:{data["member_num"]}\n行会会长:{data["leader_name"]}\n当前排名:{data["rank"]}\n当前伤害:{data["damage"]}' ) _lmt.start_cd(uid, 300) await session.send(f'\n{enter}\n'.join(msg), at_sender=True) else: await session.send('查询出错,接口可能访问过大或维护,休息一会再试吧QwQ', at_sender=True) uid = session.ctx['user_id'] _lmt.start_cd(uid, 150)
async def wrapper(ctx): if self._check_all(ctx): plain_text = ctx['message'].extract_plain_text() if normalize: plain_text = util.normalize_str(plain_text) ctx['plain_text'] = plain_text for kw in keywords: if kw in plain_text: try: await func(self.bot, ctx) self.logger.info(f'Message {ctx["message_id"]} is handled by {func.__name__}, triggered by keyword.') except Exception as e: self.logger.exception(e) self.logger.error(f'{type(e)} occured when {func.__name__} handling message {ctx["message_id"]}.') return
async def wrapper(ctx): if self._check_all(ctx): plain_text = ctx['message'].extract_plain_text() plain_text = plain_text.strip() if normalize: plain_text = util.normalize_str(plain_text) ctx['plain_text'] = plain_text match = rex.search(plain_text) if match: try: await func(self.bot, ctx, match) self.logger.info(f'Message {ctx["message_id"]} is handled by {func.__name__}, triggered by rex.') except Exception as e: self.logger.exception(e) self.logger.error(f'{type(e)} occured when {func.__name__} handling message {ctx["message_id"]}.') return
async def modify_dit(bot, ev: CQEvent): args = util.normalize_str(ev.message.extract_plain_text()).strip() arg_list = args.split() if len(arg_list) != 2: await bot.finish(ev, "请输入要修改的记录ID和新的内容", at_sender=True) dit_id = arg_list[0] msg = arg_list[1] sender = ev.sender["nickname"] if ev.sender["card"] != "": sender = ev.sender["card"] time = datetime.now(tz).isoformat() ditter.dit_modify(str(ev.group_id), dit_id, sender, time, msg) try: await bot.send(ev, f'{dit_id}', at_sender=True) except Exception as e : sv.logger.error(f'群{ev.group_id}修改dit条目失败') sv.logger.exception(e) await bot.send(ev, '出错了亲~,请再试一次', at_sender=True)
async def set_pool(session): name = util.normalize_str(session.current_arg_text) if not name: session.finish(POOL_NAME_TIP, at_sender=True) elif name in ('b', 'b服', 'bl', 'bilibili', '国', '国服', 'cn'): name = 'BL' elif name in ('台', '台服', 'tw', 'sonet'): name = 'TW' elif name in ('日', '日服', 'jp', 'cy', 'cygames'): name = 'JP' elif name in ('混', '混合', 'mix'): name = 'MIX' else: session.finish(f'未知服务器地区 {POOL_NAME_TIP}', at_sender=True) gid = str(session.ctx['group_id']) _group_pool[gid] = name dump_pool_config() await session.send(f'卡池已切换为{name}池', at_sender=True) await gacha_info(session)