#!/usr/bin/env python3 """ Sistema de cliques automáticos baseado em reconhecimento de imagem. Clique no botão especificado por 'botaoparaclicar.png' a cada X segundos, apenas dentro do horário configurado no .env. """ import os import sys import time import random import re import cv2 import numpy as np import pyautogui from datetime import datetime from dotenv import load_dotenv load_dotenv() LARGURA_TERMINAL = int(os.getenv("LARGURA_TERMINAL", "60")) INTERVALO_MINIMO_CLIQUE = int(os.getenv("INTERVALO_MINIMO_CLIQUE", "10")) INTERVALO_MAXIMO_CLIQUE = int(os.getenv("INTERVALO_MAXIMO_CLIQUE", "30")) INTERVALO_PRIMEIRO_CLIQUE = int(os.getenv("INTERVALO_PRIMEIRO_CLIQUE", "10")) MAXIMO_CLIQUES_POR_SESSAO = int(os.getenv("MAXIMO_CLIQUES_POR_SESSAO", "20")) INTERVALO_ENTRE_SESSOES = int(os.getenv("INTERVALO_ENTRE_SESSOES", "60")) INTERVALO_CLIQUE_GLOBAL = int(os.getenv("INTERVALO_CLIQUE_GLOBAL", "900")) MAXIMO_FALHAS = int(os.getenv("MAXIMO_FALHAS", "5")) CONFIANCA = float(os.getenv("CONFIANCA", "0.8")) IMAGEM_BOTAO = os.path.join(os.path.dirname(os.path.abspath(__file__)), "botaoparaclicar.png") LOG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "log.txt") PERIODOS_SESSAO = [] def _carregar_periodos(): periodos = [] for i in range(1, 11): ini = os.getenv(f"PERIODO_{i}_INICIO", "").strip() fim = os.getenv(f"PERIODO_{i}_FIM", "").strip() if ini and fim: periodos.append((ini, fim)) return periodos PERIODOS_SESSAO = _carregar_periodos() def dentro_horario(periodos: list) -> bool: agora = datetime.now().time() for ini, fim in periodos: hi = datetime.strptime(ini, "%H:%M").time() hf = datetime.strptime(fim, "%H:%M").time() if hi <= agora <= hf: return True return False pyautogui.FAILSAFE = True RST = "\033[0m" BOLD = "\033[1m" C_TITLE = "\033[96m" C_GREEN = "\033[92m" C_YELLOW = "\033[93m" C_RED = "\033[91m" C_MAG = "\033[95m" C_WHITE = "\033[97m" C_GRAY = "\033[90m" C_CYAN = "\033[36m" _RE_ANSI = re.compile(r"\x1b\[[0-9;]*m") def vis_largura(texto: str) -> int: sem_ansi = _RE_ANSI.sub("", texto) emojis = re.findall( r"[\U0001F300-\U0001FAFF" r"\U00002702-\U000027B0" r"\U0000FE0F" r"\U0001F1E0-\U0001F1FF" r"\U00002600-\U000026FF" r"\U00002700-\U000027BF" r"]+", sem_ansi, flags=re.UNICODE, ) ajuste_emoji = sum((2 - len(e)) for e in emojis) return len(sem_ansi) + ajuste_emoji def terminal_width() -> int: return LARGURA_TERMINAL def largura() -> int: return terminal_width() def center(text: str, pre: str = "", pos: str = "") -> str: w = largura() - vis_largura(pre) - vis_largura(pos) return pre + text.center(w) + pos def banner() -> None: print() print(center(" 🤖 AUTO-CLICADOR 🤖 ", C_TITLE + BOLD)) print(center("Sistema de Cliques Automáticos", C_TITLE)) print() def painel_config() -> None: itens = [ (C_GRAY, "Largura do terminal", f"{LARGURA_TERMINAL} colunas"), (C_GREEN, "Intervalo mínimo entre cliques", f"{INTERVALO_MINIMO_CLIQUE}s"), (C_GREEN, "Intervalo máximo entre cliques", f"{INTERVALO_MAXIMO_CLIQUE}s"), (C_CYAN, "Espera antes do 1º clique", f"{INTERVALO_PRIMEIRO_CLIQUE}s"), (C_YELLOW, "Máximo de cliques por sessão", str(MAXIMO_CLIQUES_POR_SESSAO)), (C_YELLOW, "Intervalo entre sessões", f"{INTERVALO_ENTRE_SESSOES}s"), (C_CYAN, "Clique global de espera", f"{INTERVALO_CLIQUE_GLOBAL}s"), (C_MAG, "Máximo de falhas", str(MAXIMO_FALHAS)), (C_GRAY, "Confiança mínima", str(CONFIANCA)), ] print(center(" ⚙ CONFIGURAÇÃO", C_TITLE + BOLD)) print() labels_sem_cor = [_RE_ANSI.sub("", f" {label}") for _, label, _ in itens] max_label_vis = max(vis_largura(l) for l in labels_sem_cor) largura_label = max(max_label_vis, 1) for cor, label, valor in itens: label_raw = f" {label}" label_vis = vis_largura(label_raw) padding_label = max(1, largura_label - label_vis + 2) print( cor + label_raw + RST + " " * padding_label + C_WHITE + str(valor) + RST ) print() if PERIODOS_SESSAO: print(C_WHITE + BOLD + center(" 📅 Períodos de sessão", C_TITLE) + RST) print() for idx, (ini, fim) in enumerate(PERIODOS_SESSAO, 1): print(C_WHITE + f" {idx}. " + C_CYAN + f"{ini} → {fim}" + RST) else: print(C_RED + center(" ⚠ Nenhum período configurado!", C_RED) + RST) print() def cabecalho_sessao(num: int) -> None: print() print(C_MAG + BOLD + f" 🔄 SESSÃO {num} 🔄" + RST) print() def rodape_clique() -> None: w = largura() inner_w = w - 4 barra_preenchida = max(1, int(inner_w * 0.4)) barra_vazia = inner_w - barra_preenchida barra = C_GREEN + "█" * barra_preenchida + C_GRAY + "░" * barra_vazia + RST print(" " + barra) def log(mensagem: str, cor: str = C_WHITE) -> None: agora = datetime.now() ts = agora.strftime("%H:%M:%S") ts_raw = f"[{ts}] " ts_color = C_GRAY linha_terminal = ( " " + ts_color + ts_raw + RST + cor + mensagem + RST ) print(linha_terminal) try: with open(LOG_FILE, "a", encoding="utf-8") as f: ts_full = agora.strftime("%Y-%m-%d %H:%M:%S") f.write(f"[{ts_full}] {_RE_ANSI.sub('', mensagem)}\n") except Exception: pass def encontrar_botao(imagem_path: str, confianca: float = 0.8): if not os.path.isfile(imagem_path): log(f"ERRO: Imagem '{imagem_path}' não encontrada.", C_RED) return None screenshot = pyautogui.screenshot() screenshot_np = np.array(screenshot) screenshot_cv = cv2.cvtColor(screenshot_np, cv2.COLOR_RGB2BGR) template = cv2.imread(imagem_path, cv2.IMREAD_UNCHANGED) if template is None: log(f"ERRO: Não foi possível carregar '{imagem_path}'.", C_RED) return None if template.shape[2] == 4: template_bgr = cv2.cvtColor(template, cv2.COLOR_BGRA2BGR) alpha = template[:, :, 3] mascara = alpha.astype(np.uint8) else: template_bgr = template mascara = None resultado = cv2.matchTemplate(screenshot_cv, template_bgr, cv2.TM_CCOEFF_NORMED, mask=mascara) _, max_val, _, max_loc = cv2.minMaxLoc(resultado) if max_val >= confianca: h, w2 = template_bgr.shape[:2] centro_x = max_loc[0] + w2 // 2 centro_y = max_loc[1] + h // 2 log( f"Botão encontrado! Confiança: {max_val:.2f} | Pos: ({centro_x}, {centro_y})", C_GREEN, ) return (centro_x, centro_y) else: log( f"Botão NÃO encontrado. Confiança: {max_val:.2f} (mín: {confianca})", C_RED, ) return None def gerar_intervalo_aleatorio(intervalo_min: int, intervalo_max: int) -> int: return random.randint(intervalo_min, intervalo_max) def main() -> None: banner() painel_config() print(center(" 🚀 Iniciando sistema... ", C_TITLE + BOLD)) print() log("Pressione CTRL+C para parar.", C_YELLOW) print() primeiro_clique = True falhas_consecutivas = 0 total_cliques = 0 cliques_na_sessao = 0 numero_sessao = 1 try: while True: agora = datetime.now() if not dentro_horario(PERIODOS_SESSAO): log( f"[Sessão {numero_sessao}] Fora de todos os períodos configurados ({agora.strftime('%H:%M:%S')}).", C_YELLOW, ) log(f"[Espera] Dormindo {INTERVALO_CLIQUE_GLOBAL}s até o próximo clique global...", C_GRAY) time.sleep(INTERVALO_CLIQUE_GLOBAL) pos_global = encontrar_botao(IMAGEM_BOTAO, CONFIANCA) if pos_global: gx, gy = pos_global log( f"[Espera] Clicando globalmente em ({gx}, {gy})...", C_GREEN, ) pyautogui.click(gx, gy) else: log("[Espera] Botão não encontrado no clique global.", C_RED) falhas_consecutivas = 0 cliques_na_sessao = 0 continue if primeiro_clique: cabecalho_sessao(numero_sessao) log( f"Aguardando {INTERVALO_PRIMEIRO_CLIQUE}s antes do primeiro clique...", C_CYAN, ) time.sleep(INTERVALO_PRIMEIRO_CLIQUE) primeiro_clique = False posicao = encontrar_botao(IMAGEM_BOTAO, CONFIANCA) if posicao: x, y = posicao log( f"[Sessão {numero_sessao}] Clicando em ({x}, {y})...", C_GREEN, ) pyautogui.click(x, y) total_cliques += 1 cliques_na_sessao += 1 log( f"Clique #{cliques_na_sessao}/{MAXIMO_CLIQUES_POR_SESSAO} | Total geral: {total_cliques}", C_GREEN + BOLD, ) falhas_consecutivas = 0 rodape_clique() else: falhas_consecutivas += 1 log( f"[Sessão {numero_sessao}] Botão não encontrado. " f"Falhas: {falhas_consecutivas} | Total: {total_cliques}", C_RED, ) log( f"[Sessão {numero_sessao}] Re-tentando em {INTERVALO_MINIMO_CLIQUE}s...", C_YELLOW, ) time.sleep(INTERVALO_MINIMO_CLIQUE) continue if cliques_na_sessao >= MAXIMO_CLIQUES_POR_SESSAO: log( f"--- Sessão {numero_sessao} finalizada: {cliques_na_sessao} cliques ---", C_MAG + BOLD, ) numero_sessao += 1 cliques_na_sessao = 0 log( f"Aguardando {INTERVALO_ENTRE_SESSOES}s até a Sessão {numero_sessao}...", C_YELLOW, ) tempo_restante = INTERVALO_ENTRE_SESSOES while tempo_restante > 0: tempo_ate_clique = min(tempo_restante, INTERVALO_CLIQUE_GLOBAL) log( f"[Espera] Dormindo {tempo_ate_clique}s (restam {tempo_restante}s)...", C_GRAY, ) time.sleep(tempo_ate_clique) tempo_restante -= tempo_ate_clique if tempo_restante <= 0: break log( f"[Espera] Executando clique de keep-alive (restam {tempo_restante}s)...", C_CYAN, ) pos_espera = encontrar_botao(IMAGEM_BOTAO, CONFIANCA) if pos_espera: px, py = pos_espera log( f"[Espera] Clicando em ({px}, {py})...", C_GREEN, ) pyautogui.click(px, py) else: log("[Espera] Botão não encontrado no clique de keep-alive.", C_RED) primeiro_clique = True continue intervalo = gerar_intervalo_aleatorio( INTERVALO_MINIMO_CLIQUE, INTERVALO_MAXIMO_CLIQUE ) log( f"[Sessão {numero_sessao}] Aguardando {intervalo}s até o próximo ciclo...", C_CYAN, ) time.sleep(intervalo) except KeyboardInterrupt: print() print(C_RED + BOLD + center(" 🛑 INTERROMPIDO PELO USUÁRIO 🛑 ") + RST) print() log(f"Total de cliques realizados: {total_cliques}", C_YELLOW) if __name__ == "__main__": main()