Телеграм бот для e-commerce на Python и Fauna

В данной статье вы узнаете, как создать Телеграм бот для малого бизнеса. Этот бот по своим возможностям будет очень похож на популярную платформу WhatsApp Business. Мы рассмотрим, как реализовать этот функционал и настроить нашу базу данных в бессерверной системе Fauna.

Прежде чем мы начнем, давайте договоримся, что далее мы будем называть этого бота Telegram-business.

От редакции Pythonist. Возможно, вас также заинтересует статья «Создание телеграм-бота с веб-интерфейсом при помощи Python и Replit».

Что Telegram-business сможет делать

Прежде всего заметим, что есть два типа пользователей: клиенты и владельцы бизнеса. Возможности бота классифицируются в зависимости от типа пользователей, которым разрешено работать на платформе. Ниже перечислены потребительские свойства Telegram-Business:

Для клиентов

Пользователь может:

  • видеть четыре различных категории товаров или услуг;
  • просматривать товары или услуги в данных категориях;
  • сделать заказ любого товара.

Для владельцев бизнеса

Пользователь может:

  • создавать каталог товаров
  • добавлять в каталог товары и услуги;
  • получать уведомление при размещении заказа.

Необходимые приготовления

В данной статье мы будем использовать Python 3 и платформу Fauna. Соответственно, чтобы следовать инструкциям этого руководства, вам необходимо установить Python и иметь аккаунт в Fauna.

Если аккаунта в Fauna у вас еще нет, можно бесплатно зарегистрироваться.

И разумеется, в ваших интересах иметь хорошие знания языка Python: без этого может быть трудно разобраться.

Для хранения изображений мы будем использовать платформу Cloudinary. Вы также можете бесплатно зарегистрировать на ней свой аккаунт.

Итак, полный список необходимого:

  • Python 3
  • Аккаунт в Fauna
  • Аккаунт в Cloudinary.
python logo

Марк Лутц «Изучаем Python»

Скачивайте книгу у нас в телеграм

×

Готовим базу данных

Первое, что нам необходимо сделать, это создать базу данных в Fauna. Перейдите на сайт fauna.com и войдите в панель управления. Сделав это, нажмите на кнопку «New database», чтобы создать базу данных.

Это действие приведет вас на страницу, которую вы видите на скриншоте. Чтобы создать новую базу данных для нашего бота, заполните все необходимые поля и нажмите на кнопку «Save».

Создание коллекций

Далее, для каждой сущности нашей платформы, а именно для пользователей (Users), владельцев бизнеса (Business) и товаров (Products), мы создадим по одной коллекции. Нажмите на кнопку «New collection» и вы попадете на страницу, подобную той, что приведена ниже.

Назовем нашу первую коллекцию Users и нажмем кнопку «Save». После этого таким же образом создадим еще две коллекции: Business и Products. Сделав это, вы должны видеть все три коллекции в меню, как это показано ниже.

Теперь перейдем к созданию индексов для удобного получения данных из наших коллекций.

Создание индексов

Для быстрого извлечения информации для нашей системы электронной коммерции нам необходимо создать несколько индексов. Чтобы это сделать, перейдите в меню индексов и нажмите на кнопку «New index». Это приведет вас на страницу, вид которой показан ниже:

Первый индекс, который мы хотим создать, позволит нам находить пользователей по их имени. Мы выберем исходную коллекцию, в данном случае Users, и дадим нашему индексу имя “user_by_name”.

Затем мы выбираем условие, по которому мы будем производить поиск, то есть вводим «name» — имя пользователя. Остальные поля можно оставить пустыми. После этого ваша страница будет выглядетьтак, как показано на скриншоте:

Таким образом мы создадим три индекса со следующими условиями:

  • business_by_name: {исходная коллекция: Business, условие: name}
  • product_by_business: {исходная коллекция: Product, условие: sme}
  • business_by_category: {исходная коллекция: Business, условие: category}

Создание API-ключа (API_key)

Чтобы посылать запросы к нашей базе данных через интернет, нам необходимо сгенерировать API-ключ.

Перейдите в раздел «Security options» (Безопасность) и нажмите кнопку «New key» (Новый ключ). Далее нажмите на кнопку «Save», скопируйте сгенерированный ключ и сохраните его пока в каком-нибудь текстовом файле. Позже он нам пригодится.

Создание бота

Диалог с BotFather

Чтобы создать Телеграм бот, мы должны для начала зарегистрировать его у «бота-отца» в Телеграм (далее BotFather). Найдите в Телеграм BotFather или нажмите здесь для перехода к нему.

Оказавшись в чате с этим ботом, введите «/ start», чтобы начать разговор с BotFather. Он покажет вам список команд, которые можно использовать для взаимодействия с ним. Эти команды можно просто ввести с клавиатуры или щелкнуть по ним для их выполнения.

Для создания нового бота выбираем команду /newbot. После этого BotFather попросит нас ввести имя бота и имя пользователя. Как только мы это сделаем, BotFather предоставит нам информацию о новом боте, например ссылку на бота и токен доступа, который нам будет нужен. Скопируйте все это и где-нибудь сохраните.

Написание кода для бота Telegram-Business

Мы начнем с создания виртуального окружения для нашего проекта. Для этого мы переходим в командную строку.

Выбираем нужную нам директорию. Предпочтительно, чтобы это была новая пустая директория.

Затем мы создадим виртуальное окружение. Для этого можно использовать любой менеджер виртуальной среды Python. Мы будем использовать virtualenv. Чтобы создать виртуальную среду под названием env, мы выполним следующую команду:

virtualenv env

Затем мы активируем нашу виртуальную среду и установим туда следующие пакеты Python:

  • Python-telegram-bot: этот пакет поможет нам контролировать наш бот.
  • Faunadb: для взаимодействия с API Fauna.
  • python-dotenv: для удобства работы с конфигурационными переменными.
  • Cloudinary: клиентская библиотека для работы с API хранилища фотографий. Не забудьте создать там аккаунт и получить API-ключ и секретный ключ. Они нам впоследствии пригодятся.

Мы начнем с создания следующих файлов в папке нашего проекта:

  • handlers.py
  • main.py
  • config.py
  • .env

Разделим реализацию модуля handlers на две части:

  • функционал клиента;
  • функционал владельцев бизнеса.

Что же, начнем с функционала, который будет отвечать за взаимодействие с ботом пользователей — владельцев бизнеса. Сперва нам нужно добавить наши токены и ключи от botFather, cloudinary и Fauna в файл .env следующим образом:

BOT_TOKEN=***486**:*******-****ro
API_SECRET=-****OcAGV****
API_KEY=********21
FAUNA_KEY=f****8sZ*****J****Gd4****Q

Сделав это, перейдем к файлу config.py. Там мы создадим переменные и присвоим им значения из файла .env. Это мы сделаем при помощи модуля python-dotenv, установленного нами ранее. Этот пакет дает нам возможность читать переменные среды прямо из файлов и даже из командной строки.

import os
from dotenv import load_dotenv

load_dotenv()

TOKEN = os.getenv('BOT_TOKEN')
api_secret = os.getenv('API_SECRET')
api_key = os.getenv('API_KEY')
FAUNA_KEY = os.getenv('FAUNA_KEY')

Теперь мы можем использовать эти переменные в нашем коде без чтения из файла .env.

Приступаем к созданию наших методов-обработчиков. Они будут управлять взаимодействиями с пользователями в Телеграм.

Начнем с методов-обработчиков, отвечающих за управление взаимодействием бота с пользователями, которые регистрируются как владельцы бизнеса. Данный код будет находится в файле handlers.py.

Сперва мы импортируем необходимые библиотеки.

from telegram import (
    ReplyKeyboardMarkup,
    ReplyKeyboardRemove, Update,
    InlineKeyboardButton, InlineKeyboardMarkup
)
from telegram.ext import (
    CommandHandler, CallbackContext,
    ConversationHandler, MessageHandler,
    Filters, Updater, CallbackQueryHandler
)
from config import (
    api_key, sender_email,
    api_secret,
    FAUNA_KEY
)
import cloudinary
from cloudinary.uploader import upload
from faunadb import query as q
from faunadb.client import FaunaClient
from faunadb.errors import NotFound

Нам нужно добавить шаблонную конфигурацию для некоторых из наших зависимостей. Поэтому после импорта библиотек добавьте следующее:

# configure cloudinary
cloudinary.config(
    cloud_name="curiouspaul",
    api_key=api_key,
    api_secret=api_secret
)

# fauna client config
client = FaunaClient(secret=FAUNA_KEY)

# Define Options
CHOOSING, CLASS_STATE, SME_DETAILS, CHOOSE_PREF, \
    SME_CAT, ADD_PRODUCTS, SHOW_STOCKS, POST_VIEW_PRODUCTS = range(8)

Первая конфигурация предназначена для экземпляра Cloudinary. Вторая — для экземпляра клиента Fauna.

Следующие за ними переменные представляют состояния бота. Ну, или (по крайней мере) состояние, в котором он может находиться, когда с ним взаимодействуют пользователи.

Этого требует метод conversationhandler. Он позволяет нам создавать сценарии для наших ботов, делая их взаимодействие с людьми более естественным.

Имена каждой переменной прозрачно указывают, какое состояние они представляют. Например, состояние SME_DETAILS привязано к определенным методам запроса у пользователя деталей их бизнеса.

Добавляем методы обработчика

Теперь приступим к добавлению в наш код методов-обработчиков. Сначала добавим метод, который обрабатывает то, что происходит при инициации диалога с ботом с помощью команды /start.

Следующие строки кода вызываются, когда кто-то использует команду / start в нашем боте, поэтому добавим их в код обработчика.

def start(update, context: CallbackContext) -> int:
    print("You called")
    bot = context.bot
    chat_id = update.message.chat.id
    bot.send_message(
        chat_id=chat_id,
        text= "Hi fellow, Welcome to SMEbot ,"
        "Please tell me about yourself, "
        "provide your full name, email, and phone number, "
        "separated by comma each e.g: "
        "John Doe, JohnD@gmail.com, +234567897809"
    )
    return CHOOSING

Посмотрите, как мы возвращаем новое состояние разговора бота. Ранее мы его уже предопределили. Эта функция отправляет вводное сообщение и запрашивает информацию у пользователя.

Следующее состояние называется CHOOSING (выбор). Оно связано с другим методом, который принимает вводимые пользователем данные и анализирует их. Метод проверяет, что данные верны, прежде чем использовать их для регистрации пользователя.

Затем пользователю предлагается выбрать, кем он является: клиентом или владельцем бизнеса. Для этого добавим следующие строки кода:

# get data generic user data from user and store
def choose(update, context):
    bot = context.bot
    chat_id = update.message.chat.id
    # create new data entry
    data = update.message.text.split(',')
    if len(data) < 3 or len(data) > 3:
        bot.send_message(
            chat_id=chat_id,
            text="Invalid entry, please make sure to input the details "
            "as requested in the instructions"
        )
        bot.send_message(
            chat_id=chat_id,
            text="Type /start, to restart bot"
        )
        return ConversationHandler.END
    #TODO: Check if user already exists before creating new user
    new_user = client.query(
        q.create(q.collection('User'), {
            "data":{
                "name":data[0],
                "email":data[1],
                "telephone":data[2],
                "is_smeowner":False,
                "preference": "",
                "chat_id":chat_id
            }
        })
    )
    context.user_data["user-id"] = new_user["ref"].id()
    context.user_data["user-name"] = data[0]
    context.user_data['user-data'] = new_user['data']
    reply_keyboard = [
    [
        InlineKeyboardButton(
            text="SME",
            callback_data="SME"
        ),
        InlineKeyboardButton(
            text="Customer",
            callback_data="Customer"
        )
    ]
  ]
  markup = InlineKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
    bot.send_message(
        chat_id=chat_id,
        text="Collected information succesfully!..🎉🎉 \n"
        "Which of the following do you identify as ?",
        reply_markup=markup
    )
    return CLASS_STATE

Эта функция принимает вводимые пользователем данные, парсит их и использует результат для сохранения информации о пользователе в нашей базе данных Fauna. Для отправки почтового запроса в Fauna API, который и выполняет запись в базу данных, используется метод запроса из библиотеки Fauna.

Мы также храним некоторую информацию о пользователе в памяти с помощью атрибута context.user_data[], который нам понадобится позже в других методах обработчика.

Наконец, мы возвращаем ответ, а также новое состояние разговора. В этом состоянии (CLASS_STATE) мы определяем, с каким именно пользователем мы имеем дело: с клиентом или владельцем бизнеса.

def classer(update, context):
    bot = context.bot
    chat_id = update.callback_query.message.chat.id
    name = context.user_data["user-name"]
    if update.callback_query.data.lower() == "sme":
        # update user as smeowner
        client.query(
            q.update(
                q.ref(q.collection("User"), context.user_data["user-id"]),
                {"data": {"is_smeowner":True}}
            )
        )
        bot.send_message(
            chat_id=chat_id,
            text=f"Great! {name}, please tell me about your business, "
            "provide your BrandName, Brand email, Address, and phone number"
            "in that order, each separated by comma(,) each e.g: "
            "JDWears, JDWears@gmail.com, 101-Mike Avenue-Ikeja, +234567897809",
            reply_markup=ReplyKeyboardRemove()
        )

        return SME_DETAILS
    categories = [  
        [
            InlineKeyboardButton(
                text="Clothing/Fashion",
                callback_data="Clothing/Fashion"
            ),
            InlineKeyboardButton(
                text="Hardware Accessories",
                callback_data="Hardware Accessories"
            )
        ],
        [
            InlineKeyboardButton(
                text="Food/Kitchen Ware",
                callback_data="Food/Kitchen Ware"
            ),
            InlineKeyboardButton(
                text="ArtnDesign",
                callback_data="ArtnDesign"
            )
        ]
    ]
    bot.send_message(
        chat_id=chat_id,
        text="Here's a list of categories available"
        "Choose one that matches your interest",
        reply_markup=InlineKeyboardMarkup(categories)
    )
    return CHOOSE_PREF

Если пользователь идентифицирует себя как клиента, функция отправляет ему другой ответ.

У нас есть меню категорий, в котором клиент может выбирать, что просмотреть.

Если пользователь идентифицирует себя как клиента, эти категории отправляются ему в качестве ответа, и возвращается новое состояние, связанное с клиентским функционалом.

Давайте добавим обработчик команды cancel (отменить), чтобы пользователи могли в любой момент прекратить взаимодействие с ботом. Добавим следующую функцию:

# Control
def cancel(update: Update, context: CallbackContext) -> int: 
    update.message.reply_text(
        'Bye! I hope we can talk again some day.',
        reply_markup=ReplyKeyboardRemove()
    )

    return ConversationHandler.END

На этом этапе мы уже можем протестировать наш Телеграм бот, чтобы увидеть, как он реагирует на наши действия. Для этого нам нужно зарегистрировать наши обработчики с помощью метода обработчика разговора в main.py и сопоставить их с каждым состоянием следующим образом:

import handlers
from telegram.ext import (
    CommandHandler, CallbackContext,
    ConversationHandler, MessageHandler,
    Filters, Updater, CallbackQueryHandler
)
from config import TOKEN

updater = Updater(token=TOKEN, use_context=True)
print(updater)
dispatcher = updater.dispatcher


def main():
    conv_handler = ConversationHandler(
        entry_points=[CommandHandler('start', handlers.start)],
        states={
            handlers.CHOOSING: [
                MessageHandler(
                    Filters.all, handlers.choose
                )
            ],
            handlers.CLASS_STATE: [
                CallbackQueryHandler(handlers.classer)
            ]
        },
        fallbacks=[CommandHandler('cancel', handlers.cancel)],
        allow_reentry=True
    )
    dispatcher.add_handler(conv_handler)
    updater.start_polling()
    updater.idle()

if __name__ == '__main__':
    main()

Сохраним это, перейдем в терминал и запустим нашего бота:

Запустив эту команду, вы можете не увидеть немедленного запроса от терминала, пока не начнете взаимодействовать с ботом.

Пойдем к нашему боту и начнем разговор. Используйте ссылку, которую предоставил вам botFather, или просто найдите ваш Телеграм бот по имени (в приложении).

Найдя свой бот, используйте команду /start или просто нажмите кнопку пуск в чате, чтобы ваш бот начал разговор. Вы увидите, что бот отреагирует следующим образом:

Следуя подсказке, введите необходимую информацию и отправьте ее в виде сообщения. Это активирует следующее состояние разговора. В нем вас попросят выбрать, кем вы являетесь: владельцем бизнеса или клиентом.

Если выберете вариант SME (small and medium business — малый и средний бизнес), вам будет предложено ввести сведения о вашем бизнесе. Это прописано в методе classer(), который мы определили ранее.

А если выберете вариант Customer (клиент), то бот ответит списком категорий на выбор.

На данный момент это все, на что способен наш бот, ведь мы еще не добавили методы-обработчики для остальных запланированных функций. Поэтому продолжим написание кода нашего бота.

Ниже приведен ряд методов-обработчиков, которые отвечают за весь бизнес-функционал. Добавьте их в указанном ниже порядке. Мы приводим эти методы, описывая их работу. Некоторые из них могут включать в себя более одной функции.

Сбор и хранение бизнес-информации

Для этого есть две функции. Первая принимает вводимые пользователем данные о бизнесе, анализирует их, сохраняет в памяти и передает данные следующему методу обработчика. Затем она предлагает пользователю выбрать категорию, к которой относится его бизнес.

А вторая берет все данные и использует их для создания нового бизнес-документа в нашем экземпляре Faunadb. Затем предлагает пользователю добавить новый продукт. Далее функция возвращает новое состояние разговора, связанное с методами обработчика, отвечающими за добавление продуктов во вновь созданный бизнес.

def business_details(update, context):
    bot = context.bot
    chat_id = update.message.chat.id
    data = update.message.text.split(',')
    if len(data) < 4 or len(data) > 4:
        bot.send_message(
            chat_id=chat_id,
            text="Invalid entry, please make sure to input the details "
            "as requested in the instructions"
        )
        return SME_DETAILS
    context.user_data["sme_dets"] = data
    # categories = [
    #         ['Clothing/Fashion', 'Hardware Accessories'],
    #         ['Food/Kitchen Ware', 'ArtnDesign'],
    #         ['Other']
    # ]
    categories = [  
        [
            InlineKeyboardButton(
                text="Clothing/Fashion",
                callback_data="Clothing/Fashion"
            ),
            InlineKeyboardButton(
                text="Hardware Accessories",
                callback_data="Hardware Accessories"
            )
        ],
        [
            InlineKeyboardButton(
                text="Food/Kitchen Ware",
                callback_data="Food/Kitchen Ware"
            ),
            InlineKeyboardButton(
                text="ArtnDesign",
                callback_data="ArtnDesign"
            )
        ]
    ]
    markup = InlineKeyboardMarkup(categories, one_time_keyboard=True)
    bot.send_message(
        chat_id=chat_id,
        text="Pick a category for your business from the options",
        reply_markup=markup
    )
    return SME_CAT

def business_details_update(update, context):
    bot = context.bot
    chat_id = update.callback_query.message.chat.id
    choice = update.callback_query.data
    # create business
    new_sme = client.query(
        q.create(
            q.collection("Business"),
            {"data":{
                "name":context.user_data["sme_dets"][0],
                "email":context.user_data["sme_dets"][1],
                "address":context.user_data["sme_dets"][2],
                "telephone":context.user_data["sme_dets"][3],
                "category":choice.lower()
            }}
        )
    )
    context.user_data["sme_name"] = context.user_data["sme_dets"][0]
    context.user_data["sme_id"] = new_sme["ref"].id()
    context.user_data["sme_cat"] = choice
    button = [[
        InlineKeyboardButton(
            text="Add a product",
            callback_data=choice.lower()
        )
    ]]
    bot.send_message(
        chat_id=chat_id,
        text="Business account created successfully, "
        "let's add some products shall we!.",
        reply_markup=InlineKeyboardMarkup(button)
    )
    return ADD_PRODUCTS

Добавление товаров

Здесь также присутствует две функции. Первая запрашивает пользователя и дает инструкции о том, как можно добавить новый товар. А вторая принимает вводимые пользователем данные, анализирует и использует их для создания нового товара. Затем функция добавляет этот товар в качестве нового документа в коллекцию товаров, которая находится в базе данных Fauna.

def add_product(update, context):
    bot = context.bot
    chat_id = update.callback_query.message.chat.id
    bot.send_message(
        chat_id=chat_id,
        text="Add the Name, Description, and Price of product, "
        "separated by commas(,) as caption to the product's image"
    )
    return ADD_PRODUCTS

def product_info(update: Update, context: CallbackContext):
    data = update.message
    bot = context.bot
    photo = bot.getFile(update.message.photo[-1].file_id)
    file_ = open('product_image', 'wb')
    photo.download(out=file_)
    data = update.message.caption.split(',')
    # upload image to cloudinary
    send_photo = upload('product_image', width=200, height=150, crop='thumb')
    # create new product
    newprod = client.query(
        q.create(
            q.collection("Product"),
            {"data": {
                    "name":data[0],
                    "description":data[1],
                    "price":float(data[2]),
                    "image":send_photo["secure_url"],
                    "sme":context.user_data["sme_name"],
                    "sme_chat_id": update.message.chat.id,
                    "category":context.user_data["sme_cat"]
                }
            }
        )
    )
    # add new product as latest
    client.query(
        q.update(
            q.ref(q.collection("Business"), context.user_data["sme_id"]),
            {"data": {
                "latest": newprod["ref"].id()
            }}
        )
    )
    # context.user_data["product_data"] = newprod['data']
    button = [[InlineKeyboardButton(
        text='Add another product',
        callback_data=context.user_data["sme_name"]
    )]]
    update.message.reply_text(
        "Added product successfully",
        reply_markup=InlineKeyboardMarkup(button)
    )
    return ADD_PRODUCTS

На этом мы завершаем часть нашего бота, которая предназначается для владельцев бизнеса. Далее мы добавим методы-обработчики, которые охватят весь функционал клиента.

Просмотр категорий бизнеса

Здесь у нас есть метод обработчика, принимающий выбор пользователя и использующий индекс business_by_category для поиска всех предприятий в данной категории. Метод выводит их список с последними товарами в виде миниатюрных изображений.

## CUSTOMER
def customer_pref(update, context):
    bot = context.bot
    chat_id = update.callback_query.message.chat.id
    data = update.callback_query.data
    print(data)
    # get all businesses in category
    try:
        smes_ = client.query(
            q.map_(
                lambda var: q.get(var),
                q.paginate(
                    q.match(
                        q.index("business_by_category"),
                        str(data).lower()
                    )
                )
            )
        )
        print(smes_)
        for sme in smes_["data"]:
            button = [
                [
                    InlineKeyboardButton(
                        text="View Products",
                        callback_data=sme["data"]["name"]
                    )
                ],
                [
                    InlineKeyboardButton(
                        text="Select for updates",
                        callback_data="pref"+','+sme["data"]["name"]
                    )
                ]
            ]
            if "latest" in sme['data'].keys():
                thumbnail = client.query(q.get(q.ref(q.collection("Product"), sme["data"]["latest"])))
                print(thumbnail)
                bot.send_photo(
                    chat_id=chat_id,
                    photo=thumbnail["data"]["image"],
                    caption=f"{sme['data']['name']}",
                    reply_markup=InlineKeyboardMarkup(button)
                )
            else:
                bot.send_message(
                    chat_id=chat_id,
                    text=f"{sme['data']['name']}",
                    reply_markup=InlineKeyboardMarkup(button)
                )
    except NotFound:
        button = [[
            InlineKeyboardButton(
                text="Select another Category?",
                callback_data="customer"
            )
        ]]
        bot.send_message(
            chat_id=chat_id,
            text="Nothing here yet",
            reply_markup=InlineKeyboardMarkup(button)
        )
        return CLASS_STATE
    return SHOW_STOCKS

Просмотр товаров

В принципе, название данного обработчика говорит само за себя. Данный метод производит перечисление всех товаров, принадлежащих выбранному пользователем бизнесу.

Мы можем отображать все продукты компании с помощью индекса products_by_business, который мы создали для нашей базы данных Fauna ранее. Это означает, что мы можем отправить запрос для получения всех продуктов, которые есть в каталоге компании (с индексом products_by_business), а затем отобразить результаты пользователю.

def show_products(update, context):
    bot = context.bot
    chat_id = update.callback_query.message.chat.id
    data = update.callback_query.data
    if "pref" in  data:
        data = data.split(',')[0].replace(' ', '')
        print(data)
        user = client.query(
            q.get(
                q.ref(
                    q.match(q.index('user_by_name'), context.user_data['user-data']['name']),

                )
            )
        )
        # update preference
        client.query(
            q.update(
                q.ref(
                    q.collection('User'), user['ref'].id()
                ),
                {'data': {'preference': user['data']['preference']+data+','}}
            )
        )
        button = [
            [
                InlineKeyboardButton(
                    text="Vieew more businesses category",
                    callback_data='customer'
                )
            ]
        ]
        bot.send_message(
            chat_id=chat_id,
            text="Updated preference successfully!!"
        )
        return CLASS_STATE
    products = client.query(
        q.map_(
            lambda x: q.get(x),
            q.paginate(
                q.match(
                    q.index("product_by_business"),
                    update.callback_query.data
                )
            )
        )
    )
    # print(products)
    if len(products) <= 0:
        bot.send_message(
            chat_id=chat_id,
            text="'Nothing here yet, user hasn't added any products!, check back later"
        )
        return CLASS_STATE
    for product in products["data"]:
        context.user_data["sme_id"] = product['data']['sme']
        button = [
            [
                InlineKeyboardButton(
                    text="Send Order",
                    callback_data="order;" + product["ref"].id()
                )
            ],
            [
                InlineKeyboardButton(
                    text="Contact business owner",
                    callback_data="contact;" + product["data"]["sme"]
                )
            ]
        ]
        bot.send_photo(
            chat_id=chat_id,
            photo=product["data"]["image"],
            caption=f"{product['data']['name']} \nDescription: {product['data']['description']}\nPrice:{product['data']['price']}",
            reply_markup=InlineKeyboardMarkup(button)
        )
    return POST_VIEW_PRODUCTS

Данный блок кода отвечает за получение всех продуктов компании и их отображение пользователю с возможностью размещения заказа или связи с владельцем компании.

Размещение заказа и контактные данные владельца бизнеса

Следующий блок кода отвечает за размещение заказа и предоставление клиенту данных о владельце бизнеса.

Когда пользователь размещает заказ, Телеграм бот отправляет сообщение владельцу бизнеса. В этом сообщении содержится информация о заказанном продукте и контакты потенциального покупателя в Телеграм.

def post_view_products(update, context):
    bot = context.bot
    chat_id = update.callback_query.message.chat.id
    data = update.callback_query.data
    product = client.query(
        q.get(
            q.ref(
                q.collection("Product"),
                data.split(';')[1]
            )
        )
    )["data"]
    if "order" in data:
        bot.send_message(
            chat_id=product['sme_chat_id'],
            text="Hey you have a new order"
        )
        bot.send_photo(
            chat_id=product['sme_chat_id'],
            caption=f"Name: {product['name']}\n\nDescription: {product['description']}\n\nPrice: {product['price']}"
            f"\n\n Customer's Name: {context.user_data['user-name']}",
            photo=product['image']
        )  
        bot.send_contact(
            chat_id=product['sme_chat_id'],
            phone_number=context.user_data['user-data']['telephone'],
            first_name=context.user_data['user-data']['name']
        )
        bot.send_message(
            chat_id=chat_id,
            text="Placed order successfully"
        )
    elif 'contact' in data:
        sme_ = client.query(
            q.get( 
                q.match(
                    q.index("business_by_name"), 
                    product['sme']
                )
            )
        )['data']
        bot.send_message(
            chat_id=chat_id,
            text=f"Name: {sme_['name']}\n\nTelephone: {sme_['telephone']}\n\nEmail:{sme_['email']}"
        )

Тестируем наш Телеграм бот

Сохраним написанный выше код в файле handlers.py. После этого перейдем к регистрации этих методов в файле main.py, как мы уже делали ранее.

import handlers
from telegram.ext import (
    CommandHandler, CallbackContext,
    ConversationHandler, MessageHandler,
    Filters, Updater, CallbackQueryHandler
)
from config import TOKEN

updater = Updater(token=TOKEN, use_context=True)
print(updater)
dispatcher = updater.dispatcher


def main():
    conv_handler = ConversationHandler(
        entry_points=[CommandHandler('start', handlers.start)],
        states={
            handlers.CHOOSING: [
                MessageHandler(
                    Filters.all, handlers.choose
                )
            ],
            handlers.CLASS_STATE: [
                CallbackQueryHandler(handlers.classer)
            ],
            handlers.SME_DETAILS: [
                MessageHandler(
                    Filters.all, handlers.business_details
                )
            ],
            handlers.SME_CAT: [
                CallbackQueryHandler(handlers.business_details_update)
            ],
            handlers.ADD_PRODUCTS: [
                CallbackQueryHandler(handlers.add_product),
                MessageHandler(Filters.all, handlers.product_info)
            ],
            handlers.CHOOSE_PREF: [
                CallbackQueryHandler(handlers.customer_pref)
            ],
            handlers.SHOW_STOCKS: [
                CallbackQueryHandler(handlers.show_products)
            ],
            handlers.POST_VIEW_PRODUCTS: [
                CallbackQueryHandler(handlers.post_view_products)
            ]
        },
        fallbacks=[CommandHandler('cancel', handlers.cancel)],
        allow_reentry=True
    )
    dispatcher.add_handler(conv_handler)
    updater.start_polling()
    updater.idle()

if __name__ == '__main__':
    main()

Теперь сохраним этот код в файл main.py. Сделав это, мы можем перейти к тестированию нашего бота, запустив его из командной строки.

Как только он будет запущен, можно переходить в Телеграм и начинать создавать разные бизнесы и товары. Для взаимодействия с ботом в качестве клиента можно также использовать и другой аккаунт Телеграм.

Второе изображение показывает нам, что видит владелец бизнеса, когда у него делают заказ.

Заключение

В этой статье мы рассмотрели, как использовать бессерверную платформу Fauna для создания и размещения базы данных, а также индексов и коллекций. Мы разобрали, как сгенерировать API-ключ для связи с нашей базой данных из других приложений Python. В нашем случае это было приложение telegram-business. Мы также узнали, как создать с нуля Телеграм-бот, используя Python и необходимые библиотеки.

Однако, примеры из данной статьи совершенно не исчерпывают тему приложения telegram-business. Мы надеемся, что эта статья сможет вдохновить вас на расширение функционала бота так, чтобы он смог решать уже ваши собственные задачи.

Перевод статьи «Building An E-commerce Telegram Bot Using Python and Fauna».