const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); // Адрес сервера данного приложения (для доступа из Интернета) const SERVER_URL = ''; // Адрес сервера Loop const LOOP_URL = ''; // Порт на котором будет слушать наш сервер const PORT = 4000; // Описание первой формы const form1 = { title: 'Первая форма', icon: 'icon.png', // Иконку для каждой формы можно задать свою fields: [ { type: 'text', name: 'message', modal_label: 'Тут какое-то поле', position: 1, is_required: true, }, ], submit: { // Куда отправлять запрос после нажатия кнопки "Отправить" path: '/second_modal', // Контекст для запроса expand: { // Запрашиваем данные пользователя который вызвал действие acting_user: "all", // запрашиваем канал в котором было вызвано действие channel: "all", } }, } // Описание второй формы const form2 = { title: 'Вторая форма', icon: 'icon.png', fields: [ { type: 'text', name: 'message2', modal_label: 'Еще одно поле', position: 1, }, ], submit: { // Куда отправлять запрос после нажатия кнопки "Отправить" path: '/second_result', expand: { // Запрашиваем данные пользователя который вызвал действие acting_user: "all", // запрашиваем канал в котором было вызвано действие channel: "all", } }, } // Отправка сообщения в канал Loop const createPost = async (bot_access_token, channel_id, message) => { try { await fetch(`${LOOP_URL}/api/v4/posts`, { method: 'POST', headers: { Authorization: 'Bearer ' + bot_access_token, }, body: JSON.stringify({ channel_id, message, }) }); } catch (e) { console.error('Error when create post', e); } } // Обработчик результата первой формы app.post('/second_modal', (req, res) => { const context = req.body.context; // Отправляем данные формы в канал createPost( context.bot_access_token, context.channel.id, '### Заполнена форма 1\n```json\n' + JSON.stringify(req.body.values, null, 2) + '\n```', ); // Отвечаем второй формой. Обязательно статус 200 (не 201 и тд) res.status(200).json({ type: 'form', form: form2, }) }); // Обработчик результата второй формы app.post('/second_result', (req, res) => { const context = req.body.context; // Отправляем результат в канал createPost( context.bot_access_token, context.channel.id, '### Заполнена форма 2\n```json\n' + JSON.stringify(req.body.values, null, 2) + '\n```', ); // Отвечаем что все хорошо. Обязательно статус 200 (не 201 и тд) res.status(200).json({ type: 'ok', }); }) // Данный метод будет вызываться при открытии любого канала, любым пользователем // Отвечает за отрисовку кнопки в сайдбаре app.post('/bindings', (req, res) => { res.status(200).json({ type: 'ok', data: [ { // Место где показать кнопку location: '/channel_header', // Описание кнопки bindings: [ { // идентификатор, который будет отправлен в запросе location: 'send-button', // Иконка кнопки icon: 'icon.png', // Подпись для кнопки label: 'Открыть форму', // Первая форма form: form1, } ] }, ] }) }) // Манифест для установки приложения app.get('/manifest.json', (req, res) => { res.json({ app_id: 'ru.loop.dialog-sequence-example', version: '0.1.0', display_name: 'Loop Dialog Sequence Example', description: 'An app that opens modal dialogs sequentially', icon: 'icon.png', homepage_url: 'https://git.wilix.dev/loop/integration-examples', // Разрешения для приложения requested_permissions: [ // разрешение, чтобы приложение могло писать от имени бота 'act_as_bot', // разрешение, чтобы получать данные пользователя, который вызывает действия 'act_as_user' ], // Разрешения для установки кнопок или команд requested_locations: [ // Устанавливает кнопку во всех каналах в сайдбаре или в заголовке канала // (расположение завит от того, включен ли сайдбар) '/channel_header' ], app_type: 'http', http: { root_url: SERVER_URL } }) }) // Сервим иконки из папки. Все иконки всегда будут искаться по пути /static app.use('/static', express.static('./static')); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });