feat: implementar flujo A4 de onboarding con DM JIT y activar

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
webui
brobert 2 weeks ago
parent c4568587a9
commit b9252f4c49

@ -335,6 +335,18 @@ export class WebhookServer {
const messageTextTrimmed = messageText.trim();
const isAdminCmd = messageTextTrimmed.startsWith('/admin');
// A4: Primer DM "activar" — alta/confirmación idempotente (solo en DM)
if (!isGroupId(remoteJid) && messageTextTrimmed === 'activar') {
const base = (process.env.WEB_BASE_URL || '').trim();
const msg = base
? "Listo, ya puedes reclamar/ser responsable y acceder a la web. Para acceder a la web, envía '/t web' y abre el enlace."
: "Listo, ya puedes reclamar/ser responsable.";
try {
await ResponseQueue.add([{ recipient: normalizedSenderId, message: msg }]);
} catch {}
return;
}
// Etapa 2: Descubrimiento seguro de grupos (modo 'discover')
if (isGroupId(remoteJid)) {
try { (AllowedGroups as any).dbInstance = WebhookServer.dbInstance; } catch {}

@ -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;
}

Loading…
Cancel
Save