first commit

This commit is contained in:
jack
2025-11-12 15:56:25 +08:00
commit 2c0019254d
108 changed files with 51129 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
yujieccyj11@hotmail.com|||2pcRp86m1r76nSzdtGy1DNpXjrF|||192.168.31.201:52012|||127.0.0.1:7890
+12
View File
@@ -0,0 +1,12 @@
yujiec0210@gmail.com|||28a3e1c1-3572-45a7-8c66-2843132af302|||192.168.31.201:52001|||192.168.31.201:53001
yujieccyj01@hotmail.com|||2oL7nKR3rlHANh08znNYVNYgZXv|||192.168.31.201:52002|||192.168.31.201:53002
yujieccyj02@hotmail.com|||2oWgZ1hCAD6u6fZjPcWsDJowo7Y|||192.168.31.201:52003|||192.168.31.201:53003
yujieccyj03@hotmail.com|||2oWjwicrgUXjpUxtRNE7ufVSDxl|||192.168.31.201:52004|||192.168.31.201:53004
yujieccyj04@hotmail.com|||2oWkFItRXDMW40AYKpDjGa3SJlw|||192.168.31.201:52005|||192.168.31.201:53005
yujieccyj05@hotmail.com|||2oY2LqL7JDLSKZ8pScMqeGDXnV3|||192.168.31.201:52006|||192.168.31.201:53006
yujieccyj06@hotmail.com|||2pT2P5xXpWgrlZa20ZlW4bdCsZT|||192.168.31.201:52007|||192.168.31.201:53007
yujieccyj07@hotmail.com|||2pT2zNB1oYsMbb0okgnBvtTWYpZ|||192.168.31.201:52008|||192.168.31.201:53008
yujieccyj08@hotmail.com|||2pT3nzb7IGEIRojfB2vypMgBcUw|||192.168.31.201:52009|||192.168.31.201:53009
yujieccyj09@hotmail.com|||2pT49IOTlvXlr8AAqpPa08vBu5o|||192.168.31.201:52010|||192.168.31.201:53010
yujieccyj10@hotmail.com|||2pT5tt43UBpk36v3Sf64KBAoQiR|||192.168.31.201:52011|||192.168.31.201:53011
yujieccyj11@hotmail.com|||2pcRp86m1r76nSzdtGy1DNpXjrF|||192.168.31.201:52012|||192.168.31.201:53012
+12
View File
@@ -0,0 +1,12 @@
yujiec0210@gamil.com:aaaAAA111!!!
yujieccyj01@hotmail.com:aaaAAA111!!!
yujieccyj02@hotmail.com:aaaAAA111!!!
yujieccyj03@hotmail.com:aaaAAA111!!!
yujieccyj04@hotmail.com:aaaAAA111!!!
yujieccyj05@hotmail.com:aaaAAA111!!!
yujieccyj06@hotmail.com:aaaAAA111!!!
yujieccyj07@hotmail.com:aaaAAA111!!!
yujieccyj08@hotmail.com:aaaAAA111!!!
yujieccyj09@hotmail.com:aaaAAA111!!!
yujieccyj10@hotmail.com:aaaAAA111!!!
yujieccyj11@hotmail.com:aaaAAA111!!!
@@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
import random
import time
import httpx
class GrassClient:
def __init__(self, email, password, user_agent, proxy_ip, proxy_api_port, proxy_web_port):
self.email = email
self.password = password
self.user_agent = user_agent
self.proxy_ip = proxy_ip
self.proxy_api_port = proxy_api_port
self.proxy_web_port = proxy_web_port
self.session = httpx.Client()
def login(self):
url = 'https://api.getgrass.io/login'
json_data = {
'password': self.password,
'username': self.email,
}
headers = {
'authority': 'api.getgrass.io',
'accept': 'application/json, text/plain, */*',
'accept-language': 'uk-UA,uk;q=0.9,en-US;q=0.8,en;q=0.7',
'content-type': 'application/json',
'origin': 'https://app.getgrass.io',
'referer': 'https://app.getgrass.io/',
'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-site',
'user-agent': self.user_agent,
}
for retry in range(5):
try:
# 使用 httpx 发送 POST 请求
proxy_web_port = random.choice(self.proxy_web_port)
proxy = f"{self.proxy_ip}:{proxy_web_port}"
proxies = {
"http://": f"http://{proxy}",
"https://": f"http://{proxy}",
}
with httpx.Client(proxies=proxies, headers=headers) as client:
response = client.post(url, json=json_data)
if response.status_code != 200:
error_message = response.text
raise httpx.RequestError(f"登录失败,状态码 {response.status_code}: {error_message}")
data = response.json()
userId = data['result']['data']['userId']
return userId
except httpx.RequestError as e:
print(e)
time.sleep(3)
def close(self):
self.session.close()
# 使用示例
def main():
email_list = [
"yujiec0210@gmail.com",
"yujieccyj01@hotmail.com",
"yujieccyj02@hotmail.com",
"yujieccyj03@hotmail.com",
"yujieccyj04@hotmail.com",
"yujieccyj05@hotmail.com",
"yujieccyj06@hotmail.com",
"yujieccyj07@hotmail.com",
"yujieccyj08@hotmail.com",
"yujieccyj09@hotmail.com",
"yujieccyj10@hotmail.com",
"yujieccyj11@hotmail.com"
]
proxy_api_port = ["52001", "52002", "52003", "52004", "52005", "52006", "52007", "52008", "52009", "52010", "52011",
"52012"]
proxy_web_port = ["53001", "53002", "53003", "53004", "53005", "53006", "53007", "53008", "53009", "53010", "53011",
"53012"]
password = "aaaAAA111!!!"
proxy_ip = "192.168.31.201"
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
user_id_list = []
for email in email_list:
client = GrassClient(email, password, user_agent, proxy_ip, proxy_api_port, proxy_web_port)
try:
login_response = client.login()
print("userId:", f'{email} - {login_response}')
user_id_list.append(login_response)
finally:
client.close()
if user_id_list:
with open("account_details.txt", "w") as file:
for email, user_id, api_port, web_port in zip(email_list, user_id_list, proxy_api_port, proxy_web_port):
file.write(f"{email}|||{user_id}|||{proxy_ip}:{api_port}|||{proxy_ip}:{web_port}\n")
# 运行主函数
if __name__ == "__main__":
main()
+256
View File
@@ -0,0 +1,256 @@
import asyncio
import json
import random
import sys
import time
import traceback
import uuid
import aiohttp
from fake_useragent import UserAgent
from tenacity import stop_after_attempt, retry, retry_if_not_exception_type, wait_random, retry_if_exception_type
# 配置文件路径
ACCOUNTS_DETAILS_FILE_PATH = "account_details.txt"
ACCOUNTS_FILE_PATH = "accounts.txt"
PROXIES_FILE_PATH = "proxies.txt"
MIN_PROXY_SCORE = 50
# 日志记录
class Logger:
@staticmethod
def info(message):
print(f"[INFO] {message}")
@staticmethod
def error(message):
print(f"[ERROR] {message}")
@staticmethod
def success(message):
print(f"[SUCCESS] {message}")
logger = Logger()
# 异常类
class ProxyForbiddenException(Exception):
pass
class LowProxyScoreException(Exception):
pass
class ProxyScoreNotFoundException(Exception):
pass
class WebsocketClosedException(Exception):
pass
# Grass 类,负责登录、检测代理分数和持久连接
class Grass:
def __init__(self, _id: int, email: str, user_id: str, proxy: str = None):
self.proxy = f"socks5://{proxy}" if proxy else None # 添加协议
self.email = email
self.user_id = user_id
self.user_agent = UserAgent().random
self.proxy_score = None
self.id = _id
self.session = aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(ssl=False))
self.all_proxies_name = []
async def start(self):
try:
user_id = self.user_id
browser_id = str(self.email) # 使用邮箱作为浏览器ID
await self.run(browser_id, user_id)
except Exception as e:
logger.error(f"{self.id} | Error: {e}")
finally:
await self.close()
async def run(self, browser_id: str, user_id: str):
while True:
try:
await self.connection_handler()
await self.auth_to_extension(browser_id, user_id)
if self.proxy_score is None:
await asyncio.sleep(1)
await self.handle_proxy_score(MIN_PROXY_SCORE)
while True:
await self.send_ping()
await self.send_pong()
logger.info(f"{self.id} | Mined grass.")
await asyncio.sleep(19.9)
except WebsocketClosedException as e:
logger.info(f"Websocket closed: {e}. Retrying...")
except ConnectionResetError as e:
logger.info(f"Connection reset: {e}. Retrying...")
except TypeError as e:
logger.info(f"Type error: {e}. Retrying...")
await asyncio.sleep(1)
@retry(stop=stop_after_attempt(30),
retry=(retry_if_exception_type(ConnectionError) | retry_if_not_exception_type(ProxyForbiddenException)),
wait=wait_random(0.5, 1),
reraise=True)
async def connection_handler(self):
logger.info(f"{self.id} | Connecting...")
await self.connect()
logger.info(f"{self.id} | Connected")
@retry(stop=stop_after_attempt(10),
retry=retry_if_not_exception_type(LowProxyScoreException),
before_sleep=lambda retry_state, **kwargs: logger.info(f"{retry_state.outcome.exception()}"),
wait=wait_random(5, 7),
reraise=True)
async def handle_proxy_score(self, min_score: int):
if (proxy_score := await self.get_proxy_score_by_device_id()) is None:
raise ProxyScoreNotFoundException(f"{self.id} | Proxy score not found for {self.proxy}. Guess Bad proxies!")
elif proxy_score >= min_score:
self.proxy_score = proxy_score
logger.success(f"{self.id} | Proxy score: {self.proxy_score}")
return True
else:
raise LowProxyScoreException(f"{self.id} | Too low proxy score: {proxy_score} for {self.proxy}. Exit...")
async def auth_to_extension(self, browser_id: str, user_id: str):
connection_id = await self.get_connection_id()
message = json.dumps(
{
"id": connection_id,
"origin_action": "AUTH",
"result": {
"browser_id": browser_id,
"user_id": user_id,
"user_agent": self.user_agent,
"timestamp": int(time.time()),
"device_type": "extension",
"version": "4.26.2"
}
}
)
print(message)
await self.send_message(message)
async def connect(self):
uri = "wss://proxy.wynd.network"
headers = {
'Pragma': 'no-cache',
'Origin': 'chrome-extension://ilehaonighjijnmpnagapkhpcdbhclfg',
'Accept-Language': 'uk-UA,uk;q=0.9,en-US;q=0.8,en;q=0.7',
'User-Agent': self.user_agent,
'Upgrade': 'websocket',
'Cache-Control': 'no-cache',
'Connection': 'Upgrade',
'Sec-WebSocket-Version': '13',
'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
}
try:
self.websocket = await self.session.ws_connect(uri, proxy_headers=headers, proxy=self.proxy)
except Exception as e:
if 'status' in dir(e) and e.status == 403:
raise ProxyForbiddenException(f"Low proxy score. Can't connect. Error: {e}")
raise e
async def get_proxy_score_by_device_id(self):
url = 'https://api.getgrass.io/extension/user-score'
headers = {
'authority': 'api.getgrass.io',
'accept': 'application/json, text/plain, */*',
'accept-language': 'uk-UA,uk;q=0.9,en-US;q=0.8,en;q=0.7',
'content-type': 'application/json',
'origin': 'https://app.getgrass.io',
'referer': 'https://app.getgrass.io/',
'sec-ch-ua': '"Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'none',
'user-agent': self.user_agent,
}
response = await self.session.get(url, headers=headers, proxy=self.proxy)
res_json = await response.json()
print(res_json)
if not (isinstance(res_json, dict) and res_json.get("data", None) is not None):
return
devices = res_json['data']['currentDeviceData']
self.ip = await self.get_ip()
logger.info(f"当前代理IP: {self.ip}")
return next((device['final_score'] for device in devices
if device['device_ip'] == self.ip), None)
async def send_message(self, message):
await self.websocket.send_str(message)
async def receive_message(self):
msg = await self.websocket.receive()
if msg.type == aiohttp.WSMsgType.CLOSED:
raise WebsocketClosedException(f"Websocket closed: {msg}")
return msg.data
async def get_connection_id(self):
msg = await self.receive_message()
return json.loads(msg)['id']
async def send_ping(self):
message = json.dumps(
{"id": str(uuid.uuid4()), "version": "1.0.0", "action": "PING", "data": {}}
)
await self.send_message(message)
async def send_pong(self):
connection_id = await self.get_connection_id()
message = json.dumps(
{"id": connection_id, "origin_action": "PONG"}
)
await self.send_message(message)
async def get_ip(self):
return await (await self.session.get('https://api.ipify.org', proxy=self.proxy)).text()
async def close(self):
if self.session:
await self.session.close()
# 主函数
async def main():
account_details = []
with open(ACCOUNTS_DETAILS_FILE_PATH, 'r') as f:
account_details = f.readlines()
grass_instances = []
tasks = []
for i, account in enumerate(account_details):
email, user_id, proxy_web, proxy_api = account.strip().split("|||")
grass = Grass(i, email, user_id, proxy_api)
grass_instances.append(grass)
tasks.append(grass.start())
try:
await asyncio.gather(*tasks)
except Exception as e:
logger.error(f"An error occurred: {e}")
finally:
# 确保所有会话都被关闭
for grass in grass_instances:
await grass.close()
if __name__ == "__main__":
if sys.platform == 'win32':
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())
else:
asyncio.run(main())
+12
View File
@@ -0,0 +1,12 @@
192.168.31.201:53001
192.168.31.201:53002
192.168.31.201:53003
192.168.31.201:53004
192.168.31.201:53005
192.168.31.201:53006
192.168.31.201:53007
192.168.31.201:53008
192.168.31.201:53009
192.168.31.201:53010
192.168.31.201:53011
192.168.31.201:53012
+172
View File
@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
'''
切换到随机代理
'''
import random
import httpx
import time
import logging
import subprocess
from typing import Optional, List
logging.basicConfig(level=logging.INFO)
BASE_URL = "http://192.168.31.201"
PORT_LIST = [
["52001", "53001"],
["52002", "53002"],
["52003", "53003"],
["52004", "53004"],
["52005", "53005"],
["52006", "53006"],
["52007", "53007"],
["52008", "53008"],
["52009", "53009"],
["52011", "53012"],
["52012", "53012"]
]
class ClashProxyManager:
def __init__(self, base_url, base_port):
self.key_group = 0
self.base_url = base_url
self.base_port = base_port
self.all_proxies = []
def get_all_proxies(self, clash_tool_url: str) -> List[str]:
url = f"{clash_tool_url}/api/proxies"
try:
response = httpx.get(url)
response.raise_for_status()
proxies = response.json()
logging.info("Available proxies:")
# 输出读取的所有代理信息
# for proxy_name, proxy_info in proxies['proxies'].items():
# logging.info(f"Name: {proxy_name}, Type: {proxy_info.get('type', 'Unknown')}")
proxy_list = list(proxies['proxies'].keys())
filtered_list = [item for item in proxy_list if item not in {'REJECT', 'GLOBAL', 'DIRECT'}]
return filtered_list
except Exception as e:
logging.error(f"Failed to get proxies: {e}")
return []
def switch_proxy(self, proxy_name: str, url_and_port: str) -> None:
logging.info("switch proxy")
url = f"{url_and_port}/api/proxies/GLOBAL"
data = {"name": proxy_name}
try:
response = httpx.put(url, json=data)
if response.status_code == 204:
logging.info(f"Switched to proxy: {proxy_name}")
else:
logging.error(f"Failed to switch proxy: {response.status_code} - {proxy_name}")
except Exception as e:
logging.error(f"Failed to switch proxy: {e}")
def update_configs(self):
for base_port in self.base_port:
url_and_port = self.base_url + ":" + base_port[0]
key = "/api/configs"
url = url_and_port + key
headers = {
"accept": "application/json, text/plain, */*",
"accept-encoding": "gzip, deflate, br, zstd",
"accept-language": "zh-CN,zh",
"connection": "keep-alive",
"content-type": "application/json",
"origin": url_and_port,
"referer": url_and_port,
"sec-ch-ua": '"Not A(Brand";v="8", "Chromium";v="132", "Brave";v="132"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"macOS"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"sec-gpc": "1",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
}
# 请求体数据
data = {"mode": "Global"} # 替换为实际的请求数据
# 使用 httpx 发送 PATCH 请求
try:
with httpx.Client() as client:
response = client.patch(url, headers=headers, json=data)
if response.status_code == 204:
print(f"{url} OK")
else:
print("响应内容:", response.text)
except httpx.RequestError as exc:
print(f"请求失败: {exc}")
def check_proxy(self, proxy_url):
# proxy_url: 代理地址, 没有密码
# 测试目标地址:
command = ["curl", "-x", proxy_url, "ip.sb"]
# 执行命令并获取输出
try:
result = subprocess.run(command, capture_output=True, text=True, check=True)
# 打印命令的标准输出
print("Output:", result.stdout)
return True
except subprocess.CalledProcessError as e:
# 如果命令执行失败,打印错误信息
print("Error:", e.stderr)
return False
def main(self) -> None:
# 设置全局代理
self.update_configs()
# 读取所有代理
if not self.all_proxies:
for port_list in self.base_port:
base_url = self.base_url + ":" + port_list[0]
clash_tool_url = f"{base_url}"
proxies = self.get_all_proxies(clash_tool_url)
if proxies:
self.all_proxies = proxies
break
if not self.all_proxies:
logging.error("Failed to get all proxies")
return
else:
logging.info(f"Found {self.all_proxies} proxies.")
logging.info(f"Found {len(self.all_proxies)} proxies.")
# 遍历所有的线路api, 切换不重复代理
# 切换后, 检测代理, 如果检测返回失败, 再次切换
used_proxy = []
for base_port in self.base_port:
url_and_port = self.base_url + ":" + base_port[0]
proxy_url = self.base_url + ":" + base_port[1]
while True:
# 选一个代理
choose_proxy = random.choice(self.all_proxies)
# 如果已经选过就继续下一个
if choose_proxy in used_proxy:
continue
# 切换代理
self.switch_proxy(choose_proxy, url_and_port)
# 切换后检测代理
if self.check_proxy(proxy_url):
print(f"代理 {choose_proxy} 切换成功,检测通过!")
used_proxy.append(choose_proxy) # 标记为已使用
break # 成功后退出当前代理的重试循环
else:
print(f"{url_and_port} 切换 {choose_proxy} 检测失败")
time.sleep(1)
if __name__ == "__main__":
manager = ClashProxyManager(BASE_URL, PORT_LIST)
manager.main()
File diff suppressed because one or more lines are too long
@@ -0,0 +1,77 @@
import asyncio
import random
from fake_useragent import UserAgent
import aiohttp
import logging
class ProxyForbiddenException(Exception):
pass
class GrassConnect:
def __init__(self, proxy: str = None):
self.user_agent = UserAgent().random
self.proxy = proxy
self.session = None
self.websocket = None
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
self.logger = logging.getLogger(__name__)
async def __aenter__(self):
self.session = aiohttp.ClientSession()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.close()
async def connect(self):
uri = f"wss://proxy.wynd.network"
headers = {
'Pragma': 'no-cache',
'Origin': 'chrome-extension://ilehaonighjijnmpnagapkhpcdbhclfg',
'Accept-Language': 'uk-UA,uk;q=0.9,en-US;q=0.8,en;q=0.7',
'User-Agent': self.user_agent,
'Upgrade': 'websocket',
'Cache-Control': 'no-cache',
'Connection': 'Upgrade',
'Sec-WebSocket-Version': '13',
'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits'
}
while True:
self.logger.info(f"Attempting to connect to {uri} using proxy {self.proxy}")
try:
self.websocket = await self.session.ws_connect(
uri,
headers=headers,
proxy=self.proxy,
proxy_headers=headers
)
self.logger.info(f"Successfully connected to {uri}")
return self.websocket
except aiohttp.ClientResponseError as e:
self.logger.error(f"HTTP error occurred: {e}")
if e.status == 403:
raise ProxyForbiddenException(f"Low proxy score. Can't connect. Error: {e}")
except Exception as e:
self.logger.error(f"Unexpected error occurred: {e}")
self.logger.info("Connection failed. Retrying in 3 seconds...")
await asyncio.sleep(3)
async def close(self):
if self.websocket:
await self.websocket.close()
self.logger.info("Websocket connection closed.")
if self.session:
await self.session.close()
self.logger.info("Client session closed.")
# Example usage
async def main():
proxy = "socks5://192.168.31.201:53012"
async with GrassConnect(proxy=proxy) as connector:
websocket = await connector.connect()
# 继续处理websocket连接
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-