Compare commits
10 Commits
9326923b72
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0539fa6621 | ||
a9979def99 | |||
19ae430779 | |||
c873628abc | |||
ba0d9bf430 | |||
f8a8365f90 | |||
06c7f42bf4 | |||
927ac2edc9 | |||
f7976d3738 | |||
0799dc6a4d |
40
.eslintrc.js
40
.eslintrc.js
@@ -1,26 +1,24 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
env: {
|
||||||
"node": true,
|
node: true,
|
||||||
"commonjs": true,
|
commonjs: true,
|
||||||
"es2021": true
|
es2021: true,
|
||||||
|
"jest/globals": true,
|
||||||
},
|
},
|
||||||
"extends": "eslint:recommended",
|
extends: "eslint:recommended",
|
||||||
"overrides": [
|
overrides: [
|
||||||
{
|
{
|
||||||
"env": {
|
env: {
|
||||||
"node": true
|
node: true,
|
||||||
},
|
},
|
||||||
"files": [
|
files: [".eslintrc.{js,cjs}"],
|
||||||
".eslintrc.{js,cjs}"
|
parserOptions: {
|
||||||
],
|
sourceType: "script",
|
||||||
"parserOptions": {
|
|
||||||
"sourceType": "script"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": "latest"
|
|
||||||
},
|
},
|
||||||
"rules": {
|
},
|
||||||
}
|
],
|
||||||
}
|
parserOptions: {
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
},
|
||||||
|
rules: {},
|
||||||
|
};
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
out
|
out
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
10
.vscode/settings.json
vendored
10
.vscode/settings.json
vendored
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"sqltools.connections": [
|
|
||||||
{
|
|
||||||
"previewLimit": 50,
|
|
||||||
"driver": "SQLite",
|
|
||||||
"name": "1",
|
|
||||||
"database": "E:\\tmp\\output.db"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
53
center.js
53
center.js
@@ -3,7 +3,7 @@ const commandLineArgs = require("command-line-args");
|
|||||||
const commandLineUsage = require("command-line-usage");
|
const commandLineUsage = require("command-line-usage");
|
||||||
const NanoTimer = require("nanotimer");
|
const NanoTimer = require("nanotimer");
|
||||||
const { createBaseLogger, createSessionLogger } = require("./logger");
|
const { createBaseLogger, createSessionLogger } = require("./logger");
|
||||||
const { verifyDefaults, verifyExists } = require("./utils");
|
const { verifyDefaults, verifyExists, sendPdu } = require("./utils");
|
||||||
const { centerOptions } = require("./cliOptions");
|
const { centerOptions } = require("./cliOptions");
|
||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
const { MetricManager } = require("./metrics/metricManager");
|
const { MetricManager } = require("./metrics/metricManager");
|
||||||
@@ -28,7 +28,6 @@ if (options.help) {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
verifyDefaults(options, centerOptions);
|
verifyDefaults(options, centerOptions);
|
||||||
verifyExists(options.port, "Port can not be undefined or empty! (--port)", logger);
|
verifyExists(options.port, "Port can not be undefined or empty! (--port)", logger);
|
||||||
verifyExists(options.systemid, "SystemID can not be undefined or empty! (--systemid)", logger);
|
verifyExists(options.systemid, "SystemID can not be undefined or empty! (--systemid)", logger);
|
||||||
@@ -41,15 +40,16 @@ let failed = 0;
|
|||||||
const sendTimer = new NanoTimer();
|
const sendTimer = new NanoTimer();
|
||||||
const metricManager = new MetricManager(options);
|
const metricManager = new MetricManager(options);
|
||||||
|
|
||||||
// TODO: Fix issue where a client disconnecting does not stop this timer
|
// TODO: Currently bars are broken
|
||||||
// TODO: Fix issue where only one session is being utilized because they all share the same timer
|
// A major rework will need to happen before bars are able to play nice with multiple sessions
|
||||||
// TODO: Maybe add only receiver and only transmitter modes instead of transciever
|
// TODO: Maybe add only receiver and only transmitter modes instead of transciever
|
||||||
// Instead just use the same timer but make a pool of connections; That way both problems will be solved
|
// Instead just use the same timer but make a pool of connections; That way both problems will be solved
|
||||||
function startInterval(session, sessionLogger, rxMetrics) {
|
function startInterval(sessions, sessionLogger, rxMetrics) {
|
||||||
if (!options.messagecount > 0) {
|
if (!options.messagecount > 0) {
|
||||||
sessionLogger.info("No messages to send");
|
sessionLogger.info("No messages to send");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let sessionPointer = 0;
|
||||||
sendTimer.setInterval(
|
sendTimer.setInterval(
|
||||||
async () => {
|
async () => {
|
||||||
if (sent >= options.messagecount) {
|
if (sent >= options.messagecount) {
|
||||||
@@ -57,24 +57,26 @@ function startInterval(session, sessionLogger, rxMetrics) {
|
|||||||
sendTimer.clearInterval();
|
sendTimer.clearInterval();
|
||||||
} else if (inFlight < options.window) {
|
} else if (inFlight < options.window) {
|
||||||
sessionLogger.info(`Sending message ${sent + 1}/${options.messagecount}`);
|
sessionLogger.info(`Sending message ${sent + 1}/${options.messagecount}`);
|
||||||
session.deliver_sm(
|
const pdu = new smpp.PDU("deliver_sm", {
|
||||||
{
|
|
||||||
source_addr: options.source,
|
source_addr: options.source,
|
||||||
destination_addr: options.destination,
|
destination_addr: options.destination,
|
||||||
short_message: options.message,
|
short_message: options.message,
|
||||||
},
|
});
|
||||||
function (pdu) {
|
|
||||||
|
if (sessionPointer >= sessions.length) {
|
||||||
|
sessionPointer = 0;
|
||||||
|
}
|
||||||
|
sendPdu(sessions[sessionPointer++], pdu, sessionLogger, options.longsms)
|
||||||
|
.then((resp) => {
|
||||||
inFlight--;
|
inFlight--;
|
||||||
if (pdu.command_status === 0) {
|
sessionLogger.info(`Received response with id ${resp.message_id}`);
|
||||||
sessionLogger.info(`Received response with id ${pdu.message_id}`);
|
|
||||||
success++;
|
success++;
|
||||||
} else {
|
})
|
||||||
sessionLogger.warn(`Message failed with id ${pdu.message_id}`);
|
.catch((resp) => {
|
||||||
|
inFlight--;
|
||||||
|
sessionLogger.warn(`Message failed with id ${resp.message_id}`);
|
||||||
failed++;
|
failed++;
|
||||||
}
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
rxMetrics.AddEvent();
|
|
||||||
sent++;
|
sent++;
|
||||||
inFlight++;
|
inFlight++;
|
||||||
} else {
|
} else {
|
||||||
@@ -82,7 +84,7 @@ function startInterval(session, sessionLogger, rxMetrics) {
|
|||||||
`${inFlight}/${options.window} messages pending, waiting for a reply before sending more`
|
`${inFlight}/${options.window} messages pending, waiting for a reply before sending more`
|
||||||
);
|
);
|
||||||
sendTimer.clearInterval();
|
sendTimer.clearInterval();
|
||||||
setTimeout(() => startInterval(session, sessionLogger), options.windowsleep);
|
setTimeout(() => startInterval(sessions, sessionLogger), options.windowsleep);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"",
|
"",
|
||||||
@@ -93,6 +95,7 @@ function startInterval(session, sessionLogger, rxMetrics) {
|
|||||||
logger.info(`Staring server on port ${options.port}...`);
|
logger.info(`Staring server on port ${options.port}...`);
|
||||||
let sessionid = 1;
|
let sessionid = 1;
|
||||||
let messageid = 0;
|
let messageid = 0;
|
||||||
|
const sessions = [];
|
||||||
const server = smpp.createServer(
|
const server = smpp.createServer(
|
||||||
{
|
{
|
||||||
debug: options.debug,
|
debug: options.debug,
|
||||||
@@ -105,9 +108,10 @@ const server = smpp.createServer(
|
|||||||
|
|
||||||
session.on("bind_transceiver", function (pdu) {
|
session.on("bind_transceiver", function (pdu) {
|
||||||
if (pdu.system_id === options.systemid && pdu.password === options.password) {
|
if (pdu.system_id === options.systemid && pdu.password === options.password) {
|
||||||
sessionLogger.info("Client connected");
|
sessions.push(session);
|
||||||
|
sessionLogger.info(`Client connected, currently: ${sessions.length}`);
|
||||||
session.send(pdu.response());
|
session.send(pdu.response());
|
||||||
startInterval(session, sessionLogger);
|
startInterval(sessions, sessionLogger);
|
||||||
} else {
|
} else {
|
||||||
sessionLogger.warn(
|
sessionLogger.warn(
|
||||||
`Client tried to connect with incorrect login ('${pdu.system_id}' '${pdu.password}')`
|
`Client tried to connect with incorrect login ('${pdu.system_id}' '${pdu.password}')`
|
||||||
@@ -122,7 +126,7 @@ const server = smpp.createServer(
|
|||||||
if (pdu.system_id === options.systemid && pdu.password === options.password) {
|
if (pdu.system_id === options.systemid && pdu.password === options.password) {
|
||||||
sessionLogger.info("Client connected");
|
sessionLogger.info("Client connected");
|
||||||
session.send(pdu.response());
|
session.send(pdu.response());
|
||||||
startInterval(session, sessionLogger);
|
startInterval(session, sessionLogger, rxMetrics);
|
||||||
} else {
|
} else {
|
||||||
sessionLogger.warn(
|
sessionLogger.warn(
|
||||||
`Client tried to connect with incorrect login ('${pdu.system_id}' '${pdu.password}')`
|
`Client tried to connect with incorrect login ('${pdu.system_id}' '${pdu.password}')`
|
||||||
@@ -207,8 +211,13 @@ const server = smpp.createServer(
|
|||||||
});
|
});
|
||||||
|
|
||||||
session.on("close", function () {
|
session.on("close", function () {
|
||||||
sessionLogger.warn(`Session closed`);
|
sessions.splice(sessions.indexOf(session), 1);
|
||||||
|
sessionLogger.warn(`Session closed, now ${sessions.length}`);
|
||||||
session.close();
|
session.close();
|
||||||
|
if (sessions.length === 0) {
|
||||||
|
sessionLogger.info("No more sessions, stopping sending timer");
|
||||||
|
sendTimer.clearInterval();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
session.on("error", function (err) {
|
session.on("error", function (err) {
|
||||||
sessionLogger.error(`Fatal error ${err}`);
|
sessionLogger.error(`Fatal error ${err}`);
|
||||||
|
@@ -68,6 +68,11 @@ const clientOptions = [
|
|||||||
defaultOption: 1000,
|
defaultOption: 1000,
|
||||||
description: "Default max rate for metrics/bars."
|
description: "Default max rate for metrics/bars."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "longsms",
|
||||||
|
type: Boolean,
|
||||||
|
description: "Split messages into multiple parts. Applies only if message is too big for one packet."
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const centerOptions = [
|
const centerOptions = [
|
||||||
@@ -151,6 +156,11 @@ const centerOptions = [
|
|||||||
defaultOption: 1000,
|
defaultOption: 1000,
|
||||||
description: "Default max rate for metrics/bars."
|
description: "Default max rate for metrics/bars."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "longsms",
|
||||||
|
type: Boolean,
|
||||||
|
description: "Split messages into multiple parts. Applies only if message is too big for one packet."
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = { clientOptions, centerOptions };
|
module.exports = { clientOptions, centerOptions };
|
||||||
|
36
client.js
36
client.js
@@ -3,7 +3,7 @@ const commandLineArgs = require("command-line-args");
|
|||||||
const commandLineUsage = require("command-line-usage");
|
const commandLineUsage = require("command-line-usage");
|
||||||
const NanoTimer = require("nanotimer");
|
const NanoTimer = require("nanotimer");
|
||||||
const { createBaseLogger, createSessionLogger } = require("./logger");
|
const { createBaseLogger, createSessionLogger } = require("./logger");
|
||||||
const { verifyDefaults, verifyExists } = require("./utils");
|
const { verifyDefaults, verifyExists, sendPdu } = require("./utils");
|
||||||
const { clientOptions } = require("./cliOptions");
|
const { clientOptions } = require("./cliOptions");
|
||||||
const { MetricManager } = require("./metrics/metricManager");
|
const { MetricManager } = require("./metrics/metricManager");
|
||||||
|
|
||||||
@@ -58,26 +58,24 @@ function startInterval(session, sessionLogger, metrics) {
|
|||||||
metrics.progress.bar.increment();
|
metrics.progress.bar.increment();
|
||||||
metrics.window.bar.increment();
|
metrics.window.bar.increment();
|
||||||
}
|
}
|
||||||
session.submit_sm(
|
const pdu = new smpp.PDU("submit_sm", {
|
||||||
{
|
|
||||||
source_addr: options.source,
|
source_addr: options.source,
|
||||||
destination_addr: options.destination,
|
destination_addr: options.destination,
|
||||||
short_message: options.message,
|
short_message: options.message,
|
||||||
},
|
});
|
||||||
function (pdu) {
|
|
||||||
if (metrics.window?.bar) {
|
sendPdu(session, pdu, sessionLogger, options.longsms)
|
||||||
metrics.window.bar.update(metrics.window.bar.value - 1);
|
.then((resp) => {
|
||||||
}
|
|
||||||
inFlight--;
|
inFlight--;
|
||||||
if (pdu.command_status === 0) {
|
sessionLogger.info(`Received response with id ${resp.message_id}`);
|
||||||
sessionLogger.info(`Received response with id ${pdu.message_id}`);
|
|
||||||
success++;
|
success++;
|
||||||
} else {
|
})
|
||||||
sessionLogger.warn(`Message failed with id ${pdu.message_id}`);
|
.catch((resp) => {
|
||||||
|
inFlight--;
|
||||||
|
sessionLogger.warn(`Message failed with id ${resp.message_id}`);
|
||||||
failed++;
|
failed++;
|
||||||
}
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
if (metrics.txMetrics) {
|
if (metrics.txMetrics) {
|
||||||
metrics.txMetrics.AddEvent();
|
metrics.txMetrics.AddEvent();
|
||||||
}
|
}
|
||||||
@@ -109,6 +107,10 @@ for (let i = 0; i < options.sessions; i++) {
|
|||||||
sessionLogger.info(
|
sessionLogger.info(
|
||||||
`Connected, sending bind_transciever with systemId '${options.systemid}' and password '${options.password}'...`
|
`Connected, sending bind_transciever with systemId '${options.systemid}' and password '${options.password}'...`
|
||||||
);
|
);
|
||||||
|
session.on('close', function () {
|
||||||
|
sessionLogger.error(`Session closed`);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
session.bind_transceiver(
|
session.bind_transceiver(
|
||||||
{
|
{
|
||||||
system_id: options.systemid,
|
system_id: options.systemid,
|
||||||
@@ -125,7 +127,6 @@ for (let i = 0; i < options.sessions; i++) {
|
|||||||
rxMetrics,
|
rxMetrics,
|
||||||
txMetrics,
|
txMetrics,
|
||||||
});
|
});
|
||||||
// TODO: Add error message for invalid systemid and password
|
|
||||||
|
|
||||||
session.on("deliver_sm", function (pdu) {
|
session.on("deliver_sm", function (pdu) {
|
||||||
if (rxMetrics) {
|
if (rxMetrics) {
|
||||||
@@ -152,6 +153,9 @@ for (let i = 0; i < options.sessions; i++) {
|
|||||||
sessionLogger.error(`Fatal error ${err}`);
|
sessionLogger.error(`Fatal error ${err}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
sessionLogger.error(`Failed to bind, status ${pdu.command_status}`);
|
||||||
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -8,7 +8,8 @@
|
|||||||
"build-windows-client": "nexe -i client.js -o out/client-windows -t windows-x86-18.18.2",
|
"build-windows-client": "nexe -i client.js -o out/client-windows -t windows-x86-18.18.2",
|
||||||
"build-linux-server": "nexe -i server.js -o out/server-linux -t linux-x64-18.18.2",
|
"build-linux-server": "nexe -i server.js -o out/server-linux -t linux-x64-18.18.2",
|
||||||
"build-windows-server": "nexe -i server.js -o out/server-windows -t windows-x86-18.18.2",
|
"build-windows-server": "nexe -i server.js -o out/server-windows -t windows-x86-18.18.2",
|
||||||
"build": "sh build.sh"
|
"build": "sh build.sh",
|
||||||
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
@@ -22,6 +23,8 @@
|
|||||||
"winston": "^3.11.0"
|
"winston": "^3.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.53.0"
|
"@types/jest": "^29.5.10",
|
||||||
|
"eslint": "^8.55.0",
|
||||||
|
"jest": "^29.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2040
pnpm-lock.yaml
generated
2040
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
94
tests/utils.test.js
Normal file
94
tests/utils.test.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
const smpp = require("smpp");
|
||||||
|
const { splitToParts, verifyExists, getCharacterSizeForEncoding } = require("../utils");
|
||||||
|
|
||||||
|
describe("splitToParts", () => {
|
||||||
|
// A pdu is expected to be one part if it has less than 160 characters and is encoded using GSM7 (data_coding = null or 0)
|
||||||
|
// Given a pdu with short_message length less than 160 chars, it should return an array with a single pdu.
|
||||||
|
it("should return an array with a single pdu when short_message length is less than or equal to maxMessageSizeBits", () => {
|
||||||
|
const pdu = new smpp.PDU("deliver_sm", {
|
||||||
|
short_message: "test message",
|
||||||
|
});
|
||||||
|
const result = splitToParts(pdu);
|
||||||
|
expect(result.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Given a pdu with short_message length greater than 160 chars, it should return an array with 2 pdus.
|
||||||
|
it("should return an array with two pdus when short_message length is greater than maxMessageSizeBits and less than or equal to maxMessageSizeBits * 2", () => {
|
||||||
|
const pdu = new smpp.PDU("deliver_sm", {
|
||||||
|
short_message: "c".repeat(200),
|
||||||
|
});
|
||||||
|
const result = splitToParts(pdu);
|
||||||
|
expect(result.length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Given a pdu with short_message length greater than 320 chars, it should return an array with 2 pdus.
|
||||||
|
it("should return an array with three pdus when short_message length is greater than maxMessageSizeBits * 2 and less than or equal to maxMessageSizeBits * 3", () => {
|
||||||
|
const pdu = new smpp.PDU("deliver_sm", {
|
||||||
|
short_message: "c".repeat(400),
|
||||||
|
});
|
||||||
|
const result = splitToParts(pdu);
|
||||||
|
expect(result.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Given a pdu with short_message length equal to 0, it should return an empty array.
|
||||||
|
it("should return an empty array when short_message length is equal to 0", () => {
|
||||||
|
const pdu = new smpp.PDU("deliver_sm", {
|
||||||
|
short_message: "",
|
||||||
|
});
|
||||||
|
const result = splitToParts(pdu);
|
||||||
|
expect(result.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Given a pdu with short_message length equal to 320, it should return an array with two pdus.
|
||||||
|
it("should return an array with two pdus when short_message length is equal to maxMessageSizeBits", () => {
|
||||||
|
const pdu = new smpp.PDU("deliver_sm", {
|
||||||
|
short_message: "c".repeat(320),
|
||||||
|
});
|
||||||
|
const result = splitToParts(pdu);
|
||||||
|
expect(result.length).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getCharacterSizeForEncoding", () => {
|
||||||
|
// Returns 7 when data_coding is 0
|
||||||
|
it("should return 7 when data_coding is 0", () => {
|
||||||
|
const pdu = { data_coding: 0 };
|
||||||
|
const result = getCharacterSizeForEncoding(pdu);
|
||||||
|
expect(result).toBe(7);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns 8 when data_coding is 1
|
||||||
|
it("should return 8 when data_coding is 1", () => {
|
||||||
|
const pdu = { data_coding: 1 };
|
||||||
|
const result = getCharacterSizeForEncoding(pdu);
|
||||||
|
expect(result).toBe(8);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns 16 when data_coding is 8
|
||||||
|
it("should return 16 when data_coding is 8", () => {
|
||||||
|
const pdu = { data_coding: 8 };
|
||||||
|
const result = getCharacterSizeForEncoding(pdu);
|
||||||
|
expect(result).toBe(16);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns 7 when data_coding is null
|
||||||
|
it("should return 0 when data_coding is null", () => {
|
||||||
|
const pdu = { data_coding: null };
|
||||||
|
const result = getCharacterSizeForEncoding(pdu);
|
||||||
|
expect(result).toBe(7);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns 0 when data_coding is not a number
|
||||||
|
it("should return 0 when data_coding is not a number", () => {
|
||||||
|
const pdu = { data_coding: "abc" };
|
||||||
|
const result = getCharacterSizeForEncoding(pdu);
|
||||||
|
expect(result).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns 0 when data_coding is negative
|
||||||
|
it("should return 0 when data_coding is negative", () => {
|
||||||
|
const pdu = { data_coding: -1 };
|
||||||
|
const result = getCharacterSizeForEncoding(pdu);
|
||||||
|
expect(result).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
89
utils.js
89
utils.js
@@ -1,3 +1,5 @@
|
|||||||
|
const smpp = require("smpp");
|
||||||
|
|
||||||
function verifyExists(value, err, logger) {
|
function verifyExists(value, err, logger) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
@@ -14,4 +16,89 @@ function verifyDefaults(options, definitions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { verifyDefaults, verifyExists };
|
function getCharacterSizeForEncoding(pdu) {
|
||||||
|
let encoding = pdu.data_coding;
|
||||||
|
if (!encoding) {
|
||||||
|
encoding = 0;
|
||||||
|
}
|
||||||
|
let characterSizeBits = 0;
|
||||||
|
switch (encoding) {
|
||||||
|
case 0:
|
||||||
|
characterSizeBits = 7;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
characterSizeBits = 8;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
characterSizeBits = 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return characterSizeBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxMessageSizeBits = 1120;
|
||||||
|
function splitToParts(pdu) {
|
||||||
|
const charSize = getCharacterSizeForEncoding(pdu);
|
||||||
|
const maxMessageLength = maxMessageSizeBits / charSize;
|
||||||
|
|
||||||
|
const splitMessage = [];
|
||||||
|
const message = pdu.short_message;
|
||||||
|
const messageLength = message.length;
|
||||||
|
const messageCount = (messageLength / maxMessageLength) | 0;
|
||||||
|
for (let i = 0; i < messageCount; i++) {
|
||||||
|
splitMessage.push(message.slice(i * maxMessageLength, i * maxMessageLength + maxMessageLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
const pdus = splitMessage.map((messagePart, index) => {
|
||||||
|
let udh = Buffer.from([0x05, 0x00, 0x03, this.iterator++, messageCount, index + 1]);
|
||||||
|
|
||||||
|
let partPdu = new smpp.PDU(pdu.command, { ...pdu });
|
||||||
|
partPdu.short_message = {
|
||||||
|
udh: udh,
|
||||||
|
message: messagePart,
|
||||||
|
};
|
||||||
|
return partPdu;
|
||||||
|
});
|
||||||
|
return pdus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add "uselongsms" switch to options;
|
||||||
|
async function sendPdu(session, pdu, logger, uselongsms) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (uselongsms) {
|
||||||
|
const pdus = splitToParts(pdu);
|
||||||
|
logger.info(`Sending long sms of ${pdus.length} parts`);
|
||||||
|
const total = pdus.length;
|
||||||
|
let success = 0;
|
||||||
|
let failed = 0;
|
||||||
|
const promises = pdus.map((pdu) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
session.send(pdu, (respPdu) => {
|
||||||
|
if (respPdu.command_status === 0) {
|
||||||
|
resolve(respPdu);
|
||||||
|
} else {
|
||||||
|
reject(respPdu);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Promise.all(promises)
|
||||||
|
.then((responses) => {
|
||||||
|
resolve(responses[0]);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
session.send(pdu, (respPdu) => {
|
||||||
|
if (respPdu.command_status === 0) {
|
||||||
|
resolve(respPdu);
|
||||||
|
} else {
|
||||||
|
reject(respPdu);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { verifyDefaults, verifyExists, sendPdu, splitToParts, getCharacterSizeForEncoding };
|
||||||
|
Reference in New Issue
Block a user