Some more little fixes, and added a Makefile.

master
Sean B. Palmer 2008-02-29 15:36:18 +00:00
parent cbdf9ebd73
commit 3d920f4317
9 changed files with 150 additions and 50 deletions

6
Makefile Normal file
View File

@ -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
View File

@ -79,17 +79,16 @@ class Phenny(irc.Bot):
def bind(self, priority, regexp, 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 hasattr(func, 'name'):
name = func.name
else: name = func.__name__
if hasattr(func, 'example'):
example = func.example
example = example.replace('$nickname', self.nick)
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):
# These replacements have significant order
@ -127,8 +126,8 @@ class Phenny(irc.Bot):
prefix = self.config.prefix
commands, pattern = func.rule
for command in commands:
command = r'(%s) +' % command
regexp = re.compile(prefix + command + pattern)
command = r'(%s)(?: +(?:%s))?' % (command, pattern)
regexp = re.compile(prefix + command)
bind(self, func.priority, regexp, func)
# 3) e.g. ('$nick', ['p', 'q'], '(.*)')
@ -196,8 +195,6 @@ class Phenny(irc.Bot):
match = regexp.match(text)
if match:
# print 'STATS:', origin.sender, func.__name__
phenny = self.wrapped(origin, text, match)
input = self.input(origin, text, bytes, match, event)
@ -207,5 +204,10 @@ class Phenny(irc.Bot):
t.start()
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__':
print __doc__

View File

@ -21,7 +21,8 @@ def about(u, cp=None, name=None):
def codepoint_simple(arg):
arg = arg.upper()
r_label = re.compile('\\b' + arg.replace(' ', '.*\\b'))
r_label = re.compile('\\b' + arg.replace(' ', '.*\\b') + '\\b')
results = []
for cp in xrange(0xFFFF):
@ -31,6 +32,16 @@ def codepoint_simple(arg):
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):
results.append((len(name), u, cp, name))
if not results:
return None

View File

@ -7,22 +7,24 @@ Licensed under the Eiffel Forum License 2.
http://inamidst.com/phenny/
"""
import re, urllib, urlparse
import re, urllib, urlparse, time
from htmlentitydefs import name2codepoint
import web
from tools import deprecated
@deprecated
def f_httphead(self, origin, match, args):
""".head <URI> <FieldName>? - Perform an HTTP HEAD on URI."""
if origin.sender == '#talis': return
uri = match.group(2)
header = match.group(3)
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'):
uri = phenny.last_seen_uri
try: info = web.head(uri)
except IOError:
self.msg(origin.sender, "Can't connect to %s" % uri)
return
except IOError: return phenny.say("Can't connect to %s" % uri)
if not isinstance(info, list):
info = dict(info)
@ -33,17 +35,27 @@ def f_httphead(self, origin, match, args):
info = newInfo
if header is None:
msg = 'Status: %s (for more, try ".head uri header")' % info['Status']
self.msg(origin.sender, msg)
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):
self.msg(origin.sender, header + ': ' + info.get(headerlower))
phenny.say(header + ': ' + info.get(headerlower))
else:
msg = 'There was no %s header in the response.' % header
self.msg(origin.sender, msg)
f_httphead.rule = (['head'], r'(\S+)(?: +(\S+))?')
f_httphead.thread = True
phenny.say(msg)
head.commands = ['head']
head.example = '.head http://www.w3.org/'
r_title = re.compile(r'(?ims)<title[^>]*>(.*?)</title\s*>')
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):
""".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
if not ':' in uri:
uri = 'http://' + uri
@ -74,10 +91,10 @@ def f_title(self, origin, match, args):
self.msg(origin.sender, origin.nick + ": Too many redirects")
return
try: mtype = info['Content-Type']
try: mtype = info['content-type']
except:
self.msg(origin.sender, origin.nick + ": Document isn't HTML")
return
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
@ -119,8 +136,13 @@ def f_title(self, origin, match, args):
title = '[Title is the empty document, "".]'
self.msg(origin.sender, origin.nick + ': ' + title)
else: self.msg(origin.sender, origin.nick + ': No title found')
f_title.rule = (['title'], r'(\S+)')
f_title.thread = True
f_title.commands = ['title']
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__':
print __doc__

View File

@ -40,5 +40,48 @@ def help(phenny, input):
help.rule = ('$nick', r'(?i)help(?:[?!]+)?$')
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__':
print __doc__.strip()

View File

@ -14,6 +14,10 @@ def f_reload(phenny, input):
if not input.admin: return
name = input.group(2)
if not name:
phenny.setup()
return phenny.reply('done')
try: module = getattr(__import__('modules.' + name), name)
except ImportError:
module = getattr(__import__('opt.' + name), name)
@ -30,7 +34,8 @@ def f_reload(phenny, input):
phenny.reply('%r (version: %s)' % (module, modified))
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__':
print __doc__.strip()

View File

@ -8,7 +8,7 @@ Licensed under the Eiffel Forum License 2.
http://inamidst.com/phenny/
"""
import re
import re, time
import web
r_translation = re.compile(r'<div style=padding:10px;>([^<]+)</div>')
@ -43,7 +43,7 @@ def guess_language(phrase):
try: return languages[lang]
except KeyError:
return lang
return 'unknown'
return 'Moon Language'
def translate(phrase, lang, target='en'):
babelfish = 'http://world.altavista.com/tr'
@ -68,35 +68,40 @@ def translate(phrase, lang, target='en'):
def tr(phenny, input):
"""Translates a phrase, with an optional language hint."""
lang, phrase = input.groups()
input, output, phrase = input.groups()
phrase = phrase.encode('utf-8')
if (len(phrase) > 350) and (not phenny.admin(input.nick)):
return phenny.reply('Phrase must be under 350 characters.')
language = guess_language(phrase)
if language is None:
input = input or guess_language(phrase)
if not input:
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':
translation = translate(phrase, language)
if not ((input == 'en') and (output == 'en')):
translation = translate(phrase, input, output)
if translation is not None:
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."
return phenny.reply(error % language.title())
return phenny.reply(error % input.title())
# 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, other, 'en')
time.sleep(0.1)
if phrase is not None:
return phenny.reply(u'"%s" (en-unmangled)' % phrase)
return phenny.reply("I think it's English already.")
# @@ 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.priority = 'low'

View File

@ -55,6 +55,7 @@ def search(term):
else: return term
def wikipedia(term, last=False):
global wikiuri
bytes = web.get(wikiuri % urllib.quote(term))
bytes = r_tr.sub('', bytes)
@ -83,7 +84,8 @@ def wikipedia(term, last=False):
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 'id="coordinates"' in para
and not 'class="thumb' in para]
for i, para in enumerate(paragraphs):
para = para.replace('<sup>', '|')
@ -119,7 +121,9 @@ def wikipedia(term, last=False):
return None
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):
origterm = input.groups()[1]

View File

@ -20,13 +20,15 @@ def replaced(phenny, input):
'v': '.v has been replaced by .val',
'validate': '.validate has been replaced by .validate',
'thesaurus': ".thesaurus hasn't been ported to my new codebase yet",
'rate': ".rate hasn't been ported to my new codebase yet, sorry!",
'rates': ".rates hasn't been ported to my new codebase yet, sorry!"
'rates': "moon wanter. moOOoon wanter!",
'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]
phenny.reply(response)
replaced.commands = [
'cp', 'pc', 'unicode', 'compare', 'map', 'acronym', 'img',
'v', 'validate', 'thesaurus', 'rate', 'rates'
'v', 'validate', 'thesaurus', 'rates', 'web', 'mangle', 'origin'
]
replaced.priority = 'low'