├── 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 | 
35 | 
--------------------------------------------------------------------------------
/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 "