tkellem 0.9.0.beta3 → 0.9.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/tkellem/bouncer.rb +14 -30
- data/lib/tkellem/bouncer_connection.rb +58 -42
- data/lib/tkellem/daemon.rb +18 -7
- data/lib/tkellem/irc_server.rb +124 -0
- data/lib/tkellem/migrations/001_init_db.rb +22 -39
- data/lib/tkellem/migrations/002_at_connect_columns.rb +4 -21
- data/lib/tkellem/migrations/003_settings.rb +9 -18
- data/lib/tkellem/models/host.rb +2 -2
- data/lib/tkellem/models/listen_address.rb +1 -13
- data/lib/tkellem/models/network.rb +7 -11
- data/lib/tkellem/models/network_user.rb +6 -22
- data/lib/tkellem/models/setting.rb +3 -3
- data/lib/tkellem/models/user.rb +6 -13
- data/lib/tkellem/plugins/backlog.rb +31 -43
- data/lib/tkellem/socket_server.rb +7 -15
- data/lib/tkellem/tkellem_bot.rb +19 -11
- data/lib/tkellem/tkellem_server.rb +70 -77
- data/lib/tkellem/version.rb +1 -1
- data/lib/tkellem.rb +10 -1
- data/spec/bouncer_connection_spec.rb +37 -0
- data/spec/irc_server_spec.rb +145 -0
- data/spec/spec_helper.rb +17 -4
- data/tkellem.gemspec +5 -6
- metadata +31 -43
- data/lib/tkellem/celluloid_tools.rb +0 -131
data/lib/tkellem/bouncer.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
require 'active_support/core_ext/class/attribute_accessors'
|
2
|
-
require 'celluloid/io'
|
3
2
|
|
3
|
+
require 'tkellem/irc_server'
|
4
4
|
require 'tkellem/bouncer_connection'
|
5
5
|
|
6
6
|
module Tkellem
|
7
7
|
|
8
8
|
class Bouncer
|
9
|
-
include Celluloid::IO
|
10
9
|
include Tkellem::EasyLogger
|
11
|
-
include Tkellem::CelluloidTools::LineReader
|
12
10
|
|
13
11
|
attr_reader :user, :network, :nick, :network_user, :connected_at
|
14
12
|
cattr_accessor :plugins
|
@@ -33,7 +31,7 @@ class Bouncer
|
|
33
31
|
# clients waiting for us to connect to the irc server
|
34
32
|
@waiting_clients = []
|
35
33
|
|
36
|
-
|
34
|
+
connect!
|
37
35
|
end
|
38
36
|
|
39
37
|
def data(key)
|
@@ -104,13 +102,6 @@ class Bouncer
|
|
104
102
|
end
|
105
103
|
end
|
106
104
|
|
107
|
-
def receive_line(line)
|
108
|
-
trace "from server: #{line}"
|
109
|
-
return if line.blank?
|
110
|
-
msg = IrcMessage.parse(line)
|
111
|
-
server_msg(msg)
|
112
|
-
end
|
113
|
-
|
114
105
|
def server_msg(msg)
|
115
106
|
return if plugins.any? do |plugin|
|
116
107
|
!plugin.server_msg(self, msg)
|
@@ -187,27 +178,32 @@ class Bouncer
|
|
187
178
|
alias_method :log_name, :name
|
188
179
|
|
189
180
|
def send_msg(msg)
|
181
|
+
return unless @conn
|
190
182
|
trace "to server: #{msg}"
|
191
|
-
@
|
183
|
+
@conn.send_data("#{msg}\r\n")
|
192
184
|
end
|
193
185
|
|
194
|
-
def connection_established
|
195
|
-
|
186
|
+
def connection_established(conn)
|
187
|
+
@conn = conn
|
196
188
|
# TODO: support sending a real username, realname, etc
|
197
189
|
send_msg("USER #{@user.username} somehost tkellem :#{@user.name}@tkellem")
|
198
190
|
change_nick(@nick, true)
|
199
191
|
@connected_at = Time.now
|
200
|
-
async.run
|
201
192
|
end
|
202
193
|
|
203
194
|
def disconnected!
|
204
195
|
debug "OMG we got disconnected."
|
196
|
+
@conn = nil
|
205
197
|
@connected = false
|
206
198
|
@connected_at = nil
|
207
199
|
@active_conns.each { |c,s| c.close_connection }
|
208
200
|
connect!
|
209
201
|
end
|
210
202
|
|
203
|
+
def kill!
|
204
|
+
@active_conns.each { |c,s| c.close_connection }
|
205
|
+
end
|
206
|
+
|
211
207
|
protected
|
212
208
|
|
213
209
|
def change_nick(new_nick, force = false)
|
@@ -220,21 +216,9 @@ class Bouncer
|
|
220
216
|
@welcomes.each { |msg| msg.args[0] = nick; bouncer_conn.send_msg(msg) }
|
221
217
|
end
|
222
218
|
|
223
|
-
def connect
|
224
|
-
|
225
|
-
|
226
|
-
# Note: Celluloid::TCPSocket also is random rather than round-robin when
|
227
|
-
# picking the DNS IP record to connect to
|
228
|
-
host = hosts[rand(hosts.length)]
|
229
|
-
begin
|
230
|
-
info "Connecting to #{host}"
|
231
|
-
@socket = TCPSocket.new(host.address, host.port)
|
232
|
-
if host.ssl
|
233
|
-
@socket = SSLSocket.new(@socket)
|
234
|
-
@socket.connect
|
235
|
-
end
|
236
|
-
end
|
237
|
-
connection_established
|
219
|
+
def connect!
|
220
|
+
@connector ||= IrcServerConnection.connector(self, network)
|
221
|
+
@connector.connect!
|
238
222
|
end
|
239
223
|
|
240
224
|
def ready!
|
@@ -1,19 +1,19 @@
|
|
1
1
|
require 'active_support/core_ext/object/blank'
|
2
|
-
require 'celluloid/io'
|
3
2
|
|
3
|
+
require 'eventmachine'
|
4
4
|
require 'tkellem/irc_message'
|
5
|
-
require 'tkellem/celluloid_tools'
|
6
5
|
|
7
6
|
module Tkellem
|
8
7
|
|
9
|
-
|
10
|
-
include
|
8
|
+
module BouncerConnection
|
9
|
+
include EM::Protocols::LineText2
|
11
10
|
include Tkellem::EasyLogger
|
12
|
-
include Tkellem::CelluloidTools::LineReader
|
13
11
|
|
14
|
-
def initialize(tkellem_server,
|
12
|
+
def initialize(tkellem_server, do_ssl)
|
13
|
+
set_delimiter "\r\n"
|
14
|
+
|
15
|
+
@ssl = do_ssl
|
15
16
|
@tkellem = tkellem_server
|
16
|
-
@socket = socket
|
17
17
|
|
18
18
|
@state = :auth
|
19
19
|
@name = 'new-conn'
|
@@ -31,11 +31,24 @@ class BouncerConnection
|
|
31
31
|
@data[key] ||= {}
|
32
32
|
end
|
33
33
|
|
34
|
+
def post_init
|
35
|
+
failsafe(:post_init) do
|
36
|
+
if ssl
|
37
|
+
start_tls :verify_peer => false
|
38
|
+
else
|
39
|
+
ssl_handshake_completed
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def ssl_handshake_completed
|
45
|
+
end
|
46
|
+
|
34
47
|
def error!(msg)
|
35
48
|
info("ERROR :#{msg}")
|
36
49
|
say_as_tkellem(msg)
|
37
50
|
send_msg("ERROR :#{msg}")
|
38
|
-
close_connection
|
51
|
+
close_connection(true)
|
39
52
|
end
|
40
53
|
|
41
54
|
def connect_to_irc_server
|
@@ -77,38 +90,40 @@ class BouncerConnection
|
|
77
90
|
end
|
78
91
|
|
79
92
|
def receive_line(line)
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
@
|
96
|
-
|
97
|
-
@
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
@username
|
93
|
+
failsafe("message: {#{line}}") do
|
94
|
+
trace "from client: #{line}"
|
95
|
+
return if line.blank?
|
96
|
+
msg = IrcMessage.parse(line)
|
97
|
+
|
98
|
+
command = msg.command
|
99
|
+
if @state != :auth && command == 'PRIVMSG' && msg.args.first == '-tkellem'
|
100
|
+
msg_tkellem(IrcMessage.new(nil, 'TKELLEM', [msg.args.last]))
|
101
|
+
elsif command == 'TKELLEM' || command == 'TK'
|
102
|
+
msg_tkellem(msg)
|
103
|
+
elsif command == 'CAP'
|
104
|
+
# TODO: full support for CAP -- this just gets mobile colloquy connecting
|
105
|
+
if msg.args.first =~ /req/i
|
106
|
+
send_msg("CAP NAK")
|
107
|
+
end
|
108
|
+
elsif command == 'PASS' && @state == :auth
|
109
|
+
@password = msg.args.first
|
110
|
+
elsif command == 'NICK' && @state == :auth
|
111
|
+
@connecting_nick = msg.args.first
|
112
|
+
maybe_connect
|
113
|
+
elsif command == 'QUIT'
|
114
|
+
close_connection
|
115
|
+
elsif command == 'USER' && @state == :auth
|
116
|
+
unless @username
|
117
|
+
@username, @conn_info = msg.args.first.strip.split('@', 2).map { |a| a.downcase }
|
118
|
+
end
|
119
|
+
maybe_connect
|
120
|
+
elsif @state == :auth
|
121
|
+
error!("Protocol error. You must authenticate first.")
|
122
|
+
elsif @state == :connected
|
123
|
+
@bouncer.client_msg(self, msg)
|
124
|
+
else
|
125
|
+
say_as_tkellem("You must connect to an irc network to do that.")
|
104
126
|
end
|
105
|
-
maybe_connect
|
106
|
-
elsif @state == :auth
|
107
|
-
error!("Protocol error. You must authenticate first.")
|
108
|
-
elsif @state == :connected
|
109
|
-
@bouncer.client_msg(self, msg)
|
110
|
-
else
|
111
|
-
say_as_tkellem("You must connect to an irc network to do that.")
|
112
127
|
end
|
113
128
|
end
|
114
129
|
|
@@ -182,13 +197,14 @@ class BouncerConnection
|
|
182
197
|
end
|
183
198
|
|
184
199
|
def send_msg(msg)
|
185
|
-
return if @socket.closed?
|
186
200
|
trace "to client: #{msg}"
|
187
|
-
|
201
|
+
send_data("#{msg}\r\n")
|
188
202
|
end
|
189
203
|
|
190
204
|
def unbind
|
191
|
-
|
205
|
+
failsafe(:unbind) do
|
206
|
+
@bouncer.disconnect_client(self) if @bouncer
|
207
|
+
end
|
192
208
|
end
|
193
209
|
end
|
194
210
|
|
data/lib/tkellem/daemon.rb
CHANGED
@@ -2,6 +2,7 @@ require 'fileutils'
|
|
2
2
|
require 'optparse'
|
3
3
|
|
4
4
|
require 'tkellem'
|
5
|
+
require 'tkellem/socket_server'
|
5
6
|
|
6
7
|
class Tkellem::Daemon
|
7
8
|
attr_reader :options
|
@@ -14,7 +15,7 @@ class Tkellem::Daemon
|
|
14
15
|
end
|
15
16
|
|
16
17
|
def run
|
17
|
-
|
18
|
+
op = OptionParser.new do |opts|
|
18
19
|
opts.banner = "Usage #{$0} <command> <options>"
|
19
20
|
opts.separator %{\nWhere <command> is one of:
|
20
21
|
start start the jobs daemon
|
@@ -29,13 +30,14 @@ class Tkellem::Daemon
|
|
29
30
|
opts.on("-p", "--path", "Use alternate folder for tkellem data (default #{options[:path]})") { |p| options[:path] = p }
|
30
31
|
opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
|
31
32
|
end
|
32
|
-
|
33
|
+
op.parse!(@args)
|
33
34
|
|
34
35
|
FileUtils.mkdir_p(path)
|
35
36
|
File.chmod(0700, path)
|
36
37
|
command = @args.shift
|
37
38
|
case command
|
38
39
|
when 'start'
|
40
|
+
abort_if_running
|
39
41
|
daemonize
|
40
42
|
start
|
41
43
|
when 'stop'
|
@@ -85,8 +87,12 @@ class Tkellem::Daemon
|
|
85
87
|
end
|
86
88
|
|
87
89
|
def start
|
90
|
+
trap("INT") { EM.stop }
|
88
91
|
remove_files
|
89
|
-
|
92
|
+
EM.run do
|
93
|
+
@admin = EM.start_unix_domain_server(socket_file, Tkellem::SocketServer)
|
94
|
+
Tkellem::TkellemServer.new
|
95
|
+
end
|
90
96
|
ensure
|
91
97
|
remove_files
|
92
98
|
end
|
@@ -99,9 +105,6 @@ class Tkellem::Daemon
|
|
99
105
|
@daemon = true
|
100
106
|
File.open(pid_file, 'wb') { |f| f.write(Process.pid.to_s) }
|
101
107
|
|
102
|
-
# TODO: support syslog
|
103
|
-
require 'logger'
|
104
|
-
logger = Logger.new(log_file)
|
105
108
|
STDIN.reopen("/dev/null")
|
106
109
|
STDOUT.reopen(log_file, 'a')
|
107
110
|
STDERR.reopen(STDOUT)
|
@@ -133,8 +136,16 @@ class Tkellem::Daemon
|
|
133
136
|
pid.to_i > 0 ? pid.to_i : nil
|
134
137
|
end
|
135
138
|
|
139
|
+
def abort_if_running
|
140
|
+
pid = File.read(pid_file) if File.file?(pid_file)
|
141
|
+
if pid.to_i > 0
|
142
|
+
puts "tkellem already running, pid: #{pid}"
|
143
|
+
exit
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
136
147
|
def remove_files
|
137
|
-
FileUtils.rm(socket_file) if File.
|
148
|
+
FileUtils.rm(socket_file) if File.file?(socket_file)
|
138
149
|
return unless @daemon
|
139
150
|
pid = File.read(pid_file) if File.file?(pid_file)
|
140
151
|
if pid.to_i == Process.pid
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'set'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
require 'tkellem/irc_message'
|
6
|
+
require 'tkellem/bouncer_connection'
|
7
|
+
|
8
|
+
module Tkellem
|
9
|
+
|
10
|
+
module IrcServerConnection
|
11
|
+
include EM::Protocols::LineText2
|
12
|
+
|
13
|
+
def initialize(connection_state, bouncer, do_ssl)
|
14
|
+
set_delimiter "\r\n"
|
15
|
+
@bouncer = bouncer
|
16
|
+
@ssl = do_ssl
|
17
|
+
@connection_state = connection_state
|
18
|
+
@connected = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def connection_completed
|
22
|
+
if @ssl
|
23
|
+
@bouncer.failsafe(:connection_completed) do
|
24
|
+
@bouncer.debug "starting TLS"
|
25
|
+
# TODO: support strict cert checks
|
26
|
+
start_tls :verify_peer => false
|
27
|
+
end
|
28
|
+
else
|
29
|
+
ssl_handshake_completed
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def ssl_handshake_completed
|
34
|
+
@bouncer.failsafe(:ssl_handshake_completed) do
|
35
|
+
@connected = true
|
36
|
+
@bouncer.connection_established(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def receive_line(line)
|
41
|
+
@bouncer.failsafe(:receive_line) do
|
42
|
+
@bouncer.trace "from server: #{line}"
|
43
|
+
return if line.blank?
|
44
|
+
msg = IrcMessage.parse(line)
|
45
|
+
@bouncer.server_msg(msg)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def unbind
|
50
|
+
@bouncer.failsafe(:unbind) do
|
51
|
+
if @connected
|
52
|
+
@bouncer.disconnected!
|
53
|
+
else
|
54
|
+
@bouncer.debug "Connection failed, trying next"
|
55
|
+
@connection_state.connect!
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class ConnectionState < Struct.new(:bouncer, :network, :attempted, :getting)
|
61
|
+
def initialize(bouncer, network)
|
62
|
+
super(bouncer, network, Set.new, false)
|
63
|
+
reset
|
64
|
+
end
|
65
|
+
|
66
|
+
def connect!
|
67
|
+
raise("already in the process of getting an address") if getting
|
68
|
+
self.getting = true
|
69
|
+
network.reload
|
70
|
+
host_infos = network.hosts.map { |h| h.attributes }
|
71
|
+
EM.defer(proc { find_address(host_infos) }, method(:got_address))
|
72
|
+
end
|
73
|
+
|
74
|
+
def reset
|
75
|
+
self.attempted.clear
|
76
|
+
end
|
77
|
+
|
78
|
+
# runs in threadpool
|
79
|
+
def find_address(hosts)
|
80
|
+
candidates = Set.new
|
81
|
+
hosts.each do |host|
|
82
|
+
Socket.getaddrinfo(host['address'], host['port'], Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP).each do |found|
|
83
|
+
candidates << [found[3], host['port'], host['ssl']]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
to_try = candidates.to_a.sort_by { rand }.find { |c| !attempted.include?(c) }
|
88
|
+
if to_try.nil?
|
89
|
+
# we've tried all possible hosts, start over
|
90
|
+
return nil
|
91
|
+
end
|
92
|
+
|
93
|
+
return to_try
|
94
|
+
end
|
95
|
+
|
96
|
+
# back on event thread
|
97
|
+
def got_address(to_try)
|
98
|
+
self.getting = false
|
99
|
+
|
100
|
+
if !to_try
|
101
|
+
# sleep for a bit and try again
|
102
|
+
bouncer.debug "All available addresses failed, sleeping 5s and then trying over"
|
103
|
+
reset
|
104
|
+
EM.add_timer(5) { connect! }
|
105
|
+
return
|
106
|
+
end
|
107
|
+
|
108
|
+
attempted << to_try
|
109
|
+
address, port, ssl = to_try
|
110
|
+
|
111
|
+
bouncer.debug "Connecting to: #{Host.address_string(address, port, ssl)}"
|
112
|
+
bouncer.failsafe("connect: #{Host.address_string(address, port, ssl)}") do
|
113
|
+
EM.connect(address, port, IrcServerConnection, self, bouncer, ssl)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.connector(bouncer, network)
|
119
|
+
ConnectionState.new(bouncer, network)
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
@@ -1,50 +1,33 @@
|
|
1
|
-
|
2
|
-
up
|
3
|
-
|
4
|
-
|
1
|
+
class InitDb < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table 'listen_addresses' do |t|
|
4
|
+
t.string 'address', :null => false
|
5
|
+
t.integer 'port', :null => false
|
6
|
+
t.boolean 'ssl', :null => false, :default => false
|
5
7
|
end
|
6
8
|
|
7
|
-
create_table '
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
boolean 'ssl', :null => false, :default => false
|
9
|
+
create_table 'users' do |t|
|
10
|
+
t.string 'username', :null => false
|
11
|
+
t.string 'password'
|
12
|
+
t.string 'role', :null => false, :default => 'user'
|
12
13
|
end
|
13
14
|
|
14
|
-
create_table '
|
15
|
-
|
16
|
-
|
17
|
-
String 'password'
|
18
|
-
String 'role', :null => false, :default => 'user'
|
15
|
+
create_table 'networks' do |t|
|
16
|
+
t.belongs_to 'user'
|
17
|
+
t.string 'name', :null => false
|
19
18
|
end
|
20
19
|
|
21
|
-
create_table '
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
create_table 'hosts' do |t|
|
21
|
+
t.belongs_to 'network'
|
22
|
+
t.string 'address', :null => false
|
23
|
+
t.integer 'port', :null => false
|
24
|
+
t.boolean 'ssl', :null => false, :default => false
|
25
25
|
end
|
26
26
|
|
27
|
-
create_table '
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
Integer 'port', :null => false
|
32
|
-
boolean 'ssl', :null => false, :default => false
|
27
|
+
create_table 'network_users' do |t|
|
28
|
+
t.belongs_to 'user'
|
29
|
+
t.belongs_to 'network'
|
30
|
+
t.string 'nick'
|
33
31
|
end
|
34
|
-
|
35
|
-
create_table 'network_users' do
|
36
|
-
primary_key :id
|
37
|
-
foreign_key :user_id, :users
|
38
|
-
foreign_key :network_id, :networks
|
39
|
-
String 'nick'
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
down do
|
44
|
-
drop_table 'network_users'
|
45
|
-
drop_table 'hosts'
|
46
|
-
drop_table 'networks'
|
47
|
-
drop_table 'users'
|
48
|
-
drop_table 'listen_addresses'
|
49
32
|
end
|
50
33
|
end
|
@@ -1,24 +1,7 @@
|
|
1
|
-
|
2
|
-
up
|
3
|
-
|
4
|
-
|
5
|
-
end
|
6
|
-
|
7
|
-
alter_table 'networks' do
|
8
|
-
add_column 'at_connect', String, :text => true
|
9
|
-
end
|
10
|
-
alter_table 'network_users' do
|
11
|
-
add_column 'at_connect', String, :text => true
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
down do
|
16
|
-
alter_table 'network_users' do
|
17
|
-
drop_column 'at_connect'
|
18
|
-
end
|
19
|
-
alter_table 'networks' do
|
20
|
-
drop_column 'at_connect'
|
21
|
-
end
|
1
|
+
class AtConnectColumns < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
add_column 'networks', 'at_connect', 'text'
|
4
|
+
add_column 'network_users', 'at_connect', 'text'
|
22
5
|
end
|
23
6
|
end
|
24
7
|
|
@@ -1,22 +1,13 @@
|
|
1
|
-
|
2
|
-
up
|
3
|
-
|
4
|
-
|
1
|
+
class Settings < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table 'settings' do |t|
|
4
|
+
t.string :name, :null => false
|
5
|
+
t.string :value, :null => false
|
6
|
+
t.boolean :unchanged, :null => false, :default => true
|
5
7
|
end
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
String :value, :null => false
|
11
|
-
boolean :unchanged, :null => false, :default => true
|
12
|
-
end
|
13
|
-
|
14
|
-
self[:settings].insert(:name => 'user_registration', :value => 'closed')
|
15
|
-
self[:settings].insert(:name => 'recaptcha_api_key', :value => '')
|
16
|
-
self[:settings].insert(:name => 'allow_user_networks', :value => 'false')
|
17
|
-
end
|
18
|
-
|
19
|
-
down do
|
20
|
-
drop_table 'settings'
|
9
|
+
Tkellem::Setting.make_new('user_registration', 'closed')
|
10
|
+
Tkellem::Setting.make_new('recaptcha_api_key', '')
|
11
|
+
Tkellem::Setting.make_new('allow_user_networks', 'false')
|
21
12
|
end
|
22
13
|
end
|
data/lib/tkellem/models/host.rb
CHANGED
@@ -1,24 +1,12 @@
|
|
1
1
|
module Tkellem
|
2
2
|
|
3
|
-
class ListenAddress <
|
4
|
-
plugin :validation_class_methods
|
5
|
-
|
3
|
+
class ListenAddress < ActiveRecord::Base
|
6
4
|
validates_uniqueness_of :port, :scope => [:address]
|
7
5
|
validates_presence_of :address, :port
|
8
6
|
|
9
7
|
def to_s
|
10
8
|
"#{ssl ? 'ircs' : 'irc'}://#{address}:#{port}"
|
11
9
|
end
|
12
|
-
|
13
|
-
def after_create
|
14
|
-
super
|
15
|
-
$tkellem_server.try(:after_create, self)
|
16
|
-
end
|
17
|
-
|
18
|
-
def after_destroy
|
19
|
-
super
|
20
|
-
$tkellem_server.try(:after_destroy, self)
|
21
|
-
end
|
22
10
|
end
|
23
11
|
|
24
12
|
end
|
@@ -1,24 +1,20 @@
|
|
1
1
|
module Tkellem
|
2
2
|
|
3
|
-
class Network <
|
4
|
-
|
5
|
-
|
6
|
-
plugin :serialization
|
3
|
+
class Network < ActiveRecord::Base
|
4
|
+
has_many :hosts, :dependent => :destroy
|
5
|
+
accepts_nested_attributes_for :hosts
|
7
6
|
|
8
|
-
|
9
|
-
nested_attributes :hosts
|
10
|
-
|
11
|
-
one_to_many :network_users, :dependent => :destroy
|
7
|
+
has_many :network_users, :dependent => :destroy
|
12
8
|
# networks either belong to a specific user, or they are public and any user
|
13
9
|
# can join them.
|
14
|
-
|
10
|
+
belongs_to :user
|
15
11
|
|
16
12
|
validates_uniqueness_of :name, :scope => :user_id
|
17
13
|
|
18
|
-
|
14
|
+
serialize :at_connect, Array
|
19
15
|
|
20
16
|
def at_connect
|
21
|
-
|
17
|
+
read_attribute(:at_connect) || []
|
22
18
|
end
|
23
19
|
|
24
20
|
def public?
|
@@ -1,33 +1,17 @@
|
|
1
1
|
module Tkellem
|
2
2
|
|
3
|
-
class NetworkUser <
|
4
|
-
|
3
|
+
class NetworkUser < ActiveRecord::Base
|
4
|
+
belongs_to :network
|
5
|
+
belongs_to :user
|
5
6
|
|
6
|
-
|
7
|
-
many_to_one :user
|
8
|
-
|
9
|
-
serialize_attributes :yaml, :at_connect
|
10
|
-
|
11
|
-
def at_connect
|
12
|
-
super || []
|
13
|
-
end
|
7
|
+
serialize :at_connect, Array
|
14
8
|
|
15
9
|
def nick
|
16
|
-
|
10
|
+
read_attribute(:nick) || user.name
|
17
11
|
end
|
18
12
|
|
19
13
|
def combined_at_connect
|
20
|
-
network.at_connect + at_connect
|
21
|
-
end
|
22
|
-
|
23
|
-
def after_create
|
24
|
-
super
|
25
|
-
$tkellem_server.try(:after_create, self)
|
26
|
-
end
|
27
|
-
|
28
|
-
def after_destroy
|
29
|
-
super
|
30
|
-
$tkellem_server.try(:after_destroy, self)
|
14
|
+
network.at_connect + (at_connect || [])
|
31
15
|
end
|
32
16
|
end
|
33
17
|
|