diff options
Diffstat (limited to 'rbot/ircsocket.rb')
-rw-r--r-- | rbot/ircsocket.rb | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/rbot/ircsocket.rb b/rbot/ircsocket.rb new file mode 100644 index 00000000..25895644 --- /dev/null +++ b/rbot/ircsocket.rb @@ -0,0 +1,186 @@ +module Irc + + require 'socket' + require 'thread' + + # wrapped TCPSocket for communication with the server. + # emulates a subset of TCPSocket functionality + class IrcSocket + # total number of lines sent to the irc server + attr_reader :lines_sent + # total number of lines received from the irc server + attr_reader :lines_received + # server:: server to connect to + # port:: IRCd port + # host:: optional local host to bind to (ruby 1.7+ required) + # create a new IrcSocket + def initialize(server, port, host, sendfreq=2, maxburst=4) + @server = server.dup + @port = port.to_i + @host = host + @lines_sent = 0 + @lines_received = 0 + if sendfreq + @sendfreq = sendfreq.to_f + else + @sendfreq = 2 + end + @last_send = Time.new - @sendfreq + @burst = 0 + if maxburst + @maxburst = maxburst.to_i + else + @maxburst = 4 + end + end + + # open a TCP connection to the server + def connect + if(@host) + begin + @sock=TCPSocket.new(@server, @port, @host) + rescue ArgumentError => e + $stderr.puts "Your version of ruby does not support binding to a " + $stderr.puts "specific local address, please upgrade if you wish " + $stderr.puts "to use HOST = foo" + $stderr.puts "(this option has been disabled in order to continue)" + @sock=TCPSocket.new(@server, @port) + end + else + @sock=TCPSocket.new(@server, @port) + end + @qthread = false + @qmutex = Mutex.new + @sendq = Array.new + if (@sendfreq > 0) + @qthread = Thread.new { spooler } + end + end + + def set_sendq(newfreq) + debug "changing sendq frequency to #{newfreq}" + @qmutex.synchronize do + @sendfreq = newfreq + if newfreq == 0 && @qthread + clearq + Thread.kill(@qthread) + @qthread = false + elsif(newfreq != 0 && !@qthread) + @qthread = Thread.new { spooler } + end + end + end + + def set_maxburst(newburst) + @qmutex.synchronize do + @maxburst = newburst + end + end + + def get_maxburst + return @maxburst + end + + def get_sendq + return @sendfreq + end + + # used to send lines to the remote IRCd + # message: IRC message to send + def puts(message) + @qmutex.synchronize do + # debug "In puts - got mutex" + puts_critical(message) + end + end + + # get the next line from the server (blocks) + def gets + reply = @sock.gets + @lines_received += 1 + if(reply) + reply.strip! + end + debug "RECV: #{reply.inspect}" + reply + end + + def queue(msg) + if @sendfreq > 0 + @qmutex.synchronize do + # debug "QUEUEING: #{msg}" + @sendq.push msg + end + else + # just send it if queueing is disabled + self.puts(msg) + end + end + + def spooler + while true + spool + sleep 0.1 + end + end + + # pop a message off the queue, send it + def spool + unless @sendq.empty? + now = Time.new + if (now >= (@last_send + @sendfreq)) + # reset burst counter after @sendfreq has passed + @burst = 0 + debug "in spool, resetting @burst" + elsif (@burst >= @maxburst) + # nope. can't send anything + return + end + @qmutex.synchronize do + debug "(can send #{@maxburst - @burst} lines, there are #{@sendq.length} to send)" + (@maxburst - @burst).times do + break if @sendq.empty? + puts_critical(@sendq.shift) + end + end + end + end + + def clearq + unless @sendq.empty? + @qmutex.synchronize do + @sendq.clear + end + end + end + + # flush the TCPSocket + def flush + @sock.flush + end + + # Wraps Kernel.select on the socket + def select(timeout) + Kernel.select([@sock], nil, nil, timeout) + end + + # shutdown the connection to the server + def shutdown(how=2) + @sock.shutdown(how) + end + + private + + # same as puts, but expects to be called with a mutex held on @qmutex + def puts_critical(message) + # debug "in puts_critical" + debug "SEND: #{message.inspect}" + @sock.send(message + "\n",0) + @last_send = Time.new + @lines_sent += 1 + @burst += 1 + end + + end + +end |