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):
|
||||
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__
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
Loading…
Reference in New Issue