blur 1.8.6 → 2.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +10 -24
- data/executables/blur +63 -0
- data/library/blur/callbacks.rb +53 -0
- data/library/blur/channel.rb +78 -0
- data/library/blur/client.rb +165 -107
- data/library/blur/handling.rb +259 -179
- data/library/blur/network/connection.rb +26 -30
- data/library/blur/network/isupport.rb +38 -32
- data/library/blur/network.rb +193 -55
- data/library/blur/script.rb +132 -116
- data/library/blur/script_cache.rb +45 -0
- data/library/blur/user.rb +122 -0
- data/library/blur/version.rb +3 -3
- data/library/blur.rb +46 -19
- metadata +35 -28
- data/library/blur/encryption/base64.rb +0 -71
- data/library/blur/encryption/fish.rb +0 -80
- data/library/blur/encryption.rb +0 -17
- data/library/blur/enhancements.rb +0 -41
- data/library/blur/evaluable.rb +0 -13
- data/library/blur/extension.rb +0 -42
- data/library/blur/network/channel.rb +0 -91
- data/library/blur/network/command.rb +0 -83
- data/library/blur/network/user.rb +0 -106
- data/library/blur/script/cache.rb +0 -77
- data/library/blur/script/commands.rb +0 -77
- data/library/blur/script/dsl.rb +0 -52
data/library/blur/handling.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
#
|
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
|
8
|
-
# looking for a got_(the
|
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_(
|
15
|
-
# implement) that accepts 2 parameters, +network+ and +
|
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
|
18
|
-
# you can access the parameters of it through {Network::
|
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,
|
27
|
-
# puts "nick: #{
|
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,
|
38
|
+
def got_end_of_motd network, _message
|
40
39
|
emit :connection_ready, network
|
41
|
-
|
42
|
-
network.options[
|
43
|
-
network.
|
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,
|
49
|
-
name =
|
50
|
-
|
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
|
-
|
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
|
-
|
65
|
-
end
|
56
|
+
return unless (channel = find_or_create_channel(name, network))
|
66
57
|
|
67
|
-
|
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,
|
76
|
-
|
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
|
-
|
82
|
-
|
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,
|
87
|
-
network.
|
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
|
94
|
-
def got_nick network,
|
95
|
-
|
96
|
-
|
97
|
-
if
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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,
|
117
|
-
return
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
user
|
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 =
|
143
|
-
|
144
|
-
|
145
|
-
|
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,
|
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,
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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,
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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,
|
192
|
-
nick =
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
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,
|
213
|
-
name, target, reason =
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
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,
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
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,
|
250
|
-
name,
|
251
|
-
|
252
|
-
|
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,
|
271
|
-
params =
|
253
|
+
def got_005 network, message
|
254
|
+
params = message.parameters[1..-2]
|
272
255
|
|
273
|
-
network.isupport.parse
|
256
|
+
network.isupport.parse(*params)
|
274
257
|
end
|
275
258
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
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
|
-
|
270
|
+
req << 'sasl' if network.sasl? && capabilities.include?('sasl')
|
271
|
+
req << 'account-tag' if capabilities.include?('account-tag')
|
282
272
|
|
283
|
-
|
284
|
-
channel = network.channel_by_name name
|
273
|
+
network.transmit :CAP, 'REQ', req.join(' ') if req.any?
|
285
274
|
|
286
|
-
|
287
|
-
channel = Network::Channel.new name, network, users
|
288
|
-
network.channels << channel
|
275
|
+
capabilities.each { |name| network.capabilities.push name }
|
289
276
|
|
290
|
-
|
291
|
-
|
292
|
-
|
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
|
|