@ihazz/bitrix24 0.2.2 → 0.2.5

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 CHANGED
@@ -45,12 +45,13 @@ Add to your `openclaw.json`:
45
45
  "channels": {
46
46
  "bitrix24": {
47
47
  "webhookUrl": "https://your-portal.bitrix24.com/rest/1/abc123xyz456/",
48
+ "callbackUrl": "https://your-server.com/hooks/bitrix24",
48
49
  "botName": "OpenClaw",
49
50
  "botCode": "openclaw",
50
- "callbackUrl": "https://your-server.com/hooks/bitrix24",
51
51
  "dmPolicy": "open",
52
52
  "allowFrom": ["*"],
53
- "showTyping": true
53
+ "showTyping": true,
54
+ "capabilities": ["inlineButtons"]
54
55
  }
55
56
  }
56
57
  }
@@ -73,15 +74,35 @@ Only `webhookUrl` is required. The gateway will not start without it.
73
74
  | Parameter | Default | Description |
74
75
  |---|---|---|
75
76
  | `webhookUrl` | — | Bitrix24 REST webhook URL (**required**) |
77
+ | `callbackUrl` | — | Full public URL for bot EVENT_HANDLER (e.g. `https://your-server.com/hooks/bitrix24`). Path is auto-extracted for route registration. (**required**) |
76
78
  | `botName` | `"OpenClaw"` | Bot display name (shown in welcome message) |
77
79
  | `botCode` | `"openclaw"` | Unique bot code for `imbot.register` |
78
- | `callbackUrl` | — | Full public URL for bot EVENT_HANDLER (e.g. `https://your-server.com/hooks/bitrix24`). Path is auto-extracted for route registration. |
79
80
  | `dmPolicy` | `"open"` | Access policy: `"open"` / `"allowlist"` / `"pairing"` |
80
81
  | `allowFrom` | — | Allowed B24 user IDs (when `dmPolicy: "allowlist"`) |
81
82
  | `showTyping` | `true` | Send typing indicator before responding |
82
83
  | `streamUpdates` | `false` | Stream response via message updates |
83
84
  | `updateIntervalMs` | `10000` | Throttle interval for streaming updates (ms, min 500) |
84
85
  | `enabled` | `true` | Whether this account is enabled |
86
+ | `capabilities` | `[]` | Runtime capabilities to enable (see below) |
87
+
88
+ ### Capabilities
89
+
90
+ The `capabilities` array activates additional runtime features for the AI agent. Add them to the channel config in `openclaw.json`:
91
+
92
+ ```json
93
+ {
94
+ "channels": {
95
+ "bitrix24": {
96
+ ...
97
+ "capabilities": ["inlineButtons"]
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ | Capability | Description |
104
+ |---|---|
105
+ | `inlineButtons` | Enables inline keyboard buttons in bot messages. The AI agent will be able to send interactive buttons using `action=send` with `buttons=[[{text, callback_data, style?}]]`. Without this capability the agent will not attempt to generate buttons. |
85
106
 
86
107
  ### Access Policies
87
108
 
@@ -139,10 +160,10 @@ ngrok http 3000
139
160
 
140
161
  Use the HTTPS URL from ngrok as the event handler URL in Bitrix24 bot settings.
141
162
 
142
- ## Running
163
+ ## Running (restarting)
143
164
 
144
165
  ```bash
145
- openclaw start
166
+ systemctl restart openclaw
146
167
  ```
147
168
 
148
169
  On successful start, logs will show: `Bitrix24 gateway started, webhook at /hooks/bitrix24`
@@ -204,6 +225,19 @@ SendService:
204
225
  - **Deduplication**: B24 retries webhooks if it doesn't get a 200 in time — the plugin stores MESSAGE_IDs for 5 minutes
205
226
  - **Text conversion**: B24 uses BB-code, not Markdown — automatic conversion (`**bold**` -> `[B]bold[/B]`, etc.)
206
227
 
228
+ ## Customization
229
+
230
+ ### Bot Avatar
231
+
232
+ The default bot avatar is stored in `src/bot-avatar.ts` as a base64-encoded JPEG string. To change the avatar:
233
+
234
+ 1. Prepare a JPEG or PNG image (recommended: 200×200 px, ≤ 50 KB)
235
+ 2. Convert it to base64
236
+ 3. Replace the `DEFAULT_AVATAR_BASE64` value in `src/bot-avatar.ts`
237
+ 4. Rebuild and redeploy
238
+
239
+ The avatar can also be overridden per-account via the `botAvatar` config option (base64 string).
240
+
207
241
  ## Development
208
242
 
209
243
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ihazz/bitrix24",
3
- "version": "0.2.2",
3
+ "version": "0.2.5",
4
4
  "description": "Bitrix24 Messenger channel for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -89,7 +89,9 @@ export async function checkAccessWithPairing(params: {
89
89
  channel: 'bitrix24',
90
90
  accountId,
91
91
  });
92
- await sendReply(reply.text);
92
+ // buildPairingReply returns a string directly, not an object
93
+ const replyText = typeof reply === 'string' ? reply : (reply as { text?: string })?.text ?? String(reply);
94
+ await sendReply(replyText);
93
95
  }
94
96
 
95
97
  logger?.debug('Pairing request handled', { senderId, code, created });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Default bot avatar image (JPEG, 200×200).
3
+ * To change the avatar, replace this base64 string with a new one.
4
+ * Recommended: JPEG or PNG, 200×200 px, ≤ 50 KB.
5
+ */
6
+ export const DEFAULT_AVATAR_BASE64 = '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';
package/src/channel.ts CHANGED
@@ -8,6 +8,7 @@ import { MediaService } from './media-service.js';
8
8
  import type { DownloadedMedia } from './media-service.js';
9
9
  import { InboundHandler } from './inbound-handler.js';
10
10
  import { normalizeAllowEntry, checkAccessWithPairing } from './access-control.js';
11
+ import { DEFAULT_AVATAR_BASE64 } from './bot-avatar.js';
11
12
  import { defaultLogger } from './utils.js';
12
13
  import { getBitrix24Runtime } from './runtime.js';
13
14
  import type { ChannelPairingAdapter } from './runtime.js';
@@ -55,11 +56,6 @@ function mediaDownloadFailedMsg(lang: string | undefined, fileNames: string): st
55
56
  return fn(fileNames);
56
57
  }
57
58
 
58
- // ─── Default bot avatar (64×64 red circle with white "C") ───────────────────
59
-
60
- const DEFAULT_AVATAR_BASE64 =
61
- 'iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAABp0lEQVR42u1bsa3DIBBNmVlcZgbKNF7BK6TIHG4yi2fwAjS/8AIs4IZAdC5RAtxh+LyTniJFlq33gLsD7i4XGOxU+7vduiA5OEwOs8PioB2Mw+5g6dfQ/ws9558fWiatHF4OG5FMxUbvUS2Qvjo8aCStADS9/1oj+SdNY1sA/jvPWoiPgiP+y4wYT/PetDZtBXgVjSbk1ddKyB9Yi0QN8u5sa90bs29QkuTvOURjLUOIu9TIi5NmFENxr3lzBvEMIQyLTyBvv9ZAPkGENTs6xIS6khYTInOTnKqIJwoxpgqgayYfIYJOze3tPxHARu0daFdnWiAfIYL5eRdJW85myEeI8GBZ+9LTVfD9miXjK5XWCn1HZcX9wjm9yNb5mwAbpwCVkf+cMX7L+dmmZYXkDwwhAaYOyHtMIQHmTgSYQwIsHAJUTt5jEY3/DQigQwKYTgQwIQH2TgTYQyc/PTjAA5gB8AGIAsgDkAliL4DdIM4DcCKEM0GcCuNeADdDuBvE7TDqA1AhghohVImhThCVoqgVRrU4+gXQMYKeIXSNoW8QnaPoHe7F3lyXWKOZrIuJAAAAAElFTkSuQmCC';
62
-
63
59
  // ─── Default command keyboard ────────────────────────────────────────────────
64
60
 
65
61
  /** Default keyboard shown with command responses and welcome messages. */
@@ -298,6 +294,25 @@ export async function handleWebhookRequest(req: IncomingMessage, res: ServerResp
298
294
  }
299
295
  }
300
296
 
297
+ // ─── Outbound adapter helpers ────────────────────────────────────────────────
298
+
299
+ /**
300
+ * Build a minimal SendContext from the delivery pipeline's outbound context.
301
+ * The pipeline provides `cfg` (full OpenClaw config) and `to` (normalized dialog ID).
302
+ * We resolve the account config to get `webhookUrl`.
303
+ */
304
+ function resolveOutboundSendCtx(params: {
305
+ cfg: Record<string, unknown>;
306
+ to: string;
307
+ accountId?: string;
308
+ }): { webhookUrl?: string; dialogId: string } {
309
+ const { config } = resolveAccount(params.cfg, params.accountId);
310
+ return {
311
+ webhookUrl: config.webhookUrl,
312
+ dialogId: params.to,
313
+ };
314
+ }
315
+
301
316
  /**
302
317
  * The Bitrix24 channel plugin object.
303
318
  *
@@ -325,6 +340,26 @@ export const bitrix24Plugin = {
325
340
  inlineButtons: 'all',
326
341
  },
327
342
 
343
+ messaging: {
344
+ /**
345
+ * Normalize target ID by stripping the channel prefix.
346
+ * Called by the delivery pipeline so that `to` in outbound context is a clean numeric ID.
347
+ */
348
+ normalizeTarget: (raw: string) => raw.trim().replace(/^(bitrix24|b24|bx24):/i, ''),
349
+ targetResolver: {
350
+ hint: 'Use a numeric chat/dialog ID, e.g. "1" or "chat42".',
351
+ /**
352
+ * Recognize any numeric string as a valid Bitrix24 target ID.
353
+ * B24 dialog IDs can be short (e.g. "1"), so the default 6+ digit check is too strict.
354
+ */
355
+ looksLikeId: (raw: string, _normalized: string) => {
356
+ // raw may include channel prefix like "bitrix24:1" — strip it first
357
+ const stripped = raw.trim().replace(/^(bitrix24|b24|bx24):/i, '');
358
+ return /^\d+$/.test(stripped);
359
+ },
360
+ },
361
+ },
362
+
328
363
  config: {
329
364
  listAccountIds: (cfg: Record<string, unknown>) => listAccountIds(cfg),
330
365
  resolveAccount: (cfg: Record<string, unknown>, accountId?: string) =>
@@ -361,94 +396,87 @@ export const bitrix24Plugin = {
361
396
 
362
397
  outbound: {
363
398
  deliveryMode: 'direct' as const,
399
+ textChunkLimit: 4000,
364
400
 
365
401
  /**
366
402
  * Send a text message to B24 via the bot.
367
- * Called by OpenClaw when the agent produces a response.
403
+ * Called by the OpenClaw delivery pipeline (message tool path).
404
+ *
405
+ * Context shape: { cfg, to, accountId, text, replyToId?, threadId?, ... }
368
406
  */
369
- sendText: async (params: {
407
+ sendText: async (ctx: {
408
+ cfg: Record<string, unknown>;
409
+ to: string;
410
+ accountId?: string;
370
411
  text: string;
371
- context: B24MsgContext;
372
- account: { config: { webhookUrl?: string; showTyping?: boolean } };
412
+ [key: string]: unknown;
373
413
  }) => {
374
- const { text, context, account } = params;
375
-
376
- if (!gatewayState) {
377
- return { ok: false, error: 'Gateway not started', channel: 'bitrix24' };
378
- }
379
-
380
- const { sendService } = gatewayState;
381
-
382
- const sendCtx = {
383
- webhookUrl: account.config.webhookUrl,
384
- clientEndpoint: context.clientEndpoint,
385
- botToken: context.botToken,
386
- dialogId: context.chatId,
387
- };
388
-
389
- // Send typing indicator
390
- if (account.config.showTyping !== false) {
391
- await sendService.sendTyping(sendCtx);
392
- }
393
-
394
- // Send the response
395
- const result = await sendService.sendText(sendCtx, text);
396
-
397
- return {
398
- ok: result.ok,
399
- messageId: result.messageId,
400
- channel: 'bitrix24' as const,
401
- error: result.error,
402
- };
414
+ if (!gatewayState) throw new Error('Bitrix24 gateway not started');
415
+ const sendCtx = resolveOutboundSendCtx(ctx);
416
+ const result = await gatewayState.sendService.sendText(sendCtx, ctx.text);
417
+ return { messageId: String(result.messageId ?? '') };
403
418
  },
404
419
 
405
420
  /**
406
- * Send a payload with optional channelData (keyboards, etc.) to B24.
407
- * Called by OpenClaw when the response includes channelData.
421
+ * Send a media message to B24.
422
+ * Called by the delivery pipeline when the agent sends media.
423
+ *
424
+ * Note: full media upload requires OAuth bot token (clientEndpoint + botToken)
425
+ * which is only available in the reply path (inbound webhook events).
426
+ * In the message tool path we only have the webhook URL, so we send the caption text.
408
427
  */
409
- sendPayload: async (params: {
428
+ sendMedia: async (ctx: {
429
+ cfg: Record<string, unknown>;
430
+ to: string;
431
+ accountId?: string;
410
432
  text: string;
411
- channelData?: Record<string, unknown>;
412
- context: B24MsgContext;
413
- account: { config: { webhookUrl?: string; showTyping?: boolean } };
433
+ mediaUrl?: string;
434
+ [key: string]: unknown;
414
435
  }) => {
415
- const { text, channelData, context, account } = params;
416
-
417
- if (!gatewayState) {
418
- return { ok: false, error: 'Gateway not started', channel: 'bitrix24' };
436
+ if (!gatewayState) throw new Error('Bitrix24 gateway not started');
437
+ const sendCtx = resolveOutboundSendCtx(ctx);
438
+ // Media upload via message tool path not supported (no OAuth token).
439
+ // Send caption text only.
440
+ if (ctx.text) {
441
+ const result = await gatewayState.sendService.sendText(sendCtx, ctx.text);
442
+ return { messageId: String(result.messageId ?? '') };
419
443
  }
444
+ return { messageId: '' };
445
+ },
420
446
 
421
- const { sendService } = gatewayState;
422
-
423
- const sendCtx = {
424
- webhookUrl: account.config.webhookUrl,
425
- clientEndpoint: context.clientEndpoint,
426
- botToken: context.botToken,
427
- dialogId: context.chatId,
428
- };
429
-
430
- // Send typing indicator
431
- if (account.config.showTyping !== false) {
432
- await sendService.sendTyping(sendCtx);
433
- }
447
+ /**
448
+ * Send a rich payload with optional channelData (keyboards, etc.) to B24.
449
+ * Called by the delivery pipeline when payload includes channelData.
450
+ *
451
+ * Context shape: { cfg, to, accountId, text, mediaUrl?, payload, ... }
452
+ */
453
+ sendPayload: async (ctx: {
454
+ cfg: Record<string, unknown>;
455
+ to: string;
456
+ accountId?: string;
457
+ text: string;
458
+ mediaUrl?: string;
459
+ payload?: { channelData?: Record<string, unknown>; [key: string]: unknown };
460
+ [key: string]: unknown;
461
+ }) => {
462
+ if (!gatewayState) throw new Error('Bitrix24 gateway not started');
463
+ const sendCtx = resolveOutboundSendCtx(ctx);
434
464
 
435
465
  // Extract keyboard from channelData
436
- const keyboard = channelData
437
- ? extractKeyboardFromPayload({ channelData })
466
+ const keyboard = ctx.payload?.channelData
467
+ ? extractKeyboardFromPayload({ channelData: ctx.payload.channelData })
438
468
  : undefined;
439
469
 
440
- const result = await sendService.sendText(
441
- sendCtx,
442
- text,
443
- keyboard ? { keyboard } : undefined,
444
- );
445
-
446
- return {
447
- ok: result.ok,
448
- messageId: result.messageId,
449
- channel: 'bitrix24' as const,
450
- error: result.error,
451
- };
470
+ // Send text + keyboard
471
+ if (ctx.text) {
472
+ const result = await gatewayState.sendService.sendText(
473
+ sendCtx,
474
+ ctx.text,
475
+ keyboard ? { keyboard } : undefined,
476
+ );
477
+ return { messageId: String(result.messageId ?? '') };
478
+ }
479
+ return { messageId: '' };
452
480
  },
453
481
  },
454
482
 
@@ -415,67 +415,64 @@ describe('bitrix24Plugin', () => {
415
415
  });
416
416
  });
417
417
 
418
+ describe('messaging', () => {
419
+ it('normalizeTarget strips bitrix24: prefix', () => {
420
+ expect(bitrix24Plugin.messaging.normalizeTarget('bitrix24:1')).toBe('1');
421
+ expect(bitrix24Plugin.messaging.normalizeTarget('b24:42')).toBe('42');
422
+ expect(bitrix24Plugin.messaging.normalizeTarget('bx24:100')).toBe('100');
423
+ expect(bitrix24Plugin.messaging.normalizeTarget('123')).toBe('123');
424
+ expect(bitrix24Plugin.messaging.normalizeTarget(' bitrix24:5 ')).toBe('5');
425
+ });
426
+
427
+ it('looksLikeId recognizes numeric IDs', () => {
428
+ expect(bitrix24Plugin.messaging.targetResolver.looksLikeId('bitrix24:1', '1')).toBe(true);
429
+ expect(bitrix24Plugin.messaging.targetResolver.looksLikeId('42', '42')).toBe(true);
430
+ expect(bitrix24Plugin.messaging.targetResolver.looksLikeId('abc', 'abc')).toBe(false);
431
+ });
432
+ });
433
+
418
434
  describe('outbound', () => {
419
435
  it('has direct delivery mode', () => {
420
436
  expect(bitrix24Plugin.outbound.deliveryMode).toBe('direct');
421
437
  });
422
438
 
423
- it('sendText returns error when gateway not started', async () => {
424
- const result = await bitrix24Plugin.outbound.sendText({
425
- text: 'Hello',
426
- context: {
427
- channel: 'bitrix24',
428
- senderId: '1',
429
- senderName: 'Test',
430
- chatId: '1',
431
- chatInternalId: '100',
432
- messageId: '1',
433
- text: 'Hello',
434
- isDm: true,
435
- isGroup: false,
436
- media: [],
437
- raw: {} as any,
438
- botToken: 'token',
439
- userToken: 'utoken',
440
- clientEndpoint: 'https://test.bitrix24.com/rest/',
441
- botId: 1,
442
- memberId: 'mem1',
443
- },
444
- account: { config: { webhookUrl: 'https://test.bitrix24.com/rest/1/token/' } },
445
- });
439
+ it('has textChunkLimit of 4000', () => {
440
+ expect(bitrix24Plugin.outbound.textChunkLimit).toBe(4000);
441
+ });
446
442
 
447
- expect(result.ok).toBe(false);
448
- expect(result.error).toBe('Gateway not started');
449
- expect(result.channel).toBe('bitrix24');
443
+ it('sendText throws when gateway not started', async () => {
444
+ await expect(
445
+ bitrix24Plugin.outbound.sendText({
446
+ cfg: { channels: { bitrix24: { webhookUrl: 'https://test.bitrix24.com/rest/1/token/' } } },
447
+ to: '1',
448
+ accountId: 'default',
449
+ text: 'Hello',
450
+ }),
451
+ ).rejects.toThrow('Bitrix24 gateway not started');
450
452
  });
451
453
 
452
- it('sendPayload returns error when gateway not started', async () => {
453
- const result = await bitrix24Plugin.outbound.sendPayload({
454
- text: 'Hello',
455
- channelData: { telegram: { buttons: [[{ text: 'OK' }]] } },
456
- context: {
457
- channel: 'bitrix24',
458
- senderId: '1',
459
- senderName: 'Test',
460
- chatId: '1',
461
- chatInternalId: '100',
462
- messageId: '1',
454
+ it('sendMedia throws when gateway not started', async () => {
455
+ await expect(
456
+ bitrix24Plugin.outbound.sendMedia({
457
+ cfg: { channels: { bitrix24: { webhookUrl: 'https://test.bitrix24.com/rest/1/token/' } } },
458
+ to: '1',
459
+ accountId: 'default',
463
460
  text: 'Hello',
464
- isDm: true,
465
- isGroup: false,
466
- media: [],
467
- raw: {} as any,
468
- botToken: 'token',
469
- userToken: 'utoken',
470
- clientEndpoint: 'https://test.bitrix24.com/rest/',
471
- botId: 1,
472
- memberId: 'mem1',
473
- },
474
- account: { config: { webhookUrl: 'https://test.bitrix24.com/rest/1/token/' } },
475
- });
461
+ mediaUrl: '/tmp/test.jpg',
462
+ }),
463
+ ).rejects.toThrow('Bitrix24 gateway not started');
464
+ });
476
465
 
477
- expect(result.ok).toBe(false);
478
- expect(result.error).toBe('Gateway not started');
466
+ it('sendPayload throws when gateway not started', async () => {
467
+ await expect(
468
+ bitrix24Plugin.outbound.sendPayload({
469
+ cfg: { channels: { bitrix24: { webhookUrl: 'https://test.bitrix24.com/rest/1/token/' } } },
470
+ to: '1',
471
+ accountId: 'default',
472
+ text: 'Hello',
473
+ payload: { channelData: { telegram: { buttons: [[{ text: 'OK' }]] } } },
474
+ }),
475
+ ).rejects.toThrow('Bitrix24 gateway not started');
479
476
  });
480
477
  });
481
478
 
@@ -28,7 +28,7 @@ describe('markdownToBbCode', () => {
28
28
 
29
29
  it('converts code blocks', () => {
30
30
  const md = '```js\nconst x = 1;\n```';
31
- const expected = '[CODE]const x = 1;\n[/CODE]';
31
+ const expected = '[CODE]const x = 1;[/CODE]';
32
32
  expect(markdownToBbCode(md)).toBe(expected);
33
33
  });
34
34