Skip to content

Commit

Permalink
chore: rewrite codebase (#4)
Browse files Browse the repository at this point in the history
- Switch bot framework from discord.py to hikari
- Use pydantic for data type validation
- Use rich for prettier console logging
- Add settings module for storing constants used accross Dayong's modules
- Rename extensions directory from cogs to components
- Separate bot config from bot session
- Implement interfaces

NOTE: For interfaces, see PEP 544 -- Protocols: Structural subtyping (static duck typing)
  • Loading branch information
huenique committed Sep 27, 2021
1 parent 95a29e9 commit 4a0888e
Show file tree
Hide file tree
Showing 28 changed files with 445 additions and 753 deletions.
4 changes: 1 addition & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Create a copy of this file inside the project root directory. Remove the
# .example at the end.

BOT_COMMAND_PREFIX=.
TOKEN=your_bot_token
APPLICATION_ID=your_application_id
BOT_TOKEN=your_bot_token
DATABASE_URI=postgresql+asyncpg://[userspec@][hostspec][/dbname][?paramspec]
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 SurPath Hub

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
12 changes: 0 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,3 @@ Dayong is dedicated to helping Discord servers build and manage their communitie
- Self-hosted and easy to deploy —just a few more steps to take.
- Free and open-source —tinker with it, and feel free to contribute to its improvement!
- Modular —easily add extensions and features.

> For setup and installation instructions, please refer to the [documentation](./docs).
## Usage

1. From the project root directory, run:

```
python dayong
```

2. Open your Discord application. Go to the server where you invited the bot and run `<your command prefix>help`. For instance: `.help` or `!help`. The dot prefix is the default.
3 changes: 2 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"bot_prefix": ".",
"embeddings": {
"greetings_channel": "welcome",
"readme_channel_id": 790110106809401344,
Expand Down Expand Up @@ -31,4 +32,4 @@
}
}
}
}
}
10 changes: 0 additions & 10 deletions dayong/__init__.py

This file was deleted.

8 changes: 2 additions & 6 deletions dayong/__main__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
"""
Dayong's entry point.
"""
from dayong.cli import SetupCLI, command_line, display_banner
from dayong import bot

if __name__ == "__main__":
args = command_line()
display_banner()
setup = SetupCLI()
setup.check_configs()
setup.run_dayong()
bot.run()
28 changes: 0 additions & 28 deletions dayong/banner.txt

This file was deleted.

111 changes: 47 additions & 64 deletions dayong/bot.py
Original file line number Diff line number Diff line change
@@ -1,84 +1,67 @@
"""
This module defines the setup logic for Dayong.
"""
import json
import os
import sys
from pathlib import Path
from typing import Union

from discord import Intents # type: ignore
from discord.ext.commands import Bot # type: ignore
from dotenv import load_dotenv
import hikari
import tanjun

from dayong.exceptions import exception_handler
from dayong.config import DayongConfig, DayongConfigLoader
from dayong.impls import DatabaseImpl
from dayong.protocols import DatabaseProto
from dayong.settings import BASE_DIR

# Parse the .env file and load the environment variables.
load_dotenv()
if os.name != "nt":
import uvloop

BASE_DIR = Path(__file__).resolve().parent
ROOT_DIR = BASE_DIR.parent
CONFIG_FILE = os.path.join(ROOT_DIR, "config.json")
EMBEDDINGS: dict
uvloop.install()

with open(CONFIG_FILE, encoding="utf-8") as cfp:
config = json.load(cfp)
EMBEDDINGS = config["embeddings"]

# Environment variables or secrets.
BOT_COMMAND_PREFIX: Union[str, None] = os.getenv("BOT_COMMAND_PREFIX")
TOKEN: Union[str, None] = os.getenv("TOKEN")
APPLICATION_ID: Union[str, None] = os.getenv("APPLICATION_ID")
DATABASE_URI: Union[str, None] = os.getenv("DATABASE_URI")
async def get_prefix(
ctx: tanjun.abc.MessageContext,
db: DatabaseProto = tanjun.injected(type=DatabaseProto),
) -> Union[list[str], tuple[()]]:
if ctx.guild_id and (guild_info := await db.get_guild_info(ctx.guild_id)):
return guild_info.prefixes

return ()

class Setup:
"""Base Setup class."""

dayong: Bot
def fetch_component() -> list[str]:
"""Traverse the components directory and collect component modules.
@staticmethod
def check_configs() -> None:
"""Check if `config.json` exists."""
if not os.path.isfile(CONFIG_FILE):
sys.exit(f"config.json missing from {ROOT_DIR}!")
This will fetch the module from the components directory, remove its
extension and convert it to `sys.path`.
@staticmethod
def load_extensions() -> list[str]:
"""Traverse the `cogs` directory and collect cog modules.
Returns:
list[str]: Sequence of python modules.
"""
extensions: list[str] = []
components = os.path.join(BASE_DIR, "components")

Returns:
list[str]: A list of Python modules. The `.py` extension should be
omitted.
"""
extensions: list[str] = []
for file in os.listdir(components):
if file.endswith(".py") and "__" not in file:
file = f"components.{file}".replace(".py", "")
extensions.append(file)

for file in os.listdir(os.path.join(BASE_DIR, "cogs")):
# Append cog modules and ignore dunder files.
if file.endswith(".py") and "__" not in file:
extensions.append(file.replace(".py", ""))
return extensions

return extensions

@exception_handler
def run_dayong(self) -> None:
"""Run Dayong with the configurations and the owner's bot credentials."""
pref = BOT_COMMAND_PREFIX
exts = self.load_extensions()

intents = Intents.default()
intents.members = True # pylint: disable=E0237

self.dayong = Bot(pref, intents=intents)

for ext in exts:
self.dayong.load_extension(f"cogs.{ext}")

if TOKEN is None:
raise Exception("Bot token missing: {TOKEN}")

self.dayong.run(TOKEN)
def run() -> None:
"""Run Dayong using configs and deps."""
loaded_config = DayongConfig(**DayongConfigLoader().__dict__)
bot = hikari.GatewayBot(loaded_config.bot_token)
(
tanjun.Client.from_gateway_bot(bot)
.load_modules(*fetch_component())
.add_prefix(loaded_config.bot_prefix)
.set_prefix_getter(get_prefix)
.set_type_dependency(DayongConfig, lambda: loaded_config)
.set_type_dependency(
DatabaseProto,
tanjun.cache_callback(DatabaseImpl.connect),
)
)
bot.run()


if __name__ == "__main__":
pass
run()
34 changes: 0 additions & 34 deletions dayong/cli.py

This file was deleted.

3 changes: 0 additions & 3 deletions dayong/cogs/__init__.py

This file was deleted.

0 comments on commit 4a0888e

Please sign in to comment.