Нам дають картинки та опис товарів, і з них потрібно зробити каталог.
Цей каталог буде використовуватися телеграм-ботом, який блукає каталогами і пропонуватиме користувачеві товари, відображаючи картинки та опції для навігації по каталогу.
Картинки та текстові файли складатимемо в каталоги.
У нас буде загальний каталог, у якому кожен бот матиме свій власний, названий його ім’ям.
У каталозі бота буде багато підкаталогів, у кожному з яких буде картинка товару та текстовий файл message.txt з інструкціями для робота зі створення повідомлення при попаданні в цей каталог.
Цей файл містить вміст кожного повідомлення бота оформленого у вигляді xml документа.
Наприклад.
<message>Hello! Welcome</message>
<button value="path_to_one_step">
Do you want to see the catalog?
</button>
<button value="path_to_second_step">
No thank you
</button>
З тегів button будуть створені кнопки, за якими бот стрибатиме в інші директорії, вказані в їхньому атрибуті value.
Для роботи нам знадобляться такі бібліотеки:
python-telegram-bot - для використання API Telegram;
bs4 - для парсингу xml текстового файлу.
Створимо робочий каталог.
mkdir tbot
cd tbot
У ньому файл requirements.txt де перерахуємо необхідні залежності.
python-telegram-bot==12.0.0b1
bs4
Встановимо віртуальне оточення і встановимо пакети.
virtualenv -p python3 venv
source ./venv/bin/activate
pip install -r requirements.txt
Напишемо скрипт інсталяції робота install.py.
На початку створимо нову порожню директорію storage, де зберігатимемо каталоги ботів, тому їх може бути кілька.
Потім, поставимо користувачеві 2 питання з проханням вказати:
Ім’я робота.
Секретний ключ.
На ім’я бота будемо створювати підкаталоги в папці storage.
Код інсталятора.
#!/usr/bin/env python
import os
print('Устанавливаем бот!')
DATA_DIR = 'storage'
if __name__ == '__main__': # если запуск из консоли
# Створюємо каталог storage якщо його немає
if not os.path.exists(DATA_DIR):
print('Створюю директорію %s' % DATA_DIR)
os.makedirs(DATA_DIR
nname = input('Вкажіть ім'я бота:')
Формуємо шлях до каталогу бота
bot_path = %s/%s % (DATA_DIR, name)
if not os.path.exists(bot_path):
os.makedirs(bot_path)
if not os.path.exists(bot_path+'/index'):
print('Створюю директорію для бота %s' % name)
os.makedirs(bot_path+'/index')
else:
print("Такой бот с именем %s уже есть!" % bot_path)
key = input('Укажите ключ: ')
# записуємо секретний ключ у файл
f = open(%s/key % bot_path, w+)
f.write(key)
f.close()
print("Все готово. Для активації бота запустіть команду ./run.py %s" % name)
Результат роботи із введеним не валідним ключем.
Потрібно знайти контакт BotFather.
Написати команду /newbot
Підібрати унікальне ім’я, після чого вам надішле повідомлення такого виду.
Done! Congratulations on your new bot. You will find it at t.me/zdimon77_bot. You can now add a description,
about section and profile picture for your bot, see /help for a list of commands.
By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it.
Just make sure the bot is fully operational before you do this.
Use this token to access the HTTP API:
829228816:AAGSjgoh0hfj-fwyMBo4UlGvGxHtnp6Z_сs
Keep your token secure and store it safely, it can be used by anyone to control your bot.
For a description of the Bot API, see this page: https://core.telegram.org/bots/api
Почнемо писати робота у файлі run.py.
Він запускатиметься з одним параметром - ім’ям бота.
Спочатку імпортуємо все, що нам потрібно.
import sys
import os
from telegram.ext import Updater
from telegram.ext import CommandHandler, CallbackContext, CallbackQueryHandler
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram import Bot
from bs4 import BeautifulSoup
from install import DATA_DIR
Забираємо ім’я бота з першого аргументу командного рядка, а якщо його немає, то повідомимо про це і вивалимося з програми.
try:
botname = sys.argv[1]
except:
print("Введите в качестве аргумента имя бота например ./run.py my_bot")
sys.exit()
Знайдемо секретний ключ та використовуючи його, створимо об’єкт бота.
print("Запускаю бота %s" % botname)
bot_path = '%s/%s' % (DATA_DIR,botname)
print("Читаю настройки из %s" % bot_path)
f = open(bot_path+'/key','r')
key = f.read()
f.close()
print("Ключ: %s" % key)
bot = Bot(token=key)
Визначимо функцію start, яка спрацьовуватиме коли користувач натисне кнопку Start у програмі Telergam.
def start(update: Updater, context: CallbackContext):
print("Start command!")
У цю функцію буде передано два об’єкти update та context.
Ми будемо використовувати update для отримання інформації про користувача.
Наприклад, отримати його логін та ідентифікатор кімнати можна так:
username = update.message.from_user['username']
room_id = update.message.chat_id
Ми будемо використовувати цей ідентифікатор для надсилання повідомлень у канал користувача.
Для того, щоб прив’язати цю функцію до обробника події натискання на кнопку “Старт”, потрібно створити об’єкт-обробник класу CommandHandler та “годувати” йому нашу функцію start.
start_handler = CommandHandler('start', start)
Перший параметр це ім’я команди, у разі це зарезервоване ім’я, але може бути довільним і визначено програмістом.
Контейнер для обробників буде об’єкт updater.
updater = Updater(token=key, use_context=True)
Додаємо обробник у контейнер та запускаємо нескінченний процес опитування telegram кімнати бота.
updater.dispatcher.add_handler(start_handler)
updater.start_polling()
Повний код програми.
#!/usr/bin/env python
import sys
import os
from telegram.ext import Updater
from telegram.ext import CommandHandler, CallbackContext, CallbackQueryHandler
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram import Bot
from bs4 import BeautifulSoup
from install import DATA_DIR
try:
botname = sys.argv[1]
except:
print("Введіть як аргумент ім'я бота наприклад ./run.py my_bot")
sys.exit()
print("Запускаю бота %s" % botname)
bot_path = %s/%s % (DATA_DIR,botname)
print("Читаю налаштування з %s" % bot_path)
f = open(bot_path+'/key','r')
key = f.read()
f.close()
bot = Bot(token=key)
def start(update: Updater, context: CallbackContext):
print("Start command!")
username = update.message.from_user['username']
room_id = update.message.chat_id
print("Новий користувач %s room_id: %s" % (username, room_id))
start_handler = CommandHandler('start', start)
updater = Updater(token=key, use_context=True)
updater.dispatcher.add_handler(start_handler)
updater.start_polling()
Результат праці.
Пробуємо надіслати ботом повідомлення привітання.
Це робиться функцією bot.send_message(), в яку, крім повідомлення, передається ідентифікатор кімнати.
def start(update: Updater, context: CallbackContext):
print("Start command!")
username = update.message.from_user['username']
room_id = update.message.chat_id
print("Новый пользователь %s room_id: %s" % (username, room_id))
bot.send_message(room_id, 'Привет!')
Якщо треба надіслати картинку, то зробити це можна іншою функцією.
bot.send_photo(chat_id=room_id, photo=open(img_path, 'rb'))
Тепер ми знаємо як надсилати повідомлення та картинки.
Давайте розбиратися з кнопками і як їх посилати?
Спочатку необхідно визначитися з їх компонуванням.
Допустимо, ми закинули кнопки в список, наприклад:
lst = ['btn1', 'btn2', 'btn3', 'btn4']
Щоб розташувати їх у два ряди по дві, необхідно з цього списку сформувати таку структуру:
[['btn1', 'btn2'], ['btn3', 'btn4']]
Ось невелика функція, яка це робить.
def build_menu(buttons,n_cols):
menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)]
return menu
Тут я використовую генератор списку, який формує список із рядом вкладених списків за кількістю другого параметра.
Сама кнопка створюється такою конструкцією.
from telegram import InlineKeyboardButton
btn = InlineKeyboardButton('Натисни мене',callback_data='button_pressed')
При створенні ми визначаємо назву кнопки та корисне навантаження, яке отримаємо після натискання і зможемо визначити, яка саме кнопка була натиснута.
Відправимо 4 тестові кнопки по 2 до ряду.
список із кнопками
btn_list = []
# закидаю 4 штучки
for cnt in range(0,4):
btn_list.append(InlineKeyboardButton('Натисніть мене %s' % cnt,callback_data='button_%s_pressed' % cnt))
# формую об'єкт розмітки із класу InlineKeyboardMarkup
markup_list = InlineKeyboardMarkup(build_menu(btn_list,n_cols=2))
# посилаю кнопки
bot.send_message(room_id, 'Кнопки', reply_markup=markup_list)
Сигнатура функції-обробника для кнопок виглядає за аналогією зі start
def press_button(update: Updater, context: CallbackContext):
print("Pressing button %s" % update.callback_query.data)
Тільки зв’язування обробника відрізняється використанням класу CallbackQueryHandler, який прийме нашу функцію.
updater.dispatcher.add_handler(CallbackQueryHandler(press_button))
Тепер залишилося зв’язати це все разом, і змусити бота переміщатися каталогами і підбирати їх вміст.
У новій функції navigate, яка буде викликатись при натисканні на кнопку, я робитиму наступне:
Переходити у потрібний каталог.
Посилати картинку image.png якщо вона там є.
Прочитати файл message.txt.
Посилати вміст тега message та кнопки в тегах button.
Код функції.
def navigate(command, chat_id):
path = '%s/%s' % (bot_path,command)
img_path = '%s/image.png' % path
print(img_path)
# шлю картинку если нашел
if os.path.isfile(img_path):
bot.send_photo(chat_id=chat_id, photo=open(img_path, 'rb'))
message_path = path+'/message.txt'
# находим файл message.txt
if os.path.isfile(message_path):
with open(message_path) as f:
but_txt = f.read()
# парсим xml
soup = BeautifulSoup(but_txt, 'html.parser')
msg = soup.find('message')
btns = soup.findAll('button')
btn_lst = []
# формуємо кнопки
for bt in btns:
btn_lst.append(InlineKeyboardButton(bt.text,callback_data=bt['value']))
button_list = InlineKeyboardMarkup(build_menu(btn_lst,n_cols=1))
# посилаємо кнопки та повідомлення
bot.send_message(chat_id, msg.text, reply_markup=button_list)
Останній пункт використовує бібліотеку BeautifulSoup і реалізовано в такий спосіб.
with open(message_path) as f:
but_txt = f.read()
soup = BeautifulSoup(but_txt, 'html.parser')
msg = soup.find('message') # один элемент
btns = soup.findAll('button') # несколько
Повний код робота:
#!/usr/bin/env python3
import sys
import os
from telegram.ext import Updater
from telegram.ext import CommandHandler, CallbackContext, CallbackQueryHandler
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram import Bot
from bs4 import BeautifulSoup
from install import DATA_DIR
try:
botname = sys.argv[1]
except:
print("Введіть як аргумент ім'я бота наприклад./run.py my_bot")
sys.exit()
print(“Запускаю бота %s” % botname) bot_path = %s/%s % (DATA_DIR,botname) print(“Читаю налаштування з %s” % bot_path) f = open(bot_path+’/key’,’r’) key = f.read() f.close() bot = Bot(token=key)
def navigate(command, chat_id):
path = '%s/%s' % (bot_path,command)
img_path = '%s/image.png' % path
print(img_path)
# шлю картинку якщо знайшов
if os.path.isfile(img_path):
bot.send_photo(chat_id=chat_id, photo=open(img_path, 'rb'))
message_path = path+'/message.txt'
# знаходимо файл message.txt'
# знаходимо файл message.txt
if os.path.isfile(message_path):
with open(message_path) as f:
but_txt = f.read()
# парсим xml
soup = BeautifulSoup(but_txt, 'html.parser')
msg = soup.find('message')
btns = soup.findAll('button')
btn_lst = []
# формируем кнопки
for bt in btns:
btn_lst.append(InlineKeyboardButton(bt.text,callback_data=bt['value']))
button_list = InlineKeyboardMarkup(build_menu(btn_lst,n_cols=1))
# посилаємо кнопки та повідомлення
bot.send_message(chat_id, msg.text, reply_markup=button_list)
def build_menu(buttons,n_cols):
menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)]
return menu
def press_button(update: Updater, context: CallbackContext):
print("Pressing button %s" % update.callback_query.data)
navigate(update.callback_query.data,update.callback_query.message.chat_id)
def start(update: Updater, context: CallbackContext):
print("Start command!")
username = update.message.from_user['username']
room_id = update.message.chat_id
navigate('index',update.message.chat_id)
start_handler = CommandHandler('start', start)
updater = Updater(token=key, use_context=True)
updater.dispatcher.add_handler(start_handler)
updater.dispatcher.add_handler(CallbackQueryHandler(press_button))
updater.start_polling()
Результат праці.
У статті розглянуто процес створення телеграм-бота мовою Python. В його основу покладено принцип навігації по каталогах та відправлення повідомлень, що містять текст, картинки. та кнопки для навігації до наступного каталогу. Висвітлено механізми управління ботом і реакції на події користувача.
Не вистачає функціонала з прийняття контактної інформації від користувача зі збереженням замовлень у базі даних та повідомлення адміністратора. Про це намагатимуся розповісти у наступній статті.