├── .gitignore ├── README.en.md ├── README.md ├── config.yaml ├── index.py ├── logs └── .gitkeep ├── requirements.txt ├── run.bat ├── run.sh ├── scripts ├── download_and_extract.py ├── install.bat ├── install.sh └── requirements.txt ├── src ├── bot │ ├── action.py │ ├── heroes.py │ ├── logger.py │ ├── login.py │ └── utils.py ├── decorators │ ├── check_metamask_notification.py │ └── force_full_screen.py ├── env.py ├── main.py ├── main_multi_account.py └── utils │ ├── assets.py │ ├── config.py │ ├── date.py │ ├── image.py │ ├── number.py │ ├── opencv.py │ └── string.py └── targets ├── connect-wallet.png ├── full-stamina.png ├── go-back-arrow.png ├── go-work-all.png ├── go-work-old.png ├── go-work.png ├── green-bar.png ├── hero-icon.png ├── hero-item.png ├── new-map.png ├── ok.png ├── piece.png ├── puzzle.png ├── robot.png ├── select-wallet-1-hover.png ├── select-wallet-1-no-hover.png ├── select-wallet-2.png ├── send-home.png ├── slider.png ├── treasure-hunt-icon.png └── x.png /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | .vim 3 | 4 | # Por hora, acabei de criar o arquivo 5 | devlog.md 6 | 7 | # test 8 | test.py 9 | 10 | # log 11 | /logs/new-map.log 12 | /logs/logger.log 13 | /logs/error.log 14 | logs/ 15 | 16 | # cache 17 | /src/__pycache__ 18 | 19 | # Byte-compiled / optimized / DLL files 20 | __pycache__/ 21 | *.py[cod] 22 | *$py.class 23 | 24 | # vscode plugins 25 | /.history 26 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # bombcrypto-bot 2 | 3 | ## Added extra functions 4 | 5 | - Use browser with different zoom (scale) 6 | - Support for multiple accounts on the same monitor (use IFRAME URL to access) 7 | - Debug by game functions 8 | - Support for retina displays 9 | 10 | # Installation: 11 | 12 | 1- Download and install Python in version greater than 3 from [official website](https://www.python.org/downloads/) or through [windows store](https://www.microsoft.com/p/python-37/9nj46sx7x90p?activetab=pivot:overviewtab). 13 | 14 | 2 - After installing python: 15 | 16 | - for `windows` _run as administrator_ the `install.bat` file in the bot's main folder. 17 | - for `linux` execute `run.sh` file in the bot's main folder. 18 | 19 | # Settings: 20 | 21 | You can configure some options by changing the `config.yaml` file in the bot's main folder. 22 | 23 | ## `scale_image` 24 | 25 | - You now have support to put how many % zoom you are using in your browser. 26 | 27 | > Also pay attention to the ZOOM of the _Metamask_ notification window, it must be the same used in the browser. 28 | 29 | - ### `enable` 30 | 31 | When `True`, activates the functionality to use a different scale. Otherwise, leave the value as `False` 32 | 33 | > Value must be: `True` or `False` 34 | 35 | - ### `percent` 36 | The zoom percentage of your browser and the metamask notification window. 37 | > Value must be from: `50` to `100`. The lower the value, the more imprecise the bot's detections will be. 38 | 39 | ## `is_retina_screen` 40 | 41 | - If your computer is a mac device with retina display, you will need to enable this option for the bot to click accurately. If your bot moves the mouse to random places, maybe this option will help you. 42 | > Value must be: `True` to enable, or `False` to disable 43 | 44 | ## `mouse_move_speed` 45 | 46 | - You can set the speed with which the mouse moves on the screen before clicking. 47 | > Value must be from: `0.1` to `1` 48 | 49 | ## `multiples_accounts_same_monitor` 50 | 51 | - This option enables the use of multiple accounts on the same monitor, the actions being performed synchronously, that is, one window at a time. To use this functionality we suggest using a URL from the game's IFRAME. 52 | 53 | - ### `enable` 54 | 55 | > Value must be: `True` to enable or `False` to disable 56 | 57 | - ### `window_contains_title` 58 | This option offers the possibility to change the text that will be used to detect as windows. This text must be in the title of the never window. 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bombcrypto-bot 2 | 3 | ## Funções extras adicionadas 4 | 5 | - Usar browser com zoom diferente (scale) 6 | - Suporte a multiplas contas no mesmo monitor (usar URL do IFRAME para acessar) 7 | - Debug por funções do game 8 | - Suporte a monitores retina 9 | 10 | # Instalação: 11 | 12 | 1- Baixe e instale Python na versão maior que 3 no [site oficial](https://www.python.org/downloads/) ou através da [windows store](https://www.microsoft.com/p/python-37/9nj46sx7x90p?activetab=pivot:overviewtab). 13 | 14 | 2 - Após instalado python: 15 | 16 | - Para `windows` _execute como administrador_ o arquivo `run.bat` na pasta principal. 17 | - Para `linux` o arquivo `run.sh` na pasta principal. 18 | 19 | # Configurações: 20 | 21 | Você pode configurar algumas opções alterando o arquivo `config.yaml` na pasta principal do bot. 22 | 23 | ## `scale_image` 24 | 25 | - Você agora tem suporte de colocar quantos % de zoom está usando em seu navegador. 26 | 27 | > Se atente também ao ZOOM da janela de notificação do _Metamask_, ela deve ser a mesma usada no navegador. 28 | 29 | - ### `enable` 30 | 31 | Quando `True`, ativa a funcionalidade de usar um scale diferente. Caso contrário, deixe o valor como `False` 32 | 33 | > O valor deve ser: `True` ou `False` 34 | 35 | - ### `percent` 36 | A porcentagem de zoom do seu navegador e da janela de notificação do metamask. 37 | > O Valor deve ser de: `50` a `100`. Quanto menor o valor, mais impreciso serão as detecções do bot. 38 | 39 | ## `is_retina_screen` 40 | 41 | - Caso seu computador seja um dispositivo mac com tela retina, será necessário ativar essa opção para que o bot realize clicks com precisão. Se seu bot move o mouse para lugares aleatórios, talvez essa opção te ajude. 42 | > O valor deve ser: `True` para ativar, ou `False` para desativar 43 | 44 | ## `mouse_move_speed` 45 | 46 | - Você pode configurar a velocidade com que o mouse se move na tela antes da realização do click. 47 | > O valor deve ser de: `0.1` a `1` 48 | 49 | ## `multiples_accounts_same_monitor` 50 | 51 | - Essa opção habilita o uso de multiplas contas no mesmo monitor, sendo as ações feitas de forma sincrona, ou seja, uma janela por vez. Para usar essa funcionalidade sugiro utilizar a URL do IFRAME do jogo. 52 | 53 | - ### `enable` 54 | 55 | > O Valor deve ser: `True` para habilitar ou `False` para desabilitar 56 | 57 | - ### `window_contains_title` 58 | Essa opção te fornece a possibilidade de alterar qual o texto que será utilizado para detectar as janelas. Esse texto deve estar no título da janela do nevegador. 59 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # [en_US] Time intervals configuration 3 | # 4 | # [pt_BR] Configurações de intervalos de tempo 5 | # 6 | time_intervals: 7 | # [en_US] Time interval for check if there are available heroes to work 8 | # Default value (in minutes): 10 9 | # 10 | # [pt_BR] Intervalo de tempo para verificar se há heróis disponíveis para trabalhar 11 | # Valor padrão (em minutos): 10 12 | send_heroes_for_work: 12 13 | 14 | # [en_US] Time interval to update heroes position in the map, 15 | # Default value (in minutes): 3 16 | # 17 | # [pt_BR] Intervalo de tempo para atualizar a posição dos heróis no mapa 18 | # Valor padrão (em minutos): 3 19 | refresh_heroes_positions: 3 20 | 21 | # [en_US] Time interval to check for new maps 22 | # Default value (in minutes): 5 23 | # 24 | # [pt_BR] Intervalo de tempo para verificar por novos mapas 25 | # Valor padrão (em segundos): 5 26 | check_for_new_map_button: 5 27 | 28 | # [en_US] Time interval to check for login request 29 | # Default value (in minutes): 3 30 | # 31 | # [pt_BR] Intervalo de tempo para verificar se existe solicitação de login 32 | # Valor padrão (em minutos): 5 33 | check_for_login: 5 34 | 35 | # [en_US] Time interval between moviments 36 | # Default value (in seconds): 1 37 | # 38 | # [pt_BR] Intervalo de tempo entre movimentos 39 | # Valor padrão (em segundos): 1 40 | interval_between_moviments: 1 41 | 42 | # [en_US] How confident the bot needs to be to click the buttons (values from 0 to 1. 0 is the minimum value, 1 is the maximum value) 43 | # 44 | # [pt_BR] O quão confiante o bot precisa estar para clicar nos botões (valores entre 0 e 1. 0 é o valor mínimo, 1 é o valor máximo) 45 | # 46 | threshold: 47 | # [en_US] 48 | # Default value: 0.7 49 | # 50 | # [pt_BR] 51 | # Valor padrão: 0.7 52 | default: 0.7 53 | 54 | # [en_US] 55 | # Default value: 0.8 56 | # 57 | # [pt_BR] 58 | # Valor padrão: 0.8 59 | commom: 0.8 60 | 61 | # [en_US] 62 | # Default value: 0.8 63 | # 64 | # [pt_BR] 65 | # Valor padrão: 0.8 66 | select_wallet_buttons: 0.8 67 | 68 | # [en_US] 69 | # Default value: 0.9 70 | # 71 | # [pt_BR] 72 | # Valor padrão: 0.9 73 | go_to_work_btn: 0.9 74 | 75 | # [en_US] 76 | # Default value: 0.9 77 | # 78 | # [pt_BR] 79 | # Valor padrão: 0.9 80 | go_to_work_all_btn: 0.8 81 | 82 | # [en_US] 83 | # Default value: 0.9 84 | # 85 | # [pt_BR] 86 | # Valor padrão: 0.9 87 | green_bar: 0.9 88 | 89 | home: 90 | enable: False 91 | # If bot is sending the wrong hero home, make this number bigger. 92 | # if bot is not sending any hero home make this number smaller. 93 | hero_threshold: 0.90 94 | home_button_threshold: 0.95 95 | 96 | # [en_US] 97 | # Default value: 60 98 | # 99 | # [pt_BR] 100 | # Valor padrão: 60 101 | scroll_size: 60 102 | 103 | # [en_US] 104 | # Default value: 3 105 | # 106 | # [pt_BR] 107 | # Valor padrão: 3 108 | scroll_attemps: 4 109 | 110 | # [en_US] 111 | # Default value: true 112 | # 113 | # [pt_BR] 114 | # Valor padrão: true 115 | use_click_and_drag_instead_of_scroll: true 116 | 117 | # [en_US] 118 | # Default value: 200 119 | # 120 | # [pt_BR] 121 | # Valor padrão: 200 122 | click_and_drag_amount: 200 123 | 124 | # [en_US] 125 | # Default value: green 126 | # Available options: 127 | # all = select all heroes (regardless of the stamina bar) 128 | # green = select the heroes with green stamina bar (half or full) 129 | # full = select only the heroes with full stamina bar 130 | # 131 | # [pt_BR] 132 | # Valor padrão: green 133 | # Opções disponíveis: 134 | # all = seleciona todos os heróis (independente da barra de stamina) 135 | # green = seleciona os heróis com a barra de stamina verde (metade ou cheia) 136 | # full = seleciona somente os heróis com a barra de stamina cheia 137 | select_heroes_mode: "green" 138 | 139 | # [en_US] Option for save logs to a file (logs.txt) 140 | # Default value: False 141 | # Available options: False or True 142 | # 143 | # [pt_BR] Opção para salvar os logs em arquivo (logs.txt) 144 | # Valor padrão : False 145 | # Opções disponíveis: False ou True 146 | save_log_to_file: True 147 | 148 | # [en_US] Option for use multiples accounts on same monitor 149 | # Default value: False 150 | # 151 | # [pt_BR] Opção para usar multiplas contas no mesmo monitor 152 | # Valor padrão: False 153 | multiples_accounts_same_monitor: 154 | enable: False 155 | window_contains_title: "bombcrypto" 156 | 157 | # [en_US] If you have macbook or laptop with Retina screen, enable this feature 158 | # Default value: False 159 | # 160 | # [pt_BR] Se você tem um macbook ou notebook com tela retina, habilite essa função 161 | # Valor padrão: False 162 | is_retina_screen: False 163 | 164 | # [en_US] Speed of mouse move for automation 165 | # Default value: 1 166 | # 167 | # [pt_BR] Velocidade do movimento do mouse para a automação 168 | # Valor padrão: 1 169 | mouse_move_speed: 0.6 170 | 171 | scale_image: 172 | # [en_US] Enable for use different scale on your browser 173 | # Default value: False 174 | # 175 | # [pt_BR] Habilita para usar uma escala diferente de visão em seu navegador 176 | # Valor padrão: False 177 | enable: False 178 | 179 | # [en_US] When enable scale, set what scale value used on your browser 180 | # Default value: 100 181 | # 182 | # [pt_BR] Quando a escala está habilitada, especificar o valor de escala utilizada em seu navegador 183 | # Valor padrão: 100 184 | percent: 100 185 | 186 | # [en_US] When enable scale, set what scale images threshold to compare images 187 | # Default value: 0.9 188 | # 189 | # [pt_BR] Quando a escala está habilitada, especificar o valor de threshold para comparação de imagens 190 | # Valor padrão: 0.9 191 | threshold: 0.9 192 | 193 | debug: 194 | # When home feature enable 195 | sendHeroesHome: False 196 | 197 | # When select_heroes_mode == 'all' 198 | clickButtons: False 199 | 200 | # When select_heroes_mode == 'all' and HOME feature is Disabled 201 | clickWorkAllButton: False 202 | 203 | # When select_heroes_mode == 'green' 204 | clickGreenBarButtons: False 205 | 206 | # When select_heroes_mode == 'full' 207 | clickFullBarButtons: False 208 | 209 | ##### 210 | # When call clickBtn function 211 | clickBtn: False 212 | 213 | # When use scroll function 214 | scroll: False 215 | -------------------------------------------------------------------------------- /index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from time import sleep 3 | from src.main import run 4 | from src.main_multi_account import runMultiAccount 5 | from src.utils.config import loadConfigsFromFile 6 | from src.bot.logger import logger, exception 7 | 8 | config = loadConfigsFromFile() 9 | run_multi_account = config['multiples_accounts_same_monitor']['enable'] 10 | 11 | logger('Bombcrypto BOT starting', color='yellow') 12 | while True: 13 | try: 14 | if run_multi_account: 15 | logger('Running bot for MULTI ACCOUNT ON SAME MONITOR', color='cyan') 16 | runMultiAccount() 17 | else: 18 | logger('Running bot', color='cyan') 19 | run() 20 | except KeyboardInterrupt: 21 | break 22 | except Exception as e: 23 | logger('Bombcrypto BOT crashed... restarting in 5 seconds.', color='red') 24 | logger("Exception: %s" % (str(e)), color='red') 25 | exception(e) 26 | sleep(5) 27 | continue 28 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/logs/.gitkeep -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | MouseInfo==0.1.3 2 | mss==6.1.0 3 | numpy==1.21.4 4 | opencv-python==4.5.4.60 5 | PyAutoGUI==0.9.53 6 | PyGetWindow==0.0.9 7 | PyMsgBox==1.0.9 8 | pyperclip==1.8.2 9 | PyRect==0.1.4 10 | PyScreeze==0.1.28 11 | pytweening==1.0.4 12 | PyYAML==6.0 13 | -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | python --version 3>NUL 3 | if errorlevel 1 goto errorNoPython 4 | 5 | pip install -r requirements.txt 6 | python index.py 7 | 8 | :errorNoPython 9 | echo Error^: Python not installed -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | pip install -r requirements.txt 4 | python index.py -------------------------------------------------------------------------------- /scripts/download_and_extract.py: -------------------------------------------------------------------------------- 1 | from urllib.request import urlopen 2 | from io import BytesIO 3 | from zipfile import ZipFile 4 | import os 5 | 6 | ZIP_REPOSITORY_MAIN_URL="https://github.com/MatheusHAS/bombcrypto-bot/archive/refs/heads/main.zip" 7 | EXTRACT_TO="C:/" 8 | 9 | if os.name != 'nt': 10 | EXTRACT_TO="~/" 11 | FINAL_EXPORTED_PATH="{}bombcrypto-bot-main/".format(EXTRACT_TO) 12 | 13 | def downloadFromUrl(url): 14 | print('Downloading data from URL:\n{}'.format(url)) 15 | http_response = urlopen(url) 16 | return BytesIO(http_response.read()) 17 | 18 | def unzipTo(extract_to, file_bytes=None): 19 | print('Extracting data to: {}'.format(extract_to)) 20 | zip = ZipFile(file_bytes) 21 | zip.extractall(path=extract_to) 22 | 23 | def main(): 24 | try: 25 | zip_content_bytes = downloadFromUrl(ZIP_REPOSITORY_MAIN_URL) 26 | unzipTo(extract_to=EXTRACT_TO, file_bytes=zip_content_bytes) 27 | print('Installed with success on folder: {}'.format(FINAL_EXPORTED_PATH)) 28 | except Exception as e: 29 | print('Bombcrypto installer crashed...') 30 | print("Exception: %s" % (str(e))) 31 | input("Press Enter to continue...") 32 | 33 | main() -------------------------------------------------------------------------------- /scripts/install.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | python --version 3>NUL 4 | if errorlevel 1 goto errorNoPython 5 | 6 | pip install -r requirements.txt 7 | python ./download_and_extract.py 8 | goto:eof 9 | 10 | :errorNoPython 11 | echo Error^: Python not installed -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | pip install -r requirements.txt 3 | python ./download_and_extract.py -------------------------------------------------------------------------------- /scripts/requirements.txt: -------------------------------------------------------------------------------- 1 | MouseInfo==0.1.3 2 | mss==6.1.0 3 | numpy==1.21.4 4 | opencv-python==4.5.4.60 5 | PyAutoGUI==0.9.53 6 | PyGetWindow==0.0.9 7 | PyMsgBox==1.0.9 8 | pyperclip==1.8.2 9 | PyRect==0.1.4 10 | PyScreeze==0.1.28 11 | pytweening==1.0.4 12 | PyYAML==6.0 13 | -------------------------------------------------------------------------------- /src/bot/action.py: -------------------------------------------------------------------------------- 1 | from random import random 2 | from cv2 import cv2 3 | import numpy as np 4 | import pyautogui 5 | import time 6 | 7 | import pygetwindow 8 | 9 | import src.bot.logger as Log 10 | import src.env as env 11 | from src.utils.number import addRandomness 12 | from src.utils.image import printScreen, printScreenForWindow 13 | from src.decorators.force_full_screen import forceFullScreenForThis 14 | from src.utils.opencv import show 15 | 16 | def moveToWithRandomness(x,y,t=env.mouse_move_speed): 17 | pos_x = x 18 | pos_y = y 19 | if env.window_object is not None and (not env.force_full_screen or not env.in_login_process): 20 | pos_x = pos_x+env.window_object.left 21 | pos_y = pos_y+env.window_object.top 22 | if env.cfg['is_retina_screen']: 23 | pos_x = pos_x / 2 24 | pos_y = pos_y / 2 25 | pyautogui.moveTo(addRandomness(pos_x,10),addRandomness(pos_y,10),t+random()/2) 26 | 27 | def clickBtn(img,name=None, timeout=3, threshold = env.threshold['default']): 28 | Log.logger(None, progress_indicator=True) 29 | if not name is None: 30 | pass 31 | start = time.time() 32 | while(True): 33 | matches = getPositions(img, threshold=threshold) 34 | if(len(matches)==0): 35 | hast_timed_out = time.time()-start > timeout 36 | if(hast_timed_out): 37 | if not name is None: 38 | pass 39 | return False 40 | continue 41 | if env.debug['clickBtn']: 42 | show(matches, None, '[clickBtn] name -> {}'.format(name)) 43 | x,y,w,h = matches[0] 44 | pos_click_x = x+w/2 45 | pos_click_y = y+h/2 46 | moveToWithRandomness(pos_click_x,pos_click_y) 47 | pyautogui.click() 48 | return True 49 | 50 | def scroll(): 51 | hero_item_list = getPositions(env.images['hero-item'], threshold = env.threshold['commom']) 52 | if env.debug['scroll']: 53 | show(hero_item_list, None, '[scroll] hero_item_list') 54 | if (len(hero_item_list) == 0): 55 | return 56 | x,y,w,h = hero_item_list[len(hero_item_list)-1] 57 | moveToWithRandomness(x,y) 58 | 59 | if not env.cfg['use_click_and_drag_instead_of_scroll']: 60 | pyautogui.scroll(-env.cfg['scroll_size']) 61 | else: 62 | pyautogui.dragRel(0,-env.cfg['click_and_drag_amount'],duration=1, button='left') 63 | 64 | def getPositions(target, threshold=env.threshold['default'],img = None): 65 | if img is None: 66 | running_multi_account = env.window_object is not None 67 | if running_multi_account and not env.force_full_screen: 68 | img = printScreenForWindow(env.window_object, env.in_login_process == False) 69 | else: 70 | img = printScreen() 71 | result = cv2.matchTemplate(img,target,cv2.TM_CCOEFF_NORMED) 72 | w = target.shape[1] 73 | h = target.shape[0] 74 | 75 | yloc, xloc = np.where(result >= threshold) 76 | 77 | 78 | rectangles = [] 79 | for (x, y) in zip(xloc, yloc): 80 | rectangles.append([int(x), int(y), int(w), int(h)]) 81 | rectangles.append([int(x), int(y), int(w), int(h)]) 82 | 83 | rectangles, weights = cv2.groupRectangles(rectangles, 1, 0.2) 84 | return rectangles 85 | 86 | def goToHeroes(): 87 | if clickBtn(env.images['go-back-arrow']): 88 | env.login_attempts = 0 89 | time.sleep(1) 90 | clickBtn(env.images['hero-icon']) 91 | time.sleep(1) 92 | 93 | def goToGame(): 94 | clickBtn(env.images['x']) 95 | clickBtn(env.images['x']) 96 | 97 | clickBtn(env.images['treasure-hunt-icon']) 98 | 99 | def refreshHeroesPositions(): 100 | Log.logger('🔃 Refreshing Heroes Positions') 101 | clickBtn(env.images['go-back-arrow']) 102 | clickBtn(env.images['treasure-hunt-icon']) 103 | clickBtn(env.images['treasure-hunt-icon']) 104 | 105 | def activeWindow(): 106 | try: 107 | env.window_object.activate() 108 | except: 109 | env.window_object.minimize() 110 | env.window_object.activate() 111 | 112 | @forceFullScreenForThis 113 | def goToNextMap(): 114 | if clickBtn(env.images['new-map']): 115 | Log.logNewMapClicked() 116 | 117 | def closeMetamaskWindow(): 118 | try: 119 | title = 'MetaMask Notification' 120 | time.sleep(7) 121 | windows = pygetwindow.getWindowsWithTitle(title) 122 | for window in windows: 123 | window.close() 124 | except: 125 | print('error for close metamask window') 126 | 127 | def maximizeMetamaskNotification(): 128 | title = 'MetaMask Notification' 129 | time.sleep(8) 130 | windows = pygetwindow.getWindowsWithTitle(title) 131 | if len(windows) > 0: 132 | current_window = windows[0] 133 | current_window.activate() 134 | current_window.maximize() -------------------------------------------------------------------------------- /src/bot/heroes.py: -------------------------------------------------------------------------------- 1 | import time 2 | import pyautogui 3 | 4 | import src.env as env 5 | import src.bot.logger as Log 6 | from src.bot.action import clickBtn, goToGame, goToHeroes, moveToWithRandomness, scroll, getPositions 7 | from src.bot.utils import isHome, isWorking 8 | from src.utils.opencv import show 9 | 10 | def clickButtons(): 11 | buttons = getPositions(env.images['go-work'], threshold=env.threshold['go_to_work_btn']*env.scale_image['threshold'] if env.scale_image['enable'] else env.threshold['go_to_work_btn']) 12 | if env.debug['clickButtons']: 13 | show(buttons, None, '[clickButtons] buttons') 14 | for (x, y, w, h) in buttons: 15 | moveToWithRandomness(x+(w/2),y+(h/2)) 16 | pyautogui.click() 17 | env.hero_clicks = env.hero_clicks + 1 18 | if env.hero_clicks > 20: 19 | Log.logger('too many hero clicks, try to increase the go_to_work_btn threshold') 20 | return 21 | return len(buttons) 22 | 23 | def clickWorkAllButton(): 24 | if env.debug['clickWorkAllButton']: 25 | buttons = getPositions(env.images['go-work-all'], threshold=env.threshold['go_to_work_all_btn']*env.scale_image['threshold'] if env.scale_image['enable'] else env.threshold['go_to_work_all_btn']) 26 | show(buttons, None, '[clickWorkAllButton] [temp] buttons') 27 | return clickBtn(env.images['go-work-all'],'go-work-all', timeout=4, threshold=env.threshold['go_to_work_all_btn']*env.scale_image['threshold'] if env.scale_image['enable'] else env.threshold['go_to_work_all_btn']) 28 | 29 | def clickGreenBarButtons(): 30 | debug_mode_enabled = env.debug['clickGreenBarButtons'] 31 | offset = 130 32 | 33 | green_bars = getPositions(env.images['green-bar'], threshold=env.threshold['green_bar']*env.scale_image['threshold'] if env.scale_image['enable'] else env.threshold['green_bar']) 34 | Log.logger('🟩 %d green bars detected' % len(green_bars)) 35 | buttons = getPositions(env.images['go-work'], threshold=env.threshold['go_to_work_btn']*env.scale_image['threshold'] if env.scale_image['enable'] else env.threshold['go_to_work_btn']) 36 | Log.logger('🆗 %d buttons detected' % len(buttons)) 37 | 38 | if debug_mode_enabled: 39 | show(green_bars, None, '[clickGreenBarButtons] green_bars') 40 | show(buttons, None, '[clickGreenBarButtons] buttons') 41 | 42 | not_working_green_bars = [] 43 | for bar in green_bars: 44 | if not isWorking(bar, buttons): 45 | not_working_green_bars.append(bar) 46 | if len(not_working_green_bars) > 0: 47 | Log.logger('🆗 %d buttons with green bar detected' % len(not_working_green_bars)) 48 | Log.logger('👆 Clicking in %d heroes' % len(not_working_green_bars)) 49 | 50 | for (x, y, w, h) in not_working_green_bars: 51 | pos_click_x = x+offset+(w/2) 52 | pos_click_y = y+(h/2) 53 | moveToWithRandomness(pos_click_x,pos_click_y) 54 | pyautogui.click() 55 | env.hero_clicks = env.hero_clicks + 1 56 | if env.hero_clicks > 20: 57 | Log.logger('⚠️ Too many hero clicks, try to increase the go_to_work_btn threshold') 58 | return 59 | return len(not_working_green_bars) 60 | 61 | def clickFullBarButtons(): 62 | debug_mode_enabled = env.debug['clickFullBarButtons'] 63 | offset = 100 64 | 65 | full_bars = getPositions(env.images['full-stamina'], threshold=env.threshold['default']*env.scale_image['threshold'] if env.scale_image['enable'] else env.threshold['default']) 66 | buttons = getPositions(env.images['go-work'], threshold=env.threshold['go_to_work_btn']*env.scale_image['threshold'] if env.scale_image['enable'] else env.threshold['go_to_work_btn']) 67 | 68 | if debug_mode_enabled: 69 | show(full_bars, None, '[clickFullBarButtons] full_bars') 70 | show(buttons, None, '[clickFullBarButtons] buttons') 71 | 72 | not_working_full_bars = [] 73 | for bar in full_bars: 74 | if not isWorking(bar, buttons): 75 | not_working_full_bars.append(bar) 76 | 77 | if len(not_working_full_bars) > 0: 78 | Log.logger('👆 Clicking in %d heroes' % len(not_working_full_bars)) 79 | 80 | for (x, y, w, h) in not_working_full_bars: 81 | 82 | pos_click_x = x+offset+(w/2) 83 | pos_click_y = y+(h/2) 84 | moveToWithRandomness(pos_click_x,pos_click_y) 85 | pyautogui.click() 86 | env.hero_clicks = env.hero_clicks + 1 87 | 88 | return len(not_working_full_bars) 89 | 90 | def sendHeroesHome(): 91 | if not env.home['enable']: 92 | return 93 | heroes_positions = [] 94 | for hero in env.home_heroes: 95 | hero_positions = getPositions(hero, threshold=env.home['hero_threshold']) 96 | if not len (hero_positions) == 0: 97 | hero_position = hero_positions[0] 98 | heroes_positions.append(hero_position) 99 | 100 | n = len(heroes_positions) 101 | if n == 0: 102 | print('No heroes that should be sent home found.') 103 | return 104 | print(' %d heroes that should be sent home found' % n) 105 | 106 | debug_mode_enabled = env.debug['sendHeroesHome'] 107 | if debug_mode_enabled: 108 | show(heroes_positions, None, '[sendHeroesHome] HEROES') 109 | 110 | go_home_buttons = getPositions(env.images['send-home'], threshold=env.home['home_button_threshold']) 111 | go_work_buttons = getPositions(env.images['go-work-old'], threshold=env.threshold['go_to_work_btn']) 112 | if debug_mode_enabled: 113 | show(go_home_buttons, None, '[sendHeroesHome] go_home_buttons') 114 | show(go_work_buttons, None, '[sendHeroesHome] go_work_buttons') 115 | 116 | for position in heroes_positions: 117 | if not isHome(position,go_home_buttons): 118 | if(not isWorking(position, go_work_buttons)): 119 | print ('hero not working, sending him home') 120 | pos_click_x = go_home_buttons[0][0]+go_home_buttons[0][2]/2 121 | pos_click_y = position[1]+position[3]/2 122 | moveToWithRandomness(pos_click_x,pos_click_y) 123 | pyautogui.click() 124 | else: 125 | print ('hero working, not sending him home(no dark work button)') 126 | else: 127 | print('hero already home, or home full(no dark home button)') 128 | 129 | def sendHeroesToWork(): 130 | if env.cfg['select_heroes_mode'] == 'full': 131 | return clickFullBarButtons() 132 | elif env.cfg['select_heroes_mode'] == 'green': 133 | return clickGreenBarButtons() 134 | else: 135 | return clickButtons() 136 | 137 | def refreshHeroes(): 138 | Log.logger('🏢 Search for heroes to work') 139 | 140 | goToHeroes() 141 | 142 | if env.cfg['select_heroes_mode'] == "full": 143 | Log.logger('⚒️ Sending heroes with full stamina bar to work', 'green') 144 | elif env.cfg['select_heroes_mode'] == "green": 145 | Log.logger('⚒️ Sending heroes with green stamina bar to work', 'green') 146 | else: 147 | Log.logger('⚒️ Sending all heroes to work', 'green') 148 | 149 | empty_scrolls_attempts = env.cfg['scroll_attemps'] 150 | work_all_clicked = False 151 | if not env.home['enable'] and env.cfg['select_heroes_mode'] == 'all': 152 | time.sleep(1) 153 | work_all_clicked = clickWorkAllButton() 154 | if work_all_clicked: 155 | Log.logger('💪 ALL heroes sent to work') 156 | time.sleep(2) 157 | 158 | if not work_all_clicked: 159 | env.hero_clicks = 0 160 | while(empty_scrolls_attempts >0): 161 | sendHeroesToWork() 162 | sendHeroesHome() 163 | empty_scrolls_attempts = empty_scrolls_attempts - 1 164 | scroll() 165 | time.sleep(2) 166 | Log.logger('💪 {} heroes sent to work'.format(env.hero_clicks)) 167 | goToGame() 168 | -------------------------------------------------------------------------------- /src/bot/logger.py: -------------------------------------------------------------------------------- 1 | from src.utils.config import loadConfigsFromFile 2 | from src.utils.date import dateFormatted 3 | import traceback 4 | import sys 5 | 6 | cfg = loadConfigsFromFile() 7 | 8 | last_log_is_progress = False 9 | 10 | COLOR = { 11 | 'blue': '\033[94m', 12 | 'default': '\033[99m', 13 | 'grey': '\033[90m', 14 | 'yellow': '\033[93m', 15 | 'black': '\033[90m', 16 | 'cyan': '\033[96m', 17 | 'green': '\033[92m', 18 | 'magenta': '\033[95m', 19 | 'white': '\033[97m', 20 | 'red': '\033[91m' 21 | } 22 | 23 | def exception(error): 24 | formatted_datetime = dateFormatted() 25 | traceback_exception_format = traceback.format_exception(type(error), error, error.__traceback__) 26 | exception_message = '[{}] ERROR: '.format(formatted_datetime).join(traceback_exception_format) 27 | print(exception_message) 28 | 29 | if (cfg['save_log_to_file'] == True): 30 | logger_file = open("./logs/error.log", "a", encoding='utf-8') 31 | logger_file.write(exception_message + '\n') 32 | logger_file.close() 33 | 34 | 35 | def logger(message, progress_indicator = False, color = 'default'): 36 | global last_log_is_progress 37 | color_formatted = COLOR.get(color.lower(), COLOR['default']) 38 | 39 | formatted_datetime = dateFormatted() 40 | formatted_message = "[{}] => {}".format(formatted_datetime, message) 41 | formatted_message_colored = color_formatted + formatted_message + '\033[0m' 42 | 43 | if progress_indicator: 44 | if not last_log_is_progress: 45 | last_log_is_progress = True 46 | formatted_message = color_formatted + "[{}] => {}".format(formatted_datetime, '⬆️ Processing last action..') 47 | sys.stdout.write(formatted_message) 48 | sys.stdout.flush() 49 | else: 50 | sys.stdout.write(color_formatted + '.') 51 | sys.stdout.flush() 52 | return 53 | 54 | if last_log_is_progress: 55 | sys.stdout.write('\n') 56 | sys.stdout.flush() 57 | last_log_is_progress = False 58 | 59 | print(formatted_message_colored) 60 | 61 | if (cfg['save_log_to_file'] == True): 62 | logger_file = open("./logs/logger.log", "a", encoding='utf-8') 63 | logger_file.write(formatted_message + '\n') 64 | logger_file.close() 65 | 66 | return True 67 | 68 | def logNewMapClicked(): 69 | logger('🗺️ New Map button clicked!') 70 | logger_file = open("./logs/new-map.log", "a", encoding='utf-8') 71 | logger_file.write(dateFormatted() + '\n') 72 | logger_file.close() 73 | -------------------------------------------------------------------------------- /src/bot/login.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | import pyautogui 3 | 4 | import src.env as env 5 | import src.bot.logger as Log 6 | from src.bot.action import clickBtn, closeMetamaskWindow, getPositions 7 | from src.decorators.check_metamask_notification import checkMetamaskNotification 8 | 9 | def login(): 10 | Log.logger('😿 Checking if game has disconnected') 11 | sleep(5) 12 | 13 | # if env.login_attempts > 3: 14 | # Log.logger('🔃 Too many login attempts, refreshing') 15 | # env.login_attempts = 0 16 | # pyautogui.hotkey('ctrl','f5') 17 | # return 18 | 19 | already_refreshed = False 20 | if clickBtn(env.images['ok'], name='okBtn', timeout=5): 21 | already_refreshed = True 22 | sleep(15) 23 | 24 | closeMetamaskWindow() 25 | if not already_refreshed: 26 | in_login_screen = getPositions(env.images['connect-wallet'], threshold=env.threshold['default']) 27 | if(len(in_login_screen)!=0): 28 | pyautogui.hotkey('ctrl','f5') 29 | 30 | Log.logger('Loggin attempt: {}'.format(env.login_attempts), color='yellow') 31 | in_login_screen = getPositions(env.images['connect-wallet'], threshold=env.threshold['default']) 32 | if(len(in_login_screen)==0): 33 | Log.logger('Already logged...', color='cyan') 34 | return 35 | else: 36 | Log.logger('Logging process start...', color='cyan') 37 | 38 | sleep(10) 39 | 40 | if clickBtn(env.images['connect-wallet'], name='connectWalletBtn', timeout = 10): 41 | Log.logger('🎉 Connect wallet button detected, logging in!') 42 | env.login_attempts = env.login_attempts + 1 43 | 44 | if clickOnSignIn(): 45 | env.login_attempts = env.login_attempts + 1 46 | if clickBtn(env.images['treasure-hunt-icon'], name='teasureHunt', timeout = 15): 47 | env.login_attempts = 0 48 | return 49 | 50 | if clickBtn(env.images['ok'], name='okBtn', timeout=5): 51 | pass 52 | 53 | @checkMetamaskNotification 54 | def clickOnSignIn(): 55 | env.in_login_process = True 56 | env.force_full_screen = True 57 | result = clickBtn(env.images['select-wallet-2'], name='sign button', timeout=8) 58 | env.in_login_process = False 59 | env.force_full_screen = False 60 | return result -------------------------------------------------------------------------------- /src/bot/utils.py: -------------------------------------------------------------------------------- 1 | def isHome(hero, buttons): 2 | y = hero[1] 3 | 4 | for (_,button_y,_,button_h) in buttons: 5 | isBelow = y < (button_y + button_h) 6 | isAbove = y > (button_y - button_h) 7 | if isBelow and isAbove: 8 | return False 9 | return True 10 | 11 | def isWorking(bar, buttons): 12 | y = bar[1] 13 | 14 | for (_,button_y,_,button_h) in buttons: 15 | isBelow = y < (button_y + button_h) 16 | isAbove = y > (button_y - button_h) 17 | if isBelow and isAbove: 18 | return False 19 | return True 20 | -------------------------------------------------------------------------------- /src/decorators/check_metamask_notification.py: -------------------------------------------------------------------------------- 1 | import time 2 | from src.bot.action import maximizeMetamaskNotification 3 | 4 | def checkMetamaskNotification(fn): 5 | def exec(*args, **kwargs): 6 | maximizeMetamaskNotification() 7 | time.sleep(5) 8 | return fn(*args, **kwargs) 9 | return exec -------------------------------------------------------------------------------- /src/decorators/force_full_screen.py: -------------------------------------------------------------------------------- 1 | import src.env as env 2 | 3 | def forceFullScreenForThis(fn): 4 | def exec(*args, **kwargs): 5 | env.force_full_screen = True 6 | result = fn(*args, **kwargs) 7 | env.force_full_screen = False 8 | return result 9 | return exec -------------------------------------------------------------------------------- /src/env.py: -------------------------------------------------------------------------------- 1 | from src.utils.assets import loadImages, loadHeroesImagesToHome 2 | from src.utils.config import loadConfigsFromFile 3 | from src.bot.logger import logger 4 | 5 | logger('Setting up global variables...', color='green') 6 | global window_object 7 | global threshold 8 | global home 9 | global cfg 10 | global scale_image 11 | global login_attempts 12 | global hero_clicks 13 | global last_log_is_progress 14 | global home_heroes 15 | global images 16 | global multi_account_same_monitor 17 | global force_full_screen 18 | global mouse_move_speed 19 | global in_login_process 20 | global debug 21 | 22 | logger('Setting up default values for variables...', color='green') 23 | window_object = None 24 | login_attempts = 0 25 | hero_clicks = 0 26 | last_log_is_progress = False 27 | images = [] 28 | home_heroes = [] 29 | force_full_screen = False 30 | in_login_process = False 31 | 32 | cfg = loadConfigsFromFile() 33 | 34 | logger('Mapping configs...', color='green') 35 | threshold = cfg['threshold'] 36 | home = cfg['home'] 37 | scale_image = cfg['scale_image'] 38 | multi_account_same_monitor = cfg['multiples_accounts_same_monitor'] 39 | mouse_move_speed = cfg['mouse_move_speed'] 40 | debug = cfg['debug'] 41 | 42 | logger('Loading assets...', color='green') 43 | images = loadImages() 44 | if home['enable']: 45 | logger('HOME Enabled. Loading heroes assets...', color='green') 46 | home_heroes = loadHeroesImagesToHome() 47 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | import time 2 | from src.utils.number import addRandomness 3 | import src.bot.logger as Log 4 | import src.env as env 5 | import src.bot.heroes as Heroes 6 | import src.bot.login as Auth 7 | import src.bot.action as Action 8 | from src.bot.action import clickBtn 9 | import sys 10 | 11 | def run(): 12 | time.sleep(5) 13 | intervals = env.cfg['time_intervals'] 14 | 15 | last = { 16 | "login" : 0, 17 | "heroes" : 0, 18 | "new_map" : 0, 19 | "refresh_heroes" : 0 20 | } 21 | 22 | while True: 23 | now = time.time() 24 | 25 | if now - last["login"] > addRandomness(intervals['check_for_login'] * 60): 26 | sys.stdout.flush() 27 | last["login"] = now 28 | Auth.login() 29 | 30 | if now - last["heroes"] > addRandomness(intervals['send_heroes_for_work'] * 60): 31 | last["heroes"] = now 32 | Heroes.refreshHeroes() 33 | 34 | if now - last["new_map"] > intervals['check_for_new_map_button']: 35 | last["new_map"] = now 36 | 37 | if clickBtn(env.images['new-map']): 38 | Log.logNewMapClicked() 39 | 40 | if now - last["refresh_heroes"] > addRandomness( intervals['refresh_heroes_positions'] * 60): 41 | last["refresh_heroes"] = now 42 | Action.refreshHeroesPositions() 43 | 44 | Log.logger(None, progress_indicator=True) 45 | 46 | sys.stdout.flush() 47 | 48 | time.sleep(1) -------------------------------------------------------------------------------- /src/main_multi_account.py: -------------------------------------------------------------------------------- 1 | import time 2 | import sys 3 | import pygetwindow 4 | 5 | from src.utils.number import addRandomness 6 | import src.bot.logger as Log 7 | import src.env as env 8 | import src.bot.heroes as Heroes 9 | import src.bot.login as Auth 10 | import src.bot.action as Action 11 | 12 | def runMultiAccount(): 13 | time.sleep(5) 14 | intervals = env.cfg['time_intervals'] 15 | 16 | windows = [] 17 | title = env.multi_account_same_monitor['window_contains_title'] 18 | 19 | Log.logger('🆗 Start') 20 | Log.logger('Searching for windows with contains title: {}'.format(title), color='yellow') 21 | 22 | for w in pygetwindow.getWindowsWithTitle(title): 23 | windows.append({ 24 | "window": w, 25 | "login" : 0, 26 | "heroes" : 0, 27 | "new_map" : 0, 28 | "refresh_heroes" : 0 29 | }) 30 | 31 | Log.logger('Found {} window(s):'.format(len(windows)), color='cyan') 32 | for index, last in enumerate(windows): 33 | Log.logger('{} -> {}'.format(index+1, last['window'].title), color='cyan') 34 | 35 | if len(windows) == 0: 36 | Log.logger('Exiting because dont have windows contains "{}" title'.format(title), color='red') 37 | exit() 38 | 39 | while True: 40 | now = time.time() 41 | 42 | for last in windows: 43 | env.window_object = last["window"] 44 | Log.logger('Client active window: {}'.format(last['window'].title), color='green') 45 | time.sleep(5) 46 | 47 | if now - last["login"] > addRandomness(intervals['check_for_login'] * 60): 48 | Action.activeWindow() 49 | sys.stdout.flush() 50 | last["login"] = now 51 | Auth.login() 52 | 53 | if now - last["heroes"] > addRandomness(intervals['send_heroes_for_work'] * 60): 54 | Action.activeWindow() 55 | last["heroes"] = now 56 | Heroes.refreshHeroes() 57 | 58 | if now - last["new_map"] > intervals['check_for_new_map_button']: 59 | Action.activeWindow() 60 | last["new_map"] = now 61 | Action.goToNextMap() 62 | 63 | if now - last["refresh_heroes"] > addRandomness( intervals['refresh_heroes_positions'] * 60): 64 | Action.activeWindow() 65 | last["refresh_heroes"] = now 66 | Action.refreshHeroesPositions() 67 | 68 | Log.logger(None, progress_indicator=True) 69 | sys.stdout.flush() 70 | 71 | time.sleep(1) 72 | -------------------------------------------------------------------------------- /src/utils/assets.py: -------------------------------------------------------------------------------- 1 | from cv2 import cv2 2 | from os import listdir 3 | import src.env as env 4 | from src.utils import string 5 | from src.utils.image import resizeImageForScale 6 | 7 | def loadHeroesImagesToHome(): 8 | file_names = listdir('./targets/heroes-to-send-home') 9 | heroes = [] 10 | for file in file_names: 11 | path = './targets/heroes-to-send-home/' + file 12 | # TODO: add scale? 13 | hero_image = cv2.imread(path) 14 | heroes.append(hero_image) 15 | 16 | print('>>---> %d heroes that should be sent home loaded' % len(heroes)) 17 | return heroes 18 | 19 | def loadImages(): 20 | file_names = listdir('./targets/') 21 | targets = {} 22 | for file in file_names: 23 | path = 'targets/' + file 24 | target_name = string.removeSuffix(file, '.png') 25 | temp_image = cv2.imread(path) 26 | if env.scale_image['enable']: 27 | targets[target_name] = resizeImageForScale(temp_image, env.scale_image['percent']) 28 | else: 29 | targets[target_name] = temp_image 30 | 31 | return targets -------------------------------------------------------------------------------- /src/utils/config.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | 3 | def loadConfigsFromFile(filename='config.yaml'): 4 | _config_stream = open(filename, 'r') 5 | return yaml.safe_load(_config_stream) -------------------------------------------------------------------------------- /src/utils/date.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def dateFormatted(format = '%Y-%m-%d %H:%M:%S'): 4 | datetime = time.localtime() 5 | formatted = time.strftime(format, datetime) 6 | return formatted -------------------------------------------------------------------------------- /src/utils/image.py: -------------------------------------------------------------------------------- 1 | from cv2 import cv2 2 | import mss 3 | import numpy as np 4 | 5 | def getImageSize(img): 6 | return tuple(img.shape[1::-1]) 7 | 8 | def resizeImageForScale(image, scale=67): 9 | if image is not None: 10 | height, width = getImageSize(image) 11 | resize_size = (int(height * (scale/100)), int(width * (scale/100))) 12 | resized = cv2.resize(image, resize_size, interpolation= cv2.INTER_LINEAR) 13 | return resized 14 | return image 15 | 16 | def printScreen(): 17 | with mss.mss() as sct: 18 | monitor = sct.monitors[0] 19 | sct_img = np.array(sct.grab(monitor)) 20 | return sct_img[:,:,:3] 21 | 22 | def printScreenForWindow(window, activate=False): 23 | if activate: 24 | window.activate() 25 | width, height = window.size 26 | left = window.left 27 | top = window.top 28 | 29 | with mss.mss() as sct: 30 | monitor = sct.monitors[0] 31 | monitor = {"top": top, "left": left, "width": width, "height": height} 32 | sct_img = np.array(sct.grab(monitor)) 33 | return sct_img[:,:,:3] -------------------------------------------------------------------------------- /src/utils/number.py: -------------------------------------------------------------------------------- 1 | from random import random 2 | 3 | def addRandomness(n, randomn_factor_size=None): 4 | if randomn_factor_size is None: 5 | randomness_percentage = 0.1 6 | randomn_factor_size = randomness_percentage * n 7 | 8 | random_factor = 2 * random() * randomn_factor_size 9 | if random_factor > 5: 10 | random_factor = 5 11 | without_average_random_factor = n - randomn_factor_size 12 | randomized_n = int(without_average_random_factor + random_factor) 13 | return int(randomized_n) -------------------------------------------------------------------------------- /src/utils/opencv.py: -------------------------------------------------------------------------------- 1 | from cv2 import cv2 2 | import numpy as np 3 | import mss 4 | 5 | def show(rectangles, img = None, title='img'): 6 | if img is None: 7 | with mss.mss() as sct: 8 | monitor = sct.monitors[0] 9 | img = np.array(sct.grab(monitor)) 10 | 11 | for (x, y, w, h) in rectangles: 12 | cv2.rectangle(img, (x, y), (x + w, y + h), (255,255,255,255), 2) 13 | 14 | cv2.imshow(title,img) 15 | cv2.waitKey(0) 16 | -------------------------------------------------------------------------------- /src/utils/string.py: -------------------------------------------------------------------------------- 1 | def removeSuffix(input_string, suffix): 2 | if suffix and input_string.endswith(suffix): 3 | return input_string[:-len(suffix)] 4 | return input_string -------------------------------------------------------------------------------- /targets/connect-wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/connect-wallet.png -------------------------------------------------------------------------------- /targets/full-stamina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/full-stamina.png -------------------------------------------------------------------------------- /targets/go-back-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/go-back-arrow.png -------------------------------------------------------------------------------- /targets/go-work-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/go-work-all.png -------------------------------------------------------------------------------- /targets/go-work-old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/go-work-old.png -------------------------------------------------------------------------------- /targets/go-work.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/go-work.png -------------------------------------------------------------------------------- /targets/green-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/green-bar.png -------------------------------------------------------------------------------- /targets/hero-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/hero-icon.png -------------------------------------------------------------------------------- /targets/hero-item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/hero-item.png -------------------------------------------------------------------------------- /targets/new-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/new-map.png -------------------------------------------------------------------------------- /targets/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/ok.png -------------------------------------------------------------------------------- /targets/piece.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/piece.png -------------------------------------------------------------------------------- /targets/puzzle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/puzzle.png -------------------------------------------------------------------------------- /targets/robot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/robot.png -------------------------------------------------------------------------------- /targets/select-wallet-1-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/select-wallet-1-hover.png -------------------------------------------------------------------------------- /targets/select-wallet-1-no-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/select-wallet-1-no-hover.png -------------------------------------------------------------------------------- /targets/select-wallet-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/select-wallet-2.png -------------------------------------------------------------------------------- /targets/send-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/send-home.png -------------------------------------------------------------------------------- /targets/slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/slider.png -------------------------------------------------------------------------------- /targets/treasure-hunt-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/treasure-hunt-icon.png -------------------------------------------------------------------------------- /targets/x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatheusHAS/bombcrypto-bot/92063fb933b992c03c10ca50cf2e27eb8a19895e/targets/x.png --------------------------------------------------------------------------------