Разработка Aviasales
8365

Инструкция по созданию стикеров-информеров в Telegram от Aviasales

У нас в Aviasales всё быстро — как поиск дешёвых авиабилетов, так и создание новых штук.

В закладки

На свежие мемы мы обычно реагируем со скоростью Молнии Маккуина (в комментариях, конечно же, вы вспомните 100 и 1 инфоповод, который мы пропустили), на небольшие проекты нужна пара дней.

Последняя кампания, собранная по принципу «нам срочно это надо» — квест в Telegram. Идея нашлась в локальном геочате, где соседи по бизнес-центру делились скидками на обеды. За этим было комично наблюдать, и у ребят появилась мысль как-то использовать локальные чаты в нашем маркетинге.

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

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

Мы как-то уже писали, почему это происходит. К тому же покупка действительно дешёвых билетов — это больше про эмоции: находишь билеты в условный Милан за 3000 рублей и достаёшь карточку из кошелька. А в Telegram можно ещё и поделиться билетом с друзьями, чтобы не лететь в одиночку.

Стикеры регулярно обновляются автоматически, оставалось лишь связать базу данных цен на билеты с набором. Наш разработчик Павел Леонов рассказывает, как это сделать:

Если коротко, то цены в стикерах обновляются каждые пять минут. Мы целиком заменяем старые стикеры в паке новыми, используя Тelegram API. Специальный скрипт делает запрос к базе всех недавних поисков на Aviasales и выбирает актуальные цены и направления, потом передаёт отрисовки сервису, который и создаёт изображения для стикеров. Затем новые изображения автоматически загружаются в набор.

Павел Леонов
разработчик

А теперь подробнее.

Подготовка

Для начала нужно создать бота в Telegram, чтобы иметь доступ к API стикеров. Сделать это можно через официального бота @BotFather (подробнее о ботах в Telegram и их создании).

Для того, чтобы иметь возможность обновлять стикерпак в автоматическом режиме, нужно сначала завести его через бота, которого вы создали. Выполняем HTTP-запрос, для примера используем curl:

curl \ -F "name=myDynamicStickerPack_by_myStickerBot" \ -F "title=Мой стикерпак" \ -F "emojis=❤️" \ -F "user_id=telegramUserId" \ -F "png_sticker=@/path/to/mainSticker \ https://api.telegram.org/bot<:botSecret>/createNewStickerSet

Запрос создаёт новый стикерпак с основным стикером (в Telegram есть правило, по которому стикерпак без стикеров существовать не может). Обратите внимание, что внутреннее название стикерпака, созданного ботом, должно заканчиваться на _by_myStickerBot, где myStickerBot — имя созданного ранее бота.

У нас разработка генератора ведётся на JavaScript на движке Node.js. В качестве графического движка для рисования используем Node Canvas (адаптация классического HTML Canvas API для Node.js).

Разработка

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

  1. Генерация изображения.
  2. Замена изображения в стикерпаке.

Генерация изображения

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

Описываем функцию генерации изображения. Задаём размеры стикера (рекомендуемый — 512 на 512 пикселей). Затем комбинируем название направления, цену и тег и добавляем данные на подложку-шаблон.

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

Замена изображения в стикерпаке

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

После настраиваем cron на выполнение скрипта каждые пять минут. Готово. Вы прекрасны!

Мы решили начать с популярных и бюджетных маршрутов — Крым, Сочи, Санкт-Петербург. А ещё есть два особенных билетика: один подсказывает, куда дешевле слетать на ближайшие выходные, а второй — лучшее направление дня. Но мы лишь разогрелись.

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

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

Для этого мы и делаем рассылки, блог, социальные сети, геоквесты или стикеры-информеры. Мы как бы говорим: «Ребят, мы здесь! Каникулы могут быть не только в Новый год и на майских, можно дёшево ввязаться в приключения прямо на этих выходных — вот вам Калуга».

Кстати, нашу любимую Калугу — самое быстрое направление из Москвы — тоже непременно добавим в стикерпак, который доступен уже сегодня.

#стикеры #полеты

Материал опубликован пользователем. Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Aviasales", "author_type": "editor", "tags": ["\u0441\u0442\u0438\u043a\u0435\u0440\u044b","\u043f\u043e\u043b\u0435\u0442\u044b"], "comments": 22, "likes": 40, "favorites": 70, "is_advertisement": false, "subsite_label": "dev", "id": 75129, "is_wide": false, "is_ugc": false, "date": "Fri, 12 Jul 2019 17:37:23 +0300" }
{ "id": 75129, "author_id": 199189, "diff_limit": 1000, "urls": {"diff":"\/comments\/75129\/get","add":"\/comments\/75129\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/75129"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 235819, "last_count_and_date": null }
22 комментария

Популярные

По порядку

Написать комментарий...
7

показалось на картинке презерватив в упаковке!

Ответить
11

Aviasales. Твоя защита от дорогих билетов.

Ответить
2

Специальные, туристические, коллобрация Durex + Aviasales.

Ответить
1

Авиасалез всегда с тобой!

Ответить
0

У вас богатая фантазия :)) в реальности все прозаичнее :))

Ответить
7

Инструкция по созданию стикеров

А где инструкция?

Ответить
4

Писать статью для русской аудитории и вставлять ссылки на t.me - ну зачем так, не все же вечно под vpn сидят.

https://tlgg.ru/addstickers/tickets_by_aviasalesTicketStickersBot

Ответить
0

А там же как раз через прослойку ссылка на пак

Ответить
1

t.me давненько в РФ заблокирован ;)

Ответить
0

Лучше тогда ещё и прямую. Я скопировал указанную выше, меня перебрасывает в браузер зачем-то, пришлось редактировать сообщение, исправляя на t.me.

Ответить
3

А как этим пользоваться? Это чтобы посмотреть значение на стикере, его надо куда-то отправить?

Ответить
0

Можно просто зажать стикер: тогда получится посмотреть значение перед отправкой :)

Ответить
2

Вы большие молодцы. Спасибо, что поддержали идею и так прекрасно расскрыли материал.

Aviasales - заебись. Шлем лучи добра всей вашей команде!

Ответить
1

Огромный респект от команды Emporium, классные у вас получились стикеры ;)

Ответить
0

На стикере нет информации, в какие даты искать билеты по такой цене, как этим пользоваться?

Ответить
2

Никак. Просто прикольно.

Ответить
0

Денис, другая сторона - являться русской аудиторией и не сидеть вод VPN...

Ответить
0

Антон, дело говоришь!
Но мой любимый tunnelbear.com ломает все рабочие VPN и пришлось отказаться от него. А другого дельного - я пока не нашел.

Ответить
0

Если айфон - vpn master хороший
Пока что..

Ответить
0

Со вчера ни один стикер не обновился.

Ответить
0

Вот вам еще с погодой стикеры, но там только москва и питер
https://t.me/addstickers/weather_by_aimedservicesbot

Ответить
0
{ "page_type": "article" }

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fizc" } } }, { "id": 4, "label": "240х200_mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "flbq" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "bscsh", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223676-0", "render_to": "inpage_VI-223676-0-1104503429", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=bugf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Плашка на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudx", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byzqf", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvc" } } }, { "id": 19, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } } ]
Компания отказалась от email
в пользу общения при помощи мемов
Подписаться на push-уведомления
{ "page_type": "default" }