tkellem 0.8.11 → 0.9.0.beta1
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 +9 -0
- data/lib/tkellem/bouncer.rb +50 -15
- data/lib/tkellem/bouncer_connection.rb +46 -60
- data/lib/tkellem/celluloid_tools.rb +131 -0
- data/lib/tkellem/daemon.rb +3 -7
- data/lib/tkellem/irc_message.rb +1 -1
- data/lib/tkellem/migrations/001_init_db.rb +39 -22
- data/lib/tkellem/migrations/002_at_connect_columns.rb +21 -4
- data/lib/tkellem/migrations/003_settings.rb +18 -9
- data/lib/tkellem/models/host.rb +2 -2
- data/lib/tkellem/models/listen_address.rb +13 -1
- data/lib/tkellem/models/network.rb +11 -7
- data/lib/tkellem/models/network_user.rb +22 -6
- data/lib/tkellem/models/setting.rb +3 -3
- data/lib/tkellem/models/user.rb +13 -6
- data/lib/tkellem/plugins/backlog.rb +45 -27
- data/lib/tkellem/socket_server.rb +15 -7
- data/lib/tkellem/tkellem_bot.rb +11 -19
- data/lib/tkellem/tkellem_server.rb +84 -61
- data/lib/tkellem/version.rb +1 -1
- data/lib/tkellem.rb +1 -10
- data/spec/spec_helper.rb +5 -18
- data/tkellem.gemspec +6 -4
- metadata +56 -28
- data/lib/tkellem/irc_server.rb +0 -124
- data/spec/bouncer_connection_spec.rb +0 -37
- data/spec/irc_server_spec.rb +0 -145
data/Gemfile
CHANGED
@@ -1,4 +1,13 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
|
+
$:.push File.expand_path("../lib", __FILE__)
|
4
|
+
|
5
|
+
gem 'celluloid', :git => 'git://github.com/celluloid/celluloid'
|
6
|
+
gem 'celluloid-io', :git => 'https://github.com/celluloid/celluloid-io.git'
|
7
|
+
gem 'activesupport', '3.2.10'
|
8
|
+
gem 'sequel', '3.42.0'
|
9
|
+
gem 'sqlite3', '1.3.6'
|
10
|
+
gem 'rspec', '2.5'
|
11
|
+
|
3
12
|
# Specify your gem's dependencies in tkellem.gemspec
|
4
13
|
gemspec
|
data/lib/tkellem/bouncer.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
require 'active_support/core_ext/class/attribute_accessors'
|
2
|
+
require 'celluloid/io'
|
2
3
|
|
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
|
9
10
|
include Tkellem::EasyLogger
|
11
|
+
include Tkellem::CelluloidTools::LineReader
|
10
12
|
|
11
13
|
attr_reader :user, :network, :nick, :network_user, :connected_at
|
12
14
|
cattr_accessor :plugins
|
13
15
|
self.plugins = []
|
14
16
|
|
17
|
+
class Room < Struct.new(:name, :topic, :topic_setter, :topic_time); end
|
18
|
+
|
15
19
|
def initialize(network_user)
|
16
20
|
@network_user = network_user
|
17
21
|
@user = network_user.user
|
@@ -21,7 +25,7 @@ class Bouncer
|
|
21
25
|
# maps { client_conn => state_hash }
|
22
26
|
@active_conns = {}
|
23
27
|
@welcomes = []
|
24
|
-
@rooms =
|
28
|
+
@rooms = {}
|
25
29
|
# maps { client_conn => away_status_or_nil }
|
26
30
|
@away = {}
|
27
31
|
# plugin data
|
@@ -29,7 +33,7 @@ class Bouncer
|
|
29
33
|
# clients waiting for us to connect to the irc server
|
30
34
|
@waiting_clients = []
|
31
35
|
|
32
|
-
connect
|
36
|
+
async.connect
|
33
37
|
end
|
34
38
|
|
35
39
|
def data(key)
|
@@ -62,7 +66,7 @@ class Bouncer
|
|
62
66
|
client.send_msg(":#{client.connecting_nick} NICK #{nick}") if client.connecting_nick != nick
|
63
67
|
send_welcome(client)
|
64
68
|
# make the client join all the rooms that we're in
|
65
|
-
@rooms.
|
69
|
+
@rooms.each_value { |room| client.simulate_join(room) }
|
66
70
|
|
67
71
|
plugins.each { |plugin| plugin.new_client_connected(self, client) }
|
68
72
|
check_away_status
|
@@ -100,6 +104,13 @@ class Bouncer
|
|
100
104
|
end
|
101
105
|
end
|
102
106
|
|
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
|
+
|
103
114
|
def server_msg(msg)
|
104
115
|
return if plugins.any? do |plugin|
|
105
116
|
!plugin.server_msg(self, msg)
|
@@ -112,12 +123,25 @@ class Bouncer
|
|
112
123
|
false
|
113
124
|
when 'JOIN'
|
114
125
|
debug "#{msg.target_user} joined #{msg.args.first}"
|
115
|
-
@rooms
|
126
|
+
@rooms[msg.args.first] = Room.new(msg.args.first) if msg.target_user == @nick
|
116
127
|
true
|
117
128
|
when 'PART'
|
118
129
|
debug "#{msg.target_user} left #{msg.args.first}"
|
119
130
|
@rooms.delete(msg.args.first) if msg.target_user == @nick
|
120
131
|
true
|
132
|
+
when 'TOPIC'
|
133
|
+
if room = @rooms[msg.args.first]
|
134
|
+
room.topic = msg.args.last
|
135
|
+
end
|
136
|
+
when '332' # topic replay
|
137
|
+
if room = @rooms[msg.args[1]]
|
138
|
+
room.topic = msg.args.last
|
139
|
+
end
|
140
|
+
when '333' # topic timestamp
|
141
|
+
if room = @rooms[msg.args[1]]
|
142
|
+
room.topic_setter = msg.args[2]
|
143
|
+
room.topic_time = msg.args[3]
|
144
|
+
end
|
121
145
|
when 'PING'
|
122
146
|
send_msg("PONG tkellem!tkellem :#{msg.args.last}")
|
123
147
|
false
|
@@ -165,22 +189,21 @@ class Bouncer
|
|
165
189
|
alias_method :log_name, :name
|
166
190
|
|
167
191
|
def send_msg(msg)
|
168
|
-
return unless @conn
|
169
192
|
trace "to server: #{msg}"
|
170
|
-
@
|
193
|
+
@socket.write("#{msg}\r\n")
|
171
194
|
end
|
172
195
|
|
173
|
-
def connection_established
|
174
|
-
|
196
|
+
def connection_established
|
197
|
+
debug "Connected"
|
175
198
|
# TODO: support sending a real username, realname, etc
|
176
199
|
send_msg("USER #{@user.username} somehost tkellem :#{@user.name}@tkellem")
|
177
200
|
change_nick(@nick, true)
|
178
201
|
@connected_at = Time.now
|
202
|
+
async.run
|
179
203
|
end
|
180
204
|
|
181
205
|
def disconnected!
|
182
206
|
debug "OMG we got disconnected."
|
183
|
-
@conn = nil
|
184
207
|
@connected = false
|
185
208
|
@connected_at = nil
|
186
209
|
@active_conns.each { |c,s| c.close_connection }
|
@@ -199,14 +222,26 @@ class Bouncer
|
|
199
222
|
@welcomes.each { |msg| msg.args[0] = nick; bouncer_conn.send_msg(msg) }
|
200
223
|
end
|
201
224
|
|
202
|
-
def connect
|
203
|
-
|
204
|
-
|
225
|
+
def connect
|
226
|
+
hosts = network.reload.hosts
|
227
|
+
# random rather than round-robin, not totally ideal
|
228
|
+
# Note: Celluloid::TCPSocket also is random rather than round-robin when
|
229
|
+
# picking the DNS IP record to connect to
|
230
|
+
host = hosts[rand(hosts.length)]
|
231
|
+
begin
|
232
|
+
info "Connecting to #{host}"
|
233
|
+
@socket = TCPSocket.new(host.address, host.port)
|
234
|
+
if host.ssl
|
235
|
+
@socket = SSLSocket.new(@socket)
|
236
|
+
@socket.connect
|
237
|
+
end
|
238
|
+
end
|
239
|
+
connection_established
|
205
240
|
end
|
206
241
|
|
207
242
|
def ready!
|
208
|
-
@rooms.
|
209
|
-
send_msg("JOIN #{room}")
|
243
|
+
@rooms.each_key do |room|
|
244
|
+
send_msg("JOIN #{room.name}")
|
210
245
|
end
|
211
246
|
|
212
247
|
check_away_status
|
@@ -1,19 +1,19 @@
|
|
1
1
|
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'celluloid/io'
|
2
3
|
|
3
|
-
require 'eventmachine'
|
4
4
|
require 'tkellem/irc_message'
|
5
|
+
require 'tkellem/celluloid_tools'
|
5
6
|
|
6
7
|
module Tkellem
|
7
8
|
|
8
|
-
|
9
|
-
include
|
9
|
+
class BouncerConnection
|
10
|
+
include Celluloid::IO
|
10
11
|
include Tkellem::EasyLogger
|
12
|
+
include Tkellem::CelluloidTools::LineReader
|
11
13
|
|
12
|
-
def initialize(tkellem_server,
|
13
|
-
set_delimiter "\r\n"
|
14
|
-
|
15
|
-
@ssl = do_ssl
|
14
|
+
def initialize(tkellem_server, socket)
|
16
15
|
@tkellem = tkellem_server
|
16
|
+
@socket = socket
|
17
17
|
|
18
18
|
@state = :auth
|
19
19
|
@name = 'new-conn'
|
@@ -31,24 +31,11 @@ module 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
|
-
|
47
34
|
def error!(msg)
|
48
35
|
info("ERROR :#{msg}")
|
49
36
|
say_as_tkellem(msg)
|
50
37
|
send_msg("ERROR :#{msg}")
|
51
|
-
close_connection
|
38
|
+
close_connection
|
52
39
|
end
|
53
40
|
|
54
41
|
def connect_to_irc_server
|
@@ -90,40 +77,38 @@ module BouncerConnection
|
|
90
77
|
end
|
91
78
|
|
92
79
|
def receive_line(line)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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.")
|
80
|
+
trace "from client: #{line}"
|
81
|
+
return if line.blank?
|
82
|
+
msg = IrcMessage.parse(line)
|
83
|
+
|
84
|
+
command = msg.command
|
85
|
+
if @state != :auth && command == 'PRIVMSG' && msg.args.first == '-tkellem'
|
86
|
+
msg_tkellem(IrcMessage.new(nil, 'TKELLEM', [msg.args.last]))
|
87
|
+
elsif command == 'TKELLEM' || command == 'TK'
|
88
|
+
msg_tkellem(msg)
|
89
|
+
elsif command == 'CAP'
|
90
|
+
# TODO: full support for CAP -- this just gets mobile colloquy connecting
|
91
|
+
if msg.args.first =~ /req/i
|
92
|
+
send_msg("CAP NAK")
|
126
93
|
end
|
94
|
+
elsif command == 'PASS' && @state == :auth
|
95
|
+
@password = msg.args.first
|
96
|
+
elsif command == 'NICK' && @state == :auth
|
97
|
+
@connecting_nick = msg.args.first
|
98
|
+
maybe_connect
|
99
|
+
elsif command == 'QUIT'
|
100
|
+
close_connection
|
101
|
+
elsif command == 'USER' && @state == :auth
|
102
|
+
unless @username
|
103
|
+
@username, @conn_info = msg.args.first.strip.split('@', 2).map { |a| a.downcase }
|
104
|
+
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.")
|
127
112
|
end
|
128
113
|
end
|
129
114
|
|
@@ -188,21 +173,22 @@ module BouncerConnection
|
|
188
173
|
end
|
189
174
|
|
190
175
|
def simulate_join(room)
|
191
|
-
send_msg(":#{nick} JOIN #{room}")
|
176
|
+
send_msg(":#{nick} JOIN #{room.name}")
|
192
177
|
# TODO: intercept the NAMES response so that only this bouncer gets it
|
193
178
|
# Otherwise other clients might show an "in this room" line.
|
194
|
-
@bouncer.send_msg("NAMES #{room}\r\n")
|
179
|
+
@bouncer.send_msg("NAMES #{room.name}\r\n")
|
180
|
+
send_msg(IrcMessage.new(":tkellem", "332", [nick, room.name, room.topic])) if room.topic
|
181
|
+
send_msg(IrcMessage.new(":tkellem", "333", [nick, room.name, room.topic_setter, room.topic_time])) if room.topic_setter && room.topic_time
|
195
182
|
end
|
196
183
|
|
197
184
|
def send_msg(msg)
|
185
|
+
return if @socket.closed?
|
198
186
|
trace "to client: #{msg}"
|
199
|
-
|
187
|
+
@socket.write("#{msg}\r\n")
|
200
188
|
end
|
201
189
|
|
202
190
|
def unbind
|
203
|
-
|
204
|
-
@bouncer.disconnect_client(self) if @bouncer
|
205
|
-
end
|
191
|
+
@bouncer.try(:disconnect_client, self)
|
206
192
|
end
|
207
193
|
end
|
208
194
|
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'celluloid/io'
|
3
|
+
|
4
|
+
module Tkellem
|
5
|
+
module CelluloidTools
|
6
|
+
|
7
|
+
# Generates a new SSL context with a new cert and key
|
8
|
+
# Great for easily getting up and running, but not necessarily a good idea for
|
9
|
+
# production use
|
10
|
+
def self.generate_ssl_ctx
|
11
|
+
key = OpenSSL::PKey::RSA.new(2048)
|
12
|
+
|
13
|
+
dn = OpenSSL::X509::Name.parse("/CN=tkellem-auto")
|
14
|
+
cert = OpenSSL::X509::Certificate.new
|
15
|
+
cert.version = 2
|
16
|
+
cert.serial = 1
|
17
|
+
cert.subject = dn
|
18
|
+
cert.issuer = dn
|
19
|
+
cert.public_key = key.public_key
|
20
|
+
cert.not_before = Time.now
|
21
|
+
cert.not_after = Time.now + 94670777 # 3 years
|
22
|
+
cert.sign(key, OpenSSL::Digest::SHA1.new)
|
23
|
+
|
24
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
25
|
+
ctx.key = key
|
26
|
+
ctx.cert = cert
|
27
|
+
|
28
|
+
ctx
|
29
|
+
end
|
30
|
+
|
31
|
+
class BackoffSupervisor < ::Celluloid::SupervisionGroup
|
32
|
+
attr_reader :registry
|
33
|
+
|
34
|
+
def restart_actor(actor, reason)
|
35
|
+
member = @members.find do |member|
|
36
|
+
member.actor == actor
|
37
|
+
end
|
38
|
+
raise "a group member went missing. This shouldn't be!" unless member
|
39
|
+
|
40
|
+
if reason
|
41
|
+
end
|
42
|
+
|
43
|
+
member.restart(reason)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Listener < Struct.new(:server, :callback)
|
48
|
+
include Celluloid::IO
|
49
|
+
|
50
|
+
def self.start(*args, &callback)
|
51
|
+
listener = self.new(*args)
|
52
|
+
listener.callback = callback
|
53
|
+
listener.async.run
|
54
|
+
listener
|
55
|
+
end
|
56
|
+
|
57
|
+
def run
|
58
|
+
loop { handle_connection(server.accept) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def handle_connection(socket)
|
62
|
+
callback.(socket)
|
63
|
+
end
|
64
|
+
|
65
|
+
finalizer :close
|
66
|
+
|
67
|
+
def close
|
68
|
+
server.try(:close) unless server.try(:closed?)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class TCPListener < Listener
|
73
|
+
def initialize(host, port)
|
74
|
+
self.server = TCPServer.new(host, port)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class SSLListener < Listener
|
79
|
+
def initialize(host, port)
|
80
|
+
self.server = SSLServer.new(TCPServer.new(host, port), CelluloidTools.generate_ssl_ctx)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class UnixListener < Listener
|
85
|
+
def initialize(socket_path)
|
86
|
+
self.server = UNIXServer.new(socket_path)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module LineReader
|
91
|
+
def self.included(k)
|
92
|
+
k.send :finalizer, :close_connection
|
93
|
+
end
|
94
|
+
|
95
|
+
def readline
|
96
|
+
@delimiter ||= "\r\n"
|
97
|
+
@readline_buffer ||= ''
|
98
|
+
loop do
|
99
|
+
if idx = @readline_buffer.index(@delimiter)
|
100
|
+
postidx = idx + @delimiter.size
|
101
|
+
line = @readline_buffer[0, postidx]
|
102
|
+
@readline_buffer = @readline_buffer[postidx..-1]
|
103
|
+
return line
|
104
|
+
else
|
105
|
+
@socket.readpartial(4096, @readline_buffer)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def close_connection
|
111
|
+
@socket.close if @socket && !@socket.closed?
|
112
|
+
end
|
113
|
+
|
114
|
+
def run
|
115
|
+
loop do
|
116
|
+
line = readline
|
117
|
+
receive_line(line)
|
118
|
+
end
|
119
|
+
rescue EOFError, IOError, OpenSSL::SSL::SSLError
|
120
|
+
unbind
|
121
|
+
end
|
122
|
+
|
123
|
+
def receive_line(line)
|
124
|
+
end
|
125
|
+
|
126
|
+
def unbind
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
data/lib/tkellem/daemon.rb
CHANGED
@@ -2,7 +2,6 @@ require 'fileutils'
|
|
2
2
|
require 'optparse'
|
3
3
|
|
4
4
|
require 'tkellem'
|
5
|
-
require 'tkellem/socket_server'
|
6
5
|
|
7
6
|
class Tkellem::Daemon
|
8
7
|
attr_reader :options
|
@@ -86,11 +85,8 @@ class Tkellem::Daemon
|
|
86
85
|
end
|
87
86
|
|
88
87
|
def start
|
89
|
-
|
90
|
-
|
91
|
-
@admin = EM.start_unix_domain_server(socket_file, Tkellem::SocketServer)
|
92
|
-
Tkellem::TkellemServer.new
|
93
|
-
end
|
88
|
+
remove_files
|
89
|
+
Tkellem::TkellemServer.new(options).run
|
94
90
|
ensure
|
95
91
|
remove_files
|
96
92
|
end
|
@@ -138,7 +134,7 @@ class Tkellem::Daemon
|
|
138
134
|
end
|
139
135
|
|
140
136
|
def remove_files
|
141
|
-
FileUtils.rm(socket_file)
|
137
|
+
FileUtils.rm(socket_file) if File.exist?(socket_file)
|
142
138
|
return unless @daemon
|
143
139
|
pid = File.read(pid_file) if File.file?(pid_file)
|
144
140
|
if pid.to_i == Process.pid
|
data/lib/tkellem/irc_message.rb
CHANGED
@@ -20,7 +20,7 @@ class IrcMessage < Struct.new(:prefix, :command, :args, :ctcp)
|
|
20
20
|
|
21
21
|
msg = self.new(prefix, command, args)
|
22
22
|
|
23
|
-
if args.last.
|
23
|
+
if args.last && args.last.match(%r{#{"\x01"}([^ ]+)([^\1]*)#{"\x01"}})
|
24
24
|
msg.ctcp = $1.upcase
|
25
25
|
msg.args[-1] = $2.strip
|
26
26
|
end
|
@@ -1,33 +1,50 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
t.integer 'port', :null => false
|
6
|
-
t.boolean 'ssl', :null => false, :default => false
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
if self.table_exists?(:schema_migrations) && self[:schema_migrations].first(:version => '1')
|
4
|
+
next
|
7
5
|
end
|
8
6
|
|
9
|
-
create_table '
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
create_table 'listen_addresses' do
|
8
|
+
primary_key :id
|
9
|
+
String 'address', :null => false
|
10
|
+
Integer 'port', :null => false
|
11
|
+
boolean 'ssl', :null => false, :default => false
|
13
12
|
end
|
14
13
|
|
15
|
-
create_table '
|
16
|
-
|
17
|
-
|
14
|
+
create_table 'users' do
|
15
|
+
primary_key :id
|
16
|
+
String 'username', :null => false
|
17
|
+
String 'password'
|
18
|
+
String 'role', :null => false, :default => 'user'
|
18
19
|
end
|
19
20
|
|
20
|
-
create_table '
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
t.boolean 'ssl', :null => false, :default => false
|
21
|
+
create_table 'networks' do
|
22
|
+
primary_key :id
|
23
|
+
foreign_key :user_id, :users
|
24
|
+
String 'name', :null => false
|
25
25
|
end
|
26
26
|
|
27
|
-
create_table '
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
create_table 'hosts' do
|
28
|
+
primary_key :id
|
29
|
+
foreign_key :network_id, :networks
|
30
|
+
String 'address', :null => false
|
31
|
+
Integer 'port', :null => false
|
32
|
+
boolean 'ssl', :null => false, :default => false
|
31
33
|
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'
|
32
49
|
end
|
33
50
|
end
|
@@ -1,7 +1,24 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
if self.table_exists?(:schema_migrations) && self[:schema_migrations].first(:version => '2')
|
4
|
+
next
|
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
|
5
22
|
end
|
6
23
|
end
|
7
24
|
|
@@ -1,13 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
t.string :value, :null => false
|
6
|
-
t.boolean :unchanged, :null => false, :default => true
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
if self.table_exists?(:schema_migrations) && self[:schema_migrations].first(:version => '3')
|
4
|
+
next
|
7
5
|
end
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
create_table 'settings' do
|
8
|
+
primary_key :id
|
9
|
+
String :name, :null => false
|
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'
|
12
21
|
end
|
13
22
|
end
|
data/lib/tkellem/models/host.rb
CHANGED
@@ -1,12 +1,24 @@
|
|
1
1
|
module Tkellem
|
2
2
|
|
3
|
-
class ListenAddress <
|
3
|
+
class ListenAddress < Sequel::Model
|
4
|
+
plugin :validation_class_methods
|
5
|
+
|
4
6
|
validates_uniqueness_of :port, :scope => [:address]
|
5
7
|
validates_presence_of :address, :port
|
6
8
|
|
7
9
|
def to_s
|
8
10
|
"#{ssl ? 'ircs' : 'irc'}://#{address}:#{port}"
|
9
11
|
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
|
10
22
|
end
|
11
23
|
|
12
24
|
end
|