unismsgateway 1.3.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,12 +2,15 @@ import { ISmsGatewayDelegate, QuickSendParams, SendResult } from './types';
2
2
  export interface HubtelSmsGatewayConfig {
3
3
  clientId: string;
4
4
  clientSecret: string;
5
+ debug?: boolean;
5
6
  }
6
7
  /**
7
8
  * Wraps hubtel-sms-extended and maps API responses to {@link SendResult}.
8
9
  */
9
10
  export declare class HubtelSmsGateway implements ISmsGatewayDelegate {
10
11
  private _client;
12
+ private _debug;
11
13
  constructor(config: HubtelSmsGatewayConfig);
14
+ private log;
12
15
  quickSend(params: QuickSendParams, callback?: Function): Promise<SendResult>;
13
16
  }
@@ -16,41 +16,55 @@ const hubtel_sms_extended_1 = require("hubtel-sms-extended");
16
16
  */
17
17
  class HubtelSmsGateway {
18
18
  constructor(config) {
19
+ this._debug = config.debug || false;
19
20
  this._client = new hubtel_sms_extended_1.HubtelSms({
20
21
  clientId: config.clientId,
21
22
  clientSecret: config.clientSecret
22
23
  });
23
24
  }
25
+ log(...args) {
26
+ if (this._debug) {
27
+ console.log('[unismsgateway:hubtel]', ...args);
28
+ }
29
+ }
24
30
  quickSend(params, callback) {
31
+ var _a;
25
32
  return __awaiter(this, void 0, void 0, function* () {
33
+ this.log('quickSend params:', JSON.stringify(params));
34
+ let result;
26
35
  try {
27
36
  const raw = yield this._client.quickSend({
28
37
  From: params.From,
29
38
  To: String(params.To),
30
39
  Content: params.Content
31
40
  });
32
- const ok = Number(raw.Status) === 0;
33
- const result = {
41
+ this.log('quickSend raw response:', JSON.stringify(raw));
42
+ const ok = Number(raw === null || raw === void 0 ? void 0 : raw.Status) === 0;
43
+ result = {
34
44
  success: ok,
35
- messageId: String(raw.MessageId),
45
+ messageId: (raw === null || raw === void 0 ? void 0 : raw.MessageId) != null ? String(raw.MessageId) : undefined,
36
46
  data: raw,
37
- error: ok ? undefined : `Hubtel Status=${raw.Status}`
47
+ error: ok
48
+ ? undefined
49
+ : `Hubtel API Error: Status=${raw === null || raw === void 0 ? void 0 : raw.Status}, NetworkId=${(_a = raw === null || raw === void 0 ? void 0 : raw.NetworkId) !== null && _a !== void 0 ? _a : 'n/a'}`
38
50
  };
39
- if (callback) {
40
- callback(result);
41
- }
42
- return result;
43
51
  }
44
52
  catch (error) {
45
- const result = {
53
+ const errorMessage = error instanceof Error ? error.message : String(error);
54
+ this.log('quickSend error:', errorMessage);
55
+ result = {
46
56
  success: false,
47
- error: error instanceof Error ? error.message : String(error)
57
+ error: errorMessage,
58
+ data: error instanceof Error && error.response
59
+ ? error.response
60
+ : null
48
61
  };
49
- if (callback) {
50
- callback(result);
51
- }
52
- return result;
53
62
  }
63
+ this.log('quickSend result:', JSON.stringify(result));
64
+ if (callback) {
65
+ callback(result);
66
+ }
67
+ return result;
54
68
  });
55
69
  }
56
70
  }
@@ -3,6 +3,7 @@ export declare class NestSmsGateway implements ISmsGateway {
3
3
  private config;
4
4
  constructor(config: NestSmsConfig);
5
5
  init(): ISmsGateway;
6
+ private log;
6
7
  private makeRequest;
7
8
  quickSend(params: QuickSendParams, callback?: Function): Promise<SendResult>;
8
9
  getBalance(): Promise<{
@@ -38,59 +38,80 @@ class NestSmsGateway {
38
38
  this.config = {
39
39
  host: config.host || DEFAULT_HOST,
40
40
  protocol: config.protocol || DEFAULT_PROTOCOL,
41
- apiKey: config.apiKey
41
+ apiKey: config.apiKey,
42
+ debug: config.debug || false
42
43
  };
43
44
  }
44
45
  init() {
45
46
  return this;
46
47
  }
48
+ log(...args) {
49
+ if (this.config.debug) {
50
+ console.log('[unismsgateway:nest]', ...args);
51
+ }
52
+ }
47
53
  makeRequest(endpoint, data) {
48
54
  return __awaiter(this, void 0, void 0, function* () {
49
55
  return new Promise((resolve, reject) => {
50
- var _a;
51
56
  const postData = data ? JSON.stringify(data) : '';
52
57
  const protocol = this.config.protocol || DEFAULT_PROTOCOL;
53
58
  const httpModule = protocol === 'https' ? https : http;
54
59
  const defaultPort = protocol === 'https' ? 443 : 80;
60
+ const host = this.config.host || DEFAULT_HOST;
61
+ const hostname = host.includes(':') ? host.split(':')[0] : host;
62
+ const port = host.includes(':')
63
+ ? parseInt(host.split(':')[1], 10)
64
+ : defaultPort;
55
65
  const options = {
56
- hostname: this.config.host || DEFAULT_HOST,
57
- port: ((_a = this.config.host) === null || _a === void 0 ? void 0 : _a.includes(':'))
58
- ? parseInt(this.config.host.split(':')[1])
59
- : defaultPort,
66
+ hostname,
67
+ port,
60
68
  path: `/v5/${endpoint}`,
61
69
  method: 'POST',
62
70
  headers: {
63
- 'Host': this.config.host || DEFAULT_HOST,
71
+ 'Host': hostname,
64
72
  'Content-Type': 'application/json',
65
73
  'Accept': 'application/json',
66
74
  'Authorization': `key ${this.config.apiKey}`,
67
75
  'Content-Length': Buffer.byteLength(postData)
68
76
  }
69
77
  };
78
+ this.log(`POST /v5/${endpoint}`, data ? JSON.stringify(data) : '(no body)');
70
79
  const req = httpModule.request(options, (res) => {
71
80
  let responseBody = '';
72
81
  res.on('data', (chunk) => {
73
82
  responseBody += chunk;
74
83
  });
75
84
  res.on('end', () => {
85
+ var _a;
86
+ const statusCode = (_a = res.statusCode) !== null && _a !== void 0 ? _a : 0;
87
+ this.log(`HTTP ${statusCode} response:`, responseBody);
76
88
  try {
77
- if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
78
- const parsed = JSON.parse(responseBody);
79
- resolve(parsed);
89
+ const parsed = JSON.parse(responseBody);
90
+ if (statusCode >= 200 && statusCode < 300) {
91
+ resolve({ statusCode, body: parsed });
80
92
  }
81
93
  else {
82
- reject(new Error(`HTTP ${res.statusCode}: ${responseBody}`));
94
+ const err = new Error(`HTTP ${statusCode}: ${responseBody}`);
95
+ err.statusCode = statusCode;
96
+ err.rawBody = responseBody;
97
+ reject(err);
83
98
  }
84
99
  }
85
- catch (error) {
86
- reject(new Error(`Failed to parse response: ${responseBody}`));
100
+ catch (_b) {
101
+ const err = new Error(`Failed to parse gateway response (HTTP ${statusCode}): ${responseBody}`);
102
+ err.statusCode = statusCode;
103
+ err.rawBody = responseBody;
104
+ reject(err);
87
105
  }
88
106
  });
89
107
  });
90
108
  req.on('error', (error) => {
109
+ this.log('Network error:', error.message);
91
110
  reject(error);
92
111
  });
93
- req.write(postData);
112
+ if (postData) {
113
+ req.write(postData);
114
+ }
94
115
  req.end();
95
116
  });
96
117
  });
@@ -98,57 +119,73 @@ class NestSmsGateway {
98
119
  quickSend(params, callback) {
99
120
  var _a, _b, _c, _d, _e;
100
121
  return __awaiter(this, void 0, void 0, function* () {
101
- const endpoint = 'message/sms/send';
102
- // SMSOnlineGH v5 expects: text, sender, destinations[] (see API docs — not from/to/content).
103
122
  const requestBody = {
104
123
  text: params.Content,
105
- type: params.Type || 0,
124
+ type: (_a = params.Type) !== null && _a !== void 0 ? _a : 0,
106
125
  sender: params.From,
107
126
  destinations: [String(params.To)]
108
127
  };
128
+ this.log('quickSend params:', JSON.stringify(params));
129
+ let result;
109
130
  try {
110
- const response = yield this.makeRequest(endpoint, requestBody);
111
- const handshakeOk = Number((_a = response.handshake) === null || _a === void 0 ? void 0 : _a.id) === 0;
112
- const data = (_b = response.data) !== null && _b !== void 0 ? _b : null;
113
- const batchId = data && typeof data === 'object' ? data.batch : undefined;
114
- const firstDest = data && typeof data === 'object'
115
- ? (_c = data.destinations) === null || _c === void 0 ? void 0 : _c[0]
131
+ const { statusCode, body: response } = yield this.makeRequest('message/sms/send', requestBody);
132
+ const handshakeId = (_b = response.handshake) === null || _b === void 0 ? void 0 : _b.id;
133
+ const handshakeLabel = (_c = response.handshake) === null || _c === void 0 ? void 0 : _c.label;
134
+ const handshakeOk = Number(handshakeId) === 0;
135
+ const responseData = (_d = response.data) !== null && _d !== void 0 ? _d : null;
136
+ const batchId = responseData && typeof responseData === 'object'
137
+ ? responseData.batch
138
+ : undefined;
139
+ const firstDest = responseData && typeof responseData === 'object'
140
+ ? (_e = responseData.destinations) === null || _e === void 0 ? void 0 : _e[0]
116
141
  : undefined;
117
- const result = {
142
+ let errorMsg;
143
+ if (!handshakeOk) {
144
+ if (handshakeLabel) {
145
+ errorMsg = `API Error [code ${handshakeId}]: ${handshakeLabel}`;
146
+ }
147
+ else if (handshakeId !== undefined && handshakeId !== null) {
148
+ errorMsg = `API Error: handshake code=${handshakeId}`;
149
+ }
150
+ else {
151
+ errorMsg = `Unexpected API response: ${JSON.stringify(response)}`;
152
+ }
153
+ }
154
+ result = {
118
155
  success: handshakeOk,
119
- data,
120
156
  messageId: batchId || (firstDest === null || firstDest === void 0 ? void 0 : firstDest.id),
121
- error: handshakeOk
122
- ? undefined
123
- : (((_d = response.handshake) === null || _d === void 0 ? void 0 : _d.label)
124
- || `handshake id ${String((_e = response.handshake) === null || _e === void 0 ? void 0 : _e.id)}`)
157
+ // On failure, expose the full raw response so callers can inspect it.
158
+ data: handshakeOk ? responseData : response,
159
+ error: errorMsg,
160
+ statusCode
125
161
  };
126
- if (callback) {
127
- callback(result);
128
- }
129
- return result;
130
162
  }
131
163
  catch (error) {
164
+ const httpErr = error;
132
165
  const errorMessage = error instanceof Error ? error.message : String(error);
133
- const result = {
166
+ result = {
134
167
  success: false,
135
- error: errorMessage
168
+ error: errorMessage,
169
+ statusCode: httpErr.statusCode,
170
+ // Preserve whatever raw body we got for inspection.
171
+ data: httpErr.rawBody !== undefined ? httpErr.rawBody : null
136
172
  };
137
- if (callback) {
138
- callback(result);
139
- }
140
- return result;
141
173
  }
174
+ this.log('quickSend result:', JSON.stringify(result));
175
+ if (callback) {
176
+ callback(result);
177
+ }
178
+ return result;
142
179
  });
143
180
  }
144
181
  getBalance() {
145
- var _a, _b;
182
+ var _a, _b, _c, _d;
146
183
  return __awaiter(this, void 0, void 0, function* () {
147
- const endpoint = 'account/balance';
148
- const response = yield this.makeRequest(endpoint);
184
+ this.log('getBalance called');
185
+ const { body: response } = yield this.makeRequest('account/balance');
149
186
  return {
150
- balance: ((_a = response.data) === null || _a === void 0 ? void 0 : _a.balance) || 0,
151
- model: ((_b = response.data) === null || _b === void 0 ? void 0 : _b.model) || 'quantity'
187
+ balance: (_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a.balance) !== null && _b !== void 0 ? _b : 0,
188
+ model: (_d = (_c = response.data) === null || _c === void 0 ? void 0 : _c.model) !== null && _d !== void 0 ? _d : 'quantity'
152
189
  };
153
190
  });
154
191
  }
@@ -52,18 +52,21 @@ class smsPlatform {
52
52
  username: param.username,
53
53
  password: param.password,
54
54
  protocol: param.protocol || 'http',
55
- port: param.port || 8080
55
+ port: param.port || 8080,
56
+ debug: param.debug
56
57
  });
57
58
  case 'hubtel':
58
59
  return new hubtel_gateway_1.HubtelSmsGateway({
59
60
  clientId: param.clientId,
60
- clientSecret: param.clientSecret
61
+ clientSecret: param.clientSecret,
62
+ debug: param.debug
61
63
  });
62
64
  case 'nest':
63
65
  return new nest_gateway_1.NestSmsGateway({
64
66
  apiKey: param.apiKey,
65
67
  host: param.host,
66
- protocol: param.protocol
68
+ protocol: param.protocol,
69
+ debug: param.debug
67
70
  });
68
71
  default:
69
72
  throw new Error(`Unsupported platform: ${platformId}`);
@@ -5,8 +5,18 @@ export interface RouteSmsGatewayConfig {
5
5
  password: string;
6
6
  protocol: 'http' | 'https';
7
7
  port: number;
8
+ debug?: boolean;
8
9
  }
10
+ /**
11
+ * Adapts routemobilesms to {@link ISmsGatewayDelegate}.
12
+ *
13
+ * NOTE: routemobilesms stores config in module-level state via the constructor,
14
+ * then exposes `routeSms.sendAsync` as a static-style call. The instance is
15
+ * intentionally discarded after construction.
16
+ */
9
17
  export declare class RouteSmsGateway implements ISmsGatewayDelegate {
18
+ private _debug;
10
19
  constructor(config: RouteSmsGatewayConfig);
20
+ private log;
11
21
  quickSend(params: QuickSendParams, callback?: Function): Promise<SendResult>;
12
22
  }
@@ -11,9 +11,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.RouteSmsGateway = void 0;
13
13
  const routemobilesms_1 = require("routemobilesms");
14
- /**
15
- * Adapts routemobilesms static `sendAsync` API to {@link ISmsGatewayDelegate}.
16
- */
17
14
  function toRouteDestination(to) {
18
15
  if (typeof to === 'number') {
19
16
  return to;
@@ -22,8 +19,18 @@ function toRouteDestination(to) {
22
19
  const n = Number(digits);
23
20
  return Number.isNaN(n) ? 0 : n;
24
21
  }
22
+ /**
23
+ * Adapts routemobilesms to {@link ISmsGatewayDelegate}.
24
+ *
25
+ * NOTE: routemobilesms stores config in module-level state via the constructor,
26
+ * then exposes `routeSms.sendAsync` as a static-style call. The instance is
27
+ * intentionally discarded after construction.
28
+ */
25
29
  class RouteSmsGateway {
26
30
  constructor(config) {
31
+ this._debug = config.debug || false;
32
+ // routemobilesms configures itself through its constructor and exposes
33
+ // sendAsync as a static method — the returned instance is not needed.
27
34
  new routemobilesms_1.routeSms({
28
35
  host: config.host,
29
36
  username: config.username,
@@ -32,8 +39,15 @@ class RouteSmsGateway {
32
39
  port: config.port
33
40
  });
34
41
  }
42
+ log(...args) {
43
+ if (this._debug) {
44
+ console.log('[unismsgateway:route]', ...args);
45
+ }
46
+ }
35
47
  quickSend(params, callback) {
48
+ var _a, _b;
36
49
  return __awaiter(this, void 0, void 0, function* () {
50
+ this.log('quickSend params:', JSON.stringify(params));
37
51
  const sendParams = {
38
52
  From: params.From,
39
53
  To: toRouteDestination(params.To),
@@ -42,24 +56,47 @@ class RouteSmsGateway {
42
56
  if (params.Type !== undefined) {
43
57
  sendParams.config = { type: params.Type, dlr: 0 };
44
58
  }
45
- const raw = yield routemobilesms_1.routeSms.sendAsync(sendParams);
46
59
  let result;
47
- if (raw === undefined || raw === null) {
48
- result = { success: false, error: 'No response from route SMS gateway' };
60
+ try {
61
+ const raw = yield routemobilesms_1.routeSms.sendAsync(sendParams);
62
+ this.log('quickSend raw response:', JSON.stringify(raw));
63
+ if (raw === undefined || raw === null) {
64
+ result = {
65
+ success: false,
66
+ error: 'No response received from Route SMS gateway',
67
+ data: null
68
+ };
69
+ }
70
+ else if (Array.isArray(raw) && raw.length > 0) {
71
+ const first = raw[0];
72
+ const ok = first.status === 'successful';
73
+ result = {
74
+ success: ok,
75
+ messageId: first.id,
76
+ data: raw,
77
+ error: ok
78
+ ? undefined
79
+ : `Route SMS Error [${(_a = first.code) !== null && _a !== void 0 ? _a : 'unknown'}]: ${(_b = first.message) !== null && _b !== void 0 ? _b : 'Send failed'}`
80
+ };
81
+ }
82
+ else {
83
+ result = {
84
+ success: false,
85
+ error: 'Unexpected response format from Route SMS gateway',
86
+ data: raw
87
+ };
88
+ }
49
89
  }
50
- else if (Array.isArray(raw) && raw.length > 0) {
51
- const first = raw[0];
52
- const ok = first.status === 'successful';
90
+ catch (error) {
91
+ const errorMessage = error instanceof Error ? error.message : String(error);
92
+ this.log('quickSend error:', errorMessage);
53
93
  result = {
54
- success: ok,
55
- messageId: first.id,
56
- data: raw,
57
- error: ok ? undefined : (first.message || first.code || 'Send failed')
94
+ success: false,
95
+ error: errorMessage,
96
+ data: null
58
97
  };
59
98
  }
60
- else {
61
- result = { success: false, error: 'Unexpected response from route SMS gateway', data: raw };
62
- }
99
+ this.log('quickSend result:', JSON.stringify(result));
63
100
  if (callback) {
64
101
  callback(result);
65
102
  }
@@ -10,6 +10,8 @@ export interface SendResult {
10
10
  messageId?: string;
11
11
  data?: any;
12
12
  error?: string;
13
+ /** HTTP status code returned by the gateway (when available). */
14
+ statusCode?: number;
13
15
  }
14
16
  export interface IgatewayParam {
15
17
  host?: string;
@@ -20,6 +22,8 @@ export interface IgatewayParam {
20
22
  clientSecret?: string;
21
23
  apiKey?: string;
22
24
  protocol?: 'http' | 'https';
25
+ /** Set to true to print request/response details to console for debugging. */
26
+ debug?: boolean;
23
27
  }
24
28
  export interface IgatewaySettings {
25
29
  platformId: PlatformId;
@@ -37,6 +41,7 @@ export interface NestSmsConfig {
37
41
  apiKey: string;
38
42
  host?: string;
39
43
  protocol?: 'http' | 'https';
44
+ debug?: boolean;
40
45
  }
41
46
  export interface NestSendResponse {
42
47
  handshake: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unismsgateway",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "A unified SMS gateway library that brings access to multiple SMS gateways under a single API",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",