X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=data%2Frbot%2Fplugins%2Fseen.rb;h=717721e290d959f63139c5a55bf19066156511c1;hb=3d8bdf551aebdd4fa7ddb10fa8e824232dd4f82b;hp=80d52f6595767540124a5214b1a2e90b35501707;hpb=676dd61e6b0bea5f506d064039a685944aefd6fb;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/data/rbot/plugins/seen.rb b/data/rbot/plugins/seen.rb index 80d52f65..717721e2 100644 --- a/data/rbot/plugins/seen.rb +++ b/data/rbot/plugins/seen.rb @@ -1,27 +1,43 @@ -Saw = Struct.new("Saw", :nick, :time, :type, :where, :message) +#-- vim:sw=2:et +#++ +# +# :title: Seen Plugin +# +# Keep a database of who last said/did what + +define_structure :Saw, :nick, :time, :type, :where, :message class SeenPlugin < Plugin - # turn a number of seconds into a human readable string, e.g - # 2 days, 3 hours, 18 minutes, 10 seconds - def secs_to_string(secs) - ret = "" - days = (secs / (60 * 60 * 24)).to_i - secs = secs % (60 * 60 * 24) - hours = (secs / (60 * 60)).to_i - secs = (secs % (60 * 60)) - mins = (secs / 60).to_i - secs = (secs % 60).to_i - ret += "#{days} days, " if days > 0 - ret += "#{hours} hours, " if hours > 0 || days > 0 - ret += "#{mins} minutes and " if mins > 0 || hours > 0 || days > 0 - ret += "#{secs} seconds" - return ret - end + + MSG_PUBLIC = N_("saying \"%{message}\" in %{where}") + MSG_ACTION = N_("doing *%{nick} %{message}* in %{where}") + MSG_NICK = N_("changing nick from %{nick} to %{message}") + MSG_PART = N_("leaving %{where} (%{message})") + MSG_PART_EMPTY = N_("leaving %{where}") + MSG_JOIN = N_("joining %{where}") + MSG_QUIT = N_("quitting IRC (%{message})") + MSG_TOPIC = N_("changing the topic of %{where} to \"%{message}\"") + + CHANPRIV_CHAN = N_("a private channel") + CHANPRIV_MSG_TOPIC = N_("changing the topic of %{where}") + + MSGPRIV_MSG_PUBLIC = N_("speaking in %{where}") + MSGPRIV_MSG_ACTION = N_("doing something in %{where}") + + FORMAT_NORMAL = N_("%{nick} was last seen %{when}, %{doing}") + FORMAT_WITH_BEFORE = N_("%{nick} was last seen %{when}, %{doing} and %{time} before %{did_before}") + + Config.register Config::IntegerValue.new('seen.max_results', + :default => 3, :validate => Proc.new{|v| v >= 0}, + :desc => _("Maximum number of seen users to return in search (0 = no limit).")) + Config.register Config::ArrayValue.new('seen.ignore_patterns', + :default => [ "^no u$" ], + :desc => _("Strings/regexes that you'd like to ignore for 'last message' purposes")) def help(plugin, topic="") - "seen => have you seen, or when did you last see " + _("seen => have you seen, or when did you last see ") end - + def privmsg(m) unless(m.params =~ /^(\S)+$/) m.reply "incorrect usage: " + help(m.plugin) @@ -33,74 +49,184 @@ class SeenPlugin < Plugin if @registry.has_key?(m.params) m.reply seen(@registry[m.params]) else - m.reply "nope!" + rx = Regexp.new(m.params, true) + num_matched = 0 + @registry.each {|nick, saw| + if nick.match(rx) + m.reply seen(saw) + num_matched += 1 + break if num_matched == @bot.config['seen.max_results'] + end + } + + m.reply _("nope!") if num_matched.zero? end end def listen(m) + return unless m.source # keep database up to date with who last said what - if m.kind_of?(PrivMessage) + now = Time.new + case m + when PrivMessage return if m.private? - if m.action? - @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "ACTION", - m.target, m.message.dup) - else - @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "PUBLIC", - m.target, m.message.dup) - end - elsif m.kind_of?(QuitMessage) - return if m.address? - @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "QUIT", - nil, m.message.dup) - elsif m.kind_of?(NickMessage) + @bot.config['seen.ignore_patterns'].each { |regex| + return if m.message =~ /#{regex}/ + } + + type = m.action? ? 'ACTION' : 'PUBLIC' + store m, Saw.new(m.sourcenick.dup, now, type, + m.target.to_s, m.message.dup) + when QuitMessage return if m.address? - @registry[m.message] = Saw.new(m.sourcenick.dup, Time.new, "NICK", - nil, m.message.dup) - @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "NICK", - nil, m.message.dup) - elsif m.kind_of?(PartMessage) + store m, Saw.new(m.sourcenick.dup, now, "QUIT", + nil, m.message.dup) + when NickMessage return if m.address? - @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "PART", - m.target, m.message.dup) - elsif m.kind_of?(JoinMessage) + store m, Saw.new(m.oldnick, now, "NICK", nil, m.newnick) + when PartMessage return if m.address? - @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "JOIN", - m.target, m.message.dup) - elsif m.kind_of?(TopicMessage) + store m, Saw.new(m.sourcenick.dup, Time.new, "PART", + m.target.to_s, m.message.dup) + when JoinMessage return if m.address? - @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "TOPIC", - m.target, m.message.dup) + store m, Saw.new(m.sourcenick.dup, Time.new, "JOIN", + m.target.to_s, m.message.dup) + when TopicMessage + return if m.address? or m.info_or_set == :info + store m, Saw.new(m.sourcenick.dup, Time.new, "TOPIC", + m.target.to_s, m.message.dup) end end - - def seen(saw) - ret = "#{saw.nick} was last seen " + + def seen(reg) + saw = case reg + when Struct::Saw + reg # for backwards compatibility + when Array + reg.last + end + + if reg.kind_of? Array + before = reg.first + end + + # TODO: a message should not be disclosed if: + # - it was said in a channel that was/is invite-only, private or secret + # - UNLESS the requester is also in the channel now, or the request is made + # in the channel? + msg_privacy = false + # TODO: a channel or it's topic should not be disclosed if: + # - the channel was/is private or secret + # - UNLESS the requester is also in the channel now, or the request is made + # in the channel? + chan_privacy = false + + # What should be displayed for channel? + where = chan_privacy ? _(CHANPRIV_CHAN) : saw.where + + formats = { + :normal => _(FORMAT_NORMAL), + :with_before => _(FORMAT_WITH_BEFORE) + } + + if before && [:PART, :QUIT].include?(saw.type.to_sym) && + [:PUBLIC, :ACTION].include?(before.type.to_sym) + # TODO see chan_privacy + prev_chan_privacy = false + prev_where = prev_chan_privacy ? _(CHANPRIV_CHAN) : before.where + did_before = case before.type.to_sym + when :PUBLIC + _(msg_privacy ? MSGPRIV_MSG_PUBLIC : MSG_PUBLIC) + when :ACTION + _(msg_privacy ? MSGPRIV_MSG_ACTION : MSG_ACTION) + end % { + :nick => saw.nick, + :message => before.message, + :where => prev_where + } + + format = :with_before + + time_diff = saw.time - before.time + if time_diff < 300 + time_before = _("a moment") + elsif time_diff < 3600 + time_before = _("a while") + else + format = :normal + end + else + format = :normal + end + + nick = saw.nick ago = Time.new - saw.time - + if (ago.to_i == 0) - ret += "just now, " + time = _("just now") + else + time = _("%{time} ago") % { :time => Utils.secs_to_string(ago) } + end + + doing = case saw.type.to_sym + when :PUBLIC + _(msg_privacy ? MSGPRIV_MSG_PUBLIC : MSG_PUBLIC) + when :ACTION + _(msg_privacy ? MSGPRIV_MSG_ACTION : MSG_ACTION) + when :NICK + _(MSG_NICK) + when :PART + if saw.message.empty? + _(MSG_PART_EMPTY) + else + _(MSG_PART) + end + when :JOIN + _(MSG_JOIN) + when :QUIT + _(MSG_QUIT) + when :TOPIC + _(chan_privacy ? CHANPRIV_MSG_TOPIC : MSG_TOPIC) + end % { :message => saw.message, :where => where, :nick => saw.nick } + + case format + when :normal + formats[:normal] % { + :nick => saw.nick, + :when => time, + :doing => doing, + } + when :with_before + formats[:with_before] % { + :nick => saw.nick, + :when => time, + :doing => doing, + :time => time_before, + :did_before => did_before + } + end + end + + def store(m, saw) + # TODO: we need to store the channel state INVITE/SECRET/PRIVATE here, in + # some symbolic form, so that we know the prior state of the channel when + # it comes time to display. + reg = @registry[saw.nick] + + if reg && reg.is_a?(Array) + reg.shift if reg.size > 1 + reg.push(saw) else - ret += secs_to_string(ago) + " ago, " + reg = [saw] end - case saw.type - when "PUBLIC" - ret += "saying #{saw.message}" - when "ACTION" - ret += "doing #{saw.nick} #{saw.message}" - when "NICK" - ret += "changing nick from #{saw.nick} to #{saw.message}" - when "PART" - ret += "leaving #{saw.where}" - when "JOIN" - ret += "joining #{saw.where}" - when "QUIT" - ret += "quiting IRC (#{saw.message})" - when "TOPIC" - ret += "changing the topic of #{saw.where} to #{saw.message}" + if m.is_a? NickMessage + @registry[m.newnick] = reg end + + @registry[saw.nick] = reg end - end plugin = SeenPlugin.new plugin.register("seen")