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.
- checksums.yaml +4 -4
- data/README.md +33 -58
- data/lib/async/matrix/3.3/async_matrix_e2ee.so +0 -0
- data/lib/async/matrix/3.4/async_matrix_e2ee.so +0 -0
- data/lib/async/matrix/4.0/async_matrix_e2ee.so +0 -0
- data/lib/async/matrix/api/chain.rb +120 -94
- data/lib/async/matrix/api/concat.rb +28 -22
- data/lib/async/matrix/api/path_tree.rb +71 -44
- data/lib/async/matrix/api.rb +6 -1
- data/lib/async/matrix/app_service_client.rb +213 -0
- data/lib/async/matrix/auth_error.rb +20 -0
- data/lib/async/matrix/bad_json_error.rb +20 -0
- data/lib/async/matrix/client/encryption.rb +413 -0
- data/lib/async/matrix/client/rooms.rb +478 -0
- data/lib/async/matrix/client/sync.rb +508 -0
- data/lib/async/matrix/client.rb +217 -113
- data/lib/async/matrix/config/vivify.rb +107 -0
- data/lib/async/matrix/config.rb +184 -0
- data/lib/async/matrix/device_store.rb +1621 -0
- data/lib/async/matrix/double_puppet_client.rb +1 -1
- data/lib/async/matrix/e2ee/pickle_key.rb +182 -0
- data/lib/async/matrix/error.rb +9 -36
- data/lib/async/matrix/{application_service/error_response.rb → error_response.rb} +10 -10
- data/lib/async/matrix/homeserver_error.rb +20 -0
- data/lib/async/matrix/invalid_endpoint_error.rb +20 -0
- data/lib/async/matrix/media_client.rb +46 -35
- data/lib/async/matrix/not_found_error.rb +20 -0
- data/lib/async/matrix/notifier.rb +7 -3
- data/lib/async/matrix/response_too_large_error.rb +20 -0
- data/lib/async/matrix/version.rb +1 -1
- data/lib/async/matrix.rb +16 -7
- data/lib/protocol/matrix/canonical_json.rb +200 -0
- data/lib/protocol/matrix/content.rb +106 -0
- data/lib/protocol/matrix/encrypted_message.rb +1018 -0
- data/lib/protocol/matrix/error.rb +49 -0
- data/lib/protocol/matrix/event.rb +208 -0
- data/lib/protocol/matrix/key_backup.rb +347 -0
- data/lib/protocol/matrix/keys.rb +381 -0
- data/lib/protocol/matrix/message_batch.rb +482 -0
- data/lib/protocol/matrix/schema/registry.rb +381 -0
- data/lib/protocol/matrix/schema/validation_error.rb +234 -0
- data/lib/protocol/matrix/schema.rb +170 -0
- data/lib/protocol/matrix/secret_storage.rb +535 -0
- data/lib/protocol/matrix/signing.rb +278 -0
- data/lib/protocol/matrix.rb +25 -0
- metadata +43 -136
- data/lib/async/discord/api/path_tree.rb +0 -127
- data/lib/async/discord/api.rb +0 -151
- data/lib/async/discord/client.rb +0 -283
- data/lib/async/discord/error.rb +0 -84
- data/lib/async/discord/gateway.rb +0 -359
- data/lib/async/discord.rb +0 -15
- data/lib/async/matrix/application_service/bot.rb +0 -232
- data/lib/async/matrix/application_service/config/schema/analytics.json +0 -21
- data/lib/async/matrix/application_service/config/schema/appservice.json +0 -82
- data/lib/async/matrix/application_service/config/schema/backfill.json +0 -91
- data/lib/async/matrix/application_service/config/schema/bridge.json +0 -209
- data/lib/async/matrix/application_service/config/schema/config.json +0 -61
- data/lib/async/matrix/application_service/config/schema/database.json +0 -38
- data/lib/async/matrix/application_service/config/schema/direct_media.json +0 -35
- data/lib/async/matrix/application_service/config/schema/double_puppet.json +0 -24
- data/lib/async/matrix/application_service/config/schema/encryption.json +0 -164
- data/lib/async/matrix/application_service/config/schema/homeserver.json +0 -58
- data/lib/async/matrix/application_service/config/schema/logging.json +0 -50
- data/lib/async/matrix/application_service/config/schema/management_room_texts.json +0 -25
- data/lib/async/matrix/application_service/config/schema/matrix.json +0 -45
- data/lib/async/matrix/application_service/config/schema/permissions.json +0 -54
- data/lib/async/matrix/application_service/config/schema/provisioning.json +0 -23
- data/lib/async/matrix/application_service/config/schema/public_media.json +0 -39
- data/lib/async/matrix/application_service/config/schema/relay.json +0 -43
- data/lib/async/matrix/application_service/config/vivify.rb +0 -109
- data/lib/async/matrix/application_service/config.rb +0 -225
- data/lib/async/matrix/application_service/dispatcher.rb +0 -185
- data/lib/async/matrix/application_service/event.rb +0 -285
- data/lib/async/matrix/application_service/server.rb +0 -430
- data/lib/async/matrix/application_service/transaction.rb +0 -66
- data/lib/async/matrix/application_service/transaction_handler.rb +0 -185
- data/lib/async/matrix/application_service/transaction_store.rb +0 -80
- data/lib/async/matrix/bridge/discord/db/connection.rb +0 -141
- data/lib/async/matrix/bridge/discord/db/file.rb +0 -118
- data/lib/async/matrix/bridge/discord/db/guild.rb +0 -120
- data/lib/async/matrix/bridge/discord/db/message.rb +0 -160
- data/lib/async/matrix/bridge/discord/db/migrations/001_create_users.rb +0 -14
- data/lib/async/matrix/bridge/discord/db/migrations/002_create_guilds.rb +0 -14
- data/lib/async/matrix/bridge/discord/db/migrations/003_create_portals.rb +0 -23
- data/lib/async/matrix/bridge/discord/db/migrations/004_create_puppets.rb +0 -19
- data/lib/async/matrix/bridge/discord/db/migrations/005_create_messages.rb +0 -20
- data/lib/async/matrix/bridge/discord/db/migrations/006_create_reactions.rb +0 -19
- data/lib/async/matrix/bridge/discord/db/migrations/007_create_files.rb +0 -18
- data/lib/async/matrix/bridge/discord/db/portal.rb +0 -150
- data/lib/async/matrix/bridge/discord/db/puppet.rb +0 -128
- data/lib/async/matrix/bridge/discord/db/reaction.rb +0 -165
- data/lib/async/matrix/bridge/discord/db/schema.rb +0 -18
- data/lib/async/matrix/bridge/discord/db/user.rb +0 -112
- data/lib/async/matrix/bridge/discord/db.rb +0 -138
- data/lib/async/matrix/schema/registry.rb +0 -354
- data/lib/async/matrix/schema/validation_error.rb +0 -225
- data/lib/async/matrix/schema.rb +0 -170
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Sequel.migration do
|
|
4
|
-
change do
|
|
5
|
-
create_table(:messages) do
|
|
6
|
-
primary_key :id
|
|
7
|
-
String :discord_id, null: false
|
|
8
|
-
String :discord_attachment_id, null: false, default: ""
|
|
9
|
-
String :discord_channel_id, null: false
|
|
10
|
-
String :discord_channel_receiver, null: false, default: ""
|
|
11
|
-
String :discord_sender, null: false
|
|
12
|
-
String :mxid, null: false
|
|
13
|
-
Bignum :timestamp, null: false
|
|
14
|
-
String :discord_thread_id
|
|
15
|
-
|
|
16
|
-
unique [:discord_id, :discord_attachment_id, :discord_channel_id, :discord_channel_receiver]
|
|
17
|
-
index :mxid
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Sequel.migration do
|
|
4
|
-
change do
|
|
5
|
-
create_table(:reactions) do
|
|
6
|
-
primary_key :id
|
|
7
|
-
String :discord_message_id, null: false
|
|
8
|
-
String :discord_sender, null: false
|
|
9
|
-
String :discord_emoji_name, null: false
|
|
10
|
-
String :mxid, null: false
|
|
11
|
-
String :discord_channel_id, null: false
|
|
12
|
-
String :discord_channel_receiver, null: false, default: ""
|
|
13
|
-
String :discord_thread_id
|
|
14
|
-
|
|
15
|
-
unique [:discord_message_id, :discord_sender, :discord_emoji_name]
|
|
16
|
-
index :mxid
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Sequel.migration do
|
|
4
|
-
change do
|
|
5
|
-
create_table(:files) do
|
|
6
|
-
String :url, null: false
|
|
7
|
-
TrueClass :encrypted, null: false, default: false
|
|
8
|
-
String :mxc, null: false
|
|
9
|
-
String :mime_type
|
|
10
|
-
Bignum :size
|
|
11
|
-
Integer :width
|
|
12
|
-
Integer :height
|
|
13
|
-
String :decryption_info
|
|
14
|
-
|
|
15
|
-
primary_key [:url, :encrypted]
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,150 +0,0 @@
|
|
|
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_relative "schema"
|
|
7
|
-
|
|
8
|
-
module Async
|
|
9
|
-
module Matrix
|
|
10
|
-
module Bridge
|
|
11
|
-
module Discord
|
|
12
|
-
module DB
|
|
13
|
-
# Maps a Discord channel to a Matrix room.
|
|
14
|
-
#
|
|
15
|
-
# Uses a composite primary key of (discord_id, receiver). For guild
|
|
16
|
-
# channels, receiver is an empty string. For DMs, receiver is the
|
|
17
|
-
# Discord user ID of the bridge user who owns the portal.
|
|
18
|
-
#
|
|
19
|
-
# portal = Portal.create(discord_id: "chan123", receiver: "", mxid: "!room:x")
|
|
20
|
-
# portal.guild # => Guild or nil
|
|
21
|
-
# portal.messages # => [Message, ...]
|
|
22
|
-
#
|
|
23
|
-
class Portal < Sequel::Model
|
|
24
|
-
unrestrict_primary_key
|
|
25
|
-
|
|
26
|
-
many_to_one :guild,
|
|
27
|
-
class: "Async::Matrix::Bridge::Discord::DB::Guild",
|
|
28
|
-
key: :discord_guild_id,
|
|
29
|
-
primary_key: :discord_id
|
|
30
|
-
|
|
31
|
-
one_to_many :messages,
|
|
32
|
-
class: "Async::Matrix::Bridge::Discord::DB::Message",
|
|
33
|
-
key: [:discord_channel_id, :discord_channel_receiver],
|
|
34
|
-
primary_key: [:discord_id, :receiver]
|
|
35
|
-
|
|
36
|
-
def validate
|
|
37
|
-
super
|
|
38
|
-
errors.add(:discord_id, "cannot be empty") if discord_id.nil? || discord_id.empty?
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Is this a DM portal? (has a receiver)
|
|
42
|
-
def dm?
|
|
43
|
-
receiver && !receiver.empty?
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Is this a guild channel portal?
|
|
47
|
-
def guild_channel?
|
|
48
|
-
!dm?
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
__END__
|
|
58
|
-
describe "Async::Matrix::Bridge::Discord::DB::Portal" do
|
|
59
|
-
def setup_db
|
|
60
|
-
db = Sequel.sqlite
|
|
61
|
-
Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
|
|
62
|
-
db
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def set_datasets(db)
|
|
66
|
-
Async::Matrix::Bridge::Discord::DB::Portal.dataset = db[:portals]
|
|
67
|
-
Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
|
|
68
|
-
Async::Matrix::Bridge::Discord::DB::Message.dataset = db[:messages]
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
it "creates a guild channel portal" do
|
|
72
|
-
db = setup_db
|
|
73
|
-
set_datasets(db)
|
|
74
|
-
|
|
75
|
-
portal = Async::Matrix::Bridge::Discord::DB::Portal.create(
|
|
76
|
-
discord_id: "chan1", receiver: "", mxid: "!room:x", name: "general"
|
|
77
|
-
)
|
|
78
|
-
portal.discord_id.should == "chan1"
|
|
79
|
-
portal.receiver.should == ""
|
|
80
|
-
portal.guild_channel?.should == true
|
|
81
|
-
portal.dm?.should == false
|
|
82
|
-
db.disconnect
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
it "creates a DM portal with receiver" do
|
|
86
|
-
db = setup_db
|
|
87
|
-
set_datasets(db)
|
|
88
|
-
|
|
89
|
-
portal = Async::Matrix::Bridge::Discord::DB::Portal.create(
|
|
90
|
-
discord_id: "dm1", receiver: "user123", mxid: "!dm:x"
|
|
91
|
-
)
|
|
92
|
-
portal.dm?.should == true
|
|
93
|
-
portal.guild_channel?.should == false
|
|
94
|
-
db.disconnect
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
it "finds by composite primary key" do
|
|
98
|
-
db = setup_db
|
|
99
|
-
set_datasets(db)
|
|
100
|
-
|
|
101
|
-
Async::Matrix::Bridge::Discord::DB::Portal.create(discord_id: "c1", receiver: "")
|
|
102
|
-
Async::Matrix::Bridge::Discord::DB::Portal.create(discord_id: "c1", receiver: "u1")
|
|
103
|
-
|
|
104
|
-
found = Async::Matrix::Bridge::Discord::DB::Portal["c1", ""]
|
|
105
|
-
found.should.not.be.nil
|
|
106
|
-
found.receiver.should == ""
|
|
107
|
-
|
|
108
|
-
found2 = Async::Matrix::Bridge::Discord::DB::Portal["c1", "u1"]
|
|
109
|
-
found2.receiver.should == "u1"
|
|
110
|
-
db.disconnect
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
it "associates with a guild" do
|
|
114
|
-
db = setup_db
|
|
115
|
-
set_datasets(db)
|
|
116
|
-
|
|
117
|
-
guild = Async::Matrix::Bridge::Discord::DB::Guild.create(discord_id: "g_assoc", name: "Guild")
|
|
118
|
-
portal = Async::Matrix::Bridge::Discord::DB::Portal.create(
|
|
119
|
-
discord_id: "c_assoc", receiver: "", discord_guild_id: "g_assoc"
|
|
120
|
-
)
|
|
121
|
-
# Query through the db directly to avoid races with parallel tests
|
|
122
|
-
# that rebind the class-level dataset on Sequel models.
|
|
123
|
-
portal.discord_guild_id.should == "g_assoc"
|
|
124
|
-
linked_guild = db[:guilds].where(discord_id: portal.discord_guild_id).first
|
|
125
|
-
linked_guild[:discord_id].should == "g_assoc"
|
|
126
|
-
linked_portals = db[:portals].where(discord_guild_id: guild.discord_id).all
|
|
127
|
-
linked_portals.length.should == 1
|
|
128
|
-
db.disconnect
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
it "enforces unique mxid" do
|
|
132
|
-
db = setup_db
|
|
133
|
-
set_datasets(db)
|
|
134
|
-
|
|
135
|
-
Async::Matrix::Bridge::Discord::DB::Portal.create(discord_id: "a", receiver: "", mxid: "!r:x")
|
|
136
|
-
lambda {
|
|
137
|
-
Async::Matrix::Bridge::Discord::DB::Portal.create(discord_id: "b", receiver: "", mxid: "!r:x")
|
|
138
|
-
}.should.raise(Sequel::UniqueConstraintViolation)
|
|
139
|
-
db.disconnect
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
it "defaults encrypted to false" do
|
|
143
|
-
db = setup_db
|
|
144
|
-
set_datasets(db)
|
|
145
|
-
|
|
146
|
-
portal = Async::Matrix::Bridge::Discord::DB::Portal.create(discord_id: "e1", receiver: "")
|
|
147
|
-
portal.encrypted.should == false
|
|
148
|
-
db.disconnect
|
|
149
|
-
end
|
|
150
|
-
end
|
|
@@ -1,128 +0,0 @@
|
|
|
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_relative "schema"
|
|
7
|
-
|
|
8
|
-
module Async
|
|
9
|
-
module Matrix
|
|
10
|
-
module Bridge
|
|
11
|
-
module Discord
|
|
12
|
-
module DB
|
|
13
|
-
# Maps a Discord user to a Matrix ghost user.
|
|
14
|
-
#
|
|
15
|
-
# The ghost's Matrix ID is derived from the username template in the
|
|
16
|
-
# bridge config (e.g., @discord_123456:example.com). This model
|
|
17
|
-
# tracks the Discord user's profile info for syncing to Matrix.
|
|
18
|
-
#
|
|
19
|
-
# puppet = Puppet.create(discord_id: "123", username: "alice", name: "Alice")
|
|
20
|
-
# puppet.is_bot? # => false
|
|
21
|
-
# puppet.double_puppet? # => true (if custom_mxid is set)
|
|
22
|
-
#
|
|
23
|
-
class Puppet < Sequel::Model
|
|
24
|
-
unrestrict_primary_key
|
|
25
|
-
|
|
26
|
-
def validate
|
|
27
|
-
super
|
|
28
|
-
errors.add(:discord_id, "cannot be empty") if discord_id.nil? || discord_id.empty?
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Is this puppet configured for double puppeting?
|
|
32
|
-
def double_puppet?
|
|
33
|
-
!custom_mxid.nil? && !custom_mxid.empty?
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
__END__
|
|
43
|
-
describe "Async::Matrix::Bridge::Discord::DB::Puppet" do
|
|
44
|
-
def setup_db
|
|
45
|
-
db = Sequel.sqlite
|
|
46
|
-
Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
|
|
47
|
-
db
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it "creates and retrieves a puppet" do
|
|
51
|
-
db = setup_db
|
|
52
|
-
Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
|
|
53
|
-
|
|
54
|
-
puppet = Async::Matrix::Bridge::Discord::DB::Puppet.create(
|
|
55
|
-
discord_id: "123",
|
|
56
|
-
name: "Alice",
|
|
57
|
-
username: "alice",
|
|
58
|
-
global_name: "Alice Wonderland"
|
|
59
|
-
)
|
|
60
|
-
puppet.discord_id.should == "123"
|
|
61
|
-
puppet.name.should == "Alice"
|
|
62
|
-
puppet.username.should == "alice"
|
|
63
|
-
puppet.global_name.should == "Alice Wonderland"
|
|
64
|
-
db.disconnect
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
it "defaults boolean flags to false" do
|
|
68
|
-
db = setup_db
|
|
69
|
-
Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
|
|
70
|
-
|
|
71
|
-
puppet = Async::Matrix::Bridge::Discord::DB::Puppet.create(discord_id: "200")
|
|
72
|
-
puppet.is_bot.should == false
|
|
73
|
-
puppet.is_webhook.should == false
|
|
74
|
-
puppet.contact_info_set.should == false
|
|
75
|
-
db.disconnect
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
it "tracks bot and webhook puppets" do
|
|
79
|
-
db = setup_db
|
|
80
|
-
Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
|
|
81
|
-
|
|
82
|
-
bot = Async::Matrix::Bridge::Discord::DB::Puppet.create(discord_id: "300", is_bot: true)
|
|
83
|
-
bot.is_bot.should == true
|
|
84
|
-
|
|
85
|
-
webhook = Async::Matrix::Bridge::Discord::DB::Puppet.create(discord_id: "301", is_webhook: true)
|
|
86
|
-
webhook.is_webhook.should == true
|
|
87
|
-
db.disconnect
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
it "detects double puppet configuration" do
|
|
91
|
-
db = setup_db
|
|
92
|
-
Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
|
|
93
|
-
|
|
94
|
-
regular = Async::Matrix::Bridge::Discord::DB::Puppet.create(discord_id: "400")
|
|
95
|
-
regular.double_puppet?.should == false
|
|
96
|
-
|
|
97
|
-
double = Async::Matrix::Bridge::Discord::DB::Puppet.create(
|
|
98
|
-
discord_id: "401",
|
|
99
|
-
custom_mxid: "@alice:example.com",
|
|
100
|
-
access_token: "syt_token"
|
|
101
|
-
)
|
|
102
|
-
double.double_puppet?.should == true
|
|
103
|
-
double.access_token.should == "syt_token"
|
|
104
|
-
db.disconnect
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
it "validates discord_id is present" do
|
|
108
|
-
db = setup_db
|
|
109
|
-
Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
|
|
110
|
-
|
|
111
|
-
puppet = Async::Matrix::Bridge::Discord::DB::Puppet.new(discord_id: "")
|
|
112
|
-
puppet.valid?.should == false
|
|
113
|
-
puppet.errors[:discord_id].should.not.be.empty
|
|
114
|
-
db.disconnect
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
it "updates avatar info" do
|
|
118
|
-
db = setup_db
|
|
119
|
-
Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
|
|
120
|
-
|
|
121
|
-
puppet = Async::Matrix::Bridge::Discord::DB::Puppet.create(discord_id: "500")
|
|
122
|
-
puppet.update(avatar_hash: "abc123", avatar_url: "mxc://example.com/abc")
|
|
123
|
-
puppet.refresh
|
|
124
|
-
puppet.avatar_hash.should == "abc123"
|
|
125
|
-
puppet.avatar_url.should == "mxc://example.com/abc"
|
|
126
|
-
db.disconnect
|
|
127
|
-
end
|
|
128
|
-
end
|
|
@@ -1,165 +0,0 @@
|
|
|
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_relative "schema"
|
|
7
|
-
|
|
8
|
-
module Async
|
|
9
|
-
module Matrix
|
|
10
|
-
module Bridge
|
|
11
|
-
module Discord
|
|
12
|
-
module DB
|
|
13
|
-
# Maps a Discord reaction to a Matrix reaction event.
|
|
14
|
-
#
|
|
15
|
-
# Uniquely identified by (discord_message_id, discord_sender,
|
|
16
|
-
# discord_emoji_name) — one reaction per emoji per user per message.
|
|
17
|
-
#
|
|
18
|
-
# reaction = Reaction.create(
|
|
19
|
-
# discord_message_id: "msg1", discord_sender: "user1",
|
|
20
|
-
# discord_emoji_name: "\u{1f44d}", mxid: "$react1",
|
|
21
|
-
# discord_channel_id: "ch1"
|
|
22
|
-
# )
|
|
23
|
-
#
|
|
24
|
-
class Reaction < Sequel::Model
|
|
25
|
-
def validate
|
|
26
|
-
super
|
|
27
|
-
errors.add(:discord_message_id, "cannot be empty") if discord_message_id.nil? || discord_message_id.empty?
|
|
28
|
-
errors.add(:discord_sender, "cannot be empty") if discord_sender.nil? || discord_sender.empty?
|
|
29
|
-
errors.add(:discord_emoji_name, "cannot be empty") if discord_emoji_name.nil? || discord_emoji_name.empty?
|
|
30
|
-
errors.add(:mxid, "cannot be empty") if mxid.nil? || mxid.empty?
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Find all reactions on a Discord message.
|
|
34
|
-
def self.by_discord_message(message_id, channel_id, receiver = "")
|
|
35
|
-
where(
|
|
36
|
-
discord_message_id: message_id,
|
|
37
|
-
discord_channel_id: channel_id,
|
|
38
|
-
discord_channel_receiver: receiver
|
|
39
|
-
).all
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Find a specific reaction by its Matrix event ID.
|
|
43
|
-
def self.by_mxid(mxid)
|
|
44
|
-
first(mxid: mxid)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Find a specific reaction by Discord compound key.
|
|
48
|
-
def self.by_discord_key(message_id:, sender:, emoji_name:)
|
|
49
|
-
first(
|
|
50
|
-
discord_message_id: message_id,
|
|
51
|
-
discord_sender: sender,
|
|
52
|
-
discord_emoji_name: emoji_name
|
|
53
|
-
)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
__END__
|
|
63
|
-
describe "Async::Matrix::Bridge::Discord::DB::Reaction" do
|
|
64
|
-
def setup_db
|
|
65
|
-
db = Sequel.sqlite
|
|
66
|
-
Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
|
|
67
|
-
db
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it "creates and retrieves a reaction" do
|
|
71
|
-
db = setup_db
|
|
72
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
|
|
73
|
-
|
|
74
|
-
reaction = Async::Matrix::Bridge::Discord::DB::Reaction.create(
|
|
75
|
-
discord_message_id: "msg1",
|
|
76
|
-
discord_sender: "user1",
|
|
77
|
-
discord_emoji_name: "\u{1f44d}",
|
|
78
|
-
mxid: "$react1",
|
|
79
|
-
discord_channel_id: "ch1"
|
|
80
|
-
)
|
|
81
|
-
reaction.discord_emoji_name.should == "\u{1f44d}"
|
|
82
|
-
reaction.mxid.should == "$react1"
|
|
83
|
-
db.disconnect
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
it "finds reactions by discord message" do
|
|
87
|
-
db = setup_db
|
|
88
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
|
|
89
|
-
|
|
90
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.create(
|
|
91
|
-
discord_message_id: "msg2", discord_sender: "u1",
|
|
92
|
-
discord_emoji_name: "\u{1f44d}", mxid: "$r1", discord_channel_id: "ch1"
|
|
93
|
-
)
|
|
94
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.create(
|
|
95
|
-
discord_message_id: "msg2", discord_sender: "u2",
|
|
96
|
-
discord_emoji_name: "\u{2764}", mxid: "$r2", discord_channel_id: "ch1"
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
reactions = Async::Matrix::Bridge::Discord::DB::Reaction.by_discord_message("msg2", "ch1")
|
|
100
|
-
reactions.length.should == 2
|
|
101
|
-
db.disconnect
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
it "finds by Discord compound key" do
|
|
105
|
-
db = setup_db
|
|
106
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
|
|
107
|
-
|
|
108
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.create(
|
|
109
|
-
discord_message_id: "msg3", discord_sender: "u1",
|
|
110
|
-
discord_emoji_name: "custom_emoji", mxid: "$r3", discord_channel_id: "ch1"
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
found = Async::Matrix::Bridge::Discord::DB::Reaction.by_discord_key(
|
|
114
|
-
message_id: "msg3", sender: "u1", emoji_name: "custom_emoji"
|
|
115
|
-
)
|
|
116
|
-
found.should.not.be.nil
|
|
117
|
-
found.mxid.should == "$r3"
|
|
118
|
-
db.disconnect
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
it "finds by Matrix event ID" do
|
|
122
|
-
db = setup_db
|
|
123
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
|
|
124
|
-
|
|
125
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.create(
|
|
126
|
-
discord_message_id: "msg4", discord_sender: "u1",
|
|
127
|
-
discord_emoji_name: "x", mxid: "$find_react", discord_channel_id: "ch1"
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
found = Async::Matrix::Bridge::Discord::DB::Reaction.by_mxid("$find_react")
|
|
131
|
-
found.discord_message_id.should == "msg4"
|
|
132
|
-
db.disconnect
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
it "enforces unique reaction per user per emoji per message" do
|
|
136
|
-
db = setup_db
|
|
137
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
|
|
138
|
-
|
|
139
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.create(
|
|
140
|
-
discord_message_id: "msg5", discord_sender: "u1",
|
|
141
|
-
discord_emoji_name: "x", mxid: "$r5", discord_channel_id: "ch1"
|
|
142
|
-
)
|
|
143
|
-
lambda {
|
|
144
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.create(
|
|
145
|
-
discord_message_id: "msg5", discord_sender: "u1",
|
|
146
|
-
discord_emoji_name: "x", mxid: "$r6", discord_channel_id: "ch1"
|
|
147
|
-
)
|
|
148
|
-
}.should.raise(Sequel::UniqueConstraintViolation)
|
|
149
|
-
db.disconnect
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
it "validates required fields" do
|
|
153
|
-
db = setup_db
|
|
154
|
-
Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
|
|
155
|
-
|
|
156
|
-
r = Async::Matrix::Bridge::Discord::DB::Reaction.new(
|
|
157
|
-
discord_message_id: "", discord_sender: "",
|
|
158
|
-
discord_emoji_name: "", mxid: ""
|
|
159
|
-
)
|
|
160
|
-
r.valid?.should == false
|
|
161
|
-
r.errors[:discord_message_id].should.not.be.empty
|
|
162
|
-
r.errors[:mxid].should.not.be.empty
|
|
163
|
-
db.disconnect
|
|
164
|
-
end
|
|
165
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
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 "sequel"
|
|
7
|
-
|
|
8
|
-
# Sequel::Model requires a database connection at subclass definition time.
|
|
9
|
-
# Set a placeholder in-memory SQLite so models can be defined at load time.
|
|
10
|
-
# The real database is bound later via DB.connect / DB.bind_models.
|
|
11
|
-
#
|
|
12
|
-
# require_valid_table = false suppresses the column introspection that Sequel
|
|
13
|
-
# normally does on inheritance — critical because the placeholder DB has no
|
|
14
|
-
# tables.
|
|
15
|
-
unless Sequel::Model.instance_variable_get(:@db)
|
|
16
|
-
Sequel::Model.db = Sequel.sqlite
|
|
17
|
-
Sequel::Model.require_valid_table = false
|
|
18
|
-
end
|
|
@@ -1,112 +0,0 @@
|
|
|
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_relative "schema"
|
|
7
|
-
|
|
8
|
-
module Async
|
|
9
|
-
module Matrix
|
|
10
|
-
module Bridge
|
|
11
|
-
module Discord
|
|
12
|
-
module DB
|
|
13
|
-
# Maps a Matrix user to their Discord identity and session state.
|
|
14
|
-
#
|
|
15
|
-
# user = User.create(mxid: "@alice:example.com", discord_id: "123456789")
|
|
16
|
-
# user.discord_id # => "123456789"
|
|
17
|
-
# user.space_room # => "!space:example.com"
|
|
18
|
-
# user.portals # => [Portal, ...]
|
|
19
|
-
#
|
|
20
|
-
class User < Sequel::Model
|
|
21
|
-
unrestrict_primary_key
|
|
22
|
-
|
|
23
|
-
one_to_many :portals,
|
|
24
|
-
class: "Async::Matrix::Bridge::Discord::DB::Portal",
|
|
25
|
-
key: :receiver,
|
|
26
|
-
primary_key: :discord_id
|
|
27
|
-
|
|
28
|
-
def validate
|
|
29
|
-
super
|
|
30
|
-
errors.add(:mxid, "cannot be empty") if mxid.nil? || mxid.empty?
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
__END__
|
|
40
|
-
describe "Async::Matrix::Bridge::Discord::DB::User" do
|
|
41
|
-
def setup_db
|
|
42
|
-
db = Sequel.sqlite
|
|
43
|
-
Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
|
|
44
|
-
db
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
it "creates and retrieves a user by mxid" do
|
|
48
|
-
db = setup_db
|
|
49
|
-
Async::Matrix::Bridge::Discord::DB::User.dataset = db[:users]
|
|
50
|
-
|
|
51
|
-
user = Async::Matrix::Bridge::Discord::DB::User.create(
|
|
52
|
-
mxid: "@alice:example.com",
|
|
53
|
-
discord_id: "123456789"
|
|
54
|
-
)
|
|
55
|
-
user.mxid.should == "@alice:example.com"
|
|
56
|
-
user.discord_id.should == "123456789"
|
|
57
|
-
|
|
58
|
-
found = Async::Matrix::Bridge::Discord::DB::User["@alice:example.com"]
|
|
59
|
-
found.discord_id.should == "123456789"
|
|
60
|
-
db.disconnect
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
it "stores optional session fields" do
|
|
64
|
-
db = setup_db
|
|
65
|
-
Async::Matrix::Bridge::Discord::DB::User.dataset = db[:users]
|
|
66
|
-
|
|
67
|
-
user = Async::Matrix::Bridge::Discord::DB::User.create(
|
|
68
|
-
mxid: "@bob:example.com",
|
|
69
|
-
discord_token: "secret_token",
|
|
70
|
-
management_room: "!mgmt:example.com",
|
|
71
|
-
space_room: "!space:example.com",
|
|
72
|
-
dm_space_room: "!dm:example.com"
|
|
73
|
-
)
|
|
74
|
-
user.discord_token.should == "secret_token"
|
|
75
|
-
user.management_room.should == "!mgmt:example.com"
|
|
76
|
-
user.space_room.should == "!space:example.com"
|
|
77
|
-
user.dm_space_room.should == "!dm:example.com"
|
|
78
|
-
db.disconnect
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
it "enforces unique discord_id" do
|
|
82
|
-
db = setup_db
|
|
83
|
-
Async::Matrix::Bridge::Discord::DB::User.dataset = db[:users]
|
|
84
|
-
|
|
85
|
-
Async::Matrix::Bridge::Discord::DB::User.create(mxid: "@a:x", discord_id: "111")
|
|
86
|
-
lambda {
|
|
87
|
-
Async::Matrix::Bridge::Discord::DB::User.create(mxid: "@b:x", discord_id: "111")
|
|
88
|
-
}.should.raise(Sequel::UniqueConstraintViolation)
|
|
89
|
-
db.disconnect
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
it "validates mxid is present" do
|
|
93
|
-
db = setup_db
|
|
94
|
-
Async::Matrix::Bridge::Discord::DB::User.dataset = db[:users]
|
|
95
|
-
|
|
96
|
-
user = Async::Matrix::Bridge::Discord::DB::User.new(mxid: "")
|
|
97
|
-
user.valid?.should == false
|
|
98
|
-
user.errors[:mxid].should.not.be.empty
|
|
99
|
-
db.disconnect
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
it "updates discord token" do
|
|
103
|
-
db = setup_db
|
|
104
|
-
Async::Matrix::Bridge::Discord::DB::User.dataset = db[:users]
|
|
105
|
-
|
|
106
|
-
user = Async::Matrix::Bridge::Discord::DB::User.create(mxid: "@c:x")
|
|
107
|
-
user.update(discord_token: "new_token")
|
|
108
|
-
user.refresh
|
|
109
|
-
user.discord_token.should == "new_token"
|
|
110
|
-
db.disconnect
|
|
111
|
-
end
|
|
112
|
-
end
|