Pull to refresh

Я создал Telegram-бота (FYTT), который ищет Telegram-каналы всех ваших подписок на YouTube

Level of difficultyEasy
Reading time6 min
Views1.9K

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

Что я использовал?

Поскольку опыта в разработке у меня было мало, я выбрал следующий стек технологий:

  • Node.js с библиотекой telegraf.js для работы с Telegram API

  • MongoDB и mongoose для работы с базой данных

  • Express.js для создания веб-сервера

  • Google API для работы с YouTube

  • Lemnos API для получения дополнительной информации о каналах

Реализация

1. Основные команды бота

Начнем с главной команды /start, которая инициализирует работу с ботом:

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,
                username: username,
                awatingChannels: true,
                status: "member",
                count: 0
            })
            await newChat.save()
        } catch {
            // Если username недоступен, используем first_name
            let newChat = new Analytics({
                chatId: ctx.message.chat.id,
                username: ctx.message.chat.first_name,
                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
        }
    );
});

2. Авторизация через Google

Для работы с YouTube API необходима авторизация через Google. Вот как реализована генерация URL для авторизации:

async function generateAuthUrl(chatId) {
    const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH));
    const { client_id, client_secret } = credentials.web;
    const oAuth2Client = new OAuth2Client(
        client_id, 
        client_secret, 
        REDIRECT_URL
    );
    const authUrl = oAuth2Client.generateAuthUrl({
        access_type: 'online',
        scope: SCOPES,
        state: chatId.toString()
    });
    return authUrl;
}

Когда пользователь нажимает на кнопку поиска каналов, запускается следующий обработчик:

const find_channels = async (ctx) =&gt; {
    const chatId = ctx.chat.id;
    const authUrl = await generateAuthUrl(chatId, ctx);

    ctx.replyWithMarkdown(
        '*Нажмите кнопку ниже для авторизации на Youtube и получения списка ваших подписок:*\n\n' +
        '❗Авторизация нужна только для получения списка ваших подписок ' +
        '(запрашиваются права youtube.readonly - только чтения, подробнее /faq)❗\n\n' +
        '_Процесс займет время: ~50 секунд. (в зависимости от количества ваших подписок)_', 
        {
            reply_markup: {
                inline_keyboard: [
                    [{ text: 'Авторизоваться и найти подписки', url: authUrl }]
                ]
            }
        }
    );
};

// Обработчики команды поиска каналов
bot.action('find_channels', async (ctx) =&gt; {
    ctx.answerCbQuery();
    await find_channels(ctx)
})

bot.command('find_channels', async (ctx) =&gt; {
    await find_channels(ctx)
})

3. Дополнительные команды

Бот также имеет несколько дополнительных команд для удобства использования:

// Команда FAQ
bot.command('faq', async (ctx) =&gt; {
    ctx.replyWithMarkdown(`
**Ответы на вопросы о проекте:**

Какова цель проекта?
— Максимально упростить поиск Телеграмм каналов ваших любимых авторов.

У меня не украдут Google Аккаунт?
— Нет, бот имеет открытый исходный код, который может посмотреть каждый желающий на Github - https://github.com/VitoSperansky/FromYoutubeToTelegram.

Как работает бот?
— Бот просит вас авторизоваться в свой Google аккаунт, чтобы получить список ваших подписок на YouTube. 
Затем система обращается к своей базе данных, где хранятся соответствия YouTube-каналов и их Телеграмм-каналов. 
Если бот находит соответствия в базе данных, он записывает их в список найденных каналов. 
Если YouTube-каналы, на которые вы подписаны, отсутствуют в нашей базе данных, бот отправляет запрос 
в YouTube на получение ссылок социальных сетей, привязанных к каналу. Среди этих ссылок бот ищет 
ссылку на Телеграмм. Найдя новую ссылку на Телеграмм-канал, бот добавляет её в базу данных. 
В итоге, пользователь получает список YouTube-каналов с их Телеграмм-каналами.

Остались вопросы? - Пишите @vitosperansky
    `);
});

// Команда для рассылки сообщений (только для администратора)
bot.command('send', async (ctx) =&gt; {
    if (ctx.message.chat.id == MODERATOR_CHAT_ID) {
        let chatId = ctx.message.text.replace('/send ', '').replace(/ [\s\S]+/, '');
        let text = ctx.message.text.replace('/send ', '').replace(`${chatId} `, '').toString();

        if(chatId === 'all') {
            let Users = await Analytics.find()
            let goodSend = [];
            let badSend = [];
            ctx.reply("Рассылка началась.")
            for (let i = 0; i &lt; Users.length; i++) {
                try {
                    await bot.telegram.sendMessage(Users[i].chatId, text, { parse_mode: "HTML" });
                    goodSend.push(Users[i]);
                } catch (error) {
                    badSend.push(Users[i]);
                }
            }
            ctx.reply(`Рассылка завершена\n\nУспешно отправлено: ${goodSend.length} сообщений.\n` +
                      `Не получилось отправить: ${badSend.length} сообщений.`)
        } else {
            try {
                await bot.telegram.sendMessage(chatId, text, { parse_mode: "HTML" });
                ctx.reply(`Сообщение успешно отправлено пользователю. \n\nChatId: ${chatId}\nТекст: ${text}`)
            } catch {
                ctx.reply("Ошибка при отправке сообщения.")
            }
        }
    } else {
        ctx.reply("Вы не админ!")
    }
});

Проблемы и их решения

1. Проблема дублирования запросов

При авторизации в Google-аккаунте возникла проблема с дублированием запросов, если у пользователя несколько аккаунтов. Для решения этой проблемы я использовал флаг awaitingChannels в базе данных, который позволяет отслеживать состояние запроса и избегать дублирования.

2. Ограничение длины сообщений

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

3. Сайт

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

Процесс верификации Google

Получение доступа к YouTube API потребовало пройти верификацию Google. Процесс включал несколько этапов:

  1. Создание логотипа: Первая версия логотипа была отклонена из-за слишком явного использования элементов YouTube и Telegram. Пришлось создать более оригинальный дизайн.

    1 версия - отклонена (кто-то увидел тут силуэт лица человека xD)
    1 версия - отклонена (кто-то увидел тут силуэт лица человека xD)
    2 версия - принято (на фоне текст fromyoutubetotelegram)
    2 версия — принято (на фоне текст fromyoutubetotelegram)
  2. Разработка сайта: Потребовалось создать сайт с политикой конфиденциальности и пользовательским соглашением. При этом возникли следующие требования:

    • Необходимость владения доменом

    • Настройка SSL-сертификатов через certbot

    • Корректная политика конфиденциальности

  3. Демонстрация работы: Создание демо-видео для показа функционала бота. (пришлось им видео записать под смешную музыку)

Продвижение проекта

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

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

Полезные ссылки

Планы на будущее

  1. Улучшение алгоритма поиска каналов

  2. Оптимизация работы с базой данных

  3. Добавление новых функций по запросам пользователей

  4. Настройка правильной маршрутизации портов на сервере

Заключение

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

Tags:
Hubs:
Total votes 4: ↑3 and ↓1+4
Comments8

Articles