[bot-base]

[mod-all] Le bot démarre, mais il en fait pas plus
This commit is contained in:
Louis Chauvet 2020-04-20 19:36:45 +02:00
parent 5f72e036a5
commit 50aced7c5d
Signed by: fomys
GPG Key ID: 1ECA046A9615ABA0
11 changed files with 599 additions and 150 deletions

View File

@ -1,90 +0,0 @@
from __future__ import annotations
from typing import Dict, Any, Optional
class Config:
name: Optional[str]
parent: Optional[Config]
config: Dict[Any, Any]
def __init__(self, parent: Config = None, name: str = None, client: LBI = None):
"""Create Config
:param parent: Parent configuration
:param name: Configuration name
:type parent: Config
:type name: str"""
self.parent = parent
self.config = dict()
self.name = None
self.client = client
if self.parent:
self.name = name
self.client = self.parent.client
def init(self, config):
"""Load default configuration
:param config: Default configuration
:type config: dict
:return: None"""
# Load data from config file before initialisation
self.load()
# Get data from parent
if self.parent is not None:
self.parent.config[self.name] = self.parent.config.get(self.name) if self.parent.config.get(
self.name) is not None else self.config
self.config = self.parent.config[self.name]
# Set config only if not already defined
for k, v in config.items():
self.config[k] = self.config.get(k) if self.config.get(k) is not None else v
# Save new datas
self.save()
def _save(self):
"""Internal function for save
Must be overridden by all type of config to handle saving"""
# Call parent save if necessary
if self.parent:
self.parent.save()
def save(self):
"""Public save function
Do not override"""
self._save()
def _load(self):
"""Internal function for load
Mus be overridden by all type of config to handle loading"""
# Load parent if necessary
if self.parent:
self.parent.load()
self.config = self.parent.config.get(self.name)
# Initialize parent if necessary
if self.config is None:
self.parent.config[self.name] = {}
self.config = {}
def load(self):
"""Public load function
Do not override"""
self._load()
def __getattr__(self, item):
return self.config.get(item)
def __getitem__(self, item):
return self.config.get(item)
def __setitem__(self, key, value):
if self.parent:
self.parent[self.name][key] = value
self.config = self.parent[self.name]
else:
self.config[key] = value
self.save()

View File

@ -1,13 +1,24 @@
from typing import Type
import config.config_types.discord_types
from config.config_types.base_type import BaseType
from config.config_types.bool import Bool
from config.config_types.color import Color
from config.config_types.dict import Dict
from config.config_types.float import Float
from config.config_types.int import Int
from config.config_types.list import List
from config.config_types.str import Str
__all__ = ['factory', "BaseType", 'Dict', 'Float', 'Int', 'List', 'Str']
__all__ = ['factory', "BaseType", 'Dict', 'Float', 'Int', 'List', 'Str', 'discord_types', 'Bool', 'Color']
class Meta(type):
def __repr__(cls):
if hasattr(cls, '__class_repr__'):
return getattr(cls, '__class_repr__')()
else:
return super(Meta, cls).__repr__()
def factory(type: Type[BaseType], *args, **kwargs):
@ -16,17 +27,22 @@ def factory(type: Type[BaseType], *args, **kwargs):
:Basic usage:
>>> factory(Int) # doctest: +ELLIPSIS
<class '...'>
>>> factory(Int, min=0, max=10) # doctest: +ELLIPSIS
<class '...'>
>>> factory(Int)
<config_types.Int with parameters () {}>
>>> factory(Int, min=0, max=10)
<config_types.Int with parameters () {'min': 0, 'max': 10}>
:param type: Type to create
:return: New type
"""
class Type(type):
class Type(type, metaclass=Meta):
def __init__(self):
super().__init__(*args, **kwargs)
@classmethod
def __class_repr__(cls):
return f"<config_types.{cls.__base__.__name__} with parameters {args} {kwargs}>"
return Type

128
config/config_types/bool.py Normal file
View File

@ -0,0 +1,128 @@
from typing import Optional
from config.config_types.base_type import BaseType
class Bool(BaseType):
#: Current value of parameter
value: Optional[bool]
def __init__(self) -> None:
"""
Base Bool type for config
:Basic usage:
>>> Bool()
<config_types.Bool object with value None>
"""
self.value = None
def check_value(self, value: bool) -> bool:
"""
Check if value is a correct bool
Check if value is int, and if applicable, between ``min`` and ``max`` or in ``values``
:Basic usage:
>>> my_bool = Bool()
>>> my_bool.check_value(0)
True
>>> my_bool.check_value(-2)
True
>>> my_bool.check_value(345)
True
>>> my_bool.check_value(0)
True
>>> my_bool.check_value(-2)
True
>>> my_bool.check_value(345)
True
>>> my_bool.check_value(10)
True
>>> my_bool.check_value(-2)
True
>>> my_bool.check_value(20)
True
>>> my_bool.check_value(2)
True
>>> my_bool.check_value(4)
True
>>> my_bool.check_value(5)
True
:param value: value to check
:return: True if value is correct
"""
try:
bool(value)
except ValueError:
return False
return True
def set(self, value: bool) -> None:
"""
Set value of parameter
:Basic usage:
>>> my_bool = Bool()
>>> my_bool.set(34)
:param value: Value to set
:return: None
"""
if not self.check_value(value):
raise ValueError("Tentative de définir une valeur incompatible")
self.value = bool(value)
def get(self) -> Optional[int]:
"""
Get value of parameter
:Basic usage:
>>> my_bool = Bool()
>>> my_bool.set(34)
>>> my_bool.get()
True
:return: Value of parameter
"""
return self.value
def to_save(self) -> int:
"""
Build a serializable object
:Basic usage:
>>> my_bool = Bool()
>>> my_bool.to_save()
>>> my_bool.set(34)
>>> my_bool.to_save()
True
:return: Current value
"""
return self.value
def load(self, value: bool) -> None:
"""
Load serialized value
>>> my_bool = Bool()
>>> my_bool.load(True)
>>> my_bool.get()
True
:param value: Value to load
:return: None
"""
if not self.check_value(value):
raise ValueError("Tentative de charger une donnée incompatible.")
self.value = value
def __repr__(self):
return f'<config_types.Bool object with value {self.value}>'

View File

@ -0,0 +1,116 @@
from typing import Optional
from config.config_types.base_type import BaseType
class Color(BaseType):
#: Current value
value: Optional[int]
def __init__(self) -> None:
"""
Base Color type for config
:Basic usage:
>>> Color()
<config_types.Color object with value None>
"""
self.value = None
def check_value(self, value: int) -> bool:
"""
Check if value is a correct bool
Check if value is int, and if applicable, between ``min`` and ``max`` or in ``values``
:Basic usage:
>>> my_color = Color()
>>> my_color.check_value(0xFF00FF)
True
>>> my_color.check_value(-2)
False
>>> my_color.check_value(345)
True
>>> my_color.check_value(0xFFFFFF)
True
>>> my_color.check_value(0x000000)
True
>>> my_color.check_value(0x1000000)
False
:param value: value to check
:return: True if value is correct
"""
try:
int(value)
except ValueError:
return False
return 0xFFFFFF >= value >= 0x000000
def set(self, value: int) -> None:
"""
Set value of parameter
:Basic usage:
>>> my_color = Color()
>>> my_color.set(34)
:param value: Value to set
:return: None
"""
if not self.check_value(value):
raise ValueError("Tentative de définir une valeur incompatible")
self.value = int(value)
def get(self) -> Optional[int]:
"""
Get value of parameter
:Basic usage:
>>> my_color = Color()
>>> my_color.set(34)
>>> my_color.get()
34
:return: Value of parameter
"""
return self.value
def to_save(self) -> int:
"""
Build a serializable object
:Basic usage:
>>> my_color = Color()
>>> my_color.to_save()
>>> my_color.set(34)
>>> my_color.to_save()
34
:return: Current value
"""
return self.value
def load(self, value: int) -> None:
"""
Load serialized value
>>> my_color = Color()
>>> my_color.load(True)
>>> my_color.get()
True
:param value: Value to load
:return: None
"""
if not self.check_value(value):
raise ValueError("Tentative de charger une donnée incompatible.")
self.value = value
def __repr__(self):
return f'<config_types.Color object with value {self.value}>'

View File

@ -1,18 +1,79 @@
from typing import Optional
from config.config_types.base_type import BaseType
class Float(BaseType):
def __init__(self, min_=None, max_=None):
#: Max value for parameter
max: Optional[float]
#: Min value for parameter
min: Optional[float]
#: Current value of parameter
value: Optional[float]
def __init__(self, min: Optional[float] = None, max: Optional[float] = None) -> None:
"""
Base Float type for config
:Basic usage:
>>> Float()
<config_types.Float object with value None>
>>> Float(min=0)
<config_types.Float object with value None, min=0 max=None>
>>> Float(max=0)
<config_types.Float object with value None, min=None max=0>
>>> Float(min=10, max=20)
<config_types.Float object with value None, min=10 max=20>
:param min: Minimal value for parameter
:param max: Maximal value for parameter
"""
self.value = None
self.min = min_
self.max = max_
self.min = min
self.max = max
def check_value(self, value):
"""
Check if value is a correct int
Check if value is int, and if applicable, between ``min`` and ``max`` or in ``values``
:Basic usage:
>>> positive = Float(min=0)
>>> negative = Float(max=0)
>>> ten_to_twenty = Float(min=10, max=20)
>>> positive.check_value(0)
True
>>> positive.check_value(-2.143)
False
>>> positive.check_value(345.124)
True
>>> negative.check_value(0)
True
>>> negative.check_value(-2.1324)
True
>>> negative.check_value(345.124)
False
>>> ten_to_twenty.check_value(10)
True
>>> ten_to_twenty.check_value(-2.1234)
False
>>> ten_to_twenty.check_value(13.34)
True
>>> ten_to_twenty.check_value(20)
True
>>> ten_to_twenty.check_value(23.34)
False
:param value: value to check
:return: True if value is correct
"""
try:
float(value)
except ValueError:
return False
# TODO: < ou <=? > ou >=?
# Check min/max
if self.min is not None and float(value) < self.min:
return False
@ -21,18 +82,73 @@ class Float(BaseType):
return True
def set(self, value):
if self.check_value(value):
self.value = value
return
"""
Set value of parameter
:Basic usage:
>>> my_float = Float(min=0)
>>> my_float.set(34.324)
>>> my_float.set(-34.32412) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ValueError: ...
:raise ValueError: if attempt to set invalid value
:param value: Value to set
:return: None
"""
if not self.check_value(value):
raise ValueError("Tentative de définir une valeur incompatible")
self.value = float(value)
def get(self):
"""
Get value of parameter
:Basic usage:
>>> my_float = Float()
>>> my_float.set(-3/4)
>>> my_float.get()
-0.75
:return: Value of parameter
"""
return self.value
def to_save(self):
"""
Build a serializable object
:Basic usage:
>>> my_float = Float()
>>> my_float.to_save()
>>> my_float.set(3/4)
>>> my_float.to_save()
0.75
:return: Current value
"""
return self.value
def load(self, value):
if self.check_value(value):
"""
Load serialized value
>>> my_float = Float()
>>> my_float.load(3/4)
>>> my_float.get()
0.75
:param value: Value to load
:return: None
"""
if not self.check_value(value):
raise ValueError("Tentative de charger une donnée incompatible.")
self.value = value
def __repr__(self):
if self.min is not None or self.max is not None:
return f'<config_types.Float object with value {self.value}, min={self.min} max={self.max}>'
return f'<config_types.Float object with value {self.value}>'

View File

@ -21,15 +21,15 @@ class Int(BaseType):
:Basic usage:
>>> Int()
<Int object with value None>
<config_types.Int object with value None>
>>> Int(min=0)
<Int object with value None, min=0 max=None>
<config_types.Int object with value None, min=0 max=None>
>>> Int(max=0)
<Int object with value None, min=None max=0>
<config_types.Int object with value None, min=None max=0>
>>> Int(min=10, max=20)
<Int object with value None, min=10 max=20>
<config_types.Int object with value None, min=10 max=20>
>>> Int(values=[2, 3, 5, 7])
<Int object with value None, values=[2, 3, 5, 7]>
<config_types.Int object with value None, values=[2, 3, 5, 7]>
>>> Int(min=0, values=[3, 4, 5]) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ValueError: ...
@ -91,7 +91,6 @@ class Int(BaseType):
int(value)
except ValueError:
return False
# TODO: < ou <=? > ou >=?
# Check min/max
if self.min is not None and int(value) < self.min:
return False
@ -120,7 +119,7 @@ class Int(BaseType):
"""
if not self.check_value(value):
raise ValueError("Tentative de définir une valeur incompatible")
self.value = value
self.value = int(value)
def get(self) -> Optional[int]:
"""
@ -171,7 +170,7 @@ class Int(BaseType):
def __repr__(self):
if self.min is not None or self.max is not None:
return f'<Int object with value {self.value}, min={self.min} max={self.max}>'
return f'<config_types.Int object with value {self.value}, min={self.min} max={self.max}>'
if self.values:
return f'<Int object with value {self.value}, values={self.values}>'
return f'<Int object with value {self.value}>'
return f'<config_types.Int object with value {self.value}, values={self.values}>'
return f'<config_types.Int object with value {self.value}>'

View File

@ -1,22 +1,77 @@
import typing
from typing import Type
from config.config_types.base_type import BaseType
class List(BaseType):
#: Current list of value
values: typing.List[BaseType]
#: Type of values
type_: Type[BaseType]
def __init__(self, type_, max_len=None):
def __init__(self, type_: Type[BaseType]) -> None:
"""
Base List type for config
:Basic usage:
>>> from config.config_types import factory, Int, Float
>>> List(factory(Int))
<config_types.List of <config_types.Int with parameters () {}> objects with values []>
>>> List(factory(Float))
<config_types.List of <config_types.Float with parameters () {}> objects with values []>
:param type_: Type of items
"""
self.type_ = type_
self.max_len = max_len
self.values = None
self.values = []
def check_value(self, value):
def check_value(self, value: typing.List[typing.Any]) -> bool:
"""
Check if value is correct
:Basic usage:
>>> from config.config_types import factory, Int
>>> my_list = List(factory(Int))
>>> my_list.check_value([34,])
True
>>> my_list.check_value(345)
False
>>> my_list.check_value([345, 34, 23, 45, 34, 46, 35, 2345, 'rt'])
False
:param value: Value to check
:return: True if value is correct
"""
new_object = self.type_()
return new_object.check_value(value)
try:
for v in value:
if not new_object.check_value(v):
return False
except TypeError:
return False
return True
def set(self, value):
"""Check and set value"""
def set(self, value: typing.List[typing.Any]) -> None:
"""
Set value of parameter
:Basic usage:
>>> from config.config_types import factory, Int
>>> my_list = List(factory(Int))
>>> my_list.set(34) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ValueError: ...
>>> my_list.set([45,])
:param value: Value to set
:return: None
"""
if not self.check_value(value):
raise ValueError('Tentative de définir une valeur incompatible')
new_liste = []
for v in value:
new_element = self.type_()
@ -24,19 +79,60 @@ class List(BaseType):
new_liste.append(new_element)
self.values = new_liste
def get(self):
"""Get value"""
if self.values is None:
raise ValueError("Config non initialisée")
def get(self) -> typing.List[typing.Any]:
"""
Get value of parameter
:Basic usage:
>>> from config.config_types import factory, Int
>>> my_list = List(factory(Int))
>>> my_list.set([34, ])
>>> my_list.get()
[34]
:raise ValueError: If config is empty
:return: Value of parameter
"""
return [v.get() for v in self.values]
def to_save(self):
"""Build a serializable data to save"""
def to_save(self) -> typing.List[typing.Any]:
"""
Build a serializable object
:Basic usage:
>>> from config.config_types import factory, Int
>>> my_list = List(factory(Int))
>>> my_list.to_save()
[]
>>> my_list.set([34, ])
>>> my_list.to_save()
[34]
:return: Current value
"""
return [v.to_save() for v in self.values]
def load(self, value):
"""Fill with value"""
def load(self, value: typing.List[typing.Any]) -> None:
"""
Load serialized value
>>> from config.config_types import factory, Int
>>> my_list = List(factory(Int))
>>> my_list.load([34,])
>>> my_list.get()
[34]
:param value: Value to load
:return: None
"""
if not self.check_value(value):
raise ValueError("Tentative de charger une donnée incompatible.")
for v in value:
new_object = self.type_()
new_object.load(v)
self.values.append(new_object)
def __repr__(self):
return f'<config_types.List of {self.type_} objects with values {self.values}>'

View File

@ -2,34 +2,86 @@ from config.config_types.base_type import BaseType
class Str(BaseType):
def __init__(self):
self.value = None
value: str
def check_value(self, value):
"""Check if value is good"""
def __init__(self) -> None:
"""
Base Str type for config
:Basic usage:
>>> Str()
<config_types.Str object with value "">
"""
self.value = ""
def check_value(self, value: str) -> bool:
"""
Check if value is usable as str
:Basic usage:
>>> my_str = Str()
>>> my_str.check_value("toto")
True
>>> my_str.check_value(45)
True
:param value: Value to test
:return: True if value is usable as str
"""
try:
str(value)
except ValueError:
return False
return True
def set(self, value):
"""Check and set value"""
if self.check_value(value):
self.value = value
return
raise ValueError("Tentative de définir une valeur incompatible")
def set(self, value: str) -> None:
"""
Set value of parameter
def get(self):
"""Get value"""
:Basic usage:
>>> my_str = Str()
>>> my_str.set("e")
>>> my_str.set(34)
:raise ValueError: if attempt to set invalid value
:param value: Value to set
:return: None
"""
if not self.check_value(value):
raise ValueError("Tentative de définir une valeur incompatible")
self.value = str(value)
def get(self) -> str:
"""
Get value of parameter
:Basic usage:
>>> my_str = Str()
>>> my_str.set(34)
>>> print(my_str.get())
34
>>> my_str.set("Hey")
>>> print(my_str.get())
Hey
:return: Value of parameter
"""
return self.value
def to_save(self):
def to_save(self) -> str:
"""Build a serializable data to save"""
return self.value
def load(self, value):
def load(self, value: str) -> None:
"""Fill with value"""
if not self.check_value(value):
raise ValueError("Tentative de charger une donnée incompatible.")
self.value = value
def __repr__(self):
return f'<config_types.Str object with value "{self.value}">'

View File

@ -246,9 +246,9 @@ class LBI(discord.Client):
self.config = config
self.config.register("modules", factory(config_types.List, 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.Role, self)))
self.config.register("admin_users", factory(config_types.List, factory(config_types.discord.User, self)))
self.config.register("main_guild", factory(config_types.discord.Guild, 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("main_guild", factory(config_types.discord_types.Guild, self))
self.config.register("locale", factory(config_types.Str))
self.config.set({
@ -361,7 +361,7 @@ class LBI(discord.Client):
super().dispatch(event, *args, **kwargs)
# Dispatch to modules
for module in self.modules.values():
if module["initialized_class"].config.configured:
if module["initialized_class"].config["configured"]:
module["initialized_class"].dispatch(event, *args, **kwargs)
else:
self.warning(f"Module {module['initialized_class'].name} is not configured.")

View File

@ -5,7 +5,8 @@ from typing import List, Union, Optional
import discord
from config import Config
from config import Config, config_types
from config.config_types import factory
from storage import Objects
from utils import emojis
@ -30,6 +31,15 @@ class BaseClass:
self.client = client
self.objects = Objects(path=os.path.join("data", self.name.lower()))
self.config = Config(path=os.path.join("data", self.name.lower(), "config.toml"))
self.config.register("help_active", factory(config_types.Bool))
self.config.register("color", factory(config_types.Color))
self.config.register("auth_everyone", factory(config_types.Bool))
self.config.register("authorized_roles",
factory(config_types.List, factory(config_types.discord_types.Role, client)))
self.config.register("authorized_users",
factory(config_types.List, factory(config_types.discord_types.User, client)))
self.config.register("command_text", factory(config_types.Str))
self.config.register("configured", factory(config_types.Bool))
self.config.set({"help_active": True, "color": 0x000000, "auth_everyone": False, "authorized_roles": [],
"authorized_users": [], "command_text": self.name.lower(), "configured": False})

View File

@ -5,6 +5,8 @@ import traceback
import discord
from discord import Message
from config import config_types
from config.config_types import factory
from modules.base import BaseClassPython
@ -21,6 +23,10 @@ class MainClass(BaseClassPython):
def __init__(self, client):
super().__init__(client)
self.config.register("dev_chan",
factory(config_types.List, factory(config_types.discord_types.Channel, client)))
self.config.register("memes", factory(config_types.List, factory(config_types.Str)))
self.config.register("icon", factory(config_types.Str))
self.config.set({"dev_chan": [], "memes": [""], "icon": ""})
self.errorsList = None