]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - rbot/ircsocket.rb
initial import of rbot
[user/henk/code/ruby/rbot.git] / rbot / ircsocket.rb
1 module 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
186 end