commit 39b5eccf1d5d92639aef21a9c5c0e8bb09aa0629
parent ebb63073d1473f540d5ee1c8d1d36e56df734b30
Author: donicrosby <donicrosby1995@gmail.com>
Date: Tue, 9 Nov 2021 19:19:21 -0500
Merge branch 'main' of https://github.com/MTRNord/mrsbfh into main
Diffstat:
12 files changed, 205 insertions(+), 250 deletions(-)
diff --git a/README.md b/README.md
@@ -10,14 +10,14 @@ A toolkit for writing commandbots more efficient in rust for matrix.
## How to use
-To use it you need to add mrsbfh ust like any regular create:
+To use it you need to add mrsbfh just like any regular create:
```
-mrsbfh = {git = "https://github.com/MTRNord/mrsbfh"}
+mrsbfh = "0.2.0"
```
After that there are the possible helpers available that are further described in the docs.
-As there was no release yet, you can open them using `cargo +nigtly doc -p mrsbfh --open` in any crate depending on it or after cloning the repository.
+As there was no release yet, you can open them using `cargo doc -p mrsbfh --open` in any crate depending on it or after cloning the repository.
For a minimal example of the bot checkout the crate under the `example-bot` folder which is a minimal version of what is needed.
diff --git a/example-bot/Cargo.toml b/example-bot/Cargo.toml
@@ -1,20 +1,22 @@
-cargo-features = ["edition2021"]
[package]
name = "example-bot"
-version = "0.1.0"
+version = "0.2.0"
authors = ["MTRNord <mtrnord1@gmail.com>"]
edition = "2021"
+license = "AGPL-3.0-or-later"
+publish = false
+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.3"
+version = "0.4.1"
[dependencies]
-mrsbfh = {path = "../mrsbfh"}
+mrsbfh = {version = "0.2.0", path = "../mrsbfh"}
serde = "1.0"
tracing = "0.1"
-tracing-subscriber = "0.2"
+tracing-subscriber = "0.3.1"
tracing-futures = "0.2.4"
tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "macros"] }
clap = "=3.0.0-beta.2"
diff --git a/example-bot/src/commands/hello_world.rs b/example-bot/src/commands/hello_world.rs
@@ -1,15 +1,19 @@
use crate::config::Config;
use crate::errors::Error;
-use matrix_sdk::events::{room::message::MessageEventContent, AnyMessageEventContent};
+use matrix_sdk::ruma::events::{room::message::MessageEventContent, AnyMessageEventContent};
+use matrix_sdk::ruma::RoomId;
use matrix_sdk::Client;
use mrsbfh::commands::command;
+use std::sync::Arc;
+use tokio::sync::Mutex;
#[command(help = "`!hello_world` - Prints \"hello world\".")]
pub async fn hello_world<'a>(
_client: Client,
- mut tx: mrsbfh::Sender,
- _config: Config<'a>,
+ tx: mrsbfh::Sender,
+ _config: Arc<Mutex<Config<'a>>>,
_sender: String,
+ _room_id: RoomId,
mut _args: Vec<&str>,
) -> Result<(), Error>
where
diff --git a/example-bot/src/errors.rs b/example-bot/src/errors.rs
@@ -1,4 +1,4 @@
-use matrix_sdk::events::AnyMessageEventContent;
+use matrix_sdk::ruma::events::AnyMessageEventContent;
use thiserror::Error as ThisError;
#[derive(ThisError, Debug)]
diff --git a/example-bot/src/matrix/mod.rs b/example-bot/src/matrix/mod.rs
@@ -2,10 +2,8 @@ use crate::config::Config;
use matrix_sdk::{Client, ClientConfig, Session as SDKSession, SyncSettings};
use mrsbfh::url::Url;
use mrsbfh::utils::Session;
-use std::convert::TryFrom;
-use std::error::Error;
-use std::fs;
-use std::path::Path;
+use std::{convert::TryFrom, error::Error, fs, path::Path, sync::Arc};
+use tokio::sync::Mutex;
use tracing::*;
mod sync;
@@ -30,7 +28,7 @@ pub async fn setup(config: Config<'_>) -> Result<Client, Box<dyn Error>> {
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(),
+ user_id: matrix_sdk::ruma::UserId::try_from(session.user_id.as_str()).unwrap(),
};
if let Err(e) = client.restore_login(session).await {
@@ -72,8 +70,13 @@ pub async fn start_sync(
client: &mut Client,
config: Config<'static>,
) -> Result<(), Box<dyn Error>> {
+ client.register_event_handler(mrsbfh::sync::autojoin).await;
+
+ let config = Arc::new(Mutex::new(config));
client
- .set_event_handler(Box::new(sync::Bot::new(client.clone(), config.clone())))
+ .register_event_handler(move |ev, room, client| {
+ sync::on_room_message(ev, room, client, config.clone())
+ })
.await;
info!("Starting full Sync...");
diff --git a/example-bot/src/matrix/sync.rs b/example-bot/src/matrix/sync.rs
@@ -1,47 +1,19 @@
use crate::commands::match_command;
-use crate::config::Config;
-use matrix_sdk::async_trait;
+use crate::Config;
+use matrix_sdk::Client;
use matrix_sdk::{
- events::{
- room::member::MemberEventContent, room::message::MessageEventContent, StrippedStateEvent,
- SyncMessageEvent,
- },
room::Room,
- Client, EventHandler,
+ ruma::events::{room::message::MessageEventContent, SyncMessageEvent},
};
-use mrsbfh::lazy_static;
-use tokio::sync::mpsc;
-use tracing::*;
-
-#[derive(Debug, Clone)]
-pub struct Bot {
- client: Client,
- config: Config<'static>,
-}
-
-impl Bot {
- pub fn new(client: Client, config: Config<'static>) -> Self {
- Self {
- client,
- config: config.clone(),
- }
- }
-}
+use std::sync::Arc;
+use tokio::sync::Mutex;
#[mrsbfh::commands::commands]
-#[mrsbfh::utils::autojoin]
-#[async_trait]
-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: Room,
- room_member: &StrippedStateEvent<MemberEventContent>,
- _: Option<MemberEventContent>,
- ) {
- println!("autojoin example")
- }
+pub(crate) async fn on_room_message(
+ event: SyncMessageEvent<MessageEventContent>,
+ room: Room,
+ client: Client,
+ config: Arc<Mutex<Config<'static>>>,
+) {
+ println!("message example")
}
diff --git a/mrsbfh-macros/Cargo.toml b/mrsbfh-macros/Cargo.toml
@@ -1,15 +1,19 @@
-cargo-features = ["edition2021"]
[package]
name = "mrsbfh-macros"
-version = "0.1.0"
+version = "0.2.0"
authors = ["MTRNord <mtrnord1@gmail.com>"]
edition = "2021"
+description = "Maros for the mrsbfh crate"
+license = "AGPL-3.0-or-later"
+repository = "https://github.com/MTRNord/mrsbfh"
+keywords = ["matrix", "chat", "framework", "macros", "communication"]
+categories = ["network-programming", "parsing"]
[lib]
proc-macro = true
[dependencies]
-syn = { version= "1.0", features=["full"] }
+syn = { version= "1.0", features = ["full"] }
quote = "1.0"
convert_case = "0.4.0"
proc-macro2 = "1.0"
diff --git a/mrsbfh-macros/src/lib.rs b/mrsbfh-macros/src/lib.rs
@@ -9,8 +9,11 @@ use syn::spanned::Spanned;
/// Used to define a command
///
/// ```compile_fail
+/// use std::sync::Arc;
+/// use tokio::sync::Mutex;
+///
/// #[command(help = "Description")]
-/// async fn hello_world(mut tx: mrsbfh::Sender, config: Config, sender: String, mut args: Vec<&str>) -> Result<(), Box<dyn std::error::Error>> where Config: mrsbfh::config::Loader + Clone {}
+/// 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 {}
/// ```
#[proc_macro_attribute]
pub fn command(args: TokenStream, input: TokenStream) -> TokenStream {
@@ -80,10 +83,10 @@ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
quote! {
#command_string => {
- #command::#command(client, tx, config, sender, args).await
+ #command::#command(client, tx, config, sender, room_id, args).await
},
#command_short => {
- #command::#command(client, tx, config, sender, args).await
+ #command::#command(client, tx, config, sender, room_id, args).await
},
}
});
@@ -144,8 +147,8 @@ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
let owned_html = html.to_owned();
mrsbfh::tokio::spawn(async move {
- let content = matrix_sdk::events::AnyMessageEventContent::RoomMessage(
- matrix_sdk::events::room::message::MessageEventContent::notice_html(
+ let content = matrix_sdk::ruma::events::AnyMessageEventContent::RoomMessage(
+ matrix_sdk::ruma::events::room::message::MessageEventContent::notice_html(
&help_markdown,
owned_html,
),
@@ -159,7 +162,7 @@ pub fn command_generate(args: TokenStream, input: TokenStream) -> TokenStream {
Ok(())
}
- pub async fn match_command<'a>(cmd: &str, client: matrix_sdk::Client, config: Config<'a>, tx: mrsbfh::Sender, sender: String, args: Vec<&str>,) -> Result<(), Error> where Config<'a>: mrsbfh::config::Loader + Clone {
+ 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 {
match cmd {
#(#commands)*
"help" => {
@@ -195,88 +198,12 @@ pub fn config_derive(input: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}
-/// Used to generate code to autojoin when we get a invite for the bot
-///
-/// Requirements:
-///
-/// * Tokio
-/// * Naming of arguments needs to be EXACTLY like in the example
-/// * the async_trait macro needs to be BELOW the autojoin macro
-///
-/// ```compile_fail
-/// #[mrsbfh::utils::autojoin]
-/// #[async_trait]
-/// impl EventEmitter for Bot {
-///
-/// async fn on_stripped_state_member(
-/// &self,
-/// room: SyncRoom,
-/// room_member: &StrippedStateEvent<MemberEventContent>,
-/// _: Option<MemberEventContent>,
-/// ) {
-/// // Your own logic. (Executed BEFORE the autojoin)
-/// }
-/// }
-/// ```
-///
-#[proc_macro_attribute]
-pub fn autojoin(_: TokenStream, input: TokenStream) -> TokenStream {
- let mut input = parse_macro_input!(input as syn::ItemImpl);
- let items = &mut input.items;
-
- for item in items {
- if let syn::ImplItem::Method(method) = item {
- if method.sig.ident == "on_stripped_state_member" {
- let original = method.block.clone();
- let new_block = syn::parse_quote! {
- {
- #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;
- }
- }
- info!("Successfully joined room {}", room.room_id());
- }
- }
- };
- method.block = new_block;
- }
- }
- }
-
- TokenStream::from(quote! {#input})
-}
-
/// Used to generate code to detect commands when we get a message for the bot
///
/// Requirements:
///
/// * Tokio
+/// * Tokio tracing
/// * Naming of arguments needs to be EXACTLY like in the example
/// * the async_trait macro needs to be BELOW the commands macro
/// * The match_command MUST be imported
@@ -285,10 +212,7 @@ pub fn autojoin(_: TokenStream, input: TokenStream) -> TokenStream {
/// use crate::commands::match_command;
///
/// #[mrsbfh::commands::commands]
-/// #[async_trait]
-/// impl EventEmitter for Bot {
-///
-/// async fn on_room_message(&self, room: SyncRoom, event: &SyncMessageEvent<MessageEventContent>) {
+/// async fn on_room_message(event: SyncMessageEvent<MessageEventContent>, room: Room) {
/// // Your own logic. (Executed BEFORE the commands matching)
/// }
/// }
@@ -296,91 +220,85 @@ pub fn autojoin(_: TokenStream, input: TokenStream) -> TokenStream {
///
#[proc_macro_attribute]
pub fn commands(_: TokenStream, input: TokenStream) -> TokenStream {
- let mut input = parse_macro_input!(input as syn::ItemImpl);
- let items = &mut input.items;
-
- for item in items {
- if let syn::ImplItem::Method(method) = item {
- if method.sig.ident == "on_room_message" {
- let original = method.block.clone();
- let new_block = syn::parse_quote! {
+ let mut method = parse_macro_input!(input as syn::ItemFn);
+
+ if method.sig.ident == "on_room_message" {
+ let original = method.block.clone();
+ let new_block = syn::parse_quote! {
+ {
+ #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
{
- #original
-
- // Command matching logic
- 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 {
- msgtype: matrix_sdk::events::room::message::MessageType::Text(matrix_sdk::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) = mpsc::channel(100);
- let room_id = room.room_id();
-
- let cloned_config = self.config.clone();
- let cloned_client = self.client.clone();
- tokio::spawn(async move {
- lazy_static! {
- static ref WHITESPACE_DEDUPLICATOR_MAGIC: regex::Regex = regex::Regex::new(r"\s+").unwrap();
- static ref COMMAND_MATCHER_MAGIC: regex::Regex = 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").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.as_str(),
- cloned_client.clone(),
- cloned_config.clone(),
- tx,
- sender,
- args,
- )
- .await
- {
- error!("{}", e);
- }
-
- });
-
- while let Some(v) = rx.recv().await {
- if let Err(e) = room.send(v, None)
- .await
- {
- error!("{}", e);
- }
- }
+ 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 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").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() {
+ 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);
+ }
+
+ });
+
+ while let Some(v) = rx.recv().await {
+ if let Err(e) = room.send(v, None)
+ .await
+ {
+ tracing::error!("{}", e);
}
}
- };
- method.block = new_block;
+ }
}
- }
+ };
+ method.block = new_block;
}
- TokenStream::from(quote! {#input})
+ TokenStream::from(quote! {#method})
}
diff --git a/mrsbfh/Cargo.toml b/mrsbfh/Cargo.toml
@@ -1,20 +1,23 @@
-cargo-features = ["edition2021"]
[package]
name = "mrsbfh"
-version = "0.1.0"
+version = "0.2.0"
authors = ["MTRNord <mtrnord1@gmail.com>"]
edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+description = "A toolkit for writing commandbots more efficient in rust for matrix."
+license = "AGPL-3.0-or-later"
+repository = "https://github.com/MTRNord/mrsbfh"
+keywords = ["matrix", "chat", "framework", "simple", "communication"]
+categories = ["network-programming"]
+readme = "../README.md"
[package.metadata.docs.rs]
features = ["docs"]
rustdoc-args = ["--cfg", "feature=\"docs\""]
[dependencies.matrix-sdk]
-version = "0.3"
-# No defaults so that end user can add compiletime flags
-default-features = false
+version = "0.4.1"
+default_features = false
+features = ["encryption", "sled_cryptostore", "sled_state_store", "require_auth_for_profile_requests"]
[dependencies]
url = "2.2.1"
@@ -22,7 +25,7 @@ url = "2.2.1"
thiserror = "1.0"
# Command macros
-mrsbfh-macros = {path = "../mrsbfh-macros", optional = true}
+mrsbfh-macros = {version = "0.2.0", path = "../mrsbfh-macros", optional = true}
tokio = { version = "1", features =["rt", "rt-multi-thread", "macros"]}
tracing = "0.1"
@@ -38,6 +41,7 @@ async-trait = "0.1"
lazy_static = "1"
[features]
-default = ["macros"]
+default = ["macros", "native-tls"]
macros = ["mrsbfh-macros"]
-docs = ["macros", "matrix-sdk/encryption", "matrix-sdk/sled_cryptostore", "matrix-sdk/sled_state_store", "matrix-sdk/require_auth_for_profile_requests", "matrix-sdk/native-tls"]
+rustls = ["matrix-sdk/rustls-tls"]
+native-tls = ["matrix-sdk/native-tls"]
diff --git a/mrsbfh/src/lib.rs b/mrsbfh/src/lib.rs
@@ -25,9 +25,10 @@
pub mod commands;
pub mod config;
pub mod errors;
+pub mod sync;
pub mod utils;
-pub type Sender = tokio::sync::mpsc::Sender<matrix_sdk::events::AnyMessageEventContent>;
+pub type Sender = tokio::sync::mpsc::Sender<matrix_sdk::ruma::events::AnyMessageEventContent>;
#[async_trait::async_trait]
pub trait MatrixMessageExt {
@@ -35,7 +36,10 @@ pub trait MatrixMessageExt {
&mut self,
body: String,
formatted_body: Option<String>,
- ) -> Result<(), tokio::sync::mpsc::error::SendError<matrix_sdk::events::AnyMessageEventContent>>;
+ ) -> Result<
+ (),
+ tokio::sync::mpsc::error::SendError<matrix_sdk::ruma::events::AnyMessageEventContent>,
+ >;
}
#[async_trait::async_trait]
@@ -44,12 +48,14 @@ impl MatrixMessageExt for Sender {
&mut self,
body: String,
formatted_body: Option<String>,
- ) -> Result<(), tokio::sync::mpsc::error::SendError<matrix_sdk::events::AnyMessageEventContent>>
- {
+ ) -> Result<
+ (),
+ tokio::sync::mpsc::error::SendError<matrix_sdk::ruma::events::AnyMessageEventContent>,
+ > {
match formatted_body {
Some(formatted_body) => {
- let content = matrix_sdk::events::AnyMessageEventContent::RoomMessage(
- matrix_sdk::events::room::message::MessageEventContent::notice_html(
+ let content = matrix_sdk::ruma::events::AnyMessageEventContent::RoomMessage(
+ matrix_sdk::ruma::events::room::message::MessageEventContent::notice_html(
body,
formatted_body,
),
@@ -58,8 +64,10 @@ impl MatrixMessageExt for Sender {
self.send(content).await
}
None => {
- let content = matrix_sdk::events::AnyMessageEventContent::RoomMessage(
- matrix_sdk::events::room::message::MessageEventContent::notice_plain(body),
+ let content = matrix_sdk::ruma::events::AnyMessageEventContent::RoomMessage(
+ matrix_sdk::ruma::events::room::message::MessageEventContent::notice_plain(
+ body,
+ ),
);
self.send(content).await
}
diff --git a/mrsbfh/src/sync.rs b/mrsbfh/src/sync.rs
@@ -0,0 +1,43 @@
+use matrix_sdk::{
+ room::Room,
+ ruma::events::{room::member::MemberEventContent, StrippedStateEvent},
+ Client,
+};
+use tracing::*;
+
+pub async fn autojoin(
+ room_member: StrippedStateEvent<MemberEventContent>,
+ client: Client,
+ room: Room,
+) {
+ // Autojoin logic
+ if room_member.state_key != 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());
+ }
+}
diff --git a/mrsbfh/src/utils.rs b/mrsbfh/src/utils.rs
@@ -104,9 +104,6 @@ use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use tracing::*;
-#[cfg(feature = "macros")]
-pub use mrsbfh_macros::autojoin;
-
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Session {
/// The homeserver used for this session.