bitrix24-mcp-bridge 0.3.0 → 0.5.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 +5 -1
- package/dist/cli.js +5236 -278
- package/docs/api-notes.md +233 -310
- 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,137 @@ 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 |
|
|
78
|
+
| `task.comments.page` | `GET /task/comments/{taskId}/` | `taskId` (в путь), `IFRAME=Y`, `IFRAME_TYPE=SIDE_SLIDER` → HTML старых комментариев | см. §5.6 |
|
|
79
|
+
| `task.comments.navigate` | `bitrix:forum.comments` → `navigateComment` | форма: `ENTITY_XML_ID=TASK_<id>`, `EXEMPLAR_ID`, `signedParameters` (оба — из HTML первой страницы), `FILTER[<ID]` | `FILTER[<ID]` — курсор; проще `bitrix_task_comments` (§5.6) |
|
|
142
80
|
|
|
143
81
|
---
|
|
144
82
|
|
|
145
|
-
##
|
|
83
|
+
## 4. Справочник полей
|
|
146
84
|
|
|
147
85
|
**Задача** (`tasks.task.*`) — поля для `select`/`filter` в UPPER_CASE: `ID`, `TITLE`, `DESCRIPTION`,
|
|
148
86
|
`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` Ждёт выполнения ·
|
|
87
|
+
`CLOSED_DATE`, `PRIORITY`, `GROUP_ID`, `TAGS`, `TIME_ESTIMATE`, **`CHAT_ID`** (id чата-обсуждения
|
|
88
|
+
задачи — прямой резолвер `taskId → chatId`, §5.2), `UF_*`. Ответ подкладывает объекты `group`,
|
|
89
|
+
`responsible`, `creator`, `action`.
|
|
90
|
+
📖 (`tasks.task.get`/`list`) — сверх этого: `parentId`, `stageId`, `sprintId`, `backlogId`,
|
|
91
|
+
`commentsCount`, `timeSpentInLogs`, `favorite`, `flowId`, `mark`, `accomplices[]`, `auditors[]`,
|
|
92
|
+
`checklist{}`, `subStatus`; в официальном REST-ответе поля приходят **camelCase**, а в `select`/`filter`
|
|
93
|
+
они всегда **UPPER_CASE**. Доп. вычисления — флагами `params{WITH_TIMER_INFO, WITH_RESULT_INFO,
|
|
94
|
+
WITH_PARSED_DESCRIPTION}`.
|
|
95
|
+
|
|
96
|
+
**Статусы** (`REAL_STATUS` — «настоящее» числовое состояние): `1` Новая · `2` Ждёт выполнения ·
|
|
161
97
|
`3` Выполняется · `4` Ждёт контроля · `5` Завершена · `6` Отложена · `7` Отклонена. Поле `STATUS`
|
|
162
98
|
поверх этого несёт **мета-состояния** отображения (почти просрочена / не просмотрена / просрочена),
|
|
163
|
-
поэтому «работает ли задача сейчас» фильтруй по **`REAL_STATUS`** (напр. `{"REAL_STATUS":3}`)
|
|
164
|
-
|
|
99
|
+
поэтому «работает ли задача сейчас» фильтруй по **`REAL_STATUS`** (напр. `{"REAL_STATUS":3}`) 📖.
|
|
100
|
+
Асимметрия: в **`filter`** работает `REAL_STATUS`, а в **`select`** его указывать бесполезно — поле молча
|
|
101
|
+
не вернётся; чтобы получить состояние, проси `select:["STATUS"]` → в ответе будет ключ `status` со
|
|
102
|
+
значением по той же шкале 1..7 (у обёртки `bitrix_tasks_list` он уже в дефолтном `select`).
|
|
165
103
|
|
|
166
|
-
**Фильтр `tasks.task.list
|
|
104
|
+
**Фильтр `tasks.task.list`** — оператор ставится перед именем поля: `!` (не равно / исключить),
|
|
167
105
|
`<`, `<=`, `>`, `>=`, `%` (LIKE-подстрока). Напр. `{"!REAL_STATUS":5}` — все **незакрытые**,
|
|
168
|
-
`{">=DEADLINE":"2026-07-01"}
|
|
169
|
-
|
|
170
|
-
`
|
|
171
|
-
`DEADLINE`, `*_DATE`, `UF_CRM_TASK`.
|
|
106
|
+
`{">=DEADLINE":"2026-07-01"}`, `{"%TITLE":"карта"}`. Фильтруемые поля: `ID`, `PARENT_ID`, `GROUP_ID`,
|
|
107
|
+
`CREATED_BY`, `RESPONSIBLE_ID`, `ACCOMPLICE`, `AUDITOR`, `REAL_STATUS`, `STATUS`, `PRIORITY`, `TAG`,
|
|
108
|
+
`STAGE_ID`, `SPRINT_ID`, `BACKLOG_ID`, `DEADLINE`, `*_DATE`, `UF_CRM_TASK`.
|
|
172
109
|
|
|
173
110
|
> **«Мои задачи» ≠ только `RESPONSIBLE_ID`.** Роли участника раздельны: ответственный
|
|
174
111
|
> (`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
|
-
капсом).
|
|
112
|
+
> (`CREATED_BY`); единого «любая роль» поля в `list` нет — чтобы собрать **все** свои задачи, объедини
|
|
113
|
+
> результаты нескольких вызовов (минимум `RESPONSIBLE_ID` + `ACCOMPLICE`) и **дедуплицируй по `id`** —
|
|
114
|
+
> одна задача часто попадает сразу в несколько ролей. В **`filter`** роли в **единственном** числе
|
|
115
|
+
> (`ACCOMPLICE`/`AUDITOR`), в **`select`** — во **множественном** (`ACCOMPLICES`/`AUDITORS`).
|
|
116
|
+
|
|
117
|
+
> **Scrum-задачи (спринты).** Поле `SPRINT_ID` задачи и жёсткий `filter[SPRINT_ID]` могут расходиться:
|
|
118
|
+
> задача числится за спринтом по своему полю, но не попадает в выборку по фильтру (возвращена в бэклог
|
|
119
|
+
> или в иной stage). Если по фильтру «маловато» — перепроверь без него и отфильтруй по
|
|
120
|
+
> `SPRINT_ID`/`STAGE_ID` у себя. Метода «колонки scrum-доски» (`STAGE_ID` → имя колонки) в каталоге нет.
|
|
121
|
+
|
|
122
|
+
**Группа/проект — список** (`workgroup.list`): `ID`, `NAME`, `DESCRIPTION`, `NUMBER_OF_MEMBERS`,
|
|
123
|
+
`OWNER_ID`, `DATE_CREATE`, `PROJECT` (Y=проект/N=группа), `TYPE`; **без `select` вернётся только `ID`**.
|
|
124
|
+
📖 фильтруемые: `ID`, `NAME`, `OWNER_ID`, `ACTIVE`, `VISIBLE`, `OPENED`, `CLOSED`, `PROJECT`,
|
|
125
|
+
`SUBJECT_ID`, `SITE_ID`, `DATE_CREATE`, `DATE_UPDATE`, `DATE_ACTIVITY`; операторы фильтра
|
|
126
|
+
`>= > <= < % =% %= !% != !`.
|
|
197
127
|
**Группа — карточка** (`workgroup.get`): плюс `OWNER_DATA`, `SUBJECT_DATA`, `MEMBERS[]`,
|
|
198
|
-
`MODERATOR_MEMBERS[]`,
|
|
128
|
+
`MODERATOR_MEMBERS[]`, **`CHAT_ID`**, `DIALOG_ID`, `IMAGE_ID`, UF-поля.
|
|
199
129
|
|
|
200
130
|
**Недавний чат** (`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
|
-
|
|
131
|
+
`type` (`user`/`chat`), `title`, `message{id,text,file,author_id,date,status}`, `counter` (непрочитанных),
|
|
132
|
+
`unread`, `last_id`, `pinned`, `date_update`, `date_last_activity`, `user{…}`,
|
|
133
|
+
`chat{… entity_type, entity_id, owner, …}`; есть ли ещё страницы — `hasMore`/`hasMorePages` 📖.
|
|
134
|
+
**Недавние по секции** (`im.v2.Recent.load`): `filter[recentSection]` — `default` (обычные диалоги),
|
|
135
|
+
**`tasksTask` (чаты задач)**, `collab`/`collabDefault`. Вглубь — `im.v2.Recent.tail` с курсором
|
|
136
|
+
`filter[lastMessageDate]` (ISO-дата последнего элемента страницы).
|
|
137
|
+
|
|
208
138
|
**Карточка юзера** (`im.user.get`): `id`, `name`, `first_name`, `last_name`, `avatar`, `work_position`,
|
|
209
139
|
`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
140
|
|
|
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`).
|
|
141
|
+
**Сообщение** (`im.v2.Chat.Message.*`): `id`, `chatId`, `authorId`, `date`, `text`, `params`, `viewed`;
|
|
142
|
+
ответ также несёт `users[]` (участники), `additionalMessages[]`, `hasPrevPage`/`hasNextPage`.
|
|
226
143
|
|
|
227
144
|
---
|
|
228
145
|
|
|
229
|
-
##
|
|
146
|
+
## 5. Сценарии
|
|
230
147
|
|
|
231
148
|
```jsonc
|
|
232
149
|
// открытые задачи ответственного 55, по дедлайну, вторая страница
|
|
233
150
|
bitrix_tasks_list { "params": { "filter": {"RESPONSIBLE_ID":55,"REAL_STATUS":2},
|
|
234
151
|
"order": {"DEADLINE":"asc"}, "start": 50 } }
|
|
235
152
|
|
|
236
|
-
// карточка задачи с расширенным набором
|
|
237
|
-
bitrix_task_get
|
|
238
|
-
|
|
239
|
-
// карточка задачи через v2-подсистему (JSON-тело; id вложен в task)
|
|
153
|
+
// карточка задачи с расширенным набором полей; v2-подсистема; файлы, просмотры, связи
|
|
154
|
+
bitrix_task_get { "taskId": 4229, "params": { "select": ["ID","TITLE","DESCRIPTION","TAGS"] } }
|
|
240
155
|
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
156
|
bitrix_call { "name": "task.files", "params": { "ids": [4229] } }
|
|
245
|
-
bitrix_call { "name": "task.views.count", "params": { "task": { "id": 4229 } } }
|
|
246
157
|
bitrix_task_subtasks { "taskId": 4229 }
|
|
247
158
|
bitrix_task_related { "taskId": 4229 }
|
|
248
159
|
|
|
249
|
-
// проекты по дате создания; полная карточка группы
|
|
160
|
+
// проекты по дате создания; полная карточка группы (отдаёт CHAT_ID / DIALOG_ID)
|
|
250
161
|
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] }
|
|
162
|
+
bitrix_project_get { "groupId": 15 }
|
|
262
163
|
```
|
|
263
164
|
|
|
264
|
-
###
|
|
165
|
+
### 5.1. Мессенджер (IM): как получить `chatId`
|
|
265
166
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
`bitrix_chat_history`, `bitrix_chat_mark_read`). Прежде чем читать сообщения, нужно **получить
|
|
270
|
-
`chatId`** — почти любая ошибка агента здесь именно в этом шаге.
|
|
167
|
+
Всё общение — **чаты**: личные 1-на-1, групповые (проектов, каналов) и **чаты-обсуждения задач**.
|
|
168
|
+
Числовой **`chatId`** — ключ ко всем операциям с сообщениями (`bitrix_chat_messages`,
|
|
169
|
+
`bitrix_chat_history`, `bitrix_chat_mark_read`); почти любая ошибка агента здесь — на шаге его получения.
|
|
271
170
|
|
|
272
|
-
**Три идентификатора одного чата** (
|
|
171
|
+
**Три идентификатора одного чата** (годятся для разных операций):
|
|
273
172
|
|
|
274
173
|
| Идентификатор | Что это | Пример | Куда передавать |
|
|
275
174
|
|---|---|---|---|
|
|
@@ -277,173 +176,197 @@ bitrix_chat_mark_read { "chatId": 40271, "ids": [1884131] }
|
|
|
277
176
|
| `dialogId` | адрес для ОТКРЫТИЯ: `userId` (личный 1-на-1) **или** `"chat"+chatId` (групповой) | `11`, `"chat485"` | `bitrix_chat_load` |
|
|
278
177
|
| `externalId` | внешний ключ сущности → резолвится в `dialogId` | `"sg15"` (соцгруппа 15) | `bitrix_chat_get_dialog_id` |
|
|
279
178
|
|
|
280
|
-
Ключевое: **личный чат с человеком = его `userId` в роли `dialogId`** (отдельного «id чата» у
|
|
281
|
-
|
|
282
|
-
|
|
179
|
+
Ключевое: **личный чат с человеком = его `userId` в роли `dialogId`** (отдельного «id чата» у 1-на-1
|
|
180
|
+
знать не нужно). `bitrix_chat_load` принимает `dialogId` **или** `chatId` и всегда **возвращает
|
|
181
|
+
числовой `chatId`** — дальше оперируешь им.
|
|
283
182
|
|
|
284
|
-
**Как получить `chatId` — по
|
|
183
|
+
**Как получить `chatId` — по цели:**
|
|
285
184
|
|
|
286
185
|
| Хочу открыть чат… | Шаги |
|
|
287
186
|
|---|---|
|
|
288
|
-
| **с пользователем по ИМЕНИ** | `bitrix_entity_search { query:"дмитрий" }` →
|
|
187
|
+
| **с пользователем по ИМЕНИ** | `bitrix_entity_search { query:"дмитрий" }` → диалог с `dialogId` = его userId → `bitrix_chat_load { dialogId }` |
|
|
289
188
|
| **с пользователем, id известен** | `bitrix_chat_load { dialogId: <userId> }` (userId — из задачи `RESPONSIBLE_ID`/`CREATED_BY`, из `bitrix_chats_recent`, из `bitrix_user_get`) |
|
|
290
|
-
| **обсуждение ЗАДАЧИ** | `
|
|
189
|
+
| **обсуждение ЗАДАЧИ** | `bitrix_task_get` → `CHAT_ID` (§5.2) |
|
|
291
190
|
| **ПРОЕКТА/группы** | `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 }`
|
|
191
|
+
| **не знаю точно, какой** | `bitrix_chats_recent` (недавние, сопоставь по `title`/`user`) или `bitrix_entity_search { query }` |
|
|
293
192
|
|
|
294
193
|
> Почему не «просто список чатов»: `bitrix_chats_recent` (`im.recent.list`) отдаёт лишь недавние и
|
|
295
194
|
> **без чатов задач**. Для поиска конкретного чата — `bitrix_entity_search`; для чатов задач —
|
|
296
195
|
> `bitrix_recent_load { section:"tasksTask" }`.
|
|
297
196
|
|
|
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).
|
|
197
|
+
**Читать историю.** `bitrix_chat_messages` отдаёт только последнюю страницу; вглубь листает
|
|
198
|
+
`bitrix_chat_history` по курсору `beforeId` = минимальный id сообщения текущей страницы (повторяй,
|
|
199
|
+
уменьшая `beforeId`, до начала истории).
|
|
307
200
|
|
|
308
201
|
```jsonc
|
|
309
|
-
|
|
310
|
-
bitrix_chat_load { "dialogId": 11 }
|
|
311
|
-
// 2) листать его историю вглубь по chatId из ответа шага 1
|
|
202
|
+
bitrix_chat_load { "dialogId": 11 } // личный чат юзера 11 → в ответе chatId
|
|
312
203
|
bitrix_chat_history { "chatId": 485, "beforeId": 1861279 }
|
|
204
|
+
bitrix_chat_mark_read { "chatId": 40271, "ids": [1884131] } // ⚠ мутирующий; actionUuid — автоматом
|
|
313
205
|
```
|
|
314
206
|
|
|
315
|
-
|
|
207
|
+
📖 Публичные аналоги чтения сообщений: `im.dialog.messages.get` (дефолт 20, макс 50; листание
|
|
208
|
+
`LAST_ID` — старее, `FIRST_ID` — новее); `imbot.v2 chat.getMessageContext` — ответ `messages[]`
|
|
209
|
+
(oldest→newest) + `users[]` + `hasPrevPage`/`hasNextPage`, по форме **совпадает** с внутренними
|
|
210
|
+
`im.v2.Chat.Message.*`.
|
|
316
211
|
|
|
317
|
-
|
|
318
|
-
поиск по названию — запасной.
|
|
212
|
+
### 5.2. Обсуждение (чат) конкретной задачи
|
|
319
213
|
|
|
320
|
-
**А. Прямой —
|
|
321
|
-
|
|
322
|
-
названию и без риска промаха по тёзкам-заголовкам:
|
|
214
|
+
**А. Прямой резолв — предпочтительно.** `bitrix_task_get` возвращает `CHAT_ID` чата-обсуждения (входит
|
|
215
|
+
в дефолтный `select` 📖) — это `taskId → chatId` без поиска по названию и без промаха по тёзкам:
|
|
323
216
|
|
|
324
217
|
```jsonc
|
|
325
|
-
bitrix_task_get { "taskId": 28373 } // в ответе CHAT_ID
|
|
218
|
+
bitrix_task_get { "taskId": 28373 } // в ответе CHAT_ID
|
|
326
219
|
bitrix_chat_load { "chatId": <CHAT_ID> }
|
|
327
220
|
bitrix_chat_history { "chatId": <CHAT_ID>, "beforeId": <мин id страницы> }
|
|
328
221
|
```
|
|
329
222
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
`
|
|
333
|
-
`entityId=<taskId>`; так же адресуются чаты `SONET_GROUP` (группа/проект), `CRM`, `CALENDAR`, `MAIL`,
|
|
334
|
-
`VIDEOCONF`, `CALL`:
|
|
223
|
+
Общая альтернатива — **`bitrix_entity_chat`** (обёртка над `im.chat.get`): chatId любого связанного
|
|
224
|
+
объекта по паре `entityType`/`entityId`; кроме `TASKS_TASK` так же адресуются `SONET_GROUP`
|
|
225
|
+
(группа/проект), `CRM`, `CALENDAR`, `MAIL`, `VIDEOCONF`, `CALL`:
|
|
335
226
|
|
|
336
227
|
```jsonc
|
|
337
228
|
bitrix_entity_chat { "entityType": "TASKS_TASK", "entityId": 28373 } // → chatId
|
|
338
|
-
bitrix_chat_load { "chatId": <chatId> }
|
|
339
229
|
```
|
|
340
230
|
|
|
341
|
-
**Б. Через список чатов
|
|
342
|
-
(в `bitrix_chats_recent` / `/rest/im.recent.list` их НЕТ). Листать вглубь — `bitrix_recent_tail`.
|
|
231
|
+
**Б. Через список чатов задач** (секция `tasksTask`; в `bitrix_chats_recent` их НЕТ):
|
|
343
232
|
|
|
344
233
|
```jsonc
|
|
345
234
|
bitrix_recent_load { "section": "tasksTask" } // → чаты задач с chatId/dialogId
|
|
346
235
|
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 }
|
|
236
|
+
bitrix_chat_load { "dialogId": "chat38849" } // или { "chatId": 38849 }
|
|
350
237
|
```
|
|
351
238
|
|
|
352
|
-
|
|
239
|
+
**Чат пуст, а `COMMENTS_COUNT > 0`** — задача старше чатов задач: в чате только системное сообщение
|
|
240
|
+
«Чтобы прочитать комментарии, которые ранее оставили участники задачи…», а само обсуждение лежит в
|
|
241
|
+
форуме → `bitrix_task_comments` (§5.6).
|
|
353
242
|
|
|
354
|
-
|
|
355
|
-
bitrix_entity_search { "query": "трекер", "section": "tasksTask" }
|
|
356
|
-
```
|
|
243
|
+
**В. Поиском по названию задачи** (запасной, матч по тексту):
|
|
244
|
+
`bitrix_entity_search { "query": "трекер", "section": "tasksTask" }`
|
|
357
245
|
|
|
358
246
|
**Чат ПРОЕКТА задачи** (обсуждение группы, не самой задачи) — через `GROUP_ID`:
|
|
359
247
|
|
|
360
248
|
```jsonc
|
|
361
249
|
bitrix_task_get { "taskId": 4229, "params": { "select": ["ID","GROUP_ID"] } }
|
|
362
250
|
bitrix_project_get { "groupId": 15 } // отдаёт CHAT_ID / DIALOG_ID
|
|
363
|
-
bitrix_chat_load { "dialogId": "chat7111" }
|
|
364
251
|
bitrix_chat_get_dialog_id { "externalId": "sg15" } // либо резолв dialogId соцгруппы напрямую
|
|
365
252
|
```
|
|
366
253
|
|
|
367
|
-
###
|
|
254
|
+
### 5.3. Поиск чата / пользователя
|
|
368
255
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
`user.get`/`user.search`, для чатов — `im.recent.list` + фильтрация на стороне агента.
|
|
256
|
+
`bitrix_entity_search` (обёртка над внутренним `ui.entityselector.doSearch`) сам собирает диалог
|
|
257
|
+
`IM_CHAT_SEARCH` и `searchQuery` из строки. Публичные аналоги: люди — `user.get`/`user.search`, чаты —
|
|
258
|
+
`im.recent.list` + фильтрация у себя.
|
|
373
259
|
|
|
374
260
|
```jsonc
|
|
375
261
|
bitrix_entity_search { "query": "дмитрий" } // среди всех чатов/диалогов
|
|
376
262
|
bitrix_entity_search { "query": "трекер", "section": "tasksTask" } // среди чатов задач
|
|
263
|
+
bitrix_user_get { "userId": 11 } // резолв authorId → имя
|
|
377
264
|
```
|
|
378
265
|
|
|
379
|
-
|
|
266
|
+
### 5.4. Созвон: резюме, расшифровка, запись
|
|
267
|
+
|
|
268
|
+
AI-анализ звонка (BitrixGPT Follow-Up) живёт на серверно отрендеренной странице `/call/detail/<callId>`
|
|
269
|
+
— JSON-эндпоинта под ней нет, публичного REST для видеовстреч тоже (📖 только телефония `voximplant.*`
|
|
270
|
+
и `crm.activity.call.getTranscript`). Мост читает эту страницу и разбирает её в нормализованный JSON.
|
|
271
|
+
|
|
272
|
+
**1) Найти звонки.** Серверного поиска звонков по порталу нет — ищем в конкретном чате: звонок
|
|
273
|
+
оставляет системное сообщение с `params.COMPONENT_PARAMS = { messageType: "START", callId }`.
|
|
380
274
|
|
|
381
275
|
```jsonc
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
bitrix_user_get { "userId": 11 } // резолв authorId → имя
|
|
276
|
+
bitrix_chat_calls { "chatId": 9876 } // чат проекта: bitrix_project_get → CHAT_ID
|
|
277
|
+
bitrix_chat_calls { "chatId": 9876, "maxPages": 20, "beforeId": 5001 } // продолжить вглубь
|
|
385
278
|
```
|
|
386
279
|
|
|
387
|
-
|
|
280
|
+
Ответ: `calls[] { callId, startedAt, startedBy, startMessageId, events[], summaryMessageId }` плюс
|
|
281
|
+
`scannedPages` / `oldestScannedMessageId` / **`reachedHistoryStart`** — если он `false`, история
|
|
282
|
+
дочитана НЕ до конца, продолжай с `beforeId: <oldestScannedMessageId>`.
|
|
388
283
|
|
|
389
|
-
|
|
390
|
-
- **im.v2-чат** — структурный JSON, читается уже сейчас (§6.3); обычно этого достаточно.
|
|
391
|
-
- **Форумные легаси-комментарии** — рендерятся как **HTML** в side-slider'е. Это отдельный
|
|
392
|
-
«HTML-поддомен» внутреннего API, который **текущий мост не поддерживает** (блокеры ниже).
|
|
284
|
+
**2) Взять всё по звонку.**
|
|
393
285
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
286
|
+
```jsonc
|
|
287
|
+
bitrix_call_detail { "callId": 4242 } // всё; длинная расшифровка уедет в файл
|
|
288
|
+
bitrix_call_detail { "callId": 4242, "transcript": "inline" } // расшифровка прямо в ответе
|
|
289
|
+
bitrix_call_detail { "callId": 4242, "transcript": "none" } // расшифровка не нужна вовсе
|
|
290
|
+
bitrix_call_detail { "callId": 4242, "transcript": "file" } // всегда в файл, даже короткая
|
|
398
291
|
```
|
|
399
292
|
|
|
400
|
-
|
|
293
|
+
| Поле ответа | Что внутри |
|
|
294
|
+
|---|---|
|
|
295
|
+
| `title`, `agenda`, `meetingType` | тема, вступление, тип встречи («Статус-встреча») |
|
|
296
|
+
| `date`, `interval`, `duration` | «23 июля, 15:46», «15:46 - 16:51», «1 ч 4 мин» |
|
|
297
|
+
| `efficiency`, `qualityChecklist[]` | оценка встречи в % и критерии `{ ok, text }` |
|
|
298
|
+
| `participants[]` | `{ id, name, talkTimePercent, talkTime, efficiency, metrics[], insight }` |
|
|
299
|
+
| `overview`, `chapters[]` | общее резюме и главы `{ from, to, title, text }` с таймкодами |
|
|
300
|
+
| `decisions[]`, `tasks[]` | «что решили» и задачи `{ assigneeId, assignee, text }` |
|
|
301
|
+
| `transcript[]`, `transcriptCount` | реплики `{ from, to, speakerId, speaker, text }` |
|
|
302
|
+
| `recording` | `{ path, url, trackId }` — аудиозапись, качается `bitrix_call_recording` (§5.5) |
|
|
303
|
+
|
|
304
|
+
**Расшифровка длинного звонка не приходит в ответе.** У часовой встречи она даёт ~70 КБ из 80 КБ, поэтому
|
|
305
|
+
свыше ~20 КБ (режим `"auto"` = умолчание) пишется на диск, а в ответе остаются `transcript: []`,
|
|
306
|
+
`transcriptCount` и **`files`**: `files.transcript` — `call-<id>-transcript.txt`, где строка = реплика
|
|
307
|
+
(`[11:10—11:24] Генрих Богацкий: …`), её удобно **грепать** (найденная строка сама несёт таймкод и
|
|
308
|
+
спикера); `files.json` — полный ответ с расшифровкой. Оба файла — кеш звонка, перезаписываются.
|
|
309
|
+
|
|
310
|
+
Тонкости:
|
|
311
|
+
- **`speakerId`** резолвится по имени из таблицы анализа; кто говорил мало и в неё не попал — получит
|
|
312
|
+
`speakerId: null`, но останется в `participants` с `id: null`.
|
|
313
|
+
- **`assigneeId` задачи** — упомянутый в тексте пользователь, а НЕ id смотрящего.
|
|
314
|
+
- Нет доступа к звонку / протухла сессия → инструмент вернёт внятную ошибку (портал отдаёт HTML логина).
|
|
315
|
+
|
|
316
|
+
### 5.5. Скачать файл (вложение чата, файл задачи, запись звонка)
|
|
317
|
+
|
|
318
|
+
Ссылки портал минтит и подписывает сам, но подпись НЕ заменяет авторизацию — качает расширение в
|
|
319
|
+
сессии пользователя.
|
|
401
320
|
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
headers: bx-ajax: true, x-bitrix-site-id: s1 (плюс обычный X-Bitrix-Csrf-Token)
|
|
321
|
+
```jsonc
|
|
322
|
+
// вложение чата: ссылку берём из files[].urlDownload в ответе bitrix_chat_load
|
|
323
|
+
bitrix_file_download { "url": "https://<портал>/bitrix/services/main/ajax.php?action=disk.api.file.download&…" }
|
|
324
|
+
bitrix_call_recording { "callId": 4242 } // аудиозапись созвона (ссылку найдёт сам)
|
|
325
|
+
bitrix_file_download { "url": "…", "savePath": "/tmp/photo.png", "overwrite": true }
|
|
408
326
|
```
|
|
409
327
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
328
|
+
Ответ — **путь, а не содержимое**: `{ path, bytes, contentType, fileName }` (гигабайты не попадают в
|
|
329
|
+
контекст). По умолчанию файл ложится в `~/.bitrix24-mcp-bridge/downloads/`; `savePath` без ведущего `/`
|
|
330
|
+
трактуется как имя внутри этой папки. Потолок — 512 МБ.
|
|
413
331
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
332
|
+
Тонкости:
|
|
333
|
+
- **Граница безопасности — origin**: скачивается только с origin сконфигурированного портала.
|
|
334
|
+
- **Имя даёт портал** (`Content-Disposition`), поэтому без `savePath` угадывать его не нужно.
|
|
335
|
+
- **Повторное скачивание** того же файла падает с `already exists` — передай `overwrite: true`.
|
|
418
336
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
337
|
+
### 5.6. Старые комментарии задачи (форум)
|
|
338
|
+
|
|
339
|
+
До перехода на чаты задач обсуждение жило в форуме задачи, и у старых задач оно там и осталось: в
|
|
340
|
+
чате (`CHAT_ID`) только системная заглушка со ссылкой `/task/comments/<id>/`. Признак — в
|
|
341
|
+
`bitrix_task_get` поле `COMMENTS_COUNT > 0` (через `params.select`; `FORUM_TOPIC_ID` тоже заполнен),
|
|
342
|
+
а чат пуст. JSON-метода чтения нет — мост разбирает HTML-страницу комментариев.
|
|
343
|
+
|
|
344
|
+
```jsonc
|
|
345
|
+
bitrix_task_comments { "taskId": 1234 } // всё обсуждение; длинное уедет в файл
|
|
346
|
+
bitrix_task_comments { "taskId": 1234, "limit": 10 } // только 10 последних
|
|
347
|
+
bitrix_task_comments { "taskId": 1234, "output": "inline" } // всегда в ответе
|
|
348
|
+
bitrix_task_comments { "taskId": 1234, "beforeId": 5678 } // продолжить вглубь от id
|
|
349
|
+
```
|
|
422
350
|
|
|
423
|
-
|
|
424
|
-
|
|
351
|
+
Ответ: `comments[]` по порядку (старые → новые) — `{ id, authorId, author, date, dateIso, text, isNew,
|
|
352
|
+
system, files[] }`, плюс `total`, `oldestId`, `scannedPages` и **`reachedHistoryStart`**: если он
|
|
353
|
+
`false` (упёрлись в `maxPages`, умолчание 10, или в `limit`), продолжай с `beforeId: <oldestId>`.
|
|
354
|
+
|
|
355
|
+
- **`system: true`** — служебные записи («вы назначены исполнителем», смена срока, пинги), не слова людей.
|
|
356
|
+
- **`date`** — как на портале («9 марта 2024 08:15»), **`dateIso`** — то же в `2024-03-09T08:15`
|
|
357
|
+
(местное время портала; для «вчера, 12:00» и дат без года — `null`).
|
|
358
|
+
- **`files[]`** — `{ attachedId, name, size, url }`; `url` скармливай `bitrix_file_download` (§5.5).
|
|
359
|
+
- **`text`** — без разметки; у ссылки с подписью адрес дописан в скобках: «макет (https://…)».
|
|
360
|
+
- **Длинное обсуждение не приходит в ответе** (как расшифровка звонка, §5.4): свыше ~20 КБ (`output:
|
|
361
|
+
"auto"` = умолчание) в ответе `comments: []` и `files`: `files.text` — `task-<id>-comments.txt`,
|
|
362
|
+
строка = комментарий (`#5678 [2024-03-09T08:15] Имя (id 101): текст`, переносы — ` ⏎ `), её удобно
|
|
363
|
+
грепать; `files.json` — полный ответ. `output: "file"` — всегда в файл.
|
|
364
|
+
- **Прочитанным ничего не помечается.** Портал отмечает комментарии отдельным действием
|
|
365
|
+
`readComment`, которое шлёт JS страницы при прокрутке; мост JS не исполняет. Проверено:
|
|
366
|
+
`viewedDate` задачи после чтения не меняется. `isNew: true` — комментарий не прочитан пользователем.
|
|
367
|
+
- Нет доступа к задаче / протухла сессия → внятная ошибка (портал отдаёт HTML логина).
|
|
425
368
|
|
|
426
369
|
---
|
|
427
370
|
|
|
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
|
-
> новых методов.
|
|
371
|
+
Расширение каталога новыми методами, транспорт (`bodyType`, `responseType`, path-параметры) и
|
|
372
|
+
доставка правок в живой daemon — `docs/reconnaissance.md`.
|