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
kotonebot/backend/bot.py CHANGED
@@ -32,7 +32,7 @@ class RunStatus:
32
32
  callstack: list[Task | Action] = field(default_factory=list)
33
33
 
34
34
  def interrupt(self):
35
- vars.interrupted.set()
35
+ vars.flow.request_interrupt()
36
36
 
37
37
  # Modified from https://stackoverflow.com/questions/70982565/how-do-i-make-an-event-listener-with-decorators-in-python
38
38
  Params = ParamSpec('Params')
@@ -158,7 +158,7 @@ class KotoneBot:
158
158
  d = self._on_create_device()
159
159
  init_context(config_path=self.config_path, config_type=self.config_type, target_device=d)
160
160
  self._on_after_init_context()
161
- vars.interrupted.clear()
161
+ vars.flow.clear_interrupt()
162
162
 
163
163
  if by_priority:
164
164
  tasks = sorted(tasks, key=lambda x: x.priority, reverse=True)
@@ -180,7 +180,7 @@ class KotoneBot:
180
180
  logger.exception('Keyboard interrupt detected.')
181
181
  for task1 in tasks[tasks.index(task):]:
182
182
  self.events.task_status_changed.trigger(task1, 'cancelled')
183
- vars.interrupted.clear()
183
+ vars.flow.clear_interrupt()
184
184
  break
185
185
  # 其他错误
186
186
  except Exception as e:
@@ -3,6 +3,7 @@ import re
3
3
  import time
4
4
  import logging
5
5
  import warnings
6
+ import threading
6
7
  from datetime import datetime
7
8
  from threading import Event
8
9
  from typing import (
@@ -24,7 +25,8 @@ from typing_extensions import deprecated
24
25
  import cv2
25
26
  from cv2.typing import MatLike
26
27
 
27
- from kotonebot.client.device import Device
28
+ from kotonebot.client.device import Device, AndroidDevice, WindowsDevice
29
+ from kotonebot.backend.flow_controller import FlowController
28
30
  import kotonebot.backend.image as raw_image
29
31
  from kotonebot.backend.image import (
30
32
  TemplateMatchResult,
@@ -44,12 +46,12 @@ from kotonebot.backend.color import (
44
46
  from kotonebot.backend.ocr import (
45
47
  Ocr, OcrResult, OcrResultList, jp, en, StringMatchFunction
46
48
  )
47
- from kotonebot.client.factory import create_device
49
+ from kotonebot.client.registration import AdbBasedImpl, create_device
48
50
  from kotonebot.config.manager import load_config, save_config
49
51
  from kotonebot.config.base_config import UserConfig
50
52
  from kotonebot.backend.core import Image, HintBox
51
53
  from kotonebot.errors import KotonebotWarning
52
- from kotonebot.client.factory import DeviceImpl
54
+ from kotonebot.client import DeviceImpl
53
55
  from kotonebot.backend.preprocessor import PreprocessorProtocol
54
56
  from kotonebot.primitives import Rect
55
57
 
@@ -126,8 +128,7 @@ def interruptible(func: Callable[P, T]) -> Callable[P, T]:
126
128
  """
127
129
  def _decorator(*args: P.args, **kwargs: P.kwargs) -> T:
128
130
  global vars
129
- if vars.interrupted.is_set():
130
- raise KeyboardInterrupt("User requested interrupt.")
131
+ vars.flow.check()
131
132
  return func(*args, **kwargs)
132
133
  return _decorator
133
134
 
@@ -145,15 +146,13 @@ def interruptible_class(cls: Type[T]) -> Type[T]:
145
146
 
146
147
  def sleep(seconds: float, /):
147
148
  """
148
- 可中断的 sleep 函数。
149
+ 可中断和可暂停的 sleep 函数。
149
150
 
150
151
  建议使用本函数代替 `time.sleep()`,
151
- 这样能以最快速度响应用户请求中断。
152
+ 这样能以最快速度响应用户请求中断和暂停。
152
153
  """
153
154
  global vars
154
- vars.interrupted.wait(timeout=seconds)
155
- if vars.interrupted.is_set():
156
- raise KeyboardInterrupt("User requested interrupt.")
155
+ vars.flow.sleep(seconds)
157
156
 
158
157
  def warn_manual_screenshot_mode(name: str, alternative: str):
159
158
  """
@@ -175,8 +174,8 @@ def is_manual_screenshot_mode() -> bool:
175
174
  class ContextGlobalVars:
176
175
  def __init__(self):
177
176
  self.__vars = dict[str, Any]()
178
- self.interrupted: Event = Event()
179
- """用户请求中断事件"""
177
+ self.flow: FlowController = FlowController()
178
+ """流程控制器,负责停止、暂停、恢复等操作"""
180
179
 
181
180
  def __getitem__(self, key: str) -> Any:
182
181
  return self.__vars[key]
@@ -198,6 +197,17 @@ class ContextGlobalVars:
198
197
 
199
198
  def clear(self):
200
199
  self.__vars.clear()
200
+ self.flow.reset() # 重置流程控制器
201
+
202
+ def check_flow_control():
203
+ """
204
+ 统一的流程控制检查函数。
205
+
206
+ 检查用户是否请求中断或暂停,如果是则相应处理:
207
+ - 如果请求中断,抛出 KeyboardInterrupt 异常
208
+ - 如果请求暂停,等待直到恢复
209
+ """
210
+ vars.flow.check()
201
211
 
202
212
  class ContextStackVars:
203
213
  stack: list['ContextStackVars'] = []
@@ -707,15 +717,17 @@ class Forwarded:
707
717
  raise ValueError(f"Forwarded object {self._FORWARD_name} called before initialization.")
708
718
  setattr(self._FORWARD_getter(), name, value)
709
719
 
710
- # HACK: 这应该要有个更好的实现方式
711
- class ContextDevice(Device):
712
- def __init__(self, device: Device):
720
+
721
+ T_Device = TypeVar('T_Device', bound=Device)
722
+ class ContextDevice(Generic[T_Device], Device):
723
+ def __init__(self, device: T_Device):
713
724
  self._device = device
714
725
 
715
726
  def screenshot(self, *, force: bool = False):
716
727
  """
717
728
  截图。返回截图数据,同时更新当前上下文的截图数据。
718
729
  """
730
+ check_flow_control()
719
731
  global next_wait, last_screenshot_time, next_wait_time
720
732
  current = ContextStackVars.ensure_current()
721
733
  if force:
@@ -735,26 +747,42 @@ class ContextDevice(Device):
735
747
  current._screenshot = img
736
748
  return img
737
749
 
738
- def __getattribute__(self, name: str) -> Any:
739
- if name in ['_device', 'screenshot']:
750
+ def __getattribute__(self, name: str):
751
+ if name in ['_device', 'screenshot', 'of_android', 'of_windows']:
740
752
  return object.__getattribute__(self, name)
741
753
  else:
742
754
  return getattr(self._device, name)
743
755
 
744
756
  def __setattr__(self, name: str, value: Any):
745
- if name in ['_device', 'screenshot']:
757
+ if name in ['_device', 'screenshot', 'of_android', 'of_windows']:
746
758
  return object.__setattr__(self, name, value)
747
759
  else:
748
760
  return setattr(self._device, name, value)
749
761
 
762
+ def of_android(self) -> 'ContextDevice | AndroidDevice':
763
+ """
764
+ 确保此 ContextDevice 底层为 Android 平台。
765
+ 同时通过返回的对象可以调用 Android 平台特有的方法。
766
+ """
767
+ if not isinstance(self._device, AndroidDevice):
768
+ raise ValueError("Device is not AndroidDevice")
769
+ return self
770
+
771
+ def of_windows(self) -> 'ContextDevice | WindowsDevice':
772
+ """
773
+ 确保此 ContextDevice 底层为 Windows 平台。
774
+ 同时通过返回的对象可以调用 Windows 平台特有的方法。
775
+ """
776
+ if not isinstance(self._device, WindowsDevice):
777
+ raise ValueError("Device is not WindowsDevice")
778
+ return self
750
779
 
751
780
  class Context(Generic[T]):
752
781
  def __init__(
753
782
  self,
754
783
  config_path: str,
755
784
  config_type: Type[T],
756
- screenshot_impl: Optional[DeviceImpl] = None,
757
- device: Optional[Device] = None
785
+ device: Device
758
786
  ):
759
787
  self.__ocr = ContextOcr(self)
760
788
  self.__image = ContextImage(self)
@@ -762,14 +790,7 @@ class Context(Generic[T]):
762
790
  self.__vars = ContextGlobalVars()
763
791
  self.__debug = ContextDebug(self)
764
792
  self.__config = ContextConfig[T](self, config_path, config_type)
765
-
766
- ip = self.config.current.backend.adb_ip
767
- port = self.config.current.backend.adb_port
768
- # TODO: 处理链接失败情况
769
- if screenshot_impl is None:
770
- screenshot_impl = self.config.current.backend.screenshot_impl
771
- logger.info(f'Using "{screenshot_impl}" as screenshot implementation')
772
- self.__device = ContextDevice(device or create_device(f'{ip}:{port}', screenshot_impl))
793
+ self.__device = ContextDevice(device)
773
794
 
774
795
  def inject(
775
796
  self,
@@ -880,8 +901,7 @@ def init_context(
880
901
  config_path: str = 'config.json',
881
902
  config_type: Type[T] = dict[str, Any],
882
903
  force: bool = False,
883
- screenshot_impl: Optional[DeviceImpl] = None,
884
- target_device: Device | None = None,
904
+ target_device: Device,
885
905
  ):
886
906
  """
887
907
  初始化 Context 模块。
@@ -892,8 +912,6 @@ def init_context(
892
912
  默认为 `dict[str, Any]`,即普通的 JSON 数据,不包含任何类型信息。
893
913
  :param force: 是否强制重新初始化。
894
914
  若为 `True`,则忽略已存在的 Context 实例,并重新创建一个新的实例。
895
- :param screenshot_impl: 截图实现。
896
- 若为 `None`,则使用默认配置文件中指定的截图实现。
897
915
  :param target_device: 目标设备
898
916
  """
899
917
  global _c, device, ocr, image, color, vars, debug, config
@@ -902,8 +920,7 @@ def init_context(
902
920
  _c = Context(
903
921
  config_path=config_path,
904
922
  config_type=config_type,
905
- screenshot_impl=screenshot_impl,
906
- device=target_device
923
+ device=target_device,
907
924
  )
908
925
  device._FORWARD_getter = lambda: _c.device # type: ignore
909
926
  ocr._FORWARD_getter = lambda: _c.ocr # type: ignore
@@ -950,5 +967,4 @@ def manual_context(screenshot_mode: ScreenshotMode = 'auto') -> ManualContextMan
950
967
  默认情况下,Context* 类仅允许在 @task/@action 函数中使用。
951
968
  如果想要在其他地方使用,使用此函数手动创建一个上下文。
952
969
  """
953
- return ManualContextManager(screenshot_mode)
954
-
970
+ return ManualContextManager(screenshot_mode)
@@ -115,7 +115,7 @@ async def run_code(request: RunCodeRequest):
115
115
  except KeyboardInterrupt as e:
116
116
  result = {"status": "error", "result": stdout.getvalue(), "message": str(e), "traceback": traceback.format_exc()}
117
117
  finally:
118
- context_vars.interrupted.clear()
118
+ context_vars.flow.clear_interrupt()
119
119
  event.set()
120
120
  threading.Thread(target=_runner, daemon=True).start()
121
121
  await event.wait()
@@ -124,8 +124,8 @@ async def run_code(request: RunCodeRequest):
124
124
  @app.get("/api/code/stop")
125
125
  async def stop_code():
126
126
  from kotonebot.backend.context import vars
127
- vars.interrupted.set()
128
- while vars.interrupted.is_set():
127
+ vars.flow.request_interrupt()
128
+ while vars.flow.is_interrupted:
129
129
  await asyncio.sleep(0.1)
130
130
  return {"status": "ok"}
131
131
 
@@ -216,8 +216,6 @@ def wait_message_all_done():
216
216
  threading.Thread(target=_wait, daemon=True).start()
217
217
 
218
218
  if __name__ == "__main__":
219
- from kotonebot.backend.context import init_context
220
- init_context()
221
219
  debug_vars.debug.hide_server_log = False
222
220
  process = subprocess.Popen(["pylsp", "--port", "5479", "--ws"])
223
221
  print("LSP started. PID=", process.pid)
@@ -328,20 +328,4 @@ class SimpleDispatcher:
328
328
  self.result = self.timeout_result
329
329
  break
330
330
  device.screenshot()
331
- return self.result
332
-
333
- if __name__ == '__main__':
334
- from .context.task_action import action
335
- from .context import init_context
336
- init_context()
337
- @action('inner', dispatcher=True)
338
- def inner(ctx: DispatcherContext):
339
- print('inner')
340
- ctx.finish()
341
-
342
- @action('test', dispatcher=True)
343
- def test(ctx: DispatcherContext):
344
- print('test')
345
- inner()
346
- ctx.finish()
347
- test()
331
+ return self.result
@@ -0,0 +1,195 @@
1
+ import time
2
+ import logging
3
+ import threading
4
+ from typing import Literal
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ class FlowController:
9
+ """
10
+ 一个用于控制任务执行流程(如停止、暂停、恢复)的类。
11
+
12
+ 这个类是线程安全的,提供了以下功能:
13
+
14
+ * 停止任务执行(通过中断信号)
15
+ * 暂停/恢复任务执行
16
+ * 可中断和可暂停的 sleep 功能
17
+ * 流程状态检查
18
+
19
+ 使用方法::
20
+
21
+ controller = FlowController()
22
+
23
+ # 在任务的关键路径上调用检查
24
+ controller.check()
25
+
26
+ # 使用可控制的 sleep
27
+ controller.sleep(1.0)
28
+
29
+ # 外部控制
30
+ controller.request_pause() # 暂停
31
+ controller.request_resume() # 恢复
32
+ controller.request_stop() # 停止
33
+ """
34
+
35
+ def __init__(self):
36
+ self.interrupt_event: threading.Event = threading.Event()
37
+ """中断事件,用于停止任务"""
38
+
39
+ self.paused: bool = False
40
+ """暂停标志"""
41
+
42
+ self.pause_condition: threading.Condition = threading.Condition()
43
+ """暂停条件变量,用于线程间同步"""
44
+
45
+ def check(self) -> None:
46
+ """
47
+ 检查当前流程状态。
48
+
49
+ 如果收到停止请求,则抛出 KeyboardInterrupt 异常。
50
+ 如果收到暂停请求,则阻塞直到恢复。
51
+
52
+ 这是核心的检查点方法,应在任务的关键路径上(如循环或等待前)调用。
53
+
54
+ :raises KeyboardInterrupt: 当收到停止请求时
55
+ """
56
+ # 优先检查中断信号
57
+ if self.interrupt_event.is_set():
58
+ raise KeyboardInterrupt("User requested interrupt.")
59
+
60
+ # 检查暂停状态
61
+ with self.pause_condition:
62
+ while self.paused:
63
+ self.pause_condition.wait()
64
+
65
+ def sleep(self, seconds: float) -> None:
66
+ """
67
+ 一个可被中断和暂停的 sleep 方法。
68
+
69
+ 与标准的 time.sleep() 不同,这个方法会响应停止和暂停请求。
70
+ 在暂停状态下,计时器会暂停,恢复后继续计时。
71
+
72
+ :param seconds: 睡眠时间(秒)
73
+ :raises KeyboardInterrupt: 当收到停止请求时
74
+ """
75
+ with self.pause_condition:
76
+ end_time = time.time() + seconds
77
+ while True:
78
+ self.check() # 每次循环都检查状态
79
+ remaining = end_time - time.time()
80
+ if remaining <= 0:
81
+ break
82
+ # 等待指定时间或直到被唤醒
83
+ self.pause_condition.wait(timeout=remaining)
84
+
85
+ # 结束后再次检查状态
86
+ self.check()
87
+
88
+ def request_interrupt(self) -> None:
89
+ """
90
+ 请求停止任务。
91
+
92
+ 设置中断信号,所有正在执行的任务将在下一个检查点停止。
93
+ 停止的优先级高于暂停。
94
+ """
95
+ logger.info('Interrupt requested.')
96
+ self.interrupt_event.set()
97
+
98
+ def request_pause(self) -> None:
99
+ """
100
+ 请求暂停任务。
101
+
102
+ 设置暂停标志,所有正在执行的任务将在下一个检查点暂停。
103
+ 如果任务已经暂停,此操作无效果。
104
+ """
105
+ with self.pause_condition:
106
+ if not self.paused:
107
+ logger.info('Pause requested.')
108
+ self.paused = True
109
+
110
+ def request_resume(self) -> None:
111
+ """
112
+ 请求恢复任务。
113
+
114
+ 清除暂停标志并通知所有等待的线程恢复执行。
115
+ 如果任务没有暂停,此操作无效果。
116
+ """
117
+ with self.pause_condition:
118
+ if self.paused:
119
+ logger.info('Resume requested.')
120
+ self.paused = False
121
+ self.pause_condition.notify_all()
122
+
123
+ def toggle_pause(self) -> bool:
124
+ """
125
+ 切换暂停/恢复状态。
126
+
127
+ :returns: 操作后的暂停状态。True 表示已暂停,False 表示已恢复。
128
+ """
129
+ with self.pause_condition:
130
+ logger.info('Pause toggled.')
131
+ if self.paused:
132
+ self.paused = False
133
+ self.pause_condition.notify_all()
134
+ return False
135
+ else:
136
+ self.paused = True
137
+ return True
138
+
139
+ def clear_interrupt(self) -> None:
140
+ """
141
+ 清除中断信号。
142
+
143
+ 用于任务正常结束或重启时重置状态。
144
+ 通常在开始新任务前调用。
145
+ """
146
+ self.interrupt_event.clear()
147
+ logger.info('Interrupt cleared.')
148
+
149
+ def reset(self) -> None:
150
+ """
151
+ 重置流程控制器到初始状态。
152
+
153
+ 清除所有信号和状态,相当于重新创建一个新的控制器。
154
+ """
155
+ self.interrupt_event.clear()
156
+ with self.pause_condition:
157
+ if self.paused:
158
+ self.paused = False
159
+ self.pause_condition.notify_all()
160
+ logger.info('FlowController reset.')
161
+
162
+ @property
163
+ def is_interrupted(self) -> bool:
164
+ """
165
+ 检查是否收到中断请求。
166
+
167
+ :returns: True 表示已收到中断请求
168
+ """
169
+ return self.interrupt_event.is_set()
170
+
171
+ @property
172
+ def is_paused(self) -> bool:
173
+ """
174
+ 检查是否处于暂停状态。
175
+
176
+ :returns: True 表示当前处于暂停状态
177
+ """
178
+ return self.paused
179
+
180
+ @property
181
+ def status(self) -> Literal['running', 'paused', 'interrupted']:
182
+ """
183
+ 获取当前状态的字符串描述。
184
+
185
+ :returns: 状态描述,可能的值:'running', 'paused', 'interrupted'
186
+ """
187
+ if self.is_interrupted:
188
+ return 'interrupted'
189
+ elif self.is_paused:
190
+ return 'paused'
191
+ else:
192
+ return 'running'
193
+
194
+ def __repr__(self) -> str:
195
+ return f"FlowController(status='{self.status}')"