activematrix 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MatrixSdk::Rooms
4
+ class Space < MatrixSdk::Room
5
+ TYPE = 'm.space'
6
+
7
+ def tree(suggested_only: nil, max_rooms: nil)
8
+ begin
9
+ data = client.api.request :get, :client_r0, "/rooms/#{id}/spaces", query: {
10
+ suggested_only: suggested_only,
11
+ max_rooms_per_space: max_rooms
12
+ }.compact
13
+ rescue MatrixRequestError
14
+ data = client.api.request :get, :client_unstable, "/org.matrix.msc2946/rooms/#{id}/spaces", query: {
15
+ suggested_only: suggested_only,
16
+ max_rooms_per_space: max_rooms
17
+ }.compact
18
+ end
19
+
20
+ rooms = data.rooms.map do |r|
21
+ next if r[:room_id] == id
22
+
23
+ room = client.ensure_room(r[:room_id])
24
+ room.instance_variable_set :@room_type, r[:room_type] if r.key? :room_type
25
+ room = room.to_space if room.space?
26
+
27
+ # Inject available room information
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
34
+ end
35
+ room
36
+ end
37
+ rooms.compact!
38
+
39
+ grouping = {}
40
+ data.events.each do |ev|
41
+ next unless ev[:type] == 'm.space.child'
42
+ next unless ev[:content].key? :via
43
+
44
+ d = (grouping[ev[:room_id]] ||= [])
45
+ d << ev[:state_key]
46
+ end
47
+
48
+ build_tree = proc do |entry|
49
+ next if entry.nil?
50
+
51
+ room = self if entry == id
52
+ room ||= rooms.find { |r| r.id == entry }
53
+ puts "Unable to find room for entry #{entry}" unless room
54
+ # next if room.nil?
55
+
56
+ ret = {
57
+ room => []
58
+ }
59
+
60
+ grouping[entry]&.each do |child|
61
+ if grouping.key?(child)
62
+ ret[room] << build_tree.call(child)
63
+ else
64
+ child_r = self if child == id
65
+ child_r ||= rooms.find { |r| r.id == child }
66
+
67
+ ret[room] << child_r
68
+ end
69
+ end
70
+
71
+ ret[room].compact!
72
+
73
+ ret
74
+ end
75
+
76
+ build_tree.call(id)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'matrix_sdk'
4
+
5
+ module MatrixSdk
6
+ # A class for tracking information about a user on Matrix
7
+ class User
8
+ extend MatrixSdk::Extensions
9
+
10
+ attr_reader :id, :client
11
+ alias user_id :id
12
+
13
+ # @!method inspect
14
+ # An inspect method that skips a handful of instance variables to avoid
15
+ # flooding the terminal with debug data.
16
+ # @return [String] a regular inspect string without the data for some variables
17
+ ignore_inspect :client
18
+
19
+ def initialize(client, id, data = {})
20
+ @client = client
21
+ @id = id
22
+
23
+ @display_name = nil
24
+ @avatar_url = nil
25
+
26
+ data.each do |k, v|
27
+ instance_variable_set("@#{k}", v) if instance_variable_defined? "@#{k}"
28
+ end
29
+ end
30
+
31
+ def to_s
32
+ "#{display_name} (#{id})" if @display_name
33
+
34
+ @id.to_s
35
+ end
36
+
37
+ # @return [String] the display name
38
+ # @see MatrixSdk::Protocols::CS#get_display_name
39
+ def display_name
40
+ @display_name ||= client.api.get_display_name(id)[:displayname]
41
+ end
42
+
43
+ # @param name [String] the display name to set
44
+ # @see MatrixSdk::Protocols::CS#set_display_name
45
+ def display_name=(name)
46
+ client.api.set_display_name(id, name)
47
+ @display_name = name
48
+ end
49
+
50
+ # Gets a friendly name of the user
51
+ # @return [String] either the display name or MXID if unset
52
+ def friendly_name
53
+ display_name || id
54
+ end
55
+
56
+ # Gets the avatar for the user
57
+ #
58
+ # @see MatrixSdk::Protocols::CS#get_avatar_url
59
+ def avatar_url
60
+ @avatar_url ||= client.api.get_avatar_url(id)[:avatar_url]
61
+ end
62
+
63
+ # Set a new avatar for the user
64
+ #
65
+ # Only works for the current user object, as requested by
66
+ # client.get_user(:self)
67
+ #
68
+ # @param url [String,URI::MXC] the new avatar URL
69
+ # @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
72
+ def avatar_url=(url)
73
+ client.api.set_avatar_url(id, url)
74
+ @avatar_url = url
75
+ end
76
+
77
+ # Check if the user is an admin in a given room
78
+ #
79
+ # @param room [String,MXID] the room to check
80
+ # @return [Boolean] If the user is an admin (PL >= 100)
81
+ def admin?(room)
82
+ client.ensure_room(room).user_powerlevel(self) >= 100
83
+ end
84
+
85
+ # Check if the user is a moderator in a given room
86
+ #
87
+ # @param room [String,MXID] the room to check
88
+ # @return [Boolean] If the user is an admin (PL >= 50)
89
+ def moderator?(room)
90
+ client.ensure_room(room).user_powerlevel(self) >= 50
91
+ end
92
+
93
+ # Get the user's current presence status
94
+ #
95
+ # @return [Symbol] One of :online, :offline, :unavailable
96
+ # @see MatrixSdk::Protocols::CS#get_presence_status
97
+ # @note This information is not cached in the abstraction layer
98
+ def presence
99
+ raw_presence[:presence]&.to_sym
100
+ end
101
+
102
+ # Sets the user's current presence status
103
+ # Should be one of :online, :offline, or :unavailable
104
+ #
105
+ # @param new_presence [:online,:offline,:unavailable] The new presence status to set
106
+ # @see MatrixSdk::Protocols::CS#set_presence_status
107
+ def presence=(new_presence)
108
+ raise ArgumentError, 'Presence must be one of :online, :offline, :unavailable' unless %i[online offline unavailable].include?(presence)
109
+
110
+ client.api.set_presence_status(id, new_presence)
111
+ end
112
+
113
+ # @return [Boolean] if the user is currently active
114
+ # @note This information is not cached in the abstraction layer
115
+ def active?
116
+ raw_presence[:currently_active] == true
117
+ end
118
+
119
+ # Gets the user-specified status message - if any
120
+ #
121
+ # @see MatrixSdk::Protocols::CS#get_presence_status
122
+ # @note This information is not cached in the abstraction layer
123
+ def status_msg
124
+ raw_presence[:status_msg]
125
+ end
126
+
127
+ # Sets the user-specified status message
128
+ #
129
+ # @param message [String,nil] The message to set, or nil for no message
130
+ # @see MatrixSdk::Protocols::CS#set_presence_status
131
+ def status_msg=(message)
132
+ client.api.set_presence_status(id, presence, message: message)
133
+ end
134
+
135
+ # Gets the last time the user was active at, from the server's side
136
+ #
137
+ # @return [Time] when the user was last active
138
+ # @see MatrixSdk::Protocols::CS#get_presence_status
139
+ # @note This information is not cached in the abstraction layer
140
+ def last_active
141
+ since = raw_presence[:last_active_ago]
142
+ return unless since
143
+
144
+ Time.now - (since / 1000)
145
+ end
146
+
147
+ # Gets a direct message room with the user if one exists
148
+ #
149
+ # @return [Room,nil] A direct message room if one exists
150
+ # @see MatrixSdk::Client#direct_room
151
+ def direct_room
152
+ client.direct_room(id)
153
+ end
154
+
155
+ # Returns all the current device keys for the user, retrieving them if necessary
156
+ def device_keys
157
+ @device_keys ||= client.api.keys_query(device_keys: { id => [] }).yield_self do |resp| # rubocop:disable Style/ObjectThen # Keep Ruby 2.5 support a little longer
158
+ resp.dig(:device_keys, id.to_sym)
159
+ end
160
+ end
161
+
162
+ private
163
+
164
+ def raw_presence
165
+ client.api.get_presence_status(id).tap { |h| h.delete :user_id }
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MatrixSdk::Util
4
+ class AccountDataCache
5
+ extend MatrixSdk::Extensions
6
+ extend MatrixSdk::Util::Tinycache
7
+ include Enumerable
8
+
9
+ attr_reader :client, :room
10
+
11
+ attr_accessor :cache_time
12
+
13
+ ignore_inspect :client, :room, :tinycache_adapter
14
+
15
+ def initialize(client, room: nil, cache_time: 1 * 60 * 60, **_params)
16
+ raise ArgumentError, 'Must be given a Client instance' unless client.is_a? MatrixSdk::Client
17
+
18
+ @client = client
19
+ @cache_time = cache_time
20
+
21
+ return unless room
22
+
23
+ @room = room
24
+ @room = client.ensure_room room unless @room.is_a? MatrixSdk::Room
25
+ end
26
+
27
+ def reload!
28
+ tinycache_adapter.clear
29
+ end
30
+
31
+ def keys
32
+ tinycache_adapter.send(:cache).keys
33
+ end
34
+
35
+ def values
36
+ keys.map { |key| tinycache_adapter.read(key) }
37
+ end
38
+
39
+ def size
40
+ keys.count
41
+ end
42
+
43
+ def key?(key)
44
+ keys.key?(key.to_s)
45
+ end
46
+
47
+ def each(live: false)
48
+ return to_enum(__method__, live: live) { keys.count } unless block_given?
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
56
+ end
57
+
58
+ def delete(key)
59
+ key = key.to_s unless key.is_a? String
60
+ if room
61
+ client.api.set_room_account_data(client.mxid, room.id, key, {})
62
+ else
63
+ client.api.set_account_data(client.mxid, key, {})
64
+ end
65
+ tinycache_adapter.delete(key)
66
+ end
67
+
68
+ def [](key)
69
+ key = key.to_s unless key.is_a? String
70
+ tinycache_adapter.fetch(key, expires_in: @cache_time) do
71
+ if room
72
+ client.api.get_room_account_data(client.mxid, room.id, key)
73
+ else
74
+ client.api.get_account_data(client.mxid, key)
75
+ end
76
+ rescue MatrixSdk::MatrixNotFoundError
77
+ {}
78
+ end
79
+ end
80
+
81
+ def []=(key, value)
82
+ key = key.to_s unless key.is_a? String
83
+ if room
84
+ client.api.set_room_account_data(client.mxid, room.id, key, value)
85
+ else
86
+ client.api.set_account_data(client.mxid, key, value)
87
+ end
88
+ tinycache_adapter.write(key, value)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MatrixSdk
4
+ class EventHandlerArray < Hash
5
+ include MatrixSdk::Logging
6
+ attr_accessor :reraise_exceptions
7
+
8
+ def initialize(*args)
9
+ @reraise_exceptions = false
10
+
11
+ super(*args)
12
+ end
13
+
14
+ def add_handler(filter = nil, id = nil, &block)
15
+ id ||= block.hash
16
+ self[id] = { filter: filter, id: id, block: block }
17
+ end
18
+
19
+ def remove_handler(id)
20
+ delete id
21
+ end
22
+
23
+ def fire(event, filter = nil)
24
+ reverse_each do |_k, h|
25
+ h[:block].call(event) if !h[:filter] || event.matches?(h[:filter], filter)
26
+ rescue StandardError => e
27
+ logger.error "#{e.class.name} occurred when firing event (#{event})\n#{e}"
28
+
29
+ raise e if @reraise_exceptions
30
+ end
31
+ end
32
+ end
33
+
34
+ class Event
35
+ extend MatrixSdk::Extensions
36
+
37
+ attr_writer :handled
38
+
39
+ ignore_inspect :sender
40
+
41
+ def initialize(sender)
42
+ @sender = sender
43
+ @handled = false
44
+ end
45
+
46
+ def handled?
47
+ @handled
48
+ end
49
+
50
+ def matches?(_filter)
51
+ true
52
+ end
53
+ end
54
+
55
+ class ErrorEvent < Event
56
+ attr_accessor :error
57
+
58
+ def initialize(error, source)
59
+ @error = error
60
+ super source
61
+ end
62
+
63
+ def source
64
+ @sender
65
+ end
66
+ end
67
+
68
+ class MatrixEvent < Event
69
+ attr_accessor :event, :filter
70
+ alias data event
71
+
72
+ ignore_inspect :sender
73
+
74
+ def initialize(sender, event = nil, filter = nil)
75
+ @event = event
76
+ @filter = filter || @event[:type]
77
+ super sender
78
+ end
79
+
80
+ def matches?(filter, filter_override = nil)
81
+ return true if filter_override.nil? && (@filter.nil? || filter.nil?)
82
+
83
+ to_match = filter_override || @filter
84
+ if filter.is_a? Regexp
85
+ filter.match(to_match) { true } || false
86
+ else
87
+ to_match == filter
88
+ end
89
+ end
90
+
91
+ def [](key)
92
+ event[key]
93
+ end
94
+
95
+ def to_s
96
+ "#{event[:type]}: #{event.reject { |k, _v| k == :type }.to_json}"
97
+ end
98
+
99
+ def method_missing(method, *args)
100
+ return event[method] if event.key? method
101
+
102
+ super
103
+ end
104
+
105
+ def respond_to_missing?(method, *)
106
+ return true if event.key? method
107
+
108
+ super
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ unless Object.respond_to? :yield_self
4
+ class Object
5
+ def yield_self
6
+ yield(self)
7
+ end
8
+ end
9
+ end
10
+
11
+ module MatrixSdk
12
+ module Extensions
13
+ def events(*symbols)
14
+ module_name = "#{name}Events"
15
+
16
+ initializers = []
17
+ readers = []
18
+ methods = []
19
+
20
+ symbols.each do |sym|
21
+ name = sym.to_s
22
+
23
+ initializers << "
24
+ @on_#{name} = MatrixSdk::EventHandlerArray.new
25
+ "
26
+ readers << ":on_#{name}"
27
+ methods << "
28
+ def fire_#{name}(ev, filter = nil)
29
+ @on_#{name}.fire(ev, filter)
30
+ when_#{name}(ev) if !ev.handled?
31
+ end
32
+
33
+ def when_#{name}(ev); end
34
+ "
35
+ end
36
+
37
+ class_eval "
38
+ module #{module_name}
39
+ attr_reader #{readers.join ', '}
40
+
41
+ def event_initialize
42
+ #{initializers.join}
43
+ end
44
+
45
+ #{methods.join}
46
+ end
47
+
48
+ include #{module_name}
49
+ ", __FILE__, __LINE__ - 12
50
+ end
51
+
52
+ def ignore_inspect(*symbols)
53
+ require 'pp'
54
+
55
+ class_eval %*
56
+ def pretty_print_instance_variables
57
+ instance_variables
58
+ .reject { |f| %i[#{symbols.map { |s| "@#{s}" }.join ' '}].include? f }
59
+ .sort
60
+ end
61
+
62
+ def pretty_print(pp)
63
+ return pp.pp(self) if respond_to? :mocha_inspect
64
+
65
+ pp.pp_object(self)
66
+ end
67
+
68
+ alias inspect pretty_print_inspect
69
+ *, __FILE__, __LINE__ - 14
70
+ end
71
+ end
72
+
73
+ module Logging
74
+ def logger
75
+ return MatrixSdk.logger if MatrixSdk.global_logger?
76
+ return @logger if instance_variable_defined?(:@logger) && @logger
77
+
78
+ ::Logging.logger[self]
79
+ end
80
+
81
+ def logger=(logger)
82
+ @logger = logger
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MatrixSdk::Util
4
+ class StateEventCache
5
+ extend MatrixSdk::Extensions
6
+ extend MatrixSdk::Util::Tinycache
7
+ include Enumerable
8
+
9
+ attr_reader :room
10
+
11
+ attr_accessor :cache_time
12
+
13
+ ignore_inspect :client, :room, :tinycache_adapter
14
+
15
+ def initialize(room, cache_time: 30 * 60, **_params)
16
+ raise ArgumentError, 'Must be given a Room instance' unless room.is_a? MatrixSdk::Room
17
+
18
+ @room = room
19
+ @cache_time = cache_time
20
+ end
21
+
22
+ def client
23
+ @room.client
24
+ end
25
+
26
+ def reload!
27
+ tinycache_adapter.clear
28
+ end
29
+
30
+ def keys
31
+ tinycache_adapter.send(:cache).keys.map do |type|
32
+ real_type = type.split('|').first
33
+ state_key = type.split('|').last
34
+ state_key = nil if state_key == real_type
35
+
36
+ [real_type, state_key]
37
+ end
38
+ end
39
+
40
+ def values
41
+ keys.map { |key| tinycache_adapter.read(key) }
42
+ end
43
+
44
+ def size
45
+ keys.count
46
+ end
47
+
48
+ def key?(type, key = nil)
49
+ keys.key?("#{type}#{key ? "|#{key}" : ''}")
50
+ end
51
+
52
+ def expire(type, key = nil)
53
+ tinycache_adapter.expire("#{type}#{key ? "|#{key}" : ''}")
54
+ end
55
+
56
+ def each(live: false)
57
+ return to_enum(__method__, live: live) { keys.count } unless block_given?
58
+
59
+ keys.each do |type|
60
+ real_type = type.split('|').first
61
+ state_key = type.split('|').last
62
+ state_key = nil if state_key == real_type
63
+
64
+ v = live ? self[real_type, key: state_key] : tinycache_adapter.read(type)
65
+ # hash = v.hash
66
+ yield [real_type, state_key], v
67
+ # self[key] = v if hash != v.hash
68
+ end
69
+ end
70
+
71
+ def delete(type, key = nil)
72
+ type = type.to_s unless type.is_a? String
73
+ client.api.set_room_state(room.id, type, {}, **{ state_key: key }.compact)
74
+ tinycache_adapter.delete("#{type}#{key ? "|#{key}" : ''}")
75
+ end
76
+
77
+ def [](type, key = nil)
78
+ type = type.to_s unless type.is_a? String
79
+ tinycache_adapter.fetch("#{type}#{key ? "|#{key}" : ''}", expires_in: @cache_time) do
80
+ client.api.get_room_state(room.id, type, **{ key: key }.compact)
81
+ rescue MatrixSdk::MatrixNotFoundError
82
+ {}
83
+ end
84
+ end
85
+
86
+ def []=(type, key = nil, value) # rubocop:disable Style/OptionalArguments Not possible to put optional last
87
+ type = type.to_s unless type.is_a? String
88
+ client.api.set_room_state(room.id, type, value, **{ state_key: key }.compact)
89
+ tinycache_adapter.write("#{type}#{key ? "|#{key}" : ''}", value)
90
+ end
91
+ end
92
+ end