matrix_sdk 2.0.0 → 2.1.3
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 +34 -0
- data/lib/matrix_sdk.rb +3 -0
- data/lib/matrix_sdk/api.rb +97 -46
- data/lib/matrix_sdk/client.rb +55 -15
- data/lib/matrix_sdk/extensions.rb +2 -0
- data/lib/matrix_sdk/mxid.rb +1 -0
- data/lib/matrix_sdk/protocols/cs.rb +2 -2
- data/lib/matrix_sdk/protocols/msc.rb +147 -0
- data/lib/matrix_sdk/room.rb +69 -24
- data/lib/matrix_sdk/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 550080056a38ee354ff8e633069d71f207e1927a567fcaed4f7ed9792bcbb79a
|
4
|
+
data.tar.gz: ae8909f5a1092873453c4b7a876315efd29882cee353252d9bd92f069ce3c4ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5838b47c8ea7df7becdb7dadcb07e57c31526c8c3cdd025a18cc2add7db9e9da9f0aff62a16ed4a3a5306566785f941e7cdba90e664f2b872959c7774b4ec4bc
|
7
|
+
data.tar.gz: 3fcad5ea834b2dd0e6d68e55a8e88e5a88b444d07491f381420c4749ca1ff5375030f3a7c4b1b535ff947422caf7c4b86f81633d425614a05e5577aa9a4963ff
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
## 2.1.3 - 2020-09-18
|
2
|
+
|
3
|
+
- Adds separate state event handler as Client#on_state_event
|
4
|
+
- Changes Client sync interval to by-default run at full speed
|
5
|
+
- Fixes state events being sent twice if included in both timeline and state of a sync
|
6
|
+
- Improves error reporting of broken 200 responses
|
7
|
+
- Improves event handlers for rooms, to not depend on a specific room object instance anymore
|
8
|
+
|
9
|
+
## 2.1.2 - 2020-09-10
|
10
|
+
|
11
|
+
- Adds method for reading complete member lists for rooms, improves the CS spec adherence
|
12
|
+
- Adds test for state events
|
13
|
+
- Fixes state event handler for rooms not actually passing events
|
14
|
+
- Fixes Api#new_for_domain using a faulty URI in certain cases
|
15
|
+
|
16
|
+
## 2.1.1 - 2020-08-21
|
17
|
+
|
18
|
+
- Fixes crash if state event content is null (#11)
|
19
|
+
- Fixes an uninitialized URI constant exception when requiring only the main library file
|
20
|
+
- Fixes the Api#get_pushrules method missing an ending slash in the request URI
|
21
|
+
- Fixes discovery code for client/server connections based on domain
|
22
|
+
|
23
|
+
## 2.1.0 - 2020-05-22
|
24
|
+
|
25
|
+
- Adds unique query IDs as well as duration in API debug output, to make it easier to track long requests
|
26
|
+
- Finishes up MSC support, get sync over SSE working flawlessly
|
27
|
+
- Exposes the #listen_forever method in the client abstraction
|
28
|
+
- Fixes room access methods
|
29
|
+
|
30
|
+
## 2.0.1 - 2020-03-13
|
31
|
+
|
32
|
+
- Adds code for handling non-final MSC's in protocols
|
33
|
+
- Currently implementing clients parts of MSC2018 for Sync over Server Sent Events
|
34
|
+
|
1
35
|
## 2.0.0 - 2020-02-14
|
2
36
|
|
3
37
|
**NB**, this release includes backwards-incompatible changes;
|
data/lib/matrix_sdk.rb
CHANGED
data/lib/matrix_sdk/api.rb
CHANGED
@@ -11,10 +11,6 @@ module MatrixSdk
|
|
11
11
|
class Api
|
12
12
|
extend MatrixSdk::Extensions
|
13
13
|
include MatrixSdk::Logging
|
14
|
-
include MatrixSdk::Protocols::AS
|
15
|
-
include MatrixSdk::Protocols::CS
|
16
|
-
include MatrixSdk::Protocols::IS
|
17
|
-
include MatrixSdk::Protocols::SS
|
18
14
|
|
19
15
|
USER_AGENT = "Ruby Matrix SDK v#{MatrixSdk::VERSION}"
|
20
16
|
DEFAULT_HEADERS = {
|
@@ -23,7 +19,7 @@ module MatrixSdk
|
|
23
19
|
}.freeze
|
24
20
|
|
25
21
|
attr_accessor :access_token, :connection_address, :connection_port, :device_id, :autoretry, :global_headers
|
26
|
-
attr_reader :homeserver, :validate_certificate, :open_timeout, :read_timeout, :
|
22
|
+
attr_reader :homeserver, :validate_certificate, :open_timeout, :read_timeout, :well_known, :proxy_uri
|
27
23
|
|
28
24
|
ignore_inspect :access_token, :logger
|
29
25
|
|
@@ -51,10 +47,6 @@ module MatrixSdk
|
|
51
47
|
@homeserver.path.gsub!(/\/?_matrix\/?/, '') if @homeserver.path =~ /_matrix\/?$/
|
52
48
|
raise ArgumentError, 'Please use the base URL for your HS (without /_matrix/)' if @homeserver.path.include? '/_matrix/'
|
53
49
|
|
54
|
-
@protocols = params.fetch(:protocols, %i[CS])
|
55
|
-
@protocols = [@protocols] unless @protocols.is_a? Array
|
56
|
-
@protocols << :CS if @protocols.include?(:AS) && !@protocols.include?(:CS)
|
57
|
-
|
58
50
|
@proxy_uri = params.fetch(:proxy_uri, nil)
|
59
51
|
@connection_address = params.fetch(:address, nil)
|
60
52
|
@connection_port = params.fetch(:port, nil)
|
@@ -71,6 +63,10 @@ module MatrixSdk
|
|
71
63
|
@global_headers.merge!(params.fetch(:global_headers)) if params.key? :global_headers
|
72
64
|
@http = nil
|
73
65
|
|
66
|
+
([params.fetch(:protocols, [:CS])].flatten - protocols).each do |proto|
|
67
|
+
self.class.include MatrixSdk::Protocols.const_get(proto)
|
68
|
+
end
|
69
|
+
|
74
70
|
login(user: @homeserver.user, password: @homeserver.password) if @homeserver.user && @homeserver.password && !@access_token && !params[:skip_login] && protocol?(:CS)
|
75
71
|
@homeserver.userinfo = '' unless params[:skip_login]
|
76
72
|
end
|
@@ -96,6 +92,8 @@ module MatrixSdk
|
|
96
92
|
uri = URI("http#{ssl ? 's' : ''}://#{domain}")
|
97
93
|
well_known = nil
|
98
94
|
target_uri = nil
|
95
|
+
logger = ::Logging.logger[self]
|
96
|
+
logger.debug "Resolving #{domain}"
|
99
97
|
|
100
98
|
if !port.nil? && !port.empty?
|
101
99
|
# If the domain is fully qualified according to Matrix (FQDN and port) then skip discovery
|
@@ -103,21 +101,30 @@ module MatrixSdk
|
|
103
101
|
elsif target == :server
|
104
102
|
# Attempt SRV record discovery
|
105
103
|
target_uri = begin
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
104
|
+
require 'resolv'
|
105
|
+
resolver = Resolv::DNS.new
|
106
|
+
srv = "_matrix._tcp.#{domain}"
|
107
|
+
logger.debug "Trying DNS #{srv}..."
|
108
|
+
d = resolver.getresource(srv, Resolv::DNS::Resource::IN::SRV)
|
109
|
+
d
|
110
|
+
rescue StandardError => e
|
111
|
+
logger.debug "DNS lookup failed with #{e.class}: #{e.message}"
|
112
|
+
nil
|
113
|
+
end
|
112
114
|
|
113
115
|
if target_uri.nil?
|
114
116
|
# Attempt .well-known discovery for server-to-server
|
115
117
|
well_known = begin
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
118
|
+
wk_uri = URI("https://#{domain}/.well-known/matrix/server")
|
119
|
+
logger.debug "Trying #{wk_uri}..."
|
120
|
+
data = Net::HTTP.start(wk_uri.host, wk_uri.port, use_ssl: true, open_timeout: 5, read_timeout: 5, write_timeout: 5) do |http|
|
121
|
+
http.get(wk_uri.path).body
|
122
|
+
end
|
123
|
+
JSON.parse(data)
|
124
|
+
rescue StandardError => e
|
125
|
+
logger.debug "Well-known failed with #{e.class}: #{e.message}"
|
126
|
+
nil
|
127
|
+
end
|
121
128
|
|
122
129
|
target_uri = well_known['m.server'] if well_known&.key?('m.server')
|
123
130
|
else
|
@@ -126,11 +133,16 @@ module MatrixSdk
|
|
126
133
|
elsif %i[client identity].include? target
|
127
134
|
# Attempt .well-known discovery
|
128
135
|
well_known = begin
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
136
|
+
wk_uri = URI("https://#{domain}/.well-known/matrix/client")
|
137
|
+
logger.debug "Trying #{wk_uri}..."
|
138
|
+
data = Net::HTTP.start(wk_uri.host, wk_uri.port, use_ssl: true, open_timeout: 5, read_timeout: 5, write_timeout: 5) do |http|
|
139
|
+
http.get(wk_uri.path).body
|
140
|
+
end
|
141
|
+
JSON.parse(data)
|
142
|
+
rescue StandardError => e
|
143
|
+
logger.debug "Well-known failed with #{e.class}: #{e.message}"
|
144
|
+
nil
|
145
|
+
end
|
134
146
|
|
135
147
|
if well_known
|
136
148
|
key = 'm.homeserver'
|
@@ -142,6 +154,7 @@ module MatrixSdk
|
|
142
154
|
end
|
143
155
|
end
|
144
156
|
end
|
157
|
+
logger.debug "Using #{target_uri.inspect}"
|
145
158
|
|
146
159
|
# Fall back to direct domain connection
|
147
160
|
target_uri ||= URI("https://#{domain}:8448")
|
@@ -155,6 +168,21 @@ module MatrixSdk
|
|
155
168
|
))
|
156
169
|
end
|
157
170
|
|
171
|
+
# Get a list of enabled protocols on the API client
|
172
|
+
#
|
173
|
+
# @example
|
174
|
+
# MatrixSdk::Api.new_for_domain('matrix.org').protocols
|
175
|
+
# # => [:IS, :CS]
|
176
|
+
#
|
177
|
+
# @return [Symbol[]] An array of enabled APIs
|
178
|
+
def protocols
|
179
|
+
self
|
180
|
+
.class.included_modules
|
181
|
+
.reject { |m| m&.name.nil? }
|
182
|
+
.select { |m| m.name.start_with? 'MatrixSdk::Protocols::' }
|
183
|
+
.map { |m| m.name.split('::').last.to_sym }
|
184
|
+
end
|
185
|
+
|
158
186
|
# Check if a protocol is enabled on the API connection
|
159
187
|
#
|
160
188
|
# @example Checking for identity server API support
|
@@ -233,6 +261,7 @@ module MatrixSdk
|
|
233
261
|
# @option options [Hash,String] :body The body to attach to the request, will be JSON-encoded if sent as a hash
|
234
262
|
# @option options [IO] :body_stream A body stream to attach to the request
|
235
263
|
# @option options [Hash] :headers Additional headers to set on the request
|
264
|
+
# @option options [Boolean] :skip_auth (false) Skip authentication
|
236
265
|
def request(method, api, path, **options)
|
237
266
|
url = homeserver.dup.tap do |u|
|
238
267
|
u.path = api_to_path(api) + path
|
@@ -250,7 +279,7 @@ module MatrixSdk
|
|
250
279
|
request.content_length = (request.body || request.body_stream).size
|
251
280
|
end
|
252
281
|
|
253
|
-
request['authorization'] = "Bearer #{access_token}" if access_token
|
282
|
+
request['authorization'] = "Bearer #{access_token}" if access_token && !options.fetch(:skip_auth, false)
|
254
283
|
if options.key? :headers
|
255
284
|
options[:headers].each do |h, v|
|
256
285
|
request[h.to_s.downcase] = v
|
@@ -261,15 +290,26 @@ module MatrixSdk
|
|
261
290
|
loop do
|
262
291
|
raise MatrixConnectionError, "Server still too busy to handle request after #{failures} attempts, try again later" if failures >= 10
|
263
292
|
|
264
|
-
|
293
|
+
req_id = ('A'..'Z').to_a.sample(4).join
|
294
|
+
|
295
|
+
print_http(request, id: req_id)
|
265
296
|
begin
|
297
|
+
dur_start = Time.now
|
266
298
|
response = http.request request
|
267
|
-
|
299
|
+
dur_end = Time.now
|
300
|
+
duration = dur_end - dur_start
|
301
|
+
rescue EOFError
|
268
302
|
logger.error 'Socket closed unexpectedly'
|
269
|
-
raise
|
303
|
+
raise
|
304
|
+
end
|
305
|
+
print_http(response, duration: duration, id: req_id)
|
306
|
+
|
307
|
+
begin
|
308
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
309
|
+
rescue JSON::JSONError => e
|
310
|
+
logger.debug "#{e.class} error when parsing response. #{e}"
|
311
|
+
data = nil
|
270
312
|
end
|
271
|
-
print_http(response)
|
272
|
-
data = JSON.parse(response.body, symbolize_names: true) rescue nil
|
273
313
|
|
274
314
|
if response.is_a? Net::HTTPTooManyRequests
|
275
315
|
raise MatrixRequestError.new_by_code(data, response.code) unless autoretry
|
@@ -280,43 +320,54 @@ module MatrixSdk
|
|
280
320
|
next
|
281
321
|
end
|
282
322
|
|
283
|
-
|
323
|
+
if response.is_a? Net::HTTPSuccess
|
324
|
+
unless data
|
325
|
+
logger.error "Received non-parsable data in 200 response; #{response.body.inspect}"
|
326
|
+
raise MatrixConnectionError, response
|
327
|
+
end
|
328
|
+
return MatrixSdk::Response.new self, data
|
329
|
+
end
|
284
330
|
raise MatrixRequestError.new_by_code(data, response.code) if data
|
285
331
|
|
286
332
|
raise MatrixConnectionError.class_by_code(response.code), response
|
287
333
|
end
|
288
334
|
end
|
289
335
|
|
336
|
+
# Generate a transaction ID
|
337
|
+
#
|
338
|
+
# @return [String] An arbitrary transaction ID
|
339
|
+
def transaction_id
|
340
|
+
ret = @transaction_id ||= 0
|
341
|
+
@transaction_id = @transaction_id.succ
|
342
|
+
ret
|
343
|
+
end
|
344
|
+
|
290
345
|
private
|
291
346
|
|
292
|
-
def print_http(http)
|
347
|
+
def print_http(http, body: true, duration: nil, id: nil)
|
293
348
|
return unless logger.debug?
|
294
349
|
|
295
350
|
if http.is_a? Net::HTTPRequest
|
296
|
-
dir = '>
|
351
|
+
dir = "#{id ? id + ' : ' : nil}>"
|
297
352
|
logger.debug "#{dir} Sending a #{http.method} request to `#{http.path}`:"
|
298
353
|
else
|
299
|
-
dir = '<
|
300
|
-
logger.debug "#{dir} Received a #{http.code} #{http.message} response:"
|
354
|
+
dir = "#{id ? id + ' : ' : nil}<"
|
355
|
+
logger.debug "#{dir} Received a #{http.code} #{http.message} response:#{duration ? " [#{(duration * 1000).to_i}ms]" : nil}"
|
301
356
|
end
|
302
357
|
http.to_hash.map { |k, v| "#{k}: #{k == 'authorization' ? '[ REDACTED ]' : v.join(', ')}" }.each do |h|
|
303
358
|
logger.debug "#{dir} #{h}"
|
304
359
|
end
|
305
360
|
logger.debug dir
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
361
|
+
if body
|
362
|
+
clean_body = JSON.parse(http.body) rescue nil if http.body
|
363
|
+
clean_body.keys.each { |k| clean_body[k] = '[ REDACTED ]' if %w[password access_token].include?(k) }.to_json if clean_body.is_a? Hash
|
364
|
+
clean_body = clean_body.to_s if clean_body
|
365
|
+
logger.debug "#{dir} #{clean_body.length < 200 ? clean_body : clean_body.slice(0..200) + "... [truncated, #{clean_body.length} Bytes]"}" if clean_body
|
366
|
+
end
|
310
367
|
rescue StandardError => e
|
311
368
|
logger.warn "#{e.class} occured while printing request debug; #{e.message}\n#{e.backtrace.join "\n"}"
|
312
369
|
end
|
313
370
|
|
314
|
-
def transaction_id
|
315
|
-
ret = @transaction_id ||= 0
|
316
|
-
@transaction_id = @transaction_id.succ
|
317
|
-
ret
|
318
|
-
end
|
319
|
-
|
320
371
|
def api_to_path(api)
|
321
372
|
# TODO: <api>_current / <api>_latest
|
322
373
|
"/_matrix/#{api.to_s.split('_').join('/')}"
|
data/lib/matrix_sdk/client.rb
CHANGED
@@ -23,7 +23,7 @@ module MatrixSdk
|
|
23
23
|
attr_reader :api, :next_batch
|
24
24
|
attr_accessor :cache, :sync_filter
|
25
25
|
|
26
|
-
events :error, :event, :presence_event, :invite_event, :leave_event, :ephemeral_event
|
26
|
+
events :error, :event, :presence_event, :invite_event, :leave_event, :ephemeral_event, :state_event
|
27
27
|
ignore_inspect :api,
|
28
28
|
:on_event, :on_presence_event, :on_invite_event, :on_leave_event, :on_ephemeral_event
|
29
29
|
|
@@ -44,7 +44,7 @@ module MatrixSdk
|
|
44
44
|
# @see #initialize
|
45
45
|
def self.new_for_domain(domain, **params)
|
46
46
|
api = MatrixSdk::Api.new_for_domain(domain, keep_wellknown: true)
|
47
|
-
return new(api, params) unless api.well_known
|
47
|
+
return new(api, params) unless api.well_known&.key?('m.identity_server')
|
48
48
|
|
49
49
|
identity_server = MatrixSdk::Api.new(api.well_known['m.identity_server']['base_url'], protocols: %i[IS])
|
50
50
|
new(api, params.merge(identity_server: identity_server))
|
@@ -70,8 +70,6 @@ module MatrixSdk
|
|
70
70
|
@api = Api.new hs_url, params
|
71
71
|
end
|
72
72
|
|
73
|
-
@rooms = {}
|
74
|
-
@users = {}
|
75
73
|
@cache = client_cache
|
76
74
|
@identity_server = nil
|
77
75
|
|
@@ -79,7 +77,6 @@ module MatrixSdk
|
|
79
77
|
@sync_thread = nil
|
80
78
|
@sync_filter = { room: { timeline: { limit: params.fetch(:sync_filter_limit, 20) }, state: { lazy_load_members: true } } }
|
81
79
|
|
82
|
-
@should_listen = false
|
83
80
|
@next_batch = nil
|
84
81
|
|
85
82
|
@bad_sync_timeout_limit = 60 * 60
|
@@ -88,6 +85,11 @@ module MatrixSdk
|
|
88
85
|
instance_variable_set("@#{k}", v) if instance_variable_defined? "@#{k}"
|
89
86
|
end
|
90
87
|
|
88
|
+
@rooms = {}
|
89
|
+
@room_handlers = {}
|
90
|
+
@users = {}
|
91
|
+
@should_listen = false
|
92
|
+
|
91
93
|
raise ArgumentError, 'Cache value must be one of of [:all, :some, :none]' unless %i[all some none].include? @cache
|
92
94
|
|
93
95
|
return unless params[:user_id]
|
@@ -343,7 +345,7 @@ module MatrixSdk
|
|
343
345
|
|
344
346
|
# Joins an already created room
|
345
347
|
#
|
346
|
-
# @param room_id_or_alias [String,MXID] A room alias (#room:
|
348
|
+
# @param room_id_or_alias [String,MXID] A room alias (#room:example.com) or a room ID (!id:example.com)
|
347
349
|
# @param server_name [Array[String]] A list of servers to attempt the join through, required for IDs
|
348
350
|
# @return [Room] The resulting room
|
349
351
|
# @see Protocols::CS#join_room
|
@@ -423,7 +425,30 @@ module MatrixSdk
|
|
423
425
|
return if listening?
|
424
426
|
|
425
427
|
@should_listen = true
|
426
|
-
|
428
|
+
if api.protocol?(:MSC) && api.msc2108?
|
429
|
+
params[:filter] = sync_filter unless params.key? :filter
|
430
|
+
params[:filter] = params[:filter].to_json unless params[:filter].nil? || params[:filter].is_a?(String)
|
431
|
+
params[:since] = @next_batch if @next_batch
|
432
|
+
|
433
|
+
errors = 0
|
434
|
+
thread, cancel_token = api.msc2108_sync_sse(params) do |data, event:, id:|
|
435
|
+
@next_batch = id if id
|
436
|
+
if event.to_sym == :sync
|
437
|
+
handle_sync_response(data)
|
438
|
+
errors = 0
|
439
|
+
elsif event.to_sym == :sync_error
|
440
|
+
logger.error "SSE Sync error received; #{data.type}: #{data.message}"
|
441
|
+
errors += 1
|
442
|
+
|
443
|
+
# TODO: Allow configuring
|
444
|
+
raise 'Aborting due to excessive errors' if errors >= 5
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
@should_listen = cancel_token
|
449
|
+
else
|
450
|
+
thread = Thread.new { listen_forever(params) }
|
451
|
+
end
|
427
452
|
@sync_thread = thread
|
428
453
|
thread.run
|
429
454
|
end
|
@@ -432,8 +457,15 @@ module MatrixSdk
|
|
432
457
|
def stop_listener_thread
|
433
458
|
return unless @sync_thread
|
434
459
|
|
435
|
-
@should_listen
|
436
|
-
|
460
|
+
if @should_listen.is_a? Hash
|
461
|
+
@should_listen[:run] = false
|
462
|
+
else
|
463
|
+
@should_listen = false
|
464
|
+
end
|
465
|
+
if @sync_thread.alive?
|
466
|
+
ret = @sync_thread.join(2)
|
467
|
+
@sync_thread.kill unless ret
|
468
|
+
end
|
437
469
|
@sync_thread = nil
|
438
470
|
end
|
439
471
|
|
@@ -494,9 +526,7 @@ module MatrixSdk
|
|
494
526
|
end
|
495
527
|
end
|
496
528
|
|
497
|
-
|
498
|
-
|
499
|
-
def listen_forever(timeout: 30, bad_sync_timeout: 5, sync_interval: 30, **params)
|
529
|
+
def listen_forever(timeout: 30, bad_sync_timeout: 5, sync_interval: 0, **params)
|
500
530
|
orig_bad_sync_timeout = bad_sync_timeout + 0
|
501
531
|
while @should_listen
|
502
532
|
begin
|
@@ -519,6 +549,8 @@ module MatrixSdk
|
|
519
549
|
fire_error(ErrorEvent.new(e, :listener_thread))
|
520
550
|
end
|
521
551
|
|
552
|
+
private
|
553
|
+
|
522
554
|
def post_authentication(data)
|
523
555
|
@mxid = data[:user_id]
|
524
556
|
@api.access_token = data[:access_token]
|
@@ -530,7 +562,10 @@ module MatrixSdk
|
|
530
562
|
def handle_state(room_id, state_event)
|
531
563
|
return unless state_event.key? :type
|
532
564
|
|
565
|
+
on_state_event.fire(MatrixEvent.new(self, state_event), state_event[:type])
|
566
|
+
|
533
567
|
room = ensure_room(room_id)
|
568
|
+
room.send :put_state_event, state_event
|
534
569
|
content = state_event[:content]
|
535
570
|
case state_event[:type]
|
536
571
|
when 'm.room.name'
|
@@ -544,9 +579,9 @@ module MatrixSdk
|
|
544
579
|
when 'm.room.aliases'
|
545
580
|
room.instance_variable_get('@aliases').concat content[:aliases]
|
546
581
|
when 'm.room.join_rules'
|
547
|
-
room.instance_variable_set '@join_rule', content[:join_rule].to_sym
|
582
|
+
room.instance_variable_set '@join_rule', content[:join_rule].nil? ? nil : content[:join_rule].to_sym
|
548
583
|
when 'm.room.guest_access'
|
549
|
-
room.instance_variable_set '@guest_access', content[:guest_access].to_sym
|
584
|
+
room.instance_variable_set '@guest_access', content[:guest_access].nil? ? nil : content[:guest_access].to_sym
|
550
585
|
when 'm.room.member'
|
551
586
|
return unless cache == :all
|
552
587
|
|
@@ -587,7 +622,12 @@ module MatrixSdk
|
|
587
622
|
|
588
623
|
join[:timeline][:events].each do |event|
|
589
624
|
event[:room_id] = room_id.to_s
|
590
|
-
|
625
|
+
# Avoid sending two identical state events if it's both in state and timeline
|
626
|
+
if event.key?(:state_key)
|
627
|
+
state_event = join.dig(:state, :events).find { |ev| ev[:event_id] == event[:event_id] }
|
628
|
+
|
629
|
+
handle_state(room_id, event) unless event == state_event
|
630
|
+
end
|
591
631
|
room.send :put_event, event
|
592
632
|
|
593
633
|
fire_event(MatrixEvent.new(self, event), event[:type])
|
data/lib/matrix_sdk/mxid.rb
CHANGED
@@ -10,6 +10,7 @@ module MatrixSdk
|
|
10
10
|
raise ArgumentError, 'Identifier is too long' if identifier.size > 255
|
11
11
|
raise ArgumentError, 'Identifier lacks required data' unless identifier =~ %r{^([@!$+#][^:]+:[^:]+(?::\d+)?)|(\$[A-Za-z0-9+/]+)$}
|
12
12
|
|
13
|
+
# TODO: Community-as-a-Room / Profile-as-a-Room, in case they're going for room aliases
|
13
14
|
@sigil = identifier[0]
|
14
15
|
@localpart, @domain, @port = identifier[1..-1].split(':')
|
15
16
|
@port = @port.to_i if @port
|
@@ -1637,7 +1637,7 @@ module MatrixSdk::Protocols::CS
|
|
1637
1637
|
|
1638
1638
|
room_id = ERB::Util.url_encode room_id.to_s
|
1639
1639
|
|
1640
|
-
request(:get, :client_r0, "/rooms/#{room_id}/members", query: query)
|
1640
|
+
request(:get, :client_r0, "/rooms/#{room_id}/members", query: query.merge(params))
|
1641
1641
|
end
|
1642
1642
|
|
1643
1643
|
# Gets a list of the joined members in a room
|
@@ -1823,7 +1823,7 @@ module MatrixSdk::Protocols::CS
|
|
1823
1823
|
# @see https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules
|
1824
1824
|
# The Matrix Spec, for more information about the parameters and data
|
1825
1825
|
def get_pushrules
|
1826
|
-
request(:get, :client_r0, '/pushrules')
|
1826
|
+
request(:get, :client_r0, '/pushrules/')
|
1827
1827
|
end
|
1828
1828
|
|
1829
1829
|
# Retrieves a single registered push rule for the current user
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Preliminary support for unmerged MSCs (Matrix Spec Changes)
|
4
|
+
module MatrixSdk::Protocols::MSC
|
5
|
+
def refresh_mscs
|
6
|
+
@msc = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# Check if there's support for MSC2108 - Sync over Server Sent Events
|
10
|
+
def msc2108?
|
11
|
+
@msc ||= {}
|
12
|
+
@msc[2108] ||= \
|
13
|
+
begin
|
14
|
+
request(:get, :client_r0, '/sync/sse', skip_auth: true, headers: { accept: 'text/event-stream' })
|
15
|
+
rescue MatrixSdk::MatrixNotAuthorizedError # Returns 401 if implemented
|
16
|
+
true
|
17
|
+
rescue MatrixSdk::MatrixRequestError
|
18
|
+
false
|
19
|
+
end
|
20
|
+
rescue StandardError => e
|
21
|
+
logger.debug "Failed to check MSC2108 status;\n#{e.inspect}"
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
# Sync over Server Sent Events - MSC2108
|
26
|
+
#
|
27
|
+
# @example Syncing over SSE
|
28
|
+
# @since = 'some token'
|
29
|
+
# api.msc2108_sync_sse(since: @since) do |data, event:, id:|
|
30
|
+
# if event == 'sync'
|
31
|
+
# handle(data) # data is the same as a normal sync response
|
32
|
+
# @since = id
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# @see Protocols::CS#sync
|
37
|
+
# @see https://github.com/matrix-org/matrix-doc/pull/2108/
|
38
|
+
# rubocop:disable Metrics/MethodLength
|
39
|
+
def msc2108_sync_sse(since: nil, **params, &on_data)
|
40
|
+
raise ArgumentError, 'Must be given a block accepting two args - data and { event:, id: }' \
|
41
|
+
unless on_data.is_a?(Proc) && on_data.arity == 2
|
42
|
+
raise 'Needs to be logged in' unless access_token # TODO: Better error
|
43
|
+
|
44
|
+
query = params.select do |k, _v|
|
45
|
+
%i[filter full_state set_presence].include? k
|
46
|
+
end
|
47
|
+
query[:user_id] = params.delete(:user_id) if protocol?(:AS) && params.key?(:user_id)
|
48
|
+
|
49
|
+
req = Net::HTTP::Get.new(homeserver.dup.tap do |u|
|
50
|
+
u.path = api_to_path(:client_r0) + '/sync/sse'
|
51
|
+
u.query = URI.encode_www_form(query)
|
52
|
+
end)
|
53
|
+
req['accept'] = 'text/event-stream'
|
54
|
+
req['accept-encoding'] = 'identity' # Disable compression on the SSE stream
|
55
|
+
req['authorization'] = "Bearer #{access_token}"
|
56
|
+
req['last-event-id'] = since if since
|
57
|
+
|
58
|
+
cancellation_token = { run: true }
|
59
|
+
|
60
|
+
# rubocop:disable Metrics/BlockLength
|
61
|
+
thread = Thread.new(cancellation_token) do |ctx|
|
62
|
+
print_http(req)
|
63
|
+
http.request req do |response|
|
64
|
+
break unless ctx[:run]
|
65
|
+
|
66
|
+
print_http(response, body: false)
|
67
|
+
raise MatrixRequestError.new_by_code(JSON.parse(response.body, symbolize_names: true), response.code) unless response.is_a? Net::HTTPSuccess
|
68
|
+
|
69
|
+
# Override buffer size for BufferedIO
|
70
|
+
socket = response.instance_variable_get :@socket
|
71
|
+
if socket.is_a? Net::BufferedIO
|
72
|
+
socket.instance_eval do
|
73
|
+
def rbuf_fill
|
74
|
+
bufsize_override = 1024
|
75
|
+
loop do
|
76
|
+
case rv = @io.read_nonblock(bufsize_override, exception: false)
|
77
|
+
when String
|
78
|
+
@rbuf << rv
|
79
|
+
rv.clear
|
80
|
+
return
|
81
|
+
when :wait_readable
|
82
|
+
@io.to_io.wait_readable(@read_timeout) || raise(Net::ReadTimeout)
|
83
|
+
when :wait_writable
|
84
|
+
@io.to_io.wait_writable(@read_timeout) || raise(Net::ReadTimeout)
|
85
|
+
when nil
|
86
|
+
raise EOFError, 'end of file reached'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
stream_id = ('A'..'Z').to_a.sample(4).join
|
94
|
+
|
95
|
+
logger.debug "MSC2108 : #{stream_id} : Starting SSE stream."
|
96
|
+
|
97
|
+
buffer = ''
|
98
|
+
response.read_body do |chunk|
|
99
|
+
buffer += chunk
|
100
|
+
|
101
|
+
while (index = buffer.index(/\r\n\r\n|\n\n/))
|
102
|
+
stream = buffer.slice!(0..index)
|
103
|
+
|
104
|
+
data = ''
|
105
|
+
event = nil
|
106
|
+
id = nil
|
107
|
+
|
108
|
+
stream.split(/\r?\n/).each do |part|
|
109
|
+
/^data:(.+)$/.match(part) do |m_data|
|
110
|
+
data += "\n" unless data.empty?
|
111
|
+
data += m_data[1].strip
|
112
|
+
end
|
113
|
+
/^event:(.+)$/.match(part) do |m_event|
|
114
|
+
event = m_event[1].strip
|
115
|
+
end
|
116
|
+
/^id:(.+)$/.match(part) do |m_id|
|
117
|
+
id = m_id[1].strip
|
118
|
+
end
|
119
|
+
/^:(.+)$/.match(part) do |m_comment|
|
120
|
+
logger.debug "MSC2108 : #{stream_id} : Received comment '#{m_comment[1].strip}'"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
if %w[sync sync_error].include? event
|
125
|
+
data = JSON.parse(data, symbolize_names: true)
|
126
|
+
yield((MatrixSdk::Response.new self, data), event: event, id: id)
|
127
|
+
elsif event
|
128
|
+
logger.info "MSC2108 : #{stream_id} : Received unknown event '#{event}'; #{data}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
unless ctx[:run]
|
133
|
+
socket.close
|
134
|
+
break
|
135
|
+
end
|
136
|
+
end
|
137
|
+
break unless ctx[:run]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
# rubocop:enable Metrics/BlockLength
|
141
|
+
|
142
|
+
thread.run
|
143
|
+
|
144
|
+
[thread, cancellation_token]
|
145
|
+
end
|
146
|
+
# rubocop:enable Metrics/MethodLength
|
147
|
+
end
|
data/lib/matrix_sdk/room.rb
CHANGED
@@ -42,19 +42,11 @@ module MatrixSdk
|
|
42
42
|
# The timeline events are what will end up in here
|
43
43
|
attr_reader :id, :client, :topic, :aliases, :members, :events
|
44
44
|
|
45
|
-
# @!attribute [r] on_event
|
46
|
-
# @return [EventHandlerArray] The list of event handlers for all events
|
47
|
-
# @!attribute [r] on_state_event
|
48
|
-
# @return [EventHandlerArray] The list of event handlers for only state events
|
49
|
-
# @!attribute [r] on_ephemeral_event
|
50
|
-
# @return [EventHandlerArray] The list of event handlers for only ephemeral events
|
51
|
-
events :event, :state_event, :ephemeral_event
|
52
45
|
# @!method inspect
|
53
46
|
# An inspect method that skips a handful of instance variables to avoid
|
54
47
|
# flooding the terminal with debug data.
|
55
48
|
# @return [String] a regular inspect string without the data for some variables
|
56
|
-
ignore_inspect :client, :members, :events, :prev_batch, :logger
|
57
|
-
:on_event, :on_state_event, :on_ephemeral_event
|
49
|
+
ignore_inspect :client, :members, :events, :prev_batch, :logger
|
58
50
|
|
59
51
|
alias room_id id
|
60
52
|
|
@@ -85,8 +77,6 @@ module MatrixSdk
|
|
85
77
|
room_id = MXID.new room_id unless room_id.is_a?(MXID)
|
86
78
|
raise ArgumentError, 'room_id must be a valid Room ID' unless room_id.room_id?
|
87
79
|
|
88
|
-
event_initialize
|
89
|
-
|
90
80
|
@name = nil
|
91
81
|
@topic = nil
|
92
82
|
@canonical_alias = nil
|
@@ -114,6 +104,30 @@ module MatrixSdk
|
|
114
104
|
logger.debug "Created room #{room_id}"
|
115
105
|
end
|
116
106
|
|
107
|
+
|
108
|
+
#
|
109
|
+
# Event handlers
|
110
|
+
#
|
111
|
+
|
112
|
+
# @!attribute [r] on_event
|
113
|
+
# @return [EventHandlerArray] The list of event handlers for all events
|
114
|
+
def on_event
|
115
|
+
ensure_room_handlers[:event]
|
116
|
+
end
|
117
|
+
|
118
|
+
# @!attribute [r] on_state_event
|
119
|
+
# @return [EventHandlerArray] The list of event handlers for only state events
|
120
|
+
def on_state_event
|
121
|
+
ensure_room_handlers[:state_event]
|
122
|
+
end
|
123
|
+
|
124
|
+
# @!attribute [r] on_ephemeral_event
|
125
|
+
# @return [EventHandlerArray] The list of event handlers for only ephemeral events
|
126
|
+
def on_ephemeral_event
|
127
|
+
ensure_room_handlers[:ephemeral_event]
|
128
|
+
end
|
129
|
+
|
130
|
+
|
117
131
|
#
|
118
132
|
# State readers
|
119
133
|
#
|
@@ -157,6 +171,17 @@ module MatrixSdk
|
|
157
171
|
members
|
158
172
|
end
|
159
173
|
|
174
|
+
# Get all members (member events) in the room
|
175
|
+
#
|
176
|
+
# @note This will also count members who've knocked, been invited, have left, or have been banned.
|
177
|
+
#
|
178
|
+
# @param params [Hash] Additional query parameters to pass to the room member listing - e.g. for filtering purposes.
|
179
|
+
#
|
180
|
+
# @return [Array(User)] The complete list of members in the room, regardless of membership state
|
181
|
+
def all_members(**params)
|
182
|
+
client.api.get_room_members(id, **params)[:chunk].map { |ch| client.get_user(ch[:state_key]) }
|
183
|
+
end
|
184
|
+
|
160
185
|
# Gets the current name of the room, querying the API if necessary
|
161
186
|
#
|
162
187
|
# @note Will cache the current name for 15 minutes
|
@@ -512,10 +537,10 @@ module MatrixSdk
|
|
512
537
|
# @return [Boolean] if the name was changed or not
|
513
538
|
def reload_name!
|
514
539
|
data = begin
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
540
|
+
client.api.get_room_name(id)
|
541
|
+
rescue MatrixNotFoundError
|
542
|
+
nil
|
543
|
+
end
|
519
544
|
changed = data[:name] != @name
|
520
545
|
@name = data[:name] if changed
|
521
546
|
changed
|
@@ -535,10 +560,10 @@ module MatrixSdk
|
|
535
560
|
# @return [Boolean] if the topic was changed or not
|
536
561
|
def reload_topic!
|
537
562
|
data = begin
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
563
|
+
client.api.get_room_topic(id)
|
564
|
+
rescue MatrixNotFoundError
|
565
|
+
nil
|
566
|
+
end
|
542
567
|
changed = data[:topic] != @topic
|
543
568
|
@topic = data[:topic] if changed
|
544
569
|
changed
|
@@ -590,7 +615,7 @@ module MatrixSdk
|
|
590
615
|
#
|
591
616
|
# @param join_rule [:invite,:public] The join rule of the room
|
592
617
|
def join_rule=(join_rule)
|
593
|
-
client.api.
|
618
|
+
client.api.set_room_join_rules(id, join_rule)
|
594
619
|
@join_rule = join_rule
|
595
620
|
end
|
596
621
|
|
@@ -606,7 +631,7 @@ module MatrixSdk
|
|
606
631
|
#
|
607
632
|
# @param guest_access [:can_join,:forbidden] The new guest access status of the room
|
608
633
|
def guest_access=(guest_access)
|
609
|
-
client.api.
|
634
|
+
client.api.set_room_guest_access(id, guest_access)
|
610
635
|
@guest_access = guest_access
|
611
636
|
end
|
612
637
|
|
@@ -670,15 +695,35 @@ module MatrixSdk
|
|
670
695
|
members << member unless members.any? { |m| m.id == member.id }
|
671
696
|
end
|
672
697
|
|
698
|
+
def room_handlers?
|
699
|
+
client.instance_variable_get(:@room_handlers).key? id
|
700
|
+
end
|
701
|
+
|
702
|
+
def ensure_room_handlers
|
703
|
+
client.instance_variable_get(:@room_handlers)[id] ||= {
|
704
|
+
event: MatrixSdk::EventHandlerArray.new,
|
705
|
+
state_event: MatrixSdk::EventHandlerArray.new,
|
706
|
+
ephemeral_event: MatrixSdk::EventHandlerArray.new
|
707
|
+
}
|
708
|
+
end
|
709
|
+
|
673
710
|
def put_event(event)
|
711
|
+
ensure_room_handlers[:event].fire(MatrixEvent.new(self, event), event[:type]) if room_handlers?
|
712
|
+
|
674
713
|
@events.push event
|
675
714
|
@events.shift if @events.length > @event_history_limit
|
676
|
-
|
677
|
-
fire_event MatrixEvent.new(self, event)
|
678
715
|
end
|
679
716
|
|
680
717
|
def put_ephemeral_event(event)
|
681
|
-
|
718
|
+
return unless room_handlers?
|
719
|
+
|
720
|
+
ensure_room_handlers[:ephemeral_event].fire(MatrixEvent.new(self, event), event[:type])
|
721
|
+
end
|
722
|
+
|
723
|
+
def put_state_event(event)
|
724
|
+
return unless room_handlers?
|
725
|
+
|
726
|
+
ensure_room_handlers[:state_event].fire(MatrixEvent.new(self, event), event[:type])
|
682
727
|
end
|
683
728
|
end
|
684
729
|
end
|
data/lib/matrix_sdk/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: matrix_sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Olofsson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mocha
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- lib/matrix_sdk/protocols/as.rb
|
104
104
|
- lib/matrix_sdk/protocols/cs.rb
|
105
105
|
- lib/matrix_sdk/protocols/is.rb
|
106
|
+
- lib/matrix_sdk/protocols/msc.rb
|
106
107
|
- lib/matrix_sdk/protocols/ss.rb
|
107
108
|
- lib/matrix_sdk/response.rb
|
108
109
|
- lib/matrix_sdk/room.rb
|