diff options
Diffstat (limited to 'modules')
-rwxr-xr-x | modules/admin.py | 74 | ||||
-rwxr-xr-x | modules/calc.py | 160 | ||||
-rwxr-xr-x | modules/clock.py | 494 | ||||
-rwxr-xr-x | modules/codepoints.py | 230 | ||||
-rwxr-xr-x | modules/etymology.py | 160 | ||||
-rwxr-xr-x | modules/head.py | 322 | ||||
-rwxr-xr-x | modules/info.py | 118 | ||||
-rwxr-xr-x | modules/oblique.py | 190 | ||||
-rwxr-xr-x | modules/ping.py | 16 | ||||
-rwxr-xr-x | modules/reload.py | 76 | ||||
-rwxr-xr-x | modules/remind.py | 292 | ||||
-rwxr-xr-x | modules/search.py | 414 | ||||
-rwxr-xr-x | modules/seen.py | 68 | ||||
-rwxr-xr-x | modules/startup.py | 92 | ||||
-rwxr-xr-x | modules/tell.py | 250 | ||||
-rwxr-xr-x | modules/translate.py | 206 | ||||
-rwxr-xr-x | modules/twitter.py | 106 | ||||
-rwxr-xr-x | modules/validate.py | 58 | ||||
-rwxr-xr-x | modules/weather.py | 786 | ||||
-rwxr-xr-x | modules/wikipedia.py | 306 | ||||
-rwxr-xr-x | modules/wiktionary.py | 140 |
21 files changed, 2279 insertions, 2279 deletions
diff --git a/modules/admin.py b/modules/admin.py index b42822a..93f8448 100755 --- a/modules/admin.py +++ b/modules/admin.py @@ -7,57 +7,57 @@ Licensed under the Eiffel Forum License 2. http://inamidst.com/phenny/ """ -def join(phenny, input): - """Join the specified channel. This is an admin-only command.""" - # Can only be done in privmsg by an admin - if input.sender.startswith('#'): return - if input.admin: - channel, key = input.group(1), input.group(2) - if not key: - phenny.write(['JOIN'], channel) - else: phenny.write(['JOIN', channel, key]) +def join(phenny, input): + """Join the specified channel. This is an admin-only command.""" + # Can only be done in privmsg by an admin + if input.sender.startswith('#'): return + if input.admin: + channel, key = input.group(1), input.group(2) + if not key: + phenny.write(['JOIN'], channel) + else: phenny.write(['JOIN', channel, key]) join.rule = r'\.join (#\S+)(?: *(\S+))?' join.priority = 'low' join.example = '.join #example or .join #example key' -def part(phenny, input): - """Part the specified channel. This is an admin-only command.""" - # Can only be done in privmsg by an admin - if input.sender.startswith('#'): return - if input.admin: - phenny.write(['PART'], input.group(2)) +def part(phenny, input): + """Part the specified channel. This is an admin-only command.""" + # Can only be done in privmsg by an admin + if input.sender.startswith('#'): return + if input.admin: + phenny.write(['PART'], input.group(2)) part.commands = ['part'] part.priority = 'low' part.example = '.part #example' -def quit(phenny, input): - """Quit from the server. This is an owner-only command.""" - # Can only be done in privmsg by the owner - if input.sender.startswith('#'): return - if input.owner: - phenny.write(['QUIT']) - __import__('os')._exit(0) +def quit(phenny, input): + """Quit from the server. This is an owner-only command.""" + # Can only be done in privmsg by the owner + if input.sender.startswith('#'): return + if input.owner: + phenny.write(['QUIT']) + __import__('os')._exit(0) quit.commands = ['quit'] quit.priority = 'low' -def msg(phenny, input): - # Can only be done in privmsg by an admin - if input.sender.startswith('#'): return - a, b = input.group(2), input.group(3) - if (not a) or (not b): return - if input.admin: - phenny.msg(a, b) +def msg(phenny, input): + # Can only be done in privmsg by an admin + if input.sender.startswith('#'): return + a, b = input.group(2), input.group(3) + if (not a) or (not b): return + if input.admin: + phenny.msg(a, b) msg.rule = (['msg'], r'(#?\S+) (.+)') msg.priority = 'low' -def me(phenny, input): - # Can only be done in privmsg by an admin - if input.sender.startswith('#'): return - if input.admin: - msg = '\x01ACTION %s\x01' % input.group(3) - phenny.msg(input.group(2) or input.sender, msg) +def me(phenny, input): + # Can only be done in privmsg by an admin + if input.sender.startswith('#'): return + if input.admin: + msg = '\x01ACTION %s\x01' % input.group(3) + phenny.msg(input.group(2) or input.sender, msg) me.rule = (['me'], r'(#?\S+) (.+)') me.priority = 'low' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/calc.py b/modules/calc.py index 9a5b187..b58b730 100755 --- a/modules/calc.py +++ b/modules/calc.py @@ -15,101 +15,101 @@ r_result = re.compile(r'(?i)<A NAME=results>(.*?)</A>') r_tag = re.compile(r'<\S+.*?>') subs = [ - (' in ', ' -> '), - (' over ', ' / '), - (u'£', 'GBP '), - (u'€', 'EUR '), - ('\$', 'USD '), - (r'\bKB\b', 'kilobytes'), - (r'\bMB\b', 'megabytes'), - (r'\bGB\b', 'kilobytes'), - ('kbps', '(kilobits / second)'), - ('mbps', '(megabits / second)') + (' in ', ' -> '), + (' over ', ' / '), + (u'£', 'GBP '), + (u'€', 'EUR '), + ('\$', 'USD '), + (r'\bKB\b', 'kilobytes'), + (r'\bMB\b', 'megabytes'), + (r'\bGB\b', 'kilobytes'), + ('kbps', '(kilobits / second)'), + ('mbps', '(megabits / second)') ] -def calc(phenny, input): - """Use the Frink online calculator.""" - q = input.group(2) - if not q: - return phenny.say('0?') +def calc(phenny, input): + """Use the Frink online calculator.""" + q = input.group(2) + if not q: + return phenny.say('0?') - query = q[:] - for a, b in subs: - query = re.sub(a, b, query) - query = query.rstrip(' \t') + query = q[:] + for a, b in subs: + query = re.sub(a, b, query) + query = query.rstrip(' \t') - precision = 5 - if query[-3:] in ('GBP', 'USD', 'EUR', 'NOK'): - precision = 2 - query = web.urllib.quote(query.encode('utf-8')) + precision = 5 + if query[-3:] in ('GBP', 'USD', 'EUR', 'NOK'): + precision = 2 + query = web.urllib.quote(query.encode('utf-8')) - uri = 'http://futureboy.us/fsp/frink.fsp?fromVal=' - bytes = web.get(uri + query) - m = r_result.search(bytes) - if m: - result = m.group(1) - result = r_tag.sub('', result) # strip span.warning tags - result = result.replace('>', '>') - result = result.replace('(undefined symbol)', '(?) ') + uri = 'http://futureboy.us/fsp/frink.fsp?fromVal=' + bytes = web.get(uri + query) + m = r_result.search(bytes) + if m: + result = m.group(1) + result = r_tag.sub('', result) # strip span.warning tags + result = result.replace('>', '>') + result = result.replace('(undefined symbol)', '(?) ') - if '.' in result: - try: result = str(round(float(result), precision)) - except ValueError: pass + if '.' in result: + try: result = str(round(float(result), precision)) + except ValueError: pass - if not result.strip(): - result = '?' - elif ' in ' in q: - result += ' ' + q.split(' in ', 1)[1] + if not result.strip(): + result = '?' + elif ' in ' in q: + result += ' ' + q.split(' in ', 1)[1] - phenny.say(q + ' = ' + result[:350]) - else: phenny.reply("Sorry, can't calculate that.") - phenny.say('Note that .calc is deprecated, consider using .c') + phenny.say(q + ' = ' + result[:350]) + else: phenny.reply("Sorry, can't calculate that.") + phenny.say('Note that .calc is deprecated, consider using .c') calc.commands = ['calc'] calc.example = '.calc 5 + 3' -def c(phenny, input): - """Google calculator.""" - if not input.group(2): - return phenny.reply("Nothing to calculate.") - q = input.group(2).encode('utf-8') - q = q.replace('\xcf\x95', 'phi') # utf-8 U+03D5 - q = q.replace('\xcf\x80', 'pi') # utf-8 U+03C0 - uri = 'http://www.google.com/ig/calculator?q=' - bytes = web.get(uri + web.urllib.quote(q)) - parts = bytes.split('",') - answer = [p for p in parts if p.startswith('rhs: "')][0][6:] - if answer: - answer = answer.decode('unicode-escape') - answer = ''.join(chr(ord(c)) for c in answer) - answer = answer.decode('utf-8') - answer = answer.replace(u'\xc2\xa0', ',') - answer = answer.replace('<sup>', '^(') - answer = answer.replace('</sup>', ')') - answer = web.decode(answer) - phenny.say(answer) - else: phenny.say('Sorry, no result.') +def c(phenny, input): + """Google calculator.""" + if not input.group(2): + return phenny.reply("Nothing to calculate.") + q = input.group(2).encode('utf-8') + q = q.replace('\xcf\x95', 'phi') # utf-8 U+03D5 + q = q.replace('\xcf\x80', 'pi') # utf-8 U+03C0 + uri = 'http://www.google.com/ig/calculator?q=' + bytes = web.get(uri + web.urllib.quote(q)) + parts = bytes.split('",') + answer = [p for p in parts if p.startswith('rhs: "')][0][6:] + if answer: + answer = answer.decode('unicode-escape') + answer = ''.join(chr(ord(c)) for c in answer) + answer = answer.decode('utf-8') + answer = answer.replace(u'\xc2\xa0', ',') + answer = answer.replace('<sup>', '^(') + answer = answer.replace('</sup>', ')') + answer = web.decode(answer) + phenny.say(answer) + else: phenny.say('Sorry, no result.') c.commands = ['c'] c.example = '.c 5 + 3' -def py(phenny, input): - query = input.group(2).encode('utf-8') - uri = 'http://tumbolia.appspot.com/py/' - answer = web.get(uri + web.urllib.quote(query)) - if answer: - phenny.say(answer) - else: phenny.reply('Sorry, no result.') +def py(phenny, input): + query = input.group(2).encode('utf-8') + uri = 'http://tumbolia.appspot.com/py/' + answer = web.get(uri + web.urllib.quote(query)) + if answer: + phenny.say(answer) + else: phenny.reply('Sorry, no result.') py.commands = ['py'] -def wa(phenny, input): - if not input.group(2): - return phenny.reply("No search term.") - query = input.group(2).encode('utf-8') - uri = 'http://tumbolia.appspot.com/wa/' - answer = web.get(uri + web.urllib.quote(query.replace('+', '%2B'))) - if answer: - phenny.say(answer) - else: phenny.reply('Sorry, no result.') +def wa(phenny, input): + if not input.group(2): + return phenny.reply("No search term.") + query = input.group(2).encode('utf-8') + uri = 'http://tumbolia.appspot.com/wa/' + answer = web.get(uri + web.urllib.quote(query.replace('+', '%2B'))) + if answer: + phenny.say(answer) + else: phenny.reply('Sorry, no result.') wa.commands = ['wa'] -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/clock.py b/modules/clock.py index 91f2d5b..e0c237c 100755 --- a/modules/clock.py +++ b/modules/clock.py @@ -11,180 +11,180 @@ import re, math, time, urllib, locale, socket, struct, datetime from decimal import Decimal as dec from tools import deprecated -TimeZones = {'KST': 9, 'CADT': 10.5, 'EETDST': 3, 'MESZ': 2, 'WADT': 9, - 'EET': 2, 'MST': -7, 'WAST': 8, 'IST': 5.5, 'B': 2, - 'MSK': 3, 'X': -11, 'MSD': 4, 'CETDST': 2, 'AST': -4, - 'HKT': 8, 'JST': 9, 'CAST': 9.5, 'CET': 1, 'CEST': 2, - 'EEST': 3, 'EAST': 10, 'METDST': 2, 'MDT': -6, 'A': 1, - 'UTC': 0, 'ADT': -3, 'EST': -5, 'E': 5, 'D': 4, 'G': 7, - 'F': 6, 'I': 9, 'H': 8, 'K': 10, 'PDT': -7, 'M': 12, - 'L': 11, 'O': -2, 'MEST': 2, 'Q': -4, 'P': -3, 'S': -6, - 'R': -5, 'U': -8, 'T': -7, 'W': -10, 'WET': 0, 'Y': -12, - 'CST': -6, 'EADT': 11, 'Z': 0, 'GMT': 0, 'WETDST': 1, - 'C': 3, 'WEST': 1, 'CDT': -5, 'MET': 1, 'N': -1, 'V': -9, - 'EDT': -4, 'UT': 0, 'PST': -8, 'MEZ': 1, 'BST': 1, - 'ACS': 9.5, 'ATL': -4, 'ALA': -9, 'HAW': -10, 'AKDT': -8, - 'AKST': -9, - 'BDST': 2} +TimeZones = {'KST': 9, 'CADT': 10.5, 'EETDST': 3, 'MESZ': 2, 'WADT': 9, + 'EET': 2, 'MST': -7, 'WAST': 8, 'IST': 5.5, 'B': 2, + 'MSK': 3, 'X': -11, 'MSD': 4, 'CETDST': 2, 'AST': -4, + 'HKT': 8, 'JST': 9, 'CAST': 9.5, 'CET': 1, 'CEST': 2, + 'EEST': 3, 'EAST': 10, 'METDST': 2, 'MDT': -6, 'A': 1, + 'UTC': 0, 'ADT': -3, 'EST': -5, 'E': 5, 'D': 4, 'G': 7, + 'F': 6, 'I': 9, 'H': 8, 'K': 10, 'PDT': -7, 'M': 12, + 'L': 11, 'O': -2, 'MEST': 2, 'Q': -4, 'P': -3, 'S': -6, + 'R': -5, 'U': -8, 'T': -7, 'W': -10, 'WET': 0, 'Y': -12, + 'CST': -6, 'EADT': 11, 'Z': 0, 'GMT': 0, 'WETDST': 1, + 'C': 3, 'WEST': 1, 'CDT': -5, 'MET': 1, 'N': -1, 'V': -9, + 'EDT': -4, 'UT': 0, 'PST': -8, 'MEZ': 1, 'BST': 1, + 'ACS': 9.5, 'ATL': -4, 'ALA': -9, 'HAW': -10, 'AKDT': -8, + 'AKST': -9, + 'BDST': 2} TZ1 = { - 'NDT': -2.5, - 'BRST': -2, - 'ADT': -3, - 'EDT': -4, - 'CDT': -5, - 'MDT': -6, - 'PDT': -7, - 'YDT': -8, - 'HDT': -9, - 'BST': 1, - 'MEST': 2, - 'SST': 2, - 'FST': 2, - 'CEST': 2, - 'EEST': 3, - 'WADT': 8, - 'KDT': 10, - 'EADT': 13, - 'NZD': 13, - 'NZDT': 13, - 'GMT': 0, - 'UT': 0, - 'UTC': 0, - 'WET': 0, - 'WAT': -1, - 'AT': -2, - 'FNT': -2, - 'BRT': -3, - 'MNT': -4, - 'EWT': -4, - 'AST': -4, - 'EST': -5, - 'ACT': -5, - 'CST': -6, - 'MST': -7, - 'PST': -8, - 'YST': -9, - 'HST': -10, - 'CAT': -10, - 'AHST': -10, - 'NT': -11, - 'IDLW': -12, - 'CET': 1, - 'MEZ': 1, - 'ECT': 1, - 'MET': 1, - 'MEWT': 1, - 'SWT': 1, - 'SET': 1, - 'FWT': 1, - 'EET': 2, - 'UKR': 2, - 'BT': 3, - 'ZP4': 4, - 'ZP5': 5, - 'ZP6': 6, - 'WST': 8, - 'HKT': 8, - 'CCT': 8, - 'JST': 9, - 'KST': 9, - 'EAST': 10, - 'GST': 10, - 'NZT': 12, - 'NZST': 12, + 'NDT': -2.5, + 'BRST': -2, + 'ADT': -3, + 'EDT': -4, + 'CDT': -5, + 'MDT': -6, + 'PDT': -7, + 'YDT': -8, + 'HDT': -9, + 'BST': 1, + 'MEST': 2, + 'SST': 2, + 'FST': 2, + 'CEST': 2, + 'EEST': 3, + 'WADT': 8, + 'KDT': 10, + 'EADT': 13, + 'NZD': 13, + 'NZDT': 13, + 'GMT': 0, + 'UT': 0, + 'UTC': 0, + 'WET': 0, + 'WAT': -1, + 'AT': -2, + 'FNT': -2, + 'BRT': -3, + 'MNT': -4, + 'EWT': -4, + 'AST': -4, + 'EST': -5, + 'ACT': -5, + 'CST': -6, + 'MST': -7, + 'PST': -8, + 'YST': -9, + 'HST': -10, + 'CAT': -10, + 'AHST': -10, + 'NT': -11, + 'IDLW': -12, + 'CET': 1, + 'MEZ': 1, + 'ECT': 1, + 'MET': 1, + 'MEWT': 1, + 'SWT': 1, + 'SET': 1, + 'FWT': 1, + 'EET': 2, + 'UKR': 2, + 'BT': 3, + 'ZP4': 4, + 'ZP5': 5, + 'ZP6': 6, + 'WST': 8, + 'HKT': 8, + 'CCT': 8, + 'JST': 9, + 'KST': 9, + 'EAST': 10, + 'GST': 10, + 'NZT': 12, + 'NZST': 12, 'IDLE': 12 } TZ2 = { - 'ACDT': 10.5, - 'ACST': 9.5, - 'ADT': 3, + 'ACDT': 10.5, + 'ACST': 9.5, + 'ADT': 3, 'AEDT': 11, # hmm 'AEST': 10, # hmm - 'AHDT': 9, - 'AHST': 10, - 'AST': 4, - 'AT': 2, - 'AWDT': -9, - 'AWST': -8, - 'BAT': -3, - 'BDST': -2, - 'BET': 11, - 'BST': -1, - 'BT': -3, - 'BZT2': 3, - 'CADT': -10.5, - 'CAST': -9.5, - 'CAT': 10, - 'CCT': -8, - # 'CDT': 5, - 'CED': -2, - 'CET': -1, - 'CST': 6, - 'EAST': -10, - # 'EDT': 4, - 'EED': -3, - 'EET': -2, - 'EEST': -3, - 'EST': 5, - 'FST': -2, - 'FWT': -1, - 'GMT': 0, - 'GST': -10, - 'HDT': 9, - 'HST': 10, - 'IDLE': -12, - 'IDLW': 12, - # 'IST': -5.5, - 'IT': -3.5, - 'JST': -9, - 'JT': -7, - 'KST': -9, - 'MDT': 6, - 'MED': -2, - 'MET': -1, - 'MEST': -2, - 'MEWT': -1, - 'MST': 7, - 'MT': -8, - 'NDT': 2.5, - 'NFT': 3.5, - 'NT': 11, - 'NST': -6.5, - 'NZ': -11, - 'NZST': -12, - 'NZDT': -13, - 'NZT': -12, - # 'PDT': 7, - 'PST': 8, - 'ROK': -9, - 'SAD': -10, - 'SAST': -9, - 'SAT': -9, - 'SDT': -10, - 'SST': -2, - 'SWT': -1, - 'USZ3': -4, - 'USZ4': -5, - 'USZ5': -6, - 'USZ6': -7, - 'UT': 0, - 'UTC': 0, - 'UZ10': -11, - 'WAT': 1, - 'WET': 0, - 'WST': -8, - 'YDT': 8, - 'YST': 9, - 'ZP4': -4, - 'ZP5': -5, + 'AHDT': 9, + 'AHST': 10, + 'AST': 4, + 'AT': 2, + 'AWDT': -9, + 'AWST': -8, + 'BAT': -3, + 'BDST': -2, + 'BET': 11, + 'BST': -1, + 'BT': -3, + 'BZT2': 3, + 'CADT': -10.5, + 'CAST': -9.5, + 'CAT': 10, + 'CCT': -8, + # 'CDT': 5, + 'CED': -2, + 'CET': -1, + 'CST': 6, + 'EAST': -10, + # 'EDT': 4, + 'EED': -3, + 'EET': -2, + 'EEST': -3, + 'EST': 5, + 'FST': -2, + 'FWT': -1, + 'GMT': 0, + 'GST': -10, + 'HDT': 9, + 'HST': 10, + 'IDLE': -12, + 'IDLW': 12, + # 'IST': -5.5, + 'IT': -3.5, + 'JST': -9, + 'JT': -7, + 'KST': -9, + 'MDT': 6, + 'MED': -2, + 'MET': -1, + 'MEST': -2, + 'MEWT': -1, + 'MST': 7, + 'MT': -8, + 'NDT': 2.5, + 'NFT': 3.5, + 'NT': 11, + 'NST': -6.5, + 'NZ': -11, + 'NZST': -12, + 'NZDT': -13, + 'NZT': -12, + # 'PDT': 7, + 'PST': 8, + 'ROK': -9, + 'SAD': -10, + 'SAST': -9, + 'SAT': -9, + 'SDT': -10, + 'SST': -2, + 'SWT': -1, + 'USZ3': -4, + 'USZ4': -5, + 'USZ5': -6, + 'USZ6': -7, + 'UT': 0, + 'UTC': 0, + 'UZ10': -11, + 'WAT': 1, + 'WET': 0, + 'WST': -8, + 'YDT': 8, + 'YST': 9, + 'ZP4': -4, + 'ZP5': -5, 'ZP6': -6 } TZ3 = { - 'AEST': 10, - 'AEDT': 11 + 'AEST': 10, + 'AEDT': 11 } # TimeZones.update(TZ2) # do these have to be negated? @@ -194,109 +194,109 @@ TimeZones.update(TZ3) r_local = re.compile(r'\([a-z]+_[A-Z]+\)') @deprecated -def f_time(self, origin, match, args): - """Returns the current time.""" - tz = match.group(2) or 'GMT' +def f_time(self, origin, match, args): + """Returns the current time.""" + tz = match.group(2) or 'GMT' - # Personal time zones, because they're rad - if hasattr(self.config, 'timezones'): - People = self.config.timezones - else: People = {} + # Personal time zones, because they're rad + if hasattr(self.config, 'timezones'): + People = self.config.timezones + else: People = {} - if People.has_key(tz): - tz = People[tz] - elif (not match.group(2)) and People.has_key(origin.nick): - tz = People[origin.nick] + if People.has_key(tz): + tz = People[tz] + elif (not match.group(2)) and People.has_key(origin.nick): + tz = People[origin.nick] - TZ = tz.upper() - if len(tz) > 30: return + TZ = tz.upper() + if len(tz) > 30: return - if (TZ == 'UTC') or (TZ == 'Z'): - msg = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) - self.msg(origin.sender, msg) - elif r_local.match(tz): # thanks to Mark Shoulsdon (clsn) - locale.setlocale(locale.LC_TIME, (tz[1:-1], 'UTF-8')) - msg = time.strftime("%A, %d %B %Y %H:%M:%SZ", time.gmtime()) - self.msg(origin.sender, msg) - elif TimeZones.has_key(TZ): - offset = TimeZones[TZ] * 3600 - timenow = time.gmtime(time.time() + offset) - msg = time.strftime("%a, %d %b %Y %H:%M:%S " + str(TZ), timenow) - self.msg(origin.sender, msg) - elif tz and tz[0] in ('+', '-') and 4 <= len(tz) <= 6: - timenow = time.gmtime(time.time() + (int(tz[:3]) * 3600)) - msg = time.strftime("%a, %d %b %Y %H:%M:%S " + str(tz), timenow) - self.msg(origin.sender, msg) - else: - try: t = float(tz) - except ValueError: - import os, re, subprocess - r_tz = re.compile(r'^[A-Za-z]+(?:/[A-Za-z_]+)*$') - if r_tz.match(tz) and os.path.isfile('/usr/share/zoneinfo/' + tz): - cmd, PIPE = 'TZ=%s date' % tz, subprocess.PIPE - proc = subprocess.Popen(cmd, shell=True, stdout=PIPE) - self.msg(origin.sender, proc.communicate()[0]) - else: - error = "Sorry, I don't know about the '%s' timezone." % tz - self.msg(origin.sender, origin.nick + ': ' + error) - else: - timenow = time.gmtime(time.time() + (t * 3600)) - msg = time.strftime("%a, %d %b %Y %H:%M:%S " + str(tz), timenow) - self.msg(origin.sender, msg) + if (TZ == 'UTC') or (TZ == 'Z'): + msg = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) + self.msg(origin.sender, msg) + elif r_local.match(tz): # thanks to Mark Shoulsdon (clsn) + locale.setlocale(locale.LC_TIME, (tz[1:-1], 'UTF-8')) + msg = time.strftime("%A, %d %B %Y %H:%M:%SZ", time.gmtime()) + self.msg(origin.sender, msg) + elif TimeZones.has_key(TZ): + offset = TimeZones[TZ] * 3600 + timenow = time.gmtime(time.time() + offset) + msg = time.strftime("%a, %d %b %Y %H:%M:%S " + str(TZ), timenow) + self.msg(origin.sender, msg) + elif tz and tz[0] in ('+', '-') and 4 <= len(tz) <= 6: + timenow = time.gmtime(time.time() + (int(tz[:3]) * 3600)) + msg = time.strftime("%a, %d %b %Y %H:%M:%S " + str(tz), timenow) + self.msg(origin.sender, msg) + else: + try: t = float(tz) + except ValueError: + import os, re, subprocess + r_tz = re.compile(r'^[A-Za-z]+(?:/[A-Za-z_]+)*$') + if r_tz.match(tz) and os.path.isfile('/usr/share/zoneinfo/' + tz): + cmd, PIPE = 'TZ=%s date' % tz, subprocess.PIPE + proc = subprocess.Popen(cmd, shell=True, stdout=PIPE) + self.msg(origin.sender, proc.communicate()[0]) + else: + error = "Sorry, I don't know about the '%s' timezone." % tz + self.msg(origin.sender, origin.nick + ': ' + error) + else: + timenow = time.gmtime(time.time() + (t * 3600)) + msg = time.strftime("%a, %d %b %Y %H:%M:%S " + str(tz), timenow) + self.msg(origin.sender, msg) f_time.commands = ['t'] f_time.name = 't' f_time.example = '.t UTC' -def beats(phenny, input): - """Shows the internet time in Swatch beats.""" - beats = ((time.time() + 3600) % 86400) / 86.4 - beats = int(math.floor(beats)) - phenny.say('@%03i' % beats) +def beats(phenny, input): + """Shows the internet time in Swatch beats.""" + beats = ((time.time() + 3600) % 86400) / 86.4 + beats = int(math.floor(beats)) + phenny.say('@%03i' % beats) beats.commands = ['beats'] beats.priority = 'low' -def divide(input, by): - return (input / by), (input % by) +def divide(input, by): + return (input / by), (input % by) -def yi(phenny, input): - """Shows whether it is currently yi or not.""" - quadraels, remainder = divide(int(time.time()), 1753200) - raels = quadraels * 4 - extraraels, remainder = divide(remainder, 432000) - if extraraels == 4: - return phenny.say('Yes! PARTAI!') - else: phenny.say('Not yet...') +def yi(phenny, input): + """Shows whether it is currently yi or not.""" + quadraels, remainder = divide(int(time.time()), 1753200) + raels = quadraels * 4 + extraraels, remainder = divide(remainder, 432000) + if extraraels == 4: + return phenny.say('Yes! PARTAI!') + else: phenny.say('Not yet...') yi.commands = ['yi'] yi.priority = 'low' -def tock(phenny, input): - """Shows the time from the USNO's atomic clock.""" - u = urllib.urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl') - info = u.info() - u.close() - phenny.say('"' + info['Date'] + '" - tycho.usno.navy.mil') +def tock(phenny, input): + """Shows the time from the USNO's atomic clock.""" + u = urllib.urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl') + info = u.info() + u.close() + phenny.say('"' + info['Date'] + '" - tycho.usno.navy.mil') tock.commands = ['tock'] tock.priority = 'high' -def npl(phenny, input): - """Shows the time from NPL's SNTP server.""" - # for server in ('ntp1.npl.co.uk', 'ntp2.npl.co.uk'): - client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - client.sendto('\x1b' + 47 * '\0', ('ntp1.npl.co.uk', 123)) - data, address = client.recvfrom(1024) - if data: - buf = struct.unpack('B' * 48, data) - d = dec('0.0') - for i in range(8): - d += dec(buf[32 + i]) * dec(str(math.pow(2, (3 - i) * 8))) - d -= dec(2208988800L) - a, b = str(d).split('.') - f = '%Y-%m-%d %H:%M:%S' - result = datetime.datetime.fromtimestamp(d).strftime(f) + '.' + b[:6] - phenny.say(result + ' - ntp1.npl.co.uk') - else: phenny.say('No data received, sorry') +def npl(phenny, input): + """Shows the time from NPL's SNTP server.""" + # for server in ('ntp1.npl.co.uk', 'ntp2.npl.co.uk'): + client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + client.sendto('\x1b' + 47 * '\0', ('ntp1.npl.co.uk', 123)) + data, address = client.recvfrom(1024) + if data: + buf = struct.unpack('B' * 48, data) + d = dec('0.0') + for i in range(8): + d += dec(buf[32 + i]) * dec(str(math.pow(2, (3 - i) * 8))) + d -= dec(2208988800L) + a, b = str(d).split('.') + f = '%Y-%m-%d %H:%M:%S' + result = datetime.datetime.fromtimestamp(d).strftime(f) + '.' + b[:6] + phenny.say(result + ' - ntp1.npl.co.uk') + else: phenny.say('No data received, sorry') npl.commands = ['npl'] npl.priority = 'high' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/codepoints.py b/modules/codepoints.py index eb9c8bf..a83d292 100755 --- a/modules/codepoints.py +++ b/modules/codepoints.py @@ -10,125 +10,125 @@ http://inamidst.com/phenny/ import re, unicodedata from itertools import islice -def about(u, cp=None, name=None): - if cp is None: - cp = ord(u) - if name is None: - try: name = unicodedata.name(u) - except ValueError: - return 'U+%04X (No name found)' % cp - - if not unicodedata.combining(u): - template = 'U+%04X %s (%s)' - else: template = 'U+%04X %s (\xe2\x97\x8c%s)' - return template % (cp, name, u.encode('utf-8')) - -def codepoint_simple(arg): - arg = arg.upper() - - r_label = re.compile('\\b' + arg.replace(' ', '.*\\b') + '\\b') - - results = [] - for cp in xrange(0xFFFF): - u = unichr(cp) - try: name = unicodedata.name(u) - except ValueError: continue - - if r_label.search(name): - results.append((len(name), u, cp, name)) - if not results: - r_label = re.compile('\\b' + arg.replace(' ', '.*\\b')) - for cp in xrange(0xFFFF): - u = unichr(cp) - try: name = unicodedata.name(u) - except ValueError: continue - - if r_label.search(name): +def about(u, cp=None, name=None): + if cp is None: + cp = ord(u) + if name is None: + try: name = unicodedata.name(u) + except ValueError: + return 'U+%04X (No name found)' % cp + + if not unicodedata.combining(u): + template = 'U+%04X %s (%s)' + else: template = 'U+%04X %s (\xe2\x97\x8c%s)' + return template % (cp, name, u.encode('utf-8')) + +def codepoint_simple(arg): + arg = arg.upper() + + r_label = re.compile('\\b' + arg.replace(' ', '.*\\b') + '\\b') + + results = [] + for cp in xrange(0xFFFF): + u = unichr(cp) + try: name = unicodedata.name(u) + except ValueError: continue + + if r_label.search(name): results.append((len(name), u, cp, name)) - - if not results: - return None - - length, u, cp, name = sorted(results)[0] - return about(u, cp, name) - -def codepoint_extended(arg): - arg = arg.upper() - try: r_search = re.compile(arg) - except: raise ValueError('Broken regexp: %r' % arg) - - for cp in xrange(1, 0x10FFFF): - u = unichr(cp) - name = unicodedata.name(u, '-') - - if r_search.search(name): - yield about(u, cp, name) - -def u(phenny, input): - """Look up unicode information.""" - arg = input.bytes[3:] - # phenny.msg('#inamidst', '%r' % arg) - if not arg: - return phenny.reply('You gave me zero length input.') - elif not arg.strip(' '): - if len(arg) > 1: return phenny.reply('%s SPACEs (U+0020)' % len(arg)) - return phenny.reply('1 SPACE (U+0020)') - - # @@ space - if set(arg.upper()) - set( - 'ABCDEFGHIJKLMNOPQRSTUVWYXYZ0123456789- .?+*{}[]\\/^$'): - printable = False - elif len(arg) > 1: - printable = True - else: printable = False - - if printable: - extended = False - for c in '.?+*{}[]\\/^$': - if c in arg: - extended = True - break - - if len(arg) == 4: - try: u = unichr(int(arg, 16)) - except ValueError: pass - else: return phenny.say(about(u)) - - if extended: - # look up a codepoint with regexp - results = list(islice(codepoint_extended(arg), 4)) - for i, result in enumerate(results): - if (i < 2) or ((i == 2) and (len(results) < 4)): - phenny.say(result) - elif (i == 2) and (len(results) > 3): - phenny.say(result + ' [...]') - if not results: - phenny.reply('Sorry, no results') - else: - # look up a codepoint freely - result = codepoint_simple(arg) - if result is not None: - phenny.say(result) - else: phenny.reply("Sorry, no results for %r." % arg) - else: - text = arg.decode('utf-8') - # look up less than three podecoints - if len(text) <= 3: - for u in text: - phenny.say(about(u)) - # look up more than three podecoints - elif len(text) <= 10: - phenny.reply(' '.join('U+%04X' % ord(c) for c in text)) - else: phenny.reply('Sorry, your input is too long!') + if not results: + r_label = re.compile('\\b' + arg.replace(' ', '.*\\b')) + for cp in xrange(0xFFFF): + u = unichr(cp) + try: name = unicodedata.name(u) + except ValueError: continue + + if r_label.search(name): + results.append((len(name), u, cp, name)) + + if not results: + return None + + length, u, cp, name = sorted(results)[0] + return about(u, cp, name) + +def codepoint_extended(arg): + arg = arg.upper() + try: r_search = re.compile(arg) + except: raise ValueError('Broken regexp: %r' % arg) + + for cp in xrange(1, 0x10FFFF): + u = unichr(cp) + name = unicodedata.name(u, '-') + + if r_search.search(name): + yield about(u, cp, name) + +def u(phenny, input): + """Look up unicode information.""" + arg = input.bytes[3:] + # phenny.msg('#inamidst', '%r' % arg) + if not arg: + return phenny.reply('You gave me zero length input.') + elif not arg.strip(' '): + if len(arg) > 1: return phenny.reply('%s SPACEs (U+0020)' % len(arg)) + return phenny.reply('1 SPACE (U+0020)') + + # @@ space + if set(arg.upper()) - set( + 'ABCDEFGHIJKLMNOPQRSTUVWYXYZ0123456789- .?+*{}[]\\/^$'): + printable = False + elif len(arg) > 1: + printable = True + else: printable = False + + if printable: + extended = False + for c in '.?+*{}[]\\/^$': + if c in arg: + extended = True + break + + if len(arg) == 4: + try: u = unichr(int(arg, 16)) + except ValueError: pass + else: return phenny.say(about(u)) + + if extended: + # look up a codepoint with regexp + results = list(islice(codepoint_extended(arg), 4)) + for i, result in enumerate(results): + if (i < 2) or ((i == 2) and (len(results) < 4)): + phenny.say(result) + elif (i == 2) and (len(results) > 3): + phenny.say(result + ' [...]') + if not results: + phenny.reply('Sorry, no results') + else: + # look up a codepoint freely + result = codepoint_simple(arg) + if result is not None: + phenny.say(result) + else: phenny.reply("Sorry, no results for %r." % arg) + else: + text = arg.decode('utf-8') + # look up less than three podecoints + if len(text) <= 3: + for u in text: + phenny.say(about(u)) + # look up more than three podecoints + elif len(text) <= 10: + phenny.reply(' '.join('U+%04X' % ord(c) for c in text)) + else: phenny.reply('Sorry, your input is too long!') u.commands = ['u'] u.example = '.u 203D' -def bytes(phenny, input): - """Show the input as pretty printed bytes.""" - b = input.bytes - phenny.reply('%r' % b[b.find(' ') + 1:]) +def bytes(phenny, input): + """Show the input as pretty printed bytes.""" + b = input.bytes + phenny.reply('%r' % b[b.find(' ') + 1:]) bytes.commands = ['bytes'] bytes.example = '.bytes \xe3\x8b\xa1' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/etymology.py b/modules/etymology.py index cc93cfe..9edd218 100755 --- a/modules/etymology.py +++ b/modules/etymology.py @@ -19,96 +19,96 @@ r_definition = re.compile(r'(?ims)<dd[^>]*>.*?</dd>') r_tag = re.compile(r'<(?!!)[^>]+>') r_whitespace = re.compile(r'[\t\r\n ]+') -class Grab(urllib.URLopener): - def __init__(self, *args): - self.version = 'Mozilla/5.0 (Phenny)' - urllib.URLopener.__init__(self, *args) - def http_error_default(self, url, fp, errcode, errmsg, headers): - return urllib.addinfourl(fp, [headers, errcode], "http:" + url) +class Grab(urllib.URLopener): + def __init__(self, *args): + self.version = 'Mozilla/5.0 (Phenny)' + urllib.URLopener.__init__(self, *args) + def http_error_default(self, url, fp, errcode, errmsg, headers): + return urllib.addinfourl(fp, [headers, errcode], "http:" + url) abbrs = [ - 'cf', 'lit', 'etc', 'Ger', 'Du', 'Skt', 'Rus', 'Eng', 'Amer.Eng', 'Sp', - 'Fr', 'N', 'E', 'S', 'W', 'L', 'Gen', 'J.C', 'dial', 'Gk', - '19c', '18c', '17c', '16c', 'St', 'Capt', 'obs', 'Jan', 'Feb', 'Mar', - 'Apr', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'c', 'tr', 'e', 'g' + 'cf', 'lit', 'etc', 'Ger', 'Du', 'Skt', 'Rus', 'Eng', 'Amer.Eng', 'Sp', + 'Fr', 'N', 'E', 'S', 'W', 'L', 'Gen', 'J.C', 'dial', 'Gk', + '19c', '18c', '17c', '16c', 'St', 'Capt', 'obs', 'Jan', 'Feb', 'Mar', + 'Apr', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'c', 'tr', 'e', 'g' ] t_sentence = r'^.*?(?<!%s)(?:\.(?= [A-Z0-9]|\Z)|\Z)' r_sentence = re.compile(t_sentence % ')(?<!'.join(abbrs)) -def unescape(s): - s = s.replace('>', '>') - s = s.replace('<', '<') - s = s.replace('&', '&') - return s - -def text(html): - html = r_tag.sub('', html) - html = r_whitespace.sub(' ', html) - return unescape(html).strip() - -def etymology(word): - # @@ <nsh> sbp, would it be possible to have a flag for .ety to get 2nd/etc - # entries? - http://swhack.com/logs/2006-07-19#T15-05-29 - - if len(word) > 25: - raise ValueError("Word too long: %s[...]" % word[:10]) - word = {'axe': 'ax/axe'}.get(word, word) - - grab = urllib._urlopener - urllib._urlopener = Grab() - urllib._urlopener.addheader("Referer", "http://www.etymonline.com/") - bytes = web.get(etyuri % web.urllib.quote(word)) - urllib._urlopener = grab - definitions = r_definition.findall(bytes) - - if not definitions: - return None - - defn = text(definitions[0]) - m = r_sentence.match(defn) - if not m: - return None - sentence = m.group(0) - - # try: - # sentence = unicode(sentence, 'iso-8859-1') - # sentence = sentence.encode('utf-8') - # except: pass - sentence = web.decode(sentence) - - maxlength = 275 - if len(sentence) > maxlength: - sentence = sentence[:maxlength] - words = sentence[:-5].split(' ') - words.pop() - sentence = ' '.join(words) + ' [...]' - - sentence = '"' + sentence.replace('"', "'") + '"' - return sentence + ' - ' + ('http://etymonline.com/index.php?term=%s' % web.urllib.quote(word)) +def unescape(s): + s = s.replace('>', '>') + s = s.replace('<', '<') + s = s.replace('&', '&') + return s + +def text(html): + html = r_tag.sub('', html) + html = r_whitespace.sub(' ', html) + return unescape(html).strip() + +def etymology(word): + # @@ <nsh> sbp, would it be possible to have a flag for .ety to get 2nd/etc + # entries? - http://swhack.com/logs/2006-07-19#T15-05-29 + + if len(word) > 25: + raise ValueError("Word too long: %s[...]" % word[:10]) + word = {'axe': 'ax/axe'}.get(word, word) + + grab = urllib._urlopener + urllib._urlopener = Grab() + urllib._urlopener.addheader("Referer", "http://www.etymonline.com/") + bytes = web.get(etyuri % web.urllib.quote(word)) + urllib._urlopener = grab + definitions = r_definition.findall(bytes) + + if not definitions: + return None + + defn = text(definitions[0]) + m = r_sentence.match(defn) + if not m: + return None + sentence = m.group(0) + + # try: + # sentence = unicode(sentence, 'iso-8859-1') + # sentence = sentence.encode('utf-8') + # except: pass + sentence = web.decode(sentence) + + maxlength = 275 + if len(sentence) > maxlength: + sentence = sentence[:maxlength] + words = sentence[:-5].split(' ') + words.pop() + sentence = ' '.join(words) + ' [...]' + + sentence = '"' + sentence.replace('"', "'") + '"' + return sentence + ' - ' + ('http://etymonline.com/index.php?term=%s' % web.urllib.quote(word)) @deprecated -def f_etymology(self, origin, match, args): - word = match.group(2) - - try: result = etymology(word.encode('iso-8859-1')) - except IOError: - msg = "Can't connect to etymonline.com (%s)" % (etyuri % word) - self.msg(origin.sender, msg) - return - except AttributeError: - result = None - - if result is not None: - self.msg(origin.sender, result) - else: - uri = etysearch % word - msg = 'Can\'t find the etymology for "%s". Try %s' % (word, ('http://etymonline.com/index.php?term=%s' % web.urllib.quote(word))) - self.msg(origin.sender, msg) +def f_etymology(self, origin, match, args): + word = match.group(2) + + try: result = etymology(word.encode('iso-8859-1')) + except IOError: + msg = "Can't connect to etymonline.com (%s)" % (etyuri % word) + self.msg(origin.sender, msg) + return + except AttributeError: + result = None + + if result is not None: + self.msg(origin.sender, result) + else: + uri = etysearch % word + msg = 'Can\'t find the etymology for "%s". Try %s' % (word, ('http://etymonline.com/index.php?term=%s' % web.urllib.quote(word))) + self.msg(origin.sender, msg) # @@ Cf. http://swhack.com/logs/2006-01-04#T01-50-22 f_etymology.rule = (['ety'], r"(.+?)$") f_etymology.thread = True f_etymology.priority = 'high' -if __name__=="__main__": - import sys - print etymology(sys.argv[1]) +if __name__=="__main__": + import sys + print etymology(sys.argv[1]) diff --git a/modules/head.py b/modules/head.py index 66e9eef..939ac3c 100755 --- a/modules/head.py +++ b/modules/head.py @@ -12,56 +12,56 @@ from htmlentitydefs import name2codepoint import web from tools import deprecated -def head(phenny, input): - """Provide HTTP HEAD information.""" - uri = input.group(2) - uri = (uri or '').encode('utf-8') - if ' ' in uri: - uri, header = uri.rsplit(' ', 1) - else: uri, header = uri, None - - if not uri and hasattr(phenny, 'last_seen_uri'): - try: uri = phenny.last_seen_uri[input.sender] - except KeyError: return phenny.say('?') - - if not uri.startswith('htt'): - uri = 'http://' + uri - # uri = uri.replace('#!', '?_escaped_fragment_=') - - try: info = web.head(uri) - except IOError: return phenny.say("Can't connect to %s" % uri) - except httplib.InvalidURL: return phenny.say("Not a valid URI, sorry.") - - if not isinstance(info, list): - try: info = dict(info) - except TypeError: - return phenny.reply('Try .head http://example.org/ [optional header]') - info['Status'] = '200' - else: - newInfo = dict(info[0]) - newInfo['Status'] = str(info[1]) - info = newInfo - - if header is None: - data = [] - if info.has_key('Status'): - data.append(info['Status']) - if info.has_key('content-type'): - data.append(info['content-type'].replace('; charset=', ', ')) - if info.has_key('last-modified'): - modified = info['last-modified'] - modified = time.strptime(modified, '%a, %d %b %Y %H:%M:%S %Z') - data.append(time.strftime('%Y-%m-%d %H:%M:%S UTC', modified)) - if info.has_key('content-length'): - data.append(info['content-length'] + ' bytes') - phenny.reply(', '.join(data)) - else: - headerlower = header.lower() - if info.has_key(headerlower): - phenny.say(header + ': ' + info.get(headerlower)) - else: - msg = 'There was no %s header in the response.' % header - phenny.say(msg) +def head(phenny, input): + """Provide HTTP HEAD information.""" + uri = input.group(2) + uri = (uri or '').encode('utf-8') + if ' ' in uri: + uri, header = uri.rsplit(' ', 1) + else: uri, header = uri, None + + if not uri and hasattr(phenny, 'last_seen_uri'): + try: uri = phenny.last_seen_uri[input.sender] + except KeyError: return phenny.say('?') + + if not uri.startswith('htt'): + uri = 'http://' + uri + # uri = uri.replace('#!', '?_escaped_fragment_=') + + try: info = web.head(uri) + except IOError: return phenny.say("Can't connect to %s" % uri) + except httplib.InvalidURL: return phenny.say("Not a valid URI, sorry.") + + if not isinstance(info, list): + try: info = dict(info) + except TypeError: + return phenny.reply('Try .head http://example.org/ [optional header]') + info['Status'] = '200' + else: + newInfo = dict(info[0]) + newInfo['Status'] = str(info[1]) + info = newInfo + + if header is None: + data = [] + if info.has_key('Status'): + data.append(info['Status']) + if info.has_key('content-type'): + data.append(info['content-type'].replace('; charset=', ', ')) + if info.has_key('last-modified'): + modified = info['last-modified'] + modified = time.strptime(modified, '%a, %d %b %Y %H:%M:%S %Z') + data.append(time.strftime('%Y-%m-%d %H:%M:%S UTC', modified)) + if info.has_key('content-length'): + data.append(info['content-length'] + ' bytes') + phenny.reply(', '.join(data)) + else: + headerlower = header.lower() + if info.has_key(headerlower): + phenny.say(header + ': ' + info.get(headerlower)) + else: + msg = 'There was no %s header in the response.' % header + phenny.say(msg) head.commands = ['head'] head.example = '.head http://www.w3.org/' @@ -69,121 +69,121 @@ r_title = re.compile(r'(?ims)<title[^>]*>(.*?)</title\s*>') r_entity = re.compile(r'&[A-Za-z0-9#]+;') @deprecated -def f_title(self, origin, match, args): - """.title <URI> - Return the title of URI.""" - uri = match.group(2) - uri = (uri or '').encode('utf-8') - - if not uri and hasattr(self, 'last_seen_uri'): - uri = self.last_seen_uri.get(origin.sender) - if not uri: - return self.msg(origin.sender, 'I need a URI to give the title of...') - - if not ':' in uri: - uri = 'http://' + uri - uri = uri.replace('#!', '?_escaped_fragment_=') - - localhost = [ - 'http://localhost/', 'http://localhost:80/', - 'http://localhost:8080/', 'http://127.0.0.1/', - 'http://127.0.0.1:80/', 'http://127.0.0.1:8080/', - 'https://localhost/', 'https://localhost:80/', - 'https://localhost:8080/', 'https://127.0.0.1/', - 'https://127.0.0.1:80/', 'https://127.0.0.1:8080/', - ] - for s in localhost: - if uri.startswith(s): - return phenny.reply('Sorry, access forbidden.') - - try: - redirects = 0 - while True: - headers = { - 'Accept': 'text/html', - 'User-Agent': 'Mozilla/5.0 (Phenny)' - } - req = urllib2.Request(uri, headers=headers) - u = urllib2.urlopen(req) - info = u.info() - u.close() - # info = web.head(uri) - - if not isinstance(info, list): - status = '200' - else: - status = str(info[1]) - info = info[0] - if status.startswith('3'): - uri = urlparse.urljoin(uri, info['Location']) - else: break - - redirects += 1 - if redirects >= 25: - self.msg(origin.sender, origin.nick + ": Too many redirects") +def f_title(self, origin, match, args): + """.title <URI> - Return the title of URI.""" + uri = match.group(2) + uri = (uri or '').encode('utf-8') + + if not uri and hasattr(self, 'last_seen_uri'): + uri = self.last_seen_uri.get(origin.sender) + if not uri: + return self.msg(origin.sender, 'I need a URI to give the title of...') + + if not ':' in uri: + uri = 'http://' + uri + uri = uri.replace('#!', '?_escaped_fragment_=') + + localhost = [ + 'http://localhost/', 'http://localhost:80/', + 'http://localhost:8080/', 'http://127.0.0.1/', + 'http://127.0.0.1:80/', 'http://127.0.0.1:8080/', + 'https://localhost/', 'https://localhost:80/', + 'https://localhost:8080/', 'https://127.0.0.1/', + 'https://127.0.0.1:80/', 'https://127.0.0.1:8080/', + ] + for s in localhost: + if uri.startswith(s): + return phenny.reply('Sorry, access forbidden.') + + try: + redirects = 0 + while True: + headers = { + 'Accept': 'text/html', + 'User-Agent': 'Mozilla/5.0 (Phenny)' + } + req = urllib2.Request(uri, headers=headers) + u = urllib2.urlopen(req) + info = u.info() + u.close() + # info = web.head(uri) + + if not isinstance(info, list): + status = '200' + else: + status = str(info[1]) + info = info[0] + if status.startswith('3'): + uri = urlparse.urljoin(uri, info['Location']) + else: break + + redirects += 1 + if redirects >= 25: + self.msg(origin.sender, origin.nick + ": Too many redirects") + return + + try: mtype = info['content-type'] + except: + err = ": Couldn't get the Content-Type, sorry" + return self.msg(origin.sender, origin.nick + err) + if not (('/html' in mtype) or ('/xhtml' in mtype)): + self.msg(origin.sender, origin.nick + ": Document isn't HTML") return - try: mtype = info['content-type'] - except: - err = ": Couldn't get the Content-Type, sorry" - return self.msg(origin.sender, origin.nick + err) - if not (('/html' in mtype) or ('/xhtml' in mtype)): - self.msg(origin.sender, origin.nick + ": Document isn't HTML") - return - - u = urllib2.urlopen(req) - bytes = u.read(262144) - u.close() - - except IOError: - self.msg(origin.sender, "Can't connect to %s" % uri) - return - - m = r_title.search(bytes) - if m: - title = m.group(1) - title = title.strip() - title = title.replace('\t', ' ') - title = title.replace('\r', ' ') - title = title.replace('\n', ' ') - while ' ' in title: - title = title.replace(' ', ' ') - if len(title) > 200: - title = title[:200] + '[...]' - - def e(m): - entity = m.group(0) - if entity.startswith('&#x'): - cp = int(entity[3:-1], 16) - return unichr(cp).encode('utf-8') - elif entity.startswith('&#'): - cp = int(entity[2:-1]) - return unichr(cp).encode('utf-8') - else: - char = name2codepoint[entity[1:-1]] - return unichr(char).encode('utf-8') - title = r_entity.sub(e, title) - - if title: - try: title.decode('utf-8') - except: - try: title = title.decode('iso-8859-1').encode('utf-8') - except: title = title.decode('cp1252').encode('utf-8') - else: pass - else: title = '[The title is empty.]' - - title = title.replace('\n', '') - title = title.replace('\r', '') - self.msg(origin.sender, origin.nick + ': ' + title) - else: self.msg(origin.sender, origin.nick + ': No title found') + u = urllib2.urlopen(req) + bytes = u.read(262144) + u.close() + + except IOError: + self.msg(origin.sender, "Can't connect to %s" % uri) + return + + m = r_title.search(bytes) + if m: + title = m.group(1) + title = title.strip() + title = title.replace('\t', ' ') + title = title.replace('\r', ' ') + title = title.replace('\n', ' ') + while ' ' in title: + title = title.replace(' ', ' ') + if len(title) > 200: + title = title[:200] + '[...]' + + def e(m): + entity = m.group(0) + if entity.startswith('&#x'): + cp = int(entity[3:-1], 16) + return unichr(cp).encode('utf-8') + elif entity.startswith('&#'): + cp = int(entity[2:-1]) + return unichr(cp).encode('utf-8') + else: + char = name2codepoint[entity[1:-1]] + return unichr(char).encode('utf-8') + title = r_entity.sub(e, title) + + if title: + try: title.decode('utf-8') + except: + try: title = title.decode('iso-8859-1').encode('utf-8') + except: title = title.decode('cp1252').encode('utf-8') + else: pass + else: title = '[The title is empty.]' + + title = title.replace('\n', '') + title = title.replace('\r', '') + self.msg(origin.sender, origin.nick + ': ' + title) + else: self.msg(origin.sender, origin.nick + ': No title found') f_title.commands = ['title'] -def noteuri(phenny, input): - uri = input.group(1).encode('utf-8') - if not hasattr(phenny.bot, 'last_seen_uri'): - phenny.bot.last_seen_uri = {} - phenny.bot.last_seen_uri[input.sender] = uri +def noteuri(phenny, input): + uri = input.group(1).encode('utf-8') + if not hasattr(phenny.bot, 'last_seen_uri'): + phenny.bot.last_seen_uri = {} + phenny.bot.last_seen_uri[input.sender] = uri noteuri.rule = r'.*(http[s]?://[^<> "\x01]+)[,.]?' noteuri.priority = 'low' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/info.py b/modules/info.py index dbf2d44..1e41864 100755 --- a/modules/info.py +++ b/modules/info.py @@ -7,83 +7,83 @@ Licensed under the Eiffel Forum License 2. http://inamidst.com/phenny/ """ -def doc(phenny, input): - """Shows a command's documentation, and possibly an example.""" - name = input.group(1) - name = name.lower() +def doc(phenny, input): + """Shows a command's documentation, and possibly an example.""" + name = input.group(1) + name = name.lower() - if phenny.doc.has_key(name): - phenny.reply(phenny.doc[name][0]) - if phenny.doc[name][1]: - phenny.say('e.g. ' + phenny.doc[name][1]) + if phenny.doc.has_key(name): + phenny.reply(phenny.doc[name][0]) + if phenny.doc[name][1]: + phenny.say('e.g. ' + phenny.doc[name][1]) doc.rule = ('$nick', '(?i)(?:help|doc) +([A-Za-z]+)(?:\?+)?$') doc.example = '$nickname: doc tell?' doc.priority = 'low' -def commands(phenny, input): - # This function only works in private message - if input.sender.startswith('#'): return - names = ', '.join(sorted(phenny.doc.iterkeys())) - phenny.say('Commands I recognise: ' + names + '.') - phenny.say(("For help, do '%s: help example?' where example is the " + - "name of the command you want help for.") % phenny.nick) +def commands(phenny, input): + # This function only works in private message + if input.sender.startswith('#'): return + names = ', '.join(sorted(phenny.doc.iterkeys())) + phenny.say('Commands I recognise: ' + names + '.') + phenny.say(("For help, do '%s: help example?' where example is the " + + "name of the command you want help for.") % phenny.nick) commands.commands = ['commands'] commands.priority = 'low' -def help(phenny, input): - response = ( - 'Hi, I\'m a bot. Say ".commands" to me in private for a list ' + - 'of my commands, or see http://inamidst.com/phenny/ for more ' + - 'general details. My owner is %s.' - ) % phenny.config.owner - phenny.reply(response) +def help(phenny, input): + response = ( + 'Hi, I\'m a bot. Say ".commands" to me in private for a list ' + + 'of my commands, or see http://inamidst.com/phenny/ for more ' + + 'general details. My owner is %s.' + ) % phenny.config.owner + phenny.reply(response) help.rule = ('$nick', r'(?i)help(?:[?!]+)?$') help.priority = 'low' -def stats(phenny, input): - """Show information on command usage patterns.""" - commands = {} - users = {} - channels = {} +def stats(phenny, input): + """Show information on command usage patterns.""" + commands = {} + users = {} + channels = {} - ignore = set(['f_note', 'startup', 'message', 'noteuri']) - for (name, user), count in phenny.stats.items(): - if name in ignore: continue - if not user: continue + ignore = set(['f_note', 'startup', 'message', 'noteuri']) + for (name, user), count in phenny.stats.items(): + if name in ignore: continue + if not user: continue - if not user.startswith('#'): - try: users[user] += count - except KeyError: users[user] = count - else: - try: commands[name] += count - except KeyError: commands[name] = count + if not user.startswith('#'): + try: users[user] += count + except KeyError: users[user] = count + else: + try: commands[name] += count + except KeyError: commands[name] = count - try: channels[user] += count - except KeyError: channels[user] = count + try: channels[user] += count + except KeyError: channels[user] = count - comrank = sorted([(b, a) for (a, b) in commands.iteritems()], reverse=True) - userank = sorted([(b, a) for (a, b) in users.iteritems()], reverse=True) - charank = sorted([(b, a) for (a, b) in channels.iteritems()], reverse=True) + comrank = sorted([(b, a) for (a, b) in commands.iteritems()], reverse=True) + userank = sorted([(b, a) for (a, b) in users.iteritems()], reverse=True) + charank = sorted([(b, a) for (a, b) in channels.iteritems()], reverse=True) - # most heavily used commands - creply = 'most used commands: ' - for count, command in comrank[:10]: - creply += '%s (%s), ' % (command, count) - phenny.say(creply.rstrip(', ')) + # most heavily used commands + creply = 'most used commands: ' + for count, command in comrank[:10]: + creply += '%s (%s), ' % (command, count) + phenny.say(creply.rstrip(', ')) - # most heavy users - reply = 'power users: ' - for count, user in userank[:10]: - reply += '%s (%s), ' % (user, count) - phenny.say(reply.rstrip(', ')) + # most heavy users + reply = 'power users: ' + for count, user in userank[:10]: + reply += '%s (%s), ' % (user, count) + phenny.say(reply.rstrip(', ')) - # most heavy channels - chreply = 'power channels: ' - for count, channel in charank[:3]: - chreply += '%s (%s), ' % (channel, count) - phenny.say(chreply.rstrip(', ')) + # most heavy channels + chreply = 'power channels: ' + for count, channel in charank[:3]: + chreply += '%s (%s), ' % (channel, count) + phenny.say(chreply.rstrip(', ')) stats.commands = ['stats'] stats.priority = 'low' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/oblique.py b/modules/oblique.py index d93446e..3a5f35a 100755 --- a/modules/oblique.py +++ b/modules/oblique.py @@ -15,105 +15,105 @@ definitions = 'https://github.com/nslater/oblique/wiki' r_item = re.compile(r'(?i)<li>(.*?)</li>') r_tag = re.compile(r'<[^>]+>') -def mappings(uri): - result = {} - bytes = web.get(uri) - for item in r_item.findall(bytes): - item = r_tag.sub('', item).strip(' \t\r\n') - if not ' ' in item: continue - - command, template = item.split(' ', 1) - if not command.isalnum(): continue - if not template.startswith('http://'): continue - result[command] = template.replace('&', '&') - return result - -def service(phenny, input, command, args): - t = o.services[command] - template = t.replace('${args}', urllib.quote(args.encode('utf-8'), '')) - template = template.replace('${nick}', urllib.quote(input.nick, '')) - uri = template.replace('${sender}', urllib.quote(input.sender, '')) - - info = web.head(uri) - if isinstance(info, list): - info = info[0] - if not 'text/plain' in info.get('content-type', '').lower(): - return phenny.reply("Sorry, the service didn't respond in plain text.") - bytes = web.get(uri) - lines = bytes.splitlines() - if not lines: - return phenny.reply("Sorry, the service didn't respond any output.") - try: line = lines[0].encode('utf-8')[:350] - except: line = lines[0][:250] - phenny.say(line) - -def refresh(phenny): - if hasattr(phenny.config, 'services'): - services = phenny.config.services - else: services = definitions - - old = o.services - o.serviceURI = services - o.services = mappings(o.serviceURI) - return len(o.services), set(o.services) - set(old) - -def o(phenny, input): - """Call a webservice.""" - text = input.group(2) - - if (not o.services) or (text == 'refresh'): - length, added = refresh(phenny) - if text == 'refresh': - msg = 'Okay, found %s services.' % length - if added: - msg += ' Added: ' + ', '.join(sorted(added)[:5]) - if len(added) > 5: msg += ', &c.' - return phenny.reply(msg) - - if not text: - return phenny.reply('Try %s for details.' % o.serviceURI) - - if ' ' in text: - command, args = text.split(' ', 1) - else: command, args = text, '' - command = command.lower() - - if command == 'service': - msg = o.services.get(args, 'No such service!') - return phenny.reply(msg) - - if not o.services.has_key(command): - return phenny.reply('Service not found in %s' % o.serviceURI) - - if hasattr(phenny.config, 'external'): - default = phenny.config.external.get('*') - manifest = phenny.config.external.get(input.sender, default) - if manifest: - commands = set(manifest) - if (command not in commands) and (manifest[0] != '!'): - return phenny.reply('Sorry, %s is not whitelisted' % command) - elif (command in commands) and (manifest[0] == '!'): - return phenny.reply('Sorry, %s is blacklisted' % command) - service(phenny, input, command, args) +def mappings(uri): + result = {} + bytes = web.get(uri) + for item in r_item.findall(bytes): + item = r_tag.sub('', item).strip(' \t\r\n') + if not ' ' in item: continue + + command, template = item.split(' ', 1) + if not command.isalnum(): continue + if not template.startswith('http://'): continue + result[command] = template.replace('&', '&') + return result + +def service(phenny, input, command, args): + t = o.services[command] + template = t.replace('${args}', urllib.quote(args.encode('utf-8'), '')) + template = template.replace('${nick}', urllib.quote(input.nick, '')) + uri = template.replace('${sender}', urllib.quote(input.sender, '')) + + info = web.head(uri) + if isinstance(info, list): + info = info[0] + if not 'text/plain' in info.get('content-type', '').lower(): + return phenny.reply("Sorry, the service didn't respond in plain text.") + bytes = web.get(uri) + lines = bytes.splitlines() + if not lines: + return phenny.reply("Sorry, the service didn't respond any output.") + try: line = lines[0].encode('utf-8')[:350] + except: line = lines[0][:250] + phenny.say(line) + +def refresh(phenny): + if hasattr(phenny.config, 'services'): + services = phenny.config.services + else: services = definitions + + old = o.services + o.serviceURI = services + o.services = mappings(o.serviceURI) + return len(o.services), set(o.services) - set(old) + +def o(phenny, input): + """Call a webservice.""" + text = input.group(2) + + if (not o.services) or (text == 'refresh'): + length, added = refresh(phenny) + if text == 'refresh': + msg = 'Okay, found %s services.' % length + if added: + msg += ' Added: ' + ', '.join(sorted(added)[:5]) + if len(added) > 5: msg += ', &c.' + return phenny.reply(msg) + + if not text: + return phenny.reply('Try %s for details.' % o.serviceURI) + + if ' ' in text: + command, args = text.split(' ', 1) + else: command, args = text, '' + command = command.lower() + + if command == 'service': + msg = o.services.get(args, 'No such service!') + return phenny.reply(msg) + + if not o.services.has_key(command): + return phenny.reply('Service not found in %s' % o.serviceURI) + + if hasattr(phenny.config, 'external'): + default = phenny.config.external.get('*') + manifest = phenny.config.external.get(input.sender, default) + if manifest: + commands = set(manifest) + if (command not in commands) and (manifest[0] != '!'): + return phenny.reply('Sorry, %s is not whitelisted' % command) + elif (command in commands) and (manifest[0] == '!'): + return phenny.reply('Sorry, %s is blacklisted' % command) + service(phenny, input, command, args) o.commands = ['o'] o.example = '.o servicename arg1 arg2 arg3' o.services = {} o.serviceURI = None -def snippet(phenny, input): - if not o.services: - refresh(phenny) - - search = urllib.quote(input.group(2).encode('utf-8')) - py = "BeautifulSoup.BeautifulSoup(re.sub('<.*?>|(?<= ) +', '', " + \ - "''.join(chr(ord(c)) for c in " + \ - "eval(urllib.urlopen('http://ajax.googleapis.com/ajax/serv" + \ - "ices/search/web?v=1.0&q=" + search + "').read()" + \ - ".replace('null', 'None'))['responseData']['resul" + \ - "ts'][0]['content'].decode('unicode-escape')).replace(" + \ - "'"', '\x22')), convertEntities=True)" - service(phenny, input, 'py', py) +def snippet(phenny, input): + if not o.services: + refresh(phenny) + + search = urllib.quote(input.group(2).encode('utf-8')) + py = "BeautifulSoup.BeautifulSoup(re.sub('<.*?>|(?<= ) +', '', " + \ + "''.join(chr(ord(c)) for c in " + \ + "eval(urllib.urlopen('http://ajax.googleapis.com/ajax/serv" + \ + "ices/search/web?v=1.0&q=" + search + "').read()" + \ + ".replace('null', 'None'))['responseData']['resul" + \ + "ts'][0]['content'].decode('unicode-escape')).replace(" + \ + "'"', '\x22')), convertEntities=True)" + service(phenny, input, 'py', py) snippet.commands = ['snippet'] -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/ping.py b/modules/ping.py index 23219ac..9ce7429 100755 --- a/modules/ping.py +++ b/modules/ping.py @@ -7,17 +7,17 @@ About: http://inamidst.com/phenny/ import random -def hello(phenny, input): - greeting = random.choice(('Hi', 'Hey', 'Hello')) - punctuation = random.choice(('', '!')) - phenny.say(greeting + ' ' + input.nick + punctuation) +def hello(phenny, input): + greeting = random.choice(('Hi', 'Hey', 'Hello')) + punctuation = random.choice(('', '!')) + phenny.say(greeting + ' ' + input.nick + punctuation) hello.rule = r'(?i)(hi|hello|hey) $nickname[ \t]*$' -def interjection(phenny, input): - phenny.say(input.nick + '!') +def interjection(phenny, input): + phenny.say(input.nick + '!') interjection.rule = r'$nickname!' interjection.priority = 'high' interjection.thread = False -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/reload.py b/modules/reload.py index dfd0e8e..bef2f19 100755 --- a/modules/reload.py +++ b/modules/reload.py @@ -10,46 +10,46 @@ http://inamidst.com/phenny/ import sys, os.path, time, imp import irc -def f_reload(phenny, input): - """Reloads a module, for use by admins only.""" - if not input.admin: return - - name = input.group(2) - if name == phenny.config.owner: - return phenny.reply('What?') - - if (not name) or (name == '*'): - phenny.variables = None - phenny.commands = None - phenny.setup() - return phenny.reply('done') - - if not sys.modules.has_key(name): - return phenny.reply('%s: no such module!' % name) - - # Thanks to moot for prodding me on this - path = sys.modules[name].__file__ - if path.endswith('.pyc') or path.endswith('.pyo'): - path = path[:-1] - if not os.path.isfile(path): - return phenny.reply('Found %s, but not the source file' % name) - - module = imp.load_source(name, path) - sys.modules[name] = module - if hasattr(module, 'setup'): - module.setup(phenny) - - mtime = os.path.getmtime(module.__file__) - modified = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(mtime)) - - phenny.register(vars(module)) - phenny.bind_commands() - - phenny.reply('%r (version: %s)' % (module, modified)) +def f_reload(phenny, input): + """Reloads a module, for use by admins only.""" + if not input.admin: return + + name = input.group(2) + if name == phenny.config.owner: + return phenny.reply('What?') + + if (not name) or (name == '*'): + phenny.variables = None + phenny.commands = None + phenny.setup() + return phenny.reply('done') + + if not sys.modules.has_key(name): + return phenny.reply('%s: no such module!' % name) + + # Thanks to moot for prodding me on this + path = sys.modules[name].__file__ + if path.endswith('.pyc') or path.endswith('.pyo'): + path = path[:-1] + if not os.path.isfile(path): + return phenny.reply('Found %s, but not the source file' % name) + + module = imp.load_source(name, path) + sys.modules[name] = module + if hasattr(module, 'setup'): + module.setup(phenny) + + mtime = os.path.getmtime(module.__file__) + modified = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(mtime)) + + phenny.register(vars(module)) + phenny.bind_commands() + + phenny.reply('%r (version: %s)' % (module, modified)) f_reload.name = 'reload' f_reload.rule = ('$nick', ['reload'], r'(\S+)?') f_reload.priority = 'low' f_reload.thread = False -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/remind.py b/modules/remind.py index fbfd258..871d585 100755 --- a/modules/remind.py +++ b/modules/remind.py @@ -9,133 +9,133 @@ http://inamidst.com/phenny/ import os, re, time, threading -def filename(self): - name = self.nick + '-' + self.config.host + '.reminders.db' - return os.path.join(os.path.expanduser('~/.phenny'), name) - -def load_database(name): - data = {} - if os.path.isfile(name): - f = open(name, 'rb') - for line in f: - unixtime, channel, nick, message = line.split('\t') - message = message.rstrip('\n') - t = int(unixtime) - reminder = (channel, nick, message) - try: data[t].append(reminder) - except KeyError: data[t] = [reminder] - f.close() - return data - -def dump_database(name, data): - f = open(name, 'wb') - for unixtime, reminders in data.iteritems(): - for channel, nick, message in reminders: - f.write('%s\t%s\t%s\t%s\n' % (unixtime, channel, nick, message)) - f.close() - -def setup(phenny): - phenny.rfn = filename(phenny) - - # phenny.sending.acquire() - phenny.rdb = load_database(phenny.rfn) - # phenny.sending.release() - - def monitor(phenny): - time.sleep(5) - while True: - now = int(time.time()) - unixtimes = [int(key) for key in phenny.rdb] - oldtimes = [t for t in unixtimes if t <= now] - if oldtimes: - for oldtime in oldtimes: - for (channel, nick, message) in phenny.rdb[oldtime]: - if message: - phenny.msg(channel, nick + ': ' + message) - else: phenny.msg(channel, nick + '!') - del phenny.rdb[oldtime] - - # phenny.sending.acquire() - dump_database(phenny.rfn, phenny.rdb) - # phenny.sending.release() - time.sleep(2.5) - - targs = (phenny,) - t = threading.Thread(target=monitor, args=targs) - t.start() +def filename(self): + name = self.nick + '-' + self.config.host + '.reminders.db' + return os.path.join(os.path.expanduser('~/.phenny'), name) + +def load_database(name): + data = {} + if os.path.isfile(name): + f = open(name, 'rb') + for line in f: + unixtime, channel, nick, message = line.split('\t') + message = message.rstrip('\n') + t = int(unixtime) + reminder = (channel, nick, message) + try: data[t].append(reminder) + except KeyError: data[t] = [reminder] + f.close() + return data + +def dump_database(name, data): + f = open(name, 'wb') + for unixtime, reminders in data.iteritems(): + for channel, nick, message in reminders: + f.write('%s\t%s\t%s\t%s\n' % (unixtime, channel, nick, message)) + f.close() + +def setup(phenny): + phenny.rfn = filename(phenny) + + # phenny.sending.acquire() + phenny.rdb = load_database(phenny.rfn) + # phenny.sending.release() + + def monitor(phenny): + time.sleep(5) + while True: + now = int(time.time()) + unixtimes = [int(key) for key in phenny.rdb] + oldtimes = [t for t in unixtimes if t <= now] + if oldtimes: + for oldtime in oldtimes: + for (channel, nick, message) in phenny.rdb[oldtime]: + if message: + phenny.msg(channel, nick + ': ' + message) + else: phenny.msg(channel, nick + '!') + del phenny.rdb[oldtime] + + # phenny.sending.acquire() + dump_database(phenny.rfn, phenny.rdb) + # phenny.sending.release() + time.sleep(2.5) + + targs = (phenny,) + t = threading.Thread(target=monitor, args=targs) + t.start() scaling = { - 'years': 365.25 * 24 * 3600, - 'year': 365.25 * 24 * 3600, - 'yrs': 365.25 * 24 * 3600, - 'y': 365.25 * 24 * 3600, - - 'months': 29.53059 * 24 * 3600, - 'month': 29.53059 * 24 * 3600, - 'mo': 29.53059 * 24 * 3600, - - 'weeks': 7 * 24 * 3600, - 'week': 7 * 24 * 3600, - 'wks': 7 * 24 * 3600, - 'wk': 7 * 24 * 3600, - 'w': 7 * 24 * 3600, - - 'days': 24 * 3600, - 'day': 24 * 3600, - 'd': 24 * 3600, - - 'hours': 3600, - 'hour': 3600, - 'hrs': 3600, - 'hr': 3600, - 'h': 3600, - - 'minutes': 60, - 'minute': 60, - 'mins': 60, - 'min': 60, - 'm': 60, - - 'seconds': 1, - 'second': 1, - 'secs': 1, - 'sec': 1, - 's': 1 + 'years': 365.25 * 24 * 3600, + 'year': 365.25 * 24 * 3600, + 'yrs': 365.25 * 24 * 3600, + 'y': 365.25 * 24 * 3600, + + 'months': 29.53059 * 24 * 3600, + 'month': 29.53059 * 24 * 3600, + 'mo': 29.53059 * 24 * 3600, + + 'weeks': 7 * 24 * 3600, + 'week': 7 * 24 * 3600, + 'wks': 7 * 24 * 3600, + 'wk': 7 * 24 * 3600, + 'w': 7 * 24 * 3600, + + 'days': 24 * 3600, + 'day': 24 * 3600, + 'd': 24 * 3600, + + 'hours': 3600, + 'hour': 3600, + 'hrs': 3600, + 'hr': 3600, + 'h': 3600, + + 'minutes': 60, + 'minute': 60, + 'mins': 60, + 'min': 60, + 'm': 60, + + 'seconds': 1, + 'second': 1, + 'secs': 1, + 'sec': 1, + 's': 1 } periods = '|'.join(scaling.keys()) p_command = r'\.in ([0-9]+(?:\.[0-9]+)?)\s?((?:%s)\b)?:?\s?(.*)' % periods r_command = re.compile(p_command) -def remind(phenny, input): - m = r_command.match(input.bytes) - if not m: - return phenny.reply("Sorry, didn't understand the input.") - length, scale, message = m.groups() +def remind(phenny, input): + m = r_command.match(input.bytes) + if not m: + return phenny.reply("Sorry, didn't understand the input.") + length, scale, message = m.groups() - length = float(length) - factor = scaling.get(scale, 60) - duration = length * factor + length = float(length) + factor = scaling.get(scale, 60) + duration = length * factor - if duration % 1: - duration = int(duration) + 1 - else: duration = int(duration) + if duration % 1: + duration = int(duration) + 1 + else: duration = int(duration) - t = int(time.time()) + duration - reminder = (input.sender, input.nick, message) + t = int(time.time()) + duration + reminder = (input.sender, input.nick, message) - try: phenny.rdb[t].append(reminder) - except KeyError: phenny.rdb[t] = [reminder] + try: phenny.rdb[t].append(reminder) + except KeyError: phenny.rdb[t] = [reminder] - dump_database(phenny.rfn, phenny.rdb) + dump_database(phenny.rfn, phenny.rdb) - if duration >= 60: - w = '' - if duration >= 3600 * 12: - w += time.strftime(' on %d %b %Y', time.gmtime(t)) - w += time.strftime(' at %H:%MZ', time.gmtime(t)) - phenny.reply('Okay, will remind%s' % w) - else: phenny.reply('Okay, will remind in %s secs' % duration) + if duration >= 60: + w = '' + if duration >= 3600 * 12: + w += time.strftime(' on %d %b %Y', time.gmtime(t)) + w += time.strftime(' at %H:%MZ', time.gmtime(t)) + phenny.reply('Okay, will remind%s' % w) + else: phenny.reply('Okay, will remind in %s secs' % duration) remind.commands = ['in'] r_time = re.compile(r'^([0-9]{2}[:.][0-9]{2})') @@ -145,49 +145,49 @@ import calendar from clock import TimeZones def at(phenny, input): - bytes = input[4:] + bytes = input[4:] - m = r_time.match(bytes) - if not m: - return phenny.reply("Sorry, didn't understand the time spec.") - t = m.group(1).replace('.', ':') - bytes = bytes[len(t):] + m = r_time.match(bytes) + if not m: + return phenny.reply("Sorry, didn't understand the time spec.") + t = m.group(1).replace('.', ':') + bytes = bytes[len(t):] - m = r_zone.match(bytes) - if not m: - return phenny.reply("Sorry, didn't understand the zone spec.") - z = m.group(2) - bytes = bytes[len(m.group(1)):].strip().encode("utf-8") + m = r_zone.match(bytes) + if not m: + return phenny.reply("Sorry, didn't understand the zone spec.") + z = m.group(2) + bytes = bytes[len(m.group(1)):].strip().encode("utf-8") - if z.startswith('+') or z.startswith('-'): - tz = int(z) + if z.startswith('+') or z.startswith('-'): + tz = int(z) - if TimeZones.has_key(z): - tz = TimeZones[z] - else: return phenny.reply("Sorry, didn't understand the time zone.") + if TimeZones.has_key(z): + tz = TimeZones[z] + else: return phenny.reply("Sorry, didn't understand the time zone.") - d = time.strftime("%Y-%m-%d", time.gmtime()) - d = time.strptime(("%s %s" % (d, t)).encode("utf-8"), "%Y-%m-%d %H:%M") + d = time.strftime("%Y-%m-%d", time.gmtime()) + d = time.strptime(("%s %s" % (d, t)).encode("utf-8"), "%Y-%m-%d %H:%M") - d = int(calendar.timegm(d) - (3600 * tz)) - duration = int((d - time.time()) / 60) + d = int(calendar.timegm(d) - (3600 * tz)) + duration = int((d - time.time()) / 60) - if duration < 1: - return phenny.reply("Sorry, that date is this minute or in the past. And only times in the same day are supported!") + if duration < 1: + return phenny.reply("Sorry, that date is this minute or in the past. And only times in the same day are supported!") - # phenny.say("%s %s %s" % (t, tz, d)) + # phenny.say("%s %s %s" % (t, tz, d)) - reminder = (input.sender, input.nick, bytes) - # phenny.say(str((d, reminder))) - try: phenny.rdb[d].append(reminder) - except KeyError: phenny.rdb[d] = [reminder] + reminder = (input.sender, input.nick, bytes) + # phenny.say(str((d, reminder))) + try: phenny.rdb[d].append(reminder) + except KeyError: phenny.rdb[d] = [reminder] - phenny.sending.acquire() - dump_database(phenny.rfn, phenny.rdb) - phenny.sending.release() + phenny.sending.acquire() + dump_database(phenny.rfn, phenny.rdb) + phenny.sending.release() - phenny.reply("Reminding at %s %s - in %s minute(s)" % (t, z, duration)) + phenny.reply("Reminding at %s %s - in %s minute(s)" % (t, z, duration)) at.commands = ['at'] -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/search.py b/modules/search.py index 5d038af..bd71d71 100755 --- a/modules/search.py +++ b/modules/search.py @@ -11,255 +11,255 @@ import re import web class Grab(web.urllib.URLopener): - def __init__(self, *args): - self.version = 'Mozilla/5.0 (Phenny)' - web.urllib.URLopener.__init__(self, *args) - self.addheader('Referer', 'https://github.com/sbp/phenny') - def http_error_default(self, url, fp, errcode, errmsg, headers): - return web.urllib.addinfourl(fp, [headers, errcode], "http:" + url) - -def google_ajax(query): - """Search using AjaxSearch, and return its JSON.""" - if isinstance(query, unicode): - query = query.encode('utf-8') - uri = 'http://ajax.googleapis.com/ajax/services/search/web' - args = '?v=1.0&safe=off&q=' + web.urllib.quote(query) - handler = web.urllib._urlopener - web.urllib._urlopener = Grab() - bytes = web.get(uri + args) - web.urllib._urlopener = handler - return web.json(bytes) - -def google_search(query): - results = google_ajax(query) - try: return results['responseData']['results'][0]['unescapedUrl'] - except IndexError: return None - except TypeError: - print results - return False - -def google_count(query): - results = google_ajax(query) - if not results.has_key('responseData'): return '0' - if not results['responseData'].has_key('cursor'): return '0' - if not results['responseData']['cursor'].has_key('estimatedResultCount'): - return '0' - return results['responseData']['cursor']['estimatedResultCount'] - -def formatnumber(n): - """Format a number with beautiful commas.""" - parts = list(str(n)) - for i in range((len(parts) - 3), 0, -3): - parts.insert(i, ',') - return ''.join(parts) + def __init__(self, *args): + self.version = 'Mozilla/5.0 (Phenny)' + web.urllib.URLopener.__init__(self, *args) + self.addheader('Referer', 'https://github.com/sbp/phenny') + def http_error_default(self, url, fp, errcode, errmsg, headers): + return web.urllib.addinfourl(fp, [headers, errcode], "http:" + url) + +def google_ajax(query): + """Search using AjaxSearch, and return its JSON.""" + if isinstance(query, unicode): + query = query.encode('utf-8') + uri = 'http://ajax.googleapis.com/ajax/services/search/web' + args = '?v=1.0&safe=off&q=' + web.urllib.quote(query) + handler = web.urllib._urlopener + web.urllib._urlopener = Grab() + bytes = web.get(uri + args) + web.urllib._urlopener = handler + return web.json(bytes) + +def google_search(query): + results = google_ajax(query) + try: return results['responseData']['results'][0]['unescapedUrl'] + except IndexError: return None + except TypeError: + print results + return False + +def google_count(query): + results = google_ajax(query) + if not results.has_key('responseData'): return '0' + if not results['responseData'].has_key('cursor'): return '0' + if not results['responseData']['cursor'].has_key('estimatedResultCount'): + return '0' + return results['responseData']['cursor']['estimatedResultCount'] + +def formatnumber(n): + """Format a number with beautiful commas.""" + parts = list(str(n)) + for i in range((len(parts) - 3), 0, -3): + parts.insert(i, ',') + return ''.join(parts) def old_gc(query): - return formatnumber(google_count(query)) - -def g(phenny, input): - """Queries Google for the specified input.""" - query = input.group(2) - if not query: - return phenny.reply('.g what?') - query = query.encode('utf-8') - uri = google_search(query) - if uri: - phenny.reply(uri) - if not hasattr(phenny.bot, 'last_seen_uri'): - phenny.bot.last_seen_uri = {} - phenny.bot.last_seen_uri[input.sender] = uri - elif uri is False: phenny.reply("Problem getting data from Google.") - else: phenny.reply("No results found for '%s'." % query) + return formatnumber(google_count(query)) + +def g(phenny, input): + """Queries Google for the specified input.""" + query = input.group(2) + if not query: + return phenny.reply('.g what?') + query = query.encode('utf-8') + uri = google_search(query) + if uri: + phenny.reply(uri) + if not hasattr(phenny.bot, 'last_seen_uri'): + phenny.bot.last_seen_uri = {} + phenny.bot.last_seen_uri[input.sender] = uri + elif uri is False: phenny.reply("Problem getting data from Google.") + else: phenny.reply("No results found for '%s'." % query) g.commands = ['g'] g.priority = 'high' g.example = '.g swhack' -def oldgc(phenny, input): - """Returns the number of Google results for the specified input.""" - query = input.group(2) - if not query: - return phenny.reply('.gc what?') - query = query.encode('utf-8') - num = formatnumber(google_count(query)) - phenny.say(query + ': ' + num) +def oldgc(phenny, input): + """Returns the number of Google results for the specified input.""" + query = input.group(2) + if not query: + return phenny.reply('.gc what?') + query = query.encode('utf-8') + num = formatnumber(google_count(query)) + phenny.say(query + ': ' + num) oldgc.commands = ['ogc', 'oldgc'] oldgc.example = '.oldgc extrapolate' r_query = re.compile( - r'\+?"[^"\\]*(?:\\.[^"\\]*)*"|\[[^]\\]*(?:\\.[^]\\]*)*\]|\S+' + r'\+?"[^"\\]*(?:\\.[^"\\]*)*"|\[[^]\\]*(?:\\.[^]\\]*)*\]|\S+' ) -def gcs(phenny, input): - if not input.group(2): - return phenny.reply("Nothing to compare.") - queries = r_query.findall(input.group(2)) - if len(queries) > 6: - return phenny.reply('Sorry, can only compare up to six things.') - - results = [] - for i, query in enumerate(queries): - query = query.strip('[]') - query = query.encode('utf-8') - n = int((formatnumber(google_count(query)) or '0').replace(',', '')) - results.append((n, query)) - if i >= 2: __import__('time').sleep(0.25) - if i >= 4: __import__('time').sleep(0.25) - - results = [(term, n) for (n, term) in reversed(sorted(results))] - reply = ', '.join('%s (%s)' % (t, formatnumber(n)) for (t, n) in results) - phenny.say(reply) +def gcs(phenny, input): + if not input.group(2): + return phenny.reply("Nothing to compare.") + queries = r_query.findall(input.group(2)) + if len(queries) > 6: + return phenny.reply('Sorry, can only compare up to six things.') + + results = [] + for i, query in enumerate(queries): + query = query.strip('[]') + query = query.encode('utf-8') + n = int((formatnumber(google_count(query)) or '0').replace(',', '')) + results.append((n, query)) + if i >= 2: __import__('time').sleep(0.25) + if i >= 4: __import__('time').sleep(0.25) + + results = [(term, n) for (n, term) in reversed(sorted(results))] + reply = ', '.join('%s (%s)' % (t, formatnumber(n)) for (t, n) in results) + phenny.say(reply) gcs.commands = ['gcs', 'comp'] r_bing = re.compile(r'<h3><a href="([^"]+)"') -def bing_search(query, lang='en-GB'): - query = web.urllib.quote(query) - base = 'http://www.bing.com/search?mkt=%s&q=' % lang - bytes = web.get(base + query) - for result in r_bing.findall(bytes): - if "r.msn.com/" in result: continue - return result - -def bing(phenny, input): - """Queries Bing for the specified input.""" - query = input.group(2) - if query.startswith(':'): - lang, query = query.split(' ', 1) - lang = lang[1:] - else: lang = 'en-GB' - if not query: - return phenny.reply('.bing what?') - - query = query.encode('utf-8') - uri = bing_search(query, lang) - if uri: - phenny.reply(uri) - if not hasattr(phenny.bot, 'last_seen_uri'): - phenny.bot.last_seen_uri = {} - phenny.bot.last_seen_uri[input.sender] = uri - else: phenny.reply("No results found for '%s'." % query) +def bing_search(query, lang='en-GB'): + query = web.urllib.quote(query) + base = 'http://www.bing.com/search?mkt=%s&q=' % lang + bytes = web.get(base + query) + for result in r_bing.findall(bytes): + if "r.msn.com/" in result: continue + return result + +def bing(phenny, input): + """Queries Bing for the specified input.""" + query = input.group(2) + if query.startswith(':'): + lang, query = query.split(' ', 1) + lang = lang[1:] + else: lang = 'en-GB' + if not query: + return phenny.reply('.bing what?') + + query = query.encode('utf-8') + uri = bing_search(query, lang) + if uri: + phenny.reply(uri) + if not hasattr(phenny.bot, 'last_seen_uri'): + phenny.bot.last_seen_uri = {} + phenny.bot.last_seen_uri[input.sender] = uri + else: phenny.reply("No results found for '%s'." % query) bing.commands = ['bing'] bing.example = '.bing swhack' r_duck = re.compile(r'nofollow" class="[^"]+" href="(.*?)">') -def duck_search(query): - query = query.replace('!', '') - query = web.urllib.quote(query) - uri = 'http://duckduckgo.com/html/?q=%s&kl=uk-en' % query - bytes = web.get(uri) - m = r_duck.search(bytes) - if m: return web.decode(m.group(1)) - -def duck(phenny, input): - query = input.group(2) - if not query: return phenny.reply('.ddg what?') - - query = query.encode('utf-8') - uri = duck_search(query) - if uri: - phenny.reply(uri) - if not hasattr(phenny.bot, 'last_seen_uri'): - phenny.bot.last_seen_uri = {} - phenny.bot.last_seen_uri[input.sender] = uri - else: phenny.reply("No results found for '%s'." % query) +def duck_search(query): + query = query.replace('!', '') + query = web.urllib.quote(query) + uri = 'http://duckduckgo.com/html/?q=%s&kl=uk-en' % query + bytes = web.get(uri) + m = r_duck.search(bytes) + if m: return web.decode(m.group(1)) + +def duck(phenny, input): + query = input.group(2) + if not query: return phenny.reply('.ddg what?') + + query = query.encode('utf-8') + uri = duck_search(query) + if uri: + phenny.reply(uri) + if not hasattr(phenny.bot, 'last_seen_uri'): + phenny.bot.last_seen_uri = {} + phenny.bot.last_seen_uri[input.sender] = uri + else: phenny.reply("No results found for '%s'." % query) duck.commands = ['duck', 'ddg'] -def search(phenny, input): - if not input.group(2): - return phenny.reply('.search for what?') - query = input.group(2).encode('utf-8') - gu = google_search(query) or '-' - bu = bing_search(query) or '-' - du = duck_search(query) or '-' - - if (gu == bu) and (bu == du): - result = '%s (g, b, d)' % gu - elif (gu == bu): - result = '%s (g, b), %s (d)' % (gu, du) - elif (bu == du): - result = '%s (b, d), %s (g)' % (bu, gu) - elif (gu == du): - result = '%s (g, d), %s (b)' % (gu, bu) - else: - if len(gu) > 250: gu = '(extremely long link)' - if len(bu) > 150: bu = '(extremely long link)' - if len(du) > 150: du = '(extremely long link)' - result = '%s (g), %s (b), %s (d)' % (gu, bu, du) - - phenny.reply(result) +def search(phenny, input): + if not input.group(2): + return phenny.reply('.search for what?') + query = input.group(2).encode('utf-8') + gu = google_search(query) or '-' + bu = bing_search(query) or '-' + du = duck_search(query) or '-' + + if (gu == bu) and (bu == du): + result = '%s (g, b, d)' % gu + elif (gu == bu): + result = '%s (g, b), %s (d)' % (gu, du) + elif (bu == du): + result = '%s (b, d), %s (g)' % (bu, gu) + elif (gu == du): + result = '%s (g, d), %s (b)' % (gu, bu) + else: + if len(gu) > 250: gu = '(extremely long link)' + if len(bu) > 150: bu = '(extremely long link)' + if len(du) > 150: du = '(extremely long link)' + result = '%s (g), %s (b), %s (d)' % (gu, bu, du) + + phenny.reply(result) search.commands = ['search'] -def suggest(phenny, input): - if not input.group(2): - return phenny.reply("No query term.") - query = input.group(2).encode('utf-8') - uri = 'http://websitedev.de/temp-bin/suggest.pl?q=' - answer = web.get(uri + web.urllib.quote(query).replace('+', '%2B')) - if answer: - phenny.say(answer) - else: phenny.reply('Sorry, no result.') +def suggest(phenny, input): + if not input.group(2): + return phenny.reply("No query term.") + query = input.group(2).encode('utf-8') + uri = 'http://websitedev.de/temp-bin/suggest.pl?q=' + answer = web.get(uri + web.urllib.quote(query).replace('+', '%2B')) + if answer: + phenny.say(answer) + else: phenny.reply('Sorry, no result.') suggest.commands = ['suggest'] def new_gc(query): - uri = 'https://www.google.com/search?hl=en&q=' - uri = uri + web.urllib.quote(query).replace('+', '%2B') - # if '"' in query: uri += '&tbs=li:1' - bytes = web.get(uri) - if "did not match any documents" in bytes: - return "0" - for result in re.compile(r'(?ims)([0-9,]+) results?').findall(bytes): - return result - return None + uri = 'https://www.google.com/search?hl=en&q=' + uri = uri + web.urllib.quote(query).replace('+', '%2B') + # if '"' in query: uri += '&tbs=li:1' + bytes = web.get(uri) + if "did not match any documents" in bytes: + return "0" + for result in re.compile(r'(?ims)([0-9,]+) results?').findall(bytes): + return result + return None def newest_gc(query): - uri = 'https://www.google.com/search?hl=en&q=' - uri = uri + web.urllib.quote(query).replace('+', '%2B') - bytes = web.get(uri + '&tbs=li:1') - if "did not match any documents" in bytes: - return "0" - for result in re.compile(r'(?ims)([0-9,]+) results?').findall(bytes): - return result - return None + uri = 'https://www.google.com/search?hl=en&q=' + uri = uri + web.urllib.quote(query).replace('+', '%2B') + bytes = web.get(uri + '&tbs=li:1') + if "did not match any documents" in bytes: + return "0" + for result in re.compile(r'(?ims)([0-9,]+) results?').findall(bytes): + return result + return None def newerest_gc(query): - uri = 'https://www.google.com/search?hl=en&q=' - uri = uri + web.urllib.quote(query).replace('+', '%2B') - bytes = web.get(uri + '&prmd=imvns&start=950') - if "did not match any documents" in bytes: - return "0" - for result in re.compile(r'(?ims)([0-9,]+) results?').findall(bytes): - return result - return None + uri = 'https://www.google.com/search?hl=en&q=' + uri = uri + web.urllib.quote(query).replace('+', '%2B') + bytes = web.get(uri + '&prmd=imvns&start=950') + if "did not match any documents" in bytes: + return "0" + for result in re.compile(r'(?ims)([0-9,]+) results?').findall(bytes): + return result + return None def ngc(phenny, input): - if not input.group(2): - return phenny.reply("No query term.") - query = input.group(2).encode('utf-8') - result = new_gc(query) - if result: - phenny.say(query + ": " + result) - else: phenny.reply("Sorry, couldn't get a result.") + if not input.group(2): + return phenny.reply("No query term.") + query = input.group(2).encode('utf-8') + result = new_gc(query) + if result: + phenny.say(query + ": " + result) + else: phenny.reply("Sorry, couldn't get a result.") ngc.commands = ['ngc'] ngc.priority = 'high' ngc.example = '.ngc extrapolate' def gc(phenny, input): - if not input.group(2): - return phenny.reply("No query term.") - query = input.group(2).encode('utf-8') - result = query + ": " - result += (old_gc(query) or "?") + " (api)" - result += ", " + (newerest_gc(query) or "?") + " (end)" - result += ", " + (new_gc(query) or "?") + " (site)" - if '"' in query: - result += ", " + (newest_gc(query) or "?") + " (verbatim)" - phenny.say(result) + if not input.group(2): + return phenny.reply("No query term.") + query = input.group(2).encode('utf-8') + result = query + ": " + result += (old_gc(query) or "?") + " (api)" + result += ", " + (newerest_gc(query) or "?") + " (end)" + result += ", " + (new_gc(query) or "?") + " (site)" + if '"' in query: + result += ", " + (newest_gc(query) or "?") + " (verbatim)" + phenny.say(result) gc.commands = ['gc'] gc.priority = 'high' gc.example = '.gc extrapolate' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/seen.py b/modules/seen.py index 8ed41a8..06dca3e 100755 --- a/modules/seen.py +++ b/modules/seen.py @@ -10,43 +10,43 @@ http://inamidst.com/phenny/ import time from tools import deprecated -def seen(phenny, input): - """.seen <nick> - Reports when <nick> was last seen.""" - nick = input.group(2) - if not nick: - return phenny.reply("Need a nickname to search for...") - nick = nick.lower() - - if not hasattr(phenny, 'seen'): - return phenny.reply("?") - - if phenny.seen.has_key(nick): - channel, t = phenny.seen[nick] - t = time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime(t)) - - msg = "I last saw %s at %s on %s" % (nick, t, channel) - phenny.reply(msg) - else: phenny.reply("Sorry, I haven't seen %s around." % nick) +def seen(phenny, input): + """.seen <nick> - Reports when <nick> was last seen.""" + nick = input.group(2) + if not nick: + return phenny.reply("Need a nickname to search for...") + nick = nick.lower() + + if not hasattr(phenny, 'seen'): + return phenny.reply("?") + + if phenny.seen.has_key(nick): + channel, t = phenny.seen[nick] + t = time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime(t)) + + msg = "I last saw %s at %s on %s" % (nick, t, channel) + phenny.reply(msg) + else: phenny.reply("Sorry, I haven't seen %s around." % nick) seen.rule = (['seen'], r'(\S+)') @deprecated -def f_note(self, origin, match, args): - def note(self, origin, match, args): - if not hasattr(self.bot, 'seen'): - self.bot.seen = {} - if origin.sender.startswith('#'): - # if origin.sender == '#inamidst': return - self.seen[origin.nick.lower()] = (origin.sender, time.time()) - - # if not hasattr(self, 'chanspeak'): - # self.chanspeak = {} - # if (len(args) > 2) and args[2].startswith('#'): - # self.chanspeak[args[2]] = args[0] - - try: note(self, origin, match, args) - except Exception, e: print e +def f_note(self, origin, match, args): + def note(self, origin, match, args): + if not hasattr(self.bot, 'seen'): + self.bot.seen = {} + if origin.sender.startswith('#'): + # if origin.sender == '#inamidst': return + self.seen[origin.nick.lower()] = (origin.sender, time.time()) + + # if not hasattr(self, 'chanspeak'): + # self.chanspeak = {} + # if (len(args) > 2) and args[2].startswith('#'): + # self.chanspeak[args[2]] = args[0] + + try: note(self, origin, match, args) + except Exception, e: print e f_note.rule = r'(.*)' f_note.priority = 'low' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/startup.py b/modules/startup.py index 81b3ecf..b9ad910 100755 --- a/modules/startup.py +++ b/modules/startup.py @@ -9,61 +9,61 @@ http://inamidst.com/phenny/ import threading, time -def setup(phenny): - print("Setting up phenny") - # by clsn - phenny.data = {} - refresh_delay = 300.0 +def setup(phenny): + print("Setting up phenny") + # by clsn + phenny.data = {} + refresh_delay = 300.0 - if hasattr(phenny.config, 'refresh_delay'): - try: refresh_delay = float(phenny.config.refresh_delay) - except: pass + if hasattr(phenny.config, 'refresh_delay'): + try: refresh_delay = float(phenny.config.refresh_delay) + except: pass - def close(): - print "Nobody PONGed our PING, restarting" - phenny.handle_close() - - def pingloop(): - timer = threading.Timer(refresh_delay, close, ()) - phenny.data['startup.setup.timer'] = timer - phenny.data['startup.setup.timer'].start() - # print "PING!" - phenny.write(('PING', phenny.config.host)) - phenny.data['startup.setup.pingloop'] = pingloop + def close(): + print "Nobody PONGed our PING, restarting" + phenny.handle_close() - def pong(phenny, input): - try: - # print "PONG!" - phenny.data['startup.setup.timer'].cancel() - time.sleep(refresh_delay + 60.0) - pingloop() - except: pass - pong.event = 'PONG' - pong.thread = True - pong.rule = r'.*' - phenny.variables['pong'] = pong + def pingloop(): + timer = threading.Timer(refresh_delay, close, ()) + phenny.data['startup.setup.timer'] = timer + phenny.data['startup.setup.timer'].start() + # print "PING!" + phenny.write(('PING', phenny.config.host)) + phenny.data['startup.setup.pingloop'] = pingloop -def startup(phenny, input): - import time + def pong(phenny, input): + try: + # print "PONG!" + phenny.data['startup.setup.timer'].cancel() + time.sleep(refresh_delay + 60.0) + pingloop() + except: pass + pong.event = 'PONG' + pong.thread = True + pong.rule = r'.*' + phenny.variables['pong'] = pong - # Start the ping loop. Has to be done after USER on e.g. quakenet - if phenny.data.get('startup.setup.pingloop'): - phenny.data['startup.setup.pingloop']() +def startup(phenny, input): + import time - if hasattr(phenny.config, 'serverpass'): - phenny.write(('PASS', phenny.config.serverpass)) + # Start the ping loop. Has to be done after USER on e.g. quakenet + if phenny.data.get('startup.setup.pingloop'): + phenny.data['startup.setup.pingloop']() - if hasattr(phenny.config, 'password'): - phenny.msg('NickServ', 'IDENTIFY %s' % phenny.config.password) - time.sleep(5) + if hasattr(phenny.config, 'serverpass'): + phenny.write(('PASS', phenny.config.serverpass)) - # Cf. http://swhack.com/logs/2005-12-05#T19-32-36 - for channel in phenny.channels: - phenny.write(('JOIN', channel)) - time.sleep(0.5) + if hasattr(phenny.config, 'password'): + phenny.msg('NickServ', 'IDENTIFY %s' % phenny.config.password) + time.sleep(5) + + # Cf. http://swhack.com/logs/2005-12-05#T19-32-36 + for channel in phenny.channels: + phenny.write(('JOIN', channel)) + time.sleep(0.5) startup.rule = r'(.*)' startup.event = '251' startup.priority = 'low' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/tell.py b/modules/tell.py index a82fad1..f230c06 100755 --- a/modules/tell.py +++ b/modules/tell.py @@ -23,136 +23,136 @@ lispchannels = frozenset([ '#lisp', '#scheme', '#opendarwin', '#macdev', '#perl6', '#sdlperl', '#ksvg', '#rcirc', '#code4lib', '#linux-quebec', '#programmering', '#maxima', '#robin', '##concurrency', '#paredit' ]) -def loadReminders(fn): - result = {} - f = open(fn) - for line in f: - line = line.strip() - if line: - try: tellee, teller, verb, timenow, msg = line.split('\t', 4) - except ValueError: continue # @@ hmm - result.setdefault(tellee, []).append((teller, verb, timenow, msg)) - f.close() - return result - -def dumpReminders(fn, data): - f = open(fn, 'w') - for tellee in data.iterkeys(): - for remindon in data[tellee]: - line = '\t'.join((tellee,) + remindon) - try: f.write(line + '\n') - except IOError: break - try: f.close() - except IOError: pass - return True - -def setup(self): - fn = self.nick + '-' + self.config.host + '.tell.db' - self.tell_filename = os.path.join(os.path.expanduser('~/.phenny'), fn) - if not os.path.exists(self.tell_filename): - try: f = open(self.tell_filename, 'w') - except OSError: pass - else: - f.write('') - f.close() - self.reminders = loadReminders(self.tell_filename) # @@ tell - -def f_remind(phenny, input): - teller = input.nick - - # @@ Multiple comma-separated tellees? Cf. Terje, #swhack, 2006-04-15 - verb, tellee, msg = input.groups() - verb = verb.encode('utf-8') - tellee = tellee.encode('utf-8') - msg = msg.encode('utf-8') - - tellee_original = tellee.rstrip('.,:;') - tellee = tellee_original.lower() - - if not os.path.exists(phenny.tell_filename): - return - - if len(tellee) > 20: - return phenny.reply('That nickname is too long.') - - timenow = time.strftime('%d %b %H:%MZ', time.gmtime()) - if not tellee in (teller.lower(), phenny.nick, 'me'): # @@ - # @@ <deltab> and year, if necessary - warn = False - if not phenny.reminders.has_key(tellee): - phenny.reminders[tellee] = [(teller, verb, timenow, msg)] - else: - # if len(phenny.reminders[tellee]) >= maximum: - # warn = True - phenny.reminders[tellee].append((teller, verb, timenow, msg)) - # @@ Stephanie's augmentation - response = "I'll pass that on when %s is around." % tellee_original - # if warn: response += (" I'll have to use a pastebin, though, so " + - # "your message may get lost.") - - rand = random.random() - if rand > 0.9999: response = "yeah, yeah" - elif rand > 0.999: response = "yeah, sure, whatever" - - phenny.reply(response) - elif teller.lower() == tellee: - phenny.say('You can %s yourself that.' % verb) - else: phenny.say("Hey, I'm not as stupid as Monty you know!") - - dumpReminders(phenny.tell_filename, phenny.reminders) # @@ tell +def loadReminders(fn): + result = {} + f = open(fn) + for line in f: + line = line.strip() + if line: + try: tellee, teller, verb, timenow, msg = line.split('\t', 4) + except ValueError: continue # @@ hmm + result.setdefault(tellee, []).append((teller, verb, timenow, msg)) + f.close() + return result + +def dumpReminders(fn, data): + f = open(fn, 'w') + for tellee in data.iterkeys(): + for remindon in data[tellee]: + line = '\t'.join((tellee,) + remindon) + try: f.write(line + '\n') + except IOError: break + try: f.close() + except IOError: pass + return True + +def setup(self): + fn = self.nick + '-' + self.config.host + '.tell.db' + self.tell_filename = os.path.join(os.path.expanduser('~/.phenny'), fn) + if not os.path.exists(self.tell_filename): + try: f = open(self.tell_filename, 'w') + except OSError: pass + else: + f.write('') + f.close() + self.reminders = loadReminders(self.tell_filename) # @@ tell + +def f_remind(phenny, input): + teller = input.nick + + # @@ Multiple comma-separated tellees? Cf. Terje, #swhack, 2006-04-15 + verb, tellee, msg = input.groups() + verb = verb.encode('utf-8') + tellee = tellee.encode('utf-8') + msg = msg.encode('utf-8') + + tellee_original = tellee.rstrip('.,:;') + tellee = tellee_original.lower() + + if not os.path.exists(phenny.tell_filename): + return + + if len(tellee) > 20: + return phenny.reply('That nickname is too long.') + + timenow = time.strftime('%d %b %H:%MZ', time.gmtime()) + if not tellee in (teller.lower(), phenny.nick, 'me'): # @@ + # @@ <deltab> and year, if necessary + warn = False + if not phenny.reminders.has_key(tellee): + phenny.reminders[tellee] = [(teller, verb, timenow, msg)] + else: + # if len(phenny.reminders[tellee]) >= maximum: + # warn = True + phenny.reminders[tellee].append((teller, verb, timenow, msg)) + # @@ Stephanie's augmentation + response = "I'll pass that on when %s is around." % tellee_original + # if warn: response += (" I'll have to use a pastebin, though, so " + + # "your message may get lost.") + + rand = random.random() + if rand > 0.9999: response = "yeah, yeah" + elif rand > 0.999: response = "yeah, sure, whatever" + + phenny.reply(response) + elif teller.lower() == tellee: + phenny.say('You can %s yourself that.' % verb) + else: phenny.say("Hey, I'm not as stupid as Monty you know!") + + dumpReminders(phenny.tell_filename, phenny.reminders) # @@ tell f_remind.rule = ('$nick', ['tell', 'ask'], r'(\S+) (.*)') -def getReminders(phenny, channel, key, tellee): - lines = [] - template = "%s: %s <%s> %s %s %s" - today = time.strftime('%d %b', time.gmtime()) - - for (teller, verb, datetime, msg) in phenny.reminders[key]: - if datetime.startswith(today): - datetime = datetime[len(today)+1:] - lines.append(template % (tellee, datetime, teller, verb, tellee, msg)) - - try: del phenny.reminders[key] - except KeyError: phenny.msg(channel, 'Er...') - return lines - -def message(phenny, input): - if not input.sender.startswith('#'): return - - tellee = input.nick - channel = input.sender - - if not os: return - if not os.path.exists(phenny.tell_filename): - return - - reminders = [] - remkeys = list(reversed(sorted(phenny.reminders.keys()))) - for remkey in remkeys: - if not remkey.endswith('*') or remkey.endswith(':'): - if tellee.lower() == remkey: +def getReminders(phenny, channel, key, tellee): + lines = [] + template = "%s: %s <%s> %s %s %s" + today = time.strftime('%d %b', time.gmtime()) + + for (teller, verb, datetime, msg) in phenny.reminders[key]: + if datetime.startswith(today): + datetime = datetime[len(today)+1:] + lines.append(template % (tellee, datetime, teller, verb, tellee, msg)) + + try: del phenny.reminders[key] + except KeyError: phenny.msg(channel, 'Er...') + return lines + +def message(phenny, input): + if not input.sender.startswith('#'): return + + tellee = input.nick + channel = input.sender + + if not os: return + if not os.path.exists(phenny.tell_filename): + return + + reminders = [] + remkeys = list(reversed(sorted(phenny.reminders.keys()))) + for remkey in remkeys: + if not remkey.endswith('*') or remkey.endswith(':'): + if tellee.lower() == remkey: + phenny.sending.acquire() + reminders.extend(getReminders(phenny, channel, remkey, tellee)) + phenny.sending.release() + elif tellee.lower().strip("0123456789_-[]`") == remkey.rstrip('*:'): phenny.sending.acquire() reminders.extend(getReminders(phenny, channel, remkey, tellee)) phenny.sending.release() - elif tellee.lower().strip("0123456789_-[]`") == remkey.rstrip('*:'): - phenny.sending.acquire() - reminders.extend(getReminders(phenny, channel, remkey, tellee)) - phenny.sending.release() - - for line in reminders[:maximum]: - phenny.say(line) - - if reminders[maximum:]: - phenny.say('Further messages sent privately') - for line in reminders[maximum:]: - phenny.msg(tellee, line) - - if len(phenny.reminders.keys()) != remkeys: - phenny.sending.acquire() - dumpReminders(phenny.tell_filename, phenny.reminders) # @@ tell - phenny.sending.release() + + for line in reminders[:maximum]: + phenny.say(line) + + if reminders[maximum:]: + phenny.say('Further messages sent privately') + for line in reminders[maximum:]: + phenny.msg(tellee, line) + + if len(phenny.reminders.keys()) != remkeys: + phenny.sending.acquire() + dumpReminders(phenny.tell_filename, phenny.reminders) # @@ tell + phenny.sending.release() message.rule = r'(.*)' message.priority = 'low' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/translate.py b/modules/translate.py index 11e4f28..dce080e 100755 --- a/modules/translate.py +++ b/modules/translate.py @@ -11,135 +11,135 @@ http://inamidst.com/phenny/ import re, urllib import web -def translate(text, input='auto', output='en'): - raw = False - if output.endswith('-raw'): - output = output[:-4] - raw = True +def translate(text, input='auto', output='en'): + raw = False + if output.endswith('-raw'): + output = output[:-4] + raw = True - import urllib2, json - opener = urllib2.build_opener() - opener.addheaders = [( - 'User-Agent', 'Mozilla/5.0' + - '(X11; U; Linux i686)' + - 'Gecko/20071127 Firefox/2.0.0.11' - )] + import urllib2, json + opener = urllib2.build_opener() + opener.addheaders = [( + 'User-Agent', 'Mozilla/5.0' + + '(X11; U; Linux i686)' + + 'Gecko/20071127 Firefox/2.0.0.11' + )] - input, output = urllib.quote(input), urllib.quote(output) - text = urllib.quote(text) + input, output = urllib.quote(input), urllib.quote(output) + text = urllib.quote(text) - result = opener.open('http://translate.google.com/translate_a/t?' + - ('client=t&hl=en&sl=%s&tl=%s&multires=1' % (input, output)) + - ('&otf=1&ssel=0&tsel=0&uptl=en&sc=1&text=%s' % text)).read() + result = opener.open('http://translate.google.com/translate_a/t?' + + ('client=t&hl=en&sl=%s&tl=%s&multires=1' % (input, output)) + + ('&otf=1&ssel=0&tsel=0&uptl=en&sc=1&text=%s' % text)).read() - while ',,' in result: - result = result.replace(',,', ',null,') - data = json.loads(result) + while ',,' in result: + result = result.replace(',,', ',null,') + data = json.loads(result) - if raw: - return str(data), 'en-raw' + if raw: + return str(data), 'en-raw' - try: language = data[2] # -2][0][0] - except: language = '?' + try: language = data[2] # -2][0][0] + except: language = '?' - return ''.join(x[0] for x in data[0]), language + return ''.join(x[0] for x in data[0]), language -def tr(phenny, context): - """Translates a phrase, with an optional language hint.""" - input, output, phrase = context.groups() +def tr(phenny, context): + """Translates a phrase, with an optional language hint.""" + input, output, phrase = context.groups() - phrase = phrase.encode('utf-8') + phrase = phrase.encode('utf-8') - if (len(phrase) > 350) and (not context.admin): - return phenny.reply('Phrase must be under 350 characters.') + if (len(phrase) > 350) and (not context.admin): + return phenny.reply('Phrase must be under 350 characters.') - input = input or 'auto' - input = input.encode('utf-8') - output = (output or 'en').encode('utf-8') + input = input or 'auto' + input = input.encode('utf-8') + output = (output or 'en').encode('utf-8') - if input != output: - msg, input = translate(phrase, input, output) - if isinstance(msg, str): - msg = msg.decode('utf-8') - if msg: - msg = web.decode(msg) # msg.replace(''', "'") - msg = '"%s" (%s to %s, translate.google.com)' % (msg, input, output) - else: msg = 'The %s to %s translation failed, sorry!' % (input, output) + if input != output: + msg, input = translate(phrase, input, output) + if isinstance(msg, str): + msg = msg.decode('utf-8') + if msg: + msg = web.decode(msg) # msg.replace(''', "'") + msg = '"%s" (%s to %s, translate.google.com)' % (msg, input, output) + else: msg = 'The %s to %s translation failed, sorry!' % (input, output) - phenny.reply(msg) - else: phenny.reply('Language guessing failed, so try suggesting one!') + phenny.reply(msg) + else: phenny.reply('Language guessing failed, so try suggesting one!') tr.rule = ('$nick', ur'(?:([a-z]{2}) +)?(?:([a-z]{2}|en-raw) +)?["“](.+?)["”]\? *$') tr.example = '$nickname: "mon chien"? or $nickname: fr "mon chien"?' tr.priority = 'low' -def tr2(phenny, input): - """Translates a phrase, with an optional language hint.""" - command = input.group(2) - if not command: - return phenny.reply("Need something to translate!") - command = command.encode('utf-8') - - def langcode(p): - return p.startswith(':') and (2 < len(p) < 10) and p[1:].isalpha() - - args = ['auto', 'en'] - - for i in xrange(2): - if not ' ' in command: break - prefix, cmd = command.split(' ', 1) - if langcode(prefix): - args[i] = prefix[1:] - command = cmd - phrase = command - - # if (len(phrase) > 350) and (not input.admin): - # return phenny.reply('Phrase must be under 350 characters.') - - src, dest = args - if src != dest: - msg, src = translate(phrase, src, dest) - if isinstance(msg, str): - msg = msg.decode('utf-8') - if msg: - msg = web.decode(msg) # msg.replace(''', "'") - if len(msg) > 450: msg = msg[:450] + '[...]' - msg = '"%s" (%s to %s, translate.google.com)' % (msg, src, dest) - else: msg = 'The %s to %s translation failed, sorry!' % (src, dest) - - phenny.reply(msg) - else: phenny.reply('Language guessing failed, so try suggesting one!') +def tr2(phenny, input): + """Translates a phrase, with an optional language hint.""" + command = input.group(2) + if not command: + return phenny.reply("Need something to translate!") + command = command.encode('utf-8') + + def langcode(p): + return p.startswith(':') and (2 < len(p) < 10) and p[1:].isalpha() + + args = ['auto', 'en'] + + for i in xrange(2): + if not ' ' in command: break + prefix, cmd = command.split(' ', 1) + if langcode(prefix): + args[i] = prefix[1:] + command = cmd + phrase = command + + # if (len(phrase) > 350) and (not input.admin): + # return phenny.reply('Phrase must be under 350 characters.') + + src, dest = args + if src != dest: + msg, src = translate(phrase, src, dest) + if isinstance(msg, str): + msg = msg.decode('utf-8') + if msg: + msg = web.decode(msg) # msg.replace(''', "'") + if len(msg) > 450: msg = msg[:450] + '[...]' + msg = '"%s" (%s to %s, translate.google.com)' % (msg, src, dest) + else: msg = 'The %s to %s translation failed, sorry!' % (src, dest) + + phenny.reply(msg) + else: phenny.reply('Language guessing failed, so try suggesting one!') tr2.commands = ['tr'] tr2.priority = 'low' -def mangle(phenny, input): - import time +def mangle(phenny, input): + import time - phrase = input.group(2).encode('utf-8') - for lang in ['fr', 'de', 'es', 'it', 'ja']: - backup = phrase[:] - phrase, _lang = translate(phrase, 'en', lang) - phrase = phrase.encode("utf-8") + phrase = input.group(2).encode('utf-8') + for lang in ['fr', 'de', 'es', 'it', 'ja']: + backup = phrase[:] + phrase, _lang = translate(phrase, 'en', lang) + phrase = phrase.encode("utf-8") - if not phrase: - phrase = backup[:] - break - time.sleep(0.25) + if not phrase: + phrase = backup[:] + break + time.sleep(0.25) - backup = phrase[:] - phrase, _lang = translate(phrase, lang, 'en') - phrase = phrase.encode("utf-8") + backup = phrase[:] + phrase, _lang = translate(phrase, lang, 'en') + phrase = phrase.encode("utf-8") - if not phrase: - phrase = backup[:] - break - time.sleep(0.25) + if not phrase: + phrase = backup[:] + break + time.sleep(0.25) - phrase = phrase.replace(' ,', ',').replace(' .', '.') - phrase = phrase.strip(' ,') - phenny.reply(phrase or 'ERRORS SRY') + phrase = phrase.replace(' ,', ',').replace(' .', '.') + phrase = phrase.strip(' ,') + phenny.reply(phrase or 'ERRORS SRY') mangle.commands = ['mangle'] -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/twitter.py b/modules/twitter.py index afd1d5c..0ec4744 100755 --- a/modules/twitter.py +++ b/modules/twitter.py @@ -20,74 +20,74 @@ r_whiteline = re.compile(r'(?ims)[ \t]+[\r\n]+') r_breaks = re.compile(r'(?ims)[\r\n]+') def entity(*args, **kargs): - return web.entity(*args, **kargs).encode('utf-8') + return web.entity(*args, **kargs).encode('utf-8') -def decode(html): - return web.r_entity.sub(entity, html) +def decode(html): + return web.r_entity.sub(entity, html) def expand(tweet): - def replacement(match): - anchor = match.group(1) - for link in r_expanded.findall(anchor): - return link - return r_tag.sub('', anchor) - return r_anchor.sub(replacement, tweet) + def replacement(match): + anchor = match.group(1) + for link in r_expanded.findall(anchor): + return link + return r_tag.sub('', anchor) + return r_anchor.sub(replacement, tweet) def read_tweet(url): - bytes = web.get(url) - shim = '<div class="content clearfix">' - if shim in bytes: - bytes = bytes.split(shim, 1).pop() - - for text in r_p.findall(bytes): - text = expand(text) - text = r_tag.sub('', text) - text = text.strip() - text = r_whiteline.sub(' ', text) - text = r_breaks.sub(' ', text) - return decode(text) - return "Sorry, couldn't get a tweet from %s" % url + bytes = web.get(url) + shim = '<div class="content clearfix">' + if shim in bytes: + bytes = bytes.split(shim, 1).pop() + + for text in r_p.findall(bytes): + text = expand(text) + text = r_tag.sub('', text) + text = text.strip() + text = r_whiteline.sub(' ', text) + text = r_breaks.sub(' ', text) + return decode(text) + return "Sorry, couldn't get a tweet from %s" % url def format(tweet, username): - return '%s (@%s)' % (tweet, username) + return '%s (@%s)' % (tweet, username) def user_tweet(username): - tweet = read_tweet('https://twitter.com/' + username + "?" + str(time.time())) - return format(tweet, username) + tweet = read_tweet('https://twitter.com/' + username + "?" + str(time.time())) + return format(tweet, username) def id_tweet(tid): - link = 'https://twitter.com/twitter/status/' + tid - data = web.head(link) - message, status = tuple(data) - if status == 301: - url = message.get("Location") - if not url: return "Sorry, couldn't get a tweet from %s" % link - username = url.split('/')[3] - tweet = read_tweet(url) - return format(tweet, username) - return "Sorry, couldn't get a tweet from %s" % link + link = 'https://twitter.com/twitter/status/' + tid + data = web.head(link) + message, status = tuple(data) + if status == 301: + url = message.get("Location") + if not url: return "Sorry, couldn't get a tweet from %s" % link + username = url.split('/')[3] + tweet = read_tweet(url) + return format(tweet, username) + return "Sorry, couldn't get a tweet from %s" % link def twitter(phenny, input): - arg = input.group(2) - if not arg: - return phenny.reply("Give me a link, a username, or a tweet id") - - arg = arg.strip() - if isinstance(arg, unicode): - arg = arg.encode('utf-8') - - if arg.isdigit(): - phenny.say(id_tweet(arg)) - elif r_username.match(arg): - phenny.say(user_tweet(arg)) - elif r_link.match(arg): - username = arg.split('/')[3] - tweet = read_tweet(arg) - phenny.say(format(tweet, username)) - else: phenny.reply("Give me a link, a username, or a tweet id") + arg = input.group(2) + if not arg: + return phenny.reply("Give me a link, a username, or a tweet id") + + arg = arg.strip() + if isinstance(arg, unicode): + arg = arg.encode('utf-8') + + if arg.isdigit(): + phenny.say(id_tweet(arg)) + elif r_username.match(arg): + phenny.say(user_tweet(arg)) + elif r_link.match(arg): + username = arg.split('/')[3] + tweet = read_tweet(arg) + phenny.say(format(tweet, username)) + else: phenny.reply("Give me a link, a username, or a tweet id") twitter.commands = ['tw', 'twitter'] twitter.thread = True if __name__ == '__main__': - print __doc__ + print __doc__ diff --git a/modules/validate.py b/modules/validate.py index 85815d1..ac036f5 100755 --- a/modules/validate.py +++ b/modules/validate.py @@ -9,35 +9,35 @@ http://inamidst.com/phenny/ import web -def val(phenny, input): - """Check a webpage using the W3C Markup Validator.""" - if not input.group(2): - return phenny.reply("Nothing to validate.") - uri = input.group(2) - if not uri.startswith('http://'): - uri = 'http://' + uri - - path = '/check?uri=%s;output=xml' % web.urllib.quote(uri) - info = web.head('http://validator.w3.org' + path) - - result = uri + ' is ' - - if isinstance(info, list): - return phenny.say('Got HTTP response %s' % info[1]) - - if info.has_key('X-W3C-Validator-Status'): - result += str(info['X-W3C-Validator-Status']) - if info['X-W3C-Validator-Status'] != 'Valid': - if info.has_key('X-W3C-Validator-Errors'): - n = int(info['X-W3C-Validator-Errors'].split(' ')[0]) - if n != 1: - result += ' (%s errors)' % n - else: result += ' (%s error)' % n - else: result += 'Unvalidatable: no X-W3C-Validator-Status' - - phenny.reply(result) +def val(phenny, input): + """Check a webpage using the W3C Markup Validator.""" + if not input.group(2): + return phenny.reply("Nothing to validate.") + uri = input.group(2) + if not uri.startswith('http://'): + uri = 'http://' + uri + + path = '/check?uri=%s;output=xml' % web.urllib.quote(uri) + info = web.head('http://validator.w3.org' + path) + + result = uri + ' is ' + + if isinstance(info, list): + return phenny.say('Got HTTP response %s' % info[1]) + + if info.has_key('X-W3C-Validator-Status'): + result += str(info['X-W3C-Validator-Status']) + if info['X-W3C-Validator-Status'] != 'Valid': + if info.has_key('X-W3C-Validator-Errors'): + n = int(info['X-W3C-Validator-Errors'].split(' ')[0]) + if n != 1: + result += ' (%s errors)' % n + else: result += ' (%s error)' % n + else: result += 'Unvalidatable: no X-W3C-Validator-Status' + + phenny.reply(result) val.rule = (['val'], r'(?i)(\S+)') val.example = '.val http://www.w3.org/' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/weather.py b/modules/weather.py index 29b01e5..5999b79 100755 --- a/modules/weather.py +++ b/modules/weather.py @@ -13,400 +13,400 @@ from tools import deprecated r_from = re.compile(r'(?i)([+-]\d+):00 from') -def location(name): - name = urllib.quote(name.encode('utf-8')) - uri = 'http://ws.geonames.org/searchJSON?q=%s&maxRows=1' % name - for i in xrange(10): - u = urllib.urlopen(uri) - if u is not None: break - bytes = u.read() - u.close() - - results = web.json(bytes) - try: name = results['geonames'][0]['name'] - except IndexError: - return '?', '?', '0', '0' - countryName = results['geonames'][0]['countryName'] - lat = results['geonames'][0]['lat'] - lng = results['geonames'][0]['lng'] - return name, countryName, lat, lng - -class GrumbleError(object): - pass - -def local(icao, hour, minute): - uri = ('http://www.flightstats.com/' + - 'go/Airport/airportDetails.do?airportCode=%s') - try: bytes = web.get(uri % icao) - except AttributeError: - raise GrumbleError('A WEBSITE HAS GONE DOWN WTF STUPID WEB') - m = r_from.search(bytes) - if m: - offset = m.group(1) - lhour = int(hour) + int(offset) - lhour = lhour % 24 - return (str(lhour) + ':' + str(minute) + ', ' + str(hour) + - str(minute) + 'Z') - # return (str(lhour) + ':' + str(minute) + ' (' + str(hour) + - # ':' + str(minute) + 'Z)') - return str(hour) + ':' + str(minute) + 'Z' - -def code(phenny, search): - from icao import data - - if search.upper() in [loc[0] for loc in data]: - return search.upper() - else: - name, country, latitude, longitude = location(search) - if name == '?': return False - sumOfSquares = (99999999999999999999999999999, 'ICAO') - for icao_code, lat, lon in data: - latDiff = abs(latitude - lat) - lonDiff = abs(longitude - lon) - diff = (latDiff * latDiff) + (lonDiff * lonDiff) - if diff < sumOfSquares[0]: - sumOfSquares = (diff, icao_code) - return sumOfSquares[1] +def location(name): + name = urllib.quote(name.encode('utf-8')) + uri = 'http://ws.geonames.org/searchJSON?q=%s&maxRows=1' % name + for i in xrange(10): + u = urllib.urlopen(uri) + if u is not None: break + bytes = u.read() + u.close() + + results = web.json(bytes) + try: name = results['geonames'][0]['name'] + except IndexError: + return '?', '?', '0', '0' + countryName = results['geonames'][0]['countryName'] + lat = results['geonames'][0]['lat'] + lng = results['geonames'][0]['lng'] + return name, countryName, lat, lng + +class GrumbleError(object): + pass + +def local(icao, hour, minute): + uri = ('http://www.flightstats.com/' + + 'go/Airport/airportDetails.do?airportCode=%s') + try: bytes = web.get(uri % icao) + except AttributeError: + raise GrumbleError('A WEBSITE HAS GONE DOWN WTF STUPID WEB') + m = r_from.search(bytes) + if m: + offset = m.group(1) + lhour = int(hour) + int(offset) + lhour = lhour % 24 + return (str(lhour) + ':' + str(minute) + ', ' + str(hour) + + str(minute) + 'Z') + # return (str(lhour) + ':' + str(minute) + ' (' + str(hour) + + # ':' + str(minute) + 'Z)') + return str(hour) + ':' + str(minute) + 'Z' + +def code(phenny, search): + from icao import data + + if search.upper() in [loc[0] for loc in data]: + return search.upper() + else: + name, country, latitude, longitude = location(search) + if name == '?': return False + sumOfSquares = (99999999999999999999999999999, 'ICAO') + for icao_code, lat, lon in data: + latDiff = abs(latitude - lat) + lonDiff = abs(longitude - lon) + diff = (latDiff * latDiff) + (lonDiff * lonDiff) + if diff < sumOfSquares[0]: + sumOfSquares = (diff, icao_code) + return sumOfSquares[1] @deprecated -def f_weather(self, origin, match, args): - """.weather <ICAO> - Show the weather at airport with the code <ICAO>.""" - if origin.sender == '#talis': - if args[0].startswith('.weather '): return - - icao_code = match.group(2) - if not icao_code: - return self.msg(origin.sender, 'Try .weather London, for example?') - - icao_code = code(self, icao_code) - - if not icao_code: - self.msg(origin.sender, 'No ICAO code found, sorry') - return - - uri = 'http://weather.noaa.gov/pub/data/observations/metar/stations/%s.TXT' - try: bytes = web.get(uri % icao_code) - except AttributeError: - raise GrumbleError('OH CRAP NOAA HAS GONE DOWN THE WEB IS BROKEN') - if 'Not Found' in bytes: - self.msg(origin.sender, icao_code+': no such ICAO code, or no NOAA data') - return - - metar = bytes.splitlines().pop() - metar = metar.split(' ') - - if len(metar[0]) == 4: - metar = metar[1:] - - if metar[0].endswith('Z'): - time = metar[0] - metar = metar[1:] - else: time = None - - if metar[0] == 'AUTO': - metar = metar[1:] - if metar[0] == 'VCU': - self.msg(origin.sender, icao_code + ': no data provided') - return - - if metar[0].endswith('KT'): - wind = metar[0] - metar = metar[1:] - else: wind = None - - if ('V' in metar[0]) and (metar[0] != 'CAVOK'): - vari = metar[0] - metar = metar[1:] - else: vari = None - - if ((len(metar[0]) == 4) or - metar[0].endswith('SM')): - visibility = metar[0] - metar = metar[1:] - else: visibility = None - - while metar[0].startswith('R') and (metar[0].endswith('L') - or 'L/' in metar[0]): - metar = metar[1:] - - if len(metar[0]) == 6 and (metar[0].endswith('N') or - metar[0].endswith('E') or - metar[0].endswith('S') or - metar[0].endswith('W')): - metar = metar[1:] # 7000SE? - - cond = [] - while (((len(metar[0]) < 5) or - metar[0].startswith('+') or - metar[0].startswith('-')) and (not (metar[0].startswith('VV') or - metar[0].startswith('SKC') or metar[0].startswith('CLR') or - metar[0].startswith('FEW') or metar[0].startswith('SCT') or - metar[0].startswith('BKN') or metar[0].startswith('OVC')))): - cond.append(metar[0]) - metar = metar[1:] - - while '/P' in metar[0]: - metar = metar[1:] - - if not metar: - self.msg(origin.sender, icao_code + ': no data provided') - return - - cover = [] - while (metar[0].startswith('VV') or metar[0].startswith('SKC') or - metar[0].startswith('CLR') or metar[0].startswith('FEW') or - metar[0].startswith('SCT') or metar[0].startswith('BKN') or - metar[0].startswith('OVC')): - cover.append(metar[0]) - metar = metar[1:] - if not metar: - self.msg(origin.sender, icao_code + ': no data provided') - return - - if metar[0] == 'CAVOK': - cover.append('CLR') - metar = metar[1:] - - if metar[0] == 'PRFG': - cover.append('CLR') # @@? - metar = metar[1:] - - if metar[0] == 'NSC': - cover.append('CLR') - metar = metar[1:] - - if ('/' in metar[0]) or (len(metar[0]) == 5 and metar[0][2] == '.'): - temp = metar[0] - metar = metar[1:] - else: temp = None - - if metar[0].startswith('QFE'): - metar = metar[1:] - - if metar[0].startswith('Q') or metar[0].startswith('A'): - pressure = metar[0] - metar = metar[1:] - else: pressure = None - - if time: - hour = time[2:4] - minute = time[4:6] - time = local(icao_code, hour, minute) - else: time = '(time unknown)' - - if wind: - speed = int(wind[3:5]) - if speed < 1: - description = 'Calm' - elif speed < 4: - description = 'Light air' - elif speed < 7: - description = 'Light breeze' - elif speed < 11: - description = 'Gentle breeze' - elif speed < 16: - description = 'Moderate breeze' - elif speed < 22: - description = 'Fresh breeze' - elif speed < 28: - description = 'Strong breeze' - elif speed < 34: - description = 'Near gale' - elif speed < 41: - description = 'Gale' - elif speed < 48: - description = 'Strong gale' - elif speed < 56: - description = 'Storm' - elif speed < 64: - description = 'Violent storm' - else: description = 'Hurricane' - - degrees = wind[0:3] - if degrees == 'VRB': - degrees = u'\u21BB'.encode('utf-8') - elif (degrees <= 22.5) or (degrees > 337.5): - degrees = u'\u2191'.encode('utf-8') - elif (degrees > 22.5) and (degrees <= 67.5): - degrees = u'\u2197'.encode('utf-8') - elif (degrees > 67.5) and (degrees <= 112.5): - degrees = u'\u2192'.encode('utf-8') - elif (degrees > 112.5) and (degrees <= 157.5): - degrees = u'\u2198'.encode('utf-8') - elif (degrees > 157.5) and (degrees <= 202.5): - degrees = u'\u2193'.encode('utf-8') - elif (degrees > 202.5) and (degrees <= 247.5): - degrees = u'\u2199'.encode('utf-8') - elif (degrees > 247.5) and (degrees <= 292.5): - degrees = u'\u2190'.encode('utf-8') - elif (degrees > 292.5) and (degrees <= 337.5): - degrees = u'\u2196'.encode('utf-8') - - if not icao_code.startswith('EN') and not icao_code.startswith('ED'): - wind = '%s %skt (%s)' % (description, speed, degrees) - elif icao_code.startswith('ED'): - kmh = int(round(speed * 1.852, 0)) - wind = '%s %skm/h (%skt) (%s)' % (description, kmh, speed, degrees) - elif icao_code.startswith('EN'): - ms = int(round(speed * 0.514444444, 0)) - wind = '%s %sm/s (%skt) (%s)' % (description, ms, speed, degrees) - else: wind = '(wind unknown)' - - if visibility: - visibility = visibility + 'm' - else: visibility = '(visibility unknown)' - - if cover: - level = None - for c in cover: - if c.startswith('OVC') or c.startswith('VV'): - if (level is None) or (level < 8): - level = 8 - elif c.startswith('BKN'): - if (level is None) or (level < 5): - level = 5 - elif c.startswith('SCT'): - if (level is None) or (level < 3): - level = 3 - elif c.startswith('FEW'): - if (level is None) or (level < 1): - level = 1 - elif c.startswith('SKC') or c.startswith('CLR'): - if level is None: - level = 0 - - if level == 8: - cover = u'Overcast \u2601'.encode('utf-8') - elif level == 5: - cover = 'Cloudy' - elif level == 3: - cover = 'Scattered' - elif (level == 1) or (level == 0): - cover = u'Clear \u263C'.encode('utf-8') - else: cover = 'Cover Unknown' - else: cover = 'Cover Unknown' - - if temp: - if '/' in temp: - temp = temp.split('/')[0] - else: temp = temp.split('.')[0] - if temp.startswith('M'): - temp = '-' + temp[1:] - try: temp = int(temp) - except ValueError: temp = '?' - else: temp = '?' - - if pressure: - if pressure.startswith('Q'): - pressure = pressure.lstrip('Q') - if pressure != 'NIL': - pressure = str(int(pressure)) + 'mb' - else: pressure = '?mb' - elif pressure.startswith('A'): - pressure = pressure.lstrip('A') - if pressure != 'NIL': - inches = pressure[:2] + '.' + pressure[2:] - mb = int(float(inches) * 33.7685) - pressure = '%sin (%smb)' % (inches, mb) - else: pressure = '?mb' - - if isinstance(temp, int): - f = round((temp * 1.8) + 32, 2) - temp = u'%s\u2109 (%s\u2103)'.encode('utf-8') % (f, temp) - else: pressure = '?mb' - if isinstance(temp, int): - temp = u'%s\u2103'.encode('utf-8') % temp - - if cond: - conds = cond - cond = '' - - intensities = { - '-': 'Light', - '+': 'Heavy' - } - - descriptors = { - 'MI': 'Shallow', - 'PR': 'Partial', - 'BC': 'Patches', - 'DR': 'Drifting', - 'BL': 'Blowing', - 'SH': 'Showers of', - 'TS': 'Thundery', - 'FZ': 'Freezing', - 'VC': 'In the vicinity:' - } - - phenomena = { - 'DZ': 'Drizzle', - 'RA': 'Rain', - 'SN': 'Snow', - 'SG': 'Snow Grains', - 'IC': 'Ice Crystals', - 'PL': 'Ice Pellets', - 'GR': 'Hail', - 'GS': 'Small Hail', - 'UP': 'Unknown Precipitation', - 'BR': 'Mist', - 'FG': 'Fog', - 'FU': 'Smoke', - 'VA': 'Volcanic Ash', - 'DU': 'Dust', - 'SA': 'Sand', - 'HZ': 'Haze', - 'PY': 'Spray', - 'PO': 'Whirls', - 'SQ': 'Squalls', - 'FC': 'Tornado', - 'SS': 'Sandstorm', - 'DS': 'Duststorm', - # ? Cf. http://swhack.com/logs/2007-10-05#T07-58-56 - 'TS': 'Thunderstorm', - 'SH': 'Showers' - } - - for c in conds: - if c.endswith('//'): - if cond: cond += ', ' - cond += 'Some Precipitation' - elif len(c) == 5: - intensity = intensities[c[0]] - descriptor = descriptors[c[1:3]] - phenomenon = phenomena.get(c[3:], c[3:]) - if cond: cond += ', ' - cond += intensity + ' ' + descriptor + ' ' + phenomenon - elif len(c) == 4: - descriptor = descriptors.get(c[:2], c[:2]) - phenomenon = phenomena.get(c[2:], c[2:]) - if cond: cond += ', ' - cond += descriptor + ' ' + phenomenon - elif len(c) == 3: - intensity = intensities.get(c[0], c[0]) - phenomenon = phenomena.get(c[1:], c[1:]) - if cond: cond += ', ' - cond += intensity + ' ' + phenomenon - elif len(c) == 2: - phenomenon = phenomena.get(c, c) - if cond: cond += ', ' - cond += phenomenon - - # if not cond: - # format = u'%s at %s: %s, %s, %s, %s' - # args = (icao, time, cover, temp, pressure, wind) - # else: - # format = u'%s at %s: %s, %s, %s, %s, %s' - # args = (icao, time, cover, temp, pressure, cond, wind) - - if not cond: - format = u'%s, %s, %s, %s - %s %s' - args = (cover, temp, pressure, wind, str(icao_code), time) - else: - format = u'%s, %s, %s, %s, %s - %s, %s' - args = (cover, temp, pressure, cond, wind, str(icao_code), time) - - self.msg(origin.sender, format.encode('utf-8') % args) +def f_weather(self, origin, match, args): + """.weather <ICAO> - Show the weather at airport with the code <ICAO>.""" + if origin.sender == '#talis': + if args[0].startswith('.weather '): return + + icao_code = match.group(2) + if not icao_code: + return self.msg(origin.sender, 'Try .weather London, for example?') + + icao_code = code(self, icao_code) + + if not icao_code: + self.msg(origin.sender, 'No ICAO code found, sorry') + return + + uri = 'http://weather.noaa.gov/pub/data/observations/metar/stations/%s.TXT' + try: bytes = web.get(uri % icao_code) + except AttributeError: + raise GrumbleError('OH CRAP NOAA HAS GONE DOWN THE WEB IS BROKEN') + if 'Not Found' in bytes: + self.msg(origin.sender, icao_code+': no such ICAO code, or no NOAA data') + return + + metar = bytes.splitlines().pop() + metar = metar.split(' ') + + if len(metar[0]) == 4: + metar = metar[1:] + + if metar[0].endswith('Z'): + time = metar[0] + metar = metar[1:] + else: time = None + + if metar[0] == 'AUTO': + metar = metar[1:] + if metar[0] == 'VCU': + self.msg(origin.sender, icao_code + ': no data provided') + return + + if metar[0].endswith('KT'): + wind = metar[0] + metar = metar[1:] + else: wind = None + + if ('V' in metar[0]) and (metar[0] != 'CAVOK'): + vari = metar[0] + metar = metar[1:] + else: vari = None + + if ((len(metar[0]) == 4) or + metar[0].endswith('SM')): + visibility = metar[0] + metar = metar[1:] + else: visibility = None + + while metar[0].startswith('R') and (metar[0].endswith('L') + or 'L/' in metar[0]): + metar = metar[1:] + + if len(metar[0]) == 6 and (metar[0].endswith('N') or + metar[0].endswith('E') or + metar[0].endswith('S') or + metar[0].endswith('W')): + metar = metar[1:] # 7000SE? + + cond = [] + while (((len(metar[0]) < 5) or + metar[0].startswith('+') or + metar[0].startswith('-')) and (not (metar[0].startswith('VV') or + metar[0].startswith('SKC') or metar[0].startswith('CLR') or + metar[0].startswith('FEW') or metar[0].startswith('SCT') or + metar[0].startswith('BKN') or metar[0].startswith('OVC')))): + cond.append(metar[0]) + metar = metar[1:] + + while '/P' in metar[0]: + metar = metar[1:] + + if not metar: + self.msg(origin.sender, icao_code + ': no data provided') + return + + cover = [] + while (metar[0].startswith('VV') or metar[0].startswith('SKC') or + metar[0].startswith('CLR') or metar[0].startswith('FEW') or + metar[0].startswith('SCT') or metar[0].startswith('BKN') or + metar[0].startswith('OVC')): + cover.append(metar[0]) + metar = metar[1:] + if not metar: + self.msg(origin.sender, icao_code + ': no data provided') + return + + if metar[0] == 'CAVOK': + cover.append('CLR') + metar = metar[1:] + + if metar[0] == 'PRFG': + cover.append('CLR') # @@? + metar = metar[1:] + + if metar[0] == 'NSC': + cover.append('CLR') + metar = metar[1:] + + if ('/' in metar[0]) or (len(metar[0]) == 5 and metar[0][2] == '.'): + temp = metar[0] + metar = metar[1:] + else: temp = None + + if metar[0].startswith('QFE'): + metar = metar[1:] + + if metar[0].startswith('Q') or metar[0].startswith('A'): + pressure = metar[0] + metar = metar[1:] + else: pressure = None + + if time: + hour = time[2:4] + minute = time[4:6] + time = local(icao_code, hour, minute) + else: time = '(time unknown)' + + if wind: + speed = int(wind[3:5]) + if speed < 1: + description = 'Calm' + elif speed < 4: + description = 'Light air' + elif speed < 7: + description = 'Light breeze' + elif speed < 11: + description = 'Gentle breeze' + elif speed < 16: + description = 'Moderate breeze' + elif speed < 22: + description = 'Fresh breeze' + elif speed < 28: + description = 'Strong breeze' + elif speed < 34: + description = 'Near gale' + elif speed < 41: + description = 'Gale' + elif speed < 48: + description = 'Strong gale' + elif speed < 56: + description = 'Storm' + elif speed < 64: + description = 'Violent storm' + else: description = 'Hurricane' + + degrees = wind[0:3] + if degrees == 'VRB': + degrees = u'\u21BB'.encode('utf-8') + elif (degrees <= 22.5) or (degrees > 337.5): + degrees = u'\u2191'.encode('utf-8') + elif (degrees > 22.5) and (degrees <= 67.5): + degrees = u'\u2197'.encode('utf-8') + elif (degrees > 67.5) and (degrees <= 112.5): + degrees = u'\u2192'.encode('utf-8') + elif (degrees > 112.5) and (degrees <= 157.5): + degrees = u'\u2198'.encode('utf-8') + elif (degrees > 157.5) and (degrees <= 202.5): + degrees = u'\u2193'.encode('utf-8') + elif (degrees > 202.5) and (degrees <= 247.5): + degrees = u'\u2199'.encode('utf-8') + elif (degrees > 247.5) and (degrees <= 292.5): + degrees = u'\u2190'.encode('utf-8') + elif (degrees > 292.5) and (degrees <= 337.5): + degrees = u'\u2196'.encode('utf-8') + + if not icao_code.startswith('EN') and not icao_code.startswith('ED'): + wind = '%s %skt (%s)' % (description, speed, degrees) + elif icao_code.startswith('ED'): + kmh = int(round(speed * 1.852, 0)) + wind = '%s %skm/h (%skt) (%s)' % (description, kmh, speed, degrees) + elif icao_code.startswith('EN'): + ms = int(round(speed * 0.514444444, 0)) + wind = '%s %sm/s (%skt) (%s)' % (description, ms, speed, degrees) + else: wind = '(wind unknown)' + + if visibility: + visibility = visibility + 'm' + else: visibility = '(visibility unknown)' + + if cover: + level = None + for c in cover: + if c.startswith('OVC') or c.startswith('VV'): + if (level is None) or (level < 8): + level = 8 + elif c.startswith('BKN'): + if (level is None) or (level < 5): + level = 5 + elif c.startswith('SCT'): + if (level is None) or (level < 3): + level = 3 + elif c.startswith('FEW'): + if (level is None) or (level < 1): + level = 1 + elif c.startswith('SKC') or c.startswith('CLR'): + if level is None: + level = 0 + + if level == 8: + cover = u'Overcast \u2601'.encode('utf-8') + elif level == 5: + cover = 'Cloudy' + elif level == 3: + cover = 'Scattered' + elif (level == 1) or (level == 0): + cover = u'Clear \u263C'.encode('utf-8') + else: cover = 'Cover Unknown' + else: cover = 'Cover Unknown' + + if temp: + if '/' in temp: + temp = temp.split('/')[0] + else: temp = temp.split('.')[0] + if temp.startswith('M'): + temp = '-' + temp[1:] + try: temp = int(temp) + except ValueError: temp = '?' + else: temp = '?' + + if pressure: + if pressure.startswith('Q'): + pressure = pressure.lstrip('Q') + if pressure != 'NIL': + pressure = str(int(pressure)) + 'mb' + else: pressure = '?mb' + elif pressure.startswith('A'): + pressure = pressure.lstrip('A') + if pressure != 'NIL': + inches = pressure[:2] + '.' + pressure[2:] + mb = int(float(inches) * 33.7685) + pressure = '%sin (%smb)' % (inches, mb) + else: pressure = '?mb' + + if isinstance(temp, int): + f = round((temp * 1.8) + 32, 2) + temp = u'%s\u2109 (%s\u2103)'.encode('utf-8') % (f, temp) + else: pressure = '?mb' + if isinstance(temp, int): + temp = u'%s\u2103'.encode('utf-8') % temp + + if cond: + conds = cond + cond = '' + + intensities = { + '-': 'Light', + '+': 'Heavy' + } + + descriptors = { + 'MI': 'Shallow', + 'PR': 'Partial', + 'BC': 'Patches', + 'DR': 'Drifting', + 'BL': 'Blowing', + 'SH': 'Showers of', + 'TS': 'Thundery', + 'FZ': 'Freezing', + 'VC': 'In the vicinity:' + } + + phenomena = { + 'DZ': 'Drizzle', + 'RA': 'Rain', + 'SN': 'Snow', + 'SG': 'Snow Grains', + 'IC': 'Ice Crystals', + 'PL': 'Ice Pellets', + 'GR': 'Hail', + 'GS': 'Small Hail', + 'UP': 'Unknown Precipitation', + 'BR': 'Mist', + 'FG': 'Fog', + 'FU': 'Smoke', + 'VA': 'Volcanic Ash', + 'DU': 'Dust', + 'SA': 'Sand', + 'HZ': 'Haze', + 'PY': 'Spray', + 'PO': 'Whirls', + 'SQ': 'Squalls', + 'FC': 'Tornado', + 'SS': 'Sandstorm', + 'DS': 'Duststorm', + # ? Cf. http://swhack.com/logs/2007-10-05#T07-58-56 + 'TS': 'Thunderstorm', + 'SH': 'Showers' + } + + for c in conds: + if c.endswith('//'): + if cond: cond += ', ' + cond += 'Some Precipitation' + elif len(c) == 5: + intensity = intensities[c[0]] + descriptor = descriptors[c[1:3]] + phenomenon = phenomena.get(c[3:], c[3:]) + if cond: cond += ', ' + cond += intensity + ' ' + descriptor + ' ' + phenomenon + elif len(c) == 4: + descriptor = descriptors.get(c[:2], c[:2]) + phenomenon = phenomena.get(c[2:], c[2:]) + if cond: cond += ', ' + cond += descriptor + ' ' + phenomenon + elif len(c) == 3: + intensity = intensities.get(c[0], c[0]) + phenomenon = phenomena.get(c[1:], c[1:]) + if cond: cond += ', ' + cond += intensity + ' ' + phenomenon + elif len(c) == 2: + phenomenon = phenomena.get(c, c) + if cond: cond += ', ' + cond += phenomenon + + # if not cond: + # format = u'%s at %s: %s, %s, %s, %s' + # args = (icao, time, cover, temp, pressure, wind) + # else: + # format = u'%s at %s: %s, %s, %s, %s, %s' + # args = (icao, time, cover, temp, pressure, cond, wind) + + if not cond: + format = u'%s, %s, %s, %s - %s %s' + args = (cover, temp, pressure, wind, str(icao_code), time) + else: + format = u'%s, %s, %s, %s, %s - %s, %s' + args = (cover, temp, pressure, cond, wind, str(icao_code), time) + + self.msg(origin.sender, format.encode('utf-8') % args) f_weather.rule = (['weather'], r'(.*)') -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/wikipedia.py b/modules/wikipedia.py index 1a406b3..489afe9 100755 --- a/modules/wikipedia.py +++ b/modules/wikipedia.py @@ -12,171 +12,171 @@ import web wikiuri = 'http://%s.wikipedia.org/wiki/%s' # wikisearch = 'http://%s.wikipedia.org/wiki/Special:Search?' \ -# + 'search=%s&fulltext=Search' +# + 'search=%s&fulltext=Search' r_tr = re.compile(r'(?ims)<tr[^>]*>.*?</tr>') r_paragraph = re.compile(r'(?ims)<p[^>]*>.*?</p>|<li(?!n)[^>]*>.*?</li>') r_tag = re.compile(r'<(?!!)[^>]+>') r_whitespace = re.compile(r'[\t\r\n ]+') r_redirect = re.compile( - r'(?ims)class=.redirectText.>\s*<a\s*href=./wiki/([^"/]+)' + r'(?ims)class=.redirectText.>\s*<a\s*href=./wiki/([^"/]+)' ) -abbrs = ['etc', 'ca', 'cf', 'Co', 'Ltd', 'Inc', 'Mt', 'Mr', 'Mrs', - 'Dr', 'Ms', 'Rev', 'Fr', 'St', 'Sgt', 'pron', 'approx', 'lit', - 'syn', 'transl', 'sess', 'fl', 'Op', 'Dec', 'Brig', 'Gen'] \ - + list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') \ - + list('abcdefghijklmnopqrstuvwxyz') +abbrs = ['etc', 'ca', 'cf', 'Co', 'Ltd', 'Inc', 'Mt', 'Mr', 'Mrs', + 'Dr', 'Ms', 'Rev', 'Fr', 'St', 'Sgt', 'pron', 'approx', 'lit', + 'syn', 'transl', 'sess', 'fl', 'Op', 'Dec', 'Brig', 'Gen'] \ + + list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') \ + + list('abcdefghijklmnopqrstuvwxyz') t_sentence = r'^.{5,}?(?<!\b%s)(?:\.(?=[\[ ][A-Z0-9]|\Z)|\Z)' r_sentence = re.compile(t_sentence % r')(?<!\b'.join(abbrs)) -def unescape(s): - s = s.replace('>', '>') - s = s.replace('<', '<') - s = s.replace('&', '&') - s = s.replace(' ', ' ') - return s - -def text(html): - html = r_tag.sub('', html) - html = r_whitespace.sub(' ', html) - return unescape(html).strip() - -def search(term): - try: import search - except ImportError, e: - print e - return term - - if isinstance(term, unicode): - term = term.encode('utf-8') - else: term = term.decode('utf-8') - - term = term.replace('_', ' ') - try: uri = search.google_search('site:en.wikipedia.org %s' % term) - except IndexError: return term - if uri: - return uri[len('http://en.wikipedia.org/wiki/'):] - else: return term - -def wikipedia(term, language='en', last=False): - global wikiuri - if not '%' in term: - if isinstance(term, unicode): - t = term.encode('utf-8') - else: t = term - q = urllib.quote(t) - u = wikiuri % (language, q) - bytes = web.get(u) - else: bytes = web.get(wikiuri % (language, term)) - - if bytes.startswith('\x1f\x8b\x08\x00\x00\x00\x00\x00'): - f = StringIO.StringIO(bytes) - f.seek(0) - gzip_file = gzip.GzipFile(fileobj=f) - bytes = gzip_file.read() - gzip_file.close() - f.close() - - bytes = r_tr.sub('', bytes) - - if not last: - r = r_redirect.search(bytes[:4096]) - if r: - term = urllib.unquote(r.group(1)) - return wikipedia(term, language=language, last=True) - - paragraphs = r_paragraph.findall(bytes) - - if not paragraphs: - if not last: - term = search(term) - return wikipedia(term, language=language, last=True) - return None - - # Pre-process - paragraphs = [para for para in paragraphs - if (para and 'technical limitations' not in para - and 'window.showTocToggle' not in para - and 'Deletion_policy' not in para - and 'Template:AfD_footer' not in para - and not (para.startswith('<p><i>') and - para.endswith('</i></p>')) - and not 'disambiguation)"' in para) - and not '(images and media)' in para - and not 'This article contains a' in para - and not 'id="coordinates"' in para - and not 'class="thumb' in para] - # and not 'style="display:none"' in para] - - for i, para in enumerate(paragraphs): - para = para.replace('<sup>', '|') - para = para.replace('</sup>', '|') - paragraphs[i] = text(para).strip() - - # Post-process - paragraphs = [para for para in paragraphs if - (para and not (para.endswith(':') and len(para) < 150))] - - para = text(paragraphs[0]) - m = r_sentence.match(para) - - if not m: - if not last: - term = search(term) - return wikipedia(term, language=language, last=True) - return None - sentence = m.group(0) - - maxlength = 275 - if len(sentence) > maxlength: - sentence = sentence[:maxlength] - words = sentence[:-5].split(' ') - words.pop() - sentence = ' '.join(words) + ' [...]' - - if (('using the Article Wizard if you wish' in sentence) - or ('or add a request for it' in sentence) - or ('in existing articles' in sentence)): - if not last: - term = search(term) - return wikipedia(term, language=language, last=True) - return None - - sentence = '"' + sentence.replace('"', "'") + '"' - sentence = sentence.decode('utf-8').encode('utf-8') - wikiuri = wikiuri.decode('utf-8').encode('utf-8') - term = term.decode('utf-8').encode('utf-8') - return sentence + ' - ' + (wikiuri % (language, term)) - -def wik(phenny, input): - origterm = input.groups()[1] - if not origterm: - return phenny.say('Perhaps you meant ".wik Zen"?') - origterm = origterm.encode('utf-8') - - term = urllib.unquote(origterm) - language = 'en' - if term.startswith(':') and (' ' in term): - a, b = term.split(' ', 1) - a = a.lstrip(':') - if a.isalpha(): - language, term = a, b - term = term[0].upper() + term[1:] - term = term.replace(' ', '_') - - try: result = wikipedia(term, language) - except IOError: - args = (language, wikiuri % (language, term)) - error = "Can't connect to %s.wikipedia.org (%s)" % args - return phenny.say(error) - - if result is not None: - phenny.say(result) - else: phenny.say('Can\'t find anything in Wikipedia for "%s".' % origterm) +def unescape(s): + s = s.replace('>', '>') + s = s.replace('<', '<') + s = s.replace('&', '&') + s = s.replace(' ', ' ') + return s + +def text(html): + html = r_tag.sub('', html) + html = r_whitespace.sub(' ', html) + return unescape(html).strip() + +def search(term): + try: import search + except ImportError, e: + print e + return term + + if isinstance(term, unicode): + term = term.encode('utf-8') + else: term = term.decode('utf-8') + + term = term.replace('_', ' ') + try: uri = search.google_search('site:en.wikipedia.org %s' % term) + except IndexError: return term + if uri: + return uri[len('http://en.wikipedia.org/wiki/'):] + else: return term + +def wikipedia(term, language='en', last=False): + global wikiuri + if not '%' in term: + if isinstance(term, unicode): + t = term.encode('utf-8') + else: t = term + q = urllib.quote(t) + u = wikiuri % (language, q) + bytes = web.get(u) + else: bytes = web.get(wikiuri % (language, term)) + + if bytes.startswith('\x1f\x8b\x08\x00\x00\x00\x00\x00'): + f = StringIO.StringIO(bytes) + f.seek(0) + gzip_file = gzip.GzipFile(fileobj=f) + bytes = gzip_file.read() + gzip_file.close() + f.close() + + bytes = r_tr.sub('', bytes) + + if not last: + r = r_redirect.search(bytes[:4096]) + if r: + term = urllib.unquote(r.group(1)) + return wikipedia(term, language=language, last=True) + + paragraphs = r_paragraph.findall(bytes) + + if not paragraphs: + if not last: + term = search(term) + return wikipedia(term, language=language, last=True) + return None + + # Pre-process + paragraphs = [para for para in paragraphs + if (para and 'technical limitations' not in para + and 'window.showTocToggle' not in para + and 'Deletion_policy' not in para + and 'Template:AfD_footer' not in para + and not (para.startswith('<p><i>') and + para.endswith('</i></p>')) + and not 'disambiguation)"' in para) + and not '(images and media)' in para + and not 'This article contains a' in para + and not 'id="coordinates"' in para + and not 'class="thumb' in para] + # and not 'style="display:none"' in para] + + for i, para in enumerate(paragraphs): + para = para.replace('<sup>', '|') + para = para.replace('</sup>', '|') + paragraphs[i] = text(para).strip() + + # Post-process + paragraphs = [para for para in paragraphs if + (para and not (para.endswith(':') and len(para) < 150))] + + para = text(paragraphs[0]) + m = r_sentence.match(para) + + if not m: + if not last: + term = search(term) + return wikipedia(term, language=language, last=True) + return None + sentence = m.group(0) + + maxlength = 275 + if len(sentence) > maxlength: + sentence = sentence[:maxlength] + words = sentence[:-5].split(' ') + words.pop() + sentence = ' '.join(words) + ' [...]' + + if (('using the Article Wizard if you wish' in sentence) + or ('or add a request for it' in sentence) + or ('in existing articles' in sentence)): + if not last: + term = search(term) + return wikipedia(term, language=language, last=True) + return None + + sentence = '"' + sentence.replace('"', "'") + '"' + sentence = sentence.decode('utf-8').encode('utf-8') + wikiuri = wikiuri.decode('utf-8').encode('utf-8') + term = term.decode('utf-8').encode('utf-8') + return sentence + ' - ' + (wikiuri % (language, term)) + +def wik(phenny, input): + origterm = input.groups()[1] + if not origterm: + return phenny.say('Perhaps you meant ".wik Zen"?') + origterm = origterm.encode('utf-8') + + term = urllib.unquote(origterm) + language = 'en' + if term.startswith(':') and (' ' in term): + a, b = term.split(' ', 1) + a = a.lstrip(':') + if a.isalpha(): + language, term = a, b + term = term[0].upper() + term[1:] + term = term.replace(' ', '_') + + try: result = wikipedia(term, language) + except IOError: + args = (language, wikiuri % (language, term)) + error = "Can't connect to %s.wikipedia.org (%s)" % args + return phenny.say(error) + + if result is not None: + phenny.say(result) + else: phenny.say('Can\'t find anything in Wikipedia for "%s".' % origterm) wik.commands = ['wik'] wik.priority = 'high' -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() diff --git a/modules/wiktionary.py b/modules/wiktionary.py index 9229194..d3321c5 100755 --- a/modules/wiktionary.py +++ b/modules/wiktionary.py @@ -14,87 +14,87 @@ uri = 'http://en.wiktionary.org/w/index.php?title=%s&printable=yes' r_tag = re.compile(r'<[^>]+>') r_ul = re.compile(r'(?ims)<ul>.*?</ul>') -def text(html): - text = r_tag.sub('', html).strip() - text = text.replace('\n', ' ') - text = text.replace('\r', '') - text = text.replace('(intransitive', '(intr.') - text = text.replace('(transitive', '(trans.') - return text +def text(html): + text = r_tag.sub('', html).strip() + text = text.replace('\n', ' ') + text = text.replace('\r', '') + text = text.replace('(intransitive', '(intr.') + text = text.replace('(transitive', '(trans.') + return text -def wiktionary(word): - bytes = web.get(uri % web.urllib.quote(word.encode('utf-8'))) - bytes = r_ul.sub('', bytes) +def wiktionary(word): + bytes = web.get(uri % web.urllib.quote(word.encode('utf-8'))) + bytes = r_ul.sub('', bytes) - mode = None - etymology = None - definitions = {} - for line in bytes.splitlines(): - if 'id="Etymology"' in line: - mode = 'etymology' - elif 'id="Noun"' in line: - mode = 'noun' - elif 'id="Verb"' in line: - mode = 'verb' - elif 'id="Adjective"' in line: - mode = 'adjective' - elif 'id="Adverb"' in line: - mode = 'adverb' - elif 'id="Interjection"' in line: - mode = 'interjection' - elif 'id="Particle"' in line: - mode = 'particle' - elif 'id="Preposition"' in line: - mode = 'preposition' - elif 'id="' in line: - mode = None + mode = None + etymology = None + definitions = {} + for line in bytes.splitlines(): + if 'id="Etymology"' in line: + mode = 'etymology' + elif 'id="Noun"' in line: + mode = 'noun' + elif 'id="Verb"' in line: + mode = 'verb' + elif 'id="Adjective"' in line: + mode = 'adjective' + elif 'id="Adverb"' in line: + mode = 'adverb' + elif 'id="Interjection"' in line: + mode = 'interjection' + elif 'id="Particle"' in line: + mode = 'particle' + elif 'id="Preposition"' in line: + mode = 'preposition' + elif 'id="' in line: + mode = None - elif (mode == 'etmyology') and ('<p>' in line): - etymology = text(line) - elif (mode is not None) and ('<li>' in line): - definitions.setdefault(mode, []).append(text(line)) + elif (mode == 'etmyology') and ('<p>' in line): + etymology = text(line) + elif (mode is not None) and ('<li>' in line): + definitions.setdefault(mode, []).append(text(line)) - if '<hr' in line: - break - return etymology, definitions + if '<hr' in line: + break + return etymology, definitions -parts = ('preposition', 'particle', 'noun', 'verb', - 'adjective', 'adverb', 'interjection') +parts = ('preposition', 'particle', 'noun', 'verb', + 'adjective', 'adverb', 'interjection') -def format(word, definitions, number=2): - result = '%s' % word.encode('utf-8') - for part in parts: - if definitions.has_key(part): - defs = definitions[part][:number] - result += u' \u2014 '.encode('utf-8') + ('%s: ' % part) - n = ['%s. %s' % (i + 1, e.strip(' .')) for i, e in enumerate(defs)] - result += ', '.join(n) - return result.strip(' .,') +def format(word, definitions, number=2): + result = '%s' % word.encode('utf-8') + for part in parts: + if definitions.has_key(part): + defs = definitions[part][:number] + result += u' \u2014 '.encode('utf-8') + ('%s: ' % part) + n = ['%s. %s' % (i + 1, e.strip(' .')) for i, e in enumerate(defs)] + result += ', '.join(n) + return result.strip(' .,') -def w(phenny, input): - if not input.group(2): - return phenny.reply("Nothing to define.") - word = input.group(2) - etymology, definitions = wiktionary(word) - if not definitions: - phenny.say("Couldn't get any definitions for %s." % word) - return +def w(phenny, input): + if not input.group(2): + return phenny.reply("Nothing to define.") + word = input.group(2) + etymology, definitions = wiktionary(word) + if not definitions: + phenny.say("Couldn't get any definitions for %s." % word) + return - result = format(word, definitions) - if len(result) < 150: - result = format(word, definitions, 3) - if len(result) < 150: - result = format(word, definitions, 5) + result = format(word, definitions) + if len(result) < 150: + result = format(word, definitions, 3) + if len(result) < 150: + result = format(word, definitions, 5) - if len(result) > 300: - result = result[:295] + '[...]' - phenny.say(result) + if len(result) > 300: + result = result[:295] + '[...]' + phenny.say(result) w.commands = ['w'] w.example = '.w bailiwick' -def encarta(phenny, input): - return phenny.reply('Microsoft removed Encarta, try .w instead!') +def encarta(phenny, input): + return phenny.reply('Microsoft removed Encarta, try .w instead!') encarta.commands = ['dict'] -if __name__ == '__main__': - print __doc__.strip() +if __name__ == '__main__': + print __doc__.strip() |