matrix_sdk 1.0.1 → 1.1.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: d011936f4351fdcdba80a37592a243e30632355c1b85dcb03afe59b0684775ea
4
- data.tar.gz: e5e569f87cb325a18bd0996d542bc7baca25e85aa42df1dfafdec05f9bc28dea
3
+ metadata.gz: 5b922104b5ab91c432c502117ed7f8f6a4c25121a8c407658427c28a9a9f47a3
4
+ data.tar.gz: 99bc5b83c327b3ac326ce4515f77bd9801c260797036daaf2cb4192e08e74ec1
5
5
  SHA512:
6
- metadata.gz: 6f6881da6dc7b9e9bac6ac59386298f4e09595c6e9445a970b64f77bee3140220e5a0884575e91cc9d88198edebaf1e8074c1eeadb7a301c6d484617c904ed77
7
- data.tar.gz: 8f0c02e302015dac5f9ef70da92bd073b17c31d3e8e43eb4403bb4df6c0e769f106a3af52733463651deb74ccd0437ba23d0c02138534db8767fb950c31212b6
6
+ metadata.gz: cd9020a5792c251aca178339ecd836495b07e414e7121711be4af78aef5fdd45d2c106fb59f9bead22767825dd694d0a43429c3db7c394bd90d9b48ddb9517c2
7
+ data.tar.gz: d0a15efe764f0b6b9d0fd0ec11f50385da455a1fa3e3ad92f092cfd728f377726f3d84e4d961554519b724e6dc6e402113767050281e55bcee6e9328604091d2
@@ -1,3 +1,15 @@
1
+ ## v1.1.0 - 2019-06-04
2
+
3
+ - The create_room method in the client abstraction now automatically stores the created room
4
+ - Adds more CS API endpoints, exposed as #get_joined_rooms, #get_public_rooms, and #username_available?
5
+ - Adds a method to the client abstraction to reload all joined rooms
6
+ - Adds a method to the client abstraction to get a list of all public rooms
7
+ - Adds avatar tracking to rooms in the client abstraction
8
+ - Adds lazy loading of join rules and guest access for rooms in the client abstraction
9
+ - Adds granular error classes like MatrixSdk::MatrixNotFoundError to make error handling easier
10
+ - Improves the CS API endpoint for room state retrieval
11
+ - Fixes an issue in the client abstraction where it would fail to load aliases if multiple HSes have applied aliases to a room
12
+
1
13
  ## v1.0.1 - 2019-05-24
2
14
 
3
15
  - Fixes an error in the room creation code
data/README.md CHANGED
@@ -27,8 +27,8 @@ api.request :get, :federation_v1, '/version'
27
27
  # Client wrapper
28
28
  require 'matrix_sdk'
29
29
 
30
- client = MatrixSdk::Client.new 'https://matrix.org'
31
- client.login user: 'example', password: 'notarealpass' # no_sync: true
30
+ client = MatrixSdk::Client.new 'https://example.com'
31
+ client.login 'username', 'notarealpass' #, no_sync: true
32
32
 
33
33
  client.rooms.count
34
34
  # => 5
@@ -40,6 +40,22 @@ hq.send_text "This is an example message - don't actually do this ;)"
40
40
  # => {:event_id=>"$123457890abcdef:matrix.org"}
41
41
  ```
42
42
 
43
+ ```ruby
44
+ # Client wrapper
45
+ require 'matrix_sdk'
46
+
47
+ client = MatrixSdk::Client.new 'https://example.com'
48
+ client.api.access_token = 'thisisnotarealtoken'
49
+
50
+ # Doesn't automatically trigger a sync when setting the token directly
51
+ client.rooms.count
52
+ # => 0
53
+
54
+ client.sync
55
+ client.rooms.count
56
+ # => 5
57
+ ```
58
+
43
59
  ## Contributing
44
60
 
45
61
  Bug reports and pull requests are welcome on GitHub at https://github.com/ananace/ruby-matrix-sdk
@@ -41,7 +41,7 @@ module MatrixSdk
41
41
  # @option params [Hash] :global_headers Additional headers to set for all requests
42
42
  # @option params [Boolean] :skip_login Should the API skip logging in if the HS URL contains user information
43
43
  # @option params [Hash] :well_known The .well-known object that the server was discovered through, should not be set manually
44
- def initialize(homeserver, params = {})
44
+ def initialize(homeserver, **params)
45
45
  @homeserver = homeserver
46
46
  raise ArgumentError, 'Homeserver URL must be String or URI' unless @homeserver.is_a?(String) || @homeserver.is_a?(URI)
47
47
 
@@ -178,7 +178,7 @@ module MatrixSdk
178
178
  @homeserver = hs_info
179
179
  end
180
180
 
181
- def request(method, api, path, options = {})
181
+ def request(method, api, path, **options)
182
182
  url = homeserver.dup.tap do |u|
183
183
  u.path = api_to_path(api) + path
184
184
  u.query = [u.query, URI.encode_www_form(options.fetch(:query))].flatten.compact.join('&') if options[:query]
@@ -212,7 +212,7 @@ module MatrixSdk
212
212
  data = JSON.parse(response.body, symbolize_names: true) rescue nil
213
213
 
214
214
  if response.is_a? Net::HTTPTooManyRequests
215
- raise MatrixRequestError.new(data, response.code) unless autoretry
215
+ raise MatrixRequestError.new_by_code(data, response.code) unless autoretry
216
216
 
217
217
  failures += 1
218
218
  waittime = data[:retry_after_ms] || data[:error][:retry_after_ms] || @backoff_time
@@ -221,7 +221,7 @@ module MatrixSdk
221
221
  end
222
222
 
223
223
  return MatrixSdk::Response.new self, data if response.is_a? Net::HTTPSuccess
224
- raise MatrixRequestError.new(data, response.code) if data
224
+ raise MatrixRequestError.new_by_code(data, response.code) if data
225
225
 
226
226
  raise MatrixConnectionError.class_by_code(response.code), response
227
227
  end
@@ -89,10 +89,52 @@ module MatrixSdk
89
89
  alias user_id mxid
90
90
  alias user_id= mxid=
91
91
 
92
+ def public_rooms
93
+ rooms = []
94
+ since = nil
95
+ loop do
96
+ data = api.get_public_rooms since: since
97
+
98
+ data[:chunk].each do |chunk|
99
+ rooms << Room.new(self, chunk[:room_id],
100
+ name: chunk[:name], topic: chunk[:topic], aliases: chunk[:aliases],
101
+ canonical_alias: chunk[:canonical_alias], avatar_url: chunk[:avatar_url],
102
+ join_rule: :public, world_readable: chunk[:world_readable]).tap do |r|
103
+ r.instance_variable_set :@guest_access, chunk[:guest_can_join] ? :can_join : :forbidden
104
+ end
105
+ end
106
+
107
+ break if data[:next_batch].nil?
108
+
109
+ since = data.next_batch
110
+ end
111
+
112
+ rooms
113
+ end
114
+
92
115
  def rooms
116
+ if @rooms.empty? && cache != :none
117
+ api.get_joined_rooms.joined_rooms.each do |id|
118
+ ensure_room(id)
119
+ end
120
+ end
121
+
93
122
  @rooms.values
94
123
  end
95
124
 
125
+ def reload_rooms!
126
+ return true if cache == :none
127
+
128
+ @rooms.clear
129
+ api.get_joined_rooms.joined_rooms.each do |id|
130
+ r = ensure_room(id)
131
+ r.reload!
132
+ end
133
+
134
+ true
135
+ end
136
+ alias refresh_rooms! reload_rooms!
137
+
96
138
  def register_as_guest
97
139
  data = api.register(kind: :guest)
98
140
  post_authentication(data)
@@ -158,8 +200,9 @@ module MatrixSdk
158
200
  !(mxid.nil? || @api.access_token.nil?)
159
201
  end
160
202
 
161
- def create_room(room_alias = nil, params = {})
162
- api.create_room(params.merge(room_alias: room_alias))
203
+ def create_room(room_alias = nil, **params)
204
+ data = api.create_room(params.merge(room_alias: room_alias))
205
+ ensure_room(data.room_id)
163
206
  end
164
207
 
165
208
  def join_room(room_id_or_alias)
@@ -197,7 +240,7 @@ module MatrixSdk
197
240
  raise MatrixUnexpectedResponseError, 'Upload succeeded, but no media URI returned'
198
241
  end
199
242
 
200
- def start_listener_thread(params = {})
243
+ def start_listener_thread(**params)
201
244
  @should_listen = true
202
245
  thread = Thread.new { listen_forever(params) }
203
246
  @sync_thread = thread
@@ -10,6 +10,22 @@ module MatrixSdk
10
10
  attr_reader :code, :httpstatus, :message
11
11
  alias error message
12
12
 
13
+ def self.class_by_code(code)
14
+ code = code.to_i
15
+
16
+ return MatrixNotAuthorizedError if code == 401
17
+ return MatrixForbiddenError if code == 403
18
+ return MatrixNotFoundError if code == 404
19
+ return MatrixConflictError if code == 409
20
+ return MatrixTooManyRequestsError if code == 429
21
+
22
+ MatrixRequestError
23
+ end
24
+
25
+ def self.new_by_code(data, code)
26
+ class_by_code(code).new(data, code)
27
+ end
28
+
13
29
  def initialize(error, status)
14
30
  @code = error[:errcode]
15
31
  @httpstatus = status
@@ -23,6 +39,12 @@ module MatrixSdk
23
39
  end
24
40
  end
25
41
 
42
+ class MatrixNotAuthorizedError < MatrixRequestError; end
43
+ class MatrixForbiddenError < MatrixRequestError; end
44
+ class MatrixNotFoundError < MatrixRequestError; end
45
+ class MatrixConflictError < MatrixRequestError; end
46
+ class MatrixTooManyRequestsError < MatrixRequestError; end
47
+
26
48
  # An error raised when errors occur in the connection layer
27
49
  class MatrixConnectionError < MatrixError
28
50
  def self.class_by_code(code)
@@ -82,6 +82,19 @@ module MatrixSdk::Protocols::CS
82
82
  end
83
83
  end
84
84
 
85
+ # Checks if a given username is available and valid for registering
86
+ #
87
+ # @example Verifying a username
88
+ # api.username_available?('example')
89
+ # # => { available: true }
90
+ #
91
+ # @param username [String] The username to check
92
+ # @return [Response]
93
+ # @see https://matrix.org/docs/spec/client_server/latest.html#get-matrix-client-r0-register-available
94
+ def username_available?(username)
95
+ request(:get, :client_r0, '/register/available', query: { username: username })
96
+ end
97
+
85
98
  # Logs in using the client API /login endpoint, and optionally stores the resulting access for API usage
86
99
  #
87
100
  # @example Logging in with username and password
@@ -138,6 +151,50 @@ module MatrixSdk::Protocols::CS
138
151
  request(:post, :client_r0, '/logout', query: query)
139
152
  end
140
153
 
154
+ # Gets the list of rooms joined by the current user
155
+ #
156
+ # @return [Response] An array of room IDs under the key :joined_rooms
157
+ # @see https://matrix.org/docs/spec/client_server/latest.html#get-matrix-client-r0-joined-rooms
158
+ def get_joined_rooms(**params)
159
+ query = {}
160
+ query[:user_id] = params.delete(:user_id) if protocol?(:AS) && params.key?(:user_id)
161
+
162
+ request(:get, :client_r0, '/joined_rooms', query: query)
163
+ end
164
+
165
+ # Gets the list of public rooms on a Matrix server
166
+ #
167
+ # @param limit [Integer] Limits the number of results returned
168
+ # @param since [String] A pagination token received from an earlier call
169
+ # @param server [String] The Matrix server to request public rooms from
170
+ # @return [Response] An array of public rooms in the :chunk key, along with
171
+ # :next_batch, :prev_batch, and :total_room_count_estimate
172
+ # for pagination
173
+ # @see https://matrix.org/docs/spec/client_server/latest.html#get-matrix-client-r0-publicrooms
174
+ # https://matrix.org/docs/spec/client_server/latest.html#post-matrix-client-r0-publicrooms
175
+ def get_public_rooms(server: nil, **params)
176
+ query = {
177
+ server: server
178
+ }.compact
179
+ body = nil
180
+ method = :get
181
+
182
+ if !params[:filter].nil? || !params[:include_all_networks].nil? || !params[:third_party_instance_id].nil?
183
+ body = {
184
+ limit: params[:limit],
185
+ since: params[:since],
186
+ filter: params[:filter],
187
+ include_all_networks: params[:include_all_networks],
188
+ third_party_instance_id: params[:third_party_instance_id]
189
+ }.merge(params).compact
190
+ method = :post
191
+ else
192
+ query = query.merge(params).compact
193
+ end
194
+
195
+ request(method, :client_r0, '/publicRooms', query: query, body: body)
196
+ end
197
+
141
198
  # Creates a new room
142
199
  # @param params [Hash] The room creation details
143
200
  # @option params [Symbol] :visibility (:public) The room visibility
@@ -418,14 +475,15 @@ module MatrixSdk::Protocols::CS
418
475
  # @return [Response] A response hash with the contents of the state event
419
476
  # @see https://matrix.org/docs/spec/client_server/r0.3.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype
420
477
  # The Matrix Spec, for more information about the call and response
421
- def get_room_state(room_id, state_type, **params)
478
+ def get_room_state(room_id, state_type = nil, key: nil, **params)
422
479
  query = {}
423
480
  query[:user_id] = params.delete(:user_id) if protocol?(:AS) && params.key?(:user_id)
424
481
 
425
482
  room_id = ERB::Util.url_encode room_id.to_s
426
483
  state_type = ERB::Util.url_encode state_type.to_s
484
+ key = ERB::Util.url_encode key.to_s
427
485
 
428
- request(:get, :client_r0, "/rooms/#{room_id}/state/#{state_type}", query: query)
486
+ request(:get, :client_r0, "/rooms/#{room_id}/state#{state_type.empty? ? nil : "/#{state_type}"}#{key.empty? ? nil : "/#{key}"}", query: query)
429
487
  end
430
488
 
431
489
  # Gets the display name of a room
@@ -707,6 +765,15 @@ module MatrixSdk::Protocols::CS
707
765
  request(:put, :client_r0, "/profile/#{user_id}/avatar_url", body: content, query: query)
708
766
  end
709
767
 
768
+ def get_profile(user_id, **params)
769
+ query = {}
770
+ query[:user_id] = params.delete(:user_id) if protocol?(:AS) && params.key?(:user_id)
771
+
772
+ user_id = ERB::Util.url_encode user_id.to_s
773
+
774
+ request(:get, :client_r0, "/profile/#{user_id}", query: query)
775
+ end
776
+
710
777
  def get_download_url(mxcurl, **_params)
711
778
  mxcurl = URI.parse(mxcurl.to_s) unless mxcurl.is_a? URI
712
779
  raise 'Not a mxc:// URL' unless mxcurl.is_a? URI::MATRIX
@@ -39,7 +39,7 @@ module MatrixSdk
39
39
  # @return [Array(Object)] the last +event_history_limit+ events to arrive in the room
40
40
  # @see https://matrix.org/docs/spec/client_server/r0.3.0.html#get-matrix-client-r0-sync
41
41
  # The timeline events are what will end up in here
42
- attr_reader :id, :client, :name, :topic, :aliases, :join_rule, :guest_access, :members, :events
42
+ attr_reader :id, :client, :name, :topic, :aliases, :members, :events
43
43
 
44
44
  # @!attribute [r] on_event
45
45
  # @return [EventHandlerArray] The list of event handlers for all events
@@ -68,10 +68,12 @@ module MatrixSdk
68
68
  @aliases = []
69
69
  @join_rule = nil
70
70
  @guest_access = nil
71
+ @world_readable = nil
71
72
  @members = []
72
73
  @events = []
73
74
  @members_loaded = false
74
75
  @event_history_limit = 10
76
+ @avatar_url = nil
75
77
 
76
78
  @prev_batch = nil
77
79
 
@@ -123,6 +125,22 @@ module MatrixSdk
123
125
  members
124
126
  end
125
127
 
128
+ # Gets the avatar url of the room - if any
129
+ def avatar_url
130
+ @avatar_url ||= client.api.get_room_state(id, 'm.room.avatar').url
131
+ rescue MatrixNotFoundError
132
+ # No avatar has been set
133
+ nil
134
+ end
135
+
136
+ def guest_access
137
+ @guest_access ||= client.api.get_room_state(id, 'm.room.guest_access').guest_access.to_sym
138
+ end
139
+
140
+ def join_rule
141
+ @join_rule ||= client.api.get_room_state(id, 'm.room.join_rules').join_rule.to_sym
142
+ end
143
+
126
144
  # Checks if +guest_access+ is set to +:can_join+
127
145
  def guest_access?
128
146
  guest_access == :can_join
@@ -328,20 +346,19 @@ module MatrixSdk
328
346
  end
329
347
 
330
348
  # Changes the room-specific user profile
331
- # @param params [Hash] the user profile changes to apply
332
- # @option params [String] :display_name the new display name to use in the room
333
- # @option params [String,URI] :avatar_url the new avatar URL to use in the room
349
+ # @param display_name [String] the new display name to use in the room
350
+ # @param avatar_url [String,URI] the new avatar URL to use in the room
334
351
  # @note the avatar URL should be a mxc:// URI
335
- def set_user_profile(params = {})
336
- return nil unless params[:display_name] || params[:avatar_url]
352
+ def set_user_profile(display_name: nil, avatar_url: nil, reason: nil)
353
+ return nil unless display_name || avatar_url
337
354
 
338
355
  data = client.api.get_membership(id, client.mxid)
339
356
  raise "Can't set profile if you haven't joined the room" unless data[:membership] == 'join'
340
357
 
341
- data[:displayname] = params[:display_name] unless params[:display_name].nil?
342
- data[:avatar_url] = params[:avatar_url] unless params[:avatar_url].nil?
358
+ data[:displayname] = display_name unless display_name.nil?
359
+ data[:avatar_url] = avatar_url unless avatar_url.nil?
343
360
 
344
- client.api.set_membership(id, client.mxid, 'join', params.fetch(:reason, 'Updating room profile information'), data)
361
+ client.api.set_membership(id, client.mxid, 'join', reason || 'Updating room profile information', data)
345
362
  true
346
363
  end
347
364
 
@@ -377,6 +394,14 @@ module MatrixSdk
377
394
  # State updates
378
395
  #
379
396
 
397
+ def reload!
398
+ reload_name!
399
+ reload_topic!
400
+ reload_aliases!
401
+ true
402
+ end
403
+ alias refresh! reload!
404
+
380
405
  def name=(name)
381
406
  client.api.set_room_name(id, name)
382
407
  @name = name
@@ -389,7 +414,10 @@ module MatrixSdk
389
414
  changed = data[:name] != name
390
415
  @name = data[:name] if changed
391
416
  changed
417
+ rescue MatrixNotFoundError
418
+ nil
392
419
  end
420
+ alias refresh_name! reload_name!
393
421
 
394
422
  def topic=(topic)
395
423
  client.api.set_room_topic(id, topic)
@@ -403,7 +431,10 @@ module MatrixSdk
403
431
  changed = data[:topic] != topic
404
432
  @topic = data[:topic] if changed
405
433
  changed
434
+ rescue MatrixNotFoundError
435
+ nil
406
436
  end
437
+ alias refresh_topic! reload_topic!
407
438
 
408
439
  # Add an alias to the room
409
440
  # @return [Boolean] if the addition was successful or not
@@ -419,16 +450,19 @@ module MatrixSdk
419
450
  # alias list updates.
420
451
  def reload_aliases!
421
452
  data = client.api.get_room_state(id)
422
- new_aliases = data.select { |chunk| chunk.key?(:content) && chunk[:content].key?(:aliases) }
453
+ new_aliases = data.select { |chunk| chunk[:type] == 'm.room.aliases' && chunk.key?(:content) && chunk[:content].key?(:aliases) }
423
454
  .map { |chunk| chunk[:content][:aliases] }
424
455
  .flatten
425
- .reject(&:nil?)
456
+ .compact
426
457
  return false if new_aliases.nil?
427
458
 
428
459
  changed = new_aliases != aliases
429
460
  @aliases = new_aliases if changed
430
461
  changed
462
+ rescue MatrixNotFoundError
463
+ nil
431
464
  end
465
+ alias refresh_aliases! reload_aliases!
432
466
 
433
467
  def invite_only=(invite_only)
434
468
  self.join_rule = invite_only ? :invite : :public
@@ -450,6 +484,17 @@ module MatrixSdk
450
484
  @guest_access = guest_access
451
485
  end
452
486
 
487
+ def avatar_url=(avatar_url)
488
+ avatar_url = URI(avatar_url) unless avatar_url.is_a? URI
489
+ raise ArgumentError, 'Must be a valid MXC URL' unless avatar_url.is_a? URI::MATRIX
490
+
491
+ content = {
492
+ url: avatar_url
493
+ }
494
+ client.api.send_state_event(id, 'm.room.avatar', content)
495
+ @avatar_url = avatar_url
496
+ end
497
+
453
498
  # Modifies the power levels of the room
454
499
  # @param users [Hash] the user-specific power levels to set or remove
455
500
  # @param users_default [Hash] the default user power levels to set
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MatrixSdk
4
- VERSION = '1.0.1'
4
+ VERSION = '1.1.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: 1.0.1
4
+ version: 1.1.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: 2019-05-24 00:00:00.000000000 Z
11
+ date: 2019-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logging
@@ -146,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
146
  - !ruby/object:Gem::Version
147
147
  version: '0'
148
148
  requirements: []
149
- rubygems_version: 3.0.3
149
+ rubygems_version: 3.0.1
150
150
  signing_key:
151
151
  specification_version: 4
152
152
  summary: SDK for applications using the Matrix protocol