Бесплатная миграция IT-инфраструктуры в облако

Как создать Telegram-бота на Node.js

Вадим Андоськин
Вадим Андоськин
Технический писатель
09 октября 2024 г.
92
15 минут чтения
Средний рейтинг статьи: 5

Боты в Telegram стали неотъемлемой частью этого мессенджера: каждый день сотни тысяч людей пользуются ими, и неспроста. Telegram-боты легки в использовании для пользователей, а разработчики могут быстро и комфортно их создавать благодаря развивающемуся API Telegram, который старается становиться лучше с каждым днём.

Идеей Telegram-ботов является автоматизация задач и расширение функционала мессенджера. Боты могут выступать как в роли простых помощников, выполняя команды, так и в роли сложных систем с полноценной бизнес-логикой. От рассылки новостей до сложных игровых механик — возможности ботостроения практически безграничны.

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

  • Базовый функционал:

    • Ответы на команды.
    • Инлайн-боты.
    • Кнопки.
  • Интеграции с внешними сервисами:

    • API и базы данных.
    • Webhook.
  • Уведомления:

    • Рассылка уведомлений по расписанию, или при наступлении определённых событий.
    • Автоматическая отправка новостей из источников через каждые N секунд.
  • Аналитика:

    • Сборка различной статистики.

Создание Telegram-бота

Сперва нужно создать бота внутри Telegram. Воспользуйтесь официальным ботом BotFather, чтобы зарегистрировать бота. Используйте кнопку «Запустить» (а если вы уже пользовались ботом, то отправьте команду /start), в ответе бота найдите и выберите команду /newbot. BotFather предложит указать имя бота, а затем его username. При этом нужно, чтобы в конце было слово bot. Например, если у вашего бота название Tetris, то username должен быть таким:

  • TetrisBot
  • Tetris_bot
  • Tetrisbot
  • Tetris_Bot

Если всё указано верно, то бот будет создан. BotFather также выдаст вам уникальный токен бота, который нельзя никому показывать.

Image9

Разработка

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

Перед началом разработки нужно, чтобы на вашем ПК были установлены Node.js и npm. Скачать Node.js можно с официального сайта, а npm установится автоматически вместе с Node.js. Если же вы используете Linux, то установить npm можно, воспользовавшись инструкцией.

Если Node.js установлен, можно приступать к разработке бота. Сперва создайте на GitHub новый приватный репозиторий и в пункте Add .gitignore выберите Node

Image11

Теперь клонируйте этот репозиторий себе на ПК используя консоль. Если вы хотите, чтобы проект находился на рабочем столе, то введите:

cd Desktop

Затем введите:

git clone https://github.com/Tulopex/School-Quiz

Вместо ссылки на мой репозиторий, используйте свою.

Далее, после клонирования, не закрывая консоль, введите:

cd School-Quiz 

Вместо School-Quiz используйте реальное название папки вашего проекта, которая клонировалась с GitHub.

Для инициализации проекта выполните команду:

npm init

Вам предложат ввести имя пакета, версию, описание, главный файл по умолчанию, тест команду, Git репозиторий, ключевые слова, автора и лицензию. Вы можете оставить значения по умолчанию, нажимая «Enter».

Image6

Теперь установим библиотеку, с помощью которой и будем писать код для бота. Введите в консоль (убедившись, что находитесь в папке с проектом) команду:

npm install node-telegram-bot-api

После завершения процесса установки можно приступать к написанию кода. Откройте файл package.json и найдите строку scripts, внутрь неё над командой test, введите:

"start": "node index.js",

Это необходимо для того, чтобы можно было запускать проект вводя в консоль npm start, а не node и имя файла.

Теперь создайте файле index.js и внутри него напишите следующее:

const TelegramBot = require('node-telegram-bot-api');
const fs = require('fs');


const bot = new TelegramBot('TOKEN', { polling: true }); // Вместо слова TOKEN, укажите реальный токен вашего бота, который выдал вам BotFather 


const ADMIN_ID = '1402655980';
let awaitingSupportMessage = {}; // Хранение информации о пользователях, ожидающих поддержки
// Хранение выбранных тем для пользователей
let userTopics = {};


// Темы и файлы с вопросами
const topics = {
  math: { name: 'Математика', file: 'questions/math.json' },
  english: { name: 'Английский', file: 'questions/english.json' },
  history: { name: 'История', file: 'questions/history.json' }
};


// Функция для получения вопросов по выбранным темам
function getQuestionsByTopics(userId) {
  const selectedTopics = userTopics[userId] || Object.keys(topics);
  let allQuestions = [];
  selectedTopics.forEach(topic => {
    const questions = JSON.parse(fs.readFileSync(topics[topic].file, 'utf8'));
    allQuestions = allQuestions.concat(questions);
  });
  return allQuestions;
}


function getRandomQuestion(userId) {
  const questions = getQuestionsByTopics(userId);
  const randomIndex = Math.floor(Math.random() * questions.length);
  return questions[randomIndex];
}


bot.onText(/\/quiz/, (msg) => {
  const chatId = msg.chat.id;
  const userId = msg.from.id;
 
  // Получаем случайный вопрос
  const questionData = getRandomQuestion(userId);
 
  // Отправляем опрос в виде викторины
  bot.sendPoll(
    chatId,
    questionData.question, // Вопрос
    questionData.options,  // Варианты ответов
    {
      type: 'quiz', // Тип викторины
      correct_option_id: questionData.correct_option_id, // Правильный ответ
      is_anonymous: false // Вопрос не будет анонимным
    }
  ).then(pollMessage => {
    // Обрабатываем результаты опроса
    bot.on('poll_answer', (answer) => {
      if (answer.poll_id === pollMessage.poll.id) {
        const selectedOption = answer.option_ids[0];
        // Проверяем, правильный ли ответ
        if (selectedOption !== questionData.correct_option_id) {
          bot.sendMessage(chatId, questionData.explanation);
        }
      }
    });
  });
});


bot.onText(/\/settopic/, (msg) => {
  const chatId = msg.chat.id;
  const userId = msg.from.id;


  const keyboard = Object.keys(topics).map(topicKey => ({
    text: `${(userTopics[userId] || []).includes(topicKey) ? '✅ ' : ''}${topics[topicKey].name}`,
    callback_data: topicKey
  }));


  bot.sendMessage(chatId, 'Выберите темы для вопросов:', {
    reply_markup: {
      inline_keyboard: [keyboard]
    }
  });
});


// Обработчик выбора тем
bot.on('callback_query', (callbackQuery) => {
  const message = callbackQuery.message;
  const userId = callbackQuery.from.id;
  const topicKey = callbackQuery.data;


  // Инициализируем выбранные темы для пользователя, если их нет
  if (!userTopics[userId]) {
    userTopics[userId] = Object.keys(topics);
  }


  // Добавляем или удаляем тему
  if (userTopics[userId].includes(topicKey)) {
    userTopics[userId] = userTopics[userId].filter(t => t !== topicKey);
  } else {
    userTopics[userId].push(topicKey);
  }


  // Обновляем сообщение с кнопками
  const keyboard = Object.keys(topics).map(topicKey => ({
    text: `${userTopics[userId].includes(topicKey) ? '✅ ' : ''}${topics[topicKey].name}`,
    callback_data: topicKey
  }));


  bot.editMessageReplyMarkup({
    inline_keyboard: [keyboard]
  }, { chat_id: message.chat.id, message_id: message.message_id });
});


bot.onText(/\/start/, (msg) => {
  const chatId = msg.chat.id;
  bot.sendMessage(chatId, "Привет! Напиши /quiz, чтобы начать викторину. Для выбора тем используй /settopic.");
});


console.log('Бот запущен.');

Теперь создайте папку внутри проекта под названием questions, а внутри этой папки нужно создать три JSON-файла:

english.json

[
  {
    "question": "Как будет 'я' на английском языке?",
    "options": ["I", "You", "We"],
    "correct_option_id": 0,
    "explanation": "Правильный ответ: I."
  },
  {
    "question": "Как переводится глагол 'run'?",
    "options": ["бежать", "идти", "стоять"],
    "correct_option_id": 0,
    "explanation": "Правильный ответ: бежать."
  },
  {
    "question": "Как будет 'он' на английском языке?",
    "options": ["He", "She", "It"],
    "correct_option_id": 0,
    "explanation": "Правильный ответ: He."
  }
]

history.json

[
  {
    "question": "В каком году началась Вторая мировая война?",
    "options": ["1939", "1941", "1914"],
    "correct_option_id": 0,
    "explanation": "Правильный ответ: 1939."
  },
  {
    "question": "Кто был первым президентом США?",
    "options": ["Авраам Линкольн", "Джордж Вашингтон", "Франклин Рузвельт"],
    "correct_option_id": 1,
    "explanation": "Правильный ответ: Джордж Вашингтон."
  },
  {
    "question": "Какая страна первой отправила человека в космос?",
    "options": ["США", "Россия", "Китай"],
    "correct_option_id": 1,
    "explanation": "Правильный ответ: Россия."
  }
]

math.json

[
  {
    "question": "Сколько будет 2 + 2?",
    "options": ["3", "4", "5"],
    "correct_option_id": 1,
    "explanation": "Правильный ответ: 4."
  },
  {
    "question": "Сколько будет 5 * 5?",
    "options": ["10", "20", "25"],
    "correct_option_id": 2,
    "explanation": "Правильный ответ: 25."
  },
  {
    "question": "Сколько будет 10 / 2?",
    "options": ["4", "5", "6"],
    "correct_option_id": 1,
    "explanation": "Правильный ответ: 5."
  }
]

Внутри этих JSON-файлов находятся заголовок вопроса, варианты ответов, номер правильного ответа, а также пояснение, которое будет отправляться, если пользователь ответил неверно.

Дополнение

Недавно в Telegram добавили внутреннюю валюту Telegram Stars, а также предоставили обновление для API, с помощью которого можно добавить донат в вашем боте в Stars. Добавим команду donate внутрь файла index.js, при отправке которой бот будет присылать счёт для оплаты.

bot.onText(/\/donate/, (msg) => {
  const chatId = msg.chat.id;
 
  bot.sendInvoice(chatId,
    'Донат',
    'Донат на поддержку проекта',
    'unique_payload',
    '', // Пустой provider_token для Stars Payments
    'XTR', // Валюта "XTR"
    [{ label: 'Донат', amount: 1 }], // Сумма: 1 Stars
  );
});

Также добавим ещё одну команду — support. С её помощью большое количество пользователей вашего бота смогут написать обращение к вам, при этом у вас не будет множества лишних чатов с различными людьми! Пользователи смогут отправлять текст, фото и видео, а бот будет пересылать эти обращения администратору (в данном случае вам). Эту команду также расположите внутри файла index.js.

В начале файла добавьте:

const ADMIN_ID = 'ID';
let awaitingSupportMessage = {}; // Хранение информации о пользователях, ожидающих поддержки

Благодаря этому ID бот будет понимать, кому нужно переслать сообщение от пользователей. Чтобы узнать свой ID, можно воспользоваться ботом Get My ID, в котором достаточно отправить команду /start

Далее в конце файла добавьте:

bot.onText(/\/support/, (msg) => {
  const chatId = msg.chat.id;
  const userId = msg.from.id;


  // Сообщаем пользователю, что ожидаем его обращение
  bot.sendMessage(chatId, "Пожалуйста, отправьте ваше обращение в одном сообщении, включая текст, фото или видео!");


  // Отмечаем, что этот пользователь сейчас пишет обращение
  awaitingSupportMessage[userId] = true;
});


// Обработка всех сообщений
bot.on('message', (msg) => {
  const userId = msg.from.id;


  // Проверяем, что пользователь отправил сообщение после команды /support
  if (awaitingSupportMessage[userId]) {
    const chatId = msg.chat.id;
    const caption = msg.caption || ''; // Подпись, если есть


    // Проверка на тип сообщения и пересылка соответствующего контента администратору
    if (msg.text) {
      // Если сообщение содержит текст
      bot.sendMessage(ADMIN_ID, `Новое обращение от пользователя @${msg.from.username || msg.from.first_name} (ID: ${userId}):\n\n${msg.text}`);
    } else if (msg.photo) {
      // Если сообщение содержит фото
      const photo = msg.photo[msg.photo.length - 1].file_id; // Берём фото с максимальным разрешением
      bot.sendPhoto(ADMIN_ID, photo, { caption: `Новое обращение от @${msg.from.username || msg.from.first_name} (ID: ${userId})\n\n${caption}` });
    } else if (msg.video) {
      // Если сообщение содержит видео
      const video = msg.video.file_id;
      bot.sendVideo(ADMIN_ID, video, { caption: `Новое обращение от @${msg.from.username || msg.from.first_name} (ID: ${userId})\n\n${caption}` });
    } else {
      // Если тип сообщения не поддерживается
      bot.sendMessage(msg.chat.id, "К сожалению, данный тип сообщения не поддерживается.");
    }


    // Подтверждаем пользователю, что его обращение отправлено
    bot.sendMessage(chatId, "Ваше обращение отправлено. Администратор свяжется с вами в ближайшее время.");


    // Убираем пользователя из списка тех, кто пишет обращение
    delete awaitingSupportMessage[userId];
  }
});

Развертывание на сервере

Теперь, чтобы бот мог работать на постоянной основе, необходимо загрузить и запустить его на сервере. Для развёртывания я буду использовать сервис Timeweb Cloud.

Загрузка на GitHub

Перед запуском бота на сервере, необходимо загрузить файлы проекта на GitHub.

Вводите эти команды в консоль последовательно.

Добавляем все изменения в текущей директории к следующему коммиту:

git add .

Создаем коммит с сообщением «first commit», фиксируя все изменения, добавленные командой git add:

git commit -m "first commit"

Отправляем изменения на GitHub:

git push

Настройка сервера

Если вы ещё не являетесь клиентом Timeweb Cloud, предварительно нужно зарегистрироваться.

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

Image5

Далее, нам нужно создать облачный сервер. При создании сервера, в первом пункте перейдите во вкладку «Маркетплейс» и выберите Node.js, а версию Ubuntu поставьте самую новую. С помощью этого, когда ваш сервер начнёт работать, на нем сразу будет установлен Node.js и вам не придется устанавливать его вручную.

Screenshot 10 09 24 14:16:26

В регионе выберите тот, который ближе всего, где минимальный пинг.

Image2

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

Image10

В сети обязательно должен быть установлен публичный IP, а дополнительные услуги настраивайте по своему усмотрению.

Image3

В авторизации и Cloud-init ничего не меняйте. В информации о сервере введите имя сервера, его описание, а также выберите проект, который мы создали ранее.

Image7

Когда всё выбрали, нажимайте на кнопку «Заказать». Через некоторое время, сервер запустится и можно продолжить работу.

Запуск бота

После создания сервера, на вкладке «дашборд», скопируйте Root-пароль для входа и перейдите на вкладку «консоль». Введите логин пользователя «root» и нажмите «Enter». Далее, вставьте пароль, который вы скопировали и также нажмите «Enter». При вставке или написании пароля, он не будет виден! Если всё правильно указали, то вас поприветствуют.

Image1

Теперь введите команду, чтобы получить последние обновления:

sudo apt-get update

Первоначально создадим новую папку, куда и поместим нашего бота. Вводите эти команды последовательно:

cd /
sudo mkdir Bot
cd Bot

Вместо папки «Bot», вы можете создать папку с другим именем. 

Перенесем код с GitHub на наш сервер.

Чтобы удостовериться, что Git установлен на сервере (по умолчанию он всегда установлен) выполните:

git --version

Установим глобальные настройки Git, чтобы он был настроен на ваш профиль на GitHub:

git config --global user.name "ваш username на Github"
git config --global user.email "email, который указан при регистрации"

После этого, введите команду для клонирования репозитория, указав свою ссылку:

git clone https://github.com/Tulopex/School-Quiz

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

Для создания личного токена доступа на GitHub выполните следующие шаги: 

  1. Перейдите в настройки профиля: 

Нажмите на свою аватарку в правом верхнем углу и выберите «Settings»

  1. В левом меню выберите «Developer settings».

  2. Создание нового токена: 

    1. В разделе «Personal access tokens» выберите «Tokens (classic)».
    2. Нажмите кнопку «Generate new token». 
  3. Установите параметры токена: 

    1. В поле «Note» укажите описание для токена.
    2. Установите срок действия токена в поле «Expiration». 
    3. Выберите необходимые разрешения для токена в разделе «Select scopes». Например, для работы с репозиториями выберите repo
  4. Нажмите кнопку «Generate token». 

  5. Скопируйте сгенерированный токен и сохраните его в безопасном месте. Учтите, что вы не сможете увидеть токен повторно после закрытия страницы.

Перейдите в вашу папку с проектом с помощью  команды:

cd School-Quiz 

Вместо School-Quiz укажите название своего проекта. Чтобы установить пакеты вашего проекта, выполните команду:

npm install

После установки пакетов, можно запустить проект, с помощью:

npm start 

В консоли появится сообщение «Бот запущен». Но есть одна проблема, если вы перезапустите сервер или закроете консоль, то бот перестает работать! Чтобы бот работал постоянно, а также автоматически запускался после перезагрузки, достаточно установить менеджера процессов pm2.

Установите pm2 командой:

sudo npm install pm2 -g

Затем запустите сервер Node.js, используя pm2:

sudo pm2 start index.js --name "bot-quiz" --watch

В примере используется имя «bot-quiz». Так будет называться запущенный процесс. Вы можете придумать любое другое имя.

Настройте автозапуск при запуске или перезагрузке сервера:

sudo pm2 startup

Сохраните все введенные изменения:

sudo pm2 save

Image4

Заключение

В данной статье мы подробно рассмотрели процесс создания Telegram-бота на платформе Node.js, начиная с регистрации бота через BotFather и заканчивая развертыванием готового решения на сервере.

Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
09 октября 2024 г.
92
15 минут чтения
Средний рейтинг статьи: 5
Пока нет комментариев