Compare commits
10 Commits
d1b6caf597
...
4a5aa484ba
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4a5aa484ba | ||
a18ac0355c | |||
a1e7d3f885 | |||
a492b3bd72 | |||
0e016438a1 | |||
5a5debb249 | |||
7d574e3162 | |||
03716404f0 | |||
![]() |
85dfc6f565 | ||
![]() |
9c755d3cda |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,3 +6,6 @@ client_sessions.json
|
|||||||
center_sessions.json
|
center_sessions.json
|
||||||
dist
|
dist
|
||||||
main.exe
|
main.exe
|
||||||
|
.run
|
||||||
|
.vscode
|
||||||
|
build
|
||||||
|
4
.prettierrc
Normal file
4
.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"tabWidth": 4,
|
||||||
|
"useTabs": true
|
||||||
|
}
|
@@ -1,18 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="main.ts" type="TypeScriptProgramRunner" factoryName="TypeScript">
|
|
||||||
<module name="smsgwtester" />
|
|
||||||
<envs>
|
|
||||||
<env name="LOG_LEVEL" value="0" />
|
|
||||||
</envs>
|
|
||||||
<option name="interpreterRef" value="project" />
|
|
||||||
<option name="enabledTsNodeEsmLoader" value="false" />
|
|
||||||
<option name="interpreterOptions" value="" />
|
|
||||||
<option name="workingDirectory" value="C:\Users\Administrator\WebstormProjects\smsgwtester\src" />
|
|
||||||
<option name="tsconfigFile" value="" />
|
|
||||||
<option name="extraTypeScriptOptions" value="" />
|
|
||||||
<option name="scriptName" value="$PROJECT_DIR$/src/main.ts" />
|
|
||||||
<option name="programParameters" value="" />
|
|
||||||
<option name="tsnodePackage" value="~\WebstormProjects\smsgwtester\node_modules\ts-node" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
@@ -1,14 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="pkg:windows" type="js.build_tools.npm" nameIsGenerated="true">
|
|
||||||
<package-json value="$PROJECT_DIR$/package.json" />
|
|
||||||
<command value="run" />
|
|
||||||
<scripts>
|
|
||||||
<script value="pkg:windows" />
|
|
||||||
</scripts>
|
|
||||||
<node-interpreter value="project" />
|
|
||||||
<envs />
|
|
||||||
<method v="2">
|
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="tsc" run_configuration_type="js.build_tools.npm" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
@@ -1,12 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="tsc" type="js.build_tools.npm" nameIsGenerated="true">
|
|
||||||
<package-json value="$PROJECT_DIR$/package.json" />
|
|
||||||
<command value="run" />
|
|
||||||
<scripts>
|
|
||||||
<script value="tsc" />
|
|
||||||
</scripts>
|
|
||||||
<node-interpreter value="project" />
|
|
||||||
<envs />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY "./main.exe/" "./"
|
||||||
|
|
||||||
|
EXPOSE 6555
|
||||||
|
EXPOSE 6556
|
||||||
|
|
||||||
|
ENTRYPOINT "./main.exe"
|
147
README.md
Normal file
147
README.md
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# smsgw-tester-api
|
||||||
|
|
||||||
|
#### This application is meant to be used for performance or functionality testing smpp processing systems such as sms centers, clients or sms gateways.
|
||||||
|
|
||||||
|
The application is capable of hosting any number of smpp clients or centers. The centers automatically accept any smpp connections while the clients can be connected/bound/disconnected at will.
|
||||||
|
|
||||||
|
---
|
||||||
|
## General use
|
||||||
|
|
||||||
|
The api is meant to be used along with the [web application](https://github.com/PhatDave/smsgw-tester-web) but can also be used standalone via http requests. The request examples can be found in the insomnia export file (although they are outdated as of 1.0).
|
||||||
|
|
||||||
|
The web view consists of a Client and Center segment. Each entity has it's 3 parameters (for a client those are the url [the client connects to], the username and password, for the center those are the port [the center listens on], username and password) of which 2 are modifiable (username and password).
|
||||||
|
|
||||||
|
Each entity can be configured to send one smpp message or multiple smpp messages at a given rate (of messages per second).
|
||||||
|
|
||||||
|
Each entity also includes a live graph representing the incoming and outgoing traffic.
|
||||||
|
|
||||||
|
Each entity also supports a set of pre and post processors that in some way modify the incoming or outgoing pdu. These processors will be described in the processor segment.
|
||||||
|
|
||||||
|
Entities can also be deleted by **double clicking** the delete button.
|
||||||
|
|
||||||
|
Currently it is not possible to temporarily disable entities but this is planned for a future release (as of 1.0).
|
||||||
|
|
||||||
|
### Center modes of operation
|
||||||
|
|
||||||
|
Center entities are "special" in the sense that they can have a few different modes of operation.
|
||||||
|
|
||||||
|
As of 1.0 the following modes are implemented (and later described in the center postprocessor segment): "Debug", Echo and DeliveryReport.
|
||||||
|
|
||||||
|
- **Debug mode** (1.0)
|
||||||
|
- Only "Deliver\_sm Reply" is enabled, the center does not reply to messages with any other messages and only acknowledges the ones delivered to it.
|
||||||
|
- **Echo mode** (1.0)
|
||||||
|
- "Echo PDU" is enabled, the center replies to messages with a copy of the received message whose source and destination fields have been swapped.
|
||||||
|
- For example a center receiving a message (src:123, dst:321) will reply to it with a message (src:321, dst:123) where the text of the message is the same as the text of the received message.
|
||||||
|
- **Delivery Report mode** (1.0)
|
||||||
|
- "Delivery Receipt" is enabled, the center replies to messages with delivery report. You can learn more about delivery reports [here](https://smpp.org/smpp-delivery-receipt.html)
|
||||||
|
- Details of the implementation can be found in the last segment, Delivery Reports
|
||||||
|
|
||||||
|
Enabling multiple modes at once will have the center send more than one reply message at once. For example enabling echo mode and delivery report mode will have the center reply with an echo message and a delivery report at the same time.
|
||||||
|
|
||||||
|
---
|
||||||
|
## Processors
|
||||||
|
|
||||||
|
Processors handle the majority of this application's functionality. On the web application active processors are highlighted green and they can be toggled by clicking on the appropriate button.
|
||||||
|
|
||||||
|
Preprocessors generally do something with the message before it is sent while postprocessors generally reply to a message.
|
||||||
|
|
||||||
|
These are the available pre and post processors per entity:
|
||||||
|
|
||||||
|
### Client Preprocessors
|
||||||
|
|
||||||
|
#### Destination & Source Enumerator (1.0)
|
||||||
|
|
||||||
|
These processors append an incrementing 4 digit number to the end of either the source or destination. For example given a destination of "3851728381" and destinationEnumerator toggled on, sending 5 messages would have their destinations be:
|
||||||
|
- 38517283810000
|
||||||
|
- 38517283810001
|
||||||
|
- 38517283810002
|
||||||
|
- 38517283810003
|
||||||
|
- 38517283810004
|
||||||
|
|
||||||
|
The functionality is identical for source enumerator.
|
||||||
|
|
||||||
|
#### Delivery Receipt Request (1.0)
|
||||||
|
|
||||||
|
This preprocessor adds a field to the pdu (registered\_delivery) and sets it to 1. This signals to the smsc that a delivery report is requested to this message.
|
||||||
|
|
||||||
|
#### Long SMS (1.0)
|
||||||
|
|
||||||
|
With this preprocessor enabled the messages body is chopped up into segments based on the encoding and maximum size of an smpp message given that encoding and each segment is sent separately. (Message segment information is set in the form of udh)
|
||||||
|
|
||||||
|
**With this preprocessor disabled any message whose body exceeds the maximum smpp message size is truncated to size**.
|
||||||
|
|
||||||
|
#### Protocol ID (+2/3 Digit Versions) (1.2)
|
||||||
|
|
||||||
|
This processor sets the protocol_id field to a value.
|
||||||
|
The basic one (ProtocolIdProcessor) sets the value to 1, 2Digit version to 16 and 3Digit version to 128.
|
||||||
|
|
||||||
|
#### Source/Destination Set Processor
|
||||||
|
|
||||||
|
Generates X random numbers that are then used for sending Y messages.
|
||||||
|
The interface with this processor is somewhat specific: To specify the amount of msisdns generated prepend "arg:X;" to the message body.
|
||||||
|
Example: message "arg:100;test123" generates 100 msisdns and sends messages with the body of "test123".
|
||||||
|
The point of this processor is to generate a set of msisdns to use with a greater number of messages.
|
||||||
|
For example sending 100k messages from 100 msisdns (effectively sending 100 messages per msisdn) to simulate 100 users.
|
||||||
|
Note, the picked msisdn per message is random (from the generated set).
|
||||||
|
|
||||||
|
### Center Preprocessors
|
||||||
|
|
||||||
|
#### Destination & Source Enumerator (1.2)
|
||||||
|
|
||||||
|
Same as the Client version.
|
||||||
|
|
||||||
|
#### Long SMS (1.2)
|
||||||
|
|
||||||
|
Same as the Client version.
|
||||||
|
|
||||||
|
#### Protocol ID (+2/3 Digit Versions) (1.2)
|
||||||
|
|
||||||
|
Same as the Client version.
|
||||||
|
|
||||||
|
#### Source/Destination Set Processor (1.2)
|
||||||
|
|
||||||
|
Same as the Client version.
|
||||||
|
|
||||||
|
### Client Postprocessors
|
||||||
|
|
||||||
|
#### Deliver\_sm Reply (1.0)
|
||||||
|
|
||||||
|
This postprocessor should never be disabled (unless you know what you're doing). It enables the client to reply to deliver\_sm pdus. (deliver\_sm -> deliver\_sm\_resp)
|
||||||
|
|
||||||
|
|
||||||
|
### Center Postprocessors
|
||||||
|
|
||||||
|
#### Bind Transciever Reply (1.0)
|
||||||
|
|
||||||
|
This postprocessor should never be disabled (unless you know what you're doing). It enables client authentication. (By replying to bind\_transceiver pdus)
|
||||||
|
|
||||||
|
#### Submit\_sm Reply (1.0)
|
||||||
|
|
||||||
|
This postprocessor should never be disabled (unless you know what you're doing). It enables the center to reply to submit\_sm pdus. (submit\_sm -> submit\_sm\_resp)
|
||||||
|
|
||||||
|
#### Enquire Link Reply (1.0)
|
||||||
|
|
||||||
|
This postprocessor should never be disabled (unless you know what you're doing). It replies to the clients "heartbeat" (the enquire\_link pdu). (enquire\_link -> enquire\_link\_resp)
|
||||||
|
|
||||||
|
#### Echo PDU (1.0)
|
||||||
|
|
||||||
|
This is the first "real" postprocessor for center entities. It is one of 3 (as of 1.0) choices of center operation.
|
||||||
|
|
||||||
|
The "Echo" mode of operation has the center reply to any submit\_sm with a deliver\_sm whose message body is the same (as the submit\_sm) and source and destination swapped.
|
||||||
|
|
||||||
|
For example, sending a submit\_sm with the source of "1234" and a destination of "4321" and text of "test123" will have the center reply with a deliver\_sm with the source of "4321" and destination of "1234" with the text "test123".
|
||||||
|
|
||||||
|
#### Delivery Receipt (1.0)
|
||||||
|
|
||||||
|
The "most important" postprocessor for an smsc the delivery receipt postprocessor handles generating and sending delivery reports. You can read more about delivery reports in the Delivery Reports section.
|
||||||
|
|
||||||
|
---
|
||||||
|
## Delivery Reports
|
||||||
|
|
||||||
|
Upon receiving a submit_sm on the center whose "Delivery Receipt" postprocessor has been enabled it:
|
||||||
|
- Checks whether the pdus "registered_delivery" field is set
|
||||||
|
- Generates a delivery report in the form of `id:<messageId> sub:001 dlvrd:001 submit date:<date> done date:<date> stat:DELIVERD err:000 text:`
|
||||||
|
- The messageId here is the one id returned in the submit_sm_resp
|
||||||
|
- The date is simply the current date as of generation of the DR
|
||||||
|
- Sets the newly generated pdu esm class to 04
|
||||||
|
- Sends the pdu (delivery report) back to the client
|
11
package.json
11
package.json
@@ -3,21 +3,26 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"tsc": "tsc",
|
"dev": "tsup && node build/main.cjs",
|
||||||
"pkg:windows": "pkg ./dist/main.js --target node16-windows-x64 --output main.exe",
|
"pkg:windows": "pkg ./dist/main.js --target node16-windows-x64 --output main.exe",
|
||||||
"pkg:linux": "pkg ./dist/main.js --target node16-linux-x64 --output main.exe"
|
"pkg:linux": "pkg ./dist/main.js --target node16-linux-x64 --output main.exe"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.4.9",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
|
"cors": "^2.8.5",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"nanotimer": "^0.3.15",
|
"nanotimer": "^0.3.15",
|
||||||
"smpp": "^0.6.0-rc.4",
|
"smpp": "0.6.0-rc.4",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
|
"tsup": "^7.2.0",
|
||||||
|
"typescript": "^5.1.6",
|
||||||
"ws": "^8.13.0",
|
"ws": "^8.13.0",
|
||||||
"zlib": "^1.0.5"
|
"zlib": "^1.0.5"
|
||||||
}
|
}
|
||||||
|
1705
pnpm-lock.yaml
generated
Normal file
1705
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
smpp_tester_options.json
Normal file
32
smpp_tester_options.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"url": "smpp://localhost:4445",
|
||||||
|
"connection_count": 8,
|
||||||
|
"system_id": "snt",
|
||||||
|
"password": "snt",
|
||||||
|
"source_addr": "44455",
|
||||||
|
"destination_addr": "test123",
|
||||||
|
"short_message": "SNT BENCHMARK!",
|
||||||
|
"message_count": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "smpp://172.17.77.204:4445",
|
||||||
|
"connection_count": 8,
|
||||||
|
"system_id": "snt",
|
||||||
|
"password": "snt",
|
||||||
|
"source_addr": "44455",
|
||||||
|
"destination_addr": "test123",
|
||||||
|
"short_message": "SNT BENCHMARK!",
|
||||||
|
"message_count": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "smpp://172.17.77.14:4445",
|
||||||
|
"connection_count": 8,
|
||||||
|
"system_id": "snt",
|
||||||
|
"password": "snt",
|
||||||
|
"source_addr": "44455",
|
||||||
|
"destination_addr": "test123",
|
||||||
|
"short_message": "SNT BENCHMARK!",
|
||||||
|
"message_count": 100
|
||||||
|
}
|
||||||
|
]
|
@@ -43,9 +43,9 @@ export default class Center extends SmppSession {
|
|||||||
this._defaultSingleJob = Job.createEmptySingle('deliver_sm');
|
this._defaultSingleJob = Job.createEmptySingle('deliver_sm');
|
||||||
this._defaultMultipleJob = Job.createEmptyMultiple('deliver_sm');
|
this._defaultMultipleJob = Job.createEmptyMultiple('deliver_sm');
|
||||||
|
|
||||||
ProcessorManager.attachProcessor(this, ProcessorManager.getProcessor(SubmitSmReplyProcessor.name));
|
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(SubmitSmReplyProcessor.name));
|
||||||
ProcessorManager.attachProcessor(this, ProcessorManager.getProcessor(BindTranscieverReplyProcessor.name));
|
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(BindTranscieverReplyProcessor.name));
|
||||||
ProcessorManager.attachProcessor(this, ProcessorManager.getProcessor(EnquireLinkReplyProcessor.name));
|
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(EnquireLinkReplyProcessor.name));
|
||||||
|
|
||||||
this.logger = new Logger(`Center-${id}`);
|
this.logger = new Logger(`Center-${id}`);
|
||||||
|
|
||||||
@@ -83,10 +83,10 @@ export default class Center extends SmppSession {
|
|||||||
if (!force) {
|
if (!force) {
|
||||||
this.validateSessions(reject);
|
this.validateSessions(reject);
|
||||||
}
|
}
|
||||||
this.logger.log5(`Center-${this.id} sending PDU: ${JSON.stringify(pdu)}`);
|
|
||||||
let pduCopy = new smpp.PDU(pdu.command, {...pdu});
|
let pduCopy = new smpp.PDU(pdu.command, {...pdu});
|
||||||
let session = this.getNextSession();
|
let session = this.getNextSession();
|
||||||
this.processors.Preprocessor.forEach((processor: PduProcessor) => processor.processPdu(session, pduCopy, this));
|
this.processors.Preprocessor.forEach((processor: PduProcessor) => processor.processPdu(session, pduCopy, this));
|
||||||
|
this.logger.log5(`Center-${this.id} sending PDU: ${JSON.stringify(pduCopy)}`);
|
||||||
this.doSendPdu(pduCopy, session).then((replyPdu: any) => {
|
this.doSendPdu(pduCopy, session).then((replyPdu: any) => {
|
||||||
resolve(replyPdu);
|
resolve(replyPdu);
|
||||||
});
|
});
|
||||||
@@ -148,20 +148,9 @@ export default class Center extends SmppSession {
|
|||||||
this.server.close();
|
this.server.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): object {
|
postSerialize(obj: any): object {
|
||||||
return {
|
obj.port = this.port;
|
||||||
id: this._id,
|
return obj;
|
||||||
port: this.port,
|
|
||||||
username: this._username,
|
|
||||||
password: this._password,
|
|
||||||
status: this._status,
|
|
||||||
defaultSingleJob: this._defaultSingleJob.serialize(),
|
|
||||||
defaultMultipleJob: this._defaultMultipleJob.serialize(),
|
|
||||||
preprocessors: this.processors.Preprocessor.map((p: PduProcessor) => p.serialize()),
|
|
||||||
postprocessors: this.processors.Postprocessor.map((p: PduProcessor) => p.serialize()),
|
|
||||||
availablePreprocessors: ProcessorManager.getPreprocessorsForType(this.constructor.name).map((p: PduProcessor) => p.serialize()),
|
|
||||||
availablePostprocessors: ProcessorManager.getPostprocessorsForType(this.constructor.name).map((p: PduProcessor) => p.serialize()),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStatus(): void {
|
updateStatus(): void {
|
||||||
|
@@ -42,7 +42,7 @@ export default class Client extends SmppSession {
|
|||||||
this._defaultSingleJob = Job.createEmptySingle('submit_sm');
|
this._defaultSingleJob = Job.createEmptySingle('submit_sm');
|
||||||
this._defaultMultipleJob = Job.createEmptyMultiple('submit_sm');
|
this._defaultMultipleJob = Job.createEmptyMultiple('submit_sm');
|
||||||
|
|
||||||
ProcessorManager.attachProcessor(this, ProcessorManager.getProcessor(DeliverSmReplyProcessor.name));
|
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(DeliverSmReplyProcessor.name));
|
||||||
|
|
||||||
this.logger = new Logger(`Client-${id}`);
|
this.logger = new Logger(`Client-${id}`);
|
||||||
}
|
}
|
||||||
@@ -116,21 +116,9 @@ export default class Client extends SmppSession {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): object {
|
postSerialize(obj: any): object {
|
||||||
// TODO: Generify this further by moving it to smpp session and creating a... "postSerialize" that is abstract
|
obj.url = this.url;
|
||||||
return {
|
return obj;
|
||||||
id: this._id,
|
|
||||||
url: this.url,
|
|
||||||
username: this._username,
|
|
||||||
password: this._password,
|
|
||||||
status: this._status,
|
|
||||||
defaultSingleJob: this._defaultSingleJob.serialize(),
|
|
||||||
defaultMultipleJob: this._defaultMultipleJob.serialize(),
|
|
||||||
preprocessors: this.processors.Preprocessor.map((p: PduProcessor) => p.serialize()),
|
|
||||||
postprocessors: this.processors.Postprocessor.map((p: PduProcessor) => p.serialize()),
|
|
||||||
availablePreprocessors: ProcessorManager.getPreprocessorsForType(this.constructor.name).map((p: PduProcessor) => p.serialize()),
|
|
||||||
availablePostprocessors: ProcessorManager.getPostprocessorsForType(this.constructor.name).map((p: PduProcessor) => p.serialize()),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(): Promise<void> {
|
close(): Promise<void> {
|
||||||
|
@@ -31,16 +31,16 @@ export default class CenterRequestHandler extends RequestHandler {
|
|||||||
|
|
||||||
doAddProcessor(req: any, res: any): void {
|
doAddProcessor(req: any, res: any): void {
|
||||||
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
|
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
|
||||||
let processor: PduProcessor = ProcessorManager.getProcessor(req.body.name);
|
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
|
||||||
ProcessorManager.attachProcessor(session, processor);
|
ProcessorManager.attachProcessors(session, processors);
|
||||||
res.send(session.serialize());
|
res.send(session.serialize());
|
||||||
}, this.handleSessionNotFound.bind(this, req, res));
|
}, this.handleSessionNotFound.bind(this, req, res));
|
||||||
}
|
}
|
||||||
|
|
||||||
doRemoveProcessor(req: any, res: any): void {
|
doRemoveProcessor(req: any, res: any): void {
|
||||||
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
|
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
|
||||||
let processor: PduProcessor = ProcessorManager.getProcessor(req.body.name);
|
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
|
||||||
ProcessorManager.detachProcessor(session, processor);
|
ProcessorManager.detachProcessors(session, processors);
|
||||||
res.send(session.serialize());
|
res.send(session.serialize());
|
||||||
}, this.handleSessionNotFound.bind(this, req, res));
|
}, this.handleSessionNotFound.bind(this, req, res));
|
||||||
}
|
}
|
||||||
|
@@ -31,16 +31,16 @@ export default class ClientRequestHandler extends RequestHandler {
|
|||||||
|
|
||||||
doAddProcessor(req: any, res: any): void {
|
doAddProcessor(req: any, res: any): void {
|
||||||
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
|
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
|
||||||
let processor: PduProcessor = ProcessorManager.getProcessor(req.body.name);
|
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
|
||||||
ProcessorManager.attachProcessor(session, processor);
|
ProcessorManager.attachProcessors(session, processors);
|
||||||
res.send(session.serialize());
|
res.send(session.serialize());
|
||||||
}, this.handleSessionNotFound.bind(this, req, res));
|
}, this.handleSessionNotFound.bind(this, req, res));
|
||||||
}
|
}
|
||||||
|
|
||||||
doRemoveProcessor(req: any, res: any): void {
|
doRemoveProcessor(req: any, res: any): void {
|
||||||
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
|
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
|
||||||
let processor: PduProcessor = ProcessorManager.getProcessor(req.body.name);
|
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
|
||||||
ProcessorManager.detachProcessor(session, processor);
|
ProcessorManager.detachProcessors(session, processors);
|
||||||
res.send(session.serialize());
|
res.send(session.serialize());
|
||||||
}, this.handleSessionNotFound.bind(this, req, res));
|
}, this.handleSessionNotFound.bind(this, req, res));
|
||||||
}
|
}
|
||||||
|
@@ -8,8 +8,9 @@ const express = require("express");
|
|||||||
const bodyParser = require("body-parser");
|
const bodyParser = require("body-parser");
|
||||||
const compression = require("compression");
|
const compression = require("compression");
|
||||||
const zlib = require("zlib");
|
const zlib = require("zlib");
|
||||||
|
const cors = require("cors");
|
||||||
|
|
||||||
const SERVER_PORT: number = Number(process.env.SERVER_PORT) || 80;
|
const SERVER_PORT: number = Number(process.env.SERVER_PORT) || 8190;
|
||||||
|
|
||||||
export default class HttpServer {
|
export default class HttpServer {
|
||||||
private readonly clientRequestHandler: RequestHandler;
|
private readonly clientRequestHandler: RequestHandler;
|
||||||
@@ -24,6 +25,7 @@ export default class HttpServer {
|
|||||||
this.centerRequestHandler = new CenterRequestHandler(centerManager);
|
this.centerRequestHandler = new CenterRequestHandler(centerManager);
|
||||||
|
|
||||||
this.app = express();
|
this.app = express();
|
||||||
|
this.app.use(cors());
|
||||||
this.app.use(bodyParser.json());
|
this.app.use(bodyParser.json());
|
||||||
|
|
||||||
this.app.use(compression({
|
this.app.use(compression({
|
||||||
|
@@ -6,12 +6,17 @@ export default abstract class PduProcessor {
|
|||||||
readonly sessionType: string;
|
readonly sessionType: string;
|
||||||
readonly name: string = this.constructor.name;
|
readonly name: string = this.constructor.name;
|
||||||
readonly logger: Logger = new Logger(`PduProcessor: ${this.name}`);
|
readonly logger: Logger = new Logger(`PduProcessor: ${this.name}`);
|
||||||
|
abstract applicableCommands: string[];
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
this.sessionType = type;
|
this.sessionType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract processPdu(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any>;
|
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): any {
|
||||||
|
if (this.pduDoesApply(pdu)) {
|
||||||
|
return this.doProcess(session, pdu, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serialize(): object {
|
serialize(): object {
|
||||||
return {
|
return {
|
||||||
@@ -20,4 +25,13 @@ export default abstract class PduProcessor {
|
|||||||
type: this.type
|
type: this.type
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected pduDoesApply(pdu: any): boolean {
|
||||||
|
if (pdu.command) {
|
||||||
|
return this.applicableCommands.includes(pdu.command);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract doProcess(session: any, pdu: any, entity?: SmppSession | undefined): any;
|
||||||
}
|
}
|
@@ -4,18 +4,19 @@ import Postprocessor from "../Postprocessor";
|
|||||||
const smpp = require("smpp");
|
const smpp = require("smpp");
|
||||||
|
|
||||||
export default class BindTranscieverReplyProcessor extends Postprocessor {
|
export default class BindTranscieverReplyProcessor extends Postprocessor {
|
||||||
|
applicableCommands: string[] = ['bind_transceiver'];
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPdu(session: any, pdu: any, entity?: Center | undefined): Promise<any> {
|
doProcess(session: any, pdu: any, entity?: Center | undefined): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!!pdu.command && pdu.command === 'bind_transceiver') {
|
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
reject();
|
reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.log1(`Center-${entity?.id} got a bind_transciever with system_id ${pdu.system_id} and password ${pdu.password}`);
|
this.logger.log1(`Center-${entity?.id} got a bind_transceiver with system_id ${pdu.system_id} and password ${pdu.password}`);
|
||||||
session.pause();
|
session.pause();
|
||||||
if (pdu.system_id === entity?.username && pdu.password === entity?.password) {
|
if (pdu.system_id === entity?.username && pdu.password === entity?.password) {
|
||||||
this.logger.log1(`Center-${entity?.id} client connection successful`);
|
this.logger.log1(`Center-${entity?.id} client connection successful`);
|
||||||
@@ -24,7 +25,7 @@ export default class BindTranscieverReplyProcessor extends Postprocessor {
|
|||||||
}
|
}
|
||||||
session.resume();
|
session.resume();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
entity?.pendingSessions = entity?.pendingSessions.filter((s) => s !== session);
|
entity.pendingSessions = entity?.pendingSessions.filter((s) => s !== session);
|
||||||
entity?.sessions.push(session);
|
entity?.sessions.push(session);
|
||||||
entity?.updateStatus();
|
entity?.updateStatus();
|
||||||
} else {
|
} else {
|
||||||
@@ -35,11 +36,10 @@ export default class BindTranscieverReplyProcessor extends Postprocessor {
|
|||||||
}), session);
|
}), session);
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
entity?.pendingSessions = entity?.pendingSessions.filter((s) => s !== session);
|
entity.pendingSessions = entity?.pendingSessions.filter((s) => s !== session);
|
||||||
entity?.updateStatus();
|
entity?.updateStatus();
|
||||||
session.close();
|
session.close();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,13 +5,20 @@ import Postprocessor from "../Postprocessor";
|
|||||||
const smpp = require("smpp");
|
const smpp = require("smpp");
|
||||||
|
|
||||||
export default class DeliveryReceiptProcessor extends Postprocessor {
|
export default class DeliveryReceiptProcessor extends Postprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm'];
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
sleep(ms: number) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
if (!!pdu.command && pdu.command === "submit_sm" && pdu.registered_delivery) {
|
|
||||||
let drMessage: string = "";
|
let drMessage: string = "";
|
||||||
let date: string = new Date().toISOString().replace(/T/, '').replace(/\..+/, '').replace(/-/g, '').replace(/:/g, '').substring(2, 12);
|
let date: string = new Date().toISOString().replace(/T/, '').replace(/\..+/, '').replace(/-/g, '').replace(/:/g, '').substring(2, 12);
|
||||||
|
|
||||||
@@ -26,21 +33,17 @@ export default class DeliveryReceiptProcessor extends Postprocessor {
|
|||||||
drMessage += "err:000 ";
|
drMessage += "err:000 ";
|
||||||
drMessage += "text:";
|
drMessage += "text:";
|
||||||
|
|
||||||
|
let sleepTime = 0;
|
||||||
let DRPdu = new smpp.PDU('deliver_sm', {
|
let DRPdu = new smpp.PDU('deliver_sm', {
|
||||||
source_addr: pdu.source_addr,
|
source_addr: pdu.source_addr,
|
||||||
destination_addr: pdu.destination_addr,
|
destination_addr: pdu.destination_addr,
|
||||||
short_message: drMessage,
|
short_message: drMessage,
|
||||||
esm_class: 4,
|
esm_class: 4,
|
||||||
});
|
});
|
||||||
entity?.doSendPdu(DRPdu, session);
|
setTimeout(() => entity?.doSendPdu(DRPdu, session), sleepTime);
|
||||||
|
|
||||||
resolve(pdu);
|
resolve(pdu);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private padLeft(str: string, pad: string, length: number): string {
|
|
||||||
return (new Array(length + 1).join(pad) + str).slice(-length);
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -4,13 +4,16 @@ import Postprocessor from "../Postprocessor";
|
|||||||
const smpp = require("smpp");
|
const smpp = require("smpp");
|
||||||
|
|
||||||
export default class EchoPduProcessor extends Postprocessor {
|
export default class EchoPduProcessor extends Postprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm'];
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
if (!!pdu.command && pdu.command === "submit_sm") {
|
// Temporary (?) safeguard against echoing long sms
|
||||||
|
if (!pdu.short_message.udh) {
|
||||||
let echoPdu = new smpp.PDU('deliver_sm', {...pdu});
|
let echoPdu = new smpp.PDU('deliver_sm', {...pdu});
|
||||||
echoPdu.source_addr = pdu.destination_addr;
|
echoPdu.source_addr = pdu.destination_addr;
|
||||||
echoPdu.destination_addr = pdu.source_addr;
|
echoPdu.destination_addr = pdu.source_addr;
|
||||||
|
@@ -2,16 +2,16 @@ import SmppSession from "../../../SmppSession";
|
|||||||
import Postprocessor from "../Postprocessor";
|
import Postprocessor from "../Postprocessor";
|
||||||
|
|
||||||
export default class EnquireLinkReplyProcessor extends Postprocessor {
|
export default class EnquireLinkReplyProcessor extends Postprocessor {
|
||||||
|
applicableCommands: string[] = ['enquire_link'];
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!!pdu.command && pdu.command === 'enquire_link') {
|
|
||||||
entity?.doSendPdu(pdu.response(), session);
|
entity?.doSendPdu(pdu.response(), session);
|
||||||
resolve(pdu);
|
resolve(pdu);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -3,22 +3,20 @@ import SmppSession from "../../../SmppSession";
|
|||||||
import Postprocessor from "../Postprocessor";
|
import Postprocessor from "../Postprocessor";
|
||||||
|
|
||||||
export default class SubmitSmReplyProcessor extends Postprocessor {
|
export default class SubmitSmReplyProcessor extends Postprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm'];
|
||||||
private messageIdIterator: number = 0;
|
private messageIdIterator: number = 0;
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!!pdu.command && pdu.command === 'submit_sm') {
|
|
||||||
// Add an ID here!
|
|
||||||
let response = pdu.response();
|
let response = pdu.response();
|
||||||
response.message_id = this.messageIdIterator++;
|
response.message_id = this.messageIdIterator++;
|
||||||
MessageIdManager.addMessageId(pdu, response.message_id);
|
MessageIdManager.addMessageId(pdu, response.message_id);
|
||||||
entity?.doSendPdu(response, session);
|
entity?.doSendPdu(response, session);
|
||||||
resolve(pdu);
|
resolve(pdu);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,16 +2,16 @@ import SmppSession from "../../../SmppSession";
|
|||||||
import Postprocessor from "../Postprocessor";
|
import Postprocessor from "../Postprocessor";
|
||||||
|
|
||||||
export default class DeliverSmReplyProcessor extends Postprocessor {
|
export default class DeliverSmReplyProcessor extends Postprocessor {
|
||||||
|
applicableCommands: string[] = ['deliver_sm'];
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!!pdu.command && pdu.command === 'deliver_sm') {
|
|
||||||
entity?.doSendPdu(pdu.response(), session);
|
entity?.doSendPdu(pdu.response(), session);
|
||||||
resolve(pdu);
|
resolve(pdu);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,17 +2,15 @@ import SmppSession from "../../../SmppSession";
|
|||||||
import Preprocessor from "../Preprocessor";
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
export default class DeliveryReceiptRequestProcessor extends Preprocessor {
|
export default class DeliveryReceiptRequestProcessor extends Preprocessor {
|
||||||
private iterator: number = 0;
|
applicableCommands: string[] = ['submit_sm'];
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
if (!!pdu.command && pdu.command === "submit_sm") {
|
|
||||||
pdu.registered_delivery = 1;
|
pdu.registered_delivery = 1;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,13 +2,14 @@ import SmppSession from "../../../SmppSession";
|
|||||||
import Preprocessor from "../Preprocessor";
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
export default class DestinationEnumeratorProcessor extends Preprocessor {
|
export default class DestinationEnumeratorProcessor extends Preprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
private iterator: number = 0;
|
private iterator: number = 0;
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
if (!!pdu.destination_addr) {
|
if (!!pdu.destination_addr) {
|
||||||
pdu.destination_addr = pdu.destination_addr + this.padLeft(String(this.iterator++), '0', 5);
|
pdu.destination_addr = pdu.destination_addr + this.padLeft(String(this.iterator++), '0', 5);
|
||||||
|
@@ -0,0 +1,40 @@
|
|||||||
|
import {PDU} from "../../../CommonObjects";
|
||||||
|
import SmppSession from "../../../SmppSession";
|
||||||
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
|
export default class DestinationSetPreprocessor extends Preprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
|
private sourceSet: string[] = [];
|
||||||
|
|
||||||
|
constructor(type: string) {
|
||||||
|
super(type);
|
||||||
|
while (this.sourceSet.length < 100) {
|
||||||
|
this.sourceSet.push(this.getRandomInt(100000, 999999).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
if (pdu.short_message) {
|
||||||
|
if (pdu.short_message.includes("arg:")) {
|
||||||
|
let temp: string = pdu.short_message.split(";");
|
||||||
|
let arg: number = Number(temp[0].split(":")[1]);
|
||||||
|
while (this.sourceSet.length < arg) {
|
||||||
|
this.sourceSet.push(this.getRandomInt(100000, 999999).toString());
|
||||||
|
}
|
||||||
|
while (this.sourceSet.length > arg) {
|
||||||
|
this.sourceSet.pop();
|
||||||
|
}
|
||||||
|
pdu.short_message = temp[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pdu.destination_addr = pdu.destination_addr + this.sourceSet[this.getRandomInt(0, this.sourceSet.length)];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRandomInt(min: number, max: number): number {
|
||||||
|
min = Math.ceil(min);
|
||||||
|
max = Math.floor(max);
|
||||||
|
return Math.floor(Math.random() * (max - min) + min);
|
||||||
|
}
|
||||||
|
}
|
17
src/PDUProcessor/Preprocessor/Client/GSM0338Preprocessor.ts
Normal file
17
src/PDUProcessor/Preprocessor/Client/GSM0338Preprocessor.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import {PDU} from "../../../CommonObjects";
|
||||||
|
import SmppSession from "../../../SmppSession";
|
||||||
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
|
export default class GSM0338Preprocessor extends Preprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
|
|
||||||
|
constructor(type: string) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
pdu.data_coding = 0xf6;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -6,6 +6,7 @@ const smpp = require('smpp');
|
|||||||
|
|
||||||
export default class LongSmsProcessor extends Preprocessor {
|
export default class LongSmsProcessor extends Preprocessor {
|
||||||
static readonly maxMessageSizeBits = 1072;
|
static readonly maxMessageSizeBits = 1072;
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
private iterator: number = 0;
|
private iterator: number = 0;
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
@@ -47,7 +48,7 @@ export default class LongSmsProcessor extends Preprocessor {
|
|||||||
return characterSizeBits;
|
return characterSizeBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
processPdu(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
|
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
if (!!pdu.short_message) {
|
if (!!pdu.short_message) {
|
||||||
let characterSizeBits: number = LongSmsProcessor.getCharacterSizeForEncoding(pdu);
|
let characterSizeBits: number = LongSmsProcessor.getCharacterSizeForEncoding(pdu);
|
||||||
|
@@ -0,0 +1,16 @@
|
|||||||
|
import SmppSession from "../../../SmppSession";
|
||||||
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
|
export default class ProtocolId2DigitProcessor extends Preprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
|
|
||||||
|
constructor(type: string) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
pdu.protocol_id = 16;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
import SmppSession from "../../../SmppSession";
|
||||||
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
|
export default class ProtocolId3DigitProcessor extends Preprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
|
|
||||||
|
constructor(type: string) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
pdu.protocol_id = 128;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,17 @@
|
|||||||
|
import {PDU} from "../../../CommonObjects";
|
||||||
|
import SmppSession from "../../../SmppSession";
|
||||||
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
|
export default class ProtocolId4DigitProcessor extends Preprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
|
|
||||||
|
constructor(type: string) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
pdu.data_coding = 2048;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
16
src/PDUProcessor/Preprocessor/Client/ProtocolIdProcessor.ts
Normal file
16
src/PDUProcessor/Preprocessor/Client/ProtocolIdProcessor.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import SmppSession from "../../../SmppSession";
|
||||||
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
|
export default class ProtocolIdProcessor extends Preprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
|
|
||||||
|
constructor(type: string) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
pdu.protocol_id = 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -2,13 +2,14 @@ import SmppSession from "../../../SmppSession";
|
|||||||
import Preprocessor from "../Preprocessor";
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
export default class SourceEnumeratorProcessor extends Preprocessor {
|
export default class SourceEnumeratorProcessor extends Preprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
private iterator: number = 0;
|
private iterator: number = 0;
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string) {
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
if (!!pdu.source_addr) {
|
if (!!pdu.source_addr) {
|
||||||
pdu.source_addr = pdu.source_addr + this.padLeft(String(this.iterator++), '0', 5);
|
pdu.source_addr = pdu.source_addr + this.padLeft(String(this.iterator++), '0', 5);
|
||||||
|
@@ -0,0 +1,40 @@
|
|||||||
|
import {PDU} from "../../../CommonObjects";
|
||||||
|
import SmppSession from "../../../SmppSession";
|
||||||
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
|
export default class SourceSetPreprocessor extends Preprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
|
private sourceSet: string[] = [];
|
||||||
|
|
||||||
|
constructor(type: string) {
|
||||||
|
super(type);
|
||||||
|
while (this.sourceSet.length < 100) {
|
||||||
|
this.sourceSet.push(this.getRandomInt(100000, 999999).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
if (pdu.short_message) {
|
||||||
|
if (pdu.short_message.includes("arg:")) {
|
||||||
|
let temp: string = pdu.short_message.split(";");
|
||||||
|
let arg: number = Number(temp[0].split(":")[1]);
|
||||||
|
while (this.sourceSet.length < arg) {
|
||||||
|
this.sourceSet.push(this.getRandomInt(100000, 999999).toString());
|
||||||
|
}
|
||||||
|
while (this.sourceSet.length > arg) {
|
||||||
|
this.sourceSet.pop();
|
||||||
|
}
|
||||||
|
pdu.short_message = temp[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pdu.source_addr = pdu.source_addr + this.sourceSet[this.getRandomInt(0, this.sourceSet.length)];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRandomInt(min: number, max: number): number {
|
||||||
|
min = Math.ceil(min);
|
||||||
|
max = Math.floor(max);
|
||||||
|
return Math.floor(Math.random() * (max - min) + min);
|
||||||
|
}
|
||||||
|
}
|
17
src/PDUProcessor/Preprocessor/Client/UCS2Preprocessor.ts
Normal file
17
src/PDUProcessor/Preprocessor/Client/UCS2Preprocessor.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import {PDU} from "../../../CommonObjects";
|
||||||
|
import SmppSession from "../../../SmppSession";
|
||||||
|
import Preprocessor from "../Preprocessor";
|
||||||
|
|
||||||
|
export default class UCS2Preprocessor extends Preprocessor {
|
||||||
|
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
|
||||||
|
|
||||||
|
constructor(type: string) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
pdu.data_coding = 8;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -12,8 +12,15 @@ import DeliverSmReplyProcessor from "./Postprocessor/Client/DeliverSmReplyProces
|
|||||||
import Postprocessor from "./Postprocessor/Postprocessor";
|
import Postprocessor from "./Postprocessor/Postprocessor";
|
||||||
import DeliveryReceiptRequestProcessor from "./Preprocessor/Client/DeliveryReceiptRequestProcessor";
|
import DeliveryReceiptRequestProcessor from "./Preprocessor/Client/DeliveryReceiptRequestProcessor";
|
||||||
import DestinationEnumeratorProcessor from "./Preprocessor/Client/DestinationEnumeratorProcessor";
|
import DestinationEnumeratorProcessor from "./Preprocessor/Client/DestinationEnumeratorProcessor";
|
||||||
|
import DestinationSetPreprocessor from "./Preprocessor/Client/DestinationSetPreprocessor";
|
||||||
|
import GSM0338Preprocessor from "./Preprocessor/Client/GSM0338Preprocessor";
|
||||||
import LongSmsProcessor from "./Preprocessor/Client/LongSmsProcessor";
|
import LongSmsProcessor from "./Preprocessor/Client/LongSmsProcessor";
|
||||||
|
import ProtocolId2DigitProcessor from "./Preprocessor/Client/ProtocolId-2Digit-Processor";
|
||||||
|
import ProtocolId3DigitProcessor from "./Preprocessor/Client/ProtocolId-3Digit-Processor";
|
||||||
|
import ProtocolIdProcessor from "./Preprocessor/Client/ProtocolIdProcessor";
|
||||||
import SourceEnumeratorProcessor from "./Preprocessor/Client/SourceEnumeratorProcessor";
|
import SourceEnumeratorProcessor from "./Preprocessor/Client/SourceEnumeratorProcessor";
|
||||||
|
import SourceSetPreprocessor from "./Preprocessor/Client/SourceSetPreprocessor";
|
||||||
|
import UCS2Preprocessor from "./Preprocessor/Client/UCS2Preprocessor";
|
||||||
import Preprocessor from "./Preprocessor/Preprocessor";
|
import Preprocessor from "./Preprocessor/Preprocessor";
|
||||||
|
|
||||||
export default class ProcessorManager {
|
export default class ProcessorManager {
|
||||||
@@ -35,8 +42,25 @@ export default class ProcessorManager {
|
|||||||
ProcessorManager.preprocessors = [
|
ProcessorManager.preprocessors = [
|
||||||
new DestinationEnumeratorProcessor(Client.name),
|
new DestinationEnumeratorProcessor(Client.name),
|
||||||
new SourceEnumeratorProcessor(Client.name),
|
new SourceEnumeratorProcessor(Client.name),
|
||||||
|
new DestinationEnumeratorProcessor(Center.name),
|
||||||
|
new SourceEnumeratorProcessor(Center.name),
|
||||||
new DeliveryReceiptRequestProcessor(Client.name),
|
new DeliveryReceiptRequestProcessor(Client.name),
|
||||||
new LongSmsProcessor(Client.name)
|
new LongSmsProcessor(Client.name),
|
||||||
|
new LongSmsProcessor(Center.name),
|
||||||
|
new ProtocolIdProcessor(Client.name),
|
||||||
|
new ProtocolIdProcessor(Center.name),
|
||||||
|
new UCS2Preprocessor(Client.name),
|
||||||
|
new UCS2Preprocessor(Center.name),
|
||||||
|
new ProtocolId2DigitProcessor(Client.name),
|
||||||
|
new ProtocolId2DigitProcessor(Center.name),
|
||||||
|
new ProtocolId3DigitProcessor(Client.name),
|
||||||
|
new ProtocolId3DigitProcessor(Center.name),
|
||||||
|
new SourceSetPreprocessor(Client.name),
|
||||||
|
new SourceSetPreprocessor(Center.name),
|
||||||
|
new DestinationSetPreprocessor(Client.name),
|
||||||
|
new DestinationSetPreprocessor(Center.name),
|
||||||
|
new GSM0338Preprocessor(Client.name),
|
||||||
|
new GSM0338Preprocessor(Center.name)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,20 +68,16 @@ export default class ProcessorManager {
|
|||||||
return this.preprocessors.concat(this.postprocessors);
|
return this.preprocessors.concat(this.postprocessors);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getProcessor(name: string): PduProcessor {
|
static getProcessors(name: string): PduProcessor[] {
|
||||||
this.logger.log1(`Looking for processor with name ${name}...`);
|
this.logger.log1(`Looking for processor with name ${name}...`);
|
||||||
let pduProcessor: PduProcessor | undefined = this.processors.find((processor: PduProcessor) => processor.name === name);
|
let pduProcessors: PduProcessor[] = this.processors.filter((processor: PduProcessor) => processor.name === name);
|
||||||
if (pduProcessor) {
|
this.logger.log1(`Found ${pduProcessors.length} processor(s) with name ${name}`);
|
||||||
this.logger.log1(`Found processor with name ${name}`);
|
return pduProcessors;
|
||||||
return pduProcessor;
|
|
||||||
} else {
|
|
||||||
this.logger.log1(`Processor with name ${name} not found`);
|
|
||||||
return this.processors[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static attachProcessor(session: SmppSession, processor: PduProcessor): void {
|
static attachProcessors(session: SmppSession, processors: PduProcessor[]): void {
|
||||||
this.logger.log1(`Trying to attach preprocessor ${processor.name} to session ${session.constructor.name}-${session.id}`);
|
this.logger.log1(`Trying to attach processor ${processors.toString()} to session ${session.constructor.name}-${session.id}`);
|
||||||
|
for (const processor of processors) {
|
||||||
if (this.areCompatible(session, processor)) {
|
if (this.areCompatible(session, processor)) {
|
||||||
// This could be done a little better but this is OK for now
|
// This could be done a little better but this is OK for now
|
||||||
switch (processor.type) {
|
switch (processor.type) {
|
||||||
@@ -73,9 +93,11 @@ export default class ProcessorManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static detachProcessor(session: SmppSession, processor: PduProcessor): void {
|
static detachProcessors(session: SmppSession, processors: PduProcessor[]): void {
|
||||||
this.logger.log1(`Trying to detach processor ${processor.name} from session ${session.constructor.name}-${session.id}`);
|
this.logger.log1(`Trying to detach processors ${processors.toString()} from session ${session.constructor.name}-${session.id}`);
|
||||||
|
for (const processor of processors) {
|
||||||
switch (processor.type) {
|
switch (processor.type) {
|
||||||
case Preprocessor.name:
|
case Preprocessor.name:
|
||||||
session.detachPreprocessor(processor);
|
session.detachPreprocessor(processor);
|
||||||
@@ -88,6 +110,7 @@ export default class ProcessorManager {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static areCompatible(session: SmppSession, processor: PduProcessor): boolean {
|
static areCompatible(session: SmppSession, processor: PduProcessor): boolean {
|
||||||
this.logger.log1(`Checking compatibility between session ${session.constructor.name}-${session.id} and processor ${processor.name}`);
|
this.logger.log1(`Checking compatibility between session ${session.constructor.name}-${session.id} and processor ${processor.name}`);
|
||||||
|
@@ -74,13 +74,12 @@ export default abstract class SessionManager {
|
|||||||
|
|
||||||
let loadedProcessors: PduProcessor[] = session.preprocessors.concat(session.postprocessors);
|
let loadedProcessors: PduProcessor[] = session.preprocessors.concat(session.postprocessors);
|
||||||
sessionObj.appliedProcessors.forEach((processor: PduProcessor) => {
|
sessionObj.appliedProcessors.forEach((processor: PduProcessor) => {
|
||||||
if (!loadedProcessors.find(p => p.name === processor.name)) {
|
let processorsToDetach: PduProcessor[] = loadedProcessors.filter(p => p.name === processor.name);
|
||||||
ProcessorManager.detachProcessor(sessionObj, processor);
|
ProcessorManager.detachProcessors(sessionObj, processorsToDetach);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
loadedProcessors.forEach((processor: PduProcessor) => {
|
loadedProcessors.forEach((processor: PduProcessor) => {
|
||||||
if (!sessionObj.appliedProcessors.find(p => p.name === processor.name)) {
|
if (!sessionObj.appliedProcessors.find(p => p.name === processor.name)) {
|
||||||
ProcessorManager.attachProcessor(sessionObj, ProcessorManager.getProcessor(processor.name));
|
ProcessorManager.attachProcessors(sessionObj, ProcessorManager.getProcessors(processor.name));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -6,6 +6,7 @@ import PduProcessor from "./PDUProcessor/PduProcessor";
|
|||||||
import Postprocessor from "./PDUProcessor/Postprocessor/Postprocessor";
|
import Postprocessor from "./PDUProcessor/Postprocessor/Postprocessor";
|
||||||
import LongSmsProcessor from "./PDUProcessor/Preprocessor/Client/LongSmsProcessor";
|
import LongSmsProcessor from "./PDUProcessor/Preprocessor/Client/LongSmsProcessor";
|
||||||
import Preprocessor from "./PDUProcessor/Preprocessor/Preprocessor";
|
import Preprocessor from "./PDUProcessor/Preprocessor/Preprocessor";
|
||||||
|
import ProcessorManager from "./PDUProcessor/ProcessorManager";
|
||||||
|
|
||||||
const NanoTimer = require("nanotimer");
|
const NanoTimer = require("nanotimer");
|
||||||
const smpp = require("smpp");
|
const smpp = require("smpp");
|
||||||
@@ -115,11 +116,11 @@ export default abstract class SmppSession {
|
|||||||
|
|
||||||
doSendPdu(pdu: PDU, session: any): Promise<any> {
|
doSendPdu(pdu: PDU, session: any): Promise<any> {
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
// let characterSizeBits: number = LongSmsProcessor.getCharacterSizeForEncoding(pdu);
|
let characterSizeBits: number = LongSmsProcessor.getCharacterSizeForEncoding(pdu);
|
||||||
// let maxMessageLength: number = LongSmsProcessor.maxMessageSizeBits / characterSizeBits;
|
let maxMessageLength: number = LongSmsProcessor.maxMessageSizeBits / characterSizeBits;
|
||||||
// if (!!pdu.short_message && pdu.short_message.length > maxMessageLength) {
|
if (!!pdu.short_message && pdu.short_message.length > maxMessageLength) {
|
||||||
// pdu.short_message = pdu.short_message.substring(0, maxMessageLength);
|
pdu.short_message = pdu.short_message.substring(0, maxMessageLength);
|
||||||
// }
|
}
|
||||||
session.send(pdu, (reply: any) => resolve(reply));
|
session.send(pdu, (reply: any) => resolve(reply));
|
||||||
this.eventEmitter.emit(this.EVENT.ANY_PDU_TX, pdu);
|
this.eventEmitter.emit(this.EVENT.ANY_PDU_TX, pdu);
|
||||||
});
|
});
|
||||||
@@ -149,7 +150,23 @@ export default abstract class SmppSession {
|
|||||||
|
|
||||||
abstract close(): Promise<void>;
|
abstract close(): Promise<void>;
|
||||||
|
|
||||||
abstract serialize(): object;
|
serialize(): object {
|
||||||
|
let obj = {
|
||||||
|
id: this._id,
|
||||||
|
username: this._username,
|
||||||
|
password: this._password,
|
||||||
|
status: this._status,
|
||||||
|
defaultSingleJob: this._defaultSingleJob.serialize(),
|
||||||
|
defaultMultipleJob: this._defaultMultipleJob.serialize(),
|
||||||
|
preprocessors: this.processors.Preprocessor.map((p: PduProcessor) => p.serialize()),
|
||||||
|
postprocessors: this.processors.Postprocessor.map((p: PduProcessor) => p.serialize()),
|
||||||
|
availablePreprocessors: ProcessorManager.getPreprocessorsForType(this.constructor.name).map((p: PduProcessor) => p.serialize()),
|
||||||
|
availablePostprocessors: ProcessorManager.getPostprocessorsForType(this.constructor.name).map((p: PduProcessor) => p.serialize()),
|
||||||
|
};
|
||||||
|
return this.postSerialize(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract postSerialize(obj: object): object;
|
||||||
|
|
||||||
on(event: string, callback: (...args: any[]) => void): void {
|
on(event: string, callback: (...args: any[]) => void): void {
|
||||||
this.eventEmitter.on(event, callback);
|
this.eventEmitter.on(event, callback);
|
||||||
@@ -210,10 +227,12 @@ export default abstract class SmppSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private detachProcessor(processor: PduProcessor, array: PduProcessor[]): void {
|
private detachProcessor(processor: PduProcessor, array: PduProcessor[]): void {
|
||||||
|
if (array.indexOf(processor) >= 0) {
|
||||||
array.splice(array.indexOf(processor), 1);
|
array.splice(array.indexOf(processor), 1);
|
||||||
this.logger.log1(`Detaching PDU processor: ${processor.constructor.name}-${this.id}, now active: ${array.length} processors`);
|
this.logger.log1(`Detaching PDU processor: ${processor.constructor.name}-${this.id}, now active: ${array.length} processors`);
|
||||||
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
|
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private attachProcessor(processor: PduProcessor, array: PduProcessor[]): void {
|
private attachProcessor(processor: PduProcessor, array: PduProcessor[]): void {
|
||||||
if (array.indexOf(processor) === -1) {
|
if (array.indexOf(processor) === -1) {
|
||||||
|
25
src/main.js
Normal file
25
src/main.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var CenterSessionManager_1 = require("./Center/CenterSessionManager");
|
||||||
|
var ClientSessionManager_1 = require("./Client/ClientSessionManager");
|
||||||
|
var HttpServer_1 = require("./HttpServer/HttpServer");
|
||||||
|
var Logger_1 = require("./Logger");
|
||||||
|
var ProcessorManager_1 = require("./PDUProcessor/ProcessorManager");
|
||||||
|
var WSServer_1 = require("./WS/WSServer");
|
||||||
|
var PDU = require("smpp").PDU;
|
||||||
|
var logger = new Logger_1.default("main");
|
||||||
|
var pm = new ProcessorManager_1.default();
|
||||||
|
var clientManager = new ClientSessionManager_1.default();
|
||||||
|
var centerManager = new CenterSessionManager_1.default();
|
||||||
|
var wss = new WSServer_1.default([clientManager, centerManager]);
|
||||||
|
var httpServer = new HttpServer_1.default(clientManager, centerManager);
|
||||||
|
function cleanup() {
|
||||||
|
logger.log1("Cleaning up...");
|
||||||
|
clientManager.cleanup();
|
||||||
|
centerManager.cleanup();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
process.on('exit', cleanup);
|
||||||
|
process.on('SIGINT', cleanup);
|
||||||
|
process.on('SIGUSR1', cleanup);
|
||||||
|
process.on('SIGUSR2', cleanup);
|
@@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es6",
|
"strict": false,
|
||||||
"module": "commonjs",
|
"verbatimModuleSyntax": false,
|
||||||
"outDir": "./dist",
|
"noUncheckedIndexedAccess": false,
|
||||||
"rootDir": "./src",
|
|
||||||
"strict": true,
|
"moduleResolution": "NodeNext",
|
||||||
"moduleResolution": "node",
|
"module": "NodeNext",
|
||||||
|
"target": "ESNext",
|
||||||
|
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"noImplicitAny": true,
|
"noEmit": true
|
||||||
},
|
},
|
||||||
"exclude": [
|
"include": ["src/**/*", "src/main.ts"]
|
||||||
"./node_modules"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
13
tsup.config.ts
Normal file
13
tsup.config.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { defineConfig } from 'tsup';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
entry: ['src/main.ts'],
|
||||||
|
dts: false,
|
||||||
|
splitting: false,
|
||||||
|
sourcemap: false,
|
||||||
|
outDir: 'build',
|
||||||
|
clean: true,
|
||||||
|
minify: true,
|
||||||
|
target: ['esnext'],
|
||||||
|
format: ['cjs']
|
||||||
|
});
|
61
workfile.js
Normal file
61
workfile.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { connect } from "smpp";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
const optionsFile = "smpp_tester_options.json";
|
||||||
|
|
||||||
|
const exampleSession = {
|
||||||
|
url: "smpp://172.17.77.203:4445",
|
||||||
|
connection_count: 1,
|
||||||
|
system_id: "snt",
|
||||||
|
password: "snt",
|
||||||
|
source_addr: "44455",
|
||||||
|
destination_addr: "test123",
|
||||||
|
short_message: "SNT BENCHMARK!",
|
||||||
|
message_count: 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!fs.existsSync(optionsFile)) {
|
||||||
|
fs.writeFileSync(optionsFile, JSON.stringify([exampleSession], null, 4));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const options = fs.readFileSync(optionsFile, "utf8") ?? "[]";
|
||||||
|
const sessions = JSON.parse(options);
|
||||||
|
const smppSessions = [];
|
||||||
|
|
||||||
|
for (const session of sessions) {
|
||||||
|
for (let i = 0; i < session.connection_count; i++) {
|
||||||
|
const smppSession = connect(
|
||||||
|
{
|
||||||
|
url: session.url,
|
||||||
|
auto_enquire_link_period: 10000,
|
||||||
|
debug: true,
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
smppSession.bind_transceiver({
|
||||||
|
system_id: session.system_id,
|
||||||
|
password: session.password,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
smppSessions.push({ properties: session, session: smppSession });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
for (const session of smppSessions) {
|
||||||
|
for (let i = 0; i < session.properties.message_count; i++) {
|
||||||
|
session.session.submit_sm(
|
||||||
|
{
|
||||||
|
source_addr: session.properties.source_addr,
|
||||||
|
destination_addr: session.properties.destination_addr,
|
||||||
|
short_message: session.properties.short_message,
|
||||||
|
},
|
||||||
|
function (pdu) {
|
||||||
|
if (pdu.command_status === 0) {
|
||||||
|
console.log(pdu.message_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
Reference in New Issue
Block a user