diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml index 45d73f0..649300a 100644 --- a/.github/workflows/build-image.yml +++ b/.github/workflows/build-image.yml @@ -28,7 +28,7 @@ permissions: packages: write env: - # GHCR image, derived from the repo (e.g. ghcr.io/nikicat/tg-echo-service). + # GHCR image, derived from the repo (e.g. ghcr.io/nikicat/tg-echo). # Assumes the repo owner/name are lowercase, as GHCR requires. IMAGE: ghcr.io/${{ github.repository }} # Published tag: the git tag name on a tag push, otherwise "latest" (master). diff --git a/CMakeLists.txt b/CMakeLists.txt index c541731..132275b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) # ASM is needed to assemble openh264's aarch64 NEON .S files on arm64. openh264 # also ships x86 SIMD, but as NASM/YASM .asm (needs a separate assembler) which # this build doesn't wire up, so x86_64 falls back to the portable C++ paths. -project(call_service LANGUAGES C CXX ASM) +project(tg-echo LANGUAGES C CXX ASM) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -238,17 +238,17 @@ set(TD_INSTALL_DIR "${CMAKE_SOURCE_DIR}/td-install") list(APPEND CMAKE_PREFIX_PATH "${TD_INSTALL_DIR}") find_package(Td REQUIRED) -# -- Our call_service binary -- -add_executable(call_service main.cpp) +# -- Our tg-echo binary -- +add_executable(tg-echo main.cpp) -target_link_libraries(call_service PRIVATE +target_link_libraries(tg-echo PRIVATE tgcalls Td::TdStatic mp3lame yuv # libyuv: I420 scaling for incoming-video recording (brings its include dir) ) -target_include_directories(call_service PRIVATE +target_include_directories(tg-echo PRIVATE "${TGCALLS_DIR}" "${TGCALLS_DIR}/tgcalls" "${WEBRTC_DIR}" @@ -257,7 +257,7 @@ target_include_directories(call_service PRIVATE "${STUB_DIR}" ) -target_compile_definitions(call_service PRIVATE +target_compile_definitions(tg-echo PRIVATE WEBRTC_POSIX WEBRTC_LINUX NDEBUG @@ -267,7 +267,7 @@ target_compile_definitions(call_service PRIVATE # Arch-specific WebRTC defines, mirroring what BuildWebRTC.cmake sets on the # webrtc target so inline header code compiles the same way in this TU. if(ANDROID_ABI STREQUAL "x86_64") - target_compile_definitions(call_service PRIVATE HAVE_SSE2) + target_compile_definitions(tg-echo PRIVATE HAVE_SSE2) elseif(ANDROID_ABI STREQUAL "arm64-v8a") - target_compile_definitions(call_service PRIVATE WEBRTC_ARCH_ARM64 WEBRTC_HAS_NEON) + target_compile_definitions(tg-echo PRIVATE WEBRTC_ARCH_ARM64 WEBRTC_HAS_NEON) endif() diff --git a/Containerfile b/Containerfile index 57d0dbb..b2dc286 100644 --- a/Containerfile +++ b/Containerfile @@ -48,8 +48,8 @@ RUN cmake --build build -j$(nproc) --target tgcalls # App source (only this layer rebuilds on main.cpp changes) COPY main.cpp . -RUN cmake --build build -j$(nproc) --target call_service -RUN strip -s build/call_service +RUN cmake --build build -j$(nproc) --target tg-echo +RUN strip -s build/tg-echo # Collect the binary's shared-library closure (minus the glibc core the runtime # base already ships) into a flat dir. The runtime stage drops these into @@ -58,7 +58,7 @@ RUN strip -s build/call_service # would clobber, breaking the dynamic linker. This keeps the runtime minimal # without hand-listing version-suffixed Debian packages, identically on amd64/arm64. RUN mkdir -p /rootfs && \ - ldd build/call_service | awk '/=> \//{print $3}' | sort -u | \ + ldd build/tg-echo | awk '/=> \//{print $3}' | sort -u | \ grep -vE '/(ld-linux.*|libc|libm|libdl|libpthread|librt|libresolv|libgcc_s)\.so' | \ xargs -I{} cp -L {} /rootfs/ @@ -76,6 +76,6 @@ WORKDIR /app # libs (and their soname links) without touching system lib dirs. COPY --from=builder /rootfs/ /usr/local/lib/ RUN ldconfig -COPY --from=builder /src/build/call_service . +COPY --from=builder /src/build/tg-echo . -ENTRYPOINT ["./call_service"] +ENTRYPOINT ["./tg-echo"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f3178ed --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Nikolay Bryskin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile index 57a7a9e..a0ad121 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ else endif export CMAKE_BUILD_PARALLEL_LEVEL -IMAGE ?= docker.io/nikicat/tg-echo-service +IMAGE ?= docker.io/nikicat/tg-echo # Extra flags for the main image build. CI injects layer-cache flags here # (e.g. --layers --cache-from/--cache-to ); empty for local builds. @@ -45,10 +45,10 @@ configure: build/Makefile build/Makefile: CMakeLists.txt tdlib cmake -B build -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -# Build call_service binary -build: build/call_service +# Build tg-echo binary +build: build/tg-echo -build/call_service: build/Makefile main.cpp +build/tg-echo: build/Makefile main.cpp cmake --build build -j$(CMAKE_BUILD_PARALLEL_LEVEL) # Generate a test prompt (440Hz beeps, MP3). Override count: make prompt BEEPS=2 @@ -73,12 +73,12 @@ glados-prompt: # Run the service (requires API_ID and API_HASH env vars) run: build prompt mkdir -p recordings - ./build/call_service + ./build/tg-echo # Run the already-built binary, skipping the build/prompt prerequisites run-only: mkdir -p recordings - ./build/call_service + ./build/tg-echo image-tools: podman build --build-arg BASE_IMAGE=$(BASE_IMAGE) -t $(IMAGE)-tools:$(TAG) -f Containerfile.tools . diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..c18e995 --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,28 @@ +# Third-party licenses + +The original source in this repository (`main.cpp`, `video_platform.cpp`, the +`stub/` helpers, and the build glue) is licensed under the MIT License — see +[LICENSE](LICENSE). + +It is built against, and statically links, several vendored dependencies +(under `vendor/`) that carry their own licenses. The most important one is +**tgcalls (LGPL v3)**: because the resulting binary links it, any binary you +distribute must remain relinkable against a modified tgcalls, per the LGPL. +If you only run the service yourself, this does not apply. + +| Dependency | Upstream | License | +|------------|----------|---------| +| tgcalls | [TGX-Android/tgcalls](https://github.com/TGX-Android/tgcalls) | LGPL v3 | +| WebRTC | [TGX-Android/webrtc](https://github.com/TGX-Android/webrtc) | BSD-3-Clause | +| TDLib | [tdlib/td](https://github.com/tdlib/td) | Boost Software License 1.0 | +| abseil-cpp | [abseil/abseil-cpp](https://github.com/abseil/abseil-cpp) | Apache-2.0 | +| libsrtp | [cisco/libsrtp](https://github.com/cisco/libsrtp) | BSD-3-Clause | +| usrsctp | [sctplab/usrsctp](https://github.com/sctplab/usrsctp) | BSD-3-Clause | +| openh264 | [cisco/openh264](https://github.com/cisco/openh264) | BSD-2-Clause | +| rnnoise | [xiph/rnnoise](https://github.com/xiph/rnnoise) | BSD-3-Clause | +| crc32c | [google/crc32c](https://github.com/google/crc32c) | BSD-3-Clause | +| libevent | [TGX-Android/libevent](https://github.com/TGX-Android/libevent) | BSD-3-Clause | +| libyuv | [chromium/libyuv](https://chromium.googlesource.com/libyuv/libyuv) | BSD-3-Clause | + +Each dependency's full license text is available in its respective directory +under `vendor/`. diff --git a/README.md b/README.md index c895446..6a2d923 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,32 @@ -# tg-call-service +# Telegram Echo -Pure C++ service that accepts incoming Telegram voice calls, plays a PCM audio prompt, echoes the caller's audio back with a configurable delay, records to MP3, and sends the recording as a voice message after the call ends. +> A self-hosted echo-test service for Telegram voice & video calls — call it to verify that your calls actually connect and that audio gets through your network. -Uses [TDLib](https://github.com/tdlib/td) for Telegram signaling and [tgcalls](https://github.com/TGX-Android/tgcalls) for WebRTC audio. +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Container image](https://img.shields.io/badge/image-docker.io%2Fnikicat%2Ftg--echo-2496ed.svg)](https://hub.docker.com/r/nikicat/tg-echo) -## Architecture +Think of it as Telegram's own version of Skype's old "Echo / Sound Test Service", but one you run yourself. When you ring it, **Telegram Echo** answers the call on a Telegram account, plays a short prompt, echoes your own voice back to you with a delay, records the call, and — after you hang up — sends the recording back as a voice message. + +Because it exercises a real call end-to-end, it's a quick way to answer a question that's otherwise hard to test alone: *do Telegram voice/video calls actually work on this connection?* + +## What it's for + +Telegram calls use peer-to-peer WebRTC, which is exactly the kind of traffic that flaky links and censorship infrastructure tend to break. This service gives you a call partner that's always available, so you can check the call path on demand: + +- 📶 **Flaky or high-latency connections** — confirm a call can connect and stay up, and hear the echo to judge the round-trip audio quality. +- 🧱 **DPI / state-level firewalls** — verify whether voice/video calls get through deep-packet-inspection censorship at all. +- 🔀 **Proxied Telegram** — check that calls still work when you route Telegram through a proxy or VPN, not just chats. +- 🛠️ **A working reference for Telegram call automation** — a complete, runnable example of wiring [TDLib](https://github.com/tdlib/td) signaling to [tgcalls](https://github.com/TGX-Android/tgcalls) WebRTC audio with a custom audio device, MP3 recording, and voice-note delivery. Working code for this is hard to find. + +## What happens when you call it + +``` +1. You dial the service on Telegram → it auto-accepts the call +2. It plays your prompt.mp3 once → you hear the greeting = signaling works +3. You speak → your voice is echoed back (with delay) = media flows +4. The call is recorded → recordings/recording_.mp3 +5. You hang up → the recording is sent back as a voice message +``` ``` TDLib (signaling) tgcalls (WebRTC audio) @@ -16,182 +38,128 @@ TDLib (signaling) tgcalls (WebRTC audio) sendMessage (voice note) FileRecorder -> recordings/*.mp3 ``` -## Prerequisites +## Quick start (no build toolchain) -### System packages (Arch Linux) +Runs from a pre-built container image — you only need Podman (or Docker) and Telegram API credentials. -``` -pacman -S openssl opus libvpx libyuv ffmpeg zlib gperf cmake lame -``` +1. **Get Telegram API credentials.** Create an app at to obtain an `api_id` and `api_hash`. -All C++ dependencies (tgcalls, WebRTC, abseil, libsrtp, usrsctp, openh264, rnnoise, crc32c, libevent, libyuv) are vendored as git submodules under `vendor/`. + ```bash + export API_ID=12345 + export API_HASH=abcdef1234567890 + ``` -## Build +2. **Make a prompt** (first time only): -```bash -git clone --recurse-submodules -cd tg-call-service -make -``` + ```bash + podman compose --profile tools run --rm prompt # simple beep + # or, with a GLaDOS voice: + podman compose --profile tools run --rm glados-prompt + ``` -This runs all build steps: init submodules, build TDLib, configure cmake, compile ~1400 sources. First build takes several minutes; subsequent `make` only recompiles changed files. +3. **Log in to Telegram** (first time only, interactive — asks for phone, code, 2FA): -Individual targets: + ```bash + podman compose --profile auth run --rm auth + ``` -| Target | Description | -|--------|-------------| -| `make` | Full build (submodules + TDLib + call_service) | -| `make tdlib` | Build TDLib only (into `td-install/`) | -| `make build` | Build call_service only (assumes TDLib is built) | -| `make prompt` | Generate a test beep prompt (MP3) | -| `make glados-prompt` | Generate prompt via GLaDOS TTS | -| `make run` | Build + generate prompt + run the service | -| `make clean` | Remove build directories | - -Pass `BUILD_TYPE=Release` for optimized build. - -Output: `build/call_service` (~270MB debug, strip with `strip -s` for ~30MB). +4. **Start the service**, then call its account from another device to run a test: -## Usage + ```bash + podman compose up -d tg-echo + ``` -### Telegram API credentials +Session data (`tdlib_db/`), recordings, and `prompt.mp3` are bind-mounted from the project directory, so they persist across restarts. -Obtain `api_id` and `api_hash` from https://my.telegram.org/apps. +> ⚠️ This logs in as a **real Telegram user account**, not a bot account — bot accounts can't receive calls. Use a dedicated account you control as the always-on test endpoint, then call it from your own account to test. -Pass via environment variables or command-line flags: +## Configuration -```bash -# env vars -export API_ID=12345 -export API_HASH=abcdef1234567890 - -# or flags -./build/call_service --api-id 12345 --api-hash abcdef1234567890 -``` - -Tip: use a `.envrc` with [direnv](https://direnv.net/) to load them automatically. - -### Prepare audio prompt - -The prompt must be an MP3 file. It is decoded to PCM at startup via LAME. - -```bash -# use any MP3 file directly -cp greeting.mp3 prompt.mp3 - -# or generate a test beep -make prompt - -# or generate a GLaDOS voice prompt (https://glados.c-net.org/) -make glados-prompt -make glados-prompt GLADOS_TEXT="The cake is a lie. Leave a message." -``` - -### Run - -```bash -./build/call_service [--prompt prompt.mp3] [--recordings-dir recordings/] [--echo-delay 1000] -``` +The service is configured via flags or environment variables: | Flag | Env var | Default | Description | |------|---------|---------|-------------| -| `--prompt` | — | `prompt.mp3` | PCM audio prompt file | -| `--recordings-dir` | — | `recordings/` | Directory for MP3 recordings | +| `--api-id` | `API_ID` | — | Telegram API id (required) | +| `--api-hash` | `API_HASH` | — | Telegram API hash (required) | +| `--prompt` | — | `prompt.mp3` | Prompt played to the caller | +| `--recordings-dir` | — | `recordings/` | Where MP3 recordings are written | | `--echo-delay` | `ECHO_DELAY` | `1000` | Echo delay in milliseconds | -On first run, TDLib will prompt interactively for: -1. Phone number -2. Auth code (sent to Telegram) -3. 2FA password (if enabled) +The prompt must be an MP3 (decoded to PCM at startup via LAME). Use any MP3 directly, or generate one with `make prompt` / `make glados-prompt GLADOS_TEXT="The cake is a lie."`. -### TDLib session storage +Recordings land in `recordings/recording_.mp3` (VBR, high quality). Play them back with `ffplay recordings/recording_*.mp3`. -TDLib persists its session in `tdlib_db/` in the working directory. Subsequent runs skip authentication. Delete this directory to log out / switch accounts. +TDLib persists its session in `tdlib_db/`; delete that directory to log out or switch accounts. -### Recordings +## Build from source -Caller audio is saved as MP3 to `recordings/recording_.mp3` (VBR, high quality). After the call ends, the recording is automatically sent back to the caller as a Telegram voice message. - -Play back: +### Prerequisites (Arch Linux) ```bash -ffplay recordings/recording_*.mp3 +pacman -S openssl opus libvpx libyuv ffmpeg zlib gperf cmake lame ``` -## Running with Podman Compose - -No build toolchain required — uses a pre-built image from Docker Hub. +All C++ dependencies (tgcalls, WebRTC, abseil, libsrtp, usrsctp, openh264, rnnoise, crc32c, libevent, libyuv) are vendored as git submodules under `vendor/`. -### Quick start +### Build ```bash -export API_ID=12345 -export API_HASH=abcdef1234567890 +git clone --recurse-submodules ssh://git@github.com/nikicat/tg-echo.git +cd tg-echo +make +``` -# Generate a prompt (first time only) -podman compose --profile tools run --rm prompt -# or with GLaDOS voice: -podman compose --profile tools run --rm glados-prompt +`make` runs every step: init submodules, build TDLib, configure cmake, compile ~1400 sources. The first build takes several minutes; later builds only recompile what changed. Pass `BUILD_TYPE=Release` for an optimized build. -# Authenticate with Telegram (first time only, interactive) -podman compose --profile auth run --rm auth +Output: `build/tg-echo` (~270 MB debug; `strip -s` it to ~30 MB). -# Start the service -podman compose up -d call-service -``` +| Target | Description | +|--------|-------------| +| `make` | Full build (submodules + TDLib + tg-echo) | +| `make tdlib` | Build TDLib only (into `td-install/`) | +| `make build` | Build the tg-echo binary only (assumes TDLib is built) | +| `make prompt` | Generate a test beep prompt (MP3) | +| `make glados-prompt` | Generate a prompt via GLaDOS TTS | +| `make run` | Build + generate prompt + run | +| `make image` / `make push` | Build / push the container image | +| `make clean` | Remove build directories | -### Building the image locally +### Run ```bash -make image # builds docker.io/nikicat/tg-echo-service -make push # builds and pushes to Docker Hub -make image IMAGE=foo/bar # custom image name +./build/tg-echo [--prompt prompt.mp3] [--recordings-dir recordings/] [--echo-delay 1000] ``` -### Compose services - -| Service | Profile | Description | -|---------|---------|-------------| -| `call-service` | *(default)* | Long-running echo service | -| `auth` | `auth` | Interactive TDLib authentication | -| `prompt` | `tools` | Generate beep prompt via ffmpeg | -| `glados-prompt` | `tools` | Generate prompt via GLaDOS TTS | - -Session data (`tdlib_db/`), recordings, and `prompt.mp3` are bind-mounted from the project directory. +On first run TDLib asks interactively for your phone number, the auth code Telegram sends you, and your 2FA password if set. ## How it works -1. TDLib receives `updateCall` with `callStatePending` (incoming call) -2. Service sends `acceptCall` with supported tgcalls protocol versions -3. TDLib receives `callStateReady` with relay servers and encryption key -4. Service maps TDLib call servers to tgcalls endpoints/RTC servers, creates a tgcalls `Instance` via `Meta::Create` -5. `FakeAudioDeviceModule` provides custom audio I/O via `EchoPlayer` (implements both `Recorder` and `Renderer`): plays the prompt once, then echoes caller audio back with a configurable delay. `FileRecorder` encodes to MP3 via LAME -6. Signaling data is relayed bidirectionally between TDLib and tgcalls -7. On hangup, the MP3 recording is finalized and sent to the caller as a voice message via `sendMessage` + `inputMessageVoiceNote` +1. TDLib receives `updateCall` with `callStatePending` (an incoming call). +2. The service sends `acceptCall` with the supported tgcalls protocol versions. +3. TDLib reports `callStateReady` with relay servers and the encryption key. +4. The service maps TDLib's call servers to tgcalls endpoints/RTC servers and creates a tgcalls `Instance` via `Meta::Create`. +5. A `FakeAudioDeviceModule` supplies custom audio I/O: `EchoPlayer` (acting as both `Recorder` and `Renderer`) plays the prompt once, then echoes caller audio back with the configured delay, while `FileRecorder` encodes everything to MP3 via LAME. +6. Signaling data is relayed bidirectionally between TDLib and tgcalls. +7. On hangup, the MP3 is finalized and sent to the caller as a voice message via `sendMessage` + `inputMessageVoiceNote`. ## Project structure ``` -CMakeLists.txt Build orchestrator (vendored deps + tgcalls + our code) main.cpp EchoPlayer, FileRecorder, CallService, auth flow, event loop +video_platform.cpp Video frame plumbing +CMakeLists.txt Build orchestrator (vendored deps + tgcalls + our code) +compose.yaml Podman/Docker Compose services (tg-echo, auth, tools) +deploy/ Podman Quadlet unit for systemd-managed deployment stub/ Build stubs (config.h, crc32c config, AudioDeviceModule stub) -vendor/ Vendored dependencies (git submodules + cmake files) - Build*.cmake CMake build scripts (from Telegram-X) - tgcalls/ TGX-Android/tgcalls (call protocol library) - webrtc/ TGX-Android/webrtc (WebRTC fork) - abseil-cpp/ abseil/abseil-cpp - libsrtp/ cisco/libsrtp - usrsctp/ sctplab/usrsctp - openh264/ cisco/openh264 - rnnoise/ xiph/rnnoise - crc32c/ google/crc32c - libevent/ TGX-Android/libevent - libyuv/ chromium/libyuv - td/ tdlib/td (TDLib source, built separately into td-install/) - webrtc_deps/ Copied files from chromium (pffft, rnnoise weights, field trials) - third_party/ Symlinks for WebRTC include resolution (libyuv, libsrtp, etc.) -td-install/ TDLib cmake exports and static libraries (not committed) +vendor/ Vendored dependencies (git submodules + cmake build scripts) + tgcalls/ webrtc/ td/ Core: call protocol, WebRTC fork, TDLib + abseil-cpp/ libsrtp/ ... Supporting libraries +td-install/ TDLib cmake exports + static libs (not committed) build/ CMake build directory (not committed) tdlib_db/ TDLib session data (not committed) recordings/ Recorded audio files (not committed) ``` + +## License + +This project's original code is released under the [MIT License](LICENSE). It links several third-party libraries with their own terms — most notably **tgcalls (LGPL v3)**. See [NOTICE.md](NOTICE.md) for the full dependency licensing breakdown and what it means for redistributing binaries. diff --git a/compose.yaml b/compose.yaml index 6665258..92cf1c9 100644 --- a/compose.yaml +++ b/compose.yaml @@ -4,11 +4,11 @@ x-podman: in_pod: false x-service: &service - image: docker.io/nikicat/tg-echo-service + image: docker.io/nikicat/tg-echo network_mode: pasta x-tools: &tools - image: docker.io/nikicat/tg-echo-service-tools + image: docker.io/nikicat/tg-echo-tools volumes: - ./:/out profiles: @@ -38,7 +38,7 @@ services: network_mode: pasta command: ["glados-prompt"] - call-service: + tg-echo: <<: *service environment: - API_ID diff --git a/deploy/README.md b/deploy/README.md index 3f5ad9d..d9b6f20 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,37 +1,37 @@ # Deploying with podman + Quadlet (rootless) -Runs the call service as a rootless systemd service via [Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html). +Runs Telegram Echo as a rootless systemd service via [Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html). Requires **podman ≥ 4.4** on the server. The image is public on GHCR, so no registry login is needed to pull. ## 1. Install the unit and config ```sh -mkdir -p ~/.config/containers/systemd ~/.config/tg-call-service -cp deploy/tg-call-service.container ~/.config/containers/systemd/ -cp deploy/tg-call-service.env.example ~/.config/tg-call-service/tg-call-service.env +mkdir -p ~/.config/containers/systemd ~/.config/tg-echo +cp deploy/tg-echo.container ~/.config/containers/systemd/ +cp deploy/tg-echo.env.example ~/.config/tg-echo/tg-echo.env ``` -Edit `~/.config/tg-call-service/tg-call-service.env` and set `API_ID` / `API_HASH` +Edit `~/.config/tg-echo/tg-echo.env` and set `API_ID` / `API_HASH` (from ). ## 2. Create data dirs and a prompt ```sh -mkdir -p ~/.local/share/tg-call-service/{tdlib_db,recordings} +mkdir -p ~/.local/share/tg-echo/{tdlib_db,recordings} ``` Provide the audio prompt (48 kHz stereo MP3) played to callers — copy your own to -`~/.local/share/tg-call-service/prompt.mp3`, or generate one with the tools image: +`~/.local/share/tg-echo/prompt.mp3`, or generate one with the tools image: ```sh # simple beep prompt -podman run --rm -v ~/.local/share/tg-call-service:/out \ - ghcr.io/nikicat/tg-echo-service-tools:latest prompt +podman run --rm -v ~/.local/share/tg-echo:/out \ + ghcr.io/nikicat/tg-echo-tools:latest prompt # or a GLaDOS TTS prompt -podman run --rm -v ~/.local/share/tg-call-service:/out \ - ghcr.io/nikicat/tg-echo-service-tools:latest \ +podman run --rm -v ~/.local/share/tg-echo:/out \ + ghcr.io/nikicat/tg-echo-tools:latest \ glados-prompt GLADOS_TEXT="Please leave a message after the beep." ``` @@ -42,9 +42,9 @@ your phone number and the login code Telegram sends): ```sh podman run --rm -it \ - --env-file ~/.config/tg-call-service/tg-call-service.env \ - -v ~/.local/share/tg-call-service/tdlib_db:/app/tdlib_db:Z \ - ghcr.io/nikicat/tg-echo-service:latest auth + --env-file ~/.config/tg-echo/tg-echo.env \ + -v ~/.local/share/tg-echo/tdlib_db:/app/tdlib_db:Z \ + ghcr.io/nikicat/tg-echo:latest auth ``` ## 4. Start it @@ -52,14 +52,14 @@ podman run --rm -it \ ```sh loginctl enable-linger "$USER" # keep the service running with no login session systemctl --user daemon-reload # generate the service from the .container unit -systemctl --user start tg-call-service # [Install] handles auto-start at boot +systemctl --user start tg-echo # [Install] handles auto-start at boot ``` Check status / logs: ```sh -systemctl --user status tg-call-service -journalctl --user -u tg-call-service -f +systemctl --user status tg-echo +journalctl --user -u tg-echo -f ``` ## 5. Updates (optional) @@ -71,7 +71,7 @@ restart automatically: systemctl --user enable --now podman-auto-update.timer ``` -To pin a release instead, set `Image=ghcr.io/nikicat/tg-echo-service:v1.2.3` in the +To pin a release instead, set `Image=ghcr.io/nikicat/tg-echo:v1.2.3` in the unit and drop the `AutoUpdate=registry` line. ## Notes @@ -82,5 +82,5 @@ unit and drop the `AutoUpdate=registry` line. - `%h` expands to the user's home. If your systemd doesn't expand it in a value, replace `%h/...` with absolute paths. - **Rootful variant:** put the `.container` in `/etc/containers/systemd/`, replace - `%h/...` with absolute paths (e.g. `/var/lib/tg-call-service/...`), then - `sudo systemctl daemon-reload && sudo systemctl start tg-call-service`. + `%h/...` with absolute paths (e.g. `/var/lib/tg-echo/...`), then + `sudo systemctl daemon-reload && sudo systemctl start tg-echo`. diff --git a/deploy/tg-call-service.container b/deploy/tg-echo.container similarity index 64% rename from deploy/tg-call-service.container rename to deploy/tg-echo.container index ebfc1db..62a5efc 100644 --- a/deploy/tg-call-service.container +++ b/deploy/tg-echo.container @@ -1,30 +1,30 @@ -# Quadlet unit for the Telegram echo call service (rootless podman + systemd). +# Quadlet unit for Telegram Echo (rootless podman + systemd). # -# Install to: ~/.config/containers/systemd/tg-call-service.container -# Then: systemctl --user daemon-reload && systemctl --user start tg-call-service +# Install to: ~/.config/containers/systemd/tg-echo.container +# Then: systemctl --user daemon-reload && systemctl --user start tg-echo # # See deploy/README.md for full setup (credentials, prompt, one-time auth, linger). # Requires podman >= 4.4 (Quadlet). [Unit] -Description=Telegram echo call service +Description=Telegram Echo call-test service After=network-online.target Wants=network-online.target [Container] -Image=ghcr.io/nikicat/tg-echo-service:latest +Image=ghcr.io/nikicat/tg-echo:latest # Pull a newer :latest on `podman auto-update` (enable the timer; see README). AutoUpdate=registry # API_ID, API_HASH and optional ECHO_DELAY. -EnvironmentFile=%h/.config/tg-call-service/tg-call-service.env +EnvironmentFile=%h/.config/tg-echo/tg-echo.env # Persistent TDLib session DB — created by the one-time `auth` run (see README). -Volume=%h/.local/share/tg-call-service/tdlib_db:/app/tdlib_db:Z +Volume=%h/.local/share/tg-echo/tdlib_db:/app/tdlib_db:Z # Recorded voice messages, written after each call. -Volume=%h/.local/share/tg-call-service/recordings:/app/recordings:Z +Volume=%h/.local/share/tg-echo/recordings:/app/recordings:Z # Audio prompt played to callers (provide your own 48 kHz stereo MP3). -Volume=%h/.local/share/tg-call-service/prompt.mp3:/app/prompt.mp3:ro,Z +Volume=%h/.local/share/tg-echo/prompt.mp3:/app/prompt.mp3:ro,Z # Rootless networking; the service only makes outbound connections (no ports). Network=pasta diff --git a/deploy/tg-call-service.env.example b/deploy/tg-echo.env.example similarity index 72% rename from deploy/tg-call-service.env.example rename to deploy/tg-echo.env.example index d5a4d9b..de85c8f 100644 --- a/deploy/tg-call-service.env.example +++ b/deploy/tg-echo.env.example @@ -1,4 +1,4 @@ -# Copy to ~/.config/tg-call-service/tg-call-service.env and fill in. +# Copy to ~/.config/tg-echo/tg-echo.env and fill in. # Telegram API credentials — create an app at https://my.telegram.org/apps API_ID= API_HASH= diff --git a/main.cpp b/main.cpp index d32dff7..5f36918 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,4 @@ -// Telegram Call Service — accepts incoming calls, plays prompt, echoes caller audio, records to MP3 +// Telegram Echo — accepts incoming calls, plays prompt, echoes caller audio, records to MP3 // Uses TDLib for signaling + tgcalls for audio #include @@ -438,7 +438,7 @@ class TdService { virtual void on_authorized() = 0; virtual void on_auth_challenge(td_api::object_ptr state, AuthHandler handler) { - std::cerr << "Not authenticated — run `call_service auth` first" << std::endl; + std::cerr << "Not authenticated — run `tg-echo auth` first" << std::endl; quit_ = true; }