ircguerilla-irc 1.2.0 → 1.3.0

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