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 12 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 scheduled-order creation, publishing, cancellation, refresh-on-take, bot wiring, and locale strings across the bot. ChangesScheduled Recurring Orders
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
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
| // 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
…ectId validation, English-only day input, localized prompts
…ct Mongoose Document typing
There was a problem hiding this comment.
Code Review Summary
Verdict: Changes Requested
I re-read the PR conversation and the current head. The feature direction is good, but there are two blocking consistency bugs in the schedule refresh flow:
Blocking
bot/modules/orders/takeOrder.ts:refreshScheduledOrder()treatspublishBuyOrderMessage()/publishSellOrderMessage()as if they return a success flag. They do not — those helpers swallow errors and returnvoid, soif (!published)is always true. That means the schedule never updates itslast_order_idorrepublish_countafter a take, so the next taken order cannot be matched back to the schedule. In practice, the recurring-order feature does not refresh correctly.jobs/scheduled_orders.ts: the hourly publisher advances the schedule after calling the publish helper, but there is no actual success signal and no atomic claim/check around the publish+save boundary. Because the publish helpers swallow failures, a Telegram/channel failure can still consume a republish cycle and movelast_order_id, leaving the schedule desynced with the real published state. There is no reconciliation path here, so a transient failure can strand the schedule or make later refreshes behave incorrectly.
Why this is blocking
- This is state that controls future publications. If it is advanced incorrectly, the feature can silently stop republishing or republish against the wrong state.
- In a schedule-driven flow, I want the code to prove the publish succeeded before it mutates the schedule record.
If you want, I can re-check the next iteration after the publish/schedule handoff is made atomic or otherwise crash-safe.
| schedule.type === 'buy' | ||
| ? publishBuyOrderMessage | ||
| : publishSellOrderMessage; | ||
| const published = await publishFn(bot, creator, newOrder, i18n, false); |
There was a problem hiding this comment.
Blocking: publishBuyOrderMessage() / publishSellOrderMessage() return void, so published is always false here. The schedule never gets its last_order_id / republish_count updated after a take, which breaks the refresh flow.
| schedule.type === 'buy' | ||
| ? publishBuyOrderMessage | ||
| : publishSellOrderMessage; | ||
| await publishFn(bot, user, order, i18n, false); |
There was a problem hiding this comment.
Blocking: this advances the schedule without a trustworthy success signal or atomic handoff. Since the publish helpers swallow failures, a send error can still consume a cycle and desync last_order_id from the actual published state.
There was a problem hiding this comment.
Code Review Summary
Verdict: Request changes
Blocking
- The new
models/scheduled_order.tsinterface still extendsDocumentwhile declaring_id: string, which is a Mongoose 8 TS2430 compile blocker. - The publish/update path in
jobs/scheduled_orders.ts(and the same refresh path inbot/modules/orders/takeOrder.ts) still advances the schedule only after the external publish. Ifschedule.save()fails after the order is already posted, the next tick can republish the same schedule or leave the refresh chain stranded.
Non-blocking
- I did not find other blocking issues on the current head.
| @@ -0,0 +1,44 @@ | |||
| import mongoose, { Document, Schema } from 'mongoose'; | |||
|
|
|||
| export interface IScheduledOrder extends Document { | |||
There was a problem hiding this comment.
This is still a TS2430 blocker on Mongoose 8: Document already defines _id as ObjectId, so IScheduledOrder cannot narrow it to string. Please switch this model to the same typing pattern used elsewhere in the repo (plain interface + HydratedDocument, or a Document generic with a consistent _id type).
| : publishSellOrderMessage; | ||
| await publishFn(bot, user, order, i18n, false); | ||
|
|
||
| // publishFn swallows errors and returns void, so we detect success from |
There was a problem hiding this comment.
This publish flow is still crash-sensitive: publishBuyOrderMessage() / publishSellOrderMessage() can succeed on Telegram and then fail on schedule.save(). In that case the order is already live, but the schedule never advances, so the next hourly tick can publish a duplicate. Please make the publish + schedule update atomic or persist a reservation before the external side effect.
| : publishSellOrderMessage; | ||
| await publishFn(bot, creator, newOrder, i18n, false); | ||
|
|
||
| // publishFn swallows errors and returns void, so we detect success from the |
There was a problem hiding this comment.
Same crash window here in the refresh path: if the refreshed order is published and schedule.save() fails, last_order_id / republish_count stay stale even though a new live order exists. That can strand the chain or let the hourly job republish again. Please fix this path with the same atomicity/retry guarantee as the cron job.
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)
jobs/scheduled_orders.ts (1)
48-93: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winReserve before creating the order, or clean up the loser.
createOrder()runs before the atomic claim. Ifreservedisnull, Line 92 only skips publishing; the already-created order is left behind. Move the reservation before order creation, then setlast_order_idafter creation, or explicitly close/delete the unclaimed order.Possible direction
- const order = await ordersActions.createOrder(i18n, bot, user, { - type: schedule.type, - amount: schedule.amount, - fiatAmount: schedule.fiat_amount, - fiatCode: schedule.fiat_code, - paymentMethod: schedule.payment_method, - status: 'PENDING', - priceMargin: schedule.price_margin, - community_id: schedule.community_id, - }); - - if (!order) { - logger.warning( - `ScheduledOrder ${schedule._id}: failed to create order`, - ); - continue; - } - const remaining = schedule.republish_count - 1; const reserved = await ScheduledOrder.findOneAndUpdate( { _id: schedule._id, @@ - { - last_order_id: order._id, - republish_count: remaining, - active: remaining > 0, - }, + { republish_count: remaining, active: remaining > 0 }, { new: true }, ); @@ continue; } + + const order = await ordersActions.createOrder(i18n, bot, user, { + type: schedule.type, + amount: schedule.amount, + fiatAmount: schedule.fiat_amount, + fiatCode: schedule.fiat_code, + paymentMethod: schedule.payment_method, + status: 'PENDING', + priceMargin: schedule.price_margin, + community_id: schedule.community_id, + }); + + if (!order) { + logger.warning( + `ScheduledOrder ${schedule._id}: failed to create order after reservation`, + ); + continue; + } + + await ScheduledOrder.updateOne( + { _id: reserved._id }, + { last_order_id: order._id }, + );🤖 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 `@jobs/scheduled_orders.ts` around lines 48 - 93, The ScheduledOrder processing flow creates the order before claiming the cycle, so when ScheduledOrder.findOneAndUpdate returns null the orphaned order is left behind. Update the logic around ordersActions.createOrder and ScheduledOrder.findOneAndUpdate so the cycle is reserved before creating the order, or add cleanup for the loser by closing/deleting the created order when the reservation fails. Use the existing ScheduledOrder and ordersActions.createOrder flow to keep the reservation and order creation consistent.
🤖 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.
Outside diff comments:
In `@jobs/scheduled_orders.ts`:
- Around line 48-93: The ScheduledOrder processing flow creates the order before
claiming the cycle, so when ScheduledOrder.findOneAndUpdate returns null the
orphaned order is left behind. Update the logic around ordersActions.createOrder
and ScheduledOrder.findOneAndUpdate so the cycle is reserved before creating the
order, or add cleanup for the loser by closing/deleting the created order when
the reservation fails. Use the existing ScheduledOrder and
ordersActions.createOrder flow to keep the reservation and order creation
consistent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: c3221794-0f60-4ed5-aa5e-3adb99f2a5db
📒 Files selected for processing (17)
bot/modules/orders/takeOrder.tsbot/modules/schedule/commands.tsbot/modules/schedule/helpers.tsbot/modules/schedule/messages.tsbot/modules/schedule/scenes.tsjobs/scheduled_orders.tslocales/de.yamllocales/en.yamllocales/es.yamllocales/fa.yamllocales/fr.yamllocales/it.yamllocales/ko.yamllocales/pt.yamllocales/ru.yamllocales/uk.yamlmodels/scheduled_order.ts
💤 Files with no reviewable changes (1)
- models/scheduled_order.ts
✅ Files skipped from review due to trivial changes (8)
- locales/en.yaml
- locales/de.yaml
- locales/ko.yaml
- locales/it.yaml
- locales/ru.yaml
- locales/pt.yaml
- locales/fr.yaml
- locales/es.yaml
🚧 Files skipped from review as they are similar to previous changes (6)
- locales/uk.yaml
- bot/modules/schedule/commands.ts
- bot/modules/schedule/messages.ts
- bot/modules/orders/takeOrder.ts
- locales/fa.yaml
- bot/modules/schedule/scenes.ts
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
/scheduleorderto create recurring buy/sell schedules and/cancelscheduleto stop them./helptext and added schedule flow translations and prompts across multiple languages.