Skip to content
This repository was archived by the owner on Jul 31, 2025. It is now read-only.

Commit 869b3c2

Browse files
committed
Fixed the problem, when the program collected mainly on left side of screen (now it collects both); Fixed the problem when the program exited when an error occured; UJson -> Json; Refactored some bad-habit code; Better logs and information in them; Updated main menu design and info; Added --idp flag that will automatically install the requirements.txt for you
1 parent f49bb7e commit 869b3c2

File tree

7 files changed

+69
-90
lines changed

7 files changed

+69
-90
lines changed

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
</p>
1414
</div>
1515

16+
## ☕ Donation method
17+
USDT (ERC-20): 0x5F06C1c23aF7Cc644B8cBaF0e2b294CbA15CC745
18+
TON: UQD2g_C_aIeZ7-zAJ7uOQJUsr538vPcd_GljLeA-iRZL7tzF
1619

1720
## ⚡ v2 Features
1821
1. Better and faster detection of points;
@@ -31,14 +34,12 @@ Clone this [**Repository**](https://github.com/devbutlazy/Blum-AutoClicker) and
3134
# GNU/Linux
3235
~/Blum-AutoClicker >>> python3 -m venv venv # Create a virtual environment
3336
~/Blum-AutoClicker >>> source venv/bin/activate # Activate the venv
34-
~/Blum-AutoClicker >>> pip install -r requirements.txt # Install dependencies
35-
~/Blum-AutoClicker >>> python3 main.py # Run the soft
37+
~/Blum-AutoClicker >>> python3 main.py --idp # Run the soft and install dependencies
3638

3739
# Windows
3840
~/Blum-AutoClicker >>> python -m venv venv # Create a virtual environment
3941
~/Blum-AutoClicker >>> venv\Scripts\activate # Activate the venv
40-
~/Blum-AutoClicker >>> pip install -r requirements.txt # Install dependencies
41-
~/Blum-AutoClicker >>> python main.py # Run the soft
42+
~/Blum-AutoClicker >>> python main.py --idp # Run the soft and install dependencies
4243
```
4344

4445
> [!TIP]
@@ -101,13 +102,10 @@ Without this people the developing proccess wouldn't happen so fast (or at all),
101102
- [qt333](https://github.com/qt333) - helped with dogs and replay button recognition
102103
- [BRamil](https://github.com/BRamil1) - is responsible for Polish localization
103104
- [Redish](https://github.com/xxmmcxx) - is responsible for Farsi localization
105+
- [vjaykrsna](https://github.com/vjaykrsna) - helped with fixing the collecting only on left side issue
104106

105107
<br>
106108

107-
## ☕ Buy me a coffee
108-
USDT (ERC-20): 0x5F06C1c23aF7Cc644B8cBaF0e2b294CbA15CC745
109-
TON: UQBTHDZJnuDr4-v6oc_cDRXYdqggIoQA_tLGv5z2li4DC7GI
110-
111109

112110
> [!NOTE]
113111
> This repository is protected by **[MIT License](https://opensource.org/license/mit)**, please avoid corrupting it.

core/clicker/blum.py

Lines changed: 29 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from core.localization.localization import get_language
1515
from core.config.config import get_config_value
1616

17-
from typing import Tuple, Any
17+
from typing import Tuple, List, Any
1818

1919

2020
class BlumClicker:
@@ -48,27 +48,35 @@ async def handle_input(self) -> bool:
4848
return self.paused
4949

5050
@staticmethod
51-
def collect_green(screen: Any, rect: Tuple[int, int, int, int]) -> bool:
51+
def collect_green(screen: Any, rect: Tuple[int, int, int, int], sides: List[str] = ["left", "right"]) -> bool:
5252
"""
53-
Click on the found point.
53+
Click on the found green button.
5454
5555
:param screen: the screenshot
5656
:param rect: the rectangle
57+
:param sides: the sides to scan
5758
:return: whether the image was found
5859
"""
5960
width, height = screen.size
60-
61-
for x, y in product(range(0, width, 20), range(0, int(height * 0.8272), 20)):
61+
scan_height = int(height * 0.8272)
62+
63+
play_button_check = screen.getpixel((int(width * 0.80), int(height * 0.63))) == (255, 255, 255)
64+
65+
x_ranges = {
66+
"left": range(0, width // 2, 20),
67+
"right": range(width // 2, width, 20)
68+
}
69+
70+
x_values = (x for side in sides if side in x_ranges for x in x_ranges[side])
71+
72+
for x, y in product(x_values, range(0, scan_height, 20)):
6273
r, g, b = screen.getpixel((x, y))
63-
greenish_range = (b < 125) and (102 <= r < 220) and (200 <= g < 255)
64-
play_button = screen.getpixel((int(width * 0.80), int(height * 0.63)))
65-
66-
if greenish_range and not play_button == (255, 255, 255):
67-
screen_x = rect[0] + x
68-
screen_y = rect[1] + y
74+
75+
if (b < 125) and (102 <= r < 220) and (200 <= g < 255) and not play_button_check:
76+
screen_x, screen_y = rect[0] + x, rect[1] + y
6977
mouse.move(screen_x, screen_y, absolute=True)
7078
mouse.click(button=mouse.LEFT)
71-
79+
7280
return True
7381

7482
return False
@@ -84,7 +92,7 @@ def collect_freeze(screen: Any, rect: Tuple[int, int, int, int]) -> bool:
8492
"""
8593
width, height = screen.size
8694

87-
for x, y in product(range(0, width, 20), range(0, height, 20)):
95+
for x, y in product(range(0, width, 10), range(0, height, 10)):
8896
r, g, b = screen.getpixel((x, y))
8997
blueish_range = (215 < b < 255) and (100 <= r < 166) and (220 <= g < 254)
9098

@@ -97,33 +105,6 @@ def collect_freeze(screen: Any, rect: Tuple[int, int, int, int]) -> bool:
97105

98106
return False
99107

100-
@staticmethod
101-
def collect_pumpkin(screen: Any, rect: Tuple[int, int, int, int]) -> bool:
102-
"""
103-
Click on the found point.
104-
105-
:param screen: the screenshot
106-
:param rect: the rectangle
107-
:return: whether the image was found
108-
"""
109-
width, height = screen.size
110-
111-
for x, y in product(range(0, width, 20), range(0, height, 20)):
112-
r, g, b = screen.getpixel((x, y))
113-
pumpkin_range = (35 < b < 75) and (200 <= r < 245) and (114 <= g < 140)
114-
115-
play_button = screen.getpixel((int(width * 0.80), int(height * 0.63)))
116-
117-
if pumpkin_range and not play_button == (255, 255, 255):
118-
screen_x = rect[0] + x
119-
screen_y = rect[1] + y
120-
mouse.move(screen_x, screen_y, absolute=True)
121-
mouse.click(button=mouse.LEFT)
122-
123-
return True
124-
125-
return False
126-
127108
@staticmethod
128109
def detect_reload_screen(screen: Any) -> bool:
129110
"""
@@ -134,8 +115,8 @@ def detect_reload_screen(screen: Any) -> bool:
134115
"""
135116
width, height = screen.size
136117

137-
x1, y1 = (math.ceil(width * 0.43781), math.ceil(height * 0.60252)) # grey reload button
138-
x2, y2 = (math.ceil(width * 0.24626), math.ceil(height * 0.429775)) # white pixel on word
118+
x1, y1 = (math.ceil(width * 0.43781), math.ceil(height * 0.60252))
119+
x2, y2 = (math.ceil(width * 0.24626), math.ceil(height * 0.429775))
139120

140121
reload_button = screen.getpixel((x1, y1))
141122
white_pixel = screen.getpixel((x2, y2))
@@ -162,21 +143,19 @@ def detect_replay(self, screen: Any, rect: Tuple[int, int, int, int]) -> bool:
162143
screen_x = rect[0] + int(screen.size[0] * 0.3075)
163144
screen_y = rect[1] + int(screen.size[1] * 0.87)
164145

165-
color = pyautogui.pixel(screen_x, screen_y)
166-
167-
if not color == (255, 255, 255):
146+
if not pyautogui.pixel(screen_x, screen_y) == (255, 255, 255):
168147
return False
169148

170149
if self.replays >= max_replays:
171-
logger.error(
150+
return logger.error(
172151
get_language("REPLAY_LIMIT_REACHED").format(replays=max_replays)
173152
)
174-
os._exit(0)
175153

154+
delay = random.randint(replay_delay, replay_delay + 3) + random.random()
176155
logger.debug(
177-
f"Detected the replay button. Remaining replays: {max_replays - self.replays}"
156+
f"Detected the replay button. Remaining replays: {max_replays - self.replays} // Delay: {delay:.2f}"
178157
)
179-
time.sleep(random.randint(replay_delay, replay_delay + 3) + random.random())
158+
time.sleep(delay)
180159

181160
mouse.move(
182161
screen_x + random.randint(1, 10),
@@ -186,8 +165,8 @@ def detect_replay(self, screen: Any, rect: Tuple[int, int, int, int]) -> bool:
186165
mouse.click(button=mouse.LEFT)
187166

188167
time.sleep(1)
189-
190168
self.replays += 1
169+
191170
return True
192171

193172
async def run(self) -> None:
@@ -211,7 +190,6 @@ async def run(self) -> None:
211190

212191
screenshot = self.utils.capture_screenshot(rect)
213192

214-
self.collect_pumpkin(screenshot, rect)
215193
self.collect_green(screenshot, rect)
216194
self.collect_freeze(screenshot, rect)
217195

core/clicker/misc.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from typing import Tuple, Any
55
from dataclasses import dataclass
66

7-
87
@dataclass
98
class Utilities:
109

@@ -16,7 +15,7 @@ def get_rect(window) -> Tuple[int, int, int, int]:
1615
:param window: The window object
1716
:return: A tuple containing the coordinates (left, top, width, height)
1817
"""
19-
return window.left, window.top, window.width, window.height
18+
return (window.left, window.top, window.width, window.height)
2019

2120
@staticmethod
2221
def capture_screenshot(rect: Tuple[int, int, int, int]) -> Any:
@@ -38,17 +37,18 @@ def get_window() -> Any:
3837
windows = next(
3938
(
4039
pwc.getWindowsWithTitle(opt)
41-
for opt in ["TelegramDesktop", "64Gram", "Nekogram", "AyuGram"]
40+
for opt in ["TelegramDesktop", "64Gram", "AyuGram", "telegram-desktop"]
4241
if pwc.getWindowsWithTitle(opt)
4342
),
4443
None,
4544
)
4645

47-
if windows and not windows[0].isActive:
48-
windows[0].minimize()
49-
windows[0].restore()
46+
window = windows[0] if windows else None
5047

51-
return windows[0]
48+
if window and not window.isActive:
49+
window.minimize()
50+
window.restore()
5251

53-
return None
52+
return window
5453

54+
return None

core/config/config.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import ujson
1+
import json
22
import os
33

44
from core.logger.logger import logger
@@ -58,23 +58,21 @@ def set_config(key: str, value: Any) -> None:
5858
"""
5959
try:
6060
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
61-
config: Dict[str, Any] = ujson.load(f)
61+
config: Dict[str, Any] = json.load(f)
6262

6363
if key == "LANGUAGE":
6464
value = Language.normalize(value)
6565

6666
config[key] = value
6767

6868
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
69-
ujson.dump(config, f, indent=4)
69+
json.dump(config, f, indent=4)
7070

71-
except (FileNotFoundError, ujson.JSONDecodeError) as e:
71+
except (FileNotFoundError, json.JSONDecodeError) as e:
7272
logger.error(f"Error with config file: {e}")
73-
os._exit(1)
7473

7574
except Exception as e:
7675
logger.error(f"An error occurred while updating the config: {e}")
77-
os._exit(1)
7876

7977

8078
def get_config_value(key: str) -> str:
@@ -86,14 +84,12 @@ def get_config_value(key: str) -> str:
8684
"""
8785
try:
8886
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
89-
config: Dict = ujson.load(f)
87+
config: Dict = json.load(f)
9088

9189
return config.get(key, None)
9290

93-
except (FileNotFoundError, ujson.JSONDecodeError) as e:
91+
except (FileNotFoundError, json.JSONDecodeError) as e:
9492
logger.error(f"Error with config file: {e}")
95-
os._exit(1)
9693

9794
except Exception as e:
9895
logger.error(f"An error occurred while getting the config value: {e}")
99-
os._exit(1)

core/localization/localization.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import ujson
1+
import json
22
from pathlib import Path
33

44
from core.logger.logger import logger
@@ -16,12 +16,12 @@ def load_json_file(file_path: Union[str, Path]) -> Dict:
1616
"""
1717
try:
1818
with open(file_path, "r", encoding="utf-8") as file:
19-
return ujson.load(file)
19+
return json.load(file)
2020

2121
except FileNotFoundError:
2222
logger.error(f"File not found: {file_path}")
2323

24-
except ujson.JSONDecodeError:
24+
except json.JSONDecodeError:
2525
logger.error(f"Failed to decode JSON in file: {file_path}")
2626

2727
return {}
@@ -55,8 +55,4 @@ def get_language(key: str) -> str:
5555

5656
data: Dict = load_json_file(file_path) or load_json_file("core/localization/langs/en.json")
5757

58-
return ujson.dumps(
59-
data.get(key, f"Localization error: '{key}' not found."),
60-
ensure_ascii=False,
61-
indent=4
62-
) if lang == "fa" else data.get(key, f"Localization error: '{key}' not found.")
58+
return data.get(key, f"Localization error: '{key}' not found.")

main.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from core.clicker.blum import BlumClicker
66
from core.config.config import set_config
7+
from core.logger.logger import logger
78
from core.localization.localization import get_language
89

910
AUTOCLICKER_TEXT = """
@@ -21,12 +22,13 @@ async def main() -> None:
2122
os.system("cls" if os.name == "nt" else "clear")
2223

2324
print(AUTOCLICKER_TEXT)
24-
print("\033[34m" + get_language("CREDITS") + "\033[0m")
25+
print("\033[34m~ " + get_language("CREDITS") + "\033[0m")
26+
print("\033[34m~ Telegram: https://t.me/blogbutlazy & https://t.me/chatbutlazy\033[0m")
2527
print(
2628
"\033[1;33;48m"
2729
+ get_language("DONATION")
2830
+ "\033[1;37;0m "
29-
+ "UQBTHDZJnuDr4-v6oc_cDRXYdqggIoQA_tLGv5z2li4DC7GI\n"
31+
+ "UQD2g_C_aIeZ7-zAJ7uOQJUsr538vPcd_GljLeA-iRZL7tzF\n"
3032
)
3133

3234
clicker = BlumClicker()
@@ -55,18 +57,28 @@ async def main() -> None:
5557
type=int,
5658
help="Set the delay between replays in seconds (e.g., --delay 5)",
5759
)
60+
parser.add_argument(
61+
"--idp",
62+
"--install-dependencies",
63+
"--dependencies",
64+
nargs="?",
65+
const=1,
66+
help="Install dependencies (e.g., --idp)",
67+
)
5868
args = parser.parse_args()
5969

6070
config_mapping = {
6171
"lang": ("LANGUAGE", args.lang),
6272
"replays": ("REPLAYS", args.replays),
63-
"delay": ("REPLAY_DELAY", args.delay),
73+
"delay": ("REPLAY_DELAY", args.delay)
6474
}
6575

6676
for arg, (config_key, config_value) in config_mapping.items():
6777
set_config(config_key, config_value) if config_value else None
6878

79+
os.system("pip install -r requirements.txt") if args.idp else None
80+
6981
try:
7082
asyncio.run(main())
7183
except KeyboardInterrupt:
72-
os._exit(0)
84+
logger.error("Exited due to keyboard interrupt.")

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
pyautogui
22
pywinctl
33
mouse
4-
ujson
54
pyscreeze
65
keyboard
76
loguru

0 commit comments

Comments
 (0)