diff --git a/README.md b/README.md index 5a8d35d..8b281f2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,86 @@ -# LibBotUniversal +

LibBotUniversal

+

+PyPI - License +Gitea Release +PyPI - Python Version +Code style: black +

+ +Handy library for Telegram/Discord bots development. + +## Getting started + +There are different sub-packages available: + +* pyrogram - Telegram bots with Pyrogram's fork "Pyrofork" +* pycord - Discord bots with Pycord +* speed - Performance improvements +* dev - Dependencies for package development purposes + +You can freely choose any sub-package you want, as well as add multiple (comma-separated) or none at all. + +```shell +# Only general features +pip install libbot + +# Only with Pyrogram +pip install libbot[pyrogram] + +# With Pycord and Performance improvements +pip install libbot[pycord,speed] +``` + +## Examples + +### Pyrogram + +```python +from libbot.pyrogram import PyroClient + +def main(): + client = PyroClient(scheduler=scheduler) + + try: + client.run() + except KeyboardInterrupt: + print("Shutting down...") + finally: + if client.scheduler is not None: + client.scheduler.shutdown() + exit() + + +if __name__ == "__main__": + main() +``` + +### Pycord + +```python +from discord import Intents +from libbot import sync +from libbot.pycord import PycordBot + + +async def main(): + intents = Intents.default() + bot = PycordBot(intents=intents) + + bot.load_extension("cogs") + + try: + await bot.start(sync.config_get("bot_token", "bot")) + except KeyboardInterrupt: + logger.warning("Shutting down...") + await bot.close() + + +if __name__ == "__main__": + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) +``` + +## Config examples + +For bot config examples please check the examples directory. Without a valid config file, the bot won't start at all, so you need to make sure the correct config file is used. diff --git a/examples/config_pycord.json b/examples/config_pycord.json new file mode 100644 index 0000000..128ebde --- /dev/null +++ b/examples/config_pycord.json @@ -0,0 +1,13 @@ +{ + "locale": "en", + "debug": false, + "bot": { + "owners": [ + 0 + ], + "debug_guilds": [ + 0 + ], + "bot_token": "" + } +} \ No newline at end of file diff --git a/examples/config.json b/examples/config_pyrogram.json similarity index 100% rename from examples/config.json rename to examples/config_pyrogram.json diff --git a/pyproject.toml b/pyproject.toml index 237b193..c9f761f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,11 +9,11 @@ authors = [{ name = "Profitroll" }] description = "Universal bot library with functions needed for basic Discord/Telegram bot development." readme = "README.md" requires-python = ">=3.8" -license = { file = "LICENSE" } +license = { text = "GPLv3" } classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Operating System :: OS Independent", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", diff --git a/requirements/dev.txt b/requirements/dev.txt index 2b24a25..c2ea1dc 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -2,10 +2,10 @@ black==24.4.2 build==1.2.1 isort==5.13.2 mypy==1.10.0 -pylint==3.2.0 -pytest-asyncio==0.23.6 +pylint==3.2.1 +pytest-asyncio==0.23.7 pytest-cov==5.0.0 pytest==8.2.0 tox==4.15.0 types-aiofiles==23.2.0.20240403 -types-ujson==5.9.0.0 \ No newline at end of file +types-ujson==5.10.0.20240515 \ No newline at end of file diff --git a/src/libbot/__init__.py b/src/libbot/__init__.py index 74b0290..ae01807 100644 --- a/src/libbot/__init__.py +++ b/src/libbot/__init__.py @@ -1,6 +1,6 @@ -__version__ = "3.0.1" +__version__ = "3.1.0" __license__ = "GPL3" __author__ = "Profitroll" -from . import i18n, pycord, pyrogram, sync +from . import errors, i18n, pycord, pyrogram, sync from .__main__ import * diff --git a/src/libbot/errors/__init__.py b/src/libbot/errors/__init__.py new file mode 100644 index 0000000..1b0549f --- /dev/null +++ b/src/libbot/errors/__init__.py @@ -0,0 +1 @@ +from .config import ConfigKeyError, ConfigValueError diff --git a/src/libbot/errors/config.py b/src/libbot/errors/config.py new file mode 100644 index 0000000..64f8a4c --- /dev/null +++ b/src/libbot/errors/config.py @@ -0,0 +1,37 @@ +from typing import Any, List, Optional, Union + + +class ConfigKeyError(Exception): + """Raised when config key is not found. + + ### Attributes: + * key (`Union[str, List[str]]`): Missing config key. + """ + + def __init__(self, key: Union[str, List[str]]) -> None: + self.key: Union[str, List[str]] = key + super().__init__( + f"Config key {'.'.join(key) if isinstance(key, list) else key} is missing. Please set in your config file." + ) + + def __str__(self): + return f"Config key {'.'.join(self.key) if isinstance(self.key, list) else self.key} is missing. Please set in your config file." + + +class ConfigValueError(Exception): + """Raised when config key's value is invalid. + + ### Attributes: + * key (`Union[str, List[str]]`): Invalid config key. + * value (`Optional[Any]`): Key's correct value. + """ + + def __init__(self, key: Union[str, List[str]], value: Optional[Any] = None) -> None: + self.key: Union[str, List[str]] = key + self.value: Optional[Any] = value + super().__init__( + f"Config key {'.'.join(key) if isinstance(key, list) else key} has invalid value. {f'Must be {value}. ' if value else ''}Please set in your config file." + ) + + def __str__(self): + return f"Config key {'.'.join(self.key) if isinstance(self.key, list) else self.key} has invalid value. {f'Must be {self.value}. ' if self.value else ''}Please set in your config file." diff --git a/src/libbot/pycord/classes/bot.py b/src/libbot/pycord/classes/bot.py index e59dad5..829961b 100644 --- a/src/libbot/pycord/classes/bot.py +++ b/src/libbot/pycord/classes/bot.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import Any, Dict, Union +from typing import Any, Dict, List, Union try: from apscheduler.schedulers.asyncio import AsyncIOScheduler @@ -29,6 +29,7 @@ class PycordBot(Bot): config_path: Union[str, Path] = Path("config.json"), locales_root: Union[str, Path, None] = None, scheduler: Union[AsyncIOScheduler, BackgroundScheduler, None] = None, + *args, **kwargs, ): if config is None: @@ -38,8 +39,11 @@ class PycordBot(Bot): self.config = config super().__init__( - debug_guilds=self.config["bot"]["debug_guilds"], + debug_guilds=( + self.config["bot"]["debug_guilds"] if self.config["debug"] else None + ), owner_ids=self.config["bot"]["owners"], + *args, **kwargs, ) @@ -48,7 +52,7 @@ class PycordBot(Bot): locales_root=(Path("locale") if locales_root is None else locales_root), ) self.default_locale: str = self.bot_locale.default - self.locales: dict = self.bot_locale.locales + self.locales: Dict[str, Any] = self.bot_locale.locales self._ = self.bot_locale._ self.in_all_locales = self.bot_locale.in_all_locales