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,1044 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'matrix_sdk'
|
4
|
+
require 'matrix_sdk/util/events'
|
5
|
+
require 'matrix_sdk/util/tinycache'
|
6
|
+
|
7
|
+
module MatrixSdk
|
8
|
+
# A class for tracking the information about a room on Matrix
|
9
|
+
class Room
|
10
|
+
extend MatrixSdk::Extensions
|
11
|
+
extend MatrixSdk::Util::Tinycache
|
12
|
+
include MatrixSdk::Logging
|
13
|
+
|
14
|
+
# @!attribute [rw] event_history_limit
|
15
|
+
# @return [Fixnum] the limit of events to keep in the event log
|
16
|
+
attr_accessor :event_history_limit
|
17
|
+
# @!attribute [r] id
|
18
|
+
# @return [String] the internal ID of the room
|
19
|
+
# @!attribute [r] client
|
20
|
+
# @return [Client] the client for the room
|
21
|
+
# @!attribute [r] events
|
22
|
+
# @return [Array(Object)] the last +event_history_limit+ events to arrive in the room
|
23
|
+
# @see https://matrix.org/docs/spec/client_server/r0.3.0.html#get-matrix-client-r0-sync
|
24
|
+
# The timeline events are what will end up in here
|
25
|
+
attr_reader :id, :client, :events
|
26
|
+
|
27
|
+
# @!method inspect
|
28
|
+
# An inspect method that skips a handful of instance variables to avoid
|
29
|
+
# flooding the terminal with debug data.
|
30
|
+
# @return [String] a regular inspect string without the data for some variables
|
31
|
+
ignore_inspect :client, :events, :prev_batch, :logger, :tinycache_adapter
|
32
|
+
|
33
|
+
# Requires heavy lookups, so they're cached for an hour
|
34
|
+
cached :joined_members, cache_level: :all, expires_in: 60 * 60
|
35
|
+
|
36
|
+
# Only cache unfiltered requests for aliases and members
|
37
|
+
cached :aliases, unless: proc { |args| args.any? }, cache_level: :all, expires_in: 60 * 60
|
38
|
+
cached :all_members, unless: proc { |args| args.any? }, cache_level: :all, expires_in: 60 * 60
|
39
|
+
|
40
|
+
alias room_id id
|
41
|
+
alias members joined_members
|
42
|
+
|
43
|
+
# Create a new room instance
|
44
|
+
#
|
45
|
+
# @note This method isn't supposed to be used directly, rather rooms should
|
46
|
+
# be retrieved from the Client abstraction.
|
47
|
+
#
|
48
|
+
# @param client [Client] The underlying connection
|
49
|
+
# @param room_id [MXID] The room ID
|
50
|
+
# @param data [Hash] Additional data to assign to the room
|
51
|
+
# @option data [String] :name The current name of the room
|
52
|
+
# @option data [String] :topic The current topic of the room
|
53
|
+
# @option data [String,MXID] :canonical_alias The canonical alias of the room
|
54
|
+
# @option data [Array(String,MXID)] :aliases All non-canonical aliases of the room
|
55
|
+
# @option data [:invite,:public,:knock] :join_rule The join rule for the room
|
56
|
+
# @option data [:can_join,:forbidden] :guest_access The guest access setting for the room
|
57
|
+
# @option data [Boolean] :world_readable If the room is readable by the entire world
|
58
|
+
# @option data [Array(User)] :members The list of joined members
|
59
|
+
# @option data [Array(Object)] :events The list of current events in the room
|
60
|
+
# @option data [Boolean] :members_loaded If the list of members is already loaded
|
61
|
+
# @option data [Integer] :event_history_limit (10) The limit of events to store for the room
|
62
|
+
# @option data [String,URI] :avatar_url The avatar URL for the room
|
63
|
+
# @option data [String] :prev_batch The previous batch token for backfill
|
64
|
+
def initialize(client, room_id, data = {})
|
65
|
+
if client.is_a? Room
|
66
|
+
copy = client
|
67
|
+
client = copy.client
|
68
|
+
room_id = copy.id
|
69
|
+
# data = copy.attributes
|
70
|
+
end
|
71
|
+
|
72
|
+
raise ArgumentError, 'Must be given a Client instance' unless client.is_a? Client
|
73
|
+
|
74
|
+
@client = client
|
75
|
+
room_id = MXID.new room_id unless room_id.is_a?(MXID)
|
76
|
+
raise ArgumentError, 'room_id must be a valid Room ID' unless room_id.room_id?
|
77
|
+
|
78
|
+
@events = []
|
79
|
+
@event_history_limit = 10
|
80
|
+
@room_type = nil
|
81
|
+
|
82
|
+
@prev_batch = nil
|
83
|
+
|
84
|
+
%i[name topic canonical_alias avatar_url].each do |type|
|
85
|
+
room_state.tinycache_adapter.write("m.room.#{type}", { type => data.delete(type) }) if data.key? type
|
86
|
+
end
|
87
|
+
room_state.tinycache_adapter.write('m.room.join_rules', { join_rule: data.delete(:join_rule) }) if data.key? :join_rule
|
88
|
+
room_state.tinycache_adapter.write('m.room.history_visibility', { history_visibility: data.delete(:world_readable) ? :world_readable : nil }) if data.key? :world_readable
|
89
|
+
|
90
|
+
data.each do |k, v|
|
91
|
+
next if %i[client].include? k
|
92
|
+
|
93
|
+
if respond_to?("#{k}_cached?".to_sym) && send("#{k}_cached?".to_sym)
|
94
|
+
tinycache_adapter.write(k, v)
|
95
|
+
elsif instance_variable_defined? "@#{k}"
|
96
|
+
instance_variable_set("@#{k}", v)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
@id = room_id.to_s
|
101
|
+
|
102
|
+
logger.debug "Created room #{room_id}"
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Casting operators
|
107
|
+
#
|
108
|
+
|
109
|
+
def to_space
|
110
|
+
return nil unless space?
|
111
|
+
|
112
|
+
Rooms::Space.new self, nil
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_s
|
116
|
+
prefix = canonical_alias || id
|
117
|
+
return "#{prefix} | #{name}" unless name.nil?
|
118
|
+
|
119
|
+
prefix
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Event handlers
|
124
|
+
#
|
125
|
+
|
126
|
+
# @!attribute [r] on_event
|
127
|
+
# @return [EventHandlerArray] The list of event handlers for all events
|
128
|
+
def on_event
|
129
|
+
ensure_room_handlers[:event]
|
130
|
+
end
|
131
|
+
|
132
|
+
# @!attribute [r] on_account_data
|
133
|
+
# @return [EventHandlerArray] The list of event handlers for account data changes
|
134
|
+
def on_account_data
|
135
|
+
ensure_room_handlers[:account_data]
|
136
|
+
end
|
137
|
+
|
138
|
+
# @!attribute [r] on_state_event
|
139
|
+
# @return [EventHandlerArray] The list of event handlers for only state events
|
140
|
+
def on_state_event
|
141
|
+
ensure_room_handlers[:state_event]
|
142
|
+
end
|
143
|
+
|
144
|
+
# @!attribute [r] on_ephemeral_event
|
145
|
+
# @return [EventHandlerArray] The list of event handlers for only ephemeral events
|
146
|
+
def on_ephemeral_event
|
147
|
+
ensure_room_handlers[:ephemeral_event]
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# State readers
|
152
|
+
#
|
153
|
+
|
154
|
+
# Gets a human-readable name for the room
|
155
|
+
#
|
156
|
+
# This will return #name or #canonical_alias if they've been set,
|
157
|
+
# otherwise it will query the API for members and generate a string from
|
158
|
+
# a subset of their names.
|
159
|
+
#
|
160
|
+
# @return [String] a human-readable name for the room
|
161
|
+
# @note This method will populate the #members list if it has to fall back
|
162
|
+
# to the member name generation.
|
163
|
+
def display_name
|
164
|
+
return name if name
|
165
|
+
return canonical_alias if canonical_alias
|
166
|
+
|
167
|
+
members = joined_members
|
168
|
+
.reject { |m| m.user_id == client.mxid }
|
169
|
+
.map(&:display_name)
|
170
|
+
|
171
|
+
return members.first if members.one?
|
172
|
+
return "#{members.first} and #{members.last}" if members.count == 2
|
173
|
+
return "#{members.first} and #{members.count - 1} others" if members.count > 2
|
174
|
+
|
175
|
+
'Empty Room'
|
176
|
+
end
|
177
|
+
|
178
|
+
# @return [String, nil] the canonical alias of the room
|
179
|
+
def canonical_alias
|
180
|
+
get_state('m.room.canonical_alias')[:alias]
|
181
|
+
rescue MatrixSdk::MatrixNotFoundError
|
182
|
+
nil
|
183
|
+
end
|
184
|
+
|
185
|
+
# Populates and returns the #members array
|
186
|
+
#
|
187
|
+
# @return [Array(User)] The list of members in the room
|
188
|
+
def joined_members
|
189
|
+
client.api.get_room_joined_members(id)[:joined].map do |mxid, data|
|
190
|
+
User.new(client, mxid.to_s,
|
191
|
+
display_name: data.fetch(:display_name, nil),
|
192
|
+
avatar_url: data.fetch(:avatar_url, nil))
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Get all members (member events) in the room
|
197
|
+
#
|
198
|
+
# @note This will also count members who've knocked, been invited, have left, or have been banned.
|
199
|
+
#
|
200
|
+
# @param params [Hash] Additional query parameters to pass to the room member listing - e.g. for filtering purposes.
|
201
|
+
#
|
202
|
+
# @return [Array(User)] The complete list of members in the room, regardless of membership state
|
203
|
+
def all_members(**params)
|
204
|
+
client.api.get_room_members(id, **params)[:chunk].map { |ch| client.get_user(ch[:state_key]) }
|
205
|
+
end
|
206
|
+
|
207
|
+
# Gets the current name of the room, querying the API if necessary
|
208
|
+
#
|
209
|
+
# @note Will cache the current name for 15 minutes
|
210
|
+
#
|
211
|
+
# @return [String,nil] The room name - if any
|
212
|
+
def name
|
213
|
+
get_state('m.room.name')[:name]
|
214
|
+
rescue MatrixNotFoundError
|
215
|
+
# No room name has been specified
|
216
|
+
nil
|
217
|
+
end
|
218
|
+
|
219
|
+
# Checks if the room is a direct message / 1:1 room
|
220
|
+
#
|
221
|
+
# @param members_only [Boolean] Should directness only care about member count?
|
222
|
+
# @return [Boolean]
|
223
|
+
def dm?(members_only: false)
|
224
|
+
return true if !members_only && client.direct_rooms.any? { |_uid, rooms| rooms.include? id.to_s }
|
225
|
+
|
226
|
+
joined_members.count <= 2
|
227
|
+
end
|
228
|
+
|
229
|
+
# Mark a room as a direct (1:1) message Room
|
230
|
+
def dm=(direct)
|
231
|
+
rooms = client.direct_rooms
|
232
|
+
dirty = false
|
233
|
+
list_for_room = (rooms[id.to_s] ||= [])
|
234
|
+
if direct && !list_for_room.include?(id.to_s)
|
235
|
+
list_for_room << id.to_s
|
236
|
+
dirty = true
|
237
|
+
elsif !direct && list_for_room.include?(id.to_s)
|
238
|
+
list_for_room.delete id.to_s
|
239
|
+
rooms.delete id.to_s if list_for_room.empty?
|
240
|
+
dirty = true
|
241
|
+
end
|
242
|
+
client.account_data['m.direct'] = rooms if dirty
|
243
|
+
end
|
244
|
+
|
245
|
+
# Gets the avatar url of the room - if any
|
246
|
+
#
|
247
|
+
# @return [String,nil] The avatar URL - if any
|
248
|
+
def avatar_url
|
249
|
+
get_state('m.room.avatar_url')[:url]
|
250
|
+
rescue MatrixNotFoundError
|
251
|
+
# No avatar has been set
|
252
|
+
nil
|
253
|
+
end
|
254
|
+
|
255
|
+
# Gets the room topic - if any
|
256
|
+
#
|
257
|
+
# @return [String,nil] The topic of the room
|
258
|
+
def topic
|
259
|
+
get_state('m.room.topic')[:topic]
|
260
|
+
rescue MatrixNotFoundError
|
261
|
+
# No room name has been specified
|
262
|
+
nil
|
263
|
+
end
|
264
|
+
|
265
|
+
# Gets the guest access rights for the room
|
266
|
+
#
|
267
|
+
# @return [:can_join,:forbidden] The current guest access right
|
268
|
+
def guest_access
|
269
|
+
get_state('m.room.guest_access')[:guest_access]&.to_sym
|
270
|
+
end
|
271
|
+
|
272
|
+
# Gets the join rule for the room
|
273
|
+
#
|
274
|
+
# @return [:public,:knock,:invite,:private] The current join rule
|
275
|
+
def join_rule
|
276
|
+
get_state('m.room.join_rules')[:join_rule]&.to_sym
|
277
|
+
end
|
278
|
+
|
279
|
+
# Checks if +guest_access+ is set to +:can_join+
|
280
|
+
def guest_access?
|
281
|
+
guest_access == :can_join
|
282
|
+
end
|
283
|
+
|
284
|
+
# Checks if +join_rule+ is set to +:invite+
|
285
|
+
def invite_only?
|
286
|
+
join_rule == :invite
|
287
|
+
end
|
288
|
+
|
289
|
+
# Checks if +join_rule+ is set to +:knock+
|
290
|
+
def knock_only?
|
291
|
+
join_rule == :knock
|
292
|
+
end
|
293
|
+
|
294
|
+
def room_state
|
295
|
+
return MatrixSdk::Util::StateEventCache.new self if client.cache == :none
|
296
|
+
|
297
|
+
@room_state ||= MatrixSdk::Util::StateEventCache.new self
|
298
|
+
end
|
299
|
+
|
300
|
+
# Gets a state object in the room
|
301
|
+
def get_state(type, state_key: nil)
|
302
|
+
room_state[type, state_key]
|
303
|
+
end
|
304
|
+
|
305
|
+
# Sets a state object in the room
|
306
|
+
def set_state(type, data, state_key: nil)
|
307
|
+
room_state[type, state_key] = data
|
308
|
+
end
|
309
|
+
|
310
|
+
# Gets the history visibility of the room
|
311
|
+
#
|
312
|
+
# @return [:invited,:joined,:shared,:world_readable] The current history visibility for the room
|
313
|
+
def history_visibility
|
314
|
+
get_state('m.room.history_visibility')[:history_visibility]&.to_sym
|
315
|
+
end
|
316
|
+
|
317
|
+
# Checks if the room history is world readable
|
318
|
+
#
|
319
|
+
# @return [Boolean] If the history is world readable
|
320
|
+
def world_readable?
|
321
|
+
history_visibility == :world_readable
|
322
|
+
end
|
323
|
+
alias world_readable world_readable?
|
324
|
+
|
325
|
+
# Gets the room aliases
|
326
|
+
#
|
327
|
+
# @param canonical_only [Boolean] Should the list of aliases only contain the canonical ones
|
328
|
+
# @return [Array[String]] The assigned room aliases
|
329
|
+
def aliases(canonical_only: true)
|
330
|
+
canonical = get_state('m.room.canonical_alias') rescue {}
|
331
|
+
aliases = ([canonical[:alias]].compact + (canonical[:alt_aliases] || [])).uniq.sort
|
332
|
+
return aliases if canonical_only
|
333
|
+
|
334
|
+
(aliases + client.api.get_room_aliases(id).aliases).uniq.sort
|
335
|
+
end
|
336
|
+
|
337
|
+
#
|
338
|
+
# Message handling
|
339
|
+
#
|
340
|
+
|
341
|
+
# Sends a plain-text message to the room
|
342
|
+
#
|
343
|
+
# @param text [String] the message to send
|
344
|
+
def send_text(text)
|
345
|
+
client.api.send_message(id, text)
|
346
|
+
end
|
347
|
+
|
348
|
+
# Sends a custom HTML message to the room
|
349
|
+
#
|
350
|
+
# @param html [String] the HTML message to send
|
351
|
+
# @param body [String,nil] a plain-text representation of the object
|
352
|
+
# (Will default to the HTML with all tags stripped away)
|
353
|
+
# @param msgtype [String] ('m.text') The message type for the message
|
354
|
+
# @param format [String] ('org.matrix.custom.html') The message format
|
355
|
+
# @see https://matrix.org/docs/spec/client_server/r0.3.0.html#m-room-message-msgtypes
|
356
|
+
# Possible message types as defined by the spec
|
357
|
+
def send_html(html, body = nil, msgtype: nil, format: nil)
|
358
|
+
content = {
|
359
|
+
body: body || html.gsub(/<\/?[^>]*>/, ''),
|
360
|
+
msgtype: msgtype || 'm.text',
|
361
|
+
format: format || 'org.matrix.custom.html',
|
362
|
+
formatted_body: html
|
363
|
+
}
|
364
|
+
|
365
|
+
client.api.send_message_event(id, 'm.room.message', content)
|
366
|
+
end
|
367
|
+
|
368
|
+
# Sends an emote (/me) message to the room
|
369
|
+
#
|
370
|
+
# @param text [String] the emote to send
|
371
|
+
def send_emote(text)
|
372
|
+
client.api.send_emote(id, text)
|
373
|
+
end
|
374
|
+
|
375
|
+
# Sends a link to a generic file to the room
|
376
|
+
#
|
377
|
+
# @param url [String,URI] the URL to the file
|
378
|
+
# @param name [String] the name of the file
|
379
|
+
# @param file_info [Hash] extra information about the file
|
380
|
+
# @option file_info [String] :mimetype the MIME type of the file
|
381
|
+
# @option file_info [Integer] :size the size of the file in bytes
|
382
|
+
# @option file_info [String,URI] :thumbnail_url the URL to a thumbnail of the file
|
383
|
+
# @option file_info [Hash] :thumbnail_info ThumbnailInfo about the thumbnail file
|
384
|
+
# @note The URLs should all be of the 'mxc://' schema
|
385
|
+
def send_file(url, name, file_info = {})
|
386
|
+
client.api.send_content(id, url, name, 'm.file', extra_information: file_info)
|
387
|
+
end
|
388
|
+
|
389
|
+
# Sends a notice (bot) message to the room
|
390
|
+
#
|
391
|
+
# @param text [String] the notice to send
|
392
|
+
def send_notice(text)
|
393
|
+
client.api.send_notice(id, text)
|
394
|
+
end
|
395
|
+
|
396
|
+
# Sends a link to an image to the room
|
397
|
+
#
|
398
|
+
# @param url [String,URI] the URL to the image
|
399
|
+
# @param name [String] the name of the image
|
400
|
+
# @param image_info [Hash] extra information about the image
|
401
|
+
# @option image_info [Integer] :h the height of the image in pixels
|
402
|
+
# @option image_info [Integer] :w the width of the image in pixels
|
403
|
+
# @option image_info [String] :mimetype the MIME type of the image
|
404
|
+
# @option image_info [Integer] :size the size of the image in bytes
|
405
|
+
# @option image_info [String,URI] :thumbnail_url the URL to a thumbnail of the image
|
406
|
+
# @option image_info [Hash] :thumbnail_info ThumbnailInfo about the thumbnail image
|
407
|
+
# @note The URLs should all be of the 'mxc://' schema
|
408
|
+
def send_image(url, name, image_info = {})
|
409
|
+
client.api.send_content(id, url, name, 'm.image', extra_information: image_info)
|
410
|
+
end
|
411
|
+
|
412
|
+
# Sends a location object to the room
|
413
|
+
#
|
414
|
+
# @param geo_uri [String,URI] the geo-URL (e.g. geo:<coords>) of the location
|
415
|
+
# @param name [String] the name of the location
|
416
|
+
# @param thumbnail_url [String,URI] the URL to a thumbnail image of the location
|
417
|
+
# @param thumbnail_info [Hash] a ThumbnailInfo for the location thumbnail
|
418
|
+
# @note The thumbnail URL should be of the 'mxc://' schema
|
419
|
+
def send_location(geo_uri, name, thumbnail_url = nil, thumbnail_info = {})
|
420
|
+
client.api.send_location(id, geo_uri, name, thumbnail_url: thumbnail_url, thumbnail_info: thumbnail_info)
|
421
|
+
end
|
422
|
+
|
423
|
+
# Sends a link to a video to the room
|
424
|
+
#
|
425
|
+
# @param url [String,URI] the URL to the video
|
426
|
+
# @param name [String] the name of the video
|
427
|
+
# @param video_info [Hash] extra information about the video
|
428
|
+
# @option video_info [Integer] :duration the duration of the video in milliseconds
|
429
|
+
# @option video_info [Integer] :h the height of the video in pixels
|
430
|
+
# @option video_info [Integer] :w the width of the video in pixels
|
431
|
+
# @option video_info [String] :mimetype the MIME type of the video
|
432
|
+
# @option video_info [Integer] :size the size of the video in bytes
|
433
|
+
# @option video_info [String,URI] :thumbnail_url the URL to a thumbnail of the video
|
434
|
+
# @option video_info [Hash] :thumbnail_info ThumbnailInfo about the thumbnail of the video
|
435
|
+
# @note The URLs should all be of the 'mxc://' schema
|
436
|
+
def send_video(url, name, video_info = {})
|
437
|
+
client.api.send_content(id, url, name, 'm.video', extra_information: video_info)
|
438
|
+
end
|
439
|
+
|
440
|
+
# Sends a link to an audio clip to the room
|
441
|
+
#
|
442
|
+
# @param url [String,URI] the URL to the audio clip
|
443
|
+
# @param name [String] the name of the audio clip
|
444
|
+
# @param audio_info [Hash] extra information about the audio clip
|
445
|
+
# @option audio_info [Integer] :duration the duration of the audio clip in milliseconds
|
446
|
+
# @option audio_info [String] :mimetype the MIME type of the audio clip
|
447
|
+
# @option audio_info [Integer] :size the size of the audio clip in bytes
|
448
|
+
# @note The URLs should all be of the 'mxc://' schema
|
449
|
+
def send_audio(url, name, audio_info = {})
|
450
|
+
client.api.send_content(id, url, name, 'm.audio', extra_information: audio_info)
|
451
|
+
end
|
452
|
+
|
453
|
+
# Sends a customized message to the Room
|
454
|
+
#
|
455
|
+
# @param body [String] The clear-text body of the message
|
456
|
+
# @param content [Hash] The custom content of the message
|
457
|
+
# @param msgtype [String] The type of the message, should be one of the known types (m.text, m.notice, m.emote, etc)
|
458
|
+
def send_custom_message(body, content = {}, msgtype: nil)
|
459
|
+
content.merge!(
|
460
|
+
body: body,
|
461
|
+
msgtype: msgtype || 'm.text'
|
462
|
+
)
|
463
|
+
|
464
|
+
client.api.send_message_event(id, 'm.room.message', content)
|
465
|
+
end
|
466
|
+
|
467
|
+
# Sends a custom timeline event to the Room
|
468
|
+
#
|
469
|
+
# @param type [String,Symbol] The type of the Event.
|
470
|
+
# For custom events, this should be written in reverse DNS format (e.g. com.example.event)
|
471
|
+
# @param content [Hash] The contents of the message, this will be the
|
472
|
+
# :content key of the resulting event object
|
473
|
+
# @see Protocols::CS#send_message_event
|
474
|
+
def send_event(type, content = {})
|
475
|
+
client.api.send_message_event(id, type, content)
|
476
|
+
end
|
477
|
+
|
478
|
+
# Redacts a message from the room
|
479
|
+
#
|
480
|
+
# @param event_id [String] the ID of the event to redact
|
481
|
+
# @param reason [String,nil] the reason for the redaction
|
482
|
+
def redact_message(event_id, reason = nil)
|
483
|
+
client.api.redact_event(id, event_id, reason: reason)
|
484
|
+
true
|
485
|
+
end
|
486
|
+
|
487
|
+
# Reports a message in the room
|
488
|
+
#
|
489
|
+
# @param event_id [MXID,String] The ID of the event to redact
|
490
|
+
# @param reason [String] The reason for the report
|
491
|
+
# @param score [Integer] The severity of the report in the range of -100 - 0
|
492
|
+
def report_message(event_id, reason:, score: -100)
|
493
|
+
client.api.report_event(id, event_id, reason: reason, score: score)
|
494
|
+
true
|
495
|
+
end
|
496
|
+
|
497
|
+
# Backfills messages into the room history
|
498
|
+
#
|
499
|
+
# @param reverse [Boolean] whether to fill messages in reverse or not
|
500
|
+
# @param limit [Integer] the maximum number of messages to backfill
|
501
|
+
# @note This will trigger the `on_event` events as messages are added
|
502
|
+
def backfill_messages(*args, reverse: false, limit: 10)
|
503
|
+
# To be backwards-compatible
|
504
|
+
if args.length == 2
|
505
|
+
reverse = args.first
|
506
|
+
limit = args.last
|
507
|
+
end
|
508
|
+
|
509
|
+
data = client.api.get_room_messages(id, @prev_batch, direction: :b, limit: limit)
|
510
|
+
|
511
|
+
events = data[:chunk]
|
512
|
+
events.reverse! unless reverse
|
513
|
+
events.each do |ev|
|
514
|
+
put_event(ev)
|
515
|
+
end
|
516
|
+
true
|
517
|
+
end
|
518
|
+
|
519
|
+
#
|
520
|
+
# User Management
|
521
|
+
#
|
522
|
+
|
523
|
+
# Invites a user into the room
|
524
|
+
#
|
525
|
+
# @param user_id [String,User] the MXID of the user
|
526
|
+
# @return [Boolean] wether the action succeeded
|
527
|
+
def invite_user(user_id)
|
528
|
+
user_id = user_id.id if user_id.is_a? MatrixSdk::User
|
529
|
+
client.api.invite_user(id, user_id)
|
530
|
+
true
|
531
|
+
end
|
532
|
+
|
533
|
+
# Kicks a user from the room
|
534
|
+
#
|
535
|
+
# @param user_id [String,User] the MXID of the user
|
536
|
+
# @param reason [String] the reason for the kick
|
537
|
+
# @return [Boolean] wether the action succeeded
|
538
|
+
def kick_user(user_id, reason = '')
|
539
|
+
user_id = user_id.id if user_id.is_a? MatrixSdk::User
|
540
|
+
client.api.kick_user(id, user_id, reason: reason)
|
541
|
+
true
|
542
|
+
end
|
543
|
+
|
544
|
+
# Bans a user from the room
|
545
|
+
#
|
546
|
+
# @param user_id [String,User] the MXID of the user
|
547
|
+
# @param reason [String] the reason for the ban
|
548
|
+
# @return [Boolean] wether the action succeeded
|
549
|
+
def ban_user(user_id, reason = '')
|
550
|
+
user_id = user_id.id if user_id.is_a? MatrixSdk::User
|
551
|
+
client.api.ban_user(id, user_id, reason: reason)
|
552
|
+
true
|
553
|
+
end
|
554
|
+
|
555
|
+
# Unbans a user from the room
|
556
|
+
#
|
557
|
+
# @param user_id [String,User] the MXID of the user
|
558
|
+
# @return [Boolean] wether the action succeeded
|
559
|
+
def unban_user(user_id)
|
560
|
+
user_id = user_id.id if user_id.is_a? MatrixSdk::User
|
561
|
+
client.api.unban_user(id, user_id)
|
562
|
+
true
|
563
|
+
end
|
564
|
+
|
565
|
+
# Requests to be removed from the room
|
566
|
+
#
|
567
|
+
# @return [Boolean] wether the request succeeded
|
568
|
+
def leave
|
569
|
+
client.api.leave_room(id)
|
570
|
+
client.instance_variable_get(:@rooms).delete id
|
571
|
+
true
|
572
|
+
end
|
573
|
+
|
574
|
+
def account_data
|
575
|
+
return MatrixSdk::Util::AccountDataCache.new client, room: self if client.cache == :none
|
576
|
+
|
577
|
+
@account_data ||= MatrixSdk::Util::AccountDataCache.new client, room: self
|
578
|
+
end
|
579
|
+
|
580
|
+
# Retrieves a custom entry from the room-specific account data
|
581
|
+
#
|
582
|
+
# @param type [String] the data type to retrieve
|
583
|
+
# @return [Hash] the data that was stored under the given type
|
584
|
+
def get_account_data(type)
|
585
|
+
account_data[type]
|
586
|
+
end
|
587
|
+
|
588
|
+
# Stores a custom entry into the room-specific account data
|
589
|
+
#
|
590
|
+
# @param type [String] the data type to store
|
591
|
+
# @param account_data [Hash] the data to store
|
592
|
+
def set_account_data(type, account_data)
|
593
|
+
self.account_data[type] = account_data
|
594
|
+
true
|
595
|
+
end
|
596
|
+
|
597
|
+
# Changes the room-specific user profile
|
598
|
+
#
|
599
|
+
# @param display_name [String] the new display name to use in the room
|
600
|
+
# @param avatar_url [String,URI] the new avatar URL to use in the room
|
601
|
+
# @note the avatar URL should be a mxc:// URI
|
602
|
+
def set_user_profile(display_name: nil, avatar_url: nil, reason: nil)
|
603
|
+
return nil unless display_name || avatar_url
|
604
|
+
|
605
|
+
data = client.api.get_membership(id, client.mxid)
|
606
|
+
raise "Can't set profile if you haven't joined the room" unless data[:membership] == 'join'
|
607
|
+
|
608
|
+
data[:displayname] = display_name unless display_name.nil?
|
609
|
+
data[:avatar_url] = avatar_url unless avatar_url.nil?
|
610
|
+
|
611
|
+
client.api.set_membership(id, client.mxid, 'join', reason || 'Updating room profile information', data)
|
612
|
+
true
|
613
|
+
end
|
614
|
+
|
615
|
+
# Gets the room creation information
|
616
|
+
#
|
617
|
+
# @return [Response] The content of the m.room.create event
|
618
|
+
def creation_info
|
619
|
+
room_state['m.room.create']
|
620
|
+
end
|
621
|
+
|
622
|
+
# Retrieves the type of the room
|
623
|
+
#
|
624
|
+
# @return ['m.space',String,nil] The type of the room
|
625
|
+
def room_type
|
626
|
+
# Can't change, so a permanent cache is ok
|
627
|
+
@room_type ||= creation_info[:type]
|
628
|
+
end
|
629
|
+
|
630
|
+
# Retrieves the room version
|
631
|
+
#
|
632
|
+
# @return [String] The version of the room
|
633
|
+
def room_version
|
634
|
+
# Can't change, so a permanent cache is ok
|
635
|
+
@room_version ||= creation_info[:room_version]
|
636
|
+
end
|
637
|
+
|
638
|
+
# Checks if the room is a Matrix Space
|
639
|
+
#
|
640
|
+
# @return [Boolean,nil] True if the room is a space
|
641
|
+
def space?
|
642
|
+
room_type == 'm.space'
|
643
|
+
rescue MatrixSdk::MatrixForbiddenError, MatrixSdk::MatrixNotFoundError
|
644
|
+
nil
|
645
|
+
end
|
646
|
+
|
647
|
+
# Returns a list of the room tags
|
648
|
+
#
|
649
|
+
# @return [Response] A list of the tags and their data, with add and remove methods implemented
|
650
|
+
# @example Managing tags
|
651
|
+
# room.tags
|
652
|
+
# # => { :room_tag => { data: false } }
|
653
|
+
# room.tags.add('some_tag', data: true)
|
654
|
+
# # => { :some_tag => { data: true }, :room_tag => { data: false} }
|
655
|
+
# room.tags.remove('room_tag')
|
656
|
+
# # => { :some_tag => { data: true} }
|
657
|
+
def tags
|
658
|
+
client.api.get_user_tags(client.mxid, id)[:tags].tap do |tag_obj|
|
659
|
+
tag_obj.instance_variable_set(:@room, self)
|
660
|
+
tag_obj.define_singleton_method(:room) do
|
661
|
+
@room
|
662
|
+
end
|
663
|
+
tag_obj.define_singleton_method(:add) do |tag, **data|
|
664
|
+
@room.add_tag(tag.to_s.to_sym, **data)
|
665
|
+
self[tag.to_s.to_sym] = data
|
666
|
+
self
|
667
|
+
end
|
668
|
+
tag_obj.define_singleton_method(:remove) do |tag|
|
669
|
+
@room.remove_tag(tag.to_s.to_sym)
|
670
|
+
delete tag.to_s.to_sym
|
671
|
+
end
|
672
|
+
end
|
673
|
+
end
|
674
|
+
|
675
|
+
# Remove a tag from the room
|
676
|
+
#
|
677
|
+
# @param [String] tag The tag to remove
|
678
|
+
def remove_tag(tag)
|
679
|
+
client.api.remove_user_tag(client.mxid, id, tag)
|
680
|
+
true
|
681
|
+
end
|
682
|
+
|
683
|
+
# Add a tag to the room
|
684
|
+
#
|
685
|
+
# @param [String] tag The tag to add
|
686
|
+
# @param [Hash] data The data to assign to the tag
|
687
|
+
def add_tag(tag, **data)
|
688
|
+
client.api.add_user_tag(client.mxid, id, tag, data)
|
689
|
+
true
|
690
|
+
end
|
691
|
+
|
692
|
+
#
|
693
|
+
# State updates
|
694
|
+
#
|
695
|
+
|
696
|
+
# Refreshes the room state caches for name, topic, and aliases
|
697
|
+
def reload!
|
698
|
+
reload_name!
|
699
|
+
reload_topic!
|
700
|
+
reload_aliases!
|
701
|
+
true
|
702
|
+
end
|
703
|
+
alias refresh! reload!
|
704
|
+
|
705
|
+
# Sets a new name on the room
|
706
|
+
#
|
707
|
+
# @param name [String] The new name to set
|
708
|
+
def name=(name)
|
709
|
+
room_state['m.room.name'] = { name: name }
|
710
|
+
name
|
711
|
+
end
|
712
|
+
|
713
|
+
# Reloads the name of the room
|
714
|
+
#
|
715
|
+
# @return [Boolean] if the name was changed or not
|
716
|
+
def reload_name!
|
717
|
+
room_state.expire('m.room.name')
|
718
|
+
end
|
719
|
+
alias refresh_name! reload_name!
|
720
|
+
|
721
|
+
# Sets a new topic on the room
|
722
|
+
#
|
723
|
+
# @param topic [String] The new topic to set
|
724
|
+
def topic=(topic)
|
725
|
+
room_state['m.room.topic'] = { topic: topic }
|
726
|
+
topic
|
727
|
+
end
|
728
|
+
|
729
|
+
# Reloads the topic of the room
|
730
|
+
#
|
731
|
+
# @return [Boolean] if the topic was changed or not
|
732
|
+
def reload_topic!
|
733
|
+
room_state.expire('m.room.topic')
|
734
|
+
end
|
735
|
+
alias refresh_topic! reload_topic!
|
736
|
+
|
737
|
+
# Add an alias to the room
|
738
|
+
#
|
739
|
+
# @return [Boolean] if the addition was successful or not
|
740
|
+
def add_alias(room_alias)
|
741
|
+
client.api.set_room_alias(id, room_alias)
|
742
|
+
tinycache_adapter.read(:aliases) << room_alias if tinycache_adapter.exist?(:aliases)
|
743
|
+
true
|
744
|
+
end
|
745
|
+
|
746
|
+
# Reloads the list of aliases by an API query
|
747
|
+
#
|
748
|
+
# @return [Boolean] if the alias list was updated or not
|
749
|
+
# @note The list of aliases is not sorted, ordering changes will result in
|
750
|
+
# alias list updates.
|
751
|
+
def reload_aliases!
|
752
|
+
room_state.expire('m.room.canonical_alias')
|
753
|
+
clear_aliases_cache
|
754
|
+
end
|
755
|
+
alias refresh_aliases! reload_aliases!
|
756
|
+
|
757
|
+
# Sets if the room should be invite only or not
|
758
|
+
#
|
759
|
+
# @param invite_only [Boolean] If it should be invite only or not
|
760
|
+
def invite_only=(invite_only)
|
761
|
+
self.join_rule = invite_only ? :invite : :public
|
762
|
+
invite_only
|
763
|
+
end
|
764
|
+
|
765
|
+
# Sets the join rule of the room
|
766
|
+
#
|
767
|
+
# @param join_rule [:invite,:public] The join rule of the room
|
768
|
+
def join_rule=(join_rule)
|
769
|
+
room_state['m.room.join_rules'] = { join_rule: join_rule }
|
770
|
+
join_rule
|
771
|
+
end
|
772
|
+
|
773
|
+
# Sets if guests are allowed in the room
|
774
|
+
#
|
775
|
+
# @param allow_guests [Boolean] If guests are allowed to join or not
|
776
|
+
def allow_guests=(allow_guests)
|
777
|
+
self.guest_access = (allow_guests ? :can_join : :forbidden)
|
778
|
+
allow_guests
|
779
|
+
end
|
780
|
+
|
781
|
+
# Sets the guest access status for the room
|
782
|
+
#
|
783
|
+
# @param guest_access [:can_join,:forbidden] The new guest access status of the room
|
784
|
+
def guest_access=(guest_access)
|
785
|
+
room_state['m.room.guest_access'] = { guest_access: guest_access }
|
786
|
+
guest_access
|
787
|
+
end
|
788
|
+
|
789
|
+
# Sets a new avatar URL for the room
|
790
|
+
#
|
791
|
+
# @param avatar_url [URI::MXC] The mxc:// URL for the new room avatar
|
792
|
+
def avatar_url=(avatar_url)
|
793
|
+
avatar_url = URI(avatar_url) unless avatar_url.is_a? URI
|
794
|
+
raise ArgumentError, 'Must be a valid MXC URL' unless avatar_url.is_a? URI::MXC
|
795
|
+
|
796
|
+
room_state['m.room.avatar_url'] = { avatar_url: avatar_url }
|
797
|
+
avatar_url
|
798
|
+
end
|
799
|
+
|
800
|
+
# Get the power levels of the room
|
801
|
+
#
|
802
|
+
# @note The returned power levels are cached for a minute
|
803
|
+
# @return [Hash] The current power levels as set for the room
|
804
|
+
# @see Protocols::CS#get_power_levels
|
805
|
+
def power_levels
|
806
|
+
get_state('m.room.power_levels')
|
807
|
+
end
|
808
|
+
|
809
|
+
# Gets the power level of a user in the room
|
810
|
+
#
|
811
|
+
# @param user [User,MXID,String] The user to check the power level for
|
812
|
+
# @param use_default [Boolean] Should the user default level be checked if no user-specific one exists
|
813
|
+
# @return [Integer,nil] The current power level for the requested user, nil if there's no user specific level
|
814
|
+
# and use_default is false
|
815
|
+
def user_powerlevel(user, use_default: true)
|
816
|
+
user = user.id if user.is_a? User
|
817
|
+
user = MXID.new(user.to_s) unless user.is_a? MXID
|
818
|
+
raise ArgumentError, 'Must provide a valid User or MXID' unless user.user?
|
819
|
+
|
820
|
+
level = power_levels.dig(:users, user.to_s.to_sym)
|
821
|
+
level ||= power_levels[:users_default] || 0 if use_default
|
822
|
+
level
|
823
|
+
end
|
824
|
+
|
825
|
+
# Checks if a user can send a given event type in the room
|
826
|
+
#
|
827
|
+
# @param user [User,MXID,String] The user to check
|
828
|
+
# @param event [String,Symbol] The event type to check
|
829
|
+
# @param state [Boolean] If the given event is a state event or a message event
|
830
|
+
# @return [Boolean] If the given user is allowed to send an event of the given type
|
831
|
+
def user_can_send?(user, event, state: false)
|
832
|
+
user_pl = user_powerlevel(user)
|
833
|
+
event_pl = power_levels.dig(:events, event.to_s.to_sym)
|
834
|
+
event_pl ||= state ? (power_levels[:state_default] || 50) : (power_levels[:events_default] || 0)
|
835
|
+
|
836
|
+
user_pl >= event_pl
|
837
|
+
end
|
838
|
+
|
839
|
+
# Check if a user is an admin in the room
|
840
|
+
#
|
841
|
+
# @param user [User,MXID,String] The user to check for admin privileges
|
842
|
+
# @param target_level [Integer] The power level that's to be considered as admin privileges
|
843
|
+
# @return [Boolean] If the requested user has a power level highe enough to be an admin
|
844
|
+
# @see #user_powerlevel
|
845
|
+
def admin?(user, target_level: 100)
|
846
|
+
level = user_powerlevel(user, use_default: false)
|
847
|
+
return false unless level
|
848
|
+
|
849
|
+
level >= target_level
|
850
|
+
end
|
851
|
+
|
852
|
+
# Make a user an admin in the room
|
853
|
+
#
|
854
|
+
# @param user [User,MXID,String] The user to give admin privileges
|
855
|
+
# @param level [Integer] The power level to set the user to
|
856
|
+
# @see #modify_user_power_levels
|
857
|
+
def admin!(user, level: 100)
|
858
|
+
return true if admin?(user, target_level: level)
|
859
|
+
|
860
|
+
user = user.id if user.is_a? User
|
861
|
+
user = MXID.new(user.to_s) unless user.is_a? MXID
|
862
|
+
raise ArgumentError, 'Must provide a valid user or MXID' unless user.user?
|
863
|
+
|
864
|
+
modify_user_power_levels({ user.to_s.to_sym => level })
|
865
|
+
end
|
866
|
+
|
867
|
+
# Check if a user is a moderator in the room
|
868
|
+
#
|
869
|
+
# @param user [User,MXID,String] The user to check for admin privileges
|
870
|
+
# @param target_level [Integer] The power level that's to be considered as admin privileges
|
871
|
+
# @return [Boolean] If the requested user has a power level highe enough to be an admin
|
872
|
+
# @see #user_powerlevel
|
873
|
+
def moderator?(user, target_level: 50)
|
874
|
+
level = user_powerlevel(user, use_default: false)
|
875
|
+
return false unless level
|
876
|
+
|
877
|
+
level >= target_level
|
878
|
+
end
|
879
|
+
|
880
|
+
# Make a user a moderator in the room
|
881
|
+
#
|
882
|
+
# @param user [User,MXID,String] The user to give moderator privileges
|
883
|
+
# @param level [Integer] The power level to set the user to
|
884
|
+
# @see #modify_user_power_levels
|
885
|
+
def moderator!(user, level: 50)
|
886
|
+
return true if moderator?(user, target_level: level)
|
887
|
+
|
888
|
+
user = user.id if user.is_a? User
|
889
|
+
user = MXID.new(user.to_s) unless user.is_a? MXID
|
890
|
+
raise ArgumentError, 'Must provide a valid user or MXID' unless user.user?
|
891
|
+
|
892
|
+
modify_user_power_levels({ user.to_s.to_sym => level })
|
893
|
+
end
|
894
|
+
|
895
|
+
# Modifies the power levels of the room
|
896
|
+
#
|
897
|
+
# @param users [Hash] the user-specific power levels to set or remove
|
898
|
+
# @param users_default [Hash] the default user power levels to set
|
899
|
+
# @return [Boolean] if the change was successful
|
900
|
+
def modify_user_power_levels(users = nil, users_default = nil)
|
901
|
+
return false if users.nil? && users_default.nil?
|
902
|
+
|
903
|
+
room_state.tinycache_adapter.expire 'm.room.power_levels'
|
904
|
+
|
905
|
+
data = power_levels
|
906
|
+
data[:users_default] = users_default unless users_default.nil?
|
907
|
+
|
908
|
+
if users
|
909
|
+
data[:users] = {} unless data.key? :users
|
910
|
+
users.each do |user, level|
|
911
|
+
user = user.id if user.is_a? User
|
912
|
+
user = MXID.new(user.to_s) unless user.is_a? MXID
|
913
|
+
raise ArgumentError, 'Must provide a valid user or MXID' unless user.user?
|
914
|
+
|
915
|
+
if level.nil?
|
916
|
+
data[:users].delete(user.to_s.to_sym)
|
917
|
+
else
|
918
|
+
data[:users][user.to_s.to_sym] = level
|
919
|
+
end
|
920
|
+
end
|
921
|
+
end
|
922
|
+
|
923
|
+
room_state['m.room.power_levels'] = data
|
924
|
+
true
|
925
|
+
end
|
926
|
+
|
927
|
+
# Modifies the required power levels for actions in the room
|
928
|
+
#
|
929
|
+
# @param events [Hash] the event-specific power levels to change
|
930
|
+
# @param params [Hash] other power-level params to change
|
931
|
+
# @return [Boolean] if the change was successful
|
932
|
+
def modify_required_power_levels(events = nil, params = {})
|
933
|
+
return false if events.nil? && (params.nil? || params.empty?)
|
934
|
+
|
935
|
+
room_state.tinycache_adapter.expire 'm.room.power_levels'
|
936
|
+
|
937
|
+
data = power_levels
|
938
|
+
data.merge!(params)
|
939
|
+
data.delete_if { |_k, v| v.nil? }
|
940
|
+
|
941
|
+
if events
|
942
|
+
data[:events] = {} unless data.key? :events
|
943
|
+
data[:events].merge!(events)
|
944
|
+
data[:events].delete_if { |_k, v| v.nil? }
|
945
|
+
end
|
946
|
+
|
947
|
+
room_state['m.room.power_levels'] = data
|
948
|
+
true
|
949
|
+
end
|
950
|
+
|
951
|
+
private
|
952
|
+
|
953
|
+
def ensure_member(member)
|
954
|
+
return unless client.cache == :all
|
955
|
+
|
956
|
+
tinycache_adapter.write(:joined_members, []) unless tinycache_adapter.exist? :joined_members
|
957
|
+
|
958
|
+
members = tinycache_adapter.read(:joined_members) || []
|
959
|
+
members << member unless members.any? { |m| m.id == member.id }
|
960
|
+
|
961
|
+
tinycache_adapter.write(:joined_members, members)
|
962
|
+
end
|
963
|
+
|
964
|
+
def handle_room_member(event)
|
965
|
+
return unless client.cache == :all
|
966
|
+
|
967
|
+
if event.dig(*%i[content membership]) == 'join'
|
968
|
+
ensure_member(client.get_user(event[:state_key]).dup.tap do |u|
|
969
|
+
u.instance_variable_set(:@display_name, event.dig(*%i[content displayname]))
|
970
|
+
end)
|
971
|
+
elsif tinycache_adapter.exist? :joined_members
|
972
|
+
members = tinycache_adapter.read(:joined_members)
|
973
|
+
members.delete_if { |m| m.id == event[:state_key] }
|
974
|
+
end
|
975
|
+
end
|
976
|
+
|
977
|
+
def handle_room_canonical_alias(event)
|
978
|
+
room_state.tinycache_adapter.write('m.room.canonical_alias', event[:content], expires_in: room_state.cache_time)
|
979
|
+
canonical_alias = event.dig(*%i[content alias])
|
980
|
+
|
981
|
+
data = tinycache_adapter.read(:aliases) || []
|
982
|
+
data << canonical_alias
|
983
|
+
data += event.dig(*%i[content alt_aliases]) || []
|
984
|
+
tinycache_adapter.write(:aliases, data.uniq.sort)
|
985
|
+
end
|
986
|
+
|
987
|
+
def room_handlers?
|
988
|
+
client.instance_variable_get(:@room_handlers).key? id
|
989
|
+
end
|
990
|
+
|
991
|
+
def ensure_room_handlers
|
992
|
+
client.instance_variable_get(:@room_handlers)[id] ||= {
|
993
|
+
account_data: MatrixSdk::EventHandlerArray.new,
|
994
|
+
event: MatrixSdk::EventHandlerArray.new,
|
995
|
+
state_event: MatrixSdk::EventHandlerArray.new,
|
996
|
+
ephemeral_event: MatrixSdk::EventHandlerArray.new
|
997
|
+
}
|
998
|
+
end
|
999
|
+
|
1000
|
+
def put_event(event)
|
1001
|
+
ensure_room_handlers[:event].fire(MatrixEvent.new(self, event), event[:type]) if room_handlers?
|
1002
|
+
|
1003
|
+
@events.push event
|
1004
|
+
@events.shift if @events.length > @event_history_limit
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
def put_account_data(event)
|
1008
|
+
if client.cache != :none
|
1009
|
+
adapter = account_data.tinycache_adapter
|
1010
|
+
adapter.write(event[:type], event[:content], expires_in: account_data.cache_time)
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
return unless room_handlers?
|
1014
|
+
|
1015
|
+
ensure_room_handlers[:account_data].fire(MatrixEvent.new(self, event))
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
def put_ephemeral_event(event)
|
1019
|
+
return unless room_handlers?
|
1020
|
+
|
1021
|
+
ensure_room_handlers[:ephemeral_event].fire(MatrixEvent.new(self, event), event[:type])
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
INTERNAL_HANDLERS = {
|
1025
|
+
'm.room.canonical_alias' => :handle_room_canonical_alias,
|
1026
|
+
'm.room.member' => :handle_room_member
|
1027
|
+
}.freeze
|
1028
|
+
|
1029
|
+
def put_state_event(event)
|
1030
|
+
if INTERNAL_HANDLERS.key? event[:type]
|
1031
|
+
send(INTERNAL_HANDLERS[event[:type]], event)
|
1032
|
+
elsif client.cache != :none
|
1033
|
+
adapter = room_state.tinycache_adapter
|
1034
|
+
key = event[:type]
|
1035
|
+
key += "|#{event[:state_key]}" unless event[:state_key].nil? || event[:state_key].empty?
|
1036
|
+
adapter.write(key, event[:content], expires_in: room_state.cache_time)
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
return unless room_handlers?
|
1040
|
+
|
1041
|
+
ensure_room_handlers[:state_event].fire(MatrixEvent.new(self, event), event[:type])
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
end
|