matrix-org / matrix-rust-sdk Goto Github PK
View Code? Open in Web Editor NEWMatrix Client-Server SDK for Rust
License: Apache License 2.0
Matrix Client-Server SDK for Rust
License: Apache License 2.0
When using the code from the autojoin bot, joining sometimes fails with the the error
Oct 08 22:36:31.192 ERROR matrix_sdk_base::models::room: add_member called on event of an already joined user
Autojoining room
thread 'main' panicked at 'Can't join room: RumaResponse(Http(Known(Error { kind: Forbidden, message: "You are not invited to this room.", status_code: 403 })))', matrix_sdk/examples/autojoin.rs:39:18
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
To Reproduce
Steps to reproduce the behavior:
@bot:tchncs.de
)@bot:tchncs.de
from another user (@user:matrix.org
) using Element Web@bot:tchncs.de
joins the room, sometimes it shows the above error message Can't join room: RumaResponse(Http(Known(Error { kind: Forbidden, message: "You are not invited to this room.", status_code: 403 })))
I used the homeservers mentioned above, but changed the user_id.
Expected behavior
I expect that I can join the room, after receiving the invite. Not sure, if this is the right repo to report this issue.
In order to test out the work on the ruma-events macros I've been doing for GSoC I got everything working in matrix-sdk (at least all the tests, I have not gotten to switching rumatui over to the new API). If you want to check it out see what you think here's the PR. I'll make some comments in places that were particularly tricky/odd or just where I wasn't sure about something.
If you have any ideas to make the diff better I wouldn't mind going through again so it's easier to read.
Could you provide some base examples, like how to login, send a message and fetch messages?
Logs show the access_token
of the user among other things that I'm not sure should be saved.
We currently have a simple example that prints out messages, it would be nice if we had a couple more:
!command
.should_upload_keys
subtraction overflowed on me.
match &self.uploaded_signed_key_count {
Some(count) => {
let max_keys = self.account.max_one_time_keys().await as u64;
let key_count = (max_keys / 2) - count.load(Ordering::Relaxed);
key_count > 0 // should this be an i64 instead of u64?
}
None => false,
}
Describe the bug
Ok so there really is something fishy with room keys, things almost always work immediately after logging in from a clean state, but even without restarting I sometimes loose keys one way or another (e.g. in a couple of channels I was typing alternatively from riot and the sdk client, seeing both messages, then now I no longer see messages from the riot device on the sdk client (but other way around works) -- earlier today I could no longer see messages from the sdk client on two different riot sessions (different users), but the sdk client could see both messages.
Once I get in this state I cannot seem to get out of it (e.g. restarting my client with restored session/store doesn't help), I need to log in again with a new device.
When things work restoring the session is fine.
To Reproduce
Steps to reproduce the behavior:
Additional context
I wanted to try a room rekey but handle_room_key_request
has a big // TODO handle room key requests here
-- not sure if that'll work? I can see a RoomKeyRequest(ToDeviceEvent{})
coming in but not sure what to do with it.
There's also the crypto-improvements
branch, should I try that first before going further? That looks like it would support it!
I unfortunately didn't have full traces on, so I'll just restart now with wonderdebug enabled and try to dig into that to see if I notice anything... Given how much things changed I'd think it would make sense to switch branch first and try to see if it happens again first, but letting you advise.
Apparently on my PC or Windows in general when using the json store the files created for rooms a) don't seem to end on "json" and b) are not being written to (probably because of a).
Currently you can only send requests that use error: ruma_client_api::Error (edit: Or ruma_client_api::r0::uiaa::UiaaResponse) in the endpoint declaration.
let request = get_server_version::Request::new();
let response = client.send(request).await.unwrap();
|
To Reproduce
`
use synapse_admin_api::v1::get_server_version;
let client = Client::new(homeserver_url).unwrap();
let request = get_server_version::Request::new();
let response: get_server_version::Response = client.send(request).await.unwrap();
leads to
let response = client.send(request).await.unwrap();
| ^^^^ the trait From<FromHttpResponseError<ruma_api::error::Void>>
is not implemented for matrix_sdk::Error
`
I saw this while poking around the spec and thought it'd be worth making a note of.
However we keep state we need to track the EventId's for certain events an example of this would be rich replies. We need a mapping or way to query the stored "event stream" to get a message from an event id.
Filters are nowadays a must to use, most importantly to use member lazy loading since it can be quite slow to handle tens of thousands members for a initial sync.
SyncSettings
needs to be expanded to take a filter, ruma-client-api already provides a nice abstraction over filter ids vs filter definitions since both can be used as a filter in the sync.
The state store used to be able to store some limited amount of events from the timeline, while the features seemed to be a bit flaky and due to the snapshot based state storage quite limited it was generally useful.
We should reintroduce this feature in the new state store.
Since the new state store keeps stuff out of memory we can without a bad conscience store the whole timeline in it. The state store trait should be expanded to allow this.
For some basic functionality, we'll need:
How gaps in the timeline should be handled needs to be figured out.
Each sync response will contain a slice of a timeline, the timeline might have been a continuation of the previous sync response, in which case the limited
parameter will be set to false
. If the timeline doesn't contain all events that happened between two syncs the limited
parameter will be set to true
and a gap exists in the timeline. More info can be found over here.
We'll likely will need to remember which event belongs to which slice and figure out how to merge slices when we request historic events using the /rooms/$room_id/messages
endpoint.
Our room_messages()
and similar methods that fetch events from the server should use events from the store if they are available and fill in from the server if not.
In order to deal with local echo could the user pass a Uuid
to AsyncClient::room_send
method or create an AsyncClient.transaction_id
field and a get method could be used to store the most recent transaction_id created and allow the user to retrieve for filtering local echo out?
Hi!
The addition of WASM pulls quite a bit of dependencies. At a cursory check it seems that is needed for write WASM bots.
What if I don't need WASM? Would it be possible to gate this behind a feature flag?
My understanding may be a bit off, so I'll be happy to receive more context.
And thanks for working on this project!
matrix_sdk::ClientConfig
currently does support only http proxys would be nice to add https proxy support as well.
In Fractal currently we use a wrapper to add HTTPS proxy support: https://gitlab.gnome.org/GNOME/fractal/-/blob/master/fractal-gtk/src/client.rs
Currently the user needs to track the RoomEvents themself on every sync, because the sdk doesn't store them (see #138).
It would be nice that a user can retrieve the events directly from the sdk.
I would suggest adding a method to Room
that returns the timeline. The method takes an start EventId
and an optional end EventId
or a limit that specifics how many events the user wants.
pub async fn get_timeline(&self,
start: EventId,
end: Option<EventId>,
limit: Option<u128>,
direction: Direction,
) -> StoreResult<Vec<AnyRoomEvent>> {
// Get events from store
}
I created to track the high-level issue separate form #138, which concerns the store itself.
Send + Sync
(#60)Instant
is using the instant crate but wasm-timer
provides an Instant
as well, so could get potentially changedfutures-locks
once a new version gets published (Extracted from: #31 (review)
I think the reason might be that matrix_sdk depends on matrix_sdk_base without passing on features, so base will always enable the default features
Is your feature request related to a problem? Please describe.
I'd like to handle "direct" rooms differently from channels - it would be trivial to handle these as IRC queries instead of channels and match my usage of IRC better.
Describe the solution you'd like
I get a "m.direct" AnyBasicEvent event on initial sync that ruma deserializes to DirectEvent<DirectEventContent>
, defined as follow:
pub struct DirectEventContent(pub BTreeMap<UserId, Vec<RoomId>>);
We could add a "direct" bool (or option bool) to our Room
struct, setting true for elements in that map when we see it. Or maybe a direct_target as option<UserId>
since we have the info?
I'm not exactly sure what happens to the direct
flag when you invite more people in them, let me know if you want me to check... That might lead to weird behaviours.
Describe alternatives you've considered
I can parse the event on my side & store the info on my per roomid hashmap; I'm not sure how useful the feature would be for other consumers of the lib.
Additional context
(If I get an ok-in-principle I'd be happy to try submitting a PR; not sure how long I'll laste but might as well get involved a bit more :P)
The proxy setting is mostly used to debug stuff with mitmproxy
, while this is useful it shouldn't be the default, we should expose a proxy argument for the CLI.
We are using strings for room ids and user ids in many places, this creates friction when making requests since they all expect the higher level ruma-identifier types.
This has been done because we otherwise have friction when we need to compare the room id or user id to a string (e.g. a state key to a user id).
Since https://github.com/ruma/ruma-identifiers/issues/14 will be worked one, we can switch to the higher level types and have easy comparison with strings.
I would like to run a job that syncs for e.g. 1 minute and then gracefully exits.
I could implement this functionality using sync_with_callback
and LoopCtrl
, but it's not obvious how to do it using the EventEmitter
API.
Is this even possible?
@martinetd was surprised by the long (re)compilation times: #104 (comment).
I've played around with this locally because I assumed this comes mostly down to the large amount of generated code in Ruma, but surprisingly the ruma-client hello_world
example recompiles in roughly 3 seconds on my machine while the matrix_sdk login
example takes almost 6 seconds on average.
Here's some things that could help that I haven't tried yet:
installing LLVMs linker, lld and using that by creating .cargo/config.toml
with
# adjust to whatever target you are building for
[target.x86_64-unknown-linux-gnu]
rustflags = [
"-C", "link-arg=-fuse-ld=lld",
]
building all dependencies in release mode, which may reduce the size of intermediary artifacts and thus improve relinking time, by adding the following to the Cargo.toml
of a project using matrix_sdk (this won't work in the workspace itself because it doesn't apply to workspace crates):
[profile.dev.package."*"]
opt-level = 2
(for more info see the Cargo Book)
Separately from that, I've been thinking about having feature flags in Ruma such that you could choose to only have request-sending + response-receiving generated rather than getting all of request-{sending,receiving} + response-{sending,receiving}.
The method is called add_event_emitter()
which seems to suggest that we can add an arbitrary amount of emitters, set_event_emitter()
may be a better name. Make sure to document this fact as well.
First for all, thank you for this useful crate! ๐
I tried to implement a simple echo bot to test-drive the matrix-sdk crate. However I cannot get past the login stage:
const DEFAULT_HOMESERVER_URL: &str = "https://matrix.org";
let homeserver_url = Url::parse(&DEFAULT_HOMESERVER_URL).unwrap();
let username = "myname";
let password = "mypassword";
let client_config = ClientConfig::default();
let client = Client::new_with_config(homeserver_url, client_config)?;
client
.login(username, password, None, Some("Bot"))
.await?;
Running this, I get the following error:
Error: can't parse the JSON response as a Matrix response
Is there any way to inspect the JSON response? Any other ideas how to debug this?
In order to get the messages feature turned on, I had to
matrix-sdk = "0.1.0"
matrix-sdk-base = "0.1.0" # in order to turn on the default features
in the Cargo.toml. Is this expected?
Describe the bug
The emoji verification example provided in the examples doesn't seem to be working. It sends the verification request which I tried accepting from element web. However, after that it is just stuck loading. I have attached a screenshot from element web. And the example script is just stuck with no output.
Please let me know if there's any additional information I can provide to help debug this issue.
To Reproduce
cargo build --examples
./target/debug/examples/emoji_verification https://matrix.org username password
Screenshots
If applicable, add screenshots to help explain your problem.
Desktop:
AFAICS, the design.md
file is not updated to match the latest changes to the SDK.
It should either get updated or removed. At the very least, it should include some kind of warning.
Describe the bug
When calling Sas::accept()
, Element Desktop and Element Android send an m.key.verification.cancel
event as soon as the accept call has returned. Element Android displays a message about a homeserver or a client potentially being compromised. The received cancellation event has the reason "Mismatched commitment".
To Reproduce
Steps to reproduce the behavior:
AnyToDeviceEvent::KeyVerificationStart
Client::get_verification
with the flow id received from the start eventSas::accept
on the Sas
return by the clientExpected behavior
Other device awaits confirmation and continues verification flow normally
Screenshots
Message displayed by element desktop:
Logs from element desktop:
Desktop (please complete the following information):
Additional context
Logs from element desktop indicate the cancellation event gets sent when the public keys are received.
Library users tend to forget to create folders for their paths and expect to relative paths to just work. Make sure this is as easy to use as possible.
We have 3 main layers going on here:
Those 3 are already quite nicely separated but to make the differing components reusable for other projects to consume we should separate them into crates and put them into a workspace.
For this we will need to go through the public API of the different layers and define and document the API better.
For clients that want matrix-sdk
to handle most of the event and room mutation, only wanting to be notified when something changes certain events a trait could be implemented that tracks all of the events and calls methods from the users struct if it impl's this trait.
/// And in `AsyncClient::sync` there could be a switch case that calls the corresponding method on
/// the `Box<dyn EventEmitter>
pub trait EventEmitter {
/// Event that alters the name or alias of this room.
fn on_room_name(&mut self, _: &Room, _: &RoomEvent) {}
/// Any event that alters the state of the room's members.
fn on_room_member(&mut self, _: &Room, _: &RoomEvent) {}
}
pub struct MyApp {
member: Vec<RoomMember>,
...
}
impl MyApp {
fn update_ui(&mut self, members: &HashMap<UserId, RoomMember>) {
self.members.push(members.get())
...
}
}
impl EventEmitter for MyApp {
fn on_room_member(&mut self, room: &Room, ev: &RoomEvent) {
// allowing something like this
match ev.membership_change() {
MembershipState::Joined => self.update_ui(&room.members))
}
}
// Methods for all relevant events .
}
async fn main() {
let client = AsyncClient::with_emitter(Box::new(MyApp));
loop {
draw_ui();
// calling sync will call the proper methods of MyApp
client.sync();
}
}
Describe the bug
I understand the examples aren't meant to be useable as is, but it's a starting point for someone taking a look at the project (hi! Please just tell me to GTFO if you can't be bothered or it's too early or whatever, it's much better than no answer -- and sorry it's a bit messy I just spent a couple of hours playing with this all)
$ ./emoji_verification https://matrix.somewhere.org test redactedpass
Error: Reqwest(reqwest::Error { kind: Request, url: "https://matrix.somewhere.org/_matrix/client/r0/login", source: hyper::Error(Connect, ConnectError("tcp connect error", Os { code: 111, kind: ConnectionRefused, message: "Connection refused" })) })
It would make sense to write something about the proxy there if possible?
(Note: is Reqwest, with a w, on purpose? I just noticed as I am filling this; that is sneaky...)
deserialize()
)$ RUST_BACKTRACE=1 ./emoji_verification https://matrix.somewhere.org test redactedpass
thread 'main' panicked at 'Can't deserialize to-device event: Error("unknown variant `m.qr_code.show.v1`, expected `m.sas.v1`", line: 1, column: 42)', matrix_sdk/examples/emoji_verification.rs:75:22
I guess that's some new element feature that would need to be implemented in matrix_sdk_crypto/src/verification/
? If I don't try to verify immediately but try to verify later by explicitely picking emoji I can get it to work but that was also a bit surprising
login()
call by restore_login()
if you have access token, device id and user id -- but with base_client
marked as private I don't see any way of accessing the first two? There's an user_id()
wrapper but nothing for the other two.Also, doing all this (always reusing the same config dir + same device id and doing emoji verification once), I can't get the command_bot
to see any encrypted message (but it works fine in an unencrypted room) -- I guess it should also listen to EncryptedEventContent? I don't find much about that, would have expected things to be transparently decrypted if keys etc are setup for the matrix-sdk.
Currently we have two structs for room members, User
and RoomMember
, they both have a events field and RoomMember
has a field that holds a User
struct.
It would probably make sense to remove the duplicate fields and just merge User
into RoomMember
.
Is your feature request related to a problem? Please describe.
Currently if I logout the only thing I can do is drop Client
, but that leaves any task running until it ends and it leaves the store in the disk.
Describe the solution you'd like
Add a .logout()
async method that handles this in the right way.
Describe alternatives you've considered
Doing that as the user of this library, but then every client would have to implement this functionality again and makes its usage more difficult.
Additional context
We need this in Fractal.
Users might get the impression that they need to manually manage sync tokens considering the sparse description around them.
Realistically, if the state store is used, everything is managed by the SDK, unless users have some special needs users don't have to care about sync tokens.
Matrix clients need to keep the room state in sync with the server, while it is possible to request the state again from the server (using full_state=true for a sync), fetching the state from the server is very taxing to the server.
The base client needs to be able to store its state and we need the ability to restore it once a client user either restores a session or logs in back again. Beside the state the sync token should be stored as well.
The store should be implemented as an async trait so library users can reimplement a store using their preferred database or storage solution. Serializing/deserializing the room structs and just saving the json to a directory as files might be a easy quick solution for a initial store implementation.
Later on we might want to add support to encrypt those files.
I understand that the matrix-sdk supports end-to-end encryption.
However it's not clear how this could work. A small example showing how to read messages from an encrypted room and how to send messages to an encrypted room would be extremely helpful. ๐
I have looked at the cross_signing_bootstrap example which sounds related, but I am not sure how to proceed.
The client has the room_send_attachment
method to upload, encrypt and send an attachment to a room. However, there doesn't seem to be a (documented) way to download and decrypt an attachment. Is this possible yet?
The AsyncClient for now only supports logging in, syncing and sending of messages.
Matrix has much more functionality and those should be added.
An incomplete list is here:
Some of these are simple enough, and don't need complex arguments (e.g. joining a room), but some of them will need some type of struct that implements a builder pattern if Ruma doesn't already provide this (creating a room).
These can be implemented one at a time.
Great crate! It's good to see a rust-skd.
I am messing around with a matrix command line client and have the login functionality down but have hit a bit of an issue with the RoomId
to room name or alias mapping.
I found the coresponding js code here but am unsure how exactly to duplicate this, here is my attempt.
I would love to help out if there is anything I could do, I realize it's early days.
I've had the same issue as another user in #matrix-rust-sdk
where I thought that Client::add_event_emitter
would keep track of the handlers in some sort of list until I looked at the implementation.
I think that the name set_event_emitter
would better convey the fact that there's only one handler at a time.
(Also I think that EventEmitter
is a poor name since it handles events, not emits them (other than application specific events, of course))
matrix_sdk_base::Room
has methods to get the members of a room, but they only return members stored in the StateStore
, but the stored list may be incomplete if lazy-loading is used.
The client developer can request the full list from the matrix server if they like, but I think we should do the request for them automatically.
I would suggest adding an boolean argument to Room::joined_user_ids()
, Room::active_members()
, Room.get_member()
, Room::joined_members()
for letting the user decide whether to automatically request the full member list, when not already stored, before return any result or return without making a request to the Matrix server.
The SDK in many places uses types defined in other crates which makes it unclear to the user that they are actually reexported by the SDK. I think it would be nice if the SDK would use the reexported types directly so that the docs clearly show which types the developer needs to include and informs the developer about the reexported types.
There are a couple of methods in the room.rs file that are setters but are named as getters.
We should stick to the naming convention described in the Rust style guide
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.