class Cinch::IRC
This class manages the connection to the IRC
server. That includes processing incoming and outgoing messages, creating Ruby objects and invoking plugins.
Constants
- EXTERNAL_ENCODING_ON_LOAD
Use it to fix the encoding of SocketError message.
Attributes
@return [Bot]
@return [ISupport]
@return [Network] The detected network
Public Class Methods
# File lib/cinch/irc.rb, line 26 def initialize(bot) @bot = bot @isupport = ISupport.new end
Public Instance Methods
@api private @return [Boolean] True if the connection could be established
# 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.message.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.message}") 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
@api private @return [void]
# 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
@return [Boolean] true if we successfully registered yet
# File lib/cinch/irc.rb, line 327 def registered? (('001'..'004').to_a - @registration).empty? || @registration.include?('422') end
Send a message to the server. @param [String] msg @return [void]
# File lib/cinch/irc.rb, line 334 def send(msg) @queue.queue(msg) end
@since 2.0.0 @api private @return [void]
# File lib/cinch/irc.rb, line 145 def send_cap_end send 'CAP END' end
@api private @return [void] @since 2.0.0
# File lib/cinch/irc.rb, line 123 def send_cap_ls send 'CAP LS' end
@api private @return [void] @since 2.0.0
# 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
@api private @return [void] @since 2.0.0
# 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
@since 2.0.0
# 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
@api private @return [void] @since 2.0.0
# File lib/cinch/irc.rb, line 41 def setup @registration = [] @network = Network.new(:unknown, :unknown) @whois_updates = {} @in_lists = Set.new end
@api private @return [void] @since 2.0.0
# 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
@return [TCPSocket] @api private @since 2.0.0
# File lib/cinch/irc.rb, line 34 def socket @socket.io end
Establish a connection.
@return [void] @since 2.0.0
# 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
@api private @return [Thread] The ping thread. @since 2.0.0
# 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
@api private @return [Thread] The quit thread. @since 2.0.0
# 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 message = args[0] command = message ? "QUIT :#{message}" : 'QUIT' send(command) sent_quit = true end end end end end
@api private @return [Thread] the reading thread @since 2.0.0
# 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
@api private @return [Thread] the sending thread @since 2.0.0
# File lib/cinch/irc.rb, line 190 def start_sending_thread Thread.new do rescue_exception do @queue.process! end end end
Private Instance Methods
@since 2.0.0
# File lib/cinch/irc.rb, line 345 def detect_network(msg, event) old_network = @network new_network = nil new_ircd = nil case event when '002' if msg.params.last =~ /^Your host is .+?, running version (.+)$/ case Regexp.last_match(1) when /\+snircd\(/ new_ircd = :snircd when /^u[\d\.]+$/ new_ircd = :ircu when /^(.+?)-?\d+/ new_ircd = Regexp.last_match(1).downcase.to_sym end elsif msg.params.last == 'Your host is jtvchat' new_network = :jtv new_ircd = :jtv end when '004' if msg.params == %w[irc.tinyspeck.com IRC-SLACK gateway] new_network = :slack new_ircd = :slack end when '005' case @isupport['NETWORK'] when 'NGameTV' new_network = :ngametv new_ircd = :ngametv when nil else new_network = @isupport['NETWORK'].downcase.to_sym end end new_network ||= old_network.name new_ircd ||= old_network.ircd if old_network.unknown_ircd? && new_ircd != :unknown @bot.loggers.info "Detected IRCd: #{new_ircd}" end if !old_network.unknown_ircd? && new_ircd != old_network.ircd @bot.loggers.info "Detected different IRCd: #{old_network.ircd} -> #{new_ircd}" end if old_network.unknown_network? && new_network != :unknown @bot.loggers.info "Detected network: #{new_network}" end if !old_network.unknown_network? && new_network != old_network.name @bot.loggers.info "Detected different network: #{old_network.name} -> #{new_network}" end @network.name = new_network @network.ircd = new_ircd end
@since 2.0.0
# File lib/cinch/irc.rb, line 660 def on_001(msg, _events) # Ensure that we know our real, possibly truncated or otherwise # modified nick. @bot.set_nick msg.params.first end
@since 2.0.0
# File lib/cinch/irc.rb, line 667 def on_002(msg, _events) detect_network(msg, '002') end
@since 2.2.6
# File lib/cinch/irc.rb, line 672 def on_004(msg, _events) detect_network(msg, '004') end
# File lib/cinch/irc.rb, line 676 def on_005(msg, _events) # ISUPPORT @isupport.parse(*msg.params[1..-2].map { |v| v.split(' ') }.flatten) detect_network(msg, '005') end
@since 2.0.0
# File lib/cinch/irc.rb, line 683 def on_301(msg, _events) # RPL_AWAY user = User(msg.params[1]) away = msg.params.last if @whois_updates[user] update_whois(user, { away: away }) end end
@since 1.1.0
# File lib/cinch/irc.rb, line 694 def on_307(msg, _events) # RPL_WHOISREGNICK user = User(msg.params[1]) update_whois(user, { registered: true }) end
# File lib/cinch/irc.rb, line 700 def on_311(msg, _events) # RPL_WHOISUSER user = User(msg.params[1]) update_whois(user, { user: msg.params[2], host: msg.params[3], realname: msg.params[5], }) end
# File lib/cinch/irc.rb, line 710 def on_313(msg, _events) # RPL_WHOISOPERATOR user = User(msg.params[1]) update_whois(user, { oper?: true }) end
# File lib/cinch/irc.rb, line 716 def on_317(msg, _events) # RPL_WHOISIDLE user = User(msg.params[1]) update_whois(user, { idle: msg.params[2].to_i, signed_on_at: Time.at(msg.params[3].to_i), }) end
# File lib/cinch/irc.rb, line 725 def on_318(msg, _events) # RPL_ENDOFWHOIS user = User(msg.params[1]) user.end_of_whois(@whois_updates[user]) @whois_updates.delete user end
# File lib/cinch/irc.rb, line 732 def on_319(msg, _events) # RPL_WHOISCHANNELS user = User(msg.params[1]) channels = msg.params[2].scan(/[#{@isupport["CHANTYPES"].join}][^ ]+/o).map { |c| Channel(c) } update_whois(user, { channels: channels }) end
# File lib/cinch/irc.rb, line 739 def on_324(msg, _events) # RPL_CHANNELMODEIS modes = {} arguments = msg.params[3..-1] msg.params[2][1..-1].split('').each do |mode| modes[mode] = if (@isupport['CHANMODES']['B'] + @isupport['CHANMODES']['C']).include?(mode) arguments.shift else true end end msg.channel.sync(:modes, modes, false) end
# File lib/cinch/irc.rb, line 755 def on_330(msg, _events) # RPL_WHOISACCOUNT user = User(msg.params[1]) authname = msg.params[2] update_whois(user, { authname: authname }) end
# File lib/cinch/irc.rb, line 762 def on_331(msg, _events) # RPL_NOTOPIC msg.channel.sync(:topic, '') end
# File lib/cinch/irc.rb, line 767 def on_332(msg, _events) # RPL_TOPIC msg.channel.sync(:topic, msg.params[2]) end
# File lib/cinch/irc.rb, line 772 def on_352(msg, _events) # RPL_WHOREPLY # "<channel> <user> <host> <server> <nick> <H|G>[*][@|+] :<hopcount> <real name>" _, channel, user, host, _, nick, _, hopsrealname = msg.params _, realname = hopsrealname.split(' ', 2) channel = Channel(channel) user_object = User(nick) user_object.sync(:user, user, true) user_object.sync(:host, host, true) user_object.sync(:realname, realname, true) end
# File lib/cinch/irc.rb, line 803 def on_353(msg, _events) # RPL_NAMEREPLY unless @in_lists.include?(:names) msg.channel.clear_users end @in_lists << :names msg.params[3].split(' ').each do |user| m = user.match(/^([#{@isupport["PREFIX"].values.join}]+)/) if m prefixes = m[1].split('').map { |s| @isupport['PREFIX'].key(s) } nick = user[prefixes.size..-1] else nick = user prefixes = [] end user = User(nick) user.online = true msg.channel.add_user(user, prefixes) user.channels_unsynced << msg.channel unless user.channels_unsynced.include?(msg.channel) end end
# File lib/cinch/irc.rb, line 784 def on_354(msg, _events) # RPL_WHOSPCRPL # We are using the following format: %acfhnru # _ user host nick f account realame # :leguin.freenode.net 354 dominikh_ ~a ip-88-152-125-117.unitymediagroup.de dominikh_ H 0 :d # :leguin.freenode.net 354 dominikh_ ~FiXato fixato.net FiXato H FiXato :FiXato, using WeeChat -- More? See: http://twitter # :leguin.freenode.net 354 dominikh_ ~dominikh cinch/developer/dominikh dominikh H DominikH :dominikh # :leguin.freenode.net 354 dominikh_ ~oddmunds s21-04214.dsl.no.powertech.net oddmunds H 0 :oddmunds _, channel, user, host, nick, _, account, realname = msg.params channel = Channel(channel) user_object = User(nick) user_object.sync(:user, user, true) user_object.sync(:host, host, true) user_object.sync(:realname, realname, true) user_object.sync(:authname, account == '0' ? nil : account, true) end
# File lib/cinch/irc.rb, line 826 def on_366(msg, _events) # RPL_ENDOFNAMES @in_lists.delete :names msg.channel.mark_as_synced(:users) end
@version 2.0.0
# File lib/cinch/irc.rb, line 833 def on_367(msg, _events) # RPL_BANLIST unless @in_lists.include?(:bans) msg.channel.bans_unsynced.clear end @in_lists << :bans mask = msg.params[2] if @network.jtv? # on the justin tv network, ban "masks" only consist of the # nick/username mask = '%s!%s@%s' % [mask, mask, mask + '.irc.justin.tv'] end by = if msg.params[3] User(msg.params[3].split('!').first) end at = Time.at(msg.params[4].to_i) ban = Ban.new(mask, by, at) msg.channel.bans_unsynced << ban end
# File lib/cinch/irc.rb, line 856 def on_368(msg, _events) # RPL_ENDOFBANLIST if @in_lists.include?(:bans) @in_lists.delete :bans else # we never received a ban, yet an end of list => no bans msg.channel.bans_unsynced.clear end msg.channel.mark_as_synced(:bans) end
# File lib/cinch/irc.rb, line 868 def on_386(msg, _events) # RPL_QLIST unless @in_lists.include?(:owners) msg.channel.owners_unsynced.clear end @in_lists << :owners owner = User(msg.params[2]) msg.channel.owners_unsynced << owner end
# File lib/cinch/irc.rb, line 879 def on_387(msg, _events) # RPL_ENDOFQLIST if @in_lists.include?(:owners) @in_lists.delete :owners else # we never received an owner, yet an end of list -> no owners msg.channel.owners_unsynced.clear end msg.channel.mark_as_synced(:owners) end
# File lib/cinch/irc.rb, line 891 def on_396(msg, _events) # RPL_HOSTHIDDEN # note: designed for freenode User(msg.params[0]).sync(:host, msg.params[1], true) end
# File lib/cinch/irc.rb, line 897 def on_401(msg, _events) # ERR_NOSUCHNICK if user = @bot.user_list.find(msg.params[1]) update_whois(user, { unknown?: true }) end end
# File lib/cinch/irc.rb, line 904 def on_402(msg, _events) # ERR_NOSUCHSERVER if user = @bot.user_list.find(msg.params[1]) # not _ensured, we only want a user that already exists user.end_of_whois({ unknown?: true }) @whois_updates.delete user # TODO: freenode specific, test on other IRCd end end
# File lib/cinch/irc.rb, line 914 def on_433(msg, _events) # ERR_NICKNAMEINUSE @bot.nick = @bot.generate_next_nick!(msg.params[1]) end
# File lib/cinch/irc.rb, line 919 def on_671(msg, _events) user = User(msg.params[1]) update_whois(user, { secure?: true }) end
@since 2.0.0
# File lib/cinch/irc.rb, line 925 def on_730(msg, _events) # RPL_MONONLINE msg.params.last.split(',').each do |mask| user = User(Mask.new(mask).nick) # User is responsible for emitting an event user.online = true end end
@since 2.0.0
# File lib/cinch/irc.rb, line 935 def on_731(msg, _events) # RPL_MONOFFLINE msg.params.last.split(',').each do |nick| user = User(nick) # User is responsible for emitting an event user.online = false end end
@since 2.0.0
# File lib/cinch/irc.rb, line 945 def on_734(msg, _events) # ERR_MONLISTFULL user = User(msg.params[2]) user.monitored = false end
@since 2.0.0
# File lib/cinch/irc.rb, line 952 def on_903(_msg, _events) # SASL authentication successful @bot.loggers.info "[SASL] SASL authentication with #{@sasl_current_method.mechanism_name} successful" send_cap_end end
@since 2.0.0
# File lib/cinch/irc.rb, line 959 def on_904(_msg, _events) # SASL authentication failed @bot.loggers.info "[SASL] SASL authentication with #{@sasl_current_method.mechanism_name} failed" send_sasl end
@since 2.0.0
# File lib/cinch/irc.rb, line 966 def on_authenticate(msg, _events) send 'AUTHENTICATE ' + @sasl_current_method.generate(@bot.config.sasl.username, @bot.config.sasl.password, msg.params.last) end
@since 2.0.0
# File lib/cinch/irc.rb, line 430 def on_away(msg, events) if msg.message.to_s.empty? # unaway msg.user.sync(:away, nil, true) events << [:unaway] else # away msg.user.sync(:away, msg.message, true) events << [:away] end end
@since 2.0.0
# File lib/cinch/irc.rb, line 443 def on_cap(msg, _events) case msg.params[1] when 'LS' @network.capabilities.concat msg.message.split(' ').map(&:to_sym) send_cap_req when 'ACK' if @network.capabilities.include?(:sasl) send_sasl else send_cap_end end when 'NAK' send_cap_end end end
@since 2.0.0
# File lib/cinch/irc.rb, line 460 def on_connect(_msg, _events) @bot.modes = @bot.config.modes end
# File lib/cinch/irc.rb, line 464 def on_join(msg, _events) if msg.user == @bot @bot.channels << msg.channel msg.channel.sync_modes end msg.channel.add_user(msg.user) msg.user.online = true end
# File lib/cinch/irc.rb, line 473 def on_kick(msg, events) target = User(msg.params[1]) if target == @bot @bot.channels.delete(msg.channel) end msg.channel.remove_user(target) set_leaving_user(msg, target, events) end
# File lib/cinch/irc.rb, line 483 def on_kill(msg, events) user = User(msg.params[1]) @bot.channel_list.each do |channel| channel.remove_user(user) end user.unsync_all user.online = false set_leaving_user(msg, user, events) end
@version 1.1.0
# File lib/cinch/irc.rb, line 497 def on_mode(msg, events) if msg.channel? parse_channel_modes(msg, events) return end if msg.params.first == bot.nick parse_bot_modes(msg) end end
# File lib/cinch/irc.rb, line 578 def on_nick(msg, _events) target = if msg.user == @bot # @bot.set_nick msg.params.last @bot else msg.user end target.update_nick(msg.params.last) target.online = true end
# File lib/cinch/irc.rb, line 590 def on_part(msg, events) msg.channel.remove_user(msg.user) msg.user.channels_unsynced.delete msg.channel if msg.user == @bot @bot.channels.delete(msg.channel) end set_leaving_user(msg, msg.user, events) end
# File lib/cinch/irc.rb, line 601 def on_ping(msg, _events) send "PONG :#{msg.params.first}" end
@since 2.0.0
# File lib/cinch/irc.rb, line 628 def on_privmsg(msg, events) if msg.user msg.user.online = true end if msg.message =~ /^\001DCC SEND (?:"([^"]+)"|(\S+)) (\S+) (\d+)(?: (\d+))?\001$/ process_dcc_send(Regexp.last_match(1) || Regexp.last_match(2), Regexp.last_match(3), Regexp.last_match(4), Regexp.last_match(5), msg, events) end end
# File lib/cinch/irc.rb, line 609 def on_quit(msg, events) @bot.channel_list.each do |channel| channel.remove_user(msg.user) end msg.user.unsync_all msg.user.online = false set_leaving_user(msg, msg.user, events) if msg.message.downcase == 'excess flood' && msg.user == @bot @bot.warn ['Looks like your bot has been kicked because of excess flood.', "If you haven't modified the throttling options manually, please file a bug report at https://github.com/cinchrb/cinch/issues and include the following information:", "- Server: #{@bot.config.server}", "- Messages per second: #{@bot.config.messages_per_second}", "- Server queue size: #{@bot.config.server_queue_size}"] end end
# File lib/cinch/irc.rb, line 605 def on_topic(msg, _events) msg.channel.sync(:topic, msg.params[1]) end
# File lib/cinch/irc.rb, line 563 def parse_bot_modes(msg) modes, err = ModeParser.parse_modes(msg.params[1], msg.params[2..-1]) unless err.nil? raise Exceptions::InvalidModeString, err end modes.each do |direction, mode, _| if direction == :add @bot.modes << mode unless @bot.modes.include?(mode) else @bot.modes.delete(mode) end end end
# File lib/cinch/irc.rb, line 507 def parse_channel_modes(msg, events) add_and_remove = @bot.irc.isupport['CHANMODES']['A'] + @bot.irc.isupport['CHANMODES']['B'] + @bot.irc.isupport['PREFIX'].keys param_modes = { add: @bot.irc.isupport['CHANMODES']['C'] + add_and_remove, remove: add_and_remove, } modes, err = ModeParser.parse_modes(msg.params[1], msg.params[2..-1], param_modes) unless err.nil? if @network.ircd != :slack || !err.is_a?(ModeParser::TooManyParametersError) raise Exceptions::InvalidModeString, err end end modes.each do |direction, mode, param| if @bot.irc.isupport['PREFIX'].keys.include?(mode) target = User(param) # (un)set a user-mode if direction == :add msg.channel.users[target] << mode unless msg.channel.users[target].include?(mode) else msg.channel.users[target].delete mode end user_events = { 'o' => 'op', 'v' => 'voice', 'h' => 'halfop', } if user_events.key?(mode) event = (direction == :add ? '' : 'de') + user_events[mode] events << [event.to_sym, target] end elsif @bot.irc.isupport['CHANMODES']['A'].include?(mode) case mode when 'b' process_ban_mode(msg, events, param, direction) when 'q' process_owner_mode(msg, events, param, direction) if @network.owner_list_mode else raise Exceptions::UnsupportedMode, mode end else # channel options if direction == :add msg.channel.modes_unsynced[mode] = param.nil? ? true : param else msg.channel.modes_unsynced.delete(mode) end end end events << [:mode_change, modes] end
# File lib/cinch/irc.rb, line 400 def process_ban_mode(msg, events, param, direction) mask = param ban = Ban.new(mask, msg.user, Time.now) if direction == :add msg.channel.bans_unsynced << ban events << [:ban, ban] else msg.channel.bans_unsynced.delete_if { |b| b.mask == ban.mask } events << [:unban, ban] end end
@since 2.0.0
# File lib/cinch/irc.rb, line 639 def process_dcc_send(filename, ip, port, size, m, events) if ip =~ /^\d+$/ # If ip is a single integer, assume it's a specification # compliant IPv4 address in network byte order. If it's any # other string, assume that it's a valid IPv4 or IPv6 address. # If it's not valid, let someone higher up the chain notice # that. ip = ip.to_i ip = [24, 16, 8, 0].collect { |b| (ip >> b) & 255 }.join('.') end port = port.to_i size = size.to_i @bot.loggers.info 'DCC: Incoming DCC SEND: File name: %s - Size: %dB - IP: %s - Port: %d' % [filename, size, ip, port] dcc = DCC::Incoming::Send.new(user: m.user, filename: filename, size: size, ip: ip, port: port) events << [:dcc_send, dcc] end
# File lib/cinch/irc.rb, line 413 def process_owner_mode(msg, events, param, direction) owner = User(param) if direction == :add msg.channel.owners_unsynced << owner unless msg.channel.owners_unsynced.include?(owner) events << [:owner, owner] else msg.channel.owners_unsynced.delete(owner) events << [:deowner, owner] end end
# File lib/cinch/irc.rb, line 340 def set_leaving_user(_message, user, events) events << [:leaving, user] end
# File lib/cinch/irc.rb, line 424 def update_whois(user, data) @whois_updates[user] ||= {} @whois_updates[user].merge!(data) end