From 45c2a3d329d5a551cd20e95733e081f804aa7e38 Mon Sep 17 00:00:00 2001 From: Zach Wily Date: Sun, 13 Nov 2011 14:00:14 -0700 Subject: [PATCH] add basic search functionality to the Backlog plugin Adds a search command to the bot that will return the last 10 lines matching the provided string in the specified context. This required sending the bouncer to the bot instead of the just the user, since we needed to get more state. Stuff to be done to make better: * Let users page through results * Let users ask for X lines of context around one of the results * Make searching more efficient (we normally want to search from the end of a backlog) * Colorize results The first two items will require keeping some state around for searches (things like last query, offsets of each result into the file, etc.) --- lib/tkellem/bouncer_connection.rb | 2 +- lib/tkellem/plugins/backlog.rb | 37 +++++++++++++++++++++++++++++++ lib/tkellem/tkellem_bot.rb | 24 +++++++++++--------- 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/lib/tkellem/bouncer_connection.rb b/lib/tkellem/bouncer_connection.rb index 57de6ea..07443e6 100644 --- a/lib/tkellem/bouncer_connection.rb +++ b/lib/tkellem/bouncer_connection.rb @@ -56,7 +56,7 @@ def connect_to_irc_server end def msg_tkellem(msg) - TkellemBot.run_command(msg.args.join(' '), @user) do |response| + TkellemBot.run_command(msg.args.join(' '), @bouncer) do |response| say_as_tkellem(response) end end diff --git a/lib/tkellem/plugins/backlog.rb b/lib/tkellem/plugins/backlog.rb index 7459150..d19e092 100644 --- a/lib/tkellem/plugins/backlog.rb +++ b/lib/tkellem/plugins/backlog.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/class/attribute_accessors' require 'tkellem/irc_message' +require 'tkellem/tkellem_bot' module Tkellem @@ -166,4 +167,40 @@ def parse_line(line, ctx_name) end end +class TkellemBot + class Search < Command + register 'search' + + def execute(args, bouncer) + ctx = args[:rest].shift + query = args[:rest].join " " + + unless ctx.present? && query.present? + r "Please specify a #room and a string to search for." + return + end + + backlog = Backlog.get_instance(bouncer) + + filename = backlog.stream_filename(ctx) + unless File.exists?(filename) + r "Can't find backlog for #{ctx}." + return + end + + r "Searching #{ctx} for \"#{query}\"" + + # TODO: implement a smarter, reverse search, as this can be expensive on very long backlogs. + # TODO: keep some state after a search and let the user get context around one of the results. + result = %x{grep #{query.shellescape} #{filename} | tail -n 10} + if result.empty? + r "No results. Sorry." + else + r "Results:" + r result + end + end + end +end + end diff --git a/lib/tkellem/tkellem_bot.rb b/lib/tkellem/tkellem_bot.rb index 34a3a38..9aeba6e 100644 --- a/lib/tkellem/tkellem_bot.rb +++ b/lib/tkellem/tkellem_bot.rb @@ -6,7 +6,7 @@ module Tkellem class TkellemBot # careful here -- if no user is given, it's assumed the command is running as # an admin - def self.run_command(line, user, &block) + def self.run_command(line, bouncer, &block) args = Shellwords.shellwords(line.downcase) command_name = args.shift.upcase command = commands[command_name] @@ -16,7 +16,7 @@ def self.run_command(line, user, &block) return end - command.run(args, user, block) + command.run(args, bouncer, block) end class Command @@ -58,7 +58,8 @@ def self.admin_only? false end - def self.run(args_arr, user, block) + def self.run(args_arr, bouncer, block) + user = bouncer.user if admin_only? && !admin_user?(user) block.call "You can only run #{name} as an admin." return @@ -67,7 +68,7 @@ def self.run(args_arr, user, block) options.cmd = cmd options.parse!(args_arr) cmd.args[:rest] = args_arr - cmd.execute(cmd.args, user) + cmd.execute(cmd.args, bouncer) rescue ArgumentError => e cmd.respond e.to_s cmd.show_help @@ -98,7 +99,7 @@ def self.admin_user?(user) class Help < Command register 'help' - def execute(args, user) + def execute(args, bouncer) name = args[:rest].first r "**** tkellem help ****" if name.nil? @@ -108,7 +109,7 @@ def execute(args, user) r "The following commands are available:" TkellemBot.commands.keys.sort.each do |name| command = TkellemBot.commands[name] - next if command.admin_only? && user && !user.admin? + next if command.admin_only? && bouncer && bouncer.user && !bouncer.user.admin? r "#{name}#{' ' * (25-name.length)}" end elsif (command = TkellemBot.commands[name.upcase]) @@ -165,13 +166,13 @@ def add(args, user) end end - def execute(args, user) + def execute(args, bouncer) if args['list'] - list(args, user) + list(args, bouncer.user) elsif args['remove'] - remove(args, user) + remove(args, bouncer.user) elsif args['add'] - add(args, user) + add(args, bouncer.user) else raise Command::ArgumentError, "Unknown sub-command" end @@ -230,7 +231,8 @@ class PasswordCommand < Command options.set('username', '--user=username', '-u', 'Change password for other username') - def execute(args, user) + def execute(args, bouncer) + user = bouncer.user if args['username'] if Command.admin_user?(user) user = User.first(:conditions => { :username => args['username'] })