Class: MatrixSdk::Client
Instance Attribute Summary collapse
-
#api ⇒ Api
readonly
The underlying API connection.
-
#cache ⇒ :all, ...
The cache level.
-
#next_batch ⇒ Object
readonly
Returns the value of attribute next_batch.
-
#sync_filter ⇒ Hash, String
The global sync filter.
Class Method Summary collapse
-
.new_for_domain(domain, **params) ⇒ Client
Create a new client instance from only a Matrix HS domain.
Instance Method Summary collapse
-
#create_room(room_alias = nil, **params) ⇒ Room
Creates a new room.
-
#ensure_room(room_id) ⇒ Room
Ensures that a room exists in the cache.
-
#find_room(room_id_or_alias, only_canonical: false) ⇒ Room?
Find a room in the locally cached list of rooms that the current user is part of.
-
#get_user(user_id) ⇒ User
Get a User instance from a MXID.
-
#initialize(hs_url, client_cache: :all, **params) ⇒ Client
constructor
A new instance of Client.
-
#join_room(room_id_or_alias, server_name: []) ⇒ Room
Joins an already created room.
- #listen_forever(timeout: 30, bad_sync_timeout: 5, sync_interval: 30, **params) ⇒ Object
-
#listening? ⇒ Boolean
Check if there’s a thread listening for events.
-
#logged_in? ⇒ Boolean
Check if there’s a currently logged in session.
-
#login(username, password, sync_timeout: 15, full_state: false, **params) ⇒ Object
Logs in as a user on the connected HS.
-
#login_with_token(username, token, sync_timeout: 15, full_state: false, **params) ⇒ Object
Logs in as a user on the connected HS.
-
#logout ⇒ Object
Logs out of the current session.
-
#mxid ⇒ MXID
(also: #user_id)
Gets the currently logged in user’s MXID.
-
#presence ⇒ Response
Gets the current user presence status object.
-
#public_rooms ⇒ Array[Room]
Gets a list of all the public rooms on the connected HS.
-
#register_as_guest ⇒ Object
Register - and log in - on the connected HS as a guest.
-
#register_with_password(username, password, **params) ⇒ Object
Register a new user account on the connected HS.
-
#registered_3pids ⇒ Response
Retrieve a list of all registered third-party IDs for the current user.
-
#reload_rooms! ⇒ Boolean
(also: #refresh_rooms!)
Refresh the list of currently handled rooms, replacing it with the user’s currently joined rooms.
-
#remove_room_alias(room_alias) ⇒ Object
Remove a room alias.
-
#rooms ⇒ Array[Room]
Gets a list of all relevant rooms, either the ones currently handled by the client, or the list of currently joined ones if no rooms are handled.
-
#set_presence(status, message: nil) ⇒ Object
Sets the current user’s presence status.
-
#start_listener_thread(**params) ⇒ Object
Starts a background thread that will listen to new events.
-
#stop_listener_thread ⇒ Object
Stops the running background thread if one is active.
-
#sync(skip_store_batch: false, **params) ⇒ Object
(also: #listen_for_events)
Run a message sync round, triggering events as necessary.
-
#upload(content, content_type) ⇒ URI::MATRIX
Upload a piece of data to the media repo.
Methods included from Extensions
Methods included from Logging
Constructor Details
#initialize(hs_url, client_cache: :all, **params) ⇒ Client
Returns a new instance of Client.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/matrix_sdk/client.rb', line 59 def initialize(hs_url, client_cache: :all, **params) event_initialize params[:user_id] ||= params[:mxid] if params[:mxid] if hs_url.is_a? Api @api = hs_url params.each do |k, v| api.instance_variable_set("@#{k}", v) if api.instance_variable_defined? "@#{k}" end else @api = Api.new hs_url, params end @rooms = {} @users = {} @cache = client_cache @identity_server = nil @sync_token = nil @sync_thread = nil @sync_filter = { room: { timeline: { limit: params.fetch(:sync_filter_limit, 20) }, state: { lazy_load_members: true } } } @should_listen = false @next_batch = nil @bad_sync_timeout_limit = 60 * 60 params.each do |k, v| instance_variable_set("@#{k}", v) if instance_variable_defined? "@#{k}" end raise ArgumentError, 'Cache value must be one of of [:all, :some, :none]' unless %i[all some none].include? @cache return unless params[:user_id] @mxid = params[:user_id] end |
Instance Attribute Details
#api ⇒ Api (readonly)
The underlying API connection
23 24 25 |
# File 'lib/matrix_sdk/client.rb', line 23 def api @api end |
#cache ⇒ :all, ...
The cache level
23 |
# File 'lib/matrix_sdk/client.rb', line 23 attr_reader :api, :next_batch |
#next_batch ⇒ Object (readonly)
Returns the value of attribute next_batch.
23 |
# File 'lib/matrix_sdk/client.rb', line 23 attr_reader :api, :next_batch |
#sync_filter ⇒ Hash, String
The global sync filter
23 |
# File 'lib/matrix_sdk/client.rb', line 23 attr_reader :api, :next_batch |
Class Method Details
.new_for_domain(domain, **params) ⇒ Client
This method will not verify that the created client has a valid connection, it will only perform the necessary lookups to build a connection URL.
Create a new client instance from only a Matrix HS domain
This will use the well-known delegation lookup to find the correct client URL
45 46 47 48 49 50 51 |
# File 'lib/matrix_sdk/client.rb', line 45 def self.new_for_domain(domain, **params) api = MatrixSdk::Api.new_for_domain(domain, keep_wellknown: true) return new(api, params) unless api.well_known&.key?('m.identity_server') identity_server = MatrixSdk::Api.new(api.well_known['m.identity_server']['base_url'], protocols: %i[IS]) new(api, params.merge(identity_server: identity_server)) end |
Instance Method Details
#create_room(room_alias = nil, **params) ⇒ Room
Creates a new room
339 340 341 342 |
# File 'lib/matrix_sdk/client.rb', line 339 def create_room(room_alias = nil, **params) data = api.create_room(params.merge(room_alias: room_alias)) ensure_room(data.room_id) end |
#ensure_room(room_id) ⇒ Room
Ensures that a room exists in the cache
515 516 517 518 519 520 521 522 523 524 525 |
# File 'lib/matrix_sdk/client.rb', line 515 def ensure_room(room_id) room_id = MXID.new room_id.to_s unless room_id.is_a? MXID raise ArgumentError, 'Must be a room ID' unless room_id.room_id? room_id = room_id.to_s @rooms.fetch(room_id) do room = Room.new(self, room_id) @rooms[room_id] = room unless cache == :none room end end |
#find_room(room_id_or_alias, only_canonical: false) ⇒ Room?
Find a room in the locally cached list of rooms that the current user is part of
362 363 364 365 366 367 368 369 370 371 |
# File 'lib/matrix_sdk/client.rb', line 362 def find_room(room_id_or_alias, only_canonical: false) room_id_or_alias = MXID.new(room_id_or_alias.to_s) unless room_id_or_alias.is_a? MXID raise ArgumentError, 'Must be a room id or alias' unless room_id_or_alias.room? return @rooms.fetch(room_id_or_alias.to_s, nil) if room_id_or_alias.room_id? return @rooms.values.find { |r| r.canonical_alias == room_id_or_alias.to_s } if only_canonical @rooms.values.find { |r| r.aliases.include? room_id_or_alias.to_s } end |
#get_user(user_id) ⇒ User
The method doesn’t perform any existence checking, so the returned User object may point to a non-existent user
Get a User instance from a MXID
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
# File 'lib/matrix_sdk/client.rb', line 379 def get_user(user_id) user_id = mxid if user_id == :self user_id = MXID.new user_id.to_s unless user_id.is_a? MXID raise ArgumentError, 'Must be a User ID' unless user_id.user? # To still use regular string storage in the hash itself user_id = user_id.to_s if cache == :all @users[user_id] ||= User.new(self, user_id) else User.new(self, user_id) end end |
#join_room(room_id_or_alias, server_name: []) ⇒ Room
Joins an already created room
350 351 352 353 354 |
# File 'lib/matrix_sdk/client.rb', line 350 def join_room(room_id_or_alias, server_name: []) server_name = [server_name] unless server_name.is_a? Array data = api.join_room(room_id_or_alias, server_name: server_name) ensure_room(data.fetch(:room_id, room_id_or_alias)) end |
#listen_forever(timeout: 30, bad_sync_timeout: 5, sync_interval: 30, **params) ⇒ Object
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
# File 'lib/matrix_sdk/client.rb', line 527 def listen_forever(timeout: 30, bad_sync_timeout: 5, sync_interval: 30, **params) orig_bad_sync_timeout = bad_sync_timeout + 0 while @should_listen begin sync(params.merge(timeout: timeout)) bad_sync_timeout = orig_bad_sync_timeout sleep(sync_interval) if sync_interval.positive? rescue MatrixRequestError => e logger.warn("A #{e.class} occurred during sync") if e.httpstatus >= 500 logger.warn("Serverside error, retrying in #{bad_sync_timeout} seconds...") sleep(bad_sync_timeout) if bad_sync_timeout.positive? # rubocop:disable Metrics/BlockNesting bad_sync_timeout = [bad_sync_timeout * 2, @bad_sync_timeout_limit].min end end end rescue StandardError => e logger.error "Unhandled #{e.class} raised in background listener" logger.error [e., *e.backtrace].join($RS) fire_error(ErrorEvent.new(e, :listener_thread)) end |
#listening? ⇒ Boolean
Check if there’s a thread listening for events
471 472 473 |
# File 'lib/matrix_sdk/client.rb', line 471 def listening? @sync_thread&.alive? == true end |
#logged_in? ⇒ Boolean
This will not check if the session is valid, only if it exists
Check if there’s a currently logged in session
292 293 294 |
# File 'lib/matrix_sdk/client.rb', line 292 def logged_in? !@api.access_token.nil? end |
#login(username, password, sync_timeout: 15, full_state: false, **params) ⇒ Object
Logs in as a user on the connected HS
This will also trigger an initial sync unless no_sync is set
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/matrix_sdk/client.rb', line 236 def login(username, password, sync_timeout: 15, full_state: false, **params) username = username.to_s unless username.is_a?(String) password = password.to_s unless password.is_a?(String) raise ArgumentError, "Username can't be nil or empty" if username.nil? || username.empty? raise ArgumentError, "Password can't be nil or empty" if password.nil? || password.empty? data = api.login(user: username, password: password) post_authentication(data) return if params[:no_sync] sync timeout: sync_timeout, full_state: full_state, allow_sync_retry: params.fetch(:allow_sync_retry, nil) end |
#login_with_token(username, token, sync_timeout: 15, full_state: false, **params) ⇒ Object
Logs in as a user on the connected HS
This will also trigger an initial sync unless no_sync is set
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/matrix_sdk/client.rb', line 264 def login_with_token(username, token, sync_timeout: 15, full_state: false, **params) username = username.to_s unless username.is_a?(String) token = token.to_s unless token.is_a?(String) raise ArgumentError, "Username can't be nil or empty" if username.nil? || username.empty? raise ArgumentError, "Token can't be nil or empty" if token.nil? || token.empty? data = api.login(user: username, token: token, type: 'm.login.token') post_authentication(data) return if params[:no_sync] sync timeout: sync_timeout, full_state: full_state, allow_sync_retry: params.fetch(:allow_sync_retry, nil) end |
#logout ⇒ Object
Logs out of the current session
282 283 284 285 286 |
# File 'lib/matrix_sdk/client.rb', line 282 def logout api.logout @api.access_token = nil @mxid = nil end |
#mxid ⇒ MXID Also known as: user_id
Gets the currently logged in user’s MXID
101 102 103 104 105 |
# File 'lib/matrix_sdk/client.rb', line 101 def mxid @mxid ||= begin MXID.new api.whoami?[:user_id] if api&.access_token end end |
#presence ⇒ Response
Gets the current user presence status object
114 115 116 |
# File 'lib/matrix_sdk/client.rb', line 114 def presence api.get_presence_status(mxid).tap { |h| h.delete :user_id } end |
#public_rooms ⇒ Array[Room]
This will try to list all public rooms on the HS, and may take a while on larger instances
Gets a list of all the public rooms on the connected HS
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/matrix_sdk/client.rb', line 134 def public_rooms rooms = [] since = nil loop do data = api.get_public_rooms since: since data[:chunk].each do |chunk| rooms << Room.new(self, chunk[:room_id], name: chunk[:name], topic: chunk[:topic], aliases: chunk[:aliases], canonical_alias: chunk[:canonical_alias], avatar_url: chunk[:avatar_url], join_rule: :public, world_readable: chunk[:world_readable]).tap do |r| r.instance_variable_set :@guest_access, chunk[:guest_can_join] ? :can_join : :forbidden end end break if data[:next_batch].nil? since = data.next_batch end rooms end |
#register_as_guest ⇒ Object
This feature is not commonly supported by many HSes
Register - and log in - on the connected HS as a guest
194 195 196 197 |
# File 'lib/matrix_sdk/client.rb', line 194 def register_as_guest data = api.register(kind: :guest) post_authentication(data) end |
#register_with_password(username, password, **params) ⇒ Object
This method will currently always use auth type ‘m.login.dummy’
Register a new user account on the connected HS
This will also trigger an initial sync unless no_sync is set
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/matrix_sdk/client.rb', line 209 def register_with_password(username, password, **params) username = username.to_s unless username.is_a?(String) password = password.to_s unless password.is_a?(String) raise ArgumentError, "Username can't be nil or empty" if username.nil? || username.empty? raise ArgumentError, "Password can't be nil or empty" if password.nil? || username.empty? data = api.register(auth: { type: 'm.login.dummy' }, username: username, password: password) post_authentication(data) return if params[:no_sync] sync full_state: true, allow_sync_retry: params.fetch(:allow_sync_retry, nil) end |
#registered_3pids ⇒ Response
Retrieve a list of all registered third-party IDs for the current user
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/matrix_sdk/client.rb', line 300 def registered_3pids data = api.get_3pids data.threepids.each do |obj| obj.instance_eval do def added_at Time.at(self[:added_at] / 1000) end def validated_at return unless validated? Time.at(self[:validated_at] / 1000) end def validated? key? :validated_at end def to_s "#{self[:medium]}:#{self[:address]}" end def inspect "#<MatrixSdk::Response 3pid=#{to_s.inspect} added_at=\"#{added_at}\"#{validated? ? " validated_at=\"#{validated_at}\"" : ''}>" end end end data end |
#reload_rooms! ⇒ Boolean Also known as: refresh_rooms!
This will be a no-op if the cache level is set to :none
Refresh the list of currently handled rooms, replacing it with the user’s currently joined rooms.
178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/matrix_sdk/client.rb', line 178 def reload_rooms! return true if cache == :none @rooms.clear api.get_joined_rooms.joined_rooms.each do |id| r = ensure_room(id) r.reload! end true end |
#remove_room_alias(room_alias) ⇒ Object
Remove a room alias
399 400 401 402 403 404 |
# File 'lib/matrix_sdk/client.rb', line 399 def remove_room_alias(room_alias) room_alias = MXID.new room_alias.to_s unless room_alias.is_a? MXID raise ArgumentError, 'Must be a room alias' unless room_alias.room_alias? api.remove_room_alias(room_alias) end |
#rooms ⇒ Array[Room]
This will always return the empty array if the cache level is set to :none
Gets a list of all relevant rooms, either the ones currently handled by the client, or the list of currently joined ones if no rooms are handled
163 164 165 166 167 168 169 170 171 |
# File 'lib/matrix_sdk/client.rb', line 163 def rooms if @rooms.empty? && cache != :none api.get_joined_rooms.joined_rooms.each do |id| ensure_room(id) end end @rooms.values end |
#set_presence(status, message: nil) ⇒ Object
Sets the current user’s presence status
124 125 126 127 128 |
# File 'lib/matrix_sdk/client.rb', line 124 def set_presence(status, message: nil) raise ArgumentError, 'Presence must be one of :online, :offline, :unavailable' unless %i[online offline unavailable].include?(status) api.set_presence_status(mxid, status, message: ) end |
#start_listener_thread(**params) ⇒ Object
Starts a background thread that will listen to new events
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/matrix_sdk/client.rb', line 422 def start_listener_thread(**params) return if listening? @should_listen = true if api.protocol?(:MSC) && api.msc2108? params[:filter] = sync_filter unless params.key? :filter params[:filter] = params[:filter].to_json unless params[:filter].nil? || params[:filter].is_a?(String) params[:since] = @next_batch if @next_batch errors = 0 thread, cancel_token = api.msc2108_sync_sse(params) do |data, event:, id:| @next_batch = id if id if event.to_sym == :sync handle_sync_response(data) errors = 0 elsif event.to_sym == :sync_error logger.error "SSE Sync error received; #{data.type}: #{data.}" errors += 1 # TODO: Allow configuring raise 'Aborting due to excessive errors' if errors >= 5 end end @should_listen = cancel_token else thread = Thread.new { listen_forever(params) } end @sync_thread = thread thread.run end |
#stop_listener_thread ⇒ Object
Stops the running background thread if one is active
455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/matrix_sdk/client.rb', line 455 def stop_listener_thread return unless @sync_thread if @should_listen.is_a? Hash @should_listen[:run] = false else @should_listen = false end if @sync_thread.alive? ret = @sync_thread.join(2) @sync_thread.kill unless ret end @sync_thread = nil end |
#sync(skip_store_batch: false, **params) ⇒ Object Also known as: listen_for_events
Run a message sync round, triggering events as necessary
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'lib/matrix_sdk/client.rb', line 485 def sync(skip_store_batch: false, **params) extra_params = { filter: sync_filter, timeout: 30 } extra_params[:since] = @next_batch unless @next_batch.nil? extra_params.merge!(params) extra_params[:filter] = extra_params[:filter].to_json unless extra_params[:filter].is_a? String attempts = 0 data = loop do begin break api.sync extra_params rescue MatrixSdk::MatrixTimeoutError => e raise e if (attempts += 1) >= params.fetch(:allow_sync_retry, 0) end end @next_batch = data[:next_batch] unless skip_store_batch handle_sync_response(data) true end |
#upload(content, content_type) ⇒ URI::MATRIX
Upload a piece of data to the media repo
412 413 414 415 416 417 |
# File 'lib/matrix_sdk/client.rb', line 412 def upload(content, content_type) data = api.media_upload(content, content_type) return data[:content_uri] if data.key? :content_uri raise MatrixUnexpectedResponseError, 'Upload succeeded, but no media URI returned' end |