def __init__(self, connectionManager, logger=None): """ * connectionManager -- An instance of the ConnectionManager * logger -- The LogData object """ self.__dict__ = self.__shared_state # If plugins have not been loaded, then load them if getattr(self, "_plugins", False) == False: self._connectionManager = connectionManager # Get data pertaining to the directory containing plugins self.PluginsDirectory = Options.get(Sections.General, Ids.PluginsDir) self.PluginsDirectoryName = split(self.PluginsDirectory)[-1] # Create a logger if one was not given if logger is None: logger = LogData() self.log = logger.get("PluginManager") self._logger = logger self._pluginMap = {} self._options = Options() self.loadPlugins(self.PluginsDirectory) # The Response object waiting for a response from Siri self._response = None
def __init__(self, connectionManager, logger=None): ''' * connectionManager -- An instance of the ConnectionManager * logger -- The LogData object ''' self.__dict__ = self.__shared_state # If plugins have not been loaded, then load them if getattr(self, "_plugins", False) == False: self._connectionManager = connectionManager # Get data pertaining to the directory containing plugins self.PluginsDirectory = Options.get(Sections.General, Ids.PluginsDir) self.PluginsDirectoryName = split(self.PluginsDirectory)[-1] # Create a logger if one was not given if logger is None: logger = LogData() self.log = logger.get("PluginManager") self._logger = logger self._pluginMap = {} self._options = Options() self.loadPlugins(self.PluginsDirectory) # The Response object waiting for a response from Siri self._response = None
def main(): # Ensure that the pysiriproxy configuration files exist in the # user's home directory ensureConfigFiles() # Create a logger just for the options loading logger = LogData(LogLevel.DEBUG, 5) # Parse the configuration options object options = Options(logger) options.parse(argv[1:], Files.ConfigFile) # Determine if the user wants to generate certificates if options.getCl(Ids.GenCerts, False): # Generate certificates in the configuration directory genCerts(Directories.Config) else: # Grab the log level and debug level from the configured options logLevel = options.get(Sections.Logging, Ids.LogLevel) debugLevel = options.get(Sections.Logging, Ids.DebugLevel) # Use an empty prefix if the timestamp property is an empty string timestampFormat = options.get(Sections.Logging, Ids.Timestamp, "") if len(timestampFormat.strip()) == 0: timePrefix = Prefix() else: timePrefix = TimePrefix(timestampFormat) # Create a new logger using the loaded configuration settings logger = LogData(logLevel, debugLevel, prefix=timePrefix) # Start the SiriProxy server iphone.connect(logger) reactor.run()
class PluginManager: """The PluginManager is responsible for loading all of the available plugins as well as processing the object filters and speech rules for each of the loaded plugins. """ # Implement the borg pattern __shared_state = {} # The class name that all plugins must have PluginClassName = "Plugin" def __init__(self, connectionManager, logger=None): """ * connectionManager -- An instance of the ConnectionManager * logger -- The LogData object """ self.__dict__ = self.__shared_state # If plugins have not been loaded, then load them if getattr(self, "_plugins", False) == False: self._connectionManager = connectionManager # Get data pertaining to the directory containing plugins self.PluginsDirectory = Options.get(Sections.General, Ids.PluginsDir) self.PluginsDirectoryName = split(self.PluginsDirectory)[-1] # Create a logger if one was not given if logger is None: logger = LogData() self.log = logger.get("PluginManager") self._logger = logger self._pluginMap = {} self._options = Options() self.loadPlugins(self.PluginsDirectory) # The Response object waiting for a response from Siri self._response = None ##### Interacting with Siri ##### def showDirections(self, directionsType, source, destination, utterance=None): """Show the given type of directions between the two locations to the user. * directionsType -- The type of directions * source -- The source location * destination -- The destination location * utterance -- The utterance to speak """ server = Directions.From_Server connection = self._connectionManager.getConnection(server) if connection is not None: self.log.debug("Making directions [%s]" % directionsType, level=3) refId = connection.getRefId() directions = ResponseFactory.directions(refId, directionsType, source, destination, utterance=utterance) connection.injectObjectToOutputStream(directions) def showDrivingDirections(self, source, destination, utterance=None): """Show driving directions between the two locations to the user. * source -- The source location * destination -- The destination location * utterance -- The utterance to speak """ self.showDirections(DirectionTypes.Driving, source, destination, utterance=utterance) def showWalkingDirections(self, source, destination, utterance=None): """Show walking directions between the two locations to the user. * source -- The source location * destination -- The destination location * utterance -- The utterance to speak """ self.showDirections(DirectionTypes.Walking, source, destination, utterance=utterance) def showPublicTransitDirections(self, source, destination, utterance=None): """Show public transportation directions between the two locations to the user. * source -- The source location * destination -- The destination location * utterance -- The utterance to speak """ self.showDirections(DirectionTypes.PublicTransit, source, destination, utterance=utterance) def makeView(self, views): """Create a view and send it to the iPhone. * views -- The list of views to create """ server = Directions.From_Server connection = self._connectionManager.getConnection(server) if connection is not None: self.log.debug("Making view", level=3) refId = connection.getRefId() view = ResponseFactory.view(refId, views) connection.injectObjectToOutputStream(view) def ask(self, question, spoken=None): """Command Siri to ask the user a question. * question -- The question to ask * spoken -- The text Siri will say """ self.say(question, spoken, prompt=True) # Complete the request, otherwise Siri will freeze, # but be sure not to reset the context self.completeRequest(resetContext=False) def completeRequest(self, refId=None, resetContext=True): """Complete a request to Siri. * refId -- The reference ID """ server = Directions.From_Server connection = self._connectionManager.getConnection(server) if connection is not None: self.log.debug("Sending Request Completed", level=3) refId = connection.getRefId() if refId is None else refId completed = ResponseFactory.requestCompleted(refId) connection.injectObjectToOutputStream(completed) # Reset the connection context if resetContext: self._connectionManager.resetConnections() def resetContext(self): """Reset the context.""" self._connectionManager.resetConnections() # Clear the current plugin that is waiting for a response if self._response is not None: self._response.close() self._response = None def say(self, text, spoken=None, prompt=False, refId=None): """Command Siri to speak a piece of text. * text -- The text that Siri will display * spoken -- The text that Siri will speak * prompt -- True to have Siri prompt for a response """ server = Directions.From_Server connection = self._connectionManager.getConnection(server) if connection is not None: self.log.debug("Saying:", level=3, text=text, spoken=spoken, prompt=prompt) refId = connection.getRefId() if refId is None else refId # Create the utterance utterance = ResponseFactory.utterance(refId, text, spoken, prompt) connection.injectObjectToOutputStream(utterance) ##### Plugin processing functions ##### def processFilters(self, obj, direction): """Process all the plugin filters for this object and data direction. * obj -- The object * direction -- The data direction """ response = None try: response = self.__processFilters(obj, direction) except: self.log.error("Failed processing filters") self.log.error(getStackTrace()) # Determine if the response should be used if response is not None: # If the response is False, then the object should be dropped # If the object has the same class as the original object, then # it should be returning if response == False: obj = None elif response.get("class") == obj.get("class"): obj = response return obj def processSpeechRules(self, text): """Process all the plugin speech rules for this recognized text. * text -- The recognized text """ try: # Speech rules return True to indicate that the response from # Apple's server should be overriden. The speech rules return # False to indicate that the response from Apple's server should # be used. return self.__processSpeechRules(text) except: self.log.error("Failed processing speech rules") self.log.error(getStackTrace()) # Have Siri respond with the the error response self.say(self._options.get(Sections.Responses, Ids.ErrorResponse)) self.completeRequest() return True def loadPlugins(self, directory): """Load all of the plugins from the plugins directory. * directory -- The plugins directory """ self.__addPluginsToPath(directory) # Traverse through all of the plugins for filename in listdir(directory): if not filename.startswith("__") and filename.endswith(".py"): # Get the module name from the filename by removing the # file extension pluginName = splitext(filename)[0] try: self.log.debug("Loading plugin [%s]" % pluginName, level=10) # Get the plugin class, and create the plugin object pluginClass = self.__importPlugin(pluginName) plugin = pluginClass(self, self._logger) # Ensure that all plugins subclass the base plugin if isinstance(plugin, BasePlugin): # Force plugins to have unique names if plugin.name not in self._pluginMap: self._pluginMap[plugin.name] = plugin else: self.log.error( "Plugin in file [%s] has name " "[%s] which already exists!" % (pluginName, plugin.name) ) else: self.log.error("Plugin [%s] must be a subclass of " "the BasePlugin class!" % plugin.name) except: self.log.error("Failed to load plugin [%s]" % pluginName) self.log.error(getStackTrace()) continue ##### Private functions ##### def __processFilters(self, obj, direction): """Process all the plugin filters for this object and data direction. * obj -- The object * direction -- The data direction """ responses = [] for plugin in self._pluginMap.values(): response = plugin.processFilters(obj, direction) # Plugins return False to drop the packet, None to ignore # the packet, or an object to respond to the packet if response == False: return False elif response is not None: responses.append(response) # Now, we need to rank the responses by their corresponding scores # to determine the best response to push forward # @todo: for now return the first response....later rank by score retResponses = (responses + [None])[0] return retResponses def __processSpeechRules(self, text): """Process all the plugin speech rules for this recognized text. * text -- The recognized text """ # If a response is waiting, pass it the text if self._response is not None: self.log.debug("Calling yield response function", level=3) try: self._response.send(text) return False except StopIteration: # Get rid of the response once it is through yielding self._response = None return True for plugin in self._pluginMap.values(): # If any plugin returns True, then one of its speech rules # matched the given text, otherwise it has not been matched yet response = plugin.processSpeechRules(text) # If the speech rule returned True, then we are done, otherwise # it might have returned a response type if response == True: self.log.info("Plugin [%s] matched the recognized speech." % plugin.name) return True else: # Create the actual response type from the given response self._response = handleResponse(self, response) # Stop processing speech rules if one is waiting for a response if self._response is not None: self.log.info("Plugin [%s] matched the recognized " "speech." % plugin.name) break # None of the plugins had speech rules that applied to this text return False def __addPluginsToPath(self, directory): """Add the plugins directory to the path. * directory -- The plugins directory """ baseDirectory = split(directory)[0] # Only add the base directory (directory which contains the plugins # directory) to the path once if baseDirectory not in path: path.insert(0, baseDirectory) import plugins def __importPlugin(self, pluginName): """Import the plugin with the given name and load the given class. * pluginName -- The name of the plugin """ module = __import__(self.PluginsDirectoryName, fromlist=[pluginName]) pluginModule = getattr(module, pluginName) return getattr(pluginModule, self.PluginClassName)
class PluginManager: '''The PluginManager is responsible for loading all of the available plugins as well as processing the object filters and speech rules for each of the loaded plugins. ''' # Implement the borg pattern __shared_state = {} # The class name that all plugins must have PluginClassName = "Plugin" def __init__(self, connectionManager, logger=None): ''' * connectionManager -- An instance of the ConnectionManager * logger -- The LogData object ''' self.__dict__ = self.__shared_state # If plugins have not been loaded, then load them if getattr(self, "_plugins", False) == False: self._connectionManager = connectionManager # Get data pertaining to the directory containing plugins self.PluginsDirectory = Options.get(Sections.General, Ids.PluginsDir) self.PluginsDirectoryName = split(self.PluginsDirectory)[-1] # Create a logger if one was not given if logger is None: logger = LogData() self.log = logger.get("PluginManager") self._logger = logger self._pluginMap = {} self._options = Options() self.loadPlugins(self.PluginsDirectory) # The Response object waiting for a response from Siri self._response = None ##### Interacting with Siri ##### def showDirections(self, directionsType, source, destination, utterance=None): '''Show the given type of directions between the two locations to the user. * directionsType -- The type of directions * source -- The source location * destination -- The destination location * utterance -- The utterance to speak ''' server = Directions.From_Server connection = self._connectionManager.getConnection(server) if connection is not None: self.log.debug("Making directions [%s]" % directionsType, level=3) refId = connection.getRefId() directions = ResponseFactory.directions(refId, directionsType, source, destination, utterance=utterance) connection.injectObjectToOutputStream(directions) def showDrivingDirections(self, source, destination, utterance=None): '''Show driving directions between the two locations to the user. * source -- The source location * destination -- The destination location * utterance -- The utterance to speak ''' self.showDirections(DirectionTypes.Driving, source, destination, utterance=utterance) def showWalkingDirections(self, source, destination, utterance=None): '''Show walking directions between the two locations to the user. * source -- The source location * destination -- The destination location * utterance -- The utterance to speak ''' self.showDirections(DirectionTypes.Walking, source, destination, utterance=utterance) def showPublicTransitDirections(self, source, destination, utterance=None): '''Show public transportation directions between the two locations to the user. * source -- The source location * destination -- The destination location * utterance -- The utterance to speak ''' self.showDirections(DirectionTypes.PublicTransit, source, destination, utterance=utterance) def makeView(self, views): '''Create a view and send it to the iPhone. * views -- The list of views to create ''' server = Directions.From_Server connection = self._connectionManager.getConnection(server) if connection is not None: self.log.debug("Making view", level=3) refId = connection.getRefId() view = ResponseFactory.view(refId, views) connection.injectObjectToOutputStream(view) def ask(self, question, spoken=None): '''Command Siri to ask the user a question. * question -- The question to ask * spoken -- The text Siri will say ''' self.say(question, spoken, prompt=True) # Complete the request, otherwise Siri will freeze, # but be sure not to reset the context self.completeRequest(resetContext=False) def completeRequest(self, refId=None, resetContext=True): '''Complete a request to Siri. * refId -- The reference ID ''' server = Directions.From_Server connection = self._connectionManager.getConnection(server) if connection is not None: self.log.debug("Sending Request Completed", level=3) refId = connection.getRefId() if refId is None else refId completed = ResponseFactory.requestCompleted(refId) connection.injectObjectToOutputStream(completed) # Reset the connection context if resetContext: self._connectionManager.resetConnections() def resetContext(self): '''Reset the context.''' self._connectionManager.resetConnections() # Clear the current plugin that is waiting for a response if self._response is not None: self._response.close() self._response = None def say(self, text, spoken=None, prompt=False, refId=None): '''Command Siri to speak a piece of text. * text -- The text that Siri will display * spoken -- The text that Siri will speak * prompt -- True to have Siri prompt for a response ''' server = Directions.From_Server connection = self._connectionManager.getConnection(server) if connection is not None: self.log.debug("Saying:", level=3, text=text, spoken=spoken, prompt=prompt) refId = connection.getRefId() if refId is None else refId # Create the utterance utterance = ResponseFactory.utterance(refId, text, spoken, prompt) connection.injectObjectToOutputStream(utterance) ##### Plugin processing functions ##### def processFilters(self, obj, direction): '''Process all the plugin filters for this object and data direction. * obj -- The object * direction -- The data direction ''' response = None try: response = self.__processFilters(obj, direction) except: self.log.error("Failed processing filters") self.log.error(getStackTrace()) # Determine if the response should be used if response is not None: # If the response is False, then the object should be dropped # If the object has the same class as the original object, then # it should be returning if response == False: obj = None elif response.get('class') == obj.get('class'): obj = response return obj def processSpeechRules(self, text): '''Process all the plugin speech rules for this recognized text. * text -- The recognized text ''' try: # Speech rules return True to indicate that the response from # Apple's server should be overriden. The speech rules return # False to indicate that the response from Apple's server should # be used. return self.__processSpeechRules(text) except: self.log.error("Failed processing speech rules") self.log.error(getStackTrace()) # Have Siri respond with the the error response self.say(self._options.get(Sections.Responses, Ids.ErrorResponse)) self.completeRequest() return True def loadPlugins(self, directory): '''Load all of the plugins from the plugins directory. * directory -- The plugins directory ''' self.__addPluginsToPath(directory) # Traverse through all of the plugins for filename in listdir(directory): if not filename.startswith("__") and filename.endswith(".py"): # Get the module name from the filename by removing the # file extension pluginName = splitext(filename)[0] try: self.log.debug("Loading plugin [%s]" % pluginName, level=10) # Get the plugin class, and create the plugin object pluginClass = self.__importPlugin(pluginName) plugin = pluginClass(self, self._logger) # Ensure that all plugins subclass the base plugin if isinstance(plugin, BasePlugin): # Force plugins to have unique names if plugin.name not in self._pluginMap: self._pluginMap[plugin.name] = plugin else: self.log.error("Plugin in file [%s] has name " \ "[%s] which already exists!" % \ (pluginName, plugin.name)) else: self.log.error("Plugin [%s] must be a subclass of " \ "the BasePlugin class!" % \ plugin.name) except: self.log.error("Failed to load plugin [%s]" % pluginName) self.log.error(getStackTrace()) continue ##### Private functions ##### def __processFilters(self, obj, direction): '''Process all the plugin filters for this object and data direction. * obj -- The object * direction -- The data direction ''' responses = [] for plugin in self._pluginMap.values(): response = plugin.processFilters(obj, direction) # Plugins return False to drop the packet, None to ignore # the packet, or an object to respond to the packet if response == False: return False elif response is not None: responses.append(response) # Now, we need to rank the responses by their corresponding scores # to determine the best response to push forward # @todo: for now return the first response....later rank by score retResponses = (responses + [None])[0] return retResponses def __processSpeechRules(self, text): '''Process all the plugin speech rules for this recognized text. * text -- The recognized text ''' # If a response is waiting, pass it the text if self._response is not None: self.log.debug("Calling yield response function", level=3) try: self._response.send(text) return False except StopIteration: # Get rid of the response once it is through yielding self._response = None return True for plugin in self._pluginMap.values(): # If any plugin returns True, then one of its speech rules # matched the given text, otherwise it has not been matched yet response = plugin.processSpeechRules(text) # If the speech rule returned True, then we are done, otherwise # it might have returned a response type if response == True: self.log.info("Plugin [%s] matched the recognized speech." % plugin.name) return True else: # Create the actual response type from the given response self._response = handleResponse(self, response) # Stop processing speech rules if one is waiting for a response if self._response is not None: self.log.info("Plugin [%s] matched the recognized " \ "speech." % plugin.name) break # None of the plugins had speech rules that applied to this text return False def __addPluginsToPath(self, directory): '''Add the plugins directory to the path. * directory -- The plugins directory ''' baseDirectory = split(directory)[0] # Only add the base directory (directory which contains the plugins # directory) to the path once if baseDirectory not in path: path.insert(0, baseDirectory) import plugins def __importPlugin(self, pluginName): '''Import the plugin with the given name and load the given class. * pluginName -- The name of the plugin ''' module = __import__(self.PluginsDirectoryName, fromlist=[pluginName]) pluginModule = getattr(module, pluginName) return getattr(pluginModule, self.PluginClassName)