# Forward definition of Markov handler class class Lita::Handlers::Markov < Lita::Handler; end require 'oj' require 'lita/handlers/markov/engine' module Lita::Handlers class Markov template_root File.expand_path('../../../../templates', __FILE__) config :database_url, type: String, required: true route(/.+/, :ingest, command: false) route(/markov (.+)/, :generate, command: true, help: { 'markov USER' => 'Generate a markov chain from the given user.' }) http.get '/markov/backlog', :backlog_form http.post '/markov/upload_backlog', :upload_backlog # Share the engine instance between all instances of the bot def self.engine(instance) @engine ||= Engine.new(instance.config.database_url) end def engine self.class.engine(self) end def ingest(chat) # Don't ingest messages addressed to ourselves return if chat.command? message = chat.matches[0].strip # Get the mention name (ie. 'dirk') of the user id = chat.user.id engine.ingest id, message end def generate(chat) name = simplify_name chat.matches[0][0] user = Lita::User.fuzzy_find name if user.nil? chat.reply "Couldn't find the user #{name}. :(" return end begin sentence = engine.generate_sentence_for user.id chat.reply sentence rescue Engine::EmptyDictionaryError chat.reply "Looks like #{name} hasn't said anything!" end end def backlog_form(request, response) render_backlog_form response end def upload_backlog(request, response) t0 = Time.now response.headers['Content-Type'] = 'text/plain' multipart = Rack::Multipart.parse_multipart request.env tempfile = multipart.values.first[:tempfile] begin messages = Oj.load File.read(tempfile.path).strip, :mode => :strict rescue Oj::ParseError => error response.write error.message return end messages.select! { |m| m['type'] == 'message' } users = {} find_user = proc do |id| users[id] ||= Lita::User.fuzzy_find id end meta_tag_regex = /<(\w|[!|@])+>/ count = 0 messages.each do |message| count += 1 begin text = message['text'.freeze] next unless text user = find_user.call message['user'.freeze] unless user response.write "User not found for message ##{count}: #{message['user']}\n" next end message = text.gsub meta_tag_regex, ''.freeze engine.ingest user.id, message if count % 1000 == 0 response.write "Processed #{count} messages\n" end rescue => error response.write "Error writing message ##{count}: #{error.inspect}\n" end end response.write "Processed #{count} total messages in #{Time.now - t0} seconds\n" end private def render_backlog_form(response) response.write render_template('backlog_form') end def simplify_name name name .strip .tr('@', '') end Lita.register_handler self end end