-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #140 from Plant-for-the-Planet-org/feature/whatsap…
…p-notifier Implement WhatsApp Notification Service and Callback URL Feature
- Loading branch information
Showing
11 changed files
with
321 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 69 additions & 26 deletions
95
apps/server/src/Services/Notifier/Notifier/WhatsAppNotifier.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
apps/server/src/pages/api/cron/text-message-callback-handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
//To reach this endpoint call this URL (POST): http://localhost:3000/api/cron/text-message-callback-handler | ||
import type { NextApiRequest, NextApiResponse } from 'next'; | ||
import { prisma } from '../../../server/db'; | ||
// import NotifierRegistry from '../../../Services/Notifier/NotifierRegistry'; | ||
import { logger } from '../../../server/logger'; | ||
// import { NotificationParameters } from '../../../Interfaces/NotificationParameters'; | ||
import { parsePhoneNumberFromString } from 'libphonenumber-js'; | ||
import {env} from "../../../env.mjs"; | ||
|
||
export default async function handler(req: NextApiRequest, res: NextApiResponse) { | ||
if (req.method !== 'POST') { | ||
res.status(405).end(`Method ${req.method} Not Allowed`); | ||
return; | ||
} | ||
// Verify the 'cron_key' in the request headers before proceeding | ||
if (env.CRON_KEY) { | ||
// Verify the 'cron_key' in the request headers | ||
const cronKey = req.query['cron_key']; | ||
if (!cronKey || cronKey !== env.CRON_KEY) { | ||
res.status(403).json({message: "Unauthorized: Invalid Cron Key"}); | ||
return; | ||
} | ||
} | ||
const { alertMethodMethod, action, destination } = req.body; | ||
|
||
if (!alertMethodMethod || typeof alertMethodMethod !== 'string') { | ||
res.status(400).json({ message: 'alertMethodMethod must be a string.', status: '400' }); | ||
return; | ||
} | ||
|
||
if (!['sms', 'whatsapp'].includes(alertMethodMethod)) { | ||
res.status(400).json({ message: 'Invalid alertMethodMethod provided.', status: '400' }); | ||
return; | ||
} | ||
|
||
if (typeof destination !== 'string') { | ||
res.status(400).json({ message: 'Destination must be a string.', status: '400' }); | ||
return; | ||
} | ||
|
||
const formattedPhoneNumber = destination.startsWith('+') ? destination : '+' + destination; | ||
const parsedPhoneNumber = parsePhoneNumberFromString(formattedPhoneNumber); | ||
let phoneNumberE164 = ''; | ||
|
||
if (parsedPhoneNumber && parsedPhoneNumber.isValid()) { | ||
phoneNumberE164 = parsedPhoneNumber.format('E.164'); | ||
} else { | ||
res.status(400).json({ message: 'Invalid destination phone number.', status: '400' }); | ||
return; | ||
} | ||
|
||
if (action === 'STOP') { | ||
try { | ||
const unverifyAlertMethod = await prisma.alertMethod.updateMany({ | ||
where: { | ||
destination: phoneNumberE164, | ||
method: alertMethodMethod, | ||
}, | ||
data: { | ||
isVerified: false, | ||
}, | ||
}); | ||
|
||
if (unverifyAlertMethod.count > 0) { | ||
// const notificationParameters: NotificationParameters = { | ||
// message: `Your FireAlert notifications for ${alertMethodMethod} have been stopped, and your number has been unverified. If this is an error, please verify your number again from our app.`, | ||
// subject: 'FireAlert Notification STOP', | ||
// }; | ||
|
||
// const notifier = NotifierRegistry.get(alertMethodMethod); | ||
// const isDelivered = await notifier.notify(phoneNumberE164, notificationParameters); | ||
|
||
// if (isDelivered) { | ||
// res.status(200).json({ message: `Notification sent successfully via ${alertMethodMethod}.`, status: '200' }); | ||
// } else { | ||
// res.status(500).json({ message: `Failed to send notification via ${alertMethodMethod}.`, status: '500' }); | ||
// } | ||
res.status(200).json({ message: `Successfully handled the WhatsApp callback action.`, status: '200' }); | ||
} else { | ||
res.status(404).json({ message: `No ${alertMethodMethod} alertMethods associated with that phonenumber`, status: '404' }); | ||
} | ||
} catch (error) { | ||
logger(`Error in ${alertMethodMethod} service handler: ${error}`, 'error'); | ||
res.status(500).json({ message: `Internal Server Error`, status: '500' }); | ||
} | ||
} else { | ||
res.status(400).json({ message: 'Invalid action provided.', status: '400' }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// Call this api to run this page: http://localhost:3000/api/tests/whatsapp?phoneNumber="E.164-phone-number" | ||
import { type NextApiRequest, type NextApiResponse } from "next"; | ||
import { logger } from "../../../../src/server/logger"; | ||
import NotifierRegistry from "../../../Services/Notifier/NotifierRegistry"; | ||
import { NotificationParameters } from "../../../Interfaces/NotificationParameters"; // Adjust this import path if necessary | ||
import {env} from "../../../../src/env.mjs"; | ||
|
||
export default async function testWhatsApp(req: NextApiRequest, res: NextApiResponse) { | ||
logger(`Running Test WhatsApp Sender.`, "info"); | ||
|
||
if(env.NODE_ENV !== 'development'){ | ||
return res.status(401).json({ | ||
message: "Unauthorized for production. Only use this endpoint for development.", | ||
status: 401, | ||
}); | ||
} | ||
if (env.CRON_KEY) { | ||
// Verify the 'cron_key' in the request headers | ||
const cronKey = req.query['cron_key']; | ||
if (!cronKey || cronKey !== env.CRON_KEY) { | ||
res.status(403).json({message: "Unauthorized: Invalid Cron Key"}); | ||
return; | ||
} | ||
} | ||
|
||
// Extract the phone number from the query parameters | ||
const destination = req.query['phoneNumber'] as string; | ||
|
||
if (!destination) { | ||
return res.status(400).json({ | ||
message: "Error: Phone number is required.", | ||
status: 400, | ||
}); | ||
} | ||
|
||
// URL encode the phone number | ||
const encodedPhoneNumber: string = encodeURIComponent(destination); | ||
|
||
// Create the notification parameters for an alert | ||
const notificationParameters_alert: NotificationParameters = { | ||
message: "Fire detected inside Las Americas 7A", | ||
subject: "FireAlert", | ||
url: "https://firealert.plant-for-the-planet.org/alert/ed1cf199-6c3a-4406-bac0-eb5519391e2e", | ||
id: "notificationId", | ||
authenticationMessage: true, | ||
otp: "12345", | ||
siteName: 'Las Americas', | ||
alert:{ | ||
id: "ed1cf199-6c3a-4406-bac0-eb5519391e2e", | ||
type: 'fire', | ||
confidence: 'high', | ||
source: "TEST", | ||
date: new Date(), | ||
longitude: 80.45728, | ||
latitude: 66.66537, | ||
distance: 0, | ||
siteId: "siteId1", | ||
siteName: "SiteName", | ||
data: {}, | ||
} | ||
}; | ||
|
||
try { | ||
// Use the NotifierRegistry to get the WhatsApp notifier | ||
const notifier = NotifierRegistry.get('whatsapp'); | ||
const isDelivered = await notifier.notify(encodedPhoneNumber, notificationParameters_alert); | ||
|
||
if (isDelivered) { | ||
res.status(200).json({ | ||
message: "WhatsApp Message Sent Successfully!", | ||
status: 200, | ||
}); | ||
} else { | ||
res.status(500).json({ | ||
message: "Failed to send WhatsApp Message.", | ||
status: 500, | ||
}); | ||
} | ||
} catch (error) { | ||
logger(`Error sending test WhatsApp: ${error}`, "error"); | ||
res.status(500).json({ | ||
message: `Error sending WhatsApp Message`, | ||
status: 500, | ||
}); | ||
} | ||
} |
Oops, something went wrong.