Я создал Телеграм бота (FYTT), который ищет Телеграм каналы всех ваших подписок на ютубе. DELUXE-версия статьи

от автора

Расширенная версия: Я создал Телеграм бота (FYTT), который ищет Телеграм каналы всех ваших подписок на ютубе / Хабр (habr.com)
Бот — https://t.me/FYTTproject_bot

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

Опыта мало в разработке, поэтому стек особо не выбирал:

  • Node.js, библиотека telegraf.js

  • MongoDB, mongoose

  • Express.js

Алгоритм работы бота, такой:

Пользователь пишет /start, ему выдается приветственное с 2 кнопками: «Найти Youtube-каналы в Telegram» и «Связаться Youtube-канал с Telegram-каналом». Также, пользователь сразу же добавляется в базу данных с сохранение его chatId, для дальнейшей связи, в случае чего и awatingChannels, о котором рассказал ниже.

bot.start(async (ctx) => {     const chatId = ctx.chat.id;     let chat = await Analytics.findOne({ chatId: chatId })     if (chat === null) {         try {             let username = ctx.message.chat.username             let newChat = new Analytics({                 chatId: ctx.message.chat.id,                 awatingChannels: true,                 status: "member",                 count: 0             })             await newChat.save()         } catch {             let newChat = new Analytics({                 chatId: ctx.message.chat.id,                 awatingChannels: true,                 status: "member",                 count: 0             })             await newChat.save()         }     } else {         chat.awatingChannels = true         await chat.save()     }       await setBotCommands()     ctx.replyWithHTML('<b>Приветствуем вас в нашем сервисе поиска Telegram-каналов ютуберов!</b>\nБот безопасен, так как представляет собой открытый исходный код, который может посмотреть каждый желающий. (/faq или пишите @vitosperansky)\n\nПоддержать проект: https://www.donationalerts.com/r/vitosperansky\n\nВыберите опцию:', Markup.inlineKeyboard([         [Markup.button.callback('Найти YouTube-каналы в Telegram', 'find_channels')],         [Markup.button.callback('Связать YouTube-канал с Telegram-каналом', 'link_channel')]     ]), {         disable_web_page_preview: true     }); });

При нажатие на первую, пользователь получает кнопку с ссылкой на авторизацию в Google, после авторизации отправляется ответ на сервер на «/oauth2callback», запуская функцию поиска:

async function listSubscriptions(auth) {     const service = google.youtube('v3');     let subscriptions = [];     let nextPageToken = null;      try {         do {             const response = await service.subscriptions.list({                 auth: auth,                 part: 'snippet',                 mine: true,                 maxResults: 50,  // 50 - лимит Google API                 pageToken: nextPageToken  // Устанавливаем токен страницы, если есть             });              // Добавляем текущую партию подписок             subscriptions = subscriptions.concat(response.data.items.map(item => ({                 title: item.snippet.title,                 channelId: item.snippet.resourceId.channelId             })));              // Получаем токен следующей страницы, если он есть             nextPageToken = response.data.nextPageToken;          } while (nextPageToken);  // Продолжаем, пока есть токен следующей страницы          return subscriptions;     } catch (error) {         console.error('Ошибка получения подписок', error);         return [];     } }

Далее функция возвращает ответ и запускается функция, которая проверяет есть ли данный канал в базе данных бота (чтобы в будущем, с увеличением базы, ускорить выдачу каналов).

async function checkAndAddNewChannels(subscriptions, youtubeApiKey, chatId) {     let chat = await Analytics.findOne({ chatId: chatId })     if (chat.awatingChannels) {         let msgWait = await bot.telegram.sendMessage(chatId, `Бот сопоставляет Youtube и Telegram каналы, это займет время...`);         chat.awatingChannels = false          await chat.save()          const youtubeUrls = subscriptions.map(sub => `https://www.youtube.com/channel/${sub.channelId}`);          const foundChannels = await Channel.find({ youtube_url: { $in: youtubeUrls } });          // Фильтрация не найденных каналов         const foundUrls = new Set(foundChannels.map(ch => ch.youtube_url));         const notFoundChannels = subscriptions.filter(sub => !foundUrls.has(`https://www.youtube.com/channel/${sub.channelId}`));         ...

Если канал не найдется в базе данных, то запустится функция для парса телеграм каналов из раздела «ссылки» на ютубе. Хотелось бы использовать для этого Youtube API аж 3 версии, но там нет функционала, для получения этих данных ?, поэтому я нашел стороннее API — YouTube operational API (lemnoslife.com), с которым приятно работать. Автору респект! (Единственное, username канала почему-то находится в разделе сообщество, именно в апи лемнос, а дело в том, что не у всех каналов есть вкладка сообщество).

// Функция для получения ссылок из описания канала с помощью Lemson API async function getChannelLinksFromDescription(channelId) {     try {         const response = await axios.get(`${LEMNOS_API_URL}?part=about&id=${channelId}`);         return response.data;     } catch (error) {         logger.error(`Error getting channel info from Lemnos Life API: ${error}`);         return null;     } }

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

Была одна надоедливая проблема, которую я костыльно решил: при авторизации в google аккаунт запрос на сервер отправляется дважды, если пользователь дважды нажимает на выбор аккаунта. Сложно объяснить, но думаю, те кто работал сталкивался с этим. Эта проблема появляется только, если у человека несколько аккаунтов. Поэтому, чтобы сообщения не отправлялись дважды, я стал записывать в документ пользователя в базе данных переменную awatingChannels. При нажатии на кнопку найти каналы ее значение становилось true, а при получение каналов меняло на false, что решило проблему. Думаю, нужно было реализовать это через сессии, что я и пробовал изначально, но что-то пошло не так и я решил по-быстрому все сделать так.

Кнопка: «Связать Youtube-канал с Telegram-каналом» позволяет пользователю вручную добавить связку ютуб-канал + тг-канал, если автор ютуб-канала не оставил ссылку на свой тг в описании. (Сначала запрос отправляется на модерацию мне, а потом, при принятии попадает в общую базу данных).

Для логирования ошибок, впоследствии, стал использовать pino.

Теперь бот работает и выполняет свою функцию. У него есть еще несколько команд, такие как:

  • /submit (text) — для связи со мной

  • /faq — ответы на вопросы

  • /send — чтобы я мог связаться с пользователем

Еще несколько системных и мой прикол — /jericho (иерихон)

bot.command('jericho', async (ctx) => {     if (ctx.message.chat.id == MODERATOR_CHAT_ID) {         ctx.replyWithVideo(`https://i.gifer.com/370.gif`)         throw new Error()     } else {         %*%(НЛО прилетело и скрыло эту надпись здесь)%*%     } })

Которая вызывает ошибку, а вместе с ней прекрасную гифку для меня: https://i.gifer.com/370.gif

Теперь есть работающий бот, но Google не дает доступ к API тем, про кого не знает (только для тестов). Поэтому нужно пройти проверку. У тебя должен быть сайт, политика, terms и прочее.

Начал я с логотипа и отправил им это

Вообще это типо ютуб сгорает, а под ним телеграм, но кто-то увидел тут нос человека :)

Вообще это типо ютуб сгорает, а под ним телеграм, но кто-то увидел тут нос человека 🙂

Google сказал, что это лого не отражает вашу индивидуальность. (видимо, из-за слишком явного использования автарок телеграма и ютуба).

Тогда я решил показать всю свою индивидуальность:

FromYoutubeToTelegram

FromYoutubeToTelegram

Они это приняли, я пошел быстренько сделал сайт, попросил ChatGPT написать политику и отправил. Оказалось надо быть владельцем домена, на котором размещен сайт, поэтому пришлось купить. Подключил сертификаты через certbot. И отправил снова.

На этот раз им не понравилась политика, они жестко ответили, мол: Вы написали, что «Google продает данные пользователей.». Я перечитал все, что ChatGPT написал, не нашел там такого, попросил переделать, отправил снова и теперь уже все приняли :). Еще надо было сделать им демо-видое работы, ну ладно без проблем — FYTT — Найди Ютуб каналы в Телеграмм (Полезный бот в Telegram) | @FYTTproject_bot — YouTube

Потом я решил продвинуть бота и записал два смешных shorts:

  • https://youtu.be/MlXEUIDBhE0 — Speech to speech моей записи на ии оригинального голоса Рика из Рика и Морти (сделал на этом сайте).

Ну и написал первую версии статьи: Я создал Телеграм бота (FYTT), который ищет Телеграм каналы всех ваших подписок на ютубе / Хабр (habr.com)

Получил много положительной реакции, до статьи у меня было 37 человек, которые запустили бота, а теперь 235 из которых 93+ использовали его функционал.

Ссылки:

Бот — https://t.me/FYTTproject_bot

Исходный код — https://github.com/VitoSperansky/FromYoutubeToTelegram

Сайт — fytt.tech:3000 (порт 3000, потому что порты ниже 1024 по умолчанию закрыты для установки серверов, их можно открыть, но это угроза безопасности. По-хорошему мне надо было сделать переадресацию с 443 порта (он дефолтный для https сайтов, поэтому скрыт в поисковой строке и пользователи его не видят) с помощью ngrok например, но я не стал тратить силы, так это все равно заглушка для гугла).

Связаться со мной по вопросам или просто — https://t.me/vitosperansky


ссылка на оригинал статьи https://habr.com/ru/articles/839592/