Say only what the SMSC marked, and trim the comments the review found redundant

This commit is contained in:
2026-09-03 19:16:13 +02:00
parent 50679ec2fe
commit dd458bfa53
4 changed files with 6 additions and 9 deletions
+2 -2
View File
@@ -284,8 +284,8 @@ Grouped by what each one constrains.
is the handset's word about a message, not the network's. A message type of 0 or one of the ten is the handset's word about a message, not the network's. A message type of 0 or one of the ten
reserved keeps the scrape, and a non-empty `receipted_message_id` TLV marks a report on the same reserved keeps the scrape, and a non-empty `receipted_message_id` TLV marks a report on the same
footing. A report this library recognises never reaches the reassembler, so an SMSC that splits one footing. A report this library recognises never reaches the reassembler, so an SMSC that splits one
across segments gets a `dlr` per segment rather than one merged report. The across segments gets a `dlr` per segment rather than one merged report. The `message_state` TLV is
`message_state` TLV is authoritative only where it names a state in the table — SMPP reserves authoritative only where it names a state in the table — SMPP reserves
0x80-0xFF for MC-vendor-specific values, so an unnameable one keeps its raw `statusId` and leaves 0x80-0xFF for MC-vendor-specific values, so an unnameable one keeps its raw `statusId` and leaves
`statusMsg` to the body. `statusMsg` to the body.
+3 -4
View File
@@ -163,9 +163,8 @@ session.on('sms', async sms => {
Delivery receipts travel on the same SMPP command but reach you as `dlr`, so nothing you write has Delivery receipts travel on the same SMPP command but reach you as `dlr`, so nothing you write has
to tell the two apart. `esm_class` is what tells them apart; where it names no message type a to tell the two apart. `esm_class` is what tells them apart; where it names no message type a
`receipted_message_id` TLV does, and failing both the message body is read for the standard `receipted_message_id` TLV does, and failing both the message body is read for the standard
`id:` and `stat:` receipt fields. An intermediate delivery notification is the SMSC reporting too, `id:` and `stat:` receipt fields. An intermediate delivery notification is the SMSC reporting as
and arrives with `dlr.intermediate` true: nothing about the message is settled, and a later receipt well, not an inbound message.
says how it ended.
Matching a receipt to a send means comparing `dlr.smsId` against the `smsIds` that `sendSms()` Matching a receipt to a send means comparing `dlr.smsId` against the `smsIds` that `sendSms()`
returned. Some SMSCs write the two in different notations — a hex `message_id` on the returned. Some SMSCs write the two in different notations — a hex `message_id` on the
@@ -319,7 +318,7 @@ TypeScript users can import `SmppLog` to have the compiler check one.
| Event | Fires when | | Event | Fires when |
| --- | --- | | --- | --- |
| `sms` | An SMS arrives, reassembled if it was multipart. Carries `sendResp()`, `sendDlr()` and its `smsId`. | | `sms` | An SMS arrives, reassembled if it was multipart. Carries `sendResp()`, `sendDlr()` and its `smsId`. |
| `dlr` | A delivery report arrives, one per segment. `intermediate` is true where the SMSC marked it a report it will follow with a final receipt. `smsId` is undefined when the peer marked a receipt whose body carries no readable id. `statusMsg` names `statusId` unless the peer sent a `message_state` this library cannot name — then `statusId` is that raw value and `statusMsg` is whatever the body said, or `UNKNOWN`. | | `dlr` | A delivery report arrives, one per segment. `intermediate` is true where the SMSC marked the report non-final. `smsId` is undefined when the peer marked a receipt whose body carries no readable id. `statusMsg` names `statusId` unless the peer sent a `message_state` this library cannot name — then `statusId` is that raw value and `statusMsg` is whatever the body said, or `UNKNOWN`. |
| `messageDlr` | Every segment of a multipart message sent with `dlr: true` has been reported on, carrying the worst status of the segments. An intermediate report never counts towards it. Merging needs the SMSC to number its segment ids `<base>-<n>`, which is this library's own server's convention — an SMSC that hands out unrelated ids per segment never fires it. A base is merged once: a later message the SMSC gives the same ids is reported on through `dlr` alone, and an earlier one still collecting loses its merged report as well. | | `messageDlr` | Every segment of a multipart message sent with `dlr: true` has been reported on, carrying the worst status of the segments. An intermediate report never counts towards it. Merging needs the SMSC to number its segment ids `<base>-<n>`, which is this library's own server's convention — an SMSC that hands out unrelated ids per segment never fires it. A base is merged once: a later message the SMSC gives the same ids is reported on through `dlr` alone, and an earlier one still collecting loses its merged report as well. |
| `close` | The session is over, because nothing will bring the link back. Fires once, whether you closed it or the link failed for good. | | `close` | The session is over, because nothing will bring the link back. Fires once, whether you closed it or the link failed for good. |
| `disconnected` | The link dropped and the reconnect loop will retry it. Do not open a replacement client here — the session you hold comes back on its own, and `reconnected` says when. Fires again for each attempt that reconnects and then fails, so it is not one-to-one with `reconnected`. | | `disconnected` | The link dropped and the reconnect loop will retry it. Do not open a replacement client here — the session you hold comes back on its own, and `reconnected` says when. Fires again for each attempt that reconnects and then fails, so it is not one-to-one with `reconnected`. |
+1 -1
View File
@@ -130,7 +130,7 @@ export function parseReceipt(message: string): Receipt {
type MessageType = 'intermediate' | 'other' | 'receipt' | 'unmarked'; type MessageType = 'intermediate' | 'other' | 'receipt' | 'unmarked';
/** The types the far-end SME writes, rather than the MC reporting. The spec reserves the remaining ten. */ /** Written by the far-end SME, not by the MC reporting on a message we submitted. */
const smeMessageTypes: number[] = [ const smeMessageTypes: number[] = [
consts.ESM_CLASS.CONVERSATION_ABORT, consts.ESM_CLASS.CONVERSATION_ABORT,
consts.ESM_CLASS.DELIVERY_ACKNOWLEDGEMENT, consts.ESM_CLASS.DELIVERY_ACKNOWLEDGEMENT,
-2
View File
@@ -215,8 +215,6 @@ describe('merging segment statuses', () => {
}; };
} }
// Filling every slot with a non-final report merges early and spends the base, so the receipts
// that say what actually happened would report nothing.
test('never counts an intermediate report toward a merge', () => { test('never counts an intermediate report toward a merge', () => {
const merger = new DlrMerger({ log: silentLog, max: 10, now: () => 0, timeout: 60_000 }); const merger = new DlrMerger({ log: silentLog, max: 10, now: () => 0, timeout: 60_000 });