ksaa 2025.9.post2__py3-none-any.whl → 2025.11__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 (354) hide show
  1. kaa/__init__.py +0 -0
  2. kaa/application/__init__.py +0 -0
  3. kaa/application/core/misc_core.py +4 -1
  4. kaa/application/services/__init__.py +0 -0
  5. kaa/application/services/config_service.py +101 -0
  6. kaa/application/services/feedback_service.py +121 -0
  7. kaa/application/services/produce_solution_service.py +83 -0
  8. kaa/application/services/task_service.py +145 -0
  9. kaa/application/services/update_service.py +208 -0
  10. kaa/application/ui/__init__.py +0 -0
  11. kaa/application/ui/common.py +123 -0
  12. kaa/application/ui/components/alert.py +251 -0
  13. kaa/application/ui/components/alert.pyi +257 -0
  14. kaa/application/ui/components/categorized_select.pyi +697 -0
  15. kaa/application/ui/facade.py +189 -0
  16. kaa/application/ui/gradio_view.py +135 -0
  17. kaa/application/ui/views/__init__.py +0 -0
  18. kaa/application/ui/views/feedback_view.py +65 -0
  19. kaa/application/ui/views/produce_view.py +251 -0
  20. kaa/application/ui/views/settings_view.py +581 -0
  21. kaa/application/ui/views/status_view.py +171 -0
  22. kaa/application/ui/views/task_view.py +102 -0
  23. kaa/application/ui/views/update_view.py +106 -0
  24. kaa/config/schema.py +26 -6
  25. kaa/errors.py +75 -0
  26. kaa/main/dmm_host.py +60 -60
  27. kaa/main/gr.py +60 -3115
  28. kaa/main/kaa.py +11 -3
  29. kaa/metadata.py +10 -0
  30. kaa/resources/__pycache__/__init__.cpython-310.pyc +0 -0
  31. kaa/resources/game.db +0 -0
  32. kaa/resources/game_ver.txt +0 -0
  33. kaa/resources/idol_cards/i_card-skin-amao-3-014_1.png +0 -0
  34. kaa/resources/idol_cards/i_card-skin-amao-3-015_0.png +0 -0
  35. kaa/resources/idol_cards/i_card-skin-amao-3-015_1.png +0 -0
  36. kaa/resources/idol_cards/i_card-skin-atbm-1-000_0.png +0 -0
  37. kaa/resources/idol_cards/i_card-skin-atbm-1-000_1.png +0 -0
  38. kaa/resources/idol_cards/i_card-skin-atbm-2-000_0.png +0 -0
  39. kaa/resources/idol_cards/i_card-skin-atbm-2-000_1.png +0 -0
  40. kaa/resources/idol_cards/i_card-skin-atbm-3-000_0.png +0 -0
  41. kaa/resources/idol_cards/i_card-skin-atbm-3-000_1.png +0 -0
  42. kaa/resources/idol_cards/i_card-skin-fktn-3-016_0.png +0 -0
  43. kaa/resources/idol_cards/i_card-skin-fktn-3-016_1.png +0 -0
  44. kaa/resources/idol_cards/i_card-skin-hmsz-3-003_0.png +0 -0
  45. kaa/resources/idol_cards/i_card-skin-hmsz-3-003_1.png +0 -0
  46. kaa/resources/idol_cards/i_card-skin-hmsz-3-009_0.png +0 -0
  47. kaa/resources/idol_cards/i_card-skin-hmsz-3-009_1.png +0 -0
  48. kaa/resources/idol_cards/i_card-skin-hmsz-3-013_0.png +0 -0
  49. kaa/resources/idol_cards/i_card-skin-hmsz-3-013_1.png +0 -0
  50. kaa/resources/idol_cards/i_card-skin-hrnm-3-015_0.png +0 -0
  51. kaa/resources/idol_cards/i_card-skin-hrnm-3-015_1.png +0 -0
  52. kaa/resources/idol_cards/i_card-skin-hski-3-015_0.png +0 -0
  53. kaa/resources/idol_cards/i_card-skin-hski-3-015_1.png +0 -0
  54. kaa/resources/idol_cards/i_card-skin-hume-3-009_0.png +0 -0
  55. kaa/resources/idol_cards/i_card-skin-hume-3-009_1.png +0 -0
  56. kaa/resources/idol_cards/i_card-skin-hume-3-015_0.png +0 -0
  57. kaa/resources/idol_cards/i_card-skin-hume-3-015_1.png +0 -0
  58. kaa/resources/idol_cards/i_card-skin-jsna-3-009_0.png +0 -0
  59. kaa/resources/idol_cards/i_card-skin-jsna-3-009_1.png +0 -0
  60. kaa/resources/idol_cards/i_card-skin-jsna-3-014_0.png +0 -0
  61. kaa/resources/idol_cards/i_card-skin-jsna-3-014_1.png +0 -0
  62. kaa/resources/idol_cards/i_card-skin-kcna-3-014_0.png +0 -0
  63. kaa/resources/idol_cards/i_card-skin-kcna-3-014_1.png +0 -0
  64. kaa/resources/idol_cards/i_card-skin-kcna-3-015_0.png +0 -0
  65. kaa/resources/idol_cards/i_card-skin-kcna-3-015_1.png +0 -0
  66. kaa/resources/idol_cards/i_card-skin-kllj-3-014_0.png +0 -0
  67. kaa/resources/idol_cards/i_card-skin-kllj-3-014_1.png +0 -0
  68. kaa/resources/idol_cards/i_card-skin-shro-3-015_0.png +0 -0
  69. kaa/resources/idol_cards/i_card-skin-shro-3-015_1.png +0 -0
  70. kaa/resources/idol_cards/i_card-skin-ssmk-3-015_0.png +0 -0
  71. kaa/resources/idol_cards/i_card-skin-ssmk-3-015_1.png +0 -0
  72. kaa/resources/idol_cards/i_card-skin-ttmr-3-015_0.png +0 -0
  73. kaa/resources/idol_cards/i_card-skin-ttmr-3-015_1.png +0 -0
  74. kaa/sprites/0cdd57aa-9bdd-4c83-9565-de7c229b34e3.png +0 -0
  75. kaa/sprites/16f04ca1-05a3-49c7-88ae-a0d02c1ec2ec.png +0 -0
  76. kaa/sprites/20ca6d42-c19e-4c3e-b82c-f6642bbe5069.png +0 -0
  77. kaa/sprites/276c0178-65af-4b69-a3c3-d04253b9ed91.png +0 -0
  78. kaa/sprites/32de6ba0-859f-4f40-a57e-92bed913e3a7.png +0 -0
  79. kaa/sprites/38613745-1cf2-4bf1-9f58-0474b1e46895.png +0 -0
  80. kaa/sprites/41904062-e218-4b28-972a-b5cfcd058d2c.png +0 -0
  81. kaa/sprites/44097699-487f-4932-846a-095a427f4ed8.png +0 -0
  82. kaa/sprites/459f532f-115c-4527-a897-affa7ddcbd76.png +0 -0
  83. kaa/sprites/46cb87f6-26f7-45f0-b10c-6491f115077e.png +0 -0
  84. kaa/sprites/4b02f8d4-7847-47a5-8f01-7ab5123709e5.png +0 -0
  85. kaa/sprites/4b7426e9-395a-410c-bb99-c6f0a7b81208.png +0 -0
  86. kaa/sprites/4c243ed4-9ac2-42ba-89c6-946ffef84120.png +0 -0
  87. kaa/sprites/527c3803-1ddd-4f6b-a896-815d3c2e4caa.png +0 -0
  88. kaa/sprites/54d67100-70c7-4a00-afaa-a80771d34893.png +0 -0
  89. kaa/sprites/5588396d-dd38-4ef1-8c37-3041a750b1cc.png +0 -0
  90. kaa/sprites/5933f6ab-7af4-4731-aac8-d95939a3fbbf.png +0 -0
  91. kaa/sprites/5c49d3b3-656e-4c8c-ae1a-9b0209b9dcc3.png +0 -0
  92. kaa/sprites/5c735cc5-7801-44e9-a8f1-2d44df4fa919.png +0 -0
  93. kaa/sprites/678073ec-6700-4d94-af41-b392af11201b.png +0 -0
  94. kaa/sprites/6a27faad-df3b-49d9-a20f-64ae8e8f9e22.png +0 -0
  95. kaa/sprites/6b63fca9-a1ec-490e-96e9-fc0beeeb8851.png +0 -0
  96. kaa/sprites/6baaecdd-2817-4ba3-a2da-346ef9ef92fd.png +0 -0
  97. kaa/sprites/6c82d28f-913c-4444-baa6-2e37c7e24bc6.png +0 -0
  98. kaa/sprites/6d302c9d-72cd-49b5-bb11-c39140ffbdd1.png +0 -0
  99. kaa/sprites/6e603eb6-2336-46d2-ad01-2ba2fc3d4cbd.png +0 -0
  100. kaa/sprites/7156a1b5-cdd7-446e-9caf-d9f6b313ef4d.png +0 -0
  101. kaa/sprites/7469d458-d308-4469-8bf1-b63e1ddc6a3c.png +0 -0
  102. kaa/sprites/78868e34-1c2a-40ee-b08c-c89e6a3aea54.png +0 -0
  103. kaa/sprites/7c8e37b7-173f-4c03-8f11-bbd47f2a02c2.png +0 -0
  104. kaa/sprites/7ce3a2d5-efa7-4091-b012-f2870d9df422.png +0 -0
  105. kaa/sprites/7db2d761-ab44-4b3b-85aa-9b9758f97d5d.png +0 -0
  106. kaa/sprites/7ddaa9db-2287-43ba-8531-065e618ac8ef.png +0 -0
  107. kaa/sprites/7def2661-0847-4459-88c6-5270c2e8a32c.png +0 -0
  108. kaa/sprites/821c86c1-bc35-40bc-95db-f66935a92299.png +0 -0
  109. kaa/sprites/8448237d-2db5-49ca-8d55-9fc0e56639a7.png +0 -0
  110. kaa/sprites/89663a2f-c6d3-4bb7-9935-a54d11d0a382.png +0 -0
  111. kaa/sprites/8a312cf5-c670-4a52-a03b-fe55e78f8f07.png +0 -0
  112. kaa/sprites/8b6a1eed-341d-4be1-b0ae-9adc52ed4543.png +0 -0
  113. kaa/sprites/8e024c75-c872-4e18-bb80-0c5894612815.png +0 -0
  114. kaa/sprites/92f7ef0b-7931-4cd3-afda-c553232eb443.png +0 -0
  115. kaa/sprites/9803a197-d873-43cc-9656-d63513e92fad.png +0 -0
  116. kaa/sprites/9934f05f-ee5f-4131-b08d-aa61d5f3505a.png +0 -0
  117. kaa/sprites/993f2140-77be-4fe2-8e11-f71c4531bc9a.png +0 -0
  118. kaa/sprites/9a8bfa87-0e37-4d54-888f-b79f8778d232.png +0 -0
  119. kaa/sprites/9b7eaa4c-2b67-4426-80da-b7538a87ed96.png +0 -0
  120. kaa/sprites/9bc6607d-ce44-467e-a5f4-b3cf30210c13.png +0 -0
  121. kaa/sprites/9db5de82-8fba-4b69-8faf-ffe7fcea32f8.png +0 -0
  122. kaa/sprites/__pycache__/__init__.cpython-310.pyc +0 -0
  123. kaa/sprites/a1251011-00a9-402b-82da-1d2744db26cf.png +0 -0
  124. kaa/sprites/a31e08c1-d3c3-4a99-a882-4a10de9ba815.png +0 -0
  125. kaa/sprites/a408adaa-bfab-4aaf-acf7-08a4a3ebdd4c.png +0 -0
  126. kaa/sprites/a408cdfd-e394-4ada-9f0e-5878e41b8e52.png +0 -0
  127. kaa/sprites/a4723b5b-2466-4ea4-b7ef-4185bb4dda74.png +0 -0
  128. kaa/sprites/a5ba613c-46f4-4442-88e1-fbda4dd65a55.png +0 -0
  129. kaa/sprites/a88e3da2-98d3-4a64-b7ac-040847028a83.png +0 -0
  130. kaa/sprites/a9868db0-b355-470e-9bbe-033c5efa4b5e.png +0 -0
  131. kaa/sprites/a9f020e9-0e4b-4ddc-b8b8-386009578c15.png +0 -0
  132. kaa/sprites/aa369159-cc3b-40f2-9fca-bca5b579cf97.png +0 -0
  133. kaa/sprites/aad1ac7d-cfa1-4459-9d46-d39c080bd78e.png +0 -0
  134. kaa/sprites/ab9e39ae-0856-4254-a6b6-585e82f8bf3b.png +0 -0
  135. kaa/sprites/ae4742aa-acda-442d-bf73-b3fe7b66e85c.png +0 -0
  136. kaa/sprites/aff3d8ac-d648-434c-9adb-6c1a10aa04b9.png +0 -0
  137. kaa/sprites/affc5006-4b67-4490-9fa7-557a7fecb133.png +0 -0
  138. kaa/sprites/b002896d-5093-48e5-9e01-d18bfb240961.png +0 -0
  139. kaa/sprites/b00b5890-3743-4064-8311-c5e35b3afbe6.png +0 -0
  140. kaa/sprites/b26ac4ff-42a0-45aa-b818-7d7603b52f12.png +0 -0
  141. kaa/sprites/b2a04604-15d4-4ff6-86f7-0823e642103f.png +0 -0
  142. kaa/sprites/b33bb89c-cabb-4fe0-822f-d911e8b42f82.png +0 -0
  143. kaa/sprites/b5584de5-650e-4fde-b743-ccbd02eab196.png +0 -0
  144. kaa/sprites/b5f80604-d7d3-49e0-9934-58f75c393ffb.png +0 -0
  145. kaa/sprites/b70b958a-9480-4b9b-86a4-5ea099535c36.png +0 -0
  146. kaa/sprites/b93b3c36-79a3-4c1a-944a-20fe6a88c46d.png +0 -0
  147. kaa/sprites/ba7676fe-4982-4e92-822d-9c1a623420ac.png +0 -0
  148. kaa/sprites/bb953e97-7185-4c2c-b177-48d743e90d96.png +0 -0
  149. kaa/sprites/bd176e60-3bb2-453f-b2ef-45305b7f21c1.png +0 -0
  150. kaa/sprites/c0478a52-d6c5-4029-848f-e3e22b2de858.png +0 -0
  151. kaa/sprites/c05baa1e-02c3-4d02-904f-76086edbb97f.png +0 -0
  152. kaa/sprites/c295af98-0b43-4f18-bbc3-e59d23055f1b.png +0 -0
  153. kaa/sprites/c424f4a1-d032-478d-ac82-b1127a429e52.png +0 -0
  154. kaa/sprites/c5356ad6-0f1e-42be-b090-059f33ea7cee.png +0 -0
  155. kaa/sprites/c54d4b7e-887c-4753-8a9e-6914fc408232.png +0 -0
  156. kaa/sprites/c68f1282-4d86-4b1a-b76b-dcf42f3f906b.png +0 -0
  157. kaa/sprites/c71f900b-9689-4800-89a0-e3371d33f47a.png +0 -0
  158. kaa/sprites/c7cad813-2326-4662-9f55-0a15e1dad3dc.png +0 -0
  159. kaa/sprites/c95e8a90-dd6e-4cca-b74b-85ee90dbd5c1.png +0 -0
  160. kaa/sprites/c98d6bc7-45ca-4877-8cd1-6f36d6a86a02.png +0 -0
  161. kaa/sprites/ca258eea-e69a-43c3-ad4c-ac7da0099011.png +0 -0
  162. kaa/sprites/cb8d5df6-300b-48e5-ae0b-2b4750c9c092.png +0 -0
  163. kaa/sprites/cc727107-139b-4092-bafb-4bc9787bcdfd.png +0 -0
  164. kaa/sprites/cd601176-0336-41c7-8d3f-31ddf8cf708d.png +0 -0
  165. kaa/sprites/cda022d1-7898-4006-95c5-b55eceda9d95.png +0 -0
  166. kaa/sprites/ce22506b-7410-4039-94de-dc1bdba8ac00.png +0 -0
  167. kaa/sprites/cea2d5c4-6e79-425e-9e98-7e7dbdee960b.png +0 -0
  168. kaa/sprites/cf302432-c592-4b6f-a6de-53c3e1bef23e.png +0 -0
  169. kaa/sprites/d0d9f650-bb30-4743-81f3-cd7ea8211d5c.png +0 -0
  170. kaa/sprites/d14a1c6c-339a-4dfe-9078-950d3518bc3b.png +0 -0
  171. kaa/sprites/d277a9c3-2b2c-42fc-9fbb-a836413c8cf4.png +0 -0
  172. kaa/sprites/d50d4438-65bc-4849-9a11-7e8b796e1b9d.png +0 -0
  173. kaa/sprites/d5407f49-fecc-404e-915a-22c9fa16a79a.png +0 -0
  174. kaa/sprites/d73e9e03-b910-42c0-9953-34980bff09f5.png +0 -0
  175. kaa/sprites/d8485972-4e67-4190-8d95-60fd3c68b2ce.png +0 -0
  176. kaa/sprites/d963e76b-3c25-4bc7-a307-78d97728a819.png +0 -0
  177. kaa/sprites/dab08e98-9053-4e2a-9e74-cf30298fea22.png +0 -0
  178. kaa/sprites/db25f60d-4772-44f9-85dc-aba4b20cffa2.png +0 -0
  179. kaa/sprites/dcbeb7cd-cd57-4daf-b023-ff2c7ef1621a.png +0 -0
  180. kaa/sprites/dd6f31d1-510d-4425-a160-958e7e984382.png +0 -0
  181. kaa/sprites/de047f08-cef2-4b22-846e-fba2a06f8996.png +0 -0
  182. kaa/sprites/df11b01f-2ab1-4bfb-831f-bb8dae6a092b.png +0 -0
  183. kaa/sprites/e2f64c10-4db0-47c8-b23d-94802493d6fb.png +0 -0
  184. kaa/sprites/e4be69f6-6030-4fd8-8a36-fd3938d1734b.png +0 -0
  185. kaa/sprites/e4d0719b-d127-41a0-a250-492f79bf5625.png +0 -0
  186. kaa/sprites/e5196344-d61a-48ff-bc48-63f8f63c3788.png +0 -0
  187. kaa/sprites/e6f8f2a8-39e3-45e1-8a52-f2dc250bcd95.png +0 -0
  188. kaa/sprites/e70ce43b-9b3b-4aa0-8b9c-1d5cc4a1d5b0.png +0 -0
  189. kaa/sprites/e7c00f25-b15a-4403-a201-07b9b168e9f5.png +0 -0
  190. kaa/sprites/e82b9645-ee3f-4f63-922e-c1d703344baf.png +0 -0
  191. kaa/sprites/e9fad64b-e4e2-46cb-a820-b2860b38da03.png +0 -0
  192. kaa/sprites/eab111a6-80bf-45c1-9a74-b44ca76508b7.png +0 -0
  193. kaa/sprites/ec0d03db-7c66-46b7-86cc-4acaa6eba1c9.png +0 -0
  194. kaa/sprites/edd2d38d-9071-4094-8931-1a68b6ebe1b8.png +0 -0
  195. kaa/sprites/eeff3d53-5a40-44c2-9d09-8f246157c72b.png +0 -0
  196. kaa/sprites/ef0db31a-fc44-4972-af7e-21c50f0b139c.png +0 -0
  197. kaa/sprites/f1fa2fc3-c3d4-4040-90f1-7941b98dcee5.png +0 -0
  198. kaa/sprites/f31531b4-79ea-41dc-b23f-e445b818139c.png +0 -0
  199. kaa/sprites/f35e19ca-7de7-4721-af53-b5f95c5e74fe.png +0 -0
  200. kaa/sprites/f5e1e7ed-fd35-42c3-97ed-5e9214a50733.png +0 -0
  201. kaa/sprites/f7246b84-e70b-47d3-8166-90f0dc4f2bb6.png +0 -0
  202. kaa/sprites/f7e8e937-1eda-4bd3-a3cf-d306cb1973e2.png +0 -0
  203. kaa/sprites/f8babf59-d55e-40c2-a0b7-5d743ab71799.png +0 -0
  204. kaa/sprites/f9697f23-07cf-4a14-8b22-d1fc95386c16.png +0 -0
  205. kaa/sprites/fad8b65b-7d9e-49d8-9f71-6d078aa672ca.png +0 -0
  206. kaa/sprites/fb017237-bac6-4b73-9437-4518e764ef45.png +0 -0
  207. kaa/sprites/fc86085c-d708-498a-bfa6-92e1a1364c60.png +0 -0
  208. kaa/tasks/R.py +140 -128
  209. kaa/tasks/common.py +5 -0
  210. kaa/tasks/daily/contest.py +2 -1
  211. kaa/tasks/daily/purchase.py +34 -2
  212. kaa/tasks/daily/upgrade_support_card.py +12 -2
  213. kaa/tasks/produce/cards.py +5 -4
  214. kaa/tasks/produce/common.py +202 -105
  215. kaa/tasks/produce/in_purodyuusu.py +35 -17
  216. kaa/tasks/produce/non_lesson_actions.py +10 -33
  217. kaa/tasks/produce/p_drink.py +5 -1
  218. kaa/tasks/produce/produce.py +12 -1
  219. kaa/tasks/start_game.py +61 -5
  220. kaa/util/reactive.py +182 -0
  221. {ksaa-2025.9.post2.dist-info → ksaa-2025.11.dist-info}/METADATA +31 -27
  222. {ksaa-2025.9.post2.dist-info → ksaa-2025.11.dist-info}/RECORD +354 -176
  223. /kaa/sprites/{3504b421-58f2-4ca0-869e-764335d60406.png → 001486b2-f627-4ece-8c93-4dfec47da9b3.png} +0 -0
  224. /kaa/sprites/{7dc79a8b-bd59-4c93-a638-e49bca276c00.png → 011e352f-6e8b-4225-b104-d1b2ccf0761c.png} +0 -0
  225. /kaa/sprites/{69f15152-8ee9-4e39-9bfa-6cd1b051bc1e.png → 0240342e-b6f3-4ed6-8da5-a64092e6fcde.png} +0 -0
  226. /kaa/sprites/{15801cb7-24f6-4504-921b-5e5a37bdc064.png → 02df555c-b19c-4776-8f1b-d1a7e2ed2f82.png} +0 -0
  227. /kaa/sprites/{55a2ba58-2412-4c39-9c6e-2627092623a2.png → 0343a3b8-8cd6-47af-b956-760dd2c79711.png} +0 -0
  228. /kaa/sprites/{d62dab9b-d36d-490e-bca5-bfd4b66911d7.png → 04c287ed-9ab1-4cd1-af07-8ab61f7f49bc.png} +0 -0
  229. /kaa/sprites/{8d99cd97-ae34-488f-b62e-54d54f75e4e8.png → 05d7740b-c6ea-453a-b805-7d0a5fe18184.png} +0 -0
  230. /kaa/sprites/{66ccbeba-3291-4f1c-8669-d691f24ea9ed.png → 06a7097c-fef6-4f2c-bece-26eed414708e.png} +0 -0
  231. /kaa/sprites/{609a340b-2cfa-4cef-9e55-fb60c7cabdbb.png → 072b167f-a2f6-4458-815e-a92ca2e6c9eb.png} +0 -0
  232. /kaa/sprites/{e881ee18-154e-4f0c-91fe-4699d156671d.png → 0899a6e7-6617-4f11-a7a2-9ba377ad6cba.png} +0 -0
  233. /kaa/sprites/{689eebfd-984c-45c9-812f-26cf9c756bed.png → 0950d760-6d88-4265-b28c-0790a782a749.png} +0 -0
  234. /kaa/sprites/{76d5d7f7-b23d-4802-8f91-7058f7adcffd.png → 0a019c0d-26fa-4441-b281-b73060722bc9.png} +0 -0
  235. /kaa/sprites/{ac194536-54e4-49e7-992c-fc82ba401f10.png → 0a27a783-0ef8-4f99-8c26-cf89768b5146.png} +0 -0
  236. /kaa/sprites/{06ee3b1e-5c99-4ebf-8a23-d6f59cda32b8.png → 0af981f5-bcc9-48eb-a392-50f79ec5149a.png} +0 -0
  237. /kaa/sprites/{e7709d81-890b-438f-8acb-50492d63022e.png → 0afb71f5-0752-47af-9ba2-85bdbe4fcbf7.png} +0 -0
  238. /kaa/sprites/{62cc930f-acc2-424a-975f-164009a555f8.png → 0b83c2e5-ebb4-4a69-8b5f-658774d4c5a6.png} +0 -0
  239. /kaa/sprites/{ad63136d-2360-45e6-b7ef-00e01612c7ce.png → 0be5a84d-eb1e-4bb3-afcd-923e104190f4.png} +0 -0
  240. /kaa/sprites/{be2985ff-c3ac-4d94-9751-c652dd561db2.png → 0c192ca4-dd10-49b4-b0be-9b1fb4637910.png} +0 -0
  241. /kaa/sprites/{bca22a82-f72b-46ca-be06-c1e18b7ad27c.png → 0cae32b3-0248-49ad-98dd-d06a89308797.png} +0 -0
  242. /kaa/sprites/{a694f6e8-6ef9-4761-816a-ad586ce93654.png → 0d1fd6f0-3a6f-4008-a31a-8189dd09be50.png} +0 -0
  243. /kaa/sprites/{cde4da05-d654-4217-8443-752bc0709742.png → 0d2143fa-7e81-4e7a-8a94-c3839719de5b.png} +0 -0
  244. /kaa/sprites/{901b48f9-d7b7-46c2-85fa-b07e427ffdaa.png → 0dbb752b-e60a-459d-80fa-f5b49e8b8e4d.png} +0 -0
  245. /kaa/sprites/{80fa9c6e-fe05-4d3e-ae50-6b7e9a237e0b.png → 0e63e0b7-6874-46e9-86de-8c23e6491f24.png} +0 -0
  246. /kaa/sprites/{603c5369-9247-404d-bc6d-4719d11de6ea.png → 0f4ac9ae-5e4d-4f3f-b9e0-b76570f2b040.png} +0 -0
  247. /kaa/sprites/{7f90beb9-8712-4a53-a818-ff7a5e82d71d.png → 0f8026e8-62a9-49e5-97a6-0d78f33dcfcf.png} +0 -0
  248. /kaa/sprites/{6bcebf50-764b-4bf2-be8e-ba241d8550d7.png → 0f80ac6f-bdf8-4927-8393-c554209f517a.png} +0 -0
  249. /kaa/sprites/{4396a9b8-b7f0-45f3-8a8f-85773a10eecb.png → 116d3f3a-0255-47ed-b656-c26b41a5cfa8.png} +0 -0
  250. /kaa/sprites/{9a0172aa-9489-4985-9c65-c5f6db0a0325.png → 130cd7fd-767e-4177-9087-71f418f154dd.png} +0 -0
  251. /kaa/sprites/{333a1e30-82d3-45b9-8e48-4c127bcb5ec6.png → 138a527b-2191-4f0f-9e36-1d08bbf6ffb1.png} +0 -0
  252. /kaa/sprites/{cee6ca8f-219c-448d-9ba6-d6722f27b600.png → 1472da80-4cbb-41e1-94e8-3ea3defd3b6f.png} +0 -0
  253. /kaa/sprites/{94121408-8cd2-46fc-bf27-33b207e4cba8.png → 14875449-7478-4969-a2d5-91ce34c9d1e4.png} +0 -0
  254. /kaa/sprites/{15da6f78-d89f-480c-9cce-c95e009fd15a.png → 1539fcff-8842-478f-98a8-0d1a885d1b43.png} +0 -0
  255. /kaa/sprites/{97329116-4d2e-40d7-af96-938afdac4e60.png → 16113270-3f35-498a-a29a-52f190d77cc1.png} +0 -0
  256. /kaa/sprites/{545068ad-6f17-40d8-99e1-d26bd5700c67.png → 1617c180-ce9f-461a-bfdc-390337ecb205.png} +0 -0
  257. /kaa/sprites/{e38a2f35-9ac5-4363-bd6d-09893907ea71.png → 16cf9d8b-4f8e-47ce-a63d-fe9bfc3f31e8.png} +0 -0
  258. /kaa/sprites/{4fc6fae5-386d-434c-893d-5450bd5a6396.png → 1b485aa9-f0c0-4b49-ba75-363a8d6e292c.png} +0 -0
  259. /kaa/sprites/{4995d301-375b-4d23-a6e5-b26a771fcbe4.png → 1d560b06-feac-413d-bf90-07ac045f11fb.png} +0 -0
  260. /kaa/sprites/{e16b116c-6a2c-4e5d-8105-8dfc8c4c730f.png → 1d92cb8e-49a7-4166-9682-a5807e0c7912.png} +0 -0
  261. /kaa/sprites/{be04f096-c46b-4784-8e6c-c1217764f6dd.png → 1dadf765-ddd0-4768-9e91-6d3d08d0ca0d.png} +0 -0
  262. /kaa/sprites/{d0652541-e289-4bcd-98ec-a88715f5adef.png → 20cbf6b8-01db-45fe-a790-dbb7dd639b1d.png} +0 -0
  263. /kaa/sprites/{8c4f60d1-d707-4ae5-bd94-d96661ea7bbc.png → 20d01fc0-9a54-4ef5-9983-2bb8414dd94d.png} +0 -0
  264. /kaa/sprites/{011c52b9-613d-4865-afd3-1f0ddab2bc5b.png → 20e1e948-6920-44cd-965e-9e1e37b3fbf2.png} +0 -0
  265. /kaa/sprites/{5ee1b47b-f20d-431b-a0bb-d203b9f3e15b.png → 23922edd-22aa-4514-ba99-65bcd72c2ad1.png} +0 -0
  266. /kaa/sprites/{3d57da0a-b166-4acd-9a8a-b247ba5ad13a.png → 24c2db22-24e2-484e-8c16-8b1c9fe3ed14.png} +0 -0
  267. /kaa/sprites/{f5145781-949e-4012-9ea9-b0a277dac72b.png → 26683d54-74a9-4e7b-a839-c2e0850d1458.png} +0 -0
  268. /kaa/sprites/{2987cb9c-5975-41b3-87f7-cc26efdcee74.png → 27bcc513-9bab-468a-97b8-ed16f918e2a4.png} +0 -0
  269. /kaa/sprites/{c24394f6-ed02-4fb4-8d58-40a7e51b259d.png → 284a19c3-2c06-48a5-8f78-b4682e8ae19e.png} +0 -0
  270. /kaa/sprites/{da615a2f-3334-462c-8535-40067ad1c857.png → 2927cceb-29dc-4c35-8614-1df42fc0733b.png} +0 -0
  271. /kaa/sprites/{0a430b29-9841-407a-a102-ea6420beeaae.png → 2c5784df-e89b-4b95-a4b9-bac083c0ec08.png} +0 -0
  272. /kaa/sprites/{3867054e-a686-4dab-89fb-6a9def58dfe1.png → 2dfa926c-83c5-4f40-adfe-ff04ce448264.png} +0 -0
  273. /kaa/sprites/{d1a6547e-7c58-4500-a7f4-cf462f44395f.png → 2f7f70ea-f3f0-4d14-8885-1a735ff00545.png} +0 -0
  274. /kaa/sprites/{4a5e8c39-4e80-4202-9886-be1a58073fd6.png → 307fcb37-7a90-4a3e-bd3a-64a691aeb5d5.png} +0 -0
  275. /kaa/sprites/{151dc2ce-fce4-4741-ac6e-cc8213f54137.png → 3114b2d5-0a96-4eba-9143-b09eabb63612.png} +0 -0
  276. /kaa/sprites/{1aa14c42-8682-4a75-b4a2-4715481a2381.png → 31168fe2-8601-4de6-aa40-81e7ce452391.png} +0 -0
  277. /kaa/sprites/{f3400d73-4f61-4027-b5d8-03765a63a186.png → 31b025df-ed25-4bc4-a94a-d05388e9156e.png} +0 -0
  278. /kaa/sprites/{0c211c20-096e-4572-8e74-3867904db1f6.png → 32af48d2-fb5b-4288-930f-eb36edfe2116.png} +0 -0
  279. /kaa/sprites/{0803c844-7085-4492-ad68-0ce3b11f9840.png → 34e50d77-7b95-4bb7-8c62-ad6801dc0e55.png} +0 -0
  280. /kaa/sprites/{bd5b168a-de03-46b3-b1b7-9fa9667f4642.png → 3658a26d-98d3-462a-abf4-4d8b74aeb134.png} +0 -0
  281. /kaa/sprites/{562deb0a-99bd-4e95-80ff-f933a73c28ad.png → 36f11c55-e110-44b7-a800-85f4d7584560.png} +0 -0
  282. /kaa/sprites/{9c827df9-2e47-4bec-95b7-8ccad6377a28.png → 38bb2a90-b2a9-4f85-9025-662e83ab1ed1.png} +0 -0
  283. /kaa/sprites/{eebbccac-25a9-465e-b7c8-3bfdaf0bf0b9.png → 38f70a7e-5f7e-4ee6-a7c5-5f8014734719.png} +0 -0
  284. /kaa/sprites/{6f79221a-8c47-423e-ac72-27d97c559d63.png → 3aceddce-09db-422a-8023-e08d41070cc8.png} +0 -0
  285. /kaa/sprites/{3fe4ec28-3b2a-4042-ace1-ceec5c8f48b1.png → 3c224540-1571-435a-b8e6-2317e4411438.png} +0 -0
  286. /kaa/sprites/{f2913d0b-342c-4de0-8235-fe3f1c580d2e.png → 3d9a9493-7616-4b2b-867e-44e776d831c0.png} +0 -0
  287. /kaa/sprites/{a8bef06b-9287-4f54-b96e-19463031d26f.png → 3fd694ea-8396-4cf3-9ba6-eaf95b61e8c8.png} +0 -0
  288. /kaa/sprites/{fd16e035-699c-4dc9-9721-61443cd6fd63.png → 4055dbef-1523-43b7-9183-6b2721b4307b.png} +0 -0
  289. /kaa/sprites/{606d4740-8af9-40f6-af18-ad9f73cecf68.png → 4059ec2d-cd20-4dd3-8586-e49a4cfa8717.png} +0 -0
  290. /kaa/sprites/{9ea3c9b4-80f3-44b9-90e3-cfc406f814be.png → 4280fa1d-4223-4fc4-85f9-caf6157cb004.png} +0 -0
  291. /kaa/sprites/{436ec8e4-aa4a-4b1f-940a-2ab55baa098d.png → 43bc2116-7fe3-4b20-8c8b-3ab2a2b5dd7d.png} +0 -0
  292. /kaa/sprites/{39815c36-38c5-4066-aff5-2177c232b4f4.png → 43d3fe01-27c0-4d3f-a908-aebd00ab219d.png} +0 -0
  293. /kaa/sprites/{9c6378a9-62ed-4881-bc6f-2166bbb470c7.png → 45062c9b-a31d-457b-83a2-9b6891384a15.png} +0 -0
  294. /kaa/sprites/{e630b33a-e73e-4271-96b9-860b94fb48ca.png → 4516d0e4-9280-4f91-ba7d-fc8af83a9996.png} +0 -0
  295. /kaa/sprites/{916391a4-09b7-409c-ba25-391b61a65e62.png → 458b8bbe-9fa5-48d4-907b-66d36baf16a1.png} +0 -0
  296. /kaa/sprites/{9092a171-66f0-47dc-99dd-bb5d7359077a.png → 45af668c-2324-4fe4-83e0-11d65cfaa086.png} +0 -0
  297. /kaa/sprites/{297fb9e0-9553-40fb-ad08-f5ab718ef119.png → 45bdb006-bea5-4a1f-bf1a-c3bcb2e99abd.png} +0 -0
  298. /kaa/sprites/{1bf95df3-255f-4e89-9b17-0af77f4affd6.png → 47186b16-f478-4a53-8e49-a132ee4c78db.png} +0 -0
  299. /kaa/sprites/{1f6805bb-dff1-4d4c-82bf-74563056cc81.png → 4876c6f3-17a5-4858-b1d1-ebb5619516c5.png} +0 -0
  300. /kaa/sprites/{b2c8ac50-87a3-4cdd-a895-9704a9674163.png → 491bd489-7a08-47b4-b1b7-5d0f44cedbca.png} +0 -0
  301. /kaa/sprites/{484594b4-06a2-4ec3-8a85-e7af3b012125.png → 4a853ac5-24d5-44ac-8ddf-9f501c60a9be.png} +0 -0
  302. /kaa/sprites/{f6e26f3c-2548-4507-8651-53917e0fe82a.png → 4b88639e-1f17-4e11-90b6-bb122f8cb5ab.png} +0 -0
  303. /kaa/sprites/{935d8115-245f-42ff-ad74-55f4e6d140b1.png → 4dc5f817-86bc-4433-aeb5-b02c7b63fa8c.png} +0 -0
  304. /kaa/sprites/{b379d704-fc45-42f9-a45c-d81a6c5ee951.png → 4e3a9fd3-86aa-4e14-ae48-46e53a553da2.png} +0 -0
  305. /kaa/sprites/{7df9b645-ff88-4459-8ba7-450da538cfd9.png → 4e7f7b91-344c-4fb2-95fe-004831e81b11.png} +0 -0
  306. /kaa/sprites/{24b3cf5c-0586-41cc-abfb-df48de07fa77.png → 511232f6-01d6-482a-81a3-2bf79a6e88ed.png} +0 -0
  307. /kaa/sprites/{101a9c54-ab8c-4b5d-a4e0-686b93b4e1d9.png → 546cb160-f99b-4cda-9719-02b38a5393a0.png} +0 -0
  308. /kaa/sprites/{74e4c222-0006-4295-99d0-a01aa8b94dc2.png → 56bc2743-5dd9-4d09-a681-af9d1fec568b.png} +0 -0
  309. /kaa/sprites/{d11f07b1-f2e9-4c41-9ad9-81529121afd4.png → 5701ecbe-7fed-435c-b409-75f8b0f2df51.png} +0 -0
  310. /kaa/sprites/{6dd4ca2b-69ca-4099-9e69-2144ce9a4d28.png → 58de1cf0-4270-4987-9dc7-3f584f1119d1.png} +0 -0
  311. /kaa/sprites/{48eb2e27-7419-4136-9642-7af1a6f83ed9.png → 5a337f99-8554-48bf-acb7-364259fbf1b7.png} +0 -0
  312. /kaa/sprites/{ccad6a54-73f6-48b8-a86f-16b9e0f46e41.png → 5cb4deb9-2ec5-4452-aba4-390eae2071c7.png} +0 -0
  313. /kaa/sprites/{f136dd9f-43c8-44cc-b081-1f86d7ce38d4.png → 5d42f16f-3475-4ffb-ac85-3c86fa8fba46.png} +0 -0
  314. /kaa/sprites/{52aba1bd-1e82-46a7-8003-0aac11586a38.png → 6c5698a4-b211-4f38-83a7-51b5f3a36911.png} +0 -0
  315. /kaa/sprites/{0ec56b89-81f2-478d-907e-b04aab199e86.png → 771d1e17-8ef4-417a-b482-26db514e17c6.png} +0 -0
  316. /kaa/sprites/{c23c29cc-ef42-46d8-8b58-283b74a254cc.png → 7833fa1e-c842-4efd-a696-c67150a1dfa8.png} +0 -0
  317. /kaa/sprites/{2bb41b59-61f8-4109-a550-faa6a14708de.png → 791b3367-fb7c-48b5-9a0f-877908e51c4a.png} +0 -0
  318. /kaa/sprites/{47cbfa79-0b33-4ccc-b762-21e3a141a7bc.png → 7bb874a3-3fac-4138-83bd-d6461c5793d3.png} +0 -0
  319. /kaa/sprites/{fc77cc87-f8eb-4b33-834c-ee5d2af5ca44.png → 7d4d06a8-7903-4803-a20b-7f45ffbe9ca7.png} +0 -0
  320. /kaa/sprites/{b581ae5b-cb67-4bf9-8ffe-72b8ba56d887.png → 81553259-3b0a-42f7-b7b9-2c6e1dfec6c4.png} +0 -0
  321. /kaa/sprites/{eda88516-ac6b-4f26-b074-f8e6e2ae42a5.png → 827cec52-d883-46d1-b24b-c11c82b1b459.png} +0 -0
  322. /kaa/sprites/{9966acd5-973a-4afb-a006-c1fa66cfe679.png → 8473bc42-1909-4d70-b80d-7eba085c9978.png} +0 -0
  323. /kaa/sprites/{c16ae471-7d60-4c1f-9981-1090414a7ea0.png → 84797a78-97f3-44fa-b3c0-132c914f534a.png} +0 -0
  324. /kaa/sprites/{df54b1e4-035c-4937-9360-19b4733826e6.png → 861bd6b8-cca7-46b1-9346-83af080e211b.png} +0 -0
  325. /kaa/sprites/{436b9822-c946-4d2e-a432-8f3159705b37.png → 8f5081e4-ed3d-445b-bcf6-2e40c7d08298.png} +0 -0
  326. /kaa/sprites/{5bb33faa-8236-4e17-92bd-ad709795f261.png → 91dbaa9c-7881-4687-bd64-8f25c201d98c.png} +0 -0
  327. /kaa/sprites/{95e11d11-f330-4906-a128-6492f95176e8.png → 93f3f6a9-e9d2-45b7-a2b3-1e8b64d10e9b.png} +0 -0
  328. /kaa/sprites/{dd59548d-0800-49d3-8701-b2b2e8512c36.png → 943cde40-f190-40b6-9235-4439fbb4ae50.png} +0 -0
  329. /kaa/sprites/{7678902c-57df-4cf8-87ef-11c85f02214b.png → 97f3fb5a-fce2-4cc5-9da5-f0496e9dd294.png} +0 -0
  330. /kaa/sprites/{d051b4c3-354e-4d69-95f6-14f5017ed79e.png → 98a68736-c4ec-4af1-ac64-40eb48f9c000.png} +0 -0
  331. /kaa/sprites/{a9029026-7a7e-4f8e-9620-bd451bfdf284.png → 9af13381-661d-41cf-b704-e4a9c3691e14.png} +0 -0
  332. /kaa/sprites/{5a892ef3-543b-4fad-bdca-5358314d6be7.png → 9b91dc78-49e8-41b2-9500-c27cb46023c4.png} +0 -0
  333. /kaa/sprites/{920ea830-31a8-4256-b247-7b9507030783.png → 9d0bf64a-2684-43fa-b9bf-552e60c717b4.png} +0 -0
  334. /kaa/sprites/{b37685c3-74a8-4393-ad79-c445ab2d77be.png → a32fa779-2fb9-488e-9153-0b99e4352281.png} +0 -0
  335. /kaa/sprites/{9f81bc0f-da2c-4321-a185-b2d8a3f0d390.png → a6269fe5-e719-43ad-b674-ea728e43066b.png} +0 -0
  336. /kaa/sprites/{2beaba86-119e-4a1c-8cf4-e03c4d0d957a.png → a8ca6a10-ba0a-4416-a27b-3323d38040ab.png} +0 -0
  337. /kaa/sprites/{f258af39-e8be-4e19-aafc-bfedce9793c0.png → acaea032-45b8-4ad8-8b54-ff011750af80.png} +0 -0
  338. /kaa/sprites/{ed7adff5-1604-4ade-ac2b-a30167ef457c.png → ae5a051e-37f5-4e54-bfc7-64c445ad6fcc.png} +0 -0
  339. /kaa/sprites/{30604b47-7c8b-4dd5-b926-fd33bad575c7.png → af5a6ae7-a355-492e-86a8-62fa2b06bb54.png} +0 -0
  340. /kaa/sprites/{36e6c1ea-8de5-4640-8340-b8f532ef40c0.png → b406baf0-fa0b-4125-adc6-d370582a6104.png} +0 -0
  341. /kaa/sprites/{42bfc60c-0249-4910-ad04-80e559b52c6b.png → b62f9c30-1faf-47fc-be8f-36db3806c5dc.png} +0 -0
  342. /kaa/sprites/{56bcde1a-5cd7-493d-a2cd-2a63394c0c36.png → ba1983ce-bf63-4022-9ef3-dcf97030e6c5.png} +0 -0
  343. /kaa/sprites/{8e648675-1a52-4926-92cf-64c7cb5c2a59.png → ba971684-1fe3-47e1-8bdb-d20a0011ea68.png} +0 -0
  344. /kaa/sprites/{3af1e6de-e718-4a09-8ee9-88be21cd4b3d.png → bdbed86f-553c-458e-9435-d587005b367d.png} +0 -0
  345. /kaa/sprites/{e2944d47-159f-4200-b476-8d5db41d30c0.png → bee41ab5-c62e-4a79-a8d0-40f9efbc7e40.png} +0 -0
  346. /kaa/sprites/{bfaf3273-76bf-457a-88b7-6816af742a30.png → c344b05d-2707-43b0-9e3e-4ec433558dcb.png} +0 -0
  347. /kaa/sprites/{7df273dc-69f7-45b9-a782-b221783f851b.png → c3c495a2-1530-439f-a10e-76ca5d929cce.png} +0 -0
  348. /kaa/sprites/{76262c30-a2ee-45ba-8279-ca682cee0bd6.png → ccac6d8e-bca2-4ab6-b88b-c0f5ba83df84.png} +0 -0
  349. /kaa/sprites/{574b2741-c4ac-4ee4-a544-f4f499e4a21e.png → ce7cec02-4039-4171-87ed-37eccc4ac832.png} +0 -0
  350. /kaa/sprites/{6dead493-c4e6-4ba9-ac56-abe8c511abbd.png → f72e9d65-68e7-4bb6-be44-4b5ed71f0bce.png} +0 -0
  351. {ksaa-2025.9.post2.dist-info → ksaa-2025.11.dist-info}/WHEEL +0 -0
  352. {ksaa-2025.9.post2.dist-info → ksaa-2025.11.dist-info}/entry_points.txt +0 -0
  353. {ksaa-2025.9.post2.dist-info → ksaa-2025.11.dist-info}/licenses/LICENSE +0 -0
  354. {ksaa-2025.9.post2.dist-info → ksaa-2025.11.dist-info}/top_level.txt +0 -0
kaa/__init__.py ADDED
File without changes
File without changes
@@ -1,11 +1,14 @@
1
1
  import os
2
2
  import sys
3
3
 
4
- from kotonebot.interop.win.shortcut import create_shortcut
4
+ from kotonebot.util import is_windows, require_windows
5
+ if is_windows():
6
+ from kotonebot.interop.win.shortcut import create_shortcut
5
7
  from kaa.errors import LauncherNotFoundError
6
8
 
7
9
 
8
10
  def create_desktop_shortcut(start_immediately: bool):
11
+ require_windows('create_desktop_shortcut')
9
12
  exe_path = os.path.abspath('./kaa.exe')
10
13
  if not os.path.exists(exe_path):
11
14
  raise LauncherNotFoundError()
File without changes
@@ -0,0 +1,101 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ from kaa.config.schema import BaseConfig
5
+ from kotonebot.config.base_config import BackendConfig, UserConfig
6
+ from kotonebot.config.manager import load_config, save_config, RootConfig
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class ConfigValidationError(ValueError):
12
+ """Custom exception for configuration validation errors."""
13
+ pass
14
+
15
+
16
+ class ConfigService:
17
+ """
18
+ Manages application configuration, including loading, saving, and validation.
19
+ """
20
+
21
+ def __init__(self, config_path: str = "config.json"):
22
+ self.config_path = config_path
23
+ self._root_config: RootConfig[BaseConfig] | None = None
24
+ self._current_user_config: UserConfig[BaseConfig] | None = None
25
+ self.load()
26
+
27
+ def load(self):
28
+ """
29
+ Loads the configuration from the specified config file.
30
+ It populates the root configuration and sets the current user config.
31
+ """
32
+ self._root_config = load_config(self.config_path, type=BaseConfig)
33
+ if self._root_config and self._root_config.user_configs:
34
+ self._current_user_config = self._root_config.user_configs[0]
35
+ else:
36
+ raise FileNotFoundError(f"No user configuration found in {self.config_path}")
37
+
38
+ def reload(self):
39
+ """Reloads configuration from disk."""
40
+ self.load()
41
+ logger.info("Configuration reloaded from disk.")
42
+
43
+ def get_root_config(self) -> RootConfig[BaseConfig]:
44
+ """Returns the entire root configuration object."""
45
+ if not self._root_config:
46
+ raise RuntimeError("Root config not loaded.")
47
+ return self._root_config
48
+
49
+ def get_current_user_config(self) -> UserConfig[BaseConfig]:
50
+ """Returns the currently active user configuration."""
51
+ if not self._current_user_config:
52
+ raise RuntimeError("User config not loaded.")
53
+ return self._current_user_config
54
+
55
+ def get_options(self) -> BaseConfig:
56
+ """Returns the 'options' part of the current user configuration."""
57
+ if not self._current_user_config:
58
+ raise RuntimeError("User config not loaded.")
59
+ return self._current_user_config.options
60
+
61
+ def save(self):
62
+ """
63
+ Validates and saves the current configuration state to disk.
64
+ """
65
+ if not self._root_config or not self._current_user_config:
66
+ raise RuntimeError("Config not loaded, cannot save.")
67
+
68
+ self._validate(self._current_user_config.backend, self._current_user_config.options)
69
+ save_config(self._root_config, self.config_path)
70
+ logger.info("Configuration saved successfully to %s", self.config_path)
71
+
72
+ def _validate(self, backend_config: BackendConfig, options: BaseConfig):
73
+ """
74
+ Performs validation checks on the configuration.
75
+ Raises ConfigValidationError if any check fails.
76
+ """
77
+ # Rule 1: Validate screenshot method against the backend type
78
+ valid_screenshot_methods = {
79
+ 'mumu12': ['adb', 'adb_raw', 'uiautomator2', 'nemu_ipc'],
80
+ 'mumu12v5': ['adb', 'adb_raw', 'uiautomator2', 'nemu_ipc'],
81
+ 'leidian': ['adb', 'adb_raw', 'uiautomator2'],
82
+ 'custom': ['adb', 'adb_raw', 'uiautomator2'],
83
+ 'dmm': ['remote_windows', 'windows']
84
+ }
85
+ if backend_config.screenshot_impl not in valid_screenshot_methods.get(backend_config.type, []):
86
+ raise ConfigValidationError(
87
+ f"截图方法 '{backend_config.screenshot_impl}' "
88
+ f"不适用于当前选择的模拟器类型 '{backend_config.type}'。"
89
+ )
90
+
91
+ # Rule 2: Ensure a produce solution is selected if produce is enabled
92
+ if options.produce.enabled and not options.produce.selected_solution_id:
93
+ raise ConfigValidationError("启用培育时,必须选择培育方案。")
94
+
95
+ # Rule 3: Ensure item lists are not empty if purchasing is enabled
96
+ if options.purchase.ap_enabled and not options.purchase.ap_items:
97
+ raise ConfigValidationError("启用AP购买时,AP商店购买物品不能为空。")
98
+
99
+ if options.purchase.money_enabled and not options.purchase.money_items:
100
+ raise ConfigValidationError("启用金币购买时,金币商店购买物品不能为空。")
101
+
@@ -0,0 +1,121 @@
1
+ import logging
2
+ import os
3
+ import re
4
+ import zipfile
5
+ from datetime import datetime, timedelta
6
+ from typing import Optional, Callable, Dict, Any
7
+
8
+ import cv2
9
+ from pydantic import BaseModel
10
+
11
+ from kaa.errors import ReportCreationError, UploadError
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class BugReportResult(BaseModel):
17
+ """错误报告创建结果的模型"""
18
+ file_path: str
19
+ upload_url: Optional[str] = None
20
+ message: str
21
+
22
+
23
+ def _sanitize_filename(s: str) -> str:
24
+ """过滤掉文件名中的非法字符"""
25
+ return re.sub(r'[\\/:*?"<>|]', '_', s)
26
+
27
+
28
+ class FeedbackService:
29
+ """处理反馈和错误报告的逻辑"""
30
+
31
+ def report(self, title: str, description: str, version: str, upload: bool, on_progress: Optional[Callable[[Dict[str, Any]], None]] = None) -> BugReportResult:
32
+ """
33
+ 创建并可能上传一个错误报告。
34
+
35
+ :param title: 报告标题。
36
+ :param description: 报告描述。
37
+ :param version: 当前版本。
38
+ :param upload: 是否上传报告。
39
+ :param on_progress: 进度回调函数,用于实时回报进度。
40
+ :return: 一个 BugReportResult 对象。
41
+ :raises ReportCreationError: 如果报告创建失败。
42
+ :raises UploadError: 如果报告上传失败。
43
+ """
44
+ from kotonebot import device
45
+ from kotonebot.backend.context import ContextStackVars
46
+
47
+ total_steps = 6 if upload else 5
48
+ def _progress(data: Dict[str, Any]):
49
+ if on_progress:
50
+ on_progress(data)
51
+
52
+ os.makedirs('logs', exist_ok=True)
53
+ os.makedirs('reports', exist_ok=True)
54
+
55
+ safe_title = _sanitize_filename(title)[:30] or "无标题"
56
+ timestamp = datetime.now().strftime("%y-%m-%d-%H-%M-%S")
57
+ path = f'./reports/bug_{timestamp}_{safe_title}.zip'
58
+
59
+ try:
60
+ with zipfile.ZipFile(path, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zipf:
61
+ _progress({'type': 'packing', 'item': '描述文件', 'step': 1, 'total_steps': total_steps})
62
+ description_content = f"标题:{title}\n类型:bug\n内容:\n{description}"
63
+ zipf.writestr('description.txt', description_content.encode('utf-8'))
64
+
65
+ _progress({'type': 'packing', 'item': '上次截图', 'step': 2, 'total_steps': total_steps})
66
+ try:
67
+ stack = ContextStackVars.current()
68
+ if stack and stack._screenshot is not None:
69
+ img = cv2.imencode('.png', stack._screenshot)[1].tobytes()
70
+ zipf.writestr('last_screenshot.png', img)
71
+ except Exception as e:
72
+ logger.warning(f"保存上次截图失败: {e}")
73
+
74
+ _progress({'type': 'packing', 'item': '当前截图', 'step': 3, 'total_steps': total_steps})
75
+ try:
76
+ screenshot = device.screenshot()
77
+ img = cv2.imencode('.png', screenshot)[1].tobytes()
78
+ zipf.writestr('current_screenshot.png', img)
79
+ except Exception as e:
80
+ logger.warning(f"保存当前截图失败: {e}")
81
+
82
+ _progress({'type': 'packing', 'item': '配置文件', 'step': 4, 'total_steps': total_steps})
83
+ if os.path.exists('config.json'):
84
+ zipf.write('config.json')
85
+
86
+ _progress({'type': 'packing', 'item': '日志', 'step': 5, 'total_steps': total_steps})
87
+ if os.path.exists('logs'):
88
+ for root, _, files in os.walk('logs'):
89
+ for file in files:
90
+ file_path = os.path.join(root, file)
91
+ arcname = os.path.join('logs', os.path.relpath(file_path, 'logs'))
92
+ zipf.write(file_path, arcname)
93
+
94
+ zipf.writestr('version.txt', version)
95
+ except Exception as e:
96
+ raise ReportCreationError(str(e)) from e
97
+
98
+ file_path = os.path.abspath(path)
99
+
100
+ if not upload:
101
+ message = f"报告已保存至 {file_path}"
102
+ _progress({'type': 'done', 'file_path': file_path, 'step': 5, 'total_steps': total_steps})
103
+ return BugReportResult(file_path=file_path, message=message)
104
+
105
+ # 上传报告
106
+ from kotonebot.ui.file_host.sensio import upload as upload_file
107
+ _progress({'type': 'uploading', 'item': '报告', 'step': 6, 'total_steps': total_steps})
108
+ try:
109
+ url = upload_file(file_path)
110
+ except Exception as e:
111
+ raise UploadError(str(e)) from e
112
+
113
+ expire_time = datetime.now() + timedelta(days=7)
114
+ final_msg = (
115
+ f"报告导出成功:{url}\n\n"
116
+ f"此链接将于 {expire_time.strftime('%Y-%m-%d %H:%M:%S')}(7 天后)过期\n\n"
117
+ '**复制以上文本并发送至 QQ 群、Github issue、B站私信等**'
118
+ )
119
+ _progress({'type': 'done', 'url': url, 'step': 6, 'total_steps': total_steps})
120
+ return BugReportResult(file_path=file_path, upload_url=url, message=final_msg)
121
+
@@ -0,0 +1,83 @@
1
+ import logging
2
+ from typing import List
3
+
4
+ from kaa.config.produce import ProduceSolution, ProduceSolutionManager, ProduceData
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class ProduceSolutionService:
10
+ """
11
+ Service layer for managing produce solutions.
12
+ This service acts as a wrapper around ProduceSolutionManager,
13
+ decoupling the UI/facade from the direct file system operations.
14
+ """
15
+
16
+ def __init__(self):
17
+ self._manager = ProduceSolutionManager()
18
+
19
+ def list_solutions(self) -> List[ProduceSolution]:
20
+ """
21
+ Retrieves a list of all available produce solutions.
22
+
23
+ :return: A list of ProduceSolution objects.
24
+ """
25
+ return self._manager.list()
26
+
27
+ def get_solution(self, solution_id: str) -> ProduceSolution:
28
+ """
29
+ Retrieves a single produce solution by its ID.
30
+
31
+ :param solution_id: The ID of the solution to retrieve.
32
+ :return: The ProduceSolution object.
33
+ :raises ProduceSolutionNotFoundError: If no solution with the given ID is found.
34
+ """
35
+ return self._manager.read(solution_id)
36
+
37
+ def create_solution(self, name: str = "新培育方案") -> ProduceSolution:
38
+ """
39
+ Creates a new, empty produce solution with a unique ID and saves it.
40
+
41
+ :param name: The name for the new solution.
42
+ :return: The newly created ProduceSolution object.
43
+ """
44
+ new_solution = self._manager.new(name)
45
+ self._manager.save(new_solution.id, new_solution)
46
+ logger.info(f"Created new produce solution '{name}' with ID {new_solution.id}")
47
+ return new_solution
48
+
49
+ def delete_solution(self, solution_id: str) -> None:
50
+ """
51
+ Deletes a produce solution by its ID.
52
+
53
+ :param solution_id: The ID of the solution to delete.
54
+ """
55
+ self._manager.delete(solution_id)
56
+ logger.info(f"Deleted produce solution with ID {solution_id}")
57
+
58
+ def save_solution(self, solution: ProduceSolution) -> None:
59
+ """
60
+ Saves a ProduceSolution object to the filesystem.
61
+ This can be used for both updating existing solutions and saving new ones.
62
+
63
+ :param solution: The ProduceSolution object to save.
64
+ """
65
+ self._manager.save(solution.id, solution)
66
+ logger.info(f"Saved produce solution '{solution.name}' with ID {solution.id}")
67
+
68
+ def update_solution_data(self, solution_id: str, name: str, description: str, data: ProduceData) -> ProduceSolution:
69
+ """
70
+ Updates an existing produce solution with new data and saves it.
71
+
72
+ :param solution_id: The ID of the solution to update.
73
+ :param name: The new name for the solution.
74
+ :param description: The new description for the solution.
75
+ :param data: The new ProduceData object.
76
+ :return: The updated ProduceSolution object.
77
+ """
78
+ solution = self.get_solution(solution_id)
79
+ solution.name = name
80
+ solution.description = description
81
+ solution.data = data
82
+ self.save_solution(solution)
83
+ return solution
@@ -0,0 +1,145 @@
1
+ import logging
2
+ from datetime import datetime, timedelta
3
+ from typing import List, Tuple, Optional
4
+
5
+ from kaa.main.kaa import Kaa
6
+ from kotonebot.backend.context import task_registry, vars as context_vars
7
+ from kotonebot.backend.bot import RunStatus
8
+ from kotonebot.errors import ContextNotInitializedError
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class TaskService:
14
+ """
15
+ Manages the lifecycle of Kaa tasks, including starting, stopping,
16
+ and pausing. It encapsulates the state related to task execution.
17
+ """
18
+
19
+ def __init__(self, kaa_instance: Kaa):
20
+ self._kaa = kaa_instance
21
+ self.run_status: RunStatus | None = None
22
+ self.is_running_all: bool = False
23
+ self.is_running_single: bool = False
24
+ self.is_stopping: bool = False
25
+ self.task_start_time: Optional[datetime] = None
26
+
27
+ def is_running(self) -> bool:
28
+ """Checks if any task (either all or single) is currently running."""
29
+ return self.is_running_all or self.is_running_single
30
+
31
+ def start_all_tasks(self) -> None:
32
+ """Starts all registered tasks."""
33
+ if self.is_running():
34
+ logger.warning("Cannot start all tasks, a task is already running.")
35
+ return
36
+
37
+ logger.info("Starting all tasks...")
38
+ self.is_running_all = True
39
+ self.is_stopping = False
40
+ self.task_start_time = datetime.now()
41
+ self.run_status = self._kaa.start_all()
42
+
43
+ def start_single_task(self, task_name: str) -> None:
44
+ """
45
+ Starts a single task by its name.
46
+
47
+ :param task_name: The name of the task to start.
48
+ :raises ValueError: If the task name is not found.
49
+ """
50
+ if self.is_running():
51
+ logger.warning(f"Cannot start task '{task_name}', a task is already running.")
52
+ return
53
+
54
+ task = task_registry.get(task_name)
55
+ if not task:
56
+ raise ValueError(f"Task '{task_name}' not found in task registry.")
57
+
58
+ logger.info(f"Starting single task: {task_name}")
59
+ self.is_running_single = True
60
+ self.is_stopping = False
61
+ self.task_start_time = datetime.now()
62
+ self.run_status = self._kaa.start([task])
63
+
64
+ def stop_tasks(self) -> None:
65
+ """Stops the currently running tasks."""
66
+ if not self.is_running() or self.is_stopping:
67
+ logger.warning("No tasks are running or tasks are already stopping.")
68
+ return
69
+
70
+ logger.info("Stopping tasks...")
71
+ self.is_stopping = True
72
+ try:
73
+ if context_vars.flow.is_paused:
74
+ logger.info("Tasks are paused, resuming before stopping.")
75
+ context_vars.flow.request_resume()
76
+ except ContextNotInitializedError:
77
+ pass # Context might not be ready if stopping very early
78
+
79
+ if self.run_status:
80
+ self.run_status.interrupt()
81
+
82
+ def get_task_statuses(self) -> List[Tuple[str, str]]:
83
+ """
84
+ Gets the current status of all registered tasks.
85
+
86
+ :return: A list of tuples, where each tuple contains the task name and its status.
87
+ """
88
+ if not self.run_status:
89
+ return [(task.name, "pending") for task in task_registry.values()]
90
+
91
+ # Reset running flags if the underlying run_status is no longer running
92
+ if not self.run_status.running:
93
+ self.is_running_all = False
94
+ self.is_running_single = False
95
+ self.is_stopping = False
96
+ self.task_start_time = None
97
+
98
+ status_list: List[Tuple[str, str]] = []
99
+ for task_status in self.run_status.tasks:
100
+ status_list.append((task_status.task.name, task_status.status))
101
+ return status_list
102
+
103
+ def toggle_pause(self) -> bool | None:
104
+ """
105
+ Toggles the pause/resume state of the running tasks.
106
+
107
+ :return: True if paused, False if resumed, None if context not initialized.
108
+ """
109
+ try:
110
+ if context_vars.flow.is_paused:
111
+ context_vars.flow.request_resume()
112
+ logger.info("Tasks resumed.")
113
+ return False
114
+ else:
115
+ context_vars.flow.request_pause()
116
+ logger.info("Tasks paused.")
117
+ return True
118
+ except ContextNotInitializedError:
119
+ logger.warning("Cannot toggle pause, context not initialized.")
120
+ return None
121
+
122
+ def get_pause_status(self) -> bool | None:
123
+ """
124
+ Gets the current pause status.
125
+
126
+ :return: True if paused, False if resumed, None if context not initialized.
127
+ """
128
+ try:
129
+ return context_vars.flow.is_paused
130
+ except ContextNotInitializedError:
131
+ return None
132
+
133
+ def get_all_task_names(self) -> List[str]:
134
+ """Returns a list of all registered task names."""
135
+ return list(task_registry.keys())
136
+
137
+ def get_task_runtime(self) -> Optional[timedelta]:
138
+ """
139
+ Gets the current task runtime.
140
+
141
+ :return: A timedelta object representing the runtime, or None if no task is running.
142
+ """
143
+ if self.task_start_time is None:
144
+ return None
145
+ return datetime.now() - self.task_start_time
@@ -0,0 +1,208 @@
1
+ import logging
2
+ import os
3
+ import subprocess
4
+ import sys
5
+ import json
6
+ import zipfile
7
+ import threading
8
+ import time
9
+ from typing import Optional, List
10
+ from pydantic import BaseModel, Field
11
+
12
+ from kaa.errors import (
13
+ UpdateFetchListError, CompatibilityError, UpdateInstallError
14
+ )
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ class VersionInfo(BaseModel):
19
+ """存储版本信息的 Pydantic 模型"""
20
+ versions: List[str] = Field(default_factory=list)
21
+ latest: Optional[str] = None
22
+ installed_version: Optional[str] = None
23
+ launcher_version: Optional[str] = None
24
+
25
+ def _compare_versions(version1: str, version2: str) -> int:
26
+ """
27
+ 比较两个版本字符串。
28
+
29
+ :param version1: 第一个版本号
30
+ :param version2: 第二个版本号
31
+ :return: -1 表示 version1 < version2, 0 表示 version1 == version2, 1 表示 version1 > version2
32
+ """
33
+ if version1 == "0.4.x":
34
+ version1 = "0.4.0"
35
+
36
+ def parse(v):
37
+ v = v.lstrip('v')
38
+ parts = v.split('.')
39
+ base = []
40
+ pre_release = ''
41
+ for part in parts:
42
+ if 'b' in part:
43
+ base.append(int(part.split('b')[0]))
44
+ pre_release = ('b', int(part.split('b')[1]))
45
+ break
46
+ elif 'a' in part:
47
+ base.append(int(part.split('a')[0]))
48
+ pre_release = ('a', int(part.split('a')[1]))
49
+ break
50
+ elif 'rc' in part:
51
+ base.append(int(part.split('rc')[0]))
52
+ pre_release = ('rc', int(part.split('rc')[1]))
53
+ break
54
+ else:
55
+ base.append(int(part))
56
+
57
+ pre_map = {'a': -2, 'b': -1, 'rc': -0.5}
58
+ if pre_release:
59
+ return tuple(base) + (pre_map[pre_release[0]], pre_release[1])
60
+ return tuple(base) + (0, 0)
61
+
62
+ v1_parsed = parse(version1)
63
+ v2_parsed = parse(version2)
64
+
65
+ if v1_parsed < v2_parsed:
66
+ return -1
67
+ if v1_parsed > v2_parsed:
68
+ return 1
69
+ return 0
70
+
71
+ class UpdateService:
72
+ """处理应用程序更新逻辑"""
73
+
74
+ def __init__(self, repo_name: str = "ksaa"):
75
+ self.repo_name = repo_name
76
+ self.bootstrap_pyz_path = os.path.join(os.getcwd(), "bootstrap.pyz")
77
+
78
+ def _get_launcher_version(self) -> Optional[str]:
79
+ """
80
+ 从 bootstrap.pyz/meta.py 读取启动器版本。
81
+
82
+ :return: 启动器版本字符串,如果无法确定则返回 None。
83
+ """
84
+ if not os.path.exists(self.bootstrap_pyz_path):
85
+ logger.warning("bootstrap.pyz not found.")
86
+ return None
87
+
88
+ try:
89
+ with zipfile.ZipFile(self.bootstrap_pyz_path, 'r') as zf:
90
+ if 'meta.py' not in zf.namelist():
91
+ logger.warning("meta.py not found in bootstrap.pyz, assuming legacy launcher.")
92
+ return "0.4.x"
93
+
94
+ meta_content = zf.read('meta.py').decode('utf-8')
95
+ exec_globals = {}
96
+ exec_locals = {}
97
+ exec(meta_content, exec_globals, exec_locals)
98
+
99
+ version = exec_locals.get('VERSION')
100
+ if version and isinstance(version, str):
101
+ logger.info(f"Launcher version from meta.py: {version}")
102
+ return version.lstrip('v')
103
+ else:
104
+ logger.warning("VERSION not found in meta.py.")
105
+ return None
106
+ except zipfile.BadZipFile:
107
+ logger.info("bootstrap.pyz is not a valid zip file, assuming legacy launcher.")
108
+ return "0.4.x"
109
+ except Exception as e:
110
+ logger.error(f"Failed to read launcher version: {e}", exc_info=True)
111
+ return None
112
+
113
+ def list_remote_versions(self) -> VersionInfo:
114
+ """
115
+ 从 PyPI 获取可用版本。
116
+
117
+ :return: 包含版本数据的 VersionInfo 对象。
118
+ :raises UpdateFetchListError: 如果无法获取或解析版本列表。
119
+ """
120
+ cmd = [
121
+ sys.executable, "-m", "pip", "index", "versions", self.repo_name,
122
+ "--json", "--pre",
123
+ "--index-url", "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple",
124
+ "--trusted-host", "mirrors.tuna.tsinghua.edu.cn"
125
+ ]
126
+ logger.info(f"Executing: {' '.join(cmd)}")
127
+
128
+ try:
129
+ result = subprocess.run(
130
+ cmd,
131
+ capture_output=True,
132
+ text=True,
133
+ timeout=30,
134
+ check=False
135
+ )
136
+
137
+ if result.returncode != 0:
138
+ raise UpdateFetchListError(result.stderr)
139
+
140
+ data = json.loads(result.stdout)
141
+ version_info = VersionInfo(
142
+ versions=data.get("versions", []),
143
+ latest=data.get("latest"),
144
+ installed_version=data.get("installed_version")
145
+ )
146
+ version_info.launcher_version = self._get_launcher_version()
147
+ logger.info(f"Found {len(version_info.versions)} available versions.")
148
+ return version_info
149
+
150
+ except (subprocess.TimeoutExpired, json.JSONDecodeError, Exception) as e:
151
+ raise UpdateFetchListError(str(e)) from e
152
+
153
+ def _check_compatibility(self, target_version: str) -> Optional[str]:
154
+ """
155
+ 检查启动器是否与目标版本兼容。
156
+
157
+ :raises CompatibilityError: 如果启动器不兼容。
158
+ :return: 如果有潜在问题,则返回警告消息,否则返回 None。
159
+ """
160
+ launcher_version = self._get_launcher_version()
161
+ if not launcher_version:
162
+ return "无法获取启动器版本,可以继续安装但可能存在兼容性问题"
163
+
164
+ logger.info(f"Launcher version: {launcher_version}, Target version: {target_version}")
165
+
166
+ if _compare_versions(launcher_version, "0.5.0") < 0:
167
+ if _compare_versions(target_version, "2025.9b1") >= 0:
168
+ raise CompatibilityError(launcher_version, target_version)
169
+ else:
170
+ return "启动器版本较低,建议升级到 v0.5.0 以上"
171
+ return None
172
+
173
+ def install_version(self, version: str):
174
+ """
175
+ 安装应用程序的特定版本。
176
+
177
+ :param version: 要安装的版本。
178
+ :raises CompatibilityError: 如果启动器不兼容。
179
+ :raises UpdateInstallError: 如果安装过程启动失败。
180
+ """
181
+ if not version:
182
+ raise ValueError("A version must be selected to install.")
183
+
184
+ warning = self._check_compatibility(version)
185
+ if warning:
186
+ logger.warning(warning)
187
+
188
+ def _install_and_exit():
189
+ try:
190
+ time.sleep(1)
191
+ cmd = [sys.executable, self.bootstrap_pyz_path, f"--install-version={version}"]
192
+ logger.info(f"Starting installation via launcher: {' '.join(cmd)}")
193
+
194
+ subprocess.Popen(
195
+ cmd,
196
+ cwd=os.getcwd(),
197
+ creationflags=subprocess.CREATE_NEW_CONSOLE if os.name == 'nt' else 0
198
+ )
199
+ logger.info("Installation process started. Exiting current application.")
200
+ os._exit(0)
201
+ except Exception as e:
202
+ logger.critical(f"Failed to launch installation process: {e}", exc_info=True)
203
+
204
+ try:
205
+ install_thread = threading.Thread(target=_install_and_exit, daemon=True)
206
+ install_thread.start()
207
+ except Exception as e:
208
+ raise UpdateInstallError(str(e)) from e
File without changes