Compare commits

...

10 commits

9 changed files with 185 additions and 579 deletions

208
.gitignore vendored
View file

@ -1,207 +1 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[codz]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py.cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
#poetry.toml
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
#pdm.lock
#pdm.toml
.pdm-python
.pdm-build/
# pixi
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
#pixi.lock
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
# in the .venv directory. It is recommended not to include this directory in version control.
.pixi
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.envrc
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Abstra
# Abstra is an AI-powered process automation framework.
# Ignore directories containing user credentials, local state, and settings.
# Learn more at https://abstra.io/docs
.abstra/
# Visual Studio Code
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
# and can be added to the global gitignore or merged into this file. However, if you prefer,
# you could uncomment the following to ignore the entire vscode folder
# .vscode/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# Cursor
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
# refer to https://docs.cursor.com/context/ignore-files
.cursorignore
.cursorindexingignore
# Marimo
marimo/_static/
marimo/_lsp/
__marimo__/
*.lock

1
.python-version Normal file
View file

@ -0,0 +1 @@
3.12

16
Pipfile
View file

@ -1,16 +0,0 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
pyotp = "*"
cryptography = "*"
opencv-python = "*"
pyzbar = "*"
pygobject = "*"
[dev-packages]
[requires]
python_version = "3.12"

305
Pipfile.lock generated
View file

@ -1,305 +0,0 @@
{
"_meta": {
"hash": {
"sha256": "1bc617a94364686f1f2832093b9bedcf1b1ef8d00a5660bbbeaf9cf69f14217a"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.12"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"cffi": {
"hashes": [
"sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb",
"sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b",
"sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f",
"sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9",
"sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44",
"sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2",
"sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c",
"sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75",
"sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65",
"sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e",
"sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a",
"sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e",
"sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25",
"sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a",
"sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe",
"sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b",
"sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91",
"sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592",
"sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187",
"sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c",
"sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1",
"sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94",
"sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba",
"sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb",
"sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165",
"sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529",
"sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca",
"sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c",
"sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6",
"sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c",
"sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0",
"sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743",
"sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63",
"sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5",
"sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5",
"sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4",
"sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d",
"sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b",
"sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93",
"sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205",
"sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27",
"sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512",
"sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d",
"sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c",
"sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037",
"sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26",
"sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322",
"sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb",
"sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c",
"sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8",
"sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4",
"sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414",
"sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9",
"sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664",
"sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9",
"sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775",
"sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739",
"sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc",
"sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062",
"sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe",
"sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9",
"sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92",
"sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5",
"sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13",
"sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d",
"sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26",
"sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f",
"sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495",
"sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b",
"sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6",
"sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c",
"sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef",
"sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5",
"sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18",
"sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad",
"sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3",
"sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7",
"sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5",
"sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534",
"sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49",
"sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2",
"sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5",
"sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453",
"sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"
],
"markers": "python_full_version >= '3.9' and platform_python_implementation != 'PyPy'",
"version": "==2.0.0"
},
"cryptography": {
"hashes": [
"sha256:04911b149eae142ccd8c9a68892a70c21613864afb47aba92d8c7ed9cc001023",
"sha256:07c0eb6657c0e9cca5891f4e35081dbf985c8131825e21d99b4f440a8f496f36",
"sha256:0b507c8e033307e37af61cb9f7159b416173bdf5b41d11c4df2e499a1d8e007c",
"sha256:0c7ffe8c9b1fcbb07a26d7c9fa5e857c2fe80d72d7b9e0353dcf1d2180ae60ee",
"sha256:1a88634851d9b8de8bb53726f4300ab191d3b2f42595e2581a54b26aba71b7cc",
"sha256:1d3b3edd145953832e09607986f2bd86f85d1dc9c48ced41808b18009d9f30e5",
"sha256:1e3b6428a3d56043bff0bb85b41c535734204e599c1c0977e1d0f261b02f3ad5",
"sha256:1fd1a69086926b623ef8126b4c33d5399ce9e2f3fac07c9c734c2a4ec38b6d02",
"sha256:218abd64a2e72f8472c2102febb596793347a3e65fafbb4ad50519969da44470",
"sha256:21b6fc8c71a3f9a604f028a329e5560009cc4a3a828bfea5fcba8eb7647d88fe",
"sha256:27c53b4f6a682a1b645fbf1cd5058c72cf2f5aeba7d74314c36838c7cbc06e0f",
"sha256:2b9cad9cf71d0c45566624ff76654e9bae5f8a25970c250a26ccfc73f8553e2d",
"sha256:2fafb6aa24e702bbf74de4cb23bfa2c3beb7ab7683a299062b69724c92e0fa73",
"sha256:3f37aa12b2d91e157827d90ce78f6180f0c02319468a0aea86ab5a9566da644b",
"sha256:48b983089378f50cba258f7f7aa28198c3f6e13e607eaf10472c26320332ca9a",
"sha256:48c01988ecbb32979bb98731f5c2b2f79042a6c58cc9a319c8c2f9987c7f68f9",
"sha256:4a766d2a5d8127364fd936572c6e6757682fc5dfcbdba1632d4554943199f2fa",
"sha256:512c0250065e0a6b286b2db4bbcc2e67d810acd53eb81733e71314340366279e",
"sha256:5840f05518caa86b09d23f8b9405a7b6d5400085aa14a72a98fdf5cf1568c0d2",
"sha256:5e38f203160a48b93010b07493c15f2babb4e0f2319bbd001885adb3f3696d21",
"sha256:6b275e398ab3a7905e168c036aad54b5969d63d3d9099a0a66cc147a3cc983be",
"sha256:7282d8f092b5be7172d6472f29b0631f39f18512a3642aefe52c3c0e0ccfad5a",
"sha256:747b6f4a4a23d5a215aadd1d0b12233b4119c4313df83ab4137631d43672cc90",
"sha256:758cfc7f4c38c5c5274b55a57ef1910107436f4ae842478c4989abbd24bd5acb",
"sha256:8b16c1ede6a937c291d41176934268e4ccac2c6521c69d3f5961c5a1e11e039e",
"sha256:8b9bf67b11ef9e28f4d78ff88b04ed0929fcd0e4f70bb0f704cfc32a5c6311ee",
"sha256:8e2ad4d1a5899b7caa3a450e33ee2734be7cc0689010964703a7c4bcc8dd4fd0",
"sha256:9066cfd7f146f291869a9898b01df1c9b0e314bfa182cef432043f13fc462c92",
"sha256:91447f2b17e83c9e0c89f133119d83f94ce6e0fb55dd47da0a959316e6e9cfa1",
"sha256:97e83bf4f2f2c084d8dd792d13841d0a9b241643151686010866bbd076b19659",
"sha256:9bd26f2f75a925fdf5e0a446c0de2714f17819bf560b44b7480e4dd632ad6c46",
"sha256:9bdc25e4e01b261a8fda4e98618f1c9515febcecebc9566ddf4a70c63967043b",
"sha256:9ec3f2e2173f36a9679d3b06d3d01121ab9b57c979de1e6a244b98d51fea1b20",
"sha256:9f13b040649bc18e7eb37936009b24fd31ca095a5c647be8bb6aaf1761142bd1",
"sha256:a08e7401a94c002e79dc3bc5231b6558cd4b2280ee525c4673f650a37e2c7685",
"sha256:a61c154cc5488272a6c4b86e8d5beff4639cdb173d75325ce464d723cda0052b",
"sha256:bb7fb9cd44c2582aa5990cf61a4183e6f54eea3172e54963787ba47287edd135",
"sha256:bca3f0ce67e5a2a2cf524e86f44697c4323a86e0fd7ba857de1c30d52c11ede1",
"sha256:bda55e8dbe8533937956c996beaa20266a8eca3570402e52ae52ed60de1faca8",
"sha256:be939b99d4e091eec9a2bcf41aaf8f351f312cd19ff74b5c83480f08a8a43e0b",
"sha256:c4b93af7920cdf80f71650769464ccf1fb49a4b56ae0024173c24c48eb6b1612",
"sha256:cb5e8daac840e8879407acbe689a174f5ebaf344a062f8918e526824eb5d97af",
"sha256:d19f5f48883752b5ab34cff9e2f7e4a7f216296f33714e77d1beb03d108632b6",
"sha256:d30bc11d35743bf4ddf76674a0a369ec8a21f87aaa09b0661b04c5f6c46e8d7b",
"sha256:e12b61e0b86611e3f4c1756686d9086c1d36e6fd15326f5658112ad1f1cc8807",
"sha256:e6f6775eaaa08c0eec73e301f7592f4367ccde5e4e4df8e58320f2ebf161ea2c",
"sha256:e7155c0b004e936d381b15425273aee1cebc94f879c0ce82b0d7fecbf755d53a",
"sha256:e8633996579961f9b5a3008683344c2558d38420029d3c0bc7ff77c17949a4e1",
"sha256:f25a41f5b34b371a06dad3f01799706631331adc7d6c05253f5bca22068c7a34",
"sha256:f3e32ab7dd1b1ef67b9232c4cf5e2ee4cd517d4316ea910acaaa9c5712a1c663",
"sha256:f9b2dc7668418fb6f221e4bf701f716e05e8eadb4f1988a2487b11aedf8abe62",
"sha256:fab8f805e9675e61ed8538f192aad70500fa6afb33a8803932999b1049363a08",
"sha256:fe245cf4a73c20592f0f48da39748b3513db114465be78f0a36da847221bd1b4",
"sha256:ff798ad7a957a5021dcbab78dfff681f0cf15744d0e6af62bd6746984d9c9e9c"
],
"index": "pypi",
"markers": "python_version >= '3.8' and python_full_version not in '3.9.0, 3.9.1'",
"version": "==46.0.2"
},
"numpy": {
"hashes": [
"sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff",
"sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47",
"sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84",
"sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d",
"sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6",
"sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f",
"sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b",
"sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49",
"sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163",
"sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571",
"sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42",
"sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff",
"sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491",
"sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4",
"sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566",
"sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf",
"sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40",
"sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd",
"sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06",
"sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282",
"sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680",
"sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db",
"sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3",
"sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90",
"sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1",
"sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289",
"sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab",
"sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c",
"sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d",
"sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb",
"sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d",
"sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a",
"sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf",
"sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1",
"sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2",
"sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a",
"sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543",
"sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00",
"sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c",
"sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f",
"sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd",
"sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868",
"sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303",
"sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83",
"sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3",
"sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d",
"sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87",
"sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa",
"sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f",
"sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae",
"sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda",
"sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915",
"sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249",
"sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de",
"sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"
],
"markers": "python_version >= '3.9'",
"version": "==2.2.6"
},
"opencv-python": {
"hashes": [
"sha256:092c16da4c5a163a818f120c22c5e4a2f96e0db4f24e659c701f1fe629a690f9",
"sha256:51fd981c7df6af3e8f70b1556696b05224c4e6b6777bdd2a46b3d4fb09de1a92",
"sha256:812eb116ad2b4de43ee116fcd8991c3a687f099ada0b04e68f64899c09448e81",
"sha256:8b738389cede219405f6f3880b851efa3415ccd674752219377353f017d2994d",
"sha256:d98edb20aa932fd8ebd276a72627dad9dc097695b3d435a4257557bbb49a79d2",
"sha256:f9a1f08883257b95a5764bf517a32d75aec325319c8ed0f89739a57fae9e92a5",
"sha256:ff554d3f725b39878ac6a2e1fa232ec509c36130927afc18a1719ebf4fbf4357"
],
"index": "pypi",
"markers": "python_version >= '3.6'",
"version": "==4.12.0.88"
},
"pycairo": {
"hashes": [
"sha256:026afd53b75291917a7412d9fe46dcfbaa0c028febd46ff1132d44a53ac2c8b6",
"sha256:082aef6b3a9dcc328fa648d38ed6b0a31c863e903ead57dd184b2e5f86790140",
"sha256:0fee15f5d72b13ba5fd065860312493dc1bca6ff2dce200ee9d704e11c94e60a",
"sha256:26ec5c6126781eb167089a123919f87baa2740da2cca9098be8b3a6b91cc5fbc",
"sha256:3ed16d48b8a79cc584cb1cb0ad62dfb265f2dda6d6a19ef5aab181693e19c83c",
"sha256:458877513eb2125513122e8aa9c938630e94bb0574f94f4fb5ab55eb23d6e9ac",
"sha256:53e6dbc98456f789965dad49ef89ce2c62f9a10fc96c8d084e14da0ffb73d8a6",
"sha256:6339979bfec8b58a06476094a9a5c104bd5a99932ddaff16ca0d9203d2f4482c",
"sha256:94f2ed204999ab95a0671a0fa948ffbb9f3d6fb8731fe787917f6d022d9c1c0f",
"sha256:957e0340ee1c279d197d4f7cfa96f6d8b48e453eec711fca999748d752468ff4",
"sha256:c00cfbb7f30eb7ca1d48886712932e2d91e8835a8496f4e423878296ceba573e",
"sha256:c6ae15392e28ebfc0b35d8dc05d395d3b6be4bad9ad4caecf0fa12c8e7150225",
"sha256:c8ab91a75025f984bc327ada335c787efb61c929ea0512063793cb36cee503d4",
"sha256:d0ab30585f536101ad6f09052fc3895e2a437ba57531ea07223d0e076248025d",
"sha256:d13352429d8a08a1cb3607767d23d2fb32e4c4f9faa642155383980ec1478c24",
"sha256:d50d190f5033992b55050b9f337ee42a45c3568445d5e5d7987bab96c278d8a6",
"sha256:da0d1e6d4842eed4d52779222c6e43d254244a486ca9fdab14e30042fd5bdf28",
"sha256:e955328c1a5147bf71ee94e206413ce15e12630296a79788fcd246c80e5337b8"
],
"markers": "python_version >= '3.9'",
"version": "==1.28.0"
},
"pycparser": {
"hashes": [
"sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2",
"sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"
],
"markers": "implementation_name != 'PyPy'",
"version": "==2.23"
},
"pygobject": {
"hashes": [
"sha256:a8da09134a0f7d56491cf2412145e35aa74e91d760e8f337096a1cda0b92bae7"
],
"index": "pypi",
"markers": "python_version >= '3.9'",
"version": "==3.54.3"
},
"pyotp": {
"hashes": [
"sha256:346b6642e0dbdde3b4ff5a930b664ca82abfa116356ed48cc42c7d6590d36f63",
"sha256:81c2e5865b8ac55e825b0358e496e1d9387c811e85bb40e71a3b29b288963612"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==2.9.0"
},
"pyzbar": {
"hashes": [
"sha256:13e3ee5a2f3a545204a285f41814d5c0db571967e8d4af8699a03afc55182a9c",
"sha256:4559628b8192feb25766d954b36a3753baaf5c97c03135aec7e4a026036b475d",
"sha256:8f4c5264c9c7c6b9f20d01efc52a4eba1ded47d9ba857a94130afe33703eb518"
],
"index": "pypi",
"version": "==0.1.9"
}
},
"develop": {}
}

66
flake.nix Normal file
View file

@ -0,0 +1,66 @@
{
description = "py2fa-gtk packaged the Nix way";
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
pythonEnv = pkgs.python312.withPackages (ps: with ps; [
cryptography
opencv-python
pygobject3
pyotp
pyzbar
]);
runtimeLibs = with pkgs; [
gtk3
zbar
gobject-introspection
];
in
{
packages.${system}.default = pkgs.stdenv.mkDerivation {
name = "py2fa-gtk";
src = ./src;
nativeBuildInputs = [
pkgs.wrapGAppsHook3
pkgs.makeWrapper
];
buildInputs = runtimeLibs ++ [ pythonEnv ];
dontBuild = true;
installPhase = ''
runHook preInstall
# A. Create standard directory structure in the Nix Store
mkdir -p $out/bin $out/share/py2fa-gtk
# B. Copy the python script to the store (Read-Only)
cp main.py $out/share/py2fa-gtk/main.py
# C. Create the binary executable
# We wrap the python executable.
# 1. We tell it to run our specific script.
# 2. We inject the ZBar library path so pyzbar works.
makeWrapper ${pythonEnv}/bin/python3 $out/bin/py2fa-gtk \
--add-flags "$out/share/py2fa-gtk/main.py" \
--prefix LD_LIBRARY_PATH : "${pkgs.lib.makeLibraryPath [ pkgs.zbar ]}"
runHook postInstall
'';
# wrapGAppsHook runs automatically after installPhase.
# It will see the binary in $out/bin/py2fa-gtk and double-wrap it
# to inject XDG_DATA_DIRS and GI_TYPELIB_PATH (fixing Pango/GTK).
};
};
}

13
pyproject.toml Normal file
View file

@ -0,0 +1,13 @@
[project]
name = "py2fa-gtk"
version = "0.1.1"
description = "Wannabe Authy replacement."
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"cryptography>=46.0.3",
"opencv-python>=4.12.0.88",
"pygobject>=3.54.5",
"pyotp>=2.9.0",
"pyzbar>=0.1.9",
]

View file

@ -40,6 +40,11 @@ class PyTFAApp:
self.timer_label = Gtk.Label()
header.pack_start(self.timer_label)
# Global timer progress bar anchored in header
self.timer_progress = Gtk.ProgressBar()
self.timer_progress.set_hexpand(True)
header.pack_start(self.timer_progress)
# Create scrolled window for accounts
scrolled = Gtk.ScrolledWindow()
self.main_box.pack_start(scrolled, True, True, 0)
@ -52,6 +57,7 @@ class PyTFAApp:
# Password entry for encryption
self.password = None
self.account_widgets = {} # Store mapping of account to widgets
self.expanded_service = None
# Load encrypted data
self.load_encrypted_data()
@ -174,6 +180,14 @@ class PyTFAApp:
else:
confirm_entry = None
def trigger_ok(*_args):
dialog.response(Gtk.ResponseType.OK)
password_entry.connect("activate", trigger_ok)
if initial_setup and confirm_entry:
confirm_entry.connect("activate", trigger_ok)
dialog.show_all()
response = dialog.run()
@ -340,64 +354,103 @@ class PyTFAApp:
box.set_margin_end(10)
row.add(box)
# First row: Service name and buttons
top_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
box.pack_start(top_box, False, False, 0)
# Header with service name and arrow indicator
header_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
arrow_image = Gtk.Image.new_from_icon_name("go-next-symbolic", Gtk.IconSize.BUTTON)
header_box.pack_start(arrow_image, False, False, 0)
# Service label
service_label = Gtk.Label(label=account['service'])
service_label.set_halign(Gtk.Align.START)
service_label.set_hexpand(True)
top_box.pack_start(service_label, True, True, 0)
header_box.pack_start(service_label, True, True, 0)
header_event_box = Gtk.EventBox()
header_event_box.set_visible_window(False)
header_event_box.add(header_box)
box.pack_start(header_event_box, False, False, 0)
# Details revealer containing buttons and code
details_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
buttons_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
# View secret button
view_btn = Gtk.Button.new_from_icon_name("document-properties", Gtk.IconSize.BUTTON)
view_btn.set_tooltip_text("View Secret")
view_btn.connect("clicked", self.on_view_secret, account)
top_box.pack_start(view_btn, False, False, 0)
buttons_box.pack_start(view_btn, False, False, 0)
# Rename button
rename_btn = Gtk.Button.new_from_icon_name("edit", Gtk.IconSize.BUTTON)
rename_btn.set_tooltip_text("Rename Account")
rename_btn.connect("clicked", self.on_rename_account, account)
top_box.pack_start(rename_btn, False, False, 0)
buttons_box.pack_start(rename_btn, False, False, 0)
# Delete button
delete_btn = Gtk.Button.new_from_icon_name("edit-delete", Gtk.IconSize.BUTTON)
delete_btn.set_tooltip_text("Delete Account")
delete_btn.connect("clicked", self.on_delete_account, account)
top_box.pack_start(delete_btn, False, False, 0)
buttons_box.pack_start(delete_btn, False, False, 0)
# Second row: Code and progress bar
bottom_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
box.pack_start(bottom_box, False, False, 0)
details_box.pack_start(buttons_box, False, False, 0)
# Code label
code_label = Gtk.Label(label="Generating...")
code_label.set_halign(Gtk.Align.START)
code_label.set_selectable(True)
# Use monospace font for code
font = Pango.FontDescription("Monospace 16")
code_label.override_font(font)
details_box.pack_start(code_label, False, False, 0)
bottom_box.pack_start(code_label, False, False, 0)
revealer = Gtk.Revealer()
revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN)
revealer.add(details_box)
box.pack_start(revealer, False, False, 0)
# Progress bar for timer
progress = Gtk.ProgressBar()
progress.set_hexpand(True)
bottom_box.pack_start(progress, True, True, 0)
header_event_box.connect(
"button-press-event",
lambda _widget, _event, service=account['service']: self.toggle_account_row(service)
)
self.accounts_list.add(row)
# Store widget references for updating
self.account_widgets[account['service']] = {
'code_label': code_label,
'progress_bar': progress
'revealer': revealer,
'arrow_image': arrow_image
}
self.accounts_list.show_all()
if self.expanded_service and self.expanded_service not in self.account_widgets:
self.expanded_service = None
for service, widgets in self.account_widgets.items():
is_expanded = service == self.expanded_service
widgets['revealer'].set_reveal_child(is_expanded)
widgets['arrow_image'].set_from_icon_name(
"go-down-symbolic" if is_expanded else "go-next-symbolic",
Gtk.IconSize.BUTTON
)
def toggle_account_row(self, service):
"""Expand or collapse the account row for the given service."""
if service == self.expanded_service:
widgets = self.account_widgets.get(service)
if widgets:
widgets['revealer'].set_reveal_child(False)
widgets['arrow_image'].set_from_icon_name("go-next-symbolic", Gtk.IconSize.BUTTON)
self.expanded_service = None
return
if self.expanded_service and self.expanded_service in self.account_widgets:
prev_widgets = self.account_widgets[self.expanded_service]
prev_widgets['revealer'].set_reveal_child(False)
prev_widgets['arrow_image'].set_from_icon_name("go-next-symbolic", Gtk.IconSize.BUTTON)
widgets = self.account_widgets.get(service)
if widgets:
widgets['revealer'].set_reveal_child(True)
widgets['arrow_image'].set_from_icon_name("go-down-symbolic", Gtk.IconSize.BUTTON)
self.expanded_service = service
def on_view_secret(self, widget, account):
"""Handle view secret button click"""
dialog = Gtk.Dialog(
@ -502,6 +555,7 @@ class PyTFAApp:
# Update global timer
self.timer_label.set_label(f"Time remaining: {int(time_remaining)}s")
self.timer_progress.set_fraction(time_remaining / 30.0)
for account in self.accounts:
try:
@ -511,10 +565,6 @@ class PyTFAApp:
# Update the code label if it exists
if account['service'] in self.account_widgets:
self.account_widgets[account['service']]['code_label'].set_label(code)
# Update progress bar
progress = time_remaining / 30.0
self.account_widgets[account['service']]['progress_bar'].set_fraction(progress)
except Exception as e:
print(f"Error generating code for {account['service']}: {e}")
if account['service'] in self.account_widgets:
@ -533,10 +583,12 @@ if __name__ == "__main__":
app.run()
# ENCRYPTED_DATA_BEGIN
# mZoIGF4HqcLjGtXOlcvolGdBQUFBQUJvOXZVNFZ3TjRvUEpsZXMzbTFndk1kNzRRdUtRT0hlM0NkSVVsYW1oOUdiOGF6U0IwN3NMd1k5d2dvM283S3lHNm9oS01oSXR4NVlfbWRqMUFZd1FlbXFQSURKUENMcldNeG9GdVVwbUVsWS0wdGVyR0hCcDduZVNNalhjeGlhZDFpNDZTOEc5elc5WE9pclVuVkhUVVZ1OEtTcXpjZHQzTmZmX3o0cU1SUnM0TWg2dz0=
# MRH8T2C7L4KboZkxOWKlmmdBQUFBQUJvOXZVNFlya1lUcldJRVF0NmYwbEVQWDBvRnZrTEE5NnJlTmszNFBMeVhqdjdNMHZwYmN6RWFidzBwUWtUVy1JTnphZTJRdWYyWTJlLVFlcG9QTlhDUzhhTjhuUU5Xb28xbm1maUdYWmI5V045ZEV4a0J0ZE9UX3FoZkRDYjc1cFlxZlU2UlJUemtDbzhxNElFNFdxYVNJM2xmQTJlQ1RISEMyYU1mYkYweHhrWEtGeG1fdTAwbVNHQmZjeU90SkdianJfbTV6ZGo5NklYTTFrYjBOOEZXRDVrQUE9PQ==
# b/XflIEz4lkcMVph0999TmdBQUFBQUJvOXZVNEV5dHVrMDR1eVNJVHA5bVRwY2FrUjc4aWlNMU9mbGF2a0hnXzFiaElUUDdwZnlLWE5rMU5jalphUmZKdE15VzhkZTNFUW1UZTFXMXJUeDFINm9acVBFb1FIUzdKTG16b1hMbVdqRmZRTGx2ZEgwOUc5RUVuNFIxZmJ0RzlHaUJRZmVDcU1rTFhCaFh3SnlTTkxhTGFFeVlrRWlUOWo1N20tNEhwbkluRDJldTRDRG1WZ0tiN1dlejdacHJheEpSZzRvV2c4cm5mMHEwVWJqakc1SzNSdEE9PQ==
# bdXDoFkfPrR87t3fp49FKmdBQUFBQUJvOXZVNGI0bGJ5bGRieVliNjRwQlBnbnJVZHlnMHhBbHRTZmF5TmJma0JncUktV1UxQUlpZGVhZ0d1UzJzWng4bW1KcTNld0E0QUFkd191TFhZdG1Ic2dzTWI4Z3JDb0FYSzh5Y09jUW5IN2U1bkNjVVlqRVdjeWtWZktzUnkxcHhrT0Y1cGxjVnhyMDRhT2IzRno2MjRKU0M3Zl9vT1JYOVlRLWJsQjNqR1VRY0JsV3I4VFlKUDVEMmVDWmVhX1VPbzdLQVZOUEhIRC1oNWVpSlpwejVheUFmYUE9PQ==
# 0kfRYRErfQmH/3PJyhe7JWdBQUFBQUJvOXZVNHZFSkxIc1NoSExuREdJbDdkS2trZFJjSVRsY0FQbXI3S0dxMFo2VC11MUkwTXFlRWYxTGpfYmVwTXhIR1VscWtrRmhaRUdwRExPN0F5SmJzcHJ0Yy1sTm9sR0ZfSEI5dnBpaEpBSHRUU25FR3dqOHVYLUxIMldrdEt1ZXBWMWdCUUNFZ3NCSlZ0aE5VNW1CUFpPdnk5YkFQSVdvVS1ObVdYYVVqQjdpOHF3aGZTWXBRZ2M0cHY1TXo2eFQxMDBmSEZ3eHdLUVBEcU9XS1ZiaWVKNkY0SEE9PQ==
# HWO6JmCH0Hcyr3d/9nOvI2dBQUFBQUJvOXZVNHYtS3B0NURmOHNDaGQ0bVVFc0ljZTRjQnVtM0hLb3c2X3ZETVFuTXVuMGszQ19vR25YX204eXRIMkFvZjVQWHBoZGhPeENUWjVKZXhtcVNIajBkSTJVUXg2UHdSNURBS0NVaklPaGZLZFYyZ1l2UE1zRGVINlEzZWlVaW1rU0o3R2xZTEpDTUhHY3ZtdUVFRElSUUhZZz09
# Ewlit3P96YVgZ86uVZrX7GdBQUFBQUJvLXdxUkJHS0NJNDN3MUVqaEl0dl9WV3JwSjVxbEVpc2J0RVVvLXh0MEJWRVM5OU5URXVfd1ZzekhTOEtOZ1VlUFNuRnVoeWJhNG1TLWcySmhRWjN0TjAteHR4QlpkR0M5Q0Z5X2hNRG0zUzRtS3Q2NWQwZjBiSmszNEg1S3lJNVBKOC1GS3drc1hyaFU1aVV4MldvQVA5M1RhUHZwbS1ZaTJvdmxTQ1JTNEpoamlMND0=
# Qcd7zQyGRMZ1SYi/F8FwT2dBQUFBQUJvLXdxUjdOSnpSZEJfZFoxOEpPN2k4VERPYWcxc0xWc1FvTDZmSlBrcTJicVplRF9lX2dDSTkxaEI4SjVQUUhwbjM3SnlKTy1GVk1tSUI0dkFFUjVWenBXZEZJYzRXanNhUVNaSUtTaTZCUV9QTzlZV3BSLUZ1cjJxQzJBa0UxMmJPUDZVRUNwSGFyOVRlY245VjJTNkRLSzdZUEVvSEE0MlhiUlBtVE9sTkxnMEpMek5lVnU2NDQwX0lMV0NEdTkxUEI2TnFtWmduVmQ1TUpRRHZmay1QQllza1E9PQ==
# G7ULlxeW09qxA8SlSBKK1GdBQUFBQUJvLXdxUm5JNkFZVWVnclZqeVBPalp5QlFiNmluYkF6RUJfdGtGak12Qk5oVkZIOVZubmJybmN5VXcwLUd2ZVVsUmk1Snp1ZmQ5RXRPTEgxakVqazFaS0pKcHBDNklVRjk4QW43cExKU1JKejNhOUJkaWQzYWhrQVBQZVp0NFpJTjVnanlEYU1SdXZ4dTVoQ3pRZUtjTXhjdlRJcWJfMldpSFUzMU92UU5Lc2ZzMHJqY21aSkljanZaa0d0bnNuNnJQZ0R1d3FmeHREYnlTVTdKa3RRRlRkOFBGMmc9PQ==
# to+Bv630zSX8GD21uLYxImdBQUFBQUJvLXdxUndoRUItNlNnLW1DeEZiLWNRQWN0ME5xTFdiM1cwenBRNGplc1hyOS1UU1dRQWkzU1ozSTlZaTFQalhGWmhIZE05cjJYSFo1R2g0SkV4U0lMRFRFNkYwVGwwWGtiX1J6bUdZWkkxWjNiRzFHNExEWDVaQ1hGQ0VHZzR5akU3R3JQczJjTlRNdy1aYTFWNkF6ZGhfcm1ZSzZrazhjSjg4a256dzJ1aVJxMlNsa0ExMURKUFo3S2NGYnBiYk5RSE9VMWk5cF93Q1AyQ0stSGpEc0EzaDEteHc9PQ==
# +9TvLgxAJ5yWkfHKg28LsmdBQUFBQUJvLXdxUll4VFN0N2ZCR2FMY2E3T2QxMVJyUFd2OHhQMkZ1RGNVVEFTRFhxT0o3djVOb3dzdjctaVVhZTNQQmQ1N3hmc05hZ3Q4YXY1ZHp5Ny1QNmphVGgxUmRYTWFycTd1TEc0a0tVT19FMnJfZHE5S3RyeWNWUnB6SVFfTUc3aWwzV0NFMGhnemhtX2k3OWFoMEktZ1haSlZaS3lLaktzbU1qN1hSbXVWbV9SN3R0amxOR0VPekFHSzVfVkc3ckVrcFZiVTBwcVVraFhEQnRoQnZOUU44NG8tamc9PQ==
# 98ekR6qtjEkNyVWP7USeCWdBQUFBQUJvLXdxUmdFam5zYVBhTjNLSFljUDAtcm1BOGtoU041anlKUlRHZmg3Uk1jTTNBdGlFaThQWVEydFBZTFNnRmExU1BQejNUYzBRWThRQjQ0ZHRrWXV0R25mbFRsdnM5NlNiamJ0WWZfT3l3MkM0YTlZVGpGLUhlX2hXejZQQWZZMG4wbG5pdEpDbjVWSFdnZGQwM0NhWXlZWmlldz09
# DuOwoPOc8aBVRxPRnYH7vWdBQUFBQUJvLXdxUkRfY0NMS3VNTmVzUGF2cG9La01WY3g4eGVGM3ZpQ0ZSS1lyOW9oaVJiUFBSY3dHY1cwQ2l6TEFwcllUNnNCM2pQRFI5TDlUM2EtUkhXZEhidGFZdlVzUzdLTVByWDZ4ZmQ2YjA1VFVzTWtMcXJ0N0FGU3p4ZGFoTm14amM1eTVBUnhoSGI3ZzJZVGJ3cFVGN3BWbDR3MTJabVEtX0l1b3h2cUZyX3dVMXM5aVNXUldyMHgtU2VQamdWUDNrZUE5Wkk3NVIwTlIxbW8yTnNoYkY4YlJ4TXc9PQ==
# GUAmOT2XETLoscmqGUYwdWdBQUFBQUJvLXdxUmphVXQ2V1ppS2w5c1JHTDNDYlkxSG9qdGJ1WWNnbXA3UDcwc3BaMExIRU81WTFwUkcxOG0zTjFFNlFBbzQzc2R2V1d1X0wtNUVoVklSTWxVRUF3Vk5CSDZmd3NZUXZSWXNETHlDSDhGcmtXVlp1Q1lWaTJPZlIyOGx4VXZMUXZOb25IeEJXeWtlc1V2RS1mUTBzTFNlYWNob18wTGFYei1pcmQtVEhfM1FaVDNXbXpSalZGb3JXZUpPVU5FcUl0Zm5mcUdRQ2hoWmNfdkczM1JNcjhiV09MOG9SbWU0cU9WZzVCNUxGSDhQak09
# ENCRYPTED_DATA_END

1
todo Normal file
View file

@ -0,0 +1 @@
convert development to devenv