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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb2cd8492e977a8c63d80d3a250841111f872b45ef48a06ac610dd6c871c5704
4
- data.tar.gz: b5932dc63938f86a4bec522f2d5736871f3a5461ad5d25581c2ba1cd79b20ab2
3
+ metadata.gz: 50821866efab2cbd39daf23bc9ff3bd7864bca5638f76c766a8ba26ab91c7423
4
+ data.tar.gz: 2601cf5141e824b322f5310e09b72d42059162e1ac87da11243167d2604f1801
5
5
  SHA512:
6
- metadata.gz: 0ed57a7d4b3296a15c244c88ca88777d6ed3f9b67a6c4cdaae017f56c6e15cf94ba0781c6831a70f7adf9c55605073c84c451b0c1b0b8bffe323015330294f19
7
- data.tar.gz: f77aaa05a45917b80aa296b7ff73f37d60ba5fb36f29a3e753cec1a730681a428c33ccdae9f4c26648952d6a7b05f9636e7d7ffbbe8757d23d2c435378d3466b
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
@@ -8,6 +8,8 @@ Live YARD documentation can be found at; https://ruby-sdk.ananace.dev
8
8
 
9
9
  ## Example usage
10
10
 
11
+ For more fully-featured examples, check the [examples](examples/) folder.
12
+
11
13
  ```ruby
12
14
  # Raw API usage
13
15
  require 'matrix_sdk'
@@ -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 [Hash] :well_known The .well-known object that the server was discovered through, should not be set manually
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, 60)
61
- @read_timeout = params.fetch(:read_timeout, 240)
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
- begin
300
+ response = duration = nil
301
+
302
+ loc_http = http
303
+ perform_request = proc do
285
304
  dur_start = Time.now
286
- response = http.request req_obj
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
- @http ||= if proxy_uri
396
- Net::HTTP.new(host, port, proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
397
- else
398
- Net::HTTP.new(host, port)
399
- end
400
-
401
- @http.open_timeout = open_timeout
402
- @http.read_timeout = read_timeout
403
- @http.use_ssl = homeserver.scheme == 'https'
404
- @http.verify_mode = validate_certificate ? ::OpenSSL::SSL::VERIFY_PEER : ::OpenSSL::SSL::VERIFY_NONE
405
- @http.start
406
- @http
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
@@ -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 ||= begin
106
- MXID.new api.whoami?[:user_id] if api&.access_token
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
- Hash[api.get_account_data(mxid, 'm.direct').map do |mxid, rooms|
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: false)
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
- return @rooms.values.find { |r| r.canonical_alias == room_id_or_alias.to_s } if only_canonical
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::MATRIX] A Matrix content (mxc://) URL pointing to the uploaded data
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
- begin
522
- break api.sync **extra_params
523
- rescue MatrixSdk::MatrixTimeoutError => e
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[:presence][:events].each do |presence_update|
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[:rooms][:invite].each do |room_id, invite|
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[:rooms][:leave].each do |room_id, left|
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[:rooms][:join].each do |room_id, join|
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[:timeline][:prev_batch]
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[:state][:events].each do |event|
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[:timeline][:events].each do |event|
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).find { |ev| ev[:event_id] == event[:event_id] }
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[:ephemeral][:events].each do |event|
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
 
@@ -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