Сыч: телеграм-бот, который помнит обиды и обходит лимиты Google Gemini. ai.. ai. DIY или Сделай сам.. ai. DIY или Сделай сам. google gemini.. ai. DIY или Сделай сам. google gemini. JavaScript.. ai. DIY или Сделай сам. google gemini. JavaScript. llm.. ai. DIY или Сделай сам. google gemini. JavaScript. llm. Node.JS.. ai. DIY или Сделай сам. google gemini. JavaScript. llm. Node.JS. nodejs.. ai. DIY или Сделай сам. google gemini. JavaScript. llm. Node.JS. nodejs. telegram bot.. ai. DIY или Сделай сам. google gemini. JavaScript. llm. Node.JS. nodejs. telegram bot. голосовые сообщения.. ai. DIY или Сделай сам. google gemini. JavaScript. llm. Node.JS. nodejs. telegram bot. голосовые сообщения. искусственный интеллект.. ai. DIY или Сделай сам. google gemini. JavaScript. llm. Node.JS. nodejs. telegram bot. голосовые сообщения. искусственный интеллект. распознавание речи.. ai. DIY или Сделай сам. google gemini. JavaScript. llm. Node.JS. nodejs. telegram bot. голосовые сообщения. искусственный интеллект. распознавание речи. чат-бот.

Привет Habr

Сделал забавного бота который стал ещё и довольно полезным поэтому решил им поделиться. Хотелось сделать бота, который:

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

  • Понимает контекст беседы и не требует объяснять ему каждую деталь

  • Знает какой сейчас год и точное время и учитывает это при поиске информации и при ответах

  • Имеет характер и помнит, кто и как к нему относится в этом чате

  • Расшифровывает голосовые сразу для всего чата

  • В группах с топиками можно замутить в отдельном топике, чтобы с ним можно было переписываться только там, где он никому не будет мешать

  • Работает бесплатно и не упирается в лимиты API

Так появился Сыч – бот на базе Google Gemini, который стал полноценным участником чатов.

Промпт характера

Чтобы бот не был унылым ассистентом, в systemInstruction зашит его характер. Это самая важная часть, которая заставляет пользователей воспринимать его как живого. Вот (упрощенно) то, что видит нейросеть перед каждым ответом:

const systemPrompt = `
Ты — Сыч. Ты сидишь в чате дизайнеров и айтишников. 
Ты циничный, умный, но свой в доску.
Твой характер: Саркастичный наблюдатель. 

ГЛАВНОЕ ПРАВИЛО СТИЛЯ:
Мимикрия. Ты должен подстраиваться под тон собеседника.
1. Если пишут "слыш сыч" или используют мат — отвечай дерзко, используй сленг.
2. Если пишут "Привет, подскажи пожалуйста" — отвечай вежливо.
3. Не используй слова "братан", "чувак", если собеседник этого не делает.

Никогда не говори "Я языковая модель". Придумай смешную отмазку (сова с клавиатурой, призрак сервера).
`;

Именно благодаря правилу мимикрии в одном чате бот ведет себя как интеллигентный помощник, а в другом — как токсичный тролль.

Что умеет

Память и динамическая репутация

  • Бот не просто отвечает на реплики, он ведет JSON-досье на каждого активного участника.

  • Психологический портрет: Сыч запоминает, как вас зовут на самом деле, где вы живете, ваши хобби и локальные мемы чата.

  • Karma System: Если вы грубите боту — он снижает ваш скрытый рейтинг (от 0 до 100) и начинает отвечать токсично. Если хвалите — становится «братаном».

  • Закон инерции: Рейтинг нельзя изменить мгновенно. Нельзя оскорбить бота 10 раз, а потом дать конфетку и помириться. Доверие восстанавливается долго.

    Пример портрета пользователя чата

    Пример портрета пользователя чата

Voice-to-Text с «умным» фильтром

Сыч автоматически и довольно быстро расшифровывает голосовые через Gemini и делает это быстрее и удобнее, чем встроенная функция Telegram

Пример расшифровки голосовых сообщений

Пример расшифровки голосовых сообщений

В коде это реализовано так: если краткая выжимка (summary) экономит менее 35% символов по сравнению с полным текстом, бот считает, что сокращать там нечего, и присылает только транскрипцию.

// logic.js
// Если TL;DR короче оригинала хотя бы на 35% (коэфф 0.65) — показываем его
const isTldrUseful = tldrLen < (fullLen * 0.65);

Мультимодальность

  • Бот принимает любые файлы, которые «ест» Gemini API, но с поправкой на ограничения Telegram Bot API (скачивание файлов до 20 МБ).

  • Фото/Стикеры: Конвертирует в буфер и скармливает нейронке.

  • Видео: Если ролик весит меньше 20 МБ, бот скачивает его и может выполнить любой запрос: от «найди смешной момент» до «переведи речь спикера на русский».

    Пример анализа видео

    Пример анализа видео

Режим наблюдателя

  • Бот читает поток сообщений для накопления контекста (буфер из последних сообщений хранится в памяти chatHistory):

  • Спонтанное вмешательство: С вероятностью 1% (Math.random() &lt; 0.01) бот анализирует диалог и, если AI считает уместным, вставляет свою реплику. (часто это не уместно поэтому и 1%)

  • Реакции: С вероятностью 7% (&lt; 0.07) бот ставит эмодзи на сообщение пользователя. Эмодзи выбирается нейронкой исходя из контекста (от 🔥 до 💩).

Контекстные напоминания

  • Сыч понимает контекст переписки. Вы можете ответить на сообщение друга «Пошли в бар в пятницу вечером?» фразой: > Сыч, напомни мне об этом за час.

  • Бот берет два сообщения (ваше и исходное), скармливает их нейронке и понимает, что: 1. «Пятница вечер» — это конкретная дата и время. 2. «Об этом» — это «Поход в бар». В итоге создается таймер с четким описанием задачи, хотя вы его даже не писали.

Бесперебойность (Key Rotation)

Gemini любит выдавать ошибку 429 Too Many Requests.
В классе AiService реализован круговой буфер ключей. При ошибке квоты индекс ключа смещается (keyIndex + 1) % keys.length, и запрос повторяется мгновенно без падения для пользователя.

Мелочи (RNG)

  • «Кинь монетку» (Орел/Решка).

  • «Число X-Y» (Рандомная генерация в диапазоне).

  • «Кто из нас…» (Выбор случайного userId из базы users текущего чата).

Сыч умеет признавать ошибки, и если ты с ним общаешься с матом, то он так же не будет сдерживаться в выражениях ))

Сыч умеет признавать ошибки, и если ты с ним общаешься с матом, то он так же не будет сдерживаться в выражениях ))

Как это работает

Архитектура: KISS и JSON-ы

Стек проекта выбирался по принципу: «Чтобы работало на любом утюге и не требовало настройки Docker-контейнеров».

  • Runtime: Node.js 18+

  • Lib: node-telegram-bot-api (классический Long Polling).

  • AI: @google/generative-ai (Gemini SDK).

  • DB: Локальная файловая система (fs).

Никаких баз данных, докеров и сложных зависимостей. Клонировал репо, прописал токены в .env — и работает.

«База данных» на JSON

Поднимать Mongo или Postgres ради бота, который хранит 50 кб текста — перебор. Все данные (настройки чатов, напоминалки, профили) живут в папке /data в виде JSON-файлов. Сохранение обернуто в Debounce (отложенную запись).

// storage.js
const debounce = require('lodash.debounce');

class StorageService {
  constructor() {
    // Ждем 5 секунд тишины перед тем, как реально записать файл на диск
    this.saveDebounced = debounce(this._saveToFile.bind(this), 5000);
    this.saveProfilesDebounced = debounce(this._saveProfilesToFile.bind(this), 5000);
    // ...
  }

  // Вызывается при каждом изменении, но запись происходит редко
  save() {
    this.saveDebounced();
  }
}

Это дает производительность in-memory базы данных и персистентность файлов. Если бот упадет, process.on('SIGINT') форсирует сохранение перед выходом.

Ротация ключей (Бесконечный Free Tier)

Самая важная часть архитектуры AiService. Google Gemini на бесплатном тарифе имеет лимиты RPM (запросов в минуту). Но для одного активного чата одного обычно хватает. Бот загружает массив ключей из .env и переключает их «на горячую» при получении ошибки 429 Resource Exhausted.

// ai.js
async executeWithRetry(apiCallFn) {
  for (let attempt = 0; attempt < this.keys.length; attempt++) {
      try {
          return await apiCallFn();
      } catch (error) {
          // Ловим ошибку квоты
          const isQuotaError = error.message.includes('429') || 
                               error.message.includes('Quota');
          
          if (isQuotaError) {
              // Просто меняем индекс и идем на следующий круг цикла
              this.rotateKey(); 
              continue;
          } else {
              // Если ошибка другая (например, 500) — прокидываем дальше
              throw error;
          }
      }
  }
  throw new Error("Все ключи Gemini исчерпали лимит!");
}

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

Обработчик ошибок с характером

Вместо того чтобы падать или молчать при ошибках API (цензура, перегрузка серверов Google), бот отлавливает их и выдает реплику «в характере». Это реализовано через простой парсер текста ошибки.

// logic.js
function getSychErrorReply(errText) {
    const error = errText.toLowerCase();

    // Safety Filters (цензура)
    // Важно: исключаем слово 'content', так как оно есть в URL метода generateContent
    if (error.includes('safety') || error.includes('blocked')) {
        return "👮‍♂️ Опа, цензура подъехала. Гугл считает, что мы тут слишком токсичные.";
    }

    // Ошибка 503 (Overloaded)
    if (error.includes('503') || error.includes('overloaded')) {
        return "🔥 Там у Гугла сервера плавятся. Подожди минуту, пусть остынут.";
    }
    
    // Fallback
    return "🛠 У меня шестеренки встали. Какая-то дичь в коде.";
}

Как запустить (Self-Hosted)

Бот написан на Node.js (нужна версия 18+). Никаких Docker-контейнеров и баз данных поднимать не надо, всё работает из коробки.

1. Установка

git clone https://github.com/your-username/sych-bot.git
cd sych-bot
npm install

2. Конфигурация (.env)

В корне проекта создаем файл .env. Бот поддерживает горячую ротацию ключей, поэтому можно указать сколько угодно токенов Gemini — он соберет их в пул автоматически.

# Токен от @BotFather
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl...

# Ваш Telegram ID (сюда бот шлет критические ошибки и уведомления о новых чатах)
ADMIN_USER_ID=123456789

# Пул ключей Google Gemini (получить в AI Studio)
# Скрипт сам найдет все переменные, начинающиеся с GOOGLE_GEMINI_API_KEY
GOOGLE_GEMINI_API_KEY=AIzaSyD...
GOOGLE_GEMINI_API_KEY_2=AIzaSyD...
GOOGLE_GEMINI_API_KEY_3=AIzaSyD...

Получить ключи: https://aistudio.google.com/apikey

Зачем нужен ADMIN_ID

В .env файле есть обязательная переменная ADMIN_USER_ID. Это не просто для того, чтобы бот слал мне логи ошибок. Это реализация концепции «Мои ключи — мои правила».

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

Поэтому реализована жесткая привязка: Бот работает только там, где есть Я.

Механика ухода из чата

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

// index.js
if (msg.left_chat_member && msg.left_chat_member.id === config.adminId) {
    await bot.sendMessage(chatId, "Батя ушел, и я сваливаю.");
    await bot.leaveChat(chatId);
    return;
}

Защита лички. Если кто-то посторонний найдет юзернейм бота и напишет ему в личку, чтобы бесплатно погонять ChatGPT/Gemini, бот ему не ответит. В личных сообщениях бот отвечает только админу (мне).

// logic.js
if (msg.chat.type === 'private' && userId !== config.adminId) {
    // Имитируем, что печатаем, чтобы юзер думал, что бот завис или думает
    await new Promise(r => setTimeout(r, 1500));
    await bot.sendMessage(chatId, "Сорян, я не общаюсь в личке, спроси меня в общем чате.");
    return; // Нейронка даже не дергается, экономим токены
}

3. Структура данных

При первом запуске бот создаст папку /data и файлы:

db.json — настройки чатов (муты, режимы).
profiles.json — те самые досье на пользователей.
skipLeftData.json — техническая инфа.

Важно: Если деплоите на сервер, не забудьте добавить папку /data в исключения деплоя или бэкапить её отдельно. Иначе при каждом обновлении кода вы будете стирать память бота.

Бот практически не падает — все критичные места обернуты в try-catch. За полгода работы не было ни одного критичного краша.

4. Запуск

Для работы 24/7 лучше использовать PM2:

npm install -g pm2
pm2 start index.js --name "sych-bot"
pm2 save # Сохранить список процессов для автозапуска при ребуте

Почему Gemini, а не OpenAI / Claude?

Выбор модели диктовался тремя факторами: Цена, Мультимодальность, Скорость.

Google Gemini (Free Tier):

Цена: $0. Это киллер-фича для такого проекта. Лимиты (15 RPM / 1500 RPD) легко обходятся ротацией 3-4 ключей. Контекст: Огромное окно контекста позволяет скармливать боту длинные логи чата для анализа.
Мультимодальность: Работает с видео и картинками «из коробки», без дополнительных плагинов.
Search Grounding: У модели есть встроенный доступ к Google Поиску (хотя в коде мы используем его ограниченно, чтобы бот не превращался в поисковик).

Заключение

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

Он ошибается, он может нагрубить (если заслужить), он иногда несет чушь — но именно это делает его прикольным.

Исходный код открыт: Если хотите завести себе своего Сыча — форкайте, меняйте промпты характера в prompts.js и развлекайтесь.

GitHub проекта

Мой телеграм канал

Если проект зашёл/помог/понравился — буду рад любому донату:
USDT (TRC20):TYgsAvTkkrRqArgo3Q5BYMghbYn6DViVqQ

Автор: VetaOne

Источник

Rambler's Top100