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


# OpenAI API key
BOT_OPENAI_MODEL=gpt-5.4
BOT_OPENAI_MODEL=gpt-5.5
OPENAI_API_KEY=


Expand Down Expand Up @@ -73,7 +73,6 @@ LOG_MAX_FORMATTERS=50
LOG_LOGGER_TTL_SECONDS=1800
# Timed Log Settings
LOG_ROTATE_WHEN=midnight
LOG_ROTATE_AT_UTC=True


# GW2 configuration
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.14.3-alpine3.23 AS python-base
FROM python:3.14.4-alpine3.23 AS python-base

LABEL Description="DiscordBot"

Expand Down
3 changes: 0 additions & 3 deletions docker-compose-localdb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ services:
discordbot_database:
condition: service_healthy
command: ["sh", "-c", "uv run --frozen --no-sync alembic upgrade head && touch /tmp/alembic_done && sleep infinity"]
deploy:
restart_policy:
delay: 60s
healthcheck:
test: ["CMD", "sh", "-c", "test -f /tmp/alembic_done"]
interval: 5s
Expand Down
6 changes: 0 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ services:
networks:
- postgres_network
command: ["sh", "-c", "uv run --frozen --no-sync alembic upgrade head && touch /tmp/alembic_done && sleep infinity"]
deploy:
restart_policy:
delay: 60s
healthcheck:
test: ["CMD", "sh", "-c", "test -f /tmp/alembic_done"]
interval: 5s
Expand All @@ -37,9 +34,6 @@ services:
depends_on:
discordbot_alembic:
condition: service_healthy
deploy:
restart_policy:
delay: 60s
command: ["uv", "run", "--frozen", "--no-sync", "python", "-m", "src"]

networks:
Expand Down
10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "DiscordBot"
version = "3.0.12"
version = "3.0.13"
description = "A simple Discord bot with OpenAI support and server administration tools"
urls.Repository = "https://github.com/ddc/DiscordBot"
urls.Homepage = "https://ddc.github.io/DiscordBot"
Expand Down Expand Up @@ -36,18 +36,18 @@ dependencies = [
"ddcdatabases[postgres]>=4.0.1",
"discord-py>=2.7.1",
"gTTS>=2.5.4",
"openai>=2.30.0",
"openai>=2.36.0",
"PyNaCl>=1.6.2",
"pythonLogs>=7.0.0",
"pythonLogs>=7.0.1",
"uuid-utils>=0.14.1",
]

[dependency-groups]
dev = [
"coverage>=7.13.5",
"poethepoet>=0.42.1",
"poethepoet>=0.45.0",
"pytest-asyncio>=1.3.0",
"ruff>=0.15.8",
"ruff>=0.15.12",
"testcontainers[postgres]>=4.14.2",
]

Expand Down
1 change: 0 additions & 1 deletion src/bot/cogs/open_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ async def _get_ai_response(self, message: str) -> str:
model=model,
messages=messages,
max_completion_tokens=1000,
temperature=0.7,
)

content = response.choices[0].message.content
Expand Down
2 changes: 1 addition & 1 deletion src/bot/constants/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def _discover_cogs() -> list[str]:
LMGTFY_URL: Final[str] = "https://lmgtfy.com"

# Date and time formatting
DATE_TIME_FORMATTER_STR: Final[str] = "%a %b %m %Y %X"
DATE_TIME_FORMATTER_STR: Final[str] = "%a %b %d %Y %X"
DATE_FORMATTER: Final[str] = "%Y-%m-%d"
TIME_FORMATTER: Final[str] = "%H:%M:%S.%f"

Expand Down
6 changes: 3 additions & 3 deletions src/gw2/cogs/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,16 +239,16 @@ async def limited_guild_fetch(task):
)

# Add account age
days = (api_req_acc["age"] / 60) / 24
created = api_req_acc["created"].split("T", 1)[0]
embed.add_field(
name="Created",
value=chat_formatting.inline(f"{created} ({round(days)} days ago)"),
value=chat_formatting.inline(f"{created}"),
inline=False,
)

embed.set_footer(
icon_url=ctx.bot.user.display_avatar.url, text=f"{bot_utils.get_current_date_time_str_long()} UTC"
icon_url=ctx.bot.user.display_avatar.url,
text=f"{bot_utils.get_current_date_time_str_long()} UTC",
)

# Stop the background typing task
Expand Down
23 changes: 10 additions & 13 deletions tests/unit/bot/cogs/test_open_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ def mock_bot():
@pytest.fixture
def openai_cog(mock_bot):
"""Create an OpenAi cog instance."""
with patch("src.bot.cogs.open_ai.get_bot_settings") as mock_settings, \
patch("src.bot.cogs.open_ai.OpenAI"):
with patch("src.bot.cogs.open_ai.get_bot_settings") as mock_settings, patch("src.bot.cogs.open_ai.OpenAI"):
mock_settings.return_value = MagicMock(openai_api_key="test-key", openai_model="gpt-3.5-turbo")
return OpenAi(mock_bot)

Expand Down Expand Up @@ -80,13 +79,12 @@ class TestOpenAi:

def test_init(self, mock_bot):
"""Test OpenAi cog initialization."""
with patch("src.bot.cogs.open_ai.get_bot_settings") as mock_settings, \
patch("src.bot.cogs.open_ai.OpenAI"):
with patch("src.bot.cogs.open_ai.get_bot_settings") as mock_settings, patch("src.bot.cogs.open_ai.OpenAI"):
mock_settings.return_value = MagicMock(openai_api_key="test-key", openai_model="gpt-3.5-turbo")
cog = OpenAi(mock_bot)
assert cog.bot == mock_bot
assert cog._openai_client is not None
assert hasattr(cog, '_bot_settings')
assert hasattr(cog, "_bot_settings")

@pytest.mark.asyncio
@patch("src.bot.cogs.open_ai.get_bot_settings")
Expand Down Expand Up @@ -155,7 +153,7 @@ async def test_get_ai_response_success(

assert call_args[1]["model"] == "gpt-3.5-turbo"
assert call_args[1]["max_completion_tokens"] == 1000
assert call_args[1]["temperature"] == pytest.approx(0.7)
assert "temperature" not in call_args[1]

# Verify message types and content
messages = call_args[1]["messages"]
Expand Down Expand Up @@ -245,9 +243,7 @@ def test_create_ai_embeds_no_bot_avatar(self, openai_cog, mock_ctx):

@pytest.mark.asyncio
@patch("src.bot.cogs.open_ai.bot_utils.send_embed")
async def test_ai_command_with_different_models(
self, mock_send_embed, openai_cog, mock_ctx, mock_openai_response
):
async def test_ai_command_with_different_models(self, mock_send_embed, openai_cog, mock_ctx, mock_openai_response):
"""Test AI command with different OpenAI models."""
# Test with GPT-4 - set model directly on the cog's stored settings
openai_cog._bot_settings.openai_model = "gpt-4"
Expand Down Expand Up @@ -334,7 +330,7 @@ async def test_get_ai_response_api_parameters(

call_args = mock_client.chat.completions.create.call_args[1]
assert call_args["max_completion_tokens"] == 1000
assert call_args["temperature"] == pytest.approx(0.7)
assert "temperature" not in call_args
assert call_args["model"] == "gpt-3.5-turbo"

@patch("src.bot.cogs.open_ai.bot_utils.get_current_date_time_str_long")
Expand All @@ -352,8 +348,7 @@ async def test_setup_function(self, mock_bot):
"""Test the setup function."""
from src.bot.cogs.open_ai import setup

with patch("src.bot.cogs.open_ai.get_bot_settings") as mock_settings, \
patch("src.bot.cogs.open_ai.OpenAI"):
with patch("src.bot.cogs.open_ai.get_bot_settings") as mock_settings, patch("src.bot.cogs.open_ai.OpenAI"):
mock_settings.return_value = MagicMock(openai_api_key="test-key", openai_model="gpt-3.5-turbo")
await setup(mock_bot)

Expand Down Expand Up @@ -465,7 +460,9 @@ def test_create_ai_embeds_splits_on_newline(self, openai_cog, mock_ctx):
@pytest.mark.asyncio
@patch("src.database.dal.bot.embed_pages_dal.EmbedPagesDal")
@patch("src.bot.cogs.open_ai.get_bot_settings")
async def test_ai_command_pagination(self, mock_get_settings, mock_dal_class, openai_cog, mock_ctx, mock_bot_settings):
async def test_ai_command_pagination(
self, mock_get_settings, mock_dal_class, openai_cog, mock_ctx, mock_bot_settings
):
"""Test AI command uses pagination for long responses."""
mock_get_settings.return_value = mock_bot_settings
mock_dal = MagicMock()
Expand Down
23 changes: 12 additions & 11 deletions tests/unit/bot/tools/test_bot_utils_extra.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ async def test_next_button_callback(self):
interaction.user.id = 42
interaction.response = AsyncMock()

with patch.object(view, '_save_current_page', new_callable=AsyncMock):
with patch.object(view, "_save_current_page", new_callable=AsyncMock):
await view.next_button.callback(interaction)

assert view.current_page == 1
Expand All @@ -693,7 +693,7 @@ async def test_previous_button_callback(self):
interaction.user.id = 42
interaction.response = AsyncMock()

with patch.object(view, '_save_current_page', new_callable=AsyncMock):
with patch.object(view, "_save_current_page", new_callable=AsyncMock):
await view.previous_button.callback(interaction)

assert view.current_page == 1
Expand Down Expand Up @@ -923,7 +923,7 @@ async def test_send_and_save(self, mock_dal_class):
call_args = mock_dal.insert_embed_pages.call_args
assert call_args[0][0] == 111 # message_id
assert call_args[0][1] == 222 # channel_id
assert call_args[0][2] == 42 # author_id
assert call_args[0][2] == 42 # author_id
assert len(call_args[0][3]) == 2 # pages data

@pytest.mark.asyncio
Expand All @@ -945,11 +945,13 @@ async def test_load_from_db_loads_from_database(self, mock_dal_class):
"""Test _load_from_db loads pages from DB when not in memory."""
mock_dal = MagicMock()
page_data = [{"title": "P1", "description": "D1"}, {"title": "P2", "description": "D2"}]
mock_dal.get_embed_pages = AsyncMock(return_value={
"pages": page_data,
"current_page": 1,
"author_id": 42,
})
mock_dal.get_embed_pages = AsyncMock(
return_value={
"pages": page_data,
"current_page": 1,
"author_id": 42,
}
)
mock_dal_class.return_value = mock_dal

view = EmbedPaginatorView() # No pages
Expand Down Expand Up @@ -983,9 +985,7 @@ async def test_load_from_db_record_not_found(self, mock_dal_class):
result = await view._load_from_db(interaction)

assert result is False
interaction.response.send_message.assert_called_once_with(
"This pagination has expired.", ephemeral=True
)
interaction.response.send_message.assert_called_once_with("This pagination has expired.", ephemeral=True)

@pytest.mark.asyncio
@patch("src.database.dal.bot.embed_pages_dal.EmbedPagesDal")
Expand Down Expand Up @@ -1030,6 +1030,7 @@ def mock_dal(self):
mock_db_utils.fetchall = AsyncMock(return_value=[])
mock_db_utils_class.return_value = mock_db_utils
from src.database.dal.bot.embed_pages_dal import EmbedPagesDal

dal = EmbedPagesDal(db_session, log)
dal._mock_db_utils = mock_db_utils
return dal
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/gw2/cogs/test_worlds.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ async def test_next_button_advances_page(self):
interaction.user.id = 42
interaction.response = AsyncMock()

with patch.object(view, '_save_current_page', new_callable=AsyncMock):
with patch.object(view, "_save_current_page", new_callable=AsyncMock):
await view.next_button.callback(interaction)

assert view.current_page == 1
Expand All @@ -508,7 +508,7 @@ async def test_previous_button_goes_back(self):
interaction.user.id = 42
interaction.response = AsyncMock()

with patch.object(view, '_save_current_page', new_callable=AsyncMock):
with patch.object(view, "_save_current_page", new_callable=AsyncMock):
await view.previous_button.callback(interaction)

assert view.current_page == 1
Expand Down
Loading
Loading