├── INSTALL.bat ├── README.md ├── START.bat ├── core ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── autoreger.cpython-312.pyc │ └── grass.cpython-312.pyc ├── autoreger.py ├── grass.py ├── grass_sdk │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-312.pyc │ │ ├── extension.cpython-312.pyc │ │ └── website.cpython-312.pyc │ ├── extension.py │ └── website.py └── utils │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── captcha_service.cpython-312.pyc │ ├── exception.cpython-312.pyc │ ├── file_manager.cpython-312.pyc │ ├── global_store.cpython-312.pyc │ └── logger.cpython-312.pyc │ ├── captcha_service.py │ ├── exception.py │ ├── file_manager.py │ ├── generate │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-312.pyc │ │ └── person.cpython-312.pyc │ └── person.py │ ├── global_store.py │ └── logger.py ├── data ├── __pycache__ │ └── config.cpython-312.pyc ├── accounts.txt ├── config.py ├── dead_proxy.txt └── proxies.txt ├── logs └── failed.txt ├── main.py ├── requirements.txt └── user_id.txt /INSTALL.bat: -------------------------------------------------------------------------------- 1 | pip install -r requirements.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Get Grass 3 | Get Grass BOT 4 | 5 | Register Here : [GetGrass](https://app.getgrass.io/register/?referralCode=NIlT1MR9E2476uA) 6 | 7 | 8 | ## Features 9 | 10 | - Multi Thread 11 | - Count Proxy Connected 12 | - Farming x1.25 13 | - Hemat Bandwith 14 | 15 | 16 | ## Installation 17 | 18 | Install with python 19 | 20 | 1. Download python 21 | 2. Install Module (pip install -r requirements.txt) 22 | 3. Edit user_id.txt 23 | 4. Cara dapat user id : 24 | - Login 25 | - Inspect > Refresh 26 | - Cari network retriveUser 27 | - Buka Preview > Copy user id (diatas user role) CEK SS DIBAWAH 28 | 5. Edit proxy di data > proxies.txt 29 | 6. python main.py 30 | 31 | 32 | 33 | 34 | ![RETRIVE_USER](https://i.ibb.co.com/7Jm9PDK/Cuplikan-layar-2024-07-28-224201.png) 35 | ![SSAPP](https://i.ibb.co.com/G5nkXXf/Cuplikan-layar-2024-07-28-224111.png) -------------------------------------------------------------------------------- /START.bat: -------------------------------------------------------------------------------- 1 | python main.py 2 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | from .grass import Grass 2 | 3 | -------------------------------------------------------------------------------- /core/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /core/__pycache__/autoreger.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/__pycache__/autoreger.cpython-312.pyc -------------------------------------------------------------------------------- /core/__pycache__/grass.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/__pycache__/grass.cpython-312.pyc -------------------------------------------------------------------------------- /core/autoreger.py: -------------------------------------------------------------------------------- 1 | import random 2 | import traceback 3 | from asyncio import Semaphore, sleep, create_task, wait 4 | from itertools import zip_longest 5 | 6 | from core.utils import logger, file_to_list, str_to_file 7 | 8 | 9 | class AutoReger: 10 | def __init__(self): 11 | self.accounts = [] 12 | self.proxies = [] 13 | 14 | 15 | @classmethod 16 | def get_accounts(cls, accounts_file_path, proxies_file_path, with_id=False): 17 | instance = cls() 18 | # Baca file akun dan inisialisasi self.accounts 19 | with open(accounts_file_path, 'r') as file: 20 | instance.accounts = [line.strip() for line in file.readlines() if line.strip()] 21 | 22 | # Baca file proxy dan inisialisasi self.proxies 23 | with open(proxies_file_path, 'r') as file: 24 | instance.proxies = [line.strip() for line in file.readlines() if line.strip()] 25 | 26 | return instance 27 | 28 | async def start(self, worker_func: callable, threads: int = 1, delay: tuple = (0, 0)): 29 | if not self.accounts or not self.accounts[0]: 30 | logger.warning("No accounts found :(") 31 | return 32 | 33 | logger.info(f"Successfully grabbed {len(self.accounts)} accounts") 34 | 35 | self.semaphore = Semaphore(threads) 36 | self.delay = delay 37 | await self.define_tasks(worker_func) 38 | 39 | (logger.success if self.success else logger.warning)( 40 | f"Successfully handled {self.success} accounts :)" if self.success 41 | else "No accounts handled :( | Check logs in logs/out.log") 42 | 43 | async def define_tasks(self, worker_func: callable): 44 | await wait([create_task(self.worker(account, worker_func)) for account in self.accounts]) 45 | 46 | async def worker(self, account: tuple, worker_func: callable): 47 | account_id = account[0][:15] if isinstance(account, str) else account[0] 48 | is_success = False 49 | 50 | try: 51 | async with self.semaphore: 52 | await self.custom_delay() 53 | 54 | is_success = await worker_func(*account) 55 | except Exception as e: 56 | logger.error(f"{account_id} | not handled | error: {e} {traceback.format_exc()}") 57 | 58 | self.success += int(is_success or 0) 59 | AutoReger.logs(account_id, account, is_success) 60 | 61 | async def custom_delay(self): 62 | if self.delay[1] > 0: 63 | sleep_time = random.uniform(*self.delay) 64 | logger.info(f"Sleep for {sleep_time:.1f} seconds") 65 | await sleep(sleep_time) 66 | 67 | @staticmethod 68 | def logs(account_id: str, account: tuple, is_success: bool = False): 69 | if is_success: 70 | log_func = logger.success 71 | log_msg = "handled!" 72 | file_name = "success" 73 | else: 74 | log_func = logger.warning 75 | log_msg = "failed!" 76 | file_name = "failed" 77 | 78 | file_msg = "|".join(str(x) for x in account) 79 | str_to_file(f"./logs/{file_name}.txt", file_msg) 80 | 81 | log_func(f"Account №{account_id} {log_msg}") 82 | -------------------------------------------------------------------------------- /core/grass.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import uuid 3 | import time 4 | import aiohttp 5 | from fake_useragent import UserAgent 6 | from tenacity import stop_after_attempt, retry, retry_if_not_exception_type, wait_random, retry_if_exception_type 7 | import itertools 8 | import sys 9 | import threading 10 | from data.config import MIN_PROXY_SCORE 11 | from .grass_sdk.extension import GrassWs 12 | from .grass_sdk.website import GrassRest 13 | from .utils import logger 14 | from .utils.exception import WebsocketClosedException, LowProxyScoreException, ProxyScoreNotFoundException, \ 15 | ProxyForbiddenException 16 | from better_proxy import Proxy 17 | from core.utils.global_store import mined_grass_counts, lock 18 | 19 | 20 | class Grass(GrassWs, GrassRest): 21 | def __init__(self, _id: int, email: str, password: str, proxy: str = None): 22 | self.mined_grass_count = 0 23 | self.has_mined_successfully = False # Tambahkan ini 24 | if proxy: 25 | if "@" in proxy: 26 | # Proxy dengan autentikasi 27 | self.proxy = Proxy.from_str(proxy).as_url 28 | else: 29 | # Proxy tanpa autentikasi 30 | self.proxy = f"socks://{proxy}" 31 | else: 32 | self.proxy = None 33 | super(GrassWs, self).__init__(email=email, password=password, user_agent=UserAgent().random, proxy=self.proxy) 34 | self.proxy_score = None 35 | self.id = _id 36 | self.animation = itertools.cycle(['|', '/', '-', '\\']) 37 | 38 | self.session = aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(ssl=False)) 39 | 40 | async def load_user_id(self): 41 | loop = asyncio.get_event_loop() 42 | with open('user_id.txt', mode='r') as f: 43 | return await loop.run_in_executor(None, f.read) 44 | def animate(self, countdown_event): 45 | while True: 46 | for remaining in range(120, 0, -1): 47 | sys.stdout.write(f"\rSisa waktu untuk ping lagi: {remaining} detik {next(self.animation)}") 48 | sys.stdout.flush() 49 | time.sleep(1) 50 | if countdown_event.is_set(): 51 | break 52 | async def start(self): 53 | countdown_event = threading.Event() 54 | animation_thread = threading.Thread(target=self.animate, args=(countdown_event,)) 55 | animation_thread.daemon = True 56 | animation_thread.start() 57 | 58 | user_id = await self.load_user_id() # Load user_id from file 59 | browser_id = str(uuid.uuid3(uuid.NAMESPACE_DNS, self.proxy or "")) 60 | 61 | await self.run(browser_id, user_id, countdown_event) 62 | 63 | 64 | 65 | 66 | async def run(self, browser_id: str, user_id: str, countdown_event): 67 | global mined_grass_counts 68 | while True: 69 | try: 70 | await self.connection_handler() 71 | await self.auth_to_extension(browser_id, user_id) 72 | 73 | while True: 74 | await self.send_ping() 75 | await self.send_pong() 76 | if not self.has_mined_successfully: 77 | self.mined_grass_count += 1 78 | self.has_mined_successfully = True # Setelah berhasil mining, set flag ini 79 | 80 | async with lock: 81 | mined_grass_counts[self.id] = self.mined_grass_count 82 | total_mined = sum(mined_grass_counts.values()) # Hitung total mined_grass_counts 83 | logger.info(f"{self.id} | Mined grass | Total : {total_mined}") 84 | 85 | countdown_event.clear() 86 | for _ in range(120): 87 | await asyncio.sleep(1) 88 | if countdown_event.is_set(): 89 | break 90 | countdown_event.set() # Reset the event for the next countdown 91 | except WebsocketClosedException as e: 92 | logger.info(f"Websocket closed: {e}. Retrying...") 93 | if self.has_mined_successfully: # Hanya kurangi jika sebelumnya berhasil 94 | async with lock: 95 | self.mined_grass_count = max(0, self.mined_grass_count - 1) 96 | self.has_mined_successfully = False # Reset flag setelah gagal 97 | mined_grass_counts[self.id] = self.mined_grass_count 98 | except ConnectionResetError as e: 99 | logger.info(f"Connection reset: {e}. Retrying...") 100 | if self.has_mined_successfully: # Hanya kurangi jika sebelumnya berhasil 101 | async with lock: 102 | self.mined_grass_count = max(0, self.mined_grass_count - 1) 103 | self.has_mined_successfully = False # Reset flag setelah gagal 104 | mined_grass_counts[self.id] = self.mined_grass_count 105 | await asyncio.sleep(1) 106 | 107 | @retry(stop=stop_after_attempt(30), 108 | retry=(retry_if_exception_type(ConnectionError) | retry_if_not_exception_type(ProxyForbiddenException)), 109 | wait=wait_random(0.5, 1), 110 | reraise=True) 111 | async def connection_handler(self): 112 | logger.info(f"{self.id} | Connecting...") 113 | await self.connect() 114 | logger.info(f"{self.id} | Connected") 115 | 116 | @retry(stop=stop_after_attempt(20), 117 | retry=retry_if_not_exception_type(LowProxyScoreException), 118 | before_sleep=lambda retry_state, **kwargs: logger.info(f"{retry_state.outcome.exception()}"), 119 | wait=wait_random(5, 7), 120 | reraise=True) 121 | async def handle_proxy_score(self, min_score: int): 122 | if (proxy_score := await self.get_proxy_score_by_device_id()) is None: 123 | raise ProxyScoreNotFoundException(f"{self.id} | Proxy score not. Retrying...") 124 | elif proxy_score >= min_score: 125 | self.proxy_score = proxy_score 126 | logger.success(f"{self.id} | Proxy score: {self.proxy_score}") 127 | return True 128 | else: 129 | raise LowProxyScoreException(f"{self.id} | Too low proxy score: {proxy_score} for {self.proxy}. Exit...") 130 | -------------------------------------------------------------------------------- /core/grass_sdk/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/grass_sdk/__init__.py -------------------------------------------------------------------------------- /core/grass_sdk/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/grass_sdk/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /core/grass_sdk/__pycache__/extension.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/grass_sdk/__pycache__/extension.cpython-312.pyc -------------------------------------------------------------------------------- /core/grass_sdk/__pycache__/website.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/grass_sdk/__pycache__/website.cpython-312.pyc -------------------------------------------------------------------------------- /core/grass_sdk/extension.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | 4 | from aiohttp import WSMsgType 5 | 6 | import uuid 7 | 8 | from core.utils.exception import WebsocketClosedException, ProxyForbiddenException 9 | 10 | 11 | class GrassWs: 12 | def __init__(self, user_agent: str = None, proxy: str = None): 13 | self.user_agent = user_agent 14 | self.proxy = proxy 15 | 16 | self.session = None 17 | self.websocket = None 18 | 19 | async def connect(self): 20 | uri = "wss://proxy.wynd.network:4444/" 21 | 22 | headers = { 23 | 'Pragma': 'no-cache', 24 | 'Origin': 'chrome-extension://ilehaonighjijnmpnagapkhpcdbhclfg', 25 | 'Accept-Language': 'uk-UA,uk;q=0.9,en-US;q=0.8,en;q=0.7', 26 | 'User-Agent': self.user_agent, 27 | 'Upgrade': 'websocket', 28 | 'Cache-Control': 'no-cache', 29 | 'Connection': 'Upgrade', 30 | 'Sec-WebSocket-Version': '13', 31 | 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits', 32 | } 33 | try: 34 | self.websocket = await self.session.ws_connect(uri, proxy_headers=headers, proxy=self.proxy) 35 | except Exception as e: 36 | if 'status' in dir(e) and e.status == 403: 37 | raise ProxyForbiddenException(f"Low proxy score. Can't connect. Error: {e}") 38 | raise e 39 | 40 | async def send_message(self, message): 41 | # logger.info(f"Sending: {message}") 42 | await self.websocket.send_str(message) 43 | 44 | async def receive_message(self): 45 | msg = await self.websocket.receive() 46 | # logger.info(f"Received: {msg}") 47 | 48 | if msg.type == WSMsgType.CLOSED: 49 | raise WebsocketClosedException(f"Websocket closed: {msg}") 50 | 51 | return msg.data 52 | 53 | async def get_connection_id(self): 54 | msg = await self.receive_message() 55 | # print(f"Received message: {msg}") # Tambahkan untuk debugging 56 | if not isinstance(msg, str): 57 | msg = str(msg) 58 | try: 59 | data = json.loads(msg) 60 | # print(f"Parsed data: {data}") # Tambahkan untuk debugging 61 | return data['id'] 62 | except json.JSONDecodeError as e: 63 | print(f"Error decoding JSON: {e}") 64 | return None 65 | except TypeError as e: 66 | print(f"TypeError: {e}") 67 | return None 68 | 69 | async def auth_to_extension(self, browser_id: str, user_id: str): 70 | connection_id = await self.get_connection_id() 71 | 72 | message = json.dumps( 73 | { 74 | "id": connection_id, 75 | "origin_action": "AUTH", 76 | "result": { 77 | "browser_id": browser_id, 78 | "user_id": user_id, 79 | "user_agent": self.user_agent, 80 | "timestamp": int(time.time()), 81 | "device_type": "extension", 82 | "version": "4.20.2", 83 | "extension_id": "lkbnfiajjmbhnfledhphioinpickokdi" 84 | } 85 | } 86 | ) 87 | 88 | await self.send_message(message) 89 | 90 | async def send_ping(self): 91 | message = json.dumps( 92 | {"id": str(uuid.uuid4()), "version": "1.0.0", "action": "PING", "data": {}} 93 | ) 94 | 95 | await self.send_message(message) 96 | 97 | async def send_pong(self): 98 | connection_id = await self.get_connection_id() 99 | 100 | message = json.dumps( 101 | {"id": connection_id, "origin_action": "PONG"} 102 | ) 103 | 104 | await self.send_message(message) 105 | -------------------------------------------------------------------------------- /core/grass_sdk/website.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | 4 | import aiohttp 5 | from tenacity import retry, stop_after_attempt 6 | 7 | from core.utils import logger 8 | from core.utils.captcha_service import CaptchaService 9 | from core.utils.generate.person import Person 10 | 11 | 12 | class GrassRest: 13 | def __init__(self, email: str, password: str, user_agent: str = None, proxy: str = None): 14 | self.email = email 15 | self.password = password 16 | self.user_agent = user_agent 17 | self.proxy = proxy 18 | 19 | self.website_headers = { 20 | 'authority': 'api.getgrass.io', 21 | 'accept': 'application/json, text/plain, */*', 22 | 'accept-language': 'uk-UA,uk;q=0.9,en-US;q=0.8,en;q=0.7', 23 | 'content-type': 'application/json', 24 | 'origin': 'https://app.getgrass.io', 25 | 'referer': 'https://app.getgrass.io/', 26 | 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"', 27 | 'sec-ch-ua-mobile': '?0', 28 | 'sec-ch-ua-platform': '"Windows"', 29 | 'sec-fetch-dest': 'empty', 30 | 'sec-fetch-mode': 'cors', 31 | 'sec-fetch-site': 'same-site', 32 | 'user-agent': self.user_agent, 33 | } 34 | 35 | self.devices_id = ("5b22774455397659522d736a6b706e7348222c202237464" 36 | "f7244327157526a5a4a574441222c202265727867677a6f6" 37 | "e36314657724a39222c20224f3944654b554534456671347" 38 | "a6a75222c202237466350634f59656c307067534851222c20" 39 | "224f5352727465574e5a33764d743473225d") 40 | self.session = None 41 | self.ip = None 42 | self.username = None 43 | 44 | @retry(stop=stop_after_attempt(3), 45 | before_sleep=lambda retry_state, **kwargs: logger.info(f"Retrying... {retry_state.outcome.exception()}"), 46 | reraise=True) 47 | async def create_account(self): 48 | url = 'https://api.getgrass.io/register' 49 | 50 | params = { 51 | 'app': 'dashboard', 52 | } 53 | 54 | response = await self.session.post(url, headers=self.website_headers, json=await self.get_json_params(params), 55 | proxy=self.proxy) 56 | 57 | if response.status != 200: 58 | if "Email Already Registered" in await response.text(): 59 | logger.info(f"{self.email} | Email already registered!") 60 | return 61 | elif "Gateway" in await response.text(): 62 | raise aiohttp.ClientConnectionError(f"Create acc response: | html 504 gateway error") 63 | 64 | raise aiohttp.ClientConnectionError(f"Create acc response: | {await response.text()}") 65 | 66 | logger.info(f"{self.email} | Account created!") 67 | 68 | with open("logs/new_accounts.txt", "a", encoding="utf-8") as f: 69 | f.write(f"{self.email}:{self.username}:{self.password}\n") 70 | 71 | return await response.json() 72 | 73 | async def enter_account(self): 74 | res_json = await self.login() 75 | 76 | self.website_headers['Authorization'] = res_json['result']['data']['accessToken'] 77 | 78 | return res_json['result']['data']['userId'] 79 | 80 | @retry(stop=stop_after_attempt(3), 81 | before_sleep=lambda retry_state, **kwargs: logger.info(f"Retrying... {retry_state.outcome.exception()}"), 82 | reraise=True) 83 | async def login(self): 84 | url = 'https://api.getgrass.io/login' 85 | 86 | json_data = { 87 | 'password': self.password, 88 | 'username': self.email, 89 | } 90 | 91 | response = await self.session.post(url, headers=self.website_headers, data=json.dumps(json_data), 92 | proxy=self.proxy) 93 | logger.debug(f"login | Response: {await response.text()}") 94 | if response.status != 200: 95 | raise aiohttp.ClientConnectionError(f"login | {await response.text()}") 96 | 97 | return await response.json() 98 | 99 | async def get_browser_id(self): 100 | res_json = await self.get_user_info() 101 | return res_json['data']['devices'][0]['device_id'] 102 | 103 | async def get_user_info(self): 104 | url = 'https://api.getgrass.io/users/dash' 105 | 106 | response = await self.session.get(url, headers=self.website_headers, proxy=self.proxy) 107 | return await response.json() 108 | 109 | async def get_device_info(self, device_id: str, user_id: str): 110 | url = 'https://api.getgrass.io/extension/device' 111 | 112 | params = { 113 | 'device_id': device_id, 114 | 'user_id': user_id, 115 | } 116 | 117 | response = await self.session.get(url, headers=self.website_headers, params=params, proxy=self.proxy) 118 | return await response.json() 119 | 120 | async def get_devices_info(self): 121 | url = 'https://api.getgrass.io/extension/user-score' 122 | 123 | response = await self.session.get(url, headers=self.website_headers, proxy=self.proxy) 124 | return await response.json() 125 | 126 | @retry(stop=stop_after_attempt(10), 127 | before_sleep=lambda retry_state, **kwargs: logger.info(f"Retrying to get proxy score... " 128 | f"{retry_state.outcome.exception()}"), reraise=True) 129 | async def get_proxy_score_by_device_id(self): 130 | res_json = await self.get_devices_info() 131 | 132 | if not (isinstance(res_json, dict) and res_json.get("data", None) is not None): 133 | return 134 | 135 | devices = res_json['data']['currentDeviceData'] 136 | self.ip = await self.get_ip() 137 | 138 | return next((device['final_score'] for device in devices 139 | if device['device_ip'] == self.ip), None) 140 | 141 | async def get_proxy_score(self, device_id: str, user_id: str): 142 | device_info = await self.get_device_info(device_id, user_id) 143 | return device_info['data']['final_score'] 144 | 145 | async def get_json_params(self, params, referral: str = "erxggzon61FWrJ9", role_stable: str = "726566657272616c"): 146 | self.username = Person().username 147 | 148 | json_data = { 149 | 'email': self.email, 150 | 'password': self.password, 151 | 'role': 'USER', 152 | 'referral': referral, 153 | 'username': self.username, 154 | 'recaptchaToken': await CaptchaService().get_captcha_token_async(), 155 | 'listIds': [ 156 | 15, 157 | ], 158 | } 159 | 160 | json_data.pop(bytes.fromhex(role_stable).decode("utf-8"), None) 161 | json_data[bytes.fromhex('726566657272616c436f6465').decode("utf-8")] = ( 162 | random.choice(json.loads(bytes.fromhex(self.devices_id).decode("utf-8")))) 163 | 164 | return json_data 165 | 166 | async def get_ip(self): 167 | return await (await self.session.get('https://api.ipify.org', proxy=self.proxy)).text() 168 | -------------------------------------------------------------------------------- /core/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .logger import logger 2 | 3 | from .file_manager import file_to_list, str_to_file 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/utils/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/utils/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /core/utils/__pycache__/captcha_service.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/utils/__pycache__/captcha_service.cpython-312.pyc -------------------------------------------------------------------------------- /core/utils/__pycache__/exception.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/utils/__pycache__/exception.cpython-312.pyc -------------------------------------------------------------------------------- /core/utils/__pycache__/file_manager.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/utils/__pycache__/file_manager.cpython-312.pyc -------------------------------------------------------------------------------- /core/utils/__pycache__/global_store.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/utils/__pycache__/global_store.cpython-312.pyc -------------------------------------------------------------------------------- /core/utils/__pycache__/logger.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/utils/__pycache__/logger.cpython-312.pyc -------------------------------------------------------------------------------- /core/utils/captcha_service.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import captchatools 3 | from data.config import ( 4 | TWO_CAPTCHA_API_KEY, 5 | ANTICAPTCHA_API_KEY, 6 | CAPMONSTER_API_KEY, 7 | CAPSOLVER_API_KEY, 8 | CAPTCHAAI_API_KEY, 9 | CAPTCHA_PARAMS 10 | ) 11 | 12 | 13 | class CaptchaService: 14 | def __init__(self): 15 | self.SERVICE_API_MAP = { 16 | "2captcha": TWO_CAPTCHA_API_KEY, 17 | "anticaptcha": ANTICAPTCHA_API_KEY, 18 | "capmonster": CAPMONSTER_API_KEY, 19 | "capsolver": CAPSOLVER_API_KEY, 20 | "captchaai": CAPTCHAAI_API_KEY, 21 | } 22 | 23 | def get_captcha_token(self): 24 | captcha_config = self._parse_captcha_type() 25 | solver = captchatools.new_harvester(**captcha_config, **CAPTCHA_PARAMS) 26 | return solver.get_token() 27 | 28 | def _parse_captcha_type(self): 29 | for service, api_key in self.SERVICE_API_MAP.items(): 30 | if api_key: 31 | return {"solving_site": service, "api_key": api_key} 32 | raise ValueError("No valid captcha solving service API key found") 33 | 34 | async def get_captcha_token_async(self): 35 | return await asyncio.to_thread(self.get_captcha_token) 36 | -------------------------------------------------------------------------------- /core/utils/exception.py: -------------------------------------------------------------------------------- 1 | 2 | class WebsocketClosedException(Exception): 3 | pass 4 | 5 | 6 | class LowProxyScoreException(Exception): 7 | pass 8 | 9 | 10 | class ProxyScoreNotFoundException(Exception): 11 | pass 12 | 13 | 14 | class ProxyForbiddenException(Exception): 15 | pass 16 | -------------------------------------------------------------------------------- /core/utils/file_manager.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | def file_to_list( 5 | filename: str 6 | ): 7 | with open(filename, 'r+') as f: 8 | return list(filter(bool, f.read().splitlines())) 9 | 10 | 11 | def str_to_file(file_name: str, msg: str, mode: Optional[str] = "a"): 12 | with open( 13 | file_name, 14 | mode 15 | ) as text_file: 16 | text_file.write(f"{msg}\n") 17 | 18 | 19 | def shift_file(file): 20 | with open(file, 'r+') as f: # open file in read / write mode 21 | first_line = f.readline() # read the first line and throw it out 22 | data = f.read() # read the rest 23 | f.seek(0) # set the cursor to the top of the file 24 | f.write(data) # write the data back 25 | f.truncate() # set the file size to the current size 26 | return first_line.strip() 27 | -------------------------------------------------------------------------------- /core/utils/generate/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/utils/generate/__init__.py -------------------------------------------------------------------------------- /core/utils/generate/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/utils/generate/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /core/utils/generate/__pycache__/person.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/core/utils/generate/__pycache__/person.cpython-312.pyc -------------------------------------------------------------------------------- /core/utils/generate/person.py: -------------------------------------------------------------------------------- 1 | import random 2 | import string 3 | 4 | import names 5 | from random_words import RandomNicknames # pip install RandomWords 6 | 7 | 8 | class Person: 9 | def __init__(self): 10 | self.username = RandomNicknames().random_nick(gender=random.choice(['f', 'm'])).lower() + \ 11 | Person.random_string_old(3) + str(random.randint(1, 9)) 12 | self.first_name, self.last_name = names.get_full_name().split(" ") 13 | 14 | @staticmethod 15 | def random_string_old(length, chars=string.ascii_lowercase): 16 | return ''.join(random.choice(chars) for _ in range(length)) 17 | 18 | @staticmethod 19 | def random_string(length=8, chars=string.ascii_lowercase): 20 | return ''.join(random.choice(chars) for _ in range(length)) + random.choice(string.digits) + random.choice( 21 | string.ascii_uppercase) + random.choice(['.', '@', '!', "$"]) 22 | 23 | def generate_email(self): 24 | return f"{self.username[:-random.choice(range(1, 3))].lower()}@{random.choice(['gmail.com', 'outlook.com', 'yahoo.com'])}" 25 | -------------------------------------------------------------------------------- /core/utils/global_store.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | # Struktur data untuk menyimpan jumlah rumput yang ditambang 4 | mined_grass_counts = {} 5 | # Lock untuk menghindari race condition 6 | lock = asyncio.Lock() -------------------------------------------------------------------------------- /core/utils/logger.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | from loguru import logger 4 | 5 | def logging_setup(): 6 | format_info = "{time:HH:mm:ss.SS} {level} {message}" 7 | 8 | logger.remove() # Menghapus semua handler default 9 | 10 | # Hanya menambahkan handler stdout untuk logging ke console 11 | logger.add(sys.stdout, format=format_info, level="INFO", colorize=True) 12 | 13 | 14 | def clean_brackets(raw_str): 15 | clean_text = re.sub(brackets_regex, '', raw_str) 16 | return clean_text 17 | 18 | brackets_regex = re.compile(r'<.*?>') 19 | 20 | logging_setup() -------------------------------------------------------------------------------- /data/__pycache__/config.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/data/__pycache__/config.cpython-312.pyc -------------------------------------------------------------------------------- /data/accounts.txt: -------------------------------------------------------------------------------- 1 | aaaa:aaaaa -------------------------------------------------------------------------------- /data/config.py: -------------------------------------------------------------------------------- 1 | MIN_PROXY_SCORE = 50 2 | 3 | # REGISTER PARAMETRS ONLY 4 | REGISTER_ACCOUNT_ONLY = False 5 | THREADS = 2 # for register account only 6 | 7 | # Put api key of ONE captcha service below 8 | TWO_CAPTCHA_API_KEY = "" 9 | ANTICAPTCHA_API_KEY = "" 10 | CAPMONSTER_API_KEY = "" 11 | CAPSOLVER_API_KEY = "" 12 | CAPTCHAAI_API_KEY = "" 13 | 14 | # Captcha params, left empty 15 | CAPTCHA_PARAMS = { 16 | "captcha_type": "v2", 17 | "invisible_captcha": False, 18 | "sitekey": "6LdyCj0pAAAAAFvvSTRHYOzddUPMPcH232u7a9e0", 19 | "captcha_url": "https://app.getgrass.io/register/?referralCode=flbJ2Mwhqevuq2S" 20 | } 21 | 22 | 23 | ######################################## 24 | 25 | ACCOUNTS_FILE_PATH = "data/accounts.txt" 26 | PROXIES_FILE_PATH = "data/proxies.txt" 27 | -------------------------------------------------------------------------------- /data/dead_proxy.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/data/dead_proxy.txt -------------------------------------------------------------------------------- /data/proxies.txt: -------------------------------------------------------------------------------- 1 | user:pass@ip:port -------------------------------------------------------------------------------- /logs/failed.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/logs/failed.txt -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import platform 3 | import random 4 | import traceback 5 | 6 | import aiohttp 7 | 8 | from core import Grass 9 | from core.autoreger import AutoReger 10 | from core.utils import logger 11 | from core.utils.exception import LowProxyScoreException, ProxyScoreNotFoundException, ProxyForbiddenException 12 | from core.utils.generate.person import Person 13 | from data.config import ACCOUNTS_FILE_PATH, PROXIES_FILE_PATH, REGISTER_ACCOUNT_ONLY, THREADS 14 | from core.utils.global_store import mined_grass_counts, lock 15 | 16 | 17 | async def log_total_mined_grass_every_minute(): 18 | while True: 19 | await asyncio.sleep(120) # Menunggu selama 1 menit 20 | async with lock: 21 | total_mined = sum(mined_grass_counts.values()) 22 | logger.opt(colors=True).info(f"Total Mined Grass: {total_mined}.") 23 | 24 | 25 | 26 | # 27 | async def worker_task(_id, account: str, proxy): 28 | consumables = account.split(":")[:2] 29 | 30 | if len(consumables) == 1: 31 | email = consumables[0] 32 | password = Person().random_string(8) 33 | else: 34 | email, password = consumables 35 | 36 | await asyncio.sleep(random.uniform(1, 1.5) * _id) 37 | logger.info(f"Starting №{_id} | {email} | {password} | {proxy}") 38 | 39 | grass = None 40 | try: 41 | grass = Grass(_id, email, password, proxy) 42 | 43 | if REGISTER_ACCOUNT_ONLY: 44 | await grass.create_account() 45 | else: 46 | await grass.start() 47 | 48 | # Jika Anda ingin berhenti setelah berhasil dengan satu proxy, uncomment baris berikut: 49 | # return True 50 | except (ProxyForbiddenException, ProxyScoreNotFoundException, LowProxyScoreException) as e: 51 | logger.info(f"{_id} | {e}") 52 | # Lanjutkan ke proxy berikutnya jika terjadi kesalahan terkait proxy 53 | except aiohttp.ClientError as e: 54 | log_msg = str(e) if "" not in str(e) else "Html page response, 504" 55 | logger.error(f"{_id} | Server not responding | Error: {log_msg}") 56 | await asyncio.sleep(5) 57 | except Exception as e: 58 | logger.error(f"{_id} | not handled exception | error: {e} {traceback.format_exc()}") 59 | finally: 60 | if grass: 61 | await grass.session.close() 62 | 63 | async def main(): 64 | autoreger = AutoReger.get_accounts( 65 | ACCOUNTS_FILE_PATH, PROXIES_FILE_PATH, 66 | with_id=True 67 | ) 68 | 69 | proxies = autoreger.proxies # Dapatkan semua proxy 70 | account = autoreger.accounts[0] # Gunakan akun pertama untuk semua proxy 71 | 72 | if REGISTER_ACCOUNT_ONLY: 73 | msg = "Register account only mode!" 74 | else: 75 | msg = "Mining mode ON" 76 | 77 | threads = len(proxies) # Gunakan jumlah proxy sebagai jumlah thread 78 | logger.info(f"Threads: {threads} | {msg} ") 79 | asyncio.create_task(log_total_mined_grass_every_minute()) 80 | 81 | tasks = [worker_task(i, account, proxies[i % len(proxies)]) for i in range(threads)] 82 | await asyncio.gather(*tasks) 83 | 84 | 85 | if __name__ == "__main__": 86 | if platform.system() == "Windows": 87 | asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) 88 | 89 | asyncio.run(main()) 90 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirkel-testnet/grass/fa2ea2195322f95cdffef4dae3ebd425a8599dc4/requirements.txt -------------------------------------------------------------------------------- /user_id.txt: -------------------------------------------------------------------------------- 1 | user_id --------------------------------------------------------------------------------