[pybot-commits] CVS: pybot/pybot/modules remoteinfo.py,NONE,1.1 infopack.py,1.6,1.7 social.py,1.8,1.
Brought to you by:
niemeyer
|
From: Gustavo N. <nie...@us...> - 2003-06-02 13:36:16
|
Update of /cvsroot/pybot/pybot/pybot/modules
In directory sc8-pr-cvs1:/tmp/cvs-serv22381/pybot/modules
Modified Files:
infopack.py social.py
Added Files:
remoteinfo.py
Log Message:
* modules/remoteinfo.py: New module!
* server.py: If unable to transform string to ISO-8859-1, send
it in UTF8.
--- NEW FILE: remoteinfo.py ---
# Copyright (c) 2000-2001 Gustavo Niemeyer <nie...@co...>
#
# This file is part of pybot.
#
# pybot is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# pybot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pybot; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from pybot import config, options, hooks, mm, db
import thread, time
import urllib
import re
import os
HELP = """
You can ask me to load information from remote URLs using
"load remote info [from] <url> [each <n>(s|m|h)] [[with] regex <regex>]"
and to unload or reload using "(re|un)load remote info [from] <url>". To
check what is being remotely loaded, use "show remote[ ]info[s]". To
load and unload remote infos you'll need the "remoteinfo" permission,
and to show and reload you need either the "remoteinfo" or the
"remoteinforeload" permission.
""","""
The default reload interval is 10 minutes. To understand how to build the
regex for the remote info command or how to build the remote information
check "help remote info syntax".
"""
HELP_SYNTAX = """
Loading remote information consists in reading a remote URL, splitting
that information in lines, checking each line against a regular
expression, and including the matching lines as items in a database for
further processing. Each of these items will have a "trigger", which is
also a regular expression, a corresponding "msg" which will be sent when
the trigger is activated, and some "flags" which configure the item.
""","""
Even though you may provide your own regular expression that is checked
against each item line, the default one is similar to the following:
"(<(?P<flags>.*?)>)? (?P<trigger>.*?) => (?P<msg>.*)". It means that you
could use in the given URL lines like "Does it work\? => Yes! it does!",
or with flags and groups like "<g> Hi (?P<someone>.*) => /me says hi
to %(someone)s as well.".
""","""
Messages may start with /me or /notice to have the expected meanings.
The current supported flags are: 'a' to disable the inclusion of the
user nick at the start of the line on channel messages; 'g' to check
even channel messages not targeted to myself; and 's' which makes the
trigger case sensitive.
"""
PERM_REMOTEINFO = """
Users with the "remoteinfo" permission can work with remote
information settings. Check "help remoteinfo" for more information.
"""
PERM_RELOADREMOTEINFO = """
Users with the "reloadremoteinfo" permission can ask me to reload
information from remote URLs. Notice that the "remoteinfo" permission
allows that as well. Check "help remoteinfo" for more information.
"""
DEFAULTREGEX = "\s*(?:<(?P<flags>[^>]*)>\s*)?(?P<trigger>.*?)\s*=>\s*(?P<msg>.*)"
DEFAULTINTERVAL = "10m"
class Info:
def __init__(self):
self.lasttime = 0
self.patterns = {}
def __repr__(self):
return "<Info: %s>" % `self.patterns`
class RemoteInfo:
def __init__(self):
db.table("remoteinfo", "url,regex,interval")
self.info = options.get("RemoteInfo.info", {})
self.info_lock = options.get("RemoteInfo.info_lock", {})
self.lock = thread.allocate_lock()
if config.has_option("global", "http_proxy"):
self.proxy = config.get("global", "http_proxy")
else:
self.proxy = None
hooks.register("Message", self.message)
hooks.register("Message", self.message_remoteinfo, priority=1000)
# load remote[ ]info [from] <url> [each <n>[ ](s[econds]|m[inutes]|h[ours])] [[with|using] regex <regex>]
self.re1 = re.compile(r"load\s+remote\s*info\s+(?:from\s+)?(?P<url>\S+)(?:\s+each\s+(?P<interval>[0-9]+)\s*(?P<intervalunit>se?c?o?n?d?s?|mi?n?u?t?e?s?|ho?u?r?s?))?(?:\s+(?:with\s+|using\s+)?regex\s+(?P<regex>.*))?\s*$", re.I)
# (un|re)load remote[ ]info [from] <url>
self.re2 = re.compile(r"(?P<cmd>un|re)load\s+remote\s*info\s+(?:from\s+)?(?P<url>\S+)\s*$", re.I)
# show remote[ ]info[s]
self.re3 = re.compile(r"show\s+remote\s+infos?$", re.I)
# remote[ ]info
mm.register_help("remote\s*info?$", HELP, "remoteinfo")
mm.register_help("remote\s*infos?\s*syntax$", HELP_SYNTAX,
"remoteinfo syntax")
mm.register_perm("remoteinfo", PERM_REMOTEINFO)
mm.register_perm("reloadremoteinfo", PERM_RELOADREMOTEINFO)
mm.hooktimer(30, self.reload_all, ())
if not self.info:
self.reload_all()
def unload(self):
mm.unhooktimer(30, self.reload_all, ())
hooks.unregister("Message", self.message)
hooks.unregister("Message", self.message_remoteinfo, priority=1000)
mm.unregister_help(HELP)
mm.unregister_help(HELP_SYNTAX)
mm.unregister_perm("remoteinfo")
mm.unregister_perm("reloadremoteinfo")
def lock_url(self, url):
self.lock.acquire()
try:
lock = self.info_lock[url]
except KeyError:
lock = thread.allocate_lock()
self.info_lock[url] = lock
self.lock.release()
return lock.acquire(0)
def unlock_url(self, url):
try:
lock = self.info_lock[url].release()
except KeyError:
pass
def reload_all(self):
cursor = db.cursor()
cursor.execute("select * from remoteinfo")
now = time.time()
for row in cursor.fetchall():
info = self.info.get(row.url)
if not info or now-info.lasttime > int(row.interval):
self.reload(row.url, row.regex)
def reload(self, url, regex=None):
if not regex:
cursor = db.cursor()
cursor.execute("select regex from remoteinfo where url=%s",
(url,))
row = cursor.fetchone()
if not row: return
regex = row.regex
if self.lock_url(url):
thread.start_new_thread(self._reload, (url, regex))
def _reload(self, url, regex):
try:
urlopener = urllib.URLopener()
if self.proxy:
proxy = {"http": self.proxy}
urlopener.proxies.update(proxy)
try:
infourl = urlopener.open(url)
except:
import traceback
traceback.print_exc()
else:
info = Info()
p = re.compile(regex)
for line in infourl.read().splitlines():
m = p.match(line)
if m:
try:
trigger = m.group("trigger")
msg = m.group("msg")
flags = m.group("flags") or ""
except IndexError:
continue
if not trigger or not msg:
continue
trigger = trigger.strip()
if trigger[-1] != "$":
trigger += "$"
msg = msg.strip()
flags = flags.strip()
try:
if 's' in flags:
trigger_re = re.compile(trigger)
else:
trigger_re = re.compile(trigger, re.I)
except re.error:
continue
info.patterns[trigger_re] = (flags, msg)
infourl.close()
info.lasttime = time.time()
self.info[url] = info
finally:
self.unlock_url(url)
def message_remoteinfo(self, msg):
ret = None
for info in self.info.values():
for p in info.patterns:
flags, infomsg = info.patterns[p]
if not ('g' in flags or msg.forme):
continue
m = p.match(msg.line)
if m:
try:
infomsg %= m.groupdict()
except KeyError:
continue
ctcp = None
notice = 0
withnick = ("a" not in flags)
if infomsg.startswith("/me "):
withnick = 0
infomsg = infomsg[4:]
ctcp = "ACTION"
elif infomsg.startswith("/notice "):
withnick = 0
infomsg = infomsg[8:]
notice = 1
if withnick:
msg.answer("%:", infomsg, ctcp=ctcp, notice=notice)
else:
msg.answer(infomsg, ctcp=ctcp, notice=notice)
ret = 0
return ret
def message(self, msg):
if not msg.forme:
return None
m = self.re1.match(msg.line)
if m:
if mm.hasperm(msg, "remoteinfo"):
url = m.group("url")
regex = (m.group("regex") or DEFAULTREGEX).strip()
interval = m.group("interval")
if not interval:
interval = DEFAULTINTERVAL[:-1]
unit = DEFAULTINTERVAL[-1]
else:
unit = m.group("intervalunit")[0]
unitindex = ["s", "m", "h"].index(unit)
unitfactor = [1, 60, 3600][unitindex]
try:
interval = int(interval)*unitfactor
if interval == 0:
raise ValueError
except ValueError:
msg.answer("%:", ["Hummm...", "Oops!", "Heh..."],
["This interval is not valid",
"There's something wrong with the "
"interval you provided"],
["!", "."])
return 0
try:
m = re.compile(regex)
except re.error:
msg.answer("%:", ["Hummm...", "Oops!", "Heh..."],
["This regex is not valid",
"There's something wrong with the "
"regex you provided"],
["!", "."])
return 0
cursor = db.cursor()
cursor.execute("select * from remoteinfo where url=%s",
(url,))
if cursor.rowcount:
msg.answer("%:", ["I can't do that.", "Nope.", None],
["I'm already loading that url",
"Can't insert repeated urls",
"This url is already in my database"],
[".", "!"])
else:
cursor.execute("insert into remoteinfo values "
"(%s,%s,%s)",
(url, regex, interval))
msg.answer("%:", ["Loading",
"No problems",
"Starting right now",
"Sure"],
[".", "!"])
self.reload(url, regex)
else:
msg.answer("%:", [("You're not",
["allowed to change remote info options",
"that good",
"allowed to do this"]),
"Nope"], [".", "!"])
return 0
m = self.re2.match(msg.line)
if m:
cmd = m.group("cmd")
url = m.group("url")
cursor = db.cursor()
cursor.execute("select * from remoteinfo where url=%s",
(url,))
if not cursor.rowcount:
msg.answer("%:", ["I can't do that.", "Nope.", None],
["I'm not loading that url",
"This url is not in my database"],
[".", "!"])
elif cmd == "un":
if mm.hasperm(msg, "remoteinfo"):
if not self.lock_url(url):
msg.answer("%:", "Can't do that now. URL is "
"being loaded in this exact "
"moment. Try again in a few "
"seconds.")
else:
if url in self.info:
del self.info[url]
if url in self.info_lock:
del self.info_lock[url]
# Unlocking is not really necessary, but
# politically right. ;-)
self.unlock_url(url)
cursor.execute("delete from remoteinfo where url=%s",
(url,))
msg.answer("%:", ["Done", "Of course", "Ready"],
[".", "!"])
else:
msg.answer("%:", [("You're not",
["allowed to change "
"remote info options",
"that good",
"allowed to do this"]),
"Nope"], [".", "!"])
else:
if mm.hasperm(msg, "remoteinfo") or \
mm.hasperm(msg, "reloadremoteinfo"):
msg.answer("%:", ["Will do that",
"In a moment",
"Will be ready in a moment",
"Starting right now"], [".", "!"])
self.reload(url)
else:
msg.answer("%:", [("You're not",
["allowed to reload remote infos",
"that good",
"allowed to do this"]),
"Nope"], [".", "!"])
return 0
m = self.re3.match(msg.line)
if m:
if mm.hasperm(msg, "remoteinfo") or \
mm.hasperm(msg, "reloadremoteinfo"):
cursor = db.cursor()
cursor.execute("select * from remoteinfo")
if cursor.rowcount:
msg.answer("%:", "The following remote info urls are "
"being loaded:")
for row in cursor.fetchall():
interval = int(row.interval)
if interval % 3600 == 0:
interval /= 3600
unit = "hour"
elif interval % 60 == 0:
interval /= 60
unit = "minute"
else:
unit = "second"
if interval > 1:
unit += "s"
msg.answer("-", row.url, "each", str(interval), unit,
"with regex", row.regex)
else:
msg.answer("%:", "No remote info urls are currently "
"being loaded", [".", "!"])
else:
msg.answer("%:", "You're not",
["allowed to show remote infos",
"that good",
"allowed to do this"], [".", "!"])
return 0
def __loadmodule__():
global mod
mod = RemoteInfo()
def __unloadmodule__():
global mod
mod.unload()
del mod
# vim:ts=4:sw=4:et
Index: infopack.py
===================================================================
RCS file: /cvsroot/pybot/pybot/pybot/modules/infopack.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** infopack.py 16 May 2003 17:28:03 -0000 1.6
--- infopack.py 2 Jun 2003 13:36:09 -0000 1.7
***************
*** 36,40 ****
PERM_INFOPACKADMIN = """
Users with the "infopackadmin" permission can change infopack
! settings. Check "help infopack" for more informatino.
"""
--- 36,40 ----
PERM_INFOPACKADMIN = """
Users with the "infopackadmin" permission can change infopack
! settings. Check "help infopack" for more information.
"""
Index: social.py
===================================================================
RCS file: /cvsroot/pybot/pybot/pybot/modules/social.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -C2 -d -r1.8 -r1.9
*** social.py 29 May 2003 16:31:13 -0000 1.8
--- social.py 2 Jun 2003 13:36:10 -0000 1.9
***************
*** 27,32 ****
hooks.register("UnhandledMessage", self.unhandled_message)
! # (hi|hello|olá|ola|hola) [there|everybody|all|guys|folks|people|pessoal|pybot] [!|.]
! self.re1 = re.compile(r'(?:hi|hello|olá|ola|hola)(?:\s+(?:there|everybody|all|guys|folks|people|pessoal|(?P<nick>\w+)))?\s*[!.]*$', re.I)
# pybot!
--- 27,32 ----
hooks.register("UnhandledMessage", self.unhandled_message)
! # (re|hi|hello|hallo|olá|ola|hola|wb|welcome|good (morning|afternoon|evening)|bom dia|boa tarde|gutten tag) [there|back|everybody|all|guys|folks|people|pessoal|(a|para) todos|pybot] [!|.]
! self.re1 = re.compile(r'(?:re|hi|hello|hallo|olá|ola|hola|wb|welcome\s+back|good\s+(?:morning|afternoon|evening)|bom\s+dia|boa\s+tarde|gutten\s+tag)(?:\s+(?:there|everybody|all|guys|folks|people|pessoal|(à|a|para)\s+todos|(?P<nick>\w+)))?\s*[!.]*$', re.I)
# pybot!
|