@deckasoft/waify 0.3.4 → 0.3.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 (2) hide show
  1. package/dist/cli/index.js +153 -112
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -212,7 +212,7 @@ var init_secrets = __esm({
212
212
  init_paths();
213
213
  SecretsSchema = z4.object({
214
214
  GEMINI_API_KEY: z4.string().min(1),
215
- OPENWA_API_KEY: z4.string().min(1)
215
+ OPENWA_API_KEY: z4.string().min(1).default("dev-admin-key")
216
216
  });
217
217
  parseEnvFile = (content) => content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).reduce((acc, line) => {
218
218
  const eq = line.indexOf("=");
@@ -963,139 +963,73 @@ import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as wr
963
963
  import { homedir as homedir2 } from "os";
964
964
  import { join as join2 } from "path";
965
965
  import { createInterface } from "readline";
966
- import * as crypto from "crypto";
967
- import { z as z5 } from "zod";
968
966
  import qrcode from "qrcode-terminal";
967
+ import { z as z5 } from "zod";
969
968
  var SessionResponseSchema = z5.object({
970
969
  id: z5.string().optional(),
971
970
  name: z5.string().optional()
972
971
  });
973
972
  var QrResponseSchema = z5.object({
974
- qr: z5.string().optional()
973
+ qrCode: z5.string().optional()
975
974
  });
976
975
  var StatusResponseSchema = z5.object({
977
- status: z5.string().optional(),
978
- connected: z5.boolean().optional()
976
+ status: z5.string().optional()
979
977
  });
980
978
  var wait = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
981
- var composeTemplate = (openwaApiKey) => `services:
979
+ var fetchWithTimeout = (url, opts = {}, timeoutMs = 5e3) => fetch(url, { ...opts, signal: AbortSignal.timeout(timeoutMs) });
980
+ var composeTemplate = () => `services:
982
981
  openwa-api:
983
982
  image: ghcr.io/deckasoft/openwa:latest
984
983
  ports:
985
- - '2785:3000'
984
+ - '2785:2785'
986
985
  environment:
987
986
  - NODE_ENV=production
988
- - OPENWA_API_KEY=${openwaApiKey}
987
+ - PORT=2785
988
+ - DATABASE_TYPE=sqlite
989
+ - DATABASE_SYNCHRONIZE=true
990
+ - ENGINE_TYPE=whatsapp-web.js
991
+ - SESSION_DATA_PATH=/app/data/sessions
992
+ - PUPPETEER_HEADLESS=true
993
+ - PUPPETEER_ARGS=--no-sandbox,--disable-setuid-sandbox,--disable-dev-shm-usage,--disable-gpu
994
+ - STORAGE_TYPE=local
995
+ - STORAGE_LOCAL_PATH=/app/data/media
996
+ - QUEUE_ENABLED=false
997
+ - REDIS_ENABLED=false
998
+ - REDIS_BUILTIN=false
989
999
  volumes:
990
1000
  - openwa-data:/app/data
991
1001
  restart: unless-stopped
992
1002
 
993
1003
  openwa-dashboard:
994
- image: ghcr.io/deckasoft/openwa:latest
1004
+ image: ghcr.io/deckasoft/openwa-dashboard:latest
995
1005
  ports:
996
- - '2886:4000'
997
- environment:
998
- - NODE_ENV=production
999
- - OPENWA_API_KEY=${openwaApiKey}
1006
+ - '2886:80'
1000
1007
  restart: unless-stopped
1001
1008
 
1002
1009
  volumes:
1003
1010
  openwa-data:
1004
1011
  `;
1005
1012
  var promptLine = (rl, question) => new Promise((resolve2) => rl.question(question, resolve2));
1013
+ var renderQr = (qrString) => new Promise((resolve2) => qrcode.generate(qrString, { small: true }, () => resolve2()));
1006
1014
  var registerSetup = (program2) => {
1007
1015
  program2.command("setup").description("Guided first-run wizard: installs OpenWA, authenticates WhatsApp, and configures waify").action(async () => {
1008
- console.warn("Checking Docker...");
1009
- const dockerCheck = spawnSync("docker", ["info"], { stdio: "pipe" });
1010
- if (dockerCheck.status !== 0) {
1011
- console.error("Docker is not running or not installed. Please install Docker and start it before running setup.");
1012
- process.exit(1);
1013
- }
1014
- console.warn("Creating config directory...");
1015
- mkdirSync4(join2(homedir2(), ".config", "waify"), { recursive: true });
1016
- console.warn("Generating credentials...");
1017
- const openwaApiKey = crypto.randomUUID();
1018
- console.warn("Writing docker-compose.yml...");
1019
- writeFileSync5(composePath(), composeTemplate(openwaApiKey), "utf-8");
1020
- console.warn("Starting OpenWA containers (this may take a minute on first run)...");
1021
- const upResult = spawnSync("docker", ["compose", "-f", composePath(), "up", "-d"], {
1022
- stdio: "inherit"
1023
- });
1024
- if (upResult.status !== 0) {
1025
- console.error("Failed to start OpenWA containers. Check docker compose logs for details.");
1026
- process.exit(1);
1027
- }
1028
- console.warn("Waiting for OpenWA API to start...");
1029
- let apiReady = false;
1030
- for (let attempt = 0; attempt < 30; attempt++) {
1031
- try {
1032
- const res = await fetch("http://localhost:2785/api");
1033
- if (res.status >= 200 && res.status < 300) {
1034
- apiReady = true;
1035
- break;
1036
- }
1037
- } catch {
1038
- }
1039
- await wait(2e3);
1040
- }
1041
- if (!apiReady) {
1042
- console.error(
1043
- "OpenWA API did not become ready in time. Check logs with: docker compose -f " + composePath() + " logs openwa-api"
1044
- );
1045
- process.exit(1);
1046
- }
1047
- console.warn("Creating WhatsApp session...");
1048
- const sessionRes = await fetch("http://localhost:2785/api/sessions", {
1049
- method: "POST",
1050
- headers: {
1051
- "X-API-Key": openwaApiKey,
1052
- "Content-Type": "application/json"
1053
- },
1054
- body: JSON.stringify({ name: "waify" })
1055
- });
1056
- if (!sessionRes.ok) {
1057
- throw new Error(`Failed to create session: ${sessionRes.status} ${sessionRes.statusText}`);
1058
- }
1059
- const sessionData = SessionResponseSchema.parse(await sessionRes.json());
1060
- const sessionId = sessionData.id ?? sessionData.name ?? "waify";
1061
- saveConfig({ ...loadConfig(), openwaApiKey, openwaSessionId: sessionId });
1062
- console.warn("\u{1F4F1} Scan the QR code below with WhatsApp on your phone:");
1063
- console.warn(" (Settings \u2192 Linked Devices \u2192 Link a Device)");
1064
- const qrRes = await fetch("http://localhost:2785/api/sessions/waify/qr", {
1065
- headers: { "X-API-Key": openwaApiKey }
1066
- });
1067
- if (!qrRes.ok) {
1068
- console.warn(" Could not fetch QR code \u2014 use the browser link below.");
1069
- }
1070
- const qrData = qrRes.ok ? QrResponseSchema.parse(await qrRes.json()) : { qr: void 0 };
1071
- const rawQr = qrData.qr ?? "";
1072
- const qrString = rawQr.startsWith("data:image/png;base64,") ? rawQr.slice("data:image/png;base64,".length) : rawQr;
1073
- qrcode.generate(qrString, { small: true });
1074
- console.warn(" Or open in browser: http://localhost:2886");
1075
- let connected = false;
1076
- for (let attempt = 0; attempt < 60; attempt++) {
1077
- try {
1078
- const statusRes = await fetch("http://localhost:2785/api/sessions/waify", {
1079
- headers: { "X-API-Key": openwaApiKey }
1080
- });
1081
- const parsed = StatusResponseSchema.safeParse(await statusRes.json());
1082
- if (!parsed.success) continue;
1083
- const statusData = parsed.data;
1084
- if (statusData.status === "CONNECTED" || statusData.connected === true) {
1085
- connected = true;
1086
- break;
1087
- }
1088
- } catch {
1089
- }
1090
- await wait(2e3);
1091
- }
1092
- if (!connected) {
1093
- console.error("WhatsApp did not connect within 2 minutes. Please re-run `waify setup` to try again.");
1094
- process.exit(1);
1095
- }
1096
- console.warn("\u2713 WhatsApp connected!");
1097
1016
  const rl = createInterface({ input: process.stdin, output: process.stdout });
1098
1017
  try {
1018
+ console.warn("Checking Docker...");
1019
+ const dockerCheck = spawnSync("docker", ["info"], { stdio: "pipe" });
1020
+ if (dockerCheck.status !== 0) {
1021
+ console.error("Docker is not running or not installed. Please install Docker and start it before running setup.");
1022
+ process.exitCode = 1;
1023
+ return;
1024
+ }
1025
+ console.warn("Creating config directory...");
1026
+ mkdirSync4(join2(homedir2(), ".config", "waify"), { recursive: true });
1027
+ const baseUrl = loadConfig().openwaBaseUrl;
1028
+ const rawApiKey = await promptLine(
1029
+ rl,
1030
+ 'OpenWA API key (press Enter to use default "dev-admin-key"):\n> '
1031
+ );
1032
+ const openwaApiKey = rawApiKey.trim() || "dev-admin-key";
1099
1033
  let geminiKey = "";
1100
1034
  while (!geminiKey.trim()) {
1101
1035
  geminiKey = await promptLine(
@@ -1106,7 +1040,6 @@ var registerSetup = (program2) => {
1106
1040
  console.warn("Gemini API key cannot be empty. Please try again.");
1107
1041
  }
1108
1042
  }
1109
- saveSecrets({ GEMINI_API_KEY: geminiKey.trim() });
1110
1043
  let recipientNumber = "";
1111
1044
  const phoneRegex = /^\d{8,15}$/;
1112
1045
  while (!phoneRegex.test(recipientNumber.trim())) {
@@ -1119,17 +1052,125 @@ var registerSetup = (program2) => {
1119
1052
  }
1120
1053
  }
1121
1054
  const chatId = `${recipientNumber.trim()}@c.us`;
1122
- saveConfig({ ...loadConfig(), recipients: [{ chatId }] });
1055
+ saveSecrets({ GEMINI_API_KEY: geminiKey.trim(), OPENWA_API_KEY: openwaApiKey });
1056
+ saveConfig({ ...loadConfig(), openwaApiKey, recipients: [{ chatId }] });
1057
+ console.warn("Writing docker-compose.yml...");
1058
+ writeFileSync5(composePath(), composeTemplate(), "utf-8");
1059
+ console.warn("Starting OpenWA containers (this may take a minute on first run)...");
1060
+ const upResult = spawnSync("docker", ["compose", "-f", composePath(), "up", "-d", "--no-deps", "openwa-api"], {
1061
+ stdio: "inherit"
1062
+ });
1063
+ if (upResult.status !== 0) {
1064
+ console.error("Failed to start OpenWA containers. Check docker compose logs for details.");
1065
+ process.exitCode = 1;
1066
+ return;
1067
+ }
1068
+ console.warn("Waiting for OpenWA API to start...");
1069
+ let apiReady = false;
1070
+ for (let attempt = 0; attempt < 30; attempt++) {
1071
+ try {
1072
+ const res = await fetchWithTimeout(`${baseUrl}/api/health`);
1073
+ if (res.status >= 200 && res.status < 300) {
1074
+ apiReady = true;
1075
+ break;
1076
+ }
1077
+ } catch {
1078
+ }
1079
+ await wait(2e3);
1080
+ }
1081
+ if (!apiReady) {
1082
+ console.error(
1083
+ "OpenWA API did not become ready in time. Check logs with: docker compose -f " + composePath() + " logs openwa-api"
1084
+ );
1085
+ process.exitCode = 1;
1086
+ return;
1087
+ }
1088
+ console.warn("Creating WhatsApp session...");
1089
+ const sessionRes = await fetchWithTimeout(`${baseUrl}/api/sessions`, {
1090
+ method: "POST",
1091
+ headers: {
1092
+ "X-API-Key": openwaApiKey,
1093
+ "Content-Type": "application/json"
1094
+ },
1095
+ body: JSON.stringify({ name: "waify" })
1096
+ }, 1e4);
1097
+ if (!sessionRes.ok) {
1098
+ throw new Error(`Failed to create session: ${sessionRes.status} ${sessionRes.statusText}`);
1099
+ }
1100
+ const sessionData = SessionResponseSchema.parse(await sessionRes.json());
1101
+ const sessionId = sessionData.id ?? sessionData.name ?? "waify";
1102
+ console.warn("Starting WhatsApp engine...");
1103
+ const startRes = await fetchWithTimeout(`${baseUrl}/api/sessions/${sessionId}/start`, {
1104
+ method: "POST",
1105
+ headers: { "X-API-Key": openwaApiKey }
1106
+ }, 1e4);
1107
+ if (!startRes.ok) {
1108
+ throw new Error(`Failed to start session: ${startRes.status} ${startRes.statusText}`);
1109
+ }
1110
+ console.warn("Waiting for QR code...");
1111
+ let qrCode;
1112
+ for (let attempt = 0; attempt < 30; attempt++) {
1113
+ try {
1114
+ const qrRes = await fetchWithTimeout(`${baseUrl}/api/sessions/${sessionId}/qr`, {
1115
+ headers: { "X-API-Key": openwaApiKey }
1116
+ });
1117
+ if (qrRes.ok) {
1118
+ const qrData = QrResponseSchema.parse(await qrRes.json());
1119
+ if (qrData.qrCode) {
1120
+ qrCode = qrData.qrCode;
1121
+ break;
1122
+ }
1123
+ }
1124
+ } catch {
1125
+ }
1126
+ await wait(2e3);
1127
+ }
1128
+ console.warn("\n\u{1F4F1} Scan the QR code with WhatsApp to link your device:");
1129
+ console.warn(" Settings \u2192 Linked Devices \u2192 Link a Device\n");
1130
+ if (qrCode) {
1131
+ await renderQr(qrCode);
1132
+ console.warn("\n (QR expires in ~20s \u2014 re-run setup if it expires before you scan)");
1133
+ } else {
1134
+ console.warn(` QR not yet ready. Check: ${baseUrl}/api/sessions/${sessionId}/qr`);
1135
+ console.warn(` (Add header: X-API-Key: ${openwaApiKey})`);
1136
+ }
1137
+ console.warn(" Waiting up to 2 minutes for you to scan...\n");
1138
+ let connected = false;
1139
+ for (let attempt = 0; attempt < 60; attempt++) {
1140
+ try {
1141
+ const statusRes = await fetchWithTimeout(`${baseUrl}/api/sessions/${sessionId}`, {
1142
+ headers: { "X-API-Key": openwaApiKey }
1143
+ });
1144
+ const parsed = StatusResponseSchema.safeParse(await statusRes.json());
1145
+ if (!parsed.success) continue;
1146
+ if (parsed.data.status === "ready") {
1147
+ connected = true;
1148
+ break;
1149
+ }
1150
+ } catch {
1151
+ }
1152
+ await wait(2e3);
1153
+ }
1154
+ if (!connected) {
1155
+ console.error("WhatsApp did not connect within 2 minutes. Please re-run `waify setup` to try again.");
1156
+ process.exitCode = 1;
1157
+ return;
1158
+ }
1159
+ console.warn("\u2713 WhatsApp connected!");
1160
+ saveConfig({ ...loadConfig(), openwaSessionId: sessionId });
1161
+ if (!existsSync6(promptPath())) {
1162
+ savePrompt(defaultPrompt);
1163
+ }
1164
+ if (!existsSync6(scheduleJsonPath())) {
1165
+ saveSchedule(defaultSchedule);
1166
+ }
1167
+ console.warn("\n\u2713 All done! Run `waify send` to send your first message.");
1168
+ } catch (err) {
1169
+ console.error(err instanceof Error ? err.message : String(err));
1170
+ process.exitCode = 1;
1123
1171
  } finally {
1124
1172
  rl.close();
1125
1173
  }
1126
- if (!existsSync6(promptPath())) {
1127
- savePrompt(defaultPrompt);
1128
- }
1129
- if (!existsSync6(scheduleJsonPath())) {
1130
- saveSchedule(defaultSchedule);
1131
- }
1132
- console.warn("\u2713 All done! Run `waify send` to send your first message.");
1133
1174
  });
1134
1175
  };
1135
1176
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deckasoft/waify",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "AI-powered daily message sender for WhatsApp, powered by OpenWA",
5
5
  "keywords": [
6
6
  "whatsapp",