Skip to content

haswelldev/media-forwarder

Repository files navigation

Media Forwarder

Build Status codecov License: MIT

A lightweight Dockerized service written in Go that monitors Telegram channels and automatically forwards posts (including photos, videos, and documents) to Discord channels via webhooks.

Features

  • Monitor multiple Telegram channels simultaneously
  • Forward to multiple Discord destinations
  • Support for photos, videos, and documents
  • Automatic filtering of oversized files (configurable)
  • Uses real Telegram user account (no bot limitations)
  • Docker-based deployment with a single static binary
  • Simple YAML configuration (backward compatible with previous Python version)
  • Structured logging
  • Session file persistence
  • Per-channel settings
  • Media-only mode
  • Caption removal
  • Per-destination file size limits
  • Smart image compression (progressive JPEG quality reduction)
  • Video compression via ffmpeg
  • Album/grouped message support
  • Rate-limit handling with Retry-After

Quick Start

Prerequisites

  • Docker and Docker Compose installed
  • Telegram account
  • Discord webhook URL(s)
  • Telegram API credentials (get from my.telegram.org)

1. Get Telegram API Credentials

  1. Go to my.telegram.org
  2. Sign in with your phone number
  3. Go to "API development tools"
  4. Create a new application (any name and description)
  5. Copy the api_id and api_hash

2. Create Configuration Files

Create a .env file:

# Telegram API credentials
TELEGRAM_API_ID=your_api_id_here
TELEGRAM_API_HASH=your_api_hash_here

# Optional: Session file name (default: media_forwarder)
TELEGRAM_SESSION_NAME=media_forwarder

# Optional: Log level (DEBUG, INFO, WARNING, ERROR)
LOG_LEVEL=INFO

Create a config/channels.yaml file:

channels:
  - channel: "@channel_name_to_monitor"
    destinations:
      - discord_main

discord_webhooks:
  discord_main:
    url: "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"

settings:
  max_file_size_mb: 10
  log_level: INFO
  include_channel_name: true
  include_timestamp: true
  group_timeout_seconds: 3.0

3. Login to Telegram

Run the login command (interactive):

docker compose run --rm media-forwarder login

Follow the prompts:

  1. Enter your phone number (with country code, e.g., +1234567890)
  2. Enter the verification code sent to Telegram
  3. Enter your 2FA password if enabled

The session file will be saved to ./sessions/media_forwarder.session

4. Validate Configuration

Check your configuration:

docker compose run --rm media-forwarder validate

5. Start the Service

docker compose up -d

6. View Logs

docker compose logs -f

Commands

Command Description
login Interactive Telegram login
run Start the forwarder (default command)
validate Validate configuration file

All commands support the --config flag to specify a custom config path:

docker compose run --rm media-forwarder validate --config /path/to/config.yaml

Configuration

Channel Format

Channels can be specified by username or numeric ID:

channels:
  # By username
  - channel: "@example_channel"

  # By numeric ID
  - channel: "-1001234567890"

Per-Destination Settings

Each destination can have its own max file size:

discord_webhooks:
  discord_main:
    url: "https://discord.com/api/webhooks/..."
    max_file_size_mb: 25  # Custom limit for this destination

Max File Size Priority:

  1. Destination-specific setting (highest priority)
  2. Channel-specific setting
  3. Global default setting

Per-Channel Settings

channels:
  - channel: "@media_channel"
    destinations:
      - discord_main
    settings:
      # Only forward messages with media
      media_only: true
      # Remove captions from media posts
      remove_captions: false
      # Custom max file size for this channel
      max_file_size_mb: 25
      # Override metadata inclusion
      include_channel_name: true
      include_timestamp: true

Available Channel Settings:

Setting Type Description
media_only bool Only forward messages with media, skip text-only posts
remove_captions bool Remove captions from media posts
max_file_size_mb int Override maximum file size for this channel
include_channel_name bool Override include channel name setting
include_timestamp bool Override include timestamp setting

Note: The translate_captions setting is accepted for backward compatibility but has no effect in this version.

Multiple Destinations

Forward a channel to multiple Discord webhooks:

channels:
  - channel: "@my_channel"
    destinations:
      - discord_main
      - discord_backup

Global Settings

settings:
  # Maximum file size in MB to upload to Discord
  max_file_size_mb: 10

  # Logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL
  log_level: INFO

  # Include channel name in forwarded messages
  include_channel_name: true

  # Include timestamp in forwarded messages
  include_timestamp: true

  # Seconds to wait for album messages before forwarding
  group_timeout_seconds: 3.0

Media Compression

Image Compression

  1. Checks if file exceeds destination's max file size
  2. Verifies compression is feasible (file < 200MB, is an image)
  3. Progressively reduces JPEG quality from 95% to 60%
  4. Accepts compression only if size reduces by at least 30%
  5. Sends compressed image if successful, falls back to text-only if not

Video Compression

  1. Checks if file exceeds destination's max file size
  2. Verifies compression is feasible (file < 500MB, is a video)
  3. Requires ffmpeg to be available in the container
  4. Uses ffmpeg to compress with H.264 codec and AAC audio
  5. Calculates target bitrate based on desired file size and video duration
  6. Sends compressed video if successful, falls back to text-only if not

Quality Thresholds

Parameter Value
Maximum JPEG quality 95%
Minimum JPEG quality 60%
Minimum compression ratio 30% reduction
Maximum source size (images) 200MB
Maximum source size (videos) 500MB
Minimum video bitrate 500 kbps

File Size Limits

Platform Limit
Telegram (Premium) 2GB
Telegram (free) 500MB
Discord (free) 25MB
Discord (Nitro) 500MB
Default config 10MB

Files larger than the configured limit are compressed if possible, otherwise the text message is still forwarded.

Session Management

The Telegram session file is stored in ./sessions/ directory:

  • Session name: media_forwarder.session (configurable via TELEGRAM_SESSION_NAME)
  • Location: Mounted as Docker volume
  • Persistence: Survives container restarts
  • Security: Contains authentication tokens, keep it private

To re-login, delete the session file and run the login command again:

rm ./sessions/media_forwarder.session
docker compose run --rm media-forwarder login

Troubleshooting

Session Not Authorized

Error: session not authorized; run the 'login' command first

Solution: Run the login command to create a new session

docker compose run --rm media-forwarder login

Channel Not Found

Error: Channel not found or No configuration found for channel

Solution:

  • Verify the channel username or ID in config/channels.yaml
  • Make sure your Telegram account has access to the channel
  • Try using the numeric ID instead of username

Discord Webhook Failed

Error: Failed to send to Discord

Solution:

  • Verify the webhook URL is correct
  • Check Discord server status
  • Ensure the webhook has permissions to post in the channel
  • Check logs for detailed error messages

File Too Large

Warning: Media too large, attempting compression

Solution:

  • Increase max_file_size_mb in configuration
  • Note: Discord has hard limits (25MB free, 500MB Nitro)

Development

Building Locally

go build -o forwarder ./cmd/forwarder/

Running Locally (without Docker)

# Set environment variables
export TELEGRAM_API_ID=your_api_id
export TELEGRAM_API_HASH=your_api_hash

# Validate config
./forwarder validate --config config/channels.yaml

# Login
./forwarder login

# Run
./forwarder run --config config/channels.yaml

Running Tests

go test ./... -v -cover

Run tests with coverage report:

go test ./... -v -cover -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html

Building Docker Image

docker build -t media-forwarder .

Running with Docker

docker run --rm \
  -v $(pwd)/sessions:/app/sessions \
  -v $(pwd)/config:/app/config \
  --env-file .env \
  media-forwarder

CI/CD

This project uses GitHub Actions for:

  • Automated Testing: Runs Go tests with race detection on every push and pull request
  • Coverage Gate: Enforces ≥85% coverage on core packages (config, discord)
  • Docker Image Building: Builds and pushes Docker images to GitHub Container Registry
  • Coverage Reporting: Uploads coverage reports to Codecov

The Docker image is available at: ghcr.io/haswelldev/media-forwarder:main

Architecture

Component Technology
Language Go
Telegram Client gotd/td (MTProto library for user accounts)
Discord Client net/http (webhook-based, with retry and rate-limit handling)
Configuration YAML with Go struct validation
Image Compression Go image/jpeg stdlib
Video Compression ffmpeg (subprocess)
Container Multi-stage Docker build (Go → Debian slim + ffmpeg)
Testing Go testing package with httptest mocks

Migration from Python Version

This is a complete rewrite from Python to Go. Key differences:

  • Config format: 100% backward compatible — same channels.yaml and .env files
  • Session files: Not compatible — you must re-login (docker compose run --rm media-forwarder login)
  • Translation feature: The translate_captions config field is accepted but has no effect
  • Binary: Single static binary, no Python runtime needed
  • Docker image: Smaller (~120MB vs ~180MB) and starts faster (~50ms vs ~2-5s)

Security

  • Session files contain authentication tokens — keep them private
  • API credentials should be stored in environment variables
  • Webhook URLs should be kept secret
  • Never commit .env files or session files to version control

License

MIT

Support

For issues and questions, please open an issue on GitHub.

About

A Dockerized service that monitors Telegram channels and automatically forwards posts (including photos, videos, and documents) to Discord channels via webhooks.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors