torchat 0.0.1.rc.1
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/README.md +43 -0
- data/bin/torchatd +537 -0
- data/doc/protocol/broadcast.md +26 -0
- data/doc/protocol/groupchat.md +140 -0
- data/doc/protocol/latency.md +30 -0
- data/doc/protocol/standard.md +279 -0
- data/doc/protocol/typing.md +41 -0
- data/etc/torchat.yml +12 -0
- data/lib/torchat.rb +239 -0
- data/lib/torchat/protocol.rb +256 -0
- data/lib/torchat/protocol/broadcast.rb +40 -0
- data/lib/torchat/protocol/groupchat.rb +197 -0
- data/lib/torchat/protocol/latency.rb +44 -0
- data/lib/torchat/protocol/standard.rb +367 -0
- data/lib/torchat/protocol/typing.rb +36 -0
- data/lib/torchat/session.rb +603 -0
- data/lib/torchat/session/broadcast/message.rb +50 -0
- data/lib/torchat/session/broadcasts.rb +72 -0
- data/lib/torchat/session/buddies.rb +152 -0
- data/lib/torchat/session/buddy.rb +343 -0
- data/lib/torchat/session/buddy/joined_group_chat.rb +42 -0
- data/lib/torchat/session/buddy/joined_group_chats.rb +46 -0
- data/lib/torchat/session/buddy/latency.rb +54 -0
- data/lib/torchat/session/event.rb +79 -0
- data/lib/torchat/session/file_transfer.rb +173 -0
- data/lib/torchat/session/file_transfer/block.rb +51 -0
- data/lib/torchat/session/file_transfers.rb +89 -0
- data/lib/torchat/session/group_chat.rb +123 -0
- data/lib/torchat/session/group_chat/participant.rb +38 -0
- data/lib/torchat/session/group_chat/participants.rb +52 -0
- data/lib/torchat/session/group_chats.rb +74 -0
- data/lib/torchat/session/incoming.rb +187 -0
- data/lib/torchat/session/outgoing.rb +102 -0
- data/lib/torchat/tor.rb +107 -0
- data/lib/torchat/utils.rb +87 -0
- data/lib/torchat/version.rb +24 -0
- data/test/file_transfer/receiver.rb +41 -0
- data/test/file_transfer/sender.rb +45 -0
- data/test/group_chat/a.rb +37 -0
- data/test/group_chat/b.rb +37 -0
- data/test/group_chat/c.rb +57 -0
- data/torchat.gemspec +21 -0
- metadata +140 -0
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
torchat - the Ruby implementation
|
2
|
+
=================================
|
3
|
+
You can find the official implementation [here](https://github.com/prof7bit/TorChat).
|
4
|
+
|
5
|
+
This aims to be a Ruby implementation and daemon to deal with a TorChat session even from
|
6
|
+
outside or with other languages without having to implement the whole thing.
|
7
|
+
|
8
|
+
The main target is supporting TorChat in bitlbee.
|
9
|
+
|
10
|
+
Setup the daemon
|
11
|
+
----------------
|
12
|
+
The daemon to work needs a tor configured with a hidden service, you can leave it automatic by
|
13
|
+
passing `-t` as an option or you can configure it yourself.
|
14
|
+
|
15
|
+
If you have an already created default TorChat session you just have to run `torchatd -t`, it
|
16
|
+
will do all is needed, and you'll be able to connect to it on port `11110`. If you use bitlbee
|
17
|
+
you just have to add the account, you can also pass a password with `-P password` which will have
|
18
|
+
to be the password you added the account with.
|
19
|
+
|
20
|
+
The daemon has a helper function to generate the needed tor configuration file based either
|
21
|
+
on a YAML file or a TorChat configuration file.
|
22
|
+
|
23
|
+
Example of tor configuration generation:
|
24
|
+
|
25
|
+
```
|
26
|
+
$ torchatd -g -c etc/torchat.yml
|
27
|
+
SocksPort 11108
|
28
|
+
|
29
|
+
HiddenServiceDir hidden_service
|
30
|
+
HiddenServicePort 11009 127.0.0.1:11008
|
31
|
+
|
32
|
+
DataDirectory tor_data
|
33
|
+
|
34
|
+
AvoidDiskWrites 1
|
35
|
+
LongLivedPorts 11009
|
36
|
+
FetchDirInfoEarly 1
|
37
|
+
CircuitBuildTimeout 30
|
38
|
+
NumEntryGuards 6
|
39
|
+
```
|
40
|
+
|
41
|
+
Contacting the author
|
42
|
+
---------------------
|
43
|
+
If you want to contact me on torchat my id is `7acbk6jpofsanyfp`.
|
data/bin/torchatd
ADDED
@@ -0,0 +1,537 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'torchat'
|
4
|
+
|
5
|
+
options = {}
|
6
|
+
|
7
|
+
OptionParser.new do |o|
|
8
|
+
options[:host] = '127.0.0.1'
|
9
|
+
options[:port] = 11110
|
10
|
+
options[:file_transfer_ports] = []
|
11
|
+
|
12
|
+
o.on '-p', '--profile NAME', 'the profile name' do |name|
|
13
|
+
options[:profile] = name
|
14
|
+
end
|
15
|
+
|
16
|
+
o.on '-c', '--config PATH', 'the path to the config file' do |path|
|
17
|
+
options[:config] = path
|
18
|
+
end
|
19
|
+
|
20
|
+
o.on '-g', '--generate [PATH]', 'the path to save the generated torrc' do |path|
|
21
|
+
options[:generate] = path || '-'
|
22
|
+
end
|
23
|
+
|
24
|
+
o.on '-t', '--tor', 'enable automatic generation and run of Tor' do
|
25
|
+
options[:tor] = true
|
26
|
+
end
|
27
|
+
|
28
|
+
o.on '-l', '--listen HOST:PORT', 'the host and port to listen on' do |value|
|
29
|
+
host, port = value.split(':')
|
30
|
+
|
31
|
+
options[:host] = host unless host.empty?
|
32
|
+
options[:port] = port.to_i unless port.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
o.on '-s', '--ssl KEY:CERT', 'the private key and cert files' do |path|
|
36
|
+
options[:ssl] = { key: path.split(':').first, cert: path.split(':').last }
|
37
|
+
end
|
38
|
+
|
39
|
+
o.on '-P', '--password PASSWORD' do |password|
|
40
|
+
options[:password] = password
|
41
|
+
end
|
42
|
+
|
43
|
+
o.on '-f', '--file-transfer-ports PORT...', Array, 'the list of ports to listen on for file transfers' do |ports|
|
44
|
+
options[:file_transfer_ports] = ports.map {|value|
|
45
|
+
value.include?('-') ? Range.new(*value.split('-').map(&:to_i)) : value.to_i
|
46
|
+
}.flatten.compact.uniq
|
47
|
+
end
|
48
|
+
|
49
|
+
o.on '-o', '--online' do
|
50
|
+
options[:online] = true
|
51
|
+
end
|
52
|
+
|
53
|
+
o.on '-d', '--debug [LEVEL=1]', 'enable debug mode' do |value|
|
54
|
+
ENV['DEBUG'] = value || ?1
|
55
|
+
end
|
56
|
+
end.parse!
|
57
|
+
|
58
|
+
class Torchatd
|
59
|
+
class Connection < EventMachine::Protocols::LineAndTextProtocol
|
60
|
+
attr_accessor :daemon, :host, :port, :ssl
|
61
|
+
|
62
|
+
def authorized?; @authorized; end
|
63
|
+
def authorize!; @authorized = true; end
|
64
|
+
|
65
|
+
def receive_line (line)
|
66
|
+
return if line.lstrip.empty?
|
67
|
+
|
68
|
+
@daemon.process self, line.lstrip
|
69
|
+
end
|
70
|
+
|
71
|
+
def send_response (text)
|
72
|
+
send_data "#{text.to_s.lstrip}\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
def unbind
|
76
|
+
@daemon.connections.delete self
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_reader :password, :file_transfer_ports, :connections
|
81
|
+
attr_accessor :profile, :tor
|
82
|
+
|
83
|
+
def initialize (password = nil, file_transfer_ports = [])
|
84
|
+
@password = password
|
85
|
+
@file_transfer_ports = file_transfer_ports.flatten.compact.uniq
|
86
|
+
|
87
|
+
@buddies = []
|
88
|
+
@connections = []
|
89
|
+
@pings = Hash.new { |h, k| h[k] = {} }
|
90
|
+
|
91
|
+
yield self if block_given?
|
92
|
+
end
|
93
|
+
|
94
|
+
def start (host, port, ssl = nil)
|
95
|
+
return if @started
|
96
|
+
|
97
|
+
@started = true
|
98
|
+
|
99
|
+
@signature = EM.start_server host, port, Connection do |conn|
|
100
|
+
@connections << conn
|
101
|
+
|
102
|
+
conn.daemon = self
|
103
|
+
conn.host = host
|
104
|
+
conn.port = port
|
105
|
+
conn.ssl = ssl
|
106
|
+
|
107
|
+
unless @password
|
108
|
+
conn.authorize!
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def stop
|
114
|
+
EM.stop_server @signature
|
115
|
+
|
116
|
+
profile.stop
|
117
|
+
tor.stop if tor
|
118
|
+
end
|
119
|
+
|
120
|
+
def process (connection, line)
|
121
|
+
command, rest = line.force_encoding('UTF-8').split(' ', 2)
|
122
|
+
|
123
|
+
case command.downcase.to_sym
|
124
|
+
when :starttls
|
125
|
+
if connection.ssl
|
126
|
+
connection.start_tls(private_key_file: connection.ssl[:key], cert_chain_file: connection.ssl[:cert])
|
127
|
+
else
|
128
|
+
connection.start_tls
|
129
|
+
end
|
130
|
+
|
131
|
+
return
|
132
|
+
|
133
|
+
when :pass
|
134
|
+
if !@password || @password == rest
|
135
|
+
connection.authorize!
|
136
|
+
connection.send_response "AUTHORIZED #{profile.session.id}"
|
137
|
+
end
|
138
|
+
|
139
|
+
return
|
140
|
+
end
|
141
|
+
|
142
|
+
unless connection.authorized?
|
143
|
+
connection.send_response "UNAUTHORIZED #{command}"
|
144
|
+
return
|
145
|
+
end
|
146
|
+
|
147
|
+
case command.downcase.to_sym
|
148
|
+
when :whoami
|
149
|
+
connection.send_response "WHOAMI #{profile.id}"
|
150
|
+
|
151
|
+
when :list
|
152
|
+
connection.send_response "LIST #{profile.buddies.keys.join(' ')}"
|
153
|
+
|
154
|
+
when :remove
|
155
|
+
profile.buddies.remove rest
|
156
|
+
|
157
|
+
when :add
|
158
|
+
attribute, rest = rest.split(' ')
|
159
|
+
|
160
|
+
if rest && attribute == 'tmp'
|
161
|
+
profile.buddies.add_temporary rest
|
162
|
+
else
|
163
|
+
profile.buddies.add attribute
|
164
|
+
end
|
165
|
+
|
166
|
+
when :typing
|
167
|
+
id, mode = rest.split(' ')
|
168
|
+
|
169
|
+
if buddy = profile.buddies[id]
|
170
|
+
buddy.send_typing(mode)
|
171
|
+
end
|
172
|
+
|
173
|
+
when :status
|
174
|
+
if rest && Torchat.normalize_id(rest, true)
|
175
|
+
if buddy = profile.buddies[rest]
|
176
|
+
connection.send_response "#{rest} STATUS #{buddy.status}"
|
177
|
+
end
|
178
|
+
else
|
179
|
+
profile.status = rest
|
180
|
+
end
|
181
|
+
|
182
|
+
when :client
|
183
|
+
if buddy = profile.buddies[rest]
|
184
|
+
if buddy.client.name
|
185
|
+
connection.send_response "#{rest} CLIENT_NAME #{buddy.client.name}"
|
186
|
+
end
|
187
|
+
|
188
|
+
if buddy.client.version
|
189
|
+
connection.send_response "#{rest} CLIENT_VERSION #{buddy.client.version}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
when :name
|
194
|
+
if rest && Torchat.normalize_id(rest, true)
|
195
|
+
if buddy = profile.buddies[rest]
|
196
|
+
connection.send_response "#{rest} NAME #{buddy.name}"
|
197
|
+
end
|
198
|
+
else
|
199
|
+
profile.name = rest
|
200
|
+
end
|
201
|
+
|
202
|
+
when :description
|
203
|
+
if rest && Torchat.normalize_id(rest, true)
|
204
|
+
if buddy = profile.buddies[rest]
|
205
|
+
connection.send_response "#{rest} DESCRIPTION #{buddy.description}"
|
206
|
+
end
|
207
|
+
else
|
208
|
+
profile.description = rest
|
209
|
+
end
|
210
|
+
|
211
|
+
when :message
|
212
|
+
profile.send_message_to *rest.split(' ', 2)
|
213
|
+
|
214
|
+
when :block
|
215
|
+
profile.buddies[rest].block!
|
216
|
+
|
217
|
+
when :allow
|
218
|
+
profile.buddies[rest].allow!
|
219
|
+
|
220
|
+
when :broadcast
|
221
|
+
profile.send_broadcast rest
|
222
|
+
|
223
|
+
when :groupchats
|
224
|
+
connection.send_response "GROUPCHATS #{profile.group_chats.keys.join(' ')}"
|
225
|
+
|
226
|
+
when :groupchat_participants
|
227
|
+
if group_chat = profile.group_chats[rest]
|
228
|
+
connection.send_response "GROUPCHAT_PARTICIPANTS #{group_chat.id} #{group_chat.participants.keys.join ' '}"
|
229
|
+
end
|
230
|
+
|
231
|
+
when :groupchat_invite
|
232
|
+
group_chat, buddy = rest.split ' '
|
233
|
+
|
234
|
+
if buddy
|
235
|
+
if (buddy = profile.buddies[buddy]) && (group_chat = profile.group_chats[group_chat])
|
236
|
+
group_chat.invite(buddy)
|
237
|
+
end
|
238
|
+
else
|
239
|
+
if buddy = profile.buddies[group_chat]
|
240
|
+
profile.group_chats.create.invite(buddy)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
when :groupchat_leave
|
245
|
+
group_chat, reason = rest.split ' ', 2
|
246
|
+
|
247
|
+
if group_chat = profile.group_chats[group_chat]
|
248
|
+
group_chat.leave reason
|
249
|
+
end
|
250
|
+
|
251
|
+
when :groupchat_message
|
252
|
+
group_chat, message = rest.split ' ', 2
|
253
|
+
|
254
|
+
if group_chat = profile.group_chats[group_chat]
|
255
|
+
group_chat.send_message message
|
256
|
+
end
|
257
|
+
|
258
|
+
when :latency
|
259
|
+
id, rest = rest.split ' ', 2
|
260
|
+
|
261
|
+
if (buddy = profile.buddies[id]) && buddy.supports?(:latency)
|
262
|
+
@pings[id][buddy.latency.ping!.id] = [Time.now, rest]
|
263
|
+
end
|
264
|
+
|
265
|
+
else
|
266
|
+
connection.send_response "UNIMPLEMENTED #{command}"
|
267
|
+
end
|
268
|
+
rescue => e
|
269
|
+
Torchat.debug e
|
270
|
+
end
|
271
|
+
|
272
|
+
def received_packet (packet)
|
273
|
+
return unless @buddies.include? packet.from
|
274
|
+
|
275
|
+
if packet.type == :message
|
276
|
+
packet.to_s.lines.each {|line|
|
277
|
+
send_everyone "#{packet.from.id} MESSAGE #{line}"
|
278
|
+
}
|
279
|
+
elsif packet.type == :status
|
280
|
+
send_everyone "#{packet.from.id} STATUS #{packet}"
|
281
|
+
elsif packet.type == :client
|
282
|
+
send_everyone "#{packet.from.id} CLIENT_NAME #{packet}"
|
283
|
+
elsif packet.type == :version
|
284
|
+
send_everyone "#{packet.from.id} CLIENT_VERSION #{packet}"
|
285
|
+
elsif packet.type == :profile_name && !packet.nil?
|
286
|
+
send_everyone "#{packet.from.id} NAME #{packet}"
|
287
|
+
elsif packet.type == :profile_text && !packet.nil?
|
288
|
+
send_everyone "#{packet.from.id} DESCRIPTION #{packet}"
|
289
|
+
elsif packet.type == :remove_me
|
290
|
+
send_everyone "#{packet.from.id} REMOVE"
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def file_transfer (what, file_transfer, *args)
|
295
|
+
if what == :start
|
296
|
+
|
297
|
+
elsif what == :stop
|
298
|
+
|
299
|
+
elsif what == :complete
|
300
|
+
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def typing (buddy, mode)
|
305
|
+
send_everyone "#{buddy.id} TYPING #{mode}"
|
306
|
+
end
|
307
|
+
|
308
|
+
def broadcast (message)
|
309
|
+
send_everyone "BROADCAST #{message}"
|
310
|
+
end
|
311
|
+
|
312
|
+
def group_chat (what, group_chat, buddy = nil, *args)
|
313
|
+
if what == :create
|
314
|
+
send_everyone "GROUPCHAT_CREATE #{group_chat.id}"
|
315
|
+
elsif what == :invite
|
316
|
+
send_everyone "#{buddy.id} GROUPCHAT_INVITE #{group_chat.id}"
|
317
|
+
elsif what == :join
|
318
|
+
send_everyone "#{buddy.id} GROUPCHAT_JOIN #{group_chat.id}#{" #{args.first.id}" if args.first}"
|
319
|
+
elsif what == :joined
|
320
|
+
send_everyone "GROUPCHAT_JOINED #{group_chat.id}"
|
321
|
+
send_everyone "GROUPCHAT_PARTICIPANTS #{group_chat.id} #{group_chat.participants.keys.join ' '}"
|
322
|
+
elsif what == :leave
|
323
|
+
send_everyone "#{buddy.id} GROUPCHAT_LEAVE #{group_chat.id}#{" #{args.first}" if args.first}"
|
324
|
+
elsif what == :left
|
325
|
+
send_everyone "GROUPCHAT_LEFT #{group_chat.id} #{args.first}"
|
326
|
+
elsif what == :message
|
327
|
+
send_everyone "#{buddy.id} GROUPCHAT_MESSAGE #{group_chat.id} #{args.first}"
|
328
|
+
elsif what == :destroy
|
329
|
+
send_everyone "GROUPCHAT_DESTROY #{group_chat.id}"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def latency (buddy, amount, id)
|
334
|
+
send_everyone "#{buddy.id} LATENCY #{@pings[buddy.id].delete(id).last}"
|
335
|
+
end
|
336
|
+
|
337
|
+
def cleanup!
|
338
|
+
@pings.each {|id, pings|
|
339
|
+
pings.reject! {|time, payload|
|
340
|
+
(Time.now - time).to_i >= 80
|
341
|
+
}
|
342
|
+
}
|
343
|
+
|
344
|
+
@pings.reject!(&:empty?)
|
345
|
+
end
|
346
|
+
|
347
|
+
def connected?; @connected; end
|
348
|
+
|
349
|
+
def connected (buddy)
|
350
|
+
@buddies << buddy
|
351
|
+
|
352
|
+
send_everyone "#{buddy.id} CONNECTED"
|
353
|
+
|
354
|
+
send_everyone "#{buddy.id} NAME #{buddy.name}" if buddy.name
|
355
|
+
send_everyone "#{buddy.id} DESCRIPTION #{buddy.description}" if buddy.description
|
356
|
+
|
357
|
+
if buddy.client.name
|
358
|
+
send_everyone "#{buddy.id} CLIENT_NAME #{buddy.client.name}"
|
359
|
+
end
|
360
|
+
|
361
|
+
if buddy.client.version
|
362
|
+
send_everyone "#{buddy.id} CLIENT_VERSION #{buddy.client.version}"
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def disconnected (buddy)
|
367
|
+
return unless @buddies.include? buddy
|
368
|
+
|
369
|
+
send_everyone "#{buddy.id} DISCONNECTED"
|
370
|
+
|
371
|
+
@buddies.delete buddy
|
372
|
+
end
|
373
|
+
|
374
|
+
def removed (buddy)
|
375
|
+
return unless @buddies.include? buddy
|
376
|
+
|
377
|
+
send_everyone "#{buddy.id} REMOVE"
|
378
|
+
end
|
379
|
+
|
380
|
+
def send_everyone (text, even_unauthorized = false)
|
381
|
+
@connections.each {|connection|
|
382
|
+
next unless connection.authorized? || even_unauthorized
|
383
|
+
|
384
|
+
connection.send_response text
|
385
|
+
}
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
EM.run {
|
390
|
+
Torchatd.new(options[:password], options[:file_transfer_ports]) {|d|
|
391
|
+
d.profile = options[:config] ? Torchat.new(options[:config]) : Torchat.profile(options[:profile])
|
392
|
+
|
393
|
+
if options[:generate]
|
394
|
+
if options[:generate] == '-'
|
395
|
+
print d.profile.tor.rc
|
396
|
+
else
|
397
|
+
File.open(options[:generate], 'w') { |f| f.print d.profile.tor.rc }
|
398
|
+
end
|
399
|
+
|
400
|
+
exit
|
401
|
+
end
|
402
|
+
|
403
|
+
puts 'torchatd starting...'
|
404
|
+
|
405
|
+
if options[:tor]
|
406
|
+
d.profile.tor.file = 'torrc.txt'
|
407
|
+
|
408
|
+
d.profile.tor.start "#{d.profile.path || '~/.torchat'}/Tor", -> {
|
409
|
+
abort 'could not load the onion id' if 20.times {
|
410
|
+
break if File.exists? 'hidden_service/hostname'
|
411
|
+
|
412
|
+
sleep 1
|
413
|
+
}
|
414
|
+
}, -> {
|
415
|
+
abort 'tor exited with errors'
|
416
|
+
}
|
417
|
+
end
|
418
|
+
|
419
|
+
unless d.profile.config['id']
|
420
|
+
if d.profile.path
|
421
|
+
if File.readable?("#{d.profile.path}/Tor/hidden_service/hostname")
|
422
|
+
d.profile.config['id'] = File.read("#{d.profile.path}/Tor/hidden_service/hostname")[/^(.*?)\.onion/, 1]
|
423
|
+
end
|
424
|
+
end or abort 'could not deduce the onion id'
|
425
|
+
end
|
426
|
+
|
427
|
+
puts "torchatd started for #{d.profile.config['id']} on #{options[:host]}:#{options[:port]}"
|
428
|
+
|
429
|
+
%w[INT KILL].each {|sig|
|
430
|
+
trap sig do
|
431
|
+
puts 'torchatd stopping...'
|
432
|
+
|
433
|
+
d.stop
|
434
|
+
|
435
|
+
EM.stop_event_loop
|
436
|
+
end
|
437
|
+
}
|
438
|
+
|
439
|
+
d.profile.start {|s|
|
440
|
+
s.on :connect_to do |e|
|
441
|
+
Torchat.debug "connecting to #{e.address}:#{e.port}"
|
442
|
+
end
|
443
|
+
|
444
|
+
s.on :connect_failure do |e|
|
445
|
+
Torchat.debug "#{e.buddy.id} failed to connect"
|
446
|
+
end
|
447
|
+
|
448
|
+
s.on :connect do |e|
|
449
|
+
Torchat.debug "#{e.buddy.id} connected"
|
450
|
+
end
|
451
|
+
|
452
|
+
s.on :verify do |e|
|
453
|
+
Torchat.debug "#{e.buddy.id} has been verified"
|
454
|
+
end
|
455
|
+
|
456
|
+
s.on :ready do |e|
|
457
|
+
d.connected e.buddy
|
458
|
+
end
|
459
|
+
|
460
|
+
s.on :remove_buddy do |e|
|
461
|
+
d.removed e.buddy
|
462
|
+
end
|
463
|
+
|
464
|
+
s.on :disconnect do |e|
|
465
|
+
Torchat.debug "#{e.buddy.id} disconnected"
|
466
|
+
|
467
|
+
d.disconnected e.buddy
|
468
|
+
end
|
469
|
+
|
470
|
+
s.on_packet do |e|
|
471
|
+
d.received_packet e.packet unless e.packet.extension
|
472
|
+
end
|
473
|
+
|
474
|
+
s.on :file_transfer_start do |e|
|
475
|
+
d.file_transfer :start, e.file_transfer
|
476
|
+
end
|
477
|
+
|
478
|
+
s.on :file_transfer_stop do |e|
|
479
|
+
d.file_transfer :stop, e.file_transfer
|
480
|
+
end
|
481
|
+
|
482
|
+
s.on :file_transfer_complete do |e|
|
483
|
+
d.file_transfer :complete, e.file_transfer
|
484
|
+
end
|
485
|
+
|
486
|
+
s.on :typing do |e|
|
487
|
+
d.typing e.buddy, e.mode
|
488
|
+
end
|
489
|
+
|
490
|
+
s.on :broadcast do |e|
|
491
|
+
d.broadcast e.message
|
492
|
+
end
|
493
|
+
|
494
|
+
s.on :group_chat_create do |e|
|
495
|
+
d.group_chat :create, e.group_chat
|
496
|
+
end
|
497
|
+
|
498
|
+
s.on :group_chat_invite do |e|
|
499
|
+
d.group_chat :invite, e.group_chat, e.buddy
|
500
|
+
end
|
501
|
+
|
502
|
+
s.on :group_chat_join do |e|
|
503
|
+
if e.buddy
|
504
|
+
d.group_chat :join, e.group_chat, e.buddy, e.invited_by
|
505
|
+
else
|
506
|
+
d.group_chat :joined, e.group_chat, nil, e.invited_by
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
s.on :group_chat_message do |e|
|
511
|
+
d.group_chat :message, e.group_chat, e.buddy, e.message
|
512
|
+
end
|
513
|
+
|
514
|
+
s.on :group_chat_leave do |e|
|
515
|
+
if e.buddy
|
516
|
+
d.group_chat :leave, e.group_chat, e.buddy, e.reason
|
517
|
+
else
|
518
|
+
d.group_chat :left, e.group_chat, nil, e.reason
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
s.on :group_chat_destroy do |e|
|
523
|
+
d.group_chat :destroy, e.group_chat
|
524
|
+
end
|
525
|
+
|
526
|
+
s.on :latency do |e|
|
527
|
+
d.latency e.buddy, e.amount, e.id
|
528
|
+
end
|
529
|
+
|
530
|
+
s.online! if options[:online]
|
531
|
+
}
|
532
|
+
|
533
|
+
EM.add_periodic_timer 60 do
|
534
|
+
d.cleanup!
|
535
|
+
end
|
536
|
+
}.start(options[:host], options[:port], options[:ssl])
|
537
|
+
}
|