blur 1.8.6 → 2.1

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.
@@ -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}>}