Настоящая инструкция, как делать динамические стикеры в Telegram

Шо, посоны, анимэ? Что это за тренд пошел писать статьи про то, как создать в Тележеньке динамические стикеры — при том, либо максимально поверхностные, либо максимально тугие (вот вам curl, и любуйтесь им сколько вам угодно).

В закладки

Решил, чего это я, потрачу полчасика, напишу статью, как реально создать динамические стикеры в тележеньке на Node.js, TypeScript, Telegraf.js и с использованием еще горсти сторонних зависимостей. Ну и с исходным кодом, который любой ламер сможет адаптировать под свои нужды. Поехали!

Структуру проекта я особо не буду объяснять — стандартный бойлерплейт TypeScript, коих в интернетах-этих-ваших мульоны. Даже я стартер для себя написал недавно, чтобы упростить запуск MVP. Перейду сразу к соку.

Мы создадим простого бота под документации Telegraf.js. По команде /start он будет создавать стикерпак (желательно сделать это один раз) с одним стикером, а после будет по куллдауну обновлять этот один стикер.

Для генерации картинки стикера, воспользуемся пакетом text-to-picture (он немного приуныл, поэтому я сделал свой форк с блекджеком и нормальными зависимостями, который и буду использовать).

Алсо, я не буду углубляться в работу с @BotFather — по нему и по инструкции использования прокатились в других статьях. На текущий момент у вас должны быть:

  • Токен бота от @BotFather
  • Установленные на компуктере node и yarn

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

Создаем бота

Ну это очень просто. За пазухой у скрипта, естественно, dotenv, в котором лежат TOKEN (токен бота) и ADMIN (ваш user id). Начинаем кодирование, быстренько накатаем скриптик, который отвечает на /start.

import Telegraf from 'telegraf' const bot = new Telegraf(process.env.TOKEN) bot.start(ctx => ctx.reply('WoW Classic — это симулятор очередей')) bot.launch().then(() => console.log("It's alive!"))

Нойс. Теперь можем запустить скриптик при помощи того же yarn develop — команду, которую я любезно включил в `package.json` нашего стартера; отправить боту /start и даже получить ответ.

Генерим картинку

async function getStickerId() { const secondsAfterPHP = Math.floor( new Date().getTime() / 1000 - new Date('1995').getTime() / 1000 ) const result = await textToPicture.convert({ text: `${secondsAfterPHP}`, source: { width: 512, height: 512, background: '0xFF0000FF', }, color: 'white', }) const file = await bot.telegram.uploadStickerFile(ownerId, { source: await result.getBuffer(), }) return file.file_id }

Скриптец выше делает следующие вещи:

  • Считает количество секунд после создания PHP в 1995 году
  • Создает картинку при помощи text-to-picture пакета
  • Загружает буфер картинки от text-to-picture в Телеграм
  • Возвращает результат функции — file_id нашего стикера

Эту функцию мы сможем использовать дальше.

Создаем стикерпак

Тут все изи. Мы берем результат getStickerId функции и пихаем ее в функцию создания стикерпака. Ну а потом грех не ответить на это сообщение созданным стикером, чтобы вы могли себе сохранить стикерпак.

const stickerSetName = 'phpSuckedSeconds' bot.start(async ctx => { try { const botUsername = ctx.me const stickerId = await getStickerId() await ctx.telegram.createNewStickerSet( ownerId, `${stickerSetName}_by_${botUsername}`, 'PHP sucked for this many seconds', { png_sticker: stickerId, emojis: '💩', mask_position: undefined, } ) const stickerSet = await bot.telegram.getStickerSet(`${stickerSetName}_by_${botUsername}`) const sticker = stickerSet.stickers[0] return ctx.replyWithSticker(sticker.file_id) } catch (err) { return ctx.reply(err.message) } })

Опять пройдемся построчно, что тут мы имеем:

  • Во-первых, тут все может плюнуть в нас ошибкой — поэтому мы ловим ошибку в одном месте и отвечаем ей пользователю, если нужно
  • Дальше мы получаем username нашего бота, чтоб создать в итоге название стикерпака, оканчивающееся на _by_mybot — неочевидная обязабма в API
  • Используем вышеописанную функцию и получаем идентификатор стикера
  • Создаем сам стикер пак простой командой
  • Так как эта команда возвращает лишь результат выполнения (получилось или нет), мы еще раз вытягиваем информацию о паке стикеров
  • Берем первый (и единственный) стикер
  • Отвечаем этим стикером нам любимым
Как-то так в итоге получается

Делаем стикерпак 🌈 *динамичным* 🦄

Здесь все просто — каждые 30 секунд мы будем удалять единственный стикер и добавлять туда новый. Вот код функции, собственно говоря.

setInterval(updateSticker, 30 * 1000) async function updateSticker() { console.log('Updating stickers') const botUsername = bot.options.username const stickerSet = await bot.telegram.getStickerSet( `${stickerSetName}_by_${botUsername}` ) const sticker = stickerSet.stickers[0] await bot.telegram.deleteStickerFromSet(sticker.file_id) await bot.telegram.addStickerToSet( ownerId, stickerSetName, { png_sticker: await getStickerId(), emojis: '💩', mask_position: undefined, }, false ) console.log('Updated stickers') }

Никакой магии, мы просто:

  • Заставляем безмозглую машину выполнять одно и то же действие каждые 30 секунд
  • Логируем нам любимым, что обновление инициированно
  • Получаем стикерпак и единственный стикер оттуда
  • Удаляем этот стикер
  • Добавляем новый стикер с новым временем, воспользовавшись функцией создания и загрузки картинки
  • Логируем окончание процесса
  • А, это все

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

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

Вот так

1. Качаете файлики из стартера

2. Ставите себе нужные зависимости через yarn install

3. Создаете файл .env

4. Заполняете в нем TOKEN и ADMIN поля, как в .env.sample

3. Открываете src/app.ts

4. Меняете stickerSetName и stickerSetDescription

5. Меняете переменную text (это то, что будет на картинке)

6. Запускаете все через yarn develop

Зетс ол фолкс!

Заключение и наглый пиар себя

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

К слову, на любые вопросы с удовольствием отвечу в тех же самых комментариях. Ну и, как принято на VC.ru, пара слов о моем новом проекте, чтобы вы обязательно зашли и покрутили его.

Todorant — это сервис, который позволяет мне лично держать голову легкой (без веса постоянно накапливающихся задач в моих тудулистах) благодаря идеям из книг Eat That Frog, Willpower и Getting Things Done, что я недавно прочитал.

Я взял оттуда методологии, упростил, убрал лишнее и обернул все в приятный вебсервис. Todorant абсолютно бесплатен — попробуйте его сегодня, если вам надоело, что вы постоянно теряетесь в задачах, которые необходимо сделать здесь и сейчас.

Спасибо!

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

Написать
{ "author_name": "Nikita Kolmogorov", "author_type": "self", "tags": [], "comments": 24, "likes": 37, "favorites": 84, "is_advertisement": false, "subsite_label": "dev", "id": 81312, "is_wide": true, "is_ugc": true, "date": "Sat, 31 Aug 2019 21:59:45 +0300", "is_special": false }
0
{ "id": 81312, "author_id": 60968, "diff_limit": 1000, "urls": {"diff":"\/comments\/81312\/get","add":"\/comments\/81312\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/81312"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 235819, "last_count_and_date": null }
24 комментария
Популярные
По порядку
Написать комментарий...
7

Спасибо за материал, я ламер и мне интересно.
Но разрешите пару замечаний. Коль вы пишите доя ламеров (остальным наверное и не надо) :
1 множество терминов да ещё английских на русском. Это не для ламеров
2 стиль изложения 'пацановский', смотрите как все просто. Это не для ламеров
По факту нужна грамотная инструкция которая научит и поможет.

Ответить
3

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

Ответить
1

NASA теперь берет с улицы? 3 языка и это только людских?

Не прибедняйтесь:) Да и улица ваших лет, это спокойствие и благодать.

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

Ответить
1

После слов node, yarn, dotenv, я приуныл и понял, что это совсем не для ламеров.

Ответить
1

Да на английском это ещё норм, погуглить можно. В транскрипте тяжелей

Ответить
3

Я первый 👏

Ответить
1

Второй на#

Ответить
0

Делаем ставки сколько еще будет инструкций

Ответить
8

Сначала будет вебсервис-конструтор таких стикеров, а потом аггрегатор таких сервисов и статей по ним.

Ответить
2

Такое ощущение, что попала в 2007-й или на Хабр. Афтар, непеши есчо. Серьёзно, найдётся кто-то, кто эту прелесть переведёт на вменяемый русский и перепишет без сленга типа «алсо»?

Ответить
2

Спасибо за комментарий, не буду писать ещё!

Ответить
2

Ггг, старый хабр на виси ;)
А сленга родного сколько. Пиши еще

Ответить
0

Спасибо за комментарий, буду писать ещё!

Ответить
2

Ну хоть кто-то начал делать полезные боты для телеграм

Ответить
2

Судя по некоторым словам автору за 30. Но статья интересная, спасибо.

Ответить
2

Зашёл на сайт:
Этот вебсайт использует печеньки. Мы не следим за вами. Но Фейсбук, возможно, следит. Понятненько!

Ясненько )))

Ответить
0

Старался быть максимально открытым и правдивым в этом попапе. Спасибо, что посмотрели сайтец :) для меня это очень важно

Ответить
1

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

Ответить
1

ИМХО, программисты понимают инструкции на любых языках — на то они и программисты, ведь все языки — это просто друг друга братья и кузены.

Если человек не может прочитать псевдокод или код на каких-нибудь путоне, рубях или (господи-упаси) Яве, то не программист это вовсе.

Но я соглашусь с вашей претензией — статья получилась черезчур JS-хеви.

Ответить
1

Я не верно выразился, правильнее сказать было бы, непонятна без вникания в код, с друго стороны непрограммисту будет проще

Ответить
0

Сначала удаляем стикер, потом добавляем новый. А что случится, если кто-нибудь запросит стикер между двумя операциями?

Ответить
0

Какой-нибудь "sticker not found". Хорошей идеей будет:

1. Всегда в паке держать минимум один постоянный стикер (например, с указанием авторства)
2. Не удалять, а потом добавлять, но наоборот: добавить — и потом удалить.

Но это уже вне рамок статьи :) ну и после удаления стикера он не исчезает из чатов, куда его уже кидали.

Ответить
0

Если удалить последний стикер, стикерпак не будет открываться с устройства пока в него не положишь новый

Ответить

Комментарий удален

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": "Article Branding", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "cfovx", "p2": "glug" } } }, { "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, "disable": true, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } } ] { "page_type": "default" }