from __future__ import annotations from pyfragment.core.constants import ( GRAM_TOPUP_MAX, GRAM_TOPUP_MIN, MNEMONIC_WORD_COUNTS_VALID, PREMIUM_MONTHS_VALID, PREMIUM_WINNERS_MAX, PREMIUM_WINNERS_MIN, STARS_GIVEAWAY_MAX, STARS_GIVEAWAY_MIN, STARS_PURCHASE_MAX, STARS_PURCHASE_MIN, STARS_WINNERS_MAX, STARS_WINNERS_MIN, ) class FragmentError(Exception): """Base exception for all pyfragment errors.""" class ClientError(FragmentError): """Raised for client configuration and setup issues.""" class ConfigurationError(ClientError): """Raised when required client parameters are missing or invalid.""" MISSING_VARS = "Missing required parameter(s): {keys}." UNSUPPORTED_VERSION = "Unsupported wallet version '{version}'. Supported values: {supported}." INVALID_MNEMONIC = f"Invalid mnemonic phrase: expected {', '.join(str(n) for n in sorted(MNEMONIC_WORD_COUNTS_VALID))} words, got {{count}}." UNSUPPORTED_PROVIDER = "Unsupported API provider '{provider}'. Supported values: {supported}." INVALID_MONTHS = f"Invalid Premium duration: choose {', '.join(str(m) for m in sorted(PREMIUM_MONTHS_VALID))} months." INVALID_STARS_AMOUNT = ( f"Invalid Stars amount: must be an integer between {STARS_PURCHASE_MIN:,} and {STARS_PURCHASE_MAX:,}." ) INVALID_GRAM_AMOUNT = f"Invalid GRAM (ex TON) amount: must be an integer between {GRAM_TOPUP_MIN:,} and {GRAM_TOPUP_MAX:,}." INVALID_WINNERS_STARS = ( f"Invalid winners count: must be an integer between {STARS_WINNERS_MIN:,} and {STARS_WINNERS_MAX:,}." ) INVALID_WINNERS_PREMIUM = ( f"Invalid winners count: must be an integer between {PREMIUM_WINNERS_MIN:,} and {PREMIUM_WINNERS_MAX:,}." ) INVALID_STARS_PER_WINNER = ( f"Invalid Stars per winner: must be an integer between {STARS_GIVEAWAY_MIN:,} and {STARS_GIVEAWAY_MAX:,}." ) INVALID_PAYMENT_METHOD = "Invalid payment method '{method}'. Supported values: {supported}." class CookieError(ClientError): """Raised when cookies are unreadable or missing required fields.""" READ_FAILED = "Failed to parse cookies: expected a JSON string or a dict, got {exc}." MISSING_KEYS = ( "Fragment cookies are missing or empty for key(s): {keys}. " "Open fragment.com in your browser, log in, and copy fresh cookies." ) UNSUPPORTED_BROWSER = "Unsupported browser '{browser}'. Supported values: {supported}." BROWSER_READ_FAILED = ( "Failed to read {browser} cookies: {exc}. Make sure {browser} is installed and you are logged in to {url}." ) MISSING_BROWSER_KEYS = ( "Fragment cookies not found in {browser}: {keys}. " "Make sure you are logged in to {url} and have connected your GRAM (ex TON) wallet in {browser}." ) EXPIRED = "Fragment session cookie expired at {expires}. Log in to fragment.com in your browser and extract fresh cookies." class FragmentAPIError(FragmentError): """Raised for errors returned by Fragment's API responses.""" NO_REQUEST_ID = "Fragment did not return a request ID for '{context}'. Your session may have expired. Refresh your cookies and try again." class FragmentPageError(FragmentAPIError): """Raised when the Fragment page cannot be fetched or the API hash is not found.""" BAD_STATUS = "Fragment returned HTTP {status} when loading {url}. Your cookies may be invalid or expired. Refresh them and try again." NOT_FOUND = "Could not extract the API hash from {url}. The page structure may have changed, or you may not be logged in. Refresh your cookies." class UserNotFoundError(FragmentAPIError): """Raised when the target Telegram user is not found on Fragment.""" NOT_FOUND = ( "Telegram user '{username}' was not found on Fragment. Double-check the username and make sure the account exists." ) NOT_A_USER = "'{username}' does not belong to a user account. Make sure the username is assigned to a personal Telegram account, not a channel or bot." class AlreadySubscribedError(FragmentAPIError): """Raised when trying to gift Premium to a user who already has an active subscription.""" PREMIUM_ACTIVE = "This account is already subscribed to Telegram Premium." class AnonymousNumberError(FragmentAPIError): """Raised for Fragment anonymous number API failures.""" NOT_OWNED = "Number '{number}' is not associated with your Fragment account or has no active sessions to terminate." TERMINATE_FAILED = "Failed to terminate sessions for '{number}': {error}" class TransactionError(FragmentAPIError): """Raised when a GRAM (ex TON) transaction fails to build or broadcast.""" INVALID_PAYLOAD = "Fragment returned an invalid transaction payload: 'transaction.messages' is missing or empty." BROADCAST_FAILED = "Transaction broadcast failed: {exc}" BROADCAST_FAILED_SSL = ( "Transaction broadcast failed due to an SSL certificate error: {exc}\n" "This usually means your system's CA bundle is missing or outdated.\n" "Fix: run `pip install --upgrade certifi` and retry. " "On macOS you may also need to run the 'Install Certificates.command' " "located in your Python installation folder." ) DUPLICATE_SEQNO = ( "Transaction broadcast failed: the GRAM (ex TON) wallet rejected the message " "because a previous transaction with the same sequence number (seqno) " "is still pending confirmation on-chain.\n" "Wait a few seconds for the previous transaction to confirm, then retry." ) class ParseError(FragmentAPIError): """Raised when a Fragment API response or payload cannot be parsed.""" UNPARSEABLE = "Failed to parse the Fragment API response for '{context}': {exc}" class VerificationError(FragmentAPIError): """Raised when Fragment requires KYC verification before proceeding.""" KYC_REQUIRED = ( "Fragment requires identity verification (KYC) before this action can be completed. " "Complete verification at https://fragment.com/my/profile and retry." ) class OperationError(FragmentError): """Raised for runtime operation failures unrelated to Fragment's API.""" class WalletError(OperationError): """Raised for GRAM (ex TON) wallet issues (connection, balance, account info).""" LOW_GRAM_BALANCE = ( "Insufficient GRAM (ex TON) balance: {balance:.4f} GRAM (ex TON) available, {required:.4f} GRAM (ex TON) required." ) LOW_USDT_BALANCE = "Insufficient USDT balance: {balance:.4f} USDT available, {required:.4f} USDT required." GRAM_BALANCE_CHECK_FAILED = "Failed to fetch GRAM (ex TON) balance: {exc}" USDT_BALANCE_CHECK_FAILED = "Failed to fetch USDT balance: {exc}" ACCOUNT_INFO_FAILED = "Failed to retrieve wallet account info from GRAM (ex TON) network: {exc}" WALLET_INFO_FAILED = "Failed to retrieve wallet info from GRAM (ex TON) network: {exc}" class UnexpectedError(OperationError): """Raised when an unexpected error occurs during an API call.""" UNEXPECTED = "An unexpected error occurred during the operation: {exc}" __all__ = [ "FragmentError", "ClientError", "ConfigurationError", "CookieError", "FragmentAPIError", "FragmentPageError", "AnonymousNumberError", "AlreadySubscribedError", "UserNotFoundError", "TransactionError", "ParseError", "VerificationError", "OperationError", "WalletError", "UnexpectedError", ]