feat: enhance FragmentClient to support custom headers; update limits for TON topup and stars purchases

This commit is contained in:
bohd4nx
2026-05-29 22:51:27 +03:00
parent 6aa7037380
commit ed54d1f1a0
6 changed files with 28 additions and 14 deletions
+5 -1
View File
@@ -4,6 +4,7 @@ import json
from typing import Any, cast
from pyfragment.core.constants import (
BASE_HEADERS,
DEFAULT_TIMEOUT,
FRAGMENT_BASE_URL,
MNEMONIC_WORD_COUNTS_VALID,
@@ -40,6 +41,7 @@ class FragmentClient:
cookies: Fragment session cookies as a dict or JSON string.
wallet_version: Wallet contract version — ``"V4R2"`` or ``"V5R1"`` (default).
timeout: HTTP request timeout in seconds. Defaults to ``30.0``.
headers: Custom HTTP request headers. If omitted, :data:`BASE_HEADERS` is used.
Raises:
ConfigurationError: If ``seed``, ``api_key``, or ``wallet_version`` are missing or invalid.
@@ -104,6 +106,7 @@ class FragmentClient:
cookies: dict[str, Any] | str,
wallet_version: str = "V5R1",
timeout: float = DEFAULT_TIMEOUT,
headers: dict[str, str] | None = None,
) -> None:
self._validate_required(seed, api_key)
parsed_cookies = self._parse_cookies(cookies)
@@ -115,6 +118,7 @@ class FragmentClient:
self.cookies: dict[str, Any] = parsed_cookies
self.wallet_version: WalletVersion = version
self.timeout: float = timeout
self.headers: dict[str, str] = headers if headers is not None else BASE_HEADERS
self.marketplace = MarketplaceService(self)
self.purchases = PurchasesService(self)
self.giveaways = GiveawaysService(self)
@@ -391,4 +395,4 @@ class FragmentClient:
page_url="https://fragment.com/premium/gift",
)
"""
return await raw_api_call(self.cookies, self.timeout, method, data, page_url)
return await raw_api_call(self.cookies, self.timeout, method, data, page_url, self.headers)
+4
View File
@@ -14,6 +14,8 @@ from pyfragment.core.constants.limits import (
STARS_PURCHASE_MIN,
STARS_WINNERS_MAX,
STARS_WINNERS_MIN,
TON_TOPUP_MAX,
TON_TOPUP_MIN,
TONAPI_KEY_MIN_LENGTH,
)
from pyfragment.core.constants.ton import DEVICE_INFO, USDT_TON_MASTER_ADDRESS
@@ -56,5 +58,7 @@ __all__ = [
"STARS_WINNERS_MAX",
"STARS_WINNERS_MIN",
"TONAPI_KEY_MIN_LENGTH",
"TON_TOPUP_MAX",
"TON_TOPUP_MIN",
"USDT_TON_MASTER_ADDRESS",
]
+10 -6
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
# Stars: direct purchase
STARS_PURCHASE_MIN: int = 50
STARS_PURCHASE_MAX: int = 10_000
STARS_PURCHASE_MAX: int = 10_000_000
# Stars: giveaway per winner
STARS_GIVEAWAY_MIN: int = 500
@@ -10,12 +10,20 @@ STARS_GIVEAWAY_MAX: int = 1_000_000
# Stars giveaway winners count
STARS_WINNERS_MIN: int = 1
STARS_WINNERS_MAX: int = 5
STARS_WINNERS_MAX: int = 15
# Premium giveaway winners count
PREMIUM_WINNERS_MIN: int = 1
PREMIUM_WINNERS_MAX: int = 24_000
# TON topup / Ads recharge amount
TON_TOPUP_MIN: int = 1
TON_TOPUP_MAX: int = 1_000_000_000
# Wallet minimum balances
MIN_TON_BALANCE: float = 0.33
MIN_USDT_BALANCE: float = 0.75
# Premium subscription durations (months)
PREMIUM_MONTHS_VALID: frozenset[int] = frozenset({3, 6, 12})
@@ -24,7 +32,3 @@ MNEMONIC_WORD_COUNTS_VALID: frozenset[int] = frozenset({12, 24})
# Tonapi API key minimum length
TONAPI_KEY_MIN_LENGTH: int = 68
# Wallet minimum balances
MIN_TON_BALANCE: float = 0.33
MIN_USDT_BALANCE: float = 0.75
+2 -2
View File
@@ -4,7 +4,7 @@ import json
import logging
from typing import TYPE_CHECKING
from pyfragment.core.constants import ADS_TOPUP_PAGE, DEVICE_INFO
from pyfragment.core.constants import ADS_TOPUP_PAGE, DEVICE_INFO, TON_TOPUP_MAX, TON_TOPUP_MIN
from pyfragment.domains.tonapi.account import get_account_info
from pyfragment.domains.tonapi.transaction import process_transaction
from pyfragment.exceptions import ConfigurationError, FragmentAPIError, FragmentError, UnexpectedError, VerificationError
@@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
async def recharge_ads(client: FragmentClient, account: str, amount: int) -> AdsRechargeResult:
if not isinstance(amount, int) or not (1 <= amount <= 1_000_000_000):
if not isinstance(amount, int) or not (TON_TOPUP_MIN <= amount <= TON_TOPUP_MAX):
raise ConfigurationError(ConfigurationError.INVALID_TON_AMOUNT)
try:
+2 -2
View File
@@ -4,7 +4,7 @@ import json
import logging
from typing import TYPE_CHECKING
from pyfragment.core.constants import ADS_TOPUP_PAGE, DEVICE_INFO
from pyfragment.core.constants import ADS_TOPUP_PAGE, DEVICE_INFO, TON_TOPUP_MAX, TON_TOPUP_MIN
from pyfragment.domains.payments import parse_required_payment_amount
from pyfragment.domains.tonapi.account import get_account_info
from pyfragment.domains.tonapi.transaction import process_transaction
@@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
async def topup_ton(client: FragmentClient, username: str, amount: int, show_sender: bool = True) -> AdsTopupResult:
if not isinstance(amount, int) or not (1 <= amount <= 1_000_000_000):
if not isinstance(amount, int) or not (TON_TOPUP_MIN <= amount <= TON_TOPUP_MAX):
raise ConfigurationError(ConfigurationError.INVALID_TON_AMOUNT)
try:
+5 -3
View File
@@ -20,14 +20,16 @@ async def raw_api_call(
method: str,
data: dict[str, Any] | None,
page_url: str,
headers: dict[str, str] | None = None,
) -> dict[str, Any]:
base = headers if headers is not None else BASE_HEADERS
payload = {"method": method, **(data or {})}
headers = {**BASE_HEADERS, "referer": page_url, "x-aj-referer": page_url}
call_headers = {**base, "referer": page_url, "x-aj-referer": page_url}
logger.debug("Starting Fragment API call '%s' on %s", method, page_url)
try:
async with httpx.AsyncClient(cookies=cookies, timeout=timeout) as session:
fragment_hash = await get_fragment_hash(cookies, headers, page_url, timeout)
response = await fragment_request(session, fragment_hash, headers, payload)
fragment_hash = await get_fragment_hash(cookies, call_headers, page_url, timeout)
response = await fragment_request(session, fragment_hash, call_headers, payload)
logger.debug("Completed Fragment API call '%s' with response keys: %s", method, sorted(response.keys()))
return response
except Exception: