Lewati ke konten
Skematik

Kirim Notifikasi Telegram Otomatis dari Google Spreadsheet pakai Apps Script

9 menit baca · Diperbarui 2026-07-18

Kamu mau tahu tiap ada order baru masuk, atau tiap stok turun di bawah batas, tanpa harus buka spreadsheet dan mantengin tab. Cara paling cepat: sambungkan Google Sheet ke bot Telegram. Begitu ada baris baru atau sel berubah, ponselmu bunyi.

Yang bikin enak, semua ini jalan dari dalam Google Apps Script. Nggak perlu server, nggak perlu hosting. Cukup satu fungsi UrlFetchApp yang nembak Bot API Telegram, plus satu trigger. Di bawah ini alurnya dari nol: bikin bot, ambil chat_id, kirim pesan, lalu bikin otomatis. Semua kode di sini sudah dites di runtime V8 dan bisa langsung copy.

Kenapa Telegram, bukan yang lain

Bot API Telegram gratis, tanpa kuota pesan resmi, dan setupnya paling ringan dibanding channel notifikasi lain. Kamu cuma butuh dua hal: token bot dan chat_id tujuan. Setelah itu satu HTTP POST beres.

Kalau target notifikasimu pelanggan (bukan tim internal), pertimbangkan kirim WhatsApp dari spreadsheet atau email otomatis. Telegram paling pas buat notif internal: alert order, alarm stok, log error harian ke grup tim.

Langkah 1: Bikin bot lewat BotFather

Buka Telegram, cari akun resmi @BotFather (ada centang biru). Kirim perintah ini satu per satu:

  1. Ketik /newbot lalu tekan kirim.
  2. Kasih nama bot (bebas, misal Notif Toko Saya).
  3. Kasih username bot, wajib diakhiri kata bot, misal notif_tokosaya_bot.
  4. BotFather balas dengan token, bentuknya seperti 123456789:AAH-xxxxxxxxxxxxxxxxxxxxxxxx.

Token itu kunci akses penuh ke botmu. Siapa pun yang pegang token bisa kirim pesan atas nama botmu. Jangan pernah taruh di kode yang di-share, dan jangan commit ke repo. Nanti kita simpan di Script Properties, bukan di badan kode.

Langkah 2: Dapatkan chat_id tujuan

Token menentukan bot mana yang kirim. chat_id menentukan ke mana pesan dikirim. Bot Telegram nggak bisa mulai chat duluan, jadi kamu harus nyapa bot dulu.

Untuk chat pribadi: buka bot yang barusan kamu buat, tekan Start atau kirim sembarang pesan (misal halo). Untuk grup: tambahkan bot ke grup, lalu kirim satu pesan di grup itu. Baru setelah itu chat_id bisa dibaca.

Cara paling cepat cari angka chat_id: kirim /start ke @userinfobot, dia langsung balas ID kamu. Tapi cara yang paling akurat (dan jalan juga untuk grup) adalah panggil endpoint getUpdates dari Apps Script. Buka Extensions lalu Apps Script di spreadsheetmu, tempel fungsi ini, jalankan, lalu lihat Logs:

function cariChatId() {
  const token = PropertiesService.getScriptProperties().getProperty('TELEGRAM_TOKEN');
  const url = 'https://api.telegram.org/bot' + token + '/getUpdates';

  const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  Logger.log(res.getContentText());
}

Di respons JSON-nya, cari bagian "chat":{"id": ... }. Angka itu chat_id kamu. Untuk chat pribadi angkanya positif; untuk grup biasanya negatif (dan bisa diawali -100 untuk supergroup). Salin apa adanya, termasuk tanda minus.

Langkah 3: Simpan token dengan aman

Sebelum nulis fungsi kirim, taruh token dan chat_id di Script Properties. Ini penyimpanan key-value bawaan Apps Script yang nggak ikut kebaca kalau kodenya kamu bagikan. Jalankan fungsi di bawah SEKALI, lalu kosongkan lagi nilainya biar nggak nyangkut di editor:

// Jalankan SEKALI untuk menyimpan kredensial.
// Setelah jalan, kosongkan nilainya di kode ini.
function simpanKredensial() {
  PropertiesService.getScriptProperties().setProperties({
    TELEGRAM_TOKEN: '123456789:AAH-isi-token-dari-BotFather',
    TELEGRAM_CHAT_ID: '987654321'
  });
  Logger.log('Kredensial tersimpan.');
}

Sekali jalan, nilainya nyimpen di proyek. Kalau mau lihat atau ubah lewat UI, buka Project Settings (ikon gerigi) lalu scroll ke Script Properties. Dari sini seterusnya, kode cukup baca pakai getProperty, nggak ada token yang nempel di badan fungsi.

Langkah 4: Fungsi kirimTelegram() yang bisa dipakai ulang

Ini inti semuanya. Satu fungsi yang terima teks, tembak endpoint sendMessage, dan cek balasannya. Perhatikan muteHttpExceptions: true, itu bikin UrlFetchApp nggak lempar exception saat respons non-200, jadi kamu bisa baca kode statusnya sendiri dan log error yang jelas.

function kirimTelegram(pesan) {
  const props = PropertiesService.getScriptProperties();
  const token = props.getProperty('TELEGRAM_TOKEN');
  const chatId = props.getProperty('TELEGRAM_CHAT_ID');

  if (!token || !chatId) {
    throw new Error('TELEGRAM_TOKEN / TELEGRAM_CHAT_ID belum diset di Script Properties.');
  }

  const url = 'https://api.telegram.org/bot' + token + '/sendMessage';
  const options = {
    method: 'post',
    payload: {
      chat_id: chatId,
      text: pesan,
      parse_mode: 'HTML'
    },
    muteHttpExceptions: true
  };

  const res = UrlFetchApp.fetch(url, options);
  const code = res.getResponseCode();

  if (code !== 200) {
    Logger.log('Telegram gagal (' + code + '): ' + res.getContentText());
  }
  return code;
}

// Tes cepat setelah kredensial diset.
function tesKirim() {
  kirimTelegram('<b>Tes</b> dari spreadsheet. Kalau ini masuk, setup beres.');
}

Jalankan tesKirim dari editor. Pertama kali, Apps Script minta izin (authorization) karena UrlFetchApp akses jaringan luar. Klik izinkan pakai akunmu. Kalau pesan masuk ke Telegram, berarti token, chat_id, dan koneksi semua beres.

Soal payload: karena objek payload dikirim sebagai form biasa (application/x-www-form-urlencoded), semua nilainya string sederhana, jadi aman tanpa JSON.stringify. Kalau nanti butuh struktur bertingkat (misal inline keyboard), baru pindah ke contentType: 'application/json' dengan payload JSON.stringify(...).

Soal parse_mode: HTML dulu, MarkdownV2 belakangan

parse_mode: 'HTML' bikin kamu bisa pakai <b>tebal</b>, <i>miring</i>, dan <code>kode</code>. Buat pemula ini paling anti-ribet. MarkdownV2 kelihatan simpel tapi galak: karakter seperti titik, tanda minus, kurung, dan seru WAJIB di-escape pakai backslash, kalau tidak Telegram balas 400 Bad Request. Kalau nggak butuh format sama sekali, hapus saja baris parse_mode, teks dikirim polos dan nggak ada yang bisa bikin error format.

Langkah 5: Bikin otomatis dengan trigger

Sekarang fungsi kirim sudah ada, tinggal disambung ke kejadian di spreadsheet. Ada dua pola yang paling sering dipakai: reaksi saat sel diedit, dan pengecekan terjadwal. Yang penting kamu paham beda simple trigger dan installable trigger, karena di sinilah orang paling sering nyangkut.

Pola A: kirim saat sel diedit (installable onEdit)

Kasih fungsimu nama yang BUKAN onEdit, misal saatDiedit, supaya nggak ketuker jadi simple trigger. Contoh ini kirim notif cuma kalau kolom Status (kolom E) diisi BARU:

function saatDiedit(e) {
  // e undefined kalau fungsi dijalankan manual dari editor.
  if (!e || !e.range) return;

  const sheet = e.range.getSheet();
  if (sheet.getName() !== 'Order') return;

  // Kolom E (ke-5) = Status. Hanya reaksi kalau diisi 'BARU'.
  if (e.range.getColumn() !== 5) return;
  if (e.value !== 'BARU') return;

  const baris = e.range.getRow();
  const data = sheet.getRange(baris, 1, 1, 4).getValues()[0];

  const pesan =
    '<b>Order baru masuk</b>\n' +
    'No: ' + data[0] + '\n' +
    'Nama: ' + data[1] + '\n' +
    'Total: ' + data[3];

  kirimTelegram(pesan);
}

e.range itu sel yang barusan diubah, dan e.value isi barunya (cuma tersedia kalau yang diedit satu sel). Karena kita cek getSheet dan getColumn duluan, fungsi ini langsung keluar untuk edit di sheet atau kolom lain, jadi hemat kuota.

Pasang triggernya lewat UI: di editor Apps Script, klik ikon jam alarm (Triggers) di panel kiri, lalu Add Trigger. Pilih function saatDiedit, event source From spreadsheet, event type On edit. Simpan, izinkan sekali. Sejak itu tiap edit manggil fungsimu dengan izin penuh.

Pola B: cek baris baru terjadwal (time-driven)

Kadang baris masuk bukan dari ketikan manusia, tapi dari Google Form, dari API, atau dari script lain. onEdit nggak selalu kepicu di kasus begitu. Lebih andal: jalankan pengecekan tiap beberapa menit dan tandai baris yang sudah dikirim biar nggak dobel.

function cekBarisBaru() {
  const sheet = SpreadsheetApp.getActive().getSheetByName('Order');
  const data = sheet.getDataRange().getValues();

  // Kolom F (indeks 5) = penanda 'terkirim'.
  for (let i = 1; i < data.length; i++) {
    const penanda = data[i][5];
    const nama = data[i][1];

    if (penanda === '' && nama !== '') {
      const code = kirimTelegram('Order baru: ' + nama);
      if (code === 200) {
        sheet.getRange(i + 1, 6).setValue('terkirim');
        Utilities.sleep(200); // jeda kecil antar-kirim
      }
    }
  }
}

Kolom penanda ini penting. Tanpa itu, tiap eksekusi bakal kirim ulang semua baris. Karena kita cuma setValue jadi terkirim saat kode balik 200, baris yang gagal kirim tetap dicoba lagi ronde berikutnya. Buat time trigger-nya lewat menu Triggers yang sama, event type Time-driven, pilih interval (misal tiap 5 menit). Detail soal ini ada di panduan menjadwalkan script dengan trigger.

Baru mulai dan bingung baca-tulis sel? Mampir dulu ke cara baca dan tulis data sheet dan Apps Script untuk pemula biar pola getRange dan getValues di atas kebaca gampang.

Penanganan error yang bikin kamu tidur nyenyak

Kode yang kirim ke jaringan luar pasti sesekali gagal: internet Telegram lagi lambat, token dicabut, chat_id salah. Karena kita pakai muteHttpExceptions dan cek getResponseCode, kamu punya kendali. Beberapa kode status yang sering muncul:

  • 200: sukses, pesan terkirim.
  • 400: payload salah, sering karena parse_mode MarkdownV2 tanpa escape, atau text kosong.
  • 401: token salah atau sudah dicabut.
  • 403: bot diblokir user, atau bot belum jadi anggota grup tujuan.
  • 429: kena rate limit, kirim terlalu cepat. Kasih jeda dengan Utilities.sleep.

Buat notif penting, bungkus panggilan di try/catch dan simpan log kegagalan ke satu sheet khusus, atau kirim ringkasan error harian ke Telegram lewat time trigger terpisah. Yang jelas, jangan biarkan satu kegagalan kirim menghentikan seluruh proses batch, itu sebabnya loop di Pola B tetap lanjut ke baris berikutnya.

Jaga kuota UrlFetchApp

Tiap pesan Telegram = satu panggilan UrlFetchApp, dan panggilan itu dibatasi kuota harian. Akun Gmail gratis dapat sekitar 20.000 panggilan UrlFetchApp per hari; akun Workspace lebih besar. Kedengarannya banyak, tapi kalau time trigger jalan tiap menit dan tiap eksekusi ngirim puluhan pesan, angkanya cepat habis.

Trik hematnya: kirim cuma saat kondisi benar-benar terpenuhi (pakai penanda seperti di Pola B), gabung beberapa alert jadi satu pesan kalau bisa, dan set interval trigger sewajarnya (5 menit biasanya cukup, nggak perlu tiap menit). Angka batas lengkap untuk tiap layanan ada di rangkuman batas kuota Apps Script.

Rangkai jadi produk beneran

Notif Telegram jarang berdiri sendiri. Biasanya dia satu potong dari alat kecil: pencatat order, monitor stok, atau antrean tugas yang tinggal di spreadsheet. Kalau kamu lagi bikin yang begitu, mulai dari kerangkanya dulu, baru tempel notifikasi ini di titik yang tepat.

Di Skematik, mode Web GAS bantu kamu ubah ide jadi PRD terstruktur khusus aplikasi Google Apps Script: sheet apa saja, kolom, alur trigger, sampai titik notifikasi. Lihat contoh nyatanya di template kasir toko berbasis spreadsheet, lalu tinggal isi sesuai kebutuhanmu.

Buat PRD Web GAS gratis

Bot, token, chat_id, satu fungsi kirim, satu trigger. Itu seluruh mesinnya. Pasang sekali, dan spreadsheet-mu bakal ngoceh ke Telegram tiap ada yang perlu kamu tahu. Sekarang tinggal tentukan: kejadian mana yang layak bikin ponselmu bunyi.

Notifikasi Telegram Otomatis via Apps Script — Skematik