globalchat 1.0.0
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/.DS_Store +0 -0
- data/.gitignore +17 -0
- data/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/GlobalChat.ui +74 -0
- data/LICENSE +22 -0
- data/LICENSE.rtf +679 -0
- data/README.md +5 -0
- data/Rakefile +2 -0
- data/ServerList.ui +159 -0
- data/bin/globalchat +439 -0
- data/gc2-qtruby.gemspec +17 -0
- data/gchat2pro.pstore +1 -0
- data/global_chat_2_pro.rb +439 -0
- data/globalchat2.ico +0 -0
- data/lib/gc2-qtruby/version.rb +5 -0
- data/lib/gc2-qtruby.rb +7 -0
- data/server.rb +308 -0
- metadata +80 -0
data/server.rb
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require 'gserver'
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'securerandom'
|
|
6
|
+
require 'pstore'
|
|
7
|
+
|
|
8
|
+
class GlobalChatServer < GServer
|
|
9
|
+
|
|
10
|
+
attr_accessor :handles, :buffer, :handle_keys, :sockets, :password, :socket_keys, :scrollback, :server_name
|
|
11
|
+
|
|
12
|
+
def initialize(port=9994, *args)
|
|
13
|
+
super(port, *args)
|
|
14
|
+
self.audit = true
|
|
15
|
+
self.debug = true
|
|
16
|
+
@pstore = PStore.new("gchat.pstore")
|
|
17
|
+
@handle_keys = {} # stores handle
|
|
18
|
+
@socket_keys = {} # stores chat_token
|
|
19
|
+
# @port_keys = {} # unnecessary in PING design
|
|
20
|
+
@handle_last_pinged = {} # used for clone removal
|
|
21
|
+
@handles = []
|
|
22
|
+
@sockets = []
|
|
23
|
+
@buffer = []
|
|
24
|
+
@server_name = "GlobalChatNet"
|
|
25
|
+
load_chat_log
|
|
26
|
+
@mutex = Mutex.new
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def broadcast(message, sender=nil)
|
|
30
|
+
@mutex.synchronize do
|
|
31
|
+
@sockets.each do |socket|
|
|
32
|
+
begin
|
|
33
|
+
sock_send(socket, message) unless socket == sender
|
|
34
|
+
rescue
|
|
35
|
+
log "broadcast fail removal event"
|
|
36
|
+
remove_dead_socket socket
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def remove_dead_socket(socket)
|
|
43
|
+
@sockets.delete socket
|
|
44
|
+
ct = @socket_keys[socket]
|
|
45
|
+
handle = @handle_keys[ct]
|
|
46
|
+
@handles.delete handle
|
|
47
|
+
@handle_keys.delete ct
|
|
48
|
+
@socket_keys.delete socket
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def remove_user_by_handle(handle)
|
|
52
|
+
ct = @handle_keys.key(handle)
|
|
53
|
+
handle = @handle_keys[ct]
|
|
54
|
+
socket = @socket_keys.key(ct)
|
|
55
|
+
@sockets.delete socket
|
|
56
|
+
|
|
57
|
+
@handles.delete handle
|
|
58
|
+
@handle_keys.delete ct
|
|
59
|
+
@socket_keys.delete socket
|
|
60
|
+
begin
|
|
61
|
+
broadcast_message(socket, "LEAVE", [handle])
|
|
62
|
+
rescue
|
|
63
|
+
log "failed to broadcast LEAVE for clone handle #{handle}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def check_token(chat_token)
|
|
68
|
+
sender = @handle_keys[chat_token]
|
|
69
|
+
return !sender.nil?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def get_handle(chat_token)
|
|
73
|
+
sender = @handle_keys[chat_token]
|
|
74
|
+
return sender
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# server tell a single socket
|
|
78
|
+
def send_message(io, opcode, args)
|
|
79
|
+
msg = opcode + "::!!::" + args.join("::!!::")
|
|
80
|
+
sock_send io, msg
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def sock_send io, msg
|
|
84
|
+
msg = "#{msg}\0"
|
|
85
|
+
log msg
|
|
86
|
+
io.send msg, 0
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# server tell all sockets except
|
|
90
|
+
# if sender is nil then everyone
|
|
91
|
+
def broadcast_message(sender, opcode, args)
|
|
92
|
+
msg = opcode + "::!!::" + args.join("::!!::")
|
|
93
|
+
broadcast msg, sender
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def build_chat_log
|
|
97
|
+
return "" unless @scrollback
|
|
98
|
+
out = ""
|
|
99
|
+
@buffer.each do |msg|
|
|
100
|
+
out += "#{msg[0]}: #{msg[1]}\n"
|
|
101
|
+
end
|
|
102
|
+
out
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def clean_handles
|
|
107
|
+
@handle_keys.each do |k, v|
|
|
108
|
+
if @handle_last_pinged[v] && @handle_last_pinged[v] < Time.now - 30
|
|
109
|
+
log "removed clone handle: #{v}"
|
|
110
|
+
remove_user_by_handle(v)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def build_handle_list
|
|
116
|
+
return @handles.join("\n")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# react to allowed commands
|
|
120
|
+
def parse_line(line, io)
|
|
121
|
+
parr = line.split("::!!::")
|
|
122
|
+
command = parr[0]
|
|
123
|
+
if command == "SIGNON"
|
|
124
|
+
handle = parr[1]
|
|
125
|
+
password = parr[2]
|
|
126
|
+
|
|
127
|
+
if !@handles.length == 0 && @handles.include?(handle)
|
|
128
|
+
send_message(io, "ALERT", ["Your handle is in use."])
|
|
129
|
+
io.close
|
|
130
|
+
return
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if handle == nil || handle == ""
|
|
134
|
+
send_message(io, "ALERT", ["You cannot have a blank name."])
|
|
135
|
+
#remove_dead_socket io
|
|
136
|
+
io.close
|
|
137
|
+
return
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if ((@password == password) || ((password === nil) && (@password == "")))
|
|
141
|
+
# uuid are guaranteed unique
|
|
142
|
+
chat_token = rand(36**8).to_s(36)
|
|
143
|
+
@mutex.synchronize do
|
|
144
|
+
@handle_keys[chat_token] = handle
|
|
145
|
+
@socket_keys[io] = chat_token
|
|
146
|
+
# @port_keys[io.peeraddr[1]] = chat_token
|
|
147
|
+
# not on list until pinged.
|
|
148
|
+
@handles << handle
|
|
149
|
+
@sockets << io
|
|
150
|
+
end
|
|
151
|
+
send_message(io, "TOKEN", [chat_token, handle, @server_name])
|
|
152
|
+
broadcast_message(io, "JOIN", [handle])
|
|
153
|
+
else
|
|
154
|
+
|
|
155
|
+
send_message(io, "ALERT", ["Password is incorrect."])
|
|
156
|
+
io.close
|
|
157
|
+
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# auth
|
|
165
|
+
chat_token = parr.last
|
|
166
|
+
|
|
167
|
+
if check_token(chat_token)
|
|
168
|
+
handle = get_handle(chat_token)
|
|
169
|
+
if command == "GETHANDLES"
|
|
170
|
+
send_message(io, "HANDLES", [build_handle_list])
|
|
171
|
+
elsif command == "GETBUFFER"
|
|
172
|
+
buffer = build_chat_log
|
|
173
|
+
send_message(io, "BUFFER", [buffer])
|
|
174
|
+
elsif command == "MESSAGE"
|
|
175
|
+
msg = parr[1]
|
|
176
|
+
message = "#{handle}: #{msg}\n"
|
|
177
|
+
@buffer << [handle, msg]
|
|
178
|
+
broadcast_message(io, "SAY", [handle, msg])
|
|
179
|
+
elsif command == "PING"
|
|
180
|
+
unless @handles.include?(handle)
|
|
181
|
+
@handles << handle
|
|
182
|
+
end
|
|
183
|
+
@handle_last_pinged[handle] = Time.now
|
|
184
|
+
elsif command == "SIGNOFF"
|
|
185
|
+
broadcast_message(nil, "LEAVE", [handle])
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def pong_everyone
|
|
191
|
+
#log "trying to pong"
|
|
192
|
+
unless @sockets.length == 0 && !self.stopped?
|
|
193
|
+
#log "ponging"
|
|
194
|
+
broadcast_message(nil, "PONG", [build_handle_list])
|
|
195
|
+
sleep 5
|
|
196
|
+
clean_handles
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def start_pong_loop
|
|
201
|
+
Thread.new do
|
|
202
|
+
loop do
|
|
203
|
+
sleep 5
|
|
204
|
+
pong_everyone
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# def disconnecting(clientPort)
|
|
210
|
+
# log "disconnect event"
|
|
211
|
+
# ct = @port_keys[clientPort]
|
|
212
|
+
# handle = @handle_keys[ct]
|
|
213
|
+
# if handle
|
|
214
|
+
# log "disconnect removal event"
|
|
215
|
+
# remove_dead_socket ct
|
|
216
|
+
# end
|
|
217
|
+
# super(clientPort)
|
|
218
|
+
# end
|
|
219
|
+
def starting
|
|
220
|
+
log("GlobalChat2 Server Running")
|
|
221
|
+
start_pong_loop
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def serve(io)
|
|
225
|
+
loop do
|
|
226
|
+
data = ""
|
|
227
|
+
begin
|
|
228
|
+
while line = io.recv(1)
|
|
229
|
+
break if line == "\0"
|
|
230
|
+
data += line
|
|
231
|
+
end
|
|
232
|
+
rescue
|
|
233
|
+
log "recv break removal event"
|
|
234
|
+
remove_dead_socket io #, true
|
|
235
|
+
break
|
|
236
|
+
end
|
|
237
|
+
unless data == ""
|
|
238
|
+
log "#{data}"
|
|
239
|
+
parse_line(data, io)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def status
|
|
245
|
+
passworded = (self.password != "")
|
|
246
|
+
scrollback = self.scrollback
|
|
247
|
+
log "#{@server_name} running on GlobalChat2 3.0 platform Replay:#{scrollback} Passworded:#{passworded}"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def log(msg)
|
|
251
|
+
puts msg
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def save_chat_log
|
|
256
|
+
log "saving chatlog"
|
|
257
|
+
@pstore.transaction do
|
|
258
|
+
@pstore[:log] = @buffer
|
|
259
|
+
#p @pstore[:log]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def load_chat_log
|
|
265
|
+
log "loading chatlog"
|
|
266
|
+
@pstore.transaction(true) do
|
|
267
|
+
@buffer = @pstore[:log] || []
|
|
268
|
+
#p @buffer
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def ping_nexus(chatnet_name, host, port)
|
|
276
|
+
puts "Pinging NexusNet that I'm Online!!"
|
|
277
|
+
uri = URI.parse("http://nexusnet.herokuapp.com/online")
|
|
278
|
+
query = {:name => chatnet_name, :port => port, :host => host}
|
|
279
|
+
uri.query = URI.encode_www_form( query )
|
|
280
|
+
Net::HTTP.get(uri)
|
|
281
|
+
$published = true
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def nexus_offline
|
|
285
|
+
puts "Informing NexusNet that I have exited!!!"
|
|
286
|
+
Net::HTTP.get_print("nexusnet.herokuapp.com", "/offline")
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
at_exit do
|
|
290
|
+
nexus_offline
|
|
291
|
+
$gc.save_chat_log
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
$gc = GlobalChatServer.new(9994, '0.0.0.0', 1000, $stderr, true)
|
|
295
|
+
$gc.password = "" # set a password here
|
|
296
|
+
$gc.scrollback = true
|
|
297
|
+
$gc.start
|
|
298
|
+
|
|
299
|
+
if ENV["RUBY_VERSION"] && ENV["RUBY_VERSION"].include?("1.9")
|
|
300
|
+
ping_nexus("GlobalChatNet2", "localhost", $gc.port)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
$gc.status
|
|
304
|
+
|
|
305
|
+
$gc.join
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
|
metadata
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: globalchat
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Jonathan "jsilver1er" Silverman
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-11-25 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: qtbindings
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 4.8.3.0
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ! '>='
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 4.8.3.0
|
|
30
|
+
description: GlobalChat 2 Pro Chat Client
|
|
31
|
+
email:
|
|
32
|
+
- jsilverone@me.com
|
|
33
|
+
executables:
|
|
34
|
+
- globalchat
|
|
35
|
+
extensions: []
|
|
36
|
+
extra_rdoc_files: []
|
|
37
|
+
files:
|
|
38
|
+
- .DS_Store
|
|
39
|
+
- .gitignore
|
|
40
|
+
- .rvmrc
|
|
41
|
+
- Gemfile
|
|
42
|
+
- GlobalChat.ui
|
|
43
|
+
- LICENSE
|
|
44
|
+
- LICENSE.rtf
|
|
45
|
+
- README.md
|
|
46
|
+
- Rakefile
|
|
47
|
+
- ServerList.ui
|
|
48
|
+
- gc2-qtruby.gemspec
|
|
49
|
+
- gchat2pro.pstore
|
|
50
|
+
- global_chat_2_pro.rb
|
|
51
|
+
- globalchat2.ico
|
|
52
|
+
- lib/gc2-qtruby.rb
|
|
53
|
+
- lib/gc2-qtruby/version.rb
|
|
54
|
+
- server.rb
|
|
55
|
+
- bin/globalchat
|
|
56
|
+
homepage: http://globalchat2.net
|
|
57
|
+
licenses: []
|
|
58
|
+
post_install_message:
|
|
59
|
+
rdoc_options: []
|
|
60
|
+
require_paths:
|
|
61
|
+
- .
|
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
63
|
+
none: false
|
|
64
|
+
requirements:
|
|
65
|
+
- - ! '>='
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
none: false
|
|
70
|
+
requirements:
|
|
71
|
+
- - ! '>='
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0'
|
|
74
|
+
requirements: []
|
|
75
|
+
rubyforge_project:
|
|
76
|
+
rubygems_version: 1.8.24
|
|
77
|
+
signing_key:
|
|
78
|
+
specification_version: 3
|
|
79
|
+
summary: QTRuby crossplatform version
|
|
80
|
+
test_files: []
|