0

Tạo BOT giúp quản lý chi tiêu cá nhân sử dụng telegram và google sheets

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

main.py

"""
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.pyadd.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
  1. add.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
  1. credentials.py
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 image.png


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí