def etym(self, irc, msg, args, etym): """queries etymonline.com for word etymology""" try: req = requests.get( "https://www.etymonline.com/word/{}".format(etym)) soup = bs4.BeautifulSoup(req.text, 'html.parser') word_soup = soup.find('div', {'class': 'word--C9UPa'}) if not word_soup: irc.reply("'{}' not found".format(etym)) return irc_reply = "" for word in word_soup: irc_reply += "{}: {} ".format(word.div.h1.text, word.div.section.text) log.info(irc_reply) irc.reply(irc_reply) except Exception as e: log.exception() irc.reply("Error! Send the log to Druid@Freenode")
def _runCommandFunction(self, irc, msg, command): """Run a command from message, as if command was sent over IRC.""" tokens = callbacks.tokenize(command) try: self.Proxy(irc.irc, msg, tokens) except Exception, e: log.exception('Uncaught exception in function called by MessageParser:')
def flush(): """Flushes all the registered flushers.""" for (i, f) in enumerate(flushers): try: f() except Exception, e: log.exception('Uncaught exception in flusher #%s (%s):', i, f)
def __init__(self, *args, **kwargs): IBugtracker.__init__(self, *args, **kwargs) self.lp = None # A word to the wise: # The Launchpad API is much better than the /+text interface we currently use, # it's faster and easier to get the information we need. # The current /+text interface is not really maintained by Launchpad and most, # or all, of the Launchpad developers hate it. For this reason, we are dropping # support for /+text in the future in favour of launchpadlib. # Terence Simpson (tsimpson) 2010-04-20 try: # Attempt to use launchpadlib, python bindings for the Launchpad API from launchpadlib.launchpad import Launchpad cachedir = os.path.join(conf.supybot.directories.data.tmp(), 'lpcache') if hasattr(Launchpad, 'login_anonymously'): self.lp = Launchpad.login_anonymously("Ubuntu Bots - Bugtracker", 'production', cachedir) else: #NOTE: Most people should have a launchpadlib new enough for .login_anonymously self.lp = Launchpad.login("Ubuntu Bots - Bugtracker", '', '', 'production', cahedir) except ImportError: # Ask for launchpadlib to be installed supylog.warning("Please install python-launchpadlib, the old interface is deprecated") except Exception: # Something unexpected happened self.lp = None supylog.exception("Unknown exception while accessing the Launchpad API")
def doPost(self, handler, path, form): if path != "/": self.send_response(404) self.send_header("Content-type", "text/plain") self.end_headers() self.wfile.write(b"404: page not found") return headers = { key.lower(): value for key, value in dict(self.headers).items() } try: asyncio.run(github.dispatch(headers, form)) self.send_response(200) self.send_header("Content-Type", "text/plain") self.end_headers() self.wfile.write(b"200: OK") except Exception: log.exception("Failed to handle GitHub event") self.send_response(403) self.send_header("Content-type", "text/plain") self.end_headers() self.wfile.write(b"403: failed processing event")
def flush(): """Flushes all the registered flushers.""" for (i, f) in enumerate(flushers): try: f() except Exception, e: log.exception("Uncaught exception in flusher #%s (%s):", i, f)
def get_bug(self, id): # This is still a little rough, but it works :) url = "%s/%d" % (self.url, id) try: raw = utils.web.getUrl("%s?format=tab" % url).decode('utf-8') except Exception as e: # Due to unreliable matching if '.' in self.name: supylog.exception(self.errget % (self.description, e, url)) return if 'HTTP Error 500' in str(e): raise BugNotFoundError raise BugtrackerError(self.errget % (self.description, e, url)) raw = raw.replace('\r\n', '\n') (headers, rest) = raw.split('\n', 1) headers = headers.strip().split('\t') rest = rest.strip().split('\t') title = status = package = severity = assignee = "" if "summary" in headers: title = rest[headers.index("summary")] if "status" in headers: status = rest[headers.index("status")] if "component" in headers: package = rest[headers.index("component")] if "severity" in headers: severity = rest[headers.index("severity")] elif "priority" in headers: severity = rest[headers.index("priority")] if "owner" in headers: assignee = rest[headers.index("owner")] return (id, package, title, severity, status, assignee, url, [], [])
def _loadPlugins(self, irc): self.log.info('Loading plugins (connecting to %s).', irc.network) alwaysLoadImportant = conf.supybot.plugins.alwaysLoadImportant() important = conf.supybot.commands.defaultPlugins.importantPlugins() for (name, value) in conf.supybot.plugins.getValues(fullNames=False): if irc.getCallback(name) is None: load = value() if not load and name in important: if alwaysLoadImportant: s = '%s is configured not to be loaded, but is being '\ 'loaded anyway because ' \ 'supybot.plugins.alwaysLoadImportant is True.' self.log.warning(s, name) load = True if load: # We don't load plugins that don't start with a capital # letter. if name[0].isupper() and not irc.getCallback(name): # This is debug because each log logs its beginning. self.log.debug('Loading %s.', name) try: m = plugin.loadPluginModule(name, ignoreDeprecation=True) plugin.loadPluginClass(irc, m) except callbacks.Error, e: # This is just an error message. log.warning(str(e)) except (plugins.NoSuitableDatabase, ImportError), e: s = 'Failed to load %s: %s' % (name, e) if not s.endswith('.'): s += '.' log.warning(s) except Exception, e: log.exception('Failed to load %s:', name)
def startServer(): """Starts the HTTP server. Shouldn't be called from other modules. The callback should be an instance of a child of SupyHTTPServerCallback.""" global http_servers addresses4 = [(4, (x, configGroup.port())) for x in configGroup.hosts4() if x != ''] addresses6 = [(6, (x, configGroup.port())) for x in configGroup.hosts6() if x != ''] http_servers = [] for protocol, address in (addresses4 + addresses6): try: server = SupyHTTPServer(address, protocol, SupyHTTPRequestHandler) except OSError as e: log.error( 'Failed to start HTTP server with protocol %s at address: %s', protocol, address, e) if e.args[0] == 98: log.error( 'This means the port (and address) is already in use by an ' 'other process. Either find the process using the port ' 'and stop it, or change the port configured in ' 'supybot.servers.http.port.') continue except: log.exception( "Failed to start HTTP server with protocol %s at address", protocol, address) continue Thread(target=server.serve_forever, name='HTTP Server').start() http_servers.append(server) log.info('Starting HTTP server: %s' % str(server))
def implementation(): try: source.update() except Exception as e: log.exception('Failed to update {}: {}'.format( source.NAME, e)) self._topic_callback()
def get_bug_new(self, id): #TODO: Rename this method to 'get_bug' try: bugdata = self.lp.bugs[id] if bugdata.private: raise BugtrackerError, "This bug is private" dup = bugdata.duplicate_of summary_prefix = '' # Used to made dups easier while dup: summary_prefix = 'duplicate for #%d ' % id bugdata = dup dup = bugdata.duplicate_of affected = bugdata.users_affected_count_with_dupes heat = bugdata.heat tasks = bugdata.bug_tasks if tasks.total_size != 1: tasks = list(tasks) try: tasks.sort(self._sort) taskdata = tasks[-1] except ValueError: tasks = [_ for _ in tasks if _.bug_target_name.endswith(u'(Ubuntu)')] if tasks: if len(tasks) != 1: try: tasks.sort(self._sort) taskdata = tasks[-1] except ValueError: taskdata = bugdata.bug_tasks[bugdata.bug_tasks.total_size - 1] else: taskdata = tasks[-1] else: taskdata = tasks[-1] else: taskdata = tasks[0] assignee = taskdata.assignee t = taskdata.bug_target_display_name #task name if assignee: # "Diaplay Name (Launchpad ID)" assignee = u"%s (%s)" % (assignee.display_name, assignee.name) else: assignee = '' except Exception, e: if type(e).__name__ == 'HTTPError': # messy, but saves trying to import lazr.restfulclient.errors.HTPError if e.response.status == 404: bugNo = e.content.split(None)[-1][2:-1] # extract the real bug number if bugNo != str(id): # A duplicate of a private bug, at least we know it exists raise BugtrackerError, 'Bug #%s is a duplicate of bug #%s, but it is private (%s/bugs/%s)' % (id, bugNo, self.url, bugNo) raise BugtrackerError, "Bug #%s (%s/bugs/%d) is private or doesn't exist" % (id, self.url, id) # Could be private, could just not exist supylog.exception("Error gathering bug data for %s bug #%d" % (self.description, id)) raise BugtrackerError, "Could not gather data from %s for bug #%s (%s/bugs/%s). The error has been logged" % (self.description, id, self.url, id) elif isinstance(e, KeyError): raise BugNotFoundError supylog.exception("Error gathering bug data for %s bug %d" % (self.description, id)) raise BugtrackerError, "Could not gather data from %s for bug #%s (%s/bugs/%s). The error has been logged" % (self.description, id, self.url, id)
def newf(*args, **kwargs): for x in xrange(0, 3): try: return f(*args, **kwargs) except Exception: log.exception('Shrinking URL failed. Trying again.') time.sleep(1) return f(*args, **kwargs)
def newf(*args, **kwargs): for x in range(0, 3): try: return f(*args, **kwargs) except Exception: log.exception('Shrinking URL failed. Trying again.') time.sleep(1) return f(*args, **kwargs)
def _email_callback(self, fileobj): try: email = parse_mail(fileobj) msg = get_message(email, new_queue=self.new_queue) if not msg: return txt = colourise(msg.for_irc()) # Simple flood/duplicate detection if txt in self.last_n_messages: return self.last_n_messages.insert(0, txt) self.last_n_messages = self.last_n_messages[:20] for channel in self.irc.state.channels: package_regex = self.registryValue( 'package_regex', channel, ) or 'a^' # match nothing by default package_match = re.search(package_regex, msg.package) maintainer_match = False maintainer_regex = self.registryValue( 'maintainer_regex', channel) if maintainer_regex: info = Maintainer().get_maintainer(msg.package) if info: maintainer_match = re.search(maintainer_regex, info['email']) if not package_match and not maintainer_match: continue distribution_regex = self.registryValue( 'distribution_regex', channel, ) if distribution_regex: if not hasattr(msg, 'distribution'): # If this channel has a distribution regex, don't # bother continuing unless the message actually has a # distribution. This filters security messages, etc. continue if not re.search(distribution_regex, msg.distribution): # Distribution doesn't match regex; don't send this # message. continue ircmsg = supybot.ircmsgs.privmsg(channel, txt) self.irc.queueMsg(ircmsg) except Exception as e: log.exception('Uncaught exception: %s ' % e)
def _loadPlugins(self, irc): self.log.info('Loading plugins (connecting to %s).', irc.network) alwaysLoadImportant = conf.supybot.plugins.alwaysLoadImportant() important = conf.supybot.commands.defaultPlugins.importantPlugins() for (name, value) in conf.supybot.plugins.getValues(fullNames=False): if irc.getCallback(name) is None: load = value() if not load and name in important: if alwaysLoadImportant: s = '%s is configured not to be loaded, but is being '\ 'loaded anyway because ' \ 'supybot.plugins.alwaysLoadImportant is True.' self.log.warning(s, name) load = True if load: # We don't load plugins that don't start with a capital # letter. if name[0].isupper() and not irc.getCallback(name): # This is debug because each log logs its beginning. self.log.debug('Loading %s.', name) try: m = plugin.loadPluginModule(name, ignoreDeprecation=True) plugin.loadPluginClass(irc, m) except callbacks.Error as e: # This is just an error message. log.warning(str(e)) except plugins.NoSuitableDatabase as e: s = 'Failed to load %s: no suitable database(%s).' % ( name, e) log.warning(s) except ImportError as e: e = str(e) if e.endswith(name): s = 'Failed to load {0}: No plugin named {0} exists.'.format( utils.str.dqrepr(name)) elif "No module named 'config'" in e: s = ( "Failed to load %s: This plugin may be incompatible " "with your current Python version. If this error is appearing " "with stock Supybot plugins, remove the stock plugins directory " "(usually ~/Limnoria/plugins) from 'config directories.plugins'." % name) else: s = 'Failed to load %s: import error (%s).' % ( name, e) log.warning(s) except Exception as e: log.exception('Failed to load %s:', name) else: # Let's import the module so configuration is preserved. try: _ = plugin.loadPluginModule(name) except Exception as e: log.debug('Attempted to load %s to preserve its ' 'configuration, but load failed: %s', name, e) world.starting = False
def _loadPlugins(self, irc): self.log.debug('Loading plugins (connecting to %s).', irc.network) alwaysLoadImportant = conf.supybot.plugins.alwaysLoadImportant() important = conf.supybot.commands.defaultPlugins.importantPlugins() for (name, value) in conf.supybot.plugins.getValues(fullNames=False): if irc.getCallback(name) is None: load = value() if not load and name in important: if alwaysLoadImportant: s = '%s is configured not to be loaded, but is being '\ 'loaded anyway because ' \ 'supybot.plugins.alwaysLoadImportant is True.' self.log.warning(s, name) load = True if load: # We don't load plugins that don't start with a capital # letter. if name[0].isupper() and not irc.getCallback(name): # This is debug because each log logs its beginning. self.log.debug('Loading %s.', name) try: m = plugin.loadPluginModule(name, ignoreDeprecation=True) plugin.loadPluginClass(irc, m) except callbacks.Error as e: # This is just an error message. log.warning(str(e)) except plugins.NoSuitableDatabase as e: s = 'Failed to load %s: no suitable database(%s).' % ( name, e) log.warning(s) except ImportError as e: e = str(e) if e.endswith(name): s = 'Failed to load {0}: No plugin named {0} exists.'.format( utils.str.dqrepr(name)) elif "No module named 'config'" in e: s = ( "Failed to load %s: This plugin may be incompatible " "with your current Python version. If this error is appearing " "with stock Supybot plugins, remove the stock plugins directory " "(usually ~/Limnoria/plugins) from 'config directories.plugins'." % name) else: s = 'Failed to load %s: import error (%s).' % ( name, e) log.warning(s) except Exception as e: log.exception('Failed to load %s:', name) else: # Let's import the module so configuration is preserved. try: _ = plugin.loadPluginModule(name) except Exception as e: log.debug( 'Attempted to load %s to preserve its ' 'configuration, but load failed: %s', name, e) world.starting = False
def flush(self): """Exports the database to a file.""" tmp_filename = self.filename + '.tmp' try: with open(tmp_filename, 'wb') as f: pickle.dump(self.db, f, 2) os.rename(tmp_filename, self.filename) except Exception as e: log.exception('%s: Unable to write database: %s', self._plugin_name, e)
def _dispatch(self, name, args): if name not in self.connector.rpcMethods: raise TypeError('no such method (%s)' % name) try: return self.connector.rpcMethods[name](*args) except Fault: raise except: log.exception('Unhandled exception in Trac XMLRPC:') raise
def __del__(self): try: Connection.__del__(self) except AttributeError: pass except Exception, e: try: log.exception('Uncaught exception in __del__:') except: pass
def _runCommandFunction(self, irc, msg, command): """Run a command from message, as if command was sent over IRC.""" try: tokens = callbacks.tokenize(command, channel=msg.channel, network=irc.network) except SyntaxError as e: # Emulate what callbacks.py does self.log.debug('Error return: %s', utils.exnToString(e)) irc.error(str(e)) try: self.Proxy(irc.irc, msg, tokens) except Exception as e: log.exception('Uncaught exception in function called by MessageParser:')
def run(self): if len(drivers._drivers) == 1 and not world.testing: log.error('Schedule is the only remaining driver, ' 'why do we continue to live?') time.sleep(1) # We're the only driver; let's pause to think. while self.schedule and self.schedule[0][0] < time.time(): (t, name) = heapq.heappop(self.schedule) f = self.events[name] del self.events[name] try: f() except Exception, e: log.exception('Uncaught exception in scheduled function:')
def open(self, filename): self.filename = filename reader = unpreserve.Reader(IrcUserCreator, self) try: self.noFlush = True try: reader.readFile(filename) self.noFlush = False self.flush() except EnvironmentError, e: log.error('Invalid user dictionary file, resetting to empty.') log.error('Exact error: %s', utils.exnToString(e)) except Exception, e: log.exception('Exact error:')
def open(self, filename): self.noFlush = True try: self.filename = filename reader = unpreserve.Reader(IrcChannelCreator, self) try: reader.readFile(filename) self.noFlush = False self.flush() except EnvironmentError, e: log.error("Invalid channel database, resetting to empty.") log.error("Exact error: %s", utils.exnToString(e)) except Exception, e: log.error("Invalid channel database, resetting to empty.") log.exception("Exact error:")
def get_bug_old(self, id): # Deprecated url = "%s/view.php?id=%d" % (self.url, id) try: raw = self.soap_client.mc_issue_get(username='', password='', issue_id=id) except Exception as e: if 'Issue #%d not found' % id in str(e): raise BugNotFoundError # Often SOAP is not enabled if '.' in self.name: supylog.exception(self.errget % (self.description, e, url)) return raise BugtrackerError(self.errget % (self.description, e, url)) if not hasattr(raw, 'id'): raise BugNotFoundError try: return (id, str(raw.project.name), str(raw.summary), str(raw.severity.name), str(raw.resolution.name), '', url, [], []) except Exception as e: raise BugtrackerError(self.errparse % (self.description, e, url))
def __init__(self, filename): self.dbs = {} cdb = conf.supybot.databases.types.cdb services = list(conf.supybot.plugins.ShrinkUrl.default.validStrings) services.append('Expand') for service in services: dbname = filename.replace('.db', service.capitalize() + '.db') try: self.dbs[service] = cdb.connect(dbname) except OSError as e: log.error('ShrinkUrl: Can not open database %s: %s', dbname, e) raise KeyError("Could not open %s" % dbname) except: log.exception( 'ShrinkUrl: Can not read database %s (data corruption?)', dbname) raise KeyError("Could not open %s" % dbname)
def __init__(self, *args, **kwargs): IBugtracker.__init__(self, *args, **kwargs) self.lp = None # A word to the wise: # The Launchpad API is much better than the /+text interface we currently use, # it's faster and easier to get the information we need. # The current /+text interface is not really maintained by Launchpad and most, # or all, of the Launchpad developers hate it. For this reason, we are dropping # support for /+text in the future in favour of launchpadlib. # Terence Simpson (tsimpson) 2010-04-20 try: from launchpadlib.launchpad import Launchpad cachedir = os.path.join(conf.supybot.directories.data.tmp(), 'launchpadlib') self.lp = Launchpad.login_anonymously("Ubuntu Bots - Bugtracker", 'production', cachedir, version='devel') except ImportError: supylog.warning("Please install python-launchpadlib, the old interface is deprecated") except Exception: self.lp = None supylog.exception("Unknown exception while accessing the Launchpad API")
def process_mails(self): log.info("Mail processing thread started.") while not self.quit: with self.cv: while not self.quit and len(self.messages) == 0: log.debug("Waiting for new mail.") self.cv.wait() if self.quit: log.info("Mail processing thread stopeed.") self.thread = None return mail = self.messages.popleft() log.debug("Got mail.") mail = base64.b64decode(mail.encode('ascii')) try: self.callback(BytesIO(mail)) except Exception as e: log.exception('Uncaught exception: {}'.format(e)) self.thread = None
def process_mails(self): log.debug("Mail processing thread started.") while not self.quit: with self.cv: while not self.quit and len(self.messages) == 0: log.debug("Waiting for new mail.") self.cv.wait() if self.quit: log.debug("Stopping.") self.thread = None return mail = self.messages.popleft() log.debug("Got mail.") mail = base64.b64decode(mail.encode('ascii')) try: self.callback(BytesIO(mail)) except Exception as e: log.exception('Uncaught exception: {}'.format(e)) self.thread = None
def _email_callback(self, fileobj): try: email = parse_mail(fileobj) msg = get_message(email) if not msg: return txt = colourise(msg.for_irc()) # Simple flood/duplicate detection if txt in self.last_n_messages: return self.last_n_messages.insert(0, txt) self.last_n_messages = self.last_n_messages[:20] for channel in self.irc.state.channels: regex = self.registryValue('package_regex', channel) or 'a^' if re.search(regex, msg.package): ircmsg = supybot.ircmsgs.privmsg(channel, txt) self.irc.queueMsg(ircmsg) except: log.exception('Uncaught exception')
def np(self, irc, msg, args, user): """[<user>] Announces the track currently being played by <user>. If <user> is not given, defaults to the LastFM user configured for your current nick. """ apiKey = self.registryValue("apiKey") if not apiKey: irc.error("The API Key is not set. Please set it via " "'config plugins.lastfm.apikey' and reload the plugin. " "You can sign up for an API Key using " "http://www.last.fm/api/account/create", Raise=True) user = (user or self.db.get(msg.prefix) or msg.nick) # see http://www.lastfm.de/api/show/user.getrecenttracks url = "%sapi_key=%s&method=user.getrecenttracks&user=%s&format=json" % (self.APIURL, apiKey, user) try: f = utils.web.getUrl(url).decode("utf-8") except utils.web.Error: irc.error("Unknown user %s." % user, Raise=True) self.log.debug("LastFM.nowPlaying: url %s", url) try: data = json.loads(f)["recenttracks"] except KeyError: irc.error("Unknown user %s." % user, Raise=True) user = data["@attr"]["user"] tracks = data["track"] # Work with only the first track. try: trackdata = tracks[0] except IndexError: irc.error("%s doesn't seem to have listened to anything." % user, Raise=True) artist = trackdata["artist"]["#text"].strip() # Artist name track = trackdata["name"].strip() # Track name # Album name (may or may not be present) album = trackdata["album"]["#text"].strip() if album: album = ircutils.bold("[%s]" % album) try: time = int(trackdata["date"]["uts"]) # Time of last listen # Format this using the preferred time format. tformat = conf.supybot.reply.format.time() time = "at %s" % datetime.fromtimestamp(time).strftime(tformat) except KeyError: # Nothing given by the API? time = "just now" public_url = '' # If the DDG plugin from this repository is loaded, we can integrate # that by finding a YouTube link for the track. if self.registryValue("fetchYouTubeLink", msg.args[0]): ddg = irc.getCallback("DDG") if ddg: try: results = ddg.search_core('site:youtube.com "%s - %s"' % (artist, track), channel_context=msg.args[0], max_results=1, show_snippet=False) if results: public_url = format('%u', results[0][2]) except: # If something breaks, log the error but don't cause the # entire np request to fail. log.exception("LastFM: failed to get YouTube link for track %s - %s", artist, track) ext_info = '' if self.registryValue("showExtendedInfo", msg.args[0]): # Get extended info via a separate API call. ext_info_url = "%sapi_key=%s&method=track.getinfo&user=%s&format=json&artist=%s&track=%s" % (self.APIURL, apiKey, user, utils.web.urlquote(artist), utils.web.urlquote(track)) ext_info_f = utils.web.getUrl(ext_info_url).decode("utf-8") self.log.debug("LastFM.nowPlaying: using url %s for extended info", ext_info_url) try: ext_data = json.loads(ext_info_f)['track'] # We currently show play count and tags - more could be added in the future... userplaycount = ext_data['userplaycount'] tags = [tag['name'] for tag in ext_data['toptags']['tag']] ext_info = ' (Playcount: %s / Tags: %s)' % (userplaycount, ', '.join(tags) or 'N/A') except KeyError: pass s = '%s listened to %s by %s %s %s%s. %s' % (ircutils.bold(user), ircutils.bold(track), ircutils.bold(artist), album, time, ext_info, public_url) irc.reply(utils.str.normalizeWhitespace(s))
def _email_callback(self, fileobj): try: emailmsg = parse_mail(fileobj) msg = get_message(emailmsg, new_queue=self.new_queue) if not msg: return txt = colourise(msg.for_irc()) # Simple flood/duplicate detection if txt in self.last_n_messages: return self.last_n_messages.insert(0, txt) self.last_n_messages = self.last_n_messages[:20] maintainer_info = None if hasattr(msg, 'maintainer'): maintainer_info = (split_address(msg.maintainer), ) else: maintainer_info = [] for package in msg.package.split(','): package = package.strip() try: maintainer_info.append( self.apt_archive.get_maintainer(package)) except NewDataSource.DataError as e: log.info("Failed to query maintainer for {}.".format( package)) for channel in self.irc.state.channels: package_regex = self.registryValue( 'package_regex', channel, ) or 'a^' # match nothing by default package_match = re.search(package_regex, msg.package) maintainer_match = False maintainer_regex = self.registryValue('maintainer_regex', channel) if maintainer_regex and maintainer_info is not None and len( maintainer_info) >= 0: for mi in maintainer_info: maintainer_match = re.search(maintainer_regex, mi['email']) if maintainer_match: break if not package_match and not maintainer_match: continue distribution_regex = self.registryValue( 'distribution_regex', channel, ) if distribution_regex: if not hasattr(msg, 'distribution'): # If this channel has a distribution regex, don't # bother continuing unless the message actually has a # distribution. This filters security messages, etc. continue if not re.search(distribution_regex, msg.distribution): # Distribution doesn't match regex; don't send this # message. continue send_privmsg = self.registryValue('send_privmsg', channel) # Send NOTICE per default and if 'send_privmsg' is set for the # channel, send PRIVMSG instead. if send_privmsg: ircmsg = supybot.ircmsgs.privmsg(channel, txt) else: ircmsg = supybot.ircmsgs.notice(channel, txt) self.irc.queueMsg(ircmsg) except Exception as e: log.exception('Uncaught exception: %s ' % e)
def _email_callback(self, fileobj): try: emailmsg = parse_mail(fileobj) msg = get_message(emailmsg, new_queue=self.new_queue) if not msg: return txt = colourise(msg.for_irc()) # Simple flood/duplicate detection if txt in self.last_n_messages: return self.last_n_messages.insert(0, txt) self.last_n_messages = self.last_n_messages[:20] maintainer_info = None if hasattr(msg, 'maintainer'): maintainer_info = (split_address(msg.maintainer), ) else: maintainer_info = [] for package in msg.package.split(','): package = package.strip() try: maintainer_info.append(self.apt_archive.get_maintainer(package)) except NewDataSource.DataError as e: log.info("Failed to query maintainer for {}.".format(package)) for channel in self.irc.state.channels: package_regex = self.registryValue( 'package_regex', channel, ) or 'a^' # match nothing by default package_match = re.search(package_regex, msg.package) maintainer_match = False maintainer_regex = self.registryValue( 'maintainer_regex', channel) if maintainer_regex and maintainer_info is not None and len(maintainer_info) >= 0: for mi in maintainer_info: maintainer_match = re.search(maintainer_regex, mi['email']) if maintainer_match: break if not package_match and not maintainer_match: continue distribution_regex = self.registryValue( 'distribution_regex', channel, ) if distribution_regex: if not hasattr(msg, 'distribution'): # If this channel has a distribution regex, don't # bother continuing unless the message actually has a # distribution. This filters security messages, etc. continue if not re.search(distribution_regex, msg.distribution): # Distribution doesn't match regex; don't send this # message. continue send_privmsg = self.registryValue('send_privmsg', channel) # Send NOTICE per default and if 'send_privmsg' is set for the # channel, send PRIVMSG instead. if send_privmsg: ircmsg = supybot.ircmsgs.privmsg(channel, txt) else: ircmsg = supybot.ircmsgs.notice(channel, txt) self.irc.queueMsg(ircmsg) except Exception as e: log.exception('Uncaught exception: %s ' % e)
def _ticker(self, irc, ticker): prev = dict() while True: if self.stop: return try: log.debug("refreshing ticker %r", ticker) data = dict() for key, url in TICKERS[ticker]['urls'].iteritems(): log.debug("fetching url %r", url) try: t0 = time.time() data[key] = json.load(urllib2.urlopen(url, timeout=30)) except Exception, e: log.exception("error fetching %r", url) continue log.info("fetched %r in %.2f s", url, time.time() - t0) log.debug("data = %r", data[key]) if not data: raise Exception("no data for ticker %s" % repr(ticker)) for output, o in TICKERS[ticker]['outputs'].iteritems(): log.debug("processing output %r: %r", output, o) values = [] diffstring = '' report = False low = high = None for metric, m in o['metrics'].iteritems(): value = d(getvalue(data, m[1:])).quantize(m[0]) if metric == 'volume': # volume is in base units currency = o['unit'] else: # others are in currency currency = o['currency'] log.debug("output %r metric %r has value %r", output, metric, value) if metric in o['reportchange']: if output+metric in prev: vdiff = value - prev[output+metric] else: vdiff = Decimal(0) prev[output+metric] = value report = True if vdiff.copy_abs() >= o['reportchange'][metric]: log.debug("output %r metric %r value diff %r exceeds %r, reporting change", output, metric, vdiff.copy_abs(), o['reportchange'][metric]) prev[output+metric] = value report = True values.append("%12s %-3s" % ( value, currency)) diffstring = updown(vdiff) else: values.append("%12s %-3s" % ( value, currency)) if low is None or value < low: low = value if high is None or value > high: high = value diffstring=updown(vdiff, '(%s%s %s)' % ( '-' if vdiff.is_signed() else '+', vdiff.copy_abs(), currency)) out = "%s %s %s %s" % ( '['+TICKERS[ticker]['label']+']', currencycolor(o['unit']), " ".join(values), diffstring) if report: for chan in self.registryValue('channels'): irc.queueMsg(ircmsgs.privmsg(chan, ircutils.bold(out))) nicks = self.lowvalues.keys() for nick in nicks: user_value = self.lowvalues[nick] if low is not None and low <= user_value: out = "%s: OMG, it went below %s to %s!" % (nick, user_value, low) irc.queueMsg(ircmsgs.privmsg(chan, ircutils.bold(out))) del self.lowvalues[nick] nicks = self.highvalues.keys() for nick in nicks: user_value = self.highvalues[nick] if high is not None and high >= user_value: out = "%s: OMG, it went above %s to %s!" % (nick, user_value, high) irc.queueMsg(ircmsgs.privmsg(chan, ircutils.bold(out))) del self.highvalues[nick] except Exception, e: log.exception("in ticker thread %r", ticker)
def np(self, irc, msg, args, user): """[<user>] Announces the track currently being played by <user>. If <user> is not given, defaults to the LastFM user configured for your current nick. """ apiKey = self.registryValue("apiKey") if not apiKey: irc.error( "The API Key is not set. Please set it via " "'config plugins.lastfm.apikey' and reload the plugin. " "You can sign up for an API Key using " "http://www.last.fm/api/account/create", Raise=True) user = (user or self.db.get(msg.prefix) or msg.nick) # see http://www.lastfm.de/api/show/user.getrecenttracks url = "%sapi_key=%s&method=user.getrecenttracks&user=%s&format=json" % ( self.APIURL, apiKey, user) try: f = utils.web.getUrl(url).decode("utf-8") except utils.web.Error: irc.error("Unknown user %s." % user, Raise=True) self.log.debug("LastFM.nowPlaying: url %s", url) try: data = json.loads(f)["recenttracks"] except KeyError: irc.error("Unknown user %s." % user, Raise=True) user = data["@attr"]["user"] tracks = data["track"] # Work with only the first track. try: trackdata = tracks[0] except IndexError: irc.error("%s doesn't seem to have listened to anything." % user, Raise=True) artist = trackdata["artist"]["#text"].strip() # Artist name track = trackdata["name"].strip() # Track name # Album name (may or may not be present) album = trackdata["album"]["#text"].strip() if album: album = ircutils.bold("[%s]" % album) try: time = int(trackdata["date"]["uts"]) # Time of last listen # Format this using the preferred time format. tformat = conf.supybot.reply.format.time() time = "at %s" % datetime.fromtimestamp(time).strftime(tformat) except KeyError: # Nothing given by the API? time = "right now" public_url = '' # If the DDG plugin from this repository is loaded, we can integrate # that by finding a YouTube link for the track. if self.registryValue("fetchYouTubeLink"): ddg = irc.getCallback("DDG") if ddg: # Each valid result has a preceding heading in the format # '<td valign="top">1. </td>', etc. try: search = [ td for td in ddg._ddgurl('site:youtube.com "%s - %s"' % (artist, track)) if "1." in td.text ] res = search[0].next_sibling.next_sibling public_url = format('%u', res.a.get('href')) except: # If something breaks, log the error but don't cause the # entire np request to fail. log.exception( "LastFM: failed to get YouTube link for track %s - %s", artist, track) s = '%s listened to %s by %s %s %s. %s' % ( ircutils.bold(user), ircutils.bold(track), ircutils.bold(artist), album, time, public_url) irc.reply(utils.str.normalizeWhitespace(s))
def implementation(): try: source.update() except Exception as e: log.exception('Failed to update {}: {}'.format(source.NAME, e)) self._topic_callback()
def _ticker(self, irc, ticker): prev = dict() while True: if self.stop: return try: log.debug("refreshing ticker %r", ticker) data = dict() for key, url in TICKERS[ticker]["urls"].iteritems(): log.debug("fetching url %r", url) try: t0 = time.time() data[key] = json.load(urllib2.urlopen(url, timeout=30)) except Exception, e: log.exception("error fetching %r", url) continue log.info("fetched %r in %.2f s", url, time.time() - t0) log.debug("data = %r", data[key]) if not data: raise Exception("no data for ticker %s" % repr(ticker)) for output, o in TICKERS[ticker]["outputs"].iteritems(): log.debug("processing output %r: %r", output, o) values = [] diffstring = "" report = False for metric, m in o["metrics"].iteritems(): value = d(getvalue(data, m[1:])).quantize(m[0]) if metric == "volume": # volume is in base units currency = o["unit"] else: # others are in currency currency = o["currency"] log.debug("output %r metric %r has value %r", output, metric, value) if metric in o["reportchange"]: if output + metric in prev: vdiff = value - prev[output + metric] else: vdiff = Decimal(0) prev[output + metric] = value report = True if vdiff.copy_abs() >= o["reportchange"][metric]: log.debug( "output %r metric %r value diff %r exceeds %r, reporting change", output, metric, vdiff.copy_abs(), o["reportchange"][metric], ) prev[output + metric] = value report = True values.append("%12s %-3s" % (value, currency)) diffstring = updown(vdiff) else: values.append("%12s %-3s" % (value, currency)) diffstring = updown( vdiff, "(%s%s %s)" % ("-" if vdiff.is_signed() else "+", vdiff.copy_abs(), currency) ) out = "%s %s %s %s" % ( "[" + TICKERS[ticker]["label"] + "]", currencycolor(o["unit"]), " ".join(values), diffstring, ) if report: for chan in self.registryValue("channels"): irc.queueMsg(ircmsgs.privmsg(chan, ircutils.bold(out))) except Exception, e: log.exception("in ticker thread %r", ticker)
def np(self, irc, msg, args, user): """[<user>] Announces the track currently being played by <user>. If <user> is not given, defaults to the LastFM user configured for your current nick. """ apiKey = self.registryValue("apiKey") if not apiKey: irc.error("The API Key is not set. Please set it via " "'config plugins.lastfm.apikey' and reload the plugin. " "You can sign up for an API Key using " "http://www.last.fm/api/account/create", Raise=True) user = (user or self.db.get(msg.prefix) or msg.nick) # see http://www.lastfm.de/api/show/user.getrecenttracks url = "%sapi_key=%s&method=user.getrecenttracks&user=%s&format=json" % (self.APIURL, apiKey, user) try: f = utils.web.getUrl(url).decode("utf-8") except utils.web.Error: irc.error("Unknown user %s." % user, Raise=True) self.log.debug("LastFM.nowPlaying: url %s", url) try: data = json.loads(f)["recenttracks"] except KeyError: irc.error("Unknown user %s." % user, Raise=True) user = data["@attr"]["user"] tracks = data["track"] # Work with only the first track. try: trackdata = tracks[0] except IndexError: irc.error("%s doesn't seem to have listened to anything." % user, Raise=True) artist = trackdata["artist"]["#text"].strip() # Artist name track = trackdata["name"].strip() # Track name # Album name (may or may not be present) album = trackdata["album"]["#text"].strip() if album: album = "[%s] " % album try: time = int(trackdata["date"]["uts"]) # Time of last listen # Format this using the preferred time format. tformat = conf.supybot.reply.format.time() time = datetime.fromtimestamp(time).strftime(tformat) except KeyError: # Nothing given by the API? time = "some point in time" public_url = '' # If the DDG plugin from this repository is loaded, we can integrate # that by finding a YouTube link for the track. if self.registryValue("fetchYouTubeLink"): ddg = irc.getCallback("DDG") if ddg: # Each valid result has a preceding heading in the format # '<td valign="top">1. </td>', etc. try: search = [td for td in ddg._ddgurl('site:youtube.com "%s - %s"' % (artist, track)) if "1." in td.text] res = search[0].next_sibling.next_sibling public_url = format(' - %u', res.a.get('href')) except: # If something breaks, log the error but don't cause the # entire np request to fail. log.exception("LastFM: failed to get YouTube link for track %s - %s", artist, track) irc.reply('%s listened to %s by %s %sat %s%s' % (ircutils.bold(user), ircutils.bold(track), ircutils.bold(artist), ircutils.bold(album), time, public_url))
def getvalue(data, keys): try: for k in keys: data = data.get(k) except Exception, e: log.exception("data=%r keys=%r", data, keys)
def np(self, irc, msg, args, user): """[<user>] Announces the track currently being played by <user>. If <user> is not given, defaults to the LastFM user configured for your current nick. """ apiKey = self.registryValue("apiKey") if not apiKey: irc.error( "The API Key is not set. Please set it via " "'config plugins.lastfm.apikey' and reload the plugin. " "You can sign up for an API Key using " "http://www.last.fm/api/account/create", Raise=True) user = (user or self.db.get(msg.prefix) or msg.nick) # see http://www.lastfm.de/api/show/user.getrecenttracks url = "%sapi_key=%s&method=user.getrecenttracks&user=%s&format=json" % ( self.APIURL, apiKey, user) try: f = utils.web.getUrl(url).decode("utf-8") except utils.web.Error: irc.error("Unknown user %s." % user, Raise=True) self.log.debug("LastFM.nowPlaying: url %s", url) try: data = json.loads(f)["recenttracks"] except KeyError: irc.error("Unknown user %s." % user, Raise=True) user = data["@attr"]["user"] tracks = data["track"] # Work with only the first track. try: trackdata = tracks[0] except IndexError: irc.error("%s doesn't seem to have listened to anything." % user, Raise=True) artist = trackdata["artist"]["#text"].strip() # Artist name track = trackdata["name"].strip() # Track name # Album name (may or may not be present) album = trackdata["album"]["#text"].strip() if album: album = ircutils.bold("[%s]" % album) try: time = int(trackdata["date"]["uts"]) # Time of last listen # Format this using the preferred time format. tformat = conf.supybot.reply.format.time() time = "at %s" % datetime.fromtimestamp(time).strftime(tformat) except KeyError: # Nothing given by the API? time = "just now" public_url = '' # If the DDG plugin from this repository is loaded, we can integrate # that by finding a YouTube link for the track. if self.registryValue("fetchYouTubeLink", msg.args[0]): ddg = irc.getCallback("DDG") if ddg: try: results = ddg.search_core('site:youtube.com "%s - %s"' % (artist, track), channel_context=msg.args[0], max_results=1, show_snippet=False) if results: public_url = format('%u', results[0][2]) except: # If something breaks, log the error but don't cause the # entire np request to fail. log.exception( "LastFM: failed to get YouTube link for track %s - %s", artist, track) ext_info = '' if self.registryValue("showExtendedInfo", msg.args[0]): # Get extended info via a separate API call. ext_info_url = "%sapi_key=%s&method=track.getinfo&user=%s&format=json&artist=%s&track=%s" % ( self.APIURL, apiKey, user, utils.web.urlquote(artist), utils.web.urlquote(track)) ext_info_f = utils.web.getUrl(ext_info_url).decode("utf-8") self.log.debug("LastFM.nowPlaying: using url %s for extended info", ext_info_url) try: ext_data = json.loads(ext_info_f)['track'] # We currently show play count and tags - more could be added in the future... userplaycount = ext_data['userplaycount'] tags = [tag['name'] for tag in ext_data['toptags']['tag']] ext_info = ' (Playcount: %s / Tags: %s)' % ( userplaycount, ', '.join(tags) or 'N/A') except KeyError: pass s = '%s listened to %s by %s %s %s%s. %s' % ( ircutils.bold(user), ircutils.bold(track), ircutils.bold(artist), album, time, ext_info, public_url) irc.reply(utils.str.normalizeWhitespace(s))