signal-sdk 0.0.8 → 0.0.9
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/dist/SignalBot.d.ts +108 -0
- package/dist/SignalBot.js +811 -0
- package/dist/SignalCli.d.ts +135 -0
- package/dist/SignalCli.js +711 -0
- package/dist/__tests__/SignalBot.test.d.ts +1 -0
- package/dist/__tests__/SignalBot.test.js +102 -0
- package/dist/__tests__/SignalCli.test.d.ts +1 -0
- package/dist/__tests__/SignalCli.test.js +235 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +22 -0
- package/dist/interfaces.d.ts +938 -0
- package/dist/interfaces.js +11 -0
- package/package.json +5 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const SignalBot_1 = require("../SignalBot");
|
|
4
|
+
describe('SignalBot', () => {
|
|
5
|
+
let bot;
|
|
6
|
+
let mockConfig;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
mockConfig = {
|
|
9
|
+
phoneNumber: '+1234567890',
|
|
10
|
+
admins: ['+0987654321'],
|
|
11
|
+
group: {
|
|
12
|
+
name: 'Test Bot Group',
|
|
13
|
+
createIfNotExists: false
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
afterEach(async () => {
|
|
18
|
+
if (bot) {
|
|
19
|
+
await bot.stop();
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
test('should create bot with minimal config', () => {
|
|
23
|
+
bot = new SignalBot_1.SignalBot({
|
|
24
|
+
phoneNumber: '+1234567890'
|
|
25
|
+
});
|
|
26
|
+
expect(bot).toBeDefined();
|
|
27
|
+
expect(bot.isAdmin('+1234567890')).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
test('should create bot with full config', () => {
|
|
30
|
+
bot = new SignalBot_1.SignalBot(mockConfig);
|
|
31
|
+
expect(bot).toBeDefined();
|
|
32
|
+
expect(bot.isAdmin('+0987654321')).toBe(true);
|
|
33
|
+
expect(bot.getBotGroupId()).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
test('should add and remove commands', () => {
|
|
36
|
+
bot = new SignalBot_1.SignalBot(mockConfig);
|
|
37
|
+
const testCommand = {
|
|
38
|
+
name: 'test',
|
|
39
|
+
description: 'Test command',
|
|
40
|
+
handler: jest.fn()
|
|
41
|
+
};
|
|
42
|
+
bot.addCommand(testCommand);
|
|
43
|
+
const commands = bot.getCommands();
|
|
44
|
+
expect(commands.some((cmd) => cmd.name === 'test')).toBe(true);
|
|
45
|
+
const removed = bot.removeCommand('test');
|
|
46
|
+
expect(removed).toBe(true);
|
|
47
|
+
const commandsAfter = bot.getCommands();
|
|
48
|
+
expect(commandsAfter.some((cmd) => cmd.name === 'test')).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
test('should have default commands', () => {
|
|
51
|
+
bot = new SignalBot_1.SignalBot(mockConfig);
|
|
52
|
+
const commands = bot.getCommands();
|
|
53
|
+
const commandNames = commands.map((cmd) => cmd.name);
|
|
54
|
+
expect(commandNames).toContain('help');
|
|
55
|
+
expect(commandNames).toContain('stats');
|
|
56
|
+
expect(commandNames).toContain('ping');
|
|
57
|
+
expect(commandNames).toContain('info');
|
|
58
|
+
});
|
|
59
|
+
test('should emit ready event when started', (done) => {
|
|
60
|
+
bot = new SignalBot_1.SignalBot({
|
|
61
|
+
phoneNumber: '+1234567890',
|
|
62
|
+
group: {
|
|
63
|
+
name: 'Test Group',
|
|
64
|
+
createIfNotExists: false
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
// Mock SignalCli methods to avoid actual Signal operations
|
|
68
|
+
const mockSignalCli = bot.getSignalCli();
|
|
69
|
+
jest.spyOn(mockSignalCli, 'listDevices').mockResolvedValue([
|
|
70
|
+
{ id: 1, name: 'Test Device', created: Date.now(), lastSeen: Date.now() }
|
|
71
|
+
]);
|
|
72
|
+
jest.spyOn(mockSignalCli, 'startDaemon').mockImplementation(() => { });
|
|
73
|
+
jest.spyOn(mockSignalCli, 'connect').mockResolvedValue(undefined);
|
|
74
|
+
jest.spyOn(mockSignalCli, 'on').mockReturnValue(mockSignalCli);
|
|
75
|
+
bot.on('ready', () => {
|
|
76
|
+
expect(true).toBe(true);
|
|
77
|
+
done();
|
|
78
|
+
});
|
|
79
|
+
bot.start().catch(done);
|
|
80
|
+
});
|
|
81
|
+
test('should get stats', () => {
|
|
82
|
+
bot = new SignalBot_1.SignalBot(mockConfig);
|
|
83
|
+
const stats = bot.getStats();
|
|
84
|
+
expect(stats).toHaveProperty('messagesReceived');
|
|
85
|
+
expect(stats).toHaveProperty('commandsExecuted');
|
|
86
|
+
expect(stats).toHaveProperty('startTime');
|
|
87
|
+
expect(stats).toHaveProperty('lastActivity');
|
|
88
|
+
expect(stats).toHaveProperty('activeUsers');
|
|
89
|
+
expect(stats.messagesReceived).toBe(0);
|
|
90
|
+
expect(stats.commandsExecuted).toBe(0);
|
|
91
|
+
expect(stats.activeUsers).toBe(0);
|
|
92
|
+
});
|
|
93
|
+
test('should handle admin permissions', () => {
|
|
94
|
+
bot = new SignalBot_1.SignalBot({
|
|
95
|
+
phoneNumber: '+1234567890',
|
|
96
|
+
admins: ['+0987654321', '+1111111111']
|
|
97
|
+
});
|
|
98
|
+
expect(bot.isAdmin('+0987654321')).toBe(true);
|
|
99
|
+
expect(bot.isAdmin('+1111111111')).toBe(true);
|
|
100
|
+
expect(bot.isAdmin('+9999999999')).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const SignalCli_1 = require("../SignalCli");
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
jest.mock('child_process');
|
|
6
|
+
describe('SignalCli', () => {
|
|
7
|
+
let signalCli;
|
|
8
|
+
let mockProcess;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
mockProcess = {
|
|
11
|
+
stdout: {
|
|
12
|
+
on: jest.fn(),
|
|
13
|
+
once: jest.fn(),
|
|
14
|
+
},
|
|
15
|
+
stderr: {
|
|
16
|
+
on: jest.fn(),
|
|
17
|
+
},
|
|
18
|
+
stdin: {
|
|
19
|
+
write: jest.fn(),
|
|
20
|
+
},
|
|
21
|
+
on: jest.fn(),
|
|
22
|
+
once: jest.fn(),
|
|
23
|
+
kill: jest.fn(),
|
|
24
|
+
};
|
|
25
|
+
const spawnMock = child_process_1.spawn;
|
|
26
|
+
spawnMock.mockReturnValue(mockProcess);
|
|
27
|
+
signalCli = new SignalCli_1.SignalCli('signal-cli', '+1234567890');
|
|
28
|
+
});
|
|
29
|
+
it('should be defined', () => {
|
|
30
|
+
expect(SignalCli_1.SignalCli).toBeDefined();
|
|
31
|
+
});
|
|
32
|
+
it('should connect to JSON-RPC mode', async () => {
|
|
33
|
+
// Mock the stdout data event to resolve connect
|
|
34
|
+
mockProcess.stdout.once.mockImplementation((event, callback) => {
|
|
35
|
+
if (event === 'data') {
|
|
36
|
+
callback();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
await signalCli.connect();
|
|
40
|
+
// On Windows, spawn may use cmd.exe /c, so we check the arguments more flexibly
|
|
41
|
+
const spawnCalls = child_process_1.spawn.mock.calls;
|
|
42
|
+
expect(spawnCalls).toHaveLength(1);
|
|
43
|
+
const [command, args] = spawnCalls[0];
|
|
44
|
+
if (process.platform === 'win32') {
|
|
45
|
+
// On Windows, expect cmd.exe with /c and the actual command
|
|
46
|
+
expect(command).toBe('cmd.exe');
|
|
47
|
+
expect(args).toEqual(['/c', 'signal-cli', '-a', '+1234567890', 'jsonRpc']);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// On Unix-like systems, expect direct command
|
|
51
|
+
expect(command).toBe('signal-cli');
|
|
52
|
+
expect(args).toEqual(['-a', '+1234567890', 'jsonRpc']);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
it('should send JSON-RPC request for sendMessage', async () => {
|
|
56
|
+
// Mock sendJsonRpcRequest directly
|
|
57
|
+
const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
|
|
58
|
+
.mockResolvedValue({ results: [{ type: 'SUCCESS' }], timestamp: 123456 });
|
|
59
|
+
await signalCli.sendMessage('+10987654321', 'Hello, world!');
|
|
60
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('send', {
|
|
61
|
+
message: 'Hello, world!',
|
|
62
|
+
account: '+1234567890',
|
|
63
|
+
recipients: ['+10987654321'],
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
it('should disconnect properly', async () => {
|
|
67
|
+
// Connect first
|
|
68
|
+
mockProcess.stdout.once.mockImplementation((event, callback) => {
|
|
69
|
+
if (event === 'data') {
|
|
70
|
+
setTimeout(callback, 0);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
await signalCli.connect();
|
|
74
|
+
// Now disconnect
|
|
75
|
+
signalCli.disconnect();
|
|
76
|
+
expect(mockProcess.kill).toHaveBeenCalled();
|
|
77
|
+
});
|
|
78
|
+
// Test new features
|
|
79
|
+
describe('New Features', () => {
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
// Mock sendJsonRpcRequest for all tests
|
|
82
|
+
jest.spyOn(signalCli, 'sendJsonRpcRequest').mockResolvedValue({});
|
|
83
|
+
});
|
|
84
|
+
it('should remove contact', async () => {
|
|
85
|
+
const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
|
|
86
|
+
.mockResolvedValue({});
|
|
87
|
+
await signalCli.removeContact('+1234567890', { forget: true });
|
|
88
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('removeContact', {
|
|
89
|
+
account: '+1234567890',
|
|
90
|
+
recipient: '+1234567890',
|
|
91
|
+
forget: true
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
it('should get user status', async () => {
|
|
95
|
+
const mockResponse = {
|
|
96
|
+
recipients: [
|
|
97
|
+
{ number: '+1234567890', isRegistered: true, uuid: 'test-uuid' }
|
|
98
|
+
]
|
|
99
|
+
};
|
|
100
|
+
const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
|
|
101
|
+
.mockResolvedValue(mockResponse);
|
|
102
|
+
const result = await signalCli.getUserStatus(['+1234567890']);
|
|
103
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('getUserStatus', {
|
|
104
|
+
account: '+1234567890',
|
|
105
|
+
recipients: ['+1234567890']
|
|
106
|
+
});
|
|
107
|
+
expect(result).toEqual([
|
|
108
|
+
{ number: '+1234567890', isRegistered: true, uuid: 'test-uuid', username: undefined }
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
111
|
+
it('should send payment notification', async () => {
|
|
112
|
+
const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
|
|
113
|
+
.mockResolvedValue({ timestamp: 123456 });
|
|
114
|
+
await signalCli.sendPaymentNotification('+1234567890', {
|
|
115
|
+
receipt: 'base64-receipt',
|
|
116
|
+
note: 'Payment for coffee'
|
|
117
|
+
});
|
|
118
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('sendPaymentNotification', {
|
|
119
|
+
account: '+1234567890',
|
|
120
|
+
recipient: '+1234567890',
|
|
121
|
+
receipt: 'base64-receipt',
|
|
122
|
+
note: 'Payment for coffee'
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
it('should upload sticker pack', async () => {
|
|
126
|
+
const mockResponse = {
|
|
127
|
+
packId: 'pack-id',
|
|
128
|
+
packKey: 'pack-key',
|
|
129
|
+
installUrl: 'https://signal.org/stickers/pack-id'
|
|
130
|
+
};
|
|
131
|
+
const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
|
|
132
|
+
.mockResolvedValue(mockResponse);
|
|
133
|
+
const result = await signalCli.uploadStickerPack({
|
|
134
|
+
path: '/path/to/manifest.json'
|
|
135
|
+
});
|
|
136
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('uploadStickerPack', {
|
|
137
|
+
account: '+1234567890',
|
|
138
|
+
path: '/path/to/manifest.json'
|
|
139
|
+
});
|
|
140
|
+
expect(result).toEqual(mockResponse);
|
|
141
|
+
});
|
|
142
|
+
it('should submit rate limit challenge', async () => {
|
|
143
|
+
const mockResponse = {
|
|
144
|
+
success: true,
|
|
145
|
+
message: 'Challenge accepted'
|
|
146
|
+
};
|
|
147
|
+
const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
|
|
148
|
+
.mockResolvedValue(mockResponse);
|
|
149
|
+
const result = await signalCli.submitRateLimitChallenge('challenge-token', 'captcha-token');
|
|
150
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('submitRateLimitChallenge', {
|
|
151
|
+
account: '+1234567890',
|
|
152
|
+
challenge: 'challenge-token',
|
|
153
|
+
captcha: 'captcha-token'
|
|
154
|
+
});
|
|
155
|
+
expect(result).toEqual(mockResponse);
|
|
156
|
+
});
|
|
157
|
+
it('should start change number', async () => {
|
|
158
|
+
const mockResponse = {
|
|
159
|
+
session: 'change-session-id',
|
|
160
|
+
challenge: 'challenge-token'
|
|
161
|
+
};
|
|
162
|
+
const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
|
|
163
|
+
.mockResolvedValue(mockResponse);
|
|
164
|
+
const result = await signalCli.startChangeNumber('+1987654321');
|
|
165
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('startChangeNumber', {
|
|
166
|
+
account: '+1234567890',
|
|
167
|
+
number: '+1987654321',
|
|
168
|
+
voice: false
|
|
169
|
+
});
|
|
170
|
+
expect(result).toEqual({
|
|
171
|
+
session: 'change-session-id',
|
|
172
|
+
newNumber: '+1987654321',
|
|
173
|
+
challenge: 'challenge-token'
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
it('should finish change number', async () => {
|
|
177
|
+
const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
|
|
178
|
+
.mockResolvedValue({});
|
|
179
|
+
await signalCli.finishChangeNumber('123456', 'pin-code');
|
|
180
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('finishChangeNumber', {
|
|
181
|
+
account: '+1234567890',
|
|
182
|
+
code: '123456',
|
|
183
|
+
pin: 'pin-code'
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
// Test cross-platform path detection
|
|
188
|
+
describe('Cross-platform compatibility', () => {
|
|
189
|
+
it('should use correct executable path based on platform', () => {
|
|
190
|
+
// Test Windows path
|
|
191
|
+
const originalPlatform = process.platform;
|
|
192
|
+
// Mock Windows
|
|
193
|
+
Object.defineProperty(process, 'platform', { value: 'win32' });
|
|
194
|
+
const windowsSignal = new SignalCli_1.SignalCli('+1234567890');
|
|
195
|
+
expect(windowsSignal.signalCliPath).toContain('signal-cli.bat');
|
|
196
|
+
// Mock Linux
|
|
197
|
+
Object.defineProperty(process, 'platform', { value: 'linux' });
|
|
198
|
+
const linuxSignal = new SignalCli_1.SignalCli('+1234567890');
|
|
199
|
+
expect(linuxSignal.signalCliPath).toContain('signal-cli');
|
|
200
|
+
expect(linuxSignal.signalCliPath).not.toContain('.bat');
|
|
201
|
+
// Mock macOS
|
|
202
|
+
Object.defineProperty(process, 'platform', { value: 'darwin' });
|
|
203
|
+
const macSignal = new SignalCli_1.SignalCli('+1234567890');
|
|
204
|
+
expect(macSignal.signalCliPath).toContain('signal-cli');
|
|
205
|
+
expect(macSignal.signalCliPath).not.toContain('.bat');
|
|
206
|
+
// Restore original platform
|
|
207
|
+
Object.defineProperty(process, 'platform', { value: originalPlatform });
|
|
208
|
+
});
|
|
209
|
+
it('should spawn correct command based on platform', async () => {
|
|
210
|
+
const originalPlatform = process.platform;
|
|
211
|
+
// Test Windows spawning
|
|
212
|
+
Object.defineProperty(process, 'platform', { value: 'win32' });
|
|
213
|
+
const windowsSignal = new SignalCli_1.SignalCli('test-path.bat', '+1234567890');
|
|
214
|
+
mockProcess.stdout.once.mockImplementation((event, callback) => {
|
|
215
|
+
if (event === 'data')
|
|
216
|
+
callback();
|
|
217
|
+
});
|
|
218
|
+
await windowsSignal.connect();
|
|
219
|
+
const windowsCalls = child_process_1.spawn.mock.calls;
|
|
220
|
+
const lastCall = windowsCalls[windowsCalls.length - 1];
|
|
221
|
+
expect(lastCall[0]).toBe('cmd.exe');
|
|
222
|
+
expect(lastCall[1]).toEqual(['/c', '"test-path.bat"', '-a', '+1234567890', 'jsonRpc']);
|
|
223
|
+
// Test Unix spawning
|
|
224
|
+
Object.defineProperty(process, 'platform', { value: 'linux' });
|
|
225
|
+
const linuxSignal = new SignalCli_1.SignalCli('test-path', '+1234567890');
|
|
226
|
+
await linuxSignal.connect();
|
|
227
|
+
const linuxCalls = child_process_1.spawn.mock.calls;
|
|
228
|
+
const lastLinuxCall = linuxCalls[linuxCalls.length - 1];
|
|
229
|
+
expect(lastLinuxCall[0]).toBe('test-path');
|
|
230
|
+
expect(lastLinuxCall[1]).toEqual(['-a', '+1234567890', 'jsonRpc']);
|
|
231
|
+
// Restore original platform
|
|
232
|
+
Object.defineProperty(process, 'platform', { value: originalPlatform });
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.SignalBot = exports.SignalCli = void 0;
|
|
18
|
+
var SignalCli_1 = require("./SignalCli");
|
|
19
|
+
Object.defineProperty(exports, "SignalCli", { enumerable: true, get: function () { return SignalCli_1.SignalCli; } });
|
|
20
|
+
var SignalBot_1 = require("./SignalBot");
|
|
21
|
+
Object.defineProperty(exports, "SignalBot", { enumerable: true, get: function () { return SignalBot_1.SignalBot; } });
|
|
22
|
+
__exportStar(require("./interfaces"), exports);
|