@ihazz/bitrix24 1.1.1 → 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.
- package/package.json +9 -1
- package/src/media-service.ts +78 -25
- package/src/state-paths.ts +24 -0
- package/tests/access-control.test.ts +0 -398
- package/tests/api.test.ts +0 -226
- package/tests/channel-flow.test.ts +0 -1692
- package/tests/channel.test.ts +0 -842
- package/tests/commands.test.ts +0 -57
- package/tests/config.test.ts +0 -210
- package/tests/dedup.test.ts +0 -50
- package/tests/fixtures/onimbotjoinchat.json +0 -48
- package/tests/fixtures/onimbotmessageadd-file.json +0 -86
- package/tests/fixtures/onimbotmessageadd-text.json +0 -59
- package/tests/fixtures/onimcommandadd.json +0 -45
- package/tests/group-access.test.ts +0 -340
- package/tests/history-cache.test.ts +0 -117
- package/tests/i18n.test.ts +0 -90
- package/tests/inbound-handler.test.ts +0 -1033
- package/tests/index.test.ts +0 -94
- package/tests/media-service.test.ts +0 -319
- package/tests/message-utils.test.ts +0 -184
- package/tests/polling-service.test.ts +0 -115
- package/tests/rate-limiter.test.ts +0 -52
- package/tests/send-service.test.ts +0 -162
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -9
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
buildGroupMatchKeys,
|
|
4
|
-
checkGroupAccessPassive,
|
|
5
|
-
checkGroupAccessWithPairing,
|
|
6
|
-
normalizeGroupAllowList,
|
|
7
|
-
normalizeGroupEntry,
|
|
8
|
-
resolveGroupAccess,
|
|
9
|
-
} from '../src/group-access.js';
|
|
10
|
-
import type { PluginRuntime, ChannelPairingAdapter } from '../src/runtime.js';
|
|
11
|
-
|
|
12
|
-
function makeMockRuntime(storeAllowFrom: string[] = []): PluginRuntime {
|
|
13
|
-
return {
|
|
14
|
-
config: { loadConfig: () => ({}) },
|
|
15
|
-
channel: {
|
|
16
|
-
routing: { resolveAgentRoute: vi.fn() },
|
|
17
|
-
reply: {
|
|
18
|
-
finalizeInboundContext: vi.fn(),
|
|
19
|
-
dispatchReplyWithBufferedBlockDispatcher: vi.fn(),
|
|
20
|
-
},
|
|
21
|
-
session: { recordInboundSession: vi.fn() },
|
|
22
|
-
pairing: {
|
|
23
|
-
readAllowFromStore: vi.fn().mockResolvedValue(storeAllowFrom),
|
|
24
|
-
upsertPairingRequest: vi.fn().mockResolvedValue({ code: 'ABCD1234', created: true }),
|
|
25
|
-
buildPairingReply: vi.fn(),
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
logging: { getChildLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }) },
|
|
29
|
-
} as unknown as PluginRuntime;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const mockAdapter: ChannelPairingAdapter = {
|
|
33
|
-
idLabel: 'bitrix24UserId',
|
|
34
|
-
normalizeAllowEntry: (entry) => entry,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
describe('group-access helpers', () => {
|
|
38
|
-
it('normalizes numeric and dialog group ids', () => {
|
|
39
|
-
expect(normalizeGroupEntry(' 208 ')).toBe('208');
|
|
40
|
-
expect(normalizeGroupEntry(' Chat208 ')).toBe('chat208');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('normalizes and deduplicates group allowlist entries', () => {
|
|
44
|
-
expect(normalizeGroupAllowList(['208', 'chat208', '208'])).toEqual(['208', 'chat208']);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('builds group match keys from dialogId and chatId', () => {
|
|
48
|
-
expect(buildGroupMatchKeys({ dialogId: 'chat208', chatId: '208' })).toEqual(['chat208', '208']);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
describe('resolveGroupAccess', () => {
|
|
53
|
-
it('defaults to webhookUser and mention-required groups', () => {
|
|
54
|
-
expect(resolveGroupAccess({
|
|
55
|
-
config: {},
|
|
56
|
-
dialogId: 'chat208',
|
|
57
|
-
chatId: '208',
|
|
58
|
-
})).toMatchObject({
|
|
59
|
-
groupPolicy: 'webhookUser',
|
|
60
|
-
requireMention: true,
|
|
61
|
-
groupAllowed: true,
|
|
62
|
-
senderAllowFrom: [],
|
|
63
|
-
watch: [],
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('matches explicit group config before wildcard', () => {
|
|
68
|
-
const result = resolveGroupAccess({
|
|
69
|
-
config: {
|
|
70
|
-
groupPolicy: 'webhookUser',
|
|
71
|
-
groups: {
|
|
72
|
-
'*': { requireMention: true },
|
|
73
|
-
chat208: { groupPolicy: 'open', requireMention: false, allowFrom: ['42'] },
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
dialogId: 'chat208',
|
|
77
|
-
chatId: '208',
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
expect(result.groupPolicy).toBe('open');
|
|
81
|
-
expect(result.requireMention).toBe(false);
|
|
82
|
-
expect(result.senderAllowFrom).toEqual(['42']);
|
|
83
|
-
expect(result.matchedGroupKey).toBe('chat208');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('resolves watch rules from matched group config', () => {
|
|
87
|
-
const result = resolveGroupAccess({
|
|
88
|
-
config: {
|
|
89
|
-
groups: {
|
|
90
|
-
chat208: {
|
|
91
|
-
watch: [
|
|
92
|
-
{ userId: 'bitrix24:77', topics: ['секрет', 'важно'], mode: 'notifyOwnerDm' },
|
|
93
|
-
],
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
dialogId: 'chat208',
|
|
98
|
-
chatId: '208',
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
expect(result.watch).toEqual([
|
|
102
|
-
{ userId: '77', topics: ['секрет', 'важно'], mode: 'notifyOwnerDm' },
|
|
103
|
-
]);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('preserves wildcard watch sender ids for any-user matching', () => {
|
|
107
|
-
const result = resolveGroupAccess({
|
|
108
|
-
config: {
|
|
109
|
-
groups: {
|
|
110
|
-
chat208: {
|
|
111
|
-
watch: [
|
|
112
|
-
{ userId: '*', topics: ['авария'] },
|
|
113
|
-
],
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
dialogId: 'chat208',
|
|
118
|
-
chatId: '208',
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
expect(result.watch).toEqual([
|
|
122
|
-
{ userId: '*', topics: ['авария'], mode: 'reply' },
|
|
123
|
-
]);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('merges explicit group watch rules before wildcard watch rules', () => {
|
|
127
|
-
const result = resolveGroupAccess({
|
|
128
|
-
config: {
|
|
129
|
-
groups: {
|
|
130
|
-
'*': {
|
|
131
|
-
watch: [
|
|
132
|
-
{ userId: '*', topics: ['авария'], mode: 'notifyOwnerDm' },
|
|
133
|
-
],
|
|
134
|
-
},
|
|
135
|
-
chat208: {
|
|
136
|
-
watch: [
|
|
137
|
-
{ userId: '77', topics: ['секрет'] },
|
|
138
|
-
],
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
dialogId: 'chat208',
|
|
143
|
-
chatId: '208',
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
expect(result.watch).toEqual([
|
|
147
|
-
{ userId: '77', topics: ['секрет'], mode: 'reply' },
|
|
148
|
-
{ userId: '*', topics: ['авария'], mode: 'notifyOwnerDm' },
|
|
149
|
-
]);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('matches numeric chat id after dialog id lookup misses', () => {
|
|
153
|
-
const result = resolveGroupAccess({
|
|
154
|
-
config: {
|
|
155
|
-
groups: {
|
|
156
|
-
'208': { groupPolicy: 'open', requireMention: false },
|
|
157
|
-
'*': { groupPolicy: 'webhookUser', requireMention: true },
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
dialogId: 'chat208',
|
|
161
|
-
chatId: '208',
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
expect(result.groupPolicy).toBe('open');
|
|
165
|
-
expect(result.requireMention).toBe(false);
|
|
166
|
-
expect(result.matchedGroupKey).toBe('208');
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('falls back to wildcard group config when no exact match exists', () => {
|
|
170
|
-
const result = resolveGroupAccess({
|
|
171
|
-
config: {
|
|
172
|
-
groups: {
|
|
173
|
-
'*': { groupPolicy: 'allowlist', requireMention: false, allowFrom: ['42'] },
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
dialogId: 'chat615',
|
|
177
|
-
chatId: '615',
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
expect(result.groupPolicy).toBe('allowlist');
|
|
181
|
-
expect(result.requireMention).toBe(false);
|
|
182
|
-
expect(result.senderAllowFrom).toEqual(['42']);
|
|
183
|
-
expect(result.matchedGroupKey).toBe('*');
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('treats explicit group override as allowed even when top-level group allowlist is present', () => {
|
|
187
|
-
const result = resolveGroupAccess({
|
|
188
|
-
config: {
|
|
189
|
-
groupAllowFrom: ['chat300'],
|
|
190
|
-
groups: {
|
|
191
|
-
chat208: { groupPolicy: 'open' },
|
|
192
|
-
},
|
|
193
|
-
},
|
|
194
|
-
dialogId: 'chat208',
|
|
195
|
-
chatId: '208',
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
expect(result.groupAllowed).toBe(true);
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
describe('checkGroupAccessWithPairing', () => {
|
|
203
|
-
it('reports pairing passively without creating a request', async () => {
|
|
204
|
-
const runtime = makeMockRuntime();
|
|
205
|
-
const result = await checkGroupAccessPassive({
|
|
206
|
-
senderId: '99',
|
|
207
|
-
dialogId: 'chat208',
|
|
208
|
-
chatId: '208',
|
|
209
|
-
config: { groupPolicy: 'pairing' },
|
|
210
|
-
runtime,
|
|
211
|
-
accountId: 'default',
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
expect(result).toBe('pairing');
|
|
215
|
-
expect(runtime.channel.pairing.upsertPairingRequest).not.toHaveBeenCalled();
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it('allows any sender in open mode', async () => {
|
|
219
|
-
const runtime = makeMockRuntime();
|
|
220
|
-
const result = await checkGroupAccessWithPairing({
|
|
221
|
-
senderId: '77',
|
|
222
|
-
dialogId: 'chat208',
|
|
223
|
-
chatId: '208',
|
|
224
|
-
config: { groupPolicy: 'open' },
|
|
225
|
-
runtime,
|
|
226
|
-
accountId: 'default',
|
|
227
|
-
pairingAdapter: mockAdapter,
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
expect(result).toBe('allow');
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('allows only webhook owner in webhookUser mode', async () => {
|
|
234
|
-
const runtime = makeMockRuntime();
|
|
235
|
-
const result = await checkGroupAccessWithPairing({
|
|
236
|
-
senderId: '42',
|
|
237
|
-
dialogId: 'chat208',
|
|
238
|
-
chatId: '208',
|
|
239
|
-
config: {
|
|
240
|
-
groupPolicy: 'webhookUser',
|
|
241
|
-
webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
|
|
242
|
-
},
|
|
243
|
-
runtime,
|
|
244
|
-
accountId: 'default',
|
|
245
|
-
pairingAdapter: mockAdapter,
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
expect(result).toBe('allow');
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it('uses merged top-level and per-group allowFrom in allowlist mode', async () => {
|
|
252
|
-
const runtime = makeMockRuntime();
|
|
253
|
-
const allowed = await checkGroupAccessWithPairing({
|
|
254
|
-
senderId: '42',
|
|
255
|
-
dialogId: 'chat208',
|
|
256
|
-
chatId: '208',
|
|
257
|
-
config: {
|
|
258
|
-
groupPolicy: 'allowlist',
|
|
259
|
-
allowFrom: ['7'],
|
|
260
|
-
groups: {
|
|
261
|
-
chat208: { allowFrom: ['42'] },
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
runtime,
|
|
265
|
-
accountId: 'default',
|
|
266
|
-
pairingAdapter: mockAdapter,
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
const denied = await checkGroupAccessWithPairing({
|
|
270
|
-
senderId: '99',
|
|
271
|
-
dialogId: 'chat208',
|
|
272
|
-
chatId: '208',
|
|
273
|
-
config: {
|
|
274
|
-
groupPolicy: 'allowlist',
|
|
275
|
-
allowFrom: ['7'],
|
|
276
|
-
groups: {
|
|
277
|
-
chat208: { allowFrom: ['42'] },
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
runtime,
|
|
281
|
-
accountId: 'default',
|
|
282
|
-
pairingAdapter: mockAdapter,
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
expect(allowed).toBe('allow');
|
|
286
|
-
expect(denied).toBe('deny');
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
it('creates pairing request for unknown sender in group pairing mode', async () => {
|
|
290
|
-
const runtime = makeMockRuntime();
|
|
291
|
-
const result = await checkGroupAccessWithPairing({
|
|
292
|
-
senderId: '99',
|
|
293
|
-
dialogId: 'chat208',
|
|
294
|
-
chatId: '208',
|
|
295
|
-
config: { groupPolicy: 'pairing' },
|
|
296
|
-
runtime,
|
|
297
|
-
accountId: 'default',
|
|
298
|
-
pairingAdapter: mockAdapter,
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
expect(result).toBe('pairing');
|
|
302
|
-
expect(runtime.channel.pairing.upsertPairingRequest).toHaveBeenCalled();
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
it('denies groups outside configured group allowlist', async () => {
|
|
306
|
-
const runtime = makeMockRuntime();
|
|
307
|
-
const result = await checkGroupAccessWithPairing({
|
|
308
|
-
senderId: '42',
|
|
309
|
-
dialogId: 'chat208',
|
|
310
|
-
chatId: '208',
|
|
311
|
-
config: {
|
|
312
|
-
groupPolicy: 'open',
|
|
313
|
-
groupAllowFrom: ['chat300'],
|
|
314
|
-
},
|
|
315
|
-
runtime,
|
|
316
|
-
accountId: 'default',
|
|
317
|
-
pairingAdapter: mockAdapter,
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
expect(result).toBe('deny');
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it('allows groups listed by numeric chat id in top-level group allowlist', async () => {
|
|
324
|
-
const runtime = makeMockRuntime();
|
|
325
|
-
const result = await checkGroupAccessWithPairing({
|
|
326
|
-
senderId: '42',
|
|
327
|
-
dialogId: 'chat208',
|
|
328
|
-
chatId: '208',
|
|
329
|
-
config: {
|
|
330
|
-
groupPolicy: 'open',
|
|
331
|
-
groupAllowFrom: ['208'],
|
|
332
|
-
},
|
|
333
|
-
runtime,
|
|
334
|
-
accountId: 'default',
|
|
335
|
-
pairingAdapter: mockAdapter,
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
expect(result).toBe('allow');
|
|
339
|
-
});
|
|
340
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { HistoryCache } from '../src/history-cache.js';
|
|
3
|
-
|
|
4
|
-
describe('HistoryCache', () => {
|
|
5
|
-
it('keeps only the newest entries up to the per-key limit', () => {
|
|
6
|
-
const cache = new HistoryCache();
|
|
7
|
-
|
|
8
|
-
cache.append({
|
|
9
|
-
key: 'chat:1',
|
|
10
|
-
limit: 2,
|
|
11
|
-
entry: { messageId: '1', sender: 'A', body: 'first' },
|
|
12
|
-
});
|
|
13
|
-
cache.append({
|
|
14
|
-
key: 'chat:1',
|
|
15
|
-
limit: 2,
|
|
16
|
-
entry: { messageId: '2', sender: 'A', body: 'second' },
|
|
17
|
-
});
|
|
18
|
-
cache.append({
|
|
19
|
-
key: 'chat:1',
|
|
20
|
-
limit: 2,
|
|
21
|
-
entry: { messageId: '3', sender: 'A', body: 'third' },
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
expect(cache.get('chat:1')).toEqual([
|
|
25
|
-
{ messageId: '2', sender: 'A', body: 'second' },
|
|
26
|
-
{ messageId: '3', sender: 'A', body: 'third' },
|
|
27
|
-
]);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('finds entries by message id', () => {
|
|
31
|
-
const cache = new HistoryCache();
|
|
32
|
-
cache.append({
|
|
33
|
-
key: 'chat:1',
|
|
34
|
-
limit: 10,
|
|
35
|
-
entry: { messageId: '55', sender: 'A', body: 'hello' },
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
expect(cache.findByMessageId('chat:1', '55')).toMatchObject({
|
|
39
|
-
messageId: '55',
|
|
40
|
-
body: 'hello',
|
|
41
|
-
});
|
|
42
|
-
expect(cache.findByMessageId('chat:1', '99')).toBeUndefined();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('evicts oldest keys when cache exceeds maxKeys', () => {
|
|
46
|
-
const cache = new HistoryCache({ maxKeys: 2 });
|
|
47
|
-
cache.append({
|
|
48
|
-
key: 'chat:1',
|
|
49
|
-
limit: 10,
|
|
50
|
-
entry: { messageId: '1', sender: 'A', body: 'one' },
|
|
51
|
-
});
|
|
52
|
-
cache.append({
|
|
53
|
-
key: 'chat:2',
|
|
54
|
-
limit: 10,
|
|
55
|
-
entry: { messageId: '2', sender: 'B', body: 'two' },
|
|
56
|
-
});
|
|
57
|
-
cache.append({
|
|
58
|
-
key: 'chat:3',
|
|
59
|
-
limit: 10,
|
|
60
|
-
entry: { messageId: '3', sender: 'C', body: 'three' },
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
expect(cache.size()).toBe(2);
|
|
64
|
-
expect(cache.get('chat:1')).toEqual([]);
|
|
65
|
-
expect(cache.get('chat:2')).toHaveLength(1);
|
|
66
|
-
expect(cache.get('chat:3')).toHaveLength(1);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('stores and lists conversation metadata for history keys', () => {
|
|
70
|
-
const cache = new HistoryCache();
|
|
71
|
-
|
|
72
|
-
cache.append({
|
|
73
|
-
key: 'default:chat520',
|
|
74
|
-
limit: 10,
|
|
75
|
-
entry: { messageId: '1', sender: 'Alice', body: 'hello' },
|
|
76
|
-
meta: {
|
|
77
|
-
dialogId: 'chat520',
|
|
78
|
-
chatId: '520',
|
|
79
|
-
chatName: 'Чат зеленый 17',
|
|
80
|
-
chatType: 'chat',
|
|
81
|
-
isGroup: true,
|
|
82
|
-
lastActivityAt: 1,
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
expect(cache.listConversations()).toEqual([
|
|
87
|
-
{
|
|
88
|
-
key: 'default:chat520',
|
|
89
|
-
dialogId: 'chat520',
|
|
90
|
-
chatId: '520',
|
|
91
|
-
chatName: 'Чат зеленый 17',
|
|
92
|
-
chatType: 'chat',
|
|
93
|
-
isGroup: true,
|
|
94
|
-
lastActivityAt: 1,
|
|
95
|
-
},
|
|
96
|
-
]);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('does not append duplicate entries with the same message id to one history key', () => {
|
|
100
|
-
const cache = new HistoryCache();
|
|
101
|
-
|
|
102
|
-
cache.append({
|
|
103
|
-
key: 'chat:1',
|
|
104
|
-
limit: 10,
|
|
105
|
-
entry: { messageId: '55', sender: 'A', body: 'hello', eventScope: 'bot' },
|
|
106
|
-
});
|
|
107
|
-
cache.append({
|
|
108
|
-
key: 'chat:1',
|
|
109
|
-
limit: 10,
|
|
110
|
-
entry: { messageId: '55', sender: 'A', body: 'hello', eventScope: 'user' },
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
expect(cache.get('chat:1')).toEqual([
|
|
114
|
-
{ messageId: '55', sender: 'A', body: 'hello', eventScope: 'bot' },
|
|
115
|
-
]);
|
|
116
|
-
});
|
|
117
|
-
});
|
package/tests/i18n.test.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
accessApproved,
|
|
4
|
-
accessDenied,
|
|
5
|
-
groupPairingPending,
|
|
6
|
-
resolveLocale,
|
|
7
|
-
welcomeMessage,
|
|
8
|
-
onboardingMessage,
|
|
9
|
-
ownerAndAllowedUsersOnly,
|
|
10
|
-
personalBotOwnerOnly,
|
|
11
|
-
watchOwnerDmNotice,
|
|
12
|
-
} from '../src/i18n.js';
|
|
13
|
-
|
|
14
|
-
describe('i18n welcome messages', () => {
|
|
15
|
-
it('resolves locale to two-letter lowercase code', () => {
|
|
16
|
-
expect(resolveLocale('RU')).toBe('ru');
|
|
17
|
-
expect(resolveLocale('en_US')).toBe('en');
|
|
18
|
-
expect(resolveLocale(undefined)).toBe('en');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('returns localized Russian welcome message', () => {
|
|
22
|
-
expect(welcomeMessage('ru', 'OpenClaw')).toContain('OpenClaw готов к работе');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('falls back to English welcome message for unknown locale', () => {
|
|
26
|
-
expect(welcomeMessage('zz', 'OpenClaw')).toBe(
|
|
27
|
-
'OpenClaw is ready. Send a message or choose a command below.',
|
|
28
|
-
);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('returns pairing onboarding message when dmPolicy is pairing', () => {
|
|
32
|
-
expect(onboardingMessage('ru', 'OpenClaw', 'pairing')).toContain('код привязки');
|
|
33
|
-
expect(onboardingMessage('en', 'OpenClaw', 'pairing')).toContain('pairing code');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('returns localized personal bot access denied message', () => {
|
|
37
|
-
expect(personalBotOwnerOnly('ru')).toContain('Писать ему может только владелец');
|
|
38
|
-
expect(personalBotOwnerOnly('en')).toContain('Only the bot owner can message it');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('returns localized access approved message', () => {
|
|
42
|
-
expect(accessApproved('ru')).toBe('Доступ к боту подтвержден.');
|
|
43
|
-
expect(accessApproved('en')).toBe('Access to the bot has been approved.');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('returns localized owner and allowlist access denied message', () => {
|
|
47
|
-
expect(ownerAndAllowedUsersOnly('ru')).toContain('пользователям с подтвержденным доступом');
|
|
48
|
-
expect(ownerAndAllowedUsersOnly('en')).toContain('approved access');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('returns localized generic access denied message', () => {
|
|
52
|
-
expect(accessDenied('ru')).toContain('нет доступа');
|
|
53
|
-
expect(accessDenied('en')).toContain('do not have access');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('returns localized group pairing pending message', () => {
|
|
57
|
-
expect(groupPairingPending('ru')).toContain('сначала нужно подтвердить доступ');
|
|
58
|
-
expect(groupPairingPending('en')).toContain('access must be approved first');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('returns localized owner watch DM notification message', () => {
|
|
62
|
-
expect(watchOwnerDmNotice('ru', {
|
|
63
|
-
chatRef: '[URL=/online/?IM_DIALOG=chat520&IM_MESSAGE=710]Green Chat 15[/URL]',
|
|
64
|
-
topicsRef: '[b]секрет[/b]',
|
|
65
|
-
})).toBe('Сработало правило отслеживания в чате [URL=/online/?IM_DIALOG=chat520&IM_MESSAGE=710]Green Chat 15[/URL]. Совпавшие темы: [b]секрет[/b]');
|
|
66
|
-
expect(watchOwnerDmNotice('en', {
|
|
67
|
-
chatRef: '[URL=/online/?IM_DIALOG=chat520&IM_MESSAGE=710]Green Chat 15[/URL]',
|
|
68
|
-
topicsRef: '[b]secret[/b]',
|
|
69
|
-
})).toBe('A watch rule was triggered in chat [URL=/online/?IM_DIALOG=chat520&IM_MESSAGE=710]Green Chat 15[/URL]. Matched topics: [b]secret[/b]');
|
|
70
|
-
expect(watchOwnerDmNotice('de', {
|
|
71
|
-
chatRef: '[URL=/online/?IM_DIALOG=chat520&IM_MESSAGE=710]Green Chat 15[/URL]',
|
|
72
|
-
topicsRef: '[b]geheim[/b]',
|
|
73
|
-
})).toBe('Eine Beobachtungsregel wurde im Chat [URL=/online/?IM_DIALOG=chat520&IM_MESSAGE=710]Green Chat 15[/URL] ausgeloest. Zugeordnete Themen: [b]geheim[/b]');
|
|
74
|
-
expect(watchOwnerDmNotice('zz', {
|
|
75
|
-
chatRef: '[URL=/online/?IM_DIALOG=chat520&IM_MESSAGE=710]Green Chat 15[/URL]',
|
|
76
|
-
topicsRef: '[b]secret[/b]',
|
|
77
|
-
})).toBe('A watch rule was triggered in chat [URL=/online/?IM_DIALOG=chat520&IM_MESSAGE=710]Green Chat 15[/URL]. Matched topics: [b]secret[/b]');
|
|
78
|
-
|
|
79
|
-
expect(watchOwnerDmNotice('ru', {
|
|
80
|
-
chatRef: '[URL=/online/?IM_DIALOG=10&IM_MESSAGE=31671]Сергей Рыжиков[/URL]',
|
|
81
|
-
topicsRef: '[b]авария[/b]',
|
|
82
|
-
sourceKind: 'dm',
|
|
83
|
-
})).toBe('Сработало правило отслеживания в личном чате с [URL=/online/?IM_DIALOG=10&IM_MESSAGE=31671]Сергей Рыжиков[/URL]. Совпавшие темы: [b]авария[/b]');
|
|
84
|
-
expect(watchOwnerDmNotice('en', {
|
|
85
|
-
chatRef: '[URL=/online/?IM_DIALOG=10&IM_MESSAGE=31671]Sergey Ryzhikov[/URL]',
|
|
86
|
-
topicsRef: '[b]incident[/b]',
|
|
87
|
-
sourceKind: 'dm',
|
|
88
|
-
})).toBe('A watch rule was triggered in a direct chat with [URL=/online/?IM_DIALOG=10&IM_MESSAGE=31671]Sergey Ryzhikov[/URL]. Matched topics: [b]incident[/b]');
|
|
89
|
-
});
|
|
90
|
-
});
|