mirror of
https://github.com/vibe-existing/pyfragment.git
synced 2026-07-25 06:54:31 +00:00
refactor: harden client, clean tests, and fix timeouts
- Security & validation: tighten hash regex, add HTTP timeouts to all requests, pass client.timeout through get_fragment_hash and AsyncClient - Constants: move all constants to types/constants.py; remove re-exports from types/__init__.py; add DEFAULT_TIMEOUT, REQUIRED_COOKIE_KEYS - FragmentClient: add timeout param (default 30 s); async-context-manager support; remove WALLET_CLASSES from public API - Exceptions: remove dead INVALID_USERNAME constant (Fragment validates server-side); keep full hierarchy intact - Tests: add 006_test_methods_mock.py (6 mock tests for all 3 methods); DRY-refactor 004_test_balance.py (_patch_wallet context manager); clean up 005_test_methods.py (remove fragile network test, rename tests) - Examples: switch all 4 examples to async-with; align error messages; replace %-format with f-strings - README: rewrite usage section with single comprehensive async-with example covering all 3 methods and full exception hierarchy - CI: add mypy step to lint job; add pytest-mock and mypy to dev deps; set FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 on all jobs; fix COOKIES_JSON to job-level env var
This commit is contained in:
@@ -22,36 +22,58 @@
|
||||
|
||||
---
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 💰 **TON Advertisement Topups** — Top up Telegram Ads balance (1–1,000,000,000 TON)
|
||||
- 👑 **Telegram Premium** — Purchase Premium for any user (3, 6, or 12 months)
|
||||
- ⭐ **Telegram Stars Purchases** — Purchase Stars for any Telegram user (50–1,000,000 Stars)
|
||||
- 🔐 **Multi-wallet support** — V4R2 and V5R1 wallet contract versions
|
||||
- ⚡ **Async-first** — Built on `httpx` and `asyncio`
|
||||
|
||||
---
|
||||
|
||||
## 📦 Installation
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install pyfragment
|
||||
```
|
||||
|
||||
Requires **Python 3.12+**.
|
||||
Requires Python 3.12+.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
## Configuration
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
| ---------------- | ------------- | -------- | -------------------------------------------------------- |
|
||||
| `seed` | `str` | — | 24-word TON wallet mnemonic |
|
||||
| `api_key` | `str` | — | Tonapi key from [tonconsole.com](https://tonconsole.com) |
|
||||
| `cookies` | `dict \| str` | — | Fragment session cookies |
|
||||
| `wallet_version` | `str` | `"V5R1"` | `"V4R2"` or `"V5R1"` |
|
||||
| `timeout` | `float` | `30.0` | HTTP request timeout in seconds |
|
||||
|
||||
---
|
||||
|
||||
## Credentials
|
||||
|
||||
**Fragment cookies** — log in to [fragment.com](https://fragment.com), install [Cookie Editor](https://chromewebstore.google.com/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm), and export these four keys: `stel_ssid`, `stel_dt`, `stel_token`, `stel_ton_token`. Pass them as a `dict` or as a JSON string. Refresh when you get authentication errors.
|
||||
|
||||
**Tonapi key** — generate at [tonconsole.com](https://tonconsole.com).
|
||||
|
||||
**Seed phrase** — 24-word mnemonic from your TON wallet (Tonkeeper → Settings → Backup). Never share it.
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from pyfragment import FragmentClient
|
||||
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
|
||||
)
|
||||
|
||||
async def main():
|
||||
|
||||
async def main() -> None:
|
||||
async with FragmentClient(
|
||||
seed="word1 word2 ... word24",
|
||||
api_key="YOUR_TONAPI_KEY",
|
||||
seed="word1 word2 ... word24", # 24-word TON wallet mnemonic
|
||||
api_key="YOUR_TONAPI_KEY", # from tonconsole.com
|
||||
cookies={
|
||||
"stel_ssid": "...",
|
||||
"stel_dt": "...",
|
||||
@@ -59,110 +81,40 @@ async def main():
|
||||
"stel_ton_token": "...",
|
||||
},
|
||||
) as client:
|
||||
# Purchase 6 months of Telegram Premium
|
||||
result = await client.purchase_premium("@username", months=6)
|
||||
print(result.transaction_id)
|
||||
try:
|
||||
# Purchase 6 months of Telegram Premium for a user
|
||||
result = await client.purchase_premium("@username", months=6)
|
||||
print(f"{result.months} months of Premium successfully sent to {result.username} | tx: {result.transaction_id}")
|
||||
|
||||
# Purchase 500 Stars
|
||||
result = await client.purchase_stars("@username", amount=500)
|
||||
print(result.transaction_id)
|
||||
# Purchase 500 Stars for a user (50–1 000 000)
|
||||
result = await client.purchase_stars("@username", amount=500)
|
||||
print(f"{result.stars} Stars successfully sent to {result.username} | tx: {result.transaction_id}")
|
||||
|
||||
# Top up 10 TON to Telegram Ads 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}")
|
||||
|
||||
# Top up 10 TON to Ads balance
|
||||
result = await client.topup_ton("@username", amount=10)
|
||||
print(result.transaction_id)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
See the [`examples/`](examples/) folder for ready-to-run scripts.
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
### `FragmentClient` parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
| ---------------- | ------------- | -------- | -------- | -------------------------------------------------------- |
|
||||
| `seed` | `str` | ✅ | — | 24-word TON wallet mnemonic phrase |
|
||||
| `api_key` | `str` | ✅ | — | Tonapi key from [tonconsole.com](https://tonconsole.com) |
|
||||
| `cookies` | `dict \| str` | ✅ | — | Fragment session cookies (dict or JSON string) |
|
||||
| `wallet_version` | `str` | ❌ | `"V5R1"` | Wallet contract version: `"V4R2"` or `"V5R1"` |
|
||||
|
||||
### Methods
|
||||
|
||||
> Usernames can be passed with or without `@`.
|
||||
|
||||
| Method | Returns | Description | Limits |
|
||||
| -------------------------------------------------- | ---------------- | ---------------------------------- | ------------------------- |
|
||||
| `purchase_premium(username, months, show_sender=True)` | `PremiumResult` | Purchase Telegram Premium | `months`: 3, 6, or 12 |
|
||||
| `purchase_stars(username, amount, show_sender=True)` | `StarsResult` | Purchase Telegram Stars | `amount`: 50–1,000,000 |
|
||||
| `topup_ton(username, amount, show_sender=True)` | `AdsTopupResult` | Top up Telegram Ads balance | `amount`: 1–1,000,000,000 |
|
||||
| `get_wallet()` | `WalletInfo` | Get wallet address, state, balance | — |
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Getting Required Credentials
|
||||
|
||||
### 🍪 Fragment.com Cookies
|
||||
|
||||
**Prerequisites**: Log in to [fragment.com](https://fragment.com), connect your TON wallet.
|
||||
|
||||
1. Install [Cookie Editor](https://chromewebstore.google.com/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm)
|
||||
2. Open [fragment.com](https://fragment.com) while logged in
|
||||
3. Click the extension → **Export** → **Header String**
|
||||
4. Extract these four fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"stel_ssid": "...",
|
||||
"stel_dt": "...",
|
||||
"stel_token": "...",
|
||||
"stel_ton_token": "..."
|
||||
}
|
||||
```
|
||||
|
||||
> ⚠️ Cookies expire. Refresh them if you start getting `FragmentPageError` or auth errors.
|
||||
|
||||
### 🔑 Tonapi Key
|
||||
|
||||
1. Go to [tonconsole.com](https://tonconsole.com)
|
||||
2. Register and generate a new API key
|
||||
3. Pass it as `api_key` to `FragmentClient`
|
||||
|
||||
### 🌱 Wallet Seed Phrase
|
||||
|
||||
If you don't have a TON wallet, create one in [Tonkeeper](https://tonkeeper.com).
|
||||
Go to **Settings → Backup** → copy the 24 words.
|
||||
|
||||
> ⚠️ Never share your seed phrase. Store it offline.
|
||||
|
||||
### 🔐 Wallet Version
|
||||
|
||||
| Version | Use when |
|
||||
| ------- | -------------------------------------------------------------- |
|
||||
| `V5R1` | Default — Tonkeeper / MyTonWallet (wallets created after 2024) |
|
||||
| `V4R2` | Older Tonkeeper or hardware wallets |
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ Error Handling
|
||||
|
||||
All exceptions inherit from `FragmentError` — see [`pyfragment/types/exceptions.py`](pyfragment/types/exceptions.py) for the full list.
|
||||
|
||||
```python
|
||||
from pyfragment import FragmentClient, UserNotFoundError, ConfigurationError, WalletError
|
||||
|
||||
try:
|
||||
result = await client.purchase_stars("@unknown", amount=100)
|
||||
except UserNotFoundError:
|
||||
print("User not found on Fragment")
|
||||
except WalletError as e:
|
||||
print(f"Wallet issue: {e}")
|
||||
except ConfigurationError as e:
|
||||
print(f"Bad params: {e}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
Reference in New Issue
Block a user