telebot_py.scheduler

Scheduler: JobQueue with in-house RRule (US3, FR-008).

Wired into the kernel by T043: ApplicationBuilder.job_queue() attaches a queue to Application; initialize() starts it once the bot is ready, stop()/shutdown() stop it, and every dispatched context exposes it as context.job_queue.

telebot_py.scheduler.job

A single scheduled job managed by the JobQueue (T042).

class telebot_py.scheduler.job.Job(*, name: str, callback: Callable[[Job[Data]], Awaitable[None] | None], queue: JobQueue, clock: Clock, sleep: SleepFn, kind: _Kind, next_t: datetime, data: Data | None = None, interval: timedelta | None = None, daily_time: time | None = None, daily_days: tuple[int, ...] = (), rrule: RRule | None = None)

Bases: Generic[Data]

A scheduled task: one-shot, repeating, daily, or RRule-driven.

Created through the run_* methods of JobQueue, never directly.

Example

>>> job = queue.run_once(send_reminder, 60.0, data={"chat_id": 1})
>>> job.cancel()  # idempotent
Variables:
  • name – Identifier of the job; auto-generated when not supplied.

  • data – Optional payload delivered to the callback as job.data.

  • next_t – When the job fires next (updated after every fire).

cancel() → None

Cancel the job and detach it from its queue.

Idempotent: a second call is a harmless no-op. A cancelled job never fires again, even if it was merely pending (not yet started).

property cancelled: bool

True once the job stops firing, whether by cancel() or completion.

telebot_py.scheduler.job.JobCallback

sync or async callables receiving the fired job.

Job payloads are read from job.data inside the callback. Callbacks keep the Job-only signature even with the kernel wiring (T043); reach the Bot client by capturing context.bot (or application.bot) in a closure.

Type:

Signature of job callbacks

alias of Callable[[Job[Data]], Awaitable[None] | None]

telebot_py.scheduler.queue

Async timer-loop job queue (T042).

Parity with the run_once/run_repeating/run_daily/run_custom surface from contracts/public-api.md section 6. The kernel wiring (T043) attaches one queue per application via ApplicationBuilder.job_queue() and drives JobQueue.start() / JobQueue.stop() with the initialize()/stop() lifecycle.

class telebot_py.scheduler.queue.JobQueue(*, clock: Callable[[], datetime] | None = None, sleep: Callable[[float], Awaitable[None]] | None = None)

Bases: object

Manages background one-shot and recurring jobs on the event loop.

Jobs scheduled while the queue is stopped wait for start(); jobs scheduled while it is running start immediately. stop() cancels every job. Timekeeping is injectable for deterministic tests: clock supplies “now” and sleep replaces asyncio.sleep.

Example

>>> queue = JobQueue()
>>> queue.start()
>>> job = queue.run_once(callback, 60.0, data={"chat_id": 1})
>>> job.cancel()
>>> queue.stop()
get_jobs_by_name(name: str) → list[Job[Any]]

Find all active jobs with the given name.

Parameters:

name – Job identifier to look up.

Returns:

Matching jobs (empty list when none).

jobs() → list[Job[Any]]

All jobs currently managed, in scheduling order.

Returns:

A snapshot list; mutating it does not affect the queue.

run_custom(rrule: RRule, callback: Callable[[Job[Data]], Awaitable[None] | None], *, data: Data | None = None, name: str | None = None) → Job[Data]

Schedule a job following an RFC 5545 recurrence rule.

The job fires at every future occurrence of rrule and finishes (marking itself cancelled and leaving the queue) once the rule is exhausted.

Parameters:
  • rrule – The recurrence engine driving the schedule.

  • callback – Sync or async callable invoked with the fired job.

  • data – Optional payload delivered as job.data.

  • name – Optional identifier; auto-generated when omitted.

Returns:

The scheduled job.

Raises:

ValueError – If the rule yields no future occurrence.

run_daily(callback: Callable[[Job[Data]], Awaitable[None] | None], time: time, *, days: tuple[int, ...] = (0, 1, 2, 3, 4, 5, 6), data: Data | None = None, name: str | None = None) → Job[Data]

Schedule a job firing every day at a wall-clock time.

Parameters:
  • callback – Sync or async callable invoked with the fired job.

  • time – Time of day to fire at.

  • days – Allowed weekdays as datetime.weekday() values (Monday=0); defaults to every day.

  • data – Optional payload delivered as job.data.

  • name – Optional identifier; auto-generated when omitted.

Returns:

The scheduled job.

Raises:

ValueError – If days is empty.

run_once(callback: Callable[[Job[Data]], Awaitable[None] | None], when: float | datetime, *, data: Data | None = None, name: str | None = None) → Job[Data]

Schedule a one-off job.

Parameters:
  • callback – Sync or async callable invoked with the fired job.

  • when – Delay in seconds, or an absolute datetime (naive values are treated as UTC).

  • data – Optional payload delivered as job.data.

  • name – Optional identifier; auto-generated when omitted.

Returns:

The scheduled job.

Raises:

TypeError – If when is neither a number nor a datetime.

run_repeating(callback: Callable[[Job[Data]], Awaitable[None] | None], interval: float, *, first: float | datetime | None = None, data: Data | None = None, name: str | None = None) → Job[Data]

Schedule a job repeating at a fixed interval.

Parameters:
  • callback – Sync or async callable invoked with the fired job.

  • interval – Seconds between successive fires; must be positive.

  • first – Delay or absolute datetime of the first fire; defaults to one interval.

  • data – Optional payload delivered as job.data.

  • name – Optional identifier; auto-generated when omitted.

Returns:

The scheduled job.

Raises:
  • ValueError – If interval is not positive.

  • TypeError – If first is neither a number nor a datetime.

property running: bool

Whether the queue’s timer loop is active.

start() → None

Activate the queue, arming every pending job’s timer.

Must be called from inside a running event loop. Raises:

ApplicationError: If the queue is already running.

stop() → None

Stop the queue and cancel every job it manages.

Raises:

ApplicationError – If the queue is not running.

telebot_py.scheduler.rrule.rule

In-house RFC 5545 recurrence rule engine (T041).

Logic ported field for field from packages/go/pkg/scheduler/rrule/rrule.go; the Go implementation is the source of truth for the semantics, including the iteration safety cap and Go-style calendar normalization for month/year arithmetic (overflowing days roll into the following month, they are not clamped).

class telebot_py.scheduler.rrule.rule.RRule(options: RRuleOptions)

Bases: object

Calculates recurring timestamps based on RFC 5545 recurrence options.

Example

>>> rule = RRule(RRuleOptions(freq=Frequency.DAILY, interval=2, dtstart=start))
>>> next_run = rule.after(datetime.now(timezone.utc), inclusive=False)
Variables:

options – The normalized options this engine was constructed with.

after(after: datetime, inclusive: bool = False) → datetime | None

Find the next occurrence strictly after (or inclusive of) a time.

Parameters:
  • after – The reference time to search past.

  • inclusive – When True, an occurrence exactly at after counts.

Returns:

The next occurrence, or None when count is exhausted, the rule ends before after (per until), or the safety cap of 10,000 iterations is hit (e.g. an unknown frequency that never advances).

between(after: datetime, before: datetime, inclusive: bool = False) → list[datetime]

Return occurrences between two times.

Parameters:
  • after – Lower bound of the window.

  • before – Upper bound of the window; an occurrence exactly at before is included.

  • inclusive – When True, an occurrence exactly at after is included.

Returns:

All occurrences inside the window, in chronological order.

telebot_py.scheduler.rrule.types

Value types for the in-house RFC 5545 recurrence rule engine.

Field-for-field port of packages/go/pkg/scheduler/rrule/types.go; the Go implementation is the source of truth for the semantics (FR-008).

class telebot_py.scheduler.rrule.types.Frequency(*values)

Bases: IntEnum

Recurrence frequency of an RRuleOptions rule.

Attributes mirror the Go rrule.Frequency constants, whose values are plain ints; RRuleOptions.freq is therefore typed int so unknown values can be constructed and hit the engine’s safety cap exactly like Go.

DAILY = 3
HOURLY = 2
MINUTELY = 1
MONTHLY = 5
SECONDLY = 0
WEEKLY = 4
YEARLY = 6
class telebot_py.scheduler.rrule.types.RRuleOptions(freq: int, interval: int = 1, dtstart: datetime | None = None, until: datetime | None = None, count: int = 0, by_hour: tuple[int, ...] = (), by_minute: tuple[int, ...] = (), by_second: tuple[int, ...] = ())

Bases: object

Parameters for an RFC 5545 recurrence rule.

Mirrors Go’s rrule.Options field for field. by_hour, by_minute, and by_second are carried for parity with the Go type but the Go engine (and therefore this port) does not expand them when computing occurrences; they are reserved for a future release.

Variables:
  • freq (int) – Recurrence frequency; one of the Frequency values.

  • interval (int) – Gap between occurrences in units of freq; values below 1 are normalized to 1 at engine construction.

  • dtstart (datetime.datetime | None) – First occurrence of the rule; defaults to “now” when omitted.

  • until (datetime.datetime | None) – Inclusive upper bound; occurrences after it are not returned.

  • count (int) – Maximum number of occurrences (including dtstart); 0 means unlimited.

  • by_hour (tuple[int, ...]) – Reserved; unused by the engine (Go parity).

  • by_minute (tuple[int, ...]) – Reserved; unused by the engine (Go parity).

  • by_second (tuple[int, ...]) – Reserved; unused by the engine (Go parity).

Example

>>> options = RRuleOptions(
...     freq=Frequency.DAILY,
...     interval=2,
...     dtstart=datetime(2026, 1, 1, 12, tzinfo=timezone.utc),
... )
by_hour: tuple[int, ...]
by_minute: tuple[int, ...]
by_second: tuple[int, ...]
count: int
dtstart: datetime | None
freq: int
interval: int
until: datetime | None