Skip to content

Commit 4fefd80

Browse files
Merge pull request #5 from AlessandroFlati/develop
Develop
2 parents 5a3748f + f0607eb commit 4fefd80

5 files changed

Lines changed: 167 additions & 6 deletions

File tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# Expose actuator base class for easy import
2-
from .base import BaseActuator
2+
from .base import *
3+
from .adb_actuator import AdbActuator, AntiDetectionAdbActuator
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import abc
2+
3+
import numpy as np
4+
from adbutils import AdbDevice
5+
6+
from BlueStacksAgent import BaseActuator
7+
8+
9+
class AdbActuator(BaseActuator, abc.ABC):
10+
def __init__(self, adb_device: AdbDevice, adb_serial: str):
11+
super().__init__()
12+
self.adb_device: AdbDevice = adb_device
13+
self.adb_serial: str = adb_serial
14+
15+
def tap(self, x, y):
16+
self.adb_device.shell(f"input tap {x} {y}")
17+
18+
def swipe(self, x1, y1, x2, y2, duration):
19+
self.adb_device.shell(f"input swipe {x1} {y1} {x2} {y2} {duration}")
20+
21+
def keyevent(self, key):
22+
self.adb_device.shell(f"input keyevent {key}")
23+
24+
def text(self, text):
25+
self.adb_device.shell(f"input text {text}")
26+
27+
def press(self, key):
28+
self.adb_device.shell(f"input press {key}")
29+
30+
def longpress(self, key):
31+
self.adb_device.shell(f"input longpress {key}")
32+
33+
34+
class AntiDetectionModifier:
35+
"""
36+
This class is used to modify the behavior of the AdbActuator to avoid detection.
37+
"""
38+
39+
@staticmethod
40+
def randomize(x, y, width, height, margin=0.05) -> (int, int):
41+
"""
42+
Changes the coordinates to avoid detection, making a normal distribution around the center of the tap area
43+
In order to avoid overflow, the coordinates are clipped to the allowed area minus a small margin
44+
:param x: center x-coordinate of the tap
45+
:param y: center y-coordinate of the tap
46+
:param width: width of the allowed area to tap
47+
:param height: height of the allowed area to tap
48+
:return: the modified coordinates
49+
"""
50+
51+
x = max(int(np.random.normal(x, width * (1 - margin) // 4)), x - width * (1 - margin) // 2)
52+
x = min(x, x + width * (1 - margin) // 2)
53+
54+
y = max(int(np.random.normal(y, height * (1 - margin) // 4)), y - height * (1 - margin) // 2)
55+
y = min(y, y + height * (1 - margin) // 2)
56+
57+
return x, y
58+
59+
@staticmethod
60+
def tap(x, y, width, height) -> (int, int):
61+
"""
62+
Modify the tap coordinates to avoid detection.
63+
:param x: center x-coordinate of the tap
64+
:param y: center y-coordinate of the tap
65+
:param width: width of the allowed area to tap
66+
:param height: height of the allowed area to tap
67+
:return: the modified coordinates
68+
"""
69+
70+
return AntiDetectionModifier.randomize(x, y, width, height)
71+
72+
@staticmethod
73+
def swipe(x1, y1, x2, y2, width1, height1, width2, height2) -> (int, int, int, int):
74+
"""
75+
Modify the swipe coordinates to avoid detection.
76+
:param x1: start x-coordinate of the swipe
77+
:param y1: start y-coordinate of the swipe
78+
:param x2: end x-coordinate of the swipe
79+
:param y2: end y-coordinate of the swipe
80+
:param width1: width of the allowed area to start the swipe
81+
:param height1: height of the allowed area to start the swipe
82+
:param width2: width of the allowed area to end the swipe
83+
:param height2: height of the allowed area to end the swipe
84+
:return: the modified coordinates
85+
"""
86+
87+
x1, y1 = AntiDetectionModifier.randomize(x1, y1, width1, height1)
88+
x2, y2 = AntiDetectionModifier.randomize(x2, y2, width2, height2)
89+
90+
return x1, y1, x2, y2
91+
92+
@staticmethod
93+
def keyevent(key) -> int:
94+
"""
95+
Modify the keyevent to avoid detection.
96+
:param key: the key to press
97+
:return: the modified key
98+
"""
99+
100+
return key
101+
102+
@staticmethod
103+
def text(text) -> str:
104+
"""
105+
Modify the text to avoid detection.
106+
:param text: the text to input
107+
:return: the modified text
108+
"""
109+
110+
return text
111+
112+
@staticmethod
113+
def press(key) -> int:
114+
"""
115+
Modify the press to avoid detection.
116+
:param key: the key to press
117+
:return: the modified key
118+
"""
119+
120+
return key
121+
122+
@staticmethod
123+
def longpress(key) -> int:
124+
"""
125+
Modify the longpress to avoid detection.
126+
:param key: the key to press
127+
:return: the modified key
128+
"""
129+
130+
return key
131+
132+
133+
class AntiDetectionAdbActuator(BaseActuator, abc.ABC):
134+
def __init__(self, adb_device: AdbDevice, adb_serial: str):
135+
super().__init__()
136+
self.adb_actuator = AdbActuator(adb_device, adb_serial)
137+
138+
def tap(self, x, y, width, height):
139+
x, y = AntiDetectionModifier.tap(x, y, width, height)
140+
self.adb_actuator.tap(x, y)
141+
142+
def swipe(self, x1, y1, x2, y2, width1, height1, width2, height2, duration):
143+
x1, y1, x2, y2 = AntiDetectionModifier.swipe(x1, y1, x2, y2, width1, height1, width2, height2)
144+
self.adb_actuator.swipe(x1, y1, x2, y2, duration)
145+
146+
def keyevent(self, key):
147+
key = AntiDetectionModifier.keyevent(key)
148+
self.adb_actuator.keyevent(key)
149+
150+
def text(self, text):
151+
text = AntiDetectionModifier.text(text)
152+
self.adb_actuator.text(text)
153+
154+
def press(self, key):
155+
key = AntiDetectionModifier.press(key)
156+
self.adb_actuator.press(key)
157+
158+
def longpress(self, key):
159+
key = AntiDetectionModifier.longpress(key)
160+
self.adb_actuator.longpress(key)

BlueStacksAgent/agents/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Expose agents for easy import
2-
from .base import BaseAgent
3-
from .scrcpy_agent import ScrcpyAgent
2+
from .base import *
3+
from .scrcpy_agent import *

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[project]
22
name = "BlueStacksAgent"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
description = "A Python library for real-time interaction with BlueStacks using scrcpy, minicap, and MediaProjection."
55
readme = "README.md"
6-
requires-python = ">=3.7"
6+
requires-python = ">=3.7, <3.11"
77
license = { file = "LICENSE" }
88
dependencies = [
99
"scrcpy-client",

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name="BlueStacksAgent",
5-
version="0.1.0",
5+
version="0.2.0",
66
description="A Python library for real-time interaction with BlueStacks using scrcpy, minicap, and MediaProjection.",
77
long_description=open("README.md", encoding="utf8").read(),
88
long_description_content_type="text/markdown",

0 commit comments

Comments
 (0)