activematrix 0.0.1 → 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.
- checksums.yaml +4 -4
- data/README.md +218 -51
- data/lib/active_matrix/agent_manager.rb +275 -0
- data/lib/active_matrix/agent_registry.rb +154 -0
- data/lib/active_matrix/bot/multi_instance_base.rb +189 -0
- data/lib/active_matrix/client.rb +5 -15
- data/lib/active_matrix/client_pool.rb +194 -0
- data/lib/active_matrix/event_router.rb +215 -0
- data/lib/active_matrix/memory/agent_memory.rb +128 -0
- data/lib/active_matrix/memory/base.rb +101 -0
- data/lib/active_matrix/memory/conversation_memory.rb +161 -0
- data/lib/active_matrix/memory/global_memory.rb +153 -0
- data/lib/active_matrix/memory.rb +28 -0
- data/lib/active_matrix/room.rb +131 -51
- data/lib/active_matrix/rooms/space.rb +1 -5
- data/lib/active_matrix/user.rb +10 -0
- data/lib/active_matrix/util/account_data_cache.rb +62 -24
- data/lib/active_matrix/util/cacheable.rb +73 -0
- data/lib/active_matrix/util/extensions.rb +4 -0
- data/lib/active_matrix/util/state_event_cache.rb +106 -31
- data/lib/active_matrix/version.rb +1 -1
- data/lib/active_matrix.rb +51 -3
- data/lib/generators/active_matrix/bot/bot_generator.rb +38 -0
- data/lib/generators/active_matrix/bot/templates/bot.rb.erb +111 -0
- data/lib/generators/active_matrix/bot/templates/bot_spec.rb.erb +68 -0
- data/lib/generators/active_matrix/install/install_generator.rb +44 -0
- data/lib/generators/active_matrix/install/templates/README +30 -0
- data/lib/generators/active_matrix/install/templates/active_matrix.rb +33 -0
- data/lib/generators/active_matrix/install/templates/agent_memory.rb +47 -0
- data/lib/generators/active_matrix/install/templates/conversation_context.rb +72 -0
- data/lib/generators/active_matrix/install/templates/create_agent_memories.rb +17 -0
- data/lib/generators/active_matrix/install/templates/create_conversation_contexts.rb +21 -0
- data/lib/generators/active_matrix/install/templates/create_global_memories.rb +20 -0
- data/lib/generators/active_matrix/install/templates/create_matrix_agents.rb +26 -0
- data/lib/generators/active_matrix/install/templates/global_memory.rb +70 -0
- data/lib/generators/active_matrix/install/templates/matrix_agent.rb +127 -0
- metadata +110 -4
- data/lib/active_matrix/util/rails_cache_adapter.rb +0 -37
- data/lib/active_matrix/util/tinycache.rb +0 -145
- data/lib/active_matrix/util/tinycache_adapter.rb +0 -87
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
module ActiveMatrix
|
6
|
+
module Memory
|
7
|
+
# Global memory storage accessible to all agents
|
8
|
+
class GlobalMemory < Base
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
# Get a value from global memory
|
12
|
+
def get(key)
|
13
|
+
fetch_with_cache(key) do
|
14
|
+
return nil unless defined?(::GlobalMemory)
|
15
|
+
|
16
|
+
::GlobalMemory.get(key)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set a value in global memory
|
21
|
+
def set(key, value, category: nil, expires_in: nil, public_read: true, public_write: false)
|
22
|
+
return false unless defined?(::GlobalMemory)
|
23
|
+
|
24
|
+
write_through(key, value, expires_in: expires_in) do
|
25
|
+
::GlobalMemory.set(key, value,
|
26
|
+
category: category,
|
27
|
+
expires_in: expires_in,
|
28
|
+
public_read: public_read,
|
29
|
+
public_write: public_write)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Check if a key exists
|
34
|
+
def exists?(key)
|
35
|
+
return false unless defined?(::GlobalMemory)
|
36
|
+
|
37
|
+
if @cache_enabled && Rails.cache.exist?(cache_key(key))
|
38
|
+
true
|
39
|
+
else
|
40
|
+
::GlobalMemory.active.exists?(key: key)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Delete a key
|
45
|
+
def delete(key)
|
46
|
+
return false unless defined?(::GlobalMemory)
|
47
|
+
|
48
|
+
delete_through(key) do
|
49
|
+
::GlobalMemory.where(key: key).destroy_all.any?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get all keys in a category
|
54
|
+
def keys(category: nil)
|
55
|
+
return [] unless defined?(::GlobalMemory)
|
56
|
+
|
57
|
+
scope = ::GlobalMemory.active
|
58
|
+
scope = scope.by_category(category) if category
|
59
|
+
scope.pluck(:key)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get all values in a category
|
63
|
+
def by_category(category)
|
64
|
+
return {} unless defined?(::GlobalMemory)
|
65
|
+
|
66
|
+
::GlobalMemory.active.by_category(category).pluck(:key, :value).to_h
|
67
|
+
end
|
68
|
+
|
69
|
+
# Check if readable by agent
|
70
|
+
def readable?(key, agent = nil)
|
71
|
+
return false unless defined?(::GlobalMemory)
|
72
|
+
|
73
|
+
memory = ::GlobalMemory.find_by(key: key)
|
74
|
+
memory&.readable_by?(agent)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Check if writable by agent
|
78
|
+
def writable?(key, agent = nil)
|
79
|
+
return false unless defined?(::GlobalMemory)
|
80
|
+
|
81
|
+
memory = ::GlobalMemory.find_by(key: key)
|
82
|
+
memory&.writable_by?(agent)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get with permission check
|
86
|
+
def get_for_agent(key, agent)
|
87
|
+
return nil unless readable?(key, agent)
|
88
|
+
|
89
|
+
get(key)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Set with permission check
|
93
|
+
def set_for_agent(key, value, agent, **)
|
94
|
+
# Allow creating new keys or updating writable ones
|
95
|
+
memory = ::GlobalMemory.find_by(key: key)
|
96
|
+
return false if memory && !memory.writable_by?(agent)
|
97
|
+
|
98
|
+
set(key, value, **)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Remember something globally
|
102
|
+
def remember(key, **)
|
103
|
+
value = get(key)
|
104
|
+
return value if value.present?
|
105
|
+
|
106
|
+
value = yield
|
107
|
+
set(key, value, **) if value.present?
|
108
|
+
value
|
109
|
+
end
|
110
|
+
|
111
|
+
# Broadcast a value to all agents
|
112
|
+
def broadcast(key, value, expires_in: 5.minutes)
|
113
|
+
set(key, value, category: 'broadcast', expires_in: expires_in, public_read: true)
|
114
|
+
|
115
|
+
# Notify all agents if event router is available
|
116
|
+
if defined?(EventRouter)
|
117
|
+
EventRouter.instance.broadcast_event({
|
118
|
+
type: 'global_memory.broadcast',
|
119
|
+
key: key,
|
120
|
+
value: value
|
121
|
+
})
|
122
|
+
end
|
123
|
+
|
124
|
+
true
|
125
|
+
end
|
126
|
+
|
127
|
+
# Share data between specific agents
|
128
|
+
def share(key, value, agent_names, expires_in: nil)
|
129
|
+
set(key, {
|
130
|
+
value: value,
|
131
|
+
allowed_agents: agent_names
|
132
|
+
}, category: 'shared', expires_in: expires_in, public_read: false)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Get shared data if allowed
|
136
|
+
def get_shared(key, agent)
|
137
|
+
data = get(key)
|
138
|
+
return nil unless data.is_a?(Hash) && data['allowed_agents']
|
139
|
+
|
140
|
+
allowed = data['allowed_agents']
|
141
|
+
return unless allowed.include?(agent.name) || allowed.include?('*')
|
142
|
+
|
143
|
+
data['value']
|
144
|
+
end
|
145
|
+
|
146
|
+
protected
|
147
|
+
|
148
|
+
def cache_key(key)
|
149
|
+
"global/#{key}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveMatrix
|
4
|
+
# Memory system for multi-agent architecture
|
5
|
+
module Memory
|
6
|
+
autoload :AgentMemory, 'active_matrix/memory/agent_memory'
|
7
|
+
autoload :ConversationMemory, 'active_matrix/memory/conversation_memory'
|
8
|
+
autoload :GlobalMemory, 'active_matrix/memory/global_memory'
|
9
|
+
autoload :Base, 'active_matrix/memory/base'
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# Get memory interface for an agent
|
13
|
+
def for_agent(agent)
|
14
|
+
AgentMemory.new(agent)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Get conversation memory for agent and user
|
18
|
+
def for_conversation(agent, user_id, room_id)
|
19
|
+
ConversationMemory.new(agent, user_id, room_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Access global memory
|
23
|
+
def global
|
24
|
+
GlobalMemory.instance
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/active_matrix/room.rb
CHANGED
@@ -4,8 +4,8 @@ module ActiveMatrix
|
|
4
4
|
# A class for tracking the information about a room on Matrix
|
5
5
|
class Room
|
6
6
|
extend ActiveMatrix::Extensions
|
7
|
-
extend ActiveMatrix::Util::Tinycache
|
8
7
|
include ActiveMatrix::Logging
|
8
|
+
include ActiveMatrix::Util::Cacheable
|
9
9
|
|
10
10
|
# @!attribute [rw] event_history_limit
|
11
11
|
# @return [Fixnum] the limit of events to keep in the event log
|
@@ -24,17 +24,9 @@ module ActiveMatrix
|
|
24
24
|
# An inspect method that skips a handful of instance variables to avoid
|
25
25
|
# flooding the terminal with debug data.
|
26
26
|
# @return [String] a regular inspect string without the data for some variables
|
27
|
-
ignore_inspect :client, :events, :prev_batch, :logger
|
28
|
-
|
29
|
-
# Requires heavy lookups, so they're cached for an hour
|
30
|
-
cached :joined_members, cache_level: :all, expires_in: 60 * 60
|
31
|
-
|
32
|
-
# Only cache unfiltered requests for aliases and members
|
33
|
-
cached :aliases, unless: proc { |_, args| args.any? }, cache_level: :all, expires_in: 60 * 60
|
34
|
-
cached :all_members, unless: proc { |_, args| args.any? }, cache_level: :all, expires_in: 60 * 60
|
27
|
+
ignore_inspect :client, :events, :prev_batch, :logger
|
35
28
|
|
36
29
|
alias room_id id
|
37
|
-
alias members joined_members
|
38
30
|
|
39
31
|
# Create a new room instance
|
40
32
|
#
|
@@ -74,23 +66,22 @@ module ActiveMatrix
|
|
74
66
|
@events = []
|
75
67
|
@event_history_limit = 10
|
76
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
|
77
72
|
|
78
73
|
@prev_batch = nil
|
79
74
|
|
80
75
|
%i[name topic canonical_alias avatar_url].each do |type|
|
81
|
-
room_state.
|
76
|
+
room_state.write("m.room.#{type}", { type => data.delete(type) }) if data.key? type
|
82
77
|
end
|
83
|
-
room_state.
|
84
|
-
room_state.
|
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
|
85
80
|
|
86
81
|
data.each do |k, v|
|
87
82
|
next if %i[client].include? k
|
88
83
|
|
89
|
-
|
90
|
-
tinycache_adapter.write(k, v)
|
91
|
-
elsif instance_variable_defined? "@#{k}"
|
92
|
-
instance_variable_set("@#{k}", v)
|
93
|
-
end
|
84
|
+
instance_variable_set("@#{k}", v) if instance_variable_defined? "@#{k}"
|
94
85
|
end
|
95
86
|
|
96
87
|
@id = room_id.to_s
|
@@ -182,6 +173,36 @@ module ActiveMatrix
|
|
182
173
|
#
|
183
174
|
# @return [Array(User)] The list of members in the room
|
184
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
|
185
206
|
client.api.get_room_joined_members(id)[:joined].map do |mxid, data|
|
186
207
|
User.new(client, mxid.to_s,
|
187
208
|
display_name: data.fetch(:display_name, nil),
|
@@ -189,6 +210,8 @@ module ActiveMatrix
|
|
189
210
|
end
|
190
211
|
end
|
191
212
|
|
213
|
+
alias members joined_members
|
214
|
+
|
192
215
|
# Get all members (member events) in the room
|
193
216
|
#
|
194
217
|
# @note This will also count members who've knocked, been invited, have left, or have been banned.
|
@@ -197,6 +220,24 @@ module ActiveMatrix
|
|
197
220
|
#
|
198
221
|
# @return [Array(User)] The complete list of members in the room, regardless of membership state
|
199
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)
|
200
241
|
client.api.get_room_members(id, **params)[:chunk].map { |ch| client.get_user(ch[:state_key]) }
|
201
242
|
end
|
202
243
|
|
@@ -206,7 +247,8 @@ module ActiveMatrix
|
|
206
247
|
#
|
207
248
|
# @return [String,nil] The room name - if any
|
208
249
|
def name
|
209
|
-
get_state('m.room.name')
|
250
|
+
state = get_state('m.room.name')
|
251
|
+
state&.dig(:name)
|
210
252
|
rescue MatrixNotFoundError
|
211
253
|
# No room name has been specified
|
212
254
|
nil
|
@@ -323,8 +365,20 @@ module ActiveMatrix
|
|
323
365
|
# @param canonical_only [Boolean] Should the list of aliases only contain the canonical ones
|
324
366
|
# @return [Array[String]] The assigned room aliases
|
325
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)
|
326
376
|
canonical = get_state('m.room.canonical_alias') rescue {}
|
327
|
-
|
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
|
328
382
|
return aliases if canonical_only
|
329
383
|
|
330
384
|
(aliases + client.api.get_room_aliases(id).aliases).uniq.sort
|
@@ -735,7 +789,8 @@ module ActiveMatrix
|
|
735
789
|
# @return [Boolean] if the addition was successful or not
|
736
790
|
def add_alias(room_alias)
|
737
791
|
client.api.set_room_alias(id, room_alias)
|
738
|
-
|
792
|
+
# Clear the cache to force refresh
|
793
|
+
cache.delete(cache_key(:aliases)) if cache_available?
|
739
794
|
true
|
740
795
|
end
|
741
796
|
|
@@ -746,7 +801,7 @@ module ActiveMatrix
|
|
746
801
|
# alias list updates.
|
747
802
|
def reload_aliases!
|
748
803
|
room_state.expire('m.room.canonical_alias')
|
749
|
-
|
804
|
+
cache.delete(cache_key(:aliases)) if cache_available?
|
750
805
|
end
|
751
806
|
alias refresh_aliases! reload_aliases!
|
752
807
|
|
@@ -896,7 +951,7 @@ module ActiveMatrix
|
|
896
951
|
def modify_user_power_levels(users = nil, users_default = nil)
|
897
952
|
return false if users.nil? && users_default.nil?
|
898
953
|
|
899
|
-
room_state.
|
954
|
+
room_state.expire 'm.room.power_levels'
|
900
955
|
|
901
956
|
data = power_levels
|
902
957
|
data[:users_default] = users_default unless users_default.nil?
|
@@ -928,7 +983,7 @@ module ActiveMatrix
|
|
928
983
|
def modify_required_power_levels(events = nil, params = {})
|
929
984
|
return false if events.nil? && (params.nil? || params.empty?)
|
930
985
|
|
931
|
-
room_state.
|
986
|
+
room_state.expire 'm.room.power_levels'
|
932
987
|
|
933
988
|
data = power_levels
|
934
989
|
data.merge!(params)
|
@@ -946,38 +1001,52 @@ module ActiveMatrix
|
|
946
1001
|
|
947
1002
|
private
|
948
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
|
+
|
949
1016
|
def ensure_member(member)
|
950
1017
|
return unless client.cache == :all
|
951
1018
|
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
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)
|
956
1022
|
|
957
|
-
|
1023
|
+
# Clear the cache to force a refresh on next access
|
1024
|
+
cache.delete(cache_key(:joined_members)) if cache_available?
|
958
1025
|
end
|
959
1026
|
|
960
1027
|
def handle_room_member(event)
|
961
1028
|
return unless client.cache == :all
|
962
1029
|
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
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?
|
971
1043
|
end
|
972
1044
|
|
973
1045
|
def handle_room_canonical_alias(event)
|
974
|
-
room_state.
|
975
|
-
canonical_alias = event.dig(*%i[content alias])
|
1046
|
+
room_state.write('m.room.canonical_alias', event[:content])
|
976
1047
|
|
977
|
-
|
978
|
-
|
979
|
-
data += event.dig(*%i[content alt_aliases]) || []
|
980
|
-
tinycache_adapter.write(:aliases, data.uniq.sort)
|
1048
|
+
# Clear the aliases cache
|
1049
|
+
cache.delete(cache_key(:aliases)) if cache_available?
|
981
1050
|
end
|
982
1051
|
|
983
1052
|
def room_handlers?
|
@@ -1001,10 +1070,8 @@ module ActiveMatrix
|
|
1001
1070
|
end
|
1002
1071
|
|
1003
1072
|
def put_account_data(event)
|
1004
|
-
|
1005
|
-
|
1006
|
-
adapter.write(event[:type], event[:content], expires_in: account_data.cache_time)
|
1007
|
-
end
|
1073
|
+
# Store the account data in cache
|
1074
|
+
account_data.write(event[:type], event[:content]) if event[:type]
|
1008
1075
|
|
1009
1076
|
return unless room_handlers?
|
1010
1077
|
|
@@ -1026,15 +1093,28 @@ module ActiveMatrix
|
|
1026
1093
|
if INTERNAL_HANDLERS.key? event[:type]
|
1027
1094
|
send(INTERNAL_HANDLERS[event[:type]], event)
|
1028
1095
|
elsif client.cache != :none
|
1029
|
-
|
1030
|
-
|
1031
|
-
key += "|#{event[:state_key]}" unless event[:state_key].nil? || event[:state_key].empty?
|
1032
|
-
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])
|
1033
1098
|
end
|
1034
1099
|
|
1035
1100
|
return unless room_handlers?
|
1036
1101
|
|
1037
1102
|
ensure_room_handlers[:state_event].fire(MatrixEvent.new(self, event), event[:type])
|
1038
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
|
1039
1119
|
end
|
1040
1120
|
end
|
@@ -26,11 +26,7 @@ module ActiveMatrix::Rooms
|
|
26
26
|
|
27
27
|
# Inject available room information
|
28
28
|
r.each do |k, v|
|
29
|
-
|
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
|
data/lib/active_matrix/user.rb
CHANGED
@@ -4,6 +4,7 @@ module ActiveMatrix
|
|
4
4
|
# A class for tracking information about a user on Matrix
|
5
5
|
class User
|
6
6
|
extend ActiveMatrix::Extensions
|
7
|
+
include ActiveMatrix::Util::Cacheable
|
7
8
|
|
8
9
|
attr_reader :id, :client
|
9
10
|
alias user_id :id
|
@@ -157,6 +158,15 @@ module ActiveMatrix
|
|
157
158
|
end
|
158
159
|
end
|
159
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
|
+
|
160
170
|
private
|
161
171
|
|
162
172
|
def raw_presence
|
@@ -3,20 +3,20 @@
|
|
3
3
|
module ActiveMatrix::Util
|
4
4
|
class AccountDataCache
|
5
5
|
extend ActiveMatrix::Extensions
|
6
|
-
extend ActiveMatrix::Util::Tinycache
|
7
6
|
include Enumerable
|
8
7
|
|
9
8
|
attr_reader :client, :room
|
10
9
|
|
11
10
|
attr_accessor :cache_time
|
12
11
|
|
13
|
-
ignore_inspect :client, :room
|
12
|
+
ignore_inspect :client, :room
|
14
13
|
|
15
14
|
def initialize(client, room: nil, cache_time: 1 * 60 * 60, **_params)
|
16
15
|
raise ArgumentError, 'Must be given a Client instance' unless client.is_a? ActiveMatrix::Client
|
17
16
|
|
18
17
|
@client = client
|
19
18
|
@cache_time = cache_time
|
19
|
+
@tracked_keys = Set.new
|
20
20
|
|
21
21
|
return unless room
|
22
22
|
|
@@ -25,15 +25,22 @@ module ActiveMatrix::Util
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def reload!
|
28
|
-
|
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
|
29
36
|
end
|
30
37
|
|
31
38
|
def keys
|
32
|
-
|
39
|
+
@tracked_keys.to_a.sort
|
33
40
|
end
|
34
41
|
|
35
42
|
def values
|
36
|
-
|
43
|
+
[]
|
37
44
|
end
|
38
45
|
|
39
46
|
def size
|
@@ -41,18 +48,12 @@ module ActiveMatrix::Util
|
|
41
48
|
end
|
42
49
|
|
43
50
|
def key?(key)
|
44
|
-
|
51
|
+
cache_available? && cache.exist?(cache_key(key))
|
45
52
|
end
|
46
53
|
|
47
54
|
def each(live: false)
|
48
|
-
|
49
|
-
|
50
|
-
keys.each do |key|
|
51
|
-
v = live ? self[key] : tinycache_adapter.read(key)
|
52
|
-
# hash = v.hash
|
53
|
-
yield key, v
|
54
|
-
# self[key] = v if hash != v.hash
|
55
|
-
end
|
55
|
+
to_enum(__method__, live: live) { 0 } unless block_given?
|
56
|
+
# Not enumerable with Rails.cache
|
56
57
|
end
|
57
58
|
|
58
59
|
def delete(key)
|
@@ -62,20 +63,30 @@ module ActiveMatrix::Util
|
|
62
63
|
else
|
63
64
|
client.api.set_account_data(client.mxid, key, {})
|
64
65
|
end
|
65
|
-
|
66
|
+
cache.delete(cache_key(key)) if cache_available?
|
66
67
|
end
|
67
68
|
|
68
69
|
def [](key)
|
69
70
|
key = key.to_s unless key.is_a? String
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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)
|
78
87
|
end
|
88
|
+
rescue ActiveMatrix::MatrixNotFoundError
|
89
|
+
{}
|
79
90
|
end
|
80
91
|
|
81
92
|
def []=(key, value)
|
@@ -85,7 +96,34 @@ module ActiveMatrix::Util
|
|
85
96
|
else
|
86
97
|
client.api.set_account_data(client.mxid, key, value)
|
87
98
|
end
|
88
|
-
|
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
|
89
127
|
end
|
90
128
|
end
|
91
129
|
end
|