jeffrafter-marvin 0.1.20081115 → 0.1.20081120
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.
- 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)
|