Skip to content

Test#65

Merged
5hojib merged 17 commits into
devfrom
test
Feb 10, 2026
Merged

Test#65
5hojib merged 17 commits into
devfrom
test

Conversation

@5hojib

@5hojib 5hojib commented Feb 10, 2026

Copy link
Copy Markdown
Member

Summary by Sourcery

Add support for new Telegram API features including suggested posts, fact-check metadata, advanced gifts, button styles, and paid/quick-reply messaging options, while updating filesystem operations to use async paths and improving compiler and CI tooling.

New Features:

  • Expose concrete type exports for Pyrogram types, bots, bot commands, stories, business objects, and updates to provide a stable public surface.
  • Extend the Gift payment type with additional fields and support for unique gifts from new raw API types.
  • Augment Message, Chat, and User models with fields for stories, paid content, fact-checking, scheduling, and verification metadata from the latest Telegram API.
  • Introduce SuggestedPost, StarsAmount, and FactCheck helper types to represent new business and message metadata structures.
  • Allow button types to accept a higher-level ButtonStyle enum and optional icon, with automatic conversion to KeyboardButtonStyle.
  • Add rich options to message and media sending methods (schedule repeat, paid stars, quick replies, send-as, background sending, drafts, and suggested posts).
  • Add helper for building InputQuickReplyShortcut objects from IDs or names.

Enhancements:

  • Switch many file existence and directory operations from pathlib.Path to anyio.AsyncPath to avoid blocking the event loop in async contexts.
  • Simplify and harden compiler/docs generation by using pathlib, list comprehensions, and safer directory traversal.
  • Mark core TLObject and Object types as unhashable to prevent misuse in hashed collections.
  • Tighten chat privileges with a flag for managing direct messages and expose additional chat/user metadata like bot verification icons and paid message pricing.

Build:

  • Ensure compiler steps only run for wheel/install Hatch targets and relax some ruff lint rules while adding anyio as a runtime dependency.

CI:

  • Update formatting-and-tests workflow to install pytest-asyncio alongside pytest for running async tests.

Documentation:

  • Generate explicit all exports for several type submodules and adjust docs compiler to better categorize and list types and enums in the API reference.

Tests:

  • Enable pytest-asyncio with function-level async loop scope in project configuration and CI, and ensure CI installs pytest-asyncio.

Chores:

  • Add a script to scrape and cache Telegram API documentation metadata for constructors, methods, and types for internal compiler use.

Mehedi Hasan Shojib and others added 14 commits February 9, 2026 15:55
* Fix ruff issues and pytest failures

- Installed missing dependencies (tgcrypto) to fix pytest failures.
- Replaced star imports with explicit imports in `pyrogram/types/__init__.py` to resolve F405 issues.
- Fixed unused imports in sub-package `__init__.py` files using explicit re-exports (as Class).
- Added `__hash__ = None` to `Object` and `TLObject` to resolve PLW1641.
- Updated compiler scripts to use `pathlib` and resolved other ruff warnings (B007, PERF401, C901).
- Corrected grouping of story-related types in the main types entry point.

Co-authored-by: 5hojib <[email protected]>

* Fix ruff issues and pytest failures

- Installed missing dependencies (tgcrypto) to fix pytest failures.
- Replaced star imports with explicit imports in `pyrogram/types/__init__.py`.
- Fixed unused imports in sub-package `__init__.py` files using `__all__` for re-exports.
- Added `__hash__ = None` to `Object` and `TLObject` to resolve PLW1641.
- Updated compiler scripts to use `pathlib` and resolved other ruff warnings (B007, PERF401, C901).
- Corrected grouping of story-related types in the main types entry point.

Co-authored-by: 5hojib <[email protected]>

* Fix ruff issues and pytest failures

- Installed missing dependencies (tgcrypto) to fix pytest failures.
- Replaced star imports with explicit imports in `pyrogram/types/__init__.py`.
- Fixed unused imports in sub-package `__init__.py` files using `__all__` for re-exports.
- Added `__hash__ = None` to `Object` and `TLObject` to resolve PLW1641.
- Updated compiler scripts to use `pathlib` and resolved other ruff warnings (B007, PERF401, C901).
- Corrected grouping of story-related types in the main types entry point.

Co-authored-by: 5hojib <[email protected]>

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Added `[tool.pytest.ini_options]` to `pyproject.toml` with:
- `asyncio_mode = "auto"` to automatically handle `async def` tests.
- `asyncio_default_test_loop_scope = "function"` to comply with newer `pytest-asyncio` requirements.

This resolves the issue where pytest failed with "async def functions are not natively supported".

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit adds a new script `compiler/api/scrape_docs.py` that scrapes
the latest Telegram MTProto API documentation from Corefork. It also
updates `compiler/api/docs.json` with the newly scraped data, providing
better descriptions and parameter documentation for many API methods
and types.

The generated files in `pyrogram/raw` were verified to be in sync with
the updated documentation.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Signed-off-by: 5hojib <[email protected]>
- Added FactCheck, StarsAmount, and SuggestedPost high-level types.
- Added missing fields to Message, User, Chat, and ChatPrivileges types.
- Updated send_message, send_photo, send_video, send_animation, and send_media_group methods with missing parameters.
- Fixed Gift._parse to handle optional fields correctly.
- Added get_input_quick_reply_shortcut utility.
- Ensured compatibility with the latest MTProto layer.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Signed-off-by: 5hojib <[email protected]>
Replaced blocking pathlib.Path methods (is_file, unlink, mkdir) with
their asynchronous equivalents from anyio.Path in async functions.
Added anyio as a dependency to the project.

This prevents the event loop from being blocked during filesystem operations,
improving performance and reliability of the library.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Signed-off-by: 5hojib <[email protected]>
This change introduces the `ButtonStyle` enum in `pyrogram.enums` with `PRIMARY`, `DANGER`, and `SUCCESS` values.
It also updates `InlineKeyboardButton`, `KeyboardButton`, and `InlineKeyboardButtonBuy` to accept this enum for their `style` parameter, making it easier to style buttons without manually instantiating `KeyboardButtonStyle`.

A new static method `_parse` was added to `KeyboardButtonStyle` to handle the conversion from `ButtonStyle` to `KeyboardButtonStyle`.

Example usage:
```python
InlineKeyboardButton(text="E", switch_inline_query="E", style=ButtonStyle.SUCCESS)
```

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Signed-off-by: 5hojib <[email protected]>
This commit adds an `icon` parameter (of type `int`) to the high-level
`InlineKeyboardButton`, `KeyboardButton`, and `InlineKeyboardButtonBuy`
classes. This allows users to directly set a custom icon for buttons
without having to manually construct a `KeyboardButtonStyle` object.

The `KeyboardButtonStyle` class was also updated to ensure `icon` is
treated as an integer throughout the library's high-level API.
Docstrings were updated for all modified classes.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
@sourcery-ai

sourcery-ai Bot commented Feb 10, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Refactors type exports to be explicit, adds new business, messaging, chat, user, and payment features (Stars, gifts, suggested posts, fact-checking, button styles, stories), introduces async filesystem handling with anyio, extends message-sending APIs, and adds new tooling for docs scraping and async testing support.

Sequence diagram for sending media with async filesystem and suggested posts

sequenceDiagram
    actor Bot
    participant Client
    participant Utils
    participant AsyncFS as AsyncPath
    participant Telegram as TelegramAPI

    Bot->>Client: send_photo(chat_id, photo, schedule_repeat_period, allow_paid_stars, quick_reply_shortcut, send_as, background, clear_draft, update_stickersets_order, suggested_post)
    activate Client

    Client->>AsyncFS: is_file(photo)
    alt local file
        AsyncFS-->>Client: True
        Client->>Client: save_file(photo)
        Client->>Telegram: messages.UploadMedia(peer, file)
        Telegram-->>Client: InputMedia
    else remote or file_id
        AsyncFS-->>Client: False
        Client->>Client: prepare InputMedia without upload
    end

    alt quick_reply_shortcut provided
        Client->>Utils: get_input_quick_reply_shortcut(shortcut)
        Utils-->>Client: InputQuickReplyShortcut
    end

    alt send_as provided
        Client->>Client: resolve_peer(send_as)
    end

    alt suggested_post provided
        Client->>Client: suggested_post.write()
        Client-->>Client: raw.types.SuggestedPost
    end

    Client->>Telegram: messages.SendMedia(schedule_repeat_period, allow_paid_stars, background, clear_draft, update_stickersets_order, quick_reply_shortcut, send_as, suggested_post, allow_paid_floodskip, other params...)
    Telegram-->>Client: Updates
    Client-->>Bot: Message
    deactivate Client
Loading

Class diagram for button classes and ButtonStyle

classDiagram
    class AutoName {
    }

    class ButtonStyle {
        <<enum>>
        PRIMARY
        DANGER
        SUCCESS
    }

    AutoName <|-- ButtonStyle

    class KeyboardButtonStyle {
        +bool bg_primary
        +bool bg_danger
        +bool bg_success
        +int icon
        +write() raw.types.KeyboardButtonStyle
        +_parse(style) KeyboardButtonStyle
    }

    class InlineKeyboardButton {
        +str text
        +str url
        +str callback_data
        +str switch_inline_query
        +str switch_inline_query_current_chat
        +CallbackGame callback_game
        +bool requires_password
        +str copy_text
        +KeyboardButtonStyle style
        +int icon
        +InlineKeyboardButton__init__(text, url, callback_data, switch_inline_query, switch_inline_query_current_chat, callback_game, requires_password, copy_text, style, icon)
        +read(b) InlineKeyboardButton
    }

    class InlineKeyboardButtonBuy {
        +str text
        +KeyboardButtonStyle style
        +int icon
        +InlineKeyboardButtonBuy__init__(text, style, icon)
        +read(b) InlineKeyboardButtonBuy
    }

    class KeyboardButton {
        +str text
        +RequestPeerTypeChat request_chat
        +RequestPeerTypeUser request_user
        +WebAppInfo web_app
        +KeyboardButtonStyle style
        +int icon
        +KeyboardButton__init__(text, request_chat, request_user, web_app, style, icon)
        +read(b) KeyboardButton
    }

    KeyboardButtonStyle ..> ButtonStyle : _parse(style)
    InlineKeyboardButton --> KeyboardButtonStyle : style
    InlineKeyboardButtonBuy --> KeyboardButtonStyle : style
    KeyboardButton --> KeyboardButtonStyle : style
Loading

File-Level Changes

Change Details Files
Replace wildcard type imports/exports with explicit symbols and expose new business, bot, story, and messaging types.
  • Expand pyrogram.types.init to import concrete classes from submodules instead of using wildcard imports.
  • Update all lists in types.init, business.init, bots.init, bot_commands.init, stories.init, and messages_and_media.init to include new symbols like FactCheck, StarsAmount, SuggestedPost, BotAllowed, and Update.
  • Ensure new SuggestedPost and StarsAmount types are exported for external use.
pyrogram/types/__init__.py
pyrogram/types/business/__init__.py
pyrogram/types/bots/__init__.py
pyrogram/types/bot_commands/__init__.py
pyrogram/types/stories/__init__.py
pyrogram/types/messages_and_media/__init__.py
Extend payments Gift model and parsing to support new StarGift variations including unique gifts and many new metadata fields.
  • Add numerous optional fields to Gift documenting gift lifecycle, auction/upgrade properties, ownership, and value metadata.
  • Relax constructor parameter types for sticker and star counts to be optional to support unique gifts without stickers or star counts.
  • Change _parse to accept raw.base.StarGift and branch between StarGift and new StarGiftUnique, mapping raw attributes (including peer IDs and timestamps) into the extended Gift dataclass.
  • Return None when an unsupported StarGift variant is passed instead of raising.
pyrogram/types/payments/gift.py
Enrich Message, Chat, User, and ChatPrivileges domain models with new flags and monetization-related fields.
  • Add offline, video_processing_pending, paid_suggested_post_* flags, FactCheck, SuggestedPost, paid_message_stars, schedule_repeat_period, summary_from_language and report_delivery_until_date to Message; wire them in init and _parse.
  • Extend Chat with story visibility, signature/autotranslation/broadcast/forum flags plus bot_verification_icon, send_paid_messages_stars, and linked_monoforum_id, including parsing from raw Channel.
  • Extend User with bot edit/close-friend/stories/app/forum flags plus bot_verification_icon and send_paid_messages_stars; parse from raw User.
  • Add can_manage_direct_messages to ChatPrivileges and map it from raw admin_rights.manage_direct_messages.
pyrogram/types/messages_and_media/message.py
pyrogram/types/user_and_chats/chat.py
pyrogram/types/user_and_chats/user.py
pyrogram/types/user_and_chats/chat_privileges.py
Introduce new business and messaging helper types for Stars and suggested posts, and FactCheck support.
  • Add StarsAmount type with write/_parse bridging to raw StarsAmount/StarsTonAmount.
  • Add SuggestedPost type supporting accepted/rejected flags, price (StarsAmount), and schedule_date with write/_parse helpers.
  • Add FactCheck type to represent server-provided fact-check data, including TextWithEntities mapping from raw.FactCheck.
  • Wire FactCheck and SuggestedPost into Message parsing and exports so they are usable in the high-level API.
pyrogram/types/business/stars_amount.py
pyrogram/types/business/suggested_post.py
pyrogram/types/messages_and_media/fact_check.py
pyrogram/types/messages_and_media/message.py
pyrogram/types/business/__init__.py
pyrogram/types/messages_and_media/__init__.py
Add ButtonStyle enum and extend keyboard button types to accept style enums and custom icons with proper parsing.
  • Introduce enums.ButtonStyle with PRIMARY/DANGER/SUCCESS values and export it via enums.init.py.
  • Add a KeyboardButtonStyle._parse helper that converts ButtonStyle enum values into KeyboardButtonStyle instances and passes through existing instances.
  • Update InlineKeyboardButton, InlineKeyboardButtonBuy, and KeyboardButton to accept style as KeyboardButtonStyle or ButtonStyle plus an optional icon, normalizing into a KeyboardButtonStyle and mutating icon when provided.
pyrogram/enums/button_style.py
pyrogram/enums/__init__.py
pyrogram/types/bots_and_keyboards/keyboard_button_style.py
pyrogram/types/bots_and_keyboards/inline_keyboard_button.py
pyrogram/types/bots_and_keyboards/inline_keyboard_button_buy.py
pyrogram/types/bots_and_keyboards/keyboard_button.py
Extend send_* message/story APIs to support new scheduling, paid stars, quick replies, send-as, background sending, and suggested post metadata.
  • Add schedule_repeat_period, allow_paid_stars, quick_reply_shortcut, send_as, background, clear_draft, update_stickersets_order, and suggested_post parameters with documentation to send_message and media-sending methods (photo, video, animation, media_group, audio, document, sticker, video_note, voice, paid_media).
  • Thread new arguments into raw.functions.messages.Send* invocations, resolving send_as peers and quick reply shortcuts via new utils.get_input_quick_reply_shortcut helper and SuggestedPost.write().
  • Update places that checked Path(...).is_file() to use AsyncPath(...).is_file() because these functions are async.
  • Extend stories edit/send methods, set_chat_photo, and sticker set methods to use AsyncPath for async path checks.
pyrogram/methods/messages/send_message.py
pyrogram/methods/messages/send_photo.py
pyrogram/methods/messages/send_video.py
pyrogram/methods/messages/send_animation.py
pyrogram/methods/messages/send_media_group.py
pyrogram/methods/messages/send_audio.py
pyrogram/methods/messages/send_document.py
pyrogram/methods/messages/send_sticker.py
pyrogram/methods/messages/send_video_note.py
pyrogram/methods/messages/send_voice.py
pyrogram/methods/messages/send_paid_media.py
pyrogram/methods/stories/send_story.py
pyrogram/methods/stories/edit_story.py
pyrogram/methods/chats/set_chat_photo.py
pyrogram/methods/stickers/add_sticker_to_set.py
pyrogram/methods/stickers/create_sticker_set.py
pyrogram/utils.py
Adopt anyio.AsyncPath for async filesystem operations across the client, storage, and media editing paths.
  • Add anyio>=4.12.1 as a dependency in pyproject.toml.
  • Replace synchronous pathlib.Path.is_file/mkdir/unlink usages inside async functions with AsyncPath equivalents in file_storage, client.handle_download, media send/edit helpers, and compiler/docs tooling.
  • Guard AsyncPath usage with appropriate type checking where necessary (e.g., TYPE_CHECKING imports).
pyproject.toml
pyrogram/storage/file_storage.py
pyrogram/client.py
pyrogram/methods/messages/send_animation.py
pyrogram/methods/messages/send_photo.py
pyrogram/methods/messages/send_video.py
pyrogram/methods/messages/send_media_group.py
pyrogram/methods/messages/edit_message_media.py
pyrogram/methods/messages/edit_inline_media.py
pyrogram/methods/messages/send_paid_media.py
pyrogram/methods/stories/edit_story.py
pyrogram/methods/stories/send_story.py
pyrogram/methods/chats/set_chat_photo.py
pyrogram/methods/messages/send_audio.py
pyrogram/methods/messages/send_document.py
pyrogram/methods/messages/send_sticker.py
pyrogram/methods/messages/send_video_note.py
pyrogram/methods/messages/send_voice.py
pyrogram/methods/stickers/add_sticker_to_set.py
pyrogram/methods/stickers/create_sticker_set.py
compiler/docs/compiler.py
compiler/errors/compiler.py
Improve compiler tooling and docs generation and add async pytest configuration and workflow updates.
  • Refactor compiler.docs.compiler.generate_raw to use pathlib.Path.iterdir and avoid manual os.listdir string concatenations; simplify AST walking when collecting classes and enums, and ignore category_name from types_categories iteration.
  • Add a new compiler/api/scrape_docs.py script that parses TL schema, scrapes Telegram core documentation pages asynchronously with httpx+lxml, and populates docs.json.
  • Change compiler.errors.compiler to use pathlib.Path.iterdir for file listing and write consistent all.py.
  • Configure pytest asyncio mode and scope in pyproject.toml and adjust GitHub workflow to install pytest-asyncio and not continue-on-error for tests.
  • Add hash = None to TLObject and Object base classes to explicitly mark them unhashable and quiet linters.
compiler/docs/compiler.py
compiler/errors/compiler.py
compiler/api/scrape_docs.py
pyproject.toml
.github/workflows/format_and_test.yml
pyrogram/raw/core/tl_object.py
pyrogram/types/object.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @5hojib, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request integrates several new Telegram API features, particularly those related to paid content, suggested posts, and advanced message sending options. It also refactors file operations to leverage asynchronous anyio.Path for improved efficiency and introduces a new utility for scraping API documentation. These changes collectively enhance the library's ability to interact with the latest Telegram functionalities and improve its internal asynchronous architecture.

Highlights

  • Asynchronous File Operations: Migrated numerous file system operations across various modules to use anyio.Path for asynchronous handling, enhancing performance and consistency in async contexts.
  • New Telegram Features Support: Expanded several message sending methods (send_animation, send_media_group, send_message, send_photo, send_video) to support new parameters related to message scheduling, paid content (stars), quick replies, sending as a specific peer, background sending, draft clearing, sticker set order updates, and suggested posts.
  • New Types and Enums: Introduced new types such as StarsAmount, SuggestedPost, and FactCheck to support new Telegram features, and added a ButtonStyle enumeration for defining button styles.
  • Documentation Scraper: Added a new script (scrape_docs.py) to programmatically fetch and process Telegram API documentation, which will be used to generate docs.json.
  • Type Enhancements: Updated Gift, Chat, and User types with numerous new attributes to reflect recent Telegram API changes, including detailed gift properties, chat story settings, and bot capabilities.
Changelog
  • compiler/api/scrape_docs.py
    • Added a new script to scrape Telegram API documentation for types, constructors, and methods.
  • compiler/docs/compiler.py
    • Removed os module import.
    • Updated directory iteration to use Path.iterdir() instead of os.listdir().
    • Changed the key for all_entities from last to path_obj.name.
    • Updated file opening to use Path(file).open().
    • Refactored class definition collection to use extend with a generator expression for conciseness.
    • Removed unused cat_name variable from types_categories.items() iteration.
  • compiler/errors/compiler.py
    • Removed os module import.
    • Updated file listing to use Path(HOME, "source").iterdir().
  • hatch_build.py
    • Added noqa: PLC0415 comments to imports to suppress Pylint warnings.
  • pyproject.toml
    • Added anyio>=4.12.1 to project dependencies.
    • Added [tool.pytest.ini_options] section to configure asyncio_mode and asyncio_default_test_loop_scope for pytest.
    • Added COM812 to the ignore list for linting.
  • pyrogram/client.py
    • Imported AsyncPath from anyio.
    • Updated handle_download to use AsyncPath for creating directories and unlinking temporary files asynchronously.
  • pyrogram/enums/init.py
    • Imported ButtonStyle.
    • Added ButtonStyle to the __all__ export list.
  • pyrogram/enums/button_style.py
    • Added a new enumeration ButtonStyle with PRIMARY, DANGER, and SUCCESS values.
  • pyrogram/methods/chats/set_chat_photo.py
    • Replaced pathlib.Path with anyio.Path for asynchronous file existence checks.
  • pyrogram/methods/messages/edit_inline_media.py
    • Imported AsyncPath from anyio.
    • Updated file existence check for is_uploaded_file to use AsyncPath.
  • pyrogram/methods/messages/edit_message_media.py
    • Imported AsyncPath from anyio.
    • Updated file existence checks for various media types (InputMediaPhoto, InputMediaVideo, InputMediaAudio, InputMediaAnimation, InputMediaDocument) to use AsyncPath.
  • pyrogram/methods/messages/send_animation.py
    • Imported AsyncPath from anyio.
    • Added new parameters: schedule_repeat_period, allow_paid_stars, quick_reply_shortcut, send_as, background, clear_draft, update_stickersets_order, and suggested_post.
    • Updated file existence check for animation to use AsyncPath.
    • Modified the raw.functions.messages.SendMedia call to include the newly added parameters.
  • pyrogram/methods/messages/send_audio.py
    • Imported AsyncPath from anyio.
    • Updated file existence check for audio to use AsyncPath.
  • pyrogram/methods/messages/send_document.py
    • Imported AsyncPath from anyio.
    • Updated file existence check for document to use AsyncPath.
  • pyrogram/methods/messages/send_media_group.py
    • Imported AsyncPath from anyio.
    • Added new parameters: schedule_repeat_period, allow_paid_stars, quick_reply_shortcut, send_as, background, clear_draft, and update_stickersets_order.
    • Updated file existence checks for various media types (InputMediaPhoto, InputMediaVideo, InputMediaAnimation, InputMediaAudio, InputMediaDocument) to use AsyncPath.
    • Modified the raw.functions.messages.SendMultiMedia call to include the newly added parameters.
  • pyrogram/methods/messages/send_message.py
    • Added new parameters: schedule_repeat_period, allow_paid_stars, quick_reply_shortcut, send_as, background, clear_draft, update_stickersets_order, and suggested_post.
    • Modified the raw.functions.messages.SendMessage call to include the newly added parameters.
  • pyrogram/methods/messages/send_paid_media.py
    • Imported AsyncPath from anyio.
    • Updated file existence checks for InputPaidMediaPhoto and InputPaidMediaVideo to use AsyncPath.
  • pyrogram/methods/messages/send_photo.py
    • Replaced pathlib.Path with anyio.Path for imports.
    • Added new parameters: schedule_repeat_period, allow_paid_stars, quick_reply_shortcut, send_as, background, clear_draft, update_stickersets_order, and suggested_post.
    • Updated file existence check for photo to use AsyncPath.
    • Modified the raw.functions.messages.SendMedia call to include the newly added parameters.
  • pyrogram/methods/messages/send_sticker.py
    • Imported AsyncPath from anyio.
    • Updated file existence check for sticker to use AsyncPath.
  • pyrogram/methods/messages/send_video.py
    • Imported AsyncPath from anyio.
    • Added new parameters: schedule_repeat_period, allow_paid_stars, quick_reply_shortcut, send_as, background, clear_draft, update_stickersets_order, and suggested_post.
    • Updated file existence check for video to use AsyncPath.
    • Modified the raw.functions.messages.SendMedia call to include the newly added parameters.
  • pyrogram/methods/messages/send_video_note.py
    • Replaced pathlib.Path with anyio.Path for imports.
    • Updated file existence check for video_note to use AsyncPath.
  • pyrogram/methods/messages/send_voice.py
    • Replaced pathlib.Path with anyio.Path for imports.
    • Updated file existence check for voice to use AsyncPath.
  • pyrogram/methods/stickers/add_sticker_to_set.py
    • Replaced pathlib.Path with anyio.Path for imports.
    • Updated file existence check for sticker to use AsyncPath.
  • pyrogram/methods/stickers/create_sticker_set.py
    • Replaced pathlib.Path with anyio.Path for imports.
    • Updated file existence check for sticker to use AsyncPath.
  • pyrogram/methods/stories/edit_story.py
    • Replaced pathlib.Path with anyio.Path for imports.
    • Updated file existence checks for animation, photo, and video to use AsyncPath.
  • pyrogram/methods/stories/send_story.py
    • Replaced pathlib.Path with anyio.Path for imports.
    • Updated file existence checks for photo and video to use AsyncPath.
  • pyrogram/raw/core/tl_object.py
    • Set __hash__ = None to explicitly make instances of TLObject unhashable.
  • pyrogram/storage/file_storage.py
    • Imported AsyncPath from anyio and Path for type hinting.
    • Updated open method to use AsyncPath for checking file existence.
    • Updated delete method to use AsyncPath for unlinking the database file.
  • pyrogram/types/init.py
    • Updated imports from wildcard (*) to explicit imports for better readability and maintainability.
    • Added FactCheck, StarsAmount, SuggestedPost, and Update to the __all__ export list.
  • pyrogram/types/bot_commands/init.py
    • Added an explicit __all__ list for exports.
  • pyrogram/types/bots/init.py
    • Added an explicit __all__ list for exports.
  • pyrogram/types/bots_and_keyboards/inline_keyboard_button.py
    • Imported enums.
    • Added an icon parameter to the InlineKeyboardButton class.
    • Updated style parsing to accept and handle enums.ButtonStyle in addition to types.KeyboardButtonStyle.
  • pyrogram/types/bots_and_keyboards/inline_keyboard_button_buy.py
    • Imported enums.
    • Added an icon parameter to the InlineKeyboardButtonBuy class.
    • Updated style parsing to accept and handle enums.ButtonStyle.
  • pyrogram/types/bots_and_keyboards/keyboard_button.py
    • Imported enums.
    • Added an icon parameter to the KeyboardButton class.
    • Updated style parsing to accept and handle enums.ButtonStyle.
  • pyrogram/types/bots_and_keyboards/keyboard_button_style.py
    • Imported enums.
    • Added a static method _parse to convert enums.ButtonStyle to KeyboardButtonStyle.
  • pyrogram/types/business/init.py
    • Added StarsAmount and SuggestedPost to the __all__ export list.
  • pyrogram/types/business/stars_amount.py
    • Added a new type StarsAmount to represent star amounts, including amount and optional nanos.
  • pyrogram/types/business/suggested_post.py
    • Added a new type SuggestedPost to represent suggested post information, including acceptance status, price, and schedule date.
  • pyrogram/types/messages_and_media/init.py
    • Added FactCheck to the __all__ export list.
  • pyrogram/types/messages_and_media/fact_check.py
    • Added a new type FactCheck for message fact-checks, including need_check, country, text, and hash.
  • pyrogram/types/messages_and_media/message.py
    • Added new attributes to the Message class: offline, video_processing_pending, paid_suggested_post_stars, paid_suggested_post_ton, fact_check, report_delivery_until_date, paid_message_stars, suggested_post, schedule_repeat_period, and summary_from_language.
    • Updated the _parse method to correctly handle and populate these new attributes from raw Telegram API data.
  • pyrogram/types/object.py
    • Set __hash__ = None to explicitly make instances of Object unhashable.
  • pyrogram/types/payments/gift.py
    • Expanded the Gift class with numerous new attributes related to gift properties, auction details, and ownership information.
    • Updated the _parse method to handle both raw.types.StarGift and raw.types.StarGiftUnique for comprehensive gift parsing.
  • pyrogram/types/stories/init.py
    • Added an explicit __all__ list for exports.
  • pyrogram/types/user_and_chats/chat.py
    • Added new attributes to the Chat class: is_stories_hidden, is_stories_hidden_min, is_stories_unavailable, is_signature_profiles, is_autotranslation, is_broadcast_messages_allowed, is_monoforum, is_forum_tabs, bot_verification_icon, send_paid_messages_stars, and linked_monoforum_id.
    • Updated the _parse_channel_chat method to correctly handle and populate these new attributes.
  • pyrogram/types/user_and_chats/chat_privileges.py
    • Added can_manage_direct_messages attribute to the ChatPrivileges class.
    • Updated the _parse method to correctly handle this new attribute.
  • pyrogram/types/user_and_chats/user.py
    • Added new attributes to the User class: is_bot_can_edit, is_close_friend, is_stories_hidden, is_stories_unavailable, is_bot_has_main_app, is_bot_forum_view, is_bot_forum_can_manage_topics, bot_verification_icon, and send_paid_messages_stars.
    • Updated the _parse method to correctly handle and populate these new attributes.
  • pyrogram/utils.py
    • Added a new async function get_input_quick_reply_shortcut to create raw.base.InputQuickReplyShortcut objects from string or integer inputs.
  • uv.lock
    • Updated the coverage package version from 7.13.3 to 7.13.4.
    • Added anyio as a dependency for electrogram.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/format_and_test.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues, and left some high level feedback:

  • In Gift._parse you now return None for unknown star_gift types while the return type is annotated as Gift; consider either widening the return annotation to Gift | None or raising explicitly so callers don't silently get None where a Gift is expected.
  • utils.get_input_quick_reply_shortcut is declared async but does not perform any asynchronous work; making it a regular function would simplify the API and avoid unnecessary await usage at all call sites.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `Gift._parse` you now return `None` for unknown `star_gift` types while the return type is annotated as `Gift`; consider either widening the return annotation to `Gift | None` or raising explicitly so callers don't silently get `None` where a `Gift` is expected.
- `utils.get_input_quick_reply_shortcut` is declared `async` but does not perform any asynchronous work; making it a regular function would simplify the API and avoid unnecessary `await` usage at all call sites.

## Individual Comments

### Comment 1
<location> `pyrogram/types/business/stars_amount.py:29-30` </location>
<code_context>
+        self.amount = amount
+        self.nanos = nanos
+
+    async def write(self) -> raw.base.StarsAmount:
+        if self.nanos:
+            return raw.types.StarsAmount(amount=self.amount, nanos=self.nanos)
+        return raw.types.StarsTonAmount(amount=self.amount)
</code_context>

<issue_to_address>
**issue (bug_risk):** Treat `nanos=0` as a valid value rather than falling back to `StarsTonAmount`.

`if self.nanos:` treats `0` as falsy, so `StarsTonAmount` will be used whenever `nanos` is `0`, which is wrong if `0` is a valid value. Use an explicit `is not None` check instead:

```python
aasync def write(self) -> raw.base.StarsAmount:
    if self.nanos is not None:
        return raw.types.StarsAmount(amount=self.amount, nanos=self.nanos)
    return raw.types.StarsTonAmount(amount=self.amount)
```
</issue_to_address>

### Comment 2
<location> `pyrogram/methods/messages/edit_message_media.py:94-95` </location>
<code_context>
             ).values()

         if isinstance(media, types.InputMediaPhoto):
-            if isinstance(media.media, io.BytesIO) or Path(media.media).is_file():
+            if isinstance(media.media, io.BytesIO) or (
+                isinstance(media.media, str)
+                and await AsyncPath(media.media).is_file()
</code_context>

<issue_to_address>
**issue (bug_risk):** The new `str` type guard accidentally drops support for `Path` objects as media inputs.

This used to work because `Path(media.media).is_file()` accepted `pathlib.Path` directly. With the new `isinstance(media.media, str)` guard, `Path` values will fall through to the remote/ID handling path instead of being treated as files. To keep the guard while preserving behavior, consider checking for `os.PathLike` (or explicitly including `Path`) before calling `AsyncPath`:

```python
from os import PathLike

is_bytes_io = isinstance(media.media, io.BytesIO)
is_path_like = isinstance(media.media, (str, PathLike))

if is_bytes_io or (is_path_like and await AsyncPath(media.media).is_file()):
    ...
```
</issue_to_address>

### Comment 3
<location> `pyrogram/methods/messages/edit_inline_media.py:67-68` </location>
<code_context>
         parse_mode = media.parse_mode

         is_bytes_io = isinstance(media.media, io.BytesIO)
-        is_uploaded_file = is_bytes_io or Path(media.media).is_file()
+        is_uploaded_file = is_bytes_io or (
+            isinstance(media.media, str) and await AsyncPath(media.media).is_file()
+        )
</code_context>

<issue_to_address>
**issue (bug_risk):** Same `str`-only file check here will ignore `Path` media inputs.

By limiting the check to `isinstance(media.media, str)`, `pathlib.Path` inputs will no longer be treated as local files and will instead be sent as non-uploaded media. To preserve backwards compatibility and keep this consistent with `edit_message_media`, consider checking for `str | os.PathLike` (or an equivalent approach) here as well.
</issue_to_address>

### Comment 4
<location> `compiler/api/scrape_docs.py:32` </location>
<code_context>
+sem = asyncio.Semaphore(MAX_TASKS)
+
+
+client = httpx.AsyncClient()
+
+
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Creating the async HTTP client at import time can lead to lifecycle issues.

Creating `httpx.AsyncClient()` at import time means any import of this module allocates a client and connection pool, even if `main()` is never called, and makes cleanup harder to guarantee if errors occur before `client.aclose()`.

Prefer constructing and closing the client within `main()` (or an async context manager), for example:

```python
async def main():
    async with httpx.AsyncClient() as client:
        ...  # pass client down or accept it as a parameter
```

This keeps side effects out of imports and ties the client lifetime to `main()` execution.

Suggested implementation:

```python
import asyncio
import json
import re
from pathlib import Path

import httpx
from lxml import html

BASE_URL = "https://corefork.telegram.org/"

MAX_TASKS = 10

sem = asyncio.Semaphore(MAX_TASKS)

ROOT_DIR = Path.cwd().absolute()

```

To fully implement the lifecycle-safe client handling you described, you will also need to:

1. Locate the `async def main(...):` (or equivalent entrypoint) in `compiler/api/scrape_docs.py` and change it to create the client in a context manager, e.g.:

```python
async def main(...):
    async with httpx.AsyncClient() as client:
        await run_scrape(client, ...)
```

2. Update functions that currently rely on a global `client` (e.g. ones doing `await client.get(...)`) to instead accept `client: httpx.AsyncClient` as a parameter, and pass the `client` object through from `main()`.

3. Remove any remaining references to a global `client` variable in this module to ensure there is no accidental use of a stale or unclosed client.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +29 to +30
async def write(self) -> raw.base.StarsAmount:
if self.nanos:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Treat nanos=0 as a valid value rather than falling back to StarsTonAmount.

if self.nanos: treats 0 as falsy, so StarsTonAmount will be used whenever nanos is 0, which is wrong if 0 is a valid value. Use an explicit is not None check instead:

aasync def write(self) -> raw.base.StarsAmount:
    if self.nanos is not None:
        return raw.types.StarsAmount(amount=self.amount, nanos=self.nanos)
    return raw.types.StarsTonAmount(amount=self.amount)

Comment on lines 94 to +95
if isinstance(media, types.InputMediaPhoto):
if isinstance(media.media, io.BytesIO) or Path(media.media).is_file():
if isinstance(media.media, io.BytesIO) or (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The new str type guard accidentally drops support for Path objects as media inputs.

This used to work because Path(media.media).is_file() accepted pathlib.Path directly. With the new isinstance(media.media, str) guard, Path values will fall through to the remote/ID handling path instead of being treated as files. To keep the guard while preserving behavior, consider checking for os.PathLike (or explicitly including Path) before calling AsyncPath:

from os import PathLike

is_bytes_io = isinstance(media.media, io.BytesIO)
is_path_like = isinstance(media.media, (str, PathLike))

if is_bytes_io or (is_path_like and await AsyncPath(media.media).is_file()):
    ...

Comment on lines 67 to +68
is_bytes_io = isinstance(media.media, io.BytesIO)
is_uploaded_file = is_bytes_io or Path(media.media).is_file()
is_uploaded_file = is_bytes_io or (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Same str-only file check here will ignore Path media inputs.

By limiting the check to isinstance(media.media, str), pathlib.Path inputs will no longer be treated as local files and will instead be sent as non-uploaded media. To preserve backwards compatibility and keep this consistent with edit_message_media, consider checking for str | os.PathLike (or an equivalent approach) here as well.

sem = asyncio.Semaphore(MAX_TASKS)


client = httpx.AsyncClient()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Creating the async HTTP client at import time can lead to lifecycle issues.

Creating httpx.AsyncClient() at import time means any import of this module allocates a client and connection pool, even if main() is never called, and makes cleanup harder to guarantee if errors occur before client.aclose().

Prefer constructing and closing the client within main() (or an async context manager), for example:

async def main():
    async with httpx.AsyncClient() as client:
        ...  # pass client down or accept it as a parameter

This keeps side effects out of imports and ties the client lifetime to main() execution.

Suggested implementation:

import asyncio
import json
import re
from pathlib import Path

import httpx
from lxml import html

BASE_URL = "https://corefork.telegram.org/"

MAX_TASKS = 10

sem = asyncio.Semaphore(MAX_TASKS)

ROOT_DIR = Path.cwd().absolute()

To fully implement the lifecycle-safe client handling you described, you will also need to:

  1. Locate the async def main(...): (or equivalent entrypoint) in compiler/api/scrape_docs.py and change it to create the client in a context manager, e.g.:
async def main(...):
    async with httpx.AsyncClient() as client:
        await run_scrape(client, ...)
  1. Update functions that currently rely on a global client (e.g. ones doing await client.get(...)) to instead accept client: httpx.AsyncClient as a parameter, and pass the client object through from main().

  2. Remove any remaining references to a global client variable in this module to ensure there is no accidental use of a stale or unclosed client.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is a substantial pull request that introduces support for many new Telegram API features and significantly improves the codebase. The migration from synchronous pathlib to asynchronous anyio.AsyncPath for file operations is a great enhancement for an async library, preventing I/O blocking. The addition of a new script to scrape API documentation is a valuable tooling improvement. Furthermore, making core objects unhashable and adding explicit __all__ exports hardens the library and provides a more stable public API. My review includes a few suggestions for improving robustness and code clarity in the new scraping script and other areas.

Comment thread compiler/api/scrape_docs.py Outdated
Comment thread compiler/api/scrape_docs.py Outdated
Comment on lines +68 to +70
is_uploaded_file = is_bytes_io or (
isinstance(media.media, str) and await AsyncPath(media.media).is_file()
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This condition for checking if the media is an uploaded file is a bit complex and is repeated in other methods like edit_message_media. To improve code clarity and reduce duplication (Don't Repeat Yourself), consider extracting this logic into a private async helper function.

For example:

async def _is_uploaded_media(self, media: Any) -> bool:
    return isinstance(media, io.BytesIO) or (
        isinstance(media, str) and await AsyncPath(media).is_file()
    )

You could then simplify this line to is_uploaded_file = await self._is_uploaded_media(media.media).

Comment on lines 227 to +306
async def _parse(
client,
star_gift: raw.types.StarGift,
star_gift: raw.base.StarGift,
) -> Gift:
doc = star_gift.sticker
attributes = {type(i): i for i in doc.attributes}

return Gift(
id=star_gift.id,
sticker=await types.Sticker._parse(client, doc, attributes),
star_count=star_gift.stars,
default_sell_star_count=star_gift.convert_stars,
remaining_count=getattr(star_gift, "availability_remains", None),
total_count=getattr(star_gift, "availability_total", None),
first_send_date=utils.timestamp_to_datetime(
getattr(star_gift, "first_sale_date", None),
),
last_send_date=utils.timestamp_to_datetime(
getattr(star_gift, "last_sale_date", None),
),
is_limited=getattr(star_gift, "limited", None),
is_sold_out=getattr(star_gift, "sold_out", None),
client=client,
)
if isinstance(star_gift, raw.types.StarGift):
doc = star_gift.sticker
attributes = {type(i): i for i in doc.attributes}

return Gift(
id=star_gift.id,
sticker=await types.Sticker._parse(client, doc, attributes),
star_count=star_gift.stars,
default_sell_star_count=star_gift.convert_stars,
remaining_count=getattr(star_gift, "availability_remains", None),
total_count=getattr(star_gift, "availability_total", None),
first_send_date=utils.timestamp_to_datetime(
getattr(star_gift, "first_sale_date", None),
),
last_send_date=utils.timestamp_to_datetime(
getattr(star_gift, "last_sale_date", None),
),
is_limited=getattr(star_gift, "limited", None),
is_sold_out=getattr(star_gift, "sold_out", None),
is_birthday=getattr(star_gift, "birthday", None),
can_upgrade=getattr(star_gift, "can_upgrade", None),
require_premium=getattr(star_gift, "require_premium", None),
is_limited_per_user=getattr(star_gift, "limited_per_user", None),
is_peer_color_available=getattr(
star_gift,
"peer_color_available",
None,
),
is_auction=getattr(star_gift, "auction", None),
availability_resale=getattr(star_gift, "availability_resale", None),
upgrade_stars=getattr(star_gift, "upgrade_stars", None),
resell_min_stars=getattr(star_gift, "resell_min_stars", None),
title=getattr(star_gift, "title", None),
released_by=utils.get_raw_peer_id(star_gift.released_by)
if getattr(star_gift, "released_by", None)
else None,
per_user_total=getattr(star_gift, "per_user_total", None),
per_user_remains=getattr(star_gift, "per_user_remains", None),
locked_until_date=utils.timestamp_to_datetime(
getattr(star_gift, "locked_until_date", None),
),
auction_slug=getattr(star_gift, "auction_slug", None),
gifts_per_round=getattr(star_gift, "gifts_per_round", None),
auction_start_date=utils.timestamp_to_datetime(
getattr(star_gift, "auction_start_date", None),
),
upgrade_variants=getattr(star_gift, "upgrade_variants", None),
is_unique=False,
client=client,
)
if isinstance(star_gift, raw.types.StarGiftUnique):
return Gift(
id=star_gift.id,
gift_id=star_gift.gift_id,
title=star_gift.title,
slug=star_gift.slug,
num=star_gift.num,
owner_id=utils.get_raw_peer_id(star_gift.owner_id)
if star_gift.owner_id
else None,
owner_name=star_gift.owner_name,
owner_address=star_gift.owner_address,
total_count=star_gift.availability_total,
gift_address=star_gift.gift_address,
released_by=utils.get_raw_peer_id(star_gift.released_by)
if star_gift.released_by
else None,
value_amount=star_gift.value_amount,
value_currency=star_gift.value_currency,
value_usd_amount=star_gift.value_usd_amount,
require_premium=star_gift.require_premium,
craft_chance_permille=star_gift.craft_chance_permille,
is_unique=True,
client=client,
)
return None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This _parse method has become quite complex as it handles two different raw types (StarGift and StarGiftUnique). To improve readability and adhere to the Single Responsibility Principle, consider refactoring it. You could create two separate private helper methods, one for raw.types.StarGift and another for raw.types.StarGiftUnique. The main _parse method would then simply act as a dispatcher based on the input type.

Mehedi Hasan Shojib and others added 3 commits February 10, 2026 11:45
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: 5hojib <[email protected]>
@5hojib
5hojib merged commit 9f50ea3 into dev Feb 10, 2026
4 checks passed
@5hojib
5hojib deleted the test branch February 10, 2026 06:09
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.

1 participant