npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

max-account-api

v0.1.0

Published

Unofficial Node.js client for personal MAX (web.max.ru) accounts. Reverse-engineered WebSocket + mobile binary protocol. Phone- or QR-login, send/receive messages, manage chats — like node-telegram-bot-api but for your own MAX account.

Downloads

129

Readme

max-account-api

⚠️ Неофициальная библиотека. Не аффилирована с MAX / VK. Используйте на свой страх и риск — ваш аккаунт может быть помечен или заблокирован за нестандартную активность.

Node.js-клиент для личного MAX-аккаунта — залогиньтесь по номеру телефона или QR-коду и автоматизируйте всё что делает обычный пользователь: получать сообщения, отвечать, читать, реагировать, редактировать, отправлять фото / файлы / стикеры / опросы, создавать группы, и т.д.

Сложные части (TLS + msgpack + LZ4 бинарный протокол для phone-auth, WebSocket для всего остального, корреляция фреймов, ping / heartbeat, авто-реконнект, ack серверных push-ев) спрятаны за маленьким event-driven API.

import { MaxClient } from 'max-account-api';

const client = await MaxClient.loginWithPhone({
  phone: '+79991234567',
  getSmsCode: async () => prompt('SMS code: '),
});

client.on('message', (m) => console.log(`[${m.chatId}] ${m.fromId}: ${m.text}`));
await client.sendMessage(0, 'Привет в Избранное!');

Возможности

| | | |---|---| | 📱 Phone-auth + auto QR-bind | Один метод loginWithPhone({phone, getSmsCode, getPassword?}) — SMS-код, опционально облачный пароль (2FA), под капотом mobile binary protocol + WS QR-bind. Web-токен сохраняется автоматически. | | 📷 QR-логин (как раньше) | Терминал-QR + событие со ссылкой. Старый flow new MaxClient().start() тоже работает. | | 💾 Гибкая сессия | По умолчанию — ./.max-session.json. Можно отдать кастомный SessionStore или инжектить deviceId+loginToken из env. | | 🧩 Несколько инстансов в процессе | Каждый со своим store / тoken pair'ом. | | 📨 Сообщения | Текст, реплаи, форварды, цитаты, редактирование, удаление, реакции, голосование в опросах. | | 📎 Вложения | Фото, файлы, видео, стикеры, контакты, опросы — с end-to-end upload. | | 👥 Чаты / группы | Создание групп / каналов, добавление / удаление / промоут участников, переименование, аватары, инвайт-линки, mute / unmute, pin, политика реакций. | | 📞 Звонки | Signalling для 1:1 audio / video (медиа через отдельный videowebrtc.okcdn.ru). | | 🔁 Auto-reconnect | Тихий повторный handshake при обрыве. | | 📡 Server push | Подтверждается автоматически — вы получаете 'message', 'chatUpdate', 'presence', 'readByOther', и т.д. | | 🔐 Управление сессией | Список активных сессий, logout, 2FA включить / выключить, смена пароля. | | 📚 Полная TypeScript-типизация | На каждый запрос и ответ. 160+ опкодов с каноническими именами, извлечёнными из декомпилированного MAX-клиента. | | 🪟 Запасной выход | client.raw(opcode, payload) для любого опкода которого нет в high-level API. |

Установка

npm install max-account-api

Требуется Node.js 18+.

Быстрый старт

Вход по номеру телефона (рекомендуется)

import { MaxClient } from 'max-account-api';
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';

const rl = readline.createInterface({ input, output });

const client = await MaxClient.loginWithPhone({
  phone: await rl.question('Phone (+79991234567): '),
  getSmsCode: () => rl.question('SMS code: '),
  // Опционально: вызовется только если у аккаунта 2FA облачный пароль
  getPassword: (challenge) =>
    rl.question(`2FA password (hint: ${challenge.hint}): `),
  sessionFile: './.max-session.json',
});

console.log('Logged in as', client.getMe()?.names?.[0]?.name);

client.on('message', async (m) => {
  if (m.fromId !== client.getMe()?.id) {
    await client.reply(m, `Эхо: ${m.text}`);
  }
});

rl.close();
// keep process alive
await new Promise(() => {});

На первом запуске MAX пришлёт SMS, после ввода кода + (если есть) 2FA пароля библиотека сохранит оба токена (mobile + web) в session-файл. На последующих запусках — тихий reconnect, ничего вводить не надо.

Регион: SMS-доставка и phone-auth заблокированы серверной геолокацией. Запускайте из РФ или через VPN с RU-IP, иначе получите service.unavailable / phone.region.unsupported.

Вход по QR-коду (без номера)

const client = new MaxClient();
client.on('qr', ({ link, expiresAt }) => {
  console.log('Откройте/отсканируйте:', link);
  console.log('Истекает в', new Date(expiresAt).toLocaleString());
});
await client.start();

QR печатается в терминал автоматически. После сканирования мобильным MAX в консоль выводятся deviceId + loginToken — сохраните их в env.

Переиспользование токена (silent login)

const client = new MaxClient({
  deviceId:   process.env.MAX_DEVICE_ID,
  loginToken: process.env.MAX_LOGIN_TOKEN,
});
await client.start();

Если оба значения корректны — никакого QR / SMS не будет.

Несколько инстансов в одном процессе

const a = new MaxClient({ deviceId: process.env.A_ID, loginToken: process.env.A_TOK });
const b = new MaxClient({ deviceId: process.env.B_ID, loginToken: process.env.B_TOK });
await Promise.all([a.start(), b.start()]);

API по разделам

Полный справочник: docs/API.md. Основные группы:

Сообщения

await client.sendMessage(chatId, 'привет');
await client.sendReply(chatId, replyToMessageId, 'ответ на сообщение');
await client.reply(incoming, 'эхо');                   // ответить на полученное
await client.editMessage(chatId, msgId, 'новый текст');
await client.deleteMessages(chatId, [msgId1, msgId2], /*forAll*/ true);
await client.forwardMessage(toChatId, fromChatId, msgId, 'опциональный комментарий');
await client.sendTyping(chatId);
await client.readMessage(chatId, msgId);
await client.getHistory(chatId, { backward: 50 });
await client.searchMessages(chatId, 'query');

Реакции

await client.setReaction(chatId, msgId, '👍');
await client.removeReaction(chatId, msgId);
const reactions = await client.getReactions(chatId, [msgId1, msgId2]);

Вложения

const photoToken = await client.uploadPhotoBytes({ data: pngBytes, filename: 'pic.png' });
await client.sendPhoto(chatId, photoToken, 'подпись');

const file = await client.uploadFileBytes({ data: pdfBytes, filename: 'doc.pdf' });
await client.sendFile(chatId, file.fileId, 'договор');

await client.sendVoice(chatId, { data: oggBytes, filename: 'voice.ogg' }, { duration: 12 });
await client.sendVideo(chatId, { data: mp4Bytes, filename: 'clip.mp4' }, 'смотри ролик');
await client.sendVideoNote(chatId, { data: mp4Bytes });  // круглый кружок

await client.sendSticker(chatId, stickerId);
await client.sendContact(chatId, contactId);
await client.sendLocation(chatId, 55.7558, 37.6173, { name: 'Кремль' });
await client.sendPoll(chatId, 'Куда едем?', ['Сочи', 'Питер', 'Казань']);

sendVoice / sendVideo / sendVideoNote требуют mobileLoginToken (т.е. сессию, заведённую через MaxClient.loginWithPhone()); через QR-only login они недоступны.

Группы / каналы

const group = await client.createGroup('Тестовая группа', [userId1, userId2]);
await client.addGroupParticipants(group.chatId, [userId3]);
await client.setGroupAdmin(group.chatId, userId1);
await client.renameGroup(group.chatId, 'Новое имя');
await client.setChatAvatar(group.chatId, photoToken);
await client.pinMessage(group.chatId, msgId);
await client.leaveChat(group.chatId);
await client.resolveChatLink('https://max.ru/somegroup');  // op 57 — фактически вступает в чат

Чаты

const chats = client.getChats();                       // снимок при логине
const fresh = await client.getChatsByIds([id1, id2]);
await client.muteChat(chatId, /*muteUntilMs*/ -1);
await client.unmuteChat(chatId);
await client.clearChatHistory(chatId);                 // только история
await client.deleteChat(chatId);                       // полностью удалить
await client.subscribeChat(chatId, true);

Контакты

const me = client.getMe();
const c = await client.getContactInfo(userId);
await client.addContactByPhone('+79991234567', 'Имя', 'Фамилия');
await client.lookupContactByPhone('+79991234567');     // без сохранения
await client.renameContact(userId, 'Новое имя');
await client.blockContact(userId);
await client.unblockContact(userId);
await client.removeContact(userId);
const blocked = await client.listBlockedContacts();
const presence = await client.getPresence([userId]);

Профиль

await client.setProfileName('Имя', 'Фамилия');
await client.setProfileDescription('обо мне');
await client.setProfileAvatar(photoToken);
await client.updateProfile({ firstName: 'Имя', description: 'обо мне' });

Сессии / безопасность

const sessions = await client.listSessions();
await client.logout();                                 // op 20 + локально стереть токен
await client.enable2faPassword({
  password: 'NewSecurePass123',
  hint: 'мамин день рождения',
  recoveryEmail: '[email protected]',
  getCode: async ({ codeLength }) => prompt(`Code from email (${codeLength}): `),
});
await client.disable2faPassword(currentPassword);

Папки

const folders = await client.getFolders(['folder-id-1']);
await client.upsertFolder({ title: 'Боты', filters: [10], /* ... */ });
await client.reorderFolders(['id1', 'id2', 'id3']);

Поиск

const result = await client.globalSearch('текст');
const channels = await client.globalSearchByType('канал', 'CHANNELS');
const inChat = await client.searchMessages(chatId, 'слово');

Запасной выход

// Любой опкод которого нет в high-level API:
const res = await client.raw<MyResponse>(Opcode.SOME_OP, { /* payload */ });

События

client.on('qr',                ({ link, expiresAt }) => …);  // QR готов
client.on('login',             (me) => …);                    // QR подтверждён, профиль есть
client.on('ready',             () => …);                      // полная инициализация
client.on('message',           (m) => …);                     // входящее сообщение
client.on('chatUpdate',        (chat) => …);                  // chat info изменился
client.on('readByOther',       (mark) => …);                  // кто-то прочитал твоё сообщение
client.on('presence',          (p) => …);                     // online/offline у контакта
client.on('contactUpdate',     (contact) => …);
client.on('memberLeave',       (notif) => …);
client.on('stickerRecentsUpdate', (push) => …);
client.on('fileUploadDone',    (push) => …);
client.on('configChanged',     (hash) => …);
client.on('raw',               (frame) => …);                 // любой WS-фрейм (отладка)
client.on('reconnect',         (attempt) => …);
client.on('close',             (code, reason) => …);
client.on('error',             (err) => …);

Документация

| Файл | Что внутри | |---|---| | docs/API.md | Полный справочник по MaxClient (каждый метод + типы аргументов / возвращаемых значений) | | docs/ROADMAP.md | Что реализовано, известные ограничения |

Примеры

В examples/ — десяток мини-сценариев (echo-бот, отправка фото / файла / голоса / видео-кружка / локации, реакции, опросы, группы, поиск, контакты, …):

npm run example:phone        # tsx examples/phone-bot.ts  — phone-login + echo
npm run example:echo         # tsx examples/echo-bot.ts   — QR-логин + echo
npx tsx examples/send-photo.ts ./pic.png 0
npx tsx examples/send-voice.ts ./voice.ogg 12 0

Подробнее — examples/README.md.

Безопасность сессии

Файл .max-session*.json содержит LOGIN-токен — это эквивалент пароля от вашего MAX-аккаунта. Никогда не коммитьте, не передавайте в логи / Slack / GitHub Issues. В .gitignore уже добавлено правило .max-session*.json.

Если токен скомпрометирован — await client.logout() или войдите в mobile MAX → Настройки → Устройства → завершите сессию.

Для multi-tenant / production хранилищ реализуйте свой SessionStore:

import type { SessionStore, StoredSession } from 'max-account-api';

class VaultSessionStore implements SessionStore {
  async load(): Promise<StoredSession> { /* read from secrets vault */ }
  async save(s: StoredSession): Promise<void> { /* write back */ }
}

const client = new MaxClient({ session: new VaultSessionStore() });

Что не покрыто / ограничения

  • Медиа звонков (videowebrtc.okcdn.ru/ws2) — есть только signalling (startCall), сами RTP / WebRTC потоки вне scope библиотеки.
  • Live streams — есть chatLivestreamInfo (op 62), но запуск стрима не реализован.
  • Mini-apps / WebView — есть openBotMiniApp (op 160), сам WebApp / WebView пользователь рендерит сам.
  • SMS phone-auth — заблокирован сервером для не-RU IP, нужен RU-IP или VPN.

Полная таблица — docs/ROADMAP.md.

Дисклеймер

Это пользовательский эксперимент по реверс-инжинирингу. Протокол не публичный; опкоды наблюдены через официальный веб-клиент и Android-клиент (v26.15.1), могут быть изменены MAX без предупреждения. Автор не несёт ответственности за блокировку аккаунтов в результате использования библиотеки.

Не используйте её для спама, скрейпинга, массовых рассылок или иных нарушений условий использования MAX.

Лицензия

MIT