diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5423c88..98881cf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,33 @@ and this project uses [Calendar Versioning](https://calver.org/) (`YYYY.MINOR.MI
---
+## [Unreleased]
+
+### Added
+
+- `payment_method` option (`"ton"` / `"usdt_ton"`) for:
+ - `purchase_stars()`
+ - `purchase_premium()`
+ - `giveaway_stars()`
+ - `giveaway_premium()`
+
+### Changed
+
+- Added runtime validation for `payment_method` via `SUPPORTED_PAYMENT_METHODS` and `ConfigurationError.INVALID_PAYMENT_METHOD`
+- Updated method docstrings to explicitly document recipient/channel formats:
+ - `@username` / `username` / `https://t.me/username`
+
+### Tests
+
+- Extended stars and premium test suites to cover:
+ - invalid payment method
+ - payment method propagation to `init*Request` payloads
+ - accepted query formats (`@`, plain username, `t.me` link)
+
+### Documentation
+
+- Simplified `README` usage example
+
## [2026.2.1] — 2026-05-03
### Fixed
@@ -48,37 +75,44 @@ and this project uses [Calendar Versioning](https://calver.org/) (`YYYY.MINOR.MI
### Added
**Giveaways**
+
- `giveaway_stars(channel, winners, amount)` — Stars giveaway; 1–5 winners, 500–1 000 000 stars each
- `giveaway_premium(channel, winners, months)` — Premium giveaway; 1–24 000 winners, 3/6/12 months each
- `StarsGiveawayResult`, `PremiumGiveawayResult` result types
**Telegram Ads**
+
- `recharge_ads(account, amount)` — top up a Telegram Ads account; 1–1 000 000 000 TON
- `AdsRechargeResult` result type
**Marketplace**
+
- `search_usernames(query?, sort?, filter?, offset_id?)` — search Fragment usernames; `sort`: `price_desc / price_asc / listed / ending`, `filter`: `auction / sale / sold`
- `search_numbers(query?, sort?, filter?, offset_id?)` — search Fragment anonymous numbers; same `sort` / `filter` / pagination semantics
- `search_gifts(query?, collection?, sort?, filter?, view?, attr?, offset?)` — search Fragment gifts; `attr` accepts `{"Model": ["Foosball"], "Backdrop": ["Celtic Blue"]}`
- `UsernamesResult`, `NumbersResult`, `GiftsResult` result types
**Anonymous numbers**
+
- `get_login_code(number)` — fetch the current pending login code
- `toggle_login_codes(number, can_receive)` — enable or disable login code delivery
- `terminate_sessions(number)` — terminate all active Telegram sessions (two-step flow handled internally)
- `LoginCodeResult`, `TerminateSessionsResult` result types; `AnonymousNumberError` exception
**Raw API**
+
- `FragmentClient.call(method, data, *, page_url)` — raw request to any Fragment API method
- `FRAGMENT_BASE_URL` constant — base URL shared across all page constants and headers
**Examples**
+
- `examples/client/` — `wallet_info.py` (wallet info), `raw_api_call.py` (raw API call)
- `examples/numbers/` — `manage_number.py` (login code fetch, session termination)
- `examples/auctions/` — `search_usernames.py`, `search_numbers.py`, `search_gifts.py` (marketplace search with pagination)
- `examples/purchase/` — `send_stars.py`, `send_premium.py`, `topup_ton_balance.py`, `run_stars_giveaway.py`, `run_premium_giveaway.py`, `recharge_ads_balance.py`
### Changed
+
- All result types now expose a unified `amount` field (`months` and `stars` removed)
- `__repr__` includes the unit — `3 months`, `500 stars`, etc.
- `timestamp` removed from all result dataclasses
@@ -91,15 +125,18 @@ and this project uses [Calendar Versioning](https://calver.org/) (`YYYY.MINOR.MI
## [2026.0.2] — 2026-03-20
### Added
+
- `timeout` parameter on `FragmentClient` (default `30.0` s) — passed through to every HTTP request
### Changed
+
- Cookie validation: narrowed type internally so no `# type: ignore` is needed in `FragmentClient.__init__`
- `WALLET_CLASSES` typed as `dict[str, Any]` so mypy resolves `from_mnemonic` correctly
- All four `examples/` files updated to `async with FragmentClient`, f-strings, and aligned error messages
- README usage section rewritten with a single comprehensive `async with` example
### Fixed
+
- mypy: missing return path in `process_transaction` after retry loop
- mypy: `cookies` union-attr error in `FragmentClient.__init__`
@@ -108,6 +145,7 @@ and this project uses [Calendar Versioning](https://calver.org/) (`YYYY.MINOR.MI
## [2026.0.1] — 2026-03-16
### Added
+
- Initial stable release of `pyfragment`
- `FragmentClient` — async client for the Fragment.com API with context manager support (`async with`)
- `purchase_premium(username, months)` — purchase Telegram Premium for any user (3, 6, or 12 months)
diff --git a/README.md b/README.md
index bb94671..c676e60 100644
--- a/README.md
+++ b/README.md
@@ -4,15 +4,14 @@
Fragment API
- 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.
+ Async Python client for the Fragment API. Buy Stars and Premium, top up TON and Ads balances, run giveaways, manage anonymous numbers, and search Fragment listings.
[](https://pypi.org/project/pyfragment/)
-[](https://pypi.org/project/pyfragment/)
+[](https://pepy.tech/projects/pyfragment)
[](https://python.org)
-[](LICENSE)
-[](https://github.com/bohd4nx/pyfragment/stargazers)
-[](https://github.com/bohd4nx/pyfragment/actions)
+[](https://github.com/bohd4nx/pyfragment/actions)
+[](https://github.com/bohd4nx/pyfragment/blob/master/LICENSE)
[Report Bug](https://github.com/bohd4nx/pyfragment/issues) · [Request Feature](https://github.com/bohd4nx/pyfragment/issues) · [**Donate TON**](https://app.tonkeeper.com/transfer/UQCppfw5DxWgdVHf3zkmZS8k1mt9oAUYxQLwq2fz3nhO8No5)
@@ -75,22 +74,13 @@ Refresh when you get authentication errors.
```python
import asyncio
-from pyfragment import (
- FragmentClient,
- FragmentError, # base — catches everything below
- UserNotFoundError, # username doesn't exist on Fragment
- WalletError, # insufficient balance or misconfiguration
- CookieError, # cookies are missing or expired
- TransactionError, # on-chain broadcast failed
- ConfigurationError, # invalid argument (months, amount, etc.)
- FragmentAPIError, # unexpected Fragment API response
-)
+from pyfragment import FragmentClient
async def main() -> None:
async with FragmentClient(
- seed="word1 word2 ... word24", # 24-word TON wallet mnemonic
- api_key="YOUR_TONAPI_KEY", # from tonconsole.com
+ seed="word1 word2 ... word24",
+ api_key="YOUR_TONAPI_KEY",
cookies={
"stel_ssid": "...",
"stel_dt": "...",
@@ -98,40 +88,23 @@ async def main() -> None:
"stel_ton_token": "...",
},
) as client:
- try:
- # Purchase 6 months of Telegram Premium
- result = await client.purchase_premium("@username", months=6)
- print(f"{result.amount} months of Premium successfully sent to {result.username} | tx: {result.transaction_id}")
+ recipient = "https://t.me/username" # also supports: @username, username
- # Purchase 500 Stars
- result = await client.purchase_stars("@username", amount=500)
- print(f"{result.amount} Stars successfully sent to {result.username} | tx: {result.transaction_id}")
+ stars = await client.purchase_stars(recipient, amount=500, payment_method="usdt_ton")
+ print(f"Stars sent: {stars.amount} to {stars.username} | tx: {stars.transaction_id}")
- # Top up 10 TON to Telegram balance
- # wallet must hold at least amount + ~0.056 TON for gas
- result = await client.topup_ton("@username", amount=10)
- print(f"{result.amount} TON successfully sent to {result.username} | tx: {result.transaction_id}")
-
- except UserNotFoundError:
- print(f"User was not found on fragment.com — check the username and try again.")
- except WalletError as e:
- print(f"Wallet error — insufficient balance or misconfiguration: {e}")
- except CookieError:
- print("Authentication failed — session cookies are missing or expired. Refresh them and retry.")
- except TransactionError as e:
- print(f"Transaction failed to broadcast on-chain: {e}")
- except ConfigurationError as e:
- print(f"Invalid argument: {e}")
- except FragmentAPIError as e:
- print(f"Unexpected response from Fragment API: {e}")
- except FragmentError as e:
- # catch-all for any other pyfragment error
- print(f"Unexpected error: {e}")
+ premium = await client.purchase_premium(recipient, months=6, payment_method="ton")
+ print(f"Premium sent: {premium.amount} months to {premium.username} | tx: {premium.transaction_id}")
asyncio.run(main())
```
+Full runnable examples:
+
+- https://github.com/bohd4nx/pyfragment/tree/master/examples
+- `examples/`
+
---
diff --git a/examples/purchase/run_premium_giveaway.py b/examples/purchase/run_premium_giveaway.py
index d0a39b8..9fb8565 100644
--- a/examples/purchase/run_premium_giveaway.py
+++ b/examples/purchase/run_premium_giveaway.py
@@ -3,6 +3,8 @@ 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.
+payment_method can be "ton" or "usdt_ton".
+Channel can be "@channel", "channel", or "https://t.me/channel".
"""
import asyncio
@@ -24,15 +26,21 @@ COOKIES = {
"stel_ton_token": "YOUR_STEL_TON_TOKEN",
}
-CHANNEL = "@channel"
+CHANNEL = "https://t.me/channel"
WINNERS = 10 # 1–24 000
MONTHS = 3 # 3, 6 or 12
+PAYMENT_METHOD = "ton" # "ton" or "usdt_ton"
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)
+ result = await client.giveaway_premium(
+ CHANNEL,
+ winners=WINNERS,
+ months=MONTHS,
+ payment_method=PAYMENT_METHOD,
+ )
except UserNotFoundError:
print(f"Channel {CHANNEL} was not found on fragment.com — check the username and try again.")
return
diff --git a/examples/purchase/run_stars_giveaway.py b/examples/purchase/run_stars_giveaway.py
index 5a73310..7f98ed9 100644
--- a/examples/purchase/run_stars_giveaway.py
+++ b/examples/purchase/run_stars_giveaway.py
@@ -3,6 +3,8 @@ 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.
+payment_method can be "ton" or "usdt_ton".
+Channel can be "@channel", "channel", or "https://t.me/channel".
"""
import asyncio
@@ -24,15 +26,21 @@ COOKIES = {
"stel_ton_token": "YOUR_STEL_TON_TOKEN",
}
-CHANNEL = "@channel"
+CHANNEL = "https://t.me/channel"
WINNERS = 3 # 1–5
AMOUNT = 1000 # 500–1 000 000 stars per winner
+PAYMENT_METHOD = "usdt_ton" # "ton" or "usdt_ton"
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)
+ result = await client.giveaway_stars(
+ CHANNEL,
+ winners=WINNERS,
+ amount=AMOUNT,
+ payment_method=PAYMENT_METHOD,
+ )
except UserNotFoundError:
print(f"Channel {CHANNEL} was not found on fragment.com — check the username and try again.")
return
diff --git a/examples/purchase/send_premium.py b/examples/purchase/send_premium.py
index f82f1ed..a28cfa7 100644
--- a/examples/purchase/send_premium.py
+++ b/examples/purchase/send_premium.py
@@ -3,6 +3,8 @@ Example: purchase Telegram Premium for a user.
Supported durations: 3, 6, or 12 months.
Set show_sender=False to send anonymously.
+payment_method can be "ton" or "usdt_ton".
+Username can be "@username", "username", or "https://t.me/username".
"""
import asyncio
@@ -24,14 +26,20 @@ COOKIES = {
"stel_ton_token": "YOUR_STEL_TON_TOKEN",
}
-USERNAME = "@username"
+USERNAME = "https://t.me/username"
MONTHS = 3 # 3, 6 or 12
+PAYMENT_METHOD = "ton" # "ton" or "usdt_ton"
async def main() -> None:
async with FragmentClient(seed=SEED, api_key=API_KEY, cookies=COOKIES) as client:
try:
- result = await client.purchase_premium(USERNAME, months=MONTHS, show_sender=True)
+ result = await client.purchase_premium(
+ USERNAME,
+ months=MONTHS,
+ show_sender=True,
+ payment_method=PAYMENT_METHOD,
+ )
except UserNotFoundError:
print(f"User {USERNAME} was not found on fragment.com — check the username and try again.")
return
diff --git a/examples/purchase/send_stars.py b/examples/purchase/send_stars.py
index 76b616a..dbe5664 100644
--- a/examples/purchase/send_stars.py
+++ b/examples/purchase/send_stars.py
@@ -3,6 +3,8 @@ Example: purchase Telegram Stars for a user.
Amount must be an integer between 50 and 1 000 000.
Set show_sender=False to send anonymously.
+payment_method can be "ton" or "usdt_ton".
+Username can be "@username", "username", or "https://t.me/username".
"""
import asyncio
@@ -24,14 +26,20 @@ COOKIES = {
"stel_ton_token": "YOUR_STEL_TON_TOKEN",
}
-USERNAME = "@username"
+USERNAME = "https://t.me/username"
AMOUNT = 500 # 50–1 000 000 stars
+PAYMENT_METHOD = "usdt_ton" # "ton" or "usdt_ton"
async def main() -> None:
async with FragmentClient(seed=SEED, api_key=API_KEY, cookies=COOKIES) as client:
try:
- result = await client.purchase_stars(USERNAME, amount=AMOUNT, show_sender=True)
+ result = await client.purchase_stars(
+ USERNAME,
+ amount=AMOUNT,
+ show_sender=True,
+ payment_method=PAYMENT_METHOD,
+ )
except UserNotFoundError:
print(f"User {USERNAME} was not found on fragment.com — check the username and try again.")
return
diff --git a/pyfragment/client.py b/pyfragment/client.py
index 43e9adc..2243957 100644
--- a/pyfragment/client.py
+++ b/pyfragment/client.py
@@ -136,7 +136,7 @@ class FragmentClient:
"""Gift Telegram Premium to a user.
Args:
- username: Recipient's Telegram username (with or without ``@``).
+ username: Recipient identifier — ``@username``, ``username``, or ``https://t.me/username``.
months: Duration — ``3``, ``6``, or ``12``.
show_sender: Show your name as the sender. Defaults to ``True``.
payment_method: Payment currency — ``"ton"`` (default) or ``"usdt_ton"``.
@@ -156,7 +156,7 @@ class FragmentClient:
"""Send Telegram Stars to a user.
Args:
- username: Recipient's Telegram username (with or without ``@``).
+ username: Recipient identifier — ``@username``, ``username``, or ``https://t.me/username``.
amount: Number of stars — integer from ``50`` to ``1 000 000``.
show_sender: Show your name as the gift sender. Defaults to ``True``.
payment_method: Payment currency — ``"ton"`` (default) or ``"usdt_ton"``.
@@ -211,7 +211,7 @@ class FragmentClient:
"""Run a Telegram Stars giveaway for a channel.
Args:
- channel: Channel username (with or without ``@``).
+ channel: Channel identifier — ``@channel``, ``channel``, or ``https://t.me/channel``.
winners: Number of winners — integer from ``1`` to ``5``.
amount: Stars each winner receives — integer from ``500`` to ``1 000 000``.
payment_method: Payment currency — ``"ton"`` (default) or ``"usdt_ton"``.
@@ -232,7 +232,7 @@ class FragmentClient:
"""Run a Telegram Premium giveaway for a channel.
Args:
- channel: Channel username (with or without ``@``).
+ channel: Channel identifier — ``@channel``, ``channel``, or ``https://t.me/channel``.
winners: Number of winners — positive integer.
months: Premium duration per winner — ``3``, ``6``, or ``12``. Defaults to ``3``.
payment_method: Payment currency — ``"ton"`` (default) or ``"usdt_ton"``.
diff --git a/pyfragment/methods/giveaway_premium.py b/pyfragment/methods/giveaway_premium.py
index 79534c4..c9b8a0d 100644
--- a/pyfragment/methods/giveaway_premium.py
+++ b/pyfragment/methods/giveaway_premium.py
@@ -30,7 +30,7 @@ async def giveaway_premium(
Args:
client: Authenticated :class:`FragmentClient` instance.
- channel: Channel username (with or without ``@``).
+ channel: Channel identifier — ``@channel``, ``channel``, or ``https://t.me/channel``.
winners: Number of winners — integer from ``1`` to ``24 000``.
months: Premium duration per winner — ``3``, ``6``, or ``12``. Defaults to ``3``.
payment_method: Payment currency — ``"ton"`` (default) or ``"usdt_ton"``.
diff --git a/pyfragment/methods/giveaway_stars.py b/pyfragment/methods/giveaway_stars.py
index 77ed64d..83369a8 100644
--- a/pyfragment/methods/giveaway_stars.py
+++ b/pyfragment/methods/giveaway_stars.py
@@ -30,7 +30,7 @@ async def giveaway_stars(
Args:
client: Authenticated :class:`FragmentClient` instance.
- channel: Channel username (with or without ``@``).
+ channel: Channel identifier — ``@channel``, ``channel``, or ``https://t.me/channel``.
winners: Number of winners — integer from ``1`` to ``5``.
amount: Stars each winner receives — integer from ``500`` to ``1 000 000``.
payment_method: Payment currency — ``"ton"`` (default) or ``"usdt_ton"``.
diff --git a/pyfragment/methods/purchase_premium.py b/pyfragment/methods/purchase_premium.py
index 79edd42..6ba68d3 100644
--- a/pyfragment/methods/purchase_premium.py
+++ b/pyfragment/methods/purchase_premium.py
@@ -31,7 +31,7 @@ async def purchase_premium(
Args:
client: Authenticated :class:`FragmentClient` instance.
- username: Recipient's Telegram username (with or without ``@``).
+ username: Recipient identifier — ``@username``, ``username``, or ``https://t.me/username``.
months: Premium duration — ``3``, ``6``, or ``12``.
show_sender: Show your name as the gift sender. Defaults to ``True``.
payment_method: Payment currency — ``"ton"`` (default) or ``"usdt_ton"``.
diff --git a/pyfragment/methods/purchase_stars.py b/pyfragment/methods/purchase_stars.py
index c6af17f..27dfc58 100644
--- a/pyfragment/methods/purchase_stars.py
+++ b/pyfragment/methods/purchase_stars.py
@@ -27,7 +27,7 @@ async def purchase_stars(
Args:
client: Authenticated :class:`FragmentClient` instance.
- username: Recipient's Telegram username (with or without ``@``).
+ username: Recipient identifier — ``@username``, ``username``, or ``https://t.me/username``.
amount: Number of Stars to send — integer from ``50`` to ``1 000 000``.
show_sender: Show your name as the gift sender. Defaults to ``True``.
payment_method: Payment currency — ``"ton"`` (default) or ``"usdt_ton"``.
diff --git a/pyproject.toml b/pyproject.toml
index d9fe781..3ed8463 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
[project]
name = "pyfragment"
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."
+description = "Async Python client for the Fragment API. Buy Stars and Premium, top up TON and Ads balances, run giveaways, manage anonymous numbers, and search Fragment listings."
readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.10"
@@ -14,19 +14,23 @@ keywords = [
"fragment",
"fragment-api",
"telegram",
+ "telegram-api",
"telegram-stars",
"telegram-premium",
+ "telegram-giveaway",
+ "telegram-ads",
"ton",
+ "ton-blockchain",
"tonkeeper",
"tonapi",
- "crypto",
- "blockchain",
+ "anonymous-numbers",
+ "username-auctions",
+ "gift-marketplace",
+ "crypto-payments",
+ "nft-marketplace",
"web3",
- "giveaway",
- "anonymous-number",
- "username",
- "nft",
- "async",
+ "python-client",
+ "typed",
"asyncio",
]
classifiers = [