├── .gitattributes ├── .github ├── .codecov.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md ├── renovate.json └── workflows │ ├── analysis-coverage.yml │ ├── docs-check.yml │ ├── docs.yml │ ├── publish.yml │ └── stale.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .run ├── TalkBot (27).run.xml ├── TalkBot (last).run.xml ├── TalkBotAI (27).run.xml ├── TalkBotAI (last).run.xml ├── ToGif (27).run.xml ├── ToGif (28).run.xml ├── ToGif (last).run.xml ├── aregister_nc_py_api (27).run.xml ├── aregister_nc_py_api (28).run.xml ├── aregister_nc_py_api (last).run.xml ├── register_nc_py_api (27).run.xml ├── register_nc_py_api (28).run.xml └── register_nc_py_api (last).run.xml ├── AUTHORS ├── CHANGELOG.md ├── LICENSE.txt ├── Makefile ├── README.md ├── benchmarks ├── aa_overhead_common.py ├── aa_overhead_dav_download.py ├── aa_overhead_dav_download_stream.py ├── aa_overhead_dav_upload.py ├── aa_overhead_dav_upload_stream.py ├── aa_overhead_ocs.py ├── conf.py └── results │ ├── dav_download_1mb__cache0_iters30__shurik.png │ ├── dav_download_stream_100mb__cache0_iters10__shurik.png │ ├── dav_upload_1mb__cache0_iters30__shurik.png │ ├── dav_upload_stream_100mb__cache0_iters10__shurik.png │ └── ocs_user_get_details__cache0_iters100__shurik.png ├── docs ├── DevSetup.rst ├── FirstSteps.rst ├── Installation.rst ├── Makefile ├── MoreAPIs.rst ├── NextcloudApp.rst ├── NextcloudApp3rdParty.rst ├── NextcloudTalkBot.rst ├── NextcloudTalkBotTransformers.rst ├── NextcloudUiApp.rst ├── Options.rst ├── benchmarks │ └── AppAPI.rst ├── conf.py ├── index.rst ├── reference │ ├── ActivityApp.rst │ ├── Apps.rst │ ├── Calendar.rst │ ├── ExApp.rst │ ├── Exceptions.rst │ ├── Files │ │ ├── Files.rst │ │ ├── Shares.rst │ │ └── index.rst │ ├── LoginFlowV2.rst │ ├── Nextcloud.rst │ ├── Notes.rst │ ├── Session.rst │ ├── Talk.rst │ ├── TalkBot.rst │ ├── Users │ │ ├── Notifications.rst │ │ ├── Users.rst │ │ ├── UsersGroups.rst │ │ ├── UsersStatus.rst │ │ ├── WeatherStatus.rst │ │ └── index.rst │ ├── Webhooks.rst │ └── index.rst └── resources │ ├── css │ ├── dark.css │ ├── light.css │ └── styles.css │ ├── js │ └── script.js │ ├── logo.svg │ └── nc_py_api_logo.png ├── examples ├── COPYING ├── as_app │ ├── talk_bot │ │ ├── Dockerfile │ │ ├── HOW_TO_INSTALL.md │ │ ├── Makefile │ │ ├── appinfo │ │ │ └── info.xml │ │ ├── lib │ │ │ └── main.py │ │ ├── requirements.txt │ │ └── screenshots │ │ │ └── talk_bot.png │ ├── talk_bot_ai │ │ ├── Dockerfile │ │ ├── HOW_TO_INSTALL.md │ │ ├── Makefile │ │ ├── appinfo │ │ │ └── info.xml │ │ ├── lib │ │ │ └── main.py │ │ ├── requirements.txt │ │ └── screenshots │ │ │ └── talk_bot_ai.png │ └── to_gif │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── appinfo │ │ └── info.xml │ │ ├── img │ │ └── icon.svg │ │ ├── lib │ │ └── main.py │ │ └── requirements.txt └── as_client │ └── files │ ├── download.py │ ├── find.py │ ├── listing.py │ └── upload.py ├── img └── icon.svg ├── nc_py_api ├── __init__.py ├── _deffered_error.py ├── _exceptions.py ├── _misc.py ├── _preferences.py ├── _preferences_ex.py ├── _session.py ├── _talk_api.py ├── _theming.py ├── _version.py ├── activity.py ├── apps.py ├── calendar_api.py ├── ex_app │ ├── __init__.py │ ├── defs.py │ ├── integration_fastapi.py │ ├── logger.py │ ├── misc.py │ ├── occ_commands.py │ ├── persist_transformers_cache.py │ ├── providers │ │ ├── __init__.py │ │ ├── providers.py │ │ └── task_processing.py │ ├── ui │ │ ├── __init__.py │ │ ├── files_actions.py │ │ ├── resources.py │ │ ├── settings.py │ │ ├── top_menu.py │ │ └── ui.py │ └── uvicorn_fastapi.py ├── files │ ├── __init__.py │ ├── _files.py │ ├── files.py │ ├── files_async.py │ └── sharing.py ├── loginflow_v2.py ├── nextcloud.py ├── notes.py ├── notifications.py ├── options.py ├── talk.py ├── talk_bot.py ├── user_status.py ├── users.py ├── users_groups.py ├── weather_status.py └── webhooks.py ├── pyproject.toml ├── scripts ├── ci_register.sh └── dev_register.sh └── tests ├── __init__.py ├── _app_security_checks.py ├── _install.py ├── _install_async.py ├── _install_init_handler_models.py ├── _install_only_enabled_handler.py ├── _install_only_enabled_handler_async.py ├── _install_wait.py ├── _talk_bot.py ├── _talk_bot_async.py ├── _tests_at_the_end.py ├── actual_tests ├── __init__.py ├── activity_test.py ├── appcfg_prefs_ex_test.py ├── apps_test.py ├── calendar_test.py ├── conftest.py ├── files_sharing_test.py ├── files_test.py ├── loginflow_v2_test.py ├── logs_test.py ├── misc_test.py ├── nc_app_test.py ├── notes_test.py ├── notifications_test.py ├── occ_commands_test.py ├── options_test.py ├── preferences_test.py ├── talk_bot_test.py ├── talk_test.py ├── taskprocessing_provider_test.py ├── theming_test.py ├── ui_files_actions_test.py ├── ui_resources_test.py ├── ui_settings_test.py ├── ui_top_menu_test.py ├── user_status_test.py ├── users_groups_test.py ├── users_test.py ├── weather_status_test.py └── webhooks_listener_test.py ├── conftest.py └── gfixture_set_env.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Declare files that always have LF line endings on checkout 2 | * text eol=lf 3 | 4 | # Denote all files that are truly binary and should not be modified 5 | *.bin binary 6 | *.heif binary 7 | *.heic binary 8 | *.hif binary 9 | *.avif binary 10 | *.png binary 11 | *.gif binary 12 | *.webp binary 13 | *.tiff binary 14 | *.jpeg binary 15 | *.jpg binary 16 | 17 | # Files to exclude from GitHub Languages statistics 18 | *.h linguist-vendored=true 19 | *.Dockerfile linguist-vendored=true 20 | -------------------------------------------------------------------------------- /.github/.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: 2 | require_changes: true 3 | layout: "diff, files" 4 | 5 | coverage: 6 | status: 7 | project: 8 | default: 9 | target: auto 10 | threshold: 1% 11 | patch: off 12 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Be openness, as well as friendly and didactic in discussions. 4 | 5 | Treat everybody equally, and value their contributions. 6 | 7 | Decisions are made based on technical merit and consensus. 8 | 9 | Try to follow most principles described here: https://nextcloud.com/code-of-conduct/ 10 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to nc_py_api 2 | 3 | Bug fixes, feature additions, tests, documentation and more can be contributed via [issues](https://github.com/cloud-py-api/nc_py_api/issues) and/or [pull requests](https://github.com/cloud_py_api/nc_py_api/pulls). 4 | All contributions are welcome. 5 | 6 | ## Bug fixes, feature additions, etc. 7 | 8 | Please send a pull request to the `main` branch. Feel free to ask questions [via issues](https://github.com/cloud-py-api/nc_py_api/issues) or [discussions](https://github.com/cloud_py_api/nc_py_api/discussions) 9 | 10 | - Fork the nc_py_api repository. 11 | - Create a new branch from `main`. 12 | - Set up development environment as described in a [Setting up dev environment](https://cloud-py-api.github.io/nc_py_api/DevSetup.html) 13 | - Create a pull request to pull the changes from your branch to the nc_py_api `main`. 14 | 15 | ### Guidelines 16 | 17 | - Separate code commits from reformatting commits. 18 | - Where possible, provide tests for any newly added code. 19 | - Follow PEP 8. 20 | - Update CHANGELOG.md as needed or appropriate with your bug fixes, feature additions, and tests. 21 | 22 | ## Security vulnerabilities 23 | 24 | Please see our [security policy](https://github.com/cloud-py-api/nc_py_api/blob/main/.github/SECURITY.md). 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help reproduce and correct the bug 3 | labels: ['Bug', 'Needs Triage'] 4 | 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: > 9 | #### Before submitting a bug, please make sure the issue hasn't been already 10 | addressed by searching through [the past issues](https://github.com/cloud-py-api/nc_py_api/issues). 11 | - type: textarea 12 | attributes: 13 | label: Describe the bug 14 | description: > 15 | A clear and concise description of what the bug is. 16 | validations: 17 | required: true 18 | - type: textarea 19 | attributes: 20 | label: Steps/Code to Reproduce 21 | description: | 22 | Please add a minimal code example that can reproduce the error when running it. Be as succinct as possible. 23 | 24 | If the code is too long, feel free to put it in a public gist and link it in the issue: https://gist.github.com. 25 | 26 | placeholder: | 27 | ``` 28 | Sample code to reproduce the problem 29 | ``` 30 | validations: 31 | required: true 32 | - type: textarea 33 | attributes: 34 | label: Expected Results 35 | description: > 36 | Please paste or describe the expected results. 37 | placeholder: > 38 | Example: No error is thrown. 39 | validations: 40 | required: true 41 | - type: textarea 42 | attributes: 43 | label: Actual Results 44 | description: | 45 | Please paste or describe the results you observe instead of the expected results. If you observe an error, please paste the error message including the **full traceback** of the exception. 46 | placeholder: > 47 | Please paste or specifically describe the actual output or traceback. 48 | validations: 49 | required: true 50 | - type: textarea 51 | attributes: 52 | label: Setup configuration 53 | description: | 54 | Paste or describe the configuration setting, including software versions, to help diagnose the problem more quickly. 55 | placeholder: > 56 | Example: nc_py_api = 0.15.0, nextcloud = 28.0.4, etc. 57 | validations: 58 | required: true 59 | - type: markdown 60 | attributes: 61 | value: > 62 | Thanks for contributing 🎉! 63 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Discussions 4 | url: https://github.com/cloud-py-api/nc_py_api/discussions/new 5 | about: Ask questions and discuss 6 | - name: Blank issue 7 | url: https://github.com/cloud-py-api/nc_py_api/issues/new 8 | about: Please note that Github Discussions should be used in most cases instead 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest a new feature, enhancement to an existing, etc. 3 | labels: ['New Feature', 'Needs Triage'] 4 | 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Describe why it is important and where it will be useful 9 | validations: 10 | required: true 11 | - type: textarea 12 | attributes: 13 | label: Describe your proposed solution 14 | validations: 15 | required: true 16 | - type: textarea 17 | attributes: 18 | label: Describe alternatives you've considered, if relevant 19 | - type: textarea 20 | attributes: 21 | label: Additional context 22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # . 2 | 3 | Changes proposed in this pull request: 4 | 5 | * 6 | * 7 | * 8 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Only the latest non beta release version of `nc_py_api` are currently being supported with security updates. 6 | 7 | ## Reporting a Vulnerability about nc_py_api 8 | 9 | Officially, Nextcloud is not responsible for this project; the project is developing by the community. 10 | 11 | Please use GitHub's Private Vulnerability Reporting feature. 12 | 13 | If there is no response within 48 hours, then create an Issue, 14 | without technical details, to report on the previously reported vulnerability. 15 | 16 | ## Conclusion 17 | 18 | We welcome and appreciate contributions to the security and advancement of open-source projects. 19 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base", 5 | ":disableDependencyDashboard" 6 | ], 7 | "ignorePaths": [ 8 | "**/docker/**", 9 | "**/docs/**", 10 | "**/examples/**", 11 | "**/tests/**" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/docs-check.yml: -------------------------------------------------------------------------------- 1 | name: Docs check 2 | on: 3 | pull_request: 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | build_docs: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-python@v5 15 | with: 16 | python-version: '3.10' 17 | 18 | - name: Install Docs dependencies 19 | run: python3 -m pip install ".[docs]" 20 | 21 | - name: Build and push Docs 22 | run: | 23 | make html SPHINXOPTS="-W" 24 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | on: 3 | push: 4 | branches: [ main ] 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | build_push_docs: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-python@v5 16 | with: 17 | python-version: '3.10' 18 | 19 | - name: Install Docs dependencies 20 | run: python3 -m pip install ".[docs]" 21 | 22 | - name: Build and push Docs 23 | run: | 24 | export CHANGES_DATE=`date -d"@$(git log -1 --pretty=%ct)" --iso-8601=seconds` 25 | make html 26 | git config --global user.name bigcat88 27 | git config --global user.email "bigcat88@users.noreply.github.com" 28 | docroot=`mktemp -d` 29 | rsync -av "docs/_build/html/" "${docroot}/" 30 | pushd "${docroot}" 31 | git init 32 | git remote add deploy "https://token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" 33 | git checkout -b gh-pages 34 | touch .nojekyll 35 | git add . 36 | msg="Docs: commit ${GITHUB_SHA} made on ${CHANGES_DATE} from ${GITHUB_REF} by ${GITHUB_ACTOR}" 37 | git commit -am "${msg}" 38 | git push deploy gh-pages --force 39 | popd 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.TOKEN_DOCS }} 42 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | build_wheels: 9 | if: "contains(github.event.head_commit.message, '[publish]')" 10 | name: Build Wheels 11 | runs-on: ubuntu-22.04 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Setup Python 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: '3.10' 19 | 20 | - name: Preparations 21 | run: python3 -m pip install -U twine build 22 | 23 | - name: Build 24 | run: python3 -m build 25 | 26 | - name: Check 27 | run: twine check dist/* 28 | 29 | - name: Upload 30 | uses: actions/upload-artifact@v4 31 | with: 32 | name: wheels_nc_py_api 33 | path: dist/*.* 34 | if-no-files-found: error 35 | 36 | publish_pypi: 37 | needs: [build_wheels] 38 | if: "contains(github.event.head_commit.message, '[publish]')" 39 | name: Upload to PyPi 40 | runs-on: ubuntu-22.04 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | - name: Get tag 45 | run: | 46 | RELEASE_VERSION=$(sed -n "s/^__version__.*\"\(.*\)\"$/\\1/p" ./nc_py_api/_version.py) 47 | echo RELEASE_TAG="v$RELEASE_VERSION" >> $GITHUB_ENV 48 | CHANGELOG=$(grep -oPz "(?s)##\s\[$RELEASE_VERSION.+?(?=##\s\[|$)" ./CHANGELOG.md | tr -d '\0' | sed /^$/d | sed '1d') 49 | CHANGELOG=$(echo "$CHANGELOG" | sed '$!N;s/^###.*\n#/#/;P;D' | sed '$!N;s/^###.*\n#/#/;P;D' | sed '${/^###/d;}') 50 | echo "CHANGELOG<> $GITHUB_ENV 51 | echo "$CHANGELOG" >> $GITHUB_ENV 52 | echo "EOF" >> $GITHUB_ENV 53 | 54 | - name: Download sdist and wheels 55 | uses: actions/download-artifact@v4 56 | with: 57 | name: wheels_nc_py_api 58 | path: wheelhouse_nc_py_api 59 | 60 | - name: Create release draft 61 | uses: ncipollo/release-action@v1.16.0 62 | with: 63 | name: ${{ env.RELEASE_TAG }} 64 | tag: ${{ env.RELEASE_TAG }} 65 | commit: ${{ github.ref }} 66 | draft: false 67 | body: ${{ env.CHANGELOG }} 68 | token: ${{ secrets.PAT_NC_PY_API }} 69 | 70 | - name: Install twine 71 | run: | 72 | python3 -m pip install twine 73 | python3 -m pip install urllib3==1.26.15 74 | 75 | - name: Publish NcPyApi 76 | run: | 77 | python3 -m twine upload --skip-existing wheelhouse_nc_py_api/*.whl 78 | python3 -m twine upload --skip-existing wheelhouse_nc_py_api/*tar.gz 79 | env: 80 | TWINE_USERNAME: __token__ 81 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 82 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues' 2 | 3 | on: 4 | schedule: 5 | - cron: '0 12 * * 0' # Every Sunday at 12:00 PM UTC 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/stale@v9 12 | with: 13 | days-before-stale: 14 14 | days-before-close: 9 15 | days-before-pr-close: -1 # Never close PR's automatically 16 | any-of-labels: 'question, invalid' 17 | stale-issue-message: 'This issue did not receive an update in the last 4 weeks. 18 | Please take a look again and update the issue with new details, 19 | otherwise it will be automatically closed in 2 weeks. Thank you!' 20 | exempt-all-pr-milestones: true 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Editor directories and files 8 | .idea 9 | .vscode 10 | *.suo 11 | *.ntvs* 12 | *.njsproj 13 | *.sln 14 | 15 | .marginalia 16 | 17 | /js/ 18 | build/ 19 | coverage/ 20 | vendor 21 | .php-cs-fixer.cache 22 | .phpunit.result.cache 23 | 24 | /out 25 | /dev/ 26 | local 27 | tmp 28 | .phpdoc 29 | clover.unit.xml 30 | clover.integration.xml 31 | proto/thrift/gen-* 32 | 33 | # Python Part 34 | 35 | # Byte-compiled / optimized / DLL files 36 | __pycache__/ 37 | *.py[cod] 38 | *$py.class 39 | 40 | # Pycharm settings 41 | .idea/ 42 | 43 | # mypy 44 | .mypy_cache/ 45 | .dmypy.json 46 | dmypy.json 47 | 48 | # Pyre type checker 49 | .pyre/ 50 | 51 | # pytype static type analyzer 52 | .pytype/ 53 | 54 | # Environments 55 | .env 56 | .venv 57 | env/ 58 | venv/ 59 | ENV/ 60 | env.bak/ 61 | venv.bak/ 62 | 63 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 64 | __pypackages__/ 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | .pybuilder/ 74 | target/ 75 | 76 | # Distribution / packaging 77 | .Python 78 | develop-eggs/ 79 | dist/ 80 | downloads/ 81 | eggs/ 82 | .eggs/ 83 | lib64/ 84 | parts/ 85 | sdist/ 86 | var/ 87 | wheels/ 88 | share/python-wheels/ 89 | *.egg-info/ 90 | .installed.cfg 91 | *.egg 92 | MANIFEST 93 | converted/ 94 | 95 | geckodriver.log 96 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | skip: [pylint] 3 | 4 | repos: 5 | - repo: https://github.com/pre-commit/pre-commit-hooks 6 | rev: v5.0.0 7 | hooks: 8 | - id: check-yaml 9 | - id: check-toml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | - id: mixed-line-ending 13 | 14 | - repo: https://github.com/PyCQA/isort 15 | rev: 6.0.1 16 | hooks: 17 | - id: isort 18 | files: >- 19 | (?x)^( 20 | nc_py_api/| 21 | benchmarks/| 22 | examples/| 23 | tests/ 24 | ) 25 | 26 | - repo: https://github.com/psf/black 27 | rev: 25.1.0 28 | hooks: 29 | - id: black 30 | files: >- 31 | (?x)^( 32 | nc_py_api/| 33 | benchmarks/| 34 | examples/| 35 | tests/ 36 | ) 37 | 38 | - repo: https://github.com/tox-dev/pyproject-fmt 39 | rev: v2.5.1 40 | hooks: 41 | - id: pyproject-fmt 42 | 43 | - repo: https://github.com/astral-sh/ruff-pre-commit 44 | rev: v0.11.6 45 | hooks: 46 | - id: ruff 47 | 48 | - repo: local 49 | hooks: 50 | - id: pylint 51 | name: pylint 52 | entry: pylint "nc_py_api/" 53 | language: system 54 | types: [ python ] 55 | pass_filenames: false 56 | args: 57 | [ 58 | "-rn", # Only display messages 59 | "-sn", # Don't display the score 60 | ] 61 | -------------------------------------------------------------------------------- /.run/TalkBot (27).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /.run/TalkBot (last).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /.run/TalkBotAI (27).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /.run/TalkBotAI (last).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /.run/ToGif (27).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /.run/ToGif (28).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /.run/ToGif (last).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /.run/aregister_nc_py_api (27).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 31 | 32 | -------------------------------------------------------------------------------- /.run/aregister_nc_py_api (28).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 31 | 32 | -------------------------------------------------------------------------------- /.run/aregister_nc_py_api (last).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 31 | 32 | -------------------------------------------------------------------------------- /.run/register_nc_py_api (27).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /.run/register_nc_py_api (28).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /.run/register_nc_py_api (last).run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- 2 | people who have submitted patches, reported bugs, added translations, helped 3 | answer newbie questions, and generally made NC-Py-API that much better: 4 | 5 | Andrey Borysenko 6 | Alexander Piskun 7 | CooperGerman 8 | Tobias Tschech 9 | Scott Williams 10 | 11 | 12 | A big THANK YOU goes to: 13 | 14 | All Nextcloud community. 15 | All Python community. 16 | 17 | Guido van Rossum for creating Python. 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-2023, NC_Py_API Developers. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of the NC_Py_API Developers nor the names of any 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := help 2 | 3 | .PHONY: docs 4 | .PHONY: html 5 | docs html: 6 | rm -rf docs/_build 7 | $(MAKE) -C docs html 8 | 9 | .PHONY: links 10 | links: 11 | $(MAKE) -C docs links 12 | 13 | .PHONY: help 14 | help: 15 | @echo "Welcome to NC_PY_API development. Please use \`make \` where is one of" 16 | @echo " docs make HTML docs" 17 | @echo " html make HTML docs" 18 | @echo " " 19 | @echo " Next commands are only for dev environment with nextcloud-docker-dev!" 20 | @echo " They should run from the host you are developing on(with activated venv) and not in the container with Nextcloud!" 21 | @echo " " 22 | @echo " register28 register nc_py_api for Nextcloud 28" 23 | @echo " register29 register nc_py_api for Nextcloud 29" 24 | @echo " register30 register nc_py_api for Nextcloud 30" 25 | @echo " register register nc_py_api for Nextcloud Last" 26 | @echo " " 27 | @echo " tests28 run nc_py_api tests for Nextcloud 28" 28 | @echo " tests29 run nc_py_api tests for Nextcloud 29" 29 | @echo " tests30 run nc_py_api tests for Nextcloud 30" 30 | @echo " tests run nc_py_api tests for Nextcloud Last" 31 | 32 | .PHONY: register28 33 | register28: 34 | /bin/sh scripts/dev_register.sh master-stable28-1 stable28.local 35 | 36 | .PHONY: register29 37 | register29: 38 | /bin/sh scripts/dev_register.sh master-stable29-1 stable29.local 39 | 40 | .PHONY: register30 41 | register30: 42 | /bin/sh scripts/dev_register.sh master-stable30-1 stable30.local 43 | 44 | .PHONY: register 45 | register: 46 | /bin/sh scripts/dev_register.sh master-nextcloud-1 nextcloud.local 47 | 48 | .PHONY: tests28 49 | tests28: 50 | NEXTCLOUD_URL=http://stable28.local python3 -m pytest 51 | 52 | .PHONY: tests29 53 | tests29: 54 | NEXTCLOUD_URL=http://stable29.local python3 -m pytest 55 | 56 | .PHONY: tests30 57 | tests30: 58 | NEXTCLOUD_URL=http://stable30.local python3 -m pytest 59 | 60 | .PHONY: tests 61 | tests: 62 | NEXTCLOUD_URL=http://nextcloud.local python3 -m pytest 63 | -------------------------------------------------------------------------------- /benchmarks/aa_overhead_common.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | from conf import NC_CFGS, init_nc, init_nc_app, init_nc_by_app_pass 6 | 7 | NC_VERSIONS = [] 8 | 9 | 10 | def measure_overhead(measure, title: str): 11 | penguin_means = { 12 | "Password": [], 13 | "AppPassword": [], 14 | "AppAPI": [], 15 | } 16 | 17 | for k, v in NC_CFGS.items(): 18 | NC_VERSIONS.append(init_nc_app(k, v).srv_version["string"]) 19 | 20 | for k, v in NC_CFGS.items(): 21 | nc = init_nc(k, v) 22 | if nc: 23 | result_nc, time_nc = measure(nc) 24 | penguin_means["Password"].append(time_nc) 25 | else: 26 | penguin_means["Password"].append(0) 27 | 28 | nc_ap = init_nc_by_app_pass(k, v) 29 | if nc_ap: 30 | result_nc_ap, time_nc_ap = measure(nc_ap) 31 | penguin_means["AppPassword"].append(time_nc_ap) 32 | else: 33 | penguin_means["AppPassword"].append(0) 34 | 35 | nc_ae = init_nc_app(k, v) 36 | result_nc_ae, time_nc_ae = measure(nc_ae) 37 | penguin_means["AppAPI"].append(time_nc_ae) 38 | 39 | # Uncomment only for functions that return predictable values. 40 | # if result_nc is not None: 41 | # assert result_nc == result_nc_ae 42 | # if result_nc_ap is not None: 43 | # assert result_nc_ap == result_nc_ae 44 | 45 | penguin_means = {k: v for k, v in penguin_means.items() if v} 46 | 47 | x = np.arange(len(NC_VERSIONS)) # the label locations 48 | width = 0.25 # the width of the bars 49 | multiplier = 0 50 | 51 | fig, ax = plt.subplots(layout="constrained") 52 | 53 | for attribute, measurement in penguin_means.items(): 54 | offset = width * multiplier 55 | rects = ax.bar(x + offset, measurement, width, label=attribute) 56 | ax.bar_label(rects, padding=3) 57 | multiplier += 1 58 | 59 | ax.set_ylabel("Time to execute") 60 | ax.set_title(title) 61 | ax.set_xticks(x + width, NC_VERSIONS) 62 | ax.legend(loc="upper left", ncols=3) 63 | values = [] 64 | for v in penguin_means.values(): 65 | values.append(max(v)) 66 | ax.set_ylim(0, max(values) + 0.18 * max(values)) 67 | 68 | 69 | def os_id(): 70 | if sys.platform.lower() == "darwin": 71 | return "macOS" 72 | if sys.platform.lower() == "win32": 73 | return "Windows" 74 | return "Linux" 75 | -------------------------------------------------------------------------------- /benchmarks/aa_overhead_dav_download.py: -------------------------------------------------------------------------------- 1 | from getpass import getuser 2 | from random import randbytes 3 | from time import perf_counter 4 | from typing import Any 5 | 6 | import matplotlib.pyplot as plt 7 | from aa_overhead_common import measure_overhead, os_id 8 | 9 | from nc_py_api import Nextcloud, NextcloudApp 10 | 11 | ITERS = 30 12 | CACHE_SESS = False 13 | 14 | 15 | def measure_download_1mb(nc_obj: Nextcloud | NextcloudApp) -> [Any, float]: 16 | __result = None 17 | small_file_name = "1Mb.bin" 18 | small_file = randbytes(1024 * 1024) 19 | nc_obj.files.upload(small_file_name, small_file) 20 | start_time = perf_counter() 21 | for _ in range(ITERS): 22 | nc_obj.files.download(small_file_name) 23 | nc_obj._session.init_adapter_dav(restart=not CACHE_SESS) # noqa 24 | end_time = perf_counter() 25 | nc_obj.files.delete(small_file_name, not_fail=True) 26 | return __result, round((end_time - start_time) / ITERS, 3) 27 | 28 | 29 | if __name__ == "__main__": 30 | title = f"download 1mb, {ITERS} iters, CACHE={CACHE_SESS} - {os_id()}" 31 | measure_overhead(measure_download_1mb, title) 32 | plt.savefig(f"results/dav_download_1mb__cache{int(CACHE_SESS)}_iters{ITERS}__{getuser()}.png", dpi=200) 33 | -------------------------------------------------------------------------------- /benchmarks/aa_overhead_dav_download_stream.py: -------------------------------------------------------------------------------- 1 | from getpass import getuser 2 | from io import BytesIO 3 | from random import randbytes 4 | from time import perf_counter 5 | from typing import Any 6 | 7 | import matplotlib.pyplot as plt 8 | from aa_overhead_common import measure_overhead, os_id 9 | 10 | from nc_py_api import Nextcloud, NextcloudApp 11 | 12 | ITERS = 10 13 | CACHE_SESS = False 14 | 15 | 16 | def measure_download_100mb(nc_obj: Nextcloud | NextcloudApp) -> [Any, float]: 17 | __result = None 18 | medium_file_name = "100Mb.bin" 19 | medium_file = BytesIO() 20 | medium_file.write(randbytes(100 * 1024 * 1024)) 21 | medium_file.seek(0) 22 | nc_obj.files.upload_stream(medium_file_name, medium_file) 23 | start_time = perf_counter() 24 | for _ in range(ITERS): 25 | medium_file.seek(0) 26 | nc_obj.files.download2stream(medium_file_name, medium_file) 27 | nc_obj._session.init_adapter_dav(restart=not CACHE_SESS) # noqa 28 | end_time = perf_counter() 29 | nc_obj.files.delete(medium_file_name, not_fail=True) 30 | return __result, round((end_time - start_time) / ITERS, 3) 31 | 32 | 33 | if __name__ == "__main__": 34 | title = f"download stream 100mb, {ITERS} iters, CACHE={CACHE_SESS} - {os_id()}" 35 | measure_overhead(measure_download_100mb, title) 36 | plt.savefig(f"results/dav_download_stream_100mb__cache{int(CACHE_SESS)}_iters{ITERS}__{getuser()}.png", dpi=200) 37 | -------------------------------------------------------------------------------- /benchmarks/aa_overhead_dav_upload.py: -------------------------------------------------------------------------------- 1 | from getpass import getuser 2 | from random import randbytes 3 | from time import perf_counter 4 | from typing import Any 5 | 6 | import matplotlib.pyplot as plt 7 | from aa_overhead_common import measure_overhead, os_id 8 | 9 | from nc_py_api import Nextcloud, NextcloudApp 10 | 11 | ITERS = 30 12 | CACHE_SESS = False 13 | 14 | 15 | def measure_upload_1mb(nc_obj: Nextcloud | NextcloudApp) -> [Any, float]: 16 | __result = None 17 | small_file_name = "1Mb.bin" 18 | small_file = randbytes(1024 * 1024) 19 | start_time = perf_counter() 20 | for _ in range(ITERS): 21 | nc_obj.files.upload(small_file_name, small_file) 22 | nc_obj._session.init_adapter_dav(restart=not CACHE_SESS) # noqa 23 | end_time = perf_counter() 24 | nc_obj.files.delete(small_file_name, not_fail=True) 25 | return __result, round((end_time - start_time) / ITERS, 3) 26 | 27 | 28 | if __name__ == "__main__": 29 | title = f"upload 1mb, {ITERS} iters, CACHE={CACHE_SESS} - {os_id()}" 30 | measure_overhead(measure_upload_1mb, title) 31 | plt.savefig(f"results/dav_upload_1mb__cache{int(CACHE_SESS)}_iters{ITERS}__{getuser()}.png", dpi=200) 32 | -------------------------------------------------------------------------------- /benchmarks/aa_overhead_dav_upload_stream.py: -------------------------------------------------------------------------------- 1 | from getpass import getuser 2 | from io import BytesIO 3 | from random import randbytes 4 | from time import perf_counter 5 | from typing import Any 6 | 7 | import matplotlib.pyplot as plt 8 | from aa_overhead_common import measure_overhead, os_id 9 | 10 | from nc_py_api import Nextcloud, NextcloudApp 11 | 12 | ITERS = 10 13 | CACHE_SESS = False 14 | 15 | 16 | def measure_upload_100mb(nc_obj: Nextcloud | NextcloudApp) -> [Any, float]: 17 | __result = None 18 | medium_file_name = "100Mb.bin" 19 | medium_file = BytesIO() 20 | medium_file.write(randbytes(100 * 1024 * 1024)) 21 | start_time = perf_counter() 22 | for _ in range(ITERS): 23 | medium_file.seek(0) 24 | nc_obj.files.upload_stream(medium_file_name, medium_file) 25 | nc_obj._session.init_adapter_dav(restart=not CACHE_SESS) # noqa 26 | end_time = perf_counter() 27 | nc_obj.files.delete(medium_file_name, not_fail=True) 28 | return __result, round((end_time - start_time) / ITERS, 3) 29 | 30 | 31 | if __name__ == "__main__": 32 | title = f"upload stream 100mb, {ITERS} iters, CACHE={CACHE_SESS} - {os_id()}" 33 | measure_overhead(measure_upload_100mb, title) 34 | plt.savefig(f"results/dav_upload_stream_100mb__cache{int(CACHE_SESS)}_iters{ITERS}__{getuser()}.png", dpi=200) 35 | -------------------------------------------------------------------------------- /benchmarks/aa_overhead_ocs.py: -------------------------------------------------------------------------------- 1 | from getpass import getuser 2 | from time import perf_counter 3 | from typing import Any 4 | 5 | import matplotlib.pyplot as plt 6 | from aa_overhead_common import measure_overhead, os_id 7 | 8 | from nc_py_api import Nextcloud, NextcloudApp 9 | 10 | ITERS = 100 11 | CACHE_SESS = False 12 | 13 | 14 | def measure_get_details(nc_obj: Nextcloud | NextcloudApp) -> [Any, float]: 15 | __result = None 16 | start_time = perf_counter() 17 | for _ in range(ITERS): 18 | __result = nc_obj.users.get_details() 19 | nc_obj._session.init_adapter(restart=not CACHE_SESS) # noqa 20 | end_time = perf_counter() 21 | return __result, round((end_time - start_time) / ITERS, 3) 22 | 23 | 24 | if __name__ == "__main__": 25 | title = f"OCS: get_user, {ITERS} iters, CACHE={CACHE_SESS} - {os_id()}" 26 | measure_overhead(measure_get_details, title) 27 | plt.savefig(f"results/ocs_user_get_details__cache{int(CACHE_SESS)}_iters{ITERS}__{getuser()}.png", dpi=200) 28 | -------------------------------------------------------------------------------- /benchmarks/conf.py: -------------------------------------------------------------------------------- 1 | from nc_py_api import Nextcloud, NextcloudApp 2 | 3 | NC_CFGS = { 4 | "http://stable26.local": { 5 | # NC_APP 6 | "secret": "12345", 7 | "app_id": "nc_py_api", 8 | "app_version": "1.0.0", 9 | "user": "admin", 10 | # NC 11 | "nc_auth_user": "admin", 12 | "nc_auth_pass": "admin", 13 | "nc_auth_app_pass": "kFEfH-cqR8T-563tB-8CAjd-96LNj", 14 | }, 15 | "http://stable27.local": { 16 | # NC_APP 17 | "secret": "12345", 18 | "app_id": "nc_py_api", 19 | "app_version": "1.0.0", 20 | "user": "admin", 21 | # NC 22 | "nc_auth_user": "admin", 23 | "nc_auth_pass": "admin", 24 | "nc_auth_app_pass": "Npi8A-LAtWM-WaPm8-CPpEA-jq9od", 25 | }, 26 | "http://nextcloud.local": { 27 | # NC_APP 28 | "secret": "12345", 29 | "app_id": "nc_py_api", 30 | "app_version": "1.0.0", 31 | "user": "admin", 32 | # NC 33 | "nc_auth_user": "admin", 34 | "nc_auth_pass": "admin", 35 | "nc_auth_app_pass": "yEaoa-5Z96a-Z7SHs-44spP-EkC4o", 36 | }, 37 | } 38 | 39 | 40 | def init_nc(url, cfg) -> Nextcloud | None: 41 | if cfg.get("nc_auth_user", "") and cfg.get("nc_auth_pass", ""): 42 | return Nextcloud(nc_auth_user=cfg["nc_auth_user"], nc_auth_pass=cfg["nc_auth_pass"], nextcloud_url=url) 43 | return None 44 | 45 | 46 | def init_nc_by_app_pass(url, cfg) -> Nextcloud | None: 47 | if cfg.get("nc_auth_user", "") and cfg.get("nc_auth_app_pass", ""): 48 | return Nextcloud(nc_auth_user=cfg["nc_auth_user"], nc_auth_pass=cfg["nc_auth_app_pass"], nextcloud_url=url) 49 | return None 50 | 51 | 52 | def init_nc_app(url, cfg) -> NextcloudApp | None: 53 | if cfg.get("secret", "") and cfg.get("app_id", ""): 54 | return NextcloudApp( 55 | app_id=cfg["app_id"], 56 | app_version=cfg["app_version"], 57 | app_secret=cfg["secret"], 58 | nextcloud_url=url, 59 | user=cfg["user"], 60 | ) 61 | return None 62 | -------------------------------------------------------------------------------- /benchmarks/results/dav_download_1mb__cache0_iters30__shurik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-py-api/nc_py_api/35e59c7a8f503f05efb2f01aff731dfed8b80f2e/benchmarks/results/dav_download_1mb__cache0_iters30__shurik.png -------------------------------------------------------------------------------- /benchmarks/results/dav_download_stream_100mb__cache0_iters10__shurik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-py-api/nc_py_api/35e59c7a8f503f05efb2f01aff731dfed8b80f2e/benchmarks/results/dav_download_stream_100mb__cache0_iters10__shurik.png -------------------------------------------------------------------------------- /benchmarks/results/dav_upload_1mb__cache0_iters30__shurik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-py-api/nc_py_api/35e59c7a8f503f05efb2f01aff731dfed8b80f2e/benchmarks/results/dav_upload_1mb__cache0_iters30__shurik.png -------------------------------------------------------------------------------- /benchmarks/results/dav_upload_stream_100mb__cache0_iters10__shurik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-py-api/nc_py_api/35e59c7a8f503f05efb2f01aff731dfed8b80f2e/benchmarks/results/dav_upload_stream_100mb__cache0_iters10__shurik.png -------------------------------------------------------------------------------- /benchmarks/results/ocs_user_get_details__cache0_iters100__shurik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-py-api/nc_py_api/35e59c7a8f503f05efb2f01aff731dfed8b80f2e/benchmarks/results/ocs_user_get_details__cache0_iters100__shurik.png -------------------------------------------------------------------------------- /docs/DevSetup.rst: -------------------------------------------------------------------------------- 1 | Setting up dev environment 2 | ========================== 3 | 4 | We highly recommend to use `Julius Haertl docker setup `_ for Nextcloud dev setup. 5 | 6 | Development of `nc-py-api` can be done on any OS as it is a **pure** Python package. 7 | 8 | .. note:: We suggest to use **PyCharm**, but of course you can use any IDE you like for this like **VS Code** or **Vim**. 9 | 10 | Steps to setup up the development environment: 11 | 12 | #. Setup Nextcloud locally or remotely. 13 | #. Install `AppAPI `_, follow it's steps to register ``deploy daemon`` if needed. 14 | #. Clone the `nc_py_api `_ with :command:`shell`:: 15 | 16 | git clone https://github.com/cloud-py-api/nc_py_api.git 17 | 18 | #. Set current working dir to the root folder of cloned **nc_py_api** with :command:`shell`:: 19 | 20 | cd nc_py_api 21 | 22 | #. Create and activate Virtual Environment with :command:`shell`:: 23 | 24 | python3 -m venv env 25 | 26 | #. Activate Python Virtual Environment with :command:`shell`:: 27 | 28 | source ./env/bin/activate 29 | 30 | #. Update ``pip`` to the last version with :command:`pip`:: 31 | 32 | python3 -m pip install --upgrade pip 33 | 34 | #. Install dev-dependencies with :command:`pip`:: 35 | 36 | pip install ".[dev]" 37 | 38 | #. Install `pre-commit` hooks with :command:`shell`:: 39 | 40 | pre-commit install 41 | 42 | #. Run `nc_py_api` with appropriate PyCharm configuration(``register_nc_py_api(xx)``) or if you are not using PyCharm execute this command in the :command:`shell`:: 43 | 44 | APP_ID=nc_py_api APP_PORT=9009 APP_SECRET=12345 APP_VERSION=1.0.0 NEXTCLOUD_URL=http://nextcloud.local APP_HOST=0.0.0.0 python3 tests/_install.py 45 | 46 | #. In a separate terminal while the ``nc_py_api`` **_install.py** script is running execute this command in the :command:`shell`:: 47 | 48 | make register28 49 | 50 | #. In ``tests/gfixture.py`` edit ``NC_AUTH_USER`` and ``NC_AUTH_PASS``, if they are different in your setup. 51 | #. Run tests to check that everything works with :command:`shell`:: 52 | 53 | python3 -m pytest 54 | 55 | #. Install documentation dependencies if needed with :command:`pip`:: 56 | 57 | pip install ".[docs]" 58 | 59 | #. You can easy build documentation with :command:`shell`:: 60 | 61 | make docs 62 | 63 | #. **Your setup is ready for the developing nc_py_api and Applications based on it. Best of Luck!** 64 | -------------------------------------------------------------------------------- /docs/Installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | First it is always a good idea to update ``pip`` to the latest version with :command:`pip`:: 5 | 6 | python -m pip install --upgrade pip 7 | 8 | To use it as a simple Nextcloud client install it without any additional dependencies with :command:`pip`:: 9 | 10 | python -m pip install --upgrade nc_py_api 11 | 12 | To use in the Nextcloud Application mode install it with additional ``app`` dependencies with :command:`pip`:: 13 | 14 | python -m pip install --upgrade "nc_py_api[app]" 15 | 16 | To use **Calendar API** just add **calendar** dependency, and command will look like this :command:`pip`:: 17 | 18 | python -m pip install --upgrade "nc_py_api[app,calendar]" 19 | 20 | To join the development of **nc_py_api** api install development dependencies with :command:`pip`:: 21 | 22 | python -m pip install --upgrade "nc_py_api[dev]" 23 | 24 | Or install last dev-version from GitHub with :command:`pip`:: 25 | 26 | python -m pip install --upgrade "nc_py_api[dev] @ git+https://github.com/cloud-py-api/nc_py_api" 27 | 28 | Congratulations, the next chapter :ref:`first-steps` awaits. 29 | 30 | .. note:: 31 | If you have any installation or building questions, you can ask them in the discussions or create a issue 32 | and we will do our best to help you. 33 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | PYTHON = python3 6 | SPHINXOPTS = 7 | SPHINXBUILD = sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | LINKCHECKDIR = _build/linkcheck 11 | 12 | # Put it first so that "make" without argument is like "make help". 13 | help: 14 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 15 | 16 | .PHONY: help Makefile 17 | 18 | github: 19 | @make html 20 | @cp -a build/html/. ./docs 21 | 22 | # Catch-all target: route all unknown targets to Sphinx using the new 23 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 24 | %: Makefile 25 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 26 | 27 | .PHONY: links 28 | links: 29 | @$(SPHINXBUILD) -b linkcheck "$(SOURCEDIR)" "$(LINKCHECKDIR)" $(ALLSPHINXOPTS) 30 | -------------------------------------------------------------------------------- /docs/MoreAPIs.rst: -------------------------------------------------------------------------------- 1 | .. _more-apis: 2 | 3 | More APIs 4 | ========= 5 | 6 | All provided APIs can be accessed using instance of `Nextcloud` or `NextcloudApp` class. 7 | 8 | For example, let's print all Talk conversations for the current user: 9 | 10 | .. code-block:: python 11 | 12 | from nc_py_api import Nextcloud 13 | 14 | 15 | nc = Nextcloud(nextcloud_url="http://nextcloud.local", nc_auth_user="admin", nc_auth_pass="admin") 16 | all_conversations = nc.talk.get_user_conversations() 17 | for conversation in all_conversations: 18 | print(conversation.conversation_type.name + ": " + conversation.display_name) 19 | 20 | Or let's find only your favorite conversations and send them a sweet message containing only heart emoticons: "❤️❤️❤️" 21 | 22 | 23 | .. code-block:: python 24 | 25 | from nc_py_api import Nextcloud 26 | 27 | 28 | nc = Nextcloud(nextcloud_url="http://nextcloud.local", nc_auth_user="admin", nc_auth_pass="admin") 29 | all_conversations = nc.talk.get_user_conversations() 30 | for conversation in all_conversations: 31 | if conversation.is_favorite: 32 | print(conversation.conversation_type.name + ": " + conversation.display_name) 33 | nc.talk.send_message("❤️❤️❤️️", conversation) 34 | -------------------------------------------------------------------------------- /docs/NextcloudApp3rdParty.rst: -------------------------------------------------------------------------------- 1 | Packaging 3rd party software as a Nextcloud Application 2 | ======================================================= 3 | 4 | This chapter explains how you can package any 3rd party software to be compatible with Nextcloud. 5 | You should already be familiar with :doc:`NextcloudApp` before reading this part. 6 | 7 | You should also have a bit of knowledge about classic PHP Nextcloud apps and `how to develop them `_. 8 | 9 | Architecture 10 | ------------ 11 | 12 | The packaged ExApp will contain two pieces of software: 13 | 14 | #. The ExApp itself which is talking to Nextcloud directly and responsible for the whole lifecycle. 15 | #. The 3rd party software you want to package. 16 | #. Frontend code which will be loaded by Nextcloud to display your iframe. 17 | 18 | Due to current restrictions of ExApps they can only utilize a single port, which means all requests for the 3rd part software have to be proxied through the ExApp. 19 | This will be improved in future released, by allowing multiple ports, so that no proxying is necessary anymore. 20 | 21 | Everything will be packaged into a single Docker image which will be used for deployments. 22 | Therefore it is an advantage if the 3rd party software is already able to run inside a Docker container and has a public Docker image available. 23 | 24 | Steps 25 | ------------------ 26 | 27 | Creating the ExApp 28 | ^^^^^^^^^^^^^^^^^^ 29 | 30 | Please follow the instructions in :doc:`NextcloudApp` and then return here. 31 | 32 | Adding the frontend 33 | ^^^^^^^^^^^^^^^^^^^ 34 | 35 | To be able to access the 3rd party software via the browser it is necessary to embed an iframe into Nextcloud. 36 | The frontend has to be added in the same way how you add it in a classic PHP app. 37 | The iframe ``src`` needs to point to ``/apps/app_api/proxy/APP_ID``, but it is necessary to use the ``generateUrl`` method to ensure the path will also work with Nextcloud instances hosted at a sub-path. 38 | If you require some features like clipboard read/write you need to allow them for the iframe using the ``allow`` attribute. 39 | 40 | To now show the frontend inside Nextcloud add the following to your enabled handler: 41 | 42 | .. code-block:: python 43 | 44 | def enabled_handler(enabled: bool, nc: NextcloudApp) -> str: 45 | if enabled: 46 | nc.ui.resources.set_script("top_menu", "APP_ID", "js/APP_ID-main") 47 | nc.ui.top_menu.register("APP_ID", "App Name", "img/app.svg") 48 | else: 49 | nc.ui.resources.delete_script("top_menu", "APP_ID", "js/APP_ID-main") 50 | nc.ui.top_menu.unregister("APP_ID") 51 | return "" 52 | 53 | Proxying the requests 54 | ^^^^^^^^^^^^^^^^^^^^^ 55 | 56 | For proxying the requests to the 3rd party software you need to register a new route: 57 | 58 | .. code-block:: python 59 | 60 | @APP.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"]) 61 | async def proxy_requests(request: Request, path: str): 62 | pass 63 | 64 | This route should have the lowest priority of all your routes, as it catches all requests that didn't match any previous route. 65 | 66 | In this request handler you need to send a new HTTP request to the 3rd party software and copy all incoming parameters like sub-path, query parameters, body and headers. 67 | When returning the response including body, headers and status code, make sure to add or override the CSP and CORS headers if necessary. 68 | 69 | Adjusting the Dockerfile 70 | ^^^^^^^^^^^^^^^^^^^^^^^^ 71 | 72 | The Dockerfile should be based on the 3rd party software you want to package. 73 | In case a Docker image is already available you should use that, otherwise you need to first create your own Docker image (it doesn't have to be a separate image, it can just be a stage in the Dockerfile for your ExApp). 74 | 75 | The 3rd party software needs to be adapted to be able to handle the proxied requests and generated correct URLs in the frontend. 76 | Depending on how the software works this might only be a config option you need to set or you need to modify the source code within the Docker image (and potentially rebuild the software afterwards). 77 | The root path of the software will be hosted at ``/index.php/apps/app_api/proxy/APP_ID`` which is the same location that was configured in the iframe ``src``. 78 | 79 | After these steps you can just continue with the normal ExApp Dockerfile steps of installing the dependencies and copying the source code. 80 | Be aware that you will need to install Python manually in your image in case the Docker image you used so far doesn't include it. 81 | 82 | At the end you will have to add a custom entrypoint script that runs the ExApp and the 3rd party software side-by-side to allow them to live in the same container. 83 | -------------------------------------------------------------------------------- /docs/NextcloudTalkBot.rst: -------------------------------------------------------------------------------- 1 | Nextcloud Talk Bot API in Applications 2 | ====================================== 3 | 4 | The AppAPI is an excellent choice for developing and deploying bots for Nextcloud Talk. 5 | 6 | Bots for Nextcloud Talk, in essence, don't differ significantly from regular external applications. 7 | The functionality of an external application can include just the bot or provide additional functionalities as well. 8 | 9 | Let's consider a simple example of how to transform the `skeleton` of an external application into a Nextcloud Talk bot. 10 | 11 | The first step is to add the **TALK_BOT** and **TALK** scopes to your `info.xml` file: 12 | 13 | .. code-block:: xml 14 | 15 | 16 | TALK 17 | TALK_BOT 18 | 19 | 20 | The TALK_BOT scope enables your application to register the bot within the Nextcloud system, while the TALK scope permits access to Talk's endpoints. 21 | 22 | In the global **enabled_handler**, you should include a call to your bot's enabled_handler, as shown in the bot example: 23 | 24 | .. code-block:: python 25 | 26 | def enabled_handler(enabled: bool, nc: NextcloudApp) -> str: 27 | try: 28 | CURRENCY_BOT.enabled_handler(enabled, nc) # registering/unregistering the bot's stuff. 29 | except Exception as e: 30 | return str(e) 31 | return "" 32 | 33 | Afterward, using FastAPI, you can define endpoints that will be invoked by Talk: 34 | 35 | .. code-block:: python 36 | 37 | @APP.post("/currency_talk_bot") 38 | async def currency_talk_bot( 39 | message: Annotated[talk_bot.TalkBotMessage, Depends(atalk_bot_msg)], 40 | background_tasks: BackgroundTasks, 41 | ): 42 | return Response() 43 | 44 | .. note:: 45 | You must include to each endpoint your bot provides the **Depends(nc_app)**. 46 | 47 | Depending on **nc_app** serves as an automatic authentication handler for messages from the cloud. 48 | 49 | **message: Annotated[talk_bot.TalkBotMessage, Depends(talk_bot_app)]** - returns the received message from Nextcloud upon successful authentication. 50 | 51 | Additionally, if your bot can provide quick and fixed execution times, you may not need to create background tasks. 52 | However, in most cases, it's recommended to segregate functionality and perform operations in the background, while promptly returning an empty response to Nextcloud. 53 | 54 | An application can implement multiple bots concurrently, but each bot's endpoints must be unique. 55 | 56 | All authentication is facilitated by the Python SDK, ensuring you needn't concern yourself with anything other than writing useful functionality. 57 | 58 | Currently, bots have access only to three methods: 59 | 60 | * :py:meth:`~nc_py_api.talk_bot.TalkBot.send_message` 61 | * :py:meth:`~nc_py_api.talk_bot.TalkBot.react_to_message` 62 | * :py:meth:`~nc_py_api.talk_bot.TalkBot.delete_reaction` 63 | 64 | .. note:: **The usage of system application functionality for user impersonation in bot development is strongly discouraged**. 65 | All bot messages should only be sent using the ``send_message`` method! 66 | 67 | All other rules and algorithms remain consistent with regular external applications. 68 | 69 | Full source of bot example can be found here: 70 | `TalkBot `_ 71 | 72 | Wishing success with your Nextcloud bot integration! May the force be with you! 73 | -------------------------------------------------------------------------------- /docs/NextcloudTalkBotTransformers.rst: -------------------------------------------------------------------------------- 1 | Talk Bot App with Transformers 2 | ============================== 3 | 4 | `Transformers provides thousands of pretrained models to perform tasks on different modalities such as text, vision, and audio.` 5 | 6 | In this article, we'll demonstrate how straightforward it is to leverage the extensive capabilities 7 | of the `Transformers `_ library in your Nextcloud application. 8 | 9 | Specifically, we'll cover: 10 | 11 | * Setting the models cache path for the Transformers library 12 | * Downloading AI models during the application initialization step 13 | * Receiving messages from Nextcloud Talk Chat and sending them to a language model 14 | * Sending the language model's reply back to the Nextcloud Talk Chat 15 | 16 | Packaging the Application 17 | """"""""""""""""""""""""" 18 | 19 | Firstly, let's touch upon the somewhat mundane topic of application packaging. 20 | 21 | For this example, we've chosen Debian as the base image because it simplifies the installation of required Python packages. 22 | 23 | .. code-block:: 24 | 25 | FROM python:3.11-bookworm 26 | 27 | 28 | While Alpine might be a better choice in some situations, that's not the focus of this example. 29 | 30 | .. note:: The selection of a suitable base image for an application is a complex topic that merits its own in-depth discussion. 31 | 32 | Requirements 33 | """""""""""" 34 | 35 | .. literalinclude:: ../examples/as_app/talk_bot_ai/requirements.txt 36 | 37 | We opt for the latest version of the Transformers library. 38 | Because the example was developed on a Mac, we ended up using Torchvision. 39 | 40 | `You're free to use TensorFlow instead of PyTorch.` 41 | 42 | Next, we integrate the latest version of `nc_py_api` to minimize code redundancy and focus on the application's logic. 43 | 44 | Prepare of Language Model 45 | """"""""""""""""""""""""" 46 | 47 | .. code-block:: 48 | 49 | MODEL_NAME = "MBZUAI/LaMini-Flan-T5-77M" 50 | 51 | We specify the model name globally so that we can easily change the model name if necessary. 52 | 53 | **When Should We Download the Language Model?** 54 | 55 | To make process of initializing applications more robust, separate logic was introduced, with an ``/init`` endpoint. 56 | 57 | This library also provides an additional functionality over this endpoint for easy downloading of models from the `huggingface `_. 58 | 59 | .. code-block:: 60 | 61 | @asynccontextmanager 62 | async def lifespan(_app: FastAPI): 63 | set_handlers(APP, enabled_handler, models_to_fetch={MODEL_NAME:{}}) 64 | yield 65 | 66 | This will automatically download models specified in ``models_to_fetch`` parameter to the application persistent storage. 67 | 68 | If you want write your own logic, you can always pass your own defined ``init_handler`` callback to ``set_handlers``. 69 | 70 | Working with Language Models 71 | """""""""""""""""""""""""""" 72 | 73 | Finally, we arrive at the core aspect of the application, where we interact with the **Language Model**: 74 | 75 | .. code-block:: 76 | 77 | def ai_talk_bot_process_request(message: talk_bot.TalkBotMessage): 78 | # Process only messages started with "@ai" 79 | r = re.search(r"@ai\s(.*)", message.object_content["message"], re.IGNORECASE) 80 | if r is None: 81 | return 82 | model = pipeline( 83 | "text2text-generation", 84 | model=snapshot_download(MODEL_NAME, local_files_only=True, cache_dir=persistent_storage()), 85 | ) 86 | # Pass all text after "@ai" we to the Language model. 87 | response_text = model(r.group(1), max_length=64, do_sample=True)[0]["generated_text"] 88 | AI_BOT.send_message(response_text, message) 89 | 90 | 91 | Simply put, AI logic is a few lines of code when using Transformers, which is incredibly efficient and cool. 92 | 93 | Messages from the AI model are then sent back to Talk Chat as you would expect from a typical chatbot. 94 | 95 | `Full source code is here `_ 96 | 97 | That's it for now! Stay tuned—this is merely the start of an exciting journey into the integration of AI and chat functionality in Nextcloud. 98 | -------------------------------------------------------------------------------- /docs/NextcloudUiApp.rst: -------------------------------------------------------------------------------- 1 | Writing Nextcloud App with UI 2 | ============================= 3 | 4 | .. note:: It is advisable to have experience writing PHP applications for Nextcloud, 5 | since the UI of applications not written in PHP is exactly the same. 6 | 7 | One of the most interesting features is the ability to register a page in the Nextcloud Top Menu. 8 | 9 | Full source of UI example can be found here: 10 | `UiExample `_ 11 | 12 | Here we will simply describe in detail what happens in the example. 13 | 14 | .. code-block:: python 15 | 16 | if enabled: 17 | nc.ui.resources.set_initial_state( 18 | "top_menu", "first_menu", "ui_example_state", {"initial_value": "test init value"} 19 | ) 20 | nc.ui.resources.set_script("top_menu", "first_menu", "js/ui_example-main") 21 | nc.ui.top_menu.register("first_menu", "UI example", "img/icon.svg") 22 | if nc.srv_version["major"] >= 29: 23 | nc.ui.settings.register_form(SETTINGS_EXAMPLE) 24 | 25 | **set_initial_state** is analogue of PHP ``OCP\AppFramework\Services\IInitialState::provideInitialState`` 26 | 27 | **set_script** is analogue of PHP ``Util::addScript`` 28 | 29 | There is also **set_style** (``Util::addStyle``) that can be used for CSS files and works the same way as **set_script**. 30 | 31 | Starting with Nextcloud **29** AppAPI supports declaring Settings UI, with very simple and robust API. 32 | 33 | Settings values you declare will be saved to ``preferences_ex`` or ``appconfig_ex`` tables and can be retrieved using 34 | :py:class:`nc_py_api._preferences_ex.PreferencesExAPI` or :py:class:`nc_py_api._preferences_ex.AppConfigExAPI` APIs. 35 | 36 | Backend 37 | ------- 38 | 39 | .. code-block:: python 40 | 41 | class Button1Format(BaseModel): 42 | initial_value: str 43 | 44 | 45 | @APP.post("/verify_initial_value") 46 | async def verify_initial_value( 47 | input1: Button1Format, 48 | ): 49 | print("Old value: ", input1.initial_value) 50 | return responses.JSONResponse(content={"initial_value": str(random.randint(0, 100))}, status_code=200) 51 | 52 | 53 | class FileInfo(BaseModel): 54 | getlastmodified: str 55 | getetag: str 56 | getcontenttype: str 57 | fileid: int 58 | permissions: str 59 | size: int 60 | getcontentlength: int 61 | favorite: int 62 | 63 | 64 | @APP.post("/nextcloud_file") 65 | async def nextcloud_file( 66 | args: dict, 67 | ): 68 | print(args["file_info"]) 69 | return responses.Response() 70 | 71 | Here is defining two endpoints for test purposes. 72 | 73 | The first is to get the current initial state of the page when the button is clicked. 74 | 75 | Second one is receiving a default information about file in the Nextcloud. 76 | 77 | Frontend 78 | -------- 79 | 80 | The frontend part is the same as the default Nextcloud apps, with slightly different URL generation since all requests are sent through the AppAPI. 81 | 82 | JS Frontend part is covered by AppAPI documentation: ``to_do`` 83 | 84 | Important notes 85 | --------------- 86 | 87 | We do not call ``top_menu.unregister`` or ``resources.delete_script`` as during uninstalling of application **AppAPI** will automatically remove this. 88 | 89 | .. note:: Recommended way is to manually clean all stuff and probably if it was not an example, we would call all unregister and cleanup stuff during ``disabling``. 90 | 91 | 92 | All resources of ExApp should be avalaible and mounted to webserver(**FastAPI** + **uvicorn** are used by default for this). 93 | 94 | .. note:: This is in case you have custom folders that Nextcloud instance should have access. 95 | 96 | 97 | *P.S.: If you are missing some required stuff for the UI part, please inform us, and we will consider adding it.* 98 | -------------------------------------------------------------------------------- /docs/Options.rst: -------------------------------------------------------------------------------- 1 | .. _options: 2 | 3 | Options 4 | ------- 5 | 6 | .. automodule:: nc_py_api.options 7 | :members: 8 | 9 | Usage examples 10 | ^^^^^^^^^^^^^^ 11 | 12 | Using kwargs 13 | """""""""""" 14 | 15 | .. note:: The names of the options if you wish to specify it in ``kwargs`` is **lowercase**. 16 | 17 | .. code-block:: python 18 | 19 | nc_client = Nextcloud(xdebug_session="PHPSTORM", npa_nc_cert=False) 20 | 21 | Will set `XDEBUG_SESSION` to ``"PHPSTORM"`` and `NPA_NC_CERT` to ``False``. 22 | 23 | With .env 24 | """"""""" 25 | 26 | Place **.env** file in your project's directory, and it will be automatically loaded using `dotenv `_ 27 | 28 | `Loading occurs only once, when "nc_py_api" is imported into the Python interpreter.` 29 | 30 | Modifying at module level 31 | """"""""""""""""""""""""" 32 | 33 | Import **nc_py_api** and modify options by setting values you need directly in **nc_py_api.options**, 34 | and all newly created classes will respect that. 35 | 36 | .. code-block:: python 37 | 38 | import nc_py_api 39 | 40 | nc_py_api.options.NPA_TIMEOUT = None 41 | nc_py_api.options.NPA_TIMEOUT_DAV = None 42 | 43 | .. note:: In case you debugging PHP code it is always a good idea to set **Timeouts** to ``None``. 44 | -------------------------------------------------------------------------------- /docs/benchmarks/AppAPI.rst: -------------------------------------------------------------------------------- 1 | AppAPI Benchmarks 2 | ================= 3 | 4 | In the current implementation, applications written and using the AppAPI 5 | so far in most cases will be authenticated at the beginning of each action. 6 | 7 | It is important to note that the AppAPI authentication type is currently the fastest among available options. 8 | Compared to traditional username/password authentication and app password authentication, 9 | both of which are considerably slower, the AppAPI provides a significant advantage in terms of speed. 10 | 11 | In summary, the AppAPI authentication offers fast and robust access to user data. 12 | 13 | Overall, the AppAPI authentication proves to be a reliable and effective method for application authentication. 14 | 15 | .. _appapi-bench-results: 16 | 17 | Detailed Benchmark Results 18 | -------------------------- 19 | 20 | Tests on MacOS (M2 CPU) are run when NC is in Docker and `nc_py_api` is in the host. 21 | 22 | Tests are run with session cache enabled and disabled to see the difference in authentication speed. 23 | 24 | | All benchmarks are run one after the other in the single thread. 25 | | Size of chunk for file stream operations = **4MB** 26 | 27 | nc-py-api version = **0.2.0** 28 | 29 | 'ocs/v1.php/cloud/USERID' endpoint 30 | ---------------------------------- 31 | 32 | .. image:: ../../benchmarks/results/ocs_user_get_details__cache0_iters100__shurik.png 33 | 34 | Downloading a 1 MB file 35 | ----------------------- 36 | 37 | .. image:: ../../benchmarks/results/dav_download_1mb__cache0_iters30__shurik.png 38 | 39 | Uploading a 1 Mb file 40 | --------------------- 41 | 42 | .. image:: ../../benchmarks/results/dav_upload_1mb__cache0_iters30__shurik.png 43 | 44 | Downloading of a 100 Mb file to the memory BytesIO python object 45 | ---------------------------------------------------------------- 46 | 47 | .. image:: ../../benchmarks/results/dav_download_stream_100mb__cache0_iters10__shurik.png 48 | 49 | Chunked uploading of a 100 Mb file from the BytesIO python object 50 | ----------------------------------------------------------------- 51 | 52 | .. image:: ../../benchmarks/results/dav_upload_stream_100mb__cache0_iters10__shurik.png 53 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from datetime import datetime 4 | 5 | import sphinx_rtd_theme 6 | 7 | dir_path = os.path.dirname(os.path.realpath(__file__)) 8 | sys.path.insert(0, os.path.abspath(dir_path + "/_ext")) 9 | sys.path.insert(0, os.path.abspath("../.")) 10 | 11 | import nc_py_api # noqa 12 | 13 | now = datetime.now() 14 | 15 | extensions = [ 16 | "sphinx.ext.autodoc", 17 | "sphinx.ext.extlinks", 18 | "sphinx.ext.intersphinx", 19 | "sphinx.ext.viewcode", 20 | "sphinx_copybutton", 21 | "sphinx_inline_tabs", 22 | "sphinx_issues", 23 | "sphinx_rtd_theme", 24 | "sphinxcontrib.autodoc_pydantic", 25 | ] 26 | 27 | intersphinx_mapping = { 28 | "python": ("https://docs.python.org/3", None), 29 | # "sqlalchemy": ("https://docs.sqlalchemy.org/en/20/", None), 30 | # "redis": ("https://redis-py.readthedocs.io/en/stable/", None), 31 | } 32 | 33 | autodoc_pydantic_model_show_json = False 34 | 35 | # General information about the project. 36 | project = "NcPyApi" 37 | copyright = str(now.year) + f" {project} Authors" # noqa 38 | 39 | # The version info for the project you're documenting, acts as replacement for 40 | # |version| and |release|, also used in various other places throughout the 41 | # built documents. 42 | # 43 | # The short X.Y version. 44 | version = nc_py_api.__version__ 45 | release = version 46 | 47 | html_theme = "sphinx_rtd_theme" 48 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 49 | 50 | html_logo = "resources/logo.svg" 51 | 52 | html_theme_options = { 53 | "display_version": True, 54 | "logo_only": True, 55 | } 56 | 57 | # If true, `todos` produce output. Else they produce nothing. 58 | todo_include_todos = False 59 | 60 | # The name of the Pygments (syntax highlighting) style to use. 61 | pygments_style = "sphinx" 62 | 63 | # If true, Sphinx will warn about all references where the target cannot be found. 64 | # Default is False. You can activate this mode temporarily using the -n command-line 65 | # switch. 66 | nitpicky = True 67 | nitpick_ignore_regex = [ 68 | (r"py:class", r"starlette\.requests\.Request"), 69 | (r"py:class", r"starlette\.requests\.HTTPConnection"), 70 | (r"py:class", r"ComputedFieldInfo"), 71 | (r"py:class", r"FieldInfo"), 72 | (r"py:class", r"ConfigDict"), 73 | (r"py:.*", r"httpx.*"), 74 | ] 75 | 76 | autodoc_member_order = "bysource" 77 | 78 | # Add any paths that contain custom static files (such as style sheets) here, 79 | # relative to this directory. They are copied after the builtin static files, 80 | # so a file named "default.css" will overwrite the builtin "default.css". 81 | html_static_path = ["resources"] 82 | 83 | 84 | def setup(app): 85 | app.add_js_file("js/script.js") 86 | app.add_css_file("css/styles.css") 87 | app.add_css_file("css/dark.css") 88 | app.add_css_file("css/light.css") 89 | 90 | 91 | issues_github_path = "cloud-py-api/nc_py_api" 92 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | NC_Py_API documentation 2 | ======================= 3 | 4 | | *A framework, library, or your best Python tool to work with Nextcloud.* 5 | | It supports two modes: as a client library or as a framework for developing next-generation applications. 6 | 7 | The key features are: 8 | * **Fast**: High performance, and as low-latency as possible. 9 | * **Intuitive**: Fast to code, easy to use. 10 | * **Reliable**: Minimum number of incompatible changes. 11 | * **Robust**: All code is covered with tests as much as possible. 12 | * **Easy**: Designed to be easy to use with excellent documentation. 13 | 14 | Overview 15 | ======== 16 | 17 | The main goal of this project is to provide a fast and easy way to develop and deploy Python applications for Nextcloud. 18 | The option to use it as a client library is a beneficial side effect. 19 | The code is unified, and the codebase for both modes is the same. Tests are carried out for both modes. 20 | 21 | If you have any questions or corrections regarding the documentation, 22 | we would be glad to address them in discussions, incorporate corrections through pull requests, 23 | and handle complex problems through issues. 24 | 25 | Have a great time with Python and Nextcloud! 26 | 27 | .. toctree:: 28 | :maxdepth: 1 29 | 30 | Installation 31 | FirstSteps 32 | MoreAPIs 33 | NextcloudApp 34 | NextcloudApp3rdParty 35 | NextcloudTalkBot 36 | NextcloudTalkBotTransformers 37 | NextcloudUiApp 38 | Options 39 | reference/index.rst 40 | DevSetup 41 | benchmarks/AppAPI.rst 42 | 43 | Indices and tables 44 | ================== 45 | 46 | * :ref:`genindex` 47 | * :ref:`modindex` 48 | -------------------------------------------------------------------------------- /docs/reference/ActivityApp.rst: -------------------------------------------------------------------------------- 1 | Activity App 2 | ------------ 3 | 4 | .. autoclass:: nc_py_api.activity.ActivityFilter 5 | :members: 6 | 7 | .. autoclass:: nc_py_api.activity.Activity 8 | :members: 9 | 10 | .. autoclass:: nc_py_api.activity._ActivityAPI 11 | :members: 12 | -------------------------------------------------------------------------------- /docs/reference/Apps.rst: -------------------------------------------------------------------------------- 1 | Applications Management 2 | ----------------------- 3 | 4 | .. autoclass:: nc_py_api.apps._AppsAPI 5 | :members: 6 | 7 | .. autoclass:: nc_py_api.apps.ExAppInfo 8 | :members: 9 | 10 | Preferences 11 | ^^^^^^^^^^^ 12 | 13 | .. autoclass:: nc_py_api._preferences_ex.CfgRecord 14 | :members: 15 | :undoc-members: 16 | 17 | User specific 18 | """"""""""""" 19 | 20 | .. autoclass:: nc_py_api._preferences.PreferencesAPI 21 | :members: 22 | 23 | .. autoclass:: nc_py_api._preferences_ex.PreferencesExAPI 24 | :members: 25 | :inherited-members: 26 | 27 | Non-user specific 28 | """"""""""""""""" 29 | 30 | .. autoclass:: nc_py_api._preferences_ex.AppConfigExAPI 31 | :members: 32 | :inherited-members: 33 | -------------------------------------------------------------------------------- /docs/reference/Calendar.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: nc_py_api.calendar_api 2 | 3 | Calendar API 4 | ============ 5 | 6 | .. note:: To make this API work you should install **nc_py_api** with **calendar** extra dependency. 7 | 8 | .. code-block:: python 9 | 10 | principal = nc.cal.principal() 11 | calendars = principal.calendars() # get list of calendars 12 | 13 | ``nc.cal`` is usual ``caldav.DAVClient`` object with the same API. 14 | 15 | Documentation for ``caldav`` can be found here: `CalDAV <"https://caldav.readthedocs.io/en/latest">`_ 16 | 17 | .. class:: _CalendarAPI 18 | 19 | Class that encapsulates ``caldav.DAVClient``. Avalaible as **cal** in the Nextcloud class. 20 | 21 | .. note:: You should not call ``close`` or ``request`` methods of CalendarAPI, they will be removed somewhere 22 | in the future when ``caldav.DAVClient`` will be rewritten(API compatability will remains). 23 | -------------------------------------------------------------------------------- /docs/reference/ExApp.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: nc_py_api.ex_app 2 | 3 | External Application 4 | ==================== 5 | 6 | Constants 7 | --------- 8 | 9 | .. autoclass:: LogLvl 10 | :members: 11 | 12 | Special functions 13 | ----------------- 14 | 15 | .. autofunction:: persistent_storage 16 | 17 | .. autofunction:: verify_version 18 | 19 | User Interface(UI) 20 | ------------------ 21 | 22 | UI methods should be accessed with the help of :class:`~nc_py_api.nextcloud.NextcloudApp` 23 | 24 | .. code-block:: python 25 | 26 | # this is an example, in most cases you will get `NextcloudApp` class instance as input param. 27 | nc = NextcloudApp() 28 | nc.ex_app.ui.files_dropdown_menu.register(...) 29 | 30 | .. autoclass:: nc_py_api.ex_app.ui.ui.UiApi 31 | :members: 32 | 33 | .. automodule:: nc_py_api.ex_app.ui.files_actions 34 | :members: 35 | 36 | .. autoclass:: nc_py_api.ex_app.ui.files_actions._UiFilesActionsAPI 37 | :members: 38 | 39 | .. automodule:: nc_py_api.ex_app.ui.top_menu 40 | :members: 41 | 42 | .. autoclass:: nc_py_api.ex_app.ui.top_menu._UiTopMenuAPI 43 | :members: 44 | 45 | .. autoclass:: nc_py_api.ex_app.ui.resources._UiResources 46 | :members: 47 | 48 | .. autoclass:: nc_py_api.ex_app.ui.resources.UiInitState 49 | :members: 50 | 51 | .. autoclass:: nc_py_api.ex_app.ui.resources.UiScript 52 | :members: 53 | 54 | .. autoclass:: nc_py_api.ex_app.ui.resources.UiStyle 55 | :members: 56 | 57 | .. autoclass:: nc_py_api.ex_app.ui.settings.SettingsField 58 | :members: 59 | 60 | .. autoclass:: nc_py_api.ex_app.ui.settings.SettingsForm 61 | :members: 62 | 63 | .. autoclass:: nc_py_api.ex_app.ui.settings.SettingsFieldType 64 | :members: 65 | 66 | .. autoclass:: nc_py_api.ex_app.ui.settings._DeclarativeSettingsAPI 67 | :members: 68 | 69 | .. autoclass:: nc_py_api.ex_app.providers.providers.ProvidersApi 70 | :members: 71 | 72 | .. autoclass:: nc_py_api.ex_app.providers.task_processing.ShapeType 73 | :members: 74 | 75 | .. autoclass:: nc_py_api.ex_app.providers.task_processing.ShapeEnumValue 76 | :members: 77 | 78 | .. autoclass:: nc_py_api.ex_app.providers.task_processing.ShapeDescriptor 79 | :members: 80 | 81 | .. autoclass:: nc_py_api.ex_app.providers.task_processing.TaskType 82 | :members: 83 | 84 | .. autoclass:: nc_py_api.ex_app.providers.task_processing.TaskProcessingProvider 85 | :members: 86 | 87 | .. autoclass:: nc_py_api.ex_app.providers.task_processing._TaskProcessingProviderAPI 88 | :members: 89 | 90 | .. autoclass:: nc_py_api.ex_app.occ_commands.OccCommand 91 | :members: 92 | 93 | .. autoclass:: nc_py_api.ex_app.occ_commands.OccCommandsAPI 94 | :members: 95 | -------------------------------------------------------------------------------- /docs/reference/Exceptions.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: nc_py_api._exceptions 2 | 3 | Exceptions 4 | ========== 5 | 6 | Avalaible as `nc_py_api.{exception_name}` 7 | 8 | .. autoclass:: NextcloudException 9 | :members: 10 | 11 | .. autoclass:: NextcloudExceptionNotModified 12 | :members: 13 | 14 | .. autoclass:: NextcloudExceptionNotFound 15 | :members: 16 | 17 | .. autoclass:: NextcloudMissingCapabilities 18 | :members: 19 | -------------------------------------------------------------------------------- /docs/reference/Files/Files.rst: -------------------------------------------------------------------------------- 1 | File System 2 | =========== 3 | 4 | The Files API is universal for both modes and provides all the necessary methods for working with the Nextcloud file system. 5 | Refer to the `Files examples `_ to see how to use them nicely. 6 | 7 | All File APIs are designed to work relative to the current user. 8 | 9 | .. autoclass:: nc_py_api.files.files.FilesAPI 10 | :members: 11 | 12 | .. autoclass:: nc_py_api.files.FsNodeInfo 13 | :members: 14 | 15 | .. autoclass:: nc_py_api.files.FsNode 16 | :members: 17 | 18 | .. autoclass:: nc_py_api.files.FilePermissions 19 | :members: 20 | 21 | .. autoclass:: nc_py_api.files.SystemTag 22 | :members: 23 | 24 | .. autoclass:: nc_py_api.files.LockType 25 | :members: 26 | 27 | .. autoclass:: nc_py_api.files.FsNodeLockInfo 28 | :members: 29 | 30 | .. autoclass:: nc_py_api.files.ActionFileInfo 31 | :members: fileId, name, directory, etag, mime, fileType, size, favorite, permissions, mtime, userId, instanceId, to_fs_node 32 | 33 | .. autoclass:: nc_py_api.files.ActionFileInfoEx 34 | :members: files 35 | -------------------------------------------------------------------------------- /docs/reference/Files/Shares.rst: -------------------------------------------------------------------------------- 1 | File Sharing 2 | ============ 3 | 4 | The Shares API is universal for both modes and provides all the necessary methods for working with the Nextcloud Shares system. 5 | 6 | .. autoclass:: nc_py_api.files.sharing._FilesSharingAPI 7 | :members: 8 | 9 | .. autoclass:: nc_py_api.files.sharing.Share 10 | :members: 11 | 12 | .. autoclass:: nc_py_api.files.sharing.ShareType 13 | :members: 14 | -------------------------------------------------------------------------------- /docs/reference/Files/index.rst: -------------------------------------------------------------------------------- 1 | Files API 2 | ========= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | Files 8 | Shares 9 | -------------------------------------------------------------------------------- /docs/reference/LoginFlowV2.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: nc_py_api.loginflow_v2 2 | 3 | LoginFlow V2 4 | ============ 5 | 6 | Login flow v2 is an authorization process for the standard Nextcloud client that allows each client to have their own set of credentials. 7 | 8 | .. autoclass:: _LoginFlowV2API 9 | :inherited-members: 10 | :members: 11 | 12 | .. autoclass:: Credentials 13 | :inherited-members: 14 | :members: 15 | 16 | .. autoclass:: LoginFlow 17 | :inherited-members: 18 | :members: 19 | -------------------------------------------------------------------------------- /docs/reference/Nextcloud.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: nc_py_api.nextcloud 2 | 3 | Nextcloud 4 | ========= 5 | 6 | Two base classes for working with Nextcloud. The first for working as a client, the second as an application. 7 | 8 | All required functionality is incorporated in them, they contains all other classes required to work with the Nextcloud. 9 | 10 | .. autoclass:: Nextcloud 11 | :inherited-members: 12 | :members: 13 | 14 | .. automethod:: __init__ 15 | 16 | .. autoclass:: NextcloudApp 17 | :inherited-members: 18 | :members: 19 | -------------------------------------------------------------------------------- /docs/reference/Notes.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: nc_py_api.notes 2 | 3 | Notes API 4 | ========= 5 | 6 | .. autoclass:: nc_py_api.notes.Note 7 | :members: 8 | 9 | .. autoclass:: nc_py_api.notes.NotesSettings 10 | :members: 11 | 12 | .. autoclass:: nc_py_api.notes._NotesAPI 13 | :members: 14 | -------------------------------------------------------------------------------- /docs/reference/Session.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: nc_py_api._session 2 | 3 | Session Structures 4 | ================== 5 | 6 | .. autoclass:: ServerVersion 7 | :members: 8 | 9 | .. autoclass:: nc_py_api._theming.ThemingInfo 10 | :members: 11 | 12 | .. autoclass:: AppConfig 13 | :members: 14 | 15 | Internal 16 | ^^^^^^^^ 17 | 18 | .. note:: The Session API is currently private and subject to change without deprecation. 19 | 20 | .. autoclass:: NcSessionBasic 21 | :members: 22 | 23 | .. autoclass:: NcSessionApp 24 | :members: 25 | :inherited-members: 26 | 27 | .. autoclass:: NcSession 28 | :members: 29 | -------------------------------------------------------------------------------- /docs/reference/Talk.rst: -------------------------------------------------------------------------------- 1 | Talk API 2 | -------- 3 | 4 | .. autoclass:: nc_py_api.talk.Conversation 5 | :members: 6 | :inherited-members: 7 | 8 | .. autoclass:: nc_py_api.talk.Participant 9 | :members: 10 | :inherited-members: 11 | 12 | .. autoclass:: nc_py_api.talk.TalkMessage 13 | :members: 14 | :inherited-members: 15 | 16 | .. autoclass:: nc_py_api.talk.TalkFileMessage 17 | :members: 18 | :inherited-members: 19 | 20 | .. autoclass:: nc_py_api._talk_api._TalkAPI 21 | :members: 22 | 23 | .. autoclass:: nc_py_api.talk.ConversationType 24 | :members: 25 | 26 | .. autoclass:: nc_py_api.talk.ParticipantType 27 | :members: 28 | 29 | .. autoclass:: nc_py_api.talk.AttendeePermissions 30 | :members: 31 | 32 | .. autoclass:: nc_py_api.talk.InCallFlags 33 | :members: 34 | :undoc-members: 35 | 36 | .. autoclass:: nc_py_api.talk.ListableScope 37 | :members: 38 | :undoc-members: 39 | 40 | .. autoclass:: nc_py_api.talk.NotificationLevel 41 | :members: 42 | :undoc-members: 43 | 44 | .. autoclass:: nc_py_api.talk.WebinarLobbyStates 45 | :members: 46 | :undoc-members: 47 | 48 | .. autoclass:: nc_py_api.talk.SipEnabledStatus 49 | :members: 50 | :undoc-members: 51 | 52 | .. autoclass:: nc_py_api.talk.CallRecordingStatus 53 | :members: 54 | :undoc-members: 55 | 56 | .. autoclass:: nc_py_api.talk.BreakoutRoomMode 57 | :members: 58 | :undoc-members: 59 | 60 | .. autoclass:: nc_py_api.talk.BreakoutRoomStatus 61 | :members: 62 | :undoc-members: 63 | 64 | .. autoclass:: nc_py_api.talk.MessageReactions 65 | :members: 66 | :undoc-members: 67 | 68 | .. autoclass:: nc_py_api.talk.BotInfo 69 | :members: 70 | :inherited-members: 71 | 72 | .. autoclass:: nc_py_api.talk.BotInfoBasic 73 | :members: 74 | 75 | .. autoclass:: nc_py_api.talk.Poll 76 | :members: 77 | 78 | .. autoclass:: nc_py_api.talk.PollDetail 79 | :members: 80 | -------------------------------------------------------------------------------- /docs/reference/TalkBot.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: nc_py_api 2 | 3 | Talk Bot API 4 | ------------ 5 | 6 | .. autoclass:: nc_py_api.talk_bot.TalkBotMessage 7 | :members: 8 | :undoc-members: 9 | 10 | .. autoclass:: nc_py_api.talk_bot.TalkBot 11 | :members: 12 | 13 | .. autoclass:: nc_py_api.talk_bot.ObjectContent 14 | :members: 15 | :undoc-members: 16 | 17 | .. autofunction:: nc_py_api.talk_bot.get_bot_secret 18 | -------------------------------------------------------------------------------- /docs/reference/Users/Notifications.rst: -------------------------------------------------------------------------------- 1 | Notifications 2 | ------------- 3 | 4 | .. autoclass:: nc_py_api.notifications._NotificationsAPI 5 | :members: 6 | 7 | .. autoclass:: nc_py_api.notifications.Notification 8 | :members: 9 | -------------------------------------------------------------------------------- /docs/reference/Users/Users.rst: -------------------------------------------------------------------------------- 1 | User Management 2 | --------------- 3 | 4 | .. autoclass:: nc_py_api.users.UserInfo 5 | :members: 6 | 7 | .. autoclass:: nc_py_api.users._UsersAPI 8 | :members: 9 | -------------------------------------------------------------------------------- /docs/reference/Users/UsersGroups.rst: -------------------------------------------------------------------------------- 1 | User Groups Management 2 | ---------------------- 3 | 4 | .. autoclass:: nc_py_api.users_groups._UsersGroupsAPI 5 | :members: 6 | 7 | .. autoclass:: nc_py_api.users_groups.GroupDetails 8 | :members: 9 | -------------------------------------------------------------------------------- /docs/reference/Users/UsersStatus.rst: -------------------------------------------------------------------------------- 1 | User Status 2 | ----------- 3 | 4 | .. autoclass:: nc_py_api.user_status._UserStatusAPI 5 | :members: 6 | 7 | .. autoclass:: nc_py_api.user_status.CurrentUserStatus 8 | :members: 9 | 10 | .. autoclass:: nc_py_api.user_status.UserStatus 11 | :members: 12 | :inherited-members: 13 | 14 | .. autoclass:: nc_py_api.user_status.PredefinedStatus 15 | :members: 16 | 17 | .. autoclass:: nc_py_api.user_status.ClearAt 18 | :members: 19 | -------------------------------------------------------------------------------- /docs/reference/Users/WeatherStatus.rst: -------------------------------------------------------------------------------- 1 | Weather Status 2 | -------------- 3 | 4 | .. autoclass:: nc_py_api.weather_status._WeatherStatusAPI 5 | :members: 6 | 7 | .. autoclass:: nc_py_api.weather_status.WeatherLocation 8 | :members: 9 | 10 | .. autoclass:: nc_py_api.weather_status.WeatherLocationMode 11 | :members: 12 | -------------------------------------------------------------------------------- /docs/reference/Users/index.rst: -------------------------------------------------------------------------------- 1 | Users API 2 | ========= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | Users 8 | UsersGroups 9 | UsersStatus 10 | Notifications 11 | WeatherStatus 12 | -------------------------------------------------------------------------------- /docs/reference/Webhooks.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: nc_py_api.webhooks 2 | 3 | Webhooks API 4 | ============ 5 | 6 | .. autoclass:: nc_py_api.webhooks.WebhookInfo 7 | :members: 8 | 9 | .. autoclass:: nc_py_api.webhooks._WebhooksAPI 10 | :members: 11 | -------------------------------------------------------------------------------- /docs/reference/index.rst: -------------------------------------------------------------------------------- 1 | Reference 2 | ========= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | Nextcloud 8 | ExApp 9 | Apps 10 | Files/index.rst 11 | Users/index.rst 12 | Exceptions 13 | Talk 14 | TalkBot 15 | Calendar 16 | ActivityApp 17 | Notes 18 | Session 19 | LoginFlowV2 20 | Webhooks 21 | -------------------------------------------------------------------------------- /docs/resources/css/light.css: -------------------------------------------------------------------------------- 1 | @media (prefers-color-scheme: light) { 2 | 3 | .wy-menu-vertical li.toctree-l2.current a, 4 | .wy-menu-vertical li.toctree-l3.current a { 5 | background-color: #c9c9c9; 6 | } 7 | 8 | } 9 | -------------------------------------------------------------------------------- /docs/resources/css/styles.css: -------------------------------------------------------------------------------- 1 | th p { 2 | margin-bottom: 0; 3 | } 4 | 5 | .rst-content tr .line-block { 6 | font-size: 1rem; 7 | margin-bottom: 0; 8 | } 9 | 10 | .wy-nav-content { 11 | max-width: 80% !important; 12 | } 13 | -------------------------------------------------------------------------------- /docs/resources/js/script.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function ($) { 2 | setTimeout(function () { 3 | var sectionID = 'base'; 4 | var search = function ($section, $sidebarItem) { 5 | $section.children('.section, .function, .method').each(function () { 6 | if ($(this).hasClass('section')) { 7 | sectionID = $(this).attr('id'); 8 | search($(this), $sidebarItem.parent().find('[href="#'+sectionID+'"]')); 9 | } else { 10 | var $dt = $(this).children('dt'); 11 | var id = $dt.attr('id'); 12 | if (id === undefined) { 13 | return; 14 | } 15 | 16 | var $functionsUL = $sidebarItem.siblings('[data-sectionID='+sectionID+']'); 17 | if (!$functionsUL.length) { 18 | $functionsUL = $('