blur 1.8.6 → 2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,9 +11,7 @@ class Exception
11
11
  #
12
12
  # @return Fixnum the line of the script the exception was raised on.
13
13
  def line
14
- if result = backtrace[0].match(Pattern)
15
- result[1].to_i + 1
16
- end
14
+ backtrace[0].match(Pattern)[1].to_i + 1
17
15
  end
18
16
  end
19
17
 
@@ -4,18 +4,18 @@ 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,8 +23,8 @@ 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
@@ -36,32 +36,31 @@ module Blur
36
36
  # Emits +:connection_ready+ with the parameter +network+.
37
37
  #
38
38
  # Automatically joins the channels specified in +:channels+.
39
- def got_end_of_motd network, command
39
+ def got_end_of_motd network, message
40
40
  emit :connection_ready, network
41
41
 
42
- network.options[:channels].each do |channel|
43
- network.transmit :JOIN, channel
42
+ network.options['channels'].each do |channel|
43
+ network.join channel
44
44
  end
45
45
  end
46
46
 
47
47
  # 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|
48
+ def got_name_reply network, message
49
+ name = message.parameters[2] # Channel name.
50
+ nicks = message.parameters[3].split.map do |nick|
51
51
  # Slice the nick if the first character is a user mode prefix.
52
52
  if network.user_prefixes.include? nick.chr
53
53
  nick.slice! 0
54
54
  end
55
55
 
56
- Network::User.new nick
56
+ nick
57
57
  end
58
58
 
59
59
  if channel = find_or_create_channel(name, network)
60
+ users = nicks.map{|nick| find_or_create_user nick, network }
60
61
  users.each do |user|
61
- user.channel = channel
62
- user.network = network
63
-
64
- channel.users << user
62
+ user.channels << channel
63
+ channel.users << user unless channel.users.include? user
65
64
  end
66
65
 
67
66
  emit :channel_who_reply, channel
@@ -72,35 +71,35 @@ module Blur
72
71
  #
73
72
  # == Callbacks:
74
73
  # Emits :topic_change with the parameters +channel+ and +topic+.
75
- def got_channel_topic network, command
76
- me, name, topic = command.params
74
+ def got_channel_topic network, message
75
+ _, channel_name, topic = message.parameters
77
76
 
78
- if channel = find_or_create_channel(name, network)
79
- emit :topic_change, channel, topic
77
+ if channel = find_or_create_channel(channel_name, network)
78
+ emit :channel_topic, channel, topic
80
79
 
81
80
  channel.topic = topic
82
81
  end
83
82
  end
84
83
 
85
84
  # Called when the server needs to verify that we're alive.
86
- def got_ping network, command
87
- network.transmit :PONG, command[0]
85
+ def got_ping network, message
86
+ network.transmit :PONG, message.parameters[0]
87
+
88
+ emit :network_ping, message.parameters[0]
88
89
  end
89
90
 
90
91
  # Called when a user changed nickname.
91
92
  #
92
93
  # == 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
94
+ # Emits :user_rename with the parameters +channel+, +user+ and +new_nick+
95
+ def got_nick network, message
96
+ old_nick = message.prefix.nick
96
97
 
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
98
+ if user = network.users.delete(old_nick)
99
+ new_nick = message.parameters[0]
100
+ emit :user_rename, user, new_nick
101
+ user.nick = new_nick
102
+ network.users[new_nick] = user
104
103
  end
105
104
  end
106
105
 
@@ -113,38 +112,27 @@ module Blur
113
112
  # Emits +:private_message+ with the parameters +user+ and +message+.
114
113
  #
115
114
  # @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"
115
+ def got_privmsg network, message
116
+ return unless message.prefix.nick # Ignore all server privmsgs
117
+ name, msg = message.parameters
133
118
 
134
- channel.encryption = nil
135
- end
136
-
137
- emit :message, user, channel, message
138
- else
139
- # Odd… this shouldn't happen
119
+ if channel = network.channels[name]
120
+ unless user = network.users[message.prefix.nick]
121
+ user = User.new message.prefix.nick, network
140
122
  end
123
+
124
+ user.name = message.prefix.user
125
+ user.host = message.prefix.host
126
+
127
+ emit :message, user, channel, msg
141
128
  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
129
+ unless user = network.users[message.prefix.nick]
130
+ user = User.new message.prefix.nick, network
131
+ user.name = message.prefix.user
132
+ user.host = message.prefix.host
133
+ end
146
134
 
147
- emit :private_message, user, message
135
+ emit :private_message, user, msg
148
136
  end
149
137
  end
150
138
 
@@ -152,18 +140,16 @@ module Blur
152
140
  #
153
141
  # == Callbacks:
154
142
  # 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
143
+ def got_join network, message
144
+ channel_name = message.parameters[0]
145
+
146
+ user = find_or_create_user message.prefix.nick, network
147
+ user.name = message.prefix.user
148
+ user.host = message.prefix.host
158
149
 
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
-
150
+ if channel = find_or_create_channel(channel_name, network)
151
+ _user_join_channel user, channel
152
+
167
153
  emit :user_entered, channel, user
168
154
  end
169
155
  end
@@ -172,12 +158,12 @@ module Blur
172
158
  #
173
159
  # == Callbacks:
174
160
  # Emits +:user_left+ with the parameters +channel+ and +user+.
175
- def got_part network, command
176
- name = command[0]
161
+ def got_part network, message
162
+ channel_name = message.parameters[0]
177
163
 
178
- if channel = network.channel_by_name(name)
179
- if user = channel.user_by_nick(command.sender.nickname)
180
- channel.users.delete user
164
+ if channel = network.channels[channel_name]
165
+ if user = network.users[message.prefix.nick]
166
+ _user_part_channel user, channel
181
167
 
182
168
  emit :user_left, channel, user
183
169
  end
@@ -188,17 +174,17 @@ module Blur
188
174
  #
189
175
  # == Callbacks:
190
176
  # Emits +:user_quit+ with the parameters +channel+ and +user+.
191
- def got_quit network, command
192
- nick = command.sender.nickname
177
+ def got_quit network, message
178
+ nick = message.prefix.nick
179
+ reason = message.parameters[2]
193
180
 
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
181
+ if user = network.users[nick]
182
+ user.channels.each do |channel|
183
+ channel.users.delete user
201
184
  end
185
+
186
+ emit :user_quit, user, reason
187
+ network.users.delete nick
202
188
  end
203
189
  end
204
190
 
@@ -209,13 +195,13 @@ module Blur
209
195
  # and +reason+.
210
196
  #
211
197
  # +kicker+ is the user that kicked +kickee+.
212
- def got_kick network, command
213
- name, target, reason = command.params
198
+ def got_kick network, message
199
+ name, target, reason = message.parameters
214
200
 
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
201
+ if channel = network.channels[name]
202
+ if kicker = network.users[message.prefix.nick]
203
+ if kickee = network.users[target]
204
+ _user_part_channel kickee, channel
219
205
 
220
206
  emit :user_kicked, kicker, channel, kickee, reason
221
207
  end
@@ -227,11 +213,11 @@ module Blur
227
213
  #
228
214
  # == Callbacks:
229
215
  # Emits :topic with the parameters +user+, +channel+ and +topic+.
230
- def got_topic network, command
231
- name, topic = command.params
216
+ def got_topic network, message
217
+ channel_name, topic = message.parameters
232
218
 
233
- if channel = network.channel_by_name(name)
234
- if user = channel.user_by_nick(command.sender.nickname)
219
+ if channel = network.channels[channel_name]
220
+ if user = network.users[message.prefix.nick]
235
221
  emit :topic, user, channel, topic
236
222
  end
237
223
 
@@ -246,29 +232,17 @@ module Blur
246
232
  # Emits +:channel_mode+ with the parameters +channel+ and +modes+.
247
233
  # === When it's user modes:
248
234
  # Emits +:user_mode+ with the parameters +user+ and +modes+.
249
- def got_mode network, command
250
- name, modes, limit, nick, mask = command.params
235
+ def got_mode network, message
236
+ name, modes, limit, nick, mask = message.parameters
251
237
 
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
238
+ if channel = network.channels[name]
239
+ # FIXME
266
240
  end
267
241
  end
268
242
 
269
243
  # Called when the network announces its ISUPPORT parameters.
270
- def got_005 network, command
271
- params = command.params[1..-2]
244
+ def got_005 network, message
245
+ params = message.parameters[1..-2]
272
246
 
273
247
  network.isupport.parse *params
274
248
  end
@@ -280,23 +254,41 @@ module Blur
280
254
 
281
255
  private
282
256
 
283
- def find_or_create_channel name, network, users = []
284
- channel = network.channel_by_name name
257
+ def _user_part_channel user, channel
258
+ user.channels.delete channel
259
+ channel.users.delete user
285
260
 
286
- if channel.nil?
287
- channel = Network::Channel.new name, network, users
288
- network.channels << channel
261
+ # Forget the user if we no longer share any channels.
262
+ if user.channels.empty?
263
+ user.network.users.delete user.nick
264
+ end
265
+ end
289
266
 
290
- if network.fish? and network.options[:fish].key? name
291
- keyphrase = network.options[:fish][name]
292
- channel.encryption = Encryption::FiSH.new keyphrase
293
- end
267
+ def _user_join_channel user, channel
268
+ channel.users << user
269
+ user.channels << channel
270
+ end
271
+
272
+ def find_or_create_user nick, network
273
+ unless user = network.users[nick]
274
+ user = User.new nick, network
275
+ network.users[nick] = user
276
+ emit :user_created, user
277
+ end
278
+
279
+ user
280
+ end
294
281
 
282
+ def find_or_create_channel name, network
283
+ unless channel = network.channels[name]
284
+ channel = Channel.new name, network
285
+ network.channels[name] = channel
295
286
  emit :channel_created, channel
296
287
  end
297
288
 
298
289
  channel
299
290
  end
300
291
  end
292
+
301
293
  end
302
294
  end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ # Temporary replacement for majic's color method.
4
+ class String
5
+ def ^ c
6
+ self
7
+ end
8
+ end
9
+
10
+ module Blur
11
+ # Very crude logging module.
12
+ module Logging
13
+ Levels = [:debug, :info, :warn, :error, :fatal]
14
+
15
+ class Logger
16
+ Levels.each do |level|
17
+ define_method level do |*messages|
18
+ Logging.mutex.synchronize do
19
+ messages.each{|m| puts "%-8s %s" % [level.to_s.upcase, m] }
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ @mutex = Mutex.new
26
+ @logger = Logger.new
27
+
28
+ class << self
29
+ attr_reader :mutex
30
+ attr_reader :logger
31
+ end
32
+
33
+ def log *messages
34
+ if messages.empty?
35
+ Logging.logger
36
+ else
37
+ Logging.logger.debug *messages
38
+ end
39
+ end
40
+ end
41
+ end
@@ -13,12 +13,13 @@ module Blur
13
13
 
14
14
  # @return [Hash] the network options.
15
15
  attr_accessor :options
16
- # @return [Array] the list of channels the client is in.
16
+
17
+ # @return [Hash] the map of users that is known.
18
+ attr_accessor :users
19
+ # @return [Hash] the map of channels the client is in.
17
20
  attr_accessor :channels
18
- # @return [Array] the list of private messages the client remembers.
19
- attr_accessor :dialogues
20
- # @return [Client] the client delegate.
21
- attr_accessor :delegate
21
+ # @return [Client] the client reference.
22
+ attr_accessor :client
22
23
  # @return [Network::Connection] the connection instance.
23
24
  attr_accessor :connection
24
25
  # @return [Network::ISupport] the network isupport specs.
@@ -30,20 +31,17 @@ module Blur
30
31
  # Get the remote hostname.
31
32
  #
32
33
  # @return [String] the remote hostname.
33
- def host; @options[:hostname] end
34
+ def host; @options['hostname'] end
34
35
 
35
36
  # Get the remote port.
36
37
  # If no port is specified, it returns 6697 if using a secure connection,
37
38
  # returns 6667 otherwise.
38
39
  #
39
40
  # @return [Fixnum] the remote port
40
- def port; @options[:port] ||= secure? ? 6697 : 6667 end
41
+ def port; @options['port'] ||= secure? ? 6697 : 6667 end
41
42
 
42
43
  # Check to see if it's a secure connection.
43
- def secure?; @options[:secure] == true end
44
-
45
- # Check to see if FiSH encryption is enabled.
46
- def fish?; not @options[:fish].nil? end
44
+ def secure?; @options['secure'] == true end
47
45
 
48
46
  # Instantiates the network.
49
47
  #
@@ -68,18 +66,24 @@ module Blur
68
66
  # remote certificate matches the specified fingerprint.
69
67
  # @option options [optional, Boolean] :ssl_no_verify Disable verification
70
68
  # alltogether.
71
- def initialize options
72
- @options = options
73
- @channels = []
69
+ def initialize options, client = nil
70
+ @client = client
71
+ @options = options
72
+ @users = {}
73
+ @channels = {}
74
74
  @isupport = ISupport.new self
75
75
 
76
- unless options[:nickname]
77
- raise ArgumentError, "nickname is missing from the networks option block"
76
+ unless options['nickname']
77
+ if options['hostname']
78
+ raise ArgumentError, "Network configuration for `#{options['hostname']}' is missing a nickname"
79
+ else
80
+ raise ArgumentError, "Network configuration is missing a nickname"
81
+ end
78
82
  end
79
83
 
80
- @options[:username] ||= @options[:nickname]
81
- @options[:realname] ||= @options[:username]
82
- @options[:channels] ||= []
84
+ @options['username'] ||= @options['nickname']
85
+ @options['realname'] ||= @options['username']
86
+ @options['channels'] ||= []
83
87
  end
84
88
 
85
89
  # Send a message to a recipient.
@@ -87,16 +91,17 @@ module Blur
87
91
  # @param [String, #to_s] recipient the recipient.
88
92
  # @param [String] message the message.
89
93
  def say recipient, message
90
- if recipient.is_a? Channel and recipient.encrypted?
91
- message = "+OK #{recipient.encryption.encrypt message}"
92
- end
93
-
94
94
  transmit :PRIVMSG, recipient.to_s, message
95
95
  end
96
96
 
97
97
  # Called when the network connection has enough data to form a command.
98
- def got_command command
99
- @delegate.got_command self, command
98
+ def got_message message
99
+ @client.got_message self, message
100
+ rescue => e
101
+ puts "#{e.class}: #{e.message}"
102
+ puts
103
+ puts "---"
104
+ puts e.backtrace
100
105
  end
101
106
 
102
107
  # Find a channel by its name.
@@ -104,7 +109,7 @@ module Blur
104
109
  # @param [String] name the channel name.
105
110
  # @return [Network::Channel] the matching channel, or nil.
106
111
  def channel_by_name name
107
- @channels.find { |channel| channel.name == name }
112
+ @channels.find {|channel| channel.name == name }
108
113
  end
109
114
 
110
115
  # Find all instances of channels in which there is a user with the nick
@@ -113,7 +118,7 @@ module Blur
113
118
  # @param [String] nick the nickname.
114
119
  # @return [Array] a list of channels in which the user is located, or nil.
115
120
  def channels_with_user nick
116
- @channels.select { |channel| channel.user_by_nick nick }
121
+ @channels.select {|channel| channel.user_by_nick nick }
117
122
  end
118
123
 
119
124
  # Returns a list of user prefixes that a nick might contain.
@@ -146,17 +151,18 @@ module Blur
146
151
 
147
152
  # Called when the connection was successfully established.
148
153
  def connected!
149
- transmit :PASS, @options[:password] if @options[:password]
150
- transmit :NICK, @options[:nickname]
151
- transmit :USER, @options[:username], :void, :void, @options[:realname]
154
+ transmit :PASS, @options['password'] if @options['password']
155
+ transmit :NICK, @options['nickname']
156
+ transmit :USER, @options['username'], 'void', 'void', @options['realname']
152
157
  end
153
158
 
154
159
  # Called when the connection was closed.
155
160
  def disconnected!
156
- @channels.each { |channel| channel.users.clear }
161
+ @channels.each {|name, channel| channel.users.clear }
157
162
  @channels.clear
163
+ @users.clear
158
164
 
159
- @delegate.network_connection_closed self
165
+ @client.network_connection_closed self
160
166
  end
161
167
 
162
168
  # Terminate the connection and clear all channels and users.
@@ -169,13 +175,25 @@ module Blur
169
175
  # @param [Symbol, String] name the command name.
170
176
  # @param [...] arguments all the prepended parameters.
171
177
  def transmit name, *arguments
172
- command = Command.new name, arguments
173
- log "#{'→' ^ :red} #{command.name.to_s.ljust(8, ' ') ^ :light_gray} #{command.params.map(&:inspect).join ' '}"
178
+ message = IRCParser::Message.new command: name.to_s, parameters: arguments
179
+
180
+ if @client.verbose
181
+ log "#{'→' ^ :red} #{message.command.to_s.ljust(8, ' ') ^ :light_gray} #{message.parameters.map(&:inspect).join ' '}"
182
+ end
174
183
 
175
- @connection.send_data "#{command}\r\n"
184
+ @connection.send_data "#{message}\r\n"
185
+ end
186
+
187
+ # Send a private message.
188
+ def send_privmsg recipient, message
189
+ transmit :PRIVMSG, recipient, message
190
+ end
191
+
192
+ # Join a channel.
193
+ def join channel
194
+ transmit :JOIN, channel
176
195
  end
177
196
 
178
-
179
197
  # Convert it to a debug-friendly format.
180
198
  def to_s
181
199
  %{#<#{self.class.name} "#{host}":#{port}>}