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