diff --git a/src/main.rs b/src/main.rs
index 607249d6448c8dd17b8d661721d288534db89942..d48e70cf41caf2909fda31da4755683caf87d1ea 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,25 +5,23 @@ mod config;
 mod matrix;
 mod utils;
 
-use log::{error, info};
+use crate::cmd::Cmd;
 use matrix_sdk;
-use std::{env, process::abort};
+use std::env;
 
 #[tokio::main]
 async fn main() -> matrix_sdk::Result<()> {
     config::setup_logging();
     config::check_argument_or_abort(true);
 
-    let config_file = env::args().last().unwrap();
-    info!("Using config file: {}", config_file);
-    return match config::from_file(&config_file) {
-        Ok(config) => matrix::connect_and_handle(config).await,
-        Err(e) => {
-            error!(
-                "Failed to read config file '{}' with error: {}",
-                config_file, e
-            );
-            abort();
-        }
-    };
+    let basic_cmds = vec![
+        Cmd::new_simple(format!("ping"), 0, |_x| true),
+        Cmd::new_echo(format!("bot_name"), 0, |_x| format!("toto")),
+    ];
+
+    matrix::Bot::new(env::args().last().unwrap())
+        .connect().await?
+        .add_package(format!("basic"), basic_cmds)
+        .listen()
+        .await
 }
diff --git a/src/matrix.rs b/src/matrix.rs
index 48b7abca801cd4b077797707b8068fc00ec55202..7a6e9378df7753b987eb1ade482cd821362a3ccf 100644
--- a/src/matrix.rs
+++ b/src/matrix.rs
@@ -1,4 +1,9 @@
-use crate::{cmd::package::CmdPackage, cmd::Cmd, config::Config, return_if};
+use crate::{
+    cmd::package::CmdPackage,
+    cmd::Cmd,
+    config::{from_file as config_from_file, Config},
+    return_if,
+};
 use futures::future::join_all;
 use log::{error, info, warn};
 use matrix_sdk::{
@@ -158,48 +163,128 @@ macro_rules! register_handler_if_enabled {
     }};
 }
 
-pub async fn connect_and_handle(config: Config) -> Result<()> {
-    // TODO: Refactor
-    let pkg_commands_vec = vec![
-        Cmd::new_simple(format!("ping"), 0, Arc::new(|_x| true)),
-        Cmd::new_echo(format!("bot_name"), 0, Arc::new(move |_x| format!("toto"))),
-    ];
-    let pkg = match config.section("basic") {
-        Some(section) => CmdPackage::from_config(&section, pkg_commands_vec),
-        None => {
-            abort();
+pub struct Bot {
+    config: Config,
+    client: Option<Client>,
+}
+
+impl Bot {
+    pub fn new(config_file: String) -> Bot {
+        match config_from_file(&config_file) {
+            Err(e) => {
+                error!(
+                    "Failed to read config file '{}' with error: {}",
+                    config_file, e
+                );
+                abort();
+            }
+            Ok(config) => {
+                info!("Using config file: {}", config_file);
+                Bot {
+                    config: config,
+                    client: None,
+                }
+            }
         }
-    };
-    GLOBAL_PKG.lock().unwrap().push(pkg);
-
-    let alice = UserId::try_from(config.user_name)?;
-    let client = Client::new(config.homeserver_url)?;
-
-    client
-        .login(
-            alice.localpart(),
-            config.user_password.as_str(),
-            None,
-            Some(config.display_name.as_str()),
-        )
-        .await?;
-    info!("Logged as: {}", alice);
-
-    client.sync_once(SyncSettings::default()).await?;
-    startup_and_log_rooms(&client).await;
-    register_handler_if_enabled!(
-        client,
-        config.handle_state_member,
-        on_state_member,
-        "state member"
-    );
-
-    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();
-    let settings = SyncSettings::default().token(token);
-    client.sync(settings).await;
-    Ok(())
+    }
+
+    pub async fn connect(&mut self) -> Result<&mut Self> {
+        let alice = UserId::try_from(self.config.user_name.clone())?;
+        let client = Client::new(self.config.homeserver_url.clone())?;
+
+        client
+            .login(
+                alice.localpart(),
+                self.config.user_password.as_str(),
+                None,
+                Some(self.config.display_name.as_str()),
+            )
+            .await?;
+
+        info!("Logged as: {}", alice);
+
+        client.sync_once(SyncSettings::default()).await?;
+        startup_and_log_rooms(&client).await;
+        register_handler_if_enabled!(
+            client,
+            self.config.handle_state_member,
+            on_state_member,
+            "state member"
+        );
+
+        client.register_event_handler(on_msg_room_event).await;
+        client.register_event_handler(on_msg_room_reaction).await;
+
+        self.client = Some(client);
+        return Ok(self);
+    }
+
+    pub async fn listen(&self) -> Result<()> {
+        match &self.client {
+            None => abort(),
+            Some(client) => {
+                info!("Entering sync loop");
+                let token = client.sync_token().await.unwrap();
+                client.sync(SyncSettings::default().token(token)).await;
+                Ok(())
+            }
+        }
+    }
+
+    pub fn add_package(&self, name: String, cmds: Vec<Cmd>) -> &Self {
+        let pkg = match self.config.section("basic") {
+            Some(section) => CmdPackage::from_config(&section, cmds),
+            None => {
+                abort();
+            }
+        };
+        GLOBAL_PKG.lock().unwrap().push(pkg);
+        &self
+    }
+
+    pub async fn connect_and_handle(config: Config) -> Result<()> {
+        // TODO: Refactor
+        let pkg_commands_vec = vec![
+            Cmd::new_simple(format!("ping"), 0, Arc::new(|_x| true)),
+            Cmd::new_echo(format!("bot_name"), 0, Arc::new(move |_x| format!("toto"))),
+        ];
+        let pkg = match config.section("basic") {
+            Some(section) => CmdPackage::from_config(&section, pkg_commands_vec),
+            None => {
+                abort();
+            }
+        };
+        GLOBAL_PKG.lock().unwrap().push(pkg);
+
+        let alice = UserId::try_from(config.user_name)?;
+        let client = Client::new(config.homeserver_url)?;
+
+        client
+            .login(
+                alice.localpart(),
+                config.user_password.as_str(),
+                None,
+                Some(config.display_name.as_str()),
+            )
+            .await?;
+        info!("Logged as: {}", alice);
+
+        client.sync_once(SyncSettings::default()).await?;
+        startup_and_log_rooms(&client).await;
+        register_handler_if_enabled!(
+            client,
+            config.handle_state_member,
+            on_state_member,
+            "state member"
+        );
+
+        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();
+        let settings = SyncSettings::default().token(token);
+        client.sync(settings).await;
+        Ok(())
+    }
 }