AutoMonkey 0.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,300 @@
1
+ """Python Automation using Mouse and Keyboard, for the masses
2
+ """
3
+ from time import sleep
4
+ from sys import exit as end
5
+
6
+ from sys import platform
7
+
8
+ if platform == "win32":
9
+ from os import startfile # It is used
10
+ elif platform == "linux":
11
+ from subprocess import call
12
+ def startfile(file):
13
+ call(["xdg-open", file])
14
+
15
+ from clipboard import paste
16
+
17
+ from pyautogui import click # It is used
18
+ from pyautogui import moveTo # It is used
19
+ from pyautogui import mouseUp # It is used
20
+ from pyautogui import confirm
21
+ from pyautogui import mouseDown # It is used
22
+ from pyautogui import locateOnScreen
23
+ from pyautogui import scroll as scrollup # It is used
24
+ from pyautogui import hscroll as scrollright # It is used
25
+ from pyautogui import leftClick as leftclick # It is used
26
+ from pyautogui import rightClick as rightclick # It is used
27
+ from pyautogui import middleClick as middleclick # It is used
28
+ from pyautogui import doubleClick as doubleclick # It is used
29
+ from pyautogui import tripleClick as tripleclick # It is used
30
+ from keyboard import send as keys3 # works mostly on windows - TODO: Check difference to below
31
+ from keyboard import press_and_release as keys4 # works mostly on windows
32
+
33
+ from screeninfo import get_monitors
34
+
35
+ from .constants import MOUSE_ACTIONS
36
+ from .constants import WAIT_ACTIONS
37
+ from .constants import KEYBOARD_ACTIONS
38
+ from .constants import APPS_ACTIONS
39
+ from .constants import IMG_ACTIONS
40
+ from .constants import CONTROL_ACTIONS
41
+ from .constants import COMMON_APPS
42
+
43
+ from .exceptions import AutoMonkeyNoAction
44
+ from .exceptions import AutoMonkeyNoTarget
45
+
46
+ from .mouse_tracker import track_mouse
47
+ from .mouse_tracker import PositionTracker
48
+
49
+ from .key_funcs import write # keyboard-layout aware
50
+ from .key_funcs import press as keys # this normally is to be used for same key left, left, left
51
+ from .key_funcs import hotkey as keys2 # this is best solution, pass list to be unpacked with *list
52
+
53
+ if platform == "win32":
54
+ from .app_funcs import open_app
55
+ from .app_funcs import minimize
56
+ from .app_funcs import maximize
57
+ from .app_funcs import close
58
+ from .app_funcs import restore
59
+ from .app_funcs import focus
60
+ from .app_funcs import msoffice_replace
61
+ from .app_funcs import copy
62
+
63
+ from .mouse_funcs import movedown
64
+ from .mouse_funcs import moveleft
65
+ from .mouse_funcs import moveright
66
+ from .mouse_funcs import moveup
67
+
68
+ from .img_funcs import _add_ext
69
+ from .img_funcs import is_on_screen
70
+ from .img_funcs import get_center
71
+ from .img_funcs import diagonal_point
72
+
73
+ from .utils import waitwhile
74
+ from .utils import waituntil
75
+ from .utils import pastetext
76
+ from .utils import copy_from
77
+ from .utils import scrolldown
78
+ from .utils import scrollleft
79
+ from .utils import copy_from_to
80
+
81
+ if platform == "win32":
82
+ from .pywinauto_funcs import get_window
83
+ from .pywinauto_funcs import get_control
84
+ from .pywinauto_funcs import start_app
85
+ from .pywinauto_funcs import click_control
86
+ from .pywinauto_funcs import double_click_control
87
+ from .pywinauto_funcs import right_click_control
88
+ from .pywinauto_funcs import type_in_control
89
+ from .pywinauto_funcs import set_control_text
90
+ from .pywinauto_funcs import get_control_text
91
+ from .pywinauto_funcs import select_menu
92
+ from .pywinauto_funcs import select_item
93
+ from .pywinauto_funcs import check_control
94
+ from .pywinauto_funcs import uncheck_control
95
+ from .pywinauto_funcs import wait_control
96
+ from .pywinauto_funcs import control_exists
97
+ from .pywinauto_funcs import list_controls
98
+ else: # linux: real AT-SPI backend; macOS: importable, raises NotImplementedError when used
99
+ from .atspi_funcs import get_window
100
+ from .atspi_funcs import get_control
101
+ from .atspi_funcs import start_app
102
+ from .atspi_funcs import click_control
103
+ from .atspi_funcs import double_click_control
104
+ from .atspi_funcs import right_click_control
105
+ from .atspi_funcs import type_in_control
106
+ from .atspi_funcs import set_control_text
107
+ from .atspi_funcs import get_control_text
108
+ from .atspi_funcs import select_menu
109
+ from .atspi_funcs import select_item
110
+ from .atspi_funcs import check_control
111
+ from .atspi_funcs import uncheck_control
112
+ from .atspi_funcs import wait_control
113
+ from .atspi_funcs import control_exists
114
+ from .atspi_funcs import list_controls
115
+
116
+
117
+ ALL_ACTIONS = MOUSE_ACTIONS + KEYBOARD_ACTIONS + WAIT_ACTIONS + APPS_ACTIONS + IMG_ACTIONS + CONTROL_ACTIONS
118
+
119
+ def _wait_for_target(target: any, skip: bool = False):
120
+ """Wait for a target to be available"""
121
+ slept = 0
122
+ while not is_on_screen(target) and not skip:
123
+ sleep(0.1)
124
+ slept += 0.1
125
+ if int(slept) == 30: # For production make it 300
126
+ stop = confirm("Next target was not found for 5 minutes.\
127
+ Would you like to continue or stop?",
128
+ "Continue?",
129
+ ["Continue", "Stop"])
130
+ if stop == "Stop":
131
+ end()
132
+
133
+
134
+ def _prepare_step(raw_step: dict) -> dict:
135
+ """Transform the raw step into a step that can be used by the script
136
+
137
+ Args:
138
+ raw_step (dict): The raw step from the json file
139
+
140
+ Raises:
141
+ AutoMonkeyNoAction: If the action is not supported
142
+ AutoMonkeyNoTarget: If the target is not supported
143
+
144
+ Returns:
145
+ dict: The step that can be used by the script
146
+ """
147
+ step = dict(
148
+ action=None,
149
+ target=None,
150
+ skip=False,
151
+ wait=0,
152
+ confidence=0.9,
153
+ v_offset=0,
154
+ h_offset=0,
155
+ offset=None,
156
+ monitor=1,
157
+ )
158
+
159
+ for arg_pair in raw_step.items():
160
+ step["action"] = arg_pair[0] if arg_pair[0] in ALL_ACTIONS else step["action"]
161
+ step["target"] = arg_pair[1] if arg_pair[0] in ALL_ACTIONS else step["target"]
162
+ step["skip"] = bool(arg_pair[1]) if arg_pair[0] == 'skip' else step["skip"]
163
+ step["wait"] = float(arg_pair[1]) if arg_pair[0] == 'wait' else step["wait"]
164
+ step["confidence"] = float(arg_pair[1]) if arg_pair[0] == 'confidence' else step["confidence"]
165
+ step["v_offset"] = int(arg_pair[1]) if arg_pair[0] == 'v_offset' else step["v_offset"]
166
+ step["h_offset"] = int(arg_pair[1]) if arg_pair[0] == 'h_offset' else step["h_offset"]
167
+ step["offset"] = str(arg_pair[1]) if arg_pair[0] == 'offset' else step["offset"]
168
+ step["monitor"] = arg_pair[1] if arg_pair[0] == 'monitor' else step["monitor"]
169
+
170
+ if step["action"] not in ALL_ACTIONS:
171
+ raise AutoMonkeyNoAction(step["action"])
172
+
173
+ if step["target"] is None:
174
+ raise AutoMonkeyNoTarget(step["target"])
175
+ return step
176
+
177
+ def __run_1(step: dict):
178
+ step["target"] = _add_ext(step["target"])
179
+ _wait_for_target(step["target"], step["skip"])
180
+
181
+ bullseye = locateOnScreen(step["target"], confidence=step["confidence"])
182
+ bullseye = get_center(bullseye)
183
+ bullseye = diagonal_point(bullseye, step["h_offset"], step["v_offset"])
184
+ if step["offset"] not in ("", None):
185
+ globals()["_offset_clicks"](bullseye, step["target"], step["offset"], step["action"])
186
+ else:
187
+ globals()[step["action"]](bullseye)
188
+
189
+ def __run_control(step: dict):
190
+ if isinstance(step["target"], dict):
191
+ globals()[step["action"]](**step["target"])
192
+ elif isinstance(step["target"], (tuple, list)):
193
+ globals()[step["action"]](*step["target"])
194
+ else:
195
+ globals()[step["action"]](step["target"])
196
+
197
+ def __run_2(step: dict):
198
+ if step["action"] in CONTROL_ACTIONS:
199
+ __run_control(step)
200
+ elif step["action"] in ("keys2", "msoffice_replace"):
201
+ globals()[step["action"]](*step["target"])
202
+ elif step["action"] == "paste":
203
+ pastetext(paste())
204
+ elif step["action"] == "open_app":
205
+ __run_3(step)
206
+ else:
207
+ globals()[step["action"]](step["target"])
208
+
209
+ def __run_3(step: dict):
210
+ if step["target"].lower() in COMMON_APPS:
211
+ globals()[step["action"]](COMMON_APPS[step["target"].lower()])
212
+ else:
213
+ globals()[step["action"]](step["target"])
214
+
215
+ def __monitors():
216
+ monitors = {}
217
+ for _, mon in enumerate(sorted([(mon.x, mon.y) for mon in get_monitors()], key=lambda tup: tup[0])):
218
+ monitors[_] = (mon[0], mon[1])
219
+ return monitors
220
+
221
+ def __target_1(step: dict):
222
+ step["target"] = step["target"].split("+") if step["action"] in ("keys", "keys2") else step["target"]
223
+ return step
224
+
225
+ def __target_2(step: dict):
226
+ monitors = __monitors()
227
+ try:
228
+ if step["action"] in ("keys", "keys2") and isinstance(step["target"], tuple):
229
+ step["target"] = (step["target"][0] + monitors[step["monitor"] - 1][0], step["target"][1]) if step["target"][0] < monitors[1][0] else step["target"]
230
+ return step
231
+ except IndexError:
232
+ pass
233
+ except KeyError:
234
+ pass
235
+ return step
236
+
237
+ def __run_1_cond(step: dict):
238
+ return step["action"] in MOUSE_ACTIONS and not isinstance(step["target"], tuple) and not isinstance(step["target"], int)
239
+
240
+ def __debug_1(debug: bool, step: dict):
241
+ if debug:
242
+ print(step)
243
+
244
+ def chain(*steps: dict, debug=False):
245
+ """Chain together a series of automation steps
246
+
247
+ Args:
248
+ *steps (dict): Unlimitted number of automation steps as dictionaries.
249
+ Each automation step should be a dictionary with 1 or more pairs:
250
+ - First pair is the Action - Target pair. The only mandatory pair.
251
+ Example: dict(click: "image.jpg") or {"click": "image.jpg"}
252
+ - Next possible pairs are optional:
253
+ * skip (True/False) - optional. If True, the step will be skipped if the target is not found.
254
+ * wait - Seconds to wait after performing the action. Defaults to zero.
255
+ * confidence - optional. Used only for actions on images. Confidence on locating the image.
256
+ Defaults to 0.9
257
+ * v_offset - optional. Vertical offset from the center of the target.
258
+ * h_offset - optional. Horizontal offset from the center of the target.
259
+ * offset - optional. Offset from the center of the target. Overrides v_offset and h_offset.
260
+ * monitor - optional. Monitor number to perform the action on. Defaults to 1.
261
+
262
+ Example of steps:
263
+ chain(
264
+ dict(write="this string", wait=0.5),
265
+ dict(write="this other string"),
266
+ dict(click="C:\\Folder1\\Folder2\\image.jpg", wait=2, confidence=0.8),
267
+ debug=True)
268
+
269
+ debug (bool, optional): Debug variable, if True will print each step. Defaults to False.
270
+
271
+ Notes:
272
+ 1. To use the scroll functions you have to select the scrollable area first
273
+ 2. Horizontal scroll (left, right) is not supported on Windows
274
+ 3. write, keys and keys2 work with any keyboard layout and PowerToys key remaps on Windows,
275
+ and write types any character there (German, Chinese, ...). keys3/keys4 are not layout aware.
276
+ 4. startfile keeps the file opened only until the end of the chain.
277
+ If you want to keep the file opened you need to perform other operations on it.
278
+ 5. When using startfile you are responsible for saving and closing the file.
279
+ 6. For the app functions (start, close, minimize, maximize, restore, focus) you need to provide the title of the window.add()
280
+ You can also use regex to match the title.
281
+ 7. The control actions (click_control, type_in_control, ...) use pywinauto on Windows
282
+ and AT-SPI on Linux (see pywinauto_for_linux.md; macOS not implemented yet).
283
+ The target is a tuple of arguments (window, control, ...) or a dict of keyword arguments:
284
+ dict(click_control=("Notepad", "File")),
285
+ dict(type_in_control=dict(window="Notepad", control={"control_type": "Document"}, text="hi")),
286
+
287
+ """
288
+ for _ in steps:
289
+ step = _prepare_step(_)
290
+ __debug_1(debug, step)
291
+
292
+ step = __target_1(step)
293
+ step = __target_2(step)
294
+
295
+ if __run_1_cond(step):
296
+ __run_1(step)
297
+ else:
298
+ __run_2(step)
299
+
300
+ sleep(step["wait"])
@@ -0,0 +1,120 @@
1
+ IMG_EXT = (
2
+ ".png",
3
+ ".jpg",
4
+ ".jpeg",
5
+ ".tiff",
6
+ ".tif",
7
+ ".bmp",
8
+ ".gif",
9
+ ".pdf",
10
+ ".webp",
11
+ ".PNG",
12
+ ".JPG",
13
+ ".JPEG",
14
+ ".TIFF",
15
+ ".TIF",
16
+ ".BMP",
17
+ ".GIF",
18
+ ".PDF",
19
+ ".WEBP"
20
+ )
21
+
22
+ MOUSE_ACTIONS = (
23
+ "click",
24
+ "leftclick",
25
+ "rightclick",
26
+ "doubleclick",
27
+ "tripleclick",
28
+ "scrollup",
29
+ "scrolldown",
30
+ "scrollleft",
31
+ "scrollright",
32
+ "moveup",
33
+ "movedown",
34
+ "moveleft",
35
+ "moveright"
36
+ )
37
+
38
+ WAIT_ACTIONS = (
39
+ "waitwhile",
40
+ "waituntil",
41
+ )
42
+
43
+ KEYBOARD_ACTIONS = (
44
+ "write",
45
+ "pastetext",
46
+ "keys",
47
+ "keys2",
48
+ "keys3",
49
+ "keys4",
50
+ "copy",
51
+ "paste",
52
+ )
53
+
54
+ APPS_ACTIONS = (
55
+ "close",
56
+ "focus",
57
+ "open_app",
58
+ "startfile",
59
+ "minimize",
60
+ "maximize",
61
+ "restore",
62
+ "copy_from",
63
+ "copy_from_to",
64
+ "msoffice_replace",
65
+ )
66
+
67
+ IMG_ACTIONS = (
68
+ "count_img",
69
+ "get_text_from_region",
70
+ )
71
+
72
+ # pywinauto based actions. Target: window title, (window, control, ...) tuple or dict of arguments
73
+ CONTROL_ACTIONS = (
74
+ "start_app",
75
+ "click_control",
76
+ "double_click_control",
77
+ "right_click_control",
78
+ "type_in_control",
79
+ "set_control_text",
80
+ "get_control_text",
81
+ "select_menu",
82
+ "select_item",
83
+ "check_control",
84
+ "uncheck_control",
85
+ "wait_control",
86
+ "list_controls",
87
+ )
88
+
89
+ COMMON_APPS = {
90
+ "edge": "msedge",
91
+ "msedge": "msedge",
92
+ "microsoft edge": "msedge",
93
+ "chrome": "chrome",
94
+ "google chrome": "chrome",
95
+ "firefox": "firefox",
96
+ "mozilla firefox": "firefox",
97
+ "notepad": "notepad",
98
+ "notepad++": "notepad++",
99
+ "excel": "excel",
100
+ "microsoft excel": "excel",
101
+ "word": "word",
102
+ "microsoft word": "word",
103
+ "powerpoint": "powerpoint",
104
+ "microsoft powerpoint": "powerpoint",
105
+ "outlook": "outlook",
106
+ "microsoft outlook": "outlook",
107
+ "onenote": "onenote",
108
+ "microsoft onenote": "onenote",
109
+ "vscode": "code",
110
+ "visual studio code": "code",
111
+ "explorer": "explorer",
112
+ "windows explorer": "explorer",
113
+ "files": "explorer",
114
+ "file explorer": "explorer",
115
+ "cmd": "cmd",
116
+ "command prompt": "cmd",
117
+ "terminal": "cmd",
118
+ "powershell": "powershell",
119
+ "power shell": "powershell",
120
+ }
@@ -0,0 +1,16 @@
1
+ class AutoMonkeyNoAction(Exception):
2
+ """
3
+ AutoMonkey chain function will raise this Exception if no valid action exists
4
+ in an automation step of the chain sequence.
5
+ """
6
+ def __init__(self, message):
7
+ super().__init__(f"The provided action is not supported: {message}")
8
+
9
+
10
+ class AutoMonkeyNoTarget(Exception):
11
+ """
12
+ AutoMonkey chain function will raise this Exception if no valid target exists
13
+ in an automation step of the chain sequence.
14
+ """
15
+ def __init__(self, message):
16
+ super().__init__(f"The provided target is not supported: {message}")