Storage Engines

Every time you login to Telegram, some personal piece of data are created and held by both parties (the client, Pyrogram and the server, Telegram). This session data is uniquely bound to your own account, indefinitely (until you logout or decide to manually terminate it) and is used to authorize a client to execute API calls on behalf of your identity.


Persisting Sessions

In order to make a client reconnect successfully between restarts, that is, without having to start a new authorization process from scratch each time, Pyrogram needs to store the generated session data somewhere.

Different Storage Engines

Pyrogram offers two different types of storage engines: a File Storage and a Memory Storage. These engines are well integrated in the framework and require a minimal effort to set up. Here’s how they work:

File Storage

This is the most common storage engine. It is implemented by using SQLite, which will store the session details. The database will be saved to disk as a single portable file and is designed to efficiently store and retrieve data whenever they are needed.

To use this type of engine, simply pass any name of your choice to the name parameter of the Client constructor, as usual:

from pyrogram import Client

async with Client("my_account") as app:
    print(await app.get_me())

Once you successfully log in (either with a user or a bot identity), a session file will be created and saved to disk as my_account.session. Any subsequent client restart will make Pyrogram search for a file named that way and the session database will be automatically loaded.

Memory Storage

In case you don’t want to have any session file saved to disk, you can use an in-memory storage by passing True to the in_memory parameter of the Client constructor:

from pyrogram import Client

async with Client("my_account", in_memory=True) as app:
    print(await app.get_me())

This storage engine is still backed by SQLite, but the database exists purely in memory. This means that, once you stop a client, the entire database is discarded and the session details used for logging in again will be lost forever.

Session Strings

In case you want to use an in-memory storage, but also want to keep access to the session you created, call export_session_string() anytime before stopping the client…

from pyrogram import Client

async with Client("my_account", in_memory=True) as app:
    print(await app.export_session_string())

…and save the resulting string. You can use this string by passing it as Client argument the next time you want to login using the same session; the storage used will still be in-memory:

from pyrogram import Client

session_string = "...ZnUIFD8jsjXTb8g_vpxx48k1zkov9sapD-tzjz-S4WZv70M..."

async with Client("my_account", session_string=session_string) as app:
    print(await app.get_me())

Session strings are useful when you want to run authorized Pyrogram clients on platforms where their ephemeral filesystems makes it harder for a file-based storage engine to properly work as intended.

Custom Storages

If you want to use a custom storage engine, you can do so by implementing the Storage class. This class is an base class that defines the interface that all storage engines must implement.

This class is a class that cannot be instantiated, but can be used to define a common interface for its subclasses. In this case, the Storage class defines the interface that all storage engines must implement.

Custom Storage can be defined in Client by passing storage_engine parameter with a Storage subclass.

class pyrogram.storage.storage.Storage(name: str)

Abstract class for storage engines.

Parameters:

name (str) – The name of the session.

abstractmethod async open()

Opens the storage engine.

abstractmethod async save()

Saves the current state of the storage engine.

abstractmethod async close()

Closes the storage engine.

abstractmethod async delete()

Deletes the storage file.

abstractmethod async update_peers(peers: List[Tuple[int, int, str, str]]) None

Update the peers table with the provided information.

Parameters:

peers (List[Tuple[int, int, str, str]]) – A list of tuples containing the information of the peers to be updated. Each tuple must contain the following information: - int: The peer id. - int: The peer access hash. - str: The peer type (user, chat or channel). - str: The peer phone number (if any).

abstractmethod async update_usernames(usernames: List[Tuple[int, List[str]]]) None

Update the usernames table with the provided information.

Parameters:

usernames (List[Tuple[int, List[str]]]) – A list of tuples containing the information of the usernames to be updated. Each tuple must contain the following information: - int: The peer id. - List of str: The peer username (if any).

abstractmethod async update_state(update_state: Tuple[int, int, int, int, int]=<class 'object'>) Tuple[int, int, int, int, int]

Get or set the update state of the current session.

Parameters:

update_state (Tuple[int, int, int, int, int]) – A tuple containing the update state to set. Tuple must contain the following information: - int: The id of the entity. - int: The pts. - int: The qts. - int: The date. - int: The seq.

abstractmethod async get_peer_by_id(peer_id: int) InputPeer

Retrieve a peer by its ID.

Parameters:

peer_id (int) – The ID of the peer to retrieve.

abstractmethod async get_peer_by_username(username: str) InputPeer

Retrieve a peer by its username.

Parameters:

username (str) – The username of the peer to retrieve.

abstractmethod async get_peer_by_phone_number(phone_number: str) InputPeer

Retrieve a peer by its phone number.

Parameters:

phone_number (str) – The phone number of the peer to retrieve.

abstractmethod async dc_id(value: int = <class 'object'>) int

Get or set the DC ID of the current session.

Parameters:

value (int, optional) – The DC ID to set.

abstractmethod async api_id(value: int = <class 'object'>) int

Get or set the API ID of the current session.

Parameters:

value (int, optional) – The API ID to set.

abstractmethod async server_address(value: str = <class 'object'>) str

Get or set the server address of the current session.

Parameters:

value (str, optional) – The server address to set.

abstractmethod async port(value: int = <class 'object'>) int

Get or set the server port of the current session.

Parameters:

value (int, optional) – The server port to set.

abstractmethod async test_mode(value: bool = <class 'object'>) bool

Get or set the test mode of the current session.

Parameters:

value (bool, optional) – The test mode to set.

abstractmethod async auth_key(value: bytes = <class 'object'>) bytes

Get or set the authorization key of the current session.

Parameters:

value (bytes, optional) – The authorization key to set.

abstractmethod async date(value: int = <class 'object'>) int

Get or set the date of the current session.

Parameters:

value (int, optional) – The date to set.

abstractmethod async user_id(value: int = <class 'object'>) int

Get or set the user ID of the current session.

Parameters:

value (int, optional) – The user ID to set.

abstractmethod async is_bot(value: bool = <class 'object'>) bool

Get or set the bot flag of the current session.

Parameters:

value (bool, optional) – The bot flag to set.

async export_session_string() str

Exports the session string for the current session.

Returns:

str – The session string for the current session.

Example of Telethon Storage

If you want to use sessions from telethon in pyrogram (originally incompatible), you can use this storage.

This storage is almost completely identical and once used in pyrogram can be reused in telethon without breaking session integrity.

from pyrogram import Client
from .storage import TelethonStorage # assumes that the path downloaded is accurate

async def main():
    workdir = Path(__file__).parent

    app = Client(
        session_name,
        api_id=api_id,
        api_hash=api_hash
    )
    app.storage = TelethonStorage(client=app)

    await app.start()

    await app.send_message(chat_id="me", text="Greetings from **Pyrogram**!")

    await app.stop()

loop = asyncio.new_event_loop()
loop.run_until_complete(main())