Petit checkpoint

This commit is contained in:
Louis Chauvet 2020-04-21 10:46:36 +02:00
parent c8922cddca
commit 0ed718d6d4
Signed by: fomys
GPG Key ID: 1ECA046A9615ABA0
80 changed files with 830 additions and 829 deletions

View File

@ -1,11 +0,0 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -1,11 +0,0 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -1,11 +0,0 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -1,11 +0,0 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -1,11 +0,0 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -1,11 +0,0 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -1,11 +0,0 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -1,11 +0,0 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -1,28 +1,28 @@
class LBIException(Exception): class LBIException(Exception):
""" """
Base exception class for LBI Base exception class for LBI
All other exceptions are subclasses All other exceptions are subclasses
""" """
pass pass
class ModuleException(LBIException): class ModuleException(LBIException):
""" """
Base exception class for all module errors Base exception class for all module errors
""" """
pass pass
class ModuleNotInstalled(ModuleException): class ModuleNotInstalled(ModuleException):
""" """
Raised when a module is not found in module directory Raised when a module is not found in module directory
""" """
pass pass
class IncompatibleModule(ModuleException): class IncompatibleModule(ModuleException):
""" """
Raised when a module is not compatible with bot version Raised when a module is not compatible with bot version
""" """
pass pass

View File

@ -1,499 +1,499 @@
#!/usr/bin/python3 #!/usr/bin/python3
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
import importlib import importlib
import json import json
import locale import locale
import logging import logging
import logging.config import logging.config
import os import os
import sys import sys
import traceback import traceback
from typing import Dict from typing import Dict
import discord import discord
import humanize import humanize
from packaging.version import Version from packaging.version import Version
from config import Config, config_types from config import Config, config_types
from config.config_types import factory from config.config_types import factory
from errors import IncompatibleModule from errors import IncompatibleModule
from modules.base import base_supported_type from modules.base import base_supported_type
__version__ = "0.1.0" __version__ = "0.1.0"
class Module: class Module:
name: str name: str
def __init__(self, name: str): def __init__(self, name: str):
""" """
Init module Init module
:param name: Module name :param name: Module name
:type name: str :type name: str
""" """
self.name = name self.name = name
MODULES.update({self.name: self}) MODULES.update({self.name: self})
@property @property
def type(self) -> str: def type(self) -> str:
""" """
Return module type. It can be python or lua Return module type. It can be python or lua
:return: Module type :return: Module type
:rtype: str :rtype: str
""" """
if not os.path.exists(os.path.join("modules", self.name, "version.json")): if not os.path.exists(os.path.join("modules", self.name, "version.json")):
return "" return ""
with open(os.path.join("modules", self.name, "version.json")) as file: with open(os.path.join("modules", self.name, "version.json")) as file:
versions = json.load(file) versions = json.load(file)
return versions["type"] return versions["type"]
@property @property
def exists(self) -> bool: def exists(self) -> bool:
""" """
Check if module exists Check if module exists
:return: True if module is present in modules folders :return: True if module is present in modules folders
:rtype: bool :rtype: bool
""" """
if not os.path.isdir(os.path.join("modules", self.name)): if not os.path.isdir(os.path.join("modules", self.name)):
return False return False
return True return True
@property @property
def complete(self) -> bool: def complete(self) -> bool:
""" """
Check if module is complete Check if module is complete
:return: True if module is compatible :return: True if module is compatible
:rtype: Boolean :rtype: Boolean
""" """
# Check if version.json exists # Check if version.json exists
if not os.path.exists(os.path.join("modules", self.name, "version.json")): if not os.path.exists(os.path.join("modules", self.name, "version.json")):
return False return False
with open(os.path.join("modules", self.name, "version.json")) as file: with open(os.path.join("modules", self.name, "version.json")) as file:
versions = json.load(file) versions = json.load(file)
if "version" not in versions.keys(): if "version" not in versions.keys():
return False return False
if "dependencies" not in versions.keys(): if "dependencies" not in versions.keys():
return False return False
if "bot_version" not in versions.keys(): if "bot_version" not in versions.keys():
return False return False
if "type" not in versions.keys(): if "type" not in versions.keys():
return False return False
if versions["type"] not in base_supported_type: if versions["type"] not in base_supported_type:
return False return False
return True return True
@property @property
def version(self) -> Version: def version(self) -> Version:
""" """
Returns module version Returns module version
:return: current module version :return: current module version
:rtype: Version :rtype: Version
""" """
with open(os.path.join("modules", self.name, "version.json")) as file: with open(os.path.join("modules", self.name, "version.json")) as file:
versions = json.load(file) versions = json.load(file)
return Version(versions["version"]) return Version(versions["version"])
@property @property
def bot_version(self) -> dict: def bot_version(self) -> dict:
""" """
returns the min and max version of the bot that is compatible with the module returns the min and max version of the bot that is compatible with the module
:return: Min and max version for bot :return: Min and max version for bot
:rtype: dict :rtype: dict
:raises IncompatibleModule: If bot_version is not properly formated (there must be min and max keys) :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: with open(os.path.join("modules", self.name, "version.json")) as file:
versions = json.load(file) versions = json.load(file)
try: try:
return {"min": Version(versions["bot_version"]["min"]), return {"min": Version(versions["bot_version"]["min"]),
"max": Version(versions["bot_version"]["max"])} "max": Version(versions["bot_version"]["max"])}
except KeyError: except KeyError:
raise IncompatibleModule(f"Module {self.name} is not compatible with bot (version.json does not " 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)") f"contain bot_version.max or bot_version.min item)")
@property @property
def dependencies(self) -> dict: def dependencies(self) -> dict:
""" """
return list of dependencies version return list of dependencies version
:raise IncompatibleModule: If bot_version is not properly formated (there must be min and max keys for each dependencies) :raise IncompatibleModule: If bot_version is not properly formated (there must be min and max keys for each dependencies)
:return: list of dependencies version :return: list of dependencies version
:rtype: dict :rtype: dict
""" """
with open(os.path.join("modules", self.name, "version.json")) as file: with open(os.path.join("modules", self.name, "version.json")) as file:
versions = json.load(file) versions = json.load(file)
try: try:
deps = {} deps = {}
for name, dep in versions["dependencies"].items(): for name, dep in versions["dependencies"].items():
dep_ver = {"min": Version(dep["min"]), dep_ver = {"min": Version(dep["min"]),
"max": Version(dep["max"])} "max": Version(dep["max"])}
deps.update({name: dep_ver}) deps.update({name: dep_ver})
return deps return deps
except KeyError: except KeyError:
raise IncompatibleModule(f"Module {self.name} is not compatible with bot (version.json does not " 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)") f"contain dependencies.modulename.max or dependencies.modulename.min item)")
@property @property
def compatible(self) -> bool: def compatible(self) -> bool:
""" """
Check if module is compatible with current installation Check if module is compatible with current installation
:return: True if all dependencies are okays :return: True if all dependencies are okays
:rtype: bool :rtype: bool
""" """
# Check bot version # Check bot version
bot_ver = Version(__version__) bot_ver = Version(__version__)
if bot_ver < self.bot_version["min"]: if bot_ver < self.bot_version["min"]:
return False return False
if bot_ver > self.bot_version["max"]: if bot_ver > self.bot_version["max"]:
return False return False
for name, dep in self.dependencies.items(): for name, dep in self.dependencies.items():
if name not in MODULES.keys(): if name not in MODULES.keys():
Module(name) Module(name)
if MODULES[name].version < dep["min"]: if MODULES[name].version < dep["min"]:
return False return False
if MODULES[name].version > dep["max"]: if MODULES[name].version > dep["max"]:
return False return False
return True return True
MODULES: Dict[str, Module] = {} 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
""" """
path = default_path path = default_path
value = os.getenv(env_key, None) value = os.getenv(env_key, None)
if value: if value:
path = value path = value
if os.path.exists(path): if os.path.exists(path):
with open(path, 'rt') as f: with open(path, 'rt') as f:
config = json.load(f) config = json.load(f)
logging.config.dictConfig(config) logging.config.dictConfig(config)
else: else:
logging.basicConfig(level=default_level) logging.basicConfig(level=default_level)
def modules_edit(func): def modules_edit(func):
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
if self.reloading: if self.reloading:
return func(self, *args, **kwargs) return func(self, *args, **kwargs)
else: else:
self.reloading = True self.reloading = True
a = func(self, *args, **kwargs) a = func(self, *args, **kwargs)
self.reloading = False self.reloading = False
return a return a
return wrapper return wrapper
def event(func): def event(func):
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
if self.reloading: if self.reloading:
return lambda: None return lambda: None
else: else:
return func(self, *args, **kwargs) return func(self, *args, **kwargs)
return wrapper return wrapper
"""def async_event(func): """def async_event(func):
async def wrapper(self, *args, **kwargs): async def wrapper(self, *args, **kwargs):
if self.reloading: if self.reloading:
return lambda: None return lambda: None
else: else:
return func(self, *args, **kwargs) return func(self, *args, **kwargs)
return wrapper""" return wrapper"""
setup_logging() setup_logging()
log_discord = logging.getLogger('discord_types') log_discord = logging.getLogger('discord_types')
log_LBI = logging.getLogger('LBI') log_LBI = logging.getLogger('LBI')
log_communication = logging.getLogger('communication') log_communication = logging.getLogger('communication')
def load_modules_info(): def load_modules_info():
for mod in os.listdir("modules"): for mod in os.listdir("modules"):
Module(mod) Module(mod)
class LBI(discord.Client): class LBI(discord.Client):
by_id: ClientById by_id: ClientById
base_path = "data" base_path = "data"
debug = log_LBI.debug debug = log_LBI.debug
info = log_LBI.info info = log_LBI.info
warning = log_LBI.warning warning = log_LBI.warning
warn = warning warn = warning
error = log_LBI.error error = log_LBI.error
critical = log_LBI.critical critical = log_LBI.critical
def __init__(self, config: Config = None, *args, **kwargs): def __init__(self, config: Config = None, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if config is None: if config is None:
config = Config(path="data/config.toml", client=self) config = Config(path="data/config.toml", client=self)
self.reloading = False self.reloading = False
self.by_id = ClientById(self) self.by_id = ClientById(self)
self.ready = False self.ready = False
# Content: {"module_name": {"module": imported module, "class": initialized class}} # Content: {"module_name": {"module": imported module, "class": initialized class}}
self.modules = {} self.modules = {}
self.config = config self.config = config
self.config.register("modules", factory(config_types.List, factory(config_types.Str))) self.config.register("modules", factory(config_types.List, factory(config_types.Str)))
self.config.register("prefix", factory(config_types.Str)) self.config.register("prefix", factory(config_types.Str))
self.config.register("admin_roles", factory(config_types.List, factory(config_types.discord_types.Role, self))) self.config.register("admin_roles", factory(config_types.List, factory(config_types.discord_types.Role, self)))
self.config.register("admin_users", factory(config_types.List, factory(config_types.discord_types.User, self))) self.config.register("admin_users", factory(config_types.List, factory(config_types.discord_types.User, self)))
self.config.register("main_guild", factory(config_types.discord_types.Guild, self)) self.config.register("main_guild", factory(config_types.discord_types.Guild, self))
self.config.register("locale", factory(config_types.Str)) self.config.register("locale", factory(config_types.Str))
self.config.set({ self.config.set({
"modules": ["modules", "errors"], "modules": ["modules", "errors"],
"prefix": "%", "prefix": "%",
"admin_roles": [], "admin_roles": [],
"admin_users": [], "admin_users": [],
"main_guild": None, "main_guild": None,
"locale": "fr_FR.UTF8", "locale": "fr_FR.UTF8",
}) })
locale.setlocale(locale.LC_TIME, self.config['locale']) locale.setlocale(locale.LC_TIME, self.config['locale'])
humanize.i18n.activate(self.config['locale']) humanize.i18n.activate(self.config['locale'])
self.load_modules() self.load_modules()
@modules_edit @modules_edit
def load_modules(self): def load_modules(self):
self.info("Starts to load modules...") self.info("Starts to load modules...")
e = {} e = {}
for module in self.config["modules"]: for module in self.config["modules"]:
e.update({module: self.load_module(module)}) e.update({module: self.load_module(module)})
self.info("Finished to load all modules.") self.info("Finished to load all modules.")
return e return e
@modules_edit @modules_edit
def load_module(self, module): def load_module(self, module):
""" """
Status codes: Status codes:
- 0: Module loaded - 0: Module loaded
- 1: Module not in modules folder - 1: Module not in modules folder
- 2: Module incomplete - 2: Module incomplete
- 3: Module incompatible - 3: Module incompatible
:param module: Module name :param module: Module name
:return: Status code :return: Status code
""" """
# Check module compatibility # Check module compatibility
load_modules_info() load_modules_info()
if not MODULES.get(module): if not MODULES.get(module):
return 1 return 1
if not MODULES[module].exists: if not MODULES[module].exists:
return 1 return 1
if not MODULES[module].complete: if not MODULES[module].complete:
return 2 return 2
if not MODULES[module].compatible: if not MODULES[module].compatible:
return 3 return 3
deps = MODULES[module].dependencies deps = MODULES[module].dependencies
for dep in deps.keys(): for dep in deps.keys():
if dep not in self.modules.keys(): if dep not in self.modules.keys():
if dep != "base": if dep != "base":
self.load_module(dep) self.load_module(dep)
if MODULES[module].type == "python": if MODULES[module].type == "python":
try: try:
self.info("Start loading module {module}...".format(module=module)) self.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)
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}})
self.info("Module {module} successfully imported.".format(module=module)) self.info("Module {module} successfully imported.".format(module=module))
initialized_class.dispatch("load") initialized_class.dispatch("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.config.save() self.config.save()
except AttributeError as e: except AttributeError as e:
self.error("Module {module} doesn't have MainClass.".format(module=module)) self.error("Module {module} doesn't have MainClass.".format(module=module))
raise e raise e
return 0 return 0
elif MODULES[module].type == "lua": elif MODULES[module].type == "lua":
self.info(f"Start loading module {module}...") self.info(f"Start loading module {module}...")
imported = importlib.import_module('modules.base.BaseLua') imported = importlib.import_module('modules.base.BaseLua')
importlib.reload(imported) importlib.reload(imported)
initialized_class = imported.BaseClassLua(self, path=f"modules/{module}/main") initialized_class = imported.BaseClassLua(self, path=f"modules/{module}/main")
self.modules.update({module: {"imported": imported, "initialized_class": initialized_class}}) self.modules.update({module: {"imported": imported, "initialized_class": initialized_class}})
self.info(f"Module {module} successfully imported.") self.info(f"Module {module} successfully imported.")
initialized_class.dispatch("load") initialized_class.dispatch("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.config.save() self.config.save()
return 0 return 0
@modules_edit @modules_edit
def unload_module(self, module): def unload_module(self, module):
self.info("Start unload module {module}...".format(module=module)) self.info("Start unload module {module}...".format(module=module))
try: try:
if module in self.config["modules"]: if module in self.config["modules"]:
self.config["modules"].remove(module) self.config["modules"].remove(module)
self.config.save() self.config.save()
self.unload_all() self.unload_all()
self.load_modules() self.load_modules()
except KeyError as e: except KeyError as e:
self.error("Module {module} not loaded.").format(module=module) self.error("Module {module} not loaded.").format(module=module)
return e return e
@modules_edit @modules_edit
def reload(self): def reload(self):
del self.modules del self.modules
self.load_modules() self.load_modules()
@modules_edit @modules_edit
def unload_all(self): def unload_all(self):
del self.modules del self.modules
self.modules = {} self.modules = {}
@event @event
def dispatch(self, event, *args, **kwargs): def dispatch(self, event, *args, **kwargs):
# Dispatch to handle wait_* commands # Dispatch to handle wait_* commands
super().dispatch(event, *args, **kwargs) super().dispatch(event, *args, **kwargs)
# Dispatch to modules # Dispatch to modules
for module in self.modules.values(): for module in self.modules.values():
module["initialized_class"].dispatch(event, *args, **kwargs) module["initialized_class"].dispatch(event, *args, **kwargs)
async def on_error(self, event_method, *args, **kwargs): async def on_error(self, event_method, *args, **kwargs):
"""Function called when error happend""" """Function called when error happend"""
# This event is special because it is call directly # This event is special because it is call directly
self.error(traceback.format_exc()) self.error(traceback.format_exc())
for module in self.modules.values(): for module in self.modules.values():
await module["initialized_class"].on_error(event_method, *args, **kwargs) await module["initialized_class"].on_error(event_method, *args, **kwargs)
class ClientById: class ClientById:
client: LBI client: LBI
def __init__(self, client_): def __init__(self, client_):
self.client = client_ self.client = client_
async def fetch_message(self, id_, *args, **kwargs): async def fetch_message(self, id_, *args, **kwargs):
"""Find a message by id """Find a message by id
:param id_: Id of message to find :param id_: Id of message to find
:type id_: int :type id_: int
:raises discord_types.NotFound: This exception is raised when a message is not found (or not accessible by bot) :raises discord_types.NotFound: This exception is raised when a message is not found (or not accessible by bot)
:rtype: discord.Message :rtype: discord.Message
:return: discord_types.Message instance if message is found. :return: discord_types.Message instance if message is found.
""" """
msg = None msg = None
for channel in self.client.get_all_channels(): for channel in self.client.get_all_channels():
try: try:
return await channel.fetch_message(id_, *args, **kwargs) return await channel.fetch_message(id_, *args, **kwargs)
except discord.NotFound: except discord.NotFound:
continue continue
if msg is None: if msg is None:
raise discord.NotFound(None, "Message not found") raise discord.NotFound(None, "Message not found")
async def edit_message(self, id, *args, **kwargs): async def edit_message(self, id, *args, **kwargs):
""" """
Edit message by id Edit message by id
:param id: Id of the message to edit :param id: Id of the message to edit
:type id: int""" :type id: int"""
message = await self.fetch_message(id) message = await self.fetch_message(id)
return await message.edit(**kwargs) return await message.edit(**kwargs)
async def remove_reaction(self, id_message, *args, **kwargs): async def remove_reaction(self, id_message, *args, **kwargs):
"""Remove reaction from message by id """Remove reaction from message by id
:param id_message: Id of message :param id_message: Id of message
:type id_message: int""" :type id_message: int"""
message = await self.fetch_message(id_message) message = await self.fetch_message(id_message)
return await message.remove_reaction(*args, **kwargs) return await message.remove_reaction(*args, **kwargs)
async def send_message(self, id_, *args, **kwargs): async def send_message(self, id_, *args, **kwargs):
"""Send message by channel id """Send message by channel id
:param id_: Id of channel where to send message :param id_: Id of channel where to send message
:type id_: int""" :type id_: int"""
channel = self.client.get_channel(id_) channel = self.client.get_channel(id_)
return channel.send(*args, **kwargs) return channel.send(*args, **kwargs)
def get_role(self, id_=None, name=None, check=None, guilds=None): def get_role(self, id_=None, name=None, check=None, guilds=None):
"""Get role by id or with custom check""" """Get role by id or with custom check"""
if guilds is None: if guilds is None:
guilds = self.client.guilds guilds = self.client.guilds
if id_ is not None: if id_ is not None:
for guild in guilds: for guild in guilds:
role = discord.utils.get(guild.roles, id=id_) role = discord.utils.get(guild.roles, id=id_)
if role: if role:
return role return role
if name is not None: if name is not None:
for guild in guilds: for guild in guilds:
role = discord.utils.get(guild.roles, name=name) role = discord.utils.get(guild.roles, name=name)
if role: if role:
return role return role
if check is not None: if check is not None:
role = None role = None
for guild in guilds: for guild in guilds:
for role_ in guild.roles: for role_ in guild.roles:
if check(role_): if check(role_):
role = role_ role = role_
break break
if role is not None: if role is not None:
break break
return role return role
return None return None
class Communication(asyncio.Protocol): class Communication(asyncio.Protocol):
debug = log_communication.debug debug = log_communication.debug
info = log_communication.info info = log_communication.info
warning = log_communication.warning warning = log_communication.warning
error = log_communication.error error = log_communication.error
critical = log_communication.critical critical = log_communication.critical
name = "Communication" name = "Communication"
def __init__(self, client): def __init__(self, client):
self.client = client self.client = client
self.transport = None self.transport = None
def connection_made(self, transport): def connection_made(self, transport):
print('%s: connection made' % self.name) print('%s: connection made' % self.name)
self.transport = transport self.transport = transport
def data_received(self, data): def data_received(self, data):
print('%s: data received: %r' % (self.name, data)) print('%s: data received: %r' % (self.name, data))
def eof_received(self): def eof_received(self):
pass pass
def connection_lost(self, exc): def connection_lost(self, exc):
print('%s: connection lost: %s' % (self.name, exc)) print('%s: connection lost: %s' % (self.name, exc))
if __name__ == "__main__": if __name__ == "__main__":
client1 = LBI(max_messages=500000) client1 = LBI(max_messages=500000)
communication = Communication(client1) communication = Communication(client1)
async def start_bot(): async def start_bot():
await client1.start(os.environ.get("DISCORD_TOKEN")) await client1.start(os.environ.get("DISCORD_TOKEN"))
print(os.path.join("/tmp", os.path.dirname(os.path.realpath(__file__))) + ".sock") print(os.path.join("/tmp", os.path.dirname(os.path.realpath(__file__))) + ".sock")
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
t = loop.create_unix_server(Communication, t = loop.create_unix_server(Communication,
path=os.path.join("/tmp", os.path.dirname(os.path.realpath(__file__)) + ".sock")) path=os.path.join("/tmp", os.path.dirname(os.path.realpath(__file__)) + ".sock"))
if not sys.platform == "win32": if not sys.platform == "win32":
loop.run_until_complete(t) loop.run_until_complete(t)
loop.create_task(start_bot()) loop.create_task(start_bot())
loop.run_forever() loop.run_forever()

View File

View File

@ -1,11 +1,11 @@
{ {
"version":"0.1.0", "version":"0.1.0",
"type": "python", "type": "python",
"dependencies": { "dependencies": {
}, },
"bot_version": { "bot_version": {
"min": "0.1.0", "min": "0.1.0",
"max": "0.1.0" "max": "0.1.0"
} }
} }

View File

@ -1,3 +1,3 @@
from .BasePython import BaseClassPython from .BasePython import BaseClassPython
from .BaseLua import BaseClassLua from .BaseLua import BaseClassLua
base_supported_type = ["python", "lua"] base_supported_type = ["python", "lua"]

View File

@ -5,7 +5,8 @@ from typing import List, Union, Optional
import discord import discord
from config import Config, config_types from config import Config
from config import config_types
from config.config_types import factory from config.config_types import factory
from storage import Objects from storage import Objects
from utils import emojis from utils import emojis
@ -35,9 +36,9 @@ class BaseClass:
self.config.register("color", factory(config_types.Color)) self.config.register("color", factory(config_types.Color))
self.config.register("auth_everyone", factory(config_types.Bool)) self.config.register("auth_everyone", factory(config_types.Bool))
self.config.register("authorized_roles", self.config.register("authorized_roles",
factory(config_types.List, factory(config_types.discord_types.Role, client))) factory(config_types.List, factory(config.config_types.discord_types.Role, client)))
self.config.register("authorized_users", self.config.register("authorized_users",
factory(config_types.List, factory(config_types.discord_types.User, client))) factory(config_types.List, factory(config.config_types.discord_types.User, client)))
self.config.register("command_text", factory(config_types.Str)) self.config.register("command_text", factory(config_types.Str))
self.config.set({"help_active": True, self.config.set({"help_active": True,
"color": 0x000000, "color": 0x000000,

View File

@ -4,7 +4,7 @@ import asyncio
import discord import discord
import lupa import lupa
from modules.base.Base import BaseClass from modules import BaseClass
class BaseClassLua(BaseClass): class BaseClassLua(BaseClass):

View File

@ -1,6 +1,6 @@
"""Base class for module, never use directly !!!""" """Base class for module, never use directly !!!"""
from modules.base.Base import BaseClass from .Base import BaseClass
class BaseClassPython(BaseClass): class BaseClassPython(BaseClass):

View File

@ -1,11 +1,11 @@
{ {
"version":"0.1.0", "version":"0.1.0",
"type": "python", "type": "python",
"dependencies": { "dependencies": {
}, },
"bot_version": { "bot_version": {
"min": "0.1.0", "min": "0.1.0",
"max": "0.1.0" "max": "0.1.0"
} }
} }

View File

@ -1,11 +1,11 @@
{ {
"version":"0.1.0", "version":"0.1.0",
"type": "python", "type": "python",
"dependencies": { "dependencies": {
}, },
"bot_version": { "bot_version": {
"min": "0.1.0", "min": "0.1.0",
"max": "0.1.0" "max": "0.1.0"
} }
} }

View File

@ -1,14 +1,14 @@
{ {
"version": "0.1.0", "version": "0.1.0",
"type": "python", "type": "python",
"dependencies": { "dependencies": {
"base": { "base": {
"min": "0.1.0", "min": "0.1.0",
"max": "0.1.0" "max": "0.1.0"
} }
}, },
"bot_version": { "bot_version": {
"min": "0.1.0", "min": "0.1.0",
"max": "0.1.0" "max": "0.1.0"
} }
} }

View File

@ -1,11 +1,11 @@
{ {
"version":"0.1.0", "version":"0.1.0",
"type": "python", "type": "python",
"dependencies": { "dependencies": {
}, },
"bot_version": { "bot_version": {
"min": "0.1.0", "min": "0.1.0",
"max": "0.1.0" "max": "0.1.0"
} }
} }

View File

@ -1,141 +1,141 @@
import os import os
import discord import discord
from aiohttp import ClientConnectorError from aiohttp import ClientConnectorError
from modules.base import BaseClassPython from modules.base import BaseClassPython
from modules.modules.api import Api from modules.modules.api import Api
class MainClass(BaseClassPython): class MainClass(BaseClassPython):
name = "modules" name = "modules"
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} reload <module>`": "Reload module `<module>`", "`{prefix}{command} reload <module>`": "Reload module `<module>`",
"`{prefix}{command} web_list`": "List all available modules from repository", "`{prefix}{command} web_list`": "List all available modules from repository",
# "`{prefix}{command} web_source`": "List all source repositories", # "`{prefix}{command} web_source`": "List all source repositories",
# "`{prefix}{command} web_source remove <url>`": "Remove url from repository list", # "`{prefix}{command} web_source remove <url>`": "Remove url from repository list",
# "`{prefix}{command} web_source add <url>`": "Add url to repository list", # "`{prefix}{command} web_source add <url>`": "Add url to repository list",
} }
} }
def __init__(self, client): def __init__(self, client):
super().__init__(client) super().__init__(client)
os.makedirs("modules", exist_ok=True) os.makedirs("modules", exist_ok=True)
self.api = Api() self.api = Api()
@staticmethod @staticmethod
def get_all_modules(): def get_all_modules():
all_items = os.listdir("modules") all_items = os.listdir("modules")
modules = [] modules = []
for item in all_items: for item in all_items:
if item not in ["__init__.py", "base", "__pycache__"]: if item not in ["__init__.py", "base", "__pycache__"]:
if os.path.isfile(os.path.join("modules", item)): if os.path.isfile(os.path.join("modules", item)):
modules.append(item[:-3]) modules.append(item[:-3])
else: else:
modules.append(item) modules.append(item)
return set(modules) return set(modules)
async def com_enable(self, message, args, kwargs): async def com_enable(self, message, args, kwargs):
args = args[1:] args = args[1:]
if len(args) == 0: if len(args) == 0:
await message.channel.send("You must specify at least one module.") await message.channel.send("You must specify at least one module.")
return return
if len(args) == 1 and args[0] == "*": if len(args) == 1 and args[0] == "*":
for module in self.get_all_modules(): for module in self.get_all_modules():
e = self.client.load_module(module) e = self.client.load_module(module)
if e: if e:
await message.channel.send("An error occurred during the loading of the module {module}." await message.channel.send("An error occurred during the loading of the module {module}."
.format(module=module)) .format(module=module))
await self.com_list(message, args, kwargs) await self.com_list(message, args, kwargs)
return return
for arg in args: for arg in args:
e = self.client.load_module(arg) e = self.client.load_module(arg)
if e == 1: if e == 1:
await message.channel.send(f"Module {arg} not exists.") await message.channel.send(f"Module {arg} not exists.")
if e == 2: if e == 2:
await message.channel.send(f"Module {arg} is incompatible.") await message.channel.send(f"Module {arg} is incompatible.")
elif e: elif e:
await message.channel.send(f"An error occurred during the loading of the module {arg}: {e}.") await message.channel.send(f"An error occurred during the loading of the module {arg}: {e}.")
await self.com_list(message, args, kwargs) await self.com_list(message, args, kwargs)
async def com_reload(self, message, args, kwargs): async def com_reload(self, message, args, kwargs):
args = args[1:] args = args[1:]
if len(args) == 0: if len(args) == 0:
await message.channel.send("You must specify at least one module.") await message.channel.send("You must specify at least one module.")
return return
if len(args) == 1 and args[0] == "*": if len(args) == 1 and args[0] == "*":
for module in self.get_all_modules(): for module in self.get_all_modules():
e = self.client.unload_module(module) e = self.client.unload_module(module)
if e: if e:
await message.channel.send(f"An error occurred during the unloading of the module {module}.") await message.channel.send(f"An error occurred during the unloading of the module {module}.")
e = self.client.load_module(module) e = self.client.load_module(module)
if e: if e:
await message.channel.send(f"An error occurred during the loading of the module {module}.") await message.channel.send(f"An error occurred during the loading of the module {module}.")
await self.com_list(message, args, kwargs) await self.com_list(message, args, kwargs)
return return
for arg in args: for arg in args:
e = self.client.unload_module(arg) e = self.client.unload_module(arg)
if e: if e:
await message.channel.send(f"An error occurred during the unloading of the module {arg}.") await message.channel.send(f"An error occurred during the unloading of the module {arg}.")
e = self.client.load_module(arg) e = self.client.load_module(arg)
if e: if e:
await message.channel.send(f"An error occurred during the loading of the module {arg}.") await message.channel.send(f"An error occurred during the loading of the module {arg}.")
await self.com_list(message, [], []) await self.com_list(message, [], [])
async def com_disable(self, message, args, kwargs): async def com_disable(self, message, args, kwargs):
args = args[1:] args = args[1:]
if len(args) == 0: if len(args) == 0:
await message.channel.send("You must specify at least one module.") await message.channel.send("You must specify at least one module.")
return return
if len(args) == 1 and args[0] == "*": if len(args) == 1 and args[0] == "*":
for module in self.get_all_modules(): for module in self.get_all_modules():
e = self.client.unload_module(module) e = self.client.unload_module(module)
if e: if e:
await message.channel.send(f"An error occurred during the loading of the module {module}.") await message.channel.send(f"An error occurred during the loading of the module {module}.")
await self.com_list(message, args, kwargs) await self.com_list(message, args, kwargs)
return return
for arg in args: for arg in args:
e = self.client.unload_module(arg) e = self.client.unload_module(arg)
if e: if e:
await message.channel.send(f"An error occurred during the loading of the module {arg}: {e}.") await message.channel.send(f"An error occurred during the loading of the module {arg}: {e}.")
await self.com_list(message, [], []) await self.com_list(message, [], [])
async def com_list(self, message, args, kwargs): async def com_list(self, message, args, kwargs):
list_files = self.get_all_modules() list_files = self.get_all_modules()
activated = set(self.client.config["modules"]) activated = set(self.client.config["modules"])
if len(activated): if len(activated):
activated_string = "\n+ " + "\n+ ".join(activated) activated_string = "\n+ " + "\n+ ".join(activated)
else: else:
activated_string = "" activated_string = ""
if len(activated) != len(list_files): if len(activated) != len(list_files):
deactivated_string = "\n- " + "\n- ".join(list_files.difference(activated)) deactivated_string = "\n- " + "\n- ".join(list_files.difference(activated))
else: else:
deactivated_string = "" deactivated_string = ""
embed = discord.Embed(title="[Modules] - Liste des modules", embed = discord.Embed(title="[Modules] - Liste des modules",
description="```diff{activated}{deactivated}```".format( description="```diff{activated}{deactivated}```".format(
activated=activated_string, activated=activated_string,
deactivated=deactivated_string) deactivated=deactivated_string)
) )
await message.channel.send(embed=embed) await message.channel.send(embed=embed)
async def com_web_list(self, message, args, kwargs): async def com_web_list(self, message, args, kwargs):
try: try:
modules = await self.api.list() modules = await self.api.list()
except ClientConnectorError: except ClientConnectorError:
await message.channel.send("Connection impossible au serveur.") await message.channel.send("Connection impossible au serveur.")
return return
text = "" text = ""
for module, versions in modules.items(): for module, versions in modules.items():
text += module + " - " + ", ".join(versions) text += module + " - " + ", ".join(versions)
await message.channel.send(text) await message.channel.send(text)
async def com_web_dl(self, message, args, kwargs): async def com_web_dl(self, message, args, kwargs):
try: try:
await self.api.download(args[1], args[2]) await self.api.download(args[1], args[2])
except ClientConnectorError: except ClientConnectorError:
await message.channel.send("Connection impossible au serveur.") await message.channel.send("Connection impossible au serveur.")

View File

@ -1,14 +1,14 @@
{ {
"version": "0.1.0", "version": "0.1.0",
"type": "python", "type": "python",
"dependencies": { "dependencies": {
"base": { "base": {
"min": "0.1.0", "min": "0.1.0",
"max": "0.1.0" "max": "0.1.0"
} }
}, },
"bot_version": { "bot_version": {
"min": "0.1.0", "min": "0.1.0",
"max": "0.1.0" "max": "0.1.0"
} }
} }

View File

@ -0,0 +1,11 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -0,0 +1,11 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -0,0 +1,11 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -0,0 +1,11 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -0,0 +1,11 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -0,0 +1,11 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -0,0 +1,11 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -0,0 +1,11 @@
{
"version":"0.1.0",
"type": "python",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

0
src/utils/__init__.py Normal file
View File