diff --git a/.gitignore b/.gitignore
index 622263c..d8f5d48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,10 @@ logs/
# Environment variables
.env
+# Test files
+tests/
+*.test.py
+
# System files
.DS_Store
Thumbs.db
\ No newline at end of file
diff --git a/README-RU.md b/README-RU.md
deleted file mode 100644
index 02bc970..0000000
--- a/README-RU.md
+++ /dev/null
@@ -1,180 +0,0 @@
-
-

-
-
π Fragment API ΠΎΡ @bohd4nx
-
-
- ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΉ TON, ΠΏΠΎΠΊΡΠΏΠΊΠΈ Telegram Premium ΠΈ Stars ΡΠ΅ΡΠ΅Π· Fragment.com
-
-
-[Π‘ΠΎΠΎΠ±ΡΠΈΡΡ ΠΎΠ± ΠΎΡΠΈΠ±ΠΊΠ΅](https://github.com/bohd4nx/fragmentapi/issues) Β· [ΠΠ°ΠΏΡΠΎΡΠΈΡΡ ΡΡΠ½ΠΊΡΠΈΡ](https://github.com/bohd4nx/fragmentapi/issues) Β· [**ΠΠΎΠ½Π°Ρ TON**](https://app.tonkeeper.com/transfer/UQBUAa7KCx1ifmoEy6lF7j-822Dm_cE1j9SR7UWteu3jzukV)
-
-
-
----
-
-## β¨ ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ
-
-- π° **ΠΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ TON Π΄Π»Ρ ΡΠ΅ΠΊΠ»Π°ΠΌΡ** - ΠΡΠΏΡΠ°Π²ΠΊΠ° TON Π΄Π»Ρ ΡΠ΅ΠΊΠ»Π°ΠΌΠ½ΡΡ
ΠΊΠ°ΠΌΠΏΠ°Π½ΠΈΠΉ ΠΈ ΠΏΠΎΠΊΡΠΏΠΊΠΈ ΠΏΠΎΠ΄Π°ΡΠΊΠΎΠ² (1-1,000,000,000 TON)
-- π **ΠΠΎΠ΄Π°ΡΠΊΠΈ Telegram Premium** - ΠΠΎΠΊΡΠΏΠΊΠ° Premium ΠΏΠΎΠ΄ΠΏΠΈΡΠΎΠΊ(3, 6 ΠΈΠ»ΠΈ 12 ΠΌΠ΅ΡΡΡΠ΅Π²)
-- β **ΠΠΎΠΊΡΠΏΠΊΠ° Telegram Stars** - ΠΠΎΠΊΡΠΏΠΊΠ° Stars Π΄Π»Ρ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Π΅ΠΉ (50-1,000,000 Stars)
-
-## π ΠΡΡΡΡΡΠΉ ΡΡΠ°ΡΡ
-
-### 1. Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
-
-```bash
-git clone https://github.com/bohd4nx/FragmentAPI.git
-cd FragmentAPI
-pip install -r requirements.txt
-```
-
-### 2. ΠΠ°ΡΡΡΠΎΠΉΠΊΠ°
-
-Π‘ΠΊΠΎΠΏΠΈΡΡΠΉΡΠ΅ ΠΏΡΠΈΠΌΠ΅Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ ΠΈ ΠΎΡΡΠ΅Π΄Π°ΠΊΡΠΈΡΡΠΉΡΠ΅:
-
-```bash
-cp .env.example .env
-```
-
-ΠΡΡΠ΅Π΄Π°ΠΊΡΠΈΡΡΠΉΡΠ΅ ΡΠ°ΠΉΠ» `.env`:
-
-```env
-COOKIES = your_fragment_cookies_here
-
-SEED = word1 word2 word3 ... word24
-
-HASH = your_fragment_hash_here
-
-API_KEY = your_ton_api_key_here
-```
-
-### 3. ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΡΡ
Π΄Π°Π½Π½ΡΡ
-
-#### πͺ ΠΡΠΊΠΈ Fragment.com
-
-**ΠΡΠ΅Π΄Π²Π°ΡΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΡΡΠ΅Π±ΠΎΠ²Π°Π½ΠΈΡ**: ΠΠΎΠΉΠ΄ΠΈΡΠ΅ Π² ΡΠ²ΠΎΠΉ Π°ΠΊΠΊΠ°ΡΠ½Ρ Telegram ΠΈ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΡΠ΅ TON ΠΊΠΎΡΠ΅Π»ΡΠΊ, ΠΊΠΎΡΠΎΡΡΠΉ Ρ
ΠΎΡΠΈΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π΄Π»Ρ ΠΏΠ»Π°ΡΠ΅ΠΆΠ΅ΠΉ.
-
-1. **Π£ΡΡΠ°Π½ΠΎΠ²ΠΈΡΠ΅ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΠ΅ Cookie Editor**:
-
- - Π‘ΠΊΠ°ΡΠ°ΠΉΡΠ΅ ΠΈΠ· [Chrome Web Store](https://chromewebstore.google.com/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm)
- - ΠΠΎΠ±Π°Π²ΡΡΠ΅ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΠ΅ Π² Π²Π°Ρ Π±ΡΠ°ΡΠ·Π΅Ρ
-
-2. **ΠΠ·Π²Π»Π΅ΡΠ΅Π½ΠΈΠ΅ ΠΊΡΠΊΠΈ**:
- - ΠΡΠΊΡΠΎΠΉΡΠ΅ [Fragment.com](https://fragment.com) ΠΈ ΡΠ±Π΅Π΄ΠΈΡΠ΅ΡΡ, ΡΡΠΎ Π²Ρ Π²ΠΎΡΠ»ΠΈ Π² ΡΠΈΡΡΠ΅ΠΌΡ
- - ΠΠΎΠ»Π½ΠΎΡΡΡΡ ΠΎΠ±Π½ΠΎΠ²ΠΈΡΠ΅ ΡΡΡΠ°Π½ΠΈΡΡ
- - ΠΠ°ΠΆΠΌΠΈΡΠ΅ Π½Π° ΠΈΠΊΠΎΠ½ΠΊΡ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ Cookie Editor
- - ΠΠ°ΠΆΠΌΠΈΡΠ΅ ΠΊΠ½ΠΎΠΏΠΊΡ **"Export"**
- - ΠΡΠ±Π΅ΡΠΈΡΠ΅ ΡΠΎΡΠΌΠ°Ρ **"Header String"**
- - Π‘ΠΊΠΎΠΏΠΈΡΡΠΉΡΠ΅ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ β Π²ΡΡΠ°Π²ΡΡΠ΅ Π² `COOKIES` Π² Π²Π°ΡΠ΅ΠΌ `.env` ΡΠ°ΠΉΠ»Π΅
- - **ΠΠ°ΠΆΠ½ΠΎ**: ΠΠ°ΠΊΠ»ΡΡΠΈΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ Π² ΠΊΠ°Π²ΡΡΠΊΠΈ, ΡΡΠΎΠ±Ρ ΡΠΎΡΠΊΠΈ Ρ Π·Π°ΠΏΡΡΠΎΠΉ Π½Π΅ ΠΈΠ½ΡΠ΅ΡΠΏΡΠ΅ΡΠΈΡΠΎΠ²Π°Π»ΠΈΡΡ ΠΊΠ°ΠΊ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ
-
-**ΠΠΆΠΈΠ΄Π°Π΅ΠΌΡΠΉ ΡΠΎΡΠΌΠ°Ρ**:
-
-```env
-COOKIES = "stel_token=;stel_dt=;stel_ssid=;stel_ton_token="
-```
-
-#### π Seed-ΡΡΠ°Π·Π° TON ΠΊΠΎΡΠ΅Π»ΡΠΊΠ°
-
-**ΠΡΠ»ΠΈ Ρ Π²Π°Ρ Π΅ΡΡ Π½Π΅Ρ TON ΠΊΠΎΡΠ΅Π»ΡΠΊΠ°**:
-
-1. **Π‘ΠΊΠ°ΡΠ°ΠΉΡΠ΅ Tonkeeper**:
-
- - iOS: [App Store](https://apps.apple.com/app/tonkeeper/id1587742107)
- - Android: [Google Play](https://play.google.com/store/apps/details?id=com.ton_keeper)
-
-2. **Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ Π½ΠΎΠ²ΡΠΉ ΠΊΠΎΡΠ΅Π»ΡΠΊ**:
-
- - ΠΡΠΊΡΠΎΠΉΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Tonkeeper
- - ΠΠ°ΠΆΠΌΠΈΡΠ΅ **"Π‘ΠΎΠ·Π΄Π°ΡΡ Π½ΠΎΠ²ΡΠΉ ΠΊΠΎΡΠ΅Π»ΡΠΊ"**
- - **ΠΠΠΠΠ**: ΠΠ°ΠΏΠΈΡΠΈΡΠ΅ Π²Π°ΡΡ 24-ΡΠ»ΠΎΠ²Π½ΡΡ seed-ΡΡΠ°Π·Ρ Π½Π° Π±ΡΠΌΠ°Π³Π΅
- - Π₯ΡΠ°Π½ΠΈΡΠ΅ Π΅Ρ Π² Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΠΌ ΠΌΠ΅ΡΡΠ΅ - Π½ΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ Π΄Π΅Π»ΠΈΡΠ΅ΡΡ Π΅ΠΉ Π½ΠΈ Ρ ΠΊΠ΅ΠΌ!
- - ΠΠ°Π²Π΅ΡΡΠΈΡΠ΅ Π½Π°ΡΡΡΠΎΠΉΠΊΡ ΠΊΠΎΡΠ΅Π»ΡΠΊΠ°
-
-3. **ΠΠΎΠ»ΡΡΠΈΡΠ΅ Π²Π°ΡΡ seed-ΡΡΠ°Π·Ρ**:
- - ΠΡΠ»ΠΈ Ρ Π²Π°Ρ ΡΠΆΠ΅ Π΅ΡΡΡ ΠΊΠΎΡΠ΅Π»ΡΠΊ, ΠΏΠ΅ΡΠ΅ΠΉΠ΄ΠΈΡΠ΅ Π² ΠΠ°ΡΡΡΠΎΠΉΠΊΠΈ β Π Π΅Π·Π΅ΡΠ²Π½ΠΎΠ΅ ΠΊΠΎΠΏΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
- - ΠΠ²Π΅Π΄ΠΈΡΠ΅ Π²Π°Ρ ΠΏΠ°ΡΠΎΠ»Ρ
- - Π‘ΠΊΠΎΠΏΠΈΡΡΠΉΡΠ΅ 24 ΡΠ»ΠΎΠ²Π° β Π²ΡΡΠ°Π²ΡΡΠ΅ Π² `SEED` Π² Π²Π°ΡΠ΅ΠΌ `.env` ΡΠ°ΠΉΠ»Π΅
-
-**Π€ΠΎΡΠΌΠ°Ρ**: `ΡΠ»ΠΎΠ²ΠΎ1 ΡΠ»ΠΎΠ²ΠΎ2 ΡΠ»ΠΎΠ²ΠΎ3 ... ΡΠ»ΠΎΠ²ΠΎ24`
-
-#### π Π₯Π΅Ρ Fragment
-
-**ΠΡΠ΅Π΄Π²Π°ΡΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΡΡΠ΅Π±ΠΎΠ²Π°Π½ΠΈΡ**: Π‘Π½Π°ΡΠ°Π»Π° Π·Π°Π²Π΅ΡΡΠΈΡΠ΅ Π½Π°ΡΡΡΠΎΠΉΠΊΡ ΠΊΡΠΊΠΈ Π²ΡΡΠ΅.
-
-1. **ΠΠΎΠ΄ΠΊΠ»ΡΡΠΈΡΠ΅ ΠΊΠΎΡΠ΅Π»ΡΠΊ ΠΊ Fragment**:
-
- - ΠΠ΅ΡΠ΅ΠΉΠ΄ΠΈΡΠ΅ Π½Π° [Fragment.com](https://fragment.com)
- - Π£Π±Π΅Π΄ΠΈΡΠ΅ΡΡ, ΡΡΠΎ Π²Π°Ρ TON ΠΊΠΎΡΠ΅Π»ΡΠΊ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΡΠ½
- - ΠΠΎΠΉΠ΄ΠΈΡΠ΅ Ρ Π²Π°ΡΠΈΠΌ Π°ΠΊΠΊΠ°ΡΠ½ΡΠΎΠΌ Telegram
-
-2. **ΠΠ·Π²Π»Π΅ΡΠ΅Π½ΠΈΠ΅ Ρ
Π΅ΡΠ°**:
- - ΠΡΠΊΡΠΎΠΉΡΠ΅ **ΠΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊΠ°** (F12)
- - ΠΠ°ΠΆΠΌΠΈΡΠ΅ Π½Π° Π²ΠΊΠ»Π°Π΄ΠΊΡ **"Π‘Π΅ΡΡ"**
- - ΠΠ΅ΡΠ΅ΠΉΠ΄ΠΈΡΠ΅ ΠΊ [ΠΏΠΎΠΊΡΠΏΠΊΠ΅ Stars](https://fragment.com/stars/buy)
- - ΠΡΠ±Π΅ΡΠΈΡΠ΅ ΠΏΠΎΠ»ΡΡΠ°ΡΠ΅Π»Ρ ΠΊΠ°ΠΊ **"Buy for myself"**
- - ΠΠ±Π½ΠΎΠ²ΠΈΡΠ΅ ΡΡΡΠ°Π½ΠΈΡΡ (Ctrl+R / Cmd+R)
- - ΠΠ°ΠΉΠ΄ΠΈΡΠ΅ ΡΠ΅ΡΠ΅Π²ΡΠ΅ Π·Π°ΠΏΡΠΎΡΡ, ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΠΈΠ΅ `?hash=`
- - ΠΠ°ΠΉΠ΄ΠΈΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ Ρ
Π΅ΡΠ° ΠΈ ΡΠΊΠΎΠΏΠΈΡΡΠΉΡΠ΅ Π΅Π³ΠΎ β Π²ΡΡΠ°Π²ΡΡΠ΅ Π² `HASH` Π² Π²Π°ΡΠ΅ΠΌ `.env` ΡΠ°ΠΉΠ»Π΅
-
-**ΠΠΆΠΈΠ΄Π°Π΅ΠΌΡΠΉ ΡΠΎΡΠΌΠ°Ρ**: `?hash=<Π²Π°Ρ_Ρ
Π΅Ρ_Π·Π΄Π΅ΡΡ>`
-
-#### π API ΠΊΠ»ΡΡ TON
-
-1. **ΠΠΎΠ»ΡΡΠΈΡΠ΅ API ΠΊΠ»ΡΡ**:
- - ΠΠΎΡΠ΅ΡΠΈΡΠ΅ [TON Console](https://tonconsole.com)
- - Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ Π°ΠΊΠΊΠ°ΡΠ½Ρ ΠΈ Π²ΠΎΠΉΠ΄ΠΈΡΠ΅
- - Π‘Π³Π΅Π½Π΅ΡΠΈΡΡΠΉΡΠ΅ Π½ΠΎΠ²ΡΠΉ API ΠΊΠ»ΡΡ
- - Π‘ΠΊΠΎΠΏΠΈΡΡΠΉΡΠ΅ ΠΊΠ»ΡΡ β Π²ΡΡΠ°Π²ΡΡΠ΅ Π² `API_KEY` Π² Π²Π°ΡΠ΅ΠΌ `.env` ΡΠ°ΠΉΠ»Π΅
-
-**ΠΠ»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Π°**: ΠΡ ΡΠ°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ [TON API](https://tonapi.io) Π΄Π»Ρ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ API ΠΊΠ»ΡΡΠ°.
-
-### 4. ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
-
-#### ΠΠ°ΠΏΡΡΠΊ ΠΏΡΠΈΠΌΠ΅ΡΠΎΠ²
-
-```bash
-python main.py
-```
-
-#### ΠΡΠΎΠ³ΡΠ°ΠΌΠΌΠ½ΠΎΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
-
-```python
-from app.methods import FragmentTon, FragmentPremium, FragmentStars
-
-# ΠΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ TON Π΄Π»Ρ ΡΠ΅ΠΊΠ»Π°ΠΌΡ
-ton_client = FragmentTon()
-result = await ton_client.topup_ton("@username", 5)
-
-# ΠΠΎΠΊΡΠΏΠΊΠ° Premium
-premium_client = FragmentPremium()
-result = await premium_client.buy_premium("@username", 6)
-
-# ΠΠΎΠΊΡΠΏΠΊΠ° Stars
-stars_client = FragmentStars()
-result = await stars_client.buy_stars("@username", 50)
-```
-
-### ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΠΌΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ
-
-| ΠΠΏΠ΅ΡΠ°ΡΠΈΡ | ΠΠ΅ΡΠΎΠ΄ | ΠΠ°ΡΠ°ΠΌΠ΅ΡΡΡ | ΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡ |
-| ------------------- | ------------------------------- | ---------------------------------- | ------------------- |
-| **ΠΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ TON** | `topup_ton(username, amount)` | ΠΠΌΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ, ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ TON | 1-1,000,000,000 TON |
-| **ΠΠΎΠ΄Π°ΡΠΎΠΊ Premium** | `buy_premium(username, months)` | ΠΠΌΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ, Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ | 3, 6 ΠΈΠ»ΠΈ 12 ΠΌΠ΅ΡΡΡΠ΅Π² |
-| **ΠΠΎΠΊΡΠΏΠΊΠ° Stars** | `buy_stars(username, amount)` | ΠΠΌΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ, ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Stars | 50-1,000,000 Stars |
-
-### Π€ΠΎΡΠΌΠ°ΡΡ ΠΈΠΌΡΠ½ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Π΅ΠΉ
-
-ΠΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ ΠΏΡΠΈΠ½ΠΈΠΌΠ°ΡΡ ΡΠ°Π·Π»ΠΈΡΠ½ΡΠ΅ ΡΠΎΡΠΌΠ°ΡΡ ΠΈΠΌΡΠ½ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Π΅ΠΉ:
-
-- `@username` (Ρ @)
-- `username` (Π±Π΅Π· @)
-
-
-
-### Made with β€οΈ by [@bohd4nx](https://t.me/bohd4nx)
-
-**ΠΠΎΡΡΠ°Π²ΡΡΠ΅ β ΡΡΠΎΠΌΡ ΡΠ΅ΠΏΠΎΠ·ΠΈΡΠΎΡΠΈΡ, Π΅ΡΠ»ΠΈ ΠΎΠ½ ΠΎΠΊΠ°Π·Π°Π»ΡΡ ΠΏΠΎΠ»Π΅Π·Π½ΡΠΌ!**
-
-
diff --git a/app/__meta__.py b/app/__meta__.py
index 229198b..54cefbb 100644
--- a/app/__meta__.py
+++ b/app/__meta__.py
@@ -1,4 +1,4 @@
__title__ = "Fragment API by @bohd4nx"
-__version__ = "2025.1.1"
+__version__ = "2025.1.2"
__author__ = "Bohdan (bohd4nx)"
-__timestamp__ = "2025-11-03T12:00:00Z"
+__timestamp__ = "2025-11-24T12:00:00Z"
diff --git a/app/methods/premium.py b/app/methods/premium.py
index 27aefd9..819e010 100644
--- a/app/methods/premium.py
+++ b/app/methods/premium.py
@@ -1,7 +1,5 @@
import base64
import logging
-import re
-import string
import time
import httpx
@@ -9,7 +7,7 @@ from tonutils.client import TonapiClient
from tonutils.wallet import WalletV5R1
from app.core.config import Config
-from app.utils import TransactionProcessor, WalletLinker, ApiClient
+from app.utils import TransactionProcessor, WalletLinker, ApiClient, clean_decode
logger = logging.getLogger(__name__)
@@ -31,20 +29,10 @@ class FragmentPremium:
'x-requested-with': 'XMLHttpRequest'
}
- self.transaction_processor = TransactionProcessor(self.config, self._clean_decode)
+ self.transaction_processor = TransactionProcessor(self.config, clean_decode)
self.wallet_linker = WalletLinker(self.config, self.headers, self.transaction_processor)
self.api_client = ApiClient(self.config, self.headers, self.wallet_linker)
- @staticmethod
- def _clean_decode(s):
- b = base64.b64decode(re.sub(r'[^A-Za-z0-9+/=]', '', s.strip()) + "=" * (-len(s) % 4))
- t = next(
- (b[i:].decode('utf-8', 'ignore') for i in range(20) if
- b[i:].decode('utf-8', 'ignore').startswith("Telegram Premium")),
- b.decode('utf-8', 'ignore')
- )
- return ''.join(c for c in t if c in string.printable or c in '\n\r\t ').strip()
-
async def _get_account_info(self):
client = TonapiClient(api_key=self.config['api_key'], is_testnet=False)
wallet, pub_key, _, _ = WalletV5R1.from_mnemonic(client=client, mnemonic=self.config['seed'])
@@ -67,7 +55,12 @@ class FragmentPremium:
search_data = {"query": username, "months": months, "method": "searchPremiumGiftRecipient"}
search_resp = await client.post(f"https://fragment.com/api?hash={self.config['hash']}",
headers=self.headers, data=search_data)
- search_result = search_resp.json()
+
+ try:
+ search_result = search_resp.json()
+ except Exception as e:
+ logger.error(f"Failed to parse search response: {e}")
+ return {"success": False, "error": f"Invalid response from Fragment API: {str(e)}"}
recipient = search_result.get("found", {}).get("recipient")
if not recipient:
@@ -80,7 +73,14 @@ class FragmentPremium:
init_data = {"recipient": recipient, "months": months, "method": "initGiftPremiumRequest"}
init_resp = await client.post(f"https://fragment.com/api?hash={self.config['hash']}",
headers=self.headers, data=init_data)
- init_result = init_resp.json()
+
+ try:
+ init_result = init_resp.json()
+ except Exception as e:
+ logger.error(f"Failed to parse init response: {e}")
+ logger.error(f"Response status: {init_resp.status_code}")
+ logger.error(f"Response content: {init_resp.content[:200]}")
+ return {"success": False, "error": f"Invalid response from Fragment API: {str(e)}"}
req_id = init_result.get("req_id")
if not req_id:
diff --git a/app/methods/stars.py b/app/methods/stars.py
index 13feed4..2c78fa0 100644
--- a/app/methods/stars.py
+++ b/app/methods/stars.py
@@ -1,6 +1,5 @@
import base64
import logging
-import re
import time
import httpx
@@ -8,7 +7,7 @@ from tonutils.client import TonapiClient
from tonutils.wallet import WalletV5R1
from app.core.config import Config
-from app.utils import TransactionProcessor, WalletLinker, ApiClient
+from app.utils import TransactionProcessor, WalletLinker, ApiClient, clean_decode
logger = logging.getLogger(__name__)
@@ -30,18 +29,10 @@ class FragmentStars:
'x-requested-with': 'XMLHttpRequest'
}
- self.transaction_processor = TransactionProcessor(self.config, self._clean_decode)
+ self.transaction_processor = TransactionProcessor(self.config, clean_decode)
self.wallet_linker = WalletLinker(self.config, self.headers, self.transaction_processor)
self.api_client = ApiClient(self.config, self.headers, self.wallet_linker)
- @staticmethod
- def _clean_decode(s):
- s = re.sub(r'[^A-Za-z0-9+/=]', '', s.strip())
- b = base64.b64decode(s + "=" * (-len(s) % 4))
- t = b.decode('utf-8', 'ignore')
- m = re.search(r'(\d+\s+Telegram\s+Stars.*?Ref#[A-Za-z0-9]+)', t, re.S)
- return m.group(1).strip() if m else t.strip()
-
async def _get_account_info(self):
client = TonapiClient(api_key=self.config['api_key'], is_testnet=False)
wallet, pub_key, _, _ = WalletV5R1.from_mnemonic(client=client, mnemonic=self.config['seed'])
@@ -64,7 +55,12 @@ class FragmentStars:
search_data = {"query": username, "quantity": "", "method": "searchStarsRecipient"}
search_resp = await client.post(f"https://fragment.com/api?hash={self.config['hash']}",
headers=self.headers, data=search_data)
- search_result = search_resp.json()
+
+ try:
+ search_result = search_resp.json()
+ except Exception as e:
+ logger.error(f"Failed to parse search response: {e}")
+ return {"success": False, "error": f"Invalid response from Fragment API: {str(e)}"}
recipient = search_result.get("found", {}).get("recipient")
if not recipient:
@@ -73,7 +69,14 @@ class FragmentStars:
init_data = {"recipient": recipient, "quantity": amount, "method": "initBuyStarsRequest"}
init_resp = await client.post(f"https://fragment.com/api?hash={self.config['hash']}",
headers=self.headers, data=init_data)
- init_result = init_resp.json()
+
+ try:
+ init_result = init_resp.json()
+ except Exception as e:
+ logger.error(f"Failed to parse init response: {e}")
+ logger.error(f"Response status: {init_resp.status_code}")
+ logger.error(f"Response content: {init_resp.content[:200]}")
+ return {"success": False, "error": f"Invalid response from Fragment API: {str(e)}"}
req_id = init_result.get("req_id")
if not req_id:
diff --git a/app/methods/ton.py b/app/methods/ton.py
index 8467ff8..e76fb27 100644
--- a/app/methods/ton.py
+++ b/app/methods/ton.py
@@ -1,6 +1,5 @@
import base64
import logging
-import re
import time
import httpx
@@ -8,7 +7,7 @@ from tonutils.client import TonapiClient
from tonutils.wallet import WalletV5R1
from app.core.config import Config
-from app.utils import TransactionProcessor, WalletLinker, ApiClient
+from app.utils import TransactionProcessor, WalletLinker, ApiClient, clean_decode
logger = logging.getLogger(__name__)
@@ -30,17 +29,10 @@ class FragmentTon:
'x-requested-with': 'XMLHttpRequest'
}
- self.transaction_processor = TransactionProcessor(self.config, self._clean_decode)
+ self.transaction_processor = TransactionProcessor(self.config, clean_decode)
self.wallet_linker = WalletLinker(self.config, self.headers, self.transaction_processor)
self.api_client = ApiClient(self.config, self.headers, self.wallet_linker)
- @staticmethod
- def _clean_decode(s):
- t = base64.b64decode(re.sub(r'[^A-Za-z0-9+/=]', '', s.strip()) + "=" * (-len(s) % 4)).decode('utf-8',
- errors='ignore')
- t = ''.join(c for c in t if c.isprintable() or c.isspace())
- return t.lstrip("r0N").strip()
-
async def _get_account_info(self):
client = TonapiClient(api_key=self.config['api_key'], is_testnet=False)
wallet, pub_key, _, _ = WalletV5R1.from_mnemonic(client=client, mnemonic=self.config['seed'])
@@ -67,7 +59,12 @@ class FragmentTon:
search_data = {"query": username, "method": "searchAdsTopupRecipient"}
search_resp = await client.post(f"https://fragment.com/api?hash={self.config['hash']}",
headers=self.headers, data=search_data)
- search_result = search_resp.json()
+
+ try:
+ search_result = search_resp.json()
+ except Exception as e:
+ logger.error(f"Failed to parse search response: {e}")
+ return {"success": False, "error": f"Invalid response from Fragment API: {str(e)}"}
recipient = search_result.get("found", {}).get("recipient")
if not recipient:
@@ -76,7 +73,14 @@ class FragmentTon:
init_data = {"recipient": recipient, "amount": amount, "method": "initAdsTopupRequest"}
init_resp = await client.post(f"https://fragment.com/api?hash={self.config['hash']}",
headers=self.headers, data=init_data)
- init_result = init_resp.json()
+
+ try:
+ init_result = init_resp.json()
+ except Exception as e:
+ logger.error(f"Failed to parse init response: {e}")
+ logger.error(f"Response status: {init_resp.status_code}")
+ logger.error(f"Response content: {init_resp.content[:200]}")
+ return {"success": False, "error": f"Invalid response from Fragment API: {str(e)}"}
req_id = init_result.get("req_id")
if not req_id:
diff --git a/app/utils/__init__.py b/app/utils/__init__.py
index d824f24..cb3ab54 100644
--- a/app/utils/__init__.py
+++ b/app/utils/__init__.py
@@ -1,5 +1,6 @@
-from .client import ApiClient
-from .transaction import TransactionProcessor
-from .wallet import WalletLinker
+from app.utils.client import ApiClient
+from app.utils.decoder import clean_decode
+from app.utils.transaction import TransactionProcessor
+from app.utils.wallet import WalletLinker
-__all__ = ['TransactionProcessor', 'WalletLinker', 'ApiClient']
+__all__ = ['TransactionProcessor', 'WalletLinker', 'ApiClient', 'clean_decode']
diff --git a/app/utils/decoder.py b/app/utils/decoder.py
new file mode 100644
index 0000000..2fff9a8
--- /dev/null
+++ b/app/utils/decoder.py
@@ -0,0 +1,49 @@
+import base64
+import logging
+import re
+import string
+
+logger = logging.getLogger(__name__)
+
+
+def clean_decode(payload: str) -> str:
+ logger.debug(f"Original payload: {payload}")
+
+ # Decode raw bytes first
+ clean = ''.join(c for c in payload if c.isalnum() or c in '+/=')
+ clean += '=' * (-len(clean) % 4)
+ raw_bytes = base64.b64decode(clean)
+ logger.debug(f"Raw decoded bytes: {raw_bytes}")
+
+ # 1. Clean Base64
+ s = re.sub(r'[^A-Za-z0-9+/=]', '', payload.strip())
+ s += '=' * (-len(s) % 4)
+
+ # 2. Base64 -> bytes
+ b = base64.b64decode(s)
+
+ # 3. Decode UTF-8, ignoring invalid bytes
+ t = b.decode('utf-8', errors='ignore')
+
+ # 4. Remove binary characters, keep only printable + whitespace
+ t = ''.join(c for c in t if c in string.printable or c.isspace())
+
+ # 5. Extract main text with Ref#
+ match = re.search(r'([0-9]*\s*Telegram .*?Ref#[A-Za-z0-9]+)', t, re.S)
+ if match:
+ result = match.group(1).strip()
+ else:
+ result = t.strip()
+
+ logger.debug(f"Cleaned result: {result}")
+
+ return result
+
+# payloads = [
+# "te6ccgEBAgEALwABTgAAAAAxMDAwMDAwIFRlbGVncmFtIFN0YXJzIAoKUmVmI1RQb01wegEABkM3ZQ",
+# "te6ccgEBAgEANAABTgAAAABUZWxlZ3JhbSBQcmVtaXVtIGZvciAxIHllYXIgCgpSZWYjcgEAEE9OQnM2cmNt",
+# "te6ccgEBAgEAMAABTgAAAABUZWxlZ3JhbSBhY2NvdW50IHRvcCB1cCAKClJlZiNrMXpDRQEACFkxd3g"
+# ]
+
+# for p in payloads:
+# logger.debug("\n" + clean_decode(p) + "\n")
diff --git a/main.py b/main.py
index 0774b4d..ad8e312 100644
--- a/main.py
+++ b/main.py
@@ -12,7 +12,7 @@ async def topup_ton_example():
ton_client = FragmentTon()
# @bohd4nx - target username, 5 - TON amount (integer 1-1000000000 (one billion))
- result = await ton_client.topup_ton("@bohd4nx", 5)
+ result = await ton_client.topup_ton("@bohd4nx", 1000000000)
if result["success"]:
data = result["data"]
@@ -27,7 +27,7 @@ async def buy_premium_example():
premium_client = FragmentPremium()
# @bohd4nx - target username, 6 - months duration (3, 6, or 12 only)
- result = await premium_client.buy_premium("@bohd4nx", 6)
+ result = await premium_client.buy_premium("@bohd4nx", 12)
if result["success"]:
data = result["data"]
@@ -42,7 +42,7 @@ async def buy_stars_example():
stars_client = FragmentStars()
# @bohd4nx - target username, 50 - stars amount (integer 50-1000000 (one million))
- result = await stars_client.buy_stars("@bohd4nx", 50)
+ result = await stars_client.buy_stars("@bohd4nx", 1000000)
if result["success"]:
data = result["data"]
diff --git a/requirements.txt b/requirements.txt
index cafc9e5..277b27f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
python-dotenv==1.2.1
asyncio==4.0.0
httpx==0.28.1
-tonutils==0.5.3
\ No newline at end of file
+tonutils==0.5.5
\ No newline at end of file