fix(inbox): never resolve our own domains to a customer (wrong sender names)
Root cause: some F customer records carry our own/staff addresses in email_billing, so matchCustomer's `email_billing LIKE '%addr%'` resolved support@targo.ca -> "Guylaine Gagnon" and gilles@targointernet.com -> "Sylvie Juteau". Every thread on one of our addresses then inherited that wrong customer + name. - OWN_DOMAINS promoted to a single source in lib/helpers.js (was duplicated in conversation.js); inbox-triage.matchCustomer() now returns null for any own-domain address — a customer is never reachable at targo.ca / targointernet.com / gigafibre.ca. - conversation.js consumes the shared OWN_DOMAINS (removes the local copy). Also ran a one-shot repair (temporary endpoint, since removed) that unlinked the 6 already-contaminated threads and reset their display name to the real address. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e604cbf237
commit
9466645e17
|
|
@ -3,7 +3,7 @@ const crypto = require('crypto')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const cfg = require('./config')
|
const cfg = require('./config')
|
||||||
const { log, json, parseBody, lookupCustomerByPhone, lookupCustomersByPhone, lookupCustomersByEmail, createCommunication, readJsonFile, writeJsonFile, stripDataUrl } = require('./helpers')
|
const { log, json, parseBody, lookupCustomerByPhone, lookupCustomersByPhone, lookupCustomersByEmail, createCommunication, readJsonFile, writeJsonFile, stripDataUrl, OWN_DOMAINS } = require('./helpers')
|
||||||
const erp = require('./erp')
|
const erp = require('./erp')
|
||||||
const sse = require('./sse')
|
const sse = require('./sse')
|
||||||
|
|
||||||
|
|
@ -259,7 +259,7 @@ const PIPELINE_STAGES = ['Nouveau', 'Qualifié', 'Devis', 'Gagné', 'Perdu'] //
|
||||||
// ── Garde-fous anti-spam de file (ingest courriel) ────────────────────────────
|
// ── Garde-fous anti-spam de file (ingest courriel) ────────────────────────────
|
||||||
// Empêche le blast d'une file pour : nos PROPRES notifs réingérées (boucle), les expéditeurs de NOS domaines
|
// Empêche le blast d'une file pour : nos PROPRES notifs réingérées (boucle), les expéditeurs de NOS domaines
|
||||||
// (staff/systèmes internes), et les RÉPONSES à un ticket existant (→ rattacher au fil, pas un nouveau routage).
|
// (staff/systèmes internes), et les RÉPONSES à un ticket existant (→ rattacher au fil, pas un nouveau routage).
|
||||||
const OWN_DOMAINS = /@([a-z0-9-]+\.)*(targo\.ca|targointernet\.com|gigafibre\.ca)$/i // inclut les SOUS-DOMAINES (ex. directadmin.targointernet.com = notre infra) → interne, jamais un « client »
|
// OWN_DOMAINS = source unique dans lib/helpers.js (inclut les SOUS-DOMAINES infra ; jamais un « client »).
|
||||||
const OWN_NOTIF_SUBJECT = /^\s*(re\s*:\s*)?\[(inbox|ticket)\s*[·:]/i // nos sujets [Inbox · …] / [Ticket · …]
|
const OWN_NOTIF_SUBJECT = /^\s*(re\s*:\s*)?\[(inbox|ticket)\s*[·:]/i // nos sujets [Inbox · …] / [Ticket · …]
|
||||||
const TICKET_REPLY_SUBJECT = /\[ticket\s*#?\s*[\w-]+\]/i // réponse à un ticket existant : [Ticket #ISS-2026-001] ou [Ticket #250819]
|
const TICKET_REPLY_SUBJECT = /\[ticket\s*#?\s*[\w-]+\]/i // réponse à un ticket existant : [Ticket #ISS-2026-001] ou [Ticket #250819]
|
||||||
// Expéditeur MACHINE (jamais un humain à qui répondre dans l'inbox client) : no-reply, daemon de courriel, panneau d'hébergement…
|
// Expéditeur MACHINE (jamais un humain à qui répondre dans l'inbox client) : no-reply, daemon de courriel, panneau d'hébergement…
|
||||||
|
|
|
||||||
|
|
@ -188,9 +188,13 @@ function saveSeenSet (path, set, cap = 3000) { try { fs.writeFileSync(path, JSON
|
||||||
// Retire le préfixe data:image/...;base64, → base64 pur (pour l'OCR vision).
|
// Retire le préfixe data:image/...;base64, → base64 pur (pour l'OCR vision).
|
||||||
function stripDataUrl (s) { return String(s || '').replace(/^data:[^;]+;base64,/, '') }
|
function stripDataUrl (s) { return String(s || '').replace(/^data:[^;]+;base64,/, '') }
|
||||||
|
|
||||||
|
// NOS PROPRES domaines (incl. sous-domaines infra) — SOURCE UNIQUE. Une adresse qui matche ici n'est
|
||||||
|
// JAMAIS un client : ni à matcher comme fiche (triage), ni à utiliser comme clé de regroupement (ingest).
|
||||||
|
const OWN_DOMAINS = /@([a-z0-9-]+\.)*(targo\.ca|targointernet\.com|gigafibre\.ca)$/i
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
log, json, parseBody, httpRequest, cors,
|
log, json, parseBody, httpRequest, cors,
|
||||||
erpFetch, erpRequest, lookupCustomerByPhone, lookupCustomersByPhone, lookupCustomersByEmail, createCommunication,
|
erpFetch, erpRequest, lookupCustomerByPhone, lookupCustomersByPhone, lookupCustomersByEmail, createCommunication,
|
||||||
nbiRequest, deepGetValue,
|
nbiRequest, deepGetValue,
|
||||||
readJsonFile, writeJsonFile, loadSeenSet, saveSeenSet, stripDataUrl,
|
readJsonFile, writeJsonFile, loadSeenSet, saveSeenSet, stripDataUrl, OWN_DOMAINS,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// classifyEmail({from,subject,body}) : client connu ? type ? faut-il suggérer un ticket ?
|
// classifyEmail({from,subject,body}) : client connu ? type ? faut-il suggérer un ticket ?
|
||||||
// SANS dépendance à la boîte mail (testable seul + réutilisable par le poller une fois la boîte branchée).
|
// SANS dépendance à la boîte mail (testable seul + réutilisable par le poller une fois la boîte branchée).
|
||||||
const cfg = require('./config')
|
const cfg = require('./config')
|
||||||
const { log } = require('./helpers')
|
const { log, OWN_DOMAINS } = require('./helpers')
|
||||||
const erp = require('./erp')
|
const erp = require('./erp')
|
||||||
|
|
||||||
function extractEmail (from) {
|
function extractEmail (from) {
|
||||||
|
|
@ -14,6 +14,10 @@ function extractEmail (from) {
|
||||||
// Match client DÉTERMINISTE par courriel (email_id, puis email_billing qui peut contenir plusieurs adresses).
|
// Match client DÉTERMINISTE par courriel (email_id, puis email_billing qui peut contenir plusieurs adresses).
|
||||||
async function matchCustomer (email) {
|
async function matchCustomer (email) {
|
||||||
if (!email) return null
|
if (!email) return null
|
||||||
|
// JAMAIS matcher une de NOS adresses (support@targo.ca, *@targointernet.com…) sur une fiche : des
|
||||||
|
// enregistrements F contiennent par erreur nos adresses dans email_billing → le LIKE liait support@targo.ca
|
||||||
|
// à « Guylaine Gagnon », gilles@ à « Sylvie Juteau ». Une fiche client n'est jamais joignable à nos domaines.
|
||||||
|
if (OWN_DOMAINS.test(email)) return null
|
||||||
try {
|
try {
|
||||||
let r = await erp.list('Customer', { filters: [['email_id', '=', email]], fields: ['name', 'customer_name'], limit: 1 })
|
let r = await erp.list('Customer', { filters: [['email_id', '=', email]], fields: ['name', 'customer_name'], limit: 1 })
|
||||||
if (r && r[0]) return r[0]
|
if (r && r[0]) return r[0]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user