pgo-automation/src/main/java/com/pokemongo/PokemonGoAutomation.java
2026-05-19 19:42:21 +02:00

635 lines
22 KiB
Java
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.pokemongo;
import java.awt.AWTException;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
/**
* Automation tool for Pokémon GO.
* Finds running application, selects all Pokémon and clicks Transfer button.
*/
public class PokemonGoAutomation {
private Robot robot;
private Rectangle windowBounds;
private BufferedImage t1Template; // TRANSFER button template
private BufferedImage t2Template; // Confirmation button template
private BufferedImage t3Template;
private BufferedImage pok1Template; // Pokemon detection template
private BufferedImage includeTemplate; // Include button template
private static final int DELAY_BETWEEN_CLICKS = 100; // ms
private static final int DELAY_AFTER_ACTION = 100; // ms
private int transferredPokemonCount = 0; // Count of transferred pokemon
private Point initialMousePosition = null; // Cursor position before start of automation
private volatile boolean stopRequested = false;
public PokemonGoAutomation() throws AWTException {
this.robot = new Robot();
this.robot.setAutoDelay(50);
this.robot.setAutoWaitForIdle(true);
loadButtonTemplates();
}
/**
* Returns count of pokemon transferred so far
*/
public int getTransferredCount() {
return transferredPokemonCount;
}
/**
* Resets counter of transferred pokemon
*/
public void resetTransferredCount() {
transferredPokemonCount = 0;
}
public void requestStop() {
stopRequested = true;
}
private boolean shouldStop() {
return stopRequested || Thread.currentThread().isInterrupted();
}
/**
* Takes screenshot of window area
*/
private BufferedImage captureScreen(Rectangle area) {
return robot.createScreenCapture(area);
}
/**
* Loads templates for TRANSFER button (t1.png) and confirmation (t2.png)
*/
private void loadButtonTemplates() {
try {
String t1Paths = "/t1.png";
t1Template = ImageIO.read(getClass().getResourceAsStream(t1Paths));
System.out.println("✅ Template TRANSFER loaded from: " + t1Paths);
String t2Paths = "/t2.png";
t2Template = ImageIO.read(getClass().getResourceAsStream(t2Paths));
System.out.println("✅ Template Confirmation loaded from: " + t2Paths);
String t3Paths = "/t3.png";
t3Template = ImageIO.read(getClass().getResourceAsStream(t3Paths));
System.out.println("✅ Template Confirmation loaded from: " + t3Paths);
String pok1Paths = "/pok1.png";
pok1Template = ImageIO.read(getClass().getResourceAsStream(pok1Paths));
System.out.println("✅ Template Pokémon loaded from: " + pok1Paths);
String includePaths = "/include.png";
includeTemplate = ImageIO.read(getClass().getResourceAsStream(includePaths));
System.out.println("✅ Template INCLUDE loaded from: " + includePaths);
if (t1Template == null) {
System.out.println("⚠️ Template t1.png not found");
}
if (t2Template == null) {
System.out.println("⚠️ Template t2.png not found");
}
if (pok1Template == null) {
System.out.println("⚠️ Template pok1.png not found");
}
if (includeTemplate == null) {
System.out.println("⚠️ Template include.png not found");
}
} catch (IOException e) {
System.err.println("⚠️ Error loading templates: " + e.getMessage());
}
}
/**
* Compares template with area in image - simple pixel comparison with
* optimization
*/
private double templateMatch(BufferedImage screenshot, int startX, int startY, BufferedImage template,
int tolerance) {
if (template == null)
return 0.0;
if (shouldStop()) {
return 0.0;
}
int tWidth = template.getWidth();
int tHeight = template.getHeight();
// Kontrola hranic
if (startX + tWidth > screenshot.getWidth() || startY + tHeight > screenshot.getHeight()) {
return 0.0;
}
if (startX < 0 || startY < 0) {
return 0.0;
}
int matchingPixels = 0;
int totalPixels = tWidth * tHeight;
int requiredMatch = (int) (totalPixels * 0.75); // 75% shody
for (int y = 0; y < tHeight; y++) {
if (shouldStop()) {
return 0.0;
}
for (int x = 0; x < tWidth; x++) {
int templateRGB = template.getRGB(x, y);
int screenRGB = screenshot.getRGB(startX + x, startY + y);
// Split RGB values
int tR = (templateRGB >> 16) & 0xFF;
int tG = (templateRGB >> 8) & 0xFF;
int tB = templateRGB & 0xFF;
int sR = (screenRGB >> 16) & 0xFF;
int sG = (screenRGB >> 8) & 0xFF;
int sB = screenRGB & 0xFF;
// Compare colors - increased tolerance for better detection
int rDiff = Math.abs(tR - sR);
int gDiff = Math.abs(tG - sG);
int bDiff = Math.abs(tB - sB);
if (rDiff < tolerance && gDiff < tolerance && bDiff < tolerance) {
matchingPixels++;
// Early exit - if we have not reached minimum, stop
if (matchingPixels < requiredMatch / 2 && (y * tWidth + x) > (totalPixels / 2)) {
return 0.0;
}
}
}
}
// Return percentage match (0.0-1.0)
return (double) matchingPixels / totalPixels;
}
/**
* Finds and returns position of confirmation button without clicking
*/
private Point findConfirmTransferButtonPosition(BufferedImage template, int tolerance) {
System.out.println("Looking for confirm TRANSFER button using template matching...");
if (shouldStop()) {
return null;
}
BufferedImage screenshot = captureScreen(windowBounds);
int tWidth = template.getWidth();
int tHeight = template.getHeight();
// Dialog is usually in upper half after TRANSFER click
int searchStartY = (int) (windowBounds.height * 0.40);
int searchEndY = (int) (windowBounds.height * 0.75);
double bestScore = 0.0;
int bestX = 0;
int bestY = 0;
// Scan Y positions with larger step - 15px instead of 5px for speed
for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) {
if (shouldStop()) {
return null;
}
// Also scan X positions to find center of match
for (int x = 0; x <= windowBounds.width - tWidth; x += 30) {
double score = templateMatch(screenshot, x, y, template, tolerance);
if (score > bestScore) {
bestScore = score;
bestX = x;
bestY = y;
}
}
}
if (bestScore > 0.85) {
Point bestMatch = new Point(
windowBounds.x + bestX + (tWidth / 2),
windowBounds.y + bestY + (tHeight / 2));
System.out.println("✅ Found confirmation button (template match) at: " + bestMatch + " (match: "
+ String.format("%.1f", bestScore * 100) + "%)");
return bestMatch;
}
System.out.println("Template not found");
return null;
}
private Point findIncludeButtonPosition(int tolerance) {
System.out.println("Looking for INCLUDE button using template matching (include.png)...");
if (shouldStop()) {
return null;
}
BufferedImage screenshot = captureScreen(windowBounds);
int tWidth = includeTemplate.getWidth();
int tHeight = includeTemplate.getHeight();
// Dialog is usually in upper half after TRANSFER click
int searchStartY = (int) (windowBounds.height * 0.40);
int searchEndY = (int) (windowBounds.height * 0.75);
double bestScore = 0.0;
int bestX = 0;
int bestY = 0;
// Scan Y positions with larger step - 15px instead of 5px for speed
for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) {
if (shouldStop()) {
return null;
}
// Also scan X positions to find center of match
for (int x = 0; x <= windowBounds.width - tWidth; x += 30) {
double score = templateMatch(screenshot, x, y, includeTemplate, tolerance);
if (score > bestScore) {
bestScore = score;
bestX = x;
bestY = y;
}
}
}
if (bestScore > 0.80) {
Point bestMatch = new Point(
windowBounds.x + bestX + (tWidth / 2),
windowBounds.y + bestY + (tHeight / 2));
System.out.println("✅ Found INCLUDE button (template match) at: " + bestMatch + " (match: "
+ String.format("%.1f", bestScore * 100) + "%)");
return bestMatch;
}
System.out.println("Template not found");
return null;
}
/**
* Finds window with name containing "Pokémon" or "Pokemon"
*/
public boolean findPokemonGoWindow() {
try {
Rectangle bounds = WindowFinder.findWindowByName("Lenovo");
if (bounds != null && bounds.width > 0 && bounds.height > 0) {
windowBounds = bounds;
System.out.println("Window found: " + windowBounds);
return true;
}
System.out.println("Pokémon GO window not found.");
return false;
} catch (Exception e) {
System.err.println("Error finding window: " + e.getMessage());
e.printStackTrace();
return false;
}
}
/**
* Aktivuje okno aplikace
*/
public void activateWindow() {
try {
// Click on top panel of window (title bar) for activation
// Title bar is usually 30-40px high at top of window
int centerX = windowBounds.x + windowBounds.width / 2;
int titleBarY = windowBounds.y - 15; // 15px from top of window = top panel
System.out.println("Activating window by clicking title bar: (" + centerX + ", " + titleBarY + ")");
robot.mouseMove(centerX, titleBarY);
robot.delay(200);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
robot.delay(DELAY_AFTER_ACTION);
System.out.println("Window activated");
} catch (Exception e) {
System.err.println("Error activating window: " + e.getMessage());
}
}
/**
* Clicks Transfer button at bottom of screen
*/
public void clickTransferButton() {
if (shouldStop()) {
return;
}
// Use button detection
// Point buttonPos = findTransferButtonPosition();
Point buttonPos = getAbsolutePoint(300, 1156);
if (buttonPos == null) {
System.err.println("TRANSFER button not found!");
return;
}
robot.mouseMove(buttonPos.x, buttonPos.y);
robot.delay(DELAY_AFTER_ACTION);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
System.out.println("TRANSFER button clicked!");
robot.delay(DELAY_AFTER_ACTION);
}
/**
* Confirms transfer (if confirmation dialog is needed)
*/
public void clickConfirmTransferButton(BufferedImage template, int tolerance) {
System.out.println("Confirming transfer - looking for confirmation button...");
if (shouldStop()) {
return;
}
robot.delay(100); // Wait for dialog to appear
// Find green TRANSFER button in confirmation dialog
Point buttonPos = findConfirmTransferButtonPosition(template, tolerance);
if (buttonPos == null) {
System.out.println("Confirmation button not found, skipping confirmation.");
return;
}
robot.mouseMove(buttonPos.x, buttonPos.y);
robot.delay(DELAY_BETWEEN_CLICKS);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
System.out.println("Transfer finally confirmed!");
robot.delay(DELAY_AFTER_ACTION);
}
public boolean clickIncludeButton(int tolerance) {
System.out.println("Confirming transfer - looking for include.png (confirmation button)...");
if (shouldStop()) {
return false;
}
robot.delay(100); // Wait for dialog to appear
// Find green INCLUDE button in confirmation dialog
Point buttonPos = findIncludeButtonPosition(tolerance);
if (buttonPos == null) {
System.out.println("INCLUDE button not found, skipping confirmation.");
return false;
}
robot.mouseMove(buttonPos.x, buttonPos.y);
robot.delay(DELAY_BETWEEN_CLICKS);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
System.out.println("Transfer finally confirmed!");
robot.delay(DELAY_AFTER_ACTION);
return true;
}
/**
* Starts Transfer automation with specific number of pokemon
*
* @param totalPokemonCount Total number of pokemon to transfer
* @param delaySeconds Delay between iterations (in seconds)
*/
public void runWithCount(int totalPokemonCount, int delaySeconds) {
System.out.println("Starting Transfer automation - Pokémon count: " + totalPokemonCount + ", Delay: "
+ delaySeconds + "s");
// Save current cursor position
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
initialMousePosition = pointerInfo != null ? pointerInfo.getLocation() : null;
if (initialMousePosition != null) {
System.out.println("Initial cursor position saved: " + initialMousePosition);
}
if (!findPokemonGoWindow()) {
System.err.println("Failed to find Pokémon GO window!");
return;
}
activateWindow();
System.out.println("Waiting 1 second before start...");
robot.delay(1000);
int transferredCount = 0;
// Transfer until we reach desired count
try {
while (transferredCount < totalPokemonCount) {
// Check interruption
if (shouldStop()) {
System.out.println("\n⚠ Automation was interrupted!");
return;
}
int pokemonThisRound = Math.min(12, totalPokemonCount - transferredCount);
System.out.println(
"\n=== Iteration " + (transferredCount / 9 + 1) + " - Transferring max " + pokemonThisRound
+ " pokemon ===");
// Select pokemon and get actual number selected
int actualTransferredCount = selectAllPokemonCount(pokemonThisRound);
if (shouldStop()) {
System.out.println("\n⚠ Automation was interrupted during selection!");
return;
}
System.out.println("Waiting for TRANSFER...");
clickTransferButton();
if (shouldStop()) {
System.out.println("\n⚠ Automation was interrupted after clicking TRANSFER!");
return;
}
System.out.println("Waiting for INCLUDE dialog...");
robot.delay(100);
if (clickIncludeButton(110)) {
System.out.println("Waiting for confirmation dialog t3...");
robot.delay(100);
clickConfirmTransferButton(t3Template, 70);
} else {
System.out.println("Waiting for confirmation dialog t2...");
robot.delay(100);
clickConfirmTransferButton(t2Template, 70);
}
transferredCount += actualTransferredCount;
transferredPokemonCount += actualTransferredCount;
System.out.println("Transferred in this iteration: " + actualTransferredCount + "pokemon (total: "
+ transferredCount + ")");
// If there is still something to transfer, wait
if (transferredCount < totalPokemonCount) {
System.out.println("Break: " + delaySeconds + " seconds...");
// Wait with checking interruption
long endTime = System.currentTimeMillis() + (delaySeconds * 1000L);
while (System.currentTimeMillis() < endTime) {
if (shouldStop()) {
System.out.println("\n⚠ Automation was interrupted during break!");
break;
}
robot.delay(100);
}
}
}
} finally {
// Obnovit pozici kurzoru
if (initialMousePosition != null) {
try {
robot.mouseMove(initialMousePosition.x, initialMousePosition.y);
System.out.println("Cursor returned to initial position: " + initialMousePosition);
} catch (Exception e) {
System.err.println("Error restoring cursor position: " + e.getMessage());
}
}
}
System.out.println("\n✅ Transfer automation complete! Transferred: " + transferredCount + "pokemon");
}
/**
* Selects specific number of pokemon
*
* @return Actual number of selected pokemon
*/
private int selectAllPokemonCount(int count) {
System.out.println("Starting to select " + count + "pokemon (template matching only)...");
List<Point> positions = getPossitionsAbsolute();
int maxPokemon = Math.min(count, positions.size());
System.out.println("Will select " + maxPokemon + "pokemon...");
for (int i = 0; i < maxPokemon; i++) {
if (shouldStop()) {
System.out.println("Selection interrupted.");
return i;
}
Point pos = positions.get(i);
System.out.println("Clicking on Pokémon " + (i + 1) + "/" + maxPokemon + " at position: " + pos);
robot.mouseMove(pos.x, pos.y);
if (i == 0) {
System.out.println(" -> Long press (activate multi-select)");
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.delay(700);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
robot.delay(DELAY_AFTER_ACTION);
} else {
System.out.println(" -> Normal click");
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
}
robot.delay(DELAY_BETWEEN_CLICKS);
}
System.out.println("Selected " + maxPokemon + "pokemon!");
System.out.println("Waiting before clicking TRANSFER...");
robot.delay(800);
return maxPokemon;
}
private List<Point> getPossitionsAbsolute() {
List<Point> possitions = new ArrayList<>();
possitions.add(getAbsolutePoint(115, 290));
possitions.add(getAbsolutePoint(310, 290));
possitions.add(getAbsolutePoint(504, 290));
possitions.add(getAbsolutePoint(115, 510));
possitions.add(getAbsolutePoint(310, 510));
possitions.add(getAbsolutePoint(504, 510));
possitions.add(getAbsolutePoint(115, 730));
possitions.add(getAbsolutePoint(310, 730));
possitions.add(getAbsolutePoint(504, 730));
possitions.add(getAbsolutePoint(115, 950));
possitions.add(getAbsolutePoint(310, 950));
possitions.add(getAbsolutePoint(504, 950));
return possitions;
}
/**
* Converts relative position in window (0.0-1.0) to absolute screen coordinates
*
* @param relativeX Relative X position (0.0 = left edge, 1.0 = right edge)
* @param relativeY Relative Y position (0.0 = top edge, 1.0 = bottom edge)
* @return Absolute position on screen
*/
private Point getAbsolutePoint(int relativeX, int relativeY) {
int xCorrection = 0;
int yCorrection = 0;
int absoluteX = windowBounds.x + relativeX + xCorrection;
int absoluteY = windowBounds.y + relativeY + yCorrection;
return new Point(absoluteX, absoluteY);
}
/**
* Cleans up all resources used by automation
* Call after automation finishes
*/
public void cleanup() {
try {
System.out.println("Cleaning up automation resources...");
// Release references to images
t1Template = null;
t2Template = null;
t3Template = null;
pok1Template = null;
includeTemplate = null;
windowBounds = null;
// Explicitly invoke garbage collector
System.gc();
System.out.println("Automation resources cleaned");
} catch (Exception e) {
System.err.println("Error cleaning up resources: " + e.getMessage());
}
}
public static void main(String[] args) {
System.out.println("=== Pokémon GO Automation Tool ===");
System.out.println("Make sure Pokémon GO is running...");
}
}