Class Irc::Socket
In: lib/rbot/ircsocket.rb
Parent: Object
BasicUserMessage JoinMessage NamesMessage WhoisMessage ModeChangeMessage KickMessage MotdMessage QuitMessage BanlistMessage UserMessage NoSuchTargetMessage TopicMessage NickMessage WelcomeMessage UnknownMessage InviteMessage PartMessage NetmaskList UserList ArrayOf ChannelList Netmask User\n[lib/rbot/botuser.rb\nlib/rbot/irc.rb] Channel Singleton RfcCasemap StrictRfcCasemap AsciiCasemap Casemap PrivMessage NoticeMessage TokyoCabinet::BDB CIBDB Btree CIBtree Socket MessageQueue QueueRing Client DBHash\n[lib/rbot/registry/bdb.rb\nlib/rbot/registry/tc.rb] DBTree\n[lib/rbot/registry/bdb.rb\nlib/rbot/registry/tc.rb] Server NetmaskDb Bot\n[lib/rbot/botuser.rb\nlib/rbot/config.rb\nlib/rbot/ircbot.rb\nlib/rbot/language.rb\nlib/rbot/message.rb\nlib/rbot/messagemapper.rb\nlib/rbot/plugins.rb\nlib/rbot/rbotconfig.rb\nlib/rbot/registry/bdb.rb\nlib/rbot/registry/tc.rb] lib/rbot/ircsocket.rb lib/rbot/rfc2812.rb lib/rbot/registry/tc.rb lib/rbot/irc.rb lib/rbot/maskdb.rb lib/rbot/message.rb lib/rbot/messagemapper.rb lib/rbot/botuser.rb lib/rbot/registry/tc.rb (null) BotConfig PKGConfig ServerOrCasemap Irc dot/m_35_0.png

wrapped TCPSocket for communication with the server. emulates a subset of TCPSocket functionality

Methods

Classes and Modules

Class Irc::Socket::IdentityFilter

Constants

MAX_IRC_SEND_PENALTY = 10

Attributes

bytes_received  [R]  total number of bytes received from the irc server
bytes_sent  [R]  total number of bytes sent to the irc server
filter  [R]  an optional filter object. we call @filter.in(data) for all incoming data and @filter.out(data) for all outgoing data
lines_received  [R]  total number of lines received from the irc server
lines_sent  [R]  total number of lines sent to the irc server
penalty_pct  [RW]  penalty multiplier (percent)
server_uri  [R]  normalized uri of the current server
throttle_bytes  [R]  accumulator for the throttle

Public Class methods

server_list:list of servers to connect to
host:optional local host to bind to (ruby 1.7+ required)

create a new Irc::Socket

[Source]

     # File lib/rbot/ircsocket.rb, line 277
277:     def initialize(server_list, host, opts={})
278:       @server_list = server_list.dup
279:       @server_uri = nil
280:       @conn_count = 0
281:       @host = host
282:       @sock = nil
283:       @filter = IdentityFilter.new
284:       @spooler = false
285:       @lines_sent = 0
286:       @lines_received = 0
287:       @ssl = opts[:ssl]
288:       @penalty_pct = opts[:penalty_pct] || 100
289:     end

Public Instance methods

[Source]

     # File lib/rbot/ircsocket.rb, line 379
379:     def clearq
380:       @sendq.clear
381:     end

open a TCP connection to the server

[Source]

     # File lib/rbot/ircsocket.rb, line 296
296:     def connect
297:       if connected?
298:         warning "reconnecting while connected"
299:         return
300:       end
301:       srv_uri = @server_list[@conn_count % @server_list.size].dup
302:       srv_uri = 'irc://' + srv_uri if !(srv_uri =~ /:\/\//)
303:       @conn_count += 1
304:       @server_uri = URI.parse(srv_uri)
305:       @server_uri.port = 6667 if !@server_uri.port
306:       debug "connection attempt \##{@conn_count} (#{@server_uri.host}:#{@server_uri.port})"
307: 
308:       if(@host)
309:         begin
310:           sock=TCPSocket.new(@server_uri.host, @server_uri.port, @host)
311:         rescue ArgumentError => e
312:           error "Your version of ruby does not support binding to a "
313:           error "specific local address, please upgrade if you wish "
314:           error "to use HOST = foo"
315:           error "(this option has been disabled in order to continue)"
316:           sock=TCPSocket.new(@server_uri.host, @server_uri.port)
317:         end
318:       else
319:         sock=TCPSocket.new(@server_uri.host, @server_uri.port)
320:       end
321:       if(@ssl)
322:         require 'openssl'
323:         ssl_context = OpenSSL::SSL::SSLContext.new()
324:         ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
325:         sock = OpenSSL::SSL::SSLSocket.new(sock, ssl_context)
326:         sock.sync_close = true
327:         sock.connect
328:       end
329:       @sock = sock
330:       @last_send = Time.new
331:       @flood_send = Time.new
332:       @burst = 0
333:       @sock.extend(MonitorMixin)
334:       @sendq = MessageQueue.new
335:       @qthread = Thread.new { writer_loop }
336:     end

[Source]

     # File lib/rbot/ircsocket.rb, line 291
291:     def connected?
292:       !@sock.nil?
293:     end

used to send lines to the remote IRCd by skipping the queue message: IRC message to send it should only be used for stuff that *must not* be queued, i.e. the initial PASS, NICK and USER command or the final QUIT message

[Source]

     # File lib/rbot/ircsocket.rb, line 343
343:     def emergency_puts(message, penalty = false)
344:       @sock.synchronize do
345:         # debug "In puts - got @sock"
346:         puts_critical(message, penalty)
347:       end
348:     end

set filter to identity, not to nil

[Source]

     # File lib/rbot/ircsocket.rb, line 270
270:     def filter=(f)
271:         @filter = f || IdentityFilter.new
272:     end

flush the TCPSocket

[Source]

     # File lib/rbot/ircsocket.rb, line 384
384:     def flush
385:       @sock.flush
386:     end

get the next line from the server (blocks)

[Source]

     # File lib/rbot/ircsocket.rb, line 359
359:     def gets
360:       if @sock.nil?
361:         warning "socket get attempted while closed"
362:         return nil
363:       end
364:       begin
365:         reply = @filter.in(@sock.gets)
366:         @lines_received += 1
367:         reply.strip! if reply
368:         debug "RECV: #{reply.inspect}"
369:         return reply
370:       rescue Exception => e
371:         handle_socket_error(:RECV, e)
372:       end
373:     end

[Source]

     # File lib/rbot/ircsocket.rb, line 350
350:     def handle_socket_error(string, e)
351:       error "#{string} failed: #{e.pretty_inspect}"
352:       # We assume that an error means that there are connection
353:       # problems and that we should reconnect, so we
354:       shutdown
355:       raise SocketError.new(e.inspect)
356:     end

[Source]

     # File lib/rbot/ircsocket.rb, line 375
375:     def queue(msg, chan=nil, ring=0)
376:       @sendq.push msg, chan, ring
377:     end

Wraps Kernel.select on the socket

[Source]

     # File lib/rbot/ircsocket.rb, line 389
389:     def select(timeout=nil)
390:       Kernel.select([@sock], nil, nil, timeout)
391:     end

shutdown the connection to the server

[Source]

     # File lib/rbot/ircsocket.rb, line 394
394:     def shutdown(how=2)
395:       return unless connected?
396:       @qthread.kill
397:       @qthread = nil
398:       begin
399:         @sock.close
400:       rescue Exception => e
401:         error "error while shutting down: #{e.pretty_inspect}"
402:       end
403:       @sock = nil
404:       @sendq.clear
405:     end

Private Instance methods

same as puts, but expects to be called with a lock held on @sock

[Source]

     # File lib/rbot/ircsocket.rb, line 431
431:     def puts_critical(message, penalty=false)
432:       # debug "in puts_critical"
433:       begin
434:         debug "SEND: #{message.inspect}"
435:         if @sock.nil?
436:           error "SEND attempted on closed socket"
437:         else
438:           # we use Socket#syswrite() instead of Socket#puts() because
439:           # the latter is racy and can cause double message output in
440:           # some circumstances
441:           actual = @filter.out(message) + "\n"
442:           now = Time.new
443:           @sock.syswrite actual
444:           @last_send = now
445:           @flood_send = now if @flood_send < now
446:           @flood_send += message.irc_send_penalty*@penalty_pct/100.0 if penalty
447:           @lines_sent += 1
448:         end
449:       rescue Exception => e
450:         handle_socket_error(:SEND, e)
451:       end
452:     end

[Source]

     # File lib/rbot/ircsocket.rb, line 409
409:     def writer_loop
410:       loop do
411:         begin
412:           now = Time.now
413:           flood_delay = @flood_send - MAX_IRC_SEND_PENALTY - now
414:           delay = [flood_delay, 0].max
415:           if delay > 0
416:             debug "sleep(#{delay}) # (f: #{flood_delay})"
417:             sleep(delay)
418:           end
419:           msg = @sendq.shift
420:           debug "got #{msg.inspect} from queue, sending"
421:           emergency_puts(msg, true)
422:         rescue Exception => e
423:           error "Spooling failed: #{e.pretty_inspect}"
424:           debug e.backtrace.join("\n")
425:           raise e
426:         end
427:       end
428:     end

[Validate]