Русский
Русский
English
Статистика
Реклама

Поддержка пользователей

Как мы обрабатываем жалобы пользователей с помощью JIRA (REST API)

09.07.2020 08:06:27 | Автор: admin


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


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


Для чего нам Report a problem?


Добавим немного контекста для чего мы предоставляем функционал жалоб и что нам нужно?
Uxcel веб-сервис для обучения UI/UX в игровой форме. Обучающим элементом у нас является Практика в большинстве случаев это 2 изображения, где одно верное, а другое нет. Что позволяет натренировать глаз находить недочеты даже в визуально идентичных элементах. Каждая практика помимо изображений имеет подсказку (hint наводку на верный ответ) и описание (description) с теорией, касающейся данной задачи.



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



Чтобы не изобретать велосипед со своими бордами, тикетами и backlog-ом, а также чтобы хранить все задачи команд в одной системе и даже в общих спринтах, было решено для этих целей использовать JIRA + REST API.


Организация тикетов в JIRA


Для каждой практики у которой есть хотя бы 1 жалоба создается BUG в JIRA в выделенном эпике Practices Reports. А сами жалобы хранятся в виде комментариев к соответствующим багам-практикам. В дополнение к этому, для разных видов практик добавляется Label (в нашем случае такие как: Course, Gym, UEye). Общая логика представлена на схеме ниже:



Таким образом, контент-команда выбирает наиболее приоритетные практики (в виде багов) для исправления в каждом спринте.


А теперь давайте взглянем на технические подробности реализации.


Интеграция с JIRA REST API


Первым делом нужно создать API token в JIRA, чтобы получить доступ к JIRA API. Для использования на продакшене рекомендую создать отдельного пользователя от имени которого и будут создаваться тикеты, иначе придется отбиваться от постоянных уведомлений, в то время как для отдельного пользователя их проще сразу все отключить.


Получение API токена:




  • Задаем имя токена -> нажимаем Create


Готово. Сохраняем куда-нибудь созданный токен, терять его нельзя.


Теперь можно выполнять API запросы к JIRA. В каждый запрос передается заголовок, содержащий емейл (пользователя для которого был создан токен) и сам токен их передаем посредством реализации HTTP basic authentication.


Пример кода (весь код на TypeScript для NodeJS):


private generateAuthHeader(): string {    // конвертируем строку email:apiToken в Base64    const basicAuthValue = Buffer.from(`${this.jiraEmail}:${this.jiraApiToken}`).toString('base64');     return `Basic ${basicAuthValue}`;}

Примечание: для хранения ключей и паролей мы используем AWS Secrets Manager. Прямо в коде такие данные хранить не безопасно. Больше информации тут.

Создание бага через API


Осталось совсем немного подготовки. Для того чтобы создать баг, нам нужно знать его Issue ID в JIRA. Один из способов его узнать вызвать GET запрос на получение информации обо всех типах:


GET https://{id}.atlassian.net/rest/api/3/issuetype 

Поскольку это разовая операция, то чтобы не писать код можно воспользоваться Postman:



Во вкладке Authorization выбираем Type: Basic Auth, вводим email и api token.


В ответе нас интересует эта часть:


{    "self": "https://{id}.atlassian.net/rest/api/3/issuetype/10004",    "id": "10001",    "description": "A problem or error.",    "iconUrl": "https://${id}.atlassian.net/secure/viewavatar?size=medium&avatarId=10303&avatarType=issuetype",    "name": "Bug",    "untranslatedName": "Bug",    "subtask": false,    "avatarId": 10303}

После того как узнали Issue Id типа BUG (10001) нам нужно узнать Project Id, к которому баг будет принадлежать. Похожим образом можем получить список всех проектов и найти id нужного.


Для этого делаем GET запрос на


GET https://{id}.atlassian.net/rest/api/3/project/search

И последний подготовительный шаг: как я выше упоминал, мы храним баги в отдельном эпике (Jira Epic). Его id знать не обязательно, достаточно скопировать его Key (расположен перед названием эпика, либо в адресной строке, например UX-1).


Все готово к созданию первого бага через API.


Я использовал npm пакет Got для создания HTTP запросов для NodeJS.

await got.post({    url: `${this.jiraApiHost}/issue`, // jiraApiHost = https://{id}.atlassian.net/rest/api/3    headers: {        Authorization: authorization, // созданный Basic Auth Header в методе generateAuthHeader        'Content-Type': 'application/json'    },    responseType: 'json',    json: {        update: {},        fields: {            issuetype: { id: this.jiraBugTypeId }, // полученный id типа BUG (пример - 10001)            project: { id: this.jiraPracticeReportProjectId }, // id проекта (пример - 10005)            parent: { key: this.jiraPracticeReportEpicKey }, // ключ Epic (пример - UX-1)            summary: practiceTicketName, // имя практики формата -  [practiceId] practiceName (#reports)            labels: [practice.label]        }    }});

API Reference


Баг создан. Далее рассмотрим остальные методы необходимые для настройки полного цикла обработки жалоб и ведения их в JIRA, такие как: Поиск, Обновление статуса, Обновление информации, Добавление комментария к багу.


Поиск бага через API


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


Пример кода:


// формируем JQL запрос, ищем по типу BUG в эпике где хранятся жалобы по id практики (которое есть в названии каждого бага)const jql = `issuetype = Bug AND project = CNT AND parent = ${this.jiraEpicKey} AND text ~ "${practiceId}" order by created DESC`; const response = await got.get({    url: `${this.jiraApiHost}/search?jql=${jql}`,    headers: {        Authorization: authorization    },    responseType: 'json'});const practiceJiraTicket = response.body['issues'] && response.body['issues'][0];

API Reference


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


Обновление статуса бага через API


Чтобы обновить статус, воспользуемся Transitions. Но для этого нужно узнать Status ID для TODO / OPENED статуса (статус зависит от настроек JIRA).


Возвращаемся к Postman:


GET https://{id}.atlassian.net/rest/api/3/project/{projectIdOrKey}/statuses

По id проекта получаем все статусы, находим статус, который указывает на открытое состояние тикета и сохраняем его id.


Запрос на перевод бага в открытый статус:


await got.post({    url: `${this.jiraApiHost}/issue/${practiceJiraTicket.key}/transitions`, // где practiceJiraTicket - найденный объект бага    headers: {        Authorization: authorization,        'Content-Type': 'application/json'    },    responseType: 'json',    json: {        transition: {            id: this.jiraToDoStatusId // id статуса полученного выше (пример - 10006)        }    }});

API Reference


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


Обновление названия бага через API


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


Код вызова API для обновления бага:


await got.put({    url: `${this.jiraApiHost}/issue/${practiceJiraTicket.key}`,    headers: {        Authorization: authorization,        'Content-Type': 'application/json'    },    responseType: 'json',    json: {        update: {            summary: [{ set: newPracticeTicketName }]        }    }});

API Reference


Далее добавим детали самой жалобы в виде комментария к уже подготовленному багу.


Добавление комментария через API


Каждая жалоба пользователя хранится в виде комментария к багу-практике.


Код создания комментария:


await got.post({    url: `${this.jiraApiHost}/issue/${practiceJiraTicket.key}/comment`,    headers: {        Authorization: authorization,        'Content-Type': 'application/json'    },    responseType: 'json',    json: comment // подробности о формировании объекта ниже});

API Reference


Объект comment формируется в виде Atlassian Document Format.
На сайте так же есть Builder, который значительно упрощает генерацию объекта: просто форматируем текст под свои нужды в редакторе при этом параллельно создается итоговый JSON объект.


Готово! Теперь можно принимать, хранить, обрабатывать, закидывать в спринты и удобно искать жалобы пользователей используя JIRA.


Как у нас выглядят жалобы в виде комментариев:



Итоговый вид нашего списка багов в JIRA (название содержит id, #N число жалоб, % верных ответов):



Дальше все зависит от вашей фантазии и требований. Например, можно:


  • реализовать асинхронную обработку жалоб, чтобы пользователь не ждал пока пройдет вся цепочка запросов к JIRA (у нас реализовано средствами AWS SNS)
  • добавить поле priority для багов и менять приоритет в зависимости от числа жалоб для более удобной фильтрации в борде
  • дополнительно информировать модераторов в Slack при появлении новой жалобы со ссылкой на созданный баг (slack/webhook пакет очень прост в интеграции)
  • настроить JIRA Webhooks, чтобы при закрытии бага автоматически рассылать уведомления всем пользователям, которые жаловались на практику с благодарностью за участие в улучшении продукта
  • автоматически назначать баг на автора контента, на который поступила жалоба.

Всем спасибо за внимание! Надеюсь, статья была для вас полезной :)
С радостью отвечу на ваши вопросы!

Подробнее..

Телеграм бот для поддержки своими руками

28.01.2021 22:20:35 | Автор: admin

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

Или, например, вы продаете что-то через свой канал. Клиентов так много, что один "продажник" (=вы) не справляется. Или поддержка вашего бизнеса отвечает всем в публичном чате, который прикреплен к вашему каналу. Но многие стесняются задать вопросы, так как их могут увидеть, поэтому пишут в личку, что не масштабируется.

Проблем много, а решение одно: сделать Телеграм бот, который будет работать посредником между вашими клиентами и командой поддержки.

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

Самый популярный конструктор таких ботов - Livegrambot. Он позволяет сделать тоже самое, но при этом бот будет писать вашим пользователям "я сделан через Livegrambot", выпрашивая деньги у вас. Будучи умелым создателем Телеграм ботов, я решил сделать свой аналог, но уже с открытым исходным кодом и легким способом запустить его бесплатно на бесплатные серверы.

Ниже я расскажу, как в 1 клик запустить такого бота и как он технически устроен.

TL;DR: Код выложил сюда: https://github.com/ohld/telegram-support-bot

Юзер стори или как с этим ботом работать.

Действующие лица:

  • Ваши Пользователи (читатели канала, клиенты),

  • Закрытый Чат Поддержки (где сидят те, кто будет отвечать на вопросы Пользователей),

  • Бот (которому Пользователи будут писать свои вопросы).

Вот так это все будет работать:

  1. Вы публикуете ссылку на Бота,

  2. Пользователи пишут в него свои вопросы,

  3. Бот пересылает их сообщения в ваш Чат Поддержки,

  4. В этом чате вы или ваши помощники отвечают на сообщение (через reply),

  5. Бот пересылает ответ обратно пользователю от своего лица, скрывая аккаунт отвечающего.

Такая схема неплохо масштабируется: достаточно нанять больше Агентов поддержки, и все Пользователи получат свои ответы вовремя и через бота.

Как это все запустить? Желательно, без навыков.

Планируя дизраптнуть платный аналог, необходимо продумать онбординг. Большинству проще заплатить, чем самому разбираться в коде, технологии и деплое. К счастью, я фанат Heroku, а именно там можно бесплатно хостить свой код, нажав всего одну кнопку.

В README.md я добавил волшебную кнопку от Heroku, которая поможет запустить код из репозитория. После нажатия, при наличии аккаунта на Heroku (который можно создать также по 1 кнопке), вы увидите такую картину:

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

- App name: название приложения в системе Heroku. Можно придумать любое.

- Choose a region: где Хероку запустит ваш код. Можно выбрать любое место.

- HEROKU_APP_NAME: впишите сюда тоже самое, что указали выше в App name (это важно для того, чтобы завести тг бота через вебхуки).

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

- TELEGRAM_TOKEN: токен вашего бота, который можно получить у BotFather.

Как узнать TELEGRAMSUPPORTCHAT_ID

Способов много, но самый простой - это добавить вот этого бота в ваш созданный приватный чат. Этот бот возвращает все данные, которые ему присылает Телеграм, в частности событие "меня добавили в чат", откуда вы и сможете извлечь chat_id.

Как реализовать такого бота?

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

Примеры кода я буду писать на языке Python и использовать библиотеку python-telegram-bot. Итогда я буду вставлять ссылки на GitHub (гит), чтобы легко можно было найти этот кусок кода в моем репозитории.

Хендлеры (обработчики событий)

Для нашей задумки необходимы всего 3 хендлера (гит):

from telegram.ext import Updaterfrom telegram.ext import CommandHandler, MessageHandler, Filtersupdater = Updater(TELEGRAM_TOKEN)dp = updater.dispatcher# Для приветственного сообщения и для "к вам подключился {username}"dp.add_handler(CommandHandler('start', start))# Для пересылки из бота в чат поддержкиdp.add_handler(MessageHandler(Filters.chat_type.private, forward_to_chat))# Для пересылки ответа из чата обратно пользователюdp.add_handler(MessageHandler(Filters.chat(TELEGRAM_SUPPORT_CHAT_ID) & Filters.reply, forward_to_user))

С командой /start все понятно. Юзер нажал - прислать приветственное сообщение - прислать в чат поддержки о том, что подключился новый юзер (гит).

def start(update, context):    update.message.reply_text(WELCOME_MESSAGE)    user_info = update.message.from_user.to_dict()    context.bot.send_message(        chat_id=TELEGRAM_SUPPORT_CHAT_ID,        text=f"? Connected {user_info}.",    )

В случае пересылки ботом сообщения пользователя из лички в чат поддержки, тоже все просто (гит):

def forward_to_chat(update, context):    update.message.forward(chat_id=TELEGRAM_SUPPORT_CHAT_ID)

В случае отправление ответа (reply) на пересланное сообщение, необходимо скопировать содержимое сообщения и отправить его от лица бота. Если аналогично сделать .forward, то будет виден отправитель. А тут как раз недавно в Telegram Bot API добавили возможность удобно копировать содержимое сообщения (гит):

def forward_to_user(update, context):    user_id = update.message.reply_to_message.forward_from.id    context.bot.copy_message(        message_id=update.message.message_id,        chat_id=user_id,        from_chat_id=update.message.chat_id    )

Бесплатный деплой на Heroku

Чтобы захостить это все бесплатно на Heroku, бот должен быть запущен в режиме Webhook, а не Pooling. Разница их в том, что вебхук "слушает новые сообщения от Телеги", а пулинг "периодически запрашивает". Чтобы запрашивать, сервер должен работать постоянно (условно, каждую секунду запрашивать у серверов Телеграмма новые сообщения, которые кто-то написал в бот). Однако, в случае с вебхуками, сервер может просто ждать, когда серверы Телеграмма сами отправят нам новые обновления бота.

Этот факт критически важен, если мы хотим бесплатно пользоваться услугами Heroku (который по факту дает нам свои серверы в аренду). Хероку любит "усыплять" простаивающие машины, которые пробуждаются в момент нового входящего запроса. Именно новые сообщения от серверов Телеграмма и будут пробуждать наш сервер тогда, когда необходимо переслать пользовательское сообщение из лички бота в наш чат поддержки.

Для того, чтобы настроить Webhook, необходимо поднять вебсервер, который будет слушать входящие сообщения по endpoint. Сказать Телеграму: "присылай события бота мне на сервер - по этому адресу". Также нужно как-нибудь защититься от злоумышленников, которые могут отправить на наш вебсервер событие, прикинувшись сервером телеги. Также телеграм требует, чтобы все работало https.

Звучит сложно, однако Heroku автоматически и бесплатно обеспечит https, а вебсервер для вебхука уже встроен в библиотеку python-telegram-bot. Если добавить секретный токен вашего бота в URL, по которому вы будете слушать события от Телеги, то можно защититься от стороннего вмешательства.

Вот как можно запустить Телеграм бот в webhook-режиме (гит) через эту библиотеку:

# запускаем слушающий вебсервер updater.start_webhook(  listen="0.0.0.0",  port=PORT,  # HEROKU требует, чтобы порт вебсервера задавался через переменные окружения  url_path=TELEGRAM_TOKEN  # добавляем секретное значение в адрес, который слушаем)# говорим Телеграму: "присылай события бота по этому адресу"updater.bot.set_webhook(f"https://{HEROKU_APP_NAME}.herokuapp.com/{TELEGRAM_TOKEN}")updater.idle()

Помните, мы отдельно задавали переменную окружения HEROKU_APP_NAME , куда копипастили название нашей Heroku App? Дело в том, что эта переменная используется в адресе, по которому Heroku запускает наш вебсервер. Но при этом, имя приложения Хероку нельзя получить изнутри, поэтому решение "скопипастить название App Name в отдельную переменную окружения" для меня звучит норм.

Что дальше?

Допустим, вы запустили бота, у вас уже много клиентов и вы хотите усовершенствовать функционал телеграм бота. Что можно сделать?

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


Спасибо за просмотр. Теперь вы знаете, как можно сделать и бесплатно задеплоить Телеграм бота поддержки. Полный код проекта (вместе с волшебной кнопкой "задеплой это на хероку") лежит тут. В своем Телеграм канале я делюсь опытом разработки больших телеграм ботов, делюсь датасетами и продуктовой аналитикой. Заходите.

А какие другие популярные юзкейсы Телеграм ботов вы бы выделили? Напишите в комментариях.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru