simplex-chat 0.4.0 → 0.6.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.
- checksums.yaml +4 -4
- data/lib/simplex-chat/version.rb +1 -1
- data/lib/simplex-chat.rb +145 -77
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b49dea10199c71c10df356701b53087eda1e8aeb5c31348e39a62032280d96b
|
4
|
+
data.tar.gz: e97b4de663eb4abe3aac556b6f8a1b2cf1dcce1d37b2ebfdfc645a7b8e9c05a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2c78755a44732f0b2ab43a080981d4f332c414e2080ff8e4d8ddf76a6af8ed53a0e6c5699561df33b0d8e9d713c92774c1dbff75cc60809adb4ff463f10af8f
|
7
|
+
data.tar.gz: 70eb7367381d1442ed72ff4ae92ed7d876d99e1d3d506bd4da9eafa833f7e1e32c814bec37e052ca9c0b932f726bba59668459de646fac20b004fff14f0ced15
|
data/lib/simplex-chat/version.rb
CHANGED
data/lib/simplex-chat.rb
CHANGED
@@ -18,7 +18,7 @@ module SimpleXChat
|
|
18
18
|
|
19
19
|
def initialize(client_uri, connect: true, log_level: Logger::INFO, timeout_ms: 10_000, interval_ms: 100)
|
20
20
|
@uri = client_uri
|
21
|
-
@message_queue =
|
21
|
+
@message_queue = Queue.new
|
22
22
|
@chat_message_queue = Queue.new
|
23
23
|
@socket = nil
|
24
24
|
@handshake = nil
|
@@ -74,8 +74,8 @@ module SimpleXChat
|
|
74
74
|
single_use_queue.push(resp)
|
75
75
|
@logger.debug("Message sent to waiter with corrId '#{corr_id}'")
|
76
76
|
else
|
77
|
-
@logger.debug("Message put on message queue")
|
78
77
|
@message_queue.push resp
|
78
|
+
@logger.debug("Message put on message queue (number of messages in queue: #{@message_queue.size})")
|
79
79
|
end
|
80
80
|
rescue IO::WaitReadable
|
81
81
|
IO.select([@socket])
|
@@ -97,10 +97,14 @@ module SimpleXChat
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def next_message
|
100
|
-
@message_queue.pop
|
100
|
+
msg = @message_queue.pop
|
101
|
+
@logger.debug("Message retrieved from queue (number of messages in queue: #{@message_queue.size})")
|
102
|
+
msg
|
101
103
|
end
|
102
104
|
|
103
|
-
def next_chat_message
|
105
|
+
def next_chat_message(
|
106
|
+
max_backlog_secs: 15.0 # if nil, it will process any incoming messages, including old ones
|
107
|
+
)
|
104
108
|
# NOTE: There can be more than one message per
|
105
109
|
# client message. Because of that, we use
|
106
110
|
# a chat message queue to insert one or
|
@@ -113,11 +117,6 @@ module SimpleXChat
|
|
113
117
|
break if msg == nil
|
114
118
|
next if not ["chatItemUpdated", "newChatItems"].include?(msg["type"])
|
115
119
|
|
116
|
-
chat_info_types = {
|
117
|
-
"direct" => ChatType::DIRECT,
|
118
|
-
"group" => ChatType::GROUP
|
119
|
-
}
|
120
|
-
|
121
120
|
# Handle one or more chat messages in a single client message
|
122
121
|
new_chat_messages = nil
|
123
122
|
if msg["type"] == "chatItemUpdated"
|
@@ -127,43 +126,22 @@ module SimpleXChat
|
|
127
126
|
end
|
128
127
|
|
129
128
|
new_chat_messages.each do |chat_item|
|
130
|
-
|
131
|
-
group = nil
|
132
|
-
sender = nil
|
133
|
-
contact = nil
|
134
|
-
contact_role = nil
|
135
|
-
if chat_type == ChatType::GROUP
|
136
|
-
# NOTE: The group can "send messages" without a contact
|
137
|
-
# For example, when a member is removed, the group
|
138
|
-
# sends a message about his removal, with no contact
|
139
|
-
contact = chat_item.dig "chatItem", "chatDir", "groupMember", "localDisplayName"
|
140
|
-
contact_role = chat_item.dig "chatItem", "chatDir", "groupMember", "memberRole"
|
141
|
-
group = chat_item["chatInfo"]["groupInfo"]["localDisplayName"]
|
142
|
-
sender = group
|
143
|
-
else
|
144
|
-
contact = chat_item["chatInfo"]["contact"]["localDisplayName"]
|
145
|
-
sender = contact
|
146
|
-
end
|
129
|
+
chat_message = parse_chat_item chat_item
|
147
130
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
:chat_type => chat_type,
|
154
|
-
:sender => sender,
|
155
|
-
:contact_role => contact_role,
|
156
|
-
:contact => contact,
|
157
|
-
:group => group,
|
158
|
-
:msg_text => msg_text,
|
159
|
-
:msg_timestamp => Time.parse(timestamp),
|
160
|
-
:img_preview => image_preview
|
161
|
-
}
|
131
|
+
time_diff = Time.now - chat_message[:msg_timestamp]
|
132
|
+
if max_backlog_secs != nil && time_diff > max_backlog_secs
|
133
|
+
@logger.debug("Skipped message (time diff: #{time_diff}, max allowed: #{max_backlog_secs}): #{chat_message}")
|
134
|
+
next
|
135
|
+
end
|
162
136
|
|
163
137
|
@chat_message_queue.push chat_message
|
164
138
|
end
|
165
139
|
|
166
|
-
|
140
|
+
# NOTE: Even after parsing the messages, the
|
141
|
+
# chat message queue can be empty because
|
142
|
+
# all the messages are too old, so we have
|
143
|
+
# to check again
|
144
|
+
return @chat_message_queue.pop if not @chat_message_queue.empty?
|
167
145
|
end
|
168
146
|
|
169
147
|
nil
|
@@ -224,18 +202,14 @@ module SimpleXChat
|
|
224
202
|
|
225
203
|
def api_version
|
226
204
|
resp = send_command '/version'
|
227
|
-
|
228
|
-
expected_resp_type = "versionInfo"
|
229
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
205
|
+
check_response_type(resp, "versionInfo")
|
230
206
|
|
231
207
|
resp["versionInfo"]["version"]
|
232
208
|
end
|
233
209
|
|
234
210
|
def api_profile
|
235
211
|
resp = send_command '/profile'
|
236
|
-
|
237
|
-
expected_resp_type = "userProfile"
|
238
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
212
|
+
check_response_type(resp, "userProfile")
|
239
213
|
|
240
214
|
{
|
241
215
|
"name" => resp["user"]["profile"]["displayName"],
|
@@ -251,54 +225,42 @@ module SimpleXChat
|
|
251
225
|
if resp_type == "chatCmdError" && resp.dig("chatError", "storeError", "type") == "userContactLinkNotFound"
|
252
226
|
return nil
|
253
227
|
end
|
254
|
-
|
255
|
-
expected_resp_type = "userContactLink"
|
256
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
228
|
+
check_response_type(resp, "userContactLink")
|
257
229
|
|
258
230
|
resp["contactLink"]["connReqContact"]
|
259
231
|
end
|
260
232
|
|
261
233
|
def api_create_user_address
|
262
234
|
resp = send_command '/address'
|
263
|
-
|
264
|
-
expected_resp_type = "userContactLinkCreated"
|
265
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
235
|
+
check_response_type(resp, "userContactLinkCreated")
|
266
236
|
|
267
237
|
resp["connReqContact"]
|
268
238
|
end
|
269
239
|
|
270
240
|
def api_send_text_message(chat_type, receiver, message)
|
271
241
|
resp = send_command "#{chat_type}#{receiver} #{message}"
|
272
|
-
|
273
|
-
expected_resp_type = "newChatItems"
|
274
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
242
|
+
check_response_type(resp, "newChatItems")
|
275
243
|
|
276
244
|
resp["chatItems"]
|
277
245
|
end
|
278
246
|
|
279
247
|
def api_send_image(chat_type, receiver, file_path)
|
280
248
|
resp = send_command "/image #{chat_type}#{receiver} #{file_path}"
|
281
|
-
|
282
|
-
expected_resp_type = "newChatItems"
|
283
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
249
|
+
check_response_type(resp, "newChatItems")
|
284
250
|
|
285
251
|
resp["chatItems"]
|
286
252
|
end
|
287
253
|
|
288
254
|
def api_send_file(chat_type, receiver, file_path)
|
289
255
|
resp = send_command "/file #{chat_type}#{receiver} #{file_path}"
|
290
|
-
|
291
|
-
expected_resp_type = "newChatItems"
|
292
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
256
|
+
check_response_type(resp, "newChatItems")
|
293
257
|
|
294
258
|
resp["chatItems"]
|
295
259
|
end
|
296
260
|
|
297
261
|
def api_contacts
|
298
262
|
resp = send_command "/contacts"
|
299
|
-
|
300
|
-
expected_resp_type = "contactsList"
|
301
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
263
|
+
check_response_type(resp, "contactsList")
|
302
264
|
|
303
265
|
contacts = resp["contacts"]
|
304
266
|
contacts.map{ |c| {
|
@@ -313,9 +275,7 @@ module SimpleXChat
|
|
313
275
|
|
314
276
|
def api_groups
|
315
277
|
resp = send_command "/groups"
|
316
|
-
|
317
|
-
expected_resp_type = "groupsList"
|
318
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
278
|
+
check_response_type(resp, "groupsList")
|
319
279
|
|
320
280
|
groups = resp["groups"]
|
321
281
|
groups.map{ |entry|
|
@@ -341,18 +301,14 @@ module SimpleXChat
|
|
341
301
|
onoff = is_enabled && "on" || "off"
|
342
302
|
|
343
303
|
resp = send_command "/auto_accept #{onoff}"
|
344
|
-
|
345
|
-
expected_resp_type = "userContactLinkUpdated"
|
346
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
304
|
+
check_response_type(resp, "userContactLinkUpdated")
|
347
305
|
|
348
306
|
nil
|
349
307
|
end
|
350
308
|
|
351
309
|
def api_kick_group_member(group, member)
|
352
310
|
resp = send_command "/remove #{group} #{member}"
|
353
|
-
|
354
|
-
expected_resp_type = "userDeletedMember"
|
355
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
311
|
+
check_response_type(resp, "userDeletedMember")
|
356
312
|
end
|
357
313
|
|
358
314
|
# Parameters for /network:
|
@@ -375,18 +331,130 @@ module SimpleXChat
|
|
375
331
|
command += " #{param}=#{value}"
|
376
332
|
end
|
377
333
|
resp = send_command command
|
378
|
-
|
379
|
-
expected_resp_type = "networkConfig"
|
380
|
-
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
334
|
+
check_response_type(resp, "networkConfig")
|
381
335
|
|
382
336
|
resp["networkConfig"]
|
383
337
|
end
|
384
338
|
|
339
|
+
def api_tail(chat_type: nil, conversation: nil, message_count: nil)
|
340
|
+
cmd = "/tail"
|
341
|
+
cmd += " #{chat_type}#{conversation}" if chat_type != nil && conversation != nil
|
342
|
+
cmd += " #{message_count}" if message_count != nil
|
343
|
+
resp = send_command cmd
|
344
|
+
check_response_type(resp, "chatItems")
|
345
|
+
|
346
|
+
resp["chatItems"].map{|chat_item| parse_chat_item chat_item}
|
347
|
+
end
|
348
|
+
|
349
|
+
def api_chats(
|
350
|
+
chat_count=20 # if nil, will return all the chats
|
351
|
+
)
|
352
|
+
param = chat_count != nil ? "#{chat_count}" : "all"
|
353
|
+
cmd = "/chats #{param}"
|
354
|
+
resp = send_command cmd
|
355
|
+
check_response_type(resp, "chats")
|
356
|
+
|
357
|
+
resp["chats"].map do |chat|
|
358
|
+
chat_type = parse_chat_info_type chat["chatInfo"]["type"]
|
359
|
+
next if chat_type == nil # WARN: Chat type "local" is currently ignored
|
360
|
+
conversation = nil
|
361
|
+
if chat_type == ChatType::GROUP
|
362
|
+
conversation = chat["chatInfo"]["groupInfo"]["localDisplayName"]
|
363
|
+
else
|
364
|
+
conversation = chat["chatInfo"]["contact"]["localDisplayName"]
|
365
|
+
end
|
366
|
+
|
367
|
+
{
|
368
|
+
:chat_type => chat_type,
|
369
|
+
:conversation => conversation
|
370
|
+
}
|
371
|
+
end.filter { |x| x != nil }
|
372
|
+
end
|
373
|
+
|
374
|
+
# TODO: Add `/_reaction members` support, either on this
|
375
|
+
# function or in a separate one
|
376
|
+
def api_reaction(chat_type, chat_id, message_item_id, add: true, emoji: '👍')
|
377
|
+
onoff = add ? "on" : "off"
|
378
|
+
param_obj = {
|
379
|
+
"type" => "emoji",
|
380
|
+
"emoji" => emoji
|
381
|
+
}
|
382
|
+
cmd = "/_reaction #{chat_type}#{chat_id} #{message_item_id} #{onoff} #{param_obj.to_json}"
|
383
|
+
resp = send_command cmd
|
384
|
+
check_response_type(resp, "chatItemReaction")
|
385
|
+
end
|
386
|
+
|
385
387
|
private
|
386
388
|
|
389
|
+
def check_response_type(resp, expected_resp_type)
|
390
|
+
resp_type = resp["type"]
|
391
|
+
raise UnexpectedResponseError.new(resp_type, expected_resp_type) unless resp_type == expected_resp_type
|
392
|
+
end
|
393
|
+
|
387
394
|
def next_corr_id
|
388
395
|
# The correlation ID has to be a string
|
389
396
|
(@corr_id.update { |x| x + 1 } - 1).to_s(10)
|
390
397
|
end
|
398
|
+
|
399
|
+
def parse_chat_info_type(type)
|
400
|
+
chat_info_types = {
|
401
|
+
"direct" => ChatType::DIRECT,
|
402
|
+
"group" => ChatType::GROUP
|
403
|
+
}
|
404
|
+
|
405
|
+
chat_info_types.dig(type)
|
406
|
+
end
|
407
|
+
|
408
|
+
def parse_chat_item(chat_item)
|
409
|
+
chat_type = parse_chat_info_type chat_item["chatInfo"]["type"]
|
410
|
+
group = nil
|
411
|
+
group_id = nil
|
412
|
+
sender = nil
|
413
|
+
sender_id = nil
|
414
|
+
contact = nil
|
415
|
+
contact_id = nil
|
416
|
+
contact_role = nil
|
417
|
+
if chat_type == ChatType::GROUP
|
418
|
+
# NOTE: The group can "send messages" without a contact
|
419
|
+
# For example, when a member is removed, the group
|
420
|
+
# sends a message about his removal, with no contact
|
421
|
+
contact = chat_item.dig "chatItem", "chatDir", "groupMember", "localDisplayName"
|
422
|
+
contact_id = chat_item.dig "chatItem", "chatDir", "groupMember", "groupMemberId"
|
423
|
+
contact_role = chat_item.dig "chatItem", "chatDir", "groupMember", "memberRole"
|
424
|
+
group = chat_item["chatInfo"]["groupInfo"]["localDisplayName"]
|
425
|
+
group_id = chat_item["chatInfo"]["groupInfo"]["groupId"]
|
426
|
+
sender = group
|
427
|
+
sender_id = group_id
|
428
|
+
else
|
429
|
+
contact = chat_item["chatInfo"]["contact"]["localDisplayName"]
|
430
|
+
contact_id = chat_item["chatInfo"]["contact"]["contactId"]
|
431
|
+
sender = contact
|
432
|
+
sender_id = contact_id
|
433
|
+
end
|
434
|
+
|
435
|
+
msg_text = chat_item["chatItem"]["meta"]["itemText"]
|
436
|
+
msg_item_id = chat_item["chatItem"]["meta"]["itemId"]
|
437
|
+
timestamp = Time.parse(chat_item["chatItem"]["meta"]["updatedAt"])
|
438
|
+
msg_image_preview = chat_item.dig "chatItem", "content", "msgContent", "image"
|
439
|
+
|
440
|
+
chat_message = {
|
441
|
+
:chat_type => chat_type,
|
442
|
+
|
443
|
+
:sender => sender,
|
444
|
+
:sender_id => sender_id,
|
445
|
+
|
446
|
+
:contact => contact,
|
447
|
+
:contact_id => contact_id,
|
448
|
+
:contact_role => contact_role,
|
449
|
+
|
450
|
+
:group => group,
|
451
|
+
:group_id => group_id,
|
452
|
+
|
453
|
+
:msg_text => msg_text,
|
454
|
+
:msg_item_id => msg_item_id,
|
455
|
+
:msg_timestamp => timestamp,
|
456
|
+
:msg_img_preview => msg_image_preview
|
457
|
+
}
|
458
|
+
end
|
391
459
|
end
|
392
460
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simplex-chat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- rdbo
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-03-
|
10
|
+
date: 2025-03-05 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: websocket
|