From d5dfed5f1a15f47407a69724925cc0205a26f2e3 Mon Sep 17 00:00:00 2001 From: bohd4nx Date: Sun, 3 May 2026 19:10:11 +0300 Subject: [PATCH] fix: update CI and publish workflows to use consistent action versions and improve error handling in API requests Co-authored-by: Copilot --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/publish.yml | 20 ++++++++++---------- CHANGELOG.md | 16 ++++++++++++++++ pyfragment/utils/http.py | 25 +++++++++++++++++-------- pyfragment/utils/wallet.py | 5 +++-- pyproject.toml | 2 +- requirements-dev.txt | 1 - requirements.txt | 1 - 8 files changed, 51 insertions(+), 27 deletions(-) delete mode 100644 requirements-dev.txt delete mode 100644 requirements.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9572560..23d5f30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,9 +14,9 @@ jobs: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@v6 - - uses: actions/setup-python@v6.2.0 + - uses: actions/setup-python@v6 with: python-version: "3.10" @@ -36,9 +36,9 @@ jobs: python-version: [ "3.10", "3.11", "3.12" ] # 3.13, 3.14 are not supported by some dependencies yet steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@v6 - - uses: actions/setup-python@v6.2.0 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 602a958..7620d90 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ jobs: is-new: ${{ steps.tag.outputs.is-new }} steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -46,17 +46,17 @@ jobs: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@v6 - - uses: actions/setup-python@v6.2.0 + - uses: actions/setup-python@v6 with: python-version: "3.12" - - uses: astral-sh/setup-uv@v8.0.0 + - uses: astral-sh/setup-uv@v8.1.0 - run: uv build - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@v4 with: name: dist path: dist/* @@ -74,12 +74,12 @@ jobs: id-token: write steps: - - uses: actions/download-artifact@v8.0.1 + - uses: actions/download-artifact@v4 with: name: dist path: dist - - uses: pypa/gh-action-pypi-publish@v1.14.0 + - uses: pypa/gh-action-pypi-publish@v1 release: name: GitHub Release @@ -91,11 +91,11 @@ jobs: contents: write steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/download-artifact@v8.0.1 + - uses: actions/download-artifact@v4 with: name: dist path: dist @@ -108,7 +108,7 @@ jobs: echo "$body" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - - uses: softprops/action-gh-release@v3.0.0 + - uses: softprops/action-gh-release@v3 with: tag_name: v${{ needs.version-check.outputs.version }} name: v${{ needs.version-check.outputs.version }} diff --git a/CHANGELOG.md b/CHANGELOG.md index ab4d62d..5423c88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project uses [Calendar Versioning](https://calver.org/) (`YYYY.MINOR.MI --- +## [2026.2.1] — 2026-05-03 + +### Fixed + +- Fragment API 429 responses are now retried automatically (up to 3 attempts) with exponential backoff and jitter in `fragment_request` +- Retry delays in TON transaction broadcasting now include jitter to reduce contention under concurrent calls +- Improved handling of non-200 HTTP responses in `get_fragment_hash` +- Removed unnecessary `method` key leaking into certain API request payloads + +### Changed + +- Type hints refined across the codebase for better clarity and `mypy` strict compliance + +--- + ## [2026.2.0] — 2026-04-14 ### Added @@ -104,6 +119,7 @@ and this project uses [Calendar Versioning](https://calver.org/) (`YYYY.MINOR.MI - `py.typed` marker — full PEP 561 typing support for type-checkers - `__repr__` on all result types for readable debug output +[2026.2.1]: https://github.com/bohd4nx/pyfragment/releases/tag/v2026.2.1 [2026.2.0]: https://github.com/bohd4nx/pyfragment/releases/tag/v2026.2.0 [2026.1.0]: https://github.com/bohd4nx/pyfragment/releases/tag/v2026.1.0 [2026.0.2]: https://github.com/bohd4nx/pyfragment/releases/tag/v2026.0.2 diff --git a/pyfragment/utils/http.py b/pyfragment/utils/http.py index 5009506..4cfc206 100644 --- a/pyfragment/utils/http.py +++ b/pyfragment/utils/http.py @@ -1,5 +1,7 @@ from __future__ import annotations +import asyncio +import random import re from typing import Any, cast @@ -106,14 +108,21 @@ async def fragment_request( Returns: Parsed API response as a dict. """ - resp = await session.post( - f"{FRAGMENT_BASE_URL}/api?hash={fragment_hash}", - headers=headers, - data=data, - ) - if resp.status_code != 200: - raise FragmentPageError(FragmentPageError.BAD_STATUS.format(status=resp.status_code, url=f"{FRAGMENT_BASE_URL}/api")) - return parse_json_response(resp, data.get("method", "request")) + for attempt in range(3): + resp = await session.post( + f"{FRAGMENT_BASE_URL}/api?hash={fragment_hash}", + headers=headers, + data=data, + ) + if resp.status_code == 429 and attempt < 2: + await asyncio.sleep(1 + attempt + random.uniform(0, 0.5)) + continue + if resp.status_code != 200: + raise FragmentPageError( + FragmentPageError.BAD_STATUS.format(status=resp.status_code, url=f"{FRAGMENT_BASE_URL}/api") + ) + return parse_json_response(resp, data.get("method", "request")) + raise FragmentPageError(FragmentPageError.BAD_STATUS.format(status=429, url=f"{FRAGMENT_BASE_URL}/api")) async def execute_transaction_request( diff --git a/pyfragment/utils/wallet.py b/pyfragment/utils/wallet.py index 7a67018..d5c4ee6 100644 --- a/pyfragment/utils/wallet.py +++ b/pyfragment/utils/wallet.py @@ -2,6 +2,7 @@ from __future__ import annotations import asyncio import base64 +import random import ssl from typing import TYPE_CHECKING, Any @@ -69,12 +70,12 @@ async def process_transaction(client: FragmentClient, transaction_data: dict[str return str(result.normalized_hash) except ProviderResponseError as exc: if exc.code == 429 and attempt == 0: - await asyncio.sleep(1) + await asyncio.sleep(1 + random.uniform(0, 0.5)) continue if exc.code == 406 and "seqno" in str(exc).lower(): # Previous tx seqno not yet confirmed — wallet will re-fetch seqno on retry if attempt < 2: - await asyncio.sleep(2) + await asyncio.sleep(2 + random.uniform(0, 1)) continue raise TransactionError(TransactionError.DUPLICATE_SEQNO) from exc raise diff --git a/pyproject.toml b/pyproject.toml index 32a72c0..d9fe781 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyfragment" -version = "2026.2.0" +version = "2026.2.1" description = "Async Python client for the Fragment API — a unified toolkit to manage Telegram assets: purchase Stars and Premium, top up TON and Ads balances, run giveaways, manage anonymous numbers, and explore the marketplace for usernames, numbers, and gifts." readme = "README.md" license = { text = "MIT" } diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index aefbcb6..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1 +0,0 @@ --e .[dev] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d6e1198..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ --e .