diff --git a/src/matrix.rs b/src/matrix.rs
index 3769de0b994eb24b98ee86cc5c51ca918e977228..08896e4a34f7a35efdf052bc2cd6c80565202979 100644
--- a/src/matrix.rs
+++ b/src/matrix.rs
@@ -1,4 +1,4 @@
-use crate::{cmd::package::CmdPackage, cmd::Cmd, config::Config};
+use crate::{cmd::package::CmdPackage, cmd::Cmd, config::Config, return_if};
 use futures::future::join_all;
 use log::{error, info, warn};
 use matrix_sdk::{
@@ -6,6 +6,7 @@ use matrix_sdk::{
     room::{Invited, Room},
     ruma::{
         events::{
+            reaction::{ReactionEventContent, Relation},
             room::{
                 member::MemberEventContent,
                 message::{MessageEventContent, MessageType, TextMessageEventContent},
@@ -16,7 +17,7 @@ use matrix_sdk::{
     },
     Client, Result, SyncSettings,
 };
-use std::{convert::TryFrom, lazy::SyncLazy, process::abort, sync::Arc, sync::Mutex, time};
+use std::{convert::TryFrom, lazy::SyncLazy, process::abort, sync::Arc, sync::Mutex};
 use tokio::time::{sleep, Duration};
 
 async fn join_room(room: &Invited) {
@@ -97,59 +98,63 @@ pub async fn connect_and_handle(config: Config) -> Result<()> {
     };
     GLOBAL_PKG.lock().unwrap().push(pkg);
 
+    let on_msg_room_reaction = |ev: SyncMessageEvent<ReactionEventContent>,
+                                room: Room,
+                                _: Option<EncryptionInfo>| async move {
+        let sender_id = ev.sender.clone();
+        return_if!(sender_id.as_str() == room.own_user_id().as_str(), ());
+        if let (
+            Room::Joined(joined),
+            ReactionEventContent {
+                relates_to: Relation {
+                    emoji, event_id, ..
+                },
+                ..
+            },
+        ) = (room, ev.content)
+        {
+            let msg_content = format!(
+                "Not handled reactions in room {} with event {event_id}, emoji was {emoji}",
+                joined.room_id()
+            );
+            let msg = AnyMessageEventContent::RoomMessage(MessageEventContent::text_html(
+                msg_content.clone(),
+                msg_content,
+            ));
+            joined.send(msg, None).await.unwrap();
+        };
+    };
+
     let on_msg_room_event = |ev: SyncMessageEvent<MessageEventContent>,
                              room: Room,
-                             _encryption_info: Option<EncryptionInfo>| async {
-        let now = time::Instant::now();
-        let sender_id = ev.sender;
-        if sender_id.as_str() == room.own_user_id().as_str() {
-            return ();
-        }
-
-        if room.name().is_none() {
-            warn!("Unsupported rooms without names");
-            return ();
-        }
-
-        if let Room::Joined(joined) = room {
-            if let MessageType::Text(TextMessageEventContent { body, .. }) = ev.content.msgtype {
-                if body.starts_with("%") {
-                    if let Some(stripped) = body.strip_prefix("%") {
-                        let args = stripped.split(' ').collect::<Vec<&str>>();
-                        let mut res: String = format!("Command not found");
-                        if let Some(pkg) = GLOBAL_PKG
-                            .lock()
-                            .unwrap()
-                            .iter()
-                            .find(|x| x.has_command(&args))
-                        {
-                            res = pkg.handle(&joined, &sender_id, args);
-                        }
-
-                        let msg = AnyMessageEventContent::RoomMessage(
-                            MessageEventContent::text_html(res.clone(), res),
-                        );
-                        let elapsed_before_send = now.elapsed();
-                        joined.send(msg, None).await.unwrap();
-                        let elapsed_after_send = now.elapsed();
-                        warn!(
-                            "Elapsed before send {}µs, elapsed after send {}ms",
-                            elapsed_before_send.as_micros(),
-                            elapsed_after_send.as_millis()
-                        );
+                             __info: Option<EncryptionInfo>| async {
+        let sender_id = ev.sender.clone();
+        return_if!(sender_id.as_str() == room.own_user_id().as_str(), ());
+
+        if let (Room::Joined(joined), MessageType::Text(TextMessageEventContent { ref body, .. })) =
+            (room, ev.content.msgtype.clone())
+        {
+            if body.starts_with("%") {
+                if let Some(stripped) = body.strip_prefix("%") {
+                    let args = stripped.split(' ').collect::<Vec<&str>>();
+                    let mut res: String = format!("Command not found");
+                    if let Some(pkg) = GLOBAL_PKG
+                        .lock()
+                        .unwrap()
+                        .iter()
+                        .find(|x| x.has_command(&args))
+                    {
+                        res = pkg.handle(&joined, &sender_id, args);
                     }
+
+                    let event = ev.into_full_event(joined.room_id().clone());
+                    let msg = AnyMessageEventContent::RoomMessage(
+                        MessageEventContent::text_reply_html(res.clone(), res, &event),
+                    );
+                    joined.send(msg, None).await.unwrap();
                 }
             }
-        }
-
-        // if let Room::Invited(_) = room {
-        //     warn!("Bot was invited by {sender_id} to room {room_id} a.k.a. {room_name}",);
-        // }
-
-        // if let Room::Left(_) = room {
-        //     error!("Bot left room {room_id} a.k.a. {room_name}")
-        // }
-        return ();
+        };
     };
 
     let alice = UserId::try_from(config.user_name)?;
@@ -176,6 +181,7 @@ pub async fn connect_and_handle(config: Config) -> Result<()> {
     }
 
     client.register_event_handler(on_msg_room_event).await;
+    client.register_event_handler(on_msg_room_reaction).await;
 
     info!("Entering sync loop");
     let token = client.sync_token().await.unwrap();