A private, guided Telegram bot for administering ArvanCloud CDN DNS records.
345
https://github.com/faridrasidov/arvancld-telegram
compose exmple
services:
bot:
image: faridrasidov/arvancld-telegram:latest
env_file:
- .env
environment:
ARVANCLD_SESSION_PATH: /app/data/arvancld-session.json
volumes:
- ./data:/app/data
restart: unless-stopped
env exmaple
# Telegram token created by @BotFather.
TELEGRAM_BOT_TOKEN=123456789:replace-with-bot-token
# Comma-separated numeric Telegram user IDs. Usernames are not accepted.
TELEGRAM_ADMIN_IDS=123456789
# One ArvanCloud account used by every allowed Telegram administrator.
[email protected]
ARVANCLD_PASSWORD=replace-with-account-password
# The Docker Compose file overrides this with /app/data/arvancld-session.json.
ARVANCLD_SESSION_PATH=data/arvancld-session.json
LOG_LEVEL=INFO
A private, guided Telegram bot for administering ArvanCloud CDN DNS records. It uses
pyTelegramBotAPI's asynchronous telebot.AsyncTeleBot and the typed
arvancld SDK.
The bot lists CDN domains and supports DNS record listing, exact-name search, type filters, creation, editing, cloud-proxy toggling, and deletion. Every DNS mutation has a single-use, five-minute confirmation screen.
Images are on Docker Hub as faridrasidov/arvancld-telegram.
compose.image.yaml pulls that image and prepares ./data for container UID 10001 before the
bot starts. You do not need to run mkdir or chown. Copy .env.example to .env, fill in
real values, then:
docker compose -f compose.image.yaml up -d
docker compose -f compose.image.yaml logs -f bot
Pin a release by changing both image tags from latest to the project version, for example
faridrasidov/arvancld-telegram:0.1.0. The session is stored in data/arvancld-session.json on
the host and must be protected like a password file.
Stop a failed startup loop with docker compose -f compose.image.yaml down. After the ArvanCloud
rate limit has cleared, start the service again and complete the login from Telegram with /auth.
The OTP prompt appears in Telegram, not in the terminal.
TELEGRAM_ADMIN_IDS are accepted./auth owns the
prompt for five minutes. The six-digit message is deleted immediately on a best-effort basis,
submitted once, and never stored or logged. Telegram delivery and deletion are not a substitute
for protecting the administrator's Telegram account and device.data/
directory private, backed up only through an encrypted mechanism, and never commit it.| Type | Guided value format |
|---|---|
A | One IPv4 address per line |
AAAA | One IPv6 address per line |
ANAME | location |
CNAME | host |
NS | host |
MX | host priority |
SRV | target port weight priority |
TXT | Full text content |
PTR | domain |
CAA | tag value |
TLSA | usage selector matching_type certificate |
Edits preserve ArvanCloud-specific fields that are not shown by the basic UI. For A and AAAA records, unchanged IP targets keep their existing port, weight, and country metadata. Changing a record type replaces incompatible value metadata and resets cloud mode to off.
Requires Python 3.10 or newer. Copy the example and fill in real values:
Copy-Item .env.example .env
TELEGRAM_BOT_TOKEN=123456789:bot-token-from-BotFather
TELEGRAM_ADMIN_IDS=123456789,987654321
[email protected]
ARVANCLD_PASSWORD=account-password
ARVANCLD_SESSION_PATH=data/arvancld-session.json
LOG_LEVEL=INFO
Create the bot token with @BotFather. Use numeric Telegram user IDs, not usernames; forwarding one of your messages to an ID-inspection bot is a common way to discover it. Treat third-party ID bots as untrusted and do not send them secrets.
The SDK dependency is pinned to the published and verified Git commit
19f8b49b993bbec935f7cd61bbb65ff9bcb1982f for reproducible installs, typed TOTP support, and
secret-safe API error diagnostics.
The bot supports ArvanCloud accounts protected by TOTP/2FA. The OTP is not entered in the
terminal or Docker stdin. When a login requires TOTP, the bot enters a restricted OTP required
state, starts Telegram polling, and asks an authorized administrator to run /auth in a private
chat. The six-digit code is accepted once, deleted on a best-effort basis, and never stored or
logged. After successful submission, the bot saves the authenticated session atomically and
validates CDN access before enabling DNS operations.
The authentication flow also provides:
/auth to claim or restart a challenge and /cancel to release an owned challenge.401 or 403; failed refresh falls back to one password login.
Operations are not silently resumed after an interactive authentication challenge.If the container repeatedly restarts during login, stop it and wait for the provider rate limit to
clear before trying again. A Docker restart loop can repeatedly submit password-login requests and
lead to HTTP 429 responses. Inspect one attempt at a time with docker compose logs bot and share
only the relevant safe diagnostic lines; never share .env, the session file, or raw provider
responses.
Create and activate a virtual environment, then install the project and development dependencies:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
python -m arvancld_telegram
The installed console command is equivalent:
arvancld-telegram
Install Python 3.10 or newer, open PowerShell in the repository directory, and run:
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -e ".[dev]"
.\.venv\Scripts\python.exe -m arvancld_telegram
When upgrading an existing virtual environment, recreate it or force-reinstall the pinned
arvancld archive. Multiple SDK commits currently identify as version 0.1.0, so pip may
otherwise retain an already-installed build.
The installed console command is equivalent:
.\.venv\Scripts\arvancld-telegram.exe
On startup the bot loads its saved session. Missing, invalid, or expired sessions trigger one
account login and an atomic session save. If that login requires TOTP, the bot starts polling in a
restricted OTP required state and best-effort notifies the configured administrator chat IDs.
Telegram may reject a notification until that user has opened the bot at least once; /auth is
always the fallback.
A later 401 or 403 triggers one token refresh; a rejected refresh triggers one password login.
If this fallback reaches TOTP, the active DNS operation stops and is never resumed automatically.
After authentication, repeat the read operation or rebuild and reconfirm the mutation. Login,
refresh, OTP submission, and DNS mutations are otherwise not retried.
compose.yaml builds from this repository. Create the bind-mounted directory before first launch.
On Linux, make it writable by container UID 10001; Windows Docker Desktop normally manages this
automatically.
mkdir -p data
sudo chown 10001:10001 data
docker compose build --no-cache --pull
docker compose up -d
docker compose logs -f bot
Install Docker Desktop with the WSL 2 backend, clone the repository, and open PowerShell in the
repository directory. Docker Desktop manages permissions for the bind mount in the usual case;
you do not need to run chown.
New-Item -ItemType Directory -Force data
docker compose build --no-cache --pull
docker compose up -d
docker compose logs -f bot
If Docker Desktop cannot access the repository or the data directory, move the project under a
path shared with Docker Desktop or enable file sharing for that drive. The session is stored in
data\arvancld-session.json on the host and must be protected like a password file.
On either platform, stop a failed startup loop before retrying authentication:
docker compose down
docker compose -f compose.image.yaml down
After the ArvanCloud rate limit has cleared, start the service again and complete the login from
Telegram with /auth. docker compose up -d is intentionally detached; the OTP prompt appears in
Telegram, not in the PowerShell window.
The multi-stage build clones the SDK repository at ARVANCLD_GIT_REF, verifies that
git rev-parse HEAD exactly matches the 40-character SHA, builds wheels, and installs only those
wheels in the non-root runtime image. Git and both source histories are absent from the final
image. Changing the SHA invalidates the SDK checkout/build layer. No container port is published
because Telegram updates are received with long polling.
Inspect the baked SDK revision without printing any environment secrets:
SDK_IMAGE=$(docker compose images -q bot)
docker image inspect "$SDK_IMAGE" \
--format '{{ index .Config.Labels "io.github.faridrasidov.arvancld.revision" }}'
docker compose logs bot | grep "arvancld sdk version="
The startup log reports the installed package version, exact SHA, module location, and whether
submit_totp() exists.
/start or /domains — list domains and open DNS administration./auth — claim, complete, or restart interactive ArvanCloud login./status — validate ArvanCloud access and show the domain count./help — show commands and record types./cancel — cancel the current input or confirmation.Domain and record pages contain eight items and provide previous/next navigation, refresh,
exact-name search, record-type filtering, and back actions. Protected provider records are visible
but read-only. Before updating, toggling, or deleting, the bot reloads the record and refuses the
change if its updated_at value no longer matches the selected snapshot.
When authentication is incomplete, /status reports OTP required, authenticating, or
unavailable without attempting a CDN request. /cancel releases an owned OTP prompt; the next
/auth starts a fresh password-login challenge. An OTP request with a network-uncertain result is
not retried and must also be restarted with /auth.
Set LOG_LEVEL=DEBUG in .env, rebuild, and reproduce once through the normal Telegram /auth
flow. Do not run a separate login command. The initial Telegram text “ArvanCloud OTP required” and
the totp_required log mean that the challenge started; they do not mean the submitted code was
rejected.
A successful attempt has this safe correlated sequence (other HTTP library debug lines may appear between stages):
event=password_login_started attempt_id=...
event=totp_required attempt_id=... challenge_revision=...
event=totp_claimed attempt_id=... challenge_revision=... actor_id=...
event=telegram_otp_received attempt_id=... challenge_revision=... actor_id=...
event=telegram_otp_deleted attempt_id=... challenge_revision=... actor_id=...
event=telegram_otp_format_accepted attempt_id=... challenge_revision=... actor_id=...
event=totp_sdk_submission_started attempt_id=... challenge_revision=... actor_id=...
event=totp_sdk_submission_accepted attempt_id=... challenge_revision=... actor_id=...
event=session_save_started attempt_id=... challenge_revision=...
event=session_saved attempt_id=... challenge_revision=...
event=cdn_validation_started attempt_id=... challenge_revision=...
event=cdn_validation_completed attempt_id=... challenge_revision=...
An API rejection logs totp_sdk_submission_rejected with HTTP status, request ID, and elapsed
time. At DEBUG it adds only content type, body byte count, top-level field names, and validation
field paths/types. A provider 200 followed by totp_sdk_submission_invalid_response identifies
a successful HTTP exchange whose response did not match the SDK contract. A deletion failure does
not stop submission. A 400, 401, 403, or 422 is submitted exactly once and keeps the
challenge usable; a network-uncertain or invalid-success-response outcome requires a fresh
/auth.
When asking for help, share only the relevant lines for one attempt_id and its provider request
ID. Do not share .env, the session file, Telegram message text, or raw provider bodies.
Automated validation never contacts Telegram or ArvanCloud:
.\.venv\Scripts\ruff.exe check .
.\.venv\Scripts\ruff.exe format --check .
.\.venv\Scripts\python.exe -m pytest
docker build --tag arvancld-telegram:test .
After deployment, perform read-only checks with /status, /domains, record pagination, search,
and filters. If live mutation validation is needed, create a clearly disposable DNS record, test
edit/cloud/delete on only that record, and verify the final state in the ArvanCloud panel.
The underlying SDK lists domains but does not implement domain creation, editing, or deletion. This bot therefore performs CRUD on DNS records only. Multi-account login, group use, webhooks, bulk zone import/export, DDNS scheduling, persistent wizard state, and localization are outside V1.
Content type
Image
Digest
sha256:5de67c466…
Size
59.8 MB
Last updated
8 days ago
docker pull faridrasidov/arvancld-telegram