В этом цикле статей мы реализуем службу поддержки для онлайн-чатов. Система должна уведомлять команду операторов о новом сообщении, делить нагрузку на команду любого размера, делегировать сообщения в зависимости от приоритета. Звучит жутко. Но без паники, я поделюсь нашим опытом построения такой системы, которая уместилась всего в пару сотен строк кода. Да, мы будем использовать Telegram-бота. Но не потому, что это модно, а потому что супер удобно.
Всё началось с того, как мы захотели делать мобильное приложение, где люди могут писать любой вопрос в чаты заведений вокруг и получить быстрый ответ. Сделать чат — не проблема. Но что если мы хотим получать уведомления о новых сообщениях в real-time, для мгновенного ответа пользователю?
В первой части статьи я покажу на тестовом примере, как можно управлять системой службы поддержки через чат-бота, почему это очень простой и удобный способ. А во второй части статьи мы реализуем этот пример на Python.
Проблема и решение
Любая команда операторов службы поддержки должна отвечать на запросы оперативно, для этого её нужно оперативно уведомлять. Нет нужды придумывать что-то своё — любой современный мессенджер справится с этой задачей на ура. Я выбрал Telegram. Он удобен, работает на всех основных платформах, а функционал ботов опережает даже титанов рынка мессенджеров как минимум на год. К чему это я про ботов?
В нашем случае, бот представляет собой информационный канал, к которому легко можно подключать людей, с которым можно взаимодествовать текстовыми командами. После того, как мы создали бота, подключить к нему людей легко, достаточно кинуть им ссылку. Однако, наш бот исключительно для внутреннего использования. Нельзя допустить, чтобы кто угодно мог получить доступ по этой ссылке. Ссылку мы прятать надёжно не можем, а вот включать функционал специальной командой с подтверждением пароля — запросто.
Telegram бот позволяет задавать для бота свои текстовые команды. Делаем так: создадим команду "/on" которая «включает» функционал бота для пользователя. Но только после подтверждения паролем! Ну и на всякий случай можно предусмотреть команду "/off", которая отключает функционал.
Теперь наш бот знает группу людей — операторов службы поддержки. Пока что представим, что группа операторов у нас всего одна, каждый вопрос от клиента очень важен, поэтому будем рассылать уведомления всей группе. В этом нет никакой магии, конечно Telegram имеет апи для рассылки сообщений людям, подключившимся к нашему боту.
Теперь каждый член команды операторов получит уведомление. В текст сообщения можно вставить ссылку, по которой можно ответить клиенту. Например, это может быть ссылка на веб-версию чата, через который вы осуществляете поддержку.
Система, которую мы разработали, работает корректно, когда оператор службы поддержки один. Безусловно, мы можем рассылать уведомления любому количеству людей. Но как понять, что кто-то уже отвечает на запрос клиента? Как распределять нагрузку, чтобы не возникло путаницы? Это мы будем делать в следующей статье с помощью… кнопок в чате. Не так давно Telegram опубликовал Bot Api 2.0. Теперь стало возможным добавлять кнопки к сообщениям и отслеживать нажатия на них пользователей. Чем мы и займёмся в следующей статье. Ну и скриншот для превью того, что мы будем делать.
Реализация
Всё необходимое для создания бота можно почитать в документации. После того, как мы создали бота, приступаем к коду. Будем использовать Python библиотеку — обёртку Telegram API. Она позволяет писать обработку сообщений в обычных функциях с декораторами, что довольно удобно. Полный код нашего тестового примера доступен тут, по мере выхода следующих статей я буду его обновлять.
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
bot.reply_to(message, "Welcome to Support_Bot!")
Так выглядит код, который реагирует на команды "/start", "/help", а так же приветствует новых пользователей бота (при первом открытии бота автоматически посылается команда "/start").
@bot.message_handler(commands=['on'])
def subscribe_chat(message):
if message.chat.id in team_users:
bot.reply_to(message, "You are already an operator")
else:
user_step[message.chat.id] = TEAM_USER_LOGGING
bot.reply_to(message, "Enter team secret phrase:")
Это уже обработка не стандартных команд ("/start" и "/help" есть по умолчанию у всех ботов). Мы создали обработчик команды "/on". После обработки мы просим ввести пароль.
@bot.message_handler(func=lambda message: user_step.get(message.chat.id) == TEAM_USER_LOGGING)
def team_user_login(message):
if message.text == 'password1':
team_users.add(TeamUser(message.chat.id))
user_step[message.chat.id] = TEAM_USER_ACCEPTED
bot.reply_to(message, "You`ve started receiving messages")
else:
bot.reply_to(message, "Wrong secrete phrase, try again")
Эта функция проверяет пароль на валидность. Но как понять, что последнее сообщение было паролем? В обработчике "/on" мы сохраняем статус диалога в глобальной переменной. Декораторы обработчиков сообщений могут принимать lambda-функции, в которые попадют входящие сообщения и если lambda-функция вернула True — идём в обработчик. На самом деле, в нашем случае текст сообщения не так важен, но мы проверяем статус глобальной переменной. Если пользователь до этого вызывал команду "/on" — значит нужно интерпретировать его сообщение, как пароль. Если пароль проходит проверку — сохраним так называемый chat id куда-нибудь, например в файл. С помощью этого id мы позже будем отправлять сообщения в чат оператору.
@bot.message_handler(commands=['off'])
def team_user_logout(message):
if message.chat.id not in team_users:
bot.reply_to(message, "You are not an operator anyway")
else:
team_users.remove_by_chat_id(message.chat.id)
bot.reply_to(message, "You`ve stopped receiving messages")
Аналогично реализуем команду отключения оператора, который больше не хочет получать уведомления.
def process(message):
text = '%s\n%s writes to %s\nReply: %s' %\
(message, 'Vasya', 'Super Support Team', '*reply_url*')
for user in team_users:
bot.send_message(user.chat_id, text, disable_web_page_preview=True)
Ну и рассылка сообщений делается ещё проще. Так как в сообщениях мы можем передавать разную необходимую информацию, включая ссылки, не будем заставлять приложение Telegram пытаться распарсить их, это будет только мешать. С помощью флага disable_web_page_preview = True Telegram не будет пытаться проходить по нашим ссылкам и выдавать картинку превью в чате.
threading.Thread(target=bot.polling).start()
Запускаем бота одной строчкой (метод polling, ещё доступны webhook-и, но для начала этого хватит с головой).
В этой статье мы сделали бота, научили его добавлять операторов только после ввода пароля, так же научили его рассылать всем в группе операторов уведомления. Я буду продолжать цикл и расскажу, как убрать возможную путаницу «кто на какой запрос отвечает», как на основе бота сделать разбиение операторов на группы, делегирование сообщений определённой группе, а так же как отвечать на вопросы клиентов не выходя из Telegram чата. Подобную систему мы используем в работе нашего приложения (Android и iOS). Ну и, конечно, будем изучать новые классные фишки Telegram Bot API 2.0. Надеюсь, наш опыт будет полезен.