Skip to content

[client] add device management, settings, logs, and health check methods#40

Merged
capcom6 merged 1 commit into
masterfrom
client/add-new-methods
Apr 21, 2026
Merged

[client] add device management, settings, logs, and health check methods#40
capcom6 merged 1 commit into
masterfrom
client/add-new-methods

Conversation

@capcom6
Copy link
Copy Markdown
Member

@capcom6 capcom6 commented Apr 14, 2026

Summary by CodeRabbit

  • New Features

    • Rebranded to SMSGate Python API Client. Added message listing/export, device listing/removal, device settings CRUD, logs retrieval, multiple health probes, token refresh, richer send options, expanded message/state/webhook schemas, and several new enums.
  • Documentation

    • README and docs reorganized: updated Quickstart and examples, reorganized Available Methods, expanded Data Structures and Enums, and updated usage examples to reflect response handling.

@capcom6
Copy link
Copy Markdown
Member Author

capcom6 commented Apr 14, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

Warning

Rate limit exceeded

@capcom6 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 58 minutes and 28 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 58 minutes and 28 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 00a6c7a2-9a9d-44f2-9d45-469357a8ba3b

📥 Commits

Reviewing files that changed from the base of the PR and between adb1bbf and 049e998.

📒 Files selected for processing (7)
  • README.md
  • android_sms_gateway/__init__.py
  • android_sms_gateway/client.py
  • android_sms_gateway/domain.py
  • android_sms_gateway/enums.py
  • dev-docs
  • user-docs
📝 Walkthrough

Walkthrough

Expanded client library: moved message endpoints to /messages, added device, settings, logs, health (liveness/readiness/startup) and token-refresh APIs; extended domain models (messages, devices, settings, logs, health, token) and enums; README/examples updated to reflect new methods and response usage. (50 words)

Changes

Cohort / File(s) Summary
Documentation & Aux files
README.md, dev-docs, user-docs
Rebranded docs and expanded API documentation and examples (use response = ...send(...) + response.id); new public schemas and links; added small repo reference files.
Public API Exports
android_sms_gateway/__init__.py
Expanded __all__ to re-export many new domain models and enums (Devices, Settings*, Health*, LogEntry, TokenRequest/Response, TextMessage/DataMessage, Webhook and various enums).
Client implementations
android_sms_gateway/client.py
Changed endpoints (/message/messages, GET /message/{id}GET /messages/{id}); added skip_phone_validation and device_active_within kwargs to send; added methods: get_messages, export_inbox, list_devices/remove_device (doc-level), get_settings/update_settings/patch_settings, get_logs, health endpoints returning HealthResponse plus liveness_check/readiness_check/startup_check, and refresh_token. Query-string encoding added.
Domain models & serialization
android_sms_gateway/domain.py
Extended Message/MessageState/Webhook (text/data split, device association, states, priority/sim/valid_until/device_id); added Device with ISO datetime parsing; added Settings* dataclasses and helpers, HealthCheck/HealthResponse, LogEntry, MessagesExportRequest, MessagesQueryFilter, QueryPagination; TokenResponse now accepts refresh_token; added from_dict/asdict helpers.
Enums
android_sms_gateway/enums.py
Added webhook events (sms:data-received, mms:received, mms:downloaded) and new enums: LimitPeriod, SimSelectionMode, MessagesProcessingOrder, HealthStatus, LogEntryPriority.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

ready

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: addition of device management, settings, logs, and health check methods to the client.
Docstring Coverage ✅ Passed Docstring coverage is 93.75% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

@android-sms-gateway android-sms-gateway deleted a comment from coderabbitai Bot Apr 16, 2026
@capcom6
Copy link
Copy Markdown
Member Author

capcom6 commented Apr 16, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

✅ Actions performed

Full review triggered.

@capcom6
Copy link
Copy Markdown
Member Author

capcom6 commented Apr 16, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

✅ Actions performed

Full review triggered.

@capcom6
Copy link
Copy Markdown
Member Author

capcom6 commented Apr 17, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

✅ Actions performed

Full review triggered.

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: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
android_sms_gateway/domain.py (1)

364-383: ⚠️ Potential issue | 🔴 Critical

refresh_token type annotation contradicts from_dict and is a breaking change.

refresh_token: str is declared as non-optional (Line 364), but from_dict uses payload.get("refreshToken") (Line 382), which returns None when the server omits the field — producing a TokenResponse whose annotation is violated. Additionally, making this a required non-default field after expires_at breaks any existing callers that construct TokenResponse positionally or via kwargs without a refresh token. The README (Lines 443) correctly documents it as Optional[str] = None.

🛠️ Suggested fix
-    refresh_token: str
-    """The refresh token."""
+    refresh_token: t.Optional[str] = None
+    """The refresh token."""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/domain.py` around lines 364 - 383, The TokenResponse
class's refresh_token field should be optional with a default to match from_dict
and existing callers: change the refresh_token annotation to Optional[str] and
give it a default of None (e.g., refresh_token: Optional[str] = None) in the
TokenResponse dataclass/definition so payload.get("refreshToken") remains valid
and positional/kw callers aren't broken; update any imports (typing.Optional)
and ensure TokenResponse.from_dict still uses payload.get("refreshToken")
unchanged.
🧹 Nitpick comments (2)
android_sms_gateway/domain.py (1)

262-305: Extract the ISO timestamp parser to reduce repetition.

The fromisoformat(... .replace("Z", "+00:00")) block is repeated 4 times here and again in LogEntry.from_dict. A small helper would improve readability and avoid drift.

♻️ Example helper
def _parse_iso(value: t.Optional[str]) -> t.Optional[datetime.datetime]:
    if value is None:
        return None
    return datetime.datetime.fromisoformat(value.replace("Z", "+00:00"))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/domain.py` around lines 262 - 305, Extract the repeated
ISO timestamp parsing into a small helper (e.g., _parse_iso) and replace the
four duplicated blocks in Device.from_dict (and the similar blocks in
LogEntry.from_dict) with calls to that helper; specifically add a function that
accepts Optional[str] and returns Optional[datetime.datetime] by handling None
and doing the value.replace("Z", "+00:00") then datetime.fromisoformat, and
update Device.from_dict to call _parse_iso(payload.get("createdAt")),
_parse_iso(payload.get("updatedAt")), _parse_iso(payload.get("deletedAt")), and
_parse_iso(payload.get("lastSeen")) (and do the same refactor in
LogEntry.from_dict).
android_sms_gateway/client.py (1)

189-189: filter shadows the Python built-in.

Ruff A002 at Line 189 and Line 610. Consider renaming (e.g. query or filter_) to avoid confusion; it also affects call-site ergonomics since users must pass it as a keyword anyway.

-        filter: t.Optional[domain.MessagesQueryFilter] = None,
+        query: t.Optional[domain.MessagesQueryFilter] = None,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/client.py` at line 189, The parameter name filter in the
function signature (typed as t.Optional[domain.MessagesQueryFilter]) shadows the
built-in and triggers Ruff A002; rename the parameter to a non-shadowing name
(e.g., filter_ or query) in the function/method definition in client.py and
update all internal references, type hints, and call-sites (including the other
occurrence flagged at the second location) to use the new name; ensure exported
API/ callers use a keyword-compatible name and run tests/linter to verify no
remaining shadowing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@android_sms_gateway/client.py`:
- Around line 159-162: The client now calls the renamed endpoints (/messages and
/messages/{id}) in send and get_state (refer to methods send and get_state),
which is a breaking change for servers still using /message; add a prominent
breaking-change entry to your release notes (CHANGELOG.md or
BREAKING_CHANGES.md) that states the endpoint rename, lists affected client
methods (send, get_state), and specifies the minimum compatible SMSGate server
version required (and any migration guidance for operators of older servers).
Ensure the entry is visible in the release headline and include an explicit note
that users on older servers will receive 404s if they upgrade the client without
upgrading the server.
- Around line 145-165: The code is passing params= to our HTTP client which
doesn't accept it; update the HttpClient and AsyncHttpClient protocol method
signatures (post and get) to include params: Optional[Dict[str, str]] = None,
then modify all concrete implementations (RequestsHttpClient, HttpxHttpClient,
AiohttpHttpClient) to accept that params argument and forward it to the
underlying library call (e.g., requests.post/get, httpx.post/get, aiohttp
session methods) using the appropriate query param parameter, and keep send(),
get_messages(), and get_logs() (and their async variants) unchanged so their
params dicts are accepted and sent through.
- Around line 186-213: Update the mismatched docstrings to reflect the actual
function signatures: in get_messages (and the async counterpart around Lines
607-621, e.g., async get_messages_async or similarly named method), replace the
Args section that lists from_, to, state, device_id, limit, offset (and any
nonexistent request param) with descriptions for filter: MessagesQueryFilter and
pagination: QueryPagination, and ensure the Returns section matches
t.List[domain.MessageState] (or an awaitable returning that type for the async
method); remove any references to nonexistent parameters and keep
examples/formatting consistent with other methods in the module.
- Around line 492-497: The refresh_token call currently invokes self.http.post
without the required payload argument and will raise TypeError; update the
synchronous refresh_token (the call wrapped by domain.TokenResponse.from_dict)
to pass an empty dict as the payload (self.http.post(..., {} , headers=...)) so
the Authorization header is sent with an empty body, and make the same change
for the async counterpart (the async refresh_token/refresh_token_async that
calls self.http.post) to pass {} as the payload as well.

In `@android_sms_gateway/domain.py`:
- Around line 400-516: The Settings asdict() methods currently emit snake_case
keys; update each Settings class (SettingsGateway.asdict,
SettingsEncryption.asdict, SettingsMessages.asdict, SettingsLogs.asdict,
SettingsPing.asdict, SettingsWebhooks.asdict) to emit camelCase wire keys (e.g.,
cloudUrl, privateToken, notificationChannel, passphrase, limitPeriod,
limitValue, logLifetimeDays, processingOrder, sendIntervalMax, sendIntervalMin,
simSelectionMode, lifetimeDays -> logLifetimeDays as requested, intervalSeconds,
retryCount, signingKey, internetRequired); keep existing behavior for optional
fields and enum .value usage but change the dictionary keys to the camelCase
names used across the module so from_dict()/API wiring remains consistent.

In `@README.md`:
- Around line 310-313: The README's get_messages signature is outdated and will
cause a TypeError; update the README entry to match the actual implementation by
replacing the old signature with get_messages(*, filter: MessagesQueryFilter =
None, pagination: QueryPagination = None) and add a minimal example showing how
to construct a MessagesQueryFilter and QueryPagination and call
APIClient.get_messages / AsyncAPIClient.get_messages; reference the actual
client method names (APIClient.get_messages, AsyncAPIClient.get_messages) and
the types (MessagesQueryFilter, QueryPagination) so readers know which objects
to import and use.
- Line 310: Update the README entry for the send method to indicate that
skip_phone_validation and device_active_within are keyword-only parameters (must
be passed as kwargs) to match the client.py signature where these appear after a
'*' (function send(message, skip_phone_validation=False,
device_active_within=0)). Edit the table row that documents send (returning
domain.MessageState) to explicitly show the parameters as keyword-only (e.g.,
send(message, *, skip_phone_validation=False, device_active_within=0) or add a
parenthetical note) so users do not attempt to call skip_phone_validation or
device_active_within positionally.

---

Outside diff comments:
In `@android_sms_gateway/domain.py`:
- Around line 364-383: The TokenResponse class's refresh_token field should be
optional with a default to match from_dict and existing callers: change the
refresh_token annotation to Optional[str] and give it a default of None (e.g.,
refresh_token: Optional[str] = None) in the TokenResponse dataclass/definition
so payload.get("refreshToken") remains valid and positional/kw callers aren't
broken; update any imports (typing.Optional) and ensure TokenResponse.from_dict
still uses payload.get("refreshToken") unchanged.

---

Nitpick comments:
In `@android_sms_gateway/client.py`:
- Line 189: The parameter name filter in the function signature (typed as
t.Optional[domain.MessagesQueryFilter]) shadows the built-in and triggers Ruff
A002; rename the parameter to a non-shadowing name (e.g., filter_ or query) in
the function/method definition in client.py and update all internal references,
type hints, and call-sites (including the other occurrence flagged at the second
location) to use the new name; ensure exported API/ callers use a
keyword-compatible name and run tests/linter to verify no remaining shadowing.

In `@android_sms_gateway/domain.py`:
- Around line 262-305: Extract the repeated ISO timestamp parsing into a small
helper (e.g., _parse_iso) and replace the four duplicated blocks in
Device.from_dict (and the similar blocks in LogEntry.from_dict) with calls to
that helper; specifically add a function that accepts Optional[str] and returns
Optional[datetime.datetime] by handling None and doing the value.replace("Z",
"+00:00") then datetime.fromisoformat, and update Device.from_dict to call
_parse_iso(payload.get("createdAt")), _parse_iso(payload.get("updatedAt")),
_parse_iso(payload.get("deletedAt")), and _parse_iso(payload.get("lastSeen"))
(and do the same refactor in LogEntry.from_dict).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 222fe3a9-ca76-40d6-b9d4-b6ecb74c9f4a

📥 Commits

Reviewing files that changed from the base of the PR and between f30b333 and 32feda8.

📒 Files selected for processing (7)
  • README.md
  • android_sms_gateway/__init__.py
  • android_sms_gateway/client.py
  • android_sms_gateway/domain.py
  • android_sms_gateway/enums.py
  • dev-docs
  • user-docs

Comment thread android_sms_gateway/client.py
Comment thread android_sms_gateway/client.py Outdated
Comment thread android_sms_gateway/client.py
Comment thread android_sms_gateway/client.py
Comment thread android_sms_gateway/domain.py
Comment thread README.md Outdated
Comment thread README.md Outdated
@capcom6 capcom6 changed the title [client] add new methods [client] add device management, settings, logs, and health check methods Apr 17, 2026
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: 2

♻️ Duplicate comments (2)
android_sms_gateway/client.py (2)

911-916: ⚠️ Potential issue | 🔴 Critical

Async refresh_token still missing payload argument — TypeError at runtime.

The sync counterpart (Lines 488–494) was fixed to pass payload={}, but the async version here was not. AsyncHttpClient.post requires payload (it's a positional parameter in the protocol and all concrete implementations). Any call to await client.refresh_token(...) will raise TypeError: post() missing 1 required positional argument: 'payload'.

🛠️ Proposed fix
         return domain.TokenResponse.from_dict(
             await self.http.post(
                 f"{self.base_url}/auth/token/refresh",
+                payload={},
                 headers=headers,
             )
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/client.py` around lines 911 - 916, The async method
refresh_token is calling self.http.post without the required payload argument,
which causes a TypeError at runtime; update the call in refresh_token so the
await self.http.post(...) includes payload={} (keeping headers and URL
unchanged) before passing the response into domain.TokenResponse.from_dict;
reference the async refresh_token method, AsyncHttpClient.post requirement, and
domain.TokenResponse.from_dict to locate and fix the call.

609-617: ⚠️ Potential issue | 🟡 Minor

Async get_messages docstring is out of sync with the signature.

Docstring's Args still says request: The query request containing filters and pagination parameters., but the parameters are actually query: MessagesQueryFilter and pagination: QueryPagination (same wording fix was applied to the sync variant above but not here).

📝 Suggested fix
         Args:
-            request: The query request containing filters and pagination parameters.
+            query: Optional query filter (date range, state, device ID).
+            pagination: Optional pagination (limit, offset).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/client.py` around lines 609 - 617, The async get_messages
docstring is outdated: update the Args section to match the function signature
by replacing the generic `request` description with entries for `query:
MessagesQueryFilter` and `pagination: QueryPagination`, briefly describing each
(query filters and pagination params) and keep the Returns description as-is;
locate the docstring inside the async def get_messages(...) function and adjust
only the parameter names/descriptions to match the signature.
🧹 Nitpick comments (3)
android_sms_gateway/domain.py (1)

668-691: LogEntry.created_at type/parser mismatch.

created_at is annotated as non-optional datetime.datetime, but _parse_iso(payload["createdAt"]) can return None when the value is None. Either make the field Optional[datetime.datetime] or inline a non-nullable parse here (e.g., datetime.datetime.fromisoformat(payload["createdAt"].replace("Z", "+00:00"))).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/domain.py` around lines 668 - 691, The LogEntry.from_dict
uses _parse_iso(payload["createdAt"]) which can return None but
LogEntry.created_at is typed as datetime.datetime; fix by either (A) making the
dataclass field LogEntry.created_at Optional[datetime.datetime] and accept None
from _parse_iso, or (B) enforce a non-nullable parse inside from_dict (e.g.,
parse payload["createdAt"] with datetime.fromisoformat after replacing "Z" with
"+00:00" and raise/handle if it's missing) so that created_at remains a
datetime; update the LogEntry.created_at annotation and any downstream code
accordingly and reference the from_dict method and _parse_iso function when
making the change.
android_sms_gateway/client.py (2)

160-160: Avoid appending a trailing ? when there are no query params.

When no params are set, urlencode({}) yields "" and the request URL becomes e.g. /messages?. This is harmless on most servers but ugly in logs and not all proxies/WAFs handle it gracefully. Affects send, get_messages, get_logs in both sync and async clients. A small helper (or conditional concatenation) would keep URLs clean:

qs = urlencode(params)
url = f"{self.base_url}/messages" + (f"?{qs}" if qs else "")

Also applies to: 214-214, 389-389, 575-575, 630-630, 812-812

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/client.py` at line 160, The URL construction currently
appends "?{urlencode(params)}" unconditionally which yields a trailing '?' when
params is empty; update the callers in client.py (methods send, get_messages,
get_logs for both sync and async clients) to build the query string first (qs =
urlencode(params)) and then append it only if non-empty (e.g., url =
f"{self.base_url}/messages" + (f"?{qs}" if qs else "")), replacing the direct
f"{self.base_url}/messages?{urlencode(params)}" usage at the affected spots.

393-405: health_check docstring mentions "readiness probe" but that's now a separate method.

Since readiness_check() exists as a dedicated method hitting /health/ready, the parenthetical "(readiness probe)" in health_check's docstring is misleading. Consider dropping it or clarifying that /health is the overall/aggregate status endpoint.

Also applies to: 816-828

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/client.py` around lines 393 - 405, Update the docstring
for health_check to remove or correct the misleading parenthetical "(readiness
probe)"—clarify that health_check() calls the aggregate /health endpoint
(overall system status) and that the dedicated readiness_check() hits
/health/ready for readiness; similarly, find the other duplicate docstring (the
same wording around the readiness probe) and apply the same clarification so
both health_check() and readiness_check() docstrings accurately describe their
distinct endpoints and purpose.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@android_sms_gateway/domain.py`:
- Around line 629-630: The annotated type for the attribute release_id is
currently t.Optional[int] but the JSON uses a string; update the type annotation
to t.Optional[str] and any related dataclass/ model field declaration
(release_id) so it accepts string values; also audit and update the
deserialization flow (e.g., from_dict or the class constructor where release_id
is assigned) to stop converting/expecting int and treat the raw JSON string as
the canonical value, and update any type hints/usages that assumed an int to use
str instead.
- Around line 543-584: DeviceSettings.from_dict currently forwards raw dicts
into Settings* constructors which leaves enum-typed fields as plain strings;
update deserialization to coerce those strings back to their Enum members either
by adding/from enhancing a from_dict classmethod on each Settings class (e.g.,
SettingsMessages.from_dict, SettingsLogs.from_dict, SettingsGateway.from_dict,
SettingsEncryption.from_dict, SettingsPing.from_dict,
SettingsWebhooks.from_dict) or by converting fields inline in
DeviceSettings.from_dict before constructing (convert
payload["messages"]["limit_period"] -> LimitPeriod(<value>),
payload["messages"]["processing_order"] -> MessagesProcessingOrder(<value>),
payload["messages"]["sim_selection_mode"] -> SimSelectionMode(<value>), etc.);
ensure every enum field referenced elsewhere (LimitPeriod,
MessagesProcessingOrder, SimSelectionMode, etc.) is converted so asdict() later
will see real Enum instances, not strings.

---

Duplicate comments:
In `@android_sms_gateway/client.py`:
- Around line 911-916: The async method refresh_token is calling self.http.post
without the required payload argument, which causes a TypeError at runtime;
update the call in refresh_token so the await self.http.post(...) includes
payload={} (keeping headers and URL unchanged) before passing the response into
domain.TokenResponse.from_dict; reference the async refresh_token method,
AsyncHttpClient.post requirement, and domain.TokenResponse.from_dict to locate
and fix the call.
- Around line 609-617: The async get_messages docstring is outdated: update the
Args section to match the function signature by replacing the generic `request`
description with entries for `query: MessagesQueryFilter` and `pagination:
QueryPagination`, briefly describing each (query filters and pagination params)
and keep the Returns description as-is; locate the docstring inside the async
def get_messages(...) function and adjust only the parameter names/descriptions
to match the signature.

---

Nitpick comments:
In `@android_sms_gateway/client.py`:
- Line 160: The URL construction currently appends "?{urlencode(params)}"
unconditionally which yields a trailing '?' when params is empty; update the
callers in client.py (methods send, get_messages, get_logs for both sync and
async clients) to build the query string first (qs = urlencode(params)) and then
append it only if non-empty (e.g., url = f"{self.base_url}/messages" + (f"?{qs}"
if qs else "")), replacing the direct
f"{self.base_url}/messages?{urlencode(params)}" usage at the affected spots.
- Around line 393-405: Update the docstring for health_check to remove or
correct the misleading parenthetical "(readiness probe)"—clarify that
health_check() calls the aggregate /health endpoint (overall system status) and
that the dedicated readiness_check() hits /health/ready for readiness;
similarly, find the other duplicate docstring (the same wording around the
readiness probe) and apply the same clarification so both health_check() and
readiness_check() docstrings accurately describe their distinct endpoints and
purpose.

In `@android_sms_gateway/domain.py`:
- Around line 668-691: The LogEntry.from_dict uses
_parse_iso(payload["createdAt"]) which can return None but LogEntry.created_at
is typed as datetime.datetime; fix by either (A) making the dataclass field
LogEntry.created_at Optional[datetime.datetime] and accept None from _parse_iso,
or (B) enforce a non-nullable parse inside from_dict (e.g., parse
payload["createdAt"] with datetime.fromisoformat after replacing "Z" with
"+00:00" and raise/handle if it's missing) so that created_at remains a
datetime; update the LogEntry.created_at annotation and any downstream code
accordingly and reference the from_dict method and _parse_iso function when
making the change.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d53ff7fd-f69a-4bfa-88c4-2b6321689d70

📥 Commits

Reviewing files that changed from the base of the PR and between 32feda8 and 2844ab6.

📒 Files selected for processing (3)
  • README.md
  • android_sms_gateway/client.py
  • android_sms_gateway/domain.py
✅ Files skipped from review due to trivial changes (1)
  • README.md

Comment thread android_sms_gateway/domain.py
Comment thread android_sms_gateway/domain.py
@coderabbitai coderabbitai Bot added the ready label Apr 17, 2026
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: 1

🧹 Nitpick comments (2)
android_sms_gateway/domain.py (2)

764-794: Inconsistent datetime handling between query filter and export request.

MessagesExportRequest.since/until are typed datetime.datetime and serialized via .isoformat(), while MessagesQueryFilter.from_/to are plain Optional[str] with a comment "RFC3339 format" — pushing formatting responsibility onto callers and inviting malformed inputs. Consider aligning the types so both accept datetime.datetime and serialize consistently.

♻️ Proposed change
-    from_: t.Optional[str] = None
+    from_: t.Optional[datetime.datetime] = None
     """Start date in RFC3339 format."""
-    to: t.Optional[str] = None
+    to: t.Optional[datetime.datetime] = None
     """End date in RFC3339 format."""
@@
-        if self.from_ is not None:
-            params["from"] = self.from_
-        if self.to is not None:
-            params["to"] = self.to
+        if self.from_ is not None:
+            params["from"] = self.from_.isoformat()
+        if self.to is not None:
+            params["to"] = self.to.isoformat()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/domain.py` around lines 764 - 794, Change
MessagesQueryFilter.from_ and .to from Optional[str] to
Optional[datetime.datetime] and update their docstrings to reflect datetime
inputs; in MessagesQueryFilter.asdict(), when from_ or to is not None call
.isoformat() to serialize to RFC3339 strings (mirroring
MessagesExportRequest.since/until behavior); ensure datetime is imported
(datetime.datetime) and keep the output keys ("from", "to") the same while
leaving device_id/state handling unchanged.

722-731: Reuse _parse_iso helper for consistency.

LogEntry.from_dict duplicates the ISO-parsing logic now encapsulated in _parse_iso (Lines 19–30). Since created_at is required, unwrap the helper’s result with a non-null assertion or a guard — but collapsing the duplication keeps the parsing rules (e.g., trailing Z handling) in one place.

♻️ Proposed change
         return cls(
             id=payload["id"],
-            created_at=datetime.datetime.fromisoformat(
-                payload["createdAt"].replace("Z", "+00:00")
-            ),
+            created_at=_parse_iso(payload["createdAt"]),
             message=payload["message"],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/domain.py` around lines 722 - 731, LogEntry.from_dict
duplicates ISO parsing; replace the inline datetime.fromisoformat logic with a
call to the existing _parse_iso helper and handle the required created_at value
by unwrapping or guarding its result (e.g., call
_parse_iso(payload["createdAt"]) and assert/raise if it returns None) so parsing
rules (like trailing "Z" handling) are centralized in _parse_iso while still
ensuring created_at is non-null.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@android_sms_gateway/domain.py`:
- Around line 586-608: DeviceSettings.from_dict currently unpacks payload
sub-dicts directly into SettingsGateway, SettingsEncryption, SettingsLogs,
SettingsPing, and SettingsWebhooks which will raise TypeError if the server
sends unknown keys; add a safe deserialization step by either (A) implementing a
from_dict on each Settings* class (mirroring SettingsMessages.from_dict) that
explicitly pulls known fields via payload.get(...) or (B) filter unknown keys
before calling the constructors using dataclasses.fields to build an allowed-key
set (e.g. _only_known(cls, data) -> {k:v for k,v in data.items() if k in
allowed}) and then call SettingsGateway(**_only_known(SettingsGateway,
payload["gateway"])) (and likewise for SettingsEncryption, SettingsLogs,
SettingsPing, SettingsWebhooks).

---

Nitpick comments:
In `@android_sms_gateway/domain.py`:
- Around line 764-794: Change MessagesQueryFilter.from_ and .to from
Optional[str] to Optional[datetime.datetime] and update their docstrings to
reflect datetime inputs; in MessagesQueryFilter.asdict(), when from_ or to is
not None call .isoformat() to serialize to RFC3339 strings (mirroring
MessagesExportRequest.since/until behavior); ensure datetime is imported
(datetime.datetime) and keep the output keys ("from", "to") the same while
leaving device_id/state handling unchanged.
- Around line 722-731: LogEntry.from_dict duplicates ISO parsing; replace the
inline datetime.fromisoformat logic with a call to the existing _parse_iso
helper and handle the required created_at value by unwrapping or guarding its
result (e.g., call _parse_iso(payload["createdAt"]) and assert/raise if it
returns None) so parsing rules (like trailing "Z" handling) are centralized in
_parse_iso while still ensuring created_at is non-null.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c8e7a2bf-9437-426a-852e-f8a6a16b7ad7

📥 Commits

Reviewing files that changed from the base of the PR and between 2844ab6 and 4a956db.

📒 Files selected for processing (2)
  • android_sms_gateway/client.py
  • android_sms_gateway/domain.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • android_sms_gateway/client.py

Comment thread android_sms_gateway/domain.py Outdated
@github-actions github-actions Bot removed the ready label Apr 19, 2026
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: 2

♻️ Duplicate comments (1)
android_sms_gateway/domain.py (1)

701-702: ⚠️ Potential issue | 🟡 Minor

release_id should be t.Optional[str], not t.Optional[int].

Per the IETF Health Check Response Format (draft-inadarei-api-health-check-06), releaseId is a string identifier (e.g., "1.2.2"). from_dict passes the raw value through without coercion, so the annotation is inconsistent with actual runtime values.

🛠️ Proposed fix
-    release_id: t.Optional[int] = None
+    release_id: t.Optional[str] = None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android_sms_gateway/domain.py` around lines 701 - 702, The field release_id
is annotated as t.Optional[int] but the health-check spec and runtime values are
strings (e.g. "1.2.2"); change the type annotation to t.Optional[str] and update
the docstring to reflect it's a string identifier, and if any deserialization
logic in from_dict currently assumes int, adjust it to accept/return the raw
string (or coerce via str(value)) so the runtime value matches the new
annotation; update references to release_id and from_dict accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@android_sms_gateway/domain.py`:
- Around line 740-763: LogEntry.created_at is typed as datetime.datetime but you
call _parse_iso(...) which returns Optional[datetime.datetime]; adjust by either
changing the field annotation to Optional[datetime.datetime] or, preferably,
make from_dict validate/assert the parse result: call created_at =
_parse_iso(payload["createdAt"]), if created_at is None raise a ValueError (or
assert) before passing it into cls(...), keeping the rest of from_dict unchanged
and referencing LogEntry.created_at, LogEntry.from_dict, and _parse_iso to
locate the code to modify.
- Around line 390-408: SettingsGateway.from_dict currently reads camelCase keys;
update it to read snake_case keys to match asdict and other Settings* classes.
Change SettingsGateway.from_dict to pull payload.get("cloud_url"),
payload.get("private_token"), and payload.get("notification_channel") so the
constructor call (in SettingsGateway.from_dict) populates cloud_url,
private_token, and notification_channel consistently with SettingsGateway.asdict
and with other Settings classes (so DeviceSettings.from_dict(...).gateway
round-trips correctly).

---

Duplicate comments:
In `@android_sms_gateway/domain.py`:
- Around line 701-702: The field release_id is annotated as t.Optional[int] but
the health-check spec and runtime values are strings (e.g. "1.2.2"); change the
type annotation to t.Optional[str] and update the docstring to reflect it's a
string identifier, and if any deserialization logic in from_dict currently
assumes int, adjust it to accept/return the raw string (or coerce via
str(value)) so the runtime value matches the new annotation; update references
to release_id and from_dict accordingly.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88f8305e-9048-4081-b9a9-4cc18ee13314

📥 Commits

Reviewing files that changed from the base of the PR and between 4a956db and adb1bbf.

📒 Files selected for processing (1)
  • android_sms_gateway/domain.py

Comment thread android_sms_gateway/domain.py
Comment thread android_sms_gateway/domain.py
@capcom6 capcom6 force-pushed the client/add-new-methods branch from 0e606f0 to 049e998 Compare April 20, 2026 07:44
@capcom6 capcom6 merged commit e0f4d57 into master Apr 21, 2026
10 checks passed
@capcom6 capcom6 deleted the client/add-new-methods branch April 21, 2026 01:46
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.

1 participant