mirror of
https://github.com/bohd4nx/FragmentAPI.git
synced 2026-07-25 06:14:29 +00:00
feat: update purchase and giveaway methods; improve header management and add example scripts
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Example: run a Telegram Premium giveaway for a channel.
|
||||
|
||||
winners must be an integer between 1 and 24 000.
|
||||
months (Premium duration per winner) must be 3, 6, or 12.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from pyfragment import ConfigurationError, FragmentClient, UserNotFoundError
|
||||
|
||||
SEED = "word1 word2 ... word24"
|
||||
API_KEY = "YOUR_TONAPI_KEY"
|
||||
COOKIES = {
|
||||
"stel_ssid": "YOUR_STEL_SSID",
|
||||
"stel_dt": "YOUR_STEL_DT",
|
||||
"stel_token": "YOUR_STEL_TOKEN",
|
||||
"stel_ton_token": "YOUR_STEL_TON_TOKEN",
|
||||
}
|
||||
|
||||
CHANNEL = "@channel"
|
||||
WINNERS = 10 # 1–24 000
|
||||
MONTHS = 3 # 3, 6 or 12
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
async with FragmentClient(seed=SEED, api_key=API_KEY, cookies=COOKIES) as client:
|
||||
try:
|
||||
result = await client.giveaway_premium(CHANNEL, winners=WINNERS, months=MONTHS)
|
||||
except UserNotFoundError:
|
||||
print(f"Channel {CHANNEL} was not found on fragment.com — check the username and try again.")
|
||||
return
|
||||
except ConfigurationError as e:
|
||||
print(f"Invalid argument: {e}")
|
||||
return
|
||||
|
||||
print(
|
||||
f"Premium giveaway created for {result.channel} — {result.winners} winners × {result.amount} months | tx: {result.transaction_id}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Example: run a Telegram Stars giveaway for a channel.
|
||||
|
||||
winners must be an integer between 1 and 5.
|
||||
amount (stars per winner) must be an integer between 500 and 1 000 000.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from pyfragment import ConfigurationError, FragmentClient, UserNotFoundError
|
||||
|
||||
SEED = "word1 word2 ... word24"
|
||||
API_KEY = "YOUR_TONAPI_KEY"
|
||||
COOKIES = {
|
||||
"stel_ssid": "YOUR_STEL_SSID",
|
||||
"stel_dt": "YOUR_STEL_DT",
|
||||
"stel_token": "YOUR_STEL_TOKEN",
|
||||
"stel_ton_token": "YOUR_STEL_TON_TOKEN",
|
||||
}
|
||||
|
||||
CHANNEL = "@channel"
|
||||
WINNERS = 3 # 1–5
|
||||
AMOUNT = 1000 # 500–1 000 000 stars per winner
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
async with FragmentClient(seed=SEED, api_key=API_KEY, cookies=COOKIES) as client:
|
||||
try:
|
||||
result = await client.giveaway_stars(CHANNEL, winners=WINNERS, amount=AMOUNT)
|
||||
except UserNotFoundError:
|
||||
print(f"Channel {CHANNEL} was not found on fragment.com — check the username and try again.")
|
||||
return
|
||||
except ConfigurationError as e:
|
||||
print(f"Invalid argument: {e}")
|
||||
return
|
||||
|
||||
print(
|
||||
f"Stars giveaway created for {result.channel} — {result.winners} winners × {result.amount} stars | tx: {result.transaction_id}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -19,7 +19,7 @@ COOKIES = {
|
||||
}
|
||||
|
||||
USERNAME = "@username"
|
||||
MONTHS = 3 # 3, 6, or 12
|
||||
MONTHS = 3 # 3, 6 or 12
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
|
||||
@@ -19,7 +19,7 @@ COOKIES = {
|
||||
}
|
||||
|
||||
USERNAME = "@username"
|
||||
AMOUNT = 500 # 50 to 1 000 000 stars, integer
|
||||
AMOUNT = 500 # 50–1 000 000 stars
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
|
||||
@@ -19,7 +19,7 @@ COOKIES = {
|
||||
}
|
||||
|
||||
USERNAME = "@username"
|
||||
AMOUNT = 10 # TON, integer — 1 to 1 000 000 000
|
||||
AMOUNT = 10 # 1–1 000 000 000 TON
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" width="512" height="512" style="width: 100%; height: 100%; transform: translate3d(0,0,0); content-visibility: visible;" preserveAspectRatio="xMidYMid meet"><defs><clipPath id="__lottie_element_2"><rect width="512" height="512" x="0" y="0"></rect></clipPath><clipPath id="__lottie_element_4"><path d="M0,0 L100,0 L100,100 L0,100z"></path></clipPath></defs><g clip-path="url(#__lottie_element_2)"><g clip-path="url(#__lottie_element_4)" style="display: block;" transform="matrix(5.119999885559082,0,0,5.119999885559082,0,0)" opacity="1"><g style="display: block;" transform="matrix(1.333299994468689,0,0,1.333299994468689,0,0)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill="rgb(30,40,51)" fill-opacity="1" d=" M47.31999969482422,5.619999885559082 C47.31999969482422,5.619999885559082 27.68000030517578,5.619999885559082 27.68000030517578,5.619999885559082 C15.5,5.619999885559082 5.619999885559082,15.5 5.619999885559082,27.68000030517578 C5.619999885559082,27.68000030517578 5.619999885559082,47.31999969482422 5.619999885559082,47.31999969482422 C5.619999885559082,59.5 15.5,69.37999725341797 27.68000030517578,69.37999725341797 C27.68000030517578,69.37999725341797 47.31999969482422,69.37999725341797 47.31999969482422,69.37999725341797 C59.5,69.37999725341797 69.37999725341797,59.5 69.37999725341797,47.31999969482422 C69.37999725341797,47.31999969482422 69.37999725341797,27.68000030517578 69.37999725341797,27.68000030517578 C69.37999725341797,15.5 59.5,5.619999885559082 47.31999969482422,5.619999885559082 C47.31999969482422,5.619999885559082 47.31999969482422,5.619999885559082 47.31999969482422,5.619999885559082z"></path></g><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill="rgb(255,255,255)" fill-opacity="1" d=" M36.349998474121094,32.79999923706055 C36.349998474121094,32.79999923706055 21.1299991607666,25.940000534057617 21.1299991607666,25.940000534057617 C20.020000457763672,25.450000762939453 20.389999389648438,23.790000915527344 21.600000381469727,23.790000915527344 C21.600000381469727,23.790000915527344 53.41999816894531,23.790000915527344 53.41999816894531,23.790000915527344 C54.630001068115234,23.790000915527344 54.9900016784668,25.440000534057617 53.880001068115234,25.940000534057617 C53.880001068115234,25.940000534057617 38.66999816894531,32.79999923706055 38.66999816894531,32.79999923706055 C37.939998626708984,33.130001068115234 37.09000015258789,33.130001068115234 36.36000061035156,32.79999923706055 C36.36000061035156,32.79999923706055 36.349998474121094,32.79999923706055 36.349998474121094,32.79999923706055z M56.81999969482422,30.06999969482422 C57.43000030517578,29.1200008392334 56.43000030517578,27.979999542236328 55.400001525878906,28.440000534057617 C55.400001525878906,28.440000534057617 40.72999954223633,35.13999938964844 40.72999954223633,35.13999938964844 C39.72999954223633,35.599998474121094 39.09000015258789,36.61000061035156 39.09000015258789,37.70000076293945 C39.09000015258789,37.70000076293945 39.09000015258789,53.810001373291016 39.09000015258789,53.810001373291016 C39.09000015258789,54.93000030517578 40.54999923706055,55.36000061035156 41.15999984741211,54.41999816894531 C41.15999984741211,54.41999816894531 56.810001373291016,30.06999969482422 56.810001373291016,30.06999969482422 C56.810001373291016,30.06999969482422 56.81999969482422,30.06999969482422 56.81999969482422,30.06999969482422z M19.600000381469727,28.440000534057617 C18.579999923706055,27.979999542236328 17.56999969482422,29.1200008392334 18.18000030517578,30.06999969482422 C18.18000030517578,30.06999969482422 33.84000015258789,54.43000030517578 33.84000015258789,54.43000030517578 C34.45000076293945,55.380001068115234 35.90999984741211,54.939998626708984 35.90999984741211,53.81999969482422 C35.90999984741211,53.81999969482422 35.90999984741211,37.70000076293945 35.90999984741211,37.70000076293945 C35.90999984741211,36.599998474121094 35.27000045776367,35.599998474121094 34.27000045776367,35.13999938964844 C34.27000045776367,35.13999938964844 19.59000015258789,28.450000762939453 19.59000015258789,28.450000762939453 C19.59000015258789,28.450000762939453 19.600000381469727,28.440000534057617 19.600000381469727,28.440000534057617z"></path></g></g></g></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 4.3 KiB |
@@ -146,7 +146,7 @@ class FragmentClient:
|
||||
|
||||
Returns:
|
||||
:class:`WalletInfo` with ``address`` (``"UQ..."``), ``state``
|
||||
(``"active"``, ``"uninit"``, or ``"frozen"``), and ``balance`` in TON.
|
||||
(``"active"``, ``"uninit"``, ``"nonexist"``, or ``"frozen"``), and ``balance`` in TON.
|
||||
"""
|
||||
return await get_wallet_info(self)
|
||||
|
||||
|
||||
@@ -10,25 +10,21 @@ from pyfragment.types import (
|
||||
UnexpectedError,
|
||||
UserNotFoundError,
|
||||
)
|
||||
from pyfragment.types.constants import BASE_HEADERS, DEVICE, PREMIUM_GIVEAWAY_PAGE
|
||||
from pyfragment.types.constants import DEVICE, PREMIUM_GIVEAWAY_PAGE
|
||||
from pyfragment.types.results import PremiumGiveawayResult
|
||||
from pyfragment.utils import (
|
||||
execute_transaction_request,
|
||||
fragment_post,
|
||||
fragment_request,
|
||||
get_account_info,
|
||||
get_fragment_hash,
|
||||
make_headers,
|
||||
process_transaction,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pyfragment.client import FragmentClient
|
||||
|
||||
# Page-specific headers
|
||||
HEADERS: dict[str, str] = {
|
||||
**BASE_HEADERS,
|
||||
"referer": PREMIUM_GIVEAWAY_PAGE,
|
||||
"x-aj-referer": PREMIUM_GIVEAWAY_PAGE,
|
||||
}
|
||||
HEADERS: dict[str, str] = make_headers(PREMIUM_GIVEAWAY_PAGE)
|
||||
|
||||
|
||||
async def _search_recipient(
|
||||
@@ -38,7 +34,7 @@ async def _search_recipient(
|
||||
winners: int,
|
||||
months: int,
|
||||
) -> str:
|
||||
result = await fragment_post(
|
||||
result = await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
@@ -62,7 +58,7 @@ async def _init_request(
|
||||
winners: int,
|
||||
months: int,
|
||||
) -> str:
|
||||
result = await fragment_post(
|
||||
result = await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
|
||||
@@ -10,25 +10,21 @@ from pyfragment.types import (
|
||||
UnexpectedError,
|
||||
UserNotFoundError,
|
||||
)
|
||||
from pyfragment.types.constants import BASE_HEADERS, DEVICE, STARS_GIVEAWAY_PAGE
|
||||
from pyfragment.types.constants import DEVICE, STARS_GIVEAWAY_PAGE
|
||||
from pyfragment.types.results import StarsGiveawayResult
|
||||
from pyfragment.utils import (
|
||||
execute_transaction_request,
|
||||
fragment_post,
|
||||
fragment_request,
|
||||
get_account_info,
|
||||
get_fragment_hash,
|
||||
make_headers,
|
||||
process_transaction,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pyfragment.client import FragmentClient
|
||||
|
||||
# Page-specific headers
|
||||
HEADERS: dict[str, str] = {
|
||||
**BASE_HEADERS,
|
||||
"referer": STARS_GIVEAWAY_PAGE,
|
||||
"x-aj-referer": STARS_GIVEAWAY_PAGE,
|
||||
}
|
||||
HEADERS: dict[str, str] = make_headers(STARS_GIVEAWAY_PAGE)
|
||||
|
||||
|
||||
async def _search_recipient(
|
||||
@@ -36,7 +32,7 @@ async def _search_recipient(
|
||||
fragment_hash: str,
|
||||
channel: str,
|
||||
) -> str:
|
||||
result = await fragment_post(
|
||||
result = await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
@@ -58,7 +54,7 @@ async def _init_request(
|
||||
winners: int,
|
||||
amount: int,
|
||||
) -> str:
|
||||
result = await fragment_post(
|
||||
result = await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
|
||||
@@ -12,24 +12,20 @@ from pyfragment.types import (
|
||||
UnexpectedError,
|
||||
UserNotFoundError,
|
||||
)
|
||||
from pyfragment.types.constants import BASE_HEADERS, DEVICE, PREMIUM_PAGE
|
||||
from pyfragment.types.constants import DEVICE, PREMIUM_PAGE
|
||||
from pyfragment.utils import (
|
||||
execute_transaction_request,
|
||||
fragment_post,
|
||||
fragment_request,
|
||||
get_account_info,
|
||||
get_fragment_hash,
|
||||
make_headers,
|
||||
process_transaction,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pyfragment.client import FragmentClient
|
||||
|
||||
# Page-specific headers
|
||||
HEADERS: dict[str, str] = {
|
||||
**BASE_HEADERS,
|
||||
"referer": PREMIUM_PAGE,
|
||||
"x-aj-referer": PREMIUM_PAGE,
|
||||
}
|
||||
HEADERS: dict[str, str] = make_headers(PREMIUM_PAGE)
|
||||
|
||||
|
||||
async def _search_recipient(
|
||||
@@ -38,7 +34,7 @@ async def _search_recipient(
|
||||
username: str,
|
||||
months: int,
|
||||
) -> str:
|
||||
result = await fragment_post(
|
||||
result = await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
@@ -60,7 +56,7 @@ async def _init_request(
|
||||
recipient: str,
|
||||
months: int,
|
||||
) -> str:
|
||||
await fragment_post(
|
||||
await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
@@ -71,7 +67,7 @@ async def _init_request(
|
||||
"method": "updatePremiumState",
|
||||
},
|
||||
)
|
||||
result = await fragment_post(
|
||||
result = await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
|
||||
@@ -11,24 +11,20 @@ from pyfragment.types import (
|
||||
UnexpectedError,
|
||||
UserNotFoundError,
|
||||
)
|
||||
from pyfragment.types.constants import BASE_HEADERS, DEVICE, STARS_PAGE
|
||||
from pyfragment.types.constants import DEVICE, STARS_PAGE
|
||||
from pyfragment.utils import (
|
||||
execute_transaction_request,
|
||||
fragment_post,
|
||||
fragment_request,
|
||||
get_account_info,
|
||||
get_fragment_hash,
|
||||
make_headers,
|
||||
process_transaction,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pyfragment.client import FragmentClient
|
||||
|
||||
# Page-specific headers
|
||||
HEADERS: dict[str, str] = {
|
||||
**BASE_HEADERS,
|
||||
"referer": STARS_PAGE,
|
||||
"x-aj-referer": STARS_PAGE,
|
||||
}
|
||||
HEADERS: dict[str, str] = make_headers(STARS_PAGE)
|
||||
|
||||
|
||||
async def _search_recipient(
|
||||
@@ -36,7 +32,7 @@ async def _search_recipient(
|
||||
fragment_hash: str,
|
||||
username: str,
|
||||
) -> str:
|
||||
result = await fragment_post(
|
||||
result = await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
@@ -58,7 +54,7 @@ async def _init_request(
|
||||
recipient: str,
|
||||
amount: int,
|
||||
) -> str:
|
||||
result = await fragment_post(
|
||||
result = await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
|
||||
@@ -11,24 +11,20 @@ from pyfragment.types import (
|
||||
UnexpectedError,
|
||||
UserNotFoundError,
|
||||
)
|
||||
from pyfragment.types.constants import BASE_HEADERS, DEVICE, TON_PAGE
|
||||
from pyfragment.types.constants import DEVICE, TON_PAGE
|
||||
from pyfragment.utils import (
|
||||
execute_transaction_request,
|
||||
fragment_post,
|
||||
fragment_request,
|
||||
get_account_info,
|
||||
get_fragment_hash,
|
||||
make_headers,
|
||||
process_transaction,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pyfragment.client import FragmentClient
|
||||
|
||||
# Page-specific headers
|
||||
HEADERS: dict[str, str] = {
|
||||
**BASE_HEADERS,
|
||||
"referer": TON_PAGE,
|
||||
"x-aj-referer": TON_PAGE,
|
||||
}
|
||||
HEADERS: dict[str, str] = make_headers(TON_PAGE)
|
||||
|
||||
|
||||
async def _search_recipient(
|
||||
@@ -36,8 +32,8 @@ async def _search_recipient(
|
||||
fragment_hash: str,
|
||||
username: str,
|
||||
) -> str:
|
||||
await fragment_post(session, fragment_hash, HEADERS, {"mode": "new", "method": "updateAdsTopupState"})
|
||||
result = await fragment_post(
|
||||
await fragment_request(session, fragment_hash, HEADERS, {"mode": "new", "method": "updateAdsTopupState"})
|
||||
result = await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
@@ -58,7 +54,7 @@ async def _init_request(
|
||||
recipient: str,
|
||||
amount: int,
|
||||
) -> str:
|
||||
result = await fragment_post(
|
||||
result = await fragment_request(
|
||||
session,
|
||||
fragment_hash,
|
||||
HEADERS,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from pyfragment.utils.decoder import clean_decode
|
||||
from pyfragment.utils.http import (
|
||||
execute_transaction_request,
|
||||
fragment_post,
|
||||
fragment_request,
|
||||
get_fragment_hash,
|
||||
make_headers,
|
||||
parse_json_response,
|
||||
)
|
||||
from pyfragment.utils.wallet import get_account_info, process_transaction
|
||||
@@ -10,9 +11,10 @@ from pyfragment.utils.wallet import get_account_info, process_transaction
|
||||
__all__ = [
|
||||
"clean_decode",
|
||||
"execute_transaction_request",
|
||||
"fragment_post",
|
||||
"fragment_request",
|
||||
"get_account_info",
|
||||
"get_fragment_hash",
|
||||
"make_headers",
|
||||
"parse_json_response",
|
||||
"process_transaction",
|
||||
]
|
||||
|
||||
@@ -4,7 +4,11 @@ from typing import Any
|
||||
import httpx
|
||||
|
||||
from pyfragment.types import FragmentPageError, ParseError, VerificationError
|
||||
from pyfragment.types.constants import DEFAULT_TIMEOUT
|
||||
from pyfragment.types.constants import BASE_HEADERS, DEFAULT_TIMEOUT
|
||||
|
||||
|
||||
def make_headers(page_url: str) -> dict[str, str]:
|
||||
return {**BASE_HEADERS, "referer": page_url, "x-aj-referer": page_url}
|
||||
|
||||
|
||||
async def get_fragment_hash(
|
||||
@@ -79,7 +83,7 @@ def parse_json_response(response: httpx.Response, context: str) -> dict[str, Any
|
||||
raise ParseError(ParseError.UNPARSEABLE.format(context=context, exc=exc)) from exc
|
||||
|
||||
|
||||
async def fragment_post(
|
||||
async def fragment_request(
|
||||
session: httpx.AsyncClient,
|
||||
fragment_hash: str,
|
||||
headers: dict[str, str],
|
||||
@@ -129,7 +133,7 @@ async def execute_transaction_request(
|
||||
VerificationError: If Fragment requires KYC verification.
|
||||
ParseError: If the response cannot be parsed.
|
||||
"""
|
||||
transaction = await fragment_post(session, fragment_hash, headers, tx_data)
|
||||
transaction = await fragment_request(session, fragment_hash, headers, tx_data)
|
||||
|
||||
if transaction.get("need_verify"):
|
||||
raise VerificationError(VerificationError.KYC_REQUIRED)
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
"""Unit tests for method-level input validation — no network calls."""
|
||||
|
||||
import pytest
|
||||
|
||||
from pyfragment import FragmentClient
|
||||
from pyfragment.types import ConfigurationError
|
||||
|
||||
VALID_SEED = "abandon " * 23 + "about"
|
||||
VALID_API_KEY = "A" * 68
|
||||
VALID_COOKIES = {
|
||||
"stel_ssid": "x",
|
||||
"stel_dt": "x",
|
||||
"stel_token": "x",
|
||||
"stel_ton_token": "x",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client() -> FragmentClient:
|
||||
return FragmentClient(seed=VALID_SEED, api_key=VALID_API_KEY, cookies=VALID_COOKIES)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_purchase_premium_invalid_months(client: FragmentClient) -> None:
|
||||
with pytest.raises(ConfigurationError):
|
||||
await client.purchase_premium("@user", months=5)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_purchase_stars_amount_too_low(client: FragmentClient) -> None:
|
||||
with pytest.raises(ConfigurationError):
|
||||
await client.purchase_stars("@user", amount=49)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_purchase_stars_amount_too_high(client: FragmentClient) -> None:
|
||||
with pytest.raises(ConfigurationError):
|
||||
await client.purchase_stars("@user", amount=1_000_001)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_purchase_stars_float_amount(client: FragmentClient) -> None:
|
||||
with pytest.raises(ConfigurationError):
|
||||
await client.purchase_stars("@user", amount=100.5) # type: ignore[arg-type]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_topup_ton_amount_zero(client: FragmentClient) -> None:
|
||||
with pytest.raises(ConfigurationError):
|
||||
await client.topup_ton("@user", amount=0)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_topup_ton_amount_too_high(client: FragmentClient) -> None:
|
||||
with pytest.raises(ConfigurationError):
|
||||
await client.topup_ton("@user", amount=1_000_000_001)
|
||||
@@ -1,146 +0,0 @@
|
||||
"""Tests for purchase methods with all network calls mocked."""
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from pyfragment import FragmentClient
|
||||
from pyfragment.types import AdsTopupResult, PremiumResult, StarsResult, UserNotFoundError
|
||||
|
||||
VALID_SEED = "abandon " * 23 + "about"
|
||||
VALID_API_KEY = "A" * 68
|
||||
VALID_COOKIES = {
|
||||
"stel_ssid": "x",
|
||||
"stel_dt": "x",
|
||||
"stel_token": "x",
|
||||
"stel_ton_token": "x",
|
||||
}
|
||||
FAKE_HASH = "abc123"
|
||||
FAKE_RECIPIENT = "recipient_token"
|
||||
FAKE_REQ_ID = "req_42"
|
||||
FAKE_TX_HASH = "deadbeef" * 8
|
||||
FAKE_ACCOUNT = {"address": "0:abc", "publicKey": "pub", "chain": "-239", "walletStateInit": "base64=="}
|
||||
FAKE_TRANSACTION = {"transaction": {"messages": [{"address": "0:abc", "amount": "100000000", "payload": ""}]}}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client() -> FragmentClient:
|
||||
return FragmentClient(seed=VALID_SEED, api_key=VALID_API_KEY, cookies=VALID_COOKIES)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_purchase_stars_success(client: FragmentClient) -> None:
|
||||
with (
|
||||
patch("pyfragment.methods.purchase_stars.get_fragment_hash", AsyncMock(return_value=FAKE_HASH)),
|
||||
patch("pyfragment.methods.purchase_stars.get_account_info", AsyncMock(return_value=FAKE_ACCOUNT)),
|
||||
patch(
|
||||
"pyfragment.methods.purchase_stars.fragment_post",
|
||||
AsyncMock(
|
||||
side_effect=[
|
||||
{"found": {"recipient": FAKE_RECIPIENT}}, # searchStarsRecipient
|
||||
{"req_id": FAKE_REQ_ID}, # initBuyStarsRequest
|
||||
]
|
||||
),
|
||||
),
|
||||
patch("pyfragment.methods.purchase_stars.execute_transaction_request", AsyncMock(return_value=FAKE_TRANSACTION)),
|
||||
patch("pyfragment.methods.purchase_stars.process_transaction", AsyncMock(return_value=FAKE_TX_HASH)),
|
||||
):
|
||||
result = await client.purchase_stars("testuser", amount=100)
|
||||
|
||||
assert isinstance(result, StarsResult)
|
||||
assert result.transaction_id == FAKE_TX_HASH
|
||||
assert result.username == "testuser"
|
||||
assert result.amount == 100
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_purchase_stars_user_not_found(client: FragmentClient) -> None:
|
||||
with (
|
||||
patch("pyfragment.methods.purchase_stars.get_fragment_hash", AsyncMock(return_value=FAKE_HASH)),
|
||||
patch("pyfragment.methods.purchase_stars.get_account_info", AsyncMock(return_value=FAKE_ACCOUNT)),
|
||||
patch("pyfragment.methods.purchase_stars.fragment_post", AsyncMock(return_value={"found": {}})),
|
||||
):
|
||||
with pytest.raises(UserNotFoundError):
|
||||
await client.purchase_stars("ghost", amount=100)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_purchase_premium_success(client: FragmentClient) -> None:
|
||||
with (
|
||||
patch("pyfragment.methods.purchase_premium.get_fragment_hash", AsyncMock(return_value=FAKE_HASH)),
|
||||
patch("pyfragment.methods.purchase_premium.get_account_info", AsyncMock(return_value=FAKE_ACCOUNT)),
|
||||
patch(
|
||||
"pyfragment.methods.purchase_premium.fragment_post",
|
||||
AsyncMock(
|
||||
side_effect=[
|
||||
{"found": {"recipient": FAKE_RECIPIENT}}, # searchPremiumGiftRecipient
|
||||
{}, # updatePremiumState
|
||||
{"req_id": FAKE_REQ_ID}, # initGiftPremiumRequest
|
||||
]
|
||||
),
|
||||
),
|
||||
patch("pyfragment.methods.purchase_premium.execute_transaction_request", AsyncMock(return_value=FAKE_TRANSACTION)),
|
||||
patch("pyfragment.methods.purchase_premium.process_transaction", AsyncMock(return_value=FAKE_TX_HASH)),
|
||||
):
|
||||
result = await client.purchase_premium("testuser", months=6)
|
||||
|
||||
assert isinstance(result, PremiumResult)
|
||||
assert result.transaction_id == FAKE_TX_HASH
|
||||
assert result.username == "testuser"
|
||||
assert result.amount == 6
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_purchase_premium_user_not_found(client: FragmentClient) -> None:
|
||||
with (
|
||||
patch("pyfragment.methods.purchase_premium.get_fragment_hash", AsyncMock(return_value=FAKE_HASH)),
|
||||
patch("pyfragment.methods.purchase_premium.get_account_info", AsyncMock(return_value=FAKE_ACCOUNT)),
|
||||
patch("pyfragment.methods.purchase_premium.fragment_post", AsyncMock(return_value={"found": {}})),
|
||||
):
|
||||
with pytest.raises(UserNotFoundError):
|
||||
await client.purchase_premium("ghost", months=3)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_topup_ton_success(client: FragmentClient) -> None:
|
||||
with (
|
||||
patch("pyfragment.methods.topup_ton.get_fragment_hash", AsyncMock(return_value=FAKE_HASH)),
|
||||
patch("pyfragment.methods.topup_ton.get_account_info", AsyncMock(return_value=FAKE_ACCOUNT)),
|
||||
patch(
|
||||
"pyfragment.methods.topup_ton.fragment_post",
|
||||
AsyncMock(
|
||||
side_effect=[
|
||||
{}, # updateAdsTopupState
|
||||
{"found": {"recipient": FAKE_RECIPIENT}}, # searchAdsTopupRecipient
|
||||
{"req_id": FAKE_REQ_ID}, # initAdsTopupRequest
|
||||
]
|
||||
),
|
||||
),
|
||||
patch("pyfragment.methods.topup_ton.execute_transaction_request", AsyncMock(return_value=FAKE_TRANSACTION)),
|
||||
patch("pyfragment.methods.topup_ton.process_transaction", AsyncMock(return_value=FAKE_TX_HASH)),
|
||||
):
|
||||
result = await client.topup_ton("testuser", amount=10)
|
||||
|
||||
assert isinstance(result, AdsTopupResult)
|
||||
assert result.transaction_id == FAKE_TX_HASH
|
||||
assert result.username == "testuser"
|
||||
assert result.amount == 10
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_topup_ton_user_not_found(client: FragmentClient) -> None:
|
||||
with (
|
||||
patch("pyfragment.methods.topup_ton.get_fragment_hash", AsyncMock(return_value=FAKE_HASH)),
|
||||
patch("pyfragment.methods.topup_ton.get_account_info", AsyncMock(return_value=FAKE_ACCOUNT)),
|
||||
patch(
|
||||
"pyfragment.methods.topup_ton.fragment_post",
|
||||
AsyncMock(
|
||||
side_effect=[
|
||||
{}, # updateAdsTopupState
|
||||
{"found": {}}, # searchAdsTopupRecipient → not found
|
||||
]
|
||||
),
|
||||
),
|
||||
):
|
||||
with pytest.raises(UserNotFoundError):
|
||||
await client.topup_ton("ghost", amount=10)
|
||||
Reference in New Issue
Block a user