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