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.
- 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
|
|