blur 1.8.6 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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