Sequence dialog example
This commit is contained in:
parent
1e04cb169e
commit
3371759544
|
@ -11,3 +11,6 @@
|
||||||
|
|
||||||
#!.yarn/cache
|
#!.yarn/cache
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.idea
|
|
@ -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/`
|
||||||
|
- Без основной иконки (указанной в манифесте) приложение не установится
|
Binary file not shown.
After Width: | Height: | Size: 3.1 MiB |
|
@ -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}`);
|
||||||
|
});
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"name": "modal-pages-test",
|
|
||||||
"packageManager": "yarn@4.1.1"
|
|
||||||
}
|
|
12
yarn.lock
12
yarn.lock
|
@ -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
|
|
Loading…
Reference in New Issue