A stateless GitHub Actions-based system that relays files from Telegram to free file hosting services.
Telegram Bot β GitHub API (dispatch) β GitHub Actions β Download from Telegram β Upload to Host β Send Link Back
- Telegram bot receives file
- Bot triggers GitHub Actions via
repository_dispatchwithfile_idandchat_id - Workflow downloads file using Telegram Bot API
- File uploaded to free hosting (0x0.st β transfer.sh β gofile.io)
- Download link sent back to Telegram chat
- Temp files cleaned up
import requests
GITHUB_TOKEN = "ghp_xxxxxxxxxxxx" # Create at https://github.com/settings/tokens
REPO_OWNER = "0freyy"
REPO_NAME = "telegram-file-relay-freyy"
def relay_file_to_github(file_id, chat_id):
requests.post(
f"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/dispatches",
headers={
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+raw"
},
json={
"event_type": "telegram_file",
"client_payload": {
"file_id": file_id,
"chat_id": chat_id
}
}
)- Go to repository Settings β Secrets and variables β Actions
- Create secret:
TELEGRAM_BOT_TOKEN=123456:ABCDefghIJKlmnopQRStuvwxyz
from telegram import Update
from telegram.ext import Application, MessageHandler, filters, ContextTypes
async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE):
document = update.message.document
relay_file_to_github(document.file_id, update.effective_chat.id)
await update.message.reply_text("π€ Relaying file...")
app = Application.builder().token("YOUR_BOT_TOKEN").build()
app.add_handler(MessageHandler(filters.Document.ALL, handle_document))
app.run_polling()- β Stateless - No database, no server required
- β Resilient - 3-tier upload fallback system
- β Fast - Prefers 0x0.st (fastest, no tracking)
- β Secure - Token stored in secrets, files never persisted
- β Production-Ready - Error handling, cleanup, timeout protection
- 0x0.st - Fastest, no tracking, 365-day retention
- transfer.sh - Reliable, 14-day retention
- gofile.io - Large file support, permanent storage
- 0x0.st: 512 MB
- transfer.sh: 10 GB
- gofile.io: 100 GB
Set in workflow or via GitHub secrets:
| Variable | Description | Source |
|---|---|---|
TELEGRAM_BOT_TOKEN |
Bot token for Telegram API | GitHub Secret |
FILE_ID |
Telegram file ID | Dispatch payload |
CHAT_ID |
Telegram chat ID | Dispatch payload |
UPLOAD_TIMEOUT |
Upload timeout in seconds | Default: 300 |
- Network failures: Retried with next service
- All services down: User notified in Telegram
- File download failed: Error message sent to chat
$0 - Uses only:
- GitHub Actions free tier (2000 minutes/month for private repos)
- Free file hosting services
- Telegram Bot API (free)
- GitHub token should have minimal permissions (only
repo:statusandpublic_repo) - Never commit
TELEGRAM_BOT_TOKENto repository - Files are deleted after upload completes
- Uploaded files inherit host service's retention policy
- Max execution time: 6 hours (GitHub Actions limit)
- File download timeout: 60 seconds
- Upload timeout: 5 minutes
- Stateless design: No retry queue if Actions fails
from telegram import Update
from telegram.ext import Application, MessageHandler, filters, ContextTypes
import requests
GITHUB_TOKEN = "ghp_xxxxxxxxxxxx"
REPO = "0freyy/telegram-file-relay-freyy"
async def handle_media(update: Update, context: ContextTypes.DEFAULT_TYPE):
file_id = None
filename = "file"
if update.message.document:
file_id = update.message.document.file_id
filename = update.message.document.file_name or "document"
elif update.message.video:
file_id = update.message.video.file_id
filename = f"video_{update.message.video.file_id[:8]}.mp4"
elif update.message.audio:
file_id = update.message.audio.file_id
filename = update.message.audio.file_name or "audio.mp3"
if not file_id:
return
# Trigger relay
response = requests.post(
f"https://api.github.com/repos/{REPO}/dispatches",
headers={
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+raw"
},
json={
"event_type": "telegram_file",
"client_payload": {
"file_id": file_id,
"chat_id": str(update.effective_chat.id)
}
}
)
if response.status_code == 204:
await update.message.reply_text("π€ Uploading file...")
else:
await update.message.reply_text("β Relay failed. Try again later.")
app = Application.builder().token("YOUR_BOT_TOKEN").build()
app.add_handler(MessageHandler(filters.Document.ALL | filters.Video | filters.Audio, handle_media))
app.run_polling()Workflow doesn't run:
- Check if token has
reposcope - Verify
TELEGRAM_BOT_TOKENis set in secrets - Check workflow syntax:
gh workflow view telegram-relay.yml
File upload fails:
- Check if services are accessible (not blocked in region)
- Increase
UPLOAD_TIMEOUTif on slow connection - Verify file size is under service limits
Bot doesn't reply:
- Check if bot has
sendMessagepermission - Verify chat ID is correct (private chat vs. group)
- Check workflow logs: Actions tab β Latest run
MIT