diff --git a/plugins/News.py b/plugins/News.py index b65e9a436..d1b70c6dc 100644 --- a/plugins/News.py +++ b/plugins/News.py @@ -34,8 +34,6 @@ dates. __revision__ = "$Id$" -import supybot.plugins as plugins - import os import time @@ -43,148 +41,94 @@ import supybot.dbi as dbi import supybot.conf as conf import supybot.ircdb as ircdb import supybot.utils as utils +import supybot.plugins as plugins +from supybot.commands import wrap import supybot.ircutils as ircutils import supybot.privmsgs as privmsgs import supybot.callbacks as callbacks -class NewsRecord(dbi.Record): - __fields__ = [ - 'subject', - 'text', - 'at', - 'expires', - 'by' - ] - def __str__(self): - format = conf.supybot.humanTimestampFormat() - try: - user = ircdb.users.getUser(int(self.by)).name - except ValueError: - user = self.by - except KeyError: - user = 'a user that is no longer registered' - if int(self.expires) == 0: - s = '%s (Subject: "%s", added by %s on %s)' % \ - (self.text, self.subject, self.by, - time.strftime(format, time.localtime(int(self.at)))) - else: - s = '%s (Subject: "%s", added by %s on %s, expires at %s)' % \ - (self.text, self.subject, self.by, - time.strftime(format, time.localtime(int(self.at))), - time.strftime(format, time.localtime(int(self.expires)))) - return s +class DbiNewsDB(plugins.DbiChannelDB): + class DB(dbi.DB): + class Record(dbi.Record): + __fields__ = [ + 'subject', + 'text', + 'at', + 'expires', + 'by', + ] -class SqliteNewsDB(object): - def __init__(self, filename): - self.dbs = ircutils.IrcDict() - self.filename = filename + def __str__(self): + format = conf.supybot.humanTimestampFormat() + try: + user = ircdb.users.getUser(int(self.by)).name + except ValueError: + user = self.by + except KeyError: + user = 'a user that is no longer registered' + if int(self.expires) == 0: + s = '%s (Subject: "%s", added by %s on %s)' % \ + (self.text, self.subject, self.by, + time.strftime(format, time.localtime(int(self.at)))) + else: + s = '%s (Subject: "%s", added by %s on %s, expires at %s)' + s = s % (self.text, self.subject, user, + time.strftime(format, time.localtime(int(self.at))), + time.strftime(format, time.localtime(int(self.expires)))) + return s - def close(self): - for db in self.dbs.itervalues(): - db.close() + def __init__(self, filename): + # We use self.__class__ here because apparently DB isn't in our + # scope. python-- + self.__parent = super(self.__class__, self) + self.__parent.__init__(filename) - def _getDb(self, channel): - try: - import sqlite - except ImportError: - raise callbacks.Error, 'You need to have PySQLite installed to ' \ - 'use this plugin. Download it at ' \ - '' - filename = plugins.makeChannelFilename(self.filename, channel) - if filename in self.dbs: - return self.dbs[filename] - if os.path.exists(filename): - self.dbs[filename] = sqlite.connect(filename) - return self.dbs[filename] - db = sqlite.connect(filename) - self.dbs[filename] = db - cursor = db.cursor() - cursor.execute("""CREATE TABLE news ( - id INTEGER PRIMARY KEY, - subject TEXT, - item TEXT, - added_at TIMESTAMP, - expires_at TIMESTAMP, - added_by TEXT - )""") - db.commit() - return db + def add(self, subject, text, at, expires, by): + return self.__parent.add(self.Record(at=at, by=by, text=text, + subject=subject, expires=expires)) - def add(self, channel, subject, text, added_at, expires, by): - db = self._getDb(channel) - cursor = db.cursor() - cursor.execute("INSERT INTO news VALUES (NULL, %s, %s, %s, %s, %s)", - subject, text, added_at, expires, by) - db.commit() - - # XXX This should change only to support id, and the old functionality - # should move out to another method. - def get(self, channel, id=None, old=False): - db = self._getDb(channel) - cursor = db.cursor() - if id: - cursor.execute("""SELECT item, subject, added_at, - expires_at, added_by - FROM news - WHERE id=%s""", id) - if cursor.rowcount == 0: - raise dbi.NoRecordError, id - (text, subject, at, expires, by) = cursor.fetchone() - return NewsRecord(id, text=text, subject=subject, at=int(at), - expires=int(expires), by=by) - else: - if old: - cursor.execute("""SELECT id, subject - FROM news - WHERE expires_at <> 0 AND expires_at < %s - ORDER BY id DESC""", int(time.time())) + def getOld(self, id=None): + now = time.time() + if id: + return self.get(id) else: - cursor.execute("""SELECT id, subject - FROM news - WHERE expires_at > %s OR expires_at=0""", - int(time.time())) - if cursor.rowcount == 0: - raise dbi.NoRecordError + L = [R for R in self if R.expires < now and R.expires != 0] + if not L: + raise dbi.NoRecordError + else: + return L + + def get(self, id=None): + now = time.time() + if id: + return self.__parent.get(id) else: - return cursor.fetchall() + L = [R for R in self if R.expires >= now or R.expires == 0] + if not L: + raise dbi.NoRecordError + return L - def remove(self, channel, id): - db = self._getDb(channel) - cursor = db.cursor() - cursor.execute("""DELETE FROM news WHERE id = %s""", id) - db.commit() - if cursor.rowcount == 0: - raise dbi.NoRecordError, id + def change(self, id, f): + news = self.get(id) + s = '%s: %s' % (news.subject, news.text) + s = f(s) + (news.subject, news.text) = s.split(': ', 1) + self.set(id, news) - def change(self, channel, id, replacer): - db = self._getDb(channel) - cursor = db.cursor() - cursor.execute("""SELECT subject, item FROM news WHERE id=%s""", id) - if cursor.rowcount == 0: - raise dbi.NoRecordError, id - (subject, item) = cursor.fetchone() - s = '%s: %s' % (subject, item) - s = replacer(s) - (newSubject, newItem) = s.split(': ', 1) - cursor.execute("""UPDATE news SET subject=%s, item=%s WHERE id=%s""", - newSubject, newItem, id) - -NewsDB = plugins.DB('News', - {'sqlite': SqliteNewsDB}) +NewsDB = plugins.DB('News', {'flat': DbiNewsDB}) class News(callbacks.Privmsg): def __init__(self): self.__parent = super(News, self) self.__parent.__init__() self.db = NewsDB() - self.removeOld = False def die(self): self.__parent.die() self.db.close() - def add(self, irc, msg, args, channel): + def add(self, irc, msg, args, channel, user, at, expires, news): """[] : Adds a given news item of to a channel with the given . @@ -192,24 +136,15 @@ class News(callbacks.Privmsg): now. is only necessary if the message isn't sent in the channel itself. """ - (expire_interval, news) = privmsgs.getArgs(args, required=2) try: - expire_interval = int(expire_interval) (subject, text) = news.split(': ', 1) except ValueError: raise callbacks.ArgumentError - added_at = int(time.time()) - expires = expire_interval and (added_at + expire_interval) - # Set the other stuff needed for the insert. - try: - by = ircdb.users.getUserId(msg.prefix) - except KeyError: - by = msg.nick - self.db.add(channel, subject, text, added_at, expires, by) - irc.replySuccess() - add = privmsgs.checkChannelCapability(add, 'news') + id = self.db.add(channel, subject, text, at, expires, user.id) + irc.replySuccess('(News item #%s added)' % id) + add = wrap(add, ['channeldb', 'user', 'now', 'expiry', 'text']) - def news(self, irc, msg, args): + def news(self, irc, msg, args, channel, id): """[] [] Display the news items for in the format of '(#id) subject'. @@ -217,81 +152,92 @@ class News(callbacks.Privmsg): news items. is only necessary if the message isn't sent in the channel itself. """ - channel = privmsgs.getChannel(msg, args) - id = privmsgs.getArgs(args, required=0, optional=1) - if id: - try: - record = self.db.get(channel, int(id)) - irc.reply(str(record)) - except dbi.NoRecordError, id: - irc.errorInvalid('news item id', id) - else: + if not id: try: records = self.db.get(channel) - items = ['(#%s) %s' % (id, s) for (id, s) in records] + items = ['(#%s) %s' % (R.id, R.subject) for R in records] s = 'News for %s: %s' % (channel, '; '.join(items)) irc.reply(s) except dbi.NoRecordError: irc.reply('No news for %s.' % channel) + else: + try: + if id < 1: + raise ValueError + record = self.db.get(channel, id) + irc.reply(str(record)) + except dbi.NoRecordError, id: + irc.errorInvalid('news item id', id) + except ValueError: + irc.errorInvalid('news item id', id, + ' must be a positive integer.') + news = wrap(news, ['channeldb', ('int?', None)]) - def remove(self, irc, msg, args, channel): - """[] + def remove(self, irc, msg, args, channel, id): + """[] - Removes the news item with id from . is - only necessary if the message isn't sent in the channel itself. + Removes the news item with from . is only + necessary if the message isn't sent in the channel itself. """ - id = privmsgs.getArgs(args) try: + if id < 1: + raise ValueError self.db.remove(channel, id) irc.replySuccess() except dbi.NoRecordError: irc.errorInvalid('news item id', id) - remove = privmsgs.checkChannelCapability(remove, 'news') + except ValueError: + irc.errorInvalid('news item id', id, + ' must be a positive integer.') + remove = wrap(remove, ['channeldb', 'int']) - def change(self, irc, msg, args, channel): - """[] + def change(self, irc, msg, args, channel, id, replacer): + """[] - Changes the news item with id from according to the + Changes the news item with from according to the regular expression . should be of the form s/text/replacement/flags. is only necessary if the message isn't sent on the channel itself. """ - (id, regexp) = privmsgs.getArgs(args, required=2) - try: - replacer = utils.perlReToReplacer(regexp) - except ValueError, e: - irc.error(str(e)) - return try: + if id < 1: + raise ValueError self.db.change(channel, id, replacer) irc.replySuccess() except dbi.NoRecordError: irc.errorInvalid('news item id', id) - change = privmsgs.checkChannelCapability(change, 'news') + except ValueError: + irc.errorInvalid('news item id', id, + ' must be a positive integer.') + change = wrap(change, ['channeldb', 'int', 'regexpReplacer']) - def old(self, irc, msg, args): - """[] [] + def old(self, irc, msg, args, channel, id): + """[] [] - Returns the old news item for with id . If no number - is given, returns all the old news items in reverse order. - is only necessary if the message isn't sent in the channel itself. + Returns the old news item for with . If no number is + given, returns all the old news items in reverse order. is + only necessary if the message isn't sent in the channel itself. """ - channel = privmsgs.getChannel(msg, args) - id = privmsgs.getArgs(args, required=0, optional=1) if id: try: - record = self.db.get(channel, id, old=True) + if id < 1: + raise ValueError + record = self.db.getOld(channel, id) irc.reply(str(record)) except dbi.NoRecordError, id: irc.errorInvalid('news item id', id) + except ValueError: + irc.errorInvalid('news item id', id, + ' must be a positive integer.') else: try: - records = self.db.get(channel, old=True) - items = ['(#%s) %s' % (id, s) for (id, s) in records] + records = self.db.getOld(channel) + items = ['(#%s) %s' % (R.id, R.subject) for R in records] s = 'Old news for %s: %s' % (channel, '; '.join(items)) irc.reply(s) except dbi.NoRecordError: irc.reply('No old news for %s.' % channel) + old = wrap(old, ['channeldb', ('int?', None)]) Class = News diff --git a/test/test_News.py b/test/test_News.py index 6ca0cd4a7..03e03cdd9 100644 --- a/test/test_News.py +++ b/test/test_News.py @@ -33,57 +33,57 @@ import time import supybot.utils as utils -try: - import sqlite -except ImportError: - sqlite = None +class NewsTestCase(ChannelPluginTestCase): + plugins = ('News','User') + def setUp(self): + ChannelPluginTestCase.setUp(self) + # Create a valid user to use + self.prefix = 'news!bar@baz' + self.irc.feedMsg(ircmsgs.privmsg(self.nick, 'register tester moo', + prefix=self.prefix)) + m = self.irc.takeMsg() # Response to register. + def testAddnews(self): + self.assertNotError('add 0 subject: foo') + self.assertRegexp('news', 'subject') + self.assertNotError('add 0 subject2: foo2') + self.assertRegexp('news', 'subject.*subject2') + self.assertNotError('add 5 subject3: foo3') + self.assertRegexp('news', 'subject3') + print + print 'Sleeping to expire the news item (testAddnews)' + time.sleep(6) + print 'Done sleeping.' + self.assertNotRegexp('news', 'subject3') -if sqlite is not None: - class NewsTestCase(ChannelPluginTestCase): - plugins = ('News',) - def testAddnews(self): - self.assertNotError('add 0 subject: foo') - self.assertRegexp('news', 'subject') - self.assertNotError('add 0 subject2: foo2') - self.assertRegexp('news', 'subject.*subject2') - self.assertNotError('add 5 subject3: foo3') - self.assertRegexp('news', 'subject3') - print - print 'Sleeping to expire the news item (testAddnews)' - time.sleep(6) - print 'Done sleeping.' - self.assertNotRegexp('news', 'subject3') + def testNews(self): + # These should both fail first, as they will have nothing in the DB + self.assertRegexp('news', 'no news') + self.assertRegexp('news #channel', 'no news') + # Now we'll add news and make sure listnews doesn't fail + self.assertNotError('add #channel 0 subject: foo') + self.assertNotError('news #channel') + self.assertNotError('add 0 subject: foo') + self.assertRegexp('news', '#1') + self.assertNotError('news 1') - def testNews(self): - # These should both fail first, as they will have nothing in the DB - self.assertRegexp('news', 'no news') - self.assertRegexp('news #channel', 'no news') - # Now we'll add news and make sure listnews doesn't fail - self.assertNotError('add #channel 0 subject: foo') - self.assertNotError('news #channel') - self.assertNotError('add 0 subject: foo') - self.assertRegexp('news', '#1') - self.assertNotError('news 1') - - def testChangenews(self): - self.assertNotError('add 0 Foo: bar') - self.assertNotError('change 1 s/bar/baz/') - self.assertNotRegexp('news 1', 'bar') - self.assertRegexp('news 1', 'baz') - - def testOldnews(self): - self.assertRegexp('old', 'No old news') - self.assertNotError('add 0 a: b') - self.assertRegexp('old', 'No old news') - self.assertNotError('add 5 foo: bar') - self.assertRegexp('old', 'No old news') - print - print 'Sleeping to expire the news item (testOldnews)' - time.sleep(6) - print 'Done sleeping.' - self.assertNotError('old') + def testChangenews(self): + self.assertNotError('add 0 Foo: bar') + self.assertNotError('change 1 s/bar/baz/') + self.assertNotRegexp('news 1', 'bar') + self.assertRegexp('news 1', 'baz') + def testOldnews(self): + self.assertRegexp('old', 'No old news') + self.assertNotError('add 0 a: b') + self.assertRegexp('old', 'No old news') + self.assertNotError('add 5 foo: bar') + self.assertRegexp('old', 'No old news') + print + print 'Sleeping to expire the news item (testOldnews)' + time.sleep(6) + print 'Done sleeping.' + self.assertNotError('old') # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: