Conversation
WalkthroughAdds optional per-message scheduling ( Changes
Sequence DiagramsequenceDiagram
participant Client as Client
participant API as LocalServer/API
participant Service as MessagesService
participant Repo as MessagesRepository
participant DAO as MessagesDao
participant Worker as SendMessagesWorker
Client->>API: POST /messages (with scheduleAt)
API->>API: request.validate() (scheduleAt must be future)
API-->>Service: enqueue(request with scheduleAt)
Service->>Repo: enqueue(request with scheduleAt)
Repo-->>Repo: persist Message(scheduleAt)
Service->>DAO: nextScheduledTime()
DAO-->>Service: earliestScheduledMs
alt scheduleAt in future
Service->>Worker: start(context, force=false, scheduleAt=earliestScheduledMs)
Worker->>Worker: initialDelay = max(0, scheduleAt - now)
Worker->>Worker: setInitialDelay(initialDelay)
else immediate or no scheduleAt
Service->>Worker: start(context, force=true, scheduleAt=IMMEDIATE)
Worker->>Worker: initialDelay = 0 (expedited)
end
Worker->>DAO: getPendingLifo(now)/getPendingFifo(now)
DAO->>DAO: filter state='Pending' AND (scheduleAt IS NULL OR scheduleAt <= now)
DAO-->>Worker: ready messages
Worker->>Service: process messages
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
app/src/main/assets/api/swagger.json (1)
606-610: Addformat: "date-time"toscheduleAtfor OpenAPI schema consistency.The
scheduleAtfield at line 609 is declared astype: "string"without theformat: "date-time"annotation. Multiple other timestamp fields in this specification (receivedAt, etc.) useformat: "date-time"to comply with OpenAPI 3.0's RFC 3339 format specification, enabling proper code generation and validation in client tools. This field should follow the same pattern.Note: The nearby
validUntilfield (line 604) has the same issue.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/assets/api/swagger.json` around lines 606 - 610, Add the OpenAPI date-time format to the timestamp string fields: update the schema entries for scheduleAt and validUntil to include format: "date-time" (in addition to type: "string") so they follow the same RFC3339 pattern used by other timestamp fields like receivedAt and enable proper code generation/validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/PostMessageRequest.kt`:
- Around line 52-55: The current init validation in PostMessageRequest allows
scheduleAt == now; change the check so scheduleAt must be strictly in the
future. In the PostMessageRequest init block (the validation using
scheduleAt?.before(Date())), replace that conditional with a strict future check
such as verifying scheduleAt?.after(Date()) == true (and throw
IllegalArgumentException if it is not) so any scheduleAt equal to or before now
is rejected.
In
`@app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt`:
- Around line 121-124: In SendMessagesWorker.startAt() the call to
WorkManager.enqueueUniqueWork(...) uses the fixed SCHEDULED_NAME with
ExistingWorkPolicy.REPLACE which causes later scheduled messages to overwrite
earlier ones; change this to either generate a per-message unique work ID (e.g.,
include the message ID in the unique name passed to enqueueUniqueWork) or switch
the policy to ExistingWorkPolicy.KEEP so earlier scheduled work isn't replaced;
update the unique name creation and the enqueueUniqueWork(...) invocation
accordingly.
In `@README.md`:
- Around line 203-210: Update the curl example's scheduleAt value to a truly
future ISO-8601 timestamp so the sample matches the "future delivery" text: edit
the code block containing the curl POST example and replace the scheduleAt value
(currently "2026-04-05T09:00:00Z") with a future date/time (e.g.,
"2027-04-05T09:00:00Z") while keeping the same JSON structure and header usage;
ensure the token name scheduleAt and the POST example remain unchanged except
for the timestamp.
---
Nitpick comments:
In `@app/src/main/assets/api/swagger.json`:
- Around line 606-610: Add the OpenAPI date-time format to the timestamp string
fields: update the schema entries for scheduleAt and validUntil to include
format: "date-time" (in addition to type: "string") so they follow the same
RFC3339 pattern used by other timestamp fields like receivedAt and enable proper
code generation/validation.
🪄 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: 21d74b59-6af4-43cb-95fa-ec256adf949c
📒 Files selected for processing (14)
README.mdapp/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/data/AppDatabase.ktapp/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.ktapp/src/main/java/me/capcom/smsgateway/data/entities/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.ktapp/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
144f052 to
f0ab658
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.kt (1)
240-248: Cloud messages bypassscheduleAtvalidation that exists for local requests.
PostMessageRequestvalidates thatscheduleAtmust be in the future (context:PostMessageRequest.ktinit block), but cloud messages viaGatewayApi.Messagehave no such validation. If the cloud sends a pastscheduleAt, the message will be processed immediately (which is acceptable), but the behavior differs from the local API which rejects past values outright.Consider whether server-side validation is sufficient or if client-side validation should be added for consistency.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.kt` around lines 240 - 248, GatewayService is currently mapping GatewayApi.Message into SendParams without validating scheduleAt, causing cloud requests to accept past scheduleAt values unlike PostMessageRequest (see its init block). Add the same server-side validation in GatewayService before creating SendParams: if message.scheduleAt is non-null and is before the current time, reject the request (return/throw the same BadRequest/validation error used by PostMessageRequest) so cloud messages mirror local API behavior; implement this check adjacent to the mapping code that constructs SendParams.app/src/main/assets/api/swagger.json (1)
606-610: Consider addingformat: date-timefor better API documentation and client code generation.The schema correctly documents the
scheduleAtfield with the validation requirement. Adding the OpenAPI format specifier would improve client tooling support.📝 Suggested enhancement
"scheduleAt": { "description": "Schedule message delivery at (must be in the future)", "example": "2020-01-01T08:30:00Z", + "format": "date-time", "type": "string" },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/assets/api/swagger.json` around lines 606 - 610, The OpenAPI schema for the scheduleAt property lacks an explicit format; update the scheduleAt schema in swagger.json to include "format": "date-time" alongside its "type": "string" and existing description/example so client generators and validators recognize it as an RFC3339 timestamp.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt`:
- Around line 59-60: The query in nextScheduledTime() currently uses
COALESCE(scheduleAt, 0) which mixes types; change the SQL to explicitly exclude
NULL scheduleAt values so it returns the minimum scheduled time for messages
that have an actual schedule: replace the query with "SELECT MIN(scheduleAt)
FROM message WHERE state = 'Pending' AND scheduleAt IS NOT NULL" and keep the
function signature fun nextScheduledTime(): Long? so when no scheduled messages
exist it still returns null (or handle null at call sites to treat as 0 if
desired).
---
Nitpick comments:
In `@app/src/main/assets/api/swagger.json`:
- Around line 606-610: The OpenAPI schema for the scheduleAt property lacks an
explicit format; update the scheduleAt schema in swagger.json to include
"format": "date-time" alongside its "type": "string" and existing
description/example so client generators and validators recognize it as an
RFC3339 timestamp.
In `@app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.kt`:
- Around line 240-248: GatewayService is currently mapping GatewayApi.Message
into SendParams without validating scheduleAt, causing cloud requests to accept
past scheduleAt values unlike PostMessageRequest (see its init block). Add the
same server-side validation in GatewayService before creating SendParams: if
message.scheduleAt is non-null and is before the current time, reject the
request (return/throw the same BadRequest/validation error used by
PostMessageRequest) so cloud messages mirror local API behavior; implement this
check adjacent to the mapping code that constructs SendParams.
🪄 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: 90003aa0-eca6-4d8c-a94d-c03df1885895
📒 Files selected for processing (14)
app/schemas/me.capcom.smsgateway.data.AppDatabase/20.jsonapp/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/data/AppDatabase.ktapp/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.ktapp/src/main/java/me/capcom/smsgateway/data/entities/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.ktapp/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
✅ Files skipped from review due to trivial changes (1)
- app/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
🚧 Files skipped from review as they are similar to previous changes (8)
- app/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.kt
- app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/PostMessageRequest.kt
- app/src/main/java/me/capcom/smsgateway/data/entities/Message.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.kt
- app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt
1eb8fab to
38d928b
Compare
scheduleAt
🤖 Pull request artifacts
|
38d928b to
2699dad
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt`:
- Around line 59-60: The MIN query in MessagesDao.nextScheduledTime() is
operating on ISO-8601 TEXT (scheduleAt) and returning an invalid Long, breaking
the scheduleAt < earliest comparison in MessagesService.kt; fix by changing the
DAO query to convert the ISO-8601 TEXT to epoch milliseconds in SQL (use
SQLite's strftime('%s', scheduleAt) * 1000, with COALESCE/null handling) so
nextScheduledTime() returns the correct millisecond epoch Long?, and ensure
Converters.dateToString()/storage remains consistent or migrate to INTEGER epoch
storage if you prefer.
🪄 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: d9ada47e-2396-4ef9-abbd-af4118843fe1
📒 Files selected for processing (14)
app/schemas/me.capcom.smsgateway.data.AppDatabase/21.jsonapp/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/data/AppDatabase.ktapp/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.ktapp/src/main/java/me/capcom/smsgateway/data/entities/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.ktapp/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
✅ Files skipped from review due to trivial changes (1)
- app/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
🚧 Files skipped from review as they are similar to previous changes (10)
- app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
- app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.kt
- app/src/main/java/me/capcom/smsgateway/data/entities/Message.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.kt
- app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/PostMessageRequest.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt
- app/src/main/assets/api/swagger.json
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt
- app/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.kt
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt`:
- Around line 59-60: The problem is nextScheduledTime() returns the post-insert
global minimum, so MessagesService.enqueueMessage() can't detect when the newly
inserted message became the earliest; to fix, read the previous minimum before
inserting rather than after: in MessagesService.enqueueMessage() call
dao.nextScheduledTime() to capture priorMin, then perform
messages.enqueue(request) (or insert), then call dao.nextScheduledTime() again
and compare against priorMin to decide whether to force
SendMessagesWorker.start(force=true). Alternatively, if you prefer a DAO-based
fix, add a new DAO method like nextScheduledTimeExcluding(excludeId: Long) with
SQL excluding that id (e.g., WHERE state='Pending' AND id != :excludeId) and use
it to compare correctly; reference nextScheduledTime(),
MessagesService.enqueueMessage(), messages.enqueue(request), and
SendMessagesWorker.start(force).
- Around line 59-60: The query in MessagesDao.nextScheduledTime() uses the
sqlite-specific unixepoch() function which may not exist on older Android SQLite
builds; replace unixepoch(MIN(`scheduleAt`)) * 1000 with CAST(strftime('%s',
MIN(`scheduleAt`)) AS INTEGER) * 1000 (or simply strftime('%s',
MIN(`scheduleAt`)) * 1000) to compute milliseconds in a portable way, update the
`@Query` string accordingly in the MessagesDao interface so nextScheduledTime()
returns the same epoch-ms value without relying on unixepoch().
🪄 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: ec961037-00d9-4c62-8e44-b84e950940ba
📒 Files selected for processing (1)
app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt
e81e39c to
dc5a241
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt`:
- Around line 99-110: After processing pending messages, add reschedule logic at
the end of sendPendingMessages() (or in doWork() after successful completion) to
query dao.nextScheduledTime(), and if it returns a future timestamp scheduleAt >
System.currentTimeMillis(), call SendMessagesWorker.start(context, /* earliest =
*/ determine whether this next scheduled time is the earliest via
dao.nextScheduledTime() compared to any in-memory/queued state or reuse the
existing earliest computation logic, and pass scheduleAt) so the worker is
started for the next pending scheduled message; use the same
SendMessagesWorker.start(...) signature as in MessagesService.enqueue block and
ensure you only start when there is a valid future scheduleAt.
In
`@app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt`:
- Around line 80-91: The start(...) method in SendMessagesWorker currently calls
setExpedited(...) while also setting a non-zero initialDelay, which causes
WorkManager to throw "Expedited jobs cannot be delayed"; fix by removing the
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) call or
wrapping it so it is only applied when initialDelay == 0 before building the
OneTimeWorkRequestBuilder; keep the existing setBackoffCriteria(...) and
setInitialDelay(...) logic intact and ensure the builder only receives
setExpedited when initialDelay is zero.
🪄 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: 76e22709-c7cb-4074-b8b3-d108c642ea80
📒 Files selected for processing (14)
app/schemas/me.capcom.smsgateway.data.AppDatabase/21.jsonapp/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/data/AppDatabase.ktapp/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.ktapp/src/main/java/me/capcom/smsgateway/data/entities/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.ktapp/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
✅ Files skipped from review due to trivial changes (2)
- app/src/main/assets/api/swagger.json
- app/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.kt
🚧 Files skipped from review as they are similar to previous changes (6)
- app/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.kt
- app/src/main/java/me/capcom/smsgateway/data/entities/Message.kt
- app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/PostMessageRequest.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.kt
- app/schemas/me.capcom.smsgateway.data.AppDatabase/21.json
- app/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
dc5a241 to
21cdb7f
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt (1)
92-113:⚠️ Potential issue | 🔴 CriticalCritical: Duplicate
messages.enqueue(request)call will fail or corrupt data.The message is enqueued at line 94, and then enqueued again at line 105. This duplicate call will either:
- Throw
SQLiteConstraintException(which is caught and rethrowsConflictException), causing the request to fail even though the message was already persisted- If the ID is somehow different, create duplicate messages
Remove the duplicate enqueue call at line 105.
🐛 Proposed fix
fun enqueueMessage(request: SendRequest): MessageWithRecipients { val message = try { messages.enqueue(request) } catch (_: android.database.sqlite.SQLiteConstraintException) { throw ConflictException() } val priority = request.params.priority ?: Message.PRIORITY_DEFAULT val scheduleAt = request.params.scheduleAt?.time val earliest = request.params.scheduleAt?.let { it.time < (dao.nextScheduledTime() ?: 0) } ?: true - - messages.enqueue(request) if (scheduleAt != null && scheduleAt > System.currentTimeMillis()) { SendMessagesWorker.start(context, earliest, scheduleAt) } else { SendMessagesWorker.start(context, priority >= Message.PRIORITY_EXPEDITED, 0) } return message }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt` around lines 92 - 113, In enqueueMessage (MessagesService.enqueueMessage) remove the second duplicate call to messages.enqueue(request) that appears after computing earliest/scheduleAt; the message is already persisted by the first try/catch block (which throws ConflictException on SQLiteConstraintException), so delete the later messages.enqueue(request) and ensure the existing local variable message (MessageWithRecipients) is used when scheduling via SendMessagesWorker.start(context, earliest, scheduleAt) or SendMessagesWorker.start(context, priority >= Message.PRIORITY_EXPEDITED, 0) and returned at the end.
♻️ Duplicate comments (3)
app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt (1)
80-91:⚠️ Potential issue | 🔴 Critical
setExpedited()combined withsetInitialDelay()will crash at runtime.WorkManager throws
IllegalArgumentException("Expedited jobs cannot be delayed")when bothsetExpedited()and a non-zerosetInitialDelay()are used together. Since scheduled messages will haveinitialDelay > 0, this will crash when attempting to schedule a future message.🐛 Proposed fix: Apply expedited only when delay is zero
fun start(context: Context, force: Boolean, scheduleAt: Long) { val initialDelay = max(0, scheduleAt - System.currentTimeMillis()) val work = OneTimeWorkRequestBuilder<SendMessagesWorker>() - .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setBackoffCriteria( BackoffPolicy.LINEAR, WorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS ) .setInitialDelay(initialDelay, TimeUnit.MILLISECONDS) + .apply { + if (initialDelay == 0L) { + setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + } + } .build()WorkManager setExpedited with setInitialDelay compatibility Android🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt` around lines 80 - 91, The start function in SendMessagesWorker currently always calls setExpedited(...) while also setting a non-zero initialDelay which will throw IllegalArgumentException; fix by applying expedited only when initialDelay == 0: compute initialDelay as you already do, create the OneTimeWorkRequestBuilder<SendMessagesWorker>(), and conditionally call setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) only if initialDelay == 0 (otherwise skip that call) before setting backoff criteria, setInitialDelay(initialDelay, TimeUnit.MILLISECONDS) and build the request so scheduled messages are not marked expedited.app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt (1)
44-48:⚠️ Potential issue | 🟡 MinorValidation allows
scheduleAtequal to current time.The condition
scheduleAt?.before(Date()) == trueonly rejects times strictly before now. IfscheduleAtequals the current time,before()returnsfalseand the validation passes. Per the PR objective ("scheduleAt must be in the future"), this edge case should also be rejected.🐛 Proposed fix
init { - if (scheduleAt?.before(Date()) == true) { + if (scheduleAt != null && !scheduleAt.after(Date())) { throw IllegalArgumentException("scheduleAt must be in the future") } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt` around lines 44 - 48, The init validation in PostMessageRequest currently allows scheduleAt equal to now because it only checks before(Date()); update the check to reject values that are not strictly after the current time (i.e., treat equal-to-now as invalid). Modify the init block around scheduleAt (in class PostMessageRequest) to detect when scheduleAt is non-null and not after the current Date (for example, using scheduleAt != null && !scheduleAt.after(Date()) or scheduleAt.time <= Date().time) and throw the same IllegalArgumentException("scheduleAt must be in the future") in that case.app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt (1)
107-111:⚠️ Potential issue | 🟠 MajorMissing reschedule for remaining scheduled messages after worker completes.
When the worker runs and processes all currently-due messages, it does not reschedule itself for the next pending scheduled message. Messages with
scheduleAtin the future will sit in the database until another external trigger (new message, app restart) starts the worker again.Consider adding reschedule logic at the end of
sendPendingMessages()to querydao.nextScheduledTime()and start a new worker with that delay if there are remaining scheduled messages.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt` around lines 107 - 111, After processing due messages in sendPendingMessages(), query dao.nextScheduledTime() and if it returns a future timestamp scheduleAt > System.currentTimeMillis(), call SendMessagesWorker.start(...) to schedule the worker for that nextScheduledTime so remaining scheduled messages are picked up; use the same start signature used elsewhere in this file (SendMessagesWorker.start(context, <priorityFlagOrEarliest>, scheduleAt)) and ensure you only reschedule when there are pending future messages returned by dao.nextScheduledTime().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt`:
- Around line 101-103: The computed earliest flag in MessagesService.kt is wrong
because dao.nextScheduledTime() returns 0 when no pending scheduled messages,
making the comparison it.time < 0 always false; update the logic where earliest
is computed (the val earliest using request.params.scheduleAt and
dao.nextScheduledTime()) to treat a 0/absent nextScheduledTime as "no next" by
substituting a sentinel like Long.MAX_VALUE (or checking == 0) before comparing,
e.g. compute val next = dao.nextScheduledTime() ?: 0L; if (next == 0L) consider
earliest = true; otherwise set earliest = it.time < next so the first scheduled
message correctly becomes the earliest and the worker scheduling
(ExistingWorkPolicy.KEEP) behaves as intended.
---
Outside diff comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt`:
- Around line 92-113: In enqueueMessage (MessagesService.enqueueMessage) remove
the second duplicate call to messages.enqueue(request) that appears after
computing earliest/scheduleAt; the message is already persisted by the first
try/catch block (which throws ConflictException on SQLiteConstraintException),
so delete the later messages.enqueue(request) and ensure the existing local
variable message (MessageWithRecipients) is used when scheduling via
SendMessagesWorker.start(context, earliest, scheduleAt) or
SendMessagesWorker.start(context, priority >= Message.PRIORITY_EXPEDITED, 0) and
returned at the end.
---
Duplicate comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt`:
- Around line 44-48: The init validation in PostMessageRequest currently allows
scheduleAt equal to now because it only checks before(Date()); update the check
to reject values that are not strictly after the current time (i.e., treat
equal-to-now as invalid). Modify the init block around scheduleAt (in class
PostMessageRequest) to detect when scheduleAt is non-null and not after the
current Date (for example, using scheduleAt != null && !scheduleAt.after(Date())
or scheduleAt.time <= Date().time) and throw the same
IllegalArgumentException("scheduleAt must be in the future") in that case.
In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt`:
- Around line 107-111: After processing due messages in sendPendingMessages(),
query dao.nextScheduledTime() and if it returns a future timestamp scheduleAt >
System.currentTimeMillis(), call SendMessagesWorker.start(...) to schedule the
worker for that nextScheduledTime so remaining scheduled messages are picked up;
use the same start signature used elsewhere in this file
(SendMessagesWorker.start(context, <priorityFlagOrEarliest>, scheduleAt)) and
ensure you only reschedule when there are pending future messages returned by
dao.nextScheduledTime().
In
`@app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt`:
- Around line 80-91: The start function in SendMessagesWorker currently always
calls setExpedited(...) while also setting a non-zero initialDelay which will
throw IllegalArgumentException; fix by applying expedited only when initialDelay
== 0: compute initialDelay as you already do, create the
OneTimeWorkRequestBuilder<SendMessagesWorker>(), and conditionally call
setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) only if
initialDelay == 0 (otherwise skip that call) before setting backoff criteria,
setInitialDelay(initialDelay, TimeUnit.MILLISECONDS) and build the request so
scheduled messages are not marked expedited.
🪄 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: 2686184a-c23a-4df7-8459-a8b9686fd107
📒 Files selected for processing (14)
app/schemas/me.capcom.smsgateway.data.AppDatabase/21.jsonapp/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/data/AppDatabase.ktapp/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.ktapp/src/main/java/me/capcom/smsgateway/data/entities/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.ktapp/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
✅ Files skipped from review due to trivial changes (5)
- app/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.kt
- app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
- app/src/main/assets/api/swagger.json
- app/schemas/me.capcom.smsgateway.data.AppDatabase/21.json
🚧 Files skipped from review as they are similar to previous changes (4)
- app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.kt
- app/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.kt
- app/src/main/java/me/capcom/smsgateway/data/entities/Message.kt
- app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt (1)
210-251:⚠️ Potential issue | 🔴 CriticalCritical: Reschedule logic is unreachable dead code.
The
while (true)loop at line 210 exits only viareturn(line 211:?: return). The reschedule logic at lines 242-251 is placed after the loop and will never execute. This means scheduled messages won't be picked up until the next external trigger (new message, app restart, etc.).🐛 Proposed fix: Move reschedule logic before the return
while (true) { - val message = messages.getPending(settings.processingOrder) ?: return + val message = messages.getPending(settings.processingOrder) ?: break delay(1L) // ... rest of loop unchanged } // After processing all pending messages, check if there are any scheduled messages for the future val nextScheduledTime = dao.nextScheduledTime() ?: 0 if (nextScheduledTime > System.currentTimeMillis()) { - // Determine if this is the earliest scheduled time - // Since we've just processed all pending messages, this should be the earliest - val earliest = true - - SendMessagesWorker.start(context, earliest, nextScheduledTime) + SendMessagesWorker.start(context, true, nextScheduledTime) } }Alternatively, inline the reschedule check before the
return:- val message = messages.getPending(settings.processingOrder) ?: return + val message = messages.getPending(settings.processingOrder) + if (message == null) { + val nextScheduledTime = dao.nextScheduledTime() ?: 0 + if (nextScheduledTime > System.currentTimeMillis()) { + SendMessagesWorker.start(context, true, nextScheduledTime) + } + return + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt` around lines 210 - 251, The reschedule block after the while(true) is dead because messages.getPending(settings.processingOrder) uses ?: return to exit the loop; move the scheduled-message check so it executes when getPending returns null before returning. Specifically, inside the processing loop where you call messages.getPending(... ) ?: return, replace that return with logic that calls dao.nextScheduledTime(), compares it to System.currentTimeMillis(), and if a future time exists call SendMessagesWorker.start(context, /* earliest= */ true, nextScheduledTime) (or compute earliest as needed) and then return; keep existing flow for applyLimit(), sendMessage(...), previousPriority and settings.sendIntervalRange unchanged. Ensure you reference messages.getPending, dao.nextScheduledTime, SendMessagesWorker.start, sendMessage and previousPriority when editing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt`:
- Around line 210-251: The reschedule block after the while(true) is dead
because messages.getPending(settings.processingOrder) uses ?: return to exit the
loop; move the scheduled-message check so it executes when getPending returns
null before returning. Specifically, inside the processing loop where you call
messages.getPending(... ) ?: return, replace that return with logic that calls
dao.nextScheduledTime(), compares it to System.currentTimeMillis(), and if a
future time exists call SendMessagesWorker.start(context, /* earliest= */ true,
nextScheduledTime) (or compute earliest as needed) and then return; keep
existing flow for applyLimit(), sendMessage(...), previousPriority and
settings.sendIntervalRange unchanged. Ensure you reference messages.getPending,
dao.nextScheduledTime, SendMessagesWorker.start, sendMessage and
previousPriority when editing.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7732ce19-8636-45d3-b80f-eeb4e9cda7d1
📒 Files selected for processing (2)
app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt
eba68bc to
38dcc15
Compare
38dcc15 to
7febb1e
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
app/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.kt (2)
317-355:⚠️ Potential issue | 🟠 MajorInclude
scheduleAtin local API responses.
toDomain()is used for POST, list, and detail responses, but it does not map the persisted scheduled timestamp. Scheduled messages will therefore be returned without theirscheduleAtvalue.🐛 Proposed response mapping
states = states.associate { it.state to Date(it.updatedAt) - } + }, + scheduleAt = message.scheduleAt )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.kt` around lines 317 - 355, The toDomain() mapper for MessageWithRecipients does not populate the domain Message.scheduleAt, so scheduled messages come back without their timestamp; update MessageWithRecipients.toDomain (used for POST/list/detail) to set the domain Message's scheduleAt by mapping the persisted value (e.g., message.scheduleAt or message.scheduledAt) into the domain type (convert to Date if needed) when constructing me.capcom.smsgateway.modules.localserver.domain.messages.Message so responses include the scheduled timestamp.
242-258:⚠️ Potential issue | 🟠 MajorReject non-future
scheduleAtbefore enqueueing.This handler now passes
request.scheduleAtintoSendParams, but it never rejects timestamps that are already due or in the past, so the local API can accept requests that violate the scheduling contract.🐛 Proposed validation
if (request.simNumber != null && request.simNumber < 1) { call.respond( HttpStatusCode.BadRequest, mapOf("message" to "simNumber must be >= 1") ) return@post } + request.scheduleAt?.let { scheduleAt -> + if (!scheduleAt.after(Date())) { + call.respond( + HttpStatusCode.BadRequest, + mapOf("message" to "scheduleAt must be in the future") + ) + return@post + } + } val skipPhoneValidation = call.request.queryParameters["skipPhoneValidation"] ?.toBooleanStrict() ?: false🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.kt` around lines 242 - 258, The code builds a SendRequest with request.scheduleAt but never validates that scheduleAt is in the future; add a guard in the request handler (where SendRequest is constructed in MessagesRoutes.kt) to check if request.scheduleAt != null and request.scheduleAt <= now, and if so respond with a 400 Bad Request (or equivalent error flow) rejecting the request before creating SendRequest or enqueueing; use the same time type used elsewhere (Date/Instant) to compare against current time, and reference SendRequest, SendParams and the handler code that currently constructs them to locate where to add the validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.kt`:
- Around line 317-355: The toDomain() mapper for MessageWithRecipients does not
populate the domain Message.scheduleAt, so scheduled messages come back without
their timestamp; update MessageWithRecipients.toDomain (used for
POST/list/detail) to set the domain Message's scheduleAt by mapping the
persisted value (e.g., message.scheduleAt or message.scheduledAt) into the
domain type (convert to Date if needed) when constructing
me.capcom.smsgateway.modules.localserver.domain.messages.Message so responses
include the scheduled timestamp.
- Around line 242-258: The code builds a SendRequest with request.scheduleAt but
never validates that scheduleAt is in the future; add a guard in the request
handler (where SendRequest is constructed in MessagesRoutes.kt) to check if
request.scheduleAt != null and request.scheduleAt <= now, and if so respond with
a 400 Bad Request (or equivalent error flow) rejecting the request before
creating SendRequest or enqueueing; use the same time type used elsewhere
(Date/Instant) to compare against current time, and reference SendRequest,
SendParams and the handler code that currently constructs them to locate where
to add the validation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b4e0fcff-d145-4ec1-be6a-54a7f57463cf
📒 Files selected for processing (14)
app/schemas/me.capcom.smsgateway.data.AppDatabase/21.jsonapp/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/data/AppDatabase.ktapp/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.ktapp/src/main/java/me/capcom/smsgateway/data/entities/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.ktapp/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
✅ Files skipped from review due to trivial changes (2)
- app/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
- app/src/main/assets/api/swagger.json
🚧 Files skipped from review as they are similar to previous changes (8)
- app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.kt
- app/src/main/java/me/capcom/smsgateway/data/entities/Message.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt
- app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
- app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.kt
0e8b02a to
705522a
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt (1)
90-93:⚠️ Potential issue | 🟠 MajorKeep constructor validation strictly consistent with
validate().Line 91 still allows
scheduleAt == now, while Line 83 rejects it. Centralize the check or use the same<= System.currentTimeMillis()comparison in both places so callers that construct the DTO directly cannot bypass the strict future requirement.🐛 Proposed fix
init { - if (scheduleAt?.before(Date()) == true) { + if (scheduleAt != null && scheduleAt.time <= System.currentTimeMillis()) { throw IllegalArgumentException("scheduleAt must be in the future") } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt` around lines 90 - 93, The init block in PostMessageRequest currently checks scheduleAt?.before(Date()) allowing scheduleAt == now while validate() uses a stricter comparison; update the constructor/init to use the same strict future check as validate() (e.g., compare scheduleAt.time <= System.currentTimeMillis()) or centralize the validation into the validate() method and call it from the init so both construction and validate() enforce identical "must be in the future" logic; locate PostMessageRequest, its init block and the validate() method to make the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt`:
- Around line 83-85: PostMessageRequest currently only checks scheduleAt is in
the future but not that it occurs before message expiry; update the validation
inside PostMessageRequest (check where scheduleAt is validated) to also reject
scheduleAt that is after the message's expiry by comparing scheduleAt against
the request's expiration parameters (use validUntil if present, otherwise
compute expiry from ttl) and throw an IllegalArgumentException (e.g.,
"scheduleAt must be before message expiry") when scheduleAt is >= expiry;
reference the scheduleAt, validUntil, ttl fields and the PostMessageRequest
validation block so the check covers both expiry semantics.
---
Duplicate comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt`:
- Around line 90-93: The init block in PostMessageRequest currently checks
scheduleAt?.before(Date()) allowing scheduleAt == now while validate() uses a
stricter comparison; update the constructor/init to use the same strict future
check as validate() (e.g., compare scheduleAt.time <=
System.currentTimeMillis()) or centralize the validation into the validate()
method and call it from the init so both construction and validate() enforce
identical "must be in the future" logic; locate PostMessageRequest, its init
block and the validate() method to make 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: a8333077-cb20-4f6b-bc87-d634b8d3c8b9
📒 Files selected for processing (15)
app/schemas/me.capcom.smsgateway.data.AppDatabase/21.jsonapp/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/data/AppDatabase.ktapp/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.ktapp/src/main/java/me/capcom/smsgateway/data/entities/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.ktapp/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
✅ Files skipped from review due to trivial changes (6)
- app/src/main/assets/api/swagger.json
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.kt
- app/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.kt
- app/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt
- app/schemas/me.capcom.smsgateway.data.AppDatabase/21.json
🚧 Files skipped from review as they are similar to previous changes (6)
- app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/Message.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.kt
- app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
- app/src/main/java/me/capcom/smsgateway/data/entities/Message.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt
- app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt
705522a to
9c91e3d
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt (2)
81-81: Nit: replace the magic0for "no scheduling" with a named constant or overload.
SendMessagesWorker.start(context, false, 0)andSendMessagesWorker.start(context, ..., 0)repeat a literal whose meaning ("run as soon as constraints allow") isn't obvious at the call site. Either anIMMEDIATE = 0Lconstant on the worker, a default parameter (scheduleAt: Long = 0L), or a small overloadstart(context, force)would make the intent explicit at all three call sites.Also applies to: 106-110
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt` at line 81, Call sites use the magic literal 0 for immediate scheduling in SendMessagesWorker.start(context, ..., 0); replace this with a named constant or overload to make intent explicit — e.g., add IMMEDIATE_SCHEDULE = 0L (or IMMEDIATE = 0L) as a public constant on SendMessagesWorker and update calls to SendMessagesWorker.start(context, false, SendMessagesWorker.IMMEDIATE), or add a default parameter scheduleAt: Long = IMMEDIATE and/or an overload start(context, force: Boolean) that delegates to start(context, force, IMMEDIATE); update all occurrences (including the other start calls) to use the new constant/overload.
207-250: Reschedule infinallywill fire even on cancellation — consider scoping it.Wrapping the loop in
try { ... } finally { ... SendMessagesWorker.start(...) }means the reschedule runs on every exit path, includingCancellationException(when WorkManager cancels the job). Thefinallyblock callsstart(context, true, nextScheduledTime)withforce=true, which usesExistingWorkPolicy.REPLACE—immediately replacing a worker that was just cancelled. Meanwhile,doWork()catches theCancellationExceptionand returnsResult.retry(), creating a potential reschedule+retry loop.While
ExistingWorkPolicy.REPLACEwith backoff mitigation prevents cascading, the logic is loose. Two safer alternatives:
- Only reschedule on normal completion / non-cancel exceptions:
try { // ... loop } catch (e: CancellationException) { throw e } finally { if (coroutineContext[Job]?.isActive == true) { val nextScheduledTime = dao.nextScheduledTime() ?: 0 if (nextScheduledTime > System.currentTimeMillis()) { SendMessagesWorker.start(context, true, nextScheduledTime) } } }- Move the reschedule out of
finallyand into the worker's normal post-success path (inSendMessagesWorker.doWork()aftersendPendingMessages()returns cleanly), so it only runs when the worker completes without cancellation.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt` around lines 207 - 250, The finally block in sendPendingMessages currently always calls SendMessagesWorker.start(...) (using dao.nextScheduledTime()) even when the coroutine was cancelled; change this so rescheduling only happens on normal completion: either (A) catch CancellationException and rethrow, then in finally check coroutineContext[Job]?.isActive (or a local flag set on normal exit) before calling SendMessagesWorker.start, or (B) remove the reschedule from sendPendingMessages and instead invoke SendMessagesWorker.start(...) from the caller (e.g., SendMessagesWorker.doWork()) after sendPendingMessages returns successfully; reference sendPendingMessages, SendMessagesWorker.start, dao.nextScheduledTime and doWork to locate where to apply the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt`:
- Around line 83-94: Remove the redundant init block that performs the same
scheduleAt check as validate(); specifically delete the init { if
(scheduleAt?.before(Date()) == true) { throw
IllegalArgumentException("scheduleAt must be in the future") } } so only
validate() enforces the rule (it uses scheduleAt?.after(Date()) == false which
is the intended stricter semantics); keep validate(), scheduleAt property and
callers such as MessagesRoutes.kt that call .validate() and ensure no other
implicit initialization checks remain.
---
Nitpick comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt`:
- Line 81: Call sites use the magic literal 0 for immediate scheduling in
SendMessagesWorker.start(context, ..., 0); replace this with a named constant or
overload to make intent explicit — e.g., add IMMEDIATE_SCHEDULE = 0L (or
IMMEDIATE = 0L) as a public constant on SendMessagesWorker and update calls to
SendMessagesWorker.start(context, false, SendMessagesWorker.IMMEDIATE), or add a
default parameter scheduleAt: Long = IMMEDIATE and/or an overload start(context,
force: Boolean) that delegates to start(context, force, IMMEDIATE); update all
occurrences (including the other start calls) to use the new constant/overload.
- Around line 207-250: The finally block in sendPendingMessages currently always
calls SendMessagesWorker.start(...) (using dao.nextScheduledTime()) even when
the coroutine was cancelled; change this so rescheduling only happens on normal
completion: either (A) catch CancellationException and rethrow, then in finally
check coroutineContext[Job]?.isActive (or a local flag set on normal exit)
before calling SendMessagesWorker.start, or (B) remove the reschedule from
sendPendingMessages and instead invoke SendMessagesWorker.start(...) from the
caller (e.g., SendMessagesWorker.doWork()) after sendPendingMessages returns
successfully; reference sendPendingMessages, SendMessagesWorker.start,
dao.nextScheduledTime and doWork to locate where to apply 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: 67ea2955-0231-41f6-a4d6-f1d19ab6ce30
📒 Files selected for processing (15)
app/schemas/me.capcom.smsgateway.data.AppDatabase/21.jsonapp/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/data/AppDatabase.ktapp/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.ktapp/src/main/java/me/capcom/smsgateway/data/entities/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.ktapp/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
✅ Files skipped from review due to trivial changes (5)
- app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/Message.kt
- app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.kt
- app/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
- app/src/main/assets/api/swagger.json
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.kt
🚧 Files skipped from review as they are similar to previous changes (5)
- app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.kt
- app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
- app/src/main/java/me/capcom/smsgateway/data/entities/Message.kt
- app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt`:
- Around line 212-257: The finally block in sendPendingMessages() reschedules
work even after cancellation; remove the redundant catch(CancellationException)
and gate the reschedule so it only runs when the coroutine completed normally
(i.e., not cancelled). Concretely, import kotlin.coroutines.coroutineContext,
remove the catch block, and in the finally of sendPendingMessages() check
coroutineContext.isActive (or !coroutineContext.isActive to skip) before calling
SendMessagesWorker.start(...); this ensures SendMessagesWorker.start is not
invoked when stop()/cancellation occurred.
🪄 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: eb1bfa13-5c80-4e18-9842-728a56ac1bb5
📒 Files selected for processing (3)
app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt
ebd0f17 to
3f7afde
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt (1)
79-107:⚠️ Potential issue | 🟠 MajorSeparate immediate and scheduled WorkRequests, or always REPLACE for IMMEDIATE
Immediate and scheduled requests share the same unique name (
SendMessagesWorker), soExistingWorkPolicy.KEEPdrops non-expedited IMMEDIATE tasks if a scheduled job is pending. Choose one:
- Always REPLACE when
initialDelay == 0L:🛠 Minimal fix diff
fun start(context: Context, force: Boolean, scheduleAt: Long) { val initialDelay = max(0, scheduleAt - System.currentTimeMillis()) … WorkManager.getInstance(context) .enqueueUniqueWork( NAME, - when (force) { - true -> ExistingWorkPolicy.REPLACE - false -> ExistingWorkPolicy.KEEP - }, + when { + force || initialDelay == 0L -> ExistingWorkPolicy.REPLACE + else -> ExistingWorkPolicy.KEEP + }, work ) }
- Or introduce a separate
SCHEDULED_NAMEfor scheduled runs to isolate the two queues.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt` around lines 79 - 107, The current start function in SendMessagesWorker uses a single unique NAME for both immediate (initialDelay == 0L) and scheduled runs which can drop expedited IMMEDIATE runs when a scheduled job exists; update start to distinguish immediate vs scheduled runs by either (A) when initialDelay == 0L (IMMEDIATE) always use ExistingWorkPolicy.REPLACE for the enqueueUniqueWork call so expedited work replaces existing scheduled work, or (B) introduce a second constant SCHEDULED_NAME and use NAME for immediate runs and SCHEDULED_NAME for delayed runs so scheduled and immediate queues are isolated; implement the chosen approach inside start where initialDelay is computed and before calling WorkManager.getInstance(context).enqueueUniqueWork.
🧹 Nitpick comments (1)
app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt (1)
81-96: Minor:scheduleAtparameter is ambiguous when callers passIMMEDIATE(0L).Using a sentinel
0Lto mean "run now" works because ofmax(0, scheduleAt - now), but it's easy to misuse (e.g., accidentally passing a past timestamp yields the same effect as IMMEDIATE without the caller realizing). Consider a nullablescheduleAt: Long?wherenullmeans immediate, or two entry points (start(...)andstartAt(..., scheduleAt)), to make the contract explicit at call sites inMessagesService.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt` around lines 81 - 96, The start(...) method's scheduleAt: Long parameter is ambiguous when callers pass 0L to mean "IMMEDIATE"; change the API to make intent explicit by either (A) making scheduleAt nullable (scheduleAt: Long?) where null means immediate and update SendMessagesWorker.start to compute initialDelay when scheduleAt != null, or (B) add a separate entrypoint startNow(context, force) and keep startAt(context, force, scheduleAt: Long) for scheduled runs; update all callers in MessagesService to use the clearer method/nullable contract and adjust the logic in OneTimeWorkRequestBuilder usage and the initialDelay calculation accordingly so past timestamps and explicit immediate requests are distinguishable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt`:
- Around line 59-60: nextScheduledTime() loses milliseconds because
strftime('%s', ...) yields whole seconds; update the `@Query` to compute epoch
milliseconds using a millisecond-preserving expression (e.g. convert
julianday(scheduleAt) to epoch ms via (julianday(scheduleAt) - 2440587.5) *
86400000) and remove COALESCE nullability handling if you keep COALESCE 0 then
change the Kotlin return type from Long? to Long to reflect non-null; locate the
SQL in the MessagesDao.nextScheduledTime() definition and adjust both the SQL
expression and the function signature accordingly.
---
Outside diff comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt`:
- Around line 79-107: The current start function in SendMessagesWorker uses a
single unique NAME for both immediate (initialDelay == 0L) and scheduled runs
which can drop expedited IMMEDIATE runs when a scheduled job exists; update
start to distinguish immediate vs scheduled runs by either (A) when initialDelay
== 0L (IMMEDIATE) always use ExistingWorkPolicy.REPLACE for the
enqueueUniqueWork call so expedited work replaces existing scheduled work, or
(B) introduce a second constant SCHEDULED_NAME and use NAME for immediate runs
and SCHEDULED_NAME for delayed runs so scheduled and immediate queues are
isolated; implement the chosen approach inside start where initialDelay is
computed and before calling WorkManager.getInstance(context).enqueueUniqueWork.
---
Nitpick comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.kt`:
- Around line 81-96: The start(...) method's scheduleAt: Long parameter is
ambiguous when callers pass 0L to mean "IMMEDIATE"; change the API to make
intent explicit by either (A) making scheduleAt nullable (scheduleAt: Long?)
where null means immediate and update SendMessagesWorker.start to compute
initialDelay when scheduleAt != null, or (B) add a separate entrypoint
startNow(context, force) and keep startAt(context, force, scheduleAt: Long) for
scheduled runs; update all callers in MessagesService to use the clearer
method/nullable contract and adjust the logic in OneTimeWorkRequestBuilder usage
and the initialDelay calculation accordingly so past timestamps and explicit
immediate requests are distinguishable.
🪄 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: bcbb5ac5-3028-47d2-b158-b774e7bb33bd
📒 Files selected for processing (15)
app/schemas/me.capcom.smsgateway.data.AppDatabase/21.jsonapp/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/data/AppDatabase.ktapp/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.ktapp/src/main/java/me/capcom/smsgateway/data/entities/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.ktapp/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
✅ Files skipped from review due to trivial changes (5)
- app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/Message.kt
- app/src/main/assets/api/swagger.json
- app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
- app/schemas/me.capcom.smsgateway.data.AppDatabase/21.json
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.kt
🚧 Files skipped from review as they are similar to previous changes (6)
- app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.kt
- app/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.kt
- app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt
- app/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt (1)
97-100:⚠️ Potential issue | 🟠 MajorTreat “no existing scheduled message” as earliest.
When
dao.nextScheduledTime()returns0, this currently setsearliest = false, so the first scheduled message is not treated as the earliest scheduled work item.🐛 Proposed fix
val earliest = request.params.scheduleAt?.let { val nextScheduled = dao.nextScheduledTime() ?: 0 - nextScheduled != 0L && it.time < nextScheduled + nextScheduled == 0L || it.time < nextScheduled } ?: true🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt` around lines 97 - 100, The logic computing earliest in MessagesService is treating nextScheduledTime() == 0 as non-earliest; change it so "no existing scheduled message" (nextScheduled == 0L) counts as earliest. Update the block that sets earliest (using request.params.scheduleAt and dao.nextScheduledTime()) to return true when scheduleAt is null OR when nextScheduled == 0L, otherwise compare it.time < nextScheduled; reference the variables earliest, request.params.scheduleAt and dao.nextScheduledTime() to locate and fix the condition.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt`:
- Around line 249-255: The final reschedule check currently only enqueues a
worker when dao.nextScheduledTime() > System.currentTimeMillis(), which drops
work that became due in the gap; change the logic to schedule whenever
dao.nextScheduledTime() != 0 and treat times <= System.currentTimeMillis() as
"due now" (i.e., call SendMessagesWorker.start(...) so it runs immediately when
nextScheduledTime <= now, otherwise schedule for the future), updating the call
site that uses coroutineContext, dao.nextScheduledTime() and
SendMessagesWorker.start(context, ...) accordingly.
---
Duplicate comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt`:
- Around line 97-100: The logic computing earliest in MessagesService is
treating nextScheduledTime() == 0 as non-earliest; change it so "no existing
scheduled message" (nextScheduled == 0L) counts as earliest. Update the block
that sets earliest (using request.params.scheduleAt and dao.nextScheduledTime())
to return true when scheduleAt is null OR when nextScheduled == 0L, otherwise
compare it.time < nextScheduled; reference the variables earliest,
request.params.scheduleAt and dao.nextScheduledTime() to locate and fix the
condition.
🪄 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: 210aa9b0-67c1-4aef-8a4b-b3e7e34d20f3
📒 Files selected for processing (1)
app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt
1a49600 to
98b34bb
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (2)
app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt (1)
59-60:⚠️ Potential issue | 🟡 Minor
nextScheduledTime()still loses millisecond precision and has a misleading nullable return type.
strftime('%s', MIN(scheduleAt)) * 1000truncates the.SSSfragment from the stored ISO-8601 string, so a value like2026-04-15T10:30:45.567Zis returned as1744713045000(not1744713045567).MessagesServicecompares this directly toDate.time/System.currentTimeMillis()(full ms) both inenqueueMessage(for theearliestdecision) and in thesendPendingMessagesfinally block (for the next-reschedule decision), so messages scheduled within the same second as an existing earliest can be misclassified and trigger unnecessary (or incorrect) worker re-scheduling.Additionally,
COALESCE(..., 0)guarantees a non-null result, so declaring the return type asLong?is misleading.♻️ Suggested query preserving milliseconds
- `@Query`("SELECT COALESCE(strftime('%s', MIN(`scheduleAt`)) * 1000, 0) FROM message WHERE state = 'Pending'") - fun nextScheduledTime(): Long? + `@Query`( + """ + SELECT COALESCE( + CAST((julianday(MIN(`scheduleAt`)) - 2440587.5) * 86400000 AS INTEGER), + 0 + ) + FROM message + WHERE state = 'Pending' AND `scheduleAt` IS NOT NULL + """ + ) + fun nextScheduledTime(): LongAlternatively, consider storing
scheduleAtas an INTEGER epoch-ms via the TypeConverter to avoid ISO-8601 arithmetic entirely.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt` around lines 59 - 60, nextScheduledTime() loses millisecond precision and declares a nullable return even though COALESCE forces a value; update the DAO query used by nextScheduledTime() to compute epoch milliseconds without truncating the fractional seconds (so MessagesService comparisons in enqueueMessage and sendPendingMessages use full ms precision) and change the method signature to return Long (non-nullable). You can do this by modifying the SQL used in nextScheduledTime() to include the fractional seconds when converting MIN(scheduleAt) to epoch-ms (or switch scheduleAt to stored INTEGER epoch-ms via a TypeConverter) and ensure the function name nextScheduledTime() is updated to return Long instead of Long?.app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt (1)
249-256:⚠️ Potential issue | 🟡 MinorScheduled messages that become due during the final reschedule can be stranded.
The guard
nextScheduledTime > System.currentTimeMillis()drops any row whosescheduleAtbecame<= nowbetween the loop's finalgetPending(...)returningnulland this check. Such a message will not be re-enqueued here and will sitPendinguntil an external trigger (new enqueue, app restart, etc.) fires the worker again. Given the ms-precision truncation still present indao.nextScheduledTime(), the race window is effectively at least ~1 second.🛠️ Suggested change
} finally { if (coroutineContext.isActive) { - // After processing all pending messages, check if there are any scheduled messages for the future val nextScheduledTime = dao.nextScheduledTime() ?: 0 - if (nextScheduledTime > System.currentTimeMillis()) { - SendMessagesWorker.start(context, true, nextScheduledTime) + if (nextScheduledTime != 0L) { + val now = System.currentTimeMillis() + SendMessagesWorker.start( + context, + true, + if (nextScheduledTime > now) nextScheduledTime else SendMessagesWorker.IMMEDIATE + ) } } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt` around lines 249 - 256, The final reschedule drops rows that became due between getPending and the check because you only reschedule when nextScheduledTime > System.currentTimeMillis(); change the logic in the finally block (referencing coroutineContext.isActive, dao.nextScheduledTime(), and SendMessagesWorker.start) so that when dao.nextScheduledTime() returns a non-zero time you always re-arm the worker: if the time is <= System.currentTimeMillis() start the worker to process immediately, otherwise schedule it for the future timestamp; this ensures messages that became due during the race are not stranded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@app/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.kt`:
- Around line 59-60: nextScheduledTime() loses millisecond precision and
declares a nullable return even though COALESCE forces a value; update the DAO
query used by nextScheduledTime() to compute epoch milliseconds without
truncating the fractional seconds (so MessagesService comparisons in
enqueueMessage and sendPendingMessages use full ms precision) and change the
method signature to return Long (non-nullable). You can do this by modifying the
SQL used in nextScheduledTime() to include the fractional seconds when
converting MIN(scheduleAt) to epoch-ms (or switch scheduleAt to stored INTEGER
epoch-ms via a TypeConverter) and ensure the function name nextScheduledTime()
is updated to return Long instead of Long?.
In `@app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.kt`:
- Around line 249-256: The final reschedule drops rows that became due between
getPending and the check because you only reschedule when nextScheduledTime >
System.currentTimeMillis(); change the logic in the finally block (referencing
coroutineContext.isActive, dao.nextScheduledTime(), and
SendMessagesWorker.start) so that when dao.nextScheduledTime() returns a
non-zero time you always re-arm the worker: if the time is <=
System.currentTimeMillis() start the worker to process immediately, otherwise
schedule it for the future timestamp; this ensures messages that became due
during the race are not stranded.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7ad6d8be-d453-4e72-bfda-ad9bc25d5668
📒 Files selected for processing (15)
app/schemas/me.capcom.smsgateway.data.AppDatabase/21.jsonapp/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/data/AppDatabase.ktapp/src/main/java/me/capcom/smsgateway/data/dao/MessagesDao.ktapp/src/main/java/me/capcom/smsgateway/data/entities/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.ktapp/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/Message.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.ktapp/src/main/java/me/capcom/smsgateway/modules/localserver/routes/MessagesRoutes.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/MessagesService.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/data/SendParams.ktapp/src/main/java/me/capcom/smsgateway/modules/messages/workers/SendMessagesWorker.ktapp/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
✅ Files skipped from review due to trivial changes (4)
- app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/Message.kt
- app/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.kt
- app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
- app/schemas/me.capcom.smsgateway.data.AppDatabase/21.json
🚧 Files skipped from review as they are similar to previous changes (3)
- app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.kt
- app/src/main/java/me/capcom/smsgateway/modules/messages/MessagesRepository.kt
- app/src/main/java/me/capcom/smsgateway/modules/localserver/domain/messages/PostMessageRequest.kt
Motivation
scheduleAttimestamp throughout the stack.Description
scheduleAt: Date?to the public API, Swagger (swagger.json), README examples, local server request model (PostMessageRequest) and validation thatscheduleAtmust be in the future.scheduleAtonMessage(added DB index), propagate it throughSendParams,MessagesRepository,GatewayApi/GatewayService, and enqueue logic.(scheduleAt IS NULL OR scheduleAt <= :now)and updategetPendingLifo/getPendingFifoto accept aDate nowparameter.SendMessagesWorker(startAt/SCHEDULED_NAME) and updateMessagesService.enqueueMessageto usestartAtwhenscheduleAtis in the future; bump Room DB version to20and add auto-migration entry.Messageconstruction inMessageWithRecipientsTest.Testing
app/src/test/java/me/capcom/smsgateway/data/entities/MessageWithRecipientsTest.ktto initializescheduleAtand ran unit tests with./gradlew test, which completed successfully../gradlew assembleDebugto validate compilation after the schema and API changes and the build succeeded.Codex Task
Summary by CodeRabbit
New Features
Database
Behavior
Tests