Telegram Bot Basic – no framework – [pỐ dệCh nửA nGày]

Hôm nay là ngày 3/9, là một ngày nghỉ, vì sáng nay dậy trời sáng quá, hay là lúc gần trưa trời có mưa hay gì đấy, đại khái là vì cảm thấy thời tiết không ủng hộ nên mình nghỉ thêm 3/9. Nên viết văn nhảm.

Sooo, Hôm nay chúng ta sẽ làm một con Bot Telegram đúng chuẩn khung xương, không cần đọc quá nhiều tài liệu bên lề đâu các bạn nhỏ.

Đây không toàn toàn tính là một project, nó chỉ là một bài test của mình vào việc tích hơp Telegram vào hệ thống công ty. Thật ra thì về cơ bản hệ thống hoàn toàn có thể tích hợp thành công nếu sửa đổi đôi chút.

Yêu cầu đề bài, điểm danh cho các nhân sự onsite bằng Telegram bot.

Các phần chính sẽ như sau:

  1. Cung cấp các kiến thức cơ bản về Telegram bot. 
  2. Luồng chính tạo bot.
  3. Luồng chạy của bot.
  4. Code và phân tích.

Ối Dôi Ôi Lại Là Sách Giáo Khoa Vờ Lờ.

I. Cung cấp các kiến thức mẫu giáo về Bot Telegram ( chỉ những kiến thức cơ bản dính tới bài toán, thích làm to hay làm hẳn hoi khuyên các bạn nên lên doc tele mà đọc cũng như sử dụng Telegram bot Frame ).

Như chúng ta đã biết hiện nay Telegram là một nền tảng chat rất được ưa chuộng trong cộng đồng công nghệ, vì chuyện bảo mật (có thể), khả năng tùy biến cao cũng như không giới hạn của nó. Tại đây chúng ta sẽ làm một con bot hay cơ bản gọi là tương tác API với Telegram. Các phần cần lưu ý:

  •  Bot Telegram tham gia vào cuộc thảo luận với tư cách như một user, có nghĩa việc bạn control bot có ảnh hưởng bởi việc bạn cấp quyền view cho bản thân con bot đấy như một user. Phần lớn các trường hợp thì chúng ta phải sửa thiết lập của group chat mà bot lấy thông tin xử lý. Trong bài này vì lười nên mình cấp luôn cho con bot quyền admin, vì mình thích, ý kiến lên phường.
  • Bot Telegram không đăng nhập bằng user/password mà dùng token để định danh, một token duy nhất giảm thiểu độ khó trong coding nhưng ngược lại vấn đề bảo mật cần phải đề phòng, đừng để mất token hoặc là PAY ACC.
  • Bot Telegram có 2 cơ chế tương tác chính, Update và Webhook. Nói cách khác một là cơ chế update hoạt động bot phải tự get dữ liệu và xử lý ngược lại webhook cung cấp trigger từ phía bot kích hoạt backend code. Mình chọn dùng Update vì mình không muốn mỗi lần deploy Bot lên lại phải sửa webhook ở phía telegram cũng như việc xử lý ssl, lười.
  • Dữ liệu tương tác API Bot trả về JSON. Thế là đỡ hơn bao nhiêu rồi.
II. Luồng chính tạo bot.

Bot telegram được tạo bằng việc tương tác với @BotFather trong telegram. Ừ search google đi. Cố lên

Tạo xong chúng ta sẽ lấy được token của bot. Còn không tạo được thì bạn nhỏ nên về trồng cá và nuôi thêm rau đi.

III. Luồng chạy của bot.

Như đã nói lòi bản họng ở trên chúng ta có 2 cơ chế Update và Webhook. Nhưng vì lười nên mình nói Update thôi.

Tương tác với Bot chúng ta cơ bản là tương tác với server chứa bot (dĩ nhiên), vai trò của code chúng ta viết coi như là cung cấp thêm tầng business cho bot để tùy biến nó theo yêu cầu của bản thân. Nói cách khác bạn chỉ là một mắt xích trong hệ thống, bỏ đi cũng không sao, nó vô dụng hệt như cuộc đời của bạn vậy.

 Rồi vào đề:

1. Việc đầu tiên để kiểm tra và lấy dữ liệu của một con bot chúng ta cần phải thử kết nối và nhận token của nó. Phương pháp là sử dụng brower hoặc dễ hơn thì dùng postman để check get dữ liệu mới.

Url sẽ là : https://api.telegram.org/bot<TOKEN>/getUpdates

Và nếu trình bạn không quá gà chỉ cần cỡ búa đôi thôi thì kết quả nó sẽ thế này.

   

   Dạng trả về của URL là JSON và cũng dễ dàng nhận ra các thành phần tham gia của nó. Thôi thì đưa cho các bạn cái cho dễ cop mà xem.

 

{
    "ok": true,
    "result": [
        {
            "update_id": 9293684,
            "message": {
                "message_id": 26,
                "from": {
                    "id": 1276069150,
                    "is_bot": false,
                    "first_name": "Cù",
                    "last_name": "Xuân Bách",
                    "language_code": "vi"
                },
                "chat": {
                    "id": 1276069150,
                    "first_name": "Cù",
                    "last_name": "Xuân Bách",
                    "type": "private"
                },
                "date": 1599112712,
                "text": "."
            }
        }
    ]
}
  Đấy. Chúng ta đã lấy được dữ liệu rồi..

2.  Lấy được rồi thế gửi thì sao. Đơn giản.

Url : https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=<CHAT_ID>&text=Hello%20World

CHAT_ID lấy từ getUpdates ý các bạn nhỏ ạ.

IV. Vào code nào. Vì lười nên mình sẽ nói sơ sơ qua thôi nhá. Vì tới đây nó clear lắm rồi.

Trong bài này mình dùng Python, không phải vì mình thuộc Slytherin mà vì nó deploy dễ, viết nhanh, đơn giản.

  1. Về ý tưởng code thì con bot này do cơ chế là update lên sẽ liên tục check update_id của bot. Nếu update_id thay đổi có nghĩa là dữ liệu mới.. dĩ nhiên
    Def nhẹ con fuc  lấy thông tin lần update cuối.
      #func get update
    def last_update(req):
    response = requests.get(req + 'getUpdates')
    response = response.json()
    result = response['result']
    total_updates = len(result) -1
    return result[total_updates] #get last record
  2. Tất nhiên là con bot có nhỏ sao thì tác vụ của nó ở đây cần thiết phải có DB. Tất nhiên vì nó nhỏ nên mình dùng luôn TinyDB.

Ây khoan đã, sao mình phải phân tích sạch code nhở, ngắn vãi ra ý. thôi code đây copy đi rồi phân tích.

# PSObotAttendance

import csv
from tinydb import TinyDB, Query
db = TinyDB('db.json')
query = Query()

url = "https://api.telegram.org/bot<token>/"

#db insert
def insert(user_id,user_state):
db.insert({'user_id': user_id, 'user_state': user_state})

#db insert group
def insertx(groupid,groupname):
db.insert({'groupid': groupid, 'groupname': groupname})

#search db response json
def search(user_id):
result = db.search(query.user_id == user_id)
return result

#search db response json
def searchx(groupid):
result = db.search(query.groupid == groupid)
return result

#update_db
def updatedb(user_id,user_state_update):
db.update({'user_state': user_state_update}, query.user_id == user_id)

#get chat bot current chat id
from pip._vendor import requests
from datetime import datetime

#get group chat id
def get_chat_id(update):
chat_id = update['message']['chat']['id']
return chat_id

#get id
def get_user_id(update):
user_id = update['message']['from']['id']
return user_id

#get group chat title
def get_chat_title(update):
chat_title = update['message']['chat']['title']
return chat_title

#get chat current text
def get_messenge_text(update):
messenge = update['message']
messenge_text = messenge.get('text','notext')
return messenge_text

#func get update
def last_update(req):
response = requests.get(req + 'getUpdates')
response = response.json()
result = response['result']
total_updates = len(result) -1
return result[total_updates] #get last record

#fuc lest bot send mess
def send_message(chat_id,note,messenge_text):
params = {"chat_id":chat_id, "text": note + messenge_text }
response = requests.post(url + "sendMessage", data=params)
return response

#fuc get time messager
def get_time(update):
unixtime = update['message']['date']
time = datetime.utcfromtimestamp(unixtime).strftime('%d-%m-%Y %H:%M:%S')
return time

#fuc get name
def get_name(update):
firstnamedic = update['message']['from']
firstname = firstnamedic.get('first_name','')
lastnamedic = update['message']['from']
lastname = lastnamedic.get('last_name','')
fullname = firstname +' '+lastname
return fullname

#fuc scv
def scv_append(filename,fields):
with open(filename+'.csv', 'a',newline='\n', encoding='utf-8') as csvfile:
str1 = ""
for ele in fields:
str1 += ele
csvfile.write(str1)
csvfile.write('&&')

#main func chat
def main():
update_id = last_update(url)["update_id"] #get last update def update id

while True:
update = last_update(url) #check if last update
if update_id == update["update_id"]: #wait for new update
current_user_id = get_user_id(update)

state = 0 # state = 0 - not working / state = 1 - working

if not len(search(current_user_id)):
insert(current_user_id, state)
else:
#state = search(current_user_id)[0]
dict = search(current_user_id)[0]
state =dict["user_state"]

if get_messenge_text(update).lower() == "/checkin@psodailyattendancebot" and state == 0:
updatedb(current_user_id, 1)
name = get_name(update)
group = get_chat_title(update)
time = get_time(update)

send_message(get_chat_id(update),'Đồng chí '+name+' công tác tại '+group+' đã checkin giờ làm việc, thời gian ', time)

fields=[name,time,'checkin']
#scv_append(group,fields)
elif get_messenge_text(update).lower() == "/checkout@psodailyattendancebot" and state == 1:
updatedb(current_user_id, 0)
name = get_name(update)
group = get_chat_title(update)
time = get_time(update)

send_message(get_chat_id(update), 'Đồng chí ' + name + ' công tác tại '+group+' đã checkout nghỉ làm, thời gian ', time)

#fields = [name, time, 'checkout']
#scv_append(group, fields)

elif get_messenge_text(update).lower() == "/checkout@psodailyattendancebot" and state == 0:
send_message(get_chat_id(update), 'bạn chưa checkin.','')
elif get_messenge_text(update).lower() == "/checkin@psodailyattendancebot" and state == 1:
send_message(get_chat_id(update), 'bạn cần checkout trước khi tham gia ca làm việc khác.','')

update_id += 1 #wait for new update

main()

**** Lưu ý:

  1. Code trên là code thối, cấu trúc xây dựng của mình hiện tại chỉ hỗ trợ 1 group chat. Ban thân bài toán với mình để test tương thích cũng như mình không định đọc doc về frame của Telegram. Thật ra việc phân tách Json đây vốn là không cần thiết do frame này đã gần hoàn thiện nhưng mình không phải dev telegram for life nên là thôi kệ đi.
  2. Dữ liệu time trả về là timestamp nên phải đổi ra mà dùng nhé các bạn.
  3. Lỗi về việc ép nguyên một Json to tổ bố ra Dictionaries sẽ có thể lỗi nếu bạn call vào một trường không tồn tại vì vậy trong code mình có một số đoạn kiểu lastnamedic.get('last_name','') đây là mình ép default value nếu trường không tồn tại. Nếu bạn tự code thì dễ gặp lỗi này.
  4. ờ có vài đoạn csv lúc đầu mình lười làm db định làm logs nhưng thôi csv bỏ nhá.

Create Telegram Bot

Leave a Reply

Your email address will not be published. Required fields are marked *