hiring-bot-events

Логування подій hiring-бота

src/stack/components/user/StackHiringBot/hiring-bot-event.repository.ts · таблиця ClickHouse hiring_bot_events

Кожен крок проходження кандидатом анкети в @BeSocialHR_bot фіксується як рядок у ClickHouse. Це основа для аналітики.

Таблиця

Партиціонується по місяцях, TTL — 2 роки. Сортування по (date, user_id, timestamp).

ПолеТипОпис
timestampDateTimeUTC, формат YYYY-MM-DD HH:MM:SS
dateDateпохідна від timestamp, для партиціонування і фільтрів по діапазону
user_idStringMongoDB ObjectId кандидата
step_idLowCardinalityкрок анкети або lifecycle-маркер (start, lead_submit)
event_typeLowCardinalityтип події (div. нижче)
platformLowCardinalityios / android / desktop / ''
platform_rawStringсирий userAgent (лише в події start)
error_typeLowCardinalityкатегорія помилки (лише в події error)
utm_source/medium/campaign/content/manager/accountString/LowCardinalityUTM-мітки (лише в start і lead_submit)

Типи подій

event_typeКоли фіксуєтьсяЩо означає
startновий кандидат запустив бота вперше (/start)точка входу у воронку; несе UTM і платформу
bot_message_sentбот надіслав питаннякандидат отримав крок
user_reply_receivedкандидат відповів на питаннякандидат пройшов крок
lead_submitкандидат завершив анкету (здав номер телефону)точка конверсії; несе ті ж UTM/платформу що і start
errorтехнічна помилка на кроцівиключається з усіх метрик воронки і трафіку

Які кроки логуються

step_id для подій bot_message_sent / user_reply_received — це ключ питання анкети. Логуються не всі питання, лише ті що релевантні для аналітики воронки:

nameagevacancyexperiencedevicesemploymentTypeworkShiftvoiceCommunicationsalaryInfoaboutphoneNumber

Для lifecycle-подій step_id збігається з event_type: start і lead_submit.

Якщо в navigationHandlers додається або прибирається питання — список логованих кроків теж потрібно синхронізувати (константа LoggedCandidateQuestionKey у репозиторії).

Послідовність подій для одного кандидата

start
  → bot_message_sent (name)
  → user_reply_received (name)
  → bot_message_sent (age)
  → user_reply_received (age)
  → ...
  → bot_message_sent (phoneNumber)
  → user_reply_received (phoneNumber)
lead_submit

Кандидат що не відповідає на запитання на devices матиме bot_message_sent (devices) але не матиме user_reply_received (devices) — це і є drop-off на цьому кроці.

Де і коли пишуться події

start — одразу після створення нового кандидата в базі (перший /start). При повторному /start (перезапуск анкети) подія start не дублюється.

bot_message_sent — після того як бот відправив питання кандидату. Викликається для кожного питання окремо.

user_reply_received — після відповіді кандидата.

lead_submit — коли кандидат надав номер телефону і анкета повністю заповнена. UTM беруться з поля candidate.utm — тих самих що записались при start.

error — фіксується у трьох точках:

  • у catch-блоці основного обробника messagesFromHiringBot: api_fail (якщо впав Telegram API) або crm_error (решта); step_id = currentQuestion кандидата.
  • у хелпері handleTelegramError (глобальний catch telegraf-обробників): та сама класифікація.
  • при невалідній відповіді на питання: input_error; step_id = поточне питання.

Категорії помилок

error_typeКоли
api_failпомилка Telegram API (мережа, rate limit, невалідний токен)
crm_errorпомилка нашої логіки (БД, бізнес-правила, непередбачений стан)
input_errorкандидат ввів невалідне значення, яке не пройшло валідацію
timeoutтип зарезервований; поки що не використовується

Нюанси

  • UTM і платформа — тільки в start і lead_submit. Для bot_message_sent / user_reply_received в поле utm_* і platform записується порожній рядок. При аналітичних запитах з UTM-фільтром спочатку знаходять множину user_id за умовою step_id='start' AND utm_*=..., потім до неї приєднуються всі інші події цих юзерів.

  • Платформа визначається на сервері за userAgent-рядком. ios — якщо UA містить iPad/iPhone/iPod, android — якщо android, інакше desktop.

  • event_type='error' виключається з усіх воронкових метрик. Запити по воронці, часу відповіді і трафіку явно фільтрують event_type != 'error' або вибирають тільки конкретні event_type.