activematrix 0.0.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 +7 -0
- data/CHANGELOG.md +219 -0
- data/LICENSE.txt +21 -0
- data/README.md +82 -0
- data/lib/matrix_sdk/api.rb +451 -0
- 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 +696 -0
- data/lib/matrix_sdk/errors.rb +68 -0
- data/lib/matrix_sdk/mxid.rb +146 -0
- data/lib/matrix_sdk/protocols/as.rb +7 -0
- data/lib/matrix_sdk/protocols/cs.rb +1982 -0
- data/lib/matrix_sdk/protocols/is.rb +35 -0
- data/lib/matrix_sdk/protocols/msc.rb +152 -0
- data/lib/matrix_sdk/protocols/ss.rb +14 -0
- data/lib/matrix_sdk/response.rb +63 -0
- data/lib/matrix_sdk/room.rb +1044 -0
- data/lib/matrix_sdk/rooms/space.rb +79 -0
- data/lib/matrix_sdk/user.rb +168 -0
- data/lib/matrix_sdk/util/account_data_cache.rb +91 -0
- data/lib/matrix_sdk/util/events.rb +111 -0
- data/lib/matrix_sdk/util/extensions.rb +85 -0
- data/lib/matrix_sdk/util/state_event_cache.rb +92 -0
- data/lib/matrix_sdk/util/tinycache.rb +140 -0
- data/lib/matrix_sdk/util/tinycache_adapter.rb +87 -0
- data/lib/matrix_sdk/util/uri.rb +101 -0
- data/lib/matrix_sdk/version.rb +5 -0
- data/lib/matrix_sdk.rb +75 -0
- metadata +172 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MatrixSdk::Protocols::IS
|
4
|
+
def identity_status
|
5
|
+
request(:get, :identity_api_v1, '/')
|
6
|
+
end
|
7
|
+
|
8
|
+
def identity_get_pubkey(id)
|
9
|
+
id = ERB::Util.url_encode id.to_s
|
10
|
+
|
11
|
+
request(:get, :identity_api_v1, "/pubkey/#{id}")
|
12
|
+
end
|
13
|
+
|
14
|
+
def identity_pubkey_isvalid(key, ephemeral: false)
|
15
|
+
if ephemeral
|
16
|
+
request(:get, :identity_api_v1, '/pubkey/ephemeral/isvalid', query: { public_key: key })
|
17
|
+
else
|
18
|
+
request(:get, :identity_api_v1, '/pubkey/isvalid', query: { public_key: key })
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def identity_pubkey_ephemeral_isvalid(key)
|
23
|
+
identity_pubkey_isvalid(key, ephemeral: true)
|
24
|
+
end
|
25
|
+
|
26
|
+
def identity_lookup(medium:, address:)
|
27
|
+
request(:get, :identity_api_v1, '/lookup', query: { medium: medium, address: address })
|
28
|
+
end
|
29
|
+
|
30
|
+
def identity_bulk_lookup(threepids)
|
31
|
+
request(:post, :identity_api_v1, '/bulk_lookup', body: { threepids: threepids })
|
32
|
+
end
|
33
|
+
|
34
|
+
# XXX
|
35
|
+
end
|
@@ -0,0 +1,152 @@
|
|
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_lock&.lock
|
64
|
+
http.request req do |response|
|
65
|
+
@http_lock&.unlock
|
66
|
+
break unless ctx[:run]
|
67
|
+
|
68
|
+
print_http(response, body: false)
|
69
|
+
raise MatrixRequestError.new_by_code(JSON.parse(response.body, symbolize_names: true), response.code) unless response.is_a? Net::HTTPSuccess
|
70
|
+
|
71
|
+
# Override buffer size for BufferedIO
|
72
|
+
socket = response.instance_variable_get :@socket
|
73
|
+
if socket.is_a? Net::BufferedIO
|
74
|
+
socket.instance_eval do
|
75
|
+
def rbuf_fill
|
76
|
+
bufsize_override = 1024
|
77
|
+
loop do
|
78
|
+
case rv = @io.read_nonblock(bufsize_override, exception: false)
|
79
|
+
when String
|
80
|
+
@rbuf << rv
|
81
|
+
rv.clear
|
82
|
+
return
|
83
|
+
when :wait_readable
|
84
|
+
@io.to_io.wait_readable(@read_timeout) || raise(Net::ReadTimeout)
|
85
|
+
when :wait_writable
|
86
|
+
@io.to_io.wait_writable(@read_timeout) || raise(Net::ReadTimeout)
|
87
|
+
when nil
|
88
|
+
raise EOFError, 'end of file reached'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
stream_id = ('A'..'Z').to_a.sample(4).join
|
96
|
+
|
97
|
+
logger.debug "MSC2108 : #{stream_id} : Starting SSE stream."
|
98
|
+
|
99
|
+
buffer = ''
|
100
|
+
response.read_body do |chunk|
|
101
|
+
buffer += chunk
|
102
|
+
|
103
|
+
while (index = buffer.index(/\r\n\r\n|\n\n/))
|
104
|
+
stream = buffer.slice!(0..index)
|
105
|
+
|
106
|
+
data = ''
|
107
|
+
event = nil
|
108
|
+
id = nil
|
109
|
+
|
110
|
+
stream.split(/\r?\n/).each do |part|
|
111
|
+
/^data:(.+)$/.match(part) do |m_data|
|
112
|
+
data += "\n" unless data.empty?
|
113
|
+
data += m_data[1].strip
|
114
|
+
end
|
115
|
+
/^event:(.+)$/.match(part) do |m_event|
|
116
|
+
event = m_event[1].strip
|
117
|
+
end
|
118
|
+
/^id:(.+)$/.match(part) do |m_id|
|
119
|
+
id = m_id[1].strip
|
120
|
+
end
|
121
|
+
/^:(.+)$/.match(part) do |m_comment|
|
122
|
+
logger.debug "MSC2108 : #{stream_id} : Received comment '#{m_comment[1].strip}'"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
if %w[sync sync_error].include? event
|
127
|
+
data = JSON.parse(data, symbolize_names: true)
|
128
|
+
yield((MatrixSdk::Response.new self, data), event: event, id: id)
|
129
|
+
elsif event
|
130
|
+
logger.info "MSC2108 : #{stream_id} : Received unknown event '#{event}'; #{data}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
unless ctx[:run]
|
135
|
+
socket.close
|
136
|
+
break
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
break unless ctx[:run]
|
141
|
+
end
|
142
|
+
ensure
|
143
|
+
@http_lock.unlock if @http_lock&.owned?
|
144
|
+
end
|
145
|
+
# rubocop:enable Metrics/BlockLength
|
146
|
+
|
147
|
+
thread.run
|
148
|
+
|
149
|
+
[thread, cancellation_token]
|
150
|
+
end
|
151
|
+
# rubocop:enable Metrics/MethodLength
|
152
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MatrixSdk::Protocols::SS
|
4
|
+
# Gets the server version
|
5
|
+
def server_version
|
6
|
+
MatrixSdk::Response.new(self, request(:get, :federation_v1, '/version').server).tap do |resp|
|
7
|
+
resp.instance_eval <<-'CODE', __FILE__, __LINE__ + 1
|
8
|
+
def to_s
|
9
|
+
"#{name} #{version}"
|
10
|
+
end
|
11
|
+
CODE
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MatrixSdk
|
4
|
+
# An usability wrapper for API responses as an extended [Hash]
|
5
|
+
# All results can be read as both hash keys and as read-only methods on the key
|
6
|
+
#
|
7
|
+
# @example Simple usage of the response wrapper to get the avatar URL
|
8
|
+
# resp = api.get_avatar_url(api.whoami?.user_id)
|
9
|
+
# # => { avatar_url: 'mxc://matrix.org/SDGdghriugerRg' }
|
10
|
+
# resp.is_a? Hash
|
11
|
+
# # => true
|
12
|
+
# resp.key? :avatar_url
|
13
|
+
# # => true
|
14
|
+
# resp.avatar_url
|
15
|
+
# # => 'mxc://matrix.org/SDGdghriugerRg'
|
16
|
+
# resp.api.set_avatar_url(...)
|
17
|
+
# # => {}
|
18
|
+
#
|
19
|
+
# @since 0.0.3
|
20
|
+
# @see Hash
|
21
|
+
# @!attribute [r] api
|
22
|
+
# @return [Api] The API connection that returned the response
|
23
|
+
module Response
|
24
|
+
def self.new(api, data)
|
25
|
+
if data.is_a? Array
|
26
|
+
raise ArgumentError, 'Input data was not an array of hashes' unless data.all? { |v| v.is_a? Hash }
|
27
|
+
|
28
|
+
data.each do |value|
|
29
|
+
Response.new api, value
|
30
|
+
end
|
31
|
+
return data
|
32
|
+
end
|
33
|
+
|
34
|
+
return data if data.instance_variables.include? :@api
|
35
|
+
|
36
|
+
raise ArgumentError, 'Input data was not a hash' unless data.is_a? Hash
|
37
|
+
|
38
|
+
data.extend(Extensions)
|
39
|
+
data.instance_variable_set(:@api, api)
|
40
|
+
|
41
|
+
data.select { |_k, v| v.is_a? Hash }
|
42
|
+
.each { |_v, v| Response.new api, v }
|
43
|
+
|
44
|
+
data
|
45
|
+
end
|
46
|
+
|
47
|
+
module Extensions
|
48
|
+
attr_reader :api
|
49
|
+
|
50
|
+
def respond_to_missing?(name, *_args)
|
51
|
+
return true if key? name
|
52
|
+
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def method_missing(name, *args)
|
57
|
+
return fetch(name) if key?(name) && args.empty?
|
58
|
+
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|