diff --git a/plugins/Http.py b/plugins/Http.py index 6e04f9889..24105ad23 100644 --- a/plugins/Http.py +++ b/plugins/Http.py @@ -1,411 +1,411 @@ -### -# Copyright (c) 2002-2004, Jeremiah Fincher -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - -""" -Provides several commands that go out to websites and get things. -""" - -import supybot - -__revision__ = "$Id$" -__contributors__ = { - supybot.authors.jemfinch: ['bender', 'cyborg', 'doctype', 'freshmeat', - 'headers', 'netcraft', 'size', 'title'], - supybot.authors.jamessan: ['pgpkey', 'kernel', 'filext', 'zipinfo', - 'acronym'], - } - -import supybot.plugins as plugins - -import re -import sets -import getopt -import socket -import urllib -import xml.dom.minidom -from itertools import imap - -import supybot.conf as conf -import supybot.utils as utils -from supybot.commands import * -import supybot.webutils as webutils -import supybot.registry as registry -import supybot.callbacks as callbacks - -conf.registerPlugin('Http') - -class FreshmeatException(Exception): - pass - -class Http(callbacks.Privmsg): - threaded = True - _titleRe = re.compile(r'(.*?)', re.I | re.S) - def __init__(self): - self.__parent = super(Http, self) - self.__parent.__init__() - - def callCommand(self, name, irc, msg, *L, **kwargs): - try: - self.__parent.callCommand(name, irc, msg, *L, **kwargs) - except webutils.WebError, e: - irc.error(str(e)) - - def headers(self, irc, msg, args, url): - """ - - Returns the HTTP headers of . Only HTTP urls are valid, of - course. - """ - fd = webutils.getUrlFd(url) - try: - s = ', '.join(['%s: %s' % (k, v) for (k, v) in fd.headers.items()]) - irc.reply(s) - finally: - fd.close() - headers = wrap(headers, ['httpUrl']) - - _doctypeRe = re.compile(r'(]+>)', re.M) - def doctype(self, irc, msg, args, url): - """ - - Returns the DOCTYPE string of . Only HTTP urls are valid, of - course. - """ - size = conf.supybot.protocols.http.peekSize() - s = webutils.getUrl(url, size=size) - m = self._doctypeRe.search(s) - if m: - s = utils.normalizeWhitespace(m.group(0)) - irc.reply(s) - else: - irc.reply('That URL has no specified doctype.') - doctype = wrap(doctype, ['httpUrl']) - - def size(self, irc, msg, args, url): - """ - - Returns the Content-Length header of . Only HTTP urls are valid, - of course. - """ - fd = webutils.getUrlFd(url) - try: - try: - size = fd.headers['Content-Length'] - irc.reply('%s is %s bytes long.' % (url, size)) - except KeyError: - size = conf.supybot.protocols.http.peekSize() - s = fd.read(size) - if len(s) != size: - irc.reply('%s is %s bytes long.' % (url, len(s))) - else: - irc.reply('The server didn\'t tell me how long %s is ' - 'but it\'s longer than %s bytes.' % (url, size)) - finally: - fd.close() - size = wrap(size, ['httpUrl']) - - def title(self, irc, msg, args, url): - """ - - Returns the HTML ... of a URL. - """ - size = conf.supybot.protocols.http.peekSize() - text = webutils.getUrl(url, size=size) - m = self._titleRe.search(text) - if m is not None: - irc.reply(utils.htmlToText(m.group(1).strip())) - else: - irc.reply('That URL appears to have no HTML title ' - 'within the first %s bytes.' % size) - title = wrap(title, ['httpUrl']) - - def freshmeat(self, irc, msg, args, project): - """ - - Returns Freshmeat data about a given project. - """ - project = ''.join(project.split()) - url = 'http://www.freshmeat.net/projects-xml/%s' % project - try: - text = webutils.getUrl(url) - if text.startswith('Error'): - text = text.split(None, 1)[1] - raise FreshmeatException, text - dom = xml.dom.minidom.parseString(text) - def getNode(name): - node = dom.getElementsByTagName(name)[0] - return str(node.childNodes[0].data) - project = getNode('projectname_full') - version = getNode('latest_release_version') - vitality = getNode('vitality_percent') - popularity = getNode('popularity_percent') - lastupdated = getNode('date_updated') - irc.reply('%s, last updated %s, with a vitality percent of %s ' - 'and a popularity of %s, is in version %s.' % - (project, lastupdated, vitality, popularity, version)) - except FreshmeatException, e: - irc.error(str(e)) - freshmeat = wrap(freshmeat, ['text']) - - def stockquote(self, irc, msg, args, symbol): - """ - - Gets the information about the current price and change from the - previous day of a given company (represented by a stock symbol). - """ - url = 'http://finance.yahoo.com/d/quotes.csv?s=%s' \ - '&f=sl1d1t1c1ohgv&e=.csv' % symbol - quote = webutils.getUrl(url) - data = quote.split(',') - if data[1] != '0.00': - irc.reply('The current price of %s is %s, as of %s EST. ' - 'A change of %s from the last business day.' % - (data[0][1:-1], data[1], data[3][1:-1], data[4])) - else: - m = 'I couldn\'t find a listing for %s' % symbol - irc.error(m) - stockquote = wrap(stockquote, ['something']) - - _cyborgRe = re.compile(r'

(.*?)

', re.I) - def cyborg(self, irc, msg, args, name): - """[] - - Returns a cyborg acronym for from . - If is not specified, uses that of the user. - """ - if not name: - name = msg.nick - name = urllib.quote(name) - url = 'http://www.cyborgname.com/cyborger.cgi?acronym=%s' % name - html = webutils.getUrl(url) - m = self._cyborgRe.search(html) - if m: - s = m.group(1) - s = utils.normalizeWhitespace(s) - irc.reply(s) - else: - irc.errorPossibleBug('No cyborg name returned.') - cyborg = wrap(cyborg, [additional('somethingWithoutSpaces')]) - - _acronymre = re.compile(r'valign="middle" width="7\d%" bgcolor="[^"]+">' - r'(?:)?([^<]+)') - def acronym(self, irc, msg, args, acronym): - """ - - Displays acronym matches from acronymfinder.com - """ - url = 'http://www.acronymfinder.com/' \ - 'af-query.asp?String=exact&Acronym=%s' % urllib.quote(acronym) - html = webutils.getUrl(url) - if 'daily limit' in html: - s = 'Acronymfinder.com says I\'ve reached my daily limit. Sorry.' - irc.error(s) - return - # The following definitions are stripped and empties are removed. - defs = filter(None, imap(str.strip, self._acronymre.findall(html))) - utils.sortBy(lambda s: not s.startswith('[not an acronym]'), defs) - for (i, s) in enumerate(defs): - if s.startswith('[not an acronym]'): - defs[i] = s.split('] ', 1)[1].strip() - if len(defs) == 0: - irc.reply('No definitions found.') - else: - s = ', or '.join(defs) - irc.reply('%s could be %s' % (acronym, s)) - acronym = wrap(acronym, ['text']) - - _netcraftre = re.compile(r'td align="left">\s+]+>(.*?) - - Returns Netcraft.com's determination of what operating system and - webserver is running on the host given. - """ - url = 'http://uptime.netcraft.com/up/graph/?host=%s' % hostname - html = webutils.getUrl(url) - m = self._netcraftre.search(html) - if m: - html = m.group(1) - s = utils.htmlToText(html, tagReplace='').strip() - s = s.rstrip('-').strip() - irc.reply(s) # Snip off "the site" - elif 'We could not get any results' in html: - irc.reply('No results found for %s.' % hostname) - else: - irc.error('The format of page the was odd.') - netcraft = wrap(netcraft, ['text']) - - def kernel(self, irc, msg, args): - """takes no arguments - - Returns information about the current version of the Linux kernel. - """ - fd = webutils.getUrlFd('http://kernel.org/kdist/finger_banner') - try: - stable = 'unknown' - snapshot = 'unknown' - mm = 'unknown' - for line in fd: - (name, version) = line.split(':') - if 'latest stable' in name: - stable = version.strip() - elif 'snapshot for the stable' in name: - snapshot = version.strip() - elif '-mm patch' in name: - mm = version.strip() - finally: - fd.close() - irc.reply('The latest stable kernel is %s; ' - 'the latest snapshot of the stable kernel is %s; ' - 'the latest beta kernel is %s.' % (stable, snapshot, mm)) - kernel = wrap(kernel) - - _pgpkeyre = re.compile(r'pub\s+\d{4}\w/' - r'([^<]+)[^>]+>([^<]+)') - def pgpkey(self, irc, msg, args, search): - """ - - Returns the results of querying pgp.mit.edu for keys that match - the . - """ - urlClean = search.replace(' ', '+') - host = 'http://pgp.mit.edu:11371' - url = '%s/pks/lookup?op=index&search=%s' % (host, urlClean) - fd = webutils.getUrlFd(url, headers={}) - try: - L = [] - for line in iter(fd.next, ''): - info = self._pgpkeyre.search(line) - if info: - L.append('%s <%s%s>' % (info.group(3),host,info.group(1))) - if len(L) == 0: - irc.reply('No results found for %s.' % search) - else: - s = 'Matches found for %s: %s' % (search, ' :: '.join(L)) - irc.reply(s) - finally: - fd.close() - pgpkey = wrap(pgpkey, ['text']) - - _filextre = re.compile( - r'Extension:.*?.*?\s+\s+(?:]+>([^<]+)\s+|([^<]+))\s+' - r'(?:]+>([^<]+)| - - Returns the results of querying filext.com for file extensions that - match . - """ - # XXX This probably ought to be handled in a converter from commands.py - invalid = '|<>\^=?/[]";,*' - for c in invalid: - if c in ext: - irc.error('\'%s\' is an invalid extension character' % c) - return - s = 'http://www.filext.com/detaillist.php?extdetail=%s&goButton=Go' - text = webutils.getUrl(s % ext) - matches = self._filextre.findall(text) - #print matches - res = [] - for match in matches: - (file1, file2, comp1, comp2) = match - if file1: - filetype = file1.strip() - else: - filetype = file2.strip() - if comp1: - company = comp1.strip() - else: - company = comp2.strip() - if company: - res.append('%s\'s %s' % (company, filetype)) - else: - res.append(filetype) - if res: - irc.reply(utils.commaAndify(res)) - else: - irc.error('No matching file extensions were found.') - extension = wrap(extension, ['text']) - - _zipinfore = re.compile(r'Latitude
\(([^)]+)\)Longitude
' - r'\(([^)]+)\).*?(.*?)', re.I) - _zipstatre = re.compile(r'(Only about \d+,\d{3} of.*?in use.)') - def zipinfo(self, irc, msg, args, zipcode): - """ - - Returns a plethora of information for the given . - """ - try: - int(zipcode) - except ValueError: - irc.error('Zip code must be a 5-digit integer.') - return - if len(zipcode) != 5: - irc.error('Zip code must be a 5-digit integer.') - return - url = 'http://zipinfo.com/cgi-local/zipsrch.exe?cnty=cnty&ac=ac&'\ - 'tz=tz&ll=ll&zip=%s&Go=Go' % zipcode - text = webutils.getUrl(url) - if 'daily usage limit' in text: - irc.error('I have exceeded the site\'s daily usage limit.') - return - m = self._zipstatre.search(text) - if m: - irc.reply('%s %s is not one of them.' % (m.group(1), zipcode)) - return - n = self._zipinfore.search(text) - if not n: - irc.error('Unable to retrieve information for that zip code.') - return - (latdir, longdir, rawinfo) = n.groups() - # Info consists of the following (whitespace separated): - # City, State Abbrev., Zip Code, County, FIPS Code, Area Code, Time - # Zone, Daylight Time(?), Latitude, Longitude - info = utils.htmlToText(rawinfo) - info = info.split() - zipindex = info.index(zipcode) - resp = ['City: %s' % ' '.join(info[:zipindex-1]), - 'State: %s' % info[zipindex-1], - 'County: %s' % ' '.join(info[zipindex+1:-6]), - 'Area Code: %s' % info[-5], - 'Time Zone: %s' % info[-4], - 'Daylight Savings: %s' % info[-3], - 'Latitude: %s (%s)' % (info[-2], latdir), - 'Longitude: %s (%s)' % (info[-1], longdir), - ] - irc.reply('; '.join(resp)) - zipinfo = wrap(zipinfo, ['text']) - - -Class = Http - -# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: +### +# Copyright (c) 2002-2004, Jeremiah Fincher +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +""" +Provides several commands that go out to websites and get things. +""" + +import supybot + +__revision__ = "$Id$" +__contributors__ = { + supybot.authors.jemfinch: ['bender', 'cyborg', 'doctype', 'freshmeat', + 'headers', 'netcraft', 'size', 'title'], + supybot.authors.jamessan: ['pgpkey', 'kernel', 'filext', 'zipinfo', + 'acronym'], + } + +import supybot.plugins as plugins + +import re +import sets +import getopt +import socket +import urllib +import xml.dom.minidom +from itertools import imap + +import supybot.conf as conf +import supybot.utils as utils +from supybot.commands import * +import supybot.webutils as webutils +import supybot.registry as registry +import supybot.callbacks as callbacks + +conf.registerPlugin('Http') + +class FreshmeatException(Exception): + pass + +class Http(callbacks.Privmsg): + threaded = True + _titleRe = re.compile(r'(.*?)', re.I | re.S) + def __init__(self): + self.__parent = super(Http, self) + self.__parent.__init__() + + def callCommand(self, name, irc, msg, *L, **kwargs): + try: + self.__parent.callCommand(name, irc, msg, *L, **kwargs) + except webutils.WebError, e: + irc.error(str(e)) + + def headers(self, irc, msg, args, url): + """ + + Returns the HTTP headers of . Only HTTP urls are valid, of + course. + """ + fd = webutils.getUrlFd(url) + try: + s = ', '.join(['%s: %s' % (k, v) for (k, v) in fd.headers.items()]) + irc.reply(s) + finally: + fd.close() + headers = wrap(headers, ['httpUrl']) + + _doctypeRe = re.compile(r'(]+>)', re.M) + def doctype(self, irc, msg, args, url): + """ + + Returns the DOCTYPE string of . Only HTTP urls are valid, of + course. + """ + size = conf.supybot.protocols.http.peekSize() + s = webutils.getUrl(url, size=size) + m = self._doctypeRe.search(s) + if m: + s = utils.normalizeWhitespace(m.group(0)) + irc.reply(s) + else: + irc.reply('That URL has no specified doctype.') + doctype = wrap(doctype, ['httpUrl']) + + def size(self, irc, msg, args, url): + """ + + Returns the Content-Length header of . Only HTTP urls are valid, + of course. + """ + fd = webutils.getUrlFd(url) + try: + try: + size = fd.headers['Content-Length'] + irc.reply('%s is %s bytes long.' % (url, size)) + except KeyError: + size = conf.supybot.protocols.http.peekSize() + s = fd.read(size) + if len(s) != size: + irc.reply('%s is %s bytes long.' % (url, len(s))) + else: + irc.reply('The server didn\'t tell me how long %s is ' + 'but it\'s longer than %s bytes.' % (url, size)) + finally: + fd.close() + size = wrap(size, ['httpUrl']) + + def title(self, irc, msg, args, url): + """ + + Returns the HTML ... of a URL. + """ + size = conf.supybot.protocols.http.peekSize() + text = webutils.getUrl(url, size=size) + m = self._titleRe.search(text) + if m is not None: + irc.reply(utils.htmlToText(m.group(1).strip())) + else: + irc.reply('That URL appears to have no HTML title ' + 'within the first %s bytes.' % size) + title = wrap(title, ['httpUrl']) + + def freshmeat(self, irc, msg, args, project): + """ + + Returns Freshmeat data about a given project. + """ + project = ''.join(project.split()) + url = 'http://www.freshmeat.net/projects-xml/%s' % project + try: + text = webutils.getUrl(url) + if text.startswith('Error'): + text = text.split(None, 1)[1] + raise FreshmeatException, text + dom = xml.dom.minidom.parseString(text) + def getNode(name): + node = dom.getElementsByTagName(name)[0] + return str(node.childNodes[0].data) + project = getNode('projectname_full') + version = getNode('latest_release_version') + vitality = getNode('vitality_percent') + popularity = getNode('popularity_percent') + lastupdated = getNode('date_updated') + irc.reply('%s, last updated %s, with a vitality percent of %s ' + 'and a popularity of %s, is in version %s.' % + (project, lastupdated, vitality, popularity, version)) + except FreshmeatException, e: + irc.error(str(e)) + freshmeat = wrap(freshmeat, ['text']) + + def stockquote(self, irc, msg, args, symbol): + """ + + Gets the information about the current price and change from the + previous day of a given company (represented by a stock symbol). + """ + url = 'http://finance.yahoo.com/d/quotes.csv?s=%s' \ + '&f=sl1d1t1c1ohgv&e=.csv' % symbol + quote = webutils.getUrl(url) + data = quote.split(',') + if data[1] != '0.00': + irc.reply('The current price of %s is %s, as of %s EST. ' + 'A change of %s from the last business day.' % + (data[0][1:-1], data[1], data[3][1:-1], data[4])) + else: + m = 'I couldn\'t find a listing for %s' % symbol + irc.error(m) + stockquote = wrap(stockquote, ['something']) + + _cyborgRe = re.compile(r'

(.*?)

', re.I) + def cyborg(self, irc, msg, args, name): + """[] + + Returns a cyborg acronym for from . + If is not specified, uses that of the user. + """ + if not name: + name = msg.nick + name = urllib.quote(name) + url = 'http://www.cyborgname.com/cyborger.cgi?acronym=%s' % name + html = webutils.getUrl(url) + m = self._cyborgRe.search(html) + if m: + s = m.group(1) + s = utils.normalizeWhitespace(s) + irc.reply(s) + else: + irc.errorPossibleBug('No cyborg name returned.') + cyborg = wrap(cyborg, [additional('somethingWithoutSpaces')]) + + _acronymre = re.compile(r'valign="middle" width="7\d%" bgcolor="[^"]+">' + r'(?:)?([^<]+)') + def acronym(self, irc, msg, args, acronym): + """ + + Displays acronym matches from acronymfinder.com + """ + url = 'http://www.acronymfinder.com/' \ + 'af-query.asp?String=exact&Acronym=%s' % urllib.quote(acronym) + html = webutils.getUrl(url) + if 'daily limit' in html: + s = 'Acronymfinder.com says I\'ve reached my daily limit. Sorry.' + irc.error(s) + return + # The following definitions are stripped and empties are removed. + defs = filter(None, imap(str.strip, self._acronymre.findall(html))) + utils.sortBy(lambda s: not s.startswith('[not an acronym]'), defs) + for (i, s) in enumerate(defs): + if s.startswith('[not an acronym]'): + defs[i] = s.split('] ', 1)[1].strip() + if len(defs) == 0: + irc.reply('No definitions found.') + else: + s = ', or '.join(defs) + irc.reply('%s could be %s' % (acronym, s)) + acronym = wrap(acronym, ['text']) + + _netcraftre = re.compile(r'td align="left">\s+]+>(.*?) + + Returns Netcraft.com's determination of what operating system and + webserver is running on the host given. + """ + url = 'http://uptime.netcraft.com/up/graph/?host=%s' % hostname + html = webutils.getUrl(url) + m = self._netcraftre.search(html) + if m: + html = m.group(1) + s = utils.htmlToText(html, tagReplace='').strip() + s = s.rstrip('-').strip() + irc.reply(s) # Snip off "the site" + elif 'We could not get any results' in html: + irc.reply('No results found for %s.' % hostname) + else: + irc.error('The format of page the was odd.') + netcraft = wrap(netcraft, ['text']) + + def kernel(self, irc, msg, args): + """takes no arguments + + Returns information about the current version of the Linux kernel. + """ + fd = webutils.getUrlFd('http://kernel.org/kdist/finger_banner') + try: + stable = 'unknown' + snapshot = 'unknown' + mm = 'unknown' + for line in fd: + (name, version) = line.split(':') + if 'latest stable' in name: + stable = version.strip() + elif 'snapshot for the stable' in name: + snapshot = version.strip() + elif '-mm patch' in name: + mm = version.strip() + finally: + fd.close() + irc.reply('The latest stable kernel is %s; ' + 'the latest snapshot of the stable kernel is %s; ' + 'the latest beta kernel is %s.' % (stable, snapshot, mm)) + kernel = wrap(kernel) + + _pgpkeyre = re.compile(r'pub\s+\d{4}\w/' + r'([^<]+)[^>]+>([^<]+)') + def pgpkey(self, irc, msg, args, search): + """ + + Returns the results of querying pgp.mit.edu for keys that match + the . + """ + urlClean = search.replace(' ', '+') + host = 'http://pgp.mit.edu:11371' + url = '%s/pks/lookup?op=index&search=%s' % (host, urlClean) + fd = webutils.getUrlFd(url, headers={}) + try: + L = [] + for line in iter(fd.next, ''): + info = self._pgpkeyre.search(line) + if info: + L.append('%s <%s%s>' % (info.group(3),host,info.group(1))) + if len(L) == 0: + irc.reply('No results found for %s.' % search) + else: + s = 'Matches found for %s: %s' % (search, ' :: '.join(L)) + irc.reply(s) + finally: + fd.close() + pgpkey = wrap(pgpkey, ['text']) + + _filextre = re.compile( + r'Extension:.*?.*?\s+\s+(?:]+>([^<]+)\s+|([^<]+))\s+' + r'(?:]+>([^<]+)| + + Returns the results of querying filext.com for file extensions that + match . + """ + # XXX This probably ought to be handled in a converter from commands.py + invalid = '|<>\^=?/[]";,*' + for c in invalid: + if c in ext: + irc.error('\'%s\' is an invalid extension character' % c) + return + s = 'http://www.filext.com/detaillist.php?extdetail=%s&goButton=Go' + text = webutils.getUrl(s % ext) + matches = self._filextre.findall(text) + #print matches + res = [] + for match in matches: + (file1, file2, comp1, comp2) = match + if file1: + filetype = file1.strip() + else: + filetype = file2.strip() + if comp1: + company = comp1.strip() + else: + company = comp2.strip() + if company: + res.append('%s\'s %s' % (company, filetype)) + else: + res.append(filetype) + if res: + irc.reply(utils.commaAndify(res)) + else: + irc.error('No matching file extensions were found.') + extension = wrap(extension, ['text']) + + _zipinfore = re.compile(r'Latitude
\(([^)]+)\)Longitude
' + r'\(([^)]+)\).*?(.*?)', re.I) + _zipstatre = re.compile(r'(Only about \d+,\d{3} of.*?in use.)') + def zipinfo(self, irc, msg, args, zipcode): + """ + + Returns a plethora of information for the given . + """ + try: + int(zipcode) + except ValueError: + irc.error('Zip code must be a 5-digit integer.') + return + if len(zipcode) != 5: + irc.error('Zip code must be a 5-digit integer.') + return + url = 'http://zipinfo.com/cgi-local/zipsrch.exe?cnty=cnty&ac=ac&'\ + 'tz=tz&ll=ll&zip=%s&Go=Go' % zipcode + text = webutils.getUrl(url) + if 'daily usage limit' in text: + irc.error('I have exceeded the site\'s daily usage limit.') + return + m = self._zipstatre.search(text) + if m: + irc.reply('%s %s is not one of them.' % (m.group(1), zipcode)) + return + n = self._zipinfore.search(text) + if not n: + irc.error('Unable to retrieve information for that zip code.') + return + (latdir, longdir, rawinfo) = n.groups() + # Info consists of the following (whitespace separated): + # City, State Abbrev., Zip Code, County, FIPS Code, Area Code, Time + # Zone, Daylight Time(?), Latitude, Longitude + info = utils.htmlToText(rawinfo) + info = info.split() + zipindex = info.index(zipcode) + resp = ['City: %s' % ' '.join(info[:zipindex-1]), + 'State: %s' % info[zipindex-1], + 'County: %s' % ' '.join(info[zipindex+1:-6]), + 'Area Code: %s' % info[-5], + 'Time Zone: %s' % info[-4], + 'Daylight Savings: %s' % info[-3], + 'Latitude: %s (%s)' % (info[-2], latdir), + 'Longitude: %s (%s)' % (info[-1], longdir), + ] + irc.reply('; '.join(resp)) + zipinfo = wrap(zipinfo, ['text']) + + +Class = Http + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: