commit 1763e52ba461fdce729a3666e7a237d69ba018d5
Author: Phorge Migration <migration@localhost>
Date: Tue, 21 Jul 2026 17:32:41 +0200
Archive issues/PRs export for MTRNord/mrsbfh
Diffstat:
40 files changed, 7006 insertions(+), 0 deletions(-)
diff --git a/issues/1.json b/issues/1.json
@@ -0,0 +1,12 @@
+{
+ "number": 1,
+ "title": "Make compile in stable",
+ "state": "closed",
+ "author": "MTRNord",
+ "created_at": "2021-02-05T12:32:30Z",
+ "closed_at": "2021-03-20T17:58:01Z",
+ "labels": [],
+ "assignees": [],
+ "body": "basicly this https://github.com/MTRNord/mrsbfh/blob/main/mrsbfh-macros/src/lib.rs#L16-L45 and this https://github.com/MTRNord/mrsbfh/blob/main/mrsbfh-macros/src/lib.rs#L132 need to get changed.\r\n\r\nAlternative is to concat using format!",
+ "comments": []
+}
diff --git a/issues/1.md b/issues/1.md
@@ -0,0 +1,13 @@
+# #1 Make compile in stable
+
+- **State:** closed
+- **Author:** @MTRNord
+- **Created:** 2021-02-05T12:32:30Z
+- **Closed:** 2021-03-20T17:58:01Z
+
+---
+
+basicly this https://github.com/MTRNord/mrsbfh/blob/main/mrsbfh-macros/src/lib.rs#L16-L45 and this https://github.com/MTRNord/mrsbfh/blob/main/mrsbfh-macros/src/lib.rs#L132 need to get changed.
+
+Alternative is to concat using format!
+
diff --git a/issues/12.json b/issues/12.json
@@ -0,0 +1,57 @@
+{
+ "number": 12,
+ "title": "Add support for shared global state (similar to Actix's web::Data) ",
+ "state": "open",
+ "author": "donicrosby",
+ "created_at": "2022-03-12T06:12:42Z",
+ "closed_at": null,
+ "labels": [
+ "enhancement"
+ ],
+ "assignees": [
+ "MTRNord"
+ ],
+ "body": "I've been working on a bot that has some commands that requires a database connection in order to hold some state across restarts.\r\n\r\nWhile having the config being passed is fine for stateless bots. Anything that would require a long running connection it wouldn't be efficient to connect to the database every single message. It would be a huge improvement if there was a way to have a single state store that is stood up at startup that can be accessed by all of the commands.\r\n\r\nThe current config type must have serialize and deserialize which doesn't work for something like a DB without a huge hack of making a custom deserializer that stands up the DB connection to be stored in the config (if that's even possible)",
+ "comments": [
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T14:39:26Z",
+ "body": "Hm due to the macro currently used this seems like it is quite a lot harder to do as generics wouldnt work as wanted. I am currently trying to understand how actix solved this, as it seems non trivial."
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T14:40:05Z",
+ "body": "As `Data<Config<'a>>` in the command handler would conflict with `Data<T>` in the macro"
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T14:42:43Z",
+ "body": "To be exact this is the issue the macro currently faces:\r\n\r\n```\r\nerror[E0308]: mismatched types\r\n --> example-bot\\src\\commands\\mod.rs:7:1\r\n |\r\n7 | #[command_generate(bot_name = \"Example\", description = \"This bot prints hello!\")]\r\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `config::Config`, found type parameter `T`\r\n |\r\n = note: expected struct `mrsbfh::utils::Data<config::Config<'_>>`\r\n found struct `mrsbfh::utils::Data<T>`\r\n = note: this error originates in the attribute macro `command_generate` (in Nightly builds, run with -Z macro-backtrace for more info)\r\n```"
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T14:45:50Z",
+ "body": "It seems we would need something like this https://docs.rs/actix-web/latest/src/actix_web/handler.rs.html#124-153 But that seems restricting. I will check how axum does data passing. Maybe that has a nicer solution"
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T14:50:25Z",
+ "body": "It seems the best would be to use a trait approach, where commands implement a trait. Instead of using the proc macro."
+ },
+ {
+ "author": "donicrosby",
+ "created_at": "2022-03-12T15:06:10Z",
+ "body": "My only issue with that is it would break backwards compatibility, but we are still v0 \ud83d\ude01"
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T15:07:39Z",
+ "body": "> My only issue with that is it would break backwards compatibility, but we are still v0 \ud83d\ude01\r\n\r\nI dont think I can not break that while implementing this. But I will try to make it non breaking. Also I likely go more for the way axum does it then actix. As it seems easier to implement :)"
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T16:11:54Z",
+ "body": "Also I am trying to still keep the macro and use the traits internally. But the handlers probably will change and possibly also how you init it. But I am still in the experimentation phase and hope to later share a first working iteration. "
+ }
+ ]
+}
diff --git a/issues/12.md b/issues/12.md
@@ -0,0 +1,65 @@
+# #12 Add support for shared global state (similar to Actix's web::Data)
+
+- **State:** open
+- **Author:** @donicrosby
+- **Created:** 2022-03-12T06:12:42Z
+- **Labels:** enhancement
+- **Assignees:** @MTRNord
+
+---
+
+I've been working on a bot that has some commands that requires a database connection in order to hold some state across restarts.
+
+While having the config being passed is fine for stateless bots. Anything that would require a long running connection it wouldn't be efficient to connect to the database every single message. It would be a huge improvement if there was a way to have a single state store that is stood up at startup that can be accessed by all of the commands.
+
+The current config type must have serialize and deserialize which doesn't work for something like a DB without a huge hack of making a custom deserializer that stands up the DB connection to be stored in the config (if that's even possible)
+
+
+## Comments
+
+### @MTRNord — 2022-03-12T14:39:26Z
+
+Hm due to the macro currently used this seems like it is quite a lot harder to do as generics wouldnt work as wanted. I am currently trying to understand how actix solved this, as it seems non trivial.
+
+### @MTRNord — 2022-03-12T14:40:05Z
+
+As `Data<Config<'a>>` in the command handler would conflict with `Data<T>` in the macro
+
+### @MTRNord — 2022-03-12T14:42:43Z
+
+To be exact this is the issue the macro currently faces:
+
+```
+error[E0308]: mismatched types
+ --> example-bot\src\commands\mod.rs:7:1
+ |
+7 | #[command_generate(bot_name = "Example", description = "This bot prints hello!")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `config::Config`, found type parameter `T`
+ |
+ = note: expected struct `mrsbfh::utils::Data<config::Config<'_>>`
+ found struct `mrsbfh::utils::Data<T>`
+ = note: this error originates in the attribute macro `command_generate` (in Nightly builds, run with -Z macro-backtrace for more info)
+```
+
+### @MTRNord — 2022-03-12T14:45:50Z
+
+It seems we would need something like this https://docs.rs/actix-web/latest/src/actix_web/handler.rs.html#124-153 But that seems restricting. I will check how axum does data passing. Maybe that has a nicer solution
+
+### @MTRNord — 2022-03-12T14:50:25Z
+
+It seems the best would be to use a trait approach, where commands implement a trait. Instead of using the proc macro.
+
+### @donicrosby — 2022-03-12T15:06:10Z
+
+My only issue with that is it would break backwards compatibility, but we are still v0 😁
+
+### @MTRNord — 2022-03-12T15:07:39Z
+
+> My only issue with that is it would break backwards compatibility, but we are still v0 😁
+
+I dont think I can not break that while implementing this. But I will try to make it non breaking. Also I likely go more for the way axum does it then actix. As it seems easier to implement :)
+
+### @MTRNord — 2022-03-12T16:11:54Z
+
+Also I am trying to still keep the macro and use the traits internally. But the handlers probably will change and possibly also how you init it. But I am still in the experimentation phase and hope to later share a first working iteration.
+
diff --git a/issues/14.json b/issues/14.json
@@ -0,0 +1,14 @@
+{
+ "number": 14,
+ "title": "hygienic procedural macro - Keep track of spans",
+ "state": "open",
+ "author": "MTRNord",
+ "created_at": "2022-03-12T19:54:14Z",
+ "closed_at": null,
+ "labels": [
+ "enhancement"
+ ],
+ "assignees": [],
+ "body": "This is maybe a little harder here as some of the original types are going to get lost, or new ones are created out of thin air, but this should help debugging. Currently, all goes on to the argument itself instead of the code.\r\n\r\nhttps://docs.rs/quote/latest/quote/macro.quote_spanned.html",
+ "comments": []
+}
diff --git a/issues/14.md b/issues/14.md
@@ -0,0 +1,13 @@
+# #14 hygienic procedural macro - Keep track of spans
+
+- **State:** open
+- **Author:** @MTRNord
+- **Created:** 2022-03-12T19:54:14Z
+- **Labels:** enhancement
+
+---
+
+This is maybe a little harder here as some of the original types are going to get lost, or new ones are created out of thin air, but this should help debugging. Currently, all goes on to the argument itself instead of the code.
+
+https://docs.rs/quote/latest/quote/macro.quote_spanned.html
+
diff --git a/issues/15.json b/issues/15.json
@@ -0,0 +1,15 @@
+{
+ "number": 15,
+ "title": "Consider removing the custom event handlers",
+ "state": "open",
+ "author": "MTRNord",
+ "created_at": "2022-03-13T10:56:12Z",
+ "closed_at": null,
+ "labels": [
+ "enhancement",
+ "help wanted"
+ ],
+ "assignees": [],
+ "body": "Currently, the SDK has the same/similar type of event handlers like #13 already inbuilt. Therefor, it might make sense to remove ours.\r\n\r\nOpen Questions:\r\n\r\n- How do we still generate the Help?\r\n- Find a nice way to keep the util functions.\r\n- Write a migration guide",
+ "comments": []
+}
diff --git a/issues/15.md b/issues/15.md
@@ -0,0 +1,17 @@
+# #15 Consider removing the custom event handlers
+
+- **State:** open
+- **Author:** @MTRNord
+- **Created:** 2022-03-13T10:56:12Z
+- **Labels:** enhancement, help wanted
+
+---
+
+Currently, the SDK has the same/similar type of event handlers like #13 already inbuilt. Therefor, it might make sense to remove ours.
+
+Open Questions:
+
+- How do we still generate the Help?
+- Find a nice way to keep the util functions.
+- Write a migration guide
+
diff --git a/issues/16.json b/issues/16.json
@@ -0,0 +1,12 @@
+{
+ "number": 16,
+ "title": "Test the bot",
+ "state": "open",
+ "author": "MTRNord",
+ "created_at": "2022-03-13T12:45:32Z",
+ "closed_at": null,
+ "labels": [],
+ "assignees": [],
+ "body": "We may be able to test this using https://github.com/matrix-org/mx-tester",
+ "comments": []
+}
diff --git a/issues/16.md b/issues/16.md
@@ -0,0 +1,10 @@
+# #16 Test the bot
+
+- **State:** open
+- **Author:** @MTRNord
+- **Created:** 2022-03-13T12:45:32Z
+
+---
+
+We may be able to test this using https://github.com/matrix-org/mx-tester
+
diff --git a/issues/17.json b/issues/17.json
@@ -0,0 +1,22 @@
+{
+ "number": 17,
+ "title": "Matrix Room for Support/Dev chat",
+ "state": "open",
+ "author": "donicrosby",
+ "created_at": "2022-03-14T01:35:53Z",
+ "closed_at": null,
+ "labels": [
+ "enhancement"
+ ],
+ "assignees": [
+ "MTRNord"
+ ],
+ "body": "It would be nice to have a room for people who may need support or want to help contribute to come in and chat with other people using the project.",
+ "comments": [
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-14T13:56:00Z",
+ "body": "https://matrix.to/#/#mrsbfh:nordgedanken.dev now exists. Keeping the issue open as I need to add it to the readme still"
+ }
+ ]
+}
diff --git a/issues/17.md b/issues/17.md
@@ -0,0 +1,19 @@
+# #17 Matrix Room for Support/Dev chat
+
+- **State:** open
+- **Author:** @donicrosby
+- **Created:** 2022-03-14T01:35:53Z
+- **Labels:** enhancement
+- **Assignees:** @MTRNord
+
+---
+
+It would be nice to have a room for people who may need support or want to help contribute to come in and chat with other people using the project.
+
+
+## Comments
+
+### @MTRNord — 2022-03-14T13:56:00Z
+
+https://matrix.to/#/#mrsbfh:nordgedanken.dev now exists. Keeping the issue open as I need to add it to the readme still
+
diff --git a/issues/3.json b/issues/3.json
@@ -0,0 +1,22 @@
+{
+ "number": 3,
+ "title": "Add support for matrix-sdk built using rust-tls",
+ "state": "closed",
+ "author": "donicrosby",
+ "created_at": "2021-10-21T17:10:33Z",
+ "closed_at": "2021-11-04T19:38:10Z",
+ "labels": [
+ "enhancement"
+ ],
+ "assignees": [
+ "MTRNord"
+ ],
+ "body": "Not having to rely on openssl and it's build dependencies for building of a bot would allow for a pure rust implementations of the bot and possibly faster compilation times.",
+ "comments": [
+ {
+ "author": "MTRNord",
+ "created_at": "2021-11-04T19:39:22Z",
+ "body": "Released in version 0.1.2"
+ }
+ ]
+}
diff --git a/issues/3.md b/issues/3.md
@@ -0,0 +1,20 @@
+# #3 Add support for matrix-sdk built using rust-tls
+
+- **State:** closed
+- **Author:** @donicrosby
+- **Created:** 2021-10-21T17:10:33Z
+- **Closed:** 2021-11-04T19:38:10Z
+- **Labels:** enhancement
+- **Assignees:** @MTRNord
+
+---
+
+Not having to rely on openssl and it's build dependencies for building of a bot would allow for a pure rust implementations of the bot and possibly faster compilation times.
+
+
+## Comments
+
+### @MTRNord — 2021-11-04T19:39:22Z
+
+Released in version 0.1.2
+
diff --git a/issues/5.json b/issues/5.json
@@ -0,0 +1,22 @@
+{
+ "number": 5,
+ "title": "Add lazy_static or equivalent to all regex objects used internally",
+ "state": "closed",
+ "author": "donicrosby",
+ "created_at": "2021-11-08T20:18:07Z",
+ "closed_at": "2021-11-13T14:41:38Z",
+ "labels": [
+ "enhancement"
+ ],
+ "assignees": [
+ "MTRNord"
+ ],
+ "body": "In the regex library it recommends the use of the `lazy_static!` macro on regexs that are used frequently that are not changed during runtime.\r\n\r\nSee: https://docs.rs/regex/1.5.4/regex/#example-avoid-compiling-the-same-regex-in-a-loop\r\n\r\nWhile the regexs used in finding the command and removing whitespace aren't a huge load multiple commands coming in from a lot of rooms to a bot would mean those computations add up. Having those pre-compiled and ready to use would make the bot faster to respond to commands sent.",
+ "comments": [
+ {
+ "author": "donicrosby",
+ "created_at": "2021-11-10T03:37:46Z",
+ "body": "Added support for this in #6 "
+ }
+ ]
+}
diff --git a/issues/5.md b/issues/5.md
@@ -0,0 +1,24 @@
+# #5 Add lazy_static or equivalent to all regex objects used internally
+
+- **State:** closed
+- **Author:** @donicrosby
+- **Created:** 2021-11-08T20:18:07Z
+- **Closed:** 2021-11-13T14:41:38Z
+- **Labels:** enhancement
+- **Assignees:** @MTRNord
+
+---
+
+In the regex library it recommends the use of the `lazy_static!` macro on regexs that are used frequently that are not changed during runtime.
+
+See: https://docs.rs/regex/1.5.4/regex/#example-avoid-compiling-the-same-regex-in-a-loop
+
+While the regexs used in finding the command and removing whitespace aren't a huge load multiple commands coming in from a lot of rooms to a bot would mean those computations add up. Having those pre-compiled and ready to use would make the bot faster to respond to commands sent.
+
+
+## Comments
+
+### @donicrosby — 2021-11-10T03:37:46Z
+
+Added support for this in #6
+
diff --git a/issues/7.json b/issues/7.json
@@ -0,0 +1,16 @@
+{
+ "number": 7,
+ "title": "Check if we can do some unit tests for the logic",
+ "state": "open",
+ "author": "MTRNord",
+ "created_at": "2021-11-10T19:48:51Z",
+ "closed_at": null,
+ "labels": [
+ "enhancement"
+ ],
+ "assignees": [
+ "MTRNord"
+ ],
+ "body": "",
+ "comments": []
+}
diff --git a/issues/7.md b/issues/7.md
@@ -0,0 +1,12 @@
+# #7 Check if we can do some unit tests for the logic
+
+- **State:** open
+- **Author:** @MTRNord
+- **Created:** 2021-11-10T19:48:51Z
+- **Labels:** enhancement
+- **Assignees:** @MTRNord
+
+---
+
+_No description._
+
diff --git a/issues/8.json b/issues/8.json
@@ -0,0 +1,12 @@
+{
+ "number": 8,
+ "title": "Don't require default features for the matrix-sdk that the framework doesn't use",
+ "state": "closed",
+ "author": "donicrosby",
+ "created_at": "2021-11-11T00:04:07Z",
+ "closed_at": "2022-02-23T14:21:24Z",
+ "labels": [],
+ "assignees": [],
+ "body": "The framework does not use any special features of the matrix-sdk, most of the end customization is on the bot creator's part using the example bot as the basis for their bot and running logic.\r\n\r\nHaving the framework have the matrix-sdk as a dependency but with no default features would make updating the versions of the matrix-sdk less brittle. Without specifying any features for the matrix-sdk, the framework allows the end user to decide wither they use `rustls-tls` or the `sled_cryptostore` for example without the framework needing to keep track of the matrix-sdk's upstream features. This means that bumping versions only requires changes of the framework's code and not any of the cargo dependencies or having to add feature flags to the framework's `Cargo.toml` to enable or disable matrix-sdk's features.",
+ "comments": []
+}
diff --git a/issues/8.md b/issues/8.md
@@ -0,0 +1,13 @@
+# #8 Don't require default features for the matrix-sdk that the framework doesn't use
+
+- **State:** closed
+- **Author:** @donicrosby
+- **Created:** 2021-11-11T00:04:07Z
+- **Closed:** 2022-02-23T14:21:24Z
+
+---
+
+The framework does not use any special features of the matrix-sdk, most of the end customization is on the bot creator's part using the example bot as the basis for their bot and running logic.
+
+Having the framework have the matrix-sdk as a dependency but with no default features would make updating the versions of the matrix-sdk less brittle. Without specifying any features for the matrix-sdk, the framework allows the end user to decide wither they use `rustls-tls` or the `sled_cryptostore` for example without the framework needing to keep track of the matrix-sdk's upstream features. This means that bumping versions only requires changes of the framework's code and not any of the cargo dependencies or having to add feature flags to the framework's `Cargo.toml` to enable or disable matrix-sdk's features.
+
diff --git a/issues/9.json b/issues/9.json
@@ -0,0 +1,12 @@
+{
+ "number": 9,
+ "title": "Example Bot's Command Enum doesn't produce the same chat command as the command's help text",
+ "state": "closed",
+ "author": "donicrosby",
+ "created_at": "2021-11-18T01:27:04Z",
+ "closed_at": "2022-02-19T17:37:25Z",
+ "labels": [],
+ "assignees": [],
+ "body": "The Command block for the Enum that is used to generate the list of commands isn't correct:\r\nhttps://github.com/MTRNord/mrsbfh/blob/e073f78f27626d407ba54e3d32be9acaf8e8d9ce/example-bot/src/commands/mod.rs#L7-L10\r\n\r\nThe command that this generates is `!helloworld` (short command: `!hw`, is correct) rather than `!hello_world`. The enum value should be `Hello_World` (breaks rust's naming conventions, however)\r\n\r\nPedantic fix, but it would keep the docs and example correct so that future users don't have a tripping hazard.",
+ "comments": []
+}
diff --git a/issues/9.md b/issues/9.md
@@ -0,0 +1,16 @@
+# #9 Example Bot's Command Enum doesn't produce the same chat command as the command's help text
+
+- **State:** closed
+- **Author:** @donicrosby
+- **Created:** 2021-11-18T01:27:04Z
+- **Closed:** 2022-02-19T17:37:25Z
+
+---
+
+The Command block for the Enum that is used to generate the list of commands isn't correct:
+https://github.com/MTRNord/mrsbfh/blob/e073f78f27626d407ba54e3d32be9acaf8e8d9ce/example-bot/src/commands/mod.rs#L7-L10
+
+The command that this generates is `!helloworld` (short command: `!hw`, is correct) rather than `!hello_world`. The enum value should be `Hello_World` (breaks rust's naming conventions, however)
+
+Pedantic fix, but it would keep the docs and example correct so that future users don't have a tripping hazard.
+
diff --git a/pulls/10.diff b/pulls/10.diff
@@ -0,0 +1,39 @@
+diff --git a/example-bot/Cargo.toml b/example-bot/Cargo.toml
+index ab47333..e08fe93 100644
+--- a/example-bot/Cargo.toml
++++ b/example-bot/Cargo.toml
+@@ -12,6 +12,13 @@ repository = "https://github.com/MTRNord/mrsbfh"
+ [dependencies.matrix-sdk]
+ version = "0.4.1"
+
++# To create a bot that uses the Rust implementation of TLS use the default features minus native-tls
++# Ex:
++# [dependencies.matrix-sdk]
++# version = "0.4.1"
++# default_features = false
++# features = ["encryption", "sled_cryptostore", "sled_state_store", "require_auth_for_profile_requests", "rustls-tls"]
++
+ [dependencies]
+ mrsbfh = {version = "0.2.0", path = "../mrsbfh"}
+ serde = "1.0"
+diff --git a/mrsbfh/Cargo.toml b/mrsbfh/Cargo.toml
+index 188e14d..564b30b 100644
+--- a/mrsbfh/Cargo.toml
++++ b/mrsbfh/Cargo.toml
+@@ -13,7 +13,6 @@ readme = "../README.md"
+ [dependencies.matrix-sdk]
+ version = "0.4.1"
+ default_features = false
+-features = ["encryption", "sled_cryptostore", "sled_state_store", "require_auth_for_profile_requests"]
+
+ [dependencies]
+ url = "2.2.1"
+@@ -37,7 +36,5 @@ async-trait = "0.1"
+ lazy_static = "1"
+
+ [features]
+-default = ["macros", "native-tls"]
++default = ["macros"]
+ macros = ["mrsbfh-macros"]
+-rustls = ["matrix-sdk/rustls-tls"]
+-native-tls = ["matrix-sdk/native-tls"]
diff --git a/pulls/10.json b/pulls/10.json
@@ -0,0 +1,29 @@
+{
+ "number": 10,
+ "title": "Fix unneeded rust dependancies for mrsbfh",
+ "state": "merged",
+ "diff_file": "10.diff",
+ "author": "donicrosby",
+ "created_at": "2021-11-18T01:47:43Z",
+ "closed_at": "2022-02-23T14:21:24Z",
+ "merged_at": "2022-02-23T14:21:24Z",
+ "base_ref": "main",
+ "head_ref": "fix-unused-dependancies",
+ "labels": [],
+ "assignees": [],
+ "requested_reviewers": [],
+ "body": "Fixes #8 \r\n\r\nIt still requires the end user to pull in the matrix-sdk but doesn't force features that they may not want. If the framework starts reducing the amount of setup ( having the store and session paths be created via a function or macro in the framework) then for those functions to be enabled start requiring features of the matrix-sdk that would be needed",
+ "comments": [
+ {
+ "author": "MTRNord",
+ "created_at": "2022-02-23T14:21:21Z",
+ "body": "Sorry for the super late merge."
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-02-23T14:39:55Z",
+ "body": "Actually I am going to revert the ssl stuff as that breaks the releasing."
+ }
+ ],
+ "review_comments": []
+}
diff --git a/pulls/10.md b/pulls/10.md
@@ -0,0 +1,26 @@
+# PR #10 Fix unneeded rust dependancies for mrsbfh
+
+- **Status:** merged
+- **Author:** @donicrosby
+- **Created:** 2021-11-18T01:47:43Z
+- **Branch:** fix-unused-dependancies → main
+- **Merged:** 2022-02-23T14:21:24Z
+- **Diff:** [10.diff](./10.diff)
+
+---
+
+Fixes #8
+
+It still requires the end user to pull in the matrix-sdk but doesn't force features that they may not want. If the framework starts reducing the amount of setup ( having the store and session paths be created via a function or macro in the framework) then for those functions to be enabled start requiring features of the matrix-sdk that would be needed
+
+
+## Comments
+
+### @MTRNord — 2022-02-23T14:21:21Z
+
+Sorry for the super late merge.
+
+### @MTRNord — 2022-02-23T14:39:55Z
+
+Actually I am going to revert the ssl stuff as that breaks the releasing.
+
diff --git a/pulls/11.diff b/pulls/11.diff
@@ -0,0 +1,11 @@
+diff --git a/example-bot/src/commands/mod.rs b/example-bot/src/commands/mod.rs
+index fc4b0f1..2a36cea 100644
+--- a/example-bot/src/commands/mod.rs
++++ b/example-bot/src/commands/mod.rs
+@@ -6,5 +6,5 @@ pub mod hello_world;
+
+ #[command_generate(bot_name = "Example", description = "This bot prints hello!")]
+ enum Commands {
+- HelloWorld,
++ Hello_World,
+ }
diff --git a/pulls/11.json b/pulls/11.json
@@ -0,0 +1,18 @@
+{
+ "number": 11,
+ "title": "Fixes tripping hazard for the command that is generated after the proc macros",
+ "state": "merged",
+ "diff_file": "11.diff",
+ "author": "donicrosby",
+ "created_at": "2021-11-18T01:52:51Z",
+ "closed_at": "2022-02-19T17:37:25Z",
+ "merged_at": "2022-02-19T17:37:25Z",
+ "base_ref": "main",
+ "head_ref": "example-bot-command-enum-fix",
+ "labels": [],
+ "assignees": [],
+ "requested_reviewers": [],
+ "body": "Fixes: #9\r\n\r\nSmall change to get the example in line with what the help text for the command says, the command mangling macro may need to be updated so that the end command generated still follows Rust's naming convention ",
+ "comments": [],
+ "review_comments": []
+}
diff --git a/pulls/11.md b/pulls/11.md
@@ -0,0 +1,15 @@
+# PR #11 Fixes tripping hazard for the command that is generated after the proc macros
+
+- **Status:** merged
+- **Author:** @donicrosby
+- **Created:** 2021-11-18T01:52:51Z
+- **Branch:** example-bot-command-enum-fix → main
+- **Merged:** 2022-02-19T17:37:25Z
+- **Diff:** [11.diff](./11.diff)
+
+---
+
+Fixes: #9
+
+Small change to get the example in line with what the help text for the command says, the command mangling macro may need to be updated so that the end command generated still follows Rust's naming convention
+
diff --git a/pulls/13.diff b/pulls/13.diff
@@ -0,0 +1,5567 @@
+diff --git a/.gitignore b/.gitignore
+index 408b8a5..ff910e0 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -1,3 +1,4 @@
+ /target
+-Cargo.lock
+-.idea
+\ No newline at end of file
++.idea
++/example-bot/config.yml
++/example-bot/store
+\ No newline at end of file
+diff --git a/Cargo.lock b/Cargo.lock
+new file mode 100644
+index 0000000..44aafa0
+--- /dev/null
++++ b/Cargo.lock
+@@ -0,0 +1,3788 @@
++# This file is automatically @generated by Cargo.
++# It is not intended for manual editing.
++version = 3
++
++[[package]]
++name = "accessory"
++version = "1.3.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "87537f9ae7cfa78d5b8ebd1a1db25959f5e737126be4d8eb44a5452fc4b63cde"
++dependencies = [
++ "macroific",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "addr2line"
++version = "0.21.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
++dependencies = [
++ "gimli",
++]
++
++[[package]]
++name = "adler"
++version = "1.0.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
++
++[[package]]
++name = "aead"
++version = "0.5.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
++dependencies = [
++ "crypto-common",
++ "generic-array",
++]
++
++[[package]]
++name = "aes"
++version = "0.8.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
++dependencies = [
++ "cfg-if",
++ "cipher",
++ "cpufeatures",
++]
++
++[[package]]
++name = "ahash"
++version = "0.8.11"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
++dependencies = [
++ "cfg-if",
++ "once_cell",
++ "version_check",
++ "zerocopy",
++]
++
++[[package]]
++name = "aho-corasick"
++version = "1.1.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
++dependencies = [
++ "memchr",
++]
++
++[[package]]
++name = "allocator-api2"
++version = "0.2.18"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
++
++[[package]]
++name = "anstream"
++version = "0.6.14"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
++dependencies = [
++ "anstyle",
++ "anstyle-parse",
++ "anstyle-query",
++ "anstyle-wincon",
++ "colorchoice",
++ "is_terminal_polyfill",
++ "utf8parse",
++]
++
++[[package]]
++name = "anstyle"
++version = "1.0.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
++
++[[package]]
++name = "anstyle-parse"
++version = "0.2.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
++dependencies = [
++ "utf8parse",
++]
++
++[[package]]
++name = "anstyle-query"
++version = "1.1.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
++dependencies = [
++ "windows-sys 0.52.0",
++]
++
++[[package]]
++name = "anstyle-wincon"
++version = "3.0.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
++dependencies = [
++ "anstyle",
++ "windows-sys 0.52.0",
++]
++
++[[package]]
++name = "anyhow"
++version = "1.0.86"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
++
++[[package]]
++name = "anymap2"
++version = "0.13.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
++
++[[package]]
++name = "aquamarine"
++version = "0.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e"
++dependencies = [
++ "include_dir",
++ "itertools 0.10.5",
++ "proc-macro-error",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "arrayref"
++version = "0.3.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
++
++[[package]]
++name = "arrayvec"
++version = "0.7.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
++dependencies = [
++ "serde",
++]
++
++[[package]]
++name = "as_variant"
++version = "1.2.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f38fa22307249f86fb7fad906fcae77f2564caeb56d7209103c551cd1cf4798f"
++
++[[package]]
++name = "assign"
++version = "1.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002"
++
++[[package]]
++name = "async-channel"
++version = "2.3.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
++dependencies = [
++ "concurrent-queue",
++ "event-listener-strategy",
++ "futures-core",
++ "pin-project-lite",
++]
++
++[[package]]
++name = "async-stream"
++version = "0.3.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
++dependencies = [
++ "async-stream-impl",
++ "futures-core",
++ "pin-project-lite",
++]
++
++[[package]]
++name = "async-stream-impl"
++version = "0.3.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "async-trait"
++version = "0.1.80"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "autocfg"
++version = "1.3.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
++
++[[package]]
++name = "backoff"
++version = "0.4.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
++dependencies = [
++ "futures-core",
++ "getrandom",
++ "instant",
++ "pin-project-lite",
++ "rand",
++ "tokio",
++]
++
++[[package]]
++name = "backtrace"
++version = "0.3.71"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
++dependencies = [
++ "addr2line",
++ "cc",
++ "cfg-if",
++ "libc",
++ "miniz_oxide",
++ "object",
++ "rustc-demangle",
++]
++
++[[package]]
++name = "base64"
++version = "0.21.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
++
++[[package]]
++name = "base64ct"
++version = "1.6.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
++
++[[package]]
++name = "bitflags"
++version = "1.3.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
++
++[[package]]
++name = "bitflags"
++version = "2.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
++
++[[package]]
++name = "bitmaps"
++version = "3.2.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6"
++
++[[package]]
++name = "blake3"
++version = "1.5.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52"
++dependencies = [
++ "arrayref",
++ "arrayvec",
++ "cc",
++ "cfg-if",
++ "constant_time_eq",
++]
++
++[[package]]
++name = "block-buffer"
++version = "0.10.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
++dependencies = [
++ "generic-array",
++]
++
++[[package]]
++name = "block-padding"
++version = "0.3.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
++dependencies = [
++ "generic-array",
++]
++
++[[package]]
++name = "bs58"
++version = "0.5.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
++dependencies = [
++ "tinyvec",
++]
++
++[[package]]
++name = "bumpalo"
++version = "3.16.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
++
++[[package]]
++name = "byteorder"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
++
++[[package]]
++name = "bytes"
++version = "1.6.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
++
++[[package]]
++name = "bytesize"
++version = "1.3.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
++
++[[package]]
++name = "cbc"
++version = "0.1.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
++dependencies = [
++ "cipher",
++]
++
++[[package]]
++name = "cc"
++version = "1.0.99"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
++
++[[package]]
++name = "cfg-if"
++version = "1.0.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
++
++[[package]]
++name = "cfg-vis"
++version = "0.3.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c3a2c3bf5fc10fe2ca157564fbe08a4cb2b0a7d2ff3fe2f9683e65d5e7c7859c"
++dependencies = [
++ "proc-macro-crate 1.3.1",
++ "proc-macro2",
++ "quote",
++ "syn 1.0.109",
++]
++
++[[package]]
++name = "chacha20"
++version = "0.9.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
++dependencies = [
++ "cfg-if",
++ "cipher",
++ "cpufeatures",
++]
++
++[[package]]
++name = "chacha20poly1305"
++version = "0.10.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
++dependencies = [
++ "aead",
++ "chacha20",
++ "cipher",
++ "poly1305",
++ "zeroize",
++]
++
++[[package]]
++name = "cipher"
++version = "0.4.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
++dependencies = [
++ "crypto-common",
++ "inout",
++ "zeroize",
++]
++
++[[package]]
++name = "clap"
++version = "4.5.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
++dependencies = [
++ "clap_builder",
++ "clap_derive",
++]
++
++[[package]]
++name = "clap_builder"
++version = "4.5.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
++dependencies = [
++ "anstream",
++ "anstyle",
++ "clap_lex",
++ "strsim",
++]
++
++[[package]]
++name = "clap_derive"
++version = "4.5.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
++dependencies = [
++ "heck",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "clap_lex"
++version = "0.7.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
++
++[[package]]
++name = "color-eyre"
++version = "0.6.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
++dependencies = [
++ "backtrace",
++ "color-spantrace",
++ "eyre",
++ "indenter",
++ "once_cell",
++ "owo-colors",
++ "tracing-error",
++]
++
++[[package]]
++name = "color-spantrace"
++version = "0.2.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
++dependencies = [
++ "once_cell",
++ "owo-colors",
++ "tracing-core",
++ "tracing-error",
++]
++
++[[package]]
++name = "colorchoice"
++version = "1.0.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
++
++[[package]]
++name = "concurrent-queue"
++version = "2.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
++dependencies = [
++ "crossbeam-utils",
++]
++
++[[package]]
++name = "const-oid"
++version = "0.9.6"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
++
++[[package]]
++name = "const_panic"
++version = "0.2.8"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b"
++
++[[package]]
++name = "constant_time_eq"
++version = "0.3.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
++
++[[package]]
++name = "convert_case"
++version = "0.6.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
++dependencies = [
++ "unicode-segmentation",
++]
++
++[[package]]
++name = "core-foundation"
++version = "0.9.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
++dependencies = [
++ "core-foundation-sys",
++ "libc",
++]
++
++[[package]]
++name = "core-foundation-sys"
++version = "0.8.6"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
++
++[[package]]
++name = "cpufeatures"
++version = "0.2.12"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
++dependencies = [
++ "libc",
++]
++
++[[package]]
++name = "crossbeam-utils"
++version = "0.8.20"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
++
++[[package]]
++name = "crypto-common"
++version = "0.1.6"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
++dependencies = [
++ "generic-array",
++ "rand_core",
++ "typenum",
++]
++
++[[package]]
++name = "ctr"
++version = "0.9.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
++dependencies = [
++ "cipher",
++]
++
++[[package]]
++name = "curve25519-dalek"
++version = "4.1.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
++dependencies = [
++ "cfg-if",
++ "cpufeatures",
++ "curve25519-dalek-derive",
++ "digest",
++ "fiat-crypto",
++ "platforms",
++ "rustc_version",
++ "serde",
++ "subtle",
++ "zeroize",
++]
++
++[[package]]
++name = "curve25519-dalek-derive"
++version = "0.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "deadpool"
++version = "0.10.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490"
++dependencies = [
++ "async-trait",
++ "deadpool-runtime",
++ "num_cpus",
++ "tokio",
++]
++
++[[package]]
++name = "deadpool-runtime"
++version = "0.1.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b"
++dependencies = [
++ "tokio",
++]
++
++[[package]]
++name = "deadpool-sqlite"
++version = "0.7.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b8010e36e12f3be22543a5e478b4af20aeead9a700dd69581a5e050a070fc22c"
++dependencies = [
++ "deadpool",
++ "deadpool-sync",
++ "rusqlite",
++]
++
++[[package]]
++name = "deadpool-sync"
++version = "0.1.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "524bc3df0d57e98ecd022e21ba31166c2625e7d3e5bcc4510efaeeab4abcab04"
++dependencies = [
++ "deadpool-runtime",
++]
++
++[[package]]
++name = "delegate-display"
++version = "2.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "98a85201f233142ac819bbf6226e36d0b5e129a47bd325084674261c82d4cd66"
++dependencies = [
++ "macroific",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "der"
++version = "0.7.9"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
++dependencies = [
++ "const-oid",
++ "der_derive",
++ "flagset",
++ "zeroize",
++]
++
++[[package]]
++name = "der_derive"
++version = "0.7.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "digest"
++version = "0.10.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
++dependencies = [
++ "block-buffer",
++ "crypto-common",
++ "subtle",
++]
++
++[[package]]
++name = "displaydoc"
++version = "0.2.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "ed25519"
++version = "2.2.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
++dependencies = [
++ "pkcs8",
++ "serde",
++ "signature",
++]
++
++[[package]]
++name = "ed25519-dalek"
++version = "2.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
++dependencies = [
++ "curve25519-dalek",
++ "ed25519",
++ "rand_core",
++ "serde",
++ "sha2",
++ "subtle",
++ "zeroize",
++]
++
++[[package]]
++name = "either"
++version = "1.12.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
++
++[[package]]
++name = "encoding_rs"
++version = "0.8.34"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
++dependencies = [
++ "cfg-if",
++]
++
++[[package]]
++name = "equivalent"
++version = "1.0.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
++
++[[package]]
++name = "errno"
++version = "0.3.9"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
++dependencies = [
++ "libc",
++ "windows-sys 0.52.0",
++]
++
++[[package]]
++name = "event-listener"
++version = "4.0.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e"
++dependencies = [
++ "concurrent-queue",
++ "parking",
++ "pin-project-lite",
++]
++
++[[package]]
++name = "event-listener"
++version = "5.3.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
++dependencies = [
++ "concurrent-queue",
++ "parking",
++ "pin-project-lite",
++]
++
++[[package]]
++name = "event-listener-strategy"
++version = "0.5.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
++dependencies = [
++ "event-listener 5.3.1",
++ "pin-project-lite",
++]
++
++[[package]]
++name = "example-bot"
++version = "0.2.0"
++dependencies = [
++ "async-trait",
++ "clap",
++ "color-eyre",
++ "matrix-sdk",
++ "mrsbfh",
++ "regex",
++ "serde",
++ "thiserror",
++ "tokio",
++ "tracing",
++ "tracing-futures",
++ "tracing-subscriber",
++]
++
++[[package]]
++name = "eyeball"
++version = "0.8.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "42482893d982111055ce4b24234d6250396d3785767c6b04cedd84612a0b80fb"
++dependencies = [
++ "futures-core",
++ "readlock",
++ "tracing",
++]
++
++[[package]]
++name = "eyeball-im"
++version = "0.4.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "021fab29d9670be5867b16d56a95c29a12c3c1bb654e7d589010a028716d625d"
++dependencies = [
++ "futures-core",
++ "imbl",
++ "tokio",
++ "tokio-util",
++ "tracing",
++]
++
++[[package]]
++name = "eyre"
++version = "0.6.12"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
++dependencies = [
++ "indenter",
++ "once_cell",
++]
++
++[[package]]
++name = "fallible-iterator"
++version = "0.3.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
++
++[[package]]
++name = "fallible-streaming-iterator"
++version = "0.1.9"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
++
++[[package]]
++name = "fancy_constructor"
++version = "1.2.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f71f317e4af73b2f8f608fac190c52eac4b1879d2145df1db2fe48881ca69435"
++dependencies = [
++ "macroific",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "fastrand"
++version = "2.1.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
++
++[[package]]
++name = "fiat-crypto"
++version = "0.2.9"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
++
++[[package]]
++name = "flagset"
++version = "0.4.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1"
++
++[[package]]
++name = "fnv"
++version = "1.0.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
++
++[[package]]
++name = "foreign-types"
++version = "0.3.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
++dependencies = [
++ "foreign-types-shared",
++]
++
++[[package]]
++name = "foreign-types-shared"
++version = "0.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
++
++[[package]]
++name = "form_urlencoded"
++version = "1.2.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
++dependencies = [
++ "percent-encoding",
++]
++
++[[package]]
++name = "futures-channel"
++version = "0.3.30"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
++dependencies = [
++ "futures-core",
++]
++
++[[package]]
++name = "futures-core"
++version = "0.3.30"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
++
++[[package]]
++name = "futures-io"
++version = "0.3.30"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
++
++[[package]]
++name = "futures-macro"
++version = "0.3.30"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "futures-sink"
++version = "0.3.30"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
++
++[[package]]
++name = "futures-task"
++version = "0.3.30"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
++
++[[package]]
++name = "futures-util"
++version = "0.3.30"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
++dependencies = [
++ "futures-channel",
++ "futures-core",
++ "futures-io",
++ "futures-macro",
++ "futures-sink",
++ "futures-task",
++ "memchr",
++ "pin-project-lite",
++ "pin-utils",
++ "slab",
++]
++
++[[package]]
++name = "generic-array"
++version = "0.14.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
++dependencies = [
++ "typenum",
++ "version_check",
++]
++
++[[package]]
++name = "getopts"
++version = "0.2.21"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
++dependencies = [
++ "unicode-width",
++]
++
++[[package]]
++name = "getrandom"
++version = "0.2.15"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
++dependencies = [
++ "cfg-if",
++ "js-sys",
++ "libc",
++ "wasi",
++ "wasm-bindgen",
++]
++
++[[package]]
++name = "gimli"
++version = "0.28.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
++
++[[package]]
++name = "gloo-timers"
++version = "0.3.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
++dependencies = [
++ "futures-channel",
++ "futures-core",
++ "js-sys",
++ "wasm-bindgen",
++]
++
++[[package]]
++name = "gloo-utils"
++version = "0.2.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
++dependencies = [
++ "js-sys",
++ "serde",
++ "serde_json",
++ "wasm-bindgen",
++ "web-sys",
++]
++
++[[package]]
++name = "h2"
++version = "0.3.26"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
++dependencies = [
++ "bytes",
++ "fnv",
++ "futures-core",
++ "futures-sink",
++ "futures-util",
++ "http",
++ "indexmap",
++ "slab",
++ "tokio",
++ "tokio-util",
++ "tracing",
++]
++
++[[package]]
++name = "hashbrown"
++version = "0.14.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
++dependencies = [
++ "ahash",
++ "allocator-api2",
++]
++
++[[package]]
++name = "hashlink"
++version = "0.8.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
++dependencies = [
++ "hashbrown",
++]
++
++[[package]]
++name = "heck"
++version = "0.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
++
++[[package]]
++name = "hermit-abi"
++version = "0.3.9"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
++
++[[package]]
++name = "hkdf"
++version = "0.12.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
++dependencies = [
++ "hmac",
++]
++
++[[package]]
++name = "hmac"
++version = "0.12.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
++dependencies = [
++ "digest",
++]
++
++[[package]]
++name = "http"
++version = "0.2.12"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
++dependencies = [
++ "bytes",
++ "fnv",
++ "itoa",
++]
++
++[[package]]
++name = "http-body"
++version = "0.4.6"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
++dependencies = [
++ "bytes",
++ "http",
++ "pin-project-lite",
++]
++
++[[package]]
++name = "httparse"
++version = "1.9.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9f3935c160d00ac752e09787e6e6bfc26494c2183cc922f1bc678a60d4733bc2"
++
++[[package]]
++name = "httpdate"
++version = "1.0.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
++
++[[package]]
++name = "hyper"
++version = "0.14.29"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33"
++dependencies = [
++ "bytes",
++ "futures-channel",
++ "futures-core",
++ "futures-util",
++ "h2",
++ "http",
++ "http-body",
++ "httparse",
++ "httpdate",
++ "itoa",
++ "pin-project-lite",
++ "socket2",
++ "tokio",
++ "tower-service",
++ "tracing",
++ "want",
++]
++
++[[package]]
++name = "hyper-rustls"
++version = "0.24.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
++dependencies = [
++ "futures-util",
++ "http",
++ "hyper",
++ "rustls",
++ "tokio",
++ "tokio-rustls",
++]
++
++[[package]]
++name = "hyper-tls"
++version = "0.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
++dependencies = [
++ "bytes",
++ "hyper",
++ "native-tls",
++ "tokio",
++ "tokio-native-tls",
++]
++
++[[package]]
++name = "icu_collections"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
++dependencies = [
++ "displaydoc",
++ "yoke",
++ "zerofrom",
++ "zerovec",
++]
++
++[[package]]
++name = "icu_locid"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
++dependencies = [
++ "displaydoc",
++ "litemap",
++ "tinystr",
++ "writeable",
++ "zerovec",
++]
++
++[[package]]
++name = "icu_locid_transform"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
++dependencies = [
++ "displaydoc",
++ "icu_locid",
++ "icu_locid_transform_data",
++ "icu_provider",
++ "tinystr",
++ "zerovec",
++]
++
++[[package]]
++name = "icu_locid_transform_data"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
++
++[[package]]
++name = "icu_normalizer"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
++dependencies = [
++ "displaydoc",
++ "icu_collections",
++ "icu_normalizer_data",
++ "icu_properties",
++ "icu_provider",
++ "smallvec",
++ "utf16_iter",
++ "utf8_iter",
++ "write16",
++ "zerovec",
++]
++
++[[package]]
++name = "icu_normalizer_data"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
++
++[[package]]
++name = "icu_properties"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036"
++dependencies = [
++ "displaydoc",
++ "icu_collections",
++ "icu_locid_transform",
++ "icu_properties_data",
++ "icu_provider",
++ "tinystr",
++ "zerovec",
++]
++
++[[package]]
++name = "icu_properties_data"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
++
++[[package]]
++name = "icu_provider"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
++dependencies = [
++ "displaydoc",
++ "icu_locid",
++ "icu_provider_macros",
++ "stable_deref_trait",
++ "tinystr",
++ "writeable",
++ "yoke",
++ "zerofrom",
++ "zerovec",
++]
++
++[[package]]
++name = "icu_provider_macros"
++version = "1.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "idna"
++version = "1.0.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed"
++dependencies = [
++ "icu_normalizer",
++ "icu_properties",
++ "smallvec",
++ "utf8_iter",
++]
++
++[[package]]
++name = "imbl"
++version = "2.0.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "978d142c8028edf52095703af2fad11d6f611af1246685725d6b850634647085"
++dependencies = [
++ "bitmaps",
++ "imbl-sized-chunks",
++ "rand_core",
++ "rand_xoshiro",
++ "serde",
++ "version_check",
++]
++
++[[package]]
++name = "imbl-sized-chunks"
++version = "0.1.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "144006fb58ed787dcae3f54575ff4349755b00ccc99f4b4873860b654be1ed63"
++dependencies = [
++ "bitmaps",
++]
++
++[[package]]
++name = "include_dir"
++version = "0.7.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e"
++dependencies = [
++ "include_dir_macros",
++]
++
++[[package]]
++name = "include_dir_macros"
++version = "0.7.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f"
++dependencies = [
++ "proc-macro2",
++ "quote",
++]
++
++[[package]]
++name = "indenter"
++version = "0.3.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
++
++[[package]]
++name = "indexed_db_futures"
++version = "0.4.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6cc2083760572ee02385ab8b7c02c20925d2dd1f97a1a25a8737a238608f1152"
++dependencies = [
++ "accessory",
++ "cfg-if",
++ "delegate-display",
++ "fancy_constructor",
++ "js-sys",
++ "uuid",
++ "wasm-bindgen",
++ "wasm-bindgen-futures",
++ "web-sys",
++]
++
++[[package]]
++name = "indexmap"
++version = "2.2.6"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
++dependencies = [
++ "equivalent",
++ "hashbrown",
++ "serde",
++]
++
++[[package]]
++name = "inout"
++version = "0.1.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
++dependencies = [
++ "block-padding",
++ "generic-array",
++]
++
++[[package]]
++name = "instant"
++version = "0.1.13"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
++dependencies = [
++ "cfg-if",
++ "js-sys",
++ "wasm-bindgen",
++ "web-sys",
++]
++
++[[package]]
++name = "ipnet"
++version = "2.9.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
++
++[[package]]
++name = "is_terminal_polyfill"
++version = "1.70.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
++
++[[package]]
++name = "itertools"
++version = "0.10.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
++dependencies = [
++ "either",
++]
++
++[[package]]
++name = "itertools"
++version = "0.12.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
++dependencies = [
++ "either",
++]
++
++[[package]]
++name = "itoa"
++version = "1.0.11"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
++
++[[package]]
++name = "js-sys"
++version = "0.3.69"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
++dependencies = [
++ "wasm-bindgen",
++]
++
++[[package]]
++name = "js_int"
++version = "0.2.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d937f95470b270ce8b8950207715d71aa8e153c0d44c6684d59397ed4949160a"
++dependencies = [
++ "serde",
++]
++
++[[package]]
++name = "js_option"
++version = "0.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "68421373957a1593a767013698dbf206e2b221eefe97a44d98d18672ff38423c"
++dependencies = [
++ "serde",
++]
++
++[[package]]
++name = "konst"
++version = "0.3.9"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "50a0ba6de5f7af397afff922f22c149ff605c766cd3269cf6c1cd5e466dbe3b9"
++dependencies = [
++ "const_panic",
++ "konst_kernel",
++ "typewit",
++]
++
++[[package]]
++name = "konst_kernel"
++version = "0.3.9"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "be0a455a1719220fd6adf756088e1c69a85bf14b6a9e24537a5cc04f503edb2b"
++dependencies = [
++ "typewit",
++]
++
++[[package]]
++name = "lazy_static"
++version = "1.4.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
++
++[[package]]
++name = "libc"
++version = "0.2.155"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
++
++[[package]]
++name = "libsqlite3-sys"
++version = "0.27.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
++dependencies = [
++ "pkg-config",
++ "vcpkg",
++]
++
++[[package]]
++name = "libyml"
++version = "0.0.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "1303532258de1fbe263b4daaaba0e17e3d502b8de57b7845928b92398fb4afd1"
++
++[[package]]
++name = "linux-raw-sys"
++version = "0.4.14"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
++
++[[package]]
++name = "litemap"
++version = "0.7.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
++
++[[package]]
++name = "log"
++version = "0.4.21"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
++
++[[package]]
++name = "macroific"
++version = "1.3.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f05c00ac596022625d01047c421a0d97d7f09a18e429187b341c201cb631b9dd"
++dependencies = [
++ "macroific_attr_parse",
++ "macroific_core",
++ "macroific_macro",
++]
++
++[[package]]
++name = "macroific_attr_parse"
++version = "1.3.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67"
++dependencies = [
++ "cfg-if",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "macroific_core"
++version = "1.0.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "macroific_macro"
++version = "1.1.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b0c9853143cbed7f1e41dc39fee95f9b361bec65c8dc2a01bf609be01b61f5ae"
++dependencies = [
++ "macroific_attr_parse",
++ "macroific_core",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "maplit"
++version = "1.0.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
++
++[[package]]
++name = "matrix-pickle"
++version = "0.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d7fd26463ce5d86b8d9bb9c4142d453198ba22fb91bd46d3c9f144ae699d821d"
++dependencies = [
++ "matrix-pickle-derive",
++ "thiserror",
++]
++
++[[package]]
++name = "matrix-pickle-derive"
++version = "0.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "93779aa78d39c2fe34746287b10a866192cf8af1b81767fff76bd64099acc0f5"
++dependencies = [
++ "proc-macro-crate 2.0.2",
++ "proc-macro-error",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "matrix-sdk"
++version = "0.7.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "336687e5fc8b33661a31681e988a67e9a3090c7fb1a8323a7f71eeaabad642ec"
++dependencies = [
++ "anymap2",
++ "aquamarine",
++ "as_variant",
++ "async-channel",
++ "async-stream",
++ "async-trait",
++ "backoff",
++ "bytes",
++ "bytesize",
++ "cfg-vis",
++ "event-listener 4.0.3",
++ "eyeball",
++ "eyeball-im",
++ "futures-core",
++ "futures-util",
++ "gloo-timers",
++ "http",
++ "imbl",
++ "indexmap",
++ "matrix-sdk-base",
++ "matrix-sdk-common",
++ "matrix-sdk-indexeddb",
++ "matrix-sdk-sqlite",
++ "mime",
++ "mime2ext",
++ "reqwest",
++ "ruma",
++ "serde",
++ "serde_html_form",
++ "serde_json",
++ "tempfile",
++ "thiserror",
++ "tokio",
++ "tokio-stream",
++ "tokio-util",
++ "tracing",
++ "url",
++ "urlencoding",
++ "zeroize",
++]
++
++[[package]]
++name = "matrix-sdk-base"
++version = "0.7.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "00891954d0826a94f1d130f46cbca64176003a234c1be5d9d282970d31cf0c87"
++dependencies = [
++ "as_variant",
++ "async-trait",
++ "bitflags 2.5.0",
++ "eyeball",
++ "eyeball-im",
++ "futures-util",
++ "matrix-sdk-common",
++ "matrix-sdk-crypto",
++ "matrix-sdk-store-encryption",
++ "once_cell",
++ "ruma",
++ "serde",
++ "serde_json",
++ "thiserror",
++ "tokio",
++ "tracing",
++]
++
++[[package]]
++name = "matrix-sdk-common"
++version = "0.7.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bb365a626ab6f6c6a2422cfe2565522f19accb06706c6d04bca8f0f71df29c9f"
++dependencies = [
++ "async-trait",
++ "futures-core",
++ "futures-util",
++ "gloo-timers",
++ "instant",
++ "ruma",
++ "serde",
++ "serde_json",
++ "thiserror",
++ "tokio",
++ "tracing",
++ "tracing-subscriber",
++ "wasm-bindgen",
++ "wasm-bindgen-futures",
++ "web-sys",
++]
++
++[[package]]
++name = "matrix-sdk-crypto"
++version = "0.7.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f69cfc1741fece1d920f0ce56632f14103c7d15f5d95482626d318d3907da81c"
++dependencies = [
++ "aes",
++ "as_variant",
++ "async-trait",
++ "bs58",
++ "byteorder",
++ "cbc",
++ "cfg-if",
++ "ctr",
++ "eyeball",
++ "futures-core",
++ "futures-util",
++ "hkdf",
++ "hmac",
++ "itertools 0.12.1",
++ "matrix-sdk-common",
++ "pbkdf2",
++ "rand",
++ "rmp-serde",
++ "ruma",
++ "serde",
++ "serde_json",
++ "sha2",
++ "subtle",
++ "thiserror",
++ "tokio",
++ "tokio-stream",
++ "tracing",
++ "ulid",
++ "vodozemac",
++ "zeroize",
++]
++
++[[package]]
++name = "matrix-sdk-indexeddb"
++version = "0.7.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ad388005c5d4ed2ff38f405d52aa7fa606f4e1ab51baf5f2504721124ed4a58b"
++dependencies = [
++ "anyhow",
++ "async-trait",
++ "base64",
++ "getrandom",
++ "gloo-utils",
++ "indexed_db_futures",
++ "js-sys",
++ "matrix-sdk-base",
++ "matrix-sdk-crypto",
++ "matrix-sdk-store-encryption",
++ "ruma",
++ "serde",
++ "serde-wasm-bindgen",
++ "serde_json",
++ "thiserror",
++ "tokio",
++ "tracing",
++ "wasm-bindgen",
++ "web-sys",
++]
++
++[[package]]
++name = "matrix-sdk-sqlite"
++version = "0.7.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "20bd36bc5fa7ecd93516b242ba27466196d52b4a8743d85dd883a67bd6db11dc"
++dependencies = [
++ "async-trait",
++ "deadpool-sqlite",
++ "itertools 0.12.1",
++ "matrix-sdk-base",
++ "matrix-sdk-crypto",
++ "matrix-sdk-store-encryption",
++ "rmp-serde",
++ "ruma",
++ "rusqlite",
++ "serde",
++ "serde_json",
++ "thiserror",
++ "tokio",
++ "tracing",
++ "vodozemac",
++]
++
++[[package]]
++name = "matrix-sdk-store-encryption"
++version = "0.7.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6a7e3162e9f982a4c57ab46df01a4775f697dec8899738bf62d7e97b63faa61c"
++dependencies = [
++ "blake3",
++ "chacha20poly1305",
++ "displaydoc",
++ "getrandom",
++ "hmac",
++ "pbkdf2",
++ "rand",
++ "rmp-serde",
++ "serde",
++ "serde_json",
++ "sha2",
++ "thiserror",
++ "zeroize",
++]
++
++[[package]]
++name = "memchr"
++version = "2.7.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
++
++[[package]]
++name = "mime"
++version = "0.3.17"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
++
++[[package]]
++name = "mime2ext"
++version = "0.1.52"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a1a85a5069ebd40e64b1985773cc81addbe9d90d7ecf60e7b5475a57ad584c70"
++
++[[package]]
++name = "miniz_oxide"
++version = "0.7.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
++dependencies = [
++ "adler",
++]
++
++[[package]]
++name = "mio"
++version = "0.8.11"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
++dependencies = [
++ "libc",
++ "wasi",
++ "windows-sys 0.48.0",
++]
++
++[[package]]
++name = "mrsbfh"
++version = "0.4.1"
++dependencies = [
++ "lazy_static",
++ "matrix-sdk",
++ "mrsbfh-macros",
++ "pulldown-cmark",
++ "regex",
++ "serde",
++ "serde_json",
++ "serde_yml",
++ "thiserror",
++ "tokio",
++ "tracing",
++ "url",
++]
++
++[[package]]
++name = "mrsbfh-macros"
++version = "0.4.1"
++dependencies = [
++ "convert_case",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "native-tls"
++version = "0.2.12"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
++dependencies = [
++ "libc",
++ "log",
++ "openssl",
++ "openssl-probe",
++ "openssl-sys",
++ "schannel",
++ "security-framework",
++ "security-framework-sys",
++ "tempfile",
++]
++
++[[package]]
++name = "nu-ansi-term"
++version = "0.46.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
++dependencies = [
++ "overload",
++ "winapi",
++]
++
++[[package]]
++name = "num-traits"
++version = "0.2.19"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
++dependencies = [
++ "autocfg",
++]
++
++[[package]]
++name = "num_cpus"
++version = "1.16.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
++dependencies = [
++ "hermit-abi",
++ "libc",
++]
++
++[[package]]
++name = "object"
++version = "0.32.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
++dependencies = [
++ "memchr",
++]
++
++[[package]]
++name = "once_cell"
++version = "1.19.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
++
++[[package]]
++name = "opaque-debug"
++version = "0.3.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
++
++[[package]]
++name = "openssl"
++version = "0.10.64"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
++dependencies = [
++ "bitflags 2.5.0",
++ "cfg-if",
++ "foreign-types",
++ "libc",
++ "once_cell",
++ "openssl-macros",
++ "openssl-sys",
++]
++
++[[package]]
++name = "openssl-macros"
++version = "0.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "openssl-probe"
++version = "0.1.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
++
++[[package]]
++name = "openssl-sys"
++version = "0.9.102"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
++dependencies = [
++ "cc",
++ "libc",
++ "pkg-config",
++ "vcpkg",
++]
++
++[[package]]
++name = "overload"
++version = "0.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
++
++[[package]]
++name = "owo-colors"
++version = "3.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
++
++[[package]]
++name = "parking"
++version = "2.2.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
++
++[[package]]
++name = "paste"
++version = "1.0.15"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
++
++[[package]]
++name = "pbkdf2"
++version = "0.12.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
++dependencies = [
++ "digest",
++ "hmac",
++]
++
++[[package]]
++name = "percent-encoding"
++version = "2.3.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
++
++[[package]]
++name = "pin-project"
++version = "1.1.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
++dependencies = [
++ "pin-project-internal",
++]
++
++[[package]]
++name = "pin-project-internal"
++version = "1.1.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "pin-project-lite"
++version = "0.2.14"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
++
++[[package]]
++name = "pin-utils"
++version = "0.1.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
++
++[[package]]
++name = "pkcs7"
++version = "0.4.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d79178be066405e0602bf3035946edef6b11b3f9dde46dfe5f8bfd7dea4b77e7"
++dependencies = [
++ "der",
++ "spki",
++ "x509-cert",
++]
++
++[[package]]
++name = "pkcs8"
++version = "0.10.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
++dependencies = [
++ "der",
++ "spki",
++]
++
++[[package]]
++name = "pkg-config"
++version = "0.3.30"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
++
++[[package]]
++name = "platforms"
++version = "3.4.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7"
++
++[[package]]
++name = "poly1305"
++version = "0.8.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
++dependencies = [
++ "cpufeatures",
++ "opaque-debug",
++ "universal-hash",
++]
++
++[[package]]
++name = "ppv-lite86"
++version = "0.2.17"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
++
++[[package]]
++name = "proc-macro-crate"
++version = "1.3.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
++dependencies = [
++ "once_cell",
++ "toml_edit 0.19.15",
++]
++
++[[package]]
++name = "proc-macro-crate"
++version = "2.0.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24"
++dependencies = [
++ "toml_datetime",
++ "toml_edit 0.20.2",
++]
++
++[[package]]
++name = "proc-macro-error"
++version = "1.0.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
++dependencies = [
++ "proc-macro-error-attr",
++ "proc-macro2",
++ "quote",
++ "syn 1.0.109",
++ "version_check",
++]
++
++[[package]]
++name = "proc-macro-error-attr"
++version = "1.0.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "version_check",
++]
++
++[[package]]
++name = "proc-macro2"
++version = "1.0.85"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
++dependencies = [
++ "unicode-ident",
++]
++
++[[package]]
++name = "prost"
++version = "0.12.6"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
++dependencies = [
++ "bytes",
++ "prost-derive",
++]
++
++[[package]]
++name = "prost-derive"
++version = "0.12.6"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
++dependencies = [
++ "anyhow",
++ "itertools 0.12.1",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "pulldown-cmark"
++version = "0.11.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8746739f11d39ce5ad5c2520a9b75285310dbfe78c541ccf832d38615765aec0"
++dependencies = [
++ "bitflags 2.5.0",
++ "getopts",
++ "memchr",
++ "pulldown-cmark-escape",
++ "unicase",
++]
++
++[[package]]
++name = "pulldown-cmark-escape"
++version = "0.11.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
++
++[[package]]
++name = "quote"
++version = "1.0.36"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
++dependencies = [
++ "proc-macro2",
++]
++
++[[package]]
++name = "rand"
++version = "0.8.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
++dependencies = [
++ "libc",
++ "rand_chacha",
++ "rand_core",
++]
++
++[[package]]
++name = "rand_chacha"
++version = "0.3.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
++dependencies = [
++ "ppv-lite86",
++ "rand_core",
++]
++
++[[package]]
++name = "rand_core"
++version = "0.6.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
++dependencies = [
++ "getrandom",
++]
++
++[[package]]
++name = "rand_xoshiro"
++version = "0.6.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
++dependencies = [
++ "rand_core",
++]
++
++[[package]]
++name = "readlock"
++version = "0.1.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d7b323e7196daa571c8584de958be19e92941c41f845776fe06babfe8fa280a2"
++
++[[package]]
++name = "regex"
++version = "1.10.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
++dependencies = [
++ "aho-corasick",
++ "memchr",
++ "regex-automata",
++ "regex-syntax",
++]
++
++[[package]]
++name = "regex-automata"
++version = "0.4.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
++dependencies = [
++ "aho-corasick",
++ "memchr",
++ "regex-syntax",
++]
++
++[[package]]
++name = "regex-syntax"
++version = "0.8.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
++
++[[package]]
++name = "reqwest"
++version = "0.11.27"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
++dependencies = [
++ "base64",
++ "bytes",
++ "encoding_rs",
++ "futures-core",
++ "futures-util",
++ "h2",
++ "http",
++ "http-body",
++ "hyper",
++ "hyper-rustls",
++ "hyper-tls",
++ "ipnet",
++ "js-sys",
++ "log",
++ "mime",
++ "native-tls",
++ "once_cell",
++ "percent-encoding",
++ "pin-project-lite",
++ "rustls",
++ "rustls-pemfile",
++ "serde",
++ "serde_json",
++ "serde_urlencoded",
++ "sync_wrapper",
++ "system-configuration",
++ "tokio",
++ "tokio-native-tls",
++ "tokio-rustls",
++ "tokio-util",
++ "tower-service",
++ "url",
++ "wasm-bindgen",
++ "wasm-bindgen-futures",
++ "wasm-streams",
++ "web-sys",
++ "webpki-roots",
++ "winreg",
++]
++
++[[package]]
++name = "ring"
++version = "0.17.8"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
++dependencies = [
++ "cc",
++ "cfg-if",
++ "getrandom",
++ "libc",
++ "spin",
++ "untrusted",
++ "windows-sys 0.52.0",
++]
++
++[[package]]
++name = "rmp"
++version = "0.8.14"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
++dependencies = [
++ "byteorder",
++ "num-traits",
++ "paste",
++]
++
++[[package]]
++name = "rmp-serde"
++version = "1.3.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
++dependencies = [
++ "byteorder",
++ "rmp",
++ "serde",
++]
++
++[[package]]
++name = "ruma"
++version = "0.9.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "2779c38df072964c63476259d9300efb07d0d1a7178c6469893636ce0c547a36"
++dependencies = [
++ "assign",
++ "js_int",
++ "js_option",
++ "ruma-client-api",
++ "ruma-common",
++ "ruma-events",
++ "ruma-federation-api",
++]
++
++[[package]]
++name = "ruma-client-api"
++version = "0.17.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "641837258fa214a70823477514954ef0f5d3bc6ae8e1d5d85081856a33103386"
++dependencies = [
++ "assign",
++ "bytes",
++ "http",
++ "js_int",
++ "js_option",
++ "maplit",
++ "ruma-common",
++ "ruma-events",
++ "serde",
++ "serde_html_form",
++ "serde_json",
++]
++
++[[package]]
++name = "ruma-common"
++version = "0.12.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3bca4c33c50e47b4cdceeac71bdef0c04153b0e29aa992d9030ec14a62323e85"
++dependencies = [
++ "as_variant",
++ "base64",
++ "bytes",
++ "form_urlencoded",
++ "getrandom",
++ "http",
++ "indexmap",
++ "js-sys",
++ "js_int",
++ "konst",
++ "percent-encoding",
++ "rand",
++ "regex",
++ "ruma-identifiers-validation",
++ "ruma-macros",
++ "serde",
++ "serde_html_form",
++ "serde_json",
++ "thiserror",
++ "tracing",
++ "url",
++ "uuid",
++ "wildmatch",
++]
++
++[[package]]
++name = "ruma-events"
++version = "0.27.11"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d20a52770e5a9fb30b7a1c14ba8b3dcf76dadc01674e58e40094f78e6bd5e3f1"
++dependencies = [
++ "as_variant",
++ "indexmap",
++ "js_int",
++ "js_option",
++ "percent-encoding",
++ "regex",
++ "ruma-common",
++ "ruma-identifiers-validation",
++ "ruma-macros",
++ "serde",
++ "serde_json",
++ "thiserror",
++ "tracing",
++ "url",
++ "wildmatch",
++]
++
++[[package]]
++name = "ruma-federation-api"
++version = "0.8.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e1901c1f27bc327652d58af2a130c73acef3198abeccd24cee97f7267fdf3fe7"
++dependencies = [
++ "js_int",
++ "ruma-common",
++ "ruma-events",
++ "serde",
++ "serde_json",
++]
++
++[[package]]
++name = "ruma-identifiers-validation"
++version = "0.9.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9fa38974f5901ed4e00e10aec57b9ad3b4d6d6c1a1ae683c51b88700b9f4ffba"
++dependencies = [
++ "js_int",
++ "thiserror",
++]
++
++[[package]]
++name = "ruma-macros"
++version = "0.12.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0280534a4b3e34416f883285fac4f9c408cd0b737890ae66f3e7a7056d14be80"
++dependencies = [
++ "once_cell",
++ "proc-macro-crate 2.0.2",
++ "proc-macro2",
++ "quote",
++ "ruma-identifiers-validation",
++ "serde",
++ "syn 2.0.66",
++ "toml",
++]
++
++[[package]]
++name = "rusqlite"
++version = "0.30.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d"
++dependencies = [
++ "bitflags 2.5.0",
++ "fallible-iterator",
++ "fallible-streaming-iterator",
++ "hashlink",
++ "libsqlite3-sys",
++ "smallvec",
++]
++
++[[package]]
++name = "rustc-demangle"
++version = "0.1.24"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
++
++[[package]]
++name = "rustc_version"
++version = "0.4.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
++dependencies = [
++ "semver",
++]
++
++[[package]]
++name = "rustix"
++version = "0.38.34"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
++dependencies = [
++ "bitflags 2.5.0",
++ "errno",
++ "libc",
++ "linux-raw-sys",
++ "windows-sys 0.52.0",
++]
++
++[[package]]
++name = "rustls"
++version = "0.21.12"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
++dependencies = [
++ "log",
++ "ring",
++ "rustls-webpki",
++ "sct",
++]
++
++[[package]]
++name = "rustls-pemfile"
++version = "1.0.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
++dependencies = [
++ "base64",
++]
++
++[[package]]
++name = "rustls-webpki"
++version = "0.101.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
++dependencies = [
++ "ring",
++ "untrusted",
++]
++
++[[package]]
++name = "ryu"
++version = "1.0.18"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
++
++[[package]]
++name = "schannel"
++version = "0.1.23"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
++dependencies = [
++ "windows-sys 0.52.0",
++]
++
++[[package]]
++name = "sct"
++version = "0.7.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
++dependencies = [
++ "ring",
++ "untrusted",
++]
++
++[[package]]
++name = "security-framework"
++version = "2.11.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
++dependencies = [
++ "bitflags 2.5.0",
++ "core-foundation",
++ "core-foundation-sys",
++ "libc",
++ "security-framework-sys",
++]
++
++[[package]]
++name = "security-framework-sys"
++version = "2.11.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7"
++dependencies = [
++ "core-foundation-sys",
++ "libc",
++]
++
++[[package]]
++name = "semver"
++version = "1.0.23"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
++
++[[package]]
++name = "serde"
++version = "1.0.203"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
++dependencies = [
++ "serde_derive",
++]
++
++[[package]]
++name = "serde-wasm-bindgen"
++version = "0.6.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
++dependencies = [
++ "js-sys",
++ "serde",
++ "wasm-bindgen",
++]
++
++[[package]]
++name = "serde_bytes"
++version = "0.11.14"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734"
++dependencies = [
++ "serde",
++]
++
++[[package]]
++name = "serde_derive"
++version = "1.0.203"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "serde_html_form"
++version = "0.2.6"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5"
++dependencies = [
++ "form_urlencoded",
++ "indexmap",
++ "itoa",
++ "ryu",
++ "serde",
++]
++
++[[package]]
++name = "serde_json"
++version = "1.0.117"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
++dependencies = [
++ "itoa",
++ "ryu",
++ "serde",
++]
++
++[[package]]
++name = "serde_spanned"
++version = "0.6.6"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
++dependencies = [
++ "serde",
++]
++
++[[package]]
++name = "serde_urlencoded"
++version = "0.7.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
++dependencies = [
++ "form_urlencoded",
++ "itoa",
++ "ryu",
++ "serde",
++]
++
++[[package]]
++name = "serde_yml"
++version = "0.0.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c83ad088d28e2f6640b8e6b7d64bbca3cafb7a0442fe53673ac9a8b1f5fe621b"
++dependencies = [
++ "indexmap",
++ "itoa",
++ "libyml",
++ "log",
++ "memchr",
++ "ryu",
++ "serde",
++ "serde_json",
++]
++
++[[package]]
++name = "sha2"
++version = "0.10.8"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
++dependencies = [
++ "cfg-if",
++ "cpufeatures",
++ "digest",
++]
++
++[[package]]
++name = "sharded-slab"
++version = "0.1.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
++dependencies = [
++ "lazy_static",
++]
++
++[[package]]
++name = "signature"
++version = "2.2.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
++dependencies = [
++ "rand_core",
++]
++
++[[package]]
++name = "slab"
++version = "0.4.9"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
++dependencies = [
++ "autocfg",
++]
++
++[[package]]
++name = "smallvec"
++version = "1.13.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
++
++[[package]]
++name = "socket2"
++version = "0.5.7"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
++dependencies = [
++ "libc",
++ "windows-sys 0.52.0",
++]
++
++[[package]]
++name = "spin"
++version = "0.9.8"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
++
++[[package]]
++name = "spki"
++version = "0.7.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
++dependencies = [
++ "base64ct",
++ "der",
++]
++
++[[package]]
++name = "stable_deref_trait"
++version = "1.2.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
++
++[[package]]
++name = "strsim"
++version = "0.11.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
++
++[[package]]
++name = "subtle"
++version = "2.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
++
++[[package]]
++name = "syn"
++version = "1.0.109"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "unicode-ident",
++]
++
++[[package]]
++name = "syn"
++version = "2.0.66"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "unicode-ident",
++]
++
++[[package]]
++name = "sync_wrapper"
++version = "0.1.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
++
++[[package]]
++name = "synstructure"
++version = "0.13.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "system-configuration"
++version = "0.5.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
++dependencies = [
++ "bitflags 1.3.2",
++ "core-foundation",
++ "system-configuration-sys",
++]
++
++[[package]]
++name = "system-configuration-sys"
++version = "0.5.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
++dependencies = [
++ "core-foundation-sys",
++ "libc",
++]
++
++[[package]]
++name = "tempfile"
++version = "3.10.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
++dependencies = [
++ "cfg-if",
++ "fastrand",
++ "rustix",
++ "windows-sys 0.52.0",
++]
++
++[[package]]
++name = "thiserror"
++version = "1.0.61"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
++dependencies = [
++ "thiserror-impl",
++]
++
++[[package]]
++name = "thiserror-impl"
++version = "1.0.61"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "thread_local"
++version = "1.1.8"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
++dependencies = [
++ "cfg-if",
++ "once_cell",
++]
++
++[[package]]
++name = "tinystr"
++version = "0.7.6"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
++dependencies = [
++ "displaydoc",
++ "zerovec",
++]
++
++[[package]]
++name = "tinyvec"
++version = "1.6.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
++dependencies = [
++ "tinyvec_macros",
++]
++
++[[package]]
++name = "tinyvec_macros"
++version = "0.1.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
++
++[[package]]
++name = "tokio"
++version = "1.38.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
++dependencies = [
++ "backtrace",
++ "bytes",
++ "libc",
++ "mio",
++ "num_cpus",
++ "pin-project-lite",
++ "socket2",
++ "tokio-macros",
++ "windows-sys 0.48.0",
++]
++
++[[package]]
++name = "tokio-macros"
++version = "2.3.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "tokio-native-tls"
++version = "0.3.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
++dependencies = [
++ "native-tls",
++ "tokio",
++]
++
++[[package]]
++name = "tokio-rustls"
++version = "0.24.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
++dependencies = [
++ "rustls",
++ "tokio",
++]
++
++[[package]]
++name = "tokio-stream"
++version = "0.1.15"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
++dependencies = [
++ "futures-core",
++ "pin-project-lite",
++ "tokio",
++ "tokio-util",
++]
++
++[[package]]
++name = "tokio-util"
++version = "0.7.11"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
++dependencies = [
++ "bytes",
++ "futures-core",
++ "futures-sink",
++ "pin-project-lite",
++ "tokio",
++]
++
++[[package]]
++name = "toml"
++version = "0.8.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
++dependencies = [
++ "serde",
++ "serde_spanned",
++ "toml_datetime",
++ "toml_edit 0.20.2",
++]
++
++[[package]]
++name = "toml_datetime"
++version = "0.6.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
++dependencies = [
++ "serde",
++]
++
++[[package]]
++name = "toml_edit"
++version = "0.19.15"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
++dependencies = [
++ "indexmap",
++ "toml_datetime",
++ "winnow",
++]
++
++[[package]]
++name = "toml_edit"
++version = "0.20.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
++dependencies = [
++ "indexmap",
++ "serde",
++ "serde_spanned",
++ "toml_datetime",
++ "winnow",
++]
++
++[[package]]
++name = "tower-service"
++version = "0.3.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
++
++[[package]]
++name = "tracing"
++version = "0.1.40"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
++dependencies = [
++ "pin-project-lite",
++ "tracing-attributes",
++ "tracing-core",
++]
++
++[[package]]
++name = "tracing-attributes"
++version = "0.1.27"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "tracing-core"
++version = "0.1.32"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
++dependencies = [
++ "once_cell",
++ "valuable",
++]
++
++[[package]]
++name = "tracing-error"
++version = "0.2.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
++dependencies = [
++ "tracing",
++ "tracing-subscriber",
++]
++
++[[package]]
++name = "tracing-futures"
++version = "0.2.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
++dependencies = [
++ "pin-project",
++ "tracing",
++]
++
++[[package]]
++name = "tracing-log"
++version = "0.2.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
++dependencies = [
++ "log",
++ "once_cell",
++ "tracing-core",
++]
++
++[[package]]
++name = "tracing-subscriber"
++version = "0.3.18"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
++dependencies = [
++ "nu-ansi-term",
++ "sharded-slab",
++ "smallvec",
++ "thread_local",
++ "tracing-core",
++ "tracing-log",
++]
++
++[[package]]
++name = "try-lock"
++version = "0.2.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
++
++[[package]]
++name = "typenum"
++version = "1.17.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
++
++[[package]]
++name = "typewit"
++version = "1.9.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c6fb9ae6a3cafaf0a5d14c2302ca525f9ae8e07a0f0e6949de88d882c37a6e24"
++dependencies = [
++ "typewit_proc_macros",
++]
++
++[[package]]
++name = "typewit_proc_macros"
++version = "1.8.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6"
++
++[[package]]
++name = "ulid"
++version = "1.1.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259"
++dependencies = [
++ "getrandom",
++ "rand",
++ "web-time",
++]
++
++[[package]]
++name = "unicase"
++version = "2.7.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
++dependencies = [
++ "version_check",
++]
++
++[[package]]
++name = "unicode-ident"
++version = "1.0.12"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
++
++[[package]]
++name = "unicode-segmentation"
++version = "1.11.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
++
++[[package]]
++name = "unicode-width"
++version = "0.1.13"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
++
++[[package]]
++name = "universal-hash"
++version = "0.5.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
++dependencies = [
++ "crypto-common",
++ "subtle",
++]
++
++[[package]]
++name = "untrusted"
++version = "0.9.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
++
++[[package]]
++name = "url"
++version = "2.5.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56"
++dependencies = [
++ "form_urlencoded",
++ "idna",
++ "percent-encoding",
++]
++
++[[package]]
++name = "urlencoding"
++version = "2.1.3"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
++
++[[package]]
++name = "utf16_iter"
++version = "1.0.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
++
++[[package]]
++name = "utf8_iter"
++version = "1.0.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
++
++[[package]]
++name = "utf8parse"
++version = "0.2.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
++
++[[package]]
++name = "uuid"
++version = "1.6.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
++dependencies = [
++ "getrandom",
++ "wasm-bindgen",
++]
++
++[[package]]
++name = "valuable"
++version = "0.1.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
++
++[[package]]
++name = "vcpkg"
++version = "0.2.15"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
++
++[[package]]
++name = "version_check"
++version = "0.9.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
++
++[[package]]
++name = "vodozemac"
++version = "0.5.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "2790dffeecc522299d72d9a855c43adb0c23ba1dc1112d79a651fdf3beb2a356"
++dependencies = [
++ "aes",
++ "arrayvec",
++ "base64",
++ "cbc",
++ "curve25519-dalek",
++ "ed25519-dalek",
++ "getrandom",
++ "hkdf",
++ "hmac",
++ "matrix-pickle",
++ "pkcs7",
++ "prost",
++ "rand",
++ "serde",
++ "serde_bytes",
++ "serde_json",
++ "sha2",
++ "subtle",
++ "thiserror",
++ "x25519-dalek",
++ "zeroize",
++]
++
++[[package]]
++name = "want"
++version = "0.3.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
++dependencies = [
++ "try-lock",
++]
++
++[[package]]
++name = "wasi"
++version = "0.11.0+wasi-snapshot-preview1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
++
++[[package]]
++name = "wasm-bindgen"
++version = "0.2.92"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
++dependencies = [
++ "cfg-if",
++ "wasm-bindgen-macro",
++]
++
++[[package]]
++name = "wasm-bindgen-backend"
++version = "0.2.92"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
++dependencies = [
++ "bumpalo",
++ "log",
++ "once_cell",
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++ "wasm-bindgen-shared",
++]
++
++[[package]]
++name = "wasm-bindgen-futures"
++version = "0.4.42"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
++dependencies = [
++ "cfg-if",
++ "js-sys",
++ "wasm-bindgen",
++ "web-sys",
++]
++
++[[package]]
++name = "wasm-bindgen-macro"
++version = "0.2.92"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
++dependencies = [
++ "quote",
++ "wasm-bindgen-macro-support",
++]
++
++[[package]]
++name = "wasm-bindgen-macro-support"
++version = "0.2.92"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++ "wasm-bindgen-backend",
++ "wasm-bindgen-shared",
++]
++
++[[package]]
++name = "wasm-bindgen-shared"
++version = "0.2.92"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
++
++[[package]]
++name = "wasm-streams"
++version = "0.4.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
++dependencies = [
++ "futures-util",
++ "js-sys",
++ "wasm-bindgen",
++ "wasm-bindgen-futures",
++ "web-sys",
++]
++
++[[package]]
++name = "web-sys"
++version = "0.3.69"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
++dependencies = [
++ "js-sys",
++ "wasm-bindgen",
++]
++
++[[package]]
++name = "web-time"
++version = "1.1.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
++dependencies = [
++ "js-sys",
++ "wasm-bindgen",
++]
++
++[[package]]
++name = "webpki-roots"
++version = "0.25.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
++
++[[package]]
++name = "wildmatch"
++version = "2.3.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "3928939971918220fed093266b809d1ee4ec6c1a2d72692ff6876898f3b16c19"
++
++[[package]]
++name = "winapi"
++version = "0.3.9"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
++dependencies = [
++ "winapi-i686-pc-windows-gnu",
++ "winapi-x86_64-pc-windows-gnu",
++]
++
++[[package]]
++name = "winapi-i686-pc-windows-gnu"
++version = "0.4.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
++
++[[package]]
++name = "winapi-x86_64-pc-windows-gnu"
++version = "0.4.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
++
++[[package]]
++name = "windows-sys"
++version = "0.48.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
++dependencies = [
++ "windows-targets 0.48.5",
++]
++
++[[package]]
++name = "windows-sys"
++version = "0.52.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
++dependencies = [
++ "windows-targets 0.52.5",
++]
++
++[[package]]
++name = "windows-targets"
++version = "0.48.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
++dependencies = [
++ "windows_aarch64_gnullvm 0.48.5",
++ "windows_aarch64_msvc 0.48.5",
++ "windows_i686_gnu 0.48.5",
++ "windows_i686_msvc 0.48.5",
++ "windows_x86_64_gnu 0.48.5",
++ "windows_x86_64_gnullvm 0.48.5",
++ "windows_x86_64_msvc 0.48.5",
++]
++
++[[package]]
++name = "windows-targets"
++version = "0.52.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
++dependencies = [
++ "windows_aarch64_gnullvm 0.52.5",
++ "windows_aarch64_msvc 0.52.5",
++ "windows_i686_gnu 0.52.5",
++ "windows_i686_gnullvm",
++ "windows_i686_msvc 0.52.5",
++ "windows_x86_64_gnu 0.52.5",
++ "windows_x86_64_gnullvm 0.52.5",
++ "windows_x86_64_msvc 0.52.5",
++]
++
++[[package]]
++name = "windows_aarch64_gnullvm"
++version = "0.48.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
++
++[[package]]
++name = "windows_aarch64_gnullvm"
++version = "0.52.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
++
++[[package]]
++name = "windows_aarch64_msvc"
++version = "0.48.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
++
++[[package]]
++name = "windows_aarch64_msvc"
++version = "0.52.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
++
++[[package]]
++name = "windows_i686_gnu"
++version = "0.48.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
++
++[[package]]
++name = "windows_i686_gnu"
++version = "0.52.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
++
++[[package]]
++name = "windows_i686_gnullvm"
++version = "0.52.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
++
++[[package]]
++name = "windows_i686_msvc"
++version = "0.48.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
++
++[[package]]
++name = "windows_i686_msvc"
++version = "0.52.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
++
++[[package]]
++name = "windows_x86_64_gnu"
++version = "0.48.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
++
++[[package]]
++name = "windows_x86_64_gnu"
++version = "0.52.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
++
++[[package]]
++name = "windows_x86_64_gnullvm"
++version = "0.48.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
++
++[[package]]
++name = "windows_x86_64_gnullvm"
++version = "0.52.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
++
++[[package]]
++name = "windows_x86_64_msvc"
++version = "0.48.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
++
++[[package]]
++name = "windows_x86_64_msvc"
++version = "0.52.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
++
++[[package]]
++name = "winnow"
++version = "0.5.40"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
++dependencies = [
++ "memchr",
++]
++
++[[package]]
++name = "winreg"
++version = "0.50.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
++dependencies = [
++ "cfg-if",
++ "windows-sys 0.48.0",
++]
++
++[[package]]
++name = "write16"
++version = "1.0.0"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
++
++[[package]]
++name = "writeable"
++version = "0.5.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
++
++[[package]]
++name = "x25519-dalek"
++version = "2.0.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
++dependencies = [
++ "curve25519-dalek",
++ "rand_core",
++ "serde",
++ "zeroize",
++]
++
++[[package]]
++name = "x509-cert"
++version = "0.2.5"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94"
++dependencies = [
++ "const-oid",
++ "der",
++ "spki",
++]
++
++[[package]]
++name = "yoke"
++version = "0.7.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
++dependencies = [
++ "serde",
++ "stable_deref_trait",
++ "yoke-derive",
++ "zerofrom",
++]
++
++[[package]]
++name = "yoke-derive"
++version = "0.7.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++ "synstructure",
++]
++
++[[package]]
++name = "zerocopy"
++version = "0.7.34"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
++dependencies = [
++ "zerocopy-derive",
++]
++
++[[package]]
++name = "zerocopy-derive"
++version = "0.7.34"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "zerofrom"
++version = "0.1.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
++dependencies = [
++ "zerofrom-derive",
++]
++
++[[package]]
++name = "zerofrom-derive"
++version = "0.1.4"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++ "synstructure",
++]
++
++[[package]]
++name = "zeroize"
++version = "1.8.1"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
++dependencies = [
++ "zeroize_derive",
++]
++
++[[package]]
++name = "zeroize_derive"
++version = "1.4.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
++
++[[package]]
++name = "zerovec"
++version = "0.10.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c"
++dependencies = [
++ "yoke",
++ "zerofrom",
++ "zerovec-derive",
++]
++
++[[package]]
++name = "zerovec-derive"
++version = "0.10.2"
++source = "registry+https://github.com/rust-lang/crates.io-index"
++checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7"
++dependencies = [
++ "proc-macro2",
++ "quote",
++ "syn 2.0.66",
++]
+diff --git a/Cargo.toml b/Cargo.toml
+index d2731e0..dbb1a9d 100644
+--- a/Cargo.toml
++++ b/Cargo.toml
+@@ -1,5 +1,5 @@
+ [workspace]
+-
++resolver = "2"
+ members = [
+ "mrsbfh",
+ "mrsbfh-macros",
+diff --git a/README.md b/README.md
+index ca4892b..daad9e5 100644
+--- a/README.md
++++ b/README.md
+@@ -5,14 +5,13 @@
+ [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-mrsbfh-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/mrsbfh)
+ <!--[<img alt="build status" src="https://img.shields.io/github/workflow/status/MTRNord/mrsbfh/CI/master?style=for-the-badge" height="20">](https://github.com/MTRNord/mrsbfh/actions?query=branch%3Amaster) -->
+
+-
+ A toolkit for writing commandbots more efficient in rust for matrix.
+
+ ## How to use
+
+ To use it you need to add mrsbfh just like any regular create:
+
+-```
++```toml
+ mrsbfh = "0.4.1"
+ ```
+
+diff --git a/example-bot/Cargo.toml b/example-bot/Cargo.toml
+index aec98c6..1464212 100644
+--- a/example-bot/Cargo.toml
++++ b/example-bot/Cargo.toml
+@@ -10,7 +10,7 @@ repository = "https://github.com/MTRNord/mrsbfh"
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+ [dependencies.matrix-sdk]
+-version = "0.4.1"
++version = "0.7.1"
+
+ # To create a bot that uses the Rust implementation of TLS use the default features minus native-tls
+ # Ex:
+@@ -20,13 +20,19 @@ version = "0.4.1"
+ # features = ["encryption", "sled_cryptostore", "sled_state_store", "require_auth_for_profile_requests", "rustls-tls"]
+
+ [dependencies]
+-mrsbfh = {version = "0.4.0", path = "../mrsbfh"}
++mrsbfh = { version = "0.4.0", path = "../mrsbfh" }
+ serde = "1.0"
+ tracing = "0.1"
+ tracing-subscriber = "0.3.9"
+ tracing-futures = "0.2.5"
+-tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "macros"] }
+-clap = { version = "3.1.1", features = ["derive"] }
++tokio = { version = "1", features = [
++ "rt",
++ "rt-multi-thread",
++ "sync",
++ "macros",
++] }
++clap = { version = "4.5.4", features = ["derive"] }
+ async-trait = "0.1.52"
+ thiserror = "1.0"
+ regex = "1.5.4"
++color-eyre = "0.6.3"
+diff --git a/example-bot/src/commands/hello_world.rs b/example-bot/src/commands/hello_world.rs
+index f5c0014..674314d 100644
+--- a/example-bot/src/commands/hello_world.rs
++++ b/example-bot/src/commands/hello_world.rs
+@@ -1,27 +1,12 @@
+-use crate::config::Config;
+ use crate::errors::Error;
+-use matrix_sdk::ruma::events::{room::message::MessageEventContent, AnyMessageEventContent};
+-use matrix_sdk::ruma::RoomId;
+-use matrix_sdk::Client;
++use matrix_sdk::ruma::events::room::message::RoomMessageEventContent;
+ use mrsbfh::commands::command;
+-use std::sync::Arc;
+-use tokio::sync::Mutex;
++use mrsbfh::commands::extract::Extension;
+
+ #[command(help = "`!hello_world` - Prints \"hello world\".")]
+-pub async fn hello_world<'a>(
+- _client: Client,
+- tx: mrsbfh::Sender,
+- _config: Arc<Mutex<Config<'a>>>,
+- _sender: String,
+- _room_id: RoomId,
+- mut _args: Vec<&str>,
+-) -> Result<(), Error>
+-where
+- Config<'a>: mrsbfh::config::Loader + Clone,
+-{
+- let content =
+- AnyMessageEventContent::RoomMessage(MessageEventContent::notice_plain("Hello World!"));
++pub async fn hello_world<'a>(Extension(room): Extension<matrix_sdk::Room>) -> Result<(), Error> {
++ let content = RoomMessageEventContent::notice_plain("Hello World!");
+
+- tx.send(content).await?;
++ room.lock().await.send(content).await?;
+ Ok(())
+ }
+diff --git a/example-bot/src/commands/mod.rs b/example-bot/src/commands/mod.rs
+index 2a36cea..2344bc2 100644
+--- a/example-bot/src/commands/mod.rs
++++ b/example-bot/src/commands/mod.rs
+@@ -1,5 +1,3 @@
+-use crate::config::Config;
+-use crate::errors::Error;
+ use mrsbfh::commands::command_generate;
+
+ pub mod hello_world;
+diff --git a/example-bot/src/errors.rs b/example-bot/src/errors.rs
+index a77f976..6e80cb5 100644
+--- a/example-bot/src/errors.rs
++++ b/example-bot/src/errors.rs
+@@ -1,8 +1,7 @@
+-use matrix_sdk::ruma::events::AnyMessageEventContent;
+ use thiserror::Error as ThisError;
+
+ #[derive(ThisError, Debug)]
+ pub enum Error {
+ #[error(transparent)]
+- SendError(#[from] tokio::sync::mpsc::error::SendError<AnyMessageEventContent>),
++ MatrixError(#[from] matrix_sdk::Error)
+ }
+diff --git a/example-bot/src/main.rs b/example-bot/src/main.rs
+index 8311f02..ec456cd 100644
+--- a/example-bot/src/main.rs
++++ b/example-bot/src/main.rs
+@@ -1,7 +1,9 @@
++#![warn(clippy::missing_const_for_fn)]
++
+ use crate::config::Config;
+ use clap::Parser;
++use color_eyre::Result;
+ use mrsbfh::config::Loader;
+-use std::error::Error;
+ use tracing::*;
+
+ pub mod commands;
+@@ -17,7 +19,8 @@ struct Opts {
+ }
+
+ #[tokio::main]
+-async fn main() -> Result<(), Box<dyn Error>> {
++async fn main() -> Result<()> {
++ color_eyre::install()?;
+ tracing_subscriber::fmt()
+ .pretty()
+ .with_thread_names(true)
+diff --git a/example-bot/src/matrix/mod.rs b/example-bot/src/matrix/mod.rs
+index d56a803..2a75927 100644
+--- a/example-bot/src/matrix/mod.rs
++++ b/example-bot/src/matrix/mod.rs
+@@ -1,58 +1,71 @@
+ use crate::config::Config;
+-use matrix_sdk::{Client, ClientConfig, Session as SDKSession, SyncSettings};
++use color_eyre::Result;
++use matrix_sdk::config::SyncSettings;
++use matrix_sdk::matrix_auth::MatrixSessionTokens;
++use matrix_sdk::ruma::UserId;
++use matrix_sdk::SessionMeta;
++use matrix_sdk::{matrix_auth::MatrixSession, Client};
+ use mrsbfh::url::Url;
+ use mrsbfh::utils::Session;
+-use std::{convert::TryFrom, error::Error, fs, path::Path, sync::Arc};
++use std::sync::Arc;
++use std::{convert::TryFrom, fs, path::Path};
+ use tokio::sync::Mutex;
+ use tracing::*;
+
+ mod sync;
+
+-pub async fn setup(config: Config<'_>) -> Result<Client, Box<dyn Error>> {
++pub async fn setup(config: Config<'_>) -> Result<Client> {
+ info!("Beginning Matrix Setup");
+ let store_path_string = config.store_path.to_string();
+ let store_path = Path::new(&store_path_string);
+ if !store_path.exists() {
+ fs::create_dir_all(store_path)?;
+ }
+- let client_config = ClientConfig::new().store_path(fs::canonicalize(&store_path)?);
+
+ let homeserver_url =
+ Url::parse(&config.homeserver_url).expect("Couldn't parse the homeserver URL");
+
+- let client = Client::new_with_config(homeserver_url, client_config).unwrap();
++ let client = Client::builder()
++ .homeserver_url(homeserver_url)
++ .sqlite_store(store_path, None)
++ .build()
++ .await?;
+
+ if let Some(session) = Session::load(config.session_path.parse().unwrap()) {
+ info!("Starting relogin");
+
+- let session = SDKSession {
+- access_token: session.access_token,
+- device_id: session.device_id.into(),
+- user_id: matrix_sdk::ruma::UserId::try_from(session.user_id.as_str()).unwrap(),
++ let session = MatrixSession {
++ meta: SessionMeta {
++ user_id: <&UserId>::try_from(session.user_id.as_str())
++ .unwrap()
++ .to_owned(),
++ device_id: session.device_id.into(),
++ },
++ tokens: MatrixSessionTokens {
++ access_token: session.access_token,
++ refresh_token: None,
++ },
+ };
+
+- if let Err(e) = client.restore_login(session).await {
++ if let Err(e) = client.restore_session(session).await {
+ error!("{}", e);
+ };
+ info!("Finished relogin");
+ } else {
+ info!("Starting login");
+ let login_response = client
+- .login(
+- &config.mxid,
+- &config.password,
+- None,
+- Some(&"timetracking-bot".to_string()),
+- )
++ .matrix_auth()
++ .login_username(&config.mxid, &config.password)
++ .initial_device_display_name("timetracking-bot")
+ .await;
+ match login_response {
+ Ok(login_response) => {
+ info!("Session: {:#?}", login_response);
+ let session = Session {
+- homeserver: client.homeserver().await.to_string(),
++ homeserver: client.homeserver().to_string(),
+ user_id: login_response.user_id.to_string(),
+ access_token: login_response.access_token,
+- device_id: login_response.device_id.into(),
++ device_id: login_response.device_id.to_string(),
+ };
+ session.save(config.session_path.parse().unwrap())?;
+ }
+@@ -66,21 +79,18 @@ pub async fn setup(config: Config<'_>) -> Result<Client, Box<dyn Error>> {
+ Ok(client)
+ }
+
+-pub async fn start_sync(
+- client: &mut Client,
+- config: Config<'static>,
+-) -> Result<(), Box<dyn Error>> {
+- client.register_event_handler(mrsbfh::sync::autojoin).await;
++pub async fn start_sync(client: &mut Client, config: Config<'static>) -> Result<()> {
++ client.add_event_handler(mrsbfh::sync::autojoin);
+
+ let config = Arc::new(Mutex::new(config));
+- client
+- .register_event_handler(move |ev, room, client| {
+- sync::on_room_message(ev, room, client, config.clone())
+- })
+- .await;
++ let cloned_config = Arc::clone(&config);
++ client.add_event_handler(move |ev, room, client| {
++ let cloned_config = Arc::clone(&cloned_config);
++ sync::on_room_message(ev, room, client, cloned_config)
++ });
+
+ info!("Starting full Sync...");
+- client.sync(SyncSettings::default()).await;
++ client.sync(SyncSettings::default()).await?;
+
+ Ok(())
+ }
+diff --git a/example-bot/src/matrix/sync.rs b/example-bot/src/matrix/sync.rs
+index fc30c78..5c23162 100644
+--- a/example-bot/src/matrix/sync.rs
++++ b/example-bot/src/matrix/sync.rs
+@@ -1,19 +1,16 @@
+ use crate::commands::match_command;
+ use crate::Config;
++use matrix_sdk::room::Room;
++use matrix_sdk::ruma::events::room::message::OriginalSyncRoomMessageEvent;
+ use matrix_sdk::Client;
+-use matrix_sdk::{
+- room::Room,
+- ruma::events::{room::message::MessageEventContent, SyncMessageEvent},
+-};
+ use std::sync::Arc;
+ use tokio::sync::Mutex;
+
+ #[mrsbfh::commands::commands]
+ pub(crate) async fn on_room_message(
+- event: SyncMessageEvent<MessageEventContent>,
++ event: OriginalSyncRoomMessageEvent,
+ room: Room,
+ client: Client,
+ config: Arc<Mutex<Config<'static>>>,
+ ) {
+- println!("message example")
+ }
+diff --git a/mrsbfh-macros/Cargo.toml b/mrsbfh-macros/Cargo.toml
+index f8d6078..36a01cd 100644
+--- a/mrsbfh-macros/Cargo.toml
++++ b/mrsbfh-macros/Cargo.toml
+@@ -13,7 +13,7 @@ categories = ["network-programming", "parsing"]
+ proc-macro = true
+
+ [dependencies]
+-syn = { version= "1.0", features = ["full"] }
++syn = { version = "2.0", features = ["full"] }
+ quote = "1.0"
+-convert_case = "0.5.0"
++convert_case = "0.6.0"
+ proc-macro2 = "1.0"
+diff --git a/mrsbfh-macros/src/lib.rs b/mrsbfh-macros/src/lib.rs
+index b9d00ca..25dc7a2 100644
+--- a/mrsbfh-macros/src/lib.rs
++++ b/mrsbfh-macros/src/lib.rs
+@@ -1,25 +1,31 @@
++#![warn(clippy::missing_const_for_fn)]
++
+ pub(crate) mod utils;
+-use crate::utils::get_arg;
+ use convert_case::{Case, Casing};
+ use proc_macro::TokenStream;
+-use quote::quote;
+-use syn::parse_macro_input;
++use proc_macro2::Span;
++use quote::{quote, quote_spanned};
+ use syn::spanned::Spanned;
++use syn::{parse_macro_input, Ident};
++use utils::Args;
+
+ /// Used to define a command
+ ///
++/// Important: Do not return `Ok(())` yourself.
++/// The macro takes care of the correct return value
++///
+ /// ```compile_fail
+-/// use std::sync::Arc;
+-/// use tokio::sync::Mutex;
++/// use mrsbfh::commands::extract::Extension;
++/// use mrsbfh_macros::command;
+ ///
+ /// #[command(help = "Description")]
+-/// async fn hello_world(mut tx: mrsbfh::Sender, config: Arc<Mutex<Config>>, sender: String, mut args: Vec<&str>) -> Result<(), Box<dyn std::error::Error>> where Config: mrsbfh::config::Loader + Clone {}
++/// async fn hello_world(Extension(room): Extension<matrix_sdk::Room>,) -> Result<(), Box<dyn std::error::Error>> {}
+ /// ```
+ #[proc_macro_attribute]
+ pub fn command(args: TokenStream, input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as syn::ItemFn);
+
+- let args = parse_macro_input!(args as syn::AttributeArgs);
++ let args = parse_macro_input!(args as Args);
+
+ let help_const_name = syn::Ident::new(
+ &format!(
+@@ -28,9 +34,8 @@ pub fn command(args: TokenStream, input: TokenStream) -> TokenStream {
+ ),
+ input.sig.span(),
+ );
+- let help_description = match get_arg(
++ let help_description = match args.get_arg(
+ input.span(),
+- args,
+ "help",
+ "#[command(help = \"<description>\")]",
+ 1,
+@@ -50,6 +55,7 @@ pub fn command(args: TokenStream, input: TokenStream) -> TokenStream {
+ /// Used to generate the match case and help text
+ ///
+ /// ```compile_fail
++/// use mrsbfh_macros::command_generate;
+ /// #[command_generate(bot_name = "botless", description = "Is it a bot or is it not?")]
+ /// enum Commands {
+ /// In,
+@@ -62,7 +68,7 @@ pub fn command(args: TokenStream, input: TokenStream) -> TokenStream {
+ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as syn::ItemEnum);
+
+- let args = parse_macro_input!(args as syn::AttributeArgs);
++ let args = parse_macro_input!(args as Args);
+
+ let commands = input.variants.iter().map(|v| {
+ let command_string = v.ident.to_string().to_lowercase();
+@@ -83,10 +89,10 @@ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
+
+ quote! {
+ #command_string => {
+- #command::#command(client, tx, config, sender, room_id, args).await
++ mrsbfh::commands::Command::call(#command::#command, msg).await
+ },
+ #command_short => {
+- #command::#command(client, tx, config, sender, room_id, args).await
++ mrsbfh::commands::Command::call(#command::#command, msg).await
+ },
+ }
+ });
+@@ -109,9 +115,8 @@ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
+ help_format_string = format!("{}{}", help_format_string, "{}");
+ });
+
+- let bot_name = match get_arg(
++ let bot_name = match args.get_arg(
+ input.span(),
+- args.clone(),
+ "bot_name",
+ "#[command_generate(bot_name = \"<bot name>\", description = \"<bot description>\")]",
+ 2,
+@@ -119,9 +124,8 @@ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
+ Ok(v) => v.value(),
+ Err(e) => return e,
+ };
+- let description = match get_arg(
++ let description = match args.get_arg(
+ input.span(),
+- args,
+ "description",
+ "#[command_generate(bot_name = \"<bot name>\", description = \"<bot description>\")]",
+ 2,
+@@ -135,10 +139,9 @@ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
+ let help_preamble = help_title + &description + commands_title;
+
+ let code = quote! {
+-
+ async fn help(
+- mut tx: mrsbfh::Sender,
+- ) -> Result<(), Error> {
++ mrsbfh::commands::extract::Extension(room): mrsbfh::commands::extract::Extension<matrix_sdk::Room>,
++ ) -> Result<(), mrsbfh::errors::Errors> {
+ let options = mrsbfh::pulldown_cmark::Options::empty();
+ let help_markdown = format!(#help_format_string, #help_preamble, #(#help_parts,)*);
+ let parser = mrsbfh::pulldown_cmark::Parser::new_ext(&help_markdown, options);
+@@ -147,14 +150,12 @@ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
+ let owned_html = html.to_owned();
+
+ mrsbfh::tokio::spawn(async move {
+- let content = matrix_sdk::ruma::events::AnyMessageEventContent::RoomMessage(
+- matrix_sdk::ruma::events::room::message::MessageEventContent::notice_html(
+- &help_markdown,
+- owned_html,
+- ),
++ let content = matrix_sdk::ruma::events::room::message::RoomMessageEventContent::notice_html(
++ &help_markdown,
++ owned_html,
+ );
+
+- if let Err(e) = tx.send(content).await {
++ if let Err(e) = room.lock().await.send(content).await {
+ mrsbfh::tracing::error!("Error: {}",e);
+ };
+ });
+@@ -162,14 +163,14 @@ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
+ Ok(())
+ }
+
+- pub async fn match_command<'a>(cmd: &str, client: matrix_sdk::Client, config: std::sync::Arc<tokio::sync::Mutex<Config<'a>>>, tx: mrsbfh::Sender, sender: String, room_id: matrix_sdk::ruma::RoomId, args: Vec<&str>,) -> Result<(), Error> where Config<'a>: mrsbfh::config::Loader + Clone {
++ pub async fn match_command<'a>(cmd: &str, msg: mrsbfh::commands::Message) -> Result<(), impl std::error::Error> {
+ match cmd {
+ #(#commands)*
+ "help" => {
+- help(tx).await
++ mrsbfh::commands::Command::call(help, msg).await
+ },
+ "h" => {
+- help(tx).await
++ mrsbfh::commands::Command::call(help, msg).await
+ },
+ _ => {Ok(())}
+ }
+@@ -189,7 +190,7 @@ pub fn config_derive(input: TokenStream) -> TokenStream {
+ impl #impl_generics mrsbfh::config::Loader for #name #ty_generics #where_clause {
+ fn load<P: AsRef<std::path::Path> + std::fmt::Debug>(path: P) -> Result<Self, mrsbfh::errors::ConfigError> {
+ let contents = std::fs::read_to_string(path)?;
+- let config: Self = mrsbfh::serde_yaml::from_str(&contents)?;
++ let config: Self = mrsbfh::serde_yml::from_str(&contents)?;
+ Ok(config)
+ }
+ }
+@@ -209,17 +210,47 @@ pub fn config_derive(input: TokenStream) -> TokenStream {
+ /// * The match_command MUST be imported
+ ///
+ /// ```compile_fail
+-/// use crate::commands::match_command;
+-///
+-/// #[mrsbfh::commands::commands]
+-/// async fn on_room_message(event: SyncMessageEvent<MessageEventContent>, room: Room) {
++/// use mrsbfh_macros::commands;
++/// #[commands]
++/// async fn on_room_message(event: matrix_sdk::ruma::events::room::message::OriginalSyncRoomMessageEvent, room: Room) {
+ /// // Your own logic. (Executed BEFORE the commands matching)
+ /// }
+ /// ```
+ ///
++///
+ #[proc_macro_attribute]
+ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ let mut method = parse_macro_input!(input as syn::ItemFn);
++ let arguments = method.sig.inputs.clone();
++
++ let message_magic = arguments.iter().map(|argument| {
++ if let syn::FnArg::Typed(arg_type) = argument {
++ if let syn::Pat::Ident(ref raw_ident) = *arg_type.pat {
++ if let syn::Type::Path(ref path) = *arg_type.ty {
++ let ident = &raw_ident.ident;
++ if path
++ .path
++ .segments
++ .iter()
++ .any(|x| x.ident == Ident::new("Arc", Span::call_site())) {
++ quote! {
++ msg.extensions_mut().insert(std::sync::Arc::clone(&#ident));
++ }
++ } else {
++ quote! {
++ msg.extensions_mut().insert(std::sync::Arc::new(mrsbfh::tokio::sync::Mutex::new(#ident.clone())));
++ }
++ }
++ } else {
++ panic!("Unexpected type for argument");
++ }
++ } else {
++ panic!("Unexpected type for argument");
++ }
++ } else {
++ panic!("Unexpected type for argument");
++ }
++ });
+
+ if method.sig.ident == "on_room_message" {
+ let original = method.block.clone();
+@@ -228,74 +259,62 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ #original
+
+ // Command matching logic
+- if let matrix_sdk::room::Room::Joined(room) = room {
+- let msg_body = if let matrix_sdk::ruma::events::SyncMessageEvent {
+- content: matrix_sdk::ruma::events::room::message::MessageEventContent {
+- msgtype: matrix_sdk::ruma::events::room::message::MessageType::Text(matrix_sdk::ruma::events::room::message::TextMessageEventContent { body: msg_body, .. }),
+- ..
+- },
+- ..
+- } = event
+- {
+- msg_body.clone()
+- } else {
+- String::new()
+- };
+- if msg_body.is_empty() {
+- return;
+- }
+-
+- let sender = event.sender.clone().to_string();
+-
+- let (tx, mut rx) = tokio::sync::mpsc::channel(100);
+- let room_id = room.room_id().clone();
+-
+- let cloned_config = config.clone();
+- let cloned_client = client.clone();
+- tokio::spawn(async move {
+- let normalized_body = mrsbfh::commands::command_utils::WHITESPACE_DEDUPLICATOR_MAGIC.replace_all(&msg_body, " ");
+- let mut split = msg_body.split_whitespace();
+-
+- let command_raw = split.next().expect("This is not a command").to_lowercase();
+- let command = mrsbfh::commands::command_utils::COMMAND_MATCHER_MAGIC.captures(command_raw.as_str())
+- .map_or(String::new(), |caps| {
+- caps.get(1)
+- .map_or(String::new(),
+- |m| String::from(m.as_str()))
+- });
+- if !command.is_empty() {
+- tracing::info!("Got command: {}", command);
+- }
+- // Make sure this is immutable
+- let args: Vec<&str> = split.collect();
+- if let Err(e) = match_command(
+- command.as_str(),
+- cloned_client.clone(),
+- cloned_config.clone(),
+- tx,
+- sender,
+- room_id,
+- args,
+- )
+- .await
+- {
+- tracing::error!("{}", e);
+- }
++ if room.state() != matrix_sdk::RoomState::Joined {
++ return;
++ }
+
+- });
++ let msg_body = match event.content.msgtype {
++ matrix_sdk::ruma::events::room::message::MessageType::Text(matrix_sdk::ruma::events::room::message::TextMessageEventContent { ref body, .. }) => body.clone(),
++ _ => return,
++ };
++ if msg_body.is_empty() {
++ return;
++ }
+
+- while let Some(v) = rx.recv().await {
+- if let Err(e) = room.send(v, None)
+- .await
+- {
+- tracing::error!("{}", e);
+- }
++ let sender = event.sender.clone().to_string();
++
++ let room_id = room.room_id().clone();
++
++ let cloned_client = client.clone();
++ let cloned_room = room.clone();
++ tokio::spawn(async move {
++ let normalized_body = mrsbfh::commands::command_utils::WHITESPACE_DEDUPLICATOR_MAGIC.replace_all(&msg_body, " ");
++ let cloned_body = dbg!(normalized_body).clone();
++ let mut split = cloned_body.split_whitespace().map(|x|x.to_string());
++
++ let command_raw = split.next().expect("This is not a command").to_lowercase();
++ let command = mrsbfh::commands::command_utils::COMMAND_MATCHER_MAGIC.captures(command_raw.as_str())
++ .map_or(String::new(), |caps| {
++ caps.get(1)
++ .map_or(String::new(),
++ |m| String::from(m.as_str()))
++ });
++ if !command.is_empty() {
++ tracing::info!("Got command: {}", command);
+ }
+- }
++ // Make sure this is immutable
++ let args_raw: Vec<String> = split.collect();
++ let args: std::sync::Arc<mrsbfh::tokio::sync::Mutex<Vec<String>>> = std::sync::Arc::new(mrsbfh::tokio::sync::Mutex::new(args_raw.clone()));
++
++ let mut msg = mrsbfh::commands::Message::new();
++ #(#message_magic)*
++ msg.extensions_mut().insert(std::sync::Arc::clone(&args));
++ if let Err(e) = match_command(
++ command.as_str(),
++ msg
++ )
++ .await
++ {
++ tracing::error!("{}", e);
++ }
++
++ });
+ }
+ };
+ method.block = new_block;
++ } else {
++ panic!("Function needs to be called `on_room_message`");
+ }
+
+- TokenStream::from(quote! {#method})
++ TokenStream::from(quote_spanned! {Span::call_site()=>#method})
+ }
+diff --git a/mrsbfh-macros/src/utils.rs b/mrsbfh-macros/src/utils.rs
+index ac77c31..e0ad9cc 100644
+--- a/mrsbfh-macros/src/utils.rs
++++ b/mrsbfh-macros/src/utils.rs
+@@ -1,63 +1,75 @@
++use std::collections::HashSet;
++
+ use proc_macro::TokenStream;
+ use quote::quote;
+-use syn::spanned::Spanned;
++use syn::{
++ parse::{Parse, ParseStream},
++ punctuated::Punctuated,
++ spanned::Spanned,
++ Expr, Lit, Meta, Token,
++};
+
+-pub(crate) fn get_arg<'a>(
+- input_span: proc_macro2::Span,
+- args: syn::AttributeArgs,
+- arg: &'a str,
+- expected: &'a str,
+- expected_args: usize,
+-) -> Result<syn::LitStr, TokenStream> {
+- if args.len() == expected_args {
+- let meta = args
+- .iter()
+- .filter_map(|x| {
+- if let syn::NestedMeta::Meta(ref meta) = x {
+- if meta.path().is_ident(&arg) {
+- return Some(meta);
+- }
+- }
+- None
+- })
+- .next();
+- if let Some(meta) = meta {
+- if let syn::Meta::NameValue(ref meta) = meta {
+- let meta_lit = meta.lit.clone();
+- return match meta_lit {
+- syn::Lit::Str(s) => Ok(s),
+- _ => {
+- let error = syn::Error::new(
+- meta.lit.span(),
+- format!(
+- "expected `{}`\n\nThe field '{}' needs to be a str literal!",
+- expected, arg
+- ),
+- )
+- .to_compile_error();
+- Err(quote! {#error}.into())
++pub(crate) struct Args {
++ args: HashSet<Meta>,
++}
++
++impl Parse for Args {
++ fn parse(input: ParseStream) -> syn::Result<Self> {
++ let args = Punctuated::<Meta, Token![,]>::parse_terminated(input)?;
++ Ok(Args {
++ args: args.into_iter().collect(),
++ })
++ }
++}
++
++impl Args {
++ pub(crate) fn get_arg<'a>(
++ &self,
++ input_span: proc_macro2::Span,
++ arg: &'a str,
++ expected: &'a str,
++ expected_args: usize,
++ ) -> Result<syn::LitStr, TokenStream> {
++ if self.args.len() == expected_args {
++ let lit = self
++ .args
++ .iter()
++ .cloned()
++ .filter_map(|x| {
++ if let syn::Meta::NameValue(name_value) = x {
++ if name_value.path.is_ident(arg) {
++ if let Expr::Lit(lit) = name_value.value {
++ if let Lit::Str(lit_str) = lit.lit {
++ return Some(lit_str);
++ }
++ }
++ }
+ }
+- };
++ None
++ })
++ .next();
++ if let Some(lit) = lit {
++ return Ok(lit);
++ } else {
++ let error = syn::Error::new(
++ lit.span(),
++ format!(
++ "1expected `{}`\n\nThe field '{}' is required!",
++ expected, arg
++ ),
++ )
++ .to_compile_error();
++ return Err(quote! {#error}.into());
+ }
+- } else {
+- let error = syn::Error::new(
+- meta.span(),
+- format!(
+- "1expected `{}`\n\nThe field '{}' is required!",
+- expected, arg
+- ),
+- )
+- .to_compile_error();
+- return Err(quote! {#error}.into());
+ }
++ let error = syn::Error::new(
++ input_span,
++ format!(
++ "expected `{}` but not enough arguments where provided.",
++ expected
++ ),
++ )
++ .to_compile_error();
++ Err(quote! {#error}.into())
+ }
+- let error = syn::Error::new(
+- input_span,
+- format!(
+- "expected `{}` but not enough arguments where provided.",
+- expected
+- ),
+- )
+- .to_compile_error();
+- Err(quote! {#error}.into())
+ }
+diff --git a/mrsbfh/Cargo.toml b/mrsbfh/Cargo.toml
+index 76028ad..275eb92 100644
+--- a/mrsbfh/Cargo.toml
++++ b/mrsbfh/Cargo.toml
+@@ -11,32 +11,43 @@ categories = ["network-programming"]
+ readme = "../README.md"
+
+ [dependencies.matrix-sdk]
+-version = "0.4.1"
+-default_features = false
++version = "0.7.1"
++default-features = false
+
+ [dependencies]
+-url = "2.2.2"
++url = "2.5.0"
+
+-thiserror = "1.0"
++thiserror = "1.0.61"
+
+ # Command macros
+-mrsbfh-macros = {version = "0.4.0", path = "../mrsbfh-macros", optional = true}
++mrsbfh-macros = { version = "0.4.0", path = "../mrsbfh-macros", optional = true }
+
+-tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
+-tracing = "0.1"
++tokio = { version = "1.37.0", features = [
++ "rt",
++ "rt-multi-thread",
++ "macros",
++ "sync",
++] }
++tracing = "0.1.40"
+
+-serde = "1.0"
+-serde_yaml = "0.8"
+-serde_json = "1"
++serde = "1.0.202"
++serde_yml = "0.0.7"
++serde_json = "1.0.117"
+
+-pulldown-cmark = {version = "0.9.1", optional = true} # For generating the help text
++pulldown-cmark = { version = "0.11", optional = true } # For generating the help text
+
+-regex = "1.5"
+-async-trait = "0.1"
+-lazy_static = "1"
++regex = "1.10.4"
++lazy_static = "1.4.0"
+
+ [features]
+ default = ["macros", "native-tls"]
+ macros = ["mrsbfh-macros", "pulldown-cmark"]
+ rustls = ["matrix-sdk/rustls-tls"]
+ native-tls = ["matrix-sdk/native-tls"]
++
++# Hack for katex
++[package.metadata.docs.rs]
++rustdoc-args = [
++ "--html-in-header",
++ "misc/katex-header.html",
++]
+diff --git a/mrsbfh/misc/katex-header.html b/mrsbfh/misc/katex-header.html
+new file mode 100644
+index 0000000..3cdb91f
+--- /dev/null
++++ b/mrsbfh/misc/katex-header.html
+@@ -0,0 +1,15 @@
++<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous">
++<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script>
++<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
++<script>
++ document.addEventListener("DOMContentLoaded", function() {
++ renderMathInElement(document.body, {
++ delimiters: [
++ {left: "$$", right: "$$", display: true},
++ {left: "\\(", right: "\\)", display: false},
++ {left: "$", right: "$", display: false},
++ {left: "\\[", right: "\\]", display: true}
++ ]
++ });
++ });
++</script>
+\ No newline at end of file
+diff --git a/mrsbfh/src/commands.rs b/mrsbfh/src/commands.rs
+index ac679cf..b10947e 100644
+--- a/mrsbfh/src/commands.rs
++++ b/mrsbfh/src/commands.rs
+@@ -6,7 +6,7 @@
+ //!
+ //! These functions require a specific syntax which is described below.
+ //!
+-//! Also that function requires you to have a config struct which implements the [Loader](crate::config::Loader)
++//! Also, that function requires you to have a config struct which implements the [Loader](crate::config::Loader)
+ //! trait.
+ //!
+ //! <br>
+@@ -16,32 +16,23 @@
+ //!
+ //! In each of these submodules you can define a command like this:
+ //!
+-//! ```compile_fail
+-//! use crate::config::Config;
+-//! use crate::errors::Error;
+-//! use matrix_sdk::ruma::events::{room::message::MessageEventContent, AnyMessageEventContent};
+-//! use matrix_sdk::ruma::RoomId;
+-//! use matrix_sdk::Client;
++//! ```
++//! use matrix_sdk::ruma::events::room::message::RoomMessageEventContent;
+ //! use mrsbfh::commands::command;
+-//! use std::sync::Arc;
+-//! use tokio::sync::Mutex;
++//! use mrsbfh::commands::extract::Extension;
++//! use thiserror::Error as ThisError;
++//!
++//! #[derive(ThisError, Debug)]
++//! pub enum Error {
++//! #[error(transparent)]
++//! MatrixError(#[from] matrix_sdk::Error)
++//! }
+ //!
+ //! #[command(help = "`!hello_world` - Prints \"hello world\".")]
+-//! pub async fn hello_world<'a>(
+-//! _client: Client,
+-//! tx: mrsbfh::Sender,
+-//! _config: Arc<Mutex<Config<'a>>>,
+-//! _sender: String,
+-//! _room_id: RoomId,
+-//! mut _args: Vec<&str>,
+-//! ) -> Result<(), Error>
+-//! where
+-//! Config<'a>: mrsbfh::config::Loader + Clone,
+-//! {
+-//! let content =
+-//! AnyMessageEventContent::RoomMessage(MessageEventContent::notice_plain("Hello World!"));
+-//!
+-//! tx.send(content).await?;
++//! pub async fn hello_world<'a>(Extension(room): Extension<matrix_sdk::Room>) -> Result<(), Error> {
++//! let content = RoomMessageEventContent::notice_plain("Hello World!");
++//!
++//! room.lock().await.send(content).await?;
+ //! Ok(())
+ //! }
+ //! ```
+@@ -83,12 +74,12 @@
+ //!
+ //! ## `#[commands]` macro
+ //!
+-//! This macro is used to generate the logic in the [register_event_handler](matrix_sdk::Client::register_event_handler) method to
++//! This macro is used to generate the logic in the [register_event_handler](matrix_sdk::Client::add_event_handler) method to
+ //! handle commands after your code.
+ //!
+ //! The definition is:
+ //!
+-//! ```compile_fail
++//! ```ignore
+ //! use crate::commands::match_command;
+ //! use crate::config::Config;
+ //! use matrix_sdk::async_trait;
+@@ -115,9 +106,9 @@
+ //!
+ //! You use it using this snippet:
+ //!
+-//! ```
++//! ```ignore
+ //! client
+-//! .register_event_handler(move |ev, room, client| {
++//! .add_event_handler(move |ev, room, client| {
+ //! sync::on_room_message(ev, room, client, config.clone())
+ //! })
+ //! .await;
+@@ -129,6 +120,12 @@
+ //! * Your `match_command` function MUST be imported
+ //!
+
++pub mod extract;
++
++use crate::commands::extract::Extensions;
++use std::convert::Infallible;
++use std::future::Future;
++
+ pub mod command_utils {
+ use lazy_static::lazy_static;
+
+@@ -140,4 +137,230 @@ pub mod command_utils {
+ }
+ }
+
++/// Types that can be created from messages.
++///
++/// See [`axum::extract`] for more details.
++///
++/// [`axum::extract`]: https://docs.rs/axum/latest/axum/extract/index.html
++pub trait FromMessage: Sized {
++ /// If the extractor fails it'll use this "rejection" type. A rejection is
++ /// a kind of error that can be converted into a response.
++ type Rejection: IntoError;
++
++ /// Perform the extraction.
++ fn from_message(
++ msg: &mut MessageParts,
++ ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send;
++}
++
++impl FromMessage for Parts {
++ type Rejection = Infallible;
++
++ async fn from_message(msg: &mut MessageParts) -> Result<Self, Self::Rejection> {
++ let extensions = std::mem::take(msg.extensions_mut());
++
++ let mut temp_message = Message::new();
++ *temp_message.extensions_mut() = extensions;
++
++ let parts = temp_message.into_parts();
++
++ Ok(parts)
++ }
++}
++
++pub struct Parts {
++ /// The message's extensions
++ pub extensions: Extensions,
++}
++
++impl Parts {
++ /// Creates a new default instance of `Parts`
++ fn new() -> Parts {
++ Parts {
++ extensions: Extensions::default(),
++ }
++ }
++}
++
++pub struct Message {
++ parts: Parts,
++}
++
++impl Message {
++ /// Creates a new blank `Message`
++ ///
++ /// The component parts of this message will be set to their default.
++ ///
++ /// # Examples
++ ///
++ /// ```
++ /// use mrsbfh::commands::Message;
++ /// let message = Message::new();
++ /// ```
++ #[inline]
++ pub fn new() -> Message {
++ Message {
++ parts: Parts::new(),
++ }
++ }
++
++ /// Creates a new `Message` with the given components parts.
++ ///
++ /// # Examples
++ ///
++ /// ```
++ /// use mrsbfh::commands::Message;
++ /// let message = Message::new();
++ /// let mut parts = message.into_parts();
++ ///
++ /// let message = Message::from_parts(parts);
++ /// ```
++ #[inline]
++ pub const fn from_parts(parts: Parts) -> Message {
++ Message { parts }
++ }
++
++ /// Returns a reference to the associated extensions.
++ ///
++ /// # Examples
++ ///
++ /// ```
++ /// use mrsbfh::commands::Message;
++ /// let message: Message = Message::default();
++ /// assert!(message.extensions().get::<i32>().is_none());
++ /// ```
++ #[inline]
++ pub const fn extensions(&self) -> &Extensions {
++ &self.parts.extensions
++ }
++
++ /// Returns a mutable reference to the associated extensions.
++ ///
++ /// # Examples
++ ///
++ /// ```
++ /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
++ /// use std::ops::Deref;
++ /// use std::sync::Arc;
++ /// use tokio::sync::Mutex;
++ /// use mrsbfh::commands::Message;
++ /// let mut message: Message = Message::default();
++ /// message.extensions_mut().insert(Arc::new(Mutex::new("hello")));
++ /// assert_eq!(*message.extensions().get::<&str>().unwrap().lock().await, "hello");
++ /// # })
++ /// ```
++ #[inline]
++ pub fn extensions_mut(&mut self) -> &mut Extensions {
++ &mut self.parts.extensions
++ }
++
++ /// Consumes the message returning the parts.
++ ///
++ /// # Examples
++ ///
++ /// ```
++ /// use mrsbfh::commands::Message;
++ /// let message = Message::new();
++ /// let parts = message.into_parts();
++ /// ```
++ #[inline]
++ pub fn into_parts(self) -> Parts {
++ self.parts
++ }
++}
++
++impl Default for Message {
++ fn default() -> Self {
++ Self::new()
++ }
++}
++
++pub(crate) mod sealed {
++ #![allow(unreachable_pub, missing_docs, missing_debug_implementations)]
++
++ pub trait HiddenTrait {}
++ pub struct Hidden;
++ impl HiddenTrait for Hidden {}
++}
++
++#[derive(Debug)]
++pub struct MessageParts {
++ extensions: Extensions,
++}
++
++impl MessageParts {
++ pub fn new(msg: Message) -> Self {
++ let Parts { extensions, .. } = msg.into_parts();
++ MessageParts { extensions }
++ }
++
++ /// Gets a reference to the message extensions.
++ pub const fn extensions(&self) -> &Extensions {
++ &self.extensions
++ }
++
++ /// Gets a mutable reference to the message extensions.
++ pub fn extensions_mut(&mut self) -> &mut Extensions {
++ &mut self.extensions
++ }
++}
++
++pub trait Command<T>: Clone + Send + Sized + 'static {
++ // This seals the trait. We cannot use the regular "sealed super trait"
++ // approach due to coherence.
++ #[doc(hidden)]
++ type Sealed: sealed::HiddenTrait;
++
++ /// Call the command with the given request.
++ fn call(
++ self,
++ req: Message,
++ ) -> impl Future<Output = Result<(), crate::errors::Errors>> + Send;
++}
++
++macro_rules! impl_command {
++ ( $($ty:ident),* $(,)? ) => {
++ #[allow(non_snake_case)]
++ impl<F, Fut, Res, $($ty,)*> Command<($($ty,)*)> for F
++ where
++ F: FnOnce($($ty,)*) -> Fut + Clone + Send + 'static,
++ Fut: Future<Output = Result<(), Res>> + Send,
++ Res: IntoError,
++ $( $ty: FromMessage + Send,)*
++ {
++ type Sealed = sealed::Hidden;
++
++ async fn call(self, msg: Message) -> Result<(), crate::errors::Errors> {
++ let mut msg = MessageParts::new(msg);
++
++ $(
++ let $ty = match $ty::from_message(&mut msg).await {
++ Ok(value) => value,
++ Err(rejection) => return Err(rejection.into_error()),
++ };
++ )*
++
++ let res = self($($ty,)*).await.map_err(|x|x.into_error())?;
++
++ Ok(res)
++ }
++ }
++ };
++}
++
++crate::utils::all_the_tuples!(impl_command);
++
+ pub use mrsbfh_macros::{command, command_generate, commands};
++
++pub trait IntoError {
++ fn into_error(self) -> crate::errors::Errors;
++}
++
++impl<T> IntoError for T
++where
++ T: ToString,
++{
++ fn into_error(self) -> crate::errors::Errors {
++ crate::errors::Errors::CustomError(self.to_string())
++ }
++}
+diff --git a/mrsbfh/src/commands/extract.rs b/mrsbfh/src/commands/extract.rs
+new file mode 100644
+index 0000000..92b87c8
+--- /dev/null
++++ b/mrsbfh/src/commands/extract.rs
+@@ -0,0 +1,242 @@
++use crate::commands::FromMessage;
++use crate::commands::MessageParts;
++use crate::errors::ExtensionRejection;
++use std::any::{Any, TypeId};
++use std::collections::HashMap;
++use std::fmt;
++use std::hash::{BuildHasherDefault, Hasher};
++use std::ops::Deref;
++use std::sync::Arc;
++use tokio::sync::Mutex;
++
++type AnyMap = HashMap<TypeId, Box<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>;
++
++// With TypeIds as keys, there's no need to hash them. They are already hashes
++// themselves, coming from the compiler. The IdHasher just holds the u64 of
++// the TypeId, and then returns it, instead of doing any bit fiddling.
++#[derive(Default)]
++struct IdHasher(u64);
++
++impl Hasher for IdHasher {
++ #[inline]
++ fn finish(&self) -> u64 {
++ self.0
++ }
++
++ fn write(&mut self, _: &[u8]) {
++ unreachable!("TypeId calls write_u64");
++ }
++
++ #[inline]
++ fn write_u64(&mut self, id: u64) {
++ self.0 = id;
++ }
++}
++/// A type map of protocol extensions.
++///
++/// `Extensions` can be used by `Messages` to store
++/// extra data derived from the underlying protocol.
++#[derive(Default)]
++pub struct Extensions {
++ // If extensions are never used, no need to carry around an empty HashMap.
++ // That's 3 words. Instead, this is only 1 word.
++ map: Option<Box<AnyMap>>,
++}
++
++impl fmt::Debug for Extensions {
++ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++ f.debug_struct("Extensions").finish()
++ }
++}
++
++impl Extensions {
++ /// Create an empty `Extensions`.
++ #[inline]
++ pub fn new() -> Extensions {
++ Extensions { map: None }
++ }
++
++ /// Insert a type into this `Extensions`.
++ ///
++ /// If a extension of this type already existed, it will
++ /// be returned.
++ ///
++ /// # Example
++ ///
++ /// ```
++ /// use mrsbfh::commands::extract::Extensions;
++ /// use tokio::sync::Mutex;
++ /// use std::sync::Arc;
++ /// let mut ext = Extensions::new();
++ /// assert!(ext.insert(Arc::new(Mutex::new(5i32))).is_none());
++ /// assert!(ext.insert(Arc::new(Mutex::new(4u8))).is_none());
++ /// ```
++ pub fn insert<T: Send + Sync + 'static>(&mut self, val: Arc<Mutex<T>>) -> Option<T> {
++ self.map
++ .get_or_insert_with(|| Box::new(HashMap::default()))
++ .insert(TypeId::of::<Arc<Mutex<T>>>(), Box::new(val))
++ .and_then(|boxed| {
++ (boxed as Box<dyn Any + 'static>)
++ .downcast()
++ .ok()
++ .map(|boxed| *boxed)
++ })
++ }
++
++ /// Get a reference to a type previously inserted on this `Extensions`.
++ ///
++ /// # Example
++ ///
++ /// ```
++ /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
++ /// use mrsbfh::commands::extract::Extensions;
++ /// use tokio::sync::Mutex;
++ /// use std::sync::Arc;
++ /// let mut ext = Extensions::new();
++ /// assert!(ext.get::<i32>().is_none());
++ /// ext.insert(Arc::new(Mutex::new(5i32)));
++ ///
++ /// assert_eq!(*ext.get::<i32>().unwrap().lock().await, 5i32);
++ /// })
++ /// ```
++ pub fn get<T: Send + Sync + 'static>(&self) -> Option<&Arc<Mutex<T>>> {
++ self.map
++ .as_ref()
++ .and_then(|map| map.get(&TypeId::of::<Arc<Mutex<T>>>()))
++ .and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref())
++ }
++
++ /// Get a mutable reference to a type previously inserted on this `Extensions`.
++ ///
++ /// # Example
++ ///
++ /// ```
++ /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
++ /// use mrsbfh::commands::extract::Extensions;
++ /// use std::sync::Arc;
++ /// use tokio::sync::Mutex;
++ /// let mut ext = Extensions::new();
++ /// struct HelloWorld(String);
++ /// ext.insert(Arc::new(Mutex::new(HelloWorld(String::from("Hello")))));
++ /// {ext.get_mut::<HelloWorld>().unwrap().lock().await.0 += " World";};
++ ///
++ /// assert_eq!(ext.get::<HelloWorld>().unwrap().lock().await.0, "Hello World");
++ /// })
++ /// ```
++ pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut Arc<Mutex<T>>> {
++ self.map
++ .as_mut()
++ .and_then(|map| map.get_mut(&TypeId::of::<Arc<Mutex<T>>>()))
++ .and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut())
++ }
++
++ /// Remove a type from this `Extensions`.
++ ///
++ /// If a extension of this type existed, it will be returned.
++ ///
++ /// # Example
++ ///
++ /// ```
++ /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
++ /// use mrsbfh::commands::extract::Extensions;
++ /// let mut ext = Extensions::new();
++ /// ext.insert(std::sync::Arc::new(tokio::sync::Mutex::new(5i32)));
++ /// assert_eq!(*ext.remove::<i32>().unwrap().lock().await, 5i32);
++ /// assert!(ext.get::<i32>().is_none());
++ /// })
++ /// ```
++ pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<Arc<Mutex<T>>> {
++ self.map
++ .as_mut()
++ .and_then(|map| map.remove(&TypeId::of::<Arc<Mutex<T>>>()))
++ .and_then(|boxed| {
++ (boxed as Box<dyn Any + 'static>)
++ .downcast()
++ .ok()
++ .map(|boxed| *boxed)
++ })
++ }
++
++ /// Clear the `Extensions` of all inserted extensions.
++ ///
++ /// # Example
++ ///
++ /// ```
++ /// # use mrsbfh::commands::extract::Extensions;
++ /// let mut ext = Extensions::new();
++ /// ext.insert(std::sync::Arc::new(tokio::sync::Mutex::new(5i32)));
++ /// ext.clear();
++ ///
++ /// assert!(ext.get::<i32>().is_none());
++ /// ```
++ #[inline]
++ pub fn clear(&mut self) {
++ if let Some(ref mut map) = self.map {
++ map.clear();
++ }
++ }
++
++ /// Check whether the extension set is empty or not.
++ ///
++ /// # Example
++ ///
++ /// ```
++ /// # use mrsbfh::commands::extract::Extensions;
++ /// let mut ext = Extensions::new();
++ /// assert!(ext.is_empty());
++ /// ext.insert(std::sync::Arc::new(tokio::sync::Mutex::new(5i32)));
++ /// assert!(!ext.is_empty());
++ /// ```
++ #[inline]
++ pub fn is_empty(&self) -> bool {
++ self.map.as_ref().map_or(true, |map| map.is_empty())
++ }
++
++ /// Get the numer of extensions available.
++ ///
++ /// # Example
++ ///
++ /// ```
++ /// # use mrsbfh::commands::extract::Extensions;
++ /// let mut ext = Extensions::new();
++ /// assert_eq!(ext.len(), 0);
++ /// ext.insert(std::sync::Arc::new(tokio::sync::Mutex::new(5i32)));
++ /// assert_eq!(ext.len(), 1);
++ /// ```
++ #[inline]
++ pub fn len(&self) -> usize {
++ self.map.as_ref().map_or(0, |map| map.len())
++ }
++}
++
++#[derive(Debug, Clone)]
++pub struct Extension<T>(pub Arc<Mutex<T>>);
++
++impl<T> FromMessage for Extension<T>
++where
++ T: Clone + Send + Sync + 'static,
++{
++ type Rejection = ExtensionRejection;
++
++ async fn from_message(msg: &mut MessageParts) -> Result<Self, Self::Rejection> {
++ let value = msg
++ .extensions()
++ .get::<T>()
++ .ok_or_else(|| {
++ ExtensionRejection::MissingExtension(format!(
++ "Extension of type `{}` was not found. Perhaps you forgot to add it? See `axum::extract::Extension`.",
++ std::any::type_name::<Arc<Mutex<T>>>()
++ ))
++ }).cloned()?;
++
++ Ok(Extension(value))
++ }
++}
++
++impl<T> Deref for Extension<T> {
++ type Target = Arc<Mutex<T>>;
++
++ fn deref(&self) -> &Self::Target {
++ &self.0
++ }
++}
+diff --git a/mrsbfh/src/config.rs b/mrsbfh/src/config.rs
+index ef2bd0e..91771f2 100644
+--- a/mrsbfh/src/config.rs
++++ b/mrsbfh/src/config.rs
+@@ -7,15 +7,16 @@
+ //!
+ //! The simplest way is to use the Derive macro [`#[derive(ConfigDerive)]`](crate::config::ConfigDerive)
+ //!
+-//! It requires however that You also derive [Clone](std::clone::Clone), [Serialize](serde::Serialize)
++//! It requires however that You also derive [Clone], [Serialize]
+ //! and [Deserialize](serde::Deserialize).
+ //!
+-//! Also this only works for yaml config files. For any other format you will to implement the trait
+-//! yourself. However the crate still needs to implement [Clone](std::clone::Clone).
++//! Also, this only works for yaml config files. For any other format you will to implement the trait
++//! yourself. However, the crate still needs to implement [Clone].
+ //!
+ //! ## Example
+ //!
+-//! ```compile_fail
++//! ```
++//! use mrsbfh::config::ConfigDerive;
+ //! use serde::{Deserialize, Serialize};
+ //! use std::borrow::Cow;
+ //!
+diff --git a/mrsbfh/src/errors.rs b/mrsbfh/src/errors.rs
+index 0d3ba04..cad9316 100644
+--- a/mrsbfh/src/errors.rs
++++ b/mrsbfh/src/errors.rs
+@@ -7,7 +7,7 @@ pub enum ConfigError {
+ #[error(transparent)]
+ IOError(#[from] std::io::Error),
+ #[error(transparent)]
+- SerdeError(#[from] serde_yaml::Error),
++ SerdeError(#[from] serde_yml::Error),
+ }
+
+ #[derive(Error, Debug)]
+@@ -17,3 +17,17 @@ pub enum SessionError {
+ #[error(transparent)]
+ SerdeError(#[from] serde_json::Error),
+ }
++
++#[derive(Error, Debug)]
++pub enum ExtensionRejection {
++ #[error("Extensions taken by other extractor")]
++ ExtensionsAlreadyExtracted,
++ #[error("{0}")]
++ MissingExtension(String),
++}
++
++#[derive(Error, Debug)]
++pub enum Errors {
++ #[error("{0}")]
++ CustomError(String),
++}
+diff --git a/mrsbfh/src/lib.rs b/mrsbfh/src/lib.rs
+index 7b87908..7643a8d 100644
+--- a/mrsbfh/src/lib.rs
++++ b/mrsbfh/src/lib.rs
+@@ -21,6 +21,7 @@
+ //! ## Examples
+ //!
+ //! For examples please have a look at the [example-bot](https://github.com/MTRNord/mrsbfh/tree/main/example-bot) or take a look in the individual modules.
++#![warn(clippy::missing_const_for_fn)]
+
+ #[cfg(feature = "macros")]
+ pub mod commands;
+@@ -32,56 +33,7 @@ pub mod errors;
+ pub mod sync;
+ pub mod utils;
+
+-/// A wrapper type for the tokio sender channel with AnyMessageEventContent as content needed in multiple places
+-pub type Sender = tokio::sync::mpsc::Sender<matrix_sdk::ruma::events::AnyMessageEventContent>;
+-
+-/// An extension to simply do notices
+-#[async_trait::async_trait]
+-pub trait MatrixMessageExt {
+- async fn send_notice(
+- &mut self,
+- body: String,
+- formatted_body: Option<String>,
+- ) -> Result<
+- (),
+- tokio::sync::mpsc::error::SendError<matrix_sdk::ruma::events::AnyMessageEventContent>,
+- >;
+-}
+-
+-#[async_trait::async_trait]
+-impl MatrixMessageExt for Sender {
+- async fn send_notice(
+- &mut self,
+- body: String,
+- formatted_body: Option<String>,
+- ) -> Result<
+- (),
+- tokio::sync::mpsc::error::SendError<matrix_sdk::ruma::events::AnyMessageEventContent>,
+- > {
+- match formatted_body {
+- Some(formatted_body) => {
+- let content = matrix_sdk::ruma::events::AnyMessageEventContent::RoomMessage(
+- matrix_sdk::ruma::events::room::message::MessageEventContent::notice_html(
+- body,
+- formatted_body,
+- ),
+- );
+-
+- self.send(content).await
+- }
+- None => {
+- let content = matrix_sdk::ruma::events::AnyMessageEventContent::RoomMessage(
+- matrix_sdk::ruma::events::room::message::MessageEventContent::notice_plain(
+- body,
+- ),
+- );
+- self.send(content).await
+- }
+- }
+- }
+-}
+-
+-pub use serde_yaml;
++pub use serde_yml;
+ pub use tokio;
+ pub use tracing;
+ pub use url;
+diff --git a/mrsbfh/src/sync.rs b/mrsbfh/src/sync.rs
+index 9a0ab41..80e2531 100644
+--- a/mrsbfh/src/sync.rs
++++ b/mrsbfh/src/sync.rs
+@@ -1,37 +1,29 @@
+ //! # Helpers for the sync process
+
+-use matrix_sdk::{
+- room::Room,
+- ruma::events::{room::member::MemberEventContent, StrippedStateEvent},
+- Client,
+-};
++use matrix_sdk::{room::Room, ruma::events::room::member::StrippedRoomMemberEvent, Client};
+ use tracing::*;
+
+-/// A small helper to auto join any incitation
++/// A small helper to auto join any invitation
+ ///
+ /// To join just do this:
+-/// ```compile_fail
+-/// client.register_event_handler(mrsbfh::sync::autojoin).await;
++/// ```ignore
++/// client.add_event_handler(mrsbfh::sync::autojoin);
+ /// ```
+ /// This will also automatically retry to join if that failed with increasing
+-/// delay between tries (numeber_of_tries*2) starting with a delay of 2.
++/// delay between tries ($number\\_of\\_tries\cdot{}2$) starting with a delay of 2.
+ /// It will print an error with the room id if the delay exceeds 3600s.
+ ///
+-pub async fn autojoin(
+- room_member: StrippedStateEvent<MemberEventContent>,
+- client: Client,
+- room: Room,
+-) {
++pub async fn autojoin(room_member: StrippedRoomMemberEvent, client: Client, room: Room) {
+ // Autojoin logic
+- if room_member.state_key != client.user_id().await.unwrap() {
++ if room_member.state_key != client.user_id().unwrap() {
+ debug!("Got invite that isn't for us");
+ return;
+ }
+- if let matrix_sdk::room::Room::Invited(room) = room {
++ tokio::spawn(async move {
+ info!("Autojoining room {}", room.room_id());
+ let mut delay = 2;
+
+- while let Err(err) = room.accept_invitation().await {
++ while let Err(err) = room.join().await {
+ // retry autojoin due to synapse sending invites, before the
+ // invited user can join for more information see
+ // https://github.com/matrix-org/synapse/issues/4345
+@@ -51,5 +43,5 @@ pub async fn autojoin(
+ }
+ }
+ info!("Successfully joined room {}", room.room_id());
+- }
++ });
+ }
+diff --git a/mrsbfh/src/utils.rs b/mrsbfh/src/utils.rs
+index 1031d0e..2169372 100644
+--- a/mrsbfh/src/utils.rs
++++ b/mrsbfh/src/utils.rs
+@@ -2,26 +2,35 @@
+ //!
+ //! ## Session
+ //!
+-//! The easiest way to use the [Session](crate::utils::Session) struct is to use it like this:
++//! The easiest way to use the [Session] struct is to use it like this:
+ //!
+ //! ```compile_fail
+-//! use matrix_sdk::Session as SDKSession;
+-//!
++//! use matrix_sdk::matrix_auth::{MatrixSession, MatrixSessionTokens};
++//! use matrix_sdk::ruma::UserId;
++//! use matrix_sdk::SessionMeta;
++//! use mrsbfh::utils::Session;
+ //! if let Some(session) = Session::load(config.session_path.parse().unwrap()) {
+-//! info!("Starting relogin");
++//! tracing::info!("Starting relogin");
+ //!
+-//! let session = SDKSession {
+-//! access_token: session.access_token,
+-//! device_id: session.device_id.into(),
+-//! user_id: matrix_sdk::identifiers::UserId::try_from(session.user_id.as_str()).unwrap(),
+-//! };
++//! let session = MatrixSession {
++//! meta: SessionMeta {
++//! user_id: <&UserId>::try_from(session.user_id.as_str())
++//! .unwrap()
++//! .to_owned(),
++//! device_id: session.device_id.into(),
++//! },
++//! tokens: MatrixSessionTokens {
++//! access_token: session.access_token,
++//! refresh_token: None,
++//! },
++//! };
+ //!
+ //! if let Err(e) = client.restore_login(session).await {
+-//! error!("{}", e);
++//! tracing::error!("{}", e);
+ //! };
+-//! info!("Finished relogin");
++//! tracing::info!("Finished relogin");
+ //! } else {
+-//! info!("Starting login");
++//! tracing::info!("Starting login");
+ //! let login_response = client
+ //! .login(
+ //! &config.mxid,
+@@ -32,7 +41,7 @@
+ //! .await;
+ //! match login_response {
+ //! Ok(login_response) => {
+-//! info!("Session: {:#?}", login_response);
++//! tracing::info!("Session: {:#?}", login_response);
+ //! let session = Session {
+ //! homeserver: client.homeserver().to_string(),
+ //! user_id: login_response.user_id.to_string(),
+@@ -41,9 +50,9 @@
+ //! };
+ //! session.save(config.session_path.parse().unwrap())?;
+ //! }
+-//! Err(e) => error!("Error while login: {}", e),
++//! Err(e) => tracing::error!("Error while login: {}", e),
+ //! }
+-//! info!("Finished login");
++//! tracing::info!("Finished login");
+ //! }
+ //! ```
+ //!
+@@ -52,7 +61,7 @@
+ //! This first checks if there is a session already existing and uses it to relogin using the known
+ //! session data.
+ //!
+-//! If not it creates and saves the [Session](crate::utils::Session) struct. Allowing for a relogin on the next start.
++//! If not it creates and saves the [Session] struct. Allowing for a relogin on the next start.
+ //!
+
+ use crate::errors::SessionError;
+@@ -60,7 +69,7 @@ use serde::{Deserialize, Serialize};
+ use std::path::PathBuf;
+ use tracing::*;
+
+-/// Informations needed to keep track about a session
++/// Information needed to keep track about a session
+ #[derive(Clone, Debug, Serialize, Deserialize)]
+ pub struct Session {
+ /// The homeserver used for this session.
+@@ -101,3 +110,26 @@ impl Session {
+ }
+ }
+ }
++
++macro_rules! all_the_tuples {
++ ($name:ident) => {
++ $name!(T1);
++ $name!(T1, T2);
++ $name!(T1, T2, T3);
++ $name!(T1, T2, T3, T4);
++ $name!(T1, T2, T3, T4, T5);
++ $name!(T1, T2, T3, T4, T5, T6);
++ $name!(T1, T2, T3, T4, T5, T6, T7);
++ $name!(T1, T2, T3, T4, T5, T6, T7, T8);
++ $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
++ $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
++ $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
++ $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
++ $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
++ $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
++ $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
++ $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
++ };
++}
++
++pub(crate) use all_the_tuples;
diff --git a/pulls/13.json b/pulls/13.json
@@ -0,0 +1,87 @@
+{
+ "number": 13,
+ "title": "Add a way to add custom state and update sdk",
+ "state": "closed",
+ "diff_file": "13.diff",
+ "author": "MTRNord",
+ "created_at": "2022-03-12T18:54:50Z",
+ "closed_at": "2025-06-16T22:41:06Z",
+ "merged_at": null,
+ "base_ref": "main",
+ "head_ref": "MTRNord/issue-12",
+ "labels": [
+ "enhancement"
+ ],
+ "assignees": [
+ "MTRNord"
+ ],
+ "requested_reviewers": [],
+ "body": "This is heavily building upon what http and axum do. In fact there is a lot of reused code.\r\n\r\nThis allows for function defined like this:\r\n\r\n```rust\r\n#[command(help = \"`!hello_world` - Prints \\\"hello world\\\".\")]\r\npub async fn hello_world<'a>(\r\n Extension(tx): Extension<mrsbfh::Sender>,\r\n) -> Result<(), Error> {\r\n let content = RoomMessageEventContent::notice_plain(\"Hello World!\");\r\n\r\n tx.lock().await.send(content).await?;\r\n Ok(())\r\n}\r\n```\r\n\r\nDownsides: \r\n\r\n- It adds a Mutex to mutable stuff.\r\n- It needs stuff to be strictly async (I guess that was needed before too?)\r\n- A little more verbose?\r\n\r\nPros:\r\n\r\n- You can pass anything to it as long as it is defined in your sync handler. (the macro auto extracts the arguments from it)\r\n- You don't need to pass all things to every command\r\n\r\nFixes #12 \r\n\r\nMissing is:\r\n\r\n- [x] Getting user supplied things from the sync handler and passing it on.\r\n\r\nTests seem to work:\r\n\r\n\r\n",
+ "comments": [
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T19:54:27Z",
+ "body": "Followup issue: #14 "
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-13T20:13:23Z",
+ "body": "Some feedback from jplatte:\r\n\r\n> One thing I noticed is that you seem to have an automatic 'extension' of Arc<Mutex<mrsbfh::Sender>>. I would recomment instead making the Arc<Mutex<_>> part internal so that the sender is Clone + Send + Sync and you make it an extractor by itself (no Extension wrapping needed)\r\n\r\n> Extension can still make sense if you want users to be able to add their own context\r\n\r\n> In the SDK the Ctx type and register_event_handler_context fills the same role"
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2024-05-24T23:10:22Z",
+ "body": "Main missing TODO is now fixing the comments I think"
+ }
+ ],
+ "review_comments": [
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T18:57:36Z",
+ "body": "@donicrosby This is basically where now your wish comes in. I basically need to do this dynamically for all the things that the function provides when you use the macro. This is currently still hardcoded to only do the config. But the example bot should give you an idea how the api will look like. Which means it should be fairly similiar as before with not too many changes needed",
+ "path": "mrsbfh-macros/src/lib.rs",
+ "line": 275,
+ "diff_hunk": "@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {\n tracing::info!(\"Got command: {}\", command);\n }\n // Make sure this is immutable\n- let args: Vec<&str> = split.collect();\n+ let args_raw: Vec<String> = split.collect();\n+ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());\n+ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));\n+\n+ let mut msg = mrsbfh::commands::Message::new();\n+ // TODO insert all the things in the function args\n+ msg.extensions_mut().insert(std::sync::Arc::clone(&args));"
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T19:28:20Z",
+ "body": "Pushed the rest now. So in theory this PR should work. It compiles but I didnt actually test if it works in practice",
+ "path": "mrsbfh-macros/src/lib.rs",
+ "line": 275,
+ "diff_hunk": "@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {\n tracing::info!(\"Got command: {}\", command);\n }\n // Make sure this is immutable\n- let args: Vec<&str> = split.collect();\n+ let args_raw: Vec<String> = split.collect();\n+ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());\n+ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));\n+\n+ let mut msg = mrsbfh::commands::Message::new();\n+ // TODO insert all the things in the function args\n+ msg.extensions_mut().insert(std::sync::Arc::clone(&args));"
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T19:46:34Z",
+ "body": "One issue is that mutex stuff needs to already be `Arc<Mutex<>>` to work. It should however then clone the arc as required",
+ "path": "mrsbfh-macros/src/lib.rs",
+ "line": 275,
+ "diff_hunk": "@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {\n tracing::info!(\"Got command: {}\", command);\n }\n // Make sure this is immutable\n- let args: Vec<&str> = split.collect();\n+ let args_raw: Vec<String> = split.collect();\n+ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());\n+ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));\n+\n+ let mut msg = mrsbfh::commands::Message::new();\n+ // TODO insert all the things in the function args\n+ msg.extensions_mut().insert(std::sync::Arc::clone(&args));"
+ },
+ {
+ "author": "donicrosby",
+ "created_at": "2022-03-12T20:24:01Z",
+ "body": "That shouldn't be too difficult, I think that's fairly standard for something like this.\r\n\r\nThanks for the quick fix! I'll test it out later tonight! ",
+ "path": "mrsbfh-macros/src/lib.rs",
+ "line": 275,
+ "diff_hunk": "@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {\n tracing::info!(\"Got command: {}\", command);\n }\n // Make sure this is immutable\n- let args: Vec<&str> = split.collect();\n+ let args_raw: Vec<String> = split.collect();\n+ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());\n+ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));\n+\n+ let mut msg = mrsbfh::commands::Message::new();\n+ // TODO insert all the things in the function args\n+ msg.extensions_mut().insert(std::sync::Arc::clone(&args));"
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T20:25:00Z",
+ "body": "Sure no problem :)",
+ "path": "mrsbfh-macros/src/lib.rs",
+ "line": 275,
+ "diff_hunk": "@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {\n tracing::info!(\"Got command: {}\", command);\n }\n // Make sure this is immutable\n- let args: Vec<&str> = split.collect();\n+ let args_raw: Vec<String> = split.collect();\n+ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());\n+ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));\n+\n+ let mut msg = mrsbfh::commands::Message::new();\n+ // TODO insert all the things in the function args\n+ msg.extensions_mut().insert(std::sync::Arc::clone(&args));"
+ },
+ {
+ "author": "MTRNord",
+ "created_at": "2022-03-12T20:25:34Z",
+ "body": "If you have any bugs feel free to mention them in this PR :) I only ran the example bot as I have no other to test with currently. So there may be stuff i missed",
+ "path": "mrsbfh-macros/src/lib.rs",
+ "line": 275,
+ "diff_hunk": "@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {\n tracing::info!(\"Got command: {}\", command);\n }\n // Make sure this is immutable\n- let args: Vec<&str> = split.collect();\n+ let args_raw: Vec<String> = split.collect();\n+ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());\n+ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));\n+\n+ let mut msg = mrsbfh::commands::Message::new();\n+ // TODO insert all the things in the function args\n+ msg.extensions_mut().insert(std::sync::Arc::clone(&args));"
+ }
+ ]
+}
diff --git a/pulls/13.md b/pulls/13.md
@@ -0,0 +1,191 @@
+# PR #13 Add a way to add custom state and update sdk
+
+- **Status:** closed
+- **Author:** @MTRNord
+- **Created:** 2022-03-12T18:54:50Z
+- **Branch:** MTRNord/issue-12 → main
+- **Closed:** 2025-06-16T22:41:06Z
+- **Labels:** enhancement
+- **Assignees:** @MTRNord
+- **Diff:** [13.diff](./13.diff)
+
+---
+
+This is heavily building upon what http and axum do. In fact there is a lot of reused code.
+
+This allows for function defined like this:
+
+```rust
+#[command(help = "`!hello_world` - Prints \"hello world\".")]
+pub async fn hello_world<'a>(
+ Extension(tx): Extension<mrsbfh::Sender>,
+) -> Result<(), Error> {
+ let content = RoomMessageEventContent::notice_plain("Hello World!");
+
+ tx.lock().await.send(content).await?;
+ Ok(())
+}
+```
+
+Downsides:
+
+- It adds a Mutex to mutable stuff.
+- It needs stuff to be strictly async (I guess that was needed before too?)
+- A little more verbose?
+
+Pros:
+
+- You can pass anything to it as long as it is defined in your sync handler. (the macro auto extracts the arguments from it)
+- You don't need to pass all things to every command
+
+Fixes #12
+
+Missing is:
+
+- [x] Getting user supplied things from the sync handler and passing it on.
+
+Tests seem to work:
+
+
+
+
+
+## Review comments
+
+### @MTRNord on `mrsbfh-macros/src/lib.rs`:275 — 2022-03-12T18:57:36Z
+
+```diff
+@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ tracing::info!("Got command: {}", command);
+ }
+ // Make sure this is immutable
+- let args: Vec<&str> = split.collect();
++ let args_raw: Vec<String> = split.collect();
++ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());
++ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));
++
++ let mut msg = mrsbfh::commands::Message::new();
++ // TODO insert all the things in the function args
++ msg.extensions_mut().insert(std::sync::Arc::clone(&args));
+```
+
+@donicrosby This is basically where now your wish comes in. I basically need to do this dynamically for all the things that the function provides when you use the macro. This is currently still hardcoded to only do the config. But the example bot should give you an idea how the api will look like. Which means it should be fairly similiar as before with not too many changes needed
+
+### @MTRNord on `mrsbfh-macros/src/lib.rs`:275 — 2022-03-12T19:28:20Z
+
+```diff
+@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ tracing::info!("Got command: {}", command);
+ }
+ // Make sure this is immutable
+- let args: Vec<&str> = split.collect();
++ let args_raw: Vec<String> = split.collect();
++ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());
++ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));
++
++ let mut msg = mrsbfh::commands::Message::new();
++ // TODO insert all the things in the function args
++ msg.extensions_mut().insert(std::sync::Arc::clone(&args));
+```
+
+Pushed the rest now. So in theory this PR should work. It compiles but I didnt actually test if it works in practice
+
+### @MTRNord on `mrsbfh-macros/src/lib.rs`:275 — 2022-03-12T19:46:34Z
+
+```diff
+@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ tracing::info!("Got command: {}", command);
+ }
+ // Make sure this is immutable
+- let args: Vec<&str> = split.collect();
++ let args_raw: Vec<String> = split.collect();
++ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());
++ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));
++
++ let mut msg = mrsbfh::commands::Message::new();
++ // TODO insert all the things in the function args
++ msg.extensions_mut().insert(std::sync::Arc::clone(&args));
+```
+
+One issue is that mutex stuff needs to already be `Arc<Mutex<>>` to work. It should however then clone the arc as required
+
+### @donicrosby on `mrsbfh-macros/src/lib.rs`:275 — 2022-03-12T20:24:01Z
+
+```diff
+@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ tracing::info!("Got command: {}", command);
+ }
+ // Make sure this is immutable
+- let args: Vec<&str> = split.collect();
++ let args_raw: Vec<String> = split.collect();
++ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());
++ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));
++
++ let mut msg = mrsbfh::commands::Message::new();
++ // TODO insert all the things in the function args
++ msg.extensions_mut().insert(std::sync::Arc::clone(&args));
+```
+
+That shouldn't be too difficult, I think that's fairly standard for something like this.
+
+Thanks for the quick fix! I'll test it out later tonight!
+
+### @MTRNord on `mrsbfh-macros/src/lib.rs`:275 — 2022-03-12T20:25:00Z
+
+```diff
+@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ tracing::info!("Got command: {}", command);
+ }
+ // Make sure this is immutable
+- let args: Vec<&str> = split.collect();
++ let args_raw: Vec<String> = split.collect();
++ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());
++ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));
++
++ let mut msg = mrsbfh::commands::Message::new();
++ // TODO insert all the things in the function args
++ msg.extensions_mut().insert(std::sync::Arc::clone(&args));
+```
+
+Sure no problem :)
+
+### @MTRNord on `mrsbfh-macros/src/lib.rs`:275 — 2022-03-12T20:25:34Z
+
+```diff
+@@ -267,15 +266,18 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ tracing::info!("Got command: {}", command);
+ }
+ // Make sure this is immutable
+- let args: Vec<&str> = split.collect();
++ let args_raw: Vec<String> = split.collect();
++ let args: std::sync::Arc<Vec<String>> = std::sync::Arc::new(args_raw.clone());
++ let tx = std::sync::Arc::new(std::sync::Mutex::new(tx));
++
++ let mut msg = mrsbfh::commands::Message::new();
++ // TODO insert all the things in the function args
++ msg.extensions_mut().insert(std::sync::Arc::clone(&args));
+```
+
+If you have any bugs feel free to mention them in this PR :) I only ran the example bot as I have no other to test with currently. So there may be stuff i missed
+
+
+## Comments
+
+### @MTRNord — 2022-03-12T19:54:27Z
+
+Followup issue: #14
+
+### @MTRNord — 2022-03-13T20:13:23Z
+
+Some feedback from jplatte:
+
+> One thing I noticed is that you seem to have an automatic 'extension' of Arc<Mutex<mrsbfh::Sender>>. I would recomment instead making the Arc<Mutex<_>> part internal so that the sender is Clone + Send + Sync and you make it an extractor by itself (no Extension wrapping needed)
+
+> Extension can still make sense if you want users to be able to add their own context
+
+> In the SDK the Ctx type and register_event_handler_context fills the same role
+
+### @MTRNord — 2024-05-24T23:10:22Z
+
+Main missing TODO is now fixing the comments I think
+
diff --git a/pulls/2.diff b/pulls/2.diff
@@ -0,0 +1,237 @@
+diff --git a/example-bot/Cargo.toml b/example-bot/Cargo.toml
+index 6fb9533..ffbc3df 100644
+--- a/example-bot/Cargo.toml
++++ b/example-bot/Cargo.toml
+@@ -1,13 +1,14 @@
++cargo-features = ["edition2021"]
+ [package]
+ name = "example-bot"
+ version = "0.1.0"
+ authors = ["MTRNord <mtrnord1@gmail.com>"]
+-edition = "2018"
++edition = "2021"
+
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+ [dependencies.matrix-sdk]
+-version = "0.2"
++version = "0.3"
+
+ [dependencies]
+ mrsbfh = {path = "../mrsbfh"}
+@@ -15,8 +16,9 @@ serde = "1.0"
+ tracing = "0.1"
+ tracing-subscriber = "0.2"
+ tracing-futures = "0.2.4"
+-tokio = { version = "0.2", features = ["full"] }
+-clap = "3.0.0-beta.2"
++tokio = { version = "1", features = ["full"] }
++clap = "=3.0.0-beta.2"
++clap_derive = "=3.0.0-beta.2"
+ async-trait = "0.1.41"
+ thiserror = "1.0"
+ regex = "1.4.3"
+diff --git a/example-bot/src/matrix/mod.rs b/example-bot/src/matrix/mod.rs
+index 4e2caa7..61c8e18 100644
+--- a/example-bot/src/matrix/mod.rs
++++ b/example-bot/src/matrix/mod.rs
+@@ -51,7 +51,7 @@ pub async fn setup(config: Config<'_>) -> Result<Client, Box<dyn Error>> {
+ Ok(login_response) => {
+ info!("Session: {:#?}", login_response);
+ let session = Session {
+- homeserver: client.homeserver().to_string(),
++ homeserver: client.homeserver().await.to_string(),
+ user_id: login_response.user_id.to_string(),
+ access_token: login_response.access_token,
+ device_id: login_response.device_id.into(),
+@@ -73,7 +73,7 @@ pub async fn start_sync(
+ config: Config<'static>,
+ ) -> Result<(), Box<dyn Error>> {
+ client
+- .add_event_emitter(Box::new(sync::Bot::new(client.clone(), config.clone())))
++ .set_event_handler(Box::new(sync::Bot::new(client.clone(), config.clone())))
+ .await;
+
+ info!("Starting full Sync...");
+diff --git a/example-bot/src/matrix/sync.rs b/example-bot/src/matrix/sync.rs
+index 2ee388a..e10c999 100644
+--- a/example-bot/src/matrix/sync.rs
++++ b/example-bot/src/matrix/sync.rs
+@@ -6,7 +6,7 @@ use matrix_sdk::{
+ room::member::MemberEventContent, room::message::MessageEventContent, StrippedStateEvent,
+ SyncMessageEvent,
+ },
+- Client, EventEmitter, SyncRoom,
++ Client, EventHandler, room::Room,
+ };
+ use tokio::sync::mpsc;
+ use tracing::*;
+@@ -29,14 +29,14 @@ impl Bot {
+ #[mrsbfh::commands::commands]
+ #[mrsbfh::utils::autojoin]
+ #[async_trait]
+-impl EventEmitter for Bot {
+- async fn on_room_message(&self, room: SyncRoom, event: &SyncMessageEvent<MessageEventContent>) {
++impl EventHandler for Bot {
++ async fn on_room_message(&self, room: Room, event: &SyncMessageEvent<MessageEventContent>) {
+ println!("message example")
+ }
+
+ async fn on_stripped_state_member(
+ &self,
+- room: SyncRoom,
++ room: Room,
+ room_member: &StrippedStateEvent<MemberEventContent>,
+ _: Option<MemberEventContent>,
+ ) {
+diff --git a/mrsbfh-macros/Cargo.toml b/mrsbfh-macros/Cargo.toml
+index 2b94b30..cfd0e22 100644
+--- a/mrsbfh-macros/Cargo.toml
++++ b/mrsbfh-macros/Cargo.toml
+@@ -1,8 +1,9 @@
++cargo-features = ["edition2021"]
+ [package]
+ name = "mrsbfh-macros"
+ version = "0.1.0"
+ authors = ["MTRNord <mtrnord1@gmail.com>"]
+-edition = "2018"
++edition = "2021"
+
+ [lib]
+ proc-macro = true
+diff --git a/mrsbfh-macros/src/lib.rs b/mrsbfh-macros/src/lib.rs
+index 58732b8..8597ff5 100644
+--- a/mrsbfh-macros/src/lib.rs
++++ b/mrsbfh-macros/src/lib.rs
+@@ -103,10 +103,9 @@ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
+ });
+ let mut help_format_string = String::from("{}");
+ input.variants.iter().for_each(|_| {
+- help_format_string = format!("{}{}", help_format_string,"{}");
++ help_format_string = format!("{}{}", help_format_string, "{}");
+ });
+
+-
+ let bot_name = match get_arg(
+ input.span(),
+ args.clone(),
+@@ -230,47 +229,41 @@ pub fn autojoin(_: TokenStream, input: TokenStream) -> TokenStream {
+ if method.sig.ident == "on_stripped_state_member" {
+ let original = method.block.clone();
+ let new_block = syn::parse_quote! {
+- {
+- #original
++ {
++ #original
+
+- // Autojoin logic
+- if room_member.state_key != self.client.user_id().await.unwrap() {
+- warn!("Got invite that isn't for us");
+- return;
+- }
+- if let matrix_sdk::SyncRoom::Invited(room) = room {
+- let room_id = {
+- let room = room.read().await;
+- room.room_id.clone()
+- };
+- let client = self.client.clone();
+-
+- tokio::spawn(async move {
+- info!("Autojoining room {}", room_id);
++ // Autojoin logic
++ if room_member.state_key != self.client.user_id().await.unwrap() {
++ warn!("Got invite that isn't for us");
++ return;
++ }
++ if let matrix_sdk::room::Room::Invited(room) = room {
++ info!("Autojoining room {}", room.room_id());
+ let mut delay = 2;
+
+- while let Err(err) = client.join_room_by_id(&room_id).await {
++ while let Err(err) = room.accept_invitation().await {
+ // retry autojoin due to synapse sending invites, before the
+ // invited user can join for more information see
+ // https://github.com/matrix-org/synapse/issues/4345
+ error!(
+ "Failed to join room {} ({:?}), retrying in {}s",
+- room_id, err, delay
++ room.room_id(),
++ err,
++ delay
+ );
+
+- tokio::time::delay_for(tokio::time::Duration::from_secs(delay)).await;
++ tokio::time::sleep(tokio::time::Duration::from_secs(delay)).await;
+ delay *= 2;
+
+ if delay > 3600 {
+- error!("Can't join room {} ({:?})", room_id, err);
++ error!("Can't join room {} ({:?})", room.room_id(), err);
+ break;
+ }
+ }
+- info!("Successfully joined room {}", room_id);
+- });
++ info!("Successfully joined room {}", room.room_id());
++ }
+ }
+- }
+- };
++ };
+ method.block = new_block;
+ }
+ }
+@@ -315,9 +308,12 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ #original
+
+ // Command matching logic
+- if let matrix_sdk::SyncRoom::Joined(room) = room {
++ if let matrix_sdk::room::Room::Joined(room) = room {
+ let msg_body = if let matrix_sdk::events::SyncMessageEvent {
+- content: matrix_sdk::events::room::message::MessageEventContent::Text(matrix_sdk::events::room::message::TextMessageEventContent { body: msg_body, .. }),
++ content: matrix_sdk::events::room::message::MessageEventContent {
++ msgtype: matrix_sdk::events::room::message::MessageType::Text(matrix_sdk::events::room::message::TextMessageEventContent { body: msg_body, .. }),
++ ..
++ },
+ ..
+ } = event
+ {
+@@ -332,7 +328,7 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ let sender = event.sender.clone().to_string();
+
+ let (tx, mut rx) = mpsc::channel(100);
+- let room_id = room.read().await.clone().room_id;
++ let room_id = room.room_id();
+
+ let cloned_config = self.config.clone();
+ let cloned_client = self.client.clone();
+diff --git a/mrsbfh/Cargo.toml b/mrsbfh/Cargo.toml
+index 3ba15e5..f64056c 100644
+--- a/mrsbfh/Cargo.toml
++++ b/mrsbfh/Cargo.toml
+@@ -1,13 +1,14 @@
++cargo-features = ["edition2021"]
+ [package]
+ name = "mrsbfh"
+ version = "0.1.0"
+ authors = ["MTRNord <mtrnord1@gmail.com>"]
+-edition = "2018"
++edition = "2021"
+
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+ [dependencies.matrix-sdk]
+-version = "0.2"
++version = "0.3"
+
+ [dependencies]
+ url = "2.2.1"
+@@ -17,7 +18,7 @@ thiserror = "1.0"
+ # Command macros
+ mrsbfh-macros = {path = "../mrsbfh-macros", optional = true}
+
+-tokio = { version = "0.2", features = ["full"] }
++tokio = { version = "1", features = ["full"] }
+ tracing = "0.1"
+
+ serde = "1.0"
diff --git a/pulls/2.json b/pulls/2.json
@@ -0,0 +1,18 @@
+{
+ "number": 2,
+ "title": "Update to recent versions",
+ "state": "merged",
+ "diff_file": "2.diff",
+ "author": "MTRNord",
+ "created_at": "2021-09-08T17:43:20Z",
+ "closed_at": "2021-09-08T17:43:26Z",
+ "merged_at": "2021-09-08T17:43:26Z",
+ "base_ref": "main",
+ "head_ref": "MTRNord/edition-2021",
+ "labels": [],
+ "assignees": [],
+ "requested_reviewers": [],
+ "body": "",
+ "comments": [],
+ "review_comments": []
+}
diff --git a/pulls/2.md b/pulls/2.md
@@ -0,0 +1,13 @@
+# PR #2 Update to recent versions
+
+- **Status:** merged
+- **Author:** @MTRNord
+- **Created:** 2021-09-08T17:43:20Z
+- **Branch:** MTRNord/edition-2021 → main
+- **Merged:** 2021-09-08T17:43:26Z
+- **Diff:** [2.diff](./2.diff)
+
+---
+
+_No description._
+
diff --git a/pulls/4.diff b/pulls/4.diff
@@ -0,0 +1,115 @@
+diff --git a/mrsbfh-macros/src/lib.rs b/mrsbfh-macros/src/lib.rs
+index f1c1bf9..2179698 100644
+--- a/mrsbfh-macros/src/lib.rs
++++ b/mrsbfh-macros/src/lib.rs
+@@ -229,41 +229,41 @@ pub fn autojoin(_: TokenStream, input: TokenStream) -> TokenStream {
+ if method.sig.ident == "on_stripped_state_member" {
+ let original = method.block.clone();
+ let new_block = syn::parse_quote! {
+- {
+- #original
++ {
++ #original
+
+- // Autojoin logic
+- if room_member.state_key != self.client.user_id().await.unwrap() {
+- warn!("Got invite that isn't for us");
+- return;
+- }
+- if let matrix_sdk::room::Room::Invited(room) = room {
+- info!("Autojoining room {}", room.room_id());
+- let mut delay = 2;
+-
+- while let Err(err) = room.accept_invitation().await {
+- // retry autojoin due to synapse sending invites, before the
+- // invited user can join for more information see
+- // https://github.com/matrix-org/synapse/issues/4345
+- error!(
+- "Failed to join room {} ({:?}), retrying in {}s",
+- room.room_id(),
+- err,
+- delay
+- );
+-
+- tokio::time::sleep(tokio::time::Duration::from_secs(delay)).await;
+- delay *= 2;
+-
+- if delay > 3600 {
+- error!("Can't join room {} ({:?})", room.room_id(), err);
+- break;
+- }
++ // Autojoin logic
++ if room_member.state_key != self.client.user_id().await.unwrap() {
++ warn!("Got invite that isn't for us");
++ return;
++ }
++ if let matrix_sdk::room::Room::Invited(room) = room {
++ info!("Autojoining room {}", room.room_id());
++ let mut delay = 2;
++
++ while let Err(err) = room.accept_invitation().await {
++ // retry autojoin due to synapse sending invites, before the
++ // invited user can join for more information see
++ // https://github.com/matrix-org/synapse/issues/4345
++ error!(
++ "Failed to join room {} ({:?}), retrying in {}s",
++ room.room_id(),
++ err,
++ delay
++ );
++
++ tokio::time::sleep(tokio::time::Duration::from_secs(delay)).await;
++ delay *= 2;
++
++ if delay > 3600 {
++ error!("Can't join room {} ({:?})", room.room_id(), err);
++ break;
+ }
+- info!("Successfully joined room {}", room.room_id());
+ }
++ info!("Successfully joined room {}", room.room_id());
+ }
+- };
++ }
++ };
+ method.block = new_block;
+ }
+ }
+@@ -334,17 +334,24 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ let cloned_client = self.client.clone();
+ tokio::spawn(async move {
+ let whitespace_deduplicator_magic = regex::Regex::new(r"\s+").unwrap();
++ let command_matcher_magic = regex::Regex::new(r"!([\w-]+)").unwrap();
+ let normalized_body = whitespace_deduplicator_magic.replace_all(&msg_body, " ");
+ let mut split = msg_body.split_whitespace();
+
+- let command_raw = split.next().expect("This is not a command");
+- let command = command_raw.to_lowercase();
+- info!("Got command: {}", command);
+-
++ let command_raw = split.next().expect("This is not a command").to_lowercase();
++ let command = command_matcher_magic.captures(command_raw.as_str())
++ .map_or(String::new(), |caps| {
++ caps.get(1)
++ .map_or(String::new(),
++ |m| String::from(m.as_str()))
++ });
++ if !command.is_empty() {
++ info!("Got command: {}", command);
++ }
+ // Make sure this is immutable
+ let args: Vec<&str> = split.collect();
+ if let Err(e) = match_command(
+- command.replace("!", "").as_str(),
++ command.as_str(),
+ cloned_client.clone(),
+ cloned_config.clone(),
+ tx,
+@@ -355,6 +362,7 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ {
+ error!("{}", e);
+ }
++
+ });
+
+ while let Some(v) = rx.recv().await {
diff --git a/pulls/4.json b/pulls/4.json
@@ -0,0 +1,22 @@
+{
+ "number": 4,
+ "title": "Fixed bots not looking for ! at the start of commands",
+ "state": "merged",
+ "diff_file": "4.diff",
+ "author": "donicrosby",
+ "created_at": "2021-10-22T21:54:44Z",
+ "closed_at": "2021-10-24T12:49:46Z",
+ "merged_at": "2021-10-24T12:49:46Z",
+ "base_ref": "main",
+ "head_ref": "fix-command-flow",
+ "labels": [
+ "enhancement"
+ ],
+ "assignees": [
+ "MTRNord"
+ ],
+ "requested_reviewers": [],
+ "body": "This PR adds support for bots misinterpreting the messages \"!cmd\" and \"cmd\" as equivalent. It would cause unwanted spam and processing cycles. I have added a regex that looks for the \"!\" at the start of the command and then strips it out or returns an empty string if it does not find a \"!\" at the start of the message.",
+ "comments": [],
+ "review_comments": []
+}
diff --git a/pulls/4.md b/pulls/4.md
@@ -0,0 +1,15 @@
+# PR #4 Fixed bots not looking for ! at the start of commands
+
+- **Status:** merged
+- **Author:** @donicrosby
+- **Created:** 2021-10-22T21:54:44Z
+- **Branch:** fix-command-flow → main
+- **Merged:** 2021-10-24T12:49:46Z
+- **Labels:** enhancement
+- **Assignees:** @MTRNord
+- **Diff:** [4.diff](./4.diff)
+
+---
+
+This PR adds support for bots misinterpreting the messages "!cmd" and "cmd" as equivalent. It would cause unwanted spam and processing cycles. I have added a regex that looks for the "!" at the start of the command and then strips it out or returns an empty string if it does not find a "!" at the start of the message.
+
diff --git a/pulls/6.diff b/pulls/6.diff
@@ -0,0 +1,75 @@
+diff --git a/example-bot/Cargo.toml b/example-bot/Cargo.toml
+index a72742a..ab47333 100644
+--- a/example-bot/Cargo.toml
++++ b/example-bot/Cargo.toml
+@@ -18,7 +18,7 @@ serde = "1.0"
+ tracing = "0.1"
+ tracing-subscriber = "0.3.1"
+ tracing-futures = "0.2.4"
+-tokio = { version = "1", features = ["full"] }
++tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "macros"] }
+ clap = "=3.0.0-beta.2"
+ clap_derive = "=3.0.0-beta.2"
+ async-trait = "0.1.41"
+diff --git a/mrsbfh-macros/src/lib.rs b/mrsbfh-macros/src/lib.rs
+index 5666f22..1555b80 100644
+--- a/mrsbfh-macros/src/lib.rs
++++ b/mrsbfh-macros/src/lib.rs
+@@ -254,13 +254,11 @@ pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
+ let cloned_config = config.clone();
+ let cloned_client = client.clone();
+ tokio::spawn(async move {
+- let whitespace_deduplicator_magic = regex::Regex::new(r"\s+").unwrap();
+- let command_matcher_magic = regex::Regex::new(r"!([\w-]+)").unwrap();
+- let normalized_body = whitespace_deduplicator_magic.replace_all(&msg_body, " ");
++ let normalized_body = mrsbfh::commands::command_utils::WHITESPACE_DEDUPLICATOR_MAGIC.replace_all(&msg_body, " ");
+ let mut split = msg_body.split_whitespace();
+
+ let command_raw = split.next().expect("This is not a command").to_lowercase();
+- let command = command_matcher_magic.captures(command_raw.as_str())
++ let command = mrsbfh::commands::command_utils::COMMAND_MATCHER_MAGIC.captures(command_raw.as_str())
+ .map_or(String::new(), |caps| {
+ caps.get(1)
+ .map_or(String::new(),
+diff --git a/mrsbfh/Cargo.toml b/mrsbfh/Cargo.toml
+index 37e1abd..188e14d 100644
+--- a/mrsbfh/Cargo.toml
++++ b/mrsbfh/Cargo.toml
+@@ -23,7 +23,7 @@ thiserror = "1.0"
+ # Command macros
+ mrsbfh-macros = {version = "0.2.0", path = "../mrsbfh-macros", optional = true}
+
+-tokio = { version = "1", features = ["full"] }
++tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
+ tracing = "0.1"
+
+ serde = "1.0"
+@@ -34,6 +34,7 @@ pulldown-cmark = "0.8.0" # For generating the help text
+
+ regex = "1.4"
+ async-trait = "0.1"
++lazy_static = "1"
+
+ [features]
+ default = ["macros", "native-tls"]
+diff --git a/mrsbfh/src/commands.rs b/mrsbfh/src/commands.rs
+index 5f24e7e..84977df 100644
+--- a/mrsbfh/src/commands.rs
++++ b/mrsbfh/src/commands.rs
+@@ -119,5 +119,16 @@
+ //! in the example.
+ //!
+
++pub mod command_utils {
++ use lazy_static::lazy_static;
++
++ lazy_static! {
++ pub static ref WHITESPACE_DEDUPLICATOR_MAGIC: regex::Regex =
++ regex::Regex::new(r"\s+").unwrap();
++ pub static ref COMMAND_MATCHER_MAGIC: regex::Regex =
++ regex::Regex::new(r"!([\w-]+)").unwrap();
++ }
++}
++
+ #[cfg(feature = "macros")]
+ pub use mrsbfh_macros::{command, command_generate, commands};
diff --git a/pulls/6.json b/pulls/6.json
@@ -0,0 +1,41 @@
+{
+ "number": 6,
+ "title": "Regexs use lazy_static macro",
+ "state": "merged",
+ "diff_file": "6.diff",
+ "author": "donicrosby",
+ "created_at": "2021-11-10T02:16:53Z",
+ "closed_at": "2021-11-13T14:41:19Z",
+ "merged_at": "2021-11-13T14:41:19Z",
+ "base_ref": "main",
+ "head_ref": "use-lazy-static-macro",
+ "labels": [],
+ "assignees": [],
+ "requested_reviewers": [],
+ "body": "Added support for the bot to use pre-compiled regexs using the `lazy_static` macro",
+ "comments": [
+ {
+ "author": "MTRNord",
+ "created_at": "2021-11-13T14:41:15Z",
+ "body": "LGTM. :)"
+ }
+ ],
+ "review_comments": [
+ {
+ "author": "donicrosby",
+ "created_at": "2021-11-10T02:40:51Z",
+ "body": "Don't bring all of tokio to compile to keep the binary smaller and the build faster",
+ "path": "example-bot/Cargo.toml",
+ "line": 21,
+ "diff_hunk": "@@ -18,7 +18,7 @@ serde = \"1.0\"\n tracing = \"0.1\"\n tracing-subscriber = \"0.3.1\"\n tracing-futures = \"0.2.4\"\n-tokio = { version = \"1\", features = [\"full\"] }\n+tokio = { version = \"1\", features = [\"rt\", \"rt-multi-thread\", \"sync\", \"macros\"] }"
+ },
+ {
+ "author": "donicrosby",
+ "created_at": "2021-11-10T03:46:23Z",
+ "body": "Same thing here",
+ "path": "mrsbfh/Cargo.toml",
+ "line": 26,
+ "diff_hunk": "@@ -23,7 +23,7 @@ thiserror = \"1.0\"\n # Command macros\n mrsbfh-macros = {version = \"0.2.0\", path = \"../mrsbfh-macros\", optional = true}\n \n-tokio = { version = \"1\", features = [\"full\"] }\n+tokio = { version = \"1\", features =[\"rt\", \"rt-multi-thread\", \"macros\"]}"
+ }
+ ]
+}
diff --git a/pulls/6.md b/pulls/6.md
@@ -0,0 +1,49 @@
+# PR #6 Regexs use lazy_static macro
+
+- **Status:** merged
+- **Author:** @donicrosby
+- **Created:** 2021-11-10T02:16:53Z
+- **Branch:** use-lazy-static-macro → main
+- **Merged:** 2021-11-13T14:41:19Z
+- **Diff:** [6.diff](./6.diff)
+
+---
+
+Added support for the bot to use pre-compiled regexs using the `lazy_static` macro
+
+
+## Review comments
+
+### @donicrosby on `example-bot/Cargo.toml`:21 — 2021-11-10T02:40:51Z
+
+```diff
+@@ -18,7 +18,7 @@ serde = "1.0"
+ tracing = "0.1"
+ tracing-subscriber = "0.3.1"
+ tracing-futures = "0.2.4"
+-tokio = { version = "1", features = ["full"] }
++tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "macros"] }
+```
+
+Don't bring all of tokio to compile to keep the binary smaller and the build faster
+
+### @donicrosby on `mrsbfh/Cargo.toml`:26 — 2021-11-10T03:46:23Z
+
+```diff
+@@ -23,7 +23,7 @@ thiserror = "1.0"
+ # Command macros
+ mrsbfh-macros = {version = "0.2.0", path = "../mrsbfh-macros", optional = true}
+
+-tokio = { version = "1", features = ["full"] }
++tokio = { version = "1", features =["rt", "rt-multi-thread", "macros"]}
+```
+
+Same thing here
+
+
+## Comments
+
+### @MTRNord — 2021-11-13T14:41:15Z
+
+LGTM. :)
+