From 96618fa5025b957da8623194507ac8bf3d0a4481 Mon Sep 17 00:00:00 2001
From: Romain J <romain.ordi@gmail.com>
Date: Wed, 15 Jan 2020 21:40:12 +0100
Subject: [PATCH 1/3] fix(token): fix all possible flaw of leaked token

---
 bot.py                   |  6 +++++-
 utils/functions/extra.py | 25 ++++++++++++++++++++-----
 2 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/bot.py b/bot.py
index f06583f..e7b3bc0 100755
--- a/bot.py
+++ b/bot.py
@@ -14,6 +14,7 @@ from utils import Config
 from utils import Database
 from utils import Texts
 from utils import Version
+from utils import ContextPlus
 
 description = """
 Je suis TuxBot, le bot qui vit de l'OpenSource ! ;)
@@ -95,6 +96,9 @@ class TuxBot(commands.AutoShardedBot):
         return str(user.id) in self.config.get("permissions", "Owners").split(
             ', ')
 
+    async def get_context(self, message, *, cls=None):
+        return await super().get_context(message, cls=cls or ContextPlus)
+
     async def on_socket_response(self, msg):
         self._prev_events.append(msg)
 
@@ -112,7 +116,7 @@ class TuxBot(commands.AutoShardedBot):
             )
 
     async def process_commands(self, message: discord.message):
-        ctx = await self.get_context(message)
+        ctx: commands.Context = await self.get_context(message)
 
         if ctx.command is None:
             return
diff --git a/utils/functions/extra.py b/utils/functions/extra.py
index 318ab04..d2779f6 100644
--- a/utils/functions/extra.py
+++ b/utils/functions/extra.py
@@ -1,20 +1,35 @@
 from discord.ext import commands
+from utils import Config
 
 
-class commandsPlus(commands.Command):
+class CommandsPlus(commands.Command):
     def __init__(self, func, **kwargs):
         super().__init__(func, **kwargs)
         self.category = kwargs.get("category", 'other')
 
 
-def commandExtra(*args, **kwargs):
-    return commands.command(*args, **kwargs, cls=commandsPlus)
-
-
 class GroupPlus(commands.Group):
     def __init__(self, func, **kwargs):
         super().__init__(func, **kwargs)
         self.category = kwargs.get("category", 'other')
 
+
+class ContextPlus(commands.Context):
+    async def send(self, **kwargs):
+        config = Config('./configs/config.cfg')
+        content = kwargs.pop('content')
+
+        content = content.replace(config.get("bot", "Token"), 'Whoops! leaked token')
+        content = content.replace(config.get("webhook", "Token"), 'Whoops! leaked token')
+
+        kwargs['content'] = content
+
+        return await super().send(**kwargs)
+
+
+def commandExtra(*args, **kwargs):
+    return commands.command(*args, **kwargs, cls=CommandsPlus)
+
+
 def groupExtra(*args, **kwargs):
     return commands.group(*args, **kwargs, cls=GroupPlus)

From be1e6d24e40e9be342afa91adb9bdb16deda9289 Mon Sep 17 00:00:00 2001
From: Romain J <romain.ordi@gmail.com>
Date: Wed, 15 Jan 2020 22:56:54 +0100
Subject: [PATCH 2/3] breaking change !

update(database): change database ORM

todo: update Admin, Poll and User cogs
---
 bot.py                      | 51 ++++++++++++++++++-------------------
 cogs/Admin.py               |  2 +-
 cogs/Useful.py              |  7 -----
 configs/langs.json          |  5 ++++
 database.py                 | 19 ++++----------
 requirements.txt            |  3 ++-
 utils/__init__.py           |  2 --
 utils/functions/__init__.py |  6 +++++
 utils/functions/database.py | 11 ++++----
 utils/functions/extra.py    |  2 +-
 utils/functions/lang.py     | 21 +++++----------
 utils/models/__init__.py    | 14 +++++++---
 utils/models/alias.py       | 34 ++++++++-----------------
 utils/models/lang.py        | 12 ---------
 utils/models/poll.py        | 46 +++++++++++++++++----------------
 utils/models/warn.py        | 25 ++++++++----------
 16 files changed, 111 insertions(+), 149 deletions(-)
 create mode 100644 configs/langs.json
 delete mode 100644 utils/models/lang.py

diff --git a/bot.py b/bot.py
index e7b3bc0..4efe24d 100755
--- a/bot.py
+++ b/bot.py
@@ -10,11 +10,11 @@ import discord
 import git
 from discord.ext import commands
 
-from utils import Config
-from utils import Database
-from utils import Texts
-from utils import Version
-from utils import ContextPlus
+from utils.functions import Config
+from utils.functions import Database
+from utils.functions import Texts
+from utils.functions import Version
+from utils.functions import ContextPlus
 
 description = """
 Je suis TuxBot, le bot qui vit de l'OpenSource ! ;)
@@ -52,7 +52,7 @@ async def _prefix_callable(bot, message: discord.message) -> list:
 
 class TuxBot(commands.AutoShardedBot):
 
-    def __init__(self, database):
+    def __init__(self,):
         super().__init__(command_prefix=_prefix_callable, pm_help=None,
                          help_command=None, description=description,
                          help_attrs=dict(hidden=True),
@@ -63,20 +63,20 @@ class TuxBot(commands.AutoShardedBot):
         self.socket_stats = Counter()
         self.command_stats = Counter()
 
-        self.uptime: datetime = datetime.datetime.utcnow()
-        self._prev_events = deque(maxlen=10)
-        self.session = aiohttp.ClientSession(loop=self.loop)
-        self.database = database
-
         self.config = Config('./configs/config.cfg')
         self.prefixes = Config('./configs/prefixes.cfg')
         self.blacklist = Config('./configs/blacklist.cfg')
         self.fallbacks = Config('./configs/fallbacks.cfg')
         self.cluster = self.fallbacks.find('True', key='This', first=True)
 
+        self.uptime: datetime = datetime.datetime.utcnow()
+        self._prev_events = deque(maxlen=10)
+        self.session = aiohttp.ClientSession(loop=self.loop)
+        self.database = Database(self.config)
+
         self.version = Version(*version, pre_release='rc2', build=build)
-        self.owner: discord.User = discord.User
-        self.owners: List[discord.User] = []
+        self.owner_ids = self.config.get('permissions', 'Owners').split(', ')
+        self.owner_id = int(self.owner_ids[0])
 
         for extension in l_extensions:
             try:
@@ -93,8 +93,7 @@ class TuxBot(commands.AutoShardedBot):
                           + extension, exc_info=e)
 
     async def is_owner(self, user: discord.User) -> bool:
-        return str(user.id) in self.config.get("permissions", "Owners").split(
-            ', ')
+        return str(user.id) in self.owner_ids
 
     async def get_context(self, message, *, cls=None):
         return await super().get_context(message, cls=cls or ContextPlus)
@@ -114,6 +113,8 @@ class TuxBot(commands.AutoShardedBot):
                     "Sorry. This command is disabled and cannot be used."
                 )
             )
+        elif isinstance(error, commands.CommandOnCooldown):
+            await ctx.send(str(error))
 
     async def process_commands(self, message: discord.message):
         ctx: commands.Context = await self.get_context(message)
@@ -193,13 +194,13 @@ class TuxBot(commands.AutoShardedBot):
 
 @contextlib.contextmanager
 def setup_logging():
+    logging.getLogger('discord').setLevel(logging.INFO)
+    logging.getLogger('discord.http').setLevel(logging.WARNING)
+
+    log = logging.getLogger()
+    log.setLevel(logging.INFO)
+
     try:
-        logging.getLogger('discord').setLevel(logging.INFO)
-        logging.getLogger('discord.http').setLevel(logging.WARNING)
-
-        log = logging.getLogger()
-        log.setLevel(logging.INFO)
-
         handler = logging.FileHandler(filename='logs/tuxbot.log',
                                       encoding='utf-8', mode='w')
         fmt = logging.Formatter('[{levelname:<7}] [{asctime}]'
@@ -218,14 +219,12 @@ def setup_logging():
 
 
 if __name__ == "__main__":
-    log = logging.getLogger()
-
     print(Texts().get('Starting...'))
 
-    bot = TuxBot(Database(Config("./configs/config.cfg")))
+    app = TuxBot()
 
     try:
         with setup_logging():
-            bot.run()
+            app.run()
     except KeyboardInterrupt:
-        bot.close()
+        app.close()
diff --git a/cogs/Admin.py b/cogs/Admin.py
index b9dd6a3..80510eb 100644
--- a/cogs/Admin.py
+++ b/cogs/Admin.py
@@ -9,7 +9,7 @@ from discord.ext import commands
 
 from bot import TuxBot
 from utils import Texts
-from utils import WarnModel, LangModel
+from utils.models import WarnModel
 from utils import commandExtra, groupExtra
 
 log = logging.getLogger(__name__)
diff --git a/cogs/Useful.py b/cogs/Useful.py
index 6cdafd3..56506c1 100644
--- a/cogs/Useful.py
+++ b/cogs/Useful.py
@@ -150,13 +150,6 @@ class Useful(commands.Cog):
 
     ###########################################################################
 
-    @commands.Cog.listener()
-    async def on_command_error(self, ctx, error):
-        if isinstance(error, commands.CommandOnCooldown):
-            await ctx.send(error)
-
-    ###########################################################################
-
     @commandExtra(name='getheaders', category='network')
     async def _getheaders(self, ctx: commands.Context, addr: str):
         if (addr.startswith('http') or addr.startswith('ftp')) is not True:
diff --git a/configs/langs.json b/configs/langs.json
new file mode 100644
index 0000000..f7b1b6f
--- /dev/null
+++ b/configs/langs.json
@@ -0,0 +1,5 @@
+{
+  "default": "fr",
+  "available": ["en", "fr"],
+  "280805240977227776": "fr"
+}
\ No newline at end of file
diff --git a/database.py b/database.py
index 57a83e8..49127d5 100644
--- a/database.py
+++ b/database.py
@@ -1,7 +1,5 @@
-from utils import Config
-from utils.models import Base
-from utils import Database
-from utils.models.lang import LangModel
+import sqlalchemy
+from utils.models import database, metadata
 import argparse
 
 parser = argparse.ArgumentParser()
@@ -9,20 +7,13 @@ parser.add_argument("-m", "--migrate", action="store_true")
 parser.add_argument("-s", "--seed", action="store_true")
 args = parser.parse_args()
 
-database = Database(Config("./configs/config.cfg"))
-
 if args.migrate:
     print("Migrate...")
-    Base.metadata.create_all(database.engine)
+    engine = sqlalchemy.create_engine(str(database.url))
+    metadata.create_all(engine)
     print("Done!")
 
 if args.seed:
     print('Seeding...')
-    default = LangModel(key="default", value="fr")
-    available = LangModel(key="available", value="fr,en")
-
-    database.session.add(default)
-    database.session.add(available)
-
-    database.session.commit()
+    # todo: add seeding
     print("Done!")
diff --git a/requirements.txt b/requirements.txt
index 41b86e1..442102d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,8 @@ humanize
 git+https://github.com/Rapptz/discord.py@master
 jishaku
 gitpython
-sqlalchemy
+orm
+asyncpg
 psycopg2
 configparser
 psutil
diff --git a/utils/__init__.py b/utils/__init__.py
index a60b691..ff23e97 100755
--- a/utils/__init__.py
+++ b/utils/__init__.py
@@ -1,5 +1,3 @@
-from .models import *
-
 from utils.functions.config import *
 from utils.functions.lang import *
 from utils.functions.version import *
diff --git a/utils/functions/__init__.py b/utils/functions/__init__.py
index e69de29..171e8cc 100644
--- a/utils/functions/__init__.py
+++ b/utils/functions/__init__.py
@@ -0,0 +1,6 @@
+from .config import Config
+from .database import Database
+from .extra import *
+from .lang import Texts
+from .paginator import *
+from .version import Version
diff --git a/utils/functions/database.py b/utils/functions/database.py
index 9a7e7a3..20388f0 100644
--- a/utils/functions/database.py
+++ b/utils/functions/database.py
@@ -1,7 +1,7 @@
 from .config import Config
 
-from sqlalchemy import create_engine
-from sqlalchemy.orm import sessionmaker, session
+import sqlalchemy
+import databases
 
 
 class Database:
@@ -10,8 +10,7 @@ class Database:
         postgresql = 'postgresql://{}:{}@{}/{}'.format(
             conf_postgresql.get("Username"), conf_postgresql.get("Password"),
             conf_postgresql.get("Host"), conf_postgresql.get("DBName"))
-        self.engine = create_engine(postgresql, echo=False)
 
-        Session = sessionmaker()
-        Session.configure(bind=self.engine)
-        self.session: session = Session()
+        self.database = databases.Database(postgresql)
+        self.metadata = sqlalchemy.MetaData()
+        self.engine = sqlalchemy.create_engine(str(self.database.url))
diff --git a/utils/functions/extra.py b/utils/functions/extra.py
index d2779f6..26b7d05 100644
--- a/utils/functions/extra.py
+++ b/utils/functions/extra.py
@@ -1,5 +1,5 @@
 from discord.ext import commands
-from utils import Config
+from utils.functions import Config
 
 
 class CommandsPlus(commands.Command):
diff --git a/utils/functions/lang.py b/utils/functions/lang.py
index 4be303b..64f2648 100644
--- a/utils/functions/lang.py
+++ b/utils/functions/lang.py
@@ -1,8 +1,6 @@
 import gettext
-from .config import Config
-from .database import Database
+import json
 
-from utils.models.lang import LangModel
 from discord.ext import commands
 
 
@@ -22,17 +20,10 @@ class Texts:
 
     @staticmethod
     def get_locale(ctx):
-        database = Database(Config("./configs/config.cfg"))
+        with open('./configs/langs.json') as f:
+            data = json.load(f)
 
         if ctx is not None:
-            current = database.session\
-                .query(LangModel.value)\
-                .filter(LangModel.key == str(ctx.guild.id))
-            if current.count() > 0:
-                return current.one()[0]
-
-        default = database.session\
-            .query(LangModel.value)\
-            .filter(LangModel.key == 'default')\
-            .one()[0]
-        return default
+            return data.get(str(ctx.guild.id), data['default'])
+        else:
+            return data['default']
diff --git a/utils/models/__init__.py b/utils/models/__init__.py
index 8407711..e3a855a 100644
--- a/utils/models/__init__.py
+++ b/utils/models/__init__.py
@@ -1,7 +1,15 @@
-from sqlalchemy.ext.declarative import declarative_base
-Base = declarative_base()
+import databases
+import sqlalchemy
+from utils.functions import Config
+
+conf_postgresql = Config('./configs/config.cfg')["postgresql"]
+postgresql = 'postgresql://{}:{}@{}/{}'.format(
+    conf_postgresql.get("Username"), conf_postgresql.get("Password"),
+    conf_postgresql.get("Host"), conf_postgresql.get("DBName"))
+
+database = databases.Database(postgresql)
+metadata = sqlalchemy.MetaData()
 
-from .lang import LangModel
 from .warn import WarnModel
 from .poll import PollModel, ResponsesModel
 from .alias import AliasesModel
diff --git a/utils/models/alias.py b/utils/models/alias.py
index 7ab63df..4a1a96c 100644
--- a/utils/models/alias.py
+++ b/utils/models/alias.py
@@ -1,28 +1,14 @@
-from sqlalchemy import Column, String, BigInteger, Integer
-
-from . import Base
+import orm
+from . import database, metadata
 
 
-class AliasesModel(Base):
+class AliasesModel(orm.Model):
     __tablename__ = 'aliases'
+    __database__ = database
+    __metadata__ = metadata
 
-    id = Column(Integer, primary_key=True)
-    user_id = Column(BigInteger)
-    alias = Column(String)
-    command = Column(String)
-    guild = Column(String)
-
-    def __repr__(self):
-        return "<AliasesModel(" \
-               "id='%s', " \
-               "user_id='%s', " \
-               "alias='%s', " \
-               "command='%s', " \
-               "guild='%s', " \
-               ")>" % (
-                   self.id,
-                   self.user_id,
-                   self.alias,
-                   self.command,
-                   self.guild
-               )
+    id = orm.Integer(primary_key=True)
+    user_id = orm.String(max_length=18)
+    alias = orm.String(max_length=255)
+    command = orm.String(max_length=255)
+    guild = orm.String(max_length=255)
diff --git a/utils/models/lang.py b/utils/models/lang.py
deleted file mode 100644
index 2ceb555..0000000
--- a/utils/models/lang.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from . import Base
-from sqlalchemy import Column, String
-
-
-class LangModel(Base):
-    __tablename__ = 'langs'
-
-    key = Column(String, primary_key=True)
-    value = Column(String)
-
-    def __repr__(self):
-        return "<LangModel(key='%s', locale='%s')>" % (self.key, self.value)
diff --git a/utils/models/poll.py b/utils/models/poll.py
index 2d3d69e..a2f793e 100644
--- a/utils/models/poll.py
+++ b/utils/models/poll.py
@@ -1,27 +1,29 @@
-from . import Base
-from sqlalchemy import Column, Integer, BigInteger, JSON, ForeignKey, Boolean
-from sqlalchemy.orm import relationship
+import orm
+from . import database, metadata
 
 
-class PollModel(Base):
-    __tablename__ = 'polls'
-
-    id = Column(Integer, primary_key=True, autoincrement=True)
-    channel_id = Column(BigInteger)
-    message_id = Column(BigInteger)
-
-    content = Column(JSON)
-    is_anonymous = Column(Boolean)
-
-    available_choices = Column(Integer)
-    choice = relationship("ResponsesModel")
-
-
-class ResponsesModel(Base):
+class ResponsesModel(orm.Model):
     __tablename__ = 'responses'
+    __database__ = database
+    __metadata__ = metadata
 
-    id = Column(Integer, primary_key=True, autoincrement=True)
-    user = Column(BigInteger)
+    id = orm.Integer(primary_key=True)
+    user = orm.String(max_length=18)
 
-    poll_id = Column(Integer, ForeignKey('polls.id'))
-    choice = Column(Integer)
+    choice = orm.Integer()
+
+
+class PollModel(orm.Model):
+    __tablename__ = 'polls'
+    __database__ = database
+    __metadata__ = metadata
+
+    id = orm.Integer(primary_key=True)
+    channel_id = orm.String(max_length=18)
+    message_id = orm.String(max_length=18)
+
+    content = orm.JSON()
+    is_anonymous = orm.Boolean()
+
+    available_choices = orm.Integer()
+    choice = orm.ForeignKey(ResponsesModel)
diff --git a/utils/models/warn.py b/utils/models/warn.py
index b9d4674..77f8833 100644
--- a/utils/models/warn.py
+++ b/utils/models/warn.py
@@ -1,19 +1,14 @@
-import datetime
-
-from . import Base
-from sqlalchemy import Column, Integer, String, BIGINT, TIMESTAMP
+import orm
+from . import database, metadata
 
 
-class WarnModel(Base):
+class WarnModel(orm.Model):
     __tablename__ = 'warns'
+    __database__ = database
+    __metadata__ = metadata
 
-    id = Column(Integer, primary_key=True)
-    server_id = Column(BIGINT)
-    user_id = Column(BIGINT)
-    reason = Column(String)
-    created_at = Column(TIMESTAMP, default=datetime.datetime.now())
-
-    def __repr__(self):
-        return "<WarnModel(server_id='%s', user_id='%s', reason='%s', " \
-               "created_at='%s')>" \
-               % (self.server_id, self.user_id, self.reason, self.created_at)
+    id = orm.Integer(primary_key=True)
+    server_id = orm.String(max_length=18)
+    user_id = orm.String(max_length=18)
+    reason = orm.String(max_length=255)
+    created_at = orm.DateTime()

From cce7bb409303e9ad27ef4e5617d0bc9068810f13 Mon Sep 17 00:00:00 2001
From: Romain J <romain.ordi@gmail.com>
Date: Tue, 4 Feb 2020 18:47:11 +0100
Subject: [PATCH 3/3] =?UTF-8?q?=E2=A0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 bot.py                     | 53 +++++++++++++++++++++---------------
 cogs/API.py                | 56 ++++++++++++++++++++++++++++++++++++++
 cogs/Admin.py              | 48 +++++++++++++++-----------------
 cogs/Logs.py               |  8 +++---
 cogs/Poll.py               |  4 +--
 cogs/Useful.py             | 18 ++++++------
 cogs/User.py               |  4 +--
 configs/config.cfg.example |  6 +++-
 configs/langs.json         |  5 ----
 configs/prefixes.cfg       | 18 ------------
 utils/functions/extra.py   | 11 +++-----
 utils/functions/lang.py    | 19 ++++++++-----
 utils/models/__init__.py   |  3 ++
 13 files changed, 150 insertions(+), 103 deletions(-)
 create mode 100644 cogs/API.py
 delete mode 100644 configs/langs.json
 delete mode 100644 configs/prefixes.cfg

diff --git a/bot.py b/bot.py
index 4efe24d..5c384bf 100755
--- a/bot.py
+++ b/bot.py
@@ -1,5 +1,6 @@
 import contextlib
 import datetime
+import json
 import logging
 import sys
 from collections import deque, Counter
@@ -8,14 +9,16 @@ from typing import List
 import aiohttp
 import discord
 import git
+import sqlalchemy
 from discord.ext import commands
 
 from utils.functions import Config
-from utils.functions import Database
 from utils.functions import Texts
 from utils.functions import Version
 from utils.functions import ContextPlus
 
+from utils.models import metadata, database
+
 description = """
 Je suis TuxBot, le bot qui vit de l'OpenSource ! ;)
 """
@@ -27,6 +30,7 @@ log = logging.getLogger(__name__)
 
 l_extensions: List[str] = [
     'cogs.Admin',
+    'cogs.API',
     'cogs.Help',
     'cogs.Logs',
     # 'cogs.Monitoring',
@@ -38,21 +42,23 @@ l_extensions: List[str] = [
 
 
 async def _prefix_callable(bot, message: discord.message) -> list:
-    extras = [bot.cluster.get('Name') + '.', '.']
-    if message.guild is not None:
-        if str(message.guild.id) in bot.prefixes:
-            extras.extend(
-                bot.prefixes.get(str(message.guild.id), "prefixes").split(
-                    bot.config.get("misc", "Separator")
-                )
-            )
+    try:
+        with open(f'./configs/guilds/{message.guild.id}.json', 'r') as f:
+            data = json.load(f)
+
+        custom_prefix = data['prefixes']
+    except FileNotFoundError:
+        custom_prefix = ['']
+
+    extras = [bot.cluster.get('Name') + '.']
+    extras.extend(custom_prefix)
 
     return commands.when_mentioned_or(*extras)(bot, message)
 
 
 class TuxBot(commands.AutoShardedBot):
 
-    def __init__(self,):
+    def __init__(self, ):
         super().__init__(command_prefix=_prefix_callable, pm_help=None,
                          help_command=None, description=description,
                          help_attrs=dict(hidden=True),
@@ -64,7 +70,6 @@ class TuxBot(commands.AutoShardedBot):
         self.command_stats = Counter()
 
         self.config = Config('./configs/config.cfg')
-        self.prefixes = Config('./configs/prefixes.cfg')
         self.blacklist = Config('./configs/blacklist.cfg')
         self.fallbacks = Config('./configs/fallbacks.cfg')
         self.cluster = self.fallbacks.find('True', key='This', first=True)
@@ -72,11 +77,14 @@ class TuxBot(commands.AutoShardedBot):
         self.uptime: datetime = datetime.datetime.utcnow()
         self._prev_events = deque(maxlen=10)
         self.session = aiohttp.ClientSession(loop=self.loop)
-        self.database = Database(self.config)
+
+        self.database, self.metadata = database, metadata
+        self.engine = sqlalchemy.create_engine(str(self.database.url))
+        self.metadata.create_all(self.engine)
 
         self.version = Version(*version, pre_release='rc2', build=build)
-        self.owner_ids = self.config.get('permissions', 'Owners').split(', ')
-        self.owner_id = int(self.owner_ids[0])
+        self.owners_id = [int(owner_id) for owner_id in self.config.get('permissions', 'Owners').split(', ')]
+        self.owner_id = int(self.owners_id[0])
 
         for extension in l_extensions:
             try:
@@ -92,8 +100,16 @@ class TuxBot(commands.AutoShardedBot):
                 log.error(Texts().get("Failed to load extension : ")
                           + extension, exc_info=e)
 
+    @property
+    def owner(self):
+        return self.get_user(self.owner_id)
+
+    @property
+    def owners(self):
+        return [self.get_user(owner_id) for owner_id in self.owners_id]
+
     async def is_owner(self, user: discord.User) -> bool:
-        return str(user.id) in self.owner_ids
+        return user in self.owners
 
     async def get_context(self, message, *, cls=None):
         return await super().get_context(message, cls=cls or ContextPlus)
@@ -156,13 +172,6 @@ class TuxBot(commands.AutoShardedBot):
         print('-' * 60)
 
         await self.change_presence(**presence)
-        self.owner = await self.fetch_user(
-            int(self.config.get('permissions', 'Owners').split(', ')[0])
-        )
-        for owner in self.config.get('permissions', 'Owners').split(', '):
-            self.owners.append(
-                await self.fetch_user(int(owner))
-            )
 
     @staticmethod
     async def on_resumed():
diff --git a/cogs/API.py b/cogs/API.py
new file mode 100644
index 0000000..a483b25
--- /dev/null
+++ b/cogs/API.py
@@ -0,0 +1,56 @@
+import logging
+
+import discord
+from aiohttp import web
+from discord.ext import commands
+
+from bot import TuxBot
+
+log = logging.getLogger(__name__)
+
+
+class API(commands.Cog):
+
+    def __init__(self, bot: TuxBot):
+        self.bot = bot
+        self.site = web.TCPSite
+
+        app = web.Application()
+        app.add_routes([web.get('/users/{user_id}', self.users)])
+
+        self.runner = web.AppRunner(app)
+        self.bot.loop.create_task(self.start_HTTPMonitoring_server())
+
+    async def start_HTTPMonitoring_server(self):
+        host = self.bot.config.get('API', 'Host')
+        port = self.bot.config.get('API', 'Port')
+
+        print(f"Starting API server on {host}:{port}")
+
+        await self.runner.setup()
+        self.site = web.TCPSite(self.runner, host, port)
+        await self.site.start()
+
+    async def users(self, request):
+        try:
+            user = await self.bot.fetch_user(request.match_info['user_id'])
+        except discord.NotFound:
+            return web.Response(status=404)
+
+        json = {
+            'id': user.id,
+            'username': user.name,
+            'discriminator': user.discriminator,
+            'avatar': user.avatar,
+            'default_avatar': user.default_avatar.value,
+            'bot': user.bot,
+            'system': user.system,
+        }
+
+        return web.json_response(
+            json
+        )
+
+
+def setup(bot: TuxBot):
+    bot.add_cog(API(bot))
diff --git a/cogs/Admin.py b/cogs/Admin.py
index 80510eb..0fa400d 100644
--- a/cogs/Admin.py
+++ b/cogs/Admin.py
@@ -10,7 +10,7 @@ from discord.ext import commands
 from bot import TuxBot
 from utils import Texts
 from utils.models import WarnModel
-from utils import commandExtra, groupExtra
+from utils import command_extra, group_extra
 
 log = logging.getLogger(__name__)
 
@@ -65,7 +65,7 @@ class Admin(commands.Cog):
 
     ###########################################################################
 
-    @groupExtra(name='say', invoke_without_command=True, category='text')
+    @group_extra(name='say', invoke_without_command=True, category='text')
     async def _say(self, ctx: commands.Context, *, content: str):
         if ctx.invoked_subcommand is None:
             try:
@@ -105,7 +105,7 @@ class Admin(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='ban', category='administration')
+    @command_extra(name='ban', category='administration')
     async def _ban(self, ctx: commands.Context, user: discord.Member, *,
                    reason=""):
         try:
@@ -132,7 +132,7 @@ class Admin(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='kick', category='administration')
+    @command_extra(name='kick', category='administration')
     async def _kick(self, ctx: commands.Context, user: discord.Member, *,
                     reason=""):
         try:
@@ -159,7 +159,7 @@ class Admin(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='clear', category='text')
+    @command_extra(name='clear', category='text')
     async def _clear(self, ctx: commands.Context, count: int):
         try:
             await ctx.message.delete()
@@ -169,7 +169,7 @@ class Admin(commands.Cog):
 
     ###########################################################################
 
-    @groupExtra(name='react', category='text')
+    @group_extra(name='react', category='text')
     async def _react(self, ctx: commands.Context):
         if ctx.invoked_subcommand is None:
             await ctx.send_help('react')
@@ -203,7 +203,7 @@ class Admin(commands.Cog):
 
     ###########################################################################
 
-    @groupExtra(name='delete', invoke_without_command=True, category='text')
+    @group_extra(name='delete', invoke_without_command=True, category='text')
     async def _delete(self, ctx: commands.Context, message_id: int):
         try:
             await ctx.message.delete()
@@ -229,12 +229,14 @@ class Admin(commands.Cog):
 
         try:
             message: discord.Message = await channel.fetch_message(
-                message_id)
+                message_id
+            )
             await message.delete()
         except (discord.errors.NotFound, discord.errors.Forbidden):
             await ctx.send(
                 Texts('utils', ctx).get("Unable to find the message"),
-                delete_after=5)
+                delete_after=5
+            )
 
     ###########################################################################
 
@@ -242,24 +244,18 @@ class Admin(commands.Cog):
                        member: discord.Member = False):
         await ctx.trigger_typing()
 
-        week_ago = datetime.datetime.now() - datetime.timedelta(weeks=6)
-
         if member:
-            warns = self.bot.database.session \
-                .query(WarnModel) \
-                .filter(WarnModel.user_id == member.id,
-                        WarnModel.created_at > week_ago,
-                        WarnModel.server_id == ctx.guild.id) \
-                .order_by(WarnModel.created_at.desc())
+            warns = WarnModel.objects.filter(
+                server_id=str(ctx.guild.id),
+                user_id=member.id
+            )
         else:
-            warns = self.bot.database.session \
-                .query(WarnModel) \
-                .filter(WarnModel.created_at > week_ago,
-                        WarnModel.server_id == ctx.guild.id) \
-                .order_by(WarnModel.created_at.desc())
+            warns = WarnModel.objects.filter(
+                server_id=str(ctx.guild.id)
+            )
         warns_list = ''
 
-        for warn in warns:
+        for warn in await warns.all():
             row_id = warn.id
             user_id = warn.user_id
             user = await self.bot.fetch_user(user_id)
@@ -283,7 +279,7 @@ class Admin(commands.Cog):
         self.bot.database.session.add(warn)
         self.bot.database.session.commit()
 
-    @groupExtra(name='warn', aliases=['warns'], category='administration')
+    @group_extra(name='warn', aliases=['warns'], category='administration')
     async def _warn(self, ctx: commands.Context):
         await ctx.trigger_typing()
         if ctx.invoked_subcommand is None:
@@ -412,7 +408,7 @@ class Admin(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='language', aliases=['lang', 'langue', 'langage'], category='server')
+    @command_extra(name='language', aliases=['lang', 'langue', 'langage'], category='server')
     async def _language(self, ctx: commands.Context, locale: str):
         available = self.bot.database.session \
             .query(LangModel.value) \
@@ -443,7 +439,7 @@ class Admin(commands.Cog):
 
     ###########################################################################
 
-    @groupExtra(name='prefix', aliases=['prefixes'], category='server')
+    @group_extra(name='prefix', aliases=['prefixes'], category='server')
     async def _prefix(self, ctx: commands.Context):
         if ctx.invoked_subcommand is None:
             await ctx.send_help('prefix')
diff --git a/cogs/Logs.py b/cogs/Logs.py
index 3642367..7caa4ec 100644
--- a/cogs/Logs.py
+++ b/cogs/Logs.py
@@ -20,7 +20,7 @@ from discord.ext import commands, tasks
 
 from bot import TuxBot
 from utils import Texts
-from utils import commandExtra
+from utils import command_extra
 
 log = logging.getLogger(__name__)
 
@@ -243,7 +243,7 @@ class Logs(commands.Cog):
         msg = f'{emoji} `[{dt:%Y-%m-%d %H:%M:%S}] {record.message}`'
         await self.webhook.send(msg)
 
-    @commandExtra(name='commandstats', hidden=True, category='misc')
+    @command_extra(name='commandstats', hidden=True, category='misc')
     @commands.is_owner()
     async def _commandstats(self, ctx, limit=20):
         counter = self.bot.command_stats
@@ -258,7 +258,7 @@ class Logs(commands.Cog):
 
         await ctx.send(f'```\n{output}\n```')
 
-    @commandExtra(name='socketstats', hidden=True, category='misc')
+    @command_extra(name='socketstats', hidden=True, category='misc')
     @commands.is_owner()
     async def _socketstats(self, ctx):
         delta = datetime.datetime.utcnow() - self.bot.uptime
@@ -268,7 +268,7 @@ class Logs(commands.Cog):
         await ctx.send(
             f'{total} socket events observed ({cpm:.2f}/minute):\n{self.bot.socket_stats}')
 
-    @commandExtra(name='uptime', category='misc')
+    @command_extra(name='uptime', category='misc')
     async def _uptime(self, ctx):
         uptime = humanize.naturaltime(
             datetime.datetime.utcnow() - self.bot.uptime)
diff --git a/cogs/Poll.py b/cogs/Poll.py
index 62beac7..44dd1eb 100644
--- a/cogs/Poll.py
+++ b/cogs/Poll.py
@@ -10,7 +10,7 @@ from bot import TuxBot
 from utils import PollModel, ResponsesModel
 from utils import Texts
 from utils.functions import emotes as utils_emotes
-from utils import groupExtra
+from utils import group_extra
 
 log = logging.getLogger(__name__)
 
@@ -205,7 +205,7 @@ class Poll(commands.Cog):
         poll.content = json.dumps(content)
         self.bot.database.session.commit()
 
-    @groupExtra(name='poll', aliases=['sondage'], category='poll')
+    @group_extra(name='poll', aliases=['sondage'], category='poll')
     async def _poll(self, ctx: commands.Context):
         if ctx.invoked_subcommand is None:
             await ctx.send_help('poll')
diff --git a/cogs/Useful.py b/cogs/Useful.py
index 56506c1..a79b60c 100644
--- a/cogs/Useful.py
+++ b/cogs/Useful.py
@@ -23,7 +23,7 @@ from tcp_latency import measure_latency
 
 from bot import TuxBot
 from utils import Texts
-from utils import commandExtra, groupExtra
+from utils import command_extra, group_extra
 
 log = logging.getLogger(__name__)
 
@@ -86,7 +86,7 @@ class Useful(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='iplocalise', category='network')
+    @command_extra(name='iplocalise', category='network')
     async def _iplocalise(self, ctx: commands.Context, addr, ip_type=''):
         addr = re.sub(r'http(s?)://', '', addr)
         addr = addr[:-1] if addr.endswith('/') else addr
@@ -150,7 +150,7 @@ class Useful(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='getheaders', category='network')
+    @command_extra(name='getheaders', category='network')
     async def _getheaders(self, ctx: commands.Context, addr: str):
         if (addr.startswith('http') or addr.startswith('ftp')) is not True:
             addr = f"http://{addr}"
@@ -180,7 +180,7 @@ class Useful(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='git', aliases=['sources', 'source', 'github'], category='misc')
+    @command_extra(name='git', aliases=['sources', 'source', 'github'], category='misc')
     async def _git(self, ctx):
         e = discord.Embed(
             title=Texts('useful', ctx).get('git repo'),
@@ -195,7 +195,7 @@ class Useful(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='quote', category='misc')
+    @command_extra(name='quote', category='misc')
     async def _quote(self, ctx, message_id: discord.Message):
         e = discord.Embed(
             colour=message_id.author.colour,
@@ -217,7 +217,7 @@ class Useful(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='ping', category='network')
+    @command_extra(name='ping', category='network')
     async def _ping(self, ctx: commands.Context):
         start = time.perf_counter()
         await ctx.trigger_typing()
@@ -235,7 +235,7 @@ class Useful(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='info', aliases=['about'], category='misc')
+    @command_extra(name='info', aliases=['about'], category='misc')
     async def _info(self, ctx: commands.Context):
         proc = psutil.Process()
         total, python = self.fetch_info()
@@ -318,7 +318,7 @@ class Useful(commands.Cog):
 
     ###########################################################################
 
-    @commandExtra(name='credits', aliases=['contributors', 'authors'], category='misc')
+    @command_extra(name='credits', aliases=['contributors', 'authors'], category='misc')
     async def _credits(self, ctx: commands.Context):
         e = discord.Embed(
             title=Texts('useful', ctx).get('Contributors'),
@@ -342,7 +342,7 @@ class Useful(commands.Cog):
         await ctx.send(embed=e)
 
     ###########################################################################
-    @groupExtra(name='cb', aliases=['cc'], category='misc')
+    @group_extra(name='cb', aliases=['cc'], category='misc')
     @commands.cooldown(1, 5, type=commands.BucketType.user)
     async def _cb(self, ctx: commands.Context):
         if ctx.invoked_subcommand is None:
diff --git a/cogs/User.py b/cogs/User.py
index c883b63..ac0c830 100644
--- a/cogs/User.py
+++ b/cogs/User.py
@@ -5,7 +5,7 @@ from discord.ext import commands
 from bot import TuxBot
 from utils import AliasesModel
 from utils import Texts
-from utils import groupExtra
+from utils import group_extra
 
 log = logging.getLogger(__name__)
 
@@ -19,7 +19,7 @@ class User(commands.Cog):
 
     ###########################################################################
 
-    @groupExtra(name='alias', aliases=['aliases'], category='alias')
+    @group_extra(name='alias', aliases=['aliases'], category='alias')
     async def _alias(self, ctx: commands.Context):
         if ctx.invoked_subcommand is None:
             await ctx.send_help('alias')
diff --git a/configs/config.cfg.example b/configs/config.cfg.example
index 6bc3eb2..e156f5d 100644
--- a/configs/config.cfg.example
+++ b/configs/config.cfg.example
@@ -17,4 +17,8 @@ ID =
 Token =
 
 [misc]
-Separator =
\ No newline at end of file
+Separator =
+
+[API]
+Host =
+Port =
\ No newline at end of file
diff --git a/configs/langs.json b/configs/langs.json
deleted file mode 100644
index f7b1b6f..0000000
--- a/configs/langs.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "default": "fr",
-  "available": ["en", "fr"],
-  "280805240977227776": "fr"
-}
\ No newline at end of file
diff --git a/configs/prefixes.cfg b/configs/prefixes.cfg
deleted file mode 100644
index 1924607..0000000
--- a/configs/prefixes.cfg
+++ /dev/null
@@ -1,18 +0,0 @@
-[280805240977227776]
-prefixes = b1.
-
-[303633056944881686]
-prefixes = b1.
-
-[373881878471770112]
-prefixes = b1.
-
-[336642139381301249]
-prefixes = ba.
-
-[274247231534792704]
-prefixes = test.
-
-[528679953399676938]
-prefixes = test.
-
diff --git a/utils/functions/extra.py b/utils/functions/extra.py
index 26b7d05..b07ad88 100644
--- a/utils/functions/extra.py
+++ b/utils/functions/extra.py
@@ -15,21 +15,18 @@ class GroupPlus(commands.Group):
 
 
 class ContextPlus(commands.Context):
-    async def send(self, **kwargs):
+    async def send(self, content=None, **kwargs):
         config = Config('./configs/config.cfg')
-        content = kwargs.pop('content')
 
         content = content.replace(config.get("bot", "Token"), 'Whoops! leaked token')
         content = content.replace(config.get("webhook", "Token"), 'Whoops! leaked token')
 
-        kwargs['content'] = content
-
-        return await super().send(**kwargs)
+        return await super().send(content, **kwargs)
 
 
-def commandExtra(*args, **kwargs):
+def command_extra(*args, **kwargs):
     return commands.command(*args, **kwargs, cls=CommandsPlus)
 
 
-def groupExtra(*args, **kwargs):
+def group_extra(*args, **kwargs):
     return commands.group(*args, **kwargs, cls=GroupPlus)
diff --git a/utils/functions/lang.py b/utils/functions/lang.py
index 64f2648..fd3330b 100644
--- a/utils/functions/lang.py
+++ b/utils/functions/lang.py
@@ -19,11 +19,16 @@ class Texts:
         self.locale = lang
 
     @staticmethod
-    def get_locale(ctx):
-        with open('./configs/langs.json') as f:
-            data = json.load(f)
-
+    def get_locale(ctx: commands.Context):
+        lang = 'fr'
         if ctx is not None:
-            return data.get(str(ctx.guild.id), data['default'])
-        else:
-            return data['default']
+            try:
+                with open(f'./configs/guilds/{ctx.guild.id}.json', 'r') as f:
+                    data = json.load(f)
+
+                lang = data['lang']
+
+            except FileNotFoundError:
+                pass
+
+        return lang
diff --git a/utils/models/__init__.py b/utils/models/__init__.py
index e3a855a..36ba5cc 100644
--- a/utils/models/__init__.py
+++ b/utils/models/__init__.py
@@ -10,6 +10,9 @@ postgresql = 'postgresql://{}:{}@{}/{}'.format(
 database = databases.Database(postgresql)
 metadata = sqlalchemy.MetaData()
 
+engine = sqlalchemy.create_engine(str(database.url))
+metadata.create_all(engine)
+
 from .warn import WarnModel
 from .poll import PollModel, ResponsesModel
 from .alias import AliasesModel