some refactoing.

* added Ban.type property
 * PersistentCache renamed to ReviewStore and made it work more like defaultdict.
This commit is contained in:
Elián Hanisch 2012-06-25 19:30:11 -03:00
parent fcbe5e92ec
commit 5ffcf6511a
2 changed files with 44 additions and 35 deletions

View File

@ -130,6 +130,7 @@ class MsgQueue(object):
queue = MsgQueue() queue = MsgQueue()
class Ban(object): class Ban(object):
"""Hold my bans""" """Hold my bans"""
def __init__(self, args=None, **kwargs): def __init__(self, args=None, **kwargs):
@ -171,17 +172,31 @@ class Ban(object):
def time(self): def time(self):
return datetime.datetime.fromtimestamp(self.when) return datetime.datetime.fromtimestamp(self.when)
def guessBanType(mask): @property
if mask[0] == '%': def type(self):
return 'quiet' mask = self.mask
elif ircutils.isUserHostmask(mask) or mask.endswith('(realname)'): if mask[0] == '%':
return 'ban' return 'quiet'
return 'removal' elif ircutils.isUserHostmask(mask) or mask.endswith('(realname)'):
if not ('*' in mask or '?' in mask or '$' in mask):
# XXX hack over hack, we are supposing these are marks as normal
# bans aren't usually set to exact match, while marks are.
return 'mark'
return 'ban'
return 'removal'
class PersistentCache(dict):
class ReviewStore(dict):
def __init__(self, filename): def __init__(self, filename):
self.filename = conf.supybot.directories.data.dirize(filename) self.filename = conf.supybot.directories.data.dirize(filename)
self.time = 0 self.lastReview = 0
def __getitem__(self, k):
try:
return dict.__getitem__(self, k)
except KeyError:
self[k] = L = []
return L
def open(self): def open(self):
import csv import csv
@ -189,7 +204,7 @@ class PersistentCache(dict):
reader = csv.reader(open(self.filename, 'rb')) reader = csv.reader(open(self.filename, 'rb'))
except IOError: except IOError:
return return
self.time = int(reader.next()[1]) self.lastReview = int(reader.next()[1])
for row in reader: for row in reader:
host, value = self.deserialize(*row) host, value = self.deserialize(*row)
try: try:
@ -205,7 +220,7 @@ class PersistentCache(dict):
writer = csv.writer(open(self.filename, 'wb')) writer = csv.writer(open(self.filename, 'wb'))
except IOError: except IOError:
return return
writer.writerow(('time', str(int(self.time)))) writer.writerow(('time', str(int(self.lastReview))))
for host, values in self.iteritems(): for host, values in self.iteritems():
for v in values: for v in values:
writer.writerow(self.serialize(host, v)) writer.writerow(self.serialize(host, v))
@ -254,7 +269,7 @@ class Bantracker(callbacks.Plugin):
self.db = None self.db = None
self.get_bans(irc) self.get_bans(irc)
self.get_nicks(irc) self.get_nicks(irc)
self.pendingReviews = PersistentCache('bt.reviews.db') self.pendingReviews = ReviewStore('bt.reviews.db')
self.pendingReviews.open() self.pendingReviews.open()
self._banreviewfix() self._banreviewfix()
# add scheduled event for check bans that need review, check every hour # add scheduled event for check bans that need review, check every hour
@ -464,7 +479,7 @@ class Bantracker(callbacks.Plugin):
return return
# check the type of the action taken # check the type of the action taken
mask = ban.mask mask = ban.mask
type = guessBanType(mask) type = ban.type
if type == 'quiet': if type == 'quiet':
mask = mask[1:] mask = mask[1:]
# check if type is enabled # check if type is enabled
@ -479,11 +494,12 @@ class Bantracker(callbacks.Plugin):
if nickMatch(nick, self.registryValue('request.ignore', channel)): if nickMatch(nick, self.registryValue('request.ignore', channel)):
return return
if nickMatch(nick, self.registryValue('request.forward', channel)): if nickMatch(nick, self.registryValue('request.forward', channel)):
# somebody else should comment this (like with bans set by bots)
s = "Please somebody comment on the %s of %s in %s done by %s, use:"\ s = "Please somebody comment on the %s of %s in %s done by %s, use:"\
" %scomment %s <comment>" %(type, mask, channel, nick, prefix, ban.id) " %scomment %s <comment>" %(type, mask, channel, nick, prefix, ban.id)
self._sendForward(irc, s, 'request', channel) self._sendForward(irc, s, 'request', channel)
else: else:
# send to op # send to operator
s = "Please comment on the %s of %s in %s, use: %scomment %s <comment>" \ s = "Please comment on the %s of %s in %s, use: %scomment %s <comment>" \
%(type, mask, channel, prefix, ban.id) %(type, mask, channel, prefix, ban.id)
irc.reply(s, to=nick, private=True) irc.reply(s, to=nick, private=True)
@ -494,11 +510,11 @@ class Bantracker(callbacks.Plugin):
# time is zero, do nothing # time is zero, do nothing
return return
now = time.mktime(time.gmtime()) now = time.mktime(time.gmtime())
lastreview = self.pendingReviews.time lastReview = self.pendingReviews.lastReview
self.pendingReviews.time = now # update last time reviewed self.pendingReviews.lastReview = now # update last time reviewed
if not lastreview: if not lastReview:
# initialize last time reviewed timestamp # initialize last time reviewed timestamp
lastreview = now - reviewTime lastReview = now - reviewTime
for channel, bans in self.bans.iteritems(): for channel, bans in self.bans.iteritems():
if not self.registryValue('enabled', channel) \ if not self.registryValue('enabled', channel) \
@ -510,15 +526,14 @@ class Bantracker(callbacks.Plugin):
# the less I touch it the better. # the less I touch it the better.
if ban.mask.endswith('$#ubuntu-read-topic'): if ban.mask.endswith('$#ubuntu-read-topic'):
continue continue
type = guessBanType(ban.mask)
if type == 'removal': type = ban.type
# skip kicks if type in ('removal', 'mark'):
continue # skip kicks and marks
if not ('*' in ban.mask or '?' in ban.mask or '$' in ban.mask):
# XXX hack over hack, we are supposing these are marks.
continue continue
banAge = now - ban.when banAge = now - ban.when
reviewWindow = lastreview - ban.when reviewWindow = lastReview - ban.when
#self.log.debug('review ban: %s ban %s by %s (%s/%s/%s %s)', channel, ban.mask, #self.log.debug('review ban: %s ban %s by %s (%s/%s/%s %s)', channel, ban.mask,
# ban.who, reviewWindow, reviewTime, banAge, reviewTime - reviewWindow) # ban.who, reviewWindow, reviewTime, banAge, reviewTime - reviewWindow)
if reviewWindow <= reviewTime < banAge: if reviewWindow <= reviewTime < banAge:
@ -563,11 +578,8 @@ class Bantracker(callbacks.Plugin):
self.registryValue('bansite'), self.registryValue('bansite'),
ban.id) ban.id)
msg = ircmsgs.privmsg(nick, s) msg = ircmsgs.privmsg(nick, s)
if host in self.pendingReviews \ if (nick, msg) not in self.pendingReviews[host]:
and (nick, msg) not in self.pendingReviews[host]:
self.pendingReviews[host].append((nick, msg)) self.pendingReviews[host].append((nick, msg))
else:
self.pendingReviews[host] = [(nick, msg)]
elif banAge < reviewTime: elif banAge < reviewTime:
# since we made sure bans are sorted by time, the bans left are more recent # since we made sure bans are sorted by time, the bans left are more recent
break break
@ -596,10 +608,7 @@ class Bantracker(callbacks.Plugin):
self.pendingReviews.clear() self.pendingReviews.clear()
for host, nick, msg in nodups: for host, nick, msg in nodups:
if host in self.pendingReviews: self.pendingReviews[host].append((nick, msg))
self.pendingReviews[host].append((nick, msg))
else:
self.pendingReviews[host] = [(nick, msg)]
def _sendReviews(self, irc, msg): def _sendReviews(self, irc, msg):
host = ircutils.hostFromHostmask(msg.prefix) host = ircutils.hostFromHostmask(msg.prefix)
@ -1225,9 +1234,9 @@ class Bantracker(callbacks.Plugin):
nick, host = key.split('@', 1) nick, host = key.split('@', 1)
else: else:
nick, host = key, None nick, host = key, None
try: if host in self.pendingReviews:
reviews = self.pendingReviews[host] reviews = self.pendingReviews[host]
except KeyError: else:
irc.reply('No reviews for %s, use --verbose for check the correct nick@host key.' % key) irc.reply('No reviews for %s, use --verbose for check the correct nick@host key.' % key)
return return

View File

@ -286,7 +286,7 @@ class BantrackerTestCase(ChannelPluginTestCase):
# check not pending anymore # check not pending anymore
self.assertFalse(cb.pendingReviews) self.assertFalse(cb.pendingReviews)
def testPersistentCache(self): def testReviewStore(self):
"""Save pending reviews and when bans were last checked. This is needed for plugin """Save pending reviews and when bans were last checked. This is needed for plugin
reloads""" reloads"""
msg1 = ircmsgs.privmsg('nick', 'Hello World') msg1 = ircmsgs.privmsg('nick', 'Hello World')