· 9 years ago · May 06, 2017, 09:40 AM
1require "IRCHelp"
2require "IRCConnection"
3require "rfc2812"
4require "irc_numerics"
5#require "IRCInfoHolder"
6require "observer"
7require "rexml/document"
8require "IRCChannel"
9require "IRCModes"
10require "Mktree"
11require "IRCUserManager"
12#this class will maintain a single IRC session with a specified network.
13#Use IRCManager in order to create a session, rather than using solitary
14#IRCSession objects.
15class IRCSession < IRCConnection
16 @@debug = 2000
17 STATUS_DISCONNECTED = 0
18 STATUS_CONNECTING = 1
19 STATUS_CONNECTED = 2
20 include RFC2812
21 include Observable
22 include IRCModes
23 include Mktree
24 include IRCHelp
25 #This method is supplied with a pre-chewed
26 #REXML document object and a network
27 #name. If the network exists in the
28 #document, the session runs through a
29 #startup procedure. If not, the session
30 #object raises an error, and should be
31 #discarded at that point.
32 #=
33 #=
34 #Once started, two threads will handle input
35 #and output.
36 #=
37 #=
38 #tba: The session object should bar access
39 #to methods, and/or set an accessor to
40 #return a known constant if the connection
41 #is currently unavailable. Connections
42 #should have the capability to be
43 #re-initialized, maybe on a different
44 #server within the same group.
45 def initialize(network, conf_doc, rejoin=nil)
46 super()
47 @rejoin = rejoin #Auto rejoin after kicks if non-nil.
48 @local_rejoins = {} #This hash will store true/nil values
49 #indexed by downcased/stripped
50 #channel name.
51 @user_mgr = IRCUserManager.instance #IRCUser objects will
52 #work to handle some
53 #user-specific
54 #information and
55 #processing.
56 STDERR.puts "#{self.class}:#{self} user manager object: #{@user_mgr}"
57 @doc = conf_doc.elements["*/networks/"]
58 @addr = nil
59 @port = nil
60 @nick = nil
61 @realname = nil
62 @userid = nil
63 @pass = nil
64 @ns_pass = nil #Nickserv password. Will probably remove the
65 #current nickserv auth system in favour of a
66 #<insert service name here> authentication
67 #system.
68 @doc.elements.each{|e|
69 puts e
70 }
71 @prefixes = ["@+", "ov"] #Prefixes are used when parsing
72 #channel information to quickly
73 #decide who is an op or not.
74 #The 005 info message will be parsed
75 #and used to rewrite this variable.
76 @current_network = nil
77 @status = STATUS_DISCONNECTED
78 @current_server = nil
79 @quit_msg = "\0035IRCSession v0.01a\017 (closing link)"
80 STDERR.puts "#{self.class}:#{self} Loading details for #{network}"
81 count = 0
82 working_doc = nil
83 @doc.elements.each{|element|
84 STDERR.puts "#{self.class}:#{self} scanning element: #{element}"
85 STDERR.puts "#{self.class}:#{self} #{element.attributes['name']}"
86 if /^#{network}$/i.match(element.attributes['name'].strip) then
87 STDERR.puts "#{self.class}:#{self} matched!"
88 count += 1
89 STDERR.puts "#{self.class}:attributes: #{@doc.attributes}"
90 @current_network = element.attributes['name'].strip
91 @nick = element.attributes['nick']
92 @userid = element.attributes['userid']
93 @realname = element.attributes['realname']
94 @ns_pass = element.attributes['nickserv']
95 STDERR.puts "#{self.class}:getting servers document" if @@debug < 1
96 working_doc = element
97 STDERR.puts "#{self.class}:working_doc is: #{working_doc}" if @@debug < 1
98 STDERR.puts "#{self.class}:breaking out" if @@debug < 1
99 break
100 end
101 if count > 0 then
102 STDERR.puts "#{self.class}:breaking out." if @@debug < 1
103 break
104 end
105 }
106 if count < 1 then
107 raise "#{self.class}: initialize: Could not find entry for network #{network}: #{$!}"
108 end
109 @server_list = []
110 working_doc.elements.each{|e|
111 STDERR.puts "#{self.class}:server: #{e}" if @@debug < 1
112 @server_list.push(e)
113 }
114
115
116 begin
117 log_in(network)
118 rescue
119 raise "#{self.class}: initialize: Failed to log into network #{network}: #{$!}"
120 end
121 #connect(@addr,@port,@nick,@realname,@userid,pass)
122 #@evcap = IRCEventCapturer.new(self)
123 #@info = IRCInfoHolder.new
124 @motd = ""
125 @motdwritten = 0
126 @motdwriting = 0
127 @writingnames = []
128 @statusbuffer = []
129 @statusbuffersize = 500
130
131 #known_users hash table contains IRCChannel objects referenced
132 #by channel name.
133 @known_channels = Hash.new
134 #known_users hash table contains mode strings referenced by
135 #nickname.
136 @known_users = Hash.new
137 @anotherextendedinputthread = Thread.new {
138 while (true == true)
139 @inputvalid = nil
140 @input = getline
141 irc_parsed = irc_parse(@input)
142 if irc_parsed then
143 STDERR.puts "#{self.class}:#{self.class}: irc_parsed[1] = #{irc_parsed[1]}" if @@debug < 1
144 if irc_parsed[1] == RPL_WELCOME then
145 STDERR.puts "#{self.class}:#{self} RPL_WELCOME intercepted." if @@debug < 5
146 @status = STATUS_CONNECTED
147 STDERR.puts "#{self.class}:#{self} Sending login details to Nickserv." if @@debug < 5
148 cmd_privmsg("NickServ", "IDENTIFY #{@ns_pass}")
149 STDERR.puts "#{self.class}:#{self} done!" if @@debug < 5
150 STDERR.puts "#{self.class}:#{self} status is CONNECTED." if @@debug < 5
151 end
152 STDERR.puts "#{self.class}:#{self.class}: welcome match passed" if @@debug < 5
153 if @@debug < 1 then
154 STDERR.puts "#{self.class}:#{self.class}: irc_parsed contents:" if @@debug < 1
155 irc_parsed.each{|el|
156 STDERR.puts "#{self.class}:el: #{el}"
157 STDERR.puts "#{self.class}:el class: #{el.class}"
158 }
159 STDERR.puts "#{self.class}:#{self.class}: current network: #{@current_network}"
160 end
161 changed
162 STDERR.puts "#{self.class}:#{self.class}: changed" if @@debug < 1
163 notify_observers(irc_parsed[0], irc_parsed[1], irc_parsed[2], @current_network)
164 STDERR.puts "#{self.class}:#{self.class}: calling info parser" if @@debug < 1
165 _info_parser(irc_parsed[0], irc_parsed[1], irc_parsed[2])
166 STDERR.puts "#{self.class}:#{self.class}: info parser called" if @@debug < 1
167 else
168 sleep 0.1
169 end
170 end
171 }
172 #@anotherextendedinputthread.join
173 end
174
175 #this method will return true if the session is currently connected.
176 def is_active?
177 if @status == STATUS_CONNECTED then
178 return true
179 else
180 return nil
181 end
182 end
183
184 #this method will set the bot mute on a specific channel.
185 def mute(chan)
186 return set_mute(chan)
187 end
188
189 #this method will unset the bot mute on a specified channel.
190 def unmute(chan)
191 return set_mute(chan, 1)
192 end
193 def set_mute(chan, value)
194 dchan = chan.downcase
195 if !@known_channels.has_key?(dchan) then
196 return nil
197 end
198 @known_channels[dchan].set_mute(value)
199 return true
200 end
201
202 #this method will return true or false depending on whether
203 #the session is currently listening to the specified channel.
204 def in_channel?(chan)
205 dchan = chan.downcase
206 if @known_channels.has_key?(dchan) then
207 if !@known_channels[dchan].joined then
208 return nil
209 end
210 if nick != nil then
211 if !@known_channels[dchan].is_here?(nick) then
212 return nil
213 end
214 end
215 return true
216 else
217 return nil
218 end
219 return nil
220 end
221
222 #this method sends a raw IRC message, with trailing cr/lf attached.
223 def cmd_raw(msg)
224 STDERR.puts "#{self.class}:intercepted message." if @@debug < 1
225 msg = /^(.*)[\n\r]?$/.match(msg)[1]
226 print "sending: #{msg}\n\r" if @@debug < 1
227 putline("#{msg}\r\n")
228 end
229
230 #This method will send an INVITE message to the intended target, to join the specified channel.
231 def cmd_invite(target, channel)
232 begin
233 output = "INVITE #{target.strip} #{channel.strip}\n\r"
234 STDERR.puts "#{self.class}:sending: #{output}" if @@debug < 3000
235 putline(output)
236 return true
237 rescue
238 STDERR.puts "#{self.class}:#{self.class}: cmd_privmsg: failure: #{$!}" if @@debug < 5
239 return nil
240 end
241 end
242
243 #this method sends a PRIVMSG message to a specified target.
244 def cmd_privmsg(target, msg)
245 begin
246 output = "PRIVMSG #{target} :#{msg.strip}\n\r"
247 STDERR.puts "#{self.class}:sending: #{output}" if @@debug < 1
248 putline(output)
249 return true
250 rescue
251 STDERR.puts "#{self.class}:#{self.class}: cmd_privmsg: failure: #{$!}" if @@debug < 5
252 return nil
253 end
254 end
255
256 #this method sends a CTCP ACTION message to a specified target.
257 def cmd_action(target, msg)
258 output = "\000PRIVMSG #{target} :#{msg.strip}\000\r\n"
259 STDERR.puts "#{self.class}:#{self.class}: sending: #{output}" if @@debug < 1
260 putline(output)
261 end
262
263 #this method lists all visible IRC nicks/users.
264 def cmd_list_users
265 output = {}
266 @known_channels.each{|k, v|
267 output[k] = []
268 v.users.each{|u|
269 STDERR.puts "#{self.class}:#{self.class}: channel #{k}: #{u}" if @@debug < 1
270 output[k].push u
271 }
272 }
273 return output
274 end
275
276 #this method lists all known channels.
277 def cmd_list_chans
278 output = []
279 @known_channels.each{|k, v|
280 output.push k
281 }
282 if output.length > 0 then
283 return output
284 end
285 return nil
286 end
287
288 #this method lists all IRC nicks in a specified channel
289 def cmd_list_chan_users(chan)
290 STDERR.puts "#{self.class}:#{self.class}: returning info for channel #{chan}" if @@debug < 2
291 dchan = chan.downcase
292 if @known_channels.has_key?(dchan) then
293 STDERR.puts "#{self.class}:#{self.class}: channel is known." if @@debug < 2
294 out = []
295 @known_channels[dchan].users.each{|k, v|
296 STDERR.puts "#{self.class}:#{self.class}: pushing: [#{v.dup.join(',')}][0]" if @@debug < 2
297 out.push v[0]
298 }
299 return out
300 end
301 return nil
302 end
303
304 #this method gets the current status feed for the session.
305 def cmd_get_status
306 if (@statusbuffer.length > 0) then
307 tmp = @statusbuffer.shift
308 STDERR.puts "#{self.class}:#{self.class}: getting value: #{tmp}" if @@debug < 1
309 return tmp
310 else
311 #puts "<statusgets@ExtendedIRCConnection> no value to get."
312 return nil
313 end
314 end
315
316 #this method sends a JOIN command with the specified channel as a parameter.
317 def cmd_join_chan(chan)
318 puts "#{self.class}: cmd_join_chan triggered for channel #{chan}" if @@debug < 10000
319 cmd = "JOIN :#{chan}\r\n"
320 putline(cmd)
321 return true
322 end
323
324 #this method sends a PART command with the specified channel as a parameter.
325 def cmd_part_chan(chan)
326 cmd = "PART :#{chan}\r\n"
327 putline(cmd)
328 return true
329 end
330
331 #this method will disconnect from the server and close the session.
332 def cmd_quit(message=@quit_msg)
333 disconnect!(message)
334 @status = STATUS_DISCONNECTED
335 end
336
337 #this method will send a WHOIS command.
338 def cmd_whois(nick, target=nil)
339 if target == nil then
340 cmd = "WHOIS #{nick}\r\n"
341 else
342 cmd = "WHOIS #{target} #{nick}\r\n"
343 end
344 putline(cmd)
345 end
346
347 def rejoin
348 return @rejoin
349 end
350 def rejoin=(i)
351 if i then
352 @rejoin = true
353 else
354 @rejoin = nil
355 end
356 return @rejoin
357 end
358 def cmd_set_rejoin(chan, value=true)
359 v = nil
360 if value then
361 v = true
362 end
363 dchan = chan.downcase.strip
364 @local_rejoins[dchan] = v
365 puts "#{self.class}: cmd_set_rejoin: local rejoins set: #{dchan} = '#{v}'" if @@debug < 10000
366 return v
367 end
368 def cmd_unset_rejoin(chan)
369 @local_rejoins.delete(chan.downcase.strip)
370 end
371
372 def cmd_whowas(nick, num=nil, target=nil)
373 end
374 def cmd_who(text, target=nil)
375 end
376 def cmd_squery(service, text)
377 end
378
379 #this method will log into a specified network
380 def log_in(network)
381 STDERR.puts "#{self.class}:#{self.class}: attempting log in sequence for #{network}" if @@debug < 1
382 if @status == STATUS_DISCONNECTED then
383 @status = STATUS_CONNECTING
384 count = 0
385 begin
386 e = @server_list[count]
387 if @@debug < 1 then
388 STDERR.puts "#{self.class}:#{self.object_id}: e.class: #{e.class}"
389 STDERR.puts "#{self.class}:#{self.object_id}: e contents follow..."
390 e.attributes.each{|k, v| STDERR.puts "#{self.class}:#{k} => #{v}"}
391 end
392 @current_server = e.attributes['name']
393 STDERR.puts "#{self.class}:#{self.object_id}: Opening connection to #{@current_server}"
394 @addr = e.attributes['host']
395 @port = e.attributes['port']
396 STDERR.puts "#{self.class}:#{self.object_id}: #{@addr}:#{@port}"
397 @known_channels = Hash.new
398 if @@debug < 1 then
399 puts @nick
400 puts @userid
401 puts @realname
402 puts @ns_pass
403 end
404 connect(@addr, @port, @nick, @userid, @realname, nil, @ns_pass)
405 STDERR.puts "#{self.class}:#{self.class}: starting up new network entry for #{network}" if @@debug < 1
406 @user_mgr.new_network(network)
407 rescue
408 STDERR.puts "#{self.class}:#{self.class}: Connection attempt failed: #{$!}"
409 STDERR.puts "#{self.class}:#{self.class}: backtrace follows..."
410 prefix = ""
411 $!.backtrace.each{|tline|
412 STDERR.puts "#{self.class}:#{prefix}#{tline}"
413 }
414 count += 1
415 if count > @server_list.length then
416 count = 0
417 end
418 retry
419 end
420 @status = STATUS_DISCONNECTED
421 else
422 return nil
423 end
424 end
425
426 #this method will return true if the specified nick is an operator.
427 def is_op?(chan, nick)
428 STDERR.puts "#{self.class}:#{self.class}: #{@current_network}: checking #{chan} for ops on #{nick}" if @@debug < 1
429 dchan = chan.downcase
430 if @known_channels.has_key?(dchan) then
431 STDERR.puts "#{self.class}:#{self.class}: #{@current_network}: channel exists" if @@debug < 1
432 return @known_channels[dchan].is_op?(nick)
433 end
434 return nil
435 end
436
437 attr_reader(:writingnames, :known_channels, :nick)
438 attr_accessor(:info)
439 protected
440 def statusputs(val)
441 #puts "<statusputs@ExtendedIRCConnection> Triggered"
442 if (@statusbuffer.length <= @statusbuffersize) then
443 #puts "<statusputs@ExtendedIRCConnection> putting value #{val}"
444 @statusbuffer.push(val)
445 return nil
446 else
447 #puts "<statusputs@ExtendedIRCConnection> shifting and putting value #{val}"
448 tmp = @statusbuffer.shift
449 @statusbuffer.push(val)
450 return val
451 end
452 end
453
454 private
455 def _info_parser(prefix, cmd, params)
456 #changed
457 #notify_observers(prefix, cmd, params, @current_network)
458 begin
459 case cmd
460 when "PRIVMSG"
461 STDERR.puts "#{self.class}:#{self.class}: PRIVMSG" if @@debug < 2001
462 _event_privmsg(prefix, cmd, params)
463 when "NICK"
464 STDERR.puts "#{self.class}:NICK" if @@debug < 2001
465 _event_nick(prefix, cmd, params)
466 when "JOIN"
467 STDERR.puts "#{self.class}:JOIN" if @@debug < 2001
468 _event_join(prefix, cmd, params)
469 when "PART"
470 STDERR.puts "#{self.class}:PART" if @@debug < 2001
471 _event_part(prefix, cmd, params)
472 when "QUIT"
473 STDERR.puts "#{self.class}:QUIT" if @@debug < 2001
474 _event_quit(prefix, cmd, params)
475 when "MODE"
476 STDERR.puts "#{self.class}:MODE" if @@debug < 2001
477 _event_mode(prefix, cmd, params)
478 when "KICK"
479 STDERR.puts "#{self.class}:KICK" if @@debug < 3001
480 _event_kick(prefix, cmd, params)
481 when RPL_NAMREPLY
482 STDERR.puts "#{self.class}:RPL_NAMREPLY" if @@debug < 2001
483 _event_rpl_namreply(prefix, cmd, params)
484 when RPL_ENDOFNAMES
485 STDERR.puts "#{self.class}:RPL_ENDOFNAMES" if @@debug < 2001
486 _event_rpl_endofnames(prefix, cmd, params)
487 when RPL_BOUNCE
488 STDERR.puts "#{self.class}:RPL_BOUNCE" if @@debug < 2001
489 _event_rpl_bounce(prefix, cmd, params)
490 when RPL_NICKSERV_AUTHED
491 STDERR.puts "#{self.class}:RPL_NICKSERV_AUTHED" if @@debug < 2001
492 _event_rpl_nickserv_authed(prefix, cmd, params)
493 when RPL_WHOISUSER
494 STDERR.puts "#{self.class}:RPL_WHOISUSER" if @@debug < 2001
495 when RPL_WHOISSERVER
496 STDERR.puts "#{self.class}:RPL_WHOISSERVER" if @@debug < 2001
497 when RPL_WHOISOPERATOR
498 STDERR.puts "#{self.class}:RPL_WHOISOPERATOR" if @@debug < 2001
499 when RPL_WHOISCHANNELS
500 STDERR.puts "#{self.class}: RPL_WHOISCHANNELS" if @@debug < 2001
501 when RPL_WHOISIDLE
502 STDERR.puts "#{self.class}: RPL_WHOISIDLE" if @@debug < 2001
503 when RPL_ENDOFWHOIS
504 STDERR.puts "#{self.class}: RPL_ENDOFWHOIS" if @@debug < 2001
505 end
506 rescue
507 if @@debug < 4000 then
508 STDERR.puts "#{self.class}: Error in #_info_parser: #{$!}"
509 STDERR.puts "#{self.class}: Backtrace follows..."
510 pre = ""
511 $!.backtrace.each{|tr|
512 puts "#{self.class}: #{pre}#{tr}"
513 pre << " "
514 }
515 end
516 end
517 end
518 def _event_rpl_whoisuser(prefix, cmd, params)
519
520 end
521 def _event_privmsg(prefix, cmd, params)
522 prematch = /^\:(.*)/.match(prefix)
523 if prematch then
524 prefix = prematch[1]
525 end
526 @user_mgr.new_user(@current_network, prefix)
527 return true
528 end
529 #This method needs to be improved so it will only rejoin if the kick
530 #target is the bot's current nick/mask.
531 def _event_kick(prefix, cmd, params)
532 STDERR.puts "#{self.class}:#{self} event KICK triggered." if @@debug < 10000
533 pmatch = /^(.*)\:(.*)$/.match(params)
534 STDERR.puts "#{self.class}:#{self} kick pmatch[1]: #{pmatch[1]}" if @@debug < 10000
535 chan, nick = pmatch[1].strip.downcase.split(/\s/)
536 #dnick = nick.tr!("^ -~", "")
537 #dchan = chan.tr!("^ -~", "")
538 dnick = nick.strip
539 dchan = chan.strip
540 if @rejoin then
541 puts "#{self.class}: _event_kick: @rejoin is non-nil." if @@debug < 10000
542 cmd_join_chan(dchan) if @local_rejoins[dchan] == nil
543 else
544 puts "#{self.class}: _event_kick: @rejoin is nil." if @@debug < 10000
545 puts "#{self.class}: local rejoins for #{dchan} is '#{@local_rejoins[dchan]}'"
546 if @local_rejoins[dchan] != nil
547 puts "#{self.class}: _event_kick: local join enabled. Joining channel #{dchan}." if @@debug < 10000
548 cmd_join_chan(dchan)
549 end
550 end
551 begin
552 return @known_channels[dchan].del_user(dnick)
553 rescue
554 STDERR.puts "#{self.class}:#{self} Error removing user: #{$!}" if @@debug < 5
555 #raise "#{self.class}: _event_kick: #{$!}"
556 return nil
557 end
558 end
559 #Updates user or channel mode strings.
560 def _event_mode(prefix, cmd, params)
561 STDERR.puts "#{self.class}:#{self} event MODE triggered." if @@debug < 1
562 p_prefix = irc_parse_prefix(prefix)
563 prefix = /^\:?(.*)$/.match(prefix)[1]
564 STDERR.puts "#{self.class}:#{self} prefix trimmed to: #{prefix}" if @@debug < 1
565 if p_prefix then
566 begin
567 user = nil
568 type = irc_obj_type(prefix)
569 p_params = nil
570 nicklist = nil
571 case type[0]
572 when IRC_TYPE_NICK
573 STDERR.puts "#{self.class}:#{self} parsing parameters as USER MODE message" if @@debug < 1
574 p_params = irc_parse_user_mode_params(params)
575 when IRC_TYPE_MASK
576 STDERR.puts "#{self.class}:#{self} parsing parameters as CHANNEL MODE message" if @@debug < 1
577 p_params = irc_parse_chan_mode_params(params)
578 modes = p_params[1].strip
579 list = p_params[2]
580 chan = p_params[0].strip
581 dchan = chan.downcase
582 nicklist = irc_calc_chan_modes(list, modes)
583 if nicklist then
584 STDERR.puts "#{self.class}:#{self} nick list:" if @@debug < 1
585 nicklist.each{|k, v|
586
587 STDERR.puts "#{self.class}:#{self} #{k}: #{v}" if @@debug < 1
588 @user_mgr.new_user(@current_network, prefix)
589 if k != nil then
590 t_user = @user_mgr.get_user(@current_network, k.strip)
591 if t_user != nil then
592 #user = t_user.strip
593 t_user.put_message(prefix, cmd, params, @current_network)
594 #user.mod_chan_flags(chan, v)
595 @known_channels[dchan].set_user_mode(k, v)
596 STDERR.puts "#{self.class}:#{self} user modified. New channel modes: #{@known_channels[dchan].get_user_modes(k)}" if @@debug < 1
597 end
598 end
599 }
600 return true
601 else
602 STDERR.puts "#{self.class}:#{self} unable to parse nick list" if @@debug < 1
603 return nil
604 end
605 else
606 STDERR.puts "#{self.class}:type: #{type[0]}" if @@debug < 1
607 end
608
609 STDERR.puts "#{self.class}:params parsed." if @@debug < 1
610
611 if p_params then
612 if type[0] == IRC_TYPE_NICK then
613 STDERR.puts "#{self.class}:#{self} parsing user mode params for #{p_params[0]}" if @@debug < 1
614 else
615 STDERR.puts "#{self.class}:#{self} parsing channel mode params for #{p_params[0]}" if @@debug < 1
616 end
617 else
618 STDERR.puts "#{self.class}:#{self} invalid parameters." if @@debug < 1
619 end
620 rescue
621 STDERR.puts "#{self.class}:poo: #{$!}" if @@debug < 5
622 STDERR.puts "#{self.class}:#{self.class}: backtrace follows"
623 pre = ""
624 $!.backtrace.each{|tr|
625 STDERR.puts "#{self.class}:#{pre}#{tr}"
626 pre << " "
627 }
628 return nil
629 end
630 else
631 STDERR.puts "#{self.class}:#{self} invalid prefix." if @@debug < 1
632 end
633 end
634 def _event_nick(prefix, cmd, params)
635 STDERR.puts "#{self.class}:#{self} event NICK triggered." if @@debug < 1
636 p_prefix = irc_parse_prefix(prefix)
637 newnick = /^\:?(.*)$/.match(params.strip)[1]
638 STDERR.puts "#{self.class}:#{self} #{p_prefix[0]}'s nick is now #{newnick}" if @@debug < 1
639 if /#{newnick}/i.match(@nick) then
640 STDERR.puts "#{self.class}:nick is own nick" if @@debug < 1
641 @nick = newnick
642 end
643 STDERR.puts "#{self.class}:#{self.class}: getting/creating new user" if @@debug < 1
644 begin
645 @user_mgr.new_user(@current_network, prefix)
646 @user_mgr.get_user(@current_network, p_prefix[0]).put_message(prefix, cmd, params, @current_network)
647 #@user_mgr.get_user!(@current_network, p_prefix[0]).put_message(prefix, cmd, params, @current_network)
648 rescue
649 STDERR.puts "#{self.class}:#{self.class}: ERROR IN NICK CHANGE: #{$!}: backtrace follows"
650 pre = ""
651 $!.backtrace.each{|tr|
652 STDERR.puts "#{self.class}:#{pre}#{tr}"
653 }
654 end
655 STDERR.puts "#{self.class}:#{self.class}: new user got/created" if @@debug < 1
656 #@user_mgr.deactivate_user(@current_network, p_prefix[0])
657 #puts "#{self} old nick deactivated."
658 #newuser = @user_mgr.get_user!(@current_network, newnick)
659 #newuser.put_message(prefix, cmd, params, @current_network)
660 #puts "#{self} new user entry set."
661 #@user_mgr.activate_user(@current_network, newnick)
662 #puts "#{self} entry activated."
663 @user_mgr.get_user(@current_network, p_prefix[0]).name = newnick
664 STDERR.puts "#{self.class}:#{self.class}: user entry changed to #{newnick}" if @@debug < 1
665 @known_channels.each{|key, chan|
666 STDERR.puts "#{self.class}:#{self} examining channel #{chan.name}" if @@debug < 1
667 chan.nick_change(p_prefix[0], newnick)
668 }
669 end
670 def _event_join(prefix, cmd, params)
671 STDERR.puts "#{self.class}:#{self} _event_join triggered"
672 p_prefix = irc_parse_prefix(prefix)
673 if p_prefix then
674 if p_prefix.length > 1 then
675 begin
676 param_match = /^\:?(\S*)/.match(params.strip)[1]
677 STDERR.puts "#{self.class}:#{self} params matched" if @@debug < 2
678 rescue
679 STDERR.puts "#{self.class}:#{self} invalid parameter" if @@debug < 2
680 return nil
681 end
682 nick = p_prefix[0]
683 STDERR.puts "#{self.class}:#{self} nick detected." if @@debug < 2
684 chan = param_match
685 STDERR.puts "#{self.class}:#{self} chan detected: #{chan}!" if @@debug < 2
686 dchan = chan.downcase.strip
687 dchan.tr!("^ -~", "")
688 @user_mgr.new_user(@current_network, prefix)
689 a = @user_mgr.get_user(@current_network, nick)
690 a.put_message(prefix, cmd, params, @current_network)
691 STDERR.puts "#{self.class}:#{self} user got from manager." if @@debug < 2
692 STDERR.puts "#{self.class}:#{self} event_join: user #{p_prefix[0]} is joining channel #{chan} with entry name: #{dchan}!" if @@debug < 2
693 if !@known_channels.has_key?(dchan) then
694 STDERR.puts "#{self.class}:#{self} Creating record for channel #{dchan}" if @@debug < 2
695 @known_channels[dchan] = IRCChannel.new(chan)
696 else
697 @known_channels[dchan].join
698 end
699 @known_channels[dchan].add_user(nick)
700 if @@debug < 2 then
701 STDERR.puts "#{self.class}:#{self} new channel list:"
702 @known_channels.each{|k, v|
703 puts v.name
704 if v.users.length > 0 then
705 STDERR.puts "#{self.class}: user list for #{v.name}"
706 v.users.each{|e|
707 STDERR.puts "#{self.class}: #{e}"
708 }
709 end
710 }
711 end
712 else
713 STDERR.puts "#{self.class}:#{self} invalid prefix type in _event_join" if @@debug < 2
714 return nil
715 end
716 else
717 return nil
718 end
719 end
720 def _event_part(prefix, cmd, params)
721 parsed_prefix = irc_parse_prefix(prefix)
722 if parsed_prefix then
723 if parsed_prefix.length > 1 then
724 if /\:/.match(params.strip) then
725 chan, trailing = /^([^\:]*)\:(.*)$/.match(params.strip)[1..2]
726 else
727 chan = params.strip
728 end
729 if chan then
730 dchan = chan.downcase.strip
731 dchan.tr!("^ -~", "")
732 nick = parsed_prefix[0]
733 dnick = nick.downcase.strip
734 dnick.tr!("^ -~", "")
735 STDERR.puts "#{self.class}:#{self} user #{parsed_prefix[0]} parting from channel #{chan}" if @@debug < 2
736 begin
737 if !@known_channels.has_key?(dchan) then
738 STDERR.puts "#{self.class}:#{self} Creating record for channel #{chan}" if @@debug < 2
739 @known_channels[dchan] = IRCChannel.new(chan)
740 end
741 rescue
742 STDERR.puts "#{self.class}:#{self} oops: #{$!}" if @@debug < 5
743 end
744 STDERR.puts "#{self.class}:#{self} removing user #{parsed_prefix[0]} from channel list #{dchan}" if @@debug < 2
745 STDERR.puts "#{self.class}:#{self.class}: object class is: #{parsed_prefix[0].class}" if @@debug < 2
746 begin
747 @known_channels[dchan].del_user(parsed_prefix[0].to_s)
748 rescue
749 STDERR.puts "#{self.class}:#{self} Error removing user: #{$!}" if @@debug < 5
750 raise "#{self.class}: _event_part: #{$!}"
751 end
752 if @@debug < 2 then
753 STDERR.puts "#{self.class}:#{self} User removed. New channel list:"
754 STDERR.puts "#{self.class}:#{self} new channel list:"
755 @known_channels.each{|k, v|
756 puts v.name
757 if v.users.length > 0 then
758 STDERR.puts "#{self.class}: user list for #{v.name}"
759 v.users.each{|e|
760 STDERR.puts "#{self.class}: #{e}"
761 }
762 end
763 }
764 end
765 @user_mgr.new_user(@current_network, prefix)
766 a = @user_mgr.get_user(@current_network, parsed_prefix[0])
767 #puts "#{self} removing channel entry from IRCUser object"
768 #@user_mgr.get_user!(@current_network, parsed_prefix[0]).del_chan(dchan)
769 #puts "#{self} entry removed!"
770 else
771 STDERR.puts "#{self.class}:#{self} mangled params for part message!" if @@debug < 2
772 return nil
773 end
774 else
775 return nil
776 end
777 else
778 return nil
779 end
780 end
781 def _event_quit(prefix, cmd, params)
782 parsed_prefix = irc_parse_prefix(prefix)
783 if parsed_prefix then
784 if parsed_prefix.length > 1 then
785 parsed_params = /^\:?(.*)$/.match(params.strip)
786 if parsed_params then
787 nick = parsed_prefix[0]
788 @known_channels.each{|k, v|
789 v.del_user(nick)
790 }
791 STDERR.puts "#{self.class}:#{self} user #{nick} quit (#{parsed_params})" if @@debug < 2
792 else
793 STDERR.puts "#{self.class}:#{self} mangled params for quit message!" if @@debug < 2
794 return nil
795 end
796 else
797 return nil
798 end
799 else
800 return nil
801 end
802 end
803 def _event_rpl_namreply(prefix, cmd, params)
804 begin
805 STDERR.puts "#{self.class}:#{self} event namreply..." if @@debug < 1
806 parsed_params = irc_parse_namreply_params(params)
807 if parsed_params.class == Array then
808 chan = parsed_params[1].strip
809 dchan = chan.downcase
810 if !@writingnames.include?(dchan) then
811 STDERR.puts "#{self.class}:#{self} new name list. Deleting old data." if @@debug < 1
812 @known_channels[dchan].clear_users
813 @writingnames.push dchan
814 end
815
816
817
818 STDERR.puts "#{self.class}:#{self.class} scanning newly-recieved users list." if @@debug < 1
819 parsed_params[2].each{|raw_name|
820 begin
821 user_prefixes = ""
822 name = raw_name
823 STDERR.puts "#{self.class}:#{self} matching first character: #{raw_name[0,1]}" if @@debug < 1
824 @prefixes[0].each_byte{|bt|
825 STDERR.puts "#{self.class}:#{self} matching against #{bt.chr}" if @@debug < 1
826 if raw_name[0,1] == bt.chr then
827 STDERR.puts "#{self.class}:match!" if @@debug < 1
828 name = raw_name[1..raw_name.length]
829 user_prefixes << raw_name[0,1]
830 end
831 }
832 user_modes = ""
833 user_prefixes.split('').each{|el|
834 STDERR.puts "#{self.class}:#{self} converting prefix #{el}" if @@debug < 1
835 @prefixes[0].split('').each_index{|idx|
836 STDERR.puts "#{self.class}:#{self} is it #{@prefixes[0][idx].chr}?" if @@debug < 1
837 if el == @prefixes[0][idx].chr then
838 user_modes << @prefixes[1][idx].chr
839 STDERR.puts "#{self.class}:yes!" if @@debug < 1
840 STDERR.puts "#{self.class}:adding prefix: #{@prefixes[1][idx].chr}" if @@debug < 1
841 else
842 STDERR.puts "#{self.class}:no!" if @@debug < 1
843 end
844 }
845 }
846
847 STDERR.puts "#{self.class}:#{self} getting/creating user #{name}" if @@debug < 1
848 a_user = @user_mgr.get_user!(@current_network, name)
849
850 #a_user.mod_chan_flags(chan, "+#{user_prefixes}") if user_prefixes != ""
851 STDERR.puts "#{self.class}:#{self} adding name to session list" if @@debug < 1
852 @known_channels[dchan].add_user(name)
853 STDERR.puts "#{self.class}:#{self} applying modestring if needed. user_prefixes: #{user_prefixes}, user_modes: #{user_modes}" if @@debug < 1
854 @known_channels[dchan].set_user_mode!(name, "+#{user_modes}")
855 rescue
856 STDERR.puts "#{self.class}:#{self} oh crap: #{$!}" if @@debug < 1
857 raise $!
858 end
859 }
860 end
861 rescue
862 raise "#{self.class}: _event_rpl_namreply: #{$!}"
863 end
864 end
865 def _event_rpl_endofnames(prefix, cmd, params)
866 STDERR.puts "#{self.class}:#{self} event rpl_endofnames triggered." if @@debug < 1
867 parsed_params = irc_parse_twoarg_params(params)
868 STDERR.puts "#{self.class}:#{self} params preprocessed." if @@debug < 1
869 nick, chan = parsed_params[0], parsed_params[1]
870 STDERR.puts "#{self.class}:#{self} nick and chan assigned." if @@debug < 1
871 STDERR.puts "#{self.class}:#{self} nick value: #{nick}" if @@debug < 1
872 STDERR.puts "#{self.class}:#{self} chan value: #{chan}" if @@debug < 1
873 @writingnames.delete_if{|el| el == chan.downcase}
874 STDERR.puts "#{self.class}:#{self} channel #{chan} is no longer writing names." if @@debug < 1
875 end
876 def _event_rpl_bounce(prefix, cmd, params)
877 STDERR.puts "#{self.class}:#{self} _event_rpl_bounce: params:" if @@debug < 1
878 puts params
879 p_match1 = /^(\S*)\s(.*)(\:.*$)?/.match(params.strip)
880 if p_match1 then
881 if @@debug < 1
882 STDERR.puts "#{self.class}:RPL_BOUNCE has extended info. Initial results:"
883 STDERR.puts "#{self.class}:user: #{p_match1[1]}"
884 STDERR.puts "#{self.class}:msg: #{p_match1[2]}"
885 STDERR.puts "#{self.class}:end: #{p_match1[3]}" if p_match1[3]
886 end
887 p_match2 = p_match1[2].split(' ')
888 p_match_hsh = {}
889 p_match2.each{|el|
890 k, v = el.split('=')
891 p_match_hsh[k] = v
892 }
893 if @@debug < 1
894 STDERR.puts "#{self.class}:RPL_BOUNCE filtered results:"
895 p_match_hsh.each{|k, v|
896 STDERR.puts "#{self.class}:#{k}: #{v}"
897 }
898 end
899 if p_match_hsh.has_key?("PREFIX") then
900 v = p_match_hsh["PREFIX"]
901 prefix_match = /^\((\S)\)(\S)$/.match(v.strip)
902 @prefixes = [prefix_match[1], prefix_match[2]] if prefix_match
903 STDERR.puts "#{self.class}:#{self} changed prefix values." if @@debug < 1
904 STDERR.puts "#{self.class}:chars: #{@prefixes[0]}, modes: #{@prefixes[1]}" if @@debug < 1
905 end
906 else
907 STDERR.puts "#{self.class}:#{self} This may be an rfc2812 RPL_BOUNCE. Write a fallback to cope here..." if @@debug < 5
908 end
909 end
910 def _event_rpl_nickserv_authed(prefix, cmd, params)
911 p_params = /^(\S)\s(\S)/.match(params.strip)
912 begin
913 @irc_mgr.get_user(p_params[2]).set_auth
914 rescue
915 STDERR.puts "#{self.class}:#{self} _event_rpl_nickserv_authed: #{$!}" if @@debug < 5
916 end
917 end
918
919 def _save_channels
920 @known_channels.each{|k, v|
921 }
922 end
923end
924#(self.methods - Object.methods).each{|el|
925# puts el
926#}
927
928
929
930def login_test
931 doc = nil
932 File.open("bot_conf.xml", "r"){|fo|
933 STDERR.puts "#{self.class}:file open."
934 doc = REXML::Document.new(fo)
935 STDERR.puts "#{self.class}:document loaded."
936 puts doc.elements["*/networks/*"]
937 }
938 #umgr = IRCUserManager.instance
939 #umgr.start("test")
940 begin
941 a = IRCSession.new("Espernet", doc)
942 sleep 10
943 a.cmd_join_chan("#crystalbot")
944 sleep 1.0
945 a.cmd_privmsg("#crystalbot", "IRCSession object seems to be working.")
946 sleep 3.0
947 a.cmd_quit("test complete.")
948 sleep 1.0
949 rescue
950 STDERR.puts "#{self.class}:the shit hit the fan here: #{$!}"
951 end
952end
953def xmltest
954 doc = nil
955 File.open("irc_conf.xml", "r"){|fo|
956 STDERR.puts "#{self.class}:file open."
957 doc = REXML::Document.new(fo)
958 STDERR.puts "#{self.class}:document loaded."
959 puts doc
960 STDERR.puts "#{self.class}:filtered results:"
961 puts doc.elements["*/networks/*"]
962 }
963 return doc
964end
965def standalone_session
966 standalone = 1
967 if standalone > 0 then
968 #@a = IRCSession.new("192.168.0.1",6670,"RubyBot","RubyBot","RubyBot",nil, "lol")
969 @a = IRCSession.new("EsperNet", xmltest)
970 @t = Thread.new{
971 tstr = Time.now.strftime("%Y%m%d%H%M%S")
972 @fileobj = File.open("#{self.class}#{tstr}.txt", "w+"){|fobj|
973 while true == true
974 tmp = @a.cmd_get_status
975 print "#{tmp.strip}\n" if tmp
976 fobj.print "#{tmp.strip}\n" if tmp
977 fobj.flush if fobj
978 end
979 STDERR.puts "#{self.class}:<@t-thread> Out of main loop."
980 }
981 }
982 STDERR.puts "#{self.class}:Bot awake. Joining channels."
983 @a.cmd_join_chan("#crystalbot")
984
985 @a.cmd_join_chan("#crystalbot2")
986
987 @a.cmd_join_chan("#crystalbot3")
988
989 @a.cmd_privmsg("#crystalbot", "Hello, World.")
990 STDERR.puts "#{self.class}:Sent privmsg"
991
992 @t.join
993 while true == true
994 end
995 end
996end
997#standalone_session