> ## Content Index
> Fetch the complete content index at: https://kk-lab.uk/llms.txt
> Use this file to discover other available public pages before exploring further.

# Membangun Downloader Telegram Otomatis: Tinggal Forward, Server yang Kerja
- URL: https://kk-lab.uk/membangun-downloader-telegram-otomatis-tinggal-forward-server-yang-kerja/
- Published: 2026-09-17T02:31:01.000Z
- Updated: 2026-09-17T02:31:01.000Z
- Author: Ryan Arief
- Tags: Homelab, My projects, Software & Development

Ada satu masalah klasik kalau sudah punya server media sendiri.

Media berseliweran di Telegram.

**IYKWIM.** 😏

Kadang ada video, kadang ada foto, kadang ada file yang rasanya:

> “Hmm... ini kayaknya harus masuk koleksi Jellyfin.”

Sebenarnya cara manualnya gampang.

Download di HP → pindahkan ke komputer → upload ke server → rapikan folder → tunggu Jellyfin scan.

Secara teknis tidak sulit.

Masalahnya cuma satu:

> **You know how lazy I am laaa.**

Kalau cuma satu file, masih oke.

Tapi kalau harus dilakukan berkali-kali, mulai muncul kalimat:

> “Nanti aja.”

Dan biasanya “nanti” itu tidak pernah datang.

Akhirnya kepikiran:

> **Kenapa nggak Telegram saja yang mengantarkan medianya ke server?**

Jadi daripada download di HP lalu upload manual ke Jellyfin, saya cukup forward media dari **nomor A ke nomor B**.

Sisanya biar homelab yang kerja.

---

# 1\. Memanfaatkan Git

Daripada membuat downloader Telegram sendiri dari nol, saya mencari project yang sudah mengerjakan bagian yang merepotkan tersebut.

Pilihan yang digunakan adalah:

[Dineshkarthik/telegram\_media\_downloader di GitHub](https://github.com/Dineshkarthik/telegram%5Fmedia%5Fdownloader?utm%5Fsource=chatgpt.com)

Repository tersebut kemudian di-clone ke homelab:

```bash
cd /home/serverkamu/PYTHON

git clone https://github.com/Dineshkarthik/telegram_media_downloader.git

cd telegram_media_downloader

```

Kenapa pakai Git?

Ya karena kalau nanti project-nya mau di-update, dicek perubahannya, atau sekadar ingin tahu source code yang sedang digunakan, semuanya sudah rapi.

Tidak perlu:

> “Ini file Python bikinan siapa ya?”

---

# 2\. Python Virtual Environment

Karena ini Python project, dependency-nya dipisahkan dari Python sistem.

```bash
python3 -m venv .venv
source .venv/bin/activate

```

Kemudian:

```bash
make install

```

Sekarang downloader punya lingkungan Python sendiri.

```text
telegram_media_downloader/
├── .venv/
├── media_downloader.py
├── config.yaml
├── media_downloader.session
└── DOWNLOADS/

```

Virtual environment ini nantinya juga penting ketika program dijalankan oleh systemd.

---

# 3\. Nomor B Menjadi Downloader

Saya tidak menggunakan Telegram Bot API.

Tidak ada bot token.

Tidak ada:

```text
A → Bot → Server

```

Yang digunakan adalah akun Telegram biasa.

Misalnya:

```text
Nomor A = sumber media
Nomor B = akun downloader

```

Jadi workflow-nya:

```text
Nomor A
    │
    │ forward media
    ▼
Nomor B

```

Nomor B login menggunakan Telegram API dan Telethon.

API ID dan API Hash dibuat melalui:

[my.telegram.org/apps](https://my.telegram.org/apps?utm%5Fsource=chatgpt.com)

Setelah login pertama kali, Telethon membuat session:

```text
media_downloader.session

```

Session inilah yang menyimpan autentikasi akun B.

Jadi setelah login pertama:

```text
Nomor B
   ↓
Telethon
   ↓
media_downloader.session

```

program tidak perlu meminta OTP setiap kali dijalankan.

---

# 4\. Menentukan Chat yang Dipantau

Downloader perlu tahu percakapan mana yang harus dipantau.

Dari akun B, chat dengan nomor A mempunyai ID:

```text
169172416

```

Kemudian dimasukkan ke `config.yaml`:

```yaml
chats:
  - chat_id: 169172416
    last_read_message_id: 0
    ids_to_retry: []
    download_directory: ./DOWNLOADS

```

Sekarang server tahu:

> Chat yang perlu diperiksa adalah `169172416`.

---

# 5\. Checkpoint Supaya Tidak Download Ulang

Bagian yang sangat penting adalah:

```yaml
last_read_message_id

```

Anggap saja downloader sudah memproses:

```text
message ID = 500

```

Maka config menyimpan:

```yaml
last_read_message_id: 500

```

Saat program dijalankan lagi, dia tidak mengulang semuanya dari awal.

Dia tahu:

> “Saya sudah sampai message 500.”

Ini juga terbukti ketika downloader dijalankan kembali: media yang sudah diproses tidak di-download ulang.

Dengan kata lain, restart server bukan berarti download ulang semua isi chat.

---

# 6\. Downloader Ternyata Bukan Daemon

Pada awalnya saya kira tinggal menjalankan:

```bash
python3 media_downloader.py

```

dan membiarkannya hidup.

Ternyata tidak.

Downloader bekerja seperti ini:

```text
connect
   ↓
check messages
   ↓
download
   ↓
update checkpoint
   ↓
disconnect
   ↓
exit

```

Jadi dia bukan proses yang terus menunggu pesan baru.

Kalau ada pesan baru lima menit kemudian, downloader yang sudah exit tentu tidak tahu.

Maka dibutuhkan watcher.

---

# 7\. Watcher: Cek Setiap 30 Detik

Watcher dibuat sangat sederhana.

Setiap **30 detik**, dia:

1. membaca `config.yaml`,
2. mengambil `last_read_message_id`,
3. mengambil message terbaru dari chat A → B,
4. membandingkan ID,
5. kalau sama → tidak melakukan apa-apa,
6. kalau berbeda → menjalankan downloader.

Konsepnya:

```text
                 setiap 30 detik
                       │
                       ▼
                message terbaru
                       │
                       ▼
             current ID == last ID?
                  │           │
                 YES          NO
                  │           │
                  ▼           ▼
                skip      downloader

```

Watcher tidak mengubah checkpoint.

Yang mengubah:

```text
last_read_message_id

```

tetap downloader.

Dengan begitu hanya downloader yang bertanggung jawab terhadap status message yang sudah diproses.

---

# 8\. Kenapa Pakai systemd?

Kalau watcher harus dijalankan manual setiap kali server boot:

```bash
python3 telegram_watcher.py

```

berarti kita belum benar-benar otomatis.

Maka watcher dijadikan **systemd service**.

File:

```text
/etc/systemd/system/telegram-watcher.service

```

Konfigurasinya:

```ini
[Unit]
Description=Telegram Media Watcher
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=serverkamu
WorkingDirectory=/home/serverkamu/PYTHON/telegram_media_downloader
ExecStart=/home/serverkamu/PYTHON/telegram_media_downloader/.venv/bin/python telegram_watcher.py
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

```

Kemudian:

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now telegram-watcher

```

Sekarang setiap server boot:

```text
server boot
    ↓
systemd
    ↓
telegram-watcher
    ↓
check Telegram
    ↓
setiap 30 detik

```

Tidak perlu menjalankan apa-apa secara manual.

Kalau watcher crash, systemd juga bisa menjalankannya kembali.

Untuk melihat status:

```bash
systemctl status telegram-watcher

```

Untuk melihat aktivitas secara live:

```bash
journalctl -u telegram-watcher -f

```

---

# 9\. Ada Sedikit Drama dengan Python 3.14

Tentu saja tidak mungkin proyek homelab berjalan mulus tanpa sedikit drama.

Server menggunakan:

```text
CPython 3.14.4

```

Sementara downloader masih menggunakan pola `asyncio` lama.

Saat pertama dijalankan muncul:

```text
RuntimeError:
There is no current event loop in thread 'MainThread'.

```

Untungnya masalahnya cukup lokal.

Event loop dibuat terlebih dahulu sebelum `main()` dijalankan.

Setelah patch kecil tersebut:

```text
Telegram connection → OK
chat → OK
download → OK
checkpoint → OK
disconnect → OK

```

Lanjut.

---

# 10\. Watcher Juga Sempat Salah Python

Setelah watcher berjalan, muncul error:

```text
ModuleNotFoundError: No module named 'telethon'

```

Padahal Telethon sudah terinstall.

Ternyata watcher menggunakan Python dari `.venv`, tetapi ketika menjalankan downloader dia memanggil:

```python
python3 media_downloader.py

```

yang mengarah ke Python sistem.

Solusinya adalah menggunakan interpreter yang sedang menjalankan watcher:

```python
subprocess.run(
    [sys.executable, "media_downloader.py"],
    check=True,
)

```

Sekarang watcher dan downloader sama-sama menggunakan Python dari `.venv`.

---

# 11\. Dan Akhirnya: Tinggal Forward

Setelah semua bagian selesai, workflow sehari-hari menjadi sangat sederhana.

Misalnya ada sebuah film yang **mau ditangkap**.

Tidak perlu:

```text
download ke HP
     ↓
upload ke komputer
     ↓
copy ke server
     ↓
rapikan folder
     ↓
tunggu Jellyfin

```

Cukup:

```text
Telegram
   │
   │ forward film
   ▼
Nomor B

```

Kemudian server akan melakukan sisanya.

```text
Nomor B
   ↓
watcher cek setiap 30 detik
   ↓
ada message baru
   ↓
media_downloader.py
   ↓
download
   ↓
update last_read_message_id
   ↓
storage
   ↓
Jellyfin

```

Jadi dari sisi saya:

> **Film yang mau ditangkap? Forward ke nomor B.**

Sudah.

Server akan mengecek maksimal sekitar 30 detik kemudian, menjalankan downloader, dan mengambil medianya sendiri.

---

# 12\. Berikutnya: Caption Sebagai Routing

Setelah mekanisme download stabil, sistem masih bisa dibuat lebih malas lagi.

Misalnya nomor A forward film dengan caption:

```text
indonesia

```

Watcher bisa membaca caption tersebut dan menjadikannya routing key.

Misalnya:

```text
indonesia → /home/serverkamu//MEDIA/indonesia/
jepang    → /home/serverkamu/DATA/CLOUD/MEDIA/jepang/
korea     → /home/serverkamu/DATA/CLOUD/MEDIA/korea/

```

Maka caption tidak lagi hanya menjadi keterangan.

Dia menjadi instruksi:

```text
caption
   ↓
routing
   ↓
folder tujuan

```

Untuk tahap awal, semuanya bahkan bisa diarahkan statis ke:

```text
/home/serverkamu//MEDIA/indonesia/

```

Baru nanti kalau sudah terbukti stabil, caption bisa digunakan untuk memilih folder secara otomatis.

---

# Penutup

Pada akhirnya, tujuan sistem ini sebenarnya bukan membuat sesuatu yang canggih.

Tujuannya justru sebaliknya:

**mengurangi sebanyak mungkin pekerjaan manual.**

Awalnya:

```text
Telegram
 ↓
download HP
 ↓
upload
 ↓
copy
 ↓
rapikan
 ↓
Jellyfin

```

Sekarang:

```text
Telegram
 ↓
forward ke B
 ↓
selesai.

```

Sementara di belakang layar:

```text
Nomor B
    ↓
Telethon session
    ↓
Watcher
    ↓
cek setiap 30 detik
    ↓
Downloader
    ↓
Checkpoint
    ↓
Storage
    ↓
Jellyfin

```

Dan itulah inti homelab ini.

Bukan supaya saya bisa melakukan lebih banyak pekerjaan.

Tapi supaya **server melakukan pekerjaan yang sebenarnya tidak ingin saya lakukan.**

Kalau ada film yang mau ditangkap:

> **forward ke nomor B.**

Sisanya?

**Biar server yang capek.** 😌