@deckasoft/waify 0.4.4 → 0.5.0
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/cli/index.js +68 -13
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -119,16 +119,42 @@ var init_prompt = __esm({
|
|
|
119
119
|
|
|
120
120
|
// src/core/schedule.ts
|
|
121
121
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
122
|
-
import { dirname as dirname3
|
|
122
|
+
import { dirname as dirname3 } from "path";
|
|
123
123
|
import { z as z3 } from "zod";
|
|
124
|
-
var ScheduledJobSchema, ScheduleSchema, defaultSchedule, scheduleJsonPath2, loadSchedule, saveSchedule, ofeliaRuntime, renderJob, renderOfeliaIni, regenerateOfeliaIni, addJob, removeJob;
|
|
124
|
+
var CRON_RANGES, STEP_RE, RANGE_RE, isValidCronField, isValidCron, ScheduledJobSchema, ScheduleSchema, defaultSchedule, scheduleJsonPath2, loadSchedule, saveSchedule, ofeliaRuntime, renderJob, renderOfeliaIni, regenerateOfeliaIni, addJob, removeJob;
|
|
125
125
|
var init_schedule = __esm({
|
|
126
126
|
"src/core/schedule.ts"() {
|
|
127
127
|
"use strict";
|
|
128
128
|
init_paths();
|
|
129
|
+
CRON_RANGES = [
|
|
130
|
+
[0, 59],
|
|
131
|
+
// seconds
|
|
132
|
+
[0, 59],
|
|
133
|
+
// minutes
|
|
134
|
+
[0, 23],
|
|
135
|
+
// hours
|
|
136
|
+
[1, 31],
|
|
137
|
+
// day-of-month
|
|
138
|
+
[1, 12],
|
|
139
|
+
// month
|
|
140
|
+
[0, 7]
|
|
141
|
+
// day-of-week
|
|
142
|
+
];
|
|
143
|
+
STEP_RE = /^(\*|\d+)\/\d+$/;
|
|
144
|
+
RANGE_RE = /^\d+-\d+$/;
|
|
145
|
+
isValidCronField = (field, [min, max]) => {
|
|
146
|
+
if (field === "*") return true;
|
|
147
|
+
if (STEP_RE.test(field) || RANGE_RE.test(field)) return true;
|
|
148
|
+
const n = Number(field);
|
|
149
|
+
return Number.isInteger(n) && n >= min && n <= max;
|
|
150
|
+
};
|
|
151
|
+
isValidCron = (value) => {
|
|
152
|
+
const fields = value.trim().split(/\s+/);
|
|
153
|
+
return fields.length === 6 && fields.every((f, i) => isValidCronField(f, CRON_RANGES[i]));
|
|
154
|
+
};
|
|
129
155
|
ScheduledJobSchema = z3.object({
|
|
130
156
|
name: z3.string().min(1).regex(/^[a-z0-9-]+$/, "name must be lowercase alphanumeric with dashes"),
|
|
131
|
-
schedule: z3.string().min(1),
|
|
157
|
+
schedule: z3.string().min(1).refine(isValidCron, { message: "schedule must be a 6-field cron expression (e.g. 0 0 9 * * *)" }),
|
|
132
158
|
command: z3.string().min(1).default("send")
|
|
133
159
|
});
|
|
134
160
|
ScheduleSchema = z3.object({
|
|
@@ -156,8 +182,8 @@ var init_schedule = __esm({
|
|
|
156
182
|
ofeliaRuntime = () => ({
|
|
157
183
|
image: process.env["WAIFY_SENDER_IMAGE"] ?? "openwa-scripts-sender:latest",
|
|
158
184
|
network: process.env["WAIFY_NETWORK"] ?? "waify-network",
|
|
159
|
-
hostDataDir: process.env["WAIFY_HOST_DATA_DIR"] ??
|
|
160
|
-
hostEnvFile: process.env["WAIFY_HOST_ENV_FILE"] ??
|
|
185
|
+
hostDataDir: process.env["WAIFY_HOST_DATA_DIR"] ?? dataDir(),
|
|
186
|
+
hostEnvFile: process.env["WAIFY_HOST_ENV_FILE"] ?? envPath()
|
|
161
187
|
});
|
|
162
188
|
renderJob = (job, runtime) => [
|
|
163
189
|
`[job-run "${job.name}"]`,
|
|
@@ -1054,7 +1080,7 @@ var createSpinner = (message) => {
|
|
|
1054
1080
|
}
|
|
1055
1081
|
};
|
|
1056
1082
|
};
|
|
1057
|
-
var wait = (ms) => new Promise((
|
|
1083
|
+
var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1058
1084
|
var fetchWithTimeout = (url, opts = {}, timeoutMs = 5e3) => fetch(url, { ...opts, signal: AbortSignal.timeout(timeoutMs) });
|
|
1059
1085
|
var renderQrInTerminal = (dataUrl) => {
|
|
1060
1086
|
const raw = decodeQrDataUrl(dataUrl);
|
|
@@ -1110,17 +1136,45 @@ var composeTemplate = () => `services:
|
|
|
1110
1136
|
volumes:
|
|
1111
1137
|
openwa-data:
|
|
1112
1138
|
`;
|
|
1113
|
-
var finalizeSetup = (sessionId) => {
|
|
1139
|
+
var finalizeSetup = (sessionId, jobs) => {
|
|
1114
1140
|
saveConfig({ ...loadConfig(), openwaSessionId: sessionId });
|
|
1115
1141
|
if (!existsSync6(promptPath())) {
|
|
1116
1142
|
savePrompt(defaultPrompt);
|
|
1117
1143
|
}
|
|
1118
|
-
|
|
1119
|
-
saveSchedule(defaultSchedule);
|
|
1120
|
-
}
|
|
1144
|
+
saveSchedule({ jobs });
|
|
1121
1145
|
console.warn("\n\u2713 All done! Run `waify send` to send your first message.");
|
|
1122
1146
|
};
|
|
1123
|
-
var promptLine = (rl, question) => new Promise((
|
|
1147
|
+
var promptLine = (rl, question) => new Promise((resolve) => rl.question(question, resolve));
|
|
1148
|
+
var promptUntilValid = async (promptFn, question, validate, errorMsg) => {
|
|
1149
|
+
const answer = (await promptFn(question)).trim();
|
|
1150
|
+
if (validate(answer)) return answer;
|
|
1151
|
+
process.stderr.write(errorMsg + "\n");
|
|
1152
|
+
return promptUntilValid(promptFn, question, validate, errorMsg);
|
|
1153
|
+
};
|
|
1154
|
+
var collectJobs = async (promptFn, accumulated = []) => {
|
|
1155
|
+
const name = await promptUntilValid(
|
|
1156
|
+
promptFn,
|
|
1157
|
+
"Job name: ",
|
|
1158
|
+
(v) => /^[a-z0-9-]+$/.test(v),
|
|
1159
|
+
"Name must be lowercase letters, numbers, and dashes only."
|
|
1160
|
+
);
|
|
1161
|
+
const schedule = await promptUntilValid(
|
|
1162
|
+
promptFn,
|
|
1163
|
+
"Cron pattern (e.g. 0 0 9 * * *): ",
|
|
1164
|
+
isValidCron,
|
|
1165
|
+
"Invalid cron pattern. Use 6 space-separated fields, e.g. 0 0 9 * * *"
|
|
1166
|
+
);
|
|
1167
|
+
const job = ScheduledJobSchema.parse({ name, schedule, command: "send" });
|
|
1168
|
+
const jobs = [...accumulated, job];
|
|
1169
|
+
const more = (await promptFn("Add another schedule? (y/N) ")).trim().toLowerCase();
|
|
1170
|
+
return more === "y" ? collectJobs(promptFn, jobs) : jobs;
|
|
1171
|
+
};
|
|
1172
|
+
var promptScheduleJobs = async (promptFn) => {
|
|
1173
|
+
process.stderr.write(
|
|
1174
|
+
"\nConfigure your message schedule (at least one job required).\nJob names: lowercase letters, numbers, and dashes only.\nCron pattern: 6 fields, e.g. 0 0 9 * * * (sec min hour dom month dow)\n\n"
|
|
1175
|
+
);
|
|
1176
|
+
return collectJobs(promptFn);
|
|
1177
|
+
};
|
|
1124
1178
|
var registerSetup = (program2) => {
|
|
1125
1179
|
program2.command("setup").description(
|
|
1126
1180
|
"Guided first-run wizard: installs OpenWA, authenticates WhatsApp, and configures waify"
|
|
@@ -1168,6 +1222,7 @@ var registerSetup = (program2) => {
|
|
|
1168
1222
|
const chatId = `${recipientNumber.trim()}@c.us`;
|
|
1169
1223
|
saveSecrets({ GEMINI_API_KEY: geminiKey.trim(), OPENWA_API_KEY: "" });
|
|
1170
1224
|
saveConfig({ ...loadConfig(), recipients: [{ chatId }] });
|
|
1225
|
+
const jobs = await promptScheduleJobs((q) => promptLine(rl, q));
|
|
1171
1226
|
console.warn("Writing docker-compose.yml...");
|
|
1172
1227
|
writeFileSync6(composePath(), composeTemplate(), "utf-8");
|
|
1173
1228
|
console.warn(
|
|
@@ -1338,7 +1393,7 @@ var registerSetup = (program2) => {
|
|
|
1338
1393
|
);
|
|
1339
1394
|
if (preStatus.success && preStatus.data.status === "ready") {
|
|
1340
1395
|
console.warn("\u2713 WhatsApp already linked \u2014 skipping QR scan");
|
|
1341
|
-
finalizeSetup(sessionId);
|
|
1396
|
+
finalizeSetup(sessionId, jobs);
|
|
1342
1397
|
return;
|
|
1343
1398
|
}
|
|
1344
1399
|
}
|
|
@@ -1415,7 +1470,7 @@ var registerSetup = (program2) => {
|
|
|
1415
1470
|
return;
|
|
1416
1471
|
}
|
|
1417
1472
|
connectSpinner.succeed("WhatsApp connected!");
|
|
1418
|
-
finalizeSetup(sessionId);
|
|
1473
|
+
finalizeSetup(sessionId, jobs);
|
|
1419
1474
|
} catch (err) {
|
|
1420
1475
|
console.error(err instanceof Error ? err.message : String(err));
|
|
1421
1476
|
process.exitCode = 1;
|