Is Nome do Atendente para WhatsApp Web — Zapme safe?
Zapme is medium risk. The AI writing and daily-summary buttons in WhatsApp Web build a prompt from your message or today's chat records. The background worker posts that prompt to OpenAI's responses endpoint using the OpenAI API key stored in the extension.
Who publishes itLuanCastoldi - no other listings under this identity
LuanCastoldi - no other listings under this identity
What this publisher told the store about itself, and the other listings that told it the same thing.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
WhatsApp AI prompts relayed to OpenAI
The AI writing and daily-summary buttons in WhatsApp Web build a prompt from your message or today's chat records.
The background worker posts that prompt to OpenAI's responses endpoint using the OpenAI API key stored in the extension.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You click the extension's AI text-improvement or daily-summary button in WhatsApp Web.
The feature is user-triggered; the code checks for an API key and message content before sending.
The extension sends the resulting WhatsApp prompt to OpenAI for processing.
The same background relay handles both the draft-text and conversation-summary message types.
- Draft WhatsApp messagePreciso remarcar a reuniao de hoje para 15h. (illustrative)
The text you are composing can be sent so the AI feature can rewrite it.
- Today's chat records[{"id":1,"me":false,"txt":"Oi, voce tem horario hoje?","by":"+55 11 99999-1234","t":"09:14","dt":"15/07/2026"}] (illustrative)
Messages from the current WhatsApp conversation can be sent for the daily-summary feature.
- Chat metadataby=Maria, t=09:14, dt=15/07/2026 (illustrative)
The summary prompt tells OpenAI which sender, time, and date belong to each message.
- OpenAI API keyAuthorization: Bearer sk-<redacted>
The request is authorized with the key saved in the extension's AI module.
Dynamic analysis confirmed a POST to OpenAI's responses endpoint with model gpt-4o-mini and the prompt in the input field.
- Content-Type
- application/json
- Authorization
- Bearer sk-<redacted>
Content script builds WhatsApp prompts and sends them to the background relay
AI buttons are attached to WhatsApp Web actions
deobfuscated/index.jsloadModules(attempt = 0) { clearTimeout(IA.LOAD_TIMER); IA.removeImproveTextButton(); const isImproveTextEnabled = IA.isImproveTextEnabled(); const isSummarizeConversationEnabled = IA.isSummarizeConversationEnabled(); if (!isImproveTextEnabled && !isSummarizeConversationEnabled) return; const keyboard = KEYBOARD; if (!keyboard || !document.body.contains(keyboard)) { if (attempt < 10) { IA.LOAD_TIMER = setTimeout(() => IA.loadModules(attempt + 1), 200); } return; } const keyboardRect = keyboard.getBoundingClientRect(); const actionBar = document.createElement('div'); actionBar.className = 'zapme-ia-actions-bar'; actionBar.setAttribute('aria-label', 'Acoes de IA'); if (isImproveTextEnabled) { const button = document.createElement('button'); button.type = 'button'; button.className = 'zapme-ia-improve-text-btn'; button.disabled = IA.IS_IMPROVING_TEXT; IA.renderImproveTextButton(button, IA.IS_IMPROVING_TEXT); button.addEventListener('click', () => IA.improveTextWithOpenAI(button)); actionBar.appendChild(button); } if (isSummarizeConversationEnabled) { const summarizeButton = document.createElement('button'); summarizeButton.type = 'button'; summarizeButton.className = 'zapme-ia-summary-chat-btn'; summarizeButton.disabled = IA.IS_SUMMARIZING_CONVERSATION; IA.renderSummarizeConversationButton(summarizeButton, IA.IS_SUMMARIZING_CONVERSATION); summarizeButton.addEventListener('click', () => IA.summarizeTodayConversationWithOpenAI(summarizeButton)); actionBar.appendChild(summarizeButton); } document.body.appendChild(actionBar); IA.positionImproveTextActions(actionBar, keyboardRect); }Daily summary serializes today's WhatsApp chat records into the prompt
deobfuscated/index.jsgetTodayChatMessagesForSummary() { const messages = APP.getMsgAllChat(); const todayDateFormats = IA.getTodayChatDateFormats(); const todayMessages = messages.filter((message) => { const messageDate = String(message.dt || "").trim(); return todayDateFormats.has(messageDate); }); return todayMessages; }, compactMessagesForConversationSummary(messages) { return messages; }, buildConversationSummaryPrompt(messages) { const compactMessages = IA.compactMessagesForConversationSummary(messages); const messagesJson = JSON.stringify(compactMessages); return `Voce recebera uma conversa extraida do WhatsApp Web em JSON.Objetivo:Gerar um resumo bem enxuto para a equipe entender rapidamente o essencial da conversa.Explique mentalmente os atributos antes de resumir:- id: posicao cronologica da mensagem dentro da lista enviada.- me: true significa mensagem enviada pela nossa equipe/atendente; false significa mensagem enviada pelo contato/cliente.- txt: conteudo real da mensagem. Quando o texto comecar com um nome de pessoa seguido de dois-pontos, por exemplo "Luan:", esse nome representa o atendente que escreveu a mensagem, nao faz parte da fala do cliente.- by: nome, telefone ou identificacao exibida pelo WhatsApp para quem enviou a mensagem.- t: horario em que a mensagem foi enviada.- dt: data em que a mensagem foi enviada.Regras de interpretacao:- As mensagens estao em ordem cronologica, da mais antiga para a mais recente.- Separe mentalmente atendente/equipe e contato/cliente usando me.- Trate nomes no inicio de mensagens com me true, como "Luan:", como assinatura/nome do atendente.- Preserve apenas fatos, decisoes, pedidos, objecoes e combinacoes realmente importantes.- Nao invente informacoes que nao estejam no JSON.- Nao responda ao cliente.- Nao faca perguntas.- Nao inclua comentarios sobre o JSON, atributos, prompt ou processo.Formato da resposta:Retorne apenas o resumo da conversa em portugues do Brasil.Use no maximo 4 bullets curtos.Nao use titulo.Nao inclua perguntas.Nao passe de 600 caracteres.JSON da conversa:${messagesJson}`; }Draft text and both prompt types are sent through chrome.runtime messaging
deobfuscated/index.jsasync improveTextWithOpenAI(button = null) { if (IA.IS_IMPROVING_TEXT) return; const text = String(KEYBOARD?.innerText || KEYBOARD?.textContent || "").trim(); if (!text) { APP.alertDialog("Digite um texto no campo de mensagem antes de usar a melhoria com IA.", "I.A"); return; } const apiToken = IA.getApiTokenForRequest(); if (!apiToken) { APP.alertDialog("Cadastre uma chave de acesso no módulo de I.A antes de continuar.", "I.A"); return; } IA.IS_IMPROVING_TEXT = true; IA.syncImproveTextButtonState(); try { const outputText = await IA.requestOpenAITextImprove(apiToken, IA.buildImproveTextPrompt(text)); await IA.confirmAndReplaceText(outputText); } catch (error) { APP.alertDialog(error.message || "Não foi possível melhorar o texto com IA.", "I.A"); } finally { IA.IS_IMPROVING_TEXT = false; IA.syncImproveTextButtonState(); } }, buildImproveTextPrompt(text) { const activeName = NICKNAME.ACTIVE_NAME && NICKNAME.ACTIVE_NAME != "null" ? cleanNameUser(NICKNAME.ACTIVE_NAME).trim() : ""; const ignoreNameRule = activeName ? `\n- Ignore e remova do retorno o nome personalizado do atendente: "${activeName}".` : ""; const improvePrompt = IA.getImproveTextPrompt(); return `${improvePrompt}: ${text}Regras:- Retorne somente a frase melhorada.- Não adicione explicações, comentários, títulos, aspas, sugestões ou perguntas complementares.${ignoreNameRule}`; }, requestOpenAITextImprove(apiToken, prompt) { return new Promise((resolve, reject) => { chrome.runtime.sendMessage({ type: "openaiImproveText", apiToken, prompt }, (response) => { if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } if (!response?.ok) { reject(new Error(response?.error || "Erro na requisição para a OpenAI.")); return; } resolve(response.outputText || ""); }); }); }, requestOpenAIConversationSummary(apiToken, prompt) { return new Promise((resolve, reject) => { chrome.runtime.sendMessage({ type: "openaiConversationSummary", apiToken, prompt }, (response) => { if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } if (!response?.ok) { reject(new Error(response?.error || "Erro na requisição para a OpenAI.")); return; } resolve(response.outputText || ""); }); }); }, getApiTokenForRequest() { const encodedApiToken = localStorage.getItem(LOCAL.IA_API_TOKEN); if (!encodedApiToken) return null; try { return atob(encodedApiToken); } catch (error) { return null; } }Background service worker posts the prompt to OpenAI
OpenAI relay and message handlers
deobfuscated/background.jsasync function improveTextWithOpenAI(apiToken, prompt) { const response = await fetch("https://api.openai.com/v1/responses", { method: "POST", headers: { "Authorization": `Bearer ${apiToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ model: "gpt-4o-mini", input: prompt }) }); const data = await response.json().catch(() => null); if (!response.ok) { throw new Error(data?.error?.message || `OpenAI retornou status ${response.status}`); } return data?.output_text || data?.choices?.[0]?.message?.content || data?.output ?.flatMap((item) => item.content || []) ?.find((content) => content.type === "output_text") ?.text || "";}chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === 'openaiImproveText') { improveTextWithOpenAI(message.apiToken, message.prompt) .then((outputText) => sendResponse({ ok: true, outputText })) .catch((error) => sendResponse({ ok: false, error: error.message })); return true; } if (message.type === 'openaiConversationSummary') { improveTextWithOpenAI(message.apiToken, message.prompt) .then((outputText) => sendResponse({ ok: true, outputText })) .catch((error) => sendResponse({ ok: false, error: error.message })); return true; } if (message.type === 'loadInfoPro30') { loadInfoPro30().then((licenca) => { sendResponse({ licenca_30: licenca ?? null }); }); return true; // permite resposta assíncrona } if (message.type === 'loadPlanoAnual') { loadPlanoAnual().then((licenca) => { sendResponse({ licenca_ano: licenca ?? null }); }); return true; // permite resposta assíncrona } if (message.type === 'saveInfoPro30') { saveInfoPro30(message.timestamp || null).then(() => { sendResponse({ status: 'ok' }); }); return true; } if (message.type === 'savePlanoAnual') { savePlanoAnual(message.timestamp || null).then(() => { sendResponse({ status: 'ok' }); }); return true; } if (message.type === 'saveIdPaymentPro') { saveIdPaymentPro(message.idPayment || null).then(() => { sendResponse({ status: 'ok' }); }); return true; } if (message.type === 'loadIdPayment') { loadIdPayment().then((idPayment) => { sendResponse({ idPayment: idPayment }); }); return true; // permite resposta assíncrona }});- api.openai.com
OpenAI responses endpoint that receives the WhatsApp-derived prompt and returns generated text or a summary.