bitrix24-mcp-bridge 0.3.0 → 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 +16 -3
- package/actions.example.json +3 -1
- package/dist/cli.js +5023 -283
- package/docs/api-notes.md +198 -313
- package/extension/dist/connector.js +68 -10
- package/extension/dist/connector.js.map +2 -2
- package/package.json +2 -1
package/docs/api-notes.md
CHANGED
|
@@ -1,55 +1,34 @@
|
|
|
1
1
|
# Bitrix24 API notes (reverse-engineered)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
> библиотека `/bitrix24/b24restdocs`). Для публично-документированных методов доки применимы напрямую;
|
|
13
|
-
> для внутренних `im.v2.*`/`tasks.v2.*` — как **справочный аналог** (конверт/регистр полей могут
|
|
14
|
-
> отличаться, сверяй реверсом). Дальше по тексту такие отсылки помечены **📖**; иди по ним, если нужна
|
|
15
|
-
> деталь, которой здесь нет.
|
|
16
|
-
|
|
17
|
-
Наблюдения сняты на портале `example.bitrix24.ru` (облако, мессенджер **im.v2**, задачи в режиме
|
|
18
|
-
**scrum-борда**). Имена полей и конвенции стандартны для облачного Bitrix24, но при переносе на другой
|
|
19
|
-
портал сверяйся реверсом (`docs/reconnaissance.md`).
|
|
3
|
+
Как агенту работать с Bitrix24 через этот мост: формат `params`, имена полей, готовые цепочки вызовов.
|
|
4
|
+
|
|
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`).
|
|
20
12
|
|
|
21
13
|
---
|
|
22
14
|
|
|
23
15
|
## 1. Как вызывать
|
|
24
16
|
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
чаты задач), `bitrix_recent_tail` (листать недавние вглубь), `bitrix_chat_load` (открыть по
|
|
37
|
-
`dialogId`/`chatId`), `bitrix_chat_messages`, `bitrix_chat_history` (листать вглубь по `beforeId`),
|
|
38
|
-
`bitrix_chat_get_dialog_id` (dialogId по externalId), `bitrix_chat_mark_read` (⚠ мутирующий),
|
|
39
|
-
`bitrix_chat_read_all` (⚠ мутирующий).
|
|
40
|
-
- Люди/поиск: `bitrix_user_get` (карточка юзера), `bitrix_entity_selector` (загрузка селектора),
|
|
41
|
-
`bitrix_entity_search` (текстовый поиск чатов/сущностей через `ui.entityselector.doSearch`),
|
|
42
|
-
`bitrix_entity_chat` (chatId чата связанного объекта: задачи/группы/CRM — через `im.chat.get`).
|
|
43
|
-
|
|
44
|
-
Любое имя каталога всегда доступно и напрямую через `bitrix_call { name, params }`.
|
|
45
|
-
- **`bitrix_help`** — этот же гайд, отдаётся через MCP (инструмент + resource `bitrix://api-notes`).
|
|
46
|
-
Источник — **этот файл** (`docs/api-notes.md`).
|
|
47
|
-
|
|
48
|
-
Полный список имён каталога отдаёт `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
|
+
на вкладке портала.
|
|
49
28
|
|
|
50
29
|
---
|
|
51
30
|
|
|
52
|
-
## 2. Формат ответа
|
|
31
|
+
## 2. Формат ответа
|
|
53
32
|
|
|
54
33
|
Ajax-контроллеры (`/bitrix/services/main/ajax.php`) отвечают HTTP 200 с конвертом:
|
|
55
34
|
|
|
@@ -59,217 +38,135 @@ Ajax-контроллеры (`/bitrix/services/main/ajax.php`) отвечают
|
|
|
59
38
|
"errors": [] } // ПУСТОЙ массив при успехе! (в JS [] — truthy)
|
|
60
39
|
```
|
|
61
40
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
41
|
+
Успех = `status:"success"` и/или **пустой** `errors`. Непустой `errors` / `status:"error"` / top-level
|
|
42
|
+
`error` — реальная ошибка (мост маппит её в `ok:false`). REST-эндпоинты (`/rest/*.json`) отвечают
|
|
43
|
+
иначе: `{ "result": …, "next", "total", "time" }` — без `errors`.
|
|
65
44
|
|
|
66
45
|
---
|
|
67
46
|
|
|
68
|
-
## 3.
|
|
69
|
-
|
|
70
|
-
У каждой записи каталога есть **`bodyType`** — как мост кодирует тело:
|
|
71
|
-
|
|
72
|
-
- **`bodyType: "form"`** (по умолчанию) — `application/x-www-form-urlencoded`; вложенность кодируется
|
|
73
|
-
PHP-стилем (`filter[STATUS]=2`, `select[0]=ID`). Объект в `params` рекурсивно разворачивается в такие
|
|
74
|
-
ключи. Классические `tasks.task.*`, мессенджер `im.v2.*`, REST — всё это форма.
|
|
75
|
-
- **`bodyType: "json"`** — тело уходит как `JSON.stringify(params)` с `Content-Type: application/json`.
|
|
76
|
-
Так работают **`tasks.v2.*`** (напр. `tasks.v2.Task.get` ждёт `{"task":{"id":N}}`) и `ui.entityselector.*`.
|
|
77
|
-
`params` при этом сохраняет вложенную структуру как есть (не разворачивается в bracket-ключи).
|
|
78
|
-
|
|
79
|
-
> Историческая заметка: раньше мост умел только форму, и `tasks.v2.*` были непригодны. Теперь
|
|
80
|
-
> поддержаны — в записи каталога ставь `"bodyType": "json"`, и мост отправит их корректно.
|
|
81
|
-
|
|
82
|
-
**CSRF / sessid:** мост всегда шлёт заголовок **`X-Bitrix-Csrf-Token: <sessid>`** (свежий
|
|
83
|
-
`BX.bitrix_sessid()`). Form-тела дополнительно несут `sessid` полем в теле (legacy REST это требует);
|
|
84
|
-
JSON-тела несут sessid **только** в заголовке. Ответ `invalid_csrf`/`invalid_authentication` = сессия
|
|
85
|
-
протухла (перелогинься в браузере).
|
|
86
|
-
|
|
87
|
-
| Метод (action / endpoint) | bodyType | Где `select`/`filter`/`order` | Пагинация |
|
|
88
|
-
|---|---|---|---|
|
|
89
|
-
| `tasks.task.list` | form | **верхний уровень**: `select[]`, `filter{}`, `order{}` | `start` (сдвиг, шаг 50) |
|
|
90
|
-
| `tasks.task.get` | form | верхний уровень: `taskId`, `select[]` | — |
|
|
91
|
-
| `tasks.v2.Task.get` | **json** | `{"task": {"id": N}}` | — |
|
|
92
|
-
| `tasks.v2.Scrum.getTaskInfo` | **json** | `{"taskId": N}` | — |
|
|
93
|
-
| `tasks.v2.File.listObjects` | **json** | `{"ids": [N,…]}` | — |
|
|
94
|
-
| `tasks.v2.Task.View.User.count` | **json** | `{"task": {"id": N}}` | — |
|
|
95
|
-
| `tasks.v2.Task.Relation.Child.list` | **json** | `{"taskId": N, "withIds":true, "navigation":{"size":N}}` | `navigation` |
|
|
96
|
-
| `tasks.v2.Task.Relation.Related.list` | **json** | `{"taskId": N, …}` (как Child) | `navigation` |
|
|
97
|
-
| `socialnetwork.api.workgroup.list` | form | **верхний уровень**: `select[]`, `order{}`, `filter{}` | `start` (шаг 50; `start=(N-1)*50`, `-1` — ответ без `total`) 📖 |
|
|
98
|
-
| `socialnetwork.api.workgroup.get` | form | **обёрнуто**: `params[groupId]`, `params[select][]` | — |
|
|
99
|
-
| `/rest/im.recent.list.json` | form (rest) | плоско: `LIMIT`, `SKIP_OPENLINES`, `UNREAD_ONLY`, … | `LIMIT` |
|
|
100
|
-
| `/rest/im.user.get.json` | form (rest) | плоско: `ID` | — |
|
|
101
|
-
| `/rest/im.chat.get.json` | form (rest) | плоско: `ENTITY_TYPE`, `ENTITY_ID` | — |
|
|
102
|
-
| `im.v2.Recent.load` | form | плоско: `limit`, `filter[recentSection]`, `filter[unread]`, `filter[parentId]` | `im.v2.Recent.tail` |
|
|
103
|
-
| `im.v2.Recent.tail` | form | плоско: `limit`, `filter[lastMessageDate]` (курсор), `filter[recentSection]` | `filter[lastMessageDate]` |
|
|
104
|
-
| `im.v2.Chat.load` | form | плоско: `dialogId`\|`chatId`, `messageLimit` | — |
|
|
105
|
-
| `im.v2.Chat.getDialogId` | form | плоско: `externalId` | — |
|
|
106
|
-
| `im.v2.Chat.Message.list` | form | плоско: `chatId`, `limit` | только последняя страница — назад **не листает**, используй `.tail` |
|
|
107
|
-
| `im.v2.Chat.Message.tail` | form | плоско: `chatId`, `limit`, `filter[lastId]`, `order[id]` | **см. раздел 6** |
|
|
108
|
-
| `im.v2.Chat.Message.read` | form | плоско: `chatId`, `ids[]`, `actionUuid` | — (мутирующий) |
|
|
109
|
-
| `im.v2.Chat.readAll` | form | без параметров | — (мутирующий) |
|
|
110
|
-
| `ui.entityselector.load` | **json** | `{"dialog": {entities,preselectedItems,…}}` | — |
|
|
111
|
-
| `ui.entityselector.doSearch` | **json** | `{"dialog": {…}, "searchQuery": {"query": "...", "queryWords": ["..."]}}` | — |
|
|
112
|
-
|
|
113
|
-
---
|
|
47
|
+
## 3. Каталог: имя → action → params
|
|
114
48
|
|
|
115
|
-
|
|
49
|
+
Форма `params` различается по методу: где-то плоско, где-то вложенно, где-то обёрнуто в `params[…]` —
|
|
50
|
+
колонка «params» показывает точную форму (кодирование тела мост берёт на себя).
|
|
116
51
|
|
|
117
|
-
| Имя (`
|
|
52
|
+
| Имя (`bitrix_call`) | action / endpoint | params | Пагинация |
|
|
118
53
|
|---|---|---|---|
|
|
119
|
-
| `tasks.list` | `tasks.task.list` |
|
|
120
|
-
| `task.get` | `tasks.task.get` |
|
|
121
|
-
| `task.v2.get` | `tasks.v2.Task.get` |
|
|
122
|
-
| `task.scrum.info` | `tasks.v2.Scrum.getTaskInfo` |
|
|
123
|
-
| `task.files` | `tasks.v2.File.listObjects` |
|
|
124
|
-
| `task.views.count` | `tasks.v2.Task.View.User.count` |
|
|
125
|
-
| `task.subtasks` | `tasks.v2.Task.Relation.Child.list` |
|
|
126
|
-
| `task.related` | `tasks.v2.Task.Relation.Related.list` |
|
|
127
|
-
| `projects.list` | `socialnetwork.api.workgroup.list` |
|
|
128
|
-
| `projects.get` | `socialnetwork.api.workgroup.get` |
|
|
129
|
-
| `chats.recent` | `/rest/im.recent.list.json` |
|
|
130
|
-
| `recent.load` | `im.v2.Recent.load` |
|
|
131
|
-
| `recent.tail` | `im.v2.Recent.tail` |
|
|
132
|
-
| `chat.load` | `im.v2.Chat.load` |
|
|
133
|
-
| `chat.dialogId` | `im.v2.Chat.getDialogId` |
|
|
134
|
-
| `chat.messages` | `im.v2.Chat.Message.list` |
|
|
135
|
-
| `chat.messages.tail` | `im.v2.Chat.Message.tail` |
|
|
136
|
-
| `chat.message.read` | `im.v2.Chat.Message.read` |
|
|
137
|
-
| `chat.read.all` | `im.v2.Chat.readAll` |
|
|
138
|
-
| `im.user.get` | `/rest/im.user.get.json` |
|
|
139
|
-
| `im.chat.get` | `/rest/im.chat.get.json` |
|
|
140
|
-
| `entityselector.load` | `ui.entityselector.load` |
|
|
141
|
-
| `entityselector.search` | `ui.entityselector.doSearch` |
|
|
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 |
|
|
142
78
|
|
|
143
79
|
---
|
|
144
80
|
|
|
145
|
-
##
|
|
81
|
+
## 4. Справочник полей
|
|
146
82
|
|
|
147
83
|
**Задача** (`tasks.task.*`) — поля для `select`/`filter` в UPPER_CASE: `ID`, `TITLE`, `DESCRIPTION`,
|
|
148
84
|
`STATUS`, `REAL_STATUS`, `RESPONSIBLE_ID`, `CREATED_BY`, `CREATED_DATE`, `CHANGED_DATE`, `DEADLINE`,
|
|
149
|
-
`CLOSED_DATE`, `PRIORITY`, `GROUP_ID`, `TAGS`, `TIME_ESTIMATE`, **`CHAT_ID`** (id
|
|
150
|
-
задачи — прямой резолвер `taskId → chatId`,
|
|
151
|
-
`
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
`
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
Статусы (`REAL_STATUS` — «настоящее» числовое состояние): `1` Новая · `2` Ждёт выполнения ·
|
|
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` Ждёт выполнения ·
|
|
161
95
|
`3` Выполняется · `4` Ждёт контроля · `5` Завершена · `6` Отложена · `7` Отклонена. Поле `STATUS`
|
|
162
96
|
поверх этого несёт **мета-состояния** отображения (почти просрочена / не просмотрена / просрочена),
|
|
163
|
-
поэтому «работает ли задача сейчас» фильтруй по **`REAL_STATUS`** (напр. `{"REAL_STATUS":3}`)
|
|
164
|
-
|
|
97
|
+
поэтому «работает ли задача сейчас» фильтруй по **`REAL_STATUS`** (напр. `{"REAL_STATUS":3}`) 📖.
|
|
98
|
+
Асимметрия: в **`filter`** работает `REAL_STATUS`, а в **`select`** его указывать бесполезно — поле молча
|
|
99
|
+
не вернётся; чтобы получить состояние, проси `select:["STATUS"]` → в ответе будет ключ `status` со
|
|
100
|
+
значением по той же шкале 1..7 (у обёртки `bitrix_tasks_list` он уже в дефолтном `select`).
|
|
165
101
|
|
|
166
|
-
**Фильтр `tasks.task.list
|
|
102
|
+
**Фильтр `tasks.task.list`** — оператор ставится перед именем поля: `!` (не равно / исключить),
|
|
167
103
|
`<`, `<=`, `>`, `>=`, `%` (LIKE-подстрока). Напр. `{"!REAL_STATUS":5}` — все **незакрытые**,
|
|
168
|
-
`{">=DEADLINE":"2026-07-01"}
|
|
169
|
-
|
|
170
|
-
`
|
|
171
|
-
`DEADLINE`, `*_DATE`, `UF_CRM_TASK`.
|
|
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`.
|
|
172
107
|
|
|
173
108
|
> **«Мои задачи» ≠ только `RESPONSIBLE_ID`.** Роли участника раздельны: ответственный
|
|
174
109
|
> (`RESPONSIBLE_ID`), соисполнитель (`ACCOMPLICE`), наблюдатель (`AUDITOR`), постановщик
|
|
175
|
-
> (`CREATED_BY`)
|
|
176
|
-
>
|
|
177
|
-
> в **`filter`** роли в **единственном** числе
|
|
178
|
-
> **множественном** (`ACCOMPLICES`/`AUDITORS`).
|
|
179
|
-
|
|
180
|
-
> **Scrum-задачи (спринты).** Поле `SPRINT_ID`
|
|
181
|
-
>
|
|
182
|
-
>
|
|
183
|
-
>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
**Группа/проект — список** (`workgroup.list`, camelCase в ответе): `ID`, `NAME`, `DESCRIPTION`,
|
|
191
|
-
`NUMBER_OF_MEMBERS`, `OWNER_ID`, `DATE_CREATE`, `PROJECT` (Y/N), `TYPE`.
|
|
192
|
-
📖 офиц. (`socialnetwork.api.workgroup.list`) — фильтруемые поля: `ID`, `NAME`, `OWNER_ID`, `ACTIVE`,
|
|
193
|
-
`VISIBLE`, `OPENED`, `CLOSED`, `PROJECT` (Y=проект/N=группа), `SUBJECT_ID`, `SITE_ID`, `DATE_CREATE`,
|
|
194
|
-
`DATE_UPDATE`, `DATE_ACTIVITY`; операторы фильтра `>= > <= < % =% %= !% != !`; без `select` вернётся
|
|
195
|
-
только `ID`. Старый публичный аналог — `sonet_group.get` (те же поля/пагинация, ключи `FILTER`/`ORDER`
|
|
196
|
-
капсом).
|
|
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
|
+
`>= > <= < % =% %= !% != !`.
|
|
197
125
|
**Группа — карточка** (`workgroup.get`): плюс `OWNER_DATA`, `SUBJECT_DATA`, `MEMBERS[]`,
|
|
198
|
-
`MODERATOR_MEMBERS[]`,
|
|
126
|
+
`MODERATOR_MEMBERS[]`, **`CHAT_ID`**, `DIALOG_ID`, `IMAGE_ID`, UF-поля.
|
|
199
127
|
|
|
200
128
|
**Недавний чат** (`im.recent.list`): `id` (это dialogId: число = юзер, `"chat"+N` = чат), `chat_id`,
|
|
201
|
-
`type` (`user`/`chat`), `title`, `message{id,text,file,author_id,date,status
|
|
202
|
-
|
|
203
|
-
`chat{… entity_type, entity_id, owner, …}
|
|
204
|
-
(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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-дата последнего элемента страницы).
|
|
135
|
+
|
|
208
136
|
**Карточка юзера** (`im.user.get`): `id`, `name`, `first_name`, `last_name`, `avatar`, `work_position`,
|
|
209
137
|
`gender`, `status`, `online` — резолв автора сообщения (`authorId`) в имя.
|
|
210
|
-
**Сообщение** (`im.v2.Chat.Message.*`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`.
|
|
211
|
-
Ответ также несёт `users[]` (участники), `additionalMessages[]`, `hasPrevPage`/`hasNextPage`.
|
|
212
|
-
`chatId` берётся из `im.recent.list` (`chat_id`) или из `workgroup.get` (`CHAT_ID`/`DIALOG_ID`).
|
|
213
|
-
|
|
214
|
-
**Адресация чата (`chat.load`): `dialogId` vs `chatId`.** `chat.load` принимает любой из двух:
|
|
215
|
-
- `dialogId` = **ID пользователя** (число, напр. `11`) → **личный чат 1-на-1** с этим пользователем.
|
|
216
|
-
- `dialogId` = **`"chat"+CHAT_ID`** (напр. `"chat7111"`) → **групповой чат** (проекта/задачи/канала).
|
|
217
|
-
- `chatId` = числовой `CHAT_ID` — то же, что групповой `dialogId`, но без префикса.
|
|
218
138
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
`im.v2.Chat.load` надёжно принимает `dialogId`/`chatId`, а форму `sgXXX` для него сверь реверсом —
|
|
222
|
-
гарантированный путь к чату группы это `bitrix_chat_get_dialog_id { externalId:"sg<groupId>" }`.
|
|
223
|
-
|
|
224
|
-
Ответ `chat.load` возвращает числовой `chatId` — используй его дальше для `chat.messages` /
|
|
225
|
-
`chat.messages.tail` (листание вглубь) и `chat.message.read` (эти три работают по `chatId`, не по `dialogId`).
|
|
139
|
+
**Сообщение** (`im.v2.Chat.Message.*`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`;
|
|
140
|
+
ответ также несёт `users[]` (участники), `additionalMessages[]`, `hasPrevPage`/`hasNextPage`.
|
|
226
141
|
|
|
227
142
|
---
|
|
228
143
|
|
|
229
|
-
##
|
|
144
|
+
## 5. Сценарии
|
|
230
145
|
|
|
231
146
|
```jsonc
|
|
232
147
|
// открытые задачи ответственного 55, по дедлайну, вторая страница
|
|
233
148
|
bitrix_tasks_list { "params": { "filter": {"RESPONSIBLE_ID":55,"REAL_STATUS":2},
|
|
234
149
|
"order": {"DEADLINE":"asc"}, "start": 50 } }
|
|
235
150
|
|
|
236
|
-
// карточка задачи с расширенным набором
|
|
237
|
-
bitrix_task_get
|
|
238
|
-
|
|
239
|
-
// карточка задачи через v2-подсистему (JSON-тело; id вложен в task)
|
|
151
|
+
// карточка задачи с расширенным набором полей; v2-подсистема; файлы, просмотры, связи
|
|
152
|
+
bitrix_task_get { "taskId": 4229, "params": { "select": ["ID","TITLE","DESCRIPTION","TAGS"] } }
|
|
240
153
|
bitrix_task_get_v2 { "taskId": 4229 } // → { "task": { "id": 4229 } }
|
|
241
|
-
bitrix_call { "name": "task.v2.get", "params": { "task": { "id": 4229 } } }
|
|
242
|
-
|
|
243
|
-
// файлы задачи и счётчик просмотров (v2, JSON-тело); подзадачи/связанные
|
|
244
154
|
bitrix_call { "name": "task.files", "params": { "ids": [4229] } }
|
|
245
|
-
bitrix_call { "name": "task.views.count", "params": { "task": { "id": 4229 } } }
|
|
246
155
|
bitrix_task_subtasks { "taskId": 4229 }
|
|
247
156
|
bitrix_task_related { "taskId": 4229 }
|
|
248
157
|
|
|
249
|
-
// проекты по дате создания; полная карточка группы
|
|
158
|
+
// проекты по дате создания; полная карточка группы (отдаёт CHAT_ID / DIALOG_ID)
|
|
250
159
|
bitrix_projects_list { "params": { "order": {"DATE_CREATE":"desc"} } }
|
|
251
|
-
bitrix_project_get
|
|
252
|
-
|
|
253
|
-
// открыть чат (первые сообщения)
|
|
254
|
-
bitrix_chat_load { "chatId": 485, "messageLimit": 50 }
|
|
255
|
-
|
|
256
|
-
// ЛИСТАНИЕ ИСТОРИИ ВГЛУБЬ (старые сообщения): beforeId = минимальный id сообщения
|
|
257
|
-
// из текущей страницы. Повторяй, уменьшая beforeId, до начала истории (order DESC — дефолт).
|
|
258
|
-
bitrix_chat_history { "chatId": 485, "beforeId": 1861279 }
|
|
259
|
-
|
|
260
|
-
// пометить сообщения прочитанными (МУТИРУЮЩИЙ). actionUuid генерируется автоматически.
|
|
261
|
-
bitrix_chat_mark_read { "chatId": 40271, "ids": [1884131] }
|
|
160
|
+
bitrix_project_get { "groupId": 15 }
|
|
262
161
|
```
|
|
263
162
|
|
|
264
|
-
###
|
|
163
|
+
### 5.1. Мессенджер (IM): как получить `chatId`
|
|
265
164
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
`bitrix_chat_history`, `bitrix_chat_mark_read`). Прежде чем читать сообщения, нужно **получить
|
|
270
|
-
`chatId`** — почти любая ошибка агента здесь именно в этом шаге.
|
|
165
|
+
Всё общение — **чаты**: личные 1-на-1, групповые (проектов, каналов) и **чаты-обсуждения задач**.
|
|
166
|
+
Числовой **`chatId`** — ключ ко всем операциям с сообщениями (`bitrix_chat_messages`,
|
|
167
|
+
`bitrix_chat_history`, `bitrix_chat_mark_read`); почти любая ошибка агента здесь — на шаге его получения.
|
|
271
168
|
|
|
272
|
-
**Три идентификатора одного чата** (
|
|
169
|
+
**Три идентификатора одного чата** (годятся для разных операций):
|
|
273
170
|
|
|
274
171
|
| Идентификатор | Что это | Пример | Куда передавать |
|
|
275
172
|
|---|---|---|---|
|
|
@@ -277,173 +174,161 @@ bitrix_chat_mark_read { "chatId": 40271, "ids": [1884131] }
|
|
|
277
174
|
| `dialogId` | адрес для ОТКРЫТИЯ: `userId` (личный 1-на-1) **или** `"chat"+chatId` (групповой) | `11`, `"chat485"` | `bitrix_chat_load` |
|
|
278
175
|
| `externalId` | внешний ключ сущности → резолвится в `dialogId` | `"sg15"` (соцгруппа 15) | `bitrix_chat_get_dialog_id` |
|
|
279
176
|
|
|
280
|
-
Ключевое: **личный чат с человеком = его `userId` в роли `dialogId`** (отдельного «id чата» у
|
|
281
|
-
|
|
282
|
-
|
|
177
|
+
Ключевое: **личный чат с человеком = его `userId` в роли `dialogId`** (отдельного «id чата» у 1-на-1
|
|
178
|
+
знать не нужно). `bitrix_chat_load` принимает `dialogId` **или** `chatId` и всегда **возвращает
|
|
179
|
+
числовой `chatId`** — дальше оперируешь им.
|
|
283
180
|
|
|
284
|
-
**Как получить `chatId` — по
|
|
181
|
+
**Как получить `chatId` — по цели:**
|
|
285
182
|
|
|
286
183
|
| Хочу открыть чат… | Шаги |
|
|
287
184
|
|---|---|
|
|
288
|
-
| **с пользователем по ИМЕНИ** | `bitrix_entity_search { query:"дмитрий" }` →
|
|
185
|
+
| **с пользователем по ИМЕНИ** | `bitrix_entity_search { query:"дмитрий" }` → диалог с `dialogId` = его userId → `bitrix_chat_load { dialogId }` |
|
|
289
186
|
| **с пользователем, id известен** | `bitrix_chat_load { dialogId: <userId> }` (userId — из задачи `RESPONSIBLE_ID`/`CREATED_BY`, из `bitrix_chats_recent`, из `bitrix_user_get`) |
|
|
290
|
-
| **обсуждение ЗАДАЧИ** | `
|
|
187
|
+
| **обсуждение ЗАДАЧИ** | `bitrix_task_get` → `CHAT_ID` (§5.2) |
|
|
291
188
|
| **ПРОЕКТА/группы** | `bitrix_task_get`→`GROUP_ID`→`bitrix_project_get`→`CHAT_ID`; либо `bitrix_chat_get_dialog_id { externalId:"sg<groupId>" }` |
|
|
292
|
-
| **не знаю точно, какой** | `bitrix_chats_recent` (недавние, сопоставь по `title`/`user`) или `bitrix_entity_search { query }`
|
|
189
|
+
| **не знаю точно, какой** | `bitrix_chats_recent` (недавние, сопоставь по `title`/`user`) или `bitrix_entity_search { query }` |
|
|
293
190
|
|
|
294
191
|
> Почему не «просто список чатов»: `bitrix_chats_recent` (`im.recent.list`) отдаёт лишь недавние и
|
|
295
192
|
> **без чатов задач**. Для поиска конкретного чата — `bitrix_entity_search`; для чатов задач —
|
|
296
193
|
> `bitrix_recent_load { section:"tasksTask" }`.
|
|
297
194
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
`hasPrevPage`/`hasNextPage`, что **совпадает по форме** с нашими внутренними `im.v2.Chat.Message.*`.
|
|
302
|
-
|
|
303
|
-
### 6.2. Сценарий: чат с конкретным пользователем
|
|
304
|
-
|
|
305
|
-
Личный чат 1-на-1 адресуется `dialogId` = **ID пользователя**. Id берётся из задачи
|
|
306
|
-
(`RESPONSIBLE_ID`/`CREATED_BY`), из `bitrix_chats_recent`, либо резолвится поиском по имени (§6.4).
|
|
195
|
+
**Читать историю.** `bitrix_chat_messages` отдаёт только последнюю страницу; вглубь листает
|
|
196
|
+
`bitrix_chat_history` по курсору `beforeId` = минимальный id сообщения текущей страницы (повторяй,
|
|
197
|
+
уменьшая `beforeId`, до начала истории).
|
|
307
198
|
|
|
308
199
|
```jsonc
|
|
309
|
-
|
|
310
|
-
bitrix_chat_load { "dialogId": 11 }
|
|
311
|
-
// 2) листать его историю вглубь по chatId из ответа шага 1
|
|
200
|
+
bitrix_chat_load { "dialogId": 11 } // личный чат юзера 11 → в ответе chatId
|
|
312
201
|
bitrix_chat_history { "chatId": 485, "beforeId": 1861279 }
|
|
202
|
+
bitrix_chat_mark_read { "chatId": 40271, "ids": [1884131] } // ⚠ мутирующий; actionUuid — автоматом
|
|
313
203
|
```
|
|
314
204
|
|
|
315
|
-
|
|
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.*`.
|
|
316
209
|
|
|
317
|
-
|
|
318
|
-
поиск по названию — запасной.
|
|
210
|
+
### 5.2. Обсуждение (чат) конкретной задачи
|
|
319
211
|
|
|
320
|
-
**А. Прямой —
|
|
321
|
-
|
|
322
|
-
названию и без риска промаха по тёзкам-заголовкам:
|
|
212
|
+
**А. Прямой резолв — предпочтительно.** `bitrix_task_get` возвращает `CHAT_ID` чата-обсуждения (входит
|
|
213
|
+
в дефолтный `select` 📖) — это `taskId → chatId` без поиска по названию и без промаха по тёзкам:
|
|
323
214
|
|
|
324
215
|
```jsonc
|
|
325
|
-
bitrix_task_get { "taskId": 28373 } // в ответе CHAT_ID
|
|
216
|
+
bitrix_task_get { "taskId": 28373 } // в ответе CHAT_ID
|
|
326
217
|
bitrix_chat_load { "chatId": <CHAT_ID> }
|
|
327
218
|
bitrix_chat_history { "chatId": <CHAT_ID>, "beforeId": <мин id страницы> }
|
|
328
219
|
```
|
|
329
220
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
`
|
|
333
|
-
`entityId=<taskId>`; так же адресуются чаты `SONET_GROUP` (группа/проект), `CRM`, `CALENDAR`, `MAIL`,
|
|
334
|
-
`VIDEOCONF`, `CALL`:
|
|
221
|
+
Общая альтернатива — **`bitrix_entity_chat`** (обёртка над `im.chat.get`): chatId любого связанного
|
|
222
|
+
объекта по паре `entityType`/`entityId`; кроме `TASKS_TASK` так же адресуются `SONET_GROUP`
|
|
223
|
+
(группа/проект), `CRM`, `CALENDAR`, `MAIL`, `VIDEOCONF`, `CALL`:
|
|
335
224
|
|
|
336
225
|
```jsonc
|
|
337
226
|
bitrix_entity_chat { "entityType": "TASKS_TASK", "entityId": 28373 } // → chatId
|
|
338
|
-
bitrix_chat_load { "chatId": <chatId> }
|
|
339
227
|
```
|
|
340
228
|
|
|
341
|
-
**Б. Через список чатов
|
|
342
|
-
(в `bitrix_chats_recent` / `/rest/im.recent.list` их НЕТ). Листать вглубь — `bitrix_recent_tail`.
|
|
229
|
+
**Б. Через список чатов задач** (секция `tasksTask`; в `bitrix_chats_recent` их НЕТ):
|
|
343
230
|
|
|
344
231
|
```jsonc
|
|
345
232
|
bitrix_recent_load { "section": "tasksTask" } // → чаты задач с chatId/dialogId
|
|
346
233
|
bitrix_recent_tail { "section": "tasksTask", "lastMessageDate": "2026-06-29T17:25:38+03:00" }
|
|
347
|
-
//
|
|
348
|
-
bitrix_chat_load { "dialogId": "chat38849" } // или { "chatId": 38849 }
|
|
349
|
-
bitrix_chat_history { "chatId": 38849, "beforeId": 1722353 }
|
|
234
|
+
bitrix_chat_load { "dialogId": "chat38849" } // или { "chatId": 38849 }
|
|
350
235
|
```
|
|
351
236
|
|
|
352
|
-
**В. Поиском по названию задачи**
|
|
353
|
-
|
|
354
|
-
```jsonc
|
|
355
|
-
bitrix_entity_search { "query": "трекер", "section": "tasksTask" }
|
|
356
|
-
```
|
|
237
|
+
**В. Поиском по названию задачи** (запасной, матч по тексту):
|
|
238
|
+
`bitrix_entity_search { "query": "трекер", "section": "tasksTask" }`
|
|
357
239
|
|
|
358
240
|
**Чат ПРОЕКТА задачи** (обсуждение группы, не самой задачи) — через `GROUP_ID`:
|
|
359
241
|
|
|
360
242
|
```jsonc
|
|
361
243
|
bitrix_task_get { "taskId": 4229, "params": { "select": ["ID","GROUP_ID"] } }
|
|
362
244
|
bitrix_project_get { "groupId": 15 } // отдаёт CHAT_ID / DIALOG_ID
|
|
363
|
-
bitrix_chat_load { "dialogId": "chat7111" }
|
|
364
245
|
bitrix_chat_get_dialog_id { "externalId": "sg15" } // либо резолв dialogId соцгруппы напрямую
|
|
365
246
|
```
|
|
366
247
|
|
|
367
|
-
###
|
|
248
|
+
### 5.3. Поиск чата / пользователя
|
|
368
249
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
`user.get`/`user.search`, для чатов — `im.recent.list` + фильтрация на стороне агента.
|
|
250
|
+
`bitrix_entity_search` (обёртка над внутренним `ui.entityselector.doSearch`) сам собирает диалог
|
|
251
|
+
`IM_CHAT_SEARCH` и `searchQuery` из строки. Публичные аналоги: люди — `user.get`/`user.search`, чаты —
|
|
252
|
+
`im.recent.list` + фильтрация у себя.
|
|
373
253
|
|
|
374
254
|
```jsonc
|
|
375
255
|
bitrix_entity_search { "query": "дмитрий" } // среди всех чатов/диалогов
|
|
376
256
|
bitrix_entity_search { "query": "трекер", "section": "tasksTask" } // среди чатов задач
|
|
257
|
+
bitrix_user_get { "userId": 11 } // резолв authorId → имя
|
|
377
258
|
```
|
|
378
259
|
|
|
379
|
-
|
|
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 }`.
|
|
380
268
|
|
|
381
269
|
```jsonc
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
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 } // продолжить вглубь
|
|
385
272
|
```
|
|
386
273
|
|
|
387
|
-
|
|
274
|
+
Ответ: `calls[] { callId, startedAt, startedBy, startMessageId, events[], summaryMessageId }` плюс
|
|
275
|
+
`scannedPages` / `oldestScannedMessageId` / **`reachedHistoryStart`** — если он `false`, история
|
|
276
|
+
дочитана НЕ до конца, продолжай с `beforeId: <oldestScannedMessageId>`.
|
|
388
277
|
|
|
389
|
-
|
|
390
|
-
- **im.v2-чат** — структурный JSON, читается уже сейчас (§6.3); обычно этого достаточно.
|
|
391
|
-
- **Форумные легаси-комментарии** — рендерятся как **HTML** в side-slider'е. Это отдельный
|
|
392
|
-
«HTML-поддомен» внутреннего API, который **текущий мост не поддерживает** (блокеры ниже).
|
|
278
|
+
**2) Взять всё по звонку.**
|
|
393
279
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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" } // всегда в файл, даже короткая
|
|
398
285
|
```
|
|
399
286
|
|
|
400
|
-
|
|
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
|
+
сессии пользователя.
|
|
401
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 }
|
|
402
320
|
```
|
|
403
|
-
POST /bitrix/services/main/ajax.php?mode=class&c=bitrix:forum.comments&action=navigateComment
|
|
404
|
-
form: AJAX_POST=Y, ENTITY_XML_ID=TASK_<taskId>, taskId=<taskId>, MODE=LIST,
|
|
405
|
-
FILTER[<ID]=<курсор: id, ДО которого грузить старые>, PAGEN_1=1,
|
|
406
|
-
signedParameters=<подписанный blob>, IFRAME=Y, IFRAME_TYPE=SIDE_SLIDER
|
|
407
|
-
headers: bx-ajax: true, x-bitrix-site-id: s1 (плюс обычный X-Bitrix-Csrf-Token)
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
`signedParameters` — base64 PHP-массива + HMAC-подпись сервера; кодирует
|
|
411
|
-
`FORUM_ID, ENTITY_TYPE=TK, ENTITY_ID=<taskId>, ENTITY_XML_ID=TASK_<taskId>, …`. **Подделать нельзя** —
|
|
412
|
-
берётся из HTML первой страницы.
|
|
413
|
-
|
|
414
|
-
**Почему пока не в каталоге (3 блокера):**
|
|
415
|
-
1. Мост парсит ответ только как JSON (`extension/src/connector.ts` → `resp.json()`) — HTML бросит исключение.
|
|
416
|
-
2. Path-параметр в URL (`/task/comments/<taskId>/`) — записи каталога статичны, шаблонов пути нет.
|
|
417
|
-
3. `signedParameters` + заголовки `bx-ajax`/`x-bitrix-site-id` — нужен предварительный GET HTML-страницы.
|
|
418
321
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
322
|
+
Ответ — **путь, а не содержимое**: `{ path, bytes, contentType, fileName }` (гигабайты не попадают в
|
|
323
|
+
контекст). По умолчанию файл ложится в `~/.bitrix24-mcp-bridge/downloads/`; `savePath` без ведущего `/`
|
|
324
|
+
трактуется как имя внутри этой папки. Потолок — 512 МБ.
|
|
422
325
|
|
|
423
|
-
|
|
424
|
-
|
|
326
|
+
Тонкости:
|
|
327
|
+
- **Граница безопасности — origin**: скачивается только с origin сконфигурированного портала.
|
|
328
|
+
- **Имя даёт портал** (`Content-Disposition`), поэтому без `savePath` угадывать его не нужно.
|
|
329
|
+
- **Повторное скачивание** того же файла падает с `already exists` — передай `overwrite: true`.
|
|
425
330
|
|
|
426
331
|
---
|
|
427
332
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
> Совет: сперва проверь, задокументирован ли метод в 📖 [`apidocs.bitrix24.com`](https://apidocs.bitrix24.com)
|
|
431
|
-
> (context7: `/bitrix24/b24restdocs`). Для публичных `*.list`/`*.get` там готовые поля, фильтры и
|
|
432
|
-
> пагинация — реверс тогда нужен лишь чтобы подтвердить транспорт (form/json) и конверт ответа. Для
|
|
433
|
-
> внутренних `im.v2.*`/`tasks.v2.*`/UI-методов доков нет — только реверс (`docs/reconnaissance.md`).
|
|
434
|
-
|
|
435
|
-
1. Сними реальные вызовы (`docs/reconnaissance.md` — авто-запись `bun run capture` или HAR →
|
|
436
|
-
`bun run catalog:draft`). Черновик `actions.draft.json` накапливает **все уникальные комбинации
|
|
437
|
-
параметров** на экшен и проставляет `bodyType` (form/json) автоматически.
|
|
438
|
-
2. Определи транспорт по разделу 3: если черновик показал `"bodyType": "json"` — перенеси его в запись.
|
|
439
|
-
3. Добавь запись в `actions.json` (может быть мутирующей — каталог это допускает).
|
|
440
|
-
4. Если удобно — оберни в типизированный инструмент в `src/tools/register.ts` (обёртка регистрируется
|
|
441
|
-
только если её `catalogName` есть в каталоге).
|
|
442
|
-
5. **`bun run sync:runtime`** — доставь изменения живому daemon: пересобрать бандл, скопировать
|
|
443
|
-
репо-`actions.json` в `~/.bitrix24-mcp-bridge/actions.json` и погасить daemon. Без этого агент
|
|
444
|
-
новых имён/инструментов не увидит (daemon читает runtime-папку, не репо). Проверь через
|
|
445
|
-
`bitrix_call { name, params }` или новую обёртку.
|
|
446
|
-
|
|
447
|
-
> `actions.json` / `actions.draft.json` — данные конкретного портала (gitignored). Этот файл
|
|
448
|
-
> (`api-notes.md`) — переносимые знания о самом API; держи его в актуальном состоянии при добавлении
|
|
449
|
-
> новых методов.
|
|
333
|
+
Расширение каталога новыми методами, транспорт (`bodyType`, `responseType`, path-параметры) и
|
|
334
|
+
доставка правок в живой daemon — `docs/reconnaissance.md`.
|