blur 1.8.6 → 2.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +1,21 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Blur
4
4
  class Client
5
5
  # The +Handling+ module is the very core of the IRC-part in Blur.
6
6
  #
7
- # When the client receives a parsed command instance, it immediately starts
8
- # looking for a got_(the command name) method inside the client, which
7
+ # When the client receives a parsed message instance, it immediately starts
8
+ # looking for a got_(the message name) method inside the client, which
9
9
  # is implemented in this module.
10
10
  #
11
11
  # == Implementing a handler
12
12
  # Implementing a handler is very, very easy.
13
13
  #
14
- # All you need to do is define a method named got_(command you want to
15
- # implement) that accepts 2 parameters, +network+ and +command+.
14
+ # All you need to do is define a method named got_(message you want to
15
+ # implement) that accepts 2 parameters, +network+ and +message+.
16
16
  #
17
- # You can then do whatever you need to do with the command instance,
18
- # you can access the parameters of it through {Network::Command#[]}.
17
+ # You can then do whatever you need to do with the message instance,
18
+ # you can access the parameters of it through {Network::message#[]}.
19
19
  #
20
20
  # Don't forget that this module is inside the clients scope, so you can
21
21
  # access all instance-variables and methods.
@@ -23,87 +23,99 @@ module Blur
23
23
  # @example
24
24
  # # RPL_WHOISUSER
25
25
  # # <nick> <user> <host> * :<real name>
26
- # def got_whois_user network, command
27
- # puts "nick: #{command[0]} user: #{command[1]} host: #{command[2]} "
26
+ # def got_whois_user network, message
27
+ # puts "nick: #{message.parameters[0]} user: #{message.parameters[1]} host: #{message.parameters[2]} ..."
28
28
  # end
29
- #
29
+ #
30
30
  # @see http://www.irchelp.org/irchelp/rfc/chapter6.html
31
31
  module Handling
32
-
33
32
  # Called when the MOTD was received, which also means it is ready.
34
33
  #
35
34
  # == Callbacks:
36
35
  # Emits +:connection_ready+ with the parameter +network+.
37
36
  #
38
37
  # Automatically joins the channels specified in +:channels+.
39
- def got_end_of_motd network, command
38
+ def got_end_of_motd network, _message
40
39
  emit :connection_ready, network
41
-
42
- network.options[:channels].each do |channel|
43
- network.transmit :JOIN, channel
40
+
41
+ network.options['channels'].each do |channel|
42
+ network.join channel
44
43
  end
45
44
  end
46
-
45
+
47
46
  # Called when the namelist of a channel was received.
48
- def got_name_reply network, command
49
- name = command[2]
50
- users = command[3].split.map do |nick|
47
+ def got_name_reply network, message
48
+ name = message.parameters[2] # Channel name.
49
+ nicks = message.parameters[3].split.map do |nick|
51
50
  # Slice the nick if the first character is a user mode prefix.
52
- if network.user_prefixes.include? nick.chr
53
- nick.slice! 0
54
- end
51
+ nick.slice! 0 if network.user_prefixes.include? nick.chr
55
52
 
56
- Network::User.new nick
53
+ nick
57
54
  end
58
-
59
- if channel = find_or_create_channel(name, network)
60
- users.each do |user|
61
- user.channel = channel
62
- user.network = network
63
55
 
64
- channel.users << user
65
- end
56
+ return unless (channel = find_or_create_channel(name, network))
66
57
 
67
- emit :channel_who_reply, channel
58
+ users = nicks.map { |nick| find_or_create_user nick, network }
59
+ users.each do |user|
60
+ user.channels << channel
61
+ channel.users << user unless channel.users.include? user
68
62
  end
63
+
64
+ emit :channel_who_reply, channel
69
65
  end
70
-
66
+
71
67
  # Called when a channel topic was changed.
72
68
  #
73
69
  # == Callbacks:
74
70
  # Emits :topic_change with the parameters +channel+ and +topic+.
75
- def got_channel_topic network, command
76
- me, name, topic = command.params
77
-
78
- if channel = find_or_create_channel(name, network)
79
- emit :topic_change, channel, topic
71
+ def got_channel_topic network, message
72
+ _, channel_name, topic = message.parameters
80
73
 
81
- channel.topic = topic
82
- end
74
+ return unless (channel = find_or_create_channel(channel_name, network))
75
+
76
+ emit :channel_topic, channel, topic
77
+
78
+ channel.topic = topic
83
79
  end
84
-
80
+
85
81
  # Called when the server needs to verify that we're alive.
86
- def got_ping network, command
87
- network.transmit :PONG, command[0]
82
+ def got_ping network, message
83
+ network.last_pong_time = Time.now
84
+ network.transmit :PONG, message.parameters[0]
85
+
86
+ emit :network_ping, network, message.parameters[0]
88
87
  end
89
-
88
+
89
+ # Called when the server reponds to our periodic PINGs.
90
+ def got_pong network, message
91
+ network.last_pong_time = Time.now
92
+
93
+ emit :network_pong, network, message.parameters[0]
94
+ end
95
+
90
96
  # Called when a user changed nickname.
91
97
  #
92
98
  # == Callbacks:
93
- # Emits :user_rename with the parameters +channel+, +user+, +old_nick and +new_nick+
94
- def got_nick network, command
95
- nick = command.sender.nickname
96
-
97
- if channels = network.channels_with_user(nick)
98
- channels.each do |channel|
99
- if user = channel.user_by_nick(nick)
100
- emit :user_rename, channel, user, user.nick, command[0]
101
- user.nick = command[0]
102
- end
103
- end
99
+ # Emits :user_rename with the parameters +channel+, +user+ and +new_nick+
100
+ def got_nick network, message
101
+ old_nick = message.prefix.nick
102
+
103
+ # Update or own nickname if has been changed by the server
104
+ if network.nickname == old_nick
105
+ new_nick = message.parameters[0]
106
+
107
+ emit :nick, new_nick
108
+ network.nickname = new_nick
104
109
  end
110
+
111
+ return unless (user = network.users.delete(old_nick))
112
+
113
+ new_nick = message.parameters[0]
114
+ emit :user_rename, user, new_nick
115
+ user.nick = new_nick
116
+ network.users[new_nick] = user
105
117
  end
106
-
118
+
107
119
  # Called when a message was received (both channel and private messages).
108
120
  #
109
121
  # == Callbacks:
@@ -113,95 +125,82 @@ module Blur
113
125
  # Emits +:private_message+ with the parameters +user+ and +message+.
114
126
  #
115
127
  # @note Messages are contained as strings.
116
- def got_privmsg network, command
117
- return if command.sender.is_a? String # Ignore all server privmsgs
118
- name, message = command.params
119
-
120
- if channel = network.channel_by_name(name)
121
- if user = channel.user_by_nick(command.sender.nickname)
122
- user.name = command.sender.username
123
- user.host = command.sender.hostname
124
-
125
- begin
126
- if message[0..3] == "+OK " and channel.encrypted?
127
- message = channel.encryption.decrypt message[4..-1]
128
- end
129
- rescue Encryption::BadInputError
130
- puts "-!- FiSH: #{$!.message}"
131
- rescue => exception
132
- puts "-!- There was a problem with the FiSH encryption, disabling"
133
-
134
- channel.encryption = nil
135
- end
136
-
137
- emit :message, user, channel, message
138
- else
139
- # Odd… this shouldn't happen
128
+ def got_privmsg network, message
129
+ return unless message.prefix.nick # Ignore all server privmsgs
130
+
131
+ name, msg = message.parameters
132
+
133
+ if (channel = network.channels[name])
134
+ unless (user = network.users[message.prefix.nick])
135
+ user = User.new message.prefix.nick, network
140
136
  end
137
+
138
+ user.name = message.prefix.user
139
+ user.host = message.prefix.host
140
+
141
+ emit :message, user, channel, msg
141
142
  else # This is a private message
142
- user = Network::User.new command.sender.nickname
143
- user.name = command.sender.username
144
- user.host = command.sender.hostname
145
- user.network = network
143
+ unless (user = network.users[message.prefix.nick])
144
+ user = User.new message.prefix.nick, network
145
+ user.name = message.prefix.user
146
+ user.host = message.prefix.host
147
+ end
146
148
 
147
- emit :private_message, user, message
149
+ emit :private_message, user, msg
148
150
  end
149
151
  end
150
-
152
+
151
153
  # Called when a user joined a channel.
152
154
  #
153
155
  # == Callbacks:
154
156
  # Emits +:user_entered+ with the parameters +channel+ and +user+.
155
- def got_join network, command
156
- name = command[0]
157
- user = Network::User.new command.sender.nickname
158
-
159
- if channel = network.channel_by_name(name)
160
- user.name = command.sender.username
161
- user.host = command.sender.hostname
162
- user.channel = channel
163
- user.network = network
164
-
165
- channel.users << user
166
-
167
- emit :user_entered, channel, user
168
- end
157
+ def got_join network, message
158
+ channel_name = message.parameters[0]
159
+
160
+ user = find_or_create_user message.prefix.nick, network
161
+ user.name = message.prefix.user
162
+ user.host = message.prefix.host
163
+
164
+ return unless (channel = find_or_create_channel(channel_name, network))
165
+
166
+ _user_join_channel user, channel
167
+
168
+ emit :user_entered, channel, user
169
169
  end
170
-
170
+
171
171
  # Called when a user left a channel.
172
172
  #
173
173
  # == Callbacks:
174
174
  # Emits +:user_left+ with the parameters +channel+ and +user+.
175
- def got_part network, command
176
- name = command[0]
177
-
178
- if channel = network.channel_by_name(name)
179
- if user = channel.user_by_nick(command.sender.nickname)
180
- channel.users.delete user
181
-
182
- emit :user_left, channel, user
183
- end
184
- end
175
+ def got_part network, message
176
+ channel_name = message.parameters[0]
177
+
178
+ return unless (channel = network.channels[channel_name])
179
+ return unless (user = network.users[message.prefix.nick])
180
+
181
+ _user_part_channel user, channel
182
+
183
+ emit :user_left, channel, user
185
184
  end
186
-
185
+
187
186
  # Called when a user disconnected from a network.
188
187
  #
189
188
  # == Callbacks:
190
189
  # Emits +:user_quit+ with the parameters +channel+ and +user+.
191
- def got_quit network, command
192
- nick = command.sender.nickname
193
-
194
- if channels = network.channels_with_user(nick)
195
- channels.each do |channel|
196
- if user = channel.user_by_nick(nick)
197
- channel.users.delete user
198
-
199
- emit :user_quit, channel, user
200
- end
201
- end
190
+ def got_quit network, message
191
+ nick = message.prefix.nick
192
+ reason = message.parameters[2]
193
+
194
+ return unless (user = network.users[nick])
195
+
196
+ user.channels.each do |channel|
197
+ channel.users.delete user
202
198
  end
199
+
200
+ emit :user_quit, user, reason
201
+ network.users.delete nick
203
202
  end
204
-
203
+
205
204
  # Called when a user was kicked from a channel.
206
205
  #
207
206
  # == Callbacks:
@@ -209,34 +208,32 @@ module Blur
209
208
  # and +reason+.
210
209
  #
211
210
  # +kicker+ is the user that kicked +kickee+.
212
- def got_kick network, command
213
- name, target, reason = command.params
214
-
215
- if channel = network.channel_by_name(name)
216
- if kicker = channel.user_by_nick(command.sender.nickname)
217
- if kickee = channel.user_by_nick(target)
218
- channel.users.delete kickee
219
-
220
- emit :user_kicked, kicker, channel, kickee, reason
221
- end
222
- end
223
- end
211
+ def got_kick network, message
212
+ name, target, reason = message.parameters
213
+
214
+ return unless (channel = network.channels[name])
215
+ return unless (kicker = network.users[message.prefix.nick])
216
+ return unless (kickee = network.users[target])
217
+
218
+ _user_part_channel kickee, channel
219
+
220
+ emit :user_kicked, kicker, channel, kickee, reason
224
221
  end
225
-
222
+
226
223
  # Called when a topic was changed for a channel.
227
224
  #
228
225
  # == Callbacks:
229
226
  # Emits :topic with the parameters +user+, +channel+ and +topic+.
230
- def got_topic network, command
231
- name, topic = command.params
232
-
233
- if channel = network.channel_by_name(name)
234
- if user = channel.user_by_nick(command.sender.nickname)
235
- emit :topic, user, channel, topic
236
- end
237
-
238
- channel.topic = topic
227
+ def got_topic network, message
228
+ _channel_name, topic = message.parameters
229
+
230
+ return unless (channel = network.channels[channel.name])
231
+
232
+ if (user = network.users[message.prefix.nick])
233
+ emit :topic, user, channel, topic
239
234
  end
235
+
236
+ channel.topic = topic
240
237
  end
241
238
 
242
239
  # Called when a channel or a users flags was altered.
@@ -246,52 +243,135 @@ module Blur
246
243
  # Emits +:channel_mode+ with the parameters +channel+ and +modes+.
247
244
  # === When it's user modes:
248
245
  # Emits +:user_mode+ with the parameters +user+ and +modes+.
249
- def got_mode network, command
250
- name, modes, limit, nick, mask = command.params
251
-
252
- if channel = network.channel_by_name(name)
253
- if limit
254
- unless limit.numeric?
255
- nick = limit
256
- end
257
-
258
- if user = channel.user_by_nick(nick)
259
- user.merge_modes modes
260
- emit :user_mode, user, modes
261
- end
262
- else
263
- channel.merge_modes modes
264
- emit :channel_mode, channel, modes
265
- end
266
- end
246
+ def got_mode network, message
247
+ name, _modes, _limit, _nick, _mask = message.parameters
248
+
249
+ return unless (_channel = network.channels[name])
267
250
  end
268
251
 
269
252
  # Called when the network announces its ISUPPORT parameters.
270
- def got_005 network, command
271
- params = command.params[1..-2]
253
+ def got_005 network, message
254
+ params = message.parameters[1..-2]
272
255
 
273
- network.isupport.parse *params
256
+ network.isupport.parse(*params)
274
257
  end
275
258
 
276
- alias_method :got_353, :got_name_reply
277
- alias_method :got_422, :got_end_of_motd
278
- alias_method :got_376, :got_end_of_motd
279
- alias_method :got_332, :got_channel_topic
259
+ # Received when the server supports capability negotiation.
260
+ #
261
+ # CAP * LS :multi-prefix sasl
262
+ def got_cap network, message
263
+ _id, command = message.parameters[0..1]
264
+
265
+ case command
266
+ when 'LS'
267
+ capabilities = message.parameters[2]&.split
268
+ req = []
280
269
 
281
- private
270
+ req << 'sasl' if network.sasl? && capabilities.include?('sasl')
271
+ req << 'account-tag' if capabilities.include?('account-tag')
282
272
 
283
- def find_or_create_channel name, network, users = []
284
- channel = network.channel_by_name name
273
+ network.transmit :CAP, 'REQ', req.join(' ') if req.any?
285
274
 
286
- if channel.nil?
287
- channel = Network::Channel.new name, network, users
288
- network.channels << channel
275
+ capabilities.each { |name| network.capabilities.push name }
289
276
 
290
- if network.fish? and network.options[:fish].key? name
291
- keyphrase = network.options[:fish][name]
292
- channel.encryption = Encryption::FiSH.new keyphrase
277
+ emit :network_capabilities, network, capabilities
278
+
279
+ when 'ACK'
280
+ capabilities = message.parameters[2]&.split
281
+
282
+ network.transmit :AUTHENTICATE, 'PLAIN' if capabilities&.include?('sasl') && network.sasl?
283
+
284
+ network.cap_end if network.waiting_for_cap
285
+ when 'NAK'
286
+ capabilities = message.parameters[2]&.split
287
+
288
+ if capabilities&.include?('sasl') && network.sasl?
289
+ puts "The server does not support SASL, but you've configured it " \
290
+ 'as such! Disconnecting!'
291
+
292
+ network.disconnect
293
293
  end
294
+ end
295
+ end
296
+
297
+ def got_001 network, message
298
+ network.abort_cap_neg if network.waiting_for_cap
299
+
300
+ # Get our nickname from the server
301
+ nickname = message.parameters[0]
302
+ network.nickname = nickname
303
+ end
304
+
305
+ def got_authenticate network, message
306
+ case message.parameters[0]
307
+ when '+'
308
+ return unless network.sasl?
309
+
310
+ sasl = network.options['sasl']
311
+
312
+ response = "#{sasl['username']}\x00#{sasl['username']}\x00#{sasl['password']}"
313
+ network.transmit :AUTHENTICATE, Base64.encode64(response).strip
314
+ end
315
+ end
316
+
317
+ # :server 900 <nick> <nick>!<ident>@<host> <account> :You are now logged in as <user>
318
+ # RPL_LOGGEDIN SASL
319
+ def got_900 network, _message
320
+ network.cap_end if network.waiting_for_cap
321
+ end
322
+
323
+ # :server 904 <nick> :SASL authentication failed
324
+ # ERR_SASLFAIL
325
+ def got_904 network, message
326
+ _nick, _message = message.parameters
327
+
328
+ puts 'SASL authentication failed! Disconnecting!'
329
+
330
+ network.disconnect
331
+ end
332
+
333
+ def got_nickname_in_use network, message
334
+ nickname = network.options['nickname']
335
+ network.transmit :NICK, "#{nickname}_"
336
+ end
337
+
338
+ alias got_353 got_name_reply
339
+ alias got_422 got_end_of_motd
340
+ alias got_376 got_end_of_motd
341
+ alias got_332 got_channel_topic
342
+ alias got_433 got_nickname_in_use
343
+
344
+ private
345
+
346
+ def _user_part_channel user, channel
347
+ user.channels.delete channel
348
+ channel.users.delete user
349
+
350
+ # Forget the user if we no longer share any channels.
351
+ return unless user.channels.empty?
352
+
353
+ user.network.users.delete user.nick
354
+ end
355
+
356
+ def _user_join_channel user, channel
357
+ channel.users << user
358
+ user.channels << channel
359
+ end
360
+
361
+ def find_or_create_user nick, network
362
+ unless (user = network.users[nick])
363
+ user = User.new nick, network
364
+ network.users[nick] = user
365
+ emit :user_created, user
366
+ end
367
+
368
+ user
369
+ end
294
370
 
371
+ def find_or_create_channel name, network
372
+ unless (channel = network.channels[name])
373
+ channel = Channel.new name, network
374
+ network.channels[name] = channel
295
375
  emit :channel_created, channel
296
376
  end
297
377