Some more little fixes, and added a Makefile.
parent
cbdf9ebd73
commit
3d920f4317
|
@ -0,0 +1,6 @@
|
||||||
|
# Makefile
|
||||||
|
# Copyright 2008, Sean B. Palmer, inamidst.com
|
||||||
|
# Licensed under the Eiffel Forum License 2.
|
||||||
|
|
||||||
|
archive: ;
|
||||||
|
hg archive -t tbz2 phenny.tar.bz2
|
22
bot.py
22
bot.py
|
@ -79,17 +79,16 @@ class Phenny(irc.Bot):
|
||||||
|
|
||||||
def bind(self, priority, regexp, func):
|
def bind(self, priority, regexp, func):
|
||||||
print priority, regexp.pattern.encode('utf-8'), func
|
print priority, regexp.pattern.encode('utf-8'), func
|
||||||
self.commands[priority].setdefault(regexp, []).append(func)
|
# register documentation
|
||||||
# @@ register documentation
|
if not hasattr(func, 'name'):
|
||||||
|
func.name = func.__name__
|
||||||
if func.__doc__:
|
if func.__doc__:
|
||||||
if hasattr(func, 'name'):
|
|
||||||
name = func.name
|
|
||||||
else: name = func.__name__
|
|
||||||
if hasattr(func, 'example'):
|
if hasattr(func, 'example'):
|
||||||
example = func.example
|
example = func.example
|
||||||
example = example.replace('$nickname', self.nick)
|
example = example.replace('$nickname', self.nick)
|
||||||
else: example = None
|
else: example = None
|
||||||
self.doc[name] = (func.__doc__, example)
|
self.doc[func.name] = (func.__doc__, example)
|
||||||
|
self.commands[priority].setdefault(regexp, []).append(func)
|
||||||
|
|
||||||
def sub(pattern, self=self):
|
def sub(pattern, self=self):
|
||||||
# These replacements have significant order
|
# These replacements have significant order
|
||||||
|
@ -127,8 +126,8 @@ class Phenny(irc.Bot):
|
||||||
prefix = self.config.prefix
|
prefix = self.config.prefix
|
||||||
commands, pattern = func.rule
|
commands, pattern = func.rule
|
||||||
for command in commands:
|
for command in commands:
|
||||||
command = r'(%s) +' % command
|
command = r'(%s)(?: +(?:%s))?' % (command, pattern)
|
||||||
regexp = re.compile(prefix + command + pattern)
|
regexp = re.compile(prefix + command)
|
||||||
bind(self, func.priority, regexp, func)
|
bind(self, func.priority, regexp, func)
|
||||||
|
|
||||||
# 3) e.g. ('$nick', ['p', 'q'], '(.*)')
|
# 3) e.g. ('$nick', ['p', 'q'], '(.*)')
|
||||||
|
@ -196,8 +195,6 @@ class Phenny(irc.Bot):
|
||||||
|
|
||||||
match = regexp.match(text)
|
match = regexp.match(text)
|
||||||
if match:
|
if match:
|
||||||
# print 'STATS:', origin.sender, func.__name__
|
|
||||||
|
|
||||||
phenny = self.wrapped(origin, text, match)
|
phenny = self.wrapped(origin, text, match)
|
||||||
input = self.input(origin, text, bytes, match, event)
|
input = self.input(origin, text, bytes, match, event)
|
||||||
|
|
||||||
|
@ -207,5 +204,10 @@ class Phenny(irc.Bot):
|
||||||
t.start()
|
t.start()
|
||||||
else: self.call(func, origin, phenny, input)
|
else: self.call(func, origin, phenny, input)
|
||||||
|
|
||||||
|
for source in [origin.sender, origin.nick]:
|
||||||
|
try: self.stats[(func.name, source)] += 1
|
||||||
|
except KeyError:
|
||||||
|
self.stats[(func.name, source)] = 1
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print __doc__
|
print __doc__
|
||||||
|
|
|
@ -21,7 +21,8 @@ def about(u, cp=None, name=None):
|
||||||
|
|
||||||
def codepoint_simple(arg):
|
def codepoint_simple(arg):
|
||||||
arg = arg.upper()
|
arg = arg.upper()
|
||||||
r_label = re.compile('\\b' + arg.replace(' ', '.*\\b'))
|
|
||||||
|
r_label = re.compile('\\b' + arg.replace(' ', '.*\\b') + '\\b')
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
for cp in xrange(0xFFFF):
|
for cp in xrange(0xFFFF):
|
||||||
|
@ -31,6 +32,16 @@ def codepoint_simple(arg):
|
||||||
|
|
||||||
if r_label.search(name):
|
if r_label.search(name):
|
||||||
results.append((len(name), u, cp, 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):
|
||||||
|
results.append((len(name), u, cp, name))
|
||||||
|
|
||||||
if not results:
|
if not results:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -7,22 +7,24 @@ Licensed under the Eiffel Forum License 2.
|
||||||
http://inamidst.com/phenny/
|
http://inamidst.com/phenny/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re, urllib, urlparse
|
import re, urllib, urlparse, time
|
||||||
from htmlentitydefs import name2codepoint
|
from htmlentitydefs import name2codepoint
|
||||||
import web
|
import web
|
||||||
from tools import deprecated
|
from tools import deprecated
|
||||||
|
|
||||||
@deprecated
|
def head(phenny, input):
|
||||||
def f_httphead(self, origin, match, args):
|
"""Provide HTTP HEAD information."""
|
||||||
""".head <URI> <FieldName>? - Perform an HTTP HEAD on URI."""
|
uri = input.group(2)
|
||||||
if origin.sender == '#talis': return
|
uri = (uri or '').encode('utf-8')
|
||||||
uri = match.group(2)
|
if ' ' in uri:
|
||||||
header = match.group(3)
|
uri, header = uri.rsplit(' ', 1)
|
||||||
|
else: uri, header = uri, None
|
||||||
|
|
||||||
|
if not uri and hasattr(phenny, 'last_seen_uri'):
|
||||||
|
uri = phenny.last_seen_uri
|
||||||
|
|
||||||
try: info = web.head(uri)
|
try: info = web.head(uri)
|
||||||
except IOError:
|
except IOError: return phenny.say("Can't connect to %s" % uri)
|
||||||
self.msg(origin.sender, "Can't connect to %s" % uri)
|
|
||||||
return
|
|
||||||
|
|
||||||
if not isinstance(info, list):
|
if not isinstance(info, list):
|
||||||
info = dict(info)
|
info = dict(info)
|
||||||
|
@ -33,17 +35,27 @@ def f_httphead(self, origin, match, args):
|
||||||
info = newInfo
|
info = newInfo
|
||||||
|
|
||||||
if header is None:
|
if header is None:
|
||||||
msg = 'Status: %s (for more, try ".head uri header")' % info['Status']
|
data = []
|
||||||
self.msg(origin.sender, msg)
|
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:
|
else:
|
||||||
headerlower = header.lower()
|
headerlower = header.lower()
|
||||||
if info.has_key(headerlower):
|
if info.has_key(headerlower):
|
||||||
self.msg(origin.sender, header + ': ' + info.get(headerlower))
|
phenny.say(header + ': ' + info.get(headerlower))
|
||||||
else:
|
else:
|
||||||
msg = 'There was no %s header in the response.' % header
|
msg = 'There was no %s header in the response.' % header
|
||||||
self.msg(origin.sender, msg)
|
phenny.say(msg)
|
||||||
f_httphead.rule = (['head'], r'(\S+)(?: +(\S+))?')
|
head.commands = ['head']
|
||||||
f_httphead.thread = True
|
head.example = '.head http://www.w3.org/'
|
||||||
|
|
||||||
r_title = re.compile(r'(?ims)<title[^>]*>(.*?)</title\s*>')
|
r_title = re.compile(r'(?ims)<title[^>]*>(.*?)</title\s*>')
|
||||||
r_entity = re.compile(r'&[A-Za-z0-9#]+;')
|
r_entity = re.compile(r'&[A-Za-z0-9#]+;')
|
||||||
|
@ -52,6 +64,11 @@ r_entity = re.compile(r'&[A-Za-z0-9#]+;')
|
||||||
def f_title(self, origin, match, args):
|
def f_title(self, origin, match, args):
|
||||||
""".title <URI> - Return the title of URI."""
|
""".title <URI> - Return the title of URI."""
|
||||||
uri = match.group(2)
|
uri = match.group(2)
|
||||||
|
uri = (uri or '').encode('utf-8')
|
||||||
|
|
||||||
|
if not uri and hasattr(self, 'last_seen_uri'):
|
||||||
|
uri = self.last_seen_uri
|
||||||
|
|
||||||
if not ':' in uri:
|
if not ':' in uri:
|
||||||
uri = 'http://' + uri
|
uri = 'http://' + uri
|
||||||
|
|
||||||
|
@ -74,10 +91,10 @@ def f_title(self, origin, match, args):
|
||||||
self.msg(origin.sender, origin.nick + ": Too many redirects")
|
self.msg(origin.sender, origin.nick + ": Too many redirects")
|
||||||
return
|
return
|
||||||
|
|
||||||
try: mtype = info['Content-Type']
|
try: mtype = info['content-type']
|
||||||
except:
|
except:
|
||||||
self.msg(origin.sender, origin.nick + ": Document isn't HTML")
|
err = ": Couldn't get the Content-Type, sorry"
|
||||||
return
|
return self.msg(origin.sender, origin.nick + err)
|
||||||
if not (('/html' in mtype) or ('/xhtml' in mtype)):
|
if not (('/html' in mtype) or ('/xhtml' in mtype)):
|
||||||
self.msg(origin.sender, origin.nick + ": Document isn't HTML")
|
self.msg(origin.sender, origin.nick + ": Document isn't HTML")
|
||||||
return
|
return
|
||||||
|
@ -119,8 +136,13 @@ def f_title(self, origin, match, args):
|
||||||
title = '[Title is the empty document, "".]'
|
title = '[Title is the empty document, "".]'
|
||||||
self.msg(origin.sender, origin.nick + ': ' + title)
|
self.msg(origin.sender, origin.nick + ': ' + title)
|
||||||
else: self.msg(origin.sender, origin.nick + ': No title found')
|
else: self.msg(origin.sender, origin.nick + ': No title found')
|
||||||
f_title.rule = (['title'], r'(\S+)')
|
f_title.commands = ['title']
|
||||||
f_title.thread = True
|
|
||||||
|
def noteuri(phenny, input):
|
||||||
|
uri = input.group(1).encode('utf-8')
|
||||||
|
phenny.bot.last_seen_uri = uri
|
||||||
|
noteuri.rule = r'.*(http://[^<> "]+)[,.]?'
|
||||||
|
noteuri.priority = 'low'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print __doc__
|
print __doc__
|
||||||
|
|
|
@ -40,5 +40,48 @@ def help(phenny, input):
|
||||||
help.rule = ('$nick', r'(?i)help(?:[?!]+)?$')
|
help.rule = ('$nick', r'(?i)help(?:[?!]+)?$')
|
||||||
help.priority = 'low'
|
help.priority = 'low'
|
||||||
|
|
||||||
|
def stats(phenny, input):
|
||||||
|
commands = {}
|
||||||
|
users = {}
|
||||||
|
channels = {}
|
||||||
|
|
||||||
|
ignore = set(['f_note', 'startup', 'message', 'noteuri'])
|
||||||
|
for (name, user), count in phenny.stats.iteritems():
|
||||||
|
if name in ignore: continue
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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 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(', '))
|
||||||
|
stats.commands = ['stats']
|
||||||
|
stats.priority = 'low'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print __doc__.strip()
|
print __doc__.strip()
|
||||||
|
|
|
@ -14,6 +14,10 @@ def f_reload(phenny, input):
|
||||||
if not input.admin: return
|
if not input.admin: return
|
||||||
|
|
||||||
name = input.group(2)
|
name = input.group(2)
|
||||||
|
if not name:
|
||||||
|
phenny.setup()
|
||||||
|
return phenny.reply('done')
|
||||||
|
|
||||||
try: module = getattr(__import__('modules.' + name), name)
|
try: module = getattr(__import__('modules.' + name), name)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
module = getattr(__import__('opt.' + name), name)
|
module = getattr(__import__('opt.' + name), name)
|
||||||
|
@ -30,7 +34,8 @@ def f_reload(phenny, input):
|
||||||
|
|
||||||
phenny.reply('%r (version: %s)' % (module, modified))
|
phenny.reply('%r (version: %s)' % (module, modified))
|
||||||
f_reload.name = 'reload'
|
f_reload.name = 'reload'
|
||||||
f_reload.rule = ('$nick', ['reload'], r'(\S+)')
|
f_reload.rule = ('$nick', ['reload'], r'(\S+)?')
|
||||||
|
f_reload.priority = 'low'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print __doc__.strip()
|
print __doc__.strip()
|
||||||
|
|
|
@ -8,7 +8,7 @@ Licensed under the Eiffel Forum License 2.
|
||||||
http://inamidst.com/phenny/
|
http://inamidst.com/phenny/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re, time
|
||||||
import web
|
import web
|
||||||
|
|
||||||
r_translation = re.compile(r'<div style=padding:10px;>([^<]+)</div>')
|
r_translation = re.compile(r'<div style=padding:10px;>([^<]+)</div>')
|
||||||
|
@ -43,7 +43,7 @@ def guess_language(phrase):
|
||||||
try: return languages[lang]
|
try: return languages[lang]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return lang
|
return lang
|
||||||
return 'unknown'
|
return 'Moon Language'
|
||||||
|
|
||||||
def translate(phrase, lang, target='en'):
|
def translate(phrase, lang, target='en'):
|
||||||
babelfish = 'http://world.altavista.com/tr'
|
babelfish = 'http://world.altavista.com/tr'
|
||||||
|
@ -68,35 +68,40 @@ def translate(phrase, lang, target='en'):
|
||||||
|
|
||||||
def tr(phenny, input):
|
def tr(phenny, input):
|
||||||
"""Translates a phrase, with an optional language hint."""
|
"""Translates a phrase, with an optional language hint."""
|
||||||
lang, phrase = input.groups()
|
input, output, phrase = input.groups()
|
||||||
phrase = phrase.encode('utf-8')
|
phrase = phrase.encode('utf-8')
|
||||||
if (len(phrase) > 350) and (not phenny.admin(input.nick)):
|
if (len(phrase) > 350) and (not phenny.admin(input.nick)):
|
||||||
return phenny.reply('Phrase must be under 350 characters.')
|
return phenny.reply('Phrase must be under 350 characters.')
|
||||||
|
|
||||||
language = guess_language(phrase)
|
input = input or guess_language(phrase)
|
||||||
if language is None:
|
if not input:
|
||||||
return phenny.reply('Unable to guess the language, sorry.')
|
return phenny.reply('Unable to guess the language, sorry.')
|
||||||
else: language = lang.encode('utf-8')
|
input = input.encode('utf-8')
|
||||||
|
output = (output or 'en').encode('utf-8')
|
||||||
|
|
||||||
if language != 'en':
|
if not ((input == 'en') and (output == 'en')):
|
||||||
translation = translate(phrase, language)
|
translation = translate(phrase, input, output)
|
||||||
if translation is not None:
|
if translation is not None:
|
||||||
translation = translation.decode('utf-8').encode('utf-8')
|
translation = translation.decode('utf-8').encode('utf-8')
|
||||||
return phenny.reply('"%s" (%s)' % (translation, language))
|
if output == 'en':
|
||||||
|
return phenny.reply('"%s" (%s)' % (translation, input))
|
||||||
|
else: return phenny.reply('"%s" (%s -> %s)' % \
|
||||||
|
(translation, input, output))
|
||||||
|
|
||||||
error = "I think it's %s, which I can't translate."
|
error = "I think it's %s, which I can't translate."
|
||||||
return phenny.reply(error % language.title())
|
return phenny.reply(error % input.title())
|
||||||
|
|
||||||
# Otherwise, it's English, so mangle it for fun
|
# Otherwise, it's English, so mangle it for fun
|
||||||
for other in ['de', 'ja']:
|
for other in ['de', 'ja', 'de', 'ja', 'de', 'ja', 'de', 'ja', 'de', 'ja']:
|
||||||
phrase = translate(phrase, 'en', other)
|
phrase = translate(phrase, 'en', other)
|
||||||
phrase = translate(phrase, other, 'en')
|
phrase = translate(phrase, other, 'en')
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
if phrase is not None:
|
if phrase is not None:
|
||||||
return phenny.reply(u'"%s" (en-unmangled)' % phrase)
|
return phenny.reply(u'"%s" (en-unmangled)' % phrase)
|
||||||
return phenny.reply("I think it's English already.")
|
return phenny.reply("I think it's English already.")
|
||||||
# @@ or 'Why but that be English, sire.'
|
# @@ or 'Why but that be English, sire.'
|
||||||
tr.rule = ('$nick', ur'(?:([a-z]{2}) +)?["“](.+?)["”]\? *$')
|
tr.rule = ('$nick', ur'(?:([a-z]{2}) +)?(?:([a-z]{2}) +)?["“](.+?)["”]\? *$')
|
||||||
tr.example = '$nickname: "mon chien"? or $nickname: fr "mon chien"?'
|
tr.example = '$nickname: "mon chien"? or $nickname: fr "mon chien"?'
|
||||||
tr.priority = 'low'
|
tr.priority = 'low'
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ def search(term):
|
||||||
else: return term
|
else: return term
|
||||||
|
|
||||||
def wikipedia(term, last=False):
|
def wikipedia(term, last=False):
|
||||||
|
global wikiuri
|
||||||
bytes = web.get(wikiuri % urllib.quote(term))
|
bytes = web.get(wikiuri % urllib.quote(term))
|
||||||
bytes = r_tr.sub('', bytes)
|
bytes = r_tr.sub('', bytes)
|
||||||
|
|
||||||
|
@ -83,7 +84,8 @@ def wikipedia(term, last=False):
|
||||||
and not 'disambiguation)"' in para)
|
and not 'disambiguation)"' in para)
|
||||||
and not '(images and media)' in para
|
and not '(images and media)' in para
|
||||||
and not 'This article contains a' in para
|
and not 'This article contains a' in para
|
||||||
and not 'id="coordinates"' in para]
|
and not 'id="coordinates"' in para
|
||||||
|
and not 'class="thumb' in para]
|
||||||
|
|
||||||
for i, para in enumerate(paragraphs):
|
for i, para in enumerate(paragraphs):
|
||||||
para = para.replace('<sup>', '|')
|
para = para.replace('<sup>', '|')
|
||||||
|
@ -119,7 +121,9 @@ def wikipedia(term, last=False):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sentence = '"' + sentence.replace('"', "'") + '"'
|
sentence = '"' + sentence.replace('"', "'") + '"'
|
||||||
return sentence + ' - ' + (wikiuri % term)
|
sentence = sentence.decode('utf-8').encode('utf-8')
|
||||||
|
wikiuri = wikiuri.encode('utf-8')
|
||||||
|
return sentence + ' - ' + (wikiuri % term.encode('utf-8'))
|
||||||
|
|
||||||
def wik(phenny, input):
|
def wik(phenny, input):
|
||||||
origterm = input.groups()[1]
|
origterm = input.groups()[1]
|
||||||
|
|
|
@ -20,13 +20,15 @@ def replaced(phenny, input):
|
||||||
'v': '.v has been replaced by .val',
|
'v': '.v has been replaced by .val',
|
||||||
'validate': '.validate has been replaced by .validate',
|
'validate': '.validate has been replaced by .validate',
|
||||||
'thesaurus': ".thesaurus hasn't been ported to my new codebase yet",
|
'thesaurus': ".thesaurus hasn't been ported to my new codebase yet",
|
||||||
'rate': ".rate hasn't been ported to my new codebase yet, sorry!",
|
'rates': "moon wanter. moOOoon wanter!",
|
||||||
'rates': ".rates hasn't been ported to my new codebase yet, sorry!"
|
'web': 'the .web command has been removed; ask sbp for details',
|
||||||
|
'mangle': ".mangle hasn't been ported to my new codebase yet",
|
||||||
|
'origin': ".origin hasn't been ported to my new codebase yet"
|
||||||
}[command]
|
}[command]
|
||||||
phenny.reply(response)
|
phenny.reply(response)
|
||||||
replaced.commands = [
|
replaced.commands = [
|
||||||
'cp', 'pc', 'unicode', 'compare', 'map', 'acronym', 'img',
|
'cp', 'pc', 'unicode', 'compare', 'map', 'acronym', 'img',
|
||||||
'v', 'validate', 'thesaurus', 'rate', 'rates'
|
'v', 'validate', 'thesaurus', 'rates', 'web', 'mangle', 'origin'
|
||||||
]
|
]
|
||||||
replaced.priority = 'low'
|
replaced.priority = 'low'
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue