ircguerilla-irc 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/lib/irc/client/command_handler.rb +2 -2
  2. data/lib/irc/client/connected_state.rb +53 -8
  3. data/lib/irc/client/connection.rb +41 -6
  4. data/lib/irc/client/connection_listener.rb +26 -11
  5. data/lib/irc/client/connection_state.rb +15 -2
  6. data/lib/irc/client/context.rb +49 -11
  7. data/lib/irc/client/disconnected_state.rb +28 -16
  8. data/lib/irc/client/message_handler.rb +40 -16
  9. data/lib/irc/client/registered_state.rb +18 -0
  10. data/lib/irc/client/unregistered_state.rb +51 -16
  11. data/lib/irc/commands/nick_command.rb +14 -0
  12. data/lib/irc/messages/error_join_message.rb +1 -1
  13. data/lib/irc/messages/error_message.rb +53 -24
  14. data/lib/irc/messages/error_nick_name_in_use_message.rb +70 -0
  15. data/lib/irc/messages/factory.rb +16 -0
  16. data/lib/irc/messages/isupport_message.rb +86 -0
  17. data/lib/irc/messages/kick_message.rb +114 -0
  18. data/lib/irc/messages/message.rb +21 -1
  19. data/lib/irc/messages/nick_message.rb +17 -2
  20. data/lib/irc/messages/pong_message.rb +1 -1
  21. data/lib/irc/messages/welcome_message.rb +72 -0
  22. data/lib/irc/util/color.rb +77 -0
  23. data/lib/irc/util/nick_generator.rb +58 -0
  24. data/test/functional/irc/client/connection_test.rb +34 -1
  25. data/test/functional/irc/client/context_test.rb +5 -2
  26. data/test/mocks/test/irc/client/connection.rb +128 -0
  27. data/test/mocks/test/irc/client/disconnected_state.rb +44 -0
  28. data/test/test_helper.rb +1 -1
  29. data/test/unit/irc/client/command_handler_test.rb +12 -12
  30. data/test/unit/irc/client/message_handler_test.rb +136 -0
  31. data/test/unit/irc/commands/command_test.rb +87 -84
  32. data/test/unit/irc/commands/join_command_test.rb +3 -1
  33. data/test/unit/irc/commands/nick_command_test.rb +7 -1
  34. data/test/unit/irc/commands/ping_command_test.rb +12 -16
  35. data/test/unit/irc/commands/pong_command_test.rb +2 -1
  36. data/test/unit/irc/messages/error_join_message_test.rb +23 -2
  37. data/test/unit/irc/messages/error_message_test.rb +56 -0
  38. data/test/unit/irc/messages/error_nick_name_in_use_message_test.rb +56 -0
  39. data/test/unit/irc/messages/factory_test.rb +25 -0
  40. data/test/unit/irc/messages/isupport_message_test.rb +138 -0
  41. data/test/unit/irc/messages/join_message_test.rb +22 -1
  42. data/test/unit/irc/messages/kick_message_test.rb +76 -0
  43. data/test/unit/irc/messages/message_test.rb +33 -0
  44. data/test/unit/irc/messages/nick_message_test.rb +22 -1
  45. data/test/unit/irc/messages/notice_message_test.rb +22 -1
  46. data/test/unit/irc/messages/part_message_test.rb +22 -1
  47. data/test/unit/irc/messages/ping_message_test.rb +21 -1
  48. data/test/unit/irc/messages/pong_message_test.rb +21 -1
  49. data/test/unit/irc/messages/private_message_test.rb +22 -1
  50. data/test/unit/irc/messages/welcome_message_test.rb +59 -0
  51. data/test/unit/irc/util/color_test.rb +97 -0
  52. data/test/unit/irc/util/nick_generator_test.rb +52 -0
  53. metadata +69 -52
@@ -56,7 +56,7 @@ module IRC
56
56
  # Sends the command directly to the server bypassing the queue. Sending commands too fast
57
57
  # to the server can result in a disconnect from the server.
58
58
  def send_command(command)
59
- @context.socket << "#{command.to_s}\r\n"
59
+ @context.output_socket << "#{command.to_s}\r\n"
60
60
  @log.debug("[#{@context.network.to_s.upcase}] >>> #{command.to_s}")
61
61
  end
62
62
 
@@ -99,7 +99,7 @@ module IRC
99
99
  raise CommandHandlerError.new("Can't stop command handler. Already stopped.") if @thread == nil
100
100
 
101
101
  # Kill the thread & clear the command queue.
102
- @thread.kill
102
+ Thread.kill(@thread)
103
103
  @queue.clear
104
104
 
105
105
  @log.debug("[#{@context.network.to_s.upcase}] Command handler successfully stopped.")
@@ -26,6 +26,9 @@
26
26
  require 'irc/client/client_error'
27
27
  require 'irc/client/connection_state'
28
28
  require 'irc/client/disconnected_state'
29
+ require 'irc/commands/nick_command'
30
+ require 'irc/commands/password_command'
31
+ require 'irc/commands/user_command'
29
32
  require 'irc/commands/pong_command'
30
33
  require 'irc/commands/quit_command'
31
34
  require 'log4r'
@@ -40,6 +43,10 @@ module IRC
40
43
  @log = Log4r::Logger.new('IRC::Client::ConnectedState')
41
44
  end
42
45
 
46
+ # Changes the nick name.
47
+ def change_nick(context, nick)
48
+ end
49
+
43
50
  # Connects to the server.
44
51
  def connect(context, server)
45
52
  raise ClientError.new("Can't connect to server. Already connected to a server.")
@@ -61,25 +68,63 @@ module IRC
61
68
  # Set network & server to nil.
62
69
  context.network = context.server = nil
63
70
 
64
- # Close socket and set socket to nil.
65
- context.socket.close if context.socket != nil
66
- context.socket = nil
67
-
71
+ # Close input/output socket and set them to nil.
72
+ context.input_socket.close if context.input_socket != nil && !context.input_socket.closed?
73
+ context.input_socket = nil
74
+
75
+ context.output_socket.close if context.output_socket != nil && !context.output_socket.closed?
76
+ context.output_socket = nil
77
+
68
78
  # Change to the disconnected state.
69
79
  change_state(context, DisconnectedState.instance)
70
80
 
71
81
  end
72
82
 
83
+ # Register the connection by sending the password, nick and user commands.
84
+ def register(context, nick, login, realname)
85
+ raise ClientError.new("Can't register connection. Connection registration is only possible in the unregistered state.")
86
+ end
87
+
73
88
  # ConnectionListener
89
+
90
+ def on_error(context, reason)
74
91
 
92
+ @log.debug("#{network_name(context)} Got disconnected from server #{context.server.hostname}. #{reason.capitalize}.")
93
+
94
+ # Stop the command & message handlers.
95
+ context.stop_command_handler
96
+ context.stop_message_handler
97
+
98
+ # Set network & server to nil.
99
+ context.network = context.server = nil
100
+
101
+ # Close input/output socket and set them to nil.
102
+ context.input_socket.close if context.input_socket != nil && !context.input_socket.closed?
103
+ context.input_socket = nil
104
+
105
+ context.output_socket.close if context.output_socket != nil && !context.output_socket.closed?
106
+ context.output_socket = nil
107
+
108
+ # Change to the disconnected state.
109
+ change_state(context, DisconnectedState.instance)
110
+
111
+ end
112
+
75
113
  # This method gets called when disconnected from a server.
76
- def on_disconnect(connection, server, reason = nil)
77
- log.debug("#{network_name(connection)} Successfully disconnected from #{server}. #{reason != nil ? reason + '.' : ''}")
114
+ def on_disconnect(context, server, reason = nil)
115
+ log.debug("#{network_name(context)} Successfully disconnected from #{server}. #{reason != nil ? reason + '.' : ''}")
78
116
  end
79
117
 
118
+ # This method gets called when a user changes it's nick name.
119
+ def on_nick(context, before, after)
120
+ return if before.nick.strip.downcase != context.nick.strip.downcase
121
+ context.nick = after.nick
122
+ @log.debug("#{network_name(context)} Successfully changed nick name from #{before.nick} to #{after.nick}.")
123
+ end
124
+
80
125
  # This method gets called when a ping message was received from the server.
81
- def on_ping(connection, servers)
82
- IRC::Commands::PongCommand.new(servers[0], servers[1]).execute(connection)
126
+ def on_ping(context, servers)
127
+ IRC::Commands::PongCommand.new(servers[0], servers[1]).execute(context)
83
128
  end
84
129
 
85
130
 
@@ -51,8 +51,7 @@ module IRC
51
51
 
52
52
  # Connects to the server.
53
53
  def connect(server)
54
- @network = server.network
55
- state.connect(self, server)
54
+ @context.connect(server)
56
55
  end
57
56
 
58
57
  # Returns true if a connection to an IRC server has been established.
@@ -60,6 +59,11 @@ module IRC
60
59
  @context.connected?
61
60
  end
62
61
 
62
+ # Returns all registered connection listeners.
63
+ def connection_listeners
64
+ return @context.connection_listeners
65
+ end
66
+
63
67
  # Disconnects from the currently connected server.
64
68
  def disconnect(message = nil)
65
69
  @context.disconnect(message)
@@ -85,16 +89,41 @@ module IRC
85
89
  @context.network
86
90
  end
87
91
 
88
- # Returns true if a connection to an IRC server has been established and the connection
89
- # has been successfully registered.
90
- def registered?
91
- @context.registered?
92
+ # Returns the nick name that is used when connected to an IRC server.
93
+ def nick
94
+ @context.nick
95
+ end
96
+
97
+ # Returns the nick name that is used when connected to an IRC server.
98
+ def nick=(new_nick)
99
+ @context.change_nick(new_nick)
100
+ end
101
+
102
+ # Returns the supported options of the currently connected server.
103
+ def options
104
+ return @context.options
92
105
  end
93
106
 
94
107
  # Leaves the given channels.
95
108
  def part(channels)
96
109
  @context.part(channels)
97
110
  end
111
+
112
+ # Returns the real name that is used when connected to an IRC server.
113
+ def realname
114
+ @context.realname
115
+ end
116
+
117
+ # Register the connection by sending the password, nick and user commands.
118
+ def register(nick = nil, login = nil, realname = nil)
119
+ @context.register(nick, login, realname)
120
+ end
121
+
122
+ # Returns true if a connection to an IRC server has been established and the connection
123
+ # has been successfully registered.
124
+ def registered?
125
+ @context.registered?
126
+ end
98
127
 
99
128
  # Removes a previously added connection listener. The connection listener will not
100
129
  # get notified any longer when a message was received from the IRC server.
@@ -112,6 +141,12 @@ module IRC
112
141
  @context.send_command_via_queue(command)
113
142
  end
114
143
 
144
+ # Returns the server, to which the connection object is connected to.
145
+ def server
146
+ return @context.server
147
+ end
148
+
149
+
115
150
  end
116
151
 
117
152
  end
@@ -35,17 +35,12 @@ module IRC
35
35
  def on_connect(connection, server)
36
36
  end
37
37
 
38
- # This method gets called when disconnected from a server.
39
- def on_disconnect(connection, server, reason = nil)
40
- end
41
-
42
- # This method gets called when a private message was received from a channel or an user.
43
- def on_private_message(connection, target, message)
38
+ # This method gets called an error message was received from the IRC server.
39
+ def on_error(connection, reason)
44
40
  end
45
41
 
46
- # This method gets called when a user sends a public message
47
- # to a channel. If the message was a private message the channel is nil.
48
- def on_need_more_parameters(connection, nick)
42
+ # This method gets called when disconnected from a server.
43
+ def on_disconnect(connection, server, reason = nil)
49
44
  end
50
45
 
51
46
  # This method gets called when a notice message was received.
@@ -61,9 +56,17 @@ module IRC
61
56
  end
62
57
 
63
58
  # This method gets called when a user gets kicked from a channel.
64
- def on_kick(connection, channel, kick_user, kicked_user, reason)
59
+ def on_kick(connection, channel, kicker_user, kicked_user, reason)
60
+ end
61
+
62
+ # This method gets called when a user changes it's nick name.
63
+ def on_nick(connection, before, after)
65
64
  end
66
65
 
66
+ # This method gets called when the chosen nick name is already in use.
67
+ def on_nick_already_in_use(connection, nick_name)
68
+ end
69
+
67
70
  # This method gets called when a user (possibly us) leaves a channel.
68
71
  def on_part(connection, channel, user)
69
72
  end
@@ -80,14 +83,26 @@ module IRC
80
83
  def on_pong(connection, daemons)
81
84
  end
82
85
 
86
+ # This method gets called when a private message was received from a channel or an user.
87
+ def on_private_message(connection, target, message)
88
+ end
89
+
90
+ # This method gets called when the connection was successfully registered.
91
+ def on_registration(connection)
92
+ end
93
+
83
94
  # This method gets called when an error happend during the connection registration.
84
95
  def on_registration_failure(connection, message)
85
96
  end
86
97
 
87
- # This method gets called when a message from the server is received.
98
+ # This method gets called when a message from the server was received.
88
99
  def on_server_response(connection, message)
89
100
  end
90
101
 
102
+ # This method gets called when a welcome message was received.
103
+ def on_welcome(connection, user, text)
104
+ end
105
+
91
106
  end
92
107
 
93
108
  end
@@ -35,6 +35,10 @@ module IRC
35
35
  include ConnectionListener
36
36
  include ::Singleton
37
37
 
38
+ # Changes the nick name.
39
+ def change_nick(context, nick)
40
+ end
41
+
38
42
  # Connects to the server.
39
43
  def connect(context, server)
40
44
  end
@@ -54,6 +58,10 @@ module IRC
54
58
  # Sends a private message to the target (a nick or a channel).
55
59
  def private_message(context, target, message)
56
60
  end
61
+
62
+ # Register the connection by sending the password, nick and user commands.
63
+ def register(context, nick, login, realname)
64
+ end
57
65
 
58
66
  # Sends the command to the server bypassing the command queue.
59
67
  def send_command(context, command)
@@ -68,8 +76,13 @@ module IRC
68
76
  # ConnectionListener
69
77
 
70
78
  # This method gets called when a ping message was received from the server.
71
- def on_ping(connection, servers)
72
- IRC::Commands::PongCommand.new(servers[0], servers[1]).send(connection)
79
+ def on_ping(context, servers)
80
+ IRC::Commands::PongCommand.new(servers[0], servers[1]).execute(context)
81
+ end
82
+
83
+ # This method gets called when a welcome message was received.
84
+ def on_welcome(context, user, text)
85
+ context.nick = user.nick
73
86
  end
74
87
 
75
88
  protected
@@ -29,6 +29,7 @@ require 'irc/client/context'
29
29
  require 'irc/client/connection_listener'
30
30
  require 'irc/client/disconnected_state'
31
31
  require 'irc/client/message_handler'
32
+ require 'irc/util/nick_generator'
32
33
  require 'log4r'
33
34
 
34
35
  module IRC
@@ -46,30 +47,40 @@ module IRC
46
47
 
47
48
  # The connection listeners that get notified when a message was received by the IRC server.
48
49
  attr_reader :connection_listeners
49
-
50
- # The login name that is used when connected to an IRC server.
51
- attr_reader :login
52
-
53
- # The nick name that is used when connected to an IRC server.
54
- attr_reader :nick
55
-
56
- # The real name that is used when connected to an IRC server.
57
- attr_reader :realname
50
+
51
+ # The nick name generator that used, when automatic nick name change is enabled.
52
+ attr_reader :nick_generator
58
53
 
59
54
  # The context's current state. The initial state is disconnected.
60
55
  attr_reader :state
61
56
 
62
57
  # ACCESSOR
63
58
 
59
+ # When set to true the connection object changes the nick name automatically when
60
+ # the chosen name is already in use.
61
+ attr_accessor :auto_nick_change
62
+
63
+ # The login name that is used when connected to an IRC server.
64
+ attr_accessor :login
65
+
64
66
  # The network to which the context is connected., or nil if no connection has been established.
65
67
  attr_accessor :network
66
68
 
69
+ # The nick name that is used when connected to an IRC server.
70
+ attr_accessor :nick
71
+
72
+ # The real name that is used when connected to an IRC server.
73
+ attr_accessor :realname
74
+
75
+ # The options that are supported by the server.
76
+ attr_accessor :options
77
+
67
78
  # The IRC server the context is connected to, or nil if no connection has been established.
68
79
  attr_accessor :server
69
80
 
70
81
  # The TCPSocket the context uses to talk to the IRC server.
71
- attr_accessor :socket
72
-
82
+ attr_writer :input_socket
83
+ attr_writer :output_socket
73
84
 
74
85
  def initialize(nick = nil, login = nil, realname = nil)
75
86
 
@@ -82,6 +93,10 @@ module IRC
82
93
  @login = login || ENV['USER'] || ENV['USERNAME'] || nick
83
94
  @realname = realname || ENV['USER'] || ENV['USERNAME'] || nick
84
95
 
96
+ # Enable automatic nick change. Use the nick name generator.
97
+ @auto_nick_change = true
98
+ @nick_generator = IRC::Util::NickGenerator.new(@nick)
99
+
85
100
  # A hash to keep track of currently joined channels.
86
101
  @joined_channels = Hash.new
87
102
 
@@ -97,6 +112,9 @@ module IRC
97
112
  # Change into the disconnected state.
98
113
  change_state(DisconnectedState.instance)
99
114
 
115
+ # The options from the isupport messages.
116
+ @options = Hash.new
117
+
100
118
  end
101
119
 
102
120
  # Adds a new connection listener. The connection listener gets notified when a
@@ -110,6 +128,11 @@ module IRC
110
128
  send_command_via_queue(command)
111
129
  end
112
130
 
131
+ # Changes the context's state to the next state,
132
+ def change_nick(new_nick)
133
+ state.change_nick(self, new_nick)
134
+ end
135
+
113
136
  # Changes the context's state to the next state,
114
137
  def change_state(next_state)
115
138
 
@@ -144,6 +167,16 @@ module IRC
144
167
  def disconnect(message = nil)
145
168
  state.disconnect(self, message)
146
169
  end
170
+
171
+ # Returns the socket, that is used to receive messages from the server.
172
+ def input_socket
173
+ return @input_socket
174
+ end
175
+
176
+ # Returns the socket, that is used to send commands to the server.
177
+ def output_socket
178
+ return @output_socket
179
+ end
147
180
 
148
181
  # Joins the given channels.
149
182
  def join(channels)
@@ -195,6 +228,11 @@ module IRC
195
228
  state.part(self, channels)
196
229
  end
197
230
 
231
+ # Register the connection by sending the password, nick and user commands.
232
+ def register(nick = nil, login = nil, realname = nil)
233
+ state.register(self, nick || self.nick, login || self.login, realname || self.realname)
234
+ end
235
+
198
236
  # Returns true if a connection to an IRC server has been established and the connection
199
237
  # has been successfully registered.
200
238
  def registered?
@@ -27,9 +27,6 @@ require 'irc/client/client_error'
27
27
  require 'irc/client/connection'
28
28
  require 'irc/client/connection_state'
29
29
  require 'irc/client/unregistered_state'
30
- require 'irc/commands/nick_command'
31
- require 'irc/commands/password_command'
32
- require 'irc/commands/user_command'
33
30
  require 'irc/models/server'
34
31
  require 'log4r'
35
32
  require 'socket'
@@ -46,34 +43,39 @@ module IRC
46
43
  @log = Log4r::Logger.new('IRC::Client::DisconnectedState')
47
44
  end
48
45
 
46
+ # Changes the nick name.
47
+ def change_nick(context, nick)
48
+ raise ClientError.new("Can't change nick name. Not connected to a server.")
49
+ end
50
+
49
51
  # Connects to the server.
50
52
  def connect(context, server)
51
53
 
52
54
  @log.debug("#{network_name(context)} Trying to connect to server #{server.hostname} on port #{server.port}.")
53
55
 
54
56
  # Establish the connection to the server.
55
- context.socket = establish_connection(server)
57
+ establish_connection(context, server)
56
58
 
57
59
  # Set the server to which we are connected.
58
60
  context.server = server
59
61
 
62
+ # Clear the supported options, that get initialized by the isupport messages.
63
+ context.options = Hash.new
64
+
60
65
  # Initialize and start the command & message handlers.
61
66
  context.start_command_handler
62
- context.start_message_handler
67
+ context.start_message_handler
63
68
 
64
69
  # Change to the unregistered state.
65
70
  change_state(context, UnregisteredState.instance)
66
71
 
67
- # Send the connection registration commands.
68
- if server.password
69
- context.send_command_via_queue(IRC::Commands::PasswordCommand.new(server.password))
70
- end
71
-
72
- context.send_command_via_queue(IRC::Commands::NickCommand.new(context.nick))
73
- context.send_command_via_queue(IRC::Commands::UserCommand.new(context.nick, Socket.gethostname, server.hostname, context.realname))
72
+ # Notify all connection listeners that we are successfully connected now.
73
+ context.connection_listeners.each do |connection_listener|
74
+ connection_listener.on_connect(context, server)
75
+ end
74
76
 
75
- # rescue Exception => e
76
- # raise ClientError.new("Can't connect to server #{server.hostname} on port #{server.port}. #{e.message.capitalize}.")
77
+ rescue Exception => e
78
+ raise ClientError.new("Can't connect to server #{server.hostname} on port #{server.port}. #{e.message.capitalize}.")
77
79
 
78
80
  end
79
81
 
@@ -97,6 +99,11 @@ module IRC
97
99
  raise ClientError.new("Can't send a private message. Not connected to a server.")
98
100
  end
99
101
 
102
+ # Register the connection by sending the password, nick and user commands.
103
+ def register(context, nick, login, realname)
104
+ raise ClientError.new("Can't register connection. Not connected to a server.")
105
+ end
106
+
100
107
  # Sends the command to the server bypassing the command queue.
101
108
  def send_command(context, command)
102
109
  raise ClientError.new("Can't send command. Not connected to a server.")
@@ -116,11 +123,16 @@ module IRC
116
123
 
117
124
  private
118
125
 
119
- def establish_connection(server)
126
+ def establish_connection(context, server)
120
127
 
121
128
  # Try to connect to the server before the timeout exceeds.
122
129
  Timeout::timeout(Connection::MAX_CONNECTION_TIMEOUT_IN_SEC) do
123
- return TCPSocket.open(server.hostname, server.port)
130
+
131
+ socket = TCPSocket.open(server.hostname, server.port)
132
+
133
+ context.input_socket = socket
134
+ context.output_socket = socket
135
+
124
136
  end
125
137
 
126
138
  end