So I want to accept the invite if I am a bot. First I need to find out that the bot has been invited. Right now, we are listening to OriginalSyncRoomMessageEvent, which is only getting Joined(room) message events. It’s not clear from the docs which event I should use for listening for invites. Could it be ThirdPartyInvite? That doesn’t sound very promissing. I’m guessing that event is triggered when someone else in a room that you are in invites someone to the room. Maybe its CallInviteEvent? That seems more likely to be triggered when WE invite someone, rather than when we’re invited. It’s not clear that I can find out for sure without testing out multiple event handlers and seeing if they get triggered when the invite comes through. CallInviteEvent’s doc string is “An event.” which clarifys things as far as to show that this code was done in a rote manner, copying over a list of items from somewhere else, or autogenerated. Wait….

It seems like there is a bug in The full doc strings are not being shown. Here is the full source for the event:

/// The content of an `` event.
/// This event is sent by the caller when they wish to establish a call.
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "", kind = MessageLike)]
pub struct CallInviteEventContent {
    /// A unique identifier for the call.
    pub call_id: OwnedVoipId,

    #[cfg(feature = "unstable-msc2746")]
    /// **Required in VoIP version 1.** A unique ID for this session for the duration of the call.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub party_id: Option<OwnedVoipId>,

    /// The time in milliseconds that the invite is valid for.
    /// Once the invite age exceeds this value, clients should discard it. They should also no
    /// longer show the call as awaiting an answer in the UI.
    pub lifetime: UInt,

    /// The session description object.
    pub offer: OfferSessionDescription,

    /// The version of the VoIP specification this messages adheres to.
    pub version: VoipVersionId,

    #[cfg(feature = "unstable-msc2746")]
    /// **Added in VoIP version 1.** The VoIP capabilities of the client.
    #[serde(default, skip_serializing_if = "CallCapabilities::is_default")]
    pub capabilities: CallCapabilities,

    #[cfg(feature = "unstable-msc2746")]
    /// **Added in VoIP version 1.** The intended target of the invite, if any.
    /// If this is `None`, the invite is intended for any member of the room, except the sender.
    /// The invite should be ignored if the invitee is set and doesn't match the user's ID.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub invitee: Option<OwnedUserId>,

Well at least now we know two things.

  1. Searching docs < reading source code
  2. CallInviteEvent is triggered when you are invited to a VOIP(Voice Over IP) phone call

Here is the real doc string for ThirdPartyInviteEvent:

/// The content of an `` event.
/// An invitation to a room issued to a third party identifier, rather than a matrix user ID.
/// Acts as an `` invite event, where there isn't a target user_id to invite. This
/// event contains a token and a public key whose private key must be used to sign the token.
/// Any user who can present that signature may use this invitation to join the target room.

While snooping around I also found this:

    /// The kind of membership events to filter for.
    #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/"))]
    #[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
    #[ruma_enum(rename_all = "lowercase")]
    pub enum MembershipEventFilter {
        /// The user has joined.

        /// The user has been invited.

        /// The user has left.

        /// The user has been banned.


Seems to relate to other users though. Though it does seem that somehow this can be used to get invite events.

Unfortunately, snooping through ruma doesn’t seem to be helping. Another thing I’ll attempt, is looking through matrix-rust-sdk example programs to see if any of them accept an invite. The autojoin example looks super prommissing.

It seems that it has an event handler that can get Invite events and join rooms. Just what we want, but when I set it up, no room invite events are being triggered.

Probably this is because I’m not running:


But how can I do that and still have my message send/recieve loop? Seems like this does the trick after some fiddling with ownership of the cleint object.

    tokio::spawn(async move {

Now we end up with another problem. It turns out that the event handler OriginalSyncRoomMessageEvent actually gets ALL messages, including ones we send ourselves :O . We’ll need to filter those by sender so we end up only processing messages that the other party sent. Unfortunately, I cannot find how OriginalSyncRoomMessageEvent is defined in source code. I’m importing it via matrix_rust_sdk from a re-exported ruma but when I do:

[timothy@nixos:~/pu/vegan-buddies/third-party/ruma]$ git grep OriginalSyncRoomMessageEvent
crates/ruma-common/src/doc/[`OriginalSyncRoomMessageEvent`], you have to convert it first by calling


I find it’s not defined in ruma. When I search I get something even weirder. I get doc entry as if it WAS defined in ruma. But clicking on the source link leads me to a seamingly unrelated struct. I also don’t see where it might be defined in matrix_rust_sdk, though its certainly used a lot there. Could it be comming from some sort of macro?

[timothy@nixos:~/pu/vegan-buddies/third-party/matrix-rust-sdk]$ git grep OriginalSyncRoomMessageEvent
crates/matrix-sdk/src/client/    ///     ruma::events::room::message::OriginalSyncRoomMessageEvent, Client,
crates/matrix-sdk/src/client/    /// client.add_event_handler(|ev: OriginalSyncRoomMessageEvent| async move {
crates/matrix-sdk/src/client/    ///     ruma::events::room::message::OriginalSyncRoomMessageEvent, Client,
crates/matrix-sdk/src/client/    /// client.add_event_handler(|ev: OriginalSyncRoomMessageEvent| async move {
crates/matrix-sdk/src/room/timeline/ ruma::events::room::message::{OriginalSyncRoomMessageEvent, Relation};
crates/matrix-sdk/src/room/timeline/    pub fn _new(ev: OriginalSyncRoomMessageEvent, raw: Raw<AnySyncTimelineEvent>) -> Self {
examples/command_bot/src/        MessageType, OriginalSyncRoomMessageEvent, RoomMessageEventContent,
examples/command_bot/src/ fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) {
examples/custom_events/src/                message::{MessageType, OriginalSyncRoomMessageEvent},
examples/custom_events/src/ fn on_regular_room_message(event: OriginalSyncRoomMessageEvent, room: Room) {
examples/emoji_verification/src/            room::message::{MessageType, OriginalSyncRoomMessageEvent},
examples/emoji_verification/src/    client.add_event_handler(|ev: OriginalSyncRoomMessageEvent, client: Client| async move {
examples/getting_started/src/        message::{MessageType, OriginalSyncRoomMessageEvent, RoomMessageEventContent},
examples/getting_started/src/ fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) {
examples/image_bot/src/    ruma::events::room::message::{MessageType, OriginalSyncRoomMessageEvent},
examples/image_bot/src/ fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room, image: Arc<[u8]>) {
examples/login/src/        MessageType, OriginalSyncRoomMessageEvent, RoomMessageEventContent, TextMessageEventContent,
examples/login/src/ fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) {
examples/login/src/        if let OriginalSyncRoomMessageEvent {
examples/wasm_command_bot/src/            room::message::{MessageType, OriginalSyncRoomMessageEvent, RoomMessageEventContent},
examples/wasm_command_bot/src/    async fn on_room_message(&self, room_id: &RoomId, event: &OriginalSyncRoomMessageEvent) {


At this point I’ve run out of time. I did mannage to figure out how to join rooms that I’ve been invited too, so at least I achieved that objective.