tkellem 0.8.7 → 0.8.8
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/Gemfile +1 -1
- data/Rakefile +0 -23
- data/lib/tkellem/bouncer.rb +3 -14
- data/lib/tkellem/bouncer_connection.rb +1 -1
- data/lib/tkellem/irc_message.rb +4 -2
- data/lib/tkellem/irc_server.rb +86 -14
- data/lib/tkellem/models/host.rb +4 -0
- data/lib/tkellem/plugins/push_service.rb +2 -1
- data/lib/tkellem/version.rb +1 -1
- data/spec/irc_message_spec.rb +8 -0
- data/spec/irc_server_spec.rb +4 -3
- data/tkellem.gemspec +0 -4
- metadata +10 -43
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -11,26 +11,3 @@ RSpec::Core::RakeTask.new(:rcov) do |t|
|
|
11
11
|
t.rcov = true
|
12
12
|
t.rcov_opts = ["--exclude", "spec,gems/,rubygems/"]
|
13
13
|
end
|
14
|
-
|
15
|
-
require 'yard'
|
16
|
-
YARD::Rake::YardocTask.new(:doc) do |t|
|
17
|
-
version = Tkellem::VERSION
|
18
|
-
t.options = ["--title", "tkellem #{version}", "--files", "LICENSE,README.md"]
|
19
|
-
end
|
20
|
-
|
21
|
-
begin
|
22
|
-
require 'jeweler'
|
23
|
-
Jeweler::Tasks.new do |gem|
|
24
|
-
gem.name = 'tkellem'
|
25
|
-
gem.summary = 'IRC bouncer with multi-client support'
|
26
|
-
gem.email = 'brian@codekitchen.net'
|
27
|
-
gem.homepage = 'http://github.com/codekitchen/tkellem'
|
28
|
-
gem.authors = ['Brian Palmer']
|
29
|
-
gem.add_dependency 'eventmachine'
|
30
|
-
gem.executables = %w(tkellem)
|
31
|
-
end
|
32
|
-
|
33
|
-
Jeweler::GemcutterTasks.new
|
34
|
-
rescue LoadError
|
35
|
-
# om nom nom
|
36
|
-
end
|
data/lib/tkellem/bouncer.rb
CHANGED
@@ -183,7 +183,7 @@ class Bouncer
|
|
183
183
|
@conn = nil
|
184
184
|
@connected = false
|
185
185
|
@connected_at = nil
|
186
|
-
@active_conns.each { |c,s| c.
|
186
|
+
@active_conns.each { |c,s| c.close_connection }
|
187
187
|
connect!
|
188
188
|
end
|
189
189
|
|
@@ -200,19 +200,8 @@ class Bouncer
|
|
200
200
|
end
|
201
201
|
|
202
202
|
def connect!
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
if span < 5 || hosts.length < 1
|
207
|
-
EM.add_timer(5) { connect! }
|
208
|
-
return
|
209
|
-
end
|
210
|
-
@last_connect = Time.now
|
211
|
-
@cur_host = (@cur_host || 0) % hosts.length
|
212
|
-
host = hosts[@cur_host]
|
213
|
-
failsafe("connect: #{host}") do
|
214
|
-
EM.connect(host.address, host.port, IrcServerConnection, self, host.ssl)
|
215
|
-
end
|
203
|
+
@connector ||= IrcServerConnection.connector(self, network)
|
204
|
+
@connector.connect!
|
216
205
|
end
|
217
206
|
|
218
207
|
def ready!
|
@@ -97,7 +97,7 @@ module BouncerConnection
|
|
97
97
|
command = msg.command
|
98
98
|
if @state != :auth && command == 'PRIVMSG' && msg.args.first == '-tkellem'
|
99
99
|
msg_tkellem(IrcMessage.new(nil, 'TKELLEM', [msg.args.last]))
|
100
|
-
elsif command == 'TKELLEM'
|
100
|
+
elsif command == 'TKELLEM' || command == 'TK'
|
101
101
|
msg_tkellem(msg)
|
102
102
|
elsif command == 'CAP'
|
103
103
|
# TODO: full support for CAP -- this just gets mobile colloquy connecting
|
data/lib/tkellem/irc_message.rb
CHANGED
@@ -34,9 +34,11 @@ class IrcMessage < Struct.new(:prefix, :command, :args, :ctcp)
|
|
34
34
|
# /msg #someroom hey guys
|
35
35
|
def self.parse_client_command(line)
|
36
36
|
return nil unless line[0] == '/'[0]
|
37
|
+
if line =~ %r{^/msg\s+(\S+)\s+(.*)$}
|
38
|
+
line = "/PRIVMSG #{$1} :#{$2}"
|
39
|
+
end
|
37
40
|
msg = parse(line[1..-1])
|
38
41
|
return nil unless msg
|
39
|
-
msg.command = 'PRIVMSG' if msg.command == 'MSG'
|
40
42
|
msg
|
41
43
|
end
|
42
44
|
|
@@ -56,7 +58,7 @@ class IrcMessage < Struct.new(:prefix, :command, :args, :ctcp)
|
|
56
58
|
line = []
|
57
59
|
line << ":#{prefix}" unless prefix.nil?
|
58
60
|
line << command
|
59
|
-
ext_arg = args.last if args.last && args.last.match(%r{
|
61
|
+
ext_arg = args.last if args.last && args.last.match(%r{^:|\s})
|
60
62
|
line += ext_arg ? args[0...-1] : args
|
61
63
|
if ctcp?
|
62
64
|
line << ":\x01#{ctcp} #{ext_arg}\x01"
|
data/lib/tkellem/irc_server.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
require 'set'
|
2
1
|
require 'eventmachine'
|
2
|
+
require 'set'
|
3
|
+
require 'socket'
|
4
|
+
|
3
5
|
require 'tkellem/irc_message'
|
4
6
|
require 'tkellem/bouncer_connection'
|
5
7
|
|
@@ -7,45 +9,115 @@ module Tkellem
|
|
7
9
|
|
8
10
|
module IrcServerConnection
|
9
11
|
include EM::Protocols::LineText2
|
10
|
-
include Tkellem::EasyLogger
|
11
12
|
|
12
|
-
def initialize(bouncer, do_ssl)
|
13
|
+
def initialize(connection_state, bouncer, do_ssl)
|
13
14
|
set_delimiter "\r\n"
|
14
15
|
@bouncer = bouncer
|
15
16
|
@ssl = do_ssl
|
17
|
+
@connection_state = connection_state
|
18
|
+
@connected = false
|
16
19
|
end
|
17
20
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
+
def connection_completed
|
22
|
+
if @ssl
|
23
|
+
@bouncer.failsafe(:connection_completed) do
|
21
24
|
@bouncer.debug "starting TLS"
|
22
25
|
# TODO: support strict cert checks
|
23
26
|
start_tls :verify_peer => false
|
24
|
-
else
|
25
|
-
ssl_handshake_completed
|
26
27
|
end
|
28
|
+
else
|
29
|
+
ssl_handshake_completed
|
27
30
|
end
|
28
31
|
end
|
29
32
|
|
30
33
|
def ssl_handshake_completed
|
31
|
-
failsafe(:ssl_handshake_completed) do
|
32
|
-
|
34
|
+
@bouncer.failsafe(:ssl_handshake_completed) do
|
35
|
+
@connected = true
|
36
|
+
@bouncer.connection_established(self)
|
33
37
|
end
|
34
38
|
end
|
35
39
|
|
36
40
|
def receive_line(line)
|
37
|
-
failsafe(:receive_line) do
|
38
|
-
trace "from server: #{line}"
|
41
|
+
@bouncer.failsafe(:receive_line) do
|
42
|
+
@bouncer.trace "from server: #{line}"
|
39
43
|
msg = IrcMessage.parse(line)
|
40
44
|
@bouncer.server_msg(msg)
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
44
48
|
def unbind
|
45
|
-
failsafe(:unbind) do
|
46
|
-
@
|
49
|
+
@bouncer.failsafe(:unbind) do
|
50
|
+
if @connected
|
51
|
+
@bouncer.disconnected!
|
52
|
+
else
|
53
|
+
@bouncer.debug "Connection failed, trying next"
|
54
|
+
@connection_state.connect!
|
55
|
+
end
|
47
56
|
end
|
48
57
|
end
|
58
|
+
|
59
|
+
class ConnectionState < Struct.new(:bouncer, :network, :attempted, :getting)
|
60
|
+
def initialize(bouncer, network)
|
61
|
+
super(bouncer, network, Set.new, false)
|
62
|
+
reset
|
63
|
+
end
|
64
|
+
|
65
|
+
def connect!
|
66
|
+
raise("already in the process of getting an address") if getting
|
67
|
+
self.getting = true
|
68
|
+
network.reload
|
69
|
+
host_infos = network.hosts.map { |h| h.attributes }
|
70
|
+
EM.defer(proc { find_address(host_infos) }, method(:got_address))
|
71
|
+
end
|
72
|
+
|
73
|
+
def reset
|
74
|
+
self.attempted.clear
|
75
|
+
end
|
76
|
+
|
77
|
+
# runs in threadpool
|
78
|
+
def find_address(hosts)
|
79
|
+
candidates = Set.new
|
80
|
+
hosts.each do |host|
|
81
|
+
Socket.getaddrinfo(host['address'], host['port'], Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP).each do |found|
|
82
|
+
candidates << [found[3], host['port'], host['ssl']]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
to_try = candidates.to_a.sort_by { rand }.find { |c| !attempted.include?(c) }
|
87
|
+
if to_try.nil?
|
88
|
+
# we've tried all possible hosts, start over
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
|
92
|
+
return to_try
|
93
|
+
end
|
94
|
+
|
95
|
+
# back on event thread
|
96
|
+
def got_address(to_try)
|
97
|
+
self.getting = false
|
98
|
+
|
99
|
+
if !to_try
|
100
|
+
# sleep for a bit and try again
|
101
|
+
bouncer.debug "All available addresses failed, sleeping 5s and then trying over"
|
102
|
+
reset
|
103
|
+
EM.add_timer(5) { connect! }
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
107
|
+
attempted << to_try
|
108
|
+
address, port, ssl = to_try
|
109
|
+
|
110
|
+
bouncer.debug "Connecting to: #{Host.address_string(address, port, ssl)}"
|
111
|
+
bouncer.failsafe("connect: #{Host.address_string(address, port, ssl)}") do
|
112
|
+
EM.connect(address, port, IrcServerConnection, self, bouncer, ssl)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.connector(bouncer, network)
|
118
|
+
ConnectionState.new(bouncer, network)
|
119
|
+
end
|
120
|
+
|
49
121
|
end
|
50
122
|
|
51
123
|
end
|
data/lib/tkellem/models/host.rb
CHANGED
data/lib/tkellem/version.rb
CHANGED
data/spec/irc_message_spec.rb
CHANGED
@@ -22,6 +22,14 @@ describe IrcMessage, ".parse" do
|
|
22
22
|
line.replay.should == orig
|
23
23
|
end
|
24
24
|
|
25
|
+
it "should parse and replay messages with leading colons" do
|
26
|
+
orig = "MSG #myroom ::)"
|
27
|
+
line = IrcMessage.parse(orig)
|
28
|
+
line.command.should == "MSG"
|
29
|
+
line.args.should == ["#myroom", ":)"]
|
30
|
+
line.replay.should == orig
|
31
|
+
end
|
32
|
+
|
25
33
|
it "should parse with no arguments" do
|
26
34
|
line = IrcMessage.parse("AWAY")
|
27
35
|
line.command.should == "AWAY"
|
data/spec/irc_server_spec.rb
CHANGED
@@ -9,7 +9,8 @@ describe Bouncer, "connection" do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def make_server
|
12
|
-
|
12
|
+
network = Network.create!(:hosts => [Host.create!(:address => 'localhost', :port => 4321)], :name => 'test')
|
13
|
+
b = Bouncer.new(NetworkUser.create!(:user => User.new(:username => 'speccer'), :network => network))
|
13
14
|
b
|
14
15
|
end
|
15
16
|
|
@@ -66,7 +67,7 @@ describe Bouncer, "connection" do
|
|
66
67
|
network_user
|
67
68
|
@bouncer = $tk_server.bouncers.values.last
|
68
69
|
if opts[:connect]
|
69
|
-
@server_conn = em(IrcServerConnection).new(@bouncer, false)
|
70
|
+
@server_conn = em(IrcServerConnection).new(nil, @bouncer, false)
|
70
71
|
@server_conn.stub!(:send_data)
|
71
72
|
@bouncer.connection_established(@server_conn)
|
72
73
|
@bouncer.send :ready!
|
@@ -96,7 +97,7 @@ describe Bouncer, "connection" do
|
|
96
97
|
it "should attempt another nick if the default is taken" do
|
97
98
|
network_user(:nick => 'mynick')
|
98
99
|
bouncer
|
99
|
-
@server_conn = em(IrcServerConnection).new(@bouncer, false)
|
100
|
+
@server_conn = em(IrcServerConnection).new(nil, @bouncer, false)
|
100
101
|
@server_conn.stub!(:send_data)
|
101
102
|
@bouncer.connection_established(@server_conn)
|
102
103
|
@server_conn.should_receive(:send_data).with("NICK mynick_\r\n")
|
data/tkellem.gemspec
CHANGED
@@ -18,13 +18,9 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
20
|
s.add_dependency "eventmachine", "~> 0.12.10"
|
21
|
-
s.add_dependency "daemons", "~> 1.1.0"
|
22
|
-
s.add_dependency "json"
|
23
21
|
s.add_dependency "activerecord", "~> 3.0.0"
|
24
22
|
s.add_dependency "sqlite3", "~> 1.3.3"
|
25
23
|
|
26
24
|
s.add_development_dependency "rspec", "~> 2.5"
|
27
25
|
s.add_development_dependency "rcov"
|
28
|
-
s.add_development_dependency "yard"
|
29
26
|
end
|
30
|
-
|
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.8
|
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-
|
13
|
+
date: 2011-07-01 00:00:00 -06:00
|
14
14
|
default_executable: tkellem
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -24,83 +24,50 @@ dependencies:
|
|
24
24
|
version: 0.12.10
|
25
25
|
type: :runtime
|
26
26
|
version_requirements: *id001
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: daemons
|
29
|
-
prerelease: false
|
30
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
-
none: false
|
32
|
-
requirements:
|
33
|
-
- - ~>
|
34
|
-
- !ruby/object:Gem::Version
|
35
|
-
version: 1.1.0
|
36
|
-
type: :runtime
|
37
|
-
version_requirements: *id002
|
38
|
-
- !ruby/object:Gem::Dependency
|
39
|
-
name: json
|
40
|
-
prerelease: false
|
41
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
|
-
requirements:
|
44
|
-
- - ">="
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: "0"
|
47
|
-
type: :runtime
|
48
|
-
version_requirements: *id003
|
49
27
|
- !ruby/object:Gem::Dependency
|
50
28
|
name: activerecord
|
51
29
|
prerelease: false
|
52
|
-
requirement: &
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
53
31
|
none: false
|
54
32
|
requirements:
|
55
33
|
- - ~>
|
56
34
|
- !ruby/object:Gem::Version
|
57
35
|
version: 3.0.0
|
58
36
|
type: :runtime
|
59
|
-
version_requirements: *
|
37
|
+
version_requirements: *id002
|
60
38
|
- !ruby/object:Gem::Dependency
|
61
39
|
name: sqlite3
|
62
40
|
prerelease: false
|
63
|
-
requirement: &
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
64
42
|
none: false
|
65
43
|
requirements:
|
66
44
|
- - ~>
|
67
45
|
- !ruby/object:Gem::Version
|
68
46
|
version: 1.3.3
|
69
47
|
type: :runtime
|
70
|
-
version_requirements: *
|
48
|
+
version_requirements: *id003
|
71
49
|
- !ruby/object:Gem::Dependency
|
72
50
|
name: rspec
|
73
51
|
prerelease: false
|
74
|
-
requirement: &
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
75
53
|
none: false
|
76
54
|
requirements:
|
77
55
|
- - ~>
|
78
56
|
- !ruby/object:Gem::Version
|
79
57
|
version: "2.5"
|
80
58
|
type: :development
|
81
|
-
version_requirements: *
|
59
|
+
version_requirements: *id004
|
82
60
|
- !ruby/object:Gem::Dependency
|
83
61
|
name: rcov
|
84
62
|
prerelease: false
|
85
|
-
requirement: &
|
86
|
-
none: false
|
87
|
-
requirements:
|
88
|
-
- - ">="
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version: "0"
|
91
|
-
type: :development
|
92
|
-
version_requirements: *id007
|
93
|
-
- !ruby/object:Gem::Dependency
|
94
|
-
name: yard
|
95
|
-
prerelease: false
|
96
|
-
requirement: &id008 !ruby/object:Gem::Requirement
|
63
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
97
64
|
none: false
|
98
65
|
requirements:
|
99
66
|
- - ">="
|
100
67
|
- !ruby/object:Gem::Version
|
101
68
|
version: "0"
|
102
69
|
type: :development
|
103
|
-
version_requirements: *
|
70
|
+
version_requirements: *id005
|
104
71
|
description:
|
105
72
|
email:
|
106
73
|
- brian@codekitchen.net
|