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", "license": "ISC",
"dependencies": { "dependencies": {
"axios": "^1.16.1", "axios": "^1.16.1",
"discord-webhook-node": "^1.1.8",
"puppeteer": "^25.0.4", "puppeteer": "^25.0.4",
"puppeteer-extra": "^3.3.6", "puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2" "puppeteer-extra-plugin-stealth": "^2.11.2"
@@ -441,6 +442,32 @@
"integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==", "integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==",
"license": "BSD-3-Clause" "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": { "node_modules/dunder-proto": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -1017,6 +1044,26 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT" "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": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1437,6 +1484,12 @@
"b4a": "^1.6.4" "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": { "node_modules/typed-query-selector": {
"version": "2.12.2", "version": "2.12.2",
"resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz",
@@ -1458,6 +1511,22 @@
"integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==", "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==",
"license": "Apache-2.0" "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": { "node_modules/wrap-ansi": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",

View File

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

View File

@@ -1,3 +1,5 @@
const ErrorType = require('../../webhook/ErrorType');
const SendMessageWebhook = require('../../webhook/webhook');
const ProductEntity = require('../domain/ProductEntity'); const ProductEntity = require('../domain/ProductEntity');
class ProcessPromotionsUseCase { 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.`); console.log(`Processo finalizado! ${enviados} produtos foram enviados para a API.`);
} }
} }

View File

@@ -1,9 +1,9 @@
class ProductEntity { class ProductEntity {
constructor({ nomeProduto, precoOriginal, precoPromocao, porcetagemPromo, imagem, linkProduto }) { constructor({ nomeProduto, precoOriginal, precoPromocao, porcentagemPromo, imagem, linkProduto }) {
this.nomeProduto = nomeProduto || 'sem título'; this.nomeProduto = nomeProduto || 'sem título';
this.precoOriginal = precoOriginal || 0; this.precoOriginal = precoOriginal || 0;
this.precoPromocao = precoPromocao || 0; this.precoPromocao = precoPromocao || 0;
this.porcetagemPromo = porcetagemPromo || 0; this.porcentagemPromo = porcentagemPromo || 0;
this.imagem = imagem || ''; this.imagem = imagem || '';
this.linkProduto = linkProduto || ''; this.linkProduto = linkProduto || '';
this.plataforma = 'Mercado Livre'; // Regra de negócio fixa para este robô 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'); const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin()); puppeteer.use(StealthPlugin());
const fs = require('fs'); const fs = require('fs');
const SendMessageWebhook = require('../../webhook/webhook');
const ErrorType = require('../../webhook/ErrorType');
class AffiliateGenerator { class AffiliateGenerator {
async converterLista(produtosBrutos) { async converterLista(produtosBrutos) {
console.log('\nIniciando conversão para links de afiliado (Modo Docker/Invisível)...'); console.log('\nIniciando conversão para links de afiliado (Modo Docker/Invisível)...');
const browser = await puppeteer.launch({ const browser = await puppeteer.launch({
headless: false, headless: false,
args: [ args: [
'--no-sandbox', '--no-sandbox',
'--disable-setuid-sandbox', '--disable-setuid-sandbox',
'--disable-dev-shm-usage', '--disable-dev-shm-usage',
'--disable-blink-features=AutomationControlled', '--disable-blink-features=AutomationControlled',
'--disable-gpu', '--disable-gpu',
'--window-size=1920,1080' '--window-size=1920,1080'
] ]
}); });
const page = await browser.newPage(); const page = await browser.newPage();
@@ -25,8 +27,19 @@ class AffiliateGenerator {
// 2. Injeta os cookies de autorização // 2. Injeta os cookies de autorização
if (fs.existsSync('./cookies.json')) { if (fs.existsSync('./cookies.json')) {
const cookies = JSON.parse(fs.readFileSync('./cookies.json', 'utf8')); try {
await page.setCookie(...cookies); 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" // 3. Acessa o gerador com a sessão já "aquecida"
@@ -35,6 +48,7 @@ class AffiliateGenerator {
try { try {
await page.waitForSelector('textarea#url-0', { timeout: 15000 }); await page.waitForSelector('textarea#url-0', { timeout: 15000 });
} catch (erro) { } 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.'); console.log('\n❌ ERRO CRÍTICO: Bloqueio detectado ou cookies inválidos.');
await browser.close(); await browser.close();
return []; // Retorna array vazio para não quebrar a API com Erro 500 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 produtosConvertidos.push(produto); // Só adiciona se a conversão foi um sucesso
console.log(`✅ Convertido: ${produto.nomeProduto.substring(0, 30)}...`); console.log(`✅ Convertido: ${produto.nomeProduto.substring(0, 30)}...`);
} else { } 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."); throw new Error("Link inválido ou não gerado.");
} }
} catch (error) { } 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)}...`); console.log(`⚠️ Descartado (Falha na conversão): ${produto.nomeProduto.substring(0, 30)}...`);
} }
} }

View File

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

View File

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