@ihazz/bitrix24 1.1.2 → 1.1.4
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 +5 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/src/access-control.d.ts +43 -0
- package/dist/src/access-control.d.ts.map +1 -0
- package/dist/src/access-control.js +128 -0
- package/dist/src/access-control.js.map +1 -0
- package/dist/src/api.d.ts +161 -0
- package/dist/src/api.d.ts.map +1 -0
- package/dist/src/api.js +357 -0
- package/dist/src/api.js.map +1 -0
- package/dist/src/bot-avatar.d.ts +7 -0
- package/dist/src/bot-avatar.d.ts.map +1 -0
- package/dist/src/bot-avatar.js +7 -0
- package/dist/src/bot-avatar.js.map +1 -0
- package/dist/src/channel.d.ts +216 -0
- package/dist/src/channel.d.ts.map +1 -0
- package/dist/src/channel.js +2324 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/commands.d.ts +22 -0
- package/dist/src/commands.d.ts.map +1 -0
- package/dist/src/commands.js +160 -0
- package/dist/src/commands.js.map +1 -0
- package/dist/src/config-schema.d.ts +356 -0
- package/dist/src/config-schema.d.ts.map +1 -0
- package/dist/src/config-schema.js +43 -0
- package/dist/src/config-schema.js.map +1 -0
- package/dist/src/config.d.ts +11 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +50 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/dedup.d.ts +22 -0
- package/dist/src/dedup.d.ts.map +1 -0
- package/dist/src/dedup.js +49 -0
- package/dist/src/dedup.js.map +1 -0
- package/dist/src/group-access.d.ts +52 -0
- package/dist/src/group-access.d.ts.map +1 -0
- package/dist/src/group-access.js +180 -0
- package/dist/src/group-access.js.map +1 -0
- package/dist/src/history-cache.d.ts +41 -0
- package/dist/src/history-cache.d.ts.map +1 -0
- package/dist/src/history-cache.js +82 -0
- package/dist/src/history-cache.js.map +1 -0
- package/dist/src/i18n.d.ts +22 -0
- package/dist/src/i18n.d.ts.map +1 -0
- package/dist/src/i18n.js +175 -0
- package/dist/src/i18n.js.map +1 -0
- package/dist/src/inbound-handler.d.ts +92 -0
- package/dist/src/inbound-handler.d.ts.map +1 -0
- package/dist/src/inbound-handler.js +417 -0
- package/dist/src/inbound-handler.js.map +1 -0
- package/dist/src/media-service.d.ts +52 -0
- package/dist/src/media-service.d.ts.map +1 -0
- package/dist/src/media-service.js +423 -0
- package/dist/src/media-service.js.map +1 -0
- package/dist/src/message-utils.d.ts +34 -0
- package/dist/src/message-utils.d.ts.map +1 -0
- package/dist/src/message-utils.js +392 -0
- package/dist/src/message-utils.js.map +1 -0
- package/dist/src/polling-service.d.ts +39 -0
- package/dist/src/polling-service.d.ts.map +1 -0
- package/dist/src/polling-service.js +204 -0
- package/dist/src/polling-service.js.map +1 -0
- package/dist/src/rate-limiter.d.ts +22 -0
- package/dist/src/rate-limiter.d.ts.map +1 -0
- package/dist/src/rate-limiter.js +72 -0
- package/dist/src/rate-limiter.js.map +1 -0
- package/dist/src/runtime.d.ts +106 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +11 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/send-service.d.ts +66 -0
- package/dist/src/send-service.d.ts.map +1 -0
- package/dist/src/send-service.js +177 -0
- package/dist/src/send-service.js.map +1 -0
- package/dist/src/state-paths.d.ts +3 -0
- package/dist/src/state-paths.d.ts.map +1 -0
- package/dist/src/state-paths.js +23 -0
- package/dist/src/state-paths.js.map +1 -0
- package/dist/src/types.d.ts +381 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils.d.ts +60 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +131 -0
- package/dist/src/utils.js.map +1 -0
- package/index.ts +1 -1
- package/openclaw.plugin.json +278 -1
- package/package.json +11 -2
- package/src/api.ts +0 -3
- package/src/channel.ts +76 -73
- package/src/config-schema.ts +1 -2
- package/src/config.ts +6 -8
- package/src/group-access.ts +1 -8
- package/src/inbound-handler.ts +128 -15
- package/src/media-service.ts +160 -45
- package/src/polling-service.ts +2 -3
- package/src/send-service.ts +4 -3
- package/src/state-paths.ts +4 -0
- package/src/types.ts +1 -2
- package/src/utils.ts +31 -4
package/openclaw.plugin.json
CHANGED
|
@@ -5,6 +5,283 @@
|
|
|
5
5
|
"configSchema": {
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": true,
|
|
8
|
-
"
|
|
8
|
+
"$defs": {
|
|
9
|
+
"watchRule": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"additionalProperties": true,
|
|
12
|
+
"properties": {
|
|
13
|
+
"userId": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"minLength": 1
|
|
16
|
+
},
|
|
17
|
+
"topics": {
|
|
18
|
+
"type": "array",
|
|
19
|
+
"items": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"minLength": 1
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"mode": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": ["reply", "notifyOwnerDm"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"agentWatchRule": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"additionalProperties": true,
|
|
33
|
+
"properties": {
|
|
34
|
+
"userId": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"minLength": 1
|
|
37
|
+
},
|
|
38
|
+
"topics": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"items": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"minLength": 1
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"mode": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"enum": ["notifyOwnerDm"]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"groupConfig": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"additionalProperties": true,
|
|
54
|
+
"properties": {
|
|
55
|
+
"groupPolicy": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"enum": ["disabled", "webhookUser", "pairing", "allowlist", "open"]
|
|
58
|
+
},
|
|
59
|
+
"requireMention": {
|
|
60
|
+
"type": "boolean",
|
|
61
|
+
"default": true
|
|
62
|
+
},
|
|
63
|
+
"allowFrom": {
|
|
64
|
+
"type": "array",
|
|
65
|
+
"items": {
|
|
66
|
+
"type": "string"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"watch": {
|
|
70
|
+
"type": "array",
|
|
71
|
+
"items": {
|
|
72
|
+
"$ref": "#/$defs/watchRule"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"accountConfig": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"additionalProperties": true,
|
|
80
|
+
"properties": {
|
|
81
|
+
"enabled": {
|
|
82
|
+
"type": "boolean",
|
|
83
|
+
"default": true
|
|
84
|
+
},
|
|
85
|
+
"webhookUrl": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"pattern": "^https?://\\S+$"
|
|
88
|
+
},
|
|
89
|
+
"botToken": {
|
|
90
|
+
"type": "string"
|
|
91
|
+
},
|
|
92
|
+
"botName": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"default": "OpenClaw"
|
|
95
|
+
},
|
|
96
|
+
"botCode": {
|
|
97
|
+
"type": "string"
|
|
98
|
+
},
|
|
99
|
+
"botAvatar": {
|
|
100
|
+
"type": "string"
|
|
101
|
+
},
|
|
102
|
+
"allowFrom": {
|
|
103
|
+
"type": "array",
|
|
104
|
+
"items": {
|
|
105
|
+
"type": "string"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"callbackUrl": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"pattern": "^https?://\\S+$"
|
|
111
|
+
},
|
|
112
|
+
"eventMode": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"enum": ["fetch", "webhook"]
|
|
115
|
+
},
|
|
116
|
+
"agentMode": {
|
|
117
|
+
"type": "boolean",
|
|
118
|
+
"default": false
|
|
119
|
+
},
|
|
120
|
+
"pollingIntervalMs": {
|
|
121
|
+
"type": "integer",
|
|
122
|
+
"minimum": 500,
|
|
123
|
+
"default": 3000
|
|
124
|
+
},
|
|
125
|
+
"pollingFastIntervalMs": {
|
|
126
|
+
"type": "integer",
|
|
127
|
+
"minimum": 50,
|
|
128
|
+
"default": 100
|
|
129
|
+
},
|
|
130
|
+
"dmPolicy": {
|
|
131
|
+
"type": "string",
|
|
132
|
+
"enum": ["pairing", "webhookUser", "allowlist", "open"],
|
|
133
|
+
"default": "webhookUser"
|
|
134
|
+
},
|
|
135
|
+
"groupPolicy": {
|
|
136
|
+
"type": "string",
|
|
137
|
+
"enum": ["disabled", "webhookUser", "pairing", "allowlist", "open"],
|
|
138
|
+
"default": "webhookUser"
|
|
139
|
+
},
|
|
140
|
+
"groupAllowFrom": {
|
|
141
|
+
"type": "array",
|
|
142
|
+
"items": {
|
|
143
|
+
"type": "string"
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
"requireMention": {
|
|
147
|
+
"type": "boolean",
|
|
148
|
+
"default": true
|
|
149
|
+
},
|
|
150
|
+
"historyLimit": {
|
|
151
|
+
"type": "integer",
|
|
152
|
+
"minimum": 0,
|
|
153
|
+
"default": 100
|
|
154
|
+
},
|
|
155
|
+
"groups": {
|
|
156
|
+
"type": "object",
|
|
157
|
+
"additionalProperties": {
|
|
158
|
+
"$ref": "#/$defs/groupConfig"
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"agentWatch": {
|
|
162
|
+
"type": "object",
|
|
163
|
+
"additionalProperties": {
|
|
164
|
+
"type": "array",
|
|
165
|
+
"items": {
|
|
166
|
+
"$ref": "#/$defs/agentWatchRule"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"showTyping": {
|
|
171
|
+
"type": "boolean",
|
|
172
|
+
"default": true
|
|
173
|
+
},
|
|
174
|
+
"verboseLog": {
|
|
175
|
+
"type": "boolean",
|
|
176
|
+
"default": false
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"properties": {
|
|
182
|
+
"enabled": {
|
|
183
|
+
"type": "boolean",
|
|
184
|
+
"default": true
|
|
185
|
+
},
|
|
186
|
+
"webhookUrl": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"pattern": "^https?://\\S+$"
|
|
189
|
+
},
|
|
190
|
+
"botToken": {
|
|
191
|
+
"type": "string"
|
|
192
|
+
},
|
|
193
|
+
"botName": {
|
|
194
|
+
"type": "string",
|
|
195
|
+
"default": "OpenClaw"
|
|
196
|
+
},
|
|
197
|
+
"botCode": {
|
|
198
|
+
"type": "string"
|
|
199
|
+
},
|
|
200
|
+
"botAvatar": {
|
|
201
|
+
"type": "string"
|
|
202
|
+
},
|
|
203
|
+
"allowFrom": {
|
|
204
|
+
"type": "array",
|
|
205
|
+
"items": {
|
|
206
|
+
"type": "string"
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"callbackUrl": {
|
|
210
|
+
"type": "string",
|
|
211
|
+
"pattern": "^https?://\\S+$"
|
|
212
|
+
},
|
|
213
|
+
"eventMode": {
|
|
214
|
+
"type": "string",
|
|
215
|
+
"enum": ["fetch", "webhook"]
|
|
216
|
+
},
|
|
217
|
+
"agentMode": {
|
|
218
|
+
"type": "boolean",
|
|
219
|
+
"default": false
|
|
220
|
+
},
|
|
221
|
+
"pollingIntervalMs": {
|
|
222
|
+
"type": "integer",
|
|
223
|
+
"minimum": 500,
|
|
224
|
+
"default": 3000
|
|
225
|
+
},
|
|
226
|
+
"pollingFastIntervalMs": {
|
|
227
|
+
"type": "integer",
|
|
228
|
+
"minimum": 50,
|
|
229
|
+
"default": 100
|
|
230
|
+
},
|
|
231
|
+
"dmPolicy": {
|
|
232
|
+
"type": "string",
|
|
233
|
+
"enum": ["pairing", "webhookUser", "allowlist", "open"],
|
|
234
|
+
"default": "webhookUser"
|
|
235
|
+
},
|
|
236
|
+
"groupPolicy": {
|
|
237
|
+
"type": "string",
|
|
238
|
+
"enum": ["disabled", "webhookUser", "pairing", "allowlist", "open"],
|
|
239
|
+
"default": "webhookUser"
|
|
240
|
+
},
|
|
241
|
+
"groupAllowFrom": {
|
|
242
|
+
"type": "array",
|
|
243
|
+
"items": {
|
|
244
|
+
"type": "string"
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"requireMention": {
|
|
248
|
+
"type": "boolean",
|
|
249
|
+
"default": true
|
|
250
|
+
},
|
|
251
|
+
"historyLimit": {
|
|
252
|
+
"type": "integer",
|
|
253
|
+
"minimum": 0,
|
|
254
|
+
"default": 100
|
|
255
|
+
},
|
|
256
|
+
"groups": {
|
|
257
|
+
"type": "object",
|
|
258
|
+
"additionalProperties": {
|
|
259
|
+
"$ref": "#/$defs/groupConfig"
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
"agentWatch": {
|
|
263
|
+
"type": "object",
|
|
264
|
+
"additionalProperties": {
|
|
265
|
+
"type": "array",
|
|
266
|
+
"items": {
|
|
267
|
+
"$ref": "#/$defs/agentWatchRule"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
"showTyping": {
|
|
272
|
+
"type": "boolean",
|
|
273
|
+
"default": true
|
|
274
|
+
},
|
|
275
|
+
"verboseLog": {
|
|
276
|
+
"type": "boolean",
|
|
277
|
+
"default": false
|
|
278
|
+
},
|
|
279
|
+
"accounts": {
|
|
280
|
+
"type": "object",
|
|
281
|
+
"additionalProperties": {
|
|
282
|
+
"$ref": "#/$defs/accountConfig"
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
9
286
|
}
|
|
10
287
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ihazz/bitrix24",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "Bitrix24 Messenger channel for OpenClaw",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
8
15
|
"files": [
|
|
16
|
+
"dist",
|
|
9
17
|
"index.ts",
|
|
10
18
|
"openclaw.plugin.json",
|
|
11
19
|
"README.md",
|
|
@@ -15,7 +23,7 @@
|
|
|
15
23
|
],
|
|
16
24
|
"openclaw": {
|
|
17
25
|
"extensions": [
|
|
18
|
-
"./index.
|
|
26
|
+
"./dist/index.js"
|
|
19
27
|
],
|
|
20
28
|
"channel": {
|
|
21
29
|
"id": "bitrix24",
|
|
@@ -33,6 +41,7 @@
|
|
|
33
41
|
},
|
|
34
42
|
"scripts": {
|
|
35
43
|
"build": "tsc",
|
|
44
|
+
"prepack": "npm run build",
|
|
36
45
|
"test": "vitest run",
|
|
37
46
|
"test:watch": "vitest"
|
|
38
47
|
},
|
package/src/api.ts
CHANGED
package/src/channel.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { CommandSendContext, SendContext } from './send-service.js';
|
|
|
9
9
|
import { MediaService } from './media-service.js';
|
|
10
10
|
import type { DownloadedMedia } from './media-service.js';
|
|
11
11
|
import { InboundHandler } from './inbound-handler.js';
|
|
12
|
-
import type { FetchCommandContext, FetchJoinChatContext } from './inbound-handler.js';
|
|
12
|
+
import type { FetchCommandContext, FetchJoinChatContext, FetchReactionContext } from './inbound-handler.js';
|
|
13
13
|
import { PollingService } from './polling-service.js';
|
|
14
14
|
import {
|
|
15
15
|
normalizeAllowEntry,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
resolveGroupAccess,
|
|
25
25
|
} from './group-access.js';
|
|
26
26
|
import { DEFAULT_AVATAR_BASE64 } from './bot-avatar.js';
|
|
27
|
-
import { Bitrix24ApiError, defaultLogger, CHANNEL_PREFIX_RE } from './utils.js';
|
|
27
|
+
import { Bitrix24ApiError, createVerboseLogger, defaultLogger, CHANNEL_PREFIX_RE } from './utils.js';
|
|
28
28
|
import { getBitrix24Runtime } from './runtime.js';
|
|
29
29
|
import type { ChannelPairingAdapter } from './runtime.js';
|
|
30
30
|
import { OPENCLAW_COMMANDS, buildCommandsHelpText, formatModelsCommandReply } from './commands.js';
|
|
@@ -75,6 +75,7 @@ const HISTORY_CACHE_MAX_KEYS = 1000;
|
|
|
75
75
|
const HISTORY_CONTEXT_MARKER = '[Chat messages since your last reply - for context]';
|
|
76
76
|
const CROSS_CHAT_HISTORY_LIMIT = 20;
|
|
77
77
|
const ACCESS_DENIED_REACTION = 'crossMark';
|
|
78
|
+
const BOT_MESSAGE_WATCH_REACTION = 'eyes';
|
|
78
79
|
const FORWARDED_CONTEXT_RANGE = 5;
|
|
79
80
|
const REGISTERED_COMMANDS = new Set(OPENCLAW_COMMANDS.map((command) => command.command));
|
|
80
81
|
|
|
@@ -1407,19 +1408,16 @@ function collectOutboundMediaUrls(input: {
|
|
|
1407
1408
|
}
|
|
1408
1409
|
|
|
1409
1410
|
async function uploadOutboundMedia(params: {
|
|
1410
|
-
|
|
1411
|
+
mediaService: MediaService;
|
|
1411
1412
|
mediaUrls: string[];
|
|
1412
|
-
|
|
1413
|
+
sendCtx: SendContext;
|
|
1414
|
+
initialMessage?: string;
|
|
1413
1415
|
}): Promise<string> {
|
|
1414
|
-
if (!gatewayState) {
|
|
1415
|
-
throw new Error('Bitrix24 gateway not started');
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
1416
|
let lastMessageId = '';
|
|
1419
|
-
let message = params.
|
|
1417
|
+
let message = params.initialMessage;
|
|
1420
1418
|
|
|
1421
1419
|
for (const mediaUrl of params.mediaUrls) {
|
|
1422
|
-
const result = await
|
|
1420
|
+
const result = await params.mediaService.uploadMediaToChat({
|
|
1423
1421
|
localPath: mediaUrl,
|
|
1424
1422
|
fileName: basename(mediaUrl),
|
|
1425
1423
|
webhookUrl: params.sendCtx.webhookUrl,
|
|
@@ -1562,9 +1560,10 @@ export const bitrix24Plugin = {
|
|
|
1562
1560
|
const mediaUrls = collectOutboundMediaUrls({ mediaUrl: ctx.mediaUrl });
|
|
1563
1561
|
if (mediaUrls.length > 0) {
|
|
1564
1562
|
const messageId = await uploadOutboundMedia({
|
|
1563
|
+
mediaService: gatewayState.mediaService,
|
|
1565
1564
|
sendCtx,
|
|
1566
1565
|
mediaUrls,
|
|
1567
|
-
|
|
1566
|
+
initialMessage: ctx.text,
|
|
1568
1567
|
});
|
|
1569
1568
|
return { messageId };
|
|
1570
1569
|
}
|
|
@@ -1598,6 +1597,7 @@ export const bitrix24Plugin = {
|
|
|
1598
1597
|
|
|
1599
1598
|
if (mediaUrls.length > 0) {
|
|
1600
1599
|
const uploadedMessageId = await uploadOutboundMedia({
|
|
1600
|
+
mediaService: gatewayState.mediaService,
|
|
1601
1601
|
sendCtx,
|
|
1602
1602
|
mediaUrls,
|
|
1603
1603
|
});
|
|
@@ -1645,6 +1645,12 @@ export const bitrix24Plugin = {
|
|
|
1645
1645
|
params: Record<string, unknown>;
|
|
1646
1646
|
[key: string]: unknown;
|
|
1647
1647
|
}): Promise<Record<string, unknown> | null> => {
|
|
1648
|
+
// Helper: wrap payload as gateway-compatible tool result
|
|
1649
|
+
const toolResult = (payload: Record<string, unknown>) => ({
|
|
1650
|
+
content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],
|
|
1651
|
+
details: payload,
|
|
1652
|
+
});
|
|
1653
|
+
|
|
1648
1654
|
// ─── Send with buttons ──────────────────────────────────────────────
|
|
1649
1655
|
if (ctx.action === 'send') {
|
|
1650
1656
|
// Only intercept send when buttons are present; otherwise let gateway handle normally
|
|
@@ -1675,11 +1681,6 @@ export const bitrix24Plugin = {
|
|
|
1675
1681
|
|
|
1676
1682
|
const keyboard = buttons?.length ? convertButtonsToKeyboard(buttons) : undefined;
|
|
1677
1683
|
|
|
1678
|
-
const toolResult = (payload: Record<string, unknown>) => ({
|
|
1679
|
-
content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],
|
|
1680
|
-
details: payload,
|
|
1681
|
-
});
|
|
1682
|
-
|
|
1683
1684
|
try {
|
|
1684
1685
|
const result = await gatewayState.sendService.sendText(
|
|
1685
1686
|
sendCtx, message || ' ', keyboard ? { keyboard } : undefined,
|
|
@@ -1700,12 +1701,6 @@ export const bitrix24Plugin = {
|
|
|
1700
1701
|
// ─── React ────────────────────────────────────────────────────────────
|
|
1701
1702
|
if (ctx.action !== 'react') return null;
|
|
1702
1703
|
|
|
1703
|
-
// Helper: wrap payload as gateway-compatible tool result
|
|
1704
|
-
const toolResult = (payload: Record<string, unknown>) => ({
|
|
1705
|
-
content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],
|
|
1706
|
-
details: payload,
|
|
1707
|
-
});
|
|
1708
|
-
|
|
1709
1704
|
const { config } = resolveAccount(ctx.cfg, ctx.accountId);
|
|
1710
1705
|
if (!config.webhookUrl || !gatewayState) {
|
|
1711
1706
|
return toolResult({ ok: false, reason: 'not_started', hint: 'Bitrix24 gateway not started. Do not retry.' });
|
|
@@ -1782,8 +1777,6 @@ export const bitrix24Plugin = {
|
|
|
1782
1777
|
log?: Logger;
|
|
1783
1778
|
setStatus?: (status: Record<string, unknown>) => void;
|
|
1784
1779
|
}) => {
|
|
1785
|
-
const logger = ctx.log ?? defaultLogger;
|
|
1786
|
-
|
|
1787
1780
|
// Guard: only one account can run at a time (singleton gateway)
|
|
1788
1781
|
if (gatewayState !== null) {
|
|
1789
1782
|
throw new Error(
|
|
@@ -1793,6 +1786,7 @@ export const bitrix24Plugin = {
|
|
|
1793
1786
|
}
|
|
1794
1787
|
|
|
1795
1788
|
const config = getConfig(ctx.cfg, ctx.accountId);
|
|
1789
|
+
const logger = createVerboseLogger(ctx.log ?? defaultLogger, Boolean(config.verboseLog));
|
|
1796
1790
|
|
|
1797
1791
|
if (!config.webhookUrl) {
|
|
1798
1792
|
logger.warn(`[${ctx.accountId}] no webhookUrl configured, skipping`);
|
|
@@ -2114,18 +2108,12 @@ export const bitrix24Plugin = {
|
|
|
2114
2108
|
deliver: async (payload) => {
|
|
2115
2109
|
await replyStatusHeartbeat.stopAndWait();
|
|
2116
2110
|
const mediaUrls = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
bot,
|
|
2123
|
-
dialogId: conversation.dialogId,
|
|
2111
|
+
if (mediaUrls.length > 0) {
|
|
2112
|
+
await uploadOutboundMedia({
|
|
2113
|
+
mediaService,
|
|
2114
|
+
sendCtx,
|
|
2115
|
+
mediaUrls,
|
|
2124
2116
|
});
|
|
2125
|
-
|
|
2126
|
-
if (!uploadResult.ok) {
|
|
2127
|
-
throw new Error(`Failed to upload media: ${basename(mediaUrl)}`);
|
|
2128
|
-
}
|
|
2129
2117
|
}
|
|
2130
2118
|
if (payload.text) {
|
|
2131
2119
|
let text = payload.text;
|
|
@@ -2209,6 +2197,45 @@ export const bitrix24Plugin = {
|
|
|
2209
2197
|
});
|
|
2210
2198
|
}
|
|
2211
2199
|
};
|
|
2200
|
+
const maybeReactToBotMessageReaction = async (reactionCtx: FetchReactionContext): Promise<void> => {
|
|
2201
|
+
if (reactionCtx.action !== 'set') {
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
const botId = String(bot.botId);
|
|
2206
|
+
if (reactionCtx.senderId === botId || reactionCtx.messageAuthorId !== botId) {
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
const messageId = toMessageId(reactionCtx.messageId);
|
|
2211
|
+
if (!messageId) {
|
|
2212
|
+
return;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
try {
|
|
2216
|
+
await api.addReaction(
|
|
2217
|
+
webhookUrl,
|
|
2218
|
+
bot,
|
|
2219
|
+
messageId,
|
|
2220
|
+
BOT_MESSAGE_WATCH_REACTION,
|
|
2221
|
+
);
|
|
2222
|
+
} catch (err) {
|
|
2223
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2224
|
+
if (errMsg.includes('REACTION_ALREADY_SET')) {
|
|
2225
|
+
logger.debug('Watch reaction already set on bot message', {
|
|
2226
|
+
chatId: reactionCtx.dialogId,
|
|
2227
|
+
messageId: reactionCtx.messageId,
|
|
2228
|
+
});
|
|
2229
|
+
return;
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
logger.warn('Failed to add watch reaction to bot message', {
|
|
2233
|
+
chatId: reactionCtx.dialogId,
|
|
2234
|
+
messageId: reactionCtx.messageId,
|
|
2235
|
+
error: err,
|
|
2236
|
+
});
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2212
2239
|
const notifyWebhookOwnerAboutWatchMatch = async (
|
|
2213
2240
|
msgCtx: B24MsgContext,
|
|
2214
2241
|
watchRule: Bitrix24GroupWatchConfig | Bitrix24AgentWatchConfig,
|
|
@@ -2295,6 +2322,9 @@ export const bitrix24Plugin = {
|
|
|
2295
2322
|
const inboundHandler = new InboundHandler({
|
|
2296
2323
|
config,
|
|
2297
2324
|
logger,
|
|
2325
|
+
onReactionChange: async (reactionCtx) => {
|
|
2326
|
+
await maybeReactToBotMessageReaction(reactionCtx);
|
|
2327
|
+
},
|
|
2298
2328
|
|
|
2299
2329
|
onMessage: async (msgCtx: B24MsgContext) => {
|
|
2300
2330
|
logger.info('Inbound message', {
|
|
@@ -2349,6 +2379,10 @@ export const bitrix24Plugin = {
|
|
|
2349
2379
|
? findMatchingWatchRule(msgCtx, agentWatchRules)
|
|
2350
2380
|
: undefined;
|
|
2351
2381
|
|
|
2382
|
+
/** Shorthand: record message in RAM history for this dialog. */
|
|
2383
|
+
const recordHistory = (body?: string) =>
|
|
2384
|
+
appendMessageToHistory({ historyCache, historyKey, historyLimit, msgCtx, body });
|
|
2385
|
+
|
|
2352
2386
|
if (msgCtx.eventScope === 'user') {
|
|
2353
2387
|
const isBotDialogUserEvent = msgCtx.isDm && msgCtx.chatId === String(bot.botId);
|
|
2354
2388
|
const isBotAuthoredUserEvent = msgCtx.senderId === String(bot.botId);
|
|
@@ -2364,12 +2398,7 @@ export const bitrix24Plugin = {
|
|
|
2364
2398
|
return;
|
|
2365
2399
|
}
|
|
2366
2400
|
|
|
2367
|
-
|
|
2368
|
-
historyCache,
|
|
2369
|
-
historyKey,
|
|
2370
|
-
historyLimit,
|
|
2371
|
-
msgCtx,
|
|
2372
|
-
});
|
|
2401
|
+
recordHistory();
|
|
2373
2402
|
|
|
2374
2403
|
if (webhookOwnerId && msgCtx.senderId !== webhookOwnerId && agentWatchRule?.mode === 'notifyOwnerDm') {
|
|
2375
2404
|
await notifyWebhookOwnerAboutWatchMatch(msgCtx, agentWatchRule);
|
|
@@ -2405,12 +2434,7 @@ export const bitrix24Plugin = {
|
|
|
2405
2434
|
&& groupAccess?.requireMention
|
|
2406
2435
|
&& !msgCtx.wasMentioned
|
|
2407
2436
|
) {
|
|
2408
|
-
|
|
2409
|
-
historyCache,
|
|
2410
|
-
historyKey,
|
|
2411
|
-
historyLimit,
|
|
2412
|
-
msgCtx,
|
|
2413
|
-
});
|
|
2437
|
+
recordHistory();
|
|
2414
2438
|
|
|
2415
2439
|
logger.info('Skipping group message without mention', {
|
|
2416
2440
|
chatId: msgCtx.chatId,
|
|
@@ -2421,12 +2445,7 @@ export const bitrix24Plugin = {
|
|
|
2421
2445
|
}
|
|
2422
2446
|
|
|
2423
2447
|
if (activeWatchRule?.mode === 'notifyOwnerDm') {
|
|
2424
|
-
|
|
2425
|
-
historyCache,
|
|
2426
|
-
historyKey,
|
|
2427
|
-
historyLimit,
|
|
2428
|
-
msgCtx,
|
|
2429
|
-
});
|
|
2448
|
+
recordHistory();
|
|
2430
2449
|
|
|
2431
2450
|
await notifyWebhookOwnerAboutWatchMatch(msgCtx, activeWatchRule);
|
|
2432
2451
|
logger.debug('Group watch matched and notified webhook owner in DM', {
|
|
@@ -2466,12 +2485,7 @@ export const bitrix24Plugin = {
|
|
|
2466
2485
|
|
|
2467
2486
|
if (accessResult === 'deny') {
|
|
2468
2487
|
if (msgCtx.isGroup) {
|
|
2469
|
-
|
|
2470
|
-
historyCache,
|
|
2471
|
-
historyKey,
|
|
2472
|
-
historyLimit,
|
|
2473
|
-
msgCtx,
|
|
2474
|
-
});
|
|
2488
|
+
recordHistory();
|
|
2475
2489
|
|
|
2476
2490
|
if (!msgCtx.wasMentioned) {
|
|
2477
2491
|
logger.debug('Group message blocked silently without mention', {
|
|
@@ -2507,12 +2521,7 @@ export const bitrix24Plugin = {
|
|
|
2507
2521
|
|
|
2508
2522
|
if (accessResult === 'pairing') {
|
|
2509
2523
|
if (msgCtx.isGroup) {
|
|
2510
|
-
|
|
2511
|
-
historyCache,
|
|
2512
|
-
historyKey,
|
|
2513
|
-
historyLimit,
|
|
2514
|
-
msgCtx,
|
|
2515
|
-
});
|
|
2524
|
+
recordHistory();
|
|
2516
2525
|
|
|
2517
2526
|
await maybeSendDialogNotice(
|
|
2518
2527
|
`group-pairing:${msgCtx.chatId}:${msgCtx.senderId}`,
|
|
@@ -2659,8 +2668,6 @@ export const bitrix24Plugin = {
|
|
|
2659
2668
|
logger.warn('Command event has invalid messageId, skipping response', { commandId, messageId, dialogId });
|
|
2660
2669
|
return;
|
|
2661
2670
|
}
|
|
2662
|
-
const canMarkRead = true;
|
|
2663
|
-
|
|
2664
2671
|
if (!isDm && groupAccess?.groupPolicy === 'disabled') {
|
|
2665
2672
|
await sendService.answerCommandText(
|
|
2666
2673
|
commandSendCtx,
|
|
@@ -2673,9 +2680,7 @@ export const bitrix24Plugin = {
|
|
|
2673
2680
|
await sendService.sendStatus(sendCtx, 'IMBOT_AGENT_ACTION_THINKING', 8);
|
|
2674
2681
|
|
|
2675
2682
|
if (accessResult === 'deny') {
|
|
2676
|
-
|
|
2677
|
-
await sendService.markRead(sendCtx, commandMessageId);
|
|
2678
|
-
}
|
|
2683
|
+
await sendService.markRead(sendCtx, commandMessageId);
|
|
2679
2684
|
await sendService.answerCommandText(
|
|
2680
2685
|
commandSendCtx,
|
|
2681
2686
|
buildAccessDeniedNotice(
|
|
@@ -2693,9 +2698,7 @@ export const bitrix24Plugin = {
|
|
|
2693
2698
|
return;
|
|
2694
2699
|
}
|
|
2695
2700
|
|
|
2696
|
-
|
|
2697
|
-
await sendService.markRead(sendCtx, commandMessageId);
|
|
2698
|
-
}
|
|
2701
|
+
await sendService.markRead(sendCtx, commandMessageId);
|
|
2699
2702
|
|
|
2700
2703
|
if (accessResult === 'pairing') {
|
|
2701
2704
|
if (!isDm) {
|
package/src/config-schema.ts
CHANGED
|
@@ -38,8 +38,7 @@ const AccountSchema = z.object({
|
|
|
38
38
|
groups: z.record(z.string(), GroupSchema).optional(),
|
|
39
39
|
agentWatch: z.record(z.string(), z.array(AgentWatchRuleSchema)).optional(),
|
|
40
40
|
showTyping: z.boolean().optional().default(true),
|
|
41
|
-
|
|
42
|
-
updateIntervalMs: z.number().int().min(500).optional().default(10000),
|
|
41
|
+
verboseLog: z.boolean().optional().default(false),
|
|
43
42
|
});
|
|
44
43
|
|
|
45
44
|
export const Bitrix24ConfigSchema = AccountSchema.extend({
|
package/src/config.ts
CHANGED
|
@@ -54,16 +54,14 @@ export function resolveAccount(
|
|
|
54
54
|
let config = getConfig(cfg, id);
|
|
55
55
|
|
|
56
56
|
// Validate and normalize config against Zod schema.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw new Error(`[bitrix24] Invalid config for account "${id}": ${issues}`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
config = result.data;
|
|
57
|
+
const result = Bitrix24ConfigSchema.safeParse(config);
|
|
58
|
+
if (!result.success) {
|
|
59
|
+
const issues = result.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ');
|
|
60
|
+
throw new Error(`[bitrix24] Invalid config for account "${id}": ${issues}`);
|
|
65
61
|
}
|
|
66
62
|
|
|
63
|
+
config = result.data;
|
|
64
|
+
|
|
67
65
|
return {
|
|
68
66
|
accountId: id,
|
|
69
67
|
config,
|
package/src/group-access.ts
CHANGED
|
@@ -37,14 +37,7 @@ function normalizeWatchRules<T extends { userId: string; topics?: string[]; mode
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export function normalizeGroupEntry(entry: string): string {
|
|
40
|
-
|
|
41
|
-
if (/^\d+$/.test(normalized)) {
|
|
42
|
-
return normalized;
|
|
43
|
-
}
|
|
44
|
-
if (/^chat\d+$/.test(normalized)) {
|
|
45
|
-
return normalized;
|
|
46
|
-
}
|
|
47
|
-
return normalized;
|
|
40
|
+
return String(entry).trim().toLowerCase();
|
|
48
41
|
}
|
|
49
42
|
|
|
50
43
|
export function normalizeGroupAllowList(entries: string[] | undefined): string[] {
|