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
			
			
This commit is contained in:
		
							parent
							
								
									4e7c19a02a
								
							
						
					
					
						commit
						305625044a
					
				| @ -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; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user