Есть Telegram-каналы, которые отслеживают определенные события в мире. А мы следим за их сообщениями, чтобы не упустить ничего важного. Если собирать и анализировать данные вручную, процесс будет отнимать много времени и требовать высокой концентрации от человека. Чтобы решить эту проблему, наш партнер и фронтенд-разработчик Владилен Минин создал Telegram-бота, который автоматизирует процесс и выдает резюме по собранной информации.

В этой статье рассказываем, как с помощью нейросети создать бота, который собирает и обрабатывает информацию из сообщений, и задеплоить его на облачный сервер.

Возможно, эти тексты тоже вас заинтересуют:

Введение
Авторизация
Реализация функционала
Пропускаем сообщения через нейросеть
Деплой

Введение


В качестве примера рассмотрим бота, который отслеживает перемещение криптовалют:



/sum — команда обращения к боту, whale_alert_io — ID канала (можно подставить любой), остальной текст — произвольная команда для робота.



Авторизация


Первым делом нужно авторизовать аккаунт, чтобы получить доступ к сообщениям бота. Через бота такой функционал недоступен, поэтому будем использовать библиотеку GramJS.

Заходим на my.telegram.org, открываем API development tools и создаем приложение.


В ответе получаем параметры apiId и apiHash. Подставляем данные в код авторизации:

const { TelegramClient } = require('telegram')
const input = require('input')
const { StringSession } = require('telegram/sessions')
const session = new StringSession('')
const apiId = 123 // подставляем данные из Telegram
const apiHash = 'abc' // подставляем данные из Telegram
;(async () => {
  console.log('Loading interactive example...')
  const client = new TelegramClient(session, apiId, apiHash, {
    connectionRetries: 5,
  })
  await client.start({
    phoneNumber: async () => await input.text('Please enter your number: '),
    password: async () => await input.text('Please enter your password: '),
    phoneCode: async () =>
      await input.text('Please enter the code you received: '),
    onError: (err) => console.log(err),
  })
  console.log('You should now be connected.')
  console.log(client.session.save()) // в консоли получим строчку, которую нужно будет сохранить
})()

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



Запускаем на Node.js функцию node auth, чтобы получить ключ авторизации и продолжить работу с запросами к Telegram.



Любите детективы? Пройдите квест «В поисках пропавших ссылок»! Регистрируйтесь на сайте и попробуйте себя в роли сыщика: найдите на страницах Selectel спрятанные ссылки и первыми дойдите до финала. Выиграйте эксклюзивный мерч и промокод на сервисы Selectel.


Реализация функционала


Добавим в Telegram-бот функцию поиска непрочитанных сообщений. Для этого инициализируем client с уже сохраненной сессей. После — используем метод getUnreadMessages:

const { TelegramClient } = require('telegram')
const { NewMessage } = require('telegram/events')
const { session, apiId, apiHash} = require('./config')
const client = new TelegramClient(session, apiId, apiHash, {})
async function getUnreadMessages(channelId, limit = 10) {
  const dialogs = await client.getDialogs({}) // получаем все чаты
  const channel = dialogs.find((d) => d.entity.username === channelId) // находим нужный по ID
  if (channel) { // если канал найдет
    const messages = await client.getMessages(channel.entity, { // получаем список сообщений
      // limit: channel.unreadCount, // можем прочесть количество непрочитанных сообщений
      limit, // сколько сообщений получаем
    })
          console.log(messages.map((m) => m.message).join(' ')) // выводим в консоль
  } else {
    console.log('Канал не найден')
  }
}
;(async function run() {
  // const channel = 'whale_alert_io'
  // watchNewMessages(channel) // важно, чтоб метод был до client.connect
  await client.connect()
  await getUnreadMessages(channelId, 10)
})()

Чтобы бот анализировал не только непрочитанные, но и новые сообщения в чатах, можно использовать метод watchNewMessages:

function watchNewMessages(channelId) {
  client.addEventHandler((event) => {
    console.log('new message', event.message.message)
  }, new NewMessage({ fromUsers: [channelId] }))
}

Пропускаем сообщения через нейросеть


Мы будем использовать GPT-модель GigaChat, которая предоставляет бесплатно до 1 млн токенов. Чтобы начать работу с API, нужно узнать значения ClientID и ClientSecret.

Для этого регистрируемся на сайте GigaChat API и создаем проект. Значение Client ID просто копируем из соответствующего поля в панели справа.


Поле Client ID. Источник.

И нажимаем Получить Client Secret, чтобы сгенерировать новый секрет.


Окно с Client Secret. Источник.

Алгоритм работы модели простой. Сначала отправляем запрос на получение токена. Затем делаем запрос к чату с имеющимся токенов. Ниже — пример, как реализован функционал в нашем боте:

const { gigaAuth } = require('./config')
const { v4: uuidv4 } = require('uuid')
const axios = require('axios')
const qs = require('qs')

// метод для получение токена
async function getToken() {
// конфиг запроса
  const config = {
    method: 'post',
    maxBodyLength: Infinity,
    url: '<https://ngw.devices.sberbank.ru:9443/api/v2/oauth>',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Accept: 'application/json',
      RqUID: uuidv4(),
      Authorization: `Basic ${gigaAuth}`,
    },
    data: qs.stringify({
      scope: 'GIGACHAT_API_PERS'
    }),
  }

  try {
    const response = await axios(config)
    const { access_token: accessToken, expires_at: expiresAt } = response.data

    return { accessToken, expiresAt }
  } catch (error) {
    console.log(error)
  }
}

// выполняем запрос к модели уже с токеном
async function giga(content = '', system = '') {
  if (!content) return

  // получаем токен
  const token = await getToken()

  const messages = []

	// если передавали контекст, то добавляем его как системное сообщение.
  // здесь мы говорим как чату себя вести
  if (system) {
    messages.push({ role: 'system', content: system })
  }

	// формируем данные для обращения
  const data = JSON.stringify({
    model: 'GigaChat',
    messages: messages.concat([
      {
        role: 'user',
        content,
      },
    ]),
    temperature: 1,
    top_p: 0.1,
    n: 1,
    stream: false,
    max_tokens: 512,
    repetition_penalty: 1,
    update_interval: 0,
  })

	// настраиваем запрос
  const config = {
    method: 'post',
    maxBodyLength: Infinity,
    url: '<https://gigachat.devices.sberbank.ru/api/v1/chat/completions>',
    headers: {
      'Content-Type': 'application/json',
      Accept: 'application/json',
      Authorization: `Bearer ${token.accessToken}`,
    },
    data,
  }

	// выполняем запрос возвращая ответ самого чата
  try {
    const response = await axios(config)
    const message = response.data.choices[0].message
    return message.content
  } catch (e) {
    console.log(e)
  }
}

module.exports = { giga }

Устанавливаем Telegraf, чтобы добавить интерфейс программы в Telegram-бот:

npm i telegraf

Далее получаем токен для @BotFather и создаем Telegram-бот. Вот пример финального файла функционала:

const { TelegramClient } = require('telegram')
const { Telegraf } = require('telegraf')
const { NewMessage } = require('telegram/events')
const { session, apiId, apiHash, botToken } = require('./config')
const { giga } = require('./giga')

<h3> создаем бота</h3>
const bot = new Telegraf(botToken)
const client = new TelegramClient(session, apiId, apiHash)
async function getUnreadMessages(channelId, limit = 10) {
  const dialogs = await client.getDialogs({})
  const channel = dialogs.find((d) => d.entity.username === channelId)
  if (channel) {
    const messages = await client.getMessages(channel.entity, {
      limit,
    })
    return messages.map((m) => m.message).join(' ')
  } else {
    console.log('Канал не найден')
  }
}
;(async function run() {
  await client.connect()
        // слушаем команду sum
  bot.command('sum', async (ctx) => {
                // получаем как параметры ID-канала и сообщение для GPT
    const [, channelId, ...task] = ctx.message.text.split(' ')
    if (!channelId) {
      return ctx.reply(`Вы не указали канал`)
    }
                // получаем строку из сообщений в канале
    const messagesString = await getUnreadMessages(channelId, 10)
                // передаем роль и сообщения из канала в GigaChat
    const gigaResponse = await giga(messagesString, task.join(' '))
                // отправляем пользователю ответ от GigaChat с анализом
    await ctx.reply(gigaResponse)
  })
  bot.launch()
})()

Деплой


Чтобы Telegram-бот был всегда под рукой, задеплоим его в облако Selectel. Для этого переносим шаблон кода в Git:

git init
git add .
git commit -m “init”
git push -u origin master

Далее заходим в панель управления Selectel. Переходим из Облачной платформы в Серверы и нажимаем Создать сервер.


Далее настраиваем параметры сервера.

  • Источник: Ubuntu 22.04 LTS 64-bit;
  • Конфигурация: фиксированная, Shared;
  • Доля vCPU: 20% (4 vCPU, 8 ГБ RAM).


После инициализации сервера нужно его настроить. Устанавливаем Git и загружаем репозиторий.

apt update
apt install git
git clone REPO_NAME

После устанавливаем Node.js и NPM через NVM. Подробнее об этом рассказывали в Академии.

Чтобы запустить программу, устанавливаем Node.js на Ubuntu 20.04:

nvm install 20
nvm use 20
cd REPO_NAME
npm install

Далее устанавливаем PM2 для работы бота в фоновом режиме:

npm install pm2 -g
pm2 start main

Готово. Теперь бот работает и вы можете анализировать Telegram-каналы и переписки!



Больше контента от Владилена Минина вы найдете на его YouTube-канале.

Комментарии (3)


  1. BeerMaster
    26.03.2024 11:09

    Все было хорошо и понятно зачем, пока не появилась нейросеть. Что она делает с с собщениями?


    1. Dominux
      26.03.2024 11:09

      Автор знает только js, до внутренностей LLM ему как до Китая


    1. Sagittarius67
      26.03.2024 11:09

      Как я понял, GigaChat анализирует сообщения в канале, ID которого передаётся в запросе, и выдаёт ответ. В ютуб канале этого чела есть ролик по теме - там поподробнее рассказывется.