matrix_sdk 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -41,9 +41,13 @@ module MatrixSdk
41
41
  end
42
42
 
43
43
  class MatrixNotAuthorizedError < MatrixRequestError; end
44
+
44
45
  class MatrixForbiddenError < MatrixRequestError; end
46
+
45
47
  class MatrixNotFoundError < MatrixRequestError; end
48
+
46
49
  class MatrixConflictError < MatrixRequestError; end
50
+
47
51
  class MatrixTooManyRequestsError < MatrixRequestError; end
48
52
 
49
53
  # An error raised when errors occur in the connection layer
@@ -32,13 +32,13 @@ module MatrixSdk
32
32
  #
33
33
  # @return [String]
34
34
  def homeserver
35
- port_s = port ? ':' + port.to_s : ''
35
+ port_s = port ? ":#{port}" : ''
36
36
  domain ? domain + port_s : ''
37
37
  end
38
38
 
39
39
  # Gets the homserver part of the ID as a suffix (':homeserver')
40
40
  def homeserver_suffix
41
- ':' + homeserver if domain
41
+ ":#{homeserver}" if domain
42
42
  end
43
43
 
44
44
  def to_s
@@ -49,18 +49,13 @@ module MatrixSdk
49
49
  #
50
50
  # @return [Symbol] The MXID type, one of (:user_id, :room_id, :event_id, :group_id, or :room_alias)
51
51
  def type
52
- case sigil
53
- when '@'
54
- :user_id
55
- when '!'
56
- :room_id
57
- when '$'
58
- :event_id
59
- when '+'
60
- :group_id
61
- when '#'
62
- :room_alias
63
- end
52
+ {
53
+ '@' => :user_id,
54
+ '!' => :room_id,
55
+ '$' => :event_id,
56
+ '+' => :group_id,
57
+ '#' => :room_alias
58
+ }[sigil]
64
59
  end
65
60
 
66
61
  # Checks if the ID is valid
@@ -584,7 +584,7 @@ module MatrixSdk::Protocols::CS
584
584
  }
585
585
  content.merge!(params.fetch(:extra_content)) if params.key? :extra_content
586
586
 
587
- send_message_event(room_id, 'm.room.message', content, params)
587
+ send_message_event(room_id, 'm.room.message', content, **params)
588
588
  end
589
589
 
590
590
  # Send a geographic location to a room
@@ -610,7 +610,7 @@ module MatrixSdk::Protocols::CS
610
610
  content[:info][:thumbnail_url] = params.delete(:thumbnail_url) if params.key? :thumbnail_url
611
611
  content[:info][:thumbnail_info] = params.delete(:thumbnail_info) if params.key? :thumbnail_info
612
612
 
613
- send_message_event(room_id, 'm.room.message', content, params)
613
+ send_message_event(room_id, 'm.room.message', content, **params)
614
614
  end
615
615
 
616
616
  # Send a plaintext message to a room
@@ -628,7 +628,7 @@ module MatrixSdk::Protocols::CS
628
628
  msgtype: params.delete(:msg_type) { 'm.text' },
629
629
  body: message
630
630
  }
631
- send_message_event(room_id, 'm.room.message', content, params)
631
+ send_message_event(room_id, 'm.room.message', content, **params)
632
632
  end
633
633
 
634
634
  # Send a plaintext emote to a room
@@ -646,7 +646,7 @@ module MatrixSdk::Protocols::CS
646
646
  msgtype: params.delete(:msg_type) { 'm.emote' },
647
647
  body: emote
648
648
  }
649
- send_message_event(room_id, 'm.room.message', content, params)
649
+ send_message_event(room_id, 'm.room.message', content, **params)
650
650
  end
651
651
 
652
652
  # Send a plaintext notice to a room
@@ -664,7 +664,7 @@ module MatrixSdk::Protocols::CS
664
664
  msgtype: params.delete(:msg_type) { 'm.notice' },
665
665
  body: notice
666
666
  }
667
- send_message_event(room_id, 'm.room.message', content, params)
667
+ send_message_event(room_id, 'm.room.message', content, **params)
668
668
  end
669
669
 
670
670
  # Report an event in a room
@@ -702,7 +702,7 @@ module MatrixSdk::Protocols::CS
702
702
  # @return [Response] A response hash with the message information containing :start, :end, and :chunk fields
703
703
  # @see https://matrix.org/docs/spec/client_server/latest.html#get-matrix-client-r0-rooms-roomid-messages
704
704
  # The Matrix Spec, for more information about the call and response
705
- def get_room_messages(room_id, token, direction, limit: 10, **params)
705
+ def get_room_messages(room_id, token, direction:, limit: 10, **params)
706
706
  query = {
707
707
  from: token,
708
708
  dir: direction,
@@ -767,6 +767,30 @@ module MatrixSdk::Protocols::CS
767
767
  request(:get, :client_r0, "/rooms/#{room_id}/state", query: query)
768
768
  end
769
769
 
770
+ # Retrieves number of events that happened just before and after the specified event
771
+ #
772
+ # @param room_id [MXID,String] The room to get events from.
773
+ # @param event_id [MXID,String] The event to get context around.
774
+ # @option params [Integer] :limit (10) The limit of messages to retrieve
775
+ # @option params [String] :filter A filter to limit the retrieval to
776
+ # @return [Response] A response hash with contextual event information
777
+ # @see https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-context-eventid
778
+ # The Matrix Spec, for more information about the call and response
779
+ # @example Find event context with filter and limit specified
780
+ # api.get_room_event_context('#room:example.com', '$event_id:example.com', filter: { types: ['m.room.message'] }.to_json, limit: 20)
781
+ def get_room_event_context(room_id, event_id, **params)
782
+ query = {}
783
+ query[:user_id] = params.delete(:user_id) if protocol?(:AS) && params.key?(:user_id)
784
+
785
+ query[:limit] = params.fetch(:limit) if params.key? :limit
786
+ query[:filter] = params.fetch(:filter) if params.key? :filter
787
+
788
+ room_id = ERB::Util.url_encode room_id.to_s
789
+ event_id = ERB::Util.url_encode event_id.to_s
790
+
791
+ request(:get, :client_r0, "/rooms/#{room_id}/context/#{event_id}", query: query)
792
+ end
793
+
770
794
  ## Specialized getters for specced state
771
795
  #
772
796
 
@@ -780,7 +804,7 @@ module MatrixSdk::Protocols::CS
780
804
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-name
781
805
  # The Matrix Spec, for more information about the event and data
782
806
  def get_room_name(room_id, **params)
783
- get_room_state(room_id, 'm.room.name', params)
807
+ get_room_state(room_id, 'm.room.name', **params)
784
808
  end
785
809
 
786
810
  # Sets the display name of a room
@@ -795,7 +819,7 @@ module MatrixSdk::Protocols::CS
795
819
  content = {
796
820
  name: name
797
821
  }
798
- send_state_event(room_id, 'm.room.name', content, params)
822
+ send_state_event(room_id, 'm.room.name', content, **params)
799
823
  end
800
824
 
801
825
  # Gets the current topic of a room
@@ -808,7 +832,7 @@ module MatrixSdk::Protocols::CS
808
832
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-topic
809
833
  # The Matrix Spec, for more information about the event and data
810
834
  def get_room_topic(room_id, **params)
811
- get_room_state(room_id, 'm.room.topic', params)
835
+ get_room_state(room_id, 'm.room.topic', **params)
812
836
  end
813
837
 
814
838
  # Sets the topic of a room
@@ -823,7 +847,7 @@ module MatrixSdk::Protocols::CS
823
847
  content = {
824
848
  topic: topic
825
849
  }
826
- send_state_event(room_id, 'm.room.topic', content, params)
850
+ send_state_event(room_id, 'm.room.topic', content, **params)
827
851
  end
828
852
 
829
853
  # Gets the current avatar URL of a room
@@ -836,7 +860,7 @@ module MatrixSdk::Protocols::CS
836
860
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-avatar
837
861
  # The Matrix Spec, for more information about the event and data
838
862
  def get_room_avatar(room_id, **params)
839
- get_room_state(room_id, 'm.room.avatar', params)
863
+ get_room_state(room_id, 'm.room.avatar', **params)
840
864
  end
841
865
 
842
866
  # Sets the avatar URL for a room
@@ -851,7 +875,7 @@ module MatrixSdk::Protocols::CS
851
875
  content = {
852
876
  url: url
853
877
  }
854
- send_state_event(room_id, 'm.room.avatar', content, params)
878
+ send_state_event(room_id, 'm.room.avatar', content, **params)
855
879
  end
856
880
 
857
881
  # Gets a list of current aliases of a room
@@ -878,7 +902,7 @@ module MatrixSdk::Protocols::CS
878
902
  # .compact
879
903
  # # => ["#synapse:im.kabi.tk", "#synapse:matrix.org", "#synapse-community:matrix.org", "#synapse-ops:matrix.org", "#synops:matrix.org", ...
880
904
  def get_room_aliases(room_id, **params)
881
- get_room_state(room_id, 'm.room.aliases', params)
905
+ get_room_state(room_id, 'm.room.aliases', **params)
882
906
  end
883
907
 
884
908
  # Gets a list of pinned events in a room
@@ -891,7 +915,7 @@ module MatrixSdk::Protocols::CS
891
915
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-pinned-events
892
916
  # The Matrix Spec, for more information about the event and data
893
917
  def get_room_pinned_events(room_id, **params)
894
- get_room_state(room_id, 'm.room.pinned_events', params)
918
+ get_room_state(room_id, 'm.room.pinned_events', **params)
895
919
  end
896
920
 
897
921
  # Sets the list of pinned events in a room
@@ -906,7 +930,7 @@ module MatrixSdk::Protocols::CS
906
930
  content = {
907
931
  pinned: events
908
932
  }
909
- send_state_event(room_id, 'm.room.pinned_events', content, params)
933
+ send_state_event(room_id, 'm.room.pinned_events', content, **params)
910
934
  end
911
935
 
912
936
  # Gets the configured power levels for a room
@@ -918,7 +942,7 @@ module MatrixSdk::Protocols::CS
918
942
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-power-levels
919
943
  # The Matrix Spec, for more information about the event and data
920
944
  def get_room_power_levels(room_id, **params)
921
- get_room_state(room_id, 'm.room.power_levels', params)
945
+ get_room_state(room_id, 'm.room.power_levels', **params)
922
946
  end
923
947
  alias get_power_levels get_room_power_levels
924
948
 
@@ -932,7 +956,7 @@ module MatrixSdk::Protocols::CS
932
956
  # The Matrix Spec, for more information about the event and data
933
957
  def set_room_power_levels(room_id, content, **params)
934
958
  content[:events] = {} unless content.key? :events
935
- send_state_event(room_id, 'm.room.power_levels', content, params)
959
+ send_state_event(room_id, 'm.room.power_levels', content, **params)
936
960
  end
937
961
  alias set_power_levels set_room_power_levels
938
962
 
@@ -945,7 +969,7 @@ module MatrixSdk::Protocols::CS
945
969
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-join-rules
946
970
  # The Matrix Spec, for more information about the event and data
947
971
  def get_room_join_rules(room_id, **params)
948
- get_room_state(room_id, 'm.room.join_rules', params)
972
+ get_room_state(room_id, 'm.room.join_rules', **params)
949
973
  end
950
974
 
951
975
  # Sets the join rules for a room
@@ -961,7 +985,7 @@ module MatrixSdk::Protocols::CS
961
985
  join_rule: join_rule
962
986
  }
963
987
 
964
- send_state_event(room_id, 'm.room.join_rules', content, params)
988
+ send_state_event(room_id, 'm.room.join_rules', content, **params)
965
989
  end
966
990
 
967
991
  # Gets the guest access settings for a room
@@ -973,7 +997,7 @@ module MatrixSdk::Protocols::CS
973
997
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-guest-access
974
998
  # The Matrix Spec, for more information about the event and data
975
999
  def get_room_guest_access(room_id, **params)
976
- get_room_state(room_id, 'm.room.guest_access', params)
1000
+ get_room_state(room_id, 'm.room.guest_access', **params)
977
1001
  end
978
1002
 
979
1003
  # Sets the guest access settings for a room
@@ -989,7 +1013,7 @@ module MatrixSdk::Protocols::CS
989
1013
  guest_access: guest_access
990
1014
  }
991
1015
 
992
- send_state_event(room_id, 'm.room.guest_access', content, params)
1016
+ send_state_event(room_id, 'm.room.guest_access', content, **params)
993
1017
  end
994
1018
 
995
1019
  # Gets the creation configuration object for a room
@@ -1001,7 +1025,7 @@ module MatrixSdk::Protocols::CS
1001
1025
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-create
1002
1026
  # The Matrix Spec, for more information about the event and data
1003
1027
  def get_room_creation_info(room_id, **params)
1004
- get_room_state(room_id, 'm.room.create', params)
1028
+ get_room_state(room_id, 'm.room.create', **params)
1005
1029
  end
1006
1030
 
1007
1031
  # Gets the encryption configuration for a room
@@ -1013,7 +1037,7 @@ module MatrixSdk::Protocols::CS
1013
1037
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-encryption
1014
1038
  # The Matrix Spec, for more information about the event and data
1015
1039
  def get_room_encryption_settings(room_id, **params)
1016
- get_room_state(room_id, 'm.room.encryption', params)
1040
+ get_room_state(room_id, 'm.room.encryption', **params)
1017
1041
  end
1018
1042
 
1019
1043
  # Sets the encryption configuration for a room
@@ -1032,7 +1056,7 @@ module MatrixSdk::Protocols::CS
1032
1056
  rotation_period_ms: rotation_period_ms,
1033
1057
  rotation_period_msgs: rotation_period_msgs
1034
1058
  }
1035
- send_state_event(room_id, 'm.room.encryption', content, params)
1059
+ send_state_event(room_id, 'm.room.encryption', content, **params)
1036
1060
  end
1037
1061
 
1038
1062
  # Gets the history availabiilty for a room
@@ -1044,7 +1068,7 @@ module MatrixSdk::Protocols::CS
1044
1068
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-history-visibility
1045
1069
  # The Matrix Spec, for more information about the event and data
1046
1070
  def get_room_history_visibility(room_id, **params)
1047
- get_room_state(room_id, 'm.room.history_visibility', params)
1071
+ get_room_state(room_id, 'm.room.history_visibility', **params)
1048
1072
  end
1049
1073
 
1050
1074
  # Sets the history availability for a room
@@ -1060,7 +1084,7 @@ module MatrixSdk::Protocols::CS
1060
1084
  history_visibility: visibility
1061
1085
  }
1062
1086
 
1063
- send_state_event(room_id, 'm.room.history_visibility', content, params)
1087
+ send_state_event(room_id, 'm.room.history_visibility', content, **params)
1064
1088
  end
1065
1089
 
1066
1090
  # Gets the server ACLs for a room
@@ -1072,7 +1096,7 @@ module MatrixSdk::Protocols::CS
1072
1096
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-server-acl
1073
1097
  # The Matrix Spec, for more information about the event and data
1074
1098
  def get_room_server_acl(room_id, **params)
1075
- get_room_state(room_id, 'm.room.server_acl', params)
1099
+ get_room_state(room_id, 'm.room.server_acl', **params)
1076
1100
  end
1077
1101
 
1078
1102
  # Sets the server ACL configuration for a room
@@ -1085,14 +1109,14 @@ module MatrixSdk::Protocols::CS
1085
1109
  # @return [Response] The resulting state event
1086
1110
  # @see https://matrix.org/docs/spec/client_server/latest.html#m-room-guest-server-acl
1087
1111
  # The Matrix Spec, for more information about the event and data
1088
- def set_room_server_acl(room_id, allow_ip_literals: false, allow:, deny:, **params)
1112
+ def set_room_server_acl(room_id, allow:, deny:, allow_ip_literals: false, **params)
1089
1113
  content = {
1090
1114
  allow_ip_literals: allow_ip_literals,
1091
1115
  allow: allow,
1092
1116
  deny: deny
1093
1117
  }
1094
1118
 
1095
- send_state_event(room_id, 'm.room.server_acl', content, params)
1119
+ send_state_event(room_id, 'm.room.server_acl', content, **params)
1096
1120
  end
1097
1121
 
1098
1122
  def leave_room(room_id, **params)
@@ -1637,7 +1661,7 @@ module MatrixSdk::Protocols::CS
1637
1661
 
1638
1662
  room_id = ERB::Util.url_encode room_id.to_s
1639
1663
 
1640
- request(:get, :client_r0, "/rooms/#{room_id}/members", query: query)
1664
+ request(:get, :client_r0, "/rooms/#{room_id}/members", query: query.merge(params))
1641
1665
  end
1642
1666
 
1643
1667
  # Gets a list of the joined members in a room
@@ -1712,7 +1736,7 @@ module MatrixSdk::Protocols::CS
1712
1736
  # # => { :device_keys => { :'@alice:example.com' => { :ABCDEFGHIJ => { ...
1713
1737
  # @see https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-keys-query
1714
1738
  # The Matrix Spec, for more information about the parameters and data
1715
- def keys_query(timeout: nil, device_keys:, token: nil, **params)
1739
+ def keys_query(device_keys:, timeout: nil, token: nil, **params)
1716
1740
  body = {
1717
1741
  timeout: (timeout || 10) * 1000,
1718
1742
  device_keys: device_keys
@@ -1823,7 +1847,7 @@ module MatrixSdk::Protocols::CS
1823
1847
  # @see https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules
1824
1848
  # The Matrix Spec, for more information about the parameters and data
1825
1849
  def get_pushrules
1826
- request(:get, :client_r0, '/pushrules')
1850
+ request(:get, :client_r0, '/pushrules/')
1827
1851
  end
1828
1852
 
1829
1853
  # Retrieves a single registered push rule for the current user
@@ -1834,7 +1858,7 @@ module MatrixSdk::Protocols::CS
1834
1858
  # @return [Response] A response hash containing the full data of the requested push rule
1835
1859
  # @see https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules-scope-kind-ruleid
1836
1860
  # The Matrix Spec, for more information about the parameters and data
1837
- def get_pushrule(scope: 'global', kind:, id:)
1861
+ def get_pushrule(kind:, id:, scope: 'global')
1838
1862
  scope = ERB::Util.url_encode scope.to_s
1839
1863
  kind = ERB::Util.url_encode kind.to_s
1840
1864
  id = ERB::Util.url_encode id.to_s
@@ -1850,7 +1874,7 @@ module MatrixSdk::Protocols::CS
1850
1874
  # @return [Response] A response hash containing an :enabled key for if the rule is enabled or not
1851
1875
  # @see https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules-scope-kind-ruleid-enabled
1852
1876
  # The Matrix Spec, for more information about the parameters and data
1853
- def get_pushrule_enabled(scope: 'global', kind:, id:)
1877
+ def get_pushrule_enabled(kind:, id:, scope: 'global')
1854
1878
  scope = ERB::Util.url_encode scope.to_s
1855
1879
  kind = ERB::Util.url_encode kind.to_s
1856
1880
  id = ERB::Util.url_encode id.to_s
@@ -1867,7 +1891,7 @@ module MatrixSdk::Protocols::CS
1867
1891
  # @return [Response] An empty response hash if the push rule was enabled/disabled successfully
1868
1892
  # @see https://matrix.org/docs/spec/client_server/latest#put-matrix-client-r0-pushrules-scope-kind-ruleid-enabled
1869
1893
  # The Matrix Spec, for more information about the parameters and data
1870
- def set_pushrule_enabled(enabled, scope: 'global', kind:, id:)
1894
+ def set_pushrule_enabled(enabled, kind:, id:, scope: 'global')
1871
1895
  scope = ERB::Util.url_encode scope.to_s
1872
1896
  kind = ERB::Util.url_encode kind.to_s
1873
1897
  id = ERB::Util.url_encode id.to_s
@@ -1887,7 +1911,7 @@ module MatrixSdk::Protocols::CS
1887
1911
  # @return [Response] A response hash containing an :enabled key for if the rule is enabled or not
1888
1912
  # @see https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules-scope-kind-ruleid-actions
1889
1913
  # The Matrix Spec, for more information about the parameters and data
1890
- def get_pushrule_actions(scope: 'global', kind:, id:)
1914
+ def get_pushrule_actions(kind:, id:, scope: 'global')
1891
1915
  scope = ERB::Util.url_encode scope.to_s
1892
1916
  kind = ERB::Util.url_encode kind.to_s
1893
1917
  id = ERB::Util.url_encode id.to_s
@@ -1904,7 +1928,7 @@ module MatrixSdk::Protocols::CS
1904
1928
  # @return [Response] An empty response hash if the push rule actions were modified successfully
1905
1929
  # @see https://matrix.org/docs/spec/client_server/latest#put-matrix-client-r0-pushrules-scope-kind-ruleid-actions
1906
1930
  # The Matrix Spec, for more information about the parameters and data
1907
- def set_pushrule_actions(actions, scope: 'global', kind:, id:)
1931
+ def set_pushrule_actions(actions, kind:, id:, scope: 'global')
1908
1932
  scope = ERB::Util.url_encode scope.to_s
1909
1933
  kind = ERB::Util.url_encode kind.to_s
1910
1934
  id = ERB::Util.url_encode id.to_s
@@ -47,7 +47,7 @@ module MatrixSdk::Protocols::MSC
47
47
  query[:user_id] = params.delete(:user_id) if protocol?(:AS) && params.key?(:user_id)
48
48
 
49
49
  req = Net::HTTP::Get.new(homeserver.dup.tap do |u|
50
- u.path = api_to_path(:client_r0) + '/sync/sse'
50
+ u.path = "#{api_to_path :client_r0}/sync/sse"
51
51
  u.query = URI.encode_www_form(query)
52
52
  end)
53
53
  req['accept'] = 'text/event-stream'
@@ -46,7 +46,9 @@ module MatrixSdk
46
46
  attr_reader :api
47
47
 
48
48
  def respond_to_missing?(name, *_args)
49
- key? name
49
+ return true if key? name
50
+
51
+ super
50
52
  end
51
53
 
52
54
  def method_missing(name, *args)
@@ -1,62 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'matrix_sdk'
4
+ require 'matrix_sdk/util/events'
5
+ require 'matrix_sdk/util/tinycache'
4
6
 
5
7
  module MatrixSdk
6
8
  # A class for tracking the information about a room on Matrix
7
9
  class Room
8
10
  extend MatrixSdk::Extensions
11
+ extend MatrixSdk::Util::Tinycache
9
12
  include MatrixSdk::Logging
10
13
 
11
- # @!attribute [rw] canonical_alias
12
- # @return [String, nil] the canonical alias of the room
13
14
  # @!attribute [rw] event_history_limit
14
15
  # @return [Fixnum] the limit of events to keep in the event log
15
- attr_accessor :canonical_alias, :event_history_limit
16
+ attr_accessor :event_history_limit
16
17
  # @!attribute [r] id
17
18
  # @return [String] the internal ID of the room
18
19
  # @!attribute [r] client
19
20
  # @return [Client] the client for the room
20
- # @!attribute [rw] name
21
- # @return [String, nil] the user-provided name of the room
22
- # @see reload_name!
23
- # @!attribute [rw] topic
24
- # @return [String, nil] the user-provided topic of the room
25
- # @see reload_topic!
26
- # @!attribute [r] aliases
27
- # @return [Array(String)] a list of user-set aliases for the room
28
- # @see add_alias
29
- # @see reload_alias!
30
- # @!attribute [rw] join_rule
31
- # @return [:invite, :public] the join rule for the room -
32
- # either +:invite+ or +:public+
33
- # @!attribute [rw] guest_access
34
- # @return [:can_join, :forbidden] the guest access for the room -
35
- # either +:can_join+ or +:forbidden+
36
- # @!attribute [r] members
37
- # @return [Array(User)] the members of the room
38
- # @see reload_members!
39
21
  # @!attribute [r] events
40
22
  # @return [Array(Object)] the last +event_history_limit+ events to arrive in the room
41
23
  # @see https://matrix.org/docs/spec/client_server/r0.3.0.html#get-matrix-client-r0-sync
42
24
  # The timeline events are what will end up in here
43
- attr_reader :id, :client, :topic, :aliases, :members, :events
25
+ attr_reader :id, :client, :events
44
26
 
45
- # @!attribute [r] on_event
46
- # @return [EventHandlerArray] The list of event handlers for all events
47
- # @!attribute [r] on_state_event
48
- # @return [EventHandlerArray] The list of event handlers for only state events
49
- # @!attribute [r] on_ephemeral_event
50
- # @return [EventHandlerArray] The list of event handlers for only ephemeral events
51
- events :event, :state_event, :ephemeral_event
52
27
  # @!method inspect
53
28
  # An inspect method that skips a handful of instance variables to avoid
54
29
  # flooding the terminal with debug data.
55
30
  # @return [String] a regular inspect string without the data for some variables
56
- ignore_inspect :client, :members, :events, :prev_batch, :logger,
57
- :on_event, :on_state_event, :on_ephemeral_event
31
+ ignore_inspect :client, :events, :prev_batch, :logger
32
+
33
+ # Requires heavy lookups, so they're cached for an hour
34
+ cached :joined_members, :aliases, cache_level: :all, expires_in: 60 * 60
35
+ # Only cache unfiltered requests for all members
36
+ cached :all_members, unless: proc { |args| args.any? }, cache_level: :all, expires_in: 3600
37
+
38
+ # Much simpler to look up, and lighter data-wise, so the cache is wider
39
+ cached :canonical_alias, :name, :avatar_url, :topic, :guest_access, :join_rule, :power_levels, cache_level: :some, expires_in: 15 * 60
58
40
 
59
41
  alias room_id id
42
+ alias members joined_members
60
43
 
61
44
  # Create a new room instance
62
45
  #
@@ -82,38 +65,52 @@ module MatrixSdk
82
65
  def initialize(client, room_id, data = {})
83
66
  raise ArgumentError, 'Must be given a Client instance' unless client.is_a? Client
84
67
 
68
+ @client = client
85
69
  room_id = MXID.new room_id unless room_id.is_a?(MXID)
86
70
  raise ArgumentError, 'room_id must be a valid Room ID' unless room_id.room_id?
87
71
 
88
- event_initialize
89
-
90
- @name = nil
91
- @topic = nil
92
- @canonical_alias = nil
93
- @aliases = []
94
- @join_rule = nil
95
- @guest_access = nil
96
- @world_readable = nil
97
- @members = []
98
72
  @events = []
99
- @members_loaded = false
100
73
  @event_history_limit = 10
101
- @avatar_url = nil
102
74
 
103
75
  @prev_batch = nil
104
76
 
105
77
  data.each do |k, v|
106
- instance_variable_set("@#{k}", v) if instance_variable_defined? "@#{k}"
78
+ next if %i[client].include? k
79
+
80
+ if respond_to?("#{k}_cached?".to_sym) && send("#{k}_cached?".to_sym)
81
+ tinycache_adapter.write(k, v)
82
+ elsif instance_variable_defined? "@#{k}"
83
+ instance_variable_set("@#{k}", v)
84
+ end
107
85
  end
108
86
 
109
- @client = client
110
87
  @id = room_id.to_s
111
88
 
112
- @name_checked = Time.new(0)
113
-
114
89
  logger.debug "Created room #{room_id}"
115
90
  end
116
91
 
92
+ #
93
+ # Event handlers
94
+ #
95
+
96
+ # @!attribute [r] on_event
97
+ # @return [EventHandlerArray] The list of event handlers for all events
98
+ def on_event
99
+ ensure_room_handlers[:event]
100
+ end
101
+
102
+ # @!attribute [r] on_state_event
103
+ # @return [EventHandlerArray] The list of event handlers for only state events
104
+ def on_state_event
105
+ ensure_room_handlers[:state_event]
106
+ end
107
+
108
+ # @!attribute [r] on_ephemeral_event
109
+ # @return [EventHandlerArray] The list of event handlers for only ephemeral events
110
+ def on_ephemeral_event
111
+ ensure_room_handlers[:ephemeral_event]
112
+ end
113
+
117
114
  #
118
115
  # State readers
119
116
  #
@@ -142,19 +139,33 @@ module MatrixSdk
142
139
  'Empty Room'
143
140
  end
144
141
 
142
+ # @return [String, nil] the canonical alias of the room
143
+ def canonical_alias
144
+ client.api.get_room_state(id, 'm.room.canonical_alias')[:alias]
145
+ rescue MatrixSdk::MatrixNotFoundError
146
+ nil
147
+ end
148
+
145
149
  # Populates and returns the #members array
146
150
  #
147
151
  # @return [Array(User)] The list of members in the room
148
152
  def joined_members
149
- return members if @members_loaded && !members.empty?
150
-
151
- client.api.get_room_joined_members(id)[:joined].each do |mxid, data|
152
- ensure_member(User.new(client, mxid.to_s,
153
- display_name: data.fetch(:display_name, nil),
154
- avatar_url: data.fetch(:avatar_url, nil)))
153
+ client.api.get_room_joined_members(id)[:joined].map do |mxid, data|
154
+ User.new(client, mxid.to_s,
155
+ display_name: data.fetch(:display_name, nil),
156
+ avatar_url: data.fetch(:avatar_url, nil))
155
157
  end
156
- @members_loaded = true
157
- members
158
+ end
159
+
160
+ # Get all members (member events) in the room
161
+ #
162
+ # @note This will also count members who've knocked, been invited, have left, or have been banned.
163
+ #
164
+ # @param params [Hash] Additional query parameters to pass to the room member listing - e.g. for filtering purposes.
165
+ #
166
+ # @return [Array(User)] The complete list of members in the room, regardless of membership state
167
+ def all_members(**params)
168
+ client.api.get_room_members(id, **params)[:chunk].map { |ch| client.get_user(ch[:state_key]) }
158
169
  end
159
170
 
160
171
  # Gets the current name of the room, querying the API if necessary
@@ -163,10 +174,7 @@ module MatrixSdk
163
174
  #
164
175
  # @return [String,nil] The room name - if any
165
176
  def name
166
- return @name if Time.now - @name_checked < 900
167
-
168
- @name_checked = Time.now
169
- @name ||= client.api.get_room_name(id)
177
+ client.api.get_room_name(id)[:name]
170
178
  rescue MatrixNotFoundError
171
179
  # No room name has been specified
172
180
  nil
@@ -176,18 +184,34 @@ module MatrixSdk
176
184
  #
177
185
  # @return [String,nil] The avatar URL - if any
178
186
  def avatar_url
179
- @avatar_url ||= client.api.get_room_avatar(id).url
187
+ client.api.get_room_avatar(id)[:url]
180
188
  rescue MatrixNotFoundError
181
189
  # No avatar has been set
182
190
  nil
183
191
  end
184
192
 
193
+ # Gets the room topic - if any
194
+ #
195
+ # @return [String,nil] The topic of the room
196
+ def topic
197
+ client.api.get_room_topic(id)[:topic]
198
+ rescue MatrixNotFoundError
199
+ # No room name has been specified
200
+ nil
201
+ end
202
+
203
+ # Gets the guest access rights for the room
204
+ #
205
+ # @return [:can_join,:forbidden] The current guest access right
185
206
  def guest_access
186
- @guest_access ||= client.api.get_room_guest_access(id).guest_access.to_sym
207
+ client.api.get_room_guest_access(id)[:guest_access].to_sym
187
208
  end
188
209
 
210
+ # Gets the join rule for the room
211
+ #
212
+ # @return [:public,:knock,:invite,:private] The current join rule
189
213
  def join_rule
190
- @join_rule ||= client.api.get_room_join_rules(id).join_rule.to_sym
214
+ client.api.get_room_join_rules(id)[:join_rule].to_sym
191
215
  end
192
216
 
193
217
  # Checks if +guest_access+ is set to +:can_join+
@@ -200,6 +224,34 @@ module MatrixSdk
200
224
  join_rule == :invite
201
225
  end
202
226
 
227
+ # Gets the history visibility of the room
228
+ #
229
+ # @return [:invited,:joined,:shared,:world_readable] The current history visibility for the room
230
+ def history_visibility
231
+ client.api.get_room_state(id, 'm.room.history_visibility')[:history_visibility].to_sym
232
+ end
233
+
234
+ # Checks if the room history is world readable
235
+ #
236
+ # @return [Boolean] If the history is world readable
237
+ def world_readable?
238
+ history_visibility == :world_readable
239
+ end
240
+ alias world_readable world_readable?
241
+
242
+ # Gets the room aliases
243
+ #
244
+ # @return [Array[String]] The assigned room aliases
245
+ def aliases
246
+ client.api.get_room_aliases(id).aliases
247
+ rescue MatrixNotFoundError
248
+ data = client.api.get_room_state_all(id)
249
+ data.select { |chunk| chunk[:type] == 'm.room.aliases' && chunk.key?(:content) && chunk[:content].key?(:aliases) }
250
+ .map { |chunk| chunk[:content][:aliases] }
251
+ .flatten
252
+ .compact
253
+ end
254
+
203
255
  #
204
256
  # Message handling
205
257
  #
@@ -340,7 +392,13 @@ module MatrixSdk
340
392
  # @param reverse [Boolean] whether to fill messages in reverse or not
341
393
  # @param limit [Integer] the maximum number of messages to backfill
342
394
  # @note This will trigger the `on_event` events as messages are added
343
- def backfill_messages(reverse = false, limit = 10)
395
+ def backfill_messages(*args, reverse: false, limit: 10)
396
+ # To be backwards-compatible
397
+ if args.length == 2
398
+ reverse = args.first
399
+ limit = args.last
400
+ end
401
+
344
402
  data = client.api.get_room_messages(id, @prev_batch, direction: :b, limit: limit)
345
403
 
346
404
  events = data[:chunk]
@@ -458,7 +516,7 @@ module MatrixSdk
458
516
  @room
459
517
  end
460
518
  tag_obj.define_singleton_method(:add) do |tag, **data|
461
- @room.add_tag(tag.to_s.to_sym, data)
519
+ @room.add_tag(tag.to_s.to_sym, **data)
462
520
  self[tag.to_s.to_sym] = data
463
521
  self
464
522
  end
@@ -503,22 +561,16 @@ module MatrixSdk
503
561
  #
504
562
  # @param name [String] The new name to set
505
563
  def name=(name)
564
+ tinycache_adapter.write(:name, name)
506
565
  client.api.set_room_name(id, name)
507
- @name = name
566
+ name
508
567
  end
509
568
 
510
569
  # Reloads the name of the room
511
570
  #
512
571
  # @return [Boolean] if the name was changed or not
513
572
  def reload_name!
514
- data = begin
515
- client.api.get_room_name(id)
516
- rescue MatrixNotFoundError
517
- nil
518
- end
519
- changed = data[:name] != @name
520
- @name = data[:name] if changed
521
- changed
573
+ clear_name_cache
522
574
  end
523
575
  alias refresh_name! reload_name!
524
576
 
@@ -526,22 +578,16 @@ module MatrixSdk
526
578
  #
527
579
  # @param topic [String] The new topic to set
528
580
  def topic=(topic)
581
+ tinycache_adapter.write(:topic, topic)
529
582
  client.api.set_room_topic(id, topic)
530
- @topic = topic
583
+ topic
531
584
  end
532
585
 
533
586
  # Reloads the topic of the room
534
587
  #
535
588
  # @return [Boolean] if the topic was changed or not
536
589
  def reload_topic!
537
- data = begin
538
- client.api.get_room_topic(id)
539
- rescue MatrixNotFoundError
540
- nil
541
- end
542
- changed = data[:topic] != @topic
543
- @topic = data[:topic] if changed
544
- changed
590
+ clear_topic_cache
545
591
  end
546
592
  alias refresh_topic! reload_topic!
547
593
 
@@ -550,7 +596,7 @@ module MatrixSdk
550
596
  # @return [Boolean] if the addition was successful or not
551
597
  def add_alias(room_alias)
552
598
  client.api.set_room_alias(id, room_alias)
553
- @aliases << room_alias
599
+ tinycache_adapter.read(:aliases) << room_alias if tinycache_adapter.exist?(:aliases)
554
600
  true
555
601
  end
556
602
 
@@ -560,21 +606,7 @@ module MatrixSdk
560
606
  # @note The list of aliases is not sorted, ordering changes will result in
561
607
  # alias list updates.
562
608
  def reload_aliases!
563
- begin
564
- new_aliases = client.api.get_room_aliases(id).aliases
565
- rescue MatrixNotFoundError
566
- data = client.api.get_room_state_all(id)
567
- new_aliases = data.select { |chunk| chunk[:type] == 'm.room.aliases' && chunk.key?(:content) && chunk[:content].key?(:aliases) }
568
- .map { |chunk| chunk[:content][:aliases] }
569
- .flatten
570
- .compact
571
- end
572
-
573
- return false if new_aliases.nil?
574
-
575
- changed = new_aliases != aliases
576
- @aliases = new_aliases if changed
577
- changed
609
+ clear_aliases_cache
578
610
  end
579
611
  alias refresh_aliases! reload_aliases!
580
612
 
@@ -583,7 +615,7 @@ module MatrixSdk
583
615
  # @param invite_only [Boolean] If it should be invite only or not
584
616
  def invite_only=(invite_only)
585
617
  self.join_rule = invite_only ? :invite : :public
586
- @join_rule == :invite
618
+ invite_only
587
619
  end
588
620
 
589
621
  # Sets the join rule of the room
@@ -591,7 +623,8 @@ module MatrixSdk
591
623
  # @param join_rule [:invite,:public] The join rule of the room
592
624
  def join_rule=(join_rule)
593
625
  client.api.set_room_join_rules(id, join_rule)
594
- @join_rule = join_rule
626
+ tinycache_adapter.write(:join_rule, join_rule)
627
+ join_rule
595
628
  end
596
629
 
597
630
  # Sets if guests are allowed in the room
@@ -599,7 +632,7 @@ module MatrixSdk
599
632
  # @param allow_guests [Boolean] If guests are allowed to join or not
600
633
  def allow_guests=(allow_guests)
601
634
  self.guest_access = (allow_guests ? :can_join : :forbidden)
602
- @guest_access == :can_join
635
+ allow_guests
603
636
  end
604
637
 
605
638
  # Sets the guest access status for the room
@@ -607,7 +640,8 @@ module MatrixSdk
607
640
  # @param guest_access [:can_join,:forbidden] The new guest access status of the room
608
641
  def guest_access=(guest_access)
609
642
  client.api.set_room_guest_access(id, guest_access)
610
- @guest_access = guest_access
643
+ tinycache_adapter.write(:guest_access, guest_access)
644
+ guest_access
611
645
  end
612
646
 
613
647
  # Sets a new avatar URL for the room
@@ -618,7 +652,89 @@ module MatrixSdk
618
652
  raise ArgumentError, 'Must be a valid MXC URL' unless avatar_url.is_a? URI::MATRIX
619
653
 
620
654
  client.api.set_room_avatar(id, avatar_url)
621
- @avatar_url = avatar_url
655
+ tinycache_adapter.write(:avatar_url, avatar_url)
656
+ avatar_url
657
+ end
658
+
659
+ # Get the power levels of the room
660
+ #
661
+ # @note The returned power levels are cached for a minute
662
+ # @return [Hash] The current power levels as set for the room
663
+ # @see Protocols::CS#get_power_levels
664
+ def power_levels
665
+ client.api.get_power_levels(id)
666
+ end
667
+
668
+ # Gets the power level of a user in the room
669
+ #
670
+ # @param user [User,MXID,String] The user to check the power level for
671
+ # @param use_default [Boolean] Should the user default level be checked if no user-specific one exists
672
+ # @return [Integer,nil] The current power level for the requested user, nil if there's no user specific level
673
+ # and use_default is false
674
+ def user_powerlevel(user, use_default: true)
675
+ user = user.id if user.is_a? User
676
+ user = MXID.new(user.to_s) unless user.is_a? MXID
677
+ raise ArgumentError, 'Must provide a valid user or MXID' unless user.user?
678
+
679
+ level = power_levels[:users][user.to_s.to_sym]
680
+ level = power_levels[:users_default] || 0 if level.nil? && use_default
681
+ level
682
+ end
683
+
684
+ # Check if a user is an admin in the room
685
+ #
686
+ # @param user [User,MXID,String] The user to check for admin privileges
687
+ # @param target_level [Integer] The power level that's to be considered as admin privileges
688
+ # @return [Boolean] If the requested user has a power level highe enough to be an admin
689
+ # @see #user_powerlevel
690
+ def admin?(user, target_level: 100)
691
+ level = user_powerlevel(user, use_default: false)
692
+ return false unless level
693
+
694
+ level >= target_level
695
+ end
696
+
697
+ # Make a user an admin in the room
698
+ #
699
+ # @param user [User,MXID,String] The user to give admin privileges
700
+ # @param level [Integer] The power level to set the user to
701
+ # @see #modify_user_power_levels
702
+ def admin!(user, level: 100)
703
+ return true if admin?(user, target_level: level)
704
+
705
+ user = user.id if user.is_a? User
706
+ user = MXID.new(user.to_s) unless user.is_a? MXID
707
+ raise ArgumentError, 'Must provide a valid user or MXID' unless user.user?
708
+
709
+ modify_user_power_levels({ user.to_s.to_sym => level })
710
+ end
711
+
712
+ # Check if a user is a moderator in the room
713
+ #
714
+ # @param user [User,MXID,String] The user to check for admin privileges
715
+ # @param target_level [Integer] The power level that's to be considered as admin privileges
716
+ # @return [Boolean] If the requested user has a power level highe enough to be an admin
717
+ # @see #user_powerlevel
718
+ def moderator?(user, target_level: 50)
719
+ level = user_powerlevel(user, use_default: false)
720
+ return false unless level
721
+
722
+ level >= target_level
723
+ end
724
+
725
+ # Make a user a moderator in the room
726
+ #
727
+ # @param user [User,MXID,String] The user to give moderator privileges
728
+ # @param level [Integer] The power level to set the user to
729
+ # @see #modify_user_power_levels
730
+ def moderator!(user, level: 50)
731
+ return true if moderator?(user, target_level: level)
732
+
733
+ user = user.id if user.is_a? User
734
+ user = MXID.new(user.to_s) unless user.is_a? MXID
735
+ raise ArgumentError, 'Must provide a valid user or MXID' unless user.user?
736
+
737
+ modify_user_power_levels({ user.to_s.to_sym => level })
622
738
  end
623
739
 
624
740
  # Modifies the power levels of the room
@@ -629,7 +745,8 @@ module MatrixSdk
629
745
  def modify_user_power_levels(users = nil, users_default = nil)
630
746
  return false if users.nil? && users_default.nil?
631
747
 
632
- data = client.api.get_power_levels(id)
748
+ data = power_levels_without_cache
749
+ tinycache_adapter.write(:power_levels, data)
633
750
  data[:users_default] = users_default unless users_default.nil?
634
751
 
635
752
  if users
@@ -650,7 +767,8 @@ module MatrixSdk
650
767
  def modify_required_power_levels(events = nil, params = {})
651
768
  return false if events.nil? && (params.nil? || params.empty?)
652
769
 
653
- data = client.api.get_power_levels(id)
770
+ data = power_levels_without_cache
771
+ tinycache_adapter.write(:power_levels, data)
654
772
  data.merge!(params)
655
773
  data.delete_if { |_k, v| v.nil? }
656
774
 
@@ -667,18 +785,103 @@ module MatrixSdk
667
785
  private
668
786
 
669
787
  def ensure_member(member)
788
+ tinycache_adapter.write(:joined_members, []) unless tinycache_adapter.exist? :joined_members
789
+
790
+ members = tinycache_adapter.read(:joined_members) || []
670
791
  members << member unless members.any? { |m| m.id == member.id }
671
792
  end
672
793
 
794
+ def handle_power_levels(event)
795
+ tinycache_adapter.write(:power_levels, event[:content])
796
+ end
797
+
798
+ def handle_room_name(event)
799
+ tinycache_adapter.write(:name, event[:content][:name])
800
+ end
801
+
802
+ def handle_room_topic(event)
803
+ tinycache_adapter.write(:topic, event[:content][:topic])
804
+ end
805
+
806
+ def handle_room_guest_access(event)
807
+ tinycache_adapter.write(:guest_access, event[:content][:guest_access].to_sym)
808
+ end
809
+
810
+ def handle_room_join_rules(event)
811
+ tinycache_adapter.write(:join_rule, event[:content][:join_rule].to_sym)
812
+ end
813
+
814
+ def handle_room_member(event)
815
+ return unless client.cache == :all
816
+
817
+ if event[:content][:membership] == 'join'
818
+ ensure_member(client.get_user(event[:state_key]).dup.tap do |u|
819
+ u.instance_variable_set :@display_name, event[:content][:displayname]
820
+ end)
821
+ elsif tinycache_adapter.exist? :joined_members
822
+ members = tinycache_adapter.read(:joined_members)
823
+ members.delete_if { |m| m.id == event[:state_key] }
824
+ end
825
+ end
826
+
827
+ def handle_room_canonical_alias(event)
828
+ canonical_alias = tinycache_adapter.write :canonical_alias, event[:content][:alias]
829
+
830
+ data = tinycache_adapter.read(:aliases) || []
831
+ data << canonical_alias
832
+ tinycache_adapter.write(:aliases, data)
833
+ end
834
+
835
+ def handle_room_aliases(event)
836
+ tinycache_adapter.write(:aliases, []) unless tinycache_adapter.exist? :aliases
837
+
838
+ aliases = tinycache_adapter.read(:aliases) || []
839
+ aliases.concat event[:content][:aliases]
840
+
841
+ tinycache_adapter.write(:aliases, aliases)
842
+ end
843
+
844
+ def room_handlers?
845
+ client.instance_variable_get(:@room_handlers).key? id
846
+ end
847
+
848
+ def ensure_room_handlers
849
+ client.instance_variable_get(:@room_handlers)[id] ||= {
850
+ event: MatrixSdk::EventHandlerArray.new,
851
+ state_event: MatrixSdk::EventHandlerArray.new,
852
+ ephemeral_event: MatrixSdk::EventHandlerArray.new
853
+ }
854
+ end
855
+
856
+ INTERNAL_HANDLERS = {
857
+ 'm.room.aliases' => :handle_room_aliases,
858
+ 'm.room.canonical_alias' => :handle_room_canonical_alias,
859
+ 'm.room.guest_access' => :handle_room_guest_access,
860
+ 'm.room.join_rules' => :handle_room_join_rules,
861
+ 'm.room.member' => :handle_room_member,
862
+ 'm.room.name' => :handle_room_name,
863
+ 'm.room.power_levels' => :handle_power_levels,
864
+ 'm.room.topic' => :handle_room_topic
865
+ }.freeze
673
866
  def put_event(event)
867
+ ensure_room_handlers[:event].fire(MatrixEvent.new(self, event), event[:type]) if room_handlers?
868
+
674
869
  @events.push event
675
870
  @events.shift if @events.length > @event_history_limit
676
-
677
- fire_event MatrixEvent.new(self, event)
678
871
  end
679
872
 
680
873
  def put_ephemeral_event(event)
681
- fire_ephemeral_event MatrixEvent.new(self, event)
874
+ return unless room_handlers?
875
+
876
+ ensure_room_handlers[:ephemeral_event].fire(MatrixEvent.new(self, event), event[:type])
877
+ end
878
+
879
+ def put_state_event(event)
880
+ send(INTERNAL_HANDLERS[event[:type]], event) if INTERNAL_HANDLERS.key? event[:type]
881
+
882
+ return unless room_handlers?
883
+
884
+ ensure_room_handlers[:state_event].fire(MatrixEvent.new(self, event), event[:type])
682
885
  end
683
886
  end
684
887
  end