Skip to content

3.0#9

Open
m-xim wants to merge 22 commits into
developfrom
dev3
Open

3.0#9
m-xim wants to merge 22 commits into
developfrom
dev3

Conversation

@m-xim
Copy link
Copy Markdown
Owner

@m-xim m-xim commented May 18, 2026


Open in Devin Review

Summary by CodeRabbit

  • New Features

    • Introduced new Route class for flexible webhook URL configuration with path parameters and query support
    • Added SingleBotEngine and enhanced TokenEngine for improved bot management
    • Implemented new web adapter abstraction supporting FastAPI and aiohttp frameworks
    • Added comprehensive error handling with specialized exception types
  • Documentation

    • Completely rewrote README with updated quick-start examples and simplified feature overview
    • Updated CONTRIBUTING.md with new project structure and scope guidelines
  • Refactor

    • Reorganized package structure: routingroute, adaptersweb
    • Modernized security verification workflow with improved error reporting
  • Chores

    • Added code coverage collection to test pipeline

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ae300d2d-a6f0-4946-9057-09a58cb41088

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev3

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@m-xim m-xim requested a review from Copilot May 18, 2026 21:08
@codecov-commenter
Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
src/aiogram_webhook/engines/multi.py (1)

29-29: 💤 Low value

Redundant assignment before super().init.

self.route is set here, but super().__init__ also sets self.route = route. The assignment on this line can be removed.

♻️ Proposed fix
     def __init__(
         self,
         dispatcher,
         /,
         *,
         web: WebAdapter[AppT, RawRequestT, FrameworkResponseT],
         route: Route,
         security: Security | None = None,
         handle_in_background: bool = True,
     ) -> None:
 
-        self.route = route
         self._task_trackers: dict[int, TaskTracker] = {}
         self._bots: dict[int, Bot] = {}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aiogram_webhook/engines/multi.py` at line 29, Remove the redundant
assignment to self.route prior to calling super().__init__ in the constructor of
the class in multi.py: the constructor currently sets self.route = route and
then calls super().__init__(...) which also sets self.route, so delete the
earlier standalone assignment (the symbol to locate is the self.route assignment
in the class __init__ and the super().__init__ call). Ensure only the
super().__init__ is responsible for initializing self.route to avoid duplicate
work.
src/aiogram_webhook/tasks.py (1)

46-52: ⚡ Quick win

Avoid unbounded wait after cancellation in close().

After timeout-triggered cancellation, awaiting gather(*pending) can still hang forever if a task ignores/swallows cancellation. Add a bounded second wait and log stubborn tasks instead of blocking shutdown indefinitely.

Proposed change
         if pending:
             logger.warning("Timeout reached. Cancelling %s pending tasks.", len(pending))
             for task in pending:
                 task.cancel()

-            # Wait for cancellations to process
-            await asyncio.gather(*pending, return_exceptions=True)
+            # Wait for cancellations to process, but do not block forever.
+            _, still_pending = await asyncio.wait(
+                pending,
+                timeout=timeout,
+                return_when=asyncio.ALL_COMPLETED,
+            )
+            if still_pending:
+                logger.error("Failed to cancel %s background tasks within timeout.", len(still_pending))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aiogram_webhook/tasks.py` around lines 46 - 52, In close() after
cancelling pending tasks (the block that logs "Timeout reached. Cancelling %s
pending tasks." and calls task.cancel()), add a bounded second wait instead of a
raw await asyncio.gather(*pending, return_exceptions=True): call asyncio.wait or
asyncio.wait_for with a short timeout (e.g., a few seconds) to avoid hanging if
a task swallows CancelledError, then log any tasks that did not finish as
"stubborn" including their repr/len(pending) (use the same pending variable and
task objects) and optionally cancel again or set task.cancel() + create_task to
ensure shutdown proceeds; ensure you still use return_exceptions=True when
awaiting to avoid raising exceptions during the bounded wait and preserve
existing logging context from logger.
src/aiogram_webhook/web/aiohttp.py (1)

4-4: ⚡ Quick win

Avoid exposing aiohttp private _SENTINEL in adapter typing.

Using _SENTINEL ties this module to aiohttp internals and makes upgrades brittle.

Suggested fix
-from aiohttp.helpers import _SENTINEL, sentinel
+from aiohttp.helpers import sentinel
@@
-    def json_response(
-        self, status_code: int, data: Mapping[str, str] | _SENTINEL = sentinel, headers: Mapping[str, str] | None = None
-    ) -> Response:
+    def json_response(
+        self, status_code: int, data: Any = sentinel, headers: Mapping[str, str] | None = None
+    ) -> Response:
         return json_response(status=status_code, data=data, headers=headers)

Also applies to: 79-80

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aiogram_webhook/web/aiohttp.py` at line 4, The import exposes aiohttp's
private _SENTINEL which couples this adapter to aiohttp internals; remove the
private import and stop referencing _SENTINEL in the module (including usages
around the current sentinel handling at the lines noted ~79-80). Replace uses of
_SENTINEL with the public aiohttp.helpers.sentinel (already imported as
sentinel) or with a local sentinel/typing.Any/Optional sentinel placeholder in
type hints so the adapter relies only on public API; update any function/type
annotations or default parameter checks that reference _SENTINEL to use the
public sentinel or a local equivalent (e.g., named constant) instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/aiogram_webhook/__init__.py`:
- Around line 10-15: The optional import block around FastAPIAdapter currently
catches all ImportError and can hide real bugs inside
aiogram_webhook.web.fastapi; change the except clause to catch
ModuleNotFoundError only so only missing optional dependency errors are
suppressed (leave the import and the __all__.insert("FastAPIAdapter") logic
as-is and do not swallow other exceptions raised from the module).

In `@src/aiogram_webhook/configs/webhook.py`:
- Around line 16-17: Add bounds validation for the max_connections field in the
webhook config dataclass by implementing a __post_init__ on the dataclass (or
extending the existing __post_init__) that checks if max_connections is not None
and then ensures 1 <= max_connections <= 100, raising a ValueError with a clear
message if the check fails; reference the max_connections attribute and the
dataclass's __post_init__ to locate where to insert the guard so invalid values
are rejected at construction rather than later when calling the Telegram API.

In `@src/aiogram_webhook/route/query.py`:
- Around line 76-82: The loop that builds values currently treats any
non-str/int item as a QueryValue by casting (values, items, QueryValue, Const),
which hides bad input and causes AttributeError later; change the normalization
in the items loop to explicitly accept only str, int or actual QueryValue
instances (use isinstance(item, QueryValue) or equivalent) and immediately raise
a clear TypeError/ValueError with a descriptive message identifying the
offending item when encountering any other type, so invalid config is rejected
early.

In `@src/aiogram_webhook/route/route.py`:
- Around line 59-63: The code computes self._path_params using
self._path_param_names and self._params before calling self._validate_config(),
which can cause a raw KeyError instead of raising
MissingRouteParamDeclarationError; fix by validating configuration first (call
self._validate_config() before building self._path_params and self._query_items)
or alternatively guard the lookup in the tuple comprehension to detect missing
names and raise MissingRouteParamDeclarationError explicitly (refer to symbols:
self._validate_config, self._path_params, self._path_param_names, self._params,
MissingRouteParamDeclarationError).

In `@src/aiogram_webhook/web/aiohttp.py`:
- Around line 35-38: In client_ip, guard against request.transport being None
and against get_extra_info returning a non-tuple/None: first check
self._request.transport is not None (cast to Transport only after the
None-check), call get_extra_info("peername") only if transport exists, verify
peername is a sequence/tuple with at least one element and that the first
element is a string (or convertible) before returning peername[0]; otherwise
return None. Update references to Transport and self._request.transport in
client_ip to implement these defensive checks.

In `@src/aiogram_webhook/web/fastapi.py`:
- Around line 61-67: FastAPIAdapter.register currently ignores the on_startup
and on_shutdown parameters (they are marked unused), so lifecycle callbacks
never run; update the register method to register these with the FastAPI app by
calling app.add_event_handler("startup", on_startup) and
app.add_event_handler("shutdown", on_shutdown) after creating/initializing app
and before adding the route, mirroring AiohttpAdapter behavior; locate the
register method in FastAPIAdapter and add those two calls using the on_startup
and on_shutdown symbols so engine lifecycle logic is executed.

---

Nitpick comments:
In `@src/aiogram_webhook/engines/multi.py`:
- Line 29: Remove the redundant assignment to self.route prior to calling
super().__init__ in the constructor of the class in multi.py: the constructor
currently sets self.route = route and then calls super().__init__(...) which
also sets self.route, so delete the earlier standalone assignment (the symbol to
locate is the self.route assignment in the class __init__ and the
super().__init__ call). Ensure only the super().__init__ is responsible for
initializing self.route to avoid duplicate work.

In `@src/aiogram_webhook/tasks.py`:
- Around line 46-52: In close() after cancelling pending tasks (the block that
logs "Timeout reached. Cancelling %s pending tasks." and calls task.cancel()),
add a bounded second wait instead of a raw await asyncio.gather(*pending,
return_exceptions=True): call asyncio.wait or asyncio.wait_for with a short
timeout (e.g., a few seconds) to avoid hanging if a task swallows
CancelledError, then log any tasks that did not finish as "stubborn" including
their repr/len(pending) (use the same pending variable and task objects) and
optionally cancel again or set task.cancel() + create_task to ensure shutdown
proceeds; ensure you still use return_exceptions=True when awaiting to avoid
raising exceptions during the bounded wait and preserve existing logging context
from logger.

In `@src/aiogram_webhook/web/aiohttp.py`:
- Line 4: The import exposes aiohttp's private _SENTINEL which couples this
adapter to aiohttp internals; remove the private import and stop referencing
_SENTINEL in the module (including usages around the current sentinel handling
at the lines noted ~79-80). Replace uses of _SENTINEL with the public
aiohttp.helpers.sentinel (already imported as sentinel) or with a local
sentinel/typing.Any/Optional sentinel placeholder in type hints so the adapter
relies only on public API; update any function/type annotations or default
parameter checks that reference _SENTINEL to use the public sentinel or a local
equivalent (e.g., named constant) instead.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1ca9eca-c119-4594-9628-f3a902a84af9

📥 Commits

Reviewing files that changed from the base of the PR and between 8ef42bf and 4b98ec9.

📒 Files selected for processing (57)
  • .github/workflows/tests.yml
  • CONTRIBUTING.md
  • README.md
  • pyproject.toml
  • src/aiogram_webhook/__init__.py
  • src/aiogram_webhook/adapters/aiohttp/adapter.py
  • src/aiogram_webhook/adapters/aiohttp/mapping.py
  • src/aiogram_webhook/adapters/base_adapter.py
  • src/aiogram_webhook/adapters/base_mapping.py
  • src/aiogram_webhook/adapters/fastapi/adapter.py
  • src/aiogram_webhook/adapters/fastapi/mapping.py
  • src/aiogram_webhook/config/__init__.py
  • src/aiogram_webhook/configs/__init__.py
  • src/aiogram_webhook/configs/bot.py
  • src/aiogram_webhook/configs/webhook.py
  • src/aiogram_webhook/engines/__init__.py
  • src/aiogram_webhook/engines/base.py
  • src/aiogram_webhook/engines/errors.py
  • src/aiogram_webhook/engines/multi.py
  • src/aiogram_webhook/engines/simple.py
  • src/aiogram_webhook/engines/single.py
  • src/aiogram_webhook/engines/target.py
  • src/aiogram_webhook/engines/token.py
  • src/aiogram_webhook/errors.py
  • src/aiogram_webhook/logs.py
  • src/aiogram_webhook/py.typed
  • src/aiogram_webhook/route/__init__.py
  • src/aiogram_webhook/route/errors.py
  • src/aiogram_webhook/route/params.py
  • src/aiogram_webhook/route/query.py
  • src/aiogram_webhook/route/route.py
  • src/aiogram_webhook/routing/__init__.py
  • src/aiogram_webhook/routing/base.py
  • src/aiogram_webhook/routing/path.py
  • src/aiogram_webhook/routing/query.py
  • src/aiogram_webhook/routing/static.py
  • src/aiogram_webhook/security/checks/check.py
  • src/aiogram_webhook/security/checks/ip.py
  • src/aiogram_webhook/security/errors.py
  • src/aiogram_webhook/security/secret_token.py
  • src/aiogram_webhook/security/security.py
  • src/aiogram_webhook/tasks.py
  • src/aiogram_webhook/utils/__init__.py
  • src/aiogram_webhook/utils/config.py
  • src/aiogram_webhook/utils/webhook_payload.py
  • src/aiogram_webhook/web/__init__.py
  • src/aiogram_webhook/web/aiohttp.py
  • src/aiogram_webhook/web/base.py
  • src/aiogram_webhook/web/fastapi.py
  • tests/fixtures/__init__.py
  • tests/fixtures/fixtures_bound_request.py
  • tests/fixtures/fixtures_checks.py
  • tests/test_ip_check.py
  • tests/test_route.py
  • tests/test_routing.py
  • tests/test_secret_token.py
  • tests/test_security.py
💤 Files with no reviewable changes (15)
  • tests/fixtures/fixtures_bound_request.py
  • src/aiogram_webhook/routing/base.py
  • src/aiogram_webhook/adapters/base_adapter.py
  • src/aiogram_webhook/adapters/fastapi/adapter.py
  • src/aiogram_webhook/engines/simple.py
  • src/aiogram_webhook/adapters/base_mapping.py
  • src/aiogram_webhook/engines/init.py
  • tests/test_routing.py
  • src/aiogram_webhook/routing/init.py
  • src/aiogram_webhook/adapters/fastapi/mapping.py
  • src/aiogram_webhook/adapters/aiohttp/adapter.py
  • src/aiogram_webhook/routing/static.py
  • src/aiogram_webhook/routing/query.py
  • src/aiogram_webhook/routing/path.py
  • src/aiogram_webhook/adapters/aiohttp/mapping.py

Comment thread src/aiogram_webhook/__init__.py Outdated
Comment thread src/aiogram_webhook/configs/webhook.py Outdated
Comment on lines +76 to +82
values: list[QueryValue] = []
for item in items:
if isinstance(item, str | int):
values.append(Const(item))
else:
values.append(cast("QueryValue", item))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject unsupported query item types during normalization.

Line 81 blindly casts unknown objects to QueryValue; invalid inputs then fail later with AttributeError instead of a config error.

Suggested fix
         values: list[QueryValue] = []
         for item in items:
-            if isinstance(item, str | int):
+            if isinstance(item, str | int):
                 values.append(Const(item))
+            elif isinstance(item, QueryValue):
+                values.append(item)
             else:
-                values.append(cast("QueryValue", item))
+                raise RouteConfigError(
+                    f"Invalid Route config: unsupported query value type for param {name!r}: {type(item).__name__}."
+                )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
values: list[QueryValue] = []
for item in items:
if isinstance(item, str | int):
values.append(Const(item))
else:
values.append(cast("QueryValue", item))
values: list[QueryValue] = []
for item in items:
if isinstance(item, str | int):
values.append(Const(item))
elif isinstance(item, QueryValue):
values.append(item)
else:
raise RouteConfigError(
f"Invalid Route config: unsupported query value type for param {name!r}: {type(item).__name__}."
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aiogram_webhook/route/query.py` around lines 76 - 82, The loop that
builds values currently treats any non-str/int item as a QueryValue by casting
(values, items, QueryValue, Const), which hides bad input and causes
AttributeError later; change the normalization in the items loop to explicitly
accept only str, int or actual QueryValue instances (use isinstance(item,
QueryValue) or equivalent) and immediately raise a clear TypeError/ValueError
with a descriptive message identifying the offending item when encountering any
other type, so invalid config is rejected early.

Comment thread src/aiogram_webhook/route/route.py Outdated
Comment thread src/aiogram_webhook/web/aiohttp.py
Comment thread src/aiogram_webhook/web/fastapi.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 52 out of 57 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (1)

src/aiogram_webhook/configs/webhook.py:16

  • The previous model enforced Telegram's max_connections range, but the dataclass now accepts any integer even though the field documentation still says 1-100. Reintroduce validation (for example in __post_init__ or before set_webhook) so invalid configuration fails locally instead of being sent to Telegram.

Comment thread src/aiogram_webhook/engines/token.py Outdated
Comment thread src/aiogram_webhook/tasks.py
Comment thread src/aiogram_webhook/web/aiohttp.py Outdated
Comment thread src/aiogram_webhook/web/fastapi.py
Comment thread src/aiogram_webhook/security/errors.py
overrides["allowed_updates"] = allowed_updates
return self.webhook_config.model_copy(update=overrides) if overrides else self.webhook_config
def _build_lifecycle_data(self, *, app: AppT, **kwargs) -> dict[str, Any]:
return {"app": app, "dispatcher": self.dispatcher, **kwargs}
Comment thread src/aiogram_webhook/engines/token.py Outdated
devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/aiogram_webhook/route/query.py (1)

70-85: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast on unsupported query input types.

Line 70-85 still accepts invalid query input shapes/types: non-iterable unsupported values can throw raw TypeError, and unsupported iterable items get coerced via Const(item) instead of raising a route config error.

Suggested fix
-        if not value:
+        if not isinstance(value, (list, tuple)):
+            raise RouteConfigError(
+                f"Invalid Route config: unsupported query input type for param {name!r}: {type(value).__name__}."
+            )
+
+        if not value:
             raise RouteConfigError(
                 f"Invalid Route config: query param must contain at least one value. Param: {name!r}."
             )
@@
         values: list[QueryValue] = []
         for item in value:
             if isinstance(item, QueryValue):
                 values.append(item)
+            elif isinstance(item, (str, int)):
+                values.append(Const(item))
             else:
-                values.append(Const(item))
+                raise RouteConfigError(
+                    f"Invalid Route config: unsupported query value type for param {name!r}: {type(item).__name__}."
+                )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aiogram_webhook/route/query.py` around lines 70 - 85, The code accepts
unsupported query input shapes and silently coerces unexpected iterable items;
update the block that handles `value` so it fail-fast: if `value` is a str or
int keep current `normalized[name] = (Const(value),)`, otherwise if `value` is
not a proper iterable (e.g., not list/tuple/set) raise `RouteConfigError` with a
clear message, and when iterating `for item in value` only accept `QueryValue`
or supported primitive types (str/int) — if an item is unsupported, raise
`RouteConfigError` instead of wrapping it with `Const(item)`. Use the existing
symbols `RouteConfigError`, `QueryValue`, `Const`, and `normalized[name]` to
locate and change the logic.
🧹 Nitpick comments (1)
src/aiogram_webhook/engines/single.py (1)

17-28: ⚡ Quick win

Make bot session ownership explicit before closing it on shutdown.

SingleBotEngine receives an external Bot instance but unconditionally closes its session during shutdown. This contradicts TokenBotEngine, which checks session ownership before closing. If the bot's session is managed externally, this will unexpectedly tear down a shared resource.

Add a close_bot_session parameter (default False) to control whether the session is closed during shutdown, matching the ownership-aware pattern already established in TokenBotEngine.

Suggested patch
 class SingleBotEngine(
     BaseWebhookEngine[AppT, RawRequestT, FrameworkResponseT], Generic[AppT, RawRequestT, FrameworkResponseT]
 ):
     def __init__(
         self,
         dispatcher,
         bot: Bot,
         /,
         *,
         web: WebAdapter[AppT, RawRequestT, FrameworkResponseT],
         route: Route,
         security=None,
         webhook_config=None,
         handle_in_background: bool = True,
+        close_bot_session: bool = False,
     ) -> None:
         self.bot = bot
         self._task_tracker = TaskTracker()
+        self._close_bot_session = close_bot_session
 
         super().__init__(
             dispatcher,
             web=web,
             route=route,
@@
     async def on_shutdown(self, app: AppT, *args, **kwargs) -> None:  # noqa: ARG002
         logger.info("Stopping single-bot webhook engine for bot %s", self.bot.id)
         await self._task_tracker.close()
@@
-        if self.bot.session is not None:
+        if self._close_bot_session and self.bot.session is not None:
             await self.bot.session.close()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aiogram_webhook/engines/single.py` around lines 17 - 28,
SingleBotEngine's __init__ currently takes an external Bot but always closes its
session on shutdown; add a close_bot_session: bool = False parameter to __init__
(store it as self._close_bot_session) so ownership is explicit. Update the
shutdown method in SingleBotEngine to only await self._bot.session.close() when
self._close_bot_session is True (mirroring TokenBotEngine's ownership check) and
ensure any existing logic that references closing the bot session uses this flag
instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/aiogram_webhook/route/query.py`:
- Around line 70-85: The code accepts unsupported query input shapes and
silently coerces unexpected iterable items; update the block that handles
`value` so it fail-fast: if `value` is a str or int keep current
`normalized[name] = (Const(value),)`, otherwise if `value` is not a proper
iterable (e.g., not list/tuple/set) raise `RouteConfigError` with a clear
message, and when iterating `for item in value` only accept `QueryValue` or
supported primitive types (str/int) — if an item is unsupported, raise
`RouteConfigError` instead of wrapping it with `Const(item)`. Use the existing
symbols `RouteConfigError`, `QueryValue`, `Const`, and `normalized[name]` to
locate and change the logic.

---

Nitpick comments:
In `@src/aiogram_webhook/engines/single.py`:
- Around line 17-28: SingleBotEngine's __init__ currently takes an external Bot
but always closes its session on shutdown; add a close_bot_session: bool = False
parameter to __init__ (store it as self._close_bot_session) so ownership is
explicit. Update the shutdown method in SingleBotEngine to only await
self._bot.session.close() when self._close_bot_session is True (mirroring
TokenBotEngine's ownership check) and ensure any existing logic that references
closing the bot session uses this flag instead.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 87a7f539-48c6-4d3c-9279-0d4fea776235

📥 Commits

Reviewing files that changed from the base of the PR and between 4b98ec9 and 86666f8.

📒 Files selected for processing (7)
  • src/aiogram_webhook/configs/webhook.py
  • src/aiogram_webhook/engines/base.py
  • src/aiogram_webhook/engines/single.py
  • src/aiogram_webhook/engines/token.py
  • src/aiogram_webhook/route/query.py
  • src/aiogram_webhook/route/route.py
  • src/aiogram_webhook/web/aiohttp.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/aiogram_webhook/configs/webhook.py
  • src/aiogram_webhook/engines/token.py
  • src/aiogram_webhook/engines/base.py
  • src/aiogram_webhook/web/aiohttp.py

devin-ai-integration[bot]

This comment was marked as resolved.

@m-xim m-xim requested a review from Copilot May 19, 2026 06:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants