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"
|
||||
|
||||
[packages]
|
||||
discord = {extras = ["voice", "doc"], ref = "rewrite", git = "https://github.com/Rapptz/discord.py"}
|
||||
packaging = "*"
|
||||
discord-py = {extras = ["voice"],git = "https://github.com/Rapptz/discord.py",ref = "84c1eac62a775a37b03bd0971b221b0c50724630"}
|
||||
|
||||
[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
|
166
main.py
166
main.py
@ -6,11 +6,142 @@ import logging
|
||||
import logging.config
|
||||
import os
|
||||
import traceback
|
||||
from typing import Dict
|
||||
|
||||
import discord
|
||||
from packaging.version import Version
|
||||
|
||||
from errors import IncompatibleModule
|
||||
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'):
|
||||
"""Setup logging configuration
|
||||
@ -63,6 +194,11 @@ error = log_LBI.error
|
||||
critical = log_LBI.critical
|
||||
|
||||
|
||||
def load_modules_info():
|
||||
for mod in os.listdir("modules"):
|
||||
Module(mod)
|
||||
|
||||
|
||||
class LBI(discord.Client):
|
||||
base_path = "storage"
|
||||
debug = log_LBI.debug
|
||||
@ -113,11 +249,36 @@ class LBI(discord.Client):
|
||||
|
||||
@modules_edit
|
||||
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:
|
||||
info("Start loading module {module}...".format(module=module))
|
||||
imported = importlib.import_module('modules.' + module)
|
||||
importlib.reload(imported)
|
||||
if issubclass(imported.MainClass, BaseClass):
|
||||
initialized_class = imported.MainClass(self)
|
||||
self.modules.update({module: {"imported": imported, "initialized_class": initialized_class}})
|
||||
info("Module {module} successfully imported.".format(module=module))
|
||||
@ -125,11 +286,10 @@ class LBI(discord.Client):
|
||||
if module not in self.config["modules"]:
|
||||
self.config["modules"].append(module)
|
||||
self.save_config()
|
||||
else:
|
||||
error("Module {module} isn't an instance of BaseClass.".format(module=module))
|
||||
except AttributeError as e:
|
||||
error("Module {module} doesn't have MainClass.".format(module=module))
|
||||
return e
|
||||
return 0
|
||||
|
||||
@modules_edit
|
||||
def unload_module(self, module):
|
||||
|
@ -4,7 +4,6 @@ import pickle
|
||||
import zipfile
|
||||
|
||||
import discord
|
||||
from packaging import version
|
||||
|
||||
|
||||
class Storage:
|
||||
@ -51,9 +50,6 @@ class BaseClass:
|
||||
color = 0x000000
|
||||
command_text = None
|
||||
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):
|
||||
"""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 collections
|
||||
import random
|
||||
import traceback
|
||||
|
||||
import collections
|
||||
import discord
|
||||
|
||||
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):
|
||||
name = "Git"
|
||||
name = "git"
|
||||
|
||||
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
|
||||
|
||||
from modules.base import BaseClass
|
||||
import discord
|
||||
|
||||
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 discord
|
||||
from aiohttp import ClientSession
|
||||
|
||||
from .base import BaseClass
|
||||
from modules.base import BaseClass
|
||||
|
||||
|
||||
# todo: tout réécrire
|
||||
|
||||
class MainClass(BaseClass):
|
||||
name = "modules"
|
||||
super_users = [431043517217898496]
|
||||
|
||||
command_text = "modules"
|
||||
color = 0x000000
|
||||
help_active = True
|
||||
help = {
|
||||
"description": "Manage bot modules.",
|
||||
"commands": {
|
||||
"`{prefix}{command} list`": "List of available modules.",
|
||||
"`{prefix}{command} enable <module>`": "Enable 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):
|
||||
super().__init__(client)
|
||||
@ -89,13 +90,5 @@ class MainClass(BaseClass):
|
||||
)
|
||||
await message.channel.send(embed=embed)
|
||||
|
||||
async def com_download(self, message, args, kwargs):
|
||||
if len(args) == 3:
|
||||
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())
|
||||
|
||||
async def com_web_list(self, message, args, kwargs):
|
||||
pass
|
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