matrix_sdk 2.1.0 → 2.3.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.
@@ -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