def search(self, name): """Perform a search through each of the provided search urls for the given name. Return a list of .torrent files. """ results = [] for url in self.search_urls.values(): try: page = urllib.urlopen(url.format(name)).read() soup = BeautifulSoup(page) table = soup.find("table").findAll("tr") # soup.findAll(attrs={'class':"vertTh"}) for result in table: if str(result) != "\n" and not result.attrs: vals = result.findAll("td") if len(vals) == 4: seeders = int(vals[2].contents[0]) leechers = int(vals[3].contents[0]) info = vals[1].findNext("a") # title = dict(info.attrs)['title'] # details = "Details for " # if title.startswith(details): # title = title[len(details):] link = dict(info.findNext("a").attrs)["href"] results.append((seeders, link)) except Exception as err: logger.log("Error performing torrent search", err) continue top = None try: top = max(results)[1] except ValueError: pass return top
def search(self, name): """Perform a search through each of the provided search urls for the given name. Return a list of .torrent files. """ results = [] for url in self.search_urls.values(): try: page = urllib.urlopen(url.format(name)).read() soup = BeautifulSoup(page) table = soup.find('table').findAll('tr') #soup.findAll(attrs={'class':"vertTh"}) for result in table: if str(result) != "\n" and not result.attrs: vals = result.findAll('td') if len(vals) == 4: seeders = int(vals[2].contents[0]) leechers = int(vals[3].contents[0]) info = vals[1].findNext('a') #title = dict(info.attrs)['title'] #details = "Details for " #if title.startswith(details): # title = title[len(details):] link = dict(info.findNext('a').attrs)['href'] results.append((seeders, link)) except Exception as err: logger.log("Error performing torrent search", err) continue top = None try: top = max(results)[1] except ValueError: pass return top
def fire(self, *args, **kargs): """Execute all callables registered to this event.""" for handler in self.handlers: try: handler(*args, **kargs) except Exception as err: # pylint: disable-msg=W0703 # We don't want a rogue handler to mess up everyone else logger.log("Exception encountered during event handling", err)
def __init__(self, registrar): super(Weather, self).__init__(registrar) self.ttl = 3600 # seconds self.last = None # last weather value recorded, tuple of (location, value) settings = ConfigParser.SafeConfigParser() settings.read(utils.get_plugin_settings_paths(__name__)) try: self.api_key = settings.get("Settings", "wunderground_api_key") except ConfigParser.Error: raise plugin.PluginLoadError("Must provide " "wunderground_api_key in Settings.") self.format = 'F' try: scale = settings.get("Settings", "temp_scale").upper() if scale in ('F', 'C', 'K'): self.format = scale except ConfigParser.Error: pass self.max_cache_distance = 25 try: self.max_cache_distance = int( settings.get("Settings", "max_cache_distance")) except (ConfigParser.Error, TypeError): pass self.CacheItem = namedtuple('CacheItem', ['expires', 'val']) self.locations = {} try: self.locations = dict( (key, value) for key, value in settings.items("Aliases")) except ConfigParser.NoSectionError: logger.log("No section 'Aliases' found in weather config file.") # remove spaces after commas - these would screw up lat/long searches for loc in self.locations: self.locations[loc] = re.sub(',\s*', ',', self.locations[loc]) # dict of location:CacheItem pairs # if current time in sec since epoch is less than ttl, cache has expired # should return val if cache is still valid self.cache = {} grammar = { "when": ["for", "be like"], "where": ["at", "near", "in", 'for'], } registrar.register_service("weather", self.execute, grammar=grammar, usage=""" USAGE: %s [location|alias] Returns the weather for a provided location or alias. If no location is provided, gets the weather data for the last recorded position. """, namespace=__name__)
def __init__(self, registrar): super(Weather, self).__init__(registrar) self.ttl = 3600 # seconds self.last = None # last weather value recorded, tuple of (location, value) settings = ConfigParser.SafeConfigParser() settings.read(utils.get_plugin_settings_paths(__name__)) try: self.api_key = settings.get("Settings", "wunderground_api_key") except ConfigParser.Error: raise plugin.PluginLoadError("Must provide " "wunderground_api_key in Settings.") self.format = 'F' try: scale = settings.get("Settings", "temp_scale").upper() if scale in ('F', 'C', 'K'): self.format = scale except ConfigParser.Error: pass self.max_cache_distance = 25 try: self.max_cache_distance = int(settings.get("Settings", "max_cache_distance")) except (ConfigParser.Error, TypeError): pass self.CacheItem = namedtuple('CacheItem', ['expires', 'val']) self.locations = {} try: self.locations = dict((key, value) for key, value in settings.items("Aliases")) except ConfigParser.NoSectionError: logger.log("No section 'Aliases' found in weather config file.") # remove spaces after commas - these would screw up lat/long searches for loc in self.locations: self.locations[loc] = re.sub(',\s*', ',', self.locations[loc]) # dict of location:CacheItem pairs # if current time in sec since epoch is less than ttl, cache has expired # should return val if cache is still valid self.cache = {} grammar = { "when": ["for", "be like"], "where": ["at","near","in", 'for'], } registrar.register_service("weather", self.execute, grammar=grammar, usage=""" USAGE: %s [location|alias] Returns the weather for a provided location or alias. If no location is provided, gets the weather data for the last recorded position. """, namespace=__name__)
def begin_torrent(self, movie_name): """Attempt to start a torrent program to begin download of the torrent.""" filename = os.path.join(self.settings.get("Downloads", "download_dir"), os.path.basename(movie_name)) try: urllib.urlretrieve(movie_name, filename) except IOError as err: logger.log("Error downloading torrent:", err) raise Exception("Error downloading torrent.") out = sp.call(["transmission-remote", "-a {0}".format(filename)]) succ = out.lower().find("success") > 0 if not succ: logger.log(out) return succ
def execute(self, **kwargs): """Insert memo text as a new line in the memo file. Keyword arguments: memo -- the text to insert into the memo file """ if "memo" in kwargs: try: with open(self.settings.get('Settings', 'memo_file'), "a") as fil: fil.write(kwargs["memo"] + "\n") return "Inserted into memo file." except IOError, err: logger.log("Error while writing to file", err) raise plugin.UnsuccessfulExecution("Error while writing to file.")
def registerClient(self, appname=None, publish_url=None): """ Register a client service with the server. Calculate a UUID that will identify which plugins are loaded for each client service and return it to the caller. """ ident = str(uuid.uuid1()) while ident in self.client_manager.registered_clients: ident = str(uuid.uuid1()) if appname is not None: ident = re.sub('[\W_]+', '', appname) + '-' + ident logger.log("Registering client {0}".format(ident)) self.client_manager.add_client(ident, publish_url=publish_url) return ident
def execute(self, **kwargs): """Download and start the provided media. Downloads the link with the highest seed count. Keyword arguments: file -- the .torrent file to find and download name -- either an IMDB link to the media or the media name to search for """ if not self.settings.has_option("Downloads", "download_dir"): raise plugin.UnsuccessfulExecution( "Could not continue - " "no download directory in settings.") if "file" in kwargs: tfile = kwargs["file"] movie_name = "torrent" elif "name" in kwargs: movie_name = kwargs["name"] if movie_name.startswith("http://www.imdb.com/"): try: response = urllib2.urlopen(movie_name) data = response.read() match = re.search("<title>(.*?)</title>", data) if match: title_extra = " - IMDb" movie_name = match.group(1) if movie_name.endswith(title_extra): movie_name = movie_name[0:len(movie_name) - len(title_extra)] else: return "Error finding IMDB movie title." except urllib2.URLError: return "Error loading IMDB link." tfile = self.search(movie_name) if not tfile: return "Could not find any torrents." else: raise plugin.UnsuccessfulExecution("No media name provided.") try: if self.begin_torrent(tfile): return "Now downloading {0}".format(movie_name) else: return "Torrent downloaded, but could not be started." except Exception as err: logger.log("Error starting torrent", err) raise plugin.UnsuccessfulExecution("Error starting torrent.")
def unregisterClient(self, clientid): """Unregister a client service from the server. Any further use of its clientid will result in a ServiceNotRegisteredError. Arguments: clientid -- a unique id assigned to the client when registering Raises: ClientNotRegisteredError """ if clientid not in self.client_manager.registered_clients: raise exceptions.ClientNotRegisteredError() logger.log("Unregistering client {0}".format(clientid)) del self.client_manager.registered_clients[clientid]
def _executionthread(self): """Continually poll the queue for tasks to be run.""" while True: expire, (command, namespace, args) = self.remove_task_if_past() if command is not None: val = None try: val = self.registrar.request_service( command, namespace=namespace, argdict=args) except Exception as err: val = "Exception encountered: {0}".format(err) logger.log("Scheduled command {0} has been run, with " "return value: \"{1}\"".format(command, val)) else: twait = max((expire - datetime.datetime.now()).total_seconds(), 0) self.event.wait(twait) self.event.clear()
def handle_error(self): import sys, traceback exc_type, exc_value, exc_traceback = sys.exc_info() cherrypy.response.status = 500 logger.log(''.join(traceback.format_exception( exc_type, exc_value, exc_traceback))) cherrypy.response.headers['Content-Type'] = 'application/json' try: cherrypy.response.body = [''.join(self.make_error_dict( 500, "Unexpected exception occurred during execution.", kind=exc_type.__name__))] except Exception as err: print err err = cherrypy._cperror.bare_error() status = err[0] message = '\n'.join(err[2]) cherrypy.response.body = [''.join(self.make_error_dict(status, message))]
def execute(self, **kwargs): """Insert memo text as a new line in the memo file. Keyword arguments: memo -- the text to insert into the memo file """ if "memo" in kwargs: try: with open(self.settings.get('Settings', 'memo_file'), "a") as fil: fil.write(kwargs["memo"] + "\n") return "Inserted into memo file." except IOError, err: logger.log("Error while writing to file", err) raise plugin.UnsuccessfulExecution( "Error while writing to file.")
def interpret(self, arg='', **kwargs): """Retrieve directions from Google Maps and format them in a human-readable manner. Keyword arguments: to -- the destination address from -- the source address (default: current location, if available) """ if "to" in kwargs: destination = kwargs["to"] else: destination = (yield "Please provide a destination.")["_raw"] origin = kwargs.get("from", '') if origin == '': try: origin = self.registrar.request_service( *self.registrar.find_best_service('location')) except Exception as error: logger.log("Could not determine location", error) # raise plugin.UnsuccessfulExecution("Could not determine location.") origin = (yield "Could not automatically determine your location. " "Where are you starting from?")["_raw"] params = { 'q': 'from:{0} to:{1}'.format(origin, destination), 'output': 'json', 'oe': 'utf8', } encoded_params = urllib.urlencode(params) url = 'http://maps.google.com/maps/nav?' + encoded_params request = urllib2.Request(url) resp = urllib2.urlopen(request) response = json.load(resp) status_code = response['Status']['code'] if status_code == 200: yield self.format_directions_for_human( response['Directions']['Routes'][0]['Steps']) return elif status_code == 602: raise plugin.UnsuccessfulExecution('malformed query') raise plugin.UnsuccessfulExecution("Unknown status code: " + status_code)
def __init__(self, withgui=False): self.withgui = withgui self.registrar = registrar.Registrar() # A dictionary mapping clientids to registered plugins self.client_manager = clientmanager.ClientManager() # pylint: disable-msg=C0103 self.LoadedPlugin = namedtuple('LoadedPlugin', ['obj', 'lock']) self.loaded_plugins = {} # Update __init__.py in the automaton package when a new plugin is added for plugin in automaton.plugins.__all__: try: self.enablePlugin(plugin) except Exception as e: # pylint: disable-msg=W0703 logger.log("Error loading module {0}.".format(plugin), e)
def execute(self, **kwargs): """Download and start the provided media. Downloads the link with the highest seed count. Keyword arguments: file -- the .torrent file to find and download name -- either an IMDB link to the media or the media name to search for """ if not self.settings.has_option("Downloads", "download_dir"): raise plugin.UnsuccessfulExecution("Could not continue - " "no download directory in settings.") if "file" in kwargs: tfile = kwargs["file"] movie_name = "torrent" elif "name" in kwargs: movie_name = kwargs["name"] if movie_name.startswith("http://www.imdb.com/"): try: response = urllib2.urlopen(movie_name) data = response.read() match = re.search("<title>(.*?)</title>", data) if match: title_extra = " - IMDb" movie_name = match.group(1) if movie_name.endswith(title_extra): movie_name = movie_name[0 : len(movie_name) - len(title_extra)] else: return "Error finding IMDB movie title." except urllib2.URLError: return "Error loading IMDB link." tfile = self.search(movie_name) if not tfile: return "Could not find any torrents." else: raise plugin.UnsuccessfulExecution("No media name provided.") try: if self.begin_torrent(tfile): return "Now downloading {0}".format(movie_name) else: return "Torrent downloaded, but could not be started." except Exception as err: logger.log("Error starting torrent", err) raise plugin.UnsuccessfulExecution("Error starting torrent.")
def disallowService(self, clientid, name): """Unregister a service from being used with a client. Arguments: clientid -- a unique id assigned to the client when registering name -- the name of the service to enable Raises: ClientNotRegisteredError ServiceNotLoadedException """ name = name.lower() client = self.get_client(clientid) if name not in client.plugins: raise exceptions.ServiceNotRegisteredError(name) logger.log("Removing service {0} for client {1}".format(name, clientid)) client.plugins.remove(name)
def _executionthread(self): """Continually poll the queue for tasks to be run.""" while True: expire, (command, namespace, args) = self.remove_task_if_past() if command is not None: val = None try: val = self.registrar.request_service(command, namespace=namespace, argdict=args) except Exception as err: val = "Exception encountered: {0}".format(err) logger.log("Scheduled command {0} has been run, with " "return value: \"{1}\"".format(command, val)) else: twait = max((expire - datetime.datetime.now()).total_seconds(), 0) self.event.wait(twait) self.event.clear()
def start(self): """Start the server. Ensures any networking is done in a separate thread from the UI. """ if self.withgui: try: import gtk import automaton.lib.ui as ui ui.StatusIcon(self) thread = threading.Thread(target=self._start) thread.setDaemon(True) thread.start() gtk.gdk.threads_init() gtk.main() return except ImportError: logger.log("gtk toolkit not present, so no graphical " "user interface will be available.") self._start()
def allowService(self, clientid, name): """Register a service for use by a client. Arguments: clientid -- a unique id assigned to the client when registering name -- the name of the service to enable Raises: ClientNotRegisteredError ServiceNotLoadedException """ name = name.lower() client = self.get_client(clientid) if name not in self.registrar.services: raise exceptions.ServiceNotProvidedError(name) if name not in client.plugins: logger.log("Adding service {0} for client {1}".format(name, clientid)) client.plugins.add(name)
def __init__(self, registrar): super(Schedule, self).__init__(registrar) self.settings = ConfigParser.SafeConfigParser() self.settings.read(utils.get_plugin_settings_paths(__name__)) if not self.settings.has_option("Settings", "queue_file"): self.settings.set("Settings", "queue_file", autoplatform.get_existing_file("schedule.queue")) else: if not os.access(self.settings.get("Settings", "queue_file"), os.W_OK): self.settings.set("Settings", "queue_file", None) self.queue = PersistentPriorityQueue( storagefile=self.settings.get("Settings", "queue_file")) if (not self.settings.has_option("Settings", "queue_file") or self.settings.get("Settings", "queue_file") is None): logger.log( "Scheduler could not find an existing queue file and " "no write access to create a new one. Any scheduled tasks " "will disappear once server is stopped.") self.event = threading.Event() thread = threading.Thread(target=self._executionthread) thread.setDaemon(True) thread.start() self.registrar.register_service( "schedule", self.execute, grammar={ "at": ["at"], "in": ["in"], "command": [], }, usage= ("USAGE: schedule WHAT [at WHEN] | [in WHEN]\n" "Schedules a command to be run at a specific time, repeating if\n" "necessary."), namespace=__name__)
def __init__(self, registrar): super(Music, self).__init__(registrar) settings = ConfigParser.SafeConfigParser() settings.read(utils.get_plugin_settings_paths(__name__)) hostname = "localhost" port = 6600 try: hostname = settings.get("MPD", "hostname") port = settings.get("MPD", "port") except ConfigParser.NoSectionError: raise plugin.PluginLoadError("'MPD' section in config does not exist.") except ConfigParser.NoOptionError as err: if err.section == "hostname": logger.log("MPD hostname not in settings. " "Using default of '{0}'".format(hostname)) elif err.section == "port": logger.log("MPD port not in settings. " "Using default of '{0}'".format(port)) except TypeError: logger.log("Error loading settings for MPD. Using host and port " "{0}:{1}.".format(hostname, port)) self.client = mpd.MPDClient() self.client.connect(hostname, port) self.version = tuple(int(i) for i in self.client.mpd_version.split('.')) registrar.register_service("play", self.play, {"target": []}, namespace=__name__) registrar.register_service("listen", self.play, {"target": ["listen to"]}, namespace=__name__) registrar.register_service("pause", self.pause, {}, namespace=__name__) registrar.register_service("stop", self.stop, {}, namespace=__name__) registrar.register_service("song", self.song, { "next": ["next"], "previous": ["previous"], }, namespace=__name__)
def handle_error(self): import sys, traceback exc_type, exc_value, exc_traceback = sys.exc_info() cherrypy.response.status = 500 logger.log(''.join( traceback.format_exception(exc_type, exc_value, exc_traceback))) cherrypy.response.headers['Content-Type'] = 'application/json' try: cherrypy.response.body = [ ''.join( self.make_error_dict( 500, "Unexpected exception occurred during execution.", kind=exc_type.__name__)) ] except Exception as err: print err err = cherrypy._cperror.bare_error() status = err[0] message = '\n'.join(err[2]) cherrypy.response.body = [ ''.join(self.make_error_dict(status, message)) ]
def __init__(self, registrar): super(Schedule, self).__init__(registrar) self.settings = ConfigParser.SafeConfigParser() self.settings.read(utils.get_plugin_settings_paths(__name__)) if not self.settings.has_option("Settings", "queue_file"): self.settings.set("Settings", "queue_file", autoplatform.get_existing_file("schedule.queue")) else: if not os.access(self.settings.get("Settings", "queue_file"), os.W_OK): self.settings.set("Settings", "queue_file", None) self.queue = PersistentPriorityQueue( storagefile=self.settings.get("Settings", "queue_file")) if (not self.settings.has_option("Settings", "queue_file") or self.settings.get("Settings", "queue_file") is None): logger.log("Scheduler could not find an existing queue file and " "no write access to create a new one. Any scheduled tasks " "will disappear once server is stopped.") self.event = threading.Event() thread = threading.Thread(target=self._executionthread) thread.setDaemon(True) thread.start() self.registrar.register_service("schedule", self.execute, grammar={ "at": ["at"], "in": ["in"], "command": [], }, usage=("USAGE: schedule WHAT [at WHEN] | [in WHEN]\n" "Schedules a command to be run at a specific time, repeating if\n" "necessary."), namespace=__name__)
def reloadPlugin(self, name): """Disable and re-enable a plugin, reloading the code from source.""" if name in self.loaded_plugins: plugin = self.loaded_plugins[name] plugin.lock.acquire() try: cmd = reload(__import__('automaton.plugins.{0}'.format(name), fromlist=[name])) plugin.obj = getattr(cmd, name)() logger.log("Plugin {0} has been successfully reloaded.".format(name)) except Exception as err: self.disablePlugin(name) logger.log("Exception encountered reloading {0}. " "Plugin disabled.".format(name), err) logger.log(name) finally: plugin.lock.release()
else: data = None cursor.close() return data try: s = serial.Serial("/dev/ttyUSB0", 2400) except serial.serialutil.SerialException: s = serial.Serial("/dev/ttyUSB1", 2400) op = {"DBHOST": "localhost", "DBUSER": "******", "DBPASS": ""} op.update(settings_loader.load_app_settings(sys.argv[0])) if op["DBPASS"] == "": logger.log("Error: no db password provided") sys.exit() try: db = pgdb.connect(user=op["DBUSER"], password=op["DBPASS"], host=op["DBHOST"], database="Automaton") except Exception as e: print "Error connecting to database:", e sys.exit() client = ClientWrapper.ClientWrapper(op["THRIFT_SERVER"]) client.open() try: client.registerPlugin("say") except exceptions.PluginNotLoadedException:
def gotBuddyList(self, l): self.activateSSI() self.setIdleTime(0) self.clientReady() logger.log("Client online.")
def clientConnectionFailed(self, connector, reason): logger.log("Connection failed: " + str(reason)) protocol.ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
def clientConnectionLost(self, connector, reason): logger.log("Lost connection: " + str(reason)) if reason.check([exceptions.ClientError]): reactor.stop()
'MASTER': '', 'THRIFT_SERVER': 'tails.local' } settings = ConfigParser.SafeConfigParser() settings.read(utils.get_app_settings_paths("AIM")) try: if not settings.has_option("AIM", "oscar_host"): settings.set("AIM", "oscar_host", "login.oscar.aol.com") if not settings.has_option("AIM", "oscar_port"): settings.set("AIM", "oscar_port", "5190") if not (settings.has_option("AIM", "username") and settings.has_option("AIM", "password") and settings.has_option("AIM", "master")): logger.log("Missing necessary credentials to log in to AIM.") sys.exit() except ConfigParser.NoSectionError as err: print "AIM config file has no section '{0}'".format(err.section) class B(oscar.BOSConnection): capabilities = [oscar.CAP_CHAT] def __init__(self, s, p, f): self.factory = f oscar.BOSConnection.__init__(self, s, p) def initDone(self): self.requestSelfInfo().addCallback(self.gotSelfInfo) self.requestSSI().addCallback(self.gotBuddyList)
def execute(self, **kwargs): """Retrieve the latest weather for the provided location for the given time. Keyword arguments: last -- if True, return the last retrieved weather data where -- the location to get weather data for (defaults to current location) Can be an alias (defined in the configuration file) when -- one of 'today' or 'tomorrow' (default 'today') """ if kwargs.get("last", None): if self.last: if time.time() < self.last[1].ttl: yield "Data for {0}: {1}".format(self.last[0], self.last[1].val) return else: self.last = None raise plugin.UnsuccessfulExecution("No recent data stored.") if "where" not in kwargs: try: # Try to call on latitude plugin to get the current location kwargs["where"] = re.sub( "[() ]", "", self.registrar.request_service('location', noreverse=True)) except: #raise plugin.UnsuccessfulExecution("No current location is available.") kwargs["where"] = (yield "I could not find your current location, " "where do you want weather for?")["_raw"] forecastday = 0 if "when" in kwargs: if kwargs["when"].upper() == "TODAY": forecastday = 0 if kwargs["when"].upper() == "TOMORROW": forecastday = 1 location = kwargs["where"] # Sub any aliases if location in self.locations: location = self.locations[location] # Look in the cache, pick item with minimum distance from location, then # check to see if it's within the tolerance if len(self.cache) > 0: cache_hit_distances = ((item, self.distance(location, item)) for item in self.cache) closest_hit = min(cache_hit_distances, key=lambda x: x[1])[0] if closest_hit < self.max_cache_distance: if time.time() < self.cache[location].expires: yield self.cache[location].val return else: self.cache.pop(location, None) url = "http://api.wunderground.com/api/{0}/forecast".format( self.api_key) query = "/q/{0}.json".format(re.sub('\s+', '_', location)) resp = json.load(urllib2.urlopen(urllib2.Request(url + query))) if not resp: raise plugin.UnsuccessfulExecution("Parsing failed for location " + location) ret = None if 'results' in resp['response']: results = resp['response']['results'] while len(results) > 1: ret = "Did you mean " states = set() countries = set() for result in results: if result['state'] != '': states.add(result['state'].lower()) elif result['country'] != '': countries.add(result['country'].lower()) if len(states) > 0: ret += "the state" if len(states) > 1: ret += "s" ret += ' ' + utils.humanize_join( abbrev.states.try_long(x).capitalize() for x in states) if len(countries) > 0: if len(states) > 0: ret += " or " ret += "the country" if len(countries) > 1: ret += "s" ret += ' ' + utils.humanize_join( abbrev.countries.try_long(x).capitalize() for x in countries) ret += "?" # TODO better parsing of conversation arguments cont = (yield ret)['_raw'].split() old_results = results results = [] for word in cont: # Try to convert long name to abbrev state = abbrev.states.try_short(word) country = abbrev.countries.try_short(word) for result in old_results: if result['state'].lower() == state.lower(): results.append(result) elif result['country'].lower() == country.lower(): results.append(result) if len(results) == 0: # Invalid input was given, back up results = old_results query = results[0]['l'] + '.json' resp = json.load(urllib2.urlopen(urllib2.Request(url + query))) if not resp: raise plugin.UnsuccessfulExecution( "Parsing failed for location " + location) try: forecast = resp['forecast']['txt_forecast']['forecastday'] ret = forecast[forecastday]['fcttext'] except KeyError as err: logger.log("Error parsing forecast:", err) if not ret: raise plugin.UnsuccessfulExecution( "There is no weather data for this location") self.last = (location, self.CacheItem(time.time() + self.ttl, ret)) self.cache[location] = self.last[1] yield ret
def disallowAllServices(self, clientid): """Disallow all services for the given client id.""" logger.log("Removing all services for client {0}".format(clientid)) self.get_client(clientid).plugins = set()
'IMAP_PORT': 993, 'IMAP_USER': '', 'IMAP_PASSWORD': '', 'SMTP_SERVER': 'smtp.gmail.com', 'SMTP_PORT': 587, 'DBHOST": 'localhost', 'DBUSER": '******', 'DBPASS": '' } op.update(settings_loader.load_app_settings(sys.argv[0])) # Fail if authentication info is not present. for operator in ('IMAP_USER', 'IMAP_PASSWORD', 'DBPASS'): if op[operator] == '': log("Missing necessary credentials to log in to Textecute.") sys.exit() # Set SMTP authentication to the same as IMAP # if none has been provided. if not 'SMTP_USER' in op: op['SMTP_USER'] = op['IMAP_USER'] if not'SMTP_PASSWORD' in op: op['SMTP_PASSWORD'] = op['IMAP_PASSWORD'] try: db = pgdb.connect(user=op["DBUSER"], password=op["DBPASS"], host=op["DBHOST"], database="Automaton") except Exception as e: log("Error connecting to database: %s" % e) sys.exit()
def execute(self, **kwargs): """Retrieve the latest weather for the provided location for the given time. Keyword arguments: last -- if True, return the last retrieved weather data where -- the location to get weather data for (defaults to current location) Can be an alias (defined in the configuration file) when -- one of 'today' or 'tomorrow' (default 'today') """ if kwargs.get("last", None): if self.last: if time.time() < self.last[1].ttl: yield "Data for {0}: {1}".format(self.last[0], self.last[1].val) return else: self.last = None raise plugin.UnsuccessfulExecution("No recent data stored.") if "where" not in kwargs: try: # Try to call on latitude plugin to get the current location kwargs["where"] = re.sub("[() ]", "", self.registrar.request_service('location', noreverse=True)) except: #raise plugin.UnsuccessfulExecution("No current location is available.") kwargs["where"] = (yield "I could not find your current location, " "where do you want weather for?")["_raw"] forecastday = 0 if "when" in kwargs: if kwargs["when"].upper() == "TODAY": forecastday = 0 if kwargs["when"].upper() == "TOMORROW": forecastday = 1 location = kwargs["where"] # Sub any aliases if location in self.locations: location = self.locations[location] # Look in the cache, pick item with minimum distance from location, then # check to see if it's within the tolerance if len(self.cache) > 0: cache_hit_distances = ((item, self.distance(location, item)) for item in self.cache) closest_hit = min(cache_hit_distances, key=lambda x: x[1])[0] if closest_hit < self.max_cache_distance: if time.time() < self.cache[location].expires: yield self.cache[location].val return else: self.cache.pop(location, None) url = "http://api.wunderground.com/api/{0}/forecast".format(self.api_key) query = "/q/{0}.json".format(re.sub('\s+', '_', location)) resp = json.load(urllib2.urlopen(urllib2.Request(url + query))) if not resp: raise plugin.UnsuccessfulExecution( "Parsing failed for location " + location) ret = None if 'results' in resp['response']: results = resp['response']['results'] while len(results) > 1: ret = "Did you mean " states = set() countries = set() for result in results: if result['state'] != '': states.add(result['state'].lower()) elif result['country'] != '': countries.add(result['country'].lower()) if len(states) > 0: ret += "the state" if len(states) > 1: ret += "s" ret += ' ' + utils.humanize_join( abbrev.states.try_long(x).capitalize() for x in states) if len(countries) > 0: if len(states) > 0: ret += " or " ret += "the country" if len(countries) > 1: ret += "s" ret += ' ' + utils.humanize_join( abbrev.countries.try_long(x).capitalize() for x in countries) ret += "?" # TODO better parsing of conversation arguments cont = (yield ret)['_raw'].split() old_results = results results = [] for word in cont: # Try to convert long name to abbrev state = abbrev.states.try_short(word) country = abbrev.countries.try_short(word) for result in old_results: if result['state'].lower() == state.lower(): results.append(result) elif result['country'].lower() == country.lower(): results.append(result) if len(results) == 0: # Invalid input was given, back up results = old_results query = results[0]['l'] + '.json' resp = json.load(urllib2.urlopen(urllib2.Request(url + query))) if not resp: raise plugin.UnsuccessfulExecution( "Parsing failed for location " + location) try: forecast = resp['forecast']['txt_forecast']['forecastday'] ret = forecast[forecastday]['fcttext'] except KeyError as err: logger.log("Error parsing forecast:", err) if not ret: raise plugin.UnsuccessfulExecution( "There is no weather data for this location") self.last = (location, self.CacheItem(time.time() + self.ttl, ret)) self.cache[location] = self.last[1] yield ret
def allowAllServices(self, clientid): """Allow all services for the given clientid.""" logger.log("Allowing all services for client {0}".format(clientid)) self.get_client(clientid).plugins = set(self.registrar.services.keys())