summaryrefslogtreecommitdiff
path: root/rbot/ircsocket.rb
diff options
context:
space:
mode:
Diffstat (limited to 'rbot/ircsocket.rb')
-rw-r--r--rbot/ircsocket.rb186
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