]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/message.rb
75427a9eea83a6c7f38a68446651e9b4711f3b32
[user/henk/code/ruby/rbot.git] / lib / rbot / message.rb
1 module Irc
2   BotConfig.register BotConfigArrayValue.new('core.address_prefix',
3     :default => [], :wizard => true,
4     :desc => "what non nick-matching prefixes should the bot respond to as if addressed (e.g !, so that '!foo' is treated like 'rbot: foo')"
5   )
6
7   BotConfig.register BotConfigBooleanValue.new('core.reply_with_nick',
8     :default => false, :wizard => true,
9     :desc => "if true, the bot will prepend the nick to what he has to say when replying (e.g. 'markey: you can't do that!')"
10   )
11
12   BotConfig.register BotConfigStringValue.new('core.nick_postfix',
13     :default => ':', :wizard => true,
14     :desc => "when replying with nick put this character after the nick of the user the bot is replying to"
15   )
16
17   # base user message class, all user messages derive from this
18   # (a user message is defined as having a source hostmask, a target
19   # nick/channel and a message part)
20   class BasicUserMessage
21
22     # associated bot
23     attr_reader :bot
24
25     # associated server
26     attr_reader :server
27
28     # when the message was received
29     attr_reader :time
30
31     # User that originated the message
32     attr_reader :source
33
34     # User/Channel message was sent to
35     attr_reader :target
36
37     # contents of the message
38     attr_accessor :message
39
40     # has the message been replied to/handled by a plugin?
41     attr_accessor :replied
42
43     # instantiate a new Message
44     # bot::      associated bot class
45     # server::   Server where the message took place
46     # source::   User that sent the message
47     # target::   User/Channel is destined for
48     # message::  actual message
49     def initialize(bot, server, source, target, message)
50       @msg_wants_id = false unless defined? @msg_wants_id
51
52       @time = Time.now
53       @bot = bot
54       @source = source
55       @address = false
56       @target = target
57       @message = BasicUserMessage.stripcolour message
58       @replied = false
59       @server = server
60
61       @identified = false
62       if @msg_wants_id && @server.capabilities[:"identify-msg"]
63         if @message =~ /^([-+])(.*)/
64           @identified = ($1=="+")
65           @message = $2
66         else
67           warning "Message does not have identification"
68         end
69       end
70
71       if target && target == @bot.myself
72         @address = true
73       end
74
75     end
76
77     # Access the nick of the source
78     #
79     def sourcenick
80       @source.nick
81     end
82
83     # Access the user@host of the source
84     #
85     def sourceaddress
86       "#{@source.user}@#{@source.host}"
87     end
88
89     # Was the message from an identified user?
90     def identified?
91       return @identified
92     end
93
94     # returns true if the message was addressed to the bot.
95     # This includes any private message to the bot, or any public message
96     # which looks like it's addressed to the bot, e.g. "bot: foo", "bot, foo",
97     # a kick message when bot was kicked etc.
98     def address?
99       return @address
100     end
101
102     # has this message been replied to by a plugin?
103     def replied?
104       return @replied
105     end
106
107     # strip mIRC colour escapes from a string
108     def BasicUserMessage.stripcolour(string)
109       return "" unless string
110       ret = string.gsub(ColorRx, "")
111       #ret.tr!("\x00-\x1f", "")
112       ret
113     end
114
115   end
116
117   # class for handling IRC user messages. Includes some utilities for handling
118   # the message, for example in plugins.
119   # The +message+ member will have any bot addressing "^bot: " removed
120   # (address? will return true in this case)
121   class UserMessage < BasicUserMessage
122
123     # for plugin messages, the name of the plugin invoked by the message
124     attr_reader :plugin
125
126     # for plugin messages, the rest of the message, with the plugin name
127     # removed
128     attr_reader :params
129
130     # convenience member. Who to reply to (i.e. would be sourcenick for a
131     # privately addressed message, or target (the channel) for a publicly
132     # addressed message
133     attr_reader :replyto
134
135     # channel the message was in, nil for privately addressed messages
136     attr_reader :channel
137
138     # for PRIVMSGs, true if the message was a CTCP ACTION (CTCP stuff
139     # will be stripped from the message)
140     attr_reader :action
141
142     # instantiate a new UserMessage
143     # bot::      associated bot class
144     # source::   hostmask of the message source
145     # target::   nick/channel message is destined for
146     # message::  message part
147     def initialize(bot, server, source, target, message)
148       super(bot, server, source, target, message)
149       @target = target
150       @private = false
151       @plugin = nil
152       @action = false
153
154       if target == @bot.myself
155         @private = true
156         @address = true
157         @channel = nil
158         @replyto = source
159       else
160         @replyto = @target
161         @channel = @target
162       end
163
164       # check for option extra addressing prefixes, e.g "|search foo", or
165       # "!version" - first match wins
166       bot.config['core.address_prefix'].each {|mprefix|
167         if @message.gsub!(/^#{Regexp.escape(mprefix)}\s*/, "")
168           @address = true
169           break
170         end
171       }
172
173       # even if they used above prefixes, we allow for silly people who
174       # combine all possible types, e.g. "|rbot: hello", or
175       # "/msg rbot rbot: hello", etc
176       if @message.gsub!(/^\s*#{Regexp.escape(bot.nick)}\s*([:;,>]|\s)\s*/i, "")
177         @address = true
178       end
179
180       if(@message =~ /^\001ACTION\s(.+)\001/)
181         @message = $1
182         @action = true
183       end
184
185       # free splitting for plugins
186       @params = @message.dup
187       if @params.gsub!(/^\s*(\S+)[\s$]*/, "")
188         @plugin = $1.downcase
189         @params = nil unless @params.length > 0
190       end
191     end
192
193     # returns true for private messages, e.g. "/msg bot hello"
194     def private?
195       return @private
196     end
197
198     # returns true if the message was in a channel
199     def public?
200       return !@private
201     end
202
203     def action?
204       return @action
205     end
206
207     # convenience method to reply to a message, useful in plugins. It's the
208     # same as doing:
209     # <tt>@bot.say m.replyto, string</tt>
210     # So if the message is private, it will reply to the user. If it was
211     # in a channel, it will reply in the channel.
212     def plainreply(string, options={})
213       @bot.say @replyto, string, options
214       @replied = true
215     end
216
217     # Same as reply, but when replying in public it adds the nick of the user
218     # the bot is replying to
219     def nickreply(string, options={})
220       extra = self.public? ? "#{@source}#{@bot.config['core.nick_postfix']} " : ""
221       @bot.say @replyto, extra + string, options
222       @replied = true
223     end
224
225     # the default reply style is to nickreply unless the reply already contains
226     # the nick or core.reply_with_nick is set to false
227     #
228     def reply(string, options={})
229       if @bot.config['core.reply_with_nick'] and not string =~ /\b#{@source}\b/
230         return nickreply(string, options)
231       end
232       plainreply(string, options)
233     end
234
235     # convenience method to reply to a message with an action. It's the
236     # same as doing:
237     # <tt>@bot.action m.replyto, string</tt>
238     # So if the message is private, it will reply to the user. If it was
239     # in a channel, it will reply in the channel.
240     def act(string, options={})
241       @bot.action @replyto, string, options
242       @replied = true
243     end
244
245     # convenience method to reply "okay" in the current language to the
246     # message
247     def plainokay
248       self.plainreply @bot.lang.get("okay")
249     end
250
251     # Like the above, but append the username
252     def nickokay
253       str = @bot.lang.get("okay").dup
254       if self.public?
255         # remove final punctuation
256         str.gsub!(/[!,.]$/,"")
257         str += ", #{@source}"
258       end
259       self.plainreply str
260     end
261
262     # the default okay style is the same as the default reply style
263     #
264     def okay
265       if @bot.config['core.reply_with_nick']
266         return nickokay
267       end
268       plainokay
269     end
270
271   end
272
273   # class to manage IRC PRIVMSGs
274   class PrivMessage < UserMessage
275     def initialize(bot, server, source, target, message)
276       @msg_wants_id = true
277       super
278     end
279   end
280
281   # class to manage IRC NOTICEs
282   class NoticeMessage < UserMessage
283     def initialize(bot, server, source, target, message)
284       @msg_wants_id = true
285       super
286     end
287   end
288
289   # class to manage IRC KICKs
290   # +address?+ can be used as a shortcut to see if the bot was kicked,
291   # basically, +target+ was kicked from +channel+ by +source+ with +message+
292   class KickMessage < BasicUserMessage
293     # channel user was kicked from
294     attr_reader :channel
295
296     def initialize(bot, server, source, target, channel, message="")
297       super(bot, server, source, target, message)
298       @channel = channel
299     end
300   end
301
302   # class to pass IRC Nick changes in. @message contains the old nickame,
303   # @sourcenick contains the new one.
304   class NickMessage < BasicUserMessage
305     def initialize(bot, server, source, oldnick, newnick)
306       super(bot, server, source, oldnick, newnick)
307     end
308
309     def oldnick
310       return @target
311     end
312
313     def newnick
314       return @message
315     end
316   end
317
318   class QuitMessage < BasicUserMessage
319     def initialize(bot, server, source, target, message="")
320       super(bot, server, source, target, message)
321     end
322   end
323
324   class TopicMessage < BasicUserMessage
325     # channel topic
326     attr_reader :topic
327     # topic set at (unixtime)
328     attr_reader :timestamp
329     # topic set on channel
330     attr_reader :channel
331
332     def initialize(bot, server, source, channel, topic=ChannelTopic.new)
333       super(bot, server, source, channel, topic.text)
334       @topic = topic
335       @timestamp = topic.set_on
336       @channel = channel
337     end
338   end
339
340   # class to manage channel joins
341   class JoinMessage < BasicUserMessage
342     # channel joined
343     attr_reader :channel
344     def initialize(bot, server, source, channel, message="")
345       super(bot, server, source, channel, message)
346       @channel = channel
347       # in this case sourcenick is the nick that could be the bot
348       @address = (source == @bot.myself)
349     end
350   end
351
352   # class to manage channel parts
353   # same as a join, but can have a message too
354   class PartMessage < JoinMessage
355   end
356 end