telebot_py.storage

Pluggable persistence backends: memory, JSON file, SQLite (FR-009).

telebot_py.storage.base

Persistence contract and the key/value adapter it can be built on (T032).

BasePersistence is the python-telegram-bot-shaped contract the kernel and persistent ConversationHandlers consume (contracts/public-api.md section 7). KeyValuePersistence implements that whole contract on top of the raw StorageDriver key/value surface, so new backends only provide get_raw/set_raw/delete_raw.

class telebot_py.storage.base.BasePersistence

Bases: ABC

Contract every persistence backend implements (FR-009).

All methods are awaitable so backends can keep I/O off the event loop. update_* replaces the stored data wholesale (python-telegram-bot semantics); refresh_* returns a fresh snapshot. Passing None as a conversation state deletes that conversation entry.

Example

>>> app = Application.builder().token(token).persistence(backend).build()
async flush() → None

Flush any buffered state to the underlying storage.

abstractmethod async get_bot_data() → dict[Any, Any]

Return the persisted global bot data dict.

Returns:

The bot data dict; empty when none stored.

abstractmethod async get_chat_data() → dict[int | str, dict[Any, Any]]

Return all persisted per-chat data dicts keyed by chat id.

Returns:

Mapping of chat ids to their data dicts; empty when none stored.

abstractmethod async get_conversations(name: str) → dict[tuple[Any, ...], Any]

Return all persisted conversation states for name.

Parameters:

name – The conversation handler’s persistence name.

Returns:

Mapping of conversation key tuples to stored states; empty when the name is unknown.

abstractmethod async get_user_data() → dict[int, dict[Any, Any]]

Return all persisted per-user data dicts keyed by user id.

Returns:

Mapping of user ids to their data dicts; empty when none stored.

async refresh_bot_data() → dict[Any, Any]

Return a fresh snapshot of the stored global bot data.

Returns:

The bot data dict.

async refresh_chat_data() → dict[int | str, dict[Any, Any]]

Return a fresh snapshot of the stored per-chat data.

Returns:

Mapping of chat ids to their data dicts.

async refresh_conversations(name: str) → dict[tuple[Any, ...], Any]

Return a fresh snapshot of the conversations stored under name.

Parameters:

name – The conversation handler’s persistence name.

Returns:

Mapping of conversation key tuples to stored states.

async refresh_user_data() → dict[int, dict[Any, Any]]

Return a fresh snapshot of the stored per-user data.

Returns:

Mapping of user ids to their data dicts.

async shutdown() → None

Flush and release backend resources.

abstractmethod async update_bot_data(bot_data: dict[Any, Any]) → None

Replace the stored global bot data.

Parameters:

bot_data – The new bot data dict.

abstractmethod async update_chat_data(chat_data: dict[int | str, dict[Any, Any]]) → None

Replace the stored per-chat data wholesale.

Parameters:

chat_data – The full mapping of chat ids to data dicts.

abstractmethod async update_conversation(name: str, key: tuple[Any, ...], state: Any) → None

Persist one conversation state (write-through on every transition).

Parameters:
  • name – The conversation handler’s persistence name.

  • key – The conversation key tuple, e.g. (chat_id, user_id).

  • state – The new state; None deletes the entry.

abstractmethod async update_user_data(user_data: dict[int, dict[Any, Any]]) → None

Replace the stored per-user data wholesale.

Parameters:

user_data – The full mapping of user ids to data dicts.

telebot_py.storage.base.ConversationKey

a tuple of chat/user/message ids depending on the handler’s per_chat/per_user/per_message flags.

Type:

Conversation storage key

alias of tuple[Any, …]

class telebot_py.storage.base.KeyValuePersistence(driver: StorageDriver | None = None)

Bases: BasePersistence

Adapter building the full contract on a raw key/value store.

Subclasses override get_raw(), set_raw(), and delete_raw() (or inject a StorageDriver) and inherit conversations, chat/user/bot data, and jobs for free.

Example

>>> class DictPersistence(KeyValuePersistence):
...     async def get_raw(self, key): return self.store.get(key)
...     async def set_raw(self, key, value): self.store[key] = value
...     async def delete_raw(self, key): self.store.pop(key, None)
async delete_raw(key: str) → None

Remove key from the backing driver; no-op when absent.

Parameters:

key – The storage key to delete.

Raises:

NotImplementedError – When no driver was injected and the subclass does not override this method.

async flush() → None

Flush the backing driver when one was injected.

async get_bot_data() → dict[Any, Any]

Return the persisted global bot data dict.

Returns:

The bot data dict; empty when none stored.

async get_chat_data() → dict[int | str, dict[Any, Any]]

Return all persisted per-chat data dicts keyed by chat id.

Returns:

Mapping of chat ids to their data dicts.

async get_conversations(name: str) → dict[tuple[Any, ...], Any]

Return all persisted conversation states for name.

Parameters:

name – The conversation handler’s persistence name.

Returns:

Mapping of conversation key tuples to stored states.

async get_jobs() → list[PersistedJob]

Return persisted job definitions, skipping corrupt rows.

Returns:

The list of persisted jobs, possibly empty.

async get_raw(key: str) → str | None

Return the raw value for key from the backing driver.

Parameters:

key – The storage key to look up.

Returns:

The raw JSON string, or None when absent.

Raises:

NotImplementedError – When no driver was injected and the subclass does not override this method.

async get_user_data() → dict[int, dict[Any, Any]]

Return all persisted per-user data dicts keyed by user id.

Returns:

Mapping of user ids to their data dicts.

async set_jobs(jobs: list[PersistedJob]) → None

Overwrite the persisted job definitions.

Parameters:

jobs – The full list of jobs to persist.

async set_raw(key: str, value: str) → None

Store value under key in the backing driver.

Parameters:
  • key – The storage key to write.

  • value – Serialized JSON text to persist.

Raises:

NotImplementedError – When no driver was injected and the subclass does not override this method.

async update_bot_data(bot_data: dict[Any, Any]) → None

Replace the stored global bot data.

Parameters:

bot_data – The new bot data dict.

async update_chat_data(chat_data: dict[int | str, dict[Any, Any]]) → None

Replace the stored per-chat data wholesale.

Parameters:

chat_data – The full mapping of chat ids to data dicts.

async update_conversation(name: str, key: tuple[Any, ...], state: Any) → None

Persist one conversation state; None deletes the entry.

Parameters:
  • name – The conversation handler’s persistence name.

  • key – The conversation key tuple.

  • state – The new state, or None to delete.

async update_user_data(user_data: dict[int, dict[Any, Any]]) → None

Replace the stored per-user data wholesale.

Parameters:

user_data – The full mapping of user ids to data dicts.

telebot_py.storage.driver

Pluggable storage driver interface and persisted value types.

Python parity of packages/node/src/storage/driver.ts: concrete backends (memory, JSON file, SQLite) expose a raw asynchronous key/value surface that KeyValuePersistence builds the full persistence contract on top of.

class telebot_py.storage.driver.PersistedJob(name: str, next_run: float, interval: float | None = None, rrule: str | dict[str, Any] | None = None, timezone: str | None = None, data: Any = None)

Bases: object

A scheduled job restored from persistence across restarts.

Variables:
  • name (str) – Unique name of the job.

  • next_run (float) – Epoch timestamp (seconds) of the job’s next execution.

  • interval (float | None) – Optional recurrence interval in seconds.

  • rrule (str | dict[str, Any] | None) – Optional RFC 5545 RRule string or options mapping.

  • timezone (str | None) – Optional IANA timezone name the schedule is evaluated in.

  • data (Any) – Optional serializable payload handed to the job callback.

data: Any = None
interval: float | None = None
name: str
next_run: float
rrule: str | dict[str, Any] | None = None
timezone: str | None = None
class telebot_py.storage.driver.StorageDriver(*args, **kwargs)

Bases: Protocol

Raw asynchronous key/value store persistence backends plug into.

Mirrors the node Persistence driver contract in snake_case: every operation is awaitable so backends can keep I/O off the event loop (worker threads, asyncio.to_thread). Keys are opaque strings; values are serialized JSON text.

async delete_raw(key: str) → None

Remove key from the store; no-op when absent.

Parameters:

key – The storage key to delete.

async flush() → None

Flush buffered writes to the underlying storage.

async get_raw(key: str) → str | None

Return the stored value for key, or None when absent.

Parameters:

key – The storage key to look up.

Returns:

The raw JSON string, or None if the key does not exist.

async set_raw(key: str, value: str) → None

Store value under key, overwriting any previous value.

Parameters:
  • key – The storage key to write.

  • value – Serialized JSON text to persist.

telebot_py.storage.json

JSON file-backed persistence with atomic writes (T033).

class telebot_py.storage.json.JSONPersistence(file_path: str | Path)

Bases: KeyValuePersistence

Persists the whole bot state to a single JSON file.

Every write serializes the full store to a temporary file in the target directory and atomically swaps it in with os.replace(), so readers never observe a truncated file. All file I/O runs in a worker thread via asyncio.to_thread() and never blocks the event loop.

Example

>>> persistence = JSONPersistence("data/state.json")
Variables:

file_path – The JSON file backing this persistence instance.

async delete_raw(key: str) → None

Remove key and rewrite the file atomically; no-op when absent.

Parameters:

key – The storage key to delete.

async flush() → None

Write the current store to disk, even when nothing changed.

async get_raw(key: str) → str | None

Return the stored value for key, or None when absent.

Parameters:

key – The storage key to look up.

Returns:

The raw JSON string, or None if the key does not exist.

async set_raw(key: str, value: str) → None

Store value under key and rewrite the file atomically.

Parameters:
  • key – The storage key to write.

  • value – Serialized JSON text to persist.

telebot_py.storage.memory

In-memory persistence backend (T032).

class telebot_py.storage.memory.MemoryPersistence

Bases: KeyValuePersistence

Default in-memory persistence; state is lost when the process exits.

Used implicitly when no backend is configured on the builder: data dicts live for the application’s lifetime and conversations start fresh on each run (documented behavior, no crash).

Example

>>> app = Application.builder().token(token).persistence(MemoryPersistence()).build()
async delete_raw(key: str) → None

Remove key from the store; no-op when absent.

Parameters:

key – The storage key to delete.

async get_raw(key: str) → str | None

Return the stored value for key, or None when absent.

Parameters:

key – The storage key to look up.

Returns:

The raw JSON string, or None if the key does not exist.

async set_raw(key: str, value: str) → None

Store value under key, overwriting any previous value.

Parameters:
  • key – The storage key to write.

  • value – Serialized JSON text to persist.

telebot_py.storage.sqlite

SQLite-backed persistence on the stdlib sqlite3 module (T034).

A single dedicated worker thread owns the database connection; every operation is submitted through a queue and awaited as a future, so the event loop never blocks and concurrent callers are serialized without corrupting state. The database runs in WAL mode for concurrent readers.

class telebot_py.storage.sqlite.SQLitePersistence(db_path: str | Path)

Bases: BasePersistence

Durable persistence backed by a SQLite database file.

All access happens on one worker thread (stdlib sqlite3 connections are not safe to share across threads); awaitables handed back to callers resolve when the worker finishes, keeping the event loop free.

Example

>>> persistence = SQLitePersistence("data/bot.sqlite3")
Variables:

db_path – The database file path (or ":memory:").

async flush() → None

No-op: every operation commits before resolving.

async get_bot_data() → dict[Any, Any]

Return the persisted global bot data dict.

Returns:

The bot data dict; empty when none stored.

async get_chat_data() → dict[int | str, dict[Any, Any]]

Return all persisted per-chat data dicts keyed by chat id.

Returns:

Mapping of chat ids to their data dicts.

async get_conversations(name: str) → dict[tuple[Any, ...], Any]

Return all persisted conversation states for name.

Parameters:

name – The conversation handler’s persistence name.

Returns:

Mapping of conversation key tuples to stored states.

async get_user_data() → dict[int, dict[Any, Any]]

Return all persisted per-user data dicts keyed by user id.

Returns:

Mapping of user ids to their data dicts.

async shutdown() → None

Stop the worker thread and close the connection; idempotent.

async update_bot_data(bot_data: dict[Any, Any]) → None

Replace the stored global bot data.

Parameters:

bot_data – The new bot data dict.

async update_chat_data(chat_data: dict[int | str, dict[Any, Any]]) → None

Replace the stored per-chat data wholesale.

Parameters:

chat_data – The full mapping of chat ids to data dicts.

async update_conversation(name: str, key: tuple[Any, ...], state: Any) → None

Persist one conversation state; None deletes the entry.

Parameters:
  • name – The conversation handler’s persistence name.

  • key – The conversation key tuple.

  • state – The new state, or None to delete.

async update_user_data(user_data: dict[int, dict[Any, Any]]) → None

Replace the stored per-user data wholesale.

Parameters:

user_data – The full mapping of user ids to data dicts.