PageRenderTime 43ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/News/plugin.py

https://bitbucket.org/Freso/supybot-code
Python | 211 lines | 136 code | 20 blank | 55 comment | 22 complexity | b844e1f52f3f7cda43fbbd5400b88f43 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. ###
  2. # Copyright (c) 2003-2005, Daniel DiPaolo
  3. # All rights reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions are met:
  7. #
  8. # * Redistributions of source code must retain the above copyright notice,
  9. # this list of conditions, and the following disclaimer.
  10. # * Redistributions in binary form must reproduce the above copyright notice,
  11. # this list of conditions, and the following disclaimer in the
  12. # documentation and/or other materials provided with the distribution.
  13. # * Neither the name of the author of this software nor the name of
  14. # contributors to this software may be used to endorse or promote products
  15. # derived from this software without specific prior written consent.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  21. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. # POSSIBILITY OF SUCH DAMAGE.
  28. ###
  29. import time
  30. import supybot.dbi as dbi
  31. import supybot.conf as conf
  32. import supybot.utils as utils
  33. from supybot.commands import *
  34. import supybot.plugins as plugins
  35. import supybot.ircutils as ircutils
  36. import supybot.callbacks as callbacks
  37. class DbiNewsDB(plugins.DbiChannelDB):
  38. class DB(dbi.DB):
  39. class Record(dbi.Record):
  40. __fields__ = [
  41. 'subject',
  42. 'text',
  43. 'at',
  44. 'expires',
  45. 'by',
  46. ]
  47. def __str__(self):
  48. user = plugins.getUserName(self.by)
  49. if self.expires == 0:
  50. s = format('%s (Subject: %q, added by %s on %s)',
  51. self.text, self.subject, self.by,
  52. utils.str.timestamp(self.at))
  53. else:
  54. s = format('%s (Subject: %q, added by %s on %s, '
  55. 'expires at %s)',
  56. self.text, self.subject, user,
  57. utils.str.timestamp(self.at),
  58. utils.str.timestamp(self.expires))
  59. return s
  60. def __init__(self, filename):
  61. # We use self.__class__ here because apparently DB isn't in our
  62. # scope. python--
  63. self.__parent = super(self.__class__, self)
  64. self.__parent.__init__(filename)
  65. def add(self, subject, text, at, expires, by):
  66. return self.__parent.add(self.Record(at=at, by=by, text=text,
  67. subject=subject, expires=expires))
  68. def getOld(self, id=None):
  69. now = time.time()
  70. if id:
  71. return self.get(id)
  72. else:
  73. L = [R for R in self if R.expires < now and R.expires != 0]
  74. if not L:
  75. raise dbi.NoRecordError
  76. else:
  77. return L
  78. def get(self, id=None):
  79. now = time.time()
  80. if id:
  81. return self.__parent.get(id)
  82. else:
  83. L = [R for R in self if R.expires >= now or R.expires == 0]
  84. if not L:
  85. raise dbi.NoRecordError
  86. return L
  87. def change(self, id, f):
  88. news = self.get(id)
  89. s = '%s: %s' % (news.subject, news.text)
  90. s = f(s)
  91. (news.subject, news.text) = s.split(': ', 1)
  92. self.set(id, news)
  93. NewsDB = plugins.DB('News', {'flat': DbiNewsDB})
  94. class News(callbacks.Plugin):
  95. def __init__(self, irc):
  96. self.__parent = super(News, self)
  97. self.__parent.__init__(irc)
  98. self.db = NewsDB()
  99. def die(self):
  100. self.__parent.die()
  101. self.db.close()
  102. def add(self, irc, msg, args, channel, user, at, expires, news):
  103. """[<channel>] <expires> <subject>: <text>
  104. Adds a given news item of <text> to a channel with the given <subject>.
  105. If <expires> isn't 0, that news item will expire <expires> seconds from
  106. now. <channel> is only necessary if the message isn't sent in the
  107. channel itself.
  108. """
  109. try:
  110. (subject, text) = news.split(': ', 1)
  111. except ValueError:
  112. raise callbacks.ArgumentError
  113. id = self.db.add(channel, subject, text, at, expires, user.id)
  114. irc.replySuccess(format('(News item #%i added)', id))
  115. add = wrap(add, ['channeldb', 'user', 'now', 'expiry', 'text'])
  116. def news(self, irc, msg, args, channel, id):
  117. """[<channel>] [<id>]
  118. Display the news items for <channel> in the format of '(#id) subject'.
  119. If <id> is given, retrieve only that news item; otherwise retrieve all
  120. news items. <channel> is only necessary if the message isn't sent in
  121. the channel itself.
  122. """
  123. if not id:
  124. try:
  125. records = self.db.get(channel)
  126. items = [format('(#%i) %s', R.id, R.subject) for R in records]
  127. s = format('News for %s: %s', channel, '; '.join(items))
  128. irc.reply(s)
  129. except dbi.NoRecordError:
  130. irc.reply(format('No news for %s.', channel))
  131. else:
  132. try:
  133. record = self.db.get(channel, id)
  134. irc.reply(str(record))
  135. except dbi.NoRecordError, id:
  136. irc.errorInvalid('news item id', id)
  137. news = wrap(news, ['channeldb', additional('positiveInt')])
  138. def remove(self, irc, msg, args, channel, id):
  139. """[<channel>] <id>
  140. Removes the news item with <id> from <channel>. <channel> is only
  141. necessary if the message isn't sent in the channel itself.
  142. """
  143. try:
  144. self.db.remove(channel, id)
  145. irc.replySuccess()
  146. except dbi.NoRecordError:
  147. irc.errorInvalid('news item id', id)
  148. remove = wrap(remove, ['channeldb', 'positiveInt'])
  149. def change(self, irc, msg, args, channel, id, replacer):
  150. """[<channel>] <id> <regexp>
  151. Changes the news item with <id> from <channel> according to the
  152. regular expression <regexp>. <regexp> should be of the form
  153. s/text/replacement/flags. <channel> is only necessary if the message
  154. isn't sent on the channel itself.
  155. """
  156. try:
  157. self.db.change(channel, id, replacer)
  158. irc.replySuccess()
  159. except dbi.NoRecordError:
  160. irc.errorInvalid('news item id', id)
  161. change = wrap(change, ['channeldb', 'positiveInt', 'regexpReplacer'])
  162. def old(self, irc, msg, args, channel, id):
  163. """[<channel>] [<id>]
  164. Returns the old news item for <channel> with <id>. If no number is
  165. given, returns all the old news items in reverse order. <channel> is
  166. only necessary if the message isn't sent in the channel itself.
  167. """
  168. if id:
  169. try:
  170. record = self.db.getOld(channel, id)
  171. irc.reply(str(record))
  172. except dbi.NoRecordError, id:
  173. irc.errorInvalid('news item id', id)
  174. else:
  175. try:
  176. records = self.db.getOld(channel)
  177. items = [format('(#%i) %s', R.id, R.subject) for R in records]
  178. s = format('Old news for %s: %s', channel, '; '.join(items))
  179. irc.reply(s)
  180. except dbi.NoRecordError:
  181. irc.reply(format('No old news for %s.', channel))
  182. old = wrap(old, ['channeldb', additional('positiveInt')])
  183. Class = News
  184. # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: