ErinRiptide
Junior Member
- Sep 5, 2022
- 128
- 210
I know this is an age old method but this can still give you some money. I just stopped doing this because 20$ a day just ain't worth for me. I got the inspiration from another member who
posted the method here.
So this was just a passion project to see if this could still work in 2024 and it sure did though I would rather read book than monitor this bot which makes 20$ a day but it could maybe be of some help to someone else.
So basically you could set up an android emulator/ phone and use adb + image recognition/ coordinates to specify where exactly the bot must click. There are various untapped games with even higher potential which could make 50-100$ per day and you could easily automate everything from account creation (some games have restrictions after which you could just start spamming) to spamming the chat.
Earnings Proof:

posted the method here.
So this was just a passion project to see if this could still work in 2024 and it sure did though I would rather read book than monitor this bot which makes 20$ a day but it could maybe be of some help to someone else.
So basically you could set up an android emulator/ phone and use adb + image recognition/ coordinates to specify where exactly the bot must click. There are various untapped games with even higher potential which could make 50-100$ per day and you could easily automate everything from account creation (some games have restrictions after which you could just start spamming) to spamming the chat.
Code:
import cv2
import numpy as np
import time
import logging
import sys
import os
import subprocess
from PIL import Image
IMAGE_DIR = "C:/GameAutomation/Images/"
CHAT_ICON_IMAGE = os.path.join(IMAGE_DIR, "chat_icon.png")
CHAT_ICON_IMAGE1 = os.path.join(IMAGE_DIR, "chat_icon1.png")
CHAT_INPUT_IMAGE = os.path.join(IMAGE_DIR, "chat_input.png")
JOIN_BUTTON_IMAGE = os.path.join(IMAGE_DIR, "join_button.png")
CLAN_ICON_IMAGE = os.path.join(IMAGE_DIR, "clan_icon.png")
HAMBURGER_ICON_IMAGE = os.path.join(IMAGE_DIR, "hamburger_icon.png")
LEAVE_BUTTON_IMAGE = os.path.join(IMAGE_DIR, "leave_button.png")
TOURNAMENT_IMAGE = os.path.join(IMAGE_DIR, "tournament.png")
CHAT_YELLOW_IMAGE = os.path.join(IMAGE_DIR, "chat_yellow.png")
CONFIRM_LEAVE_BUTTON_IMAGE = os.path.join(IMAGE_DIR, "confirm_leave_button.png")
CANCEL_BUTTON_IMAGE = os.path.join(IMAGE_DIR, "cancel_button.png")
GO_BACK_BUTTON_IMAGE = os.path.join(IMAGE_DIR, "go_back_button.png")
TOURN_CONTINUE=os.path.join(IMAGE_DIR, "tourn_continue.png")
FILTERS=os.path.join(IMAGE_DIR, "filters.png")
CHAT_MESSAGE = ""
LOG_DIR = "C:/GameAutomation/Logs/"
os.makedirs(LOG_DIR, exist_ok=True)
LOG_FILE = os.path.join(LOG_DIR, "automation.log")
logging.basicConfig(
filename=LOG_FILE,
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
)
# Threshold for template matching
MATCH_THRESHOLD = 0.8 # Adjust based on testing
# Delay configurations (in seconds)
UI_LOAD_DELAY = 5
CLICK_DELAY = 1
SHORT_DELAY = 0.5
current_min_trophy = 200 # Starting value for Min Trophies
current_min_medal = 50 # Starting value for Min Medals
max_min_trophy = 2000 # Maximum threshold for Min Trophies
max_min_medal = 1000 # Maximum threshold for Min Medals
increment_trophy = 5 # Increment amount for Min Trophies
increment_medal = 100 # Increment amount for Min Medals
toggle_flag = False # True for Trophy, False for Medal
# Fixed Coordinates for Clans and Filters
FIXED_X_COORD = 464 # Adjust based on your device
FIXED_Y_SETS = [
[375, 503, 622, 743,865, 970, 1068],
[375, 503, 622, 743,865, 970, 1068],
[424, 540, 660,780, 900, 1020]
]
# Coordinates for UI elements (replace these with the correct values for your app)
x_filters, y_filters = 543, 250 # Coordinates for the Filters button
x_increase_trophy_button, y_increase_trophy_button = 580, 830 # Coordinates for Increase Trophy button
x_increase_medal_button, y_increase_medal_button = 580, 700 # Coordinates for Increase Medal button
x_save_button, y_save_button = 470, 1170 # Coordinates for the Save button
# ADB Path (if not in PATH, specify full path)
ADB_PATH = 'adb' # or 'C:/Path/To/adb.exe'
# ------------------------------ Helper Functions ------------------------------
def log_and_print(message, level="info"):
"""Logs the message and prints it to the console."""
levels = {
"info": logging.info,
"warning": logging.warning,
"error": logging.error,
"critical": logging.critical
}
levels.get(level, logging.info)(message)
print(f"[{level.upper()}] {message}")
def adb_shell(command_args):
"""Executes an adb shell command and returns the output."""
try:
result = subprocess.check_output([ADB_PATH, 'shell'] + command_args, stderr=subprocess.STDOUT)
return result.decode('utf-8')
except subprocess.CalledProcessError as e:
log_and_print(f"ADB command failed: {e}", "error")
return None
def adb_input_tap(x, y):
"""Performs an adb shell input tap at the specified coordinates."""
try:
subprocess.check_call([ADB_PATH, 'shell', 'input', 'tap', str(x), str(y)])
log_and_print(f"ADB tap at ({x}, {y})", "info")
time.sleep(CLICK_DELAY)
return True
except subprocess.CalledProcessError as e:
log_and_print(f"ADB tap failed: {e}", "error")
return False
def adb_screencap():
"""
Takes a screenshot using adb exec-out and returns the image as a NumPy array.
Streams the screenshot directly without saving it to disk.
"""
try:
# Execute screencap and stream the output directly
screenshot_bytes = subprocess.check_output([ADB_PATH, 'exec-out', 'screencap', '-p'])
# Convert the byte data to a NumPy array
nparr = np.frombuffer(screenshot_bytes, np.uint8)
# Decode the image from the NumPy array
image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
if image is None:
log_and_print("Failed to decode the screenshot image.", "error")
return None
log_and_print("Screenshot captured successfully using exec-out.", "info")
return image
except subprocess.CalledProcessError as e:
log_and_print(f"ADB screencap (exec-out) failed: {e}", "error")
return None
except Exception as e:
log_and_print(f"Unexpected error during adb_screencap_exec_out: {e}", "error")
return None
def get_screen_size():
"""Gets the screen size of the device."""
output = adb_shell(['wm', 'size'])
if output:
size_line = output.strip()
if 'Physical size:' in size_line:
size_str = size_line.split('Physical size:')[1].strip()
width, height = map(int, size_str.split('x'))
log_and_print(f"Screen size: {width}x{height}", "info")
return width, height
log_and_print("Failed to get screen size.", "error")
return None, None
def find_image_on_screen(template_path, color=None, threshold=MATCH_THRESHOLD, region=None):
"""
Searches for the given image on the screen within the specified region.
If a color is provided, only clicks on that color within the found image.
Returns the top-left coordinates if found, else None.
"""
try:
screenshot = adb_screencap()
if screenshot is None:
log_and_print("Failed to capture screenshot.", "error")
return None
if region:
x1, y1, x2, y2 = region
screenshot = screenshot[y1:y2, x1:x2]
template = cv2.imread(template_path, cv2.IMREAD_COLOR)
if template is None:
log_and_print(f"Template image '{template_path}' not found.", "error")
return None
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
w, h = template_gray.shape[::-1]
screenshot_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
# Template matching
res = cv2.matchTemplate(screenshot_gray, template_gray, cv2.TM_CCOEFF_NORMED)
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
# Log the match details
log_and_print(f"Found image '{os.path.basename(template_path)}' at ({pt[0]}, {pt[1]}).", "info")
if color:
# Crop the found region from the screenshot
found_region = screenshot[pt[1]:pt[1]+h, pt[0]:pt[0]+w]
# Define the color range for detection (assuming BGR format)
lower_color = np.array(color[0]) # Lower bound of the color
upper_color = np.array(color[1]) # Upper bound of the color
# Create a mask for the specified color
mask = cv2.inRange(found_region, lower_color, upper_color)
# Find the contours of the colored regions
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
# Get the center of the largest colored area
largest_contour = max(contours, key=cv2.contourArea)
M = cv2.moments(largest_contour)
if M["m00"] > 0:
center_x = int(M["m10"] / M["m00"]) + pt[0]
center_y = int(M["m01"] / M["m00"]) + pt[1]
log_and_print(f"Found color region at ({center_x}, {center_y}).", "info")
return (center_x, center_y)
else:
return pt
log_and_print(f"Image '{os.path.basename(template_path)}' not found on the screen.", "warning")
return None
except Exception as e:
log_and_print(f"Exception in find_image_on_screen: {e}", "error")
return None
def click_image(template_path, color=None, threshold=MATCH_THRESHOLD, region=None):
"""
Finds the image on the screen and clicks its center using adb tap.
If a color is provided, clicks on that color within the found image.
Returns True if clicked, else False.
"""
pt = find_image_on_screen(template_path, color, threshold, region)
if pt:
template = cv2.imread(template_path, cv2.IMREAD_COLOR)
if template is None:
log_and_print(f"Template image '{template_path}' not found during click.", "error")
return False
if not color: # Click on the center of the image if no color is provided
h, w, _ = template.shape
center_x = (pt[0] + w // 2) +5
center_y = pt[1] + h // 2
else:
# If color is provided, pt is already the color's center
center_x, center_y = pt
# Perform adb tap
if adb_input_tap(center_x, center_y):
log_and_print(f"Clicked on '{os.path.basename(template_path)}' at ({center_x}, {center_y}).", "info")
return True
else:
return False
return False
def tap_screen(x=None, y=None):
"""
Taps on the screen at the specified (x, y) coordinates using adb tap.
If no coordinates are provided, does nothing.
"""
try:
if x is not None and y is not None:
if adb_input_tap(x, y):
log_and_print(f"Tapped on the screen at ({x}, {y})", "info")
return True
else:
return False
else:
log_and_print("No coordinates provided for tap_screen.", "error")
return False
except Exception as e:
log_and_print(f"Exception during screen tap: {e}", "error")
return False
def drag_scroll(x_start=340, y_start=1090, x_end=340, y_end=360, num_scrolls=1, duration=2000):
try:
log_and_print(f"Swipe coordinates from ({x_start}, {y_start}) to ({x_end}, {y_end})", "info")
for i in range(num_scrolls):
subprocess.check_call([
ADB_PATH, 'shell', 'input', 'swipe',
str(x_start), str(y_start), str(x_end), str(y_end), str(duration)
])
log_and_print(f"Performed swipe {i+1}/{num_scrolls} from ({x_start}, {y_start}) to ({x_end}, {y_end})", "info")
time.sleep(SHORT_DELAY) # Delay between swipes
log_and_print("Completed drag_scroll_fixed operation successfully.", "info")
return True
except subprocess.CalledProcessError as e:
log_and_print(f"ADB swipe command failed: {e}", "error")
return False
except Exception as e:
log_and_print(f"Exception during drag_scroll_fixed: {e}", "error")
return False
def wait_for_image(template_path, timeout=15, check_interval=0.5, region=None):
"""
Waits until the specified image appears on the screen or until timeout.
Returns True if found, else False.
"""
start_time = time.time()
while time.time() - start_time < timeout:
if find_image_on_screen(template_path, region=region):
log_and_print(f"Image '{os.path.basename(template_path)}' found during wait.", "info")
return True
time.sleep(check_interval)
log_and_print(f"Timeout reached. Image '{os.path.basename(template_path)}' not found.", "warning")
return False
def simulate_long_press():
"""
Simulates a long press at (200, 1200) to bring up the 'Paste' option.
"""
try:
# Simulate a long press at (200, 1200) for 1.5 seconds
subprocess.check_call([ADB_PATH, 'shell', 'input', 'swipe', '200', '1200', '200', '1200', '1500'])
log_and_print("Simulated long press at (200, 1200).", "info")
return True
except subprocess.CalledProcessError as e:
log_and_print(f"Failed to simulate long press: {e}", "error")
return False
def simulate_paste():
"""
Simulates a tap at (75, 1230) to select the 'Paste' option.
"""
try:
# Simulate a tap at (75, 1230) to choose 'Paste'
subprocess.check_call([ADB_PATH, 'shell', 'input', 'tap', '75', '1230'])
log_and_print("Simulated tap on 'Paste' option at (75, 1230).", "info")
return True
except subprocess.CalledProcessError as e:
log_and_print(f"Failed to simulate paste: {e}", "error")
return False
def send_chat_message():
"""
Sends a predefined chat message.
"""
# Click on Chat Icon
if not click_image(CHAT_ICON_IMAGE):
log_and_print("Failed to click on Chat Icon.", "error")
return False
log_and_print("Clicked on Chat Icon.", "info")
time.sleep(SHORT_DELAY)
# Click on Chat Input Field
if not click_image(CHAT_INPUT_IMAGE):
log_and_print("Failed to click on Chat Input Field.", "error")
return False
log_and_print("Clicked on Chat Input Field.", "info")
time.sleep(SHORT_DELAY)
if not simulate_long_press():
log_and_print("Failed to simulate long press for 'Paste' option.", "error")
return False
# Simulate Paste
if not simulate_paste():
log_and_print("Failed to simulate paste.", "error")
return False
time.sleep(SHORT_DELAY)
if not adb_input_tap(500,500):
return False
if not click_image(CHAT_ICON_IMAGE1):
log_and_print("Failed to click on Chat Icon to send message.", "error")
return False
log_and_print("Clicked on Chat Icon to send the message.", "info")
time.sleep(CLICK_DELAY)
return True
def leave_clan():
"""
Leaves the currently joined clan by navigating through the UI.
"""
# Click on Clan Icon to navigate back to clan list
if not click_image(CLAN_ICON_IMAGE):
log_and_print("Failed to click on Clan Icon.", "error")
return False
time.sleep(SHORT_DELAY)
# Click on Hamburger Icon
if not click_image(HAMBURGER_ICON_IMAGE):
log_and_print("Failed to click on Hamburger Icon.", "error")
return False
time.sleep(SHORT_DELAY)
# Click on Leave Button
if not click_image(LEAVE_BUTTON_IMAGE):
log_and_print("Failed to click on Leave Button.", "error")
return False
log_and_print("Clicked on Leave Button. Waiting for confirmation dialog.", "info")
time.sleep(SHORT_DELAY)
# Wait for the confirmation Leave Button to appear
if CONFIRM_LEAVE_BUTTON_IMAGE and os.path.isfile(CONFIRM_LEAVE_BUTTON_IMAGE):
if not wait_for_image(CONFIRM_LEAVE_BUTTON_IMAGE, timeout=10):
log_and_print("Confirmation Leave Button did not appear.", "error")
return False
# Click on Confirm Leave Button
if not click_image(CONFIRM_LEAVE_BUTTON_IMAGE):
log_and_print("Failed to click on Confirm Leave Button.", "error")
return False
else:
# Using the same Leave Button Image for confirmation
if not wait_for_image(LEAVE_BUTTON_IMAGE, timeout=10):
log_and_print("Confirmation Leave Button did not appear.", "error")
return False
# Click on Confirm Leave Button
if not click_image(LEAVE_BUTTON_IMAGE):
log_and_print("Failed to click on Confirm Leave Button.", "error")
return False
log_and_print("Successfully confirmed leaving the clan.", "info")
time.sleep(UI_LOAD_DELAY)
return True
def process_clan_at_position(x_coord, y_coord):
"""
Processes a single clan at the given x_coord and y_coord.
"""
try:
log_and_print(f"Processing clan at (x={x_coord}, y={y_coord}).", "info")
# Tap on the clan position
if not tap_screen(x_coord, y_coord):
log_and_print("Failed to tap on clan position.", "error")
return False
time.sleep(UI_LOAD_DELAY)
# Click on Join Button
if not click_image(JOIN_BUTTON_IMAGE):
log_and_print("Failed to click on Join button.", "error")
return False
if not tap_screen(332, 238):
log_and_print("Failed to tap on clan position.", "error")
return False
time.sleep(CLICK_DELAY)
# Handle Cancel button if appears
if find_image_on_screen(CANCEL_BUTTON_IMAGE):
log_and_print("Cancel Button detected after clicking Join. Clicking Cancel.", "info")
if not click_image(CANCEL_BUTTON_IMAGE):
log_and_print("Failed to click on Cancel Button.", "error")
time.sleep(SHORT_DELAY)
# Go back to the clan list
if not click_image(GO_BACK_BUTTON_IMAGE):
log_and_print("Failed to click on Go Back Button.", "error")
time.sleep(SHORT_DELAY)
return True # Skip further actions for this clan
# Wait to ensure UI readiness
time.sleep(1)
tap_screen(500,500)
time.sleep(5)
if find_image_on_screen(TOURNAMENT_IMAGE, threshold=MATCH_THRESHOLD):
if not click_image(TOURN_CONTINUE,threshold=MATCH_THRESHOLD):
log_and_print("Failed to click on TOURN CONTINUE.", "error")
return False
if not click_image(TOURN_CONTINUE,threshold=MATCH_THRESHOLD):
log_and_print("Failed to click on TOURN CONTINUE.", "error")
return False
time.sleep(3)
log_and_print("Tournament detected.", "info")
if not click_image(CHAT_YELLOW_IMAGE, threshold=0.75):
log_and_print("Failed to click on CHAT_YELLOW_IMAGE.", "error")
return False
time.sleep(SHORT_DELAY)
# Send chat message
if not send_chat_message():
log_and_print("Failed to send chat message.", "error")
return False
# Leave clan
if not leave_clan():
log_and_print("Failed to leave the clan.", "error")
return False
time.sleep(SHORT_DELAY) # Wait between clans
return True
except Exception as e:
log_and_print(f"Exception in process_clan_at_position: {e}", "error")
return False
def send_word_by_word(chat_message):
"""
Sends a chat message word by word with spaces in between using adb shell input.
Parameters:
- chat_message: The message string to be sent word by word.
"""
words = chat_message.split(" ") # Split message into words
try:
for word in words:
# Send each word
subprocess.check_call([ADB_PATH, 'shell', 'input', 'text', word])
# Simulate space key press
subprocess.check_call([ADB_PATH, 'shell', 'input', 'keyevent', '62'])
time.sleep(SHORT_DELAY) # Add delay between each word
return True
except subprocess.CalledProcessError as e:
print(f"Failed to send word by word message: {e}")
return False
def apply_filters_and_medals():
"""
Applies filters and medals, then saves the settings.
Alternates between increasing Min Trophies and Min Medals.
Uses fixed x, y coordinates instead of image recognition.
"""
global current_min_trophy, current_min_medal, toggle_flag
# Tap on Filters button
if not click_image(FILTERS,threshold=MATCH_THRESHOLD):
log_and_print("Failed to tap on Filters button.", "error")
return False
time.sleep(SHORT_DELAY)
# Determine which filter to adjust
if toggle_flag:
# Adjust Min Trophies
log_and_print("Adjusting Minimum Trophies.", "info")
if current_min_trophy >= max_min_trophy:
log_and_print(f"Minimum Trophies reached max threshold ({max_min_trophy}). Skipping adjustment.", "warning")
else:
# Tap on Increase Trophy button
if not tap_screen(x_increase_trophy_button, y_increase_trophy_button):
log_and_print("Failed to tap on Increase Trophy button.", "error")
return False
# Update the current_min_trophy
current_min_trophy += increment_trophy
log_and_print(f"Increased Minimum Trophies to {current_min_trophy}.", "info")
else:
# Adjust Min Medals
log_and_print("Adjusting Minimum Medals.", "info")
if current_min_medal >= max_min_medal:
log_and_print(f"Minimum Medals reached max threshold ({max_min_medal}). Skipping adjustment.", "warning")
else:
# Tap on Increase Medal button
if not tap_screen(x_increase_medal_button, y_increase_medal_button):
log_and_print("Failed to tap on Increase Medal button.", "error")
return False
# Update the current_min_medal
current_min_medal += increment_medal
log_and_print(f"Increased Minimum Medals to {current_min_medal}.", "info")
# Toggle for next adjustment
toggle_flag = not toggle_flag
time.sleep(SHORT_DELAY)
# Tap on Save button
if not tap_screen(x_save_button, y_save_button):
log_and_print("Failed to tap on Save button.", "error")
return False
log_and_print("Filters and Medals applied and saved successfully.", "info")
time.sleep(UI_LOAD_DELAY)
return True
# ------------------------------ Main Automation Function ------------------------------
def automate_game_operations():
"""
Automates processing of clans using fixed coordinates and performs drag scrolls based on the index.
Implements a retry mechanism for processing clans.
"""
log_and_print("Automation script started.", "info")
# Verify that all required images exist
required_images = [
CHAT_ICON_IMAGE,
CHAT_INPUT_IMAGE,
JOIN_BUTTON_IMAGE,
CLAN_ICON_IMAGE,
HAMBURGER_ICON_IMAGE,
LEAVE_BUTTON_IMAGE,
CONFIRM_LEAVE_BUTTON_IMAGE,
CHAT_ICON_IMAGE1,
CANCEL_BUTTON_IMAGE,
# Add any additional required images here
]
all_exist = True
for img in required_images:
if not os.path.isfile(img):
log_and_print(f"Required image '{img}' is missing.", "critical")
all_exist = False
if not all_exist:
log_and_print("One or more required images are missing. Exiting.", "critical")
sys.exit(1)
# Initialize variables
clan_index = -1 # To keep track of the overall clan index
# Define retry parameters
MAX_RETRIES = 2 # Maximum number of retries per clan
RETRY_DELAY = 3 # Delay between retries in seconds
while True:
# Iterate through each set of Y coordinates
for index, y_set in enumerate(FIXED_Y_SETS):
for y_coord in y_set:
clan_index += 1 # Increment clan index at the start
retry_count = 0 # Initialize retry count for the current clan
while retry_count <= MAX_RETRIES:
log_and_print(f"Processing clan #{clan_index} at (x={FIXED_X_COORD}, y={y_coord}). Attempt {retry_count + 1}/{MAX_RETRIES}.", "info")
success = process_clan_at_position(FIXED_X_COORD, y_coord)
if success:
log_and_print(f"Successfully processed clan #{clan_index}.", "info")
break # Exit the retry loop and proceed to the next clan
else:
retry_count += 1
if retry_count < MAX_RETRIES:
log_and_print(f"Retrying clan #{clan_index} after {RETRY_DELAY} seconds...", "warning")
time.sleep(RETRY_DELAY)
else:
log_and_print(f"Failed to process clan #{clan_index} after {MAX_RETRIES} attempts. Skipping to the next clan.", "error")
# Scroll based on the current clan index
if 6 <= clan_index < 13: # For clans 7 to 14 (index 6 to 13)
log_and_print(f"Scrolling once after processing clan #{clan_index}.", "info")
drag_scroll()
elif clan_index >= 13: # For clans 15 and onwards
log_and_print(f"Scrolling twice after processing clan #{clan_index}.", "info")
drag_scroll()
drag_scroll()
# Apply filters and medals after processing all clan sets
apply_filters_and_medals()
log_and_print("Applying filters and medals after processing all clan sets.", "info")
# Reset clan index for the next iteration
clan_index = 0
# Check for a stopping condition if necessary
# For demonstration, we'll break after one full iteration
# Remove or modify this condition as needed
log_and_print("Completed one full iteration. Exiting loop.", "info")
log_and_print("Automation script completed.", "info")
# ------------------------------ Initial Filters Application ------------------------------
def apply_initial_filters_and_medals():
log_and_print("Applying initial filters and medals.", "info")
# ------------------------------ Main Entry Point ------------------------------
def main():
try:
apply_initial_filters_and_medals()
automate_game_operations()
except KeyboardInterrupt:
log_and_print("Script interrupted by user.", "warning")
except Exception as e:
log_and_print(f"Unexpected exception: {e}", "critical")
sys.exit(1)
if __name__ == "__main__":
main()
sys.exit(0)
Earnings Proof:
