From 305625044af67ce75885bb2eda0665eaff73f67a Mon Sep 17 00:00:00 2001 From: "Brian S. Stephan" Date: Sat, 23 Apr 2011 16:07:32 -0500 Subject: [PATCH] Markov: track the context of said lines a context is a meta-classification ('banter, 'secrets', whatever) based on targets (channels or nicknames). when a line is being learned from a known target, the chains are placed in that context. this is for allowing one brain to have multiple personalities, in a sense, for large networks or cases where there may be a more sanitized set of channels and a couple channels where everyone lets it rip. a later enhancement would have sentence creation choose from context-less chains (and contexts matching the current target), but i need to go back to the drawing board on that one a bit. ramble ramble ramble --- modules/Markov.py | 76 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/modules/Markov.py b/modules/Markov.py index 4e76a2e..74030f7 100644 --- a/modules/Markov.py +++ b/modules/Markov.py @@ -87,6 +87,32 @@ class Markov(Module): db.rollback() print("sqlite error: " + str(e)) raise + if (version < 2): + db = self.get_db() + try: + db.execute(''' + ALTER TABLE markov_chain + ADD COLUMN context TEXT DEFAULT NULL''') + db.execute(''' + CREATE TABLE markov_context ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + context TEXT NOT NULL + )''') + db.execute(''' + CREATE TABLE markov_target_to_context_map ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + target TEXT NOT NULL, + context_id INTEGER NOT NULL, + FOREIGN KEY(context_id) REFERENCES markov_context(id) + )''') + db.execute('UPDATE drbotzo_modules SET version = ? WHERE module = ?', + (2, self.__class__.__name__)) + db.commit() + version = 2 + except sqlite3.Error as e: + db.rollback() + print('sqlite error: ' + str(e)) + raise def register_handlers(self): """Handle pubmsg/privmsg, to learn and/or reply to IRC events.""" @@ -108,12 +134,13 @@ class Markov(Module): what = ''.join(event.arguments()[0]) my_nick = connection.get_nickname() what = re.sub('^' + my_nick + '[:,]\s+', '', what) + target = event.target() # don't learn from commands if self.trainre.search(what) or self.learnre.search(what) or self.replyre.search(what): return - self._learn_line(what) + self._learn_line(what, target) def do(self, connection, event, nick, userhost, what, admin_unlocked): """Handle commands and inputs.""" @@ -154,10 +181,11 @@ class Markov(Module): def markov_learn(self, connection, event, nick, userhost, what, admin_unlocked): """Learn one line, as provided to the command.""" + target = event.target() match = self.learnre.search(what) if match: line = match.group(1) - self._learn_line(line) + self._learn_line(line, target) # return what was learned, for weird chaining purposes return line @@ -181,13 +209,18 @@ class Markov(Module): else: return self._reply(min_size=min_size, max_size=max_size) - def _learn_line(self, line): + def _learn_line(self, line, target=None): """Create Markov chains from the provided line.""" # set up the head of the chain k1 = self.start1 k2 = self.start2 + # see if there's a context for this + context = None + if target: + context = self._get_context_for_target(target) + words = line.split() if len(words) <= 0: return line @@ -197,12 +230,20 @@ class Markov(Module): try: db = self.get_db() cur = db.cursor() - statement = 'INSERT INTO markov_chain (k1, k2, v) VALUES (?, ?, ?)' + if context: + statement = 'INSERT INTO markov_chain (k1, k2, v, context) VALUES (?, ?, ?, ?)' - for word in words: - cur.execute(statement, (k1.decode('utf-8', 'replace').lower(), k2.decode('utf-8', 'replace').lower(), word.decode('utf-8', 'replace').lower())) - k1, k2 = k2, word - cur.execute(statement, (k1.decode('utf-8', 'replace').lower(), k2.decode('utf-8', 'replace').lower(), self.stop)) + for word in words: + cur.execute(statement, (k1.decode('utf-8', 'replace').lower(), k2.decode('utf-8', 'replace').lower(), word.decode('utf-8', 'replace').lower(), context)) + k1, k2 = k2, word + cur.execute(statement, (k1.decode('utf-8', 'replace').lower(), k2.decode('utf-8', 'replace').lower(), self.stop, context)) + else: + statement = 'INSERT INTO markov_chain (k1, k2, v) VALUES (?, ?, ?)' + + for word in words: + cur.execute(statement, (k1.decode('utf-8', 'replace').lower(), k2.decode('utf-8', 'replace').lower(), word.decode('utf-8', 'replace').lower())) + k1, k2 = k2, word + cur.execute(statement, (k1.decode('utf-8', 'replace').lower(), k2.decode('utf-8', 'replace').lower(), self.stop)) db.commit() except sqlite3.Error as e: @@ -336,5 +377,24 @@ class Markov(Module): print('sqlite error: ' + str(e)) raise + def _get_context_for_target(self, target): + """Get the context for a channel/nick, if defined.""" + + try: + db = self.get_db() + query = ''' + SELECT context_id FROM markov_target_to_context_map + WHERE target = ? + ''' + cursor = db.execute(query, (target,)) + result = cursor.fetchone() + if result: + return result['context_id'] + else: + return None + except sqlite3.Error as e: + print('sqlite error: ' + str(e)) + raise + # vi:tabstop=4:expandtab:autoindent # kate: indent-mode python;indent-width 4;replace-tabs on;