feat: agregar soporte para el envío de plantillas de WhatsApp a través de Meta, incluyendo nueva funcionalidad y adaptadores
This commit is contained in:
@@ -6,7 +6,7 @@ export enum NotificationJobStatus {
|
||||
CANCELLED = "cancelled",
|
||||
}
|
||||
|
||||
export type NotificationChannel = "whatsapp" | "email" | "system";
|
||||
export type NotificationChannel = "whatsapp" | "meta_wap" | "email" | "system";
|
||||
|
||||
export type NotificationJobType =
|
||||
| "creation"
|
||||
|
||||
+2
-2
@@ -32,13 +32,13 @@ export class ClientCompanyNotificationOverrideAdapterMongoose
|
||||
type: [String],
|
||||
required: true,
|
||||
default: ["whatsapp", "email", "system"],
|
||||
enum: ["whatsapp", "email", "system"],
|
||||
enum: ["whatsapp", "meta_wap", "email", "system"],
|
||||
},
|
||||
mutedChannels: {
|
||||
type: [String],
|
||||
required: true,
|
||||
default: [],
|
||||
enum: ["whatsapp", "email", "system"],
|
||||
enum: ["whatsapp", "meta_wap", "email", "system"],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+2
-2
@@ -28,13 +28,13 @@ export class ClientNotificationPreferencesAdapterMongoose
|
||||
type: [String],
|
||||
required: true,
|
||||
default: ["whatsapp", "email", "system"],
|
||||
enum: ["whatsapp", "email", "system"],
|
||||
enum: ["whatsapp", "meta_wap", "email", "system"],
|
||||
},
|
||||
mutedChannels: {
|
||||
type: [String],
|
||||
required: true,
|
||||
default: [],
|
||||
enum: ["whatsapp", "email", "system"],
|
||||
enum: ["whatsapp", "meta_wap", "email", "system"],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+2
-2
@@ -28,13 +28,13 @@ export class CompanyNotificationPolicyAdapterMongoose
|
||||
type: [String],
|
||||
required: true,
|
||||
default: ["whatsapp", "email", "system"],
|
||||
enum: ["whatsapp", "email", "system"],
|
||||
enum: ["whatsapp", "meta_wap", "email", "system"],
|
||||
},
|
||||
mutedChannels: {
|
||||
type: [String],
|
||||
required: false,
|
||||
default: [],
|
||||
enum: ["whatsapp", "email", "system"],
|
||||
enum: ["whatsapp", "meta_wap", "email", "system"],
|
||||
},
|
||||
timezone: {
|
||||
type: String,
|
||||
|
||||
@@ -248,6 +248,10 @@ export class NotificationJobService {
|
||||
|
||||
if (payload?.phoneNumber) {
|
||||
channels.push("whatsapp");
|
||||
// meta_wap requires templateName in payload to be useful
|
||||
if (payload?.templateName) {
|
||||
channels.push("meta_wap");
|
||||
}
|
||||
}
|
||||
|
||||
if (payload?.userId) {
|
||||
|
||||
@@ -14,6 +14,7 @@ const SYSTEM_DEFAULT_CHANNELS: NotificationChannel[] = ["whatsapp", "email", "sy
|
||||
|
||||
const PLAN_CHANNEL_MAP: Record<string, PlanFeatures> = {
|
||||
whatsapp: PlanFeatures.WAP_NOTIFICATIONS,
|
||||
meta_wap: PlanFeatures.WAP_NOTIFICATIONS,
|
||||
email: PlanFeatures.MAIL_NOTIFICATIONS,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import axios from "axios";
|
||||
import { INotificationsAdapter, NotificationDataMeta } from "./Notifications.Interface";
|
||||
|
||||
export class NotificationsMetaAdapter implements INotificationsAdapter<NotificationDataMeta> {
|
||||
// ── Datos hardcodeados para pruebas (etapa 1) ──
|
||||
private readonly phoneNumberId = "1379557278565544"; // ej: "1379557278565544"
|
||||
private readonly authToken =
|
||||
"EAAeZCf8kXotYBSlhC68tvuv6fPhFgfbqwdoF8Ab6r50cAQXJLOPl9qRCYtKYgz4r8ZBYzPIOVg3zZBU8JRcTmgoWU9DAphsWB3xQHQuZCMYZAMs3YHukOK567GUQeZCxOw8oZBnavnaqc0KukT0GzweRB2VC4CD5a6Vj5EsiJnZCjYuZCrffoha5WCkRRyUryook9syNTQ2cexsObVyhMVpRRc7X74BxbCWuzLZAvbZA6ZAb6zIZCHadhUWKX30dpYd1elqZAWuygnVC8pZBQyFG0YET498DAfs"; // tu token de Meta
|
||||
private readonly apiVersion = "v25.0";
|
||||
|
||||
get baseUrl(): string {
|
||||
return `https://graph.facebook.com/${this.apiVersion}/${this.phoneNumberId}/messages`;
|
||||
}
|
||||
|
||||
async send(data: NotificationDataMeta): Promise<void> {
|
||||
const templateBody: Record<string, unknown> = {
|
||||
messaging_product: "whatsapp",
|
||||
to: data.phoneNumber,
|
||||
type: "template",
|
||||
template: {
|
||||
name: data.templateName,
|
||||
language: { code: data.languageCode },
|
||||
},
|
||||
};
|
||||
|
||||
// Si hay variables, convertirlas al formato components que exige Meta
|
||||
if (data.templateVariables && data.templateVariables.length > 0) {
|
||||
(templateBody.template as Record<string, unknown>).components = [
|
||||
{
|
||||
type: "body",
|
||||
parameters: data.templateVariables.map((value) => ({
|
||||
type: "text",
|
||||
text: value,
|
||||
})),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const response = await axios.post(this.baseUrl, templateBody, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,19 @@
|
||||
export enum NOTIFICATION_TYPES {
|
||||
EMAIL = "EMAIL",
|
||||
WAP = "WAP",
|
||||
META_WAP = "META_WAP",
|
||||
SYSTEM = "SYSTEM",
|
||||
}
|
||||
|
||||
export type NotificationDataMeta = {
|
||||
phoneNumber: string;
|
||||
templateName: string;
|
||||
languageCode: string;
|
||||
// Si el template tiene variables, este array es obligatorio.
|
||||
// Cada elemento mapea a {{1}}, {{2}}, {{3}}, etc.
|
||||
templateVariables?: string[];
|
||||
};
|
||||
|
||||
export type NotificationDataEmail = {
|
||||
email: string;
|
||||
subject?: string;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import {
|
||||
NotificationDataEmail,
|
||||
NotificationDataMeta,
|
||||
NotificationDataSystem,
|
||||
NotificationDataWap,
|
||||
} from "./Notifications.Interface";
|
||||
|
||||
import { NotificationsWhatsappBotAdapter } from "./Notifications.Adapter.Bot";
|
||||
import { NotificationsMetaAdapter } from "./Notifications.Adapter.Meta";
|
||||
import { NotificationsSystemAdapter } from "./Notifications.Adapter.System";
|
||||
import { NotificationsDonWebAdapter } from "./Notifications.Adapter.donweb";
|
||||
|
||||
@@ -12,11 +14,13 @@ class NotificationsMngr {
|
||||
//notificationsNodeMailer: NotificationsNodemailerAdapter;
|
||||
notificationsDonWeb: NotificationsDonWebAdapter;
|
||||
notificationsBot: NotificationsWhatsappBotAdapter;
|
||||
notificationsMeta: NotificationsMetaAdapter;
|
||||
notificationsSystem: NotificationsSystemAdapter;
|
||||
constructor() {
|
||||
//this.notificationsNodeMailer = new NotificationsNodemailerAdapter();
|
||||
this.notificationsDonWeb = new NotificationsDonWebAdapter();
|
||||
this.notificationsBot = new NotificationsWhatsappBotAdapter();
|
||||
this.notificationsMeta = new NotificationsMetaAdapter();
|
||||
this.notificationsSystem = new NotificationsSystemAdapter();
|
||||
}
|
||||
|
||||
@@ -29,6 +33,10 @@ class NotificationsMngr {
|
||||
await this.notificationsBot.send(data);
|
||||
}
|
||||
|
||||
async sendWhatsAppTemplate(data: NotificationDataMeta): Promise<void> {
|
||||
await this.notificationsMeta.send(data);
|
||||
}
|
||||
|
||||
async sendSystemNotification(data: NotificationDataSystem): Promise<void> {
|
||||
await this.notificationsSystem.send(data);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,14 @@ interface SendWapByContentParams {
|
||||
sessionUser: string;
|
||||
}
|
||||
|
||||
interface SendWhatsAppTemplateParams {
|
||||
systemToken: string;
|
||||
phoneNumber: string;
|
||||
templateName: string;
|
||||
languageCode: string;
|
||||
templateVariables?: string[];
|
||||
}
|
||||
|
||||
@Route("notifications/send-email")
|
||||
@Middlewares([rateLimiter, systemTokenMiddleware])
|
||||
export class SendEmailByContentController extends Controller {
|
||||
@@ -43,23 +51,44 @@ export class SendEmailByContentController extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
@Route("notifications/send-wap")
|
||||
@Middlewares([rateLimiter, systemTokenMiddleware])
|
||||
export class SendWapByContentController extends Controller {
|
||||
@SuccessResponse(200, "Done")
|
||||
@Post()
|
||||
public async sendWapByContent(@Body() requestBody: SendWapByContentParams): Promise<ApiVoidResult> {
|
||||
await NotificationsManager.sendWap({
|
||||
phoneNumber: requestBody.phoneNumber,
|
||||
message: requestBody.message,
|
||||
companyId: requestBody.companyId,
|
||||
sessionUser: requestBody.sessionUser,
|
||||
});
|
||||
@Route("notifications/send-wap")
|
||||
@Middlewares([rateLimiter, systemTokenMiddleware])
|
||||
export class SendWapByContentController extends Controller {
|
||||
@SuccessResponse(200, "Done")
|
||||
@Post()
|
||||
public async sendWapByContent(@Body() requestBody: SendWapByContentParams): Promise<ApiVoidResult> {
|
||||
await NotificationsManager.sendWap({
|
||||
phoneNumber: requestBody.phoneNumber,
|
||||
message: requestBody.message,
|
||||
companyId: requestBody.companyId,
|
||||
sessionUser: requestBody.sessionUser,
|
||||
});
|
||||
|
||||
this.setStatus(200);
|
||||
return {
|
||||
message: "Done",
|
||||
success: true,
|
||||
};
|
||||
this.setStatus(200);
|
||||
return {
|
||||
message: "Done",
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Route("notifications/send-whatsapp-template")
|
||||
@Middlewares([rateLimiter, systemTokenMiddleware])
|
||||
export class SendWhatsAppTemplateController extends Controller {
|
||||
@SuccessResponse(200, "Done")
|
||||
@Post()
|
||||
public async sendWhatsAppTemplate(@Body() requestBody: SendWhatsAppTemplateParams): Promise<ApiVoidResult> {
|
||||
await NotificationsManager.sendWhatsAppTemplate({
|
||||
phoneNumber: requestBody.phoneNumber,
|
||||
templateName: requestBody.templateName,
|
||||
languageCode: requestBody.languageCode,
|
||||
templateVariables: requestBody.templateVariables,
|
||||
});
|
||||
|
||||
this.setStatus(200);
|
||||
return {
|
||||
message: "Done",
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user