rabin 6 anos atrás
pai
commit
0007d8e107
7 arquivos alterados com 147 adições e 54 exclusões
  1. 44 41
      service/brain.py
  2. 2 2
      service/ear.py
  3. 4 4
      service/mouth.py
  4. 79 0
      service/skill.py
  5. 1 0
      service/skill/__init__.py
  6. 14 0
      service/skill/echo.py
  7. 3 7
      vecan.py

+ 44 - 41
service/brain.py

@@ -11,84 +11,84 @@ class Brain(object):
 		self.memory = []
 		# 询问模式
 		self.hasPardon = False
-		# 插件
-		self.plugins = []
-		#self.plugins = plugin_loader.get_plugins(self.robot)
 		self.handling = False
 		return self
 
-	def isImmersive(self, plugin, text, parsed):
-		return self.robot.getImmersiveMode() == plugin.SLUG and \
-			plugin.isValidImmersive(text, parsed)
+	def isImmersive(self, skill, text, parsed):
+		return self.robot.getImmersiveMode() == skill.__name__ and \
+			skill.matchImmersive(text, parsed)
 
-	def wakeup(self):
-		plugin_list = []
-		for plugin in self.plugins:
-			plugin_list.append(plugin.SLUG)
-		self.robot.logger.info(self.log('已激活插件:{}'.format(plugin_list)))
+	def loadSkill(self):
+		self.skills = []
+		loadSkill = []
+		locations = [
+			File.path() + 'service/skill',
+			'~/.vecan/skill',
+		]
+		for skill in Demeter.getPath(locations):
+			skill = getattr(skill, skill.__name__.capitalize())
+			self.skills.append(skill)
+			loadSkill.append(skill.__name__)
 
-	def query(self, text):
-		"""
-		query 模块
+		self.robot.logger.info(self.log('载入技能:{}'.format(loadSkill)))
 
-		Arguments:
-		text -- 用户输入
-		"""
+	def wakeup(self):
+		self.loadSkill()
 
+	def query(self, text):
 		args = {
 			"service_id": "S13442",
-			"api_key": 'w5v7gUV3iPGsGntcM84PtOOM',
-			"secret_key": 'KffXwW6E1alcGplcabcNs63Li6GvvnfL'
 		}
 		parsed = self.robot.doParse(text, **args)
 		return parsed
 
-		for plugin in self.plugins:
-			if not plugin.isValid(text, parsed) and not self.isImmersive(plugin, text, parsed):
+		for skill in self.skills:
+			if not skill.match(text, parsed) and not self.isImmersive(skill, text, parsed):
 				continue
 
-			self.robot.logger.info(self.log("'{}' 命中技能 {}".format(text, plugin.SLUG)))
-			self.robot.matchPlugin = plugin.SLUG
+			skill.init(self.robot)
+			self.robot.logger.info(self.log("'{}' 命中技能 {}".format(text, skill.__name__)))
+			self.robot.matchskill = skill.__name__
 
-			if plugin.IS_IMMERSIVE:
-				self.robot.setImmersiveMode(plugin.SLUG)
+			if skill.IS_IMMERSIVE:
+				self.robot.setImmersiveMode(skill.__name__)
 
 			continueHandle = False
 			try:
 				self.handling = True
-				continueHandle = plugin.handle(text, parsed)
+				continueHandle = skill.handle(text, parsed)
 				self.handling = False				
 			except Exception:
-				self.robot.logger.critical('Failed to execute plugin',
+				self.robot.logger.critical('Failed to execute skill',
 								   exc_info=True)
-				reply = u"抱歉,插件{}出故障了,晚点再试试吧".format(plugin.SLUG)
-				self.robot.say(reply, plugin=plugin.SLUG)
+				reply = u"抱歉,插件{}出故障了,晚点再试试吧".format(skill.__name__)
+				self.robot.say(reply, skill=skill.__name__)
 			else:
 				self.robot.logger.debug(self.log("Handling of phrase '%s' by " +
-								   "plugin '%s' completed", text,
-								   plugin.SLUG))					
+								   "skill '%s' completed", text,
+								   skill.__name__))					
 			finally:
 				if not continueHandle:
 					return True
 
-		self.robot.logger.debug(self.log("No plugin was able to handle phrase {} ".format(text)))
+		self.robot.logger.debug(self.log("No skill was able to handle phrase {} ".format(text)))
 		return False
 
+	""" 恢复某个技能的处理 """
 	def restore(self):
-		""" 恢复某个技能的处理 """
 		if not self.robot.immersiveMode:
 			return
-		for plugin in self.plugins:
-			if plugin.SLUG == self.robot.immersiveMode and plugin.restore:
-				plugin.restore()
+		for skill in self.skills:
+			if skill.__name__ == self.robot.immersiveMode and skill.restore:
+				skill.restore()
 
+	""" 暂停某个技能的处理 """
 	def pause(self):
-		""" 暂停某个技能的处理 """
 		if not self.robot.immersiveMode:
 			return
-		for plugin in self.plugins:
-			if plugin.SLUG == self.robot.immersiveMode and plugin.pause:
-				plugin.pause()
+		for skill in self.skills:
+			if skill.__name__ == self.robot.immersiveMode and skill.pause:
+				skill.pause()
 
 	def understand(self, fp):
 		if self.robot and self.robot.tool['asr']:
@@ -119,7 +119,7 @@ class Brain(object):
 			msg = self.robot.tool['ai'].ai(query)
 			self.robot.say(msg, True, completed=self.robot.checkRestore)
 		else:
-			if lastImmersiveMode is not None and lastImmersiveMode != self.robot.matchPlugin:
+			if lastImmersiveMode is not None and lastImmersiveMode != self.robot.matchskill:
 				time.sleep(1)
 				if self.checkSpeeker():
 					self.robot.logger.debug(self.log('等说完再checkRestore'))
@@ -128,11 +128,13 @@ class Brain(object):
 					self.robot.logger.debug(self.log('checkRestore'))
 					self.robot.checkRestore()
 
+	# 询问
 	def doubt(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.robot.ear.listen()
 			self.think(query)
 
+	# 没听清
 	def pardon(self):
 		if not self.hasPardon:
 			self.robot.say('抱歉,刚刚没听清,能再说一遍吗?', completed=lambda: self.response(self.robot.ear.listen()))
@@ -159,6 +161,7 @@ class Brain(object):
 			urls = re.findall(url_pattern, text)
 			for url in urls:
 				text = text.replace(url, '<a href={} target="_blank">{}</a>'.format(url, url))
+			# 后续存入数据库,分为永久记忆、临时记忆、碎片记忆
 			self.memory.append({'type': t, 'text': text, 'time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), 'uid': uid})
 
 	def log(self, text):

+ 2 - 2
service/ear.py

@@ -18,7 +18,7 @@ class Ear(object):
 
 	# 语音唤醒模式
 	def wait(self):
-		self.robot.logger.info(self.log('唤醒模式'))
+		self.robot.logger.info(self.log('唤醒模式:喊我名字叫醒我吧,我听着呢'))
 		if self.detector is not None:
 			self.detector.terminate()
 
@@ -38,7 +38,7 @@ class Ear(object):
 
 	# listen
 	def listen(self, silent=False):
-		self.robot.logger.info(self.log('聆听模式'))
+		self.robot.logger.info(self.log('聆听模式:和我直接对话吧'))
 		try:
 			if not silent:
 				time.sleep(1)

+ 4 - 4
service/mouth.py

@@ -29,7 +29,7 @@ class Mouth(object):
 			self.robot.logger.info(self.log('内容包含URL,无法读取'))
 			return
 		voice = ''
-		self.robot.logger.info(self.log('播放语音 - ' + msg))
+		self.robot.logger.info(self.log('获取文字 - ' + msg))
 		if getCache(msg):
 			self.robot.logger.info(self.log('命中缓存,播放缓存语音'))
 			voice = getCache(msg)
@@ -45,11 +45,11 @@ class Mouth(object):
 			completed = lambda: self.robot.brain.doubt(msg)
 		self.speek(voice, not cache, completed=completed)
 
-	def speek(self, voice, delete=False, completed=None):
+	def speek(self, voice, delete=False, completed=None, volume=1):
 		if self.speaker == None:
 			self.speaker = Demeter.service('sox', 'player')
 			self.speaker.init(self)
-		self.speaker.play(voice, delete=delete, onCompleted=completed)
+		self.speaker.play(voice, delete=delete, onCompleted=completed, volume=volume)
 
 	def checkSpeeker(self):
 		return self.speaker is not None and self.speaker.is_playing()
@@ -58,7 +58,7 @@ class Mouth(object):
 	def close(self):
 		if self.checkSpeeker():
 			self.speaker.stop()
-			self.speaker = None
+			#self.speaker = None
 
 	def talk(self, fp, callback=None):
 		self.speek(self.robot.data + 'beep_lo.wav')

+ 79 - 0
service/skill.py

@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+from __future__ import division
+from .__load__ import *
+from abc import ABCMeta, abstractmethod
+
+class Skill(metaclass=ABCMeta):
+
+		isImmersive = False
+
+	def init(self, robot):
+		self.priority = 0
+		self.robot = robot
+
+	def play(self, src, delete=False, onCompleted=None, volume=1):
+		self.robot.mouth.speek(src, delete, onCompleted, volume)
+
+	def say(self, text, cache=False, onCompleted=None):
+		self.robot.mouth.say(text, cache=cache, plugin=self.__name__, onCompleted=onCompleted)
+
+	def activeListen(self, silent=False):
+		return self.robot.ear.listen(silent)
+
+	def clearImmersive(self):
+		self.robot.setImmersiveMode(None)
+
+	"""
+	是否适合由该插件处理
+
+	参数:
+	query -- 用户的指令字符串
+	parsed -- 用户指令经过 NLU 解析后的结果
+
+	返回:
+	True: 适合由该插件处理
+	False: 不适合由该插件处理
+	"""
+	@abstractmethod
+	def match(self, query, parsed):
+		return False
+
+	"""
+	处理逻辑
+
+	参数:
+	query -- 用户的指令字符串
+	parsed -- 用户指令经过 NLU 解析后的结果
+	"""
+	@abstractmethod
+	def handle(self, query, parsed):
+		pass
+
+	"""
+	是否适合在沉浸模式下处理,
+	仅适用于有沉浸模式的插件(如音乐等)
+	当用户唤醒时,可以响应更多指令集。
+	例如:“"上一首"、"下一首" 等
+	"""
+	def matchImmersive(self, query, parsed):
+		return False
+
+	"""
+	暂停当前正在处理的任务,
+	当处于该沉浸模式下且被唤醒时,
+	将自动触发这个方法,
+	可以用于强制暂停一个耗时的操作		
+	"""
+	def pause(self):
+		return
+
+	"""
+	恢复当前插件,
+	仅适用于有沉浸模式的插件(如音乐等)
+	当用户误唤醒或者唤醒进行闲聊后,
+	可以自动恢复当前插件的处理逻辑
+	"""
+	def restore(self):
+		return
+		
+		

+ 1 - 0
service/skill/__init__.py

@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-

+ 14 - 0
service/skill/echo.py

@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+from demeter.core import *
+from service import *
+print(__path__)
+class Echo(Skill):
+
+    def handle(self, text, parsed):
+        text = text.lower().replace('echo', '').replace(u'传话', '')
+        self.say(text, cache=False)
+
+    def match(self, text, parsed):
+        return any(word in text.lower() for word in ["echo", u"传话"])
+        
+        

+ 3 - 7
vecan.py

@@ -18,8 +18,8 @@ class Vecan(object):
 		self.robot = Demeter.service('robot')
 		self.robot.init(profiling=Demeter.option['profiling'], logout=Demeter.option['logout'])
 		#self.robot.say('亲爱的,我是神算终端机器人vecan,很高兴为你服务', True, completed=lambda: self.robot.wait())
-		self.robot.say('亲爱的,我是神算终端机器人vecan,很高兴为你服务', True)
-		self.robot.wait()
+		#self.robot.say('亲爱的,我是神算终端机器人vecan,很高兴为你服务', True)
+		#self.robot.wait()
 
 		Demeter.dog = WatchDog.init()
 
@@ -47,11 +47,7 @@ class Vecan(object):
 			Demeter.option['logout'] = True
 
 	def run(self):
-		try:
-			self.init()
-		except AttributeError:
-			self.robot.logger.error('初始化机器人功能失败')
-			pass
+		self.init()
 		signal.signal(signal.SIGINT, self.robot.sleep)
 		#Demeter.webInit('front')