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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ BOT_OPENAI_COOLDOWN=10
BOT_OWNER_COOLDOWN=5

# OpenAI API key
BOT_OPENAI_MODEL=gpt-4o-mini
BOT_OPENAI_MODEL=gpt-5.2
OPENAI_API_KEY=

# ddcDatabases configs
Expand Down
33 changes: 23 additions & 10 deletions .github/PULL_REQUEST_TEMPLATE
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
## Summary
<!-- Brief description of what this PR does and why -->

<!-- What is this pull request for? Does it fix any issues? -->
## Changes Made
<!-- List the key changes -->

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change (existing functionality affected)
- [ ] Refactoring (no functional changes)
- [ ] Documentation
- [ ] CI/CD or build configuration
- [ ] Dependencies update

## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] All existing tests pass
- [ ] Manual testing performed

## Checklist
- [ ] If code changes were made, then they have been tested
- [ ] I have updated the documentation to reflect any changes made
- [ ] I have thought about how this code may affect other services
- [ ] This PR fixes an issue
- [ ] This PR is a breaking change (e.g. method, parameters, env variables)
- [ ] This PR adds something new (e.g. method, parameters, env variables)
- [ ] This PR change unit and integration tests
- [ ] This PR is **NOT** a code change (e.g. documentation, packages)
- [ ] Code follows the project's style and conventions
- [ ] Documentation updated (if applicable)
- [ ] No new warnings or linter errors introduced
- [ ] I have considered how this change may affect other services

## Reviewer
- [ ] I understand that approving this code, I am also responsible for it going into the codebase
- [ ] I understand that by approving this PR, I share responsibility for these changes
6 changes: 4 additions & 2 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
with:
timeout_minutes: 2
max_attempts: 3
command: uv run --no-sync pytest tests/unit
command: uv run --no-sync coverage run -m pytest tests/unit && uv run --no-sync coverage report && uv run --no-sync coverage xml
shell: bash

- name: Upload coverage to Codecov
Expand Down Expand Up @@ -75,7 +75,7 @@ jobs:
with:
timeout_minutes: 3
max_attempts: 3
command: uv run --no-sync pytest tests/integration --no-cov
command: uv run --no-sync pytest tests/integration
shell: bash

docker:
Expand All @@ -86,6 +86,8 @@ jobs:

- name: Lint Dockerfile
uses: hadolint/hadolint-action@v3.1.0
with:
config: .hadolint.yml

- name: Validate compose file
run: |
Expand Down
2 changes: 2 additions & 0 deletions .hadolint.yaml → .hadolint.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
ignored:
- DL3003 # Use WORKDIR to switch to a directory (cd is correct inside RUN for source builds)
- DL3013 # Pin versions in pip install (we use uv, not pip)
- DL3018 # Pin versions in apk add (Alpine packages aren't version-pinnable)
- DL3044 # Env var reference in same ENV where defined (intentional for PYTHONPATH)
- DL3048 # Invalid label key (we use Description, not OCI labels)
- DL4006 # Set SHELL pipefail (Alpine uses ash, not bash)
- SC2046 # Quote to prevent word splitting (nproc returns a single number)
- SC2086 # Double quote variables (not needed in controlled Dockerfile context)
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ services:
DOCKER_BUILDKIT: 1
networks:
- postgres_network
command: ["sh", "-c", "uv run --frozen alembic upgrade head && touch /tmp/alembic_done && sleep infinity"]
command: ["sh", "-c", "uv run --frozen --no-sync alembic upgrade head && touch /tmp/alembic_done && sleep infinity"]
deploy:
restart_policy:
delay: 60s
Expand Down Expand Up @@ -40,7 +40,7 @@ services:
deploy:
restart_policy:
delay: 60s
command: ["uv", "run", "--frozen", "python", "-m", "src"]
command: ["uv", "run", "--frozen", "--no-sync", "python", "-m", "src"]

networks:
postgres_network:
Expand Down
37 changes: 18 additions & 19 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[project]
name = "DiscordBot"
version = "3.0.3"
version = "3.0.4"
description = "A simple Discord bot with OpenAI support and server administration tools"
urls.Repository = "https://github.com/ddc/DiscordBot"
urls.Homepage = "https://github.com/ddc/DiscordBot"
license = { text = "MIT" }
readme = "README.md"
authors = [
{ name = "Daniel Costa", email = "ddcsoftwares@proton.me" },
{ name = "Daniel Costa", email = "daniel@ddcsoftwares.com" },
]
maintainers = [
{ name = "Daniel Costa" },
Expand Down Expand Up @@ -36,42 +36,41 @@ dependencies = [
"ddcdatabases[postgres]>=3.0.10",
"discord-py>=2.6.4",
"gTTS>=2.5.4",
"openai>=2.21.0",
"openai>=2.23.0",
"PyNaCl>=1.6.2",
"pythonLogs>=6.0.2",
]

[dependency-groups]
dev = [
"pytest-asyncio>=1.3.0",
"pytest-cov>=7.0.0",
"testcontainers[postgres]>=4.14.1",
"poethepoet>=0.41.0",
"ruff>=0.15.1",
"coverage>=7.13.4",
"poethepoet>=0.42.0",
"pytest-asyncio>=1.3.0",
"ruff>=0.15.2",
"testcontainers[postgres]>=4.14.1",
]

[tool.poe.tasks]
linter.shell = "uv run ruff check --fix . && uv run ruff format ."
profile = "uv run python -m cProfile -o cprofile_unit.prof -m pytest tests/unit --no-cov"
profile-integration = "uv run python -m cProfile -o cprofile_integration.prof -m pytest tests/integration --no-cov"
test = "uv run pytest tests/unit"
test-integration = "uv run pytest tests/integration --no-cov"
hadolint.shell = "docker run --rm -i -v $(pwd)/.hadolint.yaml:/.config/hadolint.yaml:ro hadolint/hadolint < Dockerfile"
test-docker = "uv run pytest tests/docker -v --no-cov"
migration = "uv run --frozen alembic upgrade head"
tests.sequence = ["test", "test-integration", "hadolint", "test-docker"]
profile = "uv run python -m cProfile -o cprofile_unit.prof -m pytest tests/unit"
profile-integration = "uv run python -m cProfile -o cprofile_integration.prof -m pytest tests/integration"
test.sequence = [{ shell = "uv run coverage run -m pytest tests/unit" }, { shell = "uv run coverage report" }, { shell = "uv run coverage xml" }]
test-integration = "uv run pytest tests/integration"
hadolint.shell = "docker run --rm -i -v $(pwd)/.hadolint.yml:/.config/hadolint.yml:ro hadolint/hadolint < Dockerfile"
test-docker = "uv run pytest tests/docker"
tests.sequence = ["linter", "hadolint", "test-docker", "test", "test-integration"]
updatedev.sequence = ["linter", {shell = "uv lock --upgrade && uv sync --all-extras --group dev"}]
migration = "uv run --frozen alembic upgrade head"

[tool.pytest.ini_options]
addopts = "-v --import-mode=importlib --cov --cov-report=term --cov-report=xml --junitxml=junit.xml"
addopts = "-v --import-mode=importlib --junitxml=junit.xml"
junit_family = "legacy"
pythonpath = ["."]
testpaths = ["tests/unit"]
asyncio_mode = "strict"
asyncio_default_fixture_loop_scope = "function"
markers = [
"integration: marks tests as integration tests",
"docker: Docker and compose file tests",
"gw2_api: marks tests that require GW2 API access",
]
[tool.coverage.run]
omit = [
Expand Down
2 changes: 1 addition & 1 deletion src/bot/cogs/dice_rolls.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def _create_roll_embed(author: discord.Member, message_parts: list[str]) -> disc
"""Create embed for roll result."""
description = "\n".join(message_parts)
embed = discord.Embed(description=description, color=discord.Color.red())
embed.set_author(name=author.display_name, icon_url=author.avatar.url)
embed.set_author(name=author.display_name, icon_url=author.display_avatar.url)
return embed

@staticmethod
Expand Down
12 changes: 8 additions & 4 deletions src/bot/cogs/events/on_command_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ def build_command_invoke_error(context: ErrorContext) -> str:
"""Build message for command invoke error."""
error_conditions = {
("Cannot send messages to this user", "status code: 403"): messages.DIRECT_MESSAGES_DISABLED,
("AttributeError",): f"{messages.COMMAND_ERROR}: `{context.command}`",
("Missing Permissions",): f"{messages.NO_PERMISSION_EXECUTE_COMMAND}: `{context.command}`",
(
"NoOptionError",
Expand All @@ -111,7 +110,7 @@ def build_command_invoke_error(context: ErrorContext) -> str:
base_msg = message
break
else:
base_msg = f"{messages.COMMAND_INTERNAL_ERROR}: `{context.command}`"
base_msg = f"{messages.COMMAND_ERROR}: `{context.command}`\n{context.error_msg}"

return f"{base_msg}\n{messages.HELP_COMMAND_MORE_INFO}: `{context.help_command}`"

Expand Down Expand Up @@ -154,14 +153,18 @@ async def on_command_error(ctx: commands.Context, error: Exception) -> None:
await handler(context, should_log)

@staticmethod
async def _send_error_message(ctx: commands.Context, error_msg: str, should_log: bool) -> None:
async def _send_error_message(
ctx: commands.Context, error_msg: str, should_log: bool, original_error: Exception | None = None
) -> None:
"""Send an error message to user and optionally log it."""
await bot_utils.send_error_msg(ctx, error_msg)
if should_log:
log_msg = f"({error_msg}) ({ctx.message.content}) ({ctx.message.author})"
if ctx.guild is not None:
log_msg += f"(Server[{ctx.guild.name}:{ctx.guild.id}])"
log_msg += f"(Channel[{ctx.message.channel}:{ctx.message.channel.id}])"
if original_error is not None:
log_msg += f"(Error[{original_error}])"
ctx.bot.log.error(log_msg)

async def _handle_no_private_message(self, context: ErrorContext, should_log: bool) -> None:
Expand Down Expand Up @@ -206,7 +209,8 @@ async def _handle_command_error(self, context: ErrorContext, should_log: bool) -
async def _handle_command_invoke_error(self, context: ErrorContext, should_log: bool) -> None:
"""Handle CommandInvokeError."""
error_msg = self.message_builder.build_command_invoke_error(context)
await self._send_error_message(context.ctx, error_msg, should_log)
original = getattr(context.error, "original", context.error)
await self._send_error_message(context.ctx, error_msg, should_log, original)

async def _handle_command_on_cooldown(self, context: ErrorContext, should_log: bool) -> None:
"""Handle CommandOnCooldown error."""
Expand Down
6 changes: 3 additions & 3 deletions src/bot/cogs/events/on_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ async def _censor_message(self, ctx: commands.Context, user_msg: str) -> None:

# Send censorship notification
embed = discord.Embed(title="", color=discord.Color.red(), description=messages.MESSAGE_CENSURED)
embed.set_author(name=ctx.message.author.display_name, icon_url=ctx.message.author.avatar.url)
embed.set_author(name=ctx.message.author.display_name, icon_url=ctx.message.author.display_avatar.url)

try:
await ctx.message.channel.send(embed=embed)
Expand Down Expand Up @@ -161,7 +161,7 @@ async def _send_reaction_message(message: discord.Message, response: str) -> Non
await message.channel.typing()
description = f"{messages.BOT_REACT_EMOJIS}\n{chat_formatting.inline(response)}"
embed = discord.Embed(color=discord.Color.red(), description=description)
embed.set_author(name=message.author.display_name, icon_url=message.author.avatar.url)
embed.set_author(name=message.author.display_name, icon_url=message.author.display_avatar.url)
await message.channel.send(embed=embed)


Expand Down Expand Up @@ -311,7 +311,7 @@ async def _handle_invisible_member(self, ctx: commands.Context) -> None:
color=discord.Color.red(),
description=chat_formatting.error_inline(message_text),
)
embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar.url)
embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.display_avatar.url)

# Try to send DM, fall back to channel, then mention
try:
Expand Down
11 changes: 6 additions & 5 deletions src/bot/cogs/events/on_user_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ async def on_user_update(self, before, after):

msg = f"{messages.PROFILE_CHANGES}:\n\n"
embed = bot_utils.get_embed(self)
embed.set_author(name=after.display_name, icon_url=after.avatar.url)
embed.set_author(name=after.display_name, icon_url=after.display_avatar.url)
embed.set_footer(
icon_url=self.bot.user.avatar.url,
icon_url=self.bot.user.display_avatar.url,
text=f"{bot_utils.get_current_date_time_str_long()} UTC",
)

if str(before.avatar.url) != str(after.avatar.url):
embed.set_thumbnail(url=after.avatar.url)
if before.avatar != after.avatar:
if after.avatar:
embed.set_thumbnail(url=after.avatar.url)
embed.add_field(name=messages.NEW_AVATAR, value="-->")
msg += f"{messages.NEW_AVATAR}: \n{after.avatar.url}\n"
msg += f"{messages.NEW_AVATAR}: \n{after.display_avatar.url}\n"

if str(before.name) != str(after.name):
if before.name is not None:
Expand Down
2 changes: 1 addition & 1 deletion src/bot/cogs/open_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ async def _get_ai_response(self, message: str) -> str:
response = self.openai_client.chat.completions.create(
model=model,
messages=messages,
max_tokens=1000,
max_completion_tokens=1000,
temperature=0.7,
)

Expand Down
Loading