|
|
|
|
@ -1042,10 +1042,15 @@ export class CommandService {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 1) Menciones aportadas por el backend (JIDs crudos)
|
|
|
|
|
const unresolvedAssigneeDisplays: string[] = [];
|
|
|
|
|
const mentionsNormalizedFromContext = Array.from(new Set(
|
|
|
|
|
(context.mentions || []).map((j) => {
|
|
|
|
|
const norm = normalizeWhatsAppId(j);
|
|
|
|
|
if (!norm) {
|
|
|
|
|
// agregar a no resolubles para JIT (mostrar sin @ ni dominio)
|
|
|
|
|
const raw = String(j || '');
|
|
|
|
|
const disp = raw.split('@')[0].split(':')[0].replace(/^@+/, '').replace(/^\+/, '');
|
|
|
|
|
if (disp) unresolvedAssigneeDisplays.push(disp);
|
|
|
|
|
incOnboardingFailure('mentions', 'invalid');
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@ -1053,6 +1058,8 @@ export class CommandService {
|
|
|
|
|
if (resolved) return resolved;
|
|
|
|
|
const p = plausibility(norm);
|
|
|
|
|
if (p.ok) return norm;
|
|
|
|
|
// conservar para copy JIT
|
|
|
|
|
unresolvedAssigneeDisplays.push(norm);
|
|
|
|
|
incOnboardingFailure('mentions', p.reason!);
|
|
|
|
|
return null;
|
|
|
|
|
}).filter((id): id is string => !!id)
|
|
|
|
|
@ -1066,6 +1073,8 @@ export class CommandService {
|
|
|
|
|
atTokenCandidates.map((v) => {
|
|
|
|
|
const norm = normalizeWhatsAppId(v);
|
|
|
|
|
if (!norm) {
|
|
|
|
|
// agregar a no resolubles para JIT (texto ya viene sin @/+)
|
|
|
|
|
if (v) unresolvedAssigneeDisplays.push(v);
|
|
|
|
|
incOnboardingFailure('tokens', 'invalid');
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@ -1073,6 +1082,8 @@ export class CommandService {
|
|
|
|
|
if (resolved) return resolved;
|
|
|
|
|
const p = plausibility(norm);
|
|
|
|
|
if (p.ok) return norm;
|
|
|
|
|
// conservar para copy JIT (preferimos el token limpio v)
|
|
|
|
|
unresolvedAssigneeDisplays.push(v);
|
|
|
|
|
incOnboardingFailure('tokens', p.reason!);
|
|
|
|
|
return null;
|
|
|
|
|
}).filter((id): id is string => !!id)
|
|
|
|
|
@ -1195,7 +1206,38 @@ export class CommandService {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// A4: DM JIT al asignador si quedaron menciones/tokens irrecuperables
|
|
|
|
|
{
|
|
|
|
|
const unresolvedList = Array.from(new Set(unresolvedAssigneeDisplays.filter(Boolean)));
|
|
|
|
|
if (unresolvedList.length > 0) {
|
|
|
|
|
const isTest = String(process.env.NODE_ENV || '').toLowerCase() === 'test';
|
|
|
|
|
const enabled = isTest
|
|
|
|
|
? String(process.env.ONBOARDING_ENABLE_IN_TEST || '').toLowerCase() === 'true'
|
|
|
|
|
: (() => {
|
|
|
|
|
const v = process.env.ONBOARDING_PROMPTS_ENABLED;
|
|
|
|
|
return v == null ? true : ['true','1','yes'].includes(String(v).toLowerCase());
|
|
|
|
|
})();
|
|
|
|
|
const groupLabel = isGroupId(context.groupId) ? String(context.groupId) : 'dm';
|
|
|
|
|
if (!enabled) {
|
|
|
|
|
try { Metrics.inc('onboarding_prompts_skipped_total', 1, { group_id: groupLabel, source: 'jit_assignee_failure', reason: 'disabled' }); } catch {}
|
|
|
|
|
} else {
|
|
|
|
|
const bot = String(process.env.CHATBOT_PHONE_NUMBER || '').trim();
|
|
|
|
|
if (!bot || !/^\d+$/.test(bot)) {
|
|
|
|
|
try { Metrics.inc('onboarding_prompts_skipped_total', 1, { group_id: groupLabel, source: 'jit_assignee_failure', reason: 'missing_bot_number' }); } catch {}
|
|
|
|
|
} else {
|
|
|
|
|
const list = unresolvedList.join(', ');
|
|
|
|
|
let groupCtx = '';
|
|
|
|
|
if (isGroupId(context.groupId)) {
|
|
|
|
|
const name = GroupSyncService.activeGroupsCache.get(context.groupId) || context.groupId;
|
|
|
|
|
groupCtx = ` (en el grupo ${name})`;
|
|
|
|
|
}
|
|
|
|
|
const msg = `No puedo asignar a ${list} aún${groupCtx}. Pídele que toque este enlace y diga 'activar': https://wa.me/${bot}`;
|
|
|
|
|
responses.push({ recipient: createdBy, message: msg });
|
|
|
|
|
try { Metrics.inc('onboarding_prompts_sent_total', 1, { group_id: groupLabel, source: 'jit_assignee_failure' }); } catch {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return responses;
|
|
|
|
|
}
|
|
|
|
|
|