Commit f53fe083fc8cd09e46ceb45818a36e177c3027d0
1 parent
30ac636c83
Exists in
master
Sender chooser algo
Showing 5 changed files with 125 additions and 7 deletions Side-by-side Diff
config.sample.json
... | ... | @@ -22,5 +22,18 @@ |
22 | 22 | |
23 | 23 | "do_not_trim_long_sms": false, |
24 | 24 | |
25 | - "default_modem": "SMS0" | |
25 | + "default_modem": "SMS0", | |
26 | + | |
27 | + "# handler_chooser_algorithm": "algoritma untuk memilih modem dalam mengirim SMS. Pilihan: LAST-SEEN, FORCED. Default: LAST-SEEN", | |
28 | + "handler_chooser_algorithm": "LAST-SEEN", | |
29 | + | |
30 | + "# sending handler": "list modem yang dipakai untuk mengirim jika handler_chooser_algorithm === 'FORCED'", | |
31 | + "sending_handler": [ | |
32 | + "SMS0", | |
33 | + "SMS1" | |
34 | + ], | |
35 | + | |
36 | + "redis": { | |
37 | + "host": "127.0.0.1" | |
38 | + } | |
26 | 39 | } |
27 | 40 | \ No newline at end of file |
lib/handler-callback-server.js
... | ... | @@ -13,6 +13,7 @@ const config = require('komodo-sdk/config'); |
13 | 13 | const logger = require('komodo-sdk/logger'); |
14 | 14 | |
15 | 15 | const transport = require('./transport'); |
16 | +const partnerLastSeen = require('./partner-last-seen'); | |
16 | 17 | |
17 | 18 | const app = express(); |
18 | 19 | messagingService.setTransport(transport); |
... | ... | @@ -31,6 +32,8 @@ function onIncomingSms(req, res) { |
31 | 32 | res.end('OK'); |
32 | 33 | const numberWithSuffix = req.query.number + (config.number_suffix || ''); |
33 | 34 | |
35 | + partnerLastSeen.set(req.query.number, req.query.modem); | |
36 | + | |
34 | 37 | logger.info('HANDLER-CALLBACK-SERVER: Incoming SMS', { modem: req.query.modem, from: req.query.number, from_with_suffix: numberWithSuffix, msg: req.query.msg }); |
35 | 38 | messagingService.onIncomingMessage({ |
36 | 39 | me: req.query.modem, |
lib/partner-last-seen.js
... | ... | @@ -0,0 +1,57 @@ |
1 | +"use strict"; | |
2 | + | |
3 | +const REDIS_TTL_SECS = 3600 * 24 * 7; | |
4 | + | |
5 | +const config = require('komodo-sdk/config'); | |
6 | + | |
7 | +const redis = require('redis'); | |
8 | +const redisClient = redis.createClient(config.redis || { host: '127.0.0.1' }); | |
9 | + | |
10 | +const _caches = {}; | |
11 | + | |
12 | + | |
13 | +function _composeKeyword(partner) { | |
14 | + return `POCHINKI_PARTNER_LAST_SEEN_${ partner }`; | |
15 | +} | |
16 | + | |
17 | +function get(partnerNumber) { | |
18 | + return new Promise(function(resolve) { | |
19 | + | |
20 | + if (!partnerNumber) { | |
21 | + resolve(null); | |
22 | + } | |
23 | + else if (_caches[partnerNumber]) { | |
24 | + resolve(_caches[partnerNumber]); | |
25 | + } | |
26 | + else { | |
27 | + const keyword = _composeKeyword(partnerNumber); | |
28 | + | |
29 | + redisClient.get(keyword, function(err, reply) { | |
30 | + if (err) { | |
31 | + resolve(null); | |
32 | + } | |
33 | + else if (reply) { | |
34 | + resolve(Number(reply)); | |
35 | + _caches[partnerNumber] = Number(reply); | |
36 | + } | |
37 | + else { | |
38 | + resolve(null); | |
39 | + } | |
40 | + }) | |
41 | + } | |
42 | + }) | |
43 | +} | |
44 | + | |
45 | +function set(partnerNumber, modemName) { | |
46 | + if (!partnerNumber || !modemName) { | |
47 | + return; | |
48 | + } | |
49 | + | |
50 | + _caches[partnerNumber] = modemName; | |
51 | + | |
52 | + const keyword = _composeKeyword(partnerNumber); | |
53 | + redisClient.set(keyword, modemName, 'EX', REDIS_TTL_SECS); | |
54 | +} | |
55 | + | |
56 | +exports.get = get; | |
57 | +exports.set = set; |
lib/transport.js
... | ... | @@ -7,8 +7,50 @@ const config = require('komodo-sdk/config'); |
7 | 7 | const logger = require('komodo-sdk/logger'); |
8 | 8 | |
9 | 9 | const modems = require('./modems'); |
10 | +const partnerLastSeen = require('./partner-last-seen'); | |
10 | 11 | |
11 | -function send(partner, msg, origin) { | |
12 | +async function _getApproriateHandlerByLastSeen(partnerNumber) { | |
13 | + logger.verbose('Looking for last seen on for partner number ' + partnerNumber); | |
14 | + const lastSeenFrom = await partnerLastSeen.get(partnerNumber); | |
15 | + return lastSeenFrom; | |
16 | +} | |
17 | + | |
18 | +function _getApproriateHandlerByForced() { | |
19 | + if (!config.sending_handler || !config.sending_handler.length) return; | |
20 | + | |
21 | + const sendingHandlerCount = config.sending_handler.length; | |
22 | + const idx = Math.floor(Math.random() * sendingHandlerCount); | |
23 | + return config.sending_handler[idx]; | |
24 | +} | |
25 | + | |
26 | +async function _getApproriateHandler(partnerNumber, origin) { | |
27 | + let handlerToUse; | |
28 | + | |
29 | + if (config.handler_chooser_algorithm === 'FORCED') { | |
30 | + handlerToUse = _getApproriateHandlerByForced(); | |
31 | + logger.verbose('Config file mentioned to using FORCED handler chooser algorithm', { handler_to_use: handlerToUse}); | |
32 | + } | |
33 | + else { | |
34 | + handlerToUse = await _getApproriateHandlerByLastSeen(partnerNumber); | |
35 | + logger.verbose('Config file mentioned to using LAST-SEEN handler chooser algorithm', { handler_to_use: handlerToUse}); | |
36 | + } | |
37 | + | |
38 | + if (!modems.getModemConfig(handlerToUse, config.modems)) { | |
39 | + const handlerWithSameOrigin = modems.getModemConfig(origin, config.modems); | |
40 | + if (handlerWithSameOrigin) { | |
41 | + logger.verbose('Invalid approriate handler, using handler from the same ORIGIN request by CORE to send sms') | |
42 | + handlerToUse = origin; | |
43 | + } | |
44 | + else { | |
45 | + logger.verbose('Invalid approriate handler, using default handler to send sms') | |
46 | + handlerToUse = config.default_modem; | |
47 | + } | |
48 | + } | |
49 | + | |
50 | + return handlerToUse; | |
51 | +} | |
52 | + | |
53 | +async function send(partner, msg, origin) { | |
12 | 54 | if (!partner) return; |
13 | 55 | |
14 | 56 | if (typeof msg !== 'string') { |
... | ... | @@ -19,12 +61,17 @@ function send(partner, msg, origin) { |
19 | 61 | msg = msg.trim(); |
20 | 62 | if (!msg) return; |
21 | 63 | |
64 | + const reqId = uuidv4(); | |
65 | + | |
22 | 66 | if (msg.length > 160 && !config.do_not_trim_long_sms) { |
23 | 67 | logger.verbose('Message trim to 160 chars'); |
24 | 68 | msg = msg.slice(0, 156) + ' ...'; |
25 | 69 | } |
26 | 70 | |
27 | - const handlerName = origin || config.default_modem; | |
71 | + const destinationNumber = modems.removeSuffixFromNumber(partner, config); | |
72 | + | |
73 | + logger.verbose('Choosing handler name', { req_id: reqId, partner: partner, msg: msg, origin: origin }); | |
74 | + let handlerName = ( await _getApproriateHandler(destinationNumber) ); | |
28 | 75 | |
29 | 76 | const modem = modems.getModemConfig(handlerName, config.modems); |
30 | 77 | if (!modem) { |
... | ... | @@ -37,9 +84,6 @@ function send(partner, msg, origin) { |
37 | 84 | return; |
38 | 85 | } |
39 | 86 | |
40 | - const reqId = uuidv4(); | |
41 | - const destinationNumber = modems.removeSuffixFromNumber(partner, config); | |
42 | - | |
43 | 87 | const requestOptions = { |
44 | 88 | url: modem.url, |
45 | 89 | qs: { |
... | ... | @@ -50,7 +94,7 @@ function send(partner, msg, origin) { |
50 | 94 | } |
51 | 95 | } |
52 | 96 | |
53 | - logger.info('Sending message to modem handler', { req_id: reqId, partner: partner, destination_number: destinationNumber, msg: msg, handler_name: handlerName }); | |
97 | + logger.info('Sending message to modem handler', { req_id: reqId, partner: partner, destination_number: destinationNumber, msg: msg, msg_length: msg.length, handler_name: handlerName }); | |
54 | 98 | request(requestOptions, function(err, res, body) { |
55 | 99 | if (err) { |
56 | 100 | logger.warn('Error requesting to modem handler. ' + err.toString(), { req_id: reqId, handler_name: handlerName }); |
package.json
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | "express": "^4.17.1", |
23 | 23 | "komodo-center-messaging-client-lib": "git+http://gitlab.kodesumber.com/komodo/komodo-center-messaging-client-lib.git", |
24 | 24 | "komodo-sdk": "git+http://gitlab.kodesumber.com/komodo/komodo-sdk.git", |
25 | + "redis": "^2.8.0", | |
25 | 26 | "request": "^2.88.0", |
26 | 27 | "uuid": "^3.3.2" |
27 | 28 | }, |