robot.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. # -*- coding: utf-8 -*-
  2. from __future__ import division
  3. from .__load__ import *
  4. class Robot(object):
  5. tool = {}
  6. def init(self, profiling=False):
  7. self.start()
  8. # 历史记录
  9. self.history = []
  10. # 数据文件夹
  11. self.data = Demeter.config['vecan']['data']
  12. # 日志
  13. self.logger = Log.init(__name__)
  14. # 是否正在录音
  15. self.isRecording = False
  16. # 插件验证--暂时无用
  17. self.matchPlugin = None
  18. # 勿扰模式
  19. self.immersiveMode = None
  20. # 优化
  21. self.profiling = profiling
  22. # 询问模式
  23. self.hasPardon = False
  24. # 播放器
  25. self.player = None
  26. # snowboy
  27. self.snowboy = None
  28. # 对话
  29. self.onSay = None
  30. # 技能
  31. self.skill = None
  32. def initSnowboy(self, snowboy):
  33. self.snowboy = snowboy
  34. def getSnowBoy(self):
  35. if self.snowboy:
  36. return self.snowboy
  37. else:
  38. self.snowboy = Demeter.service('snowboy')
  39. self.snowboy.init(self)
  40. return self.snowboy
  41. def getHistory(self):
  42. return self.history
  43. def say(self, msg, cache=False, plugin='', completed=None):
  44. print msg
  45. if self.onSay:
  46. self.logger.info('onSay: {}'.format(msg))
  47. if plugin != '':
  48. self.onSay("[{}] {}".format(plugin, msg))
  49. else:
  50. self.onSay(msg)
  51. self.onSay = None
  52. if plugin != '':
  53. self.appendHistory(1, "[{}] {}".format(plugin, msg))
  54. else:
  55. self.appendHistory(1, msg)
  56. pattern = r'^https?://.+'
  57. if re.match(pattern, msg):
  58. self.logger.info('内容包含URL,无法读取')
  59. return
  60. voice = ''
  61. voice = self.tool['tts'].tts(msg)
  62. if cache:
  63. saveCache(voice, msg)
  64. '''
  65. if getCache(msg):
  66. self.logger.info('命中缓存,播放缓存语音')
  67. voice = getCache(msg)
  68. else:
  69. try:
  70. voice = self.tool['tts'].tts(msg)
  71. if cache:
  72. saveCache(voice, msg)
  73. except Exception as e:
  74. self.logger.error('保存缓存失败:{}'.format(e))
  75. '''
  76. if completed is None:
  77. completed = lambda: self.completed(msg)
  78. self.play(voice, not cache, completed=completed)
  79. def play(self, voice, delete=False, completed=None):
  80. if self.player == None:
  81. self.player = Demeter.service('sox', 'player')
  82. self.player.play(voice, delete=delete, onCompleted=completed)
  83. def start(self):
  84. # 机器人配置,后续改成从数据库读取
  85. self.config = Demeter.config['robot']
  86. self.reload()
  87. def reload(self):
  88. try:
  89. for item in self.config:
  90. setting = self.setting(self.config[item])
  91. if item not in self.tool:
  92. self.tool[item] = Demeter.service(self.config[item], 'api')
  93. self.tool[item].setting(**setting)
  94. self.player = None
  95. self.skill = Demeter.service('skill')
  96. self.skill.init(self)
  97. self.skill.printPlugins()
  98. except Exception as e:
  99. self.logger.critical('机器人初始化失败:{}'.format(e))
  100. def checkRestore(self):
  101. if self.immersiveMode:
  102. self.skill.restore()
  103. def checkPlayer(self):
  104. return self.player is not None and self.player.is_playing()
  105. def setting(self, name):
  106. # 后续要改成数据库读取
  107. config = Demeter.config[name]
  108. return config
  109. # 中断、停止
  110. def interrupt(self):
  111. if self.checkPlayer():
  112. self.player.stop()
  113. self.player = None
  114. if self.immersiveMode:
  115. self.skill.pause()
  116. def talk(self, fp, callback=None):
  117. self.play(self.data + 'beep_lo.wav')
  118. self.logger.info('结束录音')
  119. self.isRecording = False
  120. if self.profiling:
  121. self.logger.info('性能调试已打开')
  122. pr = cProfile.Profile()
  123. pr.enable()
  124. self.onTalk(fp, callback)
  125. pr.disable()
  126. s = io.StringIO()
  127. sortby = 'cumulative'
  128. ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
  129. ps.print_stats()
  130. print(s.getvalue())
  131. else:
  132. self.onTalk(fp, callback)
  133. def onTalk(self, fp, callback=None, onSay=None):
  134. try:
  135. self.interrupt()
  136. query = self.tool['asr'].asr(fp)
  137. File.remove(fp)
  138. self.response(query, callback, onSay)
  139. except Exception as e:
  140. self.logger.critical(e)
  141. clean()
  142. def doParse(self, query, **args):
  143. return self.tool['nlu'].nlu(query, **args)
  144. def setImmersiveMode(self, slug):
  145. self.immersiveMode = slug
  146. def getImmersiveMode(self):
  147. return self.immersiveMode
  148. def response(self, query, uid='', onSay=None):
  149. # 统计
  150. #statistic.report(1)
  151. self.interrupt()
  152. self.appendHistory(0, query, uid)
  153. if onSay:
  154. self.onSay = onSay
  155. if query.strip() == '':
  156. self.pardon()
  157. return
  158. lastImmersiveMode = self.immersiveMode
  159. if not self.skill.query(query):
  160. # 没命中技能,使用机器人回复
  161. msg = self.tool['ai'].ai(query)
  162. self.say(msg, True, completed=self.checkRestore)
  163. else:
  164. if lastImmersiveMode is not None and lastImmersiveMode != self.matchPlugin:
  165. time.sleep(1)
  166. if self.checkPlayer():
  167. self.logger.debug('等说完再checkRestore')
  168. self.player.appendOnCompleted(lambda: self.checkRestore())
  169. else:
  170. self.logger.debug('checkRestore')
  171. self.checkRestore()
  172. #将会话历史加进历史记录
  173. def appendHistory(self, t, text, uid=''):
  174. if t in (0, 1) and text != '':
  175. if text.endswith(',') or text.endswith(','):
  176. text = text[:-1]
  177. if uid == '' or uid == None or uid == 'null':
  178. uid = Demeter.uuid(__name__)
  179. # 将图片处理成HTML
  180. pattern = r'https?://.+\.(?:png|jpg|jpeg|bmp|gif|JPG|PNG|JPEG|BMP|GIF)'
  181. url_pattern = r'^https?://.+'
  182. imgs = re.findall(pattern, text)
  183. for img in imgs:
  184. text = text.replace(img, '<img src={} class="img"/>'.format(img))
  185. urls = re.findall(url_pattern, text)
  186. for url in urls:
  187. text = text.replace(url, '<a href={} target="_blank">{}</a>'.format(url, url))
  188. self.history.append({'type': t, 'text': text, 'time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), 'uid': uid})
  189. def completed(self, msg):
  190. if Demeter.config['snowboy']['active_mode'] and (msg.endswith('?') or msg.endswith(u'?') or u'告诉我' in msg or u'请回答' in msg):
  191. query = self.getSnowBoy().listen()
  192. self.response(query)
  193. def pardon(self):
  194. if not self.hasPardon:
  195. self.say('抱歉,刚刚没听清,能再说一遍吗?', completed=lambda: self.response(self.getSnowBoy().listen()))
  196. self.hasPardon = True
  197. else:
  198. self.say('没听清呢')
  199. self.hasPardon = False