@ihazz/bitrix24 1.1.0 → 1.1.2

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.
@@ -1,57 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { buildCommandsHelpText, formatModelsCommandReply } from '../src/commands.js';
3
-
4
- describe('buildCommandsHelpText', () => {
5
- it('builds concise Russian help for keyboard commands', () => {
6
- const text = buildCommandsHelpText('ru', { concise: true });
7
-
8
- expect(text).toContain('[B]Основные команды[/B]');
9
- expect(text).toContain('[send=/status]/status[/send]');
10
- expect(text).toContain('[send=/commands]/commands[/send]');
11
- expect(text).toContain('[put=/models ]/models[/put]');
12
- expect(text).toContain('[COLOR=#6C788A]Полный список:[/COLOR] [send=/commands]');
13
- });
14
-
15
- it('builds full Russian commands list', () => {
16
- const text = buildCommandsHelpText('ru');
17
-
18
- expect(text).toContain('[B]Доступные команды[/B]');
19
- expect(text).toContain('[B]Справка и статус[/B]');
20
- expect(text).toContain('[send=/help]/help[/send]');
21
- expect(text).toContain('[send=/export-session]/export-session[/send]');
22
- expect(text).toContain('[COLOR=#6C788A]Параметры: off | tokens | full | cost[/COLOR]');
23
- });
24
-
25
- it('falls back to English text', () => {
26
- const text = buildCommandsHelpText('en', { concise: true });
27
-
28
- expect(text).toContain('[B]Key commands[/B]');
29
- expect(text).toContain('[COLOR=#6C788A]Full list:[/COLOR] [send=/commands]');
30
- });
31
- });
32
-
33
- describe('formatModelsCommandReply', () => {
34
- it('formats provider summary into interactive Bitrix text', () => {
35
- const text = formatModelsCommandReply(
36
- [
37
- 'Providers:',
38
- '• openai (36)',
39
- '• anthropic (23)',
40
- '',
41
- 'Use: /models <provider>',
42
- 'Switch: /model <provider/model>',
43
- ].join('\n'),
44
- 'ru',
45
- );
46
-
47
- expect(text).toContain('[B]Провайдеры[/B]');
48
- expect(text).toContain('[send=/models openai]openai[/send]');
49
- expect(text).toContain('[send=/models anthropic]anthropic[/send]');
50
- expect(text).toContain('[put=/models ]/models <provider>[/put]');
51
- expect(text).toContain('[put=/model ]/model <provider/model>[/put]');
52
- });
53
-
54
- it('returns null for unrelated text', () => {
55
- expect(formatModelsCommandReply('Status ok', 'ru')).toBeNull();
56
- });
57
- });
@@ -1,210 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { getConfig, isConfigured, listAccountIds, resolveAccount } from '../src/config.js';
3
- import { Bitrix24ConfigSchema } from '../src/config-schema.js';
4
-
5
- const mockCfg = {
6
- channels: {
7
- bitrix24: {
8
- webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
9
- botName: 'TestBot',
10
- dmPolicy: 'pairing',
11
- allowFrom: ['bitrix24:1'],
12
- accounts: {
13
- portal2: {
14
- webhookUrl: 'https://portal2.bitrix24.com/rest/1/xyz789/',
15
- botName: 'Portal2Bot',
16
- allowFrom: ['bitrix24:2'],
17
- },
18
- },
19
- },
20
- },
21
- };
22
-
23
- describe('getConfig', () => {
24
- it('returns default config', () => {
25
- const cfg = getConfig(mockCfg);
26
- expect(cfg.webhookUrl).toBe('https://test.bitrix24.com/rest/1/abc123/');
27
- expect(cfg.botName).toBe('TestBot');
28
- expect(cfg.dmPolicy).toBe('pairing');
29
- expect(cfg.allowFrom).toEqual(['bitrix24:1']);
30
- });
31
-
32
- it('returns account-specific config', () => {
33
- const cfg = getConfig(mockCfg, 'portal2');
34
- expect(cfg.webhookUrl).toBe('https://portal2.bitrix24.com/rest/1/xyz789/');
35
- expect(cfg.botName).toBe('Portal2Bot');
36
- expect(cfg.allowFrom).toEqual(['bitrix24:2']);
37
- });
38
-
39
- it('returns empty config when no bitrix24 section', () => {
40
- const cfg = getConfig({});
41
- expect(cfg).toEqual({});
42
- });
43
-
44
- it('returns default config when account not found', () => {
45
- const cfg = getConfig(mockCfg, 'nonexistent');
46
- expect(cfg.webhookUrl).toBe('https://test.bitrix24.com/rest/1/abc123/');
47
- });
48
-
49
- it('keeps group-related config fields intact', () => {
50
- const cfg = getConfig({
51
- channels: {
52
- bitrix24: {
53
- webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
54
- groupPolicy: 'webhookUser',
55
- groupAllowFrom: ['chat208', '615'],
56
- requireMention: true,
57
- historyLimit: 100,
58
- groups: {
59
- '*': { requireMention: true },
60
- chat208: {
61
- groupPolicy: 'open',
62
- requireMention: false,
63
- watch: [{ userId: '77', topics: ['секрет'] }],
64
- },
65
- },
66
- },
67
- },
68
- });
69
-
70
- expect(cfg.groupPolicy).toBe('webhookUser');
71
- expect(cfg.groupAllowFrom).toEqual(['chat208', '615']);
72
- expect(cfg.requireMention).toBe(true);
73
- expect(cfg.historyLimit).toBe(100);
74
- expect(cfg.groups).toEqual({
75
- '*': { requireMention: true },
76
- chat208: {
77
- groupPolicy: 'open',
78
- requireMention: false,
79
- watch: [{ userId: '77', topics: ['секрет'] }],
80
- },
81
- });
82
- });
83
- });
84
-
85
- describe('isConfigured', () => {
86
- it('returns true when webhookUrl is set', () => {
87
- expect(isConfigured(mockCfg)).toBe(true);
88
- });
89
-
90
- it('returns false when no config', () => {
91
- expect(isConfigured({})).toBe(false);
92
- });
93
- });
94
-
95
- describe('listAccountIds', () => {
96
- it('lists default and named accounts', () => {
97
- const ids = listAccountIds(mockCfg);
98
- expect(ids).toContain('default');
99
- expect(ids).toContain('portal2');
100
- expect(ids).toHaveLength(2);
101
- });
102
-
103
- it('returns empty when no config', () => {
104
- expect(listAccountIds({})).toEqual([]);
105
- });
106
- });
107
-
108
- describe('resolveAccount', () => {
109
- it('resolves default account', () => {
110
- const acct = resolveAccount(mockCfg);
111
- expect(acct.accountId).toBe('default');
112
- expect(acct.configured).toBe(true);
113
- expect(acct.enabled).toBe(true);
114
- });
115
-
116
- it('resolves named account', () => {
117
- const acct = resolveAccount(mockCfg, 'portal2');
118
- expect(acct.accountId).toBe('portal2');
119
- expect(acct.configured).toBe(true);
120
- expect(acct.config.webhookUrl).toBe('https://portal2.bitrix24.com/rest/1/xyz789/');
121
- });
122
-
123
- it('handles missing config gracefully', () => {
124
- const acct = resolveAccount({}, 'missing');
125
- expect(acct.configured).toBe(false);
126
- });
127
-
128
- it('accepts new group policy fields in account config', () => {
129
- const acct = resolveAccount({
130
- channels: {
131
- bitrix24: {
132
- webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
133
- dmPolicy: 'allowlist',
134
- groupPolicy: 'pairing',
135
- groupAllowFrom: ['chat208'],
136
- requireMention: false,
137
- historyLimit: 50,
138
- groups: {
139
- '208': { groupPolicy: 'open' },
140
- },
141
- },
142
- },
143
- });
144
-
145
- expect(acct.configured).toBe(true);
146
- expect(acct.config.groupPolicy).toBe('pairing');
147
- expect(acct.config.groupAllowFrom).toEqual(['chat208']);
148
- expect(acct.config.requireMention).toBe(false);
149
- expect(acct.config.historyLimit).toBe(50);
150
- expect(acct.config.groups).toEqual({
151
- '208': { groupPolicy: 'open', requireMention: true },
152
- });
153
- });
154
-
155
- it('accepts agent mode watch rules in account config', () => {
156
- const acct = resolveAccount({
157
- channels: {
158
- bitrix24: {
159
- webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
160
- agentMode: true,
161
- agentWatch: {
162
- '*': [
163
- { userId: '*', topics: ['авария'], mode: 'notifyOwnerDm' },
164
- ],
165
- '77': [
166
- { userId: '*', topics: ['срочно'] },
167
- ],
168
- },
169
- },
170
- },
171
- });
172
-
173
- expect(acct.configured).toBe(true);
174
- expect(acct.config.agentMode).toBe(true);
175
- expect(acct.config.agentWatch).toEqual({
176
- '*': [
177
- { userId: '*', topics: ['авария'], mode: 'notifyOwnerDm' },
178
- ],
179
- '77': [
180
- { userId: '*', topics: ['срочно'] },
181
- ],
182
- });
183
- });
184
-
185
- it('rejects invalid group policy values', () => {
186
- expect(() => resolveAccount({
187
- channels: {
188
- bitrix24: {
189
- webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
190
- groupPolicy: 'invalid',
191
- },
192
- },
193
- } as unknown as Record<string, unknown>)).toThrow('Invalid config');
194
- });
195
- });
196
-
197
- describe('Bitrix24ConfigSchema', () => {
198
- it('applies defaults for new access and history options', () => {
199
- const parsed = Bitrix24ConfigSchema.parse({
200
- webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
201
- });
202
-
203
- expect(parsed.dmPolicy).toBe('webhookUser');
204
- expect(parsed.groupPolicy).toBe('webhookUser');
205
- expect(parsed.agentMode).toBe(false);
206
- expect(parsed.requireMention).toBe(true);
207
- expect(parsed.historyLimit).toBe(100);
208
- expect(parsed.showTyping).toBe(true);
209
- });
210
- });
@@ -1,50 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import { Dedup } from '../src/dedup.js';
3
-
4
- describe('Dedup', () => {
5
- let dedup: Dedup;
6
-
7
- afterEach(() => {
8
- dedup?.destroy();
9
- });
10
-
11
- it('returns false for first occurrence', () => {
12
- dedup = new Dedup();
13
- expect(dedup.isDuplicate(123)).toBe(false);
14
- });
15
-
16
- it('returns true for second occurrence', () => {
17
- dedup = new Dedup();
18
- dedup.isDuplicate(123);
19
- expect(dedup.isDuplicate(123)).toBe(true);
20
- });
21
-
22
- it('handles string IDs', () => {
23
- dedup = new Dedup();
24
- expect(dedup.isDuplicate('abc')).toBe(false);
25
- expect(dedup.isDuplicate('abc')).toBe(true);
26
- });
27
-
28
- it('tracks different IDs independently', () => {
29
- dedup = new Dedup();
30
- expect(dedup.isDuplicate(1)).toBe(false);
31
- expect(dedup.isDuplicate(2)).toBe(false);
32
- expect(dedup.isDuplicate(1)).toBe(true);
33
- expect(dedup.isDuplicate(2)).toBe(true);
34
- });
35
-
36
- it('reports size correctly', () => {
37
- dedup = new Dedup();
38
- expect(dedup.size).toBe(0);
39
- dedup.isDuplicate(1);
40
- dedup.isDuplicate(2);
41
- expect(dedup.size).toBe(2);
42
- });
43
-
44
- it('cleans up on destroy', () => {
45
- dedup = new Dedup();
46
- dedup.isDuplicate(1);
47
- dedup.destroy();
48
- expect(dedup.size).toBe(0);
49
- });
50
- });
@@ -1,48 +0,0 @@
1
- {
2
- "event": "ONIMBOTJOINCHAT",
3
- "event_handler_id": 27,
4
- "data": {
5
- "BOT": {
6
- "2081": {
7
- "access_token": "bot_token_test_abc123",
8
- "expires": 1771448424,
9
- "scope": "imbot,im,disk",
10
- "domain": "test.bitrix24.com",
11
- "client_endpoint": "https://test.bitrix24.com/rest/",
12
- "member_id": "test_member_id_123",
13
- "user_id": 2081,
14
- "client_id": "local.test123.app",
15
- "application_token": "app_token_test_xyz",
16
- "BOT_ID": 2081,
17
- "BOT_CODE": "openclaw"
18
- }
19
- },
20
- "PARAMS": {
21
- "CHAT_TYPE": "P",
22
- "MESSAGE_TYPE": "P",
23
- "BOT_ID": 2081,
24
- "USER_ID": 1,
25
- "DIALOG_ID": "1",
26
- "LANGUAGE": "ru"
27
- },
28
- "USER": {
29
- "ID": 1,
30
- "NAME": "Test User",
31
- "FIRST_NAME": "Test",
32
- "LAST_NAME": "User",
33
- "WORK_POSITION": "Developer",
34
- "GENDER": "M"
35
- }
36
- },
37
- "ts": 1771444000,
38
- "auth": {
39
- "access_token": "user_token_test_def456",
40
- "expires": 1771448424,
41
- "scope": "imbot",
42
- "domain": "test.bitrix24.com",
43
- "client_endpoint": "https://test.bitrix24.com/rest/",
44
- "member_id": "test_member_id_123",
45
- "user_id": 1,
46
- "application_token": "app_token_test_xyz"
47
- }
48
- }
@@ -1,86 +0,0 @@
1
- {
2
- "event": "ONIMBOTMESSAGEADD",
3
- "event_handler_id": 27,
4
- "data": {
5
- "BOT": {
6
- "2081": {
7
- "access_token": "bot_token_test_abc123",
8
- "expires": 1771448424,
9
- "scope": "imbot,im,disk",
10
- "domain": "test.bitrix24.com",
11
- "client_endpoint": "https://test.bitrix24.com/rest/",
12
- "member_id": "test_member_id_123",
13
- "user_id": 2081,
14
- "client_id": "local.test123.app",
15
- "application_token": "app_token_test_xyz",
16
- "BOT_ID": 2081,
17
- "BOT_CODE": "openclaw"
18
- }
19
- },
20
- "PARAMS": {
21
- "MESSAGE": "",
22
- "TEMPLATE_ID": "e9c8e294-test-5678",
23
- "MESSAGE_TYPE": "P",
24
- "FROM_USER_ID": 1,
25
- "DIALOG_ID": "1",
26
- "TO_CHAT_ID": 40985,
27
- "TO_USER_ID": 2081,
28
- "MESSAGE_ID": 492035,
29
- "CHAT_TYPE": "P",
30
- "LANGUAGE": "ru",
31
- "PLATFORM_CONTEXT": "web",
32
- "PARAMS": {
33
- "FILE_ID": [94611]
34
- },
35
- "FILES": {
36
- "94611": {
37
- "id": 94611,
38
- "chatId": 40985,
39
- "date": "2026-02-21T10:27:08+03:00",
40
- "type": "file",
41
- "name": "document.txt",
42
- "extension": "txt",
43
- "size": 101,
44
- "image": 0,
45
- "status": "done",
46
- "progress": 100,
47
- "authorId": 1,
48
- "authorName": "Test User",
49
- "urlPreview": "",
50
- "urlShow": "https://test.bitrix24.com/disk/showFile/94611/",
51
- "urlDownload": "https://test.bitrix24.com/disk/downloadFile/94611/",
52
- "viewerAttrs": {
53
- "viewer": "",
54
- "viewerType": "code",
55
- "src": "https://test.bitrix24.com/disk/viewer/94611/",
56
- "objectId": 94611,
57
- "title": "document.txt"
58
- }
59
- }
60
- }
61
- },
62
- "USER": {
63
- "ID": 1,
64
- "NAME": "Test User",
65
- "FIRST_NAME": "Test",
66
- "LAST_NAME": "User",
67
- "WORK_POSITION": "Developer",
68
- "GENDER": "M",
69
- "IS_BOT": "N",
70
- "IS_CONNECTOR": "N",
71
- "IS_NETWORK": "N",
72
- "IS_EXTRANET": "N"
73
- }
74
- },
75
- "ts": 1771444900,
76
- "auth": {
77
- "access_token": "user_token_test_def456",
78
- "expires": 1771448424,
79
- "scope": "imbot",
80
- "domain": "test.bitrix24.com",
81
- "client_endpoint": "https://test.bitrix24.com/rest/",
82
- "member_id": "test_member_id_123",
83
- "user_id": 1,
84
- "application_token": "app_token_test_xyz"
85
- }
86
- }
@@ -1,59 +0,0 @@
1
- {
2
- "event": "ONIMBOTMESSAGEADD",
3
- "event_handler_id": 27,
4
- "data": {
5
- "BOT": {
6
- "2081": {
7
- "access_token": "bot_token_test_abc123",
8
- "expires": 1771448424,
9
- "scope": "imbot,im,disk",
10
- "domain": "test.bitrix24.com",
11
- "client_endpoint": "https://test.bitrix24.com/rest/",
12
- "member_id": "test_member_id_123",
13
- "user_id": 2081,
14
- "client_id": "local.test123.app",
15
- "application_token": "app_token_test_xyz",
16
- "BOT_ID": 2081,
17
- "BOT_CODE": "openclaw"
18
- }
19
- },
20
- "PARAMS": {
21
- "MESSAGE": "Hello, how are you?",
22
- "TEMPLATE_ID": "ea602c80-test-1234",
23
- "MESSAGE_TYPE": "P",
24
- "FROM_USER_ID": 1,
25
- "DIALOG_ID": "1",
26
- "TO_CHAT_ID": 40985,
27
- "AUTHOR_ID": 1,
28
- "TO_USER_ID": 2081,
29
- "MESSAGE_ID": 490659,
30
- "CHAT_TYPE": "P",
31
- "LANGUAGE": "ru",
32
- "PLATFORM_CONTEXT": "web",
33
- "CHAT_USER_COUNT": 2
34
- },
35
- "USER": {
36
- "ID": 1,
37
- "NAME": "Test User",
38
- "FIRST_NAME": "Test",
39
- "LAST_NAME": "User",
40
- "WORK_POSITION": "Developer",
41
- "GENDER": "M",
42
- "IS_BOT": "N",
43
- "IS_CONNECTOR": "N",
44
- "IS_NETWORK": "N",
45
- "IS_EXTRANET": "N"
46
- }
47
- },
48
- "ts": 1771444824,
49
- "auth": {
50
- "access_token": "user_token_test_def456",
51
- "expires": 1771448424,
52
- "scope": "imbot",
53
- "domain": "test.bitrix24.com",
54
- "client_endpoint": "https://test.bitrix24.com/rest/",
55
- "member_id": "test_member_id_123",
56
- "user_id": 1,
57
- "application_token": "app_token_test_xyz"
58
- }
59
- }
@@ -1,45 +0,0 @@
1
- {
2
- "event": "ONIMCOMMANDADD",
3
- "event_handler_id": 27,
4
- "data": {
5
- "COMMAND": {
6
- "53": {
7
- "access_token": "bot_token_test_abc123",
8
- "BOT_ID": 2081,
9
- "BOT_CODE": "openclaw",
10
- "COMMAND": "help",
11
- "COMMAND_ID": 53,
12
- "COMMAND_PARAMS": "",
13
- "COMMAND_CONTEXT": "TEXTAREA",
14
- "MESSAGE_ID": 490667
15
- }
16
- },
17
- "PARAMS": {
18
- "MESSAGE": "/help",
19
- "FROM_USER_ID": 1,
20
- "DIALOG_ID": "1",
21
- "TO_CHAT_ID": 40985,
22
- "MESSAGE_ID": 490667,
23
- "CHAT_TYPE": "P"
24
- },
25
- "USER": {
26
- "ID": 1,
27
- "NAME": "Test User",
28
- "FIRST_NAME": "Test",
29
- "LAST_NAME": "User",
30
- "WORK_POSITION": "Developer",
31
- "GENDER": "M"
32
- }
33
- },
34
- "ts": 1771445000,
35
- "auth": {
36
- "access_token": "user_token_test_def456",
37
- "expires": 1771448424,
38
- "scope": "imbot",
39
- "domain": "test.bitrix24.com",
40
- "client_endpoint": "https://test.bitrix24.com/rest/",
41
- "member_id": "test_member_id_123",
42
- "user_id": 1,
43
- "application_token": "app_token_test_xyz"
44
- }
45
- }