matrix_sdk 2.5.0 → 2.8.0

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