@ihazz/bitrix24 1.1.1 → 1.1.3

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.
Files changed (127) hide show
  1. package/README.md +5 -0
  2. package/dist/index.d.ts +30 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +55 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/access-control.d.ts +43 -0
  7. package/dist/src/access-control.d.ts.map +1 -0
  8. package/dist/src/access-control.js +128 -0
  9. package/dist/src/access-control.js.map +1 -0
  10. package/dist/src/api.d.ts +161 -0
  11. package/dist/src/api.d.ts.map +1 -0
  12. package/dist/src/api.js +357 -0
  13. package/dist/src/api.js.map +1 -0
  14. package/dist/src/bot-avatar.d.ts +7 -0
  15. package/dist/src/bot-avatar.d.ts.map +1 -0
  16. package/dist/src/bot-avatar.js +7 -0
  17. package/dist/src/bot-avatar.js.map +1 -0
  18. package/dist/src/channel.d.ts +216 -0
  19. package/dist/src/channel.d.ts.map +1 -0
  20. package/dist/src/channel.js +2324 -0
  21. package/dist/src/channel.js.map +1 -0
  22. package/dist/src/commands.d.ts +22 -0
  23. package/dist/src/commands.d.ts.map +1 -0
  24. package/dist/src/commands.js +160 -0
  25. package/dist/src/commands.js.map +1 -0
  26. package/dist/src/config-schema.d.ts +356 -0
  27. package/dist/src/config-schema.d.ts.map +1 -0
  28. package/dist/src/config-schema.js +43 -0
  29. package/dist/src/config-schema.js.map +1 -0
  30. package/dist/src/config.d.ts +11 -0
  31. package/dist/src/config.d.ts.map +1 -0
  32. package/dist/src/config.js +50 -0
  33. package/dist/src/config.js.map +1 -0
  34. package/dist/src/dedup.d.ts +22 -0
  35. package/dist/src/dedup.d.ts.map +1 -0
  36. package/dist/src/dedup.js +49 -0
  37. package/dist/src/dedup.js.map +1 -0
  38. package/dist/src/group-access.d.ts +52 -0
  39. package/dist/src/group-access.d.ts.map +1 -0
  40. package/dist/src/group-access.js +180 -0
  41. package/dist/src/group-access.js.map +1 -0
  42. package/dist/src/history-cache.d.ts +41 -0
  43. package/dist/src/history-cache.d.ts.map +1 -0
  44. package/dist/src/history-cache.js +82 -0
  45. package/dist/src/history-cache.js.map +1 -0
  46. package/dist/src/i18n.d.ts +22 -0
  47. package/dist/src/i18n.d.ts.map +1 -0
  48. package/dist/src/i18n.js +175 -0
  49. package/dist/src/i18n.js.map +1 -0
  50. package/dist/src/inbound-handler.d.ts +92 -0
  51. package/dist/src/inbound-handler.d.ts.map +1 -0
  52. package/dist/src/inbound-handler.js +417 -0
  53. package/dist/src/inbound-handler.js.map +1 -0
  54. package/dist/src/media-service.d.ts +52 -0
  55. package/dist/src/media-service.d.ts.map +1 -0
  56. package/dist/src/media-service.js +423 -0
  57. package/dist/src/media-service.js.map +1 -0
  58. package/dist/src/message-utils.d.ts +34 -0
  59. package/dist/src/message-utils.d.ts.map +1 -0
  60. package/dist/src/message-utils.js +392 -0
  61. package/dist/src/message-utils.js.map +1 -0
  62. package/dist/src/polling-service.d.ts +39 -0
  63. package/dist/src/polling-service.d.ts.map +1 -0
  64. package/dist/src/polling-service.js +204 -0
  65. package/dist/src/polling-service.js.map +1 -0
  66. package/dist/src/rate-limiter.d.ts +22 -0
  67. package/dist/src/rate-limiter.d.ts.map +1 -0
  68. package/dist/src/rate-limiter.js +72 -0
  69. package/dist/src/rate-limiter.js.map +1 -0
  70. package/dist/src/runtime.d.ts +106 -0
  71. package/dist/src/runtime.d.ts.map +1 -0
  72. package/dist/src/runtime.js +11 -0
  73. package/dist/src/runtime.js.map +1 -0
  74. package/dist/src/send-service.d.ts +66 -0
  75. package/dist/src/send-service.d.ts.map +1 -0
  76. package/dist/src/send-service.js +177 -0
  77. package/dist/src/send-service.js.map +1 -0
  78. package/dist/src/state-paths.d.ts +3 -0
  79. package/dist/src/state-paths.d.ts.map +1 -0
  80. package/dist/src/state-paths.js +23 -0
  81. package/dist/src/state-paths.js.map +1 -0
  82. package/dist/src/types.d.ts +381 -0
  83. package/dist/src/types.d.ts.map +1 -0
  84. package/dist/src/types.js +3 -0
  85. package/dist/src/types.js.map +1 -0
  86. package/dist/src/utils.d.ts +60 -0
  87. package/dist/src/utils.d.ts.map +1 -0
  88. package/dist/src/utils.js +131 -0
  89. package/dist/src/utils.js.map +1 -0
  90. package/index.ts +1 -1
  91. package/openclaw.plugin.json +278 -1
  92. package/package.json +19 -2
  93. package/src/api.ts +0 -3
  94. package/src/channel.ts +76 -73
  95. package/src/config-schema.ts +1 -2
  96. package/src/config.ts +6 -8
  97. package/src/group-access.ts +1 -8
  98. package/src/inbound-handler.ts +128 -15
  99. package/src/media-service.ts +229 -61
  100. package/src/polling-service.ts +2 -3
  101. package/src/send-service.ts +4 -3
  102. package/src/state-paths.ts +28 -0
  103. package/src/types.ts +1 -2
  104. package/src/utils.ts +31 -4
  105. package/tests/access-control.test.ts +0 -398
  106. package/tests/api.test.ts +0 -226
  107. package/tests/channel-flow.test.ts +0 -1692
  108. package/tests/channel.test.ts +0 -842
  109. package/tests/commands.test.ts +0 -57
  110. package/tests/config.test.ts +0 -210
  111. package/tests/dedup.test.ts +0 -50
  112. package/tests/fixtures/onimbotjoinchat.json +0 -48
  113. package/tests/fixtures/onimbotmessageadd-file.json +0 -86
  114. package/tests/fixtures/onimbotmessageadd-text.json +0 -59
  115. package/tests/fixtures/onimcommandadd.json +0 -45
  116. package/tests/group-access.test.ts +0 -340
  117. package/tests/history-cache.test.ts +0 -117
  118. package/tests/i18n.test.ts +0 -90
  119. package/tests/inbound-handler.test.ts +0 -1033
  120. package/tests/index.test.ts +0 -94
  121. package/tests/media-service.test.ts +0 -319
  122. package/tests/message-utils.test.ts +0 -184
  123. package/tests/polling-service.test.ts +0 -115
  124. package/tests/rate-limiter.test.ts +0 -52
  125. package/tests/send-service.test.ts +0 -162
  126. package/tsconfig.json +0 -22
  127. package/vitest.config.ts +0 -9
@@ -0,0 +1,28 @@
1
+ import { homedir } from 'node:os';
2
+ import { join, resolve as resolvePath } from 'node:path';
3
+
4
+ function resolveHomeDir(env: NodeJS.ProcessEnv = process.env): string {
5
+ const homePath = env.HOME?.trim() || env.USERPROFILE?.trim();
6
+ if (homePath) {
7
+ return resolvePath(homePath);
8
+ }
9
+
10
+ return homedir();
11
+ }
12
+
13
+ function resolveOpenClawStateDir(env: NodeJS.ProcessEnv = process.env): string {
14
+ const override = env.OPENCLAW_STATE_DIR?.trim() || env.CLAWDBOT_STATE_DIR?.trim();
15
+ if (override) {
16
+ return resolvePath(override);
17
+ }
18
+
19
+ return join(resolveHomeDir(env), '.openclaw');
20
+ }
21
+
22
+ export function resolveManagedMediaDir(env: NodeJS.ProcessEnv = process.env): string {
23
+ return join(resolveOpenClawStateDir(env), 'media', 'bitrix24');
24
+ }
25
+
26
+ export function resolvePollingStateDir(env: NodeJS.ProcessEnv = process.env): string {
27
+ return join(resolveOpenClawStateDir(env), 'state', 'bitrix24');
28
+ }
package/src/types.ts CHANGED
@@ -411,8 +411,7 @@ export interface Bitrix24AccountConfig {
411
411
  groups?: Record<string, Bitrix24GroupConfig>;
412
412
  agentWatch?: Record<string, Bitrix24AgentWatchConfig[]>;
413
413
  showTyping?: boolean;
414
- streamUpdates?: boolean;
415
- updateIntervalMs?: number;
414
+ verboseLog?: boolean;
416
415
  }
417
416
 
418
417
  export interface Bitrix24PluginConfig extends Bitrix24AccountConfig {
package/src/utils.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { Logger } from './types.js';
2
+
1
3
  /**
2
4
  * Mask a token for safe logging: show first 4 and last 4 characters.
3
5
  */
@@ -7,11 +9,11 @@ export function maskToken(token: string): string {
7
9
  }
8
10
 
9
11
  /**
10
- * Mask Bitrix24 webhook credentials in a REST URL before logging it.
12
+ * Mask sensitive URL parts before logging them.
11
13
  */
12
- export function maskWebhookUrl(webhookUrl: string): string {
14
+ export function maskUrlForLog(rawUrl: string): string {
13
15
  try {
14
- const url = new URL(webhookUrl);
16
+ const url = new URL(rawUrl);
15
17
  const pathParts = url.pathname.split('/').filter(Boolean);
16
18
 
17
19
  if (pathParts[0] === 'rest' && pathParts.length >= 3) {
@@ -25,10 +27,17 @@ export function maskWebhookUrl(webhookUrl: string): string {
25
27
  url.search = '';
26
28
  return url.toString();
27
29
  } catch {
28
- return '[masked webhook url]';
30
+ return '[masked url]';
29
31
  }
30
32
  }
31
33
 
34
+ /**
35
+ * Mask Bitrix24 webhook credentials in a REST URL before logging it.
36
+ */
37
+ export function maskWebhookUrl(webhookUrl: string): string {
38
+ return maskUrlForLog(webhookUrl);
39
+ }
40
+
32
41
  /**
33
42
  * Convert an unknown error into a log-friendly plain object.
34
43
  */
@@ -114,6 +123,24 @@ export function stripChannelPrefix(id: string): string {
114
123
  return id.replace(CHANNEL_PREFIX_RE, '');
115
124
  }
116
125
 
126
+ const noop = (): void => undefined;
127
+
128
+ /**
129
+ * Build a logger that keeps warnings/errors and enables info/debug only in verbose mode.
130
+ */
131
+ export function createVerboseLogger(logger: Logger = defaultLogger, verbose = false): Logger {
132
+ if (verbose) {
133
+ return logger;
134
+ }
135
+
136
+ return {
137
+ info: noop,
138
+ debug: noop,
139
+ warn: (...args: unknown[]) => logger.warn(...args),
140
+ error: (...args: unknown[]) => logger.error(...args),
141
+ };
142
+ }
143
+
117
144
  /**
118
145
  * Simple console logger fallback.
119
146
  */
@@ -1,398 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import {
3
- checkAccess,
4
- checkAccessWithPairing,
5
- getWebhookUserId,
6
- normalizeAllowEntry,
7
- normalizeAllowList,
8
- } from '../src/access-control.js';
9
- import type { PluginRuntime, ChannelPairingAdapter } from '../src/runtime.js';
10
-
11
- describe('normalizeAllowEntry', () => {
12
- it('strips bitrix24: prefix', () => {
13
- expect(normalizeAllowEntry('bitrix24:42')).toBe('42');
14
- });
15
-
16
- it('strips b24: prefix', () => {
17
- expect(normalizeAllowEntry('b24:100')).toBe('100');
18
- });
19
-
20
- it('strips bx24: prefix', () => {
21
- expect(normalizeAllowEntry('bx24:7')).toBe('7');
22
- });
23
-
24
- it('strips prefixes case-insensitively', () => {
25
- expect(normalizeAllowEntry('Bitrix24:42')).toBe('42');
26
- expect(normalizeAllowEntry('B24:100')).toBe('100');
27
- expect(normalizeAllowEntry('BX24:7')).toBe('7');
28
- });
29
-
30
- it('returns plain ID as-is', () => {
31
- expect(normalizeAllowEntry('42')).toBe('42');
32
- });
33
-
34
- it('trims whitespace', () => {
35
- expect(normalizeAllowEntry(' b24:42 ')).toBe('42');
36
- });
37
- });
38
-
39
- describe('normalizeAllowList', () => {
40
- it('normalizes, deduplicates and filters empty allowFrom entries', () => {
41
- expect(normalizeAllowList([' bitrix24:42 ', '42', 'b24:7', ''])).toEqual(['42', '7']);
42
- });
43
-
44
- it('returns empty list for missing allowFrom config', () => {
45
- expect(normalizeAllowList(undefined)).toEqual([]);
46
- });
47
- });
48
-
49
- describe('getWebhookUserId', () => {
50
- it('extracts owner ID from webhook URL', () => {
51
- expect(getWebhookUserId('https://test.bitrix24.com/rest/42/token/')).toBe('42');
52
- });
53
-
54
- it('normalizes prefixed user IDs', () => {
55
- expect(getWebhookUserId('https://test.bitrix24.com/rest/b24:42/token/')).toBe('42');
56
- });
57
-
58
- it('returns null for invalid URLs', () => {
59
- expect(getWebhookUserId('not-a-url')).toBeNull();
60
- });
61
-
62
- it('returns null when rest segment is missing', () => {
63
- expect(getWebhookUserId('https://test.bitrix24.com/api/42/token/')).toBeNull();
64
- });
65
- });
66
-
67
- describe('checkAccess', () => {
68
- it('defaults to webhookUser and denies without a valid webhook owner', () => {
69
- expect(checkAccess('1', {})).toBe(false);
70
- });
71
-
72
- it('allows only the webhook owner in webhookUser mode', () => {
73
- const config = {
74
- dmPolicy: 'webhookUser' as const,
75
- webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
76
- };
77
-
78
- expect(checkAccess('42', config)).toBe(true);
79
- expect(checkAccess('99', config)).toBe(false);
80
- });
81
-
82
- it('allows webhook owner when dmPolicy is omitted', () => {
83
- expect(checkAccess('42', { webhookUrl: 'https://test.bitrix24.com/rest/42/token/' })).toBe(true);
84
- });
85
-
86
- it('keeps senderId as the access identity even when direct dialogId differs', () => {
87
- const config = {
88
- dmPolicy: 'webhookUser' as const,
89
- webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
90
- };
91
-
92
- expect(checkAccess('42', config, { dialogId: '2386', isDirect: true })).toBe(true);
93
- expect(checkAccess('77', config, { dialogId: '42', isDirect: true })).toBe(false);
94
- });
95
-
96
- it('denies when webhookUser mode has no valid webhook owner', () => {
97
- expect(checkAccess('42', { dmPolicy: 'webhookUser', webhookUrl: 'invalid' })).toBe(false);
98
- });
99
-
100
- it('returns false for pairing mode without runtime state', () => {
101
- expect(checkAccess('1', { dmPolicy: 'pairing' })).toBe(false);
102
- });
103
-
104
- it('allows any sender in open mode', () => {
105
- expect(checkAccess('1', { dmPolicy: 'open' })).toBe(true);
106
- expect(checkAccess('77', { dmPolicy: 'open' })).toBe(true);
107
- });
108
-
109
- it('allows only configured identities in allowlist mode', () => {
110
- expect(checkAccess('42', { dmPolicy: 'allowlist', allowFrom: ['bitrix24:42'] })).toBe(true);
111
- expect(checkAccess('77', { dmPolicy: 'allowlist', allowFrom: ['bitrix24:42'] })).toBe(false);
112
- });
113
- });
114
-
115
- function makeMockRuntime(storeAllowFrom: string[] = []): PluginRuntime {
116
- return {
117
- config: { loadConfig: () => ({}) },
118
- channel: {
119
- routing: { resolveAgentRoute: vi.fn() },
120
- reply: {
121
- finalizeInboundContext: vi.fn(),
122
- dispatchReplyWithBufferedBlockDispatcher: vi.fn(),
123
- },
124
- session: { recordInboundSession: vi.fn() },
125
- pairing: {
126
- readAllowFromStore: vi.fn().mockResolvedValue(storeAllowFrom),
127
- upsertPairingRequest: vi.fn().mockResolvedValue({ code: 'ABCD1234', created: true }),
128
- buildPairingReply: vi.fn().mockReturnValue('Your pairing code: ABCD1234'),
129
- },
130
- },
131
- logging: { getChildLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }) },
132
- } as unknown as PluginRuntime;
133
- }
134
-
135
- const mockAdapter: ChannelPairingAdapter = {
136
- idLabel: 'bitrix24UserId',
137
- normalizeAllowEntry: (entry) => entry.replace(/^(bitrix24|b24|bx24):/i, ''),
138
- };
139
-
140
- const silentLogger = { debug: vi.fn() };
141
-
142
- describe('checkAccessWithPairing', () => {
143
- it('defaults to webhookUser when dmPolicy is omitted', async () => {
144
- const runtime = makeMockRuntime();
145
-
146
- const result = await checkAccessWithPairing({
147
- senderId: '42',
148
- config: { webhookUrl: 'https://test.bitrix24.com/rest/42/token/' },
149
- runtime,
150
- accountId: 'default',
151
- pairingAdapter: mockAdapter,
152
- sendReply: vi.fn(),
153
- logger: silentLogger,
154
- });
155
-
156
- expect(result).toBe('allow');
157
- expect(runtime.channel.pairing.readAllowFromStore).not.toHaveBeenCalled();
158
- });
159
-
160
- it('allows only the webhook owner in webhookUser mode', async () => {
161
- const runtime = makeMockRuntime();
162
- const sendReply = vi.fn();
163
-
164
- const result = await checkAccessWithPairing({
165
- senderId: '42',
166
- config: {
167
- dmPolicy: 'webhookUser',
168
- webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
169
- },
170
- runtime,
171
- accountId: 'default',
172
- pairingAdapter: mockAdapter,
173
- sendReply,
174
- logger: silentLogger,
175
- });
176
-
177
- expect(result).toBe('allow');
178
- expect(runtime.channel.pairing.readAllowFromStore).not.toHaveBeenCalled();
179
- expect(runtime.channel.pairing.upsertPairingRequest).not.toHaveBeenCalled();
180
- expect(sendReply).not.toHaveBeenCalled();
181
- });
182
-
183
- it('keeps senderId as the identity in webhookUser mode even when dialogId differs', async () => {
184
- const runtime = makeMockRuntime();
185
-
186
- const allowed = await checkAccessWithPairing({
187
- senderId: '42',
188
- dialogId: '2386',
189
- isDirect: true,
190
- config: {
191
- dmPolicy: 'webhookUser',
192
- webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
193
- },
194
- runtime,
195
- accountId: 'default',
196
- pairingAdapter: mockAdapter,
197
- sendReply: vi.fn(),
198
- logger: silentLogger,
199
- });
200
-
201
- const denied = await checkAccessWithPairing({
202
- senderId: '77',
203
- dialogId: '42',
204
- isDirect: true,
205
- config: {
206
- dmPolicy: 'webhookUser',
207
- webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
208
- },
209
- runtime,
210
- accountId: 'default',
211
- pairingAdapter: mockAdapter,
212
- sendReply: vi.fn(),
213
- logger: silentLogger,
214
- });
215
-
216
- expect(allowed).toBe('allow');
217
- expect(denied).toBe('deny');
218
- });
219
-
220
- it('denies non-owner users in webhookUser mode', async () => {
221
- const runtime = makeMockRuntime();
222
- const sendReply = vi.fn();
223
-
224
- const result = await checkAccessWithPairing({
225
- senderId: '77',
226
- config: {
227
- dmPolicy: 'webhookUser',
228
- webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
229
- },
230
- runtime,
231
- accountId: 'default',
232
- pairingAdapter: mockAdapter,
233
- sendReply,
234
- logger: silentLogger,
235
- });
236
-
237
- expect(result).toBe('deny');
238
- expect(runtime.channel.pairing.readAllowFromStore).not.toHaveBeenCalled();
239
- expect(runtime.channel.pairing.upsertPairingRequest).not.toHaveBeenCalled();
240
- expect(sendReply).not.toHaveBeenCalled();
241
- });
242
-
243
- it('returns allow when sender is in pairing store', async () => {
244
- const runtime = makeMockRuntime(['42']);
245
-
246
- const result = await checkAccessWithPairing({
247
- senderId: '42',
248
- config: { dmPolicy: 'pairing' },
249
- runtime,
250
- accountId: 'default',
251
- pairingAdapter: mockAdapter,
252
- sendReply: vi.fn(),
253
- logger: silentLogger,
254
- });
255
-
256
- expect(result).toBe('allow');
257
- });
258
-
259
- it('returns allow when sender is in config allowFrom', async () => {
260
- const runtime = makeMockRuntime();
261
-
262
- const result = await checkAccessWithPairing({
263
- senderId: '42',
264
- config: {
265
- dmPolicy: 'pairing',
266
- allowFrom: ['bitrix24:42'],
267
- },
268
- runtime,
269
- accountId: 'default',
270
- pairingAdapter: mockAdapter,
271
- sendReply: vi.fn(),
272
- logger: silentLogger,
273
- });
274
-
275
- expect(result).toBe('allow');
276
- expect(runtime.channel.pairing.upsertPairingRequest).not.toHaveBeenCalled();
277
- });
278
-
279
- it('returns allow for any sender in open mode', async () => {
280
- const runtime = makeMockRuntime();
281
-
282
- const result = await checkAccessWithPairing({
283
- senderId: '77',
284
- config: { dmPolicy: 'open' },
285
- runtime,
286
- accountId: 'default',
287
- pairingAdapter: mockAdapter,
288
- sendReply: vi.fn(),
289
- logger: silentLogger,
290
- });
291
-
292
- expect(result).toBe('allow');
293
- });
294
-
295
- it('returns deny for non-allowlisted sender in allowlist mode', async () => {
296
- const runtime = makeMockRuntime();
297
-
298
- const result = await checkAccessWithPairing({
299
- senderId: '77',
300
- config: { dmPolicy: 'allowlist', allowFrom: ['bitrix24:42'] },
301
- runtime,
302
- accountId: 'default',
303
- pairingAdapter: mockAdapter,
304
- sendReply: vi.fn(),
305
- logger: silentLogger,
306
- });
307
-
308
- expect(result).toBe('deny');
309
- expect(runtime.channel.pairing.upsertPairingRequest).not.toHaveBeenCalled();
310
- });
311
-
312
- it('uses senderId as the pairing identity even when direct dialogId differs', async () => {
313
- const runtime = makeMockRuntime(['42']);
314
-
315
- const result = await checkAccessWithPairing({
316
- senderId: '42',
317
- dialogId: '42',
318
- isDirect: true,
319
- config: { dmPolicy: 'pairing' },
320
- runtime,
321
- accountId: 'default',
322
- pairingAdapter: mockAdapter,
323
- sendReply: vi.fn(),
324
- logger: silentLogger,
325
- });
326
-
327
- expect(result).toBe('allow');
328
- });
329
-
330
- it('normalizes prefixed entries from pairing store', async () => {
331
- const runtime = makeMockRuntime(['b24:42']);
332
-
333
- const result = await checkAccessWithPairing({
334
- senderId: '42',
335
- config: { dmPolicy: 'pairing' },
336
- runtime,
337
- accountId: 'default',
338
- pairingAdapter: mockAdapter,
339
- sendReply: vi.fn(),
340
- logger: silentLogger,
341
- });
342
-
343
- expect(result).toBe('allow');
344
- });
345
-
346
- it('upserts pairing request and sends reply for new pairing', async () => {
347
- const runtime = makeMockRuntime();
348
- const sendReply = vi.fn();
349
-
350
- const result = await checkAccessWithPairing({
351
- senderId: '77',
352
- config: { dmPolicy: 'pairing' },
353
- runtime,
354
- accountId: 'default',
355
- pairingAdapter: mockAdapter,
356
- sendReply,
357
- logger: silentLogger,
358
- });
359
-
360
- expect(result).toBe('pairing');
361
- expect(runtime.channel.pairing.readAllowFromStore).toHaveBeenCalledWith('bitrix24', '', 'default');
362
- expect(runtime.channel.pairing.upsertPairingRequest).toHaveBeenCalledWith({
363
- channel: 'bitrix24',
364
- id: '77',
365
- accountId: 'default',
366
- meta: {},
367
- pairingAdapter: mockAdapter,
368
- });
369
- expect(runtime.channel.pairing.buildPairingReply).toHaveBeenCalledWith({
370
- code: 'ABCD1234',
371
- channel: 'bitrix24',
372
- accountId: 'default',
373
- });
374
- expect(sendReply).toHaveBeenCalledWith('Your pairing code: ABCD1234');
375
- });
376
-
377
- it('does not send reply for duplicate pairing request', async () => {
378
- const runtime = makeMockRuntime();
379
- (runtime.channel.pairing.upsertPairingRequest as ReturnType<typeof vi.fn>).mockResolvedValue({
380
- code: 'ABCD1234',
381
- created: false,
382
- });
383
- const sendReply = vi.fn();
384
-
385
- const result = await checkAccessWithPairing({
386
- senderId: '77',
387
- config: { dmPolicy: 'pairing' },
388
- runtime,
389
- accountId: 'default',
390
- pairingAdapter: mockAdapter,
391
- sendReply,
392
- logger: silentLogger,
393
- });
394
-
395
- expect(result).toBe('pairing');
396
- expect(sendReply).not.toHaveBeenCalled();
397
- });
398
- });