ksaa 2025.7.20.0__py3-none-any.whl → 2025.7.27.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.
- kotonebot/backend/bot.py +60 -12
- kotonebot/backend/context/task_action.py +9 -46
- kotonebot/backend/dispatch.py +1 -104
- kotonebot/backend/flow_controller.py +3 -1
- kotonebot/errors.py +37 -2
- kotonebot/interop/win/message_box.py +314 -0
- kotonebot/interop/win/task_dialog.py +469 -0
- kotonebot/kaa/config/produce.py +8 -6
- kotonebot/kaa/config/schema.py +2 -0
- kotonebot/kaa/errors.py +38 -0
- kotonebot/kaa/main/gr.py +22 -5
- kotonebot/kaa/metadata.py +14 -0
- kotonebot/kaa/resources/__pycache__/__init__.cpython-310.pyc +0 -0
- kotonebot/kaa/resources/game.db +0 -0
- kotonebot/kaa/resources/game_ver.txt +0 -0
- kotonebot/kaa/tasks/R.py +126 -126
- kotonebot/kaa/tasks/daily/contest.py +39 -8
- kotonebot/kaa/tasks/daily/purchase.py +0 -1
- kotonebot/kaa/tasks/end_game.py +4 -3
- kotonebot/kaa/tasks/produce/in_purodyuusu.py +32 -29
- kotonebot/kaa/tasks/produce/produce.py +2 -1
- kotonebot/kaa/tasks/start_game.py +2 -0
- kotonebot/ui/user.py +43 -13
- {ksaa-2025.7.20.0.dist-info → ksaa-2025.7.27.0.dist-info}/METADATA +2 -1
- {ksaa-2025.7.20.0.dist-info → ksaa-2025.7.27.0.dist-info}/RECORD +155 -152
- /kotonebot/kaa/sprites/{70671b9e-9ebb-4dee-bc5a-dbcb70364adf.png → 039771df-9741-4f4e-9793-9017386ac68e.png} +0 -0
- /kotonebot/kaa/sprites/{32907971-0e98-4adc-91b8-ac0308c11c3c.png → 0c826d2e-41ce-474b-ad99-f4561baf03de.png} +0 -0
- /kotonebot/kaa/sprites/{89d3c0a6-266e-486e-998d-ecd0d2536814.png → 0fb240b0-1bb5-4aee-8ad3-a24062ee9a20.png} +0 -0
- /kotonebot/kaa/sprites/{20733b9b-1df7-42af-ab55-3364e8623602.png → 10636534-51ee-4698-b582-e56751bebb4d.png} +0 -0
- /kotonebot/kaa/sprites/{562f176f-af1e-4352-aeed-62e362177f9f.png → 1218bb7e-2cab-477e-aa51-f71aa4f52a7d.png} +0 -0
- /kotonebot/kaa/sprites/{b66e13a1-636c-4478-9d72-7d200ff670a9.png → 1917a430-d216-4359-8f62-0b62108f58c1.png} +0 -0
- /kotonebot/kaa/sprites/{39f88061-a997-4a8a-b09e-4a2425f4a83a.png → 1f56fbc1-47f5-4dc3-b91b-6e49e4ea00dc.png} +0 -0
- /kotonebot/kaa/sprites/{3e613561-8dbd-4094-9889-53f08541a716.png → 25797538-2dc6-436c-b64a-597ae6d5db6e.png} +0 -0
- /kotonebot/kaa/sprites/{38c0ed08-5af3-4fca-a37f-27d66fd35a4c.png → 27d59ead-0f94-4554-8c9a-cd3aeb606fea.png} +0 -0
- /kotonebot/kaa/sprites/{cab0b1ff-b40a-42a8-b799-75e309b2dee3.png → 2822bf2b-b350-437b-b330-0e8c122bfbe7.png} +0 -0
- /kotonebot/kaa/sprites/{9169bf8e-5907-42e5-b102-47da3bf9a0b1.png → 287ec12a-c1fb-4b8f-a77c-acc99821eabd.png} +0 -0
- /kotonebot/kaa/sprites/{131fb7e1-6eb9-4722-9943-76ff05c5baee.png → 290aaff5-71eb-4d71-87db-3757d3e9794b.png} +0 -0
- /kotonebot/kaa/sprites/{1737a9e0-f8c4-416e-bbad-cb97a658ae9a.png → 29fcf4cb-883f-475e-a68f-107cffc83f1c.png} +0 -0
- /kotonebot/kaa/sprites/{6ac4a3b5-66d2-4429-8cd1-945fbb48280c.png → 2ac90dcf-2284-437e-a61e-51934f937f8f.png} +0 -0
- /kotonebot/kaa/sprites/{05a23a4a-9b93-4fbd-b4e9-15a7c4a91fdc.png → 2e3410a1-49b5-4f65-8f26-bb1df213c2b8.png} +0 -0
- /kotonebot/kaa/sprites/{9b696383-10dd-4a35-8546-1ccdb8891373.png → 30bc31e4-40b3-4bcb-bb36-1dd5a8c59a32.png} +0 -0
- /kotonebot/kaa/sprites/{9dbc5d76-8c6a-4638-b0c2-eba79542ec58.png → 34eaf943-a8b5-4358-aadc-62ff656ab542.png} +0 -0
- /kotonebot/kaa/sprites/{0aa4472c-b533-46cc-a072-9d165d955424.png → 37f1d878-467f-4ba0-bc59-bb327797191b.png} +0 -0
- /kotonebot/kaa/sprites/{1aed664e-ceb3-4a1d-a338-df08dc8ebbd3.png → 3aa13898-1a50-4234-b9ec-f35878f05592.png} +0 -0
- /kotonebot/kaa/sprites/{0e29489c-6728-46cd-bd13-2b9d9e74b2da.png → 3ca43ccf-8748-439f-80f7-a95978a6b1f2.png} +0 -0
- /kotonebot/kaa/sprites/{dcc2da94-2c9d-4248-bce5-731651e391ae.png → 3efe8341-33bc-492e-8a0f-69a507502b43.png} +0 -0
- /kotonebot/kaa/sprites/{b169172d-8ce2-432a-bed6-1dfc30bcc95c.png → 3f6b5de5-c942-4ef1-8b04-5ee6859bb27b.png} +0 -0
- /kotonebot/kaa/sprites/{f0952cb2-dadb-4d9a-933e-cabcbf8850fb.png → 432aad91-9cc5-4518-bbfd-6cbed522dac6.png} +0 -0
- /kotonebot/kaa/sprites/{c577c19e-aa5d-4670-b44c-1c0b3d72451a.png → 452e213a-9762-42ec-8e2b-2cabc3eb254d.png} +0 -0
- /kotonebot/kaa/sprites/{79226566-26e2-4c2f-8f6a-4e4deb9fa9ea.png → 46a8bf3e-7149-4d9b-8b31-a6b9c3d6b166.png} +0 -0
- /kotonebot/kaa/sprites/{fd77088d-652a-472e-9f84-dcc83d40a6bf.png → 48293a53-d05b-4198-995f-103531f26923.png} +0 -0
- /kotonebot/kaa/sprites/{7f4accdd-bd72-45e6-8469-692e27986486.png → 4c0dc941-6ecf-45b4-91f8-3fb10e770512.png} +0 -0
- /kotonebot/kaa/sprites/{2cea06ca-99f6-4bc5-8dd0-2bba80b07b17.png → 50986ada-ff64-405f-911d-f6c3655fb658.png} +0 -0
- /kotonebot/kaa/sprites/{10a329df-2515-4551-a130-07e86058abdf.png → 535db21f-b22e-4a05-a7e8-81e3664305b8.png} +0 -0
- /kotonebot/kaa/sprites/{1609d037-a3fc-485b-96c6-cf1790434c01.png → 5410568f-2e3c-49b2-8112-f43c52342298.png} +0 -0
- /kotonebot/kaa/sprites/{d627ba81-9c86-4b53-9ab9-86c8748d8daa.png → 5aa47ea4-79de-4547-9320-cf8c614c7366.png} +0 -0
- /kotonebot/kaa/sprites/{a77c1724-eb8f-4a1a-8997-516deb75ae78.png → 5d1627a0-f803-4736-8c3c-7bf1f0de0aa0.png} +0 -0
- /kotonebot/kaa/sprites/{88945ecf-9ac5-46dc-8af6-b21dbfded665.png → 5eaa9fa9-0bce-47ce-8f28-d5ba32c566c3.png} +0 -0
- /kotonebot/kaa/sprites/{63cd694d-343e-468d-a53f-360c97f71ee6.png → 650eee06-c926-4e91-bd2c-38f3068938c0.png} +0 -0
- /kotonebot/kaa/sprites/{5aef61f9-d616-42f2-ae8d-5c5a033231d3.png → 652c9b9f-316e-46b2-a2d4-8dc8d5e6116f.png} +0 -0
- /kotonebot/kaa/sprites/{d83f2631-b9d8-4fd6-ab1c-50b0a013f3f0.png → 6621d884-5ba5-4356-bd50-94239ec5395a.png} +0 -0
- /kotonebot/kaa/sprites/{dcc214f3-9707-4a2c-aaff-3834e6f12f0b.png → 66de3573-9562-4cd8-82df-5d6be6b1f213.png} +0 -0
- /kotonebot/kaa/sprites/{a5d3efa4-ec3e-4637-835a-147af973bbf8.png → 6b71968e-97d3-4fad-89d0-ffb7743446a9.png} +0 -0
- /kotonebot/kaa/sprites/{f16d6f1e-b9ff-4cff-aed3-dd150caf8f07.png → 6c7cb85e-0aac-40d8-9b55-d9b455c89985.png} +0 -0
- /kotonebot/kaa/sprites/{1bb92c94-af17-492e-9d22-393c12b7f346.png → 6cba0d0c-a67b-470d-9abe-1c55b14310ba.png} +0 -0
- /kotonebot/kaa/sprites/{731ed76d-6d6c-48b9-82c3-045cc1ecc419.png → 6d59fa9e-b988-47e3-99b5-e06510f98e45.png} +0 -0
- /kotonebot/kaa/sprites/{04425af8-ba28-407d-ad18-8c3a2483025d.png → 6eb8e6a5-b361-4c4f-b161-e2ccd5ef277d.png} +0 -0
- /kotonebot/kaa/sprites/{e36ec591-8bf5-4deb-9f28-3977576de931.png → 6ed77f5b-e4ce-4722-934e-3c5ec34b6d5a.png} +0 -0
- /kotonebot/kaa/sprites/{8e0964c4-6e34-43cb-a333-d60bceae7f11.png → 6f4d32df-2a01-40e8-bfd1-9e83c6864789.png} +0 -0
- /kotonebot/kaa/sprites/{41f0e90a-a352-450c-a788-0303d038227a.png → 6f56551e-bfa3-4efe-b95a-411e54885ceb.png} +0 -0
- /kotonebot/kaa/sprites/{d54fc8f5-e50d-45dd-b123-92414f93488c.png → 725d6877-7260-4ca3-91d3-da4e9ccbf206.png} +0 -0
- /kotonebot/kaa/sprites/{d14633f5-9682-4b59-a4ef-e92a2d26d86b.png → 74bb6050-8d6d-48aa-8e71-31e92304b5af.png} +0 -0
- /kotonebot/kaa/sprites/{a009c9de-029e-4999-b728-e6877374325a.png → 760655f5-1d32-437c-81fc-306e80fabfd8.png} +0 -0
- /kotonebot/kaa/sprites/{487ae483-2787-4982-93b0-4847665683e9.png → 763968fd-f7ab-4a46-a0ef-6a496b21d3dd.png} +0 -0
- /kotonebot/kaa/sprites/{0e4b4481-6805-4a1b-a3d8-913681968662.png → 77b7b404-bc0c-4637-96b0-7344f782989e.png} +0 -0
- /kotonebot/kaa/sprites/{fef40be1-5b75-4023-9977-df46cd211a86.png → 79cddeac-828b-49a1-b860-b422e2c8d2ea.png} +0 -0
- /kotonebot/kaa/sprites/{3645c21a-acd8-4ec4-a286-d9031736342c.png → 7b74cacf-b8b4-4915-a71a-1dab321dab60.png} +0 -0
- /kotonebot/kaa/sprites/{c92a2935-cf41-4f56-911a-bc40f122cbf3.png → 7d931bd2-5cfc-4e4c-83c0-498b5e17d15c.png} +0 -0
- /kotonebot/kaa/sprites/{2bb9dbf7-6cb2-426a-a35c-7228a1320220.png → 7dc4ea14-42a9-4b2f-a164-99da3dbb000b.png} +0 -0
- /kotonebot/kaa/sprites/{df9d09c3-8f15-4caf-bb21-f69644fb4705.png → 7dc5f386-2db3-4405-9796-34f1b8a6755f.png} +0 -0
- /kotonebot/kaa/sprites/{40f04ad0-44c0-4913-89aa-bc466bfd6ace.png → 843174a9-6cff-45ba-9573-67904691a30f.png} +0 -0
- /kotonebot/kaa/sprites/{ddae5592-adc3-4c36-bfdd-6c6d91823296.png → 85ca40d2-f02e-45a4-9db0-c483f6ae1db5.png} +0 -0
- /kotonebot/kaa/sprites/{15fe6944-396c-467b-80db-476bbdf65a80.png → 88207e47-1f50-485f-9984-54cea01d740a.png} +0 -0
- /kotonebot/kaa/sprites/{49ccf947-13a5-4839-8f85-d0fe40094e77.png → 88e01e80-50e2-4b0f-834c-86f6a3508287.png} +0 -0
- /kotonebot/kaa/sprites/{a4553bb1-27b9-47c4-8f51-1cff2129df8e.png → 89b364ae-9050-4671-b595-632ba0efc78b.png} +0 -0
- /kotonebot/kaa/sprites/{2cff833f-10bd-478c-8760-99162c71e9e3.png → 8d1a63f3-7ec4-405c-9d78-d176018389dc.png} +0 -0
- /kotonebot/kaa/sprites/{6e59d072-06f8-488f-a453-8ba60a05d691.png → 8f941a44-bbe8-4d3d-82b3-7b14c2356998.png} +0 -0
- /kotonebot/kaa/sprites/{183df82f-acea-4d40-87a8-18fc8ddab6d0.png → 95962144-c639-4902-ad2d-be11ccc5eaab.png} +0 -0
- /kotonebot/kaa/sprites/{a3384f38-334c-440a-ab06-405bad293bab.png → 96c79132-d55f-43d2-bdb3-6fe55734b6d5.png} +0 -0
- /kotonebot/kaa/sprites/{3fc47270-febf-492e-b8fc-0a9793bc0d56.png → 9895c28f-eba2-4e37-911b-6c0f05f7d72b.png} +0 -0
- /kotonebot/kaa/sprites/{bc01675d-453c-43f2-9075-f9567c8aa6f9.png → 98df22d6-1ff2-4c9a-87e7-f70e9f91019e.png} +0 -0
- /kotonebot/kaa/sprites/{71d6a37d-45df-456f-b673-d1e8b35ee619.png → 9b01dbe2-5043-40af-858c-33bbbf4ccc22.png} +0 -0
- /kotonebot/kaa/sprites/{fda67b44-d46d-4789-89a3-0786c60987ab.png → 9dc9f428-6e6d-4362-b4d4-dce50d9e732e.png} +0 -0
- /kotonebot/kaa/sprites/{3a595ec9-d813-4c09-b8b8-be8f5da88faa.png → 9df02971-cdd8-4464-abf9-8035bc233d2f.png} +0 -0
- /kotonebot/kaa/sprites/{d81b7022-5c81-4533-b949-7e9d75a5b8b4.png → 9e7bf71b-fe8d-43cf-8bdd-6cfb1fb1b597.png} +0 -0
- /kotonebot/kaa/sprites/{2dab2877-c6c3-45d9-849f-fb2d79b7b74e.png → a043ed15-0ceb-49b5-b3df-d03274677340.png} +0 -0
- /kotonebot/kaa/sprites/{6582463d-b4fa-40a1-b771-664c7f71fe9c.png → a200b1e4-2ebc-4c15-bdf2-2575408877f8.png} +0 -0
- /kotonebot/kaa/sprites/{9449ff36-7adb-42d5-b89d-bf9147d62811.png → a718a6f9-db30-4a9c-9d2b-b83055cf36ae.png} +0 -0
- /kotonebot/kaa/sprites/{966bcfd4-fefc-4704-907d-45bc6098d3f4.png → a7ada2b7-1366-40c8-864e-fbb42d248124.png} +0 -0
- /kotonebot/kaa/sprites/{b6f5243b-cfb4-42d2-b4eb-2fd1a9d33ab7.png → aa42da27-2c6a-4731-9678-edd3aefde4a7.png} +0 -0
- /kotonebot/kaa/sprites/{0002785c-3caa-4c8f-8d47-f29aee04a588.png → aaa770da-fc42-4e4a-978c-8fef22c0ebf9.png} +0 -0
- /kotonebot/kaa/sprites/{83f9e9a0-54dd-4601-8e54-3091c2892e67.png → af54e00c-b768-4497-829b-6bfac16f4efc.png} +0 -0
- /kotonebot/kaa/sprites/{0554264f-0474-44d0-9209-8135d8840c53.png → afe94aea-47e2-4310-8e25-3e7cadc5a75d.png} +0 -0
- /kotonebot/kaa/sprites/{0a4c6cbe-f42e-4301-9c1a-101fc33421d2.png → b0a88b9e-358f-4f35-b823-d6c025a516cf.png} +0 -0
- /kotonebot/kaa/sprites/{b25627d6-3352-4fe8-93c2-b653efe104d4.png → b4ede50d-7edb-4461-bee6-77d13456fa30.png} +0 -0
- /kotonebot/kaa/sprites/{f5727018-9a23-40de-840e-610d6e5dfa9d.png → b503e883-c9d2-46c0-9de2-158770abb32b.png} +0 -0
- /kotonebot/kaa/sprites/{92a42da1-d8d2-4233-b6d0-5997a72f3d03.png → b60b3563-20b4-46ad-b36b-3113d9e07d4c.png} +0 -0
- /kotonebot/kaa/sprites/{67f00dd1-86c9-4a49-a37c-87879171a46a.png → b9ded3ea-9701-441d-a593-e73e44d5fa3f.png} +0 -0
- /kotonebot/kaa/sprites/{be83e205-8ec5-452f-b42b-b98156516aa8.png → b9f285e4-eb97-47f0-9ca5-28ca558acf58.png} +0 -0
- /kotonebot/kaa/sprites/{6cacdb8f-2ea3-4e3c-a124-b957b75ea427.png → bbee032e-7fc1-4b4d-b1db-cc54b24c20c8.png} +0 -0
- /kotonebot/kaa/sprites/{401966c8-2095-4eef-b92a-764122de02d9.png → bc68fbda-8e6d-4368-b322-49a6854d81e3.png} +0 -0
- /kotonebot/kaa/sprites/{12151e09-e5f1-4b1d-a211-63cb300cb082.png → bf8d6419-472c-4941-8d69-9bb01af5c71a.png} +0 -0
- /kotonebot/kaa/sprites/{9d80c1ef-0f42-4622-ab9a-bfcc077dace6.png → c3548011-dc57-4542-a855-1e7f9d7762f7.png} +0 -0
- /kotonebot/kaa/sprites/{8fb17c7f-778d-4cd5-8d2f-dbf21d264297.png → c46f1705-b4d5-4f39-b138-391a85fb0302.png} +0 -0
- /kotonebot/kaa/sprites/{f58c3a84-60ea-415b-ac63-4b534dc8cb6c.png → c58be1dd-1981-42ee-8de3-e365102884e5.png} +0 -0
- /kotonebot/kaa/sprites/{dd699396-5500-43ad-8470-cf7a8ba013ec.png → c8321b5a-8f65-4465-892a-a3432efba5ac.png} +0 -0
- /kotonebot/kaa/sprites/{e89c23b0-ca58-49d1-b439-967a4a4a431d.png → c95ee1bb-1e53-4568-ba92-9606071ad3a6.png} +0 -0
- /kotonebot/kaa/sprites/{e04331fe-d62e-4f77-953d-78515ee5686e.png → ca9f52b1-42c0-4091-bc29-efc0da9606e0.png} +0 -0
- /kotonebot/kaa/sprites/{d56feb26-d560-423f-b5f8-dc06f7a3c8a1.png → ccba7903-9882-4982-ab84-355667d8419a.png} +0 -0
- /kotonebot/kaa/sprites/{e4a92c92-0974-40e0-b488-16ea7a4c1a29.png → d0dd43b7-5571-4e47-af51-796b4a6a1cfe.png} +0 -0
- /kotonebot/kaa/sprites/{e075c8cb-7eef-47cf-b9fe-9e3d1bd98e85.png → d274bf4d-f4ff-4270-9a5f-87685a0c6817.png} +0 -0
- /kotonebot/kaa/sprites/{64298c97-baac-48b9-be7d-cb14091aa3cb.png → d3565383-91b0-4b33-87a9-3468dfd56e52.png} +0 -0
- /kotonebot/kaa/sprites/{c3562fef-44f7-43d2-a4e6-e7ee21f8bdc5.png → d552db90-8e22-427c-b004-1799adbcc6ba.png} +0 -0
- /kotonebot/kaa/sprites/{681157d7-b37f-442f-b784-86fa9029ecdb.png → d5943dc2-12df-42bb-84bc-d6958260cb75.png} +0 -0
- /kotonebot/kaa/sprites/{10d9a788-24a2-42e1-9284-78f507986606.png → d5be0aac-6f00-4961-b21c-4075bd6ca528.png} +0 -0
- /kotonebot/kaa/sprites/{c5774fb4-be11-4b28-8f58-299ae8872cc1.png → d65cf124-6c8e-41e4-80dc-0c63b41d5636.png} +0 -0
- /kotonebot/kaa/sprites/{9a9d9398-15df-46ea-a247-04ba01f61603.png → d79069c1-3b8e-4df7-8b14-4eeaa44241e1.png} +0 -0
- /kotonebot/kaa/sprites/{5d3b3a20-89bb-4cf2-9973-25581c32e91c.png → db3ab847-948d-4ee0-b5cb-c57c0caa24fd.png} +0 -0
- /kotonebot/kaa/sprites/{610ea09a-d623-428f-ac97-1b51dc5e9202.png → dbc449e9-404e-435b-8b0f-3c853c7f0efe.png} +0 -0
- /kotonebot/kaa/sprites/{25f18ff9-dbae-4aed-87b4-c05ca6c4ebf4.png → dc3f2602-1f20-460a-b408-693001aa70bc.png} +0 -0
- /kotonebot/kaa/sprites/{f217d7cf-acf5-44eb-9f99-025bbcaea866.png → dcb63213-c81e-4eb7-82f1-a16a838299fe.png} +0 -0
- /kotonebot/kaa/sprites/{bcc433e0-8a4e-455f-b625-4583cea19c1c.png → df004f6b-683d-41e8-848f-2a838c585f43.png} +0 -0
- /kotonebot/kaa/sprites/{94021aa4-fb87-41e7-920a-988a360f8321.png → dfab89de-7365-4bc8-b332-6f62733123a2.png} +0 -0
- /kotonebot/kaa/sprites/{12d0271c-1a86-4d2c-b379-b7df847fe20b.png → e0a34a9a-69b6-4732-9b1c-7a2c6445cb76.png} +0 -0
- /kotonebot/kaa/sprites/{e281792b-000b-4048-afb7-e8bb0dbd88b3.png → e0cea27c-5b4e-4831-8fcf-65bccb9e5950.png} +0 -0
- /kotonebot/kaa/sprites/{a514ce10-bff1-4aa0-9625-bf560dcb8be5.png → e311e99e-ae64-442a-aed1-6fac29687d18.png} +0 -0
- /kotonebot/kaa/sprites/{9dfa46c9-54ec-4dd7-8f2e-e1e66c1206f1.png → e8e25d3c-569e-4eae-a707-3afa8c5f46ec.png} +0 -0
- /kotonebot/kaa/sprites/{951ae4be-40f3-4ccc-8578-77aca7f25eef.png → e9afeef0-fcbe-4d97-8b39-c57ab24a3f84.png} +0 -0
- /kotonebot/kaa/sprites/{8a2938ad-5657-4f66-8447-4bc7fa45b857.png → ec9656c3-a984-47db-8661-e1ae8751d288.png} +0 -0
- /kotonebot/kaa/sprites/{377857a9-c37f-4a90-afa9-00c4ff7b24ae.png → ee4862f3-f298-446e-a68b-bd97259052e6.png} +0 -0
- /kotonebot/kaa/sprites/{3e34e821-ae69-49aa-9d04-bc34b3037cd5.png → ef585824-7bfa-427a-a185-4317c0bcb4f6.png} +0 -0
- /kotonebot/kaa/sprites/{87c7532f-2394-403e-a6d8-7a216043ec13.png → ef861253-914d-436e-aa01-731a5dd11e38.png} +0 -0
- /kotonebot/kaa/sprites/{97a543a8-a859-4a1e-8e33-602f600db389.png → f0b983cd-8bce-4b67-9214-493364244c26.png} +0 -0
- /kotonebot/kaa/sprites/{143db8eb-fdde-4de0-a946-a14123b9262e.png → f2906fc0-9192-48ca-bc5d-52e922b04d8d.png} +0 -0
- /kotonebot/kaa/sprites/{f543861e-c3d5-4bc9-a677-47f9e86918ea.png → f3890caa-fa8b-43bc-ba30-82c5bbe1d12a.png} +0 -0
- /kotonebot/kaa/sprites/{d6314592-b8a4-4025-9875-b135bf986cf7.png → f4c3a6f9-1149-4530-af30-c56d7e3d02b9.png} +0 -0
- /kotonebot/kaa/sprites/{1f9f101b-2647-4d14-931e-7a8307495475.png → fa3bdcef-dfba-4cf2-a06f-e6078c07fb7d.png} +0 -0
- /kotonebot/kaa/sprites/{9e0492b7-3b66-48a2-b589-91e7fd16106d.png → fab770fd-e250-4dd4-b9d2-80966ad452ac.png} +0 -0
- /kotonebot/kaa/sprites/{f2d8a534-8afd-475c-ac20-738c7efffc90.png → fb2f308f-a025-4606-b789-0455c7cf0af8.png} +0 -0
- /kotonebot/kaa/sprites/{4622e955-7bb1-450d-a776-01543540fde3.png → fcc71fcd-b28e-4247-beb6-6f5422218c54.png} +0 -0
- /kotonebot/kaa/sprites/{04feeecc-a607-402d-bbc4-a71af87c7fc2.png → fe42018b-4987-4389-8d44-fb0cfd0f4dab.png} +0 -0
- {ksaa-2025.7.20.0.dist-info → ksaa-2025.7.27.0.dist-info}/WHEEL +0 -0
- {ksaa-2025.7.20.0.dist-info → ksaa-2025.7.27.0.dist-info}/entry_points.txt +0 -0
- {ksaa-2025.7.20.0.dist-info → ksaa-2025.7.27.0.dist-info}/licenses/LICENSE +0 -0
- {ksaa-2025.7.20.0.dist-info → ksaa-2025.7.27.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
from ctypes import wintypes
|
|
3
|
+
import time
|
|
4
|
+
from typing import List, Tuple, Optional
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"TaskDialog",
|
|
9
|
+
"TDCBF_OK_BUTTON", "TDCBF_YES_BUTTON", "TDCBF_NO_BUTTON", "TDCBF_CANCEL_BUTTON",
|
|
10
|
+
"TDCBF_RETRY_BUTTON", "TDCBF_CLOSE_BUTTON",
|
|
11
|
+
"IDOK", "IDCANCEL", "IDABORT", "IDRETRY", "IDIGNORE", "IDYES", "IDNO", "IDCLOSE",
|
|
12
|
+
"TD_WARNING_ICON", "TD_ERROR_ICON", "TD_INFORMATION_ICON", "TD_SHIELD_ICON"
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
# --- Windows API 常量定义 ---
|
|
16
|
+
|
|
17
|
+
# 常用按钮
|
|
18
|
+
TDCBF_OK_BUTTON = 0x0001
|
|
19
|
+
TDCBF_YES_BUTTON = 0x0002
|
|
20
|
+
TDCBF_NO_BUTTON = 0x0004
|
|
21
|
+
TDCBF_CANCEL_BUTTON = 0x0008
|
|
22
|
+
TDCBF_RETRY_BUTTON = 0x0010
|
|
23
|
+
TDCBF_CLOSE_BUTTON = 0x0020
|
|
24
|
+
|
|
25
|
+
# 对话框返回值
|
|
26
|
+
IDOK = 1
|
|
27
|
+
IDCANCEL = 2
|
|
28
|
+
IDABORT = 3
|
|
29
|
+
IDRETRY = 4
|
|
30
|
+
IDIGNORE = 5
|
|
31
|
+
IDYES = 6
|
|
32
|
+
IDNO = 7
|
|
33
|
+
IDCLOSE = 8
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# 标准图标 (使用 MAKEINTRESOURCE 宏)
|
|
37
|
+
def MAKEINTRESOURCE(i: int) -> wintypes.LPWSTR:
|
|
38
|
+
return wintypes.LPWSTR(i)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
TD_WARNING_ICON = MAKEINTRESOURCE(65535)
|
|
42
|
+
TD_ERROR_ICON = MAKEINTRESOURCE(65534)
|
|
43
|
+
TD_INFORMATION_ICON = MAKEINTRESOURCE(65533)
|
|
44
|
+
TD_SHIELD_ICON = MAKEINTRESOURCE(65532)
|
|
45
|
+
|
|
46
|
+
# Task Dialog 标志
|
|
47
|
+
TDF_ENABLE_HYPERLINKS = 0x0001
|
|
48
|
+
TDF_USE_HICON_MAIN = 0x0002
|
|
49
|
+
TDF_USE_HICON_FOOTER = 0x0004
|
|
50
|
+
TDF_ALLOW_DIALOG_CANCELLATION = 0x0008
|
|
51
|
+
TDF_USE_COMMAND_LINKS = 0x0010
|
|
52
|
+
TDF_USE_COMMAND_LINKS_NO_ICON = 0x0020
|
|
53
|
+
TDF_EXPAND_FOOTER_AREA = 0x0040
|
|
54
|
+
TDF_EXPANDED_BY_DEFAULT = 0x0080
|
|
55
|
+
TDF_VERIFICATION_FLAG_CHECKED = 0x0100
|
|
56
|
+
TDF_SHOW_PROGRESS_BAR = 0x0200
|
|
57
|
+
TDF_SHOW_MARQUEE_PROGRESS_BAR = 0x0400
|
|
58
|
+
TDF_CALLBACK_TIMER = 0x0800
|
|
59
|
+
TDF_POSITION_RELATIVE_TO_WINDOW = 0x1000
|
|
60
|
+
TDF_RTL_LAYOUT = 0x2000
|
|
61
|
+
TDF_NO_DEFAULT_RADIO_BUTTON = 0x4000
|
|
62
|
+
TDF_CAN_BE_MINIMIZED = 0x8000
|
|
63
|
+
|
|
64
|
+
# Task Dialog 通知
|
|
65
|
+
TDN_CREATED = 0
|
|
66
|
+
TDN_NAVIGATED = 1
|
|
67
|
+
TDN_BUTTON_CLICKED = 2
|
|
68
|
+
TDN_HYPERLINK_CLICKED = 3
|
|
69
|
+
TDN_TIMER = 4
|
|
70
|
+
TDN_DESTROYED = 5
|
|
71
|
+
TDN_RADIO_BUTTON_CLICKED = 6
|
|
72
|
+
TDN_DIALOG_CONSTRUCTED = 7
|
|
73
|
+
TDN_VERIFICATION_CLICKED = 8
|
|
74
|
+
TDN_HELP = 9
|
|
75
|
+
TDN_EXPANDO_BUTTON_CLICKED = 10
|
|
76
|
+
|
|
77
|
+
# Windows 消息
|
|
78
|
+
WM_USER = 0x0400
|
|
79
|
+
TDM_SET_PROGRESS_BAR_POS = WM_USER + 114
|
|
80
|
+
|
|
81
|
+
CommonButtonLiteral = Literal["ok", "yes", "no", "cancel", "retry", "close"]
|
|
82
|
+
IconLiteral = Literal["warning", "error", "information", "shield"]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# --- C 结构体定义 (使用 ctypes) ---
|
|
86
|
+
|
|
87
|
+
class TASKDIALOG_BUTTON(ctypes.Structure):
|
|
88
|
+
_pack_ = 1
|
|
89
|
+
_fields_ = [("nButtonID", ctypes.c_int),
|
|
90
|
+
("pszButtonText", wintypes.LPCWSTR)]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# 定义回调函数指针原型
|
|
94
|
+
PFTASKDIALOGCALLBACK = ctypes.WINFUNCTYPE(
|
|
95
|
+
ctypes.HRESULT, # 返回值
|
|
96
|
+
wintypes.HWND, # hwnd
|
|
97
|
+
ctypes.c_uint, # msg
|
|
98
|
+
ctypes.c_size_t, # wParam
|
|
99
|
+
ctypes.c_size_t, # lParam
|
|
100
|
+
ctypes.c_ssize_t # lpRefData
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TASKDIALOGCONFIG(ctypes.Structure):
|
|
105
|
+
_pack_ = 1
|
|
106
|
+
_fields_ = [
|
|
107
|
+
("cbSize", ctypes.c_uint),
|
|
108
|
+
("hwndParent", wintypes.HWND),
|
|
109
|
+
("hInstance", wintypes.HINSTANCE),
|
|
110
|
+
("dwFlags", ctypes.c_uint),
|
|
111
|
+
("dwCommonButtons", ctypes.c_uint),
|
|
112
|
+
("pszWindowTitle", wintypes.LPCWSTR),
|
|
113
|
+
("pszMainIcon", wintypes.LPCWSTR),
|
|
114
|
+
("pszMainInstruction", wintypes.LPCWSTR),
|
|
115
|
+
("pszContent", wintypes.LPCWSTR),
|
|
116
|
+
("cButtons", ctypes.c_uint),
|
|
117
|
+
("pButtons", ctypes.POINTER(TASKDIALOG_BUTTON)),
|
|
118
|
+
("nDefaultButton", ctypes.c_int),
|
|
119
|
+
("cRadioButtons", ctypes.c_uint),
|
|
120
|
+
("pRadioButtons", ctypes.POINTER(TASKDIALOG_BUTTON)),
|
|
121
|
+
("nDefaultRadioButton", ctypes.c_int),
|
|
122
|
+
("pszVerificationText", wintypes.LPCWSTR),
|
|
123
|
+
("pszExpandedInformation", wintypes.LPCWSTR),
|
|
124
|
+
("pszExpandedControlText", wintypes.LPCWSTR),
|
|
125
|
+
("pszCollapsedControlText", wintypes.LPCWSTR),
|
|
126
|
+
("pszFooterIcon", wintypes.LPCWSTR),
|
|
127
|
+
("pszFooter", wintypes.LPCWSTR),
|
|
128
|
+
("pfCallback", PFTASKDIALOGCALLBACK), # 使用定义好的原型
|
|
129
|
+
("lpCallbackData", ctypes.c_ssize_t),
|
|
130
|
+
("cxWidth", ctypes.c_uint)
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# --- 加载 comctl32.dll 并定义函数原型 ---
|
|
135
|
+
|
|
136
|
+
comctl32 = ctypes.WinDLL('comctl32')
|
|
137
|
+
user32 = ctypes.WinDLL('user32')
|
|
138
|
+
|
|
139
|
+
TaskDialogIndirect = comctl32.TaskDialogIndirect
|
|
140
|
+
TaskDialogIndirect.restype = ctypes.HRESULT
|
|
141
|
+
TaskDialogIndirect.argtypes = [
|
|
142
|
+
ctypes.POINTER(TASKDIALOGCONFIG),
|
|
143
|
+
ctypes.POINTER(ctypes.c_int),
|
|
144
|
+
ctypes.POINTER(ctypes.c_int),
|
|
145
|
+
ctypes.POINTER(wintypes.BOOL)
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# --- Python 封装类 ---
|
|
150
|
+
|
|
151
|
+
class TaskDialog:
|
|
152
|
+
"""
|
|
153
|
+
一个用于显示 Windows TaskDialog 的 Python 封装类。
|
|
154
|
+
支持自定义按钮、单选按钮、进度条、验证框等。
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
def __init__(self,
|
|
158
|
+
parent_hwnd: Optional[int] = None,
|
|
159
|
+
title: str = "Task Dialog",
|
|
160
|
+
main_instruction: str = "",
|
|
161
|
+
content: str = "",
|
|
162
|
+
common_buttons: int | List[CommonButtonLiteral] = TDCBF_OK_BUTTON,
|
|
163
|
+
main_icon: Optional[wintypes.LPWSTR | int | IconLiteral] = None,
|
|
164
|
+
footer: str = "",
|
|
165
|
+
custom_buttons: Optional[List[Tuple[int, str]]] = None,
|
|
166
|
+
default_button: int = 0,
|
|
167
|
+
radio_buttons: Optional[List[Tuple[int, str]]] = None,
|
|
168
|
+
default_radio_button: int = 0,
|
|
169
|
+
verification_text: Optional[str] = None,
|
|
170
|
+
verification_checked_by_default: bool = False,
|
|
171
|
+
show_progress_bar: bool = False,
|
|
172
|
+
show_marquee_progress_bar: bool = False
|
|
173
|
+
):
|
|
174
|
+
"""初始化 TaskDialog 实例。
|
|
175
|
+
|
|
176
|
+
:param parent_hwnd: 父窗口的句柄。
|
|
177
|
+
:param title: 对话框窗口的标题。
|
|
178
|
+
:param main_instruction: 对话框的主要指令文本。
|
|
179
|
+
:param content: 对话框的详细内容文本。
|
|
180
|
+
:param common_buttons: 要显示的通用按钮。可以是以下两种形式之一:
|
|
181
|
+
1. TDCBF_* 常量的按位或组合 (例如 TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON)
|
|
182
|
+
2. 字符串列表,支持 "ok", "yes", "no", "cancel", "retry", "close"
|
|
183
|
+
:param main_icon: 主图标。可以是以下几种形式之一:
|
|
184
|
+
1. TD_*_ICON 常量之一
|
|
185
|
+
2. HICON 句柄
|
|
186
|
+
3. 字符串:"warning", "error", "information", "shield"
|
|
187
|
+
:param footer: 页脚区域显示的文本。
|
|
188
|
+
:param custom_buttons: 自定义按钮列表。每个元组包含 (按钮ID, 按钮文本)。
|
|
189
|
+
:param default_button: 默认按钮的ID。可以是通用按钮ID (例如 IDOK) 或自定义按钮ID。
|
|
190
|
+
:param radio_buttons: 单选按钮列表。每个元组包含 (按钮ID, 按钮文本)。
|
|
191
|
+
:param default_radio_button: 默认选中的单选按钮的ID。
|
|
192
|
+
:param verification_text: 验证复选框的文本。如果为 None,则不显示复选框。
|
|
193
|
+
:param verification_checked_by_default: 验证复选框是否默认勾选。
|
|
194
|
+
:param show_progress_bar: 是否显示标准进度条。
|
|
195
|
+
:param show_marquee_progress_bar: 是否显示跑马灯式进度条。
|
|
196
|
+
"""
|
|
197
|
+
self.config = TASKDIALOGCONFIG()
|
|
198
|
+
self.config.cbSize = ctypes.sizeof(TASKDIALOGCONFIG)
|
|
199
|
+
self.config.hwndParent = parent_hwnd
|
|
200
|
+
self.config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW
|
|
201
|
+
self.config.dwCommonButtons = self._process_common_buttons(common_buttons)
|
|
202
|
+
self.config.pszWindowTitle = title
|
|
203
|
+
self.config.pszMainInstruction = main_instruction
|
|
204
|
+
self.config.pszContent = content
|
|
205
|
+
self.config.pszFooter = footer
|
|
206
|
+
|
|
207
|
+
self.progress: int = 0
|
|
208
|
+
if show_progress_bar or show_marquee_progress_bar:
|
|
209
|
+
# 进度条暂时还没实现
|
|
210
|
+
raise NotImplementedError("Progress bar is not implemented yet.")
|
|
211
|
+
self.config.dwFlags |= TDF_CALLBACK_TIMER
|
|
212
|
+
if show_progress_bar:
|
|
213
|
+
self.config.dwFlags |= TDF_SHOW_PROGRESS_BAR
|
|
214
|
+
else:
|
|
215
|
+
self.config.dwFlags |= TDF_SHOW_MARQUEE_PROGRESS_BAR
|
|
216
|
+
|
|
217
|
+
# 将实例方法转为 C 回调函数指针。
|
|
218
|
+
# 必须将其保存为实例成员,否则会被垃圾回收!
|
|
219
|
+
self._callback_func_ptr = PFTASKDIALOGCALLBACK(self._callback)
|
|
220
|
+
self.config.pfCallback = self._callback_func_ptr
|
|
221
|
+
# 将本实例的id作为lpCallbackData传递,以便在回调中识别
|
|
222
|
+
self.config.lpCallbackData = id(self)
|
|
223
|
+
|
|
224
|
+
# --- 图标设置 ---
|
|
225
|
+
processed_icon = self._process_main_icon(main_icon)
|
|
226
|
+
if processed_icon is not None:
|
|
227
|
+
if isinstance(processed_icon, wintypes.LPWSTR):
|
|
228
|
+
self.config.pszMainIcon = processed_icon
|
|
229
|
+
else:
|
|
230
|
+
self.config.dwFlags |= TDF_USE_HICON_MAIN
|
|
231
|
+
self.config.hMainIcon = processed_icon
|
|
232
|
+
|
|
233
|
+
# --- 自定义按钮设置 ---
|
|
234
|
+
self.custom_buttons_list = []
|
|
235
|
+
if custom_buttons:
|
|
236
|
+
self.config.cButtons = len(custom_buttons)
|
|
237
|
+
button_array_type = TASKDIALOG_BUTTON * len(custom_buttons)
|
|
238
|
+
self.custom_buttons_list = button_array_type()
|
|
239
|
+
for i, (btn_id, btn_text) in enumerate(custom_buttons):
|
|
240
|
+
self.custom_buttons_list[i].nButtonID = btn_id
|
|
241
|
+
self.custom_buttons_list[i].pszButtonText = btn_text
|
|
242
|
+
self.config.pButtons = self.custom_buttons_list
|
|
243
|
+
|
|
244
|
+
if default_button:
|
|
245
|
+
self.config.nDefaultButton = default_button
|
|
246
|
+
|
|
247
|
+
# --- 单选按钮设置 ---
|
|
248
|
+
self.radio_buttons_list = []
|
|
249
|
+
if radio_buttons:
|
|
250
|
+
self.config.cRadioButtons = len(radio_buttons)
|
|
251
|
+
radio_array_type = TASKDIALOG_BUTTON * len(radio_buttons)
|
|
252
|
+
self.radio_buttons_list = radio_array_type()
|
|
253
|
+
for i, (btn_id, btn_text) in enumerate(radio_buttons):
|
|
254
|
+
self.radio_buttons_list[i].nButtonID = btn_id
|
|
255
|
+
self.radio_buttons_list[i].pszButtonText = btn_text
|
|
256
|
+
self.config.pRadioButtons = self.radio_buttons_list
|
|
257
|
+
|
|
258
|
+
if default_radio_button:
|
|
259
|
+
self.config.nDefaultRadioButton = default_radio_button
|
|
260
|
+
|
|
261
|
+
# --- 验证复选框设置 ---
|
|
262
|
+
if verification_text:
|
|
263
|
+
self.config.pszVerificationText = verification_text
|
|
264
|
+
if verification_checked_by_default:
|
|
265
|
+
self.config.dwFlags |= TDF_VERIFICATION_FLAG_CHECKED
|
|
266
|
+
|
|
267
|
+
def _process_common_buttons(self, common_buttons: int | List[CommonButtonLiteral]) -> int:
|
|
268
|
+
"""处理 common_buttons 参数,支持常量和字符串列表两种形式"""
|
|
269
|
+
if isinstance(common_buttons, int):
|
|
270
|
+
# 直接使用 Win32 常量
|
|
271
|
+
return common_buttons
|
|
272
|
+
elif isinstance(common_buttons, list):
|
|
273
|
+
# 处理字符串列表
|
|
274
|
+
result = 0
|
|
275
|
+
for button in common_buttons:
|
|
276
|
+
# 使用 match 和 assert_never 进行类型检查
|
|
277
|
+
match button:
|
|
278
|
+
case "ok":
|
|
279
|
+
result |= TDCBF_OK_BUTTON
|
|
280
|
+
case "yes":
|
|
281
|
+
result |= TDCBF_YES_BUTTON
|
|
282
|
+
case "no":
|
|
283
|
+
result |= TDCBF_NO_BUTTON
|
|
284
|
+
case "cancel":
|
|
285
|
+
result |= TDCBF_CANCEL_BUTTON
|
|
286
|
+
case "retry":
|
|
287
|
+
result |= TDCBF_RETRY_BUTTON
|
|
288
|
+
case "close":
|
|
289
|
+
result |= TDCBF_CLOSE_BUTTON
|
|
290
|
+
case _:
|
|
291
|
+
# 这在实际中不会发生,因为类型检查会阻止它
|
|
292
|
+
from typing import assert_never
|
|
293
|
+
assert_never(button)
|
|
294
|
+
return result
|
|
295
|
+
else:
|
|
296
|
+
raise TypeError("common_buttons must be either an int or a list of strings")
|
|
297
|
+
|
|
298
|
+
def _process_main_icon(self, main_icon: Optional[wintypes.LPWSTR | int | IconLiteral]) -> Optional[wintypes.LPWSTR | int]:
|
|
299
|
+
"""处理 main_icon 参数,支持常量和字符串两种形式"""
|
|
300
|
+
if main_icon is None:
|
|
301
|
+
return None
|
|
302
|
+
elif isinstance(main_icon, (wintypes.LPWSTR, int)):
|
|
303
|
+
# 直接使用 Win32 常量或 HICON 句柄
|
|
304
|
+
return main_icon
|
|
305
|
+
elif isinstance(main_icon, str):
|
|
306
|
+
# 处理字符串
|
|
307
|
+
match main_icon:
|
|
308
|
+
case "warning":
|
|
309
|
+
return TD_WARNING_ICON
|
|
310
|
+
case "error":
|
|
311
|
+
return TD_ERROR_ICON
|
|
312
|
+
case "information":
|
|
313
|
+
return TD_INFORMATION_ICON
|
|
314
|
+
case "shield":
|
|
315
|
+
return TD_SHIELD_ICON
|
|
316
|
+
case _:
|
|
317
|
+
# 这在实际中不会发生,因为类型检查会阻止它
|
|
318
|
+
from typing import assert_never
|
|
319
|
+
assert_never(main_icon)
|
|
320
|
+
else:
|
|
321
|
+
raise TypeError("main_icon must be None, a Windows constant, or a string")
|
|
322
|
+
|
|
323
|
+
def _callback(self, hwnd: wintypes.HWND, msg: int, wParam: int, lParam: int, lpRefData: int) -> int:
|
|
324
|
+
# 仅当 lpRefData 指向的是当前这个对象实例时才处理
|
|
325
|
+
if lpRefData != id(self):
|
|
326
|
+
return 0 # S_OK
|
|
327
|
+
|
|
328
|
+
if msg == TDN_TIMER:
|
|
329
|
+
# 更新进度条
|
|
330
|
+
if self.progress < 100:
|
|
331
|
+
self.progress += 5
|
|
332
|
+
# 发送消息给对话框来更新进度条位置
|
|
333
|
+
user32.SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, self.progress, 0)
|
|
334
|
+
else:
|
|
335
|
+
# 示例:进度达到100%后,可以模拟点击OK按钮关闭对话框
|
|
336
|
+
# from ctypes import wintypes
|
|
337
|
+
# user32.PostMessageW(hwnd, wintypes.UINT(1125), IDOK, 0) # TDM_CLICK_BUTTON
|
|
338
|
+
pass
|
|
339
|
+
|
|
340
|
+
elif msg == TDN_DESTROYED:
|
|
341
|
+
# 对话框已销毁
|
|
342
|
+
pass
|
|
343
|
+
|
|
344
|
+
return 0 # S_OK
|
|
345
|
+
|
|
346
|
+
def show(self) -> Tuple[int, int, bool]:
|
|
347
|
+
"""
|
|
348
|
+
显示对话框并返回用户交互的结果。
|
|
349
|
+
|
|
350
|
+
:return: 一个元组 (button_id, radio_button_id, verification_checked)
|
|
351
|
+
- button_id: 用户点击的按钮ID (例如 IDOK, IDCANCEL)。
|
|
352
|
+
- radio_button_id: 用户选择的单选按钮的ID。
|
|
353
|
+
- verification_checked: 验证复选框是否被勾选 (True/False)。
|
|
354
|
+
"""
|
|
355
|
+
pnButton = ctypes.c_int(0)
|
|
356
|
+
pnRadioButton = ctypes.c_int(0)
|
|
357
|
+
pfVerificationFlagChecked = wintypes.BOOL(False)
|
|
358
|
+
|
|
359
|
+
hr = TaskDialogIndirect(
|
|
360
|
+
ctypes.byref(self.config),
|
|
361
|
+
ctypes.byref(pnButton),
|
|
362
|
+
ctypes.byref(pnRadioButton),
|
|
363
|
+
ctypes.byref(pfVerificationFlagChecked)
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
if hr == 0: # S_OK
|
|
367
|
+
return pnButton.value, pnRadioButton.value, bool(pfVerificationFlagChecked.value)
|
|
368
|
+
else:
|
|
369
|
+
raise ctypes.WinError(hr)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
# --- 示例用法 ---
|
|
373
|
+
if __name__ == '__main__':
|
|
374
|
+
|
|
375
|
+
print("--- 示例 1: 简单信息框 ---")
|
|
376
|
+
dlg_simple = TaskDialog(
|
|
377
|
+
title="操作成功",
|
|
378
|
+
main_instruction="您的操作已成功完成。",
|
|
379
|
+
content="文件已保存到您的文档目录。",
|
|
380
|
+
common_buttons=["ok"],
|
|
381
|
+
main_icon="information"
|
|
382
|
+
)
|
|
383
|
+
result_simple, _, _ = dlg_simple.show()
|
|
384
|
+
print(f"用户点击了按钮: {result_simple} (1=OK)\n")
|
|
385
|
+
|
|
386
|
+
print("--- 示例 2: 确认框 ---")
|
|
387
|
+
dlg_confirm = TaskDialog(
|
|
388
|
+
title="确认删除",
|
|
389
|
+
main_instruction="您确定要永久删除这个文件吗?",
|
|
390
|
+
content="这个操作无法撤销。文件将被立即删除。",
|
|
391
|
+
common_buttons=["yes", "no", "cancel"],
|
|
392
|
+
main_icon="warning",
|
|
393
|
+
default_button=IDNO
|
|
394
|
+
)
|
|
395
|
+
result_confirm, _, _ = dlg_confirm.show()
|
|
396
|
+
if result_confirm == IDYES:
|
|
397
|
+
print("用户选择了“是”。")
|
|
398
|
+
elif result_confirm == IDNO:
|
|
399
|
+
print("用户选择了“否”。")
|
|
400
|
+
elif result_confirm == IDCANCEL:
|
|
401
|
+
print("用户选择了“取消”。")
|
|
402
|
+
print(f"返回的按钮ID: {result_confirm}\n")
|
|
403
|
+
|
|
404
|
+
# 示例 3
|
|
405
|
+
print("--- 示例 3: 自定义按钮 ---")
|
|
406
|
+
CUSTOM_BUTTON_SAVE_ID = 101
|
|
407
|
+
CUSTOM_BUTTON_DONT_SAVE_ID = 102
|
|
408
|
+
my_buttons = [
|
|
409
|
+
(CUSTOM_BUTTON_SAVE_ID, "保存并退出"),
|
|
410
|
+
(CUSTOM_BUTTON_DONT_SAVE_ID, "不保存直接退出")
|
|
411
|
+
]
|
|
412
|
+
dlg_custom = TaskDialog(
|
|
413
|
+
title="未保存的更改",
|
|
414
|
+
main_instruction="文档中有未保存的更改,您想如何处理?",
|
|
415
|
+
custom_buttons=my_buttons,
|
|
416
|
+
common_buttons=["cancel"],
|
|
417
|
+
main_icon="warning",
|
|
418
|
+
footer="这是一个重要的提醒!"
|
|
419
|
+
)
|
|
420
|
+
result_custom, _, _ = dlg_custom.show()
|
|
421
|
+
if result_custom == CUSTOM_BUTTON_SAVE_ID:
|
|
422
|
+
print("用户选择了“保存并退出”。")
|
|
423
|
+
elif result_custom == CUSTOM_BUTTON_DONT_SAVE_ID:
|
|
424
|
+
print("用户选择了“不保存直接退出”。")
|
|
425
|
+
elif result_custom == IDCANCEL:
|
|
426
|
+
print("用户选择了“取消”。")
|
|
427
|
+
print(f"返回的按钮ID: {result_custom}\n")
|
|
428
|
+
|
|
429
|
+
# 示例 4: 带单选按钮和验证框的对话框
|
|
430
|
+
print("--- 示例 4: 单选按钮和验证框 ---")
|
|
431
|
+
RADIO_BTN_WORD_ID = 201
|
|
432
|
+
RADIO_BTN_EXCEL_ID = 202
|
|
433
|
+
RADIO_BTN_PDF_ID = 203
|
|
434
|
+
|
|
435
|
+
radio_buttons = [
|
|
436
|
+
(RADIO_BTN_WORD_ID, "保存为 Word 文档 (.docx)"),
|
|
437
|
+
(RADIO_BTN_EXCEL_ID, "保存为 Excel 表格 (.xlsx)"),
|
|
438
|
+
(RADIO_BTN_PDF_ID, "导出为 PDF 文档 (.pdf)")
|
|
439
|
+
]
|
|
440
|
+
|
|
441
|
+
dlg_radio = TaskDialog(
|
|
442
|
+
title="选择导出格式",
|
|
443
|
+
main_instruction="请选择您想要导出的文件格式。",
|
|
444
|
+
content="选择一个格式后,点击“确定”继续。",
|
|
445
|
+
common_buttons=["ok", "cancel"],
|
|
446
|
+
main_icon="information",
|
|
447
|
+
radio_buttons=radio_buttons,
|
|
448
|
+
default_radio_button=RADIO_BTN_PDF_ID, # 默认选中PDF
|
|
449
|
+
verification_text="设为我的默认导出选项",
|
|
450
|
+
verification_checked_by_default=True
|
|
451
|
+
)
|
|
452
|
+
btn_id, radio_id, checked = dlg_radio.show()
|
|
453
|
+
|
|
454
|
+
if btn_id == IDOK:
|
|
455
|
+
print(f"用户点击了“确定”。")
|
|
456
|
+
if radio_id == RADIO_BTN_WORD_ID:
|
|
457
|
+
print("选择了导出为 Word。")
|
|
458
|
+
elif radio_id == RADIO_BTN_EXCEL_ID:
|
|
459
|
+
print("选择了导出为 Excel。")
|
|
460
|
+
elif radio_id == RADIO_BTN_PDF_ID:
|
|
461
|
+
print("选择了导出为 PDF。")
|
|
462
|
+
|
|
463
|
+
if checked:
|
|
464
|
+
print("用户勾选了“设为我的默认导出选项”。")
|
|
465
|
+
else:
|
|
466
|
+
print("用户未勾选“设为我的默认导出选项”。")
|
|
467
|
+
else:
|
|
468
|
+
print("用户点击了“取消”。")
|
|
469
|
+
print(f"返回的按钮ID: {btn_id}, 单选按钮ID: {radio_id}, 验证框状态: {checked}\n")
|
kotonebot/kaa/config/produce.py
CHANGED
|
@@ -4,7 +4,9 @@ import uuid
|
|
|
4
4
|
import re
|
|
5
5
|
import logging
|
|
6
6
|
from typing import Literal
|
|
7
|
-
from pydantic import BaseModel, ConfigDict, field_serializer, field_validator
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, ValidationError, field_serializer, field_validator
|
|
8
|
+
|
|
9
|
+
from kotonebot.kaa.errors import ProduceSolutionInvalidError, ProduceSolutionNotFoundError
|
|
8
10
|
|
|
9
11
|
from .const import ProduceAction, RecommendCardDetectionMode
|
|
10
12
|
|
|
@@ -217,17 +219,17 @@ class ProduceSolutionManager:
|
|
|
217
219
|
|
|
218
220
|
:param id: 方案ID
|
|
219
221
|
:return: 方案对象
|
|
220
|
-
:raises
|
|
222
|
+
:raises ProduceSloutionNotFoundError: 当方案不存在时
|
|
221
223
|
"""
|
|
222
224
|
file_path = self._find_file_path_by_id(id)
|
|
223
225
|
if not file_path:
|
|
224
|
-
raise
|
|
226
|
+
raise ProduceSolutionNotFoundError(id)
|
|
225
227
|
|
|
226
228
|
try:
|
|
227
229
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
228
230
|
return ProduceSolution.model_validate_json(f.read())
|
|
229
|
-
except
|
|
230
|
-
raise
|
|
231
|
+
except ValidationError as e:
|
|
232
|
+
raise ProduceSolutionInvalidError(id, file_path, e)
|
|
231
233
|
|
|
232
234
|
def duplicate(self, id: str) -> ProduceSolution:
|
|
233
235
|
"""
|
|
@@ -235,7 +237,7 @@ class ProduceSolutionManager:
|
|
|
235
237
|
|
|
236
238
|
:param id: 要复制的方案ID
|
|
237
239
|
:return: 新的方案对象(具有新的ID和名称)
|
|
238
|
-
:raises
|
|
240
|
+
:raises ProduceSolutionNotFoundError: 当原方案不存在时
|
|
239
241
|
"""
|
|
240
242
|
original = self.read(id)
|
|
241
243
|
|
kotonebot/kaa/config/schema.py
CHANGED
|
@@ -64,6 +64,8 @@ class ContestConfig(ConfigBaseModel):
|
|
|
64
64
|
select_which_contestant: Literal[1, 2, 3] = 1
|
|
65
65
|
"""选择第几个挑战者"""
|
|
66
66
|
|
|
67
|
+
when_no_set: Literal['remind', 'wait', 'auto_set', 'auto_set_silent'] = 'remind'
|
|
68
|
+
"""竞赛队伍未编成时应该:remind=通知我并跳过竞赛,wait=提醒我并等待手动编成,auto_set=使用自动编成并提醒,auto_set_silent=使用自动编成不提醒"""
|
|
67
69
|
|
|
68
70
|
|
|
69
71
|
class ProduceConfig(ConfigBaseModel):
|
kotonebot/kaa/errors.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from kotonebot.errors import UserFriendlyError
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class KaaError(Exception):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
class KaaUserFriendlyError(UserFriendlyError, KaaError):
|
|
9
|
+
def __init__(self, message: str, help_link: str):
|
|
10
|
+
super().__init__(message, [
|
|
11
|
+
(0, '打开帮助', lambda: os.startfile(help_link)),
|
|
12
|
+
(1, '知道了', lambda: None)
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
class ProduceSolutionNotFoundError(KaaUserFriendlyError):
|
|
16
|
+
def __init__(self, solution_id: str):
|
|
17
|
+
self.solution_id = solution_id
|
|
18
|
+
super().__init__(
|
|
19
|
+
f'培育方案「{solution_id}」不存在,请检查设置是否正确。',
|
|
20
|
+
'https://kdocs.cn/l/cetCY8mGKHLj?linkname=saPrDAmMd4'
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
class ProduceSolutionInvalidError(KaaUserFriendlyError):
|
|
24
|
+
def __init__(self, solution_id: str, file_path: str, reason: Exception):
|
|
25
|
+
self.solution_id = solution_id
|
|
26
|
+
self.reason = reason
|
|
27
|
+
super().__init__(
|
|
28
|
+
f'培育方案「{solution_id}」(路径 {file_path})存在无效配置,载入失败。',
|
|
29
|
+
'https://kdocs.cn/l/cetCY8mGKHLj?linkname=xnLUW1YYKz'
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
class IdolCardNotFoundError(KaaUserFriendlyError):
|
|
33
|
+
def __init__(self, skin_id: str):
|
|
34
|
+
self.skin_id = skin_id
|
|
35
|
+
super().__init__(
|
|
36
|
+
f'未找到 ID 为「{skin_id}」的偶像卡。请检查游戏内偶像皮肤与培育方案中偶像皮肤是否一致。',
|
|
37
|
+
'https://kdocs.cn/l/cetCY8mGKHLj?linkname=cySASqoPGj'
|
|
38
|
+
)
|
kotonebot/kaa/main/gr.py
CHANGED
|
@@ -14,6 +14,7 @@ from typing import List, Dict, Tuple, Literal, Generator, Callable, Any, get_arg
|
|
|
14
14
|
import cv2
|
|
15
15
|
import gradio as gr
|
|
16
16
|
|
|
17
|
+
from kotonebot.kaa.errors import ProduceSolutionNotFoundError
|
|
17
18
|
from kotonebot.kaa.main import Kaa
|
|
18
19
|
from kotonebot.kaa.db import IdolCard
|
|
19
20
|
from kotonebot.backend.context.context import vars
|
|
@@ -51,7 +52,7 @@ ConfigKey = Literal[
|
|
|
51
52
|
'mini_live_reassign', 'mini_live_duration',
|
|
52
53
|
'online_live_reassign', 'online_live_duration',
|
|
53
54
|
'contest_enabled',
|
|
54
|
-
'select_which_contestant',
|
|
55
|
+
'select_which_contestant', 'when_no_set',
|
|
55
56
|
|
|
56
57
|
# produce
|
|
57
58
|
'produce_enabled', 'selected_solution_id', 'produce_count',
|
|
@@ -1250,6 +1251,20 @@ class KotoneBotUI:
|
|
|
1250
1251
|
interactive=True,
|
|
1251
1252
|
info=ContestConfig.model_fields['select_which_contestant'].description
|
|
1252
1253
|
)
|
|
1254
|
+
|
|
1255
|
+
when_no_set_choices = [
|
|
1256
|
+
("通知我并跳过竞赛", "remind"),
|
|
1257
|
+
("提醒我并等待手动编成", "wait"),
|
|
1258
|
+
("使用自动编成并提醒我", "auto_set"),
|
|
1259
|
+
("使用自动编成", "auto_set_silent")
|
|
1260
|
+
]
|
|
1261
|
+
when_no_set = gr.Dropdown(
|
|
1262
|
+
choices=when_no_set_choices,
|
|
1263
|
+
value=self.current_config.options.contest.when_no_set,
|
|
1264
|
+
label="竞赛队伍未编成时",
|
|
1265
|
+
interactive=True,
|
|
1266
|
+
info=ContestConfig.model_fields['when_no_set'].description
|
|
1267
|
+
)
|
|
1253
1268
|
contest_enabled.change(
|
|
1254
1269
|
fn=lambda x: gr.Group(visible=x),
|
|
1255
1270
|
inputs=[contest_enabled],
|
|
@@ -1259,10 +1274,12 @@ class KotoneBotUI:
|
|
|
1259
1274
|
def set_config(config: BaseConfig, data: dict[ConfigKey, Any]) -> None:
|
|
1260
1275
|
config.contest.enabled = data['contest_enabled']
|
|
1261
1276
|
config.contest.select_which_contestant = data['select_which_contestant']
|
|
1262
|
-
|
|
1277
|
+
config.contest.when_no_set = data['when_no_set']
|
|
1278
|
+
|
|
1263
1279
|
return set_config, {
|
|
1264
1280
|
'contest_enabled': contest_enabled,
|
|
1265
|
-
'select_which_contestant': select_which_contestant
|
|
1281
|
+
'select_which_contestant': select_which_contestant,
|
|
1282
|
+
'when_no_set': when_no_set
|
|
1266
1283
|
}
|
|
1267
1284
|
|
|
1268
1285
|
def _create_produce_settings(self) -> ConfigBuilderReturnValue:
|
|
@@ -1353,7 +1370,7 @@ class KotoneBotUI:
|
|
|
1353
1370
|
if selected_solution_id:
|
|
1354
1371
|
try:
|
|
1355
1372
|
current_solution = solution_manager.read(selected_solution_id)
|
|
1356
|
-
except
|
|
1373
|
+
except ProduceSolutionNotFoundError:
|
|
1357
1374
|
pass
|
|
1358
1375
|
|
|
1359
1376
|
if current_solution is None:
|
|
@@ -1675,7 +1692,7 @@ class KotoneBotUI:
|
|
|
1675
1692
|
gr.Checkbox(value=solution.data.skip_commu, visible=True),
|
|
1676
1693
|
gr.Button(visible=True), # save_solution_btn
|
|
1677
1694
|
]
|
|
1678
|
-
except
|
|
1695
|
+
except ProduceSolutionNotFoundError:
|
|
1679
1696
|
gr.Warning(f"培育方案 {solution_id} 不存在")
|
|
1680
1697
|
return on_solution_change(None)
|
|
1681
1698
|
except Exception as e:
|
kotonebot/kaa/metadata.py
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
WHATS_NEW = """
|
|
2
2
|
# 更新日志
|
|
3
3
|
## kaa
|
|
4
|
+
### v2025.7.27.0
|
|
5
|
+
脚本:
|
|
6
|
+
* [修复] 修复竞赛总是卡在跳过页面(#738ec9e)
|
|
7
|
+
* [修复] 修复 DMM 版启动游戏的过程中无法中断的问题(#ceaaed7)
|
|
8
|
+
* [新增] 即使执行任务出错也会执行关闭游戏任务(#9c9e4af)
|
|
9
|
+
* [新增] 优化培育方案错误与选人未找到错误的提示(#b51f9cd)
|
|
10
|
+
* [新增] 竞赛未编成时支持暂停与通知(#3be8485)
|
|
11
|
+
|
|
12
|
+
框架:
|
|
13
|
+
* [新增] Task 新增 pre、post、regular、manual 四种 run_at 类型(#0b7054e)
|
|
14
|
+
* [新增] 新增 MessageBox 与 TaskDialog 的 Win32API 封装(#3e544e9)
|
|
15
|
+
* [新增] 支持任务执行中只跳过或停止当前任务(#a167cbf)
|
|
16
|
+
* [重构] 移除了废弃的 dispatcher 与 action 分发器重载(#09252c5)
|
|
17
|
+
|
|
4
18
|
### v2025.7.20.0
|
|
5
19
|
脚本:
|
|
6
20
|
* [修复] 修复商店购买中购买推荐商品时点击推荐标签不生效的问题(#b07d4d3)
|
|
Binary file
|
kotonebot/kaa/resources/game.db
CHANGED
|
Binary file
|
|
Binary file
|