From 6cbc7468fbc64ecec35da0fec3c0c8da9c24c9fd Mon Sep 17 00:00:00 2001 From: m5r Date: Sun, 1 Aug 2021 18:36:32 +0800 Subject: [PATCH] handle incoming sms --- app/api/ddd.ts | 9 ++- app/api/queue/fetch-messages.ts | 2 +- app/api/queue/insert-messages.ts | 2 +- app/core/layouts/layout/index.tsx | 22 ++++++- app/messages/api/webhook/incoming-message.ts | 64 +++++++++++++++++--- app/messages/components/conversation.tsx | 1 + app/messages/hooks/use-conversation.ts | 2 +- app/messages/pages/messages.tsx | 2 +- 8 files changed, 87 insertions(+), 17 deletions(-) diff --git a/app/api/ddd.ts b/app/api/ddd.ts index d99f2ae..2806ae4 100644 --- a/app/api/ddd.ts +++ b/app/api/ddd.ts @@ -20,7 +20,7 @@ export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) { // .phoneNumbers("+33613370787") .phoneNumbers("+33476982071") .fetch();*/ - try { + /*try { await twilio(accountSid, authToken).messages.create({ body: "content", to: "+213744123789", @@ -31,7 +31,12 @@ export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) { console.log(error.moreInfo); console.log(error.details); // console.log(JSON.stringify(Object.keys(error))); - } + }*/ + const ddd = await twilio(accountSid, authToken).messages.create({ + body: "content", + to: "+33757592025", + from: "+33757592722", + }); res.status(200).send(ddd); } diff --git a/app/api/queue/fetch-messages.ts b/app/api/queue/fetch-messages.ts index 8068af9..f7c5c40 100644 --- a/app/api/queue/fetch-messages.ts +++ b/app/api/queue/fetch-messages.ts @@ -21,7 +21,7 @@ const fetchMessagesQueue = Queue("api/queue/fetch-messages", async ({ c }), ]); const messages = [...messagesSent, ...messagesReceived].sort( - (a, b) => a.dateSent.getTime() - b.dateSent.getTime() + (a, b) => a.dateCreated.getTime() - b.dateCreated.getTime() ); await insertMessagesQueue.enqueue( diff --git a/app/api/queue/insert-messages.ts b/app/api/queue/insert-messages.ts index b809ce1..babfe53 100644 --- a/app/api/queue/insert-messages.ts +++ b/app/api/queue/insert-messages.ts @@ -24,7 +24,7 @@ const insertMessagesQueue = Queue( status: translateStatus(message.status), direction: translateDirection(message.direction), twilioSid: message.sid, - sentAt: new Date(message.dateSent), + sentAt: new Date(message.dateCreated), })) .sort((a, b) => a.sentAt.getTime() - b.sentAt.getTime()); diff --git a/app/core/layouts/layout/index.tsx b/app/core/layouts/layout/index.tsx index af69c0d..aea1ba5 100644 --- a/app/core/layouts/layout/index.tsx +++ b/app/core/layouts/layout/index.tsx @@ -1,6 +1,14 @@ import type { ErrorInfo, FunctionComponent } from "react"; import { Component } from "react"; -import { Head, withRouter } from "blitz"; +import { + Head, + withRouter, + AuthenticationError, + AuthorizationError, + CSRFTokenMismatchError, + NotFoundError, + RedirectError, +} from "blitz"; import type { WithRouterProps } from "next/dist/client/with-router"; import appLogger from "../../../../integrations/logger"; @@ -52,6 +60,14 @@ type ErrorBoundaryState = errorMessage: string; }; +const blitzErrorNames = [ + RedirectError.name, + AuthenticationError.name, + AuthorizationError.name, + CSRFTokenMismatchError.name, + NotFoundError.name, +]; + const ErrorBoundary = withRouter( class ErrorBoundary extends Component { public readonly state = { @@ -67,6 +83,10 @@ const ErrorBoundary = withRouter( public componentDidCatch(error: Error, errorInfo: ErrorInfo) { logger.error(error, errorInfo.componentStack); + if (blitzErrorNames.includes(error.name)) { + // let Blitz ErrorBoundary handle this one + throw error; + } } public render() { diff --git a/app/messages/api/webhook/incoming-message.ts b/app/messages/api/webhook/incoming-message.ts index a168569..a30635b 100644 --- a/app/messages/api/webhook/incoming-message.ts +++ b/app/messages/api/webhook/incoming-message.ts @@ -37,21 +37,36 @@ export default async function incomingMessageHandler(req: BlitzApiRequest, res: } console.log("req.body", req.body); + // TODO: return 200 and process this in the background try { const phoneNumber = req.body.To; const customerPhoneNumber = await db.phoneNumber.findFirst({ where: { phoneNumber }, }); + console.log("customerPhoneNumber", customerPhoneNumber); + if (!customerPhoneNumber) { + // phone number is not registered by any customer + res.status(200).end(); + return; + } + const customer = await db.customer.findFirst({ - where: { id: customerPhoneNumber!.customerId }, + where: { id: customerPhoneNumber.customerId }, }); - const url = "https://phone.mokhtar.dev/api/webhook/incoming-message"; + console.log("customer", customer); + if (!customer || !customer.authToken) { + res.status(200).end(); + return; + } + + const url = "https://4cbc3f38c23a.ngrok.io/api/webhook/incoming-message"; const isRequestValid = twilio.validateRequest( - customer!.authToken!, + customer.authToken, twilioSignature, url, req.body ); + console.log("isRequestValid", isRequestValid); if (!isRequestValid) { const statusCode = 400; const apiError: ApiError = { @@ -64,15 +79,22 @@ export default async function incomingMessageHandler(req: BlitzApiRequest, res: return; } + // TODO: send notification + + const body: Body = req.body; + const messageSid = body.MessageSid; + const message = await twilio(customer.accountSid!, customer.authToken) + .messages.get(messageSid) + .fetch(); await db.message.create({ data: { - customerId: customer!.id, - to: req.body.To, - from: req.body.From, - status: MessageStatus.Received, - direction: Direction.Inbound, - sentAt: req.body.DateSent, - content: encrypt(req.body.Body, customer!.encryptionKey), + customerId: customer.id, + to: message.to, + from: message.from, + status: translateStatus(message.status), + direction: translateDirection(message.direction), + sentAt: message.dateCreated, + content: encrypt(message.body, customer.encryptionKey), }, }); } catch (error) { @@ -87,6 +109,28 @@ export default async function incomingMessageHandler(req: BlitzApiRequest, res: } } +type Body = { + ToCountry: string; + ToState: string; + SmsMessageSid: string; + NumMedia: string; + ToCity: string; + FromZip: string; + SmsSid: string; + FromState: string; + SmsStatus: string; + FromCity: string; + Body: string; + FromCountry: string; + To: string; + ToZip: string; + NumSegments: string; + MessageSid: string; + AccountSid: string; + From: string; + ApiVersion: string; +}; + function translateDirection(direction: MessageInstance["direction"]): Direction { switch (direction) { case "inbound": diff --git a/app/messages/components/conversation.tsx b/app/messages/components/conversation.tsx index 53fcd40..730350d 100644 --- a/app/messages/components/conversation.tsx +++ b/app/messages/components/conversation.tsx @@ -20,6 +20,7 @@ export default function Conversation() { <>
    + {conversation.length === 0 ? "empty state" : null} {conversation.map((message, index) => { const isOutbound = message.direction === Direction.Outbound; const nextMessage = conversation![index + 1]; diff --git a/app/messages/hooks/use-conversation.ts b/app/messages/hooks/use-conversation.ts index 03b0403..adde552 100644 --- a/app/messages/hooks/use-conversation.ts +++ b/app/messages/hooks/use-conversation.ts @@ -9,7 +9,7 @@ export default function useConversation(recipient: string) { { select(conversations) { if (!conversations[recipient]) { - throw new Error("Conversation not found"); + return []; } return conversations[recipient]!; diff --git a/app/messages/pages/messages.tsx b/app/messages/pages/messages.tsx index 1e1fbcc..56e2bc3 100644 --- a/app/messages/pages/messages.tsx +++ b/app/messages/pages/messages.tsx @@ -29,6 +29,6 @@ const Messages: BlitzPage = () => { Messages.getLayout = (page) => {page}; -Messages.authenticate = { redirectTo: Routes.SignIn() }; +Messages.authenticate = { redirectTo: Routes.SignIn().pathname }; export default Messages;