async def get_result(__url: str, paras: dict) -> dict: timeout_count = 0 while timeout_count < 3: try: timeout = aiohttp.ClientTimeout(total=10) async with aiohttp.ClientSession(timeout=timeout) as __session: headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36' } async with __session.get(url=__url, params=paras, headers=headers, timeout=timeout) as resp: json = await resp.json() return json except Exception as e: error_info = f'{repr(e)} Occurred in get_result trying {timeout_count + 1} using paras: {paras}' logger.info(error_info) finally: timeout_count += 1 else: error_info = f'Failed too many times in get_result using paras: {paras}' logger.warning(error_info) return {'header': {'status': 1}, 'results': []}
def get_tags(self, raw_post: RawPost) -> Optional[list[Tag]]: "Return Tag list of given RawPost" text = raw_post["mblog"]["text"] soup = bs(text, "html.parser") res = list( map( lambda x: x[1:-1], filter( lambda s: s[0] == "#" and s[-1] == "#", map(lambda x: x.text, soup.find_all("span", class_="surl-text")), ), ) ) super_topic_img = soup.find( "img", src=re.compile(r"timeline_card_small_super_default") ) if super_topic_img: try: res.append( super_topic_img.parent.parent.find("span", class_="surl-text").text + "超话" ) except: logger.info("super_topic extract error: {}".format(text)) return res
async def get_ascii2d_result(__url: str) -> str: timeout_count = 0 while timeout_count < 3: try: timeout = aiohttp.ClientTimeout(total=10) async with aiohttp.ClientSession(timeout=timeout) as __session: headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/83.0.4103.116 Safari/537.36', 'accept-language': 'zh-CN,zh;q=0.9' } async with __session.get(url=__url, headers=headers, timeout=timeout) as resp: res_headers = await resp.text() return res_headers except Exception as e: error_info = f'{repr(e)} Occurred in get_ascii2d_result trying {timeout_count + 1} using url: {__url}' logger.info(error_info) finally: timeout_count += 1 else: error_info = f'Failed too many times in get_result using url: {__url}' logger.warning(error_info) return ''
def batchSend(): global _IMAGE_LIST_CACHE _IMAGE_LIST_CACHE = None logger.debug("Begin to start daily greeting") groupsList = [i["group_id"] for i in callModuleAPI("get_group_list")] successSend = 0 for groupID in groupsList: enabled = PluginManager._getSettings(__plugin_name__, type="group", id=groupID).status if not enabled: continue try: callModuleAPI( "send_msg", params={ "group_id": groupID, "message": timeTelling() }, ) except Exception: eid = ExceptionProcess.catch() logger.exception( f"Failed to greeting in group {groupID},traceback id:{eid}") else: successSend += 1 logger.info( f"Daily greeting finished,total send:{len(groupsList)},success:{successSend}" )
async def fetch_new_post( self, _: Target, users: list[User]) -> list[tuple[User, list[Post]]]: try: config = Config() post_list = await self.get_sub_list() new_posts = await self.filter_common(post_list) res: list[tuple[User, list[Post]]] = [] if not new_posts: return [] else: for post in new_posts: logger.info('fetch new post from {}: {}'.format( self.platform_name, self.get_id(post))) for user in users: required_tags = config.get_sub_tags( self.platform_name, 'default', user.user_type, user.user) if self.enable_tag else [] cats = config.get_sub_category(self.platform_name, 'default', user.user_type, user.user) user_raw_post = await self.filter_user_custom( new_posts, cats, required_tags) user_post: list[Post] = [] for raw_post in user_raw_post: user_post.append(await self._parse_with_cache(raw_post)) res.append((user, user_post)) self.cache = {} return res except httpx.RequestError as err: logger.warning("network connection error: {}, url: {}".format( type(err), err.request.url)) return []
async def handle_check(bot: Bot, event: GroupMessageEvent, state: T_State): check_msg = state['check'] room_id = state['room_id'] if check_msg != '是': await bilibili_live.finish('操作已取消') sub_command = state['sub_command'] if sub_command == '订阅': _res = await sub_add(bot=bot, event=event, state=state) elif sub_command == '取消订阅': _res = await sub_del(bot=bot, event=event, state=state) elif sub_command == '清空订阅': _res = await sub_clear(bot=bot, event=event, state=state) else: _res = Result(error=True, info='Unknown error, except sub_command', result=-1) if _res.success(): logger.info( f"{sub_command}直播间成功, group_id: {event.group_id}, room_id: {room_id}" ) await bilibili_live.finish(f'{sub_command}成功!') else: logger.error( f"{sub_command}直播间失败, group_id: {event.group_id}, room_id: {room_id}," f"info: {_res.info}") await bilibili_live.finish(f'{sub_command}失败了QAQ, 可能并未订阅该用户, 或请稍后再试~')
async def handle_sub_command_args(bot: Bot, event: GroupMessageEvent, state: T_State): sub_command = state['sub_command'] if sub_command not in ['订阅', '取消订阅']: await pixivision.reject('没有这个命令哦, 请在【订阅/取消订阅】中选择并重新发送, 取消命令请发送【取消】:') if sub_command == '订阅': _res = await sub_add(bot=bot, event=event, state=state) elif sub_command == '取消订阅': _res = await sub_del(bot=bot, event=event, state=state) else: _res = Result(error=True, info='Unknown error, except sub_command', result=-1) if _res.success(): logger.info( f"{sub_command}Pixivision成功, group_id: {event.group_id}, {_res.info}" ) await pixivision.finish(f'{sub_command}成功!') else: logger.error( f"{sub_command}Pixivision失败, group_id: {event.group_id}, {_res.info}" ) await pixivision.finish( f'{sub_command}失败了QAQ, 可能并未订阅Pixivision, 或请稍后再试~')
async def get_image(pic_url: str): timeout_count = 0 while timeout_count < 3: try: timeout = aiohttp.ClientTimeout(total=10) async with aiohttp.ClientSession(timeout=timeout) as session: headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36' } async with session.get(url=pic_url, headers=headers, timeout=timeout) as resp: _res = await resp.read() return _res except Exception as _e: error_info = f'{repr(_e)} Occurred in get_image trying {timeout_count + 1} using paras: {pic_url}' logger.info(error_info) finally: timeout_count += 1 else: error_info = f'Failed too many times in get_image using paras: {pic_url}' logger.warning(error_info) return None
async def filter_common(self, target: Target, raw_post_list: list[RawPost]) -> list[RawPost]: if not self.inited.get(target, False): # target not init for raw_post in raw_post_list: post_id = self.get_id(raw_post) self.exists_posts[target].add(post_id) logger.info('init {}-{} with {}'.format(self.platform_name, target, self.exists_posts[target])) self.inited[target] = True return [] res: list[RawPost] = [] for raw_post in raw_post_list: post_id = self.get_id(raw_post) if post_id in self.exists_posts[target]: continue if (post_time := self.get_date(raw_post) ) and time.time() - post_time > 2 * 60 * 60: continue try: if not self.filter_platform_custom(raw_post): continue except NotImplementedError: pass try: self.get_category(raw_post) except CategoryNotSupport: continue except NotImplementedError: pass res.append(raw_post) self.exists_posts[target].add(post_id)
async def push_news(self): """ 推送消息 """ # 没有启用的群则不推送消息 if not plugin_config.push_news_group_id: return logger.info('开始检查 公主连结Re:Dive 动态') news_list = [] news = await self.get_news() if news is None: logger.error('公主连结Re:Dive 动态获取失败') return if not plugin_config.push_news_last_news_id: # 如果初次运行,则记录并发送第一条动态 plugin_config.push_news_last_news_id = self.get_news_id(news[0]) news_list.append(news[0]) for item in news: if self.get_news_id(item) <= plugin_config.push_news_last_news_id: break news_list.append(item) if news_list: # 参考 [Yobot](https://github.com/pcrbot/yobot/blob/master/src/client/ybplugins/push_news.py) 的格式 msg = '公主连结Re:Dive B站动态更新:\n=======\n' + '\n-------\n'.join( map(self.format_message, news_list)) for group_id in plugin_config.push_news_group_id: await get_first_bot().send_msg(message_type='group', group_id=group_id, message=msg) # 添加最新的那一条动态的 ID plugin_config.push_news_last_news_id = self.get_news_id( news_list[0])
async def push_news(self): """ 推送消息 """ logger.info('开始检查 最终幻想XIV 新闻') news_list = [] news = await self.get_news() if news is None: logger.error('最终幻想XIV 新闻获取失败') return if not self.last_news_id: # 如果初次运行,则记录并发送第一条新闻 self.last_news_id = news['Data'][0]['Id'] news_list.append(news['Data'][0]) for item in news['Data']: if item['Id'] <= self.last_news_id: break news_list.append(item) if news_list: # 添加最新的那一条新闻的 ID self.last_news_id = news_list[0]['Id'] group_id = get_bot().config.GROUP_ID[0] for item in news_list: await get_bot().send_msg(message_type='group', group_id=group_id, message=self.format_message(item))
async def handle_admin_mail_bind(bot: Bot, event: GroupMessageEvent, state: T_State): mailbox_list = state['mailbox_list'] email_address = state['email_address'] if email_address not in mailbox_list: logger.warning( f'Group:{event.group_id}/User:{event.user_id} 绑定邮箱: {email_address} 失败, 不在可绑定邮箱中的邮箱' ) await admin_mail_bind.finish('该邮箱不在可绑定邮箱中!') group_id = event.group_id res = DBGroup(group_id=group_id).mailbox_add(mailbox=DBEmailBox( address=email_address)) if res.success(): logger.info( f'Group:{event.group_id}/User:{event.user_id} 绑定邮箱: {email_address} 成功' ) await admin_mail_bind.finish('Success! 绑定成功') else: logger.error( f'Group:{event.group_id}/User:{event.user_id} 绑定邮箱: {email_address} 失败, error: {res.info}' ) await admin_mail_bind.finish('绑定邮箱失败QAQ, 请检联系管理员处理')
async def handle_admin_mail_add(bot: Bot, event: MessageEvent, state: T_State): address = state['address'] server_host = state['server_host'] password = state['password'] check_result = await check_mailbox(address=address, server_host=server_host, password=password) if not check_result.success(): logger.warning( f'{event.user_id} 添加邮箱: {address} 失败, 邮箱验证不通过, error: {check_result.info}' ) await admin_mail_add.finish('验证邮箱失败了QAQ, 请检查邮箱信息或稍后再试') # 对密码加密保存 password = encrypt_password(plaintext=password) add_result = DBEmailBox(address=address).add(server_host=server_host, password=password) if add_result.success(): logger.info(f'{event.user_id} 添加邮箱: {address} 成功') await admin_mail_add.finish('Success! 邮箱添加成功') else: logger.error( f'{event.user_id} 添加邮箱: {address} 失败, 数据库写入失败, error: {add_result.info}' ) await admin_mail_add.finish('邮箱添加失败QAQ, 请检联系管理员处理')
async def push_news(self) -> None: """ 推送消息 """ # 没有启用的群则不推送消息 if not plugin_config.push_news_group_id: return logger.info('开始检查 最终幻想XIV 新闻') news_list = [] news = await self.get_news() if news is None: logger.error('最终幻想XIV 新闻获取失败') return if not plugin_config.push_news_last_news_id: # 如果初次运行,则记录并发送第一条新闻 plugin_config.push_news_last_news_id = news['Data'][0]['Id'] news_list.append(news['Data'][0]) for item in news['Data']: if item['Id'] <= plugin_config.push_news_last_news_id: break news_list.append(item) if news_list: # 参考 [Yobot](https://github.com/pcrbot/yobot/blob/master/src/client/ybplugins/push_news.py) 的格式 msg = '最终幻想XIV官网更新:\n=======\n' + '\n-------\n'.join( map(self.format_message, news_list) ) for group_id in plugin_config.push_news_group_id: await get_first_bot().send_msg( message_type='group', group_id=group_id, message=msg ) # 添加最新的那一条新闻的 ID plugin_config.push_news_last_news_id = news_list[0]['Id']
async def refresh_calender(self) -> None: """ 获取最新的日程表 """ if self._timeline: # 最近四小时内才更新的不用再次更新 if self._timeline_update_time > datetime.now() - timedelta( hours=4): return None try: async with httpx.AsyncClient() as client: r = await client.get(self._url) if r.status_code != 200: # 如果 HTTP 响应状态码不是 200,说明调用失败 return None # 清楚以前的时间线 self._timeline.clear() for event in r.json(): start_time = datetime.strptime(event['start_time'], '%Y/%m/%d %H:%M:%S') end_time = datetime.strptime(event['end_time'], '%Y/%m/%d %H:%M:%S') name = event['name'] self.add_event(start_time, end_time, name) self._timeline_update_time = datetime.now() logger.info('公主连结Re:Dive 日程表刷新成功') except (httpx.HTTPError, KeyError) as e: logger.error(f'获取日程表出错,{e}') # 抛出上面任何异常,说明调用失败 return None
def is_repeat(session: CommandSession, message: str): """ 是否复读这个消息 """ group_id = session.event.group_id user_id = session.event.user_id # 只复读指定群内消息 if group_id not in session.bot.config.GROUP_ID: return False # 不要复读指令 match = re.match(r'^\/', message) if match: return False # 记录群内发送消息数量和时间 now = datetime.now() recorder.add_msg_send_time(now, group_id) # 如果不是PRO版本则不复读纯图片 match = re.search(r'\[CQ:image[^\]]+\]$', message) if match and not session.bot.config.IS_COOLQ_PRO: return False # 不要复读应用消息 if user_id == 1000000: return False # 不要复读签到,分享 match = re.match(r'^\[CQ:(sign|share).+\]', message) if match: return False # 复读之后1分钟之内不再复读 time = recorder.last_message_on(group_id) if now < time + timedelta(minutes=REPEAT_INTERVAL): return False repeat_rate = REPEAT_RATE # 当10分钟内发送消息数量大于30条时,降低复读概率 # 因为排行榜需要固定概率来展示欧非,暂时取消 # if recorder.message_number(10) > 30: # logger.info('Repeat rate changed!') # repeat_rate = 5 # 记录每个人发送消息数量 recorder.add_msg_number_list(user_id, group_id) # 按照设定概率复读 random = secrets.SystemRandom() rand = random.randint(1, 100) logger.info(f'repeat: {rand}') if rand > repeat_rate: return False # 记录复读时间 recorder.reset_last_message_on(group_id) # 记录复读次数 recorder.add_repeat_list(user_id, group_id) return True
async def handle_sub_command(bot: Bot, event: GroupMessageEvent, state: T_State): # 子命令列表 command = { 'init': group_init, 'upgrade': group_upgrade, 'notice': set_group_notice, 'command': set_group_command, 'setlevel': set_group_level, 'showpermission': show_group_permission, 'resetpermission': reset_group_permission } # 需要回复信息的命令列表 need_reply = [ 'showpermission' ] sub_command = state["sub_command"] # 在这里对参数进行验证 if sub_command not in command.keys(): await omega.finish('没有这个命令哦QAQ') result = await command[sub_command](bot=bot, event=event, state=state) if result.success(): logger.info(f"Group: {event.group_id}, {sub_command}, Success, {result.info}") if sub_command in need_reply: await omega.finish(result.result) else: await omega.finish('Success') else: logger.error(f"Group: {event.group_id}, {sub_command}, Failed, {result.info}") await omega.finish('Failed QAQ')
async def get_ascii2d_redirects(_url: str) -> dict: timeout_count = 0 while timeout_count < 3: try: timeout = aiohttp.ClientTimeout(total=10) async with aiohttp.ClientSession(timeout=timeout) as __session: headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36', 'accept-language': 'zh-CN,zh;q=0.9' } async with __session.get(url=_url, headers=headers, timeout=timeout, allow_redirects=False) as resp: res_headers = resp.headers res_dict = {'error': False, 'body': dict(res_headers)} return res_dict except Exception as e: error_info = f'{repr(e)} Occurred in get_ascii2d_redirects trying {timeout_count + 1} using url: {_url}' logger.info(error_info) finally: timeout_count += 1 else: error_info = f'Failed too many times in get_result using url: {_url}' logger.warning(error_info) return {'error': True, 'body': None}
async def handle_sub_command(bot: Bot, event: GroupMessageEvent, state: T_State): # 子命令列表 command = { '清单': skill_list, '我会的': user_skill_list, '设置': user_skill_set, '删除': user_skill_del, '清空': user_skill_clear } # 需要回复信息的命令列表 need_reply = [ '清单', '我会的', '设置' ] sub_command = state["sub_command"] # 在这里对参数进行验证 if sub_command not in command.keys(): await skill_group_user.finish('没有这个命令哦QAQ') result = await command[sub_command](bot=bot, event=event, state=state) if result.success(): logger.info(f"Group: {event.group_id}, {sub_command}, Success, {result.info}") if sub_command in need_reply: await skill_group_user.finish(result.result) else: await skill_group_user.finish('Success') else: logger.error(f"Group: {event.group_id}, {sub_command}, Failed, {result.info}") await skill_group_user.finish('Failed QAQ')
async def update_seeker(): ''' 轮询官方四格漫画更新 若有更新则推送至订阅群 ''' index_api = 'https://comic.priconne-redive.jp/api/index' index = load_index() # 获取最新漫画信息 resp = await aiorequests.get(index_api, timeout=10) data = await resp.json() id_ = data['latest_cartoon']['id'] episode = data['latest_cartoon']['episode_num'] title = data['latest_cartoon']['title'] # 检查是否已在目录中 # 同一episode可能会被更新为另一张图片(官方修正),此时episode不变而id改变 # 所以需要两步判断 if episode in index: qs = urlparse(index[episode]['link']).query old_id = parse_qs(qs)['id'][0] if id_ == old_id: logger.info('未检测到官漫更新') return # 确定已有更新,下载图片 logger.info(f'发现更新 id={id_}') await download_comic(id_) # 推送至各个订阅群 pic = R.img('priconne/comic', get_pic_name(episode)).cqcode msg = f'プリンセスコネクト!Re:Dive公式4コマ更新!\n第{episode}話 {title}\n{pic}' await sv.broadcast(msg, 'PCR官方四格', 0.5)
async def cache_data(self) -> None: """ 缓存数据 """ jobs = await get_jobs_info() for boss in plugin_config.fflogs_cache_boss: for job in jobs: await self.dps(boss, job.name) logger.info(f'{boss} {job.name}的数据缓存完成。') await asyncio.sleep(30)
async def usage(self): # 获取设置了名称的插件列表 plugins = list(filter(lambda p: p.name, nonebot.get_loaded_plugins())) arg = self.stripped_msg.lower() plugin = next((p for p in plugins if p.name == arg), False) logger.info(plugin) await self.send( self.render_image('usage', plugins=plugins, plugin=plugin))
async def check_status(event: aiocqhttp.Event): """ 启动时发送问好信息 """ hello_str = get_message() for group_id in get_bot().config.GROUP_ID: await get_bot().send_msg(message_type='group', group_id=group_id, message=hello_str) logger.info('发送首次启动的问好信息')
async def _(bot: Bot, event: Event, state: dict): """ 启动时发送问好信息 """ hello_str = get_first_connect_message() for group_id in plugin_config.group_id: await bot.send_msg(message_type='group', group_id=group_id, message=hello_str) logger.info('发送首次启动的问好信息')
async def morning(): """ 早安 """ hello_str = await get_message() for group_id in get_bot().config.GROUP_ID: await get_bot().send_msg(message_type='group', group_id=group_id, message=hello_str) logger.info('发送早安信息')
async def clear_data(): """ 每个月最后一天 24 点(下月 0 点)保存记录于历史记录文件夹,并重置记录 """ # 保存数据到历史文件夹 date = datetime.now() - timedelta(hours=1) DATA.save_pkl(recorder.get_data(), get_history_pkl_name(date)) # 清除现有数据 recorder.init_data() logger.info('记录清除完成')
def init(self): """ 初始化日程自动推送 """ logger.info('初始化 公主连结Re:Dive 日程推送') self._job = scheduler.add_job(self.push_calender, 'cron', hour=plugin_config.calender_hour, minute=plugin_config.calender_minute, second=plugin_config.calender_second, id='push_calender')
def init(self): """ 初始化动态自动推送 """ logger.info('初始化 公主连结Re:Dive 动态推送') # 启动之后先运行一次 scheduler.add_job(self.push_news, 'date', run_date=(datetime.now() + timedelta(seconds=30))) self._job = scheduler.add_job(self.push_news, 'interval', minutes=plugin_config.push_news_interval)
async def _start_scheduler(): if scheduler and not scheduler.running: scheduler.configure(_bot.config.APSCHEDULER_CONFIG) scheduler.start() logger.info('Scheduler started') # def set_config(): # dirname, _ = path.split(path.abspath(__file__)) # copyfile(path.join(dirname, "simple_config.py"), path.join(getcwd(), "config.py"))
async def add_superuser(bot: Bot, event: RequestEvent, state: T_State): if str(event.user_id ) in bot.config.superusers and event.request_type == 'private': await event.approve(bot) logger.info('add user {}'.format(event.user_id)) elif event.sub_type == 'invite' and str( event.user_id ) in bot.config.superusers and event.request_type == 'group': await event.approve(bot) logger.info('add group {}'.format(event.group_id))