feat: add /scheduleorder and /cancelschedule for recurring auto-republishing orders#849
feat: add /scheduleorder and /cancelschedule for recurring auto-republishing orders#849Matobi98 wants to merge 4 commits into
Conversation
…rders Creates a new ScheduledOrder model that acts as a mold: each cron trigger publishes a brand-new Order with its own id. No order lifecycle is mutated. Taking a scheduled order resets the cycle counter and immediately republishes from the mold so the trader's offer stays live without manual intervention.
WalkthroughAdds a full recurring order scheduling feature: a ChangesScheduled Recurring Orders
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 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: 15
🧹 Nitpick comments (1)
tests/bot/bot.spec.ts (1)
466-466: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the scheduled-order cron explicitly.
This only checks the total number of jobs. The test still passes if some unrelated job is added while the
publishScheduledOrdersregistration disappears or its cron expression changes.🧪 Tighten the assertion
- expect(scheduleStub.scheduleJob.callCount).to.be.equal(9); + expect(scheduleStub.scheduleJob.callCount).to.equal(9); + expect( + scheduleStub.scheduleJob + .getCalls() + .some(call => call.args[0] === '0 * * * *'), + ).to.equal(true);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/bot/bot.spec.ts` at line 466, The test currently only verifies the total number of scheduled jobs, so it can miss regressions in the `publishScheduledOrders` registration. Update the assertion in `bot.spec.ts` to explicitly check the `scheduleJob` call for `publishScheduledOrders`, including its cron expression and identifying callback/argument, so the test fails if that schedule is removed or changed even when the overall call count stays the same.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bot/modules/orders/takeOrder.ts`:
- Around line 98-100: The take flow in takeOrder.ts is being delayed by awaiting
scheduled-order republishing before sending the taker’s next-step message. In
the take-buy and take-sell paths around refreshScheduledOrder and
messages.beginTakeBuyMessage / messages.beginTakeSellMessage, start
refreshScheduledOrder in parallel without awaiting it, or move it after the
beginTake*Message call so Telegram/channel publish latency does not block the
active trade flow.
- Around line 200-204: The schedule update in takeOrder is happening
unconditionally after publishFn, even when the order was not actually published;
update the schedule only when the new order has been successfully published. Add
a guard in takeOrder around the schedule.last_order_id and
schedule.republish_count assignments, using the published state returned or set
by publishFn/newOrder before calling schedule.save. Keep the fix localized to
the publish-and-save flow in takeOrder so failed or skipped publishes do not
advance the schedule.
In `@bot/modules/schedule/commands.ts`:
- Around line 1-6: The schedule cancellation flow should validate scheduleId
before calling ScheduledOrder.findOne, since a non-ObjectId value can throw and
currently only gets logged without replying to the user. Add a guard using
Types.ObjectId.isValid(scheduleId) in the command path in commands.ts before the
query, and return an appropriate user-facing message when the id is invalid so
/cancelschedule cannot fail silently.
In `@bot/modules/schedule/helpers.ts`:
- Around line 8-58: parseCustomDays is currently hardcoded to English/Spanish
weekday aliases, so localized wizard prompts still reject valid user input in
other locales. Update the weekday parsing in helpers.ts by either making
DAY_ALIASES locale-aware (driven by the active locale or translated weekday
names) or switching the custom day input flow to a locale-neutral format, and
keep the parser logic in parseCustomDays aligned with whatever format the
schedule wizard expects.
In `@bot/modules/schedule/messages.ts`:
- Around line 3-15: formatDays() is hardcoding English weekday abbreviations, so
the confirmation summary is not localized. Update the DAY_LABELS mapping in
messages.ts to read weekday names from i18n/locales instead of static strings,
and make sure the caller still uses formatDays(days) unchanged. Keep the
localized labels aligned with the existing confirmation text flow and add/update
the matching entries under locales/ for each weekday.
In `@bot/modules/schedule/scenes.ts`:
- Around line 83-88: The schedule wizard hour parsing is too permissive because
scenes.ts uses parseInt on ctx.message text, which accepts inputs like 7pm,
12.5, and 0x10. Tighten the validation in the hour-handling flow so only a
strict integer string in the 0–23 range is accepted before setting state.hour,
and keep the invalid_hour reply path in the same scene logic.
In `@jobs/scheduled_orders.ts`:
- Around line 48-75: The scheduled order flow in the job handler should only
advance the schedule when a post is actually published successfully. Update the
logic around createOrder and the publishBuyOrderMessage/publishSellOrderMessage
call so you capture an explicit publish-success result, and only then set
last_order_id, decrement republish_count, and possibly deactivate the schedule.
Also reserve the current UTC slot before calling the publish helper, and make
sure a failed schedule.save() does not leave the order eligible to be published
again on the next run.
In `@locales/de.yaml`:
- Around line 264-265: The help text for the schedule order command is
translating the required buy|sell argument, which breaks the command handler’s
expected input. Update the localized usage strings in the entries for the
schedule-order command so the argument remains exactly buy|sell while keeping
the surrounding German text translated. Make the same correction in the
duplicate occurrence mentioned in the comment, and verify the command names and
placeholders in the help output still match what the wizard/parser expects.
In `@locales/es.yaml`:
- Around line 265-266: The Spanish command help text is using translated
placeholders for the first argument, but bot/modules/schedule/commands.ts only
accepts the literal values buy or sell, so update the schedule command syntax in
locales/es.yaml to keep buy|sell unchanged while leaving the rest of the
localized text intact. Make the same correction anywhere else in the Spanish
schedule help entries that mirror this syntax so users are shown the exact
values accepted by the command parser.
In `@locales/fa.yaml`:
- Line 317: The help text for the `/scheduleorder` command contains a malformed
optional premium token, so update the string in the locale entry to use a
properly closed optional bracketed token for the premium argument. Locate the
command usage text in the `fa.yaml` locale and correct the `[_پریمیوم_>`
fragment so the optional parameter syntax is valid and consistent with the other
placeholders.
In `@locales/fr.yaml`:
- Line 266: The `/scheduleorder` help text is using translated action tokens,
but the command handler only accepts the literal `buy|sell` values. Update the
relevant `/scheduleorder` entries in the locale definitions to document
`buy|sell` instead of `acheter|vendre`, keeping the rest of the argument
placeholders unchanged so the displayed usage matches the parser expected by the
schedule order handler.
- Around line 741-742: The weekday prompts in the French locale are using
examples that the schedule parser does not accept. Update the messages for
schedule_enter_custom_days and invalid_days in locales/fr.yaml to use weekday
aliases supported by bot/modules/schedule/helpers.ts, or add matching French
aliases in that parser so the examples and validation are aligned.
In `@locales/it.yaml`:
- Line 264: The localized `/scheduleorder` help text is using translated
arguments that do not match what `bot/modules/schedule/commands.ts` actually
parses. Update the `/scheduleorder` usage string in the locale entry to keep the
action argument as `buy|sell` (including the other affected locale occurrence)
so the help output matches the command’s accepted values and users can copy it
correctly.
In `@locales/pt.yaml`:
- Line 265: The `/scheduleorder` help text in the Portuguese locale is
advertising unsupported argument names, which makes the command docs
inconsistent with the parser. Update the localized string for the schedule order
entry in the locale file to use the same `buy|sell` arguments that the command
handler recognizes, and make the same correction in the other referenced
occurrence so the help text stays aligned with the actual parser.
In `@models/scheduled_order.ts`:
- Around line 3-5: The scheduled order typing still uses an `extends Document`
interface with `_id: string`, which should be updated consistently with the
other model typings to fix the Mongoose 8 TS2430 issue. Refactor
`IScheduledOrder` to avoid directly extending `Document`, and instead use the
same pattern applied in `models/user.ts`, `models/order.ts`, and
`models/community.ts`—either a plain interface paired with `HydratedDocument`,
or a `Document` generic approach that types `_id` consistently across all
models.
---
Nitpick comments:
In `@tests/bot/bot.spec.ts`:
- Line 466: The test currently only verifies the total number of scheduled jobs,
so it can miss regressions in the `publishScheduledOrders` registration. Update
the assertion in `bot.spec.ts` to explicitly check the `scheduleJob` call for
`publishScheduledOrders`, including its cron expression and identifying
callback/argument, so the test fails if that schedule is removed or changed even
when the overall call count stays the same.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: ce3d73c9-7d9c-4d07-84ff-ec93c2b72026
📒 Files selected for processing (23)
bot/middleware/stage.tsbot/modules/orders/takeOrder.tsbot/modules/schedule/commands.tsbot/modules/schedule/helpers.tsbot/modules/schedule/index.tsbot/modules/schedule/messages.tsbot/modules/schedule/scenes.tsbot/start.tsjobs/index.tsjobs/scheduled_orders.tslocales/de.yamllocales/en.yamllocales/es.yamllocales/fa.yamllocales/fr.yamllocales/it.yamllocales/ko.yamllocales/pt.yamllocales/ru.yamllocales/uk.yamlmodels/index.tsmodels/scheduled_order.tstests/bot/bot.spec.ts
| await refreshScheduledOrder(order._id, bot); | ||
|
|
||
| await messages.beginTakeBuyMessage(ctx, bot, user, order); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Don’t block the take flow on scheduled republishing.
Line 98 and Line 151 await a DB create plus Telegram channel publish before sending the taker’s next-step message. Start the refresh in parallel or move it after beginTake*Message so channel-publish latency does not delay the active trade flow.
Suggested ordering
await deleteOrderFromChannel(order, bot.telegram);
- await refreshScheduledOrder(order._id, bot);
-
+ const refreshPromise = refreshScheduledOrder(order._id, bot);
await messages.beginTakeBuyMessage(ctx, bot, user, order);
+ await refreshPromise; await deleteOrderFromChannel(order, bot.telegram);
- await refreshScheduledOrder(order._id, bot);
+ const refreshPromise = refreshScheduledOrder(order._id, bot);
await messages.beginTakeSellMessage(ctx, bot, user, order);
+ await refreshPromise;Also applies to: 151-152
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bot/modules/orders/takeOrder.ts` around lines 98 - 100, The take flow in
takeOrder.ts is being delayed by awaiting scheduled-order republishing before
sending the taker’s next-step message. In the take-buy and take-sell paths
around refreshScheduledOrder and messages.beginTakeBuyMessage /
messages.beginTakeSellMessage, start refreshScheduledOrder in parallel without
awaiting it, or move it after the beginTake*Message call so Telegram/channel
publish latency does not block the active trade flow.
| await publishFn(bot, creator, newOrder, i18n, false); | ||
|
|
||
| schedule.last_order_id = newOrder._id; | ||
| schedule.republish_count = getRepublishCount(); | ||
| await schedule.save(); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Only advance the schedule after a successful publish.
The publish helpers can return after closing the new order or swallowing a send failure, but Line 202 still stores that order as last_order_id and Line 203 resets the counter. Guard on the published state before updating the schedule.
Guard before saving the schedule
await publishFn(bot, creator, newOrder, i18n, false);
+
+ if (newOrder.status === 'CLOSED' || !newOrder.tg_channel_message1) {
+ logger.error(
+ `refreshScheduledOrder: refreshed order ${newOrder._id} was not published`,
+ );
+ return;
+ }
schedule.last_order_id = newOrder._id;
schedule.republish_count = getRepublishCount();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| await publishFn(bot, creator, newOrder, i18n, false); | |
| schedule.last_order_id = newOrder._id; | |
| schedule.republish_count = getRepublishCount(); | |
| await schedule.save(); | |
| await publishFn(bot, creator, newOrder, i18n, false); | |
| if (newOrder.status === 'CLOSED' || !newOrder.tg_channel_message1) { | |
| logger.error( | |
| `refreshScheduledOrder: refreshed order ${newOrder._id} was not published`, | |
| ); | |
| return; | |
| } | |
| schedule.last_order_id = newOrder._id; | |
| schedule.republish_count = getRepublishCount(); | |
| await schedule.save(); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bot/modules/orders/takeOrder.ts` around lines 200 - 204, The schedule update
in takeOrder is happening unconditionally after publishFn, even when the order
was not actually published; update the schedule only when the new order has been
successfully published. Add a guard in takeOrder around the
schedule.last_order_id and schedule.republish_count assignments, using the
published state returned or set by publishFn/newOrder before calling
schedule.save. Keep the fix localized to the publish-and-save flow in takeOrder
so failed or skipped publishes do not advance the schedule.
| import { CommunityContext } from '../community/communityContext'; | ||
| import { ScheduledOrder } from '../../../models'; | ||
| import { validateSellOrder, validateBuyOrder } from '../../validations'; | ||
| import { getCurrency } from '../../../util'; | ||
| import { logger } from '../../../logger'; | ||
| import { SCHEDULE_ORDER } from './scenes'; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## File outline\n'
ast-grep outline bot/modules/schedule/commands.ts --view expanded || true
printf '\n## Relevant file section\n'
sed -n '1,140p' bot/modules/schedule/commands.ts | cat -n
printf '\n## Mongoose dependency/version\n'
rg -n '"mongoose"|from '"'"'mongoose'"'"'|from "mongoose"' package.json . --glob 'package.json' --glob '!**/node_modules/**' || trueRepository: lnp2pBot/bot
Length of output: 3330
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## Mongoose ObjectId validation probe\n'
node - <<'JS'
const samples = [undefined, null, '', 'foo', '507f1f77bcf86cd799439011', '507f1f77bcf86cd79943901', 123];
try {
const mongoose = require('mongoose');
console.log('mongoose version:', mongoose.version);
for (const s of samples) {
let valid = mongoose.Types.ObjectId.isValid(s);
console.log(JSON.stringify(s), '=>', valid);
}
} catch (e) {
console.log('Cannot require mongoose in sandbox:', e.message);
}
JSRepository: lnp2pBot/bot
Length of output: 356
Validate scheduleId before findOne.
ScheduledOrder.findOne({ _id: scheduleId, ... }) will throw on a non-ObjectId value like foo, and this catch only logs the error, so /cancelschedule foo fails without any user-facing reply. Guard with Types.ObjectId.isValid(scheduleId) before querying.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bot/modules/schedule/commands.ts` around lines 1 - 6, The schedule
cancellation flow should validate scheduleId before calling
ScheduledOrder.findOne, since a non-ObjectId value can throw and currently only
gets logged without replying to the user. Add a guard using
Types.ObjectId.isValid(scheduleId) in the command path in commands.ts before the
query, and return an appropriate user-facing message when the id is invalid so
/cancelschedule cannot fail silently.
| // Day names (Spanish, English, abbreviated) mapped to getUTCDay() values (0=Sun) | ||
| const DAY_ALIASES: Record<string, number> = { | ||
| domingo: 0, | ||
| sunday: 0, | ||
| sun: 0, | ||
| dom: 0, | ||
| lunes: 1, | ||
| monday: 1, | ||
| mon: 1, | ||
| lun: 1, | ||
| martes: 2, | ||
| tuesday: 2, | ||
| tue: 2, | ||
| mar: 2, | ||
| miercoles: 3, | ||
| wednesday: 3, | ||
| wed: 3, | ||
| mie: 3, | ||
| jueves: 4, | ||
| thursday: 4, | ||
| thu: 4, | ||
| jue: 4, | ||
| viernes: 5, | ||
| friday: 5, | ||
| fri: 5, | ||
| vie: 5, | ||
| sabado: 6, | ||
| saturday: 6, | ||
| sat: 6, | ||
| sab: 6, | ||
| }; | ||
|
|
||
| // Parses a comma/space separated list of day names into sorted weekday numbers. | ||
| // Tolerant to accents, casing and extra whitespace. Returns null if any token | ||
| // is not a recognized day. | ||
| export const parseCustomDays = (input: string): number[] | null => { | ||
| const parts = input | ||
| .toLowerCase() | ||
| .split(/[,\s]+/) | ||
| .filter(Boolean); | ||
| if (parts.length === 0) return null; | ||
| const days = new Set<number>(); | ||
| for (const part of parts) { | ||
| // strip accents so "miércoles" -> "miercoles" | ||
| const normalized = part.normalize('NFD').replace(/[̀-ͯ]/g, ''); | ||
| const dayNum = DAY_ALIASES[normalized]; | ||
| if (dayNum === undefined) return null; | ||
| days.add(dayNum); | ||
| } | ||
| return [...days].sort((a, b) => a - b); | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Custom day parsing is locale-limited.
This wizard is being shipped with 10 locales, but parseCustomDays() only accepts English/Spanish weekday tokens. In fr, de, ru, etc., users can follow the localized prompt and still never get past invalid_days when they enter weekday names in their own language. Either localize the alias table per locale or change the custom input format to something locale-neutral. As per coding guidelines, "bot/**/*.ts: House commands, scenes, and middleware modules in bot/ directory; pair new flows with text updates under locales/".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bot/modules/schedule/helpers.ts` around lines 8 - 58, parseCustomDays is
currently hardcoded to English/Spanish weekday aliases, so localized wizard
prompts still reject valid user input in other locales. Update the weekday
parsing in helpers.ts by either making DAY_ALIASES locale-aware (driven by the
active locale or translated weekday names) or switching the custom day input
flow to a locale-neutral format, and keep the parser logic in parseCustomDays
aligned with whatever format the schedule wizard expects.
Source: Coding guidelines
| const DAY_LABELS: Record<number, string> = { | ||
| 0: 'Sun', | ||
| 1: 'Mon', | ||
| 2: 'Tue', | ||
| 3: 'Wed', | ||
| 4: 'Thu', | ||
| 5: 'Fri', | ||
| 6: 'Sat', | ||
| }; | ||
|
|
||
| export const formatDays = (days: number[]): string => | ||
| days.map(d => DAY_LABELS[d]).join(', '); | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Localize the weekday labels in the confirmation summary.
formatDays() always renders Sun/Mon/… so every non-English locale gets an English day list inside an otherwise translated confirmation message. Pull these labels from i18n instead of hardcoding them here. As per coding guidelines, "bot/**/*.ts: House commands, scenes, and middleware modules in bot/ directory; pair new flows with text updates under locales/".
Also applies to: 65-70
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bot/modules/schedule/messages.ts` around lines 3 - 15, formatDays() is
hardcoding English weekday abbreviations, so the confirmation summary is not
localized. Update the DAY_LABELS mapping in messages.ts to read weekday names
from i18n/locales instead of static strings, and make sure the caller still uses
formatDays(days) unchanged. Keep the localized labels aligned with the existing
confirmation text flow and add/update the matching entries under locales/ for
each weekday.
Source: Coding guidelines
| /blocklist - Liste de vos utilisateurs bloqués | ||
| /cancel <_order id_> - Annule une commande qui n'a pas encore été prise | ||
| /cancelall - Annule toutes les commandes validées et non prises | ||
| /scheduleorder <_acheter|vendre_> <_sats_> <_montant fiat_> <_code fiat_> <_méthode de paiement_> [_prime_] - Crée une commande récurrente qui se republie automatiquement |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep /scheduleorder arguments as buy|sell.
The handler only accepts the literal English tokens buy and sell. Documenting acheter|vendre here will bounce French users straight back to the usage message.
Proposed fix
- /scheduleorder <_acheter|vendre_> <_sats_> <_montant fiat_> <_code fiat_> <_méthode de paiement_> [_prime_] - Crée une commande récurrente qui se republie automatiquement
+ /scheduleorder <_buy|sell_> <_sats_> <_montant fiat_> <_code fiat_> <_méthode de paiement_> [_prime_] - Crée une commande récurrente qui se republie automatiquement-scheduleorder_usage: "Utilisation : /scheduleorder <acheter|vendre> <sats> <montant fiat> <code fiat> <méthode de paiement> [prime]"
+scheduleorder_usage: "Utilisation : /scheduleorder <buy|sell> <sats> <montant fiat> <code fiat> <méthode de paiement> [prime]"Also applies to: 730-730
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@locales/fr.yaml` at line 266, The `/scheduleorder` help text is using
translated action tokens, but the command handler only accepts the literal
`buy|sell` values. Update the relevant `/scheduleorder` entries in the locale
definitions to document `buy|sell` instead of `acheter|vendre`, keeping the rest
of the argument placeholders unchanged so the displayed usage matches the parser
expected by the schedule order handler.
| schedule_enter_custom_days: "✍️ Entrez les jours séparés par des virgules (ex : lundi, mercredi, vendredi)" | ||
| invalid_days: "⚠️ Je n'ai pas compris ces jours. Veuillez les entrer séparés par des virgules (ex : lundi, mercredi, vendredi)." |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== helpers outline =="
ast-grep outline bot/modules/schedule/helpers.ts --view expanded
echo
echo "== DAY_ALIASES / parseCustomDays =="
nl -ba bot/modules/schedule/helpers.ts | sed -n '1,260p' | sed -n '/DAY_ALIASES/,+140p'Repository: lnp2pBot/bot
Length of output: 542
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,140p' bot/modules/schedule/helpers.tsRepository: lnp2pBot/bot
Length of output: 1703
Add French weekday aliases or change the examples
lundi, mercredi, and vendredi are not accepted by bot/modules/schedule/helpers.ts; the parser only maps Spanish and English aliases, so this prompt currently tells users to enter invalid values.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@locales/fr.yaml` around lines 741 - 742, The weekday prompts in the French
locale are using examples that the schedule parser does not accept. Update the
messages for schedule_enter_custom_days and invalid_days in locales/fr.yaml to
use weekday aliases supported by bot/modules/schedule/helpers.ts, or add
matching French aliases in that parser so the examples and validation are
aligned.
| /blocklist - Elenco degli utenti bloccati | ||
| /cancel <_order id_> - Cancella un ordine che non è ancora stato accettato | ||
| /cancelall - Cancella tutti gli ordini non ancora accettati | ||
| /scheduleorder <_comprare|vendere_> <_sats_> <_importo fiat_> <_codice fiat_> <_metodo di pagamento_> [_premio_] - Crea un ordine ricorrente che si ripubblica automaticamente |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep /scheduleorder arguments as buy|sell.
bot/modules/schedule/commands.ts only accepts buy and sell. Showing comprare|vendere here makes the localized help incorrect and the command fail for users who follow it.
Proposed fix
- /scheduleorder <_comprare|vendere_> <_sats_> <_importo fiat_> <_codice fiat_> <_metodo di pagamento_> [_premio_] - Crea un ordine ricorrente che si ripubblica automaticamente
+ /scheduleorder <_buy|sell_> <_sats_> <_importo fiat_> <_codice fiat_> <_metodo di pagamento_> [_premio_] - Crea un ordine ricorrente che si ripubblica automaticamente-scheduleorder_usage: "Utilizzo: /scheduleorder <comprare|vendere> <sats> <importo fiat> <codice fiat> <metodo di pagamento> [premio]"
+scheduleorder_usage: "Utilizzo: /scheduleorder <buy|sell> <sats> <importo fiat> <codice fiat> <metodo di pagamento> [premio]"Also applies to: 728-728
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@locales/it.yaml` at line 264, The localized `/scheduleorder` help text is
using translated arguments that do not match what
`bot/modules/schedule/commands.ts` actually parses. Update the `/scheduleorder`
usage string in the locale entry to keep the action argument as `buy|sell`
(including the other affected locale occurrence) so the help output matches the
command’s accepted values and users can copy it correctly.
| /blocklist - Lista de seus usuários bloqueados | ||
| /cancel <ID do pedido> - Cancela um pedido que não foi feito | ||
| /cancelall - Cancela todos os pedidos postados e não atendidos | ||
| /scheduleorder <_comprar|vender_> <_sats_> <_valor fiat_> <_código fiat_> <_método de pagamento_> [_prêmio_] - Cria um pedido recorrente que se republica automaticamente |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep /scheduleorder arguments as buy|sell.
The command handler only recognizes buy and sell. Advertising comprar|vender here makes the Portuguese help unusable for the actual parser.
Proposed fix
- /scheduleorder <_comprar|vender_> <_sats_> <_valor fiat_> <_código fiat_> <_método de pagamento_> [_prêmio_] - Cria um pedido recorrente que se republica automaticamente
+ /scheduleorder <_buy|sell_> <_sats_> <_valor fiat_> <_código fiat_> <_método de pagamento_> [_prêmio_] - Cria um pedido recorrente que se republica automaticamente-scheduleorder_usage: "Uso: /scheduleorder <comprar|vender> <sats> <valor fiat> <código fiat> <método de pagamento> [prêmio]"
+scheduleorder_usage: "Uso: /scheduleorder <buy|sell> <sats> <valor fiat> <código fiat> <método de pagamento> [prêmio]"Also applies to: 730-730
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@locales/pt.yaml` at line 265, The `/scheduleorder` help text in the
Portuguese locale is advertising unsupported argument names, which makes the
command docs inconsistent with the parser. Update the localized string for the
schedule order entry in the locale file to use the same `buy|sell` arguments
that the command handler recognizes, and make the same correction in the other
referenced occurrence so the help text stays aligned with the actual parser.
| export interface IScheduledOrder extends Document { | ||
| _id: string; | ||
| creator_id: string; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== tsconfig files =="
fd 'tsconfig.*json$'
echo
echo "== Document extensions in model files =="
rg -n --type=ts 'extends Document' models
echo
echo "== explicit _id string declarations =="
rg -n --type=ts '_id:\s*string' models
echo
echo "== scheduled_order model =="
sed -n '1,80p' models/scheduled_order.tsRepository: lnp2pBot/bot
Length of output: 3534
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== package.json (deps + scripts) =="
sed -n '1,220p' package.json
echo
echo "== tsconfig.json =="
sed -n '1,220p' tsconfig.json
echo
echo "== tsconfig.test.json =="
sed -n '1,220p' tsconfig.test.json
echo
echo "== model files with Document/_id patterns =="
for f in models/*.ts; do
echo "--- $f ---"
rg -n 'extends Document|_id:\s*string|mongoose\.model<|Schema<' "$f" || true
doneRepository: lnp2pBot/bot
Length of output: 5299
🌐 Web query:
Mongoose 8 Document _id string TS2430 interface extends Document typings
💡 Result:
In Mongoose 8, you should avoid creating interfaces that extend the Document class, as this pattern is discouraged, causes performance issues, and often leads to TypeScript errors like TS2430 [1][2]. Instead, use a separate interface for your raw document data and define a union type for the hydrated document [3][4]. If your _id is a string instead of the default ObjectId, you must explicitly specify this using the Document generic [5]. Recommended Pattern: 1. Define a clean interface representing the data structure (do not extend Document) [3][4]. 2. Use HydratedDocument or an intersection type for Mongoose-specific functionality [3][6][4]. // 1. Define raw data interface interface IUser { _id: string; // Explicitly define as string name: string; } // 2. Define the HydratedDocument type import { HydratedDocument } from 'mongoose'; type UserDocument = HydratedDocument; // Alternatively, use an intersection if you need manual control type UserDocument = IUser & Document; Why TS2430 occurs: This error typically arises because the interface you are extending (Document) has a predefined _id property (defaulting to ObjectId), and your custom interface attempts to override it with a different type (e.g., string), which violates inheritance constraints [7][8]. By moving away from extends Document and utilizing the Mongoose generics (e.g., Document), you resolve these conflicts correctly [7][5]. For further guidance, consult the official Mongoose TypeScript documentation which explicitly advises against the "extends Document" approach [3][4].
Citations:
- 1: https://mongoosejs.com/docs/6.x/docs/typescript.html
- 2: TypeScript: remove support for document interfaces that
extends DocumentAutomattic/mongoose#11615 - 3: https://mongoosejs.com/docs/8.x/docs/typescript.html
- 4: https://mongoosejs.com/docs/typescript.html
- 5: https://github.com/Digital-Defiance/mongoose-types/
- 6: How to make inferred type of Model.constructor and Model.findOne be Document<string> not Document<unknown> Automattic/mongoose#14085
- 7: PopulatedDoc is returning any as type Automattic/mongoose#10432
- 8: Problem defining interfaces for return document with mongoose DefinitelyTyped/DefinitelyTyped#21592
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== scheduled_order usages =="
rg -n --type=ts 'ScheduledOrder|IScheduledOrder|scheduled_order' .
echo
echo "== _id usage on scheduled order instances =="
rg -n --type=ts '\._id\b' models jobs util bot ln lnurl app.ts scripts testsRepository: lnp2pBot/bot
Length of output: 23854
Update the Mongoose model typings across all extends Document interfaces with _id: string.
models/scheduled_order.ts has the same pattern as models/user.ts, models/order.ts, and models/community.ts, so changing only this file won’t clear the Mongoose 8 TS2430 error. Use a raw interface + HydratedDocument, or type _id through the Document generic consistently.
🧰 Tools
🪛 GitHub Actions: NodeJS Continuous Integration / 0_ci_to_main.txt
[error] 3-3: TypeScript (tsc) error TS2430: Interface 'IScheduledOrder' incorrectly extends interface 'Document<ObjectId, any, any, Record<string, any>, {}>'. Types of property '_id' are incompatible: Type 'string' is not assignable to type 'ObjectId'.
🪛 GitHub Actions: NodeJS Continuous Integration / ci_to_main
[error] 3-3: TypeScript (tsc) error TS2430: Interface 'IScheduledOrder' incorrectly extends interface 'Document<ObjectId, any, any, Record<string, any>, {}>'. Types of property '_id' are incompatible: Type 'string' is not assignable to type 'ObjectId'.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@models/scheduled_order.ts` around lines 3 - 5, The scheduled order typing
still uses an `extends Document` interface with `_id: string`, which should be
updated consistently with the other model typings to fix the Mongoose 8 TS2430
issue. Refactor `IScheduledOrder` to avoid directly extending `Document`, and
instead use the same pattern applied in `models/user.ts`, `models/order.ts`, and
`models/community.ts`—either a plain interface paired with `HydratedDocument`,
or a `Document` generic approach that types `_id` consistently across all
models.
Source: Pipeline failures
feat: add /scheduleorder and /cancelschedule for recurring auto-republishing orders
Closes #769.
Adds
/scheduleorder— creates an order that automatically republishes on a recurring schedule (e.g. every day, weekdays, or specific days at a chosen hour) — and/cancelschedule <schedule_id>to stop it.Design
Following the maintainer feedback on the original PR (mutating an order's
republish_count/ status was too invasive and let an order migrate between states), this implementation introduces a dedicatedScheduledOrdermodel that acts as a mold:Orderwith its own id.ScheduledOrderstores the trade config + recurrence (days,hourin UTC) + arepublish_countcap +last_order_id(mold → order link).models/order.tsis left completely untouched — no new fields onOrder.How it works
/scheduleorder <buy|sell> <sats> <fiat_amount> <fiat_code> <payment_method> [premium]parses the order with the existing buy/sell validators, then opens an interactive wizard (/exitto leave at any time):📅 Every day·💼 Mon–Fri·🌴 Weekend·⚙️ Custom(custom accepts a comma-separated list of day names, accent/case tolerant).0–23(UTC).✅ / ❌.jobs/scheduled_orders.ts) runs hourly and, for each active schedule whose day/hour (UTC) match, publishes a fresh order from the mold and decrementsrepublish_count. When the counter hits0the schedule deactivates itself (self-cleaning, as the issue requested). The cap defaults toREPUBLISH_ORDER_DAYS(10)./cancelschedule <schedule_id>deactivates a schedule by its own id.Implementation built as a WizardScene
The multi-step flow is a
Scenes.WizardSceneregistered instageMiddleware, exactly like the/buyand/sellorder wizard. This means:/exitworks at any step (inherited from the generic scene commands).bot.on('text')/bot.on('callback_query')listeners, no cross-chat interference, no duplicated middleware.Even though
republish_countcaps the total number of republications as the issue asked, scheduled orders do not pile up on the channel. Each published order is a normal order and expires afterORDER_PUBLISHED_EXPIRATION_WINDOW(23h by default), removed by the existingdelete_published_ordersjob. Since 23h is shorter than any schedule interval (daily/weekly), at most one scheduled order is live at a time — a weekly order expires ~23h after publishing, days before the next one.New config
REPUBLISH_ORDER_DAYS=10 # max republication cycles before a schedule self-deactivates
Testing
/helpupdated with both commands; i18n strings added.Summary by CodeRabbit