feat(secretaria): assina as respostas da IA com "*Nome:*" em negrito
A Secretária passa a prefixar cada resposta enviada com o PRIMEIRO nome do agente em negrito (ex.: "Ana — Atendente Virtual" → "*Ana:*\n<resposta>"), igual à assinatura do operador humano — identifica a IA na caixa compartilhada e é por-sessão (número com agente "Ana Clínica A" assina "*Ana:*"). Aplicado no envio e no corpo persistido, nos dois caminhos (auto-reply e painel /ask). Testado em dev: a Ana respondeu no WhatsApp com "*Ana:*" em negrito. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -205,11 +205,17 @@ function buildExtRoutes(prisma, manager, db, config, hooks, io) {
|
|||||||
const router = (0, express_1.Router)();
|
const router = (0, express_1.Router)();
|
||||||
// ── Helper: envia reply da IA via WA e persiste no banco principal ─────────
|
// ── Helper: envia reply da IA via WA e persiste no banco principal ─────────
|
||||||
async function sendSecretariaReply(opts) {
|
async function sendSecretariaReply(opts) {
|
||||||
const { instanceId, tenantId, chatId, jid, reply } = opts;
|
const { instanceId, tenantId, chatId, jid, reply, agentName } = opts;
|
||||||
const sock = manager?.getSocket(instanceId);
|
const sock = manager?.getSocket(instanceId);
|
||||||
if (!sock)
|
if (!sock)
|
||||||
return;
|
return;
|
||||||
const sent = await sock.sendMessage(jid, { text: reply }).catch(() => null);
|
// Assinatura da Secretária: prefixa "*Nome:*\n" (negrito) com o PRIMEIRO nome
|
||||||
|
// do agente (ex.: "Ana — Atendente Virtual" → "Ana"). Identifica a IA na caixa
|
||||||
|
// compartilhada — como a assinatura do operador humano. O ":" fica DENTRO do
|
||||||
|
// negrito p/ todos os dispositivos verem "Ana:" em negrito consistente.
|
||||||
|
const first = (agentName ?? '').trim().split(/[\s—–-]+/).filter(Boolean)[0];
|
||||||
|
const text = first ? `*${first}:*\n${reply}` : reply;
|
||||||
|
const sent = await sock.sendMessage(jid, { text }).catch(() => null);
|
||||||
const persisted = await prisma.message.create({
|
const persisted = await prisma.message.create({
|
||||||
data: {
|
data: {
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -219,7 +225,7 @@ function buildExtRoutes(prisma, manager, db, config, hooks, io) {
|
|||||||
messageId: sent?.key.id ?? `sec-${Date.now()}`,
|
messageId: sent?.key.id ?? `sec-${Date.now()}`,
|
||||||
fromMe: true,
|
fromMe: true,
|
||||||
type: 'TEXT',
|
type: 'TEXT',
|
||||||
body: reply,
|
body: text,
|
||||||
status: 'SENT',
|
status: 'SENT',
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
},
|
},
|
||||||
@@ -345,7 +351,8 @@ function buildExtRoutes(prisma, manager, db, config, hooks, io) {
|
|||||||
const brain = new brain_1.ProtocolEngine(db, config);
|
const brain = new brain_1.ProtocolEngine(db, config);
|
||||||
const reply = await brain.chat(conv.id, text.trim(), { tenantId, hooks, clinicaId: channel?.clinica_id, instanceId });
|
const reply = await brain.chat(conv.id, text.trim(), { tenantId, hooks, clinicaId: channel?.clinica_id, instanceId });
|
||||||
await sock?.sendPresenceUpdate('paused', jid).catch(() => { });
|
await sock?.sendPresenceUpdate('paused', jid).catch(() => { });
|
||||||
await sendSecretariaReply({ instanceId, tenantId, chatId, jid, reply });
|
const secAgent = await db('sec_agents').where({ id: conv.agent_id }).select('name').first();
|
||||||
|
await sendSecretariaReply({ instanceId, tenantId, chatId, jid, reply, agentName: secAgent?.name });
|
||||||
});
|
});
|
||||||
// ── Escalation notification listener ──────────────────────────────────────
|
// ── Escalation notification listener ──────────────────────────────────────
|
||||||
// When escalar_humano tool fires, send a WA message to the configured admin phone.
|
// When escalar_humano tool fires, send a WA message to the configured admin phone.
|
||||||
@@ -1795,12 +1802,14 @@ function buildExtRoutes(prisma, manager, db, config, hooks, io) {
|
|||||||
select: { id: true, instanceId: true },
|
select: { id: true, instanceId: true },
|
||||||
});
|
});
|
||||||
if (chat) {
|
if (chat) {
|
||||||
|
const secAgent = await db('sec_agents').where({ id: conv.agent_id }).select('name').first();
|
||||||
await sendSecretariaReply({
|
await sendSecretariaReply({
|
||||||
instanceId: chat.instanceId,
|
instanceId: chat.instanceId,
|
||||||
tenantId,
|
tenantId,
|
||||||
chatId: chat.id,
|
chatId: chat.id,
|
||||||
jid: chatId,
|
jid: chatId,
|
||||||
reply,
|
reply,
|
||||||
|
agentName: secAgent?.name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
res.json({
|
res.json({
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -182,11 +182,18 @@ export function buildExtRoutes(
|
|||||||
chatId: string
|
chatId: string
|
||||||
jid: string
|
jid: string
|
||||||
reply: string
|
reply: string
|
||||||
|
agentName?: string
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const { instanceId, tenantId, chatId, jid, reply } = opts
|
const { instanceId, tenantId, chatId, jid, reply, agentName } = opts
|
||||||
const sock = manager?.getSocket(instanceId)
|
const sock = manager?.getSocket(instanceId)
|
||||||
if (!sock) return
|
if (!sock) return
|
||||||
const sent = await sock.sendMessage(jid, { text: reply }).catch(() => null)
|
// Assinatura da Secretária: prefixa "*Nome:*\n" (negrito) com o PRIMEIRO nome
|
||||||
|
// do agente (ex.: "Ana — Atendente Virtual" → "Ana"). Identifica a IA na caixa
|
||||||
|
// compartilhada — como a assinatura do operador humano. O ":" fica DENTRO do
|
||||||
|
// negrito p/ todos os dispositivos verem "Ana:" em negrito consistente.
|
||||||
|
const first = (agentName ?? '').trim().split(/[\s—–-]+/).filter(Boolean)[0]
|
||||||
|
const text = first ? `*${first}:*\n${reply}` : reply
|
||||||
|
const sent = await sock.sendMessage(jid, { text }).catch(() => null)
|
||||||
const persisted = await prisma.message.create({
|
const persisted = await prisma.message.create({
|
||||||
data: {
|
data: {
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -196,7 +203,7 @@ export function buildExtRoutes(
|
|||||||
messageId: sent?.key.id ?? `sec-${Date.now()}`,
|
messageId: sent?.key.id ?? `sec-${Date.now()}`,
|
||||||
fromMe: true,
|
fromMe: true,
|
||||||
type: 'TEXT',
|
type: 'TEXT',
|
||||||
body: reply,
|
body: text,
|
||||||
status: 'SENT',
|
status: 'SENT',
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
},
|
},
|
||||||
@@ -328,7 +335,8 @@ export function buildExtRoutes(
|
|||||||
const reply = await brain.chat(conv.id as string, text.trim(), { tenantId, hooks, clinicaId: channel?.clinica_id, instanceId })
|
const reply = await brain.chat(conv.id as string, text.trim(), { tenantId, hooks, clinicaId: channel?.clinica_id, instanceId })
|
||||||
|
|
||||||
await sock?.sendPresenceUpdate('paused', jid).catch(() => {})
|
await sock?.sendPresenceUpdate('paused', jid).catch(() => {})
|
||||||
await sendSecretariaReply({ instanceId, tenantId, chatId, jid, reply })
|
const secAgent = await db('sec_agents').where({ id: conv.agent_id }).select('name').first()
|
||||||
|
await sendSecretariaReply({ instanceId, tenantId, chatId, jid, reply, agentName: secAgent?.name })
|
||||||
})
|
})
|
||||||
|
|
||||||
// ── Escalation notification listener ──────────────────────────────────────
|
// ── Escalation notification listener ──────────────────────────────────────
|
||||||
@@ -1662,12 +1670,14 @@ export function buildExtRoutes(
|
|||||||
select: { id: true, instanceId: true },
|
select: { id: true, instanceId: true },
|
||||||
})
|
})
|
||||||
if (chat) {
|
if (chat) {
|
||||||
|
const secAgent = await db('sec_agents').where({ id: conv.agent_id }).select('name').first()
|
||||||
await sendSecretariaReply({
|
await sendSecretariaReply({
|
||||||
instanceId: chat.instanceId,
|
instanceId: chat.instanceId,
|
||||||
tenantId,
|
tenantId,
|
||||||
chatId: chat.id,
|
chatId: chat.id,
|
||||||
jid: chatId,
|
jid: chatId,
|
||||||
reply,
|
reply,
|
||||||
|
agentName: secAgent?.name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user