async-matrix 2.1.0 → 3.0.1

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +8 -8
  3. data/README.md +33 -58
  4. data/ext/async_matrix_e2ee/Cargo.toml +7 -1
  5. data/lib/async/matrix/api/chain.rb +120 -94
  6. data/lib/async/matrix/api/concat.rb +28 -22
  7. data/lib/async/matrix/api/path_tree.rb +71 -44
  8. data/lib/async/matrix/api.rb +6 -1
  9. data/lib/async/matrix/app_service_client.rb +213 -0
  10. data/lib/async/matrix/auth_error.rb +20 -0
  11. data/lib/async/matrix/bad_json_error.rb +20 -0
  12. data/lib/async/matrix/client/encryption.rb +413 -0
  13. data/lib/async/matrix/client/rooms.rb +478 -0
  14. data/lib/async/matrix/client/sync.rb +508 -0
  15. data/lib/async/matrix/client.rb +217 -113
  16. data/lib/async/matrix/config/vivify.rb +107 -0
  17. data/lib/async/matrix/config.rb +184 -0
  18. data/lib/async/matrix/device_store.rb +1621 -0
  19. data/lib/async/matrix/double_puppet_client.rb +1 -1
  20. data/lib/async/matrix/e2ee/pickle_key.rb +182 -0
  21. data/lib/async/matrix/error.rb +9 -36
  22. data/lib/async/matrix/{application_service/error_response.rb → error_response.rb} +10 -10
  23. data/lib/async/matrix/homeserver_error.rb +20 -0
  24. data/lib/async/matrix/invalid_endpoint_error.rb +20 -0
  25. data/lib/async/matrix/media_client.rb +46 -35
  26. data/lib/async/matrix/not_found_error.rb +20 -0
  27. data/lib/async/matrix/notifier.rb +7 -3
  28. data/lib/async/matrix/response_too_large_error.rb +20 -0
  29. data/lib/async/matrix/version.rb +1 -1
  30. data/lib/async/matrix.rb +16 -7
  31. data/lib/protocol/matrix/canonical_json.rb +200 -0
  32. data/lib/protocol/matrix/content.rb +106 -0
  33. data/lib/protocol/matrix/encrypted_message.rb +1018 -0
  34. data/lib/protocol/matrix/error.rb +49 -0
  35. data/lib/protocol/matrix/event.rb +208 -0
  36. data/lib/protocol/matrix/key_backup.rb +347 -0
  37. data/lib/protocol/matrix/keys.rb +381 -0
  38. data/lib/protocol/matrix/message_batch.rb +482 -0
  39. data/lib/protocol/matrix/schema/registry.rb +381 -0
  40. data/lib/protocol/matrix/schema/validation_error.rb +234 -0
  41. data/lib/protocol/matrix/schema.rb +170 -0
  42. data/lib/protocol/matrix/secret_storage.rb +535 -0
  43. data/lib/protocol/matrix/signing.rb +278 -0
  44. data/lib/protocol/matrix.rb +25 -0
  45. metadata +42 -135
  46. data/lib/async/discord/api/path_tree.rb +0 -127
  47. data/lib/async/discord/api.rb +0 -151
  48. data/lib/async/discord/client.rb +0 -283
  49. data/lib/async/discord/error.rb +0 -84
  50. data/lib/async/discord/gateway.rb +0 -359
  51. data/lib/async/discord.rb +0 -15
  52. data/lib/async/matrix/application_service/bot.rb +0 -232
  53. data/lib/async/matrix/application_service/config/schema/analytics.json +0 -21
  54. data/lib/async/matrix/application_service/config/schema/appservice.json +0 -82
  55. data/lib/async/matrix/application_service/config/schema/backfill.json +0 -91
  56. data/lib/async/matrix/application_service/config/schema/bridge.json +0 -209
  57. data/lib/async/matrix/application_service/config/schema/config.json +0 -61
  58. data/lib/async/matrix/application_service/config/schema/database.json +0 -38
  59. data/lib/async/matrix/application_service/config/schema/direct_media.json +0 -35
  60. data/lib/async/matrix/application_service/config/schema/double_puppet.json +0 -24
  61. data/lib/async/matrix/application_service/config/schema/encryption.json +0 -164
  62. data/lib/async/matrix/application_service/config/schema/homeserver.json +0 -58
  63. data/lib/async/matrix/application_service/config/schema/logging.json +0 -50
  64. data/lib/async/matrix/application_service/config/schema/management_room_texts.json +0 -25
  65. data/lib/async/matrix/application_service/config/schema/matrix.json +0 -45
  66. data/lib/async/matrix/application_service/config/schema/permissions.json +0 -54
  67. data/lib/async/matrix/application_service/config/schema/provisioning.json +0 -23
  68. data/lib/async/matrix/application_service/config/schema/public_media.json +0 -39
  69. data/lib/async/matrix/application_service/config/schema/relay.json +0 -43
  70. data/lib/async/matrix/application_service/config/vivify.rb +0 -109
  71. data/lib/async/matrix/application_service/config.rb +0 -225
  72. data/lib/async/matrix/application_service/dispatcher.rb +0 -185
  73. data/lib/async/matrix/application_service/event.rb +0 -285
  74. data/lib/async/matrix/application_service/server.rb +0 -430
  75. data/lib/async/matrix/application_service/transaction.rb +0 -66
  76. data/lib/async/matrix/application_service/transaction_handler.rb +0 -185
  77. data/lib/async/matrix/application_service/transaction_store.rb +0 -80
  78. data/lib/async/matrix/bridge/discord/db/connection.rb +0 -141
  79. data/lib/async/matrix/bridge/discord/db/file.rb +0 -118
  80. data/lib/async/matrix/bridge/discord/db/guild.rb +0 -120
  81. data/lib/async/matrix/bridge/discord/db/message.rb +0 -160
  82. data/lib/async/matrix/bridge/discord/db/migrations/001_create_users.rb +0 -14
  83. data/lib/async/matrix/bridge/discord/db/migrations/002_create_guilds.rb +0 -14
  84. data/lib/async/matrix/bridge/discord/db/migrations/003_create_portals.rb +0 -23
  85. data/lib/async/matrix/bridge/discord/db/migrations/004_create_puppets.rb +0 -19
  86. data/lib/async/matrix/bridge/discord/db/migrations/005_create_messages.rb +0 -20
  87. data/lib/async/matrix/bridge/discord/db/migrations/006_create_reactions.rb +0 -19
  88. data/lib/async/matrix/bridge/discord/db/migrations/007_create_files.rb +0 -18
  89. data/lib/async/matrix/bridge/discord/db/portal.rb +0 -150
  90. data/lib/async/matrix/bridge/discord/db/puppet.rb +0 -128
  91. data/lib/async/matrix/bridge/discord/db/reaction.rb +0 -165
  92. data/lib/async/matrix/bridge/discord/db/schema.rb +0 -18
  93. data/lib/async/matrix/bridge/discord/db/user.rb +0 -112
  94. data/lib/async/matrix/bridge/discord/db.rb +0 -138
  95. data/lib/async/matrix/schema/registry.rb +0 -354
  96. data/lib/async/matrix/schema/validation_error.rb +0 -225
  97. data/lib/async/matrix/schema.rb +0 -170
@@ -0,0 +1,184 @@
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 "forwardable"
7
+ require "yaml"
8
+ require "pathname"
9
+ require_relative "config/vivify"
10
+
11
+ module Async
12
+ module Matrix
13
+ # Dot-notation access to a Matrix service's YAML configuration.
14
+ #
15
+ # {Client} only reads two fields — `homeserver.address` and
16
+ # `appservice.as_token` — so this class validates nothing: it vivifies
17
+ # whatever hash (or YAML file) it is handed and exposes every nested field
18
+ # as a method call.
19
+ #
20
+ # config = Config.load("config/appservice.yml")
21
+ # config.homeserver.address # => "http://synapse:8008"
22
+ # config.appservice.as_token # => "secret..."
23
+ # config.appservice.bot.username # => "bot"
24
+ # config.bot_mxid # => "@bot:localhost"
25
+ #
26
+ # A subclass layers validation on by overriding {.validate!}; that is how
27
+ # async-matrix-bridge's Config applies the mautrix bridgev2 JSON Schema
28
+ # suite without reimplementing any of the loading below.
29
+ class Config
30
+ extend Forwardable
31
+
32
+ # Delegate every top-level config section to the vivified data hash. An
33
+ # explicit list, not a method_missing forward: a typo'd section should
34
+ # raise NoMethodError rather than autovivify into an empty hash.
35
+ def_delegators :@data,
36
+ :network,
37
+ :bridge,
38
+ :database,
39
+ :homeserver,
40
+ :appservice,
41
+ :matrix,
42
+ :analytics,
43
+ :provisioning,
44
+ :public_media,
45
+ :direct_media,
46
+ :backfill,
47
+ :double_puppet,
48
+ :encryption,
49
+ :logging,
50
+ :management_room_texts,
51
+ :env_config_prefix
52
+
53
+ def initialize(data)
54
+ self.class.validate!(data)
55
+ @data = Vivify.deep_vivify(data)
56
+ end
57
+
58
+ # Load a YAML config file from disk.
59
+ def self.load(path)
60
+ unless File.exist?(path)
61
+ raise Async::Matrix::NotFoundError.new(
62
+ "M_NOT_FOUND",
63
+ "Config not found: #{path}",
64
+ )
65
+ end
66
+
67
+ data = YAML.safe_load_file(path, permitted_classes: [Symbol])
68
+ new(data)
69
+ end
70
+
71
+ # Validation hook, called with the raw (string-keyed) hash before it is
72
+ # vivified. A no-op here; subclasses override it to raise on bad input,
73
+ # and may mutate `data` to insert defaults.
74
+ def self.validate!(data)
75
+ nil
76
+ end
77
+
78
+ # Convenience: derive the bot's full Matrix ID from
79
+ # appservice.bot.username + homeserver.domain.
80
+ def bot_mxid
81
+ "@#{appservice.bot.username}:#{homeserver.domain}"
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ __END__
88
+ require "tempfile"
89
+
90
+ describe "Async::Matrix::Config" do
91
+ def minimal_data
92
+ {
93
+ "homeserver" => {
94
+ "address" => "http://localhost:8008",
95
+ "domain" => "localhost",
96
+ },
97
+ "appservice" => {
98
+ "as_token" => "as_secret_token_value",
99
+ "hs_token" => "hs_secret_token_value",
100
+ "bot" => {"username" => "bot"},
101
+ },
102
+ }
103
+ end
104
+
105
+ it "exposes top-level sections" do
106
+ config = Async::Matrix::Config.new(minimal_data)
107
+ config.homeserver.address.should == "http://localhost:8008"
108
+ config.homeserver.domain.should == "localhost"
109
+ config.appservice.as_token.should == "as_secret_token_value"
110
+ config.appservice.hs_token.should == "hs_secret_token_value"
111
+ config.appservice.bot.username.should == "bot"
112
+ end
113
+
114
+ it "derives bot_mxid from appservice.bot.username and homeserver.domain" do
115
+ Async::Matrix::Config.new(minimal_data).bot_mxid.should == "@bot:localhost"
116
+ end
117
+
118
+ it "provides dot-notation access to deeply nested fields" do
119
+ data = minimal_data.merge(
120
+ "encryption" => {
121
+ "allow" => true,
122
+ "rotation" => {"messages" => 200},
123
+ },
124
+ )
125
+ config = Async::Matrix::Config.new(data)
126
+ config.encryption.allow.should == true
127
+ config.encryption.rotation.messages.should == 200
128
+ end
129
+
130
+ it "autovivifies missing optional sections as empty hashes" do
131
+ config = Async::Matrix::Config.new(minimal_data)
132
+ config.analytics.should.be.kind_of Hash
133
+ config.analytics.should.be.empty?
134
+ end
135
+
136
+ it "raises NoMethodError for sections it does not delegate" do
137
+ config = Async::Matrix::Config.new(minimal_data)
138
+ lambda { config.bogus }.should.raise(NoMethodError)
139
+ end
140
+
141
+ it "validates nothing by default" do
142
+ lambda { Async::Matrix::Config.new({}) }.should.not.raise
143
+ end
144
+
145
+ it "loads from a YAML file" do
146
+ file = Tempfile.new(["config", ".yml"])
147
+ file.write(<<~YAML)
148
+ homeserver:
149
+ address: "http://localhost:8008"
150
+ domain: "localhost"
151
+ appservice:
152
+ as_token: "as123_secret"
153
+ hs_token: "hs456_secret"
154
+ bot:
155
+ username: "testbot"
156
+ YAML
157
+ file.close
158
+
159
+ config = Async::Matrix::Config.load(file.path)
160
+ config.homeserver.address.should == "http://localhost:8008"
161
+ config.bot_mxid.should == "@testbot:localhost"
162
+ ensure
163
+ file.unlink
164
+ end
165
+
166
+ it "raises NotFoundError for a missing file" do
167
+ lambda {
168
+ Async::Matrix::Config.load("/nonexistent/path.yml")
169
+ }.should.raise(Async::Matrix::NotFoundError)
170
+ end
171
+
172
+ it "runs the validate! hook a subclass overrides" do
173
+ strict = Class.new(Async::Matrix::Config) do
174
+ def self.validate!(data)
175
+ unless data.key?("homeserver")
176
+ raise Async::Matrix::BadJsonError.new("M_BAD_JSON", "no homeserver")
177
+ end
178
+ end
179
+ end
180
+
181
+ lambda { strict.new({}) }.should.raise(Async::Matrix::BadJsonError)
182
+ strict.new(minimal_data).homeserver.domain.should == "localhost"
183
+ end
184
+ end