Source code for apparser.movers.math_antirobot

import random
from typing import Generator, Callable

import pyautogui

from apparser.geometry import distance, Point

from apparser.movers.base import BaseMover


class DefaultMoveGenerator:
    """Generate multi-step cursor paths with random timing and offsets."""

    def __init__(self, min_time: float = 0.1, max_time: float = 2, min_shift: float = 30, max_shift: float = 100):
        """Initialize a movement path generator.

        :param min_time: Minimum delay between steps.
        :type min_time: float
        :param max_time: Maximum delay between steps.
        :type max_time: float
        :param min_shift: Minimum shift distance for intermediate steps.
        :type min_shift: float
        :param max_shift: Maximum shift distance for intermediate steps.
        :type max_shift: float
        :raises TypeError: If any argument has an invalid type.
        :raises ValueError: If time or shift limits are invalid.
        """
        if not (isinstance(min_time, float) or isinstance(min_time, int)):
            raise TypeError('min_time must be number')

        if not (isinstance(max_time, float) or isinstance(max_time, int)):
            raise TypeError('max_time must be number')

        if not (isinstance(min_shift, float) or isinstance(min_shift, int)):
            raise TypeError('min_shift must be number')

        if not (isinstance(max_shift, float) or isinstance(max_shift, int)):
            raise TypeError('max_shift must be number')

        if min_shift > max_shift:
            raise ValueError('min_shift must be less than max_shift')

        if min_time > max_time:
            raise ValueError('min_time must be less than max_time')

        if min_time < 0:
            raise ValueError('min_time must be >= 0')

        self.__min_time = min_time
        self.__max_time = max_time
        self.__min_shift = min_shift
        self.__max_shift = max_shift

    def __get_random_time(self) -> float:
        return random.uniform(self.__min_time, self.__max_time)

    def __get_random_position(self, current_position: Point, end_position: Point) -> Point:
        current_distance = distance(current_position, end_position)
        random_shift = min(random.uniform(self.__min_shift, self.__max_shift), current_distance)

        x_shift = end_position.x - current_position.x
        y_shift = end_position.y - current_position.y

        x_sign = 1 if x_shift >= 0 else -1
        y_sign = 1 if y_shift >= 0 else -1

        x_part = abs(x_shift) / current_distance
        y_part = abs(y_shift) / current_distance

        return Point(round(random_shift * x_part) * x_sign,
                     round(random_shift * y_part) * y_sign)

    def __call__(self, start_position: Point, end_position: Point) -> Generator[tuple[Point, float], None, None]:
        """Yield intermediate cursor positions and durations.

        :param start_position: Initial cursor position.
        :type start_position: Point
        :param end_position: Final cursor position.
        :type end_position: Point
        :return: Generated cursor path.
        :rtype: Generator[tuple[Point, float], None, None]
        :raises TypeError: If either point has an invalid type.
        """
        if not isinstance(start_position, Point):
            raise TypeError('start_position must be Point')

        if not isinstance(end_position, Point):
            raise TypeError('end_position must be Point')

        while start_position != end_position:
            if distance(start_position, end_position) <= self.__max_shift:
                yield end_position, self.__get_random_time()
                break
            added_point = self.__get_random_position(start_position, end_position)
            start_position += added_point
            yield start_position, self.__get_random_time()


[docs] class AntiRobotMover(BaseMover): """Move the cursor by using generated multi-step paths.""" def __init__(self, move_generator: Callable[[Point, Point], Generator[tuple[Point, float], None, None]] = DefaultMoveGenerator(0.3, 0.6)): """Initialize a mover that follows generated paths. :param move_generator: Callable that yields cursor positions and durations. :type move_generator: Callable[[Point, Point], Generator[tuple[Point, float], None, None]] """ self.__move_generator = move_generator
[docs] def move(self, position: Point): """Move the cursor to the target position. :param position: Target cursor position. :type position: Point :raises TypeError: If ``position`` has an invalid type. """ if not isinstance(position, Point): raise TypeError('position must be Point') current_position = Point(*pyautogui.position()) for i, t in self.__move_generator(current_position, position): pyautogui.moveTo(i.x, i.y, duration=t)