Sequence dialog example

This commit is contained in:
dmitry 2024-06-17 23:01:33 +03:00
parent 1e04cb169e
commit 3371759544
9 changed files with 230 additions and 16 deletions

View File

@ -11,3 +11,6 @@
#!.yarn/cache
.pnp.*
node_modules
.idea

33
dialog-sequence/README.md Normal file
View File

@ -0,0 +1,33 @@
# Loop Dialog Sequence Example
Пример приложения для последовательного открытия модальных окон с формой.
### Запуск и установка
В файле index.js меняем переменные:
- SERVER_URL - адрес вашего сервера Loop
- LOOP_URL - адрес приложения
- PORT - по желанию
Устанавливаем зависимости `yarn` или `npm install`
Запускаем командой `yarn start` или `npm start`
Переходим в Loop и устанавливаем приложение через слеш команду в любом канале:
`/apps install http _SERVER_URL_/manifest.json`
в появившемся окне ставим галочку, что мы все понимаем и подтверждаем.
После установки должна появиться новая кнопка на правой панели или в верхней панели канала.
- Переходим в Loop и создаем тестовый канал
- Приглашаем бота ru.loop.dialog-sequence-example в команду
- Добавляем бота ru.loop.dialog-sequence-example в тестовый канал
- Нажимаем на новую кнопку "Открыть форму" (с иконкой Мистфикса)
- Заполняем форму и нажимаем отправить
- Если все хорошо, в канал придет сообщение от бота и откроется следующая форма
- Enjoy
#### Важные замечания по работе с apps framework
- Все ответы должны быть с кодом 200 (не 201 и тд.)
- Для получения данных о канале требуется разрешение act_as_user, если его убрать то контекст канала приходить не будет
- Все иконки описываются без указания пути, Loop всегда будет их искать по пути `_SERVER_URL_/static/`
- Без основной иконки (указанной в манифесте) приложение не установится

BIN
dialog-sequence/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

178
dialog-sequence/index.js Normal file
View File

@ -0,0 +1,178 @@
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}`);
});

View File

@ -0,0 +1,16 @@
{
"name": "loop-dialog-sequence-example",
"version": "0.1.0",
"description": "Example App for dialog sequence",
"scripts": {
"start": "node ./index.js"
},
"engines": {
"node": "21"
},
"license": "MIT",
"dependencies": {
"body-parser": "^1.20.2",
"express": "4.17.1"
}
}

View File

@ -1,4 +0,0 @@
{
"name": "modal-pages-test",
"packageManager": "yarn@4.1.1"
}

View File

@ -1,12 +0,0 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10c0
"modal-pages-test@workspace:.":
version: 0.0.0-use.local
resolution: "modal-pages-test@workspace:."
languageName: unknown
linkType: soft