Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/584.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
More additional fixes
7 changes: 5 additions & 2 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ ENV UV_LINK_MODE=copy
# Disable Python downloads as the system Python interpreter instead
ENV UV_PYTHON_DOWNLOADS=0

# Disable development dependencies from being installed
ENV UV_NO_DEV=1

# Set Tini version
# Ref: https://github.com/krallin/tini?tab=readme-ov-file#using-tini
ENV TINI_VERSION=v0.19.0
Expand All @@ -27,14 +30,14 @@ COPY pyproject.toml uv.lock /app/
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project --no-dev
uv sync --frozen --no-install-project

# Add project source code
COPY ./src /app

# Install the rest (this allows for optimal caching)
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev
uv sync --frozen

FROM python:3.13-slim-trixie

Expand Down
4 changes: 2 additions & 2 deletions docker/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: kumiko_dev

services:
database:
container_name: beaklevision_postgres
container_name: kumiko_postgres
image: postgres:17
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
Expand Down Expand Up @@ -39,7 +39,7 @@ services:
database:
condition: service_healthy
volumes:
- ../app/schema.sql:/schema.sql
- ../src/schema.sql:/schema.sql

volumes:
database:
Expand Down
6 changes: 3 additions & 3 deletions docker/docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
image: ghcr.io/no767/kumiko:edge
volumes:
# Do not edit the next line. If you want to change the path of the configuration file, please edit the CONFIG_LOCATION variable
- ${CONFIG_LOCATION}:/app/config.yml:ro
- ${CONFIG_LOCATION}:/app/config.yml
ports:
- 8770:8770
depends_on:
Expand Down Expand Up @@ -72,7 +72,7 @@ services:
database:
condition: service_healthy
volumes:
- ../app/schema.sql:/schema.sql
- ../src/schema.sql:/schema.sql

volumes:
database:
Expand All @@ -81,4 +81,4 @@ volumes:

networks:
db_bridge:
driver: bridge
driver: bridge
6 changes: 3 additions & 3 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
image: ghcr.io/no767/kumiko:edge
volumes:
# Do not edit the next line. If you want to change the path of the configuration file, please edit the CONFIG_LOCATION variable
- ${CONFIG_LOCATION}:/app/config.yml:ro
- ${CONFIG_LOCATION}:/app/config.yml
ports:
- 8770:8770
depends_on:
Expand Down Expand Up @@ -53,11 +53,11 @@ services:
database:
condition: service_healthy
volumes:
- ../app/schema.sql:/schema.sql
- ../src/schema.sql:/schema.sql

volumes:
database:

networks:
db_bridge:
driver: bridge
driver: bridge
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ dependencies = [
"msgspec~=0.19.0",
"prometheus-async~=25.1.0",
"prometheus-client~=0.23.1",
"psutil~=7.1.1",
"psutil~=7.1.2",
"pygit2~=1.19.0",
"python-dateutil~=2.9.0.post0",
"pyyaml~=6.0.3",
"typing-extensions~=4.15.0 ; python_full_version < '3.11'",
"uvloop~=0.22.1 ; sys_platform != 'win32'",
"watchfiles~=1.1.1",
"winloop~=0.3.1 ; sys_platform == 'win32'",
]

Expand All @@ -28,11 +29,11 @@ discord-ext-menus = { git = "https://github.com/Rapptz/discord-ext-menus", rev =

[dependency-groups]
dev = [
"watchfiles~=1.1.1",
"towncrier~=25.8.0",
{ include-group = "lint" },
{ include-group = "docs" }
]

lint = [
"pyright[nodejs]~=1.1.407",
"ruff~=0.14.2",
Expand Down
2 changes: 1 addition & 1 deletion src/cogs/ext/prometheus.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def _get_stats(self) -> GuildCount:
elif isinstance(channel, discord.VoiceChannel):
voice += 1

return GuildCount(count=guilds, text=text, voice=voice, users=users)
return GuildCount(amount=guilds, text=text, voice=voice, users=users)

def fill(self) -> None:
stats = self._get_stats()
Expand Down
2 changes: 1 addition & 1 deletion src/utils/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class KeyboardInterruptHandler:
def __init__(self, bot: Kumiko):
self.bot = bot
self._task: Optional[asyncio.Task]
self._task: Optional[asyncio.Task] = None

def __call__(self):
if self._task:
Expand Down
10 changes: 7 additions & 3 deletions src/utils/help.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
from __future__ import annotations

import inspect
import itertools
from collections.abc import Mapping
from typing import Any, Optional, Union
from typing import TYPE_CHECKING, Any, Optional, Union

import discord
from discord.ext import commands, menus

from core import Kumiko
from utils.embeds import Embed

from .pages import KumikoPages

if TYPE_CHECKING:
from collections.abc import Mapping

from core import Kumiko
# RGB Colors:
# Pink (255, 161, 231) - Used for the main bot page
# Lavender (197, 184, 255) - Used for cog and group pages
Expand Down
71 changes: 45 additions & 26 deletions src/utils/logger.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,65 @@
import logging
from logging.handlers import RotatingFileHandler
from types import TracebackType
from typing import TypeVar

import discord
from utils.checks import is_docker

BE = TypeVar("BE", bound=BaseException)
MAX_BYTES = 32 * 1024 * 1024 # 32 MiB


class KumikoLogger:
def __init__(self) -> None:
self.self = self
self.log = logging.getLogger("kumiko")
self._disable_watchfiles_logger()

def __enter__(self) -> None:
max_bytes = 32 * 1024 * 1024 # 32 MiB
self.log.setLevel(logging.INFO)
logging.getLogger("watchfiles").setLevel(logging.ERROR)
logging.getLogger("discord").setLevel(logging.INFO)
handler = RotatingFileHandler(
filename="kumiko.log",
encoding="utf-8",
mode="w",
maxBytes=max_bytes,
backupCount=5,
)
fmt = logging.Formatter(
fmt="%(asctime)s %(levelname)s\t%(message)s",
datefmt="[%Y-%m-%d %H:%M:%S]",
def _get_formatter(self) -> logging.Formatter:
dt_fmt = "%Y-%m-%d %H:%M:%S"
return logging.Formatter(
"[{asctime}] [{levelname}]\t\t{message}", dt_fmt, style="{"
)
handler.setFormatter(fmt)
self.log.addHandler(handler)
discord.utils.setup_logging(formatter=fmt)

def _disable_watchfiles_logger(self) -> None:
watchfiles = logging.getLogger("watchfiles")

watchfiles.propagate = False
watchfiles.addHandler(logging.NullHandler())

def __enter__(self) -> None:
discord_logger = logging.getLogger("discord")

root = logging.getLogger("kumiko")

handler = logging.StreamHandler()
handler.setFormatter(self._get_formatter())

if not is_docker():
file_handler = RotatingFileHandler(
filename="kumiko.log",
encoding="utf-8",
mode="w",
maxBytes=MAX_BYTES,
backupCount=5,
)
file_handler.setFormatter(self._get_formatter())

discord_logger.addHandler(file_handler)
root.addHandler(file_handler)

discord_logger.setLevel(logging.INFO)
discord_logger.addHandler(handler)

root.setLevel(logging.INFO)
root.addHandler(handler)

def __exit__(
self,
exc_type: type[BaseException] | None,
exc: BaseException | None,
traceback: TracebackType | None,
) -> None:
self.log.info("Shutting down...")
handlers = self.log.handlers[:]
root = logging.getLogger("kumiko")

root.info("Shutting down...")
handlers = root.handlers[:]
for hdlr in handlers:
hdlr.close()
self.log.removeHandler(hdlr)
root.removeHandler(hdlr)
22 changes: 11 additions & 11 deletions src/utils/reloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(self, bot: Kumiko, root_path: Path):
self.root_path = root_path
self.logger = self.bot.logger
self._cogs_path = self.root_path / "cogs"
self._libs_path = self.root_path / "libs"
self._utils_path = self.root_path / "utils"

async def reload_or_load_extension(self, module: str) -> None:
try:
Expand All @@ -40,7 +40,7 @@ async def reload_or_load_extension(self, module: str) -> None:
await self.bot.load_extension(module)
self.logger.info("Loaded extension: %s", module)

def reload_lib_modules(self, module: str) -> None:
def reload_utils_modules(self, module: str) -> None:
try:
actual_module = sys.modules[module]
importlib.reload(actual_module)
Expand All @@ -55,31 +55,31 @@ def find_modules_from_path(self, path: str) -> Optional[str]:

def find_true_module(self, module: str) -> str:
parts = module.split(".")
if "libs" in parts:
lib_index = parts.index("libs")
return ".".join(parts[lib_index:])
if "utils" in parts:
utils_index = parts.index("utils")
return ".".join(parts[utils_index:])
cog_index = parts.index("cogs")
return ".".join(parts[cog_index:])

async def reload_cogs_and_libs(self, ctype: Change, true_module: str) -> None:
async def reload_cogs_and_utils(self, ctype: Change, true_module: str) -> None:
if true_module.startswith("cogs"):
if ctype in (Change.modified, Change.added):
await self.reload_or_load_extension(true_module)
elif ctype == Change.deleted:
await self.bot.unload_extension(true_module)
elif true_module.startswith("libs"):
self.logger.info("Reloaded library module: %s", true_module)
self.reload_lib_modules(true_module)
elif true_module.startswith("utils"):
self.logger.info("Reloaded utils module: %s", true_module)
self.reload_utils_modules(true_module)

async def _watch_cogs(self):
async for changes in awatch(self._cogs_path, self._libs_path, recursive=True):
async for changes in awatch(self._cogs_path, self._utils_path, recursive=True):
for ctype, cpath in changes:
module = self.find_modules_from_path(cpath)
if module is None:
continue

true_module = self.find_true_module(module)
await self.reload_cogs_and_libs(ctype, true_module)
await self.reload_cogs_and_utils(ctype, true_module)

def start(self) -> None:
if _HAS_WATCHFILES:
Expand Down
Loading