matrix_sdk 2.3.0 → 2.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/CHANGELOG.md +25 -0
- data/README.md +2 -0
- data/lib/matrix_sdk/api.rb +52 -18
- data/lib/matrix_sdk/client.rb +41 -27
- data/lib/matrix_sdk/mxid.rb +41 -1
- data/lib/matrix_sdk/protocols/cs.rb +134 -108
- data/lib/matrix_sdk/protocols/msc.rb +5 -0
- data/lib/matrix_sdk/room.rb +106 -39
- data/lib/matrix_sdk/rooms/space.rb +79 -0
- data/lib/matrix_sdk/user.rb +4 -4
- data/lib/matrix_sdk/util/events.rb +4 -6
- data/lib/matrix_sdk/util/extensions.rb +13 -19
- data/lib/matrix_sdk/util/tinycache.rb +31 -7
- data/lib/matrix_sdk/util/tinycache_adapter.rb +5 -0
- data/lib/matrix_sdk/util/uri.rb +101 -0
- data/lib/matrix_sdk/version.rb +1 -1
- data/lib/matrix_sdk.rb +10 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50821866efab2cbd39daf23bc9ff3bd7864bca5638f76c766a8ba26ab91c7423
|
4
|
+
data.tar.gz: 2601cf5141e824b322f5310e09b72d42059162e1ac87da11243167d2604f1801
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7bb45dfcb6696b75ed0be95b92fbb3f569b258d4d0456a3cd8ea16fd22d5e3d5050607e6229bfa0e32b84fc0248e554db8f93d48cd970b2ff95475cf0bcfba85
|
7
|
+
data.tar.gz: 58d8524e9d83ef570ca19a41f28758fc129c1a3988ea3522a994e58eaa93c4807850b63a2171423dd69a948738e69468542d56d882ecf98c60e6f8607928b6d1
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
## 2.6.0 - 2022-07-15
|
2
|
+
|
3
|
+
- Adds some multi-thread usage support to the API (create your API/client with `threadsafe: :multithread/true/false`)
|
4
|
+
The API will currently default to running with multi-threaded requests. In case your application is single-threaded - or never performs requests from multiple threads - then you can set `threadsafe: true/false` to support connection reuse.
|
5
|
+
- Changes room finding to ignore non-canonical aliases by default
|
6
|
+
- Improves room alias handling
|
7
|
+
- Improves Ruby 3.0+ support
|
8
|
+
- Improves debug output by supporting PP (Ruby pretty print) on all MatrixSdk objects
|
9
|
+
|
10
|
+
## 2.5.0 - 2022-01-14
|
11
|
+
|
12
|
+
- Adds preliminary support for the Matrix v1.1 `client/v3` API
|
13
|
+
- Adds some support for knocking rooms
|
14
|
+
- Adds mutex synchronization on API requests to avoid some threading issues
|
15
|
+
- Fixes error on attempting to skip cache for certain requests (#19)
|
16
|
+
- Fixes inconsistency in MXID typing for the Client abstraction (#18 #20)
|
17
|
+
- Fixes missed autoloader entries for errors (#22)
|
18
|
+
- Fixes some potential issues arising from broken user-provided state data
|
19
|
+
|
20
|
+
## 2.4.0 - 2021-07-19
|
21
|
+
|
22
|
+
- Adds support for matrix: URI's according to MSC2312
|
23
|
+
- Adds some basic support for detecting Spaces (MSC1772)
|
24
|
+
- Fixes sync against Synapse 1.38.0 missing empty fields
|
25
|
+
|
1
26
|
## 2.3.0 - 2021-03-26
|
2
27
|
|
3
28
|
- Adds support for Ruby 3.0 (#15)
|
data/README.md
CHANGED
data/lib/matrix_sdk/api.rb
CHANGED
@@ -19,7 +19,7 @@ module MatrixSdk
|
|
19
19
|
}.freeze
|
20
20
|
|
21
21
|
attr_accessor :access_token, :connection_address, :connection_port, :device_id, :autoretry, :global_headers
|
22
|
-
attr_reader :homeserver, :validate_certificate, :open_timeout, :read_timeout, :well_known, :proxy_uri
|
22
|
+
attr_reader :homeserver, :validate_certificate, :open_timeout, :read_timeout, :well_known, :proxy_uri, :threadsafe
|
23
23
|
|
24
24
|
ignore_inspect :access_token, :logger
|
25
25
|
|
@@ -39,7 +39,10 @@ module MatrixSdk
|
|
39
39
|
# @option params [Hash] :global_headers Additional headers to set for all requests
|
40
40
|
# @option params [Boolean] :skip_login Should the API skip logging in if the HS URL contains user information
|
41
41
|
# @option params [Boolean] :synapse (true) Is the API connecting to a Synapse instance
|
42
|
-
# @option params [
|
42
|
+
# @option params [Boolean,:multithread] :threadsafe (:multithread) Should the connection be threadsafe/mutexed - or
|
43
|
+
# safe for simultaneous multi-thread usage. Will default to +:multithread+ - a.k.a. per-thread HTTP connections
|
44
|
+
# and requests
|
45
|
+
# @note Using threadsafe +:multithread+ currently doesn't support connection re-use
|
43
46
|
def initialize(homeserver, **params)
|
44
47
|
@homeserver = homeserver
|
45
48
|
raise ArgumentError, 'Homeserver URL must be String or URI' unless @homeserver.is_a?(String) || @homeserver.is_a?(URI)
|
@@ -57,14 +60,16 @@ module MatrixSdk
|
|
57
60
|
@validate_certificate = params.fetch(:validate_certificate, false)
|
58
61
|
@transaction_id = params.fetch(:transaction_id, 0)
|
59
62
|
@backoff_time = params.fetch(:backoff_time, 5000)
|
60
|
-
@open_timeout = params.fetch(:open_timeout,
|
61
|
-
@read_timeout = params.fetch(:read_timeout,
|
63
|
+
@open_timeout = params.fetch(:open_timeout, nil)
|
64
|
+
@read_timeout = params.fetch(:read_timeout, nil)
|
62
65
|
@well_known = params.fetch(:well_known, {})
|
63
66
|
@global_headers = DEFAULT_HEADERS.dup
|
64
67
|
@global_headers.merge!(params.fetch(:global_headers)) if params.key? :global_headers
|
65
68
|
@synapse = params.fetch(:synapse, true)
|
66
69
|
@http = nil
|
67
70
|
|
71
|
+
self.threadsafe = params.fetch(:threadsafe, :multithread)
|
72
|
+
|
68
73
|
([params.fetch(:protocols, [:CS])].flatten - protocols).each do |proto|
|
69
74
|
self.class.include MatrixSdk::Protocols.const_get(proto)
|
70
75
|
end
|
@@ -243,6 +248,17 @@ module MatrixSdk
|
|
243
248
|
@proxy_uri = proxy_uri
|
244
249
|
end
|
245
250
|
|
251
|
+
# @param [Boolean,:multithread] threadsafe What level of thread-safety the API should use
|
252
|
+
# @return [Boolean,:multithread]
|
253
|
+
def threadsafe=(threadsafe)
|
254
|
+
raise ArgumentError, 'Threadsafe must be either a boolean or :multithread' unless [true, false, :multithread].include? threadsafe
|
255
|
+
raise ArugmentError, 'JRuby only support :multithread/false for threadsafe' if RUBY_ENGINE == 'jruby' && threadsafe == true
|
256
|
+
|
257
|
+
@threadsafe = threadsafe
|
258
|
+
@http_lock = nil unless threadsafe == true
|
259
|
+
@threadsafe
|
260
|
+
end
|
261
|
+
|
246
262
|
# Perform a raw Matrix API request
|
247
263
|
#
|
248
264
|
# @example Simple API query
|
@@ -281,15 +297,25 @@ module MatrixSdk
|
|
281
297
|
|
282
298
|
req_obj = construct_request(url: url, method: method, **options)
|
283
299
|
print_http(req_obj, id: req_id)
|
284
|
-
|
300
|
+
response = duration = nil
|
301
|
+
|
302
|
+
loc_http = http
|
303
|
+
perform_request = proc do
|
285
304
|
dur_start = Time.now
|
286
|
-
response =
|
305
|
+
response = loc_http.request req_obj
|
287
306
|
dur_end = Time.now
|
288
307
|
duration = dur_end - dur_start
|
289
308
|
rescue EOFError
|
290
309
|
logger.error 'Socket closed unexpectedly'
|
291
310
|
raise
|
292
311
|
end
|
312
|
+
|
313
|
+
if @threadsafe == true
|
314
|
+
http_lock.synchronize { perform_request.call }
|
315
|
+
else
|
316
|
+
perform_request.call
|
317
|
+
loc_http.finish if @threadsafe == :multithread
|
318
|
+
end
|
293
319
|
print_http(response, duration: duration, id: req_id)
|
294
320
|
|
295
321
|
begin
|
@@ -392,18 +418,26 @@ module MatrixSdk
|
|
392
418
|
|
393
419
|
host = (@connection_address || homeserver.host)
|
394
420
|
port = (@connection_port || homeserver.port)
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
421
|
+
|
422
|
+
connection = @http unless @threadsafe == :multithread
|
423
|
+
connection ||= if proxy_uri
|
424
|
+
Net::HTTP.new(host, port, proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
|
425
|
+
else
|
426
|
+
Net::HTTP.new(host, port)
|
427
|
+
end
|
428
|
+
|
429
|
+
connection.open_timeout = open_timeout if open_timeout
|
430
|
+
connection.read_timeout = read_timeout if read_timeout
|
431
|
+
connection.use_ssl = homeserver.scheme == 'https'
|
432
|
+
connection.verify_mode = validate_certificate ? ::OpenSSL::SSL::VERIFY_PEER : ::OpenSSL::SSL::VERIFY_NONE
|
433
|
+
connection.start
|
434
|
+
@http = connection unless @threadsafe == :multithread
|
435
|
+
|
436
|
+
connection
|
437
|
+
end
|
438
|
+
|
439
|
+
def http_lock
|
440
|
+
@http_lock ||= Mutex.new if @threadsafe == true
|
407
441
|
end
|
408
442
|
end
|
409
443
|
end
|
data/lib/matrix_sdk/client.rb
CHANGED
@@ -73,6 +73,7 @@ module MatrixSdk
|
|
73
73
|
|
74
74
|
@cache = client_cache
|
75
75
|
@identity_server = nil
|
76
|
+
@mxid = nil
|
76
77
|
|
77
78
|
@sync_token = nil
|
78
79
|
@sync_thread = nil
|
@@ -102,9 +103,8 @@ module MatrixSdk
|
|
102
103
|
#
|
103
104
|
# @return [MXID] The MXID of the current user
|
104
105
|
def mxid
|
105
|
-
@mxid ||=
|
106
|
-
|
107
|
-
end
|
106
|
+
@mxid ||= MXID.new api.whoami?[:user_id] if api&.access_token
|
107
|
+
@mxid
|
108
108
|
end
|
109
109
|
|
110
110
|
alias user_id mxid
|
@@ -161,9 +161,7 @@ module MatrixSdk
|
|
161
161
|
#
|
162
162
|
# @return [Hash[String,Array[String]]] A mapping of MXIDs to a list of direct rooms with that user
|
163
163
|
def direct_rooms
|
164
|
-
|
165
|
-
[mxid.to_s, rooms]
|
166
|
-
end]
|
164
|
+
api.get_account_data(mxid, 'm.direct').transform_keys(&:to_s)
|
167
165
|
end
|
168
166
|
|
169
167
|
# Gets a direct message room for the given user if one exists
|
@@ -194,6 +192,19 @@ module MatrixSdk
|
|
194
192
|
@rooms.values
|
195
193
|
end
|
196
194
|
|
195
|
+
# Get a list of all joined Matrix Spaces
|
196
|
+
#
|
197
|
+
# @return [Array[Room]] All the currently joined Spaces
|
198
|
+
def spaces
|
199
|
+
rooms = if cache == :none
|
200
|
+
api.get_joined_rooms.joined_rooms.map { |id| Room.new(self, id) }
|
201
|
+
else
|
202
|
+
self.rooms
|
203
|
+
end
|
204
|
+
|
205
|
+
rooms.select(&:space?)
|
206
|
+
end
|
207
|
+
|
197
208
|
# Refresh the list of currently handled rooms, replacing it with the user's
|
198
209
|
# currently joined rooms.
|
199
210
|
#
|
@@ -211,6 +222,7 @@ module MatrixSdk
|
|
211
222
|
true
|
212
223
|
end
|
213
224
|
alias refresh_rooms! reload_rooms!
|
225
|
+
alias reload_spaces! reload_rooms!
|
214
226
|
|
215
227
|
# Register - and log in - on the connected HS as a guest
|
216
228
|
#
|
@@ -383,15 +395,16 @@ module MatrixSdk
|
|
383
395
|
# @param only_canonical [Boolean] Only match alias against the canonical alias
|
384
396
|
# @return [Room] The found room
|
385
397
|
# @return [nil] If no room was found
|
386
|
-
def find_room(room_id_or_alias, only_canonical:
|
398
|
+
def find_room(room_id_or_alias, only_canonical: true)
|
387
399
|
room_id_or_alias = MXID.new(room_id_or_alias.to_s) unless room_id_or_alias.is_a? MXID
|
388
400
|
raise ArgumentError, 'Must be a room id or alias' unless room_id_or_alias.room?
|
389
401
|
|
390
402
|
return @rooms.fetch(room_id_or_alias.to_s, nil) if room_id_or_alias.room_id?
|
391
403
|
|
392
|
-
|
404
|
+
room = @rooms.values.find { |r| r.aliases.include? room_id_or_alias.to_s }
|
405
|
+
return room if only_canonical
|
393
406
|
|
394
|
-
@rooms.values.find { |r| r.aliases.include? room_id_or_alias.to_s }
|
407
|
+
room || @rooms.values.find { |r| r.aliases(canonical_only: false).include? room_id_or_alias.to_s }
|
395
408
|
end
|
396
409
|
|
397
410
|
# Get a User instance from a MXID
|
@@ -429,13 +442,13 @@ module MatrixSdk
|
|
429
442
|
|
430
443
|
# Upload a piece of data to the media repo
|
431
444
|
#
|
432
|
-
# @return [URI::
|
445
|
+
# @return [URI::MXC] A Matrix content (mxc://) URL pointing to the uploaded data
|
433
446
|
# @param content [String] The data to upload
|
434
447
|
# @param content_type [String] The MIME type of the data
|
435
448
|
# @see Protocols::CS#media_upload
|
436
449
|
def upload(content, content_type)
|
437
450
|
data = api.media_upload(content, content_type)
|
438
|
-
return data[:content_uri] if data.key? :content_uri
|
451
|
+
return URI(data[:content_uri]) if data.key? :content_uri
|
439
452
|
|
440
453
|
raise MatrixUnexpectedResponseError, 'Upload succeeded, but no media URI returned'
|
441
454
|
end
|
@@ -518,11 +531,9 @@ module MatrixSdk
|
|
518
531
|
|
519
532
|
attempts = 0
|
520
533
|
data = loop do
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
raise e if (attempts += 1) >= params.fetch(:allow_sync_retry, 0)
|
525
|
-
end
|
534
|
+
break api.sync(**extra_params)
|
535
|
+
rescue MatrixSdk::MatrixTimeoutError => e
|
536
|
+
raise e if (attempts += 1) >= params.fetch(:allow_sync_retry, 0)
|
526
537
|
end
|
527
538
|
|
528
539
|
@next_batch = data[:next_batch] unless skip_store_batch
|
@@ -542,11 +553,14 @@ module MatrixSdk
|
|
542
553
|
raise ArgumentError, 'Must be a room ID' unless room_id.room_id?
|
543
554
|
|
544
555
|
room_id = room_id.to_s
|
545
|
-
@rooms.fetch(room_id) do
|
556
|
+
ret = @rooms.fetch(room_id) do
|
546
557
|
room = Room.new(self, room_id)
|
547
558
|
@rooms[room_id] = room unless cache == :none
|
548
559
|
room
|
549
560
|
end
|
561
|
+
# Need to figure out a way to handle multiple types
|
562
|
+
ret = @rooms[room_id] = ret.to_space if ret.instance_variable_get :@room_type
|
563
|
+
ret
|
550
564
|
end
|
551
565
|
|
552
566
|
def listen_forever(timeout: 30, bad_sync_timeout: 5, sync_interval: 0, **params)
|
@@ -575,7 +589,7 @@ module MatrixSdk
|
|
575
589
|
private
|
576
590
|
|
577
591
|
def post_authentication(data)
|
578
|
-
@mxid = data[:user_id]
|
592
|
+
@mxid = MXID.new data[:user_id]
|
579
593
|
@api.access_token = data[:access_token]
|
580
594
|
@api.device_id = data[:device_id]
|
581
595
|
@api.homeserver = data[:home_server]
|
@@ -592,35 +606,35 @@ module MatrixSdk
|
|
592
606
|
end
|
593
607
|
|
594
608
|
def handle_sync_response(data)
|
595
|
-
data
|
609
|
+
data.dig(:presence, :events)&.each do |presence_update|
|
596
610
|
fire_presence_event(MatrixEvent.new(self, presence_update))
|
597
611
|
end
|
598
612
|
|
599
|
-
data
|
613
|
+
data.dig(:rooms, :invite)&.each do |room_id, invite|
|
600
614
|
invite[:room_id] = room_id.to_s
|
601
615
|
fire_invite_event(MatrixEvent.new(self, invite), room_id.to_s)
|
602
616
|
end
|
603
617
|
|
604
|
-
data
|
618
|
+
data.dig(:rooms, :leave)&.each do |room_id, left|
|
605
619
|
left[:room_id] = room_id.to_s
|
606
620
|
fire_leave_event(MatrixEvent.new(self, left), room_id.to_s)
|
607
621
|
end
|
608
622
|
|
609
|
-
data
|
623
|
+
data.dig(:rooms, :join)&.each do |room_id, join|
|
610
624
|
room = ensure_room(room_id)
|
611
|
-
room.instance_variable_set '@prev_batch', join
|
625
|
+
room.instance_variable_set '@prev_batch', join.dig(:timeline, :prev_batch)
|
612
626
|
room.instance_variable_set :@members_loaded, true unless sync_filter.fetch(:room, {}).fetch(:state, {}).fetch(:lazy_load_members, false)
|
613
627
|
|
614
|
-
join
|
628
|
+
join.dig(:state, :events)&.each do |event|
|
615
629
|
event[:room_id] = room_id.to_s
|
616
630
|
handle_state(room_id, event)
|
617
631
|
end
|
618
632
|
|
619
|
-
join
|
633
|
+
join.dig(:timeline, :events)&.each do |event|
|
620
634
|
event[:room_id] = room_id.to_s
|
621
635
|
# Avoid sending two identical state events if it's both in state and timeline
|
622
636
|
if event.key?(:state_key)
|
623
|
-
state_event = join.dig(:state, :events)
|
637
|
+
state_event = join.dig(:state, :events)&.find { |ev| ev[:event_id] == event[:event_id] }
|
624
638
|
|
625
639
|
handle_state(room_id, event) unless event == state_event
|
626
640
|
end
|
@@ -629,7 +643,7 @@ module MatrixSdk
|
|
629
643
|
fire_event(MatrixEvent.new(self, event), event[:type])
|
630
644
|
end
|
631
645
|
|
632
|
-
join
|
646
|
+
join.dig(:ephemeral, :events)&.each do |event|
|
633
647
|
event[:room_id] = room_id.to_s
|
634
648
|
room.send :put_ephemeral_event, event
|
635
649
|
|
data/lib/matrix_sdk/mxid.rb
CHANGED
@@ -12,7 +12,7 @@ module MatrixSdk
|
|
12
12
|
|
13
13
|
# TODO: Community-as-a-Room / Profile-as-a-Room, in case they're going for room aliases
|
14
14
|
@sigil = identifier[0]
|
15
|
-
@localpart, @domain, @port = identifier[1..-1].split(':')
|
15
|
+
@localpart, @domain, @port = identifier[1..-1].split(':') # rubocop:disable Style/SlicingWithRange # Keep support for slightly older Rubies
|
16
16
|
@port = @port.to_i if @port
|
17
17
|
|
18
18
|
raise ArgumentError, 'Identifier is not a valid MXID' unless valid?
|
@@ -100,5 +100,45 @@ module MatrixSdk
|
|
100
100
|
def room_alias?
|
101
101
|
type == :room_alias
|
102
102
|
end
|
103
|
+
|
104
|
+
# Converts the MXID to a matrix: URI according to MSC2312
|
105
|
+
# @param event_id [String,MXID] An event ID to append to the URI (only valid for rooms)
|
106
|
+
# @param action [String,Symbol] The action that should be requested
|
107
|
+
# @param via [Array[String]] The list of servers to use for a join
|
108
|
+
# @see https://github.com/matrix-org/matrix-doc/blob/master/proposals/2312-matrix-uri.md
|
109
|
+
def to_uri(event_id: nil, action: nil, via: nil)
|
110
|
+
uri = ''
|
111
|
+
|
112
|
+
case sigil
|
113
|
+
when '@'
|
114
|
+
raise ArgumentError, "can't provide via for user URIs" if via
|
115
|
+
raise ArgumentError, "can't provide event_id for user URIs" if event_id
|
116
|
+
|
117
|
+
uri += 'u'
|
118
|
+
when '#'
|
119
|
+
uri += 'r'
|
120
|
+
when '!'
|
121
|
+
uri += 'roomid'
|
122
|
+
else
|
123
|
+
raise ArgumentError, "this MXID can't be converted to a URI"
|
124
|
+
end
|
125
|
+
|
126
|
+
uri = "matrix:#{uri}/#{localpart}#{homeserver_suffix}"
|
127
|
+
|
128
|
+
uri += "/e/#{event_id.to_s.delete_prefix('$')}" if event_id
|
129
|
+
query = []
|
130
|
+
query << "action=#{action}" if action
|
131
|
+
[via].flatten.compact.each { |v| query << "via=#{v}" }
|
132
|
+
|
133
|
+
uri += "?#{query.join('&')}" unless query.empty?
|
134
|
+
|
135
|
+
URI(uri)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Check if two MXIDs are equal
|
139
|
+
# @return [Boolean]
|
140
|
+
def ==(other)
|
141
|
+
to_s == other.to_s
|
142
|
+
end
|
103
143
|
end
|
104
144
|
end
|