natureco-cli 2.8.3 → 2.9.1
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/bin/natureco.js +9 -4
- package/package.json +2 -2
- package/src/commands/cron.js +107 -339
- package/src/commands/dashboard.js +2 -2
- package/src/commands/gateway-server.js +85 -1
package/bin/natureco.js
CHANGED
|
@@ -106,11 +106,16 @@ program
|
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
program
|
|
109
|
-
.command('cron
|
|
110
|
-
.
|
|
111
|
-
.
|
|
109
|
+
.command('cron <action>')
|
|
110
|
+
.option('--name <name>', 'Cron job adı')
|
|
111
|
+
.option('--schedule <schedule>', 'Cron schedule (örnek: "0 9 * * *")')
|
|
112
|
+
.option('--action <action>', 'Aksiyon: whatsapp veya telegram')
|
|
113
|
+
.option('--target <target>', 'Hedef numara veya chat ID')
|
|
114
|
+
.option('--prompt <prompt>', 'AI\'a gönderilecek prompt')
|
|
115
|
+
.description('Cron job yönetimi (add|list|remove)')
|
|
116
|
+
.action((action, options) => {
|
|
112
117
|
const cronCmd = require('../src/commands/cron');
|
|
113
|
-
cronCmd(action,
|
|
118
|
+
cronCmd(action, options);
|
|
114
119
|
});
|
|
115
120
|
|
|
116
121
|
program
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.1",
|
|
4
4
|
"description": "NatureCo AI Bot Terminal Interface",
|
|
5
5
|
"main": "bin/natureco.js",
|
|
6
6
|
"bin": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"conf": "^10.2.0",
|
|
32
32
|
"eventsource": "^2.0.2",
|
|
33
33
|
"inquirer": "^8.2.7",
|
|
34
|
-
"node-cron": "^
|
|
34
|
+
"node-cron": "^3.0.3",
|
|
35
35
|
"node-telegram-bot-api": "^0.65.1",
|
|
36
36
|
"ora": "^5.4.1",
|
|
37
37
|
"pino": "^8.21.0",
|
package/src/commands/cron.js
CHANGED
|
@@ -1,383 +1,151 @@
|
|
|
1
|
-
const inquirer = require('inquirer');
|
|
2
1
|
const chalk = require('chalk');
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const {
|
|
7
|
-
loadCronJobs,
|
|
8
|
-
addCronJob,
|
|
9
|
-
removeCronJob,
|
|
10
|
-
updateCronJob,
|
|
11
|
-
getCronJob,
|
|
12
|
-
parseCronSchedule,
|
|
13
|
-
validateCronExpression,
|
|
14
|
-
logCronOutput,
|
|
15
|
-
getCronLog,
|
|
16
|
-
} = require('../utils/cron');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
17
5
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return addCronJobInteractive();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (action === 'remove') {
|
|
28
|
-
const id = args[0];
|
|
29
|
-
if (!id) {
|
|
30
|
-
console.log(chalk.red('\n❌ Job ID required\n'));
|
|
31
|
-
console.log(chalk.gray('Usage: natureco cron remove <id>\n'));
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
return removeCronJobCommand(id);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (action === 'run') {
|
|
38
|
-
const id = args[0];
|
|
39
|
-
if (!id) {
|
|
40
|
-
console.log(chalk.red('\n❌ Job ID required\n'));
|
|
41
|
-
console.log(chalk.gray('Usage: natureco cron run <id>\n'));
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
return runCronJobNow(id);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (action === 'enable') {
|
|
48
|
-
const id = args[0];
|
|
49
|
-
if (!id) {
|
|
50
|
-
console.log(chalk.red('\n❌ Job ID required\n'));
|
|
51
|
-
console.log(chalk.gray('Usage: natureco cron enable <id>\n'));
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
return toggleCronJob(id, true);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (action === 'disable') {
|
|
58
|
-
const id = args[0];
|
|
59
|
-
if (!id) {
|
|
60
|
-
console.log(chalk.red('\n❌ Job ID required\n'));
|
|
61
|
-
console.log(chalk.gray('Usage: natureco cron disable <id>\n'));
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
|
-
return toggleCronJob(id, false);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (action === 'start') {
|
|
68
|
-
return startCronDaemon();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (action === 'logs') {
|
|
72
|
-
const id = args[0];
|
|
73
|
-
if (!id) {
|
|
74
|
-
console.log(chalk.red('\n❌ Job ID required\n'));
|
|
75
|
-
console.log(chalk.gray('Usage: natureco cron logs <id>\n'));
|
|
76
|
-
process.exit(1);
|
|
6
|
+
const CRONS_FILE = path.join(os.homedir(), '.natureco', 'crons.json');
|
|
7
|
+
|
|
8
|
+
function loadCrons() {
|
|
9
|
+
try {
|
|
10
|
+
if (!fs.existsSync(CRONS_FILE)) {
|
|
11
|
+
return [];
|
|
77
12
|
}
|
|
78
|
-
return
|
|
13
|
+
return JSON.parse(fs.readFileSync(CRONS_FILE, 'utf-8'));
|
|
14
|
+
} catch {
|
|
15
|
+
return [];
|
|
79
16
|
}
|
|
80
|
-
|
|
81
|
-
console.log(chalk.red(`\n❌ Unknown action: ${action}\n`));
|
|
82
|
-
console.log(chalk.gray('Available actions: list, add, remove, run, enable, disable, start, logs\n'));
|
|
83
|
-
process.exit(1);
|
|
84
17
|
}
|
|
85
18
|
|
|
86
|
-
function
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
console.log(chalk.gray('\nNo cron jobs found.\n'));
|
|
91
|
-
console.log(chalk.gray('Create one with: natureco cron add\n'));
|
|
92
|
-
return;
|
|
19
|
+
function saveCrons(crons) {
|
|
20
|
+
const dir = path.dirname(CRONS_FILE);
|
|
21
|
+
if (!fs.existsSync(dir)) {
|
|
22
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
93
23
|
}
|
|
94
|
-
|
|
95
|
-
console.log(chalk.yellow('\nCron Jobs:\n'));
|
|
96
|
-
|
|
97
|
-
jobs.forEach(job => {
|
|
98
|
-
const status = job.enabled ? chalk.green('✓ enabled') : chalk.gray('✗ disabled');
|
|
99
|
-
console.log(` ${chalk.cyan(job.id)} ${status}`);
|
|
100
|
-
console.log(chalk.white(` ${job.name}`));
|
|
101
|
-
console.log(chalk.gray(` Bot: ${job.botName} | Schedule: ${job.schedule}`));
|
|
102
|
-
console.log(chalk.gray(` Output: ${job.outputType === 'file' ? 'log file' : 'terminal'}`));
|
|
103
|
-
console.log('');
|
|
104
|
-
});
|
|
24
|
+
fs.writeFileSync(CRONS_FILE, JSON.stringify(crons, null, 2), 'utf-8');
|
|
105
25
|
}
|
|
106
26
|
|
|
107
|
-
async function
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
console.log(chalk.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
botList = await getBots(apiKey);
|
|
118
|
-
} catch (err) {
|
|
119
|
-
console.log(chalk.red(`\n❌ Error: ${err.message}\n`));
|
|
27
|
+
async function cron(action, options) {
|
|
28
|
+
if (!action || !['add', 'list', 'remove'].includes(action)) {
|
|
29
|
+
console.log(chalk.red('\n❌ Geçersiz aksiyon\n'));
|
|
30
|
+
console.log(chalk.gray('Kullanım:'));
|
|
31
|
+
console.log(chalk.cyan(' natureco cron add --name <name> --schedule <cron> --action <channel> --target <target> --prompt <prompt>'));
|
|
32
|
+
console.log(chalk.cyan(' natureco cron list'));
|
|
33
|
+
console.log(chalk.cyan(' natureco cron remove --name <name>'));
|
|
34
|
+
console.log(chalk.gray('\nÖrnek:'));
|
|
35
|
+
console.log(chalk.cyan(' natureco cron add --name "bitcoin-fiyat" --schedule "0 9 * * *" --action "whatsapp" --target "+905422842631" --prompt "Bugünkü Bitcoin fiyatını öğren ve kısaca bildir"\n'));
|
|
120
36
|
process.exit(1);
|
|
121
37
|
}
|
|
122
38
|
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const answers = await inquirer.prompt([
|
|
131
|
-
{
|
|
132
|
-
type: 'input',
|
|
133
|
-
name: 'name',
|
|
134
|
-
message: 'Job name:',
|
|
135
|
-
validate: (val) => val.trim() !== '' || 'Name cannot be empty',
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
type: 'input',
|
|
139
|
-
name: 'message',
|
|
140
|
-
message: 'Message to send to bot:',
|
|
141
|
-
validate: (val) => val.trim() !== '' || 'Message cannot be empty',
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
type: 'list',
|
|
145
|
-
name: 'botId',
|
|
146
|
-
message: 'Select bot:',
|
|
147
|
-
choices: botList.bots.map(b => ({ name: b.name, value: b.id })),
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
type: 'list',
|
|
151
|
-
name: 'scheduleType',
|
|
152
|
-
message: 'Schedule type:',
|
|
153
|
-
choices: [
|
|
154
|
-
{ name: 'Daily at specific time', value: 'daily' },
|
|
155
|
-
{ name: 'Every X hours', value: 'hours' },
|
|
156
|
-
{ name: 'Custom cron expression', value: 'custom' },
|
|
157
|
-
],
|
|
158
|
-
},
|
|
159
|
-
]);
|
|
160
|
-
|
|
161
|
-
let schedule;
|
|
162
|
-
|
|
163
|
-
if (answers.scheduleType === 'daily') {
|
|
164
|
-
const timeAnswer = await inquirer.prompt([
|
|
165
|
-
{
|
|
166
|
-
type: 'input',
|
|
167
|
-
name: 'time',
|
|
168
|
-
message: 'Time (HH:MM):',
|
|
169
|
-
default: '09:00',
|
|
170
|
-
validate: (val) => {
|
|
171
|
-
const match = val.match(/^(\d{1,2}):(\d{2})$/);
|
|
172
|
-
if (!match) return 'Invalid time format. Use HH:MM';
|
|
173
|
-
const hour = parseInt(match[1]);
|
|
174
|
-
const minute = parseInt(match[2]);
|
|
175
|
-
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
|
|
176
|
-
return 'Invalid time';
|
|
177
|
-
}
|
|
178
|
-
return true;
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
]);
|
|
182
|
-
schedule = `daily at ${timeAnswer.time}`;
|
|
183
|
-
} else if (answers.scheduleType === 'hours') {
|
|
184
|
-
const hoursAnswer = await inquirer.prompt([
|
|
185
|
-
{
|
|
186
|
-
type: 'input',
|
|
187
|
-
name: 'hours',
|
|
188
|
-
message: 'Every X hours:',
|
|
189
|
-
default: '1',
|
|
190
|
-
validate: (val) => {
|
|
191
|
-
const num = parseInt(val);
|
|
192
|
-
if (isNaN(num) || num < 1 || num > 24) {
|
|
193
|
-
return 'Enter a number between 1 and 24';
|
|
194
|
-
}
|
|
195
|
-
return true;
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
]);
|
|
199
|
-
schedule = `every ${hoursAnswer.hours} hours`;
|
|
200
|
-
} else {
|
|
201
|
-
const cronAnswer = await inquirer.prompt([
|
|
202
|
-
{
|
|
203
|
-
type: 'input',
|
|
204
|
-
name: 'cron',
|
|
205
|
-
message: 'Cron expression:',
|
|
206
|
-
default: '0 9 * * *',
|
|
207
|
-
validate: (val) => {
|
|
208
|
-
const parsed = parseCronSchedule(val);
|
|
209
|
-
if (!validateCronExpression(parsed)) {
|
|
210
|
-
return 'Invalid cron expression';
|
|
211
|
-
}
|
|
212
|
-
return true;
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
]);
|
|
216
|
-
schedule = cronAnswer.cron;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const outputAnswer = await inquirer.prompt([
|
|
220
|
-
{
|
|
221
|
-
type: 'list',
|
|
222
|
-
name: 'outputType',
|
|
223
|
-
message: 'Output destination:',
|
|
224
|
-
choices: [
|
|
225
|
-
{ name: 'Save to log file', value: 'file' },
|
|
226
|
-
{ name: 'Print to terminal', value: 'terminal' },
|
|
227
|
-
],
|
|
228
|
-
default: 'file',
|
|
229
|
-
},
|
|
230
|
-
]);
|
|
231
|
-
|
|
232
|
-
const bot = botList.bots.find(b => b.id === answers.botId);
|
|
233
|
-
|
|
234
|
-
const job = addCronJob({
|
|
235
|
-
name: answers.name,
|
|
236
|
-
message: answers.message,
|
|
237
|
-
botId: answers.botId,
|
|
238
|
-
botName: bot.name,
|
|
239
|
-
schedule,
|
|
240
|
-
outputType: outputAnswer.outputType,
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
console.log(chalk.green(`\n✅ Cron job created: ${job.id}\n`));
|
|
244
|
-
console.log(chalk.gray('Start the cron daemon with: natureco cron start\n'));
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function removeCronJobCommand(id) {
|
|
248
|
-
const removed = removeCronJob(id);
|
|
249
|
-
if (removed) {
|
|
250
|
-
console.log(chalk.green(`\n✅ Cron job removed: ${id}\n`));
|
|
251
|
-
} else {
|
|
252
|
-
console.log(chalk.red(`\n❌ Cron job not found: ${id}\n`));
|
|
253
|
-
process.exit(1);
|
|
39
|
+
if (action === 'add') {
|
|
40
|
+
await addCron(options);
|
|
41
|
+
} else if (action === 'list') {
|
|
42
|
+
listCrons();
|
|
43
|
+
} else if (action === 'remove') {
|
|
44
|
+
removeCron(options);
|
|
254
45
|
}
|
|
255
46
|
}
|
|
256
47
|
|
|
257
|
-
async function
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
48
|
+
async function addCron(options) {
|
|
49
|
+
const { name, schedule, action, target, prompt } = options;
|
|
50
|
+
|
|
51
|
+
if (!name || !schedule || !action || !target || !prompt) {
|
|
52
|
+
console.log(chalk.red('\n❌ Eksik parametre\n'));
|
|
53
|
+
console.log(chalk.gray('Gerekli parametreler: --name, --schedule, --action, --target, --prompt\n'));
|
|
261
54
|
process.exit(1);
|
|
262
55
|
}
|
|
263
56
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
console.log(chalk.red('\n❌ Not logged in. Run "natureco login" first.\n'));
|
|
57
|
+
if (!['whatsapp', 'telegram'].includes(action)) {
|
|
58
|
+
console.log(chalk.red('\n❌ Geçersiz action. Sadece "whatsapp" veya "telegram" kullanılabilir\n'));
|
|
267
59
|
process.exit(1);
|
|
268
60
|
}
|
|
269
61
|
|
|
270
|
-
|
|
271
|
-
|
|
62
|
+
// Validate cron expression
|
|
272
63
|
try {
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if (job.outputType === 'file') {
|
|
279
|
-
logCronOutput(job.id, output);
|
|
280
|
-
console.log(chalk.green('✅ Job completed. Output saved to log file.\n'));
|
|
281
|
-
} else {
|
|
282
|
-
console.log(chalk.green('✅ Job completed:\n'));
|
|
283
|
-
console.log(chalk.white(reply));
|
|
284
|
-
console.log('');
|
|
64
|
+
const nodeCron = require('node-cron');
|
|
65
|
+
if (!nodeCron.validate(schedule)) {
|
|
66
|
+
console.log(chalk.red('\n❌ Geçersiz cron ifadesi\n'));
|
|
67
|
+
console.log(chalk.gray('Örnek: "0 9 * * *" (her gün saat 09:00)\n'));
|
|
68
|
+
process.exit(1);
|
|
285
69
|
}
|
|
286
70
|
} catch (err) {
|
|
287
|
-
console.log(chalk.red(
|
|
71
|
+
console.log(chalk.red('\n❌ node-cron yüklü değil\n'));
|
|
72
|
+
console.log(chalk.yellow('Yüklemek için:'), chalk.cyan('npm install -g node-cron\n'));
|
|
288
73
|
process.exit(1);
|
|
289
74
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
console.log(chalk.
|
|
297
|
-
} else {
|
|
298
|
-
console.log(chalk.red(`\n❌ Cron job not found: ${id}\n`));
|
|
75
|
+
|
|
76
|
+
const crons = loadCrons();
|
|
77
|
+
|
|
78
|
+
// Check if name already exists
|
|
79
|
+
if (crons.find(c => c.name === name)) {
|
|
80
|
+
console.log(chalk.red('\n❌ Bu isimde bir cron zaten var\n'));
|
|
81
|
+
console.log(chalk.yellow('Önce silin:'), chalk.cyan(`natureco cron remove --name "${name}"\n`));
|
|
299
82
|
process.exit(1);
|
|
300
83
|
}
|
|
84
|
+
|
|
85
|
+
const newCron = {
|
|
86
|
+
name,
|
|
87
|
+
schedule,
|
|
88
|
+
action,
|
|
89
|
+
target,
|
|
90
|
+
prompt,
|
|
91
|
+
createdAt: new Date().toISOString(),
|
|
92
|
+
enabled: true
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
crons.push(newCron);
|
|
96
|
+
saveCrons(crons);
|
|
97
|
+
|
|
98
|
+
console.log(chalk.green('\n✅ Cron eklendi!\n'));
|
|
99
|
+
console.log(chalk.cyan('İsim:'), chalk.white(name));
|
|
100
|
+
console.log(chalk.cyan('Zamanlama:'), chalk.white(schedule));
|
|
101
|
+
console.log(chalk.cyan('Kanal:'), chalk.white(action));
|
|
102
|
+
console.log(chalk.cyan('Hedef:'), chalk.white(target));
|
|
103
|
+
console.log(chalk.cyan('Prompt:'), chalk.white(prompt));
|
|
104
|
+
console.log(chalk.gray('\nCron\'lar gateway başlatıldığında aktif olur.'));
|
|
105
|
+
console.log(chalk.gray('Gateway çalışıyorsa yeniden başlatın: natureco gateway stop && natureco gateway start\n'));
|
|
301
106
|
}
|
|
302
107
|
|
|
303
|
-
|
|
304
|
-
const
|
|
305
|
-
if (!apiKey) {
|
|
306
|
-
console.log(chalk.red('\n❌ Not logged in. Run "natureco login" first.\n'));
|
|
307
|
-
process.exit(1);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
console.log(chalk.green('🚀 Cron daemon started\n'));
|
|
311
|
-
console.log(chalk.gray('Press Ctrl+C to stop\n'));
|
|
108
|
+
function listCrons() {
|
|
109
|
+
const crons = loadCrons();
|
|
312
110
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
jobs.forEach(job => {
|
|
317
|
-
if (!job.enabled) return;
|
|
318
|
-
|
|
319
|
-
const cronExpression = parseCronSchedule(job.schedule);
|
|
320
|
-
|
|
321
|
-
if (!validateCronExpression(cronExpression)) {
|
|
322
|
-
console.log(chalk.red(`❌ Invalid cron expression for job ${job.id}: ${job.schedule}`));
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const task = cron.schedule(cronExpression, async () => {
|
|
327
|
-
console.log(chalk.yellow(`⏳ Running job: ${job.name}`));
|
|
328
|
-
|
|
329
|
-
try {
|
|
330
|
-
const response = await sendMessage(apiKey, job.botId, job.message, null, '');
|
|
331
|
-
const reply = response.reply || response.message || 'No response';
|
|
332
|
-
|
|
333
|
-
const output = `Job: ${job.name}\nBot: ${job.botName}\nMessage: ${job.message}\nReply: ${reply}`;
|
|
334
|
-
|
|
335
|
-
if (job.outputType === 'file') {
|
|
336
|
-
logCronOutput(job.id, output);
|
|
337
|
-
console.log(chalk.green(`✅ Job completed: ${job.name} (saved to log)`));
|
|
338
|
-
} else {
|
|
339
|
-
console.log(chalk.green(`✅ Job completed: ${job.name}`));
|
|
340
|
-
console.log(chalk.white(reply));
|
|
341
|
-
}
|
|
342
|
-
} catch (err) {
|
|
343
|
-
console.log(chalk.red(`❌ Job failed: ${job.name} - ${err.message}`));
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
tasks.push(task);
|
|
348
|
-
console.log(chalk.cyan(`✓ Scheduled: ${job.name} (${job.schedule})`));
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
if (tasks.length === 0) {
|
|
352
|
-
console.log(chalk.gray('No enabled jobs found.\n'));
|
|
353
|
-
process.exit(0);
|
|
111
|
+
if (crons.length === 0) {
|
|
112
|
+
console.log(chalk.gray('\n⚪ Kayıtlı cron yok\n'));
|
|
113
|
+
return;
|
|
354
114
|
}
|
|
355
115
|
|
|
356
|
-
console.log('
|
|
116
|
+
console.log(chalk.green(`\n📅 Kayıtlı Cron'lar (${crons.length})\n`));
|
|
357
117
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
console.log(chalk.gray(
|
|
361
|
-
|
|
362
|
-
|
|
118
|
+
crons.forEach((c, i) => {
|
|
119
|
+
console.log(chalk.cyan(`${i + 1}. ${c.name}`));
|
|
120
|
+
console.log(chalk.gray(` Zamanlama: ${c.schedule}`));
|
|
121
|
+
console.log(chalk.gray(` Kanal: ${c.action} → ${c.target}`));
|
|
122
|
+
console.log(chalk.gray(` Prompt: ${c.prompt.substring(0, 60)}${c.prompt.length > 60 ? '...' : ''}`));
|
|
123
|
+
console.log(chalk.gray(` Durum: ${c.enabled ? '✅ Aktif' : '❌ Pasif'}`));
|
|
124
|
+
console.log('');
|
|
363
125
|
});
|
|
364
126
|
}
|
|
365
127
|
|
|
366
|
-
function
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
128
|
+
function removeCron(options) {
|
|
129
|
+
const { name } = options;
|
|
130
|
+
|
|
131
|
+
if (!name) {
|
|
132
|
+
console.log(chalk.red('\n❌ --name parametresi gerekli\n'));
|
|
370
133
|
process.exit(1);
|
|
371
134
|
}
|
|
372
135
|
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
136
|
+
const crons = loadCrons();
|
|
137
|
+
const index = crons.findIndex(c => c.name === name);
|
|
138
|
+
|
|
139
|
+
if (index === -1) {
|
|
140
|
+
console.log(chalk.red('\n❌ Bu isimde bir cron bulunamadı\n'));
|
|
141
|
+
process.exit(1);
|
|
377
142
|
}
|
|
378
143
|
|
|
379
|
-
|
|
380
|
-
|
|
144
|
+
crons.splice(index, 1);
|
|
145
|
+
saveCrons(crons);
|
|
146
|
+
|
|
147
|
+
console.log(chalk.green('\n✅ Cron silindi!\n'));
|
|
148
|
+
console.log(chalk.gray('Gateway çalışıyorsa yeniden başlatın: natureco gateway stop && natureco gateway start\n'));
|
|
381
149
|
}
|
|
382
150
|
|
|
383
|
-
module.exports =
|
|
151
|
+
module.exports = cron;
|
|
@@ -211,7 +211,7 @@ body::before{
|
|
|
211
211
|
<div class="header-bot-name" id="header-bot-name">Nature Bot</div>
|
|
212
212
|
<div class="header-bot-model" id="header-bot-model">NatureCo</div>
|
|
213
213
|
</div>
|
|
214
|
-
<div class="version-badge" id="version-badge">v2.
|
|
214
|
+
<div class="version-badge" id="version-badge">v2.9.1</div>
|
|
215
215
|
</div>
|
|
216
216
|
<div class="messages" id="messages"></div>
|
|
217
217
|
<div class="input-area">
|
|
@@ -341,7 +341,7 @@ function dashboard(action) {
|
|
|
341
341
|
apiKey: cfg.apiKey,
|
|
342
342
|
defaultBot: cfg.defaultBot,
|
|
343
343
|
defaultBotId: cfg.defaultBotId,
|
|
344
|
-
version: 'v2.
|
|
344
|
+
version: 'v2.9.1',
|
|
345
345
|
bots: cfg.bots || [],
|
|
346
346
|
telegramToken: cfg.telegramToken || null,
|
|
347
347
|
whatsappConnected: cfg.whatsappConnected || false,
|
|
@@ -138,7 +138,7 @@ async function startGateway() {
|
|
|
138
138
|
|
|
139
139
|
async function runGatewayWorker() {
|
|
140
140
|
// This runs in the background
|
|
141
|
-
log('gateway', 'Starting NatureCo Gateway v2.
|
|
141
|
+
log('gateway', 'Starting NatureCo Gateway v2.9.1...', 'green');
|
|
142
142
|
|
|
143
143
|
// Load config
|
|
144
144
|
const { getConfig } = require('../utils/config');
|
|
@@ -179,6 +179,9 @@ async function runGatewayWorker() {
|
|
|
179
179
|
// Start HTTP server for message sending
|
|
180
180
|
startHttpServer();
|
|
181
181
|
|
|
182
|
+
// Load and start cron jobs
|
|
183
|
+
startCronJobs(config);
|
|
184
|
+
|
|
182
185
|
// Health check every 60 seconds
|
|
183
186
|
setInterval(() => {
|
|
184
187
|
log('gateway', 'health check: OK', 'gray');
|
|
@@ -571,6 +574,87 @@ function startHttpServer() {
|
|
|
571
574
|
});
|
|
572
575
|
}
|
|
573
576
|
|
|
577
|
+
function startCronJobs(config) {
|
|
578
|
+
try {
|
|
579
|
+
const nodeCron = require('node-cron');
|
|
580
|
+
const CRONS_FILE = path.join(os.homedir(), '.natureco', 'crons.json');
|
|
581
|
+
|
|
582
|
+
if (!fs.existsSync(CRONS_FILE)) {
|
|
583
|
+
log('cron', 'No cron jobs configured', 'gray');
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const crons = JSON.parse(fs.readFileSync(CRONS_FILE, 'utf-8'));
|
|
588
|
+
const enabledCrons = crons.filter(c => c.enabled);
|
|
589
|
+
|
|
590
|
+
if (enabledCrons.length === 0) {
|
|
591
|
+
log('cron', 'No enabled cron jobs', 'gray');
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
log('cron', `Loading ${enabledCrons.length} cron job(s)...`, 'cyan');
|
|
596
|
+
|
|
597
|
+
enabledCrons.forEach(cronJob => {
|
|
598
|
+
try {
|
|
599
|
+
nodeCron.schedule(cronJob.schedule, async () => {
|
|
600
|
+
log('cron', `Triggered: ${cronJob.name}`, 'yellow');
|
|
601
|
+
|
|
602
|
+
try {
|
|
603
|
+
// Send prompt to AI
|
|
604
|
+
const { sendMessage } = require('../utils/api');
|
|
605
|
+
const conversationId = `cron_${cronJob.name.replace(/\s+/g, '_')}`;
|
|
606
|
+
|
|
607
|
+
log('cron', `Sending prompt to AI...`, 'cyan');
|
|
608
|
+
const response = await sendMessage(null, null, cronJob.prompt, conversationId);
|
|
609
|
+
const reply = response?.reply || response?.message || '';
|
|
610
|
+
|
|
611
|
+
if (!reply) {
|
|
612
|
+
log('cron', `No response from AI`, 'red');
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
log('cron', `AI response received (${reply.length} chars)`, 'green');
|
|
617
|
+
|
|
618
|
+
// Send to target channel
|
|
619
|
+
if (cronJob.action === 'whatsapp') {
|
|
620
|
+
if (!global.whatsappSock) {
|
|
621
|
+
log('cron', `WhatsApp not connected, skipping`, 'red');
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const normalizedTarget = cronJob.target.replace(/[^\d]/g, '');
|
|
626
|
+
const jid = `${normalizedTarget}@s.whatsapp.net`;
|
|
627
|
+
|
|
628
|
+
await global.whatsappSock.sendMessage(jid, { text: reply });
|
|
629
|
+
log('cron', `Sent to WhatsApp: ${cronJob.target}`, 'green');
|
|
630
|
+
|
|
631
|
+
} else if (cronJob.action === 'telegram') {
|
|
632
|
+
if (!global.telegramBot) {
|
|
633
|
+
log('cron', `Telegram not connected, skipping`, 'red');
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
await global.telegramBot.sendMessage(cronJob.target, reply);
|
|
638
|
+
log('cron', `Sent to Telegram: ${cronJob.target}`, 'green');
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
} catch (err) {
|
|
642
|
+
log('cron', `Error executing ${cronJob.name}: ${err.message}`, 'red');
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
log('cron', `Scheduled: ${cronJob.name} (${cronJob.schedule})`, 'green');
|
|
647
|
+
|
|
648
|
+
} catch (err) {
|
|
649
|
+
log('cron', `Failed to schedule ${cronJob.name}: ${err.message}`, 'red');
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
} catch (err) {
|
|
654
|
+
log('cron', `Failed to load cron jobs: ${err.message}`, 'red');
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
574
658
|
function stopGateway() {
|
|
575
659
|
if (!fs.existsSync(PID_FILE)) {
|
|
576
660
|
console.log(chalk.gray('\n⚠️ Gateway not running\n'));
|