ksaa 2025.5.23.3__py3-none-any.whl → 2025.6.23.0__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.
Files changed (202) hide show
  1. kotonebot/backend/bot.py +3 -3
  2. kotonebot/backend/context/context.py +52 -36
  3. kotonebot/backend/debug/server.py +3 -5
  4. kotonebot/backend/dispatch.py +1 -17
  5. kotonebot/backend/flow_controller.py +195 -0
  6. kotonebot/backend/loop.py +277 -0
  7. kotonebot/client/__init__.py +4 -1
  8. kotonebot/client/device.py +23 -21
  9. kotonebot/client/host/__init__.py +2 -1
  10. kotonebot/client/host/custom.py +26 -3
  11. kotonebot/client/host/leidian_host.py +22 -11
  12. kotonebot/client/host/mumu12_host.py +25 -3
  13. kotonebot/client/host/protocol.py +40 -18
  14. kotonebot/client/implements/__init__.py +6 -0
  15. kotonebot/client/implements/adb.py +67 -7
  16. kotonebot/client/implements/adb_raw.py +12 -5
  17. kotonebot/client/implements/remote_windows.py +20 -18
  18. kotonebot/client/implements/uiautomator2.py +8 -0
  19. kotonebot/client/implements/windows.py +56 -28
  20. kotonebot/client/protocol.py +13 -0
  21. kotonebot/client/registration.py +89 -0
  22. kotonebot/config/base_config.py +5 -1
  23. kotonebot/debug_entry.py +7 -2
  24. kotonebot/kaa/clear_logs.py +0 -3
  25. kotonebot/kaa/common.py +21 -0
  26. kotonebot/kaa/game_ui/common.py +4 -7
  27. kotonebot/kaa/game_ui/dialog.py +24 -4
  28. kotonebot/kaa/game_ui/idols_overview.py +28 -12
  29. kotonebot/kaa/game_ui/primary_button.py +55 -0
  30. kotonebot/kaa/game_ui/scrollable.py +0 -5
  31. kotonebot/kaa/main/cli.py +9 -4
  32. kotonebot/kaa/main/dmm_host.py +33 -7
  33. kotonebot/kaa/main/gr.py +156 -26
  34. kotonebot/kaa/main/kaa.py +113 -41
  35. kotonebot/kaa/metadata.py +27 -0
  36. kotonebot/kaa/resources/__pycache__/__init__.cpython-310.pyc +0 -0
  37. kotonebot/kaa/resources/game.db +0 -0
  38. kotonebot/kaa/resources/game_ver.txt +0 -0
  39. kotonebot/kaa/resources/idol_cards/i_card-skin-kllj-3-012_0.png +0 -0
  40. kotonebot/kaa/resources/idol_cards/i_card-skin-kllj-3-012_1.png +0 -0
  41. kotonebot/kaa/resources/idol_cards/i_card-skin-ssmk-3-012_0.png +0 -0
  42. kotonebot/kaa/resources/idol_cards/i_card-skin-ssmk-3-012_1.png +0 -0
  43. kotonebot/kaa/sprites/24e99232-9434-457f-a9a0-69dd7ecf675f.png +0 -0
  44. kotonebot/kaa/sprites/2ededcf5-1d80-4e2a-9c83-2a31998331ce.png +0 -0
  45. kotonebot/kaa/sprites/3b473fe6-e147-477f-b088-9b8fb042a4f6.png +0 -0
  46. kotonebot/kaa/sprites/55f7db71-0a18-4b3d-b847-57959b8d2e32.png +0 -0
  47. kotonebot/kaa/sprites/6cd80be8-c9b3-4ba5-bf17-3ffc9b000743.png +0 -0
  48. kotonebot/kaa/sprites/74ec3510-583d-4a76-ac69-38480fbf1387.png +0 -0
  49. kotonebot/kaa/sprites/a0bd6a5f-784d-4f0a-9d66-10f4b80c8d3e.png +0 -0
  50. kotonebot/kaa/sprites/d3424d31-0502-4623-996e-f0194e5085ce.png +0 -0
  51. kotonebot/kaa/sprites/e6b45405-cd9f-4c6e-a9f1-6ec953747c65.png +0 -0
  52. kotonebot/kaa/sprites/f5c16d2f-ebc5-4617-9b96-971696af7c52.png +0 -0
  53. kotonebot/kaa/tasks/R.py +157 -135
  54. kotonebot/kaa/tasks/end_game.py +14 -0
  55. kotonebot/kaa/tasks/produce/common.py +1 -0
  56. kotonebot/kaa/tasks/produce/in_purodyuusu.py +0 -4
  57. kotonebot/kaa/tasks/produce/non_lesson_actions.py +1 -4
  58. kotonebot/kaa/tasks/produce/produce.py +119 -70
  59. kotonebot/kaa/tasks/start_game.py +56 -7
  60. kotonebot/kaa/util/paths.py +6 -1
  61. kotonebot/tools/mirror.py +23 -13
  62. kotonebot/util.py +32 -0
  63. {ksaa-2025.5.23.3.dist-info → ksaa-2025.6.23.0.dist-info}/METADATA +38 -1
  64. {ksaa-2025.5.23.3.dist-info → ksaa-2025.6.23.0.dist-info}/RECORD +200 -183
  65. ksaa-2025.6.23.0.dist-info/licenses/LICENSE +674 -0
  66. kotonebot/client/factory.py +0 -92
  67. kotonebot/kaa/sprites/b260d390-2b1a-4974-92db-d68ff7b5b8e6.png +0 -0
  68. /kotonebot/kaa/sprites/{f6d36222-847e-4300-8f90-c98c7a8f36e3.png → 0235ae3d-1741-4a81-8053-f60bc395ee85.png} +0 -0
  69. /kotonebot/kaa/sprites/{48a02c33-b7af-4ab5-bda1-3a2ec62a7076.png → 038c69e6-7cb0-4c8d-99da-b537e48a86b7.png} +0 -0
  70. /kotonebot/kaa/sprites/{b99f0684-8200-4080-9b54-808c4ec12c99.png → 080b3cf5-ce35-4382-9bfa-d6a12efb09b3.png} +0 -0
  71. /kotonebot/kaa/sprites/{7c65fc91-602b-4d1b-82f9-c9b561a55d9b.png → 09fb5e41-6caa-4246-a415-8317b12d8d81.png} +0 -0
  72. /kotonebot/kaa/sprites/{5e74dc61-0ad5-4e57-b331-e3d2ed67b6d2.png → 0a6c6b1e-7a80-428e-a483-1858bfb8ac59.png} +0 -0
  73. /kotonebot/kaa/sprites/{aad29ac0-b9ad-4c38-93f5-e8036ad7b96b.png → 0ad2f603-a3ad-4a92-a398-c411145fd8e9.png} +0 -0
  74. /kotonebot/kaa/sprites/{e98037f8-3b4a-4de8-adc1-ca0f736b6c98.png → 0d4b669e-5e43-4458-b65a-5565797335ae.png} +0 -0
  75. /kotonebot/kaa/sprites/{382bc600-72f6-433f-809e-9fe2fdb31b32.png → 0fbcb3f6-9ca0-43c7-975b-e7c15c26c897.png} +0 -0
  76. /kotonebot/kaa/sprites/{6d39d7a2-0b1f-4f49-9ab8-062bca9b5470.png → 15465879-eb84-4ca6-ae0f-d3c5ac71e723.png} +0 -0
  77. /kotonebot/kaa/sprites/{474ce25b-8a3d-435e-8d7a-ab6566ae61a0.png → 1639278d-67e0-4452-a2b5-ec4cf4011f42.png} +0 -0
  78. /kotonebot/kaa/sprites/{7d4f53fb-02bc-4139-b32c-3345a5f92a79.png → 17414e9e-b046-49e0-9596-cad1f4673a9e.png} +0 -0
  79. /kotonebot/kaa/sprites/{9a72b63e-8bd1-47dc-8298-cdf02220201e.png → 17a8b0a5-5b71-4612-8a68-16c51f03c71b.png} +0 -0
  80. /kotonebot/kaa/sprites/{a1610e9f-93f0-4ef2-b32a-b2effd1673e8.png → 1aaceb21-dc5c-4a58-96d3-d2c66b2fb357.png} +0 -0
  81. /kotonebot/kaa/sprites/{237636f2-aec0-40be-b7bb-e08607a89307.png → 1aae0ab3-f619-47e0-8bf6-3d3791f7d445.png} +0 -0
  82. /kotonebot/kaa/sprites/{872c37e7-e596-4449-86f4-5ba65836a7a7.png → 1b6be6dd-2711-441a-9fa6-3281a6ad10c7.png} +0 -0
  83. /kotonebot/kaa/sprites/{a829f038-5421-4cc4-a494-105a973351e2.png → 1bb00a74-2fdc-4973-84ae-dc1edee15f81.png} +0 -0
  84. /kotonebot/kaa/sprites/{e5922ce7-9aa7-4806-acdd-9c35408a0f37.png → 1ccdcacf-4f7d-442c-8e2f-09aa7fe14e56.png} +0 -0
  85. /kotonebot/kaa/sprites/{1df6ff4b-17f8-4ee6-8df0-0b53e26b7f1e.png → 1d5f7454-672d-4e87-a368-ec4bb32f9b1f.png} +0 -0
  86. /kotonebot/kaa/sprites/{51f38e84-56c8-4c80-9a67-35196ef54b89.png → 1f2981d8-5eb6-4332-aa9b-17f4db2d4163.png} +0 -0
  87. /kotonebot/kaa/sprites/{f4b226eb-ed9e-4e5e-9ba0-1b777e401aa3.png → 25910d8e-d57f-409f-b0be-ee68c5af4920.png} +0 -0
  88. /kotonebot/kaa/sprites/{e9e399f7-4e2f-4392-bb8f-850c4a272dfa.png → 264e056f-3bcc-4b16-94d4-f767c78ab6f5.png} +0 -0
  89. /kotonebot/kaa/sprites/{cc0ac4cf-01fd-48f9-8707-2d6a2aa0ba8c.png → 26580772-ff20-4dcf-8496-f939e0f2890d.png} +0 -0
  90. /kotonebot/kaa/sprites/{7dd31493-9f3b-4382-a43a-f8eb1c745ad8.png → 29cb7b0f-5684-4516-8fae-4439f7f28e08.png} +0 -0
  91. /kotonebot/kaa/sprites/{415c45a8-9b71-4e0b-8cd2-f80a0a4aafb7.png → 2d145ade-b926-4276-b872-baec4ef78308.png} +0 -0
  92. /kotonebot/kaa/sprites/{d2732870-dd6b-4b7b-b21e-4ff2a4958b32.png → 2dd34393-ee6f-4b19-8a9c-76556d1bd7d7.png} +0 -0
  93. /kotonebot/kaa/sprites/{0d1918db-e000-4452-8490-ace623127e76.png → 2e00ac54-d23a-4d51-82f2-829c012552a6.png} +0 -0
  94. /kotonebot/kaa/sprites/{267fcd93-6a5c-4954-a06b-e97f327e9141.png → 2f74b04f-1992-4584-8dc6-e789b081ddc3.png} +0 -0
  95. /kotonebot/kaa/sprites/{08c2352f-0150-453a-aa0e-948dd0deef49.png → 302fde7d-de85-44c7-ba57-fe4b5f6db5fa.png} +0 -0
  96. /kotonebot/kaa/sprites/{e508161a-e595-4daf-a364-625aaf67e481.png → 3cc9685d-6347-4114-9d39-7683c2dc9df6.png} +0 -0
  97. /kotonebot/kaa/sprites/{d0c085fa-3089-4adf-979c-d4b274b32c3b.png → 42d8b5dc-ef27-4411-b8b5-d981e562990b.png} +0 -0
  98. /kotonebot/kaa/sprites/{2ef87b7f-d69e-4da2-878f-1cc35e48b15c.png → 448ea37d-a10d-4102-ac9f-a58403ee8914.png} +0 -0
  99. /kotonebot/kaa/sprites/{6dcc0bb3-984f-4c92-b0a3-74281d684182.png → 4a310bdd-0a7d-4669-9d57-6ae69bc8f022.png} +0 -0
  100. /kotonebot/kaa/sprites/{52e2b832-7837-47ca-a089-1f1593a99b08.png → 4b20b364-5f5f-4b34-960e-b0cfd08b1662.png} +0 -0
  101. /kotonebot/kaa/sprites/{34ec068a-d6fd-47ac-bd2f-0c31f93c19f3.png → 4e8f3e59-edfa-4523-939d-bc46988fc909.png} +0 -0
  102. /kotonebot/kaa/sprites/{157cfa99-d40c-48c2-8cf4-70835535522f.png → 4eaa5db2-d624-4df4-b2e4-01f72755f1a4.png} +0 -0
  103. /kotonebot/kaa/sprites/{56a70bc5-bd16-493b-b5b6-770f42f13de5.png → 5152e3fa-079c-4592-8df7-656101694df8.png} +0 -0
  104. /kotonebot/kaa/sprites/{b5009ffd-7e53-41ae-b78d-918ba0e7eda4.png → 52f7c8b1-af88-43c5-a999-de4da3caa03a.png} +0 -0
  105. /kotonebot/kaa/sprites/{465f1030-e538-4b22-a6f7-0087287b2e12.png → 534d1310-3503-46d3-8ecd-426d9bcf183e.png} +0 -0
  106. /kotonebot/kaa/sprites/{465d4e1a-c5f9-41b7-aa31-ce26d5c69c45.png → 5868ebfb-5e61-4ac6-b270-acb1e89b738f.png} +0 -0
  107. /kotonebot/kaa/sprites/{3e10014e-2063-424c-aeda-df985d351c12.png → 58a5f51d-d1a3-480b-8dde-ecead34d9f02.png} +0 -0
  108. /kotonebot/kaa/sprites/{4ffd7c97-86dd-4171-92e7-8e8e83e8f394.png → 5d3d61e9-bf13-4668-b0c7-fed730ea2ed6.png} +0 -0
  109. /kotonebot/kaa/sprites/{f4a1201f-9305-4ec5-8962-2bef0144ab46.png → 5dd0cc39-4102-4f0f-8ad3-342bbf825977.png} +0 -0
  110. /kotonebot/kaa/sprites/{dc17dea0-7749-4ecb-bbc0-1a52fb992dcf.png → 5ee8e9a8-b133-4ab7-a52c-e7e4e1d1ce7b.png} +0 -0
  111. /kotonebot/kaa/sprites/{172318e0-5e7b-4b61-9a17-6e2e66b27e69.png → 60be53a6-98e9-4fc6-9285-85506dc0e335.png} +0 -0
  112. /kotonebot/kaa/sprites/{8da0b45e-3d90-40f0-9781-c1ff4cd0e060.png → 6114d53e-dd69-4cee-82a7-7e43b338ff8d.png} +0 -0
  113. /kotonebot/kaa/sprites/{26433c9e-0ea2-4da4-9207-8f3fbad04235.png → 6156116e-56dd-4b41-a6de-ccec55dab7c8.png} +0 -0
  114. /kotonebot/kaa/sprites/{bdb09790-f702-4af2-ab67-0b3f05fd93ca.png → 65d2a5e5-48aa-4390-bb87-bfd8e044e4f6.png} +0 -0
  115. /kotonebot/kaa/sprites/{5a4a763f-b69b-4405-994d-470a78476f67.png → 66172bc0-3869-4a48-a26d-0dc3fa404ab8.png} +0 -0
  116. /kotonebot/kaa/sprites/{e0a5c772-49d3-468a-bcf5-0e3f24365253.png → 66678caa-7b25-4ff7-8c3a-017f67279349.png} +0 -0
  117. /kotonebot/kaa/sprites/{7f0783fe-89d9-4405-b7a3-6334ba543826.png → 68838f92-5916-424e-a37e-47e0b3834eb3.png} +0 -0
  118. /kotonebot/kaa/sprites/{c4fed66b-e195-4288-8b1f-ef4b0ef35174.png → 694d3a07-9697-4bda-984d-967779d530f7.png} +0 -0
  119. /kotonebot/kaa/sprites/{37e3a757-bcbc-4a37-bbf9-a38162a8627e.png → 69b022a8-e42f-4985-bd16-b4a530ad8a20.png} +0 -0
  120. /kotonebot/kaa/sprites/{5eddc669-dff6-462c-a495-81ddf951429e.png → 6aa34982-e7ab-4001-808f-8c5cf370d087.png} +0 -0
  121. /kotonebot/kaa/sprites/{428f770b-e149-417e-bb8a-38c1cba024e1.png → 7516c9a1-87ec-4d2f-ac5e-40d1aec27104.png} +0 -0
  122. /kotonebot/kaa/sprites/{63bfee9f-546e-4c3c-a82f-c2707bc62b90.png → 7aa2435c-da90-4d52-b831-88dbec57dde9.png} +0 -0
  123. /kotonebot/kaa/sprites/{d3b97b8f-f13a-413d-a097-5c456b8aea0d.png → 7b7dad18-a838-4267-b3c7-6ab43b8361f0.png} +0 -0
  124. /kotonebot/kaa/sprites/{ef539419-d100-43d5-83a1-14e65b85d816.png → 7cabdf91-2f15-4991-b165-eb9cdd557af6.png} +0 -0
  125. /kotonebot/kaa/sprites/{12356f5d-4188-4aa5-abb3-d219ca0167ca.png → 7f6cb60e-6ab3-4db4-86e1-8558250062a4.png} +0 -0
  126. /kotonebot/kaa/sprites/{1d6d5160-9525-42bf-a8d4-43c347d213eb.png → 83acbca8-3567-4262-a1e7-50f158348d45.png} +0 -0
  127. /kotonebot/kaa/sprites/{4b121018-f5c8-4c31-a5d9-48947982b191.png → 83cf48ba-d1af-4674-a52e-6bb80a626db7.png} +0 -0
  128. /kotonebot/kaa/sprites/{76c07ba7-afea-405c-806a-7691ce73f83a.png → 83f17b06-2792-4035-bc90-1800279ae131.png} +0 -0
  129. /kotonebot/kaa/sprites/{c287899b-b9a3-4ee2-a8fd-ebda89099b4b.png → 84bb4986-0295-4a45-9486-86e31fcdf1f1.png} +0 -0
  130. /kotonebot/kaa/sprites/{7b46a3e5-18d3-45a0-9500-1d7c854a12bb.png → 8b9c115b-d4ce-492e-88cb-08bd01c43570.png} +0 -0
  131. /kotonebot/kaa/sprites/{263708c5-0177-4c6a-8df3-611ec314fa56.png → 8e48af2b-c083-4480-a27c-c59c31a7ede1.png} +0 -0
  132. /kotonebot/kaa/sprites/{ced345fa-b375-457d-8b72-802424512e6c.png → 8f1e5644-1079-44b1-9d44-97bae3817bc5.png} +0 -0
  133. /kotonebot/kaa/sprites/{c94488b2-0ff0-4467-bbb0-405d3a2c55a3.png → 90def878-cd6e-46e5-9dc4-9c92dd904b80.png} +0 -0
  134. /kotonebot/kaa/sprites/{a69febc4-2d01-4628-be92-29b421604c4d.png → 920a3974-4875-4d2b-bbac-caa175c03ce0.png} +0 -0
  135. /kotonebot/kaa/sprites/{ddb2e9ee-e16a-4745-8598-b3f9c2f011be.png → 94433cf2-dd04-4b74-ae14-e5e3d945f579.png} +0 -0
  136. /kotonebot/kaa/sprites/{4ddc656f-ef7e-4580-ad12-9e382ea76e4f.png → 969eb98c-f761-4007-b8bd-2b3fb8d1f08c.png} +0 -0
  137. /kotonebot/kaa/sprites/{7d968a7f-f4a8-40a0-b9bf-e73ec1f2d7b5.png → 97adee05-1586-4589-93e6-c6c3a9cdae9d.png} +0 -0
  138. /kotonebot/kaa/sprites/{d364470a-d077-405f-a242-0d4fad32bc40.png → 9aef1aa8-41e1-42a7-83ad-b4e212ee3976.png} +0 -0
  139. /kotonebot/kaa/sprites/{9ce49f31-6b3a-4bc7-a9c8-9330d6f650f7.png → 9c7f1bcc-0bc8-454b-95fb-56a803030771.png} +0 -0
  140. /kotonebot/kaa/sprites/{99c36126-0173-49f3-a6b8-819bf9bd9306.png → 9cd7e1f1-13c4-471e-abee-223ffd9126cb.png} +0 -0
  141. /kotonebot/kaa/sprites/{370afc82-eea9-4063-afb7-5d5fec075510.png → 9d5dd50c-3341-41a4-a3dd-ccc00edcf201.png} +0 -0
  142. /kotonebot/kaa/sprites/{a5d05106-5a60-414c-972d-a886664abde4.png → 9f99b0e9-8cc0-4877-8f8a-d26257ef5386.png} +0 -0
  143. /kotonebot/kaa/sprites/{3720275d-cbf7-4513-a4a4-09f712b2286a.png → a1853223-d6b9-4660-af4e-91e59bdddc3c.png} +0 -0
  144. /kotonebot/kaa/sprites/{588f3405-4110-48dd-99ec-f89c004cf02e.png → a1bba683-a740-4eaf-8c37-3b1042b963b0.png} +0 -0
  145. /kotonebot/kaa/sprites/{60e43a33-18ee-45f1-bd9a-57bb6539611b.png → a1cf80ee-64bf-4d54-a7db-ffadf3c97026.png} +0 -0
  146. /kotonebot/kaa/sprites/{14cc953d-0ff6-493f-9478-691b5cbd0d07.png → a3b15425-0bfd-4668-a3a9-2ab64da8505f.png} +0 -0
  147. /kotonebot/kaa/sprites/{f400e1fe-a04d-442b-a6de-a448813443fd.png → a4075991-98da-4d06-bcde-28054cd124d2.png} +0 -0
  148. /kotonebot/kaa/sprites/{cecd2638-070f-4710-9142-2dc492660241.png → a7da51c2-8444-4770-a16b-b6543e40197f.png} +0 -0
  149. /kotonebot/kaa/sprites/{9d7a7bbb-b48b-458c-9964-963a57e10a7d.png → a8b3fdce-9e4a-495c-839e-0142f67fee69.png} +0 -0
  150. /kotonebot/kaa/sprites/{e55da40e-5fb5-48e0-91b1-2db0b3b32750.png → aa3b717f-67f8-445f-a6bf-b89f7200d95b.png} +0 -0
  151. /kotonebot/kaa/sprites/{8cc764de-f162-40a4-ba6f-40d60eac5633.png → abcc709c-1f4d-4832-a063-eafbad94ac31.png} +0 -0
  152. /kotonebot/kaa/sprites/{e6e66cdf-69c8-4862-a770-1f81c62f96ac.png → ad172750-d1a6-4a25-8ce8-9483f4b5affb.png} +0 -0
  153. /kotonebot/kaa/sprites/{eb229797-bffc-4a48-96de-85480af1608a.png → aefdec38-e3b6-4762-aed8-33107686ddac.png} +0 -0
  154. /kotonebot/kaa/sprites/{23217053-c549-4ac7-b8c0-3324728e6acd.png → b01c25d2-21bf-4db9-b5b2-ec35817f1b04.png} +0 -0
  155. /kotonebot/kaa/sprites/{9d908390-6f0f-46e6-a8fc-a7dfe23eda18.png → b1e7f24e-f246-406e-9073-58e2f3e4e819.png} +0 -0
  156. /kotonebot/kaa/sprites/{863550ee-e9b8-4aa2-8ebb-e906b09dde3b.png → b5d4265c-82e0-470d-a48e-8aa4edbdef5a.png} +0 -0
  157. /kotonebot/kaa/sprites/{608de0a3-ce93-4344-8f9b-3cb3fab5ad12.png → b65724b4-ec8f-48a9-9ab2-e7ce5b8f35b2.png} +0 -0
  158. /kotonebot/kaa/sprites/{22ab93e6-0192-48e1-a85f-73ee8db00395.png → b8aada91-eedd-4c53-99f3-f903d547235e.png} +0 -0
  159. /kotonebot/kaa/sprites/{09a01092-0d32-4894-a47e-9983b39753df.png → ba759205-ce99-4417-8cab-d32fe55dff4c.png} +0 -0
  160. /kotonebot/kaa/sprites/{934e5383-b63c-472e-afdb-d184c92bdfe3.png → bb0ae68d-44d5-47c6-91dd-a529bc34225b.png} +0 -0
  161. /kotonebot/kaa/sprites/{55d105dc-c488-47d8-9b95-aba5707cfc2a.png → bed9575d-ccad-4f4e-825e-47b2a9cb0925.png} +0 -0
  162. /kotonebot/kaa/sprites/{3a0e917b-f419-4398-8638-77f696d47cd4.png → bfec58df-2628-454f-92aa-769cad186448.png} +0 -0
  163. /kotonebot/kaa/sprites/{1e9e795c-5dc1-4851-8d49-86181b9f4efe.png → c25c7ce5-ad8d-4b2c-83eb-ae6d4871a6c3.png} +0 -0
  164. /kotonebot/kaa/sprites/{c30d08f7-5c07-487a-86c3-122c082bb07b.png → c3ec61aa-f62e-46fd-b359-48864ea7a150.png} +0 -0
  165. /kotonebot/kaa/sprites/{685a7a90-8196-4a6a-a67a-4e7078965982.png → c484ca57-6b19-46b5-a107-1805a7488d48.png} +0 -0
  166. /kotonebot/kaa/sprites/{9e74a9e6-6ea5-4551-9578-8e561195bd3d.png → c571e1c0-ff85-4b2e-97ed-97d33b8d92e3.png} +0 -0
  167. /kotonebot/kaa/sprites/{a2ea2e0f-4f92-4769-9bc4-1ee10c43712c.png → c5e16f75-9b5a-4853-a144-275f3d20c186.png} +0 -0
  168. /kotonebot/kaa/sprites/{37f421f8-f615-43d1-a10f-f9a915a940d1.png → c5ea3dfd-6761-466e-b44c-86592ef05323.png} +0 -0
  169. /kotonebot/kaa/sprites/{276a66cc-95b1-4f86-b700-afa9c9f18149.png → ca2adfb8-1e88-4a31-b3cc-1f70568ce4a3.png} +0 -0
  170. /kotonebot/kaa/sprites/{bbfcc1ea-b441-4e81-85d2-52ce3bf4eb62.png → cb24b3df-1ec3-4ffd-95f1-ca4b0dd5b7da.png} +0 -0
  171. /kotonebot/kaa/sprites/{9f6aef36-f2e5-4208-9748-b6bbdb5546fd.png → cdbbb5ba-5117-488a-8172-48cfc92c14c6.png} +0 -0
  172. /kotonebot/kaa/sprites/{3851e552-fd41-4a15-a6b6-9636c773f67a.png → cf66ae12-48bc-4512-a988-0e5de337c76d.png} +0 -0
  173. /kotonebot/kaa/sprites/{74db3d1c-d6be-4d1f-92be-9bb3ffad7f51.png → d1699baa-467b-46d1-9276-aae78fe8f589.png} +0 -0
  174. /kotonebot/kaa/sprites/{8383a31c-a075-4b5f-b839-cbeb63a7aef3.png → d3e6e2e9-c5b5-463f-98f4-de72c9320d9d.png} +0 -0
  175. /kotonebot/kaa/sprites/{51b836ff-5f2c-4612-85f1-9db74761f300.png → d4c47cc7-5b3e-4c3e-8e80-78cb94e7eed9.png} +0 -0
  176. /kotonebot/kaa/sprites/{2e0f97d4-ce0b-4930-addd-cbdaedffc918.png → d5d854fc-1b87-43d4-b32d-66eccb66fa41.png} +0 -0
  177. /kotonebot/kaa/sprites/{593f4c04-4ca4-4bb0-8fea-ad3af32316f7.png → d60a395b-d907-416a-b091-36bfee4948b3.png} +0 -0
  178. /kotonebot/kaa/sprites/{19fae7eb-addd-4ea2-99ac-0f9262a4d9d6.png → dad4ff8c-07f4-4b07-abd3-5d02bbcc4562.png} +0 -0
  179. /kotonebot/kaa/sprites/{8778f70d-6bb8-4378-aed7-d77c798ab032.png → dbd537c7-d1d3-4d64-9ccd-43dd4519bedd.png} +0 -0
  180. /kotonebot/kaa/sprites/{7250e776-3195-406c-ac6f-ec7dbed49cbe.png → dde03640-6c3a-4b13-a783-f672816bfec4.png} +0 -0
  181. /kotonebot/kaa/sprites/{4b87be27-868a-40fb-b7e4-67c963c6c4b2.png → df97421e-4b74-430e-b3d1-ed9bac6918a7.png} +0 -0
  182. /kotonebot/kaa/sprites/{7079f76b-e2a3-487c-8616-28ce505f5a0e.png → dfb14c98-f672-4906-80bd-d2a18bdd62cd.png} +0 -0
  183. /kotonebot/kaa/sprites/{b3f1b38f-77d0-4dd5-8c19-8a35bbc39516.png → e1e0d751-9006-4fa4-a933-4c0dc5e1f3d2.png} +0 -0
  184. /kotonebot/kaa/sprites/{aa8c4533-bd58-496e-a460-6fa47f7ded83.png → e394ff91-09d3-41e1-bbf5-bc42a71a623b.png} +0 -0
  185. /kotonebot/kaa/sprites/{72b7124a-baf2-4d60-9553-2634370a6d49.png → e5259dfb-3a49-4cfc-9f89-b42958ce6eec.png} +0 -0
  186. /kotonebot/kaa/sprites/{c072da5f-bd35-4e8b-8f32-6c5df18c3aea.png → e5eeb9af-8003-4981-bf56-2e32e76cec5d.png} +0 -0
  187. /kotonebot/kaa/sprites/{46ed11a3-f89e-4d5c-b534-da88d38b382b.png → e9c45a73-2011-4584-87b7-c4e566eba87f.png} +0 -0
  188. /kotonebot/kaa/sprites/{e3cfea3a-3dc4-412b-b226-f9034f5b51f1.png → eae83523-e58c-4db9-a356-4335a5da3dc8.png} +0 -0
  189. /kotonebot/kaa/sprites/{f3b4bb3b-f8d6-4ed0-8bde-0d3322bd8391.png → ed6d3e3d-0f04-4fa4-b0f9-fdbf324649f6.png} +0 -0
  190. /kotonebot/kaa/sprites/{fe031296-8000-4fdc-ab3f-b4aae2af6e5d.png → efb28e1d-7b52-4e20-aec5-117c798b7fbe.png} +0 -0
  191. /kotonebot/kaa/sprites/{b4afb6bd-e049-4b2e-b1ca-26333c7afcb0.png → f025027f-daa0-4943-8395-5d7d44cc7f15.png} +0 -0
  192. /kotonebot/kaa/sprites/{cd53bad6-81fa-4e15-8fe7-5a992367bf90.png → f12c2b0b-8273-4197-a8c6-2a8ef2788a9b.png} +0 -0
  193. /kotonebot/kaa/sprites/{86bcda3a-c7a6-4abf-ae4c-968788f3c667.png → f528ea0e-1e55-4e41-844f-bb1db9185ef8.png} +0 -0
  194. /kotonebot/kaa/sprites/{9b798f35-e745-4964-931d-3e0ae811174d.png → f611bbd9-c062-4989-96a4-65ff6c59673c.png} +0 -0
  195. /kotonebot/kaa/sprites/{de0b3d75-7dc8-4433-a275-2584e9d7ed0b.png → f6bfbf6a-6e53-4563-9345-3f3756d6ea9a.png} +0 -0
  196. /kotonebot/kaa/sprites/{6e338dd7-6834-4e9b-8ac1-afecdff16c43.png → f72bd3d6-850d-4190-9775-938f474d263c.png} +0 -0
  197. /kotonebot/kaa/sprites/{86501f18-e5fd-4a66-a360-20e34935f1a9.png → f8eec9b2-55ca-4195-b098-a5393d02a98a.png} +0 -0
  198. /kotonebot/kaa/sprites/{a1a03d83-6a3b-481a-a807-931f2b0f7cd2.png → faf069e1-f623-47c1-b879-cfa246fbc7df.png} +0 -0
  199. /kotonebot/kaa/sprites/{8e04cb5c-ce45-49d9-81a9-2e5f717d3eb2.png → faf360e8-7845-4fcf-b0a6-0f5f93c050f1.png} +0 -0
  200. {ksaa-2025.5.23.3.dist-info → ksaa-2025.6.23.0.dist-info}/WHEEL +0 -0
  201. {ksaa-2025.5.23.3.dist-info → ksaa-2025.6.23.0.dist-info}/entry_points.txt +0 -0
  202. {ksaa-2025.5.23.3.dist-info → ksaa-2025.6.23.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,277 @@
1
+ import time
2
+ from functools import lru_cache, partial
3
+ from typing import Callable, Any, overload, Literal, Generic, TypeVar, cast, get_args, get_origin
4
+
5
+ from cv2.typing import MatLike
6
+
7
+ from kotonebot.util import Interval
8
+ from kotonebot import device, image, ocr
9
+ from kotonebot.backend.core import Image
10
+ from kotonebot.backend.ocr import TextComparator
11
+ from kotonebot.client.protocol import ClickableObjectProtocol
12
+
13
+
14
+ class LoopAction:
15
+ def __init__(self, loop: 'Loop', func: Callable[[], ClickableObjectProtocol | None]):
16
+ self.loop = loop
17
+ self.func = func
18
+ self.result: ClickableObjectProtocol | None = None
19
+
20
+ @property
21
+ def found(self):
22
+ """
23
+ 是否找到结果。若父 Loop 未在运行中,则返回 False。
24
+ """
25
+ if not self.loop.running:
26
+ return False
27
+ return bool(self.result)
28
+
29
+ def __bool__(self):
30
+ return self.found
31
+
32
+ def reset(self):
33
+ """
34
+ 重置 LoopAction,以复用此对象。
35
+ """
36
+ self.result = None
37
+
38
+ def do(self):
39
+ """
40
+ 执行 LoopAction。
41
+ :return: 执行结果。
42
+ """
43
+ if not self.loop.running:
44
+ return
45
+ if self.loop.found_anything:
46
+ # 本轮循环已执行任意操作,因此不需要再继续检测
47
+ return
48
+ self.result = self.func()
49
+ if self.result:
50
+ self.loop.found_anything = True
51
+
52
+ def click(self, *, at: tuple[int, int] | None = None):
53
+ """
54
+ 点击寻找结果。若结果为空,会跳过执行。
55
+
56
+ :return:
57
+ """
58
+ if self.result:
59
+ if at is not None:
60
+ device.click(*at)
61
+ else:
62
+ device.click(self.result)
63
+
64
+ def call(self, func: Callable[[ClickableObjectProtocol], Any]):
65
+ pass
66
+
67
+
68
+ class Loop:
69
+ def __init__(
70
+ self,
71
+ *,
72
+ timeout: float = 300,
73
+ interval: float = 0.3,
74
+ auto_screenshot: bool = True
75
+ ):
76
+ self.running = True
77
+ self.found_anything = False
78
+ self.auto_screenshot = auto_screenshot
79
+ """
80
+ 是否在每次循环开始时(Loop.tick() 被调用时)截图。
81
+ """
82
+ self.__last_loop: float = -1
83
+ self.__interval = Interval(interval)
84
+ self.screenshot: MatLike | None = None
85
+ """上次截图时的图像数据。"""
86
+
87
+ def __iter__(self):
88
+ self.__interval.reset()
89
+ return self
90
+
91
+ def __next__(self):
92
+ if not self.running:
93
+ raise StopIteration
94
+ self.found_anything = False
95
+ self.__last_loop = time.time()
96
+ return self.tick()
97
+
98
+ def tick(self):
99
+ self.__interval.wait()
100
+ if self.auto_screenshot:
101
+ self.screenshot = device.screenshot()
102
+ self.__last_loop = time.time()
103
+ self.found_anything = False
104
+ return self
105
+
106
+ def exit(self):
107
+ """
108
+ 结束循环。
109
+ """
110
+ self.running = False
111
+
112
+ @overload
113
+ def when(self, condition: Image) -> LoopAction:
114
+ ...
115
+
116
+ @overload
117
+ def when(self, condition: TextComparator) -> LoopAction:
118
+ ...
119
+
120
+ def when(self, condition: Any):
121
+ """
122
+ 判断某个条件是否成立。
123
+
124
+ :param condition:
125
+ :return:
126
+ """
127
+ if isinstance(condition, Image):
128
+ func = partial(image.find, condition)
129
+ elif isinstance(condition, TextComparator):
130
+ func = partial(ocr.find, condition)
131
+ else:
132
+ raise ValueError('Invalid condition type.')
133
+ la = LoopAction(self, func)
134
+ la.reset()
135
+ la.do()
136
+ return la
137
+
138
+ def until(self, condition: Any):
139
+ """
140
+ 当满足指定条件时,结束循环。
141
+
142
+ 等价于 ``loop.when(...).call(lambda _: loop.exit())``
143
+ """
144
+ return self.when(condition).call(lambda _: self.exit())
145
+
146
+ def click_if(self, condition: Any, *, at: tuple[int, int] | None = None):
147
+ """
148
+ 检测指定对象是否出现,若出现,点击该对象或指定位置。
149
+
150
+ ``click_if()`` 等价于 ``loop.when(...).click(...)``。
151
+
152
+ :param condition: 检测目标。
153
+ :param at: 点击位置。若为 None,表示点击找到的目标。
154
+ """
155
+ return self.when(condition).click(at=at)
156
+
157
+ StateType = TypeVar('StateType')
158
+ class StatedLoop(Loop, Generic[StateType]):
159
+ def __init__(
160
+ self,
161
+ states: list[Any] | None = None,
162
+ initial_state: StateType | None = None,
163
+ *,
164
+ timeout: float = 300,
165
+ interval: float = 0.3,
166
+ auto_screenshot: bool = True
167
+ ):
168
+ self.__tmp_states = states
169
+ self.__tmp_initial_state = initial_state
170
+ self.state: StateType
171
+ super().__init__(timeout=timeout, interval=interval, auto_screenshot=auto_screenshot)
172
+
173
+ def __iter__(self):
174
+ # __retrive_state_values() 只能在非 __init__ 中调用
175
+ self.__retrive_state_values()
176
+ return super().__iter__()
177
+
178
+ def __retrive_state_values(self):
179
+ # HACK: __orig_class__ 是 undocumented 属性
180
+ if not hasattr(self, '__orig_class__'):
181
+ # 如果 Foo 不是以参数化泛型的方式实例化的,可能没有 __orig_class__
182
+ if self.state is None:
183
+ raise ValueError('Either specify `states` or use StatedLoop[Literal[...]] syntax.')
184
+ else:
185
+ generic_type_args = get_args(self.__orig_class__) # type: ignore
186
+ if len(generic_type_args) != 1:
187
+ raise ValueError('StatedLoop must have exactly one generic type argument.')
188
+ state_values = get_args(generic_type_args[0])
189
+ if not state_values:
190
+ raise ValueError('StatedLoop must have at least one state value.')
191
+ self.states = cast(tuple[StateType, ...], state_values)
192
+ self.state = self.__tmp_initial_state or self.states[0]
193
+ return state_values
194
+
195
+
196
+ def StatedLoop2(states: StateType) -> StatedLoop[StateType]:
197
+ state_values = get_args(states)
198
+ return cast(StatedLoop[StateType], Loop())
199
+
200
+ if __name__ == '__main__':
201
+ from kotonebot.kaa.tasks import R
202
+ from kotonebot.backend.ocr import contains
203
+ from kotonebot.backend.context import manual_context, init_context
204
+
205
+ # T = TypeVar('T')
206
+ # class Foo(Generic[T]):
207
+ # def get_literal_params(self) -> list | None:
208
+ # """
209
+ # 尝试获取泛型参数 T (如果它是 Literal 类型) 的参数列表。
210
+ # """
211
+ # # self.__orig_class__ 会是 Foo 的具体参数化类型,
212
+ # # 例如 Foo[Literal['p0', 'p1', 'p2', 'p3', 'ap']]
213
+ # if not hasattr(self, '__orig_class__'):
214
+ # # 如果 Foo 不是以参数化泛型的方式实例化的,可能没有 __orig_class__
215
+ # return None
216
+ #
217
+ # # generic_type_args 是传递给 Foo 的类型参数元组
218
+ # # 例如 (Literal['p0', 'p1', 'p2', 'p3', 'ap'],)
219
+ # generic_type_args = get_args(self.__orig_class__)
220
+ #
221
+ # if not generic_type_args:
222
+ # # Foo 没有类型参数
223
+ # return None
224
+ #
225
+ # # T_type 是 Foo 的第一个类型参数
226
+ # # 例如 Literal['p0', 'p1', 'p2', 'p3', 'ap']
227
+ # t_type = generic_type_args[0]
228
+ #
229
+ # # 检查 T_type 是否是 Literal 类型
230
+ # if get_origin(t_type) is Literal:
231
+ # # literal_args 是 Literal 类型的参数元组
232
+ # # 例如 ('p0', 'p1', 'p2', 'p3', 'ap')
233
+ # literal_args = get_args(t_type)
234
+ # return list(literal_args)
235
+ # else:
236
+ # # T 不是 Literal 类型
237
+ # return None
238
+ # f = Foo[Literal['p0', 'p1', 'p2', 'p3', 'ap']]()
239
+ # values = f.get_literal_params()
240
+ # 1
241
+
242
+ from typing_extensions import reveal_type
243
+ slp = StatedLoop[Literal['p0', 'p1', 'p2', 'p3', 'ap']]()
244
+ for l in slp:
245
+ reveal_type(l.states)
246
+
247
+ # init_context()
248
+ # manual_context().begin()
249
+ # for l in Loop():
250
+ # l.when(R.Produce.ButtonUse).click()
251
+ # l.when(R.Produce.ButtonRefillAP).click()
252
+ # l.when(contains("123")).click()
253
+ # l.click_if(contains("!23"), at=(1, 2))
254
+
255
+ # State = Literal['p0', 'p1', 'p2', 'p3', 'ap']
256
+ # for sl in StatedLoop[State]():
257
+ # match sl.state:
258
+ # case 'p0':
259
+ # sl.click_if(R.Produce.ButtonProduce)
260
+ # sl.click_if(contains('master'))
261
+ # sl.when(R.Produce.ButtonPIdolOverview).goto('p1')
262
+ # # AP 不足
263
+ # sl.when(R.Produce.TextAPInsufficient).goto('ap')
264
+ # case 'ap':
265
+ # pass
266
+ # # p1: 选择偶像
267
+ # case 'p1':
268
+ # sl.call(lambda _: select_idol(idol_skin_id), once=True)
269
+ # sl.when(R.Produce.TextAnotherIdolAvailableDialog).call(dialog.no)
270
+ # sl.click_if(R.Common.ButtonNextNoIcon)
271
+ # sl.until(R.Produce.TextStepIndicator2).goto('p2')
272
+ # case 'p2':
273
+ # sl.when(contains("123")).click()
274
+ # case 'p3':
275
+ # sl.click_if(contains("!23"), at=(1, 2))
276
+ # case _:
277
+ # assert_never(sl.state)
@@ -1,5 +1,8 @@
1
1
  from .device import Device
2
- from .factory import create_device, DeviceImpl
2
+ from .registration import create_device, DeviceImpl
3
+
4
+ # 确保所有实现都被注册
5
+ from . import implements # noqa: F401
3
6
 
4
7
  __all__ = [
5
8
  'Device',
@@ -11,7 +11,7 @@ from adbutils._device import AdbDevice as AdbUtilsDevice
11
11
  from ..backend.debug import result
12
12
  from kotonebot.backend.core import HintBox
13
13
  from kotonebot.primitives import Rect, Point, is_point
14
- from .protocol import ClickableObjectProtocol, Commandable, Touchable, Screenshotable
14
+ from .protocol import ClickableObjectProtocol, Commandable, Touchable, Screenshotable, AndroidCommandable, WindowsCommandable
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
17
 
@@ -71,7 +71,6 @@ class Device:
71
71
  横屏时为 'landscape',竖屏时为 'portrait'。
72
72
  """
73
73
 
74
- self._command: Commandable
75
74
  self._touch: Touchable
76
75
  self._screenshot: Screenshotable
77
76
 
@@ -90,12 +89,6 @@ class Device:
90
89
  def adb(self, value: AdbUtilsDevice) -> None:
91
90
  self._adb = value
92
91
 
93
- def launch_app(self, package_name: str) -> None:
94
- """
95
- 根据包名启动 app
96
- """
97
- self._command.launch_app(package_name)
98
-
99
92
  @overload
100
93
  def click(self) -> None:
101
94
  """
@@ -306,17 +299,6 @@ class Device:
306
299
  """
307
300
  return self._screenshot.screen_size
308
301
 
309
- def current_package(self) -> str | None:
310
- """
311
- 获取前台 APP 的包名。
312
-
313
- :return: 前台 APP 的包名。如果获取失败,则返回 None。
314
- :exception: 如果设备不支持此功能,则抛出 NotImplementedError。
315
- """
316
- ret = self._command.current_package()
317
- logger.debug("current_package: %s", ret)
318
- return ret
319
-
320
302
  def detect_orientation(self) -> Literal['portrait', 'landscape'] | None:
321
303
  """
322
304
  检测当前设备方向并设置 `self.orientation` 属性。
@@ -330,10 +312,30 @@ class AndroidDevice(Device):
330
312
  def __init__(self, adb_connection: AdbUtilsDevice | None = None) -> None:
331
313
  super().__init__('android')
332
314
  self._adb: AdbUtilsDevice | None = adb_connection
315
+ self.commands: AndroidCommandable
316
+
317
+ def current_package(self) -> str | None:
318
+ """
319
+ 获取前台 APP 的包名。
320
+
321
+ :return: 前台 APP 的包名。如果获取失败,则返回 None。
322
+ :exception: 如果设备不支持此功能,则抛出 NotImplementedError。
323
+ """
324
+ ret = self.commands.current_package()
325
+ logger.debug("current_package: %s", ret)
326
+ return ret
327
+
328
+ def launch_app(self, package_name: str) -> None:
329
+ """
330
+ 根据包名启动 app
331
+ """
332
+ self.commands.launch_app(package_name)
333
+
333
334
 
334
335
  class WindowsDevice(Device):
335
336
  def __init__(self) -> None:
336
337
  super().__init__('windows')
338
+ self.commands: WindowsCommandable
337
339
 
338
340
 
339
341
  if __name__ == "__main__":
@@ -346,10 +348,10 @@ if __name__ == "__main__":
346
348
  d = adb.device_list()[-1]
347
349
  d.shell("dumpsys activity top | grep ACTIVITY | tail -n 1")
348
350
  dd = AndroidDevice(d)
349
- adb_imp = AdbRawImpl(dd)
350
- dd._command = adb_imp
351
+ adb_imp = AdbRawImpl(d)
351
352
  dd._touch = adb_imp
352
353
  dd._screenshot = adb_imp
354
+ dd.commands = adb_imp
353
355
  # dd._screenshot = MinicapScreenshotImpl(dd)
354
356
  # dd._screenshot = UiAutomator2Impl(dd)
355
357
 
@@ -1,10 +1,11 @@
1
- from .protocol import HostProtocol, Instance
1
+ from .protocol import HostProtocol, Instance, AdbHostConfig, WindowsHostConfig, RemoteWindowsHostConfig
2
2
  from .custom import CustomInstance, create as create_custom
3
3
  from .mumu12_host import Mumu12Host, Mumu12Instance
4
4
  from .leidian_host import LeidianHost, LeidianInstance
5
5
 
6
6
  __all__ = [
7
7
  'HostProtocol', 'Instance',
8
+ 'AdbHostConfig', 'WindowsHostConfig', 'RemoteWindowsHostConfig',
8
9
  'CustomInstance', 'create_custom',
9
10
  'Mumu12Host', 'Mumu12Instance',
10
11
  'LeidianHost', 'LeidianInstance'
@@ -1,19 +1,22 @@
1
1
  import os
2
2
  import subprocess
3
3
  from psutil import process_iter
4
- from .protocol import HostProtocol, Instance
5
- from typing import Optional, ParamSpec, TypeVar, TypeGuard
4
+ from .protocol import Instance, AdbHostConfig
5
+ from typing import ParamSpec, TypeVar, cast
6
6
  from typing_extensions import override
7
7
 
8
8
  from kotonebot import logging
9
+ from kotonebot.client import DeviceImpl
9
10
  from kotonebot.client.device import Device
11
+ from kotonebot.client.registration import AdbBasedImpl, create_device
12
+ from kotonebot.client.implements.adb import AdbImplConfig
10
13
 
11
14
  logger = logging.getLogger(__name__)
12
15
 
13
16
  P = ParamSpec('P')
14
17
  T = TypeVar('T')
15
18
 
16
- class CustomInstance(Instance):
19
+ class CustomInstance(Instance[AdbHostConfig]):
17
20
  def __init__(self, exe_path: str | None, emulator_args: str = "", *args, **kwargs):
18
21
  super().__init__(*args, **kwargs)
19
22
  self.exe_path: str | None = exe_path
@@ -65,6 +68,26 @@ class CustomInstance(Instance):
65
68
  def refresh(self):
66
69
  pass
67
70
 
71
+ @override
72
+ def create_device(self, impl: DeviceImpl, host_config: AdbHostConfig) -> Device:
73
+ """为自定义实例创建 Device。"""
74
+ if self.adb_port is None:
75
+ raise ValueError("ADB port is not set and is required.")
76
+
77
+ # 为 ADB 相关的实现创建配置
78
+ if impl in ['adb', 'adb_raw', 'uiautomator2']:
79
+ config = AdbImplConfig(
80
+ addr=f'{self.adb_ip}:{self.adb_port}',
81
+ connect=True,
82
+ disconnect=True,
83
+ device_serial=self.adb_name,
84
+ timeout=host_config.timeout
85
+ )
86
+ impl = cast(AdbBasedImpl, impl) # make pylance happy
87
+ return create_device(impl, config)
88
+ else:
89
+ raise ValueError(f'Unsupported device implementation for Custom: {impl}')
90
+
68
91
  def __repr__(self) -> str:
69
92
  return f'CustomInstance(#{self.id}# at "{self.exe_path}" with {self.adb_ip}:{self.adb_port})'
70
93
 
@@ -1,13 +1,16 @@
1
1
  import os
2
2
  import subprocess
3
+ from typing import cast
3
4
  from functools import lru_cache
4
5
  from typing_extensions import override
5
6
 
6
7
  from kotonebot import logging
7
- from kotonebot.client import DeviceImpl, create_device
8
+ from kotonebot.client import DeviceImpl
8
9
  from kotonebot.client.device import Device
10
+ from kotonebot.client.registration import AdbBasedImpl, create_device
11
+ from kotonebot.client.implements.adb import AdbImplConfig
9
12
  from kotonebot.util import Countdown, Interval
10
- from .protocol import HostProtocol, Instance, copy_type
13
+ from .protocol import HostProtocol, Instance, copy_type, AdbHostConfig
11
14
 
12
15
  logger = logging.getLogger(__name__)
13
16
 
@@ -18,7 +21,7 @@ else:
18
21
  """Stub for read_reg on non-Windows platforms."""
19
22
  return default
20
23
 
21
- class LeidianInstance(Instance):
24
+ class LeidianInstance(Instance[AdbHostConfig]):
22
25
  @copy_type(Instance.__init__)
23
26
  def __init__(self, *args, **kwargs):
24
27
  super().__init__(*args, **kwargs)
@@ -70,16 +73,24 @@ class LeidianInstance(Instance):
70
73
  return result.strip() == 'running'
71
74
 
72
75
  @override
73
- def create_device(self, impl: DeviceImpl, *, timeout: float = 180) -> Device:
76
+ def create_device(self, impl: DeviceImpl, host_config: AdbHostConfig) -> Device:
77
+ """为雷电模拟器实例创建 Device。"""
74
78
  if self.adb_port is None:
75
79
  raise ValueError("ADB port is not set and is required.")
76
- return create_device(
77
- addr=f'{self.adb_ip}:{self.adb_port}',
78
- impl=impl,
79
- device_serial=self.adb_name,
80
- connect=False,
81
- timeout=timeout
82
- )
80
+
81
+ # 为 ADB 相关的实现创建配置
82
+ if impl in ['adb', 'adb_raw', 'uiautomator2']:
83
+ config = AdbImplConfig(
84
+ addr=f'{self.adb_ip}:{self.adb_port}',
85
+ connect=False, # 雷电模拟器不需要 adb connect
86
+ disconnect=False,
87
+ device_serial=self.adb_name,
88
+ timeout=host_config.timeout
89
+ )
90
+ impl = cast(AdbBasedImpl, impl) # make pylance happy
91
+ return create_device(impl, config)
92
+ else:
93
+ raise ValueError(f'Unsupported device implementation for Leidian: {impl}')
83
94
 
84
95
  class LeidianHost(HostProtocol):
85
96
  @staticmethod
@@ -2,13 +2,15 @@ import os
2
2
  import json
3
3
  import subprocess
4
4
  from functools import lru_cache
5
- from typing import Any
5
+ from typing import Any, cast
6
6
  from typing_extensions import override
7
7
 
8
8
  from kotonebot import logging
9
9
  from kotonebot.client import DeviceImpl, Device
10
+ from kotonebot.client.registration import AdbBasedImpl, create_device
11
+ from kotonebot.client.implements.adb import AdbImplConfig
10
12
  from kotonebot.util import Countdown, Interval
11
- from .protocol import HostProtocol, Instance, copy_type
13
+ from .protocol import HostProtocol, Instance, copy_type, AdbHostConfig
12
14
 
13
15
  logger = logging.getLogger(__name__)
14
16
 
@@ -19,7 +21,7 @@ else:
19
21
  """Stub for read_reg on non-Windows platforms."""
20
22
  return default
21
23
 
22
- class Mumu12Instance(Instance):
24
+ class Mumu12Instance(Instance[AdbHostConfig]):
23
25
  @copy_type(Instance.__init__)
24
26
  def __init__(self, *args, **kwargs):
25
27
  super().__init__(*args, **kwargs)
@@ -70,6 +72,26 @@ class Mumu12Instance(Instance):
70
72
  def running(self) -> bool:
71
73
  return self.is_android_started
72
74
 
75
+ @override
76
+ def create_device(self, impl: DeviceImpl, host_config: AdbHostConfig) -> Device:
77
+ """为MuMu12模拟器实例创建 Device。"""
78
+ if self.adb_port is None:
79
+ raise ValueError("ADB port is not set and is required.")
80
+
81
+ # 为 ADB 相关的实现创建配置
82
+ if impl in ['adb', 'adb_raw', 'uiautomator2']:
83
+ config = AdbImplConfig(
84
+ addr=f'{self.adb_ip}:{self.adb_port}',
85
+ connect=True,
86
+ disconnect=True,
87
+ device_serial=self.adb_name,
88
+ timeout=host_config.timeout
89
+ )
90
+ impl = cast(AdbBasedImpl, impl) # make pylance happy
91
+ return create_device(impl, config)
92
+ else:
93
+ raise ValueError(f'Unsupported device implementation for MuMu12: {impl}')
94
+
73
95
  class Mumu12Host(HostProtocol):
74
96
  @staticmethod
75
97
  @lru_cache(maxsize=1)
@@ -1,14 +1,15 @@
1
1
  import time
2
2
  import socket
3
3
  from abc import ABC, abstractmethod
4
- from typing_extensions import ParamSpec, Concatenate
5
- from typing import Callable, TypeVar, Generic, Protocol, runtime_checkable, Type, Any
4
+ from typing import Callable, TypeVar, Protocol, Any, Generic
5
+ from dataclasses import dataclass
6
6
 
7
7
  from adbutils import adb, AdbTimeout, AdbError
8
8
  from adbutils._device import AdbDevice
9
9
 
10
10
  from kotonebot import logging
11
- from kotonebot.client import Device, create_device, DeviceImpl
11
+ from kotonebot.client import Device, DeviceImpl
12
+
12
13
  from kotonebot.util import Countdown, Interval
13
14
 
14
15
  logger = logging.getLogger(__name__)
@@ -17,6 +18,28 @@ _T = TypeVar("_T")
17
18
  def copy_type(_: _T) -> Callable[[Any], _T]:
18
19
  return lambda x: x
19
20
 
21
+ # --- 定义专用的 HostConfig 数据类 ---
22
+ @dataclass
23
+ class AdbHostConfig:
24
+ """由外部为基于 ADB 的主机提供的配置。"""
25
+ timeout: float = 180
26
+
27
+ @dataclass
28
+ class WindowsHostConfig:
29
+ """由外部为 Windows 实现提供配置。"""
30
+ window_title: str
31
+ ahk_exe_path: str
32
+
33
+ @dataclass
34
+ class RemoteWindowsHostConfig:
35
+ """由外部为远程 Windows 实现提供配置。"""
36
+ windows_host_config: WindowsHostConfig
37
+ host: str
38
+ port: int
39
+
40
+ # --- 使用泛型改造 Instance 协议 ---
41
+ T_HostConfig = TypeVar("T_HostConfig")
42
+
20
43
  def tcp_ping(host: str, port: int, timeout: float = 1.0) -> bool:
21
44
  """
22
45
  通过 TCP ping 检查主机和端口是否可达。
@@ -36,7 +59,11 @@ def tcp_ping(host: str, port: int, timeout: float = 1.0) -> bool:
36
59
  return False
37
60
 
38
61
 
39
- class Instance(ABC):
62
+ class Instance(Generic[T_HostConfig], ABC):
63
+ """
64
+ 代表一个可运行环境的实例(如一个模拟器)。
65
+ 使用泛型来约束 create_device 方法的配置参数类型。
66
+ """
40
67
  def __init__(self,
41
68
  id: str,
42
69
  name: str,
@@ -68,7 +95,7 @@ class Instance(ABC):
68
95
  启动模拟器实例。
69
96
  """
70
97
  raise NotImplementedError()
71
-
98
+
72
99
  @abstractmethod
73
100
  def stop(self):
74
101
  """
@@ -80,21 +107,16 @@ class Instance(ABC):
80
107
  def running(self) -> bool:
81
108
  raise NotImplementedError()
82
109
 
83
- def create_device(self, impl: DeviceImpl, *, timeout: float = 180) -> Device:
110
+ @abstractmethod
111
+ def create_device(self, impl: DeviceImpl, host_config: T_HostConfig) -> Device:
84
112
  """
85
- 创建 Device 实例,可用于控制模拟器系统。
86
-
87
- :return: Device 实例
113
+ 根据实现名称和类型化的主机配置创建设备。
114
+
115
+ :param impl: 设备实现的名称。
116
+ :param host_config: 一个类型化的数据对象,包含创建所需的所有外部配置。
117
+ :return: 配置好的 Device 实例。
88
118
  """
89
- if self.adb_port is None:
90
- raise ValueError("ADB port is not set and is required.")
91
- return create_device(
92
- addr=f'{self.adb_ip}:{self.adb_port}',
93
- impl=impl,
94
- device_serial=self.adb_name,
95
- connect=True,
96
- timeout=timeout
97
- )
119
+ raise NotImplementedError()
98
120
 
99
121
  def wait_available(self, timeout: float = 180):
100
122
  logger.info('Starting to wait for emulator %s(127.0.0.1:%d) to be available...', self.name, self.adb_port)
@@ -0,0 +1,6 @@
1
+ # 导入所有内置实现,以触发它们的 @register_impl 装饰器
2
+ from . import adb # noqa: F401
3
+ from . import adb_raw # noqa: F401
4
+ from . import remote_windows # noqa: F401
5
+ from . import uiautomator2 # noqa: F401
6
+ from . import windows # noqa: F401