mirror of
https://github.com/bohd4nx/FragmentAPI.git
synced 2026-07-25 06:14:29 +00:00
Refactor examples: reorganize and update scripts for managing Telegram services
- Removed outdated example scripts for managing anonymous numbers, making raw API calls, fetching wallet info, and various giveaways. - Introduced new scripts for searching gifts, numbers, and usernames in the Fragment marketplace. - Added examples for recharging Telegram Ads accounts and managing user balances with TON. - Updated example scripts to improve clarity and maintainability, ensuring they align with the latest API changes.
This commit is contained in:
+25
-33
@@ -12,51 +12,43 @@ and this project uses [Calendar Versioning](https://calver.org/) (`YYYY.MINOR.MI
|
||||
### Added
|
||||
|
||||
**Giveaways**
|
||||
- `giveaway_stars(channel, winners, amount)` — run a Telegram Stars giveaway for a channel (1–5 winners, 500–1 000 000 stars each); raises `UserNotFoundError` if the channel is not found on Fragment, `ConfigurationError` if `winners` or `amount` are out of range
|
||||
- `giveaway_premium(channel, winners, months)` — run a Telegram Premium giveaway for a channel (1–24 000 winners, 3/6/12 months each); raises `UserNotFoundError` if the channel is not found on Fragment, `ConfigurationError` if `winners` or `months` are invalid
|
||||
- `StarsGiveawayResult` and `PremiumGiveawayResult` result types
|
||||
- `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)` — add funds to your own Telegram Ads account; `account` is the channel or bot username linked to your Ads account; raises `ConfigurationError` if `amount` is out of range (1–1 000 000 000 TON)
|
||||
- `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 the Fragment marketplace for Telegram usernames; `sort` is one of `"price_desc"`, `"price_asc"`, `"listed"`, `"ending"`; `filter` is one of `""`, `"auction"`, `"sale"`, `"sold"`; supports pagination via `offset_id`; returns parsed item dicts with `slug`, `name`, `status`, `price`, `date`
|
||||
- `search_numbers(query?, sort?, filter?, offset_id?)` — search the Fragment marketplace for anonymous Telegram numbers; same `sort` / `filter` / `offset_id` semantics as `search_usernames`
|
||||
- `search_gifts(query?, collection?, sort?, filter?, view?, attr?, offset?)` — search the Fragment gifts marketplace; `collection` is the gift collection slug (e.g. `"artisanbrick"`); `attr` accepts trait filters as `{"Model": ["Foosball"], "Backdrop": ["Celtic Blue"]}`; supports integer-based pagination via `offset`; returns parsed item dicts with `slug`, `name`, `status`, `price`, `date`
|
||||
- `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
|
||||
|
||||
**Raw API access**
|
||||
- `FragmentClient.call(method, data, *, page_url)` — send a raw request to any Fragment API method without waiting for a library update
|
||||
- `FRAGMENT_BASE_URL` constant — single source of truth for the Fragment base URL used across all page constants and headers
|
||||
**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
|
||||
|
||||
**Anonymous number management**
|
||||
- `get_login_code(number)` — fetch the current pending login code for an anonymous number
|
||||
- `toggle_login_codes(number, can_receive)` — enable or disable login code delivery for an anonymous number
|
||||
- `terminate_sessions(number)` — terminate all active Telegram sessions for an anonymous number (two-step flow handled internally)
|
||||
- `LoginCodeResult` and `TerminateSessionsResult` result types
|
||||
- `AnonymousNumberError` exception — raised when a number is not owned or session termination fails
|
||||
**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/giveaway_stars.py` — Stars giveaway with `UserNotFoundError` / `ConfigurationError` handling
|
||||
- `examples/giveaway_premium.py` — Premium giveaway with `UserNotFoundError` / `ConfigurationError` handling
|
||||
- `examples/call.py` — raw API call via `client.call()`
|
||||
- `examples/anonymous_number.py` — login code fetch and session termination with `AnonymousNumberError` handling
|
||||
- `examples/recharge_ads.py` — self-service Ads recharge with `ConfigurationError` / `WalletError` handling
|
||||
- `examples/search_usernames.py` — username marketplace search with pagination
|
||||
- `examples/search_numbers.py` — number marketplace search with pagination
|
||||
- `examples/search_gifts.py` — gifts marketplace search with collection, attr, and pagination
|
||||
- `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 (`PremiumResult`, `StarsResult`, `StarsGiveawayResult`, `PremiumGiveawayResult`) now use a single unified `amount` field instead of `months`, `stars` — consistent API across every method
|
||||
- `__repr__` on result types now includes the unit (`3 months`, `500 stars`) for clarity
|
||||
- Method module files renamed to match their function: `premium.py` → `purchase_premium.py`, `stars.py` → `purchase_stars.py`, `ton.py` → `topup_ton.py`
|
||||
- `timestamp` field removed from all result dataclasses
|
||||
- All page URL constants (`STARS_PAGE`, `PREMIUM_PAGE`, etc.) now built from `FRAGMENT_BASE_URL` instead of hardcoded strings
|
||||
- `STARS_REVENUE_PAGE` and `ADS_PAGE` constants replaced by a single `ADS_TOPUP_PAGE` shared by both `topup_ton` and `recharge_ads`
|
||||
- `TransactionError` now includes an SSL hint when a broadcast fails due to certificate verification errors
|
||||
- `TransactionError.DUPLICATE_SEQNO` — dedicated error raised after 3 failed attempts due to a `406 Duplicate msg_seqno` response from the TON network; broadcast is automatically retried (up to 2 retries, 2 s apart) before giving up
|
||||
- All error message templates rewritten to follow a "what happened → why → what to do" pattern — every template is now actionable and includes a fix hint where applicable
|
||||
- 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
|
||||
- All page URL constants built from `FRAGMENT_BASE_URL`;
|
||||
- `TransactionError` includes an SSL hint; `DUPLICATE_SEQNO` variant auto-retried up to 2 times (2 s apart)
|
||||
- Error messages rewritten: "what happened → why → what to do"
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -28,12 +28,16 @@ FILTER = "" # "", "auction", "sale", "sold" — or omit
|
||||
|
||||
async def main() -> None:
|
||||
async with FragmentClient(seed=SEED, api_key=API_KEY, cookies=COOKIES) as client:
|
||||
result: GiftsResult = await client.search_gifts(QUERY, collection=COLLECTION, sort=SORT, filter=FILTER)
|
||||
result: GiftsResult = await client.search_gifts(
|
||||
QUERY, collection=COLLECTION, sort=SORT, filter=FILTER
|
||||
)
|
||||
|
||||
print(f"Found {len(result.items)} result(s):")
|
||||
for item in result.items:
|
||||
price = f"{item['price']} TON" if item["price"] else "n/a"
|
||||
print(f" {item['name']:30s} {item['status'] or '':15s} {price:12s} {item['date'] or '—'}")
|
||||
print(
|
||||
f" {item['name']:30s} {item['status'] or '':15s} {price:12s} {item['date'] or '—'}"
|
||||
)
|
||||
|
||||
if result.next_offset:
|
||||
print(f"\nMore results available — next page offset: {result.next_offset}")
|
||||
@@ -26,15 +26,21 @@ FILTER = "" # "", "auction", "sale", "sold" — or omit
|
||||
|
||||
async def main() -> None:
|
||||
async with FragmentClient(seed=SEED, api_key=API_KEY, cookies=COOKIES) as client:
|
||||
result: NumbersResult = await client.search_numbers(QUERY, sort=SORT, filter=FILTER)
|
||||
result: NumbersResult = await client.search_numbers(
|
||||
QUERY, sort=SORT, filter=FILTER
|
||||
)
|
||||
|
||||
print(f"Found {len(result.items)} result(s):")
|
||||
for item in result.items:
|
||||
price = f"{item['price']} TON" if item["price"] else "n/a"
|
||||
print(f" {item['name']:20s} {item['status'] or '':15s} {price:10s} {item['date'] or '—'}")
|
||||
print(
|
||||
f" {item['name']:20s} {item['status'] or '':15s} {price:10s} {item['date'] or '—'}"
|
||||
)
|
||||
|
||||
if result.next_offset_id:
|
||||
print(f"\nMore results available — next page offset: {result.next_offset_id}")
|
||||
print(
|
||||
f"\nMore results available — next page offset: {result.next_offset_id}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -26,15 +26,21 @@ FILTER = "auction" # "", "auction", "sale", "sold" — or omit
|
||||
|
||||
async def main() -> None:
|
||||
async with FragmentClient(seed=SEED, api_key=API_KEY, cookies=COOKIES) as client:
|
||||
result: UsernamesResult = await client.search_usernames(QUERY, sort=SORT, filter=FILTER)
|
||||
result: UsernamesResult = await client.search_usernames(
|
||||
QUERY, sort=SORT, filter=FILTER
|
||||
)
|
||||
|
||||
print(f"Found {len(result.items)} result(s):")
|
||||
for item in result.items:
|
||||
price = f"{item['price']} TON" if item["price"] else "n/a"
|
||||
print(f" {item['name']:20s} {item['status'] or '':15s} {price:10s} {item['date'] or '—'}")
|
||||
print(
|
||||
f" {item['name']:20s} {item['status'] or '':15s} {price:10s} {item['date'] or '—'}"
|
||||
)
|
||||
|
||||
if result.next_offset_id:
|
||||
print(f"\nMore results available — next page offset: {result.next_offset_id}")
|
||||
print(
|
||||
f"\nMore results available — next page offset: {result.next_offset_id}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -27,14 +27,21 @@ async def main() -> None:
|
||||
# Fetch the latest login code
|
||||
result = await client.get_login_code(NUMBER)
|
||||
if result.code:
|
||||
print(f"Login code for {result.number}: {result.code} ({result.active_sessions} active session(s))")
|
||||
print(
|
||||
f"Login code for {result.number}: {result.code} ({result.active_sessions} active session(s))"
|
||||
)
|
||||
else:
|
||||
print(f"No pending login code for {result.number} ({result.active_sessions} active session(s))")
|
||||
print(
|
||||
f"No pending login code for {result.number} ({result.active_sessions} active session(s))"
|
||||
)
|
||||
|
||||
# Terminate all active sessions
|
||||
try:
|
||||
terminated = await client.terminate_sessions(NUMBER)
|
||||
print(f"Sessions terminated for {terminated.number}" + (f": {terminated.message}" if terminated.message else ""))
|
||||
print(
|
||||
f"Sessions terminated for {terminated.number}"
|
||||
+ (f": {terminated.message}" if terminated.message else "")
|
||||
)
|
||||
except AnonymousNumberError as e:
|
||||
print(f"Could not terminate sessions: {e}")
|
||||
|
||||
@@ -7,7 +7,12 @@ Your wallet must hold at least the recharge amount + ~0.056 TON for gas.
|
||||
|
||||
import asyncio
|
||||
|
||||
from pyfragment import AdsRechargeResult, ConfigurationError, FragmentClient, WalletError
|
||||
from pyfragment import (
|
||||
AdsRechargeResult,
|
||||
ConfigurationError,
|
||||
FragmentClient,
|
||||
WalletError,
|
||||
)
|
||||
|
||||
SEED = "word1 word2 ... word24"
|
||||
API_KEY = "YOUR_TONAPI_KEY"
|
||||
@@ -25,7 +30,9 @@ AMOUNT = 10 # 1–1 000 000 000 TON
|
||||
async def main() -> None:
|
||||
async with FragmentClient(seed=SEED, api_key=API_KEY, cookies=COOKIES) as client:
|
||||
try:
|
||||
result: AdsRechargeResult = await client.recharge_ads(ACCOUNT, amount=AMOUNT)
|
||||
result: AdsRechargeResult = await client.recharge_ads(
|
||||
ACCOUNT, amount=AMOUNT
|
||||
)
|
||||
except WalletError as e:
|
||||
print(f"Wallet error — insufficient balance or misconfiguration: {e}")
|
||||
return
|
||||
@@ -33,7 +40,9 @@ async def main() -> None:
|
||||
print(f"Invalid argument: {e}")
|
||||
return
|
||||
|
||||
print(f"{result.amount} TON recharged to Ads account {ACCOUNT} | tx: {result.transaction_id}")
|
||||
print(
|
||||
f"{result.amount} TON recharged to Ads account {ACCOUNT} | tx: {result.transaction_id}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -26,9 +26,13 @@ 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)
|
||||
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.")
|
||||
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}")
|
||||
@@ -26,9 +26,13 @@ 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)
|
||||
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.")
|
||||
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}")
|
||||
@@ -25,15 +25,21 @@ 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.purchase_premium(USERNAME, months=MONTHS, show_sender=True)
|
||||
result = await client.purchase_premium(
|
||||
USERNAME, months=MONTHS, show_sender=True
|
||||
)
|
||||
except UserNotFoundError:
|
||||
print(f"User {USERNAME} was not found on fragment.com — check the username and try again.")
|
||||
print(
|
||||
f"User {USERNAME} 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"{result.amount} months of Premium successfully sent to {result.username} | tx: {result.transaction_id}")
|
||||
print(
|
||||
f"{result.amount} months of Premium successfully sent to {result.username} | tx: {result.transaction_id}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -25,15 +25,21 @@ AMOUNT = 500 # 50–1 000 000 stars
|
||||
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
|
||||
)
|
||||
except UserNotFoundError:
|
||||
print(f"User {USERNAME} was not found on fragment.com — check the username and try again.")
|
||||
print(
|
||||
f"User {USERNAME} 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"{result.amount} Stars successfully sent to {result.username} | tx: {result.transaction_id}")
|
||||
print(
|
||||
f"{result.amount} Stars successfully sent to {result.username} | tx: {result.transaction_id}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -9,7 +9,12 @@ Your wallet must hold at least the topup amount + ~0.056 TON for gas.
|
||||
|
||||
import asyncio
|
||||
|
||||
from pyfragment import ConfigurationError, FragmentClient, UserNotFoundError, WalletError
|
||||
from pyfragment import (
|
||||
ConfigurationError,
|
||||
FragmentClient,
|
||||
UserNotFoundError,
|
||||
WalletError,
|
||||
)
|
||||
|
||||
SEED = "word1 word2 ... word24"
|
||||
API_KEY = "YOUR_TONAPI_KEY"
|
||||
@@ -29,7 +34,9 @@ async def main() -> None:
|
||||
try:
|
||||
result = await client.topup_ton(USERNAME, amount=AMOUNT, show_sender=True)
|
||||
except UserNotFoundError:
|
||||
print(f"User {USERNAME} was not found on fragment.com — check the username and try again.")
|
||||
print(
|
||||
f"User {USERNAME} was not found on fragment.com — check the username and try again."
|
||||
)
|
||||
return
|
||||
except WalletError as e:
|
||||
print(f"Wallet error — insufficient balance or misconfiguration: {e}")
|
||||
@@ -38,7 +45,9 @@ async def main() -> None:
|
||||
print(f"Invalid argument: {e}")
|
||||
return
|
||||
|
||||
print(f"{result.amount} TON successfully topped up for {result.username} | tx: {result.transaction_id}")
|
||||
print(
|
||||
f"{result.amount} TON successfully topped up for {result.username} | tx: {result.transaction_id}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Reference in New Issue
Block a user