diff --git a/.gitignore b/.gitignore index 788da23..e6736fb 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ dist/ build/ .venv .env* +conf.yaml tags __pycache__ *.egg-info/ diff --git a/src/SideBot/__init__.py b/SideBot/__init__.py similarity index 76% rename from src/SideBot/__init__.py rename to SideBot/__init__.py index ff50461..93a085a 100644 --- a/src/SideBot/__init__.py +++ b/SideBot/__init__.py @@ -5,6 +5,7 @@ import pathlib import typing +import yaml import asyncpg import discord from discord.ext import commands @@ -12,7 +13,7 @@ from SideBot.db.tags import Tag -from .utils import ButtonLink, DiscordUser +from .utils import ButtonLink, DiscordUser, BotConfig class SideBot(Bot): @@ -20,8 +21,7 @@ class SideBot(Bot): def __init__(self, config: dict[str, str]) -> None: """Initialize the bot with the given configuration.""" - self.__tok = config.pop("DTOKEN") - self.config = config + self.config = BotConfig.from_dict(config) self.logger = logging.getLogger(__name__) intents = discord.Intents.all() @@ -31,16 +31,16 @@ def __init__(self, config: dict[str, str]) -> None: intents=intents, ) - self.owner_id = int(self.config["OWNER"]) - self.conf_cogs = self.config["COGS"].split(",") + self.owner_id = self.config.owner + self.conf_cogs = self.config.cogs async def setup_connection(self) -> asyncpg.Connection: """Set up the database connection.""" - return await asyncpg.connect(self.config["DATABASE_URL"]) + return await asyncpg.connect(self.config.db_url) async def setup_hook(self) -> None: """Set up cogs and app commands.""" - for cog in self.conf_cogs: + for cog in self.config.cogs: await self.load_extension(f"SideBot.cogs.{cog}") self.logger.debug(self.extensions) self.logger.debug(self.tree.get_commands()) @@ -55,6 +55,7 @@ async def on_ready(self) -> None: self.user.id, ) self.connection: asyncpg.Connection = await self.setup_connection() + self.logger.info("Connected to postgresql!") await Tag.write_schema(self.connection) @@ -94,14 +95,9 @@ def run( """Run the bot with the given token.""" if token: return super().run(token, *args, root_logger=True, **kwargs) - return super().run(self.__tok, *args, root_logger=True, **kwargs) + return super().run(self.config.token, *args, root_logger=True, **kwargs) @classmethod - def from_env(cls, path: str = ".env") -> "SideBot": - """Load the bot from a .env file with the proper configuration.""" - with pathlib.Path(path).open(encoding="utf-8") as env: - conf = { - k: v for line in env if (k := line.strip().split("=", 1)[0]) and (v := line.strip().split("=", 1)[1]) - } - - return cls(conf) + def from_yaml_file(cls, path: str = "conf.yaml") -> "SideBot": + with open(path, 'r') as f: + return cls(yaml.safe_load(f)) diff --git a/src/SideBot/__main__.py b/SideBot/__main__.py similarity index 63% rename from src/SideBot/__main__.py rename to SideBot/__main__.py index ee2df43..07fc957 100644 --- a/src/SideBot/__main__.py +++ b/SideBot/__main__.py @@ -2,4 +2,4 @@ from . import SideBot -SideBot.from_env().run() +SideBot.from_yaml_file().run() diff --git a/src/SideBot/cogs/__init__.py b/SideBot/cogs/__init__.py similarity index 100% rename from src/SideBot/cogs/__init__.py rename to SideBot/cogs/__init__.py diff --git a/src/SideBot/cogs/admin.py b/SideBot/cogs/admin.py similarity index 100% rename from src/SideBot/cogs/admin.py rename to SideBot/cogs/admin.py diff --git a/src/SideBot/cogs/basecog.py b/SideBot/cogs/basecog.py similarity index 100% rename from src/SideBot/cogs/basecog.py rename to SideBot/cogs/basecog.py diff --git a/src/SideBot/cogs/developer.py b/SideBot/cogs/developer.py similarity index 100% rename from src/SideBot/cogs/developer.py rename to SideBot/cogs/developer.py diff --git a/src/SideBot/cogs/tags.py b/SideBot/cogs/tags.py similarity index 100% rename from src/SideBot/cogs/tags.py rename to SideBot/cogs/tags.py diff --git a/src/SideBot/cogs/utility.py b/SideBot/cogs/utility.py similarity index 100% rename from src/SideBot/cogs/utility.py rename to SideBot/cogs/utility.py diff --git a/src/SideBot/db/__init__.py b/SideBot/db/__init__.py similarity index 100% rename from src/SideBot/db/__init__.py rename to SideBot/db/__init__.py diff --git a/src/SideBot/db/tags.py b/SideBot/db/tags.py similarity index 100% rename from src/SideBot/db/tags.py rename to SideBot/db/tags.py diff --git a/SideBot/utils/__init__.py b/SideBot/utils/__init__.py new file mode 100644 index 0000000..e5e08d6 --- /dev/null +++ b/SideBot/utils/__init__.py @@ -0,0 +1,120 @@ +"""utilites for SideBot.""" + +import discord + + +class DBConfig: + """DBConfig class for Postgresql with Bot""" + __slot__ = ('user', 'password', 'host', 'port', 'name') + + def __init__( + self, + user: str, + password: str, + host: str, + port: int | None = None, + name: str | None = None + ): + self.user = user + self.password = password + self.host = host + self.port = port or 5432 + self.name = name or user + + @property + def connect_str(self): + return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.name}" + + @property + def as_dict(self): + return { + 'user': self.user, + 'pass': self.password, + 'host': self.host, + 'port': self.port, + 'name': self.name, + } + + @classmethod + def from_dict(cls, data: dict): + return cls( + data['user'], + data['pass'], + data['host'], + data['port'] if 'port' in data else None, + data['name'] if 'name' in data else data['user'] + ) + +class BotConfig: + """BotConfig class for SideBot""" + __slots__ = ('token', 'owner', 'db_url', 'cogs') + + def __init__( + self, + token: str, + owner: int, + db_url: str, + cogs: list[str] + ): + self.token = token + self.owner = owner + self.db_url = db_url + self.cogs = cogs + + @classmethod + def from_dict(cls, data: dict): + if 'botDB' in data: + return cls( + data['discordToken'], + data['owner'], + DBConfig.from_dict(data['botDB']).connect_str, + data['cogs'] + ) + return cls( + data['discordToken'], + data['owner'], + data['botDBURL'], + data['cogs'] + ) + + +class DiscordUser: + """DiscordUser class.""" + + def __init__(self, iden: int, name: str) -> None: + """DiscordUser class.""" + self.id = iden + self.name = name + + def to_tuple(self) -> tuple[int, str]: + """Convert to tuple.""" + return self.id, self.name + + @classmethod + def from_tuple(cls, user: tuple[int, str]) -> "DiscordUser": + """Convert from tuple.""" + return cls(*user) + + @classmethod + def from_dpy_user(cls, user: discord.User | discord.Member) -> "DiscordUser": + """Convert from discord.py User.""" + return cls(user.id, user.name) + + +class ButtonLink: + """ButtonLink class.""" + + def __init__(self, label: str, url: str) -> None: + """ButtonLink class.""" + self.label = label + self.url = url + + @classmethod + def to_tuple(cls, button: "ButtonLink") -> tuple[str, str]: + """Convert to tuple.""" + return button.label, button.url + + @classmethod + def from_tuple(cls, button: tuple[str, str]) -> "ButtonLink": + """Convert from tuple.""" + return cls(*button) diff --git a/conf.yaml.sample b/conf.yaml.sample new file mode 100644 index 0000000..074644c --- /dev/null +++ b/conf.yaml.sample @@ -0,0 +1,16 @@ +owner: 195864152856723456 +cogs: +- admin +- developer +- tags +- utility +discordToken: 111111111111111111111111111111111111111111111111111111111111111111111111 +botDBURL: postgresql://sidebot:password@127.0.0.1:5432/sidebot +# or +botDB: + user: sidebot + pass: password + host: 127.0.0.1 + # Optional + port: 5432 + name: sidebot diff --git a/pyproject.toml b/pyproject.toml index 2b7b508..65e5279 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ description = "SideBot is a multipurpose bot for the SideStore community." readme = "README.md" requires-python = ">=3.12" dependencies = [ - "discord.py>=2.3.2", + "discord.py>=2.4.0", "asyncpg>=0.29.0", "openai>=1.37.0", ] diff --git a/src/SideBot/utils/__init__.py b/src/SideBot/utils/__init__.py deleted file mode 100644 index cd8c655..0000000 --- a/src/SideBot/utils/__init__.py +++ /dev/null @@ -1,46 +0,0 @@ -"""utilites for SideBot.""" - -import discord - - -class DiscordUser: - """DiscordUser class.""" - - def __init__(self, iden: int, name: str) -> None: - """DiscordUser class.""" - self.id = iden - self.name = name - - @classmethod - def to_tuple(cls, user: "DiscordUser") -> tuple[int, str]: - """Convert to tuple.""" - return user.id, user.name - - @classmethod - def from_tuple(cls, user: tuple[int, str]) -> "DiscordUser": - """Convert from tuple.""" - return cls(*user) - - @classmethod - def from_dpy_user(cls, user: discord.User | discord.Member) -> "DiscordUser": - """Convert from discord.py User.""" - return cls(user.id, user.name) - - -class ButtonLink: - """ButtonLink class.""" - - def __init__(self, label: str, url: str) -> None: - """ButtonLink class.""" - self.label = label - self.url = url - - @classmethod - def to_tuple(cls, button: "ButtonLink") -> tuple[str, str]: - """Convert to tuple.""" - return button.label, button.url - - @classmethod - def from_tuple(cls, button: tuple[str, str]) -> "ButtonLink": - """Convert from tuple.""" - return cls(*button)