diff --git a/setup.cfg b/setup.cfg
index 033c898..8f000f7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -11,14 +11,16 @@ packages = find_namespace:
 python_requires = >=3.7
 ;todo: remove flatten_dict (core/config.py)
 install_requires =
-    appdirs==1.4.4
-    Babel==2.8.0
+    appdirs>=1.4.4
+    Babel>=2.8.0
     discord.py==1.4.1
     discord_flags==2.1.1
-    flatten_dict==0.3.0
-    jishaku==1.19.1.200
-    PyYAML==5.3.1
-    rich==6.0.0
+    flatten_dict>=0.3.0
+    jishaku>=1.19.1.200
+    psutil>=5.7.2
+    PyYAML>=5.3.1
+    rich>=6.0.0
+    structured_config>=4.12
 
 [options.entry_points]
 console_scripts =
diff --git a/tuxbot/__main__.py b/tuxbot/__main__.py
index 14f8e33..3d626a2 100644
--- a/tuxbot/__main__.py
+++ b/tuxbot/__main__.py
@@ -1,318 +1,26 @@
-import argparse
-import asyncio
-import json
-import logging
-import signal
-import sys
-import os
-from argparse import Namespace
 from typing import NoReturn
 
-import discord
-import pip
-import tracemalloc
-from rich.columns import Columns
 from rich.console import Console
-from rich.panel import Panel
 from rich.traceback import install
-from rich.table import Table, box
-from rich.text import Text
-from rich import print
-
-import tuxbot.logging
-from tuxbot.core import data_manager
-from tuxbot.core.bot import Tux
-from . import __version__, version_info, ExitCodes
-
-log = logging.getLogger("tuxbot.main")
+from tuxbot import ExitCodes
 
 console = Console()
 install(console=console)
-tracemalloc.start()
-
-
-def list_instances() -> NoReturn:
-    """List all available instances
-
-    """
-    with data_manager.config_file.open() as fs:
-        data = json.load(fs)
-
-    console.print(
-        Panel("[bold green]Instances", style="green"),
-        justify="center"
-    )
-    console.print()
-
-    columns = Columns(expand=True, padding=2, align="center")
-    for instance, details in data.items():
-        is_running = details.get('IS_RUNNING')
-
-        table = Table(
-            style="dim", border_style="not dim",
-            box=box.HEAVY_HEAD
-        )
-        table.add_column("Name")
-        table.add_column(("Running" if is_running else "Down") + " since")
-        table.add_row(instance, "42")
-        table.title = Text(
-            instance,
-            style="green" if is_running else "red"
-        )
-        columns.add_renderable(table)
-    console.print(columns)
-    console.print()
-
-    sys.exit(os.EX_OK)
-
-
-def debug_info() -> NoReturn:
-    """Show debug info relatives to the bot
-
-    """
-    python_version = sys.version.replace("\n", "")
-    pip_version = pip.__version__
-    tuxbot_version = __version__
-    dpy_version = discord.__version__
-
-    uptime = os.popen('uptime').read().strip().split()
-
-    console.print(
-        Panel("[bold blue]Debug Info", style="blue"),
-        justify="center"
-    )
-    console.print()
-
-    columns = Columns(expand=True, padding=2, align="center")
-
-    table = Table(
-        style="dim", border_style="not dim",
-        box=box.HEAVY_HEAD
-    )
-    table.add_column(
-        "Bot Info",
-    )
-    table.add_row(f"[u]Tuxbot version:[/u] {tuxbot_version}")
-    table.add_row(f"[u]Major:[/u] {version_info.major}")
-    table.add_row(f"[u]Minor:[/u] {version_info.minor}")
-    table.add_row(f"[u]Micro:[/u] {version_info.micro}")
-    table.add_row(f"[u]Level:[/u] {version_info.releaselevel}")
-    table.add_row(f"[u]Last change:[/u] {version_info.info}")
-    columns.add_renderable(table)
-
-    table = Table(
-        style="dim", border_style="not dim",
-        box=box.HEAVY_HEAD
-    )
-    table.add_column(
-        "Python Info",
-    )
-    table.add_row(f"[u]Python version:[/u] {python_version}")
-    table.add_row(f"[u]Python executable path:[/u] {sys.executable}")
-    table.add_row(f"[u]Pip version:[/u] {pip_version}")
-    table.add_row(f"[u]Discord.py version:[/u] {dpy_version}")
-    columns.add_renderable(table)
-
-    table = Table(
-        style="dim", border_style="not dim",
-        box=box.HEAVY_HEAD
-    )
-    table.add_column(
-        "Server Info",
-    )
-    table.add_row(f"[u]System:[/u] {os.uname().sysname}")
-    table.add_row(f"[u]System arch:[/u] {os.uname().machine}")
-    table.add_row(f"[u]Kernel:[/u] {os.uname().release}")
-    table.add_row(f"[u]User:[/u] {os.getlogin()}")
-    table.add_row(f"[u]Uptime:[/u] {uptime[2]}")
-    table.add_row(
-        f"[u]Load Average:[/u] {' '.join(map(str, os.getloadavg()))}"
-    )
-    columns.add_renderable(table)
-
-    console.print(columns)
-    console.print()
-
-    sys.exit(os.EX_OK)
-
-
-def parse_cli_flags(args: list) -> Namespace:
-    """Parser for cli values.
-
-    Parameters
-    ----------
-    args:list
-        Is a list of all passed values.
-    Returns
-    -------
-    Namespace
-    """
-    parser = argparse.ArgumentParser(
-        description="Tuxbot - OpenSource bot",
-        usage="tuxbot <instance_name> [arguments]",
-    )
-    parser.add_argument(
-        "--version", "-V", action="store_true",
-        help="Show tuxbot's used version"
-    )
-    parser.add_argument(
-        "--debug", action="store_true",
-        help="Show debug information."
-    )
-    parser.add_argument(
-        "--list-instances", "-L", action="store_true",
-        help="List all instance names"
-    )
-    parser.add_argument("--token", "-T", type=str,
-                        help="Run Tuxbot with passed token")
-    parser.add_argument(
-        "instance_name",
-        nargs="?",
-        help="Name of the bot instance created during `tuxbot-setup`.",
-    )
-
-    args = parser.parse_args(args)
-
-    return args
-
-
-async def shutdown_handler(tux: Tux, signal_type, exit_code=None) -> NoReturn:
-    """Handler when the bot shutdown
-
-    It cancels all running task.
-
-    Parameters
-    ----------
-    tux:Tux
-        Object for the bot.
-    signal_type:int, None
-        Exiting signal code.
-    exit_code:None|int
-        Code to show when exiting.
-    """
-    if signal_type:
-        log.info("%s received. Quitting...", signal_type)
-    elif exit_code is None:
-        log.info("Shutting down from unhandled exception")
-        tux.shutdown_code = ExitCodes.CRITICAL
-
-    if exit_code is not None:
-        tux.shutdown_code = exit_code
-
-    await tux.shutdown()
-
-
-async def run_bot(tux: Tux, cli_flags: Namespace) -> None:
-    """This run the bot.
-
-    Parameters
-    ----------
-    tux:Tux
-        Object for the bot.
-    cli_flags:Namespace
-        All different flags passed in the console.
-
-    Returns
-    -------
-    None
-        When exiting, this function return None.
-    """
-    data_path = data_manager.data_path(tux.instance_name)
-
-    tuxbot.logging.init_logging(10, location=data_path / "logs")
-
-    log.debug("====Basic Config====")
-    log.debug("Data Path: %s", data_path)
-
-    if cli_flags.token:
-        token = cli_flags.token
-    else:
-        token = tux.config("core").get("token")
-
-    if not token:
-        log.critical("Token must be set if you want to login.")
-        sys.exit(ExitCodes.CRITICAL)
-
-    try:
-        await tux.load_packages()
-        console.print()
-        await tux.start(token=token, bot=True)
-    except discord.LoginFailure:
-        log.critical("This token appears to be valid.")
-        console.print()
-        console.print(
-            "[prompt.invalid]This token appears to be valid. [i]exiting...[/i]"
-        )
-        sys.exit(ExitCodes.CRITICAL)
-    except Exception as e:
-        raise e
-
-    return None
 
 
 def main() -> NoReturn:
-    """Main function
-
-    """
-    tux = None
-    cli_flags = parse_cli_flags(sys.argv[1:])
-
-    if cli_flags.list_instances:
-        list_instances()
-    elif cli_flags.debug:
-        debug_info()
-    elif cli_flags.version:
-        print(f"Tuxbot V{version_info.major}")
-        print(f"Complete Version: {__version__}")
-
-        sys.exit(os.EX_OK)
-
-    loop = asyncio.new_event_loop()
-    asyncio.set_event_loop(loop)
-
     try:
-        if not cli_flags.instance_name:
-            console.print(
-                "[red]No instance provided ! "
-                "You can use 'tuxbot -L' to list all available instances"
-            )
-            sys.exit(ExitCodes.CRITICAL)
+        from .__run__ import run
 
-        tux = Tux(
-            cli_flags=cli_flags,
-            description="Tuxbot, made from and for OpenSource",
-            dm_help=None,
-        )
-
-        loop.run_until_complete(run_bot(tux, cli_flags))
-    except KeyboardInterrupt:
-        console.print(
-            "  [red]Please use <prefix>quit instead of Ctrl+C to Shutdown!"
-        )
-        log.warning("Please use <prefix>quit instead of Ctrl+C to Shutdown!")
-        log.info("Received KeyboardInterrupt")
-        console.print("[i]Trying to shutdown...")
-        if tux is not None:
-            loop.run_until_complete(shutdown_handler(tux, signal.SIGINT))
+        run()
     except SystemExit as exc:
-        log.info("Shutting down with exit code: %s", exc.code)
-        if tux is not None:
-            loop.run_until_complete(shutdown_handler(tux, None, exc.code))
-    except Exception as exc:
-        log.error("Unexpected exception (%s): ", type(exc))
+        if exc.code == ExitCodes.RESTART:
+            from .__run__ import run  # reimport to load changes
+            run()
+        else:
+            raise exc
+    except Exception:
         console.print_exception()
-        if tux is not None:
-            loop.run_until_complete(shutdown_handler(tux, None, 1))
-    finally:
-        loop.run_until_complete(loop.shutdown_asyncgens())
-        log.info("Please wait, cleaning up a bit more")
-        loop.run_until_complete(asyncio.sleep(1))
-        asyncio.set_event_loop(None)
-        loop.stop()
-        loop.close()
-        exit_code = ExitCodes.CRITICAL if tux is None else tux.shutdown_code
-
-        sys.exit(exit_code)
 
 
 if __name__ == "__main__":
diff --git a/tuxbot/__run__.py b/tuxbot/__run__.py
new file mode 100644
index 0000000..f8dd4c1
--- /dev/null
+++ b/tuxbot/__run__.py
@@ -0,0 +1,315 @@
+import argparse
+import asyncio
+import json
+import logging
+import signal
+import sys
+import os
+from argparse import Namespace
+from typing import NoReturn
+
+import discord
+import pip
+import tracemalloc
+from rich.columns import Columns
+from rich.console import Console
+from rich.panel import Panel
+from rich.traceback import install
+from rich.table import Table, box
+from rich.text import Text
+from rich import print
+
+import tuxbot.logging
+from tuxbot.core import data_manager
+from tuxbot.core.bot import Tux
+from . import __version__, version_info, ExitCodes
+
+log = logging.getLogger("tuxbot.main")
+
+console = Console()
+install(console=console)
+tracemalloc.start()
+
+
+def list_instances() -> NoReturn:
+    """List all available instances
+
+    """
+    with data_manager.config_file.open() as fs:
+        data = json.load(fs)
+
+    console.print(
+        Panel("[bold green]Instances", style="green"),
+        justify="center"
+    )
+    console.print()
+
+    columns = Columns(expand=True, padding=2, align="center")
+    for instance, details in data.items():
+        is_running = details.get('IS_RUNNING')
+
+        table = Table(
+            style="dim", border_style="not dim",
+            box=box.HEAVY_HEAD
+        )
+        table.add_column("Name")
+        table.add_column(("Running" if is_running else "Down") + " since")
+        table.add_row(instance, "42")
+        table.title = Text(
+            instance,
+            style="green" if is_running else "red"
+        )
+        columns.add_renderable(table)
+    console.print(columns)
+    console.print()
+
+    sys.exit(os.EX_OK)
+
+
+def debug_info() -> NoReturn:
+    """Show debug info relatives to the bot
+
+    """
+    python_version = sys.version.replace("\n", "")
+    pip_version = pip.__version__
+    tuxbot_version = __version__
+    dpy_version = discord.__version__
+
+    uptime = os.popen('uptime').read().strip().split()
+
+    console.print(
+        Panel("[bold blue]Debug Info", style="blue"),
+        justify="center"
+    )
+    console.print()
+
+    columns = Columns(expand=True, padding=2, align="center")
+
+    table = Table(
+        style="dim", border_style="not dim",
+        box=box.HEAVY_HEAD
+    )
+    table.add_column(
+        "Bot Info",
+    )
+    table.add_row(f"[u]Tuxbot version:[/u] {tuxbot_version}")
+    table.add_row(f"[u]Major:[/u] {version_info.major}")
+    table.add_row(f"[u]Minor:[/u] {version_info.minor}")
+    table.add_row(f"[u]Micro:[/u] {version_info.micro}")
+    table.add_row(f"[u]Level:[/u] {version_info.releaselevel}")
+    table.add_row(f"[u]Last change:[/u] {version_info.info}")
+    columns.add_renderable(table)
+
+    table = Table(
+        style="dim", border_style="not dim",
+        box=box.HEAVY_HEAD
+    )
+    table.add_column(
+        "Python Info",
+    )
+    table.add_row(f"[u]Python version:[/u] {python_version}")
+    table.add_row(f"[u]Python executable path:[/u] {sys.executable}")
+    table.add_row(f"[u]Pip version:[/u] {pip_version}")
+    table.add_row(f"[u]Discord.py version:[/u] {dpy_version}")
+    columns.add_renderable(table)
+
+    table = Table(
+        style="dim", border_style="not dim",
+        box=box.HEAVY_HEAD
+    )
+    table.add_column(
+        "Server Info",
+    )
+    table.add_row(f"[u]System:[/u] {os.uname().sysname}")
+    table.add_row(f"[u]System arch:[/u] {os.uname().machine}")
+    table.add_row(f"[u]Kernel:[/u] {os.uname().release}")
+    table.add_row(f"[u]User:[/u] {os.getlogin()}")
+    table.add_row(f"[u]Uptime:[/u] {uptime[2]}")
+    table.add_row(
+        f"[u]Load Average:[/u] {' '.join(map(str, os.getloadavg()))}"
+    )
+    columns.add_renderable(table)
+
+    console.print(columns)
+    console.print()
+
+    sys.exit(os.EX_OK)
+
+
+def parse_cli_flags(args: list) -> Namespace:
+    """Parser for cli values.
+
+    Parameters
+    ----------
+    args:list
+        Is a list of all passed values.
+    Returns
+    -------
+    Namespace
+    """
+    parser = argparse.ArgumentParser(
+        description="Tuxbot - OpenSource bot",
+        usage="tuxbot <instance_name> [arguments]",
+    )
+    parser.add_argument(
+        "--version", "-V", action="store_true",
+        help="Show tuxbot's used version"
+    )
+    parser.add_argument(
+        "--debug", action="store_true",
+        help="Show debug information."
+    )
+    parser.add_argument(
+        "--list-instances", "-L", action="store_true",
+        help="List all instance names"
+    )
+    parser.add_argument("--token", "-T", type=str,
+                        help="Run Tuxbot with passed token")
+    parser.add_argument(
+        "instance_name",
+        nargs="?",
+        help="Name of the bot instance created during `tuxbot-setup`.",
+    )
+
+    args = parser.parse_args(args)
+
+    return args
+
+
+async def shutdown_handler(tux: Tux, signal_type, exit_code=None) -> NoReturn:
+    """Handler when the bot shutdown
+
+    It cancels all running task.
+
+    Parameters
+    ----------
+    tux:Tux
+        Object for the bot.
+    signal_type:int, None
+        Exiting signal code.
+    exit_code:None|int
+        Code to show when exiting.
+    """
+    if signal_type:
+        log.info("%s received. Quitting...", signal_type)
+    elif exit_code is None:
+        log.info("Shutting down from unhandled exception")
+        tux.shutdown_code = ExitCodes.CRITICAL
+
+    if exit_code is not None:
+        tux.shutdown_code = exit_code
+
+    await tux.shutdown()
+
+
+async def run_bot(tux: Tux, cli_flags: Namespace) -> None:
+    """This run the bot.
+
+    Parameters
+    ----------
+    tux:Tux
+        Object for the bot.
+    cli_flags:Namespace
+        All different flags passed in the console.
+
+    Returns
+    -------
+    None
+        When exiting, this function return None.
+    """
+    data_path = data_manager.data_path(tux.instance_name)
+
+    tuxbot.logging.init_logging(10, location=data_path / "logs")
+
+    log.debug("====Basic Config====")
+    log.debug("Data Path: %s", data_path)
+
+    if cli_flags.token:
+        token = cli_flags.token
+    else:
+        token = tux.config("core").get("token")
+
+    if not token:
+        log.critical("Token must be set if you want to login.")
+        sys.exit(ExitCodes.CRITICAL)
+
+    try:
+        await tux.load_packages()
+        console.print()
+        await tux.start(token=token, bot=True)
+    except discord.LoginFailure:
+        log.critical("This token appears to be valid.")
+        console.print()
+        console.print(
+            "[prompt.invalid]This token appears to be valid. [i]exiting...[/i]"
+        )
+        sys.exit(ExitCodes.CRITICAL)
+    except Exception as e:
+        raise e
+
+    return None
+
+
+def run() -> NoReturn:
+    """Main function
+
+    """
+    tux = None
+    cli_flags = parse_cli_flags(sys.argv[1:])
+
+    if cli_flags.list_instances:
+        list_instances()
+    elif cli_flags.debug:
+        debug_info()
+    elif cli_flags.version:
+        print(f"Tuxbot V{version_info.major}")
+        print(f"Complete Version: {__version__}")
+
+        sys.exit(os.EX_OK)
+
+    loop = asyncio.new_event_loop()
+    asyncio.set_event_loop(loop)
+
+    try:
+        if not cli_flags.instance_name:
+            console.print(
+                "[red]No instance provided ! "
+                "You can use 'tuxbot -L' to list all available instances"
+            )
+            sys.exit(ExitCodes.CRITICAL)
+
+        tux = Tux(
+            cli_flags=cli_flags,
+            description="Tuxbot, made from and for OpenSource",
+            dm_help=None,
+        )
+
+        loop.run_until_complete(run_bot(tux, cli_flags))
+    except KeyboardInterrupt:
+        console.print(
+            "  [red]Please use <prefix>quit instead of Ctrl+C to Shutdown!"
+        )
+        log.warning("Please use <prefix>quit instead of Ctrl+C to Shutdown!")
+        log.info("Received KeyboardInterrupt")
+        console.print("[i]Trying to shutdown...")
+        if tux is not None:
+            loop.run_until_complete(shutdown_handler(tux, signal.SIGINT))
+    except SystemExit as exc:
+        log.info("Shutting down with exit code: %s", exc.code)
+        if tux is not None:
+            loop.run_until_complete(shutdown_handler(tux, None, exc.code))
+    except Exception as exc:
+        log.error("Unexpected exception (%s): ", type(exc))
+        console.print_exception()
+        if tux is not None:
+            loop.run_until_complete(shutdown_handler(tux, None, 1))
+    finally:
+        loop.run_until_complete(loop.shutdown_asyncgens())
+        log.info("Please wait, cleaning up a bit more")
+        loop.run_until_complete(asyncio.sleep(1))
+        asyncio.set_event_loop(None)
+        loop.stop()
+        loop.close()
+        exit_code = ExitCodes.CRITICAL if tux is None else tux.shutdown_code
+
+        sys.exit(exit_code)
diff --git a/tuxbot/cogs/admin/__init__.py b/tuxbot/cogs/admin/__init__.py
index a1281fa..11565d1 100644
--- a/tuxbot/cogs/admin/__init__.py
+++ b/tuxbot/cogs/admin/__init__.py
@@ -1,6 +1,7 @@
 from collections import namedtuple
 
 from .admin import Admin
+from .config import AdminConfig
 from ...core.bot import Tux
 
 VersionInfo = namedtuple("VersionInfo", "major minor micro release_level")
diff --git a/tuxbot/cogs/admin/admin.py b/tuxbot/cogs/admin/admin.py
index 5957825..1da8cff 100644
--- a/tuxbot/cogs/admin/admin.py
+++ b/tuxbot/cogs/admin/admin.py
@@ -6,7 +6,10 @@ from discord.ext import commands
 from tuxbot.core import checks
 from tuxbot.core.bot import Tux
 from tuxbot.core.i18n import Translator, find_locale, get_locale_name, available_locales
-from tuxbot.core.utils.functions.extra import group_extra, ContextPlus
+from tuxbot.core.utils.functions.extra import (
+    group_extra, command_extra,
+    ContextPlus
+)
 
 log = logging.getLogger("tuxbot.cogs.admin")
 _ = Translator("Admin", __file__)
@@ -40,10 +43,30 @@ class Admin(commands.Cog, name="Admin"):
 
     @_lang.command(name="list", aliases=["liste", "all", "view"])
     async def _lang_list(self, ctx: ContextPlus):
+        description = ''
+        for key, value in available_locales.items():
+            description += f":flag_{key[-2:].lower()}: {value[0]}\n"
+
         e = discord.Embed(
             title=_("List of available locales: ", ctx, self.bot.config),
-            description="\n".join([i[0] for i in available_locales.values()]),
+            description=description,
             color=0x36393E,
         )
 
         await ctx.send(embed=e)
+
+    # =========================================================================
+
+    @command_extra(name="quit", aliases=["shutdown"], deletable=False)
+    @commands.guild_only()
+    @checks.is_owner()
+    async def _quit(self, ctx: ContextPlus):
+        await ctx.send("*quit...*")
+        await self.bot.shutdown()
+
+    @command_extra(name="restart", deletable=False)
+    @commands.guild_only()
+    @checks.is_owner()
+    async def _restart(self, ctx: ContextPlus):
+        await ctx.send("*restart...*")
+        await self.bot.shutdown(restart=True)
diff --git a/tuxbot/cogs/admin/config.py b/tuxbot/cogs/admin/config.py
new file mode 100644
index 0000000..4ec934f
--- /dev/null
+++ b/tuxbot/cogs/admin/config.py
@@ -0,0 +1,18 @@
+from structured_config import Structure, StrField
+
+
+class AdminConfig(Structure):
+    dm: str = StrField("")
+    mentions: str = StrField("")
+    guilds: str = StrField("")
+    errors: str = StrField("")
+    gateway: str = StrField("")
+
+
+extra = {
+    'dm': str,
+    'mentions': str,
+    'guilds': str,
+    'errors': str,
+    'gateway': str,
+}
diff --git a/tuxbot/core/bot.py b/tuxbot/core/bot.py
index 3d84267..f924a5a 100644
--- a/tuxbot/core/bot.py
+++ b/tuxbot/core/bot.py
@@ -25,7 +25,10 @@ log = logging.getLogger("tuxbot")
 console = Console()
 install(console=console)
 
-packages: List[str] = ["jishaku", "tuxbot.cogs.warnings", "tuxbot.cogs.admin"]
+packages: List[str] = [
+    "jishaku",
+    "tuxbot.cogs.admin"
+]
 
 
 class Tux(commands.AutoShardedBot):
@@ -109,6 +112,7 @@ class Tux(commands.AutoShardedBot):
         self._progress.get("main").remove_task(
             self._progress.get("tasks")["connecting"]
         )
+        self._progress.get("tasks").pop("connecting")
         console.clear()
 
         console.print(
@@ -155,8 +159,8 @@ class Tux(commands.AutoShardedBot):
         console.print(columns)
         console.print()
 
-    async def is_owner(self,
-                       user: Union[discord.User, discord.Member]) -> bool:
+    async def is_owner(self, user: Union[discord.User, discord.Member])\
+            -> bool:
         """Determines if the user is a bot owner.
 
         Parameters
@@ -245,7 +249,7 @@ class Tux(commands.AutoShardedBot):
         for task in pending:
             console.log("Canceling", task.get_name(), f"({task.get_coro()})")
             task.cancel()
-        await asyncio.gather(*pending, return_exceptions=True)
+        await asyncio.gather(*pending, return_exceptions=False)
 
         await super().logout()
 
@@ -265,4 +269,8 @@ class Tux(commands.AutoShardedBot):
             self.shutdown_code = ExitCodes.RESTART
 
         await self.logout()
-        sys.exit(self.shutdown_code)
+
+        sys_e = SystemExit()
+        sys_e.code = self.shutdown_code
+
+        raise sys_e
diff --git a/tuxbot/core/config.py b/tuxbot/core/config.py
index 5a4702c..0cd6b9c 100644
--- a/tuxbot/core/config.py
+++ b/tuxbot/core/config.py
@@ -1,8 +1,10 @@
 import asyncio
-import json
 import logging
-from typing import List, Dict, Union, Any
-from flatten_dict import flatten, unflatten
+from typing import List, Dict
+from structured_config import (
+    ConfigFile,
+    Structure, IntField, StrField, BoolField
+)
 
 import discord
 
@@ -13,159 +15,44 @@ __all__ = ["Config"]
 log = logging.getLogger("tuxbot.core.config")
 
 
-class Config:
-    def __init__(self, cog_instance: str = None):
-        self._cog_instance = cog_instance
+class Server(Structure):
+    prefixes: List[str] = []
+    disabled_command: List[str] = []
+    locale: str = StrField("")
 
-        self.lock = asyncio.Lock()
-        self.loop = asyncio.get_event_loop()
 
-        self._settings_file = None
-        self._datas = {}
+class User(Structure):
+    aliases: List[dict] = []
+    locale: str = StrField("")
 
-    def __getitem__(self, item) -> Dict:
-        path = data_path(self._cog_instance)
 
-        if item != "core":
-            path = path / "cogs" / item
-        else:
-            path /= "core"
+class Config(Structure):
+    class Servers(Structure):
+        count: int = IntField(0)
+        all: List[Server] = []
 
-        settings_file = path / "settings.json"
+    class Users(Structure):
+        all: List[User] = []
 
-        if not settings_file.exists():
-            raise FileNotFoundError(
-                f"Unable to find settings file " f"'{settings_file}'"
-            )
-        else:
-            with settings_file.open("r") as f:
-                return json.load(f)
+    class Core(Structure):
+        owners_id: List[int] = []
+        prefixes: List[str] = []
+        token: str = StrField("")
+        mentionable: bool = BoolField("")
+        locale: str = StrField("")
 
-    def __call__(self, item):
-        return self.__getitem__(item)
+    class Cogs(Structure):
+        pass
 
-    def owners_id(self) -> List[int]:
-        """Simply return the owners id saved in config file.
 
-        Returns
-        -------
-        str
-            Owners id.
-        """
-        return self.__getitem__("core").get("owners_id")
+# =============================================================================
+# Configuration of Tuxbot Application (not the bot)
+# =============================================================================
 
-    def token(self) -> str:
-        """Simply return the bot token saved in config file.
+class Instance(Structure):
+    path: str = StrField("")
+    active: bool = BoolField(False)
 
-        Returns
-        -------
-        str
-            Bot token.
-        """
-        return self.__getitem__("core").get("token")
 
-    def get_prefixes(self, guild: discord.Guild) -> List[str]:
-        """Get custom  prefixes for one guild.
-
-        Parameters
-        ----------
-        guild:discord.Guild
-            The required guild prefixes.
-
-        Returns
-        -------
-        List[str]
-            List of all prefixes.
-        """
-        core = self.__getitem__("core")
-        prefixes = core.get("guild", {}).get(guild.id, {}).get("prefixes", [])
-
-        return prefixes
-
-    def get_blacklist(self, key: str) -> List[Union[str, int]]:
-        """Return list off all blacklisted values
-
-        Parameters
-        ----------
-        key:str
-            Which type of blacklist to choice (guilds ? channels ?,...).
-
-        Returns
-        -------
-        List[Union[str, int]]
-            List containing blacklisted values.
-        """
-        core = self.__getitem__("core")
-        blacklist = core.get("blacklist", {}).get(key, [])
-
-        return blacklist
-
-    def _dump(self):
-        with self._settings_file.open("w") as f:
-            json.dump(self._datas, f, indent=4)
-
-    async def update(self, cog_name: str, item: str, value: Any) -> dict:
-        """Update values in config file.
-
-        Parameters
-        ----------
-        cog_name:str
-            Name of cog who's corresponding to the config file.
-        item:str
-            Key to update.
-        value:Any
-            New values to apply.
-
-        Returns
-        -------
-        dict:
-            Updated values.
-
-        """
-        datas = self.__getitem__(cog_name)
-        path = data_path(self._cog_instance)
-
-        flat_datas = flatten(datas)
-        flat_datas[tuple(item.split("."))] = value
-        datas = unflatten(flat_datas)
-
-        self._datas = datas
-
-        if cog_name != "core":
-            path = path / "cogs" / cog_name
-        else:
-            path /= "core"
-
-        self._settings_file = path / "settings.json"
-
-        async with self.lock:
-            await self.loop.run_in_executor(None, self._dump)
-
-        return datas
-
-    def get_value(self, cog_name: str, key: str, default: Any = None) -> Any:
-        """Get value by key.
-
-        Parameters
-        ----------
-        cog_name:str
-            Name of cog who's corresponding to the config file.
-        key:str
-            Key to fetch.
-        default:Any|Optional
-            Default value.
-
-        Returns
-        -------
-        Any:
-            Recovered value.
-
-        """
-        datas = self.__getitem__(cog_name)
-
-        flat_datas = flatten(datas)
-
-        try:
-            return flat_datas[tuple(key.split("."))]
-        except KeyError:
-            return default
+class AppConfig(Structure):
+    instances: Dict[str, Instance] = {}
diff --git a/tuxbot/core/data_manager.py b/tuxbot/core/data_manager.py
index d96661c..a6c438f 100644
--- a/tuxbot/core/data_manager.py
+++ b/tuxbot/core/data_manager.py
@@ -7,7 +7,7 @@ log = logging.getLogger("tuxbot.core.data_manager")
 
 app_dir = appdirs.AppDirs("Tuxbot-bot")
 config_dir = Path(app_dir.user_config_dir)
-config_file = config_dir / "config.json"
+config_file = config_dir / "config.yaml"
 
 
 def data_path(instance_name: str) -> Path:
diff --git a/tuxbot/core/exceptions.py b/tuxbot/core/exceptions.py
new file mode 100644
index 0000000..d47a97f
--- /dev/null
+++ b/tuxbot/core/exceptions.py
@@ -0,0 +1,9 @@
+from discord.ext import commands
+
+
+class DisabledCommandByServerOwner(commands.CheckFailure):
+    pass
+
+
+class DisabledCommandByBotOwner(commands.CheckFailure):
+    pass
diff --git a/tuxbot/core/i18n.py b/tuxbot/core/i18n.py
index 4190dc0..51e0fd1 100644
--- a/tuxbot/core/i18n.py
+++ b/tuxbot/core/i18n.py
@@ -38,14 +38,14 @@ def get_locale_name(locale: str) -> str:
 class Translator(Callable[[str], str]):
     """Class to load texts at init."""
 
-    def __init__(self, name: str, file_location: Union[str, Path, os.PathLike]):
+    def __init__(self, name: str, file_location: Union[Path, os.PathLike]):
         """Initializes the Translator object.
 
         Parameters
         ----------
         name : str
             The cog name.
-        file_location:str|Path|os.PathLike
+        file_location:Path|os.PathLike
             File path for the required extension.
 
         """
diff --git a/tuxbot/core/utils/functions/cli.py b/tuxbot/core/utils/functions/cli.py
deleted file mode 100644
index 56bc67a..0000000
--- a/tuxbot/core/utils/functions/cli.py
+++ /dev/null
@@ -1,89 +0,0 @@
-import codecs
-import itertools
-import sys
-
-
-def bordered(*columns: dict) -> str:
-    """
-    credits to https://github.com/Cog-Creators/Red-DiscordBot/blob/V3/develop/redbot/core/utils/chat_formatting.py
-
-    Get two blocks of text in a borders.
-
-    Note
-    ----
-    This will only work with a monospaced font.
-
-    Parameters
-    ----------
-    *columns : `sequence` of `str`
-        The columns of text, each being a list of lines in that column.
-
-    Returns
-    -------
-    str
-        The bordered text.
-
-    """
-    encoder = codecs.getencoder(sys.stdout.encoding)
-    try:
-        encoder("┌┐└┘─│")  # border symbols
-    except UnicodeEncodeError:
-        ascii_border = True
-    else:
-        ascii_border = False
-
-    borders = {
-        "TL": "+" if ascii_border else "┌",  # Top-left
-        "TR": "+" if ascii_border else "┐",  # Top-right
-        "BL": "+" if ascii_border else "└",  # Bottom-left
-        "BR": "+" if ascii_border else "┘",  # Bottom-right
-        "HZ": "-" if ascii_border else "─",  # Horizontal
-        "VT": "|" if ascii_border else "│",  # Vertical
-    }
-
-    sep = " " * 4  # Separator between boxes
-    widths = tuple(
-        max(len(row) for row in column.get("rows")) + 9 for column in columns
-    )  # width of each col
-    cols_done = [False] * len(columns)  # whether or not each column is done
-    lines = [""]
-
-    for i, column in enumerate(columns):
-        lines[0] += (
-            "{TL}"
-            + "{HZ}"
-            + column.get("title")
-            + "{HZ}" * (widths[i] - len(column.get("title")) - 1)
-            + "{TR}"
-            + sep
-        )
-
-    for line in itertools.zip_longest(*[column.get("rows") for column in columns]):
-        row = []
-        for colidx, column in enumerate(line):
-            width = widths[colidx]
-            done = cols_done[colidx]
-            if column is None:
-                if not done:
-                    # bottom border of column
-                    column = "{HZ}" * width
-                    row.append("{BL}" + column + "{BR}")
-                    cols_done[colidx] = True  # mark column as done
-                else:
-                    # leave empty
-                    row.append(" " * (width + 2))
-            else:
-                column += " " * (width - len(column))  # append padded spaces
-                row.append("{VT}" + column + "{VT}")
-
-        lines.append(sep.join(row))
-
-    final_row = []
-    for width, done in zip(widths, cols_done):
-        if not done:
-            final_row.append("{BL}" + "{HZ}" * width + "{BR}")
-        else:
-            final_row.append(" " * (width + 2))
-    lines.append(sep.join(final_row))
-
-    return "\n".join(lines).format(**borders)
diff --git a/tuxbot/core/utils/functions/extra.py b/tuxbot/core/utils/functions/extra.py
index 98c48ca..ace86f5 100644
--- a/tuxbot/core/utils/functions/extra.py
+++ b/tuxbot/core/utils/functions/extra.py
@@ -5,6 +5,11 @@ import discord
 from discord import Embed
 from discord.ext import commands, flags
 
+from rich.console import Console
+console = Console()
+
+console.clear()
+
 
 class ContextPlus(commands.Context):
     async def send(self, content=None, *args, **kwargs):
@@ -16,12 +21,11 @@ class ContextPlus(commands.Context):
             e = str(kwargs.get('embed').to_dict())
             e = e.replace(self.bot.config('core').get('token'), '<token>')
             e = yaml.load(e, Loader=yaml.FullLoader)
-
             kwargs['embed'] = Embed.from_dict(e)
 
         if (
             hasattr(self.command, "deletable") and self.command.deletable
-        ) and kwargs.pop("deletable", True):
+        ) or kwargs.pop("deletable", False):
             message = await super().send(content, *args, **kwargs)
             await message.add_reaction("🗑")
 
@@ -33,7 +37,10 @@ class ContextPlus(commands.Context):
                 )
 
             try:
-                await self.bot.wait_for("reaction_add", timeout=45.0, check=check)
+                await self.bot.wait_for(
+                    "reaction_add",
+                    timeout=42.0, check=check
+                )
             except asyncio.TimeoutError:
                 await message.remove_reaction("🗑", self.bot.user)
             else:
diff --git a/tuxbot/setup.py b/tuxbot/setup.py
index 38d9b6c..5aa8858 100644
--- a/tuxbot/setup.py
+++ b/tuxbot/setup.py
@@ -9,9 +9,9 @@ from rich.prompt import Prompt, IntPrompt
 from rich.console import Console
 from rich.rule import Rule
 from rich.traceback import install
-from rich import print
 
 from tuxbot.core.data_manager import config_dir, app_dir
+from tuxbot.core import config
 
 console = Console()
 console.clear()
@@ -20,56 +20,15 @@ install(console=console)
 try:
     config_dir.mkdir(parents=True, exist_ok=True)
 except PermissionError:
-    print(f"mkdir: cannot create directory '{config_dir}': Permission denied")
+    console.print(f"mkdir: cannot create directory '{config_dir}': Permission denied")
     sys.exit(1)
 
-config_file = config_dir / "config.json"
+app_config = config.ConfigFile(config_dir / "config.yaml", config.AppConfig)
 
-
-def load_existing_config() -> dict:
-    """Loading and returning configs.
-
-    Returns
-    -------
-    dict
-        a dict containing all configurations.
-
-    """
-    if not config_file.exists():
-        return {}
-
-    with config_file.open() as fs:
-        return json.load(fs)
-
-
-instances_data = load_existing_config()
-if not instances_data:
+if not app_config.config.instances:
     instances_list = []
 else:
-    instances_list = list(instances_data.keys())
-
-
-def save_config(name: str, data: dict, delete=False) -> NoReturn:
-    """save data in config file.
-
-    Parameters
-    ----------
-    name:str
-        name of instance.
-    data:dict
-        settings for `name` instance.
-    delete:bool
-        delete or no data.
-    """
-    _config = load_existing_config()
-
-    if delete and name in _config:
-        _config.pop(name)
-    else:
-        _config[name] = data
-
-    with config_file.open("w") as fs:
-        json.dump(_config, fs, indent=4)
+    instances_list = list(app_config.config.instances.keys())
 
 
 def get_name() -> str:
@@ -89,8 +48,8 @@ def get_name() -> str:
             console=console
         )
         if re.fullmatch(r"[a-zA-Z0-9_\-]*", name) is None:
-            print()
-            print("[prompt.invalid]ERROR: Invalid characters provided")
+            console.print()
+            console.print("[prompt.invalid]ERROR: Invalid characters provided")
             name = ""
     return name
 
@@ -111,14 +70,14 @@ def get_data_dir(instance_name: str) -> Path:
     """
     data_path = Path(app_dir.user_data_dir) / "data" / instance_name
     data_path_input = ""
-    print()
+    console.print()
 
     def make_data_dir(path: Path) -> Union[Path, str]:
         try:
             path.mkdir(parents=True, exist_ok=True)
         except OSError:
-            print()
-            print(
+            console.print()
+            console.print(
                 f"mkdir: cannot create directory '{path}': Permission denied"
             )
             path = ""
@@ -137,8 +96,8 @@ def get_data_dir(instance_name: str) -> Path:
         try:
             exists = data_path_input.exists()
         except OSError:
-            print()
-            print(
+            console.print()
+            console.print(
                 "[prompt.invalid]"
                 "Impossible to verify the validity of the path,"
                 " make sure it does not contain any invalid characters."
@@ -149,8 +108,8 @@ def get_data_dir(instance_name: str) -> Path:
         if data_path_input and not exists:
             data_path_input = make_data_dir(data_path_input)
 
-    print()
-    print(
+    console.print()
+    console.print(
         f"You have chosen {data_path_input} to be your config directory for "
         f"`{instance_name}` instance"
     )
@@ -160,7 +119,7 @@ def get_data_dir(instance_name: str) -> Path:
             choices=["y", "n"], default="y",
             console=console
     ) != "y":
-        print("Rerun the process to redo this configuration.")
+        console.print("Rerun the process to redo this configuration.")
         sys.exit(0)
 
     (data_path_input / "core").mkdir(parents=True, exist_ok=True)
@@ -191,7 +150,7 @@ def get_token() -> str:
                 r"|mfa\.[a-zA-Z0-9_\-]{84})",
                 token) \
                 is None:
-            print("[prompt.invalid]ERROR: Invalid token provided")
+            console.print("[prompt.invalid]ERROR: Invalid token provided")
             token = ""
     return token
 
@@ -234,7 +193,7 @@ def get_multiple(
         if new not in values:
             values.append(new)
         else:
-            print(
+            console.print(
                 f"[prompt.invalid]"
                 f"ERROR: `{new}` is already present, [i]ignored[/i]"
             )
@@ -250,21 +209,21 @@ def additional_config() -> dict:
     dict:
         Dict with cog name as key and configs as value.
     """
-    p = Path(r"tuxbot/cogs").glob("**/additional_config.json")
+    p = Path("tuxbot/cogs").glob("**/config.py")
     data = {}
 
     for file in p:
-        print("\n" * 4)
+        console.print("\n" * 4)
         cog_name = str(file.parent).split("/")[-1]
         data[cog_name] = {}
 
         with file.open("r") as f:
             data = json.load(f)
 
-        print(Rule(f"\nConfiguration for `{cog_name}` module"))
+        console.print(Rule(f"\nConfiguration for `{cog_name}` module"))
 
         for key, value in data.items():
-            print()
+            console.print()
             data[cog_name][key] = Prompt.ask(value["description"])
 
     return data
@@ -278,79 +237,62 @@ def finish_setup(data_dir: Path) -> NoReturn:
     data_dir:Path
         Where to save configs.
     """
-    print(
+    console.print(
         Rule(
             "Now, it's time to finish this setup by giving bot information"
         )
     )
-    print()
+    console.print()
 
     token = get_token()
 
-    print()
+    console.print()
     prefixes = get_multiple(
         "Choice a (or multiple) prefix for the bot", "Add another prefix ?",
         str
     )
 
-    print()
+    console.print()
     mentionable = Prompt.ask(
         "Does the bot answer if it's mentioned?",
         choices=["y", "n"],
         default="y"
     ) == "y"
 
-    print()
+    console.print()
     owners_id = get_multiple(
         "Give the owner id of this bot", "Add another owner ?", int
     )
 
-    cogs_config = additional_config()
+    # cogs_config = additional_config()
 
-    core_file = data_dir / "core" / "settings.json"
-    core = {
-        "token": token,
-        "prefixes": prefixes,
-        "mentionable": mentionable,
-        "owners_id": owners_id,
-        "locale": "en-US",
-    }
+    instance_config = config.ConfigFile(
+        str(data_dir / "config.yaml"), config.Config
+    )
 
-    with core_file.open("w") as fs:
-        json.dump(core, fs, indent=4)
-
-    for cog, data in cogs_config.items():
-        data_cog_dir = data_dir / "cogs" / cog
-        data_cog_dir.mkdir(parents=True, exist_ok=True)
-
-        data_cog_file = data_cog_dir / "settings.json"
-
-        with data_cog_file.open("w") as fs:
-            json.dump(data, fs, indent=4)
+    instance_config.config.Core.owners_id = owners_id
+    instance_config.config.Core.prefixes = prefixes
+    instance_config.config.Core.token = token
+    instance_config.config.Core.mentionable = mentionable
+    instance_config.config.Core.locale = "en-US"
 
 
 def basic_setup() -> NoReturn:
     """Configs who refer to instances.
 
     """
-    print(
+    console.print(
         Rule(
             "Hi ! it's time for you to give me information about you instance"
         )
     )
-    print()
+    console.print()
     name = get_name()
 
     data_dir = get_data_dir(name)
 
-    configs = load_existing_config()
-    instance_config = configs[name] if name in instances_list else {}
-
-    instance_config["DATA_PATH"] = str(data_dir.resolve())
-    instance_config["IS_RUNNING"] = False
-
     if name in instances_list:
-        print()
+        console.print()
         console.print(
             f"WARNING: An instance named `{name}` already exists "
             f"Continuing will overwrite this instance configs.", style="red"
@@ -359,17 +301,21 @@ def basic_setup() -> NoReturn:
                 "Are you sure you want to continue?",
                 choices=["y", "n"], default="n"
         ) == "n":
-            print("Abandon...")
+            console.print("Abandon...")
             sys.exit(0)
 
-    save_config(name, instance_config)
+    instance = config.Instance()
+    instance.path = str(data_dir.resolve())
+    instance.active = False
 
-    print("\n" * 4)
+    app_config.config.instances[name] = instance
+
+    console.print("\n" * 4)
 
     finish_setup(data_dir)
 
-    print()
-    print(
+    console.print()
+    console.print(
         f"Instance successfully created! "
         f"You can now run `tuxbot {name}` to launch this instance"
     )
@@ -392,8 +338,8 @@ def setup() -> NoReturn:
 
         basic_setup()
     except KeyboardInterrupt:
-        print("Exiting...")
-    except:
+        console.print("Exiting...")
+    except Exception:
         console.print_exception()