Серверные скрипты

Облачный Pyrus

Серверный скрипт — это бот и привязанный к нему программный код, который исполняется на стороне Pyrus при получении комментария ботом.

В отличие от обычных ботов, серверные скрипты не требуют собственного сервера и публичного https-адреса для приложения-обработчика, и позволяют настроить и запустить бота через приложение Pyrus с минимальным количеством кода.

Ограничения и лимиты

  • Язык TypeScript;
  • Среда исполнения кода Node.js;
  • Доступна стандартная библиотека Node.js;
  • Доступна предустановленная библиотека pyrus-api;
  • Доступны сетевые запросы (как к Pyrus API, так и внешним сервисам);
  • Без возможности подключения сторонних библиотек (пока);
  • Без возможности записи файлов на диск;
  • Без сохранения состояния между запусками (stateless);
  • Максимальное время исполнения скрипта — 60 секунд для платного тарифа, 1 секунда для бесплатного тарифа;
  • Максимальное количество запусков — 10 раз за 10 секунд;
  • Максимальное использование оперативной памяти — 256 Мб.

Создание бота с серверным скриптом

  1. Перейдите в раздел Боты, а затем нажмите Добавить бота в верхней панели справа.

  2. В открывшемся окне введите название бота и выберите тип бота Серверный скрипт и нажмите Добавить.

    Откроется редактор серверного скрипта с примером кода.

  3. Вы можете написать собственный скрипт или вставить готовый пример. После сохранения изменений бот готов к работе.

В дальнейшем редактор скрипта будет доступен из профиля бота:

Структура кода

В коде скрипта необходимо объявить функцию-обработчик и экспортировать её по умолчанию (export default). Функция принимает на вход объект аналогичный телу запроса к обычному боту, включающий задачу, из которой был вызван бот, его настройки и токен авторизации для запросов в Public API.

Пример простейшего скрипта:

import {BotHookRequest, BotHookResponse} from "pyrus-api";

export default async function(
    request: BotHookRequest
): Promise<BotHookResponse> {
  return {
    text: `Hello ${request.task.author.first_name}!`
  };
}

В этом примере бот поприветствует автора задачи по имени.

Возвращаемые значения

Функция должна вернуть результат либо Promise с результатом. В качестве результата функции может быть пустое значение (null/undefined) либо комментарий к задаче — объект comment.

Если функция выполняет асинхронные операции, такие как запросы к Pyrus API или внешним сервисам, то необходимо вернуть Promise. Это можно сделать в явном виде:

import {BotHookRequest, BotHookResponse} from "pyrus-api";

export default function(request: BotHookRequest): BotHookResponse {
    return Promise(resolve => {
        functionWithCallback(() => resolve());
    });
}

но рекомендуется использовать синтаксис async/await:

import {BotHookRequest, BotHookResponse} from "pyrus-api";

export default async function(
    request: BotHookRequest
): Promise<BotHookResponse> {
    await doSomethingAsync();
}

Важно: выполнение скрипта будет прервано после того, как функция вернёт результат. Незавершенные асинхронные операции при этом тоже прервутся.

import {BotHookRequest, BotHookResponse} from "pyrus-api";

export default async function(
    request: BotHookRequest
): Promise<BotHookResponse> {
    doSomethingAsync();
  //↑ операция будет прервана
  //  после возврата из родительской функции
}

Автодополнения и проверка типов

В коде скриптов доступен синтаксис и типы языка TypeScript. Это не влияет на исполнение скрипта, но поможет легче писать код и избежать опечаток. Для аргументов и возвращаемого значения функции-обработчика есть типы BotHookRequest и BotHookResponse, доступные из предустановленной библиотеки pyrus-api: Вы можете использовать чистый JavaScript (без объявления типов), но в этом случае проверки и автодополнения при написании кода будут недоступны.

Примеры скриптов

Сброс последующих согласований

Присутствующий в наблюдателях задачи бот сбросит все согласования на последующих этапах, если такие есть. Это полезно тогда, когда необходимо заново пройти всю цепочку согласований при перезапросе согласования на одном из предыдущих этапов.

import {BotHookRequest, BotHookResponse} from "pyrus-api";

export default function({task}: BotHookRequest): BotHookResponse {
	const reapproval: number[][] = [];
	let approveFound = false;

	for (let i = 0; i < task.approvals.length; i++) {
		const stepReapproval = [];
		if (task.current_step < i + 1) {
			for (const approval of task.approvals[i]) {
				if (approval.approval_choice !== "waiting") {
					stepReapproval.push(approval.person.id);
					approveFound = true;
				}
			}
		}
		reapproval.push(step_reapproval)
	}

	if (approveFound)
		return {
			approvals_rerequested: reapproval
		};
}

Объявление для участников роли

Если упомянуть в одном комментарии одновременно бота и одну или несколько ролей, скрипт добавит в наблюдатели всех участников этих ролей. Это полезно в случаях, когда требуется уведомить о чем-то группу коллег и получить от каждого визу об ознакомлении с уведомлением.

import {BotHookRequest, BotHookResponse, PyrusApiClient} from "pyrus-api";

export default async function({
    task,
    user_id,
    access_token
}: BotHookRequest): Promise<BotHookResponse> {
    const lastComment = task.comments[task.comments.length - 1];
    const mentions = new Set(lastComment.mentions);

    if (!mentions.has(user_id))
        return;

    const roles = task.subscribers
        ?.filter(p =>
            p.person.type === "role" &&
            mentions.has(p.person.id)
        )
        .map(r => r.person.id);

    if (!roles?.length)
        return;

    const api = new PyrusApiClient(access_token);

    const membersArrays = await Promise.all(
        roles.map(id => api.role.get({id}).then(r => r.member_ids))
    );

    return {
        subscribers_added: membersArrays.flat().map(id => ({id})),
        subscribers_removed: [user_id, ...roles].map(id => ({id}))
    };
}

Была ли эта статья полезной?