mirror of
https://github.com/bohd4nx/FragmentAPI.git
synced 2026-07-25 06:14:29 +00:00
d2d046a2b9
- Rename app/ → fragmentapi/ for proper package naming - Add FragmentClient class with gift_premium, gift_stars, topup_ton methods - Restructure core/ → types/ (exceptions, results, constants) - Merge utils/hash.py into utils/client.py - Replace _version.py with importlib.metadata - Add input validation with min/max bounds - Add unit tests: decode, client init/cookies - Rename 002_test_hash → 003_test_hash - Clean up pyproject.toml: pin deps, production classifiers
109 lines
3.2 KiB
Python
109 lines
3.2 KiB
Python
import json
|
|
from typing import TYPE_CHECKING
|
|
|
|
import httpx
|
|
|
|
from fragmentapi.types import (
|
|
BASE_HEADERS,
|
|
DEVICE,
|
|
TON_PAGE,
|
|
AdsTopupResult,
|
|
ConfigError,
|
|
FragmentAPIError,
|
|
FragmentError,
|
|
UnexpectedError,
|
|
UserNotFoundError,
|
|
)
|
|
from fragmentapi.utils import (
|
|
execute_transaction_request,
|
|
get_account_info,
|
|
get_fragment_hash,
|
|
parse_json_response,
|
|
process_transaction,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from fragmentapi.client import FragmentClient
|
|
|
|
# Page-specific headers
|
|
HEADERS: dict[str, str] = {
|
|
**BASE_HEADERS,
|
|
"referer": TON_PAGE,
|
|
"x-aj-referer": TON_PAGE,
|
|
}
|
|
|
|
|
|
async def _search_recipient(
|
|
session: httpx.AsyncClient,
|
|
fragment_hash: str,
|
|
username: str,
|
|
) -> str:
|
|
await session.post(
|
|
f"https://fragment.com/api?hash={fragment_hash}",
|
|
headers=HEADERS,
|
|
data={"mode": "new", "method": "updateAdsTopupState"},
|
|
)
|
|
resp = await session.post(
|
|
f"https://fragment.com/api?hash={fragment_hash}",
|
|
headers=HEADERS,
|
|
data={"query": username, "method": "searchAdsTopupRecipient"},
|
|
)
|
|
result = parse_json_response(resp, "searchAdsTopupRecipient")
|
|
recipient = result.get("found", {}).get("recipient")
|
|
if not recipient:
|
|
raise UserNotFoundError(UserNotFoundError.NOT_FOUND.format(username=username))
|
|
return recipient
|
|
|
|
|
|
async def _init_request(
|
|
session: httpx.AsyncClient,
|
|
fragment_hash: str,
|
|
recipient: str,
|
|
amount: int,
|
|
) -> str:
|
|
resp = await session.post(
|
|
f"https://fragment.com/api?hash={fragment_hash}",
|
|
headers=HEADERS,
|
|
data={
|
|
"recipient": recipient,
|
|
"amount": amount,
|
|
"method": "initAdsTopupRequest",
|
|
},
|
|
)
|
|
result = parse_json_response(resp, "initAdsTopupRequest")
|
|
req_id = result.get("req_id")
|
|
if not req_id:
|
|
raise FragmentAPIError(FragmentAPIError.NO_REQUEST_ID.format(context="TON topup"))
|
|
return req_id
|
|
|
|
|
|
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):
|
|
raise ConfigError(ConfigError.INVALID_TON_AMOUNT)
|
|
|
|
try:
|
|
fragment_hash = await get_fragment_hash(client.cookies, HEADERS, TON_PAGE)
|
|
account = await get_account_info(client)
|
|
|
|
async with httpx.AsyncClient(cookies=client.cookies) as session:
|
|
recipient = await _search_recipient(session, fragment_hash, username)
|
|
req_id = await _init_request(session, fragment_hash, recipient, amount)
|
|
|
|
tx_data = {
|
|
"account": json.dumps(account),
|
|
"device": DEVICE,
|
|
"transaction": 1,
|
|
"id": req_id,
|
|
"show_sender": int(show_sender),
|
|
"method": "getAdsTopupLink",
|
|
}
|
|
transaction = await execute_transaction_request(session, HEADERS, tx_data, fragment_hash)
|
|
|
|
tx_hash = await process_transaction(client, transaction)
|
|
return AdsTopupResult(transaction_id=tx_hash, username=username, amount=amount)
|
|
|
|
except FragmentError:
|
|
raise
|
|
except Exception as exc:
|
|
raise UnexpectedError(UnexpectedError.UNEXPECTED.format(exc=exc)) from exc
|