matrix_sdk 2.5.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: 24a67aeb6af66f75480104c5c0dc44987e2fe54163ed96047da58bf091476752
4
- data.tar.gz: e5935a94b430a1b195b55d748415cc0ad95ea649e32f5e97045cc82726dd2bd3
3
+ metadata.gz: 50821866efab2cbd39daf23bc9ff3bd7864bca5638f76c766a8ba26ab91c7423
4
+ data.tar.gz: 2601cf5141e824b322f5310e09b72d42059162e1ac87da11243167d2604f1801
5
5
  SHA512:
6
- metadata.gz: 1ea88ea4c76b573dad7b03aeba49e91b8f7c01040118c38032b49ca00cfbe687bd931c4e17387545e27df529f6ae06fc835ee964b5e79489fb0c5cf84186b35b
7
- data.tar.gz: e2a6a5cd78e7629ffb2cc4af5ec9c9154f2f63db570479ebb2c6fa9aff13b7b231bea877d56694ae799019698af8d4a87394f88fa162dcb172dbbd1ede09fc8f
6
+ metadata.gz: 7bb45dfcb6696b75ed0be95b92fbb3f569b258d4d0456a3cd8ea16fd22d5e3d5050607e6229bfa0e32b84fc0248e554db8f93d48cd970b2ff95475cf0bcfba85
7
+ data.tar.gz: 58d8524e9d83ef570ca19a41f28758fc129c1a3988ea3522a994e58eaa93c4807850b63a2171423dd69a948738e69468542d56d882ecf98c60e6f8607928b6d1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
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
+
1
10
  ## 2.5.0 - 2022-01-14
2
11
 
3
12
  - Adds preliminary support for the Matrix v1.1 `client/v3` API
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)
@@ -64,7 +67,8 @@ module MatrixSdk
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
- @http_lock = Mutex.new
70
+
71
+ self.threadsafe = params.fetch(:threadsafe, :multithread)
68
72
 
69
73
  ([params.fetch(:protocols, [:CS])].flatten - protocols).each do |proto|
70
74
  self.class.include MatrixSdk::Protocols.const_get(proto)
@@ -244,6 +248,17 @@ module MatrixSdk
244
248
  @proxy_uri = proxy_uri
245
249
  end
246
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
+
247
262
  # Perform a raw Matrix API request
248
263
  #
249
264
  # @example Simple API query
@@ -284,15 +299,23 @@ module MatrixSdk
284
299
  print_http(req_obj, id: req_id)
285
300
  response = duration = nil
286
301
 
287
- @http_lock.synchronize do
302
+ loc_http = http
303
+ perform_request = proc do
288
304
  dur_start = Time.now
289
- response = http.request req_obj
305
+ response = loc_http.request req_obj
290
306
  dur_end = Time.now
291
307
  duration = dur_end - dur_start
292
308
  rescue EOFError
293
309
  logger.error 'Socket closed unexpectedly'
294
310
  raise
295
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
296
319
  print_http(response, duration: duration, id: req_id)
297
320
 
298
321
  begin
@@ -395,18 +418,26 @@ module MatrixSdk
395
418
 
396
419
  host = (@connection_address || homeserver.host)
397
420
  port = (@connection_port || homeserver.port)
398
- @http ||= if proxy_uri
399
- Net::HTTP.new(host, port, proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
400
- else
401
- Net::HTTP.new(host, port)
402
- end
403
-
404
- @http.open_timeout = open_timeout if open_timeout
405
- @http.read_timeout = read_timeout if read_timeout
406
- @http.use_ssl = homeserver.scheme == 'https'
407
- @http.verify_mode = validate_certificate ? ::OpenSSL::SSL::VERIFY_PEER : ::OpenSSL::SSL::VERIFY_NONE
408
- @http.start
409
- @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
410
441
  end
411
442
  end
412
443
  end
@@ -395,15 +395,16 @@ module MatrixSdk
395
395
  # @param only_canonical [Boolean] Only match alias against the canonical alias
396
396
  # @return [Room] The found room
397
397
  # @return [nil] If no room was found
398
- def find_room(room_id_or_alias, only_canonical: false)
398
+ def find_room(room_id_or_alias, only_canonical: true)
399
399
  room_id_or_alias = MXID.new(room_id_or_alias.to_s) unless room_id_or_alias.is_a? MXID
400
400
  raise ArgumentError, 'Must be a room id or alias' unless room_id_or_alias.room?
401
401
 
402
402
  return @rooms.fetch(room_id_or_alias.to_s, nil) if room_id_or_alias.room_id?
403
403
 
404
- 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
405
406
 
406
- @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 }
407
408
  end
408
409
 
409
410
  # Get a User instance from a MXID
@@ -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..].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?
@@ -203,7 +203,7 @@ module MatrixSdk::Protocols::CS
203
203
  data[:device_id] = device_id if device_id
204
204
 
205
205
  request(:post, client_api_latest, '/login', body: data, query: query).tap do |resp|
206
- @access_token = resp.token if resp.key?(:token) && options[:store_token]
206
+ @access_token = resp.access_token if resp.key?(:access_token) && options[:store_token]
207
207
  @device_id = resp.device_id if resp.key?(:device_id) && options[:store_device_id]
208
208
  end
209
209
  end
@@ -912,31 +912,23 @@ module MatrixSdk::Protocols::CS
912
912
  send_state_event(room_id, 'm.room.avatar', content, **params)
913
913
  end
914
914
 
915
- # Gets a list of current aliases of a room
915
+ # Gets a list of currently known aliases of a room
916
916
  #
917
917
  # @param [MXID,String] room_id The room ID to look up
918
- # @param [Hash] params Extra options to provide to the request, see #get_room_state
919
918
  # @return [Response] A response hash with the array :aliases
920
- # @raise [MatrixNotFoundError] Raised if no aliases has been set on the room by the specified HS
921
- # @see get_room_state
922
- # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-avatar
919
+ # @raise [MatrixForbiddenError] Raised if the user doesn't have the right to read aliases
920
+ # @see https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3roomsroomidaliases
923
921
  # The Matrix Spec, for more information about the event and data
924
922
  # @example Looking up aliases for a room
925
923
  # api.get_room_aliases('!QtykxKocfZaZOUrTwp:matrix.org')
926
- # # MatrixSdk::MatrixNotFoundError: HTTP 404 (M_NOT_FOUND): Event not found.
927
- # api.get_room_aliases('!QtykxKocfZaZOUrTwp:matrix.org', key: 'matrix.org')
928
924
  # # => {:aliases=>["#matrix:matrix.org"]}
929
- # api.get_room_aliases('!QtykxKocfZaZOUrTwp:matrix.org', key: 'kittenface.studio')
930
- # # => {:aliases=>["#worlddominationhq:kittenface.studio"]}
931
- # @example A way to find all aliases for a room
932
- # api.get_room_state('!mjbDjyNsRXndKLkHIe:matrix.org')
933
- # .select { |ch| ch[:type] == 'm.room.aliases' }
934
- # .map { |ch| ch[:content][:aliases] }
935
- # .flatten
936
- # .compact
937
- # # => ["#synapse:im.kabi.tk", "#synapse:matrix.org", "#synapse-community:matrix.org", "#synapse-ops:matrix.org", "#synops:matrix.org", ...
938
925
  def get_room_aliases(room_id, **params)
939
- get_room_state(room_id, 'm.room.aliases', **params)
926
+ query = {}
927
+ query[:user_id] = params.delete(:user_id) if protocol?(:AS) && params.key?(:user_id)
928
+
929
+ room_id = ERB::Util.url_encode room_id.to_s
930
+
931
+ request(:get, client_api_latest, "/rooms/#{room_id}/aliases", query: query)
940
932
  end
941
933
 
942
934
  # Gets a list of pinned events in a room
@@ -60,9 +60,9 @@ module MatrixSdk::Protocols::MSC
60
60
  # rubocop:disable Metrics/BlockLength
61
61
  thread = Thread.new(cancellation_token) do |ctx|
62
62
  print_http(req)
63
- @http_lock.lock
63
+ @http_lock&.lock
64
64
  http.request req do |response|
65
- @http_lock.unlock
65
+ @http_lock&.unlock
66
66
  break unless ctx[:run]
67
67
 
68
68
  print_http(response, body: false)
@@ -136,10 +136,11 @@ module MatrixSdk::Protocols::MSC
136
136
  break
137
137
  end
138
138
  end
139
+
139
140
  break unless ctx[:run]
140
141
  end
141
- rescue StandardError
142
- @http_lock.unlock if @http_lock.owned?
142
+ ensure
143
+ @http_lock.unlock if @http_lock&.owned?
143
144
  end
144
145
  # rubocop:enable Metrics/BlockLength
145
146
 
@@ -31,9 +31,11 @@ module MatrixSdk
31
31
  ignore_inspect :client, :events, :prev_batch, :logger, :tinycache_adapter
32
32
 
33
33
  # Requires heavy lookups, so they're cached for an hour
34
- cached :joined_members, :aliases, cache_level: :all, expires_in: 60 * 60
35
- # Only cache unfiltered requests for all members
36
- cached :all_members, unless: proc { |args| args.any? }, cache_level: :all, expires_in: 3600
34
+ cached :joined_members, cache_level: :all, expires_in: 60 * 60
35
+
36
+ # Only cache unfiltered requests for aliases and members
37
+ cached :aliases, unless: proc { |args| args.any? }, cache_level: :all, expires_in: 60 * 60
38
+ cached :all_members, unless: proc { |args| args.any? }, cache_level: :all, expires_in: 60 * 60
37
39
 
38
40
  # Much simpler to look up, and lighter data-wise, so the cache is wider
39
41
  cached :canonical_alias, :name, :avatar_url, :topic, :guest_access, :join_rule, :power_levels, cache_level: :some, expires_in: 15 * 60
@@ -272,15 +274,14 @@ module MatrixSdk
272
274
 
273
275
  # Gets the room aliases
274
276
  #
277
+ # @param canonical_only [Boolean] Should the list of aliases only contain the canonical ones
275
278
  # @return [Array[String]] The assigned room aliases
276
- def aliases
277
- client.api.get_room_aliases(id).aliases
278
- rescue MatrixNotFoundError
279
- data = client.api.get_room_state_all(id)
280
- data.select { |chunk| chunk[:type] == 'm.room.aliases' && !chunk.dig(*%i[content aliases]).nil? }
281
- .map { |chunk| chunk.dig(*%i[content aliases]) }
282
- .flatten
283
- .compact
279
+ def aliases(canonical_only: true)
280
+ canonical = client.api.get_room_state(id, 'm.room.canonical_alias') rescue {}
281
+ aliases = ([canonical[:alias]].compact + (canonical[:alt_aliases] || [])).uniq.sort
282
+ return aliases if canonical_only
283
+
284
+ (aliases + client.api.get_room_aliases(id).aliases).uniq.sort
284
285
  end
285
286
 
286
287
  #
@@ -904,16 +905,8 @@ module MatrixSdk
904
905
 
905
906
  data = tinycache_adapter.read(:aliases) || []
906
907
  data << canonical_alias
907
- tinycache_adapter.write(:aliases, data)
908
- end
909
-
910
- def handle_room_aliases(event)
911
- tinycache_adapter.write(:aliases, []) unless tinycache_adapter.exist? :aliases
912
-
913
- aliases = tinycache_adapter.read(:aliases) || []
914
- aliases.concat(event.dig(*%i[content aliases]))
915
-
916
- tinycache_adapter.write(:aliases, aliases)
908
+ data += event.dig(*%i[content alt_aliases]) || []
909
+ tinycache_adapter.write(:aliases, data.uniq.sort)
917
910
  end
918
911
 
919
912
  def room_handlers?
@@ -929,7 +922,6 @@ module MatrixSdk
929
922
  end
930
923
 
931
924
  INTERNAL_HANDLERS = {
932
- 'm.room.aliases' => :handle_room_aliases,
933
925
  'm.room.canonical_alias' => :handle_room_canonical_alias,
934
926
  'm.room.guest_access' => :handle_room_guest_access,
935
927
  'm.room.join_rules' => :handle_room_join_rules,
@@ -6,12 +6,12 @@ module MatrixSdk::Rooms
6
6
 
7
7
  def tree(suggested_only: nil, max_rooms: nil)
8
8
  begin
9
- data = client.api.request :get, :client_unstable, "/org.matrix.msc2946/rooms/#{id}/spaces", query: {
9
+ data = client.api.request :get, :client_r0, "/rooms/#{id}/spaces", query: {
10
10
  suggested_only: suggested_only,
11
11
  max_rooms_per_space: max_rooms
12
12
  }.compact
13
13
  rescue MatrixRequestError
14
- data = client.api.request :get, :client_r0, "/rooms/#{id}/spaces", query: {
14
+ data = client.api.request :get, :client_unstable, "/org.matrix.msc2946/rooms/#{id}/spaces", query: {
15
15
  suggested_only: suggested_only,
16
16
  max_rooms_per_space: max_rooms
17
17
  }.compact
@@ -132,7 +132,7 @@ module MatrixSdk
132
132
 
133
133
  # Returns all the current device keys for the user, retrieving them if necessary
134
134
  def device_keys
135
- @device_keys ||= client.api.keys_query(device_keys: { id => [] }).yield_self do |resp|
135
+ @device_keys ||= client.api.keys_query(device_keys: { id => [] }).yield_self do |resp| # rubocop:disable Style/ObjectThen # Keep Ruby 2.5 support a little longer
136
136
  resp.dig(:device_keys, id.to_sym)
137
137
  end
138
138
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pp'
4
+
3
5
  unless Object.respond_to? :yield_self
4
6
  class Object
5
7
  def yield_self
@@ -51,13 +53,20 @@ module MatrixSdk
51
53
 
52
54
  def ignore_inspect(*symbols)
53
55
  class_eval %*
54
- def inspect
55
- reentrant = caller_locations.any? { |l| l.absolute_path == __FILE__ && l.label == 'inspect' }
56
- "\\\#<\#{self.class} \#{instance_variables
56
+ include PP::ObjectMixin
57
+
58
+ def pretty_print_instance_variables
59
+ instance_variables
57
60
  .reject { |f| %i[#{symbols.map { |s| "@#{s}" }.join ' '}].include? f }
58
- .map { |f| "\#{f}=\#{reentrant ? instance_variable_get(f) : instance_variable_get(f).inspect}" }.join " " }}>"
61
+ .sort
59
62
  end
60
- *, __FILE__, __LINE__ - 7
63
+
64
+ def pretty_print(pp)
65
+ pp.pp_object(self)
66
+ end
67
+
68
+ alias inspect pretty_print_inspect
69
+ *, __FILE__, __LINE__ - 14
61
70
  end
62
71
  end
63
72
 
@@ -75,13 +75,25 @@ module MatrixSdk::Util
75
75
 
76
76
  define_method(method_names[:with_cache]) do |*args|
77
77
  tinycache_adapter.fetch(__send__(method_names[:cache_key], *args), expires_in: expires_in) do
78
- __send__(method_names[:without_cache], *args)
78
+ named = args.delete_at(-1) if args.last.is_a? Hash
79
+
80
+ if named
81
+ __send__(method_names[:without_cache], *args, **named)
82
+ else
83
+ __send__(method_names[:without_cache], *args)
84
+ end
79
85
  end
80
86
  end
81
87
 
82
88
  define_method(method_names[:without_cache]) do |*args|
83
89
  orig = method(method_name).super_method
84
- orig.call(*args)
90
+ named = args.delete_at(-1) if args.last.is_a? Hash
91
+
92
+ if named
93
+ orig.call(*args, **named)
94
+ else
95
+ orig.call(*args)
96
+ end
85
97
  end
86
98
 
87
99
  define_method(method_names[:clear_cache]) do |*args|
@@ -7,14 +7,20 @@ module URI
7
7
  class MXC < Generic
8
8
  def full_path
9
9
  select(:host, :port, :path, :query, :fragment)
10
- .reject(&:nil?)
10
+ .compact
11
11
  .join
12
12
  end
13
13
  end
14
14
 
15
- @@schemes['MXC'] = MXC
15
+ # TODO: Use +register_scheme+ on Ruby >=3.1 and fall back to old behavior
16
+ # for older Rubies. May be removed at EOL of Ruby 3.0.
17
+ if respond_to? :register_scheme
18
+ register_scheme 'MXC', MXC
19
+ else
20
+ @@schemes['MXC'] = MXC
21
+ end
16
22
 
17
- unless @@schemes.key? 'MATRIX'
23
+ unless scheme_list.key? 'MATRIX'
18
24
  # A matrix: URI according to MSC2312
19
25
  class MATRIX < Generic
20
26
  attr_reader :authority, :action, :mxid, :mxid2, :via
@@ -84,6 +90,12 @@ module URI
84
90
  end
85
91
  end
86
92
 
87
- @@schemes['MATRIX'] = MATRIX
93
+ # TODO: Use +register_scheme+ on Ruby >=3.1 and fall back to old behavior
94
+ # for older Rubies. May be removed at EOL of Ruby 3.0.
95
+ if respond_to? :register_scheme
96
+ register_scheme 'MATRIX', MATRIX
97
+ else
98
+ @@schemes['MATRIX'] = MATRIX
99
+ end
88
100
  end
89
101
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MatrixSdk
4
- VERSION = '2.5.0'
4
+ VERSION = '2.6.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matrix_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Olofsson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-14 00:00:00.000000000 Z
11
+ date: 2022-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mocha
@@ -132,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
132
  - !ruby/object:Gem::Version
133
133
  version: '0'
134
134
  requirements: []
135
- rubygems_version: 3.2.22
135
+ rubygems_version: 3.3.8
136
136
  signing_key:
137
137
  specification_version: 4
138
138
  summary: SDK for applications using the Matrix protocol