├── .github └── workflows │ └── pylint.yml ├── README.md ├── constants.py ├── main.py ├── prepare_app.py └── requirements.txt /.github/workflows/pylint.yml: -------------------------------------------------------------------------------- 1 | name: Pylint 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: ["3.10", "3.11", "3.12"] 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Set up Python ${{ matrix.python-version }} 14 | uses: actions/setup-python@v3 15 | with: 16 | python-version: ${{ matrix.python-version }} 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip 20 | pip install pylint 21 | - name: Analysing the code with pylint 22 | run: | 23 | pylint $(git ls-files '*.py') --indent-string="\t" --disable=import-error --exit-zero 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BlumBot 2 | ___ 3 | ### Donations 4 | ``` 5 | TON: UQBKgZ6OViqVj2UUIdUB1WY_-sb6lJeYMXMdBORA-XRp7GtJ 6 | ``` 7 | 8 | ## To enable "TGE mode" run `main.py --tge` 9 | A simple autoclicker for blum drop mini-game on python (autoclicker collects **$dogs**) 10 | 11 | ## Usage 12 | - Start tg app, then `main.py` and press play 13 | - DO NOT CLOSE GAME WINDOW BEFORE GAME IS FINISHED 14 | - You can run `main.py [amount_of_games]`, where `[amount_of_games]` is amount of games to be played in a row 15 | - See `--help` for additional information 16 | 17 | #### Keybindigs: 18 | - `1` - decrease clicks limit 19 | - `2` - increase clicks limit 20 | 21 | ### Will I be banned for using this script? 22 | --- 23 | - In contrast to other bots, My script is interacting with user interface, NOT with Blum api, and does casual left button mouse clicks on "stars". 24 | - I CANNOT guarantee anything about punishments from Blum development team, because Im not a developer of blum 25 | - The only thing that I can guarantee is that My script is programmatically untraceable, which means Blum developers cannot definitely distinguish my script from a casual human user. 26 | -------------------------------------------------------------------------------- /constants.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | """On different devices app size is also different, 4 | so i located triggers on mine and devided it by window size on my pc, 5 | in game this coefficients are multiplied by actual window size to get correct coordinates""" 6 | TGE_MODE = '--tge' in sys.argv 7 | DOGS_DROP_TOGGLE = '--enable-dogs' in sys.argv 8 | 9 | CLICK_LIMIT = 1.0 10 | for arg in sys.argv: 11 | if '--click-limit' in arg: 12 | CLICK_LIMIT = float(arg.split('=')[1]) 13 | 14 | 15 | DEV_SCREEN_SIZE_CONST = (402, 712) 16 | 17 | APPLICATION_NAME = 'Blum' 18 | DEFAULT_COLOR_TRIGGER = { 19 | "red":{"min":90, "max":255}, 20 | "green":{"min":220, "max":255}, 21 | "blue":{"min":5, "max":55}} 22 | APPLICATION_TRIGGER = {"color":(234, 212, 12), "positions":[(60/402, 112/712), (43/402, 110/712), 23 | (102/402, 113/712), (61/402, 106/712)], 24 | "range":(range(35, 116, 5), range(100, 125, 5))} 25 | PIXELS_PER_ITERATION = 10 26 | 27 | NEW_GAME_TRIGGER_POS = (210/402, 615/712) 28 | AVG_GAME_DURATION = 30 # seconds 29 | 30 | 31 | #Dogs drop 32 | DOGS_WHITE_COLOR_RANGE = (238, 256) 33 | 34 | # TGE update 35 | 36 | TGE_COLOR_TRIGGERS = [ 37 | { 38 | "red":{"min":220, "max":255}, 39 | "green":{"min":240, "max":255}, 40 | "blue":{"min":90, "max":140} 41 | }, 42 | { 43 | "red":{"min":110, "max":255}, 44 | "green":{"min":0, "max":5}, 45 | "blue":{"min":110, "max":255} 46 | } 47 | ] 48 | 49 | 50 | HELP_STRING = \ 51 | """ 52 | Usage: main.py [AMOUNT OF GAMES] [OPTIONS] 53 | 54 | Options: 55 | --help - show this string 56 | --tge - enable \"TGE\" mode 57 | --enable-dogs - collect dogs 58 | --click-limit=n - limit clicks (Example: --click-limit=0.05, only 5% of clicks) 59 | 60 | Keybidings: 61 | 1 - decrease clicks limit 62 | 2 - increase clicks limit 63 | """ 64 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Autoclicker for Blum drop mini-game 4 | """ 5 | 6 | import sys 7 | import time 8 | import mouse 9 | import random 10 | import keyboard 11 | import datetime 12 | import dxcam_cpp as dxcam 13 | # import dxcam 14 | 15 | from PIL import Image 16 | 17 | from prepare_app import prepare_app 18 | from constants import ( 19 | TGE_MODE, 20 | CLICK_LIMIT, 21 | HELP_STRING, 22 | DOGS_DROP_TOGGLE, 23 | AVG_GAME_DURATION, 24 | TGE_COLOR_TRIGGERS, 25 | APPLICATION_TRIGGER, 26 | NEW_GAME_TRIGGER_POS, 27 | PIXELS_PER_ITERATION, 28 | DEFAULT_COLOR_TRIGGER, 29 | DOGS_WHITE_COLOR_RANGE, 30 | ) 31 | 32 | 33 | __author__ = "Wokzy" 34 | 35 | def check_running(frame, application_bbox) -> bool: 36 | """ Check if game is running by scanning color on timer positions """ 37 | 38 | for x, y in APPLICATION_TRIGGER['positions']: 39 | 40 | x *= application_bbox[2] - application_bbox[0] 41 | y *= application_bbox[3] - application_bbox[1] 42 | x = int(x) 43 | y = int(y) 44 | 45 | x += application_bbox[0] 46 | y += application_bbox[1] 47 | if frame[y][x][0] == APPLICATION_TRIGGER['color'][0]: 48 | if frame[y][x][1] == APPLICATION_TRIGGER['color'][1]: 49 | if frame[y][x][2] == APPLICATION_TRIGGER['color'][2]: 50 | return True 51 | 52 | for x in APPLICATION_TRIGGER['range'][0]: 53 | x += application_bbox[0] 54 | for y in APPLICATION_TRIGGER['range'][1]: 55 | y += application_bbox[1] 56 | if frame[y][x][0] == APPLICATION_TRIGGER['color'][0]: 57 | if frame[y][x][1] == APPLICATION_TRIGGER['color'][1]: 58 | if frame[y][x][2] == APPLICATION_TRIGGER['color'][2]: 59 | return True 60 | 61 | return False 62 | 63 | 64 | def check_object(frame, x:int, y:int) -> bool: 65 | """ Finding dropping objects by color """ 66 | 67 | def _check_color_trigger(color_trigger, check_x=x, check_y=y, limit:bool=True): 68 | if limit and random.random() > CLICK_LIMIT: 69 | return False 70 | 71 | if color_trigger['red']['min'] <= frame[check_y][check_x][0] <= color_trigger['red']['max']: 72 | if color_trigger['green']['min'] <= frame[check_y][check_x][1] <= color_trigger['green']['max']: 73 | # print(frame[check_y][check_x]) 74 | if color_trigger['blue']['min'] <= frame[check_y][check_x][2] <= color_trigger['blue']['max']: 75 | return True 76 | return False 77 | 78 | if TGE_MODE: 79 | for i in range(len(TGE_COLOR_TRIGGERS)): 80 | trigger = TGE_COLOR_TRIGGERS[i] 81 | if _check_color_trigger(TGE_COLOR_TRIGGERS[i]): 82 | return True 83 | return False 84 | elif _check_color_trigger(DEFAULT_COLOR_TRIGGER): 85 | return True 86 | 87 | #DOGS DROP 88 | if DOGS_DROP_TOGGLE: 89 | if frame[y][x][0] == frame[y][x][1] == frame[y][x][2] and DOGS_WHITE_COLOR_RANGE[0] <= frame[y][x][0] <= DOGS_WHITE_COLOR_RANGE[1]: 90 | counter = 0 91 | for i in range(-1, 2): 92 | for j in range(-4, 2): 93 | counter += (frame[y + j][x + i][0] == frame[y + j][x + i][1] == frame[y + j][x + i][2] and DOGS_WHITE_COLOR_RANGE[0] <= frame[y + j][x + i][0] <= DOGS_WHITE_COLOR_RANGE[1]) 94 | 95 | if counter >= 10: 96 | return True 97 | 98 | 99 | return False 100 | 101 | 102 | def wait_running_game(camera, timeout:float = .0) -> None: 103 | application_bbox = prepare_app() 104 | frame = camera.get_latest_frame() 105 | timer = time.time() 106 | while not check_running(frame, application_bbox): 107 | application_bbox = prepare_app() 108 | frame = camera.get_latest_frame() 109 | 110 | if timeout > 0.0: 111 | assert time.time() - timer < timeout, f"Game has not been started for {timeout} seconds, exiting" 112 | 113 | 114 | def main(): 115 | """ Autoclicker impl """ 116 | 117 | amount_of_games = 1 118 | if len(sys.argv) > 1: 119 | for arg in sys.argv: 120 | if arg.isnumeric(): 121 | amount_of_games = int(arg) 122 | break 123 | 124 | camera = dxcam.create() 125 | camera.start(target_fps=60) 126 | 127 | frame = camera.get_latest_frame() # frame is an array with shape (y, x, 3) 128 | 129 | print(f'Limited clicks: {CLICK_LIMIT}') 130 | print('Trying to detect running game, click play') 131 | wait_running_game(camera) 132 | # time.sleep(2) 133 | 134 | x_shift = 20 135 | y_shift_top = 150 136 | y_shift_bot = 250 137 | 138 | game_counter = 0 139 | 140 | application_bbox = prepare_app() 141 | 142 | x_range = range(application_bbox[0] + x_shift, application_bbox[2] - x_shift, PIXELS_PER_ITERATION) 143 | y_range = range(application_bbox[1] + y_shift_top, application_bbox[3] - y_shift_bot, PIXELS_PER_ITERATION) 144 | 145 | while game_counter < amount_of_games: 146 | game_counter += 1 147 | print(f'Game {game_counter} detected!') 148 | frame = camera.get_latest_frame() 149 | 150 | __timer = datetime.datetime.now() 151 | 152 | while (datetime.datetime.now() - __timer).total_seconds() < AVG_GAME_DURATION or check_running(frame, application_bbox): 153 | 154 | for x in x_range: 155 | for y in y_range: 156 | if check_object(frame, x, y): 157 | mouse.move(x, y, absolute=True) 158 | mouse.click(button='left') 159 | 160 | time.sleep(0.28) 161 | frame = camera.get_latest_frame() 162 | else: 163 | print('Finished') 164 | 165 | if game_counter < amount_of_games: 166 | time.sleep(0.5) 167 | x = application_bbox[0] + int(NEW_GAME_TRIGGER_POS[0] * (application_bbox[2] - application_bbox[0])) 168 | y = application_bbox[1] + int(NEW_GAME_TRIGGER_POS[1] * (application_bbox[3] - application_bbox[1])) 169 | mouse.move(x, y, absolute=True) 170 | mouse.click(button='left') 171 | 172 | wait_running_game(camera, timeout = 12.5) 173 | 174 | camera.stop() 175 | 176 | def increase_click_limit(): 177 | global CLICK_LIMIT 178 | CLICK_LIMIT = min(1.0, CLICK_LIMIT + 0.01) 179 | print(f'Limit: {CLICK_LIMIT:.4}') 180 | 181 | 182 | def decrease_click_limit(): 183 | global CLICK_LIMIT 184 | CLICK_LIMIT = max(0.0, CLICK_LIMIT - 0.01) 185 | print(f'Limit: {CLICK_LIMIT:.4}') 186 | 187 | 188 | if __name__ == "__main__": 189 | keyboard.add_hotkey('1', decrease_click_limit) 190 | keyboard.add_hotkey('2', increase_click_limit) 191 | 192 | if '--help' in sys.argv: 193 | print(HELP_STRING) 194 | else: 195 | main() 196 | -------------------------------------------------------------------------------- /prepare_app.py: -------------------------------------------------------------------------------- 1 | """ 2 | Opens game window and provides info about it 3 | """ 4 | 5 | import win32gui 6 | 7 | # from constants import APPLICATION_NAME 8 | 9 | def prepare_app() -> tuple[int]: 10 | """ Top up window and return its bbox """ 11 | toplist, winlist = [], [] 12 | def _enum_cb(hwnd, results): 13 | winlist.append((hwnd, win32gui.GetWindowText(hwnd))) 14 | win32gui.EnumWindows(_enum_cb, toplist) 15 | 16 | application = [(hwnd, title) for hwnd, title in winlist if title.endswith('Blum')] 17 | 18 | application = application[0] 19 | hwnd = application[0] 20 | 21 | win32gui.SetForegroundWindow(hwnd) 22 | return win32gui.GetWindowRect(hwnd) 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | #Python3.10 or higher on WINDOWS! 2 | 3 | mouse 4 | pywin32 5 | keyboard 6 | dxcam-cpp>=0.2.3 7 | --------------------------------------------------------------------------------