Showing preview only (2,639K chars total). Download the full file or copy to clipboard to get everything.
Repository: zeshuaro/telegram-pdf-bot
Branch: master
Commit: ca470d7bc728
Files: 261
Total size: 2.4 MB
Directory structure:
gitextract_l0t6q8aw/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ ├── github-actions.yml
│ ├── pull-request-target.yml
│ ├── pull-request.yml
│ ├── scheduled.yml
│ └── update-translations.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .python-version
├── .vscode/
│ ├── launch.json
│ └── settings.json
├── Dockerfile
├── LICENSE
├── README.md
├── _typos.toml
├── codecov.yml
├── crowdin.yml
├── locale/
│ ├── af_ZA/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── am_ET/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ar_SA/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ca_ES/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── cs_CZ/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── da_DK/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── de_DE/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── el_GR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── en_GB/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── en_US/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── es_ES/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── fa_IR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── fi_FI/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── fr_FR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── he_IL/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── hi_IN/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── hu_HU/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── id_ID/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── it_IT/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ja_JP/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ko_KR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ky_KG/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ms_MY/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── nl_NL/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── no_NO/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── om_ET/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── pl_PL/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── pt_BR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── pt_PT/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ro_RO/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ru_RU/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── si_LK/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── sr_SP/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── sv_SE/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ta_IN/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── tr_TR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── uk_UA/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── uz_UZ/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── vi_VN/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── zh_CN/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── zh_HK/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ └── zh_TW/
│ └── LC_MESSAGES/
│ └── pdf_bot.po
├── pdf_bot/
│ ├── __init__.py
│ ├── __main__.py
│ ├── account/
│ │ ├── __init__.py
│ │ ├── account_repository.py
│ │ └── account_service.py
│ ├── analytics/
│ │ ├── __init__.py
│ │ ├── analytics_repository.py
│ │ ├── analytics_service.py
│ │ └── models.py
│ ├── cli/
│ │ ├── __init__.py
│ │ ├── cli_service.py
│ │ └── exceptions.py
│ ├── command/
│ │ ├── __init__.py
│ │ ├── command_handler.py
│ │ └── command_service.py
│ ├── compare/
│ │ ├── __init__.py
│ │ ├── compare_handler.py
│ │ └── compare_service.py
│ ├── consts.py
│ ├── containers.py
│ ├── datastore/
│ │ ├── __init__.py
│ │ └── datastore_client.py
│ ├── error/
│ │ ├── __init__.py
│ │ ├── error_callback_query_handler.py
│ │ ├── error_handler.py
│ │ └── error_service.py
│ ├── errors.py
│ ├── feedback/
│ │ ├── __init__.py
│ │ ├── feedback_handler.py
│ │ ├── feedback_repository.py
│ │ └── feedback_service.py
│ ├── file/
│ │ ├── __init__.py
│ │ ├── file_handler.py
│ │ └── file_service.py
│ ├── file_processor/
│ │ ├── __init__.py
│ │ ├── abstract_file_processor.py
│ │ ├── abstract_file_task_processor.py
│ │ ├── errors.py
│ │ └── file_task_mixin.py
│ ├── image/
│ │ ├── __init__.py
│ │ └── image_service.py
│ ├── image_handler/
│ │ ├── __init__.py
│ │ ├── batch_image_handler.py
│ │ └── batch_image_service.py
│ ├── image_processor/
│ │ ├── __init__.py
│ │ ├── abstract_image_processor.py
│ │ ├── beautify_image_processor.py
│ │ ├── image_task_processor.py
│ │ └── image_to_pdf_processor.py
│ ├── io_internal/
│ │ ├── __init__.py
│ │ └── io_service.py
│ ├── language/
│ │ ├── __init__.py
│ │ ├── language_handler.py
│ │ ├── language_repository.py
│ │ ├── language_service.py
│ │ └── models.py
│ ├── log/
│ │ ├── __init__.py
│ │ └── log_handler.py
│ ├── merge/
│ │ ├── __init__.py
│ │ ├── merge_handler.py
│ │ └── merge_service.py
│ ├── models.py
│ ├── payment/
│ │ ├── __init__.py
│ │ ├── models.py
│ │ ├── payment_handler.py
│ │ └── payment_service.py
│ ├── pdf/
│ │ ├── __init__.py
│ │ ├── exceptions.py
│ │ ├── models.py
│ │ └── pdf_service.py
│ ├── pdf_processor/
│ │ ├── __init__.py
│ │ ├── abstract_pdf_processor.py
│ │ ├── abstract_pdf_select_and_text_processor.py
│ │ ├── abstract_pdf_text_input_processor.py
│ │ ├── compress_pdf_processor.py
│ │ ├── crop_pdf_processor.py
│ │ ├── decrypt_pdf_processor.py
│ │ ├── encrypt_pdf_processor.py
│ │ ├── extract_pdf_image_processor.py
│ │ ├── extract_pdf_text_processor.py
│ │ ├── grayscale_pdf_processor.py
│ │ ├── ocr_pdf_processor.py
│ │ ├── pdf_task_processor.py
│ │ ├── pdf_to_image_processor.py
│ │ ├── preview_pdf_processor.py
│ │ ├── rename_pdf_processor.py
│ │ ├── rotate_pdf_processor.py
│ │ ├── scale_pdf_processor.py
│ │ └── split_pdf_processor.py
│ ├── settings.py
│ ├── telegram_handler/
│ │ ├── __init__.py
│ │ └── abstract_telegram_handler.py
│ ├── telegram_internal/
│ │ ├── __init__.py
│ │ ├── exceptions.py
│ │ └── telegram_service.py
│ ├── text/
│ │ ├── __init__.py
│ │ ├── text_handler.py
│ │ ├── text_repository.py
│ │ └── text_service.py
│ ├── watermark/
│ │ ├── __init__.py
│ │ ├── watermark_handler.py
│ │ └── watermark_service.py
│ └── webpage/
│ ├── __init__.py
│ ├── webpage_handler.py
│ └── webpage_service.py
├── pyproject.toml
├── renovate.json
└── tests/
├── __init__.py
├── account/
│ ├── __init__.py
│ ├── test_account_repository.py
│ └── test_account_service.py
├── analytics/
│ ├── __init__.py
│ ├── test_analytics_repository.py
│ └── test_analytics_service.py
├── cli/
│ ├── __init__.py
│ └── test_cli_service.py
├── command/
│ ├── __init__.py
│ ├── test_command_handler.py
│ └── test_command_service.py
├── compare/
│ ├── __init__.py
│ ├── test_compare_handler.py
│ └── test_compare_service.py
├── conftest.py
├── datastore/
│ ├── __init__.py
│ └── test_datastore_client.py
├── error/
│ ├── __init__.py
│ ├── test_error_callback_query_handler.py
│ ├── test_error_handler.py
│ └── test_error_service.py
├── feedback/
│ ├── __init__.py
│ ├── test_feedback_handler.py
│ ├── test_feedback_repository.py
│ └── test_feedback_service.py
├── file/
│ ├── __init__.py
│ ├── test_file_handler.py
│ └── test_file_service.py
├── file_processor/
│ ├── __init__.py
│ ├── test_abstract_file_processor.py
│ ├── test_abstract_file_task_processor.py
│ └── test_file_task_mixin.py
├── image/
│ ├── __init__.py
│ └── test_image_service.py
├── image_handler/
│ ├── __init__.py
│ ├── test_batch_image_handler.py
│ └── test_batch_image_service.py
├── image_processor/
│ ├── __init__.py
│ ├── test_abstract_image_processor.py
│ ├── test_beautify_image_processor.py
│ ├── test_image_task_processor.py
│ └── test_image_to_pdf_processor.py
├── io_internal/
│ ├── __init__.py
│ └── test_io_service.py
├── language/
│ ├── __init__.py
│ ├── language_service_test_mixin.py
│ ├── test_language_handler.py
│ ├── test_language_repository.py
│ └── test_language_service.py
├── merge/
│ ├── __init__.py
│ ├── test_merge_handler.py
│ └── test_merge_service.py
├── path_test_mixin.py
├── payment/
│ ├── __init__.py
│ ├── test_payment_handler.py
│ └── test_payment_service.py
├── pdf/
│ ├── __init__.py
│ ├── test_compress_result.py
│ └── test_pdf_service.py
├── pdf_processor/
│ ├── __init__.py
│ ├── test_abstract_pdf_processor.py
│ ├── test_abstract_pdf_select_and_text_processor.py
│ ├── test_abstract_pdf_text_input_processor.py
│ ├── test_compress_pdf_processor.py
│ ├── test_crop_pdf_processor.py
│ ├── test_decrypt_pdf_processor.py
│ ├── test_encrypt_pdf_processor.py
│ ├── test_extract_pdf_image_processor.py
│ ├── test_extract_pdf_text_processor.py
│ ├── test_grayscale_pdf_processor.py
│ ├── test_ocr_pdf_processor.py
│ ├── test_pdf_task_processor.py
│ ├── test_pdf_to_image_processor.py
│ ├── test_preview_pdf_processor.py
│ ├── test_rename_pdf_processor.py
│ ├── test_rotate_pdf_processor.py
│ ├── test_scale_pdf_processor.py
│ └── test_split_pdf_processor.py
├── telegram_internal/
│ ├── __init__.py
│ ├── telegram_service_test_mixin.py
│ ├── telegram_test_mixin.py
│ └── test_telegram_service.py
├── test_containers.py
├── test_models.py
├── text/
│ ├── __init__.py
│ ├── test_text_handler.py
│ ├── test_text_repository.py
│ └── test_text_service.py
├── watermark/
│ ├── __init__.py
│ ├── test_watermark_handler.py
│ └── test_watermark_service.py
└── webpage/
├── __init__.py
├── test_webpage_handler.py
└── test_webpage_service.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
github: [zeshuaro]
ko_fi: zeshuaro
liberapay: zeshuaro
patreon: zeshuaro
custom:
- "https://www.buymeacoffee.com/zeshuaro"
- "https://paypal.me/JoshuaTang"
================================================
FILE: .github/workflows/github-actions.yml
================================================
name: GitHub Actions
on:
push:
branches:
- master
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
cancel-in-progress: true
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v6
- name: Setup Python 🐍
id: setup-python
uses: actions/setup-python@v6
with:
python-version-file: ".python-version"
- name: Install Poetry 📦
uses: snok/install-poetry@v1.4.1
with:
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache virtual environment 💾
uses: actions/cache@v5
with:
path: .venv
key: ${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-
- name: Install dependencies ⚙️
run: poetry install --no-interaction
- name: Run Tests 🧪
run: |
source .venv/bin/activate
pybabel compile -D pdf_bot -d locale
pytest --cov --cov-report=xml
- name: Upload coverage report 📡
uses: codecov/codecov-action@v6.0.0
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v6
- name: Setup Python 🐍
id: setup-python
uses: actions/setup-python@v6
with:
python-version-file: ".python-version"
- name: Install Poetry 📦
uses: snok/install-poetry@v1.4.1
with:
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache virtual environment 💾
uses: actions/cache@v5
with:
path: .venv
key: ${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
${{ runner.os }}-venv-${{ steps.setup-python.outputs.python-version }}-
- name: Install dependencies ⚙️
run: poetry install --no-interaction --no-root
- name: Run linting 🧪
run: |
source .venv/bin/activate
pre-commit run --all-files
check-docker-image:
name: Check Docker image
runs-on: ubuntu-latest
if: ${{ github.ref != 'refs/heads/master' }}
steps:
- name: Checkout 🛎️
uses: actions/checkout@v6
- name: Build Docker image 🏗
run: |-
docker build .
check-pyenv:
name: Check pyenv
runs-on: ubuntu-latest
if: ${{ github.ref != 'refs/heads/master' }}
steps:
- name: Checkout 🛎️
uses: actions/checkout@v6
- name: Check Python version in pyenv 🐍
run: |
curl https://pyenv.run | bash
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
pyenv install
================================================
FILE: .github/workflows/pull-request-target.yml
================================================
name: Pull request target actions
on:
pull_request_target:
jobs:
pull-request:
uses: zeshuaro/github-actions-workflows/.github/workflows/pull-request-target.yml@cf90821e20f6d6557d4d182b76a7ef763c3653d0 # main
================================================
FILE: .github/workflows/pull-request.yml
================================================
name: Pull request actions
on:
pull_request:
jobs:
pull-request:
uses: zeshuaro/github-actions-workflows/.github/workflows/pull-request.yml@cf90821e20f6d6557d4d182b76a7ef763c3653d0 # main
================================================
FILE: .github/workflows/scheduled.yml
================================================
name: Scheduled actions
on:
schedule:
- cron: "0 8 * * *"
jobs:
scheduled:
uses: zeshuaro/github-actions-workflows/.github/workflows/scheduled.yml@cf90821e20f6d6557d4d182b76a7ef763c3653d0 # main
secrets:
GH_APP_ID: ${{ secrets.GH_APP_ID }}
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
================================================
FILE: .github/workflows/update-translations.yml
================================================
name: Update Translations
on:
schedule:
- cron: "0 8 * * *"
workflow_dispatch:
jobs:
update-translations:
name: Update Translations
runs-on: ubuntu-latest
steps:
- name: Create GitHub App token 🔑
id: app-token
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Checkout 🛎️
uses: actions/checkout@v6
with:
token: ${{ steps.app-token.outputs.token }}
persist-credentials: false
- name: Download translations 🌐
uses: crowdin/github-action@v2.16.2
with:
upload_sources: false
upload_translations: false
download_translations: true
commit_message: "fix: new translations"
create_pull_request: true
pull_request_title: "fix: new translations"
pull_request_labels: l10n
config: ./crowdin.yml
source: locale/en_GB/LC_MESSAGES/pdf_bot.po
translation: /locale/%locale_with_underscore%/LC_MESSAGES/pdf_bot.po
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
================================================
FILE: .gitignore
================================================
# Created by https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,python
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,visualstudiocode,python
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$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
# 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
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# 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
.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/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,python
# Ruff
.ruff_cache/
================================================
FILE: .pre-commit-config.yaml
================================================
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-added-large-files
- id: check-json
- id: check-toml
- id: check-yaml
- id: end-of-file-fixer
exclude: ^locale/
- id: trailing-whitespace
- repo: local
hooks:
- id: ruff-lint
name: ruff lint
entry: ruff
args:
- check
language: system
types: [python]
- id: ruff-format
name: ruff format
entry: ruff
args:
- format
- --check
- --diff
language: system
types: [python]
- id: mypy
name: mypy
entry: mypy
language: system
types: [python]
================================================
FILE: .python-version
================================================
3.14.4
================================================
FILE: .vscode/launch.json
================================================
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: pdf_bot",
"type": "python",
"request": "launch",
"module": "pdf_bot",
"console": "integratedTerminal",
"justMyCode": false
}
]
}
================================================
FILE: .vscode/settings.json
================================================
{
"editor.formatOnSave": true,
"python.analysis.indexing": true,
"python.testing.pytestArgs": ["tests"],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
}
}
================================================
FILE: Dockerfile
================================================
FROM --platform=linux/amd64 python:3.14.4-slim AS build
ARG COMMIT_HASH
WORKDIR /build
RUN apt-get update && apt-get install -y --no-install-recommends g++ gcc git libcairo2-dev \
pkg-config python3-dev
RUN pip install -U pip && pip install poetry
COPY pyproject.toml poetry.lock ./
RUN poetry config virtualenvs.in-project true \
&& poetry install --only main --no-root --no-interaction
ENV PATH="/build/.venv/bin:${PATH}"
COPY locale locale/
RUN pybabel compile -D pdf_bot -d locale \
&& find locale -type f -name '*.po' -delete
FROM --platform=linux/amd64 python:3.14.4-slim AS deploy
ARG COMMIT_HASH
ENV SENTRY_RELEASE $COMMIT_HASH
RUN apt-get update \
&& apt-get install -y --no-install-recommends ghostscript libpango-1.0-0 \
libpangoft2-1.0-0 ocrmypdf poppler-utils \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=build /build/.venv/ /build/.venv/
ENV PATH="/build/.venv/bin:${PATH}"
COPY --from=build /build/locale /app/locale/
COPY pdf_bot pdf_bot/
CMD ["python", "-m", "pdf_bot"]
================================================
FILE: LICENSE
================================================
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
A Telegram bot that can do a lot of things related to PDF files.
Copyright (C) 2016-2025 Joshua Tang
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
================================================
FILE: README.md
================================================
# Telegram PDF Bot
[](https://t.me/pdfbot)
[](https://github.com/zeshuaro/telegram-pdf-bot/blob/master/LICENSE)
[](https://github.com/zeshuaro/telegram-pdf-bot/actions/workflows/github-actions.yml)
[](https://codecov.io/github/zeshuaro/telegram-pdf-bot)
[](https://app.codacy.com/gh/zeshuaro/telegram-pdf-bot/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[](https://github.com/astral-sh/ruff)
[](https://crowdin.com/project/telegram-pdf-bot)
[](https://t.me/pdf2botdev)
[](https://github.com/ebertti/awesome-telegram)
[](https://github.com/sponsors/zeshuaro)
[](https://www.buymeacoffee.com/zeshuaro)
[](https://ko-fi.com/zeshuaro)
[](https://liberapay.com/zeshuaro/)
[](https://patreon.com/zeshuaro)
[](https://paypal.me/JoshuaTang)
A Telegram bot that can:
- Compress, crop, decrypt, encrypt, merge, preview, rename, rotate, scale and split PDF files
- Compare text differences between two PDF files
- Create PDF files from text messages
- Add watermark to PDF files
- Add text layers to PDF files to make them searchable with text
- Extract images and text from PDF files
- Convert PDF files into images
- Beautify handwritten notes images into PDF files
- Convert webpages and images into PDF files
## Getting Started
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes
### Setup Database
The bot uses [Datastore](https://cloud.google.com/datastore) on Google Cloud Platform (GCP). Create a new project on GCP and enabble Datastore in the project. Install the [gcloud CLI](https://cloud.google.com/sdk/) and run `gcloud init` to initialise it with your project.
### OS Requirements
Ubuntu
```sh
apt-get install poppler-utils libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 libffi-dev shared-mime-info
```
macOS
```sh
brew install libxml2 libxslt poppler cairo pango gdk-pixbuf libffi
```
### Install dependencies
This project uses [Poetry](https://python-poetry.org/) as the dependency manager, run the following command to install the dependencies:
```sh
poetry install --no-root
```
### Compile the translation files
Run the following command to compile all the translation files:
```sh
pybabel compile -D pdf_bot -d locale/
```
### Setup Your Environment Variables
Copy the `.env` example file and edit the variables within the file:
```sh
cp .env.example .env
```
### Running The Bot
You can then start the bot with the following command:
```bash
python -m pdf_bot
```
================================================
FILE: _typos.toml
================================================
[type.po]
extend-glob = ["*.po"]
check-file = false
================================================
FILE: codecov.yml
================================================
comment: false
================================================
FILE: crowdin.yml
================================================
project_id_env: CROWDIN_PROJECT_ID
api_token_env: CROWDIN_PERSONAL_TOKEN
================================================
FILE: locale/af_ZA/LC_MESSAGES/pdf_bot.po
================================================
# locale translations for telegram-pdf-bot.
# Copyright (C) 2021 zeshuaro
# This file is distributed under the same license as the telegram-pdf-bot
# project.
# zeshuaro <zeshuaro@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: telegram-pdf-bot\n"
"Report-Msgid-Bugs-To: zeshuaro@gmail.com\n"
"POT-Creation-Date: 2025-10-19 06:16+0000\n"
"PO-Revision-Date: 2025-10-19 06:16\n"
"Last-Translator: \n"
"Language: af\n"
"Language-Team: Afrikaans\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.17.0\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: telegram-pdf-bot\n"
"X-Crowdin-Project-ID: 370289\n"
"X-Crowdin-Language: af\n"
"X-Crowdin-File: pdf_bot.po\n"
"X-Crowdin-File-ID: 88\n"
#: pdf_bot/consts.py:11
msgid "Cancel"
msgstr "Kanselleer"
#: pdf_bot/consts.py:12
msgid "Done"
msgstr "Gedoen"
#: pdf_bot/consts.py:13 pdf_bot/telegram_internal/telegram_service.py:48
msgid "Back"
msgstr "Terug"
#: pdf_bot/consts.py:23
msgid "Something went wrong, start over with your file or command"
msgstr "Iets het verkeerd geloop, begin met u lêer of opdrag"
#: pdf_bot/cli/cli_service.py:35
msgid "Failed to complete process"
msgstr "Kon nie die proses voltooi nie"
#: pdf_bot/command/command_service.py:38
msgid "Welcome to PDF Bot!"
msgstr "Welkom by PDF Bot!"
#: pdf_bot/command/command_service.py:39
msgid "Key features:"
msgstr "Sleutelkenmerke:"
#: pdf_bot/command/command_service.py:41
msgid "- Compress, merge, preview, rename, split and add watermark to PDF files"
msgstr "- Kompressie, saamsmelt, voorskou, hernoem, verdeel en voeg watermerk by PDF-lêers"
#: pdf_bot/command/command_service.py:43
msgid "- Create PDF files from text messages"
msgstr "- Skep PDF-lêers van teksboodskappe"
#: pdf_bot/command/command_service.py:44
msgid "- Extract images and text from PDF files"
msgstr "- Onttrek beelde en teks uit PDF-lêers"
#: pdf_bot/command/command_service.py:45
msgid "- Convert PDF files into images"
msgstr "- Omskep PDF-lêers in beelde"
#: pdf_bot/command/command_service.py:46
msgid "- Convert webpages and images into PDF files"
msgstr "- Omskep webbladsye en beelde in PDF-lêers"
#: pdf_bot/command/command_service.py:47
msgid "- Beautify handwritten notes images into PDF files"
msgstr "- Verfraai handgeskrewe notas beelde in PDF-lêers"
#: pdf_bot/command/command_service.py:48
msgid "- And more..."
msgstr "- En meer ..."
#: pdf_bot/command/command_service.py:49
#, python-brace-format
msgid "Type {command} to see how to use PDF Bot"
msgstr "Tipe {command} om te sien hoe om PDF Bot te gebruik"
#: pdf_bot/command/command_service.py:58
msgid "Set Language 🌎"
msgstr "Stel taal 🌎"
#: pdf_bot/command/command_service.py:60
#: pdf_bot/telegram_internal/telegram_service.py:222
msgid "Join Channel"
msgstr "Sluit aan by Kanaal"
#: pdf_bot/command/command_service.py:61 pdf_bot/payment/payment_service.py:74
#: pdf_bot/telegram_internal/telegram_service.py:223
msgid "Support PDF Bot"
msgstr "Ondersteun PDF Bot"
#: pdf_bot/command/command_service.py:70
msgid "You can perform most of the tasks by sending me one of the followings:"
msgstr "Jy kan die meeste van die take uitvoer deur vir my een van die volgende te stuur:"
#: pdf_bot/command/command_service.py:71
msgid "- PDF files"
msgstr "- PDF-lêers"
#: pdf_bot/command/command_service.py:72
msgid "- Images"
msgstr "- Beelde"
#: pdf_bot/command/command_service.py:73
msgid "- Webpage links"
msgstr "- Webbladsy skakels"
#: pdf_bot/command/command_service.py:74
msgid "The rest of the tasks can be performed by using the following commands:"
msgstr "Die res van die take kan uitgevoer word deur die volgende opdragte te gebruik:"
#: pdf_bot/command/command_service.py:75
#, python-brace-format
msgid "{command} - compare PDF files"
msgstr "{command} - vergelyk PDF-lêers"
#: pdf_bot/command/command_service.py:76
#, python-brace-format
msgid "{command} - merge PDF files"
msgstr "{command} - voeg PDF-lêers saam"
#: pdf_bot/command/command_service.py:78
#, python-brace-format
msgid "{command} - convert and combine multiple images into PDF files"
msgstr "{command} - omskep en kombineer verskeie beelde in PDF-lêers"
#: pdf_bot/command/command_service.py:80
#, python-brace-format
msgid "{command} - create PDF files from text messages"
msgstr "{command} - skep PDF-lêers van teksboodskappe"
#: pdf_bot/command/command_service.py:83
#, python-brace-format
msgid "{command} - add watermark to PDF files"
msgstr "{command} - voeg watermerk by PDF-lêers"
#: pdf_bot/compare/compare_service.py:41
msgid "Send me one of the PDF files that you'll like to compare"
msgstr "Stuur vir my een van die PDF-lêers wat jy graag wil vergelyk"
#: pdf_bot/compare/compare_service.py:42
msgid "Note that I can only look for text differences"
msgstr "Let daarop dat ek net teksverskille kan soek"
#: pdf_bot/compare/compare_service.py:64
msgid "Send me the other PDF file that you'll like to compare"
msgstr "Stuur vir my die ander PDF-lêer wat jy wil vergelyk"
#: pdf_bot/compare/compare_service.py:83
msgid "Comparing your PDF files"
msgstr "Vergelyk jou PDF-lêers"
#: pdf_bot/compare/compare_service.py:91
msgid "There are no text differences between your PDF files"
msgstr "Daar is geen teksverskille tussen jou PDF-lêers nie"
#: pdf_bot/error/error_handler.py:38 pdf_bot/error/error_handler.py:44
#: pdf_bot/telegram_internal/telegram_service.py:102
#: pdf_bot/telegram_internal/telegram_service.py:118
msgid "Something went wrong, please try again"
msgstr "Iets het verkeerd gegaan, probeer asseblief weer."
#: pdf_bot/error/error_handler.py:58 pdf_bot/error/error_service.py:17
msgid "The button has expired, start over with your file or command"
msgstr "Die knoppie het verval, begin met u lêer of opdrag"
#: pdf_bot/error/error_handler.py:60
msgid "The resulted image is invalid, try again"
msgstr "Die gevolglike beeld is ongeldig, probeer weer"
#: pdf_bot/feedback/feedback_service.py:31
msgid "Send me your feedback in English"
msgstr "Stuur vir my jou terugvoer in Engels"
#: pdf_bot/feedback/feedback_service.py:54
msgid "The feedback is not in English, try again"
msgstr "Die terugvoer is nie in Engels nie, probeer weer"
#: pdf_bot/feedback/feedback_service.py:58
msgid "Thank you for your feedback, I've forwarded it to my developer"
msgstr "Dankie vir u terugvoer, ek het dit aan my ontwikkelaar gestuur"
#: pdf_bot/file/file_service.py:54
msgid "Your file is too big for me to download and process"
msgstr "Jou lêer is te groot vir my om af te laai en te verwerk"
#: pdf_bot/file/file_service.py:56
msgid "Note that this is a Telegram Bot limitation and there's nothing I can do unless Telegram changes this limit"
msgstr "Let daarop dat dit 'n Telegram Bot beperking is en daar is niks wat ek kan doen nie, tensy Telegram hierdie limiet verander"
#: pdf_bot/file_processor/abstract_file_processor.py:105
#: pdf_bot/file_processor/abstract_file_processor.py:163
msgid "Processing your file"
msgstr "Verwerk u lêer"
#: pdf_bot/file_processor/file_task_mixin.py:69
msgid "Select the task that you'll like to perform"
msgstr "Kies die taak wat jy wil uitvoer"
#: pdf_bot/image_handler/batch_image_service.py:20
#: pdf_bot/image_processor/beautify_image_processor.py:24
msgid "Beautify"
msgstr "Hierdie artikel is 'n weesbladsy"
#: pdf_bot/image_handler/batch_image_service.py:21
#: pdf_bot/image_processor/image_to_pdf_processor.py:24
msgid "To PDF"
msgstr "Na PDF"
#: pdf_bot/image_handler/batch_image_service.py:22
#: pdf_bot/merge/merge_service.py:19
msgid "Remove last file"
msgstr "Verwyder laaste lêer"
#: pdf_bot/image_handler/batch_image_service.py:42
msgid "Send me the images that you'll like to beautify or convert into a PDF file"
msgstr "Stuur vir my die beelde wat u wil verfraai of omskep in 'n PDF-lêer"
#: pdf_bot/image_handler/batch_image_service.py:45
msgid "Note that the images will be beautified and converted in the order that you send me"
msgstr "Let daarop dat die beelde verfraai en omgeskakel sal word in die volgorde wat u my stuur"
#: pdf_bot/image_handler/batch_image_service.py:93
msgid "You've sent me these images so far:"
msgstr "Jy het tot dusver vir my hierdie beelde gestuur:"
#: pdf_bot/image_handler/batch_image_service.py:107
msgid "Select the task from below if you've sent me all the images, or keep sending me the images"
msgstr "Kies die taak van onder af as u al die beelde vir my gestuur het, of hou aan om vir my die beelde te stuur"
#: pdf_bot/image_handler/batch_image_service.py:127
msgid "You've already removed all the images you've sent me"
msgstr "U het al die beelde wat u aan my gestuur het, verwyder"
#: pdf_bot/image_handler/batch_image_service.py:131
#, python-brace-format
msgid "{file_name} has been removed"
msgstr "{file_name} is verwyder"
#: pdf_bot/image_handler/batch_image_service.py:151
msgid "You haven't sent me any images"
msgstr "Jy het geen beelde vir my gestuur nie"
#: pdf_bot/image_handler/batch_image_service.py:154
msgid "You've only sent me one image"
msgstr "Jy het net vir my een beeld gestuur"
#: pdf_bot/image_handler/batch_image_service.py:171
msgid "Beautifying and converting your images into a PDF file"
msgstr "Verfraai en omskep jou beelde in 'n PDF-lêer"
#: pdf_bot/image_handler/batch_image_service.py:173
msgid "Converting your images into a PDF file"
msgstr "Omskep jou beelde in 'n PDF-lêer"
#: pdf_bot/language/language_service.py:87
msgid "Select your language"
msgstr "Kies jou taal"
#: pdf_bot/language/language_service.py:119
#, python-brace-format
msgid "Your language has been set to {language}"
msgstr "Jou taal is ingestel om te {language}"
#: pdf_bot/merge/merge_service.py:38
msgid "Send me the PDF files that you'll like to merge"
msgstr "Stuur vir my die PDF-lêers wat jy graag wil saamsmelt"
#: pdf_bot/merge/merge_service.py:39
msgid "Note that the files will be merged in the order that you send me"
msgstr "Let daarop dat die lêers saamgesmelt sal word in die volgorde wat jy my stuur"
#: pdf_bot/merge/merge_service.py:85
msgid "You've sent me these PDF files so far:"
msgstr "U het tot dusver vir my hierdie PDF-lêers gestuur:"
#: pdf_bot/merge/merge_service.py:99
#, python-brace-format
msgid "Press {done} if you've sent me all the PDF files that you'll like to merge or keep sending me the PDF files"
msgstr "Druk {done} as jy vir my al die PDF-lêers gestuur het wat jy graag wil saamsmelt of aanhou om vir my die PDF-lêers te stuur"
#: pdf_bot/merge/merge_service.py:120
msgid "You've already removed all the PDF files you've sent me"
msgstr "U het reeds al die PDF-lêers verwyder wat u vir my gestuur het"
#: pdf_bot/merge/merge_service.py:124
#, python-brace-format
msgid "{file_name} has been removed for merging"
msgstr "{file_name} is verwyder vir samesmelting"
#: pdf_bot/merge/merge_service.py:146
msgid "You haven't sent me any PDF files"
msgstr "Jy het nie vir my enige PDF-lêers gestuur nie"
#: pdf_bot/merge/merge_service.py:149
msgid "You've only sent me one PDF file"
msgstr "Jy het net vir my een PDF-lêer gestuur"
#: pdf_bot/merge/merge_service.py:162
msgid "Merging your PDF files"
msgstr "Voeg jou PDF-lêers saam"
#: pdf_bot/payment/payment_service.py:26
#, python-brace-format
msgid "{message} {emoji} (${value})"
msgstr "{message} {emoji} (${value})"
#: pdf_bot/payment/payment_service.py:30
msgid "Say Thanks"
msgstr "Sê dankie"
#: pdf_bot/payment/payment_service.py:31
msgid "Coffee"
msgstr "Koffie"
#: pdf_bot/payment/payment_service.py:32
msgid "Beer"
msgstr "Bier"
#: pdf_bot/payment/payment_service.py:33
msgid "Meal"
msgstr "Maaltyd"
#: pdf_bot/payment/payment_service.py:59
msgid "Select how you want to support PDF Bot"
msgstr "Kies hoe jy PDF Bot wil ondersteun"
#: pdf_bot/payment/payment_service.py:75
msgid "Say thanks to PDF Bot and help keep it running"
msgstr "Sê dankie aan PDF Bot en help om dit aan die gang te hou"
#: pdf_bot/payment/payment_service.py:89
msgid "Something went wrong, try again"
msgstr "Iets het verkeerd geloop, probeer weer"
#: pdf_bot/payment/payment_service.py:96
msgid "Thank you for your support!"
msgstr "Dankie vir jou ondersteuning!"
#: pdf_bot/payment/payment_service.py:115
msgid "Help translate PDF Bot"
msgstr "Help vertaal PDF Bot"
#: pdf_bot/pdf/exceptions.py:29
msgid "Your PDF file is encrypted, decrypt it first then try again"
msgstr "Jou PDF-lêer is geïnkripteer, dekripteer dit eers en probeer dan weer"
#: pdf_bot/pdf/pdf_service.py:163
msgid "Your PDF file is not encrypted"
msgstr "Jou PDF-lêer is nie geënkripteer nie"
#: pdf_bot/pdf/pdf_service.py:167
msgid "Incorrect password, please try again"
msgstr "Verkeerde wagwoord, probeer asseblief weer"
#: pdf_bot/pdf/pdf_service.py:170
msgid "Your PDF file is encrypted with a method that I can't decrypt"
msgstr "Jou PDF-lêer is geënkripteer met 'n metode wat ek nie kan dekripteer nie"
#: pdf_bot/pdf/pdf_service.py:202
msgid "No images found in your PDF file"
msgstr "Geen beelde wat in jou PDF-lêer gevind word nie"
#: pdf_bot/pdf/pdf_service.py:214
msgid "No text found in your PDF file"
msgstr "Geen teks in jou PDF-lêer gevind nie"
#: pdf_bot/pdf/pdf_service.py:233
#, python-format
msgid "I couldn't merge your PDF files as this file is invalid: %s"
msgstr "Ek kon nie u PDF -lêers saamsmelt nie, aangesien hierdie lêer ongeldig is: %s"
#: pdf_bot/pdf/pdf_service.py:248
msgid "Your PDF file already has a text layer"
msgstr "U PDF-lêer het reeds 'n tekslaag"
#: pdf_bot/pdf/pdf_service.py:337
msgid "Your PDF file is invalid"
msgstr "Jou PDF-lêer is ongeldig"
#: pdf_bot/pdf_processor/compress_pdf_processor.py:24
msgid "Compress"
msgstr "Saamdruk"
#: pdf_bot/pdf_processor/compress_pdf_processor.py:35
#, python-brace-format
msgid "File size reduced by {percent}, from {old_size} to {new_size}"
msgstr "Lêergrootte verminder met {percent}, van {old_size} tot {new_size}"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:17
msgid "By percentage"
msgstr "Volgens persentasie"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:18
msgid "To margin size"
msgstr "Tot marge grootte"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:25
msgid "Send me a number between 0 and 100\n\n"
"This is the percentage of margin space to retain between the content in your PDF file and the page"
msgstr "Stuur vir my 'n nommer tussen 0 en 100\n\n"
"Dit is die persentasie margespasie om te behou tussen die inhoud in jou PDF-lêer en die bladsy"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:31
msgid "Send me a number that you'll like to adjust the margin size\n\n"
"Positive numbers will decrease the margin size and negative numbers will increase it"
msgstr "Stuur vir my 'n nommer wat jy graag die margegrootte wil aanpas\n\n"
"Positiewe getalle sal die margegrootte verminder en negatiewe getalle sal dit verhoog"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:56
msgid "Crop"
msgstr "Gewas"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:60
msgid "Select the crop type you'll like to perform"
msgstr "Kies die gewastipe wat jy graag wil uitvoer"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:68
msgid "The crop values are invalid, try again"
msgstr "Die gewaswaardes is ongeldig, probeer weer"
#: pdf_bot/pdf_processor/decrypt_pdf_processor.py:33
msgid "Decrypt"
msgstr "Dekripteer"
#: pdf_bot/pdf_processor/decrypt_pdf_processor.py:40
msgid "Send me the password to decrypt your PDF file"
msgstr "Stuur vir my die wagwoord om jou PDF-lêer te dekripteer"
#: pdf_bot/pdf_processor/encrypt_pdf_processor.py:27
msgid "Encrypt"
msgstr "Enkripteer"
#: pdf_bot/pdf_processor/encrypt_pdf_processor.py:34
msgid "Send me the password to encrypt your PDF file"
msgstr "Stuur vir my die wagwoord om jou PDF-lêer te enkripteer"
#: pdf_bot/pdf_processor/extract_pdf_image_processor.py:24
msgid "Extract images"
msgstr "Onttrek prente"
#: pdf_bot/pdf_processor/extract_pdf_text_processor.py:24
msgid "Extract text"
msgstr "Onttrek teks"
#: pdf_bot/pdf_processor/grayscale_pdf_processor.py:24
msgid "Grayscale"
msgstr "Grysskaal"
#: pdf_bot/pdf_processor/pdf_to_image_processor.py:24
msgid "To images"
msgstr "Na beelde"
#: pdf_bot/pdf_processor/preview_pdf_processor.py:24
msgid "Preview"
msgstr "Voorskou"
#: pdf_bot/pdf_processor/rename_pdf_processor.py:30
msgid "Rename"
msgstr "Hernoem"
#: pdf_bot/pdf_processor/rename_pdf_processor.py:35
#, python-format
msgid "File names can't contain any of the following characters, please try again:\n"
"%s"
msgstr "Lêername kan nie een van die volgende karakters bevat nie, probeer asseblief weer:\n"
"%s"
#: pdf_bot/pdf_processor/rename_pdf_processor.py:40
msgid "Send me the file name that you'll like to rename your PDF file into"
msgstr "Stuur vir my die lêernaam waarna jy jou PDF-lêer wil hernoem"
#: pdf_bot/pdf_processor/rotate_pdf_processor.py:38
msgid "Rotate"
msgstr "Roteer"
#: pdf_bot/pdf_processor/rotate_pdf_processor.py:76
msgid "Select the degrees that you'll like to rotate your PDF file in clockwise"
msgstr "Kies die grade wat jy jou PDF-lêer in kloksgewys wil draai"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:18
msgid "By factor"
msgstr "Volgens faktor"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:19
msgid "To dimension"
msgstr "Om dimensie te dimensie"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:26
msgid "Send me the scaling factors for the horizontal and vertical axes\n\n"
"Example: 2 0.5 - this will double the horizontal axis and halve the vertical axis"
msgstr "Stuur vir my die skaalfaktore vir die horisontale en vertikale asse\n\n"
"Voorbeeld: 2 0,5 - dit sal die horisontale as verdubbel en die vertikale as halveer"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:32
msgid "Send me the width and height\n\n"
"Example: 150 200 - this will set the width to 150 and height to 200"
msgstr "Stuur vir my die breedte en hoogte\n\n"
"Voorbeeld: 150 200 - dit stel die breedte op 150 en hoogte op 200"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:56
msgid "Scale"
msgstr "Skaal"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:60
msgid "Select the scale type you'll like to perform"
msgstr "Kies die skaaltipe wat u wil uitvoer"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:68
msgid "The scale values are invalid, try again"
msgstr "Die skaalwaardes is ongeldig, probeer weer"
#: pdf_bot/pdf_processor/split_pdf_processor.py:29
msgid "Split"
msgstr "Verdeel"
#: pdf_bot/pdf_processor/split_pdf_processor.py:33
msgid "The split range is invalid, please try again"
msgstr "Die gesplete reeks is ongeldig, probeer asseblief weer"
#: pdf_bot/pdf_processor/split_pdf_processor.py:55
msgid "Send me the range of pages that you'll like to keep"
msgstr "Stuur vir my die reeks bladsye wat jy wil hou"
#: pdf_bot/pdf_processor/split_pdf_processor.py:56
msgid "General usage"
msgstr "Algemene gebruik"
#: pdf_bot/pdf_processor/split_pdf_processor.py:57
#, python-brace-format
msgid "{range} all pages"
msgstr "{range} alle bladsye"
#: pdf_bot/pdf_processor/split_pdf_processor.py:58
#, python-brace-format
msgid "{range} page 8 only"
msgstr "{range} bladsy 8 slegs"
#: pdf_bot/pdf_processor/split_pdf_processor.py:59
#, python-brace-format
msgid "{range} first three pages"
msgstr "{range} eerste drie bladsye"
#: pdf_bot/pdf_processor/split_pdf_processor.py:60
#, python-brace-format
msgid "{range} from page 8 onward"
msgstr "{range} van bladsy 8 af"
#: pdf_bot/pdf_processor/split_pdf_processor.py:61
#, python-brace-format
msgid "{range} last page only"
msgstr "{range} laaste bladsy net"
#: pdf_bot/pdf_processor/split_pdf_processor.py:62
#, python-brace-format
msgid "{range} all pages except the last page"
msgstr "{range} alle bladsye behalwe die laaste bladsy"
#: pdf_bot/pdf_processor/split_pdf_processor.py:63
#, python-brace-format
msgid "{range} second last page only"
msgstr "{range} tweede laaste bladsy"
#: pdf_bot/pdf_processor/split_pdf_processor.py:64
#, python-brace-format
msgid "{range} last two pages"
msgstr "{range} laaste twee bladsye"
#: pdf_bot/pdf_processor/split_pdf_processor.py:65
#, python-brace-format
msgid "{range} third and second last pages"
msgstr "{range} derde en tweede laaste bladsye"
#: pdf_bot/pdf_processor/split_pdf_processor.py:66
msgid "Advanced usage"
msgstr "Gevorderde gebruik"
#: pdf_bot/pdf_processor/split_pdf_processor.py:67
#, python-brace-format
msgid "{range} pages {pages} and to the end"
msgstr "{range} bladsye {pages} en tot die einde toe"
#: pdf_bot/pdf_processor/split_pdf_processor.py:70
#, python-brace-format
msgid "{range} pages {pages}"
msgstr "{range} bladsye {pages}"
#: pdf_bot/pdf_processor/split_pdf_processor.py:71
#, python-brace-format
msgid "{range} all pages in reversed order"
msgstr "{range} alle bladsye in omgekeerde volgorde"
#: pdf_bot/pdf_processor/split_pdf_processor.py:72
#, python-brace-format
msgid "{range} pages {pages} except {page}"
msgstr "{range} bladsye {pages} behalwe {page}"
#: pdf_bot/pdf_processor/split_pdf_processor.py:75
#, python-brace-format
msgid "{range} pages {pages}"
msgstr "{range} bladsye {pages}"
#: pdf_bot/telegram_internal/telegram_service.py:69
msgid "Your file is too large for me to download and process, please try again with a different file\n\n"
"Note that this limit is enforced by Telegram and there's nothing I can do unless Telegram changes it"
msgstr "Jou lêer is te groot vir my om af te laai en te verwerk, probeer asseblief weer met 'n ander lêer\n\n"
"Let daarop dat hierdie limiet deur Telegram afgedwing word en dat daar niks is wat ek kan doen tensy Telegram dit verander nie"
#: pdf_bot/telegram_internal/telegram_service.py:81
msgid "The file is too large for me to send to you\n\n"
"Note that this limit is enforced by Telegram and there's nothing I can do unless Telegram changes it"
msgstr "Die lêer is te groot vir my om aan u te stuur\n\n"
"Let daarop dat hierdie limiet deur Telegram afgedwing word en dat daar niks is wat ek kan doen tensy Telegram dit verander nie"
#: pdf_bot/telegram_internal/telegram_service.py:156
msgid "Your file is not an image, please try again"
msgstr "Jou lêer is nie 'n beeld nie, probeer asseblief weer"
#: pdf_bot/telegram_internal/telegram_service.py:162
msgid "No image found in your message"
msgstr "Geen beeld in jou boodskap gevind nie"
#: pdf_bot/telegram_internal/telegram_service.py:172
msgid "Your file is not a PDF file, please try again"
msgstr "Jou lêer is nie 'n PDF-lêer nie, probeer asseblief weer"
#: pdf_bot/telegram_internal/telegram_service.py:197
#: pdf_bot/telegram_internal/telegram_service.py:200
msgid "Action cancelled"
msgstr "Aksie gekanselleer"
#: pdf_bot/telegram_internal/telegram_service.py:271
#: pdf_bot/telegram_internal/telegram_service.py:279
msgid "Here is your result file"
msgstr "Hier is u resultaatlêer"
#: pdf_bot/text/text_service.py:21
msgid "Skip"
msgstr "Slaan"
#: pdf_bot/text/text_service.py:40
msgid "Send me the text that you'll like to write into your PDF file"
msgstr "Stuur vir my die teks wat jy in jou PDF-lêer wil skryf"
#: pdf_bot/text/text_service.py:59
#, python-brace-format
msgid "Send me the font that you'll like to use for the PDF file or press {skip} to use the default font"
msgstr "Stuur vir my die lettertipe wat jy vir die PDF-lêer wil gebruik of druk {skip} om die verstekfont te gebruik"
#: pdf_bot/text/text_service.py:62
#, python-brace-format
msgid "See here for the list of supported fonts: {fonts}"
msgstr "Sien hier vir die lys van ondersteunde lettertipes: {fonts}"
#: pdf_bot/text/text_service.py:90
msgid "Unknown font, please try again"
msgstr "Onbekende lettertipe, probeer asseblief weer"
#: pdf_bot/text/text_service.py:108
msgid "Creating your PDF file"
msgstr "Skep jou PDF-lêer"
#: pdf_bot/watermark/watermark_service.py:38
msgid "Send me the PDF file that you'll like to add a watermark"
msgstr "Stuur vir my die PDF-lêer wat jy graag 'n watermerk wil byvoeg"
#: pdf_bot/watermark/watermark_service.py:56
msgid "Send me the watermark PDF file"
msgstr "Stuur vir my die watermerk PDF-lêer"
#: pdf_bot/watermark/watermark_service.py:74
msgid "Adding the watermark onto your PDF file"
msgstr "Voeg die watermerk by jou PDF-lêer"
#: pdf_bot/webpage/webpage_service.py:41
msgid "You've sent me this webpage already and I'm still converting it"
msgstr "Jy het al vir my hierdie webblad gestuur en ek skakel dit nog om"
#: pdf_bot/webpage/webpage_service.py:45
msgid "Converting your webpage into a PDF file"
msgstr "Omskakeling van jou webblad in 'n PDF-lêer"
#: pdf_bot/webpage/webpage_service.py:72
msgid "Unable to reach your webpage"
msgstr "Kan nie jou webblad bereik nie"
#: pdf_bot/webpage/webpage_service.py:84
msgid "Failed to convert your webpage"
msgstr "Kon nie u webblad omskakel nie"
#~ msgid ""
#~ "Send me the first photo that "
#~ "you'll like to beautify or convert "
#~ "into PDF\n"
#~ "\n"
#~ "Note that the photos will be "
#~ "beautified and converted in the order"
#~ " that you send me"
#~ msgstr ""
#~ msgid ""
#~ "Send me the next photo that you'll like to beautify or convert to PDF\n"
#~ "\n"
#~ "Select the task from below if you have sent me all the photos"
#~ msgstr ""
#~ msgid ""
#~ "Welcome to PDF Bot!\n"
#~ "\n"
#~ "*Features*\n"
#~ "- Compare, crop, decrypt, encrypt, "
#~ "merge, rotate, scale, split and add "
#~ "a watermark to a PDF file\n"
#~ "- Extract text and photos in a "
#~ "PDF file and convert a PDF file"
#~ " into photos\n"
#~ "- Beautify and convert photos into PDF format\n"
#~ "- Convert a web page into a PDF file\n"
#~ "\n"
#~ "Type /help to see how to use PDF Bot"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " simply by sending me a PDF "
#~ "file, a photo or a link to a"
#~ " web page.\n"
#~ "\n"
#~ "Some tasks can be performed by "
#~ "using the commands /compare, /merge, "
#~ "/watermark or /photo"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " by sending me one of the "
#~ "followings:\n"
#~ "- PDF files\n"
#~ "- Photos\n"
#~ "- Webpage links\n"
#~ "\n"
#~ "The rest of the tasks can be "
#~ "performed by using the commands "
#~ "/compare, /merge, /photo, /text or "
#~ "/watermark"
#~ msgstr ""
#~ msgid ""
#~ "Welcome to PDF Bot!\n"
#~ "\n"
#~ "*Key features:*\n"
#~ "- Compress, merge, preview, rename, "
#~ "split and add watermark to PDF "
#~ "files\n"
#~ "- Create PDF files from text messages\n"
#~ "- Extract images and text from PDF files\n"
#~ "- Convert PDF files into images\n"
#~ "- Convert webpages and images into PDF files\n"
#~ "- Beautify handwritten notes images into PDF files\n"
#~ "- _And more..._\n"
#~ "\n"
#~ "Type /help to see how to use PDF Bot"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " by sending me one of the "
#~ "followings:\n"
#~ "- PDF files\n"
#~ "- Photos\n"
#~ "- Webpage links\n"
#~ "\n"
#~ "The rest of the tasks can be performed by using the commands below:\n"
#~ "- /compare _PDF files_\n"
#~ "- /merge _PDF files_\n"
#~ "- /photo _convert and combine multiple photos into PDF files_\n"
#~ "- /text _create PDF files from text messages_\n"
#~ "- /watermark _add watermark to PDF files_"
#~ msgstr ""
#~ msgid ""
#~ "Press *Done* if you've sent me all"
#~ " the PDF files that you'll like "
#~ "to merge or keep sending me the"
#~ " PDF files"
#~ msgstr ""
#~ msgid "*{}* has been removed for merging"
#~ msgstr ""
#~ msgid "*{}* has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid "File size reduced by *{:.0%}*, from *{}* to *{}*"
#~ msgstr ""
#~ msgid "Renaming your PDF file into *{}*"
#~ msgstr ""
#~ msgid ""
#~ "Send me the width and height\n"
#~ "\n"
#~ "*Example: 150 200* (this will set the width to 150 and height to 200)"
#~ msgstr ""
#~ msgid ""
#~ "Send me the scaling factors for the horizontal and vertical axes\n"
#~ "\n"
#~ "2 will double the axis and 0.5 will halve the axis\n"
#~ "\n"
#~ "*Example: 2 0.5* (this will double "
#~ "the horizontal axis and halve the "
#~ "vertical axis)"
#~ msgstr ""
#~ msgid "The scaling factors *{}* are invalid, try again"
#~ msgstr ""
#~ msgid "The dimensions *{}* are invalid, try again"
#~ msgstr ""
#~ msgid "Scaling your PDF file, horizontally by *{}* and vertically by *{}*"
#~ msgstr ""
#~ msgid "Scaling your PDF file with width of *{}* and height of *{}*"
#~ msgstr ""
#~ msgid ""
#~ "Send me the range of pages that"
#~ " you'll like to keep. Use ⚡ "
#~ "*INSTANT VIEW* from below or refer "
#~ "to [here](http://telegra.ph/Telegram-PDF-Bot-07-16)"
#~ " for some range examples."
#~ msgstr ""
#~ msgid "*See above for all the text in your PDF file*"
#~ msgstr ""
#~ msgid ""
#~ "Send me the range of pages that"
#~ " you'll like to keep. Use ⚡ "
#~ "<b>INSTANT VIEW</b> from below or refer"
#~ " to [here](http://telegra.ph/Telegram-PDF-"
#~ "Bot-07-16) for some range examples."
#~ msgstr ""
#~ msgid ""
#~ "Send me your feedback or /cancel "
#~ "this action. Note that only English "
#~ "feedback will be forwarded to my "
#~ "developer."
#~ msgstr ""
#~ msgid ""
#~ "Send me your feedback, note that "
#~ "only English feedback will be forwarded"
#~ " to my developer"
#~ msgstr ""
#~ msgid ""
#~ "Send me the range of pages that you'll like to keep\n"
#~ "\n"
#~ "<b>General usage</b>\n"
#~ "<code>: all pages</code>\n"
#~ "<code>22 just the 23rd page</code>\n"
#~ "<code>0:3 the first three pages</code>\n"
#~ "<code>:3 the first three pages</code>\n"
#~ "<code>5: from the 6th page onwards</code>\n"
#~ "<code>-1 last page only</code>\n"
#~ "<code>:-1 all pages but the last page</code>\n"
#~ "<code>-2 second last page only</code>\n"
#~ "<code>-2: last two pages</code>\n"
#~ "<code>-3:-1 third and second last pages only</code>\n"
#~ "\n"
#~ "<b>Advanced usage</b>\n"
#~ "<code>::2 pages 0 2 4 ... to the end</code>\n"
#~ "<code>1:10:2 pages 1 3 5 7 9</code>\n"
#~ "<code>::-1 all pages in reversed order</code>\n"
#~ "<code>3:0:-1 pages 3 2 1 but not 0</code>\n"
#~ "<code>2::-1 pages 2 1 0</code>"
#~ msgstr ""
#~ msgid "Set Language"
#~ msgstr ""
#~ msgid "Send me the amount that you'll like to support PDF Bot"
#~ msgstr ""
#~ msgid "Say Awesome 🤩 (Custom)"
#~ msgstr ""
#~ msgid "The amount you sent is invalid, try again. {}"
#~ msgstr ""
#~ msgid "Select the font or select"
#~ msgstr ""
#~ msgid "to use the default font"
#~ msgstr ""
#~ msgid "Unknown font, please select a font from the list"
#~ msgstr ""
#~ msgid "Say Thanks 😁 ($1)"
#~ msgstr ""
#~ msgid "Coffee ☕ ($3)"
#~ msgstr ""
#~ msgid "Beer 🍺 ($5)"
#~ msgstr ""
#~ msgid "Meal 🍲 ($10)"
#~ msgstr ""
#~ msgid ""
#~ "Welcome to PDF Bot!\n"
#~ "\n"
#~ "<b>Key features:</b>\n"
#~ "- Compress, merge, preview, rename, "
#~ "split and add watermark to PDF "
#~ "files\n"
#~ "- Create PDF files from text messages\n"
#~ "- Extract images and text from PDF files\n"
#~ "- Convert PDF files into images\n"
#~ "- Convert webpages and images into PDF files\n"
#~ "- Beautify handwritten notes images into PDF files\n"
#~ "- <b><i>And more...</i></b>\n"
#~ "\n"
#~ "Type /help to see how to use PDF Bot"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " by sending me one of the "
#~ "followings:\n"
#~ "- PDF files\n"
#~ "- Photos\n"
#~ "- Webpage links\n"
#~ "\n"
#~ "The rest of the tasks can be performed by using the commands below:\n"
#~ "/compare - compare PDF files\n"
#~ "/merge - merge PDF files\n"
#~ "/photo - convert and combine multiple photos into PDF files\n"
#~ "/text - create PDF files from text messages\n"
#~ "/watermark - add watermark to PDF files"
#~ msgstr ""
#~ msgid "The file you sent is not a PDF file, try again"
#~ msgstr ""
#~ msgid ""
#~ "The PDF file you sent is too large for me to download\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid "Something went wrong, start over again"
#~ msgstr ""
#~ msgid ""
#~ "Your PDF file seems to be invalid and I couldn't open and read it\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid ""
#~ "Your {} PDF file is encrypted and you'll have to decrypt it first\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid ""
#~ "Your PDF file is encrypted and you'll have to decrypt it first\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid "You've sent me these {} so far:\n"
#~ msgstr ""
#~ msgid ""
#~ "Send me one of the PDF files that you'll like to compare\n"
#~ "\n"
#~ "Note that I can only look for differences in text"
#~ msgstr ""
#~ msgid "There are no differences in text between your PDF files"
#~ msgstr ""
#~ msgid ""
#~ "Send me the PDF files that you'll like to merge\n"
#~ "\n"
#~ "Note that the files will be merged in the order that you send me"
#~ msgstr ""
#~ msgid "The file you've sent is not a PDF file"
#~ msgstr ""
#~ msgid "The PDF file you've sent is too large for me to download"
#~ msgstr ""
#~ msgid ""
#~ "Press <b>Done</b> if you've sent me "
#~ "all the PDF files that you'll like"
#~ " to merge or keep sending me "
#~ "the PDF files"
#~ msgstr ""
#~ msgid "<b>{}</b> has been removed for merging"
#~ msgstr ""
#~ msgid "You've only sent me one PDF file."
#~ msgstr ""
#~ msgid ""
#~ "I can't merge your PDF files as"
#~ " I couldn't open and read \"{}\". "
#~ "Ensure that it is not encrypted"
#~ msgstr ""
#~ msgid ""
#~ "Send me the photos that you'll "
#~ "like to beautify or convert into a"
#~ " PDF file\n"
#~ "\n"
#~ "Note that the photos will be "
#~ "beautified and converted in the order"
#~ " that you send me"
#~ msgstr ""
#~ msgid "The file you've sent is not a photo"
#~ msgstr ""
#~ msgid "The photo you've sent is too large for me to download"
#~ msgstr ""
#~ msgid "<b>{}</b> has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid ""
#~ "Send me the font that you'll like"
#~ " to use for the PDF file or "
#~ "skip to use the default font\n"
#~ "\n"
#~ "See here for the list of supported fonts:"
#~ msgstr ""
#~ msgid "File size reduced by <b>{:.0%}</b>, from <b>{}</b> to <b>{}</b>"
#~ msgstr ""
#~ msgid "Something went wrong, try again"
#~ msgstr ""
#~ msgid ""
#~ "Send me a number between {} and"
#~ " {}. This is the percentage of "
#~ "margin space to retain between the "
#~ "content in your PDF file and the"
#~ " page"
#~ msgstr ""
#~ msgid ""
#~ "Send me a number that you'll like"
#~ " to adjust the margin size. Positive"
#~ " numbers will decrease the margin "
#~ "size and negative numbers will increase"
#~ " it"
#~ msgstr ""
#~ msgid "The number must be between {} and {}, try again"
#~ msgstr ""
#~ msgid "The number is invalid, try again"
#~ msgstr ""
#~ msgid "Your PDF file seems to be invalid and I couldn't open and read it"
#~ msgstr ""
#~ msgid "The decryption password is incorrect, try to send it again"
#~ msgstr ""
#~ msgid "Your PDF file is encrypted with a method that I cannot decrypt"
#~ msgstr ""
#~ msgid ""
#~ "Your PDF file is too big for me to download\n"
#~ "\n"
#~ "I can't perform any tasks on it"
#~ msgstr ""
#~ msgid ""
#~ "Your photo is too large for me "
#~ "to download. I can't beautify or "
#~ "convert your photo"
#~ msgstr ""
#~ msgid ""
#~ "File names can't contain any of the following characters:\n"
#~ "{}\n"
#~ "Send me another file name"
#~ msgstr ""
#~ msgid "Renaming your PDF file into <b>{}</b>"
#~ msgstr ""
#~ msgid ""
#~ "Send me the width and height\n"
#~ "\n"
#~ "<b>Example: 150 200</b> (this will set"
#~ " the width to 150 and height to"
#~ " 200)"
#~ msgstr ""
#~ msgid ""
#~ "Send me the scaling factors for the horizontal and vertical axes\n"
#~ "\n"
#~ "<b>Example: 2 0.5</b> (this will double"
#~ " the horizontal axis and halve the"
#~ " vertical axis)"
#~ msgstr ""
#~ msgid "The scaling factors <b>{}</b> are invalid, try again"
#~ msgstr ""
#~ msgid "The dimensions <b>{}</b> are invalid, try again"
#~ msgstr ""
#~ msgid ""
#~ "Scaling your PDF file, horizontally by"
#~ " <b>{}</b> and vertically by <b>{}</b>"
#~ msgstr ""
#~ msgid "Scaling your PDF file with width of <b>{}</b> and height of <b>{}</b>"
#~ msgstr ""
#~ msgid "all pages"
#~ msgstr ""
#~ msgid "page 8 only"
#~ msgstr ""
#~ msgid "first three pages"
#~ msgstr ""
#~ msgid "from page 8 onward"
#~ msgstr ""
#~ msgid "last page only"
#~ msgstr ""
#~ msgid "all pages except the last page"
#~ msgstr ""
#~ msgid "second last page only"
#~ msgstr ""
#~ msgid "last two pages"
#~ msgstr ""
#~ msgid "third and second last pages"
#~ msgstr ""
#~ msgid "pages"
#~ msgstr ""
#~ msgid "to the end"
#~ msgstr ""
#~ msgid "all pages in reversed order"
#~ msgstr ""
#~ msgid "except"
#~ msgstr ""
#~ msgid "The range is invalid. Try again"
#~ msgstr ""
#~ msgid "<b>See above for all the text in your PDF file</b>"
#~ msgstr ""
#~ msgid "Type {} to see how to use PDF Bot"
#~ msgstr ""
#~ msgid "{} - compare PDF files"
#~ msgstr ""
#~ msgid "{} - merge PDF files"
#~ msgstr ""
#~ msgid "{} - convert and combine multiple photos into PDF files"
#~ msgstr ""
#~ msgid "{} - create PDF files from text messages"
#~ msgstr ""
#~ msgid "{} - add watermark to PDF files"
#~ msgstr ""
#~ msgid "Your language has been set to {}"
#~ msgstr ""
#~ msgid "Your {} PDF file is encrypted and you'll have to decrypt it first"
#~ msgstr ""
#~ msgid "You've sent me these {} so far:"
#~ msgstr ""
#~ msgid ""
#~ "Press {} if you've sent me all "
#~ "the PDF files that you'll like to"
#~ " merge or keep sending me the "
#~ "PDF files"
#~ msgstr ""
#~ msgid "{} has been removed for merging"
#~ msgstr ""
#~ msgid "I couldn't merge your PDF files as this file is invalid: {}"
#~ msgstr ""
#~ msgid "{} has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid ""
#~ "Send me the font that you'll like"
#~ " to use for the PDF file or "
#~ "press {} to use the default font"
#~ msgstr ""
#~ msgid "See here for the list of supported fonts: {}"
#~ msgstr ""
#~ msgid "Renaming your PDF file into {}"
#~ msgstr ""
#~ msgid "Rotating your PDF file clockwise by {} degrees"
#~ msgstr ""
#~ msgid "Example: 150 200"
#~ msgstr ""
#~ msgid "Example: 2 0.5"
#~ msgstr ""
#~ msgid "The scaling factors {} are invalid, please try again"
#~ msgstr ""
#~ msgid "The dimensions {} are invalid, please try again"
#~ msgstr ""
#~ msgid "{} all pages"
#~ msgstr ""
#~ msgid "{} page 8 only"
#~ msgstr ""
#~ msgid "{} first three pages"
#~ msgstr ""
#~ msgid "{} from page 8 onward"
#~ msgstr ""
#~ msgid "{} last page only"
#~ msgstr ""
#~ msgid "{} all pages except the last page"
#~ msgstr ""
#~ msgid "{} second last page only"
#~ msgstr ""
#~ msgid "{} last two pages"
#~ msgstr ""
#~ msgid "{} third and second last pages"
#~ msgstr ""
#~ msgid "{} all pages in reversed order"
#~ msgstr ""
#~ msgid "See above for all the text in your PDF file"
#~ msgstr ""
#~ msgid "You can perform most of the tasks by sending me one of the followings"
#~ msgstr ""
#~ msgid "The rest of the tasks can be performed by using the following commands"
#~ msgstr ""
#~ msgid "Your PDF file seems to be invalid and I couldn't open and process it"
#~ msgstr ""
#~ msgid "Your PDF file is too large for me to download"
#~ msgstr ""
#~ msgid "Your file is too large for me to download"
#~ msgstr ""
#~ msgid "Your photo is too large for me to download"
#~ msgstr ""
#~ msgid "{range} pages {results} and to the end"
#~ msgstr ""
#~ msgid "{range} pages {results}"
#~ msgstr ""
#~ msgid "{range} pages {results} except {except_text}"
#~ msgstr ""
#~ msgid "{range} pages {results}"
#~ msgstr ""
#~ msgid "Your PDF file is too large for me to download and process"
#~ msgstr ""
#~ msgid "Your PDF file is too big for me to download"
#~ msgstr ""
#~ msgid "Say Thanks {emoji} ({value})"
#~ msgstr ""
#~ msgid "Coffee {emoji} ({value})"
#~ msgstr ""
#~ msgid "Beer {emoji} ({value})"
#~ msgstr ""
#~ msgid "Meal {emoji} ({value})"
#~ msgstr ""
#~ msgid "Failed to process, please try again"
#~ msgstr ""
#~ msgid "Extract Photos"
#~ msgstr ""
#~ msgid "To Photos"
#~ msgstr ""
#~ msgid "Photos"
#~ msgstr ""
#~ msgid "- Photos"
#~ msgstr ""
#~ msgid "{command} - convert and combine multiple photos into PDF files"
#~ msgstr ""
#~ msgid ""
#~ "Send me the photos that you'll "
#~ "like to beautify or convert into a"
#~ " PDF file"
#~ msgstr ""
#~ msgid ""
#~ "Note that the photos will be "
#~ "beautified and converted in the order"
#~ " that you send me"
#~ msgstr ""
#~ msgid "Your file is not a photo"
#~ msgstr ""
#~ msgid "photos"
#~ msgstr ""
#~ msgid ""
#~ "Select the task from below if "
#~ "you've sent me all the photos, or"
#~ " keep sending me the photos"
#~ msgstr ""
#~ msgid "Beautifying and converting your photos"
#~ msgstr ""
#~ msgid "Converting your photos into PDF"
#~ msgstr ""
#~ msgid "Your photo is too large for me to download and process"
#~ msgstr ""
#~ msgid "Converting your PDF file into photos"
#~ msgstr ""
#~ msgid "Extracting all the photos in your PDF file"
#~ msgstr ""
#~ msgid "I couldn't find any photos in your PDF file"
#~ msgstr ""
#~ msgid "See above for all your photos"
#~ msgstr ""
#~ msgid ""
#~ "Your {file_type} PDF file is encrypted"
#~ " and you'll have to decrypt it "
#~ "first"
#~ msgstr ""
#~ msgid "watermark"
#~ msgstr ""
#~ msgid "PDF files"
#~ msgstr ""
#~ msgid "Your file is not a PDF file"
#~ msgstr ""
#~ msgid ""
#~ "Your file is not a PDF file, "
#~ "please try again and ensure that "
#~ "your file has the .pdf extension"
#~ msgstr ""
#~ msgid "Converting your PDF file to black and white"
#~ msgstr ""
#~ msgid "Compressing your PDF file"
#~ msgstr ""
#~ msgid ""
#~ "The number must be between {min_percent}"
#~ " and {max_percent}, please try again"
#~ msgstr ""
#~ msgid "The number is invalid, please try again"
#~ msgstr ""
#~ msgid "Cropping your PDF file"
#~ msgstr ""
#~ msgid "Decrypting your PDF file"
#~ msgstr ""
#~ msgid "Your PDF file is invalid and I couldn't open and process it"
#~ msgstr ""
#~ msgid "The decryption password is incorrect, please try again"
#~ msgstr ""
#~ msgid "Encrypting your PDF file"
#~ msgstr ""
#~ msgid "Adding an OCR text layer to your PDF file"
#~ msgstr ""
#~ msgid "Renaming your PDF file into {file_name}"
#~ msgstr ""
#~ msgid "Rotating your PDF file clockwise by {degree} degrees"
#~ msgstr ""
#~ msgid ""
#~ "Scaling your PDF file, horizontally by"
#~ " {horizontal} and vertically by {vertical}"
#~ msgstr ""
#~ msgid "Scaling your PDF file with width of {width} and height of {height}"
#~ msgstr ""
#~ msgid "Splitting your PDF file"
#~ msgstr ""
#~ msgid "Select how you'll like me to send the text to you"
#~ msgstr ""
#~ msgid "Extracting text from your PDF file"
#~ msgstr ""
#~ msgid "I couldn't find any text in your PDF file"
#~ msgstr ""
#~ msgid ""
#~ "Your image is too large for me "
#~ "to download and process, please try "
#~ "again with a differnt image\n"
#~ "\n"
#~ "Note that this is a Telegram Bot"
#~ " limitation and there's nothing I can"
#~ " do unless Telegram changes this "
#~ "limit"
#~ msgstr ""
#~ msgid "Testing"
#~ msgstr ""
#~ msgid ""
#~ "Your file is too large for me "
#~ "to download and process, please try "
#~ "again with a differnt file\n"
#~ "\n"
#~ "Note that this is a Telegram Bot"
#~ " limitation and there's nothing I can"
#~ " do unless Telegram changes this "
#~ "limit"
#~ msgstr ""
#~ msgid "Remove Last File"
#~ msgstr ""
#~ msgid "Extracting a preview for your PDF file"
#~ msgstr ""
#~ msgid "Converting your PDF file into images"
#~ msgstr ""
#~ msgid "Select the result file format"
#~ msgstr ""
#~ msgid "Extracting all the images in your PDF file"
#~ msgstr ""
#~ msgid "I couldn't find any images in your PDF file"
#~ msgstr ""
#~ msgid "See above for all your images"
#~ msgstr ""
#~ msgid "Your image is too large for me to download and process"
#~ msgstr ""
#~ msgid "File name unavailable"
#~ msgstr ""
#~ msgid "Your file is not an image"
#~ msgstr ""
#~ msgid "Your file is too large for me to download and process"
#~ msgstr ""
#~ msgid "images"
#~ msgstr ""
#~ msgid "{file_name} has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid "Beautifying and converting your images"
#~ msgstr ""
#~ msgid "Converting your images into PDF"
#~ msgstr ""
#~ msgid ""
#~ "Send me your feedback (only English "
#~ "feedback will be forwarded to my "
#~ "developer)"
#~ msgstr ""
#~ msgid "Thank you for your feedback, I've already forwarded it to my developer"
#~ msgstr ""
#~ msgid "Something went wrong"
#~ msgstr ""
#~ msgid ""
#~ "Timed out processing your web page, "
#~ "your web page is probably too "
#~ "complex to process"
#~ msgstr ""
#~ msgid "Something went wrong, please start over again"
#~ msgstr ""
#~ msgid "Your file is invalid and I couldn't open and process it"
#~ msgstr ""
#~ msgid "Your PDF file is already encrypted"
#~ msgstr ""
#~ msgid "Your PDF file is encrypted and you'll have to decrypt it first"
#~ msgstr ""
#~ msgid "You've sent me these {file_type} so far:"
#~ msgstr ""
#~ msgid "The result file is too large for me to send to you"
#~ msgstr ""
#~ msgid ""
#~ "The button has expired, please try "
#~ "again with a new message/query then "
#~ "press the new button"
#~ msgstr ""
#~ msgid "Invalid rotation degree, try again"
#~ msgstr ""
#~ msgid "The scaling factors {values} are invalid, please try again"
#~ msgstr ""
#~ msgid "The dimensions {values} are invalid, please try again"
#~ msgstr ""
#~ msgid "Send me the scaling factors for the horizontal and vertical axes"
#~ msgstr ""
#~ msgid "This will double the horizontal axis and halve the vertical axis"
#~ msgstr ""
#~ msgid "Send me the width and height"
#~ msgstr ""
#~ msgid "This will set the width to 150 and height to 200"
#~ msgstr ""
#~ msgid "File names can't contain any of the following characters:"
#~ msgstr ""
#~ msgid "Please try again"
#~ msgstr ""
#~ msgid "The range is invalid, please try again"
#~ msgstr ""
#~ msgid "By scaling factor"
#~ msgstr ""
#~ msgid "Select the scale type that you'll like to perform"
#~ msgstr ""
#~ msgid "The values {values} are invalid, please try again"
#~ msgstr ""
#~ msgid "By margin size"
#~ msgstr ""
#~ msgid "Select the crop type that you'll like to perform"
#~ msgstr ""
#~ msgid ""
#~ "The number {number} is not between "
#~ "{min_percent} and {max_percent}, please try"
#~ " again"
#~ msgstr ""
#~ msgid "The number {number} is invalid, please try again"
#~ msgstr ""
#~ msgid "Send me a number between {min_percent} and {max_percent}"
#~ msgstr ""
#~ msgid ""
#~ "This is the percentage of margin "
#~ "space to retain between the content "
#~ "in your PDF file and the page"
#~ msgstr ""
#~ msgid "Send me a number that you'll like to adjust the margin size"
#~ msgstr ""
#~ msgid ""
#~ "Positive numbers will decrease the "
#~ "margin size and negative numbers will"
#~ " increase it"
#~ msgstr ""
#~ msgid ""
#~ "Select the task that you'll like "
#~ "to perform from below or select "
#~ "from the buttons"
#~ msgstr ""
#~ msgid "By Percentage"
#~ msgstr ""
#~ msgid "By Margin Size"
#~ msgstr ""
#~ msgid "Extract Images"
#~ msgstr ""
#~ msgid "To Images"
#~ msgstr ""
#~ msgid "Compressed"
#~ msgstr ""
#~ msgid "Images"
#~ msgstr ""
#~ msgid "To Dimensions"
#~ msgstr ""
#~ msgid "Extract Text"
#~ msgstr ""
#~ msgid "Text Message"
#~ msgstr ""
#~ msgid "Text File"
#~ msgstr ""
#~ msgid "Black & White"
#~ msgstr ""
#~ msgid "You've sent me this web page already and I'm still converting it"
#~ msgstr ""
#~ msgid "Converting your web page into a PDF file"
#~ msgstr ""
#~ msgid "Unable to reach your web page"
#~ msgstr ""
#~ msgid "Failed to convert your web page"
#~ msgstr ""
#~ msgid "Something went wrong, start over with your file or command"
#~ msgstr ""
#~ msgid "Your PDF file is encrypted"
#~ msgstr ""
#~ msgid "I couldn't merge your PDF files as this file is invalid: {file_name}"
#~ msgstr ""
#~ msgid ""
#~ "File names can't contain any of "
#~ "the following characters, please try "
#~ "again:\n"
#~ "{invalid_chars}"
#~ msgstr ""
#~ msgid ""
#~ "Your file is too large for me "
#~ "to download and process, please try "
#~ "again with a differnt file\n"
#~ "\n"
#~ "Note that this limit is enforced "
#~ "by Telegram and there's nothing I "
#~ "can do unless Telegram changes it"
#~ msgstr ""
================================================
FILE: locale/am_ET/LC_MESSAGES/pdf_bot.po
================================================
# locale translations for telegram-pdf-bot.
# Copyright (C) 2021 zeshuaro
# This file is distributed under the same license as the telegram-pdf-bot
# project.
# zeshuaro <zeshuaro@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: telegram-pdf-bot\n"
"Report-Msgid-Bugs-To: zeshuaro@gmail.com\n"
"POT-Creation-Date: 2025-10-19 06:16+0000\n"
"PO-Revision-Date: 2025-10-19 06:16\n"
"Last-Translator: \n"
"Language: am\n"
"Language-Team: Amharic\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.17.0\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Crowdin-Project: telegram-pdf-bot\n"
"X-Crowdin-Project-ID: 370289\n"
"X-Crowdin-Language: am\n"
"X-Crowdin-File: pdf_bot.po\n"
"X-Crowdin-File-ID: 88\n"
#: pdf_bot/consts.py:11
msgid "Cancel"
msgstr "ሰርዝ"
#: pdf_bot/consts.py:12
msgid "Done"
msgstr "ተከናውኗል"
#: pdf_bot/consts.py:13 pdf_bot/telegram_internal/telegram_service.py:48
msgid "Back"
msgstr "ተመለስ"
#: pdf_bot/consts.py:23
msgid "Something went wrong, start over with your file or command"
msgstr "አንድ ስህተት ሄደ, በእርስዎ ፋይል ወይም ትዕዛዝ እንደገና ይጀምሩ"
#: pdf_bot/cli/cli_service.py:35
msgid "Failed to complete process"
msgstr "ሂደቱን ማጠናቀቅ አልተቻለም"
#: pdf_bot/command/command_service.py:38
msgid "Welcome to PDF Bot!"
msgstr "ወደ ፒዲኤፍ ቦት እንኳን በደህና መጡ!"
#: pdf_bot/command/command_service.py:39
msgid "Key features:"
msgstr "ቁልፍ ባህሪያት:"
#: pdf_bot/command/command_service.py:41
msgid "- Compress, merge, preview, rename, split and add watermark to PDF files"
msgstr "- ወደ ፒዲኤፍ ፋይሎች ይጭመቁ ፣ ያዋህዱ ፣ ቅድመ -እይታ ፣ እንደገና ይሰይሙ ፣ ይከፋፈሉ እና የውሃ ምልክት ማድረጊያ ያክሉ"
#: pdf_bot/command/command_service.py:43
msgid "- Create PDF files from text messages"
msgstr "- ከጽሑፍ መልእክቶች የፒዲኤፍ ፋይሎችን ይፍጠሩ"
#: pdf_bot/command/command_service.py:44
msgid "- Extract images and text from PDF files"
msgstr "- ምስሎችን እና ጽሑፍን ከፒዲኤፍ ፋይሎች ያውጡ"
#: pdf_bot/command/command_service.py:45
msgid "- Convert PDF files into images"
msgstr "- የፒዲኤፍ ፋይሎችን ወደ ምስሎች ይለውጡ"
#: pdf_bot/command/command_service.py:46
msgid "- Convert webpages and images into PDF files"
msgstr "- ድረ -ገጾችን እና ምስሎችን ወደ ፒዲኤፍ ፋይሎች ይለውጡ"
#: pdf_bot/command/command_service.py:47
msgid "- Beautify handwritten notes images into PDF files"
msgstr "- በእጅ የተፃፉ ማስታወሻዎችን ምስሎች ወደ ፒዲኤፍ ፋይሎች ያምሩ"
#: pdf_bot/command/command_service.py:48
msgid "- And more..."
msgstr "- ሌሎችም..."
#: pdf_bot/command/command_service.py:49
#, python-brace-format
msgid "Type {command} to see how to use PDF Bot"
msgstr "ፒዲኤፍ ቦትን እንዴት እንደሚጠቀሙ ለማየት {command} ይተይቡ"
#: pdf_bot/command/command_service.py:58
msgid "Set Language 🌎"
msgstr "ቋንቋ አዘጋጅ 🌎"
#: pdf_bot/command/command_service.py:60
#: pdf_bot/telegram_internal/telegram_service.py:222
msgid "Join Channel"
msgstr "ቻናሉን ይቀላቀሉ"
#: pdf_bot/command/command_service.py:61 pdf_bot/payment/payment_service.py:74
#: pdf_bot/telegram_internal/telegram_service.py:223
msgid "Support PDF Bot"
msgstr "የፒዲኤፍ ቦትን ይደግፉ"
#: pdf_bot/command/command_service.py:70
msgid "You can perform most of the tasks by sending me one of the followings:"
msgstr "ከሚከተሉት ውስጥ አንዱን በመላክ አብዛኞቹን ተግባራት ማከናወን ይችላሉ-"
#: pdf_bot/command/command_service.py:71
msgid "- PDF files"
msgstr "- ፒዲኤፍ ፋይሎች"
#: pdf_bot/command/command_service.py:72
msgid "- Images"
msgstr "- ምስሎች"
#: pdf_bot/command/command_service.py:73
msgid "- Webpage links"
msgstr "- የድር አገናኞች"
#: pdf_bot/command/command_service.py:74
msgid "The rest of the tasks can be performed by using the following commands:"
msgstr "ቀሪዎቹ ተግባራት የሚከተሉትን ትዕዛዞች በመጠቀም ሊከናወኑ ይችላሉ-"
#: pdf_bot/command/command_service.py:75
#, python-brace-format
msgid "{command} - compare PDF files"
msgstr "{command} - የፒዲኤፍ ፋይሎችን ያወዳድሩ"
#: pdf_bot/command/command_service.py:76
#, python-brace-format
msgid "{command} - merge PDF files"
msgstr "{command} - የፒዲኤፍ ፋይሎችን ያዋህዱ"
#: pdf_bot/command/command_service.py:78
#, python-brace-format
msgid "{command} - convert and combine multiple images into PDF files"
msgstr "{command} - ብዙ ምስሎችን ወደ PDF ፋይሎች መቀየር እና ማቀናጀት"
#: pdf_bot/command/command_service.py:80
#, python-brace-format
msgid "{command} - create PDF files from text messages"
msgstr "{command} - ከጽሑፍ መልእክቶች የፒዲኤፍ ፋይሎችን ይፍጠሩ"
#: pdf_bot/command/command_service.py:83
#, python-brace-format
msgid "{command} - add watermark to PDF files"
msgstr "{command} - በፒዲኤፍ ፋይሎች ላይ የውሃ ምልክት ያክሉ"
#: pdf_bot/compare/compare_service.py:41
msgid "Send me one of the PDF files that you'll like to compare"
msgstr "ለማወዳደር ከሚወዷቸው የፒዲኤፍ ፋይሎች ውስጥ አንዱን ላኩልኝ"
#: pdf_bot/compare/compare_service.py:42
msgid "Note that I can only look for text differences"
msgstr "የጽሑፍ ልዩነቶችን ብቻ መፈለግ እንደምችል ልብ ይበሉ"
#: pdf_bot/compare/compare_service.py:64
msgid "Send me the other PDF file that you'll like to compare"
msgstr "ለማወዳደር የምትፈልገውን ሌላ የፒዲኤፍ ፋይል ላክልኝ"
#: pdf_bot/compare/compare_service.py:83
msgid "Comparing your PDF files"
msgstr "የፒዲኤፍ ፋይሎችዎን ማወዳደር"
#: pdf_bot/compare/compare_service.py:91
msgid "There are no text differences between your PDF files"
msgstr "በፒዲኤፍ ፋይሎችዎ መካከል ምንም የጽሑፍ ልዩነቶች የሉም"
#: pdf_bot/error/error_handler.py:38 pdf_bot/error/error_handler.py:44
#: pdf_bot/telegram_internal/telegram_service.py:102
#: pdf_bot/telegram_internal/telegram_service.py:118
msgid "Something went wrong, please try again"
msgstr "የሆነ ችግር ተፈጥሯል ፣ እባክዎ እንደገና ይሞክሩ"
#: pdf_bot/error/error_handler.py:58 pdf_bot/error/error_service.py:17
msgid "The button has expired, start over with your file or command"
msgstr "መተግበሪያው አልፏል, በእርስዎ ፋይል ወይም ትዕዛዝ እንደገና ይጀምሩ"
#: pdf_bot/error/error_handler.py:60
msgid "The resulted image is invalid, try again"
msgstr "የተገኘው ምስል ተቀባይነት የለውም, እንደገና ይሞክሩ"
#: pdf_bot/feedback/feedback_service.py:31
msgid "Send me your feedback in English"
msgstr "አስተያየታችሁን በእንግሊዝኛ ላኩልኝ"
#: pdf_bot/feedback/feedback_service.py:54
msgid "The feedback is not in English, try again"
msgstr "አስተያየትዎን ይላኩልኝ ወይም /cancel ይህንን እርምጃ ይሰርዙ። ወደ እኔ ገንቢ የሚተላለፈው የእንግሊዝኛ ጽሑፍ ብቻ መሆኑን ልብ ይበሉ።"
#: pdf_bot/feedback/feedback_service.py:58
msgid "Thank you for your feedback, I've forwarded it to my developer"
msgstr "ስለሰጠኸው አስተያየት አመሰግናለሁ፣ ለታዳጊዬ አስተናግደዋለሁ"
#: pdf_bot/file/file_service.py:54
msgid "Your file is too big for me to download and process"
msgstr "ለማውረድ እና ለማስኬድ ፋይልዎ ለእኔ በጣም ትልቅ ነው"
#: pdf_bot/file/file_service.py:56
msgid "Note that this is a Telegram Bot limitation and there's nothing I can do unless Telegram changes this limit"
msgstr "ይህ የቴሌግራም ቦት ውስንነት መሆኑን እና ቴሌግራም ይህንን ገደብ ካልቀየረ የማደርገው ምንም ነገር የለም"
#: pdf_bot/file_processor/abstract_file_processor.py:105
#: pdf_bot/file_processor/abstract_file_processor.py:163
msgid "Processing your file"
msgstr "ፋይልዎን ማሰናዳት"
#: pdf_bot/file_processor/file_task_mixin.py:69
msgid "Select the task that you'll like to perform"
msgstr "ማከናወን የሚወዱትን ተግባር ይምረጡ"
#: pdf_bot/image_handler/batch_image_service.py:20
#: pdf_bot/image_processor/beautify_image_processor.py:24
msgid "Beautify"
msgstr "አሳምር"
#: pdf_bot/image_handler/batch_image_service.py:21
#: pdf_bot/image_processor/image_to_pdf_processor.py:24
msgid "To PDF"
msgstr "ወደ ፒዲኤፍ"
#: pdf_bot/image_handler/batch_image_service.py:22
#: pdf_bot/merge/merge_service.py:19
msgid "Remove last file"
msgstr "የመጨረሻውን ፋይል አስወግድ"
#: pdf_bot/image_handler/batch_image_service.py:42
msgid "Send me the images that you'll like to beautify or convert into a PDF file"
msgstr "ማስዋብ ወይም ወደ PDF ፋይል መቀየር የምትወዱትን ምስሎች ላኩልኝ"
#: pdf_bot/image_handler/batch_image_service.py:45
msgid "Note that the images will be beautified and converted in the order that you send me"
msgstr "ምስሎቹ አንተ በምትልከኝ ቅደም ተከተል ውብ እንደሚሆኑና እንደሚለወጡ ልብ በል"
#: pdf_bot/image_handler/batch_image_service.py:93
msgid "You've sent me these images so far:"
msgstr "እነዚህን ምስሎች እስካሁን ልከኸኛል።"
#: pdf_bot/image_handler/batch_image_service.py:107
msgid "Select the task from below if you've sent me all the images, or keep sending me the images"
msgstr "ሁሉንም ምስሎች ልከውልኛል ከሆነ ከታች ያለውን ስራ ይምረጡ, ወይም ምስሎቹን መላክቀጥሉን ቀጥሉ"
#: pdf_bot/image_handler/batch_image_service.py:127
msgid "You've already removed all the images you've sent me"
msgstr "የላክኸኝን ምስሎች በሙሉ አስቀድመህ አስወግደሃል"
#: pdf_bot/image_handler/batch_image_service.py:131
#, python-brace-format
msgid "{file_name} has been removed"
msgstr "{file_name} ተወግዷል"
#: pdf_bot/image_handler/batch_image_service.py:151
msgid "You haven't sent me any images"
msgstr "ምንም ምስል አልላክኸኝም"
#: pdf_bot/image_handler/batch_image_service.py:154
msgid "You've only sent me one image"
msgstr "አንድ ምስል ብቻ ልከኸኛል"
#: pdf_bot/image_handler/batch_image_service.py:171
msgid "Beautifying and converting your images into a PDF file"
msgstr "የእርስዎን ምስሎች ወደ PDF ፋይል መቀየር እና ማስዋብ"
#: pdf_bot/image_handler/batch_image_service.py:173
msgid "Converting your images into a PDF file"
msgstr "የእርስዎን ምስሎች ወደ PDF ፋይል መቀየር"
#: pdf_bot/language/language_service.py:87
msgid "Select your language"
msgstr "ቋንቋዎን ይምረጡ"
#: pdf_bot/language/language_service.py:119
#, python-brace-format
msgid "Your language has been set to {language}"
msgstr "ቋንቋዎ ወደ {language}"
#: pdf_bot/merge/merge_service.py:38
msgid "Send me the PDF files that you'll like to merge"
msgstr "ልታዋህዳቸው የምትፈልጋቸውን የፒዲኤፍ ፋይሎች ላክልኝ"
#: pdf_bot/merge/merge_service.py:39
msgid "Note that the files will be merged in the order that you send me"
msgstr "ልብ በሉኝ ቅደም ተከተል ውስጥ ፋይሎቹ እንደሚዋሃዱ ልብ ይበሉ"
#: pdf_bot/merge/merge_service.py:85
msgid "You've sent me these PDF files so far:"
msgstr "እስከ አሁን ድረስ እነዚህን የፒዲኤፍ ፋይሎች ልከኸኛል"
#: pdf_bot/merge/merge_service.py:99
#, python-brace-format
msgid "Press {done} if you've sent me all the PDF files that you'll like to merge or keep sending me the PDF files"
msgstr "የፒዲኤፍ ፋይሎችን ማዋሃድ ወይም መቀጠል የሚፈልጓቸውን ሁሉንም የፒዲኤፍ ፋይሎች ከላኩልኝ {done}"
#: pdf_bot/merge/merge_service.py:120
msgid "You've already removed all the PDF files you've sent me"
msgstr "የላክኸኝን የፒዲኤፍ ፋይሎች በሙሉ አስወግደሃል"
#: pdf_bot/merge/merge_service.py:124
#, python-brace-format
msgid "{file_name} has been removed for merging"
msgstr "{file_name} በማዋሃድ ተወግዷል"
#: pdf_bot/merge/merge_service.py:146
msgid "You haven't sent me any PDF files"
msgstr "ምንም የፒዲኤፍ ፋይሎች አልላኩልኝም"
#: pdf_bot/merge/merge_service.py:149
msgid "You've only sent me one PDF file"
msgstr "አንድ የፒዲኤፍ ፋይል ብቻ ልከውልኛል"
#: pdf_bot/merge/merge_service.py:162
msgid "Merging your PDF files"
msgstr "የፒዲኤፍ ፋይሎችዎን ማዋሃድ"
#: pdf_bot/payment/payment_service.py:26
#, python-brace-format
msgid "{message} {emoji} (${value})"
msgstr "{message} {emoji} (${value})"
#: pdf_bot/payment/payment_service.py:30
msgid "Say Thanks"
msgstr "አመሰግናለሁ በሉ"
#: pdf_bot/payment/payment_service.py:31
msgid "Coffee"
msgstr "ቡና"
#: pdf_bot/payment/payment_service.py:32
msgid "Beer"
msgstr "ቢራ"
#: pdf_bot/payment/payment_service.py:33
msgid "Meal"
msgstr "ምግብ"
#: pdf_bot/payment/payment_service.py:59
msgid "Select how you want to support PDF Bot"
msgstr "ፒዲኤፍ ቦትን እንዴት መደገፍ እንደሚፈልጉ ይምረጡ"
#: pdf_bot/payment/payment_service.py:75
msgid "Say thanks to PDF Bot and help keep it running"
msgstr "ለፒዲኤፍ ቦት ምስጋና ይናገሩ እና እንዲሠራ ያግዙ"
#: pdf_bot/payment/payment_service.py:89
msgid "Something went wrong, try again"
msgstr "አንድ ነገር ተሳሳትኩ እንደገና ሞክር"
#: pdf_bot/payment/payment_service.py:96
msgid "Thank you for your support!"
msgstr "ለድጋፎት እናመሰግናለን!"
#: pdf_bot/payment/payment_service.py:115
msgid "Help translate PDF Bot"
msgstr "ፒዲኤፍ ቦትን ለመተርጎም ያግዙ"
#: pdf_bot/pdf/exceptions.py:29
msgid "Your PDF file is encrypted, decrypt it first then try again"
msgstr "የእርስዎ የፒዲኤፍ ፋይል ኢንክሪፕት የተደረገ ነው, በመጀመሪያ ይፈቱት ከዚያም እንደገና ይሞክሩ"
#: pdf_bot/pdf/pdf_service.py:163
msgid "Your PDF file is not encrypted"
msgstr "የእርስዎ ፒዲኤፍ ፋይል አልተመሰጠረም"
#: pdf_bot/pdf/pdf_service.py:167
msgid "Incorrect password, please try again"
msgstr "የተሳሳተ የይለፍ ቃል, እባክዎ እንደገና ይሞክሩ"
#: pdf_bot/pdf/pdf_service.py:170
msgid "Your PDF file is encrypted with a method that I can't decrypt"
msgstr "የፒዲኤፍ ፋይልዎ ዲክሪፕት ማድረግ ባልችል ዘዴ ተመስጥሯል"
#: pdf_bot/pdf/pdf_service.py:202
msgid "No images found in your PDF file"
msgstr "በፒዲኤፍ ፋይልዎ ውስጥ የተገኙ ምስሎች"
#: pdf_bot/pdf/pdf_service.py:214
msgid "No text found in your PDF file"
msgstr "በፒዲኤፍ ፋይልዎ ውስጥ ምንም ጽሑፍ አልተገኘም"
#: pdf_bot/pdf/pdf_service.py:233
#, python-format
msgid "I couldn't merge your PDF files as this file is invalid: %s"
msgstr "ይህ ፋይል ውድቅ ስለሆነ የእርስዎን የፒዲኤፍ ፋይሎች ማዋሃድ አልቻልኩም %s"
#: pdf_bot/pdf/pdf_service.py:248
msgid "Your PDF file already has a text layer"
msgstr "የእርስዎ ፒዲኤፍ ፋይል ቀደም ሲል የጽሑፍ ንብርብር አለው"
#: pdf_bot/pdf/pdf_service.py:337
msgid "Your PDF file is invalid"
msgstr "የእርስዎ የፒዲኤፍ ፋይል ዋጋ የለውም"
#: pdf_bot/pdf_processor/compress_pdf_processor.py:24
msgid "Compress"
msgstr "መጭመቅ"
#: pdf_bot/pdf_processor/compress_pdf_processor.py:35
#, python-brace-format
msgid "File size reduced by {percent}, from {old_size} to {new_size}"
msgstr "የፋይል መጠን በ {percent}ቀንሷል ፣ ከ {old_size} ወደ {new_size}"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:17
msgid "By percentage"
msgstr "በመቶ"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:18
msgid "To margin size"
msgstr "ወደ ኅዳግ መጠን"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:25
msgid "Send me a number between 0 and 100\n\n"
"This is the percentage of margin space to retain between the content in your PDF file and the page"
msgstr "ከ 0 እስከ 100\n\n"
"መካከል ቁጥር ይላኩልኝ ይህ በፒዲኤፍ ፋይልዎ እና በገጽዎ ውስጥ ባለው ይዘት መካከል ለመያዝ የኅዳግ ቦታ አሃዝ ነው"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:31
msgid "Send me a number that you'll like to adjust the margin size\n\n"
"Positive numbers will decrease the margin size and negative numbers will increase it"
msgstr "የኅዳግ መጠንን ማስተካከል የምትወዱትን ቁጥር ላኩልኝ\n\n"
"ፖዚቲቭ ቁጥሮች የኅዳግ መጠንን ይቀንሳሉ እና አሉታዊ ቁጥሮች ይጨምራሉ"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:56
msgid "Crop"
msgstr "ቁረጥ"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:60
msgid "Select the crop type you'll like to perform"
msgstr "የምትፈልገውን የሰብል ዓይነት ምረጥ"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:68
msgid "The crop values are invalid, try again"
msgstr "የሰብል እሴት ዋጋ ቢስ ነው, እንደገና ይሞክሩ"
#: pdf_bot/pdf_processor/decrypt_pdf_processor.py:33
msgid "Decrypt"
msgstr "ዲክሪፕት"
#: pdf_bot/pdf_processor/decrypt_pdf_processor.py:40
msgid "Send me the password to decrypt your PDF file"
msgstr "የፒዲኤፍ ፋይልዎን ዲክሪፕት ለማድረግ የይለፍ ቃሉን ይላኩልኝ"
#: pdf_bot/pdf_processor/encrypt_pdf_processor.py:27
msgid "Encrypt"
msgstr "ኢንክሪፕት"
#: pdf_bot/pdf_processor/encrypt_pdf_processor.py:34
msgid "Send me the password to encrypt your PDF file"
msgstr "የፒዲኤፍ ፋይልዎን ለማመስጠር የይለፍ ቃሉን ይላኩልኝ"
#: pdf_bot/pdf_processor/extract_pdf_image_processor.py:24
msgid "Extract images"
msgstr "ምስሎችን አውጥ"
#: pdf_bot/pdf_processor/extract_pdf_text_processor.py:24
msgid "Extract text"
msgstr "ጽሁፍ"
#: pdf_bot/pdf_processor/grayscale_pdf_processor.py:24
msgid "Grayscale"
msgstr "ግራጫ ቀለም"
#: pdf_bot/pdf_processor/pdf_to_image_processor.py:24
msgid "To images"
msgstr "ወደ ምስሎች"
#: pdf_bot/pdf_processor/preview_pdf_processor.py:24
msgid "Preview"
msgstr "ቅድመ-እይታ"
#: pdf_bot/pdf_processor/rename_pdf_processor.py:30
msgid "Rename"
msgstr "ዳግም ሰይም"
#: pdf_bot/pdf_processor/rename_pdf_processor.py:35
#, python-format
msgid "File names can't contain any of the following characters, please try again:\n"
"%s"
msgstr "የፋይል ስሞች ከሚከተሉት ፊደሎች መካከል አንዳቸውንም መያዝ አይችሉም, እባክዎ እንደገና ይሞክሩ\n"
"%s"
#: pdf_bot/pdf_processor/rename_pdf_processor.py:40
msgid "Send me the file name that you'll like to rename your PDF file into"
msgstr "የፒ.ዲ.ኤፍ. ፋይልዎን እንደገና ለመሰየም የሚፈልጉትን የፋይል ስም ይላኩልኝ"
#: pdf_bot/pdf_processor/rotate_pdf_processor.py:38
msgid "Rotate"
msgstr "አዙር"
#: pdf_bot/pdf_processor/rotate_pdf_processor.py:76
msgid "Select the degrees that you'll like to rotate your PDF file in clockwise"
msgstr "ፒዲኤፍ ፋይልዎን አቅጣጫ ለማሽከርከር የሚፈልጉትን ዲግሪዎች ይምረጡ"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:18
msgid "By factor"
msgstr "በምክንያት"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:19
msgid "To dimension"
msgstr "ወደ ስፋት"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:26
msgid "Send me the scaling factors for the horizontal and vertical axes\n\n"
"Example: 2 0.5 - this will double the horizontal axis and halve the vertical axis"
msgstr "ለአግድም ና ቀጥ ያለ መጥረቢያ ዎች የስኬቲንግ ምክንያቶችን ላኩልኝ\n\n"
"ምሳሌ 2 0.5 - ይህም አግድም ዛቢያውን እጥፍ ያደርጋል እና ቀጥ ያለ ዛቢያን በግማሽ ይቀንሰው"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:32
msgid "Send me the width and height\n\n"
"Example: 150 200 - this will set the width to 150 and height to 200"
msgstr "ስፋቱን እና ከፍታውን ላኩልኝ\n\n"
"ምሳሌ 150 200 - ይህም ስፋት ወደ 150 እና ቁመት ወደ 200 ያስቀምጣል"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:56
msgid "Scale"
msgstr "መጠን"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:60
msgid "Select the scale type you'll like to perform"
msgstr "ማድረግ የምትወዱትን ዓይነት ይምረጡ"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:68
msgid "The scale values are invalid, try again"
msgstr "የስፋት እሴቶች ዋጋ ቢስ ናቸው, እንደገና ይሞክሩ"
#: pdf_bot/pdf_processor/split_pdf_processor.py:29
msgid "Split"
msgstr "ተከፈለ"
#: pdf_bot/pdf_processor/split_pdf_processor.py:33
msgid "The split range is invalid, please try again"
msgstr "የተከፈለበት ክልል ተቀባይነት የለውም, እባክዎ እንደገና ይሞክሩ"
#: pdf_bot/pdf_processor/split_pdf_processor.py:55
msgid "Send me the range of pages that you'll like to keep"
msgstr "ለማቆየት የሚፈልጓቸውን የገጾች ብዛት ላክልኝ"
#: pdf_bot/pdf_processor/split_pdf_processor.py:56
msgid "General usage"
msgstr "አጠቃላይ አጠቃቀም"
#: pdf_bot/pdf_processor/split_pdf_processor.py:57
#, python-brace-format
msgid "{range} all pages"
msgstr "{range} ሁሉም ገጾች"
#: pdf_bot/pdf_processor/split_pdf_processor.py:58
#, python-brace-format
msgid "{range} page 8 only"
msgstr "{range} ገጽ 8 ብቻ"
#: pdf_bot/pdf_processor/split_pdf_processor.py:59
#, python-brace-format
msgid "{range} first three pages"
msgstr "{range} የመጀመሪያዎቹ ሦስት ገጾች"
#: pdf_bot/pdf_processor/split_pdf_processor.py:60
#, python-brace-format
msgid "{range} from page 8 onward"
msgstr "{range} ከገጽ 8 ጀምሮ"
#: pdf_bot/pdf_processor/split_pdf_processor.py:61
#, python-brace-format
msgid "{range} last page only"
msgstr "{range} የመጨረሻው ገጽ ብቻ"
#: pdf_bot/pdf_processor/split_pdf_processor.py:62
#, python-brace-format
msgid "{range} all pages except the last page"
msgstr "{range} ሁሉም ገጾች ከመጨረሻው ገጽ በስተቀር"
#: pdf_bot/pdf_processor/split_pdf_processor.py:63
#, python-brace-format
msgid "{range} second last page only"
msgstr "{range} ሰከንድ የመጨረሻ ገጽ ብቻ"
#: pdf_bot/pdf_processor/split_pdf_processor.py:64
#, python-brace-format
msgid "{range} last two pages"
msgstr "{range} የመጨረሻዎቹ ሁለት ገጾች"
#: pdf_bot/pdf_processor/split_pdf_processor.py:65
#, python-brace-format
msgid "{range} third and second last pages"
msgstr "{range} ሦስተኛ እና ሁለተኛ የመጨረሻ ገጾች"
#: pdf_bot/pdf_processor/split_pdf_processor.py:66
msgid "Advanced usage"
msgstr "የላቀ አጠቃቀም"
#: pdf_bot/pdf_processor/split_pdf_processor.py:67
#, python-brace-format
msgid "{range} pages {pages} and to the end"
msgstr "{range} ገጾች {pages} እና እስከ መጨረሻው"
#: pdf_bot/pdf_processor/split_pdf_processor.py:70
#, python-brace-format
msgid "{range} pages {pages}"
msgstr "{range} ገጾች {pages}"
#: pdf_bot/pdf_processor/split_pdf_processor.py:71
#, python-brace-format
msgid "{range} all pages in reversed order"
msgstr "{range} ሁሉም ገጾች በተገላቢጦሽ ቅደም ተከተል"
#: pdf_bot/pdf_processor/split_pdf_processor.py:72
#, python-brace-format
msgid "{range} pages {pages} except {page}"
msgstr "{range} ገጾች {pages} ከ {page}"
#: pdf_bot/pdf_processor/split_pdf_processor.py:75
#, python-brace-format
msgid "{range} pages {pages}"
msgstr "{range} ገጾች {pages}"
#: pdf_bot/telegram_internal/telegram_service.py:69
msgid "Your file is too large for me to download and process, please try again with a different file\n\n"
"Note that this limit is enforced by Telegram and there's nothing I can do unless Telegram changes it"
msgstr "የእርስዎ ፋይል በጣም ትልቅ ነው ማውረድ እና መስራት, እባክዎ በተለየ ፋይል እንደገና ይሞክሩ\n\n"
"ይህ ገደብ በቴሌግራም ተፈጻሚ እንደሆነ እና ቴሌግራም ካልቀየረው በስተቀር ምንም ማድረግ እንደማልችል ልብ በሉ"
#: pdf_bot/telegram_internal/telegram_service.py:81
msgid "The file is too large for me to send to you\n\n"
"Note that this limit is enforced by Telegram and there's nothing I can do unless Telegram changes it"
msgstr "ፋይሉ በጣም ትልቅ ነው ለእርስዎ ለመላክ\n\n"
"ማስታወሻ ይህ ገደብ በቴሌግራም ተፈፃሚ ነው እና ቴሌግራም ካልቀየረ በስተቀር ምንም ማድረግ አልችልም"
#: pdf_bot/telegram_internal/telegram_service.py:156
msgid "Your file is not an image, please try again"
msgstr "ፋይልዎ ምስል አይደለም, እባክዎ እንደገና ይሞክሩ"
#: pdf_bot/telegram_internal/telegram_service.py:162
msgid "No image found in your message"
msgstr "በመልዕክትዎ ውስጥ ምስል አልተገኘም"
#: pdf_bot/telegram_internal/telegram_service.py:172
msgid "Your file is not a PDF file, please try again"
msgstr "ፋይልዎ የፒዲኤፍ ፋይል አይደለም ፣ እባክዎ እንደገና ይሞክሩ"
#: pdf_bot/telegram_internal/telegram_service.py:197
#: pdf_bot/telegram_internal/telegram_service.py:200
msgid "Action cancelled"
msgstr "እርምጃ ተሰር .ል"
#: pdf_bot/telegram_internal/telegram_service.py:271
#: pdf_bot/telegram_internal/telegram_service.py:279
msgid "Here is your result file"
msgstr "የውጤት ፋይልዎ ይኸውና"
#: pdf_bot/text/text_service.py:21
msgid "Skip"
msgstr "ሽቅብ"
#: pdf_bot/text/text_service.py:40
msgid "Send me the text that you'll like to write into your PDF file"
msgstr "በፒዲኤፍ ፋይልዎ ውስጥ ለመጻፍ የሚፈልጉትን ጽሑፍ ይላኩልኝ"
#: pdf_bot/text/text_service.py:59
#, python-brace-format
msgid "Send me the font that you'll like to use for the PDF file or press {skip} to use the default font"
msgstr "ለፒዲኤፍ ፋይሉ ለመጠቀም የሚፈልጉትን ቅርጸ -ቁምፊ ይላኩልኝ ወይም ነባሪውን ቅርጸ -ቁምፊ ለመጠቀም {skip}"
#: pdf_bot/text/text_service.py:62
#, python-brace-format
msgid "See here for the list of supported fonts: {fonts}"
msgstr "የሚደገፉ ቅርጸ -ቁምፊዎች ዝርዝር እዚህ ይመልከቱ {fonts}"
#: pdf_bot/text/text_service.py:90
msgid "Unknown font, please try again"
msgstr "ያልታወቀ ቅርጸ -ቁምፊ ፣ እባክዎ እንደገና ይሞክሩ"
#: pdf_bot/text/text_service.py:108
msgid "Creating your PDF file"
msgstr "የፒዲኤፍ ፋይልዎን በመፍጠር ላይ"
#: pdf_bot/watermark/watermark_service.py:38
msgid "Send me the PDF file that you'll like to add a watermark"
msgstr "የውሃ ምልክት ማድረጊያ ማከል የሚፈልጉትን የፒዲኤፍ ፋይል ላኩልኝ"
#: pdf_bot/watermark/watermark_service.py:56
msgid "Send me the watermark PDF file"
msgstr "የውሃ ምልክት ማድረጊያ ፒዲኤፍ ፋይል ላክልኝ"
#: pdf_bot/watermark/watermark_service.py:74
msgid "Adding the watermark onto your PDF file"
msgstr "በፒዲኤፍ ፋይልዎ ላይ የውሃ ምልክቱን ማከል"
#: pdf_bot/webpage/webpage_service.py:41
msgid "You've sent me this webpage already and I'm still converting it"
msgstr "ይህን ድረ ገጽ ድሮ ልከኸኛል አሁንም እየቀየርኩት ነው"
#: pdf_bot/webpage/webpage_service.py:45
msgid "Converting your webpage into a PDF file"
msgstr "የእርስዎን ድረ ገጽ ወደ PDF ፋይል መቀየር"
#: pdf_bot/webpage/webpage_service.py:72
msgid "Unable to reach your webpage"
msgstr "ድረ ገጽህ ላይ መድረስ አልተቻለም"
#: pdf_bot/webpage/webpage_service.py:84
msgid "Failed to convert your webpage"
msgstr "ድረ ገጻችሁን መቀየር ሳይችሉ ቀርተዋል"
#~ msgid ""
#~ "Send me the first photo that "
#~ "you'll like to beautify or convert "
#~ "into PDF\n"
#~ "\n"
#~ "Note that the photos will be "
#~ "beautified and converted in the order"
#~ " that you send me"
#~ msgstr ""
#~ msgid ""
#~ "Send me the next photo that you'll like to beautify or convert to PDF\n"
#~ "\n"
#~ "Select the task from below if you have sent me all the photos"
#~ msgstr ""
#~ msgid ""
#~ "Welcome to PDF Bot!\n"
#~ "\n"
#~ "*Features*\n"
#~ "- Compare, crop, decrypt, encrypt, "
#~ "merge, rotate, scale, split and add "
#~ "a watermark to a PDF file\n"
#~ "- Extract text and photos in a "
#~ "PDF file and convert a PDF file"
#~ " into photos\n"
#~ "- Beautify and convert photos into PDF format\n"
#~ "- Convert a web page into a PDF file\n"
#~ "\n"
#~ "Type /help to see how to use PDF Bot"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " simply by sending me a PDF "
#~ "file, a photo or a link to a"
#~ " web page.\n"
#~ "\n"
#~ "Some tasks can be performed by "
#~ "using the commands /compare, /merge, "
#~ "/watermark or /photo"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " by sending me one of the "
#~ "followings:\n"
#~ "- PDF files\n"
#~ "- Photos\n"
#~ "- Webpage links\n"
#~ "\n"
#~ "The rest of the tasks can be "
#~ "performed by using the commands "
#~ "/compare, /merge, /photo, /text or "
#~ "/watermark"
#~ msgstr ""
#~ msgid ""
#~ "Welcome to PDF Bot!\n"
#~ "\n"
#~ "*Key features:*\n"
#~ "- Compress, merge, preview, rename, "
#~ "split and add watermark to PDF "
#~ "files\n"
#~ "- Create PDF files from text messages\n"
#~ "- Extract images and text from PDF files\n"
#~ "- Convert PDF files into images\n"
#~ "- Convert webpages and images into PDF files\n"
#~ "- Beautify handwritten notes images into PDF files\n"
#~ "- _And more..._\n"
#~ "\n"
#~ "Type /help to see how to use PDF Bot"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " by sending me one of the "
#~ "followings:\n"
#~ "- PDF files\n"
#~ "- Photos\n"
#~ "- Webpage links\n"
#~ "\n"
#~ "The rest of the tasks can be performed by using the commands below:\n"
#~ "- /compare _PDF files_\n"
#~ "- /merge _PDF files_\n"
#~ "- /photo _convert and combine multiple photos into PDF files_\n"
#~ "- /text _create PDF files from text messages_\n"
#~ "- /watermark _add watermark to PDF files_"
#~ msgstr ""
#~ msgid ""
#~ "Press *Done* if you've sent me all"
#~ " the PDF files that you'll like "
#~ "to merge or keep sending me the"
#~ " PDF files"
#~ msgstr ""
#~ msgid "*{}* has been removed for merging"
#~ msgstr ""
#~ msgid "*{}* has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid "File size reduced by *{:.0%}*, from *{}* to *{}*"
#~ msgstr ""
#~ msgid "Renaming your PDF file into *{}*"
#~ msgstr ""
#~ msgid ""
#~ "Send me the width and height\n"
#~ "\n"
#~ "*Example: 150 200* (this will set the width to 150 and height to 200)"
#~ msgstr ""
#~ msgid ""
#~ "Send me the scaling factors for the horizontal and vertical axes\n"
#~ "\n"
#~ "2 will double the axis and 0.5 will halve the axis\n"
#~ "\n"
#~ "*Example: 2 0.5* (this will double "
#~ "the horizontal axis and halve the "
#~ "vertical axis)"
#~ msgstr ""
#~ msgid "The scaling factors *{}* are invalid, try again"
#~ msgstr ""
#~ msgid "The dimensions *{}* are invalid, try again"
#~ msgstr ""
#~ msgid "Scaling your PDF file, horizontally by *{}* and vertically by *{}*"
#~ msgstr ""
#~ msgid "Scaling your PDF file with width of *{}* and height of *{}*"
#~ msgstr ""
#~ msgid ""
#~ "Send me the range of pages that"
#~ " you'll like to keep. Use ⚡ "
#~ "*INSTANT VIEW* from below or refer "
#~ "to [here](http://telegra.ph/Telegram-PDF-Bot-07-16)"
#~ " for some range examples."
#~ msgstr ""
#~ msgid "*See above for all the text in your PDF file*"
#~ msgstr ""
#~ msgid ""
#~ "Send me the range of pages that"
#~ " you'll like to keep. Use ⚡ "
#~ "<b>INSTANT VIEW</b> from below or refer"
#~ " to [here](http://telegra.ph/Telegram-PDF-"
#~ "Bot-07-16) for some range examples."
#~ msgstr ""
#~ msgid ""
#~ "Send me your feedback or /cancel "
#~ "this action. Note that only English "
#~ "feedback will be forwarded to my "
#~ "developer."
#~ msgstr ""
#~ msgid ""
#~ "Send me your feedback, note that "
#~ "only English feedback will be forwarded"
#~ " to my developer"
#~ msgstr ""
#~ msgid ""
#~ "Send me the range of pages that you'll like to keep\n"
#~ "\n"
#~ "<b>General usage</b>\n"
#~ "<code>: all pages</code>\n"
#~ "<code>22 just the 23rd page</code>\n"
#~ "<code>0:3 the first three pages</code>\n"
#~ "<code>:3 the first three pages</code>\n"
#~ "<code>5: from the 6th page onwards</code>\n"
#~ "<code>-1 last page only</code>\n"
#~ "<code>:-1 all pages but the last page</code>\n"
#~ "<code>-2 second last page only</code>\n"
#~ "<code>-2: last two pages</code>\n"
#~ "<code>-3:-1 third and second last pages only</code>\n"
#~ "\n"
#~ "<b>Advanced usage</b>\n"
#~ "<code>::2 pages 0 2 4 ... to the end</code>\n"
#~ "<code>1:10:2 pages 1 3 5 7 9</code>\n"
#~ "<code>::-1 all pages in reversed order</code>\n"
#~ "<code>3:0:-1 pages 3 2 1 but not 0</code>\n"
#~ "<code>2::-1 pages 2 1 0</code>"
#~ msgstr ""
#~ msgid "Set Language"
#~ msgstr ""
#~ msgid "Send me the amount that you'll like to support PDF Bot"
#~ msgstr ""
#~ msgid "Say Awesome 🤩 (Custom)"
#~ msgstr ""
#~ msgid "The amount you sent is invalid, try again. {}"
#~ msgstr ""
#~ msgid "Select the font or select"
#~ msgstr ""
#~ msgid "to use the default font"
#~ msgstr ""
#~ msgid "Unknown font, please select a font from the list"
#~ msgstr ""
#~ msgid "Say Thanks 😁 ($1)"
#~ msgstr ""
#~ msgid "Coffee ☕ ($3)"
#~ msgstr ""
#~ msgid "Beer 🍺 ($5)"
#~ msgstr ""
#~ msgid "Meal 🍲 ($10)"
#~ msgstr ""
#~ msgid ""
#~ "Welcome to PDF Bot!\n"
#~ "\n"
#~ "<b>Key features:</b>\n"
#~ "- Compress, merge, preview, rename, "
#~ "split and add watermark to PDF "
#~ "files\n"
#~ "- Create PDF files from text messages\n"
#~ "- Extract images and text from PDF files\n"
#~ "- Convert PDF files into images\n"
#~ "- Convert webpages and images into PDF files\n"
#~ "- Beautify handwritten notes images into PDF files\n"
#~ "- <b><i>And more...</i></b>\n"
#~ "\n"
#~ "Type /help to see how to use PDF Bot"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " by sending me one of the "
#~ "followings:\n"
#~ "- PDF files\n"
#~ "- Photos\n"
#~ "- Webpage links\n"
#~ "\n"
#~ "The rest of the tasks can be performed by using the commands below:\n"
#~ "/compare - compare PDF files\n"
#~ "/merge - merge PDF files\n"
#~ "/photo - convert and combine multiple photos into PDF files\n"
#~ "/text - create PDF files from text messages\n"
#~ "/watermark - add watermark to PDF files"
#~ msgstr ""
#~ msgid "The file you sent is not a PDF file, try again"
#~ msgstr ""
#~ msgid ""
#~ "The PDF file you sent is too large for me to download\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid "Something went wrong, start over again"
#~ msgstr ""
#~ msgid ""
#~ "Your PDF file seems to be invalid and I couldn't open and read it\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid ""
#~ "Your {} PDF file is encrypted and you'll have to decrypt it first\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid ""
#~ "Your PDF file is encrypted and you'll have to decrypt it first\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid "You've sent me these {} so far:\n"
#~ msgstr ""
#~ msgid ""
#~ "Send me one of the PDF files that you'll like to compare\n"
#~ "\n"
#~ "Note that I can only look for differences in text"
#~ msgstr ""
#~ msgid "There are no differences in text between your PDF files"
#~ msgstr ""
#~ msgid ""
#~ "Send me the PDF files that you'll like to merge\n"
#~ "\n"
#~ "Note that the files will be merged in the order that you send me"
#~ msgstr ""
#~ msgid "The file you've sent is not a PDF file"
#~ msgstr ""
#~ msgid "The PDF file you've sent is too large for me to download"
#~ msgstr ""
#~ msgid ""
#~ "Press <b>Done</b> if you've sent me "
#~ "all the PDF files that you'll like"
#~ " to merge or keep sending me "
#~ "the PDF files"
#~ msgstr ""
#~ msgid "<b>{}</b> has been removed for merging"
#~ msgstr ""
#~ msgid "You've only sent me one PDF file."
#~ msgstr ""
#~ msgid ""
#~ "I can't merge your PDF files as"
#~ " I couldn't open and read \"{}\". "
#~ "Ensure that it is not encrypted"
#~ msgstr ""
#~ msgid ""
#~ "Send me the photos that you'll "
#~ "like to beautify or convert into a"
#~ " PDF file\n"
#~ "\n"
#~ "Note that the photos will be "
#~ "beautified and converted in the order"
#~ " that you send me"
#~ msgstr ""
#~ msgid "The file you've sent is not a photo"
#~ msgstr ""
#~ msgid "The photo you've sent is too large for me to download"
#~ msgstr ""
#~ msgid "<b>{}</b> has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid ""
#~ "Send me the font that you'll like"
#~ " to use for the PDF file or "
#~ "skip to use the default font\n"
#~ "\n"
#~ "See here for the list of supported fonts:"
#~ msgstr ""
#~ msgid "File size reduced by <b>{:.0%}</b>, from <b>{}</b> to <b>{}</b>"
#~ msgstr ""
#~ msgid "Something went wrong, try again"
#~ msgstr ""
#~ msgid ""
#~ "Send me a number between {} and"
#~ " {}. This is the percentage of "
#~ "margin space to retain between the "
#~ "content in your PDF file and the"
#~ " page"
#~ msgstr ""
#~ msgid ""
#~ "Send me a number that you'll like"
#~ " to adjust the margin size. Positive"
#~ " numbers will decrease the margin "
#~ "size and negative numbers will increase"
#~ " it"
#~ msgstr ""
#~ msgid "The number must be between {} and {}, try again"
#~ msgstr ""
#~ msgid "The number is invalid, try again"
#~ msgstr ""
#~ msgid "Your PDF file seems to be invalid and I couldn't open and read it"
#~ msgstr ""
#~ msgid "The decryption password is incorrect, try to send it again"
#~ msgstr ""
#~ msgid "Your PDF file is encrypted with a method that I cannot decrypt"
#~ msgstr ""
#~ msgid ""
#~ "Your PDF file is too big for me to download\n"
#~ "\n"
#~ "I can't perform any tasks on it"
#~ msgstr ""
#~ msgid ""
#~ "Your photo is too large for me "
#~ "to download. I can't beautify or "
#~ "convert your photo"
#~ msgstr ""
#~ msgid ""
#~ "File names can't contain any of the following characters:\n"
#~ "{}\n"
#~ "Send me another file name"
#~ msgstr ""
#~ msgid "Renaming your PDF file into <b>{}</b>"
#~ msgstr ""
#~ msgid ""
#~ "Send me the width and height\n"
#~ "\n"
#~ "<b>Example: 150 200</b> (this will set"
#~ " the width to 150 and height to"
#~ " 200)"
#~ msgstr ""
#~ msgid ""
#~ "Send me the scaling factors for the horizontal and vertical axes\n"
#~ "\n"
#~ "<b>Example: 2 0.5</b> (this will double"
#~ " the horizontal axis and halve the"
#~ " vertical axis)"
#~ msgstr ""
#~ msgid "The scaling factors <b>{}</b> are invalid, try again"
#~ msgstr ""
#~ msgid "The dimensions <b>{}</b> are invalid, try again"
#~ msgstr ""
#~ msgid ""
#~ "Scaling your PDF file, horizontally by"
#~ " <b>{}</b> and vertically by <b>{}</b>"
#~ msgstr ""
#~ msgid "Scaling your PDF file with width of <b>{}</b> and height of <b>{}</b>"
#~ msgstr ""
#~ msgid "all pages"
#~ msgstr ""
#~ msgid "page 8 only"
#~ msgstr ""
#~ msgid "first three pages"
#~ msgstr ""
#~ msgid "from page 8 onward"
#~ msgstr ""
#~ msgid "last page only"
#~ msgstr ""
#~ msgid "all pages except the last page"
#~ msgstr ""
#~ msgid "second last page only"
#~ msgstr ""
#~ msgid "last two pages"
#~ msgstr ""
#~ msgid "third and second last pages"
#~ msgstr ""
#~ msgid "pages"
#~ msgstr ""
#~ msgid "to the end"
#~ msgstr ""
#~ msgid "all pages in reversed order"
#~ msgstr ""
#~ msgid "except"
#~ msgstr ""
#~ msgid "The range is invalid. Try again"
#~ msgstr ""
#~ msgid "<b>See above for all the text in your PDF file</b>"
#~ msgstr ""
#~ msgid "Type {} to see how to use PDF Bot"
#~ msgstr ""
#~ msgid "{} - compare PDF files"
#~ msgstr ""
#~ msgid "{} - merge PDF files"
#~ msgstr ""
#~ msgid "{} - convert and combine multiple photos into PDF files"
#~ msgstr ""
#~ msgid "{} - create PDF files from text messages"
#~ msgstr ""
#~ msgid "{} - add watermark to PDF files"
#~ msgstr ""
#~ msgid "Your language has been set to {}"
#~ msgstr ""
#~ msgid "Your {} PDF file is encrypted and you'll have to decrypt it first"
#~ msgstr ""
#~ msgid "You've sent me these {} so far:"
#~ msgstr ""
#~ msgid ""
#~ "Press {} if you've sent me all "
#~ "the PDF files that you'll like to"
#~ " merge or keep sending me the "
#~ "PDF files"
#~ msgstr ""
#~ msgid "{} has been removed for merging"
#~ msgstr ""
#~ msgid "I couldn't merge your PDF files as this file is invalid: {}"
#~ msgstr ""
#~ msgid "{} has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid ""
#~ "Send me the font that you'll like"
#~ " to use for the PDF file or "
#~ "press {} to use the default font"
#~ msgstr ""
#~ msgid "See here for the list of supported fonts: {}"
#~ msgstr ""
#~ msgid "Renaming your PDF file into {}"
#~ msgstr ""
#~ msgid "Rotating your PDF file clockwise by {} degrees"
#~ msgstr ""
#~ msgid "Example: 150 200"
#~ msgstr ""
#~ msgid "Example: 2 0.5"
#~ msgstr ""
#~ msgid "The scaling factors {} are invalid, please try again"
#~ msgstr ""
#~ msgid "The dimensions {} are invalid, please try again"
#~ msgstr ""
#~ msgid "{} all pages"
#~ msgstr ""
#~ msgid "{} page 8 only"
#~ msgstr ""
#~ msgid "{} first three pages"
#~ msgstr ""
#~ msgid "{} from page 8 onward"
#~ msgstr ""
#~ msgid "{} last page only"
#~ msgstr ""
#~ msgid "{} all pages except the last page"
#~ msgstr ""
#~ msgid "{} second last page only"
#~ msgstr ""
#~ msgid "{} last two pages"
#~ msgstr ""
#~ msgid "{} third and second last pages"
#~ msgstr ""
#~ msgid "{} all pages in reversed order"
#~ msgstr ""
#~ msgid "See above for all the text in your PDF file"
#~ msgstr ""
#~ msgid "You can perform most of the tasks by sending me one of the followings"
#~ msgstr ""
#~ msgid "The rest of the tasks can be performed by using the following commands"
#~ msgstr ""
#~ msgid "Your PDF file seems to be invalid and I couldn't open and process it"
#~ msgstr ""
#~ msgid "Your PDF file is too large for me to download"
#~ msgstr ""
#~ msgid "Your file is too large for me to download"
#~ msgstr ""
#~ msgid "Your photo is too large for me to download"
#~ msgstr ""
#~ msgid "{range} pages {results} and to the end"
#~ msgstr ""
#~ msgid "{range} pages {results}"
#~ msgstr ""
#~ msgid "{range} pages {results} except {except_text}"
#~ msgstr ""
#~ msgid "{range} pages {results}"
#~ msgstr ""
#~ msgid "Your PDF file is too large for me to download and process"
#~ msgstr ""
#~ msgid "Your PDF file is too big for me to download"
#~ msgstr ""
#~ msgid "Say Thanks {emoji} ({value})"
#~ msgstr ""
#~ msgid "Coffee {emoji} ({value})"
#~ msgstr ""
#~ msgid "Beer {emoji} ({value})"
#~ msgstr ""
#~ msgid "Meal {emoji} ({value})"
#~ msgstr ""
#~ msgid "Failed to process, please try again"
#~ msgstr ""
#~ msgid "Extract Photos"
#~ msgstr ""
#~ msgid "To Photos"
#~ msgstr ""
#~ msgid "Photos"
#~ msgstr ""
#~ msgid "- Photos"
#~ msgstr ""
#~ msgid "{command} - convert and combine multiple photos into PDF files"
#~ msgstr ""
#~ msgid ""
#~ "Send me the photos that you'll "
#~ "like to beautify or convert into a"
#~ " PDF file"
#~ msgstr ""
#~ msgid ""
#~ "Note that the photos will be "
#~ "beautified and converted in the order"
#~ " that you send me"
#~ msgstr ""
#~ msgid "Your file is not a photo"
#~ msgstr ""
#~ msgid "photos"
#~ msgstr ""
#~ msgid ""
#~ "Select the task from below if "
#~ "you've sent me all the photos, or"
#~ " keep sending me the photos"
#~ msgstr ""
#~ msgid "Beautifying and converting your photos"
#~ msgstr ""
#~ msgid "Converting your photos into PDF"
#~ msgstr ""
#~ msgid "Your photo is too large for me to download and process"
#~ msgstr ""
#~ msgid "Converting your PDF file into photos"
#~ msgstr ""
#~ msgid "Extracting all the photos in your PDF file"
#~ msgstr ""
#~ msgid "I couldn't find any photos in your PDF file"
#~ msgstr ""
#~ msgid "See above for all your photos"
#~ msgstr ""
#~ msgid ""
#~ "Your {file_type} PDF file is encrypted"
#~ " and you'll have to decrypt it "
#~ "first"
#~ msgstr ""
#~ msgid "watermark"
#~ msgstr ""
#~ msgid "PDF files"
#~ msgstr ""
#~ msgid "Your file is not a PDF file"
#~ msgstr ""
#~ msgid ""
#~ "Your file is not a PDF file, "
#~ "please try again and ensure that "
#~ "your file has the .pdf extension"
#~ msgstr ""
#~ msgid "Converting your PDF file to black and white"
#~ msgstr ""
#~ msgid "Compressing your PDF file"
#~ msgstr ""
#~ msgid ""
#~ "The number must be between {min_percent}"
#~ " and {max_percent}, please try again"
#~ msgstr ""
#~ msgid "The number is invalid, please try again"
#~ msgstr ""
#~ msgid "Cropping your PDF file"
#~ msgstr ""
#~ msgid "Decrypting your PDF file"
#~ msgstr ""
#~ msgid "Your PDF file is invalid and I couldn't open and process it"
#~ msgstr ""
#~ msgid "The decryption password is incorrect, please try again"
#~ msgstr ""
#~ msgid "Encrypting your PDF file"
#~ msgstr ""
#~ msgid "Adding an OCR text layer to your PDF file"
#~ msgstr ""
#~ msgid "Renaming your PDF file into {file_name}"
#~ msgstr ""
#~ msgid "Rotating your PDF file clockwise by {degree} degrees"
#~ msgstr ""
#~ msgid ""
#~ "Scaling your PDF file, horizontally by"
#~ " {horizontal} and vertically by {vertical}"
#~ msgstr ""
#~ msgid "Scaling your PDF file with width of {width} and height of {height}"
#~ msgstr ""
#~ msgid "Splitting your PDF file"
#~ msgstr ""
#~ msgid "Select how you'll like me to send the text to you"
#~ msgstr ""
#~ msgid "Extracting text from your PDF file"
#~ msgstr ""
#~ msgid "I couldn't find any text in your PDF file"
#~ msgstr ""
#~ msgid ""
#~ "Your image is too large for me "
#~ "to download and process, please try "
#~ "again with a differnt image\n"
#~ "\n"
#~ "Note that this is a Telegram Bot"
#~ " limitation and there's nothing I can"
#~ " do unless Telegram changes this "
#~ "limit"
#~ msgstr ""
#~ msgid "Testing"
#~ msgstr ""
#~ msgid ""
#~ "Your file is too large for me "
#~ "to download and process, please try "
#~ "again with a differnt file\n"
#~ "\n"
#~ "Note that this is a Telegram Bot"
#~ " limitation and there's nothing I can"
#~ " do unless Telegram changes this "
#~ "limit"
#~ msgstr ""
#~ msgid "Remove Last File"
#~ msgstr ""
#~ msgid "Extracting a preview for your PDF file"
#~ msgstr ""
#~ msgid "Converting your PDF file into images"
#~ msgstr ""
#~ msgid "Select the result file format"
#~ msgstr ""
#~ msgid "Extracting all the images in your PDF file"
#~ msgstr ""
#~ msgid "I couldn't find any images in your PDF file"
#~ msgstr ""
#~ msgid "See above for all your images"
#~ msgstr ""
#~ msgid "Your image is too large for me to download and process"
#~ msgstr ""
#~ msgid "File name unavailable"
#~ msgstr ""
#~ msgid "Your file is not an image"
#~ msgstr ""
#~ msgid "Your file is too large for me to download and process"
#~ msgstr ""
#~ msgid "images"
#~ msgstr ""
#~ msgid "{file_name} has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid "Beautifying and converting your images"
#~ msgstr ""
#~ msgid "Converting your images into PDF"
#~ msgstr ""
#~ msgid ""
#~ "Send me your feedback (only English "
#~ "feedback will be forwarded to my "
#~ "developer)"
#~ msgstr ""
#~ msgid "Thank you for your feedback, I've already forwarded it to my developer"
#~ msgstr ""
#~ msgid "Something went wrong"
#~ msgstr ""
#~ msgid ""
#~ "Timed out processing your web page, "
#~ "your web page is probably too "
#~ "complex to process"
#~ msgstr ""
#~ msgid "Something went wrong, please start over again"
#~ msgstr ""
#~ msgid "Your file is invalid and I couldn't open and process it"
#~ msgstr ""
#~ msgid "Your PDF file is already encrypted"
#~ msgstr ""
#~ msgid "Your PDF file is encrypted and you'll have to decrypt it first"
#~ msgstr ""
#~ msgid "You've sent me these {file_type} so far:"
#~ msgstr ""
#~ msgid "The result file is too large for me to send to you"
#~ msgstr ""
#~ msgid ""
#~ "The button has expired, please try "
#~ "again with a new message/query then "
#~ "press the new button"
#~ msgstr ""
#~ msgid "Invalid rotation degree, try again"
#~ msgstr ""
#~ msgid "The scaling factors {values} are invalid, please try again"
#~ msgstr ""
#~ msgid "The dimensions {values} are invalid, please try again"
#~ msgstr ""
#~ msgid "Send me the scaling factors for the horizontal and vertical axes"
#~ msgstr ""
#~ msgid "This will double the horizontal axis and halve the vertical axis"
#~ msgstr ""
#~ msgid "Send me the width and height"
#~ msgstr ""
#~ msgid "This will set the width to 150 and height to 200"
#~ msgstr ""
#~ msgid "File names can't contain any of the following characters:"
#~ msgstr ""
#~ msgid "Please try again"
#~ msgstr ""
#~ msgid "The range is invalid, please try again"
#~ msgstr ""
#~ msgid "By scaling factor"
#~ msgstr ""
#~ msgid "Select the scale type that you'll like to perform"
#~ msgstr ""
#~ msgid "The values {values} are invalid, please try again"
#~ msgstr ""
#~ msgid "By margin size"
#~ msgstr ""
#~ msgid "Select the crop type that you'll like to perform"
#~ msgstr ""
#~ msgid ""
#~ "The number {number} is not between "
#~ "{min_percent} and {max_percent}, please try"
#~ " again"
#~ msgstr ""
#~ msgid "The number {number} is invalid, please try again"
#~ msgstr ""
#~ msgid "Send me a number between {min_percent} and {max_percent}"
#~ msgstr ""
#~ msgid ""
#~ "This is the percentage of margin "
#~ "space to retain between the content "
#~ "in your PDF file and the page"
#~ msgstr ""
#~ msgid "Send me a number that you'll like to adjust the margin size"
#~ msgstr ""
#~ msgid ""
#~ "Positive numbers will decrease the "
#~ "margin size and negative numbers will"
#~ " increase it"
#~ msgstr ""
#~ msgid ""
#~ "Select the task that you'll like "
#~ "to perform from below or select "
#~ "from the buttons"
#~ msgstr ""
#~ msgid "By Percentage"
#~ msgstr ""
#~ msgid "By Margin Size"
#~ msgstr ""
#~ msgid "Extract Images"
#~ msgstr ""
#~ msgid "To Images"
#~ msgstr ""
#~ msgid "Compressed"
#~ msgstr ""
#~ msgid "Images"
#~ msgstr ""
#~ msgid "To Dimensions"
#~ msgstr ""
#~ msgid "Extract Text"
#~ msgstr ""
#~ msgid "Text Message"
#~ msgstr ""
#~ msgid "Text File"
#~ msgstr ""
#~ msgid "Black & White"
#~ msgstr ""
#~ msgid "You've sent me this web page already and I'm still converting it"
#~ msgstr ""
#~ msgid "Converting your web page into a PDF file"
#~ msgstr ""
#~ msgid "Unable to reach your web page"
#~ msgstr ""
#~ msgid "Failed to convert your web page"
#~ msgstr ""
#~ msgid "Something went wrong, start over with your file or command"
#~ msgstr ""
#~ msgid "Your PDF file is encrypted"
#~ msgstr ""
#~ msgid "I couldn't merge your PDF files as this file is invalid: {file_name}"
#~ msgstr ""
#~ msgid ""
#~ "File names can't contain any of "
#~ "the following characters, please try "
#~ "again:\n"
#~ "{invalid_chars}"
#~ msgstr ""
#~ msgid ""
#~ "Your file is too large for me "
#~ "to download and process, please try "
#~ "again with a differnt file\n"
#~ "\n"
#~ "Note that this limit is enforced "
#~ "by Telegram and there's nothing I "
#~ "can do unless Telegram changes it"
#~ msgstr ""
================================================
FILE: locale/ar_SA/LC_MESSAGES/pdf_bot.po
================================================
# locale translations for telegram-pdf-bot.
# Copyright (C) 2021 zeshuaro
# This file is distributed under the same license as the telegram-pdf-bot
# project.
# zeshuaro <zeshuaro@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: telegram-pdf-bot\n"
"Report-Msgid-Bugs-To: zeshuaro@gmail.com\n"
"POT-Creation-Date: 2025-10-19 06:16+0000\n"
"PO-Revision-Date: 2025-10-19 06:58\n"
"Last-Translator: \n"
"Language: ar\n"
"Language-Team: Arabic\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.17.0\n"
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
"X-Crowdin-Project: telegram-pdf-bot\n"
"X-Crowdin-Project-ID: 370289\n"
"X-Crowdin-Language: ar\n"
"X-Crowdin-File: pdf_bot.po\n"
"X-Crowdin-File-ID: 88\n"
#: pdf_bot/consts.py:11
msgid "Cancel"
msgstr "إلغاء"
#: pdf_bot/consts.py:12
msgid "Done"
msgstr "تم"
#: pdf_bot/consts.py:13 pdf_bot/telegram_internal/telegram_service.py:48
msgid "Back"
msgstr "رجوع"
#: pdf_bot/consts.py:23
msgid "Something went wrong, start over with your file or command"
msgstr "حدث خطأ ما، ابدأ من جديد بملفك أو أمرك"
#: pdf_bot/cli/cli_service.py:35
msgid "Failed to complete process"
msgstr "أخفق إكمال العملية"
#: pdf_bot/command/command_service.py:38
msgid "Welcome to PDF Bot!"
msgstr "مرحبا بكم في بوت PDF!"
#: pdf_bot/command/command_service.py:39
msgid "Key features:"
msgstr "الخاصيات الرئيسية"
#: pdf_bot/command/command_service.py:41
msgid "- Compress, merge, preview, rename, split and add watermark to PDF files"
msgstr "- ضغط ودمج ومعاينة وإعادة تسمية وتقسيم وإضافة علامة مائية لملفات PDF"
#: pdf_bot/command/command_service.py:43
msgid "- Create PDF files from text messages"
msgstr "- إنشاء ملفات PDF من الرسائل النصية"
#: pdf_bot/command/command_service.py:44
msgid "- Extract images and text from PDF files"
msgstr "- استخراج الصور والنصوص من ملفات PDF"
#: pdf_bot/command/command_service.py:45
msgid "- Convert PDF files into images"
msgstr "- تحويل ملفات PDF إلى صور"
#: pdf_bot/command/command_service.py:46
msgid "- Convert webpages and images into PDF files"
msgstr "- تحويل صفحات الويب والصور إلى ملفات PDF"
#: pdf_bot/command/command_service.py:47
msgid "- Beautify handwritten notes images into PDF files"
msgstr "- تجميل الملاحظات المكتوبة بخط اليد الصور في ملفات PDF"
#: pdf_bot/command/command_service.py:48
msgid "- And more..."
msgstr "-وأكثر من ذلك ..."
#: pdf_bot/command/command_service.py:49
#, python-brace-format
msgid "Type {command} to see how to use PDF Bot"
msgstr "اكتب {command} لمعرفة كيفية استخدام PDF Bot"
#: pdf_bot/command/command_service.py:58
msgid "Set Language 🌎"
msgstr "تغير اللغه 🌎"
#: pdf_bot/command/command_service.py:60
#: pdf_bot/telegram_internal/telegram_service.py:222
msgid "Join Channel"
msgstr "انضم إلى القناة"
#: pdf_bot/command/command_service.py:61 pdf_bot/payment/payment_service.py:74
#: pdf_bot/telegram_internal/telegram_service.py:223
msgid "Support PDF Bot"
msgstr "ادعم بوت ال PDF"
#: pdf_bot/command/command_service.py:70
msgid "You can perform most of the tasks by sending me one of the followings:"
msgstr "يمكنك تنفيذ معظم المهام عن طريق إرسال أحد ما يلي:"
#: pdf_bot/command/command_service.py:71
msgid "- PDF files"
msgstr "- ملفات PDF"
#: pdf_bot/command/command_service.py:72
msgid "- Images"
msgstr "- صور"
#: pdf_bot/command/command_service.py:73
msgid "- Webpage links"
msgstr "- روابط صفحة ويب"
#: pdf_bot/command/command_service.py:74
msgid "The rest of the tasks can be performed by using the following commands:"
msgstr "يمكن تنفيذ باقي المهام باستخدام الأوامر التالية:"
#: pdf_bot/command/command_service.py:75
#, python-brace-format
msgid "{command} - compare PDF files"
msgstr "{command} - مقارنة ملفات PDF"
#: pdf_bot/command/command_service.py:76
#, python-brace-format
msgid "{command} - merge PDF files"
msgstr "{command} - دمج ملفات PDF"
#: pdf_bot/command/command_service.py:78
#, python-brace-format
msgid "{command} - convert and combine multiple images into PDF files"
msgstr "{command} - تحويل ودمج صور متعددة في ملفات PDF"
#: pdf_bot/command/command_service.py:80
#, python-brace-format
msgid "{command} - create PDF files from text messages"
msgstr "{command} -- إنشاء ملفات PDF من الرسائل النصية"
#: pdf_bot/command/command_service.py:83
#, python-brace-format
msgid "{command} - add watermark to PDF files"
msgstr "{command} - إضافة علامة مائية لملفات PDF"
#: pdf_bot/compare/compare_service.py:41
msgid "Send me one of the PDF files that you'll like to compare"
msgstr "أرسل لي أحد ملفات PDF التي تريد مقارنتها"
#: pdf_bot/compare/compare_service.py:42
msgid "Note that I can only look for text differences"
msgstr "لاحظ أنه يمكنني البحث فقط عن الاختلافات النصية"
#: pdf_bot/compare/compare_service.py:64
msgid "Send me the other PDF file that you'll like to compare"
msgstr "أرسل الملف الاخر الذي تريد مقارنته"
#: pdf_bot/compare/compare_service.py:83
msgid "Comparing your PDF files"
msgstr "جاري المقارنة بين الملفين"
#: pdf_bot/compare/compare_service.py:91
msgid "There are no text differences between your PDF files"
msgstr "لا توجد اختلافات نصية بين ملفات PDF"
#: pdf_bot/error/error_handler.py:38 pdf_bot/error/error_handler.py:44
#: pdf_bot/telegram_internal/telegram_service.py:102
#: pdf_bot/telegram_internal/telegram_service.py:118
msgid "Something went wrong, please try again"
msgstr "حدث خطأ ما، يرجى المحاولة مرة أخرى"
#: pdf_bot/error/error_handler.py:58 pdf_bot/error/error_service.py:17
msgid "The button has expired, start over with your file or command"
msgstr "انتهت صلاحية الزر ، ابدأ من جديد بملفك أو أمرك"
#: pdf_bot/error/error_handler.py:60
msgid "The resulted image is invalid, try again"
msgstr "الصورة الناتجة غير صالحة، حاول مرة أخرى"
#: pdf_bot/feedback/feedback_service.py:31
msgid "Send me your feedback in English"
msgstr "أرسل لي ملاحظاتك باللغة الإنجليزية"
#: pdf_bot/feedback/feedback_service.py:54
msgid "The feedback is not in English, try again"
msgstr "هذه المراجعة ليست باللغة الإنكليزية ، حاول مجدداً "
#: pdf_bot/feedback/feedback_service.py:58
msgid "Thank you for your feedback, I've forwarded it to my developer"
msgstr "شكرا لك على ملاحظاتك ، لقد أرسلتها إلى مطور البرامج الخاص بي"
#: pdf_bot/file/file_service.py:54
msgid "Your file is too big for me to download and process"
msgstr "ملفك كبير جدا بالنسبة لي لتحميل ومعالجة"
#: pdf_bot/file/file_service.py:56
msgid "Note that this is a Telegram Bot limitation and there's nothing I can do unless Telegram changes this limit"
msgstr "لاحظ أن هذا هو تقييد تيليجرام بوت وليس هناك شيء يمكنني القيام به ما لم تغير تيليجرام هذا الحد"
#: pdf_bot/file_processor/abstract_file_processor.py:105
#: pdf_bot/file_processor/abstract_file_processor.py:163
msgid "Processing your file"
msgstr "معالجة ملفك"
#: pdf_bot/file_processor/file_task_mixin.py:69
msgid "Select the task that you'll like to perform"
msgstr "حدد نوع المهمة التي تريد تنفيذها"
#: pdf_bot/image_handler/batch_image_service.py:20
#: pdf_bot/image_processor/beautify_image_processor.py:24
msgid "Beautify"
msgstr "تجميل"
#: pdf_bot/image_handler/batch_image_service.py:21
#: pdf_bot/image_processor/image_to_pdf_processor.py:24
msgid "To PDF"
msgstr "الى PDF"
#: pdf_bot/image_handler/batch_image_service.py:22
#: pdf_bot/merge/merge_service.py:19
msgid "Remove last file"
msgstr "إزالة الملف الأخير"
#: pdf_bot/image_handler/batch_image_service.py:42
msgid "Send me the images that you'll like to beautify or convert into a PDF file"
msgstr "أرسل لي الصور التي تريد تجميلها أو تحويلها إلى ملف PDF"
#: pdf_bot/image_handler/batch_image_service.py:45
msgid "Note that the images will be beautified and converted in the order that you send me"
msgstr "لاحظ أن الصور سيتم تجميلها وتحويلها بالترتيب الذي ترسله لي"
#: pdf_bot/image_handler/batch_image_service.py:93
msgid "You've sent me these images so far:"
msgstr "لقد أرسلت لي هذه الصور حتى الآن:"
#: pdf_bot/image_handler/batch_image_service.py:107
msgid "Select the task from below if you've sent me all the images, or keep sending me the images"
msgstr "حدد المهمة من الأسفل إذا كنت قد أرسلت لي جميع الصور ، أو استمر في إرسال الصور لي"
#: pdf_bot/image_handler/batch_image_service.py:127
msgid "You've already removed all the images you've sent me"
msgstr "لقد قمت بالفعل بإزالة جميع الصور التي أرسلتها لي"
#: pdf_bot/image_handler/batch_image_service.py:131
#, python-brace-format
msgid "{file_name} has been removed"
msgstr "تمت إزالة {file_name}"
#: pdf_bot/image_handler/batch_image_service.py:151
msgid "You haven't sent me any images"
msgstr "لم ترسل لي أي صور"
#: pdf_bot/image_handler/batch_image_service.py:154
msgid "You've only sent me one image"
msgstr "لقد أرسلت لي صورة واحدة فقط"
#: pdf_bot/image_handler/batch_image_service.py:171
msgid "Beautifying and converting your images into a PDF file"
msgstr "تجميل وتحويل صورك إلى ملف PDF"
#: pdf_bot/image_handler/batch_image_service.py:173
msgid "Converting your images into a PDF file"
msgstr "تحويل صورك إلى ملف PDF"
#: pdf_bot/language/language_service.py:87
msgid "Select your language"
msgstr "حدد لغتك"
#: pdf_bot/language/language_service.py:119
#, python-brace-format
msgid "Your language has been set to {language}"
msgstr "تم تعيين لغتك إلى {language}"
#: pdf_bot/merge/merge_service.py:38
msgid "Send me the PDF files that you'll like to merge"
msgstr "أرسل لي ملفات PDF التي تريد دمجها"
#: pdf_bot/merge/merge_service.py:39
msgid "Note that the files will be merged in the order that you send me"
msgstr "لاحظ أنه سيتم دمج الملفات بالترتيب الذي ترسله لي"
#: pdf_bot/merge/merge_service.py:85
msgid "You've sent me these PDF files so far:"
msgstr "لقد أرسلت لي ملفات PDF هذه حتى الآن:"
#: pdf_bot/merge/merge_service.py:99
#, python-brace-format
msgid "Press {done} if you've sent me all the PDF files that you'll like to merge or keep sending me the PDF files"
msgstr "اضغط {done} إذا كنت قد أرسلت لي جميع ملفات PDF التي تريد دمجها أو الاستمرار في إرسال ملفات PDF لي"
#: pdf_bot/merge/merge_service.py:120
msgid "You've already removed all the PDF files you've sent me"
msgstr "لقد قمت بالفعل بإزالة جميع ملفات PDF التي أرسلتها إلي"
#: pdf_bot/merge/merge_service.py:124
#, python-brace-format
msgid "{file_name} has been removed for merging"
msgstr "تمت إزالة {file_name} للدمج"
#: pdf_bot/merge/merge_service.py:146
msgid "You haven't sent me any PDF files"
msgstr "لم ترسل لي أي ملفات PDF"
#: pdf_bot/merge/merge_service.py:149
msgid "You've only sent me one PDF file"
msgstr "لقد أرسلت لي ملف PDF واحد فقط"
#: pdf_bot/merge/merge_service.py:162
msgid "Merging your PDF files"
msgstr "جاري دمج المفات"
#: pdf_bot/payment/payment_service.py:26
#, python-brace-format
msgid "{message} {emoji} (${value})"
msgstr "{message} {emoji} (${value})"
#: pdf_bot/payment/payment_service.py:30
msgid "Say Thanks"
msgstr "قل شكراً"
#: pdf_bot/payment/payment_service.py:31
msgid "Coffee"
msgstr "قهوة"
#: pdf_bot/payment/payment_service.py:32
msgid "Beer"
msgstr "بيرة"
#: pdf_bot/payment/payment_service.py:33
msgid "Meal"
msgstr "طعام"
#: pdf_bot/payment/payment_service.py:59
msgid "Select how you want to support PDF Bot"
msgstr "حدد كيف تريد دعم PDF Bot"
#: pdf_bot/payment/payment_service.py:75
msgid "Say thanks to PDF Bot and help keep it running"
msgstr "قل سكر ل PDF Bot و ساعد باستمراره"
#: pdf_bot/payment/payment_service.py:89
msgid "Something went wrong, try again"
msgstr "حدث خطأ ما، حاول مرة أخرى"
#: pdf_bot/payment/payment_service.py:96
msgid "Thank you for your support!"
msgstr "شكرا على دعمك!"
#: pdf_bot/payment/payment_service.py:115
msgid "Help translate PDF Bot"
msgstr "ساعد على ترجمة PDF Bot"
#: pdf_bot/pdf/exceptions.py:29
msgid "Your PDF file is encrypted, decrypt it first then try again"
msgstr "ملف PDF الخاص بك مشفر ، قم بفك تشفيره أولا ثم حاول مرة أخرى"
#: pdf_bot/pdf/pdf_service.py:163
msgid "Your PDF file is not encrypted"
msgstr "ان ملفك غير مشفر"
#: pdf_bot/pdf/pdf_service.py:167
msgid "Incorrect password, please try again"
msgstr "كلمة مرور غير صحيحة، يرجى المحاولة مرة أخرى"
#: pdf_bot/pdf/pdf_service.py:170
msgid "Your PDF file is encrypted with a method that I can't decrypt"
msgstr "ملف PDF مشفر بطريقة لا أستطيع فك تشفيرها"
#: pdf_bot/pdf/pdf_service.py:202
msgid "No images found in your PDF file"
msgstr "لم يتم العثور على صور في ملف PDF الخاص بك"
#: pdf_bot/pdf/pdf_service.py:214
msgid "No text found in your PDF file"
msgstr "لم يتم العثور على نص في ملف PDF الخاص بك"
#: pdf_bot/pdf/pdf_service.py:233
#, python-format
msgid "I couldn't merge your PDF files as this file is invalid: %s"
msgstr "لم أتمكن من دمج ملفات PDF الخاصة بك لأن هذا الملف غير صالح: %s"
#: pdf_bot/pdf/pdf_service.py:248
msgid "Your PDF file already has a text layer"
msgstr "ملفك بالفعل لديه طبقة نص"
#: pdf_bot/pdf/pdf_service.py:337
msgid "Your PDF file is invalid"
msgstr "ملف PDF غير صالح"
#: pdf_bot/pdf_processor/compress_pdf_processor.py:24
msgid "Compress"
msgstr "ضغط"
#: pdf_bot/pdf_processor/compress_pdf_processor.py:35
#, python-brace-format
msgid "File size reduced by {percent}, from {old_size} to {new_size}"
msgstr "حجم الملف الذي تم تخفيضه بمقدار {percent}، من {old_size} إلى {new_size}"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:17
msgid "By percentage"
msgstr "حسب النسبة المئوية"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:18
msgid "To margin size"
msgstr "إلى حجم الهامش"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:25
msgid "Send me a number between 0 and 100\n\n"
"This is the percentage of margin space to retain between the content in your PDF file and the page"
msgstr "أرسل لي رقما بين 0 و 100\n\n"
"هذه هي النسبة المئوية لمساحة الهامش للاحتفاظ بها بين المحتوى في ملف PDF الخاص بك والصفحة"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:31
msgid "Send me a number that you'll like to adjust the margin size\n\n"
"Positive numbers will decrease the margin size and negative numbers will increase it"
msgstr "أرسل لي رقما ترغب في ضبط حجم الهامش\n\n"
"الأرقام الموجبة ستقلل من حجم الهامش وستزيده الأرقام السالبة"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:56
msgid "Crop"
msgstr "قص"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:60
msgid "Select the crop type you'll like to perform"
msgstr "حدد نوع الاقتصاص الذي ترغب في تنفيذه"
#: pdf_bot/pdf_processor/crop_pdf_processor.py:68
msgid "The crop values are invalid, try again"
msgstr "قيم الاقتصاص غير صالحة، حاول مرة أخرى"
#: pdf_bot/pdf_processor/decrypt_pdf_processor.py:33
msgid "Decrypt"
msgstr "فُكَّ التشفير"
#: pdf_bot/pdf_processor/decrypt_pdf_processor.py:40
msgid "Send me the password to decrypt your PDF file"
msgstr "أرسل كلمة السر لفك تشفير الملف"
#: pdf_bot/pdf_processor/encrypt_pdf_processor.py:27
msgid "Encrypt"
msgstr "تشفير"
#: pdf_bot/pdf_processor/encrypt_pdf_processor.py:34
msgid "Send me the password to encrypt your PDF file"
msgstr "أرسل كلمة السر ل تشفير الملف"
#: pdf_bot/pdf_processor/extract_pdf_image_processor.py:24
msgid "Extract images"
msgstr "استخراج الصور"
#: pdf_bot/pdf_processor/extract_pdf_text_processor.py:24
msgid "Extract text"
msgstr "استخراج النص"
#: pdf_bot/pdf_processor/grayscale_pdf_processor.py:24
msgid "Grayscale"
msgstr "تدرج الرمادي"
#: pdf_bot/pdf_processor/pdf_to_image_processor.py:24
msgid "To images"
msgstr "إلى الصور"
#: pdf_bot/pdf_processor/preview_pdf_processor.py:24
msgid "Preview"
msgstr "معاينة"
#: pdf_bot/pdf_processor/rename_pdf_processor.py:30
msgid "Rename"
msgstr "اعادة تسمية"
#: pdf_bot/pdf_processor/rename_pdf_processor.py:35
#, python-format
msgid "File names can't contain any of the following characters, please try again:\n"
"%s"
msgstr "لا يمكن أن تحتوي أسماء الملفات على أي من الأحرف التالية، يرجى المحاولة مرة أخرى:\n"
"%s"
#: pdf_bot/pdf_processor/rename_pdf_processor.py:40
msgid "Send me the file name that you'll like to rename your PDF file into"
msgstr "ارسل لي اسمالكي اغير ملفك له"
#: pdf_bot/pdf_processor/rotate_pdf_processor.py:38
msgid "Rotate"
msgstr "تدوير"
#: pdf_bot/pdf_processor/rotate_pdf_processor.py:76
msgid "Select the degrees that you'll like to rotate your PDF file in clockwise"
msgstr "حدد الدرجة التي تريد تدوير الملف بهاحسب حركة عقارب الساعة"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:18
msgid "By factor"
msgstr "حسب العامل"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:19
msgid "To dimension"
msgstr "إلى البعد"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:26
msgid "Send me the scaling factors for the horizontal and vertical axes\n\n"
"Example: 2 0.5 - this will double the horizontal axis and halve the vertical axis"
msgstr "أرسل لي عوامل القياس للمحاور الأفقية والرأسية\n\n"
"مثال: 2 0.5 - سيؤدي ذلك إلى مضاعفة المحور الأفقي وخفض المحور الرأسي إلى النصف"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:32
msgid "Send me the width and height\n\n"
"Example: 150 200 - this will set the width to 150 and height to 200"
msgstr "أرسل لي العرض والارتفاع\n\n"
"مثال: 150 200 - سيؤدي ذلك إلى ضبط العرض على 150 والارتفاع على 200"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:56
msgid "Scale"
msgstr "قياس - نسبة"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:60
msgid "Select the scale type you'll like to perform"
msgstr "حدد نوع المقياس الذي ترغب في تنفيذه"
#: pdf_bot/pdf_processor/scale_pdf_processor.py:68
msgid "The scale values are invalid, try again"
msgstr "قيم المقياس غير صالحة، حاول مرة أخرى"
#: pdf_bot/pdf_processor/split_pdf_processor.py:29
msgid "Split"
msgstr "تقسيم"
#: pdf_bot/pdf_processor/split_pdf_processor.py:33
msgid "The split range is invalid, please try again"
msgstr "النطاق المقسم غير صالح ، يرجى المحاولة مرة أخرى"
#: pdf_bot/pdf_processor/split_pdf_processor.py:55
msgid "Send me the range of pages that you'll like to keep"
msgstr "أرسل لي مجموعة من الصفحات التي ستحب الاحتفاظ بها"
#: pdf_bot/pdf_processor/split_pdf_processor.py:56
msgid "General usage"
msgstr "الاستخدام العام"
#: pdf_bot/pdf_processor/split_pdf_processor.py:57
#, python-brace-format
msgid "{range} all pages"
msgstr "{range} جميع الصفحات"
#: pdf_bot/pdf_processor/split_pdf_processor.py:58
#, python-brace-format
msgid "{range} page 8 only"
msgstr "{range} الصفحة 8 فقط"
#: pdf_bot/pdf_processor/split_pdf_processor.py:59
#, python-brace-format
msgid "{range} first three pages"
msgstr "{range} الصفحات الثلاث الأولى"
#: pdf_bot/pdf_processor/split_pdf_processor.py:60
#, python-brace-format
msgid "{range} from page 8 onward"
msgstr "{range} من الصفحة 8 فصاعدا"
#: pdf_bot/pdf_processor/split_pdf_processor.py:61
#, python-brace-format
msgid "{range} last page only"
msgstr "{range} الصفحة الأخيرة فقط"
#: pdf_bot/pdf_processor/split_pdf_processor.py:62
#, python-brace-format
msgid "{range} all pages except the last page"
msgstr "{range} جميع الصفحات باستثناء الصفحة الأخيرة"
#: pdf_bot/pdf_processor/split_pdf_processor.py:63
#, python-brace-format
msgid "{range} second last page only"
msgstr "{range} الصفحة الأخيرة الثانية فقط"
#: pdf_bot/pdf_processor/split_pdf_processor.py:64
#, python-brace-format
msgid "{range} last two pages"
msgstr "{range} آخر صفحتين"
#: pdf_bot/pdf_processor/split_pdf_processor.py:65
#, python-brace-format
msgid "{range} third and second last pages"
msgstr "{range} الصفحتين الثالثة والثانية الأخيرة"
#: pdf_bot/pdf_processor/split_pdf_processor.py:66
msgid "Advanced usage"
msgstr "الاستخدام المتقدم"
#: pdf_bot/pdf_processor/split_pdf_processor.py:67
#, python-brace-format
msgid "{range} pages {pages} and to the end"
msgstr "{range} صفحات {pages} وحتى النهاية"
#: pdf_bot/pdf_processor/split_pdf_processor.py:70
#, python-brace-format
msgid "{range} pages {pages}"
msgstr "صفحات {range} {pages}"
#: pdf_bot/pdf_processor/split_pdf_processor.py:71
#, python-brace-format
msgid "{range} all pages in reversed order"
msgstr "{range} جميع الصفحات بترتيب معكوس"
#: pdf_bot/pdf_processor/split_pdf_processor.py:72
#, python-brace-format
msgid "{range} pages {pages} except {page}"
msgstr "{pages} الصفحات {range} باستثناء {page}"
#: pdf_bot/pdf_processor/split_pdf_processor.py:75
#, python-brace-format
msgid "{range} pages {pages}"
msgstr "صفحات {range} {pages}"
#: pdf_bot/telegram_internal/telegram_service.py:69
msgid "Your file is too large for me to download and process, please try again with a different file\n\n"
"Note that this limit is enforced by Telegram and there's nothing I can do unless Telegram changes it"
msgstr "ملفك كبير جدا بالنسبة لي لتنزيله ومعالجته ، يرجى المحاولة مرة أخرى باستخدام ملف مختلف\n\n"
"لاحظ أن هذا الحد يتم فرضه بواسطة Telegram ولا يوجد شيء يمكنني فعله ما لم يغيره Telegram"
#: pdf_bot/telegram_internal/telegram_service.py:81
msgid "The file is too large for me to send to you\n\n"
"Note that this limit is enforced by Telegram and there's nothing I can do unless Telegram changes it"
msgstr "الملف كبير جدا بالنسبة لي لإرساله إليك\n\n"
"لاحظ أن هذا الحد يتم فرضه بواسطة Telegram ولا يوجد شيء يمكنني القيام به ما لم يغيره Telegram"
#: pdf_bot/telegram_internal/telegram_service.py:156
msgid "Your file is not an image, please try again"
msgstr "ملفك ليس صورة، يرجى المحاولة مرة أخرى"
#: pdf_bot/telegram_internal/telegram_service.py:162
msgid "No image found in your message"
msgstr "لم يتم العثور على صورة في رسالتك"
#: pdf_bot/telegram_internal/telegram_service.py:172
msgid "Your file is not a PDF file, please try again"
msgstr "ملفك ليس ملف PDF، يرجى المحاولة مرة أخرى"
#: pdf_bot/telegram_internal/telegram_service.py:197
#: pdf_bot/telegram_internal/telegram_service.py:200
msgid "Action cancelled"
msgstr "تم الغاء الامر"
#: pdf_bot/telegram_internal/telegram_service.py:271
#: pdf_bot/telegram_internal/telegram_service.py:279
msgid "Here is your result file"
msgstr "هاهو ملفك الناتج"
#: pdf_bot/text/text_service.py:21
msgid "Skip"
msgstr "تخطي"
#: pdf_bot/text/text_service.py:40
msgid "Send me the text that you'll like to write into your PDF file"
msgstr "أرسل لي النص الذي تريد أن تكتبه في ملفك ال بي دي اف"
#: pdf_bot/text/text_service.py:59
#, python-brace-format
msgid "Send me the font that you'll like to use for the PDF file or press {skip} to use the default font"
msgstr "أرسل لي الخط الذي تريد استخدامه لملف PDF أو اضغط {skip} لاستخدام الخط الافتراضي"
#: pdf_bot/text/text_service.py:62
#, python-brace-format
msgid "See here for the list of supported fonts: {fonts}"
msgstr "انظر هنا للحصول على قائمة الخطوط المعتمدة: {fonts}"
#: pdf_bot/text/text_service.py:90
msgid "Unknown font, please try again"
msgstr "خط غير معروف، الرجاء المحاولة مرة أخرى"
#: pdf_bot/text/text_service.py:108
msgid "Creating your PDF file"
msgstr "أنشئ ملفك ال بي دي اف"
#: pdf_bot/watermark/watermark_service.py:38
msgid "Send me the PDF file that you'll like to add a watermark"
msgstr "أرسل الملف الذي تريد أن يكون له علامة مائية"
#: pdf_bot/watermark/watermark_service.py:56
msgid "Send me the watermark PDF file"
msgstr "أرسل ملف العلامة المائية PDF"
#: pdf_bot/watermark/watermark_service.py:74
msgid "Adding the watermark onto your PDF file"
msgstr "جاري اضافة العلامة المائية"
#: pdf_bot/webpage/webpage_service.py:41
msgid "You've sent me this webpage already and I'm still converting it"
msgstr "لقد أرسلت لي صفحة الويب هذه بالفعل وما زلت أقوم بتحويلها"
#: pdf_bot/webpage/webpage_service.py:45
msgid "Converting your webpage into a PDF file"
msgstr "تحويل صفحة الويب الخاصة بك إلى ملف PDF"
#: pdf_bot/webpage/webpage_service.py:72
msgid "Unable to reach your webpage"
msgstr "تعذر الوصول إلى صفحة الويب الخاصة بك"
#: pdf_bot/webpage/webpage_service.py:84
msgid "Failed to convert your webpage"
msgstr "فشل في تحويل صفحة الويب الخاصة بك"
#~ msgid ""
#~ "Send me the first photo that "
#~ "you'll like to beautify or convert "
#~ "into PDF\n"
#~ "\n"
#~ "Note that the photos will be "
#~ "beautified and converted in the order"
#~ " that you send me"
#~ msgstr ""
#~ msgid ""
#~ "Send me the next photo that you'll like to beautify or convert to PDF\n"
#~ "\n"
#~ "Select the task from below if you have sent me all the photos"
#~ msgstr ""
#~ msgid ""
#~ "Welcome to PDF Bot!\n"
#~ "\n"
#~ "*Features*\n"
#~ "- Compare, crop, decrypt, encrypt, "
#~ "merge, rotate, scale, split and add "
#~ "a watermark to a PDF file\n"
#~ "- Extract text and photos in a "
#~ "PDF file and convert a PDF file"
#~ " into photos\n"
#~ "- Beautify and convert photos into PDF format\n"
#~ "- Convert a web page into a PDF file\n"
#~ "\n"
#~ "Type /help to see how to use PDF Bot"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " simply by sending me a PDF "
#~ "file, a photo or a link to a"
#~ " web page.\n"
#~ "\n"
#~ "Some tasks can be performed by "
#~ "using the commands /compare, /merge, "
#~ "/watermark or /photo"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " by sending me one of the "
#~ "followings:\n"
#~ "- PDF files\n"
#~ "- Photos\n"
#~ "- Webpage links\n"
#~ "\n"
#~ "The rest of the tasks can be "
#~ "performed by using the commands "
#~ "/compare, /merge, /photo, /text or "
#~ "/watermark"
#~ msgstr ""
#~ msgid ""
#~ "Welcome to PDF Bot!\n"
#~ "\n"
#~ "*Key features:*\n"
#~ "- Compress, merge, preview, rename, "
#~ "split and add watermark to PDF "
#~ "files\n"
#~ "- Create PDF files from text messages\n"
#~ "- Extract images and text from PDF files\n"
#~ "- Convert PDF files into images\n"
#~ "- Convert webpages and images into PDF files\n"
#~ "- Beautify handwritten notes images into PDF files\n"
#~ "- _And more..._\n"
#~ "\n"
#~ "Type /help to see how to use PDF Bot"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " by sending me one of the "
#~ "followings:\n"
#~ "- PDF files\n"
#~ "- Photos\n"
#~ "- Webpage links\n"
#~ "\n"
#~ "The rest of the tasks can be performed by using the commands below:\n"
#~ "- /compare _PDF files_\n"
#~ "- /merge _PDF files_\n"
#~ "- /photo _convert and combine multiple photos into PDF files_\n"
#~ "- /text _create PDF files from text messages_\n"
#~ "- /watermark _add watermark to PDF files_"
#~ msgstr ""
#~ msgid ""
#~ "Press *Done* if you've sent me all"
#~ " the PDF files that you'll like "
#~ "to merge or keep sending me the"
#~ " PDF files"
#~ msgstr ""
#~ msgid "*{}* has been removed for merging"
#~ msgstr ""
#~ msgid "*{}* has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid "File size reduced by *{:.0%}*, from *{}* to *{}*"
#~ msgstr ""
#~ msgid "Renaming your PDF file into *{}*"
#~ msgstr ""
#~ msgid ""
#~ "Send me the width and height\n"
#~ "\n"
#~ "*Example: 150 200* (this will set the width to 150 and height to 200)"
#~ msgstr ""
#~ msgid ""
#~ "Send me the scaling factors for the horizontal and vertical axes\n"
#~ "\n"
#~ "2 will double the axis and 0.5 will halve the axis\n"
#~ "\n"
#~ "*Example: 2 0.5* (this will double "
#~ "the horizontal axis and halve the "
#~ "vertical axis)"
#~ msgstr ""
#~ msgid "The scaling factors *{}* are invalid, try again"
#~ msgstr ""
#~ msgid "The dimensions *{}* are invalid, try again"
#~ msgstr ""
#~ msgid "Scaling your PDF file, horizontally by *{}* and vertically by *{}*"
#~ msgstr ""
#~ msgid "Scaling your PDF file with width of *{}* and height of *{}*"
#~ msgstr ""
#~ msgid ""
#~ "Send me the range of pages that"
#~ " you'll like to keep. Use ⚡ "
#~ "*INSTANT VIEW* from below or refer "
#~ "to [here](http://telegra.ph/Telegram-PDF-Bot-07-16)"
#~ " for some range examples."
#~ msgstr ""
#~ msgid "*See above for all the text in your PDF file*"
#~ msgstr ""
#~ msgid ""
#~ "Send me the range of pages that"
#~ " you'll like to keep. Use ⚡ "
#~ "<b>INSTANT VIEW</b> from below or refer"
#~ " to [here](http://telegra.ph/Telegram-PDF-"
#~ "Bot-07-16) for some range examples."
#~ msgstr ""
#~ msgid ""
#~ "Send me your feedback or /cancel "
#~ "this action. Note that only English "
#~ "feedback will be forwarded to my "
#~ "developer."
#~ msgstr ""
#~ msgid ""
#~ "Send me your feedback, note that "
#~ "only English feedback will be forwarded"
#~ " to my developer"
#~ msgstr ""
#~ msgid ""
#~ "Send me the range of pages that you'll like to keep\n"
#~ "\n"
#~ "<b>General usage</b>\n"
#~ "<code>: all pages</code>\n"
#~ "<code>22 just the 23rd page</code>\n"
#~ "<code>0:3 the first three pages</code>\n"
#~ "<code>:3 the first three pages</code>\n"
#~ "<code>5: from the 6th page onwards</code>\n"
#~ "<code>-1 last page only</code>\n"
#~ "<code>:-1 all pages but the last page</code>\n"
#~ "<code>-2 second last page only</code>\n"
#~ "<code>-2: last two pages</code>\n"
#~ "<code>-3:-1 third and second last pages only</code>\n"
#~ "\n"
#~ "<b>Advanced usage</b>\n"
#~ "<code>::2 pages 0 2 4 ... to the end</code>\n"
#~ "<code>1:10:2 pages 1 3 5 7 9</code>\n"
#~ "<code>::-1 all pages in reversed order</code>\n"
#~ "<code>3:0:-1 pages 3 2 1 but not 0</code>\n"
#~ "<code>2::-1 pages 2 1 0</code>"
#~ msgstr ""
#~ msgid "Set Language"
#~ msgstr ""
#~ msgid "Send me the amount that you'll like to support PDF Bot"
#~ msgstr ""
#~ msgid "Say Awesome 🤩 (Custom)"
#~ msgstr ""
#~ msgid "The amount you sent is invalid, try again. {}"
#~ msgstr ""
#~ msgid "Select the font or select"
#~ msgstr ""
#~ msgid "to use the default font"
#~ msgstr ""
#~ msgid "Unknown font, please select a font from the list"
#~ msgstr ""
#~ msgid "Say Thanks 😁 ($1)"
#~ msgstr ""
#~ msgid "Coffee ☕ ($3)"
#~ msgstr ""
#~ msgid "Beer 🍺 ($5)"
#~ msgstr ""
#~ msgid "Meal 🍲 ($10)"
#~ msgstr ""
#~ msgid ""
#~ "Welcome to PDF Bot!\n"
#~ "\n"
#~ "<b>Key features:</b>\n"
#~ "- Compress, merge, preview, rename, "
#~ "split and add watermark to PDF "
#~ "files\n"
#~ "- Create PDF files from text messages\n"
#~ "- Extract images and text from PDF files\n"
#~ "- Convert PDF files into images\n"
#~ "- Convert webpages and images into PDF files\n"
#~ "- Beautify handwritten notes images into PDF files\n"
#~ "- <b><i>And more...</i></b>\n"
#~ "\n"
#~ "Type /help to see how to use PDF Bot"
#~ msgstr ""
#~ msgid ""
#~ "You can perform most of the tasks"
#~ " by sending me one of the "
#~ "followings:\n"
#~ "- PDF files\n"
#~ "- Photos\n"
#~ "- Webpage links\n"
#~ "\n"
#~ "The rest of the tasks can be performed by using the commands below:\n"
#~ "/compare - compare PDF files\n"
#~ "/merge - merge PDF files\n"
#~ "/photo - convert and combine multiple photos into PDF files\n"
#~ "/text - create PDF files from text messages\n"
#~ "/watermark - add watermark to PDF files"
#~ msgstr ""
#~ msgid "The file you sent is not a PDF file, try again"
#~ msgstr ""
#~ msgid ""
#~ "The PDF file you sent is too large for me to download\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid "Something went wrong, start over again"
#~ msgstr ""
#~ msgid ""
#~ "Your PDF file seems to be invalid and I couldn't open and read it\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid ""
#~ "Your {} PDF file is encrypted and you'll have to decrypt it first\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid ""
#~ "Your PDF file is encrypted and you'll have to decrypt it first\n"
#~ "\n"
#~ "I've cancelled your action"
#~ msgstr ""
#~ msgid "You've sent me these {} so far:\n"
#~ msgstr ""
#~ msgid ""
#~ "Send me one of the PDF files that you'll like to compare\n"
#~ "\n"
#~ "Note that I can only look for differences in text"
#~ msgstr ""
#~ msgid "There are no differences in text between your PDF files"
#~ msgstr ""
#~ msgid ""
#~ "Send me the PDF files that you'll like to merge\n"
#~ "\n"
#~ "Note that the files will be merged in the order that you send me"
#~ msgstr ""
#~ msgid "The file you've sent is not a PDF file"
#~ msgstr ""
#~ msgid "The PDF file you've sent is too large for me to download"
#~ msgstr ""
#~ msgid ""
#~ "Press <b>Done</b> if you've sent me "
#~ "all the PDF files that you'll like"
#~ " to merge or keep sending me "
#~ "the PDF files"
#~ msgstr ""
#~ msgid "<b>{}</b> has been removed for merging"
#~ msgstr ""
#~ msgid "You've only sent me one PDF file."
#~ msgstr ""
#~ msgid ""
#~ "I can't merge your PDF files as"
#~ " I couldn't open and read \"{}\". "
#~ "Ensure that it is not encrypted"
#~ msgstr ""
#~ msgid ""
#~ "Send me the photos that you'll "
#~ "like to beautify or convert into a"
#~ " PDF file\n"
#~ "\n"
#~ "Note that the photos will be "
#~ "beautified and converted in the order"
#~ " that you send me"
#~ msgstr ""
#~ msgid "The file you've sent is not a photo"
#~ msgstr ""
#~ msgid "The photo you've sent is too large for me to download"
#~ msgstr ""
#~ msgid "<b>{}</b> has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid ""
#~ "Send me the font that you'll like"
#~ " to use for the PDF file or "
#~ "skip to use the default font\n"
#~ "\n"
#~ "See here for the list of supported fonts:"
#~ msgstr ""
#~ msgid "File size reduced by <b>{:.0%}</b>, from <b>{}</b> to <b>{}</b>"
#~ msgstr ""
#~ msgid "Something went wrong, try again"
#~ msgstr ""
#~ msgid ""
#~ "Send me a number between {} and"
#~ " {}. This is the percentage of "
#~ "margin space to retain between the "
#~ "content in your PDF file and the"
#~ " page"
#~ msgstr ""
#~ msgid ""
#~ "Send me a number that you'll like"
#~ " to adjust the margin size. Positive"
#~ " numbers will decrease the margin "
#~ "size and negative numbers will increase"
#~ " it"
#~ msgstr ""
#~ msgid "The number must be between {} and {}, try again"
#~ msgstr ""
#~ msgid "The number is invalid, try again"
#~ msgstr ""
#~ msgid "Your PDF file seems to be invalid and I couldn't open and read it"
#~ msgstr ""
#~ msgid "The decryption password is incorrect, try to send it again"
#~ msgstr ""
#~ msgid "Your PDF file is encrypted with a method that I cannot decrypt"
#~ msgstr ""
#~ msgid ""
#~ "Your PDF file is too big for me to download\n"
#~ "\n"
#~ "I can't perform any tasks on it"
#~ msgstr ""
#~ msgid ""
#~ "Your photo is too large for me "
#~ "to download. I can't beautify or "
#~ "convert your photo"
#~ msgstr ""
#~ msgid ""
#~ "File names can't contain any of the following characters:\n"
#~ "{}\n"
#~ "Send me another file name"
#~ msgstr ""
#~ msgid "Renaming your PDF file into <b>{}</b>"
#~ msgstr ""
#~ msgid ""
#~ "Send me the width and height\n"
#~ "\n"
#~ "<b>Example: 150 200</b> (this will set"
#~ " the width to 150 and height to"
#~ " 200)"
#~ msgstr ""
#~ msgid ""
#~ "Send me the scaling factors for the horizontal and vertical axes\n"
#~ "\n"
#~ "<b>Example: 2 0.5</b> (this will double"
#~ " the horizontal axis and halve the"
#~ " vertical axis)"
#~ msgstr ""
#~ msgid "The scaling factors <b>{}</b> are invalid, try again"
#~ msgstr ""
#~ msgid "The dimensions <b>{}</b> are invalid, try again"
#~ msgstr ""
#~ msgid ""
#~ "Scaling your PDF file, horizontally by"
#~ " <b>{}</b> and vertically by <b>{}</b>"
#~ msgstr ""
#~ msgid "Scaling your PDF file with width of <b>{}</b> and height of <b>{}</b>"
#~ msgstr ""
#~ msgid "all pages"
#~ msgstr ""
#~ msgid "page 8 only"
#~ msgstr ""
#~ msgid "first three pages"
#~ msgstr ""
#~ msgid "from page 8 onward"
#~ msgstr ""
#~ msgid "last page only"
#~ msgstr ""
#~ msgid "all pages except the last page"
#~ msgstr ""
#~ msgid "second last page only"
#~ msgstr ""
#~ msgid "last two pages"
#~ msgstr ""
#~ msgid "third and second last pages"
#~ msgstr ""
#~ msgid "pages"
#~ msgstr ""
#~ msgid "to the end"
#~ msgstr ""
#~ msgid "all pages in reversed order"
#~ msgstr ""
#~ msgid "except"
#~ msgstr ""
#~ msgid "The range is invalid. Try again"
#~ msgstr ""
#~ msgid "<b>See above for all the text in your PDF file</b>"
#~ msgstr ""
#~ msgid "Type {} to see how to use PDF Bot"
#~ msgstr ""
#~ msgid "{} - compare PDF files"
#~ msgstr ""
#~ msgid "{} - merge PDF files"
#~ msgstr ""
#~ msgid "{} - convert and combine multiple photos into PDF files"
#~ msgstr ""
#~ msgid "{} - create PDF files from text messages"
#~ msgstr ""
#~ msgid "{} - add watermark to PDF files"
#~ msgstr ""
#~ msgid "Your language has been set to {}"
#~ msgstr ""
#~ msgid "Your {} PDF file is encrypted and you'll have to decrypt it first"
#~ msgstr ""
#~ msgid "You've sent me these {} so far:"
#~ msgstr ""
#~ msgid ""
#~ "Press {} if you've sent me all "
#~ "the PDF files that you'll like to"
#~ " merge or keep sending me the "
#~ "PDF files"
#~ msgstr ""
#~ msgid "{} has been removed for merging"
#~ msgstr ""
#~ msgid "I couldn't merge your PDF files as this file is invalid: {}"
#~ msgstr ""
#~ msgid "{} has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid ""
#~ "Send me the font that you'll like"
#~ " to use for the PDF file or "
#~ "press {} to use the default font"
#~ msgstr ""
#~ msgid "See here for the list of supported fonts: {}"
#~ msgstr ""
#~ msgid "Renaming your PDF file into {}"
#~ msgstr ""
#~ msgid "Rotating your PDF file clockwise by {} degrees"
#~ msgstr ""
#~ msgid "Example: 150 200"
#~ msgstr ""
#~ msgid "Example: 2 0.5"
#~ msgstr ""
#~ msgid "The scaling factors {} are invalid, please try again"
#~ msgstr ""
#~ msgid "The dimensions {} are invalid, please try again"
#~ msgstr ""
#~ msgid "{} all pages"
#~ msgstr ""
#~ msgid "{} page 8 only"
#~ msgstr ""
#~ msgid "{} first three pages"
#~ msgstr ""
#~ msgid "{} from page 8 onward"
#~ msgstr ""
#~ msgid "{} last page only"
#~ msgstr ""
#~ msgid "{} all pages except the last page"
#~ msgstr ""
#~ msgid "{} second last page only"
#~ msgstr ""
#~ msgid "{} last two pages"
#~ msgstr ""
#~ msgid "{} third and second last pages"
#~ msgstr ""
#~ msgid "{} all pages in reversed order"
#~ msgstr ""
#~ msgid "See above for all the text in your PDF file"
#~ msgstr ""
#~ msgid "You can perform most of the tasks by sending me one of the followings"
#~ msgstr ""
#~ msgid "The rest of the tasks can be performed by using the following commands"
#~ msgstr ""
#~ msgid "Your PDF file seems to be invalid and I couldn't open and process it"
#~ msgstr ""
#~ msgid "Your PDF file is too large for me to download"
#~ msgstr ""
#~ msgid "Your file is too large for me to download"
#~ msgstr ""
#~ msgid "Your photo is too large for me to download"
#~ msgstr ""
#~ msgid "{range} pages {results} and to the end"
#~ msgstr ""
#~ msgid "{range} pages {results}"
#~ msgstr ""
#~ msgid "{range} pages {results} except {except_text}"
#~ msgstr ""
#~ msgid "{range} pages {results}"
#~ msgstr ""
#~ msgid "Your PDF file is too large for me to download and process"
#~ msgstr ""
#~ msgid "Your PDF file is too big for me to download"
#~ msgstr ""
#~ msgid "Say Thanks {emoji} ({value})"
#~ msgstr ""
#~ msgid "Coffee {emoji} ({value})"
#~ msgstr ""
#~ msgid "Beer {emoji} ({value})"
#~ msgstr ""
#~ msgid "Meal {emoji} ({value})"
#~ msgstr ""
#~ msgid "Failed to process, please try again"
#~ msgstr ""
#~ msgid "Extract Photos"
#~ msgstr ""
#~ msgid "To Photos"
#~ msgstr ""
#~ msgid "Photos"
#~ msgstr ""
#~ msgid "- Photos"
#~ msgstr ""
#~ msgid "{command} - convert and combine multiple photos into PDF files"
#~ msgstr ""
#~ msgid ""
#~ "Send me the photos that you'll "
#~ "like to beautify or convert into a"
#~ " PDF file"
#~ msgstr ""
#~ msgid ""
#~ "Note that the photos will be "
#~ "beautified and converted in the order"
#~ " that you send me"
#~ msgstr ""
#~ msgid "Your file is not a photo"
#~ msgstr ""
#~ msgid "photos"
#~ msgstr ""
#~ msgid ""
#~ "Select the task from below if "
#~ "you've sent me all the photos, or"
#~ " keep sending me the photos"
#~ msgstr ""
#~ msgid "Beautifying and converting your photos"
#~ msgstr ""
#~ msgid "Converting your photos into PDF"
#~ msgstr ""
#~ msgid "Your photo is too large for me to download and process"
#~ msgstr ""
#~ msgid "Converting your PDF file into photos"
#~ msgstr ""
#~ msgid "Extracting all the photos in your PDF file"
#~ msgstr ""
#~ msgid "I couldn't find any photos in your PDF file"
#~ msgstr ""
#~ msgid "See above for all your photos"
#~ msgstr ""
#~ msgid ""
#~ "Your {file_type} PDF file is encrypted"
#~ " and you'll have to decrypt it "
#~ "first"
#~ msgstr ""
#~ msgid "watermark"
#~ msgstr ""
#~ msgid "PDF files"
#~ msgstr ""
#~ msgid "Your file is not a PDF file"
#~ msgstr ""
#~ msgid ""
#~ "Your file is not a PDF file, "
#~ "please try again and ensure that "
#~ "your file has the .pdf extension"
#~ msgstr ""
#~ msgid "Converting your PDF file to black and white"
#~ msgstr ""
#~ msgid "Compressing your PDF file"
#~ msgstr ""
#~ msgid ""
#~ "The number must be between {min_percent}"
#~ " and {max_percent}, please try again"
#~ msgstr ""
#~ msgid "The number is invalid, please try again"
#~ msgstr ""
#~ msgid "Cropping your PDF file"
#~ msgstr ""
#~ msgid "Decrypting your PDF file"
#~ msgstr ""
#~ msgid "Your PDF file is invalid and I couldn't open and process it"
#~ msgstr ""
#~ msgid "The decryption password is incorrect, please try again"
#~ msgstr ""
#~ msgid "Encrypting your PDF file"
#~ msgstr ""
#~ msgid "Adding an OCR text layer to your PDF file"
#~ msgstr ""
#~ msgid "Renaming your PDF file into {file_name}"
#~ msgstr ""
#~ msgid "Rotating your PDF file clockwise by {degree} degrees"
#~ msgstr ""
#~ msgid ""
#~ "Scaling your PDF file, horizontally by"
#~ " {horizontal} and vertically by {vertical}"
#~ msgstr ""
#~ msgid "Scaling your PDF file with width of {width} and height of {height}"
#~ msgstr ""
#~ msgid "Splitting your PDF file"
#~ msgstr ""
#~ msgid "Select how you'll like me to send the text to you"
#~ msgstr ""
#~ msgid "Extracting text from your PDF file"
#~ msgstr ""
#~ msgid "I couldn't find any text in your PDF file"
#~ msgstr ""
#~ msgid ""
#~ "Your image is too large for me "
#~ "to download and process, please try "
#~ "again with a differnt image\n"
#~ "\n"
#~ "Note that this is a Telegram Bot"
#~ " limitation and there's nothing I can"
#~ " do unless Telegram changes this "
#~ "limit"
#~ msgstr ""
#~ msgid "Testing"
#~ msgstr ""
#~ msgid ""
#~ "Your file is too large for me "
#~ "to download and process, please try "
#~ "again with a differnt file\n"
#~ "\n"
#~ "Note that this is a Telegram Bot"
#~ " limitation and there's nothing I can"
#~ " do unless Telegram changes this "
#~ "limit"
#~ msgstr ""
#~ msgid "Remove Last File"
#~ msgstr ""
#~ msgid "Extracting a preview for your PDF file"
#~ msgstr ""
#~ msgid "Converting your PDF file into images"
#~ msgstr ""
#~ msgid "Select the result file format"
#~ msgstr ""
#~ msgid "Extracting all the images in your PDF file"
#~ msgstr ""
#~ msgid "I couldn't find any images in your PDF file"
#~ msgstr ""
#~ msgid "See above for all your images"
#~ msgstr ""
#~ msgid "Your image is too large for me to download and process"
#~ msgstr ""
#~ msgid "File name unavailable"
#~ msgstr ""
#~ msgid "Your file is not an image"
#~ msgstr ""
#~ msgid "Your file is too large for me to download and process"
#~ msgstr ""
#~ msgid "images"
#~ msgstr ""
#~ msgid "{file_name} has been removed for beautifying or converting"
#~ msgstr ""
#~ msgid "Beautifying and converting your images"
#~ msgstr ""
#~ msgid "Converting your images into PDF"
#~ msgstr ""
#~ msgid ""
#~ "Send me your feedback (only English "
#~ "feedback will be forwarded to my "
#~ "developer)"
#~ msgstr ""
#~ msgid "Thank you for your feedback, I've already forwarded it to my developer"
#~ msgstr ""
#~ msgid "Something went wrong"
#~ msgstr ""
#~ msgid ""
#~ "Timed out processing your web page, "
#~ "your web page is probably too "
#~ "complex to process"
#~ msgstr ""
#~ msgid "Something went wrong, please start over again"
#~ msgstr ""
#~ msgid "Your file is invalid and I couldn't open and process it"
#~ msgstr ""
#~ msgid "Your PDF file is already encrypted"
#~ msgstr ""
#~ msgid "Your PDF file is encrypted and you'll have to decrypt it first"
#~ msgstr ""
#~ msgid "You've sent me these {file_type} so far:"
#~ msgstr ""
#~ msgid "The result file is too large for me to send to you"
#~ msgstr ""
#~ msgid ""
#~ "The button has expired, please try "
#~ "again with a new message/query then "
#~ "press the new button"
#~ msgstr ""
#~ msgid "Invalid rotation degree, try again"
#~ msgstr ""
#~ msgid "The scaling factors {values} are invalid, please try again"
#~ msgstr ""
#~ msgid "The dimensions {values} are invalid, please try again"
#~ msgstr ""
#~ msgid "Send me the scaling factors for the horizontal and vertical axes"
#~ msgstr ""
#~ msgid "This will double the horizontal axis and halve the vertical axis"
#~ msgstr ""
#~ msgid "Send me the width and height"
#~ msgstr ""
#~ msgid "This will set the width to 150 and height to 200"
#~ msgstr ""
#~ msgid "File names can't contain any of the following characters:"
#~ msgstr ""
#~ msgid "Please try again"
#~ msgstr ""
#~ msgid "The range is invalid, please try again"
#~ msgstr ""
#~ msgid "By scaling factor"
#~ msgstr ""
#~ msgid "Select the scale type that you'll like to perform"
#~ msgstr ""
#~ msgid "The values {values} are invalid, please try again"
#~ msgstr ""
#~ msgid "By margin size"
#~ msgstr ""
#~ msgid "Select the crop type that you'll like to perform"
#~ msgstr ""
#~ msgid ""
#~ "The number {number} is not between "
#~ "{min_percent} and {max_percent}, please try"
#~ " again"
#~ msgstr ""
#~ msgid "The number {number} is invalid, please try again"
#~ msgstr ""
#~ msgid "Send me a number between {min_percent} and {max_percent}"
#~ msgstr ""
#~ msgid ""
#~ "This is the percentage of margin "
#~ "space to retain between the content "
#~ "in your PDF file and the page"
#~ msgstr ""
#~ msgid "Send me a number that you'll like to adjust the margin size"
#~ msgstr ""
#~ msgid ""
#~ "Positive numbers will decrease the "
#~ "margin size and negative numbers will"
#~ " increase it"
#~ msgstr ""
#~ msgid ""
#~ "Select the task that you'll like "
#~ "to perform from below or select "
#~ "from the buttons"
#~ msgstr ""
#~ msgid "By Percentage"
#~ msgstr ""
#~ msgid "By Margin Size"
#~ msgstr ""
#~ msgid "Extract Images"
#~ msgstr ""
#~ msgid "To Images"
#~ msgstr ""
#~ msgid "Compressed"
#~ msgstr ""
#~ msgid "Images"
#~ msgstr ""
#~ msgid "To Dimensions"
#~ msgstr ""
#~ msgid "Extract Text"
#~ msgstr ""
#~ msgid "Text Message"
#~ msgstr ""
#~ msgid "Text File"
#~ msgstr ""
#~ msgid "Black & White"
#~ msgstr ""
#~ msgid "You've sent me this web page already and I'm still converting it"
#~ msgstr ""
#~ msgid "Converting your web page into a PDF file"
#~ msgstr ""
#~ msgid "Unable to reach your web page"
#~ msgstr ""
#~ msgid "Failed to convert your web page"
#~ msgstr ""
#~ msgid "Something went wrong, start over with your file or command"
#~ msgstr ""
#~ msgid "Your PDF file is encrypted"
#~ msgstr ""
#~ msgid "I couldn't merge your PDF files as this file is invalid: {file_name}"
#~ msgstr ""
#~ msgid ""
#~ "File names can't contain any of "
#~ "the following characters, please try "
#~ "again:\n"
#~ "{invalid_chars}"
#~ msgstr ""
#~ msgid ""
#~ "Your file is too large for me "
#~ "to download and process, please try "
#~ "again with a differnt file\n"
#~ "\n"
#~ "Note that this limit is enforced "
#~ "by Telegram and there's nothing I "
#~ "can do unless Telegram changes it"
#~ msgstr ""
================================================
FILE: locale/ca_ES/LC_MESSAGES/pdf_bot.po
================================================
# locale translations for telegram-pdf-bot.
# Copyright (C) 2021 zeshuaro
# This file is distributed under the same license as the telegram-pdf-bot
# project.
# zeshuaro <zeshuaro@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: telegram-pdf-bot\n"
"Report-Msgid-Bugs-To: zeshuaro@gmail.com\n"
"POT-Creation-Date: 2025-10-19 06:16+0000\n"
"PO-Revision-Date: 2025-10-19 06:16\n"
"Last-Translator: \n"
"Language: ca\n"
"Language-Team: Catalan\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.17.0\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: telegram-pdf-bot\n"
"X-Crowdin-Project-ID: 370289\n"
"X-Crowdin-Language: ca\n"
"X-Crowdin-File: pdf_bot.po\n"
"X-Crowdin-File-ID: 88\n"
#: pdf_bot/consts.py:11
msgid "Cancel"
msgstr "Cancelar"
#: pdf_bot/consts.py:12
msgid "Done"
msgstr "Fet"
#: pdf_bot/consts.py:13 pdf_bot/telegram_internal/telegram_service.py:48
msgid "Back"
msgstr "Enrera"
#: pdf_bot/consts.py:23
msgid "Something went wrong, start over with your file or command"
msgstr "Alguna cosa ha fallat, torneu a començar amb el vostre fitxer o ordre"
#: pdf_bot/cli/cli_service.py:35
msgid "Failed to complete process"
msgstr "No s'ha pogut completar el procés"
#: pdf_bot/command/command_service.py:38
msgid "Welcome to PDF Bot!"
msgstr "Benvinguts al PDF Bot!"
#: pdf_bot/command/command_service.py:39
msgid "Key features:"
msgstr "Característiques clau:"
#: pdf_bot/command/command_service.py:41
msgid "- Compress, merge, preview, rename, split and add watermark to PDF files"
msgstr "- Comprimir, combinar, previsualitzar, canviar el nom, dividir i afegir marca d'aigua als fitxers PDF"
#: pdf_bot/command/command_service.py:43
msgid "- Create PDF files from text messages"
msgstr "- Crear fitxers PDF a partir de missatges de text"
#: pdf_bot/command/command_service.py:44
msgid "- Extract images and text from PDF files"
msgstr "- Extreure imatges i text d'arxius PDF"
#: pdf_bot/command/command_service.py:45
msgid "- Convert PDF files into images"
msgstr "- Convertir arxius PDF en imatges"
#: pdf_bot/command/command_service.py:46
msgid "- Convert webpages and images into PDF files"
msgstr "- Convertir pàgines web i imatges en arxius PDF"
#: pdf_bot/command/command_service.py:47
msgid "- Beautify handwritten notes images into PDF files"
msgstr "- Imatges de notes manuscrites embellidores en arxius PDF"
#: pdf_bot/command/command_service.py:48
msgid "- And more..."
msgstr "- I més..."
#: pdf_bot/command/command_service.py:49
#, python-brace-format
msgid "Type {command} to see how to use PDF Bot"
msgstr "Tipus {command} per veure com utilitzar PDF Bot"
#: pdf_bot/command/command_service.py:58
msgid "Set Language 🌎"
msgstr "Defineix l'idioma 🌎"
#: pdf_bot/command/command_service.py:60
#: pdf_bot/telegram_internal/telegram_service.py:222
msgid "Join Channel"
msgstr "Entrar a un canal"
#: pdf_bot/command/command_service.py:61 pdf_bot/payment/payment_service.py:74
#: pdf_bot/telegram_internal/telegram_service.py:223
msgid "Support PDF Bot"
msgstr "Ajuda PDF bot"
#: pdf_bot/command/command_service.py:70
msgid "You can perform most of the tasks by sending me one of the followings:"
msgstr "Podeu realitzar la majoria de les tasques enviant-me una de les següents:"
#: pdf_bot/command/command_service.py:71
msgid "- PDF files"
msgstr "- Arxius PDF"
#: pdf_bot/command/command_service.py:72
msgid "- Images"
msgstr "- Imatges"
#: pdf_bot/command/command_service.py:73
msgid "- Webpage links"
msgstr "- Enllaços de pàgines web"
#: pdf_bot/command/command_service.py:74
msgid "The rest of the tasks can be performed by using the following commands:"
msgstr "La resta de tasques es poden dur a terme mitjançant les ordres següents:"
#: pdf_bot/command/command_service.py:75
#, python-brace-format
msgid "{command} - compare PDF files"
msgstr "{command} - compareu els fitxers PDF"
#: pdf_bot/command/command_service.py:76
#, python-brace-format
msgid "{command} - merge PDF files"
msgstr "{command} : combina fitxers PDF"
#: pdf_bot/command/command_service.py:78
#, python-brace-format
msgid "{command} - convert and combine multiple images into PDF files"
msgstr "{command} : converteix i combina diverses imatges en fitxers PDF"
#: pdf_bot/command/command_service.py:80
#, python-brace-format
msgid "{command} - create PDF files from text messages"
msgstr "{command} : creeu fitxers PDF a partir de missatges de text"
#: pdf_bot/command/command_service.py:83
#, python-brace-format
msgid "{command} - add watermark to PDF files"
msgstr "{command} - afegiu la marca d'aigua als fitxers PDF"
#: pdf_bot/compare/compare_service.py:41
msgid "Send me one of the PDF files that you'll like to compare"
msgstr "Envia'm un dels arxius PDF que t'agradaria comparar"
#: pdf_bot/compare/compare_service.py:42
msgid "N
gitextract_l0t6q8aw/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ ├── github-actions.yml
│ ├── pull-request-target.yml
│ ├── pull-request.yml
│ ├── scheduled.yml
│ └── update-translations.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .python-version
├── .vscode/
│ ├── launch.json
│ └── settings.json
├── Dockerfile
├── LICENSE
├── README.md
├── _typos.toml
├── codecov.yml
├── crowdin.yml
├── locale/
│ ├── af_ZA/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── am_ET/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ar_SA/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ca_ES/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── cs_CZ/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── da_DK/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── de_DE/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── el_GR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── en_GB/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── en_US/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── es_ES/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── fa_IR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── fi_FI/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── fr_FR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── he_IL/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── hi_IN/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── hu_HU/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── id_ID/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── it_IT/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ja_JP/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ko_KR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ky_KG/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ms_MY/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── nl_NL/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── no_NO/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── om_ET/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── pl_PL/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── pt_BR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── pt_PT/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ro_RO/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ru_RU/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── si_LK/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── sr_SP/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── sv_SE/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── ta_IN/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── tr_TR/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── uk_UA/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── uz_UZ/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── vi_VN/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── zh_CN/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ ├── zh_HK/
│ │ └── LC_MESSAGES/
│ │ └── pdf_bot.po
│ └── zh_TW/
│ └── LC_MESSAGES/
│ └── pdf_bot.po
├── pdf_bot/
│ ├── __init__.py
│ ├── __main__.py
│ ├── account/
│ │ ├── __init__.py
│ │ ├── account_repository.py
│ │ └── account_service.py
│ ├── analytics/
│ │ ├── __init__.py
│ │ ├── analytics_repository.py
│ │ ├── analytics_service.py
│ │ └── models.py
│ ├── cli/
│ │ ├── __init__.py
│ │ ├── cli_service.py
│ │ └── exceptions.py
│ ├── command/
│ │ ├── __init__.py
│ │ ├── command_handler.py
│ │ └── command_service.py
│ ├── compare/
│ │ ├── __init__.py
│ │ ├── compare_handler.py
│ │ └── compare_service.py
│ ├── consts.py
│ ├── containers.py
│ ├── datastore/
│ │ ├── __init__.py
│ │ └── datastore_client.py
│ ├── error/
│ │ ├── __init__.py
│ │ ├── error_callback_query_handler.py
│ │ ├── error_handler.py
│ │ └── error_service.py
│ ├── errors.py
│ ├── feedback/
│ │ ├── __init__.py
│ │ ├── feedback_handler.py
│ │ ├── feedback_repository.py
│ │ └── feedback_service.py
│ ├── file/
│ │ ├── __init__.py
│ │ ├── file_handler.py
│ │ └── file_service.py
│ ├── file_processor/
│ │ ├── __init__.py
│ │ ├── abstract_file_processor.py
│ │ ├── abstract_file_task_processor.py
│ │ ├── errors.py
│ │ └── file_task_mixin.py
│ ├── image/
│ │ ├── __init__.py
│ │ └── image_service.py
│ ├── image_handler/
│ │ ├── __init__.py
│ │ ├── batch_image_handler.py
│ │ └── batch_image_service.py
│ ├── image_processor/
│ │ ├── __init__.py
│ │ ├── abstract_image_processor.py
│ │ ├── beautify_image_processor.py
│ │ ├── image_task_processor.py
│ │ └── image_to_pdf_processor.py
│ ├── io_internal/
│ │ ├── __init__.py
│ │ └── io_service.py
│ ├── language/
│ │ ├── __init__.py
│ │ ├── language_handler.py
│ │ ├── language_repository.py
│ │ ├── language_service.py
│ │ └── models.py
│ ├── log/
│ │ ├── __init__.py
│ │ └── log_handler.py
│ ├── merge/
│ │ ├── __init__.py
│ │ ├── merge_handler.py
│ │ └── merge_service.py
│ ├── models.py
│ ├── payment/
│ │ ├── __init__.py
│ │ ├── models.py
│ │ ├── payment_handler.py
│ │ └── payment_service.py
│ ├── pdf/
│ │ ├── __init__.py
│ │ ├── exceptions.py
│ │ ├── models.py
│ │ └── pdf_service.py
│ ├── pdf_processor/
│ │ ├── __init__.py
│ │ ├── abstract_pdf_processor.py
│ │ ├── abstract_pdf_select_and_text_processor.py
│ │ ├── abstract_pdf_text_input_processor.py
│ │ ├── compress_pdf_processor.py
│ │ ├── crop_pdf_processor.py
│ │ ├── decrypt_pdf_processor.py
│ │ ├── encrypt_pdf_processor.py
│ │ ├── extract_pdf_image_processor.py
│ │ ├── extract_pdf_text_processor.py
│ │ ├── grayscale_pdf_processor.py
│ │ ├── ocr_pdf_processor.py
│ │ ├── pdf_task_processor.py
│ │ ├── pdf_to_image_processor.py
│ │ ├── preview_pdf_processor.py
│ │ ├── rename_pdf_processor.py
│ │ ├── rotate_pdf_processor.py
│ │ ├── scale_pdf_processor.py
│ │ └── split_pdf_processor.py
│ ├── settings.py
│ ├── telegram_handler/
│ │ ├── __init__.py
│ │ └── abstract_telegram_handler.py
│ ├── telegram_internal/
│ │ ├── __init__.py
│ │ ├── exceptions.py
│ │ └── telegram_service.py
│ ├── text/
│ │ ├── __init__.py
│ │ ├── text_handler.py
│ │ ├── text_repository.py
│ │ └── text_service.py
│ ├── watermark/
│ │ ├── __init__.py
│ │ ├── watermark_handler.py
│ │ └── watermark_service.py
│ └── webpage/
│ ├── __init__.py
│ ├── webpage_handler.py
│ └── webpage_service.py
├── pyproject.toml
├── renovate.json
└── tests/
├── __init__.py
├── account/
│ ├── __init__.py
│ ├── test_account_repository.py
│ └── test_account_service.py
├── analytics/
│ ├── __init__.py
│ ├── test_analytics_repository.py
│ └── test_analytics_service.py
├── cli/
│ ├── __init__.py
│ └── test_cli_service.py
├── command/
│ ├── __init__.py
│ ├── test_command_handler.py
│ └── test_command_service.py
├── compare/
│ ├── __init__.py
│ ├── test_compare_handler.py
│ └── test_compare_service.py
├── conftest.py
├── datastore/
│ ├── __init__.py
│ └── test_datastore_client.py
├── error/
│ ├── __init__.py
│ ├── test_error_callback_query_handler.py
│ ├── test_error_handler.py
│ └── test_error_service.py
├── feedback/
│ ├── __init__.py
│ ├── test_feedback_handler.py
│ ├── test_feedback_repository.py
│ └── test_feedback_service.py
├── file/
│ ├── __init__.py
│ ├── test_file_handler.py
│ └── test_file_service.py
├── file_processor/
│ ├── __init__.py
│ ├── test_abstract_file_processor.py
│ ├── test_abstract_file_task_processor.py
│ └── test_file_task_mixin.py
├── image/
│ ├── __init__.py
│ └── test_image_service.py
├── image_handler/
│ ├── __init__.py
│ ├── test_batch_image_handler.py
│ └── test_batch_image_service.py
├── image_processor/
│ ├── __init__.py
│ ├── test_abstract_image_processor.py
│ ├── test_beautify_image_processor.py
│ ├── test_image_task_processor.py
│ └── test_image_to_pdf_processor.py
├── io_internal/
│ ├── __init__.py
│ └── test_io_service.py
├── language/
│ ├── __init__.py
│ ├── language_service_test_mixin.py
│ ├── test_language_handler.py
│ ├── test_language_repository.py
│ └── test_language_service.py
├── merge/
│ ├── __init__.py
│ ├── test_merge_handler.py
│ └── test_merge_service.py
├── path_test_mixin.py
├── payment/
│ ├── __init__.py
│ ├── test_payment_handler.py
│ └── test_payment_service.py
├── pdf/
│ ├── __init__.py
│ ├── test_compress_result.py
│ └── test_pdf_service.py
├── pdf_processor/
│ ├── __init__.py
│ ├── test_abstract_pdf_processor.py
│ ├── test_abstract_pdf_select_and_text_processor.py
│ ├── test_abstract_pdf_text_input_processor.py
│ ├── test_compress_pdf_processor.py
│ ├── test_crop_pdf_processor.py
│ ├── test_decrypt_pdf_processor.py
│ ├── test_encrypt_pdf_processor.py
│ ├── test_extract_pdf_image_processor.py
│ ├── test_extract_pdf_text_processor.py
│ ├── test_grayscale_pdf_processor.py
│ ├── test_ocr_pdf_processor.py
│ ├── test_pdf_task_processor.py
│ ├── test_pdf_to_image_processor.py
│ ├── test_preview_pdf_processor.py
│ ├── test_rename_pdf_processor.py
│ ├── test_rotate_pdf_processor.py
│ ├── test_scale_pdf_processor.py
│ └── test_split_pdf_processor.py
├── telegram_internal/
│ ├── __init__.py
│ ├── telegram_service_test_mixin.py
│ ├── telegram_test_mixin.py
│ └── test_telegram_service.py
├── test_containers.py
├── test_models.py
├── text/
│ ├── __init__.py
│ ├── test_text_handler.py
│ ├── test_text_repository.py
│ └── test_text_service.py
├── watermark/
│ ├── __init__.py
│ ├── test_watermark_handler.py
│ └── test_watermark_service.py
└── webpage/
├── __init__.py
├── test_webpage_handler.py
└── test_webpage_service.py
SYMBOL INDEX (1067 symbols across 149 files)
FILE: pdf_bot/__main__.py
function main (line 17) | def main(
FILE: pdf_bot/account/account_repository.py
class AccountRepository (line 6) | class AccountRepository:
method __init__ (line 7) | def __init__(self, datastore_client: Client) -> None:
method get_user (line 10) | def get_user(self, user_id: int) -> Entity | None:
method upsert_user (line 16) | def upsert_user(self, user_id: int, language_code: str) -> None:
FILE: pdf_bot/account/account_service.py
class AccountService (line 7) | class AccountService:
method __init__ (line 10) | def __init__(
method create_user (line 16) | def create_user(self, telegram_user: User) -> None:
FILE: pdf_bot/analytics/analytics_repository.py
class AnalyticsRepository (line 8) | class AnalyticsRepository:
method __init__ (line 9) | def __init__(self, api_client: Session, settings: Settings | dict[str,...
method send_event (line 22) | def send_event(self, event: dict[str, Any]) -> None:
FILE: pdf_bot/analytics/analytics_service.py
class AnalyticsService (line 15) | class AnalyticsService:
method __init__ (line 16) | def __init__(
method send_event (line 24) | def send_event(
FILE: pdf_bot/analytics/models.py
class TaskType (line 4) | class TaskType(Enum):
class EventAction (line 28) | class EventAction(Enum):
FILE: pdf_bot/cli/cli_service.py
class CLIService (line 11) | class CLIService:
method compress_pdf (line 12) | def compress_pdf(self, input_path: Path, output_path: Path) -> None:
method extract_pdf_images (line 19) | def extract_pdf_images(self, input_path: Path, output_path: Path) -> N...
method _run_command (line 24) | def _run_command(command: str) -> None:
FILE: pdf_bot/cli/exceptions.py
class CLIServiceError (line 1) | class CLIServiceError(Exception):
class CLINonZeroExitStatusError (line 5) | class CLINonZeroExitStatusError(CLIServiceError):
FILE: pdf_bot/command/command_handler.py
class MyCommandHandler (line 8) | class MyCommandHandler(AbstractTelegramHandler):
method __init__ (line 13) | def __init__(self, command_service: CommandService, admin_telegram_id:...
method handlers (line 18) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/command/command_service.py
class CommandService (line 14) | class CommandService:
method __init__ (line 15) | def __init__(self, account_service: AccountService, language_service: ...
method send_start_message (line 19) | async def send_start_message(self, update: Update, context: ContextTyp...
method send_help_message (line 54) | async def send_help_message(self, update: Update, context: ContextType...
method send_message_to_user (line 90) | async def send_message_to_user(
FILE: pdf_bot/compare/compare_handler.py
class CompareHandler (line 10) | class CompareHandler(AbstractTelegramHandler):
method __init__ (line 13) | def __init__(self, compare_service: CompareService, telegram_service: ...
method handlers (line 18) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/compare/compare_service.py
class CompareService (line 18) | class CompareService:
method __init__ (line 23) | def __init__(
method ask_first_pdf (line 33) | async def ask_first_pdf(self, update: Update, context: ContextTypes.DE...
method check_first_pdf (line 49) | async def check_first_pdf(self, update: Update, context: ContextTypes....
method compare_pdfs (line 70) | async def compare_pdfs(self, update: Update, context: ContextTypes.DEF...
method check_text (line 95) | async def check_text(self, update: Update, context: ContextTypes.DEFAU...
FILE: pdf_bot/containers.py
class Core (line 49) | class Core(containers.DeclarativeContainer):
class Clients (line 78) | class Clients(containers.DeclarativeContainer):
class Repositories (line 91) | class Repositories(containers.DeclarativeContainer):
class Services (line 106) | class Services(containers.DeclarativeContainer):
class Processors (line 205) | class Processors(containers.DeclarativeContainer):
class Handlers (line 307) | class Handlers(containers.DeclarativeContainer):
class Application (line 365) | class Application(containers.DeclarativeContainer):
FILE: pdf_bot/datastore/datastore_client.py
class MyDatastoreClient (line 5) | class MyDatastoreClient(Client):
method __init__ (line 6) | def __init__(self, service_account_dict: dict) -> None:
FILE: pdf_bot/error/error_callback_query_handler.py
class ErrorCallbackQueryHandler (line 8) | class ErrorCallbackQueryHandler(AbstractTelegramHandler):
method __init__ (line 9) | def __init__(self, error_service: ErrorService) -> None:
method handlers (line 13) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/error/error_handler.py
class ErrorHandler (line 13) | class ErrorHandler:
method __init__ (line 14) | def __init__(self, language_service: LanguageService) -> None:
method callback (line 17) | async def callback(self, update: object, context: ContextTypes.DEFAULT...
method _handle_error (line 30) | async def _handle_error(self, update: Update, context: ContextTypes.DE...
method _handle_bad_request (line 41) | async def _handle_bad_request(
method _send_message (line 66) | async def _send_message(
FILE: pdf_bot/error/error_service.py
class ErrorService (line 9) | class ErrorService:
method __init__ (line 10) | def __init__(self, language_service: LanguageService) -> None:
method process_unknown_callback_query (line 13) | async def process_unknown_callback_query(
FILE: pdf_bot/errors.py
class FileDataTypeError (line 6) | class FileDataTypeError(Exception):
method __init__ (line 7) | def __init__(self, file_data: FileData, *args: object) -> None:
class CallbackQueryDataTypeError (line 12) | class CallbackQueryDataTypeError(Exception):
method __init__ (line 13) | def __init__(self, data: Any, *args: object) -> None:
class UserIdError (line 18) | class UserIdError(Exception):
FILE: pdf_bot/feedback/feedback_handler.py
class FeedbackHandler (line 10) | class FeedbackHandler(AbstractTelegramHandler):
method __init__ (line 13) | def __init__(
method handlers (line 22) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/feedback/feedback_repository.py
class FeedbackRepository (line 7) | class FeedbackRepository:
method __init__ (line 10) | def __init__(self, slack_client: WebClient) -> None:
method save_feedback (line 13) | def save_feedback(self, chat_id: int, username: str, feedback: str) ->...
FILE: pdf_bot/feedback/feedback_service.py
class FeedbackService (line 14) | class FeedbackService:
method __init__ (line 18) | def __init__(
method ask_feedback (line 28) | async def ask_feedback(self, update: Update, context: ContextTypes.DEF...
method check_text (line 36) | async def check_text(self, update: Update, context: ContextTypes.DEFAU...
method _save_feedback (line 45) | async def _save_feedback(self, update: Update, context: ContextTypes.D...
FILE: pdf_bot/file/file_handler.py
class FileHandler (line 17) | class FileHandler(AbstractTelegramHandler):
method __init__ (line 18) | def __init__(self, file_service: FileService, telegram_service: Telegr...
method handlers (line 23) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/file/file_service.py
class FileService (line 14) | class FileService:
method __init__ (line 15) | def __init__(
method check_pdf (line 27) | async def check_pdf(self, update: Update, context: ContextTypes.DEFAUL...
method check_image (line 35) | async def check_image(self, update: Update, context: ContextTypes.DEFA...
method _get_file_data (line 43) | async def _get_file_data(
FILE: pdf_bot/file_processor/abstract_file_processor.py
class AbstractFileProcessor (line 27) | class AbstractFileProcessor(FileTaskMixin, ABC):
method __init__ (line 30) | def __init__(
method get_task_data_list (line 46) | def get_task_data_list(cls) -> Sequence[TaskData]:
method get_handlers (line 50) | def get_handlers(cls) -> list[BaseHandler]:
method task_type (line 55) | def task_type(self) -> TaskType:
method task_data (line 60) | def task_data(self) -> TaskData:
method handler (line 65) | def handler(self) -> BaseHandler:
method process_file_task (line 70) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
method generic_error_types (line 74) | def generic_error_types(self) -> set[type[Exception]]:
method custom_error_handlers (line 78) | def custom_error_handlers(
method ask_task (line 83) | async def ask_task(self, update: Update, context: ContextTypes.DEFAULT...
method process_file (line 92) | async def process_file(self, update: Update, context: ContextTypes.DEF...
method _process_file_task (line 123) | async def _process_file_task(
method _process_previous_message (line 151) | async def _process_previous_message(
method _get_error_handlers (line 165) | def _get_error_handlers(
method _handle_generic_error (line 174) | async def _handle_generic_error(
FILE: pdf_bot/file_processor/abstract_file_task_processor.py
class AbstractFileTaskProcessor (line 12) | class AbstractFileTaskProcessor(FileTaskMixin, ABC):
method __init__ (line 13) | def __init__(self, language_service: LanguageService) -> None:
method processor_type (line 18) | def processor_type(self) -> type[AbstractFileProcessor]:
method ask_task (line 21) | async def ask_task(self, update: Update, context: CallbackContext) -> ...
FILE: pdf_bot/file_processor/errors.py
class DuplicateClassError (line 1) | class DuplicateClassError(Exception):
method __init__ (line 2) | def __init__(self, cls_name: str, *args: object) -> None:
FILE: pdf_bot/file_processor/file_task_mixin.py
class FileTaskMixin (line 19) | class FileTaskMixin:
method ask_task_helper (line 23) | async def ask_task_helper(
FILE: pdf_bot/image/image_service.py
class ImageService (line 15) | class ImageService:
method __init__ (line 16) | def __init__(
method beautify_and_convert_images_to_pdf (line 27) | async def beautify_and_convert_images_to_pdf(
method convert_images_to_pdf (line 39) | async def convert_images_to_pdf(
method _get_file_ids (line 51) | def _get_file_ids(file_data_list: list[FileData]) -> list[str]:
FILE: pdf_bot/image_handler/batch_image_handler.py
class BatchImageHandler (line 10) | class BatchImageHandler(AbstractTelegramHandler):
method __init__ (line 13) | def __init__(
method handlers (line 20) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/image_handler/batch_image_service.py
class BatchImageService (line 16) | class BatchImageService:
method __init__ (line 24) | def __init__(
method ask_first_image (line 34) | async def ask_first_image(self, update: Update, context: ContextTypes....
method check_image (line 53) | async def check_image(self, update: Update, context: ContextTypes.DEFA...
method check_text (line 71) | async def check_text(self, update: Update, context: ContextTypes.DEFAU...
method _ask_next_image (line 90) | async def _ask_next_image(self, update: Update, context: ContextTypes....
method _remove_last_image (line 115) | async def _remove_last_image(
method _preprocess_images (line 140) | async def _preprocess_images(
method _process_images (line 159) | async def _process_images(
method _append_file_data (line 191) | def _append_file_data(
FILE: pdf_bot/image_processor/abstract_image_processor.py
class AbstractImageProcessor (line 11) | class AbstractImageProcessor(AbstractFileProcessor):
method __init__ (line 14) | def __init__(
method get_task_data_list (line 31) | def get_task_data_list(cls) -> list[TaskData]:
FILE: pdf_bot/image_processor/beautify_image_processor.py
class BeautifyImageData (line 13) | class BeautifyImageData(FileData):
class BeautifyImageProcessor (line 17) | class BeautifyImageProcessor(AbstractImageProcessor):
method task_type (line 19) | def task_type(self) -> TaskType:
method task_data (line 23) | def task_data(self) -> TaskData:
method handler (line 27) | def handler(self) -> CallbackQueryHandler:
method process_file_task (line 31) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/image_processor/image_task_processor.py
class ImageTaskProcessor (line 6) | class ImageTaskProcessor(AbstractFileTaskProcessor):
method processor_type (line 8) | def processor_type(self) -> type[AbstractImageProcessor]:
FILE: pdf_bot/image_processor/image_to_pdf_processor.py
class ImageToPdfData (line 13) | class ImageToPdfData(FileData):
class ImageToPdfProcessor (line 17) | class ImageToPdfProcessor(AbstractImageProcessor):
method task_type (line 19) | def task_type(self) -> TaskType:
method task_data (line 23) | def task_data(self) -> TaskData:
method handler (line 27) | def handler(self) -> CallbackQueryHandler:
method process_file_task (line 31) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/io_internal/io_service.py
class IOService (line 7) | class IOService:
method create_temp_directory (line 10) | def create_temp_directory(prefix: str | None = None) -> Generator[Path...
method create_temp_file (line 22) | def create_temp_file(
method create_temp_files (line 36) | def create_temp_files(num_files: int) -> Generator[list[Path], None, N...
method create_temp_pdf_file (line 45) | def create_temp_pdf_file(self, prefix: str | None = None) -> Generator...
method create_temp_png_file (line 50) | def create_temp_png_file(self, prefix: str) -> Generator[Path, None, N...
method create_temp_txt_file (line 55) | def create_temp_txt_file(self, prefix: str) -> Generator[Path, None, N...
FILE: pdf_bot/language/language_handler.py
class LanguageHandler (line 9) | class LanguageHandler(AbstractTelegramHandler):
method __init__ (line 12) | def __init__(self, language_service: LanguageService) -> None:
method handlers (line 16) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/language/language_repository.py
class LanguageRepository (line 6) | class LanguageRepository:
method __init__ (line 10) | def __init__(self, datastore_client: Client) -> None:
method get_language (line 13) | def get_language(self, user_id: int) -> str:
method upsert_language (line 28) | def upsert_language(self, user_id: int, language_code: str) -> None:
FILE: pdf_bot/language/language_service.py
class LanguageService (line 15) | class LanguageService:
method __init__ (line 65) | def __init__(self, language_repository: LanguageRepository) -> None:
method get_language_code_from_short_code (line 68) | def get_language_code_from_short_code(self, short_code: str) -> str | ...
method send_language_options (line 74) | async def send_language_options(
method get_user_language (line 89) | def get_user_language(self, update: Update, context: ContextTypes.DEFA...
method update_user_language (line 103) | async def update_user_language(
method set_app_language (line 122) | def set_app_language(
method _answer_query_and_drop_data (line 130) | async def _answer_query_and_drop_data(
method _get_languages_markup (line 137) | def _get_languages_markup(
method _get_user_id (line 154) | def _get_user_id(self, update: Update) -> int:
FILE: pdf_bot/language/models.py
class SetLanguageData (line 4) | class SetLanguageData:
class LanguageData (line 8) | class LanguageData(BaseModel):
method short_code (line 13) | def short_code(self) -> str:
FILE: pdf_bot/log/log_handler.py
class InterceptLoggingHandler (line 8) | class InterceptLoggingHandler(logging.Handler):
method emit (line 9) | def emit(self, record: logging.LogRecord) -> None:
class MyLogHandler (line 25) | class MyLogHandler:
method __init__ (line 29) | def __init__(self, intercept_logging_handler: InterceptLoggingHandler)...
method setup (line 32) | def setup(self) -> None:
FILE: pdf_bot/merge/merge_handler.py
class MergeHandler (line 10) | class MergeHandler(AbstractTelegramHandler):
method __init__ (line 13) | def __init__(self, merge_service: MergeService, telegram_service: Tele...
method handlers (line 18) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/merge/merge_service.py
class MergeService (line 16) | class MergeService:
method __init__ (line 21) | def __init__(
method ask_first_pdf (line 31) | async def ask_first_pdf(self, update: Update, context: ContextTypes.DE...
method check_pdf (line 45) | async def check_pdf(self, update: Update, context: ContextTypes.DEFAUL...
method check_text (line 63) | async def check_text(self, update: Update, context: ContextTypes.DEFAU...
method _ask_next_pdf (line 82) | async def _ask_next_pdf(self, update: Update, context: ContextTypes.DE...
method _remove_last_pdf (line 108) | async def _remove_last_pdf(
method _preprocess_pdfs (line 135) | async def _preprocess_pdfs(
method _merge_pdfs (line 154) | async def _merge_pdfs(
method _append_file_data (line 172) | def _append_file_data(self, context: ContextTypes.DEFAULT_TYPE, docume...
FILE: pdf_bot/models.py
class BackData (line 7) | class BackData:
class SupportData (line 11) | class SupportData:
class FileData (line 16) | class FileData:
method from_telegram_object (line 21) | def from_telegram_object(cls, obj: Document | PhotoSize) -> "FileData":
class TaskData (line 28) | class TaskData:
method get_file_data (line 32) | def get_file_data(self, obj: Document | PhotoSize) -> FileData:
class MessageData (line 37) | class MessageData:
method from_telegram_message (line 42) | def from_telegram_message(cls, message: Message) -> "MessageData":
class FileTaskResult (line 47) | class FileTaskResult:
FILE: pdf_bot/payment/models.py
class PaymentData (line 4) | class PaymentData(BaseModel):
FILE: pdf_bot/payment/payment_handler.py
class PaymentHandler (line 17) | class PaymentHandler(AbstractTelegramHandler):
method __init__ (line 21) | def __init__(self, payment_service: PaymentService) -> None:
method handlers (line 25) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/payment/payment_service.py
class PaymentService (line 23) | class PaymentService:
method __init__ (line 36) | def __init__(
method send_support_options (line 46) | async def send_support_options(
method send_invoice (line 61) | async def send_invoice(self, update: Update, context: ContextTypes.DEF...
method precheckout_check (line 84) | async def precheckout_check(self, update: Update, context: ContextType...
method successful_payment (line 93) | async def successful_payment(self, update: Update, context: ContextTyp...
method _get_support_options_markup (line 98) | def _get_support_options_markup(
FILE: pdf_bot/pdf/exceptions.py
class PdfServiceError (line 4) | class PdfServiceError(Exception):
class PdfReadError (line 8) | class PdfReadError(PdfServiceError):
class PdfDecryptError (line 12) | class PdfDecryptError(PdfServiceError):
class PdfIncorrectPasswordError (line 16) | class PdfIncorrectPasswordError(PdfServiceError):
class PdfNoTextError (line 20) | class PdfNoTextError(PdfServiceError):
class PdfNoImagesError (line 24) | class PdfNoImagesError(PdfServiceError):
class PdfEncryptedError (line 28) | class PdfEncryptedError(PdfServiceError):
method __init__ (line 31) | def __init__(self, *args: object, **kwargs: object) -> None:
FILE: pdf_bot/pdf/models.py
class CompressResult (line 8) | class CompressResult:
method reduced_percentage (line 14) | def reduced_percentage(self) -> float:
method readable_old_size (line 18) | def readable_old_size(self) -> str:
method readable_new_size (line 22) | def readable_new_size(self) -> str:
method _readable_size (line 26) | def _readable_size(size: int) -> str:
class FontData (line 31) | class FontData:
class ScaleData (line 37) | class ScaleData:
method __str__ (line 41) | def __str__(self) -> str: # pragma: no cover
method from_string (line 45) | def from_string(value: str) -> "ScaleData":
class ScaleByData (line 50) | class ScaleByData(ScaleData):
class ScaleToData (line 54) | class ScaleToData(ScaleData):
FILE: pdf_bot/pdf/pdf_service.py
class PdfService (line 40) | class PdfService:
method __init__ (line 41) | def __init__(
method add_watermark_to_pdf (line 52) | async def add_watermark_to_pdf(
method grayscale_pdf (line 69) | async def grayscale_pdf(self, file_id: str) -> AsyncGenerator[Path, No...
method compare_pdfs (line 88) | async def compare_pdfs(self, file_id_a: str, file_id_b: str) -> AsyncG...
method compress_pdf (line 98) | async def compress_pdf(self, file_id: str) -> AsyncGenerator[CompressR...
method convert_pdf_to_images (line 107) | async def convert_pdf_to_images(self, file_id: str) -> AsyncGenerator[...
method create_pdf_from_text (line 114) | async def create_pdf_from_text(
method crop_pdf_by_percentage (line 142) | async def crop_pdf_by_percentage(
method crop_pdf_by_margin_size (line 151) | async def crop_pdf_by_margin_size(
method decrypt_pdf (line 160) | async def decrypt_pdf(self, file_id: str, password: str) -> AsyncGener...
method encrypt_pdf (line 181) | async def encrypt_pdf(self, file_id: str, password: str) -> AsyncGener...
method extract_pdf_images (line 193) | async def extract_pdf_images(self, file_id: str) -> AsyncGenerator[Pat...
method extract_pdf_text (line 206) | async def extract_pdf_text(self, file_id: str) -> AsyncGenerator[Path,...
method merge_pdfs (line 223) | async def merge_pdfs(self, file_data_list: list[FileData]) -> AsyncGen...
method ocr_pdf (line 241) | async def ocr_pdf(self, file_id: str) -> AsyncGenerator[Path, None]:
method preview_pdf (line 253) | async def preview_pdf(self, file_id: str) -> AsyncGenerator[Path, None]:
method rename_pdf (line 269) | async def rename_pdf(self, file_id: str, file_name: str) -> AsyncGener...
method rotate_pdf (line 277) | async def rotate_pdf(self, file_id: str, degree: int) -> AsyncGenerato...
method scale_pdf_by_factor (line 288) | async def scale_pdf_by_factor(
method scale_pdf_to_dimension (line 302) | async def scale_pdf_to_dimension(
method split_range_valid (line 316) | def split_range_valid(split_range: str) -> bool:
method split_pdf (line 320) | async def split_pdf(self, file_id: str, split_range: str) -> AsyncGene...
method _get_file_ids (line 329) | def _get_file_ids(file_data_list: list[FileData]) -> list[str]:
method _open_pdf (line 332) | async def _open_pdf(self, file_id: str, allow_encrypted: bool = False)...
method _write_pdf (line 344) | def _write_pdf(self, writer: PdfWriter, file_prefix: str) -> Generator...
FILE: pdf_bot/pdf_processor/abstract_pdf_processor.py
class AbstractPdfProcessor (line 11) | class AbstractPdfProcessor(AbstractFileProcessor):
method __init__ (line 14) | def __init__(
method get_task_data_list (line 31) | def get_task_data_list(cls) -> list[TaskData]:
method generic_error_types (line 35) | def generic_error_types(self) -> set[type[Exception]]:
FILE: pdf_bot/pdf_processor/abstract_pdf_select_and_text_processor.py
class SelectOption (line 24) | class SelectOption(Enum):
method ask_value_text (line 27) | def ask_value_text(self) -> str:
class SelectOptionData (line 32) | class SelectOptionData(FileData):
class OptionAndInputData (line 37) | class OptionAndInputData(SelectOptionData):
class AbstractPdfSelectAndTextProcessor (line 41) | class AbstractPdfSelectAndTextProcessor(AbstractPdfProcessor):
method entry_point_data_type (line 47) | def entry_point_data_type(self) -> type[FileData]:
method ask_select_option_text (line 52) | def ask_select_option_text(self) -> str:
method select_option_type (line 57) | def select_option_type(self) -> type[SelectOption]:
method get_cleaned_text_input (line 61) | def get_cleaned_text_input(self, text: str) -> str | Any | None:
method invalid_text_input_error (line 66) | def invalid_text_input_error(self) -> str:
method option_and_input_data_type (line 70) | def option_and_input_data_type(self) -> type[OptionAndInputData]:
method handler (line 74) | def handler(self) -> ConversationHandler:
method _ask_select_option (line 98) | async def _ask_select_option(self, update: Update, context: ContextTyp...
method _get_ask_select_option_markup (line 114) | def _get_ask_select_option_markup(
method _ask_text_input (line 138) | async def _ask_text_input(self, update: Update, context: ContextTypes....
method _get_ask_text_input_markup (line 157) | def _get_ask_text_input_markup(
method _process_text_input (line 174) | async def _process_text_input(
FILE: pdf_bot/pdf_processor/abstract_pdf_text_input_processor.py
class TextInputData (line 26) | class TextInputData(FileData):
class AbstractPdfTextInputProcessor (line 30) | class AbstractPdfTextInputProcessor(AbstractPdfProcessor):
method entry_point_data_type (line 35) | def entry_point_data_type(self) -> type[FileData]:
method get_ask_text_input_text (line 39) | def get_ask_text_input_text(self, _: Callable[[str], str]) -> str:
method get_cleaned_text_input (line 43) | def get_cleaned_text_input(self, text: str) -> str | None:
method invalid_text_input_error (line 48) | def invalid_text_input_error(self) -> str:
method handler (line 52) | def handler(self) -> ConversationHandler:
method _ask_text_input (line 70) | async def _ask_text_input(self, update: Update, context: ContextTypes....
method _process_text_input (line 85) | async def _process_text_input(
FILE: pdf_bot/pdf_processor/compress_pdf_processor.py
class CompressPdfData (line 13) | class CompressPdfData(FileData):
class CompressPdfProcessor (line 17) | class CompressPdfProcessor(AbstractPdfProcessor):
method task_type (line 19) | def task_type(self) -> TaskType:
method task_data (line 23) | def task_data(self) -> TaskData:
method handler (line 27) | def handler(self) -> CallbackQueryHandler:
method process_file_task (line 31) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/crop_pdf_processor.py
class CropType (line 16) | class CropType(SelectOption):
method ask_value_text (line 21) | def ask_value_text(self) -> str: # pragma: no cover
class CropPdfData (line 37) | class CropPdfData(FileData):
class CropOptionAndInputData (line 41) | class CropOptionAndInputData(OptionAndInputData):
class CropPdfProcessor (line 45) | class CropPdfProcessor(AbstractPdfSelectAndTextProcessor):
method entry_point_data_type (line 47) | def entry_point_data_type(self) -> type[CropPdfData]:
method task_type (line 51) | def task_type(self) -> TaskType:
method task_data (line 55) | def task_data(self) -> TaskData:
method ask_select_option_text (line 59) | def ask_select_option_text(self) -> str: # pragma: no cover
method select_option_type (line 63) | def select_option_type(self) -> type[CropType]:
method invalid_text_input_error (line 67) | def invalid_text_input_error(self) -> str: # pragma: no cover
method option_and_input_data_type (line 71) | def option_and_input_data_type(self) -> type[CropOptionAndInputData]:
method get_cleaned_text_input (line 74) | def get_cleaned_text_input(self, text: str) -> float | None:
method process_file_task (line 81) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/decrypt_pdf_processor.py
class DecryptPdfData (line 18) | class DecryptPdfData(FileData):
class DecryptPdfProcessor (line 22) | class DecryptPdfProcessor(AbstractPdfTextInputProcessor):
method task_type (line 24) | def task_type(self) -> TaskType:
method entry_point_data_type (line 28) | def entry_point_data_type(self) -> type[DecryptPdfData]:
method task_data (line 32) | def task_data(self) -> TaskData:
method invalid_text_input_error (line 36) | def invalid_text_input_error(self) -> str: # pragma: no cover
method get_ask_text_input_text (line 39) | def get_ask_text_input_text(self, _: Callable[[str], str]) -> str: # ...
method get_cleaned_text_input (line 42) | def get_cleaned_text_input(self, text: str) -> str:
method custom_error_handlers (line 46) | def custom_error_handlers(self) -> dict[type[Exception], ErrorHandlerT...
method process_file_task (line 50) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
method _handle_incorrect_password (line 57) | async def _handle_incorrect_password(
FILE: pdf_bot/pdf_processor/encrypt_pdf_processor.py
class EncryptPdfData (line 12) | class EncryptPdfData(FileData):
class EncryptPdfProcessor (line 16) | class EncryptPdfProcessor(AbstractPdfTextInputProcessor):
method task_type (line 18) | def task_type(self) -> TaskType:
method entry_point_data_type (line 22) | def entry_point_data_type(self) -> type[EncryptPdfData]:
method task_data (line 26) | def task_data(self) -> TaskData:
method invalid_text_input_error (line 30) | def invalid_text_input_error(self) -> str: # pragma: no cover
method get_ask_text_input_text (line 33) | def get_ask_text_input_text(self, _: Callable[[str], str]) -> str: # ...
method get_cleaned_text_input (line 36) | def get_cleaned_text_input(self, text: str) -> str:
method process_file_task (line 40) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/extract_pdf_image_processor.py
class ExtractPdfImageData (line 13) | class ExtractPdfImageData(FileData):
class ExtractPdfImageProcessor (line 17) | class ExtractPdfImageProcessor(AbstractPdfProcessor):
method task_type (line 19) | def task_type(self) -> TaskType:
method task_data (line 23) | def task_data(self) -> TaskData:
method handler (line 27) | def handler(self) -> CallbackQueryHandler:
method process_file_task (line 31) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/extract_pdf_text_processor.py
class ExtractPdfTextData (line 13) | class ExtractPdfTextData(FileData):
class ExtractPdfTextProcessor (line 17) | class ExtractPdfTextProcessor(AbstractPdfProcessor):
method task_type (line 19) | def task_type(self) -> TaskType:
method task_data (line 23) | def task_data(self) -> TaskData:
method handler (line 27) | def handler(self) -> CallbackQueryHandler:
method process_file_task (line 31) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/grayscale_pdf_processor.py
class GrayscalePdfData (line 13) | class GrayscalePdfData(FileData):
class GrayscalePdfProcessor (line 17) | class GrayscalePdfProcessor(AbstractPdfProcessor):
method task_type (line 19) | def task_type(self) -> TaskType:
method task_data (line 23) | def task_data(self) -> TaskData:
method handler (line 27) | def handler(self) -> CallbackQueryHandler:
method process_file_task (line 31) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/ocr_pdf_processor.py
class OcrPdfData (line 12) | class OcrPdfData(FileData):
class OcrPdfProcessor (line 16) | class OcrPdfProcessor(AbstractPdfProcessor):
method task_type (line 18) | def task_type(self) -> TaskType:
method task_data (line 22) | def task_data(self) -> TaskData:
method handler (line 26) | def handler(self) -> CallbackQueryHandler:
method process_file_task (line 30) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/pdf_task_processor.py
class PdfTaskProcessor (line 6) | class PdfTaskProcessor(AbstractFileTaskProcessor):
method processor_type (line 8) | def processor_type(self) -> type[AbstractPdfProcessor]:
FILE: pdf_bot/pdf_processor/pdf_to_image_processor.py
class PdfToImageData (line 13) | class PdfToImageData(FileData):
class PdfToImageProcessor (line 17) | class PdfToImageProcessor(AbstractPdfProcessor):
method task_type (line 19) | def task_type(self) -> TaskType:
method task_data (line 23) | def task_data(self) -> TaskData:
method handler (line 27) | def handler(self) -> CallbackQueryHandler:
method process_file_task (line 31) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/preview_pdf_processor.py
class PreviewPdfData (line 13) | class PreviewPdfData(FileData):
class PreviewPdfProcessor (line 17) | class PreviewPdfProcessor(AbstractPdfProcessor):
method task_type (line 19) | def task_type(self) -> TaskType:
method task_data (line 23) | def task_data(self) -> TaskData:
method handler (line 27) | def handler(self) -> CallbackQueryHandler:
method process_file_task (line 31) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/rename_pdf_processor.py
class RenamePdfData (line 13) | class RenamePdfData(FileData):
class RenamePdfProcessor (line 17) | class RenamePdfProcessor(AbstractPdfTextInputProcessor):
method task_type (line 21) | def task_type(self) -> TaskType:
method entry_point_data_type (line 25) | def entry_point_data_type(self) -> type[RenamePdfData]:
method task_data (line 29) | def task_data(self) -> TaskData:
method invalid_text_input_error (line 33) | def invalid_text_input_error(self) -> str: # pragma: no cover
method get_ask_text_input_text (line 39) | def get_ask_text_input_text(self, _: Callable[[str], str]) -> str: # ...
method get_cleaned_text_input (line 42) | def get_cleaned_text_input(self, text: str) -> str | None:
method process_file_task (line 49) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/rotate_pdf_processor.py
class RotatePdfData (line 19) | class RotatePdfData(FileData):
class RotateDegreeData (line 24) | class RotateDegreeData(RotatePdfData):
class RotatePdfProcessor (line 28) | class RotatePdfProcessor(AbstractPdfProcessor):
method task_type (line 33) | def task_type(self) -> TaskType:
method task_data (line 37) | def task_data(self) -> TaskData:
method handler (line 41) | def handler(self) -> ConversationHandler:
method process_file_task (line 58) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
method ask_degree (line 65) | async def ask_degree(self, update: Update, context: ContextTypes.DEFAU...
method _get_ask_degree_reply_markup (line 82) | def _get_ask_degree_reply_markup(
FILE: pdf_bot/pdf_processor/scale_pdf_processor.py
class ScaleType (line 17) | class ScaleType(SelectOption):
method ask_value_text (line 22) | def ask_value_text(self) -> str: # pragma: no cover
class ScalePdfData (line 37) | class ScalePdfData(FileData):
class ScaleOptionAndInputData (line 41) | class ScaleOptionAndInputData(OptionAndInputData):
class ScalePdfProcessor (line 45) | class ScalePdfProcessor(AbstractPdfSelectAndTextProcessor):
method entry_point_data_type (line 47) | def entry_point_data_type(self) -> type[ScalePdfData]:
method task_type (line 51) | def task_type(self) -> TaskType:
method task_data (line 55) | def task_data(self) -> TaskData:
method ask_select_option_text (line 59) | def ask_select_option_text(self) -> str: # pragma: no cover
method select_option_type (line 63) | def select_option_type(self) -> type[ScaleType]:
method invalid_text_input_error (line 67) | def invalid_text_input_error(self) -> str: # pragma: no cover
method option_and_input_data_type (line 71) | def option_and_input_data_type(self) -> type[ScaleOptionAndInputData]:
method get_cleaned_text_input (line 74) | def get_cleaned_text_input(self, text: str) -> ScaleData | None:
method process_file_task (line 81) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/pdf_processor/split_pdf_processor.py
class SplitPdfData (line 12) | class SplitPdfData(FileData):
class SplitPdfProcessor (line 16) | class SplitPdfProcessor(AbstractPdfTextInputProcessor):
method task_type (line 20) | def task_type(self) -> TaskType:
method entry_point_data_type (line 24) | def entry_point_data_type(self) -> type[SplitPdfData]:
method task_data (line 28) | def task_data(self) -> TaskData:
method invalid_text_input_error (line 32) | def invalid_text_input_error(self) -> str: # pragma: no cover
method get_ask_text_input_text (line 35) | def get_ask_text_input_text(self, _: Callable[[str], str]) -> str: # ...
method get_cleaned_text_input (line 78) | def get_cleaned_text_input(self, text: str) -> str | None:
method process_file_task (line 84) | async def process_file_task(self, file_data: FileData) -> AsyncGenerat...
FILE: pdf_bot/settings.py
class Settings (line 7) | class Settings(BaseSettings):
FILE: pdf_bot/telegram_handler/abstract_telegram_handler.py
class AbstractTelegramHandler (line 6) | class AbstractTelegramHandler(ABC):
method handlers (line 9) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/telegram_internal/exceptions.py
class TelegramServiceError (line 1) | class TelegramServiceError(Exception):
class TelegramFileMimeTypeError (line 5) | class TelegramFileMimeTypeError(TelegramServiceError):
class TelegramFileTooLargeError (line 9) | class TelegramFileTooLargeError(TelegramServiceError):
class TelegramGetUserDataError (line 13) | class TelegramGetUserDataError(TelegramServiceError):
class TelegramUpdateUserDataError (line 17) | class TelegramUpdateUserDataError(TelegramServiceError):
class TelegramImageNotFoundError (line 21) | class TelegramImageNotFoundError(TelegramServiceError):
FILE: pdf_bot/telegram_internal/telegram_service.py
class _ReplyData (line 38) | class _ReplyData(BaseModel):
class TelegramService (line 44) | class TelegramService:
method __init__ (line 51) | def __init__(
method check_file_size (line 64) | def check_file_size(file: Document | PhotoSize) -> None:
method check_file_upload_size (line 77) | def check_file_upload_size(path: Path) -> None:
method get_user_data (line 88) | def get_user_data(context: ContextTypes.DEFAULT_TYPE, key: str) -> Any:
method user_data_contains (line 112) | def user_data_contains(context: ContextTypes.DEFAULT_TYPE, key: str) -...
method update_user_data (line 116) | def update_user_data(context: ContextTypes.DEFAULT_TYPE, key: str, val...
method get_file_data (line 121) | def get_file_data(self, context: ContextTypes.DEFAULT_TYPE) -> FileData:
method cache_file_data (line 125) | def cache_file_data(self, context: ContextTypes.DEFAULT_TYPE, file_dat...
method get_message_data (line 128) | def get_message_data(self, context: ContextTypes.DEFAULT_TYPE) -> Mess...
method cache_message_data (line 132) | def cache_message_data(
method answer_query_and_drop_data (line 141) | async def answer_query_and_drop_data(
method check_image (line 148) | def check_image(self, message: Message) -> Document | PhotoSize:
method check_pdf_document (line 167) | def check_pdf_document(self, message: Message) -> Document:
method download_pdf_file (line 177) | async def download_pdf_file(self, file_id: str) -> AsyncGenerator[Path...
method download_files (line 184) | async def download_files(self, file_ids: list[str]) -> AsyncGenerator[...
method cancel_conversation (line 191) | async def cancel_conversation(self, update: Update, context: ContextTy...
method get_back_button (line 204) | def get_back_button(
method get_back_inline_markup (line 210) | def get_back_inline_markup(
method get_support_markup (line 216) | def get_support_markup(
method reply_with_back_markup (line 229) | def reply_with_back_markup(
method reply_with_cancel_markup (line 239) | def reply_with_cancel_markup(
method send_file (line 249) | async def send_file(
method send_file_names (line 285) | async def send_file_names(
method send_message (line 303) | async def send_message(
method _get_chat_id (line 311) | def _get_chat_id(update: Update) -> int:
method _reply_with_markup (line 322) | def _reply_with_markup(
FILE: pdf_bot/text/text_handler.py
class TextHandler (line 10) | class TextHandler(AbstractTelegramHandler):
method __init__ (line 13) | def __init__(self, text_service: TextService, telegram_service: Telegr...
method handlers (line 18) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/text/text_repository.py
class TextRepository (line 6) | class TextRepository:
method __init__ (line 7) | def __init__(self, api_client: Session, google_fonts_token: str) -> None:
method get_font (line 11) | def get_font(self, font: str) -> FontData | None:
FILE: pdf_bot/text/text_service.py
class TextService (line 16) | class TextService:
method __init__ (line 23) | def __init__(
method ask_pdf_text (line 35) | async def ask_pdf_text(self, update: Update, context: ContextTypes.DEF...
method ask_pdf_font (line 44) | async def ask_pdf_font(self, update: Update, context: ContextTypes.DEF...
method check_text (line 73) | async def check_text(self, update: Update, context: ContextTypes.DEFAU...
method _text_to_pdf (line 93) | async def _text_to_pdf(
FILE: pdf_bot/watermark/watermark_handler.py
class WatermarkHandler (line 10) | class WatermarkHandler(AbstractTelegramHandler):
method __init__ (line 13) | def __init__(
method handlers (line 20) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/watermark/watermark_service.py
class WatermarkService (line 18) | class WatermarkService:
method __init__ (line 23) | def __init__(
method ask_source_pdf (line 33) | async def ask_source_pdf(self, update: Update, context: ContextTypes.D...
method check_source_pdf (line 42) | async def check_source_pdf(self, update: Update, context: ContextTypes...
method add_watermark_to_pdf (line 60) | async def add_watermark_to_pdf(self, update: Update, context: ContextT...
method check_text (line 88) | async def check_text(self, update: Update, context: ContextTypes.DEFAU...
FILE: pdf_bot/webpage/webpage_handler.py
class WebpageHandler (line 9) | class WebpageHandler(AbstractTelegramHandler):
method __init__ (line 10) | def __init__(self, webpage_service: WebpageService) -> None:
method handlers (line 14) | def handlers(self) -> list[BaseHandler]:
FILE: pdf_bot/webpage/webpage_service.py
class WebpageService (line 22) | class WebpageService:
method __init__ (line 23) | def __init__(
method url_to_pdf (line 33) | async def url_to_pdf(self, update: Update, context: ContextTypes.DEFAU...
method _cache_url (line 50) | def _cache_url(self, context: ContextTypes.DEFAULT_TYPE, url_hash: str...
method _clear_url_cache (line 54) | def _clear_url_cache(self, context: ContextTypes.DEFAULT_TYPE, url_has...
method _url_to_pdf (line 58) | async def _url_to_pdf(
FILE: tests/account/test_account_repository.py
class TestAccountRepository (line 9) | class TestAccountRepository:
method setup_method (line 13) | def setup_method(self) -> None:
method test_get_user (line 19) | def test_get_user(self) -> None:
method test_get_user_null (line 24) | def test_get_user_null(self) -> None:
method test_upsert_user (line 29) | def test_upsert_user(self) -> None:
method test_upsert_user_language_exists (line 39) | def test_upsert_user_language_exists(self) -> None:
method test_upsert_user_new_user (line 49) | def test_upsert_user_new_user(self) -> None:
FILE: tests/account/test_account_service.py
class TestAccountService (line 9) | class TestAccountService(LanguageServiceTestMixin):
method setup_method (line 13) | def setup_method(self) -> None:
method test_create_user (line 23) | def test_create_user(self) -> None:
method test_create_user_with_language_code (line 28) | def test_create_user_with_language_code(self) -> None:
method test_create_user_with_invalid_language_code (line 37) | def test_create_user_with_invalid_language_code(self) -> None:
FILE: tests/analytics/test_analytics_repository.py
class TestAnalyticsRepository (line 10) | class TestAnalyticsRepository:
method setup_method (line 13) | def setup_method(self) -> None:
method test_send_event (line 19) | def test_send_event(self) -> None:
FILE: tests/analytics/test_analytics_service.py
class TestAnalyticsService (line 11) | class TestAnalyticsService(LanguageServiceTestMixin, TelegramTestMixin):
method setup_method (line 16) | def setup_method(self) -> None:
method test_send_event (line 27) | def test_send_event(self) -> None:
method test_send_event_error (line 30) | def test_send_event_error(self) -> None:
method _test_and_assert_send_event (line 39) | def _test_and_assert_send_event(self) -> None:
FILE: tests/cli/test_cli_service.py
class TestCLIService (line 11) | class TestCLIService(PathTestMixin):
method setup_method (line 15) | def setup_method(self) -> None:
method teardown_method (line 30) | def teardown_method(self) -> None:
method test_compress_pdf (line 34) | async def test_compress_pdf(self) -> None:
method test_compress_pdf_error (line 40) | async def test_compress_pdf_error(self) -> None:
method test_extract_pdf_images (line 49) | async def test_extract_pdf_images(self) -> None:
method test_extract_pdf_images_error (line 55) | async def test_extract_pdf_images_error(self) -> None:
method _assert_compress_command (line 63) | def _assert_compress_command(self) -> None:
method _assert_crop_pdf_percentage_command (line 71) | def _assert_crop_pdf_percentage_command(self) -> None:
method _assert_crop_pdf_margin_size_command (line 77) | def _assert_crop_pdf_margin_size_command(self) -> None:
method _assert_get_pdf_images_command (line 83) | def _assert_get_pdf_images_command(self) -> None:
FILE: tests/command/test_command_handler.py
class TestCommandHandler (line 10) | class TestCommandHandler(TelegramTestMixin):
method setup_method (line 16) | def setup_method(self) -> None:
method test_handlers (line 22) | async def test_handlers(self) -> None:
FILE: tests/command/test_command_service.py
class TestCommandService (line 13) | class TestCommandService(LanguageServiceTestMixin, TelegramTestMixin):
method setup_method (line 14) | def setup_method(self) -> None:
method test_send_start_message (line 22) | async def test_send_start_message(self) -> None:
method test_send_help_message (line 31) | async def test_send_help_message(self) -> None:
method test_send_message_to_user (line 36) | async def test_send_message_to_user(self) -> None:
method test_send_message_to_user_unauthorized (line 42) | async def test_send_message_to_user_unauthorized(self) -> None:
method test_send_message_to_user_invalid_args (line 51) | async def test_send_message_to_user_invalid_args(self) -> None:
method _assert_send_message_to_user (line 62) | def _assert_send_message_to_user(self, message: str) -> None:
FILE: tests/compare/test_compare_handler.py
class TestCompareHandlers (line 11) | class TestCompareHandlers(TelegramServiceTestMixin):
method setup_method (line 15) | def setup_method(self) -> None:
method test_handlers (line 23) | async def test_handlers(self) -> None:
FILE: tests/compare/test_compare_service.py
class TestCompareService (line 16) | class TestCompareService(LanguageServiceTestMixin, TelegramServiceTestMi...
method setup_method (line 21) | def setup_method(self) -> None:
method test_ask_first_pdf (line 31) | async def test_ask_first_pdf(self) -> None:
method test_check_first_pdf (line 37) | async def test_check_first_pdf(self) -> None:
method test_check_first_pdf_invalid_pdf (line 45) | async def test_check_first_pdf_invalid_pdf(self) -> None:
method test_compare_pdfs (line 54) | async def test_compare_pdfs(self) -> None:
method test_compare_pdfs_no_differences (line 72) | async def test_compare_pdfs_no_differences(self) -> None:
method test_compare_pdfs_invalid_user_data (line 85) | async def test_compare_pdfs_invalid_user_data(self) -> None:
method test_compare_pdfs_invalid_pdf (line 95) | async def test_compare_pdfs_invalid_pdf(self) -> None:
method test_check_text_back (line 105) | async def test_check_text_back(self) -> None:
method test_check_text_cancel (line 111) | async def test_check_text_cancel(self) -> None:
method test_check_text_unknown (line 120) | async def test_check_text_unknown(self) -> None:
FILE: tests/conftest.py
function _after_test (line 9) | def _after_test() -> None:
FILE: tests/datastore/test_datastore_client.py
class TestMyDatastoreClient (line 8) | class TestMyDatastoreClient:
method test_init (line 9) | def test_init(self) -> None:
FILE: tests/error/test_error_callback_query_handler.py
class TestErrorCallbackQueryHandler (line 10) | class TestErrorCallbackQueryHandler(TelegramServiceTestMixin):
method setup_method (line 11) | def setup_method(self) -> None:
method test_handlers (line 18) | async def test_handlers(self) -> None:
FILE: tests/error/test_error_handler.py
class TestErrorHandler (line 11) | class TestErrorHandler(LanguageServiceTestMixin, TelegramTestMixin):
method setup_method (line 12) | def setup_method(self) -> None:
method teardown_method (line 20) | def teardown_method(self) -> None:
method test_callback_known_error (line 24) | async def test_callback_known_error(self) -> None:
method test_callback_unknown_error (line 29) | async def test_callback_unknown_error(self) -> None:
method test_callback_unknown_error_and_send_message_error (line 39) | async def test_callback_unknown_error_and_send_message_error(self) -> ...
method test_callback_unknown_error_and_without_chat_id (line 50) | async def test_callback_unknown_error_and_without_chat_id(self) -> None:
method test_callback_unknown_error_and_effective_chat (line 62) | async def test_callback_unknown_error_and_effective_chat(self) -> None:
method test_callback_unknown_error_and_not_update (line 74) | async def test_callback_unknown_error_and_not_update(self) -> None:
method test_callback_without_error (line 84) | async def test_callback_without_error(self) -> None:
method test_callback_bad_request_swallow_error (line 102) | async def test_callback_bad_request_swallow_error(self, message: str) ...
method test_callback_bad_request_swallow_and_reply_error (line 119) | async def test_callback_bad_request_swallow_and_reply_error(self, mess...
method test_callback_unknown_bad_request (line 128) | async def test_callback_unknown_bad_request(self) -> None:
FILE: tests/error/test_error_service.py
class TestErrorService (line 8) | class TestErrorService(LanguageServiceTestMixin, TelegramTestMixin):
method setup_method (line 9) | def setup_method(self) -> None:
method test_process_unknown_callback_query (line 16) | async def test_process_unknown_callback_query(self) -> None:
FILE: tests/feedback/test_feedback_handler.py
class TestFeedbackHandler (line 11) | class TestFeedbackHandler(TelegramServiceTestMixin, TelegramTestMixin):
method setup_method (line 15) | def setup_method(self) -> None:
method test_handlers (line 26) | async def test_handlers(self) -> None:
FILE: tests/feedback/test_feedback_repository.py
class TestFeedbackRepository (line 10) | class TestFeedbackRepository(TelegramTestMixin):
method setup_method (line 13) | def setup_method(self) -> None:
method test_save_feedback (line 17) | def test_save_feedback(self) -> None:
method test_save_feedback_error (line 20) | def test_save_feedback_error(self) -> None:
method _save_feedback_and_assert_slack_client (line 33) | def _save_feedback_and_assert_slack_client(self) -> None:
FILE: tests/feedback/test_feedback_service.py
class TestFeedbackService (line 11) | class TestFeedbackService(LanguageServiceTestMixin, TelegramServiceTestM...
method setup_method (line 16) | def setup_method(self) -> None:
method teardown_method (line 32) | def teardown_method(self) -> None:
method test_ask_feedback (line 37) | async def test_ask_feedback(self) -> None:
method test_check_text_save_feedback (line 44) | async def test_check_text_save_feedback(self) -> None:
method test_check_text_save_feedback_invalid_language (line 51) | async def test_check_text_save_feedback_invalid_language(self) -> None:
method test_check_text_cancel (line 61) | async def test_check_text_cancel(self) -> None:
method _assert_save_feedback_and_reply_text (line 72) | def _assert_save_feedback_and_reply_text(self) -> None:
FILE: tests/file/test_file_handler.py
class TestFileHandler (line 18) | class TestFileHandler(TelegramServiceTestMixin, TelegramTestMixin):
method setup_method (line 21) | def setup_method(self) -> None:
method test_handlers (line 29) | async def test_handlers(self) -> None:
FILE: tests/file/test_file_service.py
class TestFileService (line 13) | class TestFileService(LanguageServiceTestMixin, TelegramServiceTestMixin...
method setup_method (line 16) | def setup_method(self) -> None:
method test_check_pdf (line 31) | async def test_check_pdf(self) -> None:
method test_check_pdf_too_big (line 43) | async def test_check_pdf_too_big(self) -> None:
method test_check_image (line 52) | async def test_check_image(self) -> None:
method test_check_image_too_big (line 64) | async def test_check_image_too_big(self) -> None:
FILE: tests/file_processor/test_abstract_file_processor.py
class GenericError (line 23) | class GenericError(Exception):
class CustomError (line 27) | class CustomError(Exception):
class UnknownError (line 31) | class UnknownError(Exception):
class MockProcessor (line 35) | class MockProcessor(PathTestMixin, AbstractFileProcessor):
method __init__ (line 40) | def __init__(
method get_task_data_list (line 51) | def get_task_data_list(cls) -> Sequence[TaskData]:
method task_type (line 55) | def task_type(self) -> TaskType:
method task_data (line 59) | def task_data(self) -> TaskData:
method handler (line 63) | def handler(self) -> BaseHandler:
method process_file_task (line 67) | async def process_file_task(self, _file_data: FileData) -> AsyncGenera...
class MockProcessorWithGenericError (line 71) | class MockProcessorWithGenericError(MockProcessor):
method generic_error_types (line 73) | def generic_error_types(self) -> set[type[Exception]]:
class MockProcessorWithCustomErrorHandler (line 77) | class MockProcessorWithCustomErrorHandler(MockProcessor):
method custom_error_handlers (line 81) | def custom_error_handlers(
method _handle_custom_error (line 86) | async def _handle_custom_error(
class TestAbstractFileProcessorInit (line 96) | class TestAbstractFileProcessorInit(
method setup_method (line 100) | def setup_method(self) -> None:
method teardown_method (line 110) | def teardown_method(self) -> None:
method test_init (line 114) | def test_init(self) -> None:
method test_init_already_initialized (line 122) | def test_init_already_initialized(self) -> None:
class TestAbstractFileProcessor (line 132) | class TestAbstractFileProcessor(
method setup_method (line 140) | def setup_method(self) -> None:
method test_ask_task (line 154) | async def test_ask_task(self) -> None:
method test_ask_task_without_callback_query (line 172) | async def test_ask_task_without_callback_query(self) -> None:
method test_process_file (line 190) | async def test_process_file(self) -> None:
method test_process_file_with_result_message (line 197) | async def test_process_file_with_result_message(self) -> None:
method test_process_file_dir_output (line 211) | async def test_process_file_dir_output(self) -> None:
method test_process_file_generic_error_not_registered (line 233) | async def test_process_file_generic_error_not_registered(self) -> None:
method test_process_file_error (line 245) | async def test_process_file_error(self) -> None:
method test_process_file_custom_error (line 259) | async def test_process_file_custom_error(self) -> None:
method test_process_file_unknown_error (line 272) | async def test_process_file_unknown_error(self) -> None:
method test_process_file_invalid_file_data (line 287) | async def test_process_file_invalid_file_data(self) -> None:
method test_process_file_with_callback_query (line 297) | async def test_process_file_with_callback_query(self) -> None:
method test_process_file_with_callback_query_unknown_data (line 315) | async def test_process_file_with_callback_query_unknown_data(self) -> ...
method test_process_file_process_previous_message_error (line 326) | async def test_process_file_process_previous_message_error(self, error...
method _assert_process_file_succeed (line 335) | def _assert_process_file_succeed(self, path: Path | None = None) -> None:
method _assert_get_file_and_message_data (line 347) | def _assert_get_file_and_message_data(self) -> None:
FILE: tests/file_processor/test_abstract_file_task_processor.py
class MockProcessor (line 11) | class MockProcessor(AbstractFileTaskProcessor):
method processor_type (line 15) | def processor_type(self) -> type[AbstractFileProcessor]:
class TestAbstractFileTaskProcessor (line 21) | class TestAbstractFileTaskProcessor(LanguageServiceTestMixin, TelegramTe...
method setup_method (line 24) | def setup_method(self) -> None:
method test_ask_task (line 30) | async def test_ask_task(self) -> None:
FILE: tests/file_processor/test_file_task_mixin.py
class TestFileTaskMixin (line 16) | class TestFileTaskMixin(LanguageServiceTestMixin, TelegramTestMixin):
method setup_method (line 20) | def setup_method(self) -> None:
method test_ask_task_helper (line 26) | async def test_ask_task_helper(self) -> None:
method test_ask_task_helper_without_user_data (line 38) | async def test_ask_task_helper_without_user_data(self) -> None:
method test_ask_task_helper_without_file_data_and_file (line 52) | async def test_ask_task_helper_without_file_data_and_file(self) -> None:
method _assert_inline_keyboard (line 67) | def _assert_inline_keyboard(self) -> None:
FILE: tests/image/test_image_service.py
class TestImageService (line 14) | class TestImageService(LanguageServiceTestMixin, TelegramServiceTestMixi...
method setup_method (line 18) | def setup_method(self) -> None:
method test_beautify_and_convert_images_to_pdf (line 34) | async def test_beautify_and_convert_images_to_pdf(self, num_files: int...
method test_convert_images_to_pdf (line 52) | async def test_convert_images_to_pdf(self, num_files: int) -> None:
method _get_file_data_list (line 73) | def _get_file_data_list(
FILE: tests/image_handler/test_batch_image_handler.py
class TestCompareHandlers (line 11) | class TestCompareHandlers(TelegramServiceTestMixin):
method setup_method (line 15) | def setup_method(self) -> None:
method test_handlers (line 23) | async def test_handlers(self) -> None:
FILE: tests/image_handler/test_batch_image_service.py
class TestBatchImageService (line 15) | class TestBatchImageService(LanguageServiceTestMixin, TelegramServiceTes...
method setup_method (line 24) | def setup_method(self) -> None:
method test_ask_first_image (line 40) | async def test_ask_first_image(self) -> None:
method test_check_image (line 47) | async def test_check_image(self) -> None:
method test_check_image_invalid_image (line 68) | async def test_check_image_invalid_image(self) -> None:
method test_check_image_user_data_error (line 79) | async def test_check_image_user_data_error(self) -> None:
method test_check_text_remove_last (line 96) | async def test_check_text_remove_last(self) -> None:
method test_check_text_remove_last_with_existing_file (line 111) | async def test_check_text_remove_last_with_existing_file(self) -> None:
method test_check_text_remove_last_remove_error (line 130) | async def test_check_text_remove_last_remove_error(self) -> None:
method test_check_text_beautify (line 146) | async def test_check_text_beautify(self) -> None:
method test_check_text_to_pdf (line 171) | async def test_check_text_to_pdf(self) -> None:
method test_check_text_process_with_one_file_only (line 194) | async def test_check_text_process_with_one_file_only(self) -> None:
method test_check_text_process_without_files (line 210) | async def test_check_text_process_without_files(self) -> None:
method test_check_text_cancel (line 227) | async def test_check_text_cancel(self) -> None:
method test_check_text_unknown_text (line 239) | async def test_check_text_unknown_text(self) -> None:
method test_check_text_telegram_service_error (line 246) | async def test_check_text_telegram_service_error(self, text: str) -> N...
method _assert_ask_first_image (line 258) | def _assert_ask_first_image(self) -> None:
FILE: tests/image_processor/test_abstract_image_processor.py
class MockProcessor (line 17) | class MockProcessor(AbstractImageProcessor):
method task_type (line 19) | def task_type(self) -> TaskType:
method task_data (line 23) | def task_data(self) -> TaskData:
method handler (line 27) | def handler(self) -> BaseHandler:
method process_file_task (line 31) | async def process_file_task(self, _file_data: FileData) -> AsyncGenera...
class TestAbstractImageProcessor (line 35) | class TestAbstractImageProcessor(LanguageServiceTestMixin, TelegramServi...
method setup_method (line 36) | def setup_method(self) -> None:
method teardown_method (line 53) | def teardown_method(self) -> None:
method test_init (line 58) | def test_init(self) -> None:
method test_init_already_initialized (line 66) | def test_init_already_initialized(self) -> None:
method test_get_task_data_list (line 75) | def test_get_task_data_list(self) -> None:
FILE: tests/image_processor/test_beautify_image_processor.py
class TestBeautifyImageProcessor (line 15) | class TestBeautifyImageProcessor(
method setup_method (line 20) | def setup_method(self) -> None:
method test_task_type (line 33) | def test_task_type(self) -> None:
method test_task_data (line 37) | def test_task_data(self) -> None:
method test_handler (line 41) | def test_handler(self) -> None:
method test_process_file_task (line 48) | async def test_process_file_task(self) -> None:
FILE: tests/image_processor/test_image_task_processor.py
class TestImageTaskProcessor (line 6) | class TestImageTaskProcessor(
method setup_method (line 10) | def setup_method(self) -> None:
method test_processor_type (line 15) | def test_processor_type(self) -> None:
FILE: tests/image_processor/test_image_to_pdf_processor.py
class TestImageToPdfProcessorProcessor (line 15) | class TestImageToPdfProcessorProcessor(
method setup_method (line 20) | def setup_method(self) -> None:
method test_task_type (line 33) | def test_task_type(self) -> None:
method test_task_data (line 37) | def test_task_data(self) -> None:
method test_handler (line 41) | def test_handler(self) -> None:
method test_process_file_task (line 48) | async def test_process_file_task(self) -> None:
FILE: tests/io_internal/test_io_service.py
class TestIOService (line 10) | class TestIOService:
method setup_method (line 17) | def setup_method(self) -> None:
method teardown_method (line 28) | def teardown_method(self) -> None:
method test_create_temp_directory (line 33) | async def test_create_temp_directory(self, prefix: str | None) -> None:
method test_create_temp_file (line 60) | async def test_create_temp_file(self, prefix: str | None, suffix: str ...
method test_create_temp_files (line 69) | async def test_create_temp_files(self, num_files: int) -> None:
method test_create_temp_pdf_file (line 99) | async def test_create_temp_pdf_file(self, prefix: str | None) -> None:
method test_create_temp_png_file (line 107) | async def test_create_temp_png_file(self) -> None:
method test_create_temp_txt_file (line 113) | async def test_create_temp_txt_file(self) -> None:
method _assert_temp_file (line 118) | def _assert_temp_file(self, prefix: str | None, suffix: str | None) ->...
method _get_expected_prefix (line 122) | def _get_expected_prefix(self, prefix: str | None) -> str | None:
FILE: tests/language/language_service_test_mixin.py
class LanguageServiceTestMixin (line 6) | class LanguageServiceTestMixin:
method mock_language_service (line 8) | def mock_language_service() -> AsyncMock:
FILE: tests/language/test_language_handler.py
class TestLanguageHandler (line 10) | class TestLanguageHandler(LanguageServiceTestMixin, TelegramTestMixin):
method setup_method (line 13) | def setup_method(self) -> None:
method test_handlers (line 19) | async def test_handlers(self) -> None:
FILE: tests/language/test_language_repository.py
class TestLanguageRepository (line 10) | class TestLanguageRepository:
method setup_method (line 15) | def setup_method(self) -> None:
method test_get_language (line 21) | def test_get_language(self) -> None:
method test_get_language_without_user (line 29) | def test_get_language_without_user(self) -> None:
method test_get_language_and_language_not_set (line 34) | def test_get_language_and_language_not_set(self) -> None:
method test_get_language_legacy_en_code (line 39) | def test_get_language_legacy_en_code(self) -> None:
method test_upsert_language (line 48) | def test_upsert_language(self) -> None:
method test_upsert_language_without_user (line 56) | def test_upsert_language_without_user(self) -> None:
method _mock_user_entity_dict (line 67) | def _mock_user_entity_dict(self, user_entity_dict: dict[str, Any] | No...
FILE: tests/language/test_language_service.py
class TestLanguageService (line 10) | class TestLanguageService(TelegramTestMixin):
method setup_method (line 15) | def setup_method(self) -> None:
method teardown_method (line 25) | def teardown_method(self) -> None:
method test_get_language_code_from_short_code (line 30) | def test_get_language_code_from_short_code(self, value: str, expected:...
method test_send_language_options (line 35) | async def test_send_language_options(self) -> None:
method test_send_language_options_with_callback_query (line 45) | async def test_send_language_options_with_callback_query(self) -> None:
method test_get_user_language (line 55) | async def test_get_user_language(self) -> None:
method test_get_user_language_cached (line 67) | async def test_get_user_language_cached(self) -> None:
method test_get_user_language_without_user_data (line 76) | async def test_get_user_language_without_user_data(self) -> None:
method test_get_user_language_without_callback_query (line 85) | async def test_get_user_language_without_callback_query(self) -> None:
method test_get_user_language_with_chat_id (line 98) | async def test_get_user_language_with_chat_id(self) -> None:
method test_get_user_language_without_message_and_chat (line 113) | async def test_get_user_language_without_message_and_chat(self) -> None:
method test_update_user_language (line 127) | async def test_update_user_language(self, side_effect: type[Exception]...
method test_update_user_language_without_user_data (line 145) | async def test_update_user_language_without_user_data(self) -> None:
method test_update_user_language_invalid_callback_query_data (line 157) | async def test_update_user_language_invalid_callback_query_data(self) ...
FILE: tests/merge/test_merge_handler.py
class TestMergeHandler (line 11) | class TestMergeHandler(TelegramServiceTestMixin):
method setup_method (line 15) | def setup_method(self) -> None:
method test_conversation_handler (line 23) | async def test_conversation_handler(self) -> None:
FILE: tests/merge/test_merge_service.py
class TestMergeService (line 15) | class TestMergeService(
method setup_method (line 27) | def setup_method(self) -> None:
method test_ask_first_pdf (line 43) | async def test_ask_first_pdf(self) -> None:
method test_check_pdf (line 50) | async def test_check_pdf(self) -> None:
method test_check_pdf_invalid_pdf (line 64) | async def test_check_pdf_invalid_pdf(self) -> None:
method test_check_pdf_user_data_error (line 75) | async def test_check_pdf_user_data_error(self) -> None:
method test_check_text_remove_last (line 92) | async def test_check_text_remove_last(self) -> None:
method test_check_text_remove_last_with_existing_file (line 107) | async def test_check_text_remove_last_with_existing_file(self) -> None:
method test_check_text_remove_last_remove_error (line 126) | async def test_check_text_remove_last_remove_error(self) -> None:
method test_check_text_done_and_merge (line 142) | async def test_check_text_done_and_merge(self) -> None:
method test_check_text_done_pdf_service_error (line 163) | async def test_check_text_done_pdf_service_error(self) -> None:
method test_check_text_done_with_one_file_only (line 179) | async def test_check_text_done_with_one_file_only(self) -> None:
method test_check_text_done_without_files (line 194) | async def test_check_text_done_without_files(self) -> None:
method test_check_text_cancel (line 210) | async def test_check_text_cancel(self) -> None:
method test_check_text_unknown_text (line 222) | async def test_check_text_unknown_text(self) -> None:
method test_check_text_telegram_service_error (line 229) | async def test_check_text_telegram_service_error(self, text: str) -> N...
method _assert_ask_first_pdf (line 241) | def _assert_ask_first_pdf(self) -> None:
FILE: tests/path_test_mixin.py
class PathTestMixin (line 7) | class PathTestMixin:
method mock_file_path (line 9) | def mock_file_path() -> MagicMock:
method mock_dir_path (line 15) | def mock_dir_path() -> MagicMock:
method mock_path_open (line 21) | def mock_path_open(path: MagicMock) -> MagicMock:
method mock_path_stat (line 27) | def mock_path_stat(path: MagicMock) -> MagicMock:
FILE: tests/payment/test_payment_handler.py
class TestLanguageHandler (line 17) | class TestLanguageHandler(TelegramTestMixin):
method setup_method (line 21) | def setup_method(self) -> None:
method test_handlers (line 27) | async def test_handlers(self) -> None:
FILE: tests/payment/test_payment_service.py
class TestPaymentService (line 10) | class TestPaymentService(LanguageServiceTestMixin, TelegramServiceTestMi...
method setup_method (line 17) | def setup_method(self) -> None:
method test_send_support_options (line 25) | async def test_send_support_options(self) -> None:
method test_send_support_options_with_callback_query (line 38) | async def test_send_support_options_with_callback_query(self) -> None:
method test_send_invoice (line 52) | async def test_send_invoice(self) -> None:
method test_send_invoice_invalid_callback_query_data (line 62) | async def test_send_invoice_invalid_callback_query_data(self) -> None:
method test_precheckout_check (line 69) | async def test_precheckout_check(self) -> None:
method test_precheckout_check_invalid_payload (line 75) | async def test_precheckout_check_invalid_payload(self) -> None:
method test_successful_payment (line 83) | async def test_successful_payment(self) -> None:
method _assert_keyboard_payment_callback_data (line 87) | def _assert_keyboard_payment_callback_data(self, reply_markup: InlineK...
FILE: tests/pdf/test_compress_result.py
class TestCompressResult (line 7) | class TestCompressResult(PathTestMixin):
method setup_method (line 8) | def setup_method(self) -> None:
method test_reduced_percentage (line 12) | def test_reduced_percentage(self) -> None:
method test_readable_old_size (line 16) | def test_readable_old_size(self) -> None:
method test_readable_new_size (line 21) | def test_readable_new_size(self) -> None:
FILE: tests/pdf/test_pdf_service.py
class TestPDFService (line 37) | class TestPDFService(
method setup_method (line 44) | def setup_method(self) -> None:
method teardown_method (line 73) | def teardown_method(self) -> None:
method test_add_watermark_to_pdf (line 82) | async def test_add_watermark_to_pdf(self) -> None:
method test_add_watermark_to_pdf_read_error (line 123) | async def test_add_watermark_to_pdf_read_error(self) -> None:
method test_grayscale_pdf (line 133) | async def test_grayscale_pdf(self) -> None:
method test_compare_pdfs (line 161) | async def test_compare_pdfs(self) -> None:
method test_compress_pdf (line 174) | async def test_compress_pdf(self) -> None:
method test_convert_to_images (line 192) | async def test_convert_to_images(self) -> None:
method test_create_pdf_from_text (line 206) | async def test_create_pdf_from_text(self, has_font_data: bool) -> None:
method test_crop_pdf_by_percentage (line 250) | async def test_crop_pdf_by_percentage(self) -> None:
method test_crop_pdf_by_margin_size (line 262) | async def test_crop_pdf_by_margin_size(self) -> None:
method test_decrypt_pdf (line 277) | async def test_decrypt_pdf(self, num_pages: int) -> None:
method test_decrypt_pdf_not_encrypted (line 297) | async def test_decrypt_pdf_not_encrypted(self) -> None:
method test_decrypt_pdf_incorrect_password (line 311) | async def test_decrypt_pdf_incorrect_password(self) -> None:
method test_decrypt_pdf_invalid_encryption_method (line 323) | async def test_decrypt_pdf_invalid_encryption_method(self) -> None:
method test_encrypt_pdf (line 336) | async def test_encrypt_pdf(self, num_pages: int) -> None:
method test_encrypt_pdf_already_encrypted (line 356) | async def test_encrypt_pdf_already_encrypted(self) -> None:
method test_extract_pdf_text (line 369) | async def test_extract_pdf_text(self) -> None:
method test_extract_pdf_text_error (line 377) | async def test_extract_pdf_text_error(self) -> None:
method test_extract_pdf_text_no_text (line 389) | async def test_extract_pdf_text_no_text(self) -> None:
method test_extract_pdf_images (line 401) | async def test_extract_pdf_images(self) -> None:
method test_extract_pdf_images_no_images (line 415) | async def test_extract_pdf_images_no_images(self) -> None:
method test_extract_pdf_images_cli_error (line 430) | async def test_extract_pdf_images_cli_error(self) -> None:
method test_merge_pdfs (line 446) | async def test_merge_pdfs(self, num_files: int) -> None:
method test_merge_pdfs_read_error (line 461) | async def test_merge_pdfs_read_error(self, exception: Exception) -> None:
method test_ocr_pdf (line 477) | async def test_ocr_pdf(self) -> None:
method test_ocr_pdf_error (line 494) | async def test_ocr_pdf_error(self, error: type[Exception], expected: t...
method test_preview_pdf (line 507) | async def test_preview_pdf(self) -> None:
method test_rename_pdf (line 538) | async def test_rename_pdf(self) -> None:
method test_rotate_pdf (line 552) | async def test_rotate_pdf(self, num_pages: int) -> None:
method test_scale_pdf_by_factor (line 579) | async def test_scale_pdf_by_factor(self, num_pages: int) -> None:
method test_scale_pdf_to_dimension (line 604) | async def test_scale_pdf_to_dimension(self, num_pages: int) -> None:
method test_split_range_valid (line 647) | async def test_split_range_valid(self, split_range: str) -> None:
method test_split_range_invalid (line 651) | async def test_split_range_invalid(self) -> None:
method test_split_pdf (line 655) | async def test_split_pdf(self) -> None:
method _async_context_manager_side_effect_echo (line 670) | def _async_context_manager_side_effect_echo(
method _method_side_effect_echo (line 678) | def _method_side_effect_echo(return_value: str, *_args: Any, **_kwargs...
method _get_file_data_list (line 682) | def _get_file_data_list(
method _assert_telegram_and_io_services (line 697) | def _assert_telegram_and_io_services(self, temp_pdf_file_prefix: str) ...
method _assert_decrypt_failure (line 701) | def _assert_decrypt_failure(self, reader: MagicMock) -> None:
FILE: tests/pdf_processor/test_abstract_pdf_processor.py
class MockProcessor (line 17) | class MockProcessor(AbstractPdfProcessor):
method task_type (line 19) | def task_type(self) -> TaskType:
method task_data (line 23) | def task_data(self) -> TaskData:
method handler (line 27) | def handler(self) -> BaseHandler:
method process_file_task (line 31) | async def process_file_task(self, _file_data: FileData) -> AsyncGenera...
class TestAbstractPdfProcessor (line 35) | class TestAbstractPdfProcessor(LanguageServiceTestMixin, TelegramService...
method setup_method (line 36) | def setup_method(self) -> None:
method teardown_method (line 52) | def teardown_method(self) -> None:
method test_init (line 57) | def test_init(self) -> None:
method test_init_already_initialized (line 65) | def test_init_already_initialized(self) -> None:
method test_get_task_data_list (line 74) | def test_get_task_data_list(self) -> None:
method test_generic_error_types (line 84) | def test_generic_error_types(self) -> None:
FILE: tests/pdf_processor/test_abstract_pdf_select_and_text_processor.py
class OptionType (line 26) | class OptionType(SelectOption):
method ask_value_text (line 31) | def ask_value_text(self) -> str:
class MockProcessor (line 35) | class MockProcessor(PathTestMixin, AbstractPdfSelectAndTextProcessor):
method __init__ (line 38) | def __init__(
method entry_point_data_type (line 50) | def entry_point_data_type(self) -> type[FileData]:
method task_type (line 54) | def task_type(self) -> TaskType:
method task_data (line 58) | def task_data(self) -> TaskData:
method ask_select_option_text (line 62) | def ask_select_option_text(self) -> str:
method select_option_type (line 66) | def select_option_type(self) -> type[OptionType]:
method invalid_text_input_error (line 70) | def invalid_text_input_error(self) -> str:
method get_cleaned_text_input (line 73) | def get_cleaned_text_input(self, _text: str) -> str | None:
method process_file_task (line 77) | async def process_file_task(self, _file_data: FileData) -> AsyncGenera...
class TestAbstractPdfTextInputProcessor (line 81) | class TestAbstractPdfTextInputProcessor(
method setup_method (line 89) | def setup_method(self) -> None:
method test_handler (line 116) | def test_handler(self) -> None:
method test_ask_select_option (line 158) | async def test_ask_select_option(self) -> None:
method test_ask_select_option_invalid_callback_query_data (line 171) | async def test_ask_select_option_invalid_callback_query_data(self) -> ...
method test_ask_text_input (line 183) | async def test_ask_text_input(self) -> None:
method test_ask_text_input_invalid_callback_query_data (line 202) | async def test_ask_text_input_invalid_callback_query_data(self) -> None:
method test_process_text_input (line 215) | async def test_process_text_input(self) -> None:
method test_process_text_input_invalid_input (line 232) | async def test_process_text_input_invalid_input(self) -> None:
method test_process_text_input_invalid_file_data (line 241) | async def test_process_text_input_invalid_file_data(self) -> None:
FILE: tests/pdf_processor/test_abstract_pdf_text_input_processor.py
class MockProcessor (line 22) | class MockProcessor(PathTestMixin, AbstractPdfTextInputProcessor):
method __init__ (line 25) | def __init__(
method entry_point_data_type (line 37) | def entry_point_data_type(self) -> type[FileData]:
method task_type (line 41) | def task_type(self) -> TaskType:
method task_data (line 45) | def task_data(self) -> TaskData:
method get_ask_text_input_text (line 48) | def get_ask_text_input_text(self, _: Callable[[str], str]) -> str:
method invalid_text_input_error (line 52) | def invalid_text_input_error(self) -> str:
method get_cleaned_text_input (line 55) | def get_cleaned_text_input(self, _text: str) -> str | None:
method process_file_task (line 59) | async def process_file_task(self, _file_data: FileData) -> AsyncGenera...
class TestAbstractPdfTextInputProcessor (line 63) | class TestAbstractPdfTextInputProcessor(
method setup_method (line 70) | def setup_method(self) -> None:
method test_handler (line 90) | def test_handler(self) -> None:
method test_ask_text_input (line 122) | async def test_ask_text_input(self) -> None:
method test_process_text_input (line 141) | async def test_process_text_input(self) -> None:
method test_process_text_input_invalid_input (line 158) | async def test_process_text_input_invalid_input(self) -> None:
method test_process_text_input_get_file_data_error (line 167) | async def test_process_text_input_get_file_data_error(self) -> None:
FILE: tests/pdf_processor/test_compress_pdf_processor.py
class TestCompressPdfProcessor (line 15) | class TestCompressPdfProcessor(
method setup_method (line 20) | def setup_method(self) -> None:
method test_get_task_type (line 33) | def test_get_task_type(self) -> None:
method test_task_data (line 37) | def test_task_data(self) -> None:
method test_handler (line 41) | def test_handler(self) -> None:
method test_process_file_task (line 48) | async def test_process_file_task(self) -> None:
FILE: tests/pdf_processor/test_crop_pdf_processor.py
class TestPdfProcessor (line 14) | class TestPdfProcessor(
method setup_method (line 22) | def setup_method(self) -> None:
method test_entry_point_data_type (line 37) | def test_entry_point_data_type(self) -> None:
method test_task_type (line 41) | def test_task_type(self) -> None:
method test_task_data (line 45) | def test_task_data(self) -> None:
method test_select_option_type (line 49) | def test_select_option_type(self) -> None:
method test_option_and_input_data_type (line 53) | def test_option_and_input_data_type(self) -> None:
method test_get_cleaned_text_input (line 57) | def test_get_cleaned_text_input(self) -> None:
method test_get_cleaned_text_input_invalid (line 61) | def test_get_cleaned_text_input_invalid(self) -> None:
method test_process_file_task_scale_by_factor (line 66) | async def test_process_file_task_scale_by_factor(self) -> None:
method test_process_file_task_scale_to_dimension (line 84) | async def test_process_file_task_scale_to_dimension(self) -> None:
method test_process_file_task_invalid_file_data (line 102) | async def test_process_file_task_invalid_file_data(self) -> None:
FILE: tests/pdf_processor/test_decrypt_pdf_processor.py
class TestDecryptPdfProcessor (line 14) | class TestDecryptPdfProcessor(
method setup_method (line 21) | def setup_method(self) -> None:
method test_get_task_type (line 34) | def test_get_task_type(self) -> None:
method test_entry_point_data_type (line 38) | def test_entry_point_data_type(self) -> None:
method test_task_data (line 42) | def test_task_data(self) -> None:
method test_get_cleaned_text_input (line 46) | def test_get_cleaned_text_input(self) -> None:
method test_get_custom_error_handlers (line 51) | async def test_get_custom_error_handlers(self) -> None:
method test_process_file_task (line 67) | async def test_process_file_task(self) -> None:
method test_process_file_task_invalid_file_data (line 77) | async def test_process_file_task_invalid_file_data(self) -> None:
FILE: tests/pdf_processor/test_encrypt_pdf_processor.py
class TestEncryptPdfProcessor (line 14) | class TestEncryptPdfProcessor(
method setup_method (line 19) | def setup_method(self) -> None:
method test_get_task_type (line 32) | def test_get_task_type(self) -> None:
method test_entry_point_data_type (line 36) | def test_entry_point_data_type(self) -> None:
method test_task_data (line 40) | def test_task_data(self) -> None:
method test_get_cleaned_text_input (line 44) | def test_get_cleaned_text_input(self) -> None:
method test_process_file_task (line 49) | async def test_process_file_task(self) -> None:
method test_process_file_task_invalid_file_data (line 59) | async def test_process_file_task_invalid_file_data(self) -> None:
FILE: tests/pdf_processor/test_extract_pdf_image_processor.py
class TestExtractPdfImageProcessor (line 14) | class TestExtractPdfImageProcessor(
method setup_method (line 19) | def setup_method(self) -> None:
method test_get_task_type (line 32) | def test_get_task_type(self) -> None:
method test_task_data (line 36) | def test_task_data(self) -> None:
method test_handler (line 40) | def test_handler(self) -> None:
method test_process_file_task (line 47) | async def test_process_file_task(self) -> None:
FILE: tests/pdf_processor/test_extract_pdf_text_processor.py
class TestExtractPDFTextProcessor (line 14) | class TestExtractPDFTextProcessor(
method setup_method (line 19) | def setup_method(self) -> None:
method test_get_task_type (line 32) | def test_get_task_type(self) -> None:
method test_task_data (line 36) | def test_task_data(self) -> None:
method test_handler (line 40) | def test_handler(self) -> None:
method test_process_file_task (line 47) | async def test_process_file_task(self) -> None:
FILE: tests/pdf_processor/test_grayscale_pdf_processor.py
class TestGrayscalePdfProcessor (line 14) | class TestGrayscalePdfProcessor(
method setup_method (line 19) | def setup_method(self) -> None:
method test_get_task_type (line 32) | def test_get_task_type(self) -> None:
method test_task_data (line 36) | def test_task_data(self) -> None:
method test_handler (line 40) | def test_handler(self) -> None:
method test_process_file_task (line 47) | async def test_process_file_task(self) -> None:
FILE: tests/pdf_processor/test_ocr_pdf_processor.py
class TestOCRPdfProcessor (line 14) | class TestOCRPdfProcessor(
method setup_method (line 19) | def setup_method(self) -> None:
method test_get_task_type (line 32) | def test_get_task_type(self) -> None:
method test_task_data (line 36) | def test_task_data(self) -> None:
method test_handler (line 40) | def test_handler(self) -> None:
method test_process_file_task (line 47) | async def test_process_file_task(self) -> None:
FILE: tests/pdf_processor/test_pdf_task_processor.py
class TestPdfTaskProcessor (line 6) | class TestPdfTaskProcessor(
method setup_method (line 10) | def setup_method(self) -> None:
method test_processor_type (line 15) | def test_processor_type(self) -> None:
FILE: tests/pdf_processor/test_pdf_to_image_processor.py
class TestPdfToImageProcessor (line 14) | class TestPdfToImageProcessor(
method setup_method (line 19) | def setup_method(self) -> None:
method test_get_task_type (line 32) | def test_get_task_type(self) -> None:
method test_task_data (line 36) | def test_task_data(self) -> None:
method test_handler (line 40) | def test_handler(self) -> None:
method test_process_file_task (line 47) | async def test_process_file_task(self) -> None:
FILE: tests/pdf_processor/test_preview_pdf_processor.py
class TestPreviewPdfProcessor (line 14) | class TestPreviewPdfProcessor(
method setup_method (line 19) | def setup_method(self) -> None:
method test_get_task_type (line 32) | def test_get_task_type(self) -> None:
method test_task_data (line 36) | def test_task_data(self) -> None:
method test_handler (line 40) | def test_handler(self) -> None:
method test_process_file_task (line 47) | async def test_process_file_task(self) -> None:
FILE: tests/pdf_processor/test_rename_pdf_processor.py
class TestRenamePdfProcessor (line 14) | class TestRenamePdfProcessor(
method setup_method (line 19) | def setup_method(self) -> None:
method test_task_type (line 33) | def test_task_type(self) -> None:
method test_entry_point_data_type (line 37) | def test_entry_point_data_type(self) -> None:
method test_task_data (line 41) | def test_task_data(self) -> None:
method test_get_cleaned_text_input (line 45) | def test_get_cleaned_text_input(self) -> None:
method test_get_cleaned_text_input_invalid (line 50) | def test_get_cleaned_text_input_invalid(self) -> None:
method test_process_file_task (line 56) | async def test_process_file_task(self) -> None:
method test_process_file_task_invalid_file_data (line 66) | async def test_process_file_task_invalid_file_data(self) -> None:
FILE: tests/pdf_processor/test_rotate_pdf_processor.py
class TestRotatePdfProcessor (line 16) | class TestRotatePdfProcessor(
method setup_method (line 23) | def setup_method(self) -> None:
method test_task_type (line 38) | def test_task_type(self) -> None:
method test_task_data (line 42) | def test_task_data(self) -> None:
method test_handler (line 46) | def test_handler(self) -> None:
method test_process_file_task (line 79) | async def test_process_file_task(self) -> None:
method test_process_file_task_invalid_file_data (line 93) | async def test_process_file_task_invalid_file_data(self) -> None:
method test_ask_degree (line 100) | async def test_ask_degree(self) -> None:
method test_ask_degree_invalid_callback_query_data (line 115) | async def test_ask_degree_invalid_callback_query_data(self) -> None:
FILE: tests/pdf_processor/test_scale_pdf_processor.py
class TestPdfProcessor (line 20) | class TestPdfProcessor(
method setup_method (line 28) | def setup_method(self) -> None:
method test_entry_point_data_type (line 43) | def test_entry_point_data_type(self) -> None:
method test_task_type (line 47) | def test_task_type(self) -> None:
method test_task_data (line 51) | def test_task_data(self) -> None:
method test_select_option_type (line 55) | def test_select_option_type(self) -> None:
method test_option_and_input_data_type (line 59) | def test_option_and_input_data_type(self) -> None:
method test_get_cleaned_text_input (line 63) | def test_get_cleaned_text_input(self) -> None:
method test_get_cleaned_text_input_invalid (line 67) | def test_get_cleaned_text_input_invalid(self) -> None:
method test_process_file_task_scale_by_factor (line 72) | async def test_process_file_task_scale_by_factor(self) -> None:
method test_process_file_task_scale_to_dimension (line 88) | async def test_process_file_task_scale_to_dimension(self) -> None:
method test_process_file_task_invalid_file_data (line 106) | async def test_process_file_task_invalid_file_data(self) -> None:
FILE: tests/pdf_processor/test_split_pdf_processor.py
class TestSplitPdfProcessor (line 14) | class TestSplitPdfProcessor(
method setup_method (line 19) | def setup_method(self) -> None:
method test_task_type (line 32) | def test_task_type(self) -> None:
method test_entry_point_data_type (line 36) | def test_entry_point_data_type(self) -> None:
method test_task_data (line 40) | def test_task_data(self) -> None:
method test_get_cleaned_text_input (line 47) | def test_get_cleaned_text_input(self, is_valid: bool, expected: str | ...
method test_process_file_task (line 53) | async def test_process_file_task(self) -> None:
method test_process_file_task_invalid_file_data (line 63) | async def test_process_file_task_invalid_file_data(self) -> None:
FILE: tests/telegram_internal/telegram_service_test_mixin.py
class TelegramServiceTestMixin (line 11) | class TelegramServiceTestMixin(TelegramTestMixin):
method mock_telegram_service (line 12) | def mock_telegram_service(self) -> AsyncMock:
method get_file_data_side_effect_by_index (line 26) | def get_file_data_side_effect_by_index(
FILE: tests/telegram_internal/telegram_test_mixin.py
class TelegramTestMixin (line 23) | class TelegramTestMixin(PathTestMixin):
method setup_method (line 45) | def setup_method(self) -> None:
method teardown_method (line 97) | def teardown_method(self) -> None:
FILE: tests/telegram_internal/test_telegram_service.py
class TestTelegramService (line 25) | class TestTelegramService(LanguageServiceTestMixin, TelegramTestMixin):
method setup_method (line 35) | def setup_method(self) -> None:
method teardown_method (line 50) | def teardown_method(self) -> None:
method test_init_with_telegram_app (line 55) | async def test_init_with_telegram_app(self) -> None:
method test_check_file_size (line 62) | async def test_check_file_size(self) -> None:
method test_check_file_size_too_large (line 67) | async def test_check_file_size_too_large(self) -> None:
method test_check_file_upload_size (line 73) | async def test_check_file_upload_size(self) -> None:
method test_check_file_upload_size_too_large (line 80) | async def test_check_file_upload_size_too_large(self) -> None:
method test_check_image_document (line 88) | async def test_check_image_document(self) -> None:
method test_check_image_document_invalid_mime_type (line 98) | async def test_check_image_document_invalid_mime_type(self) -> None:
method test_check_image_document_too_large (line 106) | async def test_check_image_document_too_large(self) -> None:
method test_check_image (line 115) | async def test_check_image(self) -> None:
method test_check_image_not_found (line 125) | async def test_check_image_not_found(self) -> None:
method test_check_image_too_large (line 133) | async def test_check_image_too_large(self) -> None:
method test_check_pdf_document (line 142) | async def test_check_pdf_document(self) -> None:
method test_check_pdf_document_invalid_mime_type (line 152) | async def test_check_pdf_document_invalid_mime_type(self) -> None:
method test_check_pdf_document_too_large (line 160) | async def test_check_pdf_document_too_large(self) -> None:
method test_download_pdf_file (line 169) | async def test_download_pdf_file(self) -> None:
method test_download_files (line 180) | async def test_download_files(self, num_files: int) -> None:
method test_cancel_conversation (line 214) | async def test_cancel_conversation(self) -> None:
method test_cancel_conversation_with_callback_query (line 225) | async def test_cancel_conversation_with_callback_query(self) -> None:
method test_get_back_button (line 239) | async def test_get_back_button(self) -> None:
method test_get_back_inline_markup (line 246) | async def test_get_back_inline_markup(self) -> None:
method test_get_support_markup (line 257) | async def test_get_support_markup(self) -> None:
method test_get_user_data (line 261) | def test_get_user_data(self) -> None:
method test_get_user_data_key_error (line 270) | def test_get_user_data_key_error(self, user_data: dict | None) -> None:
method test_user_data_contains (line 276) | def test_user_data_contains(self) -> None:
method test_user_data_contains_without_key (line 281) | def test_user_data_contains_without_key(self) -> None:
method test_user_data_contains_without_user_data (line 286) | def test_user_data_contains_without_user_data(self) -> None:
method test_update_user_data (line 291) | def test_update_user_data(self) -> None:
method test_update_user_data_error (line 296) | def test_update_user_data_error(self) -> None:
method test_get_file_data (line 303) | def test_get_file_data(self) -> None:
method test_cache_file_data (line 311) | def test_cache_file_data(self) -> None:
method test_get_message_data (line 316) | def test_get_message_data(self) -> None:
method test_cache_message_data (line 324) | def test_cache_message_data(self) -> None:
method test_cache_message_data_error (line 331) | def test_cache_message_data_error(self) -> None:
method test_cache_message_data_not_message (line 335) | def test_cache_message_data_not_message(self) -> None:
method test_answer_query_and_drop_data (line 342) | async def test_answer_query_and_drop_data(self, side_effect: type[Exce...
method test_reply_with_back_markup (line 356) | async def test_reply_with_back_markup(self, parse_mode: ParseMode | No...
method test_reply_with_cancel_markup (line 367) | async def test_reply_with_cancel_markup(self, parse_mode: ParseMode | ...
method test_send_file_document (line 377) | async def test_send_file_document(self) -> None:
method test_send_file_image (line 402) | async def test_send_file_image(self) -> None:
method test_send_file_document_with_query (line 427) | async def test_send_file_document_with_query(self) -> None:
method test_send_file_too_large (line 456) | async def test_send_file_too_large(self) -> None:
method test_send_file_names (line 473) | async def test_send_file_names(self) -> None:
method test_send_file_names_truncate (line 484) | async def test_send_file_names_truncate(self) -> None:
method test_send_message (line 496) | async def test_send_message(self) -> None:
FILE: tests/test_containers.py
class TestContainer (line 9) | class TestContainer:
method test_container (line 10) | def test_container(self) -> None:
method _test_providers (line 20) | def _test_providers(self, container: Container) -> None:
FILE: tests/test_models.py
class UnknownTelegramObject (line 7) | class UnknownTelegramObject:
class TestFileData (line 11) | class TestFileData(TelegramTestMixin):
method test_from_telegram_document (line 12) | def test_from_telegram_document(self) -> None:
method test_from_telegram_photo_size (line 16) | def test_from_telegram_photo_size(self) -> None:
class TestTaskData (line 21) | class TestTaskData(TelegramTestMixin):
method test_get_file_data (line 22) | def test_get_file_data(self) -> None:
FILE: tests/text/test_text_handler.py
class TestTextHandler (line 11) | class TestTextHandler(TelegramServiceTestMixin):
method setup_method (line 15) | def setup_method(self) -> None:
method test_conversation_handler (line 23) | async def test_conversation_handler(self) -> None:
FILE: tests/text/test_text_repository.py
class TestTextRepository (line 9) | class TestTextRepository:
method setup_method (line 14) | def setup_method(self) -> None:
method test_get_font (line 25) | def test_get_font(self) -> None:
method test_get_font_no_regular_font (line 31) | def test_get_font_no_regular_font(self) -> None:
method test_get_font_unknown_font (line 39) | def test_get_font_unknown_font(self) -> None:
method _assert_api_call (line 45) | def _assert_api_call(self) -> None:
FILE: tests/text/test_text_service.py
class TestTextService (line 15) | class TestTextService(LanguageServiceTestMixin, TelegramServiceTestMixin...
method setup_method (line 23) | def setup_method(self) -> None:
method test_ask_pdf_text (line 45) | async def test_ask_pdf_text(self) -> None:
method test_ask_pdf_font (line 51) | async def test_ask_pdf_font(self) -> None:
method test_ask_pdf_font_cancel_option (line 61) | async def test_ask_pdf_font_cancel_option(self) -> None:
method test_check_text (line 71) | async def test_check_text(self) -> None:
method test_check_text_invalid_user_data (line 90) | async def test_check_text_invalid_user_data(self) -> None:
method test_check_text_unknown_font (line 104) | async def test_check_text_unknown_font(self) -> None:
method test_check_text_skip_option (line 116) | async def test_check_text_skip_option(self) -> None:
method test_check_text_cancel_option (line 136) | async def test_check_text_cancel_option(self) -> None:
FILE: tests/watermark/test_watermark_handler.py
class TestWatermarkHandler (line 11) | class TestWatermarkHandler(TelegramServiceTestMixin):
method setup_method (line 15) | def setup_method(self) -> None:
method test_conversation_handler (line 23) | async def test_conversation_handler(self) -> None:
FILE: tests/watermark/test_watermark_service.py
class TestWatermarkService (line 14) | class TestWatermarkService(LanguageServiceTestMixin, TelegramServiceTest...
method setup_method (line 20) | def setup_method(self) -> None:
method test_ask_source_pdf (line 36) | async def test_ask_source_pdf(self) -> None:
method test_check_source_pdf (line 43) | async def test_check_source_pdf(self) -> None:
method test_check_source_pdf_invalid_pdf (line 54) | async def test_check_source_pdf_invalid_pdf(self) -> None:
method test_add_watermark_to_pdf (line 65) | async def test_add_watermark_to_pdf(self) -> None:
method test_add_watermark_to_pdf_service_error (line 86) | async def test_add_watermark_to_pdf_service_error(self) -> None:
method test_add_watermark_to_pdf_invalid_user_data (line 102) | async def test_add_watermark_to_pdf_invalid_user_data(self) -> None:
method test_add_watermark_to_pdf_invalid_pdf (line 116) | async def test_add_watermark_to_pdf_invalid_pdf(self) -> None:
method test_check_text_back_option (line 128) | async def test_check_text_back_option(self) -> None:
method test_check_text_cancel_option (line 134) | async def test_check_text_cancel_option(self) -> None:
method test_check_text_unknown_text (line 143) | async def test_check_text_unknown_text(self) -> None:
FILE: tests/webpage/test_webpage_handler.py
class TestWebpageHandler (line 11) | class TestWebpageHandler(TelegramTestMixin):
method setup_method (line 12) | def setup_method(self) -> None:
method test_handlers (line 18) | async def test_handlers(self) -> None:
FILE: tests/webpage/test_webpage_service.py
class TestWebpageService (line 17) | class TestWebpageService(LanguageServiceTestMixin, TelegramServiceTestMi...
method setup_method (line 22) | def setup_method(self) -> None:
method teardown_method (line 41) | def teardown_method(self) -> None:
method test_url_to_pdf (line 45) | async def test_url_to_pdf(self) -> None:
method test_url_to_pdf_clear_cache_error (line 52) | async def test_url_to_pdf_clear_cache_error(self) -> None:
method test_url_to_pdf_cache_error (line 61) | async def test_url_to_pdf_cache_error(self) -> None:
method test_url_to_pdf_in_progress (line 70) | async def test_url_to_pdf_in_progress(self) -> None:
method test_url_to_pdf_error (line 101) | async def test_url_to_pdf_error(self, error: type[Exception]) -> None:
method _assert_url_to_pdf_calls (line 110) | def _assert_url_to_pdf_calls(self) -> None:
method _assert_url_to_pdf_send_file (line 124) | def _assert_url_to_pdf_send_file(self) -> None:
Condensed preview — 261 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,796K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 160,
"preview": "github: [zeshuaro]\nko_fi: zeshuaro\nliberapay: zeshuaro\npatreon: zeshuaro\ncustom:\n - \"https://www.buymeacoffee.com/zeshu"
},
{
"path": ".github/workflows/github-actions.yml",
"chars": 3114,
"preview": "name: GitHub Actions\n\non:\n push:\n branches:\n - master\n pull_request:\n\nconcurrency:\n group: ${{ github.workflo"
},
{
"path": ".github/workflows/pull-request-target.yml",
"chars": 219,
"preview": "name: Pull request target actions\n\non:\n pull_request_target:\n\njobs:\n pull-request:\n uses: zeshuaro/github-actions-w"
},
{
"path": ".github/workflows/pull-request.yml",
"chars": 198,
"preview": "name: Pull request actions\n\non:\n pull_request:\n\njobs:\n pull-request:\n uses: zeshuaro/github-actions-workflows/.gith"
},
{
"path": ".github/workflows/scheduled.yml",
"chars": 324,
"preview": "name: Scheduled actions\n\non:\n schedule:\n - cron: \"0 8 * * *\"\n\njobs:\n scheduled:\n uses: zeshuaro/github-actions-w"
},
{
"path": ".github/workflows/update-translations.yml",
"chars": 1332,
"preview": "name: Update Translations\n\non:\n schedule:\n - cron: \"0 8 * * *\"\n workflow_dispatch:\n\njobs:\n update-translations:\n "
},
{
"path": ".gitignore",
"chars": 4327,
"preview": "# Created by https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,python\n# Edit at https://www.toptal."
},
{
"path": ".pre-commit-config.yaml",
"chars": 755,
"preview": "repos:\n - repo: https://github.com/pre-commit/pre-commit-hooks\n rev: v6.0.0\n hooks:\n - id: check-added-large"
},
{
"path": ".python-version",
"chars": 7,
"preview": "3.14.4\n"
},
{
"path": ".vscode/launch.json",
"chars": 240,
"preview": "{\n \"version\": \"0.2.0\",\n \"configurations\": [\n {\n \"name\": \"Python: pdf_bot\",\n \"type\": \"python\",\n \"requ"
},
{
"path": ".vscode/settings.json",
"chars": 352,
"preview": "{\n \"editor.formatOnSave\": true,\n \"python.analysis.indexing\": true,\n \"python.testing.pytestArgs\": [\"tests\"],\n \"python"
},
{
"path": "Dockerfile",
"chars": 1059,
"preview": "FROM --platform=linux/amd64 python:3.14.4-slim AS build\n\nARG COMMIT_HASH\n\nWORKDIR /build\nRUN apt-get update && apt-get i"
},
{
"path": "LICENSE",
"chars": 34514,
"preview": " GNU AFFERO GENERAL PUBLIC LICENSE\n Version 3, 19 November 2007\n\n Copyright (C)"
},
{
"path": "README.md",
"chars": 3981,
"preview": "# Telegram PDF Bot\n\n[](https://t.me/pdfbot)\n[![MIT Li"
},
{
"path": "_typos.toml",
"chars": 52,
"preview": "[type.po]\nextend-glob = [\"*.po\"]\ncheck-file = false\n"
},
{
"path": "codecov.yml",
"chars": 15,
"preview": "comment: false\n"
},
{
"path": "crowdin.yml",
"chars": 73,
"preview": "project_id_env: CROWDIN_PROJECT_ID\napi_token_env: CROWDIN_PERSONAL_TOKEN\n"
},
{
"path": "locale/af_ZA/LC_MESSAGES/pdf_bot.po",
"chars": 48317,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/am_ET/LC_MESSAGES/pdf_bot.po",
"chars": 46209,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/ar_SA/LC_MESSAGES/pdf_bot.po",
"chars": 47145,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/ca_ES/LC_MESSAGES/pdf_bot.po",
"chars": 48447,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/cs_CZ/LC_MESSAGES/pdf_bot.po",
"chars": 48032,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/da_DK/LC_MESSAGES/pdf_bot.po",
"chars": 47950,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/de_DE/LC_MESSAGES/pdf_bot.po",
"chars": 49374,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/el_GR/LC_MESSAGES/pdf_bot.po",
"chars": 49168,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/en_GB/LC_MESSAGES/pdf_bot.po",
"chars": 41706,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/en_US/LC_MESSAGES/pdf_bot.po",
"chars": 45386,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/es_ES/LC_MESSAGES/pdf_bot.po",
"chars": 48540,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/fa_IR/LC_MESSAGES/pdf_bot.po",
"chars": 48224,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/fi_FI/LC_MESSAGES/pdf_bot.po",
"chars": 48103,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/fr_FR/LC_MESSAGES/pdf_bot.po",
"chars": 49110,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/he_IL/LC_MESSAGES/pdf_bot.po",
"chars": 46654,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/hi_IN/LC_MESSAGES/pdf_bot.po",
"chars": 48483,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/hu_HU/LC_MESSAGES/pdf_bot.po",
"chars": 48434,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/id_ID/LC_MESSAGES/pdf_bot.po",
"chars": 48506,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/it_IT/LC_MESSAGES/pdf_bot.po",
"chars": 48250,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/ja_JP/LC_MESSAGES/pdf_bot.po",
"chars": 45182,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/ko_KR/LC_MESSAGES/pdf_bot.po",
"chars": 44988,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/ky_KG/LC_MESSAGES/pdf_bot.po",
"chars": 48467,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/ms_MY/LC_MESSAGES/pdf_bot.po",
"chars": 48541,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/nl_NL/LC_MESSAGES/pdf_bot.po",
"chars": 48763,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/no_NO/LC_MESSAGES/pdf_bot.po",
"chars": 47724,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/om_ET/LC_MESSAGES/pdf_bot.po",
"chars": 41765,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/pl_PL/LC_MESSAGES/pdf_bot.po",
"chars": 48241,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/pt_BR/LC_MESSAGES/pdf_bot.po",
"chars": 48598,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/pt_PT/LC_MESSAGES/pdf_bot.po",
"chars": 48612,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/ro_RO/LC_MESSAGES/pdf_bot.po",
"chars": 48714,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/ru_RU/LC_MESSAGES/pdf_bot.po",
"chars": 48761,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/si_LK/LC_MESSAGES/pdf_bot.po",
"chars": 45223,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/sr_SP/LC_MESSAGES/pdf_bot.po",
"chars": 48237,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/sv_SE/LC_MESSAGES/pdf_bot.po",
"chars": 48193,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/ta_IN/LC_MESSAGES/pdf_bot.po",
"chars": 49581,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/tr_TR/LC_MESSAGES/pdf_bot.po",
"chars": 48371,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/uk_UA/LC_MESSAGES/pdf_bot.po",
"chars": 48551,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/uz_UZ/LC_MESSAGES/pdf_bot.po",
"chars": 48800,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/vi_VN/LC_MESSAGES/pdf_bot.po",
"chars": 48110,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/zh_CN/LC_MESSAGES/pdf_bot.po",
"chars": 44143,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/zh_HK/LC_MESSAGES/pdf_bot.po",
"chars": 43196,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "locale/zh_TW/LC_MESSAGES/pdf_bot.po",
"chars": 44075,
"preview": "# locale translations for telegram-pdf-bot.\n# Copyright (C) 2021 zeshuaro\n# This file is distributed under the same lice"
},
{
"path": "pdf_bot/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "pdf_bot/__main__.py",
"chars": 2531,
"preview": "from typing import Any\n\nimport sentry_sdk\nfrom dependency_injector.providers import Singleton\nfrom dependency_injector.w"
},
{
"path": "pdf_bot/account/__init__.py",
"chars": 145,
"preview": "from .account_repository import AccountRepository\nfrom .account_service import AccountService\n\n__all__ = [\"AccountReposi"
},
{
"path": "pdf_bot/account/account_repository.py",
"chars": 865,
"preview": "from google.cloud.datastore import Client, Entity\n\nfrom pdf_bot.consts import LANGUAGE, USER\n\n\nclass AccountRepository:\n"
},
{
"path": "pdf_bot/account/account_service.py",
"chars": 861,
"preview": "from telegram import User\n\nfrom pdf_bot.account.account_repository import AccountRepository\nfrom pdf_bot.language import"
},
{
"path": "pdf_bot/analytics/__init__.py",
"chars": 226,
"preview": "from .analytics_repository import AnalyticsRepository\nfrom .analytics_service import AnalyticsService\nfrom .models impor"
},
{
"path": "pdf_bot/analytics/analytics_repository.py",
"chars": 906,
"preview": "from typing import Any\n\nfrom requests import Session\n\nfrom pdf_bot.settings import Settings\n\n\nclass AnalyticsRepository:"
},
{
"path": "pdf_bot/analytics/analytics_service.py",
"chars": 1440,
"preview": "from typing import cast\nfrom uuid import UUID\n\nfrom loguru import logger\nfrom requests.exceptions import HTTPError\nfrom "
},
{
"path": "pdf_bot/analytics/models.py",
"chars": 765,
"preview": "from enum import Enum\n\n\nclass TaskType(Enum):\n beautify_image = \"beautify_image\"\n compare_pdf = \"compare_pdf\"\n "
},
{
"path": "pdf_bot/cli/__init__.py",
"chars": 121,
"preview": "from .cli_service import CLIService\nfrom .exceptions import CLIServiceError\n\n__all__ = [\"CLIService\", \"CLIServiceError\"]"
},
{
"path": "pdf_bot/cli/cli_service.py",
"chars": 1247,
"preview": "import shlex\nfrom gettext import gettext as _\nfrom pathlib import Path\nfrom subprocess import PIPE, Popen\n\nfrom loguru i"
},
{
"path": "pdf_bot/cli/exceptions.py",
"chars": 104,
"preview": "class CLIServiceError(Exception):\n pass\n\n\nclass CLINonZeroExitStatusError(CLIServiceError):\n pass\n"
},
{
"path": "pdf_bot/command/__init__.py",
"chars": 140,
"preview": "from .command_handler import MyCommandHandler\nfrom .command_service import CommandService\n\n__all__ = [\"CommandService\", "
},
{
"path": "pdf_bot/command/command_handler.py",
"chars": 948,
"preview": "from telegram.ext import BaseHandler, CommandHandler, filters\n\nfrom pdf_bot.telegram_handler import AbstractTelegramHand"
},
{
"path": "pdf_bot/command/command_service.py",
"chars": 4735,
"preview": "from typing import cast\n\nfrom telegram import InlineKeyboardButton, InlineKeyboardMarkup, Message, Update, User\nfrom tel"
},
{
"path": "pdf_bot/compare/__init__.py",
"chars": 136,
"preview": "from .compare_handler import CompareHandler\nfrom .compare_service import CompareService\n\n__all__ = [\"CompareHandler\", \"C"
},
{
"path": "pdf_bot/compare/compare_handler.py",
"chars": 1502,
"preview": "from telegram.ext import BaseHandler, CommandHandler, ConversationHandler, MessageHandler, filters\n\nfrom pdf_bot.consts "
},
{
"path": "pdf_bot/compare/compare_service.py",
"chars": 3991,
"preview": "from typing import cast\n\nfrom pdf_diff import NoDifferenceError\nfrom telegram import Message, ReplyKeyboardMarkup, Reply"
},
{
"path": "pdf_bot/consts.py",
"chars": 453,
"preview": "from gettext import gettext as _\n\nfrom telegram.ext import filters\n\nTEXT_FILTER = filters.TEXT & ~filters.COMMAND\n\n# Bot"
},
{
"path": "pdf_bot/containers.py",
"chars": 13066,
"preview": "from dependency_injector import containers, providers\nfrom requests import Session\nfrom slack_sdk import WebClient as Sl"
},
{
"path": "pdf_bot/datastore/__init__.py",
"chars": 81,
"preview": "from .datastore_client import MyDatastoreClient\n\n__all__ = [\"MyDatastoreClient\"]\n"
},
{
"path": "pdf_bot/datastore/datastore_client.py",
"chars": 323,
"preview": "from google.cloud.datastore import Client\nfrom google.oauth2.service_account import Credentials\n\n\nclass MyDatastoreClien"
},
{
"path": "pdf_bot/error/__init__.py",
"chars": 221,
"preview": "from .error_callback_query_handler import ErrorCallbackQueryHandler\nfrom .error_handler import ErrorHandler\nfrom .error_"
},
{
"path": "pdf_bot/error/error_callback_query_handler.py",
"chars": 475,
"preview": "from telegram.ext import BaseHandler, CallbackQueryHandler\n\nfrom pdf_bot.telegram_handler import AbstractTelegramHandler"
},
{
"path": "pdf_bot/error/error_handler.py",
"chars": 2902,
"preview": "from contextlib import suppress\nfrom gettext import gettext as _\n\nimport sentry_sdk\nfrom loguru import logger\nfrom teleg"
},
{
"path": "pdf_bot/error/error_service.py",
"chars": 770,
"preview": "from typing import cast\n\nfrom telegram import CallbackQuery, Message, Update\nfrom telegram.ext import ContextTypes\n\nfrom"
},
{
"path": "pdf_bot/errors.py",
"chars": 509,
"preview": "from typing import Any\n\nfrom pdf_bot.models import FileData\n\n\nclass FileDataTypeError(Exception):\n def __init__(self,"
},
{
"path": "pdf_bot/feedback/__init__.py",
"chars": 216,
"preview": "from .feedback_handler import FeedbackHandler\nfrom .feedback_repository import FeedbackRepository\nfrom .feedback_service"
},
{
"path": "pdf_bot/feedback/feedback_handler.py",
"chars": 1207,
"preview": "from telegram.ext import BaseHandler, CommandHandler, ConversationHandler, MessageHandler\n\nfrom pdf_bot.consts import TE"
},
{
"path": "pdf_bot/feedback/feedback_repository.py",
"chars": 709,
"preview": "from loguru import logger\nfrom sentry_sdk import capture_exception\nfrom slack_sdk import WebClient\nfrom slack_sdk.errors"
},
{
"path": "pdf_bot/feedback/feedback_service.py",
"chars": 2315,
"preview": "from typing import cast\n\nfrom langdetect import detect\nfrom telegram import Message, Update, User\nfrom telegram.ext impo"
},
{
"path": "pdf_bot/file/__init__.py",
"chars": 118,
"preview": "from .file_handler import FileHandler\nfrom .file_service import FileService\n\n__all__ = [\"FileHandler\", \"FileService\"]\n"
},
{
"path": "pdf_bot/file/file_handler.py",
"chars": 1491,
"preview": "from telegram.ext import (\n BaseHandler,\n CallbackQueryHandler,\n CommandHandler,\n ConversationHandler,\n M"
},
{
"path": "pdf_bot/file/file_service.py",
"chars": 2513,
"preview": "from typing import cast\n\nfrom telegram import Message, Update\nfrom telegram.constants import FileSizeLimit\nfrom telegram"
},
{
"path": "pdf_bot/file_processor/__init__.py",
"chars": 310,
"preview": "from .abstract_file_processor import AbstractFileProcessor, ErrorHandlerType\nfrom .abstract_file_task_processor import A"
},
{
"path": "pdf_bot/file_processor/abstract_file_processor.py",
"chars": 6501,
"preview": "import shutil\nfrom abc import ABC, abstractmethod\nfrom collections.abc import AsyncGenerator, Callable, Coroutine, Seque"
},
{
"path": "pdf_bot/file_processor/abstract_file_task_processor.py",
"chars": 804,
"preview": "from abc import ABC, abstractmethod\n\nfrom telegram import Update\nfrom telegram.ext import CallbackContext\n\nfrom pdf_bot."
},
{
"path": "pdf_bot/file_processor/errors.py",
"chars": 201,
"preview": "class DuplicateClassError(Exception):\n def __init__(self, cls_name: str, *args: object) -> None:\n msg = f\"Clas"
},
{
"path": "pdf_bot/file_processor/file_task_mixin.py",
"chars": 2403,
"preview": "from collections.abc import Sequence\nfrom typing import cast\n\nfrom telegram import (\n Document,\n InlineKeyboardBut"
},
{
"path": "pdf_bot/image/__init__.py",
"chars": 68,
"preview": "from .image_service import ImageService\n\n__all__ = [\"ImageService\"]\n"
},
{
"path": "pdf_bot/image/image_service.py",
"chars": 1908,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom pathlib import Path\n\nimport i"
},
{
"path": "pdf_bot/image_handler/__init__.py",
"chars": 156,
"preview": "from .batch_image_handler import BatchImageHandler\nfrom .batch_image_service import BatchImageService\n\n__all__ = [\"Batch"
},
{
"path": "pdf_bot/image_handler/batch_image_handler.py",
"chars": 1437,
"preview": "from telegram.ext import BaseHandler, CommandHandler, ConversationHandler, MessageHandler, filters\n\nfrom pdf_bot.consts "
},
{
"path": "pdf_bot/image_handler/batch_image_service.py",
"chars": 7753,
"preview": "from gettext import gettext as _\nfrom typing import cast\n\nfrom telegram import Document, Message, PhotoSize, ReplyKeyboa"
},
{
"path": "pdf_bot/image_processor/__init__.py",
"chars": 441,
"preview": "from .abstract_image_processor import AbstractImageProcessor\nfrom .beautify_image_processor import BeautifyImageData, Be"
},
{
"path": "pdf_bot/image_processor/abstract_image_processor.py",
"chars": 1138,
"preview": "from typing import ClassVar\n\nfrom pdf_bot.file_processor import AbstractFileProcessor\nfrom pdf_bot.file_processor.errors"
},
{
"path": "pdf_bot/image_processor/beautify_image_processor.py",
"chars": 1036,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom gettext import gettext as _\n\n"
},
{
"path": "pdf_bot/image_processor/image_task_processor.py",
"chars": 292,
"preview": "from pdf_bot.file_processor import AbstractFileTaskProcessor\n\nfrom .abstract_image_processor import AbstractImageProcess"
},
{
"path": "pdf_bot/image_processor/image_to_pdf_processor.py",
"chars": 1007,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom gettext import gettext as _\n\n"
},
{
"path": "pdf_bot/io_internal/__init__.py",
"chars": 59,
"preview": "from .io_service import IOService\n\n__all__ = [\"IOService\"]\n"
},
{
"path": "pdf_bot/io_internal/io_service.py",
"chars": 1924,
"preview": "from collections.abc import Generator\nfrom contextlib import contextmanager\nfrom pathlib import Path\nfrom tempfile impor"
},
{
"path": "pdf_bot/language/__init__.py",
"chars": 324,
"preview": "from .language_handler import LanguageHandler\nfrom .language_repository import LanguageRepository\nfrom .language_service"
},
{
"path": "pdf_bot/language/language_handler.py",
"chars": 861,
"preview": "from telegram.ext import BaseHandler, CallbackQueryHandler, CommandHandler\n\nfrom pdf_bot.telegram_handler import Abstrac"
},
{
"path": "pdf_bot/language/language_repository.py",
"chars": 1116,
"preview": "from google.cloud.datastore import Client, Entity\n\nfrom pdf_bot.consts import LANGUAGE, USER\n\n\nclass LanguageRepository:"
},
{
"path": "pdf_bot/language/language_service.py",
"chars": 7091,
"preview": "import gettext\nfrom collections.abc import Callable\nfrom contextlib import suppress\nfrom typing import cast\n\nfrom telegr"
},
{
"path": "pdf_bot/language/models.py",
"chars": 224,
"preview": "from pydantic import BaseModel\n\n\nclass SetLanguageData:\n pass\n\n\nclass LanguageData(BaseModel):\n label: str\n lon"
},
{
"path": "pdf_bot/log/__init__.py",
"chars": 118,
"preview": "from .log_handler import InterceptLoggingHandler, MyLogHandler\n\n__all__ = [\"InterceptLoggingHandler\", \"MyLogHandler\"]\n"
},
{
"path": "pdf_bot/log/log_handler.py",
"chars": 1462,
"preview": "import logging\nimport sys\n\nfrom loguru import logger\nfrom sentry_sdk.integrations.logging import ignore_logger\n\n\nclass I"
},
{
"path": "pdf_bot/merge/__init__.py",
"chars": 124,
"preview": "from .merge_handler import MergeHandler\nfrom .merge_service import MergeService\n\n__all__ = [\"MergeHandler\", \"MergeServic"
},
{
"path": "pdf_bot/merge/merge_handler.py",
"chars": 1261,
"preview": "from telegram.ext import BaseHandler, CommandHandler, ConversationHandler, MessageHandler, filters\n\nfrom pdf_bot.consts "
},
{
"path": "pdf_bot/merge/merge_service.py",
"chars": 7135,
"preview": "from gettext import gettext as _\nfrom typing import cast\n\nfrom telegram import Document, Message, ReplyKeyboardMarkup, R"
},
{
"path": "pdf_bot/models.py",
"chars": 943,
"preview": "from dataclasses import dataclass\nfrom pathlib import Path\n\nfrom telegram import Document, Message, PhotoSize\n\n\nclass Ba"
},
{
"path": "pdf_bot/payment/__init__.py",
"chars": 183,
"preview": "from .models import PaymentData\nfrom .payment_handler import PaymentHandler\nfrom .payment_service import PaymentService\n"
},
{
"path": "pdf_bot/payment/models.py",
"chars": 108,
"preview": "from pydantic import BaseModel\n\n\nclass PaymentData(BaseModel):\n label: str\n emoji: str\n value: int\n"
},
{
"path": "pdf_bot/payment/payment_handler.py",
"chars": 1280,
"preview": "from telegram.ext import (\n BaseHandler,\n CallbackQueryHandler,\n CommandHandler,\n MessageHandler,\n PreChe"
},
{
"path": "pdf_bot/payment/payment_service.py",
"chars": 4497,
"preview": "from gettext import gettext as _\nfrom typing import cast\n\nfrom telegram import (\n CallbackQuery,\n InlineKeyboardBu"
},
{
"path": "pdf_bot/pdf/__init__.py",
"chars": 575,
"preview": "from .exceptions import (\n PdfDecryptError,\n PdfEncryptedError,\n PdfIncorrectPasswordError,\n PdfNoTextError,"
},
{
"path": "pdf_bot/pdf/exceptions.py",
"chars": 587,
"preview": "from gettext import gettext as _\n\n\nclass PdfServiceError(Exception):\n pass\n\n\nclass PdfReadError(PdfServiceError):\n "
},
{
"path": "pdf_bot/pdf/models.py",
"chars": 1018,
"preview": "from dataclasses import dataclass\nfrom pathlib import Path\n\nimport humanize\n\n\n@dataclass\nclass CompressResult:\n old_s"
},
{
"path": "pdf_bot/pdf/pdf_service.py",
"chars": 13656,
"preview": "import asyncio\nimport shutil\nimport textwrap\nfrom collections.abc import AsyncGenerator, Generator\nfrom contextlib impor"
},
{
"path": "pdf_bot/pdf_processor/__init__.py",
"chars": 2532,
"preview": "from .abstract_pdf_processor import AbstractPdfProcessor\nfrom .abstract_pdf_select_and_text_processor import (\n Abstr"
},
{
"path": "pdf_bot/pdf_processor/abstract_pdf_processor.py",
"chars": 1238,
"preview": "from typing import ClassVar\n\nfrom pdf_bot.file_processor import AbstractFileProcessor\nfrom pdf_bot.file_processor.errors"
},
{
"path": "pdf_bot/pdf_processor/abstract_pdf_select_and_text_processor.py",
"chars": 6667,
"preview": "from abc import abstractmethod\nfrom dataclasses import dataclass\nfrom enum import Enum\nfrom typing import Any, cast\n\nfro"
},
{
"path": "pdf_bot/pdf_processor/abstract_pdf_text_input_processor.py",
"chars": 3620,
"preview": "from abc import abstractmethod\nfrom collections.abc import Callable\nfrom dataclasses import dataclass\nfrom typing import"
},
{
"path": "pdf_bot/pdf_processor/compress_pdf_processor.py",
"chars": 1325,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom gettext import gettext as _\n\n"
},
{
"path": "pdf_bot/pdf_processor/crop_pdf_processor.py",
"chars": 3030,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom gettext import gettext as _\n\n"
},
{
"path": "pdf_bot/pdf_processor/decrypt_pdf_processor.py",
"chars": 2317,
"preview": "from collections.abc import AsyncGenerator, Callable\nfrom contextlib import asynccontextmanager\nfrom gettext import gett"
},
{
"path": "pdf_bot/pdf_processor/encrypt_pdf_processor.py",
"chars": 1471,
"preview": "from collections.abc import AsyncGenerator, Callable\nfrom contextlib import asynccontextmanager\nfrom gettext import gett"
},
{
"path": "pdf_bot/pdf_processor/extract_pdf_image_processor.py",
"chars": 1026,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom gettext import gettext as _\n\n"
},
{
"path": "pdf_bot/pdf_processor/extract_pdf_text_processor.py",
"chars": 1017,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom gettext import gettext as _\n\n"
},
{
"path": "pdf_bot/pdf_processor/grayscale_pdf_processor.py",
"chars": 1004,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom gettext import gettext as _\n\n"
},
{
"path": "pdf_bot/pdf_processor/ocr_pdf_processor.py",
"chars": 926,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\n\nfrom telegram.ext import Callback"
},
{
"path": "pdf_bot/pdf_processor/pdf_task_processor.py",
"chars": 282,
"preview": "from pdf_bot.file_processor import AbstractFileTaskProcessor\n\nfrom .abstract_pdf_processor import AbstractPdfProcessor\n\n"
},
{
"path": "pdf_bot/pdf_processor/pdf_to_image_processor.py",
"chars": 1003,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom gettext import gettext as _\n\n"
},
{
"path": "pdf_bot/pdf_processor/preview_pdf_processor.py",
"chars": 990,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom gettext import gettext as _\n\n"
},
{
"path": "pdf_bot/pdf_processor/rename_pdf_processor.py",
"chars": 1840,
"preview": "import re\nfrom collections.abc import AsyncGenerator, Callable\nfrom contextlib import asynccontextmanager\nfrom gettext i"
},
{
"path": "pdf_bot/pdf_processor/rotate_pdf_processor.py",
"chars": 3657,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom dataclasses import dataclass\n"
},
{
"path": "pdf_bot/pdf_processor/scale_pdf_processor.py",
"chars": 2991,
"preview": "from collections.abc import AsyncGenerator\nfrom contextlib import asynccontextmanager\nfrom gettext import gettext as _\n\n"
},
{
"path": "pdf_bot/pdf_processor/split_pdf_processor.py",
"chars": 3738,
"preview": "from collections.abc import AsyncGenerator, Callable\nfrom contextlib import asynccontextmanager\nfrom gettext import gett"
},
{
"path": "pdf_bot/settings.py",
"chars": 950,
"preview": "from pathlib import Path\n\nfrom pydantic import Field\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n\n\ncl"
},
{
"path": "pdf_bot/telegram_handler/__init__.py",
"chars": 102,
"preview": "from .abstract_telegram_handler import AbstractTelegramHandler\n\n__all__ = [\"AbstractTelegramHandler\"]\n"
},
{
"path": "pdf_bot/telegram_handler/abstract_telegram_handler.py",
"chars": 204,
"preview": "from abc import ABC, abstractmethod\n\nfrom telegram.ext import BaseHandler\n\n\nclass AbstractTelegramHandler(ABC):\n @pro"
},
{
"path": "pdf_bot/telegram_internal/__init__.py",
"chars": 516,
"preview": "from .exceptions import (\n TelegramFileMimeTypeError,\n TelegramFileTooLargeError,\n TelegramGetUserDataError,\n "
},
{
"path": "pdf_bot/telegram_internal/exceptions.py",
"chars": 380,
"preview": "class TelegramServiceError(Exception):\n pass\n\n\nclass TelegramFileMimeTypeError(TelegramServiceError):\n pass\n\n\nclas"
},
{
"path": "pdf_bot/telegram_internal/telegram_service.py",
"chars": 12211,
"preview": "from collections.abc import AsyncGenerator, Coroutine\nfrom contextlib import asynccontextmanager, suppress\nfrom gettext "
},
{
"path": "pdf_bot/text/__init__.py",
"chars": 180,
"preview": "from .text_handler import TextHandler\nfrom .text_repository import TextRepository\nfrom .text_service import TextService\n"
},
{
"path": "pdf_bot/text/text_handler.py",
"chars": 1293,
"preview": "from telegram.ext import BaseHandler, CommandHandler, ConversationHandler, MessageHandler\n\nfrom pdf_bot.consts import TE"
},
{
"path": "pdf_bot/text/text_repository.py",
"chars": 805,
"preview": "from requests import Session\n\nfrom pdf_bot.pdf import FontData\n\n\nclass TextRepository:\n def __init__(self, api_client"
},
{
"path": "pdf_bot/text/text_service.py",
"chars": 4265,
"preview": "from gettext import gettext as _\nfrom typing import cast\n\nfrom telegram import Message, ReplyKeyboardMarkup, ReplyKeyboa"
},
{
"path": "pdf_bot/watermark/__init__.py",
"chars": 148,
"preview": "from .watermark_handler import WatermarkHandler\nfrom .watermark_service import WatermarkService\n\n__all__ = [\"WatermarkHa"
},
{
"path": "pdf_bot/watermark/watermark_handler.py",
"chars": 1728,
"preview": "from telegram.ext import BaseHandler, CommandHandler, ConversationHandler, MessageHandler, filters\n\nfrom pdf_bot.consts "
},
{
"path": "pdf_bot/watermark/watermark_service.py",
"chars": 3704,
"preview": "from typing import cast\n\nfrom telegram import Message, ReplyKeyboardMarkup, ReplyKeyboardRemove, Update\nfrom telegram.ex"
},
{
"path": "pdf_bot/webpage/__init__.py",
"chars": 136,
"preview": "from .webpage_handler import WebpageHandler\nfrom .webpage_service import WebpageService\n\n__all__ = [\"WebpageHandler\", \"W"
},
{
"path": "pdf_bot/webpage/webpage_handler.py",
"chars": 525,
"preview": "from telegram import MessageEntity\nfrom telegram.ext import BaseHandler, MessageHandler, filters\n\nfrom pdf_bot.telegram_"
},
{
"path": "pdf_bot/webpage/webpage_service.py",
"chars": 3184,
"preview": "import hashlib\nfrom contextlib import suppress\nfrom typing import cast\nfrom urllib.parse import urlparse\n\nfrom telegram "
},
{
"path": "pyproject.toml",
"chars": 3405,
"preview": "[tool.poetry]\nname = \"pdf-bot\"\nversion = \"1.0.0\"\ndescription = \"Telegram bot that can do a lot of things related to PDF "
},
{
"path": "renovate.json",
"chars": 219,
"preview": "{\n \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n \"extends\": [\n \"github>zeshuaro/renovate-configs"
},
{
"path": "tests/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/account/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/account/test_account_repository.py",
"chars": 2171,
"preview": "from unittest.mock import MagicMock, patch\n\nfrom google.cloud.datastore import Client, Entity, Key\n\nfrom pdf_bot.account"
},
{
"path": "tests/account/test_account_service.py",
"chars": 1632,
"preview": "from unittest.mock import MagicMock\n\nfrom telegram import User\n\nfrom pdf_bot.account import AccountRepository, AccountSe"
},
{
"path": "tests/analytics/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/analytics/test_analytics_repository.py",
"chars": 880,
"preview": "from typing import ClassVar\nfrom unittest.mock import MagicMock\n\nfrom requests import Session\n\nfrom pdf_bot.analytics im"
},
{
"path": "tests/analytics/test_analytics_service.py",
"chars": 2107,
"preview": "from unittest.mock import MagicMock, patch\nfrom uuid import UUID\n\nfrom requests import HTTPError\n\nfrom pdf_bot.analytics"
},
{
"path": "tests/cli/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/cli/test_cli_service.py",
"chars": 2858,
"preview": "import shlex\nfrom subprocess import Popen\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\n\nfrom pdf_bot.cli im"
},
{
"path": "tests/command/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/command/test_command_handler.py",
"chars": 1506,
"preview": "from unittest.mock import MagicMock\n\nimport pytest\nfrom telegram.ext import CommandHandler, filters\n\nfrom pdf_bot.comman"
},
{
"path": "tests/command/test_command_service.py",
"chars": 2762,
"preview": "from unittest.mock import ANY, MagicMock\n\nimport pytest\nfrom telegram.constants import ParseMode\nfrom telegram.error imp"
},
{
"path": "tests/compare/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/compare/test_compare_handler.py",
"chars": 2524,
"preview": "from unittest.mock import MagicMock\n\nimport pytest\nfrom telegram.ext import CommandHandler, ConversationHandler, Message"
},
{
"path": "tests/compare/test_compare_service.py",
"chars": 5190,
"preview": "from unittest.mock import MagicMock\n\nimport pytest\nfrom pdf_diff import NoDifferenceError\nfrom telegram.ext import Conve"
},
{
"path": "tests/conftest.py",
"chars": 437,
"preview": "import pytest\n\nfrom pdf_bot.file_processor import AbstractFileProcessor\nfrom pdf_bot.image_processor import AbstractImag"
},
{
"path": "tests/datastore/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/datastore/test_datastore_client.py",
"chars": 775,
"preview": "from unittest.mock import MagicMock, patch\n\nfrom google.oauth2.service_account import Credentials\n\nfrom pdf_bot.datastor"
},
{
"path": "tests/error/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/error/test_error_callback_query_handler.py",
"chars": 859,
"preview": "from unittest.mock import MagicMock\n\nimport pytest\nfrom telegram.ext import CallbackQueryHandler\n\nfrom pdf_bot.error imp"
},
{
"path": "tests/error/test_error_handler.py",
"chars": 5148,
"preview": "from unittest.mock import patch\n\nimport pytest\nfrom telegram.error import BadRequest, Forbidden\n\nfrom pdf_bot.error impo"
},
{
"path": "tests/error/test_error_service.py",
"chars": 750,
"preview": "import pytest\n\nfrom pdf_bot.error import ErrorService\nfrom tests.language import LanguageServiceTestMixin\nfrom tests.tel"
},
{
"path": "tests/feedback/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/feedback/test_feedback_handler.py",
"chars": 2039,
"preview": "from unittest.mock import MagicMock\n\nimport pytest\nfrom telegram.ext import CommandHandler, ConversationHandler, Message"
},
{
"path": "tests/feedback/test_feedback_repository.py",
"chars": 1582,
"preview": "from unittest.mock import MagicMock, patch\n\nfrom slack_sdk import WebClient\nfrom slack_sdk.errors import SlackApiError\n\n"
},
{
"path": "tests/feedback/test_feedback_service.py",
"chars": 2957,
"preview": "from unittest.mock import MagicMock, patch\n\nimport pytest\nfrom telegram.ext import ConversationHandler\n\nfrom pdf_bot.fee"
},
{
"path": "tests/file/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/file/test_file_handler.py",
"chars": 2056,
"preview": "import re\nfrom unittest.mock import MagicMock\n\nimport pytest\nfrom telegram.ext import (\n CallbackQueryHandler,\n Co"
},
{
"path": "tests/file/test_file_service.py",
"chars": 2663,
"preview": "from unittest.mock import MagicMock\n\nimport pytest\nfrom telegram.constants import FileSizeLimit\nfrom telegram.ext import"
},
{
"path": "tests/file_processor/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/file_processor/test_abstract_file_processor.py",
"chars": 13189,
"preview": "from collections.abc import AsyncGenerator, Sequence\nfrom contextlib import asynccontextmanager\nfrom pathlib import Path"
},
{
"path": "tests/file_processor/test_abstract_file_task_processor.py",
"chars": 1524,
"preview": "from unittest.mock import MagicMock, patch\n\nimport pytest\n\nfrom pdf_bot.file_processor import AbstractFileProcessor, Abs"
},
{
"path": "tests/file_processor/test_file_task_mixin.py",
"chars": 2825,
"preview": "from typing import TYPE_CHECKING\n\nimport pytest\nfrom telegram.ext import ConversationHandler\n\nfrom pdf_bot.consts import"
},
{
"path": "tests/image/__init__.py",
"chars": 0,
"preview": ""
}
]
// ... and 61 more files (download for full content)
About this extraction
This page contains the full source code of the zeshuaro/telegram-pdf-bot GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 261 files (2.4 MB), approximately 635.6k tokens, and a symbol index with 1067 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.