@rozek/nanoclaw 0.0.4 → 0.0.6

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 (132) hide show
  1. package/container/agent-runner/package-lock.json +1524 -0
  2. package/dist/cli.js +75 -4
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +34 -0
  6. package/dist/index.js.map +1 -1
  7. package/package.json +7 -1
  8. package/.claude/settings.json +0 -1
  9. package/.claude/skills/add-compact/SKILL.md +0 -135
  10. package/.claude/skills/add-discord/SKILL.md +0 -203
  11. package/.claude/skills/add-gmail/SKILL.md +0 -220
  12. package/.claude/skills/add-image-vision/SKILL.md +0 -94
  13. package/.claude/skills/add-ollama-tool/SKILL.md +0 -153
  14. package/.claude/skills/add-parallel/SKILL.md +0 -290
  15. package/.claude/skills/add-pdf-reader/SKILL.md +0 -104
  16. package/.claude/skills/add-reactions/SKILL.md +0 -117
  17. package/.claude/skills/add-slack/SKILL.md +0 -207
  18. package/.claude/skills/add-telegram/SKILL.md +0 -222
  19. package/.claude/skills/add-telegram-swarm/SKILL.md +0 -384
  20. package/.claude/skills/add-voice-transcription/SKILL.md +0 -148
  21. package/.claude/skills/add-whatsapp/SKILL.md +0 -372
  22. package/.claude/skills/convert-to-apple-container/SKILL.md +0 -175
  23. package/.claude/skills/customize/SKILL.md +0 -110
  24. package/.claude/skills/debug/SKILL.md +0 -349
  25. package/.claude/skills/get-qodo-rules/SKILL.md +0 -122
  26. package/.claude/skills/get-qodo-rules/references/output-format.md +0 -41
  27. package/.claude/skills/get-qodo-rules/references/pagination.md +0 -33
  28. package/.claude/skills/get-qodo-rules/references/repository-scope.md +0 -26
  29. package/.claude/skills/qodo-pr-resolver/SKILL.md +0 -326
  30. package/.claude/skills/qodo-pr-resolver/resources/providers.md +0 -329
  31. package/.claude/skills/setup/SKILL.md +0 -218
  32. package/.claude/skills/update-nanoclaw/SKILL.md +0 -235
  33. package/.claude/skills/update-skills/SKILL.md +0 -130
  34. package/.claude/skills/use-local-whisper/SKILL.md +0 -152
  35. package/.claude/skills/x-integration/SKILL.md +0 -417
  36. package/.claude/skills/x-integration/agent.ts +0 -243
  37. package/.claude/skills/x-integration/host.ts +0 -159
  38. package/.claude/skills/x-integration/lib/browser.ts +0 -148
  39. package/.claude/skills/x-integration/lib/config.ts +0 -62
  40. package/.claude/skills/x-integration/scripts/like.ts +0 -56
  41. package/.claude/skills/x-integration/scripts/post.ts +0 -66
  42. package/.claude/skills/x-integration/scripts/quote.ts +0 -80
  43. package/.claude/skills/x-integration/scripts/reply.ts +0 -74
  44. package/.claude/skills/x-integration/scripts/retweet.ts +0 -62
  45. package/.claude/skills/x-integration/scripts/setup.ts +0 -87
  46. package/.env.example +0 -1
  47. package/.github/CODEOWNERS +0 -10
  48. package/.github/PULL_REQUEST_TEMPLATE.md +0 -14
  49. package/.github/workflows/bump-version.yml +0 -32
  50. package/.github/workflows/ci.yml +0 -25
  51. package/.github/workflows/merge-forward-skills.yml +0 -160
  52. package/.github/workflows/update-tokens.yml +0 -42
  53. package/.husky/pre-commit +0 -1
  54. package/.mcp.json +0 -3
  55. package/.nvmrc +0 -1
  56. package/.prettierrc +0 -3
  57. package/CHANGELOG.md +0 -8
  58. package/CONTRIBUTING.md +0 -23
  59. package/CONTRIBUTORS.md +0 -15
  60. package/NanoClaw_with_Web-Support.md +0 -325
  61. package/README_zh.md +0 -200
  62. package/assets/nanoclaw-favicon.png +0 -0
  63. package/assets/nanoclaw-icon.png +0 -0
  64. package/assets/nanoclaw-logo-dark.png +0 -0
  65. package/assets/nanoclaw-logo.png +0 -0
  66. package/assets/nanoclaw-profile.jpeg +0 -0
  67. package/assets/nanoclaw-sales.png +0 -0
  68. package/assets/social-preview.jpg +0 -0
  69. package/config-examples/mount-allowlist.json +0 -25
  70. package/docs/APPLE-CONTAINER-NETWORKING.md +0 -90
  71. package/docs/DEBUG_CHECKLIST.md +0 -143
  72. package/docs/REQUIREMENTS.md +0 -196
  73. package/docs/SDK_DEEP_DIVE.md +0 -643
  74. package/docs/SECURITY.md +0 -122
  75. package/docs/SPEC.md +0 -785
  76. package/docs/docker-sandboxes.md +0 -359
  77. package/docs/nanoclaw-architecture-final.md +0 -1063
  78. package/docs/nanorepo-architecture.md +0 -168
  79. package/docs/skills-as-branches.md +0 -662
  80. package/groups/global/CLAUDE.md +0 -58
  81. package/groups/main/CLAUDE.md +0 -246
  82. package/launchd/com.nanoclaw.plist +0 -32
  83. package/repo-tokens/README.md +0 -113
  84. package/repo-tokens/action.yml +0 -186
  85. package/repo-tokens/badge.svg +0 -23
  86. package/repo-tokens/examples/green.svg +0 -14
  87. package/repo-tokens/examples/red.svg +0 -14
  88. package/repo-tokens/examples/yellow-green.svg +0 -14
  89. package/repo-tokens/examples/yellow.svg +0 -14
  90. package/scripts/run-migrations.ts +0 -105
  91. package/setup.sh +0 -161
  92. package/src/channels/index.ts +0 -15
  93. package/src/channels/registry.test.ts +0 -42
  94. package/src/channels/registry.ts +0 -32
  95. package/src/channels/web.ts +0 -1931
  96. package/src/cli.ts +0 -210
  97. package/src/config.ts +0 -73
  98. package/src/container-runner.test.ts +0 -210
  99. package/src/container-runner.ts +0 -768
  100. package/src/container-runtime.test.ts +0 -149
  101. package/src/container-runtime.ts +0 -127
  102. package/src/credential-proxy.test.ts +0 -192
  103. package/src/credential-proxy.ts +0 -125
  104. package/src/db.test.ts +0 -484
  105. package/src/db.ts +0 -803
  106. package/src/env.ts +0 -42
  107. package/src/formatting.test.ts +0 -256
  108. package/src/group-folder.test.ts +0 -43
  109. package/src/group-folder.ts +0 -44
  110. package/src/group-queue.test.ts +0 -484
  111. package/src/group-queue.ts +0 -379
  112. package/src/index.ts +0 -854
  113. package/src/ipc-auth.test.ts +0 -679
  114. package/src/ipc.ts +0 -461
  115. package/src/logger.ts +0 -16
  116. package/src/mount-security.ts +0 -419
  117. package/src/remote-control.test.ts +0 -397
  118. package/src/remote-control.ts +0 -224
  119. package/src/router.ts +0 -52
  120. package/src/routing.test.ts +0 -170
  121. package/src/sender-allowlist.test.ts +0 -216
  122. package/src/sender-allowlist.ts +0 -128
  123. package/src/session-commands.test.ts +0 -247
  124. package/src/session-commands.ts +0 -163
  125. package/src/task-scheduler.test.ts +0 -129
  126. package/src/task-scheduler.ts +0 -328
  127. package/src/timezone.test.ts +0 -29
  128. package/src/timezone.ts +0 -16
  129. package/src/types.ts +0 -109
  130. package/tsconfig.json +0 -20
  131. package/vitest.config.ts +0 -7
  132. package/vitest.skills.config.ts +0 -7
@@ -1,170 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
-
3
- import { _initTestDatabase, getAllChats, storeChatMetadata } from './db.js';
4
- import { getAvailableGroups, _setRegisteredGroups } from './index.js';
5
-
6
- beforeEach(() => {
7
- _initTestDatabase();
8
- _setRegisteredGroups({});
9
- });
10
-
11
- // --- JID ownership patterns ---
12
-
13
- describe('JID ownership patterns', () => {
14
- // These test the patterns that will become ownsJid() on the Channel interface
15
-
16
- it('WhatsApp group JID: ends with @g.us', () => {
17
- const jid = '12345678@g.us';
18
- expect(jid.endsWith('@g.us')).toBe(true);
19
- });
20
-
21
- it('WhatsApp DM JID: ends with @s.whatsapp.net', () => {
22
- const jid = '12345678@s.whatsapp.net';
23
- expect(jid.endsWith('@s.whatsapp.net')).toBe(true);
24
- });
25
- });
26
-
27
- // --- getAvailableGroups ---
28
-
29
- describe('getAvailableGroups', () => {
30
- it('returns only groups, excludes DMs', () => {
31
- storeChatMetadata(
32
- 'group1@g.us',
33
- '2024-01-01T00:00:01.000Z',
34
- 'Group 1',
35
- 'whatsapp',
36
- true,
37
- );
38
- storeChatMetadata(
39
- 'user@s.whatsapp.net',
40
- '2024-01-01T00:00:02.000Z',
41
- 'User DM',
42
- 'whatsapp',
43
- false,
44
- );
45
- storeChatMetadata(
46
- 'group2@g.us',
47
- '2024-01-01T00:00:03.000Z',
48
- 'Group 2',
49
- 'whatsapp',
50
- true,
51
- );
52
-
53
- const groups = getAvailableGroups();
54
- expect(groups).toHaveLength(2);
55
- expect(groups.map((g) => g.jid)).toContain('group1@g.us');
56
- expect(groups.map((g) => g.jid)).toContain('group2@g.us');
57
- expect(groups.map((g) => g.jid)).not.toContain('user@s.whatsapp.net');
58
- });
59
-
60
- it('excludes __group_sync__ sentinel', () => {
61
- storeChatMetadata('__group_sync__', '2024-01-01T00:00:00.000Z');
62
- storeChatMetadata(
63
- 'group@g.us',
64
- '2024-01-01T00:00:01.000Z',
65
- 'Group',
66
- 'whatsapp',
67
- true,
68
- );
69
-
70
- const groups = getAvailableGroups();
71
- expect(groups).toHaveLength(1);
72
- expect(groups[0].jid).toBe('group@g.us');
73
- });
74
-
75
- it('marks registered groups correctly', () => {
76
- storeChatMetadata(
77
- 'reg@g.us',
78
- '2024-01-01T00:00:01.000Z',
79
- 'Registered',
80
- 'whatsapp',
81
- true,
82
- );
83
- storeChatMetadata(
84
- 'unreg@g.us',
85
- '2024-01-01T00:00:02.000Z',
86
- 'Unregistered',
87
- 'whatsapp',
88
- true,
89
- );
90
-
91
- _setRegisteredGroups({
92
- 'reg@g.us': {
93
- name: 'Registered',
94
- folder: 'registered',
95
- trigger: '@Andy',
96
- added_at: '2024-01-01T00:00:00.000Z',
97
- },
98
- });
99
-
100
- const groups = getAvailableGroups();
101
- const reg = groups.find((g) => g.jid === 'reg@g.us');
102
- const unreg = groups.find((g) => g.jid === 'unreg@g.us');
103
-
104
- expect(reg?.isRegistered).toBe(true);
105
- expect(unreg?.isRegistered).toBe(false);
106
- });
107
-
108
- it('returns groups ordered by most recent activity', () => {
109
- storeChatMetadata(
110
- 'old@g.us',
111
- '2024-01-01T00:00:01.000Z',
112
- 'Old',
113
- 'whatsapp',
114
- true,
115
- );
116
- storeChatMetadata(
117
- 'new@g.us',
118
- '2024-01-01T00:00:05.000Z',
119
- 'New',
120
- 'whatsapp',
121
- true,
122
- );
123
- storeChatMetadata(
124
- 'mid@g.us',
125
- '2024-01-01T00:00:03.000Z',
126
- 'Mid',
127
- 'whatsapp',
128
- true,
129
- );
130
-
131
- const groups = getAvailableGroups();
132
- expect(groups[0].jid).toBe('new@g.us');
133
- expect(groups[1].jid).toBe('mid@g.us');
134
- expect(groups[2].jid).toBe('old@g.us');
135
- });
136
-
137
- it('excludes non-group chats regardless of JID format', () => {
138
- // Unknown JID format stored without is_group should not appear
139
- storeChatMetadata(
140
- 'unknown-format-123',
141
- '2024-01-01T00:00:01.000Z',
142
- 'Unknown',
143
- );
144
- // Explicitly non-group with unusual JID
145
- storeChatMetadata(
146
- 'custom:abc',
147
- '2024-01-01T00:00:02.000Z',
148
- 'Custom DM',
149
- 'custom',
150
- false,
151
- );
152
- // A real group for contrast
153
- storeChatMetadata(
154
- 'group@g.us',
155
- '2024-01-01T00:00:03.000Z',
156
- 'Group',
157
- 'whatsapp',
158
- true,
159
- );
160
-
161
- const groups = getAvailableGroups();
162
- expect(groups).toHaveLength(1);
163
- expect(groups[0].jid).toBe('group@g.us');
164
- });
165
-
166
- it('returns empty array when no chats exist', () => {
167
- const groups = getAvailableGroups();
168
- expect(groups).toHaveLength(0);
169
- });
170
- });
@@ -1,216 +0,0 @@
1
- import fs from 'fs';
2
- import os from 'os';
3
- import path from 'path';
4
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
-
6
- import {
7
- isSenderAllowed,
8
- isTriggerAllowed,
9
- loadSenderAllowlist,
10
- SenderAllowlistConfig,
11
- shouldDropMessage,
12
- } from './sender-allowlist.js';
13
-
14
- let tmpDir: string;
15
-
16
- function cfgPath(name = 'sender-allowlist.json'): string {
17
- return path.join(tmpDir, name);
18
- }
19
-
20
- function writeConfig(config: unknown, name?: string): string {
21
- const p = cfgPath(name);
22
- fs.writeFileSync(p, JSON.stringify(config));
23
- return p;
24
- }
25
-
26
- beforeEach(() => {
27
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'allowlist-test-'));
28
- });
29
-
30
- afterEach(() => {
31
- fs.rmSync(tmpDir, { recursive: true, force: true });
32
- });
33
-
34
- describe('loadSenderAllowlist', () => {
35
- it('returns allow-all defaults when file is missing', () => {
36
- const cfg = loadSenderAllowlist(cfgPath());
37
- expect(cfg.default.allow).toBe('*');
38
- expect(cfg.default.mode).toBe('trigger');
39
- expect(cfg.logDenied).toBe(true);
40
- });
41
-
42
- it('loads allow=* config', () => {
43
- const p = writeConfig({
44
- default: { allow: '*', mode: 'trigger' },
45
- chats: {},
46
- logDenied: false,
47
- });
48
- const cfg = loadSenderAllowlist(p);
49
- expect(cfg.default.allow).toBe('*');
50
- expect(cfg.logDenied).toBe(false);
51
- });
52
-
53
- it('loads allow=[] (deny all)', () => {
54
- const p = writeConfig({
55
- default: { allow: [], mode: 'trigger' },
56
- chats: {},
57
- });
58
- const cfg = loadSenderAllowlist(p);
59
- expect(cfg.default.allow).toEqual([]);
60
- });
61
-
62
- it('loads allow=[list]', () => {
63
- const p = writeConfig({
64
- default: { allow: ['alice', 'bob'], mode: 'drop' },
65
- chats: {},
66
- });
67
- const cfg = loadSenderAllowlist(p);
68
- expect(cfg.default.allow).toEqual(['alice', 'bob']);
69
- expect(cfg.default.mode).toBe('drop');
70
- });
71
-
72
- it('per-chat override beats default', () => {
73
- const p = writeConfig({
74
- default: { allow: '*', mode: 'trigger' },
75
- chats: { 'group-a': { allow: ['alice'], mode: 'drop' } },
76
- });
77
- const cfg = loadSenderAllowlist(p);
78
- expect(cfg.chats['group-a'].allow).toEqual(['alice']);
79
- expect(cfg.chats['group-a'].mode).toBe('drop');
80
- });
81
-
82
- it('returns allow-all on invalid JSON', () => {
83
- const p = cfgPath();
84
- fs.writeFileSync(p, '{ not valid json }}}');
85
- const cfg = loadSenderAllowlist(p);
86
- expect(cfg.default.allow).toBe('*');
87
- });
88
-
89
- it('returns allow-all on invalid schema', () => {
90
- const p = writeConfig({ default: { oops: true } });
91
- const cfg = loadSenderAllowlist(p);
92
- expect(cfg.default.allow).toBe('*');
93
- });
94
-
95
- it('rejects non-string allow array items', () => {
96
- const p = writeConfig({
97
- default: { allow: [123, null, true], mode: 'trigger' },
98
- chats: {},
99
- });
100
- const cfg = loadSenderAllowlist(p);
101
- expect(cfg.default.allow).toBe('*'); // falls back to default
102
- });
103
-
104
- it('skips invalid per-chat entries', () => {
105
- const p = writeConfig({
106
- default: { allow: '*', mode: 'trigger' },
107
- chats: {
108
- good: { allow: ['alice'], mode: 'trigger' },
109
- bad: { allow: 123 },
110
- },
111
- });
112
- const cfg = loadSenderAllowlist(p);
113
- expect(cfg.chats['good']).toBeDefined();
114
- expect(cfg.chats['bad']).toBeUndefined();
115
- });
116
- });
117
-
118
- describe('isSenderAllowed', () => {
119
- it('allow=* allows any sender', () => {
120
- const cfg: SenderAllowlistConfig = {
121
- default: { allow: '*', mode: 'trigger' },
122
- chats: {},
123
- logDenied: true,
124
- };
125
- expect(isSenderAllowed('g1', 'anyone', cfg)).toBe(true);
126
- });
127
-
128
- it('allow=[] denies any sender', () => {
129
- const cfg: SenderAllowlistConfig = {
130
- default: { allow: [], mode: 'trigger' },
131
- chats: {},
132
- logDenied: true,
133
- };
134
- expect(isSenderAllowed('g1', 'anyone', cfg)).toBe(false);
135
- });
136
-
137
- it('allow=[list] allows exact match only', () => {
138
- const cfg: SenderAllowlistConfig = {
139
- default: { allow: ['alice', 'bob'], mode: 'trigger' },
140
- chats: {},
141
- logDenied: true,
142
- };
143
- expect(isSenderAllowed('g1', 'alice', cfg)).toBe(true);
144
- expect(isSenderAllowed('g1', 'eve', cfg)).toBe(false);
145
- });
146
-
147
- it('uses per-chat entry over default', () => {
148
- const cfg: SenderAllowlistConfig = {
149
- default: { allow: '*', mode: 'trigger' },
150
- chats: { g1: { allow: ['alice'], mode: 'trigger' } },
151
- logDenied: true,
152
- };
153
- expect(isSenderAllowed('g1', 'bob', cfg)).toBe(false);
154
- expect(isSenderAllowed('g2', 'bob', cfg)).toBe(true);
155
- });
156
- });
157
-
158
- describe('shouldDropMessage', () => {
159
- it('returns false for trigger mode', () => {
160
- const cfg: SenderAllowlistConfig = {
161
- default: { allow: '*', mode: 'trigger' },
162
- chats: {},
163
- logDenied: true,
164
- };
165
- expect(shouldDropMessage('g1', cfg)).toBe(false);
166
- });
167
-
168
- it('returns true for drop mode', () => {
169
- const cfg: SenderAllowlistConfig = {
170
- default: { allow: '*', mode: 'drop' },
171
- chats: {},
172
- logDenied: true,
173
- };
174
- expect(shouldDropMessage('g1', cfg)).toBe(true);
175
- });
176
-
177
- it('per-chat mode override', () => {
178
- const cfg: SenderAllowlistConfig = {
179
- default: { allow: '*', mode: 'trigger' },
180
- chats: { g1: { allow: '*', mode: 'drop' } },
181
- logDenied: true,
182
- };
183
- expect(shouldDropMessage('g1', cfg)).toBe(true);
184
- expect(shouldDropMessage('g2', cfg)).toBe(false);
185
- });
186
- });
187
-
188
- describe('isTriggerAllowed', () => {
189
- it('allows trigger for allowed sender', () => {
190
- const cfg: SenderAllowlistConfig = {
191
- default: { allow: ['alice'], mode: 'trigger' },
192
- chats: {},
193
- logDenied: false,
194
- };
195
- expect(isTriggerAllowed('g1', 'alice', cfg)).toBe(true);
196
- });
197
-
198
- it('denies trigger for disallowed sender', () => {
199
- const cfg: SenderAllowlistConfig = {
200
- default: { allow: ['alice'], mode: 'trigger' },
201
- chats: {},
202
- logDenied: false,
203
- };
204
- expect(isTriggerAllowed('g1', 'eve', cfg)).toBe(false);
205
- });
206
-
207
- it('logs when logDenied is true', () => {
208
- const cfg: SenderAllowlistConfig = {
209
- default: { allow: ['alice'], mode: 'trigger' },
210
- chats: {},
211
- logDenied: true,
212
- };
213
- isTriggerAllowed('g1', 'eve', cfg);
214
- // Logger.debug is called — we just verify no crash; logger is a real pino instance
215
- });
216
- });
@@ -1,128 +0,0 @@
1
- import fs from 'fs';
2
-
3
- import { SENDER_ALLOWLIST_PATH } from './config.js';
4
- import { logger } from './logger.js';
5
-
6
- export interface ChatAllowlistEntry {
7
- allow: '*' | string[];
8
- mode: 'trigger' | 'drop';
9
- }
10
-
11
- export interface SenderAllowlistConfig {
12
- default: ChatAllowlistEntry;
13
- chats: Record<string, ChatAllowlistEntry>;
14
- logDenied: boolean;
15
- }
16
-
17
- const DEFAULT_CONFIG: SenderAllowlistConfig = {
18
- default: { allow: '*', mode: 'trigger' },
19
- chats: {},
20
- logDenied: true,
21
- };
22
-
23
- function isValidEntry(entry: unknown): entry is ChatAllowlistEntry {
24
- if (!entry || typeof entry !== 'object') return false;
25
- const e = entry as Record<string, unknown>;
26
- const validAllow =
27
- e.allow === '*' ||
28
- (Array.isArray(e.allow) && e.allow.every((v) => typeof v === 'string'));
29
- const validMode = e.mode === 'trigger' || e.mode === 'drop';
30
- return validAllow && validMode;
31
- }
32
-
33
- export function loadSenderAllowlist(
34
- pathOverride?: string,
35
- ): SenderAllowlistConfig {
36
- const filePath = pathOverride ?? SENDER_ALLOWLIST_PATH;
37
-
38
- let raw: string;
39
- try {
40
- raw = fs.readFileSync(filePath, 'utf-8');
41
- } catch (err: unknown) {
42
- if ((err as NodeJS.ErrnoException).code === 'ENOENT') return DEFAULT_CONFIG;
43
- logger.warn(
44
- { err, path: filePath },
45
- 'sender-allowlist: cannot read config',
46
- );
47
- return DEFAULT_CONFIG;
48
- }
49
-
50
- let parsed: unknown;
51
- try {
52
- parsed = JSON.parse(raw);
53
- } catch {
54
- logger.warn({ path: filePath }, 'sender-allowlist: invalid JSON');
55
- return DEFAULT_CONFIG;
56
- }
57
-
58
- const obj = parsed as Record<string, unknown>;
59
-
60
- if (!isValidEntry(obj.default)) {
61
- logger.warn(
62
- { path: filePath },
63
- 'sender-allowlist: invalid or missing default entry',
64
- );
65
- return DEFAULT_CONFIG;
66
- }
67
-
68
- const chats: Record<string, ChatAllowlistEntry> = {};
69
- if (obj.chats && typeof obj.chats === 'object') {
70
- for (const [jid, entry] of Object.entries(
71
- obj.chats as Record<string, unknown>,
72
- )) {
73
- if (isValidEntry(entry)) {
74
- chats[jid] = entry;
75
- } else {
76
- logger.warn(
77
- { jid, path: filePath },
78
- 'sender-allowlist: skipping invalid chat entry',
79
- );
80
- }
81
- }
82
- }
83
-
84
- return {
85
- default: obj.default as ChatAllowlistEntry,
86
- chats,
87
- logDenied: obj.logDenied !== false,
88
- };
89
- }
90
-
91
- function getEntry(
92
- chatJid: string,
93
- cfg: SenderAllowlistConfig,
94
- ): ChatAllowlistEntry {
95
- return cfg.chats[chatJid] ?? cfg.default;
96
- }
97
-
98
- export function isSenderAllowed(
99
- chatJid: string,
100
- sender: string,
101
- cfg: SenderAllowlistConfig,
102
- ): boolean {
103
- const entry = getEntry(chatJid, cfg);
104
- if (entry.allow === '*') return true;
105
- return entry.allow.includes(sender);
106
- }
107
-
108
- export function shouldDropMessage(
109
- chatJid: string,
110
- cfg: SenderAllowlistConfig,
111
- ): boolean {
112
- return getEntry(chatJid, cfg).mode === 'drop';
113
- }
114
-
115
- export function isTriggerAllowed(
116
- chatJid: string,
117
- sender: string,
118
- cfg: SenderAllowlistConfig,
119
- ): boolean {
120
- const allowed = isSenderAllowed(chatJid, sender, cfg);
121
- if (!allowed && cfg.logDenied) {
122
- logger.debug(
123
- { chatJid, sender },
124
- 'sender-allowlist: trigger denied for sender',
125
- );
126
- }
127
- return allowed;
128
- }