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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +17 -2
- data/lib/matrix_sdk/api.rb +56 -17
- data/lib/matrix_sdk/bot/base.rb +847 -0
- data/lib/matrix_sdk/bot/main.rb +79 -0
- data/lib/matrix_sdk/bot.rb +4 -0
- data/lib/matrix_sdk/client.rb +44 -12
- data/lib/matrix_sdk/mxid.rb +2 -0
- data/lib/matrix_sdk/protocols/cs.rb +12 -18
- data/lib/matrix_sdk/protocols/msc.rb +5 -4
- data/lib/matrix_sdk/response.rb +2 -0
- data/lib/matrix_sdk/room.rb +172 -89
- data/lib/matrix_sdk/rooms/space.rb +2 -2
- data/lib/matrix_sdk/user.rb +23 -1
- data/lib/matrix_sdk/util/account_data_cache.rb +91 -0
- data/lib/matrix_sdk/util/extensions.rb +16 -6
- data/lib/matrix_sdk/util/state_event_cache.rb +92 -0
- data/lib/matrix_sdk/util/tinycache.rb +8 -2
- data/lib/matrix_sdk/util/tinycache_adapter.rb +11 -1
- data/lib/matrix_sdk/util/uri.rb +16 -4
- data/lib/matrix_sdk/version.rb +1 -1
- data/lib/matrix_sdk.rb +7 -1
- metadata +8 -3
@@ -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
|
data/lib/matrix_sdk/client.rb
CHANGED
@@ -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
|
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
|
-
|
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:
|
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
|
-
|
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(
|
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
|
|
data/lib/matrix_sdk/mxid.rb
CHANGED
@@ -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.
|
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
|
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 [
|
921
|
-
# @see
|
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
|
-
|
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
|
63
|
+
@http_lock&.lock
|
64
64
|
http.request req do |response|
|
65
|
-
@http_lock
|
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
|
-
|
142
|
-
@http_lock.unlock if @http_lock
|
142
|
+
ensure
|
143
|
+
@http_lock.unlock if @http_lock&.owned?
|
143
144
|
end
|
144
145
|
# rubocop:enable Metrics/BlockLength
|
145
146
|
|