Commit a21e7a161eb3b8eeb6e6885d17887b2f29ce635f

Authored by Adhidarma Hadiwinoto
1 parent f031376361
Exists in master

modem-chooser for sending message

Showing 4 changed files with 73 additions and 2 deletions Inline Diff

1 { 1 {
2 "name": "SMS", 2 "name": "SMS",
3 3
4 "# messaging_url": "messaging service url", 4 "# messaging_url": "messaging service url",
5 "messaging_url": "http://localhost:32979/", 5 "messaging_url": "http://localhost:32979/",
6 6
7 "# listen_port": "HTTP port untuk mendapat perintah dari CORE", 7 "# listen_port": "HTTP port untuk mendapat perintah dari CORE",
8 "listen_port": 16480, 8 "listen_port": 16480,
9 9
10 "apiserver": { 10 "apiserver": {
11 "# listen_port": "HTTP port untuk mendapat pesan masuk dari modem handler", 11 "# listen_port": "HTTP port untuk mendapat pesan masuk dari modem handler",
12 "listen_port": 16481, 12 "listen_port": 16481,
13 "apikey": "PLEASE_CHANGE_ME" 13 "apikey": "PLEASE_CHANGE_ME"
14 }, 14 },
15 15
16 "number_suffix": "@phonenumber", 16 "number_suffix": "@phonenumber",
17 17
18 "modems": { 18 "modems": {
19 "SMS0": { 19 "SMS0": {
20 "url": "http://localhost:2110/sms", 20 "url": "http://localhost:2110/sms",
21 "apikey": "PLEASE_CHANGE_ME" 21 "apikey": "PLEASE_CHANGE_ME"
22 } 22 }
23 }, 23 },
24 24
25 "do_not_trim_long_sms": false, 25 "do_not_trim_long_sms": false,
26 26
27 "default_modem": "SMS0", 27 "default_modem": "SMS0",
28 28
29 "# handler_chooser_algorithm": "algoritma untuk memilih modem dalam mengirim SMS. Pilihan: LAST-SEEN, FORCED. Default: LAST-SEEN", 29 "# handler_chooser_algorithm": "algoritma untuk memilih modem dalam mengirim SMS. Pilihan: LAST-SEEN, FORCED. Default: LAST-SEEN",
30 "handler_chooser_algorithm": "LAST-SEEN", 30 "handler_chooser_algorithm": "LAST-SEEN",
31 31
32 "# sending handler": "list modem yang dipakai untuk mengirim jika handler_chooser_algorithm === 'FORCED'", 32 "# sending handler": "list modem yang dipakai untuk mengirim jika handler_chooser_algorithm === 'FORCED'",
33 "sending_handler": [ 33 "sending_handler": [
34 "SMS0", 34 "SMS0",
35 "SMS1" 35 "SMS1"
36 ], 36 ],
37 37
38 "imsi_senders": {
39 "prefix_names": {
40 "TELKOMSEL": [
41 "510890944235402"
42 ],
43 "XL": [
44 "510890944262917"
45 ]
46 },
47 "default": [
48 "510890944235513"
49 ]
50 },
51
38 "redis": { 52 "redis": {
39 "host": "127.0.0.1" 53 "host": "127.0.0.1"
40 } 54 }
41 } 55 }
1 "use strict"; 1 "use strict";
2 2
3 process.chdir(__dirname); 3 process.chdir(__dirname);
4 4
5 const fs = require('fs'); 5 const fs = require('fs');
6 fs.writeFileSync('pid.txt', process.pid); 6 fs.writeFileSync('pid.txt', process.pid);
7 7
8 const config = require('komodo-sdk/config'); 8 const config = require('komodo-sdk/config');
9 9
10 global.KOMODO_LOG_LABEL = `KOMODO-CENTER@${(config && typeof config.name === 'string') ? config.name.toUpperCase() : 'SMS'}`; 10 global.KOMODO_LOG_LABEL = `KOMODO-CENTER@${(config && typeof config.name === 'string') ? config.name.toUpperCase() : 'SMS'}`;
11 process.title = global.KOMODO_LOG_LABEL; 11 process.title = global.KOMODO_LOG_LABEL;
12 12
13 const logger = require('komodo-sdk/logger');
14 global.KOMODO_LOGGER = logger;
15
13 require('./lib/prefixes'); 16 require('./lib/prefixes');
14 require('./lib/transport'); 17 require('./lib/transport');
15 require('./lib/apiserver'); 18 require('./lib/apiserver');
16 19
lib/modem-chooser.js
File was created 1 'use strict';
2
3 const prefixes = require('./prefixes');
4 const modems = require('./modems2');
5 const partnerLastSeen = require('./partner-last-seen');
6
7 function filterOutCandidates(candidates) {
8 if (!Array.isArray(candidates)) {
9 return [];
10 }
11
12 return candidates.filter((item) => {
13 const modem = modems.get('by_imsi', item);
14
15 if (!modem) return false;
16 return true;
17 });
18 }
19
20 exports.chooser = async function chooser(destination, config) {
21 const logger = global.KOMODO_LOGGER;
22
23 const prefixName = await prefixes.lookup(destination);
24 if (logger) logger.verbose('Choosing suitable senders', { destination, prefixName });
25
26 let imsiSenders = [];
27 if (config.imsi_senders && config.imsi_senders.prefix_names && config.imsi_senders.prefix_names[prefixName]) {
28 imsiSenders = filterOutCandidates(config.imsi_senders.prefix_names[prefixName]);
29 if (logger) logger.verbose('Suitable senders by prefix name', { destination, prefixName, imsiSenders });
30 }
31
32 if (!imsiSenders.length && config.imsi_senders.default && Array.isArray(config.imsi_senders.default) && config.imsi_senders.default.length) {
33 imsiSenders = filterOutCandidates(config.imsi_senders.default);
34 if (logger) logger.verbose('Suitable default senders', { destination, prefixName, imsiSenders });
35 }
36
37 if (!imsiSenders.length) {
38 imsiSenders = filterOutCandidates([ await partnerLastSeen.get(destination) ]);
39 if (logger) logger.verbose('Suitable senders by last seen', { destination, prefixName, imsiSenders });
40 }
41
42 if (!imsiSenders.length) {
43 if (logger) logger.verbose('No suitable sender found', { destination, prefixName });
44 return;
45 }
46
47 const count = imsiSenders.length;
48 const idx = Math.round(Math.random() * (count - 1));
49 const imsiChoosed = imsiSenders[idx];
50 if (logger) logger.verbose(`Choose modem with IMSI ${imsiChoosed}`, { destination, prefixName, imsiSenders });
51 return imsiChoosed;
52 }
1 "use strict"; 1 "use strict";
2 2
3 const MAX_SMS_LENGTH = 160; 3 const MAX_SMS_LENGTH = 160;
4 4
5 const url = require('url'); 5 const url = require('url');
6 const request = require('request'); 6 const request = require('request');
7 const uuidv4 = require('uuid/v4'); 7 const uuidv4 = require('uuid/v4');
8 const moment = require('moment'); 8 const moment = require('moment');
9 9
10 const config = require('komodo-sdk/config'); 10 const config = require('komodo-sdk/config');
11 const logger = require('komodo-sdk/logger'); 11 const logger = require('komodo-sdk/logger');
12 12
13 const messagingService = require('komodo-center-messaging-client-lib'); 13 const messagingService = require('komodo-center-messaging-client-lib');
14 14
15 const common = require('./common'); 15 const common = require('./common');
16 const modems = require('./modems2'); 16 const modems = require('./modems2');
17 const partnerLastSeen = require('./partner-last-seen'); 17 const modemChooser = require('./modem-chooser');
18 // const partnerLastSeen = require('./partner-last-seen');
18 const history = require('./history'); 19 const history = require('./history');
19 const prefixes = require('./prefixes'); 20 const prefixes = require('./prefixes');
20 21
21 function _send(destinationNumber, msg, handlerIMSI) { 22 function _send(destinationNumber, msg, handlerIMSI) {
22 23
23 if (msg.length > 160) { 24 if (msg.length > 160) {
24 logger.info('Splitting message'); 25 logger.info('Splitting message');
25 26
26 const newMsg = msg.slice(0, MAX_SMS_LENGTH); 27 const newMsg = msg.slice(0, MAX_SMS_LENGTH);
27 const remainingMsg = msg.slice(MAX_SMS_LENGTH); 28 const remainingMsg = msg.slice(MAX_SMS_LENGTH);
28 29
29 _send(destinationNumber, newMsg, handlerIMSI); 30 _send(destinationNumber, newMsg, handlerIMSI);
30 setTimeout(() => { 31 setTimeout(() => {
31 _send(destinationNumber, remainingMsg, handlerIMSI); 32 _send(destinationNumber, remainingMsg, handlerIMSI);
32 }, 1000); 33 }, 1000);
33 34
34 return; 35 return;
35 } 36 }
36 37
37 const modem = modems.get('imsi', handlerIMSI); 38 const modem = modems.get('imsi', handlerIMSI);
38 if (!modem) { 39 if (!modem) {
39 logger.warn('Not knowing modem to use. Ignoring message', { destination_number: destinationNumber, msg: msg, modem_imsi: handlerIMSI }); 40 logger.warn('Not knowing modem to use. Ignoring message', { destination_number: destinationNumber, msg: msg, modem_imsi: handlerIMSI });
40 return; 41 return;
41 } 42 }
42 43
43 if (!modem.reportIp || !modem.reportPort || !modem.reportApikey) { 44 if (!modem.reportIp || !modem.reportPort || !modem.reportApikey) {
44 logger.warn('Invalid modem configuration', { modem }); 45 logger.warn('Invalid modem configuration', { modem });
45 return; 46 return;
46 } 47 }
47 48
48 const reqId = uuidv4(); 49 const reqId = uuidv4();
49 50
50 history.push({ 51 history.push({
51 ts: moment().format('YYYY-MM-DD HH:mm:ss'), 52 ts: moment().format('YYYY-MM-DD HH:mm:ss'),
52 modem: { 53 modem: {
53 name: modem.name, 54 name: modem.name,
54 imsi: modem.imsi, 55 imsi: modem.imsi,
55 msisdn: modem.msisdn, 56 msisdn: modem.msisdn,
56 }, 57 },
57 direction: 'OUTGOING', 58 direction: 'OUTGOING',
58 partner: destinationNumber, 59 partner: destinationNumber,
59 message: msg, 60 message: msg,
60 }); 61 });
61 62
62 logger.verbose('TRANSPORT: saving outgoing message'); 63 logger.verbose('TRANSPORT: saving outgoing message');
63 messagingService.onIncomingMessage({ 64 messagingService.onIncomingMessage({
64 me: modem.name, 65 me: modem.name,
65 partner: destinationNumber, 66 partner: destinationNumber,
66 partner_raw: `+${destinationNumber}`.replace(/^\++/, '+'), 67 partner_raw: `+${destinationNumber}`.replace(/^\++/, '+'),
67 msg: msg, 68 msg: msg,
68 origin_label: `IMSI_${modem.imsi || 'UNKNOWN'}`, 69 origin_label: `IMSI_${modem.imsi || 'UNKNOWN'}`,
69 origin_transport: 'SMS', 70 origin_transport: 'SMS',
70 origin_partner: destinationNumber, 71 origin_partner: destinationNumber,
71 do_not_forward_to_core: true, 72 do_not_forward_to_core: true,
72 is_outgoing: true, 73 is_outgoing: true,
73 }); 74 });
74 75
75 const requestOptions = { 76 const requestOptions = {
76 url: url.format({ 77 url: url.format({
77 protocol: 'http', 78 protocol: 'http',
78 hostname: modem.reportIp, 79 hostname: modem.reportIp,
79 port: modem.reportPort, 80 port: modem.reportPort,
80 pathname: modem.reportPathSms || '/sms', 81 pathname: modem.reportPathSms || '/sms',
81 }), 82 }),
82 qs: { 83 qs: {
83 msg: msg, 84 msg: msg,
84 number: destinationNumber, 85 number: destinationNumber,
85 reqid: reqId, 86 reqid: reqId,
86 apikey: modem.reportApikey, 87 apikey: modem.reportApikey,
87 } 88 }
88 } 89 }
89 90
90 logger.info('Sending message to modem handler', { req_id: reqId, destination_number: destinationNumber, msg: msg, msg_length: msg.length, modem_name: modem.name, modem_imsi: modem.imsi }); 91 logger.info('Sending message to modem handler', { req_id: reqId, destination_number: destinationNumber, msg: msg, msg_length: msg.length, modem_name: modem.name, modem_imsi: modem.imsi });
91 request(requestOptions, function(err, res, body) { 92 request(requestOptions, function(err, res, body) {
92 if (err) { 93 if (err) {
93 logger.warn('Error requesting to modem handler. ' + err.toString(), { req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi }); 94 logger.warn('Error requesting to modem handler. ' + err.toString(), { req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi });
94 95
95 } 96 }
96 else if (res.statusCode != 200) { 97 else if (res.statusCode != 200) {
97 logger.warn('Modem handler not responding with HTTP status code 200.', { http_status_code: res.statusCode, req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi }); 98 logger.warn('Modem handler not responding with HTTP status code 200.', { http_status_code: res.statusCode, req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi });
98 } 99 }
99 else { 100 else {
100 logger.verbose('Message sent to handler', { req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi, response_body: body }); 101 logger.verbose('Message sent to handler', { req_id: reqId, modem_name: modem.name, modem_imsi: modem.imsi, response_body: body });
101 } 102 }
102 }) 103 })
103 104
104 } 105 }
105 106
106 async function send(partner, msg) { 107 async function send(partner, msg) {
107 if (!partner) return; 108 if (!partner) return;
108 109
109 if (typeof msg !== 'string') { 110 if (typeof msg !== 'string') {
110 logger.warn('Message to send is not a string, ignoring message'); 111 logger.warn('Message to send is not a string, ignoring message');
111 return; 112 return;
112 } 113 }
113 114
114 msg = msg.trim(); 115 msg = msg.trim();
115 if (!msg) return; 116 if (!msg) return;
116 117
117 const destinationNumber = common.removeSuffixFromNumber(partner, config.number_suffix); 118 const destinationNumber = common.removeSuffixFromNumber(partner, config.number_suffix);
118 const prefixName = await prefixes.lookup(destinationNumber); 119 const prefixName = await prefixes.lookup(destinationNumber);
119 logger.verbose('Destination number prefix lookup', {partner: destinationNumber, prefix: prefixName}); 120 logger.verbose('Destination number prefix lookup', {partner: destinationNumber, prefix: prefixName});
120 121
121 // logger.verbose('Choosing handler name', { partner, destinationNumber, msg, origin }); 122 // logger.verbose('Choosing handler name', { partner, destinationNumber, msg, origin });
122 const handlerIMSI = await partnerLastSeen.get(destinationNumber) ; 123 // const handlerIMSI = await partnerLastSeen.get(destinationNumber) ;
124 const handlerIMSI = await modemChooser.chooser(destinationNumber, config);
123 125
124 if (!handlerIMSI) { 126 if (!handlerIMSI) {
125 logger.warn(`Unknown handler for sending message to partner`, { partner, destinationNumber }); 127 logger.warn(`Unknown handler for sending message to partner`, { partner, destinationNumber });
126 return; 128 return;
127 } 129 }
128 130
129 _send(destinationNumber, msg, handlerIMSI); 131 _send(destinationNumber, msg, handlerIMSI);
130 } 132 }
131 133
132 exports.send = send; 134 exports.send = send;