From 6aa703738062f6583cfd18877cb67a7d41cc98a0 Mon Sep 17 00:00:00 2001 From: bohd4nx Date: Fri, 29 May 2026 22:31:30 +0300 Subject: [PATCH] feat: update payment method defaults to use PaymentMethod enum; refactor related validation checks --- pyfragment/client.py | 8 ++++---- pyfragment/core/cookies.py | 2 +- pyfragment/domains/ads/recharge.py | 4 ++-- pyfragment/domains/ads/tonup.py | 4 ++-- pyfragment/domains/giveaways/giveaway.py | 16 ++++++++-------- pyfragment/domains/giveaways/service.py | 4 ++-- pyfragment/domains/purchases/purchase.py | 18 +++++++++--------- pyfragment/domains/purchases/service.py | 4 ++-- pyfragment/domains/tonapi/transaction.py | 2 +- pyfragment/models/enums.py | 10 +++++++++- tests/003_test_balance.py | 5 +++-- tests/004_test_stars.py | 5 +++-- tests/005_test_premium.py | 5 +++-- 13 files changed, 49 insertions(+), 38 deletions(-) diff --git a/pyfragment/client.py b/pyfragment/client.py index d0d5d26..7d7963a 100644 --- a/pyfragment/client.py +++ b/pyfragment/client.py @@ -136,7 +136,7 @@ class FragmentClient: username: str, months: int, show_sender: bool = True, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> PremiumResult: """Gift Telegram Premium to a user. @@ -156,7 +156,7 @@ class FragmentClient: username: str, amount: int, show_sender: bool = True, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> StarsResult: """Send Telegram Stars to a user. @@ -212,7 +212,7 @@ class FragmentClient: channel: str, winners: int, amount: int, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> StarsGiveawayResult: """Run a Telegram Stars giveaway for a channel. @@ -233,7 +233,7 @@ class FragmentClient: channel: str, winners: int, months: int = 3, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> PremiumGiveawayResult: """Run a Telegram Premium giveaway for a channel. diff --git a/pyfragment/core/cookies.py b/pyfragment/core/cookies.py index c800673..f52769e 100644 --- a/pyfragment/core/cookies.py +++ b/pyfragment/core/cookies.py @@ -19,7 +19,7 @@ def get_cookies_from_browser(browser: str = "chrome") -> CookieResult: global rookiepy key = browser.lower() - if key not in SupportedBrowser._value2member_map_: + if not any(key == m for m in SupportedBrowser): supported = ", ".join(sorted(b.value for b in SupportedBrowser)) raise CookieError(CookieError.UNSUPPORTED_BROWSER.format(browser=browser, supported=supported)) diff --git a/pyfragment/domains/ads/recharge.py b/pyfragment/domains/ads/recharge.py index 4b0163e..b79d988 100644 --- a/pyfragment/domains/ads/recharge.py +++ b/pyfragment/domains/ads/recharge.py @@ -4,7 +4,7 @@ import json import logging from typing import TYPE_CHECKING -from pyfragment.core.constants import ADS_TOPUP_PAGE, DEVICE +from pyfragment.core.constants import ADS_TOPUP_PAGE, DEVICE_INFO from pyfragment.domains.tonapi.account import get_account_info from pyfragment.domains.tonapi.transaction import process_transaction from pyfragment.exceptions import ConfigurationError, FragmentAPIError, FragmentError, UnexpectedError, VerificationError @@ -34,7 +34,7 @@ async def recharge_ads(client: FragmentClient, account: str, amount: int) -> Ads "getAdsRechargeLink", { "account": json.dumps(account_info), - "device": DEVICE, + "device": json.dumps(DEVICE_INFO), "transaction": 1, "id": req_id, }, diff --git a/pyfragment/domains/ads/tonup.py b/pyfragment/domains/ads/tonup.py index 0b30c26..df3d8c0 100644 --- a/pyfragment/domains/ads/tonup.py +++ b/pyfragment/domains/ads/tonup.py @@ -4,7 +4,7 @@ import json import logging from typing import TYPE_CHECKING -from pyfragment.core.constants import ADS_TOPUP_PAGE, DEVICE +from pyfragment.core.constants import ADS_TOPUP_PAGE, DEVICE_INFO from pyfragment.domains.payments import parse_required_payment_amount from pyfragment.domains.tonapi.account import get_account_info from pyfragment.domains.tonapi.transaction import process_transaction @@ -48,7 +48,7 @@ async def topup_ton(client: FragmentClient, username: str, amount: int, show_sen "getAdsTopupLink", { "account": json.dumps(account), - "device": DEVICE, + "device": json.dumps(DEVICE_INFO), "transaction": 1, "id": req_id, "show_sender": int(show_sender), diff --git a/pyfragment/domains/giveaways/giveaway.py b/pyfragment/domains/giveaways/giveaway.py index 642133f..d3ca352 100644 --- a/pyfragment/domains/giveaways/giveaway.py +++ b/pyfragment/domains/giveaways/giveaway.py @@ -6,7 +6,7 @@ import random from typing import TYPE_CHECKING from pyfragment.core.constants import ( - DEVICE, + DEVICE_INFO, PREMIUM_GIVEAWAY_PAGE, PREMIUM_MONTHS_VALID, PREMIUM_WINNERS_MAX, @@ -43,13 +43,13 @@ async def giveaway_stars( channel: str, winners: int, amount: int, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> StarsGiveawayResult: if not isinstance(winners, int) or not (STARS_WINNERS_MIN <= winners <= STARS_WINNERS_MAX): raise ConfigurationError(ConfigurationError.INVALID_WINNERS_STARS) if not isinstance(amount, int) or not (STARS_GIVEAWAY_MIN <= amount <= STARS_GIVEAWAY_MAX): raise ConfigurationError(ConfigurationError.INVALID_STARS_PER_WINNER) - if payment_method not in PaymentMethod._value2member_map_: + if not any(payment_method == m for m in PaymentMethod): raise ConfigurationError( ConfigurationError.INVALID_PAYMENT_METHOD.format( method=payment_method, @@ -89,7 +89,7 @@ async def giveaway_stars( "getGiveawayStarsLink", { "account": json.dumps(account), - "device": DEVICE, + "device": json.dumps(DEVICE_INFO), "transaction": 1, "id": req_id, }, @@ -133,17 +133,17 @@ async def giveaway_premium( channel: str, winners: int, months: int = 3, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> PremiumGiveawayResult: if not isinstance(winners, int) or not (PREMIUM_WINNERS_MIN <= winners <= PREMIUM_WINNERS_MAX): raise ConfigurationError(ConfigurationError.INVALID_WINNERS_PREMIUM) if months not in PREMIUM_MONTHS_VALID: raise ConfigurationError(ConfigurationError.INVALID_MONTHS) - if payment_method not in PaymentMethod._value2member_map_: + if not any(payment_method == m for m in PaymentMethod): raise ConfigurationError( ConfigurationError.INVALID_PAYMENT_METHOD.format( method=payment_method, - supported=", ".join(sorted(PaymentMethod._value2member_map_)), + supported=", ".join(sorted(m.value for m in PaymentMethod)), ) ) @@ -188,7 +188,7 @@ async def giveaway_premium( "getGiveawayPremiumLink", { "account": json.dumps(account), - "device": DEVICE, + "device": json.dumps(DEVICE_INFO), "transaction": 1, "id": req_id, }, diff --git a/pyfragment/domains/giveaways/service.py b/pyfragment/domains/giveaways/service.py index abbaefa..db70b05 100644 --- a/pyfragment/domains/giveaways/service.py +++ b/pyfragment/domains/giveaways/service.py @@ -17,7 +17,7 @@ class GiveawaysService(BaseService): channel: str, winners: int, amount: int, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> StarsGiveawayResult: return await giveaway_stars(self._client, channel, winners, amount, payment_method=payment_method) @@ -26,6 +26,6 @@ class GiveawaysService(BaseService): channel: str, winners: int, months: int = 3, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> PremiumGiveawayResult: return await giveaway_premium(self._client, channel, winners, months, payment_method=payment_method) diff --git a/pyfragment/domains/purchases/purchase.py b/pyfragment/domains/purchases/purchase.py index 7779269..cf7b11d 100644 --- a/pyfragment/domains/purchases/purchase.py +++ b/pyfragment/domains/purchases/purchase.py @@ -6,7 +6,7 @@ import time from typing import TYPE_CHECKING from pyfragment.core.constants import ( - DEVICE, + DEVICE_INFO, PREMIUM_MONTHS_VALID, PREMIUM_PAGE, STARS_PAGE, @@ -39,15 +39,15 @@ async def purchase_stars( username: str, amount: int, show_sender: bool = True, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> StarsResult: if not isinstance(amount, int) or not (STARS_PURCHASE_MIN <= amount <= STARS_PURCHASE_MAX): raise ConfigurationError(ConfigurationError.INVALID_STARS_AMOUNT) - if payment_method not in PaymentMethod._value2member_map_: + if not any(payment_method == m for m in PaymentMethod): raise ConfigurationError( ConfigurationError.INVALID_PAYMENT_METHOD.format( method=payment_method, - supported=", ".join(sorted(PaymentMethod._value2member_map_)), + supported=", ".join(sorted(m.value for m in PaymentMethod)), ) ) @@ -77,7 +77,7 @@ async def purchase_stars( "getBuyStarsLink", { "account": json.dumps(account), - "device": DEVICE, + "device": json.dumps(DEVICE_INFO), "transaction": 1, "id": req_id, "show_sender": int(show_sender), @@ -120,15 +120,15 @@ async def purchase_premium( username: str, months: int, show_sender: bool = True, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> PremiumResult: if months not in PREMIUM_MONTHS_VALID: raise ConfigurationError(ConfigurationError.INVALID_MONTHS) - if payment_method not in PaymentMethod._value2member_map_: + if not any(payment_method == m for m in PaymentMethod): raise ConfigurationError( ConfigurationError.INVALID_PAYMENT_METHOD.format( method=payment_method, - supported=", ".join(sorted(PaymentMethod._value2member_map_)), + supported=", ".join(sorted(m.value for m in PaymentMethod)), ) ) @@ -158,7 +158,7 @@ async def purchase_premium( "getGiftPremiumLink", { "account": json.dumps(account), - "device": DEVICE, + "device": json.dumps(DEVICE_INFO), "transaction": 1, "id": req_id, "show_sender": int(show_sender), diff --git a/pyfragment/domains/purchases/service.py b/pyfragment/domains/purchases/service.py index 811a21b..638d3db 100644 --- a/pyfragment/domains/purchases/service.py +++ b/pyfragment/domains/purchases/service.py @@ -17,7 +17,7 @@ class PurchasesService(BaseService): username: str, amount: int, show_sender: bool = True, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> StarsResult: return await purchase_stars(self._client, username, amount, show_sender=show_sender, payment_method=payment_method) @@ -26,6 +26,6 @@ class PurchasesService(BaseService): username: str, months: int, show_sender: bool = True, - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, ) -> PremiumResult: return await purchase_premium(self._client, username, months, show_sender=show_sender, payment_method=payment_method) diff --git a/pyfragment/domains/tonapi/transaction.py b/pyfragment/domains/tonapi/transaction.py index 1740ca2..731c1c5 100644 --- a/pyfragment/domains/tonapi/transaction.py +++ b/pyfragment/domains/tonapi/transaction.py @@ -124,7 +124,7 @@ async def _broadcast_with_retry(wallet: Any, message: dict[str, Any], payload: s async def process_transaction( client: FragmentClient, transaction_data: dict[str, Any], - payment_method: PaymentMethod = "ton", + payment_method: PaymentMethod = PaymentMethod.TON, required_payment_amount: float | None = None, ) -> str: """Sign and broadcast a Fragment transaction with the seeded TON wallet. diff --git a/pyfragment/models/enums.py b/pyfragment/models/enums.py index 6322b22..6f2ec8e 100644 --- a/pyfragment/models/enums.py +++ b/pyfragment/models/enums.py @@ -1,10 +1,18 @@ from __future__ import annotations -from enum import StrEnum +import sys +from enum import Enum from typing import Any from tonutils.contracts.wallet import WalletV4R2, WalletV5R1 +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + + class StrEnum(str, Enum): # noqa: F811 + pass + class PaymentMethod(StrEnum): TON = "ton" diff --git a/tests/003_test_balance.py b/tests/003_test_balance.py index 3f828c9..cedd4b5 100644 --- a/tests/003_test_balance.py +++ b/tests/003_test_balance.py @@ -9,6 +9,7 @@ from tonutils.exceptions import ProviderResponseError from pyfragment import TransactionError, WalletError from pyfragment.domains.tonapi.transaction import process_transaction +from pyfragment.models.enums import PaymentMethod from tests.shared import VALID_SEED @@ -145,7 +146,7 @@ async def test_usdt_payment_requires_min_ton_gas_reserve() -> None: wallet = _make_wallet(balance_nanotons=10_000_000) # 0.01 TON below MIN_TON_BALANCE with _patch_wallet(wallet), patch("pyfragment.domains.tonapi.account.get_usdt_balance", AsyncMock(return_value=100.0)): with pytest.raises(WalletError, match="Insufficient TON balance"): - await process_transaction(_make_client(), TRANSACTION_DATA, payment_method="usdt_ton") + await process_transaction(_make_client(), TRANSACTION_DATA, payment_method=PaymentMethod.USDT_TON) @pytest.mark.asyncio @@ -173,6 +174,6 @@ async def test_usdt_payment_checks_usdt_balance() -> None: await process_transaction( _make_client(), transaction, - payment_method="usdt_ton", + payment_method=PaymentMethod.USDT_TON, required_payment_amount=12.5, ) diff --git a/tests/004_test_stars.py b/tests/004_test_stars.py index e6c583a..49ec4a8 100644 --- a/tests/004_test_stars.py +++ b/tests/004_test_stars.py @@ -7,6 +7,7 @@ import pytest import pyfragment.domains.giveaways.giveaway as _giveaway_stars_mod import pyfragment.domains.purchases.purchase as _purchase_stars_mod from pyfragment import ConfigurationError, FragmentClient, StarsGiveawayResult, StarsResult, UserNotFoundError +from pyfragment.models.enums import PaymentMethod from tests.shared import FAKE_ACCOUNT, FAKE_RECIPIENT, FAKE_REQ_ID, FAKE_TRANSACTION, FAKE_TX_HASH # Stars purchase validation tests @@ -78,7 +79,7 @@ async def test_purchase_stars_passes_payment_method(client: FragmentClient) -> N patch.object(_purchase_stars_mod, "get_account_info", AsyncMock(return_value=FAKE_ACCOUNT)), patch.object(_purchase_stars_mod, "process_transaction", proc_mock), ): - await client.purchase_stars("@user", amount=500, payment_method="usdt_ton") + await client.purchase_stars("@user", amount=500, payment_method=PaymentMethod.USDT_TON) init_call = call_mock.await_args_list[2] assert init_call.args[0] == "initBuyStarsRequest" @@ -198,7 +199,7 @@ async def test_giveaway_stars_passes_payment_method(client: FragmentClient) -> N patch.object(_giveaway_stars_mod, "get_account_info", AsyncMock(return_value=FAKE_ACCOUNT)), patch.object(_giveaway_stars_mod, "process_transaction", proc_mock), ): - await client.giveaway_stars("@channel", winners=3, amount=1000, payment_method="usdt_ton") + await client.giveaway_stars("@channel", winners=3, amount=1000, payment_method=PaymentMethod.USDT_TON) init_call = call_mock.await_args_list[2] assert init_call.args[0] == "initGiveawayStarsRequest" diff --git a/tests/005_test_premium.py b/tests/005_test_premium.py index df07816..6667602 100644 --- a/tests/005_test_premium.py +++ b/tests/005_test_premium.py @@ -7,6 +7,7 @@ import pytest import pyfragment.domains.giveaways.giveaway as _giveaway_premium_mod import pyfragment.domains.purchases.purchase as _purchase_premium_mod from pyfragment import ConfigurationError, FragmentClient, PremiumGiveawayResult, PremiumResult, UserNotFoundError +from pyfragment.models.enums import PaymentMethod from tests.shared import FAKE_ACCOUNT, FAKE_RECIPIENT, FAKE_REQ_ID, FAKE_TRANSACTION, FAKE_TX_HASH # Premium purchase validation tests @@ -75,7 +76,7 @@ async def test_purchase_premium_passes_payment_method(client: FragmentClient) -> patch.object(_purchase_premium_mod, "get_account_info", AsyncMock(return_value=FAKE_ACCOUNT)), patch.object(_purchase_premium_mod, "process_transaction", proc_mock), ): - await client.purchase_premium("@user", months=6, payment_method="usdt_ton") + await client.purchase_premium("@user", months=6, payment_method=PaymentMethod.USDT_TON) init_call = call_mock.await_args_list[2] assert init_call.args[0] == "initGiftPremiumRequest" @@ -183,7 +184,7 @@ async def test_giveaway_premium_passes_payment_method(client: FragmentClient) -> patch.object(_giveaway_premium_mod, "get_account_info", AsyncMock(return_value=FAKE_ACCOUNT)), patch.object(_giveaway_premium_mod, "process_transaction", proc_mock), ): - await client.giveaway_premium("@channel", winners=10, months=6, payment_method="usdt_ton") + await client.giveaway_premium("@channel", winners=10, months=6, payment_method=PaymentMethod.USDT_TON) init_call = call_mock.await_args_list[2] assert init_call.args[0] == "initGiveawayPremiumRequest"