tkellem 0.8.3 → 0.8.4
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/.gitignore +1 -0
- data/lib/tkellem/bouncer.rb +6 -3
- data/lib/tkellem/bouncer_connection.rb +16 -15
- data/lib/tkellem/irc_message.rb +23 -4
- data/lib/tkellem/irc_server.rb +1 -1
- data/lib/tkellem/models/network_user.rb +1 -1
- data/lib/tkellem/models/user.rb +5 -1
- data/lib/tkellem/plugins/backlog.rb +16 -7
- data/lib/tkellem/tkellem_server.rb +9 -5
- data/lib/tkellem/version.rb +1 -1
- data/spec/bouncer_connection_spec.rb +30 -0
- data/spec/irc_message_spec.rb +11 -0
- data/spec/irc_server_spec.rb +60 -29
- data/spec/spec_helper.rb +26 -0
- metadata +4 -2
data/.gitignore
CHANGED
data/lib/tkellem/bouncer.rb
CHANGED
@@ -58,6 +58,8 @@ class Bouncer
|
|
58
58
|
return
|
59
59
|
end
|
60
60
|
|
61
|
+
# force the client nick
|
62
|
+
client.send_msg(":#{client.connecting_nick} NICK #{nick}") if client.connecting_nick != nick
|
61
63
|
send_welcome(client)
|
62
64
|
# make the client join all the rooms that we're in
|
63
65
|
@rooms.each { |room| client.simulate_join(room) }
|
@@ -155,9 +157,10 @@ class Bouncer
|
|
155
157
|
@conn.send_data("#{msg}\r\n")
|
156
158
|
end
|
157
159
|
|
158
|
-
def connection_established
|
160
|
+
def connection_established(conn)
|
161
|
+
@conn = conn
|
159
162
|
# TODO: support sending a real username, realname, etc
|
160
|
-
send_msg("USER #{@
|
163
|
+
send_msg("USER #{@user.username} somehost tkellem :#{@user.name}")
|
161
164
|
change_nick(@nick, true)
|
162
165
|
check_away_status
|
163
166
|
end
|
@@ -193,7 +196,7 @@ class Bouncer
|
|
193
196
|
@last_connect = Time.now
|
194
197
|
@cur_host = (@cur_host || 0) % hosts.length
|
195
198
|
host = hosts[@cur_host]
|
196
|
-
|
199
|
+
EM.connect(host.address, host.port, IrcServerConnection, self, host.ssl)
|
197
200
|
end
|
198
201
|
|
199
202
|
def ready!
|
@@ -19,11 +19,11 @@ module BouncerConnection
|
|
19
19
|
@name = 'new-conn'
|
20
20
|
@data = {}
|
21
21
|
end
|
22
|
-
attr_reader :ssl, :bouncer, :name, :device_name
|
22
|
+
attr_reader :ssl, :bouncer, :name, :device_name, :connecting_nick
|
23
23
|
alias_method :log_name, :name
|
24
24
|
|
25
25
|
def nick
|
26
|
-
@bouncer ? @bouncer.nick : @
|
26
|
+
@bouncer ? @bouncer.nick : @connecting_nick
|
27
27
|
end
|
28
28
|
|
29
29
|
def data(key)
|
@@ -79,16 +79,18 @@ module BouncerConnection
|
|
79
79
|
if msg.args.first =~ /req/i
|
80
80
|
send_msg("CAP NAK")
|
81
81
|
end
|
82
|
-
elsif command == 'PASS'
|
83
|
-
|
84
|
-
@password = msg.args.first
|
85
|
-
end
|
82
|
+
elsif command == 'PASS' && @state == :auth
|
83
|
+
@password = msg.args.first
|
86
84
|
elsif command == 'NICK' && @state == :auth
|
87
|
-
@
|
85
|
+
@connecting_nick = msg.args.first
|
86
|
+
maybe_connect
|
88
87
|
elsif command == 'QUIT'
|
89
88
|
close_connection
|
90
|
-
elsif command == 'USER'
|
91
|
-
|
89
|
+
elsif command == 'USER' && @state == :auth
|
90
|
+
unless @username
|
91
|
+
@username, @conn_info = msg.args.first.strip.split('@', 2).map { |a| a.downcase }
|
92
|
+
end
|
93
|
+
maybe_connect
|
92
94
|
elsif @state == :auth
|
93
95
|
error!("Protocol error. You must authenticate first.")
|
94
96
|
elsif @state == :connected
|
@@ -106,15 +108,14 @@ module BouncerConnection
|
|
106
108
|
end
|
107
109
|
end
|
108
110
|
|
109
|
-
def
|
110
|
-
|
111
|
-
@username, rest = msg.args.first.strip.split('@', 2).map { |a| a.downcase }
|
111
|
+
def maybe_connect
|
112
|
+
if @connecting_nick && @username && @password && !@user
|
112
113
|
@name = @username
|
113
114
|
@user = User.authenticate(@username, @password)
|
114
115
|
return error!("Unknown username: #{@username} or bad password.") unless @user
|
115
116
|
|
116
|
-
if
|
117
|
-
@conn_name, @device_name =
|
117
|
+
if @conn_info && !@conn_info.empty?
|
118
|
+
@conn_name, @device_name = @conn_info.split(':', 2)
|
118
119
|
# 'default' or missing device_name to use the default backlog
|
119
120
|
# pass a device_name to have device-independent backlogs
|
120
121
|
@device_name = @device_name.presence || 'default'
|
@@ -135,7 +136,7 @@ module BouncerConnection
|
|
135
136
|
end
|
136
137
|
|
137
138
|
def simulate_join(room)
|
138
|
-
send_msg(":#{nick}
|
139
|
+
send_msg(":#{nick} JOIN #{room}")
|
139
140
|
# TODO: intercept the NAMES response so that only this bouncer gets it
|
140
141
|
# Otherwise other clients might show an "in this room" line.
|
141
142
|
@bouncer.send_msg("NAMES #{room}\r\n")
|
data/lib/tkellem/irc_message.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Tkellem
|
2
2
|
|
3
|
-
class IrcMessage < Struct.new(:prefix, :command, :args)
|
3
|
+
class IrcMessage < Struct.new(:prefix, :command, :args, :ctcp)
|
4
4
|
RE = %r{(:[^ ]+ )?([^ ]*)(.*)}i
|
5
5
|
|
6
6
|
def self.parse(line)
|
@@ -18,7 +18,22 @@ class IrcMessage < Struct.new(:prefix, :command, :args)
|
|
18
18
|
args = args.split(' ')
|
19
19
|
end
|
20
20
|
|
21
|
-
self.new(prefix, command, args)
|
21
|
+
msg = self.new(prefix, command, args)
|
22
|
+
|
23
|
+
if args.last.match(%r{#{"\x01"}([^ ]+)([^\1]*)#{"\x01"}})
|
24
|
+
msg.ctcp = $1.upcase
|
25
|
+
msg.args[-1] = $2.strip
|
26
|
+
end
|
27
|
+
|
28
|
+
msg
|
29
|
+
end
|
30
|
+
|
31
|
+
def ctcp?
|
32
|
+
self.ctcp.present?
|
33
|
+
end
|
34
|
+
|
35
|
+
def action?
|
36
|
+
self.ctcp == 'ACTION'
|
22
37
|
end
|
23
38
|
|
24
39
|
def command?(cmd)
|
@@ -31,7 +46,11 @@ class IrcMessage < Struct.new(:prefix, :command, :args)
|
|
31
46
|
line << command
|
32
47
|
ext_arg = args.last if args.last && args.last.match(%r{\s})
|
33
48
|
line += ext_arg ? args[0...-1] : args
|
34
|
-
|
49
|
+
if ctcp?
|
50
|
+
line << ":\x01#{ctcp} #{ext_arg}\x01"
|
51
|
+
else
|
52
|
+
line << ":#{ext_arg}" unless ext_arg.nil?
|
53
|
+
end
|
35
54
|
line.join ' '
|
36
55
|
end
|
37
56
|
alias_method :to_s, :replay
|
@@ -50,7 +69,7 @@ class IrcMessage < Struct.new(:prefix, :command, :args)
|
|
50
69
|
args = args.dup
|
51
70
|
args[-1] = "#{timestamp.strftime("%H:%M:%S")}> #{args[-1]}"
|
52
71
|
end
|
53
|
-
IrcMessage.new(prefix, command, args)
|
72
|
+
IrcMessage.new(prefix, command, args, ctcp)
|
54
73
|
end
|
55
74
|
|
56
75
|
end
|
data/lib/tkellem/irc_server.rb
CHANGED
@@ -13,7 +13,7 @@ class NetworkUser < ActiveRecord::Base
|
|
13
13
|
# we use the network's at_connect until it is modified and overwritten for
|
14
14
|
# this specific network user
|
15
15
|
def at_connect
|
16
|
-
read_attribute(:at_connect)
|
16
|
+
network.at_connect + (read_attribute(:at_connect) || [])
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
data/lib/tkellem/models/user.rb
CHANGED
@@ -44,10 +44,14 @@ class User < ActiveRecord::Base
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def set_password!(password)
|
47
|
-
self.password =
|
47
|
+
self.password = password
|
48
48
|
self.save!
|
49
49
|
end
|
50
50
|
|
51
|
+
def password=(password)
|
52
|
+
write_attribute(:password, password ? OpenSSL::Digest::SHA1.hexdigest(password) : nil)
|
53
|
+
end
|
54
|
+
|
51
55
|
def admin?
|
52
56
|
role == 'admin'
|
53
57
|
end
|
@@ -45,7 +45,7 @@ class Backlog
|
|
45
45
|
@devices = {}
|
46
46
|
@streams = {}
|
47
47
|
@starting_pos = {}
|
48
|
-
@dir = File.expand_path("~/.tkellem/logs/#{bouncer.user.
|
48
|
+
@dir = File.expand_path("~/.tkellem/logs/#{bouncer.user.username}/#{bouncer.network.name}")
|
49
49
|
FileUtils.mkdir_p(@dir)
|
50
50
|
end
|
51
51
|
|
@@ -57,6 +57,7 @@ class Backlog
|
|
57
57
|
# open stream in append-only mode
|
58
58
|
return @streams[ctx] if @streams[ctx]
|
59
59
|
stream = @streams[ctx] = File.open(stream_filename(ctx), 'ab')
|
60
|
+
stream.seek(0, IO::SEEK_END)
|
60
61
|
@starting_pos[ctx] = stream.pos
|
61
62
|
stream
|
62
63
|
end
|
@@ -90,13 +91,14 @@ class Backlog
|
|
90
91
|
# transient messages
|
91
92
|
return
|
92
93
|
when 'PRIVMSG'
|
94
|
+
return if msg.ctcp? && !msg.action?
|
93
95
|
ctx = msg.args.first
|
94
96
|
if ctx == @bouncer.nick
|
95
97
|
# incoming pm, fake ctx to be the sender's nick
|
96
98
|
ctx = msg.prefix.split(/[!~@]/, 2).first
|
97
99
|
end
|
98
100
|
stream = get_stream(ctx)
|
99
|
-
stream.puts(Time.now.strftime("%d-%m-%Y %H:%M:%S < #{msg.prefix}: #{msg.args.last}"))
|
101
|
+
stream.puts(Time.now.strftime("%d-%m-%Y %H:%M:%S < #{'* ' if msg.action?}#{msg.prefix}: #{msg.args.last}"))
|
100
102
|
update_pos(ctx, stream.pos)
|
101
103
|
end
|
102
104
|
end
|
@@ -104,9 +106,10 @@ class Backlog
|
|
104
106
|
def client_msg(msg)
|
105
107
|
case msg.command
|
106
108
|
when 'PRIVMSG'
|
109
|
+
return if msg.ctcp? && !msg.action?
|
107
110
|
ctx = msg.args.first
|
108
111
|
stream = get_stream(ctx)
|
109
|
-
stream.puts(Time.now.strftime("%d-%m-%Y %H:%M:%S > #{msg.args.last}"))
|
112
|
+
stream.puts(Time.now.strftime("%d-%m-%Y %H:%M:%S > #{'* ' if msg.action?}#{msg.args.last}"))
|
110
113
|
update_pos(ctx, stream.pos)
|
111
114
|
end
|
112
115
|
end
|
@@ -145,11 +148,17 @@ class Backlog
|
|
145
148
|
def parse_line(line, ctx_name)
|
146
149
|
timestamp = Time.parse(line[0, 19])
|
147
150
|
case line[20..-1]
|
148
|
-
when %r{^> (.+)$}
|
149
|
-
msg = IrcMessage.new(nil, 'PRIVMSG', [ctx_name, $
|
151
|
+
when %r{^> (\* )?(.+)$}
|
152
|
+
msg = IrcMessage.new(nil, 'PRIVMSG', [ctx_name, $2])
|
153
|
+
if $1 == '* '
|
154
|
+
msg.ctcp = 'ACTION'
|
155
|
+
end
|
150
156
|
return timestamp, msg
|
151
|
-
when %r{^< ([^:]+): (.+)$}
|
152
|
-
msg = IrcMessage.new($
|
157
|
+
when %r{^< (\* )?([^:]+): (.+)$}
|
158
|
+
msg = IrcMessage.new($2, 'PRIVMSG', [ctx_name, $3])
|
159
|
+
if $1 == '* '
|
160
|
+
msg.ctcp = 'ACTION'
|
161
|
+
end
|
153
162
|
return timestamp, msg
|
154
163
|
else
|
155
164
|
nil
|
@@ -18,15 +18,19 @@ module Tkellem
|
|
18
18
|
class TkellemServer
|
19
19
|
include Tkellem::EasyLogger
|
20
20
|
|
21
|
+
attr_reader :bouncers
|
22
|
+
|
21
23
|
def initialize
|
22
24
|
@listeners = {}
|
23
25
|
@bouncers = {}
|
24
26
|
|
25
|
-
ActiveRecord::Base.
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
unless ActiveRecord::Base.connected?
|
28
|
+
ActiveRecord::Base.establish_connection({
|
29
|
+
:adapter => 'sqlite3',
|
30
|
+
:database => File.expand_path("~/.tkellem/tkellem.sqlite3"),
|
31
|
+
})
|
32
|
+
ActiveRecord::Migrator.migrate(File.expand_path("../migrations", __FILE__), nil)
|
33
|
+
end
|
30
34
|
|
31
35
|
ListenAddress.all.each { |a| listen(a) }
|
32
36
|
NetworkUser.find_each { |nu| add_bouncer(Bouncer.new(nu)) }
|
data/lib/tkellem/version.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tkellem/bouncer_connection'
|
3
|
+
|
4
|
+
include Tkellem
|
5
|
+
|
6
|
+
describe BouncerConnection, "connect" do
|
7
|
+
before do
|
8
|
+
u = User.create(:username => 'speccer')
|
9
|
+
u.set_password!('test123')
|
10
|
+
tk = mock(TkellemServer)
|
11
|
+
@b = mock(Bouncer)
|
12
|
+
tk.should_receive(:find_bouncer).with(u, 'testhost').and_return(@b)
|
13
|
+
@bc = em(BouncerConnection).new(tk, false)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should connect after receiving credentials" do
|
17
|
+
@bc.receive_line("NICK speccer")
|
18
|
+
@bc.receive_line("PASS test123")
|
19
|
+
@b.should_receive(:connect_client).with(@bc)
|
20
|
+
@bc.receive_line("USER speccer@testhost")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should connect when receiving user before pass" do
|
24
|
+
@bc.receive_line("USER speccer@testhost")
|
25
|
+
@bc.receive_line("PASS test123")
|
26
|
+
@b.should_receive(:connect_client).with(@bc)
|
27
|
+
@bc.receive_line("NICK speccer")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
data/spec/irc_message_spec.rb
CHANGED
@@ -45,3 +45,14 @@ describe IrcMessage do
|
|
45
45
|
line2.args.last.should == "three"
|
46
46
|
end
|
47
47
|
end
|
48
|
+
|
49
|
+
describe IrcMessage, "CTCP" do
|
50
|
+
it "should parse basic ACTION messages" do
|
51
|
+
msg = IrcMessage.parse(":user1 PRIVMSG #room :\1ACTION is a loser on IRC\1")
|
52
|
+
msg.command.should == 'PRIVMSG'
|
53
|
+
msg.args.should == ['#room', 'is a loser on IRC']
|
54
|
+
msg.ctcp?.should == true
|
55
|
+
msg.action?.should == true
|
56
|
+
msg.to_s.should == ":user1 PRIVMSG #room :\1ACTION is a loser on IRC\1"
|
57
|
+
end
|
58
|
+
end
|
data/spec/irc_server_spec.rb
CHANGED
@@ -3,26 +3,30 @@ require 'tkellem/irc_server'
|
|
3
3
|
|
4
4
|
include Tkellem
|
5
5
|
|
6
|
-
describe
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
describe Bouncer, "connection" do
|
7
|
+
before do
|
8
|
+
EM.stub!(:add_timer).and_return(nil)
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
def make_server
|
12
|
+
b = Bouncer.new(NetworkUser.new(:user => User.new(:username => 'speccer'), :network => Network.new))
|
13
|
+
b
|
13
14
|
end
|
14
15
|
|
15
16
|
def send_welcome(s, &just_before_last)
|
16
|
-
s.
|
17
|
-
s.
|
18
|
-
s.
|
17
|
+
s.should_receive(:send_msg).with("USER speccer somehost tkellem :speccer")
|
18
|
+
s.should_receive(:send_msg).with("NICK speccer")
|
19
|
+
s.should_receive(:send_msg).with("AWAY :Away")
|
20
|
+
s.connection_established(nil)
|
21
|
+
s.server_msg(IrcMessage.parse("001 blah blah"))
|
22
|
+
s.server_msg(IrcMessage.parse("002 more blah"))
|
23
|
+
s.server_msg(IrcMessage.parse("003 even more blah"))
|
19
24
|
just_before_last && just_before_last.call
|
20
|
-
s.
|
25
|
+
s.server_msg(IrcMessage.parse("376 :end of MOTD"))
|
21
26
|
end
|
22
27
|
|
23
28
|
def connected_server
|
24
29
|
s = make_server
|
25
|
-
s.post_init
|
26
30
|
send_welcome(s)
|
27
31
|
s.connected?.should be_true
|
28
32
|
s
|
@@ -31,30 +35,57 @@ describe IrcServer, "connection" do
|
|
31
35
|
it "should connect to the server on creation" do
|
32
36
|
s = make_server
|
33
37
|
s.connected?.should_not be_true
|
34
|
-
s.should_receive(:
|
35
|
-
s.should_receive(:
|
36
|
-
s.
|
38
|
+
s.should_receive(:send_msg).with("USER speccer somehost tkellem :speccer")
|
39
|
+
s.should_receive(:send_msg).with("NICK speccer")
|
40
|
+
s.should_receive(:send_msg).with("AWAY :Away")
|
41
|
+
s.connection_established(nil)
|
37
42
|
end
|
38
43
|
|
39
|
-
it "should
|
40
|
-
s =
|
41
|
-
s.
|
42
|
-
s.
|
44
|
+
it "should pong" do
|
45
|
+
s = connected_server
|
46
|
+
s.should_receive(:send_msg).with("PONG tkellem!tkellem :HAI")
|
47
|
+
s.server_msg(IrcMessage.parse(":speccer!test@host ping :HAI"))
|
48
|
+
end
|
43
49
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
s.should_receive(:send_data).with("JOIN #test1\r\n")
|
48
|
-
s.should_receive(:send_data).with("JOIN #test2\r\n")
|
50
|
+
def tk_server
|
51
|
+
@tk_server ||= TkellemServer.new
|
52
|
+
end
|
49
53
|
|
50
|
-
|
54
|
+
def network_user(opts = {})
|
55
|
+
opts[:user] ||= @user ||= User.create!(:username => 'speccer', :password => 'test123')
|
56
|
+
opts[:network] ||= @network ||= Network.create!(:name => 'localhost')
|
57
|
+
@network_user ||= NetworkUser.create!(opts)
|
58
|
+
end
|
51
59
|
|
52
|
-
|
60
|
+
def bouncer(opts = {})
|
61
|
+
tk_server
|
62
|
+
network_user
|
63
|
+
@bouncer = @tk_server.bouncers.values.last
|
64
|
+
if opts[:connect]
|
65
|
+
@server_conn = em(IrcServerConnection).new(@bouncer, false)
|
66
|
+
@server_conn.stub!(:send_data)
|
67
|
+
@bouncer.connection_established(@server_conn)
|
68
|
+
@bouncer.send :ready!
|
69
|
+
end
|
70
|
+
@bouncer
|
53
71
|
end
|
54
72
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
73
|
+
def client_connection(opts = {})
|
74
|
+
@client ||= em(BouncerConnection).new(tk_server, false)
|
75
|
+
if opts[:connect]
|
76
|
+
end
|
77
|
+
@client
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should force the client nick on connect" do
|
81
|
+
network_user(:nick => 'mynick')
|
82
|
+
bouncer(:connect => true)
|
83
|
+
@bouncer.server_msg(m ":mynick JOIN #t1")
|
84
|
+
client_connection
|
85
|
+
@client.should_receive(:send_msg).with(":some_other_nick NICK mynick")
|
86
|
+
@client.should_receive(:send_msg).with(":mynick JOIN #t1")
|
87
|
+
@client.receive_line("PASS test123")
|
88
|
+
@client.receive_line("NICK some_other_nick")
|
89
|
+
@client.receive_line("USER #{@user.username}@#{@network.name} a b :c")
|
59
90
|
end
|
60
91
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,5 +4,31 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
4
4
|
require 'tkellem'
|
5
5
|
require 'rspec'
|
6
6
|
|
7
|
+
Tkellem::EasyLogger.logger = Logger.new("test.log")
|
8
|
+
ActiveRecord::Base.logger = Tkellem::EasyLogger.logger
|
9
|
+
|
10
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
11
|
+
ActiveRecord::Migration.verbose = false
|
12
|
+
ActiveRecord::Migrator.migrate(File.expand_path("../../lib/tkellem/migrations", __FILE__), nil)
|
13
|
+
|
7
14
|
RSpec.configure do |config|
|
15
|
+
config.before(:each) do
|
16
|
+
ActiveRecord::Base.connection.increment_open_transactions
|
17
|
+
ActiveRecord::Base.connection.begin_db_transaction
|
18
|
+
end
|
19
|
+
|
20
|
+
config.after(:each) do
|
21
|
+
ActiveRecord::Base.connection.rollback_db_transaction
|
22
|
+
ActiveRecord::Base.connection.decrement_open_transactions
|
23
|
+
end
|
24
|
+
|
25
|
+
def m(line)
|
26
|
+
IrcMessage.parse(line)
|
27
|
+
end
|
28
|
+
|
29
|
+
def em(mod)
|
30
|
+
c = Class.new
|
31
|
+
c.send(:include, mod)
|
32
|
+
c
|
33
|
+
end
|
8
34
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: tkellem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.8.
|
5
|
+
version: 0.8.4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Brian Palmer
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-06-
|
13
|
+
date: 2011-06-17 00:00:00 -06:00
|
14
14
|
default_executable: tkellem
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -149,6 +149,7 @@ files:
|
|
149
149
|
- lib/tkellem/tkellem_server.rb
|
150
150
|
- lib/tkellem/version.rb
|
151
151
|
- resources/bot_command_descriptions.yml
|
152
|
+
- spec/bouncer_connection_spec.rb
|
152
153
|
- spec/irc_message_spec.rb
|
153
154
|
- spec/irc_server_spec.rb
|
154
155
|
- spec/spec_helper.rb
|
@@ -182,6 +183,7 @@ signing_key:
|
|
182
183
|
specification_version: 3
|
183
184
|
summary: IRC bouncer with multi-client support
|
184
185
|
test_files:
|
186
|
+
- spec/bouncer_connection_spec.rb
|
185
187
|
- spec/irc_message_spec.rb
|
186
188
|
- spec/irc_server_spec.rb
|
187
189
|
- spec/spec_helper.rb
|