|
@@ -3,15 +3,23 @@ from __future__ import division
|
|
|
from .__load__ import *
|
|
|
|
|
|
class Robot(object):
|
|
|
- tool = {}
|
|
|
def init(self, profiling=False):
|
|
|
- self.start()
|
|
|
- # 历史记录
|
|
|
- self.history = []
|
|
|
+ # 大脑
|
|
|
+ self.brain = Demeter.service('brain').init(self)
|
|
|
+ # 耳朵
|
|
|
+ self.ear = Demeter.service('ear').init(self)
|
|
|
+ # 嘴
|
|
|
+ self.mouth = Demeter.service('mouth').init(self)
|
|
|
+ # 眼睛
|
|
|
+ self.eye = Demeter.service('eye').init(self)
|
|
|
+
|
|
|
+ # 行为工具
|
|
|
+ self.tool = {}
|
|
|
# 数据文件夹
|
|
|
self.data = Demeter.config['vecan']['data']
|
|
|
# 日志
|
|
|
self.logger = Log.init(__name__)
|
|
|
+
|
|
|
# 是否正在录音
|
|
|
self.isRecording = False
|
|
|
# 插件验证--暂时无用
|
|
@@ -20,79 +28,13 @@ class Robot(object):
|
|
|
self.immersiveMode = None
|
|
|
# 优化
|
|
|
self.profiling = profiling
|
|
|
- # 询问模式
|
|
|
- self.hasPardon = False
|
|
|
- # 播放器
|
|
|
- self.player = None
|
|
|
- # snowboy
|
|
|
- self.snowboy = None
|
|
|
- # 对话
|
|
|
- self.onSay = None
|
|
|
- # 技能
|
|
|
- self.skill = None
|
|
|
-
|
|
|
- def initSnowboy(self, snowboy):
|
|
|
- self.snowboy = snowboy
|
|
|
-
|
|
|
- def getSnowBoy(self):
|
|
|
- if self.snowboy:
|
|
|
- return self.snowboy
|
|
|
- else:
|
|
|
- self.snowboy = Demeter.service('snowboy')
|
|
|
- self.snowboy.init(self)
|
|
|
- return self.snowboy
|
|
|
-
|
|
|
- def getHistory(self):
|
|
|
- return self.history
|
|
|
-
|
|
|
- def say(self, msg, cache=False, plugin='', completed=None):
|
|
|
- print msg
|
|
|
- if self.onSay:
|
|
|
- self.logger.info('onSay: {}'.format(msg))
|
|
|
- if plugin != '':
|
|
|
- self.onSay("[{}] {}".format(plugin, msg))
|
|
|
- else:
|
|
|
- self.onSay(msg)
|
|
|
- self.onSay = None
|
|
|
- if plugin != '':
|
|
|
- self.appendHistory(1, "[{}] {}".format(plugin, msg))
|
|
|
- else:
|
|
|
- self.appendHistory(1, msg)
|
|
|
- pattern = r'^https?://.+'
|
|
|
- if re.match(pattern, msg):
|
|
|
- self.logger.info('内容包含URL,无法读取')
|
|
|
- return
|
|
|
- voice = ''
|
|
|
- voice = self.tool['tts'].tts(msg)
|
|
|
- if cache:
|
|
|
- saveCache(voice, msg)
|
|
|
- '''
|
|
|
- if getCache(msg):
|
|
|
- self.logger.info('命中缓存,播放缓存语音')
|
|
|
- voice = getCache(msg)
|
|
|
- else:
|
|
|
- try:
|
|
|
- voice = self.tool['tts'].tts(msg)
|
|
|
- if cache:
|
|
|
- saveCache(voice, msg)
|
|
|
- except Exception as e:
|
|
|
- self.logger.error('保存缓存失败:{}'.format(e))
|
|
|
- '''
|
|
|
- if completed is None:
|
|
|
- completed = lambda: self.completed(msg)
|
|
|
- self.play(voice, not cache, completed=completed)
|
|
|
-
|
|
|
- def play(self, voice, delete=False, completed=None):
|
|
|
- if self.player == None:
|
|
|
- self.player = Demeter.service('sox', 'player')
|
|
|
- self.player.play(voice, delete=delete, onCompleted=completed)
|
|
|
+
|
|
|
+ # 醒来 启动
|
|
|
+ self.wakeup()
|
|
|
|
|
|
- def start(self):
|
|
|
+ def wakeup(self):
|
|
|
# 机器人配置,后续改成从数据库读取
|
|
|
self.config = Demeter.config['robot']
|
|
|
- self.reload()
|
|
|
-
|
|
|
- def reload(self):
|
|
|
try:
|
|
|
for item in self.config:
|
|
|
setting = self.setting(self.config[item])
|
|
@@ -100,18 +42,25 @@ class Robot(object):
|
|
|
self.tool[item] = Demeter.service(self.config[item], 'api')
|
|
|
self.tool[item].setting(**setting)
|
|
|
self.player = None
|
|
|
- self.skill = Demeter.service('skill')
|
|
|
- self.skill.init(self)
|
|
|
- self.skill.printPlugins()
|
|
|
+ self.ear.wait()
|
|
|
+ self.brain.printPlugins()
|
|
|
except Exception as e:
|
|
|
self.logger.critical('机器人初始化失败:{}'.format(e))
|
|
|
|
|
|
+ def sleep(self, signal, frame):
|
|
|
+ clean()
|
|
|
+ self.brain.pause()
|
|
|
+ self.ear.close()
|
|
|
+ self.mouth.close()
|
|
|
+ self.eye.close()
|
|
|
+ Demeter.dog.stop()
|
|
|
+
|
|
|
+ def say(self, msg, cache=False, plugin='', completed=None):
|
|
|
+ self.mouth.say(msg, cache, plugin, completed)
|
|
|
+
|
|
|
def checkRestore(self):
|
|
|
if self.immersiveMode:
|
|
|
- self.skill.restore()
|
|
|
-
|
|
|
- def checkPlayer(self):
|
|
|
- return self.player is not None and self.player.is_playing()
|
|
|
+ self.brain.restore()
|
|
|
|
|
|
def setting(self, name):
|
|
|
# 后续要改成数据库读取
|
|
@@ -119,40 +68,10 @@ class Robot(object):
|
|
|
return config
|
|
|
|
|
|
# 中断、停止
|
|
|
- def interrupt(self):
|
|
|
- if self.checkPlayer():
|
|
|
- self.player.stop()
|
|
|
- self.player = None
|
|
|
+ def stop(self):
|
|
|
+ self.mouth.close()
|
|
|
if self.immersiveMode:
|
|
|
- self.skill.pause()
|
|
|
-
|
|
|
- def talk(self, fp, callback=None):
|
|
|
- self.play(self.data + 'beep_lo.wav')
|
|
|
- self.logger.info('结束录音')
|
|
|
- self.isRecording = False
|
|
|
- if self.profiling:
|
|
|
- self.logger.info('性能调试已打开')
|
|
|
- pr = cProfile.Profile()
|
|
|
- pr.enable()
|
|
|
- self.onTalk(fp, callback)
|
|
|
- pr.disable()
|
|
|
- s = io.StringIO()
|
|
|
- sortby = 'cumulative'
|
|
|
- ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
|
|
|
- ps.print_stats()
|
|
|
- print(s.getvalue())
|
|
|
- else:
|
|
|
- self.onTalk(fp, callback)
|
|
|
-
|
|
|
- def onTalk(self, fp, callback=None, onSay=None):
|
|
|
- try:
|
|
|
- self.interrupt()
|
|
|
- query = self.tool['asr'].asr(fp)
|
|
|
- File.remove(fp)
|
|
|
- self.response(query, callback, onSay)
|
|
|
- except Exception as e:
|
|
|
- self.logger.critical(e)
|
|
|
- clean()
|
|
|
+ self.brain.pause()
|
|
|
|
|
|
def doParse(self, query, **args):
|
|
|
return self.tool['nlu'].nlu(query, **args)
|
|
@@ -161,64 +80,4 @@ class Robot(object):
|
|
|
self.immersiveMode = slug
|
|
|
|
|
|
def getImmersiveMode(self):
|
|
|
- return self.immersiveMode
|
|
|
-
|
|
|
- def response(self, query, uid='', onSay=None):
|
|
|
- # 统计
|
|
|
- #statistic.report(1)
|
|
|
- self.interrupt()
|
|
|
- self.appendHistory(0, query, uid)
|
|
|
-
|
|
|
- if onSay:
|
|
|
- self.onSay = onSay
|
|
|
-
|
|
|
- if query.strip() == '':
|
|
|
- self.pardon()
|
|
|
- return
|
|
|
-
|
|
|
- lastImmersiveMode = self.immersiveMode
|
|
|
-
|
|
|
- if not self.skill.query(query):
|
|
|
- # 没命中技能,使用机器人回复
|
|
|
- msg = self.tool['ai'].ai(query)
|
|
|
- self.say(msg, True, completed=self.checkRestore)
|
|
|
- else:
|
|
|
- if lastImmersiveMode is not None and lastImmersiveMode != self.matchPlugin:
|
|
|
- time.sleep(1)
|
|
|
- if self.checkPlayer():
|
|
|
- self.logger.debug('等说完再checkRestore')
|
|
|
- self.player.appendOnCompleted(lambda: self.checkRestore())
|
|
|
- else:
|
|
|
- self.logger.debug('checkRestore')
|
|
|
- self.checkRestore()
|
|
|
-
|
|
|
- #将会话历史加进历史记录
|
|
|
- def appendHistory(self, t, text, uid=''):
|
|
|
- if t in (0, 1) and text != '':
|
|
|
- if text.endswith(',') or text.endswith(','):
|
|
|
- text = text[:-1]
|
|
|
- if uid == '' or uid == None or uid == 'null':
|
|
|
- uid = Demeter.uuid(__name__)
|
|
|
- # 将图片处理成HTML
|
|
|
- pattern = r'https?://.+\.(?:png|jpg|jpeg|bmp|gif|JPG|PNG|JPEG|BMP|GIF)'
|
|
|
- url_pattern = r'^https?://.+'
|
|
|
- imgs = re.findall(pattern, text)
|
|
|
- for img in imgs:
|
|
|
- text = text.replace(img, '<img src={} class="img"/>'.format(img))
|
|
|
- urls = re.findall(url_pattern, text)
|
|
|
- for url in urls:
|
|
|
- text = text.replace(url, '<a href={} target="_blank">{}</a>'.format(url, url))
|
|
|
- self.history.append({'type': t, 'text': text, 'time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), 'uid': uid})
|
|
|
-
|
|
|
- def completed(self, msg):
|
|
|
- if Demeter.config['snowboy']['active_mode'] and (msg.endswith('?') or msg.endswith(u'?') or u'告诉我' in msg or u'请回答' in msg):
|
|
|
- query = self.getSnowBoy().listen()
|
|
|
- self.response(query)
|
|
|
-
|
|
|
- def pardon(self):
|
|
|
- if not self.hasPardon:
|
|
|
- self.say('抱歉,刚刚没听清,能再说一遍吗?', completed=lambda: self.response(self.getSnowBoy().listen()))
|
|
|
- self.hasPardon = True
|
|
|
- else:
|
|
|
- self.say('没听清呢')
|
|
|
- self.hasPardon = False
|
|
|
+ return self.immersiveMode
|