UNPKG

umbot

Version:

Мультиплатформенный фреймворк для создания голосовых навыков и чат-ботов с единой бизнес-логикой. Встроенная поддержка ВКонтакте, Telegram, Viber, MAX, Яндекс Алисы, Маруси и Сбера SmartApp. Архитектура на адаптерах позволяет подключать любые другие платф

492 lines (491 loc) 25.6 kB
/** * Интерфейсы и типы для работы с приложением. * Определяют основные контракты и структуры данных для взаимодействия */ import { AppContext } from '../AppContext'; import { BotController } from '../../controller'; import { IncomingMessage, ServerResponse } from 'node:http'; import { IButtonType, Buttons, IImageType, ISound } from '../../components'; import { IModelRes, TQueryCb, IQuery, IQueryData } from '../../models'; import { Bot } from '../Bot'; /** * Тип содержимого запроса к голосовому навыку или боту * Определяет возможные форматы данных, которые могут быть переданы навыку/боту * * @remarks * Возможные значения: * - string: JSON или текстовое содержимое запроса * ```ts * const content: TBotContent = '{"text": "Привет мир!"}'; * ``` * - boolean: Флаг состояния запроса * ```ts * const content: TBotContent = true; // запрос успешно обработан * ``` * - null: Пустой запрос или ошибка * ```ts * const content: TBotContent = null; // запрос не содержит данных * ``` */ export type TBotContent = object | string | null; /** * Тип авторизационного токена. * Определяет формат данных для авторизации пользователя * * @remarks * Возможные значения: * - string: Валидный токен авторизации * ```ts * const auth: TBotAuth = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; * ``` * - null: Пользователь не авторизован или авторизация не требуется * ```ts * const auth: TBotAuth = null; // авторизация отсутствует * ``` */ export type TBotAuth = string | null; /** * Интерфейс для базового ответ */ export interface IBotResponse { /** * Статус код ответа. * - 200 в случае успеха * - 400 в случае если отправлен пустой запрос, или фреймворк завершил обработку с ошибкой. * - 500 в случае ошибки самого сервера */ statusCode: number; /** * Содержимое, которое необходимо отобразить */ body: string | object; } /** * Интерфейс для состояния, которое придет в пользовательском обработчике ответа */ export interface IBotResponseState extends IBotResponse { /** * Базовый метод для отправки ответа * @param res * @param state */ defaultSend: (res: ServerResponse, state: IBotResponse) => void; } /** * Тип для пользовательской обработки запроса * @param reg - Объект входящего запроса (IncomingMessage или совместимый) * @param res - Объект ответа (ServerResponse или совместимый) * @param state Состояние выполненного запроса */ export type TBotResponseCb = (reg: IncomingMessage, res: ServerResponse, state: IBotResponseState) => void; /** * Интерфейс для плагина в виде объекта. */ export interface IPlugin { /** * Метод инициализации плагина. * Вызывается один раз при подключении через `bot.use()`. * @param appContext Контекст приложения * @param bot Основной класс приложения */ init: (appContext: AppContext, bot: Bot) => void; /** * Метод, который вызывается при уничтожении плагина. * В данном методе можно добавить отписку, либо выполнить другие действия. * @param bot Основной класс приложения */ destroy: (bot: Bot) => void | Promise<void>; } /** * Возвращаемый результат после инициализации плагина. * Возвращает либо void, либо функция, которая будет вызвана после уничтожения. */ export type TPluginFnResult = void | ((bot: Bot) => void); /** * Тип для плагина в виде функции. * Должен иметь свойство `isPlugin = true` для отличия от обычных функций. */ export interface IPluginFn { /** * Конструктор функции регистрации плагина * @extends * ```ts * function myPlugin(appContext: AppContext, bot: Bot) { * // Какая-то ваша логика * return () => { * // Функция, которая будет вызвана при уничтожении. * } * } * ``` * @param appContext Контекст приложения * @param bot Основной класс приложения */ (appContext: AppContext, bot: Bot): TPluginFnResult; /** * Флаг, говорящий о том, что функция является плагином */ isPlugin: boolean; } /** * Объединённый тип для любого плагина. */ export type TPlugin = IPlugin | IPluginFn; /** * Интерфейс для адаптеров платформы (Алиса, Салют, Telegram, VK и др.). * * Обеспечивает унификацию обработки запросов от разных платформ. * Реализуется как плагин (`IPlugin`) и регистрируется в приложении. */ export interface IPlatformAdapter<TQuery = unknown> extends IPlugin { /** * Определяет, принадлежит ли входящий запрос данной платформе. * * Метод проверяет заголовки или структуру тела запроса. * Используется для маршрутизации входящих запросов между адаптерами. * * @param query - входящий запрос (тело или объект) * @param headers - HTTP-заголовки (если есть) * @returns `true`, если запрос относится к этой платформе, иначе `false` * * @example * ```ts * // Telegram проверяет наличие заголовка 'X-Telegram-Bot-Api-Secret-Token' * isPlatformOnQuery(query, headers) { * return headers?.['x-telegram-bot-api-secret-token'] === this.secret; * } * ``` */ isPlatformOnQuery: (query: TQuery, headers?: Record<string, unknown>) => boolean; /** * Проверяет полученный запрос от платформы на корректность. * Реализация зависит от адаптера, как правило, в чувствительных платформах есть токен, который приходит с запросом, и желательно проверять что пришедший токен соответствует тому, что сохранен в настройках. * @param query * @param headers */ isCorrectQuery: (query: TQuery, headers?: Record<string, unknown>) => boolean; /** * Инициализирует данные запроса в контроллере приложения. * * Парсит входящий запрос и заполняет `controller.queryData`, `controller.user` и другие поля. * Вызывается после подтверждения, что запрос принадлежит этой платформе. * * @param query - входящий запрос * @param controller - контроллер приложения для текущего запроса * @returns `false`, если запрос повреждён или не может быть обработан; иначе `true` */ setQueryData: (query: TQuery, controller: BotController) => boolean | Promise<boolean>; /** * Формирует тело ответа для отправки пользователю. * * Возвращает платформо-специфичный ответ (например, JSON для Алисы). * Для платформ, которые отправляют ответ напрямую (например, Telegram через `sendMessage`), * метод может возвращать `{ ok: true }` или аналог. * * @param controller - контроллер с готовым ответом * @param stateData - данные для локального хранилища * @returns ответ в формате, понятном платформе */ getContent: (controller: BotController, stateData?: Record<string, unknown> | null) => object | string | Promise<object | string>; /** * Формирует контекст для отправки рейтинга (если поддерживается платформой). * * Используется только на платформах с поддержкой рейтинга (например, Сбер SmartApp). * Если рейтинг не поддерживается — метод может не реализовываться или возвращать пустой объект. * * @param controller - контроллер приложения * @returns данные для отправки рейтинга */ getRatingContext: (controller: BotController) => object | string | Promise<object | string>; /** * Устанавливает время начала обработки запроса. * * Используется для измерения времени отклика (`processingTime`). * * @param controller - контроллер приложения */ updateTimeStart: (controller: BotController) => void; /** * Возвращает время обработки запроса в миллисекундах. * * Основано на разнице между `updateTimeStart` и текущим временем. * * @param controller - контроллер приложения * @returns время обработки в мс */ getProcessingTime: (controller: BotController) => number; /** * Уникальное имя платформы (например, 'telegram', 'alisa'). */ platformName: string; /** * Указывает, поддерживает ли платформа локальное хранилище. * * @param controller - контроллер приложения * @returns `true`, если локальное хранилище доступно */ isLocalStorage: (controller: BotController) => boolean; /** * Получает данные из локального хранилища платформы. * * @param controller - контроллер приложения * @returns данные, сохранённые ранее */ getLocalStorage: <TStorageResult = unknown>(controller: BotController) => TStorageResult | Promise<TStorageResult>; /** * Сохраняет данные в локальное хранилище платформы. * * @param data - данные для сохранения * @param controller - контроллер приложения */ setLocalStorage: <TStorageData>(data: TStorageData, controller: BotController) => void | Promise<void>; /** * Флаг, указывающий, что платформа голосовая (например, Алиса, Маруся). */ isVoice: boolean; /** * Генерирует пример входящего запроса для локального тестирования навыка/бота. * * Используется в инструментах отладки и авто-тестах. * * @param query - текст запроса * @param userId - идентификатор пользователя * @param count - счётчик для уникальности * @param state - состояние сессии * @returns объект, имитирующий входящий запрос платформы */ getQueryExample: (query: string, userId: string, count: number, state: Record<string, unknown> | string) => Record<string, unknown>; /** * Отправка текста пользователю * Этот метод используется для активных рассылок — когда навык или бот инициирует диалог первым (например, уведомление). * В данном методе необходимо поддержать отправку результата пользователю. * Это необходимо для того, чтобы само приложение смогло продолжить диалог. * * Если ваша платформа не поддерживает отправку сообщений без входящего запроса (как Алиса), оставьте реализацию пустой или верните заглушку. * @param userId Ид пользователя, которому нужно отправить сообщение * @param controllerOrText Контроллер приложения или текст. Если необходимо отправить просто текст, можно передать строку, в случае, если необходимо передать картинку звук и тд, то необходимо корректно заполнить контроллер. */ send(userId: string | number, controllerOrText: BotController | string): unknown | boolean; /** * Определяет лимит платформы. * В значение указывается количество запросов, которое можно отправить платформе за 1 секунду. * В случае если у платформы нет ограничений, можно указать 0 или null. * По умолчанию null */ limit: number | null; } /** * Результат запроса к базе данных. * * Поддерживает доступ как по строковым, так и по числовым ключам. * Используется для представления результатов SELECT-запросов. * * @template TValue — тип записей в результате (например, `User`, `Session`). * * @example * ```ts * const users: IDbResult<User> = { * 'user:123': { id: '123', name: 'Alice' }, * 456: { id: '456', name: 'Bob' } * }; * ``` */ export interface IDbResult<TValue = unknown> { /** * Результат запроса, доступный по строковым ключам */ [keyStr: string]: TValue; /** * Результат запроса, доступный по числовым ключам */ [keyInt: number]: TValue; } /** * Адаптер для работы с базой данных. * * Обеспечивает унифицированный интерфейс для различных СУБД (файловая, MongoDB, PostgreSQL и др.). * Все данные подключения и соединения хранятся в `AppContext` через `IDatabaseInfo`, * чтобы избежать повторного подключения при каждом запросе. */ export interface IDatabaseAdapter extends IPlugin { /** * Вызывается при удалении модели или завершении сессии. * Может использоваться для освобождения ресурсов, связанных с таблицей. * @param tableName Название таблицы, подключение к которой закрывается */ close: (tableName: string) => void | Promise<void>; /** * Вызывается при завершении работы приложения или замене адаптера. * Используйте для закрытия соединений, сохранения данных и т.п. */ destroy: () => void | Promise<void>; /** * Выполняет SELECT-запрос. * @param selectData Дополнительная информация для запроса. Содержит информацию о таблице и структуре. * @param where Сам запрос * @param isOne Определяет нужно ли вернуть только 1 найденную запись, либо отдать все доступные данные. */ select: (selectData: IQuery, where: IQueryData | null, isOne: boolean) => Promise<IModelRes>; /** * Выполняет INSERT-запрос. * @param insertData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные. */ insert: (insertData: IQuery) => Promise<boolean>; /** * Выполняет UPDATE-запрос. * @param updateData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные. */ update: (updateData: IQuery) => Promise<boolean>; /** * Выполняет DELETE-запрос. * @param removeData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные. */ remove: (removeData: IQuery) => Promise<boolean>; /** * Выполняет произвольный запрос через callback. * * @param callback функция обработчик */ query: (callback: TQueryCb) => unknown; /** * Проверяет, установлено ли соединение с БД. */ isConnected: () => Promise<boolean> | boolean; /** * Сохраняет данные (INSERT или UPDATE в зависимости от `isNew`). * @param query Данные для запроса. Включает как запроса, так и сами данные * @param isNew Флаг, говорящий о том, что добавляется новая запись */ save(query: IQuery, isNew: boolean): Promise<boolean>; /** * Извлекает значения из результата запроса. * @param data Результат запроса. В случае успешного запроса вернутся данные, в противном случае null */ getValue: (data: IModelRes) => IDbResult | null; /** * Выполняет SELECT с ограничением до одной записи. * @param selectData Дополнительные данные для запроса * @param where Сам запроса */ selectOne: (selectData: IQuery, where: IQueryData | null) => Promise<IModelRes | null>; /** * Экранирует строку для безопасного использования в SQL-запросах. * @param str Экранируемый запрос */ escapeString: (str: string | number) => string; /** * Устанавливает подключение к базе данных. * В случае успешного подключения возвращается true */ connect: () => Promise<boolean> | boolean; } /** * Хранилище для данных подключения и состояния базы данных. * * Содержит произвольные поля, зависящие от типа БД: * - Для файловой БД: `{ userData: {...}, image: {...} }` * - Для MongoDB: `{ mongoClient: MongoClient, mongoConnect: MongoClient }` * * Используется в `AppContext<IDatabaseInfo>` для избежания повторного подключения * при каждом запросе. */ export interface IDatabaseInfo { [key: string]: unknown; } /** * Реестр подключённых платформ. * * Ключ — имя платформы (например, `'telegram'`), значение — её адаптер. */ export interface IPlatform<TQuery = unknown> { [name: string]: IPlatformAdapter<TQuery>; } /** * Обработчик кнопок для кастомизации отображения под платформу. * * Используется внутри адаптеров платформ для преобразования общего формата кнопок * в платформо-специфичный (например, для Telegram или Алисы). * * @param buttons - массив кнопок в общем формате * @returns результат в формате, понятном платформе * @example * ```ts * function buttonProcessing(buttons) { * let result = ... * return result; * } * ``` */ export type TButtonProcessing<TResult = unknown, TType = Record<string, unknown> | string | null> = (buttons: IButtonType<TType>[]) => TResult; /** * Данные для формирования карточки (галерея, кнопки, заголовок). * * Используется в `TCardProcessing` для кастомизации отображения под платформу. */ export interface ICardInfo { /** * Используется ли режим галереи (несколько карточек). */ usedGallery: boolean; /** * Массив изображений для карточки(ек). */ images: IImageType[]; /** * Кнопки, привязанные к карточке. */ buttons: Buttons; /** * Заголовок карточки. */ title: string | null; /** * Описание карточки */ description: string | null; /** * Показывать только первую карточку. */ showOne?: boolean; } /** * Обработчик карточек для кастомизации отображения под платформу. * * @param cardInfo - данные карточки в общем формате * @param controller - контроллер приложения * @returns результат в формате, понятном платформе */ export type TCardProcessing<TResult = unknown> = (cardInfo: ICardInfo, controller: BotController) => TResult; /** * Данные для формирования звукового ответа. * * Используется в `TSoundProcessing` для генерации TTS или аудиофайлов. */ export interface ISoundInfo { /** * Используется ли стандартный звуковой ответ (TTS). */ usedStandardSound: boolean; /** * Текст для озвучки (если используется TTS). */ text: string; /** * Список кастомных аудиофайлов. */ sounds: ISound[]; } /** * Обработчик звуковых сообщений. * * @param soundInfo - данные для звукового ответа * @param controller - контроллер приложения * @returns строка, массив строк или промис с медиа-контентом */ export type TSoundProcessing<TResult = string | Promise<string> | Promise<string[]>> = (soundInfo: ISoundInfo, controller: BotController) => TResult; /** * Тип, определяющий режим работы с группировкой регулярных выражений. * - auto - Режим, при котором регулярные выражения группируются при достижении определенного количества. * - no-group - Режим, запрещающий группировать регулярные выражения. * - group - Режим, при котором все регулярные выражения группируются. */ export type TCommandGroupMode = 'auto' | 'no-group' | 'group';