faucet_official.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. # -*- coding: utf-8 -*-
  2. import asyncio
  3. from loguru import logger
  4. import random
  5. import hashlib
  6. from eth_account import Account
  7. from pynocaptcha import CloudFlareCracker, TlsV1Cracker
  8. from curl_cffi.requests import AsyncSession
  9. SETTINGS = {
  10. "ATTEMPTS": 3,
  11. "RANDOM_PAUSE_BETWEEN_ACTIONS": [1, 5],
  12. }
  13. FAUCET = {
  14. "USE_CAPSOLVER_FOR_CLOUDFLARE": False,
  15. "CAPSOLVER_API_KEY": "your_capsolver_api_key",
  16. "NOCAPTCHA_API_KEY": "your_nocaptcha_api_key",
  17. }
  18. async def faucet(
  19. session: AsyncSession,
  20. account_index: int,
  21. wallet: Account,
  22. proxy: str,
  23. ) -> bool:
  24. for retry in range(SETTINGS["ATTEMPTS"]):
  25. try:
  26. logger.info(f"[{account_index}] | Starting faucet for account {wallet.address}...")
  27. user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
  28. href = "https://testnet.monad.xyz/"
  29. # Solve Cloudflare challenge
  30. if FAUCET["USE_CAPSOLVER_FOR_CLOUDFLARE"]:
  31. logger.info(f"[{account_index}] | Solving Cloudflare challenge with Capsolver...")
  32. # 这里假设Capsolver类已经定义并且可用
  33. capsolver = Capsolver(api_key=FAUCET["CAPSOLVER_API_KEY"], proxy=proxy, session=session)
  34. cf_result = await capsolver.solve_turnstile("0x4AAAAAAA-3X4Nd7hf3mNGx", href)
  35. else:
  36. logger.info(f"[{account_index}] | Solving Cloudflare challenge with Nocaptcha...")
  37. # 这里假设CloudFlareCracker类已经定义并且可用
  38. cracker = CloudFlareCracker(
  39. internal_host=True,
  40. user_token=FAUCET["NOCAPTCHA_API_KEY"],
  41. href=href,
  42. sitekey="0x4AAAAAAA-3X4Nd7hf3mNGx",
  43. proxy=proxy,
  44. debug=False,
  45. show_ad=False,
  46. timeout=60,
  47. )
  48. cf_result = cracker.crack()
  49. cf_result = cf_result["token"]
  50. if not cf_result:
  51. raise Exception("Failed to solve Cloudflare challenge")
  52. logger.success(f"[{account_index}] | Cloudflare challenge solved")
  53. # Generate visitor ID
  54. visitor_id = hashlib.md5(str(random.random()).encode()).hexdigest()
  55. json_data = {
  56. "address": wallet.address,
  57. "visitorId": visitor_id,
  58. "cloudFlareResponseToken": cf_result,
  59. }
  60. headers = {
  61. "sec-ch-ua-platform": '"Windows"',
  62. "user-agent": user_agent,
  63. "sec-ch-ua": '"Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"',
  64. "content-type": "application/json",
  65. "sec-ch-ua-mobile": "?0",
  66. "accept": "*/*",
  67. "origin": href,
  68. "sec-fetch-site": "same-origin",
  69. "sec-fetch-mode": "cors",
  70. "sec-fetch-dest": "empty",
  71. "referer": href,
  72. "accept-language": "en-GB,en;q=0.9",
  73. "priority": "u=1, i",
  74. }
  75. response = await session.post(
  76. f"{href}api/faucet/claim", headers=headers, json=json_data
  77. )
  78. response_text = response.text
  79. if not response_text:
  80. raise Exception("Failed to send claim request")
  81. if '"Success"' in response_text or '"message":"Success"' in response_text:
  82. logger.success(f"[{account_index}] | Successfully got tokens from faucet")
  83. return True
  84. if "Claimed already" in response_text:
  85. logger.success(f"[{account_index}] | Already claimed tokens from faucet")
  86. return True
  87. error_messages = {
  88. "FUNCTION_INVOCATION_TIMEOUT": "Server is not responding, wait...",
  89. "Vercel Security Checkpoint": "Failed to solve Vercel challenge, trying again...",
  90. "Server error on QuickNode API": "FAUCET DOES NOT WORK, QUICKNODE IS DOWN",
  91. "Over Enterprise free quota": "MONAD IS SHIT, FAUCET DOES NOT WORK, TRY LATER",
  92. "invalid-keys": "PLEASE UPDATE THE BOT USING GITHUB",
  93. }
  94. for key, message in error_messages.items():
  95. if key in response_text:
  96. logger.error(f"[{account_index}] | {message}")
  97. if key == "Over Enterprise free quota" or key == "invalid-keys":
  98. return False
  99. break
  100. else:
  101. logger.error(f"[{account_index}] | Failed to get tokens from faucet: {response_text}")
  102. await asyncio.sleep(3)
  103. except Exception as e:
  104. random_pause = random.randint(*SETTINGS["RANDOM_PAUSE_BETWEEN_ACTIONS"])
  105. if "operation timed out" in str(e):
  106. logger.error(
  107. f"[{account_index}] | Error faucet to monad.xyz ({retry + 1}/{SETTINGS['ATTEMPTS']}): Connection timed out. Next faucet in {random_pause} seconds")
  108. else:
  109. logger.error(
  110. f"[{account_index}] | Error faucet to monad.xyz ({retry + 1}/{SETTINGS['ATTEMPTS']}): {e}. Next faucet in {random_pause} seconds")
  111. await asyncio.sleep(random_pause)
  112. continue
  113. return False