Skip to content

Commit

Permalink
Merge branch 'twilio-messagereportfix' into stage-main-92a
Browse files Browse the repository at this point in the history
  • Loading branch information
schuyler1d committed Sep 9, 2020
2 parents 29ae405 + e979739 commit 7c0f868
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 38 deletions.
5 changes: 5 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@
"description": "ensures that the DB is connecting over SSL which is a requirement for Heroku DBs",
"required": true,
"value": "true"
},

"TWILIO_VALIDATION": {
"description": "Validate twilio message report links as well",
"value": "1"
}
},
"addons": [
Expand Down
2 changes: 1 addition & 1 deletion docs/HOWTO_INTEGRATE_TWILIO.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ If you follow the instructions above, every organization and campaign in your in
- For security, Twilio Auth Tokens are encrypted using the `SESSION_SECRET` environment variable before being stored in the database.
- You can still set instance-wide credentials in the .env file (as described above). If you do, those credentials will be used as fallback if credentials aren't configured for an organization.
- It is not required to configure all settings for all organizations. For example, to use a single site-wide Twilio account but with separate phone number pools for some organizations, follow the instuctions above and then set the Default Message Service SID (leaving the other fields blank) in the organizations settings for the orgs you want to override.
- When using multiple Twilio accounts you will need to change the Inbound Request URL for your messaging service in the Twilio console [step 7 above]. It should look like `https://<YOUR_APP_URL>/twilio/<ORG_ID>`. The correct URL to use will be displayed on the settings page after you save the Twilio credentials.
- When using multiple Twilio accounts you will need to change the Inbound Request URL for your messaging service in the Twilio console [step 7 above]. It should look like `https://<YOUR_APP_URL>/twilio/<ORG_ID>` and `https://<YOUR_APP_URL>/twilio-message-report/<ORG_ID>`. The correct URL to use will be displayed on the settings page after you save the Twilio credentials.
1 change: 1 addition & 0 deletions docs/REFERENCE-environment_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
| TWILIO_MULTI_ORG | Boolean value to indicate if organizations can override Twilio credentials in the organization settings. _Default_: false. |
| TWILIO_STATUS_CALLBACK_URL | URL for Twilio status callbacks. Should end with `/twilio-message-report`, e.g. `https://example.org/twilio-message-report`. Required if using Twilio. |
| TWILIO_SQS_QUEUE_URL | AWS SQS URL to handle incoming messages when app isn't connected to twilio |
| TWILIO_VALIDATION | Validate message report links as well -- you should enable this. It's only non-default for backwards compatibility reasons. |
| TWILIO_VOICE_URL | Global Twilio voice url for phone numbers provisioned through Spoke. If not set, the default Twilio voicemail will be used. |
| WAREHOUSE_DB_*X*<br>{TYPE,HOST,PORT,NAME,USER,PASSWORD,SCHEMA,USE_SSL} | Enables ability to load contacts directly from a SQL query from a separate data-warehouse db -- only is_superadmin-marked users will see the interface |
| WAREHOUSE_DB_LAMBDA_ITERATION | If the WAREHOUSE*DB* connection/feature is enabled, then on AWS Lambda, queries that take longer than 5min can expire. This will enable incrementing through queries on new lambda invocations to avoid timeouts. |
Expand Down
51 changes: 51 additions & 0 deletions src/server/api/lib/twilio.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Twilio from "twilio";
import { twiml } from "twilio";
import { getFormattedPhoneNumber } from "../../../lib/phone-format";
import {
Log,
Expand All @@ -9,6 +10,7 @@ import {
Campaign
} from "../../models";
import { log } from "../../../lib";
import wrap from "../../wrap";
import { saveNewIncomingMessage } from "./message-sending";
import { getConfig } from "./config";
import urlJoin from "url-join";
Expand Down Expand Up @@ -94,6 +96,54 @@ export const errorDescriptions = {
"-167": "Internal: Initial message altered (initialtext-guard)"
};

function addServerEndpoints(expressApp) {
expressApp.post(
"/twilio/:orgId?",
headerValidator(
process.env.TWILIO_MESSAGE_CALLBACK_URL ||
global.TWILIO_MESSAGE_CALLBACK_URL
),
wrap(async (req, res) => {
try {
await handleIncomingMessage(req.body);
} catch (ex) {
log.error(ex);
}
const resp = new twiml.MessagingResponse();
res.writeHead(200, { "Content-Type": "text/xml" });
res.end(resp.toString());
})
);

const messageReportHooks = [];
if (
getConfig("TWILIO_STATUS_CALLBACK_URL") ||
getConfig("TWILIO_VALIDATION")
) {
messageReportHooks.push(
headerValidator(
process.env.TWILIO_STATUS_CALLBACK_URL ||
global.TWILIO_STATUS_CALLBACK_URL
)
);
}
messageReportHooks.push(
wrap(async (req, res) => {
try {
const body = req.body;
await handleDeliveryReport(body);
} catch (ex) {
log.error(ex);
}
const resp = new twiml.MessagingResponse();
res.writeHead(200, { "Content-Type": "text/xml" });
res.end(resp.toString());
})
);

expressApp.post("/twilio-message-report/:orgId?", ...messageReportHooks);
}

async function convertMessagePartsToMessage(messageParts) {
const firstPart = messageParts[0];
const userNumber = firstPart.user_number;
Expand Down Expand Up @@ -656,6 +706,7 @@ async function deleteMessagingService(organization, messagingServiceSid) {

export default {
syncMessagePartProcessing: !!process.env.JOBS_SAME_PROCESS,
addServerEndpoints,
headerValidator,
convertMessagePartsToMessage,
sendMessage,
Expand Down
38 changes: 1 addition & 37 deletions src/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import nexmo from "./api/lib/nexmo";
import twilio from "./api/lib/twilio";
import { seedZipCodes } from "./seeds/seed-zip-codes";
import { setupUserNotificationObservers } from "./notifications";
import { twiml } from "twilio";
import { existsSync } from "fs";
import { rawAllMethods } from "../extensions/contact-loaders";

Expand Down Expand Up @@ -106,24 +105,7 @@ Object.keys(configuredIngestMethods).forEach(ingestMethodName => {
}
});

app.post(
"/twilio/:orgId?",
twilio.headerValidator(
process.env.TWILIO_MESSAGE_CALLBACK_URL ||
global.TWILIO_MESSAGE_CALLBACK_URL
),
wrap(async (req, res) => {
try {
await twilio.handleIncomingMessage(req.body);
} catch (ex) {
log.error(ex);
}

const resp = new twiml.MessagingResponse();
res.writeHead(200, { "Content-Type": "text/xml" });
res.end(resp.toString());
})
);
twilio.addServerEndpoints(app);

if (process.env.NEXMO_API_KEY) {
app.post(
Expand Down Expand Up @@ -153,24 +135,6 @@ if (process.env.NEXMO_API_KEY) {
);
}

app.post(
"/twilio-message-report",
twilio.headerValidator(
process.env.TWILIO_STATUS_CALLBACK_URL || global.TWILIO_STATUS_CALLBACK_URL
),
wrap(async (req, res) => {
try {
const body = req.body;
await twilio.handleDeliveryReport(body);
} catch (ex) {
log.error(ex);
}
const resp = new twiml.MessagingResponse();
res.writeHead(200, { "Content-Type": "text/xml" });
res.end(resp.toString());
})
);

app.get("/logout-callback", (req, res) => {
req.logOut();
res.redirect("/");
Expand Down

0 comments on commit 7c0f868

Please sign in to comment.