jeffrafter-marvin 0.1.20081115 → 0.1.20081120
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +105 -32
- data/VERSION.yml +1 -1
- data/bin/marvin +10 -6
- data/config/connections.yml.sample +5 -0
- data/config/settings.yml.sample +2 -7
- data/config/setup.rb +6 -1
- data/handlers/debug_handler.rb +5 -0
- data/handlers/hello_world.rb +1 -1
- data/lib/marvin.rb +13 -9
- data/lib/marvin/abstract_client.rb +88 -43
- data/lib/marvin/abstract_parser.rb +14 -2
- data/lib/marvin/base.rb +44 -6
- data/lib/marvin/dispatchable.rb +9 -4
- data/lib/marvin/exception_tracker.rb +1 -1
- data/lib/marvin/exceptions.rb +3 -0
- data/lib/marvin/irc.rb +4 -5
- data/lib/marvin/irc/client.rb +39 -7
- data/lib/marvin/irc/event.rb +9 -4
- data/lib/marvin/irc/replies.rb +154 -0
- data/lib/marvin/loader.rb +26 -8
- data/lib/marvin/logger.rb +66 -3
- data/lib/marvin/options.rb +33 -0
- data/lib/marvin/parsers.rb +3 -0
- data/lib/marvin/parsers/command.rb +105 -0
- data/lib/marvin/parsers/prefixes.rb +8 -0
- data/lib/marvin/parsers/prefixes/host_mask.rb +30 -0
- data/lib/marvin/parsers/prefixes/server.rb +24 -0
- data/lib/marvin/parsers/ragel_parser.rb +713 -0
- data/lib/marvin/parsers/ragel_parser.rl +144 -0
- data/lib/marvin/parsers/regexp_parser.rb +0 -3
- data/lib/marvin/parsers/simple_parser.rb +20 -81
- data/lib/marvin/settings.rb +9 -9
- data/lib/marvin/test_client.rb +5 -1
- data/lib/marvin/util.rb +20 -3
- data/script/{run → client} +0 -0
- data/script/daemon-runner +1 -1
- data/script/install +3 -0
- data/test/parser_comparison.rb +62 -0
- data/test/parser_test.rb +264 -0
- data/test/test_helper.rb +10 -0
- metadata +19 -9
- data/lib/marvin/drb_handler.rb +0 -7
- data/lib/marvin/irc/abstract_server.rb +0 -4
- data/lib/marvin/irc/base_server.rb +0 -11
- data/lib/marvin/irc/socket_client.rb +0 -69
- data/lib/marvin/parsers/simple_parser/default_events.rb +0 -37
- data/lib/marvin/parsers/simple_parser/event_extensions.rb +0 -14
- data/lib/marvin/parsers/simple_parser/prefixes.rb +0 -34
data/README.textile
CHANGED
@@ -7,30 +7,85 @@ particular need.
|
|
7
7
|
|
8
8
|
h2. Background
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
10
|
+
The library is designed to be event driven in that it:
|
11
|
+
|
12
|
+
# Uses the EventMachine library for all network connections
|
13
|
+
# It uses an architecture based on event listeners - called 'handlers'
|
14
|
+
|
15
|
+
It's been heavily influenced by rack in terms of design, making it easy
|
16
|
+
to do things like chain handlers, write your own functionality and most
|
17
|
+
of all making it easy to implement.
|
18
|
+
|
19
|
+
h2. Getting Started
|
20
|
+
|
21
|
+
The easiest way to get started with Marvin is by installing the Marvin gem. To
|
22
|
+
do this, make sure Github is added to your gem sources (and you are using
|
23
|
+
rubygems >= 1.2.0) (by default, substitute username for Sutto):
|
24
|
+
|
25
|
+
$ gem sources -a http://gems.github.com
|
26
|
+
$ sudo gem install username-marvin
|
27
|
+
|
28
|
+
|
29
|
+
Once you have installed the gem, you should have access to the "marvin" command:
|
30
|
+
|
31
|
+
$ marvin --help
|
32
|
+
|
33
|
+
You can create a new marvin folder:
|
34
|
+
|
35
|
+
$ marvin create my_marvin_project
|
36
|
+
|
37
|
+
Then simply edit your settings in the +config/settings.yml+
|
38
|
+
|
39
|
+
default:
|
40
|
+
name: Marvin
|
41
|
+
use_logging: false
|
42
|
+
datastore_location: tmp/datastore.json
|
43
|
+
development:
|
44
|
+
user: MarvinBot
|
45
|
+
name: MarvinBot
|
46
|
+
nick: Marvin
|
47
|
+
|
48
|
+
You can use the defaults or configure it. The datastore location
|
49
|
+
specifies a relative path where a simple json-backed key value
|
50
|
+
store will store persistent information for your client (if chosen).
|
51
|
+
Once that's been done, you'll want to setup some connections by editing
|
52
|
+
+config/connections.yml+, using the following format:
|
53
|
+
|
54
|
+
"server-address":
|
55
|
+
post: 6667 # Defaults to 6667
|
56
|
+
channels:
|
57
|
+
- "#marvin-testing"
|
58
|
+
- "#relayrelay"
|
59
|
+
nicks:
|
60
|
+
- List
|
61
|
+
- Of
|
62
|
+
- Alternative
|
63
|
+
- Nicks
|
64
|
+
"another-server-address":
|
65
|
+
post: 6667 # Defaults to 6667
|
66
|
+
channels:
|
67
|
+
- "#helloworld"
|
68
|
+
|
69
|
+
Which will let marvin connect to multiple servers - autojoining the specific rooms.
|
70
|
+
Next, to get started you can simply type:
|
71
|
+
|
72
|
+
$ ./script/client
|
73
|
+
|
74
|
+
The bot should join the specified channel and will respond to some simple
|
75
|
+
commands by default:
|
76
|
+
|
77
|
+
*YourName*: MarvinBot3000: hello
|
78
|
+
*MarvinBot3000*: YourName: Hola!
|
79
|
+
|
80
|
+
As defined in handlers/hello_world.rb
|
81
|
+
|
82
|
+
h2. Thanks
|
83
|
+
|
84
|
+
Thanks goes out to the following people / projects:
|
85
|
+
|
86
|
+
* Jeff Rafter - contributed code and doc changes, now one of the co-developers.
|
87
|
+
* epitron / halogrium - For the ragel state machine used in Marvin::Parsers::RagelParser
|
88
|
+
* The creator of Ruby-IRCD - the server component is heavily influenced by / part derivative of said work.
|
34
89
|
|
35
90
|
h2. Marvin::Base - A handler starting point
|
36
91
|
|
@@ -53,13 +108,15 @@ openstruct version of the details. e.g.
|
|
53
108
|
Or the like. Also, the halt! method can be called in any subclass to
|
54
109
|
halt the handler callback chain.
|
55
110
|
|
111
|
+
You also get access to the class method +on_numeric+ which makes
|
112
|
+
it relatively easy to respond to a specific numeric reply.
|
113
|
+
|
56
114
|
h2. Marvin::CommandHandler - Ridiculously easy Bots
|
57
115
|
|
58
116
|
With Marvin::CommandHandler, you get to define seriously
|
59
117
|
simple classes which can act as a simple bot. It takes
|
60
118
|
great inspiration from "MatzBot":http://github.com/defunkt/matzbot/tree/master
|
61
|
-
|
62
|
-
creating marvin.
|
119
|
+
to make it as easy as possible to make a simple bot
|
63
120
|
|
64
121
|
To write a CommandHandler, you simply create a subclass
|
65
122
|
(ala ActiveRecord::Base), define a few methods and then
|
@@ -75,7 +132,15 @@ just use the "exposes" class method. e.g.
|
|
75
132
|
Where data is an array of parameters. exposed methods will be called
|
76
133
|
when they match the following pattern:
|
77
134
|
|
78
|
-
Botname:
|
135
|
+
Botname: *exposed-method* *space-seperated-list-meaning-data*
|
136
|
+
|
137
|
+
i.e., the above handler could be called in IRC as such:
|
138
|
+
|
139
|
+
YourBotsName: hello
|
140
|
+
|
141
|
+
or, even easier, by PM'ing the bot with:
|
142
|
+
|
143
|
+
hello
|
79
144
|
|
80
145
|
h2. Marvin::MiddleMan - Introducing middleware
|
81
146
|
|
@@ -83,9 +148,10 @@ Marvin::MiddleMan lets you insert middleware between handlers
|
|
83
148
|
and you're client - letting you do things such as translating
|
84
149
|
all messages on the fly. It's build to be extensible and is
|
85
150
|
relatively simple to use. On any Marvin::Base subclass (baring
|
86
|
-
the MiddleMan itself),
|
87
|
-
|
88
|
-
|
151
|
+
the MiddleMan itself), using a middle man is easy - you simply
|
152
|
+
call the register! class method with an option argument. e.g:
|
153
|
+
|
154
|
+
HelloWorld.register! Marvin::MiddleMan
|
89
155
|
|
90
156
|
h2. Marvin::DataStore - A dead simple persistent hash store
|
91
157
|
|
@@ -104,7 +170,14 @@ If you're inside a Marvin::Base subclass it's even easier. You can get a cattr_a
|
|
104
170
|
style accessor for free - just use the "uses_datastore" method. e.g:
|
105
171
|
|
106
172
|
class X < Marvin::Base
|
107
|
-
uses_datastore "datastore-global-key", :
|
173
|
+
uses_datastore "datastore-global-key", :something
|
174
|
+
end
|
175
|
+
|
176
|
+
Then, self.something will point to the data store - letting you do
|
177
|
+
things like:
|
178
|
+
|
179
|
+
def hello(data)
|
180
|
+
(self.something[from] ||= 0) += 1
|
108
181
|
end
|
109
182
|
|
110
|
-
|
183
|
+
which will persist the count between each session.
|
data/VERSION.yml
CHANGED
data/bin/marvin
CHANGED
@@ -39,17 +39,21 @@ if ARGV.length >= 1 && !["start", "stop", "run", "restart"].include?(ARGV[0])
|
|
39
39
|
puts "Writing Settings file"
|
40
40
|
copy "config/settings.yml.sample", "config/settings.yml"
|
41
41
|
|
42
|
+
puts "Writing Connections file"
|
43
|
+
copy "config/connections.yml.sample", "config/connections.yml"
|
44
|
+
|
42
45
|
puts "Writing setup.rb"
|
43
46
|
copy "config/setup.rb"
|
44
47
|
|
45
|
-
puts "Copying start
|
46
|
-
copy "script/
|
48
|
+
puts "Copying start scripts"
|
49
|
+
copy "script/client"
|
47
50
|
copy "script/daemon-runner"
|
48
|
-
FileUtils.chmod 0755, j(DEST, "script/
|
51
|
+
FileUtils.chmod 0755, j(DEST, "script/client")
|
49
52
|
FileUtils.chmod 0755, j(DEST, "script/daemon-runner")
|
50
53
|
|
51
|
-
puts "Copying example
|
54
|
+
puts "Copying example handlers"
|
52
55
|
copy "handlers/hello_world.rb"
|
56
|
+
copy "handlers/debug_handler.rb"
|
53
57
|
|
54
58
|
puts "Done!"
|
55
59
|
elsif ARGV.length >= 1
|
@@ -59,9 +63,9 @@ elsif ARGV.length >= 1
|
|
59
63
|
end
|
60
64
|
exec "script/daemon-runner #{ARGV.map {|a| a.include?(" ") ? "\"#{a}\"" : a }.join(" ")}"
|
61
65
|
else
|
62
|
-
if !File.exist?("script/
|
66
|
+
if !File.exist?("script/client")
|
63
67
|
puts "Woops! This isn't a marvin directory."
|
64
68
|
exit(1)
|
65
69
|
end
|
66
|
-
exec "script/
|
70
|
+
exec "script/client"
|
67
71
|
end
|
data/config/settings.yml.sample
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
default:
|
2
|
-
name:
|
3
|
-
server: irc.freenode.net
|
4
|
-
port: 6667
|
5
|
-
channel: "#marvin-testing"
|
2
|
+
name: Marvin
|
6
3
|
use_logging: false
|
7
4
|
datastore_location: tmp/datastore.json
|
8
5
|
development:
|
9
6
|
user: MarvinBot
|
10
7
|
name: MarvinBot
|
11
|
-
nick:
|
12
|
-
production:
|
13
|
-
deployed: false
|
8
|
+
nick: Marvin
|
data/config/setup.rb
CHANGED
@@ -9,6 +9,11 @@ Marvin::Loader.before_connecting do
|
|
9
9
|
# Example Handler use.
|
10
10
|
# LoggingHandler.register! if Marvin::Settings.use_logging
|
11
11
|
|
12
|
-
|
12
|
+
if Marvin::Loader.type == :client
|
13
|
+
Marvin::Distributed::DispatchHandler.register!
|
14
|
+
else
|
15
|
+
HelloWorld.register!
|
16
|
+
DebugHandler.register!
|
17
|
+
end
|
13
18
|
|
14
19
|
end
|
data/handlers/hello_world.rb
CHANGED
data/lib/marvin.rb
CHANGED
@@ -8,8 +8,17 @@ require 'marvin/core_ext'
|
|
8
8
|
require 'marvin/exceptions'
|
9
9
|
|
10
10
|
module Marvin
|
11
|
+
module VERSION
|
12
|
+
MAJOR = 0
|
13
|
+
MINOR = 1
|
14
|
+
PATCH = 20081120
|
15
|
+
|
16
|
+
STRING = [MAJOR, MINOR, PATCH].join(".")
|
17
|
+
end
|
18
|
+
|
11
19
|
autoload :Util, 'marvin/util'
|
12
20
|
autoload :Dispatchable, 'marvin/dispatchable'
|
21
|
+
autoload :Distributed, 'marvin/distributed'
|
13
22
|
autoload :AbstractClient, 'marvin/abstract_client'
|
14
23
|
autoload :Base, 'marvin/base'
|
15
24
|
autoload :ClientMixin, 'marvin/client_mixin'
|
@@ -22,6 +31,7 @@ module Marvin
|
|
22
31
|
autoload :DRBHandler, 'marvin/drb_handler'
|
23
32
|
autoload :DataStore, 'marvin/data_store'
|
24
33
|
autoload :ExceptionTracker, 'marvin/exception_tracker'
|
34
|
+
autoload :Options, 'marvin/options'
|
25
35
|
# Parsers
|
26
36
|
autoload :AbstractParser, 'marvin/abstract_parser'
|
27
37
|
autoload :Parsers, 'marvin/parsers.rb'
|
@@ -31,14 +41,8 @@ module Marvin
|
|
31
41
|
|
32
42
|
Settings.setup # Load Settings etc.
|
33
43
|
|
34
|
-
|
35
|
-
|
36
|
-
def p(text)
|
37
|
-
res = Marvin::Parsers::SimpleParser.parse(text)
|
38
|
-
if res.blank?
|
39
|
-
puts "Unrecognized Result"
|
40
|
-
else
|
41
|
-
STDOUT.puts "Event: #{res.to_incoming_event_name}"
|
42
|
-
STDOUT.puts "Args: #{res.to_hash.inspect}"
|
44
|
+
def self.version
|
45
|
+
VERSION::STRING
|
43
46
|
end
|
47
|
+
|
44
48
|
end
|
@@ -7,8 +7,17 @@ module Marvin
|
|
7
7
|
|
8
8
|
include Marvin::Dispatchable
|
9
9
|
|
10
|
+
def initialize(opts = {})
|
11
|
+
self.original_opts = opts.dup # Copy the options so we can use them to reconnect.
|
12
|
+
self.server = opts[:server]
|
13
|
+
self.port = opts[:port]
|
14
|
+
self.default_channels = opts[:channels]
|
15
|
+
self.nicks = opts[:nicks] || []
|
16
|
+
self.pass = opts[:pass]
|
17
|
+
end
|
18
|
+
|
10
19
|
cattr_accessor :events, :configuration, :logger, :is_setup, :connections
|
11
|
-
attr_accessor :channels, :nickname
|
20
|
+
attr_accessor :channels, :nickname, :server, :port, :nicks, :pass, :disconnect_expected, :original_opts
|
12
21
|
|
13
22
|
# Set the default values for the variables
|
14
23
|
self.events = []
|
@@ -22,19 +31,25 @@ module Marvin
|
|
22
31
|
# call #client= on each handler if they respond to it.
|
23
32
|
def process_connect
|
24
33
|
self.class.setup
|
25
|
-
logger.
|
34
|
+
logger.info "Initializing the current instance"
|
26
35
|
self.channels = []
|
27
36
|
self.connections << self
|
28
|
-
logger.
|
37
|
+
logger.info "Setting the client for each handler"
|
29
38
|
self.handlers.each { |h| h.client = self if h.respond_to?(:client=) }
|
30
|
-
logger.
|
39
|
+
logger.info "Dispatching the default :client_connected event"
|
31
40
|
dispatch :client_connected
|
32
41
|
end
|
33
42
|
|
34
43
|
def process_disconnect
|
44
|
+
logger.info "Handling disconnect for #{self.server}:#{self.port}"
|
35
45
|
self.connections.delete(self) if self.connections.include?(self)
|
36
46
|
dispatch :client_disconnected
|
37
|
-
|
47
|
+
unless self.disconnect_expected
|
48
|
+
logger.warn "Lost connection to server - adding reconnect"
|
49
|
+
self.class.add_reconnect self.original_opts
|
50
|
+
else
|
51
|
+
Marvin::Loader.stop! if self.connections.blank?
|
52
|
+
end
|
38
53
|
end
|
39
54
|
|
40
55
|
# Sets the current class-wide settings of this IRC Client
|
@@ -51,11 +66,6 @@ module Marvin
|
|
51
66
|
# that is more widely used throughout the client.
|
52
67
|
def self.setup
|
53
68
|
return if self.is_setup
|
54
|
-
# Default the logger back to a new one.
|
55
|
-
self.configuration.channels ||= []
|
56
|
-
unless self.configuration.channel.blank? || self.configuration.channels.include?(self.configuration.channel)
|
57
|
-
self.configuration.channels.unshift(self.configuration.channel)
|
58
|
-
end
|
59
69
|
if configuration.logger.blank?
|
60
70
|
require 'logger'
|
61
71
|
configuration.logger = Marvin::Logger.logger
|
@@ -80,40 +90,39 @@ module Marvin
|
|
80
90
|
# to be in and if a password is specified in the configuration,
|
81
91
|
# it will also attempt to identify us.
|
82
92
|
def handle_client_connected(opts = {})
|
83
|
-
logger.
|
93
|
+
logger.info "About to handle client connected"
|
94
|
+
# If the pass is set
|
95
|
+
unless self.pass.blank?
|
96
|
+
logger.info "Sending pass for connection"
|
97
|
+
command :pass, self.pass
|
98
|
+
end
|
84
99
|
# IRC Connection is establish so we send all the required commands to the server.
|
85
|
-
logger.
|
86
|
-
default_nickname = self.
|
100
|
+
logger.info "Setting default nickname"
|
101
|
+
default_nickname = self.nicks.shift
|
87
102
|
nick default_nickname
|
88
|
-
logger.
|
103
|
+
logger.info "sending user command"
|
89
104
|
command :user, self.configuration.user, "0", "*", Marvin::Util.last_param(self.configuration.name)
|
90
|
-
# If a password is specified, we will attempt to message
|
91
|
-
# NickServ to identify ourselves.
|
92
|
-
say ":IDENTIFY #{self.configuration.password}", "NickServ" unless self.configuration.password.blank?
|
93
|
-
# Join the default channels
|
94
|
-
self.configuration.channels.each { |c| self.join c }
|
95
105
|
rescue Exception => e
|
96
106
|
Marvin::ExceptionTracker.log(e)
|
97
107
|
end
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
logger.info "No Nicknames available - QUITTING"
|
115
|
-
quit
|
108
|
+
|
109
|
+
def default_channels
|
110
|
+
@default_channels ||= []
|
111
|
+
end
|
112
|
+
|
113
|
+
def default_channels=(channels)
|
114
|
+
@default_channels = channels.to_a.map { |c| c.to_s }
|
115
|
+
end
|
116
|
+
|
117
|
+
def nicks
|
118
|
+
if @nicks.blank? && !@nicks_loaded
|
119
|
+
logger.info "Setting default nick list"
|
120
|
+
@nicks = []
|
121
|
+
@nicks << self.configuration.nick
|
122
|
+
@nicks += self.configuration.nicks.to_a unless self.configuration.nicks.blank?
|
123
|
+
@nicks_loaded
|
116
124
|
end
|
125
|
+
return @nicks
|
117
126
|
end
|
118
127
|
|
119
128
|
# The default response for PING's - it simply replies
|
@@ -126,9 +135,43 @@ module Marvin
|
|
126
135
|
# TODO: Get the correct mapping for a given
|
127
136
|
# Code.
|
128
137
|
def handle_incoming_numeric(opts = {})
|
138
|
+
case opts[:code]
|
139
|
+
when Marvin::IRC::Replies[:RPL_WELCOME]
|
140
|
+
handle_welcome
|
141
|
+
when Marvin::IRC::Replies[:ERR_NICKNAMEINUSE]
|
142
|
+
handle_nick_taken
|
143
|
+
end
|
129
144
|
code = opts[:code].to_i
|
130
145
|
args = Marvin::Util.arguments(opts[:data])
|
131
|
-
dispatch :incoming_numeric_processed,
|
146
|
+
dispatch :incoming_numeric_processed, :code => code, :data => args
|
147
|
+
end
|
148
|
+
|
149
|
+
def handle_welcome
|
150
|
+
logger.info "Say hello to my little friend - Got welcome"
|
151
|
+
# If a password is specified, we will attempt to message
|
152
|
+
# NickServ to identify ourselves.
|
153
|
+
say ":IDENTIFY #{self.configuration.password}", "NickServ" unless self.configuration.password.blank?
|
154
|
+
# Join the default channels IF they're already set
|
155
|
+
# Note that Marvin::IRC::Client.connect will set them AFTER this stuff is run.
|
156
|
+
self.default_channels.each { |c| self.join(c) }
|
157
|
+
end
|
158
|
+
|
159
|
+
# The default handler for when a users nickname is taken on
|
160
|
+
# on the server. It will attempt to get the nicknickname from
|
161
|
+
# the nicknames part of the configuration (if available) and
|
162
|
+
# will then call #nick to change the nickname.
|
163
|
+
def handle_nick_taken
|
164
|
+
logger.info "Nickname '#{self.nickname}' on #{self.server} taken, trying next."
|
165
|
+
logger.info "Available Nicknames: #{self.nicks.empty? ? "None" : self.nicks.join(", ")}"
|
166
|
+
if !self.nicks.empty?
|
167
|
+
logger.info "Getting next nickname to switch"
|
168
|
+
next_nick = self.nicks.shift # Get the next nickname
|
169
|
+
logger.info "Attemping to set nickname to '#{next_nick}'"
|
170
|
+
nick next_nick
|
171
|
+
else
|
172
|
+
logger.fatal "No Nicknames available - QUITTING"
|
173
|
+
quit
|
174
|
+
end
|
132
175
|
end
|
133
176
|
|
134
177
|
## General IRC Functions
|
@@ -141,13 +184,14 @@ module Marvin
|
|
141
184
|
# First, get the appropriate command
|
142
185
|
name = name.to_s.upcase
|
143
186
|
args = args.flatten.compact
|
144
|
-
irc_command = "#{name} #{args.join(" ").strip}
|
187
|
+
irc_command = "#{name} #{args.join(" ").strip}\r\n"
|
145
188
|
send_line irc_command
|
146
189
|
end
|
147
190
|
|
148
191
|
def join(channel)
|
149
192
|
channel = Marvin::Util.channel_name(channel)
|
150
193
|
# Record the fact we're entering the room.
|
194
|
+
# TODO: Refactor to only add the channel when we receive confirmation we've joined.
|
151
195
|
self.channels << channel
|
152
196
|
command :JOIN, channel
|
153
197
|
logger.info "Joined channel #{channel}"
|
@@ -166,13 +210,14 @@ module Marvin
|
|
166
210
|
end
|
167
211
|
|
168
212
|
def quit(reason = nil)
|
169
|
-
|
213
|
+
self.disconnect_expected = true
|
214
|
+
logger.info "Preparing to part from #{self.channels.size} channels"
|
170
215
|
self.channels.to_a.each do |chan|
|
171
|
-
logger.
|
216
|
+
logger.info "Parting from #{chan}"
|
172
217
|
self.part chan, reason
|
173
218
|
end
|
174
|
-
logger.
|
175
|
-
command
|
219
|
+
logger.info "Parted from all channels, quitting"
|
220
|
+
command :quit
|
176
221
|
dispatch :quit
|
177
222
|
# Remove the connections from the pool
|
178
223
|
self.connections.delete(self)
|