tkellem 0.9.0.beta3 → 0.9.0.beta4
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/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
|
|