Skip to content

2.0.0

Choose a tag to compare

@NiktarioN NiktarioN released this 29 Jul 18:13
· 42 commits to master since this release

Изменения и улучшения

Типы пакета теперь импортируются по имени, ошибки ловятся через instanceof, добавлены методы звонков и HelpDesk, вложения в сообщениях и тела всех событий вебхуков

Релиз мажорный: 16 методов переименованы, изменился CJS-импорт и поднялась минимальная версия Node. Ниже — что менять в коде, с примерами

🚀 Как обновиться

npm i getcourse-api@2

Нужен Node 22 или новее — проверьте свою версию:

node -v

⚠️ Ломающие изменения

Подключение через require

// было
const GetCourse = require("getcourse-api");

// стало
const { GetCourse } = require("getcourse-api");

Пакет собирается с именованными экспортами, поэтому require отдаёт объект, а не сам класс. Оттуда же достаются классы ошибок:

const { GetCourse, GetCourseApiError } = require("getcourse-api");

ESM-импорт не меняется ни в JavaScript, ни в TypeScript:

import GetCourse from "getcourse-api";

Node 18 и 20 больше не поддерживаются

engines.node поднят с >=18 до >=22. Обе версии вышли из поддержки Node.js, пакет на них не проверяется. На Node 22 и новее ничего делать не нужно

Переименованные методы

Имена приведены к одной схеме — глагол, сущность, объект. Тела запросов и ответы не изменились, правится только имя вызова

Было Стало
setUri subscribeWebhook
addCommentToDeal addDealComment
addCommentToUser addUserComment
addCommentToLessonAnswer addLessonAnswerComment
addCommentToWebinar sendWebinarMessage
addCommentToDialog sendDialogMessage
addNote addDialogNote
changeDepartment changeDialogDepartment
changeStatusAnswers changeLessonAnswerStatus
getAllGroups getGroups
getAllPersonalManagers getPersonalManagers
getAllWebinars getWebinars
getDealsTags getDealsWithTags
getOffersTags getOffersWithTags
getUserAnswers getUserSurveyAnswers
moderateWebinarComment moderateWebinarMessage

Переименованные типы ничего не ломают

В 1.x пакет отдавал наружу только класс — точка входа состояла из одной строки export default class GetCourse, и ни один тип по имени импортировать было нельзя. Поэтому переименования Diploma → UserDiploma, CancelReason → DealCancelReason, UpdateCustomField → UpdatedCustomField, DialogTransport → MessageTransport, SetUriRequest → SubscribeWebhookRequest, CustomField → LegacyCustomField ничей код не задевают

Начиная с этой версии имена зафиксированы и меняются только мажором

✨ Что нового

🆕 Новые методы

Восемь методов, которых не было в 1.x. Подробности с примерами — в разделах ниже

Метод Что делает
addCallComment(body) Комментарий к звонку — попадает в поле «Описание»
addCallTranscription(body) Транскрибация звонка, отображается как HTML
getTicketHistory(body) История переписки тикета HelpDesk
sendTicketMessage(body) Сообщение в тикет от имени сотрудника
changeTicketDepartment(body) Перевод тикета в другой отдел
closeTicket(body) Закрытие тикета с причиной из TicketCloseReason
getDepartments() Отделы поддержки проекта
unsubscribeWebhook(event) Отписка от события — тот же set-uri с subscribe: 0

🧩 Типы импортируются по имени

Наружу выведено 149 типов — модели, тела запросов, тела вебхуков, legacy, справочники:

import GetCourse, { GetCourseApiError } from "getcourse-api";

import type { CallWebhook, Deal, GetCourseConfig, User } from "getcourse-api";

Отдельных путей вроде getcourse-api/types нет — всё лежит в корне пакета. В чистом JavaScript типы доступны через JSDoc:

/** @param {import("getcourse-api").User} user */
function getTelegramId(user) {
  return user.bot_link.telegram[0]?.tg_user_id;
}

CJS-типы тоже корректны: require в TypeScript-проекте видит те же имена

🚨 Ошибки различаются через instanceof

import { GetCourseApiError, GetCourseNetworkError, GetCourseValidationError } from "getcourse-api";

try {
  await gc.getDealFields(99999);
} catch (err) {
  if (err instanceof GetCourseValidationError) {
    console.error(err.details); // причина с машиночитаемым кодом, запроса к API не было
  }

  if (err instanceof GetCourseApiError) {
    console.error(err.statusCode, err.apiCode, err.errors);
  }

  if (err instanceof GetCourseNetworkError) {
    console.error(err.cause); // таймаут, DNS, connection refused
  }
}

Общий предок всех трёх — GetCourseError, им ловится любая ошибка SDK разом. Новый класс здесь GetCourseValidationError: он означает, что данные не прошли проверку на стороне пакета и запрос вообще не выполнялся

📞 Звонки

await gc.addCallComment({ callId: 8421, text: "Клиент просил перезвонить в пятницу" });

await gc.addCallTranscription({
  callId: 8421,
  text: "<p>— Здравствуйте! Вам удобно говорить?</p><p>— Да, слушаю вас</p>",
});

Комментарий попадает в поле «Описание» и принимает обычный текст, транскрибация отображается как HTML — переносы делаются через <p> или <br>. Оба метода перезаписывают значение при повторном вызове

У транскрибации есть недокументированный предел длины: на превышении API отвечает 500 с пустым errors. Для русского текста безопасный ориентир — до 4000 символов

🎫 HelpDesk

await gc.sendTicketMessage({
  ticketId: 4471,
  commentText: "Разобрались, доступ восстановлен",
  transport: [1],
  userId: 903417,
});

await gc.closeTicket({ ticketId: 4471, closedReason: 2, closedComment: "Вопрос решён" });

Плюс getTicketHistory и changeTicketDepartment. Причины закрытия — справочник TicketCloseReason. HelpDesk и «Входящие» — два параллельных функционала платформы, тела их вебхуков совпадают, поэтому подписки должны идти на разные URI

📎 Вложения в сообщениях

import { readFile } from "node:fs/promises";

await gc.sendDialogMessage({
  dialogId: 48812,
  commentText: "Счёт во вложении",
  transport: [1],
  userId: 903417,
  attachedFiles: [{ filename: "Счёт №1024.pdf", content: await readFile("./invoice.pdf") }],
});

До 5 файлов по 5 МБ, байты принимаются как Buffer, Uint8Array или Blob. Лимиты проверяются до отправки — при превышении пакет бросит GetCourseValidationError, не заливая файлы на сервер. Расширение в filename обязательно: GetCourse определяет тип файла только по нему. Без вложений запрос уходит прежним JSON

📋 Анкеты

getUserSurveyAnswers возвращает SurveyAnswer[]: анкета, её поля и ответы, где ключ — id поля строкой. Поле — дискриминированный union по type, у каждого варианта свой набор settings:

import type { SurveyAnswer, SurveyField } from "getcourse-api";

function describeOptions(field: SurveyField): string[] {
  // после сужения по type доступен valueList — у остальных полей его нет
  return field.type === "select" ? field.settings.valueList.split("\n") : [];
}

function readAnswer(answer: SurveyAnswer, field: SurveyField): string | undefined {
  return answer.values[String(field.id)];
}

🔔 Вебхуки

Типизированы тела всех четырнадцати событий: заказы, диалоги, ответы на уроки, комментарии к ответам, комментарии вебинаров, звонки, HelpDesk

import type { DealPaidWebhook } from "getcourse-api";

app.post("/gc/deals", (req, res) => {
  const event = req.body as DealPaidWebhook;

  console.log(event.deal.number, event.deal.payedValue);
  res.sendStatus(200);
});

Добавились события подписки: сообщение от сотрудника (1.4), HelpDesk (9.1–9.3) и метод unsubscribeWebhook для отписки

Реальные примеры тел вебхуков с обезличенными данными лежат здесь: examples/webhooks

🆘 Если что-то пошло не так

Нашли расхождение с реальным поведением API или не хватает метода — заводите issue. Типы в пакете строятся на живых ответах API, а не на схеме, поэтому такие сообщения ценны

Откатиться на прежнюю версию всегда можно командой npm i getcourse-api@1.2.0