woocommerce integration, controllers and handlers ready, evolution api simulator ready

This commit is contained in:
Lucas Tettamanti
2026-01-02 16:49:35 -03:00
parent 556c49e53d
commit 303c3daafe
19 changed files with 637 additions and 141 deletions

View File

@@ -23,22 +23,24 @@ class ChatSimulator extends HTMLElement {
</style>
<div class="box">
<div class="muted">Simulador WhatsApp (local)</div>
<div class="muted">Evolution Sim (único chat)</div>
<div class="row" style="margin-top:8px">
<input id="from" style="flex:1" value="+5491100000000" placeholder="+54911xxxxxxxx" />
<input id="instance" style="flex:1" value="Piaf" placeholder="tenant/instance key" />
</div>
<div class="row" style="margin-top:8px">
<input id="chat" style="flex:1" value="sim:+5491100000000" placeholder="chat_id" />
<button id="reset">Reset</button>
<input id="evoFrom" style="flex:1" value="5491133230322@s.whatsapp.net" placeholder="from (remoteJid)" />
</div>
</div>
<div class="box">
<div class="muted">Chat</div>
<div class="row" style="margin-top:8px">
<input id="evoTo" style="flex:1" value="5491137887040@s.whatsapp.net" placeholder="to (destino receptor)" />
</div>
<div class="row" style="margin-top:8px">
<input id="pushName" style="flex:1" value="SimUser" placeholder="pushName (opcional)" />
</div>
<div class="muted" style="margin-top:8px">Chat</div>
<div class="chatlog" id="log"></div>
<textarea id="text" placeholder="Escribí como cliente…"></textarea>
<textarea id="evoText" placeholder="Texto a enviar por Evolution…"></textarea>
<div class="row" style="margin-top:8px">
<button class="primary" id="send" style="flex:1">Send</button>
<button class="primary" id="sendEvo" style="flex:1">Send</button>
</div>
</div>
@@ -50,47 +52,88 @@ class ChatSimulator extends HTMLElement {
}
connectedCallback() {
const fromEl = this.shadowRoot.getElementById("from");
const chatEl = this.shadowRoot.getElementById("chat");
const resetEl = this.shadowRoot.getElementById("reset");
const sendEl = this.shadowRoot.getElementById("send");
const evoInstanceEl = this.shadowRoot.getElementById("instance");
const evoFromEl = this.shadowRoot.getElementById("evoFrom");
const evoToEl = this.shadowRoot.getElementById("evoTo");
const evoPushEl = this.shadowRoot.getElementById("pushName");
const evoTextEl = this.shadowRoot.getElementById("evoText");
const sendEvoEl = this.shadowRoot.getElementById("sendEvo");
resetEl.onclick = () => {
this.shadowRoot.getElementById("log").innerHTML = "";
this.shadowRoot.getElementById("raw").textContent = "—";
const phone = (fromEl.value || "+5491100000000").trim();
chatEl.value = `sim:${phone}`;
this.append("bot", "Chat reseteado (solo UI). Enviá un mensaje para generar runs.");
};
const sendAction = async () => {
const instance = evoInstanceEl.value.trim() || "Piaf";
const from = evoFromEl.value.trim() || "5491133230322@s.whatsapp.net"; // cliente
const to = evoToEl.value.trim() || "5491137887040@s.whatsapp.net"; // canal/destino
const text = evoTextEl.value.trim();
const pushName = evoPushEl.value.trim();
sendEl.onclick = async () => {
const text = this.shadowRoot.getElementById("text").value.trim();
if (!text) return;
const from_phone = fromEl.value.trim();
const chat_id = chatEl.value.trim();
if (!from_phone || !chat_id) return alert("Falta teléfono o chat_id");
this.append("user", text);
this.shadowRoot.getElementById("text").value = "";
const data = await api.simSend({ chat_id, from_phone, text });
this.shadowRoot.getElementById("raw").textContent = JSON.stringify(data, null, 2);
if (!data.ok) {
this.append("bot", "Error en simulación.");
if (!from || !text) {
alert("Falta from o text");
return;
}
this.append("bot", data.reply);
emit("ui:selectedChat", { chat_id });
const nowSec = Math.floor(Date.now() / 1000);
const genId = () =>
(self.crypto?.randomUUID?.() || `${Date.now()}${Math.random()}`)
.replace(/-/g, "")
.slice(0, 22)
.toUpperCase();
const payload = {
body: {
event: "messages.upsert",
instance,
data: {
key: {
// remoteJid debe ser el cliente (buyer)
remoteJid: from,
fromMe: false,
id: genId(),
participant: "",
addressingMode: "pn",
},
pushName: pushName || "SimUser",
status: "DELIVERY_ACK",
message: { conversation: text },
messageType: "conversation",
messageTimestamp: nowSec,
instanceId: genId(),
source: "sim",
},
date_time: new Date().toISOString(),
sender: from,
server_url: "http://localhost",
apikey: "SIM",
},
};
const data = await api.simEvolution(payload);
this.shadowRoot.getElementById("raw").textContent = JSON.stringify(data, null, 2);
console.log("[evolution sim] webhook response:", data);
if (!data.ok) {
this.append("bot", "Error en Evolution Sim.");
return;
}
emit("ui:selectedChat", { chat_id: from });
this.append("user", text);
this.append("bot", `[Evolution] enviado (sim): ${text}`);
evoTextEl.value = "";
};
// si querés, cuando llega un upsert de conversación simulada, podés auto-seleccionarla
sendEvoEl.onclick = sendAction;
evoTextEl.addEventListener("keydown", (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
sendAction();
}
});
this._unsub = on("conversation:upsert", (c) => {
const chat_id = this.shadowRoot.getElementById("chat").value.trim();
const chat_id = evoFromEl.value.trim() || "5491133230322@s.whatsapp.net";
if (c.chat_id === chat_id) {
// no-op, pero podrías reflejar estado/intent acá si querés
// placeholder: podrías reflejar estado/intent acá si querés
}
});
}

View File

@@ -14,11 +14,11 @@ export const api = {
return fetch(u).then(r => r.json());
},
async simSend({ chat_id, from_phone, text }) {
return fetch("/sim/send", {
async simEvolution(payload) {
return fetch("/webhook/evolution", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ chat_id, from_phone, text }),
body: JSON.stringify(payload),
}).then(r => r.json());
},
};