tkellem 0.8.3 → 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|