def initDetector(self): if self.detector is not None: self.detector.terminate() models = [ constants.getHotwordModel(config.get('hotword', 'wukong.pmdl')), constants.getHotwordModel(utils.get_do_not_bother_on_hotword()), constants.getHotwordModel(utils.get_do_not_bother_off_hotword()) ] self.detector = snowboydecoder.HotwordDetector(models, sensitivity=config.get( 'sensitivity', 0.5)) # main loop try: self.detector.start( detected_callback=[ self._detected_callback, self._do_not_bother_on_callback, self._do_not_bother_off_callback ], audio_recorder_callback=self._conversation.converse, interrupt_check=self._interrupt_callback, silent_count_threshold=config.get('silent_threshold', 15), recording_timeout=config.get('recording_timeout', 5) * 4, sleep_time=0.03) self.detector.terminate() except Exception as e: logger.critical('离线唤醒机制初始化失败:{}'.format(e))
def post(self): if self.validate(self.get_argument('validate', default=None)): qaStr = self.get_argument('qa') qaJson = os.path.join(constants.TEMP_PATH, 'qa_json') try: make_json.convert(qaStr, qaJson) solr_tools.clear_documents(config.get('/anyq/host', '0.0.0.0'), 'collection1', config.get('/anyq/solr_port', '8900') ) solr_tools.upload_documents(config.get('/anyq/host', '0.0.0.0'), 'collection1', config.get('/anyq/solr_port', '8900'), qaJson, 10 ) with open(constants.getQAPath(), 'w') as f: f.write(qaStr) res = {'code': 0, 'message': 'ok'} self.write(json.dumps(res)) except Exception as e: logger.error(e) res = {'code': 1, 'message': '提交失败,请检查内容'} self.write(json.dumps(res)) else: res = {'code': 1, 'message': 'illegal visit'} self.write(json.dumps(res)) self.finish()
def post(self): if self.validate(self.get_argument("validate", default=None)): qaStr = self.get_argument("qa") qaJson = os.path.join(constants.TEMP_PATH, "qa_json") try: make_json.convert(qaStr, qaJson) solr_tools.clear_documents( config.get("/anyq/host", "0.0.0.0"), "collection1", config.get("/anyq/solr_port", "8900"), ) solr_tools.upload_documents( config.get("/anyq/host", "0.0.0.0"), "collection1", config.get("/anyq/solr_port", "8900"), qaJson, 10, ) with open(constants.getQAPath(), "w") as f: f.write(qaStr) res = {"code": 0, "message": "ok"} self.write(json.dumps(res)) except Exception as e: logger.error(e) res = {"code": 1, "message": "提交失败,请检查内容"} self.write(json.dumps(res)) else: res = {"code": 1, "message": "illegal visit"} self.write(json.dumps(res)) self.finish()
def run(self): self.init() models = [ constants.getHotwordModel(config.get('hotword', 'wukong.pmdl')), constants.getHotwordModel(utils.get_do_not_bother_on_hotword()), constants.getHotwordModel(utils.get_do_not_bother_off_hotword()) ] # capture SIGINT signal, e.g., Ctrl+C signal.signal(signal.SIGINT, self._signal_handler) detector = snowboydecoder.HotwordDetector(models, sensitivity=config.get( 'sensitivity', 0.5)) print('Listening... Press Ctrl+C to exit') # site server.run() # main loop detector.start(detected_callback=[ self._detected_callback, self._do_not_bother_on_callback, self._do_not_bother_off_callback ], audio_recorder_callback=self._conversation.converse, interrupt_check=self._interrupt_callback, silent_count_threshold=5, sleep_time=0.03) detector.terminate()
def _start_osc(self): if not importlib.util.find_spec("pythonosc"): logger.critical("错误:请先安装 python-osc !") return from pythonosc import dispatcher as dsp from pythonosc import osc_server dispatcher = dsp.Dispatcher() dispatcher.map("/muse/elements/blink", self.blink_handler, "EEG") dispatcher.map("/muse/elements/jaw_clench", self.jaw_clench_handler, "EEG") try: server = osc_server.ThreadingOSCUDPServer( ( config.get("/muse/ip", "127.0.0.1"), int(config.get("/muse/port", "5001")), ), dispatcher, ) logger.info("Muse serving on {}".format(server.server_address)) server.serve_forever() except Exception as e: logger.error(e)
def activeListen(self, silent=False): """主动问一个问题(适用于多轮对话)""" if config.get("/LED/enable", False): LED.wakeup() logger.debug("activeListen") try: if not silent: Player.play(constants.getData("beep_hi.wav")) listener = snowboydecoder.ActiveListener([ constants.getHotwordModel(config.get("hotword", "wukong.pmdl")) ]) voice = listener.listen( silent_count_threshold=config.get("silent_threshold", 15), recording_timeout=config.get("recording_timeout", 5) * 4, ) if not silent: Player.play(constants.getData("beep_lo.wav")) if voice: query = self.asr.transcribe(voice) utils.check_and_delete(voice) return query return "" except Exception as e: logger.error("主动聆听失败".format(e)) return ""
def activeListen(self, silent=False): """ 主动问一个问题(适用于多轮对话) """ if config.get('/LED/enable', False): LED.wakeup() logger.debug('activeListen') try: if not silent: #time.sleep(1) Player.play(constants.getData('beep_hi.wav')) listener = snowboydecoder.ActiveListener([ constants.getHotwordModel(config.get('hotword', 'wukong.pmdl')) ]) voice = listener.listen( silent_count_threshold=config.get('silent_threshold', 15), recording_timeout=config.get('recording_timeout', 5) * 4) if not silent: Player.play(constants.getData('beep_lo.wav')) if voice: query = self.asr.transcribe(voice) utils.check_and_delete(voice) return query return '' except Exception as e: logger.error(e) return ''
def doResponse(self, query, UUID='', onSay=None): statistic.report(1) self.interrupt() self.appendHistory(0, query, UUID) if config.get('/LED/enable', False): LED.think() if onSay: self.onSay = onSay if query.strip() == '': self.pardon() return lastImmersiveMode = self.immersiveMode if not self.brain.query(query): # 没命中技能,使用机器人回复 msg = self.ai.chat(query) self.say(msg, True, onCompleted=self.checkRestore) else: if lastImmersiveMode is not None and lastImmersiveMode != self.matchPlugin: time.sleep(1) if self.player is not None and self.player.is_playing(): logger.debug('等说完再checkRestore') self.player.appendOnCompleted(lambda: self.checkRestore()) else: logger.debug('checkRestore') self.checkRestore() if config.get('/LED/enable', False): LED.off()
def initDetector(self): if self.detector is not None: self.detector.terminate() models = [ constants.getHotwordModel(config.get('hotword', 'xiaofeng.pmdl')) ] # gql 检测唤醒词 self.detector = snowboydecoder.HotwordDetector(models, sensitivity=config.get( 'sensitivity', 0.5)) # main loop try: callbacks = self._detected_callback self.detector.start( detected_callback=callbacks, audio_recorder_callback=self._conversation.converse, interrupt_check=self._interrupt_callback, silent_count_threshold=config.get('silent_threshold', 15), recording_timeout=config.get('recording_timeout', 5) * 4, sleep_time=0.03) self.detector.terminate() except Exception as e: print("error") logger.critical('离线唤醒机制初始化失败:{}'.format(e))
def init(self): global conversation self.detector = None self._interrupted = False print(''' ******************************************************** * wukong-robot - 中文语音对话机器人 * * (c) 2019 潘伟洲 <*****@*****.**> * * https://github.com/wzpan/wukong-robot.git * ******************************************************** 后台管理端:http://{}:{} 如需退出,可以按 Ctrl-4 组合键 '''.format(config.get('/server/host', '0.0.0.0'), config.get('/server/port', '5000'))) config.init() self._conversation = Conversation(self._profiling) self._conversation.say('{} 你好!试试对我喊唤醒词叫醒我吧'.format(config.get('first_name', '主人')), True) self._observer = Observer() event_handler = ConfigMonitor(self._conversation) self._observer.schedule(event_handler, constants.CONFIG_PATH, False) self._observer.schedule(event_handler, constants.DATA_PATH, False) self._observer.start() if config.get('/muse/enable', False): self._wakeup = multiprocessing.Event() self.bci = BCI.MuseBCI(self._wakeup) self.bci.start() thread.start_new_thread(self._loop_event, ())
def post(self): if self.get_argument('username') == config.get('/server/username') and \ hashlib.md5(self.get_argument('password').encode('utf-8')).hexdigest() \ == config.get('/server/validate'): self.set_cookie("validation", config.get('/server/validate')) self.redirect("/") else: self.render('login.html', error="登录失败")
def reload(self): """ 重新初始化 """ try: self.asr = ASR.get_engine_by_slug(config.get('asr_engine', 'tencent-asr')) self.ai = AI.get_robot_by_slug(config.get('robot', 'tuling')) self.tts = TTS.get_engine_by_slug(config.get('tts_engine', 'baidu-tts')) except Exception as e: logger.critical("对话初始化失败:{}".format(e))
def get_config(self): appid = config.get('/emotibot/appid', '') location = config.get('location', '深圳') more = config.get('active_mode', False) return { 'appid': appid, 'location': location, 'more': more }
def wakeup(): if config.get('/LED/enable', False): if config.get('/LED/type') == 'aiy': thread.start_new_thread(aiy.wakeup, ()) elif config.get('/LED/type') == 'respeaker': from robot.drivers.pixels import pixels pixels.wakeup() else: logger.error('错误:不支持的灯光类型')
def start_server(): if config.get('/server/enable', False): host = config.get('/server/host', '0.0.0.0') port = config.get('/server/port', '5000') try: asyncio.set_event_loop(asyncio.new_event_loop()) application.listen(int(port)) tornado.ioloop.IOLoop.instance().start() except Exception as e: logger.critical('服务器启动失败: {}'.format(e))
def wakeup(): if config.get("/LED/enable", False): if config.get("/LED/type") == "aiy": thread.start_new_thread(aiy.wakeup, ()) elif config.get("/LED/type") == "respeaker": from robot.drivers.pixels import pixels pixels.wakeup() else: logger.error("错误:不支持的灯光类型")
def post(self): if self.get_argument("username") == config.get( "/server/username") and hashlib.md5( self.get_argument("password").encode("utf-8")).hexdigest( ) == config.get("/server/validate"): print("success") self.set_secure_cookie("validation", config.get("/server/validate")) self.redirect("/") else: self.render("login.html", error="登录失败")
def reload(self): """ 重新初始化 """ try: self.asr = ASR.get_engine_by_slug(config.get('asr_engine', 'tencent-asr')) self.ai = AI.get_robot_by_slug(config.get('robot', 'tuling')) self.tts = TTS.get_engine_by_slug(config.get('tts_engine', 'baidu-tts')) self.nlu = NLU.get_engine_by_slug(config.get('nlu_engine', 'unit')) self.player = None self.brain = Brain(self) self.brain.printPlugins() except Exception as e: logger.critical("对话初始化失败:{}".format(e))
def start_server(con, wk): global conversation, wukong conversation = con wukong = wk if config.get('/server/enable', False): port = config.get('/server/port', '5000') try: asyncio.set_event_loop(asyncio.new_event_loop()) application.listen(int(port)) tornado.ioloop.IOLoop.instance().start() except Exception as e: logger.critical('服务器启动失败: {}'.format(e))
def say(self, msg, cache=False, plugin='', onCompleted=None, wait=False): """ 说一句话 :param msg: 内容 :param cache: 是否缓存这句话的音频 :param plugin: 来自哪个插件的消息(将带上插件的说明) :param onCompleted: 完成的回调 :param wait: 是否要等待说完(为True将阻塞主线程直至说完这句话) """ if plugin != '': self.appendHistory(1, "[{}] {}".format(plugin, msg)) else: self.appendHistory(1, msg) pattern = r'^https?://.+' if re.match(pattern, msg): logger.info("内容包含URL,所以不读出来") return voice = '' cache_path = '' if utils.getCache(msg): logger.info("命中缓存,播放缓存语音") if config.get('/tts_engine') == 'hass-tts': voice = self.tts.get_speech(msg) else: voice = utils.getCache(msg) cache_path = utils.getCache(msg) else: try: voice = self.tts.get_speech(msg) if voice != '': cache_path = utils.saveCache(voice, msg) except Exception as e: logger.error('保存缓存失败:{}'.format(e)) if self.onSay: logger.info(cache) audio = 'http://{}:{}/audio/{}'.format( config.get('/server/host'), config.get('/server/port'), os.path.basename(cache_path)) logger.info('onSay: {}, {}'.format(msg, audio)) if plugin != '': self.onSay("[{}] {}".format(plugin, msg), audio) else: self.onSay(msg, audio) self.onSay = None if onCompleted is None: onCompleted = lambda: self._onCompleted(msg) self.player = Player.SoxPlayer() self.player.play(voice, not cache, onCompleted, wait) if not cache: utils.check_and_delete(cache_path, 60) # 60秒后将自动清理不缓存的音频 utils.lruCache() # 清理缓存
def activeListen(self): """ 主动问一个问题(适用于多轮对话) """ time.sleep(1) Player.play(constants.getData('beep_hi.wav')) listener = snowboydecoder.ActiveListener( [constants.getHotwordModel(config.get('hotword', 'wukong.pmdl'))]) voice = listener.listen( silent_count_threshold=config.get('silent_threshold', 15), recording_timeout=config.get('recording_timeout', 5) * 4) Player.play(constants.getData('beep_lo.wav')) query = self.asr.transcribe(voice) utils.check_and_delete(voice) return query
def handle(self, query): province = config.get('/Covid/province') if '疫情情况' in query: logger.info('命中 <疫情情况> 插件') try: # 所在省份的患病相关人数情况 url_num = 'https://lab.isaaclin.cn/nCoV/api/area?latest=1&province={}'.format( config.get('/Covid/province')) data_num = requests.get(url=url_num) # 累计确诊人数 confirmedCount = data_num.json( )["results"][0]['confirmedCount'] # 治愈人数 curedCount = data_num.json()["results"][0]['curedCount'] updateTime = ( data_num.json()["results"][0]['updateTime']) / 1000 date = time.localtime(updateTime) # 利用localtime()转换为时间数组 format_date = time.strftime('%Y-%m-%d %H:%M:%S', date) # 利用strftime()将其格式化为需要的格式 res = '截止' + str( format_date) + ',' + province + '累计确诊人数' + str( confirmedCount) + '例,' + '治愈人数' + str(curedCount) + '例' self.con.say(res, True) logger.info(res) except Exception as e: logger.error(e) self.con.say("疫情情况查询失败了!", True) elif '疫情新闻' in query: logger.info('命中 <疫情新闻> 插件') try: # 这里由于处过重点省份外,其他省份新闻的信息性不高,所以采用全国新闻 # 全国性质的新闻 url_msg = 'http://lab.isaaclin.cn/nCoV/api/overall' msgs = requests.get(url=url_msg) # 新闻的标题(考虑到内容过长,影响体验,使用标题进行播报) msg = msgs.json()["results"][0]["generalRemark"] uptime = (msgs.json()["results"][0]["updateTime"]) / 1000 date = time.localtime(uptime) # 利用localtime()转换为时间数组 format_date = time.strftime('%Y-%m-%d ', date) # 利用strftime()将其格式化为需要的格式 msgs = format_date + '新闻:' + msg self.con.say(msgs, True) logger.info(msgs) except Exception as e: logger.error(e) self.con.say("疫情新闻查询失败了!", True) else: logger.error('疫情情况获取失败了!') self.con.say("疫情情况获取失败了!", True)
def reInit(self): """重新初始化""" try: self.asr = ASR.get_engine_by_slug( config.get("asr_engine", "tencent-asr")) self.ai = AI.get_robot_by_slug(config.get("robot", "tuling")) self.tts = TTS.get_engine_by_slug( config.get("tts_engine", "baidu-tts")) self.nlu = NLU.get_engine_by_slug(config.get("nlu_engine", "unit")) self.player = None self.brain = Brain(self) self.brain.printPlugins() except Exception as e: logger.critical("对话初始化失败:{}".format(e))
def query(self, text): """ query 模块 Arguments: text -- 用户输入 """ args = { "service_id": config.get("/unit/service_id", "S13442"), "api_key": config.get("/unit/api_key", "w5v7gUV3iPGsGntcM84PtOOM"), "secret_key": config.get("/unit/secret_key", "KffXwW6E1alcGplcabcNs63Li6GvvnfL"), } parsed = self.conversation.doParse(text, **args) for plugin in self.plugins: if not self.isValid(plugin, text, parsed) and not self.isImmersive( plugin, text, parsed): continue logger.info("'{}' 命中技能 {}".format(text, plugin.SLUG)) self.conversation.matchPlugin = plugin.SLUG if plugin.IS_IMMERSIVE: self.conversation.setImmersiveMode(plugin.SLUG) continueHandle = False try: self.handling = True continueHandle = plugin.handle(text, parsed) self.handling = False except Exception: logger.critical("Failed to execute plugin", exc_info=True) reply = "抱歉,插件{}出故障了,晚点再试试吧".format(plugin.SLUG) self.conversation.say(reply, plugin=plugin.SLUG) else: logger.debug( "Handling of phrase '%s' by " + "plugin '%s' completed", text, plugin.SLUG, ) finally: if not continueHandle: return True logger.debug("No plugin was able to handle phrase {} ".format(text)) return False
def say(self, msg, cache=False, plugin='', onCompleted=None, wait=False, resident=False): """ 说一句话 :param resident: :param msg: 内容 :param cache: 是否缓存这句话的音频 :param plugin: 来自哪个插件的消息(将带上插件的说明) :param onCompleted: 完成的回调 :param wait: 是否要等待说完(为True将阻塞主线程直至说完这句话) """ self.appendHistory(1, msg, plugin=plugin) if re.match(r'^https?://.+', msg): logger.info("内容包含URL,所以不读出来") self.onSay(msg, '', plugin=plugin) self.onSay = None return voice = utils.getCache(msg, resident=resident) cache_path = '' if voice: logger.info("命中缓存,播放缓存语音") cache_path = voice else: try: voice = self.tts.get_speech(msg, resident=resident) cache_path = utils.saveCache(voice, msg, resident=resident) except Exception as e: logger.error('保存缓存失败:{}'.format(e)) if self.onSay: logger.info(cache) audio = 'http://{}:{}/audio/{}'.format( config.get('/server/host'), config.get('/server/port'), os.path.basename(cache_path)) logger.info('onSay: {}, {}'.format(msg, audio)) self.onSay(msg, audio, plugin=plugin) self.onSay = None if onCompleted is None: def onCompleted(): self._onCompleted(msg) self.player = Player.getPlayerByFileName(voice) self.player.play(voice, not cache, onCompleted, wait) utils.lruCache() # 清理缓存
def isValidImmersive(self, plugin, text, parsed): patterns = config.get("/{}/patterns".format(plugin.SLUG), []) if len(patterns) > 0: return plugin.isValidImmersive(text, parsed) or self.match( patterns, text) else: return plugin.isValidImmersive(text, parsed)
def train(self, w1, w2, w3, m): ''' 传入三个wav文件,生成snowboy的.pmdl模型 ''' def get_wave(fname): with open(fname, 'rb') as infile: return base64.b64encode(infile.read()).decode('utf-8') url = 'https://snowboy.kitt.ai/api/v1/train/' data = { "name": "wukong-robot", "language": "zh", "token": config.get('snowboy_token', ''), "voice_samples": [ {"wave": get_wave(w1)}, {"wave": get_wave(w2)}, {"wave": get_wave(w3)} ] } response = requests.post(url, json=data) if response.ok: with open(m, "wb") as outfile: outfile.write(response.content) return 'Snowboy模型已保存至{}'.format(m) else: return "Snowboy模型生成失败,原因:{}".format(response.text)
def upload(self, threadNum=10): """ 手动上传 QA 集语料,重建 solr 索引 """ try: qaJson = os.path.join(constants.TEMP_PATH, 'qa_json') make_json.run(constants.getQAPath(), qaJson) solr_tools.clear_documents(config.get('/anyq/host', '0.0.0.0'), 'collection1', config.get('/anyq/solr_port', '8900')) solr_tools.upload_documents(config.get('/anyq/host', '0.0.0.0'), 'collection1', config.get('/anyq/solr_port', '8900'), qaJson, threadNum) except Exception as e: logger.error("上传失败:{}".format(e))
def init(self): global conversation self.detector = None self._interrupted = False print(''' ******************************************************** * wukong-robot - 中文语音对话机器人 * * (c) 2019 潘伟洲 <*****@*****.**> * * https://github.com/wzpan/wukong-robot.git * ******************************************************** 如需退出,可以按 Ctrl-4 组合键。 ''') config.init() self._i2c = I2c() self._i2c.start() self._conversation = Conversation(self._i2c, self._profiling) self._conversation.say( '{} 你好!试试对我喊唤醒词叫醒我吧'.format(config.get('first_name', '主人')), True) self._observer = Observer() event_handler = ConfigMonitor(self._conversation) self._observer.schedule(event_handler, constants.CONFIG_PATH, False) self._observer.schedule(event_handler, constants.DATA_PATH, False) self._observer.start()
def getSubject(self, msg): """ Returns the title of an email Arguments: msg -- the email Returns: Title of the email. """ subject = email.header.decode_header(msg['subject']) if isinstance(subject[0][0], bytes): try: sub = subject[0][0].decode('utf-8') except UnicodeDecodeError: sub = subject[0][0].decode('gbk') else: sub = subject[0][0] to_read = False if sub.strip() == '': return '' to_read = config.get('/email/read_email_title', True) if to_read: return '邮件标题为 %s' % sub return ''