{ "name": "Telegram Drive Organizer", "description": "Telegram bot that downloads channel videos (whole channel or an /upload message range) into Google Drive topic folders.", "keywords": [ "telegram", "google-drive", "telethon", "video", "worker" ], "stack": "heroku-24", "buildpacks": [ { "url": "heroku/python" } ], "formation": { "worker": { "quantity": 1, "size": "basic" } }, "addons": [ { "plan": "heroku-postgresql:essential-0", "as": "DATABASE" } ], "env": { "TELEGRAM_MODE": { "description": "How the bot reads the channel: 'user' (your Telegram account; full history; needs TELEGRAM_SESSION_STRING) or 'bot' (bot admin of the channel; new posts, best-effort history).", "value": "user", "required": true }, "TELEGRAM_API_ID": { "description": "Numeric API ID from https://my.telegram.org > API development tools. Needed in both modes.", "required": true }, "TELEGRAM_API_HASH": { "description": "API hash from https://my.telegram.org > API development tools. Keep secret.", "required": true }, "TELEGRAM_BOT_TOKEN": { "description": "Bot token from @BotFather. Provides the admin command interface (/run, /status, ...); in bot mode it also reads the channel.", "required": true }, "TELEGRAM_SESSION_STRING": { "description": "Only for TELEGRAM_MODE=user: string session created locally with `python main.py login --session-string`. Grants access to your account - keep secret.", "required": false }, "ADMIN_USER_IDS": { "description": "Comma-separated numeric Telegram user IDs allowed to run commands (get yours from @userinfobot).", "required": true }, "SOURCE_CHANNEL_ID": { "description": "Source channel: @username, https://t.me/... link, or numeric -100... ID.", "required": true }, "GOOGLE_AUTH_MODE": { "description": "'oauth' for a personal Google Drive (uses GOOGLE_OAUTH_TOKEN_JSON) or 'service_account' for a Shared Drive (uses GOOGLE_SERVICE_ACCOUNT_JSON).", "value": "oauth", "required": true }, "GOOGLE_OAUTH_TOKEN_JSON": { "description": "Required when GOOGLE_AUTH_MODE=oauth: contents of credentials/token.json produced locally by `python main.py drive-auth` (raw JSON or base64).", "required": false }, "GOOGLE_SERVICE_ACCOUNT_JSON": { "description": "Required when GOOGLE_AUTH_MODE=service_account: the service-account key JSON (raw or base64). Service accounts only work with a Shared Drive they are a member of.", "required": false }, "GOOGLE_DRIVE_SCOPE": { "description": "'drive.file' (least privilege; the bot only sees folders it created) or 'drive' (needed to use a folder you created yourself). Must match the scope used during drive-auth.", "value": "drive.file", "required": false }, "GOOGLE_DRIVE_ROOT_FOLDER_ID": { "description": "ID of the Drive folder that will contain the topic folders (the part after /folders/ in its URL). Empty = top level of My Drive.", "required": false }, "DRIVE_SHARED_DRIVE_ID": { "description": "Shared Drive ID when the root folder lives in a Shared Drive (service-account setups).", "required": false }, "DRIVE_SHARE_MODE": { "description": "'none' keeps uploads private (default). 'anyone_with_link' makes every uploaded file readable by anyone with the link.", "value": "none", "required": false }, "PROCESS_MODE": { "description": "'existing' (channel history only), 'new' (new posts only) or 'both'.", "value": "both", "required": false }, "START_MESSAGE_ID": { "description": "First channel message ID to consider when scanning history.", "value": "1", "required": false }, "INCLUDE_DOCUMENT_VIDEOS": { "description": "Also process videos that were sent as files/documents.", "value": "true", "required": false }, "FILTER_TOPICS": { "description": "Optional comma-separated list of Topic values to process; others are recorded as SKIPPED.", "required": false }, "FILTER_BATCHES": { "description": "Optional comma-separated list of Batch values to process.", "required": false }, "BATCH_SUBFOLDERS_ENABLED": { "description": "true = Root/Batch/Topic folders; false = Root/Topic (default).", "value": "false", "required": false }, "DUPLICATE_POLICY": { "description": "When a different file already has the same name in the topic folder: suffix_number ('Name (2).mp4'), suffix_message_id ('Name [123].mp4'), skip, or fail. Files are never overwritten.", "value": "suffix_number", "required": false }, "MAX_RETRIES": { "description": "Automatic retries for a failing video before it is marked FAILED.", "value": "4", "required": false }, "RETRY_DELAY": { "description": "Base retry delay in seconds (doubles on each retry, capped by RETRY_MAX_DELAY).", "value": "30", "required": false }, "PROGRESS_INTERVAL": { "description": "Seconds between edits of the Telegram progress message.", "value": "10", "required": false }, "DOWNLOAD_DIR": { "description": "Temporary download directory on the dyno's ephemeral disk.", "value": "/tmp/telegram_drive_bot", "required": false }, "UPLOAD_CHUNK_SIZE": { "description": "Resumable upload chunk size (multiple of 256KB). Held in memory per chunk; 8MB suits 512MB dynos.", "value": "8MB", "required": false }, "MIN_FREE_DISK_SPACE": { "description": "Free disk space to keep in reserve; a download is not started if it would go below this.", "value": "512MB", "required": false }, "AUTO_START": { "description": "true = start processing at every boot. Otherwise processing starts with /run and then resumes automatically after restarts.", "value": "false", "required": false }, "LOG_LEVEL": { "description": "debug, info, warning or error.", "value": "info", "required": false }, "UPLOAD_RESTRICT_TO_SOURCE_CHANNEL": { "description": "true = /upload links must point to SOURCE_CHANNEL_ID; false = any channel the Telegram account or bot can access.", "value": "true", "required": false }, "UPLOAD_SETUP_TIMEOUT": { "description": "Minutes before an unfinished /upload setup (waiting for links or /confirm) expires.", "value": "30", "required": false }, "UPLOAD_MAX_RANGE": { "description": "Largest number of message IDs one /upload task may cover.", "value": "5000", "required": false }, "KEEP_FAILED_DOWNLOADS": { "description": "Keep local files of failed videos on the dyno disk (default false: delete; they are downloaded again on retry).", "value": "false", "required": false }, "TEMP_CLEANUP_INTERVAL": { "description": "Seconds between sweeps that delete abandoned temporary files.", "value": "3600", "required": false } } }