umbot
Version:
Мультиплатформенный фреймворк для создания голосовых навыков и чат-ботов с единой бизнес-логикой. Встроенная поддержка ВКонтакте, Telegram, Viber, MAX, Яндекс Алисы, Маруси и Сбера SmartApp. Архитектура на адаптерах позволяет подключать любые другие платф
433 lines (432 loc) • 16.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VkRequest = void 0;
const index_1 = require("../../../index");
const constants_1 = require("../VK/constants");
const constants_2 = require("./constants");
/**
* Версия VK API по умолчанию
*/
const VK_API_VERSION = '5.103';
/**
* Базовый URL для всех методов VK API
*/
const VK_API_ENDPOINT = 'https://api.vk.ru/method/';
/**
* Класс для взаимодействия с API ВКонтакте
* Предоставляет методы для отправки сообщений, загрузки файлов и работы с другими функциями API
* @see (https://vk.ru/dev/bots_docs) Смотри тут
*
* @example
* ```ts
* import { VkRequest } from './api/VkRequest';
*
* // Создание экземпляра
* const vk = new VkRequest();
* vk.initToken('your-vk-token');
*
* // Отправка простого сообщения
* await vk.messagesSend(12345, 'Привет!');
*
* // Отправка сообщения с клавиатурой
* const keyboard = {
* one_time: true,
* buttons: [[{
* action: {
* type: 'text',
* label: 'Кнопка',
* payload: '{"button": 1}'
* },
* color: 'primary'
* }]]
* };
*
* await vk.messagesSend(12345, 'Выберите действие:', {
* keyboard: JSON.stringify(keyboard)
* });
*
* // Загрузка и отправка фото
* const server = await vk.photosGetMessagesUploadServer(12345);
* if (server) {
* const upload = await vk.upload(server.upload_url, 'path/to/photo.jpg');
* if (upload) {
* const photo = await vk.photosSaveMessagesPhoto(
* upload.photo,
* upload.server,
* upload.hash
* );
* if (photo) {
* await vk.messagesSend(12345, 'Фото:', {
* attachments: [`photo${photo.owner_id}_${photo.id}`]
* });
* }
* }
* }
* ```
*/
class VkRequest {
/**
* Текущая используемая версия VK API
*/
#vkApiVersion;
/**
* Экземпляр класса для выполнения HTTP-запросов
*/
_request;
/**
* Текст последней возникшей ошибки
*/
_error;
/**
* Токен доступа к VK API
*/
token;
/**
* Флаг для прямой передачи содержимого файла
* По умолчанию: false
*/
isAttachContent;
/**
* Контекст приложения.
*/
_appContext;
/**
* Создает экземпляр класса для работы с API ВКонтакте
* Устанавливает токен из конфигурации приложения, если он доступен
*/
constructor(appContext) {
this._request = new index_1.Request(appContext);
this._request.header = {
'Content-Type': 'application/x-www-form-urlencoded',
};
this._request.maxTimeQuery = 5500;
this.isAttachContent = false;
this._appContext = appContext;
if (appContext.appConfig.tokens[constants_1.T_VK]?.api_version) {
this.#vkApiVersion = appContext.appConfig.tokens[constants_1.T_VK].api_version;
}
else {
this.#vkApiVersion = VK_API_VERSION;
}
this.token = null;
this._error = null;
if (appContext.appConfig.tokens[constants_1.T_VK]?.token) {
this.initToken(appContext.appConfig.tokens[constants_1.T_VK].token);
}
}
/**
* Инициализирует токен доступа к VK API
* @param token Токен доступа к VK API
*/
initToken(token) {
this.token = token;
}
/**
* Выполняет вызов метода VK API
* @param method Название метода VK API
* @returns Результат выполнения метода или null при ошибке
*/
async call(method) {
if (this.token) {
if (!this._request.attach) {
// vk принимает post только в таком формате
this._request.header = {
'Content-Type': 'application/x-www-form-urlencoded',
};
}
this._request.post ??= {};
if (!(this._request.post instanceof FormData)) {
this._request.post.access_token = this.token;
this._request.post.v = this.#vkApiVersion;
if (!this._request.attach) {
// vk принимает post только в таком формате
this._request.postInString = (0, index_1.httpBuildQuery)(this._request.post);
}
}
const data = await this._request.send(VK_API_ENDPOINT + method);
if (data.status && data.data) {
this._error = data.err || [];
if (data.data.error !== undefined) {
this._error = data;
this._log('call() Запрос к платформе вернулся с ошибкой.');
return null;
}
return data.data.response || data.data;
}
this._log(data.err);
}
else {
this._log((0, constants_2.getErrorToken)(constants_1.T_VK, 'call'));
}
return null;
}
/**
* Загружает файл на сервера ВКонтакте
* @param url URL для загрузки файла
* @param file Путь к файлу или его содержимое
* @returns Информация о загруженном файле или null при ошибке
*
* @remarks
* Для фотографий возвращает:
* - photo: строка, содержащая информацию о фото
* - server: строка, идентификатор сервера
* - hash: строка, хеш загруженного файла
*
* Для документов возвращает:
* - file: строка, информация о загруженном файле
*
* @example
* ```ts
* // Загрузка фото
* const server = await vk.photosGetMessagesUploadServer(12345);
* if (server) {
* const upload = await vk.upload(server.upload_url, 'photo.jpg');
* if (upload) {
* const photo = await vk.photosSaveMessagesPhoto(
* upload.photo,
* upload.server,
* upload.hash
* );
* }
* }
*
* // Загрузка документа
* const server = await vk.docsGetMessagesUploadServer(12345, 'doc');
* if (server) {
* const upload = await vk.upload(server.upload_url, 'document.pdf');
* if (upload) {
* const doc = await vk.docsSave(
* upload.file,
* 'Документ',
* 'тег1,тег2'
* );
* }
* }
* ```
*/
async upload(url, file) {
this._request.attach = file;
this._request.isAttachContent = this.isAttachContent;
this._request.header = index_1.Request.HEADER_FORM_DATA;
const data = await this._request.send(url);
if (data.status && data.data) {
if (data.data.error !== undefined) {
this._error = data;
this._log();
return null;
}
return data.data;
}
this._log(data.err);
return null;
}
/**
* Отправляет сообщение пользователю или в чат
* @param peerId Идентификатор получателя:
* - ID пользователя (например, "12345")
* - ID чата: 2000000000 + chat_id (например, для чата 1: 2000000001)
* - ID сообщества: -ID сообщества (например, "-123456789")
* @param message Текст сообщения
* @param params Дополнительные параметры:
* - random_id: уникальный ID для избежания повторов
* - attachment: медиавложения в формате "<type><owner_id>_<media_id>"
* Примеры:
* - Фото: "photo123456_789"
* - Документ: "doc123456_789"
* - Аудио: "audio123456_789"
* - Видео: "video123456_789"
* - keyboard: клавиатура в JSON формате или строкой
* - template: шаблон карусели в JSON формате или строкой
*
* @example
* ```ts
* // Простое сообщение
* await vk.messagesSend(12345, 'Привет!');
*
* // Сообщение с вложениями
* await vk.messagesSend(12345, 'Фото:', {
* attachments: ['photo123_456', 'doc123_456']
* });
*
* // Сообщение с клавиатурой
* const keyboard = {
* one_time: true,
* buttons: [[{
* action: {
* type: 'text',
* label: 'Кнопка',
* payload: '{"button": 1}'
* },
* color: 'primary'
* }]]
* };
* await vk.messagesSend(12345, 'Выберите:', {
* keyboard: JSON.stringify(keyboard)
* });
*
* // Сообщение с каруселью
* const template = {
* type: 'carousel',
* elements: [{
* title: 'Заголовок',
* description: 'Описание',
* photo_id: '-123456_789',
* buttons: [{
* action: {
* type: 'text',
* label: 'Кнопка',
* payload: '{"button": 1}'
* }
* }]
* }]
* };
* await vk.messagesSend(12345, '', {
* template: JSON.stringify(template)
* });
* ```
*
* @returns Информация об отправленном сообщении или null при ошибке
*/
async messagesSend(peerId, message, params = null) {
const method = 'messages.send';
this._request.post = {
peer_id: peerId,
message,
};
if (typeof peerId !== 'number') {
this._request.post.domain = peerId;
this._request.post.peer_id = undefined;
}
if (params) {
if (params.random_id === undefined) {
this._request.post.random_id = Date.now();
}
else {
this._request.post.random_id = params.random_id;
}
if (params.attachments !== undefined) {
this._request.post.attachment = params.attachments.join(',');
params.attachments = undefined;
}
if (params.template !== undefined) {
if (typeof params.template !== 'string') {
params.template = JSON.stringify(params.template);
}
this._request.post.template = params.template;
params.template = undefined;
}
if (params.keyboard !== undefined) {
if (this._request.post.template !== undefined) {
// await this.call<IVKSendMessage>(method);
this._request.post.template = undefined;
}
if (typeof params.keyboard !== 'string') {
params.keyboard = JSON.stringify(params.keyboard);
}
this._request.post.keyboard = params.keyboard;
params.keyboard = undefined;
}
if ((0, index_1.keysCount)(params)) {
this._request.post = { ...params, ...this._request.post };
}
}
return await this.call(method);
}
/**
* Получает информацию о пользователе или списке пользователей
* @param userId ID пользователя или массив ID
* @param params Дополнительные параметры запроса
* @returns Информация о пользователях или null при ошибке
*/
async usersGet(userId, params = null) {
if (typeof userId === 'number') {
this._request.post = { user_id: userId };
}
else {
this._request.post = { user_ids: userId };
}
if (params) {
this._request.post = { ...this._request.post, ...params };
}
return this.call('users.get');
}
/**
* Получает URL для загрузки фотографий в сообщения
* @param peerId ID получателя сообщения
* @returns Данные для загрузки или null при ошибке
*/
async photosGetMessagesUploadServer(peerId) {
this._request.post = { peer_id: peerId };
return this.call('photos.getMessagesUploadServer');
}
/**
* Сохраняет загруженную фотографию в сообщениях
* @param photo Идентификатор фотографии
* @param server Идентификатор сервера
* @param hash Хэш-сумма фотографии
* @returns Информация о сохраненной фотографии или null при ошибке
*/
async photosSaveMessagesPhoto(photo, server, hash) {
this._request.post = {
photo,
server,
hash,
};
return (await this.call('photos.saveMessagesPhoto'));
}
/**
* Получает URL для загрузки документов в сообщения
* @param peerId ID получателя сообщения
* @param type Тип документа:
* - 'doc': обычный документ (PDF, ZIP и т.д.)
* - 'audio_message': голосовое сообщение
* - 'graffiti': граффити
* @returns Данные для загрузки или null при ошибке
*
* @example
* ```ts
* // Загрузка обычного документа
* const server = await vk.docsGetMessagesUploadServer(12345, 'doc');
*
* // Загрузка голосового сообщения
* const server = await vk.docsGetMessagesUploadServer(12345, 'audio_message');
*
* // Загрузка граффити
* const server = await vk.docsGetMessagesUploadServer(12345, 'graffiti');
* ```
*/
async docsGetMessagesUploadServer(peerId, type) {
this._request.post = {
peer_id: peerId,
type,
};
return this.call('docs.getMessagesUploadServer');
}
/**
* Сохраняет загруженный документ
* @param file Идентификатор документа
* @param title Название документа
* @param tags Теги документа
* @returns Информация о сохраненном документе или null при ошибке
*/
async docsSave(file, title, tags = null) {
this._request.post = {
file,
title,
};
if (tags) {
this._request.post.tags = tags;
}
return this.call('docs.save');
}
/**
* Записывает информацию об ошибках в лог-файл
* @param error Текст ошибки для логирования
*/
_log(error = '') {
this._appContext.logError((0, constants_2.getErrorMsg)(error, 'VkRequest', this._request.url), {
error: this._error,
});
}
}
exports.VkRequest = VkRequest;