From 241020f9a8e9068393737510328f5e1002615606 Mon Sep 17 00:00:00 2001 From: Hans-Christian Payer Date: Sun, 31 May 2026 09:16:04 +0200 Subject: [PATCH] auf dot_env umgestellt --- .env | 12 + .env.example | 11 + Pipfile | 1 + Pipfile.lock | 531 ++++++++++++++++++++++--------------------- README.md | 261 ++++++++++++--------- config.ini | 17 -- config.ini.example | 17 -- ics_importer.log | 6 + ics_mail_importer.py | 108 ++++----- 9 files changed, 502 insertions(+), 462 deletions(-) create mode 100644 .env create mode 100644 .env.example delete mode 100644 config.ini delete mode 100644 config.ini.example diff --git a/.env b/.env new file mode 100644 index 0000000..ef374e1 --- /dev/null +++ b/.env @@ -0,0 +1,12 @@ +IMAP_HOST=imap.mailbox.org +IMAP_PORT=993 +IMAP_USERNAME=minitux@mailbox.org +IMAP_PASSWORD=4711Cayenne64 +IMAP_FOLDER=INBOX +IMAP_UNSEEN_ONLY=true +IMAP_MARK_AS_READ=false + +CALDAV_URL=https://dav.mailbox.org/caldav/Y2FsOi8vMC8zMg +CALDAV_USERNAME=minitux@mailbox.org +CALDAV_PASSWORD=4711Cayenne64 + diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..fe1c0db --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +IMAP_HOST=imap.mailbox.org +IMAP_PORT=993 +IMAP_USERNAME=ihr-name@mailbox.org +IMAP_PASSWORD=IHR_PASSWORT_ODER_APP_PASSWORT +IMAP_FOLDER=INBOX +IMAP_UNSEEN_ONLY=true +IMAP_MARK_AS_READ=false + +CALDAV_URL=https://dav.mailbox.org/caldav/IHR_KALENDER_ID +CALDAV_USERNAME=ihr-name@mailbox.org +CALDAV_PASSWORD=IHR_PASSWORT_ODER_APP_PASSWORT \ No newline at end of file diff --git a/Pipfile b/Pipfile index d0ab4fc..7157408 100644 --- a/Pipfile +++ b/Pipfile @@ -6,6 +6,7 @@ name = "pypi" [packages] caldav = "*" icalendar = "*" +python-dotenv = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 4e844a4..fafe8b5 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "9054d163ae11d3340046ee5035c05385631e1acc0c9d054a174ef809369b6c78" + "sha256": "3a457d8118e1aff25d23511f86d5f9e7939f91893ea824178ed850203ecbc74c" }, "pipfile-spec": 6, "requires": { @@ -18,12 +18,12 @@ "default": { "caldav": { "hashes": [ - "sha256:39361f460a96599b8e4efbbdd711382adb1bbe8fc278eac4650f23cb6155eca5", - "sha256:d4df2c73843162af2fe7d324149830de8903690bfa094244029481d9bba320d0" + "sha256:6869e505ad14741a582003c93e49be2689013d19c9b91679c7c385775b673408", + "sha256:b474900130226f22dbe3e3abed34559faaeed8a29c455af920c2afb0e355e3d3" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==3.2.0" + "version": "==3.2.1" }, "charset-normalizer": { "hashes": [ @@ -162,11 +162,11 @@ }, "click": { "hashes": [ - "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", - "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613" + "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", + "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96" ], "markers": "python_version >= '3.10'", - "version": "==8.3.3" + "version": "==8.4.1" }, "dnspython": { "hashes": [ @@ -186,272 +186,272 @@ }, "icalendar": { "hashes": [ - "sha256:10cd223c792fcc43bee4c3ebe3149d4cf32406c85cfef146624df5a0d414260f", - "sha256:6de875370d22fc4aff172ad7c439b39fb109dc2eab9ce358fcb95e8689ad7b56" + "sha256:01c76243c76c549f58bb51510a8f0a4edb7c539726adda1356dfd0dc04fb7a53", + "sha256:ebc43ebeb357be98984b573d975118008dee3410d8df28b054ef2943cf3e367e" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==7.1.0" + "version": "==7.1.2" }, "icalendar-searcher": { "hashes": [ - "sha256:66f6f5ece50041ceda5ea91995cd2ed80fa0b065a42b3b3f420f89343614b2a3", - "sha256:abd99bf1ac9c9d675d84151101db4883a97e9958755708804c55abd30df58f6c" + "sha256:2440d4d0d9fbe0021a6647916853744dbca179abd39d25c742ebb8442bbc1bc3", + "sha256:649aad558df211bc50ca3dea3646e1b2f3e93f0995bca2b6358e9c79031471b6" ], "markers": "python_version >= '3.10' and python_version < '4.0'", - "version": "==1.0.5" + "version": "==1.0.6" }, "jh2": { "hashes": [ - "sha256:05102a4610dde1dc59c630e64ca34a74076d1afd275dbeac954b230a605788b9", - "sha256:070adb3943f306257fff6dff4cbdcb5324afd78cbfa624f6686f198e6381d707", - "sha256:08392b71819ef4dec683010b0366b15da8ed495250110c6009833f25855ab6a4", - "sha256:148195763588b0b8003fc365783838fabdc8450346b7c8df0c7945b80f252fab", - "sha256:1517147850dd3bcf8e3204e7e4b4016e47440a889accfd6b055734dd2686bc89", - "sha256:176f4de35aef5f3eef38d6ae785bb530f911af1fc6a21512da620250cde95a94", - "sha256:18f10dcf0aa9f19833ac0f4d58b195af2d0b056423d428f74bf03f7839db8055", - "sha256:264b93edeb9368cd4ad8b8ba4a23e9404ba6a449ded00f6c1e62b259fabf43c8", - "sha256:27ab9efb8127d1dce11fb6f12cc4ab4339338cf66bb758734ee3e5e7937cf11a", - "sha256:2b7daf269b477e22beedc6767e471a72a3fc988538ad295b80dafceb289f3dc2", - "sha256:2d7e43c6248e3a091e9f6c5aac23236bd7ba0e30d240f4017b644bd3da049688", - "sha256:2f88407c7d2de429346e589ede8f0eaa594d6a8a3e388b658bbf4828998a53a0", - "sha256:31d5c24cc3c20b49ad00e25e2d429d51b240a7f7fc8910c48a1fa11cf84f0c71", - "sha256:31ddace327ac78b3137d79c4ce1a64fb8d5b1256a88078e7806b20280a22ed1c", - "sha256:34c0bbf4688917a3a1b1dba176bc49bb5b1ad4b75765431b989f7767061df432", - "sha256:3581858d586d0dc87bfb47c3446b67dbbbe5f26a0c4aab94bc0f88d9329a4101", - "sha256:37ecb9da2bbc590753593220a80b2c24d7e8411238bad2d1e331e5c2287bb3d1", - "sha256:3c3b06db73cde4e350e8acd5960e6bd9880e512cc8ab9c28003c74414261382b", - "sha256:3dea67e8ae492168e4271351dca8869e4bf79f3bb45d301d54f9639e8cb345ae", - "sha256:3ebfcd80cfcaa17bbb5733871953d1df79e1cc8bdc0f22d7372d9f2ef3524008", - "sha256:4aa8c32df2426f7a9d8633c2c8b5555edcace6e703640cb50f7ecb5732d9b50c", - "sha256:4dc82aee3ab2c4103f3d9092f4463dd6cc4a248ab6a27a4acab79bef0d3ac8dd", - "sha256:52f5c68a736f8f70e9b4f55e9678a5f12b203f244182d261d6ffe8b239ab08f0", - "sha256:5457b5bf89391cd21ab6cd051c2c1e2d78ee9507b2af473841ae5fafcc5bf048", - "sha256:550831792b78e547ad7510c62db98e4feb3c0df4002ac1fd9e9b0e30903bc159", - "sha256:558d4c15bc42419262ef15595d9c488ae53276b562397314e1cc934f4c7e4bdf", - "sha256:559c5ff8034ce36aa7c24c6acb80d4dc2a377ff552ff5c58be6d8762ed8ee048", - "sha256:57601fd1c5f6fce9e63ea1f2a61f83784478cf4d58e8491a7c18cc05abdb8e96", - "sha256:58f4c9da6555923f731e358d975e40d4ae6241c05b29e5a0f4dc8c91781cc229", - "sha256:5d6a1000872f99a2d50316bdab7dcb8a9eccebd1c7ca4ba2e656a74ee48015ac", - "sha256:60c7dc47084ff8d8fe98c7d8e4be6109d861045953a9f2036d791e6d72a7e1db", - "sha256:611c474b2c998fb09f5825dbe88626cc86c991a6d7dbc4c0d2a0848fa2fa437d", - "sha256:63190c909647ec4a5c8718ea8ef89230a1f33ec6588f93cb42555065f210ef87", - "sha256:66c61f837b3e5c897bd2a90149afd615f59d24c72c893526485bca1b40f6ec49", - "sha256:6ba33ff1d1275586bb4d83687c59783dad60b66ef3d420c04982bae7e0d75f9b", - "sha256:6c835b0b38d795dde7aaa4581626490ca5fcfbd4eefe9572ac18d9eb2427d215", - "sha256:6cd51dd02943b703e10eb536722c5fd205b6084333dac5b9c114bdbbc2c46b3a", - "sha256:6d15672b32f0891940691bac16a854af164e694f0b9d21bebfddd13e3c7d2f03", - "sha256:6dc37f78187655cf95032bee0e9578ff89d39734def8e27a6bf930d4caa08042", - "sha256:6df0c5f2a879ea47ec2f88f4abfb0ca14c2af174eda923b7f65e9954a4aecf38", - "sha256:73db6c7374aebb94e2758e1c34c0090b1dee39f13a28b812387c8e9478cdbee1", - "sha256:740e4e489759b749aaed695e8430d28a039c11765fc5e4d1b20bfad9c7e192f1", - "sha256:7a1388738fcce0ddc8e742d2d1c0619911299f339d54a19496bcbecfb4d7e775", - "sha256:7a78e0c94d242aa054f97e503fb05485df63b3d8e66ff5ec8710fc0dcd1fb83b", - "sha256:7c19511733a8ccf998042b64ac2077c334d73f2d0df4ce80b158694191a1f707", - "sha256:842f27673350dc22659cc0dfba035bf610927810fcfb6a9ddea594dcb3cfe774", - "sha256:84c32c68792170bbb19bc94ec5c5d8823ba31d177709d686c63cd6a9e8ef5cd3", - "sha256:84e4ef15d432f914394828f5127482584bd8bb6ca9d95220b25bc32a5bdec501", - "sha256:85cf4f09f7159c29967212af685d2819f960d9136d931420fef107683d121f56", - "sha256:86d1bd875161ce4d5303e667ad19fb7436476d1610aa04b21c14838c1669f32a", - "sha256:89c46416ccf0f457bfd4df67670c79052116f07ffb3951c5103d178c6bf372ec", - "sha256:8afe44228388f9282b4e3804e0212fc7f000ede156e73b2068f61fb821598c9f", - "sha256:8bf2a83fcbcd3dd53b80574655a54459e7ddd591d936ac67e636330764c75907", - "sha256:8d4c26ef61db31c6f33a572b40c6eff312131cac83300bbf6d75fbed1e5f073e", - "sha256:8e9f37f6497f8dbb1c1e254c77224ad06cfde22c1337230d308aeaab043eea27", - "sha256:92ff21001d59d47f929418d0dae55a97be16221c13e1f7ed134bdc79189475fb", - "sha256:963d7c46d8fc824eb5957086702e65ca552de4595a95c5c7906b41162e6459cc", - "sha256:97da940c5bc7f9ca1ddde294fac46f75ca4be2a7b7be5a32e87b6195f2f4203d", - "sha256:985a9eb136e7897bcedba873cf30b51c19481d94ab31a391d05eeecf27c390ba", - "sha256:9c39968cf5547d68f97a893c518f02f6caca94942206a958d8aa9325f8c3e330", - "sha256:a27e7e1ead8fb200a6d909b23031e3adf0dc2be0b2715fae61ca0af6b4a77de8", - "sha256:a3a66bd7612ab626a592d5c0d6b072bd48c0c1f6ab3ed1d1dbb427dbe7472ed2", - "sha256:a433f014c207ffc4b3eda0165fbd4d7d978b53cbdd6e71d441531221b2b1b879", - "sha256:a508df37d162ad523edd05477721e981ca33607ebbcd398eb000b0e7e9dca855", - "sha256:a970e377224e4f2e703e4ed72135a16c1539feb0d7f57f9e1dfa4514aa6fb257", - "sha256:aafd357af8d0de5267d3bc88e2384da30f05c38446a61425ae565925bc2ca9ba", - "sha256:af8d4f64794823fcdaa1ab4d01e40361e0dc0ddda9a6523e96a72b47a9e96e7f", - "sha256:b0008658e6308f71a3aabfbf1051cc71993fb9a993ccfa0cf711b52a1bc029dd", - "sha256:b0ad821964a7701e2b80c6f8b424b6d4ca575fefb1aa04227967ef78fa15fcd5", - "sha256:bf289d6fca3346ef133334bde88c08d4f1b76c9f3ef53cbc6be1e96bae3e3305", - "sha256:c0693e9efcb492f48b61453b6fca3cee60c544c494fa1eb7ab63dbe493189db4", - "sha256:c3c95d6c44d9d2d53bee5f5bbffdb2a1e6174c8c47803ac6ad7465e8d9e6e2d7", - "sha256:c4bc66dcfe87ef776aea114e45db658895e4c812bb044ad3a4d52abcb5d4083c", - "sha256:c5d4d20b96d643fd866d8d49b2fec2826eea99e7852c5aa0326d14e8ca517769", - "sha256:c7834d1000ac856234e7b574ed2ccf2136aab325d84051edb1db06c17e295df4", - "sha256:ca9473848bbf422b1c91c907bed9db66d7c29f37a6406c4614bd3fa782d0e2ab", - "sha256:cd19d6e0f8b82dd92e9e9836baf8c5d3a18d15d06c7838c64dae0f45b0cad24d", - "sha256:cd4187891ebc44e782c5606393e16818d63bc1dbd3a0028bafed62e2d0fdd3f2", - "sha256:cdb4bc4cb82e66a41d4f4ec9fb80f7ed7981cf7786efb6a94cf47ea27ec90e28", - "sha256:cf17baca8873260f6e80861feb8048c898b8a90569c374f430370240bd078995", - "sha256:cf85910f5d8506467e9a6fc9be3140f4ffe49e2baa973b71c83822c6e6e88480", - "sha256:d8115befc7092c4b5d4b32093d38bf4c8a543b84d9a7f7f632055854bd89cb63", - "sha256:d8d971f7e6c13c7e71b566683cdf8fa9569dfdca1443ee2545b3b8c68f3b6338", - "sha256:da8fbddf3e00a9e8e18afda67721e71cc69f1a81ba16a4a7b50f57efa47d2991", - "sha256:daadac34cefe67ea03a7d2324e03fc9b37ec8820604f1563e7d424471bee29b6", - "sha256:dbd90d3b9bba566c36b6613d9e867e69754058a1b8608d9b7c973fd3cd97ef69", - "sha256:dbe27e880218afc123c464359553771cedab793130eb3b0c72bd3f678512a2da", - "sha256:dbf08eead0483ecaf275c2f447b704d1583278f7abd6f0e945fccd6a581c7df4", - "sha256:dd0ac28b9c0e20c3b5e6840f6651a38303718f20c12df104c8e544ad245b2c24", - "sha256:dfb99fe1bd951d2da7d5dd90325c8a3c3834dd614339f536a45cbd1bc1335f1e", - "sha256:dfbb07be66cb96a289c876aaab7ac46da4fb70f6526298f1fda60076b971d5f0", - "sha256:e20f3bcf50192caea969b4bb674c8f6dc607fb5f8abe6b76248f698e9e4cab84", - "sha256:e28dabffcbd5525bf5f36d482764e3e56b513bce06a75b2fb4b540bedad80348", - "sha256:e5616ffeb5b173b540e2db230546a476c5618cf25dac5bb9149c06fc6b0a9e4f", - "sha256:e5c64aa5f61d986a4f1b4cc319056dcb7198c3c25e3a81b3302a396479e5561a", - "sha256:e6998c4e1bd862d58426ab654b99cf1f52163acff767714048711225aaf6fc3f", - "sha256:e6c5421e4eb59f9f15822b9002b26a78a8f9d4e507e4f79d6f3d5f992db4be0a", - "sha256:ebe5ec3b51704119ca66717828631a777bc64132517f445d0b9ac2f30dd38264", - "sha256:f035aa4cdf3193bc03e7e36b4154d9036f87d002ba553839ec73412242de62ef", - "sha256:f26cdbf79bd0792bc65b7825b356040c56c365041a6ae7c44e5655f8fa173fe6", - "sha256:f48bb00711eefaefbfafd791da8bd8796d683aef6a4c320b7a393555f6d23115", - "sha256:f639bbe255623af299f75b2ef8e98b0c88ead8b9f420d20abe487fb8a33238b6", - "sha256:f8e208e3bc3909d82c7ecb936bad35cc2271eb425e7faac352618c26a23568d0", - "sha256:fa18a2886a229a0d53a2c6c3d109079cdd2550466f2ae2286e43a1c66d47d627", - "sha256:fc22823c633e95c6b5298f9ffe2d77f0f1787f2d03c47ccb7dff006e6c30fac3", - "sha256:fc2bdcc2fecb3e3382bbf2834d81b981c3b8978be67c54c2aeeba01dee911161" + "sha256:020abf569e05cf807104b4a8b43ef26ab4684fd7d26441791a2b9f450a01c66d", + "sha256:03c1ba7cbebb2e3be1ea62b789d2caab907288ec5f11c97e190ea71bd640878d", + "sha256:05849bda038c2f8cee50559e3fd363af39c80fb1f24ecf947ccbc7e1a890683f", + "sha256:0ad4d68340e38a8474a48c424e2929d3e2e82e35e4e8ff1188decb1985d1b4d7", + "sha256:0ee6a5dd24a1dc0772a89b3498ed20f60167fbc633404b1e02895f04ae66c47c", + "sha256:194537a1f6d06322448f65b72726014f7a31a484325db5aa30795a670b47d7f9", + "sha256:1a75eed545420a31ea356e8046b8d86fcf694234dd1035191734a03720fb8a44", + "sha256:1a84ca3b2cf786a811a828d68d8f3b4587566f7e8236a2c42a77443157eb6053", + "sha256:1dd137086b13d55a53c4b7b8b75869c824060a4d35615e4f49180ae58c7783f5", + "sha256:1f3994201495621bac0d1aff8b5ca9a5f185b91b64638e026d9c94584fdd795f", + "sha256:1f4dffe1c6ed0d4b0d2d9a45d064622bb786cbf87db35eac469dd4c376ac6fb6", + "sha256:2110e32bde0765fce8a52ec4d51b5ebe1d679de862a1da34f67d887acbdf390d", + "sha256:2195557f5953ceee743d0eaab0b09ec537bbc1b9a2c6c1463106bfca9b03805f", + "sha256:267aaa7a96f54452e1e07c7701799bc7817e1f3bb2de09301b2c492841e4c98e", + "sha256:28cb7c8d9fa0cdb3d357776ad82312c6ced6481fc828f898a04419933c0532d5", + "sha256:296ee44dfad08ccc3857daf433dfa4a555d5b45f2df39503ccdd90ff10b32943", + "sha256:29c23f3937f257beb5cd5b17f8727404f1267ff25d26e54c0a35d5defd0c895e", + "sha256:2a37164eb8f2462268e6252db93b009cb685fa060b62ad217bf1b333b450c68d", + "sha256:2b311414989da48a155721fe47d0dc3e3b75360efd62d779aaff72d786be91cf", + "sha256:2cc4923ac6f57753e0cb4aef054a04f5d5c744b274dabf863f695fa3c2fd75f9", + "sha256:30996200b243c6dd8c176537b7b75a5d4cd910f2be6f7be9e3b12e03a39a0d9a", + "sha256:31fec2270a204669fd5183465e3ef6a666a0539af61bf8e995df91f16c945c0b", + "sha256:3ad0a4909501bcd8b063d8757a650228ece9ac7f4b3df0363108989ab71c124c", + "sha256:3e2789ac441df896333e19dce716987ab3bdbd31d15b885960e52fd8b1bb40f6", + "sha256:3e5e73bbfcf0690561a1e2bf8b2d8f1e0bfd1aca67ebbed2a753cf9440f912ef", + "sha256:3f54095cde010ff4e52f23dda92b31d3d3160c64f5f23fc6e181f8c7938afd33", + "sha256:3f74dad9036e599eed51576492b00955237b86cf886f96844708a81cd0f5c710", + "sha256:3f8c5e81254b986f8abe9fd974e7ed595aeaead3f3066ce9ed95dfff236ceca9", + "sha256:3ff9abf4b4769c9c8117d929f87c90415cb65f54faa22f64fbf044427127e65f", + "sha256:40d0a10036cdf8bf1a36b07c10b69ad0d08ce31d99b8962e3d055ee417fe3423", + "sha256:47d3203ed6fec39df4922764817979fad3a13d22cf750adc9cf9dc2b3399eacf", + "sha256:4812710810010c428db04e9290a13117f7b7c5a3a663b46a0586fe101cf25c9d", + "sha256:49531e0042b3a4d35d34ddb892bb734d77969c9b787e6f2f0253cb10b498d4e8", + "sha256:4a125855110bedee3011929e1d050c20d74fa00a36d1ec602be677847b89d764", + "sha256:4e734f8a5cf002316cc81b80d8a91a0f7c62d631270abd913746644fb9ae288d", + "sha256:4efed5de773df44042a07a50ea8dc476a4c120bfd3fcc4fd7a7ea145196832b2", + "sha256:50d224b482e1e18fa6bbe90d05125993122c5530b0b83f48829d7a535e2b60f2", + "sha256:521f871864880cabe361060f070516a56a54abc3e96f07bee9ba8b061eada891", + "sha256:53b1df29ffe966cde1d2f0085b6acfcd237504627d406a51717ef3849ec88831", + "sha256:553f6c0caa98f314d97c671c8924fb97cd59515258916283cea315bc099c4200", + "sha256:5909e2273f8b6fcea93c95564aa0f366ff9261a544a7a5d640ea95341321cc37", + "sha256:5917a20e0a7b763a8eb2786422d2a534fa0e5645baf12376045a64b63a711cb1", + "sha256:5d61bc0a62b4eee11039bf29d4bdbb26efcff760dfb9d8e0739740028aa52b0f", + "sha256:5f9a82f90a21660fd0313a81c838bff89a13c7921c317abfc3117e49f9d46c84", + "sha256:62aee4bd128e17871980e4847cc4b94b67651de612bc96dd3d7ebf647c68a6e4", + "sha256:641f65f5414b8990251bd7609513a696831558f69d2ca77d65dc3f7087f31267", + "sha256:6d84d87f6c1b2131273b8078f5863d980269fa53139d7f7fa76e6099e2a00bd1", + "sha256:6eaa9919f7faf162d2acf66e1632c4588840589eb0030688907c9eb6a5ecae17", + "sha256:6f8d364d53cc0854d910f2305726a8254acb527f13fa933dc213a591e21e7ffa", + "sha256:6f8dfd56b10c4d66f1c2338b4caeeec5c0d2cda0243e66e02aca68bb02f7ea78", + "sha256:6fefc5b57d9a345d90e283a7547661b3c5921480c14d039077d3181a7b6226de", + "sha256:70b5bd5cbfd806ceb6f57bb9bc287a073b1cb51029b25197eb9acd45cb7f1db4", + "sha256:72122b850d58c4ae0612c9b9bbabeddbb092b503eb39d87249da323ba1b074a8", + "sha256:7479e41d20278484aecee14ed2a52e83cc36f3033f176fc2d1c7b078628e8bc9", + "sha256:74cb2237406296f92a31ea762ad0daffcfb2be39e567e62595880948eb9e9e84", + "sha256:7586a835ed5e213593e35e0d6f90197dd76412fdcba62e05b15df72806af1e2b", + "sha256:75d0b2bffb2e260a0f3578a11a4999a713c94d1bd8de7cfe692c840e20ae83cf", + "sha256:797e8846a4c7a18a9b3abdd437ca2fa06d39223df7a6fdcae57adeb7cc3d7a95", + "sha256:7b7e2028a565bb04cdf73a91634722e3de40e1d8fed1adf8aa1ba8952f48ef07", + "sha256:84d42f63b1e7368194704159637e534280540369b35758a4daa712bf277e80ef", + "sha256:85d3d338284fbfbe173243c3cc62e510c6fa0d6d023e67da2292bd7578c734e3", + "sha256:882b13542d7bd20f79cb92fc31ecc9587037278d0a6b36c14b734f9ac2cb8890", + "sha256:89fd1e706f1870ea4a8a91c6b2434123e6d76d6ea1fac9bf7904cf71fca8b9cf", + "sha256:95d72124353a530ffa5a65f1f94a4396521f5472f0fde79b93dd66a3addaadff", + "sha256:96cff73359be465a4a70a2f15b72c384055a6be16c670f983633a9dc93cdb2e2", + "sha256:99aedd20daacb32f377152c9103d8d0271f32d1932bd39a5ed2cd2eef97f7e07", + "sha256:9e4d342cd71db3c8c3be74acfe26b38503f2f5948c902072319405c5b5bc5313", + "sha256:a0772ad66f346038fe05cb2928866ff1b21bcaa9ec3f14709fa1d419ed1dc287", + "sha256:a08ffa490443767a1783b372d39a9156da37a93a6c490e03a439aacf315f8a79", + "sha256:a1ef1d6c90389c366ade34bc6b100e823569c60bbb0b0f2cc3b308031090b252", + "sha256:a5146468a76bbe8f4035f23a78813c5c433c70c0ca44eb0ba5558627a9e644b8", + "sha256:a5d2ceb2e9d42ac236f0da7b2f9e8237818d32a10d892b06f30f7160ee4365ae", + "sha256:a8bfbc5f77e95f485bf046d4a19bb773866cdc9d1b21c27a194366a55d7fe361", + "sha256:a915e77adc126bd79867b7cfecc930021ef1a2599475dda058ae80537a7068ec", + "sha256:b1d8313b13acfdef19c5ff793fdf397bae3d8d75181f6eee62604f13e4ec20a8", + "sha256:b41867d5decd7cd62e12cdacfc45c5d8f6eae872578ae01d78900174d5e47b82", + "sha256:b4b6aeaa12ff46df7478150e063f540fa976c41203739e425bfb823d479d7209", + "sha256:b4eae2d5bc1a91033450fd4381c04f33db7d4c9cfd31046c4708c8c1323c06d0", + "sha256:b572daaafbd867bb5c5f324c918704ddd6d76af1cc7c2be483c3649b76ce7940", + "sha256:ba5e545b3209e8aa5b6af88e7032814473e57d60d32d3fc6b6185453733a4b26", + "sha256:ba74adbc182ee8d4781856ec3cd1ed61fa16839f6a6b2bc708e70b281211938b", + "sha256:bc238641d3bc35dc79965f330c7f0f16ca992cf999c8892dce0bd36c32056eee", + "sha256:bde7e7ff0f17e87aca9ef23f66b80540a2de0ff8e2c26af55eb2e288a2a5f74f", + "sha256:bdeb48657db508e8bc299744aa431798efdf090feb3959cf0123167c9251ebb0", + "sha256:c32befa8f915f3a4510cd097cd7874b9d8782accb06562d8aecd5b40e879e980", + "sha256:c697b250a935030a128b45b41f507a4d33a082d8b8e12311c7e73fbb3970f057", + "sha256:cc31fbfd9ac25d46603c4d27c4ee94593202d55a437f0c1e83a17570f596e21e", + "sha256:cf3dc6f7c14340241260bf63e5d6006b2ee7db45dafcb39c16fdf164ea0240a0", + "sha256:cfec229e33d3772ee71d81bee9145f195f58cde65126f54820026a41c0da1ab8", + "sha256:d0bdb220d7fa3b6deb5a108cd5c37109e4bec12b7aa9aa56f2d8f9c55f1f113e", + "sha256:d3cf203de31371289d4c1e6b4d30c9f6de95b2bd2b300fe5468ff946299d0e48", + "sha256:d5c4d4ef923464dc5b39d39b1a41209a23e387698a254e81813d0757f76ff00a", + "sha256:d8313cfecb3b15d1c1a7e5c70c0e2d42bbc9fe0d616a18cc25dc91d63290139c", + "sha256:da030729eb8ef386569e78c10e2429adb10026ce82989fdeb906b037314d66c7", + "sha256:e09fa8ede2105af24955c620675d5d778b26c51086379ef7ee98e1208c2341e4", + "sha256:f3768fa39d0c5a2578b285e49e8d42230bb182bb2f7984840549f89c04ce4a56", + "sha256:f48eeba6c0bb91321dbefc60527d945f20ccee3f0066bb3ba0d9b71120ec9417", + "sha256:f558408add7be748978c52796ea852dde5d348a00f8ba7518db848d32154f022", + "sha256:f681e197bf6552df3bb7a3abfd676e81316b69e36e7263e72383477174032ca2", + "sha256:f6e95bfee4a72a7eb6fb4ca057ac5da297df68d302c63c300a5f400a0003fe31", + "sha256:f810dd02b4a9647eacbe4eab617111c0e3ffc68e923e3ff172948db601d2ba1e", + "sha256:f8c78cffb3a35c4410513c3eb7989de36028c84277c04f07c97909dd94c23a75", + "sha256:fb8e37ecf37382c02af83da521df5a77561441bc4cbb93b00627e015b7bbf349", + "sha256:fbd4e45d9207aed97257157f0ef268757f3687af95d7047a9e290a559f3729ea", + "sha256:fd7f6c7e63717cc5f69bf814b61d4c5725c1a8976b03567c87db6a048d7e16df", + "sha256:feb3a70be5b9af031596b8328532997e0019d91e11db1cd607087ed32ce59727" ], "markers": "python_version >= '3.7'", - "version": "==5.0.11" + "version": "==5.0.13" }, "lxml": { "hashes": [ - "sha256:00750d63ef0031a05331b9223463b1c7c02b9004cef2346a5b2877f0f9494dd2", - "sha256:022981127642fe19866d2907d76241bb07ed21749601f727d5d5dd1ce5d1b773", - "sha256:045e387d1f4f42a418380930fa3f45c73c9b392faf67e495e58902e68e8f44a7", - "sha256:05b9b8787e35bec69e68daf4952b2e6dfcfb0db7ecf1a06f8cdfbbac4eb71aad", - "sha256:07f98f5496f96bf724b1e3c933c107f0cbf2745db18c03d2e13a291c3afd2635", - "sha256:08950a23f296b3f83521577274e3d3b0f3d739bf2e68d01a752e4288bc50d286", - "sha256:0d082495c5fcf426e425a6e28daaba1fcb6d8f854a4ff01effb1f1f381203eb9", - "sha256:0f0f08beb0182e3e9a86fae124b3c47a7b41b7b69b225e1377db983802404e54", - "sha256:1081dd10bc6fa437db2500e13993abf7cc30716d0a2f40e65abb935f02ec559c", - "sha256:11a873c77a181b4fef9c2e357d08ed399542c2af1390101da66720a19c7c9618", - "sha256:183bfb45a493081943be7ea2b5adfc2b611e1cf377cefa8b8a8be404f45ef9a7", - "sha256:19f4164243fc206d12ed3d866e80e74f5bc3627966520da1a5f97e42c32a3f39", - "sha256:1ae225f66e5938f4fa29d37e009a3bb3b13032ac57eb4eb42afa44f6e4054e69", - "sha256:1bc4cc83fb7f66ffb16f74d6dd0162e144333fc36ebcce32246f80c8735b2551", - "sha256:1dd6a1c3ad4cb674f44525d9957f3e9c209bb6dd9213245195167a281fcc2bdc", - "sha256:20cf4d0651987c906a2f5cba4e3a8d6ba4bfdf973cfe2a96c0d6053888ea2ecd", - "sha256:2173a7bffe97667bbf0767f8a99e587740a8c56fdf3befac4b09cb29a80276fd", - "sha256:21c3302068f50d1e8728c67c87ba92aa87043abee517aa2576cca1855326b405", - "sha256:23a5dc68e08ed13331d61815c08f260f46b4a60fdd1640bbeb82cf89a9d90289", - "sha256:23cad0cc86046d4222f7f418910e46b89971c5a45d3c8abfad0f64b7b05e4a9b", - "sha256:2593a0a6621545b9095b71ad74ed4226eba438a7d9fc3712a99bdb15508cf93a", - "sha256:264c605ab9c0e4aa1a679636f4582c4d3313700009fac3ec9c3412ed0d8f3e1d", - "sha256:26c5272c6a4bf4cf32d3f5a7890c942b0e04438691157d341616d02cca74d4bd", - "sha256:26dd9f57ee3bd41e7d35b4c98a2ffd89ed11591649f421f0ec19f67d50ec67ac", - "sha256:28902146ffbe5222df411c5d19e5352490122e14447e98cd118907ee3fd6ee62", - "sha256:29f5c00cb7d752bce2c70ebd2d31b0a42f9499ffdd3ecb2f31a5b73ee43031ad", - "sha256:30e7b2ed63b6c8e97cca8af048589a788ab5c9c905f36d9cf1c2bb549f450d2f", - "sha256:32662519149fd7a9db354175aa5e417d83485a8039b8aaa62f873ceee7ea4cad", - "sha256:363e47283bde87051b821826e71dde47f107e08614e1aa312ba0c5711e77738c", - "sha256:3648f20d25102a22b6061c688beb3a805099ea4beb0a01ce62975d926944d292", - "sha256:37448bf9c7d7adfc5254763901e2bbd6bb876228dfc1fc7f66e58c06368a7544", - "sha256:37fabd1452852636cf38ecdcc9dd5ca4bba7a35d6c53fa09725deeb894a87491", - "sha256:398443df51c538bd578529aa7e5f7afc6c292644174b47961f3bf87fe5741120", - "sha256:3ae5d8d5427f3cc317e7950f2da7ad276df0cfa37b8de2f5658959e618ea8512", - "sha256:3f00972f84450204cd5d93a5395965e348956aaceaadec693a22ec743f8ae3eb", - "sha256:40d9189f80075f2e1f88db21ef815a2b17b28adf8e50aaf5c789bfe737027f32", - "sha256:419c58fc92cc3a2c3fa5f78c63dbf5da70c1fa9c1b25f25727ecee89a96c7de2", - "sha256:41dcc4c7b10484257cbd6c37b83ddb26df2b0e5aff5ac00d095689015af868ec", - "sha256:43e4d297f11080ec9d64a4b1ad7ac02b4484c9f0e2179d9c4ef78e886e747b88", - "sha256:45e9dfbd1b661eb64ba0d4dbe762bd210c42d86dd1e5bd2bdf89d634231beb43", - "sha256:4642e04449a1e164b5ff71ffd901ddb772dfabf5c9adf1b7be5dffe1212bc037", - "sha256:468479e52ecf3ec23799c863336d02c05fc2f7ffd1a1424eeeb9a28d4eb69d13", - "sha256:47024feaae386a92a146af0d2aeed65229bf6fff738e6a11dda6b0015fb8fd03", - "sha256:481d6e2104285d9add34f41b42b247b76b61c5b5c26c303c2e9707bbf8bd9a64", - "sha256:4937460dc5df0cdd2f06a86c285c28afda06aefa3af949f9477d3e8df430c485", - "sha256:4a1503c56e4e2b38dc76f2f2da7bae69670c0f1933e27cfa34b2fa5876410b16", - "sha256:4b89b098105b8599dc57adac95d1813409ac476d3c948a498775d3d0c6124bfb", - "sha256:4bd1bdb8a9e0e2dd229de19b5f8aebac80e916921b4b2c6ef8a52bc131d0c1f9", - "sha256:4e2c54d6b47361d0f1d3bc8d4e082ad87201e56ccdcca4d3b9ee3644ff595ec8", - "sha256:52b0ac6903cf74ebf997eb8c682d2fbac7d1ab7e4c552413eec55868a9b73f39", - "sha256:546b66c0dd1bb8d9fa89d7123e5fa19a8aff3a1f2141eb22df96112afb17b842", - "sha256:56971379bc5ee8037c5a0f09fa88f66cdb7d37c3e38af3e45cf539f41131ac1f", - "sha256:5715e0e28736a070f3f34a7ccc09e2fdcba0e3060abbcf61a1a5718ff6d6b105", - "sha256:5cfa1a34df366d9dc0d5eaf420f4cf2bb1e1bebe1066d1c2fc28c179f8a4004c", - "sha256:5d27bbe326c6b539c64b42638b18bc6003a8d88f76213a97ac9ed4f885efeab7", - "sha256:6262b87f9e5c1e5fe501d6c153247289af42eb44ad7660b9b3de17baaf92d6f6", - "sha256:63aeafc26aac0be8aff14af7871249e87ea1319be92090bfd632ec68e03b16a5", - "sha256:690022c7fae793b0489aa68a658822cea83e0d5933781811cabbf5ea3bcfe73d", - "sha256:6fd8b1df8254ff4fd93fd31da1fc15770bde23ac045be9bb1f87425702f61cc9", - "sha256:73becf6d8c81d4c76b1014dbd3584cb26d904492dcf73ca85dc8bff08dcd6d2d", - "sha256:73d658216fc173cf2c939e90e07b941c5e12736b0bf6a99e7af95459cfe8eabb", - "sha256:75c4c7c619a744f972f4451bf5adf6d0fb00992a1ffc9fd78e13b0bc817cc99f", - "sha256:76b958b4ea3104483c20f74866d55aa056546e15ebe83dd7aecd63698f43b755", - "sha256:77b9f99b17cbf14026d1e618035077060fc7195dd940d025149f3e2e830fbfcb", - "sha256:7ba11752e346bd804ea312ec2eea2532dfa8b8d3261d81a32ef9e6ab16256280", - "sha256:7da13bb6fbadfafb474e0226a30570a3445cfd47c86296f2446dafbd77079ace", - "sha256:7e39ab3a28af7784e206d8606ec0e4bcad0190f63a492bca95e94e5a4aef7f6e", - "sha256:7f4a77d6f7edf9230cee3e1f7f6764722a41604ee5681844f18db9a81ea0ec33", - "sha256:80410c3a7e3c617af04de17caa9f9f20adaa817093293d69eae7d7d0522836f5", - "sha256:81ff55c70b67d19d52b6fd118a114c0a4c97d799cd3089ff9bd9e2ff4b414ee2", - "sha256:857efde87d365706590847b916baff69c0bc9252dc5af030e378c9800c0b10e3", - "sha256:89e8d73d09ac696a5ba42ec69787913d53284f12092f651506779314f10ba585", - "sha256:8c11b984b5ce6add4dccc7144c7be5d364d298f15b0c6a57da1991baedc750ce", - "sha256:8c8984e1d8c4b3949e419158fda14d921ff703a9ed8a47236c6eb7a2b6cb4946", - "sha256:8e369cbd690e788c8d15e56222d91a09c6a417f49cbc543040cba0fe2e25a79e", - "sha256:9147d8e386ec3b82c3b15d88927f734f565b0aaadef7def562b853adca45784a", - "sha256:920354904d1cb86577d4b3cfe2830c2dbe81d6f4449e57ada428f1609b5985f7", - "sha256:942454ff253da14218f972b23dc72fa4edf6c943f37edd19cd697618b626fac5", - "sha256:972a6451204798675407beaad97b868d0c733d9a74dafefc63120b81b8c2de28", - "sha256:976a6b39b1b13e8c354ad8d3f261f3a4ac6609518af91bdb5094760a08f132c4", - "sha256:97faa0860e13b05b15a51fb4986421ef7a30f0b3334061c416e0981e9450ca4c", - "sha256:9c03e048b6ce8e77b09c734e931584894ecd58d08296804ca2d0b184c933ce50", - "sha256:9e7b0a4ca6dcc007a4cef00a761bba2dea959de4bd2df98f926b33c92ca5dfb9", - "sha256:9eb667bf50856c4a58145f8ca2d5e5be160191e79eb9e30855a476191b3c3495", - "sha256:9f93d5b8b07f73e8c77e3c6556a3db269918390c804b5e5fcdd4858232cc8f16", - "sha256:a0092f2b107b69601adf562a57c956fbb596e05e3e6651cabd3054113b007e45", - "sha256:a02ca8fe48815bddcfca3248efe54451abb9dbf2f7d1c5744c8aa4142d476919", - "sha256:a1d9b99e5b2597e4f5aed2484fef835256fa1b68a19e4265c97628ef4bf8bcf4", - "sha256:a2853c8b2170cc6cd54a6b4d50d2c1a8a7aeca201f23804b4898525c7a152cfc", - "sha256:a31286dbb5e74c8e9a5344465b77ab4c5bd511a253b355b5ca2fae7e579fafec", - "sha256:a86f06f059e22a0d574990ee2df24ede03f7f3c68c1336293eee9536c4c776cd", - "sha256:ab863fd37458fed6456525f297d21239d987800c46e67da5ef04fc6b3dd93ac8", - "sha256:ac4db068889f8772a4a698c5980ec302771bb545e10c4b095d4c8be26749616f", - "sha256:b6c2f225662bc5ad416bdd06f72ca301b31b39ce4261f0e0097017fc2891b940", - "sha256:bb40648d96157f9081886defe13eac99253e663be969ff938a9289eff6e47b72", - "sha256:bba078de0031c219e5dd06cf3e6bf8fb8e6e64a77819b358f53bb132e3e03366", - "sha256:bc783ee3147e60a25aa0445ea82b3e8aabb83b240f2b95d32cb75587ff781814", - "sha256:be10838781cb3be19251e276910cd508fe127e27c3242e50521521a0f3781690", - "sha256:bfd57d8008c4965709a919c3e9a98f76c2c7cb319086b3d26858250620023b13", - "sha256:c08da09dc003c9e8c70e06b53a11db6fb3b250c21c4236b03c7d7b443c318e7a", - "sha256:c3592631e652afa34999a088f98ba7dfc7d6aff0d535c410bea77a71743f3819", - "sha256:c4a699432846df86cc3de502ee85f445ebad748a1c6021d445f3e514d2cd4b1c", - "sha256:c4e425db0c5445ef0ad56b0eec54f89b88b2d884656e536a90b2f52aecb4ca86", - "sha256:c53fa3a5a52122d590e847a57ccf955557b9634a7f99ff5a35131321b0a85317", - "sha256:c6854e9cf99c84beb004eecd7d3a3868ef1109bf2b1df92d7bc11e96a36c2180", - "sha256:c748ebcb6877de89f48ab90ca96642ac458fff5dec291a2b9337cd4d0934e383", - "sha256:c871299c595ee004d186f61840f0bfc4941aa3f17c8ba4a565ead7e4f4f820ee", - "sha256:cbd7b79cdcb4986ad78a2662625882747f09db5e4cd7b2ae178a88c9c51b3dfe", - "sha256:cc16682cc987a3da00aa56a3aa3075b08edb10d9b1e476938cfdbee8f3b67181", - "sha256:cec05be8c876f92a5aa07b01d60bbb4d11cfbdd654cad0561c0d7b5c043a61b9", - "sha256:d036ee7b99d5148072ac7c9b847193decdfeac633db350363f7bce4fff108f0e", - "sha256:d0d799ff958655781296ec870d5e2448e75150da2b3d07f13ff5b0c2c35beefd", - "sha256:d1392c569c032f78a11a25d1de1c43fff13294c793b39e19d84fade3045cbbc3", - "sha256:d2f17a16cd8751e8eb233a7e41aecdf8e511712e00088bf9be455f604cd0d28d", - "sha256:d3829a6e6fd550a219564912d4002c537f65da4c6ae4e093cc34462f4fa027ad", - "sha256:d43aa26dcda363f21e79afa0668f5029ed7394b3bb8c92a6927a3d34e8b610ea", - "sha256:d6d8efe71429635f0559579092bb5e60560d7b9115ee38c4adbea35632e7fa24", - "sha256:dabecc48db5f42ba348d1f5d5afdc54c6c4cc758e676926c7cd327045749517d", - "sha256:db88156fcf544cdbf0d95588051515cfdfd4c876fc66444eb98bceb5d6db76de", - "sha256:de550d129f18d8ab819651ffe4f38b1b713c7e116707de3c0c6400d0ef34fbc1", - "sha256:e0af85773850417d994d019741239b901b22c6680206f46a34766926e466141d", - "sha256:e3c4f84b24a1fcba435157d111c4b755099c6ff00a3daee1ad281817de75ed11", - "sha256:e3dd5fe19c9e0ac818a9c7f132a5e43c1339ec1cbbfecb1a938bd3a47875b7c9", - "sha256:e69aa6805905807186eb00e66c6d97a935c928275182eb02ee40ba00da9623b2", - "sha256:e80807d72f96b96ad5588cb85c75616e4f2795a7737d4630784c51497beb7776", - "sha256:ebe33f4ec1b2de38ceb225a1749a2965855bffeef435ba93cd2d5d540783bf2f", - "sha256:f0cea5b1d3e6e77d71bd2b9972eb2446221a69dc52bb0b9c3c6f6e5700592d93", - "sha256:f15401d8d3dbf239e23c818afc10c7207f7b95f9a307e092122b6f86dd43209a", - "sha256:f504d861d9f2a8f94020130adac88d66de93841707a23a86244263d1e54682f5", - "sha256:fc46da94826188ed45cb53bd8e3fc076ae22675aea2087843d4735627f867c6d", - "sha256:fc7140d7a7386e6b545d41b7358f4d02b656d4053f5fa6859f92f4b9c2572c4d", - "sha256:fcf3da95e93349e0647d48d4b36a12783105bcc74cb0c416952f9988410846a3", - "sha256:fe022f20bc4569ec66b63b3fb275a3d628d9d32da6326b2982584104db6d3086", - "sha256:ffb34ea45a82dd637c2c97ae1bbb920850c1e59bcae79ce1c15af531d83e7215" + "sha256:05a82eb6e1530a64f26225b55cbd178113bd0b5af1c2b625f25e5296742c26d2", + "sha256:07a4a68e286ee7a1ed7dfb8af83e615757c0ccfe9f18c6b4ea6771388d9ba8c9", + "sha256:09dd5b7075dc2f7709654a46543ba1ea3c2e217b2ed8fbd413a8a945a0f40f60", + "sha256:0b7e8a14c8634bf6f7a568634cb395305a6d964aeb5b7ee32248094bed3a7e2c", + "sha256:104c09bda8d2a562824c0e319d0768ce26a779b7601e0931d33b09b53c392ef7", + "sha256:126c93f7f56f0eda92f6d8c619edc463a4f23d9252f1c9d0405a76f25fa9f11a", + "sha256:162af1091cd785f2f27e62d3547ae9bc58ec5c86dd314d67021fd02463708d83", + "sha256:17e0e18d4ad8adbd0399291bc44845b69d9dd68439a3cdebdf35ff902ec05072", + "sha256:18b73c339ae29b90fd2d06e58ebd555a751bde9cd6bbd36cc0281b9a2c94e9d8", + "sha256:19607c6bbff2a44cf3fe8250abccd20942d3462473e0a721d01d379ed017e462", + "sha256:19b7ab10b210b0b3ad7985d9ac4eb66ab09a90b20fe6e2f7ba55d01a234345d0", + "sha256:1d4962d4c66bf830a7e59ed6cfc17d148149898a3aefa8ec6e59763e6e3ed085", + "sha256:1db753c9115ec7100d073b744d17e25e88a8f90f5c39b2f5dd878149af59671f", + "sha256:1dde6131244bba38a17c745836ba190bc753fd73c9291666287fd0a3fa3dcf30", + "sha256:25c6997a9a534e016695a0ba06b2f07945de682731ff01065b6d5a4474179da1", + "sha256:26e6eda8d38c1fcab1090dd196ee87cbd13788e531937610e2589085de074e77", + "sha256:27acc820660aaffa4f7c087f29120e12980f7779d56d8492d263170111284740", + "sha256:2a0217714657e023ef4293500f65aa20fce6164c8fd6b08fa5bd4a859fb14b9b", + "sha256:2c8daa471358dc2d6fcf02165e80ec68f77871a286df95bc5cc3816153b0fd2c", + "sha256:30a89d3ac8faec007453fb541f3f46807eeec88edd5826f6e3fe001752a2c621", + "sha256:31033dc34636ea6b7d5cc11b1ddbda78a14de858ba9d3e1ed4b69a3085bc521e", + "sha256:32ab449a5486f6c758e849bb86710d0e45edc24a04e250c01555f8f5653958f8", + "sha256:3483644525531e1d5762b0c44a8e18b6efba321b6dcf8a8952de10b037618bca", + "sha256:34c2d737beabfe35baada43941ed519251e9a12e779031496bcd5d539fcfd730", + "sha256:3779def59032b81e44a5f70096ef6bf2082f8d901937dca354474ba09782e245", + "sha256:37a58976370f36d9329d118ad0b953c5aeb9119ac9c6a4e258942a225d0573a1", + "sha256:3893c14c4b6ac5b2d54ba8cf03e99fe5104e592de491f19bd6b82756c09f8004", + "sha256:3a12689be69a28ddaa0ab99a5a1137da2afd5f8f16df7b5680b66f616d3eda1d", + "sha256:3ab541146f1f6968c462d6c2ac495148e8cdba2f8347700b2141b6ec5a75bf52", + "sha256:3abf332af33a74288675d936fe861fd4344da0dd6622193fbc4f2bfbb35536b5", + "sha256:3fd9728a2735fda14f4e8235830c86b539e9661e849665bf926d3f867943b4bf", + "sha256:424aa57aca0897eb922aef34395bd1289b3b6f04e6bae20ea123c0c7e333cffc", + "sha256:441dd227fa0690eb9fc81edabc63cdcefc212bba99b906dcf6e32cc1a9d3e533", + "sha256:469e3618338bd7ab5beb412d2439825479fcf0dab99e394ca563dbc4eaf6c834", + "sha256:47402e62c52ff5988c1e8c6c63177f5708bccf48e366dea4e3dcf1e645e04947", + "sha256:4f0dd2f01f9f8a89f565d000e03abcf0a13d692a346c8d22f628d49af098777a", + "sha256:53b7d2b7a10b1c35c0a5e21e9224accf60c1bbfba523990732e521b2b73adef2", + "sha256:53c909b62a0532183542fed00c5a7218258c56292d409bc789886fe1cb04c438", + "sha256:54a7f95e4de5fb94e2f9f4b9055c6ba33bf3d628fd77a1d647c5923caa2cdcdc", + "sha256:556e94a63c9b04716f8e4de2abb65775061f846e89331b6c5be79183a24f98ea", + "sha256:55b03549819867ea141c0202242c4816c82e52ec36e7e648db9d8da5a3dc3ed6", + "sha256:581d4c8ae690a6609e64862dd6b7c2489635c2d13907fc2b20f2bc200ff1d21e", + "sha256:58bb955caba94e467d2a96da17660d2d704e0675894cba21ab8a775b8621fd1c", + "sha256:5b7328b46d49fc9477d91ae8f6d55340347d827b7734ba3ea33faae0efef1383", + "sha256:5ba186ad207446c65d3bb3d3e0412b032b1d9f595e59861e2354798c5703d955", + "sha256:5bec7d03d78d853597d6107854c2310ce3f761fd218fe9fe91d5101fcf6c2efe", + "sha256:5c6bf403fbb3b3e348a561a5f4f0b9961835657981c802a1df03653eef8a9074", + "sha256:5f6994074ebae6ffb04447268e37dc16edc304f9859cf91acb86e0af6c1b395c", + "sha256:62aeb7e85b5d60320b9d77eef2e773994e2c0ce10121b277e0a19804e1654a5a", + "sha256:63876be28efefa04a1df615b46770e82042cce445cfdce55160522f57b231ccb", + "sha256:639f6c857d91d9be29bd7502348d6736dab168b54b5158cd899abf11684dc186", + "sha256:640f97d43d867bcb9c75b3af013b64850756b746cb6bce8ace83b70da3abba9d", + "sha256:649dda677cf3bd6ac9ae14007ba0c824ded8ce5808b53fc7431d9140399118c1", + "sha256:6540377fbd53fe1b629172288c464fb18db11ce1fa7dc15891da10aa9dcc3e7f", + "sha256:6689e828a94eee4f139408c337bb198e014724bb8a8c26d3cfac49d119ed69a6", + "sha256:68a9198d0fc122d14bb76837de9aa80cf84caed990b5b237f532ed87d3706736", + "sha256:6b1761fbf9ec984e2e9d9c589ef5f5fd684b7c19f92aadd567a26c5224958db6", + "sha256:70cdfd80589d59e43e18005dd7244e8895e93db8ab6a620b7e23df5445a4e3d2", + "sha256:70ef8a7e102a1508f8121aae5b0867abd663f72c14f0a9c937e6554cb4587b7b", + "sha256:73bc2086f141224ebddb7fc5c6a36ca58b31b94b561e1dfe8e073e3270fad1e7", + "sha256:74a9717fd0d82effef5c2854f0d917231d5324b5a3eb7275c43ac9fa32f97a14", + "sha256:752d3bbfe874715ccd0aec7f88d7fc623c0f1fd7aa7b3238a084e017bad2a009", + "sha256:762ff394d5bd56da0cf034a23dcce4e13923f15321a2adfa2ac00201dc6d3fca", + "sha256:76447f65250ed2501ead1a1552f5ce8edff159a86f308348e6a9c4acb5e1f1b4", + "sha256:766b010012d59470072c1816b5b6c69f1d243e5db36ea5968e94accf430a4635", + "sha256:787b2496d0dbe8cd180984e8d29e3a6f76e7ea34db781cb3bd55e4ba1ef8b4ee", + "sha256:793033d6c5cdf33a573f910d9bea14ef8f5771820411d118da8e1182edb53d5e", + "sha256:7d47866cb32fb503450b6edc9df355d10dc49836af2e89901bd6ac6b0896d9d9", + "sha256:7f7a92e8583f06b1fd49d01158143b8461cfcd135dcb10ec807270a3051bd603", + "sha256:80c2dfadb855da477cf73373ad29a333535dedb9b12bad02c9814c8e2b43bf08", + "sha256:83b6b30eb131da7a75b601f28c5d6971e6ed3e887919bf6b6a1ad3c2df289080", + "sha256:86281fbdd6a8162756f8d603f37e3435bfa38043adb79c6dc6a2dfee065e7525", + "sha256:86c89b9d55ebf820ad7c90bc533410f0d098054f293351f10603c0c46ff598f5", + "sha256:876e1ff5930ed8bf295ec5ef9a8155e9b6b1876bbf1deed8b3a8069311875a8f", + "sha256:88136950da4d13c318bde414ce10219931937851327f44328f2df4d2c4614067", + "sha256:88d8cb75b9d82858497a5393e3c63cfbf03035225e4b35a49ed7ccb151e4dc0e", + "sha256:8be8ad51249698103d24b0571df35a10990fbe93dd043b6c024172189485f5e3", + "sha256:8d43ca737b20e106e4aebc42b2f3ae19f00ba63d7eb731698ee083d72d15646f", + "sha256:8dadbe5b217ff35b6a8d16610dd710219b59b76d13f0e3f0d9f36786206e4485", + "sha256:9395002973c827b3ed67db77e6ec09f092919a587022174554096a269378fb13", + "sha256:96f2ec43df44b1f76249ee0a615334f9b5b060e1c8bd90e706dad2d14d02f383", + "sha256:98fc784c2c1440667aeedf8465bdfe10208acf0ead656a2c68627299f546b315", + "sha256:9e36f163528fc50cbef305f02a5fd66d404edf7049cdaff211dbc2cba5a7013e", + "sha256:9eb9b5a968f6e0f6d640092a567e14529ff8cea2e29d00da6f78a79fa49f013c", + "sha256:9f76acfb5f68ba982635a53fd985a8044be98a35b43232c2a1ee235ffab3e1dd", + "sha256:a088f287f7d8275a33c07f2cac6c50b9319309a0200a39e7e75d80c707723099", + "sha256:a10bd2fd62e8ce916ececb342f348f190724a098c1faa056fdfb2a22ad5e8660", + "sha256:a4bbea04c97f6d78a48e3fbc1cb9116d2780b1b39e03a23f6eb9b603fd61f510", + "sha256:aa366a1e55b8ebfe8ca8ddc3cfe75c8ebade181aeb0f661d0cb05986b647f72a", + "sha256:aa49e06d94aba782c6a02eecb7e507969e7e7a41b267f1b359bb35585f295d5b", + "sha256:aad9aa39483ed8ec44d6d2e59e5b98a0d80676ef0d92f44bfc374836111f62f5", + "sha256:aae97dfdb60715c164419ac2532a76d013c3918a665eb6cb7288098b5f349aaf", + "sha256:abbefa31eee84842140f67acef1c828e28bba8bbf0c3bc6e5492a9af88152c28", + "sha256:ac931cdc9442c1763b8a8f6cd62c0c938737eafc5be75eff88df55fc73bc0d00", + "sha256:acd7d70b64c0aae0c7922cca83d288a16f5f6da523637697872253415269baef", + "sha256:add8cf6ddf9a65116119a28ece0f7886e30af27ba724a7594305f1d1b58a92a1", + "sha256:aee395f5d0927f947758b4ec119fd5fc8ec71f07a1c5c52077b30b04c0fa6955", + "sha256:b1b963fd8f5caa68e99dfae060d54de1fe9cba899b8718b44a00cdca53c3e590", + "sha256:b2d444f2e66624d68e9c6b211e28a76e22fff5fcabcfff4deac18b529b7d4137", + "sha256:b8d812c6011c08b8111a15e54dd990b8923692d80adf35488bee34026c35accf", + "sha256:ba96ae44888e0185281e937633a743ea90d5a196c6000f82565ebb0580012d40", + "sha256:bdebcc8a75d38c7598dfb2c9ed852d7a9eb4a10d6e2d0764b919b802bf32ac88", + "sha256:c07da4cebf6889f03ebac8d238f62318e29f495de0aa18a51ea14e61ae907e2e", + "sha256:c08e5c694306507275f2290073350c4f32e383db15213b2c69e7ff39c1193840", + "sha256:c4f469aebd783bb741c2ecb2a681008fd26bfe5c16a9a72ed5467f834e810df2", + "sha256:c5d7152ec39ca7c402d8fb9bad86140a15b9503bd0c54484e3f1bbe3dd37ceca", + "sha256:c674693f055fa2495de12292cb45e9944199d8eaef5a2dec45175c7c61cb73e3", + "sha256:c6ed5141a5c7507cf3ee76bd363b0d6f801e3321adc35b5d825a23115faa5465", + "sha256:c921ba5c51e4e9f63b8b00267d06566e1f63407408a0496da2d1d0bfc819c7fc", + "sha256:c9a4b821dc7055bf9e05ff5719e18ec501f75c0f0bbfabd573b277559780833d", + "sha256:c9f79d5325907f13e1be0b3e4dacc1049d1dffc4aeee3c995284bea5fe0fab7d", + "sha256:cd312b9692e831d2ffcad61eab31d91d4b4655a962e61de8fb410472cbcd37aa", + "sha256:cea3f4c1af79af13cdb2da0c028111d8f8522d4f22a000c82385535f24e5cf3a", + "sha256:cecdd5dfdc87b1fd87dbf81d4b037a544f47f4c744200a67013771682d67686a", + "sha256:cf9d57306d848218f3601fee7601fab1a327c942d56e2e97610583cb4dd74206", + "sha256:d34bbf07dbc7ca5970671b1512e928991fb5e9d95365636c9b2d8b4f53af405e", + "sha256:d49514be2f28d895c38cf9d2b72d7b9a07d00314519f456c0b50b53cfcf4c785", + "sha256:d680fbcb768404c601ecb43519ecd8461f6954cb11c06a78962f666832ccfca8", + "sha256:db1d75f6617a49c1c01bc7023713e0ff59ab32c9579ae62a7674c0e34f3b0b0a", + "sha256:dcb292aa7fe485ceff7af4f92e46c5af397daec5dff64871a528f0fc47a3cc5b", + "sha256:e07c65f443c887bbcf31cc1771d932ecc192a5273943589b3c7572b749f1ffb2", + "sha256:e902da4b04e6b52e5893900d4b8ab46068f75f3561f01bf1080957f9fd932ed6", + "sha256:e9308ff8241c532df3f3e570f9a5aeed6c853f888512ba4b75638d7c11c95ef6", + "sha256:eb7c9811bfaa8b1ed5ed319f5d370dfbcaa59d52ea64be2a5a85e18195930354", + "sha256:ebe6af670449830d6d9b752c256a983291c766a1365ba5d5460048f9e33a7818", + "sha256:ed21202aec73cda4d55d1ce57b389aadb90ffb044e6cd1080b8347efe1b1ec84", + "sha256:efe0374196335f93b53269acd811b944f2e6bdc88e8894f214bd636455484909", + "sha256:f64ec5397ea6a41fc1b4af0380d79b44a755b5531dcaccd9940fb260dca93038", + "sha256:f6ac4ef4d82dff54670227a69c67782ae0b811b5cf6b17954f1e8f7502fc0d1d", + "sha256:f6f0ce10945fab9c4c06ce14e22af9059d1a87493a9af4501a5b0b9187e21cf2", + "sha256:f8844cd288697c6425c9beba919302241e3278871dc6519515e72b04e987abcf", + "sha256:fe0306bd29505a9177aac19f1877174b0e7422c222a59f70b2cd41633448c3dc", + "sha256:ff3f333630ab480244a1bff72043e511a91eb22e7595dead8653ee5612dd8f3d", + "sha256:ffecec8eb889b58ba9be5b95fb1cc78e22ea8eedea38e8736a1568fe1979250e" ], "markers": "python_version >= '3.8'", - "version": "==6.1.0" + "version": "==6.1.1" }, "niquests": { "hashes": [ @@ -466,9 +466,18 @@ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.9.0.post0" }, + "python-dotenv": { + "hashes": [ + "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", + "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3" + ], + "index": "pypi", + "markers": "python_version >= '3.10'", + "version": "==1.2.2" + }, "pyyaml": { "hashes": [ "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", @@ -667,7 +676,7 @@ "sha256:fb5662abc48582c9eac388e43b1e58b9865458d20e3eb667292afd10e9b3e244", "sha256:fbe895164d31be3c0ab5c3cfdfe13054723d72440d8144239bbd4c8a5be088d0" ], - "markers": "(platform_python_implementation != 'CPython' or python_full_version > '3.7.10') and (platform_system == 'Darwin' or platform_system == 'Windows' or platform_system == 'Linux') and (platform_machine == 'x86_64' or platform_machine == 's390x' or platform_machine == 'armv7l' or platform_machine == 'ppc64le' or platform_machine == 'ppc64' or platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'arm64' or platform_machine == 'ARM64' or platform_machine == 'x86' or platform_machine == 'i686' or platform_machine == 'riscv64' or platform_machine == 'riscv64gc') and (platform_python_implementation == 'CPython' or (platform_python_implementation == 'PyPy' and python_version < '3.12'))", + "markers": "python_version < '3.12' and (platform_python_implementation != 'CPython' or python_full_version > '3.7.10') and (platform_system == 'Darwin' or platform_system == 'Windows' or platform_system == 'Linux') and (platform_machine == 'x86_64' or platform_machine == 's390x' or platform_machine == 'armv7l' or platform_machine == 'ppc64le' or platform_machine == 'ppc64' or platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'arm64' or platform_machine == 'ARM64' or platform_machine == 'x86' or platform_machine == 'i686' or platform_machine == 'riscv64' or platform_machine == 'riscv64gc') and (platform_python_implementation == 'CPython' or platform_python_implementation == 'PyPy')", "version": "==1.8.1" }, "recurring-ical-events": { @@ -683,7 +692,7 @@ "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.17.0" }, "tzdata": { @@ -696,11 +705,11 @@ }, "urllib3-future": { "hashes": [ - "sha256:7a9dc11783c97b5013d3271b07c26f946f3f51d4ea7dfd1742e267a245474cce", - "sha256:7c4e783310872869fd99486c8d42ed82eb5bbf9541dea6826b1d531caf73190c" + "sha256:18673623d88ec8f54f7a9648a8c7075c98c07f78bf077a9a7d53fd0cf9f8ede7", + "sha256:370e83b864a67013d07fe69510a51120122de32feca02847205d4c44efc559c1" ], "markers": "python_version >= '3.7'", - "version": "==2.20.903" + "version": "==2.21.900" }, "wassima": { "hashes": [ diff --git a/README.md b/README.md index eb34567..aaba766 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,144 @@ -# README: ICS-Importer für mailbox.org unter Linux +# ICS-Importer für mailbox.org unter Linux -Dieses Projekt durchsucht ein mailbox.org-Postfach per IMAP nach `.ics`-Anhängen und importiert enthaltene Termine anschließend per CalDAV in einen mailbox.org-Kalender. mailbox.org dokumentiert Thunderbird/IMAP für E-Mail und CalDAV für Kalender; die CalDAV-URL des Zielkalenders wird in mailbox.org im Kalender über **Eigenschaften** angezeigt. [kb.mailbox](https://kb.mailbox.org/de/privat/e-mail/e-mail-einrichtung-mit-mozilla-thunderbird/) +Dieses Projekt durchsucht ein mailbox.org-Postfach per IMAP nach E-Mails mit `.ics`-Anhängen und importiert enthaltene Termine automatisch per CalDAV in einen mailbox.org-Kalender.[cite:55][cite:45] + +## Überblick + +Der technische Ablauf ist bewusst einfach gehalten: + +1. Das Python-Skript verbindet sich per IMAP mit mailbox.org und durchsucht einen Ordner, standardmäßig `INBOX`.[cite:56][cite:55] +2. Es erkennt `.ics`-Anhänge, berechnet einen Hash als Duplikat-Schutz und verarbeitet nur neue Anhänge. +3. Die Termine werden per CalDAV in den gewünschten mailbox.org-Kalender geschrieben, dessen URL in mailbox.org über die Kalendereigenschaften ermittelt wird.[cite:45][cite:51] +4. Die Ausführung erfolgt manuell oder stündlich per Cron. ## Voraussetzungen -Benötigt werden ein Linux-System, Python 3, `pipenv`, ein mailbox.org-Konto sowie Zugangsdaten für IMAP und CalDAV. Wenn bei mailbox.org die Zwei-Faktor-Authentifizierung aktiv ist, sollen für externe Anwendungen App-Passwörter beziehungsweise Applikationspasswörter verwendet werden. [kb.mailbox](https://kb.mailbox.org/de/business/adressbuch-und-kalender/caldav-bei-thunderbird/) +Benötigt werden: -## Projektinhalt +- Linux +- Python 3 +- `pipenv` +- Ein mailbox.org-Konto +- Zugriff auf IMAP und CalDAV +- Bei aktiver Zwei-Faktor-Authentifizierung ein App- bzw. Applikationspasswort für externe Anwendungen.[cite:63][cite:45] -Die typische Struktur des Projekts sieht so aus: +## Projektstruktur ```text ics-importer/ -├── ics_mail_importer.py -├── config.ini.example -├── config.ini +├── ics_mail_importer_env.py +├── .env.example +├── .env ├── imported_uids.txt ├── ics_importer.log -└── cron.log +├── cron.log +└── README.md ``` -`ics_mail_importer.py` ist das Hauptskript, `config.ini` enthält die Zugangsdaten, `imported_uids.txt` dient als Duplikat-Schutz, und `cron.log` kann die Ausgabe des Cron-Jobs aufnehmen. Der Duplikat-Schutz ist sinnvoll, weil bei wiederkehrender IMAP-Prüfung sonst identische ICS-Anhänge mehrfach verarbeitet würden. [github](https://github.com/Vilhjalmr26/import_ics) +## Installation -## Installation mit pipenv +Projektverzeichnis anlegen und Abhängigkeiten installieren: -In das Projektverzeichnis wechseln und die Python-Abhängigkeiten installieren: +```bash +mkdir -p ~/ics-importer +cd ~/ics-importer +pipenv install python-dotenv caldav icalendar +``` + +`python-dotenv` liest Schlüssel-Wert-Paare aus einer `.env`-Datei und stellt sie als Umgebungsvariablen bereit.[cite:162][cite:164] + +## Konfiguration mit `.env` + +Eine `.env.example` kann als Vorlage verwendet werden: + +```dotenv +IMAP_HOST=imap.mailbox.org +IMAP_PORT=993 +IMAP_USERNAME=ihr-name@mailbox.org +IMAP_PASSWORD=IHR_PASSWORT_ODER_APP_PASSWORT +IMAP_FOLDER=INBOX +IMAP_UNSEEN_ONLY=true +IMAP_MARK_AS_READ=false + +CALDAV_URL=https://dav.mailbox.org/caldav/IHR_KALENDER_ID +CALDAV_USERNAME=ihr-name@mailbox.org +CALDAV_PASSWORD=IHR_PASSWORT_ODER_APP_PASSWORT +``` + +Die IMAP-Standardwerte für mailbox.org sind `imap.mailbox.org`, Port `993`, SSL/TLS und die vollständige E-Mail-Adresse als Benutzername.[cite:56][cite:55] + +Die vollständige CalDAV-URL des Zielkalenders wird in mailbox.org im Kalender über **Eigenschaften** angezeigt; für Thunderbird und andere Clients wird `https://dav.mailbox.org/caldav/XXX` verwendet, wobei `XXX` die individuelle Kalender-ID ist.[cite:45][cite:51] + +`.env` lokal anlegen und schützen: + +```bash +cp .env.example .env +chmod 600 .env +nano .env +``` + +## Bedeutung der Variablen + +| Variable | Bedeutung | +| --- | --- | +| `IMAP_HOST` | mailbox.org IMAP-Server, normalerweise `imap.mailbox.org`.[cite:56] | +| `IMAP_PORT` | IMAP-SSL-Port, normalerweise `993`.[cite:56] | +| `IMAP_USERNAME` | mailbox.org-E-Mail-Adresse.[cite:55] | +| `IMAP_PASSWORD` | Passwort oder E-Mail-App-Passwort bei aktiver 2FA.[cite:63] | +| `IMAP_FOLDER` | Zu durchsuchender Ordner, meist `INBOX`. | +| `IMAP_UNSEEN_ONLY` | Wenn `true`, werden nur ungelesene Mails geprüft. | +| `IMAP_MARK_AS_READ` | Wenn `true`, werden verarbeitete Mails als gelesen markiert. | +| `CALDAV_URL` | Vollständige CalDAV-URL des Zielkalenders.[cite:45] | +| `CALDAV_USERNAME` | mailbox.org-E-Mail-Adresse als CalDAV-Benutzername.[cite:45] | +| `CALDAV_PASSWORD` | Passwort oder Applikationspasswort bei aktiver 2FA.[cite:63][cite:45] | + +## Python-Skript + +Das Skript `ics_mail_importer_env.py` nutzt `python-dotenv`, `imaplib`, `email`, `icalendar` und `caldav`. `python-dotenv` ist speziell dafür gedacht, Werte aus einer `.env`-Datei zu laden und als Umgebungsvariablen verfügbar zu machen.[cite:162][cite:164] + +Beispielhafter Programmstart: ```bash cd ~/ics-importer -pipenv install caldav icalendar +pipenv run python3 ics_mail_importer_env.py ``` -`caldav` wird für den Zugriff auf den CalDAV-Kalender benötigt, während `icalendar` ICS-Dateien parst; für das Mail-Lesen werden Standardbibliotheken wie `imaplib` und `email` verwendet. [github](https://github.com/python-caldav/caldav) +Das Skript erzeugt oder nutzt dabei unter anderem diese Dateien: -Den Interpreter-Pfad des Pipenv-Umfelds kann man mit folgendem Befehl prüfen: +- `ics_importer.log` für die Programmausgabe +- `imported_uids.txt` für den Duplikat-Schutz +- optional `cron.log`, wenn die Cron-Ausgabe dorthin umgeleitet wird + +## Erster Testlauf + +Vor dem Cron-Einsatz sollte das Skript immer einmal manuell gestartet werden: + +```bash +pipenv run python3 ics_mail_importer_env.py +``` + +Wenn die Zugangsdaten korrekt sind, verbindet sich das Skript mit IMAP, liest passende Anhänge und importiert neue Termine in den angegebenen CalDAV-Kalender.[cite:55][cite:45] + +## Cron-Einrichtung + +Den Python-Pfad des Pipenv-Umfelds ermitteln: ```bash cd ~/ics-importer pipenv --py ``` -Pipenv dokumentiert virtuelle Umgebungen und deren Interpreter-Pfade explizit; dieser Pfad ist später für Cron praktisch. [pipenv.pypa](https://pipenv.pypa.io/en/latest/virtualenv.html) - -## Konfiguration - -Die Vorlage kopieren und bearbeiten: - -```bash -cp config.ini.example config.ini -nano config.ini -``` - -Beispielinhalt: - -```ini -[imap] -host = imap.mailbox.org -port = 993 -username = ihr-name@mailbox.org -password = IHR_PASSWORT_ODER_APP_PASSWORT -folder = INBOX -unseen_only = true -mark_as_read = false - -[caldav] -url = https://dav.mailbox.org/caldav/IHR_KALENDER_ID -username = ihr-name@mailbox.org -password = IHR_PASSWORT_ODER_APP_PASSWORT -``` - -Die IMAP-Einstellungen orientieren sich an mailbox.org für Thunderbird, und die CalDAV-URL des gewünschten Kalenders wird laut mailbox.org im Kalenderbereich über die Eigenschaften des Kalenders ermittelt. [kb.mailbox](https://kb.mailbox.org/de/privat/e-mail/e-mail-einrichtung-mit-mozilla-thunderbird/) - -### Bedeutung der wichtigsten Optionen - -| Schlüssel | Bedeutung | -|---|---| -| `imap.host` | IMAP-Server von mailbox.org: `imap.mailbox.org`. [kb.mailbox](https://kb.mailbox.org/de/privat/e-mail/e-mail-einrichtung-mit-mozilla-thunderbird/) | -| `imap.port` | Standardport für IMAP über SSL: `993`. [kb.mailbox](https://kb.mailbox.org/de/privat/e-mail/e-mail-einrichtung-mit-mozilla-thunderbird/) | -| `imap.folder` | Zu prüfender Ordner, meist `INBOX`. | -| `imap.unseen_only` | Wenn `true`, werden nur ungelesene Nachrichten gesucht; das reduziert unnötige Prüfungen. | -| `imap.mark_as_read` | Wenn `true`, markiert das Skript verarbeitete Nachrichten als gelesen. | -| `caldav.url` | Vollständige CalDAV-URL des Zielkalenders aus mailbox.org. [kb.mailbox](https://kb.mailbox.org/de/business/adressbuch-und-kalender/caldav-bei-thunderbird/) | -| `caldav.username` | Meist die mailbox.org-E-Mail-Adresse. [kb.mailbox](https://kb.mailbox.org/de/business/adressbuch-und-kalender/caldav-bei-thunderbird/) | -| `caldav.password` | Passwort oder App-/Applikationspasswort bei aktiver 2FA. [kb.mailbox](https://kb.mailbox.org/de/business/adressbuch-und-kalender/caldav-bei-thunderbird/) | - -## Erster Testlauf - -Das Skript kann manuell getestet werden mit: - -```bash -cd ~/ics-importer -pipenv run python3 ics_mail_importer.py -``` - -Dabei werden neue `.ics`-Anhänge gesucht, importiert und im Log protokolliert. Python-Lösungen für IMAP-Anhangsextraktion und ICS-zu-CalDAV-Import folgen genau diesem Muster aus Mail-Abruf, Parsing und Import. [stackoverflow](https://stackoverflow.com/questions/6225763/downloading-multiple-attachments-using-imaplib) - -## Stündliche Ausführung mit Cron - -Die eigene User-Crontab wird mit `crontab -e` bearbeitet; User-Crontabs liegen auf Debian-/Raspberry-Pi-ähnlichen Systemen typischerweise unter `/var/spool/cron/crontabs/`, sollen aber nicht direkt bearbeitet werden. [cronitor](https://cronitor.io/guides/five-places-for-cron-jobs) - -Für eine Ausführung **stündlich zur vollen Stunde** sieht der Eintrag so aus: +Stündlicher Cron-Eintrag: ```cron -0 * * * * /home/hans/.local/share/virtualenvs/ics-importer-wOz4rK-o/bin/python /home/hans/ics-importer/ics_mail_importer.py >> /home/hans/ics-importer/cron.log 2>&1 +0 * * * * /home/hans/.local/share/virtualenvs/ics-importer-wOz4rK-o/bin/python /home/hans/ics-importer/ics_mail_importer_env.py >> /home/hans/ics-importer/cron.log 2>&1 ``` -Alternativ ist auch `@hourly` möglich; beide Varianten entsprechen einer stündlichen Ausführung, wobei die klassische Cron-Syntax mit `0 * * * *` die Ausführung zur Minute 0 jeder Stunde beschreibt. [wiki.ubuntuusers](https://wiki.ubuntuusers.de/Cron/) +Die User-Crontab wird mit `crontab -e` gepflegt; die direkte Bearbeitung der Crontab-Dateien ist nicht empfehlenswert. Für stündliche Jobs sind sowohl `0 * * * *` als auch `@hourly` übliche Varianten.[cite:321][cite:55] -### Cron-Eintrag ohne eigenes Umleitungslog +## Logrotation -Wenn die zusätzliche Datei `cron.log` nicht gewünscht ist, kann die Umleitung weggelassen werden: - -```cron -0 * * * * /home/hans/.local/share/virtualenvs/ics-importer-wOz4rK-o/bin/python /home/hans/ics-importer/ics_mail_importer.py -``` - -## Logrotation für `cron.log` - -Für benutzerdefinierte Logs ist `logrotate` der übliche Weg unter Linux. Eine eigene Datei unter `/etc/logrotate.d/` ist dafür die gängige Methode. [dash0](https://www.dash0.com/guides/log-rotation-linux-logrotate) - -Beispiel: - -```bash -sudo nano /etc/logrotate.d/ics-importer -``` - -Inhalt: +Wenn `cron.log` mitgeschrieben wird, sollte die Datei per `logrotate` rotiert werden. Eine typische Konfiguration unter `/etc/logrotate.d/ics-importer` sieht so aus: ```conf /home/hans/ics-importer/cron.log { @@ -139,29 +152,61 @@ Inhalt: } ``` -Diese Konfiguration rotiert wöchentlich, behält vier alte Versionen, komprimiert alte Logs und legt nach der Rotation eine neue Datei mit passenden Rechten an. [putorius](https://www.putorius.net/rotating-custom-logs-with-logrotate-on.html) +Damit wird das Log wöchentlich rotiert, vier Versionen bleiben erhalten und ältere Logs werden komprimiert. -Zum Testen der Konfiguration: +## mailbox.org in Thunderbird -```bash -sudo logrotate -d /etc/logrotate.d/ics-importer -sudo logrotate -f /etc/logrotate.d/ics-importer -``` +Für das eigentliche Projekt ist Thunderbird nicht zwingend nötig, aber mailbox.org empfiehlt für E-Mail in Thunderbird IMAP und für Kalender CalDAV.[cite:55][cite:45] -## Einfügen in vim-nox +### IMAP in Thunderbird -Wenn `crontab -e` mit Vim geöffnet wird, kann im Insert-Modus oft direkt mit `Strg+Shift+V` aus dem Terminal-Zwischenspeicher eingefügt werden. Wenn Vim mit Clipboard-Unterstützung gebaut wurde, ist im Normal-Modus auch `"+p` zum Einfügen aus der System-Zwischenablage möglich. [reddit](https://www.reddit.com/r/vim/comments/q0fsw4/how_do_i_paste_clipboard_contents_into_vim/) +Thunderbird erkennt mailbox.org in der Regel automatisch; empfohlen wird IMAP statt POP3.[cite:55][cite:63] -## Sicherheitshinweise +### CalDAV in Thunderbird -`config.ini` enthält Zugangsdaten und sollte nicht weitergegeben oder in öffentliche Repositories eingecheckt werden. Bei aktivierter Zwei-Faktor-Authentifizierung empfiehlt mailbox.org für externe Anwendungen App-Passwörter beziehungsweise Applikationspasswörter anstelle des normalen Passworts. [kb.mailbox](https://kb.mailbox.org/de/business/adressbuch-und-kalender/caldav-bei-thunderbird/) +Kalender lassen sich in Thunderbird über **Datei → Neu → Kalender → Im Netzwerk → CalDAV** einbinden. Als URL dient die vollständige mailbox.org-CalDAV-Adresse des Kalenders.[cite:45][cite:51] + +In manchen Setups muss in Thunderbird zusätzlich `calendar.network.multirealm` auf `true` gesetzt werden, damit die Authentifizierung sauber funktioniert.[cite:45][cite:51] + +## Sicherheit + +`.env` enthält Zugangsdaten im Klartext und darf nicht in ein öffentliches Repository eingecheckt werden. `python-dotenv` ist für lokale Konfigurationsdateien gedacht, ersetzt aber kein Secret-Management-System.[cite:162][cite:164] + +Empfehlungen: + +- `.env` nur lokal speichern +- `chmod 600 .env` setzen +- `.env` in `.gitignore` eintragen +- Bei aktiver 2FA App-/Applikationspasswörter statt des Hauptpassworts verwenden.[cite:63][cite:45] ## Fehlersuche -Wenn der Cron-Job nicht läuft, sind die häufigsten Ursachen ein falscher Python-Pfad, fehlende Rechte auf Dateien oder abweichende Umgebungsvariablen unter Cron. Cron-Jobs sollten deshalb mit absoluten Pfaden arbeiten, und ein separates Log wie `cron.log` macht Fehler schnell sichtbar. [betterstack](https://betterstack.com/community/guides/logging/how-to-manage-log-files-with-logrotate-on-ubuntu-20-04/) +### IMAP funktioniert nicht -Wenn Termine nicht importiert werden, sollte zuerst geprüft werden, ob die IMAP-Anmeldung funktioniert, ob tatsächlich `.ics`-Anhänge im ausgewählten Ordner vorhanden sind und ob die CalDAV-URL exakt zum Zielkalender gehört. mailbox.org beschreibt, dass die Kalenderadresse direkt aus den Kalendereigenschaften des jeweiligen Kalenders übernommen werden soll. [kb.mailbox](https://kb.mailbox.org/de/privat/e-mail/e-mail-einrichtung-mit-mozilla-thunderbird/) +Prüfen: + +- `IMAP_HOST=imap.mailbox.org` +- `IMAP_PORT=993` +- vollständige E-Mail-Adresse als Benutzername +- korrektes Passwort oder App-Passwort bei aktiver 2FA.[cite:56][cite:63] + +### CalDAV funktioniert nicht + +Prüfen: + +- vollständige CalDAV-URL aus den Kalendereigenschaften in mailbox.org +- korrekter Benutzername +- korrektes Passwort bzw. Applikationspasswort bei aktiver 2FA.[cite:45][cite:51] + +### Cron führt das Skript nicht aus + +Prüfen: + +- absoluter Python-Pfad aus `pipenv --py` +- absoluter Skriptpfad +- Schreibrechte für `cron.log` +- manueller Testlauf funktioniert bereits ## Empfohlener Betriebsmodus -Für dieses Setup ist ein stündlicher Cron-Job mit aktiviertem Duplikat-Schutz und `unseen_only = true` ein pragmatischer Standard. Das reduziert unnötige IMAP-Abfragen und passt gut zu einem Postfach, in dem Termine typischerweise per Mail-Anhang eingehen. [naschenweng](https://www.naschenweng.eu/2025/03/29/%F0%9F%93%85-sync-ics-feeds-to-your-caldav-calendar-with-emojis-deduplication-and-docker/) \ No newline at end of file +Für die meisten Setups ist `IMAP_UNSEEN_ONLY=true` sinnvoll, damit nur neue bzw. ungelesene Mails geprüft werden. In Verbindung mit dem Hash-basierten Duplikat-Schutz verhindert das unnötige Doppelimporte und reduziert die Last auf dem Postfach. \ No newline at end of file diff --git a/config.ini b/config.ini deleted file mode 100644 index 6b372a1..0000000 --- a/config.ini +++ /dev/null @@ -1,17 +0,0 @@ -[imap] -host = imap.mailbox.org -port = 993 -username = minitux@mailbox.org -# Normales Passwort ODER App-Passwort (bei aktiver 2FA zwingend!) -password = 4711Cayenne64 -folder = INBOX -# true = nur ungelesene Mails prüfen (empfohlen) -unseen_only = true -# true = verarbeitete Mails als gelesen markieren -mark_as_read = true - -[caldav] -# CalDAV-URL aus mailbox.org Office: Kalender → drei Striche → Eigenschaften -url = https://dav.mailbox.org/caldav/Y2FsOi8vMC8zMg -username = minitux@mailbox.org -password = 4711Cayenne64 diff --git a/config.ini.example b/config.ini.example deleted file mode 100644 index 2e94d9a..0000000 --- a/config.ini.example +++ /dev/null @@ -1,17 +0,0 @@ -[imap] -host = imap.mailbox.org -port = 993 -username = ihr-name@mailbox.org -# Normales Passwort ODER App-Passwort (bei aktiver 2FA zwingend!) -password = IHR_PASSWORT_ODER_APP_PASSWORT -folder = INBOX -# true = nur ungelesene Mails prüfen (empfohlen) -unseen_only = true -# true = verarbeitete Mails als gelesen markieren -mark_as_read = false - -[caldav] -# CalDAV-URL aus mailbox.org Office: Kalender → drei Striche → Eigenschaften -url = https://dav.mailbox.org/caldav/IHR_KALENDER_ID -username = ihr-name@mailbox.org -password = IHR_PASSWORT_ODER_APP_PASSWORT diff --git a/ics_importer.log b/ics_importer.log index d4ab540..7a19b9a 100644 --- a/ics_importer.log +++ b/ics_importer.log @@ -22,3 +22,9 @@ 2026-05-12 08:26:10,244 [INFO] ✓ Termin importiert: Sabine [040000008200E00074C5] 2026-05-12 08:26:10,244 [INFO] Fertig. 3 Termine insgesamt importiert. 2026-05-12 08:26:10,245 [INFO] ============================================================ +2026-05-31 09:12:16,921 [INFO] ============================================================ +2026-05-31 09:12:16,921 [INFO] ICS-Importer gestartet (2026-05-31T07:12:16.921142+00:00) +2026-05-31 09:12:16,921 [INFO] Verbinde mit IMAP imap.mailbox.org:993 als minitux@mailbox.org ... +2026-05-31 09:12:17,331 [INFO] 0 .ics-Anhang/-Anhänge gefunden. +2026-05-31 09:12:17,331 [INFO] Fertig. 0 Termine insgesamt importiert. +2026-05-31 09:12:17,331 [INFO] ============================================================ diff --git a/ics_mail_importer.py b/ics_mail_importer.py index 40cc3c9..efe028a 100644 --- a/ics_mail_importer.py +++ b/ics_mail_importer.py @@ -1,81 +1,67 @@ #!/usr/bin/env python3 """ -ics_mail_importer.py --------------------- +ics_mail_importer_env.py +------------------------ Sucht in einem mailbox.org IMAP-Postfach nach E-Mails mit .ics-Anhängen und importiert die enthaltenen Termine automatisch in einen CalDAV-Kalender. -Benötigte Pakete: - pipenv install caldav icalendar - -Einrichtung: - 1. config.ini ausfüllen (liegt im gleichen Verzeichnis) - 2. Optional: Als Cronjob einrichten (z.B. alle 30 Minuten) - */30 * * * * /pfad/zu/python3 /pfad/zum/ics_mail_importer.py +Konfiguration über .env-Datei mit python-dotenv. """ import imaplib import email -import os import logging import hashlib -import configparser +import os from pathlib import Path from datetime import datetime, timezone +from dotenv import load_dotenv import caldav from icalendar import Calendar -# ─────────────────────────── Logging ─────────────────────────────────────────── -LOG_FILE = Path(__file__).parent / "ics_importer.log" +BASE_DIR = Path(__file__).resolve().parent +load_dotenv(BASE_DIR / ".env") + +LOG_FILE = BASE_DIR / "ics_importer.log" logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", - handlers=[ - logging.FileHandler(LOG_FILE), - logging.StreamHandler(), - ], + handlers=[logging.FileHandler(LOG_FILE), logging.StreamHandler()], ) log = logging.getLogger(__name__) -# ─────────────────────────── Konfiguration laden ─────────────────────────────── -CONFIG_FILE = Path(__file__).parent / "config.ini" +SEEN_FILE = BASE_DIR / "imported_uids.txt" -def load_config(): - if not CONFIG_FILE.exists(): - log.error("config.ini nicht gefunden. Bitte config.ini.example umbenennen und ausfüllen.") - raise FileNotFoundError(str(CONFIG_FILE)) - cfg = configparser.ConfigParser() - cfg.read(CONFIG_FILE) - return cfg - -# ─────────────────────────── Duplikat-Tracking ──────────────────────────────── -SEEN_FILE = Path(__file__).parent / "imported_uids.txt" def load_seen_uids(): if not SEEN_FILE.exists(): return set() return set(SEEN_FILE.read_text().splitlines()) + def save_uid(uid: str): with SEEN_FILE.open("a") as f: f.write(uid + "\n") -# ─────────────────────────── IMAP-Anhänge holen ─────────────────────────────── -def fetch_ics_attachments(cfg): - host = cfg["imap"]["host"] - port = int(cfg["imap"].get("port", "993")) - username = cfg["imap"]["username"] - password = cfg["imap"]["password"] - folder = cfg["imap"].get("folder", "INBOX") + +def fetch_ics_attachments(): + host = os.getenv("IMAP_HOST", "imap.mailbox.org") + port = int(os.getenv("IMAP_PORT", "993")) + username = os.getenv("IMAP_USERNAME") + password = os.getenv("IMAP_PASSWORD") + folder = os.getenv("IMAP_FOLDER", "INBOX") + + if not username or not password: + raise RuntimeError("IMAP_USERNAME oder IMAP_PASSWORD fehlt in .env") log.info(f"Verbinde mit IMAP {host}:{port} als {username} ...") conn = imaplib.IMAP4_SSL(host, port) conn.login(username, password) conn.select(folder) - search_unseen_only = cfg["imap"].getboolean("unseen_only", fallback=True) - criteria = "(UNSEEN)" if search_unseen_only else "ALL" + unseen_only = os.getenv("IMAP_UNSEEN_ONLY", "true").lower() == "true" + criteria = "(UNSEEN)" if unseen_only else "ALL" _, msg_ids = conn.search(None, criteria) found = [] @@ -85,12 +71,10 @@ def fetch_ics_attachments(cfg): msg = email.message_from_bytes(raw) for part in msg.walk(): - ct = part.get_content_type() - fn = part.get_filename() or "" - - is_ics = ( - ct in ("text/calendar", "application/ics") - or fn.lower().endswith(".ics") + ct = part.get_content_type() + fn = part.get_filename() or "" + is_ics = ct in ("text/calendar", "application/ics") or fn.lower().endswith( + ".ics" ) if not is_ics: continue @@ -103,7 +87,8 @@ def fetch_ics_attachments(cfg): log.info(f" ics-Anhang gefunden: {fn!r} (Hash {uid_hash[:12]}…)") found.append((uid_hash, ics_bytes)) - if cfg["imap"].getboolean("mark_as_read", fallback=False): + mark_read = os.getenv("IMAP_MARK_AS_READ", "false").lower() == "true" + if mark_read: conn.store(mid, "+FLAGS", "\\Seen") conn.close() @@ -111,13 +96,18 @@ def fetch_ics_attachments(cfg): log.info(f" {len(found)} .ics-Anhang/-Anhänge gefunden.") return found -# ─────────────────────────── CalDAV-Import ──────────────────────────────────── -def import_to_caldav(cfg, ics_bytes: bytes, uid_hash: str): - caldav_url = cfg["caldav"]["url"] - username = cfg["caldav"]["username"] - password = cfg["caldav"]["password"] - client = caldav.DAVClient(url=caldav_url, username=username, password=password) +def import_to_caldav(ics_bytes: bytes, uid_hash: str): + caldav_url = os.getenv("CALDAV_URL") + username = os.getenv("CALDAV_USERNAME") + password = os.getenv("CALDAV_PASSWORD") + + if not caldav_url or not username or not password: + raise RuntimeError( + "CALDAV_URL, CALDAV_USERNAME oder CALDAV_PASSWORD fehlt in .env" + ) + + client = caldav.DAVClient(url=caldav_url, username=username, password=password) calendar = client.calendar(url=caldav_url) cal = Calendar.from_ical(ics_bytes) @@ -135,35 +125,35 @@ def import_to_caldav(cfg, ics_bytes: bytes, uid_hash: str): uid = str(component.get("uid", uid_hash + f"-{imported}")) try: calendar.add_event(single_cal.to_ical().decode("utf-8")) - log.info(f" ✓ Termin importiert: {component.get('summary', '(kein Titel)')} [{uid[:20]}]") + log.info( + f" ✓ Termin importiert: {component.get('summary', '(kein Titel)')} [{uid[:20]}]" + ) imported += 1 except Exception as e: log.warning(f" ✗ Fehler beim Import von {uid[:20]}: {e}") return imported -# ─────────────────────────── Hauptprogramm ──────────────────────────────────── + def main(): log.info("=" * 60) log.info(f"ICS-Importer gestartet ({datetime.now(timezone.utc).isoformat()})") - cfg = load_config() seen_uids = load_seen_uids() - - attachments = fetch_ics_attachments(cfg) + attachments = fetch_ics_attachments() total_imported = 0 for uid_hash, ics_bytes in attachments: if uid_hash in seen_uids: log.info(f" Überspringe bereits importierten Anhang {uid_hash[:12]}…") continue - - n = import_to_caldav(cfg, ics_bytes, uid_hash) + n = import_to_caldav(ics_bytes, uid_hash) total_imported += n save_uid(uid_hash) log.info(f"Fertig. {total_imported} Termine insgesamt importiert.") log.info("=" * 60) + if __name__ == "__main__": - main() + main() \ No newline at end of file