natureco-cli 1.0.30 → 1.0.32
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 +1 -1
- package/src/commands/gateway-server.js +277 -23
- package/src/commands/gateway.js +146 -1
- package/src/commands/whatsapp.js +5 -44
package/package.json
CHANGED
|
@@ -2,12 +2,58 @@ const chalk = require('chalk');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const os = require('os');
|
|
5
|
-
const { spawn } = require('child_process');
|
|
5
|
+
const { spawn, execSync } = require('child_process');
|
|
6
6
|
|
|
7
7
|
const PID_FILE = path.join(os.homedir(), '.natureco', 'gateway.pid');
|
|
8
8
|
const LOG_FILE = path.join(os.homedir(), '.natureco', 'gateway.log');
|
|
9
9
|
|
|
10
|
+
// WhatsApp imports
|
|
11
|
+
let makeWASocket, useMultiFileAuthState, DisconnectReason, fetchLatestBaileysVersion, Browsers;
|
|
12
|
+
const pino = require('pino');
|
|
13
|
+
const logger = pino({ level: 'silent' });
|
|
14
|
+
|
|
15
|
+
// Lazy load Baileys
|
|
16
|
+
function loadBaileys() {
|
|
17
|
+
if (!makeWASocket) {
|
|
18
|
+
const baileys = require('@whiskeysockets/baileys');
|
|
19
|
+
makeWASocket = baileys.default;
|
|
20
|
+
useMultiFileAuthState = baileys.useMultiFileAuthState;
|
|
21
|
+
DisconnectReason = baileys.DisconnectReason;
|
|
22
|
+
fetchLatestBaileysVersion = baileys.fetchLatestBaileysVersion;
|
|
23
|
+
Browsers = baileys.Browsers;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Log helper
|
|
28
|
+
function log(module, message, color = 'white') {
|
|
29
|
+
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
|
|
30
|
+
const logLine = `[${timestamp}] [${module}] ${message}`;
|
|
31
|
+
|
|
32
|
+
// Console output with color
|
|
33
|
+
const colorFn = chalk[color] || chalk.white;
|
|
34
|
+
console.log(colorFn(logLine));
|
|
35
|
+
|
|
36
|
+
// File output (no color)
|
|
37
|
+
try {
|
|
38
|
+
const dir = path.dirname(LOG_FILE);
|
|
39
|
+
if (!fs.existsSync(dir)) {
|
|
40
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
fs.appendFileSync(LOG_FILE, logLine + '\n', 'utf-8');
|
|
43
|
+
} catch (err) {
|
|
44
|
+
// Ignore file write errors
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
10
48
|
async function gatewayServer(action) {
|
|
49
|
+
// Check if running as background worker
|
|
50
|
+
if (process.argv.includes('--gateway-worker')) {
|
|
51
|
+
process.stdin.resume();
|
|
52
|
+
process.title = 'natureco-gateway';
|
|
53
|
+
log('gateway', 'Worker process started, PID: ' + process.pid);
|
|
54
|
+
return runGatewayWorker();
|
|
55
|
+
}
|
|
56
|
+
|
|
11
57
|
if (!action || action === 'start') {
|
|
12
58
|
return startGateway();
|
|
13
59
|
}
|
|
@@ -20,12 +66,16 @@ async function gatewayServer(action) {
|
|
|
20
66
|
return statusGateway();
|
|
21
67
|
}
|
|
22
68
|
|
|
69
|
+
if (action === 'logs') {
|
|
70
|
+
return showLogs();
|
|
71
|
+
}
|
|
72
|
+
|
|
23
73
|
console.log(chalk.red('\n❌ Unknown action\n'));
|
|
24
|
-
console.log(chalk.gray('Available actions: start, stop, status\n'));
|
|
74
|
+
console.log(chalk.gray('Available actions: start, stop, status, logs\n'));
|
|
25
75
|
process.exit(1);
|
|
26
76
|
}
|
|
27
77
|
|
|
28
|
-
function startGateway() {
|
|
78
|
+
async function startGateway() {
|
|
29
79
|
// Check if already running
|
|
30
80
|
if (fs.existsSync(PID_FILE)) {
|
|
31
81
|
const pid = fs.readFileSync(PID_FILE, 'utf-8').trim();
|
|
@@ -41,31 +91,188 @@ function startGateway() {
|
|
|
41
91
|
}
|
|
42
92
|
}
|
|
43
93
|
|
|
44
|
-
console.log(chalk.green('\n🚀 Starting
|
|
94
|
+
console.log(chalk.green('\n🚀 Starting NatureCo Gateway...\n'));
|
|
45
95
|
|
|
46
|
-
//
|
|
47
|
-
const
|
|
48
|
-
|
|
96
|
+
// Ensure log directory exists
|
|
97
|
+
const dir = path.dirname(LOG_FILE);
|
|
98
|
+
if (!fs.existsSync(dir)) {
|
|
99
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Open log file for stdio redirection
|
|
103
|
+
const logFile = fs.openSync(LOG_FILE, 'a');
|
|
104
|
+
|
|
105
|
+
// Start as detached background process
|
|
106
|
+
const child = spawn(process.execPath, [__filename, '--gateway-worker'], {
|
|
49
107
|
detached: true,
|
|
50
|
-
stdio: 'ignore',
|
|
108
|
+
stdio: ['ignore', logFile, logFile],
|
|
109
|
+
windowsHide: true,
|
|
51
110
|
});
|
|
52
111
|
|
|
53
112
|
child.unref();
|
|
54
113
|
|
|
55
114
|
// Save PID
|
|
56
|
-
const dir = path.dirname(PID_FILE);
|
|
57
115
|
if (!fs.existsSync(dir)) {
|
|
58
116
|
fs.mkdirSync(dir, { recursive: true });
|
|
59
117
|
}
|
|
60
118
|
fs.writeFileSync(PID_FILE, String(child.pid), 'utf-8');
|
|
61
119
|
|
|
62
|
-
console.log(chalk.green('✅ Gateway started'));
|
|
63
|
-
console.log(chalk.cyan('Port:'), chalk.white('3847'));
|
|
120
|
+
console.log(chalk.green('✅ Gateway started in background'));
|
|
64
121
|
console.log(chalk.cyan('PID:'), chalk.white(child.pid));
|
|
65
|
-
console.log(chalk.
|
|
66
|
-
console.log(chalk.
|
|
67
|
-
console.log(chalk.gray('
|
|
68
|
-
console.log(chalk.gray('
|
|
122
|
+
console.log(chalk.cyan('Logs:'), chalk.white(LOG_FILE));
|
|
123
|
+
console.log(chalk.gray('\nView logs: natureco gateway logs'));
|
|
124
|
+
console.log(chalk.gray('Check status: natureco gateway status'));
|
|
125
|
+
console.log(chalk.gray('Stop gateway: natureco gateway stop\n'));
|
|
126
|
+
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function runGatewayWorker() {
|
|
131
|
+
// This runs in the background
|
|
132
|
+
log('gateway', 'Starting NatureCo Gateway...', 'green');
|
|
133
|
+
|
|
134
|
+
// Load config
|
|
135
|
+
const { getConfig } = require('../utils/config');
|
|
136
|
+
const config = getConfig();
|
|
137
|
+
|
|
138
|
+
if (!config || !config.apiKey) {
|
|
139
|
+
log('gateway', 'No API key found. Run "natureco login" first.', 'red');
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Start WhatsApp if configured
|
|
144
|
+
if (config.whatsappConnected && config.whatsappBotId) {
|
|
145
|
+
const sessionDir = path.join(os.homedir(), '.natureco', 'whatsapp-sessions', config.whatsappBotId);
|
|
146
|
+
|
|
147
|
+
if (fs.existsSync(sessionDir)) {
|
|
148
|
+
const phone = config.whatsappPhone?.split(':')[0].replace('@s.whatsapp.net', '') || 'unknown';
|
|
149
|
+
log('whatsapp', `starting provider (+${phone})`, 'cyan');
|
|
150
|
+
startWhatsAppProvider(sessionDir, config);
|
|
151
|
+
} else {
|
|
152
|
+
log('whatsapp', 'session not found, skipping', 'yellow');
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
log('whatsapp', 'not configured, skipping', 'gray');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Health check every 60 seconds
|
|
159
|
+
setInterval(() => {
|
|
160
|
+
log('gateway', 'health check: OK', 'gray');
|
|
161
|
+
}, 60000);
|
|
162
|
+
|
|
163
|
+
log('gateway', 'Gateway running in background', 'green');
|
|
164
|
+
|
|
165
|
+
// Handle shutdown
|
|
166
|
+
process.on('SIGINT', () => {
|
|
167
|
+
log('gateway', 'Shutting down...', 'yellow');
|
|
168
|
+
if (fs.existsSync(PID_FILE)) {
|
|
169
|
+
fs.unlinkSync(PID_FILE);
|
|
170
|
+
}
|
|
171
|
+
process.exit(0);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
process.on('SIGTERM', () => {
|
|
175
|
+
log('gateway', 'Shutting down...', 'yellow');
|
|
176
|
+
if (fs.existsSync(PID_FILE)) {
|
|
177
|
+
fs.unlinkSync(PID_FILE);
|
|
178
|
+
}
|
|
179
|
+
process.exit(0);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function startWhatsAppProvider(sessionDir, config) {
|
|
184
|
+
try {
|
|
185
|
+
loadBaileys();
|
|
186
|
+
|
|
187
|
+
const { state, saveCreds } = await useMultiFileAuthState(sessionDir);
|
|
188
|
+
const { version } = await fetchLatestBaileysVersion();
|
|
189
|
+
|
|
190
|
+
const sock = makeWASocket({
|
|
191
|
+
version,
|
|
192
|
+
auth: state,
|
|
193
|
+
printQRInTerminal: false,
|
|
194
|
+
logger: logger,
|
|
195
|
+
browser: Browsers.ubuntu('Chrome'),
|
|
196
|
+
connectTimeoutMs: 60000,
|
|
197
|
+
defaultQueryTimeoutMs: 60000,
|
|
198
|
+
keepAliveIntervalMs: 10000,
|
|
199
|
+
retryRequestDelayMs: 2000,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
sock.ev.on('connection.update', async (update) => {
|
|
203
|
+
const { connection, lastDisconnect } = update;
|
|
204
|
+
|
|
205
|
+
if (connection === 'close') {
|
|
206
|
+
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
|
207
|
+
|
|
208
|
+
if (statusCode === 515 || statusCode === 408) {
|
|
209
|
+
log('whatsapp', 'connection lost, reconnecting in 10s...', 'yellow');
|
|
210
|
+
setTimeout(() => startWhatsAppProvider(sessionDir, config), 10000);
|
|
211
|
+
return;
|
|
212
|
+
} else if (statusCode === 401) {
|
|
213
|
+
log('whatsapp', 'session expired, please reconnect', 'red');
|
|
214
|
+
} else {
|
|
215
|
+
log('whatsapp', `disconnected (code: ${statusCode}), reconnecting in 10s...`, 'yellow');
|
|
216
|
+
setTimeout(() => startWhatsAppProvider(sessionDir, config), 10000);
|
|
217
|
+
}
|
|
218
|
+
} else if (connection === 'open') {
|
|
219
|
+
const phone = sock.user?.id?.split(':')[0].replace('@s.whatsapp.net', '') || 'unknown';
|
|
220
|
+
log('whatsapp', `Listening for inbound messages.`, 'cyan');
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
225
|
+
for (const msg of messages) {
|
|
226
|
+
if (msg.key.fromMe) continue;
|
|
227
|
+
|
|
228
|
+
const sender = msg.key.remoteJid?.split('@')[0].split(':')[0];
|
|
229
|
+
const allowedNumbers = config.whatsappAllowedNumbers || [];
|
|
230
|
+
|
|
231
|
+
// Access control
|
|
232
|
+
if (allowedNumbers.length > 0 && !allowedNumbers.includes(sender)) {
|
|
233
|
+
log('whatsapp', `blocked message from +${sender} (not in allowed list)`, 'yellow');
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const messageText = msg.message?.conversation ||
|
|
238
|
+
msg.message?.extendedTextMessage?.text ||
|
|
239
|
+
'';
|
|
240
|
+
|
|
241
|
+
if (messageText) {
|
|
242
|
+
const ownNumber = sock.user?.id?.split(':')[0].replace('@s.whatsapp.net', '') || 'unknown';
|
|
243
|
+
log('whatsapp', `Inbound message +${sender} -> +${ownNumber}`, 'cyan');
|
|
244
|
+
log('whatsapp', `Message: "${messageText.substring(0, 100)}${messageText.length > 100 ? '...' : ''}"`, 'gray');
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
const { sendMessage } = require('../utils/api');
|
|
248
|
+
log('whatsapp', 'Sending to NatureCo API...', 'cyan');
|
|
249
|
+
|
|
250
|
+
const response = await sendMessage(config.apiKey, config.whatsappBotId, messageText, null, '');
|
|
251
|
+
const reply = response?.reply || response?.message || '';
|
|
252
|
+
|
|
253
|
+
if (reply) {
|
|
254
|
+
log('whatsapp', `API response: "${reply.substring(0, 100)}${reply.length > 100 ? '...' : ''}"`, 'gray');
|
|
255
|
+
log('whatsapp', 'Sending reply...', 'cyan');
|
|
256
|
+
|
|
257
|
+
await sock.sendMessage(msg.key.remoteJid, { text: reply });
|
|
258
|
+
log('whatsapp', `Reply sent (${reply.length} chars)`, 'green');
|
|
259
|
+
} else {
|
|
260
|
+
log('whatsapp', 'No reply from API', 'yellow');
|
|
261
|
+
}
|
|
262
|
+
} catch (err) {
|
|
263
|
+
log('whatsapp', `API error: ${err.message}`, 'red');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
sock.ev.on('creds.update', saveCreds);
|
|
270
|
+
|
|
271
|
+
} catch (err) {
|
|
272
|
+
log('whatsapp', `Failed to start: ${err.message}`, 'red');
|
|
273
|
+
log('whatsapp', 'Retrying in 10s...', 'yellow');
|
|
274
|
+
setTimeout(() => startWhatsAppProvider(sessionDir, config), 10000);
|
|
275
|
+
}
|
|
69
276
|
}
|
|
70
277
|
|
|
71
278
|
function stopGateway() {
|
|
@@ -74,14 +281,52 @@ function stopGateway() {
|
|
|
74
281
|
return;
|
|
75
282
|
}
|
|
76
283
|
|
|
77
|
-
const pid = fs.readFileSync(PID_FILE, 'utf-8').trim();
|
|
284
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim());
|
|
78
285
|
|
|
79
286
|
try {
|
|
287
|
+
// Try graceful shutdown first
|
|
80
288
|
process.kill(pid, 'SIGTERM');
|
|
81
|
-
|
|
82
|
-
|
|
289
|
+
|
|
290
|
+
// Wait a bit and check if process is still running
|
|
291
|
+
setTimeout(() => {
|
|
292
|
+
try {
|
|
293
|
+
process.kill(pid, 0);
|
|
294
|
+
// Process still running, force kill
|
|
295
|
+
if (process.platform === 'win32') {
|
|
296
|
+
try {
|
|
297
|
+
execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' });
|
|
298
|
+
console.log(chalk.green('\n✅ Gateway stopped (force killed)\n'));
|
|
299
|
+
} catch (err) {
|
|
300
|
+
console.log(chalk.red(`\n❌ Failed to stop gateway: ${err.message}\n`));
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
process.kill(pid, 'SIGKILL');
|
|
304
|
+
console.log(chalk.green('\n✅ Gateway stopped (force killed)\n'));
|
|
305
|
+
}
|
|
306
|
+
} catch {
|
|
307
|
+
// Process already stopped
|
|
308
|
+
console.log(chalk.green('\n✅ Gateway stopped\n'));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Remove PID file
|
|
312
|
+
if (fs.existsSync(PID_FILE)) {
|
|
313
|
+
fs.unlinkSync(PID_FILE);
|
|
314
|
+
}
|
|
315
|
+
}, 1000);
|
|
316
|
+
|
|
83
317
|
} catch (err) {
|
|
84
|
-
|
|
318
|
+
// Process not found, try force kill on Windows
|
|
319
|
+
if (process.platform === 'win32') {
|
|
320
|
+
try {
|
|
321
|
+
execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' });
|
|
322
|
+
console.log(chalk.green('\n✅ Gateway stopped\n'));
|
|
323
|
+
} catch {
|
|
324
|
+
console.log(chalk.yellow('\n⚠️ Gateway process not found\n'));
|
|
325
|
+
}
|
|
326
|
+
} else {
|
|
327
|
+
console.log(chalk.yellow('\n⚠️ Gateway process not found\n'));
|
|
328
|
+
}
|
|
329
|
+
|
|
85
330
|
// Remove stale PID file
|
|
86
331
|
if (fs.existsSync(PID_FILE)) {
|
|
87
332
|
fs.unlinkSync(PID_FILE);
|
|
@@ -101,13 +346,12 @@ function statusGateway() {
|
|
|
101
346
|
process.kill(pid, 0);
|
|
102
347
|
console.log(chalk.green('\n✅ Gateway running\n'));
|
|
103
348
|
console.log(chalk.cyan('PID:'), chalk.white(pid));
|
|
104
|
-
console.log(chalk.cyan('
|
|
105
|
-
console.log(chalk.cyan('WebSocket:'), chalk.white('ws://localhost:3847'));
|
|
349
|
+
console.log(chalk.cyan('Logs:'), chalk.white(LOG_FILE));
|
|
106
350
|
|
|
107
351
|
// Show log tail if exists
|
|
108
352
|
if (fs.existsSync(LOG_FILE)) {
|
|
109
|
-
const logs = fs.readFileSync(LOG_FILE, 'utf-8').split('\n').slice(-
|
|
110
|
-
console.log(chalk.gray('\nRecent logs:'));
|
|
353
|
+
const logs = fs.readFileSync(LOG_FILE, 'utf-8').split('\n').filter(l => l.trim()).slice(-10).join('\n');
|
|
354
|
+
console.log(chalk.gray('\nRecent logs (last 10):'));
|
|
111
355
|
console.log(chalk.white(logs));
|
|
112
356
|
}
|
|
113
357
|
console.log('');
|
|
@@ -117,4 +361,14 @@ function statusGateway() {
|
|
|
117
361
|
}
|
|
118
362
|
}
|
|
119
363
|
|
|
364
|
+
function showLogs() {
|
|
365
|
+
if (!fs.existsSync(LOG_FILE)) {
|
|
366
|
+
console.log(chalk.gray('\n⚠️ No logs found\n'));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const logs = fs.readFileSync(LOG_FILE, 'utf-8');
|
|
371
|
+
console.log(logs);
|
|
372
|
+
}
|
|
373
|
+
|
|
120
374
|
module.exports = gatewayServer;
|
package/src/commands/gateway.js
CHANGED
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const boxen = require('boxen');
|
|
3
3
|
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
4
6
|
const packageJson = require('../../package.json');
|
|
5
7
|
const { getConfig, CONFIG_FILE } = require('../utils/config');
|
|
6
8
|
const { getSkills } = require('../utils/skills');
|
|
7
9
|
const { getMcpServers } = require('../utils/mcp');
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
// WhatsApp imports
|
|
12
|
+
let makeWASocket, useMultiFileAuthState, DisconnectReason, fetchLatestBaileysVersion, Browsers;
|
|
13
|
+
const pino = require('pino');
|
|
14
|
+
const logger = pino({ level: 'silent' });
|
|
15
|
+
|
|
16
|
+
// Lazy load Baileys
|
|
17
|
+
function loadBaileys() {
|
|
18
|
+
if (!makeWASocket) {
|
|
19
|
+
const baileys = require('@whiskeysockets/baileys');
|
|
20
|
+
makeWASocket = baileys.default;
|
|
21
|
+
useMultiFileAuthState = baileys.useMultiFileAuthState;
|
|
22
|
+
DisconnectReason = baileys.DisconnectReason;
|
|
23
|
+
fetchLatestBaileysVersion = baileys.fetchLatestBaileysVersion;
|
|
24
|
+
Browsers = baileys.Browsers;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function gateway(action) {
|
|
29
|
+
if (action === 'start') {
|
|
30
|
+
return startGateway();
|
|
31
|
+
}
|
|
32
|
+
|
|
10
33
|
// Config kontrolü - yoksa veya apiKey yoksa setup çalıştır
|
|
11
34
|
if (!fs.existsSync(CONFIG_FILE)) {
|
|
12
35
|
const setup = require('./setup');
|
|
@@ -98,6 +121,9 @@ async function gateway() {
|
|
|
98
121
|
console.log(chalk.yellow(' natureco config'));
|
|
99
122
|
console.log(chalk.gray(' Ayarları görüntüle ve düzenle\n'));
|
|
100
123
|
|
|
124
|
+
console.log(chalk.yellow(' natureco gateway start'));
|
|
125
|
+
console.log(chalk.gray(' Gateway\'i başlat (WhatsApp dahil)\n'));
|
|
126
|
+
|
|
101
127
|
console.log(chalk.yellow(' natureco help'));
|
|
102
128
|
console.log(chalk.gray(' Tüm komutları ve örnekleri göster\n'));
|
|
103
129
|
|
|
@@ -110,4 +136,123 @@ async function gateway() {
|
|
|
110
136
|
console.log('');
|
|
111
137
|
}
|
|
112
138
|
|
|
139
|
+
async function startGateway() {
|
|
140
|
+
const config = getConfig();
|
|
141
|
+
|
|
142
|
+
if (!config || !config.apiKey) {
|
|
143
|
+
console.log(chalk.red('\n❌ Not logged in. Run "natureco login" first.\n'));
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log(chalk.green.bold('\n🚀 NatureCo Gateway Starting...\n'));
|
|
148
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
149
|
+
console.log('');
|
|
150
|
+
|
|
151
|
+
// WhatsApp provider başlat
|
|
152
|
+
if (config.whatsappConnected && config.whatsappBotId) {
|
|
153
|
+
const sessionDir = path.join(os.homedir(), '.natureco', 'whatsapp-sessions', config.whatsappBotId);
|
|
154
|
+
|
|
155
|
+
if (fs.existsSync(sessionDir)) {
|
|
156
|
+
console.log(chalk.cyan('[whatsapp]'), chalk.white(`starting provider (${config.whatsappPhone || 'unknown'})`));
|
|
157
|
+
await startWhatsAppProvider(sessionDir, config);
|
|
158
|
+
} else {
|
|
159
|
+
console.log(chalk.yellow('[whatsapp]'), chalk.gray('session not found, skipping'));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
console.log('');
|
|
164
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
165
|
+
console.log(chalk.green('\n✅ Gateway running. Press Ctrl+C to stop.\n'));
|
|
166
|
+
|
|
167
|
+
// Keep process alive
|
|
168
|
+
process.on('SIGINT', () => {
|
|
169
|
+
console.log(chalk.yellow('\n\n⚠️ Gateway stopped\n'));
|
|
170
|
+
process.exit(0);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function startWhatsAppProvider(sessionDir, config) {
|
|
175
|
+
try {
|
|
176
|
+
loadBaileys();
|
|
177
|
+
|
|
178
|
+
const { state, saveCreds } = await useMultiFileAuthState(sessionDir);
|
|
179
|
+
const { version } = await fetchLatestBaileysVersion();
|
|
180
|
+
|
|
181
|
+
const sock = makeWASocket({
|
|
182
|
+
version,
|
|
183
|
+
auth: state,
|
|
184
|
+
printQRInTerminal: false,
|
|
185
|
+
logger: logger,
|
|
186
|
+
browser: Browsers.ubuntu('Chrome'),
|
|
187
|
+
connectTimeoutMs: 60000,
|
|
188
|
+
defaultQueryTimeoutMs: 60000,
|
|
189
|
+
keepAliveIntervalMs: 10000,
|
|
190
|
+
retryRequestDelayMs: 2000,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
sock.ev.on('connection.update', async (update) => {
|
|
194
|
+
const { connection, lastDisconnect } = update;
|
|
195
|
+
|
|
196
|
+
if (connection === 'close') {
|
|
197
|
+
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
|
198
|
+
|
|
199
|
+
if (statusCode === 515 || statusCode === 408) {
|
|
200
|
+
console.log(chalk.yellow('[whatsapp]'), chalk.gray('reconnecting...'));
|
|
201
|
+
setTimeout(() => startWhatsAppProvider(sessionDir, config), 2000);
|
|
202
|
+
return;
|
|
203
|
+
} else if (statusCode === 401) {
|
|
204
|
+
console.log(chalk.red('[whatsapp]'), chalk.gray('session expired'));
|
|
205
|
+
} else {
|
|
206
|
+
console.log(chalk.red('[whatsapp]'), chalk.gray(`disconnected (${statusCode})`));
|
|
207
|
+
}
|
|
208
|
+
} else if (connection === 'open') {
|
|
209
|
+
const phone = sock.user?.id?.split(':')[0].replace('@s.whatsapp.net', '') || 'unknown';
|
|
210
|
+
console.log(chalk.cyan('[whatsapp]'), chalk.white(`Listening for inbound messages.`));
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
215
|
+
for (const msg of messages) {
|
|
216
|
+
if (msg.key.fromMe) continue;
|
|
217
|
+
|
|
218
|
+
const sender = msg.key.remoteJid?.split('@')[0].split(':')[0];
|
|
219
|
+
const allowedNumbers = config.whatsappAllowedNumbers || [];
|
|
220
|
+
|
|
221
|
+
if (allowedNumbers.length > 0 && !allowedNumbers.includes(sender)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const messageText = msg.message?.conversation ||
|
|
226
|
+
msg.message?.extendedTextMessage?.text ||
|
|
227
|
+
'';
|
|
228
|
+
|
|
229
|
+
if (messageText) {
|
|
230
|
+
const ownNumber = sock.user?.id?.split(':')[0].replace('@s.whatsapp.net', '') || 'unknown';
|
|
231
|
+
console.log(chalk.cyan('[whatsapp]'), chalk.white(`Inbound message +${sender} -> +${ownNumber}`));
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
const { sendMessage } = require('../utils/api');
|
|
235
|
+
console.log(chalk.cyan('[whatsapp]'), chalk.gray('Sending reply...'));
|
|
236
|
+
|
|
237
|
+
const response = await sendMessage(config.apiKey, config.whatsappBotId, messageText, null, '');
|
|
238
|
+
const reply = response?.reply || response?.message || '';
|
|
239
|
+
|
|
240
|
+
if (reply) {
|
|
241
|
+
await sock.sendMessage(msg.key.remoteJid, { text: reply });
|
|
242
|
+
console.log(chalk.cyan('[whatsapp]'), chalk.green(`Reply sent (${reply.length} chars)`));
|
|
243
|
+
}
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.log(chalk.red('[whatsapp]'), chalk.gray(`error: ${err.message}`));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
sock.ev.on('creds.update', saveCreds);
|
|
252
|
+
|
|
253
|
+
} catch (err) {
|
|
254
|
+
console.log(chalk.red('[whatsapp]'), chalk.gray(`failed to start: ${err.message}`));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
113
258
|
module.exports = gateway;
|
package/src/commands/whatsapp.js
CHANGED
|
@@ -114,7 +114,7 @@ async function connectWhatsApp() {
|
|
|
114
114
|
.map(n => n.trim())
|
|
115
115
|
.filter(n => n.length > 0);
|
|
116
116
|
|
|
117
|
-
// Start connection
|
|
117
|
+
// Start connection (only for QR code)
|
|
118
118
|
await startWhatsAppConnection(sessionDir, botId, selectedBot, config, allowedNumbersList);
|
|
119
119
|
}
|
|
120
120
|
|
|
@@ -209,56 +209,17 @@ async function startWhatsAppConnection(sessionDir, botId, selectedBot, config, a
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
console.log(chalk.green('\n✅ Kurulum tamamlandı!\n'));
|
|
212
|
-
console.log(chalk.
|
|
212
|
+
console.log(chalk.yellow('Gateway ile başlatmak için:'), chalk.cyan('natureco gateway start'));
|
|
213
|
+
console.log(chalk.gray('Gateway, WhatsApp\'ı otomatik olarak başlatacak.\n'));
|
|
213
214
|
|
|
214
|
-
//
|
|
215
|
+
// Exit after setup
|
|
215
216
|
setTimeout(() => {
|
|
216
217
|
process.exit(0);
|
|
217
218
|
}, 2000);
|
|
218
219
|
}
|
|
219
220
|
});
|
|
220
221
|
|
|
221
|
-
// Message handler
|
|
222
|
-
sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
223
|
-
for (const msg of messages) {
|
|
224
|
-
if (msg.key.fromMe) continue; // kendi mesajları yoksay
|
|
225
|
-
|
|
226
|
-
const sender = msg.key.remoteJid?.split('@')[0].split(':')[0];
|
|
227
|
-
const allowedNumbers = config.whatsappAllowedNumbers || [];
|
|
228
|
-
|
|
229
|
-
// İzin listesi doluysa kontrol et
|
|
230
|
-
if (allowedNumbers.length > 0 && !allowedNumbers.includes(sender)) {
|
|
231
|
-
console.log(chalk.yellow(`⚠️ İzinsiz numara: ${sender}`));
|
|
232
|
-
continue; // izinsiz numara, yoksay
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Mesaj içeriğini al
|
|
236
|
-
const messageText = msg.message?.conversation ||
|
|
237
|
-
msg.message?.extendedTextMessage?.text ||
|
|
238
|
-
'';
|
|
239
|
-
|
|
240
|
-
if (messageText) {
|
|
241
|
-
console.log('📨 Mesaj alındı:', sender, messageText);
|
|
242
|
-
console.log(chalk.cyan(`\n📱 ${sender}:`), chalk.white(messageText));
|
|
243
|
-
|
|
244
|
-
try {
|
|
245
|
-
console.log('📡 API isteği gönderiliyor...');
|
|
246
|
-
const response = await sendMessage(config.apiKey, config.defaultBotId, messageText, null, '');
|
|
247
|
-
console.log('📩 API yanıtı:', JSON.stringify(response));
|
|
248
|
-
|
|
249
|
-
const reply = response?.reply || response?.message || '';
|
|
250
|
-
|
|
251
|
-
if (reply) {
|
|
252
|
-
console.log('📤 WhatsApp yanıtı gönderiliyor:', reply);
|
|
253
|
-
await sock.sendMessage(msg.key.remoteJid, { text: reply });
|
|
254
|
-
console.log(chalk.green(`✅ Cevap gönderildi: ${reply.substring(0, 50)}...`));
|
|
255
|
-
}
|
|
256
|
-
} catch (err) {
|
|
257
|
-
console.log(chalk.red('❌ API hatası:', err.message));
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
});
|
|
222
|
+
// Message handler removed - gateway handles this now
|
|
262
223
|
|
|
263
224
|
// Save credentials on update
|
|
264
225
|
sock.ev.on('creds.update', saveCreds);
|