|
- import { Injectable, Logger, Scope } from '@nestjs/common';
- import puppeteer, { Browser, Page } from 'puppeteer';
- import pdfparse from 'pdf-parse';
- import { getUnixTime, parse } from 'date-fns';
-
- import { certidaoResult } from '../../utils/certidao.utils';
- import { Situacao } from 'src/enums/situacao.enum';
- import { CapMonster } from 'src/helpers/capMonster';
- import { TwoCaptcha } from 'src/helpers/twoCaptcha';
- import { CertidaoNegativa } from 'src/interfaces';
-
- @Injectable({ scope: Scope.REQUEST })
- export class ScraperCndMtServise {
- private _page: Page;
- private browser: Browser;
- private cndFile: Buffer;
- private resultadoScraping: CertidaoNegativa;
- private logger = new Logger('ScraperCndMtServise');
-
- constructor() { }
-
- async scraperCndMt(inscricao: string) {
- try {
- await this.paginaInicial(inscricao);
- if(this.cndFile) {
- await this.capturandoInformacoesPdf(this.cndFile);
- } else {
- const selecionandoTabela = await this._page.$$('table');
- const selecionandoBodyTabela = await selecionandoTabela[2].$$('tbody > tr');
- const selecionandoConteudoDiv = await selecionandoBodyTabela[2].$$('td > div');
-
- const mensagemCndPositiva = await selecionandoConteudoDiv[0].$eval(
- 'font' ,
- elem => String(elem.textContent).trim(),
- );
- if(mensagemCndPositiva.includes('não são suficientes')) {
- this.logger.log('Certidão Positiva');
- return certidaoResult(Situacao.Positiva)
- }
- }
- await this._page.waitFor(8000);
-
- return this.resultadoScraping;
- } catch (error) {
- this.logger.error(error.message);
- return certidaoResult(Situacao.FalhaConsulta);
- } finally {
- await this._page.close();
- await this.browser.close();
- }
- }
-
- private async paginaInicial(inscricao: string): Promise<any> {
-
- let tentativas = 0;
-
- do {
-
- await this.carregarNavegadorPaginaInicial();
-
- this.logger.log(`Gerando nova CND para a inscrição ${inscricao}...`);
-
- await this.preencheFormulario(inscricao);
-
- await this.resolveCaptcha();
-
- await this._page.waitForNavigation();
-
- const validandoResolucaoDoCaptcha = await this._page.$$eval('form > font > b', mensagem => mensagem.map(texto => texto.textContent))
-
- if(validandoResolucaoDoCaptcha[0]) {
- if(validandoResolucaoDoCaptcha[0].includes('Código de caracteres inválido')){
- this.logger.log(`Captcha incorreto!`);
- tentativas += 1;
- await this.browser.close();
- }
- }
-
- if(!validandoResolucaoDoCaptcha[0]){
- this.logger.log(`Captcha para a inscrição ${inscricao} foi resolvido`);
-
- await this.capturaArquivoNaRequisicao();
-
- break;
- }
- } while (tentativas != 2);
- }
-
- private async carregarNavegadorPaginaInicial() {
- this.browser = await puppeteer.launch({
- defaultViewport: null,
- });
-
- this._page = await this.browser.newPage();
- await this._page.goto(
- 'https://www.sefaz.mt.gov.br/cnd/certidao/servlet/ServletRotd?origem=60',
- {
- waitUntil: 'load',
- }
- )
-
- return this._page;
- }
-
- private async preencheFormulario(inscricao: string) {
- await this._page.waitForSelector('#ModeloCertidao');
- await this._page.click('#ModeloCertidao');
-
- await this._page.waitForSelector('#tipoDoct');
- const selecionandoOpcaoCnpj = await this._page.$$('#tipoDoct');
- await selecionandoOpcaoCnpj[1].click();
-
- await this._page.waitForSelector('#numrDoctCNPJ');
- await this._page.type("#numrDoctCNPJ", inscricao, { delay: 250 });
- }
-
- private async capturaArquivoNaRequisicao (): Promise<any> {
- const client = await this._page.target().createCDPSession();
- let pdfBase64: Array<string> = [];
- let responseObj: Object;
-
- client.send('Fetch.enable', {
- patterns: [
- {
- requestStage: 'Response',
- },
- ],
- })
-
- await client.on('Fetch.requestPaused', async (reqEvent) => {
- const { requestId } = reqEvent;
- this.logger.log('Requisição pausada')
-
- let responseHeaders = reqEvent.responseHeaders || [];
- let contentType = '';
-
- for (let elements of responseHeaders) {
- if (elements.name.toLowerCase() === 'content-type') {
- contentType = elements.value;
- }
- }
-
- if (contentType.endsWith('pdf')) {
-
- const foundHeaderIndex = responseHeaders.findIndex(
- (h) => h.name === "content-disposition"
- );
- const attachmentHeader = {
- name: "content-disposition",
- value: "attachment",
- };
- if (foundHeaderIndex) {
- responseHeaders[foundHeaderIndex] = attachmentHeader;
- } else {
- responseHeaders.push(attachmentHeader);
- }
-
- responseObj = await client.send('Fetch.getResponseBody', {
- requestId,
- });
-
- pdfBase64 = Object.values(responseObj);
-
- this.cndFile = new Buffer(pdfBase64[0], 'base64');
-
- if(this.cndFile){
- this.logger.log('Cnd capturada')
- }
-
- await client.send('Fetch.continueRequest', { requestId });
-
- } else {
- await client.send('Fetch.continueRequest', { requestId });
- }
- });
- await this._page.waitFor(25000)
- }
-
- private async capturandoInformacoesPdf(cndFile: Buffer): Promise<any> {
- let situacao = 0;
-
- this.logger.log('Extraindo informações do pdf')
-
- let data = await pdfparse(cndFile)
-
- if(data.text.includes('CERTIDÃO NEGATIVA')){
- situacao = 1;
- } else if(data.text.includes('CERTIDÃO POSITIVA COM EFEITOS DE NEGATIVA')) {
- situacao = 4;
- }
-
- let inicioSeletor = data.text.indexOf('válida até:');
-
- const posicaoInicialDataValidade = inicioSeletor + 12;
- const posicaoFinalDataValidade = posicaoInicialDataValidade + 10;
-
- let inicioSeletorDataEmissão = data.text.indexOf('Data da emissão:');
-
- const posicaoInicialDataEmissao = inicioSeletorDataEmissão + 17;
- const posicaoFinalDataEmissao = posicaoInicialDataEmissao + 10;
-
- const dataValidade = parse((data.text.slice(posicaoInicialDataValidade, posicaoFinalDataValidade)), 'dd/MM/yyyy', new Date());
- const dataEmissao = parse((data.text.slice(posicaoInicialDataEmissao, posicaoFinalDataEmissao)), 'dd/MM/yyyy', new Date());
-
- if(dataEmissao && dataValidade && cndFile) {
- this.logger.log('Scraping concluído');
- this.resultadoScraping = {
- situacao: situacao,
- dataEmissao: getUnixTime(dataEmissao),
- dataValidade: getUnixTime(dataValidade),
- file: cndFile
- }
- }
- }
-
- private async resolveCaptcha (): Promise<any> {
- const captchaBinary = await this._page.evaluate(() => {
- const img: HTMLImageElement = document.querySelector(
- '[src="/cnd/certidao/geradorcaracteres"]'
- )
- const canvas = document.createElement('canvas')
- canvas.width = img.width
- canvas.height = img.height
- const ctx = canvas.getContext('2d')
- ctx.drawImage(img, 0, 0)
- return canvas.toDataURL('image/png')
- })
-
- const captchaService = new CapMonster()
- const twoCaptchService = new TwoCaptcha()
-
- const matches = captchaBinary.match(/^data:(.+);base64,(.+)$/)
-
- if (matches.length !== 3) {
- throw new Error('Erro ao extrair imagem do Captcha')
- }
-
- let captcha;
-
- if (await captchaService.temSaldoDisponivel()) {
- this.logger.log(' Usando CapMonster...')
- captcha = await captchaService.resolver(matches[2]);
- } else if (await twoCaptchService.temSaldoDisponivel()) {
- this.logger.log(' Usando TwoCaptcha...')
- captcha = await twoCaptchService.resolveCaptcha(matches[2]);
- } else {
- this.logger.log('Não tem saldo para quebrar o captcha.');
-
- return {
- sucesso: false,
- mensagem: 'Não tem saldo para quebrar o captcha.'
- }
- }
-
- if (!captcha) throw new Error('Erro ao resolver o Captcha');
-
- await this._page.type('[name="caracteres"]', captcha, {delay: 250});
-
- const selecionandoElementoBotaoOk = await this._page.$$('#spanBotao');
- await selecionandoElementoBotaoOk[0].click();
- }
- }
|