Hướng dẫn

Tạo Chatbot Telegram Đọc/Ghi Dữ Liệu Google Sheets Bằng Apps Script

Tuân HoangTuân Hoang
27 tháng 2, 2026
Cập nhật: 24 tháng 3, 2026
30 phút đọc
Tạo Chatbot Telegram Đọc/Ghi Dữ Liệu Google Sheets Bằng Apps Script

Telegram Bot + Google Sheets: Tại Sao Kết Hợp?

Bạn đã bao giờ muốn truy cập dữ liệu Google Sheets mọi lúc mọi nơi mà không cần mở trình duyệt? Hay muốn nhập liệu nhanh chóng chỉ bằng một tin nhắn? Telegram Bot kết hợp Google Sheets chính là giải pháp hoàn hảo. Thay vì mở laptop, đăng nhập Google, tìm file Sheets - bạn chỉ cần mở Telegram và gõ một lệnh đơn giản.

Tại sao nên kết hợp Telegram + Google Sheets?

Lợi ích cho người dùng

  • Nhập liệu mọi lúc qua điện thoại
  • Tra cứu dữ liệu không cần mở Sheets
  • Nhận thông báo tự động khi có thay đổi
  • Chia sẻ dữ liệu nhóm qua group chat
  • Không cần cài app - chỉ cần Telegram

Ứng dụng thực tế

  • Quản lý chi tiêu cá nhân / nhóm
  • Theo dõi đơn hàng, tồn kho
  • Chấm công nhân viên
  • Quản lý task / to-do list
  • Báo cáo doanh thu hàng ngày

Trong bài hướng dẫn này, chúng ta sẽ xây dựng một Telegram Bot quản lý chi tiêu hoàn chỉnh - từ tạo bot, viết Apps Script xử lý webhook, đến deploy và sử dụng. Bot sẽ hỗ trợ các lệnh: thêm chi tiêu, xem tổng hôm nay, báo cáo tháng, tìm kiếm, và xóa dòng.

Yêu cầu trước khi bắt đầu:

  • Có tài khoản Telegram (app hoặc web)
  • Có tài khoản Google (để dùng Google Sheets + Apps Script)
  • Kiến thức cơ bản JavaScript (biến, hàm, if/else, vòng lặp)
  • Khoảng 30-45 phút để hoàn thành toàn bộ

Bước 1: Tạo Bot Telegram Qua @BotFather

@BotFather là bot chính thức của Telegram dùng để tạo và quản lý bot. Mọi bot Telegram đều phải được tạo thông qua BotFather. Quá trình rất đơn giản, chỉ mất khoảng 2 phút.

Các bước tạo bot chi tiết

1

Mở Telegram, tìm @BotFather

Gõ "BotFather" vào thanh tìm kiếm. Chọn bot có dấu tick xanh xác nhận chính thức.

2

Gửi lệnh /newbot

BotFather sẽ hỏi tên hiển thị cho bot. Ví dụ: "Chi Tieu Bot" hoặc "Expense Tracker".

3

Đặt username cho bot

Username phải kết thúc bằng "bot" hoặc "_bot". Ví dụ: "chitieu_tracker_bot". Username phải duy nhất trên toàn hệ thống Telegram.

4

Lưu lại Bot Token

BotFather sẽ trả về một chuỗi token dạng: 1234567890:ABCDefGhIjKlMnOpQrStUvWxYz. Đây là "chìa khóa" để điều khiển bot - giữ bí mật tuyệt đối.

Thiết lập thêm cho bot (tùy chọn)

Sau khi tạo xong, bạn có thể gửi thêm các lệnh cho BotFather để tùy chỉnh bot:

Lệnh Chức năng Ví dụ
/setdescription Mô tả bot Bot quản lý chi tiêu kết nối Google Sheets
/setabouttext Giới thiệu ngắn Quản lý chi tiêu hàng ngày
/setuserpic Avatar cho bot Upload ảnh 512x512px
/setcommands Menu lệnh gợi ý add - Them chi tieu, today - Tong hom nay

Mẹo: Thiết lập menu lệnh

Gửi /setcommands cho BotFather, chọn bot, rồi gửi danh sách lệnh theo format: lenh - mo ta (mỗi lệnh một dòng). Telegram sẽ hiển thị menu gợi ý khi người dùng gõ "/".

Lấy Chat ID của bạn

Để bot chỉ phản hồi bạn (hoặc những người được phép), bạn cần biết Chat ID. Cách lấy:

  1. Gửi tin nhắn bất kỳ cho bot vừa tạo (ví dụ: "hello")
  2. Mở trình duyệt, truy cập: https://api.telegram.org/bot<TOKEN>/getUpdates
  3. Tìm trường "chat":{"id": 123456789} - đó chính là Chat ID của bạn
  4. Lưu lại Chat ID để dùng trong bước tiếp theo

Bước 2: Tạo Google Sheets - Cấu Trúc Dữ Liệu

Tạo một Google Spreadsheet mới và đặt tên: "Quản Lý Chi Tiêu - Telegram Bot". Sheet này sẽ là "database" cho bot.

Cấu trúc Sheet "ChiTieu"

Rename sheet đầu tiên thành "ChiTieu" và tạo header ở dòng 1:

Cột Tên Kiểu dữ liệu Mô tả Ví dụ
A ID Number Số thứ tự tự tăng 1, 2, 3...
B Ngay Date Ngày ghi nhận 2026-02-21
C MoTa Text Mô tả chi tiêu An trua
D SoTien Number Số tiền (VND) 50000
E DanhMuc Text Phân loại AnUong, DiLai, MuaSam...
F GhiChu Text Ghi chú thêm Qua Telegram Bot

Sheet "CauHinh" (tùy chọn)

Tạo thêm sheet "CauHinh" để lưu các thông số bot:

A (Key) B (Value)
BOT_TOKEN 1234567890:ABCDef...
ALLOWED_CHAT_IDS 123456789, 987654321
SHEET_NAME ChiTieu
DANH_MUC AnUong, DiLai, MuaSam, GiaiTri, SucKhoe, HocTap, Khac

Lưu ý quan trọng:

Tên sheet phải khớp chính xác với tên trong code Apps Script (phân biệt hoa thường). Nếu bạn đặt tên sheet là "ChiTieu" thì trong code cũng phải ghi "ChiTieu", không phải "chitieu" hay "Chi Tieu".

Bước 3: Viết Apps Script - Cấu Trúc Tổng Quan

Mở Google Sheets vừa tạo, vào Extensions > Apps Script. Xóa hết code mặc định và bắt đầu viết từ đầu. Chúng ta sẽ chia code thành các phần rõ ràng.

Phần 1: Cấu hình và hằng số

// ====== CAU HINH ======
var BOT_TOKEN = 'YOUR_BOT_TOKEN_HERE';
var SHEET_NAME = 'ChiTieu';
var TELEGRAM_API = 'https://api.telegram.org/bot' + BOT_TOKEN;

// Danh sach chat ID duoc phep su dung bot
var ALLOWED_USERS = [123456789]; // Thay bang Chat ID cua ban

// Danh muc chi tieu
var CATEGORIES = ['AnUong', 'DiLai', 'MuaSam', 'GiaiTri', 'SucKhoe', 'HocTap', 'Khac'];

// ====== HELPER FUNCTIONS ======
function getSheet() {
  return SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
}

function getNextId() {
  var sheet = getSheet();
  var lastRow = sheet.getLastRow();
  if (lastRow <= 1) return 1;
  var lastId = sheet.getRange(lastRow, 1).getValue();
  return Number(lastId) + 1;
}

function formatMoney(amount) {
  return amount.toString().replace(/B(?=(d{3})+(?!d))/g, ',') + ' VND';
}

function getTodayString() {
  var now = new Date();
  var dd = ('0' + now.getDate()).slice(-2);
  var mm = ('0' + (now.getMonth() + 1)).slice(-2);
  var yyyy = now.getFullYear();
  return yyyy + '-' + mm + '-' + dd;
}

Code trên định nghĩa các hằng số quan trọng: BOT_TOKEN (token từ BotFather), SHEET_NAME (tên sheet chứa dữ liệu), và các hàm tiện ích cơ bản. Hàm formatMoney giúp hiển thị số tiền đẹp hơn (50000 thành 50,000 VND).

Phần 2: Hàm gửi tin nhắn Telegram

// ====== TELEGRAM API ======
function sendMessage(chatId, text, parseMode) {
  var payload = {
    'method': 'sendMessage',
    'chat_id': String(chatId),
    'text': text,
    'parse_mode': parseMode || 'HTML'
  };

  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true
  };

  var response = UrlFetchApp.fetch(TELEGRAM_API + '/sendMessage', options);
  return JSON.parse(response.getContentText());
}

function sendMessageWithKeyboard(chatId, text, keyboard) {
  var payload = {
    'method': 'sendMessage',
    'chat_id': String(chatId),
    'text': text,
    'parse_mode': 'HTML',
    'reply_markup': JSON.stringify(keyboard)
  };

  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true
  };

  UrlFetchApp.fetch(TELEGRAM_API + '/sendMessage', options);
}

Hai hàm trên chịu trách nhiệm gửi tin nhắn từ bot đến người dùng. Hàm sendMessage gửi text thuần, còn sendMessageWithKeyboard gửi kèm inline keyboard (các nút bấm). Parse mode "HTML" cho phép dùng thẻ <b>, <i>, <code> trong tin nhắn.

Bước 4: Hàm doPost - Nhận Webhook Từ Telegram

Đây là hàm quan trọng nhất - nó nhận và xử lý mọi tin nhắn gửi đến bot. Telegram sẽ gọi hàm này mỗi khi có người gửi tin nhắn.

// ====== WEBHOOK HANDLER ======
function doPost(e) {
  try {
    var update = JSON.parse(e.postData.contents);

    // Xu ly tin nhan thuong
    if (update.message) {
      var message = update.message;
      var chatId = message.chat.id;
      var text = message.text || '';
      var userName = message.from.first_name || 'Ban';

      // Kiem tra quyen truy cap
      if (ALLOWED_USERS.indexOf(chatId) === -1) {
        sendMessage(chatId, 'Xin loi, ban khong co quyen su dung bot nay.');
        return ContentService.createTextOutput('OK');
      }

      // Phan loai lenh
      if (text.indexOf('/start') === 0) {
        handleStart(chatId, userName);
      } else if (text.indexOf('/add') === 0) {
        handleAdd(chatId, text);
      } else if (text.indexOf('/today') === 0) {
        handleToday(chatId);
      } else if (text.indexOf('/month') === 0) {
        handleMonth(chatId);
      } else if (text.indexOf('/search') === 0) {
        handleSearch(chatId, text);
      } else if (text.indexOf('/report') === 0) {
        handleReport(chatId);
      } else if (text.indexOf('/delete') === 0) {
        handleDelete(chatId, text);
      } else if (text.indexOf('/help') === 0) {
        handleHelp(chatId);
      } else {
        handleUnknown(chatId);
      }
    }

    // Xu ly callback tu inline keyboard
    if (update.callback_query) {
      handleCallback(update.callback_query);
    }

  } catch (error) {
    Logger.log('Error in doPost: ' + error.toString());
  }

  return ContentService.createTextOutput('OK');
}

Giải thích doPost:

  • e.postData.contents: Dữ liệu JSON mà Telegram gửi đến
  • update.message: Chứa thông tin tin nhắn (text, chat_id, from...)
  • ALLOWED_USERS check: Chỉ cho phép những chat_id trong danh sách sử dụng bot
  • indexOf: Kiểm tra tin nhắn bắt đầu bằng lệnh nào để gọi hàm xử lý tương ứng
  • callback_query: Xử lý khi người dùng bấm nút inline keyboard

Bước 5: Xử Lý Các Lệnh Bot

Lệnh /start và /help - Chào mừng & hướng dẫn

function handleStart(chatId, userName) {
  var text = '<b>Xin chao ' + userName + '!</b>

';
  text += 'Day la bot quan ly chi tieu ket noi Google Sheets.

';
  text += '<b>Cac lenh co ban:</b>
';
  text += '/add [mo ta] [so tien] [danh muc] - Them chi tieu
';
  text += '/today - Tong chi tieu hom nay
';
  text += '/month - Bao cao thang nay
';
  text += '/search [tu khoa] - Tim kiem
';
  text += '/report - Bao cao theo danh muc
';
  text += '/delete [id] - Xoa dong theo ID
';
  text += '/help - Xem huong dan

';
  text += '<b>Danh muc:</b> ' + CATEGORIES.join(', ');

  sendMessage(chatId, text);
}

function handleHelp(chatId) {
  var text = '<b>HUONG DAN SU DUNG BOT</b>

';
  text += '1. <b>Them chi tieu:</b>
';
  text += '   /add An trua 50000 AnUong
';
  text += '   /add Grab di lam 30000 DiLai

';
  text += '2. <b>Xem tong hom nay:</b>
';
  text += '   /today

';
  text += '3. <b>Bao cao thang:</b>
';
  text += '   /month hoac /month 2 (thang 2)

';
  text += '4. <b>Tim kiem:</b>
';
  text += '   /search grab
';
  text += '   /search an trua

';
  text += '5. <b>Bao cao chi tiet:</b>
';
  text += '   /report

';
  text += '6. <b>Xoa chi tieu:</b>
';
  text += '   /delete 5 (xoa dong co ID=5)

';
  text += '<i>Luu y: Danh muc phai la mot trong: ' + CATEGORIES.join(', ') + '</i>';

  sendMessage(chatId, text);
}

function handleUnknown(chatId) {
  sendMessage(chatId, 'Lenh khong hop le. Go /help de xem huong dan.');
}

Lệnh /add - Thêm chi tiêu mới

Đây là lệnh quan trọng nhất - thêm một dòng mới vào Google Sheets. Format: /add [mô tả] [số tiền] [danh mục]

function handleAdd(chatId, text) {
  // Parse lenh: /add Mo ta 50000 AnUong
  var parts = text.replace('/add ', '').trim();

  if (!parts || parts === '/add') {
    sendMessage(chatId, 'Sai cu phap!
Dung: /add [mo ta] [so tien] [danh muc]
Vi du: /add An trua 50000 AnUong');
    return;
  }

  // Tach cac phan
  var words = parts.split(' ');

  // Danh muc la tu cuoi cung
  var danhMuc = words[words.length - 1];

  // So tien la tu ap cuoi
  var soTien = parseInt(words[words.length - 2]);

  // Mo ta la phan con lai
  var moTa = words.slice(0, words.length - 2).join(' ');

  // Validate
  if (!moTa || isNaN(soTien) || soTien <= 0) {
    sendMessage(chatId, 'Sai cu phap!
Dung: /add [mo ta] [so tien] [danh muc]
Vi du: /add An trua 50000 AnUong');
    return;
  }

  // Kiem tra danh muc hop le
  if (CATEGORIES.indexOf(danhMuc) === -1) {
    sendMessage(chatId, 'Danh muc khong hop le!
Danh muc: ' + CATEGORIES.join(', '));
    return;
  }

  // Ghi vao sheet
  var sheet = getSheet();
  var newId = getNextId();
  var today = getTodayString();

  sheet.appendRow([newId, today, moTa, soTien, danhMuc, 'Telegram Bot']);

  // Phan hoi
  var reply = 'Da them chi tieu:

';
  reply += 'ID: <b>' + newId + '</b>
';
  reply += 'Ngay: ' + today + '
';
  reply += 'Mo ta: ' + moTa + '
';
  reply += 'So tien: <b>' + formatMoney(soTien) + '</b>
';
  reply += 'Danh muc: ' + danhMuc;

  sendMessage(chatId, reply);
}

Ví dụ sử dụng lệnh /add:

/add An trua com ga 50000 AnUong

Thêm: Ăn trưa cơm gà - 50,000 VND - Ăn uống

/add Grab di lam 32000 DiLai

Thêm: Grab đi làm - 32,000 VND - Đi lại

/add Mua sach lap trinh 250000 HocTap

Thêm: Mua sách lập trình - 250,000 VND - Học tập

/add Xem phim rap 120000 GiaiTri

Thêm: Xem phim rạp - 120,000 VND - Giải trí

Lệnh /today - Tổng chi tiêu hôm nay

function handleToday(chatId) {
  var sheet = getSheet();
  var data = sheet.getDataRange().getValues();
  var today = getTodayString();
  var total = 0;
  var items = [];

  for (var i = 1; i < data.length; i++) {
    var row = data[i];
    var rowDate = '';

    // Xu ly truong hop date la Date object hoac string
    if (row[1] instanceof Date) {
      var d = row[1];
      var dd = ('0' + d.getDate()).slice(-2);
      var mm = ('0' + (d.getMonth() + 1)).slice(-2);
      rowDate = d.getFullYear() + '-' + mm + '-' + dd;
    } else {
      rowDate = String(row[1]);
    }

    if (rowDate === today) {
      total += Number(row[3]) || 0;
      items.push({
        id: row[0],
        moTa: row[2],
        soTien: Number(row[3]) || 0,
        danhMuc: row[4]
      });
    }
  }

  if (items.length === 0) {
    sendMessage(chatId, 'Hom nay chua co chi tieu nao.');
    return;
  }

  var reply = '<b>CHI TIEU HOM NAY (' + today + ')</b>

';

  for (var j = 0; j < items.length; j++) {
    var item = items[j];
    reply += '#' + item.id + ' ' + item.moTa;
    reply += ' - ' + formatMoney(item.soTien);
    reply += ' [' + item.danhMuc + ']
';
  }

  reply += '
<b>Tong: ' + formatMoney(total) + '</b>';
  reply += '
So mon: ' + items.length;

  sendMessage(chatId, reply);
}

Lệnh /month - Báo cáo tổng tháng

function handleMonth(chatId) {
  var sheet = getSheet();
  var data = sheet.getDataRange().getValues();
  var now = new Date();
  var currentMonth = now.getMonth(); // 0-11
  var currentYear = now.getFullYear();
  var total = 0;
  var count = 0;
  var dailyTotals = {};

  for (var i = 1; i < data.length; i++) {
    var row = data[i];
    var rowDate;

    if (row[1] instanceof Date) {
      rowDate = row[1];
    } else {
      rowDate = new Date(String(row[1]));
    }

    if (rowDate.getMonth() === currentMonth && rowDate.getFullYear() === currentYear) {
      var amount = Number(row[3]) || 0;
      total += amount;
      count++;

      var dayKey = rowDate.getDate();
      if (!dailyTotals[dayKey]) dailyTotals[dayKey] = 0;
      dailyTotals[dayKey] += amount;
    }
  }

  if (count === 0) {
    sendMessage(chatId, 'Thang nay chua co chi tieu nao.');
    return;
  }

  var monthName = 'Thang ' + (currentMonth + 1) + '/' + currentYear;
  var reply = '<b>BAO CAO ' + monthName + '</b>

';
  reply += 'Tong chi tieu: <b>' + formatMoney(total) + '</b>
';
  reply += 'So giao dich: ' + count + '
';
  reply += 'Trung binh/ngay: ' + formatMoney(Math.round(total / Object.keys(dailyTotals).length)) + '

';

  reply += '<b>Chi tieu theo ngay:</b>
';
  var days = Object.keys(dailyTotals).sort(function(a, b) { return Number(a) - Number(b); });

  for (var d = 0; d < days.length; d++) {
    var day = days[d];
    reply += 'Ngay ' + day + ': ' + formatMoney(dailyTotals[day]) + '
';
  }

  sendMessage(chatId, reply);
}

Lệnh /search - Tìm kiếm trong sheet

function handleSearch(chatId, text) {
  var keyword = text.replace('/search ', '').trim().toLowerCase();

  if (!keyword || keyword === '/search') {
    sendMessage(chatId, 'Dung: /search [tu khoa]
Vi du: /search grab');
    return;
  }

  var sheet = getSheet();
  var data = sheet.getDataRange().getValues();
  var results = [];
  var total = 0;

  for (var i = 1; i < data.length; i++) {
    var row = data[i];
    var moTa = String(row[2]).toLowerCase();
    var danhMuc = String(row[4]).toLowerCase();

    if (moTa.indexOf(keyword) !== -1 || danhMuc.indexOf(keyword) !== -1) {
      results.push({
        id: row[0],
        ngay: row[1],
        moTa: row[2],
        soTien: Number(row[3]) || 0,
        danhMuc: row[4]
      });
      total += Number(row[3]) || 0;
    }
  }

  if (results.length === 0) {
    sendMessage(chatId, 'Khong tim thay ket qua cho: "' + keyword + '"');
    return;
  }

  var reply = '<b>KET QUA TIM KIEM: "' + keyword + '"</b>
';
  reply += 'Tim thay: ' + results.length + ' ket qua

';

  // Hien thi toi da 20 ket qua
  var maxShow = Math.min(results.length, 20);
  for (var j = 0; j < maxShow; j++) {
    var r = results[j];
    reply += '#' + r.id + ' ' + r.ngay + ' - ' + r.moTa;
    reply += ' - ' + formatMoney(r.soTien) + '
';
  }

  if (results.length > 20) {
    reply += '
... va ' + (results.length - 20) + ' ket qua khac
';
  }

  reply += '
<b>Tong: ' + formatMoney(total) + '</b>';

  sendMessage(chatId, reply);
}

Lệnh /report - Báo cáo chi tiết theo danh mục

function handleReport(chatId) {
  var sheet = getSheet();
  var data = sheet.getDataRange().getValues();
  var now = new Date();
  var currentMonth = now.getMonth();
  var currentYear = now.getFullYear();

  var categoryTotals = {};
  var grandTotal = 0;

  for (var i = 1; i < data.length; i++) {
    var row = data[i];
    var rowDate;

    if (row[1] instanceof Date) {
      rowDate = row[1];
    } else {
      rowDate = new Date(String(row[1]));
    }

    if (rowDate.getMonth() === currentMonth && rowDate.getFullYear() === currentYear) {
      var cat = String(row[4]) || 'Khac';
      var amount = Number(row[3]) || 0;

      if (!categoryTotals[cat]) {
        categoryTotals[cat] = { total: 0, count: 0 };
      }
      categoryTotals[cat].total += amount;
      categoryTotals[cat].count++;
      grandTotal += amount;
    }
  }

  if (grandTotal === 0) {
    sendMessage(chatId, 'Thang nay chua co chi tieu nao.');
    return;
  }

  var monthName = 'Thang ' + (currentMonth + 1) + '/' + currentYear;
  var reply = '<b>BAO CAO CHI TIET - ' + monthName + '</b>

';

  // Sap xep theo tong giam dan
  var cats = Object.keys(categoryTotals);
  cats.sort(function(a, b) {
    return categoryTotals[b].total - categoryTotals[a].total;
  });

  for (var c = 0; c < cats.length; c++) {
    var catName = cats[c];
    var catData = categoryTotals[catName];
    var percent = Math.round((catData.total / grandTotal) * 100);

    // Tao thanh progress
    var barLength = Math.round(percent / 5);
    var bar = '';
    for (var b = 0; b < barLength; b++) bar += '=';
    for (var b2 = barLength; b2 < 20; b2++) bar += '-';

    reply += '<b>' + catName + '</b>
';
    reply += '[' + bar + '] ' + percent + '%
';
    reply += formatMoney(catData.total) + ' (' + catData.count + ' giao dich)

';
  }

  reply += '<b>TONG: ' + formatMoney(grandTotal) + '</b>';

  sendMessage(chatId, reply);
}

Lệnh /delete - Xóa dòng theo ID

function handleDelete(chatId, text) {
  var idStr = text.replace('/delete ', '').trim();
  var deleteId = parseInt(idStr);

  if (isNaN(deleteId) || deleteId <= 0) {
    sendMessage(chatId, 'Dung: /delete [id]
Vi du: /delete 5');
    return;
  }

  var sheet = getSheet();
  var data = sheet.getDataRange().getValues();
  var rowToDelete = -1;
  var deletedItem = null;

  for (var i = 1; i < data.length; i++) {
    if (Number(data[i][0]) === deleteId) {
      rowToDelete = i + 1; // +1 vi data 0-indexed, sheet 1-indexed
      deletedItem = {
        moTa: data[i][2],
        soTien: Number(data[i][3]) || 0,
        danhMuc: data[i][4]
      };
      break;
    }
  }

  if (rowToDelete === -1) {
    sendMessage(chatId, 'Khong tim thay chi tieu co ID: ' + deleteId);
    return;
  }

  // Xoa dong
  sheet.deleteRow(rowToDelete);

  var reply = 'Da xoa chi tieu:

';
  reply += 'ID: <b>' + deleteId + '</b>
';
  reply += 'Mo ta: ' + deletedItem.moTa + '
';
  reply += 'So tien: ' + formatMoney(deletedItem.soTien) + '
';
  reply += 'Danh muc: ' + deletedItem.danhMuc;

  sendMessage(chatId, reply);
}

Cảnh báo với lệnh /delete:

Khi xóa dòng, ID của các dòng phía dưới sẽ không tự động cập nhật. Nếu bạn xóa dòng ID=5, các dòng ID=6,7,8... vẫn giữ nguyên số ID cũ. Đây là đặc điểm của sheet-based storage. Nếu muốn re-index, cần viết thêm hàm riêng.

Bước 6: Deploy Web App & Set Webhook

Sau khi hoàn thành code, bước tiếp theo là deploy Apps Script thành Web App và kết nối với Telegram qua webhook.

Deploy Apps Script

1

Lưu project (Ctrl + S)

Đảm bảo tất cả code đã được lưu và không có lỗi syntax.

2

Click "Deploy" > "New deployment"

Ở góc trên bên phải của Apps Script Editor.

3

Chọn type: "Web app"

Click icon bánh răng bên cạnh "Select type", chọn "Web app".

4

Cấu hình deployment

Execute as: Me (email của bạn)
Who has access: Anyone (quan trọng - để Telegram có thể gọi webhook)

5

Click "Deploy" và copy Web App URL

URL có dạng: https://script.google.com/macros/s/AKfyc.../exec

Set Webhook cho Telegram

Bước cuối cùng: nói cho Telegram biết gửi tin nhắn đến đâu. Mở trình duyệt và truy cập URL sau (thay thế TOKEN và WEBAPP_URL):

https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook?url=<YOUR_WEBAPP_URL>

Nếu thành công, bạn sẽ thấy response:

{
  "ok": true,
  "result": true,
  "description": "Webhook was set"
}

Hoặc bạn có thể set webhook bằng Apps Script (tiện hơn khi cần thay đổi):

function setWebhook() {
  var webAppUrl = 'YOUR_WEBAPP_URL_HERE';
  var url = TELEGRAM_API + '/setWebhook?url=' + encodeURIComponent(webAppUrl);

  var response = UrlFetchApp.fetch(url);
  Logger.log(response.getContentText());
}

function removeWebhook() {
  var url = TELEGRAM_API + '/deleteWebhook';
  var response = UrlFetchApp.fetch(url);
  Logger.log(response.getContentText());
}

function getWebhookInfo() {
  var url = TELEGRAM_API + '/getWebhookInfo';
  var response = UrlFetchApp.fetch(url);
  Logger.log(response.getContentText());
}

Kiểm tra webhook đã hoạt động:

  1. Mở Telegram, tìm bot bạn vừa tạo
  2. Gửi /start
  3. Nếu bot phản hồi tin nhắn chào mừng - Thành công!
  4. Thử /add Ca phe sang 35000 AnUong
  5. Kiểm tra Google Sheets - dòng mới đã được thêm

Bước 7: Nâng Cao - Inline Keyboard & Callback

Inline Keyboard là các nút bấm hiển thị ngay bên dưới tin nhắn. Khi người dùng bấm nút, Telegram gửi callback_query thay vì message thường. Tính năng này giúp bot trở nên trực quan và dễ dùng hơn.

Thêm nút chọn danh mục nhanh

function showCategoryKeyboard(chatId) {
  var keyboard = {
    'inline_keyboard': [
      [
        {'text': 'An Uong', 'callback_data': 'cat_AnUong'},
        {'text': 'Di Lai', 'callback_data': 'cat_DiLai'},
        {'text': 'Mua Sam', 'callback_data': 'cat_MuaSam'}
      ],
      [
        {'text': 'Giai Tri', 'callback_data': 'cat_GiaiTri'},
        {'text': 'Suc Khoe', 'callback_data': 'cat_SucKhoe'},
        {'text': 'Hoc Tap', 'callback_data': 'cat_HocTap'}
      ],
      [
        {'text': 'Khac', 'callback_data': 'cat_Khac'}
      ]
    ]
  };

  sendMessageWithKeyboard(chatId, '<b>Chon danh muc:</b>', keyboard);
}

function showQuickActions(chatId) {
  var keyboard = {
    'inline_keyboard': [
      [
        {'text': 'Hom nay', 'callback_data': 'action_today'},
        {'text': 'Bao cao thang', 'callback_data': 'action_month'}
      ],
      [
        {'text': 'Bao cao chi tiet', 'callback_data': 'action_report'},
        {'text': 'Huong dan', 'callback_data': 'action_help'}
      ]
    ]
  };

  sendMessageWithKeyboard(chatId, '<b>Chon thao tac:</b>', keyboard);
}

function handleCallback(callbackQuery) {
  var chatId = callbackQuery.message.chat.id;
  var data = callbackQuery.data;

  // Xu ly callback tu danh muc
  if (data.indexOf('cat_') === 0) {
    var category = data.replace('cat_', '');
    sendMessage(chatId, 'Ban da chon danh muc: <b>' + category + '</b>
Hay go: /add [mo ta] [so tien] ' + category);
  }

  // Xu ly callback tu quick action
  if (data === 'action_today') handleToday(chatId);
  if (data === 'action_month') handleMonth(chatId);
  if (data === 'action_report') handleReport(chatId);
  if (data === 'action_help') handleHelp(chatId);

  // Answer callback (tat loading indicator tren nut)
  var url = TELEGRAM_API + '/answerCallbackQuery?callback_query_id=' + callbackQuery.id;
  UrlFetchApp.fetch(url);
}

Gửi biểu đồ dạng text

Vì Telegram Bot qua webhook không hỗ trợ trực tiếp gửi chart image từ Apps Script một cách đơn giản, chúng ta có thể tạo biểu đồ text (ASCII chart) trong tin nhắn:

function createTextChart(categoryTotals, grandTotal) {
  var chart = '';
  var cats = Object.keys(categoryTotals);

  // Tim gia tri lon nhat de scale
  var maxVal = 0;
  for (var i = 0; i < cats.length; i++) {
    if (categoryTotals[cats[i]].total > maxVal) {
      maxVal = categoryTotals[cats[i]].total;
    }
  }

  // Sap xep giam dan
  cats.sort(function(a, b) {
    return categoryTotals[b].total - categoryTotals[a].total;
  });

  for (var c = 0; c < cats.length; c++) {
    var cat = cats[c];
    var total = categoryTotals[cat].total;
    var barLen = Math.round((total / maxVal) * 15);
    var bar = '';
    for (var b = 0; b < barLen; b++) bar += '|';

    var percent = Math.round((total / grandTotal) * 100);

    // Pad ten danh muc
    var name = cat;
    while (name.length < 10) name += ' ';

    chart += name + ' ' + bar + ' ' + percent + '%
';
  }

  return chart;
}

Bước 8: Bảo Mật Bot Telegram

Khi deploy web app với quyền "Anyone" (bắt buộc để Telegram gọi được webhook), bot có thể bị người lạ sử dụng. Dưới đây là các biện pháp bảo mật quan trọng:

1. Giới hạn người dùng bằng Chat ID

Chúng ta đã implement ở phần ALLOWED_USERS. Chỉ những chat_id trong danh sách mới được phép dùng bot.

2. Xác thực webhook bằng secret token

var WEBHOOK_SECRET = 'your_random_secret_string_here';

function doPost(e) {
  // Kiem tra header secret
  var secretHeader = e.parameter.secret || '';
  if (secretHeader !== WEBHOOK_SECRET) {
    return ContentService.createTextOutput('Unauthorized');
  }

  // ... phần code xử lý bình thường
}

Khi set webhook, thêm secret token:

function setWebhookWithSecret() {
  var webAppUrl = 'YOUR_WEBAPP_URL_HERE';
  var payload = {
    'url': webAppUrl,
    'secret_token': WEBHOOK_SECRET
  };

  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'payload': JSON.stringify(payload)
  };

  var response = UrlFetchApp.fetch(TELEGRAM_API + '/setWebhook', options);
  Logger.log(response.getContentText());
}

3. Log mọi request để theo dõi

function logRequest(update) {
  var logSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Logs');
  if (!logSheet) {
    logSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet('Logs');
    logSheet.appendRow(['Timestamp', 'ChatID', 'UserName', 'Command', 'RawData']);
  }

  var message = update.message || {};
  var from = message.from || {};

  logSheet.appendRow([
    new Date().toISOString(),
    message.chat ? message.chat.id : 'N/A',
    from.first_name || 'Unknown',
    message.text || 'No text',
    JSON.stringify(update).substring(0, 500)
  ]);

  // Giu log khong qua 1000 dong
  if (logSheet.getLastRow() > 1000) {
    logSheet.deleteRow(2);
  }
}

4. Rate limiting cơ bản

function checkRateLimit(chatId) {
  var cache = CacheService.getScriptCache();
  var key = 'rate_' + chatId;
  var count = cache.get(key);

  if (count && parseInt(count) >= 30) {
    return false; // Qua 30 request/phut
  }

  if (count) {
    cache.put(key, String(parseInt(count) + 1), 60);
  } else {
    cache.put(key, '1', 60);
  }

  return true;
}

// Su dung trong doPost:
// if (!checkRateLimit(chatId)) {
//   sendMessage(chatId, 'Ban gui qua nhieu lenh. Vui long doi 1 phut.');
//   return ContentService.createTextOutput('OK');
// }

Checklist bảo mật Telegram Bot

Giới hạn Chat ID trong ALLOWED_USERS Quan trọng
Không hard-code Bot Token trong script (dùng Properties) Quan trọng
Validate input (kiểm tra số tiền, format...) Khuyến nghị
Log mọi request vào sheet riêng Khuyến nghị
Rate limiting (30 request/phút) Tùy chọn
Secret token cho webhook Tùy chọn

Mẹo: Lưu Token An Toàn Với Script Properties

Thay vì hard-code Bot Token trực tiếp trong code (dễ bị lộ nếu share project), bạn nên lưu vào Script Properties:

// Luu token vao properties (chay 1 lan)
function saveToken() {
  var props = PropertiesService.getScriptProperties();
  props.setProperty('BOT_TOKEN', 'YOUR_ACTUAL_TOKEN');
  props.setProperty('ALLOWED_USERS', '123456789,987654321');
  Logger.log('Token saved!');
}

// Doc token tu properties
function getConfig() {
  var props = PropertiesService.getScriptProperties();
  return {
    botToken: props.getProperty('BOT_TOKEN'),
    allowedUsers: props.getProperty('ALLOWED_USERS').split(',').map(Number)
  };
}

// Thay doi cau hinh BOT_TOKEN va ALLOWED_USERS ban dau:
// var config = getConfig();
// var BOT_TOKEN = config.botToken;
// var ALLOWED_USERS = config.allowedUsers;

3 Ứng Dụng Khác Của Telegram Bot + Google Sheets

Ngoài quản lý chi tiêu, mô hình Telegram Bot + Google Sheets còn áp dụng được cho rất nhiều bài toán khác. Dưới đây là 3 ý tưởng phổ biến nhất:

1. Bot Quản Lý Task / To-Do List

Ý tưởng:

  • /newtask [tên task] [deadline] [người phụ trách] - Tạo task mới
  • /mytask - Xem task của mình (filter theo tên)
  • /done [id] - Đánh dấu hoàn thành
  • /overdue - Xem task quá hạn
  • /assign [id] [người] - Giao task cho người khác

Sheet cần có cột: ID, Task, Deadline, AssignTo, Status, CreatedAt, CompletedAt

function handleNewTask(chatId, text) {
  // /newtask Viet bao cao 2026-02-25 Minh
  var parts = text.replace('/newtask ', '').trim().split(' ');

  // Nguoi phu trach la tu cuoi
  var assignTo = parts[parts.length - 1];
  // Deadline la tu ap cuoi
  var deadline = parts[parts.length - 2];
  // Ten task la phan con lai
  var taskName = parts.slice(0, parts.length - 2).join(' ');

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Tasks');
  var newId = sheet.getLastRow();

  sheet.appendRow([
    newId,
    taskName,
    deadline,
    assignTo,
    'PENDING',
    new Date().toISOString(),
    ''
  ]);

  var reply = 'Task moi da tao:
';
  reply += 'ID: ' + newId + '
';
  reply += 'Task: ' + taskName + '
';
  reply += 'Deadline: ' + deadline + '
';
  reply += 'Phu trach: ' + assignTo;

  sendMessage(chatId, reply);
}

2. Bot Check Tồn Kho

Ý tưởng:

  • /stock [mã SP] - Xem tồn kho sản phẩm
  • /lowstock - Danh sách sản phẩm sắp hết
  • /import [mã SP] [số lượng] - Nhập kho
  • /export [mã SP] [số lượng] - Xuất kho
  • /stockreport - Báo cáo tồn kho tổng

Kết hợp với sheet Quản lý kho (xem bài: Quản lý kho hàng Google Sheets)

function handleStockCheck(chatId, text) {
  var maSP = text.replace('/stock ', '').trim().toUpperCase();

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('TonKho');
  var data = sheet.getDataRange().getValues();

  for (var i = 1; i < data.length; i++) {
    if (String(data[i][0]).toUpperCase() === maSP) {
      var reply = '<b>THONG TIN TON KHO</b>

';
      reply += 'Ma SP: <code>' + data[i][0] + '</code>
';
      reply += 'Ten: ' + data[i][1] + '
';
      reply += 'Ton kho: <b>' + data[i][2] + '</b> ' + data[i][3] + '
';
      reply += 'Toi thieu: ' + data[i][4] + '
';

      if (Number(data[i][2]) < Number(data[i][4])) {
        reply += '
CHU Y: Ton kho DUOI muc toi thieu!';
      }

      sendMessage(chatId, reply);
      return;
    }
  }

  sendMessage(chatId, 'Khong tim thay san pham: ' + maSP);
}

3. Bot Đặt Lịch / Booking

Ý tưởng:

  • /book [ngày] [giờ] [dịch vụ] - Đặt lịch
  • /mybook - Xem lịch hẹn của mình
  • /cancel [id] - Hủy lịch hẹn
  • /available [ngày] - Xem slot trống
  • /remind - Bật nhắc nhở trước 1 giờ

Phù hợp cho: salon, phòng khám, phòng họp, lớp học...

function handleBooking(chatId, text) {
  // /book 2026-02-25 14:00 CatToc
  var parts = text.replace('/book ', '').trim().split(' ');

  if (parts.length < 3) {
    sendMessage(chatId, 'Dung: /book [ngay] [gio] [dich vu]
VD: /book 2026-02-25 14:00 CatToc');
    return;
  }

  var ngay = parts[0];
  var gio = parts[1];
  var dichVu = parts.slice(2).join(' ');

  // Kiem tra slot da co nguoi dat chua
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Booking');
  var data = sheet.getDataRange().getValues();

  for (var i = 1; i < data.length; i++) {
    if (String(data[i][1]) === ngay && String(data[i][2]) === gio && String(data[i][5]) !== 'CANCELLED') {
      sendMessage(chatId, 'Khung gio ' + gio + ' ngay ' + ngay + ' da co nguoi dat. Vui long chon gio khac.');
      return;
    }
  }

  // Them booking
  var newId = sheet.getLastRow();
  sheet.appendRow([newId, ngay, gio, dichVu, chatId, 'CONFIRMED', new Date().toISOString()]);

  var reply = 'Dat lich thanh cong!

';
  reply += 'ID: ' + newId + '
';
  reply += 'Ngay: ' + ngay + '
';
  reply += 'Gio: ' + gio + '
';
  reply += 'Dich vu: ' + dichVu + '
';
  reply += 'Trang thai: CONFIRMED';

  sendMessage(chatId, reply);
}

Xử Lý Lỗi Thường Gặp

Trong quá trình xây dựng và sử dụng Telegram Bot, bạn sẽ gặp một số lỗi phổ biến. Dưới đây là cách xử lý:

Lỗi Nguyên nhân Cách fix
Bot không phản hồi Webhook chưa set hoặc URL sai Kiểm tra lại URL, chạy lại setWebhook
Error 403 Web app chưa deploy hoặc quyền sai Deploy lại với "Anyone" access
Bot phản hồi chậm Sheet data quá nhiều, code chưa tối ưu Dùng getRange thay getDataRange, cache kết quả
Dữ liệu ghi sai cột appendRow array không khớp header Kiểm tra thứ tự cột trong appendRow
Tin nhắn bị cắt Telegram giới hạn 4096 ký tự/tin nhắn Chia nhỏ tin nhắn, gửi nhiều lần
Date format sai Timezone Sheets khác với code Dùng Utilities.formatDate() với timezone VN
Webhook bị ngắt Code lỗi liên tục, Telegram tự ngắt Fix code, deploy mới, set webhook lại

Debug tip: Xem Execution Log

Trong Apps Script Editor, vào Executions (menu bên trái) để xem log của tất cả request. Mỗi lần Telegram gọi webhook, bạn sẽ thấy một execution record. Click vào để xem chi tiết error.

// Them try-catch va Logger.log de debug
function doPost(e) {
  try {
    Logger.log('Received: ' + e.postData.contents);
    var update = JSON.parse(e.postData.contents);
    Logger.log('Chat ID: ' + update.message.chat.id);
    Logger.log('Text: ' + update.message.text);

    // ... xu ly binh thuong

  } catch (error) {
    Logger.log('ERROR: ' + error.toString());
    Logger.log('Stack: ' + error.stack);
  }

  return ContentService.createTextOutput('OK');
}

Tối Ưu Hiệu Suất Bot

Khi dữ liệu trong sheet tăng lên (hàng trăm, hàng ngàn dòng), bot có thể phản hồi chậm. Dưới đây là các cách tối ưu:

1. Cache dữ liệu

function getCachedData() {
  var cache = CacheService.getScriptCache();
  var cached = cache.get('sheet_data');

  if (cached) {
    return JSON.parse(cached);
  }

  var sheet = getSheet();
  var data = sheet.getDataRange().getValues();

  // Cache 5 phut (300 giay)
  cache.put('sheet_data', JSON.stringify(data), 300);

  return data;
}

// Xoa cache khi ghi du lieu moi
function clearCache() {
  CacheService.getScriptCache().remove('sheet_data');
}

2. Đọc range cụ thể thay vì toàn bộ sheet

// KHONG nen:
var data = sheet.getDataRange().getValues(); // Doc het

// NEN lam:
var lastRow = sheet.getLastRow();
var data = sheet.getRange(1, 1, lastRow, 6).getValues(); // Chi doc 6 cot can thiet

// Hoac chi doc 30 ngay gan nhat:
var startRow = Math.max(2, lastRow - 100);
var data = sheet.getRange(startRow, 1, lastRow - startRow + 1, 6).getValues();

3. Batch operations

// KHONG nen (goi API nhieu lan):
for (var i = 0; i < 10; i++) {
  sheet.getRange(i + 2, 1).setValue(data[i]);
}

// NEN lam (goi API 1 lan):
var values = [];
for (var i = 0; i < 10; i++) {
  values.push([data[i]]);
}
sheet.getRange(2, 1, 10, 1).setValues(values);

Code Hoàn Chỉnh - Copy & Paste

Dưới đây là toàn bộ code gộp lại thành một file Apps Script hoàn chỉnh. Bạn chỉ cần thay BOT_TOKENALLOWED_USERS:

// ============================================
// TELEGRAM BOT + GOOGLE SHEETS
// Quan ly chi tieu ca nhan
// ============================================

// === CAU HINH (THAY DOI THEO CUA BAN) ===
var BOT_TOKEN = 'THAY_TOKEN_BOT_CUA_BAN';
var SHEET_NAME = 'ChiTieu';
var TELEGRAM_API = 'https://api.telegram.org/bot' + BOT_TOKEN;
var ALLOWED_USERS = [123456789]; // Chat ID cua ban
var CATEGORIES = ['AnUong','DiLai','MuaSam','GiaiTri','SucKhoe','HocTap','Khac'];

// === HELPER ===
function getSheet() {
  return SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
}

function getNextId() {
  var s = getSheet();
  var lr = s.getLastRow();
  if (lr <= 1) return 1;
  return Number(s.getRange(lr, 1).getValue()) + 1;
}

function formatMoney(n) {
  return n.toString().replace(/B(?=(d{3})+(?!d))/g, ',') + ' VND';
}

function getTodayString() {
  var n = new Date();
  return n.getFullYear() + '-' + ('0'+(n.getMonth()+1)).slice(-2) + '-' + ('0'+n.getDate()).slice(-2);
}

// === TELEGRAM ===
function sendMsg(chatId, text) {
  UrlFetchApp.fetch(TELEGRAM_API + '/sendMessage', {
    method:'post', contentType:'application/json',
    payload: JSON.stringify({chat_id:String(chatId), text:text, parse_mode:'HTML'}),
    muteHttpExceptions: true
  });
}

// === WEBHOOK ===
function doPost(e) {
  try {
    var u = JSON.parse(e.postData.contents);
    if (u.message) {
      var cid = u.message.chat.id;
      var txt = u.message.text || '';
      if (ALLOWED_USERS.indexOf(cid)===-1) { sendMsg(cid,'Khong co quyen.'); return ContentService.createTextOutput('OK'); }
      if (txt.indexOf('/add')===0) handleAdd(cid,txt);
      else if (txt.indexOf('/today')===0) handleToday(cid);
      else if (txt.indexOf('/month')===0) handleMonth(cid);
      else if (txt.indexOf('/search')===0) handleSearch(cid,txt);
      else if (txt.indexOf('/report')===0) handleReport(cid);
      else if (txt.indexOf('/delete')===0) handleDelete(cid,txt);
      else sendMsg(cid, 'Lenh: /add /today /month /search /report /delete');
    }
  } catch(err) { Logger.log('Error: ' + err); }
  return ContentService.createTextOutput('OK');
}

// === SET WEBHOOK (chay 1 lan) ===
function setWebhook() {
  var url = ScriptApp.getService().getUrl();
  var res = UrlFetchApp.fetch(TELEGRAM_API+'/setWebhook?url='+encodeURIComponent(url));
  Logger.log(res.getContentText());
}
// (copy cac ham handleAdd, handleToday... o tren)

Tổng kết các bước triển khai

1

Tạo bot qua @BotFather, lấy token

2

Tạo Google Sheet với cấu trúc chuẩn

3

Viết Apps Script (copy code mẫu ở trên)

4

Deploy Web App (Execute as: Me, Access: Anyone)

5

Set webhook cho Telegram

6

Test bot qua Telegram

FAQ - Câu Hỏi Thường Gặp

1. Bot có miễn phí không?

Hoàn toàn miễn phí. Telegram Bot API miễn phí không giới hạn. Google Sheets miễn phí (giới hạn 10 triệu cell/spreadsheet). Apps Script miễn phí với quota hàng ngày (90 phút runtime, 20,000 URL fetch calls). Đủ dùng cho cá nhân và nhóm nhỏ.

2. Bot có thể phục vụ bao nhiêu người cùng lúc?

Apps Script xử lý tuần tự (không parallel), nên thực tế bot phù hợp cho 1-10 người dùng. Nếu cần nhiều hơn, hãy cân nhắc dùng Google Cloud Functions hoặc server riêng với Telegram Bot API.

3. Bot ngừng hoạt động sau khi deploy lại - phải làm sao?

Mỗi lần "New deployment", URL web app sẽ thay đổi. Bạn phải set lại webhook với URL mới. Nếu chỉ muốn cập nhật code mà giữ URL cũ, hãy dùng "Manage deployments" > Edit deployment hiện tại thay vì tạo mới.

4. Có thể dùng bot trong Telegram Group không?

Có. Thêm bot vào group, bật "Group Privacy" off trong BotFather (lệnh /setprivacy). Lưu ý: chat_id của group là số âm (ví dụ: -1001234567890). Thêm group chat_id vào ALLOWED_USERS.

5. Làm sao để bot gửi thông báo tự động (không cần người dùng gửi lệnh)?

Dùng Time-driven trigger trong Apps Script. Ví dụ: trigger chạy lúc 22:00 mỗi ngày để gửi tổng kết chi tiêu. Vào Triggers (menu bên trái) > Add Trigger > chọn hàm > Time-driven > Day timer > 10pm-11pm.

Ví dụ: Trigger gửi tổng kết cuối ngày

// Tao trigger: Triggers > Add > dailySummary > Day timer > 10pm
function dailySummary() {
  var sheet = getSheet();
  var data = sheet.getDataRange().getValues();
  var today = getTodayString();
  var total = 0;
  var count = 0;

  for (var i = 1; i < data.length; i++) {
    var rowDate = '';
    if (data[i][1] instanceof Date) {
      var d = data[i][1];
      rowDate = d.getFullYear() + '-' + ('0'+(d.getMonth()+1)).slice(-2) + '-' + ('0'+d.getDate()).slice(-2);
    } else {
      rowDate = String(data[i][1]);
    }

    if (rowDate === today) {
      total += Number(data[i][3]) || 0;
      count++;
    }
  }

  var msg = '<b>TONG KET CHI TIEU HOM NAY</b>

';
  msg += 'Ngay: ' + today + '
';
  msg += 'So giao dich: ' + count + '
';
  msg += 'Tong chi: <b>' + formatMoney(total) + '</b>

';
  msg += 'Chuc ban ngu ngon!';

  // Gui cho tat ca user
  for (var u = 0; u < ALLOWED_USERS.length; u++) {
    sendMsg(ALLOWED_USERS[u], msg);
  }
}

Quản Lý Dữ Liệu Chuyên Nghiệp Hơn?

Telegram Bot + Google Sheets phù hợp cho cá nhân và nhóm nhỏ. Khi doanh nghiệp cần hệ thống quản lý bán hàng chuyên nghiệp với báo cáo realtime, phân quyền, đa kênh - hãy thử SheetStore.

SheetStore tích hợp Google Sheets + phần mềm quản lý bán hàng chuyên nghiệp. Truy cập sheet.com.vn để dùng thử miễn phí.

Chia sẻ bài viết:

Tuân Hoang

Tuân Hoang

Đội ngũ SheetStore

Bạn thấy bài viết hữu ích?

Đăng ký nhận thông báo khi có bài viết mới.

Nhận thông báo khi có bài viết mới. Không spam, hứa luôn! 😊

Bình luận (0)

Vui lòng đăng nhập để tham gia thảo luận