activematrix 0.0.0 → 0.0.2

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +218 -51
  3. data/lib/active_matrix/agent_manager.rb +275 -0
  4. data/lib/active_matrix/agent_registry.rb +154 -0
  5. data/lib/{matrix_sdk → active_matrix}/api.rb +18 -22
  6. data/lib/{matrix_sdk → active_matrix}/bot/base.rb +42 -39
  7. data/lib/{matrix_sdk → active_matrix}/bot/main.rb +4 -5
  8. data/lib/active_matrix/bot/multi_instance_base.rb +189 -0
  9. data/lib/active_matrix/bot.rb +7 -0
  10. data/lib/{matrix_sdk → active_matrix}/client.rb +21 -34
  11. data/lib/active_matrix/client_pool.rb +194 -0
  12. data/lib/{matrix_sdk → active_matrix}/errors.rb +4 -4
  13. data/lib/active_matrix/event_router.rb +215 -0
  14. data/lib/active_matrix/logging.rb +56 -0
  15. data/lib/active_matrix/memory/agent_memory.rb +128 -0
  16. data/lib/active_matrix/memory/base.rb +101 -0
  17. data/lib/active_matrix/memory/conversation_memory.rb +161 -0
  18. data/lib/active_matrix/memory/global_memory.rb +153 -0
  19. data/lib/active_matrix/memory.rb +28 -0
  20. data/lib/{matrix_sdk → active_matrix}/mxid.rb +2 -2
  21. data/lib/{matrix_sdk → active_matrix}/protocols/as.rb +1 -1
  22. data/lib/{matrix_sdk → active_matrix}/protocols/cs.rb +6 -8
  23. data/lib/{matrix_sdk → active_matrix}/protocols/is.rb +1 -1
  24. data/lib/{matrix_sdk → active_matrix}/protocols/msc.rb +6 -8
  25. data/lib/{matrix_sdk → active_matrix}/protocols/ss.rb +2 -2
  26. data/lib/active_matrix/railtie.rb +18 -0
  27. data/lib/{matrix_sdk → active_matrix}/response.rb +2 -2
  28. data/lib/{matrix_sdk → active_matrix}/room.rb +148 -72
  29. data/lib/{matrix_sdk → active_matrix}/rooms/space.rb +3 -7
  30. data/lib/{matrix_sdk → active_matrix}/user.rb +23 -15
  31. data/lib/active_matrix/util/account_data_cache.rb +129 -0
  32. data/lib/active_matrix/util/cacheable.rb +73 -0
  33. data/lib/{matrix_sdk → active_matrix}/util/events.rb +8 -8
  34. data/lib/{matrix_sdk → active_matrix}/util/extensions.rb +6 -15
  35. data/lib/active_matrix/util/state_event_cache.rb +167 -0
  36. data/lib/{matrix_sdk → active_matrix}/util/uri.rb +4 -4
  37. data/lib/active_matrix/version.rb +5 -0
  38. data/lib/active_matrix.rb +81 -0
  39. data/lib/generators/active_matrix/bot/bot_generator.rb +38 -0
  40. data/lib/generators/active_matrix/bot/templates/bot.rb.erb +111 -0
  41. data/lib/generators/active_matrix/bot/templates/bot_spec.rb.erb +68 -0
  42. data/lib/generators/active_matrix/install/install_generator.rb +44 -0
  43. data/lib/generators/active_matrix/install/templates/README +30 -0
  44. data/lib/generators/active_matrix/install/templates/active_matrix.rb +33 -0
  45. data/lib/generators/active_matrix/install/templates/agent_memory.rb +47 -0
  46. data/lib/generators/active_matrix/install/templates/conversation_context.rb +72 -0
  47. data/lib/generators/active_matrix/install/templates/create_agent_memories.rb +17 -0
  48. data/lib/generators/active_matrix/install/templates/create_conversation_contexts.rb +21 -0
  49. data/lib/generators/active_matrix/install/templates/create_global_memories.rb +20 -0
  50. data/lib/generators/active_matrix/install/templates/create_matrix_agents.rb +26 -0
  51. data/lib/generators/active_matrix/install/templates/global_memory.rb +70 -0
  52. data/lib/generators/active_matrix/install/templates/matrix_agent.rb +127 -0
  53. metadata +168 -30
  54. data/lib/matrix_sdk/bot.rb +0 -4
  55. data/lib/matrix_sdk/util/account_data_cache.rb +0 -91
  56. data/lib/matrix_sdk/util/state_event_cache.rb +0 -92
  57. data/lib/matrix_sdk/util/tinycache.rb +0 -140
  58. data/lib/matrix_sdk/util/tinycache_adapter.rb +0 -87
  59. data/lib/matrix_sdk/version.rb +0 -5
  60. data/lib/matrix_sdk.rb +0 -75
@@ -1,15 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'matrix_sdk'
4
- require 'matrix_sdk/util/events'
5
- require 'matrix_sdk/util/tinycache'
6
-
7
- module MatrixSdk
3
+ module ActiveMatrix
8
4
  # A class for tracking the information about a room on Matrix
9
5
  class Room
10
- extend MatrixSdk::Extensions
11
- extend MatrixSdk::Util::Tinycache
12
- include MatrixSdk::Logging
6
+ extend ActiveMatrix::Extensions
7
+ include ActiveMatrix::Logging
8
+ include ActiveMatrix::Util::Cacheable
13
9
 
14
10
  # @!attribute [rw] event_history_limit
15
11
  # @return [Fixnum] the limit of events to keep in the event log
@@ -28,17 +24,9 @@ module MatrixSdk
28
24
  # An inspect method that skips a handful of instance variables to avoid
29
25
  # flooding the terminal with debug data.
30
26
  # @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
27
+ ignore_inspect :client, :events, :prev_batch, :logger
39
28
 
40
29
  alias room_id id
41
- alias members joined_members
42
30
 
43
31
  # Create a new room instance
44
32
  #
@@ -78,23 +66,22 @@ module MatrixSdk
78
66
  @events = []
79
67
  @event_history_limit = 10
80
68
  @room_type = nil
69
+ @pre_populated_members = nil # For pre-populated members from tests
70
+ @cached_joined_members = nil # Instance cache for joined members
71
+ @cached_all_members = nil # Instance cache for all members
81
72
 
82
73
  @prev_batch = nil
83
74
 
84
75
  %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
76
+ room_state.write("m.room.#{type}", { type => data.delete(type) }) if data.key? type
86
77
  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
78
+ room_state.write('m.room.join_rules', { join_rule: data.delete(:join_rule) }) if data.key? :join_rule
79
+ room_state.write('m.room.history_visibility', { history_visibility: data.delete(:world_readable) ? :world_readable : nil }) if data.key? :world_readable
89
80
 
90
81
  data.each do |k, v|
91
82
  next if %i[client].include? k
92
83
 
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
84
+ instance_variable_set("@#{k}", v) if instance_variable_defined? "@#{k}"
98
85
  end
99
86
 
100
87
  @id = room_id.to_s
@@ -178,7 +165,7 @@ module MatrixSdk
178
165
  # @return [String, nil] the canonical alias of the room
179
166
  def canonical_alias
180
167
  get_state('m.room.canonical_alias')[:alias]
181
- rescue MatrixSdk::MatrixNotFoundError
168
+ rescue ActiveMatrix::MatrixNotFoundError
182
169
  nil
183
170
  end
184
171
 
@@ -186,6 +173,36 @@ module MatrixSdk
186
173
  #
187
174
  # @return [Array(User)] The list of members in the room
188
175
  def joined_members
176
+ # Return pre-populated members if they exist (for testing)
177
+ return @pre_populated_members if @pre_populated_members
178
+
179
+ # Return cached instance if available
180
+ return @cached_joined_members if @cached_joined_members
181
+
182
+ return fetch_joined_members unless cache_available?
183
+
184
+ # Cache the raw data that can be used to reconstruct User objects
185
+ members_data = cache.fetch(cache_key(:joined_members), expires_in: 1.hour) do
186
+ # Convert API response to cacheable format
187
+ api_response = client.api.get_room_joined_members(id)[:joined]
188
+ api_response.map do |mxid, data|
189
+ {
190
+ mxid: mxid.to_s,
191
+ display_name: data[:display_name],
192
+ avatar_url: data[:avatar_url]
193
+ }
194
+ end
195
+ end
196
+
197
+ # Reconstruct User objects from cached data and cache at instance level
198
+ @cached_joined_members = members_data.map do |member|
199
+ User.new(client, member[:mxid],
200
+ display_name: member[:display_name],
201
+ avatar_url: member[:avatar_url])
202
+ end
203
+ end
204
+
205
+ def fetch_joined_members
189
206
  client.api.get_room_joined_members(id)[:joined].map do |mxid, data|
190
207
  User.new(client, mxid.to_s,
191
208
  display_name: data.fetch(:display_name, nil),
@@ -193,6 +210,8 @@ module MatrixSdk
193
210
  end
194
211
  end
195
212
 
213
+ alias members joined_members
214
+
196
215
  # Get all members (member events) in the room
197
216
  #
198
217
  # @note This will also count members who've knocked, been invited, have left, or have been banned.
@@ -201,6 +220,24 @@ module MatrixSdk
201
220
  #
202
221
  # @return [Array(User)] The complete list of members in the room, regardless of membership state
203
222
  def all_members(**params)
223
+ # Return pre-populated members if they exist and no filtering params (for testing)
224
+ return @pre_populated_members if @pre_populated_members && params.empty?
225
+
226
+ # Return cached instance if available and no params
227
+ return @cached_all_members if @cached_all_members && params.empty?
228
+
229
+ return fetch_all_members(**params) if !params.empty? || client.cache == :none || !cache_available?
230
+
231
+ # Cache the raw member state keys, not User objects
232
+ members_data = cache.fetch(cache_key(:all_members), expires_in: 1.hour) do
233
+ client.api.get_room_members(id, **params)[:chunk].map { |ch| ch[:state_key] }
234
+ end
235
+
236
+ # Reconstruct User objects from cached data and cache at instance level
237
+ @cached_all_members = members_data.map { |state_key| client.get_user(state_key) }
238
+ end
239
+
240
+ def fetch_all_members(**params)
204
241
  client.api.get_room_members(id, **params)[:chunk].map { |ch| client.get_user(ch[:state_key]) }
205
242
  end
206
243
 
@@ -210,7 +247,8 @@ module MatrixSdk
210
247
  #
211
248
  # @return [String,nil] The room name - if any
212
249
  def name
213
- get_state('m.room.name')[:name]
250
+ state = get_state('m.room.name')
251
+ state&.dig(:name)
214
252
  rescue MatrixNotFoundError
215
253
  # No room name has been specified
216
254
  nil
@@ -292,9 +330,9 @@ module MatrixSdk
292
330
  end
293
331
 
294
332
  def room_state
295
- return MatrixSdk::Util::StateEventCache.new self if client.cache == :none
333
+ return ActiveMatrix::Util::StateEventCache.new self if client.cache == :none
296
334
 
297
- @room_state ||= MatrixSdk::Util::StateEventCache.new self
335
+ @room_state ||= ActiveMatrix::Util::StateEventCache.new self
298
336
  end
299
337
 
300
338
  # Gets a state object in the room
@@ -327,8 +365,20 @@ module MatrixSdk
327
365
  # @param canonical_only [Boolean] Should the list of aliases only contain the canonical ones
328
366
  # @return [Array[String]] The assigned room aliases
329
367
  def aliases(canonical_only: true)
368
+ return fetch_aliases(canonical_only: canonical_only) if !canonical_only || client.cache == :none || !cache_available?
369
+
370
+ cache.fetch(cache_key(:aliases), expires_in: 1.hour) do
371
+ fetch_aliases(canonical_only: true)
372
+ end
373
+ end
374
+
375
+ def fetch_aliases(canonical_only: true)
330
376
  canonical = get_state('m.room.canonical_alias') rescue {}
331
- aliases = ([canonical[:alias]].compact + (canonical[:alt_aliases] || [])).uniq.sort
377
+ # Handle both hash-like and Response objects
378
+ alias_value = canonical.respond_to?(:alias) ? canonical.alias : canonical[:alias]
379
+ alt_aliases = canonical.respond_to?(:alt_aliases) ? canonical.alt_aliases : canonical[:alt_aliases]
380
+
381
+ aliases = ([alias_value].compact + (alt_aliases || [])).uniq.sort
332
382
  return aliases if canonical_only
333
383
 
334
384
  (aliases + client.api.get_room_aliases(id).aliases).uniq.sort
@@ -525,7 +575,7 @@ module MatrixSdk
525
575
  # @param user_id [String,User] the MXID of the user
526
576
  # @return [Boolean] wether the action succeeded
527
577
  def invite_user(user_id)
528
- user_id = user_id.id if user_id.is_a? MatrixSdk::User
578
+ user_id = user_id.id if user_id.is_a? ActiveMatrix::User
529
579
  client.api.invite_user(id, user_id)
530
580
  true
531
581
  end
@@ -536,7 +586,7 @@ module MatrixSdk
536
586
  # @param reason [String] the reason for the kick
537
587
  # @return [Boolean] wether the action succeeded
538
588
  def kick_user(user_id, reason = '')
539
- user_id = user_id.id if user_id.is_a? MatrixSdk::User
589
+ user_id = user_id.id if user_id.is_a? ActiveMatrix::User
540
590
  client.api.kick_user(id, user_id, reason: reason)
541
591
  true
542
592
  end
@@ -547,7 +597,7 @@ module MatrixSdk
547
597
  # @param reason [String] the reason for the ban
548
598
  # @return [Boolean] wether the action succeeded
549
599
  def ban_user(user_id, reason = '')
550
- user_id = user_id.id if user_id.is_a? MatrixSdk::User
600
+ user_id = user_id.id if user_id.is_a? ActiveMatrix::User
551
601
  client.api.ban_user(id, user_id, reason: reason)
552
602
  true
553
603
  end
@@ -557,7 +607,7 @@ module MatrixSdk
557
607
  # @param user_id [String,User] the MXID of the user
558
608
  # @return [Boolean] wether the action succeeded
559
609
  def unban_user(user_id)
560
- user_id = user_id.id if user_id.is_a? MatrixSdk::User
610
+ user_id = user_id.id if user_id.is_a? ActiveMatrix::User
561
611
  client.api.unban_user(id, user_id)
562
612
  true
563
613
  end
@@ -572,9 +622,9 @@ module MatrixSdk
572
622
  end
573
623
 
574
624
  def account_data
575
- return MatrixSdk::Util::AccountDataCache.new client, room: self if client.cache == :none
625
+ return ActiveMatrix::Util::AccountDataCache.new client, room: self if client.cache == :none
576
626
 
577
- @account_data ||= MatrixSdk::Util::AccountDataCache.new client, room: self
627
+ @account_data ||= ActiveMatrix::Util::AccountDataCache.new client, room: self
578
628
  end
579
629
 
580
630
  # Retrieves a custom entry from the room-specific account data
@@ -640,7 +690,7 @@ module MatrixSdk
640
690
  # @return [Boolean,nil] True if the room is a space
641
691
  def space?
642
692
  room_type == 'm.space'
643
- rescue MatrixSdk::MatrixForbiddenError, MatrixSdk::MatrixNotFoundError
693
+ rescue ActiveMatrix::MatrixForbiddenError, ActiveMatrix::MatrixNotFoundError
644
694
  nil
645
695
  end
646
696
 
@@ -739,7 +789,8 @@ module MatrixSdk
739
789
  # @return [Boolean] if the addition was successful or not
740
790
  def add_alias(room_alias)
741
791
  client.api.set_room_alias(id, room_alias)
742
- tinycache_adapter.read(:aliases) << room_alias if tinycache_adapter.exist?(:aliases)
792
+ # Clear the cache to force refresh
793
+ cache.delete(cache_key(:aliases)) if cache_available?
743
794
  true
744
795
  end
745
796
 
@@ -750,7 +801,7 @@ module MatrixSdk
750
801
  # alias list updates.
751
802
  def reload_aliases!
752
803
  room_state.expire('m.room.canonical_alias')
753
- clear_aliases_cache
804
+ cache.delete(cache_key(:aliases)) if cache_available?
754
805
  end
755
806
  alias refresh_aliases! reload_aliases!
756
807
 
@@ -900,7 +951,7 @@ module MatrixSdk
900
951
  def modify_user_power_levels(users = nil, users_default = nil)
901
952
  return false if users.nil? && users_default.nil?
902
953
 
903
- room_state.tinycache_adapter.expire 'm.room.power_levels'
954
+ room_state.expire 'm.room.power_levels'
904
955
 
905
956
  data = power_levels
906
957
  data[:users_default] = users_default unless users_default.nil?
@@ -932,7 +983,7 @@ module MatrixSdk
932
983
  def modify_required_power_levels(events = nil, params = {})
933
984
  return false if events.nil? && (params.nil? || params.empty?)
934
985
 
935
- room_state.tinycache_adapter.expire 'm.room.power_levels'
986
+ room_state.expire 'm.room.power_levels'
936
987
 
937
988
  data = power_levels
938
989
  data.merge!(params)
@@ -950,38 +1001,52 @@ module MatrixSdk
950
1001
 
951
1002
  private
952
1003
 
1004
+ def cache_key(method_name)
1005
+ "activematrix:room:#{id}:#{method_name}"
1006
+ end
1007
+
1008
+ def cache_available?
1009
+ defined?(::Rails) && ::Rails.respond_to?(:cache) && ::Rails.cache
1010
+ end
1011
+
1012
+ def cache
1013
+ ::Rails.cache
1014
+ end
1015
+
953
1016
  def ensure_member(member)
954
1017
  return unless client.cache == :all
955
1018
 
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 }
1019
+ # Add member to pre-populated list
1020
+ @pre_populated_members ||= []
1021
+ @pre_populated_members << member unless @pre_populated_members.include?(member)
960
1022
 
961
- tinycache_adapter.write(:joined_members, members)
1023
+ # Clear the cache to force a refresh on next access
1024
+ cache.delete(cache_key(:joined_members)) if cache_available?
962
1025
  end
963
1026
 
964
1027
  def handle_room_member(event)
965
1028
  return unless client.cache == :all
966
1029
 
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
1030
+ # Cache the user if it's a join event
1031
+ client.get_user(event[:state_key]) if event.dig(:content, :membership) == 'join' && event[:state_key]
1032
+
1033
+ # Clear pre-populated members when membership changes
1034
+ @pre_populated_members = nil
1035
+
1036
+ # Clear instance caches when membership changes
1037
+ @cached_joined_members = nil
1038
+ @cached_all_members = nil
1039
+
1040
+ # Clear the cache when membership changes
1041
+ cache.delete(cache_key(:joined_members)) if cache_available?
1042
+ cache.delete(cache_key(:all_members)) if cache_available?
975
1043
  end
976
1044
 
977
1045
  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])
1046
+ room_state.write('m.room.canonical_alias', event[:content])
980
1047
 
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)
1048
+ # Clear the aliases cache
1049
+ cache.delete(cache_key(:aliases)) if cache_available?
985
1050
  end
986
1051
 
987
1052
  def room_handlers?
@@ -990,10 +1055,10 @@ module MatrixSdk
990
1055
 
991
1056
  def ensure_room_handlers
992
1057
  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
1058
+ account_data: ActiveMatrix::EventHandlerArray.new,
1059
+ event: ActiveMatrix::EventHandlerArray.new,
1060
+ state_event: ActiveMatrix::EventHandlerArray.new,
1061
+ ephemeral_event: ActiveMatrix::EventHandlerArray.new
997
1062
  }
998
1063
  end
999
1064
 
@@ -1005,10 +1070,8 @@ module MatrixSdk
1005
1070
  end
1006
1071
 
1007
1072
  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
1073
+ # Store the account data in cache
1074
+ account_data.write(event[:type], event[:content]) if event[:type]
1012
1075
 
1013
1076
  return unless room_handlers?
1014
1077
 
@@ -1030,15 +1093,28 @@ module MatrixSdk
1030
1093
  if INTERNAL_HANDLERS.key? event[:type]
1031
1094
  send(INTERNAL_HANDLERS[event[:type]], event)
1032
1095
  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)
1096
+ # StateEventCache will handle caching internally
1097
+ room_state.write(event[:type], event[:content], event[:state_key])
1037
1098
  end
1038
1099
 
1039
1100
  return unless room_handlers?
1040
1101
 
1041
1102
  ensure_room_handlers[:state_event].fire(MatrixEvent.new(self, event), event[:type])
1042
1103
  end
1104
+
1105
+ # Define what attributes to cache
1106
+ def cache_attributes
1107
+ {
1108
+ id: @id,
1109
+ room_type: @room_type,
1110
+ prev_batch: @prev_batch,
1111
+ event_history_limit: @event_history_limit
1112
+ }
1113
+ end
1114
+
1115
+ # Override cache_id to use room ID
1116
+ def cache_id
1117
+ @id
1118
+ end
1043
1119
  end
1044
1120
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module MatrixSdk::Rooms
4
- class Space < MatrixSdk::Room
3
+ module ActiveMatrix::Rooms
4
+ class Space < ActiveMatrix::Room
5
5
  TYPE = 'm.space'
6
6
 
7
7
  def tree(suggested_only: nil, max_rooms: nil)
@@ -26,11 +26,7 @@ module MatrixSdk::Rooms
26
26
 
27
27
  # Inject available room information
28
28
  r.each do |k, v|
29
- if room.respond_to?("#{k}_cached?".to_sym) && send("#{k}_cached?".to_sym)
30
- room.send(:tinycache_adapter).write(k, v)
31
- elsif room.instance_variable_defined? "@#{k}"
32
- room.instance_variable_set("@#{k}", v)
33
- end
29
+ room.instance_variable_set("@#{k}", v) if room.instance_variable_defined? "@#{k}"
34
30
  end
35
31
  room
36
32
  end
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'matrix_sdk'
4
-
5
- module MatrixSdk
3
+ module ActiveMatrix
6
4
  # A class for tracking information about a user on Matrix
7
5
  class User
8
- extend MatrixSdk::Extensions
6
+ extend ActiveMatrix::Extensions
7
+ include ActiveMatrix::Util::Cacheable
9
8
 
10
9
  attr_reader :id, :client
11
10
  alias user_id :id
@@ -35,13 +34,13 @@ module MatrixSdk
35
34
  end
36
35
 
37
36
  # @return [String] the display name
38
- # @see MatrixSdk::Protocols::CS#get_display_name
37
+ # @see ActiveMatrix::Protocols::CS#get_display_name
39
38
  def display_name
40
39
  @display_name ||= client.api.get_display_name(id)[:displayname]
41
40
  end
42
41
 
43
42
  # @param name [String] the display name to set
44
- # @see MatrixSdk::Protocols::CS#set_display_name
43
+ # @see ActiveMatrix::Protocols::CS#set_display_name
45
44
  def display_name=(name)
46
45
  client.api.set_display_name(id, name)
47
46
  @display_name = name
@@ -55,7 +54,7 @@ module MatrixSdk
55
54
 
56
55
  # Gets the avatar for the user
57
56
  #
58
- # @see MatrixSdk::Protocols::CS#get_avatar_url
57
+ # @see ActiveMatrix::Protocols::CS#get_avatar_url
59
58
  def avatar_url
60
59
  @avatar_url ||= client.api.get_avatar_url(id)[:avatar_url]
61
60
  end
@@ -67,8 +66,8 @@ module MatrixSdk
67
66
  #
68
67
  # @param url [String,URI::MXC] the new avatar URL
69
68
  # @note Requires a mxc:// URL, check example on
70
- # {MatrixSdk::Protocols::CS#set_avatar_url} for how this can be done
71
- # @see MatrixSdk::Protocols::CS#set_avatar_url
69
+ # {ActiveMatrix::Protocols::CS#set_avatar_url} for how this can be done
70
+ # @see ActiveMatrix::Protocols::CS#set_avatar_url
72
71
  def avatar_url=(url)
73
72
  client.api.set_avatar_url(id, url)
74
73
  @avatar_url = url
@@ -93,7 +92,7 @@ module MatrixSdk
93
92
  # Get the user's current presence status
94
93
  #
95
94
  # @return [Symbol] One of :online, :offline, :unavailable
96
- # @see MatrixSdk::Protocols::CS#get_presence_status
95
+ # @see ActiveMatrix::Protocols::CS#get_presence_status
97
96
  # @note This information is not cached in the abstraction layer
98
97
  def presence
99
98
  raw_presence[:presence]&.to_sym
@@ -103,7 +102,7 @@ module MatrixSdk
103
102
  # Should be one of :online, :offline, or :unavailable
104
103
  #
105
104
  # @param new_presence [:online,:offline,:unavailable] The new presence status to set
106
- # @see MatrixSdk::Protocols::CS#set_presence_status
105
+ # @see ActiveMatrix::Protocols::CS#set_presence_status
107
106
  def presence=(new_presence)
108
107
  raise ArgumentError, 'Presence must be one of :online, :offline, :unavailable' unless %i[online offline unavailable].include?(presence)
109
108
 
@@ -118,7 +117,7 @@ module MatrixSdk
118
117
 
119
118
  # Gets the user-specified status message - if any
120
119
  #
121
- # @see MatrixSdk::Protocols::CS#get_presence_status
120
+ # @see ActiveMatrix::Protocols::CS#get_presence_status
122
121
  # @note This information is not cached in the abstraction layer
123
122
  def status_msg
124
123
  raw_presence[:status_msg]
@@ -127,7 +126,7 @@ module MatrixSdk
127
126
  # Sets the user-specified status message
128
127
  #
129
128
  # @param message [String,nil] The message to set, or nil for no message
130
- # @see MatrixSdk::Protocols::CS#set_presence_status
129
+ # @see ActiveMatrix::Protocols::CS#set_presence_status
131
130
  def status_msg=(message)
132
131
  client.api.set_presence_status(id, presence, message: message)
133
132
  end
@@ -135,7 +134,7 @@ module MatrixSdk
135
134
  # Gets the last time the user was active at, from the server's side
136
135
  #
137
136
  # @return [Time] when the user was last active
138
- # @see MatrixSdk::Protocols::CS#get_presence_status
137
+ # @see ActiveMatrix::Protocols::CS#get_presence_status
139
138
  # @note This information is not cached in the abstraction layer
140
139
  def last_active
141
140
  since = raw_presence[:last_active_ago]
@@ -147,7 +146,7 @@ module MatrixSdk
147
146
  # Gets a direct message room with the user if one exists
148
147
  #
149
148
  # @return [Room,nil] A direct message room if one exists
150
- # @see MatrixSdk::Client#direct_room
149
+ # @see ActiveMatrix::Client#direct_room
151
150
  def direct_room
152
151
  client.direct_room(id)
153
152
  end
@@ -159,6 +158,15 @@ module MatrixSdk
159
158
  end
160
159
  end
161
160
 
161
+ # Define what attributes to cache
162
+ def cache_attributes
163
+ {
164
+ id: @id,
165
+ display_name: @display_name,
166
+ avatar_url: @avatar_url
167
+ }
168
+ end
169
+
162
170
  private
163
171
 
164
172
  def raw_presence
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveMatrix::Util
4
+ class AccountDataCache
5
+ extend ActiveMatrix::Extensions
6
+ include Enumerable
7
+
8
+ attr_reader :client, :room
9
+
10
+ attr_accessor :cache_time
11
+
12
+ ignore_inspect :client, :room
13
+
14
+ def initialize(client, room: nil, cache_time: 1 * 60 * 60, **_params)
15
+ raise ArgumentError, 'Must be given a Client instance' unless client.is_a? ActiveMatrix::Client
16
+
17
+ @client = client
18
+ @cache_time = cache_time
19
+ @tracked_keys = Set.new
20
+
21
+ return unless room
22
+
23
+ @room = room
24
+ @room = client.ensure_room room unless @room.is_a? ActiveMatrix::Room
25
+ end
26
+
27
+ def reload!
28
+ # Clear all cache entries for this account data
29
+ return unless cache_available?
30
+
31
+ if room
32
+ cache.delete_matched("activematrix:account_data:#{client.mxid}:room:#{room.id}:*")
33
+ else
34
+ cache.delete_matched("activematrix:account_data:#{client.mxid}:global:*")
35
+ end
36
+ end
37
+
38
+ def keys
39
+ @tracked_keys.to_a.sort
40
+ end
41
+
42
+ def values
43
+ []
44
+ end
45
+
46
+ def size
47
+ keys.count
48
+ end
49
+
50
+ def key?(key)
51
+ cache_available? && cache.exist?(cache_key(key))
52
+ end
53
+
54
+ def each(live: false)
55
+ to_enum(__method__, live: live) { 0 } unless block_given?
56
+ # Not enumerable with Rails.cache
57
+ end
58
+
59
+ def delete(key)
60
+ key = key.to_s unless key.is_a? String
61
+ if room
62
+ client.api.set_room_account_data(client.mxid, room.id, key, {})
63
+ else
64
+ client.api.set_account_data(client.mxid, key, {})
65
+ end
66
+ cache.delete(cache_key(key)) if cache_available?
67
+ end
68
+
69
+ def [](key)
70
+ key = key.to_s unless key.is_a? String
71
+
72
+ # Track the key whenever it's accessed
73
+ @tracked_keys.add(key)
74
+
75
+ return fetch_account_data(key) unless cache_available?
76
+
77
+ cache.fetch(cache_key(key), expires_in: @cache_time) do
78
+ fetch_account_data(key)
79
+ end
80
+ end
81
+
82
+ def fetch_account_data(key)
83
+ if room
84
+ client.api.get_room_account_data(client.mxid, room.id, key)
85
+ else
86
+ client.api.get_account_data(client.mxid, key)
87
+ end
88
+ rescue ActiveMatrix::MatrixNotFoundError
89
+ {}
90
+ end
91
+
92
+ def []=(key, value)
93
+ key = key.to_s unless key.is_a? String
94
+ if room
95
+ client.api.set_room_account_data(client.mxid, room.id, key, value)
96
+ else
97
+ client.api.set_account_data(client.mxid, key, value)
98
+ end
99
+
100
+ @tracked_keys.add(key)
101
+ cache.write(cache_key(key), value, expires_in: @cache_time) if cache_available?
102
+ end
103
+
104
+ # Write data without making API call (for sync responses)
105
+ def write(key, value)
106
+ key = key.to_s unless key.is_a? String
107
+ @tracked_keys.add(key)
108
+ cache.write(cache_key(key), value, expires_in: @cache_time) if cache_available?
109
+ end
110
+
111
+ private
112
+
113
+ def cache_key(key)
114
+ if room
115
+ "activematrix:account_data:#{client.mxid}:room:#{room.id}:#{key}"
116
+ else
117
+ "activematrix:account_data:#{client.mxid}:global:#{key}"
118
+ end
119
+ end
120
+
121
+ def cache_available?
122
+ defined?(::Rails) && ::Rails.respond_to?(:cache) && ::Rails.cache
123
+ end
124
+
125
+ def cache
126
+ ::Rails.cache
127
+ end
128
+ end
129
+ end