matrix_sdk 1.0.1 → 1.1.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: 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