Compare commits

2 Commits

Author SHA1 Message Date
ZyperCH
91aa7d0cb7 Webhook 2026-06-02 20:59:23 -03:00
zyperch
197757b073 refact(webhook): corrige integracao do webhook e trata erros de cookies
- Migra arquivos de webhook de TS para JS
- Corrige imports quebrados e destructuring de SendMessageWebhook e ErrorType
- Trata arquivo cookies.json vazio/invalido com try/catch
- Corrige digitação de porcentagemPromo no ProductEntity
- Envia eventos de sucesso para busca_produto e erros para error_busca
2026-06-02 20:56:00 -03:00
9 changed files with 140 additions and 16 deletions

69
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"license": "ISC",
"dependencies": {
"axios": "^1.16.1",
"discord-webhook-node": "^1.1.8",
"puppeteer": "^25.0.4",
"puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2"
@@ -441,6 +442,32 @@
"integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==",
"license": "BSD-3-Clause"
},
"node_modules/discord-webhook-node": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/discord-webhook-node/-/discord-webhook-node-1.1.8.tgz",
"integrity": "sha512-3u0rrwywwYGc6HrgYirN/9gkBYqmdpvReyQjapoXARAHi0P0fIyf3W5tS5i3U3cc7e44E+e7dIHYUeec7yWaug==",
"license": "MIT",
"dependencies": {
"form-data": "^3.0.0",
"node-fetch": "^2.6.0"
}
},
"node_modules/discord-webhook-node/node_modules/form-data": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz",
"integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.35"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -1017,6 +1044,26 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1437,6 +1484,12 @@
"b4a": "^1.6.4"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
"node_modules/typed-query-selector": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz",
@@ -1458,6 +1511,22 @@
"integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==",
"license": "Apache-2.0"
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",

View File

@@ -1,19 +1,21 @@
{
"dependencies": {
"axios": "^1.16.1",
"discord-webhook-node": "^1.1.8",
"puppeteer": "^25.0.4",
"puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"name": "codevscodesjs",
"version": "1.0.0",
"version": "1.1.0",
"description": "",
"main": "index.js",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node src/index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs"
}
}

View File

@@ -1,3 +1,5 @@
const ErrorType = require('../../webhook/ErrorType');
const SendMessageWebhook = require('../../webhook/webhook');
const ProductEntity = require('../domain/ProductEntity');
class ProcessPromotionsUseCase {
@@ -24,7 +26,10 @@ class ProcessPromotionsUseCase {
}
}
SendMessageWebhook('Processo finalizado!', `${enviados} produtos foram enviados para a API.`, ErrorType.busca_produto);
console.log(`Processo finalizado! ${enviados} produtos foram enviados para a API.`);
}
}

View File

@@ -1,9 +1,9 @@
class ProductEntity {
constructor({ nomeProduto, precoOriginal, precoPromocao, porcetagemPromo, imagem, linkProduto }) {
constructor({ nomeProduto, precoOriginal, precoPromocao, porcentagemPromo, imagem, linkProduto }) {
this.nomeProduto = nomeProduto || 'sem título';
this.precoOriginal = precoOriginal || 0;
this.precoPromocao = precoPromocao || 0;
this.porcetagemPromo = porcetagemPromo || 0;
this.porcentagemPromo = porcentagemPromo || 0;
this.imagem = imagem || '';
this.linkProduto = linkProduto || '';
this.plataforma = 'Mercado Livre'; // Regra de negócio fixa para este robô

View File

@@ -2,21 +2,23 @@ const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const fs = require('fs');
const SendMessageWebhook = require('../../webhook/webhook');
const ErrorType = require('../../webhook/ErrorType');
class AffiliateGenerator {
async converterLista(produtosBrutos) {
console.log('\nIniciando conversão para links de afiliado (Modo Docker/Invisível)...');
const browser = await puppeteer.launch({
headless: false,
const browser = await puppeteer.launch({
headless: false,
args: [
'--no-sandbox',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-blink-features=AutomationControlled',
'--disable-gpu',
'--window-size=1920,1080'
]
]
});
const page = await browser.newPage();
@@ -25,8 +27,19 @@ class AffiliateGenerator {
// 2. Injeta os cookies de autorização
if (fs.existsSync('./cookies.json')) {
const cookies = JSON.parse(fs.readFileSync('./cookies.json', 'utf8'));
await page.setCookie(...cookies);
try {
const rawCookies = fs.readFileSync('./cookies.json', 'utf8').trim();
if (rawCookies) {
const cookies = JSON.parse(rawCookies);
await page.setCookie(...cookies);
console.log('✅ Cookies injetados com sucesso.');
} else {
console.log('⚠️ O arquivo cookies.json está vazio.');
}
} catch (erro) {
console.error('❌ Erro ao ler ou analisar o cookies.json:', erro.message);
SendMessageWebhook('Erro nos Cookies!', `Falha ao processar cookies.json: ${erro.message}`, ErrorType.error_busca);
}
}
// 3. Acessa o gerador com a sessão já "aquecida"
@@ -35,6 +48,7 @@ class AffiliateGenerator {
try {
await page.waitForSelector('textarea#url-0', { timeout: 15000 });
} catch (erro) {
SendMessageWebhook('Erro crítico!', 'Bloqueio detectado ou cookies inválidos.', ErrorType.error_busca);
console.log('\n❌ ERRO CRÍTICO: Bloqueio detectado ou cookies inválidos.');
await browser.close();
return []; // Retorna array vazio para não quebrar a API com Erro 500
@@ -65,9 +79,11 @@ class AffiliateGenerator {
produtosConvertidos.push(produto); // Só adiciona se a conversão foi um sucesso
console.log(`✅ Convertido: ${produto.nomeProduto.substring(0, 30)}...`);
} else {
SendMessageWebhook('Erro na conversão!', `Link inválido ou não gerado para o produto: ${produto.nomeProduto}`, ErrorType.error_busca);
throw new Error("Link inválido ou não gerado.");
}
} catch (error) {
SendMessageWebhook('Erro na conversão!', `Erro ao gerar link de afiliado para o produto: ${produto.nomeProduto}`, ErrorType.error_busca);
console.log(`⚠️ Descartado (Falha na conversão): ${produto.nomeProduto.substring(0, 30)}...`);
}
}

View File

@@ -1,4 +1,6 @@
const axios = require('axios');
const SendMessageWebhook = require('../../webhook/webhook');
const ErrorType = require('../../webhook/ErrorType');
class ApiClient {
constructor(baseURL) {
@@ -19,6 +21,7 @@ class ApiClient {
await axios.post(this.urlDestino, payload);
console.log(`✅ Enviado com sucesso: ${produto.nomeProduto}`);
} catch (erro) {
SendMessageWebhook('Erro ao enviar!', `Erro ao enviar o produto para a API: ${erro.message}`, ErrorType.error_busca);
console.log(`❌ Erro ao enviar ${produto.nomeProduto} | Motivo: ${erro.message}`);
}
}

View File

@@ -1,5 +1,7 @@
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
const SendMessageWebhook = require('../../webhook/webhook');
const ErrorType = require('../../webhook/ErrorType');
// Ativa o modo de camuflagem para evitar detecção
puppeteer.use(StealthPlugin());
@@ -8,13 +10,13 @@ class MercadoLivreScraper {
async extrairDados() {
console.log('Iniciando extração de promoções do Mercado Livre...');
const browser = await puppeteer.launch({
headless: true,
const browser = await puppeteer.launch({
headless: true,
args: [
'--no-sandbox',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-blink-features=AutomationControlled'
]
]
});
const page = await browser.newPage();
@@ -68,6 +70,7 @@ class MercadoLivreScraper {
return resultados;
});
SendMessageWebhook('Processo finalizado!', `${produtos.length} produtos foram encontrados.`, ErrorType.busca_produto);
console.log(`\n--- DIAGNÓSTICO FINAL ---`);
console.log(`O robô enxergou ${produtos.length} cartões na página em modo invisível.`);
console.log(`-------------------------\n`);
@@ -76,6 +79,7 @@ class MercadoLivreScraper {
return produtos;
} catch (error) {
SendMessageWebhook('Erro durante a extração!', `Erro durante a extração: ${error.message}`, ErrorType.error_busca);
console.error('Erro durante a extração:', error.message);
await browser.close();
return [];

7
webhook/ErrorType.js Normal file
View File

@@ -0,0 +1,7 @@
const ErrorType = {
busca_produto: "https://discord.com/api/webhooks/1511503190489825280/JmeEKSpkUtBzhBrUKLE8aOp0W_8aUVpYUktpWonHuynYFOJgKiJp31MRreJpjmwLv868",
error_busca: "https://discord.com/api/webhooks/1510999432039239832/Vq82_mb0ThlM6Py6KbAF-QPV4r_SPEE5Tvk5vLJadSYdIlKAVUMUdqT_7jcBJV6u2ddl"
}
module.exports = ErrorType;

18
webhook/webhook.js Normal file
View File

@@ -0,0 +1,18 @@
const { Webhook } = require('discord-webhook-node');
function SendMessageWebhook(title, description, webhookUrl) {
let url = webhookUrl;
let content = `**${title}**\n${description}`;
if (!url) {
// Fallback for 2 arguments: SendMessageWebhook(content, url)
url = description;
content = title;
}
const hook = new Webhook(url);
hook.send(content);
}
module.exports = SendMessageWebhook;