25 KiB
План интеграции VK API для Randomayzer
Контекст: Randomayzer — Next.js + TypeScript + Prisma + PostgreSQL сервис для проведения доказуемо честных розыгрышей ВКонтакте. В текущей реализации (
src/providers/vk/vk-provider.ts) используется сервисный токенVK_SERVICE_TOKENи методыwall.getById,likes.getList,wall.getComments,groups.isMember. OAuth / VK ID не реализованы.Статус документа: планировочный, без production-кода. Все факты, которые не удалось подтвердить официальной документацией VK, помечены как
UNVERIFIED.
Authentication
Для работы с VK API существуют три типа ключей доступа. Для Randomayzer наиболее подходящим на старте является сервисный ключ (соответствует текущей реализации VkProvider), так как он не требует авторизации пользователя и покрывает базовые сценарии розыгрыша: получение метаданных поста, сбор лайков/комментариев с открытых стен и пакетную проверку подписки на сообщество.
Расширенные сценарии (проверка репостов на закрытых страницах, получение списка руководителей сообщества, действия от имени сообщества) требуют ключа пользователя или ключа сообщества.
Service Token (сервисный ключ доступа)
- Как получить. В панели управления приложением на dev.vk.ru (раздел Разработка → Ключи доступа) либо в кабинете сервиса авторизации VK ID при создании Standalone-приложения/сайта.
- Scope/права. Права доступа не запрашиваются. Ключ предназначен для работы с публичными данными игр/мини-приложений и методами, не требующими авторизации пользователя.
- Применимые методы.
wall.getById,likes.getList,wall.getComments,wall.getReposts,groups.isMember,groups.getMembers(с ограничениями),executeUNVERIFIED. - Ограничения.
- Работает только с открытыми профилями и открытыми группами.
- Не позволяет выполнять действия от имени пользователя или сообщества.
- Сбор репостов ограничен политикой приватности VK (закрытые профили не видны).
- Lifetime. Не ограничен; при компрометации можно перевыпустить в настройках приложения.
- Риски. Утечка ключа открывает публичные данные приложения. Ключ должен храниться только на сервере, никогда — в клиентском коде.
User Token (ключ доступа пользователя / OAuth 2.0 / VK ID)
- Как получить. Через сервис авторизации VK ID (Authorization Code Flow для сервера, Implicit Flow для клиента) или событие
VKWebAppGetAuthTokenVK Bridge в мини-приложениях/играх. - Scope/права. Определяются правами доступа, которые пользователь выдал приложению (например,
wall,groups,friends). Базовые права (имя, фото, почта) доступны сразу; расширенные требуют подтверждения профиля бизнеса. - Применимые методы. Все методы, доступные сервисному ключу, плюс методы, требующие авторизации конкретного пользователя: просмотр закрытых профилей при наличии доступа, расширенная работа со стеной,
groups.getи т.д. - Ограничения.
- Требует прохождения пользователем экрана согласия.
- Короткий срок жизни (см. ниже), необходимо обновление.
- Lifetime. 1 час для ключа, полученного через VK ID / OAuth.
- Риски.
- Необходимость безопасного хранения refresh-токенов и секретов приложения.
- Нужно реализовать OAuth flow и обработку отзыва разрешений пользователем.
- При передаче в клиент увеличивается риск перехвата.
Community Token (ключ доступа сообщества)
- Как получить. В настройках сообщества: Управление → Дополнительно → Работа с API → Ключи доступа. Можно создать несколько ключей с разным набором прав. Программно — через OAuth ВКонтакте (Authorization Code Flow / Implicit Flow) либо событие
VKWebAppGetCommunityTokenVK Bridge. - Scope/права. Права назначаются при создании ключа (например,
wall,photos,messages,manage). - Применимые методы.
- Методы в рамках одного сообщества:
wall.getById,likes.getList,wall.getComments,groups.isMember,groups.getMembers(включаяfilter=managersдля получения ролей). - Методы управления сообществом — не требуются Randomayzer на этапе сбора участников.
- Методы в рамках одного сообщества:
- Ограничения.
- Действует только в рамках сообщества, для которого выдан.
- Получить ключ может только администратор сообщества.
- Lifetime. Не ограничен; администратор может отозвать ключ в любой момент.
- Риски.
- Утечка ключа с правами
manageдаёт полный контроль над сообществом. - Требует строгого разграничения прав по ключам (принцип минимальных привилегий).
- Утечка ключа с правами
Рекомендация для Randomayzer
| Этап | Рекомендуемый токен | Обоснование |
|---|---|---|
| MVP / текущая реализация | Сервисный ключ | Без OAuth, серверная работа, публичные посты и лайки |
| Проверка репостов в закрытых профилях | Ключ пользователя организатора | Требуется авторизация владельца стены/профиля |
| Проверка администраторов/модераторов сообщества | Ключ сообщества | groups.getMembers filter=managers доступно с правами администратора |
| Массовые розыгрыши 10 000+ участников | Сервисный + пакетирование (execute) UNVERIFIED |
Высшие лимиты и отсутствие необходимости UI-авторизации |
Tokens
| Тип токена | Назначение | Доступные методы (для розыгрышей) | Ограничения | Срок жизни | Риски |
|---|---|---|---|---|---|
| Сервисный ключ | Серверные запросы без авторизации пользователя | wall.getById, likes.getList, wall.getComments, wall.getReposts, groups.isMember, groups.getMembers |
Только открытые профили/группы; репосты ограничены приватностью; execute — UNVERIFIED |
Не ограничен | Утечка открывает публичные данные; нельзя передавать на клиент |
| Ключ пользователя (VK ID OAuth) | Запросы от имени пользователя | Все методы сервисного ключа + методы, требующие авторизации (groups.get, доступ к закрытым данным при согласии) |
Требуется согласие пользователя; короткий срок жизни; refresh-логика | 1 час | Хранение секретов/refresh; необходимость OAuth flow; отзыв прав |
| Ключ сообщества | Запросы от имени сообщества | Методы в рамках одного сообщества, включая groups.getMembers filter=managers |
Только своё сообщество; нужен администратор для создания | Не ограничен | Утечка ключа с правами manage даёт контроль над сообществом |
Что каждый токен может и не может делать для розыгрышей
- Сервисный ключ
- ✅ Получить метаданные открытого поста.
- ✅ Собрать лайки (
likes.getList) и комментарии (wall.getComments). - ✅ Проверить подписку на сообщество (
groups.isMember). - ❌ Получить список руководителей сообщества (
groups.getMembers filter=managers) — UNVERIFIED, вероятно требуется ключ сообщества. - ❌ Увидеть репосты на закрытых профилях.
- Ключ пользователя
- ✅ Всё, что умеет сервисный ключ (если профили/группы доступны пользователю).
- ✅ Доступ к расширенным данным при наличии соответствующих прав.
- ❌ Не даёт прав администратора чужого сообщества.
- Ключ сообщества
- ✅ Все операции в рамках своего сообщества.
- ✅ Проверка ролей (
groups.getMembers filter=managers) — при наличии прав. - ❌ Не применим к постам/группам других организаторов.
Required VK methods
| Функциональность | VK Method | Токен | Права / Параметры | Ограничения |
|---|---|---|---|---|
| Загрузка метаданных поста | wall.getById |
Сервисный или пользовательский | posts={owner_id}_{post_id}, extended=1 |
Возвращает массив объектов post; при extended=1 дополнительно profiles и groups. Ошибка 104 Not found, если запись недоступна. |
| Сбор лайков | likes.getList |
Сервисный или пользовательский | type=post, owner_id, item_id, filter=likes, extended=1, count до 1000, offset |
Максимум 1000 идентификаторов за запрос. При filter=copies возвращает пользователей, поделившихся записью, но только если запрос отправляет администратор группы (права редактора и выше) или владелец стены. |
| Сбор комментариев | wall.getComments |
Сервисный или пользовательский | owner_id, post_id, extended=1, count до 100, offset, fields |
Максимум 100 комментариев за запрос. При extended=1 возвращает profiles и groups. Ошибка 212 Access to post comments denied. |
| Проверка подписки на сообщество | groups.isMember |
Сервисный, пользовательский или ключ сообщества | group_id, user_id или user_ids (до 500), extended=1 |
При пакетной проверке возвращает массив объектов {user_id, member}. При extended=1 дополнительно request, invitation, can_invite. |
| Информация о сообществе / список участников | groups.getMembers |
Сервисный, пользовательский или ключ сообщества | group_id, count, offset, fields, filter |
filter=managers доступно при запросе от имени администратора сообщества; возвращает role (advertiser, moderator, editor, administrator, creator). Максимальное значение count — UNVERIFIED. |
| Сбор / проверка репостов | wall.getReposts |
Сервисный или пользовательский | owner_id, post_id, offset, count |
Возвращает items (записи-репосты), profiles, groups. Закрытые профили не попадают в выдачу без авторизации владельца стены. |
| Альтернативная проверка репостов | likes.getList с filter=copies |
Ключ сообщества (администратор группы) или владелец стены | type=post, owner_id, item_id, filter=copies, extended=1 |
Возвращает пользователей, поделившихся записью, только при наличии соответствующих прав. |
| Проверка администраторов/модераторов | groups.getMembers с filter=managers |
Ключ сообщества с правами администратора | group_id, filter=managers |
Возвращает руководителей сообщества с полем role. Недоступно сервисному ключу — UNVERIFIED. |
Примечания к таблице
- Типы токенов для каждого метода определены по цветовым индикаторам в официальном справочнике VK: серый — сервисный ключ, оранжевый — пользовательский, синий — ключ сообщества.
wall.getById,likes.getList,wall.getComments,wall.getRepostsимеют индикаторы сервисного и пользовательского токенов.groups.isMemberиgroups.getMembersимеют индикаторы всех трёх типов токенов.
Rate Limit Strategy
Важно: этот раздел описывает только архитектуру и контракты. Код не реализуется в рамках данного документа.
Целевая архитектура
VK API
│
▼
┌─────────────┐ ┌──────────────┐ ┌─────────────┐ ┌─────────────┐
│ VkClient │────▶│ RateLimiter │────▶│ RetryPolicy │────▶│ VkProvider │
│ (HTTP+Auth)│ │ (token-bucket)│ │ (backoff) │ │(SocialMediaProvider)
└─────────────┘ └──────────────┘ └─────────────┘ └─────────────┘
VkProvider реализует интерфейс SocialMediaProvider и делегирует низкоуровневые вызовы VkClient. RateLimiter и RetryPolicy являются отдельными, тестируемыми абстракциями.
Контракты / интерфейсы
// Абстракция HTTP-клиента VK API
interface VkClient {
call<T>(method: string, params: Record<string, unknown>): Promise<VkResponse<T>>;
}
type VkResponse<T> =
| { response: T; error?: never }
| { response?: never; error: VkError };
interface VkError {
error_code: number;
error_msg: string;
}
// Ограничение скорости
interface RateLimiter {
acquire(tokenType: VkTokenType, cost?: number): Promise<void>;
updateLimits(tokenType: VkTokenType, remaining: number, resetAt: Date): void;
}
type VkTokenType = 'service' | 'user' | 'community';
// Политика повторных попыток
interface RetryPolicy {
execute<T>(task: () => Promise<T>, context: RetryContext): Promise<T>;
}
interface RetryContext {
maxAttempts: number;
isRetryable: (error: VkError) => boolean;
computeDelay: (attempt: number, error: VkError) => number;
}
Exponential backoff
- Начальная задержка: 500–1000 мс.
- Множитель: 2 (с jitter до 20–30 %), чтобы избежать «thundering herd».
- Максимальная задержка: 30–60 с.
- Общее время ожидания должно быть ограничено (например, 5 минут на одну операцию), после чего ошибка прокидывается вызывающему коду.
Retryable vs non-retryable errors
| Код ошибки | Статус | Действие |
|---|---|---|
6 Too many requests per second |
Retryable | Повторить после backoff, уменьшить скорость |
9 Flood control |
Retryable | Увеличить задержку, возможно — запросить капчу UNVERIFIED |
10 Internal server error |
Retryable | Повторить с backoff |
29 Rate limit reached |
Retryable/Non-retryable | Дневной лимит; повторять с большим интервалом либо прекратить |
15 Access denied |
Non-retryable | Закрытая группа/профиль; зафиксировать в аудите |
18 User was deleted or banned |
Non-retryable | Исключить пользователя из выборки |
30 Private profile |
Non-retryable | Исключить с причиной PRIVATE_PROFILE_OR_NO_REPOST |
104 Not found |
Non-retryable | Пост не найден |
212 Access to post comments denied |
Non-retryable | Ограничены комментарии |
232 Reaction can not be applied |
Non-retryable | Ошибка параметров likes.getList |
Rate limit handling
- Лимиты по типу токена (подтверждено документацией VK):
- Пользовательский ключ: 3 запроса/сек.
- Ключ сообщества: 20 запросов/сек.
- Сервисный ключ: от 5 до 60 запросов/сек в зависимости от количества пользователей приложения.
- Рекомендуется использовать token-bucket per
VkTokenTypeс консервативным начальным значением (например, 50 % от заявленного лимита) и адаптацией при получении ошибки6. - Все запросы одного розыгрыша должны учитывать общий bucket, даже если они выполняются в разных корутинах/процессах.
Pagination strategy
likes.getList:count=1000, увеличиватьoffsetна 1000 до достиженияresponse.count.wall.getComments:count=100, увеличиватьoffsetна 100.groups.isMember: батчироватьuser_idsпо 500 ID.groups.getMembers: UNVERIFIED — предположительноcountдо 1000, но в документации точное максимальное значение не указано.- При изменении данных во время pagination (например, пользователь удалил лайк) возможны дубли или пропуски. Для розыгрышей рекомендуется фиксировать
snapshotTimeи игнорировать изменения после него.
Batching через execute
- Метод
executeпозволяет выполнять код на серверах VK (VKScript). - Предполагаемые лимиты (UNVERIFIED): до 25 вызовов API внутри одного
execute. - Потенциальная эффективность:
likes.getList: 25 × 1000 = до 25 000 лайков за 1 HTTP-запрос.groups.isMember: 25 × 500 = до 12 500 проверок подписки за 1 HTTP-запрос.wall.getComments: 25 × 100 = до 2 500 комментариев за 1 HTTP-запрос.
- Риски: один упавший под-вызов внутри
executeможет прервать весь батч; требуется гранулярная обработка ошибок и fallback на последовательные запросы.
Timeout и cancellation
- Установить разумный network timeout для VK API: 30–60 с.
- Поддержать
AbortSignal/ cancellation token на уровнеVkClient, чтобы длительные операции сбора участников можно было прервать из UI. - При отмене сохранять уже загруженные данные и статус прогресса.
References
Официальная документация VK, использованная при составлении плана:
- Общий формат запросов и лимиты: https://dev.vk.ru/ru/api/api-requests
- Ключи доступа — обзор: https://dev.vk.com/api/access-token
- Сервисный ключ доступа: https://dev.vk.ru/ru/api/access-token/service-token
- Ключ доступа пользователя: https://dev.vk.ru/ru/api/access-token/user-token
- Ключ доступа сообщества: https://dev.vk.ru/ru/api/access-token/community-token
- Справочник ошибок: https://dev.vk.ru/ru/reference/errors
- Метод
wall.getById: https://dev.vk.com/method/wall.getById - Метод
likes.getList: https://dev.vk.com/method/likes.getList - Метод
wall.getComments: https://dev.vk.com/method/wall.getComments - Метод
groups.isMember: https://dev.vk.com/method/groups.isMember - Метод
groups.getMembers: https://dev.vk.com/method/groups.getMembers - Метод
wall.getReposts: https://dev.vk.com/method/wall.getReposts - Метод
execute: https://dev.vk.com/method/execute
Резюме для команды
- Сейчас (
VkProvider) используется только сервисный ключVK_SERVICE_TOKEN. Это корректный MVP-подход: без OAuth, серверная работа, базовый сбор лайков/комментариев и проверка подписки. - Дальнейшее развитие требует выбора между:
- OAuth VK ID для получения пользовательского токена организатора (для закрытых профилей и расширенных прав);
- ключом сообщества (для проверки ролей админов/модераторов и работы строго в рамках одного сообщества).
- Перед внедрением OAuth необходимо отдельно спроектировать поток авторизации, хранение токенов, refresh-логику и аудит действий от имени пользователя.
- Rate limiting должен быть вынесен в отдельные слои
RateLimiterиRetryPolicy, чтобыVkProviderоставался чистым адаптеромSocialMediaProvider. - Все неподтверждённые официальной документацией числовые лимиты (
execute, максимальныйcountдляgroups.getMembersи др.) помеченыUNVERIFIEDи требуют проверки в процессе разработки.