Add version system
Remove BaseClass verification
This commit is contained in:
parent
52bcb60e3a
commit
273d92ec5e
2
Pipfile
2
Pipfile
@ -4,8 +4,8 @@ verify_ssl = true
|
|||||||
name = "pypi"
|
name = "pypi"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
discord = {extras = ["voice", "doc"], ref = "rewrite", git = "https://github.com/Rapptz/discord.py"}
|
|
||||||
packaging = "*"
|
packaging = "*"
|
||||||
|
discord-py = {extras = ["voice"],git = "https://github.com/Rapptz/discord.py",ref = "84c1eac62a775a37b03bd0971b221b0c50724630"}
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
28
errors.py
Normal file
28
errors.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
class LBIException(Exception):
|
||||||
|
"""
|
||||||
|
Base exception class for LBI
|
||||||
|
|
||||||
|
All other exceptions are subclasses
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleException(LBIException):
|
||||||
|
"""
|
||||||
|
Base exception class for all module errors
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleNotInstalled(ModuleException):
|
||||||
|
"""
|
||||||
|
Raised when a module is not found in module directory
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class IncompatibleModule(ModuleException):
|
||||||
|
"""
|
||||||
|
Raised when a module is not compatible with bot version
|
||||||
|
"""
|
||||||
|
pass
|
180
main.py
180
main.py
@ -6,11 +6,142 @@ import logging
|
|||||||
import logging.config
|
import logging.config
|
||||||
import os
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
from packaging.version import Version
|
||||||
|
|
||||||
|
from errors import IncompatibleModule
|
||||||
from modules.base import BaseClass
|
from modules.base import BaseClass
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
name: str
|
||||||
|
|
||||||
|
def __init__(self, name: str):
|
||||||
|
"""
|
||||||
|
Init module
|
||||||
|
|
||||||
|
:param name: Module name
|
||||||
|
:type name: str
|
||||||
|
"""
|
||||||
|
self.name = name
|
||||||
|
MODULES.update({self.name: self})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def exists(self) -> bool:
|
||||||
|
"""
|
||||||
|
Check if module exists
|
||||||
|
|
||||||
|
:return: True if module is present in modules folders
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
if not os.path.isdir(os.path.join("modules", self.name)):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def complete(self) -> bool:
|
||||||
|
"""
|
||||||
|
Check if module is complete
|
||||||
|
|
||||||
|
:return: True if module is compatible
|
||||||
|
:rtype: Boolean
|
||||||
|
"""
|
||||||
|
# Check if version.json exists
|
||||||
|
if not os.path.exists(os.path.join("modules", self.name, "version.json")):
|
||||||
|
return False
|
||||||
|
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||||
|
versions = json.load(file)
|
||||||
|
if "version" not in versions.keys():
|
||||||
|
return False
|
||||||
|
if "dependencies" not in versions.keys():
|
||||||
|
return False
|
||||||
|
if "bot_version" not in versions.keys():
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def version(self) -> Version:
|
||||||
|
"""
|
||||||
|
Returns module version
|
||||||
|
|
||||||
|
:return: current module version
|
||||||
|
:rtype: Version
|
||||||
|
"""
|
||||||
|
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||||
|
versions = json.load(file)
|
||||||
|
return Version(versions["version"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bot_version(self) -> dict:
|
||||||
|
"""
|
||||||
|
returns the min and max version of the bot that is compatible with the module
|
||||||
|
|
||||||
|
:return: Min and max version for bot
|
||||||
|
:rtype: dict
|
||||||
|
:raises IncompatibleModule: If bot_version is not properly formated (there must be min and max keys)
|
||||||
|
"""
|
||||||
|
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||||
|
versions = json.load(file)
|
||||||
|
try:
|
||||||
|
return {"min": Version(versions["bot_version"]["min"]),
|
||||||
|
"max": Version(versions["bot_version"]["max"])}
|
||||||
|
except KeyError:
|
||||||
|
raise IncompatibleModule(f"Module {self.name} is not compatible with bot (version.json does not "
|
||||||
|
f"contain bot_version.max or bot_version.min item)")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dependencies(self) -> dict:
|
||||||
|
"""
|
||||||
|
return list of dependencies version
|
||||||
|
|
||||||
|
:return: list of dependencies version
|
||||||
|
:rtype: dict
|
||||||
|
:raises IncompatibleModule: If bot_version is not properly formated (there must be min and max keys for each
|
||||||
|
dependencies)
|
||||||
|
"""
|
||||||
|
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||||
|
versions = json.load(file)
|
||||||
|
try:
|
||||||
|
deps = {}
|
||||||
|
for name, dep in versions["dependencies"].items():
|
||||||
|
dep_ver = {"min": Version(dep["min"]),
|
||||||
|
"max": Version(dep["max"])}
|
||||||
|
deps.update({name: dep_ver})
|
||||||
|
return deps
|
||||||
|
except KeyError:
|
||||||
|
raise IncompatibleModule(f"Module {self.name} is not compatible with bot (version.json does not "
|
||||||
|
f"contain dependencies.modulename.max or dependencies.modulename.min item)")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def compatible(self) -> bool:
|
||||||
|
"""
|
||||||
|
Check if module is compatible with current installation
|
||||||
|
|
||||||
|
:return: True if all dependencies are okays
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
# Check bot version
|
||||||
|
bot_ver = Version(__version__)
|
||||||
|
if bot_ver < self.bot_version["min"]:
|
||||||
|
return False
|
||||||
|
if bot_ver > self.bot_version["max"]:
|
||||||
|
return False
|
||||||
|
for name, dep in self.dependencies.items():
|
||||||
|
if name not in MODULES.keys():
|
||||||
|
Module(name)
|
||||||
|
if MODULES[name].version < dep["min"]:
|
||||||
|
return False
|
||||||
|
if MODULES[name].version > dep["max"]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
MODULES: Dict[str, Module] = {}
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(default_path='config/log_config.json', default_level=logging.INFO, env_key='LBI_LOG_CONFIG'):
|
def setup_logging(default_path='config/log_config.json', default_level=logging.INFO, env_key='LBI_LOG_CONFIG'):
|
||||||
"""Setup logging configuration
|
"""Setup logging configuration
|
||||||
@ -63,6 +194,11 @@ error = log_LBI.error
|
|||||||
critical = log_LBI.critical
|
critical = log_LBI.critical
|
||||||
|
|
||||||
|
|
||||||
|
def load_modules_info():
|
||||||
|
for mod in os.listdir("modules"):
|
||||||
|
Module(mod)
|
||||||
|
|
||||||
|
|
||||||
class LBI(discord.Client):
|
class LBI(discord.Client):
|
||||||
base_path = "storage"
|
base_path = "storage"
|
||||||
debug = log_LBI.debug
|
debug = log_LBI.debug
|
||||||
@ -113,23 +249,47 @@ class LBI(discord.Client):
|
|||||||
|
|
||||||
@modules_edit
|
@modules_edit
|
||||||
def load_module(self, module):
|
def load_module(self, module):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Status codes:
|
||||||
|
- 0: Module loaded
|
||||||
|
- 1: Module not in modules folder
|
||||||
|
- 2: Module incomplete
|
||||||
|
- 3: Module incompatible
|
||||||
|
|
||||||
|
:param module: Module name
|
||||||
|
:return: Status code
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check module compatibility
|
||||||
|
load_modules_info()
|
||||||
|
if not MODULES.get(module):
|
||||||
|
return 1
|
||||||
|
if not MODULES[module].exists:
|
||||||
|
return 1
|
||||||
|
if not MODULES[module].complete:
|
||||||
|
return 2
|
||||||
|
if not MODULES[module].compatible:
|
||||||
|
return 3
|
||||||
|
deps = MODULES[module].dependencies
|
||||||
|
for dep in deps.keys():
|
||||||
|
if dep not in self.modules.keys():
|
||||||
|
self.load_module(dep)
|
||||||
try:
|
try:
|
||||||
info("Start loading module {module}...".format(module=module))
|
info("Start loading module {module}...".format(module=module))
|
||||||
imported = importlib.import_module('modules.' + module)
|
imported = importlib.import_module('modules.' + module)
|
||||||
importlib.reload(imported)
|
importlib.reload(imported)
|
||||||
if issubclass(imported.MainClass, BaseClass):
|
initialized_class = imported.MainClass(self)
|
||||||
initialized_class = imported.MainClass(self)
|
self.modules.update({module: {"imported": imported, "initialized_class": initialized_class}})
|
||||||
self.modules.update({module: {"imported": imported, "initialized_class": initialized_class}})
|
info("Module {module} successfully imported.".format(module=module))
|
||||||
info("Module {module} successfully imported.".format(module=module))
|
initialized_class.on_load()
|
||||||
initialized_class.on_load()
|
if module not in self.config["modules"]:
|
||||||
if module not in self.config["modules"]:
|
self.config["modules"].append(module)
|
||||||
self.config["modules"].append(module)
|
self.save_config()
|
||||||
self.save_config()
|
|
||||||
else:
|
|
||||||
error("Module {module} isn't an instance of BaseClass.".format(module=module))
|
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
error("Module {module} doesn't have MainClass.".format(module=module))
|
error("Module {module} doesn't have MainClass.".format(module=module))
|
||||||
return e
|
return e
|
||||||
|
return 0
|
||||||
|
|
||||||
@modules_edit
|
@modules_edit
|
||||||
def unload_module(self, module):
|
def unload_module(self, module):
|
||||||
|
@ -4,7 +4,6 @@ import pickle
|
|||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from packaging import version
|
|
||||||
|
|
||||||
|
|
||||||
class Storage:
|
class Storage:
|
||||||
@ -51,9 +50,6 @@ class BaseClass:
|
|||||||
color = 0x000000
|
color = 0x000000
|
||||||
command_text = None
|
command_text = None
|
||||||
authorized_roles = []
|
authorized_roles = []
|
||||||
version = version.parse("0.0.1")
|
|
||||||
min_base_version = version.parse("0.0.1")
|
|
||||||
max_base_version = "0.0.1"
|
|
||||||
|
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
"""Initialize module class
|
"""Initialize module class
|
10
modules/base/version.json
Normal file
10
modules/base/version.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version":"0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"bot_version": {
|
||||||
|
"min": "0.1.0",
|
||||||
|
"max": "0.1.0"
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import collections
|
|
||||||
import random
|
import random
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
import collections
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
from modules.base import BaseClass
|
from modules.base import BaseClass
|
10
modules/errors/version.json
Normal file
10
modules/errors/version.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version":"0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"base":"0.1.0"
|
||||||
|
},
|
||||||
|
"bot_version": {
|
||||||
|
"min": "0.1.0",
|
||||||
|
"max": "0.1.0"
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ from modules.base import BaseClass
|
|||||||
|
|
||||||
|
|
||||||
class MainClass(BaseClass):
|
class MainClass(BaseClass):
|
||||||
name = "Git"
|
name = "git"
|
||||||
|
|
||||||
super_users = [431043517217898496]
|
super_users = [431043517217898496]
|
||||||
|
|
13
modules/git/version.json
Normal file
13
modules/git/version.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"base": {
|
||||||
|
"min": "0.1.0",
|
||||||
|
"max": "0.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bot_version": {
|
||||||
|
"min": "0.1.0",
|
||||||
|
"max": "0.1.0"
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,5 @@
|
|||||||
import discord
|
import discord
|
||||||
|
|
||||||
from modules.base import BaseClass
|
|
||||||
import discord
|
|
||||||
|
|
||||||
from modules.base import BaseClass
|
from modules.base import BaseClass
|
||||||
|
|
||||||
|
|
10
modules/help/version.json
Normal file
10
modules/help/version.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version":"0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"base":"==0.1.0"
|
||||||
|
},
|
||||||
|
"bot_version": {
|
||||||
|
"min": "0.1.0",
|
||||||
|
"max": "0.1.0"
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,26 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from aiohttp import ClientSession
|
|
||||||
|
|
||||||
from .base import BaseClass
|
from modules.base import BaseClass
|
||||||
|
|
||||||
|
|
||||||
|
# todo: tout réécrire
|
||||||
|
|
||||||
class MainClass(BaseClass):
|
class MainClass(BaseClass):
|
||||||
name = "modules"
|
name = "modules"
|
||||||
super_users = [431043517217898496]
|
|
||||||
|
command_text = "modules"
|
||||||
color = 0x000000
|
color = 0x000000
|
||||||
help_active = True
|
|
||||||
help = {
|
help = {
|
||||||
"description": "Manage bot modules.",
|
"description": "Manage bot modules.",
|
||||||
"commands": {
|
"commands": {
|
||||||
"`{prefix}{command} list`": "List of available modules.",
|
"`{prefix}{command} list`": "List of available modules.",
|
||||||
"`{prefix}{command} enable <module>`": "Enable module `<module>`.",
|
"`{prefix}{command} enable <module>`": "Enable module `<module>`.",
|
||||||
"`{prefix}{command} disable <module>`": "Disable module `<module>`.",
|
"`{prefix}{command} disable <module>`": "Disable module `<module>`.",
|
||||||
|
"`{prefix}{command} web_list`": "List all available modules from repository",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
command_text = "modules"
|
|
||||||
|
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
super().__init__(client)
|
super().__init__(client)
|
||||||
@ -89,13 +90,5 @@ class MainClass(BaseClass):
|
|||||||
)
|
)
|
||||||
await message.channel.send(embed=embed)
|
await message.channel.send(embed=embed)
|
||||||
|
|
||||||
async def com_download(self, message, args, kwargs):
|
async def com_web_list(self, message, args, kwargs):
|
||||||
if len(args) == 3:
|
pass
|
||||||
async with ClientSession() as session:
|
|
||||||
async with session.get("http://127.0.0.1:5000/api/modules/"+args[1]+"/"+args[2]) as response:
|
|
||||||
if response.status != 200:
|
|
||||||
await message.channel.send(f"Erreur lors du téléchargement de {args[1]}")
|
|
||||||
return
|
|
||||||
with self.storage.open(os.path.join("modules", args[1]+".zip"), "wb") as file:
|
|
||||||
file.write(await response.read())
|
|
||||||
|
|
13
modules/modules/version.json
Normal file
13
modules/modules/version.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"base": {
|
||||||
|
"min": "0.1.0",
|
||||||
|
"max": "0.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bot_version": {
|
||||||
|
"min": "0.1.0",
|
||||||
|
"max": "0.1.0"
|
||||||
|
}
|
||||||
|
}
|
10
modules/restart/version.json
Normal file
10
modules/restart/version.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version":"0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"base":"==0.1.0"
|
||||||
|
},
|
||||||
|
"bot_version": {
|
||||||
|
"min": "0.1.0",
|
||||||
|
"max": "0.1.0"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user