mirror of
				https://github.com/Mikaela/Limnoria.git
				synced 2025-10-31 23:57:22 +01:00 
			
		
		
		
	Add Factoids in the new plugin format. It's probably still broken.
This commit is contained in:
		
							parent
							
								
									7e124b6ccc
								
							
						
					
					
						commit
						cc16ea6738
					
				
							
								
								
									
										1
									
								
								Factoids/README.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Factoids/README.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| Insert a description of your plugin here, with any notes, etc. about using it. | ||||
							
								
								
									
										64
									
								
								Factoids/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								Factoids/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| ### | ||||
| # Copyright (c) 2002-2005, Jeremiah Fincher | ||||
| # All rights reserved. | ||||
| # | ||||
| # Redistribution and use in source and binary forms, with or without | ||||
| # modification, are permitted provided that the following conditions are met: | ||||
| # | ||||
| #   * Redistributions of source code must retain the above copyright notice, | ||||
| #     this list of conditions, and the following disclaimer. | ||||
| #   * Redistributions in binary form must reproduce the above copyright notice, | ||||
| #     this list of conditions, and the following disclaimer in the | ||||
| #     documentation and/or other materials provided with the distribution. | ||||
| #   * Neither the name of the author of this software nor the name of | ||||
| #     contributors to this software may be used to endorse or promote products | ||||
| #     derived from this software without specific prior written consent. | ||||
| # | ||||
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
| # ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||||
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
| # POSSIBILITY OF SUCH DAMAGE. | ||||
| ### | ||||
| 
 | ||||
| """ | ||||
| Handles 'factoids,' little tidbits of information held in a database and | ||||
| available on demand via several commands. | ||||
| """ | ||||
| 
 | ||||
| import supybot | ||||
| import supybot.world as world | ||||
| 
 | ||||
| # Use this for the version of this plugin.  You may wish to put a CVS keyword | ||||
| # in here if you're keeping the plugin in CVS or some similar system. | ||||
| __version__ = "0.1" | ||||
| 
 | ||||
| __author__ = supybot.authors.jemfinch | ||||
| 
 | ||||
| # This is a dictionary mapping supybot.Author instances to lists of | ||||
| # contributions. | ||||
| __contributors__ = {} | ||||
| 
 | ||||
| # This is a url where the most recent plugin package can be downloaded. | ||||
| __url__ = '' # 'http://supybot.com/Members/yourname/Factoids/download' | ||||
| 
 | ||||
| import config | ||||
| import plugin | ||||
| reload(plugin) # In case we're being reloaded. | ||||
| # Add more reloads here if you add third-party modules and want them to be | ||||
| # reloaded when this plugin is reloaded.  Don't forget to import them as well! | ||||
| 
 | ||||
| if world.testing: | ||||
|     import test | ||||
| 
 | ||||
| Class = plugin.Class | ||||
| configure = config.configure | ||||
| 
 | ||||
| 
 | ||||
| # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: | ||||
							
								
								
									
										61
									
								
								Factoids/config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								Factoids/config.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| ### | ||||
| # Copyright (c) 2002-2005, Jeremiah Fincher | ||||
| # All rights reserved. | ||||
| # | ||||
| # Redistribution and use in source and binary forms, with or without | ||||
| # modification, are permitted provided that the following conditions are met: | ||||
| # | ||||
| #   * Redistributions of source code must retain the above copyright notice, | ||||
| #     this list of conditions, and the following disclaimer. | ||||
| #   * Redistributions in binary form must reproduce the above copyright notice, | ||||
| #     this list of conditions, and the following disclaimer in the | ||||
| #     documentation and/or other materials provided with the distribution. | ||||
| #   * Neither the name of the author of this software nor the name of | ||||
| #     contributors to this software may be used to endorse or promote products | ||||
| #     derived from this software without specific prior written consent. | ||||
| # | ||||
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
| # ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||||
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
| # POSSIBILITY OF SUCH DAMAGE. | ||||
| ### | ||||
| 
 | ||||
| import supybot.conf as conf | ||||
| import supybot.registry as registry | ||||
| 
 | ||||
| def configure(advanced): | ||||
|     # This will be called by supybot to configure this module.  advanced is | ||||
|     # a bool that specifies whether the user identified himself as an advanced | ||||
|     # user or not.  You should effect your configuration by manipulating the | ||||
|     # registry as appropriate. | ||||
|     from supybot.questions import expect, anything, something, yn | ||||
|     conf.registerPlugin('Factoids', True) | ||||
| 
 | ||||
| 
 | ||||
| Factoids = conf.registerPlugin('Factoids') | ||||
| conf.registerChannelValue(Factoids, 'learnSeparator', | ||||
|     registry.String('as', """Determines what separator must be used in the | ||||
|     learn command.  Defaults to 'as' -- learn <key> as <value>.  Users might | ||||
|     feel more comfortable with 'is' or something else, so it's | ||||
|     configurable.""")) | ||||
| conf.registerChannelValue(Factoids, 'showFactoidIfOnlyOneMatch', | ||||
|     registry.Boolean(True, """Determines whether the bot will reply with the | ||||
|     single matching factoid if only one factoid matches when using the search | ||||
|     command.""")) | ||||
| conf.registerChannelValue(Factoids, 'replyWhenInvalidCommand', | ||||
|     registry.Boolean(True,  """Determines whether the bot will reply to invalid | ||||
|     commands by searching for a factoid; basically making the whatis | ||||
|     unnecessary when you want all factoids for a given key.""")) | ||||
| conf.registerChannelValue(Factoids, 'factoidPrefix', | ||||
|     registry.StringWithSpaceOnRight('could be ', """Determines the string that | ||||
|     factoids will be introduced by.""")) | ||||
| 
 | ||||
| 
 | ||||
| # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: | ||||
							
								
								
									
										389
									
								
								Factoids/plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										389
									
								
								Factoids/plugin.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,389 @@ | ||||
| ### | ||||
| # Copyright (c) 2002-2005, Jeremiah Fincher | ||||
| # All rights reserved. | ||||
| # | ||||
| # Redistribution and use in source and binary forms, with or without | ||||
| # modification, are permitted provided that the following conditions are met: | ||||
| # | ||||
| #   * Redistributions of source code must retain the above copyright notice, | ||||
| #     this list of conditions, and the following disclaimer. | ||||
| #   * Redistributions in binary form must reproduce the above copyright notice, | ||||
| #     this list of conditions, and the following disclaimer in the | ||||
| #     documentation and/or other materials provided with the distribution. | ||||
| #   * Neither the name of the author of this software nor the name of | ||||
| #     contributors to this software may be used to endorse or promote products | ||||
| #     derived from this software without specific prior written consent. | ||||
| # | ||||
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
| # ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||||
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
| # POSSIBILITY OF SUCH DAMAGE. | ||||
| ### | ||||
| 
 | ||||
| import os | ||||
| import time | ||||
| import string | ||||
| 
 | ||||
| import supybot.conf as conf | ||||
| import supybot.ircdb as ircdb | ||||
| import supybot.utils as utils | ||||
| from supybot.commands import * | ||||
| import supybot.plugins as plugins | ||||
| import supybot.ircutils as ircutils | ||||
| import supybot.callbacks as callbacks | ||||
| 
 | ||||
| try: | ||||
|     import sqlite | ||||
| except ImportError: | ||||
|     raise callbacks.Error, 'You need to have PySQLite installed to use this ' \ | ||||
|                            'plugin.  Download it at <http://pysqlite.sf.net/>' | ||||
| 
 | ||||
| def getFactoid(irc, msg, args, state): | ||||
|     assert not state.channel | ||||
|     callConverter('channel', irc, msg, args, state) | ||||
|     separator = state.cb.registryValue('learnSeparator', state.channel) | ||||
|     try: | ||||
|         i = args.index(separator) | ||||
|     except ValueError: | ||||
|         raise callbacks.ArgumentError | ||||
|     args.pop(i) | ||||
|     key = [] | ||||
|     value = [] | ||||
|     for (j, s) in enumerate(args[:]): | ||||
|         if j < i: | ||||
|             key.append(args.pop(0)) | ||||
|         else: | ||||
|             value.append(args.pop(0)) | ||||
|     state.args.append(' '.join(key)) | ||||
|     state.args.append(' '.join(value)) | ||||
| 
 | ||||
| addConverter('factoid', getFactoid) | ||||
| 
 | ||||
| class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): | ||||
|     def makeDb(self, filename): | ||||
|         if os.path.exists(filename): | ||||
|             return sqlite.connect(filename) | ||||
|         db = sqlite.connect(filename) | ||||
|         cursor = db.cursor() | ||||
|         cursor.execute("""CREATE TABLE keys ( | ||||
|                           id INTEGER PRIMARY KEY, | ||||
|                           key TEXT UNIQUE ON CONFLICT IGNORE, | ||||
|                           locked BOOLEAN | ||||
|                           )""") | ||||
|         cursor.execute("""CREATE TABLE factoids ( | ||||
|                           id INTEGER PRIMARY KEY, | ||||
|                           key_id INTEGER, | ||||
|                           added_by TEXT, | ||||
|                           added_at TIMESTAMP, | ||||
|                           fact TEXT | ||||
|                           )""") | ||||
|         cursor.execute("""CREATE TRIGGER remove_factoids | ||||
|                           BEFORE DELETE ON keys | ||||
|                           BEGIN | ||||
|                             DELETE FROM factoids WHERE key_id = old.id; | ||||
|                           END | ||||
|                        """) | ||||
|         db.commit() | ||||
|         return db | ||||
| 
 | ||||
|     def learn(self, irc, msg, args, channel, key, factoid): | ||||
|         """[<channel>] <key> as <value> | ||||
| 
 | ||||
|         Associates <key> with <value>.  <channel> is only necessary if the | ||||
|         message isn't sent on the channel itself.  The word 'as' is necessary | ||||
|         to separate the key from the value.  It can be changed to another | ||||
|         word via the learnSeparator registry value. | ||||
|         """ | ||||
|         db = self.getDb(channel) | ||||
|         cursor = db.cursor() | ||||
|         cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s", key) | ||||
|         if cursor.rowcount == 0: | ||||
|             cursor.execute("""INSERT INTO keys VALUES (NULL, %s, 0)""", key) | ||||
|             db.commit() | ||||
|             cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s",key) | ||||
|         (id, locked) = map(int, cursor.fetchone()) | ||||
|         capability = ircdb.makeChannelCapability(channel, 'factoids') | ||||
|         if not locked: | ||||
|             if ircdb.users.hasUser(msg.prefix): | ||||
|                 name = ircdb.users.getUser(msg.prefix).name | ||||
|             else: | ||||
|                 name = msg.nick | ||||
|             cursor.execute("""INSERT INTO factoids VALUES | ||||
|                               (NULL, %s, %s, %s, %s)""", | ||||
|                            id, name, int(time.time()), factoid) | ||||
|             db.commit() | ||||
|             irc.replySuccess() | ||||
|         else: | ||||
|             irc.error('That factoid is locked.') | ||||
|     learn = wrap(learn, ['factoid']) | ||||
| 
 | ||||
|     def _lookupFactoid(self, channel, key): | ||||
|         db = self.getDb(channel) | ||||
|         cursor = db.cursor() | ||||
|         cursor.execute("""SELECT factoids.fact FROM factoids, keys | ||||
|                           WHERE keys.key LIKE %s AND factoids.key_id=keys.id | ||||
|                           ORDER BY factoids.id | ||||
|                           LIMIT 20""", key) | ||||
|         return [t[0] for t in cursor.fetchall()] | ||||
| 
 | ||||
|     def _replyFactoids(self, irc, channel, key, factoids, number=0, error=True): | ||||
|         if factoids: | ||||
|             if number: | ||||
|                 try: | ||||
|                     irc.reply(factoids[number-1]) | ||||
|                 except IndexError: | ||||
|                     irc.error('That\'s not a valid number for that key.') | ||||
|                     return | ||||
|             else: | ||||
|                 intro = self.registryValue('factoidPrefix', channel) | ||||
|                 prefix = format('%q %s', key, intro) | ||||
|                 if len(factoids) == 1: | ||||
|                     irc.reply(prefix + factoids[0]) | ||||
|                 else: | ||||
|                     factoidsS = [] | ||||
|                     counter = 1 | ||||
|                     for factoid in factoids: | ||||
|                         factoidsS.append(format('(#%i) %s', counter, factoid)) | ||||
|                         counter += 1 | ||||
|                     irc.replies(factoidsS, prefixer=prefix, | ||||
|                                 joiner=', or ', onlyPrefixFirst=True) | ||||
|         elif error: | ||||
|             irc.error('No factoid matches that key.') | ||||
| 
 | ||||
|     def tokenizedCommand(self, irc, msg, tokens): | ||||
|         if irc.isChannel(msg.args[0]): | ||||
|             channel = msg.args[0] | ||||
|             if self.registryValue('replyWhenInvalidCommand', channel): | ||||
|                 key = ' '.join(tokens) | ||||
|                 factoids = self._lookupFactoid(channel, key) | ||||
|                 self._replyFactoids(irc, channel, key, factoids, error=False) | ||||
| 
 | ||||
|     def whatis(self, irc, msg, args, channel, number, key): | ||||
|         """[<channel>] <key> [<number>] | ||||
| 
 | ||||
|         Looks up the value of <key> in the factoid database.  If given a | ||||
|         number, will return only that exact factoid.  <channel> is only | ||||
|         necessary if the message isn't sent in the channel itself. | ||||
|         """ | ||||
|         factoids = self._lookupFactoid(channel, key) | ||||
|         self._replyFactoids(irc, channel, key, factoids, number) | ||||
|     whatis = wrap(whatis, ['channel', reverse(optional('int', 0)), 'text']) | ||||
| 
 | ||||
|     def lock(self, irc, msg, args, channel, key): | ||||
|         """[<channel>] <key> | ||||
| 
 | ||||
|         Locks the factoid(s) associated with <key> so that they cannot be | ||||
|         removed or added to.  <channel> is only necessary if the message isn't | ||||
|         sent in the channel itself. | ||||
|         """ | ||||
|         db = self.getDb(channel) | ||||
|         cursor = db.cursor() | ||||
|         cursor.execute("UPDATE keys SET locked=1 WHERE key LIKE %s", key) | ||||
|         db.commit() | ||||
|         irc.replySuccess() | ||||
|     lock = wrap(lock, ['channel', 'text']) | ||||
| 
 | ||||
|     def unlock(self, irc, msg, args, channel, key): | ||||
|         """[<channel>] <key> | ||||
| 
 | ||||
|         Unlocks the factoid(s) associated with <key> so that they can be | ||||
|         removed or added to.  <channel> is only necessary if the message isn't | ||||
|         sent in the channel itself. | ||||
|         """ | ||||
|         db = self.getDb(channel) | ||||
|         cursor = db.cursor() | ||||
|         cursor.execute("UPDATE keys SET locked=0 WHERE key LIKE %s", key) | ||||
|         db.commit() | ||||
|         irc.replySuccess() | ||||
|     unlock = wrap(unlock, ['channel', 'text']) | ||||
| 
 | ||||
|     def forget(self, irc, msg, args, channel, number, key): | ||||
|         """[<channel>] <key> [<number>|*] | ||||
| 
 | ||||
|         Removes the factoid <key> from the factoids database.  If there are | ||||
|         more than one factoid with such a key, a number is necessary to | ||||
|         determine which one should be removed.  A * can be used to remove all | ||||
|         factoids associated with a key.  <channel> is only necessary if | ||||
|         the message isn't sent in the channel itself. | ||||
|         """ | ||||
|         if number == '*': | ||||
|             number = True | ||||
|         db = self.getDb(channel) | ||||
|         cursor = db.cursor() | ||||
|         cursor.execute("""SELECT keys.id, factoids.id | ||||
|                           FROM keys, factoids | ||||
|                           WHERE key LIKE %s AND | ||||
|                                 factoids.key_id=keys.id""", key) | ||||
|         if cursor.rowcount == 0: | ||||
|             irc.error('There is no such factoid.') | ||||
|         elif cursor.rowcount == 1 or number is True: | ||||
|             (id, _) = cursor.fetchone() | ||||
|             cursor.execute("""DELETE FROM factoids WHERE key_id=%s""", id) | ||||
|             cursor.execute("""DELETE FROM keys WHERE key LIKE %s""", key) | ||||
|             db.commit() | ||||
|             irc.replySuccess() | ||||
|         else: | ||||
|             if number is not None: | ||||
|                 results = cursor.fetchall() | ||||
|                 try: | ||||
|                     (_, id) = results[number] | ||||
|                 except IndexError: | ||||
|                     irc.error('Invalid factoid number.') | ||||
|                     return | ||||
|                 cursor.execute("DELETE FROM factoids WHERE id=%s", id) | ||||
|                 db.commit() | ||||
|                 irc.replySuccess() | ||||
|             else: | ||||
|                 irc.error('%s factoids have that key.  ' | ||||
|                           'Please specify which one to remove, ' | ||||
|                           'or use * to designate all of them.' % | ||||
|                           cursor.rowcount) | ||||
|     forget = wrap(forget, ['channel', | ||||
|                            reverse(optional(first(('literal', '*'), | ||||
|                                                   'id'))), | ||||
|                            'text']) | ||||
| 
 | ||||
|     def random(self, irc, msg, args, channel): | ||||
|         """[<channel>] | ||||
| 
 | ||||
|         Returns a random factoid from the database for <channel>.  <channel> | ||||
|         is only necessary if the message isn't sent in the channel itself. | ||||
|         """ | ||||
|         db = self.getDb(channel) | ||||
|         cursor = db.cursor() | ||||
|         cursor.execute("""SELECT fact, key_id FROM factoids | ||||
|                           ORDER BY random() | ||||
|                           LIMIT 3""") | ||||
|         if cursor.rowcount != 0: | ||||
|             L = [] | ||||
|             for (factoid, id) in cursor.fetchall(): | ||||
|                 cursor.execute("""SELECT key FROM keys WHERE id=%s""", id) | ||||
|                 (key,) = cursor.fetchone() | ||||
|                 L.append('"%s": %s' % (ircutils.bold(key), factoid)) | ||||
|             irc.reply('; '.join(L)) | ||||
|         else: | ||||
|             irc.error('I couldn\'t find a factoid.') | ||||
|     random = wrap(random, ['channel']) | ||||
| 
 | ||||
|     def info(self, irc, msg, args, channel, key): | ||||
|         """[<channel>] <key> | ||||
| 
 | ||||
|         Gives information about the factoid(s) associated with <key>. | ||||
|         <channel> is only necessary if the message isn't sent in the channel | ||||
|         itself. | ||||
|         """ | ||||
|         db = self.getDb(channel) | ||||
|         cursor = db.cursor() | ||||
|         cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s", key) | ||||
|         if cursor.rowcount == 0: | ||||
|             irc.error('No factoid matches that key.') | ||||
|             return | ||||
|         (id, locked) = map(int, cursor.fetchone()) | ||||
|         cursor.execute("""SELECT  added_by, added_at FROM factoids | ||||
|                           WHERE key_id=%s | ||||
|                           ORDER BY id""", id) | ||||
|         factoids = cursor.fetchall() | ||||
|         L = [] | ||||
|         counter = 0 | ||||
|         for (added_by, added_at) in factoids: | ||||
|             counter += 1 | ||||
|             added_at = time.strftime(conf.supybot.reply.format.time(), | ||||
|                                      time.localtime(int(added_at))) | ||||
|             L.append(format('#%i was added by %s at %s', | ||||
|                             counter, added_by, added_at)) | ||||
|         factoids = '; '.join(L) | ||||
|         s = format('Key %q is %s and has %n associated with it: %s', | ||||
|                    key, locked and 'locked' or 'not locked', | ||||
|                    (counter, 'factoid'), factoids) | ||||
|         irc.reply(s) | ||||
|     info = wrap(info, ['channel', 'text']) | ||||
| 
 | ||||
|     def change(self, irc, msg, args, channel, key, number, replacer): | ||||
|         """[<channel>] <key> <number> <regexp> | ||||
| 
 | ||||
|         Changes the factoid #<number> associated with <key> according to | ||||
|         <regexp>. | ||||
|         """ | ||||
|         db = self.getDb(channel) | ||||
|         cursor = db.cursor() | ||||
|         cursor.execute("""SELECT factoids.id, factoids.fact | ||||
|                           FROM keys, factoids | ||||
|                           WHERE keys.key LIKE %s AND | ||||
|                                 keys.id=factoids.key_id""", key) | ||||
|         if cursor.rowcount == 0: | ||||
|             irc.error(format('I couldn\'t find any key %q', key)) | ||||
|             return | ||||
|         elif cursor.rowcount < number: | ||||
|             irc.errorInvalid('key id') | ||||
|         (id, fact) = cursor.fetchall()[number-1] | ||||
|         newfact = replacer(fact) | ||||
|         cursor.execute("UPDATE factoids SET fact=%s WHERE id=%s", newfact, id) | ||||
|         db.commit() | ||||
|         irc.replySuccess() | ||||
|     change = wrap(change, ['channel', 'something', | ||||
|                            ('id', 'key id'), 'regexpReplacer']) | ||||
| 
 | ||||
|     _sqlTrans = string.maketrans('*?', '%_') | ||||
|     def search(self, irc, msg, args, channel, optlist, globs): | ||||
|         """[<channel>] [--values] [--{regexp} <value>] [<glob> ...] | ||||
| 
 | ||||
|         Searches the keyspace for keys matching <glob>.  If --regexp is given, | ||||
|         it associated value is taken as a regexp and matched against the keys. | ||||
|         If --values is given, search the value space instead of the keyspace. | ||||
|         """ | ||||
|         if not optlist and not globs: | ||||
|             raise callbacks.ArgumentError | ||||
|         tables = ['keys'] | ||||
|         formats = [] | ||||
|         criteria = [] | ||||
|         target = 'keys.key' | ||||
|         predicateName = 'p' | ||||
|         db = self.getDb(channel) | ||||
|         for (option, arg) in optlist: | ||||
|             if option == 'values': | ||||
|                 target = 'factoids.fact' | ||||
|                 if 'factoids' not in tables: | ||||
|                     tables.append('factoids') | ||||
|                 criteria.append('factoids.key_id=keys.id') | ||||
|             elif option == 'regexp': | ||||
|                 criteria.append('%s(TARGET)' % predicateName) | ||||
|                 def p(s, r=arg): | ||||
|                     return int(bool(r.search(s))) | ||||
|                 db.create_function(predicateName, 1, p) | ||||
|                 predicateName += 'p' | ||||
|         for glob in globs: | ||||
|             criteria.append('TARGET LIKE %s') | ||||
|             formats.append(glob.translate(self._sqlTrans)) | ||||
|         cursor = db.cursor() | ||||
|         sql = """SELECT keys.key FROM %s WHERE %s""" % \ | ||||
|               (', '.join(tables), ' AND '.join(criteria)) | ||||
|         sql = sql.replace('TARGET', target) | ||||
|         cursor.execute(sql, formats) | ||||
|         if cursor.rowcount == 0: | ||||
|             irc.reply('No keys matched that query.') | ||||
|         elif cursor.rowcount == 1 and \ | ||||
|              self.registryValue('showFactoidIfOnlyOneMatch', channel): | ||||
|             self.whatis(irc, msg, [cursor.fetchone()[0]]) | ||||
|         elif cursor.rowcount > 100: | ||||
|             irc.reply('More than 100 keys matched that query; ' | ||||
|                       'please narrow your query.') | ||||
|         else: | ||||
|             keys = [repr(t[0]) for t in cursor.fetchall()] | ||||
|             s = format('%L', keys) | ||||
|             irc.reply(s) | ||||
|     search = wrap(search, ['channel', | ||||
|                            getopts({'values': '', 'regexp': 'regexpMatcher'}), | ||||
|                            any('glob')]) | ||||
| 
 | ||||
| 
 | ||||
| Class = Factoids | ||||
| 
 | ||||
| 
 | ||||
| # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: | ||||
							
								
								
									
										154
									
								
								Factoids/test.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								Factoids/test.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,154 @@ | ||||
| ### | ||||
| # Copyright (c) 2002-2005, Jeremiah Fincher | ||||
| # All rights reserved. | ||||
| # | ||||
| # Redistribution and use in source and binary forms, with or without | ||||
| # modification, are permitted provided that the following conditions are met: | ||||
| # | ||||
| #   * Redistributions of source code must retain the above copyright notice, | ||||
| #     this list of conditions, and the following disclaimer. | ||||
| #   * Redistributions in binary form must reproduce the above copyright notice, | ||||
| #     this list of conditions, and the following disclaimer in the | ||||
| #     documentation and/or other materials provided with the distribution. | ||||
| #   * Neither the name of the author of this software nor the name of | ||||
| #     contributors to this software may be used to endorse or promote products | ||||
| #     derived from this software without specific prior written consent. | ||||
| # | ||||
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
| # ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||||
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
| # POSSIBILITY OF SUCH DAMAGE. | ||||
| ### | ||||
| 
 | ||||
| from supybot.test import * | ||||
| 
 | ||||
| try: | ||||
|     import sqlite | ||||
| except ImportError: | ||||
|     sqlite = None | ||||
| 
 | ||||
| if sqlite: | ||||
|     class FactoidsTestCase(ChannelPluginTestCase): | ||||
|         plugins = ('Factoids',) | ||||
|         def testRandomfactoid(self): | ||||
|             self.assertError('random') | ||||
|             self.assertNotError('learn jemfinch as my primary author') | ||||
|             self.assertRegexp('random', 'primary author') | ||||
| 
 | ||||
|         def testLearn(self): | ||||
|             self.assertNotError('learn jemfinch as my primary author') | ||||
|             self.assertNotError('info jemfinch') | ||||
|             self.assertRegexp('whatis jemfinch', 'my primary author') | ||||
|             self.assertRegexp('whatis JEMFINCH', 'my primary author') | ||||
|             self.assertRegexp('whatis JEMFINCH 1', 'my primary author') | ||||
|             self.assertNotError('learn jemfinch as a bad assembly programmer') | ||||
|             self.assertRegexp('whatis jemfinch 2', 'bad assembly') | ||||
|             self.assertNotRegexp('whatis jemfinch 2', 'primary author') | ||||
|             self.assertRegexp('whatis jemfinch', r'.*primary author.*assembly') | ||||
|             self.assertError('forget jemfinch') | ||||
|             self.assertError('forget jemfinch 3') | ||||
|             self.assertError('forget jemfinch 0') | ||||
|             self.assertNotError('forget jemfinch 2') | ||||
|             self.assertNotError('forget jemfinch 1') | ||||
|             self.assertError('whatis jemfinch') | ||||
|             self.assertError('info jemfinch') | ||||
| 
 | ||||
|             self.assertNotError('learn foo bar as baz') | ||||
|             self.assertNotError('info foo bar') | ||||
|             self.assertRegexp('whatis foo bar', 'baz') | ||||
|             self.assertNotError('learn foo bar as quux') | ||||
|             self.assertRegexp('whatis foo bar', '.*baz.*quux') | ||||
|             self.assertError('forget foo bar') | ||||
|             self.assertNotError('forget foo bar 2') | ||||
|             self.assertNotError('forget foo bar 1') | ||||
|             self.assertError('whatis foo bar') | ||||
|             self.assertError('info foo bar') | ||||
| 
 | ||||
|             self.assertError('learn foo bar baz') # No 'as' | ||||
|             self.assertError('learn foo bar') # No 'as' | ||||
| 
 | ||||
|         def testChangeFactoid(self): | ||||
|             self.assertNotError('learn foo as bar') | ||||
|             self.assertNotError('change foo 1 s/bar/baz/') | ||||
|             self.assertRegexp('whatis foo', 'baz') | ||||
|             self.assertError('change foo 2 s/bar/baz/') | ||||
|             self.assertError('change foo 0 s/bar/baz/') | ||||
| 
 | ||||
|         def testSearchFactoids(self): | ||||
|             self.assertNotError('learn jemfinch as my primary author') | ||||
|             self.assertNotError('learn strike as a cool guy working on me') | ||||
|             self.assertNotError('learn inkedmn as another of my developers') | ||||
|             self.assertNotError('learn jamessan as a developer of much python') | ||||
|             self.assertNotError('learn bwp as author of my weather command') | ||||
|             self.assertRegexp('factoids search --regexp /.w./', 'bwp') | ||||
|             self.assertRegexp('factoids search --regexp /^.+i/', | ||||
|                               'jemfinch.*strike') | ||||
|             self.assertNotRegexp('factoids search --regexp /^.+i/', 'inkedmn') | ||||
|             self.assertRegexp('factoids search --regexp m/j/ --regexp m/ss/', | ||||
|                               'jamessan') | ||||
|             self.assertRegexp('factoids search --regexp m/^j/ *ss*', | ||||
|                               'jamessan') | ||||
|             self.assertRegexp('factoids search --regexp /^j/', | ||||
|                               'jemfinch.*jamessan') | ||||
|             self.assertRegexp('factoids search j*', 'jemfinch.*jamessan') | ||||
|             self.assertRegexp('factoids search *ke*', | ||||
|                               'inkedmn.*strike|strike.*inkedmn') | ||||
|             self.assertRegexp('factoids search ke', | ||||
|                               'inkedmn.*strike|strike.*inkedmn') | ||||
|             self.assertRegexp('factoids search jemfinch', | ||||
|                               'my primary author') | ||||
|             self.assertRegexp('factoids search --values primary author', | ||||
|                               'my primary author') | ||||
| 
 | ||||
|         def testWhatisOnNumbers(self): | ||||
|             self.assertNotError('learn 911 as emergency number') | ||||
|             self.assertRegexp('whatis 911', 'emergency number') | ||||
| 
 | ||||
|         def testNotZeroIndexed(self): | ||||
|             self.assertNotError('learn foo as bar') | ||||
|             self.assertNotRegexp('info foo', '#0') | ||||
|             self.assertNotRegexp('whatis foo', '#0') | ||||
|             self.assertNotError('learn foo as baz') | ||||
|             self.assertNotRegexp('info foo', '#0') | ||||
|             self.assertNotRegexp('whatis foo', '#0') | ||||
| 
 | ||||
|         def testInfoReturnsRightNumber(self): | ||||
|             self.assertNotError('learn foo as bar') | ||||
|             self.assertNotRegexp('info foo', '2 factoids') | ||||
| 
 | ||||
|         def testLearnSeparator(self): | ||||
|             self.assertError('learn foo is bar') | ||||
|             self.assertNotError('learn foo as bar') | ||||
|             self.assertRegexp('whatis foo', 'bar') | ||||
|             try: | ||||
|                 conf.supybot.plugins.Factoids.learnSeparator.setValue('is') | ||||
|                 self.assertError('learn bar as baz') | ||||
|                 self.assertNotError('learn bar is baz') | ||||
|                 self.assertRegexp('whatis bar', 'baz') | ||||
|             finally: | ||||
|                 conf.supybot.plugins.Factoids.learnSeparator.setValue('as') | ||||
| 
 | ||||
|         def testShowFactoidIfOnlyOneMatch(self): | ||||
|             m1 = self.assertNotError('factoids search m/foo|bar/') | ||||
|             try: | ||||
|                 conf.supybot.plugins.Factoids. \ | ||||
|                     showFactoidIfOnlyOneMatch.setValue(False) | ||||
|                 m2 = self.assertNotError('factoids search m/foo/') | ||||
|                 self.failUnless(m1.args[1].startswith(m2.args[1])) | ||||
|             finally: | ||||
|                 conf.supybot.plugins.Factoids. \ | ||||
|                     showFactoidIfOnlyOneMatch.setValue(True) | ||||
| 
 | ||||
|         def testInvalidCommand(self): | ||||
|             self.assertNotError('learn foo as bar') | ||||
|             self.assertRegexp('foo', 'bar') | ||||
| 
 | ||||
| 
 | ||||
| # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 James Vega
						James Vega