matrix_sdk 2.3.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
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