MaaFw 4.4.0b1__py3-none-manylinux2014_x86_64.whl → 5.4.0__py3-none-manylinux2014_x86_64.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.

Potentially problematic release.


This version of MaaFw might be problematic. Click here for more details.

maa/pipeline.py ADDED
@@ -0,0 +1,509 @@
1
+ import json
2
+ from dataclasses import dataclass, field
3
+ from typing import Any, List, Optional, Tuple, Union, Dict
4
+ from strenum import StrEnum
5
+
6
+ # Type aliases to match C++ std::variant types
7
+ JRect = Tuple[int, int, int, int] # std::array<int, 4>
8
+ JTarget = Union[bool, str, JRect] # std::variant<bool, std::string, JRect>
9
+
10
+
11
+ # strenum
12
+ class JRecognitionType(StrEnum):
13
+ DirectHit = "DirectHit"
14
+ TemplateMatch = "TemplateMatch"
15
+ FeatureMatch = "FeatureMatch"
16
+ ColorMatch = "ColorMatch"
17
+ OCR = "OCR"
18
+ NeuralNetworkClassify = "NeuralNetworkClassify"
19
+ NeuralNetworkDetect = "NeuralNetworkDetect"
20
+ And = "And"
21
+ Or = "Or"
22
+ Custom = "Custom"
23
+
24
+
25
+ class JActionType(StrEnum):
26
+ DoNothing = "DoNothing"
27
+ Click = "Click"
28
+ LongPress = "LongPress"
29
+ Swipe = "Swipe"
30
+ MultiSwipe = "MultiSwipe"
31
+ TouchDown = "TouchDown"
32
+ TouchMove = "TouchMove"
33
+ TouchUp = "TouchUp"
34
+ ClickKey = "ClickKey"
35
+ LongPressKey = "LongPressKey"
36
+ KeyDown = "KeyDown"
37
+ KeyUp = "KeyUp"
38
+ InputText = "InputText"
39
+ StartApp = "StartApp"
40
+ StopApp = "StopApp"
41
+ StopTask = "StopTask"
42
+ Scroll = "Scroll"
43
+ Command = "Command"
44
+ Shell = "Shell"
45
+ Custom = "Custom"
46
+
47
+
48
+ # Recognition parameter dataclasses (matching C++ JRecognitionParam variants)
49
+ @dataclass
50
+ class JDirectHit:
51
+ roi: JTarget = (0, 0, 0, 0)
52
+ roi_offset: JRect = (0, 0, 0, 0)
53
+
54
+
55
+ @dataclass
56
+ class JTemplateMatch:
57
+ template: List[str] # 必选
58
+ roi: JTarget = (0, 0, 0, 0)
59
+ roi_offset: JRect = (0, 0, 0, 0)
60
+ threshold: List[float] = field(default_factory=lambda: [0.7])
61
+ order_by: str = "Horizontal"
62
+ index: int = 0
63
+ method: int = 5
64
+ green_mask: bool = False
65
+
66
+
67
+ @dataclass
68
+ class JFeatureMatch:
69
+ template: List[str] # 必选
70
+ roi: JTarget = (0, 0, 0, 0)
71
+ roi_offset: JRect = (0, 0, 0, 0)
72
+ detector: str = "SIFT"
73
+ order_by: str = "Horizontal"
74
+ count: int = 4
75
+ index: int = 0
76
+ green_mask: bool = False
77
+ ratio: float = 0.6
78
+
79
+
80
+ @dataclass
81
+ class JColorMatch:
82
+ lower: List[List[int]] # 必选
83
+ upper: List[List[int]] # 必选
84
+ roi: JTarget = (0, 0, 0, 0)
85
+ roi_offset: JRect = (0, 0, 0, 0)
86
+ order_by: str = "Horizontal"
87
+ method: int = 4 # RGB
88
+ count: int = 1
89
+ index: int = 0
90
+ connected: bool = False
91
+
92
+
93
+ @dataclass
94
+ class JOCR:
95
+ expected: List[str] = field(default_factory=list)
96
+ roi: JTarget = (0, 0, 0, 0)
97
+ roi_offset: JRect = (0, 0, 0, 0)
98
+ threshold: float = 0.3
99
+ replace: List[List[str]] = field(default_factory=list)
100
+ order_by: str = "Horizontal"
101
+ index: int = 0
102
+ only_rec: bool = False
103
+ model: str = ""
104
+
105
+
106
+ @dataclass
107
+ class JNeuralNetworkClassify:
108
+ model: str # 必选
109
+ expected: List[int] = field(default_factory=list)
110
+ roi: JTarget = (0, 0, 0, 0)
111
+ roi_offset: JRect = (0, 0, 0, 0)
112
+ labels: List[str] = field(default_factory=list)
113
+ order_by: str = "Horizontal"
114
+ index: int = 0
115
+
116
+
117
+ @dataclass
118
+ class JNeuralNetworkDetect:
119
+ model: str # 必选
120
+ expected: List[int] = field(default_factory=list)
121
+ roi: JTarget = (0, 0, 0, 0)
122
+ roi_offset: JRect = (0, 0, 0, 0)
123
+ labels: List[str] = field(default_factory=list)
124
+ threshold: List[float] = field(default_factory=lambda: [0.3])
125
+ order_by: str = "Horizontal"
126
+ index: int = 0
127
+
128
+
129
+ @dataclass
130
+ class JCustomRecognition:
131
+ custom_recognition: str # 必选
132
+ roi: JTarget = (0, 0, 0, 0)
133
+ roi_offset: JRect = (0, 0, 0, 0)
134
+ custom_recognition_param: Any = None
135
+
136
+
137
+ @dataclass
138
+ class JAnd:
139
+ all_of: List[Any] = field(default_factory=list)
140
+ box_index: int = 0
141
+
142
+
143
+ @dataclass
144
+ class JOr:
145
+ any_of: List[Any] = field(default_factory=list)
146
+
147
+
148
+ # Recognition parameter union type
149
+ JRecognitionParam = Union[
150
+ JDirectHit,
151
+ JTemplateMatch,
152
+ JFeatureMatch,
153
+ JColorMatch,
154
+ JOCR,
155
+ JNeuralNetworkClassify,
156
+ JNeuralNetworkDetect,
157
+ JAnd,
158
+ JOr,
159
+ JCustomRecognition,
160
+ ]
161
+
162
+
163
+ # Action parameter dataclasses (matching C++ JActionParam variants)
164
+ @dataclass
165
+ class JDoNothing:
166
+ pass
167
+
168
+
169
+ @dataclass
170
+ class JClick:
171
+ target: JTarget = True
172
+ target_offset: JRect = (0, 0, 0, 0)
173
+ contact: int = 0
174
+ pressure: int = 1
175
+
176
+
177
+ @dataclass
178
+ class JLongPress:
179
+ target: JTarget = True
180
+ target_offset: JRect = (0, 0, 0, 0)
181
+ duration: int = 1000
182
+ contact: int = 0
183
+ pressure: int = 1
184
+
185
+
186
+ @dataclass
187
+ class JSwipe:
188
+ starting: int = 0 # MultiSwipe 中使用
189
+ begin: JTarget = True
190
+ begin_offset: JRect = (0, 0, 0, 0)
191
+ end: List[JTarget] = field(default_factory=lambda: [True])
192
+ end_offset: List[JRect] = field(default_factory=lambda: [(0, 0, 0, 0)])
193
+ end_hold: List[int] = field(default_factory=lambda: [0])
194
+ duration: List[int] = field(default_factory=lambda: [200])
195
+ only_hover: bool = False
196
+ contact: int = 0
197
+ pressure: int = 1
198
+
199
+
200
+ @dataclass
201
+ class JMultiSwipe:
202
+ swipes: List[JSwipe]
203
+
204
+
205
+ @dataclass
206
+ class JTouch:
207
+ contact: int = 0
208
+ target: JTarget = True
209
+ target_offset: JRect = (0, 0, 0, 0)
210
+ pressure: int = 0
211
+
212
+
213
+ @dataclass
214
+ class JTouchUp:
215
+ contact: int = 0
216
+
217
+
218
+ @dataclass
219
+ class JClickKey:
220
+ key: List[int]
221
+
222
+
223
+ @dataclass
224
+ class JLongPressKey:
225
+ key: List[int] # 必选
226
+ duration: int = 1000
227
+
228
+
229
+ @dataclass
230
+ class JKey:
231
+ key: int
232
+
233
+
234
+ @dataclass
235
+ class JInputText:
236
+ input_text: str
237
+
238
+
239
+ @dataclass
240
+ class JStartApp:
241
+ package: str
242
+
243
+
244
+ @dataclass
245
+ class JStopApp:
246
+ package: str
247
+
248
+
249
+ @dataclass
250
+ class JStopTask:
251
+ pass
252
+
253
+
254
+ @dataclass
255
+ class JScroll:
256
+ dx: int = 0
257
+ dy: int = 0
258
+
259
+
260
+ @dataclass
261
+ class JCommand:
262
+ exec: str # 必选
263
+ args: List[str] = field(default_factory=list)
264
+ detach: bool = False
265
+
266
+
267
+ @dataclass
268
+ class JShell:
269
+ cmd: str # 必选
270
+ timeout: int = 20000
271
+
272
+
273
+ @dataclass
274
+ class JCustomAction:
275
+ custom_action: str # 必选
276
+ target: JTarget = True
277
+ custom_action_param: Any = None
278
+ target_offset: JRect = (0, 0, 0, 0)
279
+
280
+
281
+ # Action parameter union type
282
+ JActionParam = Union[
283
+ JDoNothing,
284
+ JClick,
285
+ JLongPress,
286
+ JSwipe,
287
+ JMultiSwipe,
288
+ JTouch,
289
+ JTouchUp,
290
+ JClickKey,
291
+ JLongPressKey,
292
+ JKey,
293
+ JInputText,
294
+ JStartApp,
295
+ JStopApp,
296
+ JStopTask,
297
+ JScroll,
298
+ JCommand,
299
+ JShell,
300
+ JCustomAction,
301
+ ]
302
+
303
+
304
+ # Main pipeline dataclasses
305
+ @dataclass
306
+ class JRecognition:
307
+ type: JRecognitionType
308
+ param: JRecognitionParam
309
+
310
+
311
+ @dataclass
312
+ class JAction:
313
+ type: JActionType
314
+ param: JActionParam
315
+
316
+
317
+ @dataclass
318
+ class JNodeAttr:
319
+ name: str # 必选
320
+ jump_back: bool = False
321
+ anchor: bool = False
322
+
323
+
324
+ @dataclass
325
+ class JWaitFreezes:
326
+ time: int = 1
327
+ target: JTarget = True
328
+ target_offset: JRect = (0, 0, 0, 0)
329
+ threshold: float = 0.95
330
+ method: int = 5
331
+ rate_limit: int = 1000
332
+ timeout: int = 20000
333
+
334
+
335
+ @dataclass
336
+ class JPipelineData:
337
+ recognition: JRecognition # 必选
338
+ action: JAction # 必选
339
+ next: List[JNodeAttr] = field(default_factory=list)
340
+ rate_limit: int = 1000
341
+ timeout: int = 20000
342
+ on_error: List[JNodeAttr] = field(default_factory=list)
343
+ anchor: List[str] = field(default_factory=list)
344
+ inverse: bool = False
345
+ enabled: bool = True
346
+ pre_delay: int = 200
347
+ post_delay: int = 200
348
+ pre_wait_freezes: Optional[JWaitFreezes] = None
349
+ post_wait_freezes: Optional[JWaitFreezes] = None
350
+ repeat: int = 1
351
+ repeat_delay: int = 0
352
+ repeat_wait_freezes: Optional[JWaitFreezes] = None
353
+ max_hit: int = 4294967295 # UINT_MAX
354
+ focus: Any = None
355
+ attach: Dict = field(default_factory=dict)
356
+
357
+
358
+ class JPipelineParser:
359
+ @staticmethod
360
+ def _parse_wait_freezes(data: dict) -> JWaitFreezes:
361
+ """Convert wait freezes with proper defaults"""
362
+ return JWaitFreezes(
363
+ time=data.get("time"),
364
+ target=data.get("target"), # type: ignore
365
+ target_offset=data.get("target_offset"), # type: ignore
366
+ threshold=data.get("threshold"),
367
+ method=data.get("method"),
368
+ rate_limit=data.get("rate_limit"),
369
+ timeout=data.get("timeout"),
370
+ )
371
+
372
+ @classmethod
373
+ def _parse_param(
374
+ cls,
375
+ param_type: Union[JRecognitionType, JActionType],
376
+ param_data: dict,
377
+ param_type_map: dict,
378
+ ) -> Union[JRecognitionParam, JActionParam]:
379
+ """Generic function to parse parameters based on type map."""
380
+ param_class = param_type_map.get(param_type)
381
+ if not param_class:
382
+ raise ValueError(f"Unknown type: {param_type}")
383
+
384
+ try:
385
+ return param_class(**param_data)
386
+ except TypeError as e:
387
+ print(
388
+ f"Warning: Failed to create {param_class.__name__} with data {param_data}: {e}"
389
+ )
390
+ return param_class()
391
+
392
+ @classmethod
393
+ def _parse_recognition_param(
394
+ cls, param_type: JRecognitionType, param_data: dict
395
+ ) -> JRecognitionParam:
396
+ """Convert dict to appropriate JRecognitionParam variant based on type."""
397
+ param_type_map = {
398
+ JRecognitionType.DirectHit: JDirectHit,
399
+ JRecognitionType.TemplateMatch: JTemplateMatch,
400
+ JRecognitionType.FeatureMatch: JFeatureMatch,
401
+ JRecognitionType.ColorMatch: JColorMatch,
402
+ JRecognitionType.OCR: JOCR,
403
+ JRecognitionType.NeuralNetworkClassify: JNeuralNetworkClassify,
404
+ JRecognitionType.NeuralNetworkDetect: JNeuralNetworkDetect,
405
+ JRecognitionType.And: JAnd,
406
+ JRecognitionType.Or: JOr,
407
+ JRecognitionType.Custom: JCustomRecognition,
408
+ }
409
+ return cls._parse_param(param_type, param_data, param_type_map)
410
+
411
+ @classmethod
412
+ def _parse_action_param(
413
+ cls, param_type: JActionType, param_data: dict
414
+ ) -> JActionParam:
415
+ """Convert dict to appropriate JActionParam variant based on type."""
416
+ param_type_map = {
417
+ JActionType.DoNothing: JDoNothing,
418
+ JActionType.Click: JClick,
419
+ JActionType.LongPress: JLongPress,
420
+ JActionType.Swipe: JSwipe,
421
+ JActionType.MultiSwipe: JMultiSwipe,
422
+ JActionType.TouchDown: JTouch,
423
+ JActionType.TouchMove: JTouch,
424
+ JActionType.TouchUp: JTouchUp,
425
+ JActionType.ClickKey: JClickKey,
426
+ JActionType.LongPressKey: JLongPressKey,
427
+ JActionType.KeyDown: JKey,
428
+ JActionType.KeyUp: JKey,
429
+ JActionType.InputText: JInputText,
430
+ JActionType.StartApp: JStartApp,
431
+ JActionType.StopApp: JStopApp,
432
+ JActionType.StopTask: JStopTask,
433
+ JActionType.Scroll: JScroll,
434
+ JActionType.Command: JCommand,
435
+ JActionType.Shell: JShell,
436
+ JActionType.Custom: JCustomAction,
437
+ }
438
+
439
+ return cls._parse_param(param_type, param_data, param_type_map)
440
+
441
+ @classmethod
442
+ def parse_pipeline_data(cls, pipeline_data: Union[str, Dict]) -> JPipelineData:
443
+ """Parse JSON string to JPipelineData dataclass with proper variant types."""
444
+ if isinstance(pipeline_data, dict):
445
+ data = pipeline_data
446
+ elif isinstance(pipeline_data, str):
447
+ try:
448
+ data: dict = json.loads(pipeline_data)
449
+ except json.JSONDecodeError as e:
450
+ raise ValueError(f"Invalid JSON format: {e}")
451
+ else:
452
+ raise TypeError("Input must be a JSON string or a dict.")
453
+
454
+ # Convert recognition
455
+ recognition_data: dict = data.get("recognition")
456
+ recognition_type: JRecognitionType = JRecognitionType(
457
+ recognition_data.get("type")
458
+ )
459
+ recognition_param_data: dict = recognition_data.get("param")
460
+ recognition_param = cls._parse_recognition_param(
461
+ recognition_type, recognition_param_data
462
+ )
463
+ recognition = JRecognition(type=recognition_type, param=recognition_param)
464
+
465
+ # Convert action
466
+ action_data: dict = data.get("action")
467
+ action_type: JActionType = JActionType(action_data.get("type"))
468
+ action_param_data = action_data.get("param")
469
+ action_param = cls._parse_action_param(action_type, action_param_data)
470
+ action = JAction(type=action_type, param=action_param)
471
+
472
+ pre_wait_freezes = cls._parse_wait_freezes(data.get("pre_wait_freezes")) # type: ignore
473
+ post_wait_freezes = cls._parse_wait_freezes(data.get("post_wait_freezes")) # type: ignore
474
+ repeat_wait_freezes = cls._parse_wait_freezes(data.get("repeat_wait_freezes")) # type: ignore
475
+
476
+ # Create JPipelineData with converted data
477
+ return JPipelineData(
478
+ recognition=recognition,
479
+ action=action,
480
+ next=cls._parse_node_attr_list(data.get("next")),
481
+ rate_limit=data.get("rate_limit"),
482
+ timeout=data.get("timeout"),
483
+ on_error=cls._parse_node_attr_list(data.get("on_error")),
484
+ anchor=data.get("anchor"),
485
+ inverse=data.get("inverse"),
486
+ enabled=data.get("enabled"),
487
+ pre_delay=data.get("pre_delay"),
488
+ post_delay=data.get("post_delay"),
489
+ pre_wait_freezes=pre_wait_freezes, # type: ignore
490
+ post_wait_freezes=post_wait_freezes, # type: ignore
491
+ repeat=data.get("repeat"),
492
+ repeat_delay=data.get("repeat_delay"),
493
+ repeat_wait_freezes=repeat_wait_freezes, # type: ignore
494
+ max_hit=data.get("max_hit"),
495
+ focus=data.get("focus"),
496
+ attach=data.get("attach"),
497
+ )
498
+
499
+ @staticmethod
500
+ def _parse_node_attr_list(data: List[dict]) -> List[JNodeAttr]:
501
+ """Convert list of dicts to list of JNodeAttr."""
502
+ return [
503
+ JNodeAttr(
504
+ name=item.get("name"),
505
+ jump_back=item.get("jump_back", False),
506
+ anchor=item.get("anchor", False),
507
+ )
508
+ for item in data
509
+ ]