Class: Cinch::IRC
Overview
This class manages the connection to the IRC server. That includes processing incoming and outgoing messages, creating Ruby objects and invoking plugins.
Constant Summary collapse
- EXTERNAL_ENCODING_ON_LOAD =
Use it to fix the encoding of SocketError message.
Encoding.default_external
Instance Attribute Summary collapse
-
#bot ⇒ Bot
readonly
-
#isupport ⇒ ISupport
readonly
-
#network ⇒ Network
readonly
The detected network.
Instance Method Summary collapse
-
#connect ⇒ Boolean
private
True if the connection could be established.
-
#initialize(bot) ⇒ IRC
constructor
A new instance of IRC.
-
#parse(input)
private
-
#registered? ⇒ Boolean
True if we successfully registered yet.
-
#send(msg)
Send a message to the server.
-
#send_cap_end
private
-
#send_cap_ls
private
-
#send_cap_req
private
-
#send_login
private
-
#send_sasl ⇒ Object
-
#setup
private
-
#setup_ssl(socket)
private
-
#socket ⇒ TCPSocket
private
-
#start
Establish a connection.
-
#start_ping_thread ⇒ Thread
private
The ping thread.
-
#start_quit_thread ⇒ Thread
private
The quit thread.
-
#start_reading_thread ⇒ Thread
private
The reading thread.
-
#start_sending_thread ⇒ Thread
private
The sending thread.
Methods included from Helpers
#Channel, #Format, #Sanitize, #Target, #Timer, #Unformat, #User, #debug, #error, #exception, #fatal, #incoming, #info, #log, #outgoing, #rescue_exception, sanitize, #warn
Constructor Details
Instance Attribute Details
#isupport ⇒ ISupport (readonly)
18 19 20 |
# File 'lib/cinch/irc.rb', line 18 def isupport @isupport end |
#network ⇒ Network (readonly)
Returns The detected network.
24 25 26 |
# File 'lib/cinch/irc.rb', line 24 def network @network end |
Instance Method Details
#connect ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns True if the connection could be established.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/cinch/irc.rb', line 50 def connect tcp_socket = nil begin Timeout.timeout(@bot.config.timeouts.connect) do tcp_socket = TCPSocket.new(@bot.config.server, @bot.config.port, @bot.config.local_host) end rescue Timeout::Error => e @bot.last_connection_error = e @bot.loggers.warn('Timed out while connecting') return false rescue SocketError => e # In a Windows environment, the encoding of SocketError message may be # ASCII-8BIT that causes Encoding::CompatibilityError. To prevent that # error, the error message must be encoded to UTF-8. e..force_encoding(EXTERNAL_ENCODING_ON_LOAD).encode!(Encoding::UTF_8) @bot.last_connection_error = e @bot.loggers.warn("Could not connect to the IRC server. Please check your network: #{e.}") return false rescue => e @bot.last_connection_error = e @bot.loggers.exception(e) return false end if @bot.config.ssl.use setup_ssl(tcp_socket) else @socket = tcp_socket end @socket = Net::BufferedIO.new(@socket) @socket.read_timeout = @bot.config.timeouts.read @queue = MessageQueue.new(@socket, @bot) true end |
#parse(input)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/cinch/irc.rb', line 274 def parse(input) return if input.chomp.empty? @bot.loggers.incoming(input) msg = Message.new(input, @bot) events = [[:catchall]] if %w[001 002 003 004 422].include?(msg.command) @registration << msg.command if registered? events << [:connect] @bot.last_connection_was_successful = true @bot.last_connection_error = nil on_connect(msg, events) end end if %w[PRIVMSG NOTICE].include?(msg.command) events << [:ctcp] if msg.ctcp? events << if msg.channel? [:channel] else [:private] end if msg.command == 'PRIVMSG' events << [:message] end if msg.action? events << [:action] end end meth = "on_#{msg.command.downcase}" __send__(meth, msg, events) if respond_to?(meth, true) if msg.error? events << [:error] end events << [msg.command.downcase.to_sym] msg.events = events.map(&:first) events.each do |event, *args| @bot.handlers.dispatch(event, msg, *args) end end |
#registered? ⇒ Boolean
Returns true if we successfully registered yet.
327 328 329 |
# File 'lib/cinch/irc.rb', line 327 def registered? (('001'..'004').to_a - @registration).empty? || @registration.include?('422') end |
#send(msg)
This method returns an undefined value.
Send a message to the server.
334 335 336 |
# File 'lib/cinch/irc.rb', line 334 def send(msg) @queue.queue(msg) end |
#send_cap_end
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
145 146 147 |
# File 'lib/cinch/irc.rb', line 145 def send_cap_end send 'CAP END' end |
#send_cap_ls
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
123 124 125 |
# File 'lib/cinch/irc.rb', line 123 def send_cap_ls send 'CAP LS' end |
#send_cap_req
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/cinch/irc.rb', line 130 def send_cap_req caps = %i[away-notify multi-prefix sasl twitch.tv/tags] & @network.capabilities # InspIRCd doesn't respond to empty REQs, so send an END in that # case. if !caps.empty? send 'CAP REQ :' + caps.join(' ') else send_cap_end end end |
#send_login
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
152 153 154 155 156 |
# File 'lib/cinch/irc.rb', line 152 def send_login send "PASS #{@bot.config.password}" if @bot.config.password send "NICK #{@bot.generate_next_nick!}" send "USER #{@bot.config.user} 0 * :#{@bot.config.realname}" end |
#send_sasl ⇒ Object
238 239 240 241 242 243 244 245 |
# File 'lib/cinch/irc.rb', line 238 def send_sasl if @bot.config.sasl.username && @sasl_current_method = @sasl_remaining_methods.pop @bot.loggers.info "[SASL] Trying to authenticate with #{@sasl_current_method.mechanism_name}" send "AUTHENTICATE #{@sasl_current_method.mechanism_name}" else send_cap_end end end |
#setup
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
41 42 43 44 45 46 |
# File 'lib/cinch/irc.rb', line 41 def setup @registration = [] @network = Network.new(:unknown, :unknown) @whois_updates = {} @in_lists = Set.new end |
#setup_ssl(socket)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/cinch/irc.rb', line 95 def setup_ssl(socket) # require openssl in this method so the bot doesn't break for # people who don't have SSL but don't want to use SSL anyway. require 'openssl' ssl_context = OpenSSL::SSL::SSLContext.new if @bot.config.ssl.is_a?(Configuration::SSL) if @bot.config.ssl.client_cert ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@bot.config.ssl.client_cert)) ssl_context.key = OpenSSL::PKey::RSA.new(File.read(@bot.config.ssl.client_cert)) end ssl_context.ca_path = @bot.config.ssl.ca_path ssl_context.verify_mode = @bot.config.ssl.verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE else ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE end @bot.loggers.info "Using SSL with #{@bot.config.server}:#{@bot.config.port}" @socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context) @socket.sync = true @socket.connect end |
#socket ⇒ TCPSocket
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
34 35 36 |
# File 'lib/cinch/irc.rb', line 34 def socket @socket.io end |
#start
This method returns an undefined value.
Establish a connection.
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/cinch/irc.rb', line 251 def start setup if connect @sasl_remaining_methods = @bot.config.sasl.mechanisms.reverse send_cap_ls send_login reading_thread = start_reading_thread sending_thread = start_sending_thread ping_thread = start_ping_thread quit_thread = start_quit_thread reading_thread.join sending_thread.kill ping_thread.kill @bot.quit_queue.push([:stop]) quit_thread.join end end |
#start_ping_thread ⇒ Thread
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns The ping thread.
201 202 203 204 205 206 207 208 209 210 |
# File 'lib/cinch/irc.rb', line 201 def start_ping_thread Thread.new do loop do sleep @bot.config.ping_interval # PING requires a single argument. In our case the value # doesn't matter though. send('PING 0') end end end |
#start_quit_thread ⇒ Thread
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns The quit thread.
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/cinch/irc.rb', line 215 def start_quit_thread Thread.new do sent_quit = false loop do command, *args = @bot.quit_queue.pop case command when :stop break when :quit unless sent_quit = args[0] command = ? "QUIT :#{}" : 'QUIT' send(command) sent_quit = true end end end end end |
#start_reading_thread ⇒ Thread
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns the reading thread.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/cinch/irc.rb', line 161 def start_reading_thread Thread.new do begin while line = @socket.readline rescue_exception do line = Cinch::Utilities::Encoding.encode_incoming(line, @bot.config.encoding) parse line end end rescue Timeout::Error @bot.loggers.warn 'Connection timed out.' rescue EOFError @bot.loggers.warn 'Lost connection.' rescue => e @bot.loggers.exception(e) end @socket.close @bot.handlers.dispatch(:disconnect) # FIXME: won't we kill all :disconnect handlers here? prolly # not, as they have 10 seconds to finish. that should be # plenty of time @bot.handlers.stop_all end end |
#start_sending_thread ⇒ Thread
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns the sending thread.
190 191 192 193 194 195 196 |
# File 'lib/cinch/irc.rb', line 190 def start_sending_thread Thread.new do rescue_exception do @queue.process! end end end |