chore: release 1.2.1
parent
f63bdda628
commit
c11abc1134
|
|
@ -1,6 +1,6 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 1.2.1 — Unreleased
|
## 1.2.1 — 2025-11-28
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
- **Manual heartbeat sends:** `warelay heartbeat` now accepts `--message/--body` with `--provider web|twilio` to push real outbound messages through the same plumbing; `--dry-run` previews payloads without sending.
|
- **Manual heartbeat sends:** `warelay heartbeat` now accepts `--message/--body` with `--provider web|twilio` to push real outbound messages through the same plumbing; `--dry-run` previews payloads without sending.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "warelay",
|
"name": "warelay",
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"description": "WhatsApp relay CLI (send, monitor, webhook, auto-reply) using Twilio",
|
"description": "WhatsApp relay CLI (send, monitor, webhook, auto-reply) using Twilio",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
import { mediaKindFromMime, type MediaKind } from "./constants.js";
|
import { type MediaKind, mediaKindFromMime } from "./constants.js";
|
||||||
|
|
||||||
// Map common mimes to preferred file extensions.
|
// Map common mimes to preferred file extensions.
|
||||||
const EXT_BY_MIME: Record<string, string> = {
|
const EXT_BY_MIME: Record<string, string> = {
|
||||||
|
|
@ -82,7 +82,10 @@ function sniffMime(buffer?: Buffer): string | undefined {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MP4: "ftyp" at offset 4.
|
// MP4: "ftyp" at offset 4.
|
||||||
if (buffer.length >= 12 && buffer.subarray(4, 8).toString("ascii") === "ftyp") {
|
if (
|
||||||
|
buffer.length >= 12 &&
|
||||||
|
buffer.subarray(4, 8).toString("ascii") === "ftyp"
|
||||||
|
) {
|
||||||
return "video/mp4";
|
return "video/mp4";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
|
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
const realOs = await vi.importActual<typeof import("node:os")>("node:os");
|
const realOs = await vi.importActual<typeof import("node:os")>("node:os");
|
||||||
const HOME = path.join(realOs.tmpdir(), "warelay-home-test");
|
const HOME = path.join(realOs.tmpdir(), "warelay-home-test");
|
||||||
|
|
|
||||||
|
|
@ -41,15 +41,14 @@ function looksLikeUrl(src: string) {
|
||||||
return /^https?:\/\//i.test(src);
|
return /^https?:\/\//i.test(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download media to disk while capturing the first few KB for mime sniffing.
|
||||||
|
*/
|
||||||
async function downloadToFile(
|
async function downloadToFile(
|
||||||
url: string,
|
url: string,
|
||||||
dest: string,
|
dest: string,
|
||||||
headers?: Record<string, string>,
|
headers?: Record<string, string>,
|
||||||
): Promise<{ headerMime?: string; sniffBuffer: Buffer; size: number }>
|
): Promise<{ headerMime?: string; sniffBuffer: Buffer; size: number }> {
|
||||||
/**
|
|
||||||
* Download media to disk while capturing the first few KB for mime sniffing.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
const req = request(url, { headers }, (res) => {
|
const req = request(url, { headers }, (res) => {
|
||||||
if (!res.statusCode || res.statusCode >= 400) {
|
if (!res.statusCode || res.statusCode >= 400) {
|
||||||
|
|
@ -72,9 +71,14 @@ async function downloadToFile(
|
||||||
});
|
});
|
||||||
pipeline(res, out)
|
pipeline(res, out)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const sniffBuffer = Buffer.concat(sniffChunks, Math.min(sniffLen, 16384));
|
const sniffBuffer = Buffer.concat(
|
||||||
|
sniffChunks,
|
||||||
|
Math.min(sniffLen, 16384),
|
||||||
|
);
|
||||||
const rawHeader = res.headers["content-type"];
|
const rawHeader = res.headers["content-type"];
|
||||||
const headerMime = Array.isArray(rawHeader) ? rawHeader[0] : rawHeader;
|
const headerMime = Array.isArray(rawHeader)
|
||||||
|
? rawHeader[0]
|
||||||
|
: rawHeader;
|
||||||
resolve({
|
resolve({
|
||||||
headerMime,
|
headerMime,
|
||||||
sniffBuffer,
|
sniffBuffer,
|
||||||
|
|
@ -116,7 +120,8 @@ export async function saveMediaSource(
|
||||||
headerMime,
|
headerMime,
|
||||||
filePath: source,
|
filePath: source,
|
||||||
});
|
});
|
||||||
const ext = extensionForMime(mime) ?? path.extname(new URL(source).pathname);
|
const ext =
|
||||||
|
extensionForMime(mime) ?? path.extname(new URL(source).pathname);
|
||||||
const finalDest = path.join(dir, ext ? `${id}${ext}` : id);
|
const finalDest = path.join(dir, ext ? `${id}${ext}` : id);
|
||||||
await fs.rename(tempDest, finalDest);
|
await fs.rename(tempDest, finalDest);
|
||||||
return { id, path: finalDest, size, contentType: mime };
|
return { id, path: finalDest, size, contentType: mime };
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,30 @@ import path from "node:path";
|
||||||
|
|
||||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
const HOME = path.join(os.tmpdir(), `warelay-inbound-media-${crypto.randomUUID()}`);
|
const HOME = path.join(
|
||||||
|
os.tmpdir(),
|
||||||
|
`warelay-inbound-media-${crypto.randomUUID()}`,
|
||||||
|
);
|
||||||
process.env.HOME = HOME;
|
process.env.HOME = HOME;
|
||||||
|
|
||||||
vi.mock("@whiskeysockets/baileys", async () => {
|
vi.mock("@whiskeysockets/baileys", async () => {
|
||||||
const actual = await vi.importActual<typeof import("@whiskeysockets/baileys")>(
|
const actual = await vi.importActual<
|
||||||
"@whiskeysockets/baileys",
|
typeof import("@whiskeysockets/baileys")
|
||||||
);
|
>("@whiskeysockets/baileys");
|
||||||
const jpegBuffer = Buffer.from([
|
const jpegBuffer = Buffer.from([
|
||||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x02, 0x03, 0x03,
|
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02,
|
||||||
0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09,
|
0x02, 0x03, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04,
|
||||||
0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10,
|
0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a,
|
||||||
0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc0, 0x00, 0x11, 0x08,
|
0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
|
||||||
0x00, 0x01, 0x00, 0x01, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x14, 0x00,
|
0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10,
|
||||||
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc4, 0x00,
|
0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff,
|
||||||
0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xda, 0x00,
|
0xc0, 0x00, 0x11, 0x08, 0x00, 0x01, 0x00, 0x01, 0x03, 0x01, 0x11, 0x00,
|
||||||
0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xff, 0xd9,
|
0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x14, 0x00, 0x01,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xff, 0xc4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xda,
|
||||||
|
0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
|
||||||
|
0xff, 0xd9,
|
||||||
]);
|
]);
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
|
|
@ -63,9 +71,11 @@ describe("web inbound media saves with extension", () => {
|
||||||
const onMessage = vi.fn();
|
const onMessage = vi.fn();
|
||||||
const listener = await monitorWebInbox({ verbose: false, onMessage });
|
const listener = await monitorWebInbox({ verbose: false, onMessage });
|
||||||
const { createWaSocket } = await import("./session.js");
|
const { createWaSocket } = await import("./session.js");
|
||||||
const realSock = await (createWaSocket as unknown as () => Promise<{
|
const realSock = await (
|
||||||
ev: import("node:events").EventEmitter;
|
createWaSocket as unknown as () => Promise<{
|
||||||
}>)();
|
ev: import("node:events").EventEmitter;
|
||||||
|
}>
|
||||||
|
)();
|
||||||
|
|
||||||
const upsert = {
|
const upsert = {
|
||||||
type: "notify",
|
type: "notify",
|
||||||
|
|
@ -83,9 +93,10 @@ describe("web inbound media saves with extension", () => {
|
||||||
|
|
||||||
expect(onMessage).toHaveBeenCalledTimes(1);
|
expect(onMessage).toHaveBeenCalledTimes(1);
|
||||||
const msg = onMessage.mock.calls[0][0];
|
const msg = onMessage.mock.calls[0][0];
|
||||||
expect(msg.mediaPath).toBeDefined();
|
const mediaPath = msg.mediaPath;
|
||||||
expect(path.extname(msg.mediaPath!)).toBe(".jpg");
|
expect(mediaPath).toBeDefined();
|
||||||
const stat = await fs.stat(msg.mediaPath!);
|
expect(path.extname(mediaPath as string)).toBe(".jpg");
|
||||||
|
const stat = await fs.stat(mediaPath as string);
|
||||||
expect(stat.size).toBeGreaterThan(0);
|
expect(stat.size).toBeGreaterThan(0);
|
||||||
|
|
||||||
await listener.close();
|
await listener.close();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue