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