const path = require("path"); require("dotenv").config({ path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) }); const client = require("../graphql-client/graphql-client").client; const queries = require("../graphql-client/queries"); const { phone } = require("phone"); const { admin } = require("../firebase/firebase-handler"); const logger = require("../utils/logger"); const InstanceManager = require("../utils/instanceMgr").default; exports.receive = async (req, res) => { const { ioRedis, ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom } } = req; const loggerData = { msid: req.body.SmsMessageSid, text: req.body.Body, image: !!req.body.MediaUrl0, image_path: generateMediaArray(req.body) }; logger.log("sms-inbound", "DEBUG", "api", null, loggerData); if (!req.body || !req.body.MessagingServiceSid || !req.body.SmsMessageSid) { logger.log("sms-inbound-error", "ERROR", "api", null, { ...loggerData, type: "malformed-request" }); return res.status(400).json({ success: false, error: "Malformed Request" }); } try { const response = await client.request(queries.FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID, { mssid: req.body.MessagingServiceSid, phone: phone(req.body.From).phoneNumber }); if (!response.bodyshops[0]) { return res.status(400).json({ success: false, error: "No matching bodyshop" }); } const bodyshop = response.bodyshops[0]; const isNewConversation = bodyshop.conversations.length === 0; const isDuplicate = bodyshop.conversations.length > 1; let newMessage = { msid: req.body.SmsMessageSid, text: req.body.Body, image: !!req.body.MediaUrl0, image_path: generateMediaArray(req.body) }; if (isDuplicate) { logger.log("sms-inbound-error", "ERROR", "api", null, { ...loggerData, messagingServiceSid: req.body.MessagingServiceSid, type: "duplicate-phone" }); return res.status(400).json({ success: false, error: "Duplicate phone number" }); } if (isNewConversation) { newMessage.conversation = { data: { bodyshopid: bodyshop.id, phone_num: phone(req.body.From).phoneNumber, archived: false } }; } else { const existingConversation = bodyshop.conversations[0]; // Update the conversation to unarchive it if (existingConversation.archived) { await client.request(queries.UNARCHIVE_CONVERSATION, { id: existingConversation.id, archived: false }); } newMessage.conversationid = existingConversation.id; } const query = isNewConversation ? queries.RECEIVE_MESSAGE : queries.INSERT_MESSAGE; const variables = isNewConversation ? { msg: newMessage } : { msg: newMessage, conversationid: newMessage.conversationid }; const insertresp = await client.request(query, variables); const message = insertresp?.insert_messages?.returning?.[0]; const conversation = message?.conversation || null; if (!conversation) { throw new Error("Conversation data is missing from the response."); } const broadcastRoom = getBodyshopRoom(conversation.bodyshop.id); const conversationRoom = getBodyshopConversationRoom({ bodyshopId: conversation.bodyshop.id, conversationId: conversation.id }); const commonPayload = { isoutbound: false, conversationId: conversation.id, updated_at: message.updated_at, msid: message.sid }; ioRedis.to(broadcastRoom).emit("new-message-summary", { ...commonPayload, existingConversation: !isNewConversation, newConversation: isNewConversation ? conversation : null, summary: true }); ioRedis.to(conversationRoom).emit("new-message-detailed", { newMessage: message, ...commonPayload, newConversation: isNewConversation ? conversation : null, existingConversation: !isNewConversation, summary: false }); const fcmresp = await admin.messaging().send({ topic: `${message.conversation.bodyshop.imexshopid}-messaging`, notification: { title: InstanceManager({ imex: `ImEX Online Message - ${message.conversation.phone_num}`, rome: `Rome Online Message - ${message.conversation.phone_num}`, promanager: `ProManager Message - ${message.conversation.phone_num}` }), body: message.image_path ? `Image ${message.text}` : message.text }, data: { type: "messaging-inbound", conversationid: message.conversationid || "", text: message.text || "", messageid: message.id || "", phone_num: message.conversation.phone_num || "" } }); logger.log("sms-inbound-success", "DEBUG", "api", null, { newMessage, fcmresp }); res.status(200).send(""); } catch (e) { handleError(req, e, res, "RECEIVE_MESSAGE"); } }; const generateMediaArray = (body) => { const { NumMedia } = body; if (parseInt(NumMedia) > 0) { const ret = []; for (let i = 0; i < parseInt(NumMedia); i++) { ret.push(body[`MediaUrl${i}`]); } return ret; } else { return null; } }; const handleError = (req, error, res, context) => { logger.log("sms-inbound-error", "ERROR", "api", null, { msid: req.body.SmsMessageSid, text: req.body.Body, image: !!req.body.MediaUrl0, image_path: generateMediaArray(req.body), messagingServiceSid: req.body.MessagingServiceSid, context, error }); res.status(500).json({ error: error.message || "Internal Server Error" }); };