Class: ActiveMatrix::Room

Inherits:
Object show all
Extended by:
Extensions
Includes:
Logging, Util::Cacheable
Defined in:
lib/active_matrix/room.rb

Overview

A class for tracking the information about a room on Matrix

Direct Known Subclasses

ActiveMatrix::Rooms::Space

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Extensions

ignore_inspect

Methods included from Util::Cacheable

#from_cache?, #to_cache

Methods included from Logging

included, #logger, #logger=

Constructor Details

#initialize(client, room_id, data = {}) ⇒ Room

Note:

This method isn’t supposed to be used directly, rather rooms should be retrieved from the Client abstraction.

Create a new room instance

Parameters:

  • client (Client)

    The underlying connection

  • room_id (MXID)

    The room ID

  • data (Hash) (defaults to: {})

    Additional data to assign to the room

Options Hash (data):

  • :name (String)

    The current name of the room

  • :topic (String)

    The current topic of the room

  • :canonical_alias (String, MXID)

    The canonical alias of the room

  • :aliases (Array(String, MXID))

    All non-canonical aliases of the room

  • :join_rule (:invite, :public, :knock)

    The join rule for the room

  • :guest_access (:can_join, :forbidden)

    The guest access setting for the room

  • :world_readable (Boolean)

    If the room is readable by the entire world

  • :members (Array(User))

    The list of joined members

  • :events (Array(Object))

    The list of current events in the room

  • :members_loaded (Boolean)

    If the list of members is already loaded

  • :event_history_limit (Integer) — default: 10

    The limit of events to store for the room

  • :avatar_url (String, URI)

    The avatar URL for the room

  • :prev_batch (String)

    The previous batch token for backfill

Raises:

  • (ArgumentError)


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/active_matrix/room.rb', line 52

def initialize(client, room_id, data = {})
  if client.is_a? Room
    copy = client
    client = copy.client
    room_id = copy.id
    # data = copy.attributes
  end

  raise ArgumentError, 'Must be given a Client instance' unless client.is_a? Client

  @client = client
  room_id = MXID.new room_id unless room_id.is_a?(MXID)
  raise ArgumentError, 'room_id must be a valid Room ID' unless room_id.room_id?

  @events = []
  @event_history_limit = 10
  @room_type = nil
  @pre_populated_members = nil # For pre-populated members from tests
  @cached_joined_members = nil # Instance cache for joined members
  @cached_all_members = nil    # Instance cache for all members

  @prev_batch = nil

  i[name topic canonical_alias avatar_url].each do |type|
    room_state.write("m.room.#{type}", { type => data.delete(type) }) if data.key? type
  end
  room_state.write('m.room.join_rules', { join_rule: data.delete(:join_rule) }) if data.key? :join_rule
  room_state.write('m.room.history_visibility', { history_visibility: data.delete(:world_readable) ? :world_readable : nil }) if data.key? :world_readable

  data.each do |k, v|
    next if i[client].include? k

    instance_variable_set("@#{k}", v) if instance_variable_defined? "@#{k}"
  end

  @id = room_id.to_s

  logger.debug "Created room #{room_id}"
end

Instance Attribute Details

#clientClient (readonly)

Returns the client for the room.

Returns:

  • (Client)

    the client for the room



21
# File 'lib/active_matrix/room.rb', line 21

attr_reader :id, :client, :events

#event_history_limitFixnum

Returns the limit of events to keep in the event log.

Returns:

  • (Fixnum)

    the limit of events to keep in the event log



12
13
14
# File 'lib/active_matrix/room.rb', line 12

def event_history_limit
  @event_history_limit
end

#eventsObject (readonly)

Returns the value of attribute events.



21
# File 'lib/active_matrix/room.rb', line 21

attr_reader :id, :client, :events

#idString (readonly) Also known as: room_id

Returns the internal ID of the room.

Returns:

  • (String)

    the internal ID of the room



21
22
23
# File 'lib/active_matrix/room.rb', line 21

def id
  @id
end

#on_account_dataObject (readonly)



121
122
123
# File 'lib/active_matrix/room.rb', line 121

def 
  ensure_room_handlers[:account_data]
end

#on_ephemeral_eventObject (readonly)



133
134
135
# File 'lib/active_matrix/room.rb', line 133

def on_ephemeral_event
  ensure_room_handlers[:ephemeral_event]
end

#on_eventObject (readonly)



115
116
117
# File 'lib/active_matrix/room.rb', line 115

def on_event
  ensure_room_handlers[:event]
end

#on_state_eventObject (readonly)



127
128
129
# File 'lib/active_matrix/room.rb', line 127

def on_state_event
  ensure_room_handlers[:state_event]
end

Instance Method Details

#account_dataObject



624
625
626
627
628
# File 'lib/active_matrix/room.rb', line 624

def 
  return ActiveMatrix::Util::AccountDataCache.new client, room: self if client.cache == :none

  @account_data ||= ActiveMatrix::Util::AccountDataCache.new client, room: self
end

#add_alias(room_alias) ⇒ Boolean

Add an alias to the room

Returns:

  • (Boolean)

    if the addition was successful or not



790
791
792
793
794
795
# File 'lib/active_matrix/room.rb', line 790

def add_alias(room_alias)
  client.api.set_room_alias(id, room_alias)
  # Clear the cache to force refresh
  cache.delete(cache_key(:aliases)) if cache_available?
  true
end

#add_tag(tag, **data) ⇒ Object

Add a tag to the room

Parameters:

  • tag (String)

    The tag to add

  • data (Hash)

    The data to assign to the tag



737
738
739
740
# File 'lib/active_matrix/room.rb', line 737

def add_tag(tag, **data)
  client.api.add_user_tag(client.mxid, id, tag, data)
  true
end

#admin!(user, level: 100) ⇒ Object

Make a user an admin in the room

Parameters:

  • user (User, MXID, String)

    The user to give admin privileges

  • level (Integer) (defaults to: 100)

    The power level to set the user to

Raises:

  • (ArgumentError)

See Also:



908
909
910
911
912
913
914
915
916
# File 'lib/active_matrix/room.rb', line 908

def admin!(user, level: 100)
  return true if admin?(user, target_level: level)

  user = user.id if user.is_a? User
  user = MXID.new(user.to_s) unless user.is_a? MXID
  raise ArgumentError, 'Must provide a valid user or MXID' unless user.user?

  modify_user_power_levels({ user.to_s.to_sym => level })
end

#admin?(user, target_level: 100) ⇒ Boolean

Check if a user is an admin in the room

Parameters:

  • user (User, MXID, String)

    The user to check for admin privileges

  • target_level (Integer) (defaults to: 100)

    The power level that’s to be considered as admin privileges

Returns:

  • (Boolean)

    If the requested user has a power level highe enough to be an admin

See Also:



896
897
898
899
900
901
# File 'lib/active_matrix/room.rb', line 896

def admin?(user, target_level: 100)
  level = user_powerlevel(user, use_default: false)
  return false unless level

  level >= target_level
end

#aliases(canonical_only: true) ⇒ Array[String]

Gets the room aliases

Parameters:

  • canonical_only (Boolean) (defaults to: true)

    Should the list of aliases only contain the canonical ones

Returns:

  • (Array[String])

    The assigned room aliases



367
368
369
370
371
372
373
# File 'lib/active_matrix/room.rb', line 367

def aliases(canonical_only: true)
  return fetch_aliases(canonical_only: canonical_only) if !canonical_only || client.cache == :none || !cache_available?

  cache.fetch(cache_key(:aliases), expires_in: 1.hour) do
    fetch_aliases(canonical_only: true)
  end
end

#all_members(**params) ⇒ Array(User)

Note:

This will also count members who’ve knocked, been invited, have left, or have been banned.

Get all members (member events) in the room

Parameters:

  • params (Hash)

    Additional query parameters to pass to the room member listing - e.g. for filtering purposes.

Returns:

  • (Array(User))

    The complete list of members in the room, regardless of membership state



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/active_matrix/room.rb', line 222

def all_members(**params)
  # Return pre-populated members if they exist and no filtering params (for testing)
  return @pre_populated_members if @pre_populated_members && params.empty?

  # Return cached instance if available and no params
  return @cached_all_members if @cached_all_members && params.empty?

  return fetch_all_members(**params) if !params.empty? || client.cache == :none || !cache_available?

  # Cache the raw member state keys, not User objects
  members_data = cache.fetch(cache_key(:all_members), expires_in: 1.hour) do
    client.api.get_room_members(id, **params)[:chunk].map { |ch| ch[:state_key] }
  end

  # Reconstruct User objects from cached data and cache at instance level
  @cached_all_members = members_data.map { |state_key| client.get_user(state_key) }
end

#allow_guests=(allow_guests) ⇒ Object

Sets if guests are allowed in the room

Parameters:

  • allow_guests (Boolean)

    If guests are allowed to join or not



827
828
829
830
# File 'lib/active_matrix/room.rb', line 827

def allow_guests=(allow_guests)
  self.guest_access = (allow_guests ? :can_join : :forbidden)
  allow_guests
end

#avatar_urlString?

Gets the avatar url of the room - if any

Returns:

  • (String, nil)

    The avatar URL - if any



286
287
288
289
290
291
# File 'lib/active_matrix/room.rb', line 286

def avatar_url
  get_state('m.room.avatar_url')[:url]
rescue MatrixNotFoundError
  # No avatar has been set
  nil
end

#avatar_url=(avatar_url) ⇒ Object

Sets a new avatar URL for the room

Parameters:

  • avatar_url (URI::MXC)

    The mxc:// URL for the new room avatar

Raises:

  • (ArgumentError)


843
844
845
846
847
848
849
# File 'lib/active_matrix/room.rb', line 843

def avatar_url=(avatar_url)
  avatar_url = URI(avatar_url) unless avatar_url.is_a? URI
  raise ArgumentError, 'Must be a valid MXC URL' unless avatar_url.is_a? URI::MXC

  room_state['m.room.avatar_url'] = { avatar_url: avatar_url }
  avatar_url
end

#backfill_messages(*args, reverse: false, limit: 10) ⇒ Object

Note:

This will trigger the ‘on_event` events as messages are added

Backfills messages into the room history

Parameters:

  • reverse (Boolean) (defaults to: false)

    whether to fill messages in reverse or not

  • limit (Integer) (defaults to: 10)

    the maximum number of messages to backfill



552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/active_matrix/room.rb', line 552

def backfill_messages(*args, reverse: false, limit: 10)
  # To be backwards-compatible
  if args.length == 2
    reverse = args.first
    limit = args.last
  end

  data = client.api.get_room_messages(id, @prev_batch, direction: :b, limit: limit)

  events = data[:chunk]
  events.reverse! unless reverse
  events.each do |ev|
    put_event(ev)
  end
  true
end

#ban_user(user_id, reason = '') ⇒ Boolean

Bans a user from the room

Parameters:

  • user_id (String, User)

    the MXID of the user

  • reason (String) (defaults to: '')

    the reason for the ban

Returns:

  • (Boolean)

    wether the action succeeded



599
600
601
602
603
# File 'lib/active_matrix/room.rb', line 599

def ban_user(user_id, reason = '')
  user_id = user_id.id if user_id.is_a? ActiveMatrix::User
  client.api.ban_user(id, user_id, reason: reason)
  true
end

#canonical_aliasString?

Returns the canonical alias of the room.

Returns:

  • (String, nil)

    the canonical alias of the room



166
167
168
169
170
# File 'lib/active_matrix/room.rb', line 166

def canonical_alias
  get_state('m.room.canonical_alias')[:alias]
rescue ActiveMatrix::MatrixNotFoundError
  nil
end

#creation_infoResponse

Gets the room creation information

Returns:

  • (Response)

    The content of the m.room.create event



668
669
670
# File 'lib/active_matrix/room.rb', line 668

def creation_info
  room_state['m.room.create']
end

#display_nameString

Note:

This method will populate the #members list if it has to fall back to the member name generation.

Gets a human-readable name for the room

This will return #name or #canonical_alias if they’ve been set, otherwise it will query the API for members and generate a string from a subset of their names.

Returns:

  • (String)

    a human-readable name for the room



150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/active_matrix/room.rb', line 150

def display_name
  return name if name
  return canonical_alias if canonical_alias

  members = joined_members
            .reject { |m| m.user_id == client.mxid }
            .map(&:display_name)

  return members.first if members.one?
  return "#{members.first} and #{members.last}" if members.count == 2
  return "#{members.first} and #{members.count - 1} others" if members.count > 2

  'Empty Room'
end

#dm=(direct) ⇒ Object

Mark a room as a direct (1:1) message Room



268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/active_matrix/room.rb', line 268

def dm=(direct)
  rooms = client.direct_rooms
  dirty = false
  list_for_room = (rooms[id.to_s] ||= [])
  if direct && !list_for_room.include?(id.to_s)
    list_for_room << id.to_s
    dirty = true
  elsif !direct && list_for_room.include?(id.to_s)
    list_for_room.delete id.to_s
    rooms.delete id.to_s if list_for_room.empty?
    dirty = true
  end
  client.['m.direct'] = rooms if dirty
end

#dm?(members_only: false) ⇒ Boolean

Checks if the room is a direct message / 1:1 room

Parameters:

  • members_only (Boolean) (defaults to: false)

    Should directness only care about member count?

Returns:

  • (Boolean)


261
262
263
264
265
# File 'lib/active_matrix/room.rb', line 261

def dm?(members_only: false)
  return true if !members_only && client.direct_rooms.any? { |_uid, rooms| rooms.include? id.to_s }

  joined_members.count <= 2
end

#fetch_aliases(canonical_only: true) ⇒ Object



375
376
377
378
379
380
381
382
383
384
385
# File 'lib/active_matrix/room.rb', line 375

def fetch_aliases(canonical_only: true)
  canonical = get_state('m.room.canonical_alias') rescue {}
  # Handle both hash-like and Response objects
  alias_value = canonical.respond_to?(:alias) ? canonical.alias : canonical[:alias]
  alt_aliases = canonical.respond_to?(:alt_aliases) ? canonical.alt_aliases : canonical[:alt_aliases]

  aliases = ([alias_value].compact + (alt_aliases || [])).uniq.sort
  return aliases if canonical_only

  (aliases + client.api.get_room_aliases(id).aliases).uniq.sort
end

#fetch_all_members(**params) ⇒ Object



240
241
242
# File 'lib/active_matrix/room.rb', line 240

def fetch_all_members(**params)
  client.api.get_room_members(id, **params)[:chunk].map { |ch| client.get_user(ch[:state_key]) }
end

#fetch_joined_membersObject



205
206
207
208
209
210
211
# File 'lib/active_matrix/room.rb', line 205

def fetch_joined_members
  client.api.get_room_joined_members(id)[:joined].map do |mxid, data|
    User.new(client, mxid.to_s,
             display_name: data.fetch(:display_name, nil),
             avatar_url: data.fetch(:avatar_url, nil))
  end
end

#get_account_data(type) ⇒ Hash

Retrieves a custom entry from the room-specific account data

Parameters:

  • type (String)

    the data type to retrieve

Returns:

  • (Hash)

    the data that was stored under the given type



634
635
636
# File 'lib/active_matrix/room.rb', line 634

def (type)
  [type]
end

#get_state(type, state_key: nil) ⇒ Object

Gets a state object in the room



339
340
341
# File 'lib/active_matrix/room.rb', line 339

def get_state(type, state_key: nil)
  room_state[type, state_key]
end

#guest_access:can_join, :forbidden

Gets the guest access rights for the room

Returns:

  • (:can_join, :forbidden)

    The current guest access right



306
307
308
# File 'lib/active_matrix/room.rb', line 306

def guest_access
  get_state('m.room.guest_access')[:guest_access]&.to_sym
end

#guest_access=(guest_access) ⇒ Object

Sets the guest access status for the room

Parameters:

  • guest_access (:can_join, :forbidden)

    The new guest access status of the room



835
836
837
838
# File 'lib/active_matrix/room.rb', line 835

def guest_access=(guest_access)
  room_state['m.room.guest_access'] = { guest_access: guest_access }
  guest_access
end

#guest_access?Boolean

Checks if guest_access is set to :can_join

Returns:

  • (Boolean)


318
319
320
# File 'lib/active_matrix/room.rb', line 318

def guest_access?
  guest_access == :can_join
end

#history_visibility:invited, ...

Gets the history visibility of the room

Returns:

  • (:invited, :joined, :shared, :world_readable)

    The current history visibility for the room



351
352
353
# File 'lib/active_matrix/room.rb', line 351

def history_visibility
  get_state('m.room.history_visibility')[:history_visibility]&.to_sym
end

#inspectString

An inspect method that skips a handful of instance variables to avoid flooding the terminal with debug data.

Returns:

  • (String)

    a regular inspect string without the data for some variables



27
# File 'lib/active_matrix/room.rb', line 27

ignore_inspect :client, :events, :prev_batch, :logger

#invite_only=(invite_only) ⇒ Object

Sets if the room should be invite only or not

Parameters:

  • invite_only (Boolean)

    If it should be invite only or not



811
812
813
814
# File 'lib/active_matrix/room.rb', line 811

def invite_only=(invite_only)
  self.join_rule = invite_only ? :invite : :public
  invite_only
end

#invite_only?Boolean

Checks if join_rule is set to :invite

Returns:

  • (Boolean)


323
324
325
# File 'lib/active_matrix/room.rb', line 323

def invite_only?
  join_rule == :invite
end

#invite_user(user_id) ⇒ Boolean

Invites a user into the room

Parameters:

  • user_id (String, User)

    the MXID of the user

Returns:

  • (Boolean)

    wether the action succeeded



577
578
579
580
581
# File 'lib/active_matrix/room.rb', line 577

def invite_user(user_id)
  user_id = user_id.id if user_id.is_a? ActiveMatrix::User
  client.api.invite_user(id, user_id)
  true
end

#join_rule:public, ...

Gets the join rule for the room

Returns:

  • (:public, :knock, :invite, :private)

    The current join rule



313
314
315
# File 'lib/active_matrix/room.rb', line 313

def join_rule
  get_state('m.room.join_rules')[:join_rule]&.to_sym
end

#join_rule=(join_rule) ⇒ Object

Sets the join rule of the room

Parameters:

  • join_rule (:invite, :public)

    The join rule of the room



819
820
821
822
# File 'lib/active_matrix/room.rb', line 819

def join_rule=(join_rule)
  room_state['m.room.join_rules'] = { join_rule: join_rule }
  join_rule
end

#joined_membersArray(User) Also known as: members

Populates and returns the #members array

Returns:

  • (Array(User))

    The list of members in the room



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/active_matrix/room.rb', line 175

def joined_members
  # Return pre-populated members if they exist (for testing)
  return @pre_populated_members if @pre_populated_members

  # Return cached instance if available
  return @cached_joined_members if @cached_joined_members

  return fetch_joined_members unless cache_available?

  # Cache the raw data that can be used to reconstruct User objects
  members_data = cache.fetch(cache_key(:joined_members), expires_in: 1.hour) do
    # Convert API response to cacheable format
    api_response = client.api.get_room_joined_members(id)[:joined]
    api_response.map do |mxid, data|
      {
        mxid: mxid.to_s,
        display_name: data[:display_name],
        avatar_url: data[:avatar_url]
      }
    end
  end

  # Reconstruct User objects from cached data and cache at instance level
  @cached_joined_members = members_data.map do |member|
    User.new(client, member[:mxid],
             display_name: member[:display_name],
             avatar_url: member[:avatar_url])
  end
end

#kick_user(user_id, reason = '') ⇒ Boolean

Kicks a user from the room

Parameters:

  • user_id (String, User)

    the MXID of the user

  • reason (String) (defaults to: '')

    the reason for the kick

Returns:

  • (Boolean)

    wether the action succeeded



588
589
590
591
592
# File 'lib/active_matrix/room.rb', line 588

def kick_user(user_id, reason = '')
  user_id = user_id.id if user_id.is_a? ActiveMatrix::User
  client.api.kick_user(id, user_id, reason: reason)
  true
end

#knock_only?Boolean

Checks if join_rule is set to :knock

Returns:

  • (Boolean)


328
329
330
# File 'lib/active_matrix/room.rb', line 328

def knock_only?
  join_rule == :knock
end

#leaveBoolean

Requests to be removed from the room

Returns:

  • (Boolean)

    wether the request succeeded



618
619
620
621
622
# File 'lib/active_matrix/room.rb', line 618

def leave
  client.api.leave_room(id)
  client.instance_variable_get(:@rooms).delete id
  true
end

#moderator!(user, level: 50) ⇒ Object

Make a user a moderator in the room

Parameters:

  • user (User, MXID, String)

    The user to give moderator privileges

  • level (Integer) (defaults to: 50)

    The power level to set the user to

Raises:

  • (ArgumentError)

See Also:



936
937
938
939
940
941
942
943
944
# File 'lib/active_matrix/room.rb', line 936

def moderator!(user, level: 50)
  return true if moderator?(user, target_level: level)

  user = user.id if user.is_a? User
  user = MXID.new(user.to_s) unless user.is_a? MXID
  raise ArgumentError, 'Must provide a valid user or MXID' unless user.user?

  modify_user_power_levels({ user.to_s.to_sym => level })
end

#moderator?(user, target_level: 50) ⇒ Boolean

Check if a user is a moderator in the room

Parameters:

  • user (User, MXID, String)

    The user to check for admin privileges

  • target_level (Integer) (defaults to: 50)

    The power level that’s to be considered as admin privileges

Returns:

  • (Boolean)

    If the requested user has a power level highe enough to be an admin

See Also:



924
925
926
927
928
929
# File 'lib/active_matrix/room.rb', line 924

def moderator?(user, target_level: 50)
  level = user_powerlevel(user, use_default: false)
  return false unless level

  level >= target_level
end

#modify_required_power_levels(events = nil, params = {}) ⇒ Boolean

Modifies the required power levels for actions in the room

Parameters:

  • events (Hash) (defaults to: nil)

    the event-specific power levels to change

  • params (Hash) (defaults to: {})

    other power-level params to change

Returns:

  • (Boolean)

    if the change was successful



983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# File 'lib/active_matrix/room.rb', line 983

def modify_required_power_levels(events = nil, params = {})
  return false if events.nil? && (params.nil? || params.empty?)

  room_state.expire 'm.room.power_levels'

  data = power_levels
  data.merge!(params)
  data.delete_if { |_k, v| v.nil? }

  if events
    data[:events] = {} unless data.key? :events
    data[:events].merge!(events)
    data[:events].delete_if { |_k, v| v.nil? }
  end

  room_state['m.room.power_levels'] = data
  true
end

#modify_user_power_levels(users = nil, users_default = nil) ⇒ Boolean

Modifies the power levels of the room

Parameters:

  • users (Hash) (defaults to: nil)

    the user-specific power levels to set or remove

  • users_default (Hash) (defaults to: nil)

    the default user power levels to set

Returns:

  • (Boolean)

    if the change was successful



951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
# File 'lib/active_matrix/room.rb', line 951

def modify_user_power_levels(users = nil, users_default = nil)
  return false if users.nil? && users_default.nil?

  room_state.expire 'm.room.power_levels'

  data = power_levels
  data[:users_default] = users_default unless users_default.nil?

  if users
    data[:users] = {} unless data.key? :users
    users.each do |user, level|
      user = user.id if user.is_a? User
      user = MXID.new(user.to_s) unless user.is_a? MXID
      raise ArgumentError, 'Must provide a valid user or MXID' unless user.user?

      if level.nil?
        data[:users].delete(user.to_s.to_sym)
      else
        data[:users][user.to_s.to_sym] = level
      end
    end
  end

  room_state['m.room.power_levels'] = data
  true
end

#nameString?

Note:

Will cache the current name for 15 minutes

Gets the current name of the room, querying the API if necessary

Returns:

  • (String, nil)

    The room name - if any



249
250
251
252
253
254
255
# File 'lib/active_matrix/room.rb', line 249

def name
  state = get_state('m.room.name')
  state&.dig(:name)
rescue MatrixNotFoundError
  # No room name has been specified
  nil
end

#name=(name) ⇒ Object

Sets a new name on the room

Parameters:

  • name (String)

    The new name to set



758
759
760
761
# File 'lib/active_matrix/room.rb', line 758

def name=(name)
  room_state['m.room.name'] = { name: name }
  name
end

#power_levelsHash

Note:

The returned power levels are cached for a minute

Get the power levels of the room

Returns:

  • (Hash)

    The current power levels as set for the room

See Also:



856
857
858
# File 'lib/active_matrix/room.rb', line 856

def power_levels
  get_state('m.room.power_levels')
end

#redact_message(event_id, reason = nil) ⇒ Object

Redacts a message from the room

Parameters:

  • event_id (String)

    the ID of the event to redact

  • reason (String, nil) (defaults to: nil)

    the reason for the redaction



532
533
534
535
# File 'lib/active_matrix/room.rb', line 532

def redact_message(event_id, reason = nil)
  client.api.redact_event(id, event_id, reason: reason)
  true
end

#reload!Object Also known as: refresh!

Refreshes the room state caches for name, topic, and aliases



747
748
749
750
751
752
# File 'lib/active_matrix/room.rb', line 747

def reload!
  reload_name!
  reload_topic!
  reload_aliases!
  true
end

#reload_aliases!Boolean Also known as: refresh_aliases!

Note:

The list of aliases is not sorted, ordering changes will result in alias list updates.

Reloads the list of aliases by an API query

Returns:

  • (Boolean)

    if the alias list was updated or not



802
803
804
805
# File 'lib/active_matrix/room.rb', line 802

def reload_aliases!
  room_state.expire('m.room.canonical_alias')
  cache.delete(cache_key(:aliases)) if cache_available?
end

#reload_name!Boolean Also known as: refresh_name!

Reloads the name of the room

Returns:

  • (Boolean)

    if the name was changed or not



766
767
768
# File 'lib/active_matrix/room.rb', line 766

def reload_name!
  room_state.expire('m.room.name')
end

#reload_topic!Boolean Also known as: refresh_topic!

Reloads the topic of the room

Returns:

  • (Boolean)

    if the topic was changed or not



782
783
784
# File 'lib/active_matrix/room.rb', line 782

def reload_topic!
  room_state.expire('m.room.topic')
end

#remove_tag(tag) ⇒ Object

Remove a tag from the room

Parameters:

  • tag (String)

    The tag to remove



728
729
730
731
# File 'lib/active_matrix/room.rb', line 728

def remove_tag(tag)
  client.api.remove_user_tag(client.mxid, id, tag)
  true
end

#report_message(event_id, reason:, score: -100)) ⇒ Object

Reports a message in the room

Parameters:

  • event_id (MXID, String)

    The ID of the event to redact

  • reason (String)

    The reason for the report

  • score (Integer) (defaults to: -100))

    The severity of the report in the range of -100 - 0



542
543
544
545
# File 'lib/active_matrix/room.rb', line 542

def report_message(event_id, reason:, score: -100)
  client.api.report_event(id, event_id, reason: reason, score: score)
  true
end

#room_stateObject



332
333
334
335
336
# File 'lib/active_matrix/room.rb', line 332

def room_state
  return ActiveMatrix::Util::StateEventCache.new self if client.cache == :none

  @room_state ||= ActiveMatrix::Util::StateEventCache.new self
end

#room_type'm.space', ...

Retrieves the type of the room

Returns:

  • ('m.space', String, nil)

    The type of the room



675
676
677
678
# File 'lib/active_matrix/room.rb', line 675

def room_type
  # Can't change, so a permanent cache is ok
  @room_type ||= creation_info[:type]
end

#room_versionString

Retrieves the room version

Returns:

  • (String)

    The version of the room



683
684
685
686
# File 'lib/active_matrix/room.rb', line 683

def room_version
  # Can't change, so a permanent cache is ok
  @room_version ||= creation_info[:room_version]
end

#send_audio(url, name, audio_info = {}) ⇒ Object

Note:

The URLs should all be of the ‘mxc://’ schema

Sends a link to an audio clip to the room

Parameters:

  • url (String, URI)

    the URL to the audio clip

  • name (String)

    the name of the audio clip

  • audio_info (Hash) (defaults to: {})

    extra information about the audio clip

Options Hash (audio_info):

  • :duration (Integer)

    the duration of the audio clip in milliseconds

  • :mimetype (String)

    the MIME type of the audio clip

  • :size (Integer)

    the size of the audio clip in bytes



499
500
501
# File 'lib/active_matrix/room.rb', line 499

def send_audio(url, name, audio_info = {})
  client.api.send_content(id, url, name, 'm.audio', extra_information: audio_info)
end

#send_custom_message(body, content = {}, msgtype: nil) ⇒ Object

Sends a customized message to the Room

Parameters:

  • body (String)

    The clear-text body of the message

  • content (Hash) (defaults to: {})

    The custom content of the message

  • msgtype (String) (defaults to: nil)

    The type of the message, should be one of the known types (m.text, m.notice, m.emote, etc)



508
509
510
511
512
513
514
515
# File 'lib/active_matrix/room.rb', line 508

def send_custom_message(body, content = {}, msgtype: nil)
  content.merge!(
    body: body,
    msgtype: msgtype || 'm.text'
  )

  client.api.send_message_event(id, 'm.room.message', content)
end

#send_emote(text) ⇒ Object

Sends an emote (/me) message to the room

Parameters:

  • text (String)

    the emote to send



421
422
423
# File 'lib/active_matrix/room.rb', line 421

def send_emote(text)
  client.api.send_emote(id, text)
end

#send_event(type, content = {}) ⇒ Object

Sends a custom timeline event to the Room

Parameters:

  • type (String, Symbol)

    The type of the Event. For custom events, this should be written in reverse DNS format (e.g. com.example.event)

  • content (Hash) (defaults to: {})

    The contents of the message, this will be the :content key of the resulting event object

See Also:



524
525
526
# File 'lib/active_matrix/room.rb', line 524

def send_event(type, content = {})
  client.api.send_message_event(id, type, content)
end

#send_file(url, name, file_info = {}) ⇒ Object

Note:

The URLs should all be of the ‘mxc://’ schema

Sends a link to a generic file to the room

Parameters:

  • url (String, URI)

    the URL to the file

  • name (String)

    the name of the file

  • file_info (Hash) (defaults to: {})

    extra information about the file

Options Hash (file_info):

  • :mimetype (String)

    the MIME type of the file

  • :size (Integer)

    the size of the file in bytes

  • :thumbnail_url (String, URI)

    the URL to a thumbnail of the file

  • :thumbnail_info (Hash)

    ThumbnailInfo about the thumbnail file



435
436
437
# File 'lib/active_matrix/room.rb', line 435

def send_file(url, name, file_info = {})
  client.api.send_content(id, url, name, 'm.file', extra_information: file_info)
end

#send_html(html, body = nil, msgtype: nil, format: nil) ⇒ Object

Sends a custom HTML message to the room

Parameters:

  • html (String)

    the HTML message to send

  • body (String, nil) (defaults to: nil)

    a plain-text representation of the object (Will default to the HTML with all tags stripped away)

  • msgtype (String) (defaults to: nil)

    (‘m.text’) The message type for the message

  • format (String) (defaults to: nil)

    (‘org.matrix.custom.html’) The message format

See Also:



407
408
409
410
411
412
413
414
415
416
# File 'lib/active_matrix/room.rb', line 407

def send_html(html, body = nil, msgtype: nil, format: nil)
  content = {
    body: body || html.gsub(/<\/?[^>]*>/, ''),
    msgtype: msgtype || 'm.text',
    format: format || 'org.matrix.custom.html',
    formatted_body: html
  }

  client.api.send_message_event(id, 'm.room.message', content)
end

#send_image(url, name, image_info = {}) ⇒ Object

Note:

The URLs should all be of the ‘mxc://’ schema

Sends a link to an image to the room

Parameters:

  • url (String, URI)

    the URL to the image

  • name (String)

    the name of the image

  • image_info (Hash) (defaults to: {})

    extra information about the image

Options Hash (image_info):

  • :h (Integer)

    the height of the image in pixels

  • :w (Integer)

    the width of the image in pixels

  • :mimetype (String)

    the MIME type of the image

  • :size (Integer)

    the size of the image in bytes

  • :thumbnail_url (String, URI)

    the URL to a thumbnail of the image

  • :thumbnail_info (Hash)

    ThumbnailInfo about the thumbnail image



458
459
460
# File 'lib/active_matrix/room.rb', line 458

def send_image(url, name, image_info = {})
  client.api.send_content(id, url, name, 'm.image', extra_information: image_info)
end

#send_location(geo_uri, name, thumbnail_url = nil, thumbnail_info = {}) ⇒ Object

Note:

The thumbnail URL should be of the ‘mxc://’ schema

Sends a location object to the room

Parameters:

  • geo_uri (String, URI)

    the geo-URL (e.g. geo:<coords>) of the location

  • name (String)

    the name of the location

  • thumbnail_url (String, URI) (defaults to: nil)

    the URL to a thumbnail image of the location

  • thumbnail_info (Hash) (defaults to: {})

    a ThumbnailInfo for the location thumbnail



469
470
471
# File 'lib/active_matrix/room.rb', line 469

def send_location(geo_uri, name, thumbnail_url = nil, thumbnail_info = {})
  client.api.send_location(id, geo_uri, name, thumbnail_url: thumbnail_url, thumbnail_info: thumbnail_info)
end

#send_notice(text) ⇒ Object

Sends a notice (bot) message to the room

Parameters:

  • text (String)

    the notice to send



442
443
444
# File 'lib/active_matrix/room.rb', line 442

def send_notice(text)
  client.api.send_notice(id, text)
end

#send_text(text) ⇒ Object

Sends a plain-text message to the room

Parameters:

  • text (String)

    the message to send



394
395
396
# File 'lib/active_matrix/room.rb', line 394

def send_text(text)
  client.api.send_message(id, text)
end

#send_video(url, name, video_info = {}) ⇒ Object

Note:

The URLs should all be of the ‘mxc://’ schema

Sends a link to a video to the room

Parameters:

  • url (String, URI)

    the URL to the video

  • name (String)

    the name of the video

  • video_info (Hash) (defaults to: {})

    extra information about the video

Options Hash (video_info):

  • :duration (Integer)

    the duration of the video in milliseconds

  • :h (Integer)

    the height of the video in pixels

  • :w (Integer)

    the width of the video in pixels

  • :mimetype (String)

    the MIME type of the video

  • :size (Integer)

    the size of the video in bytes

  • :thumbnail_url (String, URI)

    the URL to a thumbnail of the video

  • :thumbnail_info (Hash)

    ThumbnailInfo about the thumbnail of the video



486
487
488
# File 'lib/active_matrix/room.rb', line 486

def send_video(url, name, video_info = {})
  client.api.send_content(id, url, name, 'm.video', extra_information: video_info)
end

#set_account_data(type, account_data) ⇒ Object

Stores a custom entry into the room-specific account data

Parameters:

  • type (String)

    the data type to store

  • account_data (Hash)

    the data to store



642
643
644
645
# File 'lib/active_matrix/room.rb', line 642

def (type, )
  self.[type] = 
  true
end

#set_state(type, data, state_key: nil) ⇒ Object

Sets a state object in the room



344
345
346
# File 'lib/active_matrix/room.rb', line 344

def set_state(type, data, state_key: nil)
  room_state[type, state_key] = data
end

#set_user_profile(display_name: nil, avatar_url: nil, reason: nil) ⇒ Object

Note:

the avatar URL should be a mxc:// URI

Changes the room-specific user profile

Parameters:

  • display_name (String) (defaults to: nil)

    the new display name to use in the room

  • avatar_url (String, URI) (defaults to: nil)

    the new avatar URL to use in the room



652
653
654
655
656
657
658
659
660
661
662
663
# File 'lib/active_matrix/room.rb', line 652

def (display_name: nil, avatar_url: nil, reason: nil)
  return nil unless display_name || avatar_url

  data = client.api.get_membership(id, client.mxid)
  raise "Can't set profile if you haven't joined the room" unless data[:membership] == 'join'

  data[:displayname] = display_name unless display_name.nil?
  data[:avatar_url] = avatar_url unless avatar_url.nil?

  client.api.set_membership(id, client.mxid, 'join', reason || 'Updating room profile information', data)
  true
end

#space?Boolean?

Checks if the room is a Matrix Space

Returns:

  • (Boolean, nil)

    True if the room is a space



691
692
693
694
695
# File 'lib/active_matrix/room.rb', line 691

def space?
  room_type == 'm.space'
rescue ActiveMatrix::MatrixForbiddenError, ActiveMatrix::MatrixNotFoundError
  nil
end

#tagsResponse

Returns a list of the room tags

Examples:

Managing tags

room.tags
# => { :room_tag => { data: false } }
room.tags.add('some_tag', data: true)
# => { :some_tag => { data: true }, :room_tag => { data: false} }
room.tags.remove('room_tag')
# => { :some_tag => { data: true} }

Returns:

  • (Response)

    A list of the tags and their data, with add and remove methods implemented



707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
# File 'lib/active_matrix/room.rb', line 707

def tags
  client.api.get_user_tags(client.mxid, id)[:tags].tap do |tag_obj|
    tag_obj.instance_variable_set(:@room, self)
    tag_obj.define_singleton_method(:room) do
      @room
    end
    tag_obj.define_singleton_method(:add) do |tag, **data|
      @room.add_tag(tag.to_s.to_sym, **data)
      self[tag.to_s.to_sym] = data
      self
    end
    tag_obj.define_singleton_method(:remove) do |tag|
      @room.remove_tag(tag.to_s.to_sym)
      delete tag.to_s.to_sym
    end
  end
end

#to_sObject



102
103
104
105
106
107
# File 'lib/active_matrix/room.rb', line 102

def to_s
  prefix = canonical_alias || id
  return "#{prefix} | #{name}" unless name.nil?

  prefix
end

#to_spaceObject

Casting operators



96
97
98
99
100
# File 'lib/active_matrix/room.rb', line 96

def to_space
  return nil unless space?

  Rooms::Space.new self, nil
end

#topicString?

Gets the room topic - if any

Returns:

  • (String, nil)

    The topic of the room



296
297
298
299
300
301
# File 'lib/active_matrix/room.rb', line 296

def topic
  get_state('m.room.topic')[:topic]
rescue MatrixNotFoundError
  # No room name has been specified
  nil
end

#topic=(topic) ⇒ Object

Sets a new topic on the room

Parameters:

  • topic (String)

    The new topic to set



774
775
776
777
# File 'lib/active_matrix/room.rb', line 774

def topic=(topic)
  room_state['m.room.topic'] = { topic: topic }
  topic
end

#unban_user(user_id) ⇒ Boolean

Unbans a user from the room

Parameters:

  • user_id (String, User)

    the MXID of the user

Returns:

  • (Boolean)

    wether the action succeeded



609
610
611
612
613
# File 'lib/active_matrix/room.rb', line 609

def unban_user(user_id)
  user_id = user_id.id if user_id.is_a? ActiveMatrix::User
  client.api.unban_user(id, user_id)
  true
end

#user_can_send?(user, event, state: false) ⇒ Boolean

Checks if a user can send a given event type in the room

Parameters:

  • user (User, MXID, String)

    The user to check

  • event (String, Symbol)

    The event type to check

  • state (Boolean) (defaults to: false)

    If the given event is a state event or a message event

Returns:

  • (Boolean)

    If the given user is allowed to send an event of the given type



882
883
884
885
886
887
888
# File 'lib/active_matrix/room.rb', line 882

def user_can_send?(user, event, state: false)
  user_pl = user_powerlevel(user)
  event_pl = power_levels.dig(:events, event.to_s.to_sym)
  event_pl ||= state ? (power_levels[:state_default] || 50) : (power_levels[:events_default] || 0)

  user_pl >= event_pl
end

#user_powerlevel(user, use_default: true) ⇒ Integer?

Gets the power level of a user in the room

Parameters:

  • user (User, MXID, String)

    The user to check the power level for

  • use_default (Boolean) (defaults to: true)

    Should the user default level be checked if no user-specific one exists

Returns:

  • (Integer, nil)

    The current power level for the requested user, nil if there’s no user specific level and use_default is false

Raises:

  • (ArgumentError)


866
867
868
869
870
871
872
873
874
# File 'lib/active_matrix/room.rb', line 866

def user_powerlevel(user, use_default: true)
  user = user.id if user.is_a? User
  user = MXID.new(user.to_s) unless user.is_a? MXID
  raise ArgumentError, 'Must provide a valid User or MXID' unless user.user?

  level = power_levels.dig(:users, user.to_s.to_sym)
  level ||= power_levels[:users_default] || 0 if use_default
  level
end

#world_readable?Boolean Also known as: world_readable

Checks if the room history is world readable

Returns:

  • (Boolean)

    If the history is world readable



358
359
360
# File 'lib/active_matrix/room.rb', line 358

def world_readable?
  history_visibility == :world_readable
end