Skip to content

feat: add /schedule command for managing scheduled jobs via Telegram#151

Draft
felixeu31 wants to merge 6 commits intoRichardAtCT:mainfrom
felixeu31:features/shedule-command
Draft

feat: add /schedule command for managing scheduled jobs via Telegram#151
felixeu31 wants to merge 6 commits intoRichardAtCT:mainfrom
felixeu31:features/shedule-command

Conversation

@felixeu31
Copy link
Copy Markdown

@felixeu31 felixeu31 commented Mar 15, 2026

Description Add a user-facing /schedule command that allows managing scheduled jobs directly from Telegram chat, removing the need

for direct SQLite access and bot restarts.

Changes - Inject JobScheduler into bot dependencies (exposed from run_application())

  • Add pause_job() and resume_job() methods to JobScheduler, plus include_paused flag on list_jobs()
  • Add ScheduleCommandHandler with subcommands: - /schedule list — show all jobs (active + paused)
    • /schedule add <name> <min> <hour> <day> <month> <weekday> <prompt> — create a new job
    • /schedule remove <job_id> — soft-delete a job - /schedule pause <job_id> / /schedule resume <job_id> — toggle is_active
  • Register /schedule in both agentic and classic modes, gated on ENABLE_SCHEDULER=true
  • Auto-populate chat_id, working_directory, and created_by from message context
  • Hot-reload: jobs are live immediately via JobScheduler.add_job() — no restart needed - Documentation updated in README, CLAUDE.md, docs/setup.md, and CHANGELOG

Related Issue

Closes #150

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Testing

  • Tests added/updated (16 new tests, 100% coverage on handler)
  • All tests pass (505 existing + 16 new)
  • Manual testing completed

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Documentation updated
  • No breaking changes (or clearly documented)

Félix Díez Arias added 6 commits March 15, 2026 10:16
Add "scheduler" key to the bot deps dict and assign the real
JobScheduler instance after creation in run_application(), making
it accessible to command handlers via context.bot_data["scheduler"].
pause_job() removes job from APScheduler and sets is_active=0.
resume_job() re-registers with APScheduler and sets is_active=1.
list_jobs() now accepts include_paused flag to show all jobs.
Single /schedule entry point dispatching to subcommands:
list, add, remove, pause, resume. Auto-populates chat_id,
working_directory, and created_by from Telegram context.
Add /schedule handler to both agentic and classic mode registration,
gated on enable_scheduler. Add to get_bot_commands() for Telegram
command menu visibility.
16 tests covering all subcommands (list, add, remove, pause, resume),
argument parsing edge cases, error handling, and the scheduler-not-
available fallback path. 100% coverage on the handler module.
Update command lists in README and CLAUDE.md, expand the Job Scheduler
section in docs/setup.md with usage examples, and add CHANGELOG entry.
@RichardAtCT
Copy link
Copy Markdown
Owner

Good feature overall — the core scheduler plumbing looks solid and the test coverage is appreciated. Several issues need addressing before merge:


1. Silent failure on unknown job_id in _handle_remove (bug)

_handle_remove calls scheduler.remove_job(job_id) with no try/except. If scheduler.remove_job raises on a missing job, the user gets an unhandled exception instead of a friendly error. Wrap it:

try:
    removed = await scheduler.remove_job(job_id)
    if not removed:
        await update.message.reply_text(f"No job found with id: {job_id}")
        return
except Exception as e:
    await update.message.reply_text(f"Failed to remove job: {e}")
    return

Same pattern applies to _handle_pause and _handle_resume — their scheduler methods return bool but that return value appears unused.


2. context.user_data vs context.chat_data conflict with PR #165

_handle_add reads working directory from context.user_data.get("current_directory"). PR #165 (currently open) migrates session state to context.chat_data. These two PRs will conflict. This handler will silently fall back to settings.approved_directory once #165 lands. Please coordinate with #165 or read from context.chat_data here.


3. No cron expression validation before APScheduler ingestion (crash risk)

The raw string " ".join(args[1:6]) is passed directly to scheduler.add_job() without pre-validation. A malformed expression will either raise inside APScheduler (caught by the broad except Exception) or silently schedule something wrong. Validate upfront for a clear user error:

from apscheduler.triggers.cron import CronTrigger
try:
    CronTrigger.from_crontab(cron_expression)
except ValueError as e:
    await update.message.reply_text(f"Invalid cron expression: {e}")
    return

4. No authorization gating — any user can create/delete jobs

There is no auth check anywhere in schedule_command or its sub-handlers. Any user who can message the bot can add, remove, pause, or resume scheduled jobs globally. This is a significant security gap in a multi-user deployment. At minimum, check against an admin user list before executing any write subcommand.


5. Cron field count — clarify in usage (minor)

_handle_add takes args[1:6] (5 fields: min hour day month weekday). Make sure _show_usage shows a concrete 5-field example (0 9 * * 1-5) so users don't accidentally try 6-field expressions with seconds.


6. Missing type annotations (mypy strict violation)

None of the handler functions have type annotations — this will fail CI with disallow_untyped_defs=true:

async def schedule_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
async def _handle_add(update: Update, context: ContextTypes.DEFAULT_TYPE, scheduler: JobScheduler, args: List[str]) -> None:
async def _handle_remove(update: Update, scheduler: JobScheduler, args: List[str]) -> None:
async def _get_scheduler(context: ContextTypes.DEFAULT_TYPE) -> Optional[JobScheduler]:

Summary: The feature is well-structured and the happy path works. Fix the four functional issues (#1 remove error handling, #2 chat_data alignment, #3 cron validation, #4 auth) before merge — type annotations (#6) are a CI requirement too.

Friday, AI assistant to @RichardAtCT (posted as @RichardAtCT — FridayOpenClawBot access pending)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add /schedule command for managing scheduled jobs via Telegram

2 participants