summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean B. Palmer <http://inamidst.com/sbp/>2008-02-29 15:36:18 +0000
committerSean B. Palmer <http://inamidst.com/sbp/>2008-02-29 15:36:18 +0000
commit3d920f431789ac53596933785b5fe61463335e3b (patch)
tree654b01dfb141aba551d400393263fa6914ba9310
parentcbdf9ebd7312bf570a212057ad793ae520bac38f (diff)
downloadbot-3d920f431789ac53596933785b5fe61463335e3b.tar.gz
bot-3d920f431789ac53596933785b5fe61463335e3b.tar.bz2
bot-3d920f431789ac53596933785b5fe61463335e3b.zip
Some more little fixes, and added a Makefile.
-rw-r--r--Makefile6
-rwxr-xr-xbot.py22
-rw-r--r--modules/codepoints.py13
-rwxr-xr-xmodules/head.py64
-rw-r--r--modules/info.py43
-rwxr-xr-xmodules/reload.py7
-rw-r--r--modules/translate.py29
-rw-r--r--modules/wikipedia.py8
-rw-r--r--opt/freenode.py8
9 files changed, 150 insertions, 50 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bec372a
--- /dev/null
+++ b/Makefile
@@ -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
diff --git a/bot.py b/bot.py
index 4c27b7d..ae97d93 100755
--- a/bot.py
+++ b/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__
diff --git a/modules/codepoints.py b/modules/codepoints.py
index 83425c5..d966670 100644
--- a/modules/codepoints.py
+++ b/modules/codepoints.py
@@ -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):
@@ -32,6 +33,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
length, u, cp, name = sorted(results)[0]
diff --git a/modules/head.py b/modules/head.py
index 193286a..8f687fa 100755
--- a/modules/head.py
+++ b/modules/head.py
@@ -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__
diff --git a/modules/info.py b/modules/info.py
index a70c823..df6ad69 100644
--- a/modules/info.py
+++ b/modules/info.py
@@ -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()
diff --git a/modules/reload.py b/modules/reload.py
index 7a4c76f..2febcd2 100755
--- a/modules/reload.py
+++ b/modules/reload.py
@@ -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()
diff --git a/modules/translate.py b/modules/translate.py
index 7e14b1d..d90de43 100644
--- a/modules/translate.py
+++ b/modules/translate.py
@@ -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'
diff --git a/modules/wikipedia.py b/modules/wikipedia.py
index 0a0a415..696bce4 100644
--- a/modules/wikipedia.py
+++ b/modules/wikipedia.py
@@ -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]
diff --git a/opt/freenode.py b/opt/freenode.py
index b87a5e2..f3d285c 100644
--- a/opt/freenode.py
+++ b/opt/freenode.py
@@ -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'