bitrix24-mcp-bridge 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -5
- package/actions.example.json +4 -1
- package/dist/cli.js +5086 -333
- package/docs/api-notes.md +235 -220
- package/extension/dist/connector.js +68 -10
- package/extension/dist/connector.js.map +2 -2
- package/package.json +4 -2
package/docs/api-notes.md
CHANGED
|
@@ -1,44 +1,34 @@
|
|
|
1
1
|
# Bitrix24 API notes (reverse-engineered)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
нет публичной документации). Предназначен для любого ИИ-агента или разработчика, который вызывает
|
|
5
|
-
эти методы через мост (`bitrix_call` / типизированные инструменты) или расширяет каталог.
|
|
3
|
+
Как агенту работать с Bitrix24 через этот мост: формат `params`, имена полей, готовые цепочки вызовов.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
Две поверхности API. **Публичный REST** (`tasks.task.*`, `im.recent.list`, `im.user.get`, `im.chat.get`,
|
|
6
|
+
`socialnetwork.api.workgroup.*`) — есть официальные доки, ссылки на них помечены **📖**
|
|
7
|
+
([`apidocs.bitrix24.com`](https://apidocs.bitrix24.com); агенту с MCP context7 — библиотека
|
|
8
|
+
`/bitrix24/b24restdocs`). **Внутренние ajax-контроллеры** (`im.v2.*`, `tasks.v2.*`, `ui.entityselector.*`)
|
|
9
|
+
публичных доков не имеют — всё ниже добыто живым реверсом; конверт и регистр полей у них могут
|
|
10
|
+
отличаться от 📖-аналогов. Имена полей стандартны для облачного Bitrix24; на другом портале сверяйся
|
|
11
|
+
реверсом (`docs/reconnaissance.md`).
|
|
10
12
|
|
|
11
13
|
---
|
|
12
14
|
|
|
13
15
|
## 1. Как вызывать
|
|
14
16
|
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
чаты задач), `bitrix_recent_tail` (листать недавние вглубь), `bitrix_chat_load` (открыть по
|
|
27
|
-
`dialogId`/`chatId`), `bitrix_chat_messages`, `bitrix_chat_history` (листать вглубь по `beforeId`),
|
|
28
|
-
`bitrix_chat_get_dialog_id` (dialogId по externalId), `bitrix_chat_mark_read` (⚠ мутирующий),
|
|
29
|
-
`bitrix_chat_read_all` (⚠ мутирующий).
|
|
30
|
-
- Люди/поиск: `bitrix_user_get` (карточка юзера), `bitrix_entity_selector` (загрузка селектора),
|
|
31
|
-
`bitrix_entity_search` (текстовый поиск чатов/сущностей через `ui.entityselector.doSearch`).
|
|
32
|
-
|
|
33
|
-
Любое имя каталога всегда доступно и напрямую через `bitrix_call { name, params }`.
|
|
34
|
-
- **`bitrix_help`** — этот же гайд, отдаётся через MCP (инструмент + resource `bitrix://api-notes`).
|
|
35
|
-
Источник — **этот файл** (`docs/api-notes.md`).
|
|
36
|
-
|
|
37
|
-
Полный список имён каталога отдаёт `bitrix_call` в своём описании (`deps.catalog.names()`).
|
|
17
|
+
- **Типизированные инструменты** (`bitrix_tasks_list`, `bitrix_task_get`, `bitrix_chat_load`, …) —
|
|
18
|
+
обёртки с разумными дефолтами; их полный список и описания видны среди MCP-инструментов. Точная
|
|
19
|
+
выборка — опциональным `params`: он мержится **последним** и перекрывает дефолты.
|
|
20
|
+
- **`bitrix_call { name, params }`** — любое имя из каталога (`actions.json`), `params` в нативном
|
|
21
|
+
формате Bitrix (§3). Полный список имён отдаёт сам `bitrix_call` в своём описании.
|
|
22
|
+
- Каталог может содержать **мутирующие** вызовы: наличие записи = разрешение вызвать. Это не режим
|
|
23
|
+
«только чтение».
|
|
24
|
+
- У обёрток есть дефолты (напр. `bitrix_tasks_list` сортирует `{"ID":"desc"}`), у `bitrix_call` их нет —
|
|
25
|
+
один и тот же фильтр даст разные страницы. Сравниваешь выдачи — задавай `order` явно.
|
|
26
|
+
- Ответ `invalid_csrf` / `invalid_authentication` = сессия портала протухла → перелогинься в браузере
|
|
27
|
+
на вкладке портала.
|
|
38
28
|
|
|
39
29
|
---
|
|
40
30
|
|
|
41
|
-
## 2. Формат ответа
|
|
31
|
+
## 2. Формат ответа
|
|
42
32
|
|
|
43
33
|
Ajax-контроллеры (`/bitrix/services/main/ajax.php`) отвечают HTTP 200 с конвертом:
|
|
44
34
|
|
|
@@ -48,272 +38,297 @@ Ajax-контроллеры (`/bitrix/services/main/ajax.php`) отвечают
|
|
|
48
38
|
"errors": [] } // ПУСТОЙ массив при успехе! (в JS [] — truthy)
|
|
49
39
|
```
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
41
|
+
Успех = `status:"success"` и/или **пустой** `errors`. Непустой `errors` / `status:"error"` / top-level
|
|
42
|
+
`error` — реальная ошибка (мост маппит её в `ok:false`). REST-эндпоинты (`/rest/*.json`) отвечают
|
|
43
|
+
иначе: `{ "result": …, "next", "total", "time" }` — без `errors`.
|
|
54
44
|
|
|
55
45
|
---
|
|
56
46
|
|
|
57
|
-
## 3.
|
|
47
|
+
## 3. Каталог: имя → action → params
|
|
58
48
|
|
|
59
|
-
|
|
49
|
+
Форма `params` различается по методу: где-то плоско, где-то вложенно, где-то обёрнуто в `params[…]` —
|
|
50
|
+
колонка «params» показывает точную форму (кодирование тела мост берёт на себя).
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
PHP-стилем (`filter[STATUS]=2`, `select[0]=ID`). Объект в `params` рекурсивно разворачивается в такие
|
|
63
|
-
ключи. Классические `tasks.task.*`, мессенджер `im.v2.*`, REST — всё это форма.
|
|
64
|
-
- **`bodyType: "json"`** — тело уходит как `JSON.stringify(params)` с `Content-Type: application/json`.
|
|
65
|
-
Так работают **`tasks.v2.*`** (напр. `tasks.v2.Task.get` ждёт `{"task":{"id":N}}`) и `ui.entityselector.*`.
|
|
66
|
-
`params` при этом сохраняет вложенную структуру как есть (не разворачивается в bracket-ключи).
|
|
67
|
-
|
|
68
|
-
> Историческая заметка: раньше мост умел только форму, и `tasks.v2.*` были непригодны. Теперь
|
|
69
|
-
> поддержаны — в записи каталога ставь `"bodyType": "json"`, и мост отправит их корректно.
|
|
70
|
-
|
|
71
|
-
**CSRF / sessid:** мост всегда шлёт заголовок **`X-Bitrix-Csrf-Token: <sessid>`** (свежий
|
|
72
|
-
`BX.bitrix_sessid()`). Form-тела дополнительно несут `sessid` полем в теле (legacy REST это требует);
|
|
73
|
-
JSON-тела несут sessid **только** в заголовке. Ответ `invalid_csrf`/`invalid_authentication` = сессия
|
|
74
|
-
протухла (перелогинься в браузере).
|
|
75
|
-
|
|
76
|
-
| Метод (action / endpoint) | bodyType | Где `select`/`filter`/`order` | Пагинация |
|
|
52
|
+
| Имя (`bitrix_call`) | action / endpoint | params | Пагинация |
|
|
77
53
|
|---|---|---|---|
|
|
78
|
-
| `tasks.
|
|
79
|
-
| `
|
|
80
|
-
| `tasks.v2.Task.get` |
|
|
81
|
-
| `tasks.v2.Scrum.getTaskInfo` |
|
|
82
|
-
| `tasks.v2.File.listObjects` |
|
|
83
|
-
| `tasks.v2.Task.View.User.count` |
|
|
84
|
-
| `tasks.v2.Task.Relation.Child.list` |
|
|
85
|
-
| `tasks.v2.Task.Relation.Related.list` |
|
|
86
|
-
| `socialnetwork.api.workgroup.list` |
|
|
87
|
-
| `socialnetwork.api.workgroup.get` |
|
|
88
|
-
| `/rest/im.recent.list.json` |
|
|
89
|
-
|
|
|
90
|
-
| `im.v2.Recent.
|
|
91
|
-
| `im.v2.
|
|
92
|
-
| `im.v2.Chat.
|
|
93
|
-
| `im.v2.Chat.
|
|
94
|
-
| `im.v2.Chat.Message.
|
|
95
|
-
| `im.v2.Chat.Message.
|
|
96
|
-
| `im.v2.Chat.
|
|
97
|
-
| `im.
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
54
|
+
| `tasks.list` | `tasks.task.list` | верхний уровень: `filter{RESPONSIBLE_ID,REAL_STATUS,GROUP_ID,…}`, `select[]`, `order{}` | **`PAGEN_1`** — номер страницы (1, 2, 3…), страница = 20 задач; `start` **не работает** (вернёт ту же первую страницу) |
|
|
55
|
+
| `task.get` | `tasks.task.get` | верхний уровень: `taskId`, `select[]` | — |
|
|
56
|
+
| `task.v2.get` | `tasks.v2.Task.get` | вложенно: `{"task":{"id":N}}` | — |
|
|
57
|
+
| `task.scrum.info` | `tasks.v2.Scrum.getTaskInfo` | `{"taskId":N}` | — |
|
|
58
|
+
| `task.files` | `tasks.v2.File.listObjects` | `{"ids":[N,…]}` | — |
|
|
59
|
+
| `task.views.count` | `tasks.v2.Task.View.User.count` | вложенно: `{"task":{"id":N}}` | — |
|
|
60
|
+
| `task.subtasks` | `tasks.v2.Task.Relation.Child.list` | `{"taskId":N,"withIds":true,"navigation":{"size":N}}` | `navigation` |
|
|
61
|
+
| `task.related` | `tasks.v2.Task.Relation.Related.list` | как `task.subtasks` | `navigation` |
|
|
62
|
+
| `projects.list` | `socialnetwork.api.workgroup.list` | верхний уровень: `select[]`, `order{}`, `filter{}` | `start` (шаг 50; `start=(N-1)*50`, `-1` — ответ без `total`) 📖 |
|
|
63
|
+
| `projects.get` | `socialnetwork.api.workgroup.get` | **обёрнуто**: `params[groupId]`, `params[select][]` | — |
|
|
64
|
+
| `chats.recent` | `/rest/im.recent.list.json` | плоско: `LIMIT`, `SKIP_OPENLINES`, `UNREAD_ONLY` | `LIMIT` |
|
|
65
|
+
| `recent.load` | `im.v2.Recent.load` | плоско: `limit`, `filter[recentSection]` (`tasksTask`/`collab`/…), `filter[unread]`, `filter[parentId]` | `recent.tail` |
|
|
66
|
+
| `recent.tail` | `im.v2.Recent.tail` | плоско: `limit`, `filter[lastMessageDate]` (курсор), `filter[recentSection]` | `filter[lastMessageDate]` |
|
|
67
|
+
| `chat.load` | `im.v2.Chat.load` | плоско: `dialogId`\|`chatId`, `messageLimit` | — |
|
|
68
|
+
| `chat.dialogId` | `im.v2.Chat.getDialogId` | плоско: `externalId` (напр. `"sg"+groupId`) | — |
|
|
69
|
+
| `chat.messages` | `im.v2.Chat.Message.list` | плоско: `chatId`, `limit` | только последняя страница — назад **не листает**, используй `chat.messages.tail` |
|
|
70
|
+
| `chat.messages.tail` | `im.v2.Chat.Message.tail` | плоско: `chatId`, `limit`, `filter[lastId]`, `order[id]` | `filter[lastId]` (§5.1) |
|
|
71
|
+
| `chat.message.read` | `im.v2.Chat.Message.read` | плоско: `chatId`, `ids[]`, `actionUuid` | ⚠ мутирующий |
|
|
72
|
+
| `chat.read.all` | `im.v2.Chat.readAll` | без параметров | ⚠ мутирующий |
|
|
73
|
+
| `im.user.get` | `/rest/im.user.get.json` | плоско: `ID` | — |
|
|
74
|
+
| `im.chat.get` | `/rest/im.chat.get.json` | плоско: `ENTITY_TYPE`, `ENTITY_ID` (напр. `TASKS_TASK`+taskId) | — |
|
|
75
|
+
| `entityselector.load` | `ui.entityselector.load` | `{"dialog":{entities,preselectedItems,…}}` | — |
|
|
76
|
+
| `entityselector.search` | `ui.entityselector.doSearch` | `{"dialog":{…},"searchQuery":{"query":"…","queryWords":["…"]}}` | — |
|
|
77
|
+
| `call.detail` | `GET /call/detail/{callId}` | `callId` (подставляется в путь), `IFRAME=Y`, `IFRAME_TYPE=SIDE_SLIDER` → HTML | см. §5.4 |
|
|
100
78
|
|
|
101
79
|
---
|
|
102
80
|
|
|
103
|
-
## 4.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
81
|
+
## 4. Справочник полей
|
|
82
|
+
|
|
83
|
+
**Задача** (`tasks.task.*`) — поля для `select`/`filter` в UPPER_CASE: `ID`, `TITLE`, `DESCRIPTION`,
|
|
84
|
+
`STATUS`, `REAL_STATUS`, `RESPONSIBLE_ID`, `CREATED_BY`, `CREATED_DATE`, `CHANGED_DATE`, `DEADLINE`,
|
|
85
|
+
`CLOSED_DATE`, `PRIORITY`, `GROUP_ID`, `TAGS`, `TIME_ESTIMATE`, **`CHAT_ID`** (id чата-обсуждения
|
|
86
|
+
задачи — прямой резолвер `taskId → chatId`, §5.2), `UF_*`. Ответ подкладывает объекты `group`,
|
|
87
|
+
`responsible`, `creator`, `action`.
|
|
88
|
+
📖 (`tasks.task.get`/`list`) — сверх этого: `parentId`, `stageId`, `sprintId`, `backlogId`,
|
|
89
|
+
`commentsCount`, `timeSpentInLogs`, `favorite`, `flowId`, `mark`, `accomplices[]`, `auditors[]`,
|
|
90
|
+
`checklist{}`, `subStatus`; в официальном REST-ответе поля приходят **camelCase**, а в `select`/`filter`
|
|
91
|
+
они всегда **UPPER_CASE**. Доп. вычисления — флагами `params{WITH_TIMER_INFO, WITH_RESULT_INFO,
|
|
92
|
+
WITH_PARSED_DESCRIPTION}`.
|
|
93
|
+
|
|
94
|
+
**Статусы** (`REAL_STATUS` — «настоящее» числовое состояние): `1` Новая · `2` Ждёт выполнения ·
|
|
95
|
+
`3` Выполняется · `4` Ждёт контроля · `5` Завершена · `6` Отложена · `7` Отклонена. Поле `STATUS`
|
|
96
|
+
поверх этого несёт **мета-состояния** отображения (почти просрочена / не просмотрена / просрочена),
|
|
97
|
+
поэтому «работает ли задача сейчас» фильтруй по **`REAL_STATUS`** (напр. `{"REAL_STATUS":3}`) 📖.
|
|
98
|
+
Асимметрия: в **`filter`** работает `REAL_STATUS`, а в **`select`** его указывать бесполезно — поле молча
|
|
99
|
+
не вернётся; чтобы получить состояние, проси `select:["STATUS"]` → в ответе будет ключ `status` со
|
|
100
|
+
значением по той же шкале 1..7 (у обёртки `bitrix_tasks_list` он уже в дефолтном `select`).
|
|
101
|
+
|
|
102
|
+
**Фильтр `tasks.task.list`** — оператор ставится перед именем поля: `!` (не равно / исключить),
|
|
103
|
+
`<`, `<=`, `>`, `>=`, `%` (LIKE-подстрока). Напр. `{"!REAL_STATUS":5}` — все **незакрытые**,
|
|
104
|
+
`{">=DEADLINE":"2026-07-01"}`, `{"%TITLE":"карта"}`. Фильтруемые поля: `ID`, `PARENT_ID`, `GROUP_ID`,
|
|
105
|
+
`CREATED_BY`, `RESPONSIBLE_ID`, `ACCOMPLICE`, `AUDITOR`, `REAL_STATUS`, `STATUS`, `PRIORITY`, `TAG`,
|
|
106
|
+
`STAGE_ID`, `SPRINT_ID`, `BACKLOG_ID`, `DEADLINE`, `*_DATE`, `UF_CRM_TASK`.
|
|
107
|
+
|
|
108
|
+
> **«Мои задачи» ≠ только `RESPONSIBLE_ID`.** Роли участника раздельны: ответственный
|
|
109
|
+
> (`RESPONSIBLE_ID`), соисполнитель (`ACCOMPLICE`), наблюдатель (`AUDITOR`), постановщик
|
|
110
|
+
> (`CREATED_BY`); единого «любая роль» поля в `list` нет — чтобы собрать **все** свои задачи, объедини
|
|
111
|
+
> результаты нескольких вызовов (минимум `RESPONSIBLE_ID` + `ACCOMPLICE`) и **дедуплицируй по `id`** —
|
|
112
|
+
> одна задача часто попадает сразу в несколько ролей. В **`filter`** роли в **единственном** числе
|
|
113
|
+
> (`ACCOMPLICE`/`AUDITOR`), в **`select`** — во **множественном** (`ACCOMPLICES`/`AUDITORS`).
|
|
114
|
+
|
|
115
|
+
> **Scrum-задачи (спринты).** Поле `SPRINT_ID` задачи и жёсткий `filter[SPRINT_ID]` могут расходиться:
|
|
116
|
+
> задача числится за спринтом по своему полю, но не попадает в выборку по фильтру (возвращена в бэклог
|
|
117
|
+
> или в иной stage). Если по фильтру «маловато» — перепроверь без него и отфильтруй по
|
|
118
|
+
> `SPRINT_ID`/`STAGE_ID` у себя. Метода «колонки scrum-доски» (`STAGE_ID` → имя колонки) в каталоге нет.
|
|
119
|
+
|
|
120
|
+
**Группа/проект — список** (`workgroup.list`): `ID`, `NAME`, `DESCRIPTION`, `NUMBER_OF_MEMBERS`,
|
|
121
|
+
`OWNER_ID`, `DATE_CREATE`, `PROJECT` (Y=проект/N=группа), `TYPE`; **без `select` вернётся только `ID`**.
|
|
122
|
+
📖 фильтруемые: `ID`, `NAME`, `OWNER_ID`, `ACTIVE`, `VISIBLE`, `OPENED`, `CLOSED`, `PROJECT`,
|
|
123
|
+
`SUBJECT_ID`, `SITE_ID`, `DATE_CREATE`, `DATE_UPDATE`, `DATE_ACTIVITY`; операторы фильтра
|
|
124
|
+
`>= > <= < % =% %= !% != !`.
|
|
147
125
|
**Группа — карточка** (`workgroup.get`): плюс `OWNER_DATA`, `SUBJECT_DATA`, `MEMBERS[]`,
|
|
148
|
-
`MODERATOR_MEMBERS[]`,
|
|
126
|
+
`MODERATOR_MEMBERS[]`, **`CHAT_ID`**, `DIALOG_ID`, `IMAGE_ID`, UF-поля.
|
|
127
|
+
|
|
128
|
+
**Недавний чат** (`im.recent.list`): `id` (это dialogId: число = юзер, `"chat"+N` = чат), `chat_id`,
|
|
129
|
+
`type` (`user`/`chat`), `title`, `message{id,text,file,author_id,date,status}`, `counter` (непрочитанных),
|
|
130
|
+
`unread`, `last_id`, `pinned`, `date_update`, `date_last_activity`, `user{…}`,
|
|
131
|
+
`chat{… entity_type, entity_id, owner, …}`; есть ли ещё страницы — `hasMore`/`hasMorePages` 📖.
|
|
132
|
+
**Недавние по секции** (`im.v2.Recent.load`): `filter[recentSection]` — `default` (обычные диалоги),
|
|
133
|
+
**`tasksTask` (чаты задач)**, `collab`/`collabDefault`. Вглубь — `im.v2.Recent.tail` с курсором
|
|
134
|
+
`filter[lastMessageDate]` (ISO-дата последнего элемента страницы).
|
|
149
135
|
|
|
150
|
-
**Недавний чат** (`im.recent.list`): `chat_id`, `title`, `type`, `message{id,text,date,author_id}`,
|
|
151
|
-
`last_id`, `unread`, `pinned`, `user{…}`, `chat{…}`.
|
|
152
|
-
**Недавние по секции** (`im.v2.Recent.load`): фильтр `filter[recentSection]` — `default` (обычные
|
|
153
|
-
диалоги), **`tasksTask` (чаты задач)**, `collab`/`collabDefault` (коллабы). Листание вглубь —
|
|
154
|
-
`im.v2.Recent.tail` с курсором `filter[lastMessageDate]` (ISO-дата последнего элемента страницы).
|
|
155
136
|
**Карточка юзера** (`im.user.get`): `id`, `name`, `first_name`, `last_name`, `avatar`, `work_position`,
|
|
156
137
|
`gender`, `status`, `online` — резолв автора сообщения (`authorId`) в имя.
|
|
157
|
-
**Сообщение** (`im.v2.Chat.Message.*`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`.
|
|
158
|
-
Ответ также несёт `users[]` (участники), `additionalMessages[]`, `hasPrevPage`/`hasNextPage`.
|
|
159
|
-
`chatId` берётся из `im.recent.list` (`chat_id`) или из `workgroup.get` (`CHAT_ID`/`DIALOG_ID`).
|
|
160
|
-
|
|
161
|
-
**Адресация чата (`chat.load`): `dialogId` vs `chatId`.** `chat.load` принимает любой из двух:
|
|
162
|
-
- `dialogId` = **ID пользователя** (число, напр. `11`) → **личный чат 1-на-1** с этим пользователем.
|
|
163
|
-
- `dialogId` = **`"chat"+CHAT_ID`** (напр. `"chat7111"`) → **групповой чат** (проекта/задачи/канала).
|
|
164
|
-
- `chatId` = числовой `CHAT_ID` — то же, что групповой `dialogId`, но без префикса.
|
|
165
138
|
|
|
166
|
-
|
|
167
|
-
|
|
139
|
+
**Сообщение** (`im.v2.Chat.Message.*`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`;
|
|
140
|
+
ответ также несёт `users[]` (участники), `additionalMessages[]`, `hasPrevPage`/`hasNextPage`.
|
|
168
141
|
|
|
169
142
|
---
|
|
170
143
|
|
|
171
|
-
##
|
|
144
|
+
## 5. Сценарии
|
|
172
145
|
|
|
173
146
|
```jsonc
|
|
174
147
|
// открытые задачи ответственного 55, по дедлайну, вторая страница
|
|
175
148
|
bitrix_tasks_list { "params": { "filter": {"RESPONSIBLE_ID":55,"REAL_STATUS":2},
|
|
176
149
|
"order": {"DEADLINE":"asc"}, "start": 50 } }
|
|
177
150
|
|
|
178
|
-
// карточка задачи с расширенным набором
|
|
179
|
-
bitrix_task_get
|
|
180
|
-
|
|
181
|
-
// карточка задачи через v2-подсистему (JSON-тело; id вложен в task)
|
|
151
|
+
// карточка задачи с расширенным набором полей; v2-подсистема; файлы, просмотры, связи
|
|
152
|
+
bitrix_task_get { "taskId": 4229, "params": { "select": ["ID","TITLE","DESCRIPTION","TAGS"] } }
|
|
182
153
|
bitrix_task_get_v2 { "taskId": 4229 } // → { "task": { "id": 4229 } }
|
|
183
|
-
bitrix_call { "name": "task.v2.get", "params": { "task": { "id": 4229 } } }
|
|
184
|
-
|
|
185
|
-
// файлы задачи и счётчик просмотров (v2, JSON-тело); подзадачи/связанные
|
|
186
154
|
bitrix_call { "name": "task.files", "params": { "ids": [4229] } }
|
|
187
|
-
bitrix_call { "name": "task.views.count", "params": { "task": { "id": 4229 } } }
|
|
188
155
|
bitrix_task_subtasks { "taskId": 4229 }
|
|
189
156
|
bitrix_task_related { "taskId": 4229 }
|
|
190
157
|
|
|
191
|
-
// проекты по дате создания; полная карточка группы
|
|
158
|
+
// проекты по дате создания; полная карточка группы (отдаёт CHAT_ID / DIALOG_ID)
|
|
192
159
|
bitrix_projects_list { "params": { "order": {"DATE_CREATE":"desc"} } }
|
|
193
|
-
bitrix_project_get
|
|
160
|
+
bitrix_project_get { "groupId": 15 }
|
|
161
|
+
```
|
|
194
162
|
|
|
195
|
-
|
|
196
|
-
bitrix_chat_load { "chatId": 485, "messageLimit": 50 }
|
|
163
|
+
### 5.1. Мессенджер (IM): как получить `chatId`
|
|
197
164
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
bitrix_chat_history
|
|
165
|
+
Всё общение — **чаты**: личные 1-на-1, групповые (проектов, каналов) и **чаты-обсуждения задач**.
|
|
166
|
+
Числовой **`chatId`** — ключ ко всем операциям с сообщениями (`bitrix_chat_messages`,
|
|
167
|
+
`bitrix_chat_history`, `bitrix_chat_mark_read`); почти любая ошибка агента здесь — на шаге его получения.
|
|
201
168
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
169
|
+
**Три идентификатора одного чата** (годятся для разных операций):
|
|
170
|
+
|
|
171
|
+
| Идентификатор | Что это | Пример | Куда передавать |
|
|
172
|
+
|---|---|---|---|
|
|
173
|
+
| `chatId` | числовой id чата | `485` | сообщения / история / отметка прочитанным |
|
|
174
|
+
| `dialogId` | адрес для ОТКРЫТИЯ: `userId` (личный 1-на-1) **или** `"chat"+chatId` (групповой) | `11`, `"chat485"` | `bitrix_chat_load` |
|
|
175
|
+
| `externalId` | внешний ключ сущности → резолвится в `dialogId` | `"sg15"` (соцгруппа 15) | `bitrix_chat_get_dialog_id` |
|
|
176
|
+
|
|
177
|
+
Ключевое: **личный чат с человеком = его `userId` в роли `dialogId`** (отдельного «id чата» у 1-на-1
|
|
178
|
+
знать не нужно). `bitrix_chat_load` принимает `dialogId` **или** `chatId` и всегда **возвращает
|
|
179
|
+
числовой `chatId`** — дальше оперируешь им.
|
|
205
180
|
|
|
206
|
-
|
|
181
|
+
**Как получить `chatId` — по цели:**
|
|
207
182
|
|
|
208
|
-
|
|
209
|
-
|
|
183
|
+
| Хочу открыть чат… | Шаги |
|
|
184
|
+
|---|---|
|
|
185
|
+
| **с пользователем по ИМЕНИ** | `bitrix_entity_search { query:"дмитрий" }` → диалог с `dialogId` = его userId → `bitrix_chat_load { dialogId }` |
|
|
186
|
+
| **с пользователем, id известен** | `bitrix_chat_load { dialogId: <userId> }` (userId — из задачи `RESPONSIBLE_ID`/`CREATED_BY`, из `bitrix_chats_recent`, из `bitrix_user_get`) |
|
|
187
|
+
| **обсуждение ЗАДАЧИ** | `bitrix_task_get` → `CHAT_ID` (§5.2) |
|
|
188
|
+
| **ПРОЕКТА/группы** | `bitrix_task_get`→`GROUP_ID`→`bitrix_project_get`→`CHAT_ID`; либо `bitrix_chat_get_dialog_id { externalId:"sg<groupId>" }` |
|
|
189
|
+
| **не знаю точно, какой** | `bitrix_chats_recent` (недавние, сопоставь по `title`/`user`) или `bitrix_entity_search { query }` |
|
|
190
|
+
|
|
191
|
+
> Почему не «просто список чатов»: `bitrix_chats_recent` (`im.recent.list`) отдаёт лишь недавние и
|
|
192
|
+
> **без чатов задач**. Для поиска конкретного чата — `bitrix_entity_search`; для чатов задач —
|
|
193
|
+
> `bitrix_recent_load { section:"tasksTask" }`.
|
|
194
|
+
|
|
195
|
+
**Читать историю.** `bitrix_chat_messages` отдаёт только последнюю страницу; вглубь листает
|
|
196
|
+
`bitrix_chat_history` по курсору `beforeId` = минимальный id сообщения текущей страницы (повторяй,
|
|
197
|
+
уменьшая `beforeId`, до начала истории).
|
|
210
198
|
|
|
211
199
|
```jsonc
|
|
212
|
-
|
|
213
|
-
bitrix_chat_load { "dialogId": 11 }
|
|
214
|
-
// 2) листать его историю вглубь по chatId из ответа шага 1
|
|
200
|
+
bitrix_chat_load { "dialogId": 11 } // личный чат юзера 11 → в ответе chatId
|
|
215
201
|
bitrix_chat_history { "chatId": 485, "beforeId": 1861279 }
|
|
202
|
+
bitrix_chat_mark_read { "chatId": 40271, "ids": [1884131] } // ⚠ мутирующий; actionUuid — автоматом
|
|
216
203
|
```
|
|
217
204
|
|
|
218
|
-
|
|
205
|
+
📖 Публичные аналоги чтения сообщений: `im.dialog.messages.get` (дефолт 20, макс 50; листание
|
|
206
|
+
`LAST_ID` — старее, `FIRST_ID` — новее); `imbot.v2 chat.getMessageContext` — ответ `messages[]`
|
|
207
|
+
(oldest→newest) + `users[]` + `hasPrevPage`/`hasNextPage`, по форме **совпадает** с внутренними
|
|
208
|
+
`im.v2.Chat.Message.*`.
|
|
219
209
|
|
|
220
|
-
|
|
210
|
+
### 5.2. Обсуждение (чат) конкретной задачи
|
|
221
211
|
|
|
222
|
-
**А.
|
|
223
|
-
|
|
212
|
+
**А. Прямой резолв — предпочтительно.** `bitrix_task_get` возвращает `CHAT_ID` чата-обсуждения (входит
|
|
213
|
+
в дефолтный `select` 📖) — это `taskId → chatId` без поиска по названию и без промаха по тёзкам:
|
|
224
214
|
|
|
225
215
|
```jsonc
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
bitrix_chat_load { "dialogId": "chat38849" } // или { "chatId": 38849 }
|
|
230
|
-
bitrix_chat_history { "chatId": 38849, "beforeId": 1722353 }
|
|
216
|
+
bitrix_task_get { "taskId": 28373 } // в ответе CHAT_ID
|
|
217
|
+
bitrix_chat_load { "chatId": <CHAT_ID> }
|
|
218
|
+
bitrix_chat_history { "chatId": <CHAT_ID>, "beforeId": <мин id страницы> }
|
|
231
219
|
```
|
|
232
220
|
|
|
233
|
-
|
|
221
|
+
Общая альтернатива — **`bitrix_entity_chat`** (обёртка над `im.chat.get`): chatId любого связанного
|
|
222
|
+
объекта по паре `entityType`/`entityId`; кроме `TASKS_TASK` так же адресуются `SONET_GROUP`
|
|
223
|
+
(группа/проект), `CRM`, `CALENDAR`, `MAIL`, `VIDEOCONF`, `CALL`:
|
|
234
224
|
|
|
235
225
|
```jsonc
|
|
236
|
-
|
|
226
|
+
bitrix_entity_chat { "entityType": "TASKS_TASK", "entityId": 28373 } // → chatId
|
|
237
227
|
```
|
|
238
228
|
|
|
229
|
+
**Б. Через список чатов задач** (секция `tasksTask`; в `bitrix_chats_recent` их НЕТ):
|
|
230
|
+
|
|
231
|
+
```jsonc
|
|
232
|
+
bitrix_recent_load { "section": "tasksTask" } // → чаты задач с chatId/dialogId
|
|
233
|
+
bitrix_recent_tail { "section": "tasksTask", "lastMessageDate": "2026-06-29T17:25:38+03:00" }
|
|
234
|
+
bitrix_chat_load { "dialogId": "chat38849" } // или { "chatId": 38849 }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**В. Поиском по названию задачи** (запасной, матч по тексту):
|
|
238
|
+
`bitrix_entity_search { "query": "трекер", "section": "tasksTask" }`
|
|
239
|
+
|
|
239
240
|
**Чат ПРОЕКТА задачи** (обсуждение группы, не самой задачи) — через `GROUP_ID`:
|
|
240
241
|
|
|
241
242
|
```jsonc
|
|
242
243
|
bitrix_task_get { "taskId": 4229, "params": { "select": ["ID","GROUP_ID"] } }
|
|
243
244
|
bitrix_project_get { "groupId": 15 } // отдаёт CHAT_ID / DIALOG_ID
|
|
244
|
-
bitrix_chat_load { "dialogId": "chat7111" }
|
|
245
245
|
bitrix_chat_get_dialog_id { "externalId": "sg15" } // либо резолв dialogId соцгруппы напрямую
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
###
|
|
248
|
+
### 5.3. Поиск чата / пользователя
|
|
249
249
|
|
|
250
|
-
|
|
251
|
-
|
|
250
|
+
`bitrix_entity_search` (обёртка над внутренним `ui.entityselector.doSearch`) сам собирает диалог
|
|
251
|
+
`IM_CHAT_SEARCH` и `searchQuery` из строки. Публичные аналоги: люди — `user.get`/`user.search`, чаты —
|
|
252
|
+
`im.recent.list` + фильтрация у себя.
|
|
252
253
|
|
|
253
254
|
```jsonc
|
|
254
255
|
bitrix_entity_search { "query": "дмитрий" } // среди всех чатов/диалогов
|
|
255
256
|
bitrix_entity_search { "query": "трекер", "section": "tasksTask" } // среди чатов задач
|
|
257
|
+
bitrix_user_get { "userId": 11 } // резолв authorId → имя
|
|
256
258
|
```
|
|
257
259
|
|
|
258
|
-
|
|
260
|
+
### 5.4. Созвон: резюме, расшифровка, запись
|
|
261
|
+
|
|
262
|
+
AI-анализ звонка (BitrixGPT Follow-Up) живёт на серверно отрендеренной странице `/call/detail/<callId>`
|
|
263
|
+
— JSON-эндпоинта под ней нет, публичного REST для видеовстреч тоже (📖 только телефония `voximplant.*`
|
|
264
|
+
и `crm.activity.call.getTranscript`). Мост читает эту страницу и разбирает её в нормализованный JSON.
|
|
265
|
+
|
|
266
|
+
**1) Найти звонки.** Серверного поиска звонков по порталу нет — ищем в конкретном чате: звонок
|
|
267
|
+
оставляет системное сообщение с `params.COMPONENT_PARAMS = { messageType: "START", callId }`.
|
|
259
268
|
|
|
260
269
|
```jsonc
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
bitrix_user_get { "userId": 11 } // резолв authorId → имя
|
|
270
|
+
bitrix_chat_calls { "chatId": 9876 } // чат проекта: bitrix_project_get → CHAT_ID
|
|
271
|
+
bitrix_chat_calls { "chatId": 9876, "maxPages": 20, "beforeId": 5001 } // продолжить вглубь
|
|
264
272
|
```
|
|
265
273
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
- **im.v2-чат** — структурный JSON, читается уже сейчас (§6.2); обычно этого достаточно.
|
|
270
|
-
- **Форумные легаси-комментарии** — рендерятся как **HTML** в side-slider'е. Это отдельный
|
|
271
|
-
«HTML-поддомен» внутреннего API, который **текущий мост не поддерживает** (блокеры ниже).
|
|
274
|
+
Ответ: `calls[] { callId, startedAt, startedBy, startMessageId, events[], summaryMessageId }` плюс
|
|
275
|
+
`scannedPages` / `oldestScannedMessageId` / **`reachedHistoryStart`** — если он `false`, история
|
|
276
|
+
дочитана НЕ до конца, продолжай с `beforeId: <oldestScannedMessageId>`.
|
|
272
277
|
|
|
273
|
-
|
|
278
|
+
**2) Взять всё по звонку.**
|
|
274
279
|
|
|
275
|
-
```
|
|
276
|
-
|
|
280
|
+
```jsonc
|
|
281
|
+
bitrix_call_detail { "callId": 4242 } // всё; длинная расшифровка уедет в файл
|
|
282
|
+
bitrix_call_detail { "callId": 4242, "transcript": "inline" } // расшифровка прямо в ответе
|
|
283
|
+
bitrix_call_detail { "callId": 4242, "transcript": "none" } // расшифровка не нужна вовсе
|
|
284
|
+
bitrix_call_detail { "callId": 4242, "transcript": "file" } // всегда в файл, даже короткая
|
|
277
285
|
```
|
|
278
286
|
|
|
279
|
-
|
|
287
|
+
| Поле ответа | Что внутри |
|
|
288
|
+
|---|---|
|
|
289
|
+
| `title`, `agenda`, `meetingType` | тема, вступление, тип встречи («Статус-встреча») |
|
|
290
|
+
| `date`, `interval`, `duration` | «23 июля, 15:46», «15:46 - 16:51», «1 ч 4 мин» |
|
|
291
|
+
| `efficiency`, `qualityChecklist[]` | оценка встречи в % и критерии `{ ok, text }` |
|
|
292
|
+
| `participants[]` | `{ id, name, talkTimePercent, talkTime, efficiency, metrics[], insight }` |
|
|
293
|
+
| `overview`, `chapters[]` | общее резюме и главы `{ from, to, title, text }` с таймкодами |
|
|
294
|
+
| `decisions[]`, `tasks[]` | «что решили» и задачи `{ assigneeId, assignee, text }` |
|
|
295
|
+
| `transcript[]`, `transcriptCount` | реплики `{ from, to, speakerId, speaker, text }` |
|
|
296
|
+
| `recording` | `{ path, url, trackId }` — аудиозапись, качается `bitrix_call_recording` (§5.5) |
|
|
297
|
+
|
|
298
|
+
**Расшифровка длинного звонка не приходит в ответе.** У часовой встречи она даёт ~70 КБ из 80 КБ, поэтому
|
|
299
|
+
свыше ~20 КБ (режим `"auto"` = умолчание) пишется на диск, а в ответе остаются `transcript: []`,
|
|
300
|
+
`transcriptCount` и **`files`**: `files.transcript` — `call-<id>-transcript.txt`, где строка = реплика
|
|
301
|
+
(`[11:10—11:24] Генрих Богацкий: …`), её удобно **грепать** (найденная строка сама несёт таймкод и
|
|
302
|
+
спикера); `files.json` — полный ответ с расшифровкой. Оба файла — кеш звонка, перезаписываются.
|
|
303
|
+
|
|
304
|
+
Тонкости:
|
|
305
|
+
- **`speakerId`** резолвится по имени из таблицы анализа; кто говорил мало и в неё не попал — получит
|
|
306
|
+
`speakerId: null`, но останется в `participants` с `id: null`.
|
|
307
|
+
- **`assigneeId` задачи** — упомянутый в тексте пользователь, а НЕ id смотрящего.
|
|
308
|
+
- Нет доступа к звонку / протухла сессия → инструмент вернёт внятную ошибку (портал отдаёт HTML логина).
|
|
309
|
+
|
|
310
|
+
### 5.5. Скачать файл (вложение чата, файл задачи, запись звонка)
|
|
311
|
+
|
|
312
|
+
Ссылки портал минтит и подписывает сам, но подпись НЕ заменяет авторизацию — качает расширение в
|
|
313
|
+
сессии пользователя.
|
|
280
314
|
|
|
315
|
+
```jsonc
|
|
316
|
+
// вложение чата: ссылку берём из files[].urlDownload в ответе bitrix_chat_load
|
|
317
|
+
bitrix_file_download { "url": "https://<портал>/bitrix/services/main/ajax.php?action=disk.api.file.download&…" }
|
|
318
|
+
bitrix_call_recording { "callId": 4242 } // аудиозапись созвона (ссылку найдёт сам)
|
|
319
|
+
bitrix_file_download { "url": "…", "savePath": "/tmp/photo.png", "overwrite": true }
|
|
281
320
|
```
|
|
282
|
-
POST /bitrix/services/main/ajax.php?mode=class&c=bitrix:forum.comments&action=navigateComment
|
|
283
|
-
form: AJAX_POST=Y, ENTITY_XML_ID=TASK_<taskId>, taskId=<taskId>, MODE=LIST,
|
|
284
|
-
FILTER[<ID]=<курсор: id, ДО которого грузить старые>, PAGEN_1=1,
|
|
285
|
-
signedParameters=<подписанный blob>, IFRAME=Y, IFRAME_TYPE=SIDE_SLIDER
|
|
286
|
-
headers: bx-ajax: true, x-bitrix-site-id: s1 (плюс обычный X-Bitrix-Csrf-Token)
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
`signedParameters` — base64 PHP-массива + HMAC-подпись сервера; кодирует
|
|
290
|
-
`FORUM_ID, ENTITY_TYPE=TK, ENTITY_ID=<taskId>, ENTITY_XML_ID=TASK_<taskId>, …`. **Подделать нельзя** —
|
|
291
|
-
берётся из HTML первой страницы.
|
|
292
|
-
|
|
293
|
-
**Почему пока не в каталоге (3 блокера):**
|
|
294
|
-
1. Мост парсит ответ только как JSON (`extension/src/connector.ts` → `resp.json()`) — HTML бросит исключение.
|
|
295
|
-
2. Path-параметр в URL (`/task/comments/<taskId>/`) — записи каталога статичны, шаблонов пути нет.
|
|
296
|
-
3. `signedParameters` + заголовки `bx-ajax`/`x-bitrix-site-id` — нужен предварительный GET HTML-страницы.
|
|
297
321
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
322
|
+
Ответ — **путь, а не содержимое**: `{ path, bytes, contentType, fileName }` (гигабайты не попадают в
|
|
323
|
+
контекст). По умолчанию файл ложится в `~/.bitrix24-mcp-bridge/downloads/`; `savePath` без ведущего `/`
|
|
324
|
+
трактуется как имя внутри этой папки. Потолок — 512 МБ.
|
|
301
325
|
|
|
302
|
-
|
|
303
|
-
|
|
326
|
+
Тонкости:
|
|
327
|
+
- **Граница безопасности — origin**: скачивается только с origin сконфигурированного портала.
|
|
328
|
+
- **Имя даёт портал** (`Content-Disposition`), поэтому без `savePath` угадывать его не нужно.
|
|
329
|
+
- **Повторное скачивание** того же файла падает с `already exists` — передай `overwrite: true`.
|
|
304
330
|
|
|
305
331
|
---
|
|
306
332
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
1. Сними реальные вызовы (`docs/reconnaissance.md` — авто-запись `bun run capture` или HAR →
|
|
310
|
-
`bun run catalog:draft`). Черновик `actions.draft.json` накапливает **все уникальные комбинации
|
|
311
|
-
параметров** на экшен и проставляет `bodyType` (form/json) автоматически.
|
|
312
|
-
2. Определи транспорт по разделу 3: если черновик показал `"bodyType": "json"` — перенеси его в запись.
|
|
313
|
-
3. Добавь запись в `actions.json` (может быть мутирующей — каталог это допускает). Проверь через
|
|
314
|
-
`bitrix_call { name, params }`.
|
|
315
|
-
4. Если удобно — оберни в типизированный инструмент в `src/tools/register.ts` (с дефолтным `select`).
|
|
316
|
-
|
|
317
|
-
> `actions.json` / `actions.draft.json` — данные конкретного портала (gitignored). Этот файл
|
|
318
|
-
> (`api-notes.md`) — переносимые знания о самом API; держи его в актуальном состоянии при добавлении
|
|
319
|
-
> новых методов.
|
|
333
|
+
Расширение каталога новыми методами, транспорт (`bodyType`, `responseType`, path-параметры) и
|
|
334
|
+
доставка правок в живой daemon — `docs/reconnaissance.md`.
|