SMeet Mini Apps
Примеры
Каждый пример представляет собой законченное мини-приложение со страницей, сервером и ботом. Скачайте его, дайте ему ключ своего мини-приложения и токен своего бота и запустите за своим origin с HTTPS.
Три примера#
| Пример | Что показывает | На чём работает | Архив |
|---|---|---|---|
| Анкета | вход без cookie, черновик на устройстве, submitToBot() с подтверждением SMeet, ответ бота в чате | Node.js 22.13 или новее, без пакетов | smeet-mini-apps-forms.zip |
| Служба заявок | проверка сессии перед каждой операцией, безопасная отдача файлов, outbox уведомлений в чат, операторы | Node.js 22.13 или новее и PostgreSQL 11 или новее | smeet-mini-apps-desk.zip |
| Минимальный Python | самый короткий сервер: вход, проверка сессии, ответ бота | Python 3.9 или новее, только стандартная библиотека | smeet-mini-apps-python-minimal.zip |
У каждого примера есть README на русском и английском со всеми настройками, .env.example и тесты.
Анкета#
Анкета, которую человек заполняет в окне SMeet и отправляет боту.
- Страница подключает SDK в
<head>и вызываетconnect(),ready()и вход вwindow.onload. - Вход: одноразовая попытка от сервера,
authorize(), обмен на сервере сIdempotency-Key: login:<attempt id>, собственная сессия сервера в памяти страницы. - Черновик лежит в
localStorageпод ключом со средой и идентификатором человека и несёт свойrequestId: повторная отправка никогда не создаёт вторую анкету. - Рабочий процесс заодно единственный получатель событий бота. Он раскладывает каждое
mini_app_submissionпо егоenvironment, подтверждает позицию только после записи и отвечает в чате с ревизией из события.
Его сервер гасит попытку входа и обменивает код:
export async function signIn({ body, attempts, sessions, authorApi, environment }) {
const attemptId = field(body, 'attempt_id', { pattern: ATTEMPT_RE, max: 64 });
const attemptSecret = field(body, 'attempt_secret', { max: 128 });
const code = field(body, 'code', { max: 512 });
const codeVerifier = field(body, 'code_verifier', { pattern: VERIFIER_RE, max: 128 });
if (!attempts.consume(attemptId, attemptSecret)) {
throw new HttpError(400, 'LOGIN_RETRY', 'The sign-in attempt is unknown, used or expired', { reason: 'attempt' });
}
let exchanged;
try {
exchanged = await authorApi.exchange({ code, codeVerifier, idempotencyKey: `login:${attemptId}` });
} catch (error) {
throw pageError(error);
}
if (exchanged.environment !== environment) {
// SMeet already refuses a code of the other environment with ENVIRONMENT_MISMATCH (6.3); never trust it twice.
throw new HttpError(500, 'SERVER_MISCONFIGURED', 'The session belongs to another environment');
}
const own = sessions.create(exchanged);
return {
session_token: own.token,
expires_at: new Date(own.expiresAt).toISOString(),
environment: exchanged.environment,
user: {
id: exchanged.user?.id,
display_name: exchanged.user?.display_name ?? null,
language_code: exchanged.user?.language_code ?? 'en',
},
start_param: exchanged.start_param ?? null,
};
}Служба заявок#
Служба заявок организации: человек видит свои заявки и создаёт новую с файлом; операторы видят все заявки пространства и меняют их состояние.
checkSessionбез кеша перед чтением заявок, сохранением черновика, загрузкой и скачиванием файла, созданием заявки и сменой состояния.- Черновик и его файл хранятся на сервере по приложению, среде, пространству и человеку; при повторном открытии окна файл уже на месте.
- Заявка создаётся под
Idempotency-Keyсвоего черновика, и та же транзакция записывает задание outbox сexpected_access_revisionпроверки этого запроса (Outbox). - Воркер отправляет уведомление текстом или документом с файлом заявки, с ключом
ntf:<notification id>. - Смена состояния оператором ничего не пишет в чат; человек видит её в приложении.
- Файл отдаётся только как вложение типа
application/octet-streamсnosniffиsandbox; в окне web человек получает копию в чате (Файлы).
Минимальный Python#
Самое короткое мини-приложение со своим сервером, в одном файле server.py: страница, SDK как /sdk.js, файл подтверждения домена /.well-known/smeet-mini-app.txt, попытка входа, обмен, checkSession перед каждой операцией и sendMessage бота с ревизией этой проверки. Страница page.js вызывает ready() и authorize() в window.onload.
def checked(self, token: Optional[str]) -> Tuple[Dict[str, Any], Dict[str, Any]]:
"""checkSession without a cache before every protected operation (6.5)."""
with self.lock:
session = self.sessions.get(token or "")
if not session or session["expires"] <= self.clock():
raise PageError(401, "SESSION_EXPIRED")
try:
check = self.author("checkSession", {"session_token": session["smeet"]})
except SMeetError as error:
raise page_error(error)
if not check.get("active") or (check.get("user") or {}).get("id") != session["user"].get("id"):
with self.lock:
self.sessions.pop(token, None)
raise PageError(401, "SESSION_INACTIVE", reason=check.get("reason"))
return session, checkЧто лежит в архиве#
Архив повторяет расположение исходников документации, поэтому пример находит SDK там, где ожидает:
smeet-mini-apps-forms/
examples/.gitignore данные, сборки и пакеты не попадают в git
examples/forms/ пример: README.md, .env.example, package.json, сервер, lib, web, test
sdk/smeet-mini-apps-1.0.0.js SDK, который пример встраивает в свою страницу, с .esm.js и объявлениями .d.ts- Архив рассчитан на ваш собственный origin: базовый путь
/, а заголовокframe-ancestors https://messenger.scrile.comсервер ставит сам, беря его изSMEET_URL. - В нём нет
.env, данных и установленных пакетов. Служба заявок ставит свой единственный пакет, клиент PostgreSQL, черезnpm ciпо своемуpackage-lock.json. - Тот же код запускает собственные демо SMeet под
https://messenger.scrile.com/mini-apps/; отличается только окружение. Это размещение в архив не входит.
Запуск с SMeet#
- Один процесс на среду, у каждого ключ своей среды в
MINI_APP_KEYи свой origin. Тестовая и рабочая версии никогда не делят процесс. - У бота ровно один получатель событий: рабочий процесс (
BOT_MODE=pollingилиwebhook), который раскладывает каждую отправку по еёenvironment. Тестовый процесс работает сBOT_MODE=off; пока рабочего процесса нет, события может получать тестовый. SMEET_URLравенhttps://messenger.scrile.com. Минимальному примеру на Python нужен ещёMINI_APP_VERIFICATION_TOKEN, токен файла/.well-known/smeet-mini-app.txt.- Тесты примера запускаются через
npm testилиpython3 -m unittest test_server.py; SMeet для них не нужен.