Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ba7cc95e0 | ||
|
|
32bdaf3f23 |
@@ -5,7 +5,6 @@
|
|||||||
"www": {
|
"www": {
|
||||||
"site": "www",
|
"site": "www",
|
||||||
"tenantId": 2,
|
"tenantId": 2,
|
||||||
"currencyCode": "EUR",
|
|
||||||
"searchUrl": "https://www.yehwang.com/search.html?q=",
|
"searchUrl": "https://www.yehwang.com/search.html?q=",
|
||||||
"siteMap": "site-map.json",
|
"siteMap": "site-map.json",
|
||||||
"language": "en"
|
"language": "en"
|
||||||
@@ -13,7 +12,6 @@
|
|||||||
"us": {
|
"us": {
|
||||||
"site": "us",
|
"site": "us",
|
||||||
"tenantId": 2,
|
"tenantId": 2,
|
||||||
"currencyCode": "USD",
|
|
||||||
"searchUrl": "https://us.yehwang.com/search.html?q=",
|
"searchUrl": "https://us.yehwang.com/search.html?q=",
|
||||||
"siteMap": "site-map-us.json",
|
"siteMap": "site-map-us.json",
|
||||||
"language": "en"
|
"language": "en"
|
||||||
@@ -21,7 +19,6 @@
|
|||||||
"eu": {
|
"eu": {
|
||||||
"site": "www",
|
"site": "www",
|
||||||
"tenantId": 2,
|
"tenantId": 2,
|
||||||
"currencyCode": "EUR",
|
|
||||||
"searchUrl": "https://www.yehwang.com/search.html?q=",
|
"searchUrl": "https://www.yehwang.com/search.html?q=",
|
||||||
"siteMap": "site-map.json",
|
"siteMap": "site-map.json",
|
||||||
"language": "en"
|
"language": "en"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ function loadSites(env) {
|
|||||||
const fallback = {
|
const fallback = {
|
||||||
defaultSite: 'www',
|
defaultSite: 'www',
|
||||||
profiles: {
|
profiles: {
|
||||||
www: { site: 'www', tenantId: null, languageId: null, currencyCode: 'EUR', searchUrl: 'https://www.yehwang.com/search.html?q=', siteMap: 'site-map.json' },
|
www: { site: 'www', tenantId: null, languageId: null, currencyCode: 'USD', searchUrl: 'https://www.yehwang.com/search.html?q=', siteMap: 'site-map.json' },
|
||||||
},
|
},
|
||||||
inboxes: {},
|
inboxes: {},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,17 +17,24 @@ function createEscalation({ client, handoffUserId = 1 }) {
|
|||||||
const text = String(reply || '').replace(/\s+$/, '');
|
const text = String(reply || '').replace(/\s+$/, '');
|
||||||
const hints = {
|
const hints = {
|
||||||
zh: '您想转人工客服吗?',
|
zh: '您想转人工客服吗?',
|
||||||
|
en: 'Would you like to speak to a human agent?',
|
||||||
de: 'Möchten Sie mit einem Mitarbeiter sprechen?',
|
de: 'Möchten Sie mit einem Mitarbeiter sprechen?',
|
||||||
fr: 'Souhaitez-vous parler à un conseiller ?',
|
fr: 'Souhaitez-vous parler à un conseiller ?',
|
||||||
it: 'Vuole parlare con un operatore?',
|
it: 'Vuole parlare con un operatore?',
|
||||||
nl: 'Wilt u met een medewerker spreken?',
|
nl: 'Wilt u met een medewerker spreken?',
|
||||||
|
es: '¿Desea hablar con un agente humano?',
|
||||||
|
tr: 'Bir insan temsilcisiyle görüşmek ister misiniz?',
|
||||||
};
|
};
|
||||||
|
// 只输出一种语言:按用户输入判断语言,识别不到时用英文;不再拼英文兜底。
|
||||||
let lang = null;
|
let lang = null;
|
||||||
if (/[\u4e00-\u9fff]/.test(query)) lang = 'zh';
|
if (/[\u4e00-\u9fff]/.test(query)) lang = 'zh';
|
||||||
|
else if (/[ğışç]/i.test(query) || /\b(merhaba|istiyorum|konuşmak|temsilci|fiyat|ürün|sipariş)\b/i.test(query)) lang = 'tr';
|
||||||
else if (/[äöüß]/i.test(query)) lang = 'de';
|
else if (/[äöüß]/i.test(query)) lang = 'de';
|
||||||
else if (/[éèêëàâùûîïôç]/i.test(query)) lang = 'fr';
|
else if (/[ñ¿¡]/i.test(query) || /\b(hola|quiero|hablar|agente|puedo|gracias|precio|pedido)\b/i.test(query)) lang = 'es';
|
||||||
|
else if (/\b(vorrei|parlare|operatore|desidera|posso|prezzo|ordine)\b/i.test(query)) lang = 'it';
|
||||||
|
else if (/[éèêëàâùûîïôç]/i.test(query) || /\b(bonjour|merci|conseiller|commande|livraison|prix|voudrais)\b/i.test(query)) lang = 'fr';
|
||||||
else if (/\b(hoe|voor|wilt|bent|geen|verzending|levering|bestelling)\b/i.test(query)) lang = 'nl';
|
else if (/\b(hoe|voor|wilt|bent|geen|verzending|levering|bestelling)\b/i.test(query)) lang = 'nl';
|
||||||
const option = lang ? `\n\n${hints[lang]} / Would you like to speak to a human agent?` : '\n\nWould you like to speak to a human agent?';
|
const option = `\n\n${hints[lang] || hints.en}`;
|
||||||
return text + option;
|
return text + option;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -569,30 +569,3 @@ test('create_opportunity: creates opportunity + note + follow-up task', async ()
|
|||||||
const personIdx = twenty.calls.findIndex((c) => c[0] === 'createPerson');
|
const personIdx = twenty.calls.findIndex((c) => c[0] === 'createPerson');
|
||||||
assert.ok(companyIdx >= 0 && personIdx > companyIdx, 'company must be created before person');
|
assert.ok(companyIdx >= 0 && personIdx > companyIdx, 'company must be created before person');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sites.json: www/eu -> EUR, us -> USD', () => {
|
|
||||||
const sites = require('../config/sites.json');
|
|
||||||
assert.equal(sites.profiles.www.currencyCode, 'EUR');
|
|
||||||
assert.equal(sites.profiles.eu.currencyCode, 'EUR');
|
|
||||||
assert.equal(sites.profiles.us.currencyCode, 'USD');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('search_products: passes the site currency to the product RPC', async () => {
|
|
||||||
const rpc = fakeRpc(() => []);
|
|
||||||
const cfg = {
|
|
||||||
...baseCfg,
|
|
||||||
sites: {
|
|
||||||
defaultSite: 'www',
|
|
||||||
profiles: { www: { site: 'www', tenantId: 2, languageId: 1, currencyCode: 'EUR', searchUrl: 'https://www.yehwang.com/search.html?q=' } },
|
|
||||||
inboxes: { '1': 'www' },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const tools = createTools(cfg, { rpc, twenty: fakeTwenty() });
|
|
||||||
const tool = tools.find((t) => t.name === 'search_products');
|
|
||||||
await tool.handler({ query: 'rings' }, { inboxId: '1' });
|
|
||||||
const call = rpc.calls[0];
|
|
||||||
assert.equal(call.method, '/product/list');
|
|
||||||
assert.equal(call.params[0].currencyCode, 'EUR');
|
|
||||||
assert.equal(call.params[0].tenantBaseCurrency, 'EUR');
|
|
||||||
assert.equal(call.params[0].reqContext.currencyCode, 'EUR');
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -199,3 +199,6 @@ ss -ltnp | grep -E ':(9000|3100|4100|5440|6390)\s'
|
|||||||
| WhatsApp webhook 收不到 | 没有公网 HTTPS / Meta 回调地址不对 | 本地联调跑 `bash scripts/public-https.sh`;测试/生产使用正式域名,不要用 Quick Tunnel |
|
| WhatsApp webhook 收不到 | 没有公网 HTTPS / Meta 回调地址不对 | 本地联调跑 `bash scripts/public-https.sh`;测试/生产使用正式域名,不要用 Quick Tunnel |
|
||||||
| 公网访问到管理后台 | 网关白名单配置 | `services/public-gateway` 只放行 `/webhooks/whatsapp/*`、`/hc/*`、`/widget*`、`/static/public/*`,其余 404 |
|
| 公网访问到管理后台 | 网关白名单配置 | `services/public-gateway` 只放行 `/webhooks/whatsapp/*`、`/hc/*`、`/widget*`、`/static/public/*`,其余 404 |
|
||||||
| AI 回复"系统暂时无法查询订单" | RPC 不可达,bridge 8s 超时 | 本地开发跑 `bash scripts/rpc-tunnel.sh --apply`;测试/生产检查内网 RPC 地址/网络策略,不要用开发机隧道 |
|
| AI 回复"系统暂时无法查询订单" | RPC 不可达,bridge 8s 超时 | 本地开发跑 `bash scripts/rpc-tunnel.sh --apply`;测试/生产检查内网 RPC 地址/网络策略,不要用开发机隧道 |
|
||||||
|
| WhatsApp 真实消息收不到,但 Meta "Send to My Server" 测试事件能到 | WABA 级 `override_callback_uri` 优先于 App 级 callback,且 Meta 对 override 的验证常返回 403(与目标 URL 无关) | `GET /<WABA_ID>/subscribed_apps` 确认 override;`DELETE /<WABA_ID>/subscribed_apps` 后重新 `POST`(不带 override)清掉,确认 App 级 callback 指向目标域名,再发真实消息验证 |
|
||||||
|
| 多个栈共用同一个 WhatsApp 号,消息被旧环境接走(旧环境 AI 回复、新环境无日志) | crm-new 与旧 crm-ai-demo 配置了同一 `WA_PHONE_NUMBER_ID` / WABA,旧栈 tunnel 仍在接收 | 停旧栈 WhatsApp 接收(停旧 tunnel / 禁用旧 inbox),或给新环境申请独立测试号;不要让两套同时接同一个号 |
|
||||||
|
| 新建 WhatsApp inbox 后 webhook 验证 403 `verify token mismatch` | 创建接口传入的 `webhook_verify_token` 可能未真正生效,inbox 仍是默认值;`GET /api/v1/inboxes/<id>` 返回的是掩码值,不能用来核对 | 用 `PUT /api/v1/inboxes/<id>` 带完整 config 重新保存一次,再请求 `GET /webhooks/whatsapp/<id>?hub.mode=subscribe&hub.verify_token=...&hub.challenge=...` 确认返回 200 |
|
||||||
|
|||||||
Reference in New Issue
Block a user