IT-инфраструктура для бизнеса и творчества

Инструкция по созданию стикеров-информеров в 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 на выполнение скрипта каждые пять минут. Готово. Вы прекрасны!

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

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

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

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

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

(function () { let cdnUrl = `https://specialsf378ef5-a.akamaihd.net/SelectelBranding/images/` let previousArticleNumber = null let currentArticleNumber = 0 let platform = 'Desktop' let articles = [ { name: 'camera', url: `${cdnUrl}CameraCat`, text: 'умную камеру для\u00A0наблюдения за\u00A0котиками', link: 'https://vc.ru/selectel/306690', num: 3 }, { name: 'chill', url: `${cdnUrl}ChillCat`, text: 'трекер, который подскажет, когда пора отдохнуть', link: 'https://vc.ru/promo/288561-eye-tracker', num: 1 }, { name: 'cloud', url: `${cdnUrl}CloudCat`, text: 'котика: даёшь ему «пять», а\u00A0он делает бэкап в облако', link: 'https://vc.ru/dev/294799-maneki-neko', num: 2 } ] let buttonCycle = document.querySelector('.button--cycle') let buttonChoose = document.querySelector('.button--choose') let buttonMobile = document.querySelector('.button--mobile') let textField = document.querySelector('.selectel-footer-subtitle') let imageAgent = document.querySelector('.image--agent') let banner = document.querySelector('.selectel-footer') buttonCycle.addEventListener('click', cycleClick) buttonChoose.addEventListener('click', () => sendEvent(`Promo ${articles[currentArticleNumber].num} Left`, 'Click')) buttonMobile.addEventListener('click', () => sendEvent(`Promo ${articles[currentArticleNumber].num} Left`, 'Click')) let media = window.matchMedia("(max-width: 570px)") media.addEventListener('change', matchMedia) function matchMedia() { if (media.matches) { platform = 'Mobile' } else { platform = 'Desktop' } update() } matchMedia() function cycleClick(event) { sendEvent(`Promo ${articles[currentArticleNumber].num} Right`, 'Click') if (event) { event.preventDefault() event.stopPropagation() } window.open('https://vc.ru/tag/selectelDIY', '_blank') //cycle(event) } function cycle(event) { // incrementArticleNumber() textField.innerHTML = generatedText() imageAgent.src = articles[currentArticleNumber].url + platform + '.svg?3' imageAgent.setAttribute("class", "") imageAgent.classList.add('image--agent', articles[currentArticleNumber].name) banner.href = articles[currentArticleNumber].link } function update() { banner.href = articles[currentArticleNumber].link imageAgent.src = articles[currentArticleNumber].url + platform + '.svg' textField.innerHTML = generatedText() } function incrementArticleNumber() { previousArticleNumber = currentArticleNumber if (currentArticleNumber >= articles.length - 1) { currentArticleNumber = 0 } else { currentArticleNumber++ } } const sendEvent = (label, action = 'Click') => { const value = `SelectelDIY — loc: Footer — ${label} — ${action}`; if (window.dataLayer !== undefined) { window.dataLayer.push({ event: 'data_event', data_description: value, }); } }; function generatedText() { let defaultText if (platform === 'Desktop') { defaultText = `Мы тут собрали %text%. Хотите научим?` } else { defaultText = `Мы тут собрали %text%.` } return defaultText.replace('%text%', articles[currentArticleNumber].text) } function getRandom(min, max) { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min + 1)) + min } (function create() { currentArticleNumber = getRandom(0, articles.length - 1) cycle() let page = document.querySelector('.page--entry') if (page) { function insertAfter() { let parents = page.querySelectorAll('[data-id="7"]') let referenceNode = parents[0] referenceNode.parentNode.insertBefore(banner, referenceNode.nextSibling); loaded() } setTimeout(() => insertAfter(), 0) } }()) function loaded() { banner.classList.add('loaded') } loadImages([ `${cdnUrl}CameraCatDesktop.svg`, `${cdnUrl}ChillCatDesktop.svg`, `${cdnUrl}CloudCatDesktop.svg`, `${cdnUrl}CameraCatMobile.svg`, `${cdnUrl}ChillCatMobile.svg`, `${cdnUrl}CloudCatMobile.svg?3`, ]) function loadImages(urls) { return Promise.all(urls.map(function (url) { return new Promise(function (resolve) { var img = document.createElement('img'); img.onload = resolve; img.onerror = resolve; img.src = url; }); })); } }())
0
22 комментария
Популярные
По порядку
Написать комментарий...

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

8

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

13

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

2

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

1

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

0

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

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

8

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

https://tlgg.ru/addstickers/tickets_by_aviasalesTicketStickersBot

4

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

0

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

1

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

0

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

3

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

0

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

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

2

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

1

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

0

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

2

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

0

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

0

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

0

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

0

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

0
Читать все 22 комментария
Сбербанк компрометирует код CVC при выдаче карты

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

И сотрудников тоже касается: кибербуллинг на рабочем месте
Design vector created by pikisuperstar - www.freepik.com
Мой опыт общения с Почтой России

Первым делом хочу попросить прощения у всех граждан России. Благодаря моим действиям, описанным вкратце в этой статье, я получил некоторую сумму денег, которая иначе могла бы попасть в бюджет РФ. Но произошло это по вине сотрудников АО «Почта России». Учредителем и единственным акционером АО «Почта России» является Российская Федерация.

Сайты в 2021 году
Tele2. Недоразумение со сменой тарифа

Моя бабушка пользуется тарифом без абонентской платы от Tele2. Недавно ей позвонили и предложили поменять тарифный план. Бабушка в этом ничего не понимает, но прекрасно понимает, что не стоит принимать решения, не посоветовавшись с детьми/внуками. Поэтому она, почти сразу,прервала разговор. На следующий день выяснилось, что с ее номера телефона…

SkillFactory раздает подарки: повышенная ставка и новогодний марафон для вебмастеров

В преддверии Нового года мы решили порадовать своих настоящих и будущих партнеров — участников партнерской программы школ Skillfactory, Contented и Product LIVE. Это возможность получить денежный бонус и заодно увеличить прибыль от продажи наших курсов.

От чтения мыслей до вторжения во сны: зачем исследуют сознание и чем это грозит Статьи редакции

Учёные научились считывать структуру фраз и даже визуальные образы из мыслей. Теперь они переживают, что компании внедрят в сны рекламу.

Дайджест новостей Сбера: сайт Digital Пётр, сценарии для умного дома и платина от Forbes

Прошлый дайджест мы целиком посвятили 180-летию Сбера, поэтому новостей накопилось много. Среди них — запуск сайта по распознаванию рукописей Петра I, большое обновление на платформе умного дома Sber и другие. Рассказываем всё самое интересное.

Картинка, сгенерированная ruDALL-E по запросу «рыжий котик»
Откуда берут взрослые деревья для парков и улиц

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

Продавец eBay из Кургана стала победителем в финале Всероссийского конкурса «Молодой предприниматель России 2021»

27 ноября в Москве состоялся финал ежегодного конкурса «Молодой предприниматель России 2021». В нём приняли участие предприниматели и самозанятые в возрасте до 35 лет. Всего было подано более 300 заявок из 43 регионов страны.

Чему создатели метавселенных могут поучиться у 3D-игры Second Life Статьи редакции

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

Кадр из игры Second Life SL Community
null