1. Giới thiệu
Trước hết, hãy để tôi giới thiệu về ứng dụng mà chúng ta sẽ xây dựng. Đó chính là một BOT có khả năng quản lý chi tiêu cá nhân thông qua việc sử dụng Telegram và tích hợp Google Sheets. BOT sẽ giúp bạn ghi lại các khoản chi tiêu theo tháng và năm. Dưới đây là phiên bản siêu đơn giản PoC để các bạn có thể liên kết được các service có sẵn và tạo thành 1ứng dụng cho cá nhân
2. Công nghệ sử dụng
- Ngôn ngữ: Python, JS
- Framework: Flask, Google Script
- Database: Google Sheets
- Công nghệ: Webhook, API
- Công cụ: Telegram, Google Sheets, Google Script, Ngrok
3. Chi tiết
3.1. Xây dựng webhook cho telegram
Đầu tiên, bạn cần tạo một BOT trên Telegram (hãy tìm hiểu cách làm này trên Internet). Sau đó, bạn sẽ tạo một API để khi bạn gửi tin nhắn cho BOT, Telegram sẽ gửi yêu cầu đến API này. Dưới đây là ví dụ cơ bản:
Để thực hiện việc này, bạn cần cài đặt các thư viện sau đây:
pip install flask telepot requests
"""
Author: vitqst
Description: This script receives a message from telegram then call API to google sheets to add a new row to specific sheet.
"""
from flask import Flask, request
import telepot
from telebot.credentials import bot_token, URL, SECRET_NUM
from telebot.add import add global bot
global TOKEN
TOKEN = bot_token
bot = telepot.Bot(TOKEN)
bot.setWebhook(URL.format(SECRET_NUM), max_connections=1) app = Flask(__name__) """
Define a function to handle the message from telegram
forexample: - /start: send a welcome message - /add {amount} {note}: add a new row to google sheets
"""
@app.route('/{}'.format(SECRET_NUM), methods=["POST"])
def telegram_webhook(): update = request.get_json() if "message" in update: chat_id = update["message"]["chat"]["id"] if "text" in update["message"]: text = update["message"]["text"] # bot.sendMessage(chat_id, "From the web: you said '{}'".format(text)) if text == "/start": welcome_message = "Welcome to my bot!" bot.sendMessage(chat_id, welcome_message) elif text.startswith("/add"): add(bot, text, chat_id) else: bot.sendMessage(chat_id, "From the web: sorry, I didn't understand that kind of message") else: bot.sendMessage(chat_id, "From the web: sorry, I didn't understand that kind of message") return "OK" @app.route('/get_me', methods=['GET', 'POST'])
def get_me(): return bot.getMe() @app.route('/', methods=['GET', 'POST'])
def index(): return 'Service is running!' if __name__ == '__main__': # your app to have more than one thread app.run(threaded=True)
Bạn cũng cần tạo thêm một thư mục và hai tệp tin credentials.py và add.py để quản lý thông tin và chứa các hàm cần thiết như sau:
mkdir telebot
touch telebot/credentials.py
touch telebot/add.py
touch telebot/__init__.py
import datetime
import requests """
bot: bot object
text: text from telegram 1. This function will add a new row to google sheets
you should create sheetName on google sheets before using this function 2. Where the enduser will type /add {amount} {long text note}
and the bot will add a new row to google sheets 3. For example: /add 1000 mua bim bim
"""
import re
def add(bot, text, chat_id): try: amount = int(re.search(r'/add\s+(\d+)', text).group(1)) * 1000 name = re.search(r'/add\s+\d+\s+(.*)', text).group(1) date = datetime.datetime.now().strftime("%m/%d/%Y") sheetName = "{}/{}".format(datetime.datetime.now().month, datetime.datetime.now().year) action = "add" # add except Exception as e: print(e) bot.sendMessage(chat_id, "Sorry, I didn't understand that kind of message") return # TODO you should change the url of your google script here url = "" data = {"name":name, "amount":amount , "date": date, "sheetName": sheetName} # Start request to google script then get response to send back to telegram r = requests.post(url, json=data) # sometime bot.sendMessage() doesn't work so we will ignore it try: bot.sendMessage(chat_id, "Response from google '{}'".format(r.text)) except Exception as e: print(e) # for DEBUG purpose print(r.text) # for DEBUG purpose print(data) # for DEBUG purpose
from dotenv import load_dotenv
import os load_dotenv() bot_token = os.getenv("BOT_TOKEN", "xxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
bot_user_name = os.getenv("BOT_USER_NAME", "vitqst_bot")
URL = os.getenv("URL", "https://example.com/{}")
SECRET_NUM = os.getenv("SECRET_NUM", "xxxx")
OK giờ chúng ta có thể test thử chương trình bằng cách chạy lệnh
flask run --port=5000 # you can change port number for your purpose
curl localhost:5000 # you should see "Service is running!" on your terminal
OK Done backend cho telegram BOT rồi, giờ chúng ta sẽ tạo google script để xử lý request từ telegram BOT
Ngoài ra khi develop bạn cần ngrok để test trên localhost khi đó ngrok sẽ tạo một url public cho bạn để proxy request từ telegram BOT đến localhost của bạn có thể develop và test
ví dụ
ngrok http 5000 # sẽ tạo một url public cho bạn và proxy request từ telegram BOT tới localhost:5000 có thể sử dụng localhost:4000 để debug
3.2. Xây dựng google script
- Tạo một google sheet và tạo 1 sheet có tên là "9/2023"
- Tạo một google script và copy code dưới đây vào
/** * This script is running on script.google.com * * receive data from client then write to spreadsheet * * sample data: '{"name":"Tiền bim bim", "amount":15000000 , "date": "11/9/2023"}' */
function doPost(e) { var data = JSON.parse(e.postData.contents); if (data.action == "add") { return add(e); } if (data.action == "delete_row") { return delete_row(e); } return ContentService.createTextOutput("Invalid action");
} function add(e) { // TODO you should change the spreadsheetId here by your spreadsheetId --> copy from url --> https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/edit#gid=0 var spreadsheetId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' var data = JSON.parse(e.postData.contents); // validate data here must have name, amount, date, type, source if (!data.name || !data.amount || !data.date ) { return ContentService.createTextOutput("Missing required fields"); } var [name, amount, date ] = [ data.name, data.amount, data.date ]; var sheetName = data.sheetName; // get sheet by name var sheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName(sheetName); // scan for empty row in column A var range = sheet.getRange("A:A"); var values = range.getValues(); var row = 0; for (var i = 0; i < values.length; i++) { var rowValue = values[i][0]; if (rowValue == "" || rowValue == null || rowValue == undefined) { row = i + 1; break; } } // create new row data var newRow = [name, amount, date]; // write to spreadsheet sheet.getRange(row, 1, 1, newRow.length).setValues([newRow]); // copy format from row 1 to new row sheet.getRange(row, 1, 1, newRow.length).copyFormatToRange(sheet, 1, newRow.length, row, row); // return response to client return ContentService.createTextOutput(`Add ${name} - ${Number(amount).toLocaleString()}đ ${date} at row ${row}`);
}
- Publish google script
- Publish --> Deploy as web app --> Project version: New --> Execute the app as: Me --> Who has access to the app: Anyone, even anonymous --> Deploy
- copy đoạn url vào file
add.py
- thử nghiệm bot bằng cách gửi tin nhắn
/add 1000 mua bim bim
cho bot - Kiểm tra trên google sheet xem có dòng mới được thêm vào không
kết quả cuối cùng bạn có thể thêm những hàm hoặc những cách tính toán riêng ở trên google sheet