matrix_sdk 2.5.0 → 2.8.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.
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MatrixSdk::Bot
4
+ PARAMS_CONFIG = {} # rubocop:disable Style/MutableConstant Intended
5
+
6
+ require 'optparse'
7
+ parser = OptionParser.new do |op|
8
+ op.on('-s homeserver', 'Specify homeserver') { |val| PARAMS_CONFIG[:homeserver] = val }
9
+
10
+ op.on('-T token', 'Token') { |val| PARAMS_CONFIG[:access_token] = val }
11
+ op.on('-U username', 'Username') { |val| PARAMS_CONFIG[:username] = val }
12
+ op.on('-P password', 'Password') { |val| PARAMS_CONFIG[:password] = val }
13
+
14
+ op.on('-q', 'Disable logging') { PARAMS_CONFIG[:logging] = false }
15
+ op.on('-v', 'Enable verbose output') { PARAMS_CONFIG[:logging] = !(PARAMS_CONFIG[:log_level] = :debug).nil? }
16
+ end
17
+
18
+ begin
19
+ parser.parse!(ARGV.dup)
20
+ rescue StandardError => e
21
+ PARAMS_CONFIG[:optparse_error] = e
22
+ end
23
+
24
+ MatrixSdk.logger.appenders.each do |log|
25
+ log.layout = Logging::Layouts.pattern(
26
+ pattern: "%d|%.1l %c : %m\n"
27
+ )
28
+ end
29
+ MatrixSdk.debug! if ENV['MATRIX_DEBUG'] == '1'
30
+
31
+ require 'matrix_sdk/bot/base'
32
+ class Instance < Base
33
+ set :logging, true
34
+ set :log_level, :info
35
+
36
+ set :app_file, caller_files.first || $PROGRAM_NAME
37
+ set(:run) { File.expand_path($PROGRAM_NAME) == File.expand_path(app_file) }
38
+
39
+ if run? && ARGV.any?
40
+ error = PARAMS_CONFIG.delete(:optparse_error)
41
+ raise error if error
42
+
43
+ PARAMS_CONFIG.each { |k, v| set k, v }
44
+ end
45
+ end
46
+
47
+ module Delegator
48
+ def self.delegate(*methods)
49
+ methods.each do |method_name|
50
+ define_method(method_name) do |*args, &block|
51
+ return super(*args, &block) if respond_to? method_name
52
+
53
+ Delegator.target.send(method_name, *args, &block)
54
+ end
55
+ # ensure keyword argument passing is compatible with ruby >= 2.7
56
+ ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true)
57
+ private method_name
58
+ end
59
+ end
60
+
61
+ delegate :command, :client, :event,
62
+ :settings,
63
+ :set, :enable, :disable
64
+
65
+ class << self
66
+ attr_accessor :target
67
+ end
68
+
69
+ self.target = Instance
70
+ end
71
+
72
+ # Trigger the global instance to run once the main class finishes
73
+ at_exit do
74
+ remove_const(:PARAMS_CONFIG)
75
+ Instance.run! if $!.nil? && Instance.run? # rubocop:disable Style/SpecialGlobalVars Don't want to require into global scope
76
+ end
77
+ end
78
+
79
+ extend MatrixSdk::Bot::Delegator # rubocop:disable Style/MixinUsage Intended
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'matrix_sdk'
4
+ require 'matrix_sdk/bot/main'
@@ -21,12 +21,12 @@ module MatrixSdk
21
21
  # @!attribute sync_filter [rw] The global sync filter
22
22
  # @return [Hash,String] A filter definition, either as defined by the
23
23
  # Matrix spec, or as an identifier returned by a filter creation request
24
- attr_reader :api, :next_batch
25
- attr_accessor :cache, :sync_filter
24
+ attr_reader :api
25
+ attr_accessor :cache, :sync_filter, :next_batch
26
26
 
27
- events :error, :event, :presence_event, :invite_event, :leave_event, :ephemeral_event, :state_event
27
+ events :error, :event, :account_data, :presence_event, :invite_event, :leave_event, :ephemeral_event, :state_event
28
28
  ignore_inspect :api,
29
- :on_event, :on_presence_event, :on_invite_event, :on_leave_event, :on_ephemeral_event
29
+ :on_event, :on_account_data, :on_presence_event, :on_invite_event, :on_leave_event, :on_ephemeral_event
30
30
 
31
31
  def_delegators :@api,
32
32
  :access_token, :access_token=, :device_id, :device_id=, :homeserver, :homeserver=,
@@ -45,10 +45,10 @@ module MatrixSdk
45
45
  # @see #initialize
46
46
  def self.new_for_domain(domain, **params)
47
47
  api = MatrixSdk::Api.new_for_domain(domain, keep_wellknown: true)
48
- return new(api, params) unless api.well_known&.key?('m.identity_server')
48
+ return new(api, **params) unless api.well_known&.key?('m.identity_server')
49
49
 
50
50
  identity_server = MatrixSdk::Api.new(api.well_known['m.identity_server']['base_url'], protocols: %i[IS])
51
- new(api, params.merge(identity_server: identity_server))
51
+ new(api, **params.merge(identity_server: identity_server))
52
52
  end
53
53
 
54
54
  # @param hs_url [String,URI,Api] The URL to the Matrix homeserver, without the /_matrix/ part, or an existing Api instance
@@ -75,7 +75,6 @@ module MatrixSdk
75
75
  @identity_server = nil
76
76
  @mxid = nil
77
77
 
78
- @sync_token = nil
79
78
  @sync_thread = nil
80
79
  @sync_filter = { room: { timeline: { limit: params.fetch(:sync_filter_limit, 20) }, state: { lazy_load_members: true } } }
81
80
 
@@ -99,6 +98,9 @@ module MatrixSdk
99
98
  @mxid = params[:user_id]
100
99
  end
101
100
 
101
+ alias sync_token next_batch
102
+ alias sync_token= next_batch=
103
+
102
104
  # Gets the currently logged in user's MXID
103
105
  #
104
106
  # @return [MXID] The MXID of the current user
@@ -161,7 +163,14 @@ module MatrixSdk
161
163
  #
162
164
  # @return [Hash[String,Array[String]]] A mapping of MXIDs to a list of direct rooms with that user
163
165
  def direct_rooms
164
- api.get_account_data(mxid, 'm.direct').transform_keys(&:to_s)
166
+ account_data['m.direct'].transform_keys(&:to_s)
167
+ end
168
+
169
+ # Retrieve an account data helper
170
+ def account_data
171
+ return MatrixSdk::Util::AccountDataCache.new self if cache == :none
172
+
173
+ @account_data ||= MatrixSdk::Util::AccountDataCache.new self
165
174
  end
166
175
 
167
176
  # Gets a direct message room for the given user if one exists
@@ -395,15 +404,16 @@ module MatrixSdk
395
404
  # @param only_canonical [Boolean] Only match alias against the canonical alias
396
405
  # @return [Room] The found room
397
406
  # @return [nil] If no room was found
398
- def find_room(room_id_or_alias, only_canonical: false)
407
+ def find_room(room_id_or_alias, only_canonical: true)
399
408
  room_id_or_alias = MXID.new(room_id_or_alias.to_s) unless room_id_or_alias.is_a? MXID
400
409
  raise ArgumentError, 'Must be a room id or alias' unless room_id_or_alias.room?
401
410
 
402
411
  return @rooms.fetch(room_id_or_alias.to_s, nil) if room_id_or_alias.room_id?
403
412
 
404
- return @rooms.values.find { |r| r.canonical_alias == room_id_or_alias.to_s } if only_canonical
413
+ room = @rooms.values.find { |r| r.aliases.include? room_id_or_alias.to_s }
414
+ return room if only_canonical
405
415
 
406
- @rooms.values.find { |r| r.aliases.include? room_id_or_alias.to_s }
416
+ room || @rooms.values.find { |r| r.aliases(canonical_only: false).include? room_id_or_alias.to_s }
407
417
  end
408
418
 
409
419
  # Get a User instance from a MXID
@@ -497,8 +507,9 @@ module MatrixSdk
497
507
  else
498
508
  @should_listen = false
499
509
  end
510
+
500
511
  if @sync_thread.alive?
501
- ret = @sync_thread.join(2)
512
+ ret = @sync_thread.join(0.1)
502
513
  @sync_thread.kill unless ret
503
514
  end
504
515
  @sync_thread = nil
@@ -567,10 +578,13 @@ module MatrixSdk
567
578
  while @should_listen
568
579
  begin
569
580
  sync(**params.merge(timeout: timeout))
581
+ return unless @should_listen
570
582
 
571
583
  bad_sync_timeout = orig_bad_sync_timeout
572
584
  sleep(sync_interval) if sync_interval.positive?
573
585
  rescue MatrixRequestError => e
586
+ return unless @should_listen
587
+
574
588
  logger.warn("A #{e.class} occurred during sync")
575
589
  if e.httpstatus >= 500
576
590
  logger.warn("Serverside error, retrying in #{bad_sync_timeout} seconds...")
@@ -605,6 +619,14 @@ module MatrixSdk
605
619
  end
606
620
 
607
621
  def handle_sync_response(data)
622
+ data.dig(:account_data, :events)&.each do |account_data|
623
+ if cache != :none
624
+ adapter = self.account_data.tinycache_adapter
625
+ adapter.write(account_data[:type], account_data[:content], expires_in: self.account_data.cache_time)
626
+ end
627
+ fire_account_data(MatrixEvent.new(self, account_data))
628
+ end
629
+
608
630
  data.dig(:presence, :events)&.each do |presence_update|
609
631
  fire_presence_event(MatrixEvent.new(self, presence_update))
610
632
  end
@@ -624,6 +646,13 @@ module MatrixSdk
624
646
  room.instance_variable_set '@prev_batch', join.dig(:timeline, :prev_batch)
625
647
  room.instance_variable_set :@members_loaded, true unless sync_filter.fetch(:room, {}).fetch(:state, {}).fetch(:lazy_load_members, false)
626
648
 
649
+ join.dig(:account_data, :events)&.each do |account_data|
650
+ account_data[:room_id] = room_id.to_s
651
+ room.send :put_account_data, account_data
652
+
653
+ fire_account_data(MatrixEvent.new(self, account_data))
654
+ end
655
+
627
656
  join.dig(:state, :events)&.each do |event|
628
657
  event[:room_id] = room_id.to_s
629
658
  handle_state(room_id, event)
@@ -651,10 +680,13 @@ module MatrixSdk
651
680
  end
652
681
 
653
682
  unless cache == :none
683
+ account_data.tinycache_adapter.cleanup if instance_variable_defined?(:@account_data) && @account_data
654
684
  @rooms.each do |_id, room|
655
685
  # Clean up old cache data after every sync
656
686
  # TODO Run this in a thread?
657
687
  room.tinycache_adapter.cleanup
688
+ room.account_data.tinycache_adapter.cleanup
689
+ room.room_state.tinycache_adapter.cleanup
658
690
  end
659
691
  end
660
692
 
@@ -45,6 +45,8 @@ module MatrixSdk
45
45
  "#{sigil}#{localpart}#{homeserver_suffix}"
46
46
  end
47
47
 
48
+ alias to_str to_s
49
+
48
50
  # Returns the type of the ID
49
51
  #
50
52
  # @return [Symbol] The MXID type, one of (:user_id, :room_id, :event_id, :group_id, or :room_alias)
@@ -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
@@ -519,6 +519,8 @@ module MatrixSdk::Protocols::CS
519
519
  request(:put, client_api_latest, "/rooms/#{room_id}/state/#{event_type}#{"/#{state_key}" unless state_key.nil?}", body: content, query: query)
520
520
  end
521
521
 
522
+ alias set_room_state send_state_event
523
+
522
524
  # Sends a message event to a room
523
525
  # @param room_id [MXID,String] The room ID to send the message event to
524
526
  # @param event_type [String] The event type of the message
@@ -912,31 +914,23 @@ module MatrixSdk::Protocols::CS
912
914
  send_state_event(room_id, 'm.room.avatar', content, **params)
913
915
  end
914
916
 
915
- # Gets a list of current aliases of a room
917
+ # Gets a list of currently known aliases of a room
916
918
  #
917
919
  # @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
920
  # @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
921
+ # @raise [MatrixForbiddenError] Raised if the user doesn't have the right to read aliases
922
+ # @see https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3roomsroomidaliases
923
923
  # The Matrix Spec, for more information about the event and data
924
924
  # @example Looking up aliases for a room
925
925
  # 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
926
  # # => {: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
927
  def get_room_aliases(room_id, **params)
939
- get_room_state(room_id, 'm.room.aliases', **params)
928
+ query = {}
929
+ query[:user_id] = params.delete(:user_id) if protocol?(:AS) && params.key?(:user_id)
930
+
931
+ room_id = ERB::Util.url_encode room_id.to_s
932
+
933
+ request(:get, client_api_latest, "/rooms/#{room_id}/aliases", query: query)
940
934
  end
941
935
 
942
936
  # 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,6 +31,8 @@ module MatrixSdk
31
31
  return data
32
32
  end
33
33
 
34
+ return data if data.instance_variables.include? :@api
35
+
34
36
  raise ArgumentError, 'Input data was not a hash' unless data.is_a? Hash
35
37
 
36
38
  data.extend(Extensions)