From d49445849b86f18740d59703671ff3da05ac3a6c Mon Sep 17 00:00:00 2001 From: OpenClaw Assistant Date: Mon, 2 Mar 2026 23:46:25 +0300 Subject: [PATCH] feat(paradiz): show AI disclosure only on first message per client --- .../paradiz-web/paradiz-web-agent-server.mjs | 20 ++++++++++---- skills/vk-gateway/vk-endpoint.mjs | 27 ++++++++++++------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/integrations/paradiz-web/paradiz-web-agent-server.mjs b/integrations/paradiz-web/paradiz-web-agent-server.mjs index 4e611ce..c1bd79f 100644 --- a/integrations/paradiz-web/paradiz-web-agent-server.mjs +++ b/integrations/paradiz-web/paradiz-web-agent-server.mjs @@ -74,11 +74,20 @@ function json(res, code, payload) { const OPENCLAW_BIN = env.OPENCLAW_BIN || '/home/openclaw/.npm-global/bin/openclaw'; const pendingHumanByClient = new Map(); +const aiDisclosedByClient = new Map(); function hasContactInfo(text) { return PHONE_RE.test(text) || EMAIL_RE.test(text) || /телеграм|whatsapp|вотсап|вк|vk/i.test(text); } +function withAiDisclosure(clientId, text) { + const t = String(text || '').trim(); + if (!clientId) return `Я ИИ-агент по бронированию «Парадиз». ${t}`; + if (aiDisclosedByClient.get(clientId)) return t; + aiDisclosedByClient.set(clientId, true); + return `Я ИИ-агент по бронированию «Парадиз». ${t}`; +} + async function sendTelegramHumanLead(userText) { if (!TG_BOT || !TG_CHAT) return false; const phone = (String(userText).match(PHONE_RE) || [])[0] || '-'; @@ -166,14 +175,14 @@ const server = http.createServer(async (req, res) => { if (clientId) pendingHumanByClient.set(clientId, true); return json(res, 200, { ok: true, - answer: 'Я ИИ-агент по бронированию «Парадиз». Подключу живого менеджера. Напишите, пожалуйста, как с вами связаться: телефон или e-mail, и удобное время.' + answer: withAiDisclosure(clientId, 'Подключу живого менеджера. Напишите, пожалуйста, как с вами связаться: телефон или e-mail, и удобное время.') }); } await sendTelegramHumanLead(question).catch(() => {}); if (clientId) pendingHumanByClient.delete(clientId); return json(res, 200, { ok: true, - answer: 'Я ИИ-агент по бронированию «Парадиз». Передала ваш запрос менеджеру, он свяжется с вами по указанному контакту.' + answer: withAiDisclosure(clientId, 'Передала ваш запрос менеджеру, он свяжется с вами по указанному контакту.') }); } @@ -182,19 +191,20 @@ const server = http.createServer(async (req, res) => { pendingHumanByClient.delete(clientId); return json(res, 200, { ok: true, - answer: 'Я ИИ-агент по бронированию «Парадиз». Передала ваш контакт менеджеру, он свяжется с вами.' + answer: withAiDisclosure(clientId, 'Передала ваш контакт менеджеру, он свяжется с вами.') }); } if (isExternalActionRequest(question)) { return json(res, 200, { ok: true, - answer: 'Я ИИ-агент по бронированию «Парадиз». Я передам информацию менеджеру, он свяжется с вами. А пока напишите, пожалуйста, даты заезда/выезда и состав гостей — подготовлю варианты и стоимость.' + answer: withAiDisclosure(clientId, 'Я передам информацию менеджеру, он свяжется с вами. А пока напишите, пожалуйста, даты заезда/выезда и состав гостей — подготовлю варианты и стоимость.') }); } const answerRaw = await runAgent(question); - const answer = /^я\s*ии-агент/i.test(answerRaw.trim()) ? answerRaw : `Я ИИ-агент по бронированию «Парадиз». ${answerRaw}`; + const answerClean = answerRaw.replace(/^я\s*ии-агент[^.?!]*[.?!]?\s*/i, '').trim(); + const answer = withAiDisclosure(clientId, answerClean || answerRaw); return json(res, 200, { ok: true, answer }); } catch (e) { return json(res, 500, { ok: false, error: e.message }); diff --git a/skills/vk-gateway/vk-endpoint.mjs b/skills/vk-gateway/vk-endpoint.mjs index f7a1df4..f08a3a8 100755 --- a/skills/vk-gateway/vk-endpoint.mjs +++ b/skills/vk-gateway/vk-endpoint.mjs @@ -281,12 +281,12 @@ async function askOpenClaw(payload) { const hasContact = Boolean(store[userKey].phone || store[userKey].email || /телеграм|whatsapp|вотсап|вк|vk/i.test(userText)); if (!hasContact) { writeLeadStore(store); - return { reply: 'Я ИИ-агент по бронированию «Парадиз». Подключу живого менеджера. Напишите, пожалуйста, телефон или e-mail и удобное время для связи.' }; + return { reply: disclose('Подключу живого менеджера. Напишите, пожалуйста, телефон или e-mail и удобное время для связи.') }; } const sent = await sendTelegramHumanLead(store[userKey]); if (sent) store[userKey].human_sent_at = new Date().toISOString(); writeLeadStore(store); - return { reply: 'Я ИИ-агент по бронированию «Парадиз». Передала ваш запрос живому менеджеру, он свяжется с вами по указанному контакту.' }; + return { reply: disclose('Передала ваш запрос живому менеджеру, он свяжется с вами по указанному контакту.') }; } if (store[userKey].need_human) { @@ -296,14 +296,22 @@ async function askOpenClaw(payload) { const sent = await sendTelegramHumanLead(store[userKey]); if (sent) store[userKey].human_sent_at = new Date().toISOString(); writeLeadStore(store); - return { reply: 'Я ИИ-агент по бронированию «Парадиз». Передала ваш контакт менеджеру — он свяжется с вами.' }; + return { reply: disclose('Передала ваш контакт менеджеру — он свяжется с вами.') }; } } writeLeadStore(store); + const disclose = (text) => { + const t = String(text || '').trim(); + if (store[userKey].ai_disclosed_at) return t; + store[userKey].ai_disclosed_at = new Date().toISOString(); + writeLeadStore(store); + return `Я ИИ-агент по бронированию «Парадиз». ${t}`; + }; + if (TECH_RE.test(userText)) { - return { reply: 'Я ИИ-агент по бронированию «Парадиз». ' + buildTechRedirectReply() }; + return { reply: disclose(buildTechRedirectReply()) }; } const reqBody = { @@ -348,19 +356,18 @@ async function askOpenClaw(payload) { const payloadOut = parseAssistantPayload(text); if (!payloadOut.reply) return { silent: true }; - const disclosure = 'Я ИИ-агент по бронированию «Парадиз». '; if (!store[userKey].ai_disclosed_at) { - payloadOut.reply = disclosure + payloadOut.reply; + payloadOut.reply = `Я ИИ-агент по бронированию «Парадиз». ${payloadOut.reply}`; store[userKey].ai_disclosed_at = new Date().toISOString(); writeLeadStore(store); - } else if (!/^я\s*ии-агент/i.test(payloadOut.reply)) { - payloadOut.reply = disclosure + payloadOut.reply; + } else { + payloadOut.reply = String(payloadOut.reply || '').replace(/^я\s*ии-агент[^.?!]*[.?!]?\s*/i, '').trim() || payloadOut.reply; } return payloadOut; } catch (e) { console.error(`[vk-endpoint] askOpenClaw error user=${userKey}: ${e.message}`); - return { reply: 'Я ИИ-агент по бронированию «Парадиз». Сейчас есть техническая задержка, но я на связи. Напишите, пожалуйста, даты и состав гостей — продолжу подбор.' }; + return { reply: disclose('Сейчас есть техническая задержка, но я на связи. Напишите, пожалуйста, даты и состав гостей — продолжу подбор.') }; } finally { clearTimeout(t); } @@ -388,7 +395,7 @@ const server = http.createServer(async (req, res) => { } catch (e) { console.error(`[vk-endpoint] inbound error: ${e.message}`); res.writeHead(200, { 'content-type': 'application/json' }); - res.end(JSON.stringify({ reply: 'Я ИИ-агент по бронированию «Парадиз». Произошла техническая ошибка. Напишите, пожалуйста, ещё раз одним сообщением: даты, гости и контакт для связи.' })); + res.end(JSON.stringify({ reply: 'Произошла техническая ошибка. Напишите, пожалуйста, ещё раз одним сообщением: даты, гости и контакт для связи.' })); } });