@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
@@ -1,115 +0,0 @@
1
- import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { tmpdir } from 'node:os';
4
- import { afterEach, describe, expect, it, vi } from 'vitest';
5
- import { PollingService } from '../src/polling-service.js';
6
- import type { Bitrix24Api } from '../src/api.js';
7
-
8
- const silentLogger = {
9
- info: vi.fn(),
10
- warn: vi.fn(),
11
- error: vi.fn(),
12
- debug: vi.fn(),
13
- };
14
-
15
- describe('PollingService', () => {
16
- let tempHome = '';
17
- const originalHome = process.env.HOME;
18
-
19
- afterEach(() => {
20
- if (tempHome) {
21
- rmSync(tempHome, { recursive: true, force: true });
22
- tempHome = '';
23
- }
24
-
25
- if (originalHome === undefined) {
26
- delete process.env.HOME;
27
- } else {
28
- process.env.HOME = originalHome;
29
- }
30
-
31
- vi.clearAllMocks();
32
- });
33
-
34
- it('persists offset only up to the last successfully processed event', async () => {
35
- tempHome = mkdtempSync(join(tmpdir(), 'b24-polling-'));
36
- process.env.HOME = tempHome;
37
-
38
- const abortController = new AbortController();
39
- const fetchEvents = vi.fn().mockResolvedValue({
40
- events: [
41
- { eventId: 10, type: 'ONE', date: '', data: {} },
42
- { eventId: 11, type: 'TWO', date: '', data: {} },
43
- ],
44
- lastEventId: 11,
45
- hasMore: false,
46
- });
47
-
48
- const api = { fetchEvents } as unknown as Bitrix24Api;
49
- const onEvent = vi.fn(async (event: { eventId: number }) => {
50
- if (event.eventId === 11) {
51
- abortController.abort();
52
- throw new Error('boom');
53
- }
54
- });
55
-
56
- const service = new PollingService({
57
- api,
58
- webhookUrl: 'https://test.bitrix24.com/rest/1/token/',
59
- bot: { botId: 7, botToken: 'bot_token' },
60
- accountId: 'default',
61
- pollingIntervalMs: 10,
62
- pollingFastIntervalMs: 10,
63
- onEvent: onEvent as never,
64
- abortSignal: abortController.signal,
65
- logger: silentLogger,
66
- });
67
-
68
- await service.start();
69
-
70
- const statePath = join(tempHome, '.openclaw', 'state', 'bitrix24', 'poll-offset-default.json');
71
- const state = JSON.parse(readFileSync(statePath, 'utf-8')) as { offset: number };
72
-
73
- expect(fetchEvents).toHaveBeenCalledOnce();
74
- expect(onEvent).toHaveBeenCalledTimes(2);
75
- expect(state.offset).toBe(11);
76
- });
77
-
78
- it('passes withUserEvents through to fetch polling', async () => {
79
- tempHome = mkdtempSync(join(tmpdir(), 'b24-polling-agent-'));
80
- process.env.HOME = tempHome;
81
-
82
- const abortController = new AbortController();
83
- const fetchEvents = vi.fn().mockImplementation(async () => {
84
- abortController.abort();
85
- return {
86
- events: [],
87
- lastEventId: 0,
88
- hasMore: false,
89
- };
90
- });
91
-
92
- const api = { fetchEvents } as unknown as Bitrix24Api;
93
- const service = new PollingService({
94
- api,
95
- webhookUrl: 'https://test.bitrix24.com/rest/1/token/',
96
- bot: { botId: 7, botToken: 'bot_token' },
97
- accountId: 'default',
98
- pollingIntervalMs: 1,
99
- pollingFastIntervalMs: 1,
100
- withUserEvents: true,
101
- onEvent: vi.fn(),
102
- abortSignal: abortController.signal,
103
- logger: silentLogger,
104
- });
105
-
106
- await service.start();
107
-
108
- expect(fetchEvents).toHaveBeenCalledOnce();
109
- expect(fetchEvents).toHaveBeenCalledWith(
110
- 'https://test.bitrix24.com/rest/1/token/',
111
- { botId: 7, botToken: 'bot_token' },
112
- { offset: 0, limit: 100, withUserEvents: true },
113
- );
114
- });
115
- });
@@ -1,52 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import { RateLimiter } from '../src/rate-limiter.js';
3
-
4
- describe('RateLimiter', () => {
5
- let limiter: RateLimiter;
6
-
7
- afterEach(() => {
8
- limiter?.destroy();
9
- });
10
-
11
- it('allows immediate requests within budget', async () => {
12
- limiter = new RateLimiter({ maxPerSecond: 2 });
13
- // First two should be immediate
14
- const start = Date.now();
15
- await limiter.acquire();
16
- await limiter.acquire();
17
- const elapsed = Date.now() - start;
18
- expect(elapsed).toBeLessThan(50);
19
- });
20
-
21
- it('queues requests when budget exhausted', async () => {
22
- limiter = new RateLimiter({ maxPerSecond: 2 });
23
- // Exhaust budget
24
- await limiter.acquire();
25
- await limiter.acquire();
26
- // Third should be delayed
27
- const start = Date.now();
28
- await limiter.acquire();
29
- const elapsed = Date.now() - start;
30
- expect(elapsed).toBeGreaterThanOrEqual(400); // ~500ms for 1 token at 2/sec
31
- });
32
-
33
- it('tracks pending count', async () => {
34
- limiter = new RateLimiter({ maxPerSecond: 1 });
35
- await limiter.acquire();
36
- expect(limiter.pending).toBe(0);
37
-
38
- // Start a pending acquire
39
- const p = limiter.acquire();
40
- expect(limiter.pending).toBe(1);
41
- await p;
42
- expect(limiter.pending).toBe(0);
43
- });
44
-
45
- it('rejects pending on destroy', async () => {
46
- limiter = new RateLimiter({ maxPerSecond: 1 });
47
- await limiter.acquire();
48
- const p = limiter.acquire();
49
- limiter.destroy();
50
- await expect(p).rejects.toThrow('RateLimiter destroyed');
51
- });
52
- });
@@ -1,162 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { SendService } from '../src/send-service.js';
3
- import type { Bitrix24Api } from '../src/api.js';
4
-
5
- const silentLogger = {
6
- info: vi.fn(),
7
- warn: vi.fn(),
8
- error: vi.fn(),
9
- debug: vi.fn(),
10
- };
11
-
12
- function mockApi(): Bitrix24Api {
13
- return {
14
- answerCommand: vi.fn().mockResolvedValue(true),
15
- notifyInputAction: vi.fn().mockResolvedValue(true),
16
- sendTyping: vi.fn().mockResolvedValue(true),
17
- readMessage: vi.fn().mockResolvedValue({
18
- chatId: 1,
19
- lastId: 123,
20
- counter: 0,
21
- viewedMessages: [123],
22
- }),
23
- sendMessage: vi.fn(),
24
- updateMessage: vi.fn(),
25
- } as unknown as Bitrix24Api;
26
- }
27
-
28
- describe('SendService', () => {
29
- let api: Bitrix24Api;
30
- let service: SendService;
31
-
32
- const ctx = {
33
- webhookUrl: 'https://test.bitrix24.com/rest/1/token/',
34
- bot: { botId: 7, botToken: 'bot_token' },
35
- dialogId: '42',
36
- };
37
- const commandCtx = {
38
- ...ctx,
39
- commandId: 55,
40
- messageId: 123,
41
- };
42
-
43
- beforeEach(() => {
44
- api = mockApi();
45
- service = new SendService(api, silentLogger);
46
- vi.clearAllMocks();
47
- });
48
-
49
- it('sends explicit input action status', async () => {
50
- await service.sendStatus(ctx, 'IMBOT_AGENT_ACTION_COMPOSING', 45);
51
-
52
- expect(api.notifyInputAction).toHaveBeenCalledWith(
53
- ctx.webhookUrl,
54
- ctx.bot,
55
- ctx.dialogId,
56
- {
57
- statusMessageCode: 'IMBOT_AGENT_ACTION_COMPOSING',
58
- duration: 45,
59
- },
60
- );
61
- });
62
-
63
- it('uses notifyInputAction for sendTyping', async () => {
64
- await service.sendTyping(ctx);
65
-
66
- expect(api.notifyInputAction).toHaveBeenCalledWith(
67
- ctx.webhookUrl,
68
- ctx.bot,
69
- ctx.dialogId,
70
- );
71
- });
72
-
73
- it('marks a specific message as read', async () => {
74
- await service.markRead(ctx, 123);
75
-
76
- expect(api.readMessage).toHaveBeenCalledWith(
77
- ctx.webhookUrl,
78
- ctx.bot,
79
- ctx.dialogId,
80
- 123,
81
- );
82
- });
83
-
84
- it('swallows read failures', async () => {
85
- (api.readMessage as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error('read failed'));
86
-
87
- await expect(service.markRead(ctx, 123)).resolves.toBeUndefined();
88
- expect(silentLogger.debug).toHaveBeenCalled();
89
- });
90
-
91
- it('answers native commands via imbot.v2.Command.answer', async () => {
92
- await service.answerCommandText(commandCtx, 'Status ok');
93
-
94
- expect(api.answerCommand).toHaveBeenCalledWith(
95
- ctx.webhookUrl,
96
- ctx.bot,
97
- 55,
98
- 123,
99
- ctx.dialogId,
100
- 'Status ok',
101
- undefined,
102
- );
103
- expect(api.sendMessage).not.toHaveBeenCalled();
104
- });
105
-
106
- it('sends command follow-up chunks as regular chat messages', async () => {
107
- (api.sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce(777);
108
- const longText = `${'A'.repeat(20000)}${'B'.repeat(50)}`;
109
-
110
- const result = await service.answerCommandText(commandCtx, longText);
111
-
112
- expect(result.ok).toBe(true);
113
- expect(api.answerCommand).toHaveBeenCalledOnce();
114
- expect(api.sendMessage).toHaveBeenCalledOnce();
115
- expect(api.sendMessage).toHaveBeenCalledWith(
116
- ctx.webhookUrl,
117
- ctx.bot,
118
- ctx.dialogId,
119
- 'B'.repeat(50),
120
- undefined,
121
- );
122
- });
123
-
124
- it('throws when sendText cannot deliver a chunk', async () => {
125
- (api.sendMessage as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error('send failed'));
126
-
127
- await expect(service.sendText(ctx, 'Hello')).rejects.toThrow('send failed');
128
- expect(silentLogger.error).toHaveBeenCalled();
129
- });
130
-
131
- it('throws when command answer fails on the first chunk', async () => {
132
- (api.answerCommand as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error('answer failed'));
133
-
134
- await expect(service.answerCommandText(commandCtx, 'Status ok')).rejects.toThrow('answer failed');
135
- expect(silentLogger.error).toHaveBeenCalled();
136
- });
137
-
138
- it('throws when command follow-up chunk cannot be sent', async () => {
139
- (api.sendMessage as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error('follow-up failed'));
140
- const longText = `${'A'.repeat(20000)}${'B'.repeat(50)}`;
141
-
142
- await expect(service.answerCommandText(commandCtx, longText)).rejects.toThrow('follow-up failed');
143
- expect(silentLogger.error).toHaveBeenCalled();
144
- });
145
-
146
- it('passes forwardMessages to the first outbound chunk', async () => {
147
- (api.sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce(777);
148
-
149
- await service.sendText(ctx, 'Forward this', {
150
- convertMarkdown: false,
151
- forwardMessages: [101, 102],
152
- });
153
-
154
- expect(api.sendMessage).toHaveBeenCalledWith(
155
- ctx.webhookUrl,
156
- ctx.bot,
157
- ctx.dialogId,
158
- 'Forward this',
159
- { forwardMessages: [101, 102] },
160
- );
161
- });
162
- });
package/tsconfig.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "lib": ["ES2022"],
7
- "types": ["node"],
8
- "outDir": "./dist",
9
- "rootDir": ".",
10
- "declaration": true,
11
- "declarationMap": true,
12
- "sourceMap": true,
13
- "strict": true,
14
- "esModuleInterop": true,
15
- "skipLibCheck": true,
16
- "forceConsistentCasingInFileNames": true,
17
- "resolveJsonModule": true,
18
- "isolatedModules": true
19
- },
20
- "include": ["index.ts", "src/**/*.ts"],
21
- "exclude": ["node_modules", "dist", "tests"]
22
- }
package/vitest.config.ts DELETED
@@ -1,9 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- environment: 'node',
7
- include: ['tests/**/*.test.ts'],
8
- },
9
- });