Initial release

This commit is contained in:
Levent Duivel 2023-10-12 15:07:15 +05:00 committed by GitHub
parent bb5d2e2510
commit 592087fea9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 7780 additions and 0 deletions

20
Pipfile Normal file
View File

@ -0,0 +1,20 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
pywinauto = "*"
pillow = "*"
psutil = "*"
pytesseract = "*"
pyinstaller = "*"
httpx = {extras = ["brotli", "http2"], version = "*"}
tqdm = "*"
rapidfuzz = "*"
[dev-packages]
[requires]
python_version = "3.11"
python_full_version = "3.11.5"

524
Pipfile.lock generated Normal file
View File

@ -0,0 +1,524 @@
{
"_meta": {
"hash": {
"sha256": "cd3f12ab9e04f6f3f5949a12cd0ff2221ba1a12584ad3096659443a37e92ea79"
},
"pipfile-spec": 6,
"requires": {
"python_full_version": "3.11.5",
"python_version": "3.11"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"altgraph": {
"hashes": [
"sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406",
"sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"
],
"version": "==0.17.4"
},
"anyio": {
"hashes": [
"sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f",
"sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a"
],
"markers": "python_version >= '3.8'",
"version": "==4.0.0"
},
"brotli": {
"hashes": [
"sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208",
"sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48",
"sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354",
"sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a",
"sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128",
"sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c",
"sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088",
"sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9",
"sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a",
"sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3",
"sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438",
"sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578",
"sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b",
"sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b",
"sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68",
"sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d",
"sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd",
"sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409",
"sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da",
"sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50",
"sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0",
"sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180",
"sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d",
"sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112",
"sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc",
"sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265",
"sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327",
"sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95",
"sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd",
"sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914",
"sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0",
"sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a",
"sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7",
"sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0",
"sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451",
"sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f",
"sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e",
"sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248",
"sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91",
"sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724",
"sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966",
"sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97",
"sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d",
"sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf",
"sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac",
"sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951",
"sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74",
"sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60",
"sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c",
"sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1",
"sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8",
"sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d",
"sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc",
"sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61",
"sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460",
"sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751",
"sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9",
"sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1",
"sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474",
"sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2",
"sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6",
"sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9",
"sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2",
"sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467",
"sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619",
"sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf",
"sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408",
"sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579",
"sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84",
"sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b",
"sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59",
"sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752",
"sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80",
"sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0",
"sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2",
"sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3",
"sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64",
"sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643",
"sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e",
"sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985",
"sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596",
"sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2",
"sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064"
],
"version": "==1.1.0"
},
"certifi": {
"hashes": [
"sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082",
"sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"
],
"markers": "python_version >= '3.6'",
"version": "==2023.7.22"
},
"colorama": {
"hashes": [
"sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44",
"sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"
],
"markers": "platform_system == 'Windows'",
"version": "==0.4.6"
},
"comtypes": {
"hashes": [
"sha256:26f261b1eed6972d5cdaa3af1fadb3fa76fc59877d0a1293835327a76573380d",
"sha256:c8f2f0e995d73baf0bd899a948d62adeb9ab908c8270c66a67ff09dfcf4872b7"
],
"version": "==1.2.0"
},
"h11": {
"hashes": [
"sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
"sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
],
"markers": "python_version >= '3.7'",
"version": "==0.14.0"
},
"h2": {
"hashes": [
"sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d",
"sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"
],
"version": "==4.1.0"
},
"hpack": {
"hashes": [
"sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c",
"sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"
],
"markers": "python_full_version >= '3.6.1'",
"version": "==4.0.0"
},
"httpcore": {
"hashes": [
"sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9",
"sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced"
],
"markers": "python_version >= '3.8'",
"version": "==0.18.0"
},
"httpx": {
"extras": [
"brotli",
"http2"
],
"hashes": [
"sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100",
"sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875"
],
"markers": "python_version >= '3.8'",
"version": "==0.25.0"
},
"hyperframe": {
"hashes": [
"sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15",
"sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"
],
"markers": "python_full_version >= '3.6.1'",
"version": "==6.0.1"
},
"idna": {
"hashes": [
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
],
"markers": "python_version >= '3.5'",
"version": "==3.4"
},
"packaging": {
"hashes": [
"sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
"sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"
],
"markers": "python_version >= '3.7'",
"version": "==23.2"
},
"pefile": {
"hashes": [
"sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc",
"sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"
],
"markers": "sys_platform == 'win32'",
"version": "==2023.2.7"
},
"pillow": {
"hashes": [
"sha256:0462b1496505a3462d0f35dc1c4d7b54069747d65d00ef48e736acda2c8cbdff",
"sha256:186f7e04248103482ea6354af6d5bcedb62941ee08f7f788a1c7707bc720c66f",
"sha256:19e9adb3f22d4c416e7cd79b01375b17159d6990003633ff1d8377e21b7f1b21",
"sha256:28444cb6ad49726127d6b340217f0627abc8732f1194fd5352dec5e6a0105635",
"sha256:2872f2d7846cf39b3dbff64bc1104cc48c76145854256451d33c5faa55c04d1a",
"sha256:2cc6b86ece42a11f16f55fe8903595eff2b25e0358dec635d0a701ac9586588f",
"sha256:2d7e91b4379f7a76b31c2dda84ab9e20c6220488e50f7822e59dac36b0cd92b1",
"sha256:2fa6dd2661838c66f1a5473f3b49ab610c98a128fc08afbe81b91a1f0bf8c51d",
"sha256:32bec7423cdf25c9038fef614a853c9d25c07590e1a870ed471f47fb80b244db",
"sha256:3855447d98cced8670aaa63683808df905e956f00348732448b5a6df67ee5849",
"sha256:3a04359f308ebee571a3127fdb1bd01f88ba6f6fb6d087f8dd2e0d9bff43f2a7",
"sha256:3a0d3e54ab1df9df51b914b2233cf779a5a10dfd1ce339d0421748232cea9876",
"sha256:44e7e4587392953e5e251190a964675f61e4dae88d1e6edbe9f36d6243547ff3",
"sha256:459307cacdd4138edee3875bbe22a2492519e060660eaf378ba3b405d1c66317",
"sha256:4ce90f8a24e1c15465048959f1e94309dfef93af272633e8f37361b824532e91",
"sha256:50bd5f1ebafe9362ad622072a1d2f5850ecfa44303531ff14353a4059113b12d",
"sha256:522ff4ac3aaf839242c6f4e5b406634bfea002469656ae8358644fc6c4856a3b",
"sha256:552912dbca585b74d75279a7570dd29fa43b6d93594abb494ebb31ac19ace6bd",
"sha256:5d6c9049c6274c1bb565021367431ad04481ebb54872edecfcd6088d27edd6ed",
"sha256:697a06bdcedd473b35e50a7e7506b1d8ceb832dc238a336bd6f4f5aa91a4b500",
"sha256:71671503e3015da1b50bd18951e2f9daf5b6ffe36d16f1eb2c45711a301521a7",
"sha256:723bd25051454cea9990203405fa6b74e043ea76d4968166dfd2569b0210886a",
"sha256:764d2c0daf9c4d40ad12fbc0abd5da3af7f8aa11daf87e4fa1b834000f4b6b0a",
"sha256:787bb0169d2385a798888e1122c980c6eff26bf941a8ea79747d35d8f9210ca0",
"sha256:7f771e7219ff04b79e231d099c0a28ed83aa82af91fd5fa9fdb28f5b8d5addaf",
"sha256:847e8d1017c741c735d3cd1883fa7b03ded4f825a6e5fcb9378fd813edee995f",
"sha256:84efb46e8d881bb06b35d1d541aa87f574b58e87f781cbba8d200daa835b42e1",
"sha256:898f1d306298ff40dc1b9ca24824f0488f6f039bc0e25cfb549d3195ffa17088",
"sha256:8b451d6ead6e3500b6ce5c7916a43d8d8d25ad74b9102a629baccc0808c54971",
"sha256:8f06be50669087250f319b706decf69ca71fdecd829091a37cc89398ca4dc17a",
"sha256:92a23b0431941a33242b1f0ce6c88a952e09feeea9af4e8be48236a68ffe2205",
"sha256:93139acd8109edcdeffd85e3af8ae7d88b258b3a1e13a038f542b79b6d255c54",
"sha256:98533fd7fa764e5f85eebe56c8e4094db912ccbe6fbf3a58778d543cadd0db08",
"sha256:9f665d1e6474af9f9da5e86c2a3a2d2d6204e04d5af9c06b9d42afa6ebde3f21",
"sha256:b059ac2c4c7a97daafa7dc850b43b2d3667def858a4f112d1aa082e5c3d6cf7d",
"sha256:b1be1c872b9b5fcc229adeadbeb51422a9633abd847c0ff87dc4ef9bb184ae08",
"sha256:b7cf63d2c6928b51d35dfdbda6f2c1fddbe51a6bc4a9d4ee6ea0e11670dd981e",
"sha256:bc2e3069569ea9dbe88d6b8ea38f439a6aad8f6e7a6283a38edf61ddefb3a9bf",
"sha256:bcf1207e2f2385a576832af02702de104be71301c2696d0012b1b93fe34aaa5b",
"sha256:ca26ba5767888c84bf5a0c1a32f069e8204ce8c21d00a49c90dabeba00ce0145",
"sha256:cbe68deb8580462ca0d9eb56a81912f59eb4542e1ef8f987405e35a0179f4ea2",
"sha256:d6caf3cd38449ec3cd8a68b375e0c6fe4b6fd04edb6c9766b55ef84a6e8ddf2d",
"sha256:d72967b06be9300fed5cfbc8b5bafceec48bf7cdc7dab66b1d2549035287191d",
"sha256:d889b53ae2f030f756e61a7bff13684dcd77e9af8b10c6048fb2c559d6ed6eaf",
"sha256:de596695a75496deb3b499c8c4f8e60376e0516e1a774e7bc046f0f48cd620ad",
"sha256:e6a90167bcca1216606223a05e2cf991bb25b14695c518bc65639463d7db722d",
"sha256:ed2d9c0704f2dc4fa980b99d565c0c9a543fe5101c25b3d60488b8ba80f0cce1",
"sha256:ee7810cf7c83fa227ba9125de6084e5e8b08c59038a7b2c9045ef4dde61663b4",
"sha256:f0b4b06da13275bc02adfeb82643c4a6385bd08d26f03068c2796f60d125f6f2",
"sha256:f11c9102c56ffb9ca87134bd025a43d2aba3f1155f508eff88f694b33a9c6d19",
"sha256:f5bb289bb835f9fe1a1e9300d011eef4d69661bb9b34d5e196e5e82c4cb09b37",
"sha256:f6d3d4c905e26354e8f9d82548475c46d8e0889538cb0657aa9c6f0872a37aa4",
"sha256:fcb59711009b0168d6ee0bd8fb5eb259c4ab1717b2f538bbf36bacf207ef7a68",
"sha256:fd2a5403a75b54661182b75ec6132437a181209b901446ee5724b589af8edef1"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==10.0.1"
},
"psutil": {
"hashes": [
"sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d",
"sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217",
"sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4",
"sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c",
"sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f",
"sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da",
"sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4",
"sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42",
"sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5",
"sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4",
"sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9",
"sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f",
"sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30",
"sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"
],
"index": "pypi",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==5.9.5"
},
"pyinstaller": {
"hashes": [
"sha256:0ad7cc3776ca17d0bededcc352cba2b1c89eb4817bfabaf05972b9da8c424935",
"sha256:16a473065291dd7879bf596fa20e65bd9d1e8aafc2cef1bffa3e42e707e2e68e",
"sha256:438a9e0d72a57d5bba4f112d256e39ea4033c76c65414c0693d8311faa14b090",
"sha256:4a75bde5cda259bb31f2294960d75b9d5c148001b2b0bd20a91f9c2116675a6c",
"sha256:52e5b3a2371d7231de17515c7c78d8d4a39d70c8c095e71d55b3b83434a193a8",
"sha256:5314f6f08d2bcbc031778618ba97d9098d106119c2e616b3b081171fe42f5415",
"sha256:68769f5e6722474bb1038e35560444659db8b951388bfe0c669bb52a640cd0eb",
"sha256:aa922d1d73881d0820a341d2c406a571cc94630bdcdc275427c844a12e6e376e",
"sha256:cccdad6cfe7a5db7d7eb8df2e5678f8375268739d5933214e180da300aa54e37",
"sha256:d702cff041f30e7a53500b630e07b081e5328d4655023319253d73935e75ade2",
"sha256:d84b06fb9002109bfc542e76860b81459a8585af0bbdabcfc5dcf272ef230de7",
"sha256:fb6af82989dac7c58bd25ed9ba3323bc443f8c1f03804f69c9f5e363bf4a021c"
],
"index": "pypi",
"markers": "python_version < '3.13' and python_version >= '3.8'",
"version": "==6.0.0"
},
"pyinstaller-hooks-contrib": {
"hashes": [
"sha256:76084b5988e3957a9df169d2a935d65500136967e710ddebf57263f1a909cd80",
"sha256:f34f4c6807210025c8073ebe665f422a3aa2ac5f4c7ebf4c2a26cc77bebf63b5"
],
"markers": "python_version >= '3.7'",
"version": "==2023.9"
},
"pytesseract": {
"hashes": [
"sha256:8f22cc98f765bf13517ead0c70effedb46c153540d25783e04014f28b55a5fc6",
"sha256:f1c3a8b0f07fd01a1085d451f5b8315be6eec1d5577a6796d46dc7a62bd4120f"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==0.3.10"
},
"pywin32": {
"hashes": [
"sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d",
"sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65",
"sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e",
"sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b",
"sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4",
"sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040",
"sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a",
"sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36",
"sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8",
"sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e",
"sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802",
"sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a",
"sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407",
"sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"
],
"version": "==306"
},
"pywin32-ctypes": {
"hashes": [
"sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60",
"sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"
],
"markers": "sys_platform == 'win32'",
"version": "==0.2.2"
},
"pywinauto": {
"hashes": [
"sha256:931ce622d7f402b1892ab472987a1332e4c0681bf87e106f798390d16ca95e58",
"sha256:de23f1e977cc51e7eddd95c8f365710343136433968d1e2ad377962d6bd6540a"
],
"index": "pypi",
"version": "==0.6.8"
},
"rapidfuzz": {
"hashes": [
"sha256:01013ee67fb15608c8c5961af3bc2b1f242cff94c19f53237c9b3f0edb8e0a2d",
"sha256:01d64710060bc3241c08ac1f1a9012c7184f3f4c3d6e2eebb16c6093a03f6a67",
"sha256:02afbe7ed12e9191082ed7bda43398baced1d9d805302b7b010d397de3ae973f",
"sha256:0b9197656a6d71483959bf7d216e7fb7a6b80ca507433bcb3015fb92abc266f8",
"sha256:0d8c6cb80b5d2edf88bf6a88ac6827a353c974405c2d7e3025ed9527a5dbe1a6",
"sha256:0df66e07e42e2831fae84dea481f7803bec7cfa53c31d770e86ac47bb18dcd57",
"sha256:124578029d926b2be32d60b748be95ee0de6cb2753eb49d6d1d6146269b428b9",
"sha256:1276c7f50cd90a48b00084feb25256135c9ace6c599295dd5932949ec30c0e70",
"sha256:1438e68fe8869fe6819a313140e98641b34bfc89234b82486d8fd02044a067e8",
"sha256:1a733c10b1fcc47f837c23ab4a255cc4021a88939ff81baa64d6738231cba33d",
"sha256:1bafbd3e2e9e0b5f740f66155cc7e1e23eee1e1f2c44eff12daf14f90af0e8ab",
"sha256:1ebee7313719dfe652debb74bdd4024e8cf381a59adc6d065520ff927f3445f4",
"sha256:233bf022938c38060a93863ec548e624d69a56d7384634d8bea435b915b88e52",
"sha256:23b07685c21c93cdf6d68b49eccacfe975651b8d99ea8a02687400c60315e5bc",
"sha256:2543fd8d0fb3b1ac065bf94ee54c0ea33343c62481d8e54b6117a88c92c9b721",
"sha256:273c7c7f5b405f2f54d41e805883572d57e1f0a56861f93ca5a6733672088acb",
"sha256:28d03cd33817f6e0bea9b618b460f85ff9c9c3fedc6c19cfa0992f719a0d1801",
"sha256:2e49151572b842d290dcee2cc6f9ce7a7b40b77cc20d0f6d6b54e7afb7bafa5c",
"sha256:3456f4df5b8800315fd161045c996479016c112228e4da370d09ed80c24853e5",
"sha256:37d5f0fbad6c092c89840eea2c4c845564d40849785de74c5e6ff48b47b0ecf6",
"sha256:3af0384132e79fe6f6370d49347649382e04f689277525903bef84d30f3992fd",
"sha256:3db79070888d0dcd4f6a20fd30b8184dd975d6b0f7818acff5d7e07eba19b71f",
"sha256:437508ec1ea6e71a77126715ac6208cb9c3e74272536ebfa79be9dd008cfb85f",
"sha256:46efc5e4675e2bd5118427513f86eaf3689e1482ebd309ad4532bcefae78179d",
"sha256:4fd94acab871afbc845400814134a83512a711e824dc2c9a9776d6123464a221",
"sha256:50ad7bac98a0f00492687eddda73d2c0bdf71c78b52fddaa5901634ae323d3ce",
"sha256:51d47d52c890cbdb2d8b2085d747e557f15efd9c990cb6ae624c8f6948c4aa3a",
"sha256:52c6b7a178f0e800488fa1aede17b00f6397cab0b79d48531504b0d89e45315f",
"sha256:53bbef345644eac1c2d7cc21ade4fe9554fa289f60eb2c576f7fdc454dbc0641",
"sha256:55efb3231bb954f3597313ebdf104289b8d139d5429ad517051855f84e12b94e",
"sha256:59f851c7a54a9652b9598553547e0940244bfce7c9b672bac728efa0b9028d03",
"sha256:5b9a7ab061c1b75b274fc2ebd1d29cfa2e510c36e2f4cd9518a6d56d589003c8",
"sha256:5fe3ef7daecd79f852936528e37528fd88818bc000991e0fea23b9ac5b79e875",
"sha256:61f16bb0f3026853500e7968261831a2e1a35d56947752bb6cf6953afd70b9de",
"sha256:6286510910fcd649471a7f5b77fcc971e673729e7c84216dbf321bead580d5a1",
"sha256:63933792146f3d333680d415cecc237e6275b42ad948d0a798f9a81325517666",
"sha256:66ff93b81b382269dc7c2d46c839ce72e2d2331ad46a06321770bc94016fe236",
"sha256:698488002eb7be2f737e48679ed0cd310b76291f26d8ec792db8345d13eb6573",
"sha256:6b8258846e56b03230fa733d29bb4f9fb1f4790ac97d1ebe9faa3ff9d2850999",
"sha256:6d38596c804a9f2bd49360c15e1f4afbf016f181fe37fc4f1a4ddd247d3e91e5",
"sha256:6f5c8b901b6d3be63591c68e2612f76ad85af27193d0a88d4d87bb047aeafcb3",
"sha256:712dd91d429afaddbf7e86662155f2ad9bc8135fca5803a01035a3c1d76c5977",
"sha256:734046d557550589edb83d5ad1468a1341d1092f1c64f26fd0b1fc50f9efdce1",
"sha256:74b9a1c1fc139d325fb0b89ccc85527d27096a76f6ed690ee3378143cc38e91d",
"sha256:75d1365387ec8ef2128fd7e2f7436aa1a04a1953bc6d7068835bb769cd07c146",
"sha256:76f4162ce5fe08609455d318936ed4aa709f40784be61fb4e200a378137b0230",
"sha256:7f497f850d46c5e08f3340343842a28ede5d3997e5d1cadbd265793cf47417e5",
"sha256:805dc2aa3ac295dcbf2df8c1e420e8a73b1f632d6820a5a1c8506d22c11e0f27",
"sha256:83387fb81c4c0234b199110655779762dd5982cdf9de4f7c321110713193133e",
"sha256:87409e12f9a82aa33a5b845c49dd8d5d4264f2f171f0a69ddc638e100fcc50de",
"sha256:8756461e7ee79723b8f762fc6db226e65eb453bf9fa64b14fc0274d4aaaf9e21",
"sha256:8b38d7677b2f20b137bb7aaf0dcd3d8ac2a2cde65f09f5621bf3f57d9a1e5d6e",
"sha256:8eb33895353bfcc33ccf4b4bae837c0afb4eaf20a0361aa6f0800cef12505e91",
"sha256:8f5d2adc48c181486125d42230e80479a1e0568942e883d1ebdeb76cd3f83470",
"sha256:929e6b71e5b36caee2ee11c209e75a0fcbd716a1b76ae6162b89ee9b591b63b1",
"sha256:93ceb62ade1a0e62696487274002157a58bb751fc82cd25016fc5523ba558ca5",
"sha256:950d1dfd2927cd45c9bb2927933926718f0a17792841e651d42f4d1cb04a5c1d",
"sha256:9814905414696080d8448d6e6df788a0148954ab34d7cd8d75bcb85ba30e0b25",
"sha256:9e1142c8d35fa6f3af8150d02ff8edcbea3723c851d889e8b2172e0d1b99f3f7",
"sha256:9f295842c282fe7fe93bfe7a20e78f33f43418f47fb601f2f0a05df8a8282b43",
"sha256:a0750278693525b5ce58d3b313e432dfa5d90f00d06ae54fa8cde87f2a397eb0",
"sha256:a716efcfc92659d8695291f07da4fa60f42a131dc4ceab583931452dd5662e92",
"sha256:a7215f7c5de912b364d5cf7c4c66915ccf4acf71aafbb8da62ad346569196e15",
"sha256:a74112e2126b428c77db5e96f7ce34e91e750552147305b2d361122cbede2955",
"sha256:a7d6a9f04ea1277add8943d4e144e59215009f54f2668124ff26dee18a875343",
"sha256:a80f9aa4245a49e0677896d1b51b2b3bc36472aff7cec31c4a96f789135f03fe",
"sha256:ab981f9091ae8bd32bca9289fa1019b4ec656543489e7e13e64882d57d989282",
"sha256:b05c7d4b4ddb617e977d648689013e50e5688140ee03538d3760a3a11d4fa8a2",
"sha256:b38c7021f6114cfacba5717192fb3e1e50053261d49a774e645021a2f77e20a3",
"sha256:b6fe2aff0d9b35191701714e05afe08f79eaea376a3a6ca802b72d9e5b48b545",
"sha256:b81b8bc29114ca861fed23da548a837832b85495b0c1b2600e6060e3cf4d50aa",
"sha256:bbb05b1203f683b341f44ebe8fe38afed6e56f606094f9840d6406e4a7bf0eab",
"sha256:bd10d68baabb63a3bb36b683f98fc481fcc62230e493e4b31e316bd5b299ef68",
"sha256:bd50bc90167601963e2a90b820fb862d239ecb096a991bf3ce33ffaa1d6eedee",
"sha256:bf58ba21df06fc8aeef3056fd137eca0a593c2f5c82923a4524d251dc5f3df5d",
"sha256:bfe14711b9a7b744e242a482c6cabb696517a1a9946fc1e88d353cd3eb384788",
"sha256:c006aa481d1b91c2600920ce16e42d208a4b6f318d393aef4dd2172d568f2641",
"sha256:c0150d521199277b5ad8bd3b060a5f3c1dbdf11df0533b4d79f458ef11d07e8c",
"sha256:c2a564f748497b6a5e08a1dc0ac06655f65377cf072c4f0e2c73818acc655d36",
"sha256:c56073ba1d1b25585359ad9769163cb2f3183e7a03c03b914a0667fcbd95dc5c",
"sha256:c67f5ced39aff6277dd772b239ef8aa8fc810200a3b42f69ddbb085ea0e18232",
"sha256:c7f4f6dac25c120de8845a65a97090658c8a976827ac22b6b86e2a16a60bb820",
"sha256:c92d847c997c384670e3b4cf6727cb73a4d7a7ba6457310e2083cf06d56013c4",
"sha256:cc3efc06db79e818f4a6783a4e001b3c8b2c61bd05c0d5c4d333adaf64ed1b34",
"sha256:cfdc74afd93ac71270b5be5c25cb864b733b9ae32b07495705a6ac294ac4c390",
"sha256:d0bda173b0ec1fa546f123088c0d42c9096304771b4c0555d4e08a66a246b3f6",
"sha256:d15c364c5aa8f032dadf5b82fa02b7a4bd9688a961a27961cd5b985203f58037",
"sha256:d188e8fb5a9709931c6a48cc62c4ac9b9d163969333711e426d9dbd134c1489b",
"sha256:d1d81d380ceabc8297880525c9d8b9e93fead38d3d2254e558c36c18aaf2553f",
"sha256:d3198f70b97127e52a4f96bb2f7de447f89baa338ff398eb126930c8e3137ad1",
"sha256:d904ac97f2e370f91e8170802669c8ad68641bf84d742968416b53c5960410c6",
"sha256:da2764604a31fd1e3f1cacf226b43a871cc9f28844a3196c2a6b1ba52ae12922",
"sha256:dd54dd0355225dc3c1d55e233d510adcccee9bb25d656b4cf1136114b92e7bf3",
"sha256:e14799297f194a4480f373e45142ef16d5dc68a42084c0e2018e0bdba56a8fef",
"sha256:e182ea5c809e7ed36ebfbcef4bb1808e213d27b33c036007a33bcbb7ba498356",
"sha256:e77873126eb07e7461f0b675263e6c5d42c8a952e88e4a44eeff96f237b2b024",
"sha256:ed0d5761b44d9dd87278d5c32903bb55632346e4d84ea67ba2e4a84afc3b7d45",
"sha256:ed3da08830c08c8bcd49414cc06b704a760d3067804775facc0df725b52085a4",
"sha256:ef30b5f2720f0acbcfba0e0661a4cc118621c47cf69b5fe92531dfed1e369e1c",
"sha256:f0075ff8990437923da42202b60cf04b5c122ee2856f0cf2344fb890cadecf57",
"sha256:f1e91460baa42f5408f3c062913456a24b2fc1a181959b58a9c06b5eef700ca6",
"sha256:f3effbe9c677658b3149da0d2778a740a6b7d8190c1407fd0c0770a4e223cfe0",
"sha256:f5921780e7995e9ac3cea41fa57b623159d7295788618d3f2946d61328c25c25",
"sha256:f71454249ddd29d8ba5415ed7307e7b7493fc7e9018f1ff496127b8b9a8df94b",
"sha256:f723197f2dbce508a7030dcf6d3fc940117aa54fc876021bf6f6feeaf3825ba1",
"sha256:f7f5ea97886d2ec7b2b9a8172812a76e1d243f2ce705c2f24baf46f9ef5d3951",
"sha256:f813fb663d90038c1171d30ea1b6b275e09fced32f1d12b972c6045d9d4233f2",
"sha256:fc4b1b69a64d337c40fa07a721dae1b1550d90f17973fb348055f6440d597e26"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==3.4.0"
},
"setuptools": {
"hashes": [
"sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87",
"sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"
],
"markers": "python_version >= '3.8'",
"version": "==68.2.2"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"sniffio": {
"hashes": [
"sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101",
"sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"
],
"markers": "python_version >= '3.7'",
"version": "==1.3.0"
},
"tqdm": {
"hashes": [
"sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386",
"sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==4.66.1"
}
},
"develop": {}
}

6572
assets/gc_achievements.json Normal file

File diff suppressed because it is too large Load Diff

48
assets/gc_categories.json Normal file
View File

@ -0,0 +1,48 @@
{
"0": "Wonders of the World",
"1": "Mortal Travails: Series I",
"2": "The Art of Adventure",
"3": "The Hero's Journey",
"4": "Mondstadt: The City of Wind and Song",
"5": "Liyue: The Harbor of Stone and Contracts",
"6": "Elemental Specialist: Series I",
"7": "Marksmanship",
"8": "Challenger: Series I",
"9": "Domains and Spiral Abyss: Series I",
"10": "Olah!: Series I",
"11": "Snezhnaya Does Not Believe in Tears: Series I",
"12": "Stone Harbor's Nostalgia: Series I",
"13": "Meetings in Outrealm: Series I",
"14": "Challenger: Series II",
"15": "Challenger: Series III",
"16": "Visitors on the Icy Mountain",
"17": "Memories of the Heart",
"18": "A Realm Beyond: Series I",
"19": "A Realm Beyond: Series II",
"20": "Challenger: Series IV",
"21": "Meetings in Outrealm: Series II",
"22": "Mortal Travails: Series II",
"23": "A Realm Beyond: Series III",
"24": "Inazuma: The Islands of Thunder and Eternity - Series I",
"25": "Teyvat Fishing Guide: Series I",
"26": "Inazuma: The Islands of Thunder and Eternity - Series II",
"27": "The Chronicles of the Sea of Fog",
"28": "The Light of Day",
"29": "Challenger: Series V",
"30": "Chasmlighter",
"31": "Sumeru: The Rainforest of Lore",
"32": "Mortal Travails: Series III",
"33": "Meetings in Outrealm: Series III",
"34": "Challenger: Series VI",
"35": "Sumeru: The Gilded Desert - Series I",
"36": "Elemental Specialist: Series II",
"37": "Genius Invokation TCG",
"38": "Sumeru: The Gilded Desert - Series II",
"39": "Challenger: Series VII",
"40": "Challenger: Series VIII",
"41": "Blessed Hamada",
"42": "Fontaine: Dance of the Dew-White Springs (I)",
"43": "Mortal Travails: Series IV",
"44": "Meetings in Outrealm: Series IV",
"45": "Fontaine: Dance of the Dew-White Springs (II)"
}

2
build.cmd Normal file
View File

@ -0,0 +1,2 @@
pyinstaller main.spec
pyinstaller submit_to_gc.spec

380
main.py Normal file
View File

@ -0,0 +1,380 @@
import ctypes
import io
import json
import logging
import sys
from time import sleep
from typing import Dict, List, Tuple
from PIL import ImageOps, Image
from rapidfuzz import process, fuzz
from rapidfuzz.utils import default_process
from pywinauto import Application
from pywinauto.controls.hwndwrapper import DialogWrapper
from pywinauto.win32structures import RECT
from utils import find_process, scale_coords_to_resolution, scale_box_to_resolution, bold_color_mask, \
generate_achievement_boxes, scan_image, get_asset_path
button_coords = {
"main_achievement_button": (885, 542),
"main_achievement_category": (249, 384),
"achievement_category": (500, 290),
"achievement_scroll": (969, 448),
"category_scroll": (53, 448),
}
box_coords = {
"achievement_category": RECT(152, 240, 658, 106),
# "achievement": RECT(1167, 208, 878, 138),
# "achievement_categories": RECT(1167, 393, 878, 138),
# "achievement_status": RECT(2208, 195, 220, 161),
}
class AchievementScanner(object):
debug_mode: bool = True
debug_disable_postprocessing: bool = False
window_rect: RECT = None
buttons: Dict[str, tuple] = {} # both are scaled for user's resolution
boxes: Dict[str, RECT] = {}
achievements: Dict[str, bool] = {} # title - completed
categories: List[str] = []
database: List[str] = []
# loop
achievement_id: int = 0
category_id: int = 0
def scale_for_resolution(self):
end_achievement = RECT(1167, 1148, 881, 140)
end_achievement_status = RECT(2219, 1148, 220, 140)
end_achievement_adjust = 167
box_coords.update(generate_achievement_boxes(end_achievement, end_achievement_status, end_achievement_adjust,
key="end_achievement", count=5))
end_category = RECT(173, 1213, 697, 109)
end_category_adjust = 138
box_coords.update(generate_achievement_boxes(end_category, None, end_category_adjust,
key="end_category", count=7))
start_achievement_category = RECT(1167, 400, 900, 126)
start_achievement_category_status = RECT(2224, 400, 192, 126)
start_achievement_category_adjust = 167
box_coords.update(
generate_achievement_boxes(start_achievement_category, start_achievement_category_status,
start_achievement_category_adjust,
key="start_achievement_category", count=5, inversed=True))
start_achievement = RECT(1167, 176, 878, 138)
start_achievement_status = RECT(2208, 176, 220, 138)
start_achievement_adjust = 167
box_coords.update(
generate_achievement_boxes(start_achievement, start_achievement_status, start_achievement_adjust,
key="start_achievement", count=5, inversed=True))
self.window_rect = self.window.element_info.rectangle
resolution = (self.window_rect.width(), self.window_rect.height())
self.buttons = {k: scale_coords_to_resolution(v, resolution) for k, v in button_coords.items()}
self.boxes = {k: scale_box_to_resolution(v, self.window_rect) for k, v in box_coords.items()}
self.logger.debug('ready')
def __init__(self, window: DialogWrapper):
self.window = window
self.logger = logging.getLogger("AchievementScanner")
self.scale_for_resolution()
def scroll_mouse(self, steps: int, coords: tuple):
self.logger.debug(f"Scrolling {steps} times at {coords}")
max_scroll = steps
scrolled = 0
while scrolled < max_scroll:
self.window.wheel_mouse_input(coords=coords, wheel_dist=-100)
scrolled += 1
sleep(0.02)
self.logger.debug(f"{scrolled} / {max_scroll}")
sleep(0.5)
def adjust_scroll_steps(self, category: bool = False):
steps = 35
if self.achievement_id % 15 == 0:
steps -= 1
"""
if self.achievement_id % 14 == 0 or self.achievement_id % 41 == 0:
steps -= 1
if self.achievement_id % 42 == 0:
steps += 1
"""
if category:
steps = 6
if self.category_id % 4 == 0:
steps -= 1
if self.category_id % 33 == 0:
steps -= 1
return steps
@staticmethod
def improve_achievement_text(image: Image.Image) -> Image.Image:
improved = ImageOps.expand(image, border=20, fill='#f0e9dc')
improved = bold_color_mask(improved)
improved = ImageOps.grayscale(improved)
return improved
@staticmethod
def improve_achievement_status(image: Image.Image) -> Image.Image:
improved = bold_color_mask(image, target_color=(187, 167, 145), threshold=50)
improved = ImageOps.grayscale(improved)
return improved
@staticmethod
def improve_achievement_category(image: Image.Image) -> Image.Image:
improved = bold_color_mask(image, target_color=(73, 83, 102), threshold=100)
improved = ImageOps.grayscale(improved)
return improved
def left_click(self, coords: tuple):
self.logger.debug(f"Clicking at {coords}")
max_width, max_height = self.window_rect.width(), self.window_rect.height()
if coords[0] > max_width or coords[1] > max_height:
self.logger.warning(f"Coords {coords} are out of window bounds ({max_width}, {max_height})")
self.window.click_input(button='left', coords=coords)
def go_to_achievements(self):
for _ in range(0, 4):
self.window.type_keys('{ESC}')
sleep(1)
self.left_click(coords=self.buttons['main_achievement_button'])
sleep(2)
self.left_click(coords=self.buttons['main_achievement_category'])
sleep(2)
def load_database(self):
if len(self.database) == 0:
assets = get_asset_path()
with open(assets['gc_achievements.json'], "r", encoding='utf-8') as file:
gc_achievements = json.load(file)
gc_achievements = [v['name'] for k, v in gc_achievements.items()]
with open(assets['gc_categories.json'], "r", encoding='utf-8') as file:
gc_categories = json.load(file)
gc_categories = [v for k, v in gc_categories.items()]
self.database = gc_achievements + gc_categories
self.database.sort() # Leads to faster results down the line
return
def fix_title_by_database(self, title: str):
self.load_database()
result, confidence, choices_type = process.extractOne(title, self.database, processor=default_process)
self.logger.info(f"fix_title_by_database: {title} -> {result} ({confidence} / {choices_type})")
if confidence >= 90.0:
return result
else:
return title
def capture_image(self, box: RECT, improve_func: callable = None, debug_name: str = None):
image = self.window.capture_as_image(rect=box)
if improve_func and not self.debug_disable_postprocessing:
image = improve_func(image)
image_bytes = io.BytesIO()
image.save(image_bytes, format='PNG')
if self.debug_mode:
image_path = f'results\\debug_images\\{debug_name}.png'
image.save(image_path)
return image_bytes
def get_center_of_rect(self, box: RECT) -> Tuple[int, int]:
x, y = int(box.left), int(box.top)
x += int(box.width() / 2)
y += int(box.height() / 2)
# it needs to be within window coords for some reason, when capturing is not
if self.window_rect.left != 0:
x -= self.window_rect.left
if self.window_rect.top != 0:
y -= self.window_rect.top
return x, y
def scan_achievement(self, achievement_name_rect: RECT, status_rect: RECT):
# Capture
self.logger.info(f"Capturing achievement {self.achievement_id}")
self.left_click(coords=self.get_center_of_rect(achievement_name_rect))
title_image_bytes = self.capture_image(achievement_name_rect, improve_func=self.improve_achievement_text,
debug_name=f"{self.achievement_id}")
status_image_bytes = self.capture_image(status_rect, improve_func=self.improve_achievement_status,
debug_name=f"{self.achievement_id}_status")
# Scan
self.logger.info(f"Sending {self.achievement_id} over for scanning to OCR server")
self.left_click(coords=self.get_center_of_rect(status_rect))
scanned_title: str = scan_image(title_image_bytes.getvalue())
scanned_status: str = scan_image(status_image_bytes.getvalue())
# Fix small fuckups
scanned_title = scanned_title.strip()
if scanned_title == '':
return '', False
if scanned_title == "":
scanned_title = "\n" # so .splitlines doesn't crash the thing
scanned_title = scanned_title.splitlines()[0].replace(
"", "\"").replace("", "\"").replace('Deja', 'Déjà')
scanned_title = self.fix_title_by_database(scanned_title)
# OCR Result
self.logger.info(f"Found achievement {self.achievement_id}: {scanned_title}")
self.logger.info(f"Status: {scanned_status}")
return scanned_title, fuzz.partial_ratio("Completed", scanned_status, processor=default_process) >= 90.0
def scan_category(self, category_name_rect, skip: bool = False):
end_of_list_mode = False # debug switch
if end_of_list_mode:
for _ in range(int(285 / 5)):
self.scroll_mouse(35, self.buttons['achievement_scroll'])
category_image_bytes = self.capture_image(category_name_rect, improve_func=self.improve_achievement_category,
debug_name=f"category_{self.category_id}")
scanned_category: str = scan_image(category_image_bytes.getvalue()).strip().replace('and\nEternity',
'and Eternity')
scanned_category = self.fix_title_by_database(scanned_category)
self.logger.info(f"Found category {self.category_id}: {scanned_category}")
if scanned_category in self.categories or skip:
return scanned_category
self.categories.append(scanned_category)
last_achievement = None
skip_scroll = True
scanned = []
while not end_of_list_mode:
if not skip_scroll:
self.logger.info(f"Scrolling...")
self.scroll_mouse(self.adjust_scroll_steps(), self.buttons['achievement_scroll'])
sleep(0.5)
for i in range(0, 5): # scan start-of-page items
self.achievement_id += 1
skip_scroll = False
if self.category_id <= 2:
self.logger.info('Selected normal achievement boxes')
achievement_name_rect = self.boxes[f"start_achievement_{i}"]
status_rect = self.boxes[f"start_achievement_{i}_status"]
else:
self.logger.info('Selected namecard achievement boxes')
achievement_name_rect = self.boxes[f"start_achievement_category_{i}"]
status_rect = self.boxes[f"start_achievement_category_{i}_status"]
title, completed = self.scan_achievement(achievement_name_rect, status_rect)
# In-case we are stuck (end-of-page)
if last_achievement == title or title in scanned:
end_of_list_mode = True
break
else:
last_achievement = title
scanned.append(title)
if completed:
self.achievements[title] = completed
for i in range(0, 5): # scan end-of-page items
self.achievement_id += 1
achievement_name_rect = self.boxes[f"end_achievement_{i}"]
status_rect = self.boxes[f"end_achievement_{i}_status"]
title, completed = self.scan_achievement(achievement_name_rect, status_rect)
if completed:
self.achievements[title] = completed
if title in scanned: # leave faster whenever possible (caught on Challenger IV)
break
return scanned_category
def scan_categories(self):
skip_data = False # debug switch
last_category = None
while True:
self.category_id += 1
if self.category_id != 1:
self.logger.info(f"Scrolling to category {self.category_id}")
self.left_click(coords=self.buttons['category_scroll'])
sleep(0.5)
self.scroll_mouse(self.adjust_scroll_steps(category=True), self.buttons['category_scroll'])
sleep(0.5)
self.logger.info(f"Clicking on category {self.category_id}")
self.left_click(coords=self.buttons['achievement_category'])
sleep(0.5)
self.logger.info(f"Scanning category {self.category_id}")
category_name = self.scan_category(self.boxes['achievement_category'], skip=skip_data)
if category_name == last_category:
break
last_category = category_name
sleep(1)
for i in range(0, 7):
self.category_id += 1
self.logger.info(f"Scanning category (end-of-page) {self.category_id}")
category_box: RECT = self.boxes[f"end_category_{i}"]
self.left_click(coords=self.get_center_of_rect(category_box))
sleep(0.5)
category_name = self.scan_category(category_box, skip=skip_data)
if category_name is None:
break
return
@classmethod
def run(cls):
app = Application().connect(process=find_process("GenshinImpact.exe").pid)
main_window: DialogWrapper = app.windows()[0]
main_window.set_focus()
inst = cls(main_window)
inst.go_to_achievements()
inst.scan_categories()
with open('results\\achievements.json', 'w') as file:
json.dump(inst.achievements, file, indent=4)
return inst
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
def check_if_tesseract_is_available():
default_tesseract_path = "C:\\Program Files\\Tesseract-OCR\\tesseract.exe"
import os
if not os.path.exists(default_tesseract_path):
print("Tesseract не установлен. Пожалуйста, установите его из интернета.")
print("Tesseract is not installed. Please, install it from the web.")
print("https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-w64-setup-5.3.3.20231005.exe")
return False
return True
if __name__ == '__main__':
if is_admin():
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.getLogger('PIL').setLevel(logging.WARNING)
# logging.getLogger('PIL.PngImagePlugin').setLevel(logging.WARNING)
if not check_if_tesseract_is_available():
input("Press \"Enter\" to exit ")
sys.exit(1)
# input("Press \"Enter\" to start ")
try:
AchievementScanner.run()
except Exception as exc:
logging.exception(exc)
input("Press \"Enter\" to exit ")
else:
# Re-run the program with admin rights
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)

37
main.spec Normal file
View File

@ -0,0 +1,37 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[('C:\\Users\\Levent\\Stuff\\Projects\\GI_AchievementParser\\client\\assets\\', './assets/')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='GI_AchievementParser',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

56
submit_to_gc.py Normal file
View File

@ -0,0 +1,56 @@
import json
import logging
from time import sleep
import httpx
from tqdm import tqdm
from utils import get_asset_path
def submit_ids(ids_to_submit: list[int], cookies: str):
cookies_as_dict = {cookie.split('=', 1)[0]: cookie.split('=', 1)[1] for cookie in cookies.split(';')}
with httpx.Client(http2=True, cookies=cookies_as_dict) as client:
for id_to_submit in tqdm(ids_to_submit):
# print(f'Загружаем achievementId {id_to_submit}')
result = client.post('https://genshin-center.com/api/achievements/update',
json={'achievementId': id_to_submit, 'done': True})
result.raise_for_status()
sleep(0.1)
def main():
cookies = input('Введите свои куки из genshin-center.com и нажмите "Enter": ')
assets = get_asset_path()
with open("results\\achievements.json", "r", encoding='utf-8') as file:
completed_achievements = json.load(file)
with open(assets['gc_achievements.json'], "r", encoding='utf-8') as file:
gc_achievements = json.load(file)
# gc_map = {v['name']: k for k, v in gc_achievements.items()}
gc_map = {}
for k, v in gc_achievements.items():
ach_name = v['name']
if gc_map.get(ach_name):
gc_map[ach_name].append(int(k))
else:
gc_map[ach_name] = [int(k)]
ids_to_submit = []
for k in completed_achievements.keys():
gc_ids = gc_map.get(k)
if not gc_ids:
print(f'Пропускаем {k} (нет в базе)')
continue
if gc_achievements.get(str(gc_ids[0]), {'category_id': 123})['category_id'] == 0:
ids_to_submit += gc_ids
submit_ids(ids_to_submit, cookies)
if __name__ == "__main__":
logging.basicConfig(level=logging.WARN, format='%(asctime)s %(levelname)s %(message)s')
try:
main()
except Exception as exc:
logging.exception(exc)
input('Нажмите "Enter" для выхода.')

37
submit_to_gc.spec Normal file
View File

@ -0,0 +1,37 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['submit_to_gc.py'],
pathex=[],
binaries=[],
datas=[('C:\\Users\\Levent\\Stuff\\Projects\\GI_AchievementParser\\client\\assets\\', './assets/')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='Загрузчик в Genshin Center',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

BIN
upx.exe Normal file

Binary file not shown.

104
utils.py Normal file
View File

@ -0,0 +1,104 @@
import io
import os.path
import sys
import threading
import timeit
import psutil
import pytesseract
from PIL import Image, ImageChops
from pywinauto.win32structures import RECT
def find_process(name: str):
matching = [x for x in psutil.process_iter() if x.name() == name]
if len(matching) == 0:
return None
return matching[0]
def scale_coords_to_resolution(coords: tuple, resolution: tuple):
x, y = coords
base_x, base_y = x / 2560, y / 1440
return int(base_x * resolution[0]), int(base_y * resolution[1])
def scale_box_to_resolution(box: RECT, window_rect: RECT):
left, top, right, bottom = int(box.left), int(box.top), int(box.right), int(box.bottom)
base_left, base_top, base_right, base_bottom = left / 2560, top / 1440, right / 2560, bottom / 1440
low_res_left = int(base_left * window_rect.width()) + int(window_rect.left)
low_res_top = int(base_top * window_rect.height()) + int(window_rect.top)
low_res_right = int(base_right * window_rect.width()) + low_res_left
low_res_bottom = int(base_bottom * window_rect.height()) + low_res_top
return RECT(low_res_left, low_res_top, low_res_right, low_res_bottom)
def bold_color_mask(image: Image.Image, target_color=(85, 85, 85), threshold=50):
# Create a mask for the gradient effect
start_mask = timeit.default_timer()
mask = Image.new("L", image.size)
for x in range(image.width):
for y in range(image.height):
pixel = image.getpixel((x, y))
color_difference = sum((a - b) ** 2 for a, b in zip(pixel, target_color))
if color_difference <= threshold ** 2:
mask.putpixel((x, y), 0)
else:
mask.putpixel((x, y), 255)
end_mask = timeit.default_timer()
# Apply the mask to the original image
composited = ImageChops.composite(Image.new("RGB", image.size, (255, 255, 255)), image, mask)
end_composite = timeit.default_timer()
# print(f"Mask: {end_mask - start_mask}, Composite: {end_composite - end_mask}")
return composited
def generate_achievement_boxes(achievement: RECT, status: RECT | None, height_adjust: int,
key: str = "end_achievement", count: int = 6, inversed: bool = False) -> dict:
box_coords = {}
for adjust_count in range(0, count):
generated_achievement_y = int(achievement.top) - (adjust_count * height_adjust)
if inversed:
generated_achievement_y = int(achievement.top) + (adjust_count * height_adjust)
box_coords[f"{key}_{adjust_count}"] = RECT(achievement.left, generated_achievement_y,
achievement.right,
achievement.bottom) # it's actually width and height
if status is not None:
generated_status_y = int(status.top) - (adjust_count * height_adjust)
if inversed:
generated_status_y = int(status.top) + (adjust_count * height_adjust)
box_coords[f"{key}_{adjust_count}_status"] = RECT(status.left, generated_status_y,
status.right, status.bottom)
return box_coords
def scan_image(image: str | bytes) -> str:
data = image
if isinstance(image, str):
with open(image, 'rb') as file:
data = file.read()
default_tesseract_path = "C:\\Program Files\\Tesseract-OCR\\tesseract.exe"
if not os.path.exists(default_tesseract_path):
raise Exception(f"Can't find tesseract at {default_tesseract_path}")
else:
pytesseract.pytesseract.tesseract_cmd = default_tesseract_path
fake_file = io.BytesIO(data)
return pytesseract.image_to_string(Image.open(fake_file), lang='eng')
def get_asset_path():
assets = {
'gc_achievements.json': 'assets\\gc_achievements.json',
'gc_categories.json': 'assets\\gc_categories.json',
}
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
return {k: os.path.join(sys._MEIPASS, v) for k, v in assets.items()}
return assets