async def batch_get_prob(bot: Bot, event: MessageEvent, state: T_State): if str(event.message) in CANCEL_EXPRESSION: await learn.finish('已退出当前对话') if not event.raw_message.isdigit(): state['wrong_times'] += 1 if state['wrong_times'] < 3: await batch_learn.reject( prompt='请输入数字参数作为相对出现率,范围[0-100],请重新输入,发送[取消]结束当前学习对话') else: await batch_learn.finish('看来你是不想好好说了,就这样吧,挂了') prob = int(event.raw_message) if prob < 0 or prob > 100: state['wrong_times'] += 1 if state['wrong_times'] < 3: await batch_learn.reject( prompt='输入范围应该是[0-100],请重新输入,发送[取消]结束当前学习对话') else: await batch_learn.send('不要闹了,我先帮你设置成50了,要该的话之后用[设置出现率]来改吧') asyncio.sleep(1) # 停一秒防止设置成功的消息和此条消息顺序出错 public = 0 if state["force_priv"] else state["public"] source = event.group_id if event.message_type == "group" and 'selflearn' not in state else 0 creator = event.user_id if 'selflearn' not in state else event.self_id result = insertmany(state["question"].split('|'), state["answer"].split('|'), prob, creator, source, public) if isinstance(result, int): dlmt = DailyNumberLimiter(uid=event.user_id, func_name='学习', max_num=6) if dlmt.check(close_conn=False): if dlmt.count <= 3: exp_mu, fund_mu = 5, 10 else: exp_mu, fund_mu = 2, 3 exp = cgauss(exp_mu, 1, 1) + result - 1 fund = cgauss(fund_mu, 1, 1) + result - 1 user = UserLevel(event.user_id) await user.expup(exp, bot, event) user.turnover(fund) msg = f'已记录{result}条对话,赠送您{exp}exp 和 {fund}金币作为谢礼~' dlmt.increase() else: dlmt.conn.close() msg = f'已记录{result}条对话' if state["force_priv"]: msg += "\n(消息中含at信息,将强制设置公开性为群内限定)" else: repeat_ls = [ f"问句:{emojize(q)} 回答:{a} 被{creator}在{creation_time}创建" for q, a, creator, creation_time in result ] msg = f'以下对话已存在:\n' + '\n'.join(repeat_ls) + '\n请去除重复对话后重新学习' await batch_learn.finish(msg)
async def get_a(bot: Bot, event: MessageEvent, state: T_State): question = state["question"] if "answer" in state: answer = state["answer"] else: answer = Message(event.raw_message) # 不可以at自己 for seg in answer: if seg.type == 'at' and seg.data["qq"] == str(event.self_id): await learn.finish('我为什么要at我自己?不要这样啦,会有bug的::>_<::') answer = await msg2str(answer, localize_=True, bot=bot) if len(question) > 3000 or len(answer) > 3000: await learn.finish(f'内容太长的对话{BOTNAME}记不住的说>﹏<') if answer: logger.debug(f'Current answer is [{answer}]') source = event.group_id if event.message_type == "group" and 'selflearn' not in state else 0 public = 0 if state["force_priv"] else state["public"] creator = event.user_id if 'selflearn' not in state else event.self_id result = insertone(question, answer, 70, creator, source, public) if isinstance(result, tuple): await learn.finish(f'记录已被用户{result[0]}在{result[1]}时创建') else: logger.info( f'Insert record to corpus :\nquestion:[{question}]\nanswer:[{answer}]\npublic:{public}\ncreator:{creator}\nsource:{source}' ) dlmt = DailyNumberLimiter(uid=event.user_id, func_name='学习', max_num=6) if dlmt.check(close_conn=False): if dlmt.count <= 3: exp_mu, fund_mu = 5, 10 else: exp_mu, fund_mu = 2, 3 exp = cgauss(exp_mu, 1, 1) fund = cgauss(fund_mu, 1, 1) user = UserLevel(event.user_id) await user.expup(exp, bot, event) user.turnover(fund) msg = f'对话已记录, 赠送您{exp}exp 和 {fund}金币作为谢礼~' dlmt.increase() else: dlmt.conn.close() msg = '对话已记录' if state["force_priv"]: msg += "\n(消息中含at信息,将强制设置公开性为群内限定)" msg += "\n﹟ 当前对话相对出现率默认设置为70,如需设置出现率可直接输入0-100范围内数字,否则可忽视本条说明" preprob[event.user_id] = result await learn.finish(msg) else: await learn.finish(reply_header(event, '这条词语好像记不住耶,要不联系主人试试?'))
async def sign_(bot: Bot, event: MessageEvent): uid = event.user_id user = UserLevel(uid) # 是否可以签到 today = date.today() last_sign_day = user.last_sign.date() if today > last_sign_day: with QbotDB() as botdb: botdb.update( 'update userinfo set `last_sign`=NOW(), total_sign=total_sign+1 where qq_number=%s;', (uid, )) gndexp = cgauss(8, 2, 0) gndfund = cgauss(25, 3, 0) await user.expup(gndexp, bot, event) user.turnover(gndfund) await sign.send(reply_header(event, f'感谢签到,经验值+{gndexp},资金+{gndfund}!')) else: await sign.finish(reply_header(event, '今天你已经签到过了哦~'))
async def check_fortune(bot: Bot, event: MessageEvent): name = event.sender.card if event.message_type == 'group' else event.sender.nickname dlmt = DailyNumberLimiter(event.user_id, func_name='运势', max_num=1) stick = query_fortune(event.user_id) rp = 0 # 如果抽中了特殊的签要发生一些事件 TODO:加入一些今天功能冷却减少之类的什么奖励吧 if dlmt.check(close_conn=False) or not stick: stick = choice(sticks) if dlmt.count == 0: if stick.name == '28.jpg': # 恋爱运,惩罚 rp = 1 elif stick.name == '27.jpg': # 凶,奖励 rp = 2 elif stick.name in ('8.jpg', '16.jpg', '19.jpg'): # 金运,给钱 rp = 3 elif stick.name in ('15.jpg', '23.jpg'): # 全体运,群内统统滴奖励,私聊无效 rp = 4 elif stick.name == '17.jpg': # 关系运, 私聊的时候给予奖励吧 rp = 5 draw_fortune(event.user_id, stick.name) dlmt.increase() else: dlmt.conn.close() stick = assets_folder / stick logger.debug(f'{event.user_id} got stick {stick.name}') await fortune.send(f'{name}今日的运势是' + imgseg(stick), at_sender=True) if rp: await asleep(1.5) if event.message_type == 'group': name = event.sender.card or event.sender.nickname or event.get_user_id( ) else: name = event.sender.nickname or event.get_user_id() if rp == 1 and is_user(event.user_id): exp = randint(9, 15) fund = randint(20, 30) user = UserLevel(event.user_id) await user.expup(-exp, bot, event) user.turnover(-fund, check_overdraft=False) await fortune.finish( f'虽然你抽中了恋爱运也不一定会遇到恋爱事件,不过对和恋爱沾边的家伙给予惩罚才是正义的\n{name}的exp -{exp}, 资金 -{fund}', at_sender=True) elif rp == 2 and is_user(event.user_id): exp = randint(15, 25) fund = randint(30, 50) user = UserLevel(event.user_id) await user.expup(exp, bot, event) user.turnover(fund) await fortune.finish( 'Emmmm,因为占卜的师傅如果不是有别的什么目的的话,为了讨好前来占卜的人都会尽量说是吉签嘛\n 抽到这个凶签这种小概率事件,某种意义上这才是奇迹真正应该有的样子' f'\n{name}获得了{exp}exp和{fund}资金', at_sender=True) elif rp == 3 and is_user(event.user_id): fund = randint(20, 30) user = UserLevel(event.user_id) user.turnover(fund) await fortune.finish( f'因为{BOTNAME}给不了你软妹币所以也只好送些这个给你了~不过说不定哪天它会像比特币一样突然价值激增哦~\n{name}获得了{fund}资金', at_sender=True) elif rp == 4: if event.message_type == 'group': await fortune.send('大家会感谢你的,无私的散财者~骗你的( •̀ ω •́ )✧', at_sender=True) memberlist = await bot.get_group_member_list( group_id=event.group_id) uids = filter_users(*[m['user_id'] for m in memberlist]) if len(uids) > 5: uids = get_active_user(*uids) logger.debug(f'获得奖励的群员{uids}') if uids: for uid in uids: exp = randint(5, 8) fund = randint(5, 8) user = UserLevel(uid) await user.expup(exp, bot, event=None, gid=event.group_id) try: member = await bot.get_group_member_info( group_id=event.group_id, user_id=uid) except ActionFailed as e: logger.warning( f'可能是已经退群的群员: group: {event.group_id} qq: {uid}, error: {e}' ) await fortune.send( group_id=event.group_id, message= f'本应该在群内的成员({uid})貌似获取不到了,是不是退群了呢?没有的话请联系维护组查看一下出问题的原因哦~' ) continue name = member['card'] or member['nickname'] or str(uid) await fortune.send(message=MessageSegment.text( f'{name}获得了{exp}exp和{fund}资金') + MessageSegment.at(qq=uid)) await asleep(1.5) else: await fortune.finish('啊嘞,这个群里好像还没有能获得奖励的伙伴呢~') else: await fortune.finish('可惜这里是私聊所以全体运的效果生效不了了呢~') elif rp == 5 and is_user(event.user_id): if event.message_type == 'group': await fortune.finish('啊~获得了可以增加亲密度的签,但是这个签只有私聊抽中的时候才有效哦~残念', at_sender=True) else: exp = randint(25, 40) fund = randint(40, 50) user = UserLevel(event.user_id) await user.expup(exp, bot, event) user.turnover(fund) await fortune.finish( f'真的为了什么才和{BOTNAME}进行私密对话的吗?送你{exp}exp和{fund}资金吧,除了这些{BOTNAME}也没有其它的了' )
async def send_lolicon(bot: Bot, event: MessageEvent, state: T_State): gid = event.group_id if event.message_type=='group' else 0 if gid: if str(gid) not in sl_settings: await setu.finish('''先设置本群sl再使用此功能吧 [设置sl 最小sl-最大sl] 例如:设置sl 0-4 ──────────── sl说明: 大概可以解释成本群能接收的工口程度,sl越高的图被人看见越会触发社死事件 ※ r18权限已改为sl, 当最大sl为5时即为开启r18权限,sl0-4级别仅适用于美图,色图会自动忽略 最低sl0:不含任何ero要素,纯陶冶情操,也有一部分风景图 最高sl5: 就是R18了 中间的等级依次过渡''') max_sl = sl_settings[str(gid)]['max_sl'] min_sl = sl_settings[str(gid)]['min_sl'] restricted = True if max_sl < 5 else False # r18是否在本群受限 else: restricted = False # 限制条件优先度:r18,5张最大数,等级限制数量,频率,资金,由于要检测参数只好先把个别参数解析混入条款中了 uid = event.user_id # r18限制条款,顺便解析了r18 r18_call = state["_matched_dict"]['r18_call'] or state["_matched_dict"]['r18_call2'] if r18_call and restricted: await setu.finish(reply_header(event, f'当前群内最大sl为{max_sl},已禁止R18内容')) # 5张最大数量限制条款,顺便解析了num if state["_matched_dict"]['num']: num = cn2an(state["_matched_dict"]['num'].replace('两', '二'), 'smart') elif state["_matched_dict"]['num2']: num = cn2an(state["_matched_dict"]['num2'].replace('两', '二'), 'smart') else: num = 1 if num > 5: await setu.finish(reply_header(event, '一次最多只能要5张')) elif num == 0: await setu.finish(reply_header(event, '你好奇怪的要求')) elif num < 0: await setu.finish(reply_header(event, f'好的,你现在欠大家{-num}张涩图,快发吧')) # TODO: 想想办法把负数给提取出来 # 等级限制数量条款,注册了用户信息 userinfo = UserLevel(uid) if userinfo.level < num: if userinfo.level > 0: await setu.finish(f'您当前等级为{userinfo.level},最多一次要{userinfo.level}张') elif num > 1: await setu.finish(reply_header(event, '啊这..0级用户一次只能叫一张哦,使用[签到]或者学习对话可以提升等级~')) # 频率限制条款,注册了频率限制器 flmt = FreqLimiter(uid, 'setu') if not flmt.check(): refuse = f'你冲得太快了,请{ceil(flmt.left_time())}秒后再冲' # 不用round主要是防止出现'还有0秒'的不科学情况 if userinfo.level == 0: refuse += ',提升等级可以加快装填哦~' await setu.finish(reply_header(event, refuse)) cd = cd_step(userinfo.level, 480) # 冷却时间 flmt.start_cd(cd) # 先进行冷却,防止连续呼叫击穿频率装甲,要是没返回图的话解掉 # 资金限制条款,注册了每日次数限制器 cost = num * 3 dlmt = DailyNumberLimiter(uid, '色图', 3) in_free = True if event.message_type == 'private' and event.sub_type == 'friend'\ else dlmt.check(close_conn=False) # 来自好友的对话不消耗金币 if userinfo.fund < cost and not in_free: if userinfo.fund > 0: refuse = choice((f'你还剩{userinfo.fund}块钱啦,要饭也不至于这么穷吧!', f'你只剩{userinfo.fund}块钱了,要不考虑援交一下赚点钱?')) elif userinfo.level == 0 and userinfo.fund == 0: refuse = '每天有三次免费次数哦,使用[签到]领取资金来获得更多使用次数吧~' else: refuse = '你已经穷得裤子都穿不起了,到底是做了什么呀?!' dlmt.conn.close() # 确认直接结束不会增加调用次数了,直接返还链接 flmt.start_cd(0) await setu.finish(reply_header(event, refuse)) kwd = state["_matched_dict"]['kwd'] or '' if r18_call: r18 = 1 if r18_call in ('r18', 'R18') else 0 else: if event.message_type == 'group': if max_sl < 5: r18 = 0 elif min_sl == 5: r18 = 1 elif min_sl < 5: r18 = 2 else: r18 = 2 msg = MessageSegment.reply(id_=event.message_id) if event.message_type == 'group' else MessageSegment.text('') # 由于当前私聊回复有bug所以只在群里设置信息开始为回复消息 # 有搜索条件,从本地库中提 if kwd: kwds = tuple(kwdrex.split(kwd)) success, result = get_setu(gid, kwds, num, r18) if not success: flmt.start_cd(0) dlmt.conn.close() await setu.finish(reply_header(event, result)) count = result['count'] # 返回数量,每次处理过后自减1 miss_count = 0 # 丢失数量 for data in result['data']: if data is None: miss_count += 1 continue img : Path = data['file'] logger.debug(f'当前处理本地图片{img.name}') info = f"{data['title']}\n画师:{data['author']}\nPID:{data['source']}\n" try: im_b64 = Image_Handler(img).save2b64() except OSError as err: miss_count += 1 logger.error(f'File {img} may be damaged: {err}') continue except UnidentifiedImageError as imgerr: miss_count += 1 logger.error(f'failed to open local file: {img}: {imgerr}') continue msg += MessageSegment.text(info) + MessageSegment.image(im_b64) if count > 1: msg += MessageSegment.text('\n=====================\n') count -= 1 elif result['count'] < num: msg += MessageSegment.text(f'\n=====================\n没搜到{num}张,只搜到这些了') # 无搜索条件,链接API,5次错误退出并反馈错误 else: logger.debug('Start getting lolicon API') failed_time = 0 while failed_time < 5: try: result = await get_lolicon(kwd, r18, num) break except BaseException as e: failed_time += 1 logger.exception(f"connect api faild {failed_time} time(s)\n{e}") else: logger.error(f'多次链接API失败,当前参数: kwd: [{kwd}], num: {num}, r18: {r18}') dlmt.conn.close() flmt.start_cd(0) await setu.finish('链接API失败, 若多次失败请反馈给维护组', at_sender=True) logger.debug('Receive lolicon API data!') # 处理数据 if result['code'] == 0: count = result['count'] # 返回数量,每次处理过后自减1 untreated_ls = [] # 未处理数据列表,遇到本地库中没有的数据要加入这个列表做并发下载 miss_count = 0 # 丢失数量 for data in result['data']: pid = data['pid'] p = data['p'] name = f'{pid}_p{p}' # 按 色图备份路径->美图原文件路径 顺序查找本地图,遇到没有本地路径的等待并发下载处理 imgbkup = [f for f in Path(SETUPATH).glob(f'{name}.[jp][pn]*g')] if imgbkup: img = imgbkup[0] else: imgorg = [f for f in (Path(MEITUPATH)/'origin_info').rglob(f'{name}.[jp][pn]*g')] if imgorg: img = imgorg[0] else: untreated_ls.append(data) continue logger.debug(f'当前处理本地图片{name}') info = f"{data['title']}\n画师:{data['author']}\nPID:{name}\n" try: im_b64 = Image_Handler(img).save2b64() except UnidentifiedImageError as imgerr: miss_count += 1 logger.error(f'failed to open local file: {img}: {imgerr}') continue msg += MessageSegment.text(info) + MessageSegment.image(im_b64) if count > 1: msg += MessageSegment.text('\n=====================\n') count -= 1 elif result['count'] < num: msg += MessageSegment.text(f'\n=====================\n没搜到{num}张,只搜到这些了') # 对未处理过的数据进行并发下载,只下载1200做临时使用 async with httpx.AsyncClient() as client: task_ls = [] for imgurl in [d['url'] for d in untreated_ls]: task_ls.append(client.get(get_1200(imgurl), timeout=120)) imgs = await gather(*task_ls, return_exceptions=True) for i, data in enumerate(untreated_ls): if isinstance(imgs[i], BaseException): miss_count += 1 logger.exception(data) continue if imgs[i].status_code != httpx.codes.OK: miss_count += 1 logger.error(f'Got unsuccessful status_code [{imgs[i].status_code}] when visit url: {imgs[i].url}') continue pid = data['pid'] p = data['p'] name = f'{pid}_p{p}' info = f"{data['title']}\n画师:{data['author']}\nPID:{name}\n" logger.debug(f'当前处理网络图片{name}') try: im_b64 = Image_Handler(imgs[i].content).save2b64() except BaseException as err: logger.error(f"Error with handle {name}, url: [{data['url']}]\n{err}") miss_count += 1 continue msg += MessageSegment.text(info) + MessageSegment.image(im_b64) if count > 1: msg += MessageSegment.text('\n=====================\n') count -= 1 elif result['count'] < num: msg += MessageSegment.text(f'\n=====================\n没搜到{num}张,只搜到这些了') if miss_count > 0 and num > 1: msg += MessageSegment.text(f'\n有{miss_count}张图丢掉了,{BOTNAME}也不知道丢到哪里去了T_T') elif miss_count == 1: msg += MessageSegment.text(f'{BOTNAME}拿来了图片但是弄丢了呜呜T_T') else: flmt.start_cd(0) dlmt.conn.close() await setu.finish(msg + MessageSegment.text('获取涩图失败,请稍后再试')) try: await setu.send(msg) except NetworkError as err: logger.error(f'Maybe callout error happend: {err}') except AdapterException as err: logger.error(f"Some Unkown error: {err}") if miss_count < result['count']: if not in_free: cost = (result['count'] - miss_count) * 3 # 返回数量可能少于调用量,并且要减去miss的数量 userinfo.turnover(-cost) # 如果超过每天三次的免费次数则扣除相应资金 dlmt.increase() # 调用量加一 else: flmt.start_cd(0) # 一张没得到也刷新CD dlmt.conn.close() # 下载原始图片做本地备份 if not kwd: async with httpx.AsyncClient() as bakeuper: backup_ls = [] json_ls = [] for info in untreated_ls: url = info['url'] json_data = { 'pid': info['pid'], 'p': info['p'], 'uid': info['uid'], 'title': info['title'], 'author': info['author'], 'url': url, 'r18': info['r18'], 'tags': info['tags'] } backup_ls.append(bakeuper.get(url, timeout=500)) json_ls.append(json_data) origims = await gather(*backup_ls, return_exceptions=True) for i, im in enumerate(origims): if isinstance(im, BaseException): logger.exception(im) continue if im.status_code != httpx.codes.OK: logger.error(f'Got unsuccessful status_code [{im.status_code}] when visit url: {im.url}') continue imgfp = Path(SETUPATH)/(str(json_ls[i]['pid']) + '_p' + str(json_ls[i]['p']) + '.' + json_ls[i]['url'].split('.')[-1]) jsonfp = Path(SETUPATH)/(str(json_ls[i]['pid']) + '_p' + str(json_ls[i]['p']) + '.json') try: with imgfp.open('wb') as f: f.write(im.content) logger.info(f'Downloaded image {imgfp.absolute()}') except BaseException as e: logger.exception(e) with jsonfp.open('w', encoding='utf-8') as j: json.dump(json_ls[i], j, ensure_ascii=False, escape_forward_slashes=False, indent=4) logger.info(f'Generated json {jsonfp.absolute()}') increase_setu(**json_ls[i])