PartyFlow

Guide: Создать бота и отправить первое сообщение#

Пошаговый гайд для разработчиков, которые пишут своего первого бота для PartyFlow: получить токен, вступить в канал, проверить self-check, отправить сообщение. Весь флоу — 5-10 минут.

Для углублённого tour по архитектуре ботов — concepts/bots.md. Полный справочник endpoints — reference/bot-rest-api.md.


Что понадобится#

  • Доступ к workspace (space) в PartyFlow с правами admin'а — нужны для создания бота.
  • ID канала, в который бот будет писать — скопируйте из URL или из Channel settings → About.
  • Secrets manager (Vault, AWS Secrets Manager, 1Password для dev). Bot token показывается один раз и его нельзя восстановить — только перевыпустить.

Базовый URL в примерах — $BASE (адрес вашей инсталляции PartyFlow, например https://api.partyflow.ru).


Шаг 1. Создать бота в UI#

Space → IntegrationsBotsNew bot:

Поле Пример Заметки
Display name Standup Bot Имя, которое видят участники канала. 1..64 символа.
Avatar URL https://cdn.example.com/bot.png HTTPS only, опционально.

Нажмите Create. Появится диалог с bot token — строка вида fri_bot_<base64url>.

Сразу сохраните токен в secrets manager. После закрытия диалога его нельзя посмотреть снова — только Regenerate token, что инвалидирует старый.

# Сохраните в окружение для последующих шагов
export TOKEN='fri_bot_...'
export BASE='https://api.partyflow.ru'
export CHANNEL_ID='<uuid-канала>'

Шаг 2. Добавить бота в канал#

Бот должен быть участником канала, чтобы слать туда сообщения. Два способа:

Способ A. Через UI (ручной)#

В канале → Channel settingsMembersAdd bot → выбрать вашего бота → Add.

Способ B. Через REST (programmatic, идемпотентно)#

curl -X POST "$BASE/api/v1/bot/conversations/$CHANNEL_ID/join" \
  -H "Authorization: Bearer $TOKEN"

Ожидаемый ответ:

{"ok": true}

Этот endpoint идемпотентен — если бот уже в канале, вернётся тот же 200. Можно вызывать при каждом старте вашего сервиса — это частый паттерн для ботов, которые должны гарантировать своё присутствие в канале.

Ограничения:

  • Бот должен принадлежать тому же space, что и канал.
  • Для /join поддерживаются только групповые каналы. DM не требуют join: пользователь создаёт direct-чат с ботом в UI или бот вызывает POST /api/v1/bot/dm.

Шаг 3. Self-check через GET /api/v1/bot/me#

Проверьте, что токен активен и бот не отключён админом:

curl "$BASE/api/v1/bot/me" \
  -H "Authorization: Bearer $TOKEN"

Ответ:

{
  "ok": true,
  "bot": {
    "id": "<uuid>",
    "space_id": "<uuid>",
    "display_name": "Standup Bot",
    "avatar_url": "https://cdn.example.com/bot.png",
    "is_active": true,
    "event_types": ["MESSAGE_CREATED"],
    "channel_ids": [],
    "event_delivery_mode": "none",
    "webhook_url": ""
  }
}

Если /bot/me возвращает 401 — токен недействителен, перевыпущен или бот отключён админом. Если успешный ответ содержит "is_active": false, попросите админа включить бота обратно через UI.

Этот endpoint стоит дёшево — вызовите его при старте сервиса как health-check.


Шаг 4. Первое сообщение#

curl -X POST "$BASE/api/v1/bot/messages" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"conversation_id\":\"$CHANNEL_ID\",\"content\":\"Hello from my first bot!\"}"

Ожидаемый ответ:

{"ok": true, "message_id": "<uuid>"}

Если вы увидели это в канале — первое сообщение отправлено. Поздравляем.

Python-вариант#

import os
import requests
 
BASE = os.environ["BASE"]
TOKEN = os.environ["TOKEN"]
CHANNEL_ID = os.environ["CHANNEL_ID"]
 
session = requests.Session()
session.headers.update({"Authorization": f"Bearer {TOKEN}"})
 
# Self-check
me = session.get(f"{BASE}/api/v1/bot/me").json()
assert me["bot"]["is_active"], "bot is disabled by admin"
print(f"Bot ready: {me['bot']['display_name']}")
 
# Idempotent join (call on every startup)
session.post(f"{BASE}/api/v1/bot/conversations/{CHANNEL_ID}/join")
 
# Send message
resp = session.post(
    f"{BASE}/api/v1/bot/messages",
    json={
        "conversation_id": CHANNEL_ID,
        "content": "Hello from my first bot!",
    },
)
resp.raise_for_status()
print(resp.json())

Запускается без зависимостей, кроме requests. Для production замените session.post на обработку ошибок (см. ниже раздел Troubleshooting).


Шаг 5. DM и SSE Stream (опционально)#

Если ваш бот должен работать в личных переписках (например, AI-ассистент, helpdesk-бот, личный reminder), используйте DM + SSE Stream.

5a. Создать DM с пользователем#

curl -X POST "$BASE/api/v1/bot/dm" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"USER_ID"}'

Ответ:

{"ok": true, "conversation_id": "<uuid>"}

Сохраните conversation_id — через него бот будет отправлять сообщения в эту личку (POST /api/v1/bot/messages с этим conversation_id).

5b. Подключить SSE Stream#

curl -N "$BASE/api/v1/bot/stream" \
  -H "Authorization: Bearer $TOKEN"

Формат — text/event-stream. Каждое сообщение:

data: {"event":"message.new","message_id":"...","conversation_id":"...","author_id":"...","text":"...","msg_index":1042,"sent_at":"...","conversation_context":{"conversation_id":"...","type":"chat","scope":"dm","is_direct":true,"is_public":false}}
 

В SSE payload автор приходит как плоское поле author_id; вложенного author.user_id нет. Контекст беседы приходит в conversation_context: для DM это scope="dm", для публичных/приватных каналов в Poll/Webhook будет public или private, для Call Thread — type="thread" и subtype="call_thread".

Keep-alive ping (игнорируйте):

:ping
 

Обработка на вашей стороне:

import requests
 
resp = requests.get(
    f"{BASE}/api/v1/bot/stream",
    headers={"Authorization": f"Bearer {TOKEN}"},
    stream=True,
)
for line in resp.iter_lines():
    if not line:
        continue
    if line.startswith(b":"):
        continue  # ping
    if line.startswith(b"data: "):
        payload = json.loads(line[6:])
        if payload.get("event") == "message.new":
            ctx = payload["conversation_context"]
            print(f"New {ctx['scope']} message from {payload['author_id']}: {payload['text']}")

Reconnect: при обрыве соединения reconnect с exponential backoff (1с → 2с → 4с … max 30с). Backlog сообщений, пришедших пока бот был offline, доставляется автоматически при reconnect.


Шаг 6. Что дальше#

Базовый бот работает. Следующие расширения зависят от сценария:

  • Личные переписки (DM)reference/bot-rest-api.md → DM и SSE Stream. POST /api/v1/bot/dm + GET /api/v1/bot/stream.
  • Читать историю канала (для AI-summarizer'ов, индексеров) — reference/bot-rest-api.md → Чтение истории канала. Endpoint GET /api/v1/channels/{id}/messages.
  • Загрузить файл и прикрепить к сообщениюreference/bot-rest-api.md → Bot Files API. Создайте upload session, загрузите байты по short-lived URL, подтвердите файл и передайте attachments: [{"file_id": "..."}].
  • Долгие AI-ответыreference/bot-rest-api.md → Agent Runs. Создайте run, обновляйте sequence, завершайте completed с public_text и optional final files.
  • Task widgets для агентских задачreference/bot-rest-api.md → Execution Tasks. Используйте, когда пользователю нужен отдельный статус задачи, artifacts и review flow.
  • Кастомные slash-команды (/deploy, /standup) — reference/slash-commands.md. Бот регистрирует команду и получает invocation через HTTP callback или Poll API.
  • Интерактивные кнопки и модалки (approval flows, deploy-подтверждения) — guides/bot-interactive-quickstart.md для быстрого старта или reference/interactive-components.md для полного справочника. Доставка click/submit событий поддерживает callback и Poll API.
  • Аттачменты, блоки, severity-badges в сообщениях — reference/message-format.md. Отправляются через поле metadata_json в POST /api/v1/bot/messages.

Troubleshooting#

Симптом Причина Решение
401 invalid or revoked token Неправильный токен, токен перевыпущен, или бот отключён админом. Проверьте токен в secret manager и попросите админа подтвердить, что бот активен. Если /bot/me тоже возвращает 401, нужен новый токен или re-enable бота.
401 missing authorization header Забыли заголовок Authorization: Bearer .... Добавьте -H "Authorization: Bearer $TOKEN".
401 invalid authorization format Формат Authorization без префикса Bearer или с лишними пробелами. Используйте строго Authorization: Bearer fri_bot_....
403 / 400 failed to join conversation на /join Бот пытается вступить в канал из другого space, или беседа — DM, или канала нет. Проверьте: (1) канал существует, (2) тот же space, что и у бота, (3) это групповой канал. Для DM используйте POST /api/v1/bot/dm.
400 bot not in conversation на /messages Бот ещё не вступил в канал. Вызовите POST /api/v1/bot/conversations/{id}/join (Шаг 2).
400 content too long (max 4000 chars) Длина content > 4000 символов. Разбейте сообщение на несколько, либо используйте metadata_json с attachment для длинного блока.
400 conversation_id is required / content or attachments required Пустое или отсутствующее поле. Проверьте payload: нужен conversation_id и хотя бы content или один подтверждённый file attachment.
400 invalid JSON Сломанный JSON в body. Прогоните payload через jq . перед отправкой.
400 user not found на /bot/dm Пользователь не состоит в space'е бота, или user_id совпадает с bot_id. Проверьте user_id и убедитесь, что пользователь — участник того же space'а.
429 Too Many Requests Превышен rate limit — 10 msg/sec на канал или 1 req/sec на /bot/dm. Дождитесь окна и продолжите. Для burst'ов используйте message queue на вашей стороне.
500 failed to send message Внутренняя ошибка chat-слоя. Повторите с exponential backoff — типично транзиент.
SSE stream обрывается каждые ~30 секунд без сообщений Это нормально — :ping keep-alive. Игнорируйте строки, начинающиеся с :. Используйте их только для health-check соединения.
SSE stream не получает сообщения из групповых каналов SSE работает только для DM. Для групповых каналов настройте outgoing webhook в admin UI.
Сообщение отправилось (message_id вернулся), но в канале не видно Возможно, канал заархивирован или участники его не открывают. Проверьте в UI, откройте сам канал.

Rate limits: 10 сообщений/сек на канал. При превышении — HTTP 429 + заголовок Retry-After. Читайте заголовок, не угадывайте интервал.

Логирование: при ошибках сохраняйте полный HTTP-ответ (status + body) — это главный ключ к диагностике.


Связанное#