async-matrix 2.1.0-aarch64-linux → 3.0.1-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -58
  3. data/lib/async/matrix/3.3/async_matrix_e2ee.so +0 -0
  4. data/lib/async/matrix/3.4/async_matrix_e2ee.so +0 -0
  5. data/lib/async/matrix/4.0/async_matrix_e2ee.so +0 -0
  6. data/lib/async/matrix/api/chain.rb +120 -94
  7. data/lib/async/matrix/api/concat.rb +28 -22
  8. data/lib/async/matrix/api/path_tree.rb +71 -44
  9. data/lib/async/matrix/api.rb +6 -1
  10. data/lib/async/matrix/app_service_client.rb +213 -0
  11. data/lib/async/matrix/auth_error.rb +20 -0
  12. data/lib/async/matrix/bad_json_error.rb +20 -0
  13. data/lib/async/matrix/client/encryption.rb +413 -0
  14. data/lib/async/matrix/client/rooms.rb +478 -0
  15. data/lib/async/matrix/client/sync.rb +508 -0
  16. data/lib/async/matrix/client.rb +217 -113
  17. data/lib/async/matrix/config/vivify.rb +107 -0
  18. data/lib/async/matrix/config.rb +184 -0
  19. data/lib/async/matrix/device_store.rb +1621 -0
  20. data/lib/async/matrix/double_puppet_client.rb +1 -1
  21. data/lib/async/matrix/e2ee/pickle_key.rb +182 -0
  22. data/lib/async/matrix/error.rb +9 -36
  23. data/lib/async/matrix/{application_service/error_response.rb → error_response.rb} +10 -10
  24. data/lib/async/matrix/homeserver_error.rb +20 -0
  25. data/lib/async/matrix/invalid_endpoint_error.rb +20 -0
  26. data/lib/async/matrix/media_client.rb +46 -35
  27. data/lib/async/matrix/not_found_error.rb +20 -0
  28. data/lib/async/matrix/notifier.rb +7 -3
  29. data/lib/async/matrix/response_too_large_error.rb +20 -0
  30. data/lib/async/matrix/version.rb +1 -1
  31. data/lib/async/matrix.rb +16 -7
  32. data/lib/protocol/matrix/canonical_json.rb +200 -0
  33. data/lib/protocol/matrix/content.rb +106 -0
  34. data/lib/protocol/matrix/encrypted_message.rb +1018 -0
  35. data/lib/protocol/matrix/error.rb +49 -0
  36. data/lib/protocol/matrix/event.rb +208 -0
  37. data/lib/protocol/matrix/key_backup.rb +347 -0
  38. data/lib/protocol/matrix/keys.rb +381 -0
  39. data/lib/protocol/matrix/message_batch.rb +482 -0
  40. data/lib/protocol/matrix/schema/registry.rb +381 -0
  41. data/lib/protocol/matrix/schema/validation_error.rb +234 -0
  42. data/lib/protocol/matrix/schema.rb +170 -0
  43. data/lib/protocol/matrix/secret_storage.rb +535 -0
  44. data/lib/protocol/matrix/signing.rb +278 -0
  45. data/lib/protocol/matrix.rb +25 -0
  46. metadata +43 -136
  47. data/lib/async/discord/api/path_tree.rb +0 -127
  48. data/lib/async/discord/api.rb +0 -151
  49. data/lib/async/discord/client.rb +0 -283
  50. data/lib/async/discord/error.rb +0 -84
  51. data/lib/async/discord/gateway.rb +0 -359
  52. data/lib/async/discord.rb +0 -15
  53. data/lib/async/matrix/application_service/bot.rb +0 -232
  54. data/lib/async/matrix/application_service/config/schema/analytics.json +0 -21
  55. data/lib/async/matrix/application_service/config/schema/appservice.json +0 -82
  56. data/lib/async/matrix/application_service/config/schema/backfill.json +0 -91
  57. data/lib/async/matrix/application_service/config/schema/bridge.json +0 -209
  58. data/lib/async/matrix/application_service/config/schema/config.json +0 -61
  59. data/lib/async/matrix/application_service/config/schema/database.json +0 -38
  60. data/lib/async/matrix/application_service/config/schema/direct_media.json +0 -35
  61. data/lib/async/matrix/application_service/config/schema/double_puppet.json +0 -24
  62. data/lib/async/matrix/application_service/config/schema/encryption.json +0 -164
  63. data/lib/async/matrix/application_service/config/schema/homeserver.json +0 -58
  64. data/lib/async/matrix/application_service/config/schema/logging.json +0 -50
  65. data/lib/async/matrix/application_service/config/schema/management_room_texts.json +0 -25
  66. data/lib/async/matrix/application_service/config/schema/matrix.json +0 -45
  67. data/lib/async/matrix/application_service/config/schema/permissions.json +0 -54
  68. data/lib/async/matrix/application_service/config/schema/provisioning.json +0 -23
  69. data/lib/async/matrix/application_service/config/schema/public_media.json +0 -39
  70. data/lib/async/matrix/application_service/config/schema/relay.json +0 -43
  71. data/lib/async/matrix/application_service/config/vivify.rb +0 -109
  72. data/lib/async/matrix/application_service/config.rb +0 -225
  73. data/lib/async/matrix/application_service/dispatcher.rb +0 -185
  74. data/lib/async/matrix/application_service/event.rb +0 -285
  75. data/lib/async/matrix/application_service/server.rb +0 -430
  76. data/lib/async/matrix/application_service/transaction.rb +0 -66
  77. data/lib/async/matrix/application_service/transaction_handler.rb +0 -185
  78. data/lib/async/matrix/application_service/transaction_store.rb +0 -80
  79. data/lib/async/matrix/bridge/discord/db/connection.rb +0 -141
  80. data/lib/async/matrix/bridge/discord/db/file.rb +0 -118
  81. data/lib/async/matrix/bridge/discord/db/guild.rb +0 -120
  82. data/lib/async/matrix/bridge/discord/db/message.rb +0 -160
  83. data/lib/async/matrix/bridge/discord/db/migrations/001_create_users.rb +0 -14
  84. data/lib/async/matrix/bridge/discord/db/migrations/002_create_guilds.rb +0 -14
  85. data/lib/async/matrix/bridge/discord/db/migrations/003_create_portals.rb +0 -23
  86. data/lib/async/matrix/bridge/discord/db/migrations/004_create_puppets.rb +0 -19
  87. data/lib/async/matrix/bridge/discord/db/migrations/005_create_messages.rb +0 -20
  88. data/lib/async/matrix/bridge/discord/db/migrations/006_create_reactions.rb +0 -19
  89. data/lib/async/matrix/bridge/discord/db/migrations/007_create_files.rb +0 -18
  90. data/lib/async/matrix/bridge/discord/db/portal.rb +0 -150
  91. data/lib/async/matrix/bridge/discord/db/puppet.rb +0 -128
  92. data/lib/async/matrix/bridge/discord/db/reaction.rb +0 -165
  93. data/lib/async/matrix/bridge/discord/db/schema.rb +0 -18
  94. data/lib/async/matrix/bridge/discord/db/user.rb +0 -112
  95. data/lib/async/matrix/bridge/discord/db.rb +0 -138
  96. data/lib/async/matrix/schema/registry.rb +0 -354
  97. data/lib/async/matrix/schema/validation_error.rb +0 -225
  98. data/lib/async/matrix/schema.rb +0 -170
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the Apache License, Version 2.0.
4
+ # Copyright, 2026, by General Intelligence Systems.
5
+
6
+ require "json"
7
+
8
+ require_relative "error"
9
+
10
+ module Protocol
11
+ module Matrix
12
+ # Canonical JSON: "the shortest UTF-8 JSON encoding with dictionary keys
13
+ # lexicographically sorted by Unicode codepoint."
14
+ #
15
+ # https://spec.matrix.org/latest/appendices/#canonical-json
16
+ #
17
+ # This is not a formatting preference. A signature is computed over these
18
+ # exact bytes, so any disagreement about whitespace, key order or number
19
+ # formatting produces a signature every other implementation rejects — and
20
+ # the failure appears at the far end, as somebody else's device refusing our
21
+ # keys, with nothing locally to see.
22
+ #
23
+ # The rules, each of which is enforced below:
24
+ #
25
+ # * dictionary keys sorted by Unicode codepoint
26
+ # * no insignificant whitespace (separators are "," and ":")
27
+ # * code-points outside ASCII encoded as UTF-8, NOT as \u escapes
28
+ # * integers only, in [-(2**53)+1, (2**53)-1], no exponents, no decimals
29
+ # * "Float values are not permitted by this encoding"
30
+ # * negative zero MUST NOT appear
31
+ module CanonicalJson
32
+ MAXIMUM_INTEGER = (2**53) - 1
33
+ MINIMUM_INTEGER = -(2**53) + 1
34
+
35
+ # The two keys a signature never covers: "The `unsigned` object and the
36
+ # `signatures` object are not covered by the signature."
37
+ EXCLUDED_FROM_SIGNATURE = %w[signatures unsigned].freeze
38
+
39
+ class Error < Protocol::Matrix::Error; end
40
+
41
+ # @returns [String] the canonical encoding of +value+.
42
+ def self.encode(value)
43
+ JSON.generate(canonicalize(value))
44
+ end
45
+
46
+ # Sort, stringify and check, recursively.
47
+ #
48
+ # Ruby's String#<=> compares bytes, and UTF-8 byte order is codepoint
49
+ # order, so a plain sort IS the codepoint sort the spec asks for. That
50
+ # equivalence is the whole reason this is three lines rather than a
51
+ # custom comparator.
52
+ #
53
+ # Keys are stringified BEFORE sorting, not read back out of the hash
54
+ # afterwards: `h[k] || h[k.to_sym]` would mishandle a stored `false`.
55
+ def self.canonicalize(value)
56
+ case value
57
+ when Hash
58
+ value.to_h { |key, nested| [key.to_s, canonicalize(nested)] }.sort.to_h
59
+ when Array
60
+ value.map { |nested| canonicalize(nested) }
61
+ when Float
62
+ raise Error, "float values are not permitted by canonical JSON: #{value.inspect}"
63
+ when Integer
64
+ check_integer(value)
65
+ when Symbol
66
+ value.to_s
67
+ else
68
+ value
69
+ end
70
+ end
71
+
72
+ def self.check_integer(value)
73
+ if value > MAXIMUM_INTEGER || value < MINIMUM_INTEGER
74
+ raise Error,
75
+ "integer out of canonical JSON range [#{MINIMUM_INTEGER}, #{MAXIMUM_INTEGER}]: #{value}"
76
+ end
77
+
78
+ value
79
+ end
80
+
81
+ # The object as it is signed: everything except the two keys a signature
82
+ # does not cover. Returns a copy; the original is untouched, because the
83
+ # caller still needs its signatures.
84
+ def self.signable(object)
85
+ object.reject { |key, _| EXCLUDED_FROM_SIGNATURE.include?(key.to_s) }
86
+ end
87
+
88
+ # The exact bytes a signature is computed over.
89
+ def self.signable_bytes(object)
90
+ encode(signable(object))
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ __END__
97
+ describe "Protocol::Matrix::CanonicalJson" do
98
+ def encode(value) = Protocol::Matrix::CanonicalJson.encode(value)
99
+
100
+ # The worked examples from https://spec.matrix.org/latest/appendices/#canonical-json
101
+ it "matches the spec's empty object example" do
102
+ encode({}).should == "{}"
103
+ end
104
+
105
+ it "matches the spec's key ordering example" do
106
+ encode({"one" => 1, "two" => "Two"}).should == '{"one":1,"two":"Two"}'
107
+ encode({"b" => "2", "a" => "1"}).should == '{"a":"1","b":"2"}'
108
+ end
109
+
110
+ it "matches the spec's nested example" do
111
+ encode({"auth" => {"success" => true, "mxid" => "@john.doe:example.com"}})
112
+ .should == '{"auth":{"mxid":"@john.doe:example.com","success":true}}'
113
+ end
114
+
115
+ # "Encode code-points outside of ASCII as UTF-8 rather than \u escapes."
116
+ it "emits non-ASCII as UTF-8 rather than escapes" do
117
+ encode({"a" => "日本語"}).should == '{"a":"日本語"}'
118
+ end
119
+
120
+ it "keeps an escape the JSON grammar requires" do
121
+ encode({"a" => "quote\"and\\slash"}).should == '{"a":"quote\"and\\\\slash"}'
122
+ end
123
+
124
+ # Keys sort by Unicode codepoint, which for UTF-8 is byte order.
125
+ it "sorts keys by codepoint, not by locale or length" do
126
+ encode({"Z" => 1, "a" => 2, "A" => 3, "z" => 4})
127
+ .should == '{"A":3,"Z":1,"a":2,"z":4}'
128
+ encode({"é" => 1, "z" => 2}).should == '{"z":2,"é":1}'
129
+ end
130
+
131
+ it "sorts nested objects too" do
132
+ encode({"b" => {"d" => 1, "c" => 2}, "a" => 3})
133
+ .should == '{"a":3,"b":{"c":2,"d":1}}'
134
+ end
135
+
136
+ # Arrays are ordered data: their order is the sender's, not ours to sort.
137
+ it "leaves array order alone" do
138
+ encode({"a" => [3, 1, 2]}).should == '{"a":[3,1,2]}'
139
+ end
140
+
141
+ it "has no insignificant whitespace anywhere" do
142
+ encoded = encode({"a" => {"b" => [1, 2]}, "c" => "d"})
143
+
144
+ encoded.should == '{"a":{"b":[1,2]},"c":"d"}'
145
+ encoded.include?(" ").should == false
146
+ end
147
+
148
+ it "stringifies symbol keys and values" do
149
+ encode({a: :b}).should == '{"a":"b"}'
150
+ end
151
+
152
+ it "encodes null, true and false" do
153
+ encode({"a" => nil, "b" => true, "c" => false}).should == '{"a":null,"b":true,"c":false}'
154
+ end
155
+
156
+ # "Float values are not permitted by this encoding."
157
+ it "refuses floats, including ones that look like integers" do
158
+ lambda { encode({"a" => 1.5}) }.should.raise(Protocol::Matrix::CanonicalJson::Error)
159
+ lambda { encode({"a" => 1.0}) }.should.raise(Protocol::Matrix::CanonicalJson::Error)
160
+ lambda { encode({"a" => [1.0]}) }.should.raise(Protocol::Matrix::CanonicalJson::Error)
161
+ end
162
+
163
+ # "Numbers in the JSON must be integers in the range [-(2**53)+1, (2**53)-1]"
164
+ it "accepts the integer range boundaries" do
165
+ encode({"a" => (2**53) - 1}).should == '{"a":9007199254740991}'
166
+ encode({"a" => -(2**53) + 1}).should == '{"a":-9007199254740991}'
167
+ end
168
+
169
+ it "refuses integers outside the range" do
170
+ lambda { encode({"a" => 2**53}) }.should.raise(Protocol::Matrix::CanonicalJson::Error)
171
+ lambda { encode({"a" => -(2**53)}) }.should.raise(Protocol::Matrix::CanonicalJson::Error)
172
+ end
173
+
174
+ # ── What a signature covers ───────────────────────────────────────────────
175
+
176
+ # "The `unsigned` object and the `signatures` object are not covered by the
177
+ # signature."
178
+ it "excludes signatures and unsigned from the signable form" do
179
+ object = {
180
+ "a" => 1,
181
+ "signatures" => {"@alice:example.com" => {"ed25519:DEV" => "sig"}},
182
+ "unsigned" => {"age" => 100},
183
+ }
184
+
185
+ Protocol::Matrix::CanonicalJson.signable(object).should == {"a" => 1}
186
+ Protocol::Matrix::CanonicalJson.signable_bytes(object).should == '{"a":1}'
187
+ end
188
+
189
+ it "leaves the original object untouched" do
190
+ object = {"a" => 1, "signatures" => {"x" => "y"}}
191
+ Protocol::Matrix::CanonicalJson.signable(object)
192
+
193
+ object.key?("signatures").should == true
194
+ end
195
+
196
+ it "excludes them whether the keys are strings or symbols" do
197
+ Protocol::Matrix::CanonicalJson.signable({:a => 1, :signatures => {}, :unsigned => {}})
198
+ .should == {:a => 1}
199
+ end
200
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the Apache License, Version 2.0.
4
+ # Copyright, 2026, by General Intelligence Systems.
5
+
6
+ module Protocol
7
+ module Matrix
8
+ # Wraps the content object of a Matrix event, providing typed access to
9
+ # schema-defined properties via method_missing.
10
+ #
11
+ # Common fields like `msgtype`, `body`, and `membership` have dedicated
12
+ # accessors for convenience. Any other field defined in the event's schema
13
+ # (or present in the raw hash) is accessible as a method call:
14
+ #
15
+ # content.msgtype # => "m.text"
16
+ # content.body # => "hello"
17
+ # content.membership # => "join"
18
+ # content.avatar_url # => "mxc://example.org/abc"
19
+ # content["custom"] # => direct hash access for non-standard fields
20
+ #
21
+ class Content
22
+ attr_reader :msgtype, :body, :membership
23
+
24
+ def initialize(data)
25
+ @data = data
26
+ @msgtype = data["msgtype"]
27
+ @body = data["body"]
28
+ @membership = data["membership"]
29
+ end
30
+
31
+ def to_h = @data
32
+ def to_s = @body.to_s
33
+ def to_str = to_s
34
+
35
+ # Direct hash access for any content field.
36
+ def [](key) = @data[key.to_s]
37
+
38
+ # Dynamic access to any content field present in the raw data.
39
+ def method_missing(name, *args)
40
+ key = name.to_s
41
+
42
+ if @data.key?(key)
43
+ @data[key]
44
+ else
45
+ nil
46
+ end
47
+ end
48
+
49
+ def respond_to_missing?(name, include_private = false)
50
+ @data.key?(name.to_s) || super
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ __END__
57
+ describe "Protocol::Matrix::Content" do
58
+ it "parses msgtype, body, and membership" do
59
+ content = Protocol::Matrix::Content.new({
60
+ "msgtype" => "m.text",
61
+ "body" => "hello",
62
+ "membership" => "join"
63
+ })
64
+ content.msgtype.should == "m.text"
65
+ content.body.should == "hello"
66
+ content.membership.should == "join"
67
+ end
68
+
69
+ it "handles missing fields gracefully" do
70
+ content = Protocol::Matrix::Content.new({})
71
+ content.msgtype.should.be.nil
72
+ content.body.should.be.nil
73
+ content.membership.should.be.nil
74
+ end
75
+
76
+ it "provides hash access via []" do
77
+ content = Protocol::Matrix::Content.new({"custom_field" => "value"})
78
+ content["custom_field"].should == "value"
79
+ end
80
+
81
+ it "provides dynamic access via method_missing" do
82
+ content = Protocol::Matrix::Content.new({
83
+ "avatar_url" => "mxc://example.org/abc",
84
+ "displayname" => "Alice"
85
+ })
86
+ content.avatar_url.should == "mxc://example.org/abc"
87
+ content.displayname.should == "Alice"
88
+ end
89
+
90
+ it "returns nil for unknown fields via method_missing" do
91
+ content = Protocol::Matrix::Content.new({})
92
+ content.nonexistent.should.be.nil
93
+ end
94
+
95
+ it "returns the raw hash via to_h" do
96
+ data = {"msgtype" => "m.text", "body" => "hi"}
97
+ content = Protocol::Matrix::Content.new(data)
98
+ content.to_h.should == data
99
+ end
100
+
101
+ it "responds to keys present in the data" do
102
+ content = Protocol::Matrix::Content.new({"avatar_url" => "mxc://x/y"})
103
+ content.respond_to?(:avatar_url).should == true
104
+ content.respond_to?(:nonexistent).should == false
105
+ end
106
+ end