From 76473993e224ca0fe2cc2a804dfae16351c81442 Mon Sep 17 00:00:00 2001 From: bohd4nx Date: Mon, 24 Nov 2025 19:52:22 +0200 Subject: [PATCH] feat: Enhance Fragment API with improved error handling and clean_decode utility; (#1) update version and dependencies --- .gitignore | 4 + README-RU.md | 180 ----------------------------------------- app/__meta__.py | 4 +- app/methods/premium.py | 32 ++++---- app/methods/stars.py | 29 ++++--- app/methods/ton.py | 28 ++++--- app/utils/__init__.py | 9 ++- app/utils/decoder.py | 49 +++++++++++ main.py | 6 +- requirements.txt | 2 +- 10 files changed, 112 insertions(+), 231 deletions(-) delete mode 100644 README-RU.md create mode 100644 app/utils/decoder.py 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 Logo - -

πŸ’Ž 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