Skip to content

Commit 814da63

Browse files
committed
switched normalize phone to resolvereal phone
1 parent c411fbd commit 814da63

File tree

1 file changed

+168
-133
lines changed

1 file changed

+168
-133
lines changed

services/followUpService.js

Lines changed: 168 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const FollowUpJourney = require('../model/FollowUpJourney');
44
const WhatsappActivity = require('../model/WhatsappActivity');
55
const FollowUpFlowConfig = require('../model/FollowUpFlowConfig');
66
const MembersModel = require('../model/members');
7+
const { default: axios } = require('axios');
78

89
class FollowUpService {
910
_defaultFlowStages() {
@@ -319,18 +320,51 @@ class FollowUpService {
319320
}
320321

321322
_normalizePhone(phone) {
323+
return String(phone || '').replace(/\D/g, '');
324+
}
325+
326+
async _resolveRealPhoneFromJid(jid) {
322327
if (!jid) return null;
323328

324-
// Remove @c.us or @lid
325-
const cleaned = jid.split('@')[0];
329+
const id = jid.split('@')[0];
330+
331+
// If already normal phone
332+
if (id.startsWith('234') && id.length === 13) {
333+
return id.slice(3);
334+
}
335+
336+
if (id.startsWith('0') && id.length === 11) {
337+
return id.slice(1);
338+
}
339+
340+
// If LID → resolve via WAHA
341+
if (jid.endsWith('@lid')) {
342+
try {
343+
const response = await axios.get(
344+
`${process.env.WAHA_API_URL}/api/contacts`,
345+
{
346+
params: {
347+
contactId: jid,
348+
session: 'default'
349+
}
350+
}
351+
);
352+
353+
const contact = response.data;
354+
355+
if (contact?.number) {
356+
const full = contact.number;
357+
return full.startsWith('234') ? full.slice(3) : full;
358+
}
326359

327-
// If it starts with 0 (Nigeria local format)
328-
if (cleaned.startsWith('0')) {
329-
return '234' + cleaned.substring(1);
360+
return null;
361+
} catch (err) {
362+
console.error('Failed to resolve LID:', err.message);
363+
return null;
364+
}
330365
}
331366

332-
return cleaned;
333-
// return String(phone || '').replace(/\D/g, '');
367+
return null;
334368
}
335369

336370
_isOptOut(text) {
@@ -545,134 +579,135 @@ class FollowUpService {
545579
}
546580

547581
async handleReply(phone, messageBody) {
548-
const cleanedPhone = this._normalizePhone(phone);
582+
console.log(`Handling reply from ${phone}: ${messageBody}`);
583+
const cleanedPhone = await _resolveRealPhoneFromJid(phone)
584+
console.log(`Cleaned phone: ${cleanedPhone}`);
549585
const reply = this._normalizeInboundText(messageBody);
550586

551-
console.log(cleanedPhone,reply, 'normalize' )
552-
553-
const memberByPhone = await MembersModel.findOne({
554-
phone: { $regex: cleanedPhone },
555-
});
556-
557-
// Global commands should work even when there is no active journey.
558-
if (this._isOptOut(reply) && memberByPhone) {
559-
memberByPhone.whatsappOptIn = false;
560-
memberByPhone.whatsappOptOutDate = new Date();
561-
await memberByPhone.save();
562-
563-
await FollowUpJourney.updateMany(
564-
{ memberId: memberByPhone._id, status: { $in: ['active', 'escalated'] } },
565-
{ $set: { status: 'opted_out', nextMessageAt: null } }
566-
);
567-
568-
await wahaService.sendText(
569-
cleanedPhone,
570-
'You have been unsubscribed from WhatsApp follow-up messages. Reply START to opt in again.'
571-
);
572-
573-
return { action: 'opted_out', journey: null };
574-
}
575-
576-
if (this._isOptIn(reply) && memberByPhone) {
577-
memberByPhone.whatsappOptIn = true;
578-
memberByPhone.whatsappOptInDate = new Date();
579-
memberByPhone.whatsappOptOutDate = null;
580-
await memberByPhone.save();
581-
582-
await wahaService.sendText(
583-
cleanedPhone,
584-
'You are now subscribed again. Thank you.'
585-
);
586-
return { action: 'opted_in', journey: null };
587-
}
588-
589-
if (this._isHelp(reply)) {
590-
await wahaService.sendText(
591-
cleanedPhone,
592-
'Reply 1, 2, or 3 to choose an option. Reply STOP to opt out, START to opt in.'
593-
);
594-
return { action: 'help', journey: null };
595-
}
596-
597-
const journey = await FollowUpJourney.findOne({
598-
phone: { $regex: cleanedPhone },
599-
status: { $in: ['active', 'escalated'] },
600-
}).populate('memberId');
601-
602-
if (!journey) {
603-
if (memberByPhone) {
604-
const absentHandled = await this._handleAbsentReminderReply(
605-
memberByPhone,
606-
cleanedPhone,
607-
reply
608-
);
609-
if (absentHandled) return { action: absentHandled.action, journey: null };
610-
}
611-
return null;
612-
}
613-
614-
const member = journey.memberId;
615-
const firstName = member?.firstName || 'Friend';
616-
const flowStages = await this.getActiveFlowStages();
617-
const stageConfig = this._findStageConfig(flowStages, journey.currentStage);
618-
619-
await WhatsappActivity.create({
620-
memberId: member?._id,
621-
phone: cleanedPhone,
622-
direction: 'inbound',
623-
messageType: 'reply',
624-
content: reply,
625-
followUpStage: journey.currentStage,
626-
conversationStage: 'awaiting_reply',
627-
status: 'read',
628-
});
629-
630-
let action = 'unknown';
631-
const configuredOption = this._findConfiguredResponseOption(
632-
reply,
633-
stageConfig?.responseOptions || []
634-
);
635-
636-
if (configuredOption) {
637-
action = await this._applyConfiguredResponseOption({
638-
option: configuredOption,
639-
journey,
640-
member,
641-
phone: cleanedPhone,
642-
});
643-
} else {
644-
const option = this._detectOption(reply, journey.currentStage);
645-
if (option === 1) action = await this._handleOption1(journey, firstName, cleanedPhone);
646-
else if (option === 2) action = await this._handleOption2(journey, firstName, cleanedPhone);
647-
else if (option === 3) action = await this._handleOption3(journey, firstName, cleanedPhone);
648-
else if (
649-
journey.currentStage === 2 ||
650-
member?.whatsappConversationStage === 'prayer_requested'
651-
) {
652-
await this._handlePrayerRequest(member, cleanedPhone, reply);
653-
action = 'prayer_submitted';
654-
} else {
655-
action = 'free_text';
656-
}
657-
}
658-
659-
journey.replies.push({
660-
content: reply,
661-
receivedAt: new Date(),
662-
stage: journey.currentStage,
663-
action,
664-
});
665-
journey.engagementScore = this._calculateEngagement(journey);
666-
await journey.save();
667-
668-
if (member) {
669-
member.lastWhatsappReply = new Date();
670-
member.totalReplies = (member.totalReplies || 0) + 1;
671-
member.whatsappEngagementStatus = 'active';
672-
await member.save();
673-
}
674-
675-
return { action, journey };
587+
// const memberByPhone = await MembersModel.findOne({
588+
// phone: { $regex: cleanedPhone },
589+
// });
590+
return {}
591+
592+
// // Global commands should work even when there is no active journey.
593+
// if (this._isOptOut(reply) && memberByPhone) {
594+
// memberByPhone.whatsappOptIn = false;
595+
// memberByPhone.whatsappOptOutDate = new Date();
596+
// await memberByPhone.save();
597+
598+
// await FollowUpJourney.updateMany(
599+
// { memberId: memberByPhone._id, status: { $in: ['active', 'escalated'] } },
600+
// { $set: { status: 'opted_out', nextMessageAt: null } }
601+
// );
602+
603+
// await wahaService.sendText(
604+
// cleanedPhone,
605+
// 'You have been unsubscribed from WhatsApp follow-up messages. Reply START to opt in again.'
606+
// );
607+
608+
// return { action: 'opted_out', journey: null };
609+
// }
610+
611+
// if (this._isOptIn(reply) && memberByPhone) {
612+
// memberByPhone.whatsappOptIn = true;
613+
// memberByPhone.whatsappOptInDate = new Date();
614+
// memberByPhone.whatsappOptOutDate = null;
615+
// await memberByPhone.save();
616+
617+
// await wahaService.sendText(
618+
// cleanedPhone,
619+
// 'You are now subscribed again. Thank you.'
620+
// );
621+
// return { action: 'opted_in', journey: null };
622+
// }
623+
624+
// if (this._isHelp(reply)) {
625+
// await wahaService.sendText(
626+
// cleanedPhone,
627+
// 'Reply 1, 2, or 3 to choose an option. Reply STOP to opt out, START to opt in.'
628+
// );
629+
// return { action: 'help', journey: null };
630+
// }
631+
632+
// const journey = await FollowUpJourney.findOne({
633+
// phone: { $regex: cleanedPhone },
634+
// status: { $in: ['active', 'escalated'] },
635+
// }).populate('memberId');
636+
637+
// if (!journey) {
638+
// if (memberByPhone) {
639+
// const absentHandled = await this._handleAbsentReminderReply(
640+
// memberByPhone,
641+
// cleanedPhone,
642+
// reply
643+
// );
644+
// if (absentHandled) return { action: absentHandled.action, journey: null };
645+
// }
646+
// return null;
647+
// }
648+
649+
// const member = journey.memberId;
650+
// const firstName = member?.firstName || 'Friend';
651+
// const flowStages = await this.getActiveFlowStages();
652+
// const stageConfig = this._findStageConfig(flowStages, journey.currentStage);
653+
654+
// await WhatsappActivity.create({
655+
// memberId: member?._id,
656+
// phone: cleanedPhone,
657+
// direction: 'inbound',
658+
// messageType: 'reply',
659+
// content: reply,
660+
// followUpStage: journey.currentStage,
661+
// conversationStage: 'awaiting_reply',
662+
// status: 'read',
663+
// });
664+
665+
// let action = 'unknown';
666+
// const configuredOption = this._findConfiguredResponseOption(
667+
// reply,
668+
// stageConfig?.responseOptions || []
669+
// );
670+
671+
// if (configuredOption) {
672+
// action = await this._applyConfiguredResponseOption({
673+
// option: configuredOption,
674+
// journey,
675+
// member,
676+
// phone: cleanedPhone,
677+
// });
678+
// } else {
679+
// const option = this._detectOption(reply, journey.currentStage);
680+
// if (option === 1) action = await this._handleOption1(journey, firstName, cleanedPhone);
681+
// else if (option === 2) action = await this._handleOption2(journey, firstName, cleanedPhone);
682+
// else if (option === 3) action = await this._handleOption3(journey, firstName, cleanedPhone);
683+
// else if (
684+
// journey.currentStage === 2 ||
685+
// member?.whatsappConversationStage === 'prayer_requested'
686+
// ) {
687+
// await this._handlePrayerRequest(member, cleanedPhone, reply);
688+
// action = 'prayer_submitted';
689+
// } else {
690+
// action = 'free_text';
691+
// }
692+
// }
693+
694+
// journey.replies.push({
695+
// content: reply,
696+
// receivedAt: new Date(),
697+
// stage: journey.currentStage,
698+
// action,
699+
// });
700+
// journey.engagementScore = this._calculateEngagement(journey);
701+
// await journey.save();
702+
703+
// if (member) {
704+
// member.lastWhatsappReply = new Date();
705+
// member.totalReplies = (member.totalReplies || 0) + 1;
706+
// member.whatsappEngagementStatus = 'active';
707+
// await member.save();
708+
// }
709+
710+
// return { action, journey };
676711
}
677712

678713
async _handleOption1(journey, firstName, phone) {

0 commit comments

Comments
 (0)