Class: Blur::Network

Inherits:
Object
  • Object
show all
Defined in:
library/blur/network.rb,
library/blur/network/isupport.rb,
library/blur/network/connection.rb

Overview

The Network module is to be percieved as an IRC network.

Although the connection is a part of the network module, it is mainly used for network-related structures, such as User, Channel and Command.

Defined Under Namespace

Classes: Connection, ConnectionError, ISupport

Constant Summary collapse

DEFAULT_PING_INTERVAL =
30
DEFAULT_RECONNECT =
true

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options, client = nil) ⇒ Network

Instantiates the network.

Parameters:

  • options (Hash)

    The network options.

Options Hash (options):

  • :hostname (String)

    The hostname or IP-address we want to connect to.

  • :nickname (String)

    The nickname to use.

  • :username (optional, String) — default: Copies :nickname

    The username to use. This is also known as the ident.

  • :realname (optional, String) — default: Copies :username

    The “real name” that we want to use. This is usually what shows up as “Name” when you whois a user.

  • :password (optional, String)

    The password for the network. This is sometimes needed for private networks.

  • :port (optional, Fixnum) — default: 6697 if ssl, otherwise 6667

    The remote port we want to connect to.

  • :secure (optional, Boolean)

    Set whether this is a secure (SSL-encrypted) connection.

  • :ssl_cert_file (optional, String)

    Local path of a readable file that contains a X509 CA certificate to validate against.

  • :ssl_fingerprint (optional, String)

    Validate that the remote certificate matches the specified fingerprint.

  • :ssl_no_verify (optional, Boolean)

    Disable verification alltogether.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'library/blur/network.rb', line 108

def initialize options, client = nil
  @client = client
  @options = options
  # @log = ::Logging.logger[self]
  @users = {}
  @channels = {}
  @isupport = ISupport.new self
  @capabilities = []
  @reconnect_interval = 3
  @server_ping_interval_max = @options.fetch('server_ping_interval',
                                             150).to_i

  unless options['nickname']
    raise ArgumentError, 'Network configuration for ' \
      "`#{id}' is missing a nickname"
  end

  @nickname = options['nickname']
  @options['username'] ||= @options['nickname']
  @options['realname'] ||= @options['username']
  @options['channels'] ||= []
  @id = options.fetch 'id', "#{host}:#{port}"
end

Instance Attribute Details

#capabilitiesArray<String>

Returns list of capabilities supported by the network.

Returns:

  • (Array<String>)

    list of capabilities supported by the network.



39
40
41
# File 'library/blur/network.rb', line 39

def capabilities
  @capabilities
end

#channelsHash

Returns the map of channels the client is in.

Returns:

  • (Hash)

    the map of channels the client is in.



31
32
33
# File 'library/blur/network.rb', line 31

def channels
  @channels
end

#clientClient

Returns the client reference.

Returns:

  • (Client)

    the client reference.



33
34
35
# File 'library/blur/network.rb', line 33

def client
  @client
end

#connectionNetwork::Connection

Returns the connection instance.

Returns:



35
36
37
# File 'library/blur/network.rb', line 35

def connection
  @connection
end

#idString (readonly)

Returns a unique identifier for this network.

You can override the id in your network configuration by setting an ‘id’ key with the id you want.. If no id is specified, the the id will be constructed from the hostname and port number in the format “<host>:<port>”

Returns:

  • (String)

    the unique identifier for this network.



23
24
25
# File 'library/blur/network.rb', line 23

def id
  @id
end

#isupportNetwork::ISupport

Returns the network isupport specs.

Returns:



37
38
39
# File 'library/blur/network.rb', line 37

def isupport
  @isupport
end

#last_pong_timeTime

Returns the last time a pong was sent or received.

Returns:

  • (Time)

    the last time a pong was sent or received.



43
44
45
# File 'library/blur/network.rb', line 43

def last_pong_time
  @last_pong_time
end

#nicknameString

Returns the current nickname.

Returns:

  • (String)

    the current nickname.



25
26
27
# File 'library/blur/network.rb', line 25

def nickname
  @nickname
end

#optionsHash

Returns the network options.

Returns:

  • (Hash)

    the network options.



27
28
29
# File 'library/blur/network.rb', line 27

def options
  @options
end

#server_ping_interval_maxNumber

Note:

the actual time until a client PING is sent can vary by an additional 0-30 seconds.

The max PING interval for the server. This is used to determine when the client will attempt to send its own PING command.

Returns:

  • (Number)

    the max interval between pings from a server.



50
51
52
# File 'library/blur/network.rb', line 50

def server_ping_interval_max
  @server_ping_interval_max
end

#usersHash

Returns the map of users that is known.

Returns:

  • (Hash)

    the map of users that is known.



29
30
31
# File 'library/blur/network.rb', line 29

def users
  @users
end

#waiting_for_capBoolean (readonly)

Returns true if we’re waiting for a capability negotiation.

Returns:

  • (Boolean)

    true if we’re waiting for a capability negotiation.



41
42
43
# File 'library/blur/network.rb', line 41

def waiting_for_cap
  @waiting_for_cap
end

Instance Method Details

#abort_cap_negObject

Called when the server doesn’t support capability negotiation.



258
259
260
261
262
# File 'library/blur/network.rb', line 258

def abort_cap_neg
  @waiting_for_cap = false

  puts 'Server does not support capability negotiation'
end

#cap_endObject

Called when we’re done with capability negotiation.



265
266
267
268
269
# File 'library/blur/network.rb', line 265

def cap_end
  @waiting_for_cap = false

  transmit :CAP, 'END'
end

#channel_by_name(name) ⇒ Network::Channel

Find a channel by its name.

Parameters:

  • name (String)

    the channel name.

Returns:

  • (Network::Channel)

    the matching channel, or nil.



156
157
158
# File 'library/blur/network.rb', line 156

def channel_by_name name
  @channels.find { |channel| channel.name == name }
end

#channel_flagsArray<String>

Returns a list of channel flags (channel mode D).

Returns:

  • (Array<String>)

    a list of channel flags.



186
187
188
# File 'library/blur/network.rb', line 186

def channel_flags
  isupport['CHANMODES']['D']
end

#channels_with_user(nick) ⇒ Array

Find all instances of channels in which there is a user with the nick nick.

Parameters:

  • nick (String)

    the nickname.

Returns:

  • (Array)

    a list of channels in which the user is located, or nil.



165
166
167
# File 'library/blur/network.rb', line 165

def channels_with_user nick
  @channels.select { |channel| channel.user_by_nick nick }
end

#connectObject

Attempt to establish a connection and send initial data.

See Also:



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'library/blur/network.rb', line 193

def connect
  # @log.info "Connecting to #{self}"

  begin
    @connection = EventMachine.connect host, port, Connection, self
  rescue EventMachine::ConnectionError => e
    warn "Establishing connection to #{self} failed!"
    warn e.message

    schedule_reconnect
    return
  end

  @ping_timer = EventMachine.add_periodic_timer DEFAULT_PING_INTERVAL do
    periodic_ping_check
  end
end

#connected!Object

Called when the connection was successfully established.



245
246
247
248
249
250
251
252
253
254
255
# File 'library/blur/network.rb', line 245

def connected!
  @waiting_for_cap = true
  @capabilities.clear

  transmit :CAP, 'LS'
  transmit :PASS, @options['password'] if @options['password']
  transmit :NICK, @options['nickname']
  transmit :USER, @options['username'], 'void', 'void', @options['realname']

  @last_pong_time = Time.now
end

#connected?Boolean

Check whether or not connection is established.

Returns:

  • (Boolean)


53
54
55
# File 'library/blur/network.rb', line 53

def connected?
  @connection&.established?
end

#disconnectObject

Terminate the connection and clear all channels and users.



287
288
289
# File 'library/blur/network.rb', line 287

def disconnect
  @connection.close_connection_after_writing
end

#disconnected!Object

Called when the connection was closed.



272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'library/blur/network.rb', line 272

def disconnected!
  @channels.each { |_, channel| channel.users.clear }
  @channels.clear
  @users.clear
  @ping_timer.cancel

  # @log.debug "Connection to #{self} lost!"
  @client.network_connection_closed self

  return unless @options.fetch('reconnect', DEFAULT_RECONNECT)

  schedule_reconnect
end

#got_message(message) ⇒ Object

Forwards the received message to the client instance.

Called when the network connection has enough data to form a command.



143
144
145
146
147
148
149
150
# File 'library/blur/network.rb', line 143

def got_message message
  @client.got_message self, message
rescue StandardError => e
  puts "#{e.class}: #{e.message}"
  puts
  puts '---'
  puts e.backtrace
end

#hostString

Get the remote hostname.

Returns:

  • (String)

    the remote hostname.



60
61
62
# File 'library/blur/network.rb', line 60

def host
  @options['hostname']
end

#join(channel) ⇒ Object

Join a channel.



313
314
315
# File 'library/blur/network.rb', line 313

def join channel
  transmit :JOIN, channel
end

#periodic_ping_checkObject



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'library/blur/network.rb', line 226

def periodic_ping_check
  now = Time.now
  seconds_since_pong = now - @last_pong_time

  return unless seconds_since_pong >= @server_ping_interval_max

  # @log.info "No PING request from the server in #{seconds_since_pong}s!"

  transmit 'PING', now.to_s

  # Wait 15 seconds and declare a timeout if we didn't get a PONG.
  previous_pong_time = @last_pong_time.dup

  EventMachine.add_timer 15 do
    server_connection_timeout if @last_pong_time == previous_pong_time
  end
end

#portFixnum

Get the remote port. If no port is specified, it returns 6697 if using a secure connection, returns 6667 otherwise.

Returns:

  • (Fixnum)

    the remote port



69
70
71
# File 'library/blur/network.rb', line 69

def port
  @options['port'] ||= secure? ? 6697 : 6667
end

#sasl?Boolean

Returns whether we want to authenticate with SASL.

Returns:

  • (Boolean)

    whether we want to authenticate with SASL.



79
80
81
82
83
# File 'library/blur/network.rb', line 79

def sasl?
  @options['sasl'] &&
    @options['sasl']['username'] &&
    @options['sasl']['password']
end

#say(recipient, message) ⇒ Object

Send a message to a recipient.

Parameters:

  • recipient (String, #to_s)

    the recipient.

  • message (String)

    the message.



136
137
138
# File 'library/blur/network.rb', line 136

def say recipient, message
  transmit :PRIVMSG, recipient.to_s, message
end

#schedule_reconnectObject

Schedules a reconnect after a user-specified number of seconds.



212
213
214
215
216
217
218
# File 'library/blur/network.rb', line 212

def schedule_reconnect
  # @log.info "Reconnecting to #{self} in #{@reconnect_interval} seconds"

  EventMachine.add_timer @reconnect_interval do
    connect
  end
end

#secure?Boolean

Check to see if it’s a secure connection.

Returns:

  • (Boolean)


74
75
76
# File 'library/blur/network.rb', line 74

def secure?
  @options['secure'] == true
end

#send_privmsg(recipient, message) ⇒ Object

Send a private message.



308
309
310
# File 'library/blur/network.rb', line 308

def send_privmsg recipient, message
  transmit :PRIVMSG, recipient, message
end

#server_connection_timeoutObject



220
221
222
223
224
# File 'library/blur/network.rb', line 220

def server_connection_timeout
  @connection.close_connection

  warn "Connection to #{self} timed out"
end

#to_sObject

Convert it to a debug-friendly format.



318
319
320
# File 'library/blur/network.rb', line 318

def to_s
  %(#<#{self.class.name} "#{host}":#{port}>)
end

#transmit(name, *arguments) ⇒ Object

Transmit a command to the server.

Parameters:

  • name (Symbol, String)

    the command name.

  • arguments (...)

    all the prepended parameters.



295
296
297
298
299
300
301
302
303
304
305
# File 'library/blur/network.rb', line 295

def transmit name, *arguments
  message = IRCParser::Message.new command: name.to_s, parameters: arguments

  if @client.verbose
    formatted_command = message.command.to_s.ljust 8, ' '
    formatted_params = message.parameters.map(&:inspect).join ' '
    puts "#{formatted_command} #{formatted_params}"
  end

  @connection.send_data "#{message}\r\n"
end

#user_prefix_modesArray<String>

Returns a list of user modes that also gives a users nick a prefix.

Returns:

  • (Array<String>)

    a list of user modes.



179
180
181
# File 'library/blur/network.rb', line 179

def user_prefix_modes
  isupport['PREFIX'].keys
end

#user_prefixesArray<String>

Returns a list of user prefixes that a nick might contain.

Returns:

  • (Array<String>)

    a list of user prefixes.



172
173
174
# File 'library/blur/network.rb', line 172

def user_prefixes
  isupport['PREFIX'].values
end