loop-client
v0.1.3
Published
"Loop/Mattermost TypeScript client"
Downloads
72
Readme
Loop Client
Современный, строго типизированный TypeScript клиент для Loop. Совместим и с Node.js, и с браузерами, предоставляет (почти) полное покрытие Loop API с полной типобезопасностью.
Возможности
✨ Типобезопасность
- Написан на TypeScript 5.9 со строгой проверкой типов
- Полные определения типов для (почти) всех методов API
🔄 Надёжность
- Встроенные механизмы повторных попыток с настраиваемыми политиками
- Очередь запросов с контролем параллелизма (через breadline-ts)
🌐 Универсальная совместимость
- Работает как в Node.js (≥20), так и в браузерных окружениях
📊 Обширное покрытие API
- Каналы: Создание, управление, поиск и модерация каналов
- Сообщения: Создание, обновление, закрепление, поиск сообщений и тредов
- Пользователи: Управление пользователями, профили, аутентификация
- Команды: Создание команд, управление участниками и администрирование
- Файлы: Загрузка, скачивание и управление вложениями
- Эмодзи: Управление эмодзи
- Вебхуки: Управление вебхуками
- Боты: Управление ботами и автоматизация
- Плейбуки: Управление плейбуками и запусками
- Группы: Управление группами
- Администрирование: Системное администрирование и аналитика
- И многое другое...
🧪 Надёжность
- Комплексное покрытие unit-тестами (порог 90%+)
- Интеграционные тесты с реальным API
- Fuzz-тестирование для пограничных случаев
Установка
npm install loop-clientИли используя pnpm:
pnpm add loop-clientИли используя yarn:
yarn add loop-clientИли используя bun:
bun add loop-clientБыстрый старт
Базовое использование
import { LoopClient } from 'loop-client';
// Инициализация
const client = new LoopClient('https://your-loop-server.loop.ru', {
token: 'your-api-token',
logLevel: 'info'
});
// Получение текущего пользователя
const me = await client.users.profile.get.me();
console.log(`Привет, ${me.data.username}!`);
// Создание сообщения
const post = await client.posts.create({
channel_id: 'channel-id-here',
message: 'Привет от loop-client!'
});
// Поиск каналов
const channels = await client.channels.search.all({
term: 'general'
});
// Не забудьте очистить ресурсы по завершении
client.destroy();Расширенная конфигурация
import { LoopClient, fiveRetriesInFiveMinutes } from 'loop-client';
const client = new LoopClient('https://your-loop-server.loop.ru', {
// Аутентификация
token: 'your-api-token',
userID: 'me', // Опционально: ID текущего пользователя (вычисляется автоматически, если не передан)
// Автоматическое поведение
useCurrentUserForDirectChannels: true, // Автоматически подставлять ID текущего пользователя для создания ЛС
useCurrentUserForPostCreation: true, // Автоматически определять channel_id или user_id при создании поста
saveFetchedUserID: false, // Кешировать полученный ID пользователя для будущих запросов
// Логирование
logLevel: 'debug',
logger: customLogger, // Опционально: свой логгер
// Настройка производительности
maxRequestConcurrency: 50, // Ограничение параллельных запросов
retryConfig: fiveRetriesInFiveMinutes, // Политика повторов
// Настройка сети
timeout: 30000, // Таймаут запроса в мс
headers: {
'X-Custom-Header': 'value'
},
agent: new https.Agent({ keepAlive: true }), // Пользовательский HTTP агент (например, для прокси)
requestInterceptor: (config) => config, // Перехватчик запросов
adapter: customAdapter, // Пользовательский адаптер для Axios
// Параметры TLS/SSL
tls: {
rejectUnauthorized: false // Для самоподписанных сертификатов
},
// Тестирование
testConnectionOnInit: true // Проверка соединения при инициализации
});Справочник API
Клиент предоставляет комплексный API, организованный по типам ресурсов. Все методы возвращают Promise с типизированными результатами.
Основные ресурсы
Пользователи
// Получить пользователя по ID
await client.users.profile.get.byId({ user_id: 'user-id' });
// Поиск пользователей
await client.users.search({ term: 'john' });
// Обновить роли пользователя
await client.users.updateRoles({
user_id: 'user-id',
roles: 'system_user system_admin'
});
// Установить стандартный статус пользователя
await client.users.status.set({
user_id: 'me',
status: 'away'
});
// Установить пользовательский статус
await client.users.status.setCustom({
emoji: '🚀',
text: 'Работаю над чем-то крутым'
});Каналы
// Создать обычный канал
await client.channels.create.regular({
team_id: 'team-id',
name: 'my-channel',
display_name: 'Мой канал',
type: 'O' // 'O' для открытого, 'P' для приватного
});
// Создать канал личных сообщений
await client.channels.create.direct(['user-id-1', 'user-id-2']);
// Получить канал по имени
await client.channels.get.byName({
team_id: 'team-id',
channel_name: 'general'
});
// Добавить участника в канал
await client.channels.members.add({
channel_id: 'channel-id',
user_id: 'user-id'
});
// Поиск каналов
await client.channels.search.all({ term: 'маркетинг' });Сообщения
// Создать сообщение
await client.posts.create({
channel_id: 'channel-id',
message: 'Привет, мир!'
});
// Создать сообщение с вложенными файлами
await client.posts.create({
channel_id: 'channel-id',
message: 'Посмотрите этот файл',
file_ids: ['file-id-1', 'file-id-2']
});
// Получить сообщения для канала
await client.posts.getForChannel({
channel_id: 'channel-id',
page: 0,
per_page: 60
});
// Поиск сообщений
await client.posts.search({ terms: 'важная встреча' });
// Закрепить/открепить сообщение
await client.posts.pin({ post_id: 'post-id' });
await client.posts.unpin({ post_id: 'post-id' });
// Получить тред
await client.posts.getThread({ post_id: 'post-id' });Файлы
// Загрузить файл
const uploadResult = await client.files.upload({
channel_id: 'channel-id',
files: fileBuffer, // или Stream
filename: 'document.pdf'
}); // Примечание: реальная реализация может потребовать FormData или специфической обработки потоков в зависимости от окружения
// Получить метаданные файла
await client.files.get.metadata({ file_id: 'file-id' });
// Скачать файл
const fileData = await client.files.get.file({ file_id: 'file-id' });
// Поиск файлов
await client.files.search({ terms: 'отчёт', team_id: 'team-id' });Команды
// Создать команду
await client.teams.create({
name: 'my-team',
display_name: 'Моя команда',
type: 'O' // 'O' для открытой, 'I' для приватной
});
// Добавить участника в команду
await client.teams.members.add({
team_id: 'team-id',
user_id: 'user-id'
});
// Поиск команд
await client.teams.search({ term: 'разработка' });Дополнительные ресурсы
Клиент также предоставляет методы для:
- Эмодзи: Управление пользовательскими эмодзи (
client.emojis.*) - Реакции: Реакции на сообщения (
client.reactions.*) - Вебхуки: Входящие и исходящие вебхуки (
client.webhooks.*) - Боты: Управление ботами (
client.bots.*) - Настройки: Пользовательские настройки (
client.preferences.*) - Роли: Управление ролями и правами (
client.roles.*) - Группы: Синхронизация LDAP групп (
client.groups.*) - Плейбуки: Управление плейбуками (
client.playbooks.*) - Задачи: Управление фоновыми задачами (
client.jobs.*) - Система: Конфигурация системы и аналитика (
client.system.*) - OAuth: Управление OAuth приложениями (
client.oauth.*) - Соответствие: Отчёты о соответствии (
client.compliance.*) - И многое другое...
См. документацию Loop API для полного API.
Обработка ошибок
import { WebAPIRequestError, WebAPIServerError } from 'loop-client';
try {
await client.posts.create({
channel_id: 'invalid-channel-id',
message: 'Это не сработает'
});
} catch (error) {
if (error instanceof WebAPIRequestError) {
console.error('Ошибка запроса:', error.message);
console.error('Код статуса:', error.statusCode);
} else if (error instanceof WebAPIServerError) {
console.error('Ошибка сервера:', error.message);
console.error('ID ошибки сервера:', error.serverErrorId);
} else {
console.error('Неожиданная ошибка:', error);
}
}Политики повторных попыток
Клиент включает две встроенные политики повторных попыток:
import {
tenRetriesInAboutThirtyMinutes,
fiveRetriesInFiveMinutes
} from 'loop-client';
// По умолчанию: 10 попыток за ~30 минут
const client1 = new LoopClient(url, {
token,
retryConfig: tenRetriesInAboutThirtyMinutes
});
// Быстрее: 5 попыток за 5 минут
const client2 = new LoopClient(url, {
token,
retryConfig: fiveRetriesInFiveMinutes
});
// Своя политика повторов
const client3 = new LoopClient(url, {
token,
retryConfig: {
retries: 3,
factor: 2,
minTimeout: 1000,
maxTimeout: 60000,
random: true
}
});Разработка
Требования
- Node.js ≥20
- pnpm (рекомендуется) или npm
Настройка
# Клонировать репозиторий
git clone https://github.com/triple-sun/loop-client.git
cd loop-client
# Установить зависимости
pnpm install
# Собрать проект
pnpm buildСтруктура проекта
loop-client/
├── src/
│ ├── web-client.ts # Основной класс LoopClient
│ ├── methods.ts # Определения методов API (2900+ строк)
│ ├── errors.ts # Определения ошибок
│ ├── utils.ts # Вспомогательные функции
│ ├── const.ts # Константы и политики повторов
│ ├── logger.ts # Утилиты для логирования
│ ├── instrument.ts # Инструменты для определения метаданных
│ └── types/ # Типы
│ ├── methods/ # Типы аргументов методов
│ └── responses/ # Типы ответов
├── test/
│ ├── integration/ # Интеграционные тесты
│ ├── fuzzing/ # Fuzz-тесты
│ └── *.test.ts # Unit-тесты
├── build/ # Скомпилированный вывод
└── coverage/ # Отчёты о покрытии тестамиДоступные скрипты
# Запустить все тесты
pnpm test
# Запустить тесты с покрытием
pnpm test
# Запустить интеграционные тесты с реальным API (требуется .env.test.local)
pnpm test:real:only
# Собрать проект
pnpm build
# Проверка и форматирование кода
pnpm check
# Только форматирование
pnpm format
# Только проверка lint
pnpm lintТестирование
Проект имеет комплексное покрытие тестами:
# Запустить unit-тесты
pnpm test
# Запустить интеграционные тесты против реального API
# Создайте .env.test.local с:
# LOOP_URL=https://your-loop-server.com
# LOOP_TOKEN=your-test-token
pnpm test:real:onlyСтруктура тестов:
- Unit-тесты: Тесты с моками для основной функциональности
- Интеграционные тесты: Тесты против реального Loop API
- Fuzz-тесты: Тестирование граничных случаев и случайного ввода
- Требование покрытия: 90% строк и выражений
Качество кода
В проекте используется:
- TypeScript 5.9 со строгим режимом
- Biome для линтинга и форматирования
- Husky для git-хуков
- lint-staged для проверок перед коммитом
Конфигурация:
- Строгие настройки TypeScript в
tsconfig.json - Конфигурация Biome в
biome.json - Конфигурация Jest в
jest.config.ts
Переменные окружения
Для тестирования против реального Loop сервера создайте файл .env.test.local:
LOOP_URL=https://your-loop-server.com
LOOP_TOKEN=your-personal-access-tokenУчастие в разработке
Участие в разработке приветствуется! Пожалуйста:
- Форкните репозиторий
- Создайте ветку для новой функции (
git checkout -b feature/amazing-feature) - Внесите изменения, следуя стандартам качества кода
- Запустите тесты (
pnpm test) - Закоммитьте изменения (
git commit -m 'Добавлена крутая функция') - Отправьте в ветку (
git push origin feature/amazing-feature) - Откройте Pull Request
Стандарты кодирования
- Следуйте существующей конфигурации TypeScript и Biome
- Поддерживайте покрытие тестами выше 90%
- Добавляйте тесты для новых функций
- Обновляйте документацию по необходимости
- Используйте общепринятые сообщения коммитов
Лицензия
ISC License - см. package.json для деталей
Ссылки
- Репозиторий: https://github.com/triple-sun/loop-client
- Вопросы: https://github.com/triple-sun/loop-client/issues
- Документация Loop API: https://developers.loop.ru/API/4.0.0/loop-api-reference
- Документация Mattermost API: https://developers.mattermost.com/api-documentation
Благодарности
- Вдохновлён Slack Web API клиентом
- Создан для совместимости с Loop и Mattermost APIs
- Использует breadline-ts для очереди запросов
- Использует again-ts для логики повторов
Сделано с ❤️ triple-sun
