matrix_sdk 2.4.0 → 2.7.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 +30 -0
- data/README.md +17 -2
- data/lib/matrix_sdk/api.rb +60 -18
- data/lib/matrix_sdk/bot/base.rb +843 -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 +3 -1
- data/lib/matrix_sdk/protocols/cs.rb +132 -106
- data/lib/matrix_sdk/protocols/msc.rb +5 -0
- data/lib/matrix_sdk/response.rb +2 -0
- data/lib/matrix_sdk/room.rb +140 -40
- data/lib/matrix_sdk/rooms/space.rb +3 -3
- data/lib/matrix_sdk/user.rb +25 -3
- 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/tinycache.rb +20 -4
- data/lib/matrix_sdk/util/tinycache_adapter.rb +5 -1
- data/lib/matrix_sdk/util/uri.rb +16 -4
- data/lib/matrix_sdk/version.rb +1 -1
- data/lib/matrix_sdk.rb +11 -1
- metadata +7 -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
@@ -22,11 +22,11 @@ module MatrixSdk
|
|
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
24
|
attr_reader :api, :next_batch
|
25
|
-
attr_accessor :cache, :sync_filter
|
25
|
+
attr_accessor :cache, :sync_filter, :sync_token
|
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
|
@@ -161,7 +161,14 @@ module MatrixSdk
|
|
161
161
|
#
|
162
162
|
# @return [Hash[String,Array[String]]] A mapping of MXIDs to a list of direct rooms with that user
|
163
163
|
def direct_rooms
|
164
|
-
|
164
|
+
account_data['m.direct'].transform_keys(&:to_s)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Retrieve an account data helper
|
168
|
+
def account_data
|
169
|
+
return MatrixSdk::Util::AccountDataCache.new self if cache == :none
|
170
|
+
|
171
|
+
@account_data ||= MatrixSdk::Util::AccountDataCache.new self
|
165
172
|
end
|
166
173
|
|
167
174
|
# Gets a direct message room for the given user if one exists
|
@@ -395,15 +402,16 @@ module MatrixSdk
|
|
395
402
|
# @param only_canonical [Boolean] Only match alias against the canonical alias
|
396
403
|
# @return [Room] The found room
|
397
404
|
# @return [nil] If no room was found
|
398
|
-
def find_room(room_id_or_alias, only_canonical:
|
405
|
+
def find_room(room_id_or_alias, only_canonical: true)
|
399
406
|
room_id_or_alias = MXID.new(room_id_or_alias.to_s) unless room_id_or_alias.is_a? MXID
|
400
407
|
raise ArgumentError, 'Must be a room id or alias' unless room_id_or_alias.room?
|
401
408
|
|
402
409
|
return @rooms.fetch(room_id_or_alias.to_s, nil) if room_id_or_alias.room_id?
|
403
410
|
|
404
|
-
|
411
|
+
room = @rooms.values.find { |r| r.aliases.include? room_id_or_alias.to_s }
|
412
|
+
return room if only_canonical
|
405
413
|
|
406
|
-
@rooms.values.find { |r| r.aliases.include? room_id_or_alias.to_s }
|
414
|
+
room || @rooms.values.find { |r| r.aliases(canonical_only: false).include? room_id_or_alias.to_s }
|
407
415
|
end
|
408
416
|
|
409
417
|
# Get a User instance from a MXID
|
@@ -497,8 +505,9 @@ module MatrixSdk
|
|
497
505
|
else
|
498
506
|
@should_listen = false
|
499
507
|
end
|
508
|
+
|
500
509
|
if @sync_thread.alive?
|
501
|
-
ret = @sync_thread.join(
|
510
|
+
ret = @sync_thread.join(0.1)
|
502
511
|
@sync_thread.kill unless ret
|
503
512
|
end
|
504
513
|
@sync_thread = nil
|
@@ -552,11 +561,14 @@ module MatrixSdk
|
|
552
561
|
raise ArgumentError, 'Must be a room ID' unless room_id.room_id?
|
553
562
|
|
554
563
|
room_id = room_id.to_s
|
555
|
-
@rooms.fetch(room_id) do
|
564
|
+
ret = @rooms.fetch(room_id) do
|
556
565
|
room = Room.new(self, room_id)
|
557
566
|
@rooms[room_id] = room unless cache == :none
|
558
567
|
room
|
559
568
|
end
|
569
|
+
# Need to figure out a way to handle multiple types
|
570
|
+
ret = @rooms[room_id] = ret.to_space if ret.instance_variable_get :@room_type
|
571
|
+
ret
|
560
572
|
end
|
561
573
|
|
562
574
|
def listen_forever(timeout: 30, bad_sync_timeout: 5, sync_interval: 0, **params)
|
@@ -564,10 +576,13 @@ module MatrixSdk
|
|
564
576
|
while @should_listen
|
565
577
|
begin
|
566
578
|
sync(**params.merge(timeout: timeout))
|
579
|
+
return unless @should_listen
|
567
580
|
|
568
581
|
bad_sync_timeout = orig_bad_sync_timeout
|
569
582
|
sleep(sync_interval) if sync_interval.positive?
|
570
583
|
rescue MatrixRequestError => e
|
584
|
+
return unless @should_listen
|
585
|
+
|
571
586
|
logger.warn("A #{e.class} occurred during sync")
|
572
587
|
if e.httpstatus >= 500
|
573
588
|
logger.warn("Serverside error, retrying in #{bad_sync_timeout} seconds...")
|
@@ -585,7 +600,7 @@ module MatrixSdk
|
|
585
600
|
private
|
586
601
|
|
587
602
|
def post_authentication(data)
|
588
|
-
@mxid = data[:user_id]
|
603
|
+
@mxid = MXID.new data[:user_id]
|
589
604
|
@api.access_token = data[:access_token]
|
590
605
|
@api.device_id = data[:device_id]
|
591
606
|
@api.homeserver = data[:home_server]
|
@@ -602,6 +617,14 @@ module MatrixSdk
|
|
602
617
|
end
|
603
618
|
|
604
619
|
def handle_sync_response(data)
|
620
|
+
data.dig(:account_data, :events)&.each do |account_data|
|
621
|
+
if cache != :none
|
622
|
+
adapter = self.account_data.tinycache_adapter
|
623
|
+
adapter.write(account_data[:type], account_data[:content], expires_in: self.account_data.cache_time)
|
624
|
+
end
|
625
|
+
fire_account_data(MatrixEvent.new(self, account_data))
|
626
|
+
end
|
627
|
+
|
605
628
|
data.dig(:presence, :events)&.each do |presence_update|
|
606
629
|
fire_presence_event(MatrixEvent.new(self, presence_update))
|
607
630
|
end
|
@@ -621,6 +644,13 @@ module MatrixSdk
|
|
621
644
|
room.instance_variable_set '@prev_batch', join.dig(:timeline, :prev_batch)
|
622
645
|
room.instance_variable_set :@members_loaded, true unless sync_filter.fetch(:room, {}).fetch(:state, {}).fetch(:lazy_load_members, false)
|
623
646
|
|
647
|
+
join.dig(:account_data, :events)&.each do |account_data|
|
648
|
+
account_data[:room_id] = room_id.to_s
|
649
|
+
room.send :put_account_data, account_data
|
650
|
+
|
651
|
+
fire_account_data(MatrixEvent.new(self, account_data))
|
652
|
+
end
|
653
|
+
|
624
654
|
join.dig(:state, :events)&.each do |event|
|
625
655
|
event[:room_id] = room_id.to_s
|
626
656
|
handle_state(room_id, event)
|
@@ -648,10 +678,12 @@ module MatrixSdk
|
|
648
678
|
end
|
649
679
|
|
650
680
|
unless cache == :none
|
681
|
+
account_data.tinycache_adapter.cleanup if instance_variable_defined?(:@account_data) && @account_data
|
651
682
|
@rooms.each do |_id, room|
|
652
683
|
# Clean up old cache data after every sync
|
653
684
|
# TODO Run this in a thread?
|
654
685
|
room.tinycache_adapter.cleanup
|
686
|
+
room.account_data.tinycache_adapter.cleanup
|
655
687
|
end
|
656
688
|
end
|
657
689
|
|
data/lib/matrix_sdk/mxid.rb
CHANGED
@@ -12,7 +12,7 @@ module MatrixSdk
|
|
12
12
|
|
13
13
|
# TODO: Community-as-a-Room / Profile-as-a-Room, in case they're going for room aliases
|
14
14
|
@sigil = identifier[0]
|
15
|
-
@localpart, @domain, @port = identifier[1
|
15
|
+
@localpart, @domain, @port = identifier[1..-1].split(':') # rubocop:disable Style/SlicingWithRange # Keep support for slightly older Rubies
|
16
16
|
@port = @port.to_i if @port
|
17
17
|
|
18
18
|
raise ArgumentError, 'Identifier is not a valid MXID' unless valid?
|
@@ -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)
|