main.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. import asyncio
  2. import json
  3. import random
  4. import sys
  5. import time
  6. import traceback
  7. import uuid
  8. import aiohttp
  9. from fake_useragent import UserAgent
  10. from tenacity import stop_after_attempt, retry, retry_if_not_exception_type, wait_random, retry_if_exception_type
  11. # 配置文件路径
  12. ACCOUNTS_DETAILS_FILE_PATH = "account_details.txt"
  13. ACCOUNTS_FILE_PATH = "accounts.txt"
  14. PROXIES_FILE_PATH = "proxies.txt"
  15. MIN_PROXY_SCORE = 50
  16. # 日志记录
  17. class Logger:
  18. @staticmethod
  19. def info(message):
  20. print(f"[INFO] {message}")
  21. @staticmethod
  22. def error(message):
  23. print(f"[ERROR] {message}")
  24. @staticmethod
  25. def success(message):
  26. print(f"[SUCCESS] {message}")
  27. logger = Logger()
  28. # 异常类
  29. class ProxyForbiddenException(Exception):
  30. pass
  31. class LowProxyScoreException(Exception):
  32. pass
  33. class ProxyScoreNotFoundException(Exception):
  34. pass
  35. class WebsocketClosedException(Exception):
  36. pass
  37. # Grass 类,负责登录、检测代理分数和持久连接
  38. class Grass:
  39. def __init__(self, _id: int, email: str, user_id: str, proxy: str = None):
  40. self.proxy = f"socks5://{proxy}" if proxy else None # 添加协议
  41. self.email = email
  42. self.user_id = user_id
  43. self.user_agent = UserAgent().random
  44. self.proxy_score = None
  45. self.id = _id
  46. self.session = aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(ssl=False))
  47. self.all_proxies_name = []
  48. async def start(self):
  49. try:
  50. user_id = self.user_id
  51. browser_id = str(self.email) # 使用邮箱作为浏览器ID
  52. await self.run(browser_id, user_id)
  53. except Exception as e:
  54. logger.error(f"{self.id} | Error: {e}")
  55. finally:
  56. await self.close()
  57. async def run(self, browser_id: str, user_id: str):
  58. while True:
  59. try:
  60. await self.connection_handler()
  61. await self.auth_to_extension(browser_id, user_id)
  62. if self.proxy_score is None:
  63. await asyncio.sleep(1)
  64. await self.handle_proxy_score(MIN_PROXY_SCORE)
  65. while True:
  66. await self.send_ping()
  67. await self.send_pong()
  68. logger.info(f"{self.id} | Mined grass.")
  69. await asyncio.sleep(19.9)
  70. except WebsocketClosedException as e:
  71. logger.info(f"Websocket closed: {e}. Retrying...")
  72. except ConnectionResetError as e:
  73. logger.info(f"Connection reset: {e}. Retrying...")
  74. except TypeError as e:
  75. logger.info(f"Type error: {e}. Retrying...")
  76. await asyncio.sleep(1)
  77. @retry(stop=stop_after_attempt(30),
  78. retry=(retry_if_exception_type(ConnectionError) | retry_if_not_exception_type(ProxyForbiddenException)),
  79. wait=wait_random(0.5, 1),
  80. reraise=True)
  81. async def connection_handler(self):
  82. logger.info(f"{self.id} | Connecting...")
  83. await self.connect()
  84. logger.info(f"{self.id} | Connected")
  85. @retry(stop=stop_after_attempt(10),
  86. retry=retry_if_not_exception_type(LowProxyScoreException),
  87. before_sleep=lambda retry_state, **kwargs: logger.info(f"{retry_state.outcome.exception()}"),
  88. wait=wait_random(5, 7),
  89. reraise=True)
  90. async def handle_proxy_score(self, min_score: int):
  91. if (proxy_score := await self.get_proxy_score_by_device_id()) is None:
  92. raise ProxyScoreNotFoundException(f"{self.id} | Proxy score not found for {self.proxy}. Guess Bad proxies!")
  93. elif proxy_score >= min_score:
  94. self.proxy_score = proxy_score
  95. logger.success(f"{self.id} | Proxy score: {self.proxy_score}")
  96. return True
  97. else:
  98. raise LowProxyScoreException(f"{self.id} | Too low proxy score: {proxy_score} for {self.proxy}. Exit...")
  99. async def auth_to_extension(self, browser_id: str, user_id: str):
  100. connection_id = await self.get_connection_id()
  101. message = json.dumps(
  102. {
  103. "id": connection_id,
  104. "origin_action": "AUTH",
  105. "result": {
  106. "browser_id": browser_id,
  107. "user_id": user_id,
  108. "user_agent": self.user_agent,
  109. "timestamp": int(time.time()),
  110. "device_type": "extension",
  111. "version": "4.26.2"
  112. }
  113. }
  114. )
  115. print(message)
  116. await self.send_message(message)
  117. async def connect(self):
  118. uri = "wss://proxy.wynd.network:4650/"
  119. headers = {
  120. 'Pragma': 'no-cache',
  121. 'Origin': 'chrome-extension://ilehaonighjijnmpnagapkhpcdbhclfg',
  122. 'Accept-Language': 'uk-UA,uk;q=0.9,en-US;q=0.8,en;q=0.7',
  123. 'User-Agent': self.user_agent,
  124. 'Upgrade': 'websocket',
  125. 'Cache-Control': 'no-cache',
  126. 'Connection': 'Upgrade',
  127. 'Sec-WebSocket-Version': '13',
  128. 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
  129. }
  130. try:
  131. self.websocket = await self.session.ws_connect(uri, proxy_headers=headers, proxy=self.proxy)
  132. except Exception as e:
  133. if 'status' in dir(e) and e.status == 403:
  134. raise ProxyForbiddenException(f"Low proxy score. Can't connect. Error: {e}")
  135. raise e
  136. async def get_proxy_score_by_device_id(self):
  137. url = 'https://api.getgrass.io/extension/user-score'
  138. headers = {
  139. 'authority': 'api.getgrass.io',
  140. 'accept': 'application/json, text/plain, */*',
  141. 'accept-language': 'uk-UA,uk;q=0.9,en-US;q=0.8,en;q=0.7',
  142. 'content-type': 'application/json',
  143. 'origin': 'https://app.getgrass.io',
  144. 'referer': 'https://app.getgrass.io/',
  145. 'sec-ch-ua': '"Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"',
  146. 'sec-ch-ua-mobile': '?0',
  147. 'sec-ch-ua-platform': '"Windows"',
  148. 'sec-fetch-dest': 'document',
  149. 'sec-fetch-mode': 'navigate',
  150. 'sec-fetch-site': 'none',
  151. 'user-agent': self.user_agent,
  152. }
  153. response = await self.session.get(url, headers=headers, proxy=self.proxy)
  154. res_json = await response.json()
  155. print(res_json)
  156. if not (isinstance(res_json, dict) and res_json.get("data", None) is not None):
  157. return
  158. devices = res_json['data']['currentDeviceData']
  159. self.ip = await self.get_ip()
  160. logger.info(f"当前代理IP: {self.ip}")
  161. return next((device['final_score'] for device in devices
  162. if device['device_ip'] == self.ip), None)
  163. async def send_message(self, message):
  164. await self.websocket.send_str(message)
  165. async def receive_message(self):
  166. msg = await self.websocket.receive()
  167. if msg.type == aiohttp.WSMsgType.CLOSED:
  168. raise WebsocketClosedException(f"Websocket closed: {msg}")
  169. return msg.data
  170. async def get_connection_id(self):
  171. msg = await self.receive_message()
  172. return json.loads(msg)['id']
  173. async def send_ping(self):
  174. message = json.dumps(
  175. {"id": str(uuid.uuid4()), "version": "1.0.0", "action": "PING", "data": {}}
  176. )
  177. await self.send_message(message)
  178. async def send_pong(self):
  179. connection_id = await self.get_connection_id()
  180. message = json.dumps(
  181. {"id": connection_id, "origin_action": "PONG"}
  182. )
  183. await self.send_message(message)
  184. async def get_ip(self):
  185. return await (await self.session.get('https://api.ipify.org', proxy=self.proxy)).text()
  186. async def close(self):
  187. if self.session:
  188. await self.session.close()
  189. # 主函数
  190. async def main():
  191. account_details = []
  192. with open(ACCOUNTS_DETAILS_FILE_PATH, 'r') as f:
  193. account_details = f.readlines()
  194. grass_instances = []
  195. tasks = []
  196. for i, account in enumerate(account_details):
  197. email, user_id, proxy_web, proxy_api = account.strip().split("|||")
  198. grass = Grass(i, email, user_id, proxy_api)
  199. grass_instances.append(grass)
  200. tasks.append(grass.start())
  201. try:
  202. await asyncio.gather(*tasks)
  203. except Exception as e:
  204. logger.error(f"An error occurred: {e}")
  205. finally:
  206. # 确保所有会话都被关闭
  207. for grass in grass_instances:
  208. await grass.close()
  209. if __name__ == "__main__":
  210. if sys.platform == 'win32':
  211. asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
  212. loop = asyncio.ProactorEventLoop()
  213. asyncio.set_event_loop(loop)
  214. loop.run_until_complete(main())
  215. else:
  216. asyncio.run(main())