async def call_tuling_api(session: CommandSession, text: str) -> Optional[str]: if not text: return None url = 'http://openapi.tuling123.com/openapi/api/v2' payload = { 'reqType': 0, 'perception': { 'inputText': { 'text': text } }, 'userInfo': { 'apiKey': session.bot.config.TULING_API_KEY, 'userId': context_id(session.ctx, use_hash=True) } } group_unique_id = context_id(session.ctx, mode='group', use_hash=True) if group_unique_id: payload['userInfo']['groupId'] = group_unique_id try: async with aiohttp.ClientSession() as sess: async with sess.post(url, json=payload) as response: if response.status != 200: return None resp_payload = json.loads(await response.text()) if resp_payload['results']: for result in resp_payload['results']: if result['resultType'] == 'text': return result['values']['text'] except (aiohttp.ClientError, json.JSONDecodeError, KeyError): return None
async def call_txchat_api(session: CommandSession, text: str) -> Optional[str]: # 调用腾讯AI SDK 获取回复 app_id = session.bot.config.App_ID app_key = session.bot.config.App_Key api_url = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat' req_data = { 'app_id': app_id, 'time_stamp': int(time.time()), 'nonce_str': get_nonce_str(), 'session': context_id(session.ctx, use_hash=True), 'question': text, } print('Now Session: ' + context_id(session.ctx, use_hash=True) + '\n') req_data['sign'] = sign(req_data, app_key) req_data = sorted(req_data.items()) try: async with aiohttp.ClientSession() as sess: async with sess.get(api_url, params=req_data) as response: try: data = json.loads(await response.text()) if data['ret'] == 0: print('Answer: ' + data['data']['answer'] + '\n') return data['data']['answer'] else: return None except: print(await response.text()) return None except (aiohttp.ClientError, json.JSONDecodeError, KeyError): # 抛出上面任何异常,说明调用失败 return None
async def sched_list(session: CommandSession): job_id_prefix = scheduler.make_job_id(PLUGIN_NAME, context_id(session.ctx)) jobs = await scheduler.get_jobs(scheduler.make_job_id( PLUGIN_NAME, context_id(session.ctx))) if not jobs: await session.send(f'你还没有添加过计划任务') return for job in jobs: await session.send(format_job(job.id[len(job_id_prefix):], job)) await asyncio.sleep(0.8) await session.send(f'以上是所有的 {len(jobs)} 个计划任务')
async def _(session: CommandSession): count = await note_count(context_id(session.ctx)) if count == 0: await session.send(expr(e.LIST_EMPTY)) return all_notes = await Note.query.where( Note.context_id == context_id(session.ctx)).gino.all() for n in all_notes: await session.send(f'ID:{n.id}\r\n内容:{n.content}') await asyncio.sleep(0.8) await session.send(expr(e.LIST_COMPLETE, count=count))
async def call_tuling_api(session: BaseSession, text: Optional[str], image: Optional[Union[List[str], str]]) -> List[str]: url = 'http://openapi.tuling123.com/openapi/api/v2' api_keys = session.bot.config.TULING_API_KEY if not isinstance(api_keys, Iterable) or isinstance(api_keys, str): api_keys = [api_keys] for api_key in api_keys: payload = { 'reqType': 0, 'perception': {}, 'userInfo': { 'apiKey': api_key, 'userId': context_id(session.ctx, use_hash=True) } } group_unique_id = context_id(session.ctx, mode='group', use_hash=True) if group_unique_id: payload['userInfo']['groupId'] = group_unique_id if image and not isinstance(image, str): image = image[0] if text: payload['perception']['inputText'] = {'text': text} payload['reqType'] = 0 elif image: payload['perception']['inputImage'] = {'url': image} payload['reqType'] = 1 else: return [] try: resp = await requests.post(url, json=payload) if resp.ok: resp_payload = await resp.json() if resp_payload['intent']['code'] == 4003: # 当日请求超限 continue if resp_payload['results']: return_list = [] for result in resp_payload['results']: res_type = result.get('resultType') if res_type in ('text', 'url'): return_list.append(result['values'][res_type]) return return_list except (requests.RequestException, json.JSONDecodeError, TypeError, KeyError): pass return []
async def call_tuling_api(session: CommandSession, text: str) -> Optional[str]: """ 调用图灵机器人的 API 获取回复 """ if not TULING_API_KEY: return None if not text: return None url = 'http://openapi.tuling123.com/openapi/api/v2' # 构造请求数据 payload = { 'reqType': 0, 'perception': { 'inputText': { 'text': text } }, 'userInfo': { 'apiKey': TULING_API_KEY, 'userId': context_id(session.event, use_hash=True) } } group_unique_id = context_id(session.event, mode='group', use_hash=True) if group_unique_id: payload['userInfo']['groupId'] = group_unique_id try: # 使用 httpx 库发送最终的请求 async with httpx.AsyncClient() as client: resp = await client.get(url, json=payload) if resp.status_code != 200: # 如果 HTTP 响应状态码不是 200,说明调用失败 return None resp_payload = json.loads(resp.text) if resp_payload['intent']['code'] == 4003: # 如果 code 是 4003 说明该 apikey 没有可用请求次数 return None if resp_payload['results']: for result in resp_payload['results']: if result['resultType'] == 'text': # 返回文本类型的回复 return result['values']['text'] except (httpx.HTTPError, json.JSONDecodeError, KeyError): # 抛出上面任何异常,说明调用失败 return None
async def call_tuling_api(session: CommandSession, text: str) -> Optional[str]: # 调用图灵机器人的 API 获取回复 if not text: return None url = 'http://openapi.tuling123.com/openapi/api/v2' # 构造请求数据 payload = { 'reqType': 0, 'perception': { 'inputText': { 'text': text } }, 'userInfo': { 'apiKey': session.bot.config.TULING_API_KEY, 'userId': context_id(session.ctx, use_hash=True) } } group_unique_id = context_id(session.ctx, mode='group', use_hash=True) if group_unique_id: payload['userInfo']['groupId'] = group_unique_id result_msg = "" try: # 使用 aiohttp 库发送最终的请求 async with aiohttp.ClientSession() as sess: async with sess.post(url, json=payload) as response: if response.status != 200: # 如果 HTTP 响应状态码不是 200,说明调用失败 return None resp_payload = json.loads(await response.text()) if resp_payload['results']: for result in resp_payload['results']: if result['resultType'] == 'text': # 返回文本类型的回复 result_msg += result['values']['text'] if result['resultType'] == 'news': for news in result['values']['news'][:5]: result_msg += f'''\n{news['name']}: {news['detailurl']}\n''' return result_msg except (aiohttp.ClientError, json.JSONDecodeError, KeyError): # 抛出上面任何异常,说明调用失败 return None
async def _(session: NLPSession): # logger.debug(session.msg_text) # logger.debug(session.msg) group_id = context_id(session.ctx, mode='group') user_id = session.ctx['user_id'] msg = session.msg record = records.get(group_id) # 查找是否有原来的复读信息 # 如果没有就添加进去 if record is None: record = Record(last_msg=msg, last_usr_id=user_id, repeat_count=1) records[group_id] = record return # 如果之前已经有了不同人复读信息 if record.last_msg != msg or \ record.last_usr_id == user_id: record.last_msg = msg record.repeat_count = 1 return record.last_usr_id = user_id record.repeat_count += 1 logger.debug(record.repeat_count) logger.debug("msg" + msg) if record.repeat_count == 5: record.repeat_count = 1 if record.repeat_msg != msg: record.repeat_msg = msg return IntentCommand(60.0, 'say', current_arg=msg) return
async def _(session: NLPSession): confidence = None # by default we don't return result if session.ctx['to_me']: # if the user is talking to us, we may consider reply to him/her confidence = 60.0 ctx_id = context_id(session.ctx) if ctx_id in tuling_sessions: ne_type = tuling_sessions[ctx_id] lex_result = await nlp.lexer(session.msg_text) # we only mind the first paragraph words = lex_result[0] if lex_result else [] for w in words: if ne_type == w['ne']: # if there is a tuling session existing, # and the user's input is exactly what tuling wants, # we are sure that the user is replying tuling confidence = 100.0 - len(words) * 5.0 break if confidence: return NLPResult(confidence, 'tuling', { 'message': session.msg, 'one_time': True })
async def get_tuling_answer(session: CommandSession, message: str): if not message: return None url = 'http://openapi.tuling123.com/openapi/api/v2' data = { "reqType": 0, "perception": { "inputText": { "text": message } }, "userInfo": { "apiKey": "610a38d3c0ad4ad9ba0ad8386f8caa2e", "userId": context_id(session.ctx, use_hash=True) } } try: response = requests.post(url, data=json.dumps(data)).text response = json.loads(response) if response['results']: for i in response['results']: if i['resultType'] == 'text': return i['values']['text'] except BaseException: return None
async def _(session: CommandSession): if (pair := _ttys.get(context_id(session.event))) is not None: msg = session.current_arg or await session.aget(prompt='grouptty>') try: await session.bot.send_group_msg(group_id=pair[0], message=msg) except CQHttpError: await session.send('grouptty: 发送失败!')
async def call_command(bot: NoneBot, ctx: Context_T, name: Union[str, CommandName_T], *, current_arg: str = '', args: Optional[CommandArgs_T] = None, check_perm: bool = True, disable_interaction: bool = False) -> bool: """ Call a command internally. This function is typically called by some other commands or "handle_natural_language" when handling NLPResult object. Note: If disable_interaction is not True, after calling this function, any previous command session will be overridden, even if the command being called here does not need further interaction (a.k.a asking the user for more info). :param bot: NoneBot instance :param ctx: message context :param name: command name :param current_arg: command current argument string :param args: command args :param check_perm: should check permission before running command :param disable_interaction: disable the command's further interaction :return: the command is successfully called """ cmd = _find_command(name) if not cmd: return False session = CommandSession(bot, ctx, cmd, current_arg=current_arg, args=args) return await _real_run_command(session, context_id(session.ctx), check_perm=check_perm, disable_interaction=disable_interaction)
async def call_NLP_api(session: CommandSession, text: str) -> Optional[str]: config = nonebot.get_bot().config url = "https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat" data = { 'app_id': config.TENCENT_APP_ID, 'session': context_id(session.ctx, mode='group', use_hash=True), 'question': str(text), 'time_stamp': int(time.time()), 'nonce_str': ''.join(random.choice(string.ascii_lowercase) for _ in range(32)) } data['sign'] = get_req_sign(data, config.TENCENT_APP_KEY) try: async with aiohttp.ClientSession() as sess: async with sess.post(url, data=data) as response: logger.debug(f"Send data to api: {data}") if response.status != 200: logger.error( f"Cannot connect to {url}, Status: {response.status}") await session.send("对话api调用发生错误 :(") return None r = json.loads(await response.text()) logger.debug(f"Response from API: {r}") if not r['ret']: return r['data']['answer'] elif r['msg'] == 'chat answer not found': logger.debug( "Chat answer not found. Render not understanding instead." ) return render_expression(config.EXPR_DONT_UNDERSTAND) elif r['msg'] == 'app_id not found' or r[ 'msg'] == 'app_key not found': logger.warning( "API config invalid / unfilled. Please fill them in config.py to enable NL conversation function." ) await session.send("对话api配置错误!请联系管理员") return None else: logger.error(f"Error response from API: {r['msg']}") await session.send("对话api调用发生错误 :(") return None except (aiohttp.ClientError, json.JSONDecodeError, KeyError) as e: logger.error(f"An error occupied when calling api: {e}") await session.send("对话api调用发生错误 :(") return None
async def subscribe(session: CommandSession): message = session.get('message', prompt='你想订阅什么内容呢?') hour = session.get_optional('hour') minute = session.get_optional('minute') if hour is None or minute is None: time = session.get('time', prompt='你希望我在每天的什么时候给你推送呢?\n' '(请使用24小时制,并使用阿拉伯数字表示小时和分钟)') m = re.match(r'(?P<hour>\d{1,2})[.::](?P<minute>\d{1,2})', time) if not m: m = re.match( r'(?P<hour>\d{1,2})\s*[点时]\s*' r'(?:(?P<minute>\d{1,2}|半|一刻)\s*[分]?)?', time) if m: hour = int(m.group('hour')) session.args['hour'] = hour try: minute = int(m.group('minute') or 0) except ValueError: if m.group('minute') == '半': minute = 30 elif m.group('minute') == '一刻': minute = 15 session.args['minute'] = minute else: session.pause('时间格式不对啦,请重新发送') repeat_str = session.get('repeat', prompt='是否希望我在推送消息的时候重复你上面发的消息内容呢?(请回答是或否)') repeat = nlp.check_confirmation(repeat_str) if repeat is None: session.pause('我听不懂呀,请用是或否再回答一次呢') escaped_message = message.replace('\\', '\\\\').replace('"', '\\"') if repeat: switch_arg = f'--repeat "{escaped_message}"' else: switch_arg = f'"{escaped_message}"' try: job = await scheduler.add_scheduled_commands( ScheduledCommand('switch', switch_arg), job_id=scheduler.make_job_id( PLUGIN_NAME, context_id(session.ctx), (random_string(1, string.ascii_lowercase) + random_string(7, string.ascii_lowercase + string.digits))), ctx=session.ctx, trigger='cron', hour=hour, minute=minute, replace_existing=False) session.finish(f'订阅成功啦,下次推送时间 ' f'{job.next_run_time.strftime("%Y-%m-%d %H:%M")}') except scheduler.JobIdConflictError: session.finish('订阅失败,有可能只是运气不好哦,请稍后重试~')
async def sched_remove(session: CommandSession): parser = ArgumentParser(session=session, usage=usage.REMOVE) parser.add_argument('name') args = parser.parse_args(session.argv) ok = await scheduler.remove_job(scheduler.make_job_id( PLUGIN_NAME, context_id(session.ctx), args.name)) if ok: await session.send(f'成功删除计划任务 {args.name}') else: await session.send(f'没有找到计划任务 {args.name},请检查你的输入是否正确')
async def _(session: NLPSession): data = { 'timestamp': [session.ctx['time']], 'ctx_id': [context_id(session.ctx)], 'self_id': [session.ctx['self_id']], 'message': [str(session.ctx['message'])] } # we don't want to block the processing of message, # so just make sure it will append in the future asyncio.ensure_future(append_message(data))
def kill_current_session(event: CQEvent) -> None: """ Force kill current session of the given event context, despite whether it is running or not. :param event: message event """ ctx_id = context_id(event) if ctx_id in _sessions: del _sessions[ctx_id]
async def call_tuling_api(session: CommandSession, text: str) -> Optional[str]: # 调用图灵机器人的 API 获取回复 if not text: return None url = 'http://openapi.tuling123.com/openapi/api/v2' # 构造请求数据 payload = { 'reqType': 0, 'perception': { 'inputText': { 'text': text } }, 'userInfo': { 'apiKey': "f2ac876b077e44c7b4a40ba3f9c93040", 'userId': context_id(session.ctx, use_hash=True) } } group_unique_id = context_id(session.ctx, mode='group', use_hash=True) if group_unique_id: payload['userInfo']['groupId'] = group_unique_id try: # 使用 aiohttp 库发送最终的请求 async with aiohttp.ClientSession() as sess: async with sess.post(url, json=payload) as response: if response.status != 200: # 如果 HTTP 响应状态码不是 200,说明调用失败 return None resp_payload = json.loads(await response.text()) if resp_payload['results']: for result in resp_payload['results']: if result['resultType'] == 'text': # 返回文本类型的回复 return result['values']['text'] except (aiohttp.ClientError, json.JSONDecodeError, KeyError): # 抛出上面任何异常,说明调用失败 return None
def kill_current_session(ctx: Context_T) -> None: """ Force kill current session of the given context, despite whether it is running or not. :param ctx: message context """ ctx_id = context_id(ctx) if ctx_id in _sessions: del _sessions[ctx_id]
async def call_ownthink_api(session: CommandSession, text: str) -> Optional[str]: # 调用图灵机器人的 API 获取回复 if not text: return None url = 'https://api.ownthink.com/bot' # 构造请求数据 payload = { 'spoken': text, 'appid': session.bot.config.OWNTHINK_APPID, 'userid': context_id(session.ctx, use_hash=False) } group_unique_id = context_id(session.ctx, mode='group', use_hash=False) if group_unique_id: payload['userid'] = group_unique_id print(payload) try: # 使用 aiohttp 库发送最终的请求 async with aiohttp.ClientSession() as sess: async with sess.post(url, json=payload) as response: if response.status != 200: # 如果 HTTP 响应状态码不是 200,说明调用失败 return None resp_payload = json.loads(await response.text()) if resp_payload['message'] == 'success': if resp_payload['data']['type'] == 5000: # 返回文本类型的回复 ret = resp_payload['data']['info']['text'] for k, v in session.bot.config.OWNTHINK_REPLACE_WORDS.items( ): ret = ret.replace(k, v) return ret except (aiohttp.ClientError, json.JSONDecodeError, KeyError): # 抛出上面任何异常,说明调用失败 return None
def kill_current_session(event: CQEvent) -> None: """ Force kill current session of the given event context, despite whether it is running or not. :param event: message event """ ctx_id = context_id(event) if ctx_id in _sessions: if _sessions[ctx_id].waiting: _sessions[ctx_id]._future.set_exception(_FinishException()) del _sessions[ctx_id]
async def sched_get(session: CommandSession): parser = ArgumentParser(session=session, usage=usage.GET) parser.add_argument('name') args = parser.parse_args(session.argv) job = await scheduler.get_job(scheduler.make_job_id( PLUGIN_NAME, context_id(session.ctx), args.name)) if not job: await session.send(f'没有找到计划任务 {args.name},请检查你的输入是否正确') return await session.send('找到计划任务如下') await session.send(format_job(args.name, job))
async def note_remove(session: CommandSession): ctx_id = context_id(session.ctx) count = await note_count(ctx_id) if count == 0: await session.send(expr(e.LIST_EMPTY)) return id_ = session.get('id', prompt=expr(e.DEL_WHICH_ID)) note_ = await Note.query.where((Note.context_id == ctx_id) & (Note.id == id_)).gino.first() if note_ is None: await session.send(expr(e.DEL_ID_NOT_EXISTS, id=id_)) else: await note_.delete() await session.send(expr(e.DEL_SUCCESS, id=id_, content=note_.content))
async def ai_chat(session: CommandSession): message = session.get('message', prompt=expr(e.I_AM_READY)) ctx_id = context_id(session.event) tmp_msg = Message(message) text = tmp_msg.extract_plain_text() # call ai_chat api reply = await tencent_ai.chat(text, ctx_id) logger.debug(f'Got AI reply: {reply}') if reply: session.finish(escape(reply)) else: session.finish(expr(e.I_DONT_UNDERSTAND))
async def call_tencent_api(session: CommandSession, text: str) -> Optional[str]: """ 调用腾讯机器人的 API 获取回复 """ if not TENCENT_AI_APP_KEY: return None if not text: return None url = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat' # 构造请求数据 payload = { 'app_id': int(TENCENT_AI_APP_ID), 'time_stamp': int(time.time()), 'nonce_str': ''.join(random.sample(string.ascii_letters + string.digits, 32)), 'session': context_id(session.ctx, use_hash=True), 'question': text } # 接口鉴权 签名 payload['sign'] = gen_sign_string(payload, TENCENT_AI_APP_KEY) try: # 使用 httpx 库发送最终的请求 async with httpx.AsyncClient() as client: resp = await client.get(url, params=payload) if resp.status_code != 200: # 如果 HTTP 响应状态码不是 200,说明调用失败 return None resp_payload = json.loads(resp.text) if resp_payload['ret'] != 0: # 返回非 0 表示出错 return None return resp_payload['data']['answer'] except (httpx.HTTPError, json.JSONDecodeError, KeyError): # 抛出上面任何异常,说明调用失败 return None
async def group_msg(ctx: Context_T): ctx_id = context_id(ctx) msg_queue.append(msg_queue_item(ctx_id, ctx['time'])) now_ts = int(dt.now().timestamp()) while msg_queue and (now_ts - msg_queue[0].time) >= rcnbot.config.REMOVE_DELAY: # 如果数据大于5s则移除 msg_queue.popleft() count = 0 for item in msg_queue: if item.ctx_id != ctx_id: continue count += 1 if count > rcnbot.config.MAX_COUNT: await rcnbot.set_group_ban(**ctx, duration=60)
def kill_current_session(event: CQEvent) -> None: """强行移除当前已存在的任何命令会话,即使它正在运行。该函数可用于强制移除执行时间超过预期的命令,以保证新的消息不会被拒绝服务。 参数: event: 事件对象 用法: ```python @on_command('kill', privileged=True) async def _(session: CommandSession): kill_current_session(session.event) ``` 在特权命令 `kill` 中强行移除当前正在运行的会话。 """ ctx_id = context_id(event) if ctx_id in _sessions: if _sessions[ctx_id].waiting: _sessions[ctx_id]._future.set_exception(_FinishException()) del _sessions[ctx_id]
async def tuling(session: CommandSession): message = session.get('message', prompt=__(e.I_AM_READY)) ctx_id = context_id(session.ctx) if ctx_id in tuling_sessions: del tuling_sessions[ctx_id] tmp_msg = Message(message) text = tmp_msg.extract_plain_text() images = [ s.data['url'] for s in tmp_msg if s.type == 'image' and 'url' in s.data ] # call tuling api replies = await call_tuling_api(session, text, images) logger.debug(f'Got tuling\'s replies: {replies}') if replies: for reply in replies: await session.send(escape(reply)) await asyncio.sleep(0.8) else: await session.send(__(e.I_DONT_UNDERSTAND)) one_time = session.get_optional('one_time', False) if one_time: # tuling123 may opened a session, we should recognize the # situation that tuling123 want more information from the user. # for simplification, we only recognize named entities, # and since we will also check the user's input later, # here we can allow some ambiguity. ne_type = tuling_ne_type(replies, { 'LOC': ('哪里', '哪儿', re.compile(r'哪\S城市'), '位置'), 'TIME': ('什么时候', ), }) if ne_type: logger.debug(f'One time call, ' f'and there is a tuling session for {ne_type}') tuling_sessions[ctx_id] = ne_type else: session.pause()
async def _(session: NLPSession): group_ctx_id = context_id(session.ctx, mode='group') user_id = session.ctx['user_id'] msg = session.msg record = records.get(group_ctx_id) if record is None: record = Record(msg, user_id, repeat_count=1) records[group_ctx_id] = record return if record.last_msg != msg or record.last_user_id == user_id: return record.last_user_id = user_id record.repeat_count += 1 if record.repeat_count == 3: return NLPResult( random.randint(0, 1) * 90.0, ('delayed_echo', ), { 'delay': random.randint(5, 20) / 10, 'message': msg })
async def call_command(bot: NoneBot, event: CQEvent, name: Union[str, CommandName_T], *, current_arg: str = '', args: Optional[CommandArgs_T] = None, check_perm: bool = True, disable_interaction: bool = False) -> Optional[bool]: """从内部直接调用命令。可用于在一个插件中直接调用另一个插件的命令。 参数: bot: NoneBot 对象 event: 事件对象 name (Union[str, nonebot.typing.CommandName_T]): 要调用的命令名 current_arg: 命令会话的当前输入参数 args (Optional[nonebot.typing.CommandArgs_T]): 命令会话的(初始)参数(将会被并入命令会话的 `state` 属性) check_perm: 是否检查命令的权限,若否,则即使当前事件上下文并没有权限调用这里指定的命令,也仍然会调用成功 disable_interaction: 是否禁用交互功能,若是,则该命令的会话不会覆盖任何当前已存在的命令会话,新创建的会话也不会保留 返回: bool: 命令是否调用成功 用法: ```python await call_command(bot, event, 'say', current_arg='[CQ:face,id=14]', check_perm=False) ``` 从内部调用 `say` 命令,且不检查权限。 """ cmd = CommandManager()._find_command(name) if not cmd: return False SessionClass = cmd.session_class or CommandSession session = SessionClass(bot, event, cmd, current_arg=current_arg, args=args) return await _real_run_command(session, context_id(session.event), check_perm=check_perm, disable_interaction=disable_interaction)