@apocaliss92/nodelink-js 0.4.28 → 0.4.30
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/README.md +20 -13
- package/dist/{chunk-NQ7D5TLR.js → chunk-AZZKLRJV.js} +11 -1
- package/dist/chunk-AZZKLRJV.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +10 -0
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +1 -1
- package/dist/index.cjs +75 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +66 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-NQ7D5TLR.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -5623,6 +5623,19 @@ interface EmailPushEvent {
|
|
|
5623
5623
|
from: string;
|
|
5624
5624
|
/** Raw body text excerpt (max 500 chars). */
|
|
5625
5625
|
bodyExcerpt: string;
|
|
5626
|
+
/**
|
|
5627
|
+
* First image attachment from the e-mail, when present. The camera
|
|
5628
|
+
* sends a JPEG snapshot of the trigger frame on motion when
|
|
5629
|
+
* `attachmentType=picture`; consumers can republish it to MQTT image
|
|
5630
|
+
* entities or cache it as the camera's `lastPicture` so battery
|
|
5631
|
+
* cameras don't have to be woken up to get a fresh thumbnail.
|
|
5632
|
+
* Kept in memory only — the bus does not persist it.
|
|
5633
|
+
*/
|
|
5634
|
+
attachment?: {
|
|
5635
|
+
contentType: string;
|
|
5636
|
+
data: Buffer;
|
|
5637
|
+
filename?: string;
|
|
5638
|
+
};
|
|
5626
5639
|
}
|
|
5627
5640
|
type EventHandler = (event: EmailPushEvent) => void;
|
|
5628
5641
|
type CameraResolver = (recipient: string) => string | undefined;
|
|
@@ -6310,9 +6323,19 @@ declare class ReolinkBaichuanApi {
|
|
|
6310
6323
|
subscribeEmailPushEvents(params: {
|
|
6311
6324
|
cameraId: string;
|
|
6312
6325
|
channel?: number;
|
|
6326
|
+
/**
|
|
6327
|
+
* Optional handler invoked with the *full* `EmailPushEvent`
|
|
6328
|
+
* after the simple-event dispatch. Useful for consumers that
|
|
6329
|
+
* need fields beyond what `onSimpleEvent` carries — most
|
|
6330
|
+
* notably `event.attachment` (the JPEG snapshot from the
|
|
6331
|
+
* trigger frame) which a downstream plugin can republish to
|
|
6332
|
+
* MQTT or cache as the camera's last picture.
|
|
6333
|
+
*/
|
|
6334
|
+
onEvent?: (event: EmailPushEvent) => void;
|
|
6313
6335
|
} | {
|
|
6314
6336
|
match: (event: EmailPushEvent) => boolean;
|
|
6315
6337
|
channel?: number;
|
|
6338
|
+
onEvent?: (event: EmailPushEvent) => void;
|
|
6316
6339
|
}): () => void;
|
|
6317
6340
|
/**
|
|
6318
6341
|
* Subscribe to minimal high-level events.
|
package/dist/index.d.ts
CHANGED
|
@@ -4807,6 +4807,19 @@ export declare interface EmailPushEvent {
|
|
|
4807
4807
|
from: string;
|
|
4808
4808
|
/** Raw body text excerpt (max 500 chars). */
|
|
4809
4809
|
bodyExcerpt: string;
|
|
4810
|
+
/**
|
|
4811
|
+
* First image attachment from the e-mail, when present. The camera
|
|
4812
|
+
* sends a JPEG snapshot of the trigger frame on motion when
|
|
4813
|
+
* `attachmentType=picture`; consumers can republish it to MQTT image
|
|
4814
|
+
* entities or cache it as the camera's `lastPicture` so battery
|
|
4815
|
+
* cameras don't have to be woken up to get a fresh thumbnail.
|
|
4816
|
+
* Kept in memory only — the bus does not persist it.
|
|
4817
|
+
*/
|
|
4818
|
+
attachment?: {
|
|
4819
|
+
contentType: string;
|
|
4820
|
+
data: Buffer;
|
|
4821
|
+
filename?: string;
|
|
4822
|
+
};
|
|
4810
4823
|
}
|
|
4811
4824
|
|
|
4812
4825
|
/**
|
|
@@ -7295,9 +7308,19 @@ export declare class ReolinkBaichuanApi {
|
|
|
7295
7308
|
subscribeEmailPushEvents(params: {
|
|
7296
7309
|
cameraId: string;
|
|
7297
7310
|
channel?: number;
|
|
7311
|
+
/**
|
|
7312
|
+
* Optional handler invoked with the *full* `EmailPushEvent`
|
|
7313
|
+
* after the simple-event dispatch. Useful for consumers that
|
|
7314
|
+
* need fields beyond what `onSimpleEvent` carries — most
|
|
7315
|
+
* notably `event.attachment` (the JPEG snapshot from the
|
|
7316
|
+
* trigger frame) which a downstream plugin can republish to
|
|
7317
|
+
* MQTT or cache as the camera's last picture.
|
|
7318
|
+
*/
|
|
7319
|
+
onEvent?: (event: EmailPushEvent) => void;
|
|
7298
7320
|
} | {
|
|
7299
7321
|
match: (event: EmailPushEvent) => boolean;
|
|
7300
7322
|
channel?: number;
|
|
7323
|
+
onEvent?: (event: EmailPushEvent) => void;
|
|
7301
7324
|
}): () => void;
|
|
7302
7325
|
/**
|
|
7303
7326
|
* Subscribe to minimal high-level events.
|
package/dist/index.js
CHANGED
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
setEmailPushCameraResolver,
|
|
66
66
|
setGlobalLogger,
|
|
67
67
|
xmlIndicatesFloodlight
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-AZZKLRJV.js";
|
|
69
69
|
import {
|
|
70
70
|
ReolinkCgiApi,
|
|
71
71
|
ReolinkHttpClient,
|
|
@@ -8284,6 +8284,7 @@ function base64DecodeToBytes(b64) {
|
|
|
8284
8284
|
}
|
|
8285
8285
|
|
|
8286
8286
|
// src/emailPush/server.ts
|
|
8287
|
+
import { format as utilFormat } from "util";
|
|
8287
8288
|
import { SMTPServer } from "smtp-server";
|
|
8288
8289
|
import { simpleParser } from "mailparser";
|
|
8289
8290
|
|
|
@@ -8363,6 +8364,9 @@ function createEmailPushServer(params) {
|
|
|
8363
8364
|
return;
|
|
8364
8365
|
}
|
|
8365
8366
|
const receivedAtMs = Date.now();
|
|
8367
|
+
const imageAttachment = (parsed.attachments ?? []).find(
|
|
8368
|
+
(a) => a && typeof a.contentType === "string" && a.contentType.toLowerCase().startsWith("image/") && Buffer.isBuffer(a.content)
|
|
8369
|
+
);
|
|
8366
8370
|
const event = {
|
|
8367
8371
|
cameraId,
|
|
8368
8372
|
recipient,
|
|
@@ -8370,11 +8374,18 @@ function createEmailPushServer(params) {
|
|
|
8370
8374
|
receivedAtMs,
|
|
8371
8375
|
subject: parsed.subject ?? "",
|
|
8372
8376
|
from: typeof parsed.from === "object" && parsed.from !== null && "text" in parsed.from ? String(parsed.from.text) : "",
|
|
8373
|
-
bodyExcerpt: (parsed.text ?? "").slice(0, 500)
|
|
8377
|
+
bodyExcerpt: (parsed.text ?? "").slice(0, 500),
|
|
8378
|
+
...imageAttachment ? {
|
|
8379
|
+
attachment: {
|
|
8380
|
+
contentType: imageAttachment.contentType,
|
|
8381
|
+
data: imageAttachment.content,
|
|
8382
|
+
...imageAttachment.filename ? { filename: imageAttachment.filename } : {}
|
|
8383
|
+
}
|
|
8384
|
+
} : {}
|
|
8374
8385
|
};
|
|
8375
8386
|
status.messagesAccepted++;
|
|
8376
8387
|
log.info(
|
|
8377
|
-
`Email push for camera=${cameraId} type=${event.inferredType} subject="${event.subject.slice(0, 80)}"`
|
|
8388
|
+
`Email push for camera=${cameraId} type=${event.inferredType} attachment=${event.attachment ? `${event.attachment.contentType} ${event.attachment.data.length}B` : "none"} subject="${event.subject.slice(0, 80)}"`
|
|
8378
8389
|
);
|
|
8379
8390
|
emitEmailPushEvent(event);
|
|
8380
8391
|
}
|
|
@@ -8393,10 +8404,19 @@ function createEmailPushServer(params) {
|
|
|
8393
8404
|
disabledCommands: config.requireAuth ? [] : ["AUTH"],
|
|
8394
8405
|
allowInsecureAuth: !config.tls,
|
|
8395
8406
|
size: config.maxMessageBytes,
|
|
8407
|
+
// smtp-server invokes its internal logger as
|
|
8408
|
+
// logger.info(metadata, formatString, ...args)
|
|
8409
|
+
// where `metadata` is an opaque session/connection object and
|
|
8410
|
+
// `formatString` may contain printf-style placeholders.
|
|
8411
|
+
// Naive `.map(String).join(" ")` would surface `[object Object]`
|
|
8412
|
+
// and leave `%s` unresolved (which is what the user was seeing).
|
|
8413
|
+
// Here we strip the metadata (keeping a compact `tnx`/`cid` tag
|
|
8414
|
+
// when present), then apply `util.format` so placeholders are
|
|
8415
|
+
// expanded by Node exactly like the smtp-server defaults do.
|
|
8396
8416
|
logger: {
|
|
8397
|
-
info: (...args) => log.
|
|
8398
|
-
debug: (...args) => log.debug(
|
|
8399
|
-
error: (...args) => log.warn(
|
|
8417
|
+
info: (...args) => log.info(formatSmtpLogArgs(args)),
|
|
8418
|
+
debug: (...args) => log.debug(formatSmtpLogArgs(args)),
|
|
8419
|
+
error: (...args) => log.warn(formatSmtpLogArgs(args))
|
|
8400
8420
|
},
|
|
8401
8421
|
...tlsOptions ? { secure: false, ...tlsOptions } : {},
|
|
8402
8422
|
onConnect(session, callback) {
|
|
@@ -8445,24 +8465,37 @@ function createEmailPushServer(params) {
|
|
|
8445
8465
|
);
|
|
8446
8466
|
return callback(new Error("Invalid username or password"));
|
|
8447
8467
|
},
|
|
8448
|
-
onRcptTo(address,
|
|
8468
|
+
onRcptTo(address, session, callback) {
|
|
8449
8469
|
const cameraId = resolveCameraIdFromRecipient(address.address);
|
|
8450
8470
|
if (!cameraId) {
|
|
8451
8471
|
status.messagesRejected++;
|
|
8472
|
+
log.warn(
|
|
8473
|
+
`SMTP RCPT TO rejected ${address.address} \u2014 unknown recipient (sessionId=${session.id})`
|
|
8474
|
+
);
|
|
8452
8475
|
return callback(
|
|
8453
8476
|
new Error(
|
|
8454
8477
|
`Unknown recipient ${address.address} (not registered)`
|
|
8455
8478
|
)
|
|
8456
8479
|
);
|
|
8457
8480
|
}
|
|
8481
|
+
log.info(
|
|
8482
|
+
`SMTP RCPT TO ${address.address} \u2192 camera=${cameraId} (sessionId=${session.id})`
|
|
8483
|
+
);
|
|
8458
8484
|
callback();
|
|
8459
8485
|
},
|
|
8460
8486
|
onData(stream, session, callback) {
|
|
8487
|
+
const startedAt = Date.now();
|
|
8488
|
+
log.debug(
|
|
8489
|
+
`SMTP DATA start (sessionId=${session.id} from=${session.envelope.mailFrom ? session.envelope.mailFrom.address : "?"})`
|
|
8490
|
+
);
|
|
8461
8491
|
const chunks = [];
|
|
8462
8492
|
stream.on("data", (chunk) => chunks.push(chunk));
|
|
8463
8493
|
stream.on("end", () => {
|
|
8464
8494
|
const recipients = session.envelope.rcptTo?.map((r) => r.address) ?? [];
|
|
8465
8495
|
const buffer = Buffer.concat(chunks);
|
|
8496
|
+
log.info(
|
|
8497
|
+
`SMTP DATA received ${buffer.length}B in ${Date.now() - startedAt}ms for ${recipients.length} recipient(s) (sessionId=${session.id})`
|
|
8498
|
+
);
|
|
8466
8499
|
const matched = recipients.map((r) => ({
|
|
8467
8500
|
recipient: r,
|
|
8468
8501
|
cameraId: resolveCameraIdFromRecipient(r)
|
|
@@ -8471,6 +8504,9 @@ function createEmailPushServer(params) {
|
|
|
8471
8504
|
);
|
|
8472
8505
|
if (matched.length === 0) {
|
|
8473
8506
|
status.messagesRejected++;
|
|
8507
|
+
log.warn(
|
|
8508
|
+
`SMTP DATA dropped \u2014 no recognised recipients in [${recipients.join(", ")}] (sessionId=${session.id})`
|
|
8509
|
+
);
|
|
8474
8510
|
return callback(new Error("No recognised recipients"));
|
|
8475
8511
|
}
|
|
8476
8512
|
Promise.all(
|
|
@@ -8479,13 +8515,15 @@ function createEmailPushServer(params) {
|
|
|
8479
8515
|
)
|
|
8480
8516
|
).then(() => callback()).catch((err) => {
|
|
8481
8517
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8482
|
-
log.error(
|
|
8518
|
+
log.error(
|
|
8519
|
+
`Email push pipeline error (sessionId=${session.id}): ${msg}`
|
|
8520
|
+
);
|
|
8483
8521
|
status.lastErrorMessage = msg;
|
|
8484
8522
|
callback(new Error(msg));
|
|
8485
8523
|
});
|
|
8486
8524
|
});
|
|
8487
8525
|
stream.on("error", (err) => {
|
|
8488
|
-
log.warn(`SMTP stream error: ${err.message}`);
|
|
8526
|
+
log.warn(`SMTP stream error (sessionId=${session.id}): ${err.message}`);
|
|
8489
8527
|
callback(err);
|
|
8490
8528
|
});
|
|
8491
8529
|
}
|
|
@@ -8533,6 +8571,25 @@ function createEmailPushServer(params) {
|
|
|
8533
8571
|
}
|
|
8534
8572
|
};
|
|
8535
8573
|
}
|
|
8574
|
+
function formatSmtpLogArgs(args) {
|
|
8575
|
+
if (args.length === 0) return "[smtp]";
|
|
8576
|
+
const [first, ...rest] = args;
|
|
8577
|
+
let tag = "";
|
|
8578
|
+
let formatArgs = args;
|
|
8579
|
+
if (first && typeof first === "object" && !Array.isArray(first)) {
|
|
8580
|
+
const meta = first;
|
|
8581
|
+
const tnx = typeof meta.tnx === "string" ? meta.tnx : void 0;
|
|
8582
|
+
const cid = typeof meta.cid === "string" ? meta.cid : void 0;
|
|
8583
|
+
const sid = typeof meta.sid === "string" ? meta.sid : void 0;
|
|
8584
|
+
const bits = [];
|
|
8585
|
+
if (tnx) bits.push(tnx);
|
|
8586
|
+
if (cid) bits.push(`cid=${cid}`);
|
|
8587
|
+
if (sid) bits.push(`sid=${sid}`);
|
|
8588
|
+
if (bits.length > 0) tag = ` [${bits.join(" ")}]`;
|
|
8589
|
+
formatArgs = rest;
|
|
8590
|
+
}
|
|
8591
|
+
return `[smtp]${tag} ${utilFormat(...formatArgs)}`;
|
|
8592
|
+
}
|
|
8536
8593
|
function buildInitialStatus(config) {
|
|
8537
8594
|
return {
|
|
8538
8595
|
enabled: true,
|