root/rbot/ircsocket.rb @ 0f3e302547363ea237454dda891ddb5de1be4476

Revision 0f3e302547363ea237454dda891ddb5de1be4476, 4.3 KB (checked in by Tom Gilbert <tom@…>, 6 years ago)

initial import of rbot

  • Property mode set to 100644
Line 
1module Irc
2
3  require 'socket'
4  require 'thread'
5
6  # wrapped TCPSocket for communication with the server.
7  # emulates a subset of TCPSocket functionality
8  class IrcSocket
9    # total number of lines sent to the irc server
10    attr_reader :lines_sent
11    # total number of lines received from the irc server
12    attr_reader :lines_received
13    # server:: server to connect to
14    # port::   IRCd port
15    # host::   optional local host to bind to (ruby 1.7+ required)
16    # create a new IrcSocket
17    def initialize(server, port, host, sendfreq=2, maxburst=4)
18      @server = server.dup
19      @port = port.to_i
20      @host = host
21      @lines_sent = 0
22      @lines_received = 0
23      if sendfreq
24        @sendfreq = sendfreq.to_f
25      else
26        @sendfreq = 2
27      end
28      @last_send = Time.new - @sendfreq
29      @burst = 0
30      if maxburst
31        @maxburst = maxburst.to_i
32      else
33        @maxburst = 4
34      end
35    end
36   
37    # open a TCP connection to the server
38    def connect
39      if(@host)
40        begin
41          @sock=TCPSocket.new(@server, @port, @host)
42        rescue ArgumentError => e
43          $stderr.puts "Your version of ruby does not support binding to a "
44          $stderr.puts "specific local address, please upgrade if you wish "
45          $stderr.puts "to use HOST = foo"
46          $stderr.puts "(this option has been disabled in order to continue)"
47          @sock=TCPSocket.new(@server, @port)
48        end
49      else
50        @sock=TCPSocket.new(@server, @port)
51      end 
52      @qthread = false
53      @qmutex = Mutex.new
54      @sendq = Array.new
55      if (@sendfreq > 0)
56        @qthread = Thread.new { spooler }
57      end
58    end
59
60    def set_sendq(newfreq)
61      debug "changing sendq frequency to #{newfreq}"
62      @qmutex.synchronize do
63        @sendfreq = newfreq
64        if newfreq == 0 && @qthread
65          clearq
66          Thread.kill(@qthread)
67          @qthread = false
68        elsif(newfreq != 0 && !@qthread)
69          @qthread = Thread.new { spooler }
70        end
71      end
72    end
73
74    def set_maxburst(newburst)
75      @qmutex.synchronize do
76        @maxburst = newburst
77      end
78    end
79
80    def get_maxburst
81      return @maxburst
82    end
83
84    def get_sendq
85      return @sendfreq
86    end
87   
88    # used to send lines to the remote IRCd
89    # message: IRC message to send
90    def puts(message)
91      @qmutex.synchronize do
92        # debug "In puts - got mutex"
93        puts_critical(message)
94      end
95    end
96
97    # get the next line from the server (blocks)
98    def gets
99      reply = @sock.gets
100      @lines_received += 1
101      if(reply)
102        reply.strip!
103      end
104      debug "RECV: #{reply.inspect}"
105      reply
106    end
107
108    def queue(msg)
109      if @sendfreq > 0
110        @qmutex.synchronize do
111          # debug "QUEUEING: #{msg}"
112          @sendq.push msg
113        end
114      else
115        # just send it if queueing is disabled
116        self.puts(msg)
117      end
118    end
119
120    def spooler
121      while true
122        spool
123        sleep 0.1
124      end
125    end
126
127    # pop a message off the queue, send it
128    def spool
129      unless @sendq.empty?
130        now = Time.new
131        if (now >= (@last_send + @sendfreq))
132          # reset burst counter after @sendfreq has passed
133          @burst = 0
134          debug "in spool, resetting @burst"
135        elsif (@burst >= @maxburst)
136          # nope. can't send anything
137          return
138        end
139        @qmutex.synchronize do
140          debug "(can send #{@maxburst - @burst} lines, there are #{@sendq.length} to send)"
141          (@maxburst - @burst).times do
142            break if @sendq.empty?
143            puts_critical(@sendq.shift)
144          end
145        end
146      end
147    end
148
149    def clearq
150      unless @sendq.empty?
151        @qmutex.synchronize do
152          @sendq.clear
153        end
154      end
155    end
156
157    # flush the TCPSocket
158    def flush
159      @sock.flush
160    end
161
162    # Wraps Kernel.select on the socket
163    def select(timeout)
164      Kernel.select([@sock], nil, nil, timeout)
165    end
166
167    # shutdown the connection to the server
168    def shutdown(how=2)
169      @sock.shutdown(how)
170    end
171
172    private
173   
174    # same as puts, but expects to be called with a mutex held on @qmutex
175    def puts_critical(message)
176      # debug "in puts_critical"
177      debug "SEND: #{message.inspect}"
178      @sock.send(message + "\n",0)
179      @last_send = Time.new
180      @lines_sent += 1
181      @burst += 1
182    end
183
184  end
185
186end
Note: See TracBrowser for help on using the browser.