From 9e09784922ab1deeeb84faa02e3c3d453a424dca Mon Sep 17 00:00:00 2001 From: Erik Harding Date: Thu, 14 Sep 2023 16:04:57 +0200 Subject: [PATCH] Send chw welcome message to check sms active --- Gruntfile.js | 2 + go-app-ussd_ccmdd_wc_address_update.js | 1 - go-app-ussd_chw_rapidpro.js | 102 ++++++++++++++++- go-app-ussd_clinic_rapidpro.js | 1 - go-app-ussd_higherhealth_healthcheck.js | 1 - go-app-ussd_mcgcc_rapidpro.js | 1 - go-app-ussd_mqr_faqs.js | 1 - go-app-ussd_nurse_rapidpro.js | 1 - go-app-ussd_optout_rapidpro.js | 1 - go-app-ussd_pmtct_rapidpro.js | 1 - go-app-ussd_popi_rapidpro.js | 1 - go-app-ussd_public_rapidpro.js | 1 - go-app-ussd_tb_check.js | 1 - src/ussd_chw_rapidpro.js | 58 +++++++++- test/ussd_chw_rapidpro.test.js | 140 +++++++++++++++++++++++- 15 files changed, 296 insertions(+), 17 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 9f3999ab..ae702a86 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -52,6 +52,7 @@ module.exports = function (grunt) { ], ussd_chw_rapidpro: [ 'src/index.js', + 'src/hub.js', 'src/rapidpro.js', '<%= paths.src.app.ussd_chw_rapidpro %>', 'src/init.js' @@ -152,6 +153,7 @@ module.exports = function (grunt) { ], ussd_chw_rapidpro: [ 'test/setup.js', + 'src/hub.js', 'src/rapidpro.js', '<%= paths.src.app.ussd_chw_rapidpro %>', 'test/ussd_chw_rapidpro.test.js' diff --git a/go-app-ussd_ccmdd_wc_address_update.js b/go-app-ussd_ccmdd_wc_address_update.js index 21ddfa04..0451d12b 100644 --- a/go-app-ussd_ccmdd_wc_address_update.js +++ b/go-app-ussd_ccmdd_wc_address_update.js @@ -146,7 +146,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/go-app-ussd_chw_rapidpro.js b/go-app-ussd_chw_rapidpro.js index 3fd12b38..f7e9511c 100644 --- a/go-app-ussd_chw_rapidpro.js +++ b/go-app-ussd_chw_rapidpro.js @@ -1,6 +1,49 @@ var go = {}; go; +go.Hub = function() { + var vumigo = require('vumigo_v02'); + var events = vumigo.events; + var Eventable = events.Eventable; + var url = require("url"); + + var Hub = Eventable.extend(function(self, json_api, base_url, auth_token) { + self.json_api = json_api; + self.base_url = base_url; + self.auth_token = auth_token; + self.json_api.defaults.headers.Authorization = ['Token ' + self.auth_token]; + + self.send_whatsapp_template_message = function(msisdn, template_name, media) { + var api_url = url.resolve(self.base_url, "/api/v1/sendwhatsapptemplate"); + var data = { + "msisdn": msisdn, + "template_name": template_name + }; + if(media) { + data.media = media; + } + return self.json_api.post(api_url, {data: data}) + .then(function(response){ + return response.data.preferred_channel; + + }); + }; + + self.get_whatsapp_failure_count = function(msisdn) { + var api_url = url.resolve(self.base_url, "/api/v2/deliveryfailure/" + msisdn + "/"); + + return self.json_api.get(api_url) + .then( + function(response){ + return response.data.number_of_failures; + } + ); + }; + + }); + return Hub; +}(); + go.RapidPro = function() { var vumigo = require('vumigo_v02'); var url_utils = require('url'); @@ -99,7 +142,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; @@ -141,6 +183,15 @@ go.app = function() { self.im.config.services.rapidpro.base_url, self.im.config.services.rapidpro.token ); + self.hub = new go.Hub( + new JsonApi(self.im, { + headers: { + 'User-Agent': ["Jsbox/NDoH-Clinic"] + } + }), + self.im.config.services.hub.base_url, + self.im.config.services.hub.token + ); }; self.add = function(name, creator) { @@ -594,7 +645,51 @@ go.app = function() { ); } }, - next: "state_trigger_rapidpro_flow" + next: "state_send_welcome_template" + }); + }); + + self.add("state_send_welcome_template", function(name, opts) { + var msisdn = utils.normalize_msisdn( + _.get(self.im.user.answers, "state_enter_msisdn", self.im.user.addr), "ZA"); + var template_name = self.im.config.welcome_template; + return self.hub + .send_whatsapp_template_message(msisdn, template_name) + .then(function(preferred_channel) { + self.im.user.set_answer("preferred_channel", preferred_channel); + if (preferred_channel == "SMS") { + return self.rapidpro.get_global_flag("sms_registrations_enabled") + .then(function(sms_registration_enabled) { + if (sms_registration_enabled) { + return self.states.create("state_trigger_rapidpro_flow"); + } + else{ + return self.states.create("state_sms_registration_not_available"); + } + }); + } + return self.states.create("state_trigger_rapidpro_flow"); + }).catch(function(e) { + // Go to error state after 3 failed HTTP requests + opts.http_error_count = _.get(opts, "http_error_count", 0) + 1; + if (opts.http_error_count === 3) { + self.im.log.error(e.message); + return self.states.create("__error__", { + return_state: name + }); + } + return self.states.create(name, opts); + }); + }); + + self.states.add("state_sms_registration_not_available", function(name) { + return new EndState(name, { + next: "state_start", + text: $([ + "It seems this number is not on WhatsApp and we don't offer SMS at this moment.", + "", + "Please register the new number on WhatsApp for the MomConnect Service." + ].join("\n")) }); }); @@ -626,7 +721,8 @@ go.app = function() { "YYYYMMDD" ).format(), passport_origin: self.im.user.answers.state_passport_country, - passport_number: self.im.user.answers.state_passport_no + passport_number: self.im.user.answers.state_passport_no, + preferred_channel: self.im.user.answers.preferred_channel }; return self.rapidpro .start_flow(self.im.config.flow_uuid, null, "whatsapp:" + _.trim(msisdn, "+"), data) diff --git a/go-app-ussd_clinic_rapidpro.js b/go-app-ussd_clinic_rapidpro.js index 6b42c226..55ab2ca2 100644 --- a/go-app-ussd_clinic_rapidpro.js +++ b/go-app-ussd_clinic_rapidpro.js @@ -142,7 +142,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/go-app-ussd_higherhealth_healthcheck.js b/go-app-ussd_higherhealth_healthcheck.js index cdfd1076..7b262e25 100644 --- a/go-app-ussd_higherhealth_healthcheck.js +++ b/go-app-ussd_higherhealth_healthcheck.js @@ -99,7 +99,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/go-app-ussd_mcgcc_rapidpro.js b/go-app-ussd_mcgcc_rapidpro.js index a6fcf217..54a9a218 100644 --- a/go-app-ussd_mcgcc_rapidpro.js +++ b/go-app-ussd_mcgcc_rapidpro.js @@ -146,7 +146,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/go-app-ussd_mqr_faqs.js b/go-app-ussd_mqr_faqs.js index d837fe31..0a4a029c 100644 --- a/go-app-ussd_mqr_faqs.js +++ b/go-app-ussd_mqr_faqs.js @@ -141,7 +141,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/go-app-ussd_nurse_rapidpro.js b/go-app-ussd_nurse_rapidpro.js index be4999a7..eb4787ec 100644 --- a/go-app-ussd_nurse_rapidpro.js +++ b/go-app-ussd_nurse_rapidpro.js @@ -99,7 +99,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/go-app-ussd_optout_rapidpro.js b/go-app-ussd_optout_rapidpro.js index 5f1256b2..f5a10c4c 100644 --- a/go-app-ussd_optout_rapidpro.js +++ b/go-app-ussd_optout_rapidpro.js @@ -99,7 +99,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/go-app-ussd_pmtct_rapidpro.js b/go-app-ussd_pmtct_rapidpro.js index b7d9a9b5..4a20c723 100644 --- a/go-app-ussd_pmtct_rapidpro.js +++ b/go-app-ussd_pmtct_rapidpro.js @@ -99,7 +99,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/go-app-ussd_popi_rapidpro.js b/go-app-ussd_popi_rapidpro.js index cdc7fb1d..305499db 100644 --- a/go-app-ussd_popi_rapidpro.js +++ b/go-app-ussd_popi_rapidpro.js @@ -142,7 +142,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/go-app-ussd_public_rapidpro.js b/go-app-ussd_public_rapidpro.js index 5e28b8ea..81c9d59b 100644 --- a/go-app-ussd_public_rapidpro.js +++ b/go-app-ussd_public_rapidpro.js @@ -146,7 +146,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/go-app-ussd_tb_check.js b/go-app-ussd_tb_check.js index 88c62018..952a9c90 100644 --- a/go-app-ussd_tb_check.js +++ b/go-app-ussd_tb_check.js @@ -99,7 +99,6 @@ go.RapidPro = function() { self.get_global_flag = function(global_name) { var url = self.base_url + "/api/v2/globals.json"; - return self.json_api.get(url, {params: {key: global_name}}) .then(function(response){ var results = response.data.results; diff --git a/src/ussd_chw_rapidpro.js b/src/ussd_chw_rapidpro.js index 14e69dd3..8f1c1951 100644 --- a/src/ussd_chw_rapidpro.js +++ b/src/ussd_chw_rapidpro.js @@ -23,6 +23,15 @@ go.app = function() { self.im.config.services.rapidpro.base_url, self.im.config.services.rapidpro.token ); + self.hub = new go.Hub( + new JsonApi(self.im, { + headers: { + 'User-Agent': ["Jsbox/NDoH-Clinic"] + } + }), + self.im.config.services.hub.base_url, + self.im.config.services.hub.token + ); }; self.add = function(name, creator) { @@ -476,7 +485,51 @@ go.app = function() { ); } }, - next: "state_trigger_rapidpro_flow" + next: "state_send_welcome_template" + }); + }); + + self.add("state_send_welcome_template", function(name, opts) { + var msisdn = utils.normalize_msisdn( + _.get(self.im.user.answers, "state_enter_msisdn", self.im.user.addr), "ZA"); + var template_name = self.im.config.welcome_template; + return self.hub + .send_whatsapp_template_message(msisdn, template_name) + .then(function(preferred_channel) { + self.im.user.set_answer("preferred_channel", preferred_channel); + if (preferred_channel == "SMS") { + return self.rapidpro.get_global_flag("sms_registrations_enabled") + .then(function(sms_registration_enabled) { + if (sms_registration_enabled) { + return self.states.create("state_trigger_rapidpro_flow"); + } + else{ + return self.states.create("state_sms_registration_not_available"); + } + }); + } + return self.states.create("state_trigger_rapidpro_flow"); + }).catch(function(e) { + // Go to error state after 3 failed HTTP requests + opts.http_error_count = _.get(opts, "http_error_count", 0) + 1; + if (opts.http_error_count === 3) { + self.im.log.error(e.message); + return self.states.create("__error__", { + return_state: name + }); + } + return self.states.create(name, opts); + }); + }); + + self.states.add("state_sms_registration_not_available", function(name) { + return new EndState(name, { + next: "state_start", + text: $([ + "It seems this number is not on WhatsApp and we don't offer SMS at this moment.", + "", + "Please register the new number on WhatsApp for the MomConnect Service." + ].join("\n")) }); }); @@ -508,7 +561,8 @@ go.app = function() { "YYYYMMDD" ).format(), passport_origin: self.im.user.answers.state_passport_country, - passport_number: self.im.user.answers.state_passport_no + passport_number: self.im.user.answers.state_passport_no, + preferred_channel: self.im.user.answers.preferred_channel }; return self.rapidpro .start_flow(self.im.config.flow_uuid, null, "whatsapp:" + _.trim(msisdn, "+"), data) diff --git a/test/ussd_chw_rapidpro.test.js b/test/ussd_chw_rapidpro.test.js index f328dc9f..11c0102f 100644 --- a/test/ussd_chw_rapidpro.test.js +++ b/test/ussd_chw_rapidpro.test.js @@ -1,6 +1,7 @@ var vumigo = require("vumigo_v02"); var AppTester = vumigo.AppTester; var assert = require("assert"); +var fixtures_hub = require("./fixtures_hub")(); var fixtures_rapidpro = require("./fixtures_rapidpro")(); describe("ussd_chw app", function() { @@ -16,9 +17,14 @@ describe("ussd_chw app", function() { rapidpro: { base_url: "https://rapidpro", token: "rapidpro-token" + }, + hub: { + base_url: "http://hub", + token: "hub-token" } }, - flow_uuid: "rapidpro-flow-uuid" + flow_uuid: "rapidpro-flow-uuid", + welcome_template: "test-welcome-template" }); }); describe("state_start", function() { @@ -706,6 +712,136 @@ describe("ussd_chw app", function() { .run(); }); }); + describe("state_send_welcome_template", function() { + it("should go to state_sms_registration_not_available if sms is not enabled", function() { + return tester + .setup.user.state("state_send_welcome_template") + .setup(function(api) { + api.http.fixtures.add( + fixtures_hub.send_whatsapp_template_message( + "+27123456789", + "test-welcome-template", + null, + "SMS" + ) + ); + api.http.fixtures.add( + fixtures_rapidpro.get_global_flag("sms_registrations_enabled", "FALSE") + ); + }) + .input({session_event: "continue"}) + .check.interaction({ + state: "state_sms_registration_not_available", + reply: [ + "It seems this number is not on WhatsApp and we don't offer SMS at this moment.", + "", + "Please register the new number on WhatsApp for the MomConnect Service." + ].join("\n"), + char_limit: 160 + }) + .check.reply.ends_session() + .run(); + }); + it("should go through state_trigger_rapidpro_flow if sms is enabled", function() { + return tester + .setup.user.state("state_send_welcome_template") + .setup.user.answers({ + state_research_consent: "no", + state_enter_msisdn: "0820001001", + state_id_type: "state_sa_id_no", + state_sa_id_no: "9001020005087", + }) + .setup(function(api) { + api.http.fixtures.add( + fixtures_hub.send_whatsapp_template_message( + "+27820001001", + "test-welcome-template", + null, + "SMS" + ) + ); + api.http.fixtures.add( + fixtures_rapidpro.get_global_flag("sms_registrations_enabled", "TRUE") + ); + api.http.fixtures.add( + fixtures_rapidpro.start_flow( + "rapidpro-flow-uuid", + null, + "whatsapp:27820001001", + { + research_consent:"FALSE", + registered_by: "+27123456789", + language: "eng", + timestamp: "2014-04-04T07:07:07Z", + source: "CHW USSD", + id_type: "sa_id", + sa_id_number: "9001020005087", + dob: "1990-01-02T00:00:00Z", + preferred_channel: "SMS" + } + ) + ); + }) + .input({session_event: "continue"}) + .check.interaction({ + state: "state_registration_complete", + reply: + "You're done! 0820001001 will get helpful messages from " + + "MomConnect. To sign up for the full set of messages, " + + "visit a clinic. Have a lovely day!" + }) + .check.reply.ends_session() + .run(); + }); + it("should go through state_trigger_rapidpro_flow if whatsapp template is sent", function() { + return tester + .setup.user.state("state_send_welcome_template") + .setup.user.answers({ + state_research_consent: "no", + state_enter_msisdn: "0820001001", + state_id_type: "state_sa_id_no", + state_sa_id_no: "9001020005087", + }) + .setup(function(api) { + api.http.fixtures.add( + fixtures_hub.send_whatsapp_template_message( + "+27820001001", + "test-welcome-template", + null, + "WhatsApp" + ) + ); + api.http.fixtures.add( + fixtures_rapidpro.start_flow( + "rapidpro-flow-uuid", + null, + "whatsapp:27820001001", + { + research_consent:"FALSE", + registered_by: "+27123456789", + language: "eng", + timestamp: "2014-04-04T07:07:07Z", + source: "CHW USSD", + id_type: "sa_id", + sa_id_number: "9001020005087", + dob: "1990-01-02T00:00:00Z", + preferred_channel: "WhatsApp" + } + ) + ); + }) + .input({session_event: "continue"}) + .check.interaction({ + state: "state_registration_complete", + reply: + "You're done! 0820001001 will get helpful messages from " + + "MomConnect. To sign up for the full set of messages, " + + "visit a clinic. Have a lovely day!" + }) + .check.reply.ends_session() + .run(); + }); + }); describe("state_trigger_rapidpro_flow", function() { it("should start a flow with the correct metadata", function() { return tester @@ -715,6 +851,7 @@ describe("ussd_chw app", function() { state_enter_msisdn: "0820001001", state_id_type: "state_sa_id_no", state_sa_id_no: "9001020005087", + preferred_channel: "WhatsApp", }) .setup(function(api) { api.http.fixtures.add( @@ -731,6 +868,7 @@ describe("ussd_chw app", function() { id_type: "sa_id", sa_id_number: "9001020005087", dob: "1990-01-02T00:00:00Z", + preferred_channel: "WhatsApp", } ) );