@deckasoft/waify 0.3.3 → 0.3.5

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 +170 -128
  2. package/package.json +1 -3
package/dist/cli/index.js CHANGED
@@ -963,40 +963,46 @@ 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
966
  import { z as z5 } from "zod";
968
- import qrcode from "qrcode-terminal";
969
967
  var SessionResponseSchema = z5.object({
970
968
  id: z5.string().optional(),
971
969
  name: z5.string().optional()
972
970
  });
973
971
  var QrResponseSchema = z5.object({
974
- qr: z5.string().optional()
972
+ qrCode: z5.string().optional()
975
973
  });
976
974
  var StatusResponseSchema = z5.object({
977
- status: z5.string().optional(),
978
- connected: z5.boolean().optional()
975
+ status: z5.string().optional()
979
976
  });
980
977
  var wait = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
981
- var composeTemplate = (openwaApiKey) => `services:
978
+ var fetchWithTimeout = (url, opts = {}, timeoutMs = 5e3) => fetch(url, { ...opts, signal: AbortSignal.timeout(timeoutMs) });
979
+ var composeTemplate = () => `services:
982
980
  openwa-api:
983
981
  image: ghcr.io/deckasoft/openwa:latest
984
982
  ports:
985
- - '2785:3000'
983
+ - '2785:2785'
986
984
  environment:
987
985
  - NODE_ENV=production
988
- - OPENWA_API_KEY=${openwaApiKey}
986
+ - PORT=2785
987
+ - DATABASE_TYPE=sqlite
988
+ - DATABASE_SYNCHRONIZE=true
989
+ - ENGINE_TYPE=whatsapp-web.js
990
+ - SESSION_DATA_PATH=/app/data/sessions
991
+ - PUPPETEER_HEADLESS=true
992
+ - PUPPETEER_ARGS=--no-sandbox,--disable-setuid-sandbox,--disable-dev-shm-usage,--disable-gpu
993
+ - STORAGE_TYPE=local
994
+ - STORAGE_LOCAL_PATH=/app/data/media
995
+ - QUEUE_ENABLED=false
996
+ - REDIS_ENABLED=false
997
+ - REDIS_BUILTIN=false
989
998
  volumes:
990
999
  - openwa-data:/app/data
991
1000
  restart: unless-stopped
992
1001
 
993
1002
  openwa-dashboard:
994
- image: ghcr.io/deckasoft/openwa:latest
1003
+ image: ghcr.io/deckasoft/openwa-dashboard:latest
995
1004
  ports:
996
- - '2886:4000'
997
- environment:
998
- - NODE_ENV=production
999
- - OPENWA_API_KEY=${openwaApiKey}
1005
+ - '2886:80'
1000
1006
  restart: unless-stopped
1001
1007
 
1002
1008
  volumes:
@@ -1005,131 +1011,167 @@ volumes:
1005
1011
  var promptLine = (rl, question) => new Promise((resolve2) => rl.question(question, resolve2));
1006
1012
  var registerSetup = (program2) => {
1007
1013
  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;
1014
+ try {
1015
+ console.warn("Checking Docker...");
1016
+ const dockerCheck = spawnSync("docker", ["info"], { stdio: "pipe" });
1017
+ if (dockerCheck.status !== 0) {
1018
+ console.error("Docker is not running or not installed. Please install Docker and start it before running setup.");
1019
+ process.exit(1);
1020
+ }
1021
+ console.warn("Creating config directory...");
1022
+ mkdirSync4(join2(homedir2(), ".config", "waify"), { recursive: true });
1023
+ console.warn("Writing docker-compose.yml...");
1024
+ writeFileSync5(composePath(), composeTemplate(), "utf-8");
1025
+ console.warn("Starting OpenWA containers (this may take a minute on first run)...");
1026
+ const upResult = spawnSync("docker", ["compose", "-f", composePath(), "up", "-d", "--no-deps", "openwa-api"], {
1027
+ stdio: "inherit"
1028
+ });
1029
+ if (upResult.status !== 0) {
1030
+ console.error("Failed to start OpenWA containers. Check docker compose logs for details.");
1031
+ process.exit(1);
1032
+ }
1033
+ console.warn("Waiting for OpenWA API to start...");
1034
+ let apiReady = false;
1035
+ for (let attempt = 0; attempt < 30; attempt++) {
1036
+ try {
1037
+ const res = await fetchWithTimeout("http://localhost:2785/api/health");
1038
+ if (res.status >= 200 && res.status < 300) {
1039
+ apiReady = true;
1040
+ break;
1041
+ }
1042
+ } catch {
1036
1043
  }
1037
- } catch {
1044
+ await wait(2e3);
1038
1045
  }
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"
1046
+ if (!apiReady) {
1047
+ console.error(
1048
+ "OpenWA API did not become ready in time. Check logs with: docker compose -f " + composePath() + " logs openwa-api"
1049
+ );
1050
+ process.exit(1);
1051
+ }
1052
+ console.warn("Reading API credentials...");
1053
+ const keyResult = spawnSync(
1054
+ "docker",
1055
+ ["compose", "-f", composePath(), "exec", "-T", "openwa-api", "cat", "/app/data/.api-key"],
1056
+ { encoding: "utf-8", stdio: "pipe" }
1044
1057
  );
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;
1058
+ const openwaApiKey = keyResult.stdout?.trim();
1059
+ if (keyResult.status !== 0 || !openwaApiKey) {
1060
+ console.error(
1061
+ "Could not read API key from container. Check logs: docker compose -f " + composePath() + " logs openwa-api"
1062
+ );
1063
+ if (keyResult.stderr) console.error(keyResult.stderr);
1064
+ process.exit(1);
1065
+ }
1066
+ console.warn("Creating WhatsApp session...");
1067
+ const sessionRes = await fetchWithTimeout("http://localhost:2785/api/sessions", {
1068
+ method: "POST",
1069
+ headers: {
1070
+ "X-API-Key": openwaApiKey,
1071
+ "Content-Type": "application/json"
1072
+ },
1073
+ body: JSON.stringify({ name: "waify" })
1074
+ }, 1e4);
1075
+ if (!sessionRes.ok) {
1076
+ throw new Error(`Failed to create session: ${sessionRes.status} ${sessionRes.statusText}`);
1077
+ }
1078
+ const sessionData = SessionResponseSchema.parse(await sessionRes.json());
1079
+ const sessionId = sessionData.id ?? sessionData.name ?? "waify";
1080
+ saveConfig({ ...loadConfig(), openwaApiKey, openwaSessionId: sessionId });
1081
+ console.warn("Starting WhatsApp engine...");
1082
+ const startRes = await fetchWithTimeout(`http://localhost:2785/api/sessions/${sessionId}/start`, {
1083
+ method: "POST",
1084
+ headers: { "X-API-Key": openwaApiKey }
1085
+ }, 1e4);
1086
+ if (!startRes.ok) {
1087
+ throw new Error(`Failed to start session: ${startRes.status} ${startRes.statusText}`);
1088
+ }
1089
+ console.warn("Waiting for QR code...");
1090
+ let qrReady = false;
1091
+ for (let attempt = 0; attempt < 30; attempt++) {
1092
+ try {
1093
+ const qrRes = await fetchWithTimeout(`http://localhost:2785/api/sessions/${sessionId}/qr`, {
1094
+ headers: { "X-API-Key": openwaApiKey }
1095
+ });
1096
+ if (qrRes.ok) {
1097
+ const qrData = QrResponseSchema.parse(await qrRes.json());
1098
+ if (qrData.qrCode) {
1099
+ qrReady = true;
1100
+ break;
1101
+ }
1102
+ }
1103
+ } catch {
1087
1104
  }
1088
- } catch {
1105
+ await wait(2e3);
1089
1106
  }
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
- const rl = createInterface({ input: process.stdin, output: process.stdout });
1098
- try {
1099
- let geminiKey = "";
1100
- while (!geminiKey.trim()) {
1101
- geminiKey = await promptLine(
1102
- rl,
1103
- "Enter your Gemini API key (get one free at https://aistudio.google.com/apikey):\n> "
1104
- );
1105
- if (!geminiKey.trim()) {
1106
- console.warn("Gemini API key cannot be empty. Please try again.");
1107
+ console.warn("\n\u{1F4F1} Scan the QR code with WhatsApp to link your device:");
1108
+ console.warn(" Settings \u2192 Linked Devices \u2192 Link a Device");
1109
+ console.warn(" View QR at: http://localhost:2785/api/docs (Sessions \u2192 GET /sessions/{id}/qr)");
1110
+ if (!qrReady) {
1111
+ console.warn(" (QR not yet ready \u2014 Chromium may still be initializing, check the Swagger link above)");
1112
+ }
1113
+ console.warn(" Waiting up to 2 minutes for you to scan...\n");
1114
+ let connected = false;
1115
+ for (let attempt = 0; attempt < 60; attempt++) {
1116
+ try {
1117
+ const statusRes = await fetchWithTimeout(`http://localhost:2785/api/sessions/${sessionId}`, {
1118
+ headers: { "X-API-Key": openwaApiKey }
1119
+ });
1120
+ const parsed = StatusResponseSchema.safeParse(await statusRes.json());
1121
+ if (!parsed.success) continue;
1122
+ if (parsed.data.status === "ready") {
1123
+ connected = true;
1124
+ break;
1125
+ }
1126
+ } catch {
1107
1127
  }
1128
+ await wait(2e3);
1108
1129
  }
1109
- saveSecrets({ GEMINI_API_KEY: geminiKey.trim() });
1110
- let recipientNumber = "";
1111
- const phoneRegex = /^\d{8,15}$/;
1112
- while (!phoneRegex.test(recipientNumber.trim())) {
1113
- recipientNumber = await promptLine(
1114
- rl,
1115
- "Enter your recipient's WhatsApp number (e.g. 5511999998888 \u2014 digits only, no + or spaces):\n> "
1116
- );
1117
- if (!phoneRegex.test(recipientNumber.trim())) {
1118
- console.warn("Invalid number format. Use digits only, 8\u201315 characters. Please try again.");
1130
+ if (!connected) {
1131
+ console.error("WhatsApp did not connect within 2 minutes. Please re-run `waify setup` to try again.");
1132
+ process.exit(1);
1133
+ }
1134
+ console.warn("\u2713 WhatsApp connected!");
1135
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1136
+ try {
1137
+ let geminiKey = "";
1138
+ while (!geminiKey.trim()) {
1139
+ geminiKey = await promptLine(
1140
+ rl,
1141
+ "Enter your Gemini API key (get one free at https://aistudio.google.com/apikey):\n> "
1142
+ );
1143
+ if (!geminiKey.trim()) {
1144
+ console.warn("Gemini API key cannot be empty. Please try again.");
1145
+ }
1146
+ }
1147
+ saveSecrets({ GEMINI_API_KEY: geminiKey.trim(), OPENWA_API_KEY: openwaApiKey });
1148
+ let recipientNumber = "";
1149
+ const phoneRegex = /^\d{8,15}$/;
1150
+ while (!phoneRegex.test(recipientNumber.trim())) {
1151
+ recipientNumber = await promptLine(
1152
+ rl,
1153
+ "Enter your recipient's WhatsApp number (e.g. 5511999998888 \u2014 digits only, no + or spaces):\n> "
1154
+ );
1155
+ if (!phoneRegex.test(recipientNumber.trim())) {
1156
+ console.warn("Invalid number format. Use digits only, 8\u201315 characters. Please try again.");
1157
+ }
1119
1158
  }
1159
+ const chatId = `${recipientNumber.trim()}@c.us`;
1160
+ saveConfig({ ...loadConfig(), recipients: [{ chatId }] });
1161
+ } finally {
1162
+ rl.close();
1120
1163
  }
1121
- const chatId = `${recipientNumber.trim()}@c.us`;
1122
- saveConfig({ ...loadConfig(), recipients: [{ chatId }] });
1123
- } finally {
1124
- rl.close();
1125
- }
1126
- if (!existsSync6(promptPath())) {
1127
- savePrompt(defaultPrompt);
1128
- }
1129
- if (!existsSync6(scheduleJsonPath())) {
1130
- saveSchedule(defaultSchedule);
1164
+ if (!existsSync6(promptPath())) {
1165
+ savePrompt(defaultPrompt);
1166
+ }
1167
+ if (!existsSync6(scheduleJsonPath())) {
1168
+ saveSchedule(defaultSchedule);
1169
+ }
1170
+ console.warn("\n\u2713 All done! Run `waify send` to send your first message.");
1171
+ } catch (err) {
1172
+ console.error(err instanceof Error ? err.message : String(err));
1173
+ process.exit(1);
1131
1174
  }
1132
- console.warn("\u2713 All done! Run `waify send` to send your first message.");
1133
1175
  });
1134
1176
  };
1135
1177
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deckasoft/waify",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "AI-powered daily message sender for WhatsApp, powered by OpenWA",
5
5
  "keywords": [
6
6
  "whatsapp",
@@ -41,7 +41,6 @@
41
41
  "ink-select-input": "^6.2.0",
42
42
  "ink-spinner": "^5.0.0",
43
43
  "ink-text-input": "^6.0.0",
44
- "qrcode-terminal": "^0.12.0",
45
44
  "react": "^19.2.6",
46
45
  "zod": "^3.24.0"
47
46
  },
@@ -49,7 +48,6 @@
49
48
  "@semantic-release/changelog": "^6.0.3",
50
49
  "@semantic-release/git": "^10.0.1",
51
50
  "@types/node": "^22.0.0",
52
- "@types/qrcode-terminal": "^0.12.2",
53
51
  "@types/react": "^19.2.15",
54
52
  "semantic-release": "^25.0.3",
55
53
  "tsup": "^8.5.1",