summaryrefslogtreecommitdiff
path: root/data/rbot/plugins/seen.rb
blob: 8889d26bd1cf8c8ec451327c32cf56d4650cc2a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#-- 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
  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)."))

  def help(plugin, topic="")
    _("seen <nick> => have you seen, or when did you last see <nick>")
  end

  def privmsg(m)
    unless(m.params =~ /^(\S)+$/)
      m.reply "incorrect usage: " + help(m.plugin)
      return
    end

    m.params.gsub!(/\?$/, "")

    if @registry.has_key?(m.params)
      m.reply seen(@registry[m.params])
    else
      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
    now = Time.new
    case m
    when PrivMessage
      return if m.private?
      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?
      store m, Saw.new(m.sourcenick.dup, now, "QUIT",
                       nil, m.message.dup)
    when NickMessage
      return if m.address?
      store m, Saw.new(m.oldnick, now, "NICK", nil, m.newnick)
    when PartMessage
      return if m.address?
      store m, Saw.new(m.sourcenick.dup, Time.new, "PART",
                       m.target.to_s, m.message.dup)
    when JoinMessage
      return if m.address?
      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(reg)
    saw = case reg
    when Struct::Saw
      reg # for backwards compatibility
    when Array
      reg.last
    end

    before = reg.first

    formats = {
      :normal      => _("%{nick} was last seen %{when}, %{doing}"),
      :with_before => _("%{nick} was last seen %{when}, %{doing} and %{time} before %{did_before}")
    }

    if [:PART, :QUIT].include?(saw.type.to_sym) &&
       [:PUBLIC, :ACTION].include?(before.type.to_sym)
      did_before = case before.type.to_sym
      when :PUBLIC
        _("saying \"%{message}\"")
      when :ACTION
        _("doing *%{message}*")
      end % {
        :nick => saw.nick,
        :message => before.message
      }

      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)
      time = _("just now")
    else
      time = _("%{time} ago") % { :time => Utils.secs_to_string(ago) }
    end

    doing = case saw.type.to_sym
    when :PUBLIC
      _("saying \"%{message}\"")
    when :ACTION
      _("doing *%{message}*")
    when :NICK
      _("changing nick from %{nick} to %{message}")
    when :PART
      if saw.message.empty?
        _("leaving %{where}")
      else
        _("leaving %{where} (%{message})")
      end
    when :JOIN
      _("joining %{where}")
    when :QUIT
      _("quitting IRC (%{message})")
    when :TOPIC
      _("changing the topic of %{where} to \"%{message}\"")
    end % { :message => saw.message, :where => saw.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)
    reg = @registry[saw.nick]

    if reg && reg.is_a?(Array)
      reg.shift if reg.size > 1
      reg.push(saw)
    else
      reg = [saw]
    end

    if m.is_a? NickMessage
      @registry[m.newnick] = reg
    end

    @registry[saw.nick] = reg
  end
end
plugin = SeenPlugin.new
plugin.register("seen")