diff --git a/.github/workflows/check_api_updates.yml b/.github/workflows/check_api_updates.yml new file mode 100644 index 000000000..ab494b61f --- /dev/null +++ b/.github/workflows/check_api_updates.yml @@ -0,0 +1,164 @@ +name: Check MTProto API Schema Updates + +on: + schedule: + - cron: '0 */12 * * *' + workflow_dispatch: + inputs: + force_update: + description: 'Force update even if no changes detected' + required: false + default: false + type: boolean + +jobs: + check-schema-updates: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + + - name: Install dependencies + run: | + pip install requests + + - name: Check for API schema updates + id: check-updates + run: | + FORCE_UPDATE="" + if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.force_update }}" == "true" ]]; then + FORCE_UPDATE="--force-update" + fi + + python dev_tools/check_api_schema_updates.py $FORCE_UPDATE + EXIT_CODE=$? + + if [ $EXIT_CODE -eq 0 ]; then + echo "updates_detected=true" >> $GITHUB_OUTPUT + CURRENT_LAYER=$(grep -o "// LAYER [0-9]*" compiler/api/source/main_api.tl | awk '{print $3}') + echo "layer_version=$CURRENT_LAYER" >> $GITHUB_OUTPUT + elif [ $EXIT_CODE -eq 2 ]; then + echo "updates_detected=false" >> $GITHUB_OUTPUT + else + echo "Error checking for updates" + exit 1 + fi + + - name: Set up Git config + if: steps.check-updates.outputs.updates_detected == 'true' + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Check existing PR + if: steps.check-updates.outputs.updates_detected == 'true' + id: check-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + BRANCH_NAME="update/mtproto-api-updates" + + # Check if there's an open PR for this branch + PR_NUMBER=$(gh pr list --head "$BRANCH_NAME" --state open --json number -q '.[0].number') + + if [ -n "$PR_NUMBER" ]; then + echo "existing_pr=true" >> $GITHUB_OUTPUT + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + else + echo "existing_pr=false" >> $GITHUB_OUTPUT + fi + + - name: Create API update news entry + if: steps.check-updates.outputs.updates_detected == 'true' + run: | + LAYER_VERSION="${{ steps.check-updates.outputs.layer_version }}" + TIMESTAMP=$(date +"%Y%m%d%H%M%S") + NEWS_FILE="news/${TIMESTAMP}.misc.rst" + + mkdir -p news + + echo "API Layer Update" > $NEWS_FILE + echo "================" >> $NEWS_FILE + echo "" >> $NEWS_FILE + echo "Updated MTProto API schema to Layer $LAYER_VERSION." >> $NEWS_FILE + + - name: Compile API + if: steps.check-updates.outputs.updates_detected == 'true' + run: | + pip install . + python -m compiler.api.compiler + + - name: Compile errors + if: steps.check-updates.outputs.updates_detected == 'true' + run: | + python -m compiler.errors.compiler + + - name: Create branch and commit changes + if: steps.check-updates.outputs.updates_detected == 'true' && steps.check-pr.outputs.existing_pr == 'false' + run: | + LAYER_VERSION="${{ steps.check-updates.outputs.layer_version }}" + BRANCH_NAME="update/mtproto-api-updates" + + git checkout -b $BRANCH_NAME + git add compiler/api/source/main_api.tl hydrogram/raw/ hydrogram/errors/ news/ + git commit -m "Update MTProto API schema to Layer ${LAYER_VERSION}" + git push origin $BRANCH_NAME + + - name: Update existing branch + if: steps.check-updates.outputs.updates_detected == 'true' && steps.check-pr.outputs.existing_pr == 'true' + run: | + LAYER_VERSION="${{ steps.check-updates.outputs.layer_version }}" + BRANCH_NAME="update/mtproto-api-updates" + + # Create a new branch based on the current main branch + git checkout -b $BRANCH_NAME-temp origin/main + + # Add the changes and commit + git add compiler/api/source/main_api.tl hydrogram/raw/ hydrogram/errors/ news/ + git commit -m "Update MTProto API schema to Layer ${LAYER_VERSION}" + + # Force push to update the branch + git push --force origin $BRANCH_NAME-temp:$BRANCH_NAME + + - name: Create Pull Request + if: steps.check-updates.outputs.updates_detected == 'true' && steps.check-pr.outputs.existing_pr == 'false' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + LAYER_VERSION="${{ steps.check-updates.outputs.layer_version }}" + BRANCH_NAME="update/mtproto-api-updates" + PR_TITLE="Update MTProto API schema" + PR_BODY="This PR automatically updates the MTProto API schema to Layer ${LAYER_VERSION}.\n\n- Updated main schema file\n- Recompiled raw and errors modules\n- Created news entry for changelog" + + gh pr create \ + --title "$PR_TITLE" \ + --body "$PR_BODY" \ + --base main \ + --head "$BRANCH_NAME" \ + --label "enhancement" \ + --label "automated" + + - name: Update Pull Request Title and Description + if: steps.check-updates.outputs.updates_detected == 'true' && steps.check-pr.outputs.existing_pr == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + LAYER_VERSION="${{ steps.check-updates.outputs.layer_version }}" + PR_NUMBER="${{ steps.check-pr.outputs.pr_number }}" + PR_TITLE="Update MTProto API schema" + PR_BODY="This PR automatically updates the MTProto API schema to Layer ${LAYER_VERSION}.\n\n- Updated main schema file\n- Recompiled raw and errors modules\n- Created news entry for changelog" + + gh pr edit $PR_NUMBER --title "$PR_TITLE" --body "$PR_BODY" + + PR_COMMENT="Updated the MTProto API schema to Layer ${LAYER_VERSION}.\n\n- Updated main schema file\n- Recompiled raw and errors modules\n- Added new news entry for changelog" + gh pr comment $PR_NUMBER --body "$PR_COMMENT" diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml index 3e968fdd4..ddb31cf9d 100644 --- a/.github/workflows/code-style.yml +++ b/.github/workflows/code-style.yml @@ -6,13 +6,19 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install ruff - - name: Analysing the code with pylint + uv sync --all-extras --dev + + - name: Analysing the code with Ruff run: | - ruff format --check . + uv run ruff format --check . + uv run ruff check . --ignore=E501 diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 96a04c665..f8e311356 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -10,26 +10,26 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + - name: Install uv + uses: astral-sh/setup-uv@v5 with: + enable-cache: true + cache-dependency-glob: "uv.lock" python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install tox + uv sync --all-extras --dev - name: Generate API run: | - make venv make api - name: Run tests run: | - tox + uv run pytest diff --git a/.gitignore b/.gitignore index 0c5d72972..8a3d959c1 100644 --- a/.gitignore +++ b/.gitignore @@ -98,13 +98,6 @@ target/ # Jupyter Notebook .ipynb_checkpoints -# pyenv -.python-version - -# Rye -requirements.lock -requirements-dev.lock - # celery beat schedule file celerybeat-schedule diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7317d72f4..840009f10 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,15 @@ repos: + - repo: local + hooks: + - id: news-fragment-filenames + name: NEWS fragment + language: fail + entry: NEWS fragment files must be named *.(removal|feature|bugfix|doc|misc).rst + exclude: ^news/(.gitignore|.template.rst.jinja2|.*\.(removal|feature|bugfix|doc|misc).rst) + files: ^news/ + - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: - id: check-merge-conflict - id: detect-private-key @@ -11,8 +20,8 @@ repos: - id: check-yaml - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.10 + rev: v0.12.5 hooks: + - id: ruff-check + args: [--fix, --ignore=E501, --unsafe-fixes, --exit-non-zero-on-fix] - id: ruff-format - - id: ruff - args: [--fix, --ignore=E501, --exit-non-zero-on-fix] diff --git a/.python-version b/.python-version new file mode 100644 index 000000000..24ee5b1be --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96a97e3be..7985e81fe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -75,13 +75,13 @@ git remote add upstream https://github.com/hydrogram/hydrogram 5. Install dependencies: -Hydrogram uses and recommends [Rye](https://rye-up.com/) for managing virtual environmens and dependencies. +Hydrogram uses and recommends [uv](https://docs.astral.sh/uv/) for managing virtual environmens and dependencies. ```bash -rye sync --all-features +uv sync --all-extras ``` -> We use `--all-features` to install all the optional dependencies, which are required to run the tests and build the documentation. +> We use `--all-extras` to install all the optional dependencies, which are required to run the tests and build the documentation. 6. Install pre-commit hooks: diff --git a/Makefile b/Makefile index e6e8443c9..0e932fc92 100644 --- a/Makefile +++ b/Makefile @@ -1,58 +1,89 @@ -VENV := .venv -PYTHON := $(VENV)/bin/python -HOST = $(shell ifconfig | grep "inet " | tail -1 | cut -d\ -f2) -TAG = v$(shell grep -E '__version__ = ".*"' hydrogram/__init__.py | cut -d\" -f2) +PYTHON = python +SPHINX_BUILD = sphinx-build +SPHINX_AUTOBUILD = sphinx-autobuild +TOWNCRIER = towncrier -RM := rm -rf +HYDROGRAM_DIR = hydrogram +DOCS_DIR = docs +DOCS_SOURCE = $(DOCS_DIR)/source +DOCS_BUILD = $(DOCS_DIR)/build +API_DIRS = $(HYDROGRAM_DIR)/errors/exceptions $(HYDROGRAM_DIR)/raw/all.py $(HYDROGRAM_DIR)/raw/base $(HYDROGRAM_DIR)/raw/functions $(HYDROGRAM_DIR)/raw/types +DOCS_API_DIRS = $(DOCS_SOURCE)/api/bound-methods $(DOCS_SOURCE)/api/methods $(DOCS_SOURCE)/api/types $(DOCS_SOURCE)/telegram -.PHONY: venv clean-build clean-api clean api build clean-docs docs towncrier-build towncrier-draft +.PHONY: all clean clean-api clean-docs api api-raw api-errors docs docs-compile docs-serve live-docs towncrier towncrier-draft dev-tools check-api-schema generate-docs-json compare-bot-api cherry-pick-pyro help -venv: - $(RM) $(VENV) - python3 -m venv $(VENV) - $(PYTHON) -m pip install -U pip wheel setuptools - $(PYTHON) -m pip install -U -e . - @echo "Created venv with $$($(PYTHON) --version)" +all: api docs -clean-build: - $(RM) *.egg-info build dist +clean: clean-api clean-docs + @echo "All directories cleaned successfully" + +clean-api: + @echo "Cleaning generated API files..." + @rm -rf $(API_DIRS) clean-docs: - $(RM) docs/build - $(RM) docs/source/api/bound-methods docs/source/api/methods docs/source/api/types docs/source/telegram + @echo "Cleaning generated documentation..." + @rm -rf $(DOCS_BUILD) $(DOCS_API_DIRS) -clean-api: - $(RM) hydrogram/errors/exceptions hydrogram/raw/all.py hydrogram/raw/base hydrogram/raw/functions hydrogram/raw/types +api: api-raw api-errors + @echo "API compilation finished" -clean: - make clean-build - make clean-api +api-raw: + @echo "Compiling raw API..." + @$(PYTHON) -c "from compiler.api.compiler import start; start()" -api: - cd compiler/api && ../../$(PYTHON) compiler.py - cd compiler/errors && ../../$(PYTHON) compiler.py +api-errors: + @echo "Compiling API errors..." + @$(PYTHON) -c "from compiler.errors.compiler import start; start()" -docs: - make clean-docs - cd compiler/docs && ../../$(PYTHON) compiler.py - $(VENV)/bin/sphinx-build \ - -b html "docs/source" "docs/build/html" -j auto +docs: docs-compile docs-serve -build: - make clean - $(PYTHON) setup.py sdist - $(PYTHON) setup.py bdist_wheel +docs-compile: + @echo "Compiling documentation..." + @$(PYTHON) -c "from compiler.docs.compiler import start; start()" -tag: - git tag $(TAG) - git push origin $(TAG) +docs-serve: + @echo "Building and serving documentation..." + @$(SPHINX_BUILD) -b html $(DOCS_SOURCE) $(DOCS_BUILD)/html -j auto -dtag: - git tag -d $(TAG) - git push origin -d $(TAG) +live-docs: + @echo "Starting documentation server with live reload..." + @$(SPHINX_AUTOBUILD) $(DOCS_SOURCE) $(DOCS_BUILD)/html -j auto --watch $(HYDROGRAM_DIR) -towncrier-build: - towncrier build --yes +towncrier: + @echo "Generating release notes..." + @$(TOWNCRIER) build --yes towncrier-draft: - towncrier build --draft + @echo "Generating draft release notes..." + @$(TOWNCRIER) build --draft + +check-api-schema: + @echo "Checking Telegram API schema for updates..." + @$(PYTHON) dev_tools/check_api_schema_updates.py + +generate-docs-json: + @echo "Generating API documentation JSON..." + @$(PYTHON) dev_tools/generate_docs_json.py + +compare-bot-api: + @echo "Comparing implementation against Bot API..." + @$(PYTHON) dev_tools/compare_to_bot_api.py + +cherry-pick-pyro: + @echo "Usage: make cherry-pick-pyro TYPE= ID=" + @[ "$(TYPE)" ] && [ "$(ID)" ] && $(PYTHON) dev_tools/cherry_pick_pyro.py $(TYPE) $(ID) || echo "Please provide TYPE and ID parameters" + +help: + @echo "Available targets:" + @echo " all : Compile API and documentation" + @echo " clean : Remove all generated files" + @echo " api : Compile all API components" + @echo " docs : Compile and serve documentation" + @echo " live-docs : Start documentation server with live reload" + @echo " towncrier : Generate release notes" + @echo " towncrier-draft: Generate draft release notes" + @echo " check-api-schema: Check Telegram API schema for updates" + @echo " generate-docs-json: Generate API documentation JSON" + @echo " compare-bot-api: Compare implementation against Bot API" + @echo " cherry-pick-pyro: Cherry-pick code from Pyrogram (usage: make cherry-pick-pyro TYPE= ID=)" diff --git a/NEWS.rst b/NEWS.rst index 85fcecb76..563f26ad8 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -72,7 +72,7 @@ Misc - Added the `from __future__ import annotations` statement to the codebase in order to simplify the usage of the typing module. This statement allows for the use of forward references in type hints, which can improve code readability and maintainability. `#24 `_ - Various fixes, improvements and micro-optimizations. - + 0.1.4 (2023-12-04) diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py index c68291bb9..935d48be2 100644 --- a/compiler/api/compiler.py +++ b/compiler/api/compiler.py @@ -124,7 +124,7 @@ def get_type_hint(type: str) -> str: if type in {"Object", "!X"}: return "TLObject" - if re.match("^vector", type, re.IGNORECASE): + if re.match(r"^vector", type, re.IGNORECASE): is_core = True sub_type = type.split("<")[1][:-1] @@ -135,7 +135,7 @@ def get_type_hint(type: str) -> str: ns, name = type.split(".") if "." in type else ("", type) type = '"raw.base.' + ".".join([ns, name]).strip(".") + '"' - return f'{type}{" = None" if is_flag else ""}' + return f"{type}{' = None' if is_flag else ''}" def sort_args(args: list[tuple[str, str]]): @@ -376,7 +376,7 @@ def start(format: bool = False): arg_docs = arg_docs["params"].get(arg_name, "N/A") if arg_docs else "N/A" docstring_args.append( - f'{arg_name} ({get_docstring_arg_type(arg_type)}{", *optional*" if is_optional else ""}):\n {arg_docs}\n' + f"{arg_name} ({get_docstring_arg_type(arg_type)}{', *optional*' if is_optional else ''}):\n {arg_docs}\n" ) if c.section == "types": @@ -458,10 +458,10 @@ def start(format: bool = False): write_types += "\n " write_types += f"if self.{arg_name} is not None:\n " - write_types += f'b.write(Vector(self.{arg_name}{f", {sub_type.title()}" if sub_type in CORE_TYPES else ""}))\n ' + write_types += f"b.write(Vector(self.{arg_name}{f', {sub_type.title()}' if sub_type in CORE_TYPES else ''}))\n " read_types += "\n " - read_types += f'{arg_name} = TLObject.read(b{f", {sub_type.title()}" if sub_type in CORE_TYPES else ""}) if flags{number} & (1 << {index}) else []\n ' + read_types += f"{arg_name} = TLObject.read(b{f', {sub_type.title()}' if sub_type in CORE_TYPES else ''}) if flags{number} & (1 << {index}) else []\n " else: write_types += "\n " write_types += f"if self.{arg_name} is not None:\n " @@ -479,10 +479,10 @@ def start(format: bool = False): elif "vector" in arg_type.lower(): sub_type = arg_type.split("<")[1][:-1] - write_types += f'b.write(Vector(self.{arg_name}{f", {sub_type.title()}" if sub_type in CORE_TYPES else ""}))\n ' + write_types += f"b.write(Vector(self.{arg_name}{f', {sub_type.title()}' if sub_type in CORE_TYPES else ''}))\n " read_types += "\n " - read_types += f'{arg_name} = TLObject.read(b{f", {sub_type.title()}" if sub_type in CORE_TYPES else ""})\n ' + read_types += f"{arg_name} = TLObject.read(b{f', {sub_type.title()}' if sub_type in CORE_TYPES else ''})\n " else: write_types += f"b.write(self.{arg_name}.write())\n " diff --git a/compiler/api/docs.json b/compiler/api/docs.json index bae465027..17766e448 100644 --- a/compiler/api/docs.json +++ b/compiler/api/docs.json @@ -6,12 +6,6 @@ "days": "This account will self-destruct in the specified number of days" } }, - "AppWebViewResultUrl": { - "desc": "Contains the link that must be used to open a direct link Mini App.", - "params": { - "url": "The URL to open" - } - }, "AttachMenuBot": { "desc": "Represents a bot mini app that can be launched from the attachment/side menu \u00bb", "params": { @@ -48,7 +42,7 @@ "desc": "Represents a list of bot mini apps that can be launched from the attachment menu \u00bb", "params": { "bots": "List of bot mini apps that can be launched from the attachment menu \u00bb", - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "users": "Info about related users/bots" } }, @@ -141,15 +135,15 @@ } }, "AvailableEffect": { - "desc": "{schema}", + "desc": "Represents a message effect \u00bb.", "params": { - "effect_animation_id": "", - "effect_sticker_id": "", - "emoticon": "", + "effect_animation_id": "If set, contains the actual animated effect (TGS format \u00bb). If not set, the animated effect must be set equal to the premium animated sticker effect associated to the animated sticker specified in effect_sticker_id (always different from the preview animation, fetched thanks to the videoSize of type f as specified here \u00bb).", + "effect_sticker_id": "Contains the preview animation (TGS format \u00bb), used for the effect selection menu.", + "emoticon": "Emoji corresponding to the effect, to be used as icon for the effect if static_icon_id is not set.", "flags": "Flags, see TL conditional fields", - "id": "", - "premium_required": "", - "static_icon_id": "" + "id": "Unique effect ID.", + "premium_required": "Whether a Premium subscription is required to use this effect.", + "static_icon_id": "ID of the document containing the static icon (WEBP) of the effect." } }, "AvailableReaction": { @@ -197,12 +191,12 @@ "params": {} }, "Birthday": { - "desc": "{schema}", + "desc": "Birthday information for a user.", "params": { - "day": "", + "day": "Birth day", "flags": "Flags, see TL conditional fields", - "month": "", - "year": "" + "month": "Birth month", + "year": "(Optional) birth year." } }, "Boost": { @@ -211,11 +205,12 @@ "date": "When was the boost applied", "expires": "When does the boost expire", "flags": "Flags, see TL conditional fields", - "gift": "Whether this boost was applied because the channel directly gifted a subscription to the user.", - "giveaway": "Whether this boost was applied because the user was chosen in a giveaway started by the channel.", + "gift": "Whether this boost was applied because the channel/supergroup directly gifted a subscription to the user.", + "giveaway": "Whether this boost was applied because the user was chosen in a giveaway started by the channel/supergroup.", "giveaway_msg_id": "The message ID of the giveaway", "id": "Unique ID for this set of boosts.", "multiplier": "If set, this boost counts as multiplier boosts, otherwise it counts as a single boost.", + "stars": "Number of Telegram Stars distributed among the winners of the giveaway.", "unclaimed": "If set, the user hasn't yet invoked payments.applyGiftCode to claim a subscription gifted directly or in a giveaway by the channel.", "used_gift_slug": "The created Telegram Premium gift code, only set if either gift or giveaway are set AND it is either a gift code for the currently logged in user or if it was already claimed.", "user_id": "ID of the user that applied the boost." @@ -239,16 +234,27 @@ "desc": "Bot app info hasn't changed.", "params": {} }, + "BotAppSettings": { + "desc": "Mini app \u00bb settings", + "params": { + "background_color": "Default light mode background color", + "background_dark_color": "Default dark mode background color", + "flags": "Flags, see TL conditional fields", + "header_color": "Default light mode header color", + "header_dark_color": "Default dark mode header color", + "placeholder_path": "SVG placeholder logo, compressed using the same format used for vector thumbnails \u00bb." + } + }, "BotBusinessConnection": { - "desc": "{schema}", + "desc": "Contains info about a bot business connection.", "params": { - "can_reply": "", - "connection_id": "", - "date": "", - "dc_id": "", - "disabled": "", + "can_reply": "Whether the bot can reply on behalf of the user to messages it receives through the business connection", + "connection_id": "Business connection ID, used to identify messages coming from the connection and to reply to them as specified here \u00bb.", + "date": "When was the connection created.", + "dc_id": "ID of the datacenter where to send queries wrapped in a invokeWithBusinessConnection as specified here \u00bb.", + "disabled": "Whether this business connection is currently disabled", "flags": "Flags, see TL conditional fields", - "user_id": "" + "user_id": "ID of the user that the bot is connected to via this connection." } }, "BotCommand": { @@ -296,12 +302,15 @@ "BotInfo": { "desc": "Info about bots (available bot commands, etc)", "params": { + "app_settings": "Mini app \u00bb settings", "commands": "Bot commands that can be used in the chat", "description": "Description of the bot", "description_document": "Description animation in MPEG4 format", "description_photo": "Description photo", "flags": "Flags, see TL conditional fields", + "has_preview_medias": "If set, the bot has some preview medias for the configured Main Mini App, see here \u00bb for more info on Main Mini App preview medias.", "menu_button": "Indicates the action to execute when pressing the in-UI menu button for bots", + "privacy_policy_url": "The HTTP link to the privacy policy of the bot. If not set, then the /privacy command must be used, if supported by the bot (i.e. if it's present in the commands vector). If it isn't supported, then https://telegram.org/privacy-tpa must be opened, instead.", "user_id": "ID of the bot" } }, @@ -353,7 +362,7 @@ "BotInlineMessageMediaInvoice": { "desc": "Send an invoice", "params": { - "currency": "Three-letter ISO 4217 currency code", + "currency": "Three-letter ISO 4217 currency code, or XTR for Telegram Stars.", "description": "Product description, 1-255 characters", "flags": "Flags, see TL conditional fields", "photo": "Product photo", @@ -432,143 +441,164 @@ "desc": "Placeholder bot menu button never returned to users: see the docs for more info.", "params": {} }, + "BotPreviewMedia": { + "desc": "Represents a Main Mini App preview media, see here \u00bb for more info.", + "params": { + "date": "When was this media last updated.", + "media": "The actual photo/video." + } + }, + "BotVerification": { + "desc": "", + "params": {} + }, + "BotVerifierSettings": { + "desc": "", + "params": {} + }, "BroadcastRevenueBalances": { - "desc": "{schema}", + "desc": "Describes channel ad revenue balances \u00bb.", "params": { - "available_balance": "", - "current_balance": "", - "overall_revenue": "" + "available_balance": "Amount of withdrawable cryptocurrency, out of the currently available balance (available_balance <= current_balance).", + "current_balance": "Amount of not-yet-withdrawn cryptocurrency.", + "flags": "Flags, see TL conditional fields", + "overall_revenue": "Total amount of earned cryptocurrency.", + "withdrawal_enabled": "If set, the available balance can be withdrawn \u00bb." } }, "BroadcastRevenueTransactionProceeds": { - "desc": "{schema}", + "desc": "Describes earnings from sponsored messages in a channel in some time frame, see here \u00bb for more info.", "params": { - "amount": "", - "from_date": "", - "to_date": "" + "amount": "Amount in the smallest unit of the cryptocurrency.", + "from_date": "Start unixtime for the timeframe.", + "to_date": "End unixtime for the timeframe." } }, "BroadcastRevenueTransactionRefund": { - "desc": "{schema}", + "desc": "Describes a refund for failed withdrawal of ad earnings \u00bb", "params": { - "amount": "", - "date": "", - "provider": "" + "amount": "Amount refunded.", + "date": "Date of refund.", + "provider": "Payment provider name." } }, "BroadcastRevenueTransactionWithdrawal": { - "desc": "{schema}", + "desc": "Describes a withdrawal of ad earnings \u00bb", "params": { - "amount": "", - "date": "", - "failed": "", + "amount": "Amount withdrawn", + "date": "Withdrawal date", + "failed": "Whether the withdrawal has failed", "flags": "Flags, see TL conditional fields", - "pending": "", - "provider": "", - "transaction_date": "", - "transaction_url": "" + "pending": "Whether the withdrawal is currently pending", + "provider": "Payment provider name", + "transaction_date": "If neither pending nor failed are set, the transaction was completed successfully, and this field will contain the point in time (Unix timestamp) when the withdrawal was completed successfully.", + "transaction_url": "If neither pending nor failed are set, the transaction was completed successfully, and this field will contain a URL where the withdrawal transaction can be viewed." } }, "BusinessAwayMessage": { - "desc": "{schema}", + "desc": "Describes a Telegram Business away message, automatically sent to users writing to us when we're offline, during closing hours, while we're on vacation, or in some other custom time period when we cannot immediately answer to the user.", "params": { "flags": "Flags, see TL conditional fields", - "offline_only": "", - "recipients": "", - "schedule": "", - "shortcut_id": "" + "offline_only": "If set, the messages will not be sent if the account was online in the last 10 minutes.", + "recipients": "Allowed recipients for the away messages.", + "schedule": "Specifies when should the away messages be sent.", + "shortcut_id": "ID of a quick reply shorcut, containing the away messages to send, see here \u00bb for more info." } }, "BusinessAwayMessageScheduleAlways": { - "desc": "{schema}", + "desc": "Always send Telegram Business away messages to users writing to us in private.", "params": {} }, "BusinessAwayMessageScheduleCustom": { - "desc": "{schema}", + "desc": "Send Telegram Business away messages to users writing to us in private in the specified time span.", "params": { - "end_date": "", - "start_date": "" + "end_date": "End date (UNIX timestamp).", + "start_date": "Start date (UNIX timestamp)." } }, "BusinessAwayMessageScheduleOutsideWorkHours": { - "desc": "{schema}", + "desc": "Send Telegram Business away messages to users writing to us in private outside of the configured Telegram Business working hours.", "params": {} }, "BusinessBotRecipients": { - "desc": "{schema}", + "desc": "Specifies the private chats that a connected business bot \u00bb may receive messages and interact with.", "params": { - "contacts": "", - "exclude_selected": "", - "exclude_users": "", - "existing_chats": "", + "contacts": "Selects all private chats with contacts.", + "exclude_selected": "If set, then all private chats except the ones selected by existing_chats, new_chats, contacts, non_contacts and users are chosen. Note that if this flag is set, any values passed in exclude_users will be merged and moved into users by the server, thus exclude_users will always be empty.", + "exclude_users": "Identifiers of private chats that are always excluded.", + "existing_chats": "Selects all existing private chats.", "flags": "Flags, see TL conditional fields", - "new_chats": "", - "non_contacts": "", - "users": "" + "new_chats": "Selects all new private chats.", + "non_contacts": "Selects all private chats with non-contacts.", + "users": "Explicitly selected private chats." } }, + "BusinessBotRights": { + "desc": "", + "params": {} + }, "BusinessChatLink": { - "desc": "{schema}", + "desc": "Contains info about a business chat deep link \u00bb created by the current account.", "params": { "entities": "Message entities for styled text", "flags": "Flags, see TL conditional fields", - "link": "", - "message": "", - "title": "", - "views": "" + "link": "Business chat deep link.", + "message": "Message to pre-fill in the message input field.", + "title": "Human-readable name of the link, to simplify management in the UI (only visible to the creator of the link).", + "views": "Number of times the link was resolved (clicked/scanned/etc...)." } }, "BusinessGreetingMessage": { - "desc": "{schema}", + "desc": "Describes a Telegram Business greeting, automatically sent to new users writing to us in private for the first time, or after a certain inactivity period.", "params": { - "no_activity_days": "", - "recipients": "", - "shortcut_id": "" + "no_activity_days": "The number of days after which a private chat will be considered as inactive; currently, must be one of 7, 14, 21, or 28.", + "recipients": "Allowed recipients for the greeting messages.", + "shortcut_id": "ID of a quick reply shorcut, containing the greeting messages to send, see here \u00bb for more info." } }, "BusinessIntro": { - "desc": "{schema}", + "desc": "Telegram Business introduction \u00bb.", "params": { - "description": "", + "description": "Profile introduction (max intro_description_length_limit \u00bb UTF-8 characters).", "flags": "Flags, see TL conditional fields", - "sticker": "", - "title": "" + "sticker": "Optional introduction sticker.", + "title": "Title of the introduction message (max intro_title_length_limit \u00bb UTF-8 characters)." } }, "BusinessLocation": { - "desc": "{schema}", + "desc": "Represents the location of a Telegram Business \u00bb.", "params": { - "address": "", + "address": "Textual description of the address (mandatory).", "flags": "Flags, see TL conditional fields", - "geo_point": "" + "geo_point": "Geographical coordinates (optional)." } }, "BusinessRecipients": { - "desc": "{schema}", + "desc": "Specifies the chats that can receive Telegram Business away \u00bb and greeting \u00bb messages.", "params": { - "contacts": "", - "exclude_selected": "", - "existing_chats": "", + "contacts": "All private chats with contacts.", + "exclude_selected": "If set, inverts the selection.", + "existing_chats": "All existing private chats.", "flags": "Flags, see TL conditional fields", - "new_chats": "", - "non_contacts": "", - "users": "" + "new_chats": "All new private chats.", + "non_contacts": "All private chats with non-contacts.", + "users": "Only private chats with the specified users." } }, "BusinessWeeklyOpen": { - "desc": "{schema}", + "desc": "A time interval, indicating the opening hours of a business.", "params": { - "end_minute": "", - "start_minute": "" + "end_minute": "End minute in minutes of the week, 1 to 8*24*60 inclusively (8 and not 7 because this allows to specify intervals that, for example, start on Sunday 21:00 and end on Monday 04:00 (6*24*60+21*60 to 7*24*60+4*60) without passing an invalid end_minute < start_minute). See here \u00bb for more info.", + "start_minute": "Start minute in minutes of the week, 0 to 7*24*60 inclusively." } }, "BusinessWorkHours": { - "desc": "{schema}", + "desc": "Specifies a set of Telegram Business opening hours.", "params": { "flags": "Flags, see TL conditional fields", - "open_now": "", - "timezone_id": "", - "weekly_open": "" + "open_now": "Ignored if set while invoking account.updateBusinessWorkHours, only returned by the server in userFull.business_work_hours, indicating whether the business is currently open according to the current time and the values in weekly_open and timezone.", + "timezone_id": "An ID of one of the timezones returned by help.getTimezonesList. The timezone ID is contained timezone.id, a human-readable, localized name of the timezone is available in timezone.name and the timezone.utc_offset field contains the UTC offset in seconds, which may be displayed in hh:mm format by the client together with the human-readable name (i.e. $name UTC -01:00).", + "weekly_open": "A list of time intervals (max 28) represented by businessWeeklyOpen \u00bb, indicating the opening hours of their business." } }, "CdnConfig": { @@ -587,7 +617,7 @@ "Channel": { "desc": "Channel/supergroup info", "params": { - "access_hash": "Access hash", + "access_hash": "Access hash, see here \u00bb for more info", "admin_rights": "Admin rights of the user in this channel (see rights)", "banned_rights": "Banned rights of the user in this channel (see rights)", "broadcast": "Is this a channel?", @@ -598,35 +628,37 @@ "date": "Date when the user joined the supergroup/channel, or if the user isn't a member, its creation date", "default_banned_rights": "Default chat rights (see rights)", "emoji_status": "Emoji status", - "fake": "If set, this supergroup/channel was reported by many users as a fake or scam: be careful when interacting with it.", + "fake": "If set, this supergroup/channel was reported by many users as a fake or scam: be careful when interacting with it. Changes to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", "flags": "Flags, see TL conditional fields", "flags2": "Flags, see TL conditional fields", - "forum": "Whether this supergroup is a forum", - "gigagroup": "Whether this supergroup is a gigagroup", + "forum": "Whether this supergroup is a forum. Changes to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", + "gigagroup": "Whether this supergroup is a gigagroupChanges to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", "has_geo": "Whether this chanel has a geoposition", - "has_link": "Whether this channel has a private join link", - "id": "ID of the channel", - "join_request": "Whether a user's join request will have to be approved by administrators, toggle using channels.toggleJoinToSend", - "join_to_send": "Whether a user needs to join the supergroup before they can send messages: can be false only for discussion groups \u00bb, toggle using channels.toggleJoinToSend", + "has_link": "Whether this channel has a linked discussion group \u00bb (or this supergroup is a channel's discussion group). The actual ID of the linked channel/supergroup is contained in channelFull.linked_chat_id. Changes to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", + "id": "ID of the channel, see here \u00bb for more info", + "join_request": "Whether a user's join request will have to be approved by administrators, toggle using channels.toggleJoinToSendChanges to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", + "join_to_send": "Whether a user needs to join the supergroup before they can send messages: can be false only for discussion groups \u00bb, toggle using channels.toggleJoinToSendChanges to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", "left": "Whether the current user has left or is not a member of this channel", - "level": "Boost level", - "megagroup": "Is this a supergroup?", + "level": "Boost level. Changes to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", + "megagroup": "Is this a supergroup? Changes to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", "min": "See min", "noforwards": "Whether this channel or group is protected, thus does not allow forwarding messages from it", "participants_count": "Participant count", "photo": "Profile photo", "profile_color": "The channel's profile color.", - "restricted": "Whether viewing/writing in this channel for a reason (see restriction_reason", - "restriction_reason": "Contains the reason why access to this channel must be restricted.", - "scam": "This channel/supergroup is probably a scam", + "restricted": "Whether viewing/writing in this channel for a reason (see restriction_reason)", + "restriction_reason": "Contains the reason why access to this channel must be restricted. Changes to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", + "scam": "This channel/supergroup is probably a scam Changes to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", + "signature_profiles": "If set, messages sent by admins to this channel will link to the admin's profile (just like with groups).", "signatures": "Whether signatures are enabled (channels)", - "slowmode_enabled": "Whether slow mode is enabled for groups to prevent flood in chat", + "slowmode_enabled": "Whether slow mode is enabled for groups to prevent flood in chat. Changes to this flag should invalidate the local channelFull cache for this channel/supergroup ID, see here \u00bb for more info.", "stories_hidden": "Whether we have hidden all stories posted by this channel \u00bb.", "stories_hidden_min": "If set, indicates that the stories_hidden flag was not populated, and its value must cannot be relied on; use the previously cached value, or re-fetch the constructor using channels.getChannels to obtain the latest value of the stories_hidden flag.", "stories_max_id": "ID of the maximum read story.", "stories_unavailable": "No stories from the channel are visible.", + "subscription_until_date": "Expiration date of the Telegram Star subscription \u00bb the current user has bought to gain access to this channel.", "title": "Title", - "username": "Username", + "username": "Main active username.", "usernames": "Additional usernames", "verified": "Is this channel verified by telegram?" } @@ -662,10 +694,10 @@ } }, "ChannelAdminLogEventActionChangeEmojiStickerSet": { - "desc": "{schema}", + "desc": "The supergroup's custom emoji stickerset was changed.", "params": { - "new_stickerset": "", - "prev_stickerset": "" + "new_stickerset": "New value", + "prev_stickerset": "Old value" } }, "ChannelAdminLogEventActionChangeHistoryTTL": { @@ -844,6 +876,13 @@ "participant": "The participant that was muted" } }, + "ChannelAdminLogEventActionParticipantSubExtend": { + "desc": "A paid subscriber has extended their Telegram Star subscription \u00bb.", + "params": { + "new_participant": "The subscriber that extended the subscription.", + "prev_participant": "Same as new_participant." + } + }, "ChannelAdminLogEventActionParticipantToggleAdmin": { "desc": "The admin rights of a user were changed", "params": { @@ -932,6 +971,12 @@ "new_value": "New value" } }, + "ChannelAdminLogEventActionToggleSignatureProfiles": { + "desc": "Channel signature profiles were enabled/disabled.", + "params": { + "new_value": "New value" + } + }, "ChannelAdminLogEventActionToggleSignatures": { "desc": "Channel signatures were enabled/disabled", "params": { @@ -971,6 +1016,7 @@ "promote": "Admin promotion events", "send": "A message was posted in a channel", "settings": "Settings change events (invites, hidden prehistory, signatures, default banned rights, forum toggle events)", + "sub_extend": "Telegram Star subscription extension events \u00bb", "unban": "Unban events", "unkick": "Unkick events" } @@ -997,8 +1043,8 @@ "available_reactions": "Allowed message reactions \u00bb", "banned_count": "Number of users banned from the channel", "blocked": "Whether any anonymous admin of this supergroup was blocked: if set, you won't receive messages from anonymous group admins in discussion replies via @replies", - "boosts_applied": "", - "boosts_unrestrict": "", + "boosts_applied": "The number of boosts the current user has applied to the current supergroup.", + "boosts_unrestrict": "The number of boosts this supergroup requires to bypass slowmode and other restrictions, see here \u00bb for more info.", "bot_info": "Info about bots in the channel/supergroup", "call": "Livestream or group call information", "can_delete_channel": "Can we delete this channel?", @@ -1006,11 +1052,12 @@ "can_set_stickers": "Can we associate a stickerpack to the supergroup?", "can_set_username": "Can we set the channel's username?", "can_view_participants": "Can we view the participant list?", - "can_view_revenue": "", + "can_view_revenue": "If set, this user can view ad revenue statistics \u00bb for this channel.", + "can_view_stars_revenue": "If set, this user can view Telegram Star revenue statistics \u00bb for this channel.", "can_view_stats": "Can the user view channel/supergroup statistics", "chat_photo": "Channel picture", "default_send_as": "Default peer used for sending messages to this channel", - "emojiset": "", + "emojiset": "Custom emoji stickerset associated to the current supergroup, set using channels.setEmojiStickers after reaching the appropriate boost level, see here \u00bb for more info.", "exported_invite": "Invite link", "flags": "Flags, see TL conditional fields", "flags2": "Flags, see TL conditional fields", @@ -1020,23 +1067,25 @@ "hidden_prehistory": "Is the history before we joined hidden to us?", "id": "ID of the channel", "kicked_count": "Number of users kicked from the channel", - "linked_chat_id": "ID of the linked discussion chat for channels", + "linked_chat_id": "ID of the linked discussion chat for channels (and vice versa, the ID of the linked channel for discussion chats).", "location": "Location of the geogroup", "migrated_from_chat_id": "The chat ID from which this group was migrated", "migrated_from_max_id": "The message ID in the original chat at which this group was migrated", "notify_settings": "Notification settings", "online_count": "Number of users currently online", + "paid_media_allowed": "Whether the current user can send or forward paid media \u00bb to this channel.", + "paid_reactions_available": "If set, users may send paid Telegram Star reactions \u00bb to messages of this channel.", "participants_count": "Number of participants of the channel", "participants_hidden": "Whether the participant list is hidden.", "pending_suggestions": "A list of suggested actions for the supergroup admin, see here for more info \u00bb.", "pinned_msg_id": "Message ID of the last pinned message", "pts": "Latest PTS for this channel", - "reactions_limit": "", + "reactions_limit": "This flag may be used to impose a custom limit of unique reactions (i.e. a customizable version of appConfig.reactions_uniq_max).", "read_inbox_max_id": "Position up to which all incoming messages are read.", "read_outbox_max_id": "Position up to which all outgoing messages are read.", "recent_requesters": "IDs of users who requested to join recently", "requests_pending": "Pending join requests \u00bb", - "restricted_sponsored": "", + "restricted_sponsored": "Whether ads on this channel were disabled as specified here \u00bb (this flag is only visible to the owner of the channel).", "slowmode_next_send_date": "Indicates when the user will be allowed to send another message in the supergroup (unixtime)", "slowmode_seconds": "If specified, users in supergroups will only be able to send one message every slowmode_seconds seconds", "stats_dc": "If set, specifies the DC to use for fetching channel statistics", @@ -1078,6 +1127,8 @@ "desc": "Channel/supergroup participant", "params": { "date": "Date joined", + "flags": "Flags, see TL conditional fields", + "subscription_until_date": "If set, contains the expiration date of the current Telegram Star subscription period \u00bb for the specified participant.", "user_id": "Participant user ID" } }, @@ -1127,6 +1178,7 @@ "date": "When did I join the channel/supergroup", "flags": "Flags, see TL conditional fields", "inviter_id": "User that invited me to the channel/supergroup", + "subscription_until_date": "If set, contains the expiration date of the current Telegram Star subscription period \u00bb for the specified participant.", "user_id": "User ID", "via_request": "Whether I joined upon specific approval of an admin" } @@ -1176,7 +1228,7 @@ } }, "Chat": { - "desc": "Info about a group", + "desc": "Info about a group.", "params": { "admin_rights": "Admin rights of the user in the group", "call_active": "Whether a group call is currently active", @@ -1186,7 +1238,7 @@ "deactivated": "Whether the group was migrated", "default_banned_rights": "Default banned rights of all users in the group", "flags": "Flags, see TL conditional fields", - "id": "ID of the group", + "id": "ID of the group, see here \u00bb for more info", "left": "Whether the current user has left the group", "migrated_to": "Means this chat was upgraded to a supergroup", "noforwards": "Whether this group is protected, thus does not allow forwarding messages from it", @@ -1283,7 +1335,7 @@ "notify_settings": "Notification settings", "participants": "Participant list", "pinned_msg_id": "Message ID of the last pinned message", - "reactions_limit": "", + "reactions_limit": "This flag may be used to impose a custom limit of unique reactions (i.e. a customizable version of appConfig.reactions_uniq_max).", "recent_requesters": "IDs of users who requested to join recently", "requests_pending": "Pending join requests \u00bb", "theme_emoticon": "Emoji representing a specific chat theme", @@ -1296,6 +1348,7 @@ "params": { "about": "Description of the group of channel", "broadcast": "Whether this is a channel", + "can_refulfill_subscription": "If set, indicates that the user has already paid for the associated Telegram Star subscriptions \u00bb and it hasn't expired yet, so they may re-join the channel using messages.importChatInvite without repeating the payment.", "channel": "Whether this is a channel/supergroup or a normal group", "color": "Profile color palette ID", "fake": "If set, this chat was reported by many users as a fake or scam: be careful when interacting with it.", @@ -1307,6 +1360,8 @@ "public": "Whether this is a public channel/supergroup", "request_needed": "Whether the join request \u00bb must be first approved by an administrator", "scam": "This chat is probably a scam", + "subscription_form_id": "For Telegram Star subscriptions \u00bb, the ID of the payment form for the subscription.", + "subscription_pricing": "For Telegram Star subscriptions \u00bb, contains the pricing of the subscription the user must activate to join the private channel.", "title": "Chat/supergroup/channel title", "verified": "Is this chat or channel verified by Telegram?" } @@ -1330,6 +1385,8 @@ "requested": "Number of users that have already used this link to join", "revoked": "Whether this chat invite was revoked", "start_date": "When was this chat invite last modified", + "subscription_expired": "For Telegram Star subscriptions \u00bb, contains the number of chat members which have already joined the chat using the link, but have already left due to expiration of their subscription.", + "subscription_pricing": "For Telegram Star subscriptions \u00bb, contains the pricing of the subscription the user must activate to join the private channel.", "title": "Custom description for the invite link, visible only to admins", "usage": "How many users joined using this link", "usage_limit": "Maximum number of users that can join using this link" @@ -1445,7 +1502,7 @@ "flags": "Flags, see TL conditional fields", "logout_tokens": "Previously stored future auth tokens, see the documentation for more info \u00bb", "token": "Used only by official iOS apps for Firebase auth: device token for apple push.", - "unknown_number": "" + "unknown_number": "Set this flag if there is a SIM card in the current device, but it is not possible to check whether the specified phone number matches the SIM's phone number." } }, "Config": { @@ -1502,12 +1559,26 @@ } }, "ConnectedBot": { - "desc": "{schema}", + "desc": "Contains info about a connected business bot \u00bb.", + "params": { + "bot_id": "ID of the connected bot", + "can_reply": "Whether the the bot can reply to messages it receives through the connection", + "flags": "Flags, see TL conditional fields", + "recipients": "Specifies the private chats that a connected business bot \u00bb may receive messages and interact with." + } + }, + "ConnectedBotStarRef": { + "desc": "Info about an active affiliate program we have with a Mini App", "params": { - "bot_id": "", - "can_reply": "", + "bot_id": "ID of the mini app that created the affiliate program", + "commission_permille": "The number of Telegram Stars received by the affiliate for each 1000 Telegram Stars received by bot_id", + "date": "When did we affiliate with bot_id", + "duration_months": "Number of months the program will be active; if not set, there is no expiration date.", "flags": "Flags, see TL conditional fields", - "recipients": "" + "participants": "The number of users that used the affiliate program", + "revenue": "The number of Telegram Stars that were earned by the affiliate program", + "revoked": "If set, this affiliation was revoked by the affiliate using payments.editConnectedStarRefBot, or by the affiliation program owner using bots.updateStarRefProgram", + "url": "Referral link to be shared" } }, "Contact": { @@ -1518,10 +1589,10 @@ } }, "ContactBirthday": { - "desc": "{schema}", + "desc": "Birthday information of a contact.", "params": { - "birthday": "", - "contact_id": "" + "birthday": "Birthday information.", + "contact_id": "User ID." } }, "ContactStatus": { @@ -1585,7 +1656,7 @@ "params": { "bots": "Whether to include all bots in this folder", "broadcasts": "Whether to include all channels in this folder", - "color": "", + "color": "A color ID for the folder tag associated to this folder, see here \u00bb for more info.", "contacts": "Whether to include all contacts in this folder", "emoticon": "Emoji to use as icon for the folder.", "exclude_archived": "Whether to exclude archived chats from this folder", @@ -1604,7 +1675,7 @@ "DialogFilterChatlist": { "desc": "A folder imported using a chat folder deep link \u00bb.", "params": { - "color": "", + "color": "A color ID for the folder tag associated to this folder, see here \u00bb for more info.", "emoticon": "Emoji to use as icon for the folder.", "flags": "Flags, see TL conditional fields", "has_my_invites": "Whether the current user has created some chat folder deep links \u00bb to share the folder as well.", @@ -1651,6 +1722,10 @@ "folder_id": "Peer folder ID, for more info click here" } }, + "DisallowedGiftsSettings": { + "desc": "", + "params": {} + }, "Document": { "desc": "Document", "params": { @@ -1721,16 +1796,7 @@ }, "DocumentAttributeVideo": { "desc": "Defines a video", - "params": { - "duration": "Duration in seconds", - "flags": "Flags, see TL conditional fields", - "h": "Video height", - "nosound": "Whether the specified document is a video file with no audio tracks (a GIF animation (even as MPEG4), for example)", - "preload_prefix_size": "Number of bytes to preload when preloading videos (particularly video stories).", - "round_message": "Whether this is a round video", - "supports_streaming": "Whether the video supports streaming", - "w": "Video width" - } + "params": {} }, "DocumentEmpty": { "desc": "Empty constructor, document doesn't exist.", @@ -1742,6 +1808,7 @@ "desc": "Represents a message draft.", "params": { "date": "Date of last update of the draft.", + "effect": "A message effect that should be played as specified here \u00bb.", "entities": "Message entities for styled text.", "flags": "Flags, see TL conditional fields", "invert_media": "If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.", @@ -1800,18 +1867,18 @@ } }, "EmojiGroupGreeting": { - "desc": "{schema}", + "desc": "Represents an emoji category, that should be moved to the top of the list when choosing a sticker for a business introduction", "params": { - "emoticons": "", - "icon_emoji_id": "", - "title": "" + "emoticons": "A list of UTF-8 emojis, matching the category.", + "icon_emoji_id": "A single custom emoji used as preview for the category.", + "title": "Category name, i.e. \"Animals\", \"Flags\", \"Faces\" and so on..." } }, "EmojiGroupPremium": { - "desc": "{schema}", + "desc": "An emoji category, used to select all Premium-only stickers (i.e. those with a Premium effect \u00bb)/Premium-only custom emojis (i.e. those where the documentAttributeCustomEmoji.free flag is not set)", "params": { - "icon_emoji_id": "", - "title": "" + "icon_emoji_id": "A single custom emoji used as preview for the category.", + "title": "Category name, i.e. \"Animals\", \"Flags\", \"Faces\" and so on..." } }, "EmojiKeyword": { @@ -1847,7 +1914,7 @@ "desc": "Represents a list of custom emojis.", "params": { "document_id": "Custom emoji IDs", - "hash": "Hash for pagination, for more info click here" + "hash": "Hash used for caching, for more info click here" } }, "EmojiListNotModified": { @@ -1860,17 +1927,14 @@ "document_id": "Custom emoji document ID" } }, + "EmojiStatusCollectible": { + "desc": "", + "params": {} + }, "EmojiStatusEmpty": { "desc": "No emoji status is set", "params": {} }, - "EmojiStatusUntil": { - "desc": "An emoji status valid until the specified date", - "params": { - "document_id": "Custom emoji document ID", - "until": "This status is valid until this date" - } - }, "EmojiURL": { "desc": "An HTTP URL which can be used to automatically log in into translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation", "params": { @@ -1989,13 +2053,13 @@ } }, "FactCheck": { - "desc": "{schema}", + "desc": "Represents a fact-check \u00bb created by an independent fact-checker.", "params": { - "country": "", + "country": "A two-letter ISO 3166-1 alpha-2 country code of the country for which the fact-check should be shown.", "flags": "Flags, see TL conditional fields", - "hash": "Hash for pagination, for more info click here", - "need_check": "", - "text": "" + "hash": "Hash used for caching, for more info click here", + "need_check": "If set, the country/text fields will not be set, and the fact check must be fetched manually by the client (if it isn't already cached with the key specified in hash) using bundled messages.getFactCheck requests, when the message with the factcheck scrolls into view.", + "text": "The fact-check." } }, "FileHash": { @@ -2056,6 +2120,13 @@ "id": "The ID of the deleted forum topic." } }, + "FoundStory": { + "desc": "A story found using global story search \u00bb.", + "params": { + "peer": "The peer that posted the story.", + "story": "The story." + } + }, "Game": { "desc": "Indicates an already sent game", "params": { @@ -2079,6 +2150,16 @@ "long": "Longitude" } }, + "GeoPointAddress": { + "desc": "Address optionally associated to a geoPoint.", + "params": { + "city": "City", + "country_iso2": "Two-letter ISO 3166-1 alpha-2 country code", + "flags": "Flags, see TL conditional fields", + "state": "State", + "street": "Street" + } + }, "GeoPointEmpty": { "desc": "Empty constructor.", "params": {} @@ -2088,10 +2169,10 @@ "params": { "archive_and_mute_new_noncontact_peers": "Whether to archive and mute new chats from non-contacts", "flags": "Flags, see TL conditional fields", - "hide_read_marks": "", + "hide_read_marks": "If this flag is set, the inputPrivacyKeyStatusTimestamp key will also apply to the ability to use messages.getOutboxReadDate on messages sent to us. Meaning, users that cannot see our exact last online date due to the current value of the inputPrivacyKeyStatusTimestamp key will receive a 403 USER_PRIVACY_RESTRICTED error when invoking messages.getOutboxReadDate to fetch the exact read date of a message they sent to us. The userFull.read_dates_private flag will be set for users that have this flag enabled.", "keep_archived_folders": "Whether unmuted chats that are always included or pinned in a folder, will be kept in the Archive chat list when they get a new message. Ignored if keep_archived_unmuted is set.", "keep_archived_unmuted": "Whether unmuted chats will be kept in the Archive chat list when they get a new message.", - "new_noncontact_peers_require_premium": "" + "new_noncontact_peers_require_premium": "If set, only users that have a premium account, are in our contact list, or already have a private chat with us can write to us; a 403 PRIVACY_PREMIUM_REQUIRED error will be emitted otherwise. The userFull.contact_require_premium flag will be set for users that have this flag enabled. To check whether we can write to a user with this flag enabled, if we haven't yet cached all the required information (for example we don't have the userFull or history of all users while displaying the chat list in the sharing UI) the users.getIsPremiumRequiredToContact method may be invoked, passing the list of users currently visible in the UI, returning a list of booleans that directly specify whether we can or cannot write to each user. This option may be enabled by both non-Premium and Premium users only if the new_noncontact_peers_require_premium_without_ownpremium client configuration flag \u00bb is equal to true, otherwise it may be enabled only by Premium users and non-Premium users will receive a PREMIUM_ACCOUNT_REQUIRED error when trying to enable this flag." } }, "GroupCall": { @@ -2404,64 +2485,64 @@ } }, "InputBusinessAwayMessage": { - "desc": "{schema}", + "desc": "Describes a Telegram Business away message, automatically sent to users writing to us when we're offline, during closing hours, while we're on vacation, or in some other custom time period when we cannot immediately answer to the user.", "params": { "flags": "Flags, see TL conditional fields", - "offline_only": "", - "recipients": "", - "schedule": "", - "shortcut_id": "" + "offline_only": "If set, the messages will not be sent if the account was online in the last 10 minutes.", + "recipients": "Allowed recipients for the away messages.", + "schedule": "Specifies when should the away messages be sent.", + "shortcut_id": "ID of a quick reply shorcut, containing the away messages to send, see here \u00bb for more info." } }, "InputBusinessBotRecipients": { - "desc": "{schema}", + "desc": "Specifies the private chats that a connected business bot \u00bb may interact with.", "params": { - "contacts": "", - "exclude_selected": "", - "exclude_users": "", - "existing_chats": "", + "contacts": "Selects all private chats with contacts.", + "exclude_selected": "If set, then all private chats except the ones selected by existing_chats, new_chats, contacts, non_contacts and users are chosen. Note that if this flag is set, any values passed in exclude_users will be merged and moved into users by the server.", + "exclude_users": "Identifiers of private chats that are always excluded.", + "existing_chats": "Selects all existing private chats.", "flags": "Flags, see TL conditional fields", - "new_chats": "", - "non_contacts": "", - "users": "" + "new_chats": "Selects all new private chats.", + "non_contacts": "Selects all private chats with non-contacts.", + "users": "Explicitly selected private chats." } }, "InputBusinessChatLink": { - "desc": "{schema}", + "desc": "Contains info about a business chat deep link \u00bb to be created by the current account.", "params": { "entities": "Message entities for styled text", "flags": "Flags, see TL conditional fields", - "message": "", - "title": "" + "message": "Message to pre-fill in the message input field.", + "title": "Human-readable name of the link, to simplify management in the UI (only visible to the creator of the link)." } }, "InputBusinessGreetingMessage": { - "desc": "{schema}", + "desc": "Describes a Telegram Business greeting, automatically sent to new users writing to us in private for the first time, or after a certain inactivity period.", "params": { - "no_activity_days": "", - "recipients": "", - "shortcut_id": "" + "no_activity_days": "The number of days after which a private chat will be considered as inactive; currently, must be one of 7, 14, 21, or 28.", + "recipients": "Allowed recipients for the greeting messages.", + "shortcut_id": "ID of a quick reply shorcut, containing the greeting messages to send, see here \u00bb for more info." } }, "InputBusinessIntro": { - "desc": "{schema}", + "desc": "Telegram Business introduction \u00bb.", "params": { - "description": "", + "description": "Profile introduction", "flags": "Flags, see TL conditional fields", - "sticker": "", - "title": "" + "sticker": "Optional introduction sticker.", + "title": "Title of the introduction message" } }, "InputBusinessRecipients": { - "desc": "{schema}", + "desc": "Specifies the chats that can receive Telegram Business away \u00bb and greeting \u00bb messages.", "params": { - "contacts": "", - "exclude_selected": "", - "existing_chats": "", + "contacts": "All private chats with contacts.", + "exclude_selected": "If set, inverts the selection.", + "existing_chats": "All existing private chats.", "flags": "Flags, see TL conditional fields", - "new_chats": "", - "non_contacts": "", - "users": "" + "new_chats": "All new private chats.", + "non_contacts": "All private chats with non-contacts.", + "users": "Only private chats with the specified users." } }, "InputChannel": { @@ -2529,15 +2610,15 @@ } }, "InputCollectiblePhone": { - "desc": "{schema}", + "desc": "Represents a phone number fragment collectible", "params": { - "phone": "" + "phone": "Phone number" } }, "InputCollectibleUsername": { - "desc": "{schema}", + "desc": "Represents a username fragment collectible", "params": { - "username": "" + "username": "Username" } }, "InputDialogPeer": { @@ -2573,6 +2654,10 @@ "thumb_size": "Thumbnail size to download the thumbnail" } }, + "InputEmojiStatusCollectible": { + "desc": "", + "params": {} + }, "InputEncryptedChat": { "desc": "Creates an encrypted chat.", "params": { @@ -2641,6 +2726,12 @@ "volume_id": "Server volume" } }, + "InputFileStoryDocument": { + "desc": "Used to edit the thumbnail/static preview of a story, see here \u00bb for more info on the full flow.", + "params": { + "id": "The old story video." + } + }, "InputFolderPeer": { "desc": "Peer in a folder", "params": { @@ -2693,43 +2784,71 @@ "video_quality": "Selected video quality (0 = lowest, 1 = medium, 2 = best)" } }, + "InputInvoiceChatInviteSubscription": { + "desc": "Used to pay for a Telegram Star subscription \u00bb.", + "params": { + "hash": "The invitation link of the Telegram Star subscription \u00bb" + } + }, "InputInvoiceMessage": { - "desc": "An invoice contained in a messageMediaInvoice message.", + "desc": "An invoice contained in a messageMediaInvoice message or paid media \u00bb.", "params": { "msg_id": "Message ID", - "peer": "Chat where the invoice was sent" + "peer": "Chat where the invoice/paid media was sent" } }, "InputInvoicePremiumGiftCode": { - "desc": "Used if the user wishes to start a channel giveaway or send some giftcodes to members of a channel, in exchange for boosts.", + "desc": "Used if the user wishes to start a channel/supergroup giveaway or send some giftcodes to members of a channel/supergroup, in exchange for boosts.", "params": { "option": "Should be populated with one of the giveaway options returned by payments.getPremiumGiftCodeOptions, see the giveaways \u00bb documentation for more info.", "purpose": "Should be populated with inputStorePaymentPremiumGiveaway for giveaways and inputStorePaymentPremiumGiftCode for gifts." } }, + "InputInvoicePremiumGiftStars": { + "desc": "", + "params": {} + }, "InputInvoiceSlug": { "desc": "An invoice slug taken from an invoice deep link or from the premium_invoice_slug app config parameter \u00bb", "params": { "slug": "The invoice slug" } }, + "InputInvoiceStarGift": { + "desc": "Used to buy a Telegram Star Gift, see here \u00bb for more info.", + "params": { + "flags": "Flags, see TL conditional fields", + "gift_id": "Identifier of the gift, from starGift.id", + "hide_name": "If set, your name will be hidden if the destination user decides to display the gift on their profile (they will still see that you sent the gift)", + "message": "Optional message, attached with the gift. The maximum length for this field is specified in the stargifts_message_length_max client configuration value \u00bb.", + "user_id": "Identifier of the user that will receive the gift" + } + }, + "InputInvoiceStarGiftTransfer": { + "desc": "", + "params": {} + }, + "InputInvoiceStarGiftUpgrade": { + "desc": "", + "params": {} + }, "InputInvoiceStars": { - "desc": "{schema}", + "desc": "Used to top up the Telegram Stars balance of the current account or someone else's account, or to start a Telegram Star giveaway \u00bb.", "params": { - "option": "" + "purpose": "An inputStorePaymentStarsGiveaway, inputStorePaymentStarsTopup or inputStorePaymentStarsGift." } }, "InputKeyboardButtonRequestPeer": { - "desc": "{schema}", + "desc": "Prompts the user to select and share one or more peers with the bot using messages.sendBotRequestedPeer.", "params": { - "button_id": "", + "button_id": "Button ID, to be passed to messages.sendBotRequestedPeer.", "flags": "Flags, see TL conditional fields", - "max_quantity": "", - "name_requested": "", - "peer_type": "", - "photo_requested": "", - "text": "", - "username_requested": "" + "max_quantity": "Maximum number of peers that can be chosen.", + "name_requested": "Set this flag to request the peer's name.", + "peer_type": "Filtering criteria to use for the peer selection list shown to the user. The list should display all existing peers of the specified type, and should also offer an option for the user to create and immediately use one or more (up to max_quantity) peers of the specified type, if needed.", + "photo_requested": "Set this flag to request the peer's photo (if any).", + "text": "Button text", + "username_requested": "Set this flag to request the peer's @username (if any)." } }, "InputKeyboardButtonUrlAuth": { @@ -2831,7 +2950,7 @@ "desc": "Generated invoice of a bot payment", "params": { "description": "Product description, 1-255 characters", - "extended_media": "Extended media", + "extended_media": "Deprecated", "flags": "Flags, see TL conditional fields", "invoice": "The actual invoice", "payload": "Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.", @@ -2842,6 +2961,15 @@ "title": "Product name, 1-32 characters" } }, + "InputMediaPaidMedia": { + "desc": "Paid media, see here \u00bb for more info.", + "params": { + "extended_media": "Photos or videos.", + "flags": "Flags, see TL conditional fields", + "payload": "Bots only, specifies a custom payload that will then be passed in updateBotPurchasedPaidMedia when a payment is made (this field will not be visible to the user)", + "stars_amount": "The price of the media in Telegram Stars." + } + }, "InputMediaPhoto": { "desc": "Forwarded photo", "params": { @@ -2879,18 +3007,7 @@ }, "InputMediaUploadedDocument": { "desc": "New document", - "params": { - "attributes": "Attributes that specify the type of the document (video, audio, voice, sticker, etc.)", - "file": "The uploaded file", - "flags": "Flags, see TL conditional fields", - "force_file": "Force the media file to be uploaded as document", - "mime_type": "MIME type of document", - "nosound_video": "Whether the specified document is a video file with no audio tracks (a GIF animation (even as MPEG4), for example)", - "spoiler": "Whether this media should be hidden behind a spoiler warning", - "stickers": "Attached stickers", - "thumb": "Thumbnail of the document, uploaded as for the file", - "ttl_seconds": "Time to live in seconds of self-destructing document" - } + "params": {} }, "InputMediaUploadedPhoto": { "desc": "Photo", @@ -3200,7 +3317,7 @@ "params": {} }, "InputPrivacyKeyBirthday": { - "desc": "{schema}", + "desc": "Whether the user can see our birthday.", "params": {} }, "InputPrivacyKeyChatInvite": { @@ -3211,6 +3328,10 @@ "desc": "Whether messages forwarded from you will be anonymous", "params": {} }, + "InputPrivacyKeyNoPaidMessages": { + "desc": "", + "params": {} + }, "InputPrivacyKeyPhoneCall": { "desc": "Whether you will accept phone calls", "params": {} @@ -3227,18 +3348,26 @@ "desc": "Whether people will be able to see your profile picture", "params": {} }, + "InputPrivacyKeyStarGiftsAutoSave": { + "desc": "Whether received gifts will be automatically displayed on our profile", + "params": {} + }, "InputPrivacyKeyStatusTimestamp": { - "desc": "Whether people will be able to see your exact last online timestamp", + "desc": "Whether people will be able to see our exact last online timestamp.", "params": {} }, "InputPrivacyKeyVoiceMessages": { - "desc": "Whether people can send you voice messages", + "desc": "Whether people can send you voice messages or round videos (Premium users only).", "params": {} }, "InputPrivacyValueAllowAll": { "desc": "Allow all users", "params": {} }, + "InputPrivacyValueAllowBots": { + "desc": "Allow bots and mini apps", + "params": {} + }, "InputPrivacyValueAllowChatParticipants": { "desc": "Allow only participants of certain chats", "params": { @@ -3254,7 +3383,7 @@ "params": {} }, "InputPrivacyValueAllowPremium": { - "desc": "{schema}", + "desc": "Allow only users with a Premium subscription \u00bb, currently only usable for inputPrivacyKeyChatInvite.", "params": {} }, "InputPrivacyValueAllowUsers": { @@ -3267,6 +3396,10 @@ "desc": "Disallow all", "params": {} }, + "InputPrivacyValueDisallowBots": { + "desc": "Disallow bots and mini apps", + "params": {} + }, "InputPrivacyValueDisallowChatParticipants": { "desc": "Disallow only participants of certain chats", "params": { @@ -3284,15 +3417,15 @@ } }, "InputQuickReplyShortcut": { - "desc": "{schema}", + "desc": "Selects a quick reply shortcut by name.", "params": { - "shortcut": "" + "shortcut": "Shortcut name." } }, "InputQuickReplyShortcutId": { - "desc": "{schema}", + "desc": "Selects a quick reply shortcut by its numeric ID.", "params": { - "shortcut_id": "" + "shortcut_id": "Shortcut ID." } }, "InputReplyToMessage": { @@ -3310,7 +3443,7 @@ "InputReplyToStory": { "desc": "Reply to a story.", "params": { - "peer": "", + "peer": "Sender of the story", "story_id": "ID of the story to reply to." } }, @@ -3354,6 +3487,14 @@ "desc": "Report for violence", "params": {} }, + "InputSavedStarGiftChat": { + "desc": "", + "params": {} + }, + "InputSavedStarGiftUser": { + "desc": "", + "params": {} + }, "InputSecureFile": { "desc": "Pre-uploaded passport file, for more info see the passport docs \u00bb", "params": { @@ -3402,6 +3543,14 @@ "random_id": "Unique client media ID required to prevent message resending" } }, + "InputStarsTransaction": { + "desc": "Used to fetch info about a Telegram Star transaction \u00bb.", + "params": { + "flags": "Flags, see TL conditional fields", + "id": "Transaction ID.", + "refund": "If set, fetches info about the refund transaction for this transaction." + } + }, "InputStickerSetAnimatedEmoji": { "desc": "Animated emojis stickerset", "params": {} @@ -3482,6 +3631,10 @@ "id": "The photo" } }, + "InputStorePaymentAuthCode": { + "desc": "", + "params": {} + }, "InputStorePaymentGiftPremium": { "desc": "Info about a gifted Telegram Premium purchase", "params": { @@ -3491,12 +3644,13 @@ } }, "InputStorePaymentPremiumGiftCode": { - "desc": "Used to gift Telegram Premium subscriptions only to some specific subscribers of a channel or to some of our contacts, see here \u00bb for more info on giveaways and gifts.", + "desc": "Used to gift Telegram Premium subscriptions only to some specific subscribers of a channel/supergroup or to some of our contacts, see here \u00bb for more info on giveaways and gifts.", "params": { "amount": "Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", - "boost_peer": "If set, the gifts will be sent on behalf of a channel we are an admin of, which will also assign some boosts to it. Otherwise, the gift will be sent directly from the currently logged in users, and we will gain some extra boost slots. See here \u00bb for more info on giveaways and gifts.", + "boost_peer": "If set, the gifts will be sent on behalf of a channel/supergroup we are an admin of, which will also assign some boosts to it. Otherwise, the gift will be sent directly from the currently logged in user, and we will gain some extra boost slots. See here \u00bb for more info on giveaways and gifts.", "currency": "Three-letter ISO 4217 currency code", "flags": "Flags, see TL conditional fields", + "message": "Message attached with the gift", "users": "The users that will receive the Telegram Premium subscriptions." } }, @@ -3505,7 +3659,7 @@ "params": { "additional_peers": "Additional channels that the user must join to participate to the giveaway can be specified here.", "amount": "Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", - "boost_peer": "The channel starting the giveaway, that the user must join to participate, that will receive the giveaway boosts; see here \u00bb for more info on giveaways.", + "boost_peer": "The channel/supergroup starting the giveaway, that the user must join to participate, that will receive the giveaway boosts; see here \u00bb for more info on giveaways.", "countries_iso2": "The set of users that can participate to the giveaway can be restricted by passing here an explicit whitelist of up to giveaway_countries_max countries, specified as two-letter ISO 3166-1 alpha-2 country codes.", "currency": "Three-letter ISO 4217 currency code", "flags": "Flags, see TL conditional fields", @@ -3524,13 +3678,39 @@ "upgrade": "Pass true if this is an upgrade from a monthly subscription to a yearly subscription; only for App Store" } }, - "InputStorePaymentStars": { - "desc": "{schema}", + "InputStorePaymentStarsGift": { + "desc": "Used to gift Telegram Stars to a friend.", + "params": { + "amount": "Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", + "currency": "Three-letter ISO 4217 currency code", + "stars": "Amount of stars to gift", + "user_id": "The user to which the stars should be gifted." + } + }, + "InputStorePaymentStarsGiveaway": { + "desc": "Used to pay for a star giveaway, see here \u00bb for more info.", "params": { - "amount": "", - "currency": "", + "additional_peers": "Additional channels that the user must join to participate to the giveaway can be specified here.", + "amount": "Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", + "boost_peer": "The channel/supergroup starting the giveaway, that the user must join to participate, that will receive the giveaway boosts; see here \u00bb for more info on giveaways.", + "countries_iso2": "The set of users that can participate to the giveaway can be restricted by passing here an explicit whitelist of up to giveaway_countries_max countries, specified as two-letter ISO 3166-1 alpha-2 country codes.", + "currency": "Three-letter ISO 4217 currency code", "flags": "Flags, see TL conditional fields", - "stars": "" + "only_new_subscribers": "If set, only new subscribers starting from the giveaway creation date will be able to participate to the giveaway.", + "prize_description": "Can contain a textual description of additional giveaway prizes.", + "random_id": "Random ID to avoid resending the giveaway", + "stars": "Total number of Telegram Stars being given away (each user will receive stars/users stars).", + "until_date": "The end date of the giveaway, must be at most giveaway_period_max seconds in the future; see here \u00bb for more info on giveaways.", + "users": "Number of winners.", + "winners_are_visible": "If set, giveaway winners are public and will be listed in a messageMediaGiveawayResults message that will be automatically sent to the channel once the giveaway ends." + } + }, + "InputStorePaymentStarsTopup": { + "desc": "Used to top up the Telegram Stars balance of the current account.", + "params": { + "amount": "Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", + "currency": "Three-letter ISO 4217 currency code", + "stars": "Amount of stars to topup" } }, "InputTakeoutFileLocation": { @@ -3645,7 +3825,7 @@ "Invoice": { "desc": "Invoice", "params": { - "currency": "Three-letter ISO 4217 currency code", + "currency": "Three-letter ISO 4217 currency code, or XTR for Telegram Stars.", "email_requested": "Set this flag if you require the user's email address to complete the order", "email_to_provider": "Set this flag if user's email address should be sent to provider", "flags": "Flags, see TL conditional fields", @@ -3657,6 +3837,7 @@ "prices": "Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)", "recurring": "Whether this is a recurring payment", "shipping_address_requested": "Set this flag if you require the user's shipping address to complete the order", + "subscription_period": "The number of seconds between consecutive Telegram Star debiting for bot subscription invoices", "suggested_tip_amounts": "A vector of suggested amounts of tips in the smallest units of the currency (integer, not float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed max_tip_amount.", "terms_url": "Terms of service URL", "test": "Test invoice" @@ -3724,6 +3905,13 @@ "text": "Button text" } }, + "KeyboardButtonCopy": { + "desc": "Clipboard button: when clicked, the attached text must be copied to the clipboard.", + "params": { + "copy_text": "The text that will be copied to the clipboard", + "text": "Title of the button" + } + }, "KeyboardButtonGame": { "desc": "Button to start a game", "params": { @@ -3740,7 +3928,7 @@ "desc": "Prompts the user to select and share one or more peers with the bot using messages.sendBotRequestedPeer", "params": { "button_id": "Button ID, to be passed to messages.sendBotRequestedPeer.", - "max_quantity": "Maximum number of peers that can be chosne.", + "max_quantity": "Maximum number of peers that can be chosen.", "peer_type": "Filtering criteria to use for the peer selection list shown to the user. The list should display all existing peers of the specified type, and should also offer an option for the user to create and immediately use one or more (up to max_quantity) peers of the specified type, if needed.", "text": "Button text" } @@ -3892,7 +4080,9 @@ "MediaAreaCoordinates": { "desc": "Coordinates and size of a clicable rectangular area on top of a story.", "params": { + "flags": "Flags, see TL conditional fields", "h": "The height of the rectangle, as a percentage of the media height (0-100).", + "radius": "The radius of the rectangle corner rounding, as a percentage of the media width.", "rotation": "Clockwise rotation angle of the rectangle, in degrees (0-360).", "w": "The width of the rectangle, as a percentage of the media width (0-100).", "x": "The abscissa of the rectangle's center, as a percentage of the media width (0-100).", @@ -3902,10 +4092,16 @@ "MediaAreaGeoPoint": { "desc": "Represents a geolocation tag attached to a story.", "params": { + "address": "Optional textual representation of the address.", "coordinates": "The size and position of the media area corresponding to the location sticker on top of the story media.", + "flags": "Flags, see TL conditional fields", "geo": "Coordinates of the geolocation tag." } }, + "MediaAreaStarGift": { + "desc": "", + "params": {} + }, "MediaAreaSuggestedReaction": { "desc": "Represents a reaction bubble.", "params": { @@ -3916,6 +4112,13 @@ "reaction": "The reaction that should be sent when this area is clicked." } }, + "MediaAreaUrl": { + "desc": "Represents a URL media area.", + "params": { + "coordinates": "The size and location of the media area corresponding to the URL button on top of the story media.", + "url": "URL to open when clicked." + } + }, "MediaAreaVenue": { "desc": "Represents a location tag attached to a story, with additional venue information.", "params": { @@ -3928,19 +4131,28 @@ "venue_type": "Venue type in the provider's database" } }, + "MediaAreaWeather": { + "desc": "Represents a weather widget \u00bb.", + "params": { + "color": "ARGB background color.", + "coordinates": "The size and location of the media area corresponding to the widget on top of the story media.", + "emoji": "Weather emoji, should be rendered as an animated emoji.", + "temperature_c": "Temperature in degrees Celsius." + } + }, "Message": { "desc": "A message", "params": { "date": "Date of the message", "edit_date": "Last edit date of this message", "edit_hide": "Whether the message should be shown as not modified to the user, even if an edit date is present", - "effect": "", + "effect": "A message effect that should be played as specified here \u00bb.", "entities": "Message entities for styled text", - "factcheck": "", + "factcheck": "Represents a fact-check \u00bb.", "flags": "Flags, see TL conditional fields", - "flags2": "", + "flags2": "Flags, see TL conditional fields", "forwards": "Forward counter", - "from_boosts_applied": "", + "from_boosts_applied": "Supergroups only, contains the number of boosts this user has given the current supergroup, and should be shown in the UI in the header of the message. Only present for incoming messages from non-anonymous supergroup members that have boosted the supergroup. Note that this counter should be locally overridden for non-anonymous outgoing messages, according to the current value of channelFull.boosts_applied, to ensure the value is correct even for messages sent by the current user before a supergroup was boosted (or after a boost has expired or the number of boosts has changed); do not update this value for incoming messages from other users, even if their boosts have changed.", "from_id": "ID of the sender of the message", "from_scheduled": "Whether this is a scheduled message", "fwd_from": "Info about forwarded messages", @@ -3953,13 +4165,13 @@ "mentioned": "Whether we were mentioned in this message", "message": "The message", "noforwards": "Whether this message is protected and thus cannot be forwarded; clients should also prevent users from saving attached media (i.e. videos should only be streamed, photos should be kept in RAM, et cetera).", - "offline": "", + "offline": "If set, the message was sent because of a scheduled action by the message sender, for example, as away, or a greeting service message.", "out": "Is this an outgoing message", "peer_id": "Peer ID, the chat where this message was sent", "pinned": "Whether this message is pinned", "post": "Whether this is a channel post", "post_author": "Name of the author of this message for channel posts (with signatures enabled)", - "quick_reply_shortcut_id": "", + "quick_reply_shortcut_id": "If set, this message is a quick reply shortcut message \u00bb (note that quick reply shortcut messages sent to a private chat will not have this field set).", "reactions": "Reactions to this message", "replies": "Info about post comments (for channels) or message replies (for groups)", "reply_markup": "Reply markup (bot/inline keyboards)", @@ -3969,14 +4181,15 @@ "silent": "Whether this is a silent message (no notification triggered)", "ttl_period": "Time To Live of the message, once message.date+message.ttl_period === time(), the message will be deleted on the server, and must be deleted locally as well.", "via_bot_id": "ID of the inline bot that generated the message", - "via_business_bot_id": "", + "via_business_bot_id": "Whether the message was sent by the business bot specified in via_bot_id on behalf of the user.", + "video_processing_pending": "The video contained in the message is currently being processed by the server (i.e. to generate alternative qualities, that will be contained in the final messageMediaDocument.alt_document), and will be sent once the video is processed, which will happen approximately at the specified date (i.e. messages with this flag set should be treated similarly to scheduled messages, but instead of the scheduled date, date contains the estimated conversion date). See here \u00bb for more info.", "views": "View count for channel posts" } }, "MessageActionBoostApply": { - "desc": "{schema}", + "desc": "Some boosts \u00bb were applied to the channel or supergroup.", "params": { - "boosts": "" + "boosts": "Number of applied boosts." } }, "MessageActionBotAllowed": { @@ -4086,15 +4299,16 @@ "desc": "Contains a Telegram Premium giftcode link.", "params": { "amount": "Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", - "boost_peer": "Identifier of the channel that created the gift code either directly or through a giveaway: if we import this giftcode link, we will also automatically boost this channel.", + "boost_peer": "Identifier of the channel/supergroup that created the gift code either directly or through a giveaway: if we import this giftcode link, we will also automatically boost this channel/supergroup.", "crypto_amount": "If crypto_currency is set, contains the paid amount, in the smallest units of the cryptocurrency.", "crypto_currency": "If set, the gift was made using the specified cryptocurrency.", "currency": "Three-letter ISO 4217 currency code", "flags": "Flags, see TL conditional fields", + "message": "Message attached with the gift", "months": "Duration in months of the gifted Telegram Premium subscription.", "slug": "Slug of the Telegram Premium giftcode link", "unclaimed": "If set, the link was not redeemed yet.", - "via_giveaway": "If set, this gift code was received from a giveaway \u00bb started by a channel we're subscribed to." + "via_giveaway": "If set, this gift code was received from a giveaway \u00bb started by a channel/supergroup we're subscribed to." } }, "MessageActionGiftPremium": { @@ -4105,16 +4319,34 @@ "crypto_currency": "If the gift was bought using a cryptocurrency, the cryptocurrency name.", "currency": "Three-letter ISO 4217 currency code", "flags": "Flags, see TL conditional fields", + "message": "Message attached with the gift", "months": "Duration of the gifted Telegram Premium subscription" } }, + "MessageActionGiftStars": { + "desc": "You gifted or were gifted some Telegram Stars.", + "params": { + "amount": "Price of the gift in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", + "crypto_amount": "If the gift was bought using a cryptocurrency, price of the gift in the smallest units of a cryptocurrency.", + "crypto_currency": "If the gift was bought using a cryptocurrency, the cryptocurrency name.", + "currency": "Three-letter ISO 4217 currency code", + "flags": "Flags, see TL conditional fields", + "stars": "Amount of gifted stars", + "transaction_id": "Identifier of the transaction, only visible to the receiver of the gift." + } + }, "MessageActionGiveawayLaunch": { "desc": "A giveaway was started.", - "params": {} + "params": { + "flags": "Flags, see TL conditional fields", + "stars": "For Telegram Star giveaways, the total number of Telegram Stars being given away." + } }, "MessageActionGiveawayResults": { "desc": "A giveaway has ended.", "params": { + "flags": "Flags, see TL conditional fields", + "stars": "If set, this is a Telegram Star giveaway", "unclaimed_count": "Number of undistributed prizes", "winners_count": "Number of winners in the giveaway" } @@ -4145,14 +4377,34 @@ "users": "The invited users" } }, + "MessageActionPaidMessagesPrice": { + "desc": "", + "params": {} + }, + "MessageActionPaidMessagesRefunded": { + "desc": "", + "params": {} + }, + "MessageActionPaymentRefunded": { + "desc": "Describes a payment refund (service message received by both users and bots).", + "params": { + "charge": "Provider payment identifier", + "currency": "Currency, XTR for Telegram Stars.", + "flags": "Flags, see TL conditional fields", + "payload": "Bot specified invoice payload (only received by bots).", + "peer": "Identifier of the peer that returned the funds.", + "total_amount": "Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)." + } + }, "MessageActionPaymentSent": { "desc": "A payment was sent", "params": { - "currency": "Three-letter ISO 4217 currency code", + "currency": "Three-letter ISO 4217 currency code, or XTR for Telegram Stars.", "flags": "Flags, see TL conditional fields", "invoice_slug": "An invoice slug taken from an invoice deep link or from the premium_invoice_slug app config parameter \u00bb", "recurring_init": "Whether this is the first payment of a recurring payment we just subscribed to", "recurring_used": "Whether this payment is part of a recurring payment", + "subscription_until_date": "Expiration date of the Telegram Star subscription \u00bb.", "total_amount": "Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)." } }, @@ -4160,13 +4412,14 @@ "desc": "A user just sent a payment to me (a bot)", "params": { "charge": "Provider payment identifier", - "currency": "Three-letter ISO 4217 currency code", + "currency": "Three-letter ISO 4217 currency code, or XTR for Telegram Stars.", "flags": "Flags, see TL conditional fields", "info": "Order info provided by the user", "payload": "Bot specified invoice payload", "recurring_init": "Whether this is the first payment of a recurring payment we just subscribed to", "recurring_used": "Whether this payment is part of a recurring payment", "shipping_option_id": "Identifier of the shipping option chosen by the user", + "subscription_until_date": "Expiration date of the Telegram Star subscription \u00bb.", "total_amount": "Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)." } }, @@ -4184,18 +4437,29 @@ "desc": "A message was pinned", "params": {} }, + "MessageActionPrizeStars": { + "desc": "You won some Telegram Stars in a Telegram Star giveaway \u00bb.", + "params": { + "boost_peer": "Identifier of the peer that was automatically boosted by the winners of the giveaway.", + "flags": "Flags, see TL conditional fields", + "giveaway_msg_id": "ID of the message containing the messageMediaGiveaway", + "stars": "The number of Telegram Stars you won", + "transaction_id": "ID of the telegram star transaction.", + "unclaimed": "If set, this indicates the reverse transaction that refunds the remaining stars to the creator of a giveaway if, when the giveaway ends, the number of members in the channel is smaller than the number of winners in the giveaway." + } + }, "MessageActionRequestedPeer": { - "desc": "Contains info about one or more peers that the user shared with the bot after clicking on a keyboardButtonRequestPeer button.", + "desc": "Contains info about one or more peers that the we (the user) shared with the bot after clicking on a keyboardButtonRequestPeer button (service message sent by the user).", "params": { "button_id": "button_id contained in the keyboardButtonRequestPeer", "peers": "The shared peers" } }, "MessageActionRequestedPeerSentMe": { - "desc": "{schema}", + "desc": "Contains info about one or more peers that the a user shared with the me (the bot) after clicking on a keyboardButtonRequestPeer button (service message received by the bot).", "params": { - "button_id": "", - "peers": "" + "button_id": "button_id contained in the keyboardButtonRequestPeer", + "peers": "Info about the shared peers." } }, "MessageActionScreenshotTaken": { @@ -4238,6 +4502,22 @@ "period": "New Time-To-Live of all messages sent in this chat; if 0, autodeletion was disabled." } }, + "MessageActionStarGift": { + "desc": "You received a gift, see here \u00bb for more info.", + "params": { + "convert_stars": "The receiver of this gift may convert it to this many Telegram Stars, instead of displaying it on their profile page.convert_stars will be equal to stars only if the gift was bought using recently bought Telegram Stars, otherwise it will be less than stars.", + "converted": "Whether this gift was converted to Telegram Stars and cannot be displayed on the profile anymore.", + "flags": "Flags, see TL conditional fields", + "gift": "Info about the gift", + "message": "Additional message from the sender of the gift", + "name_hidden": "If set, the name of the sender of the gift will be hidden if the destination user decides to display the gift on their profile", + "saved": "Whether this gift was added to the destination user's profile (may be toggled using payments.saveStarGift and fetched using payments.getUserStarGifts)" + } + }, + "MessageActionStarGiftUnique": { + "desc": "", + "params": {} + }, "MessageActionSuggestProfilePhoto": { "desc": "A new profile picture was suggested using photos.uploadContactProfilePhoto.", "params": { @@ -4294,7 +4574,7 @@ "MessageEntityBlockquote": { "desc": "Message entity representing a block quote.", "params": { - "collapsed": "", + "collapsed": "Whether the quote is collapsed by default.", "flags": "Flags, see TL conditional fields", "length": "Length of message entity within message (in UTF-16 code units)", "offset": "Offset of message entity within message (in UTF-16 code units)" @@ -4431,18 +4711,18 @@ } }, "MessageExtendedMedia": { - "desc": "Extended media", + "desc": "Already purchased paid media, see here \u00bb for more info.", "params": { - "media": "Media" + "media": "The media we purchased." } }, "MessageExtendedMediaPreview": { - "desc": "Extended media preview", + "desc": "Paid media preview for not yet purchased paid media, see here \u00bb for more info.", "params": { "flags": "Flags, see TL conditional fields", "h": "Height", - "thumb": "Thumbnail", - "video_duration": "Video duration", + "thumb": "Extremely low resolution thumbnail.", + "video_duration": "Video duration for videos.", "w": "Width" } }, @@ -4485,7 +4765,7 @@ "MessageMediaDocument": { "desc": "Document (video, audio, voice, sticker, any media type except photo)", "params": { - "alt_document": "Currently only used for story videos, may contain an alternative version of the story video, explicitly encoded using H.264 (in MPEG4 transport) at a lower resolution than document.", + "alt_documents": "Videos only, contains alternative qualities of the video.", "document": "Attached document", "flags": "Flags, see TL conditional fields", "nopremium": "Whether this is a normal sticker, if not set this is a premium sticker and a premium sticker animation must be played.", @@ -4532,6 +4812,7 @@ "only_new_subscribers": "If set, only new subscribers starting from the giveaway creation date will be able to participate to the giveaway.", "prize_description": "Can contain a textual description of additional giveaway prizes.", "quantity": "Number of Telegram Premium subscriptions given away.", + "stars": "For Telegram Star giveaways, the total number of Telegram Stars being given away.", "until_date": "The end date of the giveaway.", "winners_are_visible": "If set, giveaway winners are public and will be listed in a messageMediaGiveawayResults message that will be automatically sent to the channel once the giveaway ends." } @@ -4540,13 +4821,14 @@ "desc": "A giveaway with public winners has finished, this constructor contains info about the winners.", "params": { "additional_peers_count": "Number of other channels that participated in the giveaway.", - "channel_id": "ID of the channel that was automatically boosted by the winners of the giveaway for duration of the Premium subscription.", + "channel_id": "ID of the channel/supergroup that was automatically boosted by the winners of the giveaway for duration of the Premium subscription.", "flags": "Flags, see TL conditional fields", "launch_msg_id": "Identifier of the message with the giveaway in channel_id.", "months": "Duration in months of each Telegram Premium subscription in the giveaway.", "only_new_subscribers": "If set, only new subscribers starting from the giveaway creation date participated in the giveaway.", "prize_description": "Can contain a textual description of additional giveaway prizes.", "refunded": "If set, the giveaway was canceled and was fully refunded.", + "stars": "For Telegram Star giveaways, the total number of Telegram Stars being given away.", "unclaimed_count": "Number of not-yet-claimed prizes.", "until_date": "Point in time (Unix timestamp) when the winners were selected. May be bigger than winners selection date specified in initial parameters of the giveaway.", "winners": "Up to 100 user identifiers of the winners of the giveaway.", @@ -4556,9 +4838,9 @@ "MessageMediaInvoice": { "desc": "Invoice", "params": { - "currency": "Three-letter ISO 4217 currency code", + "currency": "Three-letter ISO 4217 currency code, or XTR for Telegram Stars.", "description": "Product description, 1-255 characters", - "extended_media": "Extended media", + "extended_media": "Deprecated", "flags": "Flags, see TL conditional fields", "photo": "URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.", "receipt_msg_id": "Message ID of receipt: if set, clients should change the text of the first keyboardButtonBuy button always attached to the message to a localized version of the word Receipt", @@ -4569,6 +4851,13 @@ "total_amount": "Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies)." } }, + "MessageMediaPaidMedia": { + "desc": "Paid media, see here \u00bb for more info.", + "params": { + "extended_media": "Either the paid-for media, or super low resolution media previews if the media wasn't purchased yet, see here \u00bb for more info.", + "stars_amount": "The price of the media in Telegram Stars." + } + }, "MessageMediaPhoto": { "desc": "Attached photo.", "params": { @@ -4669,9 +4958,21 @@ "can_see_list": "Whether messages.getMessageReactionsList can be used to see how each specific peer reacted to the message", "flags": "Flags, see TL conditional fields", "min": "Similar to min objects, used for message reaction \u00bb constructors that are the same for all users so they don't have the reactions sent by the current user (you can use messages.getMessagesReactions to get the full reaction info).", - "reactions_as_tags": "", + "reactions_as_tags": "If set or if there are no reactions, all present and future reactions should be treated as message tags, see here \u00bb for more info.", "recent_reactions": "List of recent peers and their reactions", - "results": "Reactions" + "results": "Reactions", + "top_reactors": "Paid Telegram Star reactions leaderboard \u00bb for this message." + } + }, + "MessageReactor": { + "desc": "Info about a user in the paid Star reactions leaderboard for a message.", + "params": { + "anonymous": "If set, the reactor is anonymous.", + "count": "The number of sent Telegram Stars.", + "flags": "Flags, see TL conditional fields", + "my": "If set, this reactor is the current user.", + "peer_id": "Identifier of the peer that reacted: may be unset for anonymous reactors different from the current user (i.e. if the current user sent an anonymous reaction anonymous will be set but this field will also be set).", + "top": "If set, the reactor is one of the most active reactors; may be unset if the reactor is the current user." } }, "MessageReplies": { @@ -4707,10 +5008,17 @@ "MessageReplyStoryHeader": { "desc": "Represents a reply to a story", "params": { - "peer": "", + "peer": "Sender of the story.", "story_id": "Story ID" } }, + "MessageReportOption": { + "desc": "Report menu option", + "params": { + "option": "Option identifier: if the user selects this option, re-invoke messages.report, passing this option to option", + "text": "Option title" + } + }, "MessageService": { "desc": "Indicates a service message", "params": { @@ -4740,12 +5048,12 @@ } }, "MissingInvitee": { - "desc": "{schema}", + "desc": "Info about why a specific user could not be invited \u00bb.", "params": { "flags": "Flags, see TL conditional fields", - "premium_required_for_pm": "", - "premium_would_allow_invite": "", - "user_id": "" + "premium_required_for_pm": "If set, we could not add the user because of their privacy settings, and additionally, the current account needs to purchase a Telegram Premium subscription to directly share an invite link with the user via a private message.", + "premium_would_allow_invite": "If set, we could not add the user only because the current account needs to purchase a Telegram Premium subscription to complete the operation.", + "user_id": "ID of the user. If neither of the flags below are set, we could not add the user because of their privacy settings, and we can create and directly share an invite link with them using a normal message, instead." } }, "MyBoost": { @@ -4814,9 +5122,9 @@ "params": {} }, "OutboxReadDate": { - "desc": "{schema}", + "desc": "Exact read date of a private message we sent to another user.", "params": { - "date": "" + "date": "UNIX timestamp with the read date." } }, "Page": { @@ -5110,6 +5418,18 @@ "cells": "Table cells" } }, + "PaidReactionPrivacyAnonymous": { + "desc": "", + "params": {} + }, + "PaidReactionPrivacyDefault": { + "desc": "", + "params": {} + }, + "PaidReactionPrivacyPeer": { + "desc": "", + "params": {} + }, "PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow": { "desc": "This key derivation algorithm defines that SRP 2FA login must be used", "params": { @@ -5218,10 +5538,10 @@ "add_contact": "Whether we can add the user as contact", "autoarchived": "Whether this peer was automatically archived according to privacy settings and can be unarchived", "block_contact": "Whether we can block the user", - "business_bot_can_reply": "", - "business_bot_id": "", - "business_bot_manage_url": "", - "business_bot_paused": "", + "business_bot_can_reply": "This flag is set if both business_bot_id and business_bot_manage_url are set and connected business bots \u00bb can reply to messages in this chat, as specified by the settings during initial configuration.", + "business_bot_id": "Contains the ID of the business bot \u00bb managing this chat, used to display info about the bot in the action bar.", + "business_bot_manage_url": "Contains a deep link \u00bb, used to open a management menu in the business bot. This flag is set if and only if business_bot_id is set.", + "business_bot_paused": "This flag is set if both business_bot_id and business_bot_manage_url are set and all connected business bots \u00bb were paused in this chat using account.toggleConnectedBotPaused \u00bb.", "flags": "Flags, see TL conditional fields", "geo_distance": "Distance in meters between us and this peer", "invite_members": "If set, this is a recently created group chat to which new members can be invited", @@ -5254,8 +5574,8 @@ "params": { "access_hash": "Access hash", "admin_id": "User ID of the creator of the call", - "connections": "List of endpoints the user can connect to to exchange call data", - "custom_parameters": "", + "connections": "List of endpoints the user can connect to exchange call data", + "custom_parameters": "Custom JSON-encoded call parameters to be passed to tgcalls.", "date": "Date of creation of the call", "flags": "Flags, see TL conditional fields", "g_a_or_b": "Parameter for key exchange", @@ -5282,6 +5602,10 @@ "video": "Whether this is a video call" } }, + "PhoneCallDiscardReasonAllowGroupCall": { + "desc": "", + "params": {} + }, "PhoneCallDiscardReasonBusy": { "desc": "The phone call was discarded because the user is busy in another call", "params": {} @@ -5459,7 +5783,7 @@ "id": "ID of the poll", "multiple_choice": "Whether multiple options can be chosen as answer", "public_voters": "Whether cast votes are publicly visible to all users (non-anonymous poll)", - "question": "The question of the poll", + "question": "The question of the poll (only Premium users can use custom emoji entities here).", "quiz": "Whether this is a quiz (with wrong and correct answers, results shown in the return type)" } }, @@ -5467,7 +5791,7 @@ "desc": "A possible answer of a poll", "params": { "option": "The param that has to be passed to messages.sendVote.", - "text": "Textual representation of the answer" + "text": "Textual representation of the answer (only Premium users can use custom emoji entities here)." } }, "PollAnswerVoters": { @@ -5540,17 +5864,6 @@ "users": "Number of users which will be able to activate the gift codes." } }, - "PremiumGiftOption": { - "desc": "Telegram Premium gift option", - "params": { - "amount": "Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", - "bot_url": "An invoice deep link \u00bb to an invoice for in-app payment, using the official Premium bot; may be empty if direct payment isn't available.", - "currency": "Three-letter ISO 4217 currency code", - "flags": "Flags, see TL conditional fields", - "months": "Duration of gifted Telegram Premium subscription", - "store_product": "An identifier for the App Store/Play Store product associated with the Premium gift." - } - }, "PremiumSubscriptionOption": { "desc": "Describes a Telegram Premium subscription option", "params": { @@ -5574,6 +5887,16 @@ "quantity": "Number of given away Telegram Premium subscriptions." } }, + "PrepaidStarsGiveaway": { + "desc": "Contains info about a prepaid Telegram Star giveaway \u00bb.", + "params": { + "boosts": "Number of boosts the channel will gain by launching the giveaway.", + "date": "When was the giveaway paid for", + "id": "Prepaid giveaway ID.", + "quantity": "Number of giveaway winners", + "stars": "Number of given away Telegram Stars \u00bb" + } + }, "PrivacyKeyAbout": { "desc": "Whether people can see your bio", "params": {} @@ -5583,7 +5906,7 @@ "params": {} }, "PrivacyKeyBirthday": { - "desc": "{schema}", + "desc": "Whether the user can see our birthday.", "params": {} }, "PrivacyKeyChatInvite": { @@ -5594,6 +5917,10 @@ "desc": "Whether messages forwarded from the user will be anonymously forwarded", "params": {} }, + "PrivacyKeyNoPaidMessages": { + "desc": "", + "params": {} + }, "PrivacyKeyPhoneCall": { "desc": "Whether the user accepts phone calls", "params": {} @@ -5610,8 +5937,12 @@ "desc": "Whether the profile picture of the user is visible", "params": {} }, + "PrivacyKeyStarGiftsAutoSave": { + "desc": "Whether received gifts will be automatically displayed on our profile", + "params": {} + }, "PrivacyKeyStatusTimestamp": { - "desc": "Whether we can see the last online timestamp of this user", + "desc": "Whether we can see the last online timestamp of this user.", "params": {} }, "PrivacyKeyVoiceMessages": { @@ -5622,6 +5953,10 @@ "desc": "Allow all users", "params": {} }, + "PrivacyValueAllowBots": { + "desc": "Allow bots and mini apps", + "params": {} + }, "PrivacyValueAllowChatParticipants": { "desc": "Allow all participants of certain chats", "params": { @@ -5637,7 +5972,7 @@ "params": {} }, "PrivacyValueAllowPremium": { - "desc": "{schema}", + "desc": "Allow only users with a Premium subscription \u00bb, currently only usable for inputPrivacyKeyChatInvite.", "params": {} }, "PrivacyValueAllowUsers": { @@ -5650,6 +5985,10 @@ "desc": "Disallow all users", "params": {} }, + "PrivacyValueDisallowBots": { + "desc": "Disallow bots and mini apps", + "params": {} + }, "PrivacyValueDisallowChatParticipants": { "desc": "Disallow only participants of certain chats", "params": { @@ -5680,12 +6019,12 @@ } }, "QuickReply": { - "desc": "{schema}", + "desc": "A quick reply shortcut.", "params": { - "count": "", - "shortcut": "", - "shortcut_id": "", - "top_message": "" + "count": "Total number of messages in the shortcut.", + "shortcut": "Shortcut name.", + "shortcut_id": "Unique shortcut ID.", + "top_message": "ID of the last message in the shortcut." } }, "ReactionCount": { @@ -5714,21 +6053,25 @@ "params": {} }, "ReactionNotificationsFromAll": { - "desc": "{schema}", + "desc": "Receive notifications about reactions made by any user.", "params": {} }, "ReactionNotificationsFromContacts": { - "desc": "{schema}", + "desc": "Receive notifications about reactions made only by our contacts.", + "params": {} + }, + "ReactionPaid": { + "desc": "Represents a paid Telegram Star reaction \u00bb.", "params": {} }, "ReactionsNotifySettings": { - "desc": "{schema}", + "desc": "Reaction notification settings, see here \u00bb for more info.", "params": { "flags": "Flags, see TL conditional fields", - "messages_notify_from": "", - "show_previews": "", - "sound": "", - "stories_notify_from": "" + "messages_notify_from": "Message reaction notification settings, if not set completely disables notifications/updates about message reactions.", + "show_previews": "If false, push notifications \u00bb about message/story reactions will only be of type REACT_HIDDEN/REACT_STORY_HIDDEN, without any information about the reacted-to story or the reaction itself.", + "sound": "Notification sound for reactions \u00bb", + "stories_notify_from": "Story reaction notification settings, if not set completely disables notifications/updates about reactions to stories." } }, "ReadParticipantDate": { @@ -5813,6 +6156,25 @@ "single_use": "Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat \u2013 the user can press a special button in the input field to see the custom keyboard again." } }, + "ReportResultAddComment": { + "desc": "The user should enter an additional comment for the moderators, and then messages.report must be re-invoked, passing the comment to messages.report.message.", + "params": { + "flags": "Flags, see TL conditional fields", + "option": "The messages.report method must be re-invoked, passing this option to option", + "optional": "Whether this step can be skipped by the user, passing an empty message to messages.report, or if a non-empty message is mandatory." + } + }, + "ReportResultChooseOption": { + "desc": "The user must choose one of the following options, and then messages.report must be re-invoked, passing the option's option identifier to messages.report.option.", + "params": { + "options": "Available options, rendered as menu entries.", + "title": "Title of the option popup" + } + }, + "ReportResultReported": { + "desc": "The report was sent successfully, no further actions are required.", + "params": {} + }, "RequestPeerTypeBroadcast": { "desc": "Choose a channel", "params": { @@ -5844,40 +6206,52 @@ } }, "RequestedPeerChannel": { - "desc": "{schema}", + "desc": "Info about a channel/supergroup, shared by a user with the currently logged in bot using messages.sendBotRequestedPeer.", "params": { - "channel_id": "", + "channel_id": "Channel/supergroup ID.", "flags": "Flags, see TL conditional fields", - "photo": "", - "title": "", - "username": "" + "photo": "Channel/supergroup photo.", + "title": "Channel/supergroup title.", + "username": "Channel/supergroup username." } }, "RequestedPeerChat": { - "desc": "{schema}", + "desc": "Info about a chat, shared by a user with the currently logged in bot using messages.sendBotRequestedPeer.", "params": { - "chat_id": "", + "chat_id": "Chat ID.", "flags": "Flags, see TL conditional fields", - "photo": "", - "title": "" + "photo": "Chat photo.", + "title": "Chat title." } }, "RequestedPeerUser": { - "desc": "{schema}", + "desc": "Info about a user, shared by a user with the currently logged in bot using messages.sendBotRequestedPeer.", "params": { - "first_name": "", + "first_name": "First name.", "flags": "Flags, see TL conditional fields", - "last_name": "", - "photo": "", - "user_id": "", - "username": "" + "last_name": "Last name.", + "photo": "Profile photo.", + "user_id": "User ID.", + "username": "Username." } }, + "RequirementToContactEmpty": { + "desc": "", + "params": {} + }, + "RequirementToContactPaidMessages": { + "desc": "", + "params": {} + }, + "RequirementToContactPremium": { + "desc": "", + "params": {} + }, "RestrictionReason": { "desc": "Restriction reason.", "params": { "platform": "Platform identifier (ios, android, wp, all, etc.), can be concatenated with a dash as separator (android-ios, ios-wp, etc)", - "reason": "Restriction reason (porno, terms, etc.)", + "reason": "Restriction reason (porno, terms, etc.). Ignore this restriction reason if it is contained in the ignore_restriction_reasons \u00bb client configuration parameter.", "text": "Error message to be shown to the user" } }, @@ -5900,14 +6274,18 @@ } }, "SavedReactionTag": { - "desc": "{schema}", + "desc": "Info about a saved message reaction tag \u00bb.", "params": { - "count": "", + "count": "Number of messages tagged with this tag.", "flags": "Flags, see TL conditional fields", - "reaction": "", - "title": "" + "reaction": "Reaction associated to the tag.", + "title": "Custom tag name assigned by the user (max 12 UTF-8 chars)." } }, + "SavedStarGift": { + "desc": "", + "params": {} + }, "SearchResultPosition": { "desc": "Information about a message in a specific position", "params": { @@ -6258,18 +6636,12 @@ "title": "Title" } }, - "SimpleWebViewResultUrl": { - "desc": "Contains the webview URL with appropriate theme parameters added", - "params": { - "url": "URL" - } - }, "SmsJob": { - "desc": "{schema}", + "desc": "Info about an SMS job.", "params": { - "job_id": "", - "phone_number": "", - "text": "" + "job_id": "Job ID", + "phone_number": "Destination phone number", + "text": "Text" } }, "SpeakingInGroupCallAction": { @@ -6280,76 +6652,230 @@ "desc": "A sponsored message.", "params": { "additional_info": "If set, contains additional information about the sponsored message to be shown along with the message.", - "button_text": "Text of the sponsored message button.", - "can_report": "", - "color": "", - "entities": "Message entities for styled text", + "button_text": "Label of the sponsored message button.", + "can_report": "Whether this message can be reported as specified here \u00bb.", + "color": "If set, the sponsored message should use the message accent color \u00bb specified in color.", + "entities": "Message entities for styled text in message.", "flags": "Flags, see TL conditional fields", + "media": "If set, contains some media.", "message": "Sponsored message", - "photo": "", + "photo": "If set, contains a custom profile photo bubble that should be displayed for the sponsored message, like for messages sent in groups.", "random_id": "Message ID", "recommended": "Whether the message needs to be labeled as \"recommended\" instead of \"sponsored\"", "sponsor_info": "If set, contains additional information about the sponsor to be shown along with the message.", - "title": "", - "url": "" + "title": "Contains the title of the sponsored message.", + "url": "Contains the URL to open when the user clicks on the sponsored message." } }, "SponsoredMessageReportOption": { - "desc": "{schema}", + "desc": "A report option for a sponsored message \u00bb.", "params": { - "option": "", - "text": "" + "option": "Option identifier to pass to channels.reportSponsoredMessage.", + "text": "Localized description of the option." } }, - "StarsTopupOption": { - "desc": "{schema}", - "params": { - "amount": "", - "currency": "", - "extended": "", - "flags": "Flags, see TL conditional fields", - "stars": "", - "store_product": "" - } + "SponsoredPeer": { + "desc": "", + "params": {} }, - "StarsTransaction": { - "desc": "{schema}", + "StarGift": { + "desc": "Represents a star gift, see here \u00bb for more info.", "params": { - "date": "", - "description": "", + "availability_remains": "For limited-supply gifts: the remaining number of gifts that may be bought.", + "availability_total": "For limited-supply gifts: the total number of gifts that was available in the initial supply.", + "birthday": "Whether this is a birthday-themed gift", + "convert_stars": "The receiver of this gift may convert it to this many Telegram Stars, instead of displaying it on their profile page.convert_stars will be equal to stars only if the gift was bought using recently bought Telegram Stars, otherwise it will be less than stars.", + "first_sale_date": "For sold out gifts only: when was the gift first bought.", "flags": "Flags, see TL conditional fields", - "id": "", - "peer": "", - "photo": "", - "refund": "", - "stars": "", - "title": "" + "id": "Identifier of the gift", + "last_sale_date": "For sold out gifts only: when was the gift last bought.", + "limited": "Whether this is a limited-supply gift.", + "sold_out": "Whether this gift sold out and cannot be bought anymore.", + "stars": "Price of the gift in Telegram Stars.", + "sticker": "Sticker that represents the gift." } }, - "StarsTransactionPeer": { - "desc": "{schema}", - "params": { - "peer": "" - } + "StarGiftAttributeBackdrop": { + "desc": "", + "params": {} }, - "StarsTransactionPeerAppStore": { - "desc": "{schema}", + "StarGiftAttributeModel": { + "desc": "", "params": {} }, - "StarsTransactionPeerFragment": { - "desc": "{schema}", + "StarGiftAttributeOriginalDetails": { + "desc": "", "params": {} }, - "StarsTransactionPeerPlayMarket": { - "desc": "{schema}", + "StarGiftAttributePattern": { + "desc": "", + "params": {} + }, + "StarGiftUnique": { + "desc": "", + "params": {} + }, + "StarRefProgram": { + "desc": "Indo about an affiliate program offered by a bot", + "params": { + "bot_id": "ID of the bot that offers the program", + "commission_permille": "An affiliate gets a commission of starRefProgram.commission_permille\u2030 Telegram Stars for every mini app transaction made by users they refer", + "daily_revenue_per_user": "The amount of daily revenue per user in Telegram Stars of the bot that created the affiliate program. To obtain the approximated revenue per referred user, multiply this value by commission_permille and divide by 1000.", + "duration_months": "An affiliate gets a commission for every mini app transaction made by users they refer, for duration_months months after a referral link is imported, starting the bot for the first time", + "end_date": "Point in time (Unix timestamp) when the affiliate program will be closed (optional, if not set the affiliate program isn't scheduled to be closed)", + "flags": "Flags, see TL conditional fields" + } + }, + "StarsAmount": { + "desc": "Describes a real (i.e. possibly decimal) amount of Telegram Stars.", + "params": { + "amount": "The integer amount of Telegram Stars.", + "nanos": "The decimal amount of Telegram Stars, expressed as nanostars (i.e. 1 nanostar is equal to 1/1'000'000'000th of a Telegram Star). This field may also be negative (the allowed range is -999999999 to 999999999)." + } + }, + "StarsGiftOption": { + "desc": "Telegram Stars gift option.", + "params": { + "amount": "Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", + "currency": "Three-letter ISO 4217 currency code", + "extended": "If set, the option must only be shown in the full list of topup options.", + "flags": "Flags, see TL conditional fields", + "stars": "Amount of Telegram stars.", + "store_product": "Identifier of the store product associated with the option, official apps only." + } + }, + "StarsGiveawayOption": { + "desc": "Contains info about a Telegram Star giveaway option.", + "params": { + "amount": "Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", + "currency": "Three-letter ISO 4217 currency code", + "default": "If set, this option must be pre-selected by default in the option list.", + "extended": "If set, this option must only be shown in the full list of giveaway options (i.e. they must be added to the list only when the user clicks on the expand button).", + "flags": "Flags, see TL conditional fields", + "stars": "The number of Telegram Stars that will be distributed among winners", + "store_product": "Identifier of the store product associated with the option, official apps only.", + "winners": "Allowed options for the number of giveaway winners.", + "yearly_boosts": "Number of times the chat will be boosted for one year if the inputStorePaymentStarsGiveaway.boost_peer flag is populated" + } + }, + "StarsGiveawayWinnersOption": { + "desc": "Allowed options for the number of giveaway winners.", + "params": { + "default": "If set, this option must be pre-selected by default in the option list.", + "flags": "Flags, see TL conditional fields", + "per_user_stars": "The number of Telegram Stars each winner will receive.", + "users": "The number of users that will be randomly chosen as winners." + } + }, + "StarsRevenueStatus": { + "desc": "Describes Telegram Star revenue balances \u00bb.", + "params": { + "available_balance": "Amount of withdrawable Telegram Stars.", + "current_balance": "Amount of not-yet-withdrawn Telegram Stars.", + "flags": "Flags, see TL conditional fields", + "next_withdrawal_at": "Unixtime indicating when will withdrawal be available to the user. If not set, withdrawal can be started now.", + "overall_revenue": "Total amount of earned Telegram Stars.", + "withdrawal_enabled": "If set, the user may withdraw up to available_balance stars." + } + }, + "StarsSubscription": { + "desc": "Represents a Telegram Star subscription \u00bb.", + "params": { + "bot_canceled": "Set if this bot subscription was cancelled by the bot", + "can_refulfill": "Whether we left the associated private channel, but we can still rejoin it using payments.fulfillStarsSubscription because the current subscription period hasn't expired yet.", + "canceled": "Whether this subscription was cancelled.", + "chat_invite_hash": "Invitation link, used to renew the subscription after cancellation or expiration.", + "flags": "Flags, see TL conditional fields", + "id": "Subscription ID.", + "invoice_slug": "For bot subscriptions, the identifier of the subscription invoice", + "missing_balance": "Whether this subscription has expired because there are not enough stars on the user's balance to extend it.", + "peer": "Identifier of the associated private chat.", + "photo": "For bot subscriptions, the photo from the subscription invoice", + "pricing": "Pricing of the subscription in Telegram Stars.", + "title": "For bot subscriptions, the title of the subscription invoice", + "until_date": "Expiration date of the current subscription period." + } + }, + "StarsSubscriptionPricing": { + "desc": "Pricing of a Telegram Star subscription \u00bb.", + "params": { + "amount": "Price of the subscription in Telegram Stars.", + "period": "The user should pay amount stars every period seconds to gain and maintain access to the channel. Currently the only allowed subscription period is 30*24*60*60, i.e. the user will be debited amount stars every month." + } + }, + "StarsTopupOption": { + "desc": "Telegram Stars topup option.", + "params": { + "amount": "Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", + "currency": "Three-letter ISO 4217 currency code", + "extended": "If set, the option must only be shown in the full list of topup options.", + "flags": "Flags, see TL conditional fields", + "stars": "Amount of Telegram stars.", + "store_product": "Identifier of the store product associated with the option, official apps only." + } + }, + "StarsTransaction": { + "desc": "Represents a Telegram Stars transaction \u00bb.", + "params": { + "bot_payload": "Bot specified invoice payload (i.e. the payload passed to inputMediaInvoice when creating the invoice).", + "date": "Date of the transaction (unixtime).", + "description": "For transactions with bots, description of the bought product.", + "extended_media": "The purchased paid media \u00bb.", + "failed": "This transaction has failed.", + "flags": "Flags, see TL conditional fields", + "floodskip_number": "This transaction is payment for paid bot broadcasts. Paid broadcasts are only allowed if the allow_paid_floodskip parameter of messages.sendMessage and other message sending methods is set while trying to broadcast more than 30 messages per second to bot users. The integer value returned by this flag indicates the number of billed API calls.", + "gift": "This transaction was a gift from the user in peer.peer.", + "giveaway_post_id": "ID of the message containing the messageMediaGiveaway, for incoming star giveaway prizes.", + "id": "Transaction ID.", + "msg_id": "For paid media transactions \u00bb, message ID of the paid media posted to peer.peer (can point to a deleted message; either way, extended_media will always contain the bought media).", + "peer": "Source of the incoming transaction, or its recipient for outgoing transactions.", + "pending": "The transaction is currently pending.", + "photo": "For transactions with bots, photo of the bought product.", + "reaction": "This transaction is a paid reaction \u00bb.", + "refund": "Whether this transaction is a refund.", + "stargift": "This transaction indicates a purchase or a sale (conversion back to Stars) of a gift \u00bb.", + "starref_amount": "For transactions made by referred users, the amount of Telegram Stars received by the affiliate, can be negative for refunds.", + "starref_commission_permille": "This transaction is the receival (or refund) of an affiliate commission (i.e. this is the transaction received by the peer that created the referral link, flag 17 is for transactions made by users that imported the referral link).", + "starref_peer": "For transactions made by referred users, the peer that received the affiliate commission.", + "stars": "Amount of Stars (negative for outgoing transactions).", + "subscription_period": "The number of seconds between consecutive Telegram Star debiting for Telegram Star subscriptions \u00bb.", + "title": "For transactions with bots, title of the bought product.", + "transaction_date": "If neither pending nor failed are set, the transaction was completed successfully, and this field will contain the point in time (Unix timestamp) when the withdrawal was completed successfully.", + "transaction_url": "If neither pending nor failed are set, the transaction was completed successfully, and this field will contain a URL where the withdrawal transaction can be viewed." + } + }, + "StarsTransactionPeer": { + "desc": "Describes a Telegram Star transaction with another peer.", + "params": { + "peer": "The peer." + } + }, + "StarsTransactionPeerAPI": { + "desc": "Describes a Telegram Star transaction used to pay for paid API usage, such as paid bot broadcasts.", + "params": {} + }, + "StarsTransactionPeerAds": { + "desc": "Describes a Telegram Star transaction used to pay for Telegram ads as specified here \u00bb.", + "params": {} + }, + "StarsTransactionPeerAppStore": { + "desc": "Describes a Telegram Star transaction with the App Store, used when purchasing Telegram Stars through the App Store.", + "params": {} + }, + "StarsTransactionPeerFragment": { + "desc": "Describes a Telegram Star transaction with Fragment, used when purchasing Telegram Stars through Fragment.", + "params": {} + }, + "StarsTransactionPeerPlayMarket": { + "desc": "Describes a Telegram Star transaction with the Play Store, used when purchasing Telegram Stars through the Play Store.", "params": {} }, "StarsTransactionPeerPremiumBot": { - "desc": "{schema}", + "desc": "Describes a Telegram Star transaction made using @PremiumBot (i.e. using the inputInvoiceStars flow described here \u00bb).", "params": {} }, "StarsTransactionPeerUnsupported": { - "desc": "{schema}", + "desc": "Describes a Telegram Star transaction that cannot be described using the current layer.", "params": {} }, "StatsAbsValueAndPrev": { @@ -6442,9 +6968,9 @@ "params": { "access_hash": "Access hash of stickerset", "archived": "Whether this stickerset was archived (due to too many saved stickers in the current account)", - "channel_emoji_status": "If set, this custom emoji stickerset can be used in channel emoji statuses.", + "channel_emoji_status": "If set, this custom emoji stickerset can be used in channel/supergroup emoji statuses.", "count": "Number of stickers in pack", - "creator": "", + "creator": "Whether we created this stickerset", "emojis": "This is a custom emoji stickerset", "flags": "Flags, see TL conditional fields", "hash": "Hash", @@ -6519,7 +7045,7 @@ "entities": "Message entities for styled text", "expire_date": "When does the story expire.", "flags": "Flags, see TL conditional fields", - "from_id": "", + "from_id": "Sender of the story.", "fwd_from": "For reposted stories \u00bb, contains info about the original story.", "id": "ID of the story.", "media": "Story media.", @@ -6752,11 +7278,11 @@ } }, "Timezone": { - "desc": "{schema}", + "desc": "Timezone information.", "params": { - "id": "", - "name": "", - "utc_offset": "" + "id": "Unique timezone ID.", + "name": "Human-readable and localized timezone name.", + "utc_offset": "UTC offset in seconds, which may be displayed in hh:mm format by the client together with the human-readable name (i.e. $name UTC -01:00)." } }, "TopPeer": { @@ -6766,6 +7292,10 @@ "rating": "Rating as computed in top peer rating \u00bb" } }, + "TopPeerCategoryBotsApp": { + "desc": "Most frequently used Main Mini Bot Apps.", + "params": {} + }, "TopPeerCategoryBotsInline": { "desc": "Most used inline bots", "params": {} @@ -6815,10 +7345,10 @@ "params": {} }, "UpdateBotBusinessConnect": { - "desc": "{schema}", + "desc": "Connecting or disconnecting a business bot or changing the connection settings will emit an updateBotBusinessConnect update to the bot, with the new settings and a connection_id that will be used by the bot to handle updates from and send messages as the user.", "params": { - "connection": "", - "qts": "" + "connection": "Business connection settings", + "qts": "New qts value, see updates \u00bb for more info." } }, "UpdateBotCallbackQuery": { @@ -6835,7 +7365,7 @@ } }, "UpdateBotChatBoost": { - "desc": "A channel boost has changed (bots only)", + "desc": "A channel/supergroup boost has changed (bots only)", "params": { "boost": "New boost information", "peer": "Channel", @@ -6862,22 +7392,22 @@ } }, "UpdateBotDeleteBusinessMessage": { - "desc": "{schema}", + "desc": "A message was deleted in a connected business chat \u00bb.", "params": { - "connection_id": "", - "messages": "", - "peer": "", - "qts": "" + "connection_id": "Business connection ID.", + "messages": "IDs of the messages that were deleted.", + "peer": "Peer where the messages were deleted.", + "qts": "New qts value, see updates \u00bb for more info." } }, "UpdateBotEditBusinessMessage": { - "desc": "{schema}", + "desc": "A message was edited in a connected business chat \u00bb.", "params": { - "connection_id": "", + "connection_id": "Business connection ID", "flags": "Flags, see TL conditional fields", - "message": "", - "qts": "", - "reply_to_message": "" + "message": "New message.", + "qts": "New qts value, see updates \u00bb for more info.", + "reply_to_message": "The message that message is replying to." } }, "UpdateBotInlineQuery": { @@ -6933,19 +7463,19 @@ } }, "UpdateBotNewBusinessMessage": { - "desc": "{schema}", + "desc": "A message was received via a connected business chat \u00bb.", "params": { - "connection_id": "", + "connection_id": "Connection ID.", "flags": "Flags, see TL conditional fields", - "message": "", - "qts": "", - "reply_to_message": "" + "message": "New message.", + "qts": "New qts value, see updates \u00bb for more info.", + "reply_to_message": "The message that message is replying to." } }, "UpdateBotPrecheckoutQuery": { "desc": "This object contains information about an incoming pre-checkout query.", "params": { - "currency": "Three-letter ISO 4217 currency code", + "currency": "Three-letter ISO 4217 currency code, or XTR for Telegram Stars.", "flags": "Flags, see TL conditional fields", "info": "Order info provided by the user", "payload": "Bot specified invoice payload", @@ -6955,6 +7485,14 @@ "user_id": "User who sent the query" } }, + "UpdateBotPurchasedPaidMedia": { + "desc": "Bots only: a user has purchased a paid media.", + "params": { + "payload": "Payload passed by the bot in inputMediaPaidMedia.payload", + "qts": "New qts value, see updates \u00bb for more info.", + "user_id": "The user that bought the media" + } + }, "UpdateBotShippingQuery": { "desc": "This object contains information about an incoming shipping query.", "params": { @@ -6988,14 +7526,27 @@ } }, "UpdateBroadcastRevenueTransactions": { - "desc": "{schema}", + "desc": "A new channel ad revenue transaction was made, see here \u00bb for more info.", "params": { - "balances": "", - "peer": "" + "balances": "New ad revenue balance.", + "peer": "Channel" + } + }, + "UpdateBusinessBotCallbackQuery": { + "desc": "A callback button sent via a business connection was pressed, and the button data was sent to the bot that created the button.", + "params": { + "chat_instance": "Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in games.", + "connection_id": "Business connection ID", + "data": "Callback data", + "flags": "Flags, see TL conditional fields", + "message": "Message that contains the keyboard (also contains info about the chat where the message was sent).", + "query_id": "Query ID", + "reply_to_message": "The message that message is replying to.", + "user_id": "ID of the user that pressed the button" } }, "UpdateChannel": { - "desc": "A new channel or supergroup is available, or info about an existing channel has changed and must be refeteched.", + "desc": "Channel/supergroup (channel and/or channelFull) information was updated.", "params": { "channel_id": "Channel ID" } @@ -7099,7 +7650,7 @@ } }, "UpdateChat": { - "desc": "A new chat is available", + "desc": "Chat (chat and/or chatFull) information was updated.", "params": { "chat_id": "Chat ID" } @@ -7168,7 +7719,7 @@ } }, "UpdateConfig": { - "desc": "The server-side configuration has changed; the client should re-fetch the config using help.getConfig", + "desc": "The server-side configuration has changed; the client should re-fetch the config using help.getConfig and help.getAppConfig.", "params": {} }, "UpdateContactsReset": { @@ -7199,23 +7750,25 @@ } }, "UpdateDeleteQuickReply": { - "desc": "{schema}", + "desc": "A quick reply shortcut \u00bb was deleted. This will not emit updateDeleteQuickReplyMessages updates, even if all the messages in the shortcut are also deleted by this update.", "params": { - "shortcut_id": "" + "shortcut_id": "ID of the quick reply shortcut that was deleted." } }, "UpdateDeleteQuickReplyMessages": { - "desc": "{schema}", + "desc": "One or more messages in a quick reply shortcut \u00bb were deleted.", "params": { - "messages": "", - "shortcut_id": "" + "messages": "IDs of the deleted messages.", + "shortcut_id": "Quick reply shortcut ID." } }, "UpdateDeleteScheduledMessages": { - "desc": "Some scheduled messages were deleted from the schedule queue of a chat", + "desc": "Some scheduled messages were deleted (or sent) from the schedule queue of a chat", "params": { + "flags": "Flags, see TL conditional fields", "messages": "Deleted scheduled messages", - "peer": "Peer" + "peer": "Peer", + "sent_messages": "If set, this update indicates that some scheduled messages were sent (not simply deleted from the schedule queue). In this case, the messages field will contain the scheduled message IDs for the sent messages (initially returned in updateNewScheduledMessage), and sent_messages will contain the real message IDs for the sent messages." } }, "UpdateDialogFilter": { @@ -7370,11 +7923,11 @@ "params": {} }, "UpdateMessageExtendedMedia": { - "desc": "Extended media update", + "desc": "You bought a paid media \u00bb: this update contains the revealed media.", "params": { - "extended_media": "Extended media", - "msg_id": "Message ID", - "peer": "Peer" + "extended_media": "Revealed media, contains only messageExtendedMedia constructors.", + "msg_id": "ID of the message containing the paid media", + "peer": "Peer where the paid media was posted" } }, "UpdateMessageID": { @@ -7427,7 +7980,7 @@ "date": "Authorization date", "device": "Name of device, for example Android", "flags": "Flags, see TL conditional fields", - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "location": "Location, for example USA, NY (IP=1.2.3.4)", "unconfirmed": "Whether the session is unconfirmed, see here \u00bb for more info." } @@ -7456,9 +8009,9 @@ } }, "UpdateNewQuickReply": { - "desc": "{schema}", + "desc": "A new quick reply shortcut \u00bb was created.", "params": { - "quick_reply": "" + "quick_reply": "Quick reply shortcut." } }, "UpdateNewScheduledMessage": { @@ -7474,11 +8027,11 @@ } }, "UpdateNewStoryReaction": { - "desc": "{schema}", + "desc": "Represents a new reaction to a story.", "params": { - "peer": "", - "reaction": "", - "story_id": "" + "peer": "The peer where the story was posted.", + "reaction": "The reaction.", + "story_id": "Story ID." } }, "UpdateNotifySettings": { @@ -7488,6 +8041,12 @@ "peer": "Notification source" } }, + "UpdatePaidReactionPrivacy": { + "desc": "Contains the current default paid reaction privacy, see here \u00bb for more info.", + "params": { + "private": "Whether paid reaction privacy is enabled or disabled." + } + }, "UpdatePeerBlocked": { "desc": "We blocked a peer, see here \u00bb for more info on blocklists.", "params": { @@ -7597,15 +8156,15 @@ "params": {} }, "UpdateQuickReplies": { - "desc": "{schema}", + "desc": "Info about or the order of quick reply shortcuts \u00bb was changed.", "params": { - "quick_replies": "" + "quick_replies": "New quick reply shortcut order and information." } }, "UpdateQuickReplyMessage": { - "desc": "{schema}", + "desc": "A new message was added to a quick reply shortcut \u00bb.", "params": { - "message": "" + "message": "The message that was added (the message.quick_reply_shortcut_id field will contain the shortcut ID)." } }, "UpdateReadChannelDiscussionInbox": { @@ -7716,13 +8275,17 @@ "params": {} }, "UpdateSavedReactionTags": { - "desc": "{schema}", + "desc": "The list of reaction tag \u00bb names assigned by the user has changed and should be refetched using messages.getSavedReactionTags \u00bb.", "params": {} }, "UpdateSavedRingtones": { "desc": "The list of saved notification sounds has changed, use account.getSavedRingtones to fetch the new list.", "params": {} }, + "UpdateSentPhoneCode": { + "desc": "", + "params": {} + }, "UpdateSentStoryReaction": { "desc": "Indicates we reacted to a story \u00bb.", "params": { @@ -7809,15 +8372,22 @@ } }, "UpdateSmsJob": { - "desc": "{schema}", + "desc": "A new SMS job was received", "params": { - "job_id": "" + "job_id": "SMS job ID" } }, "UpdateStarsBalance": { - "desc": "{schema}", + "desc": "The current account's Telegram Stars balance \u00bb has changed.", + "params": { + "balance": "New balance." + } + }, + "UpdateStarsRevenueStatus": { + "desc": "The Telegram Star balance of a channel/bot we own has changed \u00bb.", "params": { - "balance": "" + "peer": "Channel/bot", + "status": "New Telegram Star balance." } }, "UpdateStickerSets": { @@ -7875,7 +8445,7 @@ } }, "UpdateUser": { - "desc": "User information was updated, it must be refetched using users.getFullUser.", + "desc": "User (user and/or userFull) information was updated.", "params": { "user_id": "User ID" } @@ -7976,50 +8546,52 @@ } }, "User": { - "desc": "Indicates info about a certain user", + "desc": "Indicates info about a certain user.", "params": { - "access_hash": "Access hash of the user", - "apply_min_photo": "If set, the profile picture for this user should be refetched", - "attach_menu_enabled": "Whether we installed the attachment menu web app offered by this bot", - "bot": "Is this user a bot?", + "access_hash": "Access hash of the user, see here \u00bb for more info. If this flag is set, when updating the local peer database, generate a virtual flag called min_access_hash, which is: - Set to true if min is set AND -- The phone flag is not set OR -- The phone flag is set and the associated phone number string is non-empty - Set to false otherwise. Then, apply both access_hash and min_access_hash to the local database if: - min_access_hash is false OR - min_access_hash is true AND -- There is no locally cached object for this user OR -- There is no access_hash in the local cache OR -- The cached object's min_access_hash is also true If the final merged object stored to the database has the min_access_hash field set to true, the related access_hash is only suitable to use in inputPeerPhotoFileLocation \u00bb, to directly download the profile pictures of users, everywhere else a inputPeer*FromMessage constructor will have to be generated as specified here \u00bb. Bots can also use min access hashes in some conditions, by passing 0 instead of the min access hash.", + "apply_min_photo": "If set and min is set, the value of photo can be used to update the local database, see the documentation of that flag for more info.", + "attach_menu_enabled": "Whether we installed the attachment menu web app offered by this bot. When updating the local peer database, do not apply changes to this field if the min flag is set.", + "bot": "Is this user a bot? Changes to this flag should invalidate the local userFull cache for this user ID, see here \u00bb for more info.", + "bot_active_users": "Monthly Active Users (MAU) of this bot (may be absent for small bots).", "bot_attach_menu": "Whether this bot offers an attachment menu web app", - "bot_business": "", - "bot_can_edit": "Whether we can edit the profile picture, name, about text and description of this bot because we own it.", + "bot_business": "Whether this bot can be connected to a user as specified here \u00bb.", + "bot_can_edit": "Whether we can edit the profile picture, name, about text and description of this bot because we own it. When updating the local peer database, do not apply changes to this field if the min flag is set. Changes to this flag (if min is not set) should invalidate the local userFull cache for this user ID.", "bot_chat_history": "Can the bot see all messages in groups?", - "bot_info_version": "Version of the bot_info field in userFull, incremented every time it changes", + "bot_has_main_app": "If set, this bot has configured a Main Mini App \u00bb.", + "bot_info_version": "Version of the bot_info field in userFull, incremented every time it changes. Changes to this flag should invalidate the local userFull cache for this user ID, see here \u00bb for more info.", "bot_inline_geo": "Whether the bot can request our geolocation in inline mode", "bot_inline_placeholder": "Inline placeholder for this inline bot", "bot_nochats": "Can the bot be added to groups?", - "close_friend": "Whether we marked this user as a close friend, see here \u00bb for more info", + "close_friend": "Whether we marked this user as a close friend, see here \u00bb for more info. When updating the local peer database, do not apply changes to this field if the min flag is set.", "color": "The user's accent color.", - "contact": "Whether this user is a contact", - "contact_require_premium": "", - "deleted": "Whether the account of this user was deleted", + "contact": "Whether this user is a contact When updating the local peer database, do not apply changes to this field if the min flag is set.", + "contact_require_premium": "If set, we can only write to this user if they have already sent some messages to us, if we are subscribed to Telegram Premium, or if they're a mutual contact (user.mutual_contact). All the secondary conditions listed above must be checked separately to verify whether we can still write to the user, even if this flag is set (i.e. a mutual contact will have this flag set even if we can still write to them, and so on...); to avoid doing these extra checks if we haven't yet cached all the required information (for example while displaying the chat list in the sharing UI) the users.getIsPremiumRequiredToContact method may be invoked instead, passing the list of users currently visible in the UI, returning a list of booleans that directly specify whether we can or cannot write to each user; alternatively, the userFull.contact_require_premium flag contains the same (fully checked, i.e. it's not just a copy of this flag) info returned by users.getIsPremiumRequiredToContact. To set this flag for ourselves invoke account.setGlobalPrivacySettings, setting the settings.new_noncontact_peers_require_premium flag.", + "deleted": "Whether the account of this user was deleted. Changes to this flag should invalidate the local userFull cache for this user ID, see here \u00bb for more info.", "emoji_status": "Emoji status", "fake": "If set, this user was reported by many users as a fake or scam user: be careful when interacting with them.", - "first_name": "First name", + "first_name": "First name. When updating the local peer database, apply changes to this field only if: - The min flag is not set OR - The min flag is set AND -- The min flag of the locally cached user entry is set.", "flags": "Flags, see TL conditional fields", "flags2": "Flags, see TL conditional fields", - "id": "ID of the user", + "id": "ID of the user, see here \u00bb for more info.", "lang_code": "Language code of the user", - "last_name": "Last name", + "last_name": "Last name. When updating the local peer database, apply changes to this field only if: - The min flag is not set OR - The min flag is set AND -- The min flag of the locally cached user entry is set.", "min": "See min", - "mutual_contact": "Whether this user is a mutual contact", - "phone": "Phone number", - "photo": "Profile picture of user", - "premium": "Whether this user is a Telegram Premium user", + "mutual_contact": "Whether this user is a mutual contact. When updating the local peer database, do not apply changes to this field if the min flag is set.", + "phone": "Phone number. When updating the local peer database, apply changes to this field only if: - The min flag is not set OR - The min flag is set AND -- The min flag of the locally cached user entry is set.", + "photo": "Profile picture of user. When updating the local peer database, apply changes to this field only if: - The min flag is not set OR - The min flag is set AND -- The apply_min_photo flag is set OR -- The min flag of the locally cached user entry is set.", + "premium": "Whether this user is a Telegram Premium user Changes to this flag should invalidate the local userFull cache for this user ID, see here \u00bb for more info. Changes to this flag if the self flag is set should also trigger the following calls, to refresh the respective caches: - The help.getConfig cache - The messages.getTopReactions cache if the bot flag is not set", "profile_color": "The user's profile color.", "restricted": "Access to this user must be restricted for the reason specified in restriction_reason", "restriction_reason": "Contains the reason why access to this user must be restricted.", "scam": "This may be a scam user", "self": "Whether this user indicates the currently logged in user", - "status": "Online status of user", - "stories_hidden": "Whether we have hidden \u00bb all active stories of this user.", - "stories_max_id": "ID of the maximum read story.", + "status": "Online status of user. When updating the local peer database, apply changes to this field only if: - The min flag is not set OR - The min flag is set AND -- The min flag of the locally cached user entry is set OR -- The locally cached user entry is equal to userStatusEmpty.", + "stories_hidden": "Whether we have hidden \u00bb all active stories of this user. When updating the local peer database, do not apply changes to this field if the min flag is set.", + "stories_max_id": "ID of the maximum read story. When updating the local peer database, do not apply changes to this field if the min flag of the incoming constructor is set.", "stories_unavailable": "No stories from this user are visible.", "support": "Whether this is an official support user", - "username": "Username", - "usernames": "Additional usernames", + "username": "Main active username. When updating the local peer database, apply changes to this field only if: - The min flag is not set OR - The min flag is set AND -- The min flag of the locally cached user entry is set. Changes to this flag should invalidate the local userFull cache for this user ID if the above conditions are respected and the bot_can_edit flag is also set.", + "usernames": "Additional usernames. When updating the local peer database, apply changes to this field only if: - The min flag is not set OR - The min flag is set AND -- The min flag of the locally cached user entry is set. Changes to this flag (if the above conditions are respected) should invalidate the local userFull cache for this user ID.", "verified": "Whether this user is verified" } }, @@ -8033,29 +8605,31 @@ "desc": "Extended user info", "params": { "about": "Bio of the user", - "birthday": "", + "birthday": "Contains info about the user's birthday \u00bb.", "blocked": "Whether you have blocked this user", "blocked_my_stories_from": "Whether we've blocked this user, preventing them from seeing our stories \u00bb.", "bot_broadcast_admin_rights": "A suggested set of administrator rights for the bot, to be shown when adding the bot as admin to a channel, see here for more info on how to handle them \u00bb.", + "bot_can_manage_emoji_status": "If set, this is a bot that can change our emoji status \u00bb", "bot_group_admin_rights": "A suggested set of administrator rights for the bot, to be shown when adding the bot as admin to a group, see here for more info on how to handle them \u00bb.", "bot_info": "For bots, info about the bot (bot commands, etc)", - "business_away_message": "", - "business_greeting_message": "", - "business_intro": "", - "business_location": "", - "business_work_hours": "", + "business_away_message": "Telegram Business away message \u00bb.", + "business_greeting_message": "Telegram Business greeting message \u00bb.", + "business_intro": "Specifies a custom Telegram Business profile introduction \u00bb.", + "business_location": "Telegram Business location \u00bb.", + "business_work_hours": "Telegram Business working hours \u00bb.", "can_pin_message": "Whether you can pin messages in the chat with this user, you can do this only for a chat with yourself", + "can_view_revenue": "If set, this user can view ad revenue statistics \u00bb for this bot.", "common_chats_count": "Chats in common with this user", - "contact_require_premium": "", + "contact_require_premium": "If set, we cannot write to this user: subscribe to Telegram Premium to get permission to write to this user. To set this flag for ourselves invoke account.setGlobalPrivacySettings, setting the settings.new_noncontact_peers_require_premium flag, see here \u00bb for more info.", "fallback_photo": "Fallback profile photo, displayed if no photo is present in profile_photo or personal_photo, due to privacy settings.", "flags": "Flags, see TL conditional fields", - "flags2": "", + "flags2": "Flags, see TL conditional fields", "folder_id": "Peer folder ID, for more info click here", "has_scheduled": "Whether scheduled messages are available", "id": "User ID", "notify_settings": "Notification settings", - "personal_channel_id": "", - "personal_channel_message": "", + "personal_channel_id": "ID of the associated personal channel \u00bb, that should be shown in the profile page.", + "personal_channel_message": "ID of the latest message of the associated personal channel \u00bb, that should be previewed in the profile page.", "personal_photo": "Personal profile photo, to be shown instead of profile_photo.", "phone_calls_available": "Whether this user can make VoIP calls", "phone_calls_private": "Whether this user's privacy settings allow you to call them", @@ -8063,9 +8637,11 @@ "premium_gifts": "Telegram Premium subscriptions gift options", "private_forward_name": "Anonymized text to be shown instead of the user's name on forwarded messages", "profile_photo": "Profile photo", - "read_dates_private": "", + "read_dates_private": "If set, we cannot fetch the exact read date of messages we send to this user using messages.getOutboxReadDate. The exact read date of messages might still be unavailable for other reasons, see here \u00bb for more info. To set this flag for ourselves invoke account.setGlobalPrivacySettings, setting the settings.hide_read_marks flag.", "settings": "Peer settings", - "sponsored_enabled": "", + "sponsored_enabled": "Whether ads were re-enabled for the current account (only accessible to the currently logged-in user), see here \u00bb for more info.", + "stargifts_count": "Number of gifts the user has chosen to display on their profile", + "starref_program": "This bot has an active referral program \u00bb", "stories": "Active stories \u00bb", "stories_pinned_available": "Whether this user has some pinned stories.", "theme_emoticon": "Emoji associated with chat theme", @@ -8099,14 +8675,14 @@ "UserStatusLastMonth": { "desc": "Online status: last seen last month", "params": { - "by_me": "", + "by_me": "If set, the exact user status of this user is actually available to us, but to view it we must first purchase a Premium subscription, or allow this user to see our exact last online status. See here \u00bb for more info.", "flags": "Flags, see TL conditional fields" } }, "UserStatusLastWeek": { "desc": "Online status: last seen last week", "params": { - "by_me": "", + "by_me": "If set, the exact user status of this user is actually available to us, but to view it we must first purchase a Premium subscription, or allow this user to see our exact last online status. See here \u00bb for more info.", "flags": "Flags, see TL conditional fields" } }, @@ -8125,7 +8701,7 @@ "UserStatusRecently": { "desc": "Online status: last seen recently", "params": { - "by_me": "", + "by_me": "If set, the exact user status of this user is actually available to us, but to view it we must first purchase a Premium subscription, or allow this user to see our exact last online status. See here \u00bb for more info.", "flags": "Flags, see TL conditional fields" } }, @@ -8253,22 +8829,22 @@ "embed_width": "Width of the embedded preview", "flags": "Flags, see TL conditional fields", "has_large_media": "Whether the size of the media in the preview can be changed.", - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "id": "Preview ID", "photo": "Image representing the content", "site_name": "Short name of the site (e.g., Google Docs, App Store)", "title": "Title of the content", - "type": "Type of the web page. Can be: article, photo, audio, video, document, profile, app, or something else", + "type": "Type of the web page. One of the following: - video- gif- photo- document- profile- telegram_background- telegram_theme- telegram_story- telegram_channel- telegram_channel_request- telegram_megagroup- telegram_chat- telegram_megagroup_request- telegram_chat_request- telegram_album- telegram_message- telegram_bot- telegram_voicechat- telegram_livestream- telegram_user- telegram_botapp- telegram_channel_boost- telegram_group_boost- telegram_giftcode- telegram_stickerset", "url": "URL of previewed webpage" } }, "WebPageAttributeStickerSet": { - "desc": "{schema}", + "desc": "Contains info about a stickerset \u00bb, for a webPage preview of a stickerset deep link \u00bb (the webPage will have a type of telegram_stickerset).", "params": { - "emojis": "", + "emojis": "Whether this i s a custom emoji stickerset.", "flags": "Flags, see TL conditional fields", - "stickers": "", - "text_color": "" + "stickers": "A subset of the stickerset in the stickerset.", + "text_color": "Whether the color of this TGS custom emoji stickerset should be changed to the text color when used in messages, the accent color if used as emoji status, white on chat photos, or another appropriate color based on context." } }, "WebPageAttributeStory": { @@ -8288,6 +8864,10 @@ "settings": "Theme settings" } }, + "WebPageAttributeUniqueStarGift": { + "desc": "", + "params": {} + }, "WebPageEmpty": { "desc": "No preview is available for the webpage", "params": { @@ -8322,7 +8902,10 @@ "WebViewResultUrl": { "desc": "Contains the webview URL with appropriate theme and user info parameters added", "params": { - "query_id": "Webview session ID", + "flags": "Flags, see TL conditional fields", + "fullscreen": "If set, the app must be opened in fullscreen", + "fullsize": "If set, the app must be opened in fullsize mode instead of compact mode.", + "query_id": "Webview session ID (only returned by inline button mini apps, menu button mini apps, attachment menu mini apps).", "url": "Webview URL to open" } }, @@ -8364,18 +8947,18 @@ } }, "account.BusinessChatLinks": { - "desc": "{schema}", + "desc": "Contains info about business chat deep links \u00bb created by the current account.", "params": { - "chats": "", - "links": "", - "users": "" + "chats": "Mentioned chats", + "links": "Links", + "users": "Mentioned users" } }, "account.ConnectedBots": { - "desc": "{schema}", + "desc": "Info about currently connected business bots.", "params": { - "connected_bots": "", - "users": "" + "connected_bots": "Info about the connected bots", + "users": "Bot information" } }, "account.ContentSettings": { @@ -8402,7 +8985,7 @@ "account.EmojiStatuses": { "desc": "A list of emoji statuses", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "statuses": "Emoji statuses" } }, @@ -8410,6 +8993,10 @@ "desc": "The server-side list of emoji statuses hasn't changed", "params": {} }, + "account.PaidMessagesRevenue": { + "desc": "", + "params": {} + }, "account.Password": { "desc": "Configuration for two-factor authorization", "params": { @@ -8473,14 +9060,14 @@ } }, "account.ResolvedBusinessChatLinks": { - "desc": "{schema}", + "desc": "Contains info about a single resolved business chat deep link \u00bb.", "params": { - "chats": "", + "chats": "Mentioned chats", "entities": "Message entities for styled text", "flags": "Flags, see TL conditional fields", - "message": "", - "peer": "", - "users": "" + "message": "Message to pre-fill in the message input field.", + "peer": "Destination peer", + "users": "Mentioned users" } }, "account.SavedRingtone": { @@ -8496,7 +9083,7 @@ "account.SavedRingtones": { "desc": "A list of saved notification sounds", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "ringtones": "Saved notification sounds" } }, @@ -8520,7 +9107,7 @@ "account.Themes": { "desc": "Installed themes", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "themes": "Themes" } }, @@ -8538,7 +9125,7 @@ "account.WallPapers": { "desc": "Installed wallpapers", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "wallpapers": "Wallpapers" } }, @@ -8641,6 +9228,10 @@ "type": "Phone code type" } }, + "auth.SentCodePaymentRequired": { + "desc": "", + "params": {} + }, "auth.SentCodeSuccess": { "desc": "The user successfully authorized using future auth tokens", "params": { @@ -8677,7 +9268,8 @@ "flags": "Flags, see TL conditional fields", "length": "Length of the code that will be delivered.", "nonce": "On Android, the nonce to be used as described in the auth documentation \u00bb", - "play_integrity_nonce": "", + "play_integrity_nonce": "Play Integrity API nonce", + "play_integrity_project_id": "Google Play Integrity project ID", "push_timeout": "On iOS: if a push notification with the ios_push_secret isn't received within push_timeout seconds, the next_type authentication method must be used, with auth.resendCode.", "receipt": "On iOS, must be compared with the receipt extracted from the received push notification." } @@ -8717,16 +9309,16 @@ } }, "auth.SentCodeTypeSmsPhrase": { - "desc": "{schema}", + "desc": "The code was sent via SMS as a secret phrase starting with the word specified in beginning", "params": { - "beginning": "", + "beginning": "If set, the secret phrase (and the SMS) starts with this word.", "flags": "Flags, see TL conditional fields" } }, "auth.SentCodeTypeSmsWord": { - "desc": "{schema}", + "desc": "The code was sent via SMS as a secret word, starting with the letter specified in beginning", "params": { - "beginning": "", + "beginning": "If set, the secret word in the sent SMS (which may contain multiple words) starts with this letter.", "flags": "Flags, see TL conditional fields" } }, @@ -8738,6 +9330,21 @@ "name": "Bot name" } }, + "bots.PopularAppBots": { + "desc": "Popular Main Mini Apps, to be used in the apps tab of global search \u00bb.", + "params": { + "flags": "Flags, see TL conditional fields", + "next_offset": "Offset for pagination.", + "users": "The bots associated to each Main Mini App, see here \u00bb for more info." + } + }, + "bots.PreviewInfo": { + "desc": "Contains info about Main Mini App previews, see here \u00bb for more info.", + "params": { + "lang_codes": "All available language codes for which preview medias were uploaded (regardless of the language code passed to bots.getPreviewInfo).", + "media": "All preview medias for the language code passed to bots.getPreviewInfo." + } + }, "channels.AdminLogResults": { "desc": "Admin log events", "params": { @@ -8776,18 +9383,18 @@ } }, "channels.SponsoredMessageReportResultAdsHidden": { - "desc": "{schema}", + "desc": "Sponsored messages were hidden for the user in all chats.", "params": {} }, "channels.SponsoredMessageReportResultChooseOption": { - "desc": "{schema}", + "desc": "The user must choose a report option from the localized options available in options, and after selection, channels.reportSponsoredMessage must be invoked again, passing the option's option field to the option param of the method.", "params": { - "options": "", - "title": "" + "options": "Localized list of options.", + "title": "Title of the option selection popup." } }, "channels.SponsoredMessageReportResultReported": { - "desc": "{schema}", + "desc": "The sponsored message was reported successfully.", "params": {} }, "chatlists.ChatlistInvite": { @@ -8852,10 +9459,10 @@ } }, "contacts.ContactBirthdays": { - "desc": "{schema}", + "desc": "Birthday information of our contacts.", "params": { - "contacts": "", - "users": "" + "contacts": "Birthday info", + "users": "User information" } }, "contacts.Contacts": { @@ -8896,6 +9503,14 @@ "users": "Users" } }, + "contacts.SponsoredPeers": { + "desc": "", + "params": {} + }, + "contacts.SponsoredPeersEmpty": { + "desc": "", + "params": {} + }, "contacts.TopPeers": { "desc": "Top peers", "params": { @@ -8913,21 +9528,21 @@ "params": {} }, "fragment.CollectibleInfo": { - "desc": "{schema}", + "desc": "Info about a fragment collectible.", "params": { - "amount": "", - "crypto_amount": "", - "crypto_currency": "", - "currency": "", - "purchase_date": "", - "url": "" + "amount": "Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).", + "crypto_amount": "Price, in the smallest units of the cryptocurrency.", + "crypto_currency": "Cryptocurrency name.", + "currency": "Three-letter ISO 4217 currency code for amount", + "purchase_date": "Purchase date (unixtime)", + "url": "Fragment URL with more info about the collectible" } }, "help.AppConfig": { "desc": "Contains various client configuration parameters", "params": { "config": "Client configuration parameters", - "hash": "Hash for pagination, for more info click here" + "hash": "Hash used for caching, for more info click here" } }, "help.AppConfigNotModified": { @@ -8952,7 +9567,7 @@ "desc": "Name, ISO code, localized name and phone codes/patterns of all available countries", "params": { "countries": "Name, ISO code, localized name and phone codes/patterns of all available countries", - "hash": "Hash for pagination, for more info click here" + "hash": "Hash used for caching, for more info click here" } }, "help.CountriesListNotModified": { @@ -9006,7 +9621,7 @@ "desc": "Telegram passport configuration", "params": { "countries_langs": "Localization", - "hash": "Hash for pagination, for more info click here" + "hash": "Hash used for caching, for more info click here" } }, "help.PassportConfigNotModified": { @@ -9018,10 +9633,10 @@ "params": { "channel_min_level": "Channels can use this palette only after reaching at least the boost level specified in this field.", "color_id": "Palette ID.", - "colors": "Light mode palette. Will be empty for IDs 0 to 6 inclusive, in which case a palette containing a single color from the following colors should be used: red, orange, violet, green, cyan, blue, pink for indexes 0 to 6.", + "colors": "Light mode palette. Will be empty for IDs 0 to 6 inclusive, in which case a palette containing a single color from the following colors should be used: red, orange, violet, green, cyan, blue, pink for indexes 0 to 6 (i.e. the same colors used for randomized fallback message accent colors).", "dark_colors": "Dark mode palette. Optional, defaults to the palette in colors (or the autogenerated palette for IDs 0 to 6) if absent.", "flags": "Flags, see TL conditional fields", - "group_min_level": "", + "group_min_level": "Supergroups can use this palette only after reaching at least the boost level specified in this field.", "hidden": "Whether this palette should not be displayed as an option to the user when choosing a palette to apply to profile pages or message accents." } }, @@ -9043,7 +9658,7 @@ "desc": "Contains info about multiple color palettes \u00bb.", "params": { "colors": "Usable color palettes.", - "hash": "Hash for pagination, for more info click here" + "hash": "Hash used for caching, for more info click here" } }, "help.PeerColorsNotModified": { @@ -9126,14 +9741,14 @@ } }, "help.TimezonesList": { - "desc": "{schema}", + "desc": "Timezone information that may be used elsewhere in the API, such as to set Telegram Business opening hours \u00bb.", "params": { - "hash": "Hash for pagination, for more info click here", - "timezones": "" + "hash": "Hash used for caching, for more info click here", + "timezones": "Timezones" } }, "help.TimezonesListNotModified": { - "desc": "{schema}", + "desc": "The timezone list has not changed.", "params": {} }, "help.UserInfo": { @@ -9176,7 +9791,7 @@ "messages.AllStickers": { "desc": "Info about all installed stickers", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "sets": "All stickersets" } }, @@ -9192,21 +9807,21 @@ } }, "messages.AvailableEffects": { - "desc": "{schema}", + "desc": "The full list of usable animated message effects \u00bb.", "params": { - "documents": "", - "effects": "", - "hash": "Hash for pagination, for more info click here" + "documents": "Documents specified in the effects constructors.", + "effects": "Message effects", + "hash": "Hash used for caching, for more info click here" } }, "messages.AvailableEffectsNotModified": { - "desc": "{schema}", + "desc": "The full list of usable animated message effects \u00bb hasn't changed.", "params": {} }, "messages.AvailableReactions": { "desc": "Animations and metadata associated with message reactions \u00bb", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "reactions": "Animations and metadata associated with message reactions \u00bb" } }, @@ -9236,6 +9851,13 @@ "url": "URL to open" } }, + "messages.BotPreparedInlineMessage": { + "desc": "Represents a prepared inline message saved by a bot, to be sent to the user via a web app \u00bb", + "params": { + "expire_date": "Expiration date of the message", + "id": "The ID of the saved message, to be passed to the id field of the web_app_send_prepared_message event \u00bb" + } + }, "messages.BotResults": { "desc": "Result of a query to an inline bot", "params": { @@ -9322,11 +9944,11 @@ } }, "messages.DialogFilters": { - "desc": "{schema}", + "desc": "Folder and folder tags information", "params": { - "filters": "", + "filters": "Folders.", "flags": "Flags, see TL conditional fields", - "tags_enabled": "" + "tags_enabled": "Whether folder tags are enabled." } }, "messages.Dialogs": { @@ -9371,7 +9993,7 @@ "desc": "Represents a list of emoji categories.", "params": { "groups": "A list of emoji categories.", - "hash": "Hash for pagination, for more info click here" + "hash": "Hash used for caching, for more info click here" } }, "messages.EmojiGroupsNotModified": { @@ -9404,7 +10026,7 @@ "messages.FavedStickers": { "desc": "Favorited stickers", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "packs": "Emojis associated to stickers", "stickers": "Favorited stickers" } @@ -9418,7 +10040,7 @@ "params": { "count": "Total number of featured stickers", "flags": "Flags, see TL conditional fields", - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "premium": "Whether this is a premium stickerset", "sets": "Featured stickersets", "unread": "IDs of new featured stickersets" @@ -9446,7 +10068,7 @@ "messages.FoundStickerSets": { "desc": "Found stickersets", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "sets": "Found stickersets" } }, @@ -9454,6 +10076,22 @@ "desc": "No further results were found", "params": {} }, + "messages.FoundStickers": { + "desc": "Found stickers", + "params": { + "flags": "Flags, see TL conditional fields", + "hash": "Hash used for caching, for more info click here", + "next_offset": "Offset for pagination", + "stickers": "Found stickers" + } + }, + "messages.FoundStickersNotModified": { + "desc": "No new stickers were found for the specified query", + "params": { + "flags": "Flags, see TL conditional fields", + "next_offset": "Offset for pagination" + } + }, "messages.HighScores": { "desc": "Highscores in a game", "params": { @@ -9485,10 +10123,10 @@ } }, "messages.InvitedUsers": { - "desc": "{schema}", + "desc": "Contains info about successfully or unsuccessfully invited \u00bb users.", "params": { - "missing_invitees": "", - "updates": "" + "missing_invitees": "A list of users that could not be invited, along with the reason why they couldn't be invited.", + "updates": "List of updates about successfully invited users (and eventually info about the created group)" } }, "messages.MessageEditData": { @@ -9545,10 +10183,10 @@ } }, "messages.MyStickers": { - "desc": "{schema}", + "desc": "The list of stickersets owned by the current account \u00bb.", "params": { - "count": "", - "sets": "" + "count": "Total number of owned stickersets.", + "sets": "Stickersets" } }, "messages.PeerDialogs": { @@ -9569,23 +10207,33 @@ "users": "Mentioned users" } }, + "messages.PreparedInlineMessage": { + "desc": "Represents a prepared inline message received via a bot's mini app, that can be sent to some chats \u00bb", + "params": { + "cache_time": "Caching validity of the results", + "peer_types": "Types of chats where this message can be sent", + "query_id": "The query_id to pass to messages.sendInlineBotResult", + "result": "The contents of the message, to be shown in a preview", + "users": "Users mentioned in the results" + } + }, "messages.QuickReplies": { - "desc": "{schema}", + "desc": "Info about quick reply shortcuts \u00bb.", "params": { - "chats": "", - "messages": "", - "quick_replies": "", - "users": "" + "chats": "Mentioned chats", + "messages": "Messages mentioned in quick_replies.", + "quick_replies": "Quick reply shortcuts.", + "users": "Mentioned users" } }, "messages.QuickRepliesNotModified": { - "desc": "{schema}", + "desc": "Info about quick reply shortcuts \u00bb hasn't changed.", "params": {} }, "messages.Reactions": { "desc": "List of message reactions", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "reactions": "Reactions" } }, @@ -9597,7 +10245,7 @@ "desc": "Recently used stickers", "params": { "dates": "When was each sticker last used", - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "packs": "Emojis associated to stickers", "stickers": "Recent stickers" } @@ -9635,7 +10283,7 @@ "desc": "Saved gifs", "params": { "gifs": "List of saved gifs", - "hash": "Hash for pagination, for more info click here" + "hash": "Hash used for caching, for more info click here" } }, "messages.SavedGifsNotModified": { @@ -9643,14 +10291,14 @@ "params": {} }, "messages.SavedReactionTags": { - "desc": "{schema}", + "desc": "List of reaction tag \u00bb names assigned by the user.", "params": { - "hash": "Hash for pagination, for more info click here", - "tags": "" + "hash": "Hash used for caching, for more info click here", + "tags": "Saved reaction tags." } }, "messages.SavedReactionTagsNotModified": { - "desc": "{schema}", + "desc": "The list of reaction tag \u00bb names assigned by the user hasn't changed.", "params": {} }, "messages.SearchCounter": { @@ -9737,7 +10385,7 @@ "messages.Stickers": { "desc": "Found stickers", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "stickers": "Stickers" } }, @@ -9781,6 +10429,10 @@ "webpage": "The instant view webpage." } }, + "messages.WebPagePreview": { + "desc": "", + "params": {} + }, "payments.BankCardData": { "desc": "Credit card info, provided by the card's bank(s)", "params": { @@ -9803,6 +10455,14 @@ "via_giveaway": "Whether this giftcode was created by a giveaway." } }, + "payments.ConnectedStarRefBots": { + "desc": "Active affiliations", + "params": { + "connected_bots": "The affiliations", + "count": "Total number of active affiliations", + "users": "Peers mentioned in connected_bots" + } + }, "payments.ExportedInvoice": { "desc": "Exported invoice deep link", "params": { @@ -9829,6 +10489,7 @@ "flags": "Flags, see TL conditional fields", "gift_code_slug": "If we're one of the winners of this giveaway, contains the Premium gift code, see here \u00bb for more info on the full giveaway flow.", "refunded": "Whether the giveaway was canceled and was fully refunded.", + "stars_prize": "If we're one of the winners of this Telegram Star giveaway, the number Telegram Stars we won.", "start_date": "Start date of the giveaway", "winner": "Whether we're one of the winners of this giveaway.", "winners_count": "Number of winners in the giveaway" @@ -9856,17 +10517,24 @@ "users": "Users" } }, + "payments.PaymentFormStarGift": { + "desc": "Represents a payment form for a gift, see here \u00bb for more info.", + "params": { + "form_id": "Form ID.", + "invoice": "Invoice" + } + }, "payments.PaymentFormStars": { - "desc": "{schema}", + "desc": "Represents a payment form, for payments to be using Telegram Stars, see here \u00bb for more info.", "params": { - "bot_id": "", - "description": "", + "bot_id": "Bot ID.", + "description": "Description", "flags": "Flags, see TL conditional fields", - "form_id": "", - "invoice": "", - "photo": "", - "title": "", - "users": "" + "form_id": "Form ID.", + "invoice": "Invoice", + "photo": "Product photo", + "title": "Form title", + "users": "Info about users mentioned in the other fields." } }, "payments.PaymentReceipt": { @@ -9890,19 +10558,19 @@ } }, "payments.PaymentReceiptStars": { - "desc": "{schema}", + "desc": "Receipt for payment made using Telegram Stars.", "params": { - "bot_id": "", - "currency": "", - "date": "", - "description": "", + "bot_id": "Bot ID", + "currency": "Currency, always XTR.", + "date": "Date of generation", + "description": "Description", "flags": "Flags, see TL conditional fields", - "invoice": "", - "photo": "", - "title": "", - "total_amount": "", - "transaction_id": "", - "users": "" + "invoice": "Invoice", + "photo": "Product photo", + "title": "Title", + "total_amount": "Amount of Telegram Stars.", + "transaction_id": "Transaction ID", + "users": "Info about users mentioned in the other fields." } }, "payments.PaymentResult": { @@ -9925,17 +10593,77 @@ "saved_info": "Saved server-side order information" } }, + "payments.SavedStarGifts": { + "desc": "", + "params": {} + }, + "payments.StarGiftUpgradePreview": { + "desc": "", + "params": {} + }, + "payments.StarGiftWithdrawalUrl": { + "desc": "", + "params": {} + }, + "payments.StarGifts": { + "desc": "Available gifts \u00bb.", + "params": { + "gifts": "List of available gifts.", + "hash": "Hash used for caching, for more info click here" + } + }, + "payments.StarGiftsNotModified": { + "desc": "The list of available gifts \u00bb hasn't changed.", + "params": {} + }, + "payments.StarsRevenueAdsAccountUrl": { + "desc": "Contains a URL leading to a page where the user will be able to place ads for the channel/bot, paying using Telegram Stars.", + "params": { + "url": "URL to open." + } + }, + "payments.StarsRevenueStats": { + "desc": "Star revenue statistics, see here \u00bb for more info.", + "params": { + "revenue_graph": "Star revenue graph (number of earned stars)", + "status": "Current balance, current withdrawable balance and overall earned Telegram Stars", + "usd_rate": "Current conversion rate of Telegram Stars to USD" + } + }, + "payments.StarsRevenueWithdrawalUrl": { + "desc": "Contains the URL to use to withdraw Telegram Star revenue.", + "params": { + "url": "Contains the URL to use to withdraw Telegram Star revenue." + } + }, "payments.StarsStatus": { - "desc": "{schema}", + "desc": "Info about the current Telegram Star subscriptions, balance and transaction history \u00bb.", "params": { - "balance": "", - "chats": "", + "balance": "Current Telegram Star balance.", + "chats": "Chats mentioned in history.", "flags": "Flags, see TL conditional fields", - "history": "", - "next_offset": "", - "users": "" + "history": "List of Telegram Star transactions (partial if next_offset is set).", + "next_offset": "Offset to use to fetch more transactions from the transaction history using payments.getStarsTransactions.", + "subscriptions": "Info about current Telegram Star subscriptions, only returned when invoking payments.getStarsTransactions and payments.getStarsSubscriptions.", + "subscriptions_missing_balance": "The number of Telegram Stars the user should buy to be able to extend expired subscriptions soon (i.e. the current balance is not enough to extend all expired subscriptions).", + "subscriptions_next_offset": "Offset for pagination of subscriptions: only usable and returned when invoking payments.getStarsSubscriptions.", + "users": "Users mentioned in history." } }, + "payments.SuggestedStarRefBots": { + "desc": "A list of suggested mini apps with available affiliate programs", + "params": { + "count": "Total number of results (for pagination)", + "flags": "Flags, see TL conditional fields", + "next_offset": "Next offset for pagination", + "suggested_bots": "Suggested affiliate programs (full or partial list to be fetched using pagination)", + "users": "Peers mentioned in suggested_bots" + } + }, + "payments.UniqueStarGift": { + "desc": "", + "params": {} + }, "payments.ValidatedRequestedInfo": { "desc": "Validated user-provided info", "params": { @@ -10038,13 +10766,13 @@ "boosts": "Total number of boosts acquired so far.", "current_level_boosts": "The number of boosts acquired so far in the current level.", "flags": "Flags, see TL conditional fields", - "gift_boosts": "The number of boosts acquired from created Telegram Premium gift codes and giveaways; only returned to channel admins.", - "level": "The current boost level of the channel.", - "my_boost": "Whether we're currently boosting this channel, my_boost_slots will also be set.", + "gift_boosts": "The number of boosts acquired from created Telegram Premium gift codes and giveaways; only returned to channel/supergroup admins.", + "level": "The current boost level of the channel/supergroup.", + "my_boost": "Whether we're currently boosting this channel/supergroup, my_boost_slots will also be set.", "my_boost_slots": "Indicates which of our boost slots we've assigned to this peer (populated if my_boost is set).", "next_level_boosts": "Total number of boosts needed to reach the next level; if absent, the next level isn't available.", - "premium_audience": "Only returned to channel admins: contains the approximated number of Premium users subscribed to the channel, related to the total number of subscribers.", - "prepaid_giveaways": "A list of prepaid giveaways available for the chat; only returned to channel admins." + "premium_audience": "Only returned to channel/supergroup admins: contains the approximated number of Premium users subscribed to the channel/supergroup, related to the total number of subscribers.", + "prepaid_giveaways": "A list of prepaid giveaways available for the chat; only returned to channel/supergroup admins." } }, "premium.MyBoosts": { @@ -10056,46 +10784,46 @@ } }, "smsjobs.EligibleToJoin": { - "desc": "{schema}", + "desc": "SMS jobs eligibility", "params": { - "monthly_sent_sms": "", - "terms_url": "" + "monthly_sent_sms": "Monthly sent SMSes", + "terms_url": "Terms of service URL" } }, "smsjobs.Status": { - "desc": "{schema}", + "desc": "Status", "params": { - "allow_international": "", + "allow_international": "Allow international numbers", "flags": "Flags, see TL conditional fields", - "last_gift_slug": "", - "recent_remains": "", - "recent_sent": "", - "recent_since": "", - "terms_url": "", - "total_sent": "", - "total_since": "" + "last_gift_slug": "Last gift deep link", + "recent_remains": "Remaining", + "recent_sent": "Recently sent", + "recent_since": "Since", + "terms_url": "Terms of service URL", + "total_sent": "Total sent", + "total_since": "Total since" } }, "stats.BroadcastRevenueStats": { - "desc": "{schema}", + "desc": "Channel revenue ad statistics, see here \u00bb for more info.", "params": { - "balances": "", - "revenue_graph": "", - "top_hours_graph": "", - "usd_rate": "" + "balances": "Current balance, current withdrawable balance and overall revenue", + "revenue_graph": "Ad revenue graph (in the smallest unit of the cryptocurrency in which revenue is calculated)", + "top_hours_graph": "Ad impressions graph", + "usd_rate": "Current conversion rate of the cryptocurrency (not in the smallest unit) in which revenue is calculated to USD" } }, "stats.BroadcastRevenueTransactions": { - "desc": "{schema}", + "desc": "Channel ad revenue transactions \u00bb.", "params": { - "count": "", - "transactions": "" + "count": "Total number of transactions.", + "transactions": "Transactions" } }, "stats.BroadcastRevenueWithdrawalUrl": { - "desc": "{schema}", + "desc": "Contains the URL to use to withdraw channel ad revenue.", "params": { - "url": "" + "url": "A unique URL to a Fragment page where the user will be able to specify and submit the address of the TON wallet where the funds will be sent." } }, "stats.BroadcastStats": { @@ -10239,6 +10967,17 @@ "stealth_mode": "Current stealth mode information" } }, + "stories.FoundStories": { + "desc": "Stories found using global story search \u00bb.", + "params": { + "chats": "Mentioned chats", + "count": "Total number of results found for the query.", + "flags": "Flags, see TL conditional fields", + "next_offset": "Offset used to fetch the next page, if not set this is the final page.", + "stories": "Matching stories.", + "users": "Mentioned users" + } + }, "stories.PeerStories": { "desc": "Active story list of a specific peer.", "params": { @@ -10253,7 +10992,7 @@ "chats": "Mentioned chats", "count": "Total number of stories that can be fetched", "flags": "Flags, see TL conditional fields", - "pinned_to_top": "", + "pinned_to_top": "IDs of pinned stories.", "stories": "Stories", "users": "Mentioned users" } @@ -10299,7 +11038,7 @@ "new_messages": "New messages", "other_updates": "Other updates", "pts": "The PTS from which to start getting updates the next time", - "timeout": "Clients are supposed to refetch the channel difference after timeout seconds have elapsed", + "timeout": "Clients are supposed to refetch the channel difference after timeout seconds have elapsed, if the user is currently viewing the chat, see here \u00bb for more info.", "users": "Users" } }, @@ -10309,7 +11048,7 @@ "final": "Whether there are more updates that must be fetched (always false)", "flags": "Flags, see TL conditional fields", "pts": "The latest PTS", - "timeout": "Clients are supposed to refetch the channel difference after timeout seconds have elapsed" + "timeout": "Clients are supposed to refetch the channel difference after timeout seconds have elapsed, if the user is currently viewing the chat, see here \u00bb for more info." } }, "updates.ChannelDifferenceTooLong": { @@ -10416,6 +11155,14 @@ "full_user": "Full user information", "users": "Mentioned users" } + }, + "users.Users": { + "desc": "", + "params": {} + }, + "users.UsersSlice": { + "desc": "", + "params": {} } }, "method": { @@ -10426,9 +11173,9 @@ "app_version": "Application version", "device_model": "Device model", "flags": "Flags, see TL conditional fields", - "lang_code": "Code for the language used on the client, ISO 639-1 standard", - "lang_pack": "Language pack to use", - "params": "Additional initConnection parameters. For now, only the tz_offset field is supported, for specifying timezone offset in seconds.", + "lang_code": "Either an ISO 639-1 language code or a language pack name obtained from a language pack link.", + "lang_pack": "Platform identifier (i.e. android, tdesktop, etc).", + "params": "Additional initConnection parameters. For now, only the tz_offset field is supported, for specifying the timezone offset in seconds.", "proxy": "Info about an MTProto proxy", "query": "The query itself", "system_lang_code": "Code for the language used on the device's OS, ISO 639-1 standard", @@ -10450,26 +11197,26 @@ } }, "InvokeWithApnsSecret": { - "desc": "{schema}", + "desc": "Official clients only, invoke with Apple push verification.", "params": { - "nonce": "", - "query": "", - "secret": "" + "nonce": "Nonce.", + "query": "Query.", + "secret": "Secret." } }, "InvokeWithBusinessConnection": { - "desc": "{schema}", + "desc": "Invoke a method using a Telegram Business Bot connection, see here \u00bb for more info, including a list of the methods that can be wrapped in this constructor.", "params": { - "connection_id": "", - "query": "" + "connection_id": "Business connection ID.", + "query": "The actual query." } }, "InvokeWithGooglePlayIntegrity": { - "desc": "{schema}", + "desc": "Official clients only, invoke with Google Play Integrity token.", "params": { - "nonce": "", - "query": "", - "token": "" + "nonce": "Nonce.", + "query": "Query.", + "token": "Token." } }, "InvokeWithLayer": { @@ -10486,6 +11233,10 @@ "range": "Message range" } }, + "InvokeWithReCaptcha": { + "desc": "", + "params": {} + }, "InvokeWithTakeout": { "desc": "Invoke a method within a takeout session, see here \u00bb for more info.", "params": { @@ -10509,6 +11260,10 @@ "value_hashes": "Types of values sent and their hashes" } }, + "account.AddNoPaidMessagesException": { + "desc": "", + "params": {} + }, "account.CancelPasswordEmail": { "desc": "Cancel the code that was sent to verify an email to use as 2FA recovery method.", "params": {} @@ -10555,9 +11310,9 @@ } }, "account.CreateBusinessChatLink": { - "desc": "{schema}", + "desc": "Create a business chat deep link \u00bb.", "params": { - "link": "" + "link": "Info about the link to create." } }, "account.CreateTheme": { @@ -10587,9 +11342,9 @@ "params": {} }, "account.DeleteBusinessChatLink": { - "desc": "{schema}", + "desc": "Delete a business chat deep link \u00bb.", "params": { - "slug": "" + "slug": "Slug of the link, obtained as specified here \u00bb." } }, "account.DeleteSecureValue": { @@ -10599,16 +11354,16 @@ } }, "account.DisablePeerConnectedBot": { - "desc": "{schema}", + "desc": "Permanently disconnect a specific chat from all business bots \u00bb (equivalent to specifying it in recipients.exclude_users during initial configuration with account.updateConnectedBot \u00bb); to reconnect of a chat disconnected using this method the user must reconnect the entire bot by invoking account.updateConnectedBot \u00bb.", "params": { - "peer": "" + "peer": "The chat to disconnect" } }, "account.EditBusinessChatLink": { - "desc": "{schema}", + "desc": "Edit a created business chat deep link \u00bb.", "params": { - "link": "", - "slug": "" + "link": "New link information.", + "slug": "Slug of the link, obtained as specified here \u00bb." } }, "account.FinishTakeoutSession": { @@ -10647,35 +11402,39 @@ "params": {} }, "account.GetBotBusinessConnection": { - "desc": "{schema}", + "desc": "Bots may invoke this method to re-fetch the updateBotBusinessConnect constructor associated with a specific business connection_id, see here \u00bb for more info on connected business bots.\nThis is needed for example for freshly logged in bots that are receiving some updateBotNewBusinessMessage, etc. updates because some users have already connected to the bot before it could login.\nIn this case, the bot is receiving messages from the business connection, but it hasn't cached the associated updateBotBusinessConnect with info about the connection (can it reply to messages? etc.) yet, and cannot receive the old ones because they were sent when the bot wasn't logged into the session yet.\nThis method can be used to fetch info about a not-yet-cached business connection, and should not be invoked if the info is already cached or to fetch changes, as eventual changes will automatically be sent as new updateBotBusinessConnect updates to the bot using the usual update delivery methods \u00bb.", "params": { - "connection_id": "" + "connection_id": "Business connection ID \u00bb." } }, "account.GetBusinessChatLinks": { - "desc": "{schema}", + "desc": "List all created business chat deep links \u00bb.", "params": {} }, "account.GetChannelDefaultEmojiStatuses": { "desc": "Get a list of default suggested channel emoji statuses.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the account.emojiStatuses.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "account.GetChannelRestrictedStatusEmojis": { "desc": "Returns fetch the full list of custom emoji IDs \u00bb that cannot be used in channel emoji statuses \u00bb.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the emojiList.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "account.GetChatThemes": { "desc": "Get all available chat themes \u00bb.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the account.themes.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, + "account.GetCollectibleEmojiStatuses": { + "desc": "", + "params": {} + }, "account.GetConnectedBots": { - "desc": "{schema}", + "desc": "List all currently connected business bots \u00bb", "params": {} }, "account.GetContactSignUpNotification": { @@ -10689,25 +11448,25 @@ "account.GetDefaultBackgroundEmojis": { "desc": "Get a set of suggested custom emoji stickers that can be used in an accent color pattern.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the emojiList.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "account.GetDefaultEmojiStatuses": { "desc": "Get a list of default suggested emoji statuses", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the account.emojiStatuses.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "account.GetDefaultGroupPhotoEmojis": { "desc": "Get a set of suggested custom emoji stickers that can be used as group picture", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the emojiList.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "account.GetDefaultProfilePhotoEmojis": { "desc": "Get a set of suggested custom emoji stickers that can be used as profile picture", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the emojiList.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "account.GetGlobalPrivacySettings": { @@ -10735,6 +11494,10 @@ "peer": "Notification source" } }, + "account.GetPaidMessagesRevenue": { + "desc": "", + "params": {} + }, "account.GetPassword": { "desc": "Obtain configuration for two-factor authorization with password", "params": {} @@ -10752,19 +11515,19 @@ } }, "account.GetReactionsNotifySettings": { - "desc": "{schema}", + "desc": "Get the current reaction notification settings \u00bb.", "params": {} }, "account.GetRecentEmojiStatuses": { "desc": "Get recently used emoji statuses", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the account.emojiStatuses.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "account.GetSavedRingtones": { "desc": "Fetch saved notification sounds", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the account.savedRingtones.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "account.GetSecureValue": { @@ -10784,7 +11547,7 @@ "desc": "Get installed themes", "params": { "format": "Theme format, a string that identifies the theming engines supported by the client", - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the account.themes.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "account.GetTmpPassword": { @@ -10803,7 +11566,7 @@ "account.GetWallPapers": { "desc": "Returns a list of available wallpapers.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the account.wallPapers.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "account.GetWebAuthorizations": { @@ -10914,9 +11677,9 @@ "params": {} }, "account.ResolveBusinessChatLink": { - "desc": "{schema}", + "desc": "Resolve a business chat deep link \u00bb.", "params": { - "slug": "" + "slug": "Slug of the link, obtained as specified here \u00bb." } }, "account.SaveAutoDownloadSettings": { @@ -11035,22 +11798,22 @@ } }, "account.SetReactionsNotifySettings": { - "desc": "{schema}", + "desc": "Change the reaction notification settings \u00bb.", "params": { - "settings": "" + "settings": "New reaction notification settings." } }, "account.ToggleConnectedBotPaused": { - "desc": "{schema}", + "desc": "Pause or unpause a specific chat, temporarily disconnecting it from all business bots \u00bb.", "params": { - "paused": "", - "peer": "" + "paused": "Whether to pause or unpause the chat", + "peer": "The chat to pause" } }, "account.ToggleSponsoredMessages": { - "desc": "{schema}", + "desc": "Disable or re-enable Telegram ads for the current Premium account.", "params": { - "enabled": "" + "enabled": "Enable or disable ads." } }, "account.ToggleUsername": { @@ -11069,45 +11832,45 @@ } }, "account.UpdateBirthday": { - "desc": "{schema}", + "desc": "Update our birthday, see here \u00bb for more info.", "params": { - "birthday": "", + "birthday": "Birthday.", "flags": "Flags, see TL conditional fields" } }, "account.UpdateBusinessAwayMessage": { - "desc": "{schema}", + "desc": "Set a list of Telegram Business away messages.", "params": { "flags": "Flags, see TL conditional fields", - "message": "" + "message": "Away message configuration and contents." } }, "account.UpdateBusinessGreetingMessage": { - "desc": "{schema}", + "desc": "Set a list of Telegram Business greeting messages.", "params": { "flags": "Flags, see TL conditional fields", - "message": "" + "message": "Greeting message configuration and contents." } }, "account.UpdateBusinessIntro": { - "desc": "{schema}", + "desc": "Set or remove the Telegram Business introduction \u00bb.", "params": { "flags": "Flags, see TL conditional fields", - "intro": "" + "intro": "Telegram Business introduction, to remove it call the method without setting this flag." } }, "account.UpdateBusinessLocation": { - "desc": "{schema}", + "desc": "Businesses \u00bb may advertise their location using this method, see here \u00bb for more info.", "params": { - "address": "", + "address": "Mandatory when setting/updating the location, contains a textual description of the address (max 96 UTF-8 chars).", "flags": "Flags, see TL conditional fields", - "geo_point": "" + "geo_point": "Optional, contains a set of geographical coordinates." } }, "account.UpdateBusinessWorkHours": { - "desc": "{schema}", + "desc": "Specify a set of Telegram Business opening hours.\nThis info will be contained in userFull.business_work_hours.", "params": { - "business_work_hours": "", + "business_work_hours": "Opening hours (optional, if not set removes all opening hours).", "flags": "Flags, see TL conditional fields" } }, @@ -11121,13 +11884,13 @@ } }, "account.UpdateConnectedBot": { - "desc": "{schema}", + "desc": "Connect a business bot \u00bb to the current account, or to change the current connection settings.", "params": { - "bot": "", - "can_reply": "", - "deleted": "", + "bot": "The bot to connect or disconnect", + "can_reply": "Whether the bot can reply to messages it receives from us, on behalf of us using the business connection.", + "deleted": "Whether to fully disconnect the bot from the current account.", "flags": "Flags, see TL conditional fields", - "recipients": "" + "recipients": "Configuration for the business connection" } }, "account.UpdateDeviceLocked": { @@ -11157,9 +11920,9 @@ } }, "account.UpdatePersonalChannel": { - "desc": "{schema}", + "desc": "Associate (or remove) a personal channel \u00bb, that will be listed on our personal profile page \u00bb.", "params": { - "channel": "" + "channel": "The channel, pass inputChannelEmpty to remove it." } }, "account.UpdateProfile": { @@ -11335,11 +12098,11 @@ } }, "auth.ReportMissingCode": { - "desc": "{schema}", + "desc": "Official apps only, reports that the SMS authentication code wasn't delivered.", "params": { - "mnc": "", - "phone_code_hash": "", - "phone_number": "" + "mnc": "MNC of the current network operator.", + "phone_code_hash": "The phone code hash obtained from auth.sendCode", + "phone_number": "Phone number where we were supposed to receive the code" } }, "auth.RequestFirebaseSms": { @@ -11349,7 +12112,7 @@ "ios_push_secret": "Secret token received via an apple push notification", "phone_code_hash": "Phone code hash returned by auth.sendCode", "phone_number": "Phone number", - "play_integrity_token": "", + "play_integrity_token": "On Android, an object obtained as described in the auth documentation \u00bb", "safety_net_token": "On Android, a JWS object obtained as described in the auth documentation \u00bb" } }, @@ -11363,7 +12126,7 @@ "flags": "Flags, see TL conditional fields", "phone_code_hash": "The phone code hash obtained from auth.sendCode", "phone_number": "The phone number", - "reason": "" + "reason": "Official clients only, used if the device integrity verification failed, and no secret could be obtained to invoke auth.requestFirebaseSms: in this case, the device integrity verification failure reason must be passed here." } }, "auth.ResetAuthorizations": { @@ -11402,11 +12165,19 @@ "first_name": "New user first name", "flags": "Flags, see TL conditional fields", "last_name": "New user last name", - "no_joined_notifications": "", + "no_joined_notifications": "If set, users on Telegram that have already added phone_number to their contacts will not receive signup notifications about this user.", "phone_code_hash": "SMS-message ID", "phone_number": "Phone number in the international format" } }, + "bots.AddPreviewMedia": { + "desc": "Add a main mini app preview, see here \u00bb for more info.", + "params": { + "bot": "The bot that owns the Main Mini App.", + "lang_code": "ISO 639-1 language code, indicating the localization of the preview to add.", + "media": "The photo/video preview, uploaded using messages.uploadMedia." + } + }, "bots.AllowSendMessage": { "desc": "Allow the specified bot to send us messages", "params": { @@ -11426,6 +12197,35 @@ "bot": "The bot" } }, + "bots.CheckDownloadFileParams": { + "desc": "Check if a mini app can request the download of a specific file: called when handling web_app_request_file_download events \u00bb", + "params": { + "bot": "The bot that owns the mini app that requested the download", + "file_name": "The filename from the web_app_request_file_download event \u00bb", + "url": "The url from the web_app_request_file_download event \u00bb" + } + }, + "bots.DeletePreviewMedia": { + "desc": "Delete a main mini app preview, see here \u00bb for more info.", + "params": { + "bot": "The bot that owns the Main Mini App.", + "lang_code": "ISO 639-1 language code, indicating the localization of the preview to delete.", + "media": "The photo/video preview to delete, previously fetched as specified here \u00bb." + } + }, + "bots.EditPreviewMedia": { + "desc": "Edit a main mini app preview, see here \u00bb for more info.", + "params": { + "bot": "The bot that owns the Main Mini App.", + "lang_code": "ISO 639-1 language code, indicating the localization of the preview to edit.", + "media": "The photo/video preview to replace, previously fetched as specified here \u00bb.", + "new_media": "The new photo/video preview, uploaded using messages.uploadMedia." + } + }, + "bots.GetAdminedBots": { + "desc": "Get a list of bots owned by the current user", + "params": {} + }, "bots.GetBotCommands": { "desc": "Obtain a list of bot commands for the specified bot scope and language code", "params": { @@ -11447,6 +12247,30 @@ "user_id": "User ID or empty for the default menu button." } }, + "bots.GetBotRecommendations": { + "desc": "", + "params": {} + }, + "bots.GetPopularAppBots": { + "desc": "Fetch popular Main Mini Apps, to be used in the apps tab of global search \u00bb.", + "params": { + "limit": "Maximum number of results to return, see pagination", + "offset": "Offset for pagination, initially an empty string, then re-use the next_offset returned by the previous query." + } + }, + "bots.GetPreviewInfo": { + "desc": "Bot owners only, fetch main mini app preview information, see here \u00bb for more info.", + "params": { + "bot": "The bot that owns the Main Mini App.", + "lang_code": "Fetch previews for the specified ISO 639-1 language code." + } + }, + "bots.GetPreviewMedias": { + "desc": "Fetch main mini app previews, see here \u00bb for more info.", + "params": { + "bot": "The bot that owns the Main Mini App." + } + }, "bots.InvokeWebViewCustomMethod": { "desc": "Send a custom request from a mini bot app, triggered by a web_app_invoke_custom_method event \u00bb.", "params": { @@ -11455,6 +12279,14 @@ "params": "Method parameters" } }, + "bots.ReorderPreviewMedias": { + "desc": "Reorder a main mini app previews, see here \u00bb for more info.", + "params": { + "bot": "The bot that owns the Main Mini App.", + "lang_code": "ISO 639-1 language code, indicating the localization of the previews to reorder.", + "order": "New order of the previews." + } + }, "bots.ReorderUsernames": { "desc": "Reorder usernames associated to a bot we own.", "params": { @@ -11514,6 +12346,17 @@ "user_id": "User ID" } }, + "bots.SetCustomVerification": { + "desc": "", + "params": {} + }, + "bots.ToggleUserEmojiStatusPermission": { + "desc": "Allow or prevent a bot from changing our emoji status \u00bb", + "params": { + "bot": "The bot", + "enabled": "Whether to allow or prevent the bot from changing our emoji status" + } + }, "bots.ToggleUsername": { "desc": "Activate or deactivate a purchased fragment.com username associated to a bot we own.", "params": { @@ -11522,6 +12365,22 @@ "username": "Username" } }, + "bots.UpdateStarRefProgram": { + "desc": "Create, edit or delete the affiliate program of a bot we own", + "params": { + "bot": "The bot", + "commission_permille": "The permille commission rate: it indicates the share of Telegram Stars received by affiliates for every transaction made by users they referred inside of the bot. The minimum and maximum values for this parameter are contained in the starref_min_commission_permille and starref_max_commission_permille client configuration parameters. Can be 0 to terminate the affiliate program. Both the duration and the commission may only be raised after creation of the program: to lower them, the program must first be terminated and a new one created.", + "duration_months": "Indicates the duration of the affiliate program; if not set, there is no expiration date.", + "flags": "Flags, see TL conditional fields" + } + }, + "bots.UpdateUserEmojiStatus": { + "desc": "Change the emoji status of a user (invoked by bots, see here \u00bb for more info on the full flow)", + "params": { + "emoji_status": "The emoji status", + "user_id": "The user whose emoji status should be changed" + } + }, "channels.CheckUsername": { "desc": "Check if a username is free and can be assigned to a channel/supergroup", "params": { @@ -11529,15 +12388,8 @@ "username": "The username to check" } }, - "channels.ClickSponsoredMessage": { - "desc": "Informs the server that the user has either:", - "params": { - "channel": "Channel where the sponsored message was posted", - "random_id": "Message ID" - } - }, - "channels.ConvertToGigagroup": { - "desc": "Convert a supergroup to a gigagroup, when requested by channel suggestions.", + "channels.ConvertToGigagroup": { + "desc": "Convert a supergroup to a gigagroup, when requested by channel suggestions.", "params": { "channel": "The supergroup to convert" } @@ -11699,13 +12551,13 @@ "by_location": "Get geogroups", "check_limit": "If set and the user has reached the limit of owned public channels/supergroups/geogroups, instead of returning the channel list one of the specified errors will be returned.Useful to check if a new public channel can indeed be created, even before asking the user to enter a channel username to use in channels.checkUsername/channels.updateUsername.", "flags": "Flags, see TL conditional fields", - "for_personal": "" + "for_personal": "Set this flag to only fetch the full list of channels that may be passed to account.updatePersonalChannel to display them on our profile page." } }, "channels.GetChannelRecommendations": { "desc": "Obtain a list of similarly themed public channels, selected based on similarities in their subscriber bases.", "params": { - "channel": "The method will return channels related to the passed channel.", + "channel": "The method will return channels related to the passed channel. If not set, the method will returns channels related to channels the user has joined.", "flags": "Flags, see TL conditional fields" } }, @@ -11784,12 +12636,6 @@ "peer": "The group where we intend to send messages" } }, - "channels.GetSponsoredMessages": { - "desc": "Get a list of sponsored messages", - "params": { - "channel": "Peer" - } - }, "channels.InviteToChannel": { "desc": "Invite users to a channel/supergroup", "params": { @@ -11854,36 +12700,28 @@ "participant": "Participant whose messages should be reported" } }, - "channels.ReportSponsoredMessage": { - "desc": "{schema}", - "params": { - "channel": "", - "option": "", - "random_id": "" - } - }, "channels.RestrictSponsoredMessages": { - "desc": "{schema}", + "desc": "Disable ads on the specified channel, for all users.", "params": { - "channel": "", - "restricted": "" + "channel": "The channel.", + "restricted": "Whether to disable or re-enable ads." } }, "channels.SearchPosts": { - "desc": "{schema}", + "desc": "Globally search for posts from public channels \u00bb (including those we aren't a member of) containing a specific hashtag.", "params": { - "hashtag": "", + "hashtag": "The hashtag to search, without the # character.", "limit": "Maximum number of results to return, see pagination", "offset_id": "Offsets for pagination, for more info click here", - "offset_peer": "", - "offset_rate": "" + "offset_peer": "Offsets for pagination, for more info click here", + "offset_rate": "Initially 0, then set to the next_rate parameter of messages.messagesSlice" } }, "channels.SetBoostsToUnblockRestrictions": { - "desc": "{schema}", + "desc": "Admins with ban_users admin rights \u00bb may allow users that apply a certain number of booosts \u00bb to the group to bypass slow mode \u00bb and other \u00bb supergroup restrictions, see here \u00bb for more info.", "params": { - "boosts": "", - "channel": "" + "boosts": "The number of required boosts (1-8, 0 to disable).", + "channel": "The supergroup." } }, "channels.SetDiscussionGroup": { @@ -11894,10 +12732,10 @@ } }, "channels.SetEmojiStickers": { - "desc": "{schema}", + "desc": "Set a custom emoji stickerset for supergroups. Only usable after reaching at least the boost level \u00bb specified in the group_emoji_stickers_level_min \u00bb config parameter.", "params": { - "channel": "", - "stickerset": "" + "channel": "The supergroup", + "stickerset": "The custom emoji stickerset to associate to the supergroup" } }, "channels.SetStickers": { @@ -11953,7 +12791,9 @@ "desc": "Enable/disable message signatures in channels", "params": { "channel": "Channel", - "enabled": "Value" + "flags": "Flags, see TL conditional fields", + "profiles_enabled": "If set, messages from channel admins will link to their profiles, just like for group messages: can only be set if the signatures_enabled flag is set.", + "signatures_enabled": "If set, enables message signatures." } }, "channels.ToggleSlowMode": { @@ -11985,16 +12825,20 @@ "channel": "Channel whose accent color should be changed.", "color": "ID of the accent color palette \u00bb to use (not RGB24, see here \u00bb for more info); if not set, the default palette is used.", "flags": "Flags, see TL conditional fields", - "for_profile": "Whether to change the accent color emoji pattern of the profile page; otherwise, the accent color and emoji pattern of messages will be changed." + "for_profile": "Whether to change the accent color emoji pattern of the profile page; otherwise, the accent color and emoji pattern of messages will be changed. Channels can change both message and profile palettes; supergroups can only change the profile palette, of course after reaching the appropriate boost level." } }, "channels.UpdateEmojiStatus": { - "desc": "Set an emoji status for a channel.", + "desc": "Set an emoji status for a channel or supergroup.", "params": { - "channel": "The channel, must have at least channel_emoji_status_level_min boosts.", + "channel": "The channel/supergroup, must have at least channel_emoji_status_level_min/group_emoji_status_level_min boosts.", "emoji_status": "Emoji status to set" } }, + "channels.UpdatePaidMessagesPrice": { + "desc": "", + "params": {} + }, "channels.UpdatePinnedForumTopic": { "desc": "Pin or unpin forum topics", "params": { @@ -12010,13 +12854,6 @@ "username": "New username, pass an empty string to remove the username" } }, - "channels.ViewSponsoredMessage": { - "desc": "Mark a specific sponsored message as read", - "params": { - "channel": "Peer", - "random_id": "Message ID" - } - }, "chatlists.CheckChatlistInvite": { "desc": "Obtain information about a chat folder deep link \u00bb.", "params": { @@ -12151,7 +12988,7 @@ "params": {} }, "contacts.GetBirthdays": { - "desc": "{schema}", + "desc": "Fetch all users with birthdays that fall within +1/-1 days, relative to the current day: this method should be invoked by clients every 6-8 hours, and if the result is non-empty, it should be used to appropriately update locally cached birthday information in user.birthday.", "params": {} }, "contacts.GetBlocked": { @@ -12166,13 +13003,13 @@ "contacts.GetContactIDs": { "desc": "Get the telegram IDs of all contacts.\nReturns an array of Telegram user IDs for all contacts (0 if a contact does not have an associated Telegram account or have hidden their account using privacy settings).", "params": { - "hash": "Hash for pagination, for more info click here" + "hash": "Hash used for caching, for more info click here" } }, "contacts.GetContacts": { "desc": "Returns the current user's contact list.", "params": { - "hash": "Hash for pagination, for more info click here.Note that the hash is computed using the usual algorithm, passing to the algorithm first the previously returned contacts.contacts.saved_count field, then max 100000 sorted user IDs from the contact list, including the ID of the currently logged in user if it is saved as a contact. Example: tdlib implementation." + "hash": "Hash used for caching, for more info click here.Note that the hash is computed using the usual algorithm, passing to the algorithm first the previously returned contacts.contacts.saved_count field, then max 100000 sorted user IDs from the contact list, including the ID of the currently logged in user if it is saved as a contact. Example: tdlib implementation." } }, "contacts.GetLocated": { @@ -12188,6 +13025,10 @@ "desc": "Get all contacts, requires a takeout session, see here \u00bb for more info.", "params": {} }, + "contacts.GetSponsoredPeers": { + "desc": "", + "params": {} + }, "contacts.GetStatuses": { "desc": "Use this method to obtain the online statuses of all contacts with an accessible Telegram account.", "params": {} @@ -12195,6 +13036,7 @@ "contacts.GetTopPeers": { "desc": "Get most used peers", "params": { + "bots_app": "Most frequently used Main Mini Bot Apps.", "bots_inline": "Most used inline bots", "bots_pm": "Most used bots", "channels": "Most frequently visited channels", @@ -12203,7 +13045,7 @@ "forward_chats": "Chats to which the users often forwards messages to", "forward_users": "Users to which the users often forwards messages to", "groups": "Often-opened groups and supergroups", - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "limit": "Maximum number of results to return, see pagination", "offset": "Offset for pagination", "phone_calls": "Most frequently called users" @@ -12241,6 +13083,8 @@ "contacts.ResolveUsername": { "desc": "Resolve a @username to get peer info", "params": { + "flags": "Flags, see TL conditional fields", + "referer": "Referrer ID from referral links \u00bb.", "username": "@username to resolve" } }, @@ -12281,9 +13125,9 @@ } }, "fragment.GetCollectibleInfo": { - "desc": "{schema}", + "desc": "Fetch information about a fragment collectible, see here \u00bb for more info on the full flow.", "params": { - "collectible": "" + "collectible": "Collectible to fetch info about." } }, "help.AcceptTermsOfService": { @@ -12310,7 +13154,7 @@ "help.GetAppConfig": { "desc": "Get app-specific configuration, see client configuration for more info on the result.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the help.appConfig.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "help.GetAppUpdate": { @@ -12330,7 +13174,7 @@ "help.GetCountriesList": { "desc": "Get name, ISO code, localized name and phone codes/patterns of all available countries", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the help.countriesList.hash field returned by a previous call to the method, or pass 0 if this is the first call.", + "hash": "Hash used for caching, for more info click here.", "lang_code": "Language code of the current user" } }, @@ -12351,19 +13195,19 @@ "help.GetPassportConfig": { "desc": "Get passport configuration", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the help.passportConfig.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "help.GetPeerColors": { "desc": "Get the set of accent color palettes \u00bb that can be used for message accents.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the help.peerColors.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "help.GetPeerProfileColors": { "desc": "Get the set of accent color palettes \u00bb that can be used in profile page backgrounds.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the help.peerColors.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "help.GetPremiumPromo": { @@ -12393,9 +13237,9 @@ "params": {} }, "help.GetTimezonesList": { - "desc": "{schema}", + "desc": "Returns timezone information that may be used elsewhere in the API, such as to set Telegram Business opening hours \u00bb.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the help.timezonesList.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "help.GetUserInfo": { @@ -12427,36 +13271,36 @@ "desc": "Get new strings in language pack", "params": { "from_version": "Previous localization pack version", - "lang_code": "Language code", - "lang_pack": "Language pack" + "lang_code": "Either an ISO 639-1 language code or a language pack name obtained from a language pack link.", + "lang_pack": "Platform identifier (i.e. android, tdesktop, etc)." } }, "langpack.GetLangPack": { "desc": "Get localization pack strings", "params": { - "lang_code": "Language code", - "lang_pack": "Language pack name, usually obtained from a language pack link" + "lang_code": "Either an ISO 639-1 language code or a language pack name obtained from a language pack link.", + "lang_pack": "Platform identifier (i.e. android, tdesktop, etc)." } }, "langpack.GetLanguage": { "desc": "Get information about a language in a localization pack", "params": { - "lang_code": "Language code", - "lang_pack": "Language pack name, usually obtained from a language pack link" + "lang_code": "Either an ISO 639-1 language code or a language pack name obtained from a language pack link.", + "lang_pack": "Platform identifier (i.e. android, tdesktop, etc)." } }, "langpack.GetLanguages": { "desc": "Get information about all languages in a localization pack", "params": { - "lang_pack": "Language pack" + "lang_pack": "Platform identifier (i.e. android, tdesktop, etc)." } }, "langpack.GetStrings": { "desc": "Get strings from a language pack", "params": { "keys": "Strings to get", - "lang_code": "Language code", - "lang_pack": "Language pack name, usually obtained from a language pack link" + "lang_code": "Either an ISO 639-1 language code or a language pack name obtained from a language pack link.", + "lang_pack": "Platform identifier (i.e. android, tdesktop, etc)." } }, "messages.AcceptEncryption": { @@ -12505,9 +13349,9 @@ } }, "messages.CheckQuickReplyShortcut": { - "desc": "{schema}", + "desc": "Before offering the user the choice to add a message to a quick reply shortcut, to make sure that none of the limits specified here \u00bb were reached.", "params": { - "shortcut": "" + "shortcut": "Shorcut name (not ID!)." } }, "messages.ClearAllDrafts": { @@ -12525,6 +13369,16 @@ "flags": "Flags, see TL conditional fields" } }, + "messages.ClickSponsoredMessage": { + "desc": "Informs the server that the user has interacted with a sponsored message in one of the ways listed here \u00bb.", + "params": { + "flags": "Flags, see TL conditional fields", + "fullscreen": "The user expanded the video to full screen, and then clicked on it.", + "media": "The user clicked on the media", + "peer": "The channel/bot where the ad is located", + "random_id": "The ad's unique ID." + } + }, "messages.CreateChat": { "desc": "Creates a new chat.", "params": { @@ -12557,10 +13411,10 @@ } }, "messages.DeleteFactCheck": { - "desc": "{schema}", + "desc": "Delete a fact-check from a message.", "params": { - "msg_id": "", - "peer": "" + "msg_id": "Message ID", + "peer": "Peer where the message was sent." } }, "messages.DeleteHistory": { @@ -12591,16 +13445,16 @@ } }, "messages.DeleteQuickReplyMessages": { - "desc": "{schema}", + "desc": "Delete one or more messages from a quick reply shortcut. This will also emit an updateDeleteQuickReplyMessages update.", "params": { - "id": "", - "shortcut_id": "" + "id": "IDs of shortcut messages to delete.", + "shortcut_id": "Shortcut ID." } }, "messages.DeleteQuickReplyShortcut": { - "desc": "{schema}", + "desc": "Completely delete a quick reply shortcut.\nThis will also emit an updateDeleteQuickReply update to other logged-in sessions (and no updateDeleteQuickReplyMessages updates, even if all the messages in the shortcuts are also deleted by this method).", "params": { - "shortcut_id": "" + "shortcut_id": "Shortcut ID" } }, "messages.DeleteRevokedExportedChatInvites": { @@ -12685,11 +13539,11 @@ } }, "messages.EditFactCheck": { - "desc": "{schema}", + "desc": "Edit/create a fact-check on a message.", "params": { - "msg_id": "", - "peer": "", - "text": "" + "msg_id": "Message ID", + "peer": "Peer where the message was sent", + "text": "Fact-check (maximum UTF-8 length specified in appConfig.factcheck_length_limit)." } }, "messages.EditInlineBotMessage": { @@ -12716,16 +13570,16 @@ "message": "New message", "no_webpage": "Disable webpage preview", "peer": "Where was the message sent", - "quick_reply_shortcut_id": "", + "quick_reply_shortcut_id": "If specified, edits a quick reply shortcut message, instead \u00bb.", "reply_markup": "Reply markup for inline keyboards", "schedule_date": "Scheduled message date for scheduled messages" } }, "messages.EditQuickReplyShortcut": { - "desc": "{schema}", + "desc": "Rename a quick reply shortcut.\nThis will emit an updateQuickReplies update to other logged-in sessions.", "params": { - "shortcut": "", - "shortcut_id": "" + "shortcut": "New shortcut name.", + "shortcut_id": "Shortcut ID." } }, "messages.ExportChatInvite": { @@ -12736,6 +13590,7 @@ "legacy_revoke_permanent": "Legacy flag, reproducing legacy behavior of this method: if set, revokes all previous links before creating a new one. Kept for bot API BC, should not be used by modern clients.", "peer": "Chat", "request_needed": "Whether admin confirmation is required before admitting each separate user into the chat", + "subscription_pricing": "For Telegram Star subscriptions \u00bb, contains the pricing of the subscription the user must activate to join the private channel.", "title": "Description of the invite link, visible only to administrators", "usage_limit": "Maximum number of users that can join using this link" } @@ -12750,6 +13605,7 @@ "messages.ForwardMessages": { "desc": "Forwards messages by their IDs.", "params": { + "allow_paid_floodskip": "Bots only: if set, allows sending up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.", "background": "Whether to send the message in background", "drop_author": "Whether to forward messages without quoting the original author", "drop_media_captions": "Whether to strip captions from media", @@ -12757,7 +13613,7 @@ "from_peer": "Source of messages", "id": "IDs of messages", "noforwards": "Only for bots, disallows further re-forwarding and saving of the messages, even if the destination chat doesn't have content protection enabled", - "quick_reply_shortcut": "", + "quick_reply_shortcut": "Add the messages to the specified quick reply shortcut \u00bb, instead.", "random_id": "Random ID to prevent resending of messages", "schedule_date": "Scheduled message date for scheduled messages", "send_as": "Forward the messages as the specified peer", @@ -12780,7 +13636,7 @@ "messages.GetAllStickers": { "desc": "Get all installed stickers", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.allStickers.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetArchivedStickers": { @@ -12802,7 +13658,7 @@ "messages.GetAttachMenuBots": { "desc": "Returns installed attachment menu bot mini apps \u00bb", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the attachMenuBots.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetAttachedStickers": { @@ -12812,22 +13668,22 @@ } }, "messages.GetAvailableEffects": { - "desc": "{schema}", + "desc": "Fetch the full list of usable animated message effects \u00bb.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.availableEffects.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetAvailableReactions": { "desc": "Obtain available message reactions \u00bb", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.availableReactions.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetBotApp": { "desc": "Obtain information about a direct link Mini App", "params": { "app": "Bot app information obtained from a Direct Mini App deep link \u00bb.", - "hash": "Hash for pagination, for more info click here" + "hash": "Hash used for caching, for more info click here" } }, "messages.GetBotCallbackAnswer": { @@ -12851,7 +13707,8 @@ "offset_user": "User ID for pagination: if set, offset_date must also be set.", "peer": "Chat", "q": "Search for a user in the pending join requests \u00bb list: only available when the requested flag is set, cannot be used together with a specific link.", - "requested": "If set, only returns info about users with pending join requests \u00bb" + "requested": "If set, only returns info about users with pending join requests \u00bb", + "subscription_expired": "Set this flag if the link is a Telegram Star subscription link \u00bb and only members with already expired subscription must be returned." } }, "messages.GetChats": { @@ -12881,7 +13738,7 @@ "messages.GetDefaultTagReactions": { "desc": "Fetch a default recommended list of saved message tag reactions.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.reactions.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetDhConfig": { @@ -12905,7 +13762,7 @@ "exclude_pinned": "Exclude pinned dialogs", "flags": "Flags, see TL conditional fields", "folder_id": "Peer folder ID, for more info click here", - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "limit": "Number of list elements to be returned", "offset_date": "Offsets for pagination, for more info click here", "offset_id": "Offsets for pagination, for more info click here (top_message ID used for pagination)", @@ -12928,9 +13785,9 @@ } }, "messages.GetEmojiGroups": { - "desc": "Represents a list of emoji categories, to be used when selecting custom emojis.", + "desc": "Represents a list of emoji categories.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.emojiGroups.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetEmojiKeywords": { @@ -12955,25 +13812,25 @@ "messages.GetEmojiProfilePhotoGroups": { "desc": "Represents a list of emoji categories, to be used when selecting custom emojis to set as profile picture.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.emojiGroups.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetEmojiStatusGroups": { "desc": "Represents a list of emoji categories, to be used when selecting custom emojis to set as custom emoji status.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.emojiGroups.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetEmojiStickerGroups": { - "desc": "{schema}", + "desc": "Represents a list of emoji categories, to be used when choosing a sticker.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.emojiGroups.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetEmojiStickers": { "desc": "Gets the list of currently installed custom emoji stickersets.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.allStickers.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetEmojiURL": { @@ -13002,35 +13859,35 @@ } }, "messages.GetExtendedMedia": { - "desc": "Get information about extended media", + "desc": "Fetch updated information about paid media, see here \u00bb for the full flow.", "params": { - "id": "Message IDs", - "peer": "Peer" + "id": "IDs of currently visible messages containing paid media.", + "peer": "Peer with visible paid media messages." } }, "messages.GetFactCheck": { - "desc": "{schema}", + "desc": "Fetch one or more factchecks, see here \u00bb for the full flow.", "params": { - "msg_id": "", - "peer": "" + "msg_id": "Messages that have associated factCheck constructors with the need_check flag set.", + "peer": "Peer where the messages were sent." } }, "messages.GetFavedStickers": { "desc": "Get faved stickers", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.favedStickers.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetFeaturedEmojiStickers": { "desc": "Gets featured custom emoji stickersets.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.featuredStickers.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetFeaturedStickers": { "desc": "Get featured stickers", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.featuredStickers.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetFullChat": { @@ -13081,7 +13938,7 @@ "messages.GetMaskStickers": { "desc": "Get installed mask stickers", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.allStickers.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetMessageEditData": { @@ -13131,7 +13988,7 @@ } }, "messages.GetMyStickers": { - "desc": "{schema}", + "desc": "Fetch all stickersets \u00bb owned by the current user.", "params": { "limit": "Maximum number of results to return, see pagination", "offset_id": "Offsets for pagination, for more info click here" @@ -13140,7 +13997,7 @@ "messages.GetOldFeaturedStickers": { "desc": "Method for fetching previously featured stickers", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.featuredStickers.hash field returned by a previous call to the method, or pass 0 if this is the first call.", + "hash": "Hash used for caching, for more info click here.", "limit": "Maximum number of results to return, see pagination", "offset": "Offset" } @@ -13152,12 +14009,16 @@ } }, "messages.GetOutboxReadDate": { - "desc": "{schema}", + "desc": "Get the exact read date of one of our messages, sent to a private chat with another user.", "params": { - "msg_id": "", - "peer": "" + "msg_id": "The message ID.", + "peer": "The user to whom we sent the message." } }, + "messages.GetPaidReactionPrivacy": { + "desc": "Fetches an updatePaidReactionPrivacy update with the current default paid reaction privacy, see here \u00bb for more info.", + "params": {} + }, "messages.GetPeerDialogs": { "desc": "Get dialog info of specified peers", "params": { @@ -13198,25 +14059,32 @@ "peer": "Chat where the poll was sent" } }, + "messages.GetPreparedInlineMessage": { + "desc": "Obtain a prepared inline message generated by a mini app: invoked when handling web_app_send_prepared_message events", + "params": { + "bot": "The bot that owns the mini app that emitted the web_app_send_prepared_message event", + "id": "The id from the web_app_send_prepared_message event" + } + }, "messages.GetQuickReplies": { - "desc": "{schema}", + "desc": "Fetch basic info about all existing quick reply shortcuts.", "params": { - "hash": "Hash for pagination, for more info click here" + "hash": "Hash for pagination, generated as specified here \u00bb (not the usual algorithm used for hash generation.)" } }, "messages.GetQuickReplyMessages": { - "desc": "{schema}", + "desc": "Fetch (a subset or all) messages in a quick reply shortcut \u00bb.", "params": { "flags": "Flags, see TL conditional fields", - "hash": "Hash for pagination, for more info click here", - "id": "", - "shortcut_id": "" + "hash": "Hash used for caching, for more info click here", + "id": "IDs of the messages to fetch, if empty fetches all of them.", + "shortcut_id": "Quick reply shortcut ID." } }, "messages.GetRecentLocations": { "desc": "Get live location history of a certain user", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "limit": "Maximum number of results to return, see pagination", "peer": "User" } @@ -13224,7 +14092,7 @@ "messages.GetRecentReactions": { "desc": "Get recently used message reactions", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.reactions.hash field returned by a previous call to the method, or pass 0 if this is the first call.", + "hash": "Hash used for caching, for more info click here.", "limit": "Maximum number of results to return, see pagination" } }, @@ -13233,14 +14101,14 @@ "params": { "attached": "Get stickers recently attached to photo or video files", "flags": "Flags, see TL conditional fields", - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.recentStickers.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetReplies": { "desc": "Get messages in a reply thread", "params": { "add_offset": "Offsets for pagination, for more info click here", - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "limit": "Maximum number of results to return, see pagination", "max_id": "If a positive value was transferred, the method will return only messages with ID smaller than max_id", "min_id": "If a positive value was transferred, the method will return only messages with ID bigger than min_id", @@ -13255,7 +14123,7 @@ "params": { "exclude_pinned": "Exclude pinned dialogs", "flags": "Flags, see TL conditional fields", - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "limit": "Number of list elements to be returned", "offset_date": "Offsets for pagination, for more info click here", "offset_id": "Offsets for pagination, for more info click here (top_message ID used for pagination)", @@ -13265,7 +14133,7 @@ "messages.GetSavedGifs": { "desc": "Get saved GIFs.", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.savedGifs.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetSavedHistory": { @@ -13285,14 +14153,14 @@ "desc": "Fetch the full list of saved message tags created by the user.", "params": { "flags": "Flags, see TL conditional fields", - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.savedReactionTags.hash field returned by a previous call to the method, or pass 0 if this is the first call.", + "hash": "Hash used for caching, for more info click here.", "peer": "If set, returns tags only used in the specified saved message dialog." } }, "messages.GetScheduledHistory": { "desc": "Get scheduled messages", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here. To generate the hash, populate the ids array with the id, date and edit_date (in this order) of the previously returned messages (in order, i.e. ids = [id1, date1, edit_date1, id2, date2, edit_date2, ...]).", "peer": "Peer" } }, @@ -13339,10 +14207,16 @@ "desc": "Get message ranges for saving the user's chat history", "params": {} }, + "messages.GetSponsoredMessages": { + "desc": "Get a list of sponsored messages for a peer, see here \u00bb for more info.", + "params": { + "peer": "The currently open channel/bot." + } + }, "messages.GetStickerSet": { "desc": "Get info about a stickerset", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here", "stickerset": "Stickerset" } }, @@ -13350,7 +14224,7 @@ "desc": "Get stickers by emoji", "params": { "emoticon": "The emoji", - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.stickers.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.GetSuggestedDialogFilters": { @@ -13360,7 +14234,7 @@ "messages.GetTopReactions": { "desc": "Got popular message reactions", "params": { - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.reactions.hash field returned by a previous call to the method, or pass 0 if this is the first call.", + "hash": "Hash used for caching, for more info click here.", "limit": "Maximum number of results to return, see pagination" } }, @@ -13393,7 +14267,7 @@ "messages.GetWebPage": { "desc": "Get instant view page", "params": { - "hash": "Hash for pagination, for more info click here", + "hash": "Hash used for caching, for more info click here. Note: the usual hash generation algorithm cannot be used in this case, please re-use the webPage.hash field returned by a previous call to the method, or pass 0 if this is the first call or if the previous call did not return a webPage.", "url": "URL of IV page to fetch" } }, @@ -13565,9 +14439,9 @@ } }, "messages.ReorderQuickReplies": { - "desc": "{schema}", + "desc": "Reorder quick reply shortcuts.", "params": { - "order": "" + "order": "IDs of all created quick reply shortcuts, in the desired order." } }, "messages.ReorderStickerSets": { @@ -13584,8 +14458,8 @@ "params": { "id": "IDs of messages to report", "message": "Comment for report moderation", - "peer": "Peer", - "reason": "Why are these messages being reported" + "option": "Menu option, intially empty", + "peer": "Peer" } }, "messages.ReportEncryptedSpam": { @@ -13594,6 +14468,10 @@ "peer": "The secret chat to report" } }, + "messages.ReportMessagesDelivery": { + "desc": "", + "params": {} + }, "messages.ReportReaction": { "desc": "Report a message reaction", "params": { @@ -13608,11 +14486,21 @@ "peer": "Peer to report" } }, + "messages.ReportSponsoredMessage": { + "desc": "Report a sponsored message \u00bb, see here \u00bb for more info on the full flow.", + "params": { + "option": "Chosen report option, initially an empty string, see here \u00bb for more info on the full flow.", + "peer": "The channel/bot where the ad is located", + "random_id": "The ad's unique ID." + } + }, "messages.RequestAppWebView": { "desc": "Open a bot mini app from a direct Mini App deep link, sending over user information after user confirmation.", "params": { "app": "The app obtained by invoking messages.getBotApp as specified in the direct Mini App deep link docs.", + "compact": "If set, requests to open the mini app in compact mode (as opposed to normal or fullscreen mode). Must be set if the mode parameter of the direct Mini App deep link is equal to compact.", "flags": "Flags, see TL conditional fields", + "fullscreen": "If set, requests to open the mini app in fullscreen mode (as opposed to compact or normal mode). Must be set if the mode parameter of the direct Mini App deep link is equal to fullscreen.", "peer": "If the client has clicked on the link in a Telegram chat, pass the chat's peer information; otherwise pass the bot's peer information, instead.", "platform": "Short name of the application; 0-64 English letters, digits, and underscores", "start_param": "If the startapp query string parameter is present in the direct Mini App deep link, pass it to start_param.", @@ -13628,15 +14516,30 @@ "user_id": "User ID" } }, + "messages.RequestMainWebView": { + "desc": "Open a Main Mini App.", + "params": { + "bot": "Bot that owns the main mini app.", + "compact": "If set, requests to open the mini app in compact mode (as opposed to normal or fullscreen mode). Must be set if the mode parameter of the Main Mini App link is equal to compact.", + "flags": "Flags, see TL conditional fields", + "fullscreen": "If set, requests to open the mini app in fullscreen mode (as opposed to compact or normal mode). Must be set if the mode parameter of the Main Mini App link is equal to fullscreen.", + "peer": "Currently open chat, may be inputPeerEmpty if no chat is currently open.", + "platform": "Short name of the application; 0-64 English letters, digits, and underscores", + "start_param": "Start parameter, if opening from a Main Mini App link \u00bb.", + "theme_params": "Theme parameters \u00bb" + } + }, "messages.RequestSimpleWebView": { "desc": "Open a bot mini app.", "params": { "bot": "Bot that owns the mini app", + "compact": "Deprecated.", "flags": "Flags, see TL conditional fields", - "from_side_menu": "Set this flag if opening the Mini App from the installed side menu entry \u00bb or from a Mini App link \u00bb.", + "from_side_menu": "Set this flag if opening the Mini App from the installed side menu entry \u00bb.", "from_switch_webview": "Whether the webapp was opened by clicking on the switch_webview button shown on top of the inline results list returned by messages.getInlineBotResults.", + "fullscreen": "Requests to open the app in fullscreen mode.", "platform": "Short name of the application; 0-64 English letters, digits, and underscores", - "start_param": "Start parameter, if opening from a Mini App link \u00bb.", + "start_param": "Deprecated.", "theme_params": "Theme parameters \u00bb", "url": "Web app URL, if opening from a keyboard button or inline result" } @@ -13655,8 +14558,10 @@ "desc": "Open a bot mini app, sending over user information after user confirmation.", "params": { "bot": "Bot that owns the web app", + "compact": "If set, requests to open the mini app in compact mode (as opposed to normal or fullscreen mode). Must be set if the mode parameter of the attachment menu deep link is equal to compact.", "flags": "Flags, see TL conditional fields", "from_bot_menu": "Whether the webview was opened by clicking on the bot's menu button \u00bb.", + "fullscreen": "If set, requests to open the mini app in fullscreen mode (as opposed to normal or compact mode). Must be set if the mode parameter of the attachment menu deep link is equal to fullscreen.", "peer": "Dialog where the web app is being opened, and where the resulting message will be sent (see the docs for more info \u00bb).", "platform": "Short name of the application; 0-64 English letters, digits, and underscores", "reply_to": "If set, indicates that the inline message that will be sent by the bot on behalf of the user once the web app interaction is terminated should be sent in reply to the specified message or story.", @@ -13677,6 +14582,7 @@ "messages.SaveDraft": { "desc": "Save a message draft associated to a chat.", "params": { + "effect": "Specifies a message effect \u00bb to use for the message.", "entities": "Message entities for styled text", "flags": "Flags, see TL conditional fields", "invert_media": "If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.", @@ -13694,6 +14600,15 @@ "unsave": "Whether to remove GIF from saved gifs list" } }, + "messages.SavePreparedInlineMessage": { + "desc": "Save a prepared inline message, to be shared by the user of the mini app using a web_app_send_prepared_message event", + "params": { + "flags": "Flags, see TL conditional fields", + "peer_types": "Types of chats where this message can be sent", + "result": "The message", + "user_id": "The user to whom the web_app_send_prepared_message event event will be sent" + } + }, "messages.SaveRecentSticker": { "desc": "Add/remove sticker from recent stickers list", "params": { @@ -13711,7 +14626,7 @@ "flags": "Flags, see TL conditional fields", "from_id": "Only return messages sent by the specified user ID", "hash": "Hash", - "limit": "Number of results to return", + "limit": "Number of results to return, can be 0 to only return the message counter.", "max_date": "If a positive value was transferred, only messages with a sending date smaller than the transferred one will be returned", "max_id": "Maximum message ID to return", "min_date": "If a positive value was transferred, only messages with a sending date bigger than the transferred one will be returned", @@ -13720,7 +14635,7 @@ "peer": "User or chat, histories with which are searched, or (inputPeerEmpty) constructor to search in all private chats and normal groups (not channels) \u00bb. Use messages.searchGlobal to search globally in all chats, groups, supergroups and channels.", "q": "Text search request", "saved_peer_id": "Search within the saved message dialog \u00bb with this ID.", - "saved_reaction": "", + "saved_reaction": "You may search for saved messages tagged \u00bb with one or more reactions using this flag.", "top_msg_id": "Thread ID" } }, @@ -13728,7 +14643,7 @@ "desc": "Look for custom emojis associated to a UTF8 emoji", "params": { "emoticon": "The emoji", - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the emojiList.hash field returned by a previous call to the method, or pass 0 if this is the first call." + "hash": "Hash used for caching, for more info click here." } }, "messages.SearchEmojiStickerSets": { @@ -13736,24 +14651,26 @@ "params": { "exclude_featured": "Exclude featured stickersets from results", "flags": "Flags, see TL conditional fields", - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.foundStickerSets.hash field returned by a previous call to the method, or pass 0 if this is the first call.", + "hash": "Hash used for caching, for more info click here.", "q": "Query string" } }, "messages.SearchGlobal": { "desc": "Search for messages and peers globally", "params": { - "broadcasts_only": "", + "broadcasts_only": "If set, only returns results from channels (used in the global channel search tab \u00bb).", "filter": "Global search filter", "flags": "Flags, see TL conditional fields", "folder_id": "Peer folder ID, for more info click here", + "groups_only": "Whether to search only in groups", "limit": "Offsets for pagination, for more info click here", "max_date": "If a positive value was transferred, the method will return only messages with date smaller than max_date", "min_date": "If a positive value was specified, the method will return only messages with date bigger than min_date", "offset_id": "Offsets for pagination, for more info click here", "offset_peer": "Offsets for pagination, for more info click here", "offset_rate": "Initially 0, then set to the next_rate parameter of messages.messagesSlice", - "q": "Query" + "q": "Query", + "users_only": "Whether to search only in private chats" } }, "messages.SearchSentMedia": { @@ -13769,10 +14686,23 @@ "params": { "exclude_featured": "Exclude featured stickersets from results", "flags": "Flags, see TL conditional fields", - "hash": "Hash for pagination, for more info click here.Note: the usual hash generation algorithm cannot be used in this case, please re-use the messages.foundStickerSets.hash field returned by a previous call to the method, or pass 0 if this is the first call.", + "hash": "Hash used for caching, for more info click here.", "q": "Query string" } }, + "messages.SearchStickers": { + "desc": "Search for stickers using AI-powered keyword search", + "params": { + "emojis": "If set, returns custom emoji stickers", + "emoticon": "Space-separated list of emojis to search for", + "flags": "Flags, see TL conditional fields", + "hash": "Hash used for caching, for more info click here. The hash may be generated locally by using the ids of the returned or stored sticker documents.", + "lang_code": "List of possible IETF language tags of the user's input language; may be empty if unknown", + "limit": "Maximum number of results to return, see pagination", + "offset": "Offset for pagination", + "q": "The search term" + } + }, "messages.SendBotRequestedPeer": { "desc": "Send one or more chosen peers, as requested by a keyboardButtonRequestPeer button.", "params": { @@ -13821,7 +14751,7 @@ "id": "Result ID from messages.getInlineBotResults", "peer": "Destination", "query_id": "Query ID from messages.getInlineBotResults", - "quick_reply_shortcut": "", + "quick_reply_shortcut": "Add the message to the specified quick reply shortcut \u00bb, instead.", "random_id": "Random ID to avoid resending the same query", "reply_to": "If set, indicates that the message should be sent in reply to the specified message or story.", "schedule_date": "Scheduled message date for scheduled messages", @@ -13832,9 +14762,10 @@ "messages.SendMedia": { "desc": "Send a media", "params": { + "allow_paid_floodskip": "Bots only: if set, allows sending up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.", "background": "Send message in background", "clear_draft": "Clear the draft", - "effect": "", + "effect": "Specifies a message effect \u00bb to use for the message.", "entities": "Message entities for styled text", "flags": "Flags, see TL conditional fields", "invert_media": "If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.", @@ -13842,7 +14773,7 @@ "message": "Caption", "noforwards": "Only for bots, disallows forwarding and saving of the messages, even if the destination chat doesn't have content protection enabled", "peer": "Destination", - "quick_reply_shortcut": "", + "quick_reply_shortcut": "Add the message to the specified quick reply shortcut \u00bb, instead.", "random_id": "Random ID to avoid resending the same message", "reply_markup": "Reply markup for bot keyboards", "reply_to": "If set, indicates that the message should be sent in reply to the specified message or story.", @@ -13855,9 +14786,10 @@ "messages.SendMessage": { "desc": "Sends a message to a chat", "params": { + "allow_paid_floodskip": "Bots only: if set, allows sending up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.", "background": "Send this message as background message", "clear_draft": "Clear the draft field", - "effect": "", + "effect": "Specifies a message effect \u00bb to use for the message.", "entities": "Message entities for sending styled text", "flags": "Flags, see TL conditional fields", "invert_media": "If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.", @@ -13865,7 +14797,7 @@ "no_webpage": "Set this flag to disable generation of the webpage preview", "noforwards": "Only for bots, disallows forwarding and saving of the messages, even if the destination chat doesn't have content protection enabled", "peer": "The destination where the message will be sent", - "quick_reply_shortcut": "", + "quick_reply_shortcut": "Add the message to the specified quick reply shortcut \u00bb, instead.", "random_id": "Unique client message ID required to prevent message resending", "reply_markup": "Reply markup for sending bot buttons", "reply_to": "If set, indicates that the message should be sent in reply to the specified message or story. Also used to quote other messages.", @@ -13878,15 +14810,16 @@ "messages.SendMultiMedia": { "desc": "Send an album or grouped media", "params": { + "allow_paid_floodskip": "Bots only: if set, allows sending up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.", "background": "Send in background?", "clear_draft": "Whether to clear drafts", - "effect": "", + "effect": "Specifies a message effect \u00bb to use for the message.", "flags": "Flags, see TL conditional fields", "invert_media": "If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.", "multi_media": "The medias to send: note that they must be separately uploaded using messages.uploadMedia first, using raw inputMediaUploaded* constructors is not supported.", "noforwards": "Only for bots, disallows forwarding and saving of the messages, even if the destination chat doesn't have content protection enabled", "peer": "The destination chat", - "quick_reply_shortcut": "", + "quick_reply_shortcut": "Add the message to the specified quick reply shortcut \u00bb, instead.", "reply_to": "If set, indicates that the message should be sent in reply to the specified message or story.", "schedule_date": "Scheduled message date for scheduled messages", "send_as": "Send this message as the specified peer", @@ -13894,13 +14827,17 @@ "update_stickersets_order": "Whether to move used stickersets to top, see here for more info on this flag \u00bb" } }, + "messages.SendPaidReaction": { + "desc": "Sends one or more paid Telegram Star reactions \u00bb, transferring Telegram Stars \u00bb to a channel's balance.", + "params": {} + }, "messages.SendQuickReplyMessages": { - "desc": "{schema}", + "desc": "Send a quick reply shortcut \u00bb.", "params": { - "id": "", - "peer": "", - "random_id": "", - "shortcut_id": "" + "id": "Specify a subset of messages from the shortcut to send; if empty, defaults to all of them.", + "peer": "The peer where to send the shortcut (users only, for now).", + "random_id": "Unique client IDs required to prevent message resending, one for each message we're sending, may be empty (but not recommended).", + "shortcut_id": "The ID of the quick reply shortcut to send." } }, "messages.SendReaction": { @@ -13987,8 +14924,9 @@ "params": { "available_reactions": "Allowed reaction emojis", "flags": "Flags, see TL conditional fields", + "paid_enabled": "If this flag is set and a Bool is passed, the method will enable or disable paid message reactions \u00bb. If this flag is not set, the previously stored setting will not be changed.", "peer": "Group where to apply changes", - "reactions_limit": "" + "reactions_limit": "This flag may be used to impose a custom limit of unique reactions (i.e. a customizable version of appConfig.reactions_uniq_max); this field and the other info set by the method will then be available to users in channelFull and chatFull. If this flag is not set, the previously configured reactions_limit will not be altered." } }, "messages.SetChatTheme": { @@ -14013,7 +14951,7 @@ "messages.SetDefaultHistoryTTL": { "desc": "Changes the default value of the Time-To-Live setting, applied to all new chats.", "params": { - "period": "The new default Time-To-Live of all messages sent in new chats." + "period": "The new default Time-To-Live of all messages sent in new chats, in seconds." } }, "messages.SetDefaultReaction": { @@ -14108,9 +15046,9 @@ } }, "messages.ToggleDialogFilterTags": { - "desc": "{schema}", + "desc": "Enable or disable folder tags \u00bb.", "params": { - "enabled": "" + "enabled": "Enable or disable folder tags." } }, "messages.ToggleDialogPin": { @@ -14128,6 +15066,14 @@ "peer": "The chat or channel" } }, + "messages.TogglePaidReactionPrivacy": { + "desc": "Changes the privacy of already sent paid reactions on a specific message.", + "params": { + "msg_id": "The ID of the message to which we sent the paid reactions", + "peer": "The channel", + "private": "If true, makes the current anonymous in the top sender leaderboard for this message; otherwise, does the opposite." + } + }, "messages.TogglePeerTranslations": { "desc": "Show or hide the real-time chat translation popup for a certain chat", "params": { @@ -14237,12 +15183,19 @@ "messages.UploadMedia": { "desc": "Upload a file and associate it to a chat (without actually sending it to the chat)", "params": { - "business_connection_id": "", + "business_connection_id": "Whether the media will be used only in the specified business connection \u00bb, and not directly by the bot.", "flags": "Flags, see TL conditional fields", "media": "File uploaded in chunks as described in files \u00bb", "peer": "The chat, can be inputPeerEmpty for bots and inputPeerSelf for users." } }, + "messages.ViewSponsoredMessage": { + "desc": "Mark a specific sponsored message \u00bb as read", + "params": { + "peer": "The channel/bot where the ad is located", + "random_id": "The ad's unique ID." + } + }, "payments.ApplyGiftCode": { "desc": "Apply a Telegram Premium giftcode \u00bb", "params": { @@ -14263,10 +15216,26 @@ "receipt": "Receipt" } }, - "payments.CanPurchasePremium": { - "desc": "Checks whether Telegram Premium purchase is possible. Must be called before in-store Premium purchase, official apps only.", + "payments.BotCancelStarsSubscription": { + "desc": "Cancel a bot subscription", + "params": { + "charge_id": "The provider_charge_id from the messageActionPaymentSentMe service message sent to the bot for the first subscription payment.", + "flags": "Flags, see TL conditional fields", + "restore": "If not set, disables autorenewal of the subscriptions, and prevents the user from reactivating the subscription once the current period expires: a subscription cancelled by the bot will have the starsSubscription.bot_canceled flag set. The bot can can partially undo this operation by setting this flag: this will allow the user to reactivate the subscription.", + "user_id": "The ID of the user whose subscription should be (un)cancelled" + } + }, + "payments.CanPurchaseStore": { + "desc": "", + "params": {} + }, + "payments.ChangeStarsSubscription": { + "desc": "Activate or deactivate a Telegram Star subscription \u00bb.", "params": { - "purpose": "Payment purpose" + "canceled": "Whether to cancel or reactivate the subscription.", + "flags": "Flags, see TL conditional fields", + "peer": "Always pass inputPeerSelf.", + "subscription_id": "ID of the subscription." } }, "payments.CheckGiftCode": { @@ -14283,18 +15252,65 @@ "info": "Clear the last order settings saved by the user" } }, + "payments.ConnectStarRefBot": { + "desc": "Join a bot's affiliate program, becoming an affiliate \u00bb", + "params": { + "bot": "The bot that offers the affiliate program", + "peer": "The peer that will become the affiliate: star commissions will be transferred to this peer's star balance." + } + }, + "payments.ConvertStarGift": { + "desc": "Convert a received gift \u00bb into Telegram Stars: this will permanently destroy the gift, converting it into starGift.convert_stars Telegram Stars, added to the user's balance.", + "params": { + "msg_id": "The ID of the messageService with the messageActionStarGift.", + "user_id": "ID of the user that sent us the gift." + } + }, + "payments.EditConnectedStarRefBot": { + "desc": "Leave a bot's affiliate program \u00bb", + "params": { + "flags": "Flags, see TL conditional fields", + "link": "The affiliate link to revoke", + "peer": "The peer that was affiliated", + "revoked": "If set, leaves the bot's affiliate program" + } + }, "payments.ExportInvoice": { "desc": "Generate an invoice deep link", "params": { "invoice_media": "Invoice" } }, + "payments.FulfillStarsSubscription": { + "desc": "Re-join a private channel associated to an active Telegram Star subscription \u00bb.", + "params": { + "peer": "Always pass inputPeerSelf.", + "subscription_id": "ID of the subscription." + } + }, "payments.GetBankCardData": { "desc": "Get info about a credit card", "params": { "number": "Credit card number" } }, + "payments.GetConnectedStarRefBot": { + "desc": "Fetch info about a specific bot affiliation \u00bb", + "params": { + "bot": "The bot that offers the affiliate program", + "peer": "The affiliated peer" + } + }, + "payments.GetConnectedStarRefBots": { + "desc": "Fetch all affiliations we have created for a certain peer", + "params": { + "flags": "Flags, see TL conditional fields", + "limit": "Maximum number of results to return, see pagination", + "offset_date": "If set, returns only results older than the specified unixtime", + "offset_link": "Offset for pagination, taken from the last returned connectedBotStarRef.url (initially empty)", + "peer": "The affiliated peer" + } + }, "payments.GetGiveawayInfo": { "desc": "Obtain information about a Telegram Premium giveaway \u00bb.", "params": { @@ -14307,7 +15323,7 @@ "params": { "flags": "Flags, see TL conditional fields", "invoice": "Invoice", - "theme_params": "A JSON object with the following keys, containing color theme information (integers, RGB24) to pass to the payment provider, to apply in eventual verification pages: bg_color - Background color text_color - Text color hint_color - Hint text color link_color - Link color button_color - Button color button_text_color - Button text color" + "theme_params": "Theme parameters \u00bb" } }, "payments.GetPaymentReceipt": { @@ -14328,26 +15344,115 @@ "desc": "Get saved payment information", "params": {} }, + "payments.GetSavedStarGift": { + "desc": "", + "params": {} + }, + "payments.GetSavedStarGifts": { + "desc": "", + "params": {} + }, + "payments.GetStarGiftUpgradePreview": { + "desc": "", + "params": {} + }, + "payments.GetStarGiftWithdrawalUrl": { + "desc": "", + "params": {} + }, + "payments.GetStarGifts": { + "desc": "Get a list of available gifts, see here \u00bb for more info.", + "params": { + "hash": "Hash used for caching, for more info click here.The hash may be generated locally by using the ids of the returned or stored sticker starGifts." + } + }, + "payments.GetStarsGiftOptions": { + "desc": "Obtain a list of Telegram Stars gift options \u00bb as starsGiftOption constructors.", + "params": { + "flags": "Flags, see TL conditional fields", + "user_id": "Receiver of the gift (optional)." + } + }, + "payments.GetStarsGiveawayOptions": { + "desc": "Fetch a list of star giveaway options \u00bb.", + "params": {} + }, + "payments.GetStarsRevenueAdsAccountUrl": { + "desc": "Returns a URL for a Telegram Ad platform account that can be used to set up advertisements for channel/bot in peer, paid using the Telegram Stars owned by the specified peer, see here \u00bb for more info.", + "params": { + "peer": "Channel or bot that owns the stars." + } + }, + "payments.GetStarsRevenueStats": { + "desc": "Get Telegram Star revenue statistics \u00bb.", + "params": { + "dark": "Whether to enable dark theme for graph colors", + "flags": "Flags, see TL conditional fields", + "peer": "Get statistics for the specified bot, channel or ourselves (inputPeerSelf)." + } + }, + "payments.GetStarsRevenueWithdrawalUrl": { + "desc": "Withdraw funds from a channel or bot's star balance \u00bb.", + "params": { + "password": "2FA password, see here \u00bb for more info.", + "peer": "Channel or bot from which to withdraw funds.", + "stars": "Amount of stars to withdraw." + } + }, "payments.GetStarsStatus": { - "desc": "{schema}", + "desc": "Get the current Telegram Stars balance of the current account (with peer=inputPeerSelf), or the stars balance of the bot specified in peer.", "params": { - "peer": "" + "peer": "Peer of which to get the balance." + } + }, + "payments.GetStarsSubscriptions": { + "desc": "Obtain a list of active, expired or cancelled Telegram Star subscriptions \u00bb.", + "params": { + "flags": "Flags, see TL conditional fields", + "missing_balance": "Whether to return only subscriptions expired due to an excessively low Telegram Star balance.", + "offset": "Offset for pagination, taken from payments.starsStatus.subscriptions_next_offset.", + "peer": "Always pass inputPeerSelf." } }, "payments.GetStarsTopupOptions": { - "desc": "{schema}", + "desc": "Obtain a list of Telegram Stars topup options \u00bb as starsTopupOption constructors.", "params": {} }, "payments.GetStarsTransactions": { - "desc": "{schema}", + "desc": "Fetch Telegram Stars transactions.", + "params": { + "ascending": "Return transactions in ascending order by date (instead of descending order by date).", + "flags": "Flags, see TL conditional fields", + "inbound": "If set, fetches only incoming transactions.", + "limit": "Maximum number of results to return, see pagination", + "offset": "Offset for pagination, obtained from the returned next_offset, initially an empty string \u00bb.", + "outbound": "If set, fetches only outgoing transactions.", + "peer": "Fetch the transaction history of the peer (inputPeerSelf or a bot we own).", + "subscription_id": "If set, fetches only transactions for the specified Telegram Star subscription \u00bb." + } + }, + "payments.GetStarsTransactionsByID": { + "desc": "Obtain info about Telegram Star transactions \u00bb using specific transaction IDs.", + "params": { + "id": "Transaction IDs.", + "peer": "Channel or bot." + } + }, + "payments.GetSuggestedStarRefBots": { + "desc": "Obtain a list of suggested mini apps with available affiliate programs", "params": { "flags": "Flags, see TL conditional fields", - "inbound": "", - "offset": "", - "outbound": "", - "peer": "" + "limit": "Maximum number of results to return, see pagination", + "offset": "Offset for pagination, taken from payments.suggestedStarRefBots.next_offset, initially empty.", + "order_by_date": "If set, orders results by the creation date of the affiliate program", + "order_by_revenue": "If set, orders results by the expected revenue", + "peer": "The peer that will become the affiliate: star commissions will be transferred to this peer's star balance." } }, + "payments.GetUniqueStarGift": { + "desc": "", + "params": {} + }, "payments.LaunchPrepaidGiveaway": { "desc": "Launch a prepaid giveaway \u00bb.", "params": { @@ -14357,10 +15462,19 @@ } }, "payments.RefundStarsCharge": { - "desc": "{schema}", + "desc": "Refund a Telegram Stars transaction, see here \u00bb for more info.", "params": { - "charge_id": "", - "user_id": "" + "charge_id": "Transaction ID.", + "user_id": "User to refund." + } + }, + "payments.SaveStarGift": { + "desc": "Display or remove a received gift \u00bb from our profile.", + "params": { + "flags": "Flags, see TL conditional fields", + "msg_id": "The ID of the messageService with the messageActionStarGift.", + "unsave": "If set, hides the gift from our profile.", + "user_id": "ID of the user that sent us the gift." } }, "payments.SendPaymentForm": { @@ -14376,13 +15490,28 @@ } }, "payments.SendStarsForm": { - "desc": "{schema}", + "desc": "Make a payment using Telegram Stars, see here \u00bb for more info.", "params": { - "flags": "Flags, see TL conditional fields", - "form_id": "", - "invoice": "" + "form_id": "Payment form ID", + "invoice": "Invoice" } }, + "payments.ToggleChatStarGiftNotifications": { + "desc": "", + "params": {} + }, + "payments.ToggleStarGiftsPinnedToTop": { + "desc": "", + "params": {} + }, + "payments.TransferStarGift": { + "desc": "", + "params": {} + }, + "payments.UpgradeStarGift": { + "desc": "", + "params": {} + }, "payments.ValidateRequestedInfo": { "desc": "Submit requested order information for validation", "params": { @@ -14416,6 +15545,10 @@ "protocol": "Phone call settings" } }, + "phone.CreateConferenceCall": { + "desc": "", + "params": {} + }, "phone.CreateGroupCall": { "desc": "Create a group call or livestream", "params": { @@ -14698,17 +15831,17 @@ } }, "premium.GetBoostsList": { - "desc": "Obtains info about the boosts that were applied to a certain channel (admins only)", + "desc": "Obtains info about the boosts that were applied to a certain channel or supergroup (admins only)", "params": { "flags": "Flags, see TL conditional fields", - "gifts": "Whether to return only info about boosts received from gift codes and giveaways created by the channel \u00bb", + "gifts": "Whether to return only info about boosts received from gift codes and giveaways created by the channel/supergroup \u00bb", "limit": "Maximum number of results to return, see pagination", "offset": "Offset for pagination, obtained from premium.boostsList.next_offset", - "peer": "The channel" + "peer": "The channel/supergroup" } }, "premium.GetBoostsStatus": { - "desc": "Gets the current number of boosts of a channel.", + "desc": "Gets the current number of boosts of a channel/supergroup.", "params": { "peer": "The peer." } @@ -14718,70 +15851,70 @@ "params": {} }, "premium.GetUserBoosts": { - "desc": "Returns the lists of boost that were applied to a channel by a specific user (admins only)", + "desc": "Returns the lists of boost that were applied to a channel/supergroup by a specific user (admins only)", "params": { - "peer": "The channel", + "peer": "The channel/supergroup", "user_id": "The user" } }, "smsjobs.FinishJob": { - "desc": "{schema}", + "desc": "Finish an SMS job (official clients only).", "params": { - "error": "", + "error": "If failed, the error.", "flags": "Flags, see TL conditional fields", - "job_id": "" + "job_id": "Job ID." } }, "smsjobs.GetSmsJob": { - "desc": "{schema}", + "desc": "Get info about an SMS job (official clients only).", "params": { - "job_id": "" + "job_id": "Job ID" } }, "smsjobs.GetStatus": { - "desc": "{schema}", + "desc": "Get SMS jobs status (official clients only).", "params": {} }, "smsjobs.IsEligibleToJoin": { - "desc": "{schema}", + "desc": "Check if we can process SMS jobs (official clients only).", "params": {} }, "smsjobs.Join": { - "desc": "{schema}", + "desc": "Enable SMS jobs (official clients only).", "params": {} }, "smsjobs.Leave": { - "desc": "{schema}", + "desc": "Disable SMS jobs (official clients only).", "params": {} }, "smsjobs.UpdateSettings": { - "desc": "{schema}", + "desc": "Update SMS job settings (official clients only).", "params": { - "allow_international": "", + "allow_international": "Allow international numbers?", "flags": "Flags, see TL conditional fields" } }, "stats.GetBroadcastRevenueStats": { - "desc": "{schema}", + "desc": "Get channel ad revenue statistics \u00bb.", "params": { - "channel": "", - "dark": "", - "flags": "Flags, see TL conditional fields" + "dark": "Whether to enable dark theme for graph colors", + "flags": "Flags, see TL conditional fields", + "peer": "Get ad revenue stats for the specified channel or bot" } }, "stats.GetBroadcastRevenueTransactions": { - "desc": "{schema}", + "desc": "Fetch channel ad revenue transaction history \u00bb.", "params": { - "channel": "", "limit": "Maximum number of results to return, see pagination", - "offset": "" + "offset": "Offset for pagination", + "peer": "Get ad revenue transactions for the specified channel or bot" } }, "stats.GetBroadcastRevenueWithdrawalUrl": { - "desc": "{schema}", + "desc": "Withdraw funds from a channel's ad revenue balance \u00bb.", "params": { - "channel": "", - "password": "" + "password": "2FA password, see here \u00bb for more info.", + "peer": "Get ad revenue withdrawal URL for the specified channel or bot" } }, "stats.GetBroadcastStats": { @@ -14845,14 +15978,14 @@ } }, "stickers.AddStickerToSet": { - "desc": "Add a sticker to a stickerset, bots only. The sticker set must have been created by the bot.", + "desc": "Add a sticker to a stickerset. The sticker set must have been created by the current user/bot.", "params": { "sticker": "The sticker", "stickerset": "The stickerset" } }, "stickers.ChangeSticker": { - "desc": "Update the keywords, emojis or mask coordinates of a sticker, bots only.", + "desc": "Update the keywords, emojis or mask coordinates of a sticker.", "params": { "emoji": "If set, updates the emoji list associated to the sticker", "flags": "Flags, see TL conditional fields", @@ -14862,7 +15995,7 @@ } }, "stickers.ChangeStickerPosition": { - "desc": "Changes the absolute position of a sticker in the set to which it belongs; for bots only. The sticker set must have been created by the bot", + "desc": "Changes the absolute position of a sticker in the set to which it belongs. The sticker set must have been created by the current user/bot.", "params": { "position": "The new position of the sticker, zero-based", "sticker": "The sticker" @@ -14875,7 +16008,7 @@ } }, "stickers.CreateStickerSet": { - "desc": "Create a stickerset, bots only.", + "desc": "Create a stickerset.", "params": { "emojis": "Whether this is a custom emoji stickerset.", "flags": "Flags, see TL conditional fields", @@ -14890,29 +16023,29 @@ } }, "stickers.DeleteStickerSet": { - "desc": "Deletes a stickerset we created, bots only.", + "desc": "Deletes a stickerset we created.", "params": { "stickerset": "Stickerset to delete" } }, "stickers.RemoveStickerFromSet": { - "desc": "Remove a sticker from the set where it belongs, bots only. The sticker set must have been created by the bot.", + "desc": "Remove a sticker from the set where it belongs. The sticker set must have been created by the current user/bot.", "params": { "sticker": "The sticker to remove" } }, "stickers.RenameStickerSet": { - "desc": "Renames a stickerset, bots only.", + "desc": "Renames a stickerset.", "params": { "stickerset": "Stickerset to rename", "title": "New stickerset title" } }, "stickers.ReplaceSticker": { - "desc": "{schema}", + "desc": "Replace a sticker in a stickerset \u00bb.", "params": { - "new_sticker": "", - "sticker": "" + "new_sticker": "New sticker.", + "sticker": "Old sticker document." } }, "stickers.SetStickerSetThumb": { @@ -15075,8 +16208,19 @@ "params": { "id": "IDs of the stories to report.", "message": "Comment for report moderation", - "peer": "The peer that uploaded the story.", - "reason": "Why are these storeis being reported." + "option": "Menu option, intially empty", + "peer": "The peer that uploaded the story." + } + }, + "stories.SearchPosts": { + "desc": "Globally search for stories using a hashtag or a location media area, see here \u00bb for more info on the full flow.", + "params": { + "area": "A mediaAreaGeoPoint or a mediaAreaVenue. Note mediaAreaGeoPoint areas may be searched only if they have an associated address.", + "flags": "Flags, see TL conditional fields", + "hashtag": "Hashtag (without the #)", + "limit": "Maximum number of results to return, see pagination", + "offset": "Offset for pagination: initially an empty string, then the next_offset from the previously returned stories.foundStories.", + "peer": "If set, returns only stories posted by this peer." } }, "stories.SendReaction": { @@ -15130,10 +16274,10 @@ } }, "stories.TogglePinnedToTop": { - "desc": "{schema}", + "desc": "Pin some stories to the top of the profile, see here \u00bb for more info.", "params": { - "id": "", - "peer": "" + "id": "IDs of the stories to pin (max stories_pinned_to_top_count_max).", + "peer": "Peer where to pin stories." } }, "updates.GetChannelDifference": { @@ -15234,11 +16378,9 @@ "id": "User ID" } }, - "users.GetIsPremiumRequiredToContact": { - "desc": "{schema}", - "params": { - "id": "" - } + "users.GetRequirementsToContact": { + "desc": "", + "params": {} }, "users.GetUsers": { "desc": "Returns basic user info according to their identifiers.", @@ -15258,9 +16400,6 @@ "AccountDaysTTL": { "desc": "Time-to-live of current account" }, - "AppWebViewResult": { - "desc": "Contains the link that must be used to open a direct link Mini App." - }, "AttachMenuBot": { "desc": "Represents a bot mini app that can be launched from the attachment menu \u00bb" }, @@ -15292,7 +16431,7 @@ "desc": "Media autosave settings" }, "AvailableEffect": { - "desc": "" + "desc": "Describes a message effect \u00bb." }, "AvailableReaction": { "desc": "Animations associated with a message reaction" @@ -15304,7 +16443,7 @@ "desc": "Basic theme settings" }, "Birthday": { - "desc": "" + "desc": "Birthday information for a user." }, "Bool": { "desc": "Boolean type." @@ -15315,8 +16454,11 @@ "BotApp": { "desc": "Contains information about a direct link Mini App." }, + "BotAppSettings": { + "desc": "Mini app \u00bb settings" + }, "BotBusinessConnection": { - "desc": "" + "desc": "Contains info about a bot business connection." }, "BotCommand": { "desc": "Describes a bot command that can be used in a chat" @@ -15336,41 +16478,53 @@ "BotMenuButton": { "desc": "Indicates the action to execute when pressing the in-UI menu button for bots" }, - "BroadcastRevenueBalances": { + "BotPreviewMedia": { + "desc": "Represents a Main Mini App preview media, see here \u00bb for more info." + }, + "BotVerification": { "desc": "" }, - "BroadcastRevenueTransaction": { + "BotVerifierSettings": { "desc": "" }, + "BroadcastRevenueBalances": { + "desc": "Channel ad revenue balance \u00bb information." + }, + "BroadcastRevenueTransaction": { + "desc": "A channel ad revenue \u00bb transaction." + }, "BusinessAwayMessage": { - "desc": "" + "desc": "Describes a Telegram Business away message, automatically sent to users writing to us when we're offline, during closing hours, while we're on vacation, or in some other custom time period when we cannot immediately answer to the user." }, "BusinessAwayMessageSchedule": { - "desc": "" + "desc": "Specifies when should the Telegram Business away messages be sent." }, "BusinessBotRecipients": { + "desc": "Specifies the private chats that a connected business bot \u00bb may receive messages and interact with." + }, + "BusinessBotRights": { "desc": "" }, "BusinessChatLink": { - "desc": "" + "desc": "Contains info about a business chat deep link \u00bb created by the current account." }, "BusinessGreetingMessage": { - "desc": "" + "desc": "Describes a Telegram Business greeting, automatically sent to new users writing to us in private for the first time, or after a certain inactivity period." }, "BusinessIntro": { - "desc": "" + "desc": "Telegram Business introduction \u00bb." }, "BusinessLocation": { - "desc": "" + "desc": "Represents the location of a Telegram Business \u00bb." }, "BusinessRecipients": { - "desc": "" + "desc": "Specifies the chats that can receive Telegram Business away \u00bb and greeting \u00bb messages." }, "BusinessWeeklyOpen": { - "desc": "" + "desc": "A time interval, indicating the opening hours of a Telegram Business." }, "BusinessWorkHours": { - "desc": "" + "desc": "Specifies a set of Telegram Business opening hours." }, "CdnConfig": { "desc": "Configuration for CDN file downloads." @@ -15442,13 +16596,16 @@ "desc": "Object contains info on API configuring parameters." }, "ConnectedBot": { - "desc": "" + "desc": "Contains info about a connected business bot \u00bb." + }, + "ConnectedBotStarRef": { + "desc": "Info about an active affiliate program we have with a Mini App" }, "Contact": { "desc": "A contact of the current user." }, "ContactBirthday": { - "desc": "" + "desc": "Birthday information of a contact." }, "ContactStatus": { "desc": "Contact status: online / offline." @@ -15474,6 +16631,9 @@ "DialogPeer": { "desc": "Peer, or all peers in a folder" }, + "DisallowedGiftsSettings": { + "desc": "" + }, "Document": { "desc": "A document." }, @@ -15535,7 +16695,7 @@ "desc": "Represents a story deep link" }, "FactCheck": { - "desc": "" + "desc": "Represents a fact-check \u00bb created by an independent fact-checker." }, "FileHash": { "desc": "Hash of an uploaded file, to be checked for validity after download" @@ -15549,12 +16709,18 @@ "ForumTopic": { "desc": "Contains information about a forum topic" }, + "FoundStory": { + "desc": "A story found using global story search \u00bb." + }, "Game": { "desc": "Indicates an already sent game" }, "GeoPoint": { "desc": "Object defines a GeoPoint." }, + "GeoPointAddress": { + "desc": "Address optionally associated to a geoPoint." + }, "GlobalPrivacySettings": { "desc": "Global privacy settings" }, @@ -15604,22 +16770,22 @@ "desc": "Inline bot result" }, "InputBusinessAwayMessage": { - "desc": "" + "desc": "Describes a Telegram Business away message, automatically sent to users writing to us when we're offline, during closing hours, while we're on vacation, or in some other custom time period when we cannot immediately answer to the user." }, "InputBusinessBotRecipients": { - "desc": "" + "desc": "Specifies the private chats that a connected business bot \u00bb may interact with." }, "InputBusinessChatLink": { - "desc": "" + "desc": "Contains info about a business chat deep link \u00bb to be created by the current account." }, "InputBusinessGreetingMessage": { - "desc": "" + "desc": "Describes a Telegram Business greeting, automatically sent to new users writing to us in private for the first time, or after a certain inactivity period." }, "InputBusinessIntro": { - "desc": "" + "desc": "Telegram Business introduction \u00bb." }, "InputBusinessRecipients": { - "desc": "" + "desc": "Specifies the chats that can receive Telegram Business away \u00bb and greeting \u00bb messages." }, "InputChannel": { "desc": "Represents a channel" @@ -15631,13 +16797,13 @@ "desc": "Represents a folder" }, "InputCheckPasswordSRP": { - "desc": "Constructors for checking the validity of a 2FA SRP password" + "desc": "Constructors for checking the validity of a 2FA SRP password." }, "InputClientProxy": { "desc": "Info about an MTProxy used to connect." }, "InputCollectible": { - "desc": "" + "desc": "Represents a Fragment collectible \u00bb." }, "InputContact": { "desc": "Object defines a contact from the user's phone book." @@ -15706,11 +16872,14 @@ "desc": "Privacy rules indicate who can or can't do something and are specified by a PrivacyRule, and its input counterpart InputPrivacyRule." }, "InputQuickReplyShortcut": { - "desc": "" + "desc": "Represents a quick reply shortcut \u00bb." }, "InputReplyTo": { "desc": "Contains info about a message or story to reply to." }, + "InputSavedStarGift": { + "desc": "" + }, "InputSecureFile": { "desc": "Secure passport file, for more info see the passport docs \u00bb" }, @@ -15720,6 +16889,9 @@ "InputSingleMedia": { "desc": "A single media in an album or grouped media sent with messages.sendMultiMedia." }, + "InputStarsTransaction": { + "desc": "Used to fetch info about a Telegram Star transaction \u00bb." + }, "InputStickerSet": { "desc": "Represents a stickerset" }, @@ -15796,7 +16968,7 @@ "desc": "Message entities, representing styled text in a message" }, "MessageExtendedMedia": { - "desc": "Extended media" + "desc": "Paid media, see here \u00bb for more info." }, "MessageFwdHeader": { "desc": "Info about a forwarded message" @@ -15816,12 +16988,18 @@ "MessageReactions": { "desc": "Message reactions \u00bb" }, + "MessageReactor": { + "desc": "Info about a user in the paid Star reactions leaderboard for a message." + }, "MessageReplies": { "desc": "Info about post comments (for channels) or message replies (for groups)" }, "MessageReplyHeader": { "desc": "Reply information" }, + "MessageReportOption": { + "desc": "Report menu option" + }, "MessageViews": { "desc": "View, forward counter + info about replies of a specific message" }, @@ -15829,7 +17007,7 @@ "desc": "Object describes message filter." }, "MissingInvitee": { - "desc": "" + "desc": "Info about why a specific user could not be invited \u00bb." }, "MyBoost": { "desc": "Contains information about a single boost slot \u00bb." @@ -15844,7 +17022,7 @@ "desc": "Object defines the set of users and/or groups that generate notifications." }, "OutboxReadDate": { - "desc": "" + "desc": "Exact read date of a private message we sent to another user." }, "Page": { "desc": "Instant view page" @@ -15870,6 +17048,9 @@ "PageTableRow": { "desc": "Table row" }, + "PaidReactionPrivacy": { + "desc": "" + }, "PasswordKdfAlgo": { "desc": "Key derivation function to use when generating the password hash for SRP two-factor authorization" }, @@ -15886,7 +17067,7 @@ "desc": "Saved payment credentials" }, "Peer": { - "desc": "Chat partner or group." + "desc": "Identifier of a private chat, basic group, group or channel (see here \u00bb for more info)." }, "PeerBlocked": { "desc": "Info about a blocked user" @@ -15948,9 +17129,6 @@ "PremiumGiftCodeOption": { "desc": "Giveaway option." }, - "PremiumGiftOption": { - "desc": "Telegram Premium gift option" - }, "PremiumSubscriptionOption": { "desc": "Telegram Premium subscription option" }, @@ -15967,7 +17145,7 @@ "desc": "Contains info about the forwards of a story as a message to public chats and reposts by public channels." }, "QuickReply": { - "desc": "" + "desc": "A quick reply shortcut." }, "Reaction": { "desc": "Message reaction" @@ -15976,10 +17154,10 @@ "desc": "Number of users that reacted with a certain emoji" }, "ReactionNotificationsFrom": { - "desc": "" + "desc": "Reaction notification settings" }, "ReactionsNotifySettings": { - "desc": "" + "desc": "Reaction notification settings, see here \u00bb for more info." }, "ReadParticipantDate": { "desc": "Contains info about when a certain participant has read a message" @@ -15996,10 +17174,16 @@ "ReportReason": { "desc": "Report reason" }, + "ReportResult": { + "desc": "Represents a report menu or result" + }, "RequestPeerType": { "desc": "Filtering criteria to use for the peer selection list shown to the user." }, "RequestedPeer": { + "desc": "Info about a peer, shared by a user with the currently logged in bot using messages.sendBotRequestedPeer." + }, + "RequirementToContact": { "desc": "" }, "RestrictionReason": { @@ -16015,6 +17199,9 @@ "desc": "Represents a saved message dialog \u00bb." }, "SavedReactionTag": { + "desc": "Info about a saved message reaction tag \u00bb." + }, + "SavedStarGift": { "desc": "" }, "SearchResultsCalendarPeriod": { @@ -16065,26 +17252,56 @@ "ShippingOption": { "desc": "Shipping options" }, - "SimpleWebViewResult": { - "desc": "Contains the webview URL with appropriate theme parameters added" - }, "SmsJob": { - "desc": "" + "desc": "Info about an SMS job." }, "SponsoredMessage": { "desc": "A sponsored message" }, "SponsoredMessageReportOption": { + "desc": "A report option for a sponsored message \u00bb." + }, + "SponsoredPeer": { "desc": "" }, - "StarsTopupOption": { + "StarGift": { + "desc": "Represents a star gift, see here \u00bb for more info." + }, + "StarGiftAttribute": { "desc": "" }, + "StarRefProgram": { + "desc": "Indo about an affiliate program offered by a bot" + }, + "StarsAmount": { + "desc": "Describes a real (i.e. possibly decimal) amount of Telegram Stars." + }, + "StarsGiftOption": { + "desc": "Telegram Stars gift option." + }, + "StarsGiveawayOption": { + "desc": "Contains info about a Telegram Star giveaway option." + }, + "StarsGiveawayWinnersOption": { + "desc": "Represents a possible option for the number of winners in a star giveaway" + }, + "StarsRevenueStatus": { + "desc": "Describes Telegram Star revenue balances \u00bb." + }, + "StarsSubscription": { + "desc": "Represents a Telegram Star subscription \u00bb." + }, + "StarsSubscriptionPricing": { + "desc": "Pricing of a Telegram Star subscription \u00bb." + }, + "StarsTopupOption": { + "desc": "Telegram Stars topup option." + }, "StarsTransaction": { - "desc": "" + "desc": "Represents a Telegram Stars transaction \u00bb." }, "StarsTransactionPeer": { - "desc": "" + "desc": "Source of an incoming Telegram Star transaction, or its recipient for outgoing Telegram Star transactions." }, "StatsAbsValueAndPrev": { "desc": "Channel statistics value pair" @@ -16150,7 +17367,7 @@ "desc": "Theme settings" }, "Timezone": { - "desc": "" + "desc": "Timezone information." }, "TopPeer": { "desc": "Top peer" @@ -16228,10 +17445,10 @@ "desc": "Contains media autosave settings" }, "account.BusinessChatLinks": { - "desc": "" + "desc": "Contains info about business chat deep links \u00bb created by the current account." }, "account.ConnectedBots": { - "desc": "" + "desc": "Info about currently connected business bots." }, "account.ContentSettings": { "desc": "Sensitive content settings" @@ -16242,6 +17459,9 @@ "account.EmojiStatuses": { "desc": "A list of emoji statuses" }, + "account.PaidMessagesRevenue": { + "desc": "" + }, "account.Password": { "desc": "Configuration for two-factor authorization" }, @@ -16258,7 +17478,7 @@ "desc": "Result of an account.resetPassword request." }, "account.ResolvedBusinessChatLinks": { - "desc": "" + "desc": "Contains info about a single resolved business chat deep link \u00bb." }, "account.SavedRingtone": { "desc": "Contains information about a saved notification sound" @@ -16311,6 +17531,12 @@ "bots.BotInfo": { "desc": "Localized name, about text and description of a bot." }, + "bots.PopularAppBots": { + "desc": "Popular Main Mini Apps, to be used in the apps tab of global search \u00bb." + }, + "bots.PreviewInfo": { + "desc": "Contains info about Main Mini App previews, see here \u00bb for more info." + }, "channels.AdminLogResults": { "desc": "Admin log events" }, @@ -16324,7 +17550,7 @@ "desc": "A list of peers that can be used to send messages in a specific group" }, "channels.SponsoredMessageReportResult": { - "desc": "" + "desc": "Status of the method call used to report a sponsored message \u00bb." }, "chatlists.ChatlistInvite": { "desc": "Info about a chat folder deep link \u00bb." @@ -16342,7 +17568,7 @@ "desc": "Info on users from the current user's black list." }, "contacts.ContactBirthdays": { - "desc": "" + "desc": "Birthday information of our contacts." }, "contacts.Contacts": { "desc": "Info on the current user's contact list." @@ -16356,11 +17582,14 @@ "contacts.ResolvedPeer": { "desc": "Peer returned after resolving a @username" }, + "contacts.SponsoredPeers": { + "desc": "" + }, "contacts.TopPeers": { "desc": "Top peers" }, "fragment.CollectibleInfo": { - "desc": "" + "desc": "Info about a fragment collectible." }, "help.AppConfig": { "desc": "Contains various client configuration parameters" @@ -16417,7 +17646,7 @@ "desc": "Update of Telegram's terms of service" }, "help.TimezonesList": { - "desc": "" + "desc": "Timezone information that may be used elsewhere in the API, such as to set Telegram Business opening hours \u00bb." }, "help.UserInfo": { "desc": "User info" @@ -16444,7 +17673,7 @@ "desc": "Archived stickers" }, "messages.AvailableEffects": { - "desc": "" + "desc": "Full list of usable animated message effects \u00bb." }, "messages.AvailableReactions": { "desc": "Animations and metadata associated with message reactions \u00bb" @@ -16455,6 +17684,9 @@ "messages.BotCallbackAnswer": { "desc": "Callback answer of bot" }, + "messages.BotPreparedInlineMessage": { + "desc": "Represents a prepared inline message saved by a bot, to be sent to the user via a web app \u00bb" + }, "messages.BotResults": { "desc": "Result of a query to an inline bot" }, @@ -16477,7 +17709,7 @@ "desc": "Contains Diffie-Hellman key generation protocol parameters." }, "messages.DialogFilters": { - "desc": "" + "desc": "Folder information" }, "messages.Dialogs": { "desc": "Object contains a list of chats with messages and auxiliary data." @@ -16506,6 +17738,9 @@ "messages.FoundStickerSets": { "desc": "Found stickersets" }, + "messages.FoundStickers": { + "desc": "Found stickers" + }, "messages.HighScores": { "desc": "High scores (in games)" }, @@ -16519,7 +17754,7 @@ "desc": "Inactive chat list" }, "messages.InvitedUsers": { - "desc": "" + "desc": "Contains info about successfully or unsuccessfully invited \u00bb users." }, "messages.MessageEditData": { "desc": "Message edit data for media" @@ -16534,7 +17769,7 @@ "desc": "Object contains information on list of messages with auxiliary data." }, "messages.MyStickers": { - "desc": "" + "desc": "The list of stickersets owned by the current account \u00bb." }, "messages.PeerDialogs": { "desc": "List of dialogs" @@ -16542,8 +17777,11 @@ "messages.PeerSettings": { "desc": "Peer settings" }, + "messages.PreparedInlineMessage": { + "desc": "Represents a prepared inline message received via a bot's mini app, that can be sent to some chats \u00bb" + }, "messages.QuickReplies": { - "desc": "" + "desc": "Info about quick reply shortcuts \u00bb." }, "messages.Reactions": { "desc": "A set of message reactions" @@ -16558,7 +17796,7 @@ "desc": "Saved GIFs" }, "messages.SavedReactionTags": { - "desc": "" + "desc": "List of reaction tag \u00bb names assigned by the user." }, "messages.SearchCounter": { "desc": "Number of results that would be returned by a search" @@ -16596,12 +17834,18 @@ "messages.WebPage": { "desc": "Contains an instant view webpage." }, + "messages.WebPagePreview": { + "desc": "" + }, "payments.BankCardData": { "desc": "Credit card info, provided by the card's bank(s)" }, "payments.CheckedGiftCode": { "desc": "Info about a Telegram Premium Giftcode." }, + "payments.ConnectedStarRefBots": { + "desc": "Active affiliations" + }, "payments.ExportedInvoice": { "desc": "Exported invoice" }, @@ -16620,7 +17864,34 @@ "payments.SavedInfo": { "desc": "Saved payment info" }, + "payments.SavedStarGifts": { + "desc": "" + }, + "payments.StarGiftUpgradePreview": { + "desc": "" + }, + "payments.StarGiftWithdrawalUrl": { + "desc": "" + }, + "payments.StarGifts": { + "desc": "Available gifts \u00bb." + }, + "payments.StarsRevenueAdsAccountUrl": { + "desc": "Contains a URL leading to a page where the user will be able to place ads for the channel/bot, paying using Telegram Stars." + }, + "payments.StarsRevenueStats": { + "desc": "Star revenue statistics, see here \u00bb for more info." + }, + "payments.StarsRevenueWithdrawalUrl": { + "desc": "Contains the URL to use to withdraw Telegram Star revenue." + }, "payments.StarsStatus": { + "desc": "Info about the current Telegram Star subscriptions, balance and transaction history \u00bb." + }, + "payments.SuggestedStarRefBots": { + "desc": "A list of suggested mini apps with available affiliate programs" + }, + "payments.UniqueStarGift": { "desc": "" }, "payments.ValidatedRequestedInfo": { @@ -16663,19 +17934,19 @@ "desc": "A list of peers we are currently boosting, and how many boost slots we have left." }, "smsjobs.EligibilityToJoin": { - "desc": "" + "desc": "SMS jobs eligibility" }, "smsjobs.Status": { - "desc": "" + "desc": "Status" }, "stats.BroadcastRevenueStats": { - "desc": "" + "desc": "Channel revenue ad statistics, see here \u00bb for more info." }, "stats.BroadcastRevenueTransactions": { - "desc": "" + "desc": "Channel ad revenue transactions \u00bb." }, "stats.BroadcastRevenueWithdrawalUrl": { - "desc": "" + "desc": "Contains the URL to use to withdraw channel ad revenue." }, "stats.BroadcastStats": { "desc": "Channel statistics" @@ -16701,6 +17972,9 @@ "stories.AllStories": { "desc": "Full list of active (or active and hidden) stories." }, + "stories.FoundStories": { + "desc": "Stories found using global story search \u00bb." + }, "stories.PeerStories": { "desc": "Active story list of a specific peer." }, @@ -16736,6 +18010,9 @@ }, "users.UserFull": { "desc": "Full user information, with attached context peers for reactions" + }, + "users.Users": { + "desc": "" } } -} \ No newline at end of file +} diff --git a/compiler/api/source/main_api.tl b/compiler/api/source/main_api.tl index 44c9ef033..0f99ce30d 100644 --- a/compiler/api/source/main_api.tl +++ b/compiler/api/source/main_api.tl @@ -1,5 +1,4 @@ // https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/mtproto/scheme/api.tl -// https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/mtproto/scheme/layer.tl /////////////////////////////// ///////// Main application API @@ -35,17 +34,18 @@ inputPhoneContact#f392b7f4 client_id:long phone:string first_name:string last_na inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile; inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile; +inputFileStoryDocument#62dc8b48 id:InputDocument = InputFile; inputMediaEmpty#9664f57f = InputMedia; inputMediaUploadedPhoto#1e287d04 flags:# spoiler:flags.2?true file:InputFile stickers:flags.0?Vector ttl_seconds:flags.1?int = InputMedia; inputMediaPhoto#b3ba0635 flags:# spoiler:flags.1?true id:InputPhoto ttl_seconds:flags.0?int = InputMedia; inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia; inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:string vcard:string = InputMedia; -inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true force_file:flags.4?true spoiler:flags.5?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector stickers:flags.0?Vector ttl_seconds:flags.1?int = InputMedia; -inputMediaDocument#33473058 flags:# spoiler:flags.2?true id:InputDocument ttl_seconds:flags.0?int query:flags.1?string = InputMedia; +inputMediaUploadedDocument#37c9330 flags:# nosound_video:flags.3?true force_file:flags.4?true spoiler:flags.5?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector stickers:flags.0?Vector video_cover:flags.6?InputPhoto video_timestamp:flags.7?int ttl_seconds:flags.1?int = InputMedia; +inputMediaDocument#a8763ab5 flags:# spoiler:flags.2?true id:InputDocument video_cover:flags.3?InputPhoto video_timestamp:flags.4?int ttl_seconds:flags.0?int query:flags.1?string = InputMedia; inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia; inputMediaPhotoExternal#e5bbfe1a flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int = InputMedia; -inputMediaDocumentExternal#fb52dc99 flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int = InputMedia; +inputMediaDocumentExternal#779600f9 flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int video_cover:flags.2?InputPhoto video_timestamp:flags.3?int = InputMedia; inputMediaGame#d33f43f3 id:InputGame = InputMedia; inputMediaInvoice#405fef0d flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:flags.3?string provider_data:DataJSON start_param:flags.1?string extended_media:flags.2?InputMedia = InputMedia; inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia; @@ -53,6 +53,7 @@ inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector s inputMediaDice#e66fbf7b emoticon:string = InputMedia; inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia; inputMediaWebPage#c21b8849 flags:# force_large_media:flags.0?true force_small_media:flags.1?true optional:flags.2?true url:string = InputMedia; +inputMediaPaidMedia#c4103386 flags:# stars_amount:long extended_media:Vector payload:flags.0?string = InputMedia; inputChatPhotoEmpty#1ca48f57 = InputChatPhoto; inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto; @@ -91,7 +92,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType; storage.fileWebp#1081464c = storage.FileType; userEmpty#d3bc4b7a id:long = User; -user#215c4438 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# bot_can_edit:flags2.1?true close_friend:flags2.2?true stories_hidden:flags2.3?true stories_unavailable:flags2.4?true contact_require_premium:flags2.10?true bot_business:flags2.11?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector stories_max_id:flags2.5?int color:flags2.8?PeerColor profile_color:flags2.9?PeerColor = User; +user#20b1422 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# bot_can_edit:flags2.1?true close_friend:flags2.2?true stories_hidden:flags2.3?true stories_unavailable:flags2.4?true contact_require_premium:flags2.10?true bot_business:flags2.11?true bot_has_main_app:flags2.13?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector stories_max_id:flags2.5?int color:flags2.8?PeerColor profile_color:flags2.9?PeerColor bot_active_users:flags2.12?int bot_verification_icon:flags2.14?long send_paid_messages_stars:flags2.15?long = User; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhoto#82d1f706 flags:# has_video:flags.0?true personal:flags.2?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto; @@ -106,11 +107,11 @@ userStatusLastMonth#65899777 flags:# by_me:flags.0?true = UserStatus; chatEmpty#29562865 id:long = Chat; chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chatForbidden#6592a1a7 id:long title:string = Chat; -channel#aadfc8f flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int = Chat; +channel#7482147e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true signature_profiles:flags2.12?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long send_paid_messages_stars:flags2.14?long = Chat; channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull; -channelFull#bbab348d flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull; +channelFull#52d6806b flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?true paid_reactions_available:flags2.16?true stargifts_available:flags2.19?true paid_messages_available:flags2.20?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int = ChatFull; chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant; chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant; @@ -123,15 +124,15 @@ chatPhotoEmpty#37c1011c = ChatPhoto; chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto; messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; -message#94345242 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck = Message; -messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message; +message#eabcdd4d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long = Message; +messageService#d3d28540 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message; messageMediaEmpty#3ded6320 = MessageMedia; messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia; messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia; messageMediaContact#70322949 phone_number:string first_name:string last_name:string vcard:string user_id:long = MessageMedia; messageMediaUnsupported#9f84f49e = MessageMedia; -messageMediaDocument#4cf4d72d flags:# nopremium:flags.3?true spoiler:flags.4?true video:flags.6?true round:flags.7?true voice:flags.8?true document:flags.0?Document alt_document:flags.5?Document ttl_seconds:flags.2?int = MessageMedia; +messageMediaDocument#52d8ccd9 flags:# nopremium:flags.3?true spoiler:flags.4?true video:flags.6?true round:flags.7?true voice:flags.8?true document:flags.0?Document alt_documents:flags.5?Vector video_cover:flags.9?Photo video_timestamp:flags.10?int ttl_seconds:flags.2?int = MessageMedia; messageMediaWebPage#ddf10c3b flags:# force_large_media:flags.0?true force_small_media:flags.1?true manual:flags.3?true safe:flags.4?true webpage:WebPage = MessageMedia; messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia; messageMediaGame#fdb19008 game:Game = MessageMedia; @@ -140,8 +141,9 @@ messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia; messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia; messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int story:flags.0?StoryItem = MessageMedia; -messageMediaGiveaway#daad85b0 flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector countries_iso2:flags.1?Vector prize_description:flags.3?string quantity:int months:int until_date:int = MessageMedia; -messageMediaGiveawayResults#c6991068 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector months:int prize_description:flags.1?string until_date:int = MessageMedia; +messageMediaGiveaway#aa073beb flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector countries_iso2:flags.1?Vector prize_description:flags.3?string quantity:int months:flags.4?int stars:flags.5?long until_date:int = MessageMedia; +messageMediaGiveawayResults#ceaa3ea1 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector months:flags.4?int stars:flags.5?long prize_description:flags.1?string until_date:int = MessageMedia; +messageMediaPaidMedia#a8852491 stars_amount:long extended_media:Vector = MessageMedia; messageActionEmpty#b6aef7b0 = MessageAction; messageActionChatCreate#bd47cbad title:string users:Vector = MessageAction; @@ -157,8 +159,8 @@ messageActionChannelMigrateFrom#ea3948e9 title:string chat_id:long = MessageActi messageActionPinMessage#94bd38ed = MessageAction; messageActionHistoryClear#9fbab604 = MessageAction; messageActionGameScore#92a72876 game_id:long score:int = MessageAction; -messageActionPaymentSentMe#8f31b327 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction; -messageActionPaymentSent#96163f56 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long invoice_slug:flags.0?string = MessageAction; +messageActionPaymentSentMe#ffa00ccc flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge subscription_until_date:flags.4?int = MessageAction; +messageActionPaymentSent#c624b16e flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long invoice_slug:flags.0?string subscription_until_date:flags.4?int = MessageAction; messageActionPhoneCall#80e11a7f flags:# video:flags.2?true call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction; messageActionScreenshotTaken#4792929b = MessageAction; messageActionCustomAction#fae69f56 message:string = MessageAction; @@ -175,17 +177,24 @@ messageActionSetChatTheme#aa786345 emoticon:string = MessageAction; messageActionChatJoinedByRequest#ebbca3cb = MessageAction; messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction; messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction; -messageActionGiftPremium#c83d6aec flags:# currency:string amount:long months:int crypto_currency:flags.0?string crypto_amount:flags.0?long = MessageAction; +messageActionGiftPremium#6c6274fa flags:# currency:string amount:long months:int crypto_currency:flags.0?string crypto_amount:flags.0?long message:flags.1?TextWithEntities = MessageAction; messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction; messageActionTopicEdit#c0944820 flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool hidden:flags.3?Bool = MessageAction; messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction; messageActionRequestedPeer#31518e9b button_id:int peers:Vector = MessageAction; messageActionSetChatWallPaper#5060a3f4 flags:# same:flags.0?true for_both:flags.1?true wallpaper:WallPaper = MessageAction; -messageActionGiftCode#678c2e09 flags:# via_giveaway:flags.0?true unclaimed:flags.2?true boost_peer:flags.1?Peer months:int slug:string currency:flags.2?string amount:flags.2?long crypto_currency:flags.3?string crypto_amount:flags.3?long = MessageAction; -messageActionGiveawayLaunch#332ba9ed = MessageAction; -messageActionGiveawayResults#2a9fadc5 winners_count:int unclaimed_count:int = MessageAction; +messageActionGiftCode#56d03994 flags:# via_giveaway:flags.0?true unclaimed:flags.5?true boost_peer:flags.1?Peer months:int slug:string currency:flags.2?string amount:flags.2?long crypto_currency:flags.3?string crypto_amount:flags.3?long message:flags.4?TextWithEntities = MessageAction; +messageActionGiveawayLaunch#a80f51e4 flags:# stars:flags.0?long = MessageAction; +messageActionGiveawayResults#87e2f155 flags:# stars:flags.0?true winners_count:int unclaimed_count:int = MessageAction; messageActionBoostApply#cc02aa6d boosts:int = MessageAction; messageActionRequestedPeerSentMe#93b31848 button_id:int peers:Vector = MessageAction; +messageActionPaymentRefunded#41b3e202 flags:# peer:Peer currency:string total_amount:long payload:flags.0?bytes charge:PaymentCharge = MessageAction; +messageActionGiftStars#45d5b021 flags:# currency:string amount:long stars:long crypto_currency:flags.0?string crypto_amount:flags.0?long transaction_id:flags.1?string = MessageAction; +messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long transaction_id:string boost_peer:Peer giveaway_msg_id:int = MessageAction; +messageActionStarGift#4717e8a4 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long = MessageAction; +messageActionStarGiftUnique#acdfcb81 flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long = MessageAction; +messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction; +messageActionPaidMessagesPrice#bcd71419 stars:long = MessageAction; dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog; dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; @@ -205,6 +214,7 @@ geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radiu auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode; auth.sentCodeSuccess#2390fe44 authorization:auth.Authorization = auth.SentCode; +auth.sentCodePaymentRequired#d7cef980 store_product:string phone_code_hash:string = auth.SentCode; auth.authorization#2ea2c0d4 flags:# setup_password_required:flags.1?true otherwise_relogin_days:flags.1?int tmp_sessions:flags.0?int future_auth_token:flags.2?bytes user:User = auth.Authorization; auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization; @@ -221,7 +231,7 @@ inputPeerNotifySettings#cacb6ae2 flags:# show_previews:flags.0?Bool silent:flags peerNotifySettings#99622c0c flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int ios_sound:flags.3?NotificationSound android_sound:flags.4?NotificationSound other_sound:flags.5?NotificationSound stories_muted:flags.6?Bool stories_hide_sender:flags.7?Bool stories_ios_sound:flags.8?NotificationSound stories_android_sound:flags.9?NotificationSound stories_other_sound:flags.10?NotificationSound = PeerNotifySettings; -peerSettings#acd66c5e flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true request_chat_broadcast:flags.10?true business_bot_paused:flags.11?true business_bot_can_reply:flags.12?true geo_distance:flags.6?int request_chat_title:flags.9?string request_chat_date:flags.9?int business_bot_id:flags.13?long business_bot_manage_url:flags.13?string = PeerSettings; +peerSettings#f47741f7 flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true request_chat_broadcast:flags.10?true business_bot_paused:flags.11?true business_bot_can_reply:flags.12?true geo_distance:flags.6?int request_chat_title:flags.9?string request_chat_date:flags.9?int business_bot_id:flags.13?long business_bot_manage_url:flags.13?string charge_paid_message_stars:flags.14?long registration_month:flags.15?string phone_country:flags.16?string name_change_date:flags.17?int photo_change_date:flags.18?int = PeerSettings; wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper; wallPaperNoFile#e0804116 id:long flags:# default:flags.1?true dark:flags.4?true settings:flags.2?WallPaperSettings = WallPaper; @@ -237,7 +247,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason; inputReportReasonIllegalDrugs#a8eb2be = ReportReason; inputReportReasonPersonalDetails#9ec7863d = ReportReason; -userFull#cc997720 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int = UserFull; +userFull#99e78045 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true display_gifts_button:flags2.16?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long disallowed_gifts:flags2.15?DisallowedGiftsSettings = UserFull; contact#145ade0b user_id:long mutual:Bool = Contact; @@ -355,7 +365,7 @@ updateFolderPeers#19360dc0 folder_peers:Vector pts:int pts_count:int updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update; updatePeerLocated#b4afcfb0 peers:Vector = Update; updateNewScheduledMessage#39a51dfb message:Message = Update; -updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector = Update; +updateDeleteScheduledMessages#f2a71983 flags:# peer:Peer messages:Vector sent_messages:flags.0?Vector = Update; updateTheme#8216fba3 theme:Theme = Update; updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update; updateLoginToken#564fe691 = Update; @@ -373,7 +383,7 @@ updatePinnedMessages#ed85eab5 flags:# pinned:flags.0?true peer:Peer messages:Vec updatePinnedChannelMessages#5bb98608 flags:# pinned:flags.0?true channel_id:long messages:Vector pts:int pts_count:int = Update; updateChat#f89a6a4e chat_id:long = Update; updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector version:int = Update; -updateGroupCall#14b24500 chat_id:long call:GroupCall = Update; +updateGroupCall#97d64341 flags:# chat_id:flags.0?long call:GroupCall = Update; updatePeerHistoryTTL#bb9bb9a5 flags:# peer:Peer ttl_period:flags.0?int = Update; updateChatParticipant#d087663a flags:# chat_id:long date:int actor_id:long user_id:long prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant invite:flags.2?ExportedChatInvite qts:int = Update; updateChannelParticipant#985d3abb flags:# via_chatlist:flags.3?true channel_id:long date:int actor_id:long user_id:long prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant invite:flags.2?ExportedChatInvite qts:int = Update; @@ -393,7 +403,7 @@ updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update; updateRecentEmojiStatuses#30f443db = Update; updateRecentReactions#6f7863f4 = Update; updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update; -updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update; +updateMessageExtendedMedia#d5a41724 peer:Peer msg_id:int extended_media:Vector = Update; updateChannelPinnedTopic#192efbe3 flags:# pinned:flags.0?true channel_id:long topic_id:int = Update; updateChannelPinnedTopics#fe198602 flags:# channel_id:long order:flags.0?Vector = Update; updateUser#20529438 user_id:long = Update; @@ -423,7 +433,12 @@ updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Messag updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector qts:int = Update; updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update; updateBroadcastRevenueTransactions#dfd961f5 peer:Peer balances:BroadcastRevenueBalances = Update; -updateStarsBalance#fb85198 balance:long = Update; +updateStarsBalance#4e80a379 balance:StarsAmount = Update; +updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update; +updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update; +updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update; +updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update; +updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -530,6 +545,8 @@ inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey; inputPrivacyKeyVoiceMessages#aee69d68 = InputPrivacyKey; inputPrivacyKeyAbout#3823cc40 = InputPrivacyKey; inputPrivacyKeyBirthday#d65a11cc = InputPrivacyKey; +inputPrivacyKeyStarGiftsAutoSave#e1732341 = InputPrivacyKey; +inputPrivacyKeyNoPaidMessages#bdc597b4 = InputPrivacyKey; privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey; privacyKeyChatInvite#500e6dfa = PrivacyKey; @@ -542,6 +559,8 @@ privacyKeyAddedByPhone#42ffd42b = PrivacyKey; privacyKeyVoiceMessages#697f414 = PrivacyKey; privacyKeyAbout#a486b761 = PrivacyKey; privacyKeyBirthday#2000a518 = PrivacyKey; +privacyKeyStarGiftsAutoSave#2ca4fdf8 = PrivacyKey; +privacyKeyNoPaidMessages#17d348d2 = PrivacyKey; inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule; inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule; @@ -553,6 +572,8 @@ inputPrivacyValueAllowChatParticipants#840649cf chats:Vector = InputPrivac inputPrivacyValueDisallowChatParticipants#e94f0f86 chats:Vector = InputPrivacyRule; inputPrivacyValueAllowCloseFriends#2f453e49 = InputPrivacyRule; inputPrivacyValueAllowPremium#77cdc9f1 = InputPrivacyRule; +inputPrivacyValueAllowBots#5a4fcce5 = InputPrivacyRule; +inputPrivacyValueDisallowBots#c4e57915 = InputPrivacyRule; privacyValueAllowContacts#fffe1bac = PrivacyRule; privacyValueAllowAll#65427b82 = PrivacyRule; @@ -564,6 +585,8 @@ privacyValueAllowChatParticipants#6b134e8e chats:Vector = PrivacyRule; privacyValueDisallowChatParticipants#41c87565 chats:Vector = PrivacyRule; privacyValueAllowCloseFriends#f7e8d89b = PrivacyRule; privacyValueAllowPremium#ece9814b = PrivacyRule; +privacyValueAllowBots#21461b5d = PrivacyRule; +privacyValueDisallowBots#f6a5f82f = PrivacyRule; account.privacyRules#50a04e45 rules:Vector chats:Vector users:Vector = account.PrivacyRules; @@ -572,7 +595,7 @@ accountDaysTTL#b8d0afdf days:int = AccountDaysTTL; documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute; documentAttributeAnimated#11b58939 = DocumentAttribute; documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute; -documentAttributeVideo#d38ff1c2 flags:# round_message:flags.0?true supports_streaming:flags.1?true nosound:flags.3?true duration:double w:int h:int preload_prefix_size:flags.2?int = DocumentAttribute; +documentAttributeVideo#43c57c48 flags:# round_message:flags.0?true supports_streaming:flags.1?true nosound:flags.3?true duration:double w:int h:int preload_prefix_size:flags.2?int video_start_ts:flags.4?double video_codec:flags.5?string = DocumentAttribute; documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute; documentAttributeFilename#15590068 file_name:string = DocumentAttribute; documentAttributeHasStickers#9801d2f7 = DocumentAttribute; @@ -590,7 +613,7 @@ messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMess webPageEmpty#211a1788 flags:# id:long url:flags.0?string = WebPage; webPagePending#b0d13e47 flags:# id:long url:flags.0?string date:int = WebPage; -webPage#e89c45b2 flags:# has_large_media:flags.13?true id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector = WebPage; +webPage#e89c45b2 flags:# has_large_media:flags.13?true video_cover_photo:flags.14?true id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector = WebPage; webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage; authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true encrypted_requests_disabled:flags.3?true call_requests_disabled:flags.4?true unconfirmed:flags.5?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; @@ -607,11 +630,11 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; -chatInviteExported#ab4a819 flags:# revoked:flags.0?true permanent:flags.5?true request_needed:flags.6?true link:string admin_id:long date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int requested:flags.7?int title:flags.8?string = ExportedChatInvite; +chatInviteExported#a22cbd96 flags:# revoked:flags.0?true permanent:flags.5?true request_needed:flags.6?true link:string admin_id:long date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int requested:flags.7?int subscription_expired:flags.10?int title:flags.8?string subscription_pricing:flags.9?StarsSubscriptionPricing = ExportedChatInvite; chatInvitePublicJoinRequests#ed107ab7 = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; -chatInvite#cde0ec40 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true request_needed:flags.6?true verified:flags.7?true scam:flags.8?true fake:flags.9?true title:string about:flags.5?string photo:Photo participants_count:int participants:flags.4?Vector color:int = ChatInvite; +chatInvite#5c9d3702 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true request_needed:flags.6?true verified:flags.7?true scam:flags.8?true fake:flags.9?true can_refulfill_subscription:flags.11?true title:string about:flags.5?string photo:Photo participants_count:int participants:flags.4?Vector color:int subscription_pricing:flags.10?StarsSubscriptionPricing subscription_form_id:flags.12?long bot_verification:flags.13?BotVerification = ChatInvite; chatInvitePeek#61695cb0 chat:Chat expires:int = ChatInvite; inputStickerSetEmpty#ffb62b95 = InputStickerSet; @@ -633,7 +656,7 @@ messages.stickerSetNotModified#d3f924eb = messages.StickerSet; botCommand#c27ac8c7 command:string description:string = BotCommand; -botInfo#8f300b57 flags:# user_id:flags.0?long description:flags.1?string description_photo:flags.4?Photo description_document:flags.5?Document commands:flags.2?Vector menu_button:flags.3?BotMenuButton = BotInfo; +botInfo#4d8a0299 flags:# has_preview_medias:flags.6?true user_id:flags.0?long description:flags.1?string description_photo:flags.4?Photo description_document:flags.5?Document commands:flags.2?Vector menu_button:flags.3?BotMenuButton privacy_policy_url:flags.7?string app_settings:flags.8?BotAppSettings verifier_settings:flags.9?BotVerifierSettings = BotInfo; keyboardButton#a2fa4880 text:string = KeyboardButton; keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton; @@ -652,6 +675,7 @@ keyboardButtonWebView#13767230 text:string url:string = KeyboardButton; keyboardButtonSimpleWebView#a0c0505c text:string url:string = KeyboardButton; keyboardButtonRequestPeer#53d7bfd8 text:string button_id:int peer_type:RequestPeerType max_quantity:int = KeyboardButton; inputKeyboardButtonRequestPeer#c9662d05 flags:# name_requested:flags.0?true username_requested:flags.1?true photo_requested:flags.2?true text:string button_id:int peer_type:RequestPeerType max_quantity:int = KeyboardButton; +keyboardButtonCopy#75d2698e text:string copy_text:string = KeyboardButton; keyboardButtonRow#77608b83 buttons:Vector = KeyboardButtonRow; @@ -697,8 +721,8 @@ updates.channelDifference#2064674e flags:# final:flags.0?true pts:int timeout:fl channelMessagesFilterEmpty#94d42ee7 = ChannelMessagesFilter; channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:Vector = ChannelMessagesFilter; -channelParticipant#c00c07c0 user_id:long date:int = ChannelParticipant; -channelParticipantSelf#35a8bfa7 flags:# via_request:flags.0?true user_id:long inviter_id:long date:int = ChannelParticipant; +channelParticipant#cb397619 flags:# user_id:long date:int subscription_until_date:flags.0?int = ChannelParticipant; +channelParticipantSelf#4f607bef flags:# via_request:flags.0?true user_id:long inviter_id:long date:int subscription_until_date:flags.1?int = ChannelParticipant; channelParticipantCreator#2fe601d3 flags:# user_id:long admin_rights:ChatAdminRights rank:flags.0?string = ChannelParticipant; channelParticipantAdmin#34c3bb53 flags:# can_edit:flags.0?true self:flags.1?true user_id:long inviter_id:flags.1?long promoted_by:long date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant; channelParticipantBanned#6df8014e flags:# left:flags.0?true peer:Peer kicked_by:long date:int banned_rights:ChatBannedRights = ChannelParticipant; @@ -768,7 +792,7 @@ auth.sentCodeTypeMissedCall#82006484 prefix:string length:int = auth.SentCodeTyp auth.sentCodeTypeEmailCode#f450f59b flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true email_pattern:string length:int reset_available_period:flags.3?int reset_pending_date:flags.4?int = auth.SentCodeType; auth.sentCodeTypeSetUpEmailRequired#a5491dea flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true = auth.SentCodeType; auth.sentCodeTypeFragmentSms#d9565c39 url:string length:int = auth.SentCodeType; -auth.sentCodeTypeFirebaseSms#13c90f17 flags:# nonce:flags.0?bytes play_integrity_nonce:flags.2?bytes receipt:flags.1?string push_timeout:flags.1?int length:int = auth.SentCodeType; +auth.sentCodeTypeFirebaseSms#9fd736 flags:# nonce:flags.0?bytes play_integrity_project_id:flags.2?long play_integrity_nonce:flags.2?bytes receipt:flags.1?string push_timeout:flags.1?int length:int = auth.SentCodeType; auth.sentCodeTypeSmsWord#a416ac81 flags:# beginning:flags.0?string = auth.SentCodeType; auth.sentCodeTypeSmsPhrase#b37794af flags:# beginning:flags.0?string = auth.SentCodeType; @@ -793,6 +817,7 @@ topPeerCategoryChannels#161d9628 = TopPeerCategory; topPeerCategoryPhoneCalls#1e76a78c = TopPeerCategory; topPeerCategoryForwardUsers#a8406ca9 = TopPeerCategory; topPeerCategoryForwardChats#fbeec0f0 = TopPeerCategory; +topPeerCategoryBotsApp#fd9e7bec = TopPeerCategory; topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector = TopPeerCategoryPeers; @@ -801,7 +826,7 @@ contacts.topPeers#70b772a8 categories:Vector chats:Vector< contacts.topPeersDisabled#b52c939d = contacts.TopPeers; draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage; -draftMessage#3fccf7ef flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo message:string entities:flags.3?Vector media:flags.5?InputMedia date:int = DraftMessage; +draftMessage#2d65321f flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo message:string entities:flags.3?Vector media:flags.5?InputMedia date:int effect:flags.7?long = DraftMessage; messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers; messages.featuredStickers#be382906 flags:# premium:flags.0?true hash:long count:int sets:Vector unread:Vector = messages.FeaturedStickers; @@ -884,12 +909,13 @@ phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason; phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason; phoneCallDiscardReasonHangup#57adc690 = PhoneCallDiscardReason; phoneCallDiscardReasonBusy#faf7e8c9 = PhoneCallDiscardReason; +phoneCallDiscardReasonAllowGroupCall#afe2b839 encrypted_key:bytes = PhoneCallDiscardReason; dataJSON#7d748d04 data:string = DataJSON; labeledPrice#cb296bf8 label:string amount:long = LabeledPrice; -invoice#5db95a15 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true recurring:flags.9?true currency:string prices:Vector max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector terms_url:flags.10?string = Invoice; +invoice#49ee584 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true recurring:flags.9?true currency:string prices:Vector max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector terms_url:flags.10?string subscription_period:flags.11?int = Invoice; paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge; @@ -912,6 +938,7 @@ upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mti payments.paymentForm#a0058751 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true form_id:long bot_id:long title:string description:string photo:flags.5?WebDocument invoice:Invoice provider_id:long url:string native_provider:flags.4?string native_params:flags.4?DataJSON additional_methods:flags.6?Vector saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?Vector users:Vector = payments.PaymentForm; payments.paymentFormStars#7bf6b15c flags:# form_id:long bot_id:long title:string description:string photo:flags.5?WebDocument invoice:Invoice users:Vector = payments.PaymentForm; +payments.paymentFormStarGift#b425cfe1 form_id:long invoice:Invoice = payments.PaymentForm; payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector = payments.ValidatedRequestedInfo; @@ -937,11 +964,11 @@ inputStickerSetItem#32da9e9c flags:# document:InputDocument emoji:string mask_co inputPhoneCall#1e36fded id:long access_hash:long = InputPhoneCall; phoneCallEmpty#5366c915 id:long = PhoneCall; -phoneCallWaiting#c5226f17 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall; -phoneCallRequested#14b0ed0c flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall; -phoneCallAccepted#3660c311 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_b:bytes protocol:PhoneCallProtocol = PhoneCall; -phoneCall#30535af5 flags:# p2p_allowed:flags.5?true video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connections:Vector start_date:int custom_parameters:flags.7?DataJSON = PhoneCall; -phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true video:flags.6?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall; +phoneCallWaiting#eed42858 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long protocol:PhoneCallProtocol receive_date:flags.0?int conference_call:flags.8?InputGroupCall = PhoneCall; +phoneCallRequested#45361c63 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_hash:bytes protocol:PhoneCallProtocol conference_call:flags.8?InputGroupCall = PhoneCall; +phoneCallAccepted#22fd7181 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_b:bytes protocol:PhoneCallProtocol conference_call:flags.8?InputGroupCall = PhoneCall; +phoneCall#3ba5940c flags:# p2p_allowed:flags.5?true video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connections:Vector start_date:int custom_parameters:flags.7?DataJSON conference_call:flags.8?InputGroupCall = PhoneCall; +phoneCallDiscarded#f9d25503 flags:# need_rating:flags.2?true need_debug:flags.3?true video:flags.6?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int conference_call:flags.8?InputGroupCall = PhoneCall; phoneConnection#9cc123c7 flags:# tcp:flags.0?true id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection; phoneConnectionWebrtc#635fe375 flags:# turn:flags.0?true stun:flags.1?true id:long ip:string ipv6:string port:int username:string password:string = PhoneConnection; @@ -1013,12 +1040,14 @@ channelAdminLogEventActionChangeProfilePeerColor#5e477b25 prev_value:PeerColor n channelAdminLogEventActionChangeWallpaper#31bb5d52 prev_value:WallPaper new_value:WallPaper = ChannelAdminLogEventAction; channelAdminLogEventActionChangeEmojiStatus#3ea9feb1 prev_value:EmojiStatus new_value:EmojiStatus = ChannelAdminLogEventAction; channelAdminLogEventActionChangeEmojiStickerSet#46d840ab prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction; +channelAdminLogEventActionToggleSignatureProfiles#60a79c79 new_value:Bool = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantSubExtend#64642db3 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction; channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent; channels.adminLogResults#ed8af74d events:Vector chats:Vector users:Vector = channels.AdminLogResults; -channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true = ChannelAdminLogEventsFilter; +channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true sub_extend:flags.18?true = ChannelAdminLogEventsFilter; popularContact#5ce14175 client_id:long importers:int = PopularContact; @@ -1253,6 +1282,7 @@ themeSettings#fa58b6d4 flags:# message_colors_animated:flags.2?true base_theme:B webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector settings:flags.1?ThemeSettings = WebPageAttribute; webPageAttributeStory#2e94c3e7 flags:# peer:Peer id:int story:flags.0?StoryItem = WebPageAttribute; webPageAttributeStickerSet#50cc03d3 flags:# emojis:flags.0?true text_color:flags.1?true stickers:Vector = WebPageAttribute; +webPageAttributeUniqueStarGift#cf6f6db8 gift:StarGift = WebPageAttribute; messages.votesList#4899484e flags:# count:int votes:Vector chats:Vector users:Vector next_offset:flags.0?string = messages.VotesList; @@ -1260,9 +1290,9 @@ bankCardOpenUrl#f568028a url:string name:string = BankCardOpenUrl; payments.bankCardData#3e24e573 title:string open_urls:Vector = payments.BankCardData; -dialogFilter#5fb5523b flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string color:flags.27?int pinned_peers:Vector include_peers:Vector exclude_peers:Vector = DialogFilter; +dialogFilter#aa472651 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true title_noanimate:flags.28?true id:int title:TextWithEntities emoticon:flags.25?string color:flags.27?int pinned_peers:Vector include_peers:Vector exclude_peers:Vector = DialogFilter; dialogFilterDefault#363293ae = DialogFilter; -dialogFilterChatlist#9fe28ea4 flags:# has_my_invites:flags.26?true id:int title:string emoticon:flags.25?string color:flags.27?int pinned_peers:Vector include_peers:Vector = DialogFilter; +dialogFilterChatlist#96537bd7 flags:# has_my_invites:flags.26?true title_noanimate:flags.28?true id:int title:TextWithEntities emoticon:flags.25?string color:flags.27?int pinned_peers:Vector include_peers:Vector = DialogFilter; dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested; @@ -1293,7 +1323,7 @@ statsGroupTopInviter#535f779d user_id:long invitations:int = StatsGroupTopInvite stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAndPrev messages:StatsAbsValueAndPrev viewers:StatsAbsValueAndPrev posters:StatsAbsValueAndPrev growth_graph:StatsGraph members_graph:StatsGraph new_members_by_source_graph:StatsGraph languages_graph:StatsGraph messages_graph:StatsGraph actions_graph:StatsGraph top_hours_graph:StatsGraph weekdays_graph:StatsGraph top_posters:Vector top_admins:Vector top_inviters:Vector users:Vector = stats.MegagroupStats; -globalPrivacySettings#734c4ccb flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true hide_read_marks:flags.3?true new_noncontact_peers_require_premium:flags.4?true = GlobalPrivacySettings; +globalPrivacySettings#fe41b34f flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true hide_read_marks:flags.3?true new_noncontact_peers_require_premium:flags.4?true display_gifts_button:flags.7?true noncontact_peers_paid_stars:flags.5?long disallowed_gifts:flags.6?DisallowedGiftsSettings = GlobalPrivacySettings; help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector patterns:flags.1?Vector = help.CountryCode; @@ -1318,7 +1348,7 @@ peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked; stats.messageStats#7fe91c14 views_graph:StatsGraph reactions_by_emotion_graph:StatsGraph = stats.MessageStats; groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall; -groupCall#d597650c flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true can_start_video:flags.9?true record_video_active:flags.11?true rtmp_stream:flags.12?true listeners_hidden:flags.13?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int = GroupCall; +groupCall#cdf8d3e3 flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true can_start_video:flags.9?true record_video_active:flags.11?true rtmp_stream:flags.12?true listeners_hidden:flags.13?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int conference_from_call:flags.14?long = GroupCall; inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; @@ -1378,7 +1408,7 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult; account.resetPasswordOk#e926d63e = account.ResetPasswordResult; -sponsoredMessage#bdedf566 flags:# recommended:flags.5?true can_report:flags.12?true random_id:bytes url:string title:string message:string entities:flags.1?Vector photo:flags.6?Photo color:flags.13?PeerColor button_text:string sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage; +sponsoredMessage#4d93a990 flags:# recommended:flags.5?true can_report:flags.12?true random_id:bytes url:string title:string message:string entities:flags.1?Vector photo:flags.6?Photo media:flags.14?MessageMedia color:flags.13?PeerColor button_text:string sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage; messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector chats:Vector users:Vector = messages.SponsoredMessages; messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages; @@ -1401,7 +1431,7 @@ auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes = auth.LoggedOut reactionCount#a3d1cb80 flags:# chosen_order:flags.0?int reaction:Reaction count:int = ReactionCount; -messageReactions#4f2b9479 flags:# min:flags.0?true can_see_list:flags.2?true reactions_as_tags:flags.3?true results:Vector recent_reactions:flags.1?Vector = MessageReactions; +messageReactions#a339f0b flags:# min:flags.0?true can_see_list:flags.2?true reactions_as_tags:flags.3?true results:Vector recent_reactions:flags.1?Vector top_reactors:flags.4?Vector = MessageReactions; messages.messageReactionsList#31bd492d flags:# count:int reactions:Vector chats:Vector users:Vector next_offset:flags.0?string = messages.MessageReactionsList; @@ -1429,9 +1459,7 @@ attachMenuBots#3c4301c0 hash:long bots:Vector users:Vector attachMenuBotsBot#93bf667f bot:AttachMenuBot users:Vector = AttachMenuBotsBot; -webViewResultUrl#c14557c query_id:long url:string = WebViewResult; - -simpleWebViewResultUrl#882f76bb url:string = SimpleWebViewResult; +webViewResultUrl#4d22ff98 flags:# fullsize:flags.1?true fullscreen:flags.2?true query_id:flags.0?long url:string = WebViewResult; webViewMessageSent#c94511c flags:# msg_id:flags.0?InputBotInlineMessageID = WebViewMessageSent; @@ -1459,7 +1487,12 @@ attachMenuPeerTypeBroadcast#7bfbdefc = AttachMenuPeerType; inputInvoiceMessage#c5b56859 peer:InputPeer msg_id:int = InputInvoice; inputInvoiceSlug#c326caef slug:string = InputInvoice; inputInvoicePremiumGiftCode#98986c0d purpose:InputStorePaymentPurpose option:PremiumGiftCodeOption = InputInvoice; -inputInvoiceStars#1da33ad8 option:StarsTopupOption = InputInvoice; +inputInvoiceStars#65f00ce3 purpose:InputStorePaymentPurpose = InputInvoice; +inputInvoiceChatInviteSubscription#34e793f1 hash:string = InputInvoice; +inputInvoiceStarGift#e8625e92 flags:# hide_name:flags.0?true include_upgrade:flags.2?true peer:InputPeer gift_id:long message:flags.1?TextWithEntities = InputInvoice; +inputInvoiceStarGiftUpgrade#4d818d5d flags:# keep_original_details:flags.0?true stargift:InputSavedStarGift = InputInvoice; +inputInvoiceStarGiftTransfer#4a5f5bd9 stargift:InputSavedStarGift to_id:InputPeer = InputInvoice; +inputInvoicePremiumGiftStars#dabab2ef flags:# user_id:InputUser months:int message:flags.0?TextWithEntities = InputInvoice; payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice; @@ -1469,17 +1502,19 @@ help.premiumPromo#5334759c status_text:string status_entities:Vector boost_peer:flags.0?InputPeer currency:string amount:long = InputStorePaymentPurpose; +inputStorePaymentPremiumGiftCode#fb790393 flags:# users:Vector boost_peer:flags.0?InputPeer currency:string amount:long message:flags.1?TextWithEntities = InputStorePaymentPurpose; inputStorePaymentPremiumGiveaway#160544ca flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true boost_peer:InputPeer additional_peers:flags.1?Vector countries_iso2:flags.2?Vector prize_description:flags.4?string random_id:long until_date:int currency:string amount:long = InputStorePaymentPurpose; -inputStorePaymentStars#4f0ee8df flags:# stars:long currency:string amount:long = InputStorePaymentPurpose; - -premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumGiftOption; +inputStorePaymentStarsTopup#dddd0f56 stars:long currency:string amount:long = InputStorePaymentPurpose; +inputStorePaymentStarsGift#1d741ef7 user_id:InputUser stars:long currency:string amount:long = InputStorePaymentPurpose; +inputStorePaymentStarsGiveaway#751f08fa flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true stars:long boost_peer:InputPeer additional_peers:flags.1?Vector countries_iso2:flags.2?Vector prize_description:flags.4?string random_id:long until_date:int currency:string amount:long users:int = InputStorePaymentPurpose; +inputStorePaymentAuthCode#9bb2636d flags:# restore:flags.0?true phone_number:string phone_code_hash:string currency:string amount:long = InputStorePaymentPurpose; paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod; emojiStatusEmpty#2de11aae = EmojiStatus; -emojiStatus#929b619d document_id:long = EmojiStatus; -emojiStatusUntil#fa30a8c7 document_id:long until:int = EmojiStatus; +emojiStatus#e7ff068a flags:# document_id:long until:flags.0?int = EmojiStatus; +emojiStatusCollectible#7184603b flags:# collectible_id:long document_id:long title:string slug:string pattern_document_id:long center_color:int edge_color:int pattern_color:int text_color:int until:flags.0?int = EmojiStatus; +inputEmojiStatusCollectible#7141dbf flags:# collectible_id:long until:flags.0?int = EmojiStatus; account.emojiStatusesNotModified#d08ce645 = account.EmojiStatuses; account.emojiStatuses#90c467d1 hash:long statuses:Vector = account.EmojiStatuses; @@ -1487,6 +1522,7 @@ account.emojiStatuses#90c467d1 hash:long statuses:Vector = account. reactionEmpty#79f5d419 = Reaction; reactionEmoji#1b2286b8 emoticon:string = Reaction; reactionCustomEmoji#8935fc73 document_id:long = Reaction; +reactionPaid#523da4eb = Reaction; chatReactionsNone#eafc32bc = ChatReactions; chatReactionsAll#52928bca flags:# allow_custom:flags.0?true = ChatReactions; @@ -1561,8 +1597,6 @@ botApp#95fcd1d6 flags:# id:long access_hash:long short_name:string title:string messages.botApp#eb50adf5 flags:# inactive:flags.0?true request_write_access:flags.1?true has_settings:flags.2?true app:BotApp = messages.BotApp; -appWebViewResultUrl#3c1b4f0d url:string = AppWebViewResult; - inlineBotWebView#b57295d5 text:string url:string = InlineBotWebView; readParticipantDate#4a4ff172 user_id:long date:int = ReadParticipantDate; @@ -1576,7 +1610,7 @@ chatlists.exportedChatlistInvite#10e6e3a6 filter:DialogFilter invite:ExportedCha chatlists.exportedInvites#10ab6dc7 invites:Vector chats:Vector users:Vector = chatlists.ExportedInvites; chatlists.chatlistInviteAlready#fa87f659 filter_id:int missing_peers:Vector already_peers:Vector chats:Vector users:Vector = chatlists.ChatlistInvite; -chatlists.chatlistInvite#1dcd839d flags:# title:string emoticon:flags.0?string peers:Vector chats:Vector users:Vector = chatlists.ChatlistInvite; +chatlists.chatlistInvite#f10ece2f flags:# title_noanimate:flags.1?true title:TextWithEntities emoticon:flags.0?string peers:Vector chats:Vector users:Vector = chatlists.ChatlistInvite; chatlists.chatlistUpdates#93bd878d missing_peers:Vector chats:Vector users:Vector = chatlists.ChatlistUpdates; @@ -1612,14 +1646,17 @@ exportedStoryLink#3fc9053b link:string = ExportedStoryLink; storiesStealthMode#712e27fd flags:# active_until_date:flags.0?int cooldown_until_date:flags.1?int = StoriesStealthMode; -mediaAreaCoordinates#3d1ea4e x:double y:double w:double h:double rotation:double = MediaAreaCoordinates; +mediaAreaCoordinates#cfc9e002 flags:# x:double y:double w:double h:double rotation:double radius:flags.0?double = MediaAreaCoordinates; mediaAreaVenue#be82db9c coordinates:MediaAreaCoordinates geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MediaArea; inputMediaAreaVenue#b282217f coordinates:MediaAreaCoordinates query_id:long result_id:string = MediaArea; -mediaAreaGeoPoint#df8b3b22 coordinates:MediaAreaCoordinates geo:GeoPoint = MediaArea; +mediaAreaGeoPoint#cad5452d flags:# coordinates:MediaAreaCoordinates geo:GeoPoint address:flags.0?GeoPointAddress = MediaArea; mediaAreaSuggestedReaction#14455871 flags:# dark:flags.0?true flipped:flags.1?true coordinates:MediaAreaCoordinates reaction:Reaction = MediaArea; mediaAreaChannelPost#770416af coordinates:MediaAreaCoordinates channel_id:long msg_id:int = MediaArea; inputMediaAreaChannelPost#2271f2bf coordinates:MediaAreaCoordinates channel:InputChannel msg_id:int = MediaArea; +mediaAreaUrl#37381085 coordinates:MediaAreaCoordinates url:string = MediaArea; +mediaAreaWeather#49a6549c coordinates:MediaAreaCoordinates emoji:string temperature_c:double color:int = MediaArea; +mediaAreaStarGift#5787686d coordinates:MediaAreaCoordinates slug:string = MediaArea; peerStories#9a35e999 flags:# peer:Peer max_read_id:flags.0?int stories:Vector = PeerStories; @@ -1632,11 +1669,12 @@ premiumGiftCodeOption#257e962b flags:# users:int months:int store_product:flags. payments.checkedGiftCode#284a1096 flags:# via_giveaway:flags.2?true from_id:flags.4?Peer giveaway_msg_id:flags.3?int to_id:flags.0?long date:int months:int used_date:flags.1?int chats:Vector users:Vector = payments.CheckedGiftCode; payments.giveawayInfo#4367daa0 flags:# participating:flags.0?true preparing_results:flags.3?true start_date:int joined_too_early_date:flags.1?int admin_disallowed_chat_id:flags.2?long disallowed_country:flags.4?string = payments.GiveawayInfo; -payments.giveawayInfoResults#cd5570 flags:# winner:flags.0?true refunded:flags.1?true start_date:int gift_code_slug:flags.0?string finish_date:int winners_count:int activated_count:int = payments.GiveawayInfo; +payments.giveawayInfoResults#e175e66f flags:# winner:flags.0?true refunded:flags.1?true start_date:int gift_code_slug:flags.3?string stars_prize:flags.4?long finish_date:int winners_count:int activated_count:flags.2?int = payments.GiveawayInfo; prepaidGiveaway#b2539d54 id:long months:int quantity:int date:int = PrepaidGiveaway; +prepaidStarsGiveaway#9a9d77e0 id:long stars:long quantity:int boosts:int date:int = PrepaidGiveaway; -boost#2a1c8c71 flags:# gift:flags.1?true giveaway:flags.2?true unclaimed:flags.3?true id:string user_id:flags.0?long giveaway_msg_id:flags.2?int date:int expires:int used_gift_slug:flags.4?string multiplier:flags.5?int = Boost; +boost#4b3e14d6 flags:# gift:flags.1?true giveaway:flags.2?true unclaimed:flags.3?true id:string user_id:flags.0?long giveaway_msg_id:flags.2?int date:int expires:int used_gift_slug:flags.4?string multiplier:flags.5?int stars:flags.6?long = Boost; premium.boostsList#86f8613c flags:# count:int boosts:Vector next_offset:flags.0?string users:Vector = premium.BoostsList; @@ -1728,7 +1766,7 @@ inputQuickReplyShortcutId#1190cf1 shortcut_id:int = InputQuickReplyShortcut; messages.quickReplies#c68d6695 quick_replies:Vector messages:Vector chats:Vector users:Vector = messages.QuickReplies; messages.quickRepliesNotModified#5f91eb5b = messages.QuickReplies; -connectedBot#bd068601 flags:# can_reply:flags.0?true bot_id:long recipients:BusinessBotRecipients = ConnectedBot; +connectedBot#cd64636c flags:# bot_id:long recipients:BusinessBotRecipients rights:BusinessBotRights = ConnectedBot; account.connectedBots#17d7f87b connected_bots:Vector users:Vector = account.ConnectedBots; @@ -1736,7 +1774,7 @@ messages.dialogFilters#2ad93719 flags:# tags_enabled:flags.0?true filters:Vector birthday#6c8e1e06 flags:# day:int month:int year:flags.0?int = Birthday; -botBusinessConnection#896433b4 flags:# can_reply:flags.0?true disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int = BotBusinessConnection; +botBusinessConnection#8f34b2f5 flags:# disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int rights:flags.2?BusinessBotRights = BotBusinessConnection; inputBusinessIntro#9c469cd flags:# title:string description:string sticker:flags.0?InputDocument = InputBusinessIntro; @@ -1794,7 +1832,7 @@ reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom; reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings; -broadcastRevenueBalances#8438f1c6 current_balance:long available_balance:long overall_revenue:long = BroadcastRevenueBalances; +broadcastRevenueBalances#c3ff71e7 flags:# withdrawal_enabled:flags.0?true current_balance:long available_balance:long overall_revenue:long = BroadcastRevenueBalances; availableEffect#93c3e27e flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect; @@ -1809,12 +1847,125 @@ starsTransactionPeerPlayMarket#7b560a0b = StarsTransactionPeer; starsTransactionPeerPremiumBot#250dbaf8 = StarsTransactionPeer; starsTransactionPeerFragment#e92fd902 = StarsTransactionPeer; starsTransactionPeer#d80da15d peer:Peer = StarsTransactionPeer; +starsTransactionPeerAds#60682812 = StarsTransactionPeer; +starsTransactionPeerAPI#f9677aad = StarsTransactionPeer; starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption; -starsTransaction#cc7079b2 flags:# refund:flags.3?true id:string stars:long date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument = StarsTransaction; +starsTransaction#a39fd94a flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true stargift_upgrade:flags.18?true id:string stars:StarsAmount date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector subscription_period:flags.12?int giveaway_post_id:flags.13?int stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount paid_messages:flags.19?int premium_gift_months:flags.20?int = StarsTransaction; + +payments.starsStatus#6c9ce8ed flags:# balance:StarsAmount subscriptions:flags.1?Vector subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector next_offset:flags.0?string chats:Vector users:Vector = payments.StarsStatus; + +foundStory#e87acbc0 peer:Peer story:StoryItem = FoundStory; + +stories.foundStories#e2de7737 flags:# count:int stories:Vector next_offset:flags.0?string chats:Vector users:Vector = stories.FoundStories; + +geoPointAddress#de4c5d93 flags:# country_iso2:string state:flags.0?string city:flags.1?string street:flags.2?string = GeoPointAddress; + +starsRevenueStatus#febe5491 flags:# withdrawal_enabled:flags.0?true current_balance:StarsAmount available_balance:StarsAmount overall_revenue:StarsAmount next_withdrawal_at:flags.1?int = StarsRevenueStatus; + +payments.starsRevenueStats#c92bb73b revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats; + +payments.starsRevenueWithdrawalUrl#1dab80b7 url:string = payments.StarsRevenueWithdrawalUrl; + +payments.starsRevenueAdsAccountUrl#394e7f21 url:string = payments.StarsRevenueAdsAccountUrl; + +inputStarsTransaction#206ae6d1 flags:# refund:flags.0?true id:string = InputStarsTransaction; + +starsGiftOption#5e0589f1 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsGiftOption; + +bots.popularAppBots#1991b13b flags:# next_offset:flags.0?string users:Vector = bots.PopularAppBots; + +botPreviewMedia#23e91ba3 date:int media:MessageMedia = BotPreviewMedia; + +bots.previewInfo#ca71d64 media:Vector lang_codes:Vector = bots.PreviewInfo; + +starsSubscriptionPricing#5416d58 period:int amount:long = StarsSubscriptionPricing; + +starsSubscription#2e6eab1a flags:# canceled:flags.0?true can_refulfill:flags.1?true missing_balance:flags.2?true bot_canceled:flags.7?true id:string peer:Peer until_date:int pricing:StarsSubscriptionPricing chat_invite_hash:flags.3?string title:flags.4?string photo:flags.5?WebDocument invoice_slug:flags.6?string = StarsSubscription; + +messageReactor#4ba3a95a flags:# top:flags.0?true my:flags.1?true anonymous:flags.2?true peer_id:flags.3?Peer count:int = MessageReactor; + +starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true stars:long yearly_boosts:int store_product:flags.2?string currency:string amount:long winners:Vector = StarsGiveawayOption; + +starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption; + +starGift#2cc73c8 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long = StarGift; +starGiftUnique#5c62d151 flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector availability_issued:int availability_total:int gift_address:flags.3?string = StarGift; + +payments.starGiftsNotModified#a388a368 = payments.StarGifts; +payments.starGifts#901689ea hash:int gifts:Vector = payments.StarGifts; + +messageReportOption#7903e3d9 text:string option:bytes = MessageReportOption; + +reportResultChooseOption#f0e4e0b6 title:string options:Vector = ReportResult; +reportResultAddComment#6f09ac31 flags:# optional:flags.0?true option:bytes = ReportResult; +reportResultReported#8db33c4b = ReportResult; + +messages.botPreparedInlineMessage#8ecf0511 id:string expire_date:int = messages.BotPreparedInlineMessage; + +messages.preparedInlineMessage#ff57708d query_id:long result:BotInlineResult peer_types:Vector cache_time:int users:Vector = messages.PreparedInlineMessage; + +botAppSettings#c99b1950 flags:# placeholder_path:flags.0?bytes background_color:flags.1?int background_dark_color:flags.2?int header_color:flags.3?int header_dark_color:flags.4?int = BotAppSettings; + +starRefProgram#dd0c66f2 flags:# bot_id:long commission_permille:int duration_months:flags.0?int end_date:flags.1?int daily_revenue_per_user:flags.2?StarsAmount = StarRefProgram; + +connectedBotStarRef#19a13f71 flags:# revoked:flags.1?true url:string date:int bot_id:long commission_permille:int duration_months:flags.0?int participants:long revenue:long = ConnectedBotStarRef; + +payments.connectedStarRefBots#98d5ea1d count:int connected_bots:Vector users:Vector = payments.ConnectedStarRefBots; + +payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector users:Vector next_offset:flags.0?string = payments.SuggestedStarRefBots; + +starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount; + +messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers; +messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector = messages.FoundStickers; + +botVerifierSettings#b0cd6617 flags:# can_modify_custom_description:flags.1?true icon:long company:string custom_description:flags.0?string = BotVerifierSettings; + +botVerification#f93cd45c bot_id:long icon:long description:string = BotVerification; + +starGiftAttributeModel#39d99013 name:string document:Document rarity_permille:int = StarGiftAttribute; +starGiftAttributePattern#13acff19 name:string document:Document rarity_permille:int = StarGiftAttribute; +starGiftAttributeBackdrop#94271762 name:string center_color:int edge_color:int pattern_color:int text_color:int rarity_permille:int = StarGiftAttribute; +starGiftAttributeOriginalDetails#e0bff26c flags:# sender_id:flags.0?Peer recipient_id:Peer date:int message:flags.1?TextWithEntities = StarGiftAttribute; + +payments.starGiftUpgradePreview#167bd90b sample_attributes:Vector = payments.StarGiftUpgradePreview; + +users.users#62d706b8 users:Vector = users.Users; +users.usersSlice#315a4974 count:int users:Vector = users.Users; + +payments.uniqueStarGift#caa2f60b gift:StarGift users:Vector = payments.UniqueStarGift; + +messages.webPagePreview#b53e8b21 media:MessageMedia users:Vector = messages.WebPagePreview; + +savedStarGift#6056dba5 flags:# name_hidden:flags.0?true unsaved:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true pinned_to_top:flags.12?true from_id:flags.1?Peer date:int gift:StarGift message:flags.2?TextWithEntities msg_id:flags.3?int saved_id:flags.11?long convert_stars:flags.4?long upgrade_stars:flags.6?long can_export_at:flags.7?int transfer_stars:flags.8?long = SavedStarGift; + +payments.savedStarGifts#95f389b1 flags:# count:int chat_notifications_enabled:flags.1?Bool gifts:Vector next_offset:flags.0?string chats:Vector users:Vector = payments.SavedStarGifts; + +inputSavedStarGiftUser#69279795 msg_id:int = InputSavedStarGift; +inputSavedStarGiftChat#f101aa7f peer:InputPeer saved_id:long = InputSavedStarGift; + +payments.starGiftWithdrawalUrl#84aa3a9c url:string = payments.StarGiftWithdrawalUrl; + +paidReactionPrivacyDefault#206ad49e = PaidReactionPrivacy; +paidReactionPrivacyAnonymous#1f0c1ad9 = PaidReactionPrivacy; +paidReactionPrivacyPeer#dc6cfcf0 peer:InputPeer = PaidReactionPrivacy; + +account.paidMessagesRevenue#1e109708 stars_amount:long = account.PaidMessagesRevenue; + +requirementToContactEmpty#50a9839 = RequirementToContact; +requirementToContactPremium#e581e4e9 = RequirementToContact; +requirementToContactPaidMessages#b4f67e93 stars_amount:long = RequirementToContact; + +businessBotRights#a0624cf7 flags:# reply:flags.0?true read_messages:flags.1?true delete_sent_messages:flags.2?true delete_received_messages:flags.3?true edit_name:flags.4?true edit_bio:flags.5?true edit_profile_photo:flags.6?true edit_username:flags.7?true view_gifts:flags.8?true sell_gifts:flags.9?true change_gift_settings:flags.10?true transfer_and_upgrade_gifts:flags.11?true transfer_stars:flags.12?true manage_stories:flags.13?true = BusinessBotRights; + +disallowedGiftsSettings#71f276c4 flags:# disallow_unlimited_stargifts:flags.0?true disallow_limited_stargifts:flags.1?true disallow_unique_stargifts:flags.2?true disallow_premium_gifts:flags.3?true = DisallowedGiftsSettings; + +sponsoredPeer#c69708d3 flags:# random_id:bytes peer:Peer sponsor_info:flags.0?string additional_info:flags.1?string = SponsoredPeer; -payments.starsStatus#8cf4ee60 flags:# balance:long history:Vector next_offset:flags.0?string chats:Vector users:Vector = payments.StarsStatus; +contacts.sponsoredPeersEmpty#ea32b4b1 = contacts.SponsoredPeers; +contacts.sponsoredPeers#eb032884 peers:Vector chats:Vector users:Vector = contacts.SponsoredPeers; ---functions--- @@ -1828,6 +1979,7 @@ invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X; invokeWithBusinessConnection#dd289f8e {X:Type} connection_id:string query:!X = X; invokeWithGooglePlayIntegrity#1df92984 {X:Type} nonce:string token:string query:!X = X; invokeWithApnsSecret#0dae54f8 {X:Type} nonce:string secret:string query:!X = X; +invokeWithReCaptcha#adbb0f94 {X:Type} token:string query:!X = X; auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode; auth.signUp#aac7b717 flags:# no_joined_notifications:flags.0?true phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization; @@ -1949,7 +2101,7 @@ account.updateBusinessWorkHours#4b00e066 flags:# business_work_hours:flags.0?Bus account.updateBusinessLocation#9e6b131a flags:# geo_point:flags.1?InputGeoPoint address:flags.0?string = Bool; account.updateBusinessGreetingMessage#66cdafc4 flags:# message:flags.0?InputBusinessGreetingMessage = Bool; account.updateBusinessAwayMessage#a26a7fa5 flags:# message:flags.0?InputBusinessAwayMessage = Bool; -account.updateConnectedBot#43d8521d flags:# can_reply:flags.0?true deleted:flags.1?true bot:InputUser recipients:InputBusinessBotRecipients = Updates; +account.updateConnectedBot#66a08c7e flags:# deleted:flags.1?true rights:flags.0?BusinessBotRights bot:InputUser recipients:InputBusinessBotRecipients = Updates; account.getConnectedBots#4ea4c80f = account.ConnectedBots; account.getBotBusinessConnection#76a86270 connection_id:string = Updates; account.updateBusinessIntro#a614d034 flags:# intro:flags.0?InputBusinessIntro = Bool; @@ -1965,11 +2117,14 @@ account.updatePersonalChannel#d94305e0 channel:InputChannel = Bool; account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool; account.getReactionsNotifySettings#6dd654c = ReactionsNotifySettings; account.setReactionsNotifySettings#316ce548 settings:ReactionsNotifySettings = ReactionsNotifySettings; +account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses; +account.addNoPaidMessagesException#6f688aa7 flags:# refund_charged:flags.0?true user_id:InputUser = Bool; +account.getPaidMessagesRevenue#f1266f38 user_id:InputUser = account.PaidMessagesRevenue; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#b60f5918 id:InputUser = users.UserFull; users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector = Bool; -users.getIsPremiumRequiredToContact#a622aa10 id:Vector = Vector; +users.getRequirementsToContact#d89a83a3 id:Vector = Vector; contacts.getContactIDs#7adc669d hash:long = Vector; contacts.getStatuses#c4a353ee = Vector; @@ -1981,8 +2136,8 @@ contacts.block#2e2e8734 flags:# my_stories_from:flags.0?true id:InputPeer = Bool contacts.unblock#b550d328 flags:# my_stories_from:flags.0?true id:InputPeer = Bool; contacts.getBlocked#9a868f80 flags:# my_stories_from:flags.0?true offset:int limit:int = contacts.Blocked; contacts.search#11f812d8 q:string limit:int = contacts.Found; -contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer; -contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:long = contacts.TopPeers; +contacts.resolveUsername#725afbbc flags:# username:string referer:flags.0?string = contacts.ResolvedPeer; +contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true bots_app:flags.16?true offset:int limit:int hash:long = contacts.TopPeers; contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool; contacts.resetSaved#879537f1 = Bool; contacts.getSaved#82f1e39f = Vector; @@ -1997,6 +2152,7 @@ contacts.importContactToken#13005788 token:string = User; contacts.editCloseFriends#ba6705f0 id:Vector = Bool; contacts.setBlocked#94c65c76 flags:# my_stories_from:flags.0?true id:Vector limit:int = Bool; contacts.getBirthdays#daeda864 = contacts.ContactBirthdays; +contacts.getSponsoredPeers#b6c8c393 q:string = contacts.SponsoredPeers; messages.getMessages#63c66506 id:Vector = messages.Messages; messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs; @@ -2007,12 +2163,12 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector = messages.AffectedMessages; messages.receivedMessages#5a954c0 max_id:int = Vector; messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool; -messages.sendMessage#983f9745 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long = Updates; -messages.sendMedia#7852834e flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long = Updates; -messages.forwardMessages#d5039208 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates; +messages.sendMessage#fbf2340a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates; +messages.sendMedia#a550cd78 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates; +messages.forwardMessages#bb9fa475 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true allow_paid_floodskip:flags.19?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut video_timestamp:flags.20?int allow_paid_stars:flags.21?long = Updates; messages.reportSpam#cf1592db peer:InputPeer = Bool; messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings; -messages.report#8953ab4e peer:InputPeer id:Vector reason:ReportReason message:string = Bool; +messages.report#fc78af9b peer:InputPeer id:Vector option:bytes message:string = ReportResult; messages.getChats#49e9528f id:Vector = messages.Chats; messages.getFullChat#aeb00b34 chat_id:long = messages.ChatFull; messages.editChatTitle#73783ffd chat_id:long title:string = Updates; @@ -2034,8 +2190,8 @@ messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool; messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages; messages.getStickers#d5a5d3a1 emoticon:string hash:long = messages.Stickers; messages.getAllStickers#b8a0a1a8 hash:long = messages.AllStickers; -messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; -messages.exportChatInvite#a02ce5d5 flags:# legacy_revoke_permanent:flags.2?true request_needed:flags.3?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int title:flags.4?string = ExportedChatInvite; +messages.getWebPagePreview#570d6f6f flags:# message:string entities:flags.3?Vector = messages.WebPagePreview; +messages.exportChatInvite#a455de90 flags:# legacy_revoke_permanent:flags.2?true request_needed:flags.3?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int title:flags.4?string subscription_pricing:flags.5?StarsSubscriptionPricing = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#c8a0ec74 stickerset:InputStickerSet hash:int = messages.StickerSet; @@ -2045,21 +2201,21 @@ messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_par messages.getMessagesViews#5784d3e1 peer:InputPeer id:Vector increment:Bool = messages.MessageViews; messages.editChatAdmin#a85bd1c2 chat_id:long user_id:InputUser is_admin:Bool = Bool; messages.migrateChat#a2875319 chat_id:long = Updates; -messages.searchGlobal#4bc6589a flags:# broadcasts_only:flags.1?true folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; +messages.searchGlobal#4bc6589a flags:# broadcasts_only:flags.1?true groups_only:flags.2?true users_only:flags.3?true folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; messages.reorderStickerSets#78337739 flags:# masks:flags.0?true emojis:flags.1?true order:Vector = Bool; messages.getDocumentByHash#b1f2061f sha256:bytes size:long mime_type:string = Document; messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs; messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool; messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults; messages.setInlineBotResults#bb12a419 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM switch_webview:flags.4?InlineBotWebView = Bool; -messages.sendInlineBotResult#3ebee86a flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to:flags.0?InputReplyTo random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates; +messages.sendInlineBotResult#c0cf7646 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to:flags.0?InputReplyTo random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut allow_paid_stars:flags.21?long = Updates; messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData; messages.editMessage#dfd14005 flags:# no_webpage:flags.1?true invert_media:flags.16?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.15?int quick_reply_shortcut_id:flags.17?int = Updates; messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true invert_media:flags.16?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Bool; messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer; messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool; messages.getPeerDialogs#e470bcfd peers:Vector = messages.PeerDialogs; -messages.saveDraft#7ff3b806 flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo peer:InputPeer message:string entities:flags.3?Vector media:flags.5?InputMedia = Bool; +messages.saveDraft#d372c5ce flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo peer:InputPeer message:string entities:flags.3?Vector media:flags.5?InputMedia effect:flags.7?long = Bool; messages.getAllDrafts#6a3f8d65 = Updates; messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers; messages.readFeaturedStickers#5b118126 id:Vector = Bool; @@ -2087,7 +2243,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool; messages.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory; messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages; -messages.sendMultiMedia#37b74355 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long = Updates; +messages.sendMultiMedia#1bf89d74 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates; messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile; messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets; messages.getSplitRanges#1cff7e08 = Vector; @@ -2135,7 +2291,7 @@ messages.editExportedChatInvite#bdca2f75 flags:# revoked:flags.2?true peer:Input messages.deleteRevokedExportedChatInvites#56987bd5 peer:InputPeer admin_id:InputUser = Bool; messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithInvites; -messages.getChatInviteImporters#df04dd4e flags:# requested:flags.0?true peer:InputPeer link:flags.1?string q:flags.2?string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; +messages.getChatInviteImporters#df04dd4e flags:# requested:flags.0?true subscription_expired:flags.3?true peer:InputPeer link:flags.1?string q:flags.2?string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates; messages.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer; messages.setChatTheme#e63be13f peer:InputPeer emoticon:string = Updates; @@ -2149,7 +2305,7 @@ messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool; messages.sendReaction#d30d78d4 flags:# big:flags.1?true add_to_recent:flags.2?true peer:InputPeer msg_id:int reaction:flags.0?Vector = Updates; messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector = Updates; messages.getMessageReactionsList#461b3f48 flags:# peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = messages.MessageReactionsList; -messages.setChatAvailableReactions#5a150bd4 flags:# peer:InputPeer available_reactions:ChatReactions reactions_limit:flags.0?int = Updates; +messages.setChatAvailableReactions#864b2581 flags:# peer:InputPeer available_reactions:ChatReactions reactions_limit:flags.0?int paid_enabled:flags.1?Bool = Updates; messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions; messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool; messages.translateText#63183030 flags:# peer:flags.0?InputPeer id:flags.0?Vector text:flags.1?Vector to_lang:string = messages.TranslatedText; @@ -2159,9 +2315,9 @@ messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = mes messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots; messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot; messages.toggleBotInAttachMenu#69f59d69 flags:# write_allowed:flags.0?true bot:InputUser enabled:Bool = Bool; -messages.requestWebView#269dc2c1 flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to:flags.0?InputReplyTo send_as:flags.13?InputPeer = WebViewResult; +messages.requestWebView#269dc2c1 flags:# from_bot_menu:flags.4?true silent:flags.5?true compact:flags.7?true fullscreen:flags.8?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to:flags.0?InputReplyTo send_as:flags.13?InputPeer = WebViewResult; messages.prolongWebView#b0d81a83 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to:flags.0?InputReplyTo send_as:flags.13?InputPeer = Bool; -messages.requestSimpleWebView#1a46500a flags:# from_switch_webview:flags.1?true from_side_menu:flags.2?true bot:InputUser url:flags.3?string start_param:flags.4?string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult; +messages.requestSimpleWebView#413a3e73 flags:# from_switch_webview:flags.1?true from_side_menu:flags.2?true compact:flags.7?true fullscreen:flags.8?true bot:InputUser url:flags.3?string start_param:flags.4?string theme_params:flags.0?DataJSON platform:string = WebViewResult; messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent; messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates; messages.transcribeAudio#269e9a49 peer:InputPeer msg_id:int = messages.TranscribedAudio; @@ -2183,7 +2339,7 @@ messages.getEmojiProfilePhotoGroups#21a548f3 hash:int = messages.EmojiGroups; messages.searchCustomEmoji#2c11c0d7 emoticon:string hash:long = EmojiList; messages.togglePeerTranslations#e47cb579 flags:# disabled:flags.0?true peer:InputPeer = Bool; messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp; -messages.requestAppWebView#8c5a3b3c flags:# write_allowed:flags.0?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = AppWebViewResult; +messages.requestAppWebView#53618bce flags:# write_allowed:flags.0?true compact:flags.7?true fullscreen:flags.8?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = WebViewResult; messages.setChatWallPaper#8ffacae1 flags:# for_both:flags.3?true revert:flags.4?true peer:InputPeer wallpaper:flags.0?InputWallPaper settings:flags.2?WallPaperSettings id:flags.1?int = Updates; messages.searchEmojiStickerSets#92b4494c flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets; messages.getSavedDialogs#5381d21a flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.SavedDialogs; @@ -2211,6 +2367,18 @@ messages.getAvailableEffects#dea20a39 hash:int = messages.AvailableEffects; messages.editFactCheck#589ee75 peer:InputPeer msg_id:int text:TextWithEntities = Updates; messages.deleteFactCheck#d1da940c peer:InputPeer msg_id:int = Updates; messages.getFactCheck#b9cdc5ee peer:InputPeer msg_id:Vector = Vector; +messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true fullscreen:flags.8?true peer:InputPeer bot:InputUser start_param:flags.1?string theme_params:flags.0?DataJSON platform:string = WebViewResult; +messages.sendPaidReaction#58bbcb50 flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?PaidReactionPrivacy = Updates; +messages.togglePaidReactionPrivacy#435885b5 peer:InputPeer msg_id:int private:PaidReactionPrivacy = Bool; +messages.getPaidReactionPrivacy#472455aa = Updates; +messages.viewSponsoredMessage#269e3643 random_id:bytes = Bool; +messages.clickSponsoredMessage#8235057e flags:# media:flags.0?true fullscreen:flags.1?true random_id:bytes = Bool; +messages.reportSponsoredMessage#12cbf0c4 random_id:bytes option:bytes = channels.SponsoredMessageReportResult; +messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages; +messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult user_id:InputUser peer_types:flags.0?Vector = messages.BotPreparedInlineMessage; +messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage; +messages.searchStickers#29b1c66a flags:# emojis:flags.0?true q:string emoticon:string lang_code:Vector offset:int limit:int hash:long = messages.FoundStickers; +messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector = Bool; updates.getState#edd4882a = updates.State; updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference; @@ -2276,7 +2444,7 @@ channels.leaveChannel#f836aa95 channel:InputChannel = Updates; channels.inviteToChannel#c9e33d54 channel:InputChannel users:Vector = messages.InvitedUsers; channels.deleteChannel#c0111fe3 channel:InputChannel = Updates; channels.exportMessageLink#e63fadeb flags:# grouped:flags.0?true thread:flags.1?true channel:InputChannel id:int = ExportedMessageLink; -channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates; +channels.toggleSignatures#418d549c flags:# signatures_enabled:flags.0?true profiles_enabled:flags.1?true channel:InputChannel = Updates; channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true for_personal:flags.2?true = messages.Chats; channels.editBanned#96e6cd81 channel:InputChannel participant:InputPeer banned_rights:ChatBannedRights = Updates; channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector max_id:long min_id:long limit:int = channels.AdminLogResults; @@ -2292,9 +2460,7 @@ channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint addr channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates; channels.getInactiveChannels#11e831ee = messages.InactiveChats; channels.convertToGigagroup#b290c69 channel:InputChannel = Updates; -channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bool; -channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages; -channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers; +channels.getSendAs#e785a43f flags:# for_paid_reactions:flags.0?true peer:InputPeer = channels.SendAsPeers; channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory; channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates; channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates; @@ -2312,16 +2478,15 @@ channels.reorderPinnedForumTopics#2950a18f flags:# force:flags.0?true channel:In channels.toggleAntiSpam#68f3e4eb channel:InputChannel enabled:Bool = Updates; channels.reportAntiSpamFalsePositive#a850a693 channel:InputChannel msg_id:int = Bool; channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates; -channels.clickSponsoredMessage#18afbc93 channel:InputChannel random_id:bytes = Bool; channels.updateColor#d8aa3671 flags:# for_profile:flags.1?true channel:InputChannel color:flags.2?int background_emoji_id:flags.0?long = Updates; channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates; channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel = messages.Chats; channels.updateEmojiStatus#f0d3e6a8 channel:InputChannel emoji_status:EmojiStatus = Updates; channels.setBoostsToUnblockRestrictions#ad399cee channel:InputChannel boosts:int = Updates; channels.setEmojiStickers#3cd930b7 channel:InputChannel stickerset:InputStickerSet = Bool; -channels.reportSponsoredMessage#af8ff6b9 channel:InputChannel random_id:bytes option:bytes = channels.SponsoredMessageReportResult; channels.restrictSponsoredMessages#9ae91519 channel:InputChannel restricted:Bool = Updates; channels.searchPosts#d19f987b hashtag:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; +channels.updatePaidMessagesPrice#fc84653f channel:InputChannel send_paid_messages_stars:long = Updates; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; @@ -2339,6 +2504,20 @@ bots.toggleUsername#53ca973 bot:InputUser username:string active:Bool = Bool; bots.canSendMessage#1359f4e6 bot:InputUser = Bool; bots.allowSendMessage#f132e3ef bot:InputUser = Updates; bots.invokeWebViewCustomMethod#87fc5e7 bot:InputUser custom_method:string params:DataJSON = DataJSON; +bots.getPopularAppBots#c2510192 offset:string limit:int = bots.PopularAppBots; +bots.addPreviewMedia#17aeb75a bot:InputUser lang_code:string media:InputMedia = BotPreviewMedia; +bots.editPreviewMedia#8525606f bot:InputUser lang_code:string media:InputMedia new_media:InputMedia = BotPreviewMedia; +bots.deletePreviewMedia#2d0135b3 bot:InputUser lang_code:string media:Vector = Bool; +bots.reorderPreviewMedias#b627f3aa bot:InputUser lang_code:string order:Vector = Bool; +bots.getPreviewInfo#423ab3ad bot:InputUser lang_code:string = bots.PreviewInfo; +bots.getPreviewMedias#a2a5594d bot:InputUser = Vector; +bots.updateUserEmojiStatus#ed9f30c5 user_id:InputUser emoji_status:EmojiStatus = Bool; +bots.toggleUserEmojiStatusPermission#6de6392 bot:InputUser enabled:Bool = Bool; +bots.checkDownloadFileParams#50077589 bot:InputUser file_name:string url:string = Bool; +bots.getAdminedBots#b0711d83 = Vector; +bots.updateStarRefProgram#778b5ab3 flags:# bot:InputUser commission_permille:int duration_months:flags.0?int = StarRefProgram; +bots.setCustomVerification#8b89dfbd flags:# enabled:flags.1?true bot:flags.0?InputUser peer:InputPeer custom_description:flags.2?string = Bool; +bots.getBotRecommendations#a1b70815 bot:InputUser = users.Users; payments.getPaymentForm#37148dbb flags:# invoice:InputInvoice theme_params:flags.0?DataJSON = payments.PaymentForm; payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt; @@ -2350,7 +2529,6 @@ payments.getBankCardData#2e79d779 number:string = payments.BankCardData; payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoice; payments.assignAppStoreTransaction#80ed747d receipt:bytes purpose:InputStorePaymentPurpose = Updates; payments.assignPlayMarketTransaction#dffd50d3 receipt:DataJSON purpose:InputStorePaymentPurpose = Updates; -payments.canPurchasePremium#9fc19eb6 purpose:InputStorePaymentPurpose = Bool; payments.getPremiumGiftCodeOptions#2757ba54 flags:# boost_peer:flags.0?InputPeer = Vector; payments.checkGiftCode#8e51b4c1 slug:string = payments.CheckedGiftCode; payments.applyGiftCode#f6e26854 slug:string = Updates; @@ -2358,9 +2536,37 @@ payments.getGiveawayInfo#f4239425 peer:InputPeer msg_id:int = payments.GiveawayI payments.launchPrepaidGiveaway#5ff58f20 peer:InputPeer giveaway_id:long purpose:InputStorePaymentPurpose = Updates; payments.getStarsTopupOptions#c00ec7d3 = Vector; payments.getStarsStatus#104fcfa7 peer:InputPeer = payments.StarsStatus; -payments.getStarsTransactions#673ac2f9 flags:# inbound:flags.0?true outbound:flags.1?true peer:InputPeer offset:string = payments.StarsStatus; -payments.sendStarsForm#2bb731d flags:# form_id:long invoice:InputInvoice = payments.PaymentResult; +payments.getStarsTransactions#69da4557 flags:# inbound:flags.0?true outbound:flags.1?true ascending:flags.2?true subscription_id:flags.3?string peer:InputPeer offset:string limit:int = payments.StarsStatus; +payments.sendStarsForm#7998c914 form_id:long invoice:InputInvoice = payments.PaymentResult; payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates; +payments.getStarsRevenueStats#d91ffad6 flags:# dark:flags.0?true peer:InputPeer = payments.StarsRevenueStats; +payments.getStarsRevenueWithdrawalUrl#13bbe8b3 peer:InputPeer stars:long password:InputCheckPasswordSRP = payments.StarsRevenueWithdrawalUrl; +payments.getStarsRevenueAdsAccountUrl#d1d7efc5 peer:InputPeer = payments.StarsRevenueAdsAccountUrl; +payments.getStarsTransactionsByID#27842d2e peer:InputPeer id:Vector = payments.StarsStatus; +payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector; +payments.getStarsSubscriptions#32512c5 flags:# missing_balance:flags.0?true peer:InputPeer offset:string = payments.StarsStatus; +payments.changeStarsSubscription#c7770878 flags:# peer:InputPeer subscription_id:string canceled:flags.0?Bool = Bool; +payments.fulfillStarsSubscription#cc5bebb3 peer:InputPeer subscription_id:string = Bool; +payments.getStarsGiveawayOptions#bd1efd3e = Vector; +payments.getStarGifts#c4563590 hash:int = payments.StarGifts; +payments.saveStarGift#2a2a697c flags:# unsave:flags.0?true stargift:InputSavedStarGift = Bool; +payments.convertStarGift#74bf076b stargift:InputSavedStarGift = Bool; +payments.botCancelStarsSubscription#6dfa0622 flags:# restore:flags.0?true user_id:InputUser charge_id:string = Bool; +payments.getConnectedStarRefBots#5869a553 flags:# peer:InputPeer offset_date:flags.2?int offset_link:flags.2?string limit:int = payments.ConnectedStarRefBots; +payments.getConnectedStarRefBot#b7d998f0 peer:InputPeer bot:InputUser = payments.ConnectedStarRefBots; +payments.getSuggestedStarRefBots#d6b48f7 flags:# order_by_revenue:flags.0?true order_by_date:flags.1?true peer:InputPeer offset:string limit:int = payments.SuggestedStarRefBots; +payments.connectStarRefBot#7ed5348a peer:InputPeer bot:InputUser = payments.ConnectedStarRefBots; +payments.editConnectedStarRefBot#e4fca4a3 flags:# revoked:flags.0?true peer:InputPeer link:string = payments.ConnectedStarRefBots; +payments.getStarGiftUpgradePreview#9c9abcb1 gift_id:long = payments.StarGiftUpgradePreview; +payments.upgradeStarGift#aed6e4f5 flags:# keep_original_details:flags.0?true stargift:InputSavedStarGift = Updates; +payments.transferStarGift#7f18176a stargift:InputSavedStarGift to_id:InputPeer = Updates; +payments.getUniqueStarGift#a1974d72 slug:string = payments.UniqueStarGift; +payments.getSavedStarGifts#23830de9 flags:# exclude_unsaved:flags.0?true exclude_saved:flags.1?true exclude_unlimited:flags.2?true exclude_limited:flags.3?true exclude_unique:flags.4?true sort_by_value:flags.5?true peer:InputPeer offset:string limit:int = payments.SavedStarGifts; +payments.getSavedStarGift#b455a106 stargift:Vector = payments.SavedStarGifts; +payments.getStarGiftWithdrawalUrl#d06e93a8 stargift:InputSavedStarGift password:InputCheckPasswordSRP = payments.StarGiftWithdrawalUrl; +payments.toggleChatStarGiftNotifications#60eaefa1 flags:# enabled:flags.0?true peer:InputPeer = Bool; +payments.toggleStarGiftsPinnedToTop#1513e7b0 peer:InputPeer stargift:Vector = Bool; +payments.canPurchaseStore#4fdc5ea7 purpose:InputStorePaymentPurpose = Bool; stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true emojis:flags.5?true text_color:flags.6?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector software:flags.3?string = messages.StickerSet; stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet; @@ -2375,7 +2581,7 @@ stickers.deleteStickerSet#87704394 stickerset:InputStickerSet = Bool; stickers.replaceSticker#4696459a sticker:InputDocument new_sticker:InputStickerSetItem = messages.StickerSet; phone.getCallConfig#55451fa9 = DataJSON; -phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall; +phone.requestCall#a6c4600c flags:# video:flags.0?true user_id:InputUser conference_call:flags.1?InputGroupCall random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall; phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall; phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall; phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool; @@ -2384,7 +2590,7 @@ phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhon phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool; phone.createGroupCall#48cdc6d8 flags:# rtmp_stream:flags.2?true peer:InputPeer random_id:int title:flags.0?string schedule_date:flags.1?int = Updates; -phone.joinGroupCall#b132ff7b flags:# muted:flags.0?true video_stopped:flags.2?true call:InputGroupCall join_as:InputPeer invite_hash:flags.1?string params:DataJSON = Updates; +phone.joinGroupCall#d61e1df3 flags:# muted:flags.0?true video_stopped:flags.2?true call:InputGroupCall join_as:InputPeer invite_hash:flags.1?string key_fingerprint:flags.3?long params:DataJSON = Updates; phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates; phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector = Updates; phone.discardGroupCall#7a777135 call:InputGroupCall = Updates; @@ -2405,6 +2611,7 @@ phone.leaveGroupCallPresentation#1c50d144 call:InputGroupCall = Updates; phone.getGroupCallStreamChannels#1ab21940 call:InputGroupCall = phone.GroupCallStreamChannels; phone.getGroupCallStreamRtmpUrl#deb3abbf peer:InputPeer revoke:Bool = phone.GroupCallStreamRtmpUrl; phone.saveCallLog#41248786 peer:InputPhoneCall file:InputFile = Bool; +phone.createConferenceCall#dfc909ab peer:InputPhoneCall key_fingerprint:long = phone.PhoneCall; langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference; langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector = Vector; @@ -2421,9 +2628,9 @@ stats.getMessagePublicForwards#5f150144 channel:InputChannel msg_id:int offset:s stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats; stats.getStoryStats#374fef40 flags:# dark:flags.0?true peer:InputPeer id:int = stats.StoryStats; stats.getStoryPublicForwards#a6437ef6 peer:InputPeer id:int offset:string limit:int = stats.PublicForwards; -stats.getBroadcastRevenueStats#75dfb671 flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastRevenueStats; -stats.getBroadcastRevenueWithdrawalUrl#2a65ef73 channel:InputChannel password:InputCheckPasswordSRP = stats.BroadcastRevenueWithdrawalUrl; -stats.getBroadcastRevenueTransactions#69280f channel:InputChannel offset:int limit:int = stats.BroadcastRevenueTransactions; +stats.getBroadcastRevenueStats#f788ee19 flags:# dark:flags.0?true peer:InputPeer = stats.BroadcastRevenueStats; +stats.getBroadcastRevenueWithdrawalUrl#9df4faad peer:InputPeer password:InputCheckPasswordSRP = stats.BroadcastRevenueWithdrawalUrl; +stats.getBroadcastRevenueTransactions#70990b6d peer:InputPeer offset:int limit:int = stats.BroadcastRevenueTransactions; chatlists.exportChatlistInvite#8472478e chatlist:InputChatlist title:string peers:Vector = chatlists.ExportedChatlistInvite; chatlists.deleteExportedInvite#719c5c5e chatlist:InputChatlist slug:string = Bool; @@ -2452,7 +2659,7 @@ stories.incrementStoryViews#b2028afb peer:InputPeer id:Vector = Bool; stories.getStoryViewsList#7ed23c57 flags:# just_contacts:flags.0?true reactions_first:flags.2?true forwards_first:flags.3?true peer:InputPeer q:flags.1?string id:int offset:string limit:int = stories.StoryViewsList; stories.getStoriesViews#28e16cc8 peer:InputPeer id:Vector = stories.StoryViews; stories.exportStoryLink#7b8def20 peer:InputPeer id:int = ExportedStoryLink; -stories.report#1923fa8c peer:InputPeer id:Vector reason:ReportReason message:string = Bool; +stories.report#19d8eb45 peer:InputPeer id:Vector option:bytes message:string = ReportResult; stories.activateStealthMode#57bbd166 flags:# past:flags.0?true future:flags.1?true = Updates; stories.sendReaction#7fd736b2 flags:# add_to_recent:flags.0?true peer:InputPeer story_id:int reaction:Reaction = Updates; stories.getPeerStories#2c4ada50 peer:InputPeer = stories.PeerStories; @@ -2462,6 +2669,7 @@ stories.getChatsToSend#a56a8b60 = messages.Chats; stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool; stories.getStoryReactionsList#b9b2881f flags:# forwards_first:flags.2?true peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = stories.StoryReactionsList; stories.togglePinnedToTop#b297e9b peer:InputPeer id:Vector = Bool; +stories.searchPosts#d1810907 flags:# hashtag:flags.0?string area:flags.1?MediaArea peer:flags.2?InputPeer offset:string limit:int = stories.FoundStories; premium.getBoostsList#60f67660 flags:# gifts:flags.0?true peer:InputPeer offset:string limit:int = premium.BoostsList; premium.getMyBoosts#be77b4a = premium.MyBoosts; @@ -2479,4 +2687,4 @@ smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool; fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo; -// LAYER 181 +// LAYER 201 diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 03ec4054a..bd0961045 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -17,7 +17,6 @@ # along with Hydrogram. If not, see . import ast -import os import re import shutil from pathlib import Path @@ -48,8 +47,8 @@ def generate(source_path: Path, base_name: str): def build(path: Path, level=0): last = path.name - for i in os.listdir(path): - if not i.startswith("__"): + for i in path.iterdir(): + if not i.name.startswith("__"): item_path = path / i if item_path.is_dir(): build(item_path, level=level + 1) @@ -97,7 +96,7 @@ def build(path: Path, level=0): v = sorted(v) entities = [] - entities = [f'{i} <{snake(i).replace("_", "-")}>' for i in v] + entities = [f"{i} <{snake(i).replace('_', '-')}>" for i in v] if k != base_name: inner_path = Path(base_name, k, "index.rst") @@ -238,6 +237,7 @@ def get_title_list(s: str) -> list[str]: get_send_as_chats set_send_as_chat set_chat_protected_content + transfer_chat_ownership """, "users": """ Users @@ -332,6 +332,12 @@ def get_title_list(s: str) -> list[str]: resolve_peer save_file """, + "phone": """ + Phone: + create_video_chat + discard_group_call + invite_group_call_members + """, } root = API_DOCS_DEST_PATH / "methods" @@ -488,6 +494,7 @@ def get_title_list(s: str) -> list[str]: "input_message_content": """ InputMessageContent InputMessageContent + InputPollOption InputTextMessageContent """, "authorization": """ diff --git a/compiler/docs/template/methods.rst b/compiler/docs/template/methods.rst index e9673f82d..dde59efea 100644 --- a/compiler/docs/template/methods.rst +++ b/compiler/docs/template/methods.rst @@ -151,6 +151,19 @@ Authorization {authorization} +Phone +----- + +.. autosummary:: + :nosignatures: + + {phone} + +.. toctree:: + :hidden: + + {phone} + Advanced -------- diff --git a/compiler/errors/compiler.py b/compiler/errors/compiler.py index a0d0017ac..95e7dcc92 100644 --- a/compiler/errors/compiler.py +++ b/compiler/errors/compiler.py @@ -18,7 +18,6 @@ # along with Hydrogram. If not, see . import csv -import os import re import shutil from pathlib import Path @@ -45,7 +44,7 @@ def start(): shutil.rmtree(ERRORS_DEST_PATH, ignore_errors=True) ERRORS_DEST_PATH.mkdir(parents=True) - files = os.listdir(f"{ERRORS_HOME_PATH}/source") + files = [i.name for i in (ERRORS_HOME_PATH / "source").iterdir()] with NOTICE_PATH.open(encoding="utf-8") as f: notice = [f"# {line}".strip() for line in f] @@ -82,7 +81,7 @@ def start(): super_class = camel(name) name = " ".join([ - i.capitalize() for i in re.sub(r"_", " ", name).lower().split(" ") + i.capitalize() for i in name.replace(r"_", " ").lower().split(" ") ]) sub_classes = [] @@ -100,9 +99,9 @@ def start(): error_id, error_message = row - sub_class = camel(re.sub(r"_X", "_", error_id)) + sub_class = camel(error_id.replace(r"_X", "_")) sub_class = re.sub(r"^2", "Two", sub_class) - sub_class = re.sub(r" ", "", sub_class) + sub_class = sub_class.replace(r" ", "") f_all.write(f' "{error_id}": "{sub_class}",\n') @@ -144,7 +143,7 @@ def start(): content = f.read() with (ERRORS_DEST_PATH / "all.py").open("w", encoding="utf-8") as f: - f.write(re.sub("{count}", str(count), content)) # noqa: RUF027 + f.write(re.sub(r"{count}", str(count), content)) # noqa: RUF027 if __name__ == "__main__": diff --git a/dev_tools/check_api_schema_updates.py b/dev_tools/check_api_schema_updates.py new file mode 100755 index 000000000..4db8f9278 --- /dev/null +++ b/dev_tools/check_api_schema_updates.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + +from __future__ import annotations + +import argparse +import hashlib +import logging +import re +import sys +from pathlib import Path +from typing import TypedDict + +import requests + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", +) +logger = logging.getLogger(__name__) + +# Constants +API_SCHEMA_URL = "https://raw.githubusercontent.com/telegramdesktop/tdesktop/dev/Telegram/SourceFiles/mtproto/scheme/api.tl" + +REPO_HOME = Path(__file__).parent.parent +API_DIR = REPO_HOME / "compiler" / "api" +SOURCE_DIR = API_DIR / "source" +MAIN_API_PATH = SOURCE_DIR / "main_api.tl" + +LAYER_RE = re.compile(r"//\s*LAYER\s+(\d+)") + +SCHEMA_HEADER = """// https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/mtproto/scheme/api.tl + +/////////////////////////////// +///////// Main application API +/////////////////////////////// + +---types--- + +// boolFalse#bc799737 = Bool; // Parsed manually +// boolTrue#997275b5 = Bool; // Parsed manually + +// true#3fedd339 = True; // Not used + +// vector#1cb5c415 {t:Type} # [ t ] = Vector t; // Parsed manually + +// error#c4b9f9bb code:int text:string = Error; // Not used + +// null#56730bcc = Null; // Parsed manually +""" + +BASIC_TYPES = [ + "boolFalse#bc799737 = Bool;", + "boolTrue#997275b5 = Bool;", + "true#3fedd339 = True;", + "vector#1cb5c415 {t:Type} # [ t ] = Vector t;", + "error#c4b9f9bb code:int text:string = Error;", + "null#56730bcc = Null;", +] + + +class SchemaData(TypedDict): + """Schema data returned from the Telegram API.""" + + content: str + hash: str + layer: str + + +def get_current_schema_hash() -> str | None: + """ + Get the hash of the current schema file if it exists. + + Returns: + str | None: SHA256 hash of the current schema file or None if it doesn't exist + """ + if not MAIN_API_PATH.exists(): + return None + + return hashlib.sha256(MAIN_API_PATH.read_bytes()).hexdigest() + + +def get_latest_schema() -> SchemaData | None: + """ + Fetch the latest API schema from Telegram's repository. + + Returns: + SchemaData | None: Dictionary containing the schema content, hash, and layer version + or None if fetching or processing fails + """ + logger.info("Fetching latest API schema from Telegram repository") + + try: + api_response = requests.get(API_SCHEMA_URL) + api_response.raise_for_status() + + api_content = api_response.text + + # Extract layer version from the API content + layer_match = LAYER_RE.search(api_content) + layer_version = layer_match.group(1) if layer_match else "Unknown" + + # Process in new format mode + functions_idx = api_content.find("---functions---") + if functions_idx != -1: + types_part = api_content[:functions_idx].strip() + functions_part = api_content[functions_idx:] + else: + # Just treat everything as types since there's no clear separator + types_part = api_content + functions_part = "" + + # Remove basic types that are already in our header + # But preserve empty lines to maintain block structure + processed_types: list[str] = [] + + for line in types_part.split("\n"): + line_stripped = line.strip() + + # Keep empty lines + if not line_stripped: + processed_types.append("") + continue + + # Skip basic types that are in our header + if not any( + line_stripped.startswith(basic_type.split("=")[0].strip()) + for basic_type in BASIC_TYPES + ): + processed_types.append(line) + + # Combine our header with processed schema - trim extra whitespace + processed_types_content = "\n".join(processed_types).strip() + + if functions_part: + schema_content = f"{SCHEMA_HEADER}\n{processed_types_content}\n\n{functions_part}" + else: + schema_content = f"{SCHEMA_HEADER}\n{processed_types_content}" + + schema_hash = hashlib.sha256(schema_content.encode()).hexdigest() + + return {"content": schema_content, "hash": schema_hash, "layer": layer_version} + except requests.RequestException as e: + logger.error("Error fetching schema: %s", e) + return None + except (ValueError, KeyError) as e: + logger.error("Error processing schema: %s", e) + return None + + +def update_schema(schema_data: SchemaData) -> bool: + """ + Update the schema file with new content. + + Args: + schema_data: Dictionary containing schema content, hash, and layer version + + Returns: + bool: True if update was successful + """ + logger.info("Updating schema to layer %s", schema_data["layer"]) + + SOURCE_DIR.mkdir(parents=True, exist_ok=True) + + MAIN_API_PATH.write_text(schema_data["content"], encoding="utf-8") + + logger.info("Schema updated successfully to layer %s", schema_data["layer"]) + return True + + +def get_current_layer() -> str: + """ + Get the layer version from the current schema file. + + Returns: + str: Current layer version or "Unknown" if not found + """ + if not MAIN_API_PATH.exists(): + return "Unknown" + + content = MAIN_API_PATH.read_text(encoding="utf-8") + layer_match = LAYER_RE.search(content) + return layer_match.group(1) if layer_match else "Unknown" + + +def main() -> int: + """ + Main function to check for and apply schema updates. + + Returns: + int: Exit code (0: update applied, 1: error, 2: no update needed) + """ + parser = argparse.ArgumentParser(description="Check for updates to the Telegram API schema") + parser.add_argument( + "--force-update", action="store_true", help="Force update even if no changes detected" + ) + args = parser.parse_args() + + current_hash = get_current_schema_hash() + schema_data = get_latest_schema() + + if not schema_data: + logger.error("Failed to fetch schema data") + return 1 + + if args.force_update or current_hash != schema_data["hash"]: + if current_hash: + logger.info( + "Schema update detected! Current layer: %s → New layer: %s", + get_current_layer(), + schema_data["layer"], + ) + else: + logger.info("Initializing schema with layer %s", schema_data["layer"]) + + update_schema(schema_data) + return 0 + + logger.info("No schema updates detected. Current layer: %s", get_current_layer()) + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/dev_tools/compare_to_bot_api.py b/dev_tools/compare_to_bot_api.py new file mode 100755 index 000000000..99c7c928a --- /dev/null +++ b/dev_tools/compare_to_bot_api.py @@ -0,0 +1,328 @@ +#!/bin/env python +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + +from __future__ import annotations + +import inspect +import re +from typing import Literal + +import httpx +from lxml import html + +from hydrogram import Client, types + +# Item can be ignored entirely (True) or have specific fields ignored with optional aliases +IgnoreSpec = Literal[True] | dict[str, str | Literal[True]] + +# dict of items to ignore with optional field aliases +# fmt: off +ignored_items: dict[str, IgnoreSpec] = { + "*": { + "type": True, + "from": "from_user", + "thumbnail": "thumb", + }, + "Message": { + "message_id": "id", + }, + # Polling and webhook related methods and objects. Ignored for us. + "get_updates": True, + "set_webhook": True, + "delete_webhook": True, + "get_webhook_info": True, + "Update": True, + "WebhookInfo": True, +} +# fmt: on + + +def snake(s: str) -> str: + s = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", s) + return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s).lower() + + +def check_field_ignored(field: str, ignore_spec: IgnoreSpec, hydrogram_fields: list[str]) -> bool: + """Check if a field should be ignored based on ignore specs""" + if field in hydrogram_fields: + return True + + if isinstance(ignore_spec, dict): + alias = ignore_spec.get(field) + if isinstance(alias, bool): + return True + if alias and alias in hydrogram_fields: + return True + + wildcard_spec = ignored_items.get("*", {}) + if isinstance(wildcard_spec, dict): + alias = wildcard_spec.get(field) + if isinstance(alias, bool): + return True + if alias and alias in hydrogram_fields: + return True + + return False + + +class BotAPISchema: + def __init__(self): + self.client = httpx.Client() + self.methods: dict[str, list[str]] = {} + self.objects: dict[str, list[str]] = {} + + def finish_parsing(self, obj_name: str, obj_fields: list[str]): + """Detects whether we're parsing a method or an object and stores it accordingly.""" + if obj_name[0].isupper(): + self.objects[obj_name] = obj_fields + else: + self.methods[snake(obj_name)] = obj_fields + + def parse(self): + """Parses the Telegram Bot API documentation to extract methods and objects.""" + response = self.client.get("https://core.telegram.org/bots/api") + response.raise_for_status() + + tree = html.fromstring(response.content) + content = tree.xpath('//div[@id="dev_page_content"]')[0] + + currently_parsing = "" + last_sec_fields: list[str] = [] + scraping = False + + for tag in content: + if not scraping and tag.xpath('.//a[@name="getting-updates"]'): + scraping = True + elif not scraping: + continue + + if tag.tag == "h4": + tag_content = tag.text_content().strip() + + # Telegram places other stuff alongside with methods and objects, just ignore them. + if " " in tag_content: + continue + + if currently_parsing and currently_parsing != tag_content: + self.finish_parsing(currently_parsing, last_sec_fields) + last_sec_fields = [] + + currently_parsing = tag_content + + elif tag.tag == "table": + rows = tag.xpath(".//tbody/tr") + for row in rows: + cells = row.xpath(".//td") + field_name = cells[0].text_content().strip() + last_sec_fields.append(field_name) + + if currently_parsing: + self.finish_parsing(currently_parsing, last_sec_fields) + + def get_schema(self): + """Returns the parsed methods and objects.""" + self.parse() + return self.methods, self.objects + + +class HydrogramSchema: + def __init__(self): + self.methods: dict[str, list[str]] = {} + self.objects: dict[str, list[str]] = {} + + def parse_methods(self): + """Parses methods in hydrogram.Client and extracts their arguments.""" + client_methods = inspect.getmembers(Client, predicate=inspect.isfunction) + for method_name, method in client_methods: + sig = inspect.signature(method) + args = [param.name for param in sig.parameters.values() if param.name != "self"] + self.methods[method_name] = args + + def parse_objects(self): + """Parses classes in hydrogram.types and extracts their arguments.""" + types_classes = inspect.getmembers(types, predicate=inspect.isclass) + for class_name, cls in types_classes: + if hasattr(cls, "__init__"): + sig = inspect.signature(cls.__init__) + args = [param.name for param in sig.parameters.values() if param.name != "self"] + self.objects[class_name] = args + + def parse(self): + """Parses both methods and objects.""" + self.parse_methods() + self.parse_objects() + + def get_schema(self): + """Returns the parsed methods and objects.""" + self.parse() + return self.methods, self.objects + + +def compare_item( + bot_api_item: str, + bot_api_fields: list[str], + hydrogram_fields: list[str], + ignore_spec: IgnoreSpec, +) -> list[str]: + """Compare fields between bot API and hydrogram implementations""" + if ignore_spec is True: + return [] + + return [ + field + for field in bot_api_fields + if not check_field_ignored(field, ignore_spec, hydrogram_fields) + ] + + +def compare_schemas(bot_api_methods, bot_api_objects, hydrogram_methods, hydrogram_objects): + missing_methods = {} + missing_objects = {} + method_mismatches = {} + object_mismatches = {} + + # Compare methods + for method_name, bot_api_args in bot_api_methods.items(): + ignore_spec = ignored_items.get(method_name, {}) + if ignore_spec is True: + continue + + if method_name not in hydrogram_methods: + missing_methods[method_name] = bot_api_args + else: + missing_args = compare_item( + method_name, bot_api_args, hydrogram_methods[method_name], ignore_spec + ) + if missing_args: + method_mismatches[method_name] = missing_args + + # Compare objects + for obj_name, bot_api_fields in bot_api_objects.items(): + ignore_spec = ignored_items.get(obj_name, {}) + if ignore_spec is True: + continue + + if obj_name not in hydrogram_objects: + missing_objects[obj_name] = bot_api_fields + else: + missing_fields = compare_item( + obj_name, bot_api_fields, hydrogram_objects[obj_name], ignore_spec + ) + if missing_fields: + object_mismatches[obj_name] = missing_fields + + return { + "missing_methods": missing_methods, + "missing_objects": missing_objects, + "method_mismatches": method_mismatches, + "object_mismatches": object_mismatches, + } + + +def generate_implementation_stats(total: int, unimplemented: int, partial: int) -> tuple[int, int]: + """Calculate implementation statistics""" + implemented = total - unimplemented + fully_implemented = implemented - partial + return implemented, fully_implemented + + +def generate_report( + comparison_results, bot_api_methods, bot_api_objects, hydrogram_methods, hydrogram_objects +): + report = [] + + partially_implemented_methods = comparison_results["method_mismatches"] + partially_implemented_objects = comparison_results["object_mismatches"] + unimplemented_methods = comparison_results["missing_methods"] + unimplemented_objects = comparison_results["missing_objects"] + + report.append("## ❌ Unimplemented features") + + if unimplemented_methods: + report.append("\n### Methods") + report.extend(f"- {method_name}" for method_name in unimplemented_methods) + + if unimplemented_objects: + report.append("\n### Objects") + report.extend(f"- {obj_name}" for obj_name in unimplemented_objects) + + total_methods = len(bot_api_methods) + total_objects = len(bot_api_objects) + + implemented_methods, fully_implemented_methods = generate_implementation_stats( + total_methods, len(unimplemented_methods), len(partially_implemented_methods) + ) + + implemented_objects, fully_implemented_objects = generate_implementation_stats( + total_objects, len(unimplemented_objects), len(partially_implemented_objects) + ) + + report.append("## 🚧 Partially implemented features") + + if partially_implemented_methods: + report.append("\n### Methods") + for method_name, missing_args in partially_implemented_methods.items(): + total_args = len(bot_api_methods[method_name]) + implemented_args = total_args - len(missing_args) + report.append(f"\n#### {method_name} ({implemented_args}/{total_args}):") + report.extend(f"- {arg}" for arg in missing_args) + report.append("") + + if partially_implemented_objects: + report.append("\n### Objects") + for obj_name, missing_fields in partially_implemented_objects.items(): + total_fields = len(bot_api_objects[obj_name]) + implemented_fields = total_fields - len(missing_fields) + report.append(f"\n#### {obj_name} ({implemented_fields}/{total_fields}):") + report.extend(f"- {field}" for field in missing_fields) + report.append("") + + report.append("") + + report.extend([ + f"At least partially implemented methods: {implemented_methods}/{total_methods} ({(implemented_methods / total_methods) * 100:.0f}%)", + f"At least partially implemented objects: {implemented_objects}/{total_objects} ({(implemented_objects / total_objects) * 100:.0f}%)", + ]) + + report.append("") + + report.extend([ + f"Fully implemented methods: {fully_implemented_methods}/{total_methods} ({(fully_implemented_methods / total_methods) * 100:.0f}%)", + f"Fully implemented objects: {fully_implemented_objects}/{total_objects} ({(fully_implemented_objects / total_objects) * 100:.0f}%)", + ]) + + return "\n".join(report) + + +if __name__ == "__main__": + bot_api_parser = BotAPISchema() + bot_api_parser.parse() + bot_api_methods, bot_api_objects = bot_api_parser.get_schema() + + hydrogram_parser = HydrogramSchema() + hydrogram_parser.parse() + hydrogram_methods, hydrogram_objects = hydrogram_parser.get_schema() + + comparison_results = compare_schemas( + bot_api_methods, bot_api_objects, hydrogram_methods, hydrogram_objects + ) + report = generate_report( + comparison_results, bot_api_methods, bot_api_objects, hydrogram_methods, hydrogram_objects + ) + print(report) diff --git a/dev_tools/generate_docs_json.py b/dev_tools/generate_docs_json.py index 88f18bd2d..6183fba62 100755 --- a/dev_tools/generate_docs_json.py +++ b/dev_tools/generate_docs_json.py @@ -89,7 +89,12 @@ async def get_object_data(it_type: str, it_name: str, doc_dict: dict[str, dict]) tree = html.fromstring(request.text) - page_content = tree.xpath("//div[@id='dev_page_content'][1]")[0] + page_content_xp = tree.xpath("//div[@id='dev_page_content'][1]") + if not page_content_xp: + print(f"No page content for {it_type}/{it_name}") + return + + page_content = page_content_xp[0] # Get the description of the object - always used desc_xp = page_content.xpath("./p[1]") @@ -98,27 +103,26 @@ async def get_object_data(it_type: str, it_name: str, doc_dict: dict[str, dict]) desc = desc_xp[0].text_content().strip() else: print(f"No description for {it_type}/{it_name}") - desc = None + desc = "" if it_type == "type": doc_dict["type"][it_name] = {"desc": desc} elif it_type in {"constructor", "method"}: - params_xp = ( - page_content.xpath("./h3/a[@id='parameters'][1]")[0] - .getparent() - .getnext() - .xpath("./tbody[1]") - ) - - if params_xp: - params = { - x.getchildren()[0].text_content().strip(): x.getchildren()[2] - .text_content() - .strip() - for x in params_xp[0].xpath("./tr") - } + params_link_xp = page_content.xpath("./h3/a[@id='parameters'][1]") + if params_link_xp: + params_xp = params_link_xp[0].getparent().getnext().xpath("./tbody[1]") + if params_xp: + params = { + x.getchildren()[0].text_content().strip(): x.getchildren()[2] + .text_content() + .strip() + for x in params_xp[0].xpath("./tr") + } + else: + print(f"No parameters for {it_type}/{it_name}") + params = {} else: - print(f"No parameters for {it_type}/{it_name}") + print(f"No parameters section for {it_type}/{it_name}") params = {} doc_dict[it_type][it_name] = {"desc": desc, "params": params} diff --git a/docs/source/api/decorators.rst b/docs/source/api/decorators.rst index 6ddd9ddce..26db3fe14 100644 --- a/docs/source/api/decorators.rst +++ b/docs/source/api/decorators.rst @@ -61,3 +61,4 @@ Details .. autodecorator:: hydrogram.Client.on_poll() .. autodecorator:: hydrogram.Client.on_disconnect() .. autodecorator:: hydrogram.Client.on_raw_update() +.. autodecorator:: hydrogram.Client.on_error() diff --git a/docs/source/conf.py b/docs/source/conf.py index a15a2f52d..9a49b849b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,17 +1,38 @@ +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + import datetime import sys from pathlib import Path +# Setup path for imports docs_dir = Path(__file__).parent.parent sys.path.insert(0, docs_dir.resolve().as_posix()) import hydrogram # noqa: E402 +# Project information project = "Hydrogram" author = "Hydrogram" copyright = f"{datetime.date.today().year}, {author}" release = hydrogram.__version__ +# Sphinx extensions extensions = [ "sphinx.ext.autodoc", "sphinx.ext.viewcode", @@ -22,44 +43,104 @@ "sphinxcontrib.towncrier.ext", ] +# Intersphinx mapping for external documentation intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "aiosqlite": ("https://aiosqlite.omnilib.dev/en/stable/", None), } +# Basic configuration +master_doc = "index" +source_suffix = ".rst" +pygments_style = "friendly" + +# Documentation generation settings +autodoc_member_order = "bysource" +autodoc_typehints = "none" html_use_modindex = False html_use_index = False +# Napoleon settings (for Google-style docstrings) napoleon_use_rtype = False napoleon_use_param = False -master_doc = "index" -source_suffix = ".rst" -autodoc_member_order = "bysource" -autodoc_typehints = "none" - +# Towncrier settings towncrier_draft_autoversion_mode = "draft" towncrier_draft_include_empty = True towncrier_draft_working_directory = Path(__file__).parent.parent.parent -pygments_style = "friendly" - +# HTML theme settings html_theme = "furo" html_title = f"{project} v{release} Documentation" html_last_updated_fmt = ( f"{datetime.datetime.now(tz=datetime.UTC).strftime('%d/%m/%Y, %H:%M:%S')} UTC" ) - html_copy_source = False - html_static_path = ["_static"] html_css_files = [ "css/all.min.css", "css/custom.css", ] +# Icons for footer (from https://react-icons.github.io/react-icons) +TELEGRAM_ICON = ( + '' + '' +) + +GITHUB_ICON = ( + '' + "" +) + +WEBSITE_ICON = ( + '' + '' +) + +# Theme options configuration html_theme_options = { "navigation_with_keys": True, + # CSS variables "dark_css_variables": { "admonition-title-font-size": "0.95rem", "admonition-font-size": "0.92rem", @@ -68,73 +149,27 @@ "admonition-title-font-size": "0.95rem", "admonition-font-size": "0.92rem", }, + # Logo configuration "light_logo": "hydrogram-light.png", "dark_logo": "hydrogram-dark.png", - "footer_icons": [ # all these icons are from https://react-icons.github.io/react-icons + # Footer icons + "footer_icons": [ { "name": "Telegram Channel", "url": "https://t.me/HydrogramNews/", - "html": ( - '' - '' - ), + "html": TELEGRAM_ICON, "class": "", }, { "name": "GitHub Organization", "url": "https://github.com/hydrogram/", - "html": ( - '' - "" - ), + "html": GITHUB_ICON, "class": "", }, { "name": "Hydrogram Website", "url": "https://hydrogram.org/", - "html": ( - '' - '' - ), + "html": WEBSITE_ICON, "class": "", }, ], diff --git a/docs/source/faq/why-is-the-api-key-needed-for-bots.rst b/docs/source/faq/why-is-the-api-key-needed-for-bots.rst index 65f08873f..b3256d5d5 100644 --- a/docs/source/faq/why-is-the-api-key-needed-for-bots.rst +++ b/docs/source/faq/why-is-the-api-key-needed-for-bots.rst @@ -4,7 +4,7 @@ Why is the API key needed for bots? Requests against the official bot API endpoints are made via JSON/HTTP and are handled by an intermediate server application that implements the MTProto protocol and uses its own API key to communicate with the MTProto servers. -.. figure:: //_static/img/mtproto-vs-bot-api.png +.. figure:: /_static/img/mtproto-vs-bot-api.png :align: center Using MTProto is the only way to communicate with the actual Telegram servers, and the main API requires developers to diff --git a/docs/source/hydrogram-vs-pyrogram.rst b/docs/source/hydrogram-vs-pyrogram.rst index 58b53160a..8a54e41e2 100644 --- a/docs/source/hydrogram-vs-pyrogram.rst +++ b/docs/source/hydrogram-vs-pyrogram.rst @@ -26,7 +26,7 @@ More technical changes - Declared the special variable `__all__` in all files so now we have a better IDE support. - Specify optional parameters in methods as keyword-only to prevent potential breakage in the future when introducing new parameters. -- Migrated from setuptools to hatchling with rye. +- Migrated from setuptools to `hatchling `_ with `uv `_. - Transitioned from using `os.path` to utilizing `Pathlib`. - Start utilizing towncrier to generate changelogs. diff --git a/docs/source/intro/install.rst b/docs/source/intro/install.rst index ec5935c04..77c47601c 100644 --- a/docs/source/intro/install.rst +++ b/docs/source/intro/install.rst @@ -15,16 +15,21 @@ Install Hydrogram $ pip3 install -U hydrogram -- or, with :doc:`TgCrypto <../topics/speedups>` as extra requirement (recommended): +- or, with :doc:`TgCrypto and uvloop <../topics/speedups>` as extra requirements (recommended for better performance): .. code-block:: text - $ pip3 install -U hydrogram tgcrypto + $ pip3 install -U "hydrogram[fast]" Bleeding Edge ------------- -You can install the development version from the git ``dev`` branch using this command: +The development version from the git ``dev`` branch contains the latest features and fixes, but it +may also include unfinished changes, bugs, or unstable code. Using this version can lead to unexpected +behavior, or compatibility issues. It is recommended only for advanced users who want to +test new features and are comfortable troubleshooting problems. + +You can install the development version using this command: .. code-block:: text diff --git a/docs/source/intro/quickstart.rst b/docs/source/intro/quickstart.rst index ca0f86efb..404a2f29a 100644 --- a/docs/source/intro/quickstart.rst +++ b/docs/source/intro/quickstart.rst @@ -1,54 +1,79 @@ Quick Start =========== -The next few steps serve as a quick start to see Hydrogram in action as fast as possible. +Welcome to Hydrogram! This guide will help you set up and run your first Telegram bot or client application in minutes. -Get Hydrogram Real Fast ------------------------ +What is Hydrogram? +------------------ -1. Install Hydrogram with ``pip3 install -U hydrogram``. +Hydrogram is a modern, elegant Python framework that allows you to interact with the Telegram API. With Hydrogram, you can: -2. Obtain the API key by following Telegram's instructions and rules at https://core.telegram.org/api/obtaining_api_id. +- Create Telegram bots with advanced features +- Build user clients (userbot) +- Access and manage your Telegram account programmatically +- Automate Telegram-related tasks +- Handle updates and respond to messages + +Let's get started with a simple example: + +Getting Started +--------------- + +1. **Install Hydrogram** with ``pip3 install -U hydrogram``. + +2. **Obtain your API credentials** from Telegram: + + - Go to https://my.telegram.org/apps and log in with your phone number + - Create a new application to get your ``api_id`` and ``api_hash`` .. note:: - Make sure you understand and abide to the rules for third-party clients and libraries explained in the link above. + Make sure you understand and abide by Telegram's terms of service and API usage rules explained at https://core.telegram.org/api/obtaining_api_id. -3. Open the text editor of your choice and paste the following: +3. **Create your first script** by opening your favorite text editor and pasting the following code: .. code-block:: python import asyncio from hydrogram import Client + # Replace these with your own values api_id = 12345 api_hash = "0123456789abcdef0123456789abcdef" async def main(): + # Create a new client instance async with Client("my_account", api_id, api_hash) as app: + # Send a message to yourself await app.send_message("me", "Greetings from **Hydrogram**!") + # Get information about yourself + me = await app.get_me() + print(f"Successfully logged in as {me.first_name} ({me.id})") + asyncio.run(main()) -4. Replace *api_id* and *api_hash* values with your own. +4. **Replace** the placeholder ``api_id`` and ``api_hash`` values with your own. + +5. **Save the file** as ``hello.py``. -5. Save the file as ``hello.py``. +6. **Run the script** with ``python3 hello.py`` -6. Run the script with ``python3 hello.py`` +7. **Log in to your account** by following the prompts. You'll only need to do this once. -7. Follow the instructions on your terminal to login. +8. **Watch Hydrogram in action** as it sends a message to your Saved Messages. -8. Watch Hydrogram send a message to yourself. +What's Next? +------------ -Enjoy the API -------------- +This was just a brief introduction to get you started quickly. Hydrogram offers many more powerful features for building Telegram applications. -That was just a quick overview. In the next few pages of the introduction, we'll take a much more in-depth look of what -we have just done above. +- Learn about different ways to :doc:`../start/invoking` from the API +- Explore how to :doc:`../start/updates` from Telegram +- Check out complete :doc:`../start/examples/index` to see what's possible -If you are feeling eager to continue you can take a shortcut to :doc:`../start/invoking` and come back -later to learn some more details. +Join our `community`_ on Telegram for support and updates. .. _community: https://t.me/HydrogramNews diff --git a/docs/source/topics/mtproto-vs-botapi.rst b/docs/source/topics/mtproto-vs-botapi.rst index cf06112eb..2ea5faafc 100644 --- a/docs/source/topics/mtproto-vs-botapi.rst +++ b/docs/source/topics/mtproto-vs-botapi.rst @@ -32,7 +32,7 @@ special accounts that are authorized via tokens instead of phone numbers. The Bo main Telegram API, but runs on an intermediate server application that in turn communicates with the actual Telegram servers using MTProto. -.. figure:: //_static/img/mtproto-vs-bot-api.png +.. figure:: /_static/img/mtproto-vs-bot-api.png :align: center .. _Bot API: https://core.telegram.org/bots/api diff --git a/hydrogram/__init__.py b/hydrogram/__init__.py index 93702bc74..a36afafd7 100644 --- a/hydrogram/__init__.py +++ b/hydrogram/__init__.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -__version__ = "0.2.0" +__version__ = "0.2.1.dev" __license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)" __copyright__ = "Copyright (C) 2023-present Hydrogram " diff --git a/hydrogram/client.py b/hydrogram/client.py index 251789897..c8ee13505 100644 --- a/hydrogram/client.py +++ b/hydrogram/client.py @@ -58,6 +58,8 @@ from hydrogram.types import ListenerTypes, TermsOfService, User from hydrogram.utils import ainput +from .connection import Connection +from .connection.transport import TCP, TCPAbridged from .dispatcher import Dispatcher from .file_id import FileId, FileType, ThumbnailSource from .mime_types import mime_types @@ -65,6 +67,7 @@ from .session.internals import MsgId if TYPE_CHECKING: + import builtins from collections.abc import AsyncGenerator log = logging.getLogger(__name__) @@ -190,6 +193,16 @@ class Client(Methods): Set the maximum amount of concurrent transmissions (uploads & downloads). A value that is too high may result in network related issues. Defaults to 1. + + connection_factory (:obj:`~hydrogram.connection.Connection`, *optional*): + Pass a custom connection factory to the client. + + protocol_factory (:obj:`~hydrogram.connection.transport.TCP`, *optional*): + Pass a custom protocol factory to the client. + + message_cache_size (``int``, *optional*): + Size of the message cache used to store already processed messages. + Defaults to 1000. """ APP_VERSION = f"Hydrogram {__version__}" @@ -242,6 +255,9 @@ def __init__( sleep_threshold: int = Session.SLEEP_THRESHOLD, hide_password: bool = False, max_concurrent_transmissions: int = MAX_CONCURRENT_TRANSMISSIONS, + connection_factory: builtins.type[Connection] = Connection, + protocol_factory: builtins.type[TCP] = TCPAbridged, + message_cache_size: int = 1000, ): super().__init__() @@ -270,6 +286,9 @@ def __init__( self.sleep_threshold = sleep_threshold self.hide_password = hide_password self.max_concurrent_transmissions = max_concurrent_transmissions + self.connection_factory = connection_factory + self.protocol_factory = protocol_factory + self.message_cache_size = message_cache_size self.executor = ThreadPoolExecutor(self.workers, thread_name_prefix="Handler") @@ -308,7 +327,7 @@ def __init__( self.me: User | None = None - self.message_cache = Cache(10000) + self.message_cache = Cache(message_cache_size) # Sometimes, for some reason, the server will stop sending updates and will only respond to pings. # This watchdog will invoke updates.GetState in order to wake up the server and enable it sending updates again @@ -317,7 +336,6 @@ def __init__( self.updates_watchdog_event = asyncio.Event() self.last_update_time = datetime.now() - self.loop = asyncio.get_event_loop() self.listeners = {listener_type: [] for listener_type in ListenerTypes} async def __aenter__(self): @@ -327,6 +345,15 @@ async def __aexit__(self, *args): with contextlib.suppress(ConnectionError): await self.stop() + @functools.cached_property + def loop(self): + try: + return asyncio.get_running_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + return loop + async def updates_watchdog(self): while True: try: diff --git a/hydrogram/connection/connection.py b/hydrogram/connection/connection.py index 810e01b81..e4485facc 100644 --- a/hydrogram/connection/connection.py +++ b/hydrogram/connection/connection.py @@ -21,30 +21,43 @@ import asyncio import logging +from typing import TYPE_CHECKING from hydrogram.session.internals import DataCenter from .transport import TCP, TCPAbridged +if TYPE_CHECKING: + from .transport.tcp.tcp import Proxy + log = logging.getLogger(__name__) class Connection: MAX_CONNECTION_ATTEMPTS = 3 - def __init__(self, dc_id: int, test_mode: bool, ipv6: bool, proxy: dict, media: bool = False): + def __init__( + self, + dc_id: int, + test_mode: bool, + ipv6: bool, + proxy: Proxy, + media: bool = False, + protocol_factory: type[TCP] = TCPAbridged, + ) -> None: self.dc_id = dc_id self.test_mode = test_mode self.ipv6 = ipv6 self.proxy = proxy self.media = media + self.protocol_factory = protocol_factory self.address = DataCenter(dc_id, test_mode, ipv6, media) - self.protocol: TCP = None + self.protocol: TCP | None = None - async def connect(self): + async def connect(self) -> None: for i in range(Connection.MAX_CONNECTION_ATTEMPTS): - self.protocol = TCPAbridged(self.ipv6, self.proxy) + self.protocol = self.protocol_factory(ipv6=self.ipv6, proxy=self.proxy) try: log.info("Connecting...") @@ -66,11 +79,11 @@ async def connect(self): log.warning("Connection failed! Trying again...") raise ConnectionError - async def close(self): + async def close(self) -> None: await self.protocol.close() log.info("Disconnected") - async def send(self, data: bytes): + async def send(self, data: bytes) -> None: await self.protocol.send(data) async def recv(self) -> bytes | None: diff --git a/hydrogram/connection/transport/tcp/__init__.py b/hydrogram/connection/transport/tcp/__init__.py index 9c9a42d25..06f435ca2 100644 --- a/hydrogram/connection/transport/tcp/__init__.py +++ b/hydrogram/connection/transport/tcp/__init__.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from .tcp import TCP +from .tcp import TCP, Proxy from .tcp_abridged import TCPAbridged from .tcp_abridged_o import TCPAbridgedO from .tcp_full import TCPFull @@ -26,6 +26,7 @@ __all__ = [ "TCP", + "Proxy", "TCPAbridged", "TCPAbridgedO", "TCPFull", diff --git a/hydrogram/connection/transport/tcp/tcp.py b/hydrogram/connection/transport/tcp/tcp.py index 8bf6364d7..406647b8b 100644 --- a/hydrogram/connection/transport/tcp/tcp.py +++ b/hydrogram/connection/transport/tcp/tcp.py @@ -17,107 +17,154 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . +from __future__ import annotations + import asyncio import ipaddress import logging import socket -from concurrent.futures import ThreadPoolExecutor +from typing import TypedDict import socks log = logging.getLogger(__name__) +proxy_type_by_scheme: dict[str, int] = { + "SOCKS4": socks.SOCKS4, + "SOCKS5": socks.SOCKS5, + "HTTP": socks.HTTP, +} + + +class Proxy(TypedDict): + scheme: str + hostname: str + port: int + username: str | None + password: str | None + class TCP: TIMEOUT = 10 - def __init__(self, ipv6: bool, proxy: dict): - self.socket = None + def __init__(self, ipv6: bool, proxy: Proxy) -> None: + self.ipv6 = ipv6 + self.proxy = proxy - self.reader = None - self.writer = None + self.reader: asyncio.StreamReader | None = None + self.writer: asyncio.StreamWriter | None = None self.lock = asyncio.Lock() - self.loop = asyncio.get_event_loop() + self.loop = asyncio.get_running_loop() + self._closed = True - self.proxy = proxy + @property + def closed(self) -> bool: + return ( + self._closed or self.writer is None or self.writer.is_closing() or self.reader is None + ) - if proxy: - hostname = proxy.get("hostname") + async def _connect_via_proxy(self, destination: tuple[str, int]) -> None: + scheme = self.proxy.get("scheme") + if scheme is None: + raise ValueError("No scheme specified") - try: - ip_address = ipaddress.ip_address(hostname) - except ValueError: - self.socket = socks.socksocket(socket.AF_INET) - else: - if isinstance(ip_address, ipaddress.IPv6Address): - self.socket = socks.socksocket(socket.AF_INET6) - else: - self.socket = socks.socksocket(socket.AF_INET) + proxy_type = proxy_type_by_scheme.get(scheme.upper()) + if proxy_type is None: + raise ValueError(f"Unknown proxy type {scheme}") - self.socket.set_proxy( - proxy_type=getattr(socks, proxy.get("scheme").upper()), - addr=hostname, - port=proxy.get("port"), - username=proxy.get("username"), - password=proxy.get("password"), - ) + hostname = self.proxy.get("hostname") + port = self.proxy.get("port") + username = self.proxy.get("username") + password = self.proxy.get("password") - self.socket.settimeout(TCP.TIMEOUT) - - log.info("Using proxy %s", hostname) + try: + ip_address = ipaddress.ip_address(hostname) + except ValueError: + is_proxy_ipv6 = False else: - self.socket = socket.socket(socket.AF_INET6 if ipv6 else socket.AF_INET) + is_proxy_ipv6 = isinstance(ip_address, ipaddress.IPv6Address) - self.socket.setblocking(False) + proxy_family = socket.AF_INET6 if is_proxy_ipv6 else socket.AF_INET + sock = socks.socksocket(proxy_family) - async def connect(self, address: tuple): + sock.set_proxy( + proxy_type=proxy_type, addr=hostname, port=port, username=username, password=password + ) + sock.settimeout(TCP.TIMEOUT) + + await self.loop.sock_connect(sock=sock, address=destination) + + sock.setblocking(False) + + self.reader, self.writer = await asyncio.open_connection(sock=sock) + + async def _connect_via_direct(self, destination: tuple[str, int]) -> None: + host, port = destination + family = socket.AF_INET6 if self.ipv6 else socket.AF_INET + self.reader, self.writer = await asyncio.open_connection( + host=host, port=port, family=family + ) + + async def _connect(self, destination: tuple[str, int]) -> None: if self.proxy: - with ThreadPoolExecutor(1) as executor: - await self.loop.run_in_executor(executor, self.socket.connect, address) + await self._connect_via_proxy(destination) else: - try: - await asyncio.wait_for( - asyncio.get_event_loop().sock_connect(self.socket, address), - TCP.TIMEOUT, - ) - except ( - asyncio.TimeoutError - ) as e: # Re-raise as TimeoutError. asyncio.TimeoutError is deprecated in 3.11 - raise TimeoutError("Connection timed out") from e - - self.reader, self.writer = await asyncio.open_connection(sock=self.socket) - - async def close(self): + await self._connect_via_direct(destination) + + async def connect(self, address: tuple[str, int]) -> None: + try: + await asyncio.wait_for(self._connect(address), TCP.TIMEOUT) + self._closed = False + except ( + asyncio.TimeoutError + ): # Re-raise as TimeoutError. asyncio.TimeoutError is deprecated in 3.11 + self._closed = True + raise TimeoutError("Connection timed out") + + async def close(self) -> None: + if self.writer is None: + self._closed = True + return + try: - if self.writer is not None: - self.writer.close() - await asyncio.wait_for(self.writer.wait_closed(), TCP.TIMEOUT) + self.writer.close() + await asyncio.wait_for(self.writer.wait_closed(), TCP.TIMEOUT) except Exception as e: log.info("Close exception: %s %s", type(e).__name__, e) + finally: + self._closed = True + + async def send(self, data: bytes) -> None: + if self.writer is None or self._closed: + raise OSError("Connection is closed") - async def send(self, data: bytes): async with self.lock: try: - if self.writer is not None: - self.writer.write(data) - await self.writer.drain() + self.writer.write(data) + await self.writer.drain() except Exception as e: log.info("Send exception: %s %s", type(e).__name__, e) + self._closed = True raise OSError(e) from e - async def recv(self, length: int = 0): + async def recv(self, length: int = 0) -> bytes | None: + if self._closed or self.reader is None: + return None + data = b"" while len(data) < length: try: chunk = await asyncio.wait_for(self.reader.read(length - len(data)), TCP.TIMEOUT) except (OSError, asyncio.TimeoutError): + self._closed = True return None else: if chunk: data += chunk else: + self._closed = True return None return data diff --git a/hydrogram/connection/transport/tcp/tcp_abridged.py b/hydrogram/connection/transport/tcp/tcp_abridged.py index 92010f1eb..e4a6d7450 100644 --- a/hydrogram/connection/transport/tcp/tcp_abridged.py +++ b/hydrogram/connection/transport/tcp/tcp_abridged.py @@ -21,20 +21,20 @@ import logging -from .tcp import TCP +from .tcp import TCP, Proxy log = logging.getLogger(__name__) class TCPAbridged(TCP): - def __init__(self, ipv6: bool, proxy: dict): + def __init__(self, ipv6: bool, proxy: Proxy) -> None: super().__init__(ipv6, proxy) - async def connect(self, address: tuple): + async def connect(self, address: tuple[str, int]) -> None: await super().connect(address) await super().send(b"\xef") - async def send(self, data: bytes, *args): + async def send(self, data: bytes, *args) -> None: length = len(data) // 4 await super().send( diff --git a/hydrogram/connection/transport/tcp/tcp_abridged_o.py b/hydrogram/connection/transport/tcp/tcp_abridged_o.py index 3cab96afd..61ac51dab 100644 --- a/hydrogram/connection/transport/tcp/tcp_abridged_o.py +++ b/hydrogram/connection/transport/tcp/tcp_abridged_o.py @@ -25,7 +25,7 @@ import hydrogram from hydrogram.crypto import aes -from .tcp import TCP +from .tcp import TCP, Proxy log = logging.getLogger(__name__) @@ -33,13 +33,13 @@ class TCPAbridgedO(TCP): RESERVED = (b"HEAD", b"POST", b"GET ", b"OPTI", b"\xee" * 4) - def __init__(self, ipv6: bool, proxy: dict): + def __init__(self, ipv6: bool, proxy: Proxy) -> None: super().__init__(ipv6, proxy) self.encrypt = None self.decrypt = None - async def connect(self, address: tuple): + async def connect(self, address: tuple[str, int]) -> None: await super().connect(address) while True: @@ -62,7 +62,7 @@ async def connect(self, address: tuple): await super().send(nonce) - async def send(self, data: bytes, *args): + async def send(self, data: bytes, *args) -> None: length = len(data) // 4 data = ( bytes([length]) if length <= 126 else b"\x7f" + length.to_bytes(3, "little") diff --git a/hydrogram/connection/transport/tcp/tcp_full.py b/hydrogram/connection/transport/tcp/tcp_full.py index 0e39f561e..49dedec6f 100644 --- a/hydrogram/connection/transport/tcp/tcp_full.py +++ b/hydrogram/connection/transport/tcp/tcp_full.py @@ -23,22 +23,22 @@ from binascii import crc32 from struct import pack, unpack -from .tcp import TCP +from .tcp import TCP, Proxy log = logging.getLogger(__name__) class TCPFull(TCP): - def __init__(self, ipv6: bool, proxy: dict): + def __init__(self, ipv6: bool, proxy: Proxy) -> None: super().__init__(ipv6, proxy) - self.seq_no = None + self.seq_no: int | None = None - async def connect(self, address: tuple): + async def connect(self, address: tuple[str, int]) -> None: await super().connect(address) self.seq_no = 0 - async def send(self, data: bytes, *args): + async def send(self, data: bytes, *args) -> None: data = pack(" None: super().__init__(ipv6, proxy) - async def connect(self, address: tuple): + async def connect(self, address: tuple[str, int]) -> None: await super().connect(address) await super().send(b"\xee" * 4) - async def send(self, data: bytes, *args): + async def send(self, data: bytes, *args) -> None: await super().send(pack(" bytes | None: diff --git a/hydrogram/connection/transport/tcp/tcp_intermediate_o.py b/hydrogram/connection/transport/tcp/tcp_intermediate_o.py index 4297058df..1c6daaca7 100644 --- a/hydrogram/connection/transport/tcp/tcp_intermediate_o.py +++ b/hydrogram/connection/transport/tcp/tcp_intermediate_o.py @@ -25,7 +25,7 @@ from hydrogram.crypto import aes -from .tcp import TCP +from .tcp import TCP, Proxy log = logging.getLogger(__name__) @@ -33,13 +33,13 @@ class TCPIntermediateO(TCP): RESERVED = (b"HEAD", b"POST", b"GET ", b"OPTI", b"\xee" * 4) - def __init__(self, ipv6: bool, proxy: dict): + def __init__(self, ipv6: bool, proxy: Proxy) -> None: super().__init__(ipv6, proxy) self.encrypt = None self.decrypt = None - async def connect(self, address: tuple): + async def connect(self, address: tuple[str, int]) -> None: await super().connect(address) while True: @@ -62,7 +62,7 @@ async def connect(self, address: tuple): await super().send(nonce) - async def send(self, data: bytes, *args): + async def send(self, data: bytes, *args) -> None: await super().send(aes.ctr256_encrypt(pack(" bytes | None: diff --git a/hydrogram/crypto/aes.py b/hydrogram/crypto/aes.py index 609be9642..205a28014 100644 --- a/hydrogram/crypto/aes.py +++ b/hydrogram/crypto/aes.py @@ -50,6 +50,7 @@ def xor(a: bytes, b: bytes) -> bytes: len(a), "big", ) + except ImportError: import pyaes diff --git a/hydrogram/dispatcher.py b/hydrogram/dispatcher.py index 7cb04989d..f0cdf3e9a 100644 --- a/hydrogram/dispatcher.py +++ b/hydrogram/dispatcher.py @@ -17,13 +17,16 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . +from __future__ import annotations + import asyncio import inspect import logging from collections import OrderedDict +from typing import TYPE_CHECKING, Callable, TypeVar, cast import hydrogram -from hydrogram import utils +from hydrogram import raw, types, utils from hydrogram.handlers import ( CallbackQueryHandler, ChatJoinRequestHandler, @@ -57,6 +60,16 @@ UpdateUserStatus, ) +if TYPE_CHECKING: + from collections.abc import Awaitable + + from hydrogram.handlers.handler import Handler + + UpdateType = TypeVar("UpdateType", bound=types.Update) + HandlerType = TypeVar("HandlerType", bound=type["Handler"]) + RawUpdateType = TypeVar("RawUpdateType", bound=raw.core.TLObject) + + log = logging.getLogger(__name__) @@ -72,34 +85,51 @@ class Dispatcher: CHOSEN_INLINE_RESULT_UPDATES = (UpdateBotInlineSend,) CHAT_JOIN_REQUEST_UPDATES = (UpdateBotChatInviteRequester,) - def __init__(self, client: "hydrogram.Client"): + def __init__(self, client: hydrogram.Client): self.client = client - self.loop = asyncio.get_event_loop() - self.handler_worker_tasks = [] - self.locks_list = [] + self.handler_worker_tasks: list[asyncio.Task] = [] + self.locks_list: list[asyncio.Lock] = [] self.updates_queue = asyncio.Queue() - self.groups = OrderedDict() + self.groups: dict[int, list[Handler]] = OrderedDict() + self.error_handlers: list[ErrorHandler] = [] self._init_update_parsers() - self.error_handlers = [] - - def _init_update_parsers(self): - self.update_parsers = { - Dispatcher.NEW_MESSAGE_UPDATES: self._message_parser, - Dispatcher.EDIT_MESSAGE_UPDATES: self._edited_message_parser, - Dispatcher.DELETE_MESSAGES_UPDATES: self._deleted_messages_parser, - Dispatcher.CALLBACK_QUERY_UPDATES: self._callback_query_parser, - Dispatcher.USER_STATUS_UPDATES: self._user_status_parser, - Dispatcher.BOT_INLINE_QUERY_UPDATES: self._inline_query_parser, - Dispatcher.POLL_UPDATES: self._poll_parser, - Dispatcher.CHOSEN_INLINE_RESULT_UPDATES: self._chosen_inline_result_parser, - Dispatcher.CHAT_MEMBER_UPDATES: self._chat_member_updated_parser, - Dispatcher.CHAT_JOIN_REQUEST_UPDATES: self._chat_join_request_parser, - } - self.update_parsers = { - key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple + + def _init_update_parsers(self) -> None: + update_parsers = { + ( + UpdateNewMessage, + UpdateNewChannelMessage, + UpdateNewScheduledMessage, + ): self._message_parser, + (UpdateEditMessage, UpdateEditChannelMessage): self._edited_message_parser, + (UpdateDeleteMessages, UpdateDeleteChannelMessages): self._deleted_messages_parser, + (UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery): self._callback_query_parser, + (UpdateUserStatus,): self._user_status_parser, + (UpdateBotInlineQuery,): self._inline_query_parser, + (UpdateMessagePoll,): self._poll_parser, + (UpdateBotInlineSend,): self._chosen_inline_result_parser, + (UpdateChatParticipant, UpdateChannelParticipant): self._chat_member_updated_parser, + (UpdateBotChatInviteRequester,): self._chat_join_request_parser, } - async def _message_parser(self, update, users, chats): + self.update_parsers: dict[ + type[raw.core.TLObject], + Callable[ + ..., + Awaitable[tuple[types.Update, type[Handler]]] | tuple[types.Update, type[Handler]], + ], + ] = {} + + for key_tuple, parser in update_parsers.items(): + for key in key_tuple: + self.update_parsers[key] = parser + + async def _message_parser( + self, + update: UpdateNewMessage, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + ) -> tuple[types.Message | None, type[MessageHandler]]: return ( await hydrogram.types.Message._parse( client=self.client, @@ -111,51 +141,96 @@ async def _message_parser(self, update, users, chats): MessageHandler, ) - async def _edited_message_parser(self, update, users, chats): + async def _edited_message_parser( + self, + update: UpdateNewMessage, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + ) -> tuple[types.Message | None, type[EditedMessageHandler]]: parsed, _ = await self._message_parser(update, users, chats) return parsed, EditedMessageHandler - def _deleted_messages_parser(self, update, users, chats): + def _deleted_messages_parser( + self, + update: UpdateDeleteMessages | UpdateDeleteChannelMessages, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + ) -> tuple[list[types.Message], type[DeletedMessagesHandler]]: return utils.parse_deleted_messages(self.client, update), DeletedMessagesHandler - async def _callback_query_parser(self, update, users, chats): + async def _callback_query_parser( + self, + update: UpdateBotCallbackQuery | UpdateInlineBotCallbackQuery, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + ) -> tuple[types.CallbackQuery, type[CallbackQueryHandler]]: return await hydrogram.types.CallbackQuery._parse( self.client, update, users ), CallbackQueryHandler - def _user_status_parser(self, update, users, chats): + def _user_status_parser( + self, + update: UpdateUserStatus, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + ) -> tuple[types.User, type[UserStatusHandler]]: return hydrogram.types.User._parse_user_status(self.client, update), UserStatusHandler - def _inline_query_parser(self, update, users, chats): + def _inline_query_parser( + self, + update: UpdateBotInlineQuery, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + ) -> tuple[types.InlineQuery, type[InlineQueryHandler]]: return hydrogram.types.InlineQuery._parse(self.client, update, users), InlineQueryHandler - def _poll_parser(self, update, users, chats): + def _poll_parser( + self, + update: UpdateMessagePoll, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + ) -> tuple[types.Poll | None, type[PollHandler]]: return hydrogram.types.Poll._parse_update(self.client, update), PollHandler - def _chosen_inline_result_parser(self, update, users, chats): + def _chosen_inline_result_parser( + self, + update: UpdateBotInlineSend, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + ) -> tuple[types.ChosenInlineResult, type[ChosenInlineResultHandler]]: return hydrogram.types.ChosenInlineResult._parse( self.client, update, users ), ChosenInlineResultHandler - def _chat_member_updated_parser(self, update, users, chats): + def _chat_member_updated_parser( + self, + update: UpdateChatParticipant | UpdateChannelParticipant, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + ) -> tuple[types.ChatMemberUpdated, type[ChatMemberUpdatedHandler]]: return hydrogram.types.ChatMemberUpdated._parse( self.client, update, users, chats ), ChatMemberUpdatedHandler - def _chat_join_request_parser(self, update, users, chats): + def _chat_join_request_parser( + self, + update: UpdateBotChatInviteRequester, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + ) -> tuple[types.ChatJoinRequest, type[ChatJoinRequestHandler]]: return hydrogram.types.ChatJoinRequest._parse( self.client, update, users, chats ), ChatJoinRequestHandler - async def start(self): + async def start(self) -> None: if not self.client.no_updates: self.locks_list = [asyncio.Lock() for _ in range(self.client.workers)] self.handler_worker_tasks = [ - self.loop.create_task(self.handler_worker(lock)) for lock in self.locks_list + self.client.loop.create_task(self.handler_worker(lock)) for lock in self.locks_list ] log.info("Started %s HandlerTasks", self.client.workers) - async def stop(self): + async def stop(self) -> None: if not self.client.no_updates: for _ in range(self.client.workers): self.updates_queue.put_nowait(None) @@ -166,62 +241,58 @@ async def stop(self): log.info("Stopped %s HandlerTasks", self.client.workers) - def add_handler(self, handler, group: int): - async def fn(): - async with asyncio.Lock(): - if isinstance(handler, ErrorHandler): - if handler not in self.error_handlers: - self.error_handlers.append(handler) - else: - if group not in self.groups: - self.groups[group] = [] - self.groups = OrderedDict(sorted(self.groups.items())) - self.groups[group].append(handler) - - self.loop.create_task(fn()) - - def remove_handler(self, handler, group: int): - async def fn(): - async with asyncio.Lock(): - if isinstance(handler, ErrorHandler): - if handler not in self.error_handlers: - raise ValueError( - f"Error handler {handler} does not exist. Handler was not removed." - ) - - self.error_handlers.remove(handler) - else: - if group not in self.groups: - raise ValueError(f"Group {group} does not exist. Handler was not removed.") - self.groups[group].remove(handler) - - self.loop.create_task(fn()) + def add_handler(self, handler: Handler, group: int) -> None: + if isinstance(handler, ErrorHandler): + if handler not in self.error_handlers: + self.error_handlers.append(handler) + else: + if group not in self.groups: + self.groups[group] = [] + self.groups = OrderedDict(sorted(self.groups.items())) + self.groups[group].append(handler) + + def remove_handler(self, handler: Handler, group: int) -> None: + if isinstance(handler, ErrorHandler): + if handler not in self.error_handlers: + raise ValueError( + f"Error handler {handler} does not exist. Handler was not removed." + ) + + self.error_handlers.remove(handler) + else: + if group not in self.groups: + raise ValueError(f"Group {group} does not exist. Handler was not removed.") + self.groups[group].remove(handler) - async def handler_worker(self, lock): + async def handler_worker(self, lock: asyncio.Lock) -> None: while True: packet = await self.updates_queue.get() if packet is None: break await self._process_packet(packet, lock) - async def _process_packet(self, packet, lock): + async def _process_packet( + self, + packet: tuple[raw.core.TLObject, dict[int, raw.types.User], dict[int, raw.types.Chat]], + lock: asyncio.Lock, + ) -> None: try: update, users, chats = packet parser = self.update_parsers.get(type(update)) - if not parser: - return - if inspect.iscoroutinefunction(parser): - parsed_update, handler_type = await parser(update, users, chats) + if parser is not None: + parsed_result = parser(update, users, chats) + if inspect.isawaitable(parsed_result): + parsed_update, handler_type = await parsed_result + else: + parsed_update, handler_type = cast( + "tuple[types.Update, type[Handler]]", parsed_result + ) else: - parsed_update, handler_type = parser(update, users, chats) - - async with lock: - for group in self.groups.values(): - for handler in group: - await self._handle_update( - handler, handler_type, parsed_update, update, users, chats - ) + parsed_update = None + handler_type = None + + await self._process_update(lock, update, users, chats, parsed_update, handler_type) except hydrogram.StopPropagation: pass except Exception as e: @@ -229,40 +300,58 @@ async def _process_packet(self, packet, lock): finally: self.updates_queue.task_done() - async def _handle_update(self, handler, handler_type, parsed_update, update, users, chats): - try: - if isinstance(handler, handler_type): - if await handler.check(self.client, parsed_update): - await self._execute_callback(handler, parsed_update) - elif isinstance(handler, RawUpdateHandler): - await self._execute_callback(handler, update, users, chats) - except hydrogram.StopPropagation: - raise - except hydrogram.ContinuePropagation: - pass - except Exception as e: - handled_error = False - for error_handler in self.error_handlers: - try: - if await error_handler.check(self.client, e): - await error_handler.callback(self.client, e) - handled_error = True - break - except hydrogram.StopPropagation: - raise - except hydrogram.ContinuePropagation: - continue - except Exception as e: - log.exception(e) - continue - - if not handled_error: - log.exception(e) - - async def _execute_callback(self, handler, *args): + async def _process_update( + self, + lock: asyncio.Lock, + raw_update: raw.core.TLObject, + users: dict[int, raw.types.User], + chats: dict[int, raw.types.Chat], + parsed_update: types.Update | None, + handler_type: type[Handler] | None, + ) -> None: + async with lock: + for group in self.groups.values(): + for handler in group: + try: + if isinstance(handler, RawUpdateHandler): + await self._execute_callback(handler, raw_update, users, chats) + continue + if ( + parsed_update is not None + and isinstance(handler, handler_type) + and await handler.check(self.client, parsed_update) + ): + await self._execute_callback(handler, parsed_update) + break + except (hydrogram.StopPropagation, hydrogram.ContinuePropagation) as e: + if isinstance(e, hydrogram.StopPropagation): + raise + except Exception as exception: + if parsed_update is not None: + await self._handle_exception(parsed_update, exception) + + async def _handle_exception(self, parsed_update: types.Update, exception: Exception) -> None: + handled_error = False + + for error_handler in self.error_handlers: + try: + if await error_handler.check(self.client, parsed_update, exception): + handled_error = True + break + except hydrogram.StopPropagation: + raise + except hydrogram.ContinuePropagation: + continue + except Exception as inner_exception: + log.exception("Error in error handler: %s", inner_exception) + + if not handled_error: + log.exception("Unhandled exception: %s", exception) + + async def _execute_callback(self, handler: Handler, *args) -> None: if inspect.iscoroutinefunction(handler.callback): await handler.callback(self.client, *args) else: - await self.loop.run_in_executor( + await self.client.loop.run_in_executor( self.client.executor, handler.callback, self.client, *args ) diff --git a/hydrogram/errors/__init__.py b/hydrogram/errors/__init__.py index 040a45899..9fe851629 100644 --- a/hydrogram/errors/__init__.py +++ b/hydrogram/errors/__init__.py @@ -21,480 +21,7 @@ from typing import ClassVar -from .exceptions import ( - AboutTooLong, - AccessTokenExpired, - AccessTokenInvalid, - ActiveUserRequired, - AdminRankEmojiNotAllowed, - AdminRankInvalid, - AdminsTooMuch, - AlbumPhotosTooMany, - ApiCallError, - ApiIdInvalid, - ApiIdPublishedFlood, - ArticleTitleEmpty, - AudioTitleEmpty, - AuthBytesInvalid, - AuthKeyDuplicated, - AuthKeyInvalid, - AuthKeyPermEmpty, - AuthKeyUnregistered, - AuthRestart, - AuthTokenAlreadyAccepted, - AuthTokenExpired, - AuthTokenInvalid, - AutoarchiveNotAvailable, - BadRequest, - BankCardNumberInvalid, - BannedRightsInvalid, - BasePortLocInvalid, - BotChannelsNa, - BotCommandDescriptionInvalid, - BotDomainInvalid, - BotGamesDisabled, - BotGroupsBlocked, - BotInlineDisabled, - BotInvalid, - BotMethodInvalid, - BotMissing, - BotOnesideNotAvail, - BotPaymentsDisabled, - BotPollsDisabled, - BotResponseTimeout, - BotScoreNotModified, - BotsTooMuch, - BroadcastForbidden, - BroadcastIdInvalid, - BroadcastPublicVotersForbidden, - BroadcastRequired, - ButtonDataInvalid, - ButtonTypeInvalid, - ButtonUrlInvalid, - ButtonUserPrivacyRestricted, - CallAlreadyAccepted, - CallAlreadyDeclined, - CallOccupyFailed, - CallPeerInvalid, - CallProtocolFlagsInvalid, - CdnMethodInvalid, - ChannelAddInvalid, - ChannelBanned, - ChannelInvalid, - ChannelPrivate, - ChannelPublicGroupNa, - ChannelsAdminPublicTooMuch, - ChannelsTooMuch, - ChannelTooLarge, - ChatAboutNotModified, - ChatAboutTooLong, - ChatAdminInviteRequired, - ChatAdminRequired, - ChatDiscussionUnallowed, - ChatForbidden, - ChatForwardsRestricted, - ChatIdEmpty, - ChatIdGenerateFailed, - ChatIdInvalid, - ChatInvalid, - ChatInvitePermanent, - ChatLinkExists, - ChatNotModified, - ChatOccupyLocFailed, - ChatOccupyUsernameFailed, - ChatRestricted, - ChatSendGifsForbidden, - ChatSendInlineForbidden, - ChatSendMediaForbidden, - ChatSendPollForbidden, - ChatSendStickersForbidden, - ChatTitleEmpty, - ChatTooBig, - ChatWriteForbidden, - ChpCallFail, - CodeEmpty, - CodeHashInvalid, - CodeInvalid, - ConnectionApiIdInvalid, - ConnectionAppVersionEmpty, - ConnectionDeviceModelEmpty, - ConnectionLangPackInvalid, - ConnectionLayerInvalid, - ConnectionNotInited, - ConnectionSystemEmpty, - ConnectionSystemLangCodeEmpty, - ContactAddMissing, - ContactIdInvalid, - ContactNameEmpty, - ContactReqMissing, - DataInvalid, - DataJsonInvalid, - DataTooLong, - DateEmpty, - DcIdInvalid, - DhGAInvalid, - DocumentInvalid, - EditBotInviteForbidden, - EmailHashExpired, - EmailInvalid, - EmailUnconfirmed, - EmailVerifyExpired, - EmoticonEmpty, - EmoticonInvalid, - EmoticonStickerpackMissing, - EncryptedMessageInvalid, - EncryptionAlreadyAccepted, - EncryptionAlreadyDeclined, - EncryptionDeclined, - EncryptionIdInvalid, - EncryptionOccupyAdminFailed, - EncryptionOccupyFailed, - EntitiesTooLong, - EntityBoundsInvalid, - EntityMentionUserInvalid, - ErrorTextEmpty, - ExpireDateInvalid, - ExportCardInvalid, - ExternalUrlInvalid, - FieldNameEmpty, - FieldNameInvalid, - FileIdInvalid, - FileMigrate, - FilePartEmpty, - FilePartInvalid, - FilePartLengthInvalid, - FilePartMissing, - FilePartsInvalid, - FilePartSizeChanged, - FilePartSizeInvalid, - FilePartTooBig, - FileReferenceEmpty, - FileReferenceExpired, - FileReferenceInvalid, - FilerefUpgradeNeeded, - FilterIdInvalid, - FirstnameInvalid, - Flood, - FloodTestPhoneWait, - FloodWait, - FolderDeacAutofixAll, - FolderIdEmpty, - FolderIdInvalid, - Forbidden, - FreshChangeAdminsForbidden, - FreshChangePhoneForbidden, - FreshResetAuthorisationForbidden, - FromMessageBotDisabled, - FromPeerInvalid, - GameBotInvalid, - GeoPointInvalid, - GifContentTypeInvalid, - GifIdInvalid, - GraphInvalidReload, - GraphOutdatedReload, - GroupcallAddParticipantsFailed, - GroupCallInvalid, - GroupcallSsrcDuplicateMuch, - GroupedIdOccupyFailed, - GroupedMediaInvalid, - HashInvalid, - HistoryGetFailed, - ImageEngineDown, - ImageProcessFailed, - ImportFileInvalid, - ImportFormatUnrecognized, - ImportIdInvalid, - InlineBotRequired, - InlineResultExpired, - InputConstructorInvalid, - InputFetchError, - InputFetchFail, - InputFilterInvalid, - InputLayerInvalid, - InputMethodInvalid, - InputRequestTooLong, - InputUserDeactivated, - InterdcCallError, - InterdcCallRichError, - InternalServerError, - InviteHashEmpty, - InviteHashExpired, - InviteHashInvalid, - InviteRequestSent, - InviteRevokedMissing, - LangPackInvalid, - LastnameInvalid, - LimitInvalid, - LinkNotModified, - LocationInvalid, - MaxIdInvalid, - MaxQtsInvalid, - Md5ChecksumInvalid, - MediaCaptionTooLong, - MediaEmpty, - MediaFileInvalid, - MediaInvalid, - MediaNewInvalid, - MediaPrevInvalid, - MediaTtlInvalid, - MediaVideoStoryMissing, - MegagroupIdInvalid, - MegagroupPrehistoryHidden, - MegagroupRequired, - MemberFetchFailed, - MemberNoLocation, - MemberOccupyPrimaryLocFailed, - MemberOccupyUsernameFailed, - MessageAuthorRequired, - MessageDeleteForbidden, - MessageEditTimeExpired, - MessageEmpty, - MessageIdInvalid, - MessageIdsEmpty, - MessageNotModified, - MessagePollClosed, - MessageTooLong, - MethodInvalid, - MsgidDecreaseRetry, - MsgIdInvalid, - MsgRangeUnsync, - MsgVoiceMissing, - MsgWaitFailed, - MtSendQueueTooLong, - MultiMediaTooLong, - NeedChatInvalid, - NeedMemberInvalid, - NetworkMigrate, - NewSaltInvalid, - NewSettingsInvalid, - NextOffsetInvalid, - NotAcceptable, - NoWorkersRunning, - OffsetInvalid, - OffsetPeerIdInvalid, - OptionInvalid, - OptionsTooMuch, - PackShortNameInvalid, - PackShortNameOccupied, - PackTitleInvalid, - ParticipantCallFailed, - ParticipantsTooFew, - ParticipantVersionOutdated, - PasswordEmpty, - PasswordHashInvalid, - PasswordMissing, - PasswordRecoveryNa, - PasswordRequired, - PasswordTooFresh, - PaymentProviderInvalid, - PeerFlood, - PeerIdInvalid, - PeerIdNotSupported, - PersistentTimestampEmpty, - PersistentTimestampInvalid, - PersistentTimestampOutdated, - PhoneCodeEmpty, - PhoneCodeExpired, - PhoneCodeHashEmpty, - PhoneCodeInvalid, - PhoneMigrate, - PhoneNumberAppSignupForbidden, - PhoneNumberBanned, - PhoneNumberFlood, - PhoneNumberInvalid, - PhoneNumberOccupied, - PhoneNumberUnoccupied, - PhonePasswordFlood, - PhonePasswordProtected, - PhotoContentTypeInvalid, - PhotoContentUrlEmpty, - PhotoCreateFailed, - PhotoCropFileMissing, - PhotoCropSizeSmall, - PhotoExtInvalid, - PhotoFileMissing, - PhotoIdInvalid, - PhotoInvalid, - PhotoInvalidDimensions, - PhotoSaveFileInvalid, - PhotoThumbUrlEmpty, - PhotoThumbUrlInvalid, - PinnedDialogsTooMuch, - PinRestricted, - PollAnswersInvalid, - PollOptionDuplicate, - PollOptionInvalid, - PollQuestionInvalid, - PollUnsupported, - PollVoteRequired, - PostponedTimeout, - PremiumAccountRequired, - PrivacyKeyInvalid, - PrivacyTooLong, - PrivacyValueInvalid, - PtsChangeEmpty, - QueryIdEmpty, - QueryIdInvalid, - QueryTooShort, - QuizCorrectAnswerInvalid, - QuizCorrectAnswersEmpty, - QuizCorrectAnswersTooMuch, - QuizMultipleInvalid, - RandomIdDuplicate, - RandomIdEmpty, - RandomIdInvalid, - RandomLengthInvalid, - RangesInvalid, - ReactionEmpty, - ReactionInvalid, - ReflectorNotAvailable, - RegIdGenerateFailed, - ReplyMarkupBuyEmpty, - ReplyMarkupGameEmpty, - ReplyMarkupInvalid, - ReplyMarkupTooLong, - ResultIdDuplicate, - ResultIdEmpty, - ResultIdInvalid, - ResultsTooMuch, - ResultTypeInvalid, - RevoteNotAllowed, - RightForbidden, - RpcCallFail, - RpcConnectFailed, - RPCError, - RpcMcgetFail, - RsaDecryptFailed, - ScheduleBotNotAllowed, - ScheduleDateInvalid, - ScheduleDateTooLate, - ScheduleStatusPrivate, - ScheduleTooMuch, - SearchQueryEmpty, - SecondsInvalid, - SeeOther, - SendMessageMediaInvalid, - SendMessageTypeInvalid, - SensitiveChangeForbidden, - ServiceUnavailable, - SessionExpired, - SessionPasswordNeeded, - SessionRevoked, - SessionTooFresh, - SettingsInvalid, - Sha256HashInvalid, - ShortnameOccupyFailed, - SignInFailed, - SlowmodeMultiMsgsDisabled, - SlowmodeWait, - SmsCodeCreateFailed, - SrpIdInvalid, - SrpPasswordChanged, - StartParamEmpty, - StartParamInvalid, - StartParamTooLong, - StatsMigrate, - StickerDocumentInvalid, - StickerEmojiInvalid, - StickerFileInvalid, - StickerIdInvalid, - StickerInvalid, - StickerPngDimensions, - StickerPngNopng, - StickersEmpty, - StickersetInvalid, - StickersetNotModified, - StickersetOwnerAnonymous, - StickersTooMuch, - StickerTgsNotgs, - StickerThumbPngNopng, - StickerVideoNowebm, - StorageCheckFailed, - StoreInvalidScalarType, - StoriesTooMuch, - TakeoutInitDelay, - TakeoutInvalid, - TakeoutRequired, - TempAuthKeyEmpty, - ThemeFileInvalid, - ThemeFormatInvalid, - ThemeInvalid, - ThemeMimeInvalid, - Timeout, - TmpPasswordDisabled, - TmpPasswordInvalid, - TokenInvalid, - TopicClosed, - TopicDeleted, - TranscriptionFailed, - TtlDaysInvalid, - TtlMediaInvalid, - TtlPeriodInvalid, - TwoFaConfirmWait, - TypeConstructorInvalid, - TypesEmpty, - Unauthorized, - UnknownMethod, - UntilDateInvalid, - UploadNoVolume, - UrlInvalid, - UsageLimitInvalid, - UserAdminInvalid, - UserAlreadyParticipant, - UserBannedInChannel, - UserBlocked, - UserBot, - UserBotInvalid, - UserBotRequired, - UserChannelsTooMuch, - UserCreator, - UserDeactivated, - UserDeactivatedBan, - UserIdInvalid, - UserInvalid, - UserIsBlocked, - UserIsBot, - UserKicked, - UserMigrate, - UsernameInvalid, - UsernameNotModified, - UsernameNotOccupied, - UsernameOccupied, - UserNotMutualContact, - UserNotParticipant, - UserpicUploadRequired, - UserPrivacyRestricted, - UserRestricted, - UsersTooFew, - UsersTooMuch, - VideoContentTypeInvalid, - VideoFileInvalid, - VoiceMessagesForbidden, - VolumeLocNotFound, - WallpaperFileInvalid, - WallpaperInvalid, - WallpaperMimeInvalid, - WcConvertUrlInvalid, - WebdocumentInvalid, - WebdocumentMimeInvalid, - WebdocumentSizeTooBig, - WebdocumentUrlEmpty, - WebdocumentUrlInvalid, - WebpageCurlFailed, - WebpageMediaEmpty, - WorkerBusyTooLongRetry, - WpIdGenerateFailed, - YouBlockedUser, - all, - bad_request_400, - flood_420, - forbidden_403, - internal_server_error_500, - not_acceptable_406, - see_other_303, - service_unavailable_503, - unauthorized_401, -) +from .exceptions import * # noqa: F403 from .pyromod import ListenerStopped, ListenerTimeout from .rpc_error import UnknownError @@ -545,479 +72,11 @@ def __init__(self, msg: str | None = None): __all__ = [ - "AboutTooLong", - "AccessTokenExpired", - "AccessTokenInvalid", - "ActiveUserRequired", - "AdminRankEmojiNotAllowed", - "AdminRankInvalid", - "AdminsTooMuch", - "AlbumPhotosTooMany", - "ApiCallError", - "ApiIdInvalid", - "ApiIdPublishedFlood", - "ArticleTitleEmpty", - "AudioTitleEmpty", - "AuthBytesInvalid", - "AuthKeyDuplicated", - "AuthKeyInvalid", - "AuthKeyPermEmpty", - "AuthKeyUnregistered", - "AuthRestart", - "AuthTokenAlreadyAccepted", - "AuthTokenExpired", - "AuthTokenInvalid", - "AutoarchiveNotAvailable", - "BadRequest", - "BankCardNumberInvalid", - "BannedRightsInvalid", - "BasePortLocInvalid", - "BotChannelsNa", - "BotCommandDescriptionInvalid", - "BotDomainInvalid", - "BotGamesDisabled", - "BotGroupsBlocked", - "BotInlineDisabled", - "BotInvalid", - "BotMethodInvalid", - "BotMissing", - "BotOnesideNotAvail", - "BotPaymentsDisabled", - "BotPollsDisabled", - "BotResponseTimeout", - "BotScoreNotModified", - "BotsTooMuch", - "BroadcastForbidden", - "BroadcastIdInvalid", - "BroadcastPublicVotersForbidden", - "BroadcastRequired", - "ButtonDataInvalid", - "ButtonTypeInvalid", - "ButtonUrlInvalid", - "ButtonUserPrivacyRestricted", - "CallAlreadyAccepted", - "CallAlreadyDeclined", - "CallOccupyFailed", - "CallPeerInvalid", - "CallProtocolFlagsInvalid", - "CdnMethodInvalid", - "ChannelAddInvalid", - "ChannelBanned", - "ChannelInvalid", - "ChannelPrivate", - "ChannelPublicGroupNa", - "ChannelTooLarge", - "ChannelsAdminPublicTooMuch", - "ChannelsTooMuch", - "ChatAboutNotModified", - "ChatAboutTooLong", - "ChatAdminInviteRequired", - "ChatAdminRequired", - "ChatDiscussionUnallowed", - "ChatForbidden", - "ChatForwardsRestricted", - "ChatIdEmpty", - "ChatIdGenerateFailed", - "ChatIdInvalid", - "ChatInvalid", - "ChatInvitePermanent", - "ChatLinkExists", - "ChatNotModified", - "ChatOccupyLocFailed", - "ChatOccupyUsernameFailed", - "ChatRestricted", - "ChatSendGifsForbidden", - "ChatSendInlineForbidden", - "ChatSendMediaForbidden", - "ChatSendPollForbidden", - "ChatSendStickersForbidden", - "ChatTitleEmpty", - "ChatTooBig", - "ChatWriteForbidden", - "ChpCallFail", - "CodeEmpty", - "CodeHashInvalid", - "CodeInvalid", - "ConnectionApiIdInvalid", - "ConnectionAppVersionEmpty", - "ConnectionDeviceModelEmpty", - "ConnectionLangPackInvalid", - "ConnectionLayerInvalid", - "ConnectionNotInited", - "ConnectionSystemEmpty", - "ConnectionSystemLangCodeEmpty", - "ContactAddMissing", - "ContactIdInvalid", - "ContactNameEmpty", - "ContactReqMissing", - "DataInvalid", - "DataJsonInvalid", - "DataTooLong", - "DateEmpty", - "DcIdInvalid", - "DhGAInvalid", - "DocumentInvalid", - "EditBotInviteForbidden", - "EmailHashExpired", - "EmailInvalid", - "EmailUnconfirmed", - "EmailVerifyExpired", - "EmoticonEmpty", - "EmoticonInvalid", - "EmoticonStickerpackMissing", - "EncryptedMessageInvalid", - "EncryptionAlreadyAccepted", - "EncryptionAlreadyDeclined", - "EncryptionDeclined", - "EncryptionIdInvalid", - "EncryptionOccupyAdminFailed", - "EncryptionOccupyFailed", - "EntitiesTooLong", - "EntityBoundsInvalid", - "EntityMentionUserInvalid", - "ErrorTextEmpty", - "ExpireDateInvalid", - "ExportCardInvalid", - "ExternalUrlInvalid", - "FieldNameEmpty", - "FieldNameInvalid", - "FileIdInvalid", - "FileMigrate", - "FilePartEmpty", - "FilePartInvalid", - "FilePartLengthInvalid", - "FilePartMissing", - "FilePartSizeChanged", - "FilePartSizeInvalid", - "FilePartTooBig", - "FilePartsInvalid", - "FileReferenceEmpty", - "FileReferenceExpired", - "FileReferenceInvalid", - "FilerefUpgradeNeeded", - "FilterIdInvalid", - "FirstnameInvalid", - "Flood", - "FloodTestPhoneWait", - "FloodWait", - "FolderDeacAutofixAll", - "FolderIdEmpty", - "FolderIdInvalid", - "Forbidden", - "FreshChangeAdminsForbidden", - "FreshChangePhoneForbidden", - "FreshResetAuthorisationForbidden", - "FromMessageBotDisabled", - "FromPeerInvalid", - "GameBotInvalid", - "GeoPointInvalid", - "GifContentTypeInvalid", - "GifIdInvalid", - "GraphInvalidReload", - "GraphOutdatedReload", - "GroupCallInvalid", - "GroupcallAddParticipantsFailed", - "GroupcallSsrcDuplicateMuch", - "GroupedIdOccupyFailed", - "GroupedMediaInvalid", - "HashInvalid", - "HistoryGetFailed", - "ImageEngineDown", - "ImageProcessFailed", - "ImportFileInvalid", - "ImportFormatUnrecognized", - "ImportIdInvalid", - "InlineBotRequired", - "InlineResultExpired", - "InputConstructorInvalid", - "InputFetchError", - "InputFetchFail", - "InputFilterInvalid", - "InputLayerInvalid", - "InputMethodInvalid", - "InputRequestTooLong", - "InputUserDeactivated", - "InterdcCallError", - "InterdcCallRichError", - "InternalServerError", - "InviteHashEmpty", - "InviteHashExpired", - "InviteHashInvalid", - "InviteRequestSent", - "InviteRevokedMissing", - "LangPackInvalid", - "LastnameInvalid", - "LimitInvalid", - "LinkNotModified", + "BadMsgNotification", + "CDNFileHashMismatch", "ListenerStopped", "ListenerTimeout", - "LocationInvalid", - "MaxIdInvalid", - "MaxQtsInvalid", - "Md5ChecksumInvalid", - "MediaCaptionTooLong", - "MediaEmpty", - "MediaFileInvalid", - "MediaInvalid", - "MediaNewInvalid", - "MediaPrevInvalid", - "MediaTtlInvalid", - "MediaVideoStoryMissing", - "MegagroupIdInvalid", - "MegagroupPrehistoryHidden", - "MegagroupRequired", - "MemberFetchFailed", - "MemberNoLocation", - "MemberOccupyPrimaryLocFailed", - "MemberOccupyUsernameFailed", - "MessageAuthorRequired", - "MessageDeleteForbidden", - "MessageEditTimeExpired", - "MessageEmpty", - "MessageIdInvalid", - "MessageIdsEmpty", - "MessageNotModified", - "MessagePollClosed", - "MessageTooLong", - "MethodInvalid", - "MsgIdInvalid", - "MsgRangeUnsync", - "MsgVoiceMissing", - "MsgWaitFailed", - "MsgidDecreaseRetry", - "MtSendQueueTooLong", - "MultiMediaTooLong", - "NeedChatInvalid", - "NeedMemberInvalid", - "NetworkMigrate", - "NewSaltInvalid", - "NewSettingsInvalid", - "NextOffsetInvalid", - "NoWorkersRunning", - "NotAcceptable", - "OffsetInvalid", - "OffsetPeerIdInvalid", - "OptionInvalid", - "OptionsTooMuch", - "PackShortNameInvalid", - "PackShortNameOccupied", - "PackTitleInvalid", - "ParticipantCallFailed", - "ParticipantVersionOutdated", - "ParticipantsTooFew", - "PasswordEmpty", - "PasswordHashInvalid", - "PasswordMissing", - "PasswordRecoveryNa", - "PasswordRequired", - "PasswordTooFresh", - "PaymentProviderInvalid", - "PeerFlood", - "PeerIdInvalid", - "PeerIdNotSupported", - "PersistentTimestampEmpty", - "PersistentTimestampInvalid", - "PersistentTimestampOutdated", - "PhoneCodeEmpty", - "PhoneCodeExpired", - "PhoneCodeHashEmpty", - "PhoneCodeInvalid", - "PhoneMigrate", - "PhoneNumberAppSignupForbidden", - "PhoneNumberBanned", - "PhoneNumberFlood", - "PhoneNumberInvalid", - "PhoneNumberOccupied", - "PhoneNumberUnoccupied", - "PhonePasswordFlood", - "PhonePasswordProtected", - "PhotoContentTypeInvalid", - "PhotoContentUrlEmpty", - "PhotoCreateFailed", - "PhotoCropFileMissing", - "PhotoCropSizeSmall", - "PhotoExtInvalid", - "PhotoFileMissing", - "PhotoIdInvalid", - "PhotoInvalid", - "PhotoInvalidDimensions", - "PhotoSaveFileInvalid", - "PhotoThumbUrlEmpty", - "PhotoThumbUrlInvalid", - "PinRestricted", - "PinnedDialogsTooMuch", - "PollAnswersInvalid", - "PollOptionDuplicate", - "PollOptionInvalid", - "PollQuestionInvalid", - "PollUnsupported", - "PollVoteRequired", - "PostponedTimeout", - "PremiumAccountRequired", - "PrivacyKeyInvalid", - "PrivacyTooLong", - "PrivacyValueInvalid", - "PtsChangeEmpty", - "QueryIdEmpty", - "QueryIdInvalid", - "QueryTooShort", - "QuizCorrectAnswerInvalid", - "QuizCorrectAnswersEmpty", - "QuizCorrectAnswersTooMuch", - "QuizMultipleInvalid", - "RPCError", - "RandomIdDuplicate", - "RandomIdEmpty", - "RandomIdInvalid", - "RandomLengthInvalid", - "RangesInvalid", - "ReactionEmpty", - "ReactionInvalid", - "ReflectorNotAvailable", - "RegIdGenerateFailed", - "ReplyMarkupBuyEmpty", - "ReplyMarkupGameEmpty", - "ReplyMarkupInvalid", - "ReplyMarkupTooLong", - "ResultIdDuplicate", - "ResultIdEmpty", - "ResultIdInvalid", - "ResultTypeInvalid", - "ResultsTooMuch", - "RevoteNotAllowed", - "RightForbidden", - "RpcCallFail", - "RpcConnectFailed", - "RpcMcgetFail", - "RsaDecryptFailed", - "ScheduleBotNotAllowed", - "ScheduleDateInvalid", - "ScheduleDateTooLate", - "ScheduleStatusPrivate", - "ScheduleTooMuch", - "SearchQueryEmpty", - "SecondsInvalid", - "SeeOther", - "SendMessageMediaInvalid", - "SendMessageTypeInvalid", - "SensitiveChangeForbidden", - "ServiceUnavailable", - "SessionExpired", - "SessionPasswordNeeded", - "SessionRevoked", - "SessionTooFresh", - "SettingsInvalid", - "Sha256HashInvalid", - "ShortnameOccupyFailed", - "SignInFailed", - "SlowmodeMultiMsgsDisabled", - "SlowmodeWait", - "SmsCodeCreateFailed", - "SrpIdInvalid", - "SrpPasswordChanged", - "StartParamEmpty", - "StartParamInvalid", - "StartParamTooLong", - "StatsMigrate", - "StickerDocumentInvalid", - "StickerEmojiInvalid", - "StickerFileInvalid", - "StickerIdInvalid", - "StickerInvalid", - "StickerPngDimensions", - "StickerPngNopng", - "StickerTgsNotgs", - "StickerThumbPngNopng", - "StickerVideoNowebm", - "StickersEmpty", - "StickersTooMuch", - "StickersetInvalid", - "StickersetNotModified", - "StickersetOwnerAnonymous", - "StorageCheckFailed", - "StoreInvalidScalarType", - "StoriesTooMuch", - "TakeoutInitDelay", - "TakeoutInvalid", - "TakeoutRequired", - "TempAuthKeyEmpty", - "ThemeFileInvalid", - "ThemeFormatInvalid", - "ThemeInvalid", - "ThemeMimeInvalid", - "Timeout", - "TmpPasswordDisabled", - "TmpPasswordInvalid", - "TokenInvalid", - "TopicClosed", - "TopicDeleted", - "TranscriptionFailed", - "TtlDaysInvalid", - "TtlMediaInvalid", - "TtlPeriodInvalid", - "TwoFaConfirmWait", - "TypeConstructorInvalid", - "TypesEmpty", - "Unauthorized", + "SecurityCheckMismatch", + "SecurityError", "UnknownError", - "UnknownMethod", - "UntilDateInvalid", - "UploadNoVolume", - "UrlInvalid", - "UsageLimitInvalid", - "UserAdminInvalid", - "UserAlreadyParticipant", - "UserBannedInChannel", - "UserBlocked", - "UserBot", - "UserBotInvalid", - "UserBotRequired", - "UserChannelsTooMuch", - "UserCreator", - "UserDeactivated", - "UserDeactivatedBan", - "UserIdInvalid", - "UserInvalid", - "UserIsBlocked", - "UserIsBot", - "UserKicked", - "UserMigrate", - "UserNotMutualContact", - "UserNotParticipant", - "UserPrivacyRestricted", - "UserRestricted", - "UsernameInvalid", - "UsernameNotModified", - "UsernameNotOccupied", - "UsernameOccupied", - "UserpicUploadRequired", - "UsersTooFew", - "UsersTooMuch", - "VideoContentTypeInvalid", - "VideoFileInvalid", - "VoiceMessagesForbidden", - "VolumeLocNotFound", - "WallpaperFileInvalid", - "WallpaperInvalid", - "WallpaperMimeInvalid", - "WcConvertUrlInvalid", - "WebdocumentInvalid", - "WebdocumentMimeInvalid", - "WebdocumentSizeTooBig", - "WebdocumentUrlEmpty", - "WebdocumentUrlInvalid", - "WebpageCurlFailed", - "WebpageMediaEmpty", - "WorkerBusyTooLongRetry", - "WpIdGenerateFailed", - "YouBlockedUser", - "all", - "bad_request_400", - "flood_420", - "forbidden_403", - "internal_server_error_500", - "not_acceptable_406", - "see_other_303", - "service_unavailable_503", - "unauthorized_401", ] diff --git a/hydrogram/file_id.py b/hydrogram/file_id.py index f00c1700a..4335b835b 100644 --- a/hydrogram/file_id.py +++ b/hydrogram/file_id.py @@ -66,18 +66,17 @@ def rle_encode(s: bytes) -> bytes: Returns: ``bytes``: The encoded bytes """ - r: list[int] = [] - n: int = 0 + r = bytearray() + n = 0 for b in s: - if not b: - n += 1 - else: + if b: if n: r.extend((0, n)) n = 0 - r.append(b) + else: + n += 1 if n: r.extend((0, n)) @@ -95,7 +94,7 @@ def rle_decode(s: bytes) -> bytes: Returns: ``bytes``: The decoded bytes """ - r: list[int] = [] + r = bytearray() z: bool = False for b in s: @@ -104,7 +103,7 @@ def rle_decode(s: bytes) -> bytes: continue if z: - r.extend((0,) * b) + r.extend([0] * b) z = False else: r.append(b) @@ -155,8 +154,9 @@ class ThumbnailSource(IntEnum): } DOCUMENT_TYPES = set(FileType) - PHOTO_TYPES -# Since the file type values are small enough to fit them in few bits, Telegram thought it would be a good idea to -# encode extra information about web url and file reference existence as flag inside the 4 bytes allocated for the field +# Since the file type values are small enough to fit them in few bits, Telegram thought it would +# be a good idea to encode extra information about web url and file reference existence as flag +# inside the 4 bytes allocated for the field WEB_LOCATION_FLAG = 1 << 24 FILE_REFERENCE_FLAG = 1 << 25 @@ -165,6 +165,27 @@ class FileId: MAJOR = 4 MINOR = 30 + __slots__ = ( + "access_hash", + "chat_access_hash", + "chat_id", + "dc_id", + "file_reference", + "file_type", + "local_id", + "major", + "media_id", + "minor", + "secret", + "sticker_set_access_hash", + "sticker_set_id", + "thumbnail_file_type", + "thumbnail_size", + "thumbnail_source", + "url", + "volume_id", + ) + def __init__( self, *, @@ -177,8 +198,8 @@ def __init__( media_id: int | None = None, access_hash: int | None = None, volume_id: int | None = None, - thumbnail_source: ThumbnailSource = None, - thumbnail_file_type: FileType = None, + thumbnail_source: ThumbnailSource | None = None, + thumbnail_file_type: FileType | None = None, thumbnail_size: str = "", secret: int | None = None, local_id: int | None = None, @@ -230,8 +251,7 @@ def decode(file_id: str): has_file_reference = bool(file_type & FILE_REFERENCE_FLAG) # Remove flags to restore the actual type id value - file_type &= ~WEB_LOCATION_FLAG - file_type &= ~FILE_REFERENCE_FLAG + file_type &= ~(WEB_LOCATION_FLAG | FILE_REFERENCE_FLAG) # endregion try: @@ -422,7 +442,22 @@ def encode(self, *, major: int | None = None, minor: int | None = None): return b64_encode(rle_encode(buffer.getvalue())) def __str__(self): - return str({k: v for k, v in self.__dict__.items() if v is not None}) + order = [ + "major", + "minor", + "file_type", + "dc_id", + "file_reference", + "media_id", + "access_hash", + "thumbnail_size", + ] + result = {} + for key in order: + value = getattr(self, key) + if value is not None: + result[key] = value + return str(result) class FileUniqueType(IntEnum): @@ -437,6 +472,8 @@ class FileUniqueType(IntEnum): class FileUniqueId: + __slots__ = ("file_unique_type", "local_id", "media_id", "url", "volume_id") + def __init__( self, *, @@ -485,12 +522,14 @@ def decode(file_unique_id: str): # TODO: Missing decoder for SECURE, ENCRYPTED and TEMP raise ValueError( - f"Unknown decoder for file_unique_type {file_unique_type} of file_unique_id {file_unique_id}" + f"Unknown decoder for file_unique_type {file_unique_type} " + f"of file_unique_id {file_unique_id}" ) def encode(self): if self.file_unique_type == FileUniqueType.WEB: - string = struct.pack(" Awaitable[bool]: raise NotImplementedError - def __invert__(self): + def __invert__(self) -> InvertFilter: return InvertFilter(self) - def __and__(self, other): + def __and__(self, other: Filter) -> AndFilter: return AndFilter(self, other) - def __or__(self, other): + def __or__(self, other: Filter) -> OrFilter: return OrFilter(self, other) class InvertFilter(Filter): - def __init__(self, base): + def __init__(self, base: Filter) -> None: self.base = base - async def __call__(self, client: hydrogram.Client, update: Update): + async def __call__(self, client: hydrogram.Client, update: Update) -> bool: if inspect.iscoroutinefunction(self.base.__call__): x = await self.base(client, update) else: @@ -64,17 +72,16 @@ async def __call__(self, client: hydrogram.Client, update: Update): class AndFilter(Filter): - def __init__(self, base, other): + def __init__(self, base: Filter, other: Filter) -> None: self.base = base self.other = other - async def __call__(self, client: hydrogram.Client, update: Update): + async def __call__(self, client: hydrogram.Client, update: Update) -> bool: if inspect.iscoroutinefunction(self.base.__call__): x = await self.base(client, update) else: x = await client.loop.run_in_executor(client.executor, self.base, client, update) - # short circuit if not x: return False @@ -83,21 +90,20 @@ async def __call__(self, client: hydrogram.Client, update: Update): else: y = await client.loop.run_in_executor(client.executor, self.other, client, update) - return x and y + return bool(x) and bool(y) class OrFilter(Filter): - def __init__(self, base, other): + def __init__(self, base: Filter, other: Filter) -> None: self.base = base self.other = other - async def __call__(self, client: hydrogram.Client, update: Update): + async def __call__(self, client: hydrogram.Client, update: Update) -> bool: if inspect.iscoroutinefunction(self.base.__call__): x = await self.base(client, update) else: x = await client.loop.run_in_executor(client.executor, self.base, client, update) - # short circuit if x: return True @@ -106,44 +112,66 @@ async def __call__(self, client: hydrogram.Client, update: Update): else: y = await client.loop.run_in_executor(client.executor, self.other, client, update) - return x or y - + return bool(x) or bool(y) -CUSTOM_FILTER_NAME = "CustomFilter" - -def create(func: Callable, name: str | None = None, **kwargs) -> Filter: +def create( + func: Callable[..., bool | Awaitable[bool]], + name: str | None = None, + **kwargs: Any, +) -> Filter: """Easily create a custom filter. - Custom filters give you extra control over which updates are allowed or not to be processed by your handlers. + Custom filters give you extra control over which updates are allowed or not to be processed + by your handlers. Parameters: func (``Callable``): - A function that accepts three positional arguments *(filter, client, update)* and returns a boolean: True if the - update should be handled, False otherwise. - The *filter* argument refers to the filter itself and can be used to access keyword arguments (read below). - The *client* argument refers to the :obj:`~hydrogram.Client` that received the update. - The *update* argument type will vary depending on which `Handler `_ is coming from. - For example, in a :obj:`~hydrogram.handlers.MessageHandler` the *update* argument will be a :obj:`~hydrogram.types.Message`; in a :obj:`~hydrogram.handlers.CallbackQueryHandler` the *update* will be a :obj:`~hydrogram.types.CallbackQuery`. - Your function body can then access the incoming update attributes and decide whether to allow it or not. + A function that accepts three positional arguments *(filter, client, update)* and + returns a boolean: True if the update should be handled, False otherwise. + The *filter* argument refers to the filter itself and can be used to access + keyword arguments (read below). The *client* argument refers to the + :obj:`~hydrogram.Client` that received the update. The *update* argument type + will vary depending on which `Handler `_ is coming from. For example, in + a :obj:`~hydrogram.handlers.MessageHandler` the *update* argument will be a + :obj:`~hydrogram.types.Message`; in a :obj:`~hydrogram.handlers.CallbackQueryHandler` + the *update* will be a :obj:`~hydrogram.types.CallbackQuery`. Your function body + can then access the incoming update attributes and decide whether to allow it or not. name (``str``, *optional*): Your filter's name. Can be anything you like. Defaults to "CustomFilter". **kwargs (``any``, *optional*): - Any keyword argument you would like to pass. Useful when creating parameterized custom filters, such as - :meth:`~hydrogram.filters.command` or :meth:`~hydrogram.filters.regex`. + Any keyword argument you would like to pass. Useful when creating parameterized + custom filters, such as :meth:`~hydrogram.filters.command` or + :meth:`~hydrogram.filters.regex`. """ return type( - name or func.__name__ or CUSTOM_FILTER_NAME, + name or func.__name__ or "CustomFilter", (Filter,), {"__call__": func, **kwargs}, )() -# region all_filter -def all_filter(_, __, ___): +def _attribute_filter(attribute: str) -> Callable[[Filter, hydrogram.Client, Message], bool]: + def func(_: Filter, __: hydrogram.Client, m: Message) -> bool: + return bool(getattr(m, attribute, None)) + + return func + + +def _chat_type_filter(chat_types: set[enums.ChatType], m: CallbackQuery | Message) -> bool: + if isinstance(m, Message): + value = m.chat + elif isinstance(m, CallbackQuery): + value = m.message.chat if m.message else None + else: + raise ValueError(f"Chat type filter doesn't work with {type(m)}") + return bool(value and value.type in chat_types) + + +def all_filter(_: Filter, __: hydrogram.Client, ___: Update) -> bool: return True @@ -151,11 +179,7 @@ def all_filter(_, __, ___): """Filter all messages.""" -# endregion - - -# region me_filter -def me_filter(_, __, m: Message): +def me_filter(_: Filter, __: hydrogram.Client, m: Message) -> bool: return bool(m.from_user.is_self if m.from_user else getattr(m, "outgoing", False)) @@ -163,11 +187,7 @@ def me_filter(_, __, m: Message): """Filter messages generated by you yourself.""" -# endregion - - -# region bot_filter -def bot_filter(_, __, m: Message): +def bot_filter(_: Filter, __: hydrogram.Client, m: Message) -> bool: return bool(m.from_user and m.from_user.is_bot) @@ -175,485 +195,158 @@ def bot_filter(_, __, m: Message): """Filter messages coming from bots.""" -# endregion - - -# region incoming_filter -def incoming_filter(_, __, m: Message): +def incoming_filter(_: Filter, __: hydrogram.Client, m: Message) -> bool: return not m.outgoing incoming = create(incoming_filter) -"""Filter incoming messages. Messages sent to your own chat (Saved Messages) are also recognised as incoming.""" - - -# endregion +"""Filter incoming messages. Messages sent to your own chat (Saved Messages) are also +recognised as incoming. +""" -# region outgoing_filter -def outgoing_filter(_, __, m: Message): - return m.outgoing +def outgoing_filter(_: Filter, __: hydrogram.Client, m: Message) -> bool: + return bool(m.outgoing) outgoing = create(outgoing_filter) -"""Filter outgoing messages. Messages sent to your own chat (Saved Messages) are not recognized as outgoing.""" - - -# endregion - - -# region text_filter -def text_filter(_, __, m: Message): - return bool(m.text) +"""Filter outgoing messages. Messages sent to your own chat (Saved Messages) +are not recognized as outgoing. +""" -text = create(text_filter) +text = create(_attribute_filter("text"), "text_filter") """Filter text messages.""" - -# endregion - - -# region reply_filter -def reply_filter(_, __, m: Message): - return bool(m.reply_to_message_id) - - -reply = create(reply_filter) +reply = create(_attribute_filter("reply_to_message_id"), "reply_filter") """Filter messages that are replies to other messages.""" - -# endregion - - -# region forwarded_filter -def forwarded_filter(_, __, m: Message): - return bool(m.forward_date) - - -forwarded = create(forwarded_filter) +forwarded = create(_attribute_filter("forward_date"), "forwarded_filter") """Filter messages that are forwarded.""" - -# endregion - - -# region caption_filter -def caption_filter(_, __, m: Message): - return bool(m.caption) - - -caption = create(caption_filter) +caption = create(_attribute_filter("caption"), "caption_filter") """Filter media messages that contain captions.""" - -# endregion - - -# region audio_filter -def audio_filter(_, __, m: Message): - return bool(m.audio) - - -audio = create(audio_filter) +audio = create(_attribute_filter("audio"), "audio_filter") """Filter messages that contain :obj:`~hydrogram.types.Audio` objects.""" - -# endregion - - -# region document_filter -def document_filter(_, __, m: Message): - return bool(m.document) - - -document = create(document_filter) +document = create(_attribute_filter("document"), "document_filter") """Filter messages that contain :obj:`~hydrogram.types.Document` objects.""" - -# endregion - - -# region photo_filter -def photo_filter(_, __, m: Message): - return bool(m.photo) - - -photo = create(photo_filter) +photo = create(_attribute_filter("photo"), "photo_filter") """Filter messages that contain :obj:`~hydrogram.types.Photo` objects.""" - -# endregion - - -# region sticker_filter -def sticker_filter(_, __, m: Message): - return bool(m.sticker) - - -sticker = create(sticker_filter) +sticker = create(_attribute_filter("sticker"), "sticker_filter") """Filter messages that contain :obj:`~hydrogram.types.Sticker` objects.""" - -# endregion - - -# region animation_filter -def animation_filter(_, __, m: Message): - return bool(m.animation) - - -animation = create(animation_filter) +animation = create(_attribute_filter("animation"), "animation_filter") """Filter messages that contain :obj:`~hydrogram.types.Animation` objects.""" - -# endregion - - -# region game_filter -def game_filter(_, __, m: Message): - return bool(m.game) - - -game = create(game_filter) +game = create(_attribute_filter("game"), "game_filter") """Filter messages that contain :obj:`~hydrogram.types.Game` objects.""" - -# endregion - - -# region video_filter -def video_filter(_, __, m: Message): - return bool(m.video) - - -video = create(video_filter) +video = create(_attribute_filter("video"), "video_filter") """Filter messages that contain :obj:`~hydrogram.types.Video` objects.""" - -# endregion - - -# region media_group_filter -def media_group_filter(_, __, m: Message): - return bool(m.media_group_id) - - -media_group = create(media_group_filter) +media_group = create(_attribute_filter("media_group_id"), "media_group_filter") """Filter messages containing photos or videos being part of an album.""" - -# endregion - - -# region voice_filter -def voice_filter(_, __, m: Message): - return bool(m.voice) - - -voice = create(voice_filter) +voice = create(_attribute_filter("voice"), "voice_filter") """Filter messages that contain :obj:`~hydrogram.types.Voice` note objects.""" - -# endregion - - -# region video_note_filter -def video_note_filter(_, __, m: Message): - return bool(m.video_note) - - -video_note = create(video_note_filter) +video_note = create(_attribute_filter("video_note"), "video_note_filter") """Filter messages that contain :obj:`~hydrogram.types.VideoNote` objects.""" - -# endregion - - -# region contact_filter -def contact_filter(_, __, m: Message): - return bool(m.contact) - - -contact = create(contact_filter) +contact = create(_attribute_filter("contact"), "contact_filter") """Filter messages that contain :obj:`~hydrogram.types.Contact` objects.""" - -# endregion - - -# region location_filter -def location_filter(_, __, m: Message): - return bool(m.location) - - -location = create(location_filter) +location = create(_attribute_filter("location"), "location_filter") """Filter messages that contain :obj:`~hydrogram.types.Location` objects.""" - -# endregion - - -# region venue_filter -def venue_filter(_, __, m: Message): - return bool(m.venue) - - -venue = create(venue_filter) +venue = create(_attribute_filter("venue"), "venue_filter") """Filter messages that contain :obj:`~hydrogram.types.Venue` objects.""" - -# endregion - - -# region web_page_filter -def web_page_filter(_, __, m: Message): - return bool(m.web_page) - - -web_page = create(web_page_filter) +web_page = create(_attribute_filter("web_page"), "web_page_filter") """Filter messages sent with a webpage preview.""" - -# endregion - - -# region poll_filter -def poll_filter(_, __, m: Message): - return bool(m.poll) - - -poll = create(poll_filter) +poll = create(_attribute_filter("poll"), "poll_filter") """Filter messages that contain :obj:`~hydrogram.types.Poll` objects.""" - -# endregion - - -# region dice_filter -def dice_filter(_, __, m: Message): - return bool(m.dice) - - -dice = create(dice_filter) +dice = create(_attribute_filter("dice"), "dice_filter") """Filter messages that contain :obj:`~hydrogram.types.Dice` objects.""" - -# endregion - - -# region media_spoiler -def media_spoiler_filter(_, __, m: Message): - return bool(m.has_media_spoiler) - - -media_spoiler = create(media_spoiler_filter) +media_spoiler = create(_attribute_filter("has_media_spoiler"), "media_spoiler_filter") """Filter media messages that contain a spoiler.""" -# endregion - - -# region private_filter -def private_filter(_, __, m: CallbackQuery | Message): - if isinstance(m, Message): - value = m.chat - elif isinstance(m, CallbackQuery): - value = m.message.chat - else: - raise ValueError(f"Private filter doesn't work with {type(m)}") - return bool(value and value.type in {enums.ChatType.PRIVATE, enums.ChatType.BOT}) +def private_filter(_: Filter, __: hydrogram.Client, m: CallbackQuery | Message) -> bool: + return _chat_type_filter({enums.ChatType.PRIVATE, enums.ChatType.BOT}, m) private = create(private_filter) """Filter messages sent in private chats.""" -# endregion - - -# region group_filter -def group_filter(_, __, m: CallbackQuery | Message): - if isinstance(m, Message): - value = m.chat - elif isinstance(m, CallbackQuery): - value = m.message.chat - else: - raise ValueError(f"Group filter doesn't work with {type(m)}") - return bool(value and value.type in {enums.ChatType.GROUP, enums.ChatType.SUPERGROUP}) +def group_filter(_: Filter, __: hydrogram.Client, m: CallbackQuery | Message) -> bool: + return _chat_type_filter({enums.ChatType.GROUP, enums.ChatType.SUPERGROUP}, m) group = create(group_filter) """Filter messages sent in group or supergroup chats.""" -# endregion - - -# region channel_filter -def channel_filter(_, __, m: CallbackQuery | Message): - if isinstance(m, Message): - value = m.chat - elif isinstance(m, CallbackQuery): - value = m.message.chat - else: - raise ValueError(f"Channel filter doesn't work with {type(m)}") - return bool(value and value.type == enums.ChatType.CHANNEL) +def channel_filter(_: Filter, __: hydrogram.Client, m: CallbackQuery | Message) -> bool: + return _chat_type_filter({enums.ChatType.CHANNEL}, m) channel = create(channel_filter) """Filter messages sent in channels.""" -# endregion - - -# region new_chat_members_filter -def new_chat_members_filter(_, __, m: Message): - return bool(m.new_chat_members) - - -new_chat_members = create(new_chat_members_filter) +new_chat_members = create(_attribute_filter("new_chat_members"), "new_chat_members_filter") """Filter service messages for new chat members.""" - -# endregion - - -# region left_chat_member_filter -def left_chat_member_filter(_, __, m: Message): - return bool(m.left_chat_member) - - -left_chat_member = create(left_chat_member_filter) +left_chat_member = create(_attribute_filter("left_chat_member"), "left_chat_member_filter") """Filter service messages for members that left the chat.""" - -# endregion - - -# region new_chat_title_filter -def new_chat_title_filter(_, __, m: Message): - return bool(m.new_chat_title) - - -new_chat_title = create(new_chat_title_filter) +new_chat_title = create(_attribute_filter("new_chat_title"), "new_chat_title_filter") """Filter service messages for new chat titles.""" - -# endregion - - -# region new_chat_photo_filter -def new_chat_photo_filter(_, __, m: Message): - return bool(m.new_chat_photo) - - -new_chat_photo = create(new_chat_photo_filter) +new_chat_photo = create(_attribute_filter("new_chat_photo"), "new_chat_photo_filter") """Filter service messages for new chat photos.""" - -# endregion - - -# region delete_chat_photo_filter -def delete_chat_photo_filter(_, __, m: Message): - return bool(m.delete_chat_photo) - - -delete_chat_photo = create(delete_chat_photo_filter) +delete_chat_photo = create(_attribute_filter("delete_chat_photo"), "delete_chat_photo_filter") """Filter service messages for deleted photos.""" - -# endregion - - -# region group_chat_created_filter -def group_chat_created_filter(_, __, m: Message): - return bool(m.group_chat_created) - - -group_chat_created = create(group_chat_created_filter) +group_chat_created = create(_attribute_filter("group_chat_created"), "group_chat_created_filter") """Filter service messages for group chat creations.""" - -# endregion - - -# region supergroup_chat_created_filter -def supergroup_chat_created_filter(_, __, m: Message): - return bool(m.supergroup_chat_created) - - -supergroup_chat_created = create(supergroup_chat_created_filter) +supergroup_chat_created = create( + _attribute_filter("supergroup_chat_created"), "supergroup_chat_created_filter" +) """Filter service messages for supergroup chat creations.""" - -# endregion - - -# region channel_chat_created_filter -def channel_chat_created_filter(_, __, m: Message): - return bool(m.channel_chat_created) - - -channel_chat_created = create(channel_chat_created_filter) +channel_chat_created = create( + _attribute_filter("channel_chat_created"), "channel_chat_created_filter" +) """Filter service messages for channel chat creations.""" - -# endregion - - -# region migrate_to_chat_id_filter -def migrate_to_chat_id_filter(_, __, m: Message): - return bool(m.migrate_to_chat_id) - - -migrate_to_chat_id = create(migrate_to_chat_id_filter) +migrate_to_chat_id = create(_attribute_filter("migrate_to_chat_id"), "migrate_to_chat_id_filter") """Filter service messages that contain migrate_to_chat_id.""" - -# endregion - - -# region migrate_from_chat_id_filter -def migrate_from_chat_id_filter(_, __, m: Message): - return bool(m.migrate_from_chat_id) - - -migrate_from_chat_id = create(migrate_from_chat_id_filter) +migrate_from_chat_id = create( + _attribute_filter("migrate_from_chat_id"), "migrate_from_chat_id_filter" +) """Filter service messages that contain migrate_from_chat_id.""" - -# endregion - - -# region pinned_message_filter -def pinned_message_filter(_, __, m: Message): - return bool(m.pinned_message) - - -pinned_message = create(pinned_message_filter) +pinned_message = create(_attribute_filter("pinned_message"), "pinned_message_filter") """Filter service messages for pinned messages.""" - -# endregion - - -# region game_high_score_filter -def game_high_score_filter(_, __, m: Message): - return bool(m.game_high_score) - - -game_high_score = create(game_high_score_filter) +game_high_score = create(_attribute_filter("game_high_score"), "game_high_score_filter") """Filter service messages for game high scores.""" -# endregion - - -# region reply_keyboard_filter -def reply_keyboard_filter(_, __, m: Message): +def reply_keyboard_filter(_: Filter, __: hydrogram.Client, m: Message) -> bool: return isinstance(m.reply_markup, ReplyKeyboardMarkup) @@ -661,11 +354,7 @@ def reply_keyboard_filter(_, __, m: Message): """Filter messages containing reply keyboard markups""" -# endregion - - -# region inline_keyboard_filter -def inline_keyboard_filter(_, __, m: Message): +def inline_keyboard_filter(_: Filter, __: hydrogram.Client, m: Message) -> bool: return isinstance(m.reply_markup, InlineKeyboardMarkup) @@ -673,71 +362,25 @@ def inline_keyboard_filter(_, __, m: Message): """Filter messages containing inline keyboard markups""" -# endregion - - -# region mentioned_filter -def mentioned_filter(_, __, m: Message): - return bool(m.mentioned) - - -mentioned = create(mentioned_filter) +mentioned = create(_attribute_filter("mentioned"), "mentioned_filter") """Filter messages containing mentions""" - -# endregion - - -# region via_bot_filter -def via_bot_filter(_, __, m: Message): - return bool(m.via_bot) - - -via_bot = create(via_bot_filter) +via_bot = create(_attribute_filter("via_bot"), "via_bot_filter") """Filter messages sent via inline bots""" - -# endregion - - -# region video_chat_started_filter -def video_chat_started_filter(_, __, m: Message): - return bool(m.video_chat_started) - - -video_chat_started = create(video_chat_started_filter) +video_chat_started = create(_attribute_filter("video_chat_started"), "video_chat_started_filter") """Filter messages for started video chats""" - -# endregion - - -# region video_chat_ended_filter -def video_chat_ended_filter(_, __, m: Message): - return bool(m.video_chat_ended) - - -video_chat_ended = create(video_chat_ended_filter) +video_chat_ended = create(_attribute_filter("video_chat_ended"), "video_chat_ended_filter") """Filter messages for ended video chats""" - -# endregion - - -# region video_chat_members_invited_filter -def video_chat_members_invited_filter(_, __, m: Message): - return bool(m.video_chat_members_invited) - - -video_chat_members_invited = create(video_chat_members_invited_filter) +video_chat_members_invited = create( + _attribute_filter("video_chat_members_invited"), "video_chat_members_invited_filter" +) """Filter messages for voice chat invited members""" -# endregion - - -# region service_filter -def service_filter(_, __, m: Message): +def service_filter(_: Filter, __: hydrogram.Client, m: Message) -> bool: return bool(m.service) @@ -745,57 +388,33 @@ def service_filter(_, __, m: Message): """Filter service messages. A service message contains any of the following fields set: *left_chat_member*, -*new_chat_title*, *new_chat_photo*, *delete_chat_photo*, *group_chat_created*, *supergroup_chat_created*, -*channel_chat_created*, *migrate_to_chat_id*, *migrate_from_chat_id*, *pinned_message*, *game_score*, -*video_chat_started*, *video_chat_ended*, *video_chat_members_invited*. +*new_chat_title*, *new_chat_photo*, *delete_chat_photo*, *group_chat_created*, +*supergroup_chat_created*, *channel_chat_created*, *migrate_to_chat_id*, +*migrate_from_chat_id*, *pinned_message*, *game_score*, *video_chat_started*, +*video_chat_ended*, *video_chat_members_invited*. """ -# endregion - - -# region media_filter -def media_filter(_, __, m: Message): +def media_filter(_: Filter, __: hydrogram.Client, m: Message) -> bool: return bool(m.media) media = create(media_filter) """Filter media messages. -A media message contains any of the following fields set: *audio*, *document*, *photo*, *sticker*, *video*, -*animation*, *voice*, *video_note*, *contact*, *location*, *venue*, *poll*. +A media message contains any of the following fields set: *audio*, *document*, *photo*, +*sticker*, *video*, *animation*, *voice*, *video_note*, *contact*, *location*, *venue*, *poll*. """ -# endregion - - -# region scheduled_filter -def scheduled_filter(_, __, m: Message): - return bool(m.scheduled) - - -scheduled = create(scheduled_filter) +scheduled = create(_attribute_filter("scheduled"), "scheduled_filter") """Filter messages that have been scheduled (not yet sent).""" - -# endregion - - -# region from_scheduled_filter -def from_scheduled_filter(_, __, m: Message): - return bool(m.from_scheduled) - - -from_scheduled = create(from_scheduled_filter) +from_scheduled = create(_attribute_filter("from_scheduled"), "from_scheduled_filter") """Filter new automatically sent messages that were previously scheduled.""" -# endregion - - -# region linked_channel_filter -def linked_channel_filter(_, __, m: Message): +def linked_channel_filter(_: Filter, __: hydrogram.Client, m: Message) -> bool: return bool(m.forward_from_chat and not m.from_user) @@ -803,15 +422,11 @@ def linked_channel_filter(_, __, m: Message): """Filter messages that are automatically forwarded from the linked channel to the group chat.""" -# endregion - - -# region command_filter def command( commands: str | list[str], prefixes: str | list[str] = "/", case_sensitive: bool = False, -): +) -> Filter: """Filter commands, i.e.: text messages starting with "/" or any other custom prefix. Parameters: @@ -832,8 +447,8 @@ def command( """ command_re = re.compile(r"([\"'])(.*?)(? bool: + username = client.me.username or "" # type: ignore text = message.text or message.caption message.command = None @@ -862,10 +477,6 @@ def func(flt, client: hydrogram.Client, message: Message): flags=0 if flt.case_sensitive else re.IGNORECASE, ) - # match.groups are 1-indexed, group(1) is the quote, group(2) is the text - # between the quotes, group(3) is unquoted, whitespace-split text - - # Remove the escape character from the arguments message.command = [cmd] + [ re.sub(r"\\([\"'])", r"\1", m.group(2) or m.group(3) or "") for m in command_re.finditer(without_command) @@ -875,26 +486,27 @@ def func(flt, client: hydrogram.Client, message: Message): return False - commands = commands if isinstance(commands, list) else [commands] - commands = {c if case_sensitive else c.lower() for c in commands} + commands_list = [commands] if isinstance(commands, str) else commands + commands_set = {c if case_sensitive else c.lower() for c in commands_list} - prefixes = [] if prefixes is None else prefixes - prefixes = prefixes if isinstance(prefixes, list) else [prefixes] - prefixes = set(prefixes) if prefixes else {""} + if prefixes is None: + prefixes_list = [] + elif isinstance(prefixes, str): + prefixes_list = [prefixes] + else: + prefixes_list = prefixes + prefixes_set = set(prefixes_list) if prefixes_list else {""} return create( func, "CommandFilter", - commands=commands, - prefixes=prefixes, + commands=commands_set, + prefixes=prefixes_set, case_sensitive=case_sensitive, ) -# endregion - - -def regex(pattern: str | Pattern, flags: int = 0): +def regex(pattern: str | Pattern, flags: int = 0) -> Filter: """Filter updates that match a given regular expression pattern. Can be applied to handlers that receive one of the following updates: @@ -903,7 +515,8 @@ def regex(pattern: str | Pattern, flags: int = 0): - :obj:`~hydrogram.types.CallbackQuery`: The filter will match ``data``. - :obj:`~hydrogram.types.InlineQuery`: The filter will match ``query``. - When a pattern matches, all the `Match Objects `_ are + When a pattern matches, all the + `Match Objects `_ are stored in the ``matches`` field of the update object itself. Parameters: @@ -914,7 +527,7 @@ def regex(pattern: str | Pattern, flags: int = 0): Regex flags. """ - def func(flt, _, update: Update): + def func(flt: Filter, __: hydrogram.Client, update: Update) -> bool: if isinstance(update, Message): value = update.text or update.caption elif isinstance(update, CallbackQuery): @@ -939,8 +552,8 @@ def func(flt, _, update: Update): class user(Filter, set): # noqa: N801 """Filter messages coming from one or more users. - You can use `set bound methods `_ to manipulate the - users container. + You can use `set bound methods `_ + to manipulate the users container. Parameters: users (``int`` | ``str`` | ``list``): @@ -968,8 +581,8 @@ async def __call__(self, _, message: Message): class chat(Filter, set): # noqa: N801 """Filter messages coming from one or more chats. - You can use `set bound methods `_ to manipulate the - chats container. + You can use `set bound methods `_ + to manipulate the chats container. Parameters: chats (``int`` | ``str`` | ``list``): diff --git a/hydrogram/handlers/callback_query_handler.py b/hydrogram/handlers/callback_query_handler.py index b36d01bc1..28cd2124b 100644 --- a/hydrogram/handlers/callback_query_handler.py +++ b/hydrogram/handlers/callback_query_handler.py @@ -20,7 +20,7 @@ from __future__ import annotations from asyncio import iscoroutinefunction -from typing import Callable +from typing import TYPE_CHECKING, Callable import hydrogram from hydrogram.types import CallbackQuery, Identifier, Listener, ListenerTypes @@ -28,6 +28,9 @@ from .handler import Handler +if TYPE_CHECKING: + from hydrogram.filters import Filter + class CallbackQueryHandler(Handler): """The CallbackQuery handler class. Used to handle callback queries coming from inline buttons. @@ -53,7 +56,7 @@ class CallbackQueryHandler(Handler): The received callback query. """ - def __init__(self, callback: Callable, filters=None): + def __init__(self, callback: Callable, filters: Filter | None = None): self.original_callback = callback super().__init__(self.resolve_future_or_callback, filters) diff --git a/hydrogram/handlers/chat_join_request_handler.py b/hydrogram/handlers/chat_join_request_handler.py index acb602b47..0cb16896d 100644 --- a/hydrogram/handlers/chat_join_request_handler.py +++ b/hydrogram/handlers/chat_join_request_handler.py @@ -17,10 +17,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable from .handler import Handler +if TYPE_CHECKING: + from hydrogram.filters import Filter + class ChatJoinRequestHandler(Handler): """The ChatJoinRequest handler class. Used to handle join chat requests. @@ -46,5 +51,5 @@ class ChatJoinRequestHandler(Handler): The received chat join request. """ - def __init__(self, callback: Callable, filters=None): + def __init__(self, callback: Callable, filters: Filter | None = None): super().__init__(callback, filters) diff --git a/hydrogram/handlers/chat_member_updated_handler.py b/hydrogram/handlers/chat_member_updated_handler.py index be7736927..a997868c7 100644 --- a/hydrogram/handlers/chat_member_updated_handler.py +++ b/hydrogram/handlers/chat_member_updated_handler.py @@ -17,10 +17,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable from .handler import Handler +if TYPE_CHECKING: + from hydrogram.filters import Filter + class ChatMemberUpdatedHandler(Handler): """The ChatMemberUpdated handler class. Used to handle changes in the status of a chat member. @@ -46,5 +51,5 @@ class ChatMemberUpdatedHandler(Handler): The received chat member update. """ - def __init__(self, callback: Callable, filters=None): + def __init__(self, callback: Callable, filters: Filter | None = None): super().__init__(callback, filters) diff --git a/hydrogram/handlers/chosen_inline_result_handler.py b/hydrogram/handlers/chosen_inline_result_handler.py index 5bec55c25..44452a073 100644 --- a/hydrogram/handlers/chosen_inline_result_handler.py +++ b/hydrogram/handlers/chosen_inline_result_handler.py @@ -17,10 +17,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable from .handler import Handler +if TYPE_CHECKING: + from hydrogram.filters import Filter + class ChosenInlineResultHandler(Handler): """The ChosenInlineResultHandler handler class. Used to handle chosen inline results coming from inline queries. @@ -47,5 +52,5 @@ class ChosenInlineResultHandler(Handler): The received chosen inline result. """ - def __init__(self, callback: Callable, filters=None): + def __init__(self, callback: Callable, filters: Filter | None = None): super().__init__(callback, filters) diff --git a/hydrogram/handlers/deleted_messages_handler.py b/hydrogram/handlers/deleted_messages_handler.py index 26107f5a6..9ab38905c 100644 --- a/hydrogram/handlers/deleted_messages_handler.py +++ b/hydrogram/handlers/deleted_messages_handler.py @@ -17,15 +17,18 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . +from __future__ import annotations + from typing import TYPE_CHECKING, Callable -from hydrogram.filters import Filter -from hydrogram.types import Message +import hydrogram from .handler import Handler if TYPE_CHECKING: import hydrogram + from hydrogram.filters import Filter + from hydrogram.types import Message class DeletedMessagesHandler(Handler): @@ -52,10 +55,10 @@ class DeletedMessagesHandler(Handler): The deleted messages, as list. """ - def __init__(self, callback: Callable, filters: Filter = None): + def __init__(self, callback: Callable, filters: Filter | None = None): super().__init__(callback, filters) - async def check(self, client: "hydrogram.Client", messages: list[Message]): + async def check(self, client: hydrogram.Client, messages: list[Message]): # Every message should be checked, if at least one matches the filter True is returned # otherwise, or if the list is empty, False is returned for message in messages: diff --git a/hydrogram/handlers/edited_message_handler.py b/hydrogram/handlers/edited_message_handler.py index 8c5b43b8e..5261bed2f 100644 --- a/hydrogram/handlers/edited_message_handler.py +++ b/hydrogram/handlers/edited_message_handler.py @@ -17,10 +17,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable from .handler import Handler +if TYPE_CHECKING: + from hydrogram.filters import Filter + class EditedMessageHandler(Handler): """The EditedMessage handler class. Used to handle edited messages. @@ -46,5 +51,5 @@ class EditedMessageHandler(Handler): The received edited message. """ - def __init__(self, callback: Callable, filters=None): + def __init__(self, callback: Callable, filters: Filter | None = None): super().__init__(callback, filters) diff --git a/hydrogram/handlers/error_handler.py b/hydrogram/handlers/error_handler.py index 026ecbafc..ea8039abc 100644 --- a/hydrogram/handlers/error_handler.py +++ b/hydrogram/handlers/error_handler.py @@ -25,6 +25,7 @@ if TYPE_CHECKING: import hydrogram + from hydrogram.types import Update class ErrorHandler(Handler): @@ -39,7 +40,7 @@ class ErrorHandler(Handler): Pass a function that will be called when a new Error arrives. It takes *(client, error)* as positional arguments (look at the section below for a detailed description). - errors (``Exception`` | Iterable of ``Exception``, *optional*): + exceptions (``Exception`` | Iterable of ``Exception``, *optional*): Pass one or more exception classes to allow only a subset of errors to be passed in your callback function. @@ -47,26 +48,32 @@ class ErrorHandler(Handler): client (:obj:`~hydrogram.Client`): The Client itself, useful when you want to call other API methods inside the error handler. - error (``Exception``): - The error that was raised. - update (:obj:`~hydrogram.Update`): The update that caused the error. + + error (``Exception``): + The error that was raised. """ def __init__( - self, callback: Callable, errors: type[Exception] | Iterable[type[Exception]] | None = None + self, + callback: Callable, + exceptions: type[Exception] | Iterable[type[Exception]] | None = None, ): - if errors is None: - errors = [Exception] - elif not isinstance(errors, Iterable): - errors = [errors] - - self.errors = errors + self.exceptions = ( + tuple(exceptions) + if isinstance(exceptions, Iterable) + else (exceptions,) + if exceptions + else (Exception,) + ) super().__init__(callback) - async def check(self, client: hydrogram.Client, error: Exception): - return any(isinstance(error, e) for e in self.errors) + async def check(self, client: hydrogram.Client, update: Update, exception: Exception) -> bool: + if isinstance(exception, self.exceptions): + await self.callback(client, update, exception) + return True + return False - def check_remove(self, error: Exception): - return self.errors == error or any(isinstance(error, e) for e in self.errors) + def check_remove(self, error: type[Exception] | Iterable[type[Exception]]) -> bool: + return isinstance(error, self.exceptions) diff --git a/hydrogram/handlers/handler.py b/hydrogram/handlers/handler.py index cf8e57092..6c565a02e 100644 --- a/hydrogram/handlers/handler.py +++ b/hydrogram/handlers/handler.py @@ -17,22 +17,23 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . +from __future__ import annotations + import inspect from typing import TYPE_CHECKING, Callable -from hydrogram.filters import Filter -from hydrogram.types import Update - if TYPE_CHECKING: import hydrogram + from hydrogram.filters import Filter + from hydrogram.types import Update class Handler: - def __init__(self, callback: Callable, filters: Filter = None): + def __init__(self, callback: Callable, filters: Filter | None = None): self.callback = callback self.filters = filters - async def check(self, client: "hydrogram.Client", update: Update): + async def check(self, client: hydrogram.Client, update: Update): if callable(self.filters): if inspect.iscoroutinefunction(self.filters.__call__): return await self.filters(client, update) diff --git a/hydrogram/handlers/inline_query_handler.py b/hydrogram/handlers/inline_query_handler.py index 3d54d1797..0af090509 100644 --- a/hydrogram/handlers/inline_query_handler.py +++ b/hydrogram/handlers/inline_query_handler.py @@ -17,10 +17,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable from .handler import Handler +if TYPE_CHECKING: + from hydrogram.filters import Filter + class InlineQueryHandler(Handler): """The InlineQuery handler class. Used to handle inline queries. @@ -46,5 +51,5 @@ class InlineQueryHandler(Handler): The received inline query. """ - def __init__(self, callback: Callable, filters=None): + def __init__(self, callback: Callable, filters: Filter | None = None): super().__init__(callback, filters) diff --git a/hydrogram/handlers/message_handler.py b/hydrogram/handlers/message_handler.py index fbcea445d..7860ab895 100644 --- a/hydrogram/handlers/message_handler.py +++ b/hydrogram/handlers/message_handler.py @@ -20,13 +20,16 @@ from __future__ import annotations from inspect import iscoroutinefunction -from typing import Callable +from typing import TYPE_CHECKING, Callable import hydrogram from hydrogram.types import Identifier, Listener, ListenerTypes, Message from .handler import Handler +if TYPE_CHECKING: + from hydrogram.filters import Filter + class MessageHandler(Handler): """The Message handler class. Used to handle new messages. @@ -52,7 +55,7 @@ class MessageHandler(Handler): The received message. """ - def __init__(self, callback: Callable, filters=None): + def __init__(self, callback: Callable, filters: Filter | None = None): self.original_callback = callback super().__init__(self.resolve_future_or_callback, filters) diff --git a/hydrogram/handlers/poll_handler.py b/hydrogram/handlers/poll_handler.py index e6e33b72f..f1da5c245 100644 --- a/hydrogram/handlers/poll_handler.py +++ b/hydrogram/handlers/poll_handler.py @@ -17,10 +17,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable from .handler import Handler +if TYPE_CHECKING: + from hydrogram.filters import Filter + class PollHandler(Handler): """The Poll handler class. Used to handle polls updates. @@ -47,5 +52,5 @@ class PollHandler(Handler): The received poll. """ - def __init__(self, callback: Callable, filters=None): + def __init__(self, callback: Callable, filters: Filter | None = None): super().__init__(callback, filters) diff --git a/hydrogram/handlers/user_status_handler.py b/hydrogram/handlers/user_status_handler.py index 8e275c664..6ea6cdb3e 100644 --- a/hydrogram/handlers/user_status_handler.py +++ b/hydrogram/handlers/user_status_handler.py @@ -17,10 +17,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable from .handler import Handler +if TYPE_CHECKING: + from hydrogram.filters import Filter + class UserStatusHandler(Handler): """The UserStatus handler class. Used to handle user status updates (user going online or offline). @@ -44,5 +49,5 @@ class UserStatusHandler(Handler): The user containing the updated status. """ - def __init__(self, callback: Callable, filters=None): + def __init__(self, callback: Callable, filters: Filter | None = None): super().__init__(callback, filters) diff --git a/hydrogram/methods/__init__.py b/hydrogram/methods/__init__.py index d77eade20..913af423e 100644 --- a/hydrogram/methods/__init__.py +++ b/hydrogram/methods/__init__.py @@ -26,6 +26,7 @@ from .invite_links import InviteLinks from .messages import Messages from .password import Password +from .phone import Phone from .pyromod import Pyromod from .users import Users from .utilities import Utilities @@ -44,5 +45,6 @@ class Methods( Decorators, Utilities, InviteLinks, + Phone, ): pass diff --git a/hydrogram/methods/advanced/save_file.py b/hydrogram/methods/advanced/save_file.py index 599bf54cd..fc0da1a13 100644 --- a/hydrogram/methods/advanced/save_file.py +++ b/hydrogram/methods/advanced/save_file.py @@ -137,7 +137,7 @@ async def worker(session): if file_size > file_size_limit_mib * 1024 * 1024: raise ValueError(f"Can't upload files bigger than {file_size_limit_mib} MiB") - file_total_parts = int(math.ceil(file_size / part_size)) + file_total_parts = math.ceil(file_size / part_size) is_big = file_size > 10 * 1024 * 1024 workers_count = 4 if is_big else 1 is_missing_part = file_id is not None diff --git a/hydrogram/methods/auth/__init__.py b/hydrogram/methods/auth/__init__.py index aee2cf388..60e0d8281 100644 --- a/hydrogram/methods/auth/__init__.py +++ b/hydrogram/methods/auth/__init__.py @@ -22,6 +22,7 @@ from .connect import Connect from .disconnect import Disconnect from .get_password_hint import GetPasswordHint +from .get_sessions import GetSessions from .initialize import Initialize from .log_out import LogOut from .recover_password import RecoverPassword @@ -50,5 +51,6 @@ class Auth( SignInBot, SignUp, Terminate, + GetSessions, ): pass diff --git a/hydrogram/methods/auth/get_sessions.py b/hydrogram/methods/auth/get_sessions.py new file mode 100644 index 000000000..51ce67c0b --- /dev/null +++ b/hydrogram/methods/auth/get_sessions.py @@ -0,0 +1,38 @@ +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + +import logging + +import hydrogram +from hydrogram import raw, types + +log = logging.getLogger(__name__) + + +class GetSessions: + async def get_sessions( + self: "hydrogram.Client", + ) -> list["types.Session"]: + """Get your info data by other sessions . + + Returns: + List[:obj:`~hydrogram.types.Session`]: List of active sessions. + """ + + authorizations = await self.invoke(raw.functions.account.GetAuthorizations()) + return [types.Session._parse(auth) for auth in authorizations.authorizations] diff --git a/hydrogram/methods/bots/answer_inline_query.py b/hydrogram/methods/bots/answer_inline_query.py index 69e6886e5..c82bde644 100644 --- a/hydrogram/methods/bots/answer_inline_query.py +++ b/hydrogram/methods/bots/answer_inline_query.py @@ -91,7 +91,9 @@ async def answer_inline_query( await app.answer_inline_query( inline_query_id, results=[ - InlineQueryResultArticle("Title", InputTextMessageContent("Message content")) + InlineQueryResultArticle( + "Title", InputTextMessageContent("Message content") + ) ], ) """ diff --git a/hydrogram/methods/chats/__init__.py b/hydrogram/methods/chats/__init__.py index 4f56d35c9..182c0b904 100644 --- a/hydrogram/methods/chats/__init__.py +++ b/hydrogram/methods/chats/__init__.py @@ -63,6 +63,7 @@ from .set_chat_username import SetChatUsername from .set_send_as_chat import SetSendAsChat from .set_slow_mode import SetSlowMode +from .transfer_chat_ownership import TransferChatOwnership from .unarchive_chats import UnarchiveChats from .unban_chat_member import UnbanChatMember from .unhide_general_topic import UnhideGeneralTopic @@ -122,5 +123,6 @@ class Chats( GetSendAsChats, SetSendAsChat, SetChatProtectedContent, + TransferChatOwnership, ): pass diff --git a/hydrogram/methods/chats/restrict_chat_member.py b/hydrogram/methods/chats/restrict_chat_member.py index 1630ab10b..ee494ec0b 100644 --- a/hydrogram/methods/chats/restrict_chat_member.py +++ b/hydrogram/methods/chats/restrict_chat_member.py @@ -77,7 +77,9 @@ async def restrict_chat_member( ) # Chat member can only send text messages - await app.restrict_chat_member(chat_id, user_id, ChatPermissions(can_send_messages=True)) + await app.restrict_chat_member( + chat_id, user_id, ChatPermissions(can_send_messages=True) + ) """ r = await self.invoke( raw.functions.channels.EditBanned( diff --git a/hydrogram/methods/chats/transfer_chat_ownership.py b/hydrogram/methods/chats/transfer_chat_ownership.py new file mode 100644 index 000000000..670fabce1 --- /dev/null +++ b/hydrogram/methods/chats/transfer_chat_ownership.py @@ -0,0 +1,75 @@ +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + +from __future__ import annotations + +import hydrogram +from hydrogram import raw +from hydrogram.utils import compute_password_check + + +class TransferChatOwnership: + async def transfer_chat_ownership( + self: hydrogram.Client, + chat_id: int | str, + user_id: int | str, + password: str, + ) -> bool: + """Set new chat owner. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the new owner. The ownership can't be transferred to a bot or to a deleted user. + + password (``str``): + The 2-step verification password of the current user. + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + await app.transfer_chat_ownership(chat_id, user_id, "password") + """ + + peer_channel = await self.resolve_peer(chat_id) + peer_user = await self.resolve_peer(user_id) + + if not isinstance(peer_channel, raw.types.InputPeerChannel): + raise ValueError("The chat_id must belong to a channel/supergroup.") + + if not isinstance(peer_user, raw.types.InputPeerUser): + raise ValueError("The user_id must belong to a user.") + + r = await self.invoke( + raw.functions.channels.EditCreator( + channel=peer_channel, + user_id=peer_user, + password=compute_password_check( + await self.invoke(raw.functions.account.GetPassword()), password + ), + ) + ) + + return bool(r) diff --git a/hydrogram/methods/decorators/on_callback_query.py b/hydrogram/methods/decorators/on_callback_query.py index b27caf2ed..76bf29768 100644 --- a/hydrogram/methods/decorators/on_callback_query.py +++ b/hydrogram/methods/decorators/on_callback_query.py @@ -17,14 +17,22 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram from hydrogram.filters import Filter +F = TypeVar("F", bound=Callable) + class OnCallbackQuery: - def on_callback_query(self=None, filters=None, group: int = 0) -> Callable: + def on_callback_query( + self: hydrogram.Client | Filter | None = None, # type: ignore + filters: Filter | None = None, + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling callback queries. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the @@ -39,7 +47,7 @@ def on_callback_query(self=None, filters=None, group: int = 0) -> Callable: The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.CallbackQueryHandler(func, filters), group) elif isinstance(self, Filter) or self is None: diff --git a/hydrogram/methods/decorators/on_chat_join_request.py b/hydrogram/methods/decorators/on_chat_join_request.py index 3bdac34c5..98e9b78c9 100644 --- a/hydrogram/methods/decorators/on_chat_join_request.py +++ b/hydrogram/methods/decorators/on_chat_join_request.py @@ -17,14 +17,22 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram from hydrogram.filters import Filter +F = TypeVar("F", bound=Callable) + class OnChatJoinRequest: - def on_chat_join_request(self=None, filters=None, group: int = 0) -> Callable: + def on_chat_join_request( + self: hydrogram.Client | Filter | None = None, # type: ignore + filters: Filter | None = None, + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling chat join requests. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the @@ -32,13 +40,14 @@ def on_chat_join_request(self=None, filters=None, group: int = 0) -> Callable: Parameters: filters (:obj:`~hydrogram.filters`, *optional*): - Pass one or more filters to allow only a subset of updates to be passed in your function. + Pass one or more filters to allow only a subset of chat join requests to be passed + in your function. group (``int``, *optional*): The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.ChatJoinRequestHandler(func, filters), group) elif isinstance(self, Filter) or self is None: diff --git a/hydrogram/methods/decorators/on_chat_member_updated.py b/hydrogram/methods/decorators/on_chat_member_updated.py index 15e1013fa..08c78ae23 100644 --- a/hydrogram/methods/decorators/on_chat_member_updated.py +++ b/hydrogram/methods/decorators/on_chat_member_updated.py @@ -17,14 +17,22 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram from hydrogram.filters import Filter +F = TypeVar("F", bound=Callable) + class OnChatMemberUpdated: - def on_chat_member_updated(self=None, filters=None, group: int = 0) -> Callable: + def on_chat_member_updated( + self: hydrogram.Client | Filter | None = None, # type: ignore + filters: Filter | None = None, + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling event changes on chat members. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the @@ -38,7 +46,7 @@ def on_chat_member_updated(self=None, filters=None, group: int = 0) -> Callable: The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.ChatMemberUpdatedHandler(func, filters), group) elif isinstance(self, Filter) or self is None: diff --git a/hydrogram/methods/decorators/on_chosen_inline_result.py b/hydrogram/methods/decorators/on_chosen_inline_result.py index c85ccdd1a..d261be846 100644 --- a/hydrogram/methods/decorators/on_chosen_inline_result.py +++ b/hydrogram/methods/decorators/on_chosen_inline_result.py @@ -17,14 +17,22 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram from hydrogram.filters import Filter +F = TypeVar("F", bound=Callable) + class OnChosenInlineResult: - def on_chosen_inline_result(self=None, filters=None, group: int = 0) -> Callable: + def on_chosen_inline_result( + self: hydrogram.Client | Filter | None = None, # type: ignore + filters: Filter | None = None, + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling chosen inline results. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the @@ -39,7 +47,7 @@ def on_chosen_inline_result(self=None, filters=None, group: int = 0) -> Callable The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler( hydrogram.handlers.ChosenInlineResultHandler(func, filters), group diff --git a/hydrogram/methods/decorators/on_deleted_messages.py b/hydrogram/methods/decorators/on_deleted_messages.py index 20dcc9989..083b4d698 100644 --- a/hydrogram/methods/decorators/on_deleted_messages.py +++ b/hydrogram/methods/decorators/on_deleted_messages.py @@ -17,14 +17,22 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram from hydrogram.filters import Filter +F = TypeVar("F", bound=Callable) + class OnDeletedMessages: - def on_deleted_messages(self=None, filters=None, group: int = 0) -> Callable: + def on_deleted_messages( + self: hydrogram.Client | Filter | None = None, # type: ignore + filters: Filter | None = None, + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling deleted messages. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the @@ -39,7 +47,7 @@ def on_deleted_messages(self=None, filters=None, group: int = 0) -> Callable: The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.DeletedMessagesHandler(func, filters), group) elif isinstance(self, Filter) or self is None: diff --git a/hydrogram/methods/decorators/on_disconnect.py b/hydrogram/methods/decorators/on_disconnect.py index aacdceee8..a169c689c 100644 --- a/hydrogram/methods/decorators/on_disconnect.py +++ b/hydrogram/methods/decorators/on_disconnect.py @@ -17,20 +17,24 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram +F = TypeVar("F", bound=Callable) + class OnDisconnect: - def on_disconnect(self=None) -> Callable: + def on_disconnect(self: hydrogram.Client | None | None = None) -> Callable[[F], F]: # type: ignore """Decorator for handling disconnections. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the :obj:`~hydrogram.handlers.DisconnectHandler`. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.DisconnectHandler(func)) else: diff --git a/hydrogram/methods/decorators/on_edited_message.py b/hydrogram/methods/decorators/on_edited_message.py index 608644ff5..a734747db 100644 --- a/hydrogram/methods/decorators/on_edited_message.py +++ b/hydrogram/methods/decorators/on_edited_message.py @@ -17,14 +17,22 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram from hydrogram.filters import Filter +F = TypeVar("F", bound=Callable) + class OnEditedMessage: - def on_edited_message(self=None, filters=None, group: int = 0) -> Callable: + def on_edited_message( + self: hydrogram.Client | Filter | None = None, # type: ignore + filters: Filter | None = None, + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling edited messages. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the @@ -39,7 +47,7 @@ def on_edited_message(self=None, filters=None, group: int = 0) -> Callable: The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.EditedMessageHandler(func, filters), group) elif isinstance(self, Filter) or self is None: diff --git a/hydrogram/methods/decorators/on_error.py b/hydrogram/methods/decorators/on_error.py index eb3c2dfd4..e5ae3d4e1 100644 --- a/hydrogram/methods/decorators/on_error.py +++ b/hydrogram/methods/decorators/on_error.py @@ -16,18 +16,24 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram -from hydrogram.filters import Filter + +F = TypeVar("F", bound=Callable) class OnError: - def on_error(self=None, errors=None) -> Callable: + def on_error( + self: hydrogram.Client | type[Exception] | None = None, # type: ignore + errors: type[Exception] | list[type[Exception]] | None = None, + ) -> Callable[[F], F]: """Decorator for handling new errors. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the - :obj:`~hydrogram.handlers.MessageHandler`. + :obj:`~hydrogram.handlers.ErrorHandler`. Parameters: errors (:obj:`~Exception`, *optional*): @@ -35,10 +41,10 @@ def on_error(self=None, errors=None) -> Callable: in your function. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.ErrorHandler(func, errors), 0) - elif isinstance(self, Filter) or self is None: + elif (isinstance(self, type) and issubclass(self, Exception)) or self is None: if not hasattr(func, "handlers"): func.handlers = [] diff --git a/hydrogram/methods/decorators/on_inline_query.py b/hydrogram/methods/decorators/on_inline_query.py index 4b8685cc1..c3e6f24c8 100644 --- a/hydrogram/methods/decorators/on_inline_query.py +++ b/hydrogram/methods/decorators/on_inline_query.py @@ -17,14 +17,22 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram from hydrogram.filters import Filter +F = TypeVar("F", bound=Callable) + class OnInlineQuery: - def on_inline_query(self=None, filters=None, group: int = 0) -> Callable: + def on_inline_query( + self: hydrogram.Client | Filter | None = None, # type: ignore + filters: Filter | None = None, + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling inline queries. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the @@ -39,7 +47,7 @@ def on_inline_query(self=None, filters=None, group: int = 0) -> Callable: The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.InlineQueryHandler(func, filters), group) elif isinstance(self, Filter) or self is None: diff --git a/hydrogram/methods/decorators/on_message.py b/hydrogram/methods/decorators/on_message.py index 488bb06cd..eb43772ec 100644 --- a/hydrogram/methods/decorators/on_message.py +++ b/hydrogram/methods/decorators/on_message.py @@ -17,14 +17,22 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram from hydrogram.filters import Filter +F = TypeVar("F", bound=Callable) + class OnMessage: - def on_message(self=None, filters=None, group: int = 0) -> Callable: + def on_message( + self: hydrogram.Client | Filter | None = None, # type: ignore + filters: Filter | None = None, + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling new messages. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the @@ -39,7 +47,7 @@ def on_message(self=None, filters=None, group: int = 0) -> Callable: The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.MessageHandler(func, filters), group) elif isinstance(self, Filter) or self is None: diff --git a/hydrogram/methods/decorators/on_poll.py b/hydrogram/methods/decorators/on_poll.py index 438ae2eee..f56af7975 100644 --- a/hydrogram/methods/decorators/on_poll.py +++ b/hydrogram/methods/decorators/on_poll.py @@ -17,14 +17,22 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram from hydrogram.filters import Filter +F = TypeVar("F", bound=Callable) + class OnPoll: - def on_poll(self=None, filters=None, group: int = 0) -> Callable: + def on_poll( + self: hydrogram.Client | Filter | None = None, # type: ignore + filters: Filter | None = None, + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling poll updates. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the @@ -39,7 +47,7 @@ def on_poll(self=None, filters=None, group: int = 0) -> Callable: The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.PollHandler(func, filters), group) elif isinstance(self, Filter) or self is None: diff --git a/hydrogram/methods/decorators/on_raw_update.py b/hydrogram/methods/decorators/on_raw_update.py index 78f329345..787c2c080 100644 --- a/hydrogram/methods/decorators/on_raw_update.py +++ b/hydrogram/methods/decorators/on_raw_update.py @@ -17,13 +17,20 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram +F = TypeVar("F", bound=Callable) + class OnRawUpdate: - def on_raw_update(self=None, group: int = 0) -> Callable: + def on_raw_update( + self: hydrogram.Client | None = None, # type: ignore + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling raw updates. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the @@ -34,7 +41,7 @@ def on_raw_update(self=None, group: int = 0) -> Callable: The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.RawUpdateHandler(func), group) else: diff --git a/hydrogram/methods/decorators/on_user_status.py b/hydrogram/methods/decorators/on_user_status.py index e66881003..d2b96aef3 100644 --- a/hydrogram/methods/decorators/on_user_status.py +++ b/hydrogram/methods/decorators/on_user_status.py @@ -17,14 +17,22 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -from typing import Callable +from __future__ import annotations + +from typing import Callable, TypeVar import hydrogram from hydrogram.filters import Filter +F = TypeVar("F", bound=Callable) + class OnUserStatus: - def on_user_status(self=None, filters=None, group: int = 0) -> Callable: + def on_user_status( + self: hydrogram.Client | Filter | None = None, # type: ignore + filters: Filter | None = None, + group: int = 0, + ) -> Callable[[F], F]: """Decorator for handling user status updates. This does the same thing as :meth:`~hydrogram.Client.add_handler` using the :obj:`~hydrogram.handlers.UserStatusHandler`. @@ -37,7 +45,7 @@ def on_user_status(self=None, filters=None, group: int = 0) -> Callable: The group identifier, defaults to 0. """ - def decorator(func: Callable) -> Callable: + def decorator(func: F) -> F: if isinstance(self, hydrogram.Client): self.add_handler(hydrogram.handlers.UserStatusHandler(func, filters), group) elif isinstance(self, Filter) or self is None: diff --git a/hydrogram/methods/messages/copy_media_group.py b/hydrogram/methods/messages/copy_media_group.py index 6a77bc31e..de721f384 100644 --- a/hydrogram/methods/messages/copy_media_group.py +++ b/hydrogram/methods/messages/copy_media_group.py @@ -93,7 +93,9 @@ async def copy_media_group( await app.copy_media_group(to_chat, from_chat, 123, captions="single caption") - await app.copy_media_group(to_chat, from_chat, 123, captions=["caption 1", None, ""]) + await app.copy_media_group( + to_chat, from_chat, 123, captions=["caption 1", None, ""] + ) """ media_group = await self.get_media_group(from_chat_id, message_id) diff --git a/hydrogram/methods/messages/copy_message.py b/hydrogram/methods/messages/copy_message.py index 187d3bbf1..4e690ee14 100644 --- a/hydrogram/methods/messages/copy_message.py +++ b/hydrogram/methods/messages/copy_message.py @@ -42,6 +42,7 @@ async def copy_message( message_thread_id: int | None = None, parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, disable_notification: bool | None = None, reply_to_message_id: int | None = None, schedule_date: datetime | None = None, @@ -84,6 +85,9 @@ async def copy_message( caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the new caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Pass True if the caption should be shown above the media. + disable_notification (``bool``, *optional*): Sends the message silently. Users will receive a notification with no sound. @@ -122,6 +126,7 @@ async def copy_message( caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, + show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, message_thread_id=message_thread_id, reply_to_message_id=reply_to_message_id, diff --git a/hydrogram/methods/messages/download_media.py b/hydrogram/methods/messages/download_media.py index 25df3c853..e007cb0fc 100644 --- a/hydrogram/methods/messages/download_media.py +++ b/hydrogram/methods/messages/download_media.py @@ -19,7 +19,6 @@ from __future__ import annotations -import asyncio import os from datetime import datetime from pathlib import Path @@ -182,7 +181,7 @@ async def progress(current, total): else: extension = ".unknown" - file_name = f'{FileType(file_id_obj.file_type).name.lower()}_{(date or datetime.now()).strftime("%Y-%m-%d_%H-%M-%S")}_{self.rnd_id()}{extension}' + file_name = f"{FileType(file_id_obj.file_type).name.lower()}_{(date or datetime.now()).strftime('%Y-%m-%d_%H-%M-%S')}_{self.rnd_id()}{extension}" downloader = self.handle_download(( file_id_obj, @@ -196,5 +195,5 @@ async def progress(current, total): if block: return await downloader - asyncio.get_event_loop().create_task(downloader) + self.loop.create_task(downloader) return None diff --git a/hydrogram/methods/messages/edit_inline_media.py b/hydrogram/methods/messages/edit_inline_media.py index 3e24be1c1..2271e6514 100644 --- a/hydrogram/methods/messages/edit_inline_media.py +++ b/hydrogram/methods/messages/edit_inline_media.py @@ -82,7 +82,7 @@ async def edit_inline_media( is_bytes_io = isinstance(media.media, io.BytesIO) is_uploaded_file = is_bytes_io or Path(media.media).is_file() - is_external_url = not is_uploaded_file and re.match("^https?://", media.media) + is_external_url = not is_uploaded_file and re.match(r"^https?://", media.media) if is_bytes_io and not hasattr(media.media, "name"): media.media.name = "media" diff --git a/hydrogram/methods/messages/edit_inline_reply_markup.py b/hydrogram/methods/messages/edit_inline_reply_markup.py index fd2fd93ba..a46549753 100644 --- a/hydrogram/methods/messages/edit_inline_reply_markup.py +++ b/hydrogram/methods/messages/edit_inline_reply_markup.py @@ -51,7 +51,9 @@ async def edit_inline_reply_markup( # Bots only await app.edit_inline_reply_markup( inline_message_id, - InlineKeyboardMarkup([[InlineKeyboardButton("New button", callback_data="new_data")]]), + InlineKeyboardMarkup([ + [InlineKeyboardButton("New button", callback_data="new_data")] + ]), ) """ diff --git a/hydrogram/methods/messages/edit_inline_text.py b/hydrogram/methods/messages/edit_inline_text.py index d39e87890..38c57b33a 100644 --- a/hydrogram/methods/messages/edit_inline_text.py +++ b/hydrogram/methods/messages/edit_inline_text.py @@ -67,7 +67,9 @@ async def edit_inline_text( await app.edit_inline_text(inline_message_id, "new text") # Take the same text message, remove the web page preview only - await app.edit_inline_text(inline_message_id, message.text, disable_web_page_preview=True) + await app.edit_inline_text( + inline_message_id, message.text, disable_web_page_preview=True + ) """ unpacked = utils.unpack_inline_message_id(inline_message_id) diff --git a/hydrogram/methods/messages/edit_message_media.py b/hydrogram/methods/messages/edit_message_media.py index 3b83baf50..9273dd1f7 100644 --- a/hydrogram/methods/messages/edit_message_media.py +++ b/hydrogram/methods/messages/edit_message_media.py @@ -108,7 +108,7 @@ async def edit_message_media( ), spoiler=media.has_spoiler, ) - elif re.match("^https?://", media.media): + elif re.match(r"^https?://", media.media): media = raw.types.InputMediaPhotoExternal( url=media.media, spoiler=media.has_spoiler ) @@ -148,7 +148,7 @@ async def edit_message_media( ), spoiler=media.has_spoiler, ) - elif re.match("^https?://", media.media): + elif re.match(r"^https?://", media.media): media = raw.types.InputMediaDocumentExternal( url=media.media, spoiler=media.has_spoiler ) @@ -185,7 +185,7 @@ async def edit_message_media( file_reference=media.document.file_reference, ) ) - elif re.match("^https?://", media.media): + elif re.match(r"^https?://", media.media): media = raw.types.InputMediaDocumentExternal(url=media.media) else: media = utils.get_input_media_from_file_id(media.media, FileType.AUDIO) @@ -224,7 +224,7 @@ async def edit_message_media( ), spoiler=media.has_spoiler, ) - elif re.match("^https?://", media.media): + elif re.match(r"^https?://", media.media): media = raw.types.InputMediaDocumentExternal( url=media.media, spoiler=media.has_spoiler ) @@ -256,7 +256,7 @@ async def edit_message_media( file_reference=media.document.file_reference, ) ) - elif re.match("^https?://", media.media): + elif re.match(r"^https?://", media.media): media = raw.types.InputMediaDocumentExternal(url=media.media) else: media = utils.get_input_media_from_file_id(media.media, FileType.DOCUMENT) diff --git a/hydrogram/methods/messages/edit_message_reply_markup.py b/hydrogram/methods/messages/edit_message_reply_markup.py index e0f403dd4..2ee749603 100644 --- a/hydrogram/methods/messages/edit_message_reply_markup.py +++ b/hydrogram/methods/messages/edit_message_reply_markup.py @@ -58,7 +58,9 @@ async def edit_message_reply_markup( await app.edit_message_reply_markup( chat_id, message_id, - InlineKeyboardMarkup([[InlineKeyboardButton("New button", callback_data="new_data")]]), + InlineKeyboardMarkup([ + [InlineKeyboardButton("New button", callback_data="new_data")] + ]), ) """ r = await self.invoke( diff --git a/hydrogram/methods/messages/search_global.py b/hydrogram/methods/messages/search_global.py index 224f93708..db40722a2 100644 --- a/hydrogram/methods/messages/search_global.py +++ b/hydrogram/methods/messages/search_global.py @@ -72,7 +72,9 @@ async def search_global( print(message.text) # Search for recent photos from Global. Get the first 20 results - async for message in app.search_global(filter=enums.MessagesFilter.PHOTO, limit=20): + async for message in app.search_global( + filter=enums.MessagesFilter.PHOTO, limit=20 + ): print(message.photo) """ current = 0 diff --git a/hydrogram/methods/messages/search_messages.py b/hydrogram/methods/messages/search_messages.py index 5ba3d7d95..ee59008f1 100644 --- a/hydrogram/methods/messages/search_messages.py +++ b/hydrogram/methods/messages/search_messages.py @@ -114,7 +114,9 @@ async def search_messages( print(message.text) # Search for pinned messages in chat - async for message in app.search_messages(chat_id, filter=enums.MessagesFilter.PINNED): + async for message in app.search_messages( + chat_id, filter=enums.MessagesFilter.PINNED + ): print(message.text) # Search for messages containing "hello" sent by yourself in chat diff --git a/hydrogram/methods/messages/send_animation.py b/hydrogram/methods/messages/send_animation.py index af0dd5918..13e0373fd 100644 --- a/hydrogram/methods/messages/send_animation.py +++ b/hydrogram/methods/messages/send_animation.py @@ -43,6 +43,7 @@ async def send_animation( unsave: bool = False, parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, has_spoiler: bool | None = None, duration: int = 0, width: int = 0, @@ -95,6 +96,9 @@ async def send_animation( caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Pass True if the caption should be shown above the animation. + has_spoiler (``bool``, *optional*): Pass True if the animation needs to be covered with a spoiler animation. @@ -208,7 +212,7 @@ async def progress(current, total): raw.types.DocumentAttributeAnimated(), ], ) - elif re.match("^https?://", animation): + elif re.match(r"^https?://", animation): media = raw.types.InputMediaDocumentExternal( url=animation, spoiler=has_spoiler ) @@ -249,6 +253,7 @@ async def progress(current, total): random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, + invert_media=show_caption_above_media, reply_markup=await reply_markup.write(self) if reply_markup else None, **await utils.parse_text_entities( self, caption, parse_mode, caption_entities diff --git a/hydrogram/methods/messages/send_audio.py b/hydrogram/methods/messages/send_audio.py index 96c056564..4eb5652b1 100644 --- a/hydrogram/methods/messages/send_audio.py +++ b/hydrogram/methods/messages/send_audio.py @@ -42,6 +42,7 @@ async def send_audio( message_thread_id: int | None = None, parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, duration: int = 0, performer: str | None = None, title: str | None = None, @@ -91,6 +92,9 @@ async def send_audio( caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Pass True if the caption should be shown above the audio. + duration (``int``, *optional*): Duration of the audio in seconds. @@ -163,7 +167,9 @@ async def send_audio( await app.send_audio("me", "audio.mp3", caption="audio caption") # Set audio metadata - await app.send_audio("me", "audio.mp3", title="Title", performer="Performer", duration=234) + await app.send_audio( + "me", "audio.mp3", title="Title", performer="Performer", duration=234 + ) # Keep track of the progress while uploading @@ -195,7 +201,7 @@ async def progress(current, total): ), ], ) - elif re.match("^https?://", audio): + elif re.match(r"^https?://", audio): media = raw.types.InputMediaDocumentExternal(url=audio) else: media = utils.get_input_media_from_file_id(audio, FileType.AUDIO) @@ -227,6 +233,7 @@ async def progress(current, total): random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, + invert_media=show_caption_above_media, reply_markup=await reply_markup.write(self) if reply_markup else None, **await utils.parse_text_entities( self, caption, parse_mode, caption_entities diff --git a/hydrogram/methods/messages/send_cached_media.py b/hydrogram/methods/messages/send_cached_media.py index 92632f251..5016bcb10 100644 --- a/hydrogram/methods/messages/send_cached_media.py +++ b/hydrogram/methods/messages/send_cached_media.py @@ -38,6 +38,7 @@ async def send_cached_media( message_thread_id: int | None = None, parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, disable_notification: bool | None = None, reply_to_message_id: int | None = None, schedule_date: datetime | None = None, @@ -79,6 +80,9 @@ async def send_cached_media( caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Pass True if the caption should be shown above the media. + disable_notification (``bool``, *optional*): Sends the message silently. Users will receive a notification with no sound. @@ -116,6 +120,7 @@ async def send_cached_media( random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, + invert_media=show_caption_above_media, reply_markup=await reply_markup.write(self) if reply_markup else None, **await utils.parse_text_entities(self, caption, parse_mode, caption_entities), ) diff --git a/hydrogram/methods/messages/send_document.py b/hydrogram/methods/messages/send_document.py index 4ff8ea909..931cc6a9f 100644 --- a/hydrogram/methods/messages/send_document.py +++ b/hydrogram/methods/messages/send_document.py @@ -182,7 +182,7 @@ async def progress(current, total): ) ], ) - elif re.match("^https?://", document): + elif re.match(r"^https?://", document): media = raw.types.InputMediaDocumentExternal(url=document) else: media = utils.get_input_media_from_file_id(document, FileType.DOCUMENT) diff --git a/hydrogram/methods/messages/send_location.py b/hydrogram/methods/messages/send_location.py index 9a5c177d6..4adc83aba 100644 --- a/hydrogram/methods/messages/send_location.py +++ b/hydrogram/methods/messages/send_location.py @@ -36,6 +36,7 @@ async def send_location( longitude: float, *, message_thread_id: int | None = None, + horizontal_accuracy: int | None = None, disable_notification: bool | None = None, reply_to_message_id: int | None = None, schedule_date: datetime | None = None, @@ -65,6 +66,9 @@ async def send_location( Unique identifier for the target message thread (topic) of the forum. for forum supergroups only. + horizontal_accuracy (``int``, *optional*): + The estimated horizontal accuracy of the location, in meters. + disable_notification (``bool``, *optional*): Sends the message silently. Users will receive a notification with no sound. @@ -97,7 +101,9 @@ async def send_location( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaGeoPoint( - geo_point=raw.types.InputGeoPoint(lat=latitude, long=longitude) + geo_point=raw.types.InputGeoPoint( + lat=latitude, long=longitude, accuracy_radius=horizontal_accuracy + ) ), message="", silent=disable_notification or None, diff --git a/hydrogram/methods/messages/send_media_group.py b/hydrogram/methods/messages/send_media_group.py index f541e6a45..28bc15544 100644 --- a/hydrogram/methods/messages/send_media_group.py +++ b/hydrogram/methods/messages/send_media_group.py @@ -123,7 +123,7 @@ async def send_media_group( ), spoiler=i.has_spoiler, ) - elif re.match("^https?://", i.media): + elif re.match(r"^https?://", i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), @@ -173,6 +173,7 @@ async def send_media_group( thumb=await self.save_file(i.thumb), spoiler=i.has_spoiler, mime_type=self.guess_mime_type(i.media) or "video/mp4", + nosound_video=True, attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=i.supports_streaming or None, @@ -196,7 +197,7 @@ async def send_media_group( ), spoiler=i.has_spoiler, ) - elif re.match("^https?://", i.media): + elif re.match(r"^https?://", i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), @@ -228,6 +229,7 @@ async def send_media_group( getattr(i.media, "name", "video.mp4") ) or "video/mp4", + nosound_video=True, attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=i.supports_streaming or None, @@ -282,7 +284,7 @@ async def send_media_group( file_reference=media.document.file_reference, ) ) - elif re.match("^https?://", i.media): + elif re.match(r"^https?://", i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), @@ -357,7 +359,7 @@ async def send_media_group( file_reference=media.document.file_reference, ) ) - elif re.match("^https?://", i.media): + elif re.match(r"^https?://", i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), diff --git a/hydrogram/methods/messages/send_message.py b/hydrogram/methods/messages/send_message.py index 868366eea..c4445a0fb 100644 --- a/hydrogram/methods/messages/send_message.py +++ b/hydrogram/methods/messages/send_message.py @@ -101,7 +101,9 @@ async def send_message( await app.send_message("me", "Message sent with **Hydrogram**!") # Disable web page previews - await app.send_message("me", "https://docs.hydrogram.org", disable_web_page_preview=True) + await app.send_message( + "me", "https://docs.hydrogram.org", disable_web_page_preview=True + ) # Reply to a message using its id await app.send_message("me", "this is a reply", reply_to_message_id=123) @@ -110,7 +112,11 @@ async def send_message( # For bots only, send messages with keyboards attached - from hydrogram.types import ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton + from hydrogram.types import ( + ReplyKeyboardMarkup, + InlineKeyboardMarkup, + InlineKeyboardButton, + ) # Send a normal keyboard await app.send_message( diff --git a/hydrogram/methods/messages/send_photo.py b/hydrogram/methods/messages/send_photo.py index 7b6585ff6..06432fb4b 100644 --- a/hydrogram/methods/messages/send_photo.py +++ b/hydrogram/methods/messages/send_photo.py @@ -42,6 +42,7 @@ async def send_photo( message_thread_id: int | None = None, parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, has_spoiler: bool | None = None, ttl_seconds: int | None = None, disable_notification: bool | None = None, @@ -86,6 +87,9 @@ async def send_photo( caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Pass True if the caption should be shown above the photo. + has_spoiler (``bool``, *optional*): Pass True if the photo needs to be covered with a spoiler animation. @@ -165,7 +169,7 @@ async def send_photo( elif ( isinstance(photo, str) and not Path(photo).is_file() - and re.match("^https?://", photo) + and re.match(r"^https?://", photo) ): media = raw.types.InputMediaPhotoExternal( url=photo, ttl_seconds=ttl_seconds, spoiler=has_spoiler @@ -188,6 +192,7 @@ async def send_photo( random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, + invert_media=show_caption_above_media, reply_markup=await reply_markup.write(self) if reply_markup else None, **await utils.parse_text_entities( self, caption, parse_mode, caption_entities diff --git a/hydrogram/methods/messages/send_poll.py b/hydrogram/methods/messages/send_poll.py index 9d57ec122..ae3166534 100644 --- a/hydrogram/methods/messages/send_poll.py +++ b/hydrogram/methods/messages/send_poll.py @@ -33,8 +33,10 @@ async def send_poll( self: hydrogram.Client, chat_id: int | str, question: str, - options: list[str], + options: list[types.InputPollOption], *, + question_parse_mode: enums.ParseMode = None, + question_entities: list[types.MessageEntity] | None = None, message_thread_id: int | None = None, is_anonymous: bool = True, type: enums.PollType = enums.PollType.REGULAR, @@ -68,8 +70,15 @@ async def send_poll( question (``str``): Poll question, 1-255 characters. - options (List of ``str``): - List of answer options, 2-10 strings 1-100 characters each. + options (List of :obj:`~hydrogram.types.InputPollOption`): + List of answer options, 2-10 answer options, 1-100 characters for each option. + + question_parse_mode (:obj:`~hydrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + question_entities (List of :obj:`~hydrogram.types.MessageEntity`): + List of special entities that appear in the poll question, which can be specified instead of *question_parse_mode*. message_thread_id (``int``, *optional*): Unique identifier for the target message thread (topic) of the forum. @@ -153,17 +162,41 @@ async def send_poll( reply_to = utils.get_reply_head_fm(message_thread_id, reply_to_message_id) + question, question_entities = ( + await utils.parse_text_entities(self, question, question_parse_mode, question_entities) + ).values() + if not question_entities: + question_entities = [] + + answers = [] + for i, answer_ in enumerate(options): + if isinstance(answer_, str): + answer, answer_entities = answer_, [] + else: + answer, answer_entities = ( + await utils.parse_text_entities( + self, answer_.text, answer_.text_parse_mode, answer_.text_entities + ) + ).values() + if not answer_entities: + answer_entities = [] + answers.append( + raw.types.PollAnswer( + text=raw.types.TextWithEntities(text=answer, entities=answer_entities), + option=bytes([i]), + ) + ) + r = await self.invoke( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaPoll( poll=raw.types.Poll( id=self.rnd_id(), - question=question, - answers=[ - raw.types.PollAnswer(text=text, option=bytes([i])) - for i, text in enumerate(options) - ], + question=raw.types.TextWithEntities( + text=question, entities=question_entities + ), + answers=answers, closed=is_closed, public_voters=not is_anonymous, multiple_choice=allows_multiple_answers, diff --git a/hydrogram/methods/messages/send_sticker.py b/hydrogram/methods/messages/send_sticker.py index f3de9d2f4..515d4b3e0 100644 --- a/hydrogram/methods/messages/send_sticker.py +++ b/hydrogram/methods/messages/send_sticker.py @@ -139,7 +139,7 @@ async def send_sticker( raw.types.DocumentAttributeFilename(file_name=Path(sticker).name) ], ) - elif re.match("^https?://", sticker): + elif re.match(r"^https?://", sticker): media = raw.types.InputMediaDocumentExternal(url=sticker) else: media = utils.get_input_media_from_file_id(sticker, FileType.STICKER) diff --git a/hydrogram/methods/messages/send_video.py b/hydrogram/methods/messages/send_video.py index e115e72bc..c8f61f10c 100644 --- a/hydrogram/methods/messages/send_video.py +++ b/hydrogram/methods/messages/send_video.py @@ -42,7 +42,9 @@ async def send_video( message_thread_id: int | None = None, parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, has_spoiler: bool | None = None, + no_sound: bool | None = None, ttl_seconds: int | None = None, duration: int = 0, width: int = 0, @@ -92,9 +94,16 @@ async def send_video( caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Pass True if the caption should be shown above the video. + has_spoiler (``bool``, *optional*): Pass True if the video needs to be covered with a spoiler animation. + no_sound (``bool``, *optional*): + Pass True if the video you are uploading is a video message with no sound. + Does not work for external links. + ttl_seconds (``int``, *optional*): Self-Destruct Timer. If you set a timer, the video will self-destruct in *ttl_seconds* @@ -201,6 +210,7 @@ async def progress(current, total): ttl_seconds=ttl_seconds, spoiler=has_spoiler, thumb=thumb, + nosound_video=no_sound, attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=supports_streaming or None, @@ -213,7 +223,7 @@ async def progress(current, total): ), ], ) - elif re.match("^https?://", video): + elif re.match(r"^https?://", video): media = raw.types.InputMediaDocumentExternal( url=video, ttl_seconds=ttl_seconds, spoiler=has_spoiler ) @@ -230,6 +240,7 @@ async def progress(current, total): ttl_seconds=ttl_seconds, spoiler=has_spoiler, thumb=thumb, + nosound_video=no_sound, attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=supports_streaming or None, @@ -254,6 +265,7 @@ async def progress(current, total): random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, + invert_media=show_caption_above_media, reply_markup=await reply_markup.write(self) if reply_markup else None, **await utils.parse_text_entities( self, caption, parse_mode, caption_entities diff --git a/hydrogram/methods/messages/send_voice.py b/hydrogram/methods/messages/send_voice.py index 5bae2c1e3..d563185b4 100644 --- a/hydrogram/methods/messages/send_voice.py +++ b/hydrogram/methods/messages/send_voice.py @@ -158,7 +158,7 @@ async def send_voice( raw.types.DocumentAttributeAudio(voice=True, duration=duration) ], ) - elif re.match("^https?://", voice): + elif re.match(r"^https?://", voice): media = raw.types.InputMediaDocumentExternal(url=voice) else: media = utils.get_input_media_from_file_id(voice, FileType.VOICE) diff --git a/hydrogram/methods/messages/stop_poll.py b/hydrogram/methods/messages/stop_poll.py index 3500b6f20..13072085e 100644 --- a/hydrogram/methods/messages/stop_poll.py +++ b/hydrogram/methods/messages/stop_poll.py @@ -63,7 +63,12 @@ async def stop_poll( peer=await self.resolve_peer(chat_id), id=message_id, media=raw.types.InputMediaPoll( - poll=raw.types.Poll(id=int(poll.id), closed=True, question="", answers=[]) + poll=raw.types.Poll( + id=int(poll.id), + closed=True, + question=raw.types.TextWithEntities(text="", entities=[]), + answers=[], + ) ), reply_markup=await reply_markup.write(self) if reply_markup else None, ) diff --git a/hydrogram/methods/password/change_cloud_password.py b/hydrogram/methods/password/change_cloud_password.py index d8517e55d..41eac867c 100644 --- a/hydrogram/methods/password/change_cloud_password.py +++ b/hydrogram/methods/password/change_cloud_password.py @@ -58,7 +58,9 @@ async def change_cloud_password( await app.change_cloud_password("current_password", "new_password") # Change password and hint - await app.change_cloud_password("current_password", "new_password", new_hint="hint") + await app.change_cloud_password( + "current_password", "new_password", new_hint="hint" + ) """ r = await self.invoke(raw.functions.account.GetPassword()) diff --git a/hydrogram/methods/phone/__init__.py b/hydrogram/methods/phone/__init__.py new file mode 100644 index 000000000..c0f395712 --- /dev/null +++ b/hydrogram/methods/phone/__init__.py @@ -0,0 +1,25 @@ +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + +from .create_video_chat import CreateVideoChat +from .discard_group_call import DiscardGroupCall +from .invite_group_call_members import InviteGroupCallMembers + + +class Phone(CreateVideoChat, DiscardGroupCall, InviteGroupCallMembers): + pass diff --git a/hydrogram/methods/phone/create_video_chat.py b/hydrogram/methods/phone/create_video_chat.py new file mode 100644 index 000000000..5f0fcdbc5 --- /dev/null +++ b/hydrogram/methods/phone/create_video_chat.py @@ -0,0 +1,97 @@ +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import hydrogram +from hydrogram import raw, types, utils + +if TYPE_CHECKING: + from datetime import datetime + + +class CreateVideoChat: + async def create_video_chat( + self: hydrogram.Client, + chat_id: int | str, + title: str | None = None, + start_date: datetime = utils.zero_datetime(), + is_rtmp_stream: bool | None = None, + ) -> types.Message: + """Creates a video chat (a group call bound to a chat). + + Available only for basic groups, supergroups and channels; requires can_manage_video_chats administrator right. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat in which the video chat will be created. A chat can be either a basic group, supergroup or a channel. + + title (``str``, *optional*): + Group call title; if empty, chat title will be used. + + start_date (:py:obj:`~datetime.datetime`, *optional*): + Point in time (Unix timestamp) when the group call is supposed to be started by an administrator; 0 to start the video chat immediately. The date must be at least 10 seconds and at most 8 days in the future. + + is_rtmp_stream (``bool``, *optional*): + Pass true to create an RTMP stream instead of an ordinary video chat; requires owner privileges. + + Returns: + :obj:`~hydrogram.types.Message`: On success, the sent service message is returned. + + Example: + .. code-block:: python + + await app.create_video_chat(chat_id) + + """ + peer = await self.resolve_peer(chat_id) + + if not isinstance(peer, (raw.types.InputPeerChat, raw.types.InputPeerChannel)): + raise ValueError("Target chat should be group, supergroup or channel.") + + r = await self.invoke( + raw.functions.phone.CreateGroupCall( + rtmp_stream=is_rtmp_stream, + peer=peer, + random_id=self.rnd_id() >> 32, + title=title, + schedule_date=utils.datetime_to_timestamp(start_date), + ) + ) + + for i in r.updates: + if isinstance( + i, + ( + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewMessage, + raw.types.UpdateNewScheduledMessage, + ), + ): + return await types.Message._parse( + self, + i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage), + ) + return None diff --git a/hydrogram/methods/phone/discard_group_call.py b/hydrogram/methods/phone/discard_group_call.py new file mode 100644 index 000000000..64b8ba532 --- /dev/null +++ b/hydrogram/methods/phone/discard_group_call.py @@ -0,0 +1,79 @@ +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + +from __future__ import annotations + +import hydrogram +from hydrogram import raw, types + + +class DiscardGroupCall: + async def discard_group_call( + self: hydrogram.Client, + chat_id: int | str, + ) -> types.Message: + """Terminate a group/channel call or livestream + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. A chat can be either a basic group, supergroup or a channel. + + Returns: + :obj:`~hydrogram.types.Message`: On success, the sent service message is returned. + + Example: + .. code-block:: python + + await app.discard_group_call(chat_id) + + """ + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, raw.types.InputPeerChannel): + r = await self.invoke(raw.functions.channels.GetFullChannel(channel=peer)) + elif isinstance(peer, raw.types.InputPeerChat): + r = await self.invoke(raw.functions.messages.GetFullChat(chat_id=peer.chat_id)) + else: + raise ValueError("Target chat should be group, supergroup or channel.") + + call = r.full_chat.call + + if call is None: + raise ValueError("No active group call at this chat.") + + r = await self.invoke(raw.functions.phone.DiscardGroupCall(call=call)) + + for i in r.updates: + if isinstance( + i, + ( + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewMessage, + raw.types.UpdateNewScheduledMessage, + ), + ): + return await types.Message._parse( + self, + i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage), + ) + return None diff --git a/hydrogram/methods/phone/invite_group_call_members.py b/hydrogram/methods/phone/invite_group_call_members.py new file mode 100644 index 000000000..9769b5b22 --- /dev/null +++ b/hydrogram/methods/phone/invite_group_call_members.py @@ -0,0 +1,91 @@ +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + +from __future__ import annotations + +import hydrogram +from hydrogram import raw, types + + +class InviteGroupCallMembers: + async def invite_group_call_members( + self: hydrogram.Client, + chat_id: int | str, + user_ids: int | str | list[int | str], + ) -> types.Message: + """Invites users to an active group call. Sends a service message of type :obj:`~pyrogram.enums.MessageServiceType.VIDEO_CHAT_PARTICIPANTS_INVITED` for video chats. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. A chat can be either a basic group or a supergroup. + + user_ids (``int`` | ``str`` | List of ``int`` or ``str``): + Users identifiers to invite to group call in the chat. + You can pass an ID (int) or username (str). + At most 10 users can be invited simultaneously. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the sent service message is returned. + + Example: + .. code-block:: python + + await app.invite_group_call_members(chat_id, user_id) + + """ + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, raw.types.InputPeerChannel): + r = await self.invoke(raw.functions.channels.GetFullChannel(channel=peer)) + elif isinstance(peer, raw.types.InputPeerChat): + r = await self.invoke(raw.functions.messages.GetFullChat(chat_id=peer.chat_id)) + else: + raise ValueError("Target chat should be group, supergroup or channel.") + + call = r.full_chat.call + + if call is None: + raise ValueError("No active group call at this chat.") + + user_ids = [user_ids] if not isinstance(user_ids, list) else user_ids + + r = await self.invoke( + raw.functions.phone.InviteToGroupCall( + call=call, users=[await self.resolve_peer(i) for i in user_ids] + ) + ) + + for i in r.updates: + if isinstance( + i, + ( + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewMessage, + raw.types.UpdateNewScheduledMessage, + ), + ): + return await types.Message._parse( + self, + i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage), + ) + return None diff --git a/hydrogram/methods/pyromod/listen.py b/hydrogram/methods/pyromod/listen.py index fa9d2e831..c47bdad7b 100644 --- a/hydrogram/methods/pyromod/listen.py +++ b/hydrogram/methods/pyromod/listen.py @@ -84,8 +84,7 @@ async def listen( inline_message_id=inline_message_id, ) - loop = asyncio.get_event_loop() - future = loop.create_future() + future = self.loop.create_future() listener = Listener( future=future, diff --git a/hydrogram/methods/utilities/idle.py b/hydrogram/methods/utilities/idle.py index 451437e96..4b718987a 100644 --- a/hydrogram/methods/utilities/idle.py +++ b/hydrogram/methods/utilities/idle.py @@ -70,7 +70,7 @@ async def main(): def signal_handler(signum, __): logging.info("Stop signal received (%s). Exiting...", signals[signum]) - asyncio.get_event_loop().run_in_executor(None, task.cancel) + asyncio.get_running_loop().run_in_executor(None, task.cancel) for s in (SIGINT, SIGTERM, SIGABRT): signal_fn(s, signal_handler) diff --git a/hydrogram/methods/utilities/remove_error_handler.py b/hydrogram/methods/utilities/remove_error_handler.py index e70ee17d1..ae92ba9b7 100644 --- a/hydrogram/methods/utilities/remove_error_handler.py +++ b/hydrogram/methods/utilities/remove_error_handler.py @@ -28,14 +28,19 @@ class RemoveErrorHandler: def remove_error_handler( - self: hydrogram.Client, error: type[Exception] | Iterable[type[Exception]] = Exception + self: hydrogram.Client, + exception: type[Exception] | Iterable[type[Exception]] = Exception, ): - """Remove a previously-registered error handler. (using exception classes) + """Remove a previously registered error handler using exception classes. Parameters: - error (``Exception`` | Iterable of ``Exception``, *optional*): - The error(s) for handlers to be removed. + exception (``Exception`` | Iterable of ``Exception``, *optional*): + The error(s) for handlers to be removed. Defaults to Exception. """ - for handler in self.dispatcher.error_handlers: - if handler.check_remove(error): - self.dispatcher.error_handlers.remove(handler) + to_remove = [ + handler + for handler in self.dispatcher.error_handlers + if handler.check_remove(exception) + ] + for handler in to_remove: + self.dispatcher.error_handlers.remove(handler) diff --git a/hydrogram/methods/utilities/run.py b/hydrogram/methods/utilities/run.py index 25e0e88f3..5f39950d8 100644 --- a/hydrogram/methods/utilities/run.py +++ b/hydrogram/methods/utilities/run.py @@ -17,7 +17,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -import asyncio from typing import TYPE_CHECKING from hydrogram.methods.utilities.idle import idle @@ -70,13 +69,12 @@ async def main(): app.run(main()) """ - loop = asyncio.get_event_loop() - run = loop.run_until_complete + run = self.loop.run_until_complete if coroutine is not None: run(coroutine) else: - if loop.is_running(): + if self.loop.is_running(): raise RuntimeError( "You must call client.run() method outside of an asyncio event loop. " "Otherwise, you can use client.start(), client.idle(), and client.stop() " diff --git a/hydrogram/methods/utilities/stop_transmission.py b/hydrogram/methods/utilities/stop_transmission.py index ca94aa8b8..3cbeb1db8 100644 --- a/hydrogram/methods/utilities/stop_transmission.py +++ b/hydrogram/methods/utilities/stop_transmission.py @@ -38,6 +38,8 @@ async def progress(current, total, client): async with app: - await app.send_document("me", "file.zip", progress=progress, progress_args=(app,)) + await app.send_document( + "me", "file.zip", progress=progress, progress_args=(app,) + ) """ raise hydrogram.StopTransmission diff --git a/hydrogram/raw/core/gzip_packed.py b/hydrogram/raw/core/gzip_packed.py index 3141b7e3f..3ef5500fa 100644 --- a/hydrogram/raw/core/gzip_packed.py +++ b/hydrogram/raw/core/gzip_packed.py @@ -39,7 +39,7 @@ def __init__(self, packed_data: TLObject): @staticmethod def read(data: BytesIO, *args: Any) -> "GzipPacked": # Return the Object itself instead of a GzipPacked wrapping it - return cast(GzipPacked, TLObject.read(BytesIO(decompress(Bytes.read(data))))) + return cast("GzipPacked", TLObject.read(BytesIO(decompress(Bytes.read(data))))) def write(self, *args: Any) -> bytes: b = BytesIO() diff --git a/hydrogram/raw/core/primitives/double.py b/hydrogram/raw/core/primitives/double.py index d52a20895..03fd45d98 100644 --- a/hydrogram/raw/core/primitives/double.py +++ b/hydrogram/raw/core/primitives/double.py @@ -27,7 +27,7 @@ class Double(bytes, TLObject): @classmethod def read(cls, data: BytesIO, *args: Any) -> float: - return cast(float, unpack("d", data.read(8))[0]) + return cast("float", unpack("d", data.read(8))[0]) def __new__(cls, value: float) -> bytes: # type: ignore return pack("d", value) diff --git a/hydrogram/raw/core/primitives/string.py b/hydrogram/raw/core/primitives/string.py index 2030038e8..7f6a4c61f 100644 --- a/hydrogram/raw/core/primitives/string.py +++ b/hydrogram/raw/core/primitives/string.py @@ -26,7 +26,7 @@ class String(Bytes): @classmethod def read(cls, data: BytesIO, *args) -> str: # type: ignore - return cast(bytes, super(String, String).read(data)).decode(errors="replace") + return cast("bytes", super(String, String).read(data)).decode(errors="replace") def __new__(cls, value: str) -> bytes: # type: ignore return super().__new__(cls, value.encode()) diff --git a/hydrogram/raw/core/primitives/vector.py b/hydrogram/raw/core/primitives/vector.py index f80a5214c..0d93ce56f 100644 --- a/hydrogram/raw/core/primitives/vector.py +++ b/hydrogram/raw/core/primitives/vector.py @@ -59,5 +59,5 @@ def read(cls, data: BytesIO, t: Any = None, *args: Any) -> List: def __new__(cls, value: list, t: Any = None) -> bytes: # type: ignore return b"".join( [Int(cls.ID, False), Int(len(value))] - + [cast(bytes, t(i)) if t else i.write() for i in value] + + [cast("bytes", t(i)) if t else i.write() for i in value] ) diff --git a/hydrogram/raw/core/tl_object.py b/hydrogram/raw/core/tl_object.py index dbe391bca..ead323ba2 100644 --- a/hydrogram/raw/core/tl_object.py +++ b/hydrogram/raw/core/tl_object.py @@ -35,7 +35,7 @@ class TLObject: @classmethod def read(cls, b: BytesIO, *args: Any) -> Any: - return cast(TLObject, objects[int.from_bytes(b.read(4), "little")]).read(b, *args) + return cast("TLObject", objects[int.from_bytes(b.read(4), "little")]).read(b, *args) def write(self, *args: Any) -> bytes: pass @@ -59,7 +59,7 @@ def __str__(self) -> str: def __repr__(self) -> str: return ( - f'hydrogram.raw.{self.QUALNAME}({", ".join(f"{attr}={getattr(self, attr)!r}" for attr in self.__slots__ if getattr(self, attr) is not None)})' + f"hydrogram.raw.{self.QUALNAME}({', '.join(f'{attr}={getattr(self, attr)!r}' for attr in self.__slots__ if getattr(self, attr) is not None)})" if hasattr(self, "QUALNAME") else repr(self) ) diff --git a/hydrogram/session/auth.py b/hydrogram/session/auth.py index 3defbf693..bb0d14380 100644 --- a/hydrogram/session/auth.py +++ b/hydrogram/session/auth.py @@ -17,35 +17,42 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . +from __future__ import annotations + import asyncio import logging import time from hashlib import sha1 from io import BytesIO from os import urandom +from typing import TYPE_CHECKING import hydrogram from hydrogram import raw -from hydrogram.connection import Connection from hydrogram.crypto import aes, prime, rsa from hydrogram.errors import SecurityCheckMismatch from hydrogram.raw.core import Int, Long, TLObject from .internals import MsgId +if TYPE_CHECKING: + from hydrogram.connection import Connection + log = logging.getLogger(__name__) class Auth: MAX_RETRIES = 5 - def __init__(self, client: "hydrogram.Client", dc_id: int, test_mode: bool): + def __init__(self, client: hydrogram.Client, dc_id: int, test_mode: bool): self.dc_id = dc_id self.test_mode = test_mode self.ipv6 = client.ipv6 self.proxy = client.proxy + self.connection_factory = client.connection_factory + self.protocol_factory = client.protocol_factory - self.connection = None + self.connection: Connection | None = None @staticmethod def pack(data: TLObject) -> bytes: @@ -73,7 +80,14 @@ async def create(self): # The server may close the connection at any time, causing the auth key creation to fail. # If that happens, just try again up to MAX_RETRIES times. while True: - self.connection = Connection(self.dc_id, self.test_mode, self.ipv6, self.proxy) + self.connection = self.connection_factory( + dc_id=self.dc_id, + test_mode=self.test_mode, + ipv6=self.ipv6, + proxy=self.proxy, + media=False, + protocol_factory=self.protocol_factory, + ) try: log.info("Start creating a new auth key on DC%s", self.dc_id) diff --git a/hydrogram/session/session.py b/hydrogram/session/session.py index 6d0b0c5a9..a041970ee 100644 --- a/hydrogram/session/session.py +++ b/hydrogram/session/session.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . +from __future__ import annotations + import asyncio import bisect import contextlib @@ -25,11 +27,10 @@ from datetime import datetime, timedelta from hashlib import sha1 from io import BytesIO -from typing import ClassVar +from typing import TYPE_CHECKING, ClassVar import hydrogram from hydrogram import raw -from hydrogram.connection import Connection from hydrogram.crypto import mtproto from hydrogram.errors import ( AuthKeyDuplicated, @@ -45,6 +46,9 @@ from .internals import MsgFactory, MsgId +if TYPE_CHECKING: + from hydrogram.connection import Connection + log = logging.getLogger(__name__) @@ -72,7 +76,7 @@ class Session: def __init__( self, - client: "hydrogram.Client", + client: hydrogram.Client, dc_id: int, auth_key: bytes, test_mode: bool, @@ -86,7 +90,7 @@ def __init__( self.is_media = is_media self.is_cdn = is_cdn - self.connection = None + self.connection: Connection | None = None self.auth_key_id = sha1(auth_key).digest()[-8:] @@ -107,25 +111,25 @@ def __init__( self.recv_task = None self.is_started = asyncio.Event() - - self.loop = asyncio.get_event_loop() + self.restart_lock = asyncio.Lock() self.last_reconnect_attempt = None async def start(self): while True: - self.connection = Connection( - self.dc_id, - self.test_mode, - self.client.ipv6, - self.client.proxy, - self.is_media, + self.connection = self.client.connection_factory( + dc_id=self.dc_id, + test_mode=self.test_mode, + ipv6=self.client.ipv6, + proxy=self.client.proxy, + media=self.is_media, + protocol_factory=self.client.protocol_factory, ) try: await self.connection.connect() - self.recv_task = self.loop.create_task(self.recv_worker()) + self.recv_task = self.client.loop.create_task(self.recv_worker()) await self.send(raw.functions.Ping(ping_id=0), timeout=self.START_TIMEOUT) @@ -147,7 +151,7 @@ async def start(self): timeout=self.START_TIMEOUT, ) - self.ping_task = self.loop.create_task(self.ping_worker()) + self.ping_task = self.client.loop.create_task(self.ping_worker()) log.info("Session initialized: Layer %s", layer) log.info("Device: %s - %s", self.client.device_model, self.client.app_version) @@ -179,10 +183,16 @@ async def stop(self): self.ping_task_event.clear() - await self.connection.close() + if self.connection: + await self.connection.close() + + if self.recv_task and not self.recv_task.done(): + self.recv_task.cancel() - if self.recv_task: - await self.recv_task + with contextlib.suppress(asyncio.CancelledError, asyncio.TimeoutError, RuntimeError): + await asyncio.wait_for(self.recv_task, timeout=1.0) + + self.recv_task = None if not self.is_media and callable(self.client.disconnect_handler): try: @@ -193,20 +203,21 @@ async def stop(self): log.info("Session stopped") async def restart(self): - now = datetime.now() - if ( - self.last_reconnect_attempt - and now - self.last_reconnect_attempt < self.RECONNECT_THRESHOLD - ): - log.info("Reconnecting too frequently, sleeping for a while") - await asyncio.sleep(5) - - self.last_reconnect_attempt = now - await self.stop() - await self.start() + async with self.restart_lock: + now = datetime.now() + if ( + self.last_reconnect_attempt + and now - self.last_reconnect_attempt < self.RECONNECT_THRESHOLD + ): + log.info("Reconnecting too frequently, sleeping for a while") + await asyncio.sleep(5) + + self.last_reconnect_attempt = now + await self.stop() + await self.start() async def handle_packet(self, packet): - data = await self.loop.run_in_executor( + data = await self.client.loop.run_in_executor( hydrogram.crypto_executor, mtproto.unpack, BytesIO(packet), @@ -245,13 +256,17 @@ async def handle_packet(self, packet): if time_diff > 30: raise SecurityCheckMismatch( "The msg_id belongs to over 30 seconds in the future. " - "Most likely the client time has to be synchronized." + "This usually means your system clock is ahead of the actual time. " + "Please synchronize your system time with an NTP server to avoid " + "this error in Hydrogram." ) if time_diff < -300: raise SecurityCheckMismatch( "The msg_id belongs to over 300 seconds in the past. " - "Most likely the client time has to be synchronized." + "This usually means your system clock is behind the actual time. " + "Please synchronize your system time with an NTP server to avoid " + "this error in Hydrogram." ) except SecurityCheckMismatch as e: log.info("Discarding packet: %s", e) @@ -276,7 +291,7 @@ async def handle_packet(self, packet): elif isinstance(msg.body, raw.types.Pong): msg_id = msg.body.msg_id elif self.client is not None: - self.loop.create_task(self.client.handle_updates(msg.body)) + self.client.loop.create_task(self.client.handle_updates(msg.body)) if msg_id in self.results: self.results[msg_id].value = getattr(msg.body, "result", msg.body) @@ -330,11 +345,11 @@ async def recv_worker(self): ) if self.is_started.is_set(): - self.loop.create_task(self.restart()) + self.client.loop.create_task(self.restart()) break - self.loop.create_task(self.handle_packet(packet)) + self.client.loop.create_task(self.handle_packet(packet)) log.info("NetworkTask stopped") @@ -349,7 +364,7 @@ async def send( log.debug("Sent: %s", message) - payload = await self.loop.run_in_executor( + payload = await self.client.loop.run_in_executor( hydrogram.crypto_executor, mtproto.pack, message, @@ -421,6 +436,19 @@ async def invoke( while retries > 0: try: + if ( + self.connection is None + or self.connection.protocol is None + or getattr(self.connection.protocol, "closed", True) + ): + log.warning( + "[%s] Connection is closed or not established. Attempting to reconnect...", + self.client.name, + ) + await self.restart() + await asyncio.sleep(1) + continue + return await self.send(query, timeout=timeout) except FloodWait as e: amount = e.value @@ -448,6 +476,16 @@ async def invoke( str(e) or repr(e), ) + if isinstance(e, OSError) and retries > 1: + try: + await self.restart() + except Exception as restart_error: + log.warning( + "[%s] Failed to restart session: %s", + self.client.name, + str(restart_error) or repr(restart_error), + ) + await asyncio.sleep(0.5) raise TimeoutError("Exceeded maximum number of retries") diff --git a/hydrogram/types/__init__.py b/hydrogram/types/__init__.py index 336feea4b..33b352399 100644 --- a/hydrogram/types/__init__.py +++ b/hydrogram/types/__init__.py @@ -19,6 +19,7 @@ from .authorization import ( SentCode, + Session, TermsOfService, sent_code, terms_of_service, @@ -135,8 +136,10 @@ ) from .input_message_content import ( InputMessageContent, + InputPollOption, InputTextMessageContent, input_message_content, + input_poll_option, input_text_message_content, ) from .list import List @@ -194,6 +197,7 @@ from .user_and_chats import ( Chat, ChatAdminWithInviteLinks, + ChatBackground, ChatEvent, ChatEventFilter, ChatInviteLink, @@ -266,6 +270,7 @@ "CallbackQuery", "Chat", "ChatAdminWithInviteLinks", + "ChatBackground", "ChatEvent", "ChatEventFilter", "ChatInviteLink", @@ -324,6 +329,7 @@ "InputMediaVideo", "InputMessageContent", "InputPhoneContact", + "InputPollOption", "InputTextMessageContent", "InviteLinkImporter", "KeyboardButton", @@ -352,6 +358,7 @@ "Restriction", "SentCode", "SentWebAppMessage", + "Session", "Sticker", "StrippedThumbnail", "TermsOfService", @@ -435,7 +442,7 @@ "input_media_video", "input_message_content", "input_phone_contact", - "input_text_message_content", + "input_poll_option", "input_text_message_content", "invite_link_importer", "keyboard_button", diff --git a/hydrogram/types/authorization/__init__.py b/hydrogram/types/authorization/__init__.py index 03a135421..8b8693708 100644 --- a/hydrogram/types/authorization/__init__.py +++ b/hydrogram/types/authorization/__init__.py @@ -18,6 +18,7 @@ # along with Hydrogram. If not, see . from .sent_code import SentCode +from .session import Session from .terms_of_service import TermsOfService -__all__ = ["SentCode", "TermsOfService"] +__all__ = ["SentCode", "Session", "TermsOfService"] diff --git a/hydrogram/types/authorization/session.py b/hydrogram/types/authorization/session.py new file mode 100644 index 000000000..9ea225b3d --- /dev/null +++ b/hydrogram/types/authorization/session.py @@ -0,0 +1,152 @@ +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from hydrogram.types.object import Object + +if TYPE_CHECKING: + from hydrogram import raw + + +class Session(Object): + """Contains info about a device session. + + Parameters: + id (``int``): + Session id. + + device_model (``str``): + Device model used for authorization. + + platform (``str``): + Platform used for authorization. + + system_version (``str``): + System version used for authorization. + + api_id (``int``): + API ID used for authorization. + + app_name (``str``): + App name used for authorization. + + app_version (``str``): + App version used for authorization. + + date_created (``int``): + Date when authorization was created. + + date_active (``int``): + Date when authorization was last active. + + ip (``str``): + IP address used for authorization. + + country (``str``): + Country where authorization occurred. + + region (``str``): + Region where authorization occurred. + + is_current (``bool``): + Whether this is the current authorization. + + is_official_app (``bool``): + Whether this is an official app. + + is_password_pending (``bool``): + Whether a password is pending. + + accepts_secret_chats (``bool``): + Whether secret chat requests are allowed. + + accepts_calls (``bool``): + Whether call requests are allowed. + + is_confirmed (``bool``): + Whether the authorization is confirmed. + """ + + def __init__( + self, + *, + id: int, + device_model: str, + platform: str, + system_version: str, + api_id: int, + app_name: str, + app_version: str, + date_created: int, + date_active: int, + ip: str, + country: str, + region: str, + is_current: bool, + is_official_app: bool, + is_password_pending: bool, + accepts_secret_chats: bool, + accepts_calls: bool, + is_confirmed: bool, + ): + super().__init__() + + self.id = id + self.device_model = device_model + self.platform = platform + self.system_version = system_version + self.api_id = api_id + self.app_name = app_name + self.app_version = app_version + self.date_created = date_created + self.date_active = date_active + self.ip = ip + self.country = country + self.region = region + self.is_current = is_current + self.is_official_app = is_official_app + self.is_password_pending = is_password_pending + self.accepts_secret_chats = accepts_secret_chats + self.accepts_calls = accepts_calls + self.is_confirmed = is_confirmed + + @staticmethod + def _parse(authorization: raw.types.Authorization) -> Session: + return Session( + id=authorization.hash, + device_model=authorization.device_model, + platform=authorization.platform, + system_version=authorization.system_version, + api_id=authorization.api_id, + app_name=authorization.app_name, + app_version=authorization.app_version, + date_created=authorization.date_created, + date_active=authorization.date_active, + ip=authorization.ip, + country=authorization.country, + region=authorization.region, + is_current=authorization.current, + is_official_app=authorization.official_app, + is_password_pending=authorization.password_pending, + accepts_secret_chats=not authorization.encrypted_requests_disabled, + accepts_calls=not authorization.call_requests_disabled, + is_confirmed=not authorization.unconfirmed, + ) diff --git a/hydrogram/types/bots_and_keyboards/callback_query.py b/hydrogram/types/bots_and_keyboards/callback_query.py index 150b11f0c..8af28b758 100644 --- a/hydrogram/types/bots_and_keyboards/callback_query.py +++ b/hydrogram/types/bots_and_keyboards/callback_query.py @@ -99,7 +99,7 @@ async def _parse(client: hydrogram.Client, callback_query, users) -> CallbackQue chat_id = utils.get_peer_id(callback_query.peer) message_id = callback_query.msg_id - message = client.message_cache[(chat_id, message_id)] or await client.get_messages( + message = client.message_cache[chat_id, message_id] or await client.get_messages( chat_id, message_id ) elif isinstance(callback_query, raw.types.UpdateInlineBotCallbackQuery): diff --git a/hydrogram/types/inline_mode/inline_query_result_animation.py b/hydrogram/types/inline_mode/inline_query_result_animation.py index 9377ef3f3..6fc1fbe9d 100644 --- a/hydrogram/types/inline_mode/inline_query_result_animation.py +++ b/hydrogram/types/inline_mode/inline_query_result_animation.py @@ -71,6 +71,9 @@ class InlineQueryResultAnimation(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Wether the caption should be shown above the animation. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. @@ -92,6 +95,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -108,6 +112,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.reply_markup = reply_markup self.input_message_content = input_message_content @@ -156,6 +161,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_audio.py b/hydrogram/types/inline_mode/inline_query_result_audio.py index cf5d908fd..52d1ad7e4 100644 --- a/hydrogram/types/inline_mode/inline_query_result_audio.py +++ b/hydrogram/types/inline_mode/inline_query_result_audio.py @@ -59,6 +59,9 @@ class InlineQueryResultAudio(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (:obj:`bool`, *optional*): + Wether the caption should be shown above the audio. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): Inline keyboard attached to the message. @@ -76,6 +79,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -88,6 +92,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media async def write(self, client: hydrogram.Client): audio = raw.types.InputWebDocument( @@ -123,6 +128,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_cached_animation.py b/hydrogram/types/inline_mode/inline_query_result_cached_animation.py index 9b9cd6a36..04bdb6ef5 100644 --- a/hydrogram/types/inline_mode/inline_query_result_cached_animation.py +++ b/hydrogram/types/inline_mode/inline_query_result_cached_animation.py @@ -54,6 +54,9 @@ class InlineQueryResultCachedAnimation(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Wether the caption should be shown above the animation. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. @@ -69,6 +72,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -79,6 +83,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.reply_markup = reply_markup self.input_message_content = input_message_content @@ -109,6 +114,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_cached_audio.py b/hydrogram/types/inline_mode/inline_query_result_cached_audio.py index 13932927c..d0c01315f 100644 --- a/hydrogram/types/inline_mode/inline_query_result_cached_audio.py +++ b/hydrogram/types/inline_mode/inline_query_result_cached_audio.py @@ -50,6 +50,9 @@ class InlineQueryResultCachedAudio(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (:obj:`bool`, *optional*): + Wether the caption should be shown above the audio. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. @@ -64,6 +67,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -73,6 +77,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.reply_markup = reply_markup self.input_message_content = input_message_content @@ -102,6 +107,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_cached_document.py b/hydrogram/types/inline_mode/inline_query_result_cached_document.py index 3aa399a2d..d6de6b135 100644 --- a/hydrogram/types/inline_mode/inline_query_result_cached_document.py +++ b/hydrogram/types/inline_mode/inline_query_result_cached_document.py @@ -56,6 +56,9 @@ class InlineQueryResultCachedDocument(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (:obj:`bool`, *optional*): + Wether the caption should be shown above the document. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. @@ -72,6 +75,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -83,6 +87,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.reply_markup = reply_markup self.input_message_content = input_message_content @@ -114,6 +119,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_cached_photo.py b/hydrogram/types/inline_mode/inline_query_result_cached_photo.py index 06a3e4d0d..fd3c4ec06 100644 --- a/hydrogram/types/inline_mode/inline_query_result_cached_photo.py +++ b/hydrogram/types/inline_mode/inline_query_result_cached_photo.py @@ -56,6 +56,9 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (:obj:`bool`, *optional*): + Wether the caption should be shown above the photo. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. @@ -72,6 +75,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -83,6 +87,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.reply_markup = reply_markup self.input_message_content = input_message_content @@ -112,6 +117,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_cached_video.py b/hydrogram/types/inline_mode/inline_query_result_cached_video.py index 7281a0bda..0c9252a5c 100644 --- a/hydrogram/types/inline_mode/inline_query_result_cached_video.py +++ b/hydrogram/types/inline_mode/inline_query_result_cached_video.py @@ -57,6 +57,9 @@ class InlineQueryResultCachedVideo(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (:obj:`bool`, *optional*): + Wether the caption should be shown above the video. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. @@ -73,6 +76,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -84,6 +88,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.reply_markup = reply_markup self.input_message_content = input_message_content @@ -115,6 +120,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_cached_voice.py b/hydrogram/types/inline_mode/inline_query_result_cached_voice.py index 4f1aec899..9136c0c96 100644 --- a/hydrogram/types/inline_mode/inline_query_result_cached_voice.py +++ b/hydrogram/types/inline_mode/inline_query_result_cached_voice.py @@ -54,6 +54,9 @@ class InlineQueryResultCachedVoice(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (:obj:`bool`, *optional*): + Wether the caption should be shown above the voice message. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. @@ -69,6 +72,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -79,6 +83,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.reply_markup = reply_markup self.input_message_content = input_message_content @@ -109,6 +114,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_document.py b/hydrogram/types/inline_mode/inline_query_result_document.py index 3c00f5e2e..9967b90b6 100644 --- a/hydrogram/types/inline_mode/inline_query_result_document.py +++ b/hydrogram/types/inline_mode/inline_query_result_document.py @@ -56,6 +56,9 @@ class InlineQueryResultDocument(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (:obj:`bool`, *optional*): + Wether the caption should be shown above the document. + description (``str``, *optional*): Short description of the result. @@ -84,6 +87,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, description: str = "", reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, @@ -99,6 +103,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.description = description self.thumb_url = thumb_url self.thumb_width = thumb_width @@ -144,6 +149,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_photo.py b/hydrogram/types/inline_mode/inline_query_result_photo.py index 096fd6d20..e1c53dced 100644 --- a/hydrogram/types/inline_mode/inline_query_result_photo.py +++ b/hydrogram/types/inline_mode/inline_query_result_photo.py @@ -67,6 +67,9 @@ class InlineQueryResultPhoto(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (:obj:`bool`, *optional*): + Wether the caption should be shown above the photo. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. @@ -86,6 +89,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -100,6 +104,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.reply_markup = reply_markup self.input_message_content = input_message_content @@ -142,6 +147,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_video.py b/hydrogram/types/inline_mode/inline_query_result_video.py index 80367b90e..c18854e6e 100644 --- a/hydrogram/types/inline_mode/inline_query_result_video.py +++ b/hydrogram/types/inline_mode/inline_query_result_video.py @@ -72,6 +72,9 @@ class InlineQueryResultVideo(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (:obj:`bool`, *optional*): + Wether the caption should be shown above the video. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): Inline keyboard attached to the message @@ -94,6 +97,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -109,6 +113,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.mime_type = mime_type async def write(self, client: hydrogram.Client): @@ -151,6 +156,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/inline_mode/inline_query_result_voice.py b/hydrogram/types/inline_mode/inline_query_result_voice.py index e04289b17..db1c1559c 100644 --- a/hydrogram/types/inline_mode/inline_query_result_voice.py +++ b/hydrogram/types/inline_mode/inline_query_result_voice.py @@ -56,6 +56,9 @@ class InlineQueryResultVoice(InlineQueryResult): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (:obj:`bool`, *optional*): + Wether the caption should be shown above the voice message. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup`, *optional*): Inline keyboard attached to the message. @@ -72,6 +75,7 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, reply_markup: types.InlineKeyboardMarkup = None, input_message_content: types.InputMessageContent = None, ): @@ -83,6 +87,7 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media async def write(self, client: hydrogram.Client): audio = raw.types.InputWebDocument( @@ -117,6 +122,7 @@ async def write(self, client: hydrogram.Client): else None, message=message, entities=entities, + invert_media=self.show_caption_above_media, ) ), ) diff --git a/hydrogram/types/input_media/input_media.py b/hydrogram/types/input_media/input_media.py index 8d937de6e..67e4dc558 100644 --- a/hydrogram/types/input_media/input_media.py +++ b/hydrogram/types/input_media/input_media.py @@ -24,6 +24,7 @@ from hydrogram.types.object import Object if TYPE_CHECKING: + from hydrogram import enums from hydrogram.types.messages_and_media import MessageEntity @@ -43,8 +44,9 @@ def __init__( self, media: str | BinaryIO, caption: str = "", - parse_mode: str | None = None, + parse_mode: enums.ParseMode | None = None, caption_entities: list[MessageEntity] | None = None, + show_caption_above_media: bool | None = None, ): super().__init__() @@ -52,3 +54,4 @@ def __init__( self.caption = caption self.parse_mode = parse_mode self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media diff --git a/hydrogram/types/input_media/input_media_animation.py b/hydrogram/types/input_media/input_media_animation.py index b873a0fb8..366a9b9ca 100644 --- a/hydrogram/types/input_media/input_media_animation.py +++ b/hydrogram/types/input_media/input_media_animation.py @@ -56,6 +56,9 @@ class InputMediaAnimation(InputMedia): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Wether the caption should be shown above the animation. + width (``int``, *optional*): Animation width. @@ -76,12 +79,13 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[MessageEntity] | None = None, + show_caption_above_media: bool | None = None, width: int = 0, height: int = 0, duration: int = 0, has_spoiler: bool | None = None, ): - super().__init__(media, caption, parse_mode, caption_entities) + super().__init__(media, caption, parse_mode, caption_entities, show_caption_above_media) self.thumb = thumb self.width = width diff --git a/hydrogram/types/input_media/input_media_audio.py b/hydrogram/types/input_media/input_media_audio.py index 231e70071..e82d45dba 100644 --- a/hydrogram/types/input_media/input_media_audio.py +++ b/hydrogram/types/input_media/input_media_audio.py @@ -58,6 +58,9 @@ class InputMediaAudio(InputMedia): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Wether the caption should be shown above the audio. + duration (``int``, *optional*): Duration of the audio in seconds @@ -75,11 +78,12 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[MessageEntity] | None = None, + show_caption_above_media: bool | None = None, duration: int = 0, performer: str = "", title: str = "", ): - super().__init__(media, caption, parse_mode, caption_entities) + super().__init__(media, caption, parse_mode, caption_entities, show_caption_above_media) self.thumb = thumb self.duration = duration diff --git a/hydrogram/types/input_media/input_media_document.py b/hydrogram/types/input_media/input_media_document.py index b9089263c..cf66b8340 100644 --- a/hydrogram/types/input_media/input_media_document.py +++ b/hydrogram/types/input_media/input_media_document.py @@ -55,6 +55,9 @@ class InputMediaDocument(InputMedia): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + show_caption_above_media (``bool``, *optional*): + Wether the caption should be shown above the document. """ def __init__( @@ -64,7 +67,8 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[MessageEntity] | None = None, + show_caption_above_media: bool | None = None, ): - super().__init__(media, caption, parse_mode, caption_entities) + super().__init__(media, caption, parse_mode, caption_entities, show_caption_above_media) self.thumb = thumb diff --git a/hydrogram/types/input_media/input_media_photo.py b/hydrogram/types/input_media/input_media_photo.py index c00483efb..329df99da 100644 --- a/hydrogram/types/input_media/input_media_photo.py +++ b/hydrogram/types/input_media/input_media_photo.py @@ -51,6 +51,9 @@ class InputMediaPhoto(InputMedia): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Wether the caption should be shown above the photo. + has_spoiler (``bool``, *optional*): Pass True if the photo needs to be covered with a spoiler animation. """ @@ -61,8 +64,9 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[MessageEntity] | None = None, + show_caption_above_media: bool | None = None, has_spoiler: bool | None = None, ): - super().__init__(media, caption, parse_mode, caption_entities) + super().__init__(media, caption, parse_mode, caption_entities, show_caption_above_media) self.has_spoiler = has_spoiler diff --git a/hydrogram/types/input_media/input_media_video.py b/hydrogram/types/input_media/input_media_video.py index df92616bd..638bb793c 100644 --- a/hydrogram/types/input_media/input_media_video.py +++ b/hydrogram/types/input_media/input_media_video.py @@ -57,6 +57,9 @@ class InputMediaVideo(InputMedia): caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Wether the caption should be shown above the video. + width (``int``, *optional*): Video width. @@ -71,6 +74,10 @@ class InputMediaVideo(InputMedia): has_spoiler (``bool``, *optional*): Pass True if the photo needs to be covered with a spoiler animation. + + no_sound (``bool``, *optional*): + Pass True if the video you are uploading is a video message with no sound. + Does not work for external links. """ def __init__( @@ -80,13 +87,15 @@ def __init__( caption: str = "", parse_mode: enums.ParseMode | None = None, caption_entities: list[MessageEntity] | None = None, + show_caption_above_media: bool | None = None, width: int = 0, height: int = 0, duration: int = 0, supports_streaming: bool = True, has_spoiler: bool | None = None, + no_sound: bool | None = None, ): - super().__init__(media, caption, parse_mode, caption_entities) + super().__init__(media, caption, parse_mode, caption_entities, show_caption_above_media) self.thumb = thumb self.width = width @@ -94,3 +103,4 @@ def __init__( self.duration = duration self.supports_streaming = supports_streaming self.has_spoiler = has_spoiler + self.no_sound = no_sound diff --git a/hydrogram/types/input_message_content/__init__.py b/hydrogram/types/input_message_content/__init__.py index 48927523a..c2b8a45f5 100644 --- a/hydrogram/types/input_message_content/__init__.py +++ b/hydrogram/types/input_message_content/__init__.py @@ -18,6 +18,7 @@ # along with Hydrogram. If not, see . from .input_message_content import InputMessageContent +from .input_poll_option import InputPollOption from .input_text_message_content import InputTextMessageContent -__all__ = ["InputMessageContent", "InputTextMessageContent"] +__all__ = ["InputMessageContent", "InputPollOption", "InputTextMessageContent"] diff --git a/hydrogram/types/input_message_content/input_poll_option.py b/hydrogram/types/input_message_content/input_poll_option.py new file mode 100644 index 000000000..3122d7ef2 --- /dev/null +++ b/hydrogram/types/input_message_content/input_poll_option.py @@ -0,0 +1,57 @@ +# Hydrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2023-present Hydrogram +# +# This file is part of Hydrogram. +# +# Hydrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Hydrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Hydrogram. If not, see . + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from hydrogram.types.object import Object + +if TYPE_CHECKING: + import hydrogram + + +class InputPollOption(Object): + """This object contains information about one answer option in a poll to send. + + Parameters: + text (``str``): + Option text, 1-100 characters + + text_parse_mode (:obj:`~hydrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + Currently, only custom emoji entities are allowed. + + text_entities (List of :obj:`~hydrogram.types.MessageEntity`, *optional*): + List of special entities that appear in the poll option text, which can be specified instead of *text_parse_mode*. + + """ + + def __init__( + self, + *, + text: str, + text_parse_mode: hydrogram.enums.ParseMode = None, + text_entities: list[hydrogram.types.MessageEntity] | None = None, + ): + super().__init__() + + self.text = text + self.text_parse_mode = text_parse_mode + self.text_entities = text_entities diff --git a/hydrogram/types/messages_and_media/location.py b/hydrogram/types/messages_and_media/location.py index db3cb29f6..e6041efc4 100644 --- a/hydrogram/types/messages_and_media/location.py +++ b/hydrogram/types/messages_and_media/location.py @@ -16,6 +16,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . +from __future__ import annotations import hydrogram from hydrogram import raw @@ -31,16 +32,32 @@ class Location(Object): latitude (``float``): Latitude as defined by sender. + + horizontal_accuracy (``int``, *optional*): + The estimated horizontal accuracy of the location, in meters, as defined by sender. """ - def __init__(self, *, client: "hydrogram.Client" = None, longitude: float, latitude: float): + def __init__( + self, + *, + client: hydrogram.Client = None, + longitude: float, + latitude: float, + horizontal_accuracy: int | None, + ): super().__init__(client) self.longitude = longitude self.latitude = latitude + self.horizontal_accuracy = horizontal_accuracy @staticmethod - def _parse(client, geo_point: "raw.types.GeoPoint") -> "Location": + def _parse(client, geo_point: raw.types.GeoPoint) -> Location: if isinstance(geo_point, raw.types.GeoPoint): - return Location(longitude=geo_point.long, latitude=geo_point.lat, client=client) + return Location( + longitude=geo_point.long, + latitude=geo_point.lat, + horizontal_accuracy=geo_point.accuracy_radius, + client=client, + ) return None diff --git a/hydrogram/types/messages_and_media/message.py b/hydrogram/types/messages_and_media/message.py index 23fa58c28..79dacbe80 100644 --- a/hydrogram/types/messages_and_media/message.py +++ b/hydrogram/types/messages_and_media/message.py @@ -170,6 +170,9 @@ class Message(Object, Update): For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption. + show_caption_above_media (``bool``, *optional*): + Message's caption should be shown above the media. + audio (:obj:`~hydrogram.types.Audio`, *optional*): Message is an audio file, information about the file. @@ -378,6 +381,7 @@ def __init__( text: Str = None, entities: list[types.MessageEntity] | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, audio: types.Audio = None, document: types.Document = None, photo: types.Photo = None, @@ -462,6 +466,7 @@ def __init__( self.text = text self.entities = entities self.caption_entities = caption_entities + self.show_caption_above_media = show_caption_above_media self.audio = audio self.document = document self.photo = photo @@ -756,7 +761,7 @@ async def _parse( except MessageIdsEmpty: pass - client.message_cache[(parsed_message.chat.id, parsed_message.id)] = parsed_message + client.message_cache[parsed_message.chat.id, parsed_message.id] = parsed_message if message.reply_to and message.reply_to.forum_topic: if message.reply_to.reply_to_top_id: @@ -990,28 +995,29 @@ async def _parse( ) if message.reply_to: - if message.reply_to.forum_topic: - if message.reply_to.reply_to_top_id: - thread_id = message.reply_to.reply_to_top_id - parsed_message.reply_to_message_id = message.reply_to.reply_to_msg_id - else: - thread_id = message.reply_to.reply_to_msg_id - parsed_message.message_thread_id = thread_id - parsed_message.is_topic_message = True - if topics: - parsed_message.topics = types.ForumTopic._parse(topics[thread_id]) + if isinstance(message.reply_to, raw.types.MessageReplyHeader): + if message.reply_to.forum_topic: + if message.reply_to.reply_to_top_id: + thread_id = message.reply_to.reply_to_top_id + parsed_message.reply_to_message_id = message.reply_to.reply_to_msg_id + else: + thread_id = message.reply_to.reply_to_msg_id + parsed_message.message_thread_id = thread_id + parsed_message.is_topic_message = True + if topics: + parsed_message.topics = types.ForumTopic._parse(topics[thread_id]) + else: + try: + msg = await client.get_messages(parsed_message.chat.id, message.id) + if getattr(msg, "topics"): + parsed_message.topics = msg.topics + except Exception: + pass else: - try: - msg = await client.get_messages(parsed_message.chat.id, message.id) - if getattr(msg, "topics"): - parsed_message.topics = msg.topics - except Exception: - pass - else: - parsed_message.reply_to_message_id = message.reply_to.reply_to_msg_id - parsed_message.reply_to_top_message_id = message.reply_to.reply_to_top_id - if isinstance(message.reply_to, raw.types.MessageReplyStoryHeader): - parsed_message.reply_to_message_id = message.reply_to.story_id + parsed_message.reply_to_message_id = message.reply_to.reply_to_msg_id + parsed_message.reply_to_top_message_id = message.reply_to.reply_to_top_id + if isinstance(message.reply_to, raw.types.MessageReplyStoryHeader): + parsed_message.reply_to_message_id = message.reply_to.story_id if replies: try: @@ -1030,7 +1036,7 @@ async def _parse( pass if not parsed_message.poll: # Do not cache poll messages - client.message_cache[(parsed_message.chat.id, parsed_message.id)] = parsed_message + client.message_cache[parsed_message.chat.id, parsed_message.id] = parsed_message return parsed_message return None @@ -1648,7 +1654,9 @@ async def reply_cached_media( .. code-block:: python await client.send_cached_media( - chat_id=message.chat.id, message_thread_id=message.message_thread_id, file_id=file_id + chat_id=message.chat.id, + message_thread_id=message.message_thread_id, + file_id=file_id, ) Example: @@ -1860,7 +1868,9 @@ async def reply_document( .. code-block:: python await client.send_document( - chat_id=message.chat.id, message_thread_id=message.message_thread_id, document=document + chat_id=message.chat.id, + message_thread_id=message.message_thread_id, + document=document, ) Example: @@ -2375,7 +2385,9 @@ async def reply_photo( async def reply_poll( self, question: str, - options: list[str], + options: list[types.InputPollOption], + question_parse_mode: enums.ParseMode = None, + question_entities: list[types.MessageEntity] | None = None, is_anonymous: bool = True, type: enums.PollType = enums.PollType.REGULAR, allows_multiple_answers: bool | None = None, @@ -2404,22 +2416,39 @@ async def reply_poll( await client.send_poll( chat_id=message.chat.id, - message_thread_id=message.message_thread_id, question="This is a poll", - options=["A", "B", "C] + options=[ + InputPollOption(text="A"), + InputPollOption(text="B"), + InputPollOption(text="C"), + ], ) Example: .. code-block:: python - await message.reply_poll("This is a poll", ["A", "B", "C"]) + await message.reply_poll( + question="This is a poll", + options=[ + InputPollOption(text="A"), + InputPollOption(text="B"), + InputPollOption(text="C"), + ], + ) Parameters: question (``str``): Poll question, 1-255 characters. - options (List of ``str``): - List of answer options, 2-10 strings 1-100 characters each. + options (List of :obj:`~hydrogram.types.InputPollOption`): + List of answer options, 2-10 answer options, 1-100 characters for each option. + + question_parse_mode (:obj:`~hydrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + question_entities (List of :obj:`~hydrogram.types.MessageEntity`): + List of special entities that appear in the poll question, which can be specified instead of *question_parse_mode*. is_anonymous (``bool``, *optional*): True, if the poll needs to be anonymous. @@ -2500,6 +2529,8 @@ async def reply_poll( message_thread_id=self.message_thread_id, question=question, options=options, + question_parse_mode=question_parse_mode, + question_entities=question_entities, is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, @@ -2537,7 +2568,9 @@ async def reply_sticker( .. code-block:: python await client.send_sticker( - chat_id=message.chat.id, message_thread_id=message.message_thread_id, sticker=sticker + chat_id=message.chat.id, + message_thread_id=message.message_thread_id, + sticker=sticker, ) Example: @@ -2729,6 +2762,7 @@ async def reply_video( supports_streaming: bool = True, disable_notification: bool | None = None, reply_to_message_id: int | None = None, + no_sound: bool | None = False, reply_markup: types.InlineKeyboardMarkup | types.ReplyKeyboardMarkup | types.ReplyKeyboardRemove @@ -2806,6 +2840,10 @@ async def reply_video( reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + no_sound (``bool``, *optional*): + Pass True if the video you are uploading is a video message with no sound. + Does not work for external links. + reply_markup (:obj:`~hydrogram.types.InlineKeyboardMarkup` | :obj:`~hydrogram.types.ReplyKeyboardMarkup` | :obj:`~hydrogram.types.ReplyKeyboardRemove` | :obj:`~hydrogram.types.ForceReply`, *optional*): Additional interface options. An object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. @@ -2862,6 +2900,7 @@ async def reply_video( supports_streaming=supports_streaming, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + no_sound=no_sound, reply_markup=reply_markup, progress=progress, progress_args=progress_args, @@ -3346,6 +3385,7 @@ async def copy( message_thread_id: int | None = None, parse_mode: enums.ParseMode | None = None, caption_entities: list[types.MessageEntity] | None = None, + show_caption_above_media: bool | None = None, disable_notification: bool | None = None, reply_to_message_id: int | None = None, schedule_date: datetime | None = None, @@ -3392,6 +3432,9 @@ async def copy( caption_entities (List of :obj:`~hydrogram.types.MessageEntity`): List of special entities that appear in the new caption, which can be specified instead of *parse_mode*. + show_caption_above_media (``bool``, *optional*): + Pass True if the caption should be shown above the media. + disable_notification (``bool``, *optional*): Sends the message silently. Users will receive a notification with no sound. @@ -3540,6 +3583,7 @@ async def copy( caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, + show_caption_above_media=show_caption_above_media, message_thread_id=message_thread_id, ) raise ValueError("Can't copy this message") @@ -3600,7 +3644,9 @@ async def click( .. code-block:: python - await client.send_message(chat_id=message.chat.id, text=message.reply_markup[i][j].text) + await client.send_message( + chat_id=message.chat.id, text=message.reply_markup[i][j].text + ) Example: This method can be used in three different ways: diff --git a/hydrogram/types/messages_and_media/sticker.py b/hydrogram/types/messages_and_media/sticker.py index 8432fd408..29e5fa2f4 100644 --- a/hydrogram/types/messages_and_media/sticker.py +++ b/hydrogram/types/messages_and_media/sticker.py @@ -137,7 +137,7 @@ async def _get_sticker_set_name(invoke, input_sticker_set_id): ) ).set.short_name - Sticker.cache[(set_id, set_access_hash)] = name + Sticker.cache[set_id, set_access_hash] = name if len(Sticker.cache) > 250: for _ in range(50): diff --git a/hydrogram/types/user_and_chats/chat.py b/hydrogram/types/user_and_chats/chat.py index e8d291b7f..45f85481b 100644 --- a/hydrogram/types/user_and_chats/chat.py +++ b/hydrogram/types/user_and_chats/chat.py @@ -264,6 +264,9 @@ def _parse_chat_chat(client, chat: raw.types.Chat) -> Chat: peer_id = -chat.id usernames = getattr(chat, "usernames", []) + if isinstance(chat, raw.types.ChatForbidden): + return Chat(id=peer_id, type=enums.ChatType.GROUP, title=chat.title, client=client) + return Chat( id=peer_id, type=enums.ChatType.GROUP, @@ -282,6 +285,14 @@ def _parse_chat_chat(client, chat: raw.types.Chat) -> Chat: def _parse_channel_chat(client, channel: raw.types.Channel) -> Chat: peer_id = utils.get_channel_id(channel.id) + if isinstance(channel, raw.types.ChannelForbidden): + return Chat( + id=peer_id, + type=enums.ChatType.SUPERGROUP if channel.megagroup else enums.ChatType.CHANNEL, + title=channel.title, + client=client, + ) + return Chat( id=peer_id, type=enums.ChatType.SUPERGROUP if channel.megagroup else enums.ChatType.CHANNEL, diff --git a/hydrogram/types/user_and_chats/emoji_status.py b/hydrogram/types/user_and_chats/emoji_status.py index b4ea688ad..caca54e66 100644 --- a/hydrogram/types/user_and_chats/emoji_status.py +++ b/hydrogram/types/user_and_chats/emoji_status.py @@ -33,44 +33,97 @@ class EmojiStatus(Object): """A user emoji status. Parameters: - custom_emoji_id (``int``): + custom_emoji_id (``int``, *optional*): Custom emoji id. until_date (:py:obj:`~datetime.datetime`, *optional*): Valid until date. + + title (``str``, *optional*): + Title of the collectible. + + gift_id (``int``, *optional*): + Gift collectible id. + + name (``str``, *optional*): + Name of the collectible. + + pattern_custom_emoji_id (``int``, *optional*): + Pattern emoji id. + + center_color (``int``, *optional*): + Center color of the collectible emoji in decimal format. + + edge_color (``int``, *optional*): + Edge color of the collectible emoji in decimal format. + + pattern_color (``int``, *optional*): + Pattern color of the collectible emoji in decimal format. + + text_color (``int``, *optional*): + Text color of the collectible emoji in decimal format. """ def __init__( self, *, client: hydrogram.Client = None, - custom_emoji_id: int, + custom_emoji_id: int | None = None, + gift_id: int | None = None, until_date: datetime | None = None, + title: str | None = None, + name: str | None = None, + pattern_custom_emoji_id: int | None = None, + center_color: int | None = None, + edge_color: int | None = None, + pattern_color: int | None = None, + text_color: int | None = None, ): super().__init__(client) self.custom_emoji_id = custom_emoji_id + self.gift_id = gift_id self.until_date = until_date + self.title = title + self.name = name + self.pattern_custom_emoji_id = pattern_custom_emoji_id + self.center_color = center_color + self.edge_color = edge_color + self.pattern_color = pattern_color + self.text_color = text_color @staticmethod def _parse(client, emoji_status: raw.base.EmojiStatus) -> EmojiStatus | None: if isinstance(emoji_status, raw.types.EmojiStatus): - return EmojiStatus(client=client, custom_emoji_id=emoji_status.document_id) + return EmojiStatus( + client=client, + custom_emoji_id=emoji_status.document_id, + until_date=utils.timestamp_to_datetime(getattr(emoji_status, "until", None)), + ) - if isinstance(emoji_status, raw.types.EmojiStatusUntil): + if isinstance(emoji_status, raw.types.EmojiStatusCollectible): return EmojiStatus( client=client, custom_emoji_id=emoji_status.document_id, - until_date=utils.timestamp_to_datetime(emoji_status.until), + gift_id=emoji_status.collectible_id, + until_date=utils.timestamp_to_datetime(getattr(emoji_status, "until", None)), + title=emoji_status.title, + name=emoji_status.slug, + pattern_custom_emoji_id=emoji_status.pattern_document_id, + center_color=emoji_status.center_color, + edge_color=emoji_status.edge_color, + pattern_color=emoji_status.pattern_color, + text_color=emoji_status.text_color, ) return None def write(self): - if self.until_date: - return raw.types.EmojiStatusUntil( - document_id=self.custom_emoji_id, - until=utils.datetime_to_timestamp(self.until_date), + if self.gift_id: + return raw.types.InputEmojiStatusCollectible( + collectible_id=self.gift_id, until=utils.datetime_to_timestamp(self.until_date) ) - return raw.types.EmojiStatus(document_id=self.custom_emoji_id) + return raw.types.EmojiStatus( + document_id=self.custom_emoji_id, until=utils.datetime_to_timestamp(self.until_date) + ) diff --git a/hydrogram/utils.py b/hydrogram/utils.py index 73a65c5da..132192bec 100644 --- a/hydrogram/utils.py +++ b/hydrogram/utils.py @@ -47,7 +47,7 @@ async def ainput(prompt: str = "", *, hide: bool = False): """Just like the built-in input, but async""" with ThreadPoolExecutor(1) as executor: func = functools.partial(getpass if hide else input, prompt) - return await asyncio.get_event_loop().run_in_executor(executor, func) + return await asyncio.get_running_loop().run_in_executor(executor, func) def get_input_media_from_file_id( diff --git a/news/43.feature.rst b/news/43.feature.rst new file mode 100644 index 000000000..bdd223b59 --- /dev/null +++ b/news/43.feature.rst @@ -0,0 +1 @@ +Added the `transfer_chat_ownershipt` method to the `Client`. This method allows the owner of a chat to transfer ownership to another user. diff --git a/news/45.bugfix.rst b/news/45.bugfix.rst new file mode 100644 index 000000000..53c49ddde --- /dev/null +++ b/news/45.bugfix.rst @@ -0,0 +1 @@ +Fixes a bug that caused the chat parser to return `ChatForbidden` or `ChannelForbidden` which caused some methods like `get_chat_history` to throw `AttributeError`. diff --git a/news/49.misc.rst b/news/49.misc.rst new file mode 100644 index 000000000..e9eb89a5a --- /dev/null +++ b/news/49.misc.rst @@ -0,0 +1 @@ +Defer loop obtaining to when it's actually used. diff --git a/pyproject.toml b/pyproject.toml index 32521156a..a4d76489e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", @@ -43,31 +44,35 @@ tracker = "https://github.com/hydrogram/hydrogram/issues" community = "https://t.me/HydrogramNews" source = "https://github.com/hydrogram/hydrogram" documentation = "https://docs.hydrogram.org" +changelog = "https://docs.hydrogram.org/en/latest/releases.html" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" -[tool.rye] +[tool.uv] managed = true dev-dependencies = [ - "ruff>=0.4.7", + "ruff>=0.11.7", "pytest>=7.4.3", "pytest-asyncio>=0.23.2", "pytest-cov>=4.1.0", "twine>=4.0.2", - "pre-commit>=3.7.1", + "pre-commit>=4.2.0", + "httpx[http2]>=0.28.1", + "lxml>=5.3.2", + "hatchling>=1.27.0", ] [project.optional-dependencies] docs = [ - "Sphinx>=7.3.7", - "furo>=2024.5.6", - "sphinx-autobuild>=2024.4.16", + "sphinx<8.0.0", + "furo>=2024.8.6", + "sphinx-autobuild>=2024.10.3", "sphinx-copybutton>=0.5.2", - "pygments>=2.18.0", - "towncrier>=23.11.0", - "sphinxcontrib-towncrier>=0.4.0a0", + "pygments>=2.19.1", + "towncrier>=24.8.0", + "sphinxcontrib-towncrier>=0.5.0a0", ] fast = [ "tgcrypto>=1.2.5", @@ -91,59 +96,13 @@ exclude = [ "NEWS.rst", "requirements.lock", "requirements-dev.lock", + "ruff.toml", ] [tool.hatch.build.targets.wheel] ignore-vcs = true package = "hydrogram" -[tool.ruff] -line-length = 99 -target-version = "py39" - -[tool.ruff.lint] -select = [ - "FURB", # refurb - "I", # isort - "E", # pycodestyle: error - "W", # pycodestyle: warning - "UP", # pyupgrade - "F", # pyflakes - "SIM", # flake8-simplify - "RET", # flake8-return - "C4", # flake8-comprehensions - "PTH", # flake8-use-pathlib - "PERF", # perflint - "N", # pep8-naming - "RUF", # ruff - "G", # flake8-logging-format - "TID", # flake8-tidy-imports - "TCH", # flake8-type-checking - "FA", # flake8-future-annotations - "PL", # pylint -] -ignore = [ - "RUF001", - "RUF002", - "RUF003", - "E203", - "PERF203", - "PLR09", - "PLR2004", - "PLR1702", - "PLW1514", - "PLW2901", - "PLW0603", -] -preview = true - -[tool.ruff.lint.isort] -known-first-party = ["hydrogram"] - -[tool.ruff.format] -docstring-code-format = true -preview = true - [tool.towncrier] package = "hydrogram" filename = "NEWS.rst" diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 000000000..b264a3638 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,45 @@ +line-length = 99 + +[lint] +select = [ + "FURB", # refurb + "I", # isort + "E", # pycodestyle: error + "W", # pycodestyle: warning + "UP", # pyupgrade + "F", # pyflakes + "SIM", # flake8-simplify + "RET", # flake8-return + "C4", # flake8-comprehensions + "PTH", # flake8-use-pathlib + "PERF", # perflint + "N", # pep8-naming + "RUF", # ruff + "G", # flake8-logging-format + "TID", # flake8-tidy-imports + "TC", # flake8-type-checking + "FA", # flake8-future-annotations + "PL", # pylint +] +ignore = [ + "RUF001", + "RUF002", + "RUF003", + "E203", + "PERF203", + "PLR09", + "PLR2004", + "PLR1702", + "PLW1514", + "PLW2901", + "PLW0603", + "FURB189", # This rule is unsafe and not relevant for us. +] +preview = true + +[lint.isort] +known-first-party = ["hydrogram"] + +[format] +docstring-code-format = true +preview = true diff --git a/tests/filters/test_command.py b/tests/filters/test_command.py index 9f72c79bf..6bf3248ef 100644 --- a/tests/filters/test_command.py +++ b/tests/filters/test_command.py @@ -17,133 +17,122 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . -import pytest - from hydrogram import filters from tests.filters import Client, Message c = Client() -@pytest.mark.asyncio -async def test_single(): +def test_single(): f = filters.command("start") m = Message("/start") - assert await f(c, m) + assert f(c, m) -@pytest.mark.asyncio -async def test_multiple(): +def test_multiple(): f = filters.command(["start", "help"]) m = Message("/start") - assert await f(c, m) + assert f(c, m) m = Message("/help") - assert await f(c, m) + assert f(c, m) m = Message("/settings") - assert not await f(c, m) + assert not f(c, m) -@pytest.mark.asyncio -async def test_prefixes(): +def test_prefixes(): f = filters.command("start", prefixes=list(".!#")) m = Message(".start") - assert await f(c, m) + assert f(c, m) m = Message("!start") - assert await f(c, m) + assert f(c, m) m = Message("#start") - assert await f(c, m) + assert f(c, m) m = Message("/start") - assert not await f(c, m) + assert not f(c, m) -@pytest.mark.asyncio -async def test_case_sensitive(): +def test_case_sensitive(): f = filters.command("start", case_sensitive=True) m = Message("/start") - assert await f(c, m) + assert f(c, m) m = Message("/StArT") - assert not await f(c, m) + assert not f(c, m) -@pytest.mark.asyncio -async def test_case_insensitive(): +def test_case_insensitive(): f = filters.command("start", case_sensitive=False) m = Message("/start") - assert await f(c, m) + assert f(c, m) m = Message("/StArT") - assert await f(c, m) + assert f(c, m) -@pytest.mark.asyncio -async def test_with_mention(): +def test_with_mention(): f = filters.command("start") m = Message("/start@username") - assert await f(c, m) + assert f(c, m) m = Message("/start@UserName") - assert await f(c, m) + assert f(c, m) m = Message("/start@another") - assert not await f(c, m) + assert not f(c, m) -@pytest.mark.asyncio -async def test_with_args(): +def test_with_args(): f = filters.command("start") m = Message("/start") - await f(c, m) + f(c, m) assert m.command == ["start"] m = Message("/StArT") - await f(c, m) + f(c, m) assert m.command == ["start"] m = Message("/start@username") - await f(c, m) + f(c, m) assert m.command == ["start"] m = Message("/start a b c") - await f(c, m) + f(c, m) assert m.command == ["start", *list("abc")] m = Message("/start@username a b c") - await f(c, m) + f(c, m) assert m.command == ["start", *list("abc")] m = Message("/start 'a b' c") - await f(c, m) + f(c, m) assert m.command == ["start", "a b", "c"] m = Message('/start a b "c d"') - await f(c, m) + f(c, m) assert m.command == ["start", *list("ab"), "c d"] -@pytest.mark.asyncio -async def test_caption(): +def test_caption(): f = filters.command("start") m = Message(caption="/start") - assert await f(c, m) + assert f(c, m) -@pytest.mark.asyncio -async def test_no_text(): +def test_no_text(): f = filters.command("start") m = Message() - assert not await f(c, m) + assert not f(c, m) diff --git a/tests/test_file_id.py b/tests/test_file_id.py index 4aebed16e..7a6aa7e7c 100644 --- a/tests/test_file_id.py +++ b/tests/test_file_id.py @@ -17,9 +17,12 @@ # You should have received a copy of the GNU Lesser General Public License # along with Hydrogram. If not, see . +import base64 +import struct + import pytest -from hydrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType +from hydrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType, rle_encode def check(file_id: str, expected_file_type: FileType): @@ -195,3 +198,32 @@ def test_stringify_file_unique_id(): string = "{'file_unique_type': , 'media_id': 5312458109417947140}" assert str(FileUniqueId.decode(file_unique_id)) == string + + +def test_file_unique_id_unknown_type_decode(): + data = struct.pack("" in s + assert "'url': 'https://example.com'" in s diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..642906fac --- /dev/null +++ b/uv.lock @@ -0,0 +1,1886 @@ +version = 1 +revision = 2 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version < '3.10'", +] + +[[package]] +name = "accessible-pygments" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899, upload-time = "2024-05-10T11:23:10.216Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903, upload-time = "2024-05-10T11:23:08.421Z" }, +] + +[[package]] +name = "aiosqlite" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/7d/8bca2bf9a247c2c5dfeec1d7a5f40db6518f88d314b8bca9da29670d2671/aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3", size = 13454, upload-time = "2025-02-03T07:30:16.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0", size = 15792, upload-time = "2025-02-03T07:30:13.6Z" }, +] + +[[package]] +name = "alabaster" +version = "0.7.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776, upload-time = "2024-01-10T00:56:10.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511, upload-time = "2024-01-10T00:56:08.388Z" }, +] + +[[package]] +name = "anyio" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, +] + +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, +] + +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, +] + +[[package]] +name = "backports-tarfile" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406, upload-time = "2024-05-28T17:01:54.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067, upload-time = "2025-04-15T17:05:13.836Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285, upload-time = "2025-04-15T17:05:12.221Z" }, +] + +[[package]] +name = "certifi" +version = "2025.7.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024, upload-time = "2024-09-04T20:43:34.186Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188, upload-time = "2024-09-04T20:43:36.286Z" }, + { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571, upload-time = "2024-09-04T20:43:38.586Z" }, + { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687, upload-time = "2024-09-04T20:43:40.084Z" }, + { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211, upload-time = "2024-09-04T20:43:41.526Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325, upload-time = "2024-09-04T20:43:43.117Z" }, + { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784, upload-time = "2024-09-04T20:43:45.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564, upload-time = "2024-09-04T20:43:46.779Z" }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, + { url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910, upload-time = "2024-09-04T20:45:05.315Z" }, + { url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200, upload-time = "2024-09-04T20:45:06.903Z" }, + { url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565, upload-time = "2024-09-04T20:45:08.975Z" }, + { url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635, upload-time = "2024-09-04T20:45:10.64Z" }, + { url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218, upload-time = "2024-09-04T20:45:12.366Z" }, + { url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486, upload-time = "2024-09-04T20:45:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911, upload-time = "2024-09-04T20:45:15.696Z" }, + { url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632, upload-time = "2024-09-04T20:45:17.284Z" }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, + { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, + { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, + { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, + { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, + { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, + { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/28/f8/dfb01ff6cc9af38552c69c9027501ff5a5117c4cc18dcd27cb5259fa1888/charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", size = 201671, upload-time = "2025-05-02T08:34:12.696Z" }, + { url = "https://files.pythonhosted.org/packages/32/fb/74e26ee556a9dbfe3bd264289b67be1e6d616329403036f6507bb9f3f29c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", size = 144744, upload-time = "2025-05-02T08:34:14.665Z" }, + { url = "https://files.pythonhosted.org/packages/ad/06/8499ee5aa7addc6f6d72e068691826ff093329fe59891e83b092ae4c851c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", size = 154993, upload-time = "2025-05-02T08:34:17.134Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a2/5e4c187680728219254ef107a6949c60ee0e9a916a5dadb148c7ae82459c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", size = 147382, upload-time = "2025-05-02T08:34:19.081Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/56aca740dda674f0cc1ba1418c4d84534be51f639b5f98f538b332dc9a95/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", size = 149536, upload-time = "2025-05-02T08:34:21.073Z" }, + { url = "https://files.pythonhosted.org/packages/53/13/db2e7779f892386b589173dd689c1b1e304621c5792046edd8a978cbf9e0/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", size = 151349, upload-time = "2025-05-02T08:34:23.193Z" }, + { url = "https://files.pythonhosted.org/packages/69/35/e52ab9a276186f729bce7a0638585d2982f50402046e4b0faa5d2c3ef2da/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", size = 146365, upload-time = "2025-05-02T08:34:25.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d8/af7333f732fc2e7635867d56cb7c349c28c7094910c72267586947561b4b/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", size = 154499, upload-time = "2025-05-02T08:34:27.359Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3d/a5b2e48acef264d71e036ff30bcc49e51bde80219bb628ba3e00cf59baac/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", size = 157735, upload-time = "2025-05-02T08:34:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/85/d8/23e2c112532a29f3eef374375a8684a4f3b8e784f62b01da931186f43494/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", size = 154786, upload-time = "2025-05-02T08:34:31.858Z" }, + { url = "https://files.pythonhosted.org/packages/c7/57/93e0169f08ecc20fe82d12254a200dfaceddc1c12a4077bf454ecc597e33/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", size = 150203, upload-time = "2025-05-02T08:34:33.88Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9d/9bf2b005138e7e060d7ebdec7503d0ef3240141587651f4b445bdf7286c2/charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", size = 98436, upload-time = "2025-05-02T08:34:35.907Z" }, + { url = "https://files.pythonhosted.org/packages/6d/24/5849d46cf4311bbf21b424c443b09b459f5b436b1558c04e45dbb7cc478b/charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", size = 105772, upload-time = "2025-05-02T08:34:37.935Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/8f/6ac7fbb29e35645065f7be835bfe3e0cce567f80390de2f3db65d83cb5e3/coverage-7.10.0.tar.gz", hash = "sha256:2768885aef484b5dcde56262cbdfba559b770bfc46994fe9485dc3614c7a5867", size = 819816, upload-time = "2025-07-24T16:53:00.896Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/f6/b2366476b1f48134757f2a42aaf00e7ce8e734eea5f3cf022df113116174/coverage-7.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cbd823f7ea5286c26406ad9e54268544d82f3d1cadb6d4f3b85e9877f0cab1ef", size = 214813, upload-time = "2025-07-24T16:50:18.937Z" }, + { url = "https://files.pythonhosted.org/packages/19/d1/7e26bb4c41ed1b9aca4550187ca42557d79c70d318414a703d814858eacb/coverage-7.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab3f7a5dbaab937df0b9e9e8ec6eab235ba9a6f29d71fd3b24335affaed886cc", size = 215206, upload-time = "2025-07-24T16:50:21.788Z" }, + { url = "https://files.pythonhosted.org/packages/df/71/d5ae128557c8d0ce0156eb1e980e5c6e6f7e54ef3e998c87ab4b3679ff45/coverage-7.10.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8c63aaf850523d8cbe3f5f1a5c78f689b223797bef902635f2493ab43498f36c", size = 242171, upload-time = "2025-07-24T16:50:23.483Z" }, + { url = "https://files.pythonhosted.org/packages/af/87/d586a627e3b61cfe631ebcf3d8a38bf9085142800d2ac434bc20f3699880/coverage-7.10.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4c3133ce3fa84023f7c6921c4dca711be0b658784c5a51a797168229eae26172", size = 243431, upload-time = "2025-07-24T16:50:24.913Z" }, + { url = "https://files.pythonhosted.org/packages/55/cc/ff5c6f4f99a987ebd18a3350194377c7cefee9ddd6e532ede83a0a1f332c/coverage-7.10.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3747d1d0af85b17d3a156cd30e4bbacf893815e846dc6c07050e9769da2b138e", size = 245288, upload-time = "2025-07-24T16:50:26.673Z" }, + { url = "https://files.pythonhosted.org/packages/94/d9/2758e73d7fe496c04dd715af8bb8856354a1ad4cc11553d9096c4b35dc86/coverage-7.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:241923b350437f6a7cb343d9df72998305ef940c3c40009f06e05029a047677c", size = 243235, upload-time = "2025-07-24T16:50:28.505Z" }, + { url = "https://files.pythonhosted.org/packages/9c/9b/3c273dde651d83484992d7e7bcd9cd84a363f01026caf69716390bd79e0d/coverage-7.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13e82e499309307104d58ac66f9eed237f7aaceab4325416645be34064d9a2be", size = 241909, upload-time = "2025-07-24T16:50:30.38Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7c/006d9f66035c4d414ea642d990854a30c23145551315bd0b38100daee168/coverage-7.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bf73cdde4f6c9cd4457b00bf1696236796ac3a241f859a55e0f84a4c58326a7f", size = 242202, upload-time = "2025-07-24T16:50:32.199Z" }, + { url = "https://files.pythonhosted.org/packages/8a/42/80d8747f77c63593a2114c7299df52f7568168e4fd882d7d5ebe8181564f/coverage-7.10.0-cp310-cp310-win32.whl", hash = "sha256:2396e13275b37870a3345f58bce8b15a7e0a985771d13a4b16ce9129954e07d6", size = 217311, upload-time = "2025-07-24T16:50:33.598Z" }, + { url = "https://files.pythonhosted.org/packages/e3/8b/fe04c3851e5d290524f563a8a564c7e5dcd6b5ca35ed689ce662346de230/coverage-7.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:9d45c7c71fb3d2da92ab893602e3f28f2d1560cec765a27e1824a6e0f7e92cfd", size = 218199, upload-time = "2025-07-24T16:50:36.751Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5d/0d1ee021439e3b8b1e86ba92465f5a8d8e15b0222dcdd705606ef089f4fe/coverage-7.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4abc01843581a6f9dd72d4d15761861190973a2305416639435ef509288f7a04", size = 214934, upload-time = "2025-07-24T16:50:38.173Z" }, + { url = "https://files.pythonhosted.org/packages/f2/b2/1e0727327e473aa1a68ca1c9922818a06061d05d44e0c5330109d091b525/coverage-7.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2093297773111d7d748fe4a99b68747e57994531fb5c57bbe439af17c11c169", size = 215320, upload-time = "2025-07-24T16:50:39.617Z" }, + { url = "https://files.pythonhosted.org/packages/84/17/d231e37236863ae3bed7c51615af6b6fc89639c88adf35766d2880dcd7c7/coverage-7.10.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:58240e27815bf105bd975c2fd42e700839f93d5aad034ef976411193ca32dbfd", size = 245321, upload-time = "2025-07-24T16:50:41.544Z" }, + { url = "https://files.pythonhosted.org/packages/95/77/a285aba35bf6ec12c466474931410ef0e6fa85542169009443868e98820a/coverage-7.10.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d019eac999b40ad48521ea057958b07a9f549c0c6d257a20e5c7c4ba91af8d1c", size = 247155, upload-time = "2025-07-24T16:50:43.358Z" }, + { url = "https://files.pythonhosted.org/packages/7b/82/50512eafdd5938a7aa1550014e37fa1c2ca85516bfd85ffeb2f03eff052a/coverage-7.10.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:35e0a1f5454bc80faf4ceab10d1d48f025f92046c9c0f3bec2e1a9dda55137f8", size = 249320, upload-time = "2025-07-24T16:50:44.98Z" }, + { url = "https://files.pythonhosted.org/packages/de/7b/0ec1dc75c8f4d940d03d477b1e07269b4804dcab74ad1e294d40310aba47/coverage-7.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a93dd7759c416dd1cc754123b926d065055cb9a33b6699e64a1e5bdfae1ff459", size = 247047, upload-time = "2025-07-24T16:50:46.482Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5b/40f9b78ae98c2f511a2b062660906e126aadcd35870b9190a4f10f2820ae/coverage-7.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7b3d737266048368a6ffd68f1ecd662c54de56535c82eb8f98a55ac216a72cbd", size = 245078, upload-time = "2025-07-24T16:50:47.904Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f6/672c2a728e77846be7fcc4baaa003e0df86a2174aeb8921d132c14c333d4/coverage-7.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:93227c2707cb0effd9163cd0d8f0d9ab628982f7a3e915d6d64c7107867b9a07", size = 245686, upload-time = "2025-07-24T16:50:49.461Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f3/fa078f0bfae7f0e6b14c426f9cb095f4809314d926c89b9a2641fb4ca482/coverage-7.10.0-cp311-cp311-win32.whl", hash = "sha256:69270af3014ab3058ad6108c6d0e218166f568b5a7a070dc3d62c0a63aca1c4d", size = 217350, upload-time = "2025-07-24T16:50:50.884Z" }, + { url = "https://files.pythonhosted.org/packages/1a/40/eefc3ebb9e458e3dc5db00e6b838969375577a09a8a39986d79cfa283175/coverage-7.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:43c16bbb661a7b4dafac0ab69e44d6dbcc6a64c4d93aefd89edc6f8911b6ab4a", size = 218235, upload-time = "2025-07-24T16:50:52.369Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b8/3b53890c3ad52279eaea594a86bceaf04fcc0aed16856ff81531f75735f4/coverage-7.10.0-cp311-cp311-win_arm64.whl", hash = "sha256:14e7c23fcb74ed808efb4eb48fcd25a759f0e20f685f83266d1df174860e4733", size = 216668, upload-time = "2025-07-24T16:50:53.937Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b4/7b419bb368c9f0b88889cb24805164f6e5550d7183fb59524f6173e0cf0b/coverage-7.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a2adcfdaf3b4d69b0c64ad024fe9dd6996782b52790fb6033d90f36f39e287df", size = 215124, upload-time = "2025-07-24T16:50:55.46Z" }, + { url = "https://files.pythonhosted.org/packages/f4/15/d862a806734c7e50fd5350cef18e22832ba3cdad282ca5660d6fd49def92/coverage-7.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d7b27c2c0840e8eeff3f1963782bd9d3bc767488d2e67a31de18d724327f9f6", size = 215364, upload-time = "2025-07-24T16:50:57.849Z" }, + { url = "https://files.pythonhosted.org/packages/a6/93/4671ca5b2f3650c961a01252cbad96cb41f7c0c2b85c6062f27740a66b06/coverage-7.10.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0ed50429786e935517570b08576a661fd79032e6060985ab492b9d39ba8e66ee", size = 246369, upload-time = "2025-07-24T16:50:59.505Z" }, + { url = "https://files.pythonhosted.org/packages/64/79/2ca676c712d0540df0d7957a4266232980b60858a7a654846af1878cfde0/coverage-7.10.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7171c139ab6571d70460ecf788b1dcaf376bfc75a42e1946b8c031d062bbbad4", size = 248798, upload-time = "2025-07-24T16:51:01.105Z" }, + { url = "https://files.pythonhosted.org/packages/82/c5/67e000b03ba5291f915ddd6ba7c3333e4fdee9ba003b914c8f8f2d966dfe/coverage-7.10.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a726aac7e6e406e403cdee4c443a13aed3ea3d67d856414c5beacac2e70c04e", size = 250260, upload-time = "2025-07-24T16:51:02.761Z" }, + { url = "https://files.pythonhosted.org/packages/9d/76/196783c425b5633db5c789b02a023858377bd73e4db4c805c2503cc42bbf/coverage-7.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2886257481a14e953e96861a00c0fe7151117a523f0470a51e392f00640bba03", size = 248171, upload-time = "2025-07-24T16:51:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/83/1f/bf86c75f42de3641b4bbeab9712ec2815a3a8f5939768077245a492fad9f/coverage-7.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:536578b79521e59c385a2e0a14a5dc2a8edd58761a966d79368413e339fc9535", size = 246368, upload-time = "2025-07-24T16:51:06.16Z" }, + { url = "https://files.pythonhosted.org/packages/2d/95/bfc9a3abef0b160404438e82ec778a0f38660c66a4b0ed94d0417d4d2290/coverage-7.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77fae95558f7804a9ceefabf3c38ad41af1da92b39781b87197c6440dcaaa967", size = 247578, upload-time = "2025-07-24T16:51:07.632Z" }, + { url = "https://files.pythonhosted.org/packages/c6/7e/4fb2a284d56fe2a3ba0c76806923014854a64e503dc8ce21e5a2e6497eea/coverage-7.10.0-cp312-cp312-win32.whl", hash = "sha256:97803e14736493eb029558e1502fe507bd6a08af277a5c8eeccf05c3e970cb84", size = 217521, upload-time = "2025-07-24T16:51:09.56Z" }, + { url = "https://files.pythonhosted.org/packages/f7/30/3ab51058b75e9931fc48594d79888396cf009910fabebe12a6a636ab7f9e/coverage-7.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:4c73ab554e54ffd38d114d6bc4a7115fb0c840cf6d8622211bee3da26e4bd25d", size = 218308, upload-time = "2025-07-24T16:51:11.115Z" }, + { url = "https://files.pythonhosted.org/packages/b0/34/2adc74fd132eaa1873b1688acb906b477216074ed8a37e90426eca6d2900/coverage-7.10.0-cp312-cp312-win_arm64.whl", hash = "sha256:3ae95d5a9aedab853641026b71b2ddd01983a0a7e9bf870a20ef3c8f5d904699", size = 216706, upload-time = "2025-07-24T16:51:12.632Z" }, + { url = "https://files.pythonhosted.org/packages/fc/a7/a47f64718c2229b7860a334edd4e6ff41ec8513f3d3f4246284610344392/coverage-7.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d883fee92b9245c0120fa25b5d36de71ccd4cfc29735906a448271e935d8d86d", size = 215143, upload-time = "2025-07-24T16:51:14.105Z" }, + { url = "https://files.pythonhosted.org/packages/ea/86/14d76a409e9ffab10d5aece73ac159dbd102fc56627e203413bfc6d53b24/coverage-7.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c87e59e88268d30e33d3665ede4fbb77b513981a2df0059e7c106ca3de537586", size = 215401, upload-time = "2025-07-24T16:51:15.978Z" }, + { url = "https://files.pythonhosted.org/packages/f4/b3/fb5c28148a19035a3877fac4e40b044a4c97b24658c980bcf7dff18bfab8/coverage-7.10.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f669d969f669a11d6ceee0b733e491d9a50573eb92a71ffab13b15f3aa2665d4", size = 245949, upload-time = "2025-07-24T16:51:17.628Z" }, + { url = "https://files.pythonhosted.org/packages/6d/95/357559ecfe73970d2023845797361e6c2e6c2c05f970073fff186fe19dd7/coverage-7.10.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9582bd6c6771300a847d328c1c4204e751dbc339a9e249eecdc48cada41f72e6", size = 248295, upload-time = "2025-07-24T16:51:19.46Z" }, + { url = "https://files.pythonhosted.org/packages/7e/58/bac5bc43085712af201f76a24733895331c475e5ddda88ac36c1332a65e6/coverage-7.10.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91f97e9637dc7977842776fdb7ad142075d6fa40bc1b91cb73685265e0d31d32", size = 249733, upload-time = "2025-07-24T16:51:21.518Z" }, + { url = "https://files.pythonhosted.org/packages/b2/db/104b713b3b74752ee365346677fb104765923982ae7bd93b95ca41fe256b/coverage-7.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ae4fa92b6601a62367c6c9967ad32ad4e28a89af54b6bb37d740946b0e0534dd", size = 247943, upload-time = "2025-07-24T16:51:23.194Z" }, + { url = "https://files.pythonhosted.org/packages/32/4f/bef25c797c9496cf31ae9cfa93ce96b4414cacf13688e4a6000982772fd5/coverage-7.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3a5cc8b97473e7b3623dd17a42d2194a2b49de8afecf8d7d03c8987237a9552c", size = 245914, upload-time = "2025-07-24T16:51:24.766Z" }, + { url = "https://files.pythonhosted.org/packages/36/6b/b3efa0b506dbb9a37830d6dc862438fe3ad2833c5f889152bce24d9577cf/coverage-7.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc1cbb7f623250e047c32bd7aa1bb62ebc62608d5004d74df095e1059141ac88", size = 247296, upload-time = "2025-07-24T16:51:26.361Z" }, + { url = "https://files.pythonhosted.org/packages/1f/aa/95a845266aeacab4c57b08e0f4e0e2899b07809a18fd0c1ddef2ac2c9138/coverage-7.10.0-cp313-cp313-win32.whl", hash = "sha256:1380cc5666d778e77f1587cd88cc317158111f44d54c0dd3975f0936993284e0", size = 217566, upload-time = "2025-07-24T16:51:28.961Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d1/27b6e5073a8026b9e0f4224f1ac53217ce589a4cdab1bee878f23bff64f0/coverage-7.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:bf03cf176af098ee578b754a03add4690b82bdfe070adfb5d192d0b1cd15cf82", size = 218337, upload-time = "2025-07-24T16:51:31.45Z" }, + { url = "https://files.pythonhosted.org/packages/c7/06/0e3ba498b11e2245fd96bd7e8dcdf90e1dd36d57f49f308aa650ff0561b8/coverage-7.10.0-cp313-cp313-win_arm64.whl", hash = "sha256:8041c78cd145088116db2329b2fb6e89dc338116c962fbe654b7e9f5d72ab957", size = 216740, upload-time = "2025-07-24T16:51:33.317Z" }, + { url = "https://files.pythonhosted.org/packages/44/8b/11529debbe3e6b39ef6e7c8912554724adc6dc10adbb617a855ecfd387eb/coverage-7.10.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:37cc2c06052771f48651160c080a86431884db9cd62ba622cab71049b90a95b3", size = 215866, upload-time = "2025-07-24T16:51:35.339Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6d/d8981310879e395f39af66536665b75135b1bc88dd21c7764e3340e9ce69/coverage-7.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:91f37270b16178b05fa107d85713d29bf21606e37b652d38646eef5f2dfbd458", size = 216083, upload-time = "2025-07-24T16:51:36.932Z" }, + { url = "https://files.pythonhosted.org/packages/c3/84/93295402de002de8b8c953bf6a1f19687174c4db7d44c1e85ffc153a772d/coverage-7.10.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f9b0b0168864d09bcb9a3837548f75121645c4cfd0efce0eb994c221955c5b10", size = 257320, upload-time = "2025-07-24T16:51:38.734Z" }, + { url = "https://files.pythonhosted.org/packages/02/5c/d0540db4869954dac0f69ad709adcd51f3a73ab11fcc9435ee76c518944a/coverage-7.10.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:df0be435d3b616e7d3ee3f9ebbc0d784a213986fe5dff9c6f1042ee7cfd30157", size = 259182, upload-time = "2025-07-24T16:51:40.463Z" }, + { url = "https://files.pythonhosted.org/packages/59/b2/d7d57a41a15ca4b47290862efd6b596d0a185bfd26f15d04db9f238aa56c/coverage-7.10.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:35e9aba1c4434b837b1d567a533feba5ce205e8e91179c97974b28a14c23d3a0", size = 261322, upload-time = "2025-07-24T16:51:42.44Z" }, + { url = "https://files.pythonhosted.org/packages/16/92/fd828ae411b3da63673305617b6fbeccc09feb7dfe397d164f55a65cd880/coverage-7.10.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a0b0c481e74dfad631bdc2c883e57d8b058e5c90ba8ef087600995daf7bbec18", size = 258914, upload-time = "2025-07-24T16:51:44.115Z" }, + { url = "https://files.pythonhosted.org/packages/28/49/4aa5f5464b2e1215640c0400c5b007e7f5cdade8bf39c55c33b02f3a8c7f/coverage-7.10.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8aec1b7c8922808a433c13cd44ace6fceac0609f4587773f6c8217a06102674b", size = 257051, upload-time = "2025-07-24T16:51:45.75Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5a/ded2346098c7f48ff6e135b5005b97de4cd9daec5c39adb4ecf3a60967da/coverage-7.10.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:04ec59ceb3a594af0927f2e0d810e1221212abd9a2e6b5b917769ff48760b460", size = 257869, upload-time = "2025-07-24T16:51:47.41Z" }, + { url = "https://files.pythonhosted.org/packages/46/66/e06cedb8fc7d1c96630b2f549b8cdc084e2623dcc70c900cb3b705a36a60/coverage-7.10.0-cp313-cp313t-win32.whl", hash = "sha256:b6871e62d29646eb9b3f5f92def59e7575daea1587db21f99e2b19561187abda", size = 218243, upload-time = "2025-07-24T16:51:49.136Z" }, + { url = "https://files.pythonhosted.org/packages/e7/1e/e84dd5ff35ed066bd6150e5c26fe0061ded2c59c209fd4f18db0650766c0/coverage-7.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff99cff2be44f78920b76803f782e91ffb46ccc7fa89eccccc0da3ca94285b64", size = 219334, upload-time = "2025-07-24T16:51:50.789Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e0/b7b60b5dbc4e88eac0a0e9d5b4762409a59b29bf4e772b3509c8543ccaba/coverage-7.10.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3246b63501348fe47299d12c47a27cfc221cfbffa1c2d857bcc8151323a4ae4f", size = 217196, upload-time = "2025-07-24T16:51:52.599Z" }, + { url = "https://files.pythonhosted.org/packages/15/c1/597b4fa7d6c0861d4916c4fe5c45bf30c11b31a3b07fedffed23dec5f765/coverage-7.10.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:1f628d91f941a375b4503cb486148dbeeffb48e17bc080e0f0adfee729361574", size = 215139, upload-time = "2025-07-24T16:51:54.381Z" }, + { url = "https://files.pythonhosted.org/packages/18/47/07973dcad0161355cf01ff0023ab34466b735deb460a178f37163d7c800e/coverage-7.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3a0e101d5af952d233557e445f42ebace20b06b4ceb615581595ced5386caa78", size = 215419, upload-time = "2025-07-24T16:51:56.341Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f8/c65127782da312084ef909c1531226c869bfe22dac8b92d9c609d8150131/coverage-7.10.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ec4c1abbcc53f9f650acb14ea71725d88246a9e14ed42f8dd1b4e1b694e9d842", size = 245917, upload-time = "2025-07-24T16:51:58.045Z" }, + { url = "https://files.pythonhosted.org/packages/05/97/a7f2fe79b6ae759ccc8740608cf9686ae406cc5e5591947ebbf1d679a325/coverage-7.10.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9c95f3a7f041b4cc68a8e3fecfa6366170c13ac773841049f1cd19c8650094e0", size = 248225, upload-time = "2025-07-24T16:51:59.745Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d3/d2e1496d7ac3340356c5de582e08e14b02933e254924f79d18e9749269d8/coverage-7.10.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a2cd597b69c16d24e310611f2ed6fcfb8f09429316038c03a57e7b4f5345244", size = 249844, upload-time = "2025-07-24T16:52:01.799Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7e/e26d966c9cae62500e5924107974ede2e985f7d119d10ed44d102998e509/coverage-7.10.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5e18591906a40c2b3609196c9879136aa4a47c5405052ca6b065ab10cb0b71d0", size = 247871, upload-time = "2025-07-24T16:52:03.797Z" }, + { url = "https://files.pythonhosted.org/packages/59/95/6a372a292dfb9d6e2cc019fc50878f7a6a5fbe704604018d7c5c1dbffb2d/coverage-7.10.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:485c55744252ed3f300cc1a0f5f365e684a0f2651a7aed301f7a67125906b80e", size = 245714, upload-time = "2025-07-24T16:52:05.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/7f/63da22b7bc4e82e2c1df7755223291fc94fb01942cfe75e19f2bed96129e/coverage-7.10.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4dabea1516e5b0e9577282b149c8015e4dceeb606da66fb8d9d75932d5799bf5", size = 247131, upload-time = "2025-07-24T16:52:07.661Z" }, + { url = "https://files.pythonhosted.org/packages/3d/af/883272555e34872879f48daea4207489cb36df249e3069e6a8a664dc6ba6/coverage-7.10.0-cp314-cp314-win32.whl", hash = "sha256:ac455f0537af22333fdc23b824cff81110dff2d47300bb2490f947b7c9a16017", size = 217804, upload-time = "2025-07-24T16:52:09.328Z" }, + { url = "https://files.pythonhosted.org/packages/90/f6/7afc3439994b7f7311d858438d49eef8b06eadbf2322502d921a110fae1e/coverage-7.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:b3c94b532f52f95f36fbfde3e178510a4d04eea640b484b2fe8f1491338dc653", size = 218596, upload-time = "2025-07-24T16:52:11.038Z" }, + { url = "https://files.pythonhosted.org/packages/0b/99/7c715cfa155609ee3e71bc81b4d1265e1a9b79ad00cc3d19917ea736cbac/coverage-7.10.0-cp314-cp314-win_arm64.whl", hash = "sha256:2f807f2c3a9da99c80dfa73f09ef5fc3bd21e70c73ba1c538f23396a3a772252", size = 216960, upload-time = "2025-07-24T16:52:12.77Z" }, + { url = "https://files.pythonhosted.org/packages/59/18/5cb476346d3842f2e42cd92614a91921ebad38aa97aba63f2aab51919e35/coverage-7.10.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0a889ef25215990f65073c32cadf37483363a6a22914186dedc15a6b1a597d50", size = 215881, upload-time = "2025-07-24T16:52:14.492Z" }, + { url = "https://files.pythonhosted.org/packages/80/1b/c066d6836f4c1940a8df14894a5ec99db362838fdd9eee9fb7efe0e561d2/coverage-7.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:39c638ecf3123805bacbf71aff8091e93af490c676fca10ab4e442375076e483", size = 216087, upload-time = "2025-07-24T16:52:16.216Z" }, + { url = "https://files.pythonhosted.org/packages/1d/57/f0996fd468e70d4d24d69eba10ecc2b913c2e85d9f3c1bb2075ad7554c05/coverage-7.10.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f2f2c0df0cbcf7dffa14f88a99c530cdef3f4fcfe935fa4f95d28be2e7ebc570", size = 257408, upload-time = "2025-07-24T16:52:18.136Z" }, + { url = "https://files.pythonhosted.org/packages/36/78/c9f308b2b986cc685d4964a3b829b053817a07d7ba14ff124cf06154402e/coverage-7.10.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:048d19a5d641a2296745ab59f34a27b89a08c48d6d432685f22aac0ec1ea447f", size = 259373, upload-time = "2025-07-24T16:52:20.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/13/192827b71da71255d3554cb7dc289bce561cb281bda27e1b0dd19d88e47d/coverage-7.10.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1209b65d302d7a762004be37ab9396cbd8c99525ed572bdf455477e3a9449e06", size = 261495, upload-time = "2025-07-24T16:52:23.018Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5c/cf4694353405abbb440a94468df8e5c4dbf884635da1f056b43be7284d28/coverage-7.10.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e44aa79a36a7a0aec6ea109905a4a7c28552d90f34e5941b36217ae9556657d5", size = 258970, upload-time = "2025-07-24T16:52:25.685Z" }, + { url = "https://files.pythonhosted.org/packages/c7/83/fb45dac65c42eff6ce4153fe51b9f2a9fdc832ce57b7902ab9ff216c3faa/coverage-7.10.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:96124be864b89395770c9a14652afcddbcdafb99466f53a9281c51d1466fb741", size = 257046, upload-time = "2025-07-24T16:52:27.778Z" }, + { url = "https://files.pythonhosted.org/packages/60/95/577dc757c01f493a1951157475dd44561c82084387f12635974fb62e848c/coverage-7.10.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aad222e841f94b42bd1d6be71737fade66943853f0807cf87887c88f70883a2a", size = 257946, upload-time = "2025-07-24T16:52:29.931Z" }, + { url = "https://files.pythonhosted.org/packages/da/5a/14b1be12e3a71fcf4031464ae285dab7df0939976236d0462c4c5382d317/coverage-7.10.0-cp314-cp314t-win32.whl", hash = "sha256:0eed5354d28caa5c8ad60e07e938f253e4b2810ea7dd56784339b6ce98b6f104", size = 218602, upload-time = "2025-07-24T16:52:32.074Z" }, + { url = "https://files.pythonhosted.org/packages/a0/8d/c32890c0f4f7f71b8d4a1074ef8e9ef28e9b9c2f9fd0e2896f2cc32593bf/coverage-7.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3da35f9980058acb960b2644527cc3911f1e00f94d309d704b309fa984029109", size = 219720, upload-time = "2025-07-24T16:52:34.745Z" }, + { url = "https://files.pythonhosted.org/packages/22/f7/e5cc13338aa5e2780b6226fb50e9bd8f3f88da85a4b2951447b4b51109a4/coverage-7.10.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cb9e138dfa8a4b5c52c92a537651e2ca4f2ca48d8cb1bc01a2cbe7a5773c2426", size = 217374, upload-time = "2025-07-24T16:52:36.974Z" }, + { url = "https://files.pythonhosted.org/packages/45/fb/ace937cb8faf4d723bfc6058fee39b6756d888cf7524559885e437d06d71/coverage-7.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cf283ec9c6878826291b17442eb5c32d3d252dc77d25e082b460b2d2ea67ba3c", size = 214811, upload-time = "2025-07-24T16:52:38.826Z" }, + { url = "https://files.pythonhosted.org/packages/ff/76/cbacf622916d4d3e1c5dbe07cacfdf19c80dfab9e5f65fa62d8fa0dbab31/coverage-7.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8a83488c9fc6fff487f2ab551f9b64c70672357b8949f0951b0cd778b3ed8165", size = 215190, upload-time = "2025-07-24T16:52:40.577Z" }, + { url = "https://files.pythonhosted.org/packages/7a/24/794bebf18d9b6eb83defcc33b54c3af9ae781d2584aa07539631de2a4975/coverage-7.10.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b86df3a7494d12338c11e59f210a0498d6109bbc3a4037f44de517ebb30a9c6b", size = 241262, upload-time = "2025-07-24T16:52:42.37Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/674dfe9a00de71576d21825fb4c608db18ad69bec3e1184bf0b4d6e440c0/coverage-7.10.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6de9b460809e5e4787b742e786a36ae2346a53982e2be317cdcb7a33c56412fb", size = 243159, upload-time = "2025-07-24T16:52:44.165Z" }, + { url = "https://files.pythonhosted.org/packages/73/0c/ff37bcbae61f0e7783a2b58019e757e368754819f24428beebb31a9589e9/coverage-7.10.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de5ef8a5954d63fa26a6aaa4600e48f885ce70fe495e8fce2c43aa9241fc9434", size = 244727, upload-time = "2025-07-24T16:52:46.942Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/a42496f920770374a4116ccd01349d112e01969aeb03ba6eb3af74d5b7a0/coverage-7.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f178fe5e96f1e057527d5d0b20ab76b8616e0410169c33716cc226118eaf2c4f", size = 242662, upload-time = "2025-07-24T16:52:49.365Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f0/518341fbed44ada9660d92bb7001d848d6901d606f157d1d9009b36bfe1b/coverage-7.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4a38c42f0182a012fa9ec25bc6057e51114c1ba125be304f3f776d6d283cb303", size = 240896, upload-time = "2025-07-24T16:52:51.223Z" }, + { url = "https://files.pythonhosted.org/packages/f6/08/fbe01e9a7394e11215ec3c67d51c66947abb4a02c9076cd04e8ccd454fa5/coverage-7.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bf09beb5c1785cb36aad042455c0afab561399b74bb8cdaf6e82b7d77322df99", size = 241848, upload-time = "2025-07-24T16:52:53.388Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d4/edf9d75080254d969e2a8c8b4f4a5391865a3097de493a2ad3c938c8c9d3/coverage-7.10.0-cp39-cp39-win32.whl", hash = "sha256:cb8dfbb5d3016cb8d1940444c0c69b40cdc6c8bde724b07716ee5ea47b5273c6", size = 217320, upload-time = "2025-07-24T16:52:55.258Z" }, + { url = "https://files.pythonhosted.org/packages/91/bb/4ffaec3b62fa24faf4c462cbdb0145a395f532aacc85f2e51a571d54a74f/coverage-7.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:58ff22653cd93d563110d1ff2aef958f5f21be9e917762f8124d0e36f80f172a", size = 218215, upload-time = "2025-07-24T16:52:57.118Z" }, + { url = "https://files.pythonhosted.org/packages/09/df/7c34bada8ace39f688b3bd5bc411459a20a3204ccb0984c90169a80a9366/coverage-7.10.0-py3-none-any.whl", hash = "sha256:310a786330bb0463775c21d68e26e79973839b66d29e065c5787122b8dd4489f", size = 206777, upload-time = "2025-07-24T16:52:59.009Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "cryptography" +version = "45.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/1e/49527ac611af559665f71cbb8f92b332b5ec9c6fbc4e88b0f8e92f5e85df/cryptography-45.0.5.tar.gz", hash = "sha256:72e76caa004ab63accdf26023fccd1d087f6d90ec6048ff33ad0445abf7f605a", size = 744903, upload-time = "2025-07-02T13:06:25.941Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/05/2194432935e29b91fb649f6149c1a4f9e6d3d9fc880919f4ad1bcc22641e/cryptography-45.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3a264aae5f7fbb089dbc01e0242d3b67dffe3e6292e1f5182122bdf58e65215d", size = 4205926, upload-time = "2025-07-02T13:05:04.741Z" }, + { url = "https://files.pythonhosted.org/packages/07/8b/9ef5da82350175e32de245646b1884fc01124f53eb31164c77f95a08d682/cryptography-45.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e74d30ec9c7cb2f404af331d5b4099a9b322a8a6b25c4632755c8757345baac5", size = 4429235, upload-time = "2025-07-02T13:05:07.084Z" }, + { url = "https://files.pythonhosted.org/packages/7c/e1/c809f398adde1994ee53438912192d92a1d0fc0f2d7582659d9ef4c28b0c/cryptography-45.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3af26738f2db354aafe492fb3869e955b12b2ef2e16908c8b9cb928128d42c57", size = 4209785, upload-time = "2025-07-02T13:05:09.321Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8b/07eb6bd5acff58406c5e806eff34a124936f41a4fb52909ffa4d00815f8c/cryptography-45.0.5-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e6c00130ed423201c5bc5544c23359141660b07999ad82e34e7bb8f882bb78e0", size = 3893050, upload-time = "2025-07-02T13:05:11.069Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ef/3333295ed58d900a13c92806b67e62f27876845a9a908c939f040887cca9/cryptography-45.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:dd420e577921c8c2d31289536c386aaa30140b473835e97f83bc71ea9d2baf2d", size = 4457379, upload-time = "2025-07-02T13:05:13.32Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9d/44080674dee514dbb82b21d6fa5d1055368f208304e2ab1828d85c9de8f4/cryptography-45.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d05a38884db2ba215218745f0781775806bde4f32e07b135348355fe8e4991d9", size = 4209355, upload-time = "2025-07-02T13:05:15.017Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d8/0749f7d39f53f8258e5c18a93131919ac465ee1f9dccaf1b3f420235e0b5/cryptography-45.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ad0caded895a00261a5b4aa9af828baede54638754b51955a0ac75576b831b27", size = 4456087, upload-time = "2025-07-02T13:05:16.945Z" }, + { url = "https://files.pythonhosted.org/packages/09/d7/92acac187387bf08902b0bf0699816f08553927bdd6ba3654da0010289b4/cryptography-45.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9024beb59aca9d31d36fcdc1604dd9bbeed0a55bface9f1908df19178e2f116e", size = 4332873, upload-time = "2025-07-02T13:05:18.743Z" }, + { url = "https://files.pythonhosted.org/packages/03/c2/840e0710da5106a7c3d4153c7215b2736151bba60bf4491bdb421df5056d/cryptography-45.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91098f02ca81579c85f66df8a588c78f331ca19089763d733e34ad359f474174", size = 4564651, upload-time = "2025-07-02T13:05:21.382Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e7/2187be2f871c0221a81f55ee3105d3cf3e273c0a0853651d7011eada0d7e/cryptography-45.0.5-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3fcfbefc4a7f332dece7272a88e410f611e79458fab97b5efe14e54fe476f4fd", size = 4197780, upload-time = "2025-07-02T13:05:29.299Z" }, + { url = "https://files.pythonhosted.org/packages/b9/cf/84210c447c06104e6be9122661159ad4ce7a8190011669afceeaea150524/cryptography-45.0.5-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:460f8c39ba66af7db0545a8c6f2eabcbc5a5528fc1cf6c3fa9a1e44cec33385e", size = 4420091, upload-time = "2025-07-02T13:05:31.221Z" }, + { url = "https://files.pythonhosted.org/packages/3e/6a/cb8b5c8bb82fafffa23aeff8d3a39822593cee6e2f16c5ca5c2ecca344f7/cryptography-45.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9b4cf6318915dccfe218e69bbec417fdd7c7185aa7aab139a2c0beb7468c89f0", size = 4198711, upload-time = "2025-07-02T13:05:33.062Z" }, + { url = "https://files.pythonhosted.org/packages/04/f7/36d2d69df69c94cbb2473871926daf0f01ad8e00fe3986ac3c1e8c4ca4b3/cryptography-45.0.5-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2089cc8f70a6e454601525e5bf2779e665d7865af002a5dec8d14e561002e135", size = 3883299, upload-time = "2025-07-02T13:05:34.94Z" }, + { url = "https://files.pythonhosted.org/packages/82/c7/f0ea40f016de72f81288e9fe8d1f6748036cb5ba6118774317a3ffc6022d/cryptography-45.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0027d566d65a38497bc37e0dd7c2f8ceda73597d2ac9ba93810204f56f52ebc7", size = 4450558, upload-time = "2025-07-02T13:05:37.288Z" }, + { url = "https://files.pythonhosted.org/packages/06/ae/94b504dc1a3cdf642d710407c62e86296f7da9e66f27ab12a1ee6fdf005b/cryptography-45.0.5-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:be97d3a19c16a9be00edf79dca949c8fa7eff621763666a145f9f9535a5d7f42", size = 4198020, upload-time = "2025-07-02T13:05:39.102Z" }, + { url = "https://files.pythonhosted.org/packages/05/2b/aaf0adb845d5dabb43480f18f7ca72e94f92c280aa983ddbd0bcd6ecd037/cryptography-45.0.5-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:7760c1c2e1a7084153a0f68fab76e754083b126a47d0117c9ed15e69e2103492", size = 4449759, upload-time = "2025-07-02T13:05:41.398Z" }, + { url = "https://files.pythonhosted.org/packages/91/e4/f17e02066de63e0100a3a01b56f8f1016973a1d67551beaf585157a86b3f/cryptography-45.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6ff8728d8d890b3dda5765276d1bc6fb099252915a2cd3aff960c4c195745dd0", size = 4319991, upload-time = "2025-07-02T13:05:43.64Z" }, + { url = "https://files.pythonhosted.org/packages/f2/2e/e2dbd629481b499b14516eed933f3276eb3239f7cee2dcfa4ee6b44d4711/cryptography-45.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7259038202a47fdecee7e62e0fd0b0738b6daa335354396c6ddebdbe1206af2a", size = 4554189, upload-time = "2025-07-02T13:05:46.045Z" }, + { url = "https://files.pythonhosted.org/packages/8b/5d/a19441c1e89afb0f173ac13178606ca6fab0d3bd3ebc29e9ed1318b507fc/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c648025b6840fe62e57107e0a25f604db740e728bd67da4f6f060f03017d5097", size = 4140906, upload-time = "2025-07-02T13:05:55.914Z" }, + { url = "https://files.pythonhosted.org/packages/4b/db/daceb259982a3c2da4e619f45b5bfdec0e922a23de213b2636e78ef0919b/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b8fa8b0a35a9982a3c60ec79905ba5bb090fc0b9addcfd3dc2dd04267e45f25e", size = 4374411, upload-time = "2025-07-02T13:05:57.814Z" }, + { url = "https://files.pythonhosted.org/packages/6a/35/5d06ad06402fc522c8bf7eab73422d05e789b4e38fe3206a85e3d6966c11/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:14d96584701a887763384f3c47f0ca7c1cce322aa1c31172680eb596b890ec30", size = 4140942, upload-time = "2025-07-02T13:06:00.137Z" }, + { url = "https://files.pythonhosted.org/packages/65/79/020a5413347e44c382ef1f7f7e7a66817cd6273e3e6b5a72d18177b08b2f/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:57c816dfbd1659a367831baca4b775b2a5b43c003daf52e9d57e1d30bc2e1b0e", size = 4374079, upload-time = "2025-07-02T13:06:02.043Z" }, + { url = "https://files.pythonhosted.org/packages/f0/63/83516cfb87f4a8756eaa4203f93b283fda23d210fc14e1e594bd5f20edb6/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bd4c45986472694e5121084c6ebbd112aa919a25e783b87eb95953c9573906d6", size = 4152447, upload-time = "2025-07-02T13:06:08.345Z" }, + { url = "https://files.pythonhosted.org/packages/22/11/d2823d2a5a0bd5802b3565437add16f5c8ce1f0778bf3822f89ad2740a38/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:982518cd64c54fcada9d7e5cf28eabd3ee76bd03ab18e08a48cad7e8b6f31b18", size = 4386778, upload-time = "2025-07-02T13:06:10.263Z" }, + { url = "https://files.pythonhosted.org/packages/5f/38/6bf177ca6bce4fe14704ab3e93627c5b0ca05242261a2e43ef3168472540/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:12e55281d993a793b0e883066f590c1ae1e802e3acb67f8b442e721e475e6463", size = 4151627, upload-time = "2025-07-02T13:06:13.097Z" }, + { url = "https://files.pythonhosted.org/packages/38/6a/69fc67e5266bff68a91bcb81dff8fb0aba4d79a78521a08812048913e16f/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:5aa1e32983d4443e310f726ee4b071ab7569f58eedfdd65e9675484a4eb67bd1", size = 4385593, upload-time = "2025-07-02T13:06:15.689Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, +] + +[[package]] +name = "furo" +version = "2025.7.19" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accessible-pygments" }, + { name = "beautifulsoup4" }, + { name = "pygments" }, + { name = "sphinx" }, + { name = "sphinx-basic-ng" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/69/312cd100fa45ddaea5a588334d2defa331ff427bcb61f5fe2ae61bdc3762/furo-2025.7.19.tar.gz", hash = "sha256:4164b2cafcf4023a59bb3c594e935e2516f6b9d35e9a5ea83d8f6b43808fe91f", size = 1662054, upload-time = "2025-07-19T10:52:09.754Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/34/2b07b72bee02a63241d654f5d8af87a2de977c59638eec41ca356ab915cd/furo-2025.7.19-py3-none-any.whl", hash = "sha256:bdea869822dfd2b494ea84c0973937e35d1575af088b6721a29c7f7878adc9e3", size = 342175, upload-time = "2025-07-19T10:52:02.399Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "h2" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682, upload-time = "2025-02-02T07:43:51.815Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957, upload-time = "2025-02-01T11:02:26.481Z" }, +] + +[[package]] +name = "hatchling" +version = "1.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pathspec" }, + { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "trove-classifiers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", hash = "sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6", size = 54983, upload-time = "2024-12-15T17:08:11.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", hash = "sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", size = 75794, upload-time = "2024-12-15T17:08:10.364Z" }, +] + +[[package]] +name = "hpack" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[package.optional-dependencies] +http2 = [ + { name = "h2" }, +] + +[[package]] +name = "hydrogram" +source = { editable = "." } +dependencies = [ + { name = "aiosqlite" }, + { name = "pyaes" }, + { name = "pysocks" }, +] + +[package.optional-dependencies] +docs = [ + { name = "furo" }, + { name = "pygments" }, + { name = "sphinx" }, + { name = "sphinx-autobuild" }, + { name = "sphinx-copybutton" }, + { name = "sphinxcontrib-towncrier" }, + { name = "towncrier" }, +] +fast = [ + { name = "tgcrypto" }, + { name = "uvloop", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux')" }, +] + +[package.dev-dependencies] +dev = [ + { name = "hatchling" }, + { name = "httpx", extra = ["http2"] }, + { name = "lxml" }, + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "ruff" }, + { name = "twine" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiosqlite", specifier = ">=0.19.0" }, + { name = "furo", marker = "extra == 'docs'", specifier = ">=2024.8.6" }, + { name = "pyaes", specifier = "==1.6.1" }, + { name = "pygments", marker = "extra == 'docs'", specifier = ">=2.19.1" }, + { name = "pysocks", specifier = "==1.7.1" }, + { name = "sphinx", marker = "extra == 'docs'", specifier = "<8.0.0" }, + { name = "sphinx-autobuild", marker = "extra == 'docs'", specifier = ">=2024.10.3" }, + { name = "sphinx-copybutton", marker = "extra == 'docs'", specifier = ">=0.5.2" }, + { name = "sphinxcontrib-towncrier", marker = "extra == 'docs'", specifier = ">=0.5.0a0" }, + { name = "tgcrypto", marker = "extra == 'fast'", specifier = ">=1.2.5" }, + { name = "towncrier", marker = "extra == 'docs'", specifier = ">=24.8.0" }, + { name = "uvloop", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin' and extra == 'fast') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux' and extra == 'fast')", specifier = ">=0.19.0" }, +] +provides-extras = ["docs", "fast"] + +[package.metadata.requires-dev] +dev = [ + { name = "hatchling", specifier = ">=1.27.0" }, + { name = "httpx", extras = ["http2"], specifier = ">=0.28.1" }, + { name = "lxml", specifier = ">=5.3.2" }, + { name = "pre-commit", specifier = ">=4.2.0" }, + { name = "pytest", specifier = ">=7.4.3" }, + { name = "pytest-asyncio", specifier = ">=0.23.2" }, + { name = "pytest-cov", specifier = ">=4.1.0" }, + { name = "ruff", specifier = ">=0.11.7" }, + { name = "twine", specifier = ">=4.0.2" }, +] + +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, +] + +[[package]] +name = "id" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/11/102da08f88412d875fa2f1a9a469ff7ad4c874b0ca6fed0048fe385bdb3d/id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", size = 15237, upload-time = "2024-12-04T19:53:05.575Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658", size = 13611, upload-time = "2024-12-04T19:53:03.02Z" }, +] + +[[package]] +name = "identify" +version = "2.6.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, +] + +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, +] + +[[package]] +name = "jaraco-context" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912, upload-time = "2024-08-20T03:39:27.358Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825, upload-time = "2024-08-20T03:39:25.966Z" }, +] + +[[package]] +name = "jaraco-functools" +version = "4.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/1c/831faaaa0f090b711c355c6d8b2abf277c72133aab472b6932b03322294c/jaraco_functools-4.2.1.tar.gz", hash = "sha256:be634abfccabce56fa3053f8c7ebe37b682683a4ee7793670ced17bab0087353", size = 19661, upload-time = "2025-06-21T19:22:03.201Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/fd/179a20f832824514df39a90bb0e5372b314fea99f217f5ab942b10a8a4e8/jaraco_functools-4.2.1-py3-none-any.whl", hash = "sha256:590486285803805f4b1f99c60ca9e94ed348d4added84b74c7a12885561e524e", size = 10349, upload-time = "2025-06-21T19:22:02.039Z" }, +] + +[[package]] +name = "jeepney" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "keyring" +version = "25.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", size = 62750, upload-time = "2024-12-25T15:26:45.782Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd", size = 39085, upload-time = "2024-12-25T15:26:44.377Z" }, +] + +[[package]] +name = "lxml" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/ed/60eb6fa2923602fba988d9ca7c5cdbd7cf25faa795162ed538b527a35411/lxml-6.0.0.tar.gz", hash = "sha256:032e65120339d44cdc3efc326c9f660f5f7205f3a535c1fdbf898b29ea01fb72", size = 4096938, upload-time = "2025-06-26T16:28:19.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/e9/9c3ca02fbbb7585116c2e274b354a2d92b5c70561687dd733ec7b2018490/lxml-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:35bc626eec405f745199200ccb5c6b36f202675d204aa29bb52e27ba2b71dea8", size = 8399057, upload-time = "2025-06-26T16:25:02.169Z" }, + { url = "https://files.pythonhosted.org/packages/86/25/10a6e9001191854bf283515020f3633b1b1f96fd1b39aa30bf8fff7aa666/lxml-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:246b40f8a4aec341cbbf52617cad8ab7c888d944bfe12a6abd2b1f6cfb6f6082", size = 4569676, upload-time = "2025-06-26T16:25:05.431Z" }, + { url = "https://files.pythonhosted.org/packages/f5/a5/378033415ff61d9175c81de23e7ad20a3ffb614df4ffc2ffc86bc6746ffd/lxml-6.0.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:2793a627e95d119e9f1e19720730472f5543a6d84c50ea33313ce328d870f2dd", size = 5291361, upload-time = "2025-06-26T16:25:07.901Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a6/19c87c4f3b9362b08dc5452a3c3bce528130ac9105fc8fff97ce895ce62e/lxml-6.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:46b9ed911f36bfeb6338e0b482e7fe7c27d362c52fde29f221fddbc9ee2227e7", size = 5008290, upload-time = "2025-06-28T18:47:13.196Z" }, + { url = "https://files.pythonhosted.org/packages/09/d1/e9b7ad4b4164d359c4d87ed8c49cb69b443225cb495777e75be0478da5d5/lxml-6.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b4790b558bee331a933e08883c423f65bbcd07e278f91b2272489e31ab1e2b4", size = 5163192, upload-time = "2025-06-28T18:47:17.279Z" }, + { url = "https://files.pythonhosted.org/packages/56/d6/b3eba234dc1584744b0b374a7f6c26ceee5dc2147369a7e7526e25a72332/lxml-6.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2030956cf4886b10be9a0285c6802e078ec2391e1dd7ff3eb509c2c95a69b76", size = 5076973, upload-time = "2025-06-26T16:25:10.936Z" }, + { url = "https://files.pythonhosted.org/packages/8e/47/897142dd9385dcc1925acec0c4afe14cc16d310ce02c41fcd9010ac5d15d/lxml-6.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d23854ecf381ab1facc8f353dcd9adeddef3652268ee75297c1164c987c11dc", size = 5297795, upload-time = "2025-06-26T16:25:14.282Z" }, + { url = "https://files.pythonhosted.org/packages/fb/db/551ad84515c6f415cea70193a0ff11d70210174dc0563219f4ce711655c6/lxml-6.0.0-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:43fe5af2d590bf4691531b1d9a2495d7aab2090547eaacd224a3afec95706d76", size = 4776547, upload-time = "2025-06-26T16:25:17.123Z" }, + { url = "https://files.pythonhosted.org/packages/e0/14/c4a77ab4f89aaf35037a03c472f1ccc54147191888626079bd05babd6808/lxml-6.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74e748012f8c19b47f7d6321ac929a9a94ee92ef12bc4298c47e8b7219b26541", size = 5124904, upload-time = "2025-06-26T16:25:19.485Z" }, + { url = "https://files.pythonhosted.org/packages/70/b4/12ae6a51b8da106adec6a2e9c60f532350a24ce954622367f39269e509b1/lxml-6.0.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:43cfbb7db02b30ad3926e8fceaef260ba2fb7df787e38fa2df890c1ca7966c3b", size = 4805804, upload-time = "2025-06-26T16:25:21.949Z" }, + { url = "https://files.pythonhosted.org/packages/a9/b6/2e82d34d49f6219cdcb6e3e03837ca5fb8b7f86c2f35106fb8610ac7f5b8/lxml-6.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34190a1ec4f1e84af256495436b2d196529c3f2094f0af80202947567fdbf2e7", size = 5323477, upload-time = "2025-06-26T16:25:24.475Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e6/b83ddc903b05cd08a5723fefd528eee84b0edd07bdf87f6c53a1fda841fd/lxml-6.0.0-cp310-cp310-win32.whl", hash = "sha256:5967fe415b1920a3877a4195e9a2b779249630ee49ece22021c690320ff07452", size = 3613840, upload-time = "2025-06-26T16:25:27.345Z" }, + { url = "https://files.pythonhosted.org/packages/40/af/874fb368dd0c663c030acb92612341005e52e281a102b72a4c96f42942e1/lxml-6.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:f3389924581d9a770c6caa4df4e74b606180869043b9073e2cec324bad6e306e", size = 3993584, upload-time = "2025-06-26T16:25:29.391Z" }, + { url = "https://files.pythonhosted.org/packages/4a/f4/d296bc22c17d5607653008f6dd7b46afdfda12efd31021705b507df652bb/lxml-6.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:522fe7abb41309e9543b0d9b8b434f2b630c5fdaf6482bee642b34c8c70079c8", size = 3681400, upload-time = "2025-06-26T16:25:31.421Z" }, + { url = "https://files.pythonhosted.org/packages/7c/23/828d4cc7da96c611ec0ce6147bbcea2fdbde023dc995a165afa512399bbf/lxml-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ee56288d0df919e4aac43b539dd0e34bb55d6a12a6562038e8d6f3ed07f9e36", size = 8438217, upload-time = "2025-06-26T16:25:34.349Z" }, + { url = "https://files.pythonhosted.org/packages/f1/33/5ac521212c5bcb097d573145d54b2b4a3c9766cda88af5a0e91f66037c6e/lxml-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8dd6dd0e9c1992613ccda2bcb74fc9d49159dbe0f0ca4753f37527749885c25", size = 4590317, upload-time = "2025-06-26T16:25:38.103Z" }, + { url = "https://files.pythonhosted.org/packages/2b/2e/45b7ca8bee304c07f54933c37afe7dd4d39ff61ba2757f519dcc71bc5d44/lxml-6.0.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d7ae472f74afcc47320238b5dbfd363aba111a525943c8a34a1b657c6be934c3", size = 5221628, upload-time = "2025-06-26T16:25:40.878Z" }, + { url = "https://files.pythonhosted.org/packages/32/23/526d19f7eb2b85da1f62cffb2556f647b049ebe2a5aa8d4d41b1fb2c7d36/lxml-6.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5592401cdf3dc682194727c1ddaa8aa0f3ddc57ca64fd03226a430b955eab6f6", size = 4949429, upload-time = "2025-06-28T18:47:20.046Z" }, + { url = "https://files.pythonhosted.org/packages/ac/cc/f6be27a5c656a43a5344e064d9ae004d4dcb1d3c9d4f323c8189ddfe4d13/lxml-6.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58ffd35bd5425c3c3b9692d078bf7ab851441434531a7e517c4984d5634cd65b", size = 5087909, upload-time = "2025-06-28T18:47:22.834Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e6/8ec91b5bfbe6972458bc105aeb42088e50e4b23777170404aab5dfb0c62d/lxml-6.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f720a14aa102a38907c6d5030e3d66b3b680c3e6f6bc95473931ea3c00c59967", size = 5031713, upload-time = "2025-06-26T16:25:43.226Z" }, + { url = "https://files.pythonhosted.org/packages/33/cf/05e78e613840a40e5be3e40d892c48ad3e475804db23d4bad751b8cadb9b/lxml-6.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a5e8d207311a0170aca0eb6b160af91adc29ec121832e4ac151a57743a1e1e", size = 5232417, upload-time = "2025-06-26T16:25:46.111Z" }, + { url = "https://files.pythonhosted.org/packages/ac/8c/6b306b3e35c59d5f0b32e3b9b6b3b0739b32c0dc42a295415ba111e76495/lxml-6.0.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:2dd1cc3ea7e60bfb31ff32cafe07e24839df573a5e7c2d33304082a5019bcd58", size = 4681443, upload-time = "2025-06-26T16:25:48.837Z" }, + { url = "https://files.pythonhosted.org/packages/59/43/0bd96bece5f7eea14b7220476835a60d2b27f8e9ca99c175f37c085cb154/lxml-6.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cfcf84f1defed7e5798ef4f88aa25fcc52d279be731ce904789aa7ccfb7e8d2", size = 5074542, upload-time = "2025-06-26T16:25:51.65Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3d/32103036287a8ca012d8518071f8852c68f2b3bfe048cef2a0202eb05910/lxml-6.0.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a52a4704811e2623b0324a18d41ad4b9fabf43ce5ff99b14e40a520e2190c851", size = 4729471, upload-time = "2025-06-26T16:25:54.571Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a8/7be5d17df12d637d81854bd8648cd329f29640a61e9a72a3f77add4a311b/lxml-6.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c16304bba98f48a28ae10e32a8e75c349dd742c45156f297e16eeb1ba9287a1f", size = 5256285, upload-time = "2025-06-26T16:25:56.997Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d0/6cb96174c25e0d749932557c8d51d60c6e292c877b46fae616afa23ed31a/lxml-6.0.0-cp311-cp311-win32.whl", hash = "sha256:f8d19565ae3eb956d84da3ef367aa7def14a2735d05bd275cd54c0301f0d0d6c", size = 3612004, upload-time = "2025-06-26T16:25:59.11Z" }, + { url = "https://files.pythonhosted.org/packages/ca/77/6ad43b165dfc6dead001410adeb45e88597b25185f4479b7ca3b16a5808f/lxml-6.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:b2d71cdefda9424adff9a3607ba5bbfc60ee972d73c21c7e3c19e71037574816", size = 4003470, upload-time = "2025-06-26T16:26:01.655Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bc/4c50ec0eb14f932a18efc34fc86ee936a66c0eb5f2fe065744a2da8a68b2/lxml-6.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:8a2e76efbf8772add72d002d67a4c3d0958638696f541734304c7f28217a9cab", size = 3682477, upload-time = "2025-06-26T16:26:03.808Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/d01d735c298d7e0ddcedf6f028bf556577e5ab4f4da45175ecd909c79378/lxml-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78718d8454a6e928470d511bf8ac93f469283a45c354995f7d19e77292f26108", size = 8429515, upload-time = "2025-06-26T16:26:06.776Z" }, + { url = "https://files.pythonhosted.org/packages/06/37/0e3eae3043d366b73da55a86274a590bae76dc45aa004b7042e6f97803b1/lxml-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:84ef591495ffd3f9dcabffd6391db7bb70d7230b5c35ef5148354a134f56f2be", size = 4601387, upload-time = "2025-06-26T16:26:09.511Z" }, + { url = "https://files.pythonhosted.org/packages/a3/28/e1a9a881e6d6e29dda13d633885d13acb0058f65e95da67841c8dd02b4a8/lxml-6.0.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:2930aa001a3776c3e2601cb8e0a15d21b8270528d89cc308be4843ade546b9ab", size = 5228928, upload-time = "2025-06-26T16:26:12.337Z" }, + { url = "https://files.pythonhosted.org/packages/9a/55/2cb24ea48aa30c99f805921c1c7860c1f45c0e811e44ee4e6a155668de06/lxml-6.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:219e0431ea8006e15005767f0351e3f7f9143e793e58519dc97fe9e07fae5563", size = 4952289, upload-time = "2025-06-28T18:47:25.602Z" }, + { url = "https://files.pythonhosted.org/packages/31/c0/b25d9528df296b9a3306ba21ff982fc5b698c45ab78b94d18c2d6ae71fd9/lxml-6.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bd5913b4972681ffc9718bc2d4c53cde39ef81415e1671ff93e9aa30b46595e7", size = 5111310, upload-time = "2025-06-28T18:47:28.136Z" }, + { url = "https://files.pythonhosted.org/packages/e9/af/681a8b3e4f668bea6e6514cbcb297beb6de2b641e70f09d3d78655f4f44c/lxml-6.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:390240baeb9f415a82eefc2e13285016f9c8b5ad71ec80574ae8fa9605093cd7", size = 5025457, upload-time = "2025-06-26T16:26:15.068Z" }, + { url = "https://files.pythonhosted.org/packages/99/b6/3a7971aa05b7be7dfebc7ab57262ec527775c2c3c5b2f43675cac0458cad/lxml-6.0.0-cp312-cp312-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d6e200909a119626744dd81bae409fc44134389e03fbf1d68ed2a55a2fb10991", size = 5657016, upload-time = "2025-07-03T19:19:06.008Z" }, + { url = "https://files.pythonhosted.org/packages/69/f8/693b1a10a891197143c0673fcce5b75fc69132afa81a36e4568c12c8faba/lxml-6.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ca50bd612438258a91b5b3788c6621c1f05c8c478e7951899f492be42defc0da", size = 5257565, upload-time = "2025-06-26T16:26:17.906Z" }, + { url = "https://files.pythonhosted.org/packages/a8/96/e08ff98f2c6426c98c8964513c5dab8d6eb81dadcd0af6f0c538ada78d33/lxml-6.0.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:c24b8efd9c0f62bad0439283c2c795ef916c5a6b75f03c17799775c7ae3c0c9e", size = 4713390, upload-time = "2025-06-26T16:26:20.292Z" }, + { url = "https://files.pythonhosted.org/packages/a8/83/6184aba6cc94d7413959f6f8f54807dc318fdcd4985c347fe3ea6937f772/lxml-6.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:afd27d8629ae94c5d863e32ab0e1d5590371d296b87dae0a751fb22bf3685741", size = 5066103, upload-time = "2025-06-26T16:26:22.765Z" }, + { url = "https://files.pythonhosted.org/packages/ee/01/8bf1f4035852d0ff2e36a4d9aacdbcc57e93a6cd35a54e05fa984cdf73ab/lxml-6.0.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:54c4855eabd9fc29707d30141be99e5cd1102e7d2258d2892314cf4c110726c3", size = 4791428, upload-time = "2025-06-26T16:26:26.461Z" }, + { url = "https://files.pythonhosted.org/packages/29/31/c0267d03b16954a85ed6b065116b621d37f559553d9339c7dcc4943a76f1/lxml-6.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c907516d49f77f6cd8ead1322198bdfd902003c3c330c77a1c5f3cc32a0e4d16", size = 5678523, upload-time = "2025-07-03T19:19:09.837Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f7/5495829a864bc5f8b0798d2b52a807c89966523140f3d6fa3a58ab6720ea/lxml-6.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36531f81c8214e293097cd2b7873f178997dae33d3667caaae8bdfb9666b76c0", size = 5281290, upload-time = "2025-06-26T16:26:29.406Z" }, + { url = "https://files.pythonhosted.org/packages/79/56/6b8edb79d9ed294ccc4e881f4db1023af56ba451909b9ce79f2a2cd7c532/lxml-6.0.0-cp312-cp312-win32.whl", hash = "sha256:690b20e3388a7ec98e899fd54c924e50ba6693874aa65ef9cb53de7f7de9d64a", size = 3613495, upload-time = "2025-06-26T16:26:31.588Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1e/cc32034b40ad6af80b6fd9b66301fc0f180f300002e5c3eb5a6110a93317/lxml-6.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:310b719b695b3dd442cdfbbe64936b2f2e231bb91d998e99e6f0daf991a3eba3", size = 4014711, upload-time = "2025-06-26T16:26:33.723Z" }, + { url = "https://files.pythonhosted.org/packages/55/10/dc8e5290ae4c94bdc1a4c55865be7e1f31dfd857a88b21cbba68b5fea61b/lxml-6.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:8cb26f51c82d77483cdcd2b4a53cda55bbee29b3c2f3ddeb47182a2a9064e4eb", size = 3674431, upload-time = "2025-06-26T16:26:35.959Z" }, + { url = "https://files.pythonhosted.org/packages/79/21/6e7c060822a3c954ff085e5e1b94b4a25757c06529eac91e550f3f5cd8b8/lxml-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6da7cd4f405fd7db56e51e96bff0865b9853ae70df0e6720624049da76bde2da", size = 8414372, upload-time = "2025-06-26T16:26:39.079Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f6/051b1607a459db670fc3a244fa4f06f101a8adf86cda263d1a56b3a4f9d5/lxml-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b34339898bb556a2351a1830f88f751679f343eabf9cf05841c95b165152c9e7", size = 4593940, upload-time = "2025-06-26T16:26:41.891Z" }, + { url = "https://files.pythonhosted.org/packages/8e/74/dd595d92a40bda3c687d70d4487b2c7eff93fd63b568acd64fedd2ba00fe/lxml-6.0.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:51a5e4c61a4541bd1cd3ba74766d0c9b6c12d6a1a4964ef60026832aac8e79b3", size = 5214329, upload-time = "2025-06-26T16:26:44.669Z" }, + { url = "https://files.pythonhosted.org/packages/52/46/3572761efc1bd45fcafb44a63b3b0feeb5b3f0066886821e94b0254f9253/lxml-6.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d18a25b19ca7307045581b18b3ec9ead2b1db5ccd8719c291f0cd0a5cec6cb81", size = 4947559, upload-time = "2025-06-28T18:47:31.091Z" }, + { url = "https://files.pythonhosted.org/packages/94/8a/5e40de920e67c4f2eef9151097deb9b52d86c95762d8ee238134aff2125d/lxml-6.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d4f0c66df4386b75d2ab1e20a489f30dc7fd9a06a896d64980541506086be1f1", size = 5102143, upload-time = "2025-06-28T18:47:33.612Z" }, + { url = "https://files.pythonhosted.org/packages/7c/4b/20555bdd75d57945bdabfbc45fdb1a36a1a0ff9eae4653e951b2b79c9209/lxml-6.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f4b481b6cc3a897adb4279216695150bbe7a44c03daba3c894f49d2037e0a24", size = 5021931, upload-time = "2025-06-26T16:26:47.503Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/cf03b412f3763d4ca23b25e70c96a74cfece64cec3addf1c4ec639586b13/lxml-6.0.0-cp313-cp313-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a78d6c9168f5bcb20971bf3329c2b83078611fbe1f807baadc64afc70523b3a", size = 5645469, upload-time = "2025-07-03T19:19:13.32Z" }, + { url = "https://files.pythonhosted.org/packages/d4/dd/39c8507c16db6031f8c1ddf70ed95dbb0a6d466a40002a3522c128aba472/lxml-6.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae06fbab4f1bb7db4f7c8ca9897dc8db4447d1a2b9bee78474ad403437bcc29", size = 5247467, upload-time = "2025-06-26T16:26:49.998Z" }, + { url = "https://files.pythonhosted.org/packages/4d/56/732d49def0631ad633844cfb2664563c830173a98d5efd9b172e89a4800d/lxml-6.0.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:1fa377b827ca2023244a06554c6e7dc6828a10aaf74ca41965c5d8a4925aebb4", size = 4720601, upload-time = "2025-06-26T16:26:52.564Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7f/6b956fab95fa73462bca25d1ea7fc8274ddf68fb8e60b78d56c03b65278e/lxml-6.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1676b56d48048a62ef77a250428d1f31f610763636e0784ba67a9740823988ca", size = 5060227, upload-time = "2025-06-26T16:26:55.054Z" }, + { url = "https://files.pythonhosted.org/packages/97/06/e851ac2924447e8b15a294855caf3d543424364a143c001014d22c8ca94c/lxml-6.0.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:0e32698462aacc5c1cf6bdfebc9c781821b7e74c79f13e5ffc8bfe27c42b1abf", size = 4790637, upload-time = "2025-06-26T16:26:57.384Z" }, + { url = "https://files.pythonhosted.org/packages/06/d4/fd216f3cd6625022c25b336c7570d11f4a43adbaf0a56106d3d496f727a7/lxml-6.0.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4d6036c3a296707357efb375cfc24bb64cd955b9ec731abf11ebb1e40063949f", size = 5662049, upload-time = "2025-07-03T19:19:16.409Z" }, + { url = "https://files.pythonhosted.org/packages/52/03/0e764ce00b95e008d76b99d432f1807f3574fb2945b496a17807a1645dbd/lxml-6.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7488a43033c958637b1a08cddc9188eb06d3ad36582cebc7d4815980b47e27ef", size = 5272430, upload-time = "2025-06-26T16:27:00.031Z" }, + { url = "https://files.pythonhosted.org/packages/5f/01/d48cc141bc47bc1644d20fe97bbd5e8afb30415ec94f146f2f76d0d9d098/lxml-6.0.0-cp313-cp313-win32.whl", hash = "sha256:5fcd7d3b1d8ecb91445bd71b9c88bdbeae528fefee4f379895becfc72298d181", size = 3612896, upload-time = "2025-06-26T16:27:04.251Z" }, + { url = "https://files.pythonhosted.org/packages/f4/87/6456b9541d186ee7d4cb53bf1b9a0d7f3b1068532676940fdd594ac90865/lxml-6.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:2f34687222b78fff795feeb799a7d44eca2477c3d9d3a46ce17d51a4f383e32e", size = 4013132, upload-time = "2025-06-26T16:27:06.415Z" }, + { url = "https://files.pythonhosted.org/packages/b7/42/85b3aa8f06ca0d24962f8100f001828e1f1f1a38c954c16e71154ed7d53a/lxml-6.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:21db1ec5525780fd07251636eb5f7acb84003e9382c72c18c542a87c416ade03", size = 3672642, upload-time = "2025-06-26T16:27:09.888Z" }, + { url = "https://files.pythonhosted.org/packages/dc/04/a53941fb0d7c60eed08301942c70aa63650a59308d15e05eb823acbce41d/lxml-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85b14a4689d5cff426c12eefe750738648706ea2753b20c2f973b2a000d3d261", size = 8407699, upload-time = "2025-06-26T16:27:28.167Z" }, + { url = "https://files.pythonhosted.org/packages/44/d2/e1d4526e903afebe147f858322f1c0b36e44969d5c87e5d243c23f81987f/lxml-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f64ccf593916e93b8d36ed55401bb7fe9c7d5de3180ce2e10b08f82a8f397316", size = 4574678, upload-time = "2025-06-26T16:27:30.888Z" }, + { url = "https://files.pythonhosted.org/packages/61/aa/b0a8ee233c00f2f437dbb6e7bd2df115a996d8211b7d03f4ab029b8e3378/lxml-6.0.0-cp39-cp39-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:b372d10d17a701b0945f67be58fae4664fd056b85e0ff0fbc1e6c951cdbc0512", size = 5292694, upload-time = "2025-06-26T16:27:34.037Z" }, + { url = "https://files.pythonhosted.org/packages/53/7f/e6f377489b2ac4289418b879c34ed664e5a1174b2a91590936ec4174e773/lxml-6.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a674c0948789e9136d69065cc28009c1b1874c6ea340253db58be7622ce6398f", size = 5009177, upload-time = "2025-06-28T18:47:39.377Z" }, + { url = "https://files.pythonhosted.org/packages/c6/05/ae239e997374680741b768044545251a29abc21ada42248638dbed749a0a/lxml-6.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:edf6e4c8fe14dfe316939711e3ece3f9a20760aabf686051b537a7562f4da91a", size = 5163787, upload-time = "2025-06-28T18:47:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/2a/da/4f27222570d008fd2386e19d6923af6e64c317ee6116bbb2b98247f98f31/lxml-6.0.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:048a930eb4572829604982e39a0c7289ab5dc8abc7fc9f5aabd6fbc08c154e93", size = 5075755, upload-time = "2025-06-26T16:27:36.611Z" }, + { url = "https://files.pythonhosted.org/packages/1f/65/12552caf7b3e3b9b9aba12349370dc53a36d4058e4ed482811f1d262deee/lxml-6.0.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c0b5fa5eda84057a4f1bbb4bb77a8c28ff20ae7ce211588d698ae453e13c6281", size = 5297070, upload-time = "2025-06-26T16:27:39.232Z" }, + { url = "https://files.pythonhosted.org/packages/3e/6a/f053a8369fdf4e3b8127a6ffb079c519167e684e956a1281392c5c3679b6/lxml-6.0.0-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:c352fc8f36f7e9727db17adbf93f82499457b3d7e5511368569b4c5bd155a922", size = 4779864, upload-time = "2025-06-26T16:27:41.713Z" }, + { url = "https://files.pythonhosted.org/packages/df/7b/b2a392ad34ce37a17d1cf3aec303e15125768061cf0e355a92d292d20d37/lxml-6.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8db5dc617cb937ae17ff3403c3a70a7de9df4852a046f93e71edaec678f721d0", size = 5122039, upload-time = "2025-06-26T16:27:44.252Z" }, + { url = "https://files.pythonhosted.org/packages/80/0e/6459ff8ae7d87188e1f99f11691d0f32831caa6429599c3b289de9f08b21/lxml-6.0.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:2181e4b1d07dde53986023482673c0f1fba5178ef800f9ab95ad791e8bdded6a", size = 4805117, upload-time = "2025-06-26T16:27:46.769Z" }, + { url = "https://files.pythonhosted.org/packages/ca/78/4186f573805ff623d28a8736788a3b29eeaf589afdcf0233de2c9bb9fc50/lxml-6.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b3c98d5b24c6095e89e03d65d5c574705be3d49c0d8ca10c17a8a4b5201b72f5", size = 5322300, upload-time = "2025-06-26T16:27:49.278Z" }, + { url = "https://files.pythonhosted.org/packages/e8/97/352e07992901473529c8e19dbfdba6430ba6a37f6b46a4d0fa93321f8fee/lxml-6.0.0-cp39-cp39-win32.whl", hash = "sha256:04d67ceee6db4bcb92987ccb16e53bef6b42ced872509f333c04fb58a3315256", size = 3615832, upload-time = "2025-06-26T16:27:51.728Z" }, + { url = "https://files.pythonhosted.org/packages/71/93/8f3b880e2618e548fb0ca157349abb526d81cb4f01ef5ea3a0f22bd4d0df/lxml-6.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:e0b1520ef900e9ef62e392dd3d7ae4f5fa224d1dd62897a792cf353eb20b6cae", size = 4038551, upload-time = "2025-06-26T16:27:54.193Z" }, + { url = "https://files.pythonhosted.org/packages/e7/8a/046cbf5b262dd2858c6e65833339100fd5f1c017b37b26bc47c92d4584d7/lxml-6.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:e35e8aaaf3981489f42884b59726693de32dabfc438ac10ef4eb3409961fd402", size = 3684237, upload-time = "2025-06-26T16:27:57.117Z" }, + { url = "https://files.pythonhosted.org/packages/66/e1/2c22a3cff9e16e1d717014a1e6ec2bf671bf56ea8716bb64466fcf820247/lxml-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:dbdd7679a6f4f08152818043dbb39491d1af3332128b3752c3ec5cebc0011a72", size = 3898804, upload-time = "2025-06-26T16:27:59.751Z" }, + { url = "https://files.pythonhosted.org/packages/2b/3a/d68cbcb4393a2a0a867528741fafb7ce92dac5c9f4a1680df98e5e53e8f5/lxml-6.0.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:40442e2a4456e9910875ac12951476d36c0870dcb38a68719f8c4686609897c4", size = 4216406, upload-time = "2025-06-28T18:47:45.518Z" }, + { url = "https://files.pythonhosted.org/packages/15/8f/d9bfb13dff715ee3b2a1ec2f4a021347ea3caf9aba93dea0cfe54c01969b/lxml-6.0.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db0efd6bae1c4730b9c863fc4f5f3c0fa3e8f05cae2c44ae141cb9dfc7d091dc", size = 4326455, upload-time = "2025-06-28T18:47:48.411Z" }, + { url = "https://files.pythonhosted.org/packages/01/8b/fde194529ee8a27e6f5966d7eef05fa16f0567e4a8e8abc3b855ef6b3400/lxml-6.0.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ab542c91f5a47aaa58abdd8ea84b498e8e49fe4b883d67800017757a3eb78e8", size = 4268788, upload-time = "2025-06-26T16:28:02.776Z" }, + { url = "https://files.pythonhosted.org/packages/99/a8/3b8e2581b4f8370fc9e8dc343af4abdfadd9b9229970fc71e67bd31c7df1/lxml-6.0.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:013090383863b72c62a702d07678b658fa2567aa58d373d963cca245b017e065", size = 4411394, upload-time = "2025-06-26T16:28:05.179Z" }, + { url = "https://files.pythonhosted.org/packages/e7/a5/899a4719e02ff4383f3f96e5d1878f882f734377f10dfb69e73b5f223e44/lxml-6.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c86df1c9af35d903d2b52d22ea3e66db8058d21dc0f59842ca5deb0595921141", size = 3517946, upload-time = "2025-06-26T16:28:07.665Z" }, + { url = "https://files.pythonhosted.org/packages/93/e3/ef14f1d23aea1dec1eccbe2c07a93b6d0be693fd9d5f248a47155e436701/lxml-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4337e4aec93b7c011f7ee2e357b0d30562edd1955620fdd4aeab6aacd90d43c5", size = 3892325, upload-time = "2025-06-26T16:28:10.024Z" }, + { url = "https://files.pythonhosted.org/packages/09/8a/1410b9e1ec43f606f9aac0661d09892509d86032e229711798906e1b5e7a/lxml-6.0.0-pp39-pypy39_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ae74f7c762270196d2dda56f8dd7309411f08a4084ff2dfcc0b095a218df2e06", size = 4210839, upload-time = "2025-06-28T18:47:50.768Z" }, + { url = "https://files.pythonhosted.org/packages/79/cb/6696ce0d1712c5ae94b18bdf225086a5fb04b23938ac4d2011b323b3860b/lxml-6.0.0-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:059c4cbf3973a621b62ea3132934ae737da2c132a788e6cfb9b08d63a0ef73f9", size = 4321235, upload-time = "2025-06-28T18:47:53.338Z" }, + { url = "https://files.pythonhosted.org/packages/f3/98/04997f61d720cf320a0daee66b3096e3a3b57453e15549c14b87058c2acd/lxml-6.0.0-pp39-pypy39_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f090a9bc0ce8da51a5632092f98a7e7f84bca26f33d161a98b57f7fb0004ca", size = 4265071, upload-time = "2025-06-26T16:28:12.367Z" }, + { url = "https://files.pythonhosted.org/packages/e6/86/e5f6fa80154a5f5bf2c1e89d6265892299942edeb115081ca72afe7c7199/lxml-6.0.0-pp39-pypy39_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9da022c14baeec36edfcc8daf0e281e2f55b950249a455776f0d1adeeada4734", size = 4406816, upload-time = "2025-06-26T16:28:14.744Z" }, + { url = "https://files.pythonhosted.org/packages/18/a6/ae69e0e6f5fb6293eb8cbfbf8a259e37d71608bbae3658a768dd26b69f3e/lxml-6.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a55da151d0b0c6ab176b4e761670ac0e2667817a1e0dadd04a01d0561a219349", size = 3515499, upload-time = "2025-06-26T16:28:17.035Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344, upload-time = "2024-10-18T15:21:43.721Z" }, + { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389, upload-time = "2024-10-18T15:21:44.666Z" }, + { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607, upload-time = "2024-10-18T15:21:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728, upload-time = "2024-10-18T15:21:46.295Z" }, + { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826, upload-time = "2024-10-18T15:21:47.134Z" }, + { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843, upload-time = "2024-10-18T15:21:48.334Z" }, + { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219, upload-time = "2024-10-18T15:21:49.587Z" }, + { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946, upload-time = "2024-10-18T15:21:50.441Z" }, + { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063, upload-time = "2024-10-18T15:21:51.385Z" }, + { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506, upload-time = "2024-10-18T15:21:52.974Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "more-itertools" +version = "10.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/a0/834b0cebabbfc7e311f30b46c8188790a37f89fc8d756660346fe5abfd09/more_itertools-10.7.0.tar.gz", hash = "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3", size = 127671, upload-time = "2025-04-22T14:17:41.838Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/9f/7ba6f94fc1e9ac3d2b853fdff3035fb2fa5afbed898c4a72b8a020610594/more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e", size = 65278, upload-time = "2025-04-22T14:17:40.49Z" }, +] + +[[package]] +name = "nh3" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/a4/96cff0977357f60f06ec4368c4c7a7a26cccfe7c9fcd54f5378bf0428fd3/nh3-0.3.0.tar.gz", hash = "sha256:d8ba24cb31525492ea71b6aac11a4adac91d828aadeff7c4586541bf5dc34d2f", size = 19655, upload-time = "2025-07-17T14:43:37.05Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/11/340b7a551916a4b2b68c54799d710f86cf3838a4abaad8e74d35360343bb/nh3-0.3.0-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a537ece1bf513e5a88d8cff8a872e12fe8d0f42ef71dd15a5e7520fecd191bbb", size = 1427992, upload-time = "2025-07-17T14:43:06.848Z" }, + { url = "https://files.pythonhosted.org/packages/ad/7f/7c6b8358cf1222921747844ab0eef81129e9970b952fcb814df417159fb9/nh3-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c915060a2c8131bef6a29f78debc29ba40859b6dbe2362ef9e5fd44f11487c2", size = 798194, upload-time = "2025-07-17T14:43:08.263Z" }, + { url = "https://files.pythonhosted.org/packages/63/da/c5fd472b700ba37d2df630a9e0d8cc156033551ceb8b4c49cc8a5f606b68/nh3-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba0caa8aa184196daa6e574d997a33867d6d10234018012d35f86d46024a2a95", size = 837884, upload-time = "2025-07-17T14:43:09.233Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3c/cba7b26ccc0ef150c81646478aa32f9c9535234f54845603c838a1dc955c/nh3-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:80fe20171c6da69c7978ecba33b638e951b85fb92059259edd285ff108b82a6d", size = 996365, upload-time = "2025-07-17T14:43:10.243Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ba/59e204d90727c25b253856e456ea61265ca810cda8ee802c35f3fadaab00/nh3-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e90883f9f85288f423c77b3f5a6f4486375636f25f793165112679a7b6363b35", size = 1071042, upload-time = "2025-07-17T14:43:11.57Z" }, + { url = "https://files.pythonhosted.org/packages/10/71/2fb1834c10fab6d9291d62c95192ea2f4c7518bd32ad6c46aab5d095cb87/nh3-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0649464ac8eee018644aacbc103874ccbfac80e3035643c3acaab4287e36e7f5", size = 995737, upload-time = "2025-07-17T14:43:12.659Z" }, + { url = "https://files.pythonhosted.org/packages/33/c1/8f8ccc2492a000b6156dce68a43253fcff8b4ce70ab4216d08f90a2ac998/nh3-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1adeb1062a1c2974bc75b8d1ecb014c5fd4daf2df646bbe2831f7c23659793f9", size = 980552, upload-time = "2025-07-17T14:43:13.763Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d6/f1c6e091cbe8700401c736c2bc3980c46dca770a2cf6a3b48a175114058e/nh3-0.3.0-cp313-cp313t-win32.whl", hash = "sha256:7275fdffaab10cc5801bf026e3c089d8de40a997afc9e41b981f7ac48c5aa7d5", size = 593618, upload-time = "2025-07-17T14:43:15.098Z" }, + { url = "https://files.pythonhosted.org/packages/23/1e/80a8c517655dd40bb13363fc4d9e66b2f13245763faab1a20f1df67165a7/nh3-0.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:423201bbdf3164a9e09aa01e540adbb94c9962cc177d5b1cbb385f5e1e79216e", size = 598948, upload-time = "2025-07-17T14:43:16.064Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e0/af86d2a974c87a4ba7f19bc3b44a8eaa3da480de264138fec82fe17b340b/nh3-0.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:16f8670201f7e8e0e05ed1a590eb84bfa51b01a69dd5caf1d3ea57733de6a52f", size = 580479, upload-time = "2025-07-17T14:43:17.038Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e0/cf1543e798ba86d838952e8be4cb8d18e22999be2a24b112a671f1c04fd6/nh3-0.3.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ec6cfdd2e0399cb79ba4dcffb2332b94d9696c52272ff9d48a630c5dca5e325a", size = 1442218, upload-time = "2025-07-17T14:43:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/5c/86/a96b1453c107b815f9ab8fac5412407c33cc5c7580a4daf57aabeb41b774/nh3-0.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5e7185599f89b0e391e2f29cc12dc2e206167380cea49b33beda4891be2fe1", size = 823791, upload-time = "2025-07-17T14:43:19.721Z" }, + { url = "https://files.pythonhosted.org/packages/97/33/11e7273b663839626f714cb68f6eb49899da5a0d9b6bc47b41fe870259c2/nh3-0.3.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:389d93d59b8214d51c400fb5b07866c2a4f79e4e14b071ad66c92184fec3a392", size = 811143, upload-time = "2025-07-17T14:43:20.779Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1b/b15bd1ce201a1a610aeb44afd478d55ac018b4475920a3118ffd806e2483/nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e9e6a7e4d38f7e8dda9edd1433af5170c597336c1a74b4693c5cb75ab2b30f2a", size = 1064661, upload-time = "2025-07-17T14:43:21.839Z" }, + { url = "https://files.pythonhosted.org/packages/8f/14/079670fb2e848c4ba2476c5a7a2d1319826053f4f0368f61fca9bb4227ae/nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7852f038a054e0096dac12b8141191e02e93e0b4608c4b993ec7d4ffafea4e49", size = 997061, upload-time = "2025-07-17T14:43:23.179Z" }, + { url = "https://files.pythonhosted.org/packages/a3/e5/ac7fc565f5d8bce7f979d1afd68e8cb415020d62fa6507133281c7d49f91/nh3-0.3.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af5aa8127f62bbf03d68f67a956627b1bd0469703a35b3dad28d0c1195e6c7fb", size = 924761, upload-time = "2025-07-17T14:43:24.23Z" }, + { url = "https://files.pythonhosted.org/packages/39/2c/6394301428b2017a9d5644af25f487fa557d06bc8a491769accec7524d9a/nh3-0.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f416c35efee3e6a6c9ab7716d9e57aa0a49981be915963a82697952cba1353e1", size = 803959, upload-time = "2025-07-17T14:43:26.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/9a/344b9f9c4bd1c2413a397f38ee6a3d5db30f1a507d4976e046226f12b297/nh3-0.3.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:37d3003d98dedca6cd762bf88f2e70b67f05100f6b949ffe540e189cc06887f9", size = 844073, upload-time = "2025-07-17T14:43:27.375Z" }, + { url = "https://files.pythonhosted.org/packages/66/3f/cd37f76c8ca277b02a84aa20d7bd60fbac85b4e2cbdae77cb759b22de58b/nh3-0.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:634e34e6162e0408e14fb61d5e69dbaea32f59e847cfcfa41b66100a6b796f62", size = 1000680, upload-time = "2025-07-17T14:43:28.452Z" }, + { url = "https://files.pythonhosted.org/packages/ee/db/7aa11b44bae4e7474feb1201d8dee04fabe5651c7cb51409ebda94a4ed67/nh3-0.3.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:b0612ccf5de8a480cf08f047b08f9d3fecc12e63d2ee91769cb19d7290614c23", size = 1076613, upload-time = "2025-07-17T14:43:30.031Z" }, + { url = "https://files.pythonhosted.org/packages/97/03/03f79f7e5178eb1ad5083af84faff471e866801beb980cc72943a4397368/nh3-0.3.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c7a32a7f0d89f7d30cb8f4a84bdbd56d1eb88b78a2434534f62c71dac538c450", size = 1001418, upload-time = "2025-07-17T14:43:31.429Z" }, + { url = "https://files.pythonhosted.org/packages/ce/55/1974bcc16884a397ee699cebd3914e1f59be64ab305533347ca2d983756f/nh3-0.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3f1b4f8a264a0c86ea01da0d0c390fe295ea0bcacc52c2103aca286f6884f518", size = 986499, upload-time = "2025-07-17T14:43:32.459Z" }, + { url = "https://files.pythonhosted.org/packages/c9/50/76936ec021fe1f3270c03278b8af5f2079038116b5d0bfe8538ffe699d69/nh3-0.3.0-cp38-abi3-win32.whl", hash = "sha256:6d68fa277b4a3cf04e5c4b84dd0c6149ff7d56c12b3e3fab304c525b850f613d", size = 599000, upload-time = "2025-07-17T14:43:33.852Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ae/324b165d904dc1672eee5f5661c0a68d4bab5b59fbb07afb6d8d19a30b45/nh3-0.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:bae63772408fd63ad836ec569a7c8f444dd32863d0c67f6e0b25ebbd606afa95", size = 604530, upload-time = "2025-07-17T14:43:34.95Z" }, + { url = "https://files.pythonhosted.org/packages/5b/76/3165e84e5266d146d967a6cc784ff2fbf6ddd00985a55ec006b72bc39d5d/nh3-0.3.0-cp38-abi3-win_arm64.whl", hash = "sha256:d97d3efd61404af7e5721a0e74d81cdbfc6e5f97e11e731bb6d090e30a7b62b2", size = 585971, upload-time = "2025-07-17T14:43:35.936Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, +] + +[[package]] +name = "pyaes" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/66/2c17bae31c906613795711fc78045c285048168919ace2220daa372c7d72/pyaes-1.6.1.tar.gz", hash = "sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f", size = 28536, upload-time = "2017-09-20T21:17:54.23Z" } + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pysocks" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, +] + +[[package]] +name = "pytest-cov" +version = "6.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, +] + +[[package]] +name = "readme-renderer" +version = "44.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "nh3" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056, upload-time = "2024-07-08T15:00:57.805Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310, upload-time = "2024-07-08T15:00:56.577Z" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, +] + +[[package]] +name = "rfc3986" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" }, +] + +[[package]] +name = "rich" +version = "14.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441, upload-time = "2025-07-25T07:32:58.125Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368, upload-time = "2025-07-25T07:32:56.73Z" }, +] + +[[package]] +name = "ruff" +version = "0.12.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/cd/01015eb5034605fd98d829c5839ec2c6b4582b479707f7c1c2af861e8258/ruff-0.12.5.tar.gz", hash = "sha256:b209db6102b66f13625940b7f8c7d0f18e20039bb7f6101fbdac935c9612057e", size = 5170722, upload-time = "2025-07-24T13:26:37.456Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/de/ad2f68f0798ff15dd8c0bcc2889558970d9a685b3249565a937cd820ad34/ruff-0.12.5-py3-none-linux_armv6l.whl", hash = "sha256:1de2c887e9dec6cb31fcb9948299de5b2db38144e66403b9660c9548a67abd92", size = 11819133, upload-time = "2025-07-24T13:25:56.369Z" }, + { url = "https://files.pythonhosted.org/packages/f8/fc/c6b65cd0e7fbe60f17e7ad619dca796aa49fbca34bb9bea5f8faf1ec2643/ruff-0.12.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d1ab65e7d8152f519e7dea4de892317c9da7a108da1c56b6a3c1d5e7cf4c5e9a", size = 12501114, upload-time = "2025-07-24T13:25:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/c5/de/c6bec1dce5ead9f9e6a946ea15e8d698c35f19edc508289d70a577921b30/ruff-0.12.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:962775ed5b27c7aa3fdc0d8f4d4433deae7659ef99ea20f783d666e77338b8cf", size = 11716873, upload-time = "2025-07-24T13:26:01.496Z" }, + { url = "https://files.pythonhosted.org/packages/a1/16/cf372d2ebe91e4eb5b82a2275c3acfa879e0566a7ac94d331ea37b765ac8/ruff-0.12.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73b4cae449597e7195a49eb1cdca89fd9fbb16140c7579899e87f4c85bf82f73", size = 11958829, upload-time = "2025-07-24T13:26:03.721Z" }, + { url = "https://files.pythonhosted.org/packages/25/bf/cd07e8f6a3a6ec746c62556b4c4b79eeb9b0328b362bb8431b7b8afd3856/ruff-0.12.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b13489c3dc50de5e2d40110c0cce371e00186b880842e245186ca862bf9a1ac", size = 11626619, upload-time = "2025-07-24T13:26:06.118Z" }, + { url = "https://files.pythonhosted.org/packages/d8/c9/c2ccb3b8cbb5661ffda6925f81a13edbb786e623876141b04919d1128370/ruff-0.12.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1504fea81461cf4841778b3ef0a078757602a3b3ea4b008feb1308cb3f23e08", size = 13221894, upload-time = "2025-07-24T13:26:08.292Z" }, + { url = "https://files.pythonhosted.org/packages/6b/58/68a5be2c8e5590ecdad922b2bcd5583af19ba648f7648f95c51c3c1eca81/ruff-0.12.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c7da4129016ae26c32dfcbd5b671fe652b5ab7fc40095d80dcff78175e7eddd4", size = 14163909, upload-time = "2025-07-24T13:26:10.474Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d1/ef6b19622009ba8386fdb792c0743f709cf917b0b2f1400589cbe4739a33/ruff-0.12.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca972c80f7ebcfd8af75a0f18b17c42d9f1ef203d163669150453f50ca98ab7b", size = 13583652, upload-time = "2025-07-24T13:26:13.381Z" }, + { url = "https://files.pythonhosted.org/packages/62/e3/1c98c566fe6809a0c83751d825a03727f242cdbe0d142c9e292725585521/ruff-0.12.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dbbf9f25dfb501f4237ae7501d6364b76a01341c6f1b2cd6764fe449124bb2a", size = 12700451, upload-time = "2025-07-24T13:26:15.488Z" }, + { url = "https://files.pythonhosted.org/packages/24/ff/96058f6506aac0fbc0d0fc0d60b0d0bd746240a0594657a2d94ad28033ba/ruff-0.12.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c47dea6ae39421851685141ba9734767f960113d51e83fd7bb9958d5be8763a", size = 12937465, upload-time = "2025-07-24T13:26:17.808Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d3/68bc5e7ab96c94b3589d1789f2dd6dd4b27b263310019529ac9be1e8f31b/ruff-0.12.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5076aa0e61e30f848846f0265c873c249d4b558105b221be1828f9f79903dc5", size = 11771136, upload-time = "2025-07-24T13:26:20.422Z" }, + { url = "https://files.pythonhosted.org/packages/52/75/7356af30a14584981cabfefcf6106dea98cec9a7af4acb5daaf4b114845f/ruff-0.12.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a5a4c7830dadd3d8c39b1cc85386e2c1e62344f20766be6f173c22fb5f72f293", size = 11601644, upload-time = "2025-07-24T13:26:22.928Z" }, + { url = "https://files.pythonhosted.org/packages/c2/67/91c71d27205871737cae11025ee2b098f512104e26ffd8656fd93d0ada0a/ruff-0.12.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:46699f73c2b5b137b9dc0fc1a190b43e35b008b398c6066ea1350cce6326adcb", size = 12478068, upload-time = "2025-07-24T13:26:26.134Z" }, + { url = "https://files.pythonhosted.org/packages/34/04/b6b00383cf2f48e8e78e14eb258942fdf2a9bf0287fbf5cdd398b749193a/ruff-0.12.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5a655a0a0d396f0f072faafc18ebd59adde8ca85fb848dc1b0d9f024b9c4d3bb", size = 12991537, upload-time = "2025-07-24T13:26:28.533Z" }, + { url = "https://files.pythonhosted.org/packages/3e/b9/053d6445dc7544fb6594785056d8ece61daae7214859ada4a152ad56b6e0/ruff-0.12.5-py3-none-win32.whl", hash = "sha256:dfeb2627c459b0b78ca2bbdc38dd11cc9a0a88bf91db982058b26ce41714ffa9", size = 11751575, upload-time = "2025-07-24T13:26:30.835Z" }, + { url = "https://files.pythonhosted.org/packages/bc/0f/ab16e8259493137598b9149734fec2e06fdeda9837e6f634f5c4e35916da/ruff-0.12.5-py3-none-win_amd64.whl", hash = "sha256:ae0d90cf5f49466c954991b9d8b953bd093c32c27608e409ae3564c63c5306a5", size = 12882273, upload-time = "2025-07-24T13:26:32.929Z" }, + { url = "https://files.pythonhosted.org/packages/00/db/c376b0661c24cf770cb8815268190668ec1330eba8374a126ceef8c72d55/ruff-0.12.5-py3-none-win_arm64.whl", hash = "sha256:48cdbfc633de2c5c37d9f090ba3b352d1576b0015bfc3bc98eaf230275b7e805", size = 11951564, upload-time = "2025-07-24T13:26:34.994Z" }, +] + +[[package]] +name = "secretstorage" +version = "3.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "jeepney" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", size = 19739, upload-time = "2022-08-13T16:22:46.976Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", size = 15221, upload-time = "2022-08-13T16:22:44.457Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "snowballstemmer" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, +] + +[[package]] +name = "soupsieve" +version = "2.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418, upload-time = "2025-04-20T18:50:08.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" }, +] + +[[package]] +name = "sphinx" +version = "7.4.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911, upload-time = "2024-07-20T14:46:56.059Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624, upload-time = "2024-07-20T14:46:52.142Z" }, +] + +[[package]] +name = "sphinx-autobuild" +version = "2024.10.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "sphinx" }, + { name = "starlette" }, + { name = "uvicorn" }, + { name = "watchfiles" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023, upload-time = "2024-10-02T23:15:30.172Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/c0/eba125db38c84d3c74717008fd3cb5000b68cd7e2cbafd1349c6a38c3d3b/sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa", size = 11908, upload-time = "2024-10-02T23:15:28.739Z" }, +] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/0b/a866924ded68efec7a1759587a4e478aec7559d8165fac8b2ad1c0e774d6/sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9", size = 20736, upload-time = "2023-07-08T18:40:54.166Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/dd/018ce05c532a22007ac58d4f45232514cd9d6dd0ee1dc374e309db830983/sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b", size = 22496, upload-time = "2023-07-08T18:40:52.659Z" }, +] + +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, +] + +[[package]] +name = "sphinxcontrib-towncrier" +version = "0.5.0a0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx" }, + { name = "towncrier" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/fe/72ed57093e28af10595c50839b183c5fdf0952482e9ef0ca6eb90eb85c5d/sphinxcontrib_towncrier-0.5.0a0.tar.gz", hash = "sha256:294e69df6e275e7a86df7ea6a927cc7c28c2c370a884cd5c45de6ec989858f27", size = 62453, upload-time = "2025-02-28T01:59:16.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/5c/f7e39f243636a5e1894f2f5a72579977bf3968922afdb75175ee45062066/sphinxcontrib_towncrier-0.5.0a0-py3-none-any.whl", hash = "sha256:11d130c3ad5e4649821d543c4ea7ab64bbe78df4d859ef94f4298e7845dc0f59", size = 12609, upload-time = "2025-02-28T01:59:15.178Z" }, +] + +[[package]] +name = "starlette" +version = "0.47.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8", size = 2583948, upload-time = "2025-07-20T17:31:58.522Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" }, +] + +[[package]] +name = "tgcrypto" +version = "1.2.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/59/7cf5ced989e3139a791d5452d58cb8994de589576b80f9267ba76d794f6c/TgCrypto-1.2.5.tar.gz", hash = "sha256:9bc2cac6fb9a12ef5b08f3dd500174fe374d89b660cce981f57e3138559cb682", size = 37280, upload-time = "2022-11-11T19:55:11.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/1e/6e33a82e6f8bbb261c762ab18da67396b05920e4cf0232ca9a8afc933a67/TgCrypto-1.2.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4507102377002966f35f2481830b7529e00c9bbff8c7d1e09634f984af801675", size = 58986, upload-time = "2022-11-11T19:51:20.084Z" }, + { url = "https://files.pythonhosted.org/packages/74/82/90b8eedd225e66f96ce87936505ea56e289944e022476976601923b74c4b/TgCrypto-1.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:38fe25c0d79b41d7a89caba2a78dea0358e17ca73b033cefd16abed680685829", size = 43349, upload-time = "2022-11-11T19:51:23.151Z" }, + { url = "https://files.pythonhosted.org/packages/76/c1/057ff6b2a022e82bf958b9b936647cca452f155ad27f5d3ceb0a6c04520c/TgCrypto-1.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c035bf8ef89846f67e77e82ea85c089b6ea30631b32e8ac1a6511b9be52ab065", size = 43125, upload-time = "2022-11-11T19:51:25.213Z" }, + { url = "https://files.pythonhosted.org/packages/99/02/b48d2e2ef16cad958138e2c48a11f6fce802bb9b42a66b28509cd530fe12/TgCrypto-1.2.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f594e2680daf20dbac6bf56862f567ddc3cc8d6a19757ed07faa8320ff7acee4", size = 59895, upload-time = "2022-11-11T19:51:28.068Z" }, + { url = "https://files.pythonhosted.org/packages/f0/4d/a29fb5a8bcad988f01662445f42231afd9c7518141dc8f745b4358a9c5e9/TgCrypto-1.2.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8723a16076e229ffdf537fdb5e638227d10f44ca43e6939db1eab524de6eaed7", size = 59682, upload-time = "2022-11-11T19:51:30.367Z" }, + { url = "https://files.pythonhosted.org/packages/3e/95/aa3f59e01cd7f9feae058c9b3d0379315e3102f23ac5fd9a8050bb5d7a07/TgCrypto-1.2.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c1c8d974b8b2d7132364b6f0f6712b92bfe47ab9c5dcee25c70327ff68d22d95", size = 63566, upload-time = "2022-11-11T19:51:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/9d/12/1a47a04a22953fb5d48c6b6c70ffc37b84e9a9553e606863a4e5c56478f4/TgCrypto-1.2.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89d9c143a1fcdb2562a4aa887152abbe9253e1979d7bebef2b489148e0bbe086", size = 63617, upload-time = "2022-11-11T19:51:35.306Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f7/e336feec07fe8b1716498158d7b876354fb68d8ddd17ad81354adf7f7112/TgCrypto-1.2.5-cp310-cp310-win32.whl", hash = "sha256:aa4bc1d11d4a90811c162abd45a5981f171679d1b5bd0322cd7ccd16447366a2", size = 44719, upload-time = "2022-11-11T19:51:37.373Z" }, + { url = "https://files.pythonhosted.org/packages/29/e0/c81f525860e00d0ec188a2114cebcce2912048d3a82bfdd125b295aa6e92/TgCrypto-1.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:39145103614c5e38fe938549742d355920f4a0778fa8259eb69c0c85ba4b1d28", size = 45072, upload-time = "2022-11-11T19:51:40.043Z" }, + { url = "https://files.pythonhosted.org/packages/48/e6/07dc413e57f3677e0b887975b174d9cbc8170e92651dd35a784da9571d5f/TgCrypto-1.2.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59597cdb1c87eb1184088563d20b42a8f2e431e9334fed64926079044ad2a4af", size = 58988, upload-time = "2022-11-11T19:51:41.686Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fe/a418223ff6079184f50f5ad8c2dac37ec40adbb42b886c3ee44fdba2a540/TgCrypto-1.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1283337ae75b02406dd700377b8b783e70033b548492517df6e6c4156b0ed69c", size = 43347, upload-time = "2022-11-11T19:51:43.662Z" }, + { url = "https://files.pythonhosted.org/packages/39/0e/e861a9987c6d9d3ae04d156732311e018558f862490c466c369d73094807/TgCrypto-1.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1735437df0023a40e5fdd95e6b09ce806ec8f2cd2f8879023818840dfae60cab", size = 43123, upload-time = "2022-11-11T19:51:45.094Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ce/d60e249ac4d31d591f4c8ca76e7bd537f4c54f1947b400a14ac3a1678b09/TgCrypto-1.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfa17a20206532c6d2442c9d7a7f6434120bd75896ad9a3e9b9277477afa084f", size = 60713, upload-time = "2022-11-11T19:51:46.411Z" }, + { url = "https://files.pythonhosted.org/packages/27/55/50f81b1a557bd8f9b73f7fcda2381f8826665858750968394ceec0b01282/TgCrypto-1.2.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48da3674474839e5619e7430ff1f98aed9f55369f3cfaef7f65511852869572e", size = 60418, upload-time = "2022-11-11T19:51:47.872Z" }, + { url = "https://files.pythonhosted.org/packages/f3/55/7f5f5a024b71ef81b07c3fe15e79d03b634eb349b04bc79a62c8c3ed3376/TgCrypto-1.2.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b49e982e5b156be821a5235bd9102c00dc506a58607e2c8bd50ac872724a951f", size = 64397, upload-time = "2022-11-11T19:51:49.609Z" }, + { url = "https://files.pythonhosted.org/packages/76/3e/8f059edce82de8ebbc1e513076c0f65a86fac95887fae21ae087440ef23a/TgCrypto-1.2.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9d9f13586065a6d86d05c16409054033a84be208acee29b49f6f194e27b08642", size = 64436, upload-time = "2022-11-11T19:51:51.414Z" }, + { url = "https://files.pythonhosted.org/packages/eb/87/fe894eb7c4cce4b6f710223c24e0ca22f4107fe46f3f56091ceabd482de7/TgCrypto-1.2.5-cp311-cp311-win32.whl", hash = "sha256:10dd3870aecb1a783c6eafd3b164b2149dbc93a9ee13feb7e6f5c58f87c24cd0", size = 44723, upload-time = "2022-11-11T19:51:52.982Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/b918235b19b70a45f4a596c773fad2c379d941d2ac5005293e2ee18bdc63/TgCrypto-1.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:a1beec47d6af8b509af7cf266e30f7703208076076594714005b42d2c25225b3", size = 45073, upload-time = "2022-11-11T19:51:54.724Z" }, + { url = "https://files.pythonhosted.org/packages/e9/8e/0a0b14d40af57dd2c9a18af56714d7afc53d64e072de5474ea62e5b6fdb9/TgCrypto-1.2.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7885a75db09ce8bdba42d2c332085bfe314f232541a729808c7507ffa261ff9a", size = 58984, upload-time = "2022-11-11T19:52:26.32Z" }, + { url = "https://files.pythonhosted.org/packages/a5/49/1306f6c1fdc51769fd29706f13b2aab71f8b616bf24799bc50009e7276af/TgCrypto-1.2.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0d28aa317364a5c27317fe97a48267aa1c65c9aaf589909e97489ebe82a714e3", size = 43348, upload-time = "2022-11-11T19:52:27.694Z" }, + { url = "https://files.pythonhosted.org/packages/c2/6a/b5311038ff1ddacb1f40d35f8b69b1d8c7c5ec9bf874655ae8e6dd8b2214/TgCrypto-1.2.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:940974e19044dc65bcf7b9c5255173b896dff010142f3833047dc55d59cde21c", size = 43118, upload-time = "2022-11-11T19:52:29.849Z" }, + { url = "https://files.pythonhosted.org/packages/a0/7e/48a6a93da084727d2929ed0b162bdf5cf42f746b511523fae111f6f24edd/TgCrypto-1.2.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:457c657dd10ffb4bbbb007132a0f6a7bee5080176a98c51f285fedf636b624cb", size = 59687, upload-time = "2022-11-11T19:52:31.468Z" }, + { url = "https://files.pythonhosted.org/packages/b5/b1/458f6ea8be37beb46600adc568c297edab09e87c1bd881cc5aa1a316074c/TgCrypto-1.2.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:539bdc6b9239fb6a6b134591a998dc7f50d4dcc4fed861f80540682acc0c3802", size = 59489, upload-time = "2022-11-11T19:52:32.926Z" }, + { url = "https://files.pythonhosted.org/packages/67/76/8478b9a4d243d93bd1e25649229e8e891d3dd86b6753f36d442cd5fd8549/TgCrypto-1.2.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d70d5517d64ca952896b726d22c8a66594e6f6259ee2cb4fa134c02d0e8c3e0", size = 63357, upload-time = "2022-11-11T19:52:35.359Z" }, + { url = "https://files.pythonhosted.org/packages/fa/24/81f3062d5f1989210e5bdf03f5725fdc92551e73f7136b9f6f2ddbc2d2d5/TgCrypto-1.2.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:90b6337d3ae4348ed14f89dd2ebf7011fa63d67a48c8a98d955a1e392176c60a", size = 63416, upload-time = "2022-11-11T19:52:36.974Z" }, + { url = "https://files.pythonhosted.org/packages/47/58/4d42aadaca4422e7ca69cd2ec24033165dc67be08267a5b3f425f5728d1d/TgCrypto-1.2.5-cp39-cp39-win32.whl", hash = "sha256:37c4b9be82716fbc6d2b123caef448eee28683888803db075d842327766f7624", size = 44719, upload-time = "2022-11-11T19:52:38.501Z" }, + { url = "https://files.pythonhosted.org/packages/f4/59/fd84e8d904f2dd294980543c6f046d64ad9d44bb9f7e83670bb903298abb/TgCrypto-1.2.5-cp39-cp39-win_amd64.whl", hash = "sha256:6e96b3a478fae977228c5750194c20a18cde402bbbea6593de424f84d4a8893b", size = 45068, upload-time = "2022-11-11T19:52:40.703Z" }, + { url = "https://files.pythonhosted.org/packages/63/cd/9a34246dc7b864c72703f46ae8c1c469c28abcfba2a8c3bcde14415deab2/TgCrypto-1.2.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b0a088ff2e05b6bbe891da936f62b99bd85202b2b9f4f57f71a408490dd518c", size = 42850, upload-time = "2022-11-11T19:52:54.432Z" }, + { url = "https://files.pythonhosted.org/packages/ff/54/a311f3ae06adbe153f9216e40314ee0ae4f9f9cc33b48a198bc6dd25b2a6/TgCrypto-1.2.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f245895c7d518342089d15b5dca3cee9ffa5a0f3534db9d5a930f6a27dff4adf", size = 43266, upload-time = "2022-11-11T19:52:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/51/7c/b21327db01900b1e03688797d247c89a1d0923f9412ee35119ccb5ab3a0a/TgCrypto-1.2.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7dbf607d645c39a577a0f8571039d11ddd2dcdf9656465be75f9e0f540472444", size = 43887, upload-time = "2022-11-11T19:52:57.259Z" }, + { url = "https://files.pythonhosted.org/packages/9a/85/bbecf5e7794ba23cd44efdc27c4c3fcb0399591fae139e613ff52524fa30/TgCrypto-1.2.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6b0c2dc84e632ce7b3d0b767cfe20967e557ad7d71ea5dbd7df2dd544323181", size = 45216, upload-time = "2022-11-11T19:52:58.728Z" }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, +] + +[[package]] +name = "towncrier" +version = "24.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "importlib-resources", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/d7/a5183f788cf4fabf03f7c767ae0b968249b0c737eb64b4d87a07ae01fea7/towncrier-24.8.0.tar.gz", hash = "sha256:013423ee7eed102b2f393c287d22d95f66f1a3ea10a4baa82d298001a7f18af3", size = 61635, upload-time = "2024-08-23T14:52:28.197Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/1b/2f7b88506e22d9798c139261af4946865c0787cfa345514ca3c70173a9cc/towncrier-24.8.0-py3-none-any.whl", hash = "sha256:9343209592b839209cdf28c339ba45792fbfe9775b5f9c177462fd693e127d8d", size = 56981, upload-time = "2024-08-23T14:52:26.659Z" }, +] + +[[package]] +name = "trove-classifiers" +version = "2025.5.9.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/04/1cd43f72c241fedcf0d9a18d0783953ee301eac9e5d9db1df0f0f089d9af/trove_classifiers-2025.5.9.12.tar.gz", hash = "sha256:7ca7c8a7a76e2cd314468c677c69d12cc2357711fcab4a60f87994c1589e5cb5", size = 16940, upload-time = "2025-05-09T12:04:48.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/ef/c6deb083748be3bcad6f471b6ae983950c161890bf5ae1b2af80cc56c530/trove_classifiers-2025.5.9.12-py3-none-any.whl", hash = "sha256:e381c05537adac78881c8fa345fd0e9970159f4e4a04fcc42cfd3129cca640ce", size = 14119, upload-time = "2025-05-09T12:04:46.38Z" }, +] + +[[package]] +name = "twine" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "id" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" }, + { name = "packaging" }, + { name = "readme-renderer" }, + { name = "requests" }, + { name = "requests-toolbelt" }, + { name = "rfc3986" }, + { name = "rich" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/a2/6df94fc5c8e2170d21d7134a565c3a8fb84f9797c1dd65a5976aaf714418/twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd", size = 168404, upload-time = "2025-01-21T18:45:26.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/b6/74e927715a285743351233f33ea3c684528a0d374d2e43ff9ce9585b73fe/twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384", size = 40791, upload-time = "2025-01-21T18:45:24.584Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.35.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "h11" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, +] + +[[package]] +name = "uvloop" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741, upload-time = "2024-10-14T23:38:35.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/76/44a55515e8c9505aa1420aebacf4dd82552e5e15691654894e90d0bd051a/uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f", size = 1442019, upload-time = "2024-10-14T23:37:20.068Z" }, + { url = "https://files.pythonhosted.org/packages/35/5a/62d5800358a78cc25c8a6c72ef8b10851bdb8cca22e14d9c74167b7f86da/uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d", size = 801898, upload-time = "2024-10-14T23:37:22.663Z" }, + { url = "https://files.pythonhosted.org/packages/f3/96/63695e0ebd7da6c741ccd4489b5947394435e198a1382349c17b1146bb97/uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26", size = 3827735, upload-time = "2024-10-14T23:37:25.129Z" }, + { url = "https://files.pythonhosted.org/packages/61/e0/f0f8ec84979068ffae132c58c79af1de9cceeb664076beea86d941af1a30/uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb", size = 3825126, upload-time = "2024-10-14T23:37:27.59Z" }, + { url = "https://files.pythonhosted.org/packages/bf/fe/5e94a977d058a54a19df95f12f7161ab6e323ad49f4dabc28822eb2df7ea/uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f", size = 3705789, upload-time = "2024-10-14T23:37:29.385Z" }, + { url = "https://files.pythonhosted.org/packages/26/dd/c7179618e46092a77e036650c1f056041a028a35c4d76945089fcfc38af8/uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c", size = 3800523, upload-time = "2024-10-14T23:37:32.048Z" }, + { url = "https://files.pythonhosted.org/packages/57/a7/4cf0334105c1160dd6819f3297f8700fda7fc30ab4f61fbf3e725acbc7cc/uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8", size = 1447410, upload-time = "2024-10-14T23:37:33.612Z" }, + { url = "https://files.pythonhosted.org/packages/8c/7c/1517b0bbc2dbe784b563d6ab54f2ef88c890fdad77232c98ed490aa07132/uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0", size = 805476, upload-time = "2024-10-14T23:37:36.11Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ea/0bfae1aceb82a503f358d8d2fa126ca9dbdb2ba9c7866974faec1cb5875c/uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e", size = 3960855, upload-time = "2024-10-14T23:37:37.683Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ca/0864176a649838b838f36d44bf31c451597ab363b60dc9e09c9630619d41/uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb", size = 3973185, upload-time = "2024-10-14T23:37:40.226Z" }, + { url = "https://files.pythonhosted.org/packages/30/bf/08ad29979a936d63787ba47a540de2132169f140d54aa25bc8c3df3e67f4/uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6", size = 3820256, upload-time = "2024-10-14T23:37:42.839Z" }, + { url = "https://files.pythonhosted.org/packages/da/e2/5cf6ef37e3daf2f06e651aae5ea108ad30df3cb269102678b61ebf1fdf42/uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d", size = 3937323, upload-time = "2024-10-14T23:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c", size = 1471284, upload-time = "2024-10-14T23:37:47.833Z" }, + { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2", size = 821349, upload-time = "2024-10-14T23:37:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d", size = 4580089, upload-time = "2024-10-14T23:37:51.703Z" }, + { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc", size = 4693770, upload-time = "2024-10-14T23:37:54.122Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb", size = 4451321, upload-time = "2024-10-14T23:37:55.766Z" }, + { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f", size = 4659022, upload-time = "2024-10-14T23:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123, upload-time = "2024-10-14T23:38:00.688Z" }, + { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325, upload-time = "2024-10-14T23:38:02.309Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806, upload-time = "2024-10-14T23:38:04.711Z" }, + { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068, upload-time = "2024-10-14T23:38:06.385Z" }, + { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428, upload-time = "2024-10-14T23:38:08.416Z" }, + { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018, upload-time = "2024-10-14T23:38:10.888Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a4/646a9d0edff7cde25fc1734695d3dfcee0501140dd0e723e4df3f0a50acb/uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b", size = 1439646, upload-time = "2024-10-14T23:38:24.656Z" }, + { url = "https://files.pythonhosted.org/packages/01/2e/e128c66106af9728f86ebfeeb52af27ecd3cb09336f3e2f3e06053707a15/uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2", size = 800931, upload-time = "2024-10-14T23:38:26.087Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1a/9fbc2b1543d0df11f7aed1632f64bdf5ecc4053cf98cdc9edb91a65494f9/uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0", size = 3829660, upload-time = "2024-10-14T23:38:27.905Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c0/392e235e4100ae3b95b5c6dac77f82b529d2760942b1e7e0981e5d8e895d/uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75", size = 3827185, upload-time = "2024-10-14T23:38:29.458Z" }, + { url = "https://files.pythonhosted.org/packages/e1/24/a5da6aba58f99aed5255eca87d58d1760853e8302d390820cc29058408e3/uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd", size = 3705833, upload-time = "2024-10-14T23:38:31.155Z" }, + { url = "https://files.pythonhosted.org/packages/1a/5c/6ba221bb60f1e6474474102e17e38612ec7a06dc320e22b687ab563d877f/uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff", size = 3804696, upload-time = "2024-10-14T23:38:33.633Z" }, +] + +[[package]] +name = "virtualenv" +version = "20.32.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/96/0834f30fa08dca3738614e6a9d42752b6420ee94e58971d702118f7cfd30/virtualenv-20.32.0.tar.gz", hash = "sha256:886bf75cadfdc964674e6e33eb74d787dff31ca314ceace03ca5810620f4ecf0", size = 6076970, upload-time = "2025-07-21T04:09:50.985Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/c6/f8f28009920a736d0df434b52e9feebfb4d702ba942f15338cb4a83eafc1/virtualenv-20.32.0-py3-none-any.whl", hash = "sha256:2c310aecb62e5aa1b06103ed7c2977b81e042695de2697d01017ff0f1034af56", size = 6057761, upload-time = "2025-07-21T04:09:48.059Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/dd/579d1dc57f0f895426a1211c4ef3b0cb37eb9e642bb04bdcd962b5df206a/watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc", size = 405757, upload-time = "2025-06-15T19:04:51.058Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a0/7a0318cd874393344d48c34d53b3dd419466adf59a29ba5b51c88dd18b86/watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df", size = 397511, upload-time = "2025-06-15T19:04:52.79Z" }, + { url = "https://files.pythonhosted.org/packages/06/be/503514656d0555ec2195f60d810eca29b938772e9bfb112d5cd5ad6f6a9e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68", size = 450739, upload-time = "2025-06-15T19:04:54.203Z" }, + { url = "https://files.pythonhosted.org/packages/4e/0d/a05dd9e5f136cdc29751816d0890d084ab99f8c17b86f25697288ca09bc7/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc", size = 458106, upload-time = "2025-06-15T19:04:55.607Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fa/9cd16e4dfdb831072b7ac39e7bea986e52128526251038eb481effe9f48e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97", size = 484264, upload-time = "2025-06-15T19:04:57.009Z" }, + { url = "https://files.pythonhosted.org/packages/32/04/1da8a637c7e2b70e750a0308e9c8e662ada0cca46211fa9ef24a23937e0b/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c", size = 597612, upload-time = "2025-06-15T19:04:58.409Z" }, + { url = "https://files.pythonhosted.org/packages/30/01/109f2762e968d3e58c95731a206e5d7d2a7abaed4299dd8a94597250153c/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5", size = 477242, upload-time = "2025-06-15T19:04:59.786Z" }, + { url = "https://files.pythonhosted.org/packages/b5/b8/46f58cf4969d3b7bc3ca35a98e739fa4085b0657a1540ccc29a1a0bc016f/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9", size = 453148, upload-time = "2025-06-15T19:05:01.103Z" }, + { url = "https://files.pythonhosted.org/packages/a5/cd/8267594263b1770f1eb76914940d7b2d03ee55eca212302329608208e061/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72", size = 626574, upload-time = "2025-06-15T19:05:02.582Z" }, + { url = "https://files.pythonhosted.org/packages/a1/2f/7f2722e85899bed337cba715723e19185e288ef361360718973f891805be/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc", size = 624378, upload-time = "2025-06-15T19:05:03.719Z" }, + { url = "https://files.pythonhosted.org/packages/bf/20/64c88ec43d90a568234d021ab4b2a6f42a5230d772b987c3f9c00cc27b8b/watchfiles-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587", size = 279829, upload-time = "2025-06-15T19:05:04.822Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/a9c1ed33de7af80935e4eac09570de679c6e21c07070aa99f74b4431f4d6/watchfiles-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82", size = 292192, upload-time = "2025-06-15T19:05:06.348Z" }, + { url = "https://files.pythonhosted.org/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2", size = 405751, upload-time = "2025-06-15T19:05:07.679Z" }, + { url = "https://files.pythonhosted.org/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c", size = 397313, upload-time = "2025-06-15T19:05:08.764Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d", size = 450792, upload-time = "2025-06-15T19:05:09.869Z" }, + { url = "https://files.pythonhosted.org/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7", size = 458196, upload-time = "2025-06-15T19:05:11.91Z" }, + { url = "https://files.pythonhosted.org/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c", size = 484788, upload-time = "2025-06-15T19:05:13.373Z" }, + { url = "https://files.pythonhosted.org/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575", size = 597879, upload-time = "2025-06-15T19:05:14.725Z" }, + { url = "https://files.pythonhosted.org/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8", size = 477447, upload-time = "2025-06-15T19:05:15.775Z" }, + { url = "https://files.pythonhosted.org/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f", size = 453145, upload-time = "2025-06-15T19:05:17.17Z" }, + { url = "https://files.pythonhosted.org/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4", size = 626539, upload-time = "2025-06-15T19:05:18.557Z" }, + { url = "https://files.pythonhosted.org/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d", size = 624472, upload-time = "2025-06-15T19:05:19.588Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2", size = 279348, upload-time = "2025-06-15T19:05:20.856Z" }, + { url = "https://files.pythonhosted.org/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12", size = 292607, upload-time = "2025-06-15T19:05:21.937Z" }, + { url = "https://files.pythonhosted.org/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a", size = 285056, upload-time = "2025-06-15T19:05:23.12Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339, upload-time = "2025-06-15T19:05:24.516Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409, upload-time = "2025-06-15T19:05:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939, upload-time = "2025-06-15T19:05:26.494Z" }, + { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270, upload-time = "2025-06-15T19:05:27.466Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370, upload-time = "2025-06-15T19:05:28.548Z" }, + { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654, upload-time = "2025-06-15T19:05:29.997Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667, upload-time = "2025-06-15T19:05:31.172Z" }, + { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213, upload-time = "2025-06-15T19:05:32.299Z" }, + { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718, upload-time = "2025-06-15T19:05:33.415Z" }, + { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098, upload-time = "2025-06-15T19:05:34.534Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209, upload-time = "2025-06-15T19:05:35.577Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786, upload-time = "2025-06-15T19:05:36.559Z" }, + { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" }, + { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004, upload-time = "2025-06-15T19:05:38.499Z" }, + { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671, upload-time = "2025-06-15T19:05:39.52Z" }, + { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772, upload-time = "2025-06-15T19:05:40.897Z" }, + { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789, upload-time = "2025-06-15T19:05:42.045Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551, upload-time = "2025-06-15T19:05:43.781Z" }, + { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420, upload-time = "2025-06-15T19:05:45.244Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950, upload-time = "2025-06-15T19:05:46.332Z" }, + { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706, upload-time = "2025-06-15T19:05:47.459Z" }, + { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814, upload-time = "2025-06-15T19:05:48.654Z" }, + { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820, upload-time = "2025-06-15T19:05:50.088Z" }, + { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194, upload-time = "2025-06-15T19:05:51.186Z" }, + { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349, upload-time = "2025-06-15T19:05:52.201Z" }, + { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836, upload-time = "2025-06-15T19:05:53.265Z" }, + { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343, upload-time = "2025-06-15T19:05:54.252Z" }, + { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916, upload-time = "2025-06-15T19:05:55.264Z" }, + { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582, upload-time = "2025-06-15T19:05:56.317Z" }, + { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752, upload-time = "2025-06-15T19:05:57.359Z" }, + { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436, upload-time = "2025-06-15T19:05:58.447Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016, upload-time = "2025-06-15T19:05:59.59Z" }, + { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727, upload-time = "2025-06-15T19:06:01.086Z" }, + { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864, upload-time = "2025-06-15T19:06:02.144Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626, upload-time = "2025-06-15T19:06:03.578Z" }, + { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" }, + { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114, upload-time = "2025-06-15T19:06:06.186Z" }, + { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879, upload-time = "2025-06-15T19:06:07.369Z" }, + { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026, upload-time = "2025-06-15T19:06:08.476Z" }, + { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917, upload-time = "2025-06-15T19:06:09.988Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602, upload-time = "2025-06-15T19:06:11.088Z" }, + { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758, upload-time = "2025-06-15T19:06:12.197Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601, upload-time = "2025-06-15T19:06:13.391Z" }, + { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936, upload-time = "2025-06-15T19:06:14.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243, upload-time = "2025-06-15T19:06:16.232Z" }, + { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073, upload-time = "2025-06-15T19:06:17.457Z" }, + { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872, upload-time = "2025-06-15T19:06:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877, upload-time = "2025-06-15T19:06:19.55Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645, upload-time = "2025-06-15T19:06:20.66Z" }, + { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424, upload-time = "2025-06-15T19:06:21.712Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584, upload-time = "2025-06-15T19:06:22.777Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675, upload-time = "2025-06-15T19:06:24.226Z" }, + { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363, upload-time = "2025-06-15T19:06:25.42Z" }, + { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240, upload-time = "2025-06-15T19:06:26.552Z" }, + { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607, upload-time = "2025-06-15T19:06:27.606Z" }, + { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" }, + { url = "https://files.pythonhosted.org/packages/47/8a/a45db804b9f0740f8408626ab2bca89c3136432e57c4673b50180bf85dd9/watchfiles-1.1.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:865c8e95713744cf5ae261f3067861e9da5f1370ba91fc536431e29b418676fa", size = 406400, upload-time = "2025-06-15T19:06:30.233Z" }, + { url = "https://files.pythonhosted.org/packages/64/06/a08684f628fb41addd451845aceedc2407dc3d843b4b060a7c4350ddee0c/watchfiles-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42f92befc848bb7a19658f21f3e7bae80d7d005d13891c62c2cd4d4d0abb3433", size = 397920, upload-time = "2025-06-15T19:06:31.315Z" }, + { url = "https://files.pythonhosted.org/packages/79/e6/e10d5675af653b1b07d4156906858041149ca222edaf8995877f2605ba9e/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa0cc8365ab29487eb4f9979fd41b22549853389e22d5de3f134a6796e1b05a4", size = 451196, upload-time = "2025-06-15T19:06:32.435Z" }, + { url = "https://files.pythonhosted.org/packages/f6/8a/facd6988100cd0f39e89f6c550af80edb28e3a529e1ee662e750663e6b36/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:90ebb429e933645f3da534c89b29b665e285048973b4d2b6946526888c3eb2c7", size = 458218, upload-time = "2025-06-15T19:06:33.503Z" }, + { url = "https://files.pythonhosted.org/packages/90/26/34cbcbc4d0f2f8f9cc243007e65d741ae039f7a11ef8ec6e9cd25bee08d1/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c588c45da9b08ab3da81d08d7987dae6d2a3badd63acdb3e206a42dbfa7cb76f", size = 484851, upload-time = "2025-06-15T19:06:34.541Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1f/f59faa9fc4b0e36dbcdd28a18c430416443b309d295d8b82e18192d120ad/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c55b0f9f68590115c25272b06e63f0824f03d4fc7d6deed43d8ad5660cabdbf", size = 599520, upload-time = "2025-06-15T19:06:35.785Z" }, + { url = "https://files.pythonhosted.org/packages/83/72/3637abecb3bf590529f5154ca000924003e5f4bbb9619744feeaf6f0b70b/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd17a1e489f02ce9117b0de3c0b1fab1c3e2eedc82311b299ee6b6faf6c23a29", size = 477956, upload-time = "2025-06-15T19:06:36.965Z" }, + { url = "https://files.pythonhosted.org/packages/f7/f3/d14ffd9acc0c1bd4790378995e320981423263a5d70bd3929e2e0dc87fff/watchfiles-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da71945c9ace018d8634822f16cbc2a78323ef6c876b1d34bbf5d5222fd6a72e", size = 453196, upload-time = "2025-06-15T19:06:38.024Z" }, + { url = "https://files.pythonhosted.org/packages/7f/38/78ad77bd99e20c0fdc82262be571ef114fc0beef9b43db52adb939768c38/watchfiles-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:51556d5004887045dba3acdd1fdf61dddea2be0a7e18048b5e853dcd37149b86", size = 627479, upload-time = "2025-06-15T19:06:39.442Z" }, + { url = "https://files.pythonhosted.org/packages/e6/cf/549d50a22fcc83f1017c6427b1c76c053233f91b526f4ad7a45971e70c0b/watchfiles-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04e4ed5d1cd3eae68c89bcc1a485a109f39f2fd8de05f705e98af6b5f1861f1f", size = 624414, upload-time = "2025-06-15T19:06:40.859Z" }, + { url = "https://files.pythonhosted.org/packages/72/de/57d6e40dc9140af71c12f3a9fc2d3efc5529d93981cd4d265d484d7c9148/watchfiles-1.1.0-cp39-cp39-win32.whl", hash = "sha256:c600e85f2ffd9f1035222b1a312aff85fd11ea39baff1d705b9b047aad2ce267", size = 280020, upload-time = "2025-06-15T19:06:41.89Z" }, + { url = "https://files.pythonhosted.org/packages/88/bb/7d287fc2a762396b128a0fca2dbae29386e0a242b81d1046daf389641db3/watchfiles-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:3aba215958d88182e8d2acba0fdaf687745180974946609119953c0e112397dc", size = 292758, upload-time = "2025-06-15T19:06:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/be/7c/a3d7c55cfa377c2f62c4ae3c6502b997186bc5e38156bafcb9b653de9a6d/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5", size = 406748, upload-time = "2025-06-15T19:06:44.2Z" }, + { url = "https://files.pythonhosted.org/packages/38/d0/c46f1b2c0ca47f3667b144de6f0515f6d1c670d72f2ca29861cac78abaa1/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d", size = 398801, upload-time = "2025-06-15T19:06:45.774Z" }, + { url = "https://files.pythonhosted.org/packages/70/9c/9a6a42e97f92eeed77c3485a43ea96723900aefa3ac739a8c73f4bff2cd7/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea", size = 451528, upload-time = "2025-06-15T19:06:46.791Z" }, + { url = "https://files.pythonhosted.org/packages/51/7b/98c7f4f7ce7ff03023cf971cd84a3ee3b790021ae7584ffffa0eb2554b96/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6", size = 454095, upload-time = "2025-06-15T19:06:48.211Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3", size = 406910, upload-time = "2025-06-15T19:06:49.335Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c", size = 398816, upload-time = "2025-06-15T19:06:50.433Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432", size = 451584, upload-time = "2025-06-15T19:06:51.834Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009, upload-time = "2025-06-15T19:06:52.896Z" }, + { url = "https://files.pythonhosted.org/packages/48/93/5c96bdb65e7f88f7da40645f34c0a3c317a2931ed82161e93c91e8eddd27/watchfiles-1.1.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7b3443f4ec3ba5aa00b0e9fa90cf31d98321cbff8b925a7c7b84161619870bc9", size = 406640, upload-time = "2025-06-15T19:06:54.868Z" }, + { url = "https://files.pythonhosted.org/packages/e3/25/09204836e93e1b99cce88802ce87264a1d20610c7a8f6de24def27ad95b1/watchfiles-1.1.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7049e52167fc75fc3cc418fc13d39a8e520cbb60ca08b47f6cedb85e181d2f2a", size = 398543, upload-time = "2025-06-15T19:06:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/5e/dc/6f324a6f32c5ab73b54311b5f393a79df34c1584b8d2404cf7e6d780aa5d/watchfiles-1.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54062ef956807ba806559b3c3d52105ae1827a0d4ab47b621b31132b6b7e2866", size = 451787, upload-time = "2025-06-15T19:06:56.998Z" }, + { url = "https://files.pythonhosted.org/packages/45/5d/1d02ef4caa4ec02389e72d5594cdf9c67f1800a7c380baa55063c30c6598/watchfiles-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a7bd57a1bb02f9d5c398c0c1675384e7ab1dd39da0ca50b7f09af45fa435277", size = 454272, upload-time = "2025-06-15T19:06:58.055Z" }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, + { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, + { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, + { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, + { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, + { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, + { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, + { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, + { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, + { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, + { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, + { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, + { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, + { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, + { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, + { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, + { url = "https://files.pythonhosted.org/packages/36/db/3fff0bcbe339a6fa6a3b9e3fbc2bfb321ec2f4cd233692272c5a8d6cf801/websockets-15.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5", size = 175424, upload-time = "2025-03-05T20:02:56.505Z" }, + { url = "https://files.pythonhosted.org/packages/46/e6/519054c2f477def4165b0ec060ad664ed174e140b0d1cbb9fafa4a54f6db/websockets-15.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a", size = 173077, upload-time = "2025-03-05T20:02:58.37Z" }, + { url = "https://files.pythonhosted.org/packages/1a/21/c0712e382df64c93a0d16449ecbf87b647163485ca1cc3f6cbadb36d2b03/websockets-15.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b", size = 173324, upload-time = "2025-03-05T20:02:59.773Z" }, + { url = "https://files.pythonhosted.org/packages/1c/cb/51ba82e59b3a664df54beed8ad95517c1b4dc1a913730e7a7db778f21291/websockets-15.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770", size = 182094, upload-time = "2025-03-05T20:03:01.827Z" }, + { url = "https://files.pythonhosted.org/packages/fb/0f/bf3788c03fec679bcdaef787518dbe60d12fe5615a544a6d4cf82f045193/websockets-15.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb", size = 181094, upload-time = "2025-03-05T20:03:03.123Z" }, + { url = "https://files.pythonhosted.org/packages/5e/da/9fb8c21edbc719b66763a571afbaf206cb6d3736d28255a46fc2fe20f902/websockets-15.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054", size = 181397, upload-time = "2025-03-05T20:03:04.443Z" }, + { url = "https://files.pythonhosted.org/packages/2e/65/65f379525a2719e91d9d90c38fe8b8bc62bd3c702ac651b7278609b696c4/websockets-15.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee", size = 181794, upload-time = "2025-03-05T20:03:06.708Z" }, + { url = "https://files.pythonhosted.org/packages/d9/26/31ac2d08f8e9304d81a1a7ed2851c0300f636019a57cbaa91342015c72cc/websockets-15.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed", size = 181194, upload-time = "2025-03-05T20:03:08.844Z" }, + { url = "https://files.pythonhosted.org/packages/98/72/1090de20d6c91994cd4b357c3f75a4f25ee231b63e03adea89671cc12a3f/websockets-15.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880", size = 181164, upload-time = "2025-03-05T20:03:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/2d/37/098f2e1c103ae8ed79b0e77f08d83b0ec0b241cf4b7f2f10edd0126472e1/websockets-15.0.1-cp39-cp39-win32.whl", hash = "sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411", size = 176381, upload-time = "2025-03-05T20:03:12.77Z" }, + { url = "https://files.pythonhosted.org/packages/75/8b/a32978a3ab42cebb2ebdd5b05df0696a09f4d436ce69def11893afa301f0/websockets-15.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4", size = 176841, upload-time = "2025-03-05T20:03:14.367Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, + { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, + { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, + { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, + { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/b7/48/4b67623bac4d79beb3a6bb27b803ba75c1bdedc06bd827e465803690a4b2/websockets-15.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940", size = 173106, upload-time = "2025-03-05T20:03:29.404Z" }, + { url = "https://files.pythonhosted.org/packages/ed/f0/adb07514a49fe5728192764e04295be78859e4a537ab8fcc518a3dbb3281/websockets-15.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e", size = 173339, upload-time = "2025-03-05T20:03:30.755Z" }, + { url = "https://files.pythonhosted.org/packages/87/28/bd23c6344b18fb43df40d0700f6d3fffcd7cef14a6995b4f976978b52e62/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9", size = 174597, upload-time = "2025-03-05T20:03:32.247Z" }, + { url = "https://files.pythonhosted.org/packages/6d/79/ca288495863d0f23a60f546f0905ae8f3ed467ad87f8b6aceb65f4c013e4/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b", size = 174205, upload-time = "2025-03-05T20:03:33.731Z" }, + { url = "https://files.pythonhosted.org/packages/04/e4/120ff3180b0872b1fe6637f6f995bcb009fb5c87d597c1fc21456f50c848/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f", size = 174150, upload-time = "2025-03-05T20:03:35.757Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c3/30e2f9c539b8da8b1d76f64012f3b19253271a63413b2d3adb94b143407f/websockets-15.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123", size = 176877, upload-time = "2025-03-05T20:03:37.199Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +]