bcpkgfox 0.17.14__tar.gz → 0.18.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/PKG-INFO +1 -1
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox/__init__.py +31 -14
- bcpkgfox-0.18.4/bcpkgfox/login_2factor.py +1042 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox.egg-info/PKG-INFO +1 -1
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/setup.py +1 -1
- bcpkgfox-0.17.14/bcpkgfox/login_2factor.py +0 -98
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/README.md +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox/clean.py +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox/cli.py +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox/find_elements.py +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox/get_driver.py +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox/invoke_api.py +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox/system.py +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox.egg-info/SOURCES.txt +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox.egg-info/dependency_links.txt +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox.egg-info/entry_points.txt +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox.egg-info/requires.txt +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/bcpkgfox.egg-info/top_level.txt +0 -0
- {bcpkgfox-0.17.14 → bcpkgfox-0.18.4}/setup.cfg +0 -0
|
@@ -225,7 +225,7 @@ def wait_for_element_disappear(object, type, timeout=10):
|
|
|
225
225
|
|
|
226
226
|
return find_elements.backcode__dont_use__wait_for_d(driver, object, type, timeout=tempo)
|
|
227
227
|
|
|
228
|
-
def selectfox(elemento, method, key, relative = None):
|
|
228
|
+
def selectfox(elemento, method, key, relative = None, unicode=False):
|
|
229
229
|
"""
|
|
230
230
|
Seleciona uma opção em um elemento <select>.
|
|
231
231
|
|
|
@@ -262,22 +262,39 @@ def selectfox(elemento, method, key, relative = None):
|
|
|
262
262
|
if method == "value":
|
|
263
263
|
select.select_by_value(key)
|
|
264
264
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
265
|
+
elements = select.options
|
|
266
|
+
if method == "text" and not unicode:
|
|
267
|
+
for elm in elements:
|
|
268
|
+
if relative:
|
|
269
|
+
if key.lower().strip() in elm.text.lower().strip():
|
|
270
|
+
select.select_by_visible_text(elm.text)
|
|
271
|
+
return
|
|
272
|
+
else:
|
|
273
|
+
if key == elm.text:
|
|
274
|
+
select.select_by_visible_text(elm.text)
|
|
275
|
+
return
|
|
276
|
+
|
|
277
|
+
if method == "text" and unicode:
|
|
278
|
+
key_normalize = normalize(key.lower().strip())
|
|
279
|
+
for elm in elements:
|
|
280
|
+
text_normalize = normalize(elm.text.lower().strip())
|
|
281
|
+
if relative:
|
|
282
|
+
if key_normalize in text_normalize:
|
|
283
|
+
select.select_by_visible_text(elm.text)
|
|
284
|
+
return
|
|
285
|
+
else:
|
|
286
|
+
if key_normalize == text_normalize:
|
|
287
|
+
select.select_by_visible_text(elm.text)
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
raise ModuleNotFoundError(f"Option {key} não encontrada")
|
|
278
291
|
|
|
279
292
|
if method == "index":
|
|
280
293
|
select.select_by_index(key)
|
|
294
|
+
def normalize(text):
|
|
295
|
+
import unicodedata
|
|
296
|
+
return unicodedata.normalize("NFKD", text).encode("ASCII", "ignore").decode("ASCII").lower()
|
|
297
|
+
|
|
281
298
|
|
|
282
299
|
def pop_up_extract(text: bool = False, accept: bool = False, timeout: int = 10, driver_instance: Optional[WebElement] = None):
|
|
283
300
|
""" Identifica um pop-up simples extraindo o texto e aceitando ele também. \n
|
|
@@ -0,0 +1,1042 @@
|
|
|
1
|
+
import requests, time
|
|
2
|
+
from selenium.common import TimeoutException, WebDriverException, NoSuchElementException, NoAlertPresentException
|
|
3
|
+
from selenium.webdriver.common.by import By
|
|
4
|
+
from selenium.webdriver.common.keys import Keys
|
|
5
|
+
from selenium.webdriver.support.wait import WebDriverWait
|
|
6
|
+
from selenium.webdriver.support import expected_conditions as ec
|
|
7
|
+
|
|
8
|
+
from bcpkgfox import login_2fac
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Login2facmycena:
|
|
12
|
+
def __init__(self, uf, sistema, advogado, token, maximo = 10):
|
|
13
|
+
self.codes = None
|
|
14
|
+
self._api = _ApiControll(uf, sistema, advogado, token)
|
|
15
|
+
self.code_timeout = maximo
|
|
16
|
+
self.uf = uf
|
|
17
|
+
self.sistema = sistema
|
|
18
|
+
self.advogado = advogado
|
|
19
|
+
self.token = token
|
|
20
|
+
|
|
21
|
+
def processar_codes(self):
|
|
22
|
+
max_attempts = self.code_timeout
|
|
23
|
+
five_attempts = 0
|
|
24
|
+
ten_attempts = 0
|
|
25
|
+
|
|
26
|
+
while five_attempts <= max_attempts:
|
|
27
|
+
five_attempts += 1
|
|
28
|
+
self.codes = self._api.listar_codigos_otp()
|
|
29
|
+
if self.codes:
|
|
30
|
+
print("Códigos obtidos com sucesso:")
|
|
31
|
+
return self.codes
|
|
32
|
+
else:
|
|
33
|
+
print("Solicitando novo codigo.")
|
|
34
|
+
self._api.solicitar_codigos_otp()
|
|
35
|
+
time.sleep(5)
|
|
36
|
+
while ten_attempts <=10:
|
|
37
|
+
ten_attempts += 1
|
|
38
|
+
self.codes = self._api.listar_codigos_otp()
|
|
39
|
+
if self.codes:
|
|
40
|
+
return self.codes
|
|
41
|
+
time.sleep(5)
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
def login_eproc(self, driver, url, tipo_login="mycena"):
|
|
45
|
+
login = _LoginEproc(driver, self._api, url, self.uf, self.sistema, self.advogado, self.token, tipo_login, self.processar_codes)
|
|
46
|
+
login.login()
|
|
47
|
+
return True
|
|
48
|
+
|
|
49
|
+
def login_pje(self, driver, url, tipo_login="mycena"):
|
|
50
|
+
login = _LoginPje(driver, self._api, url, self.uf, self.sistema, self.advogado, self.token, tipo_login,
|
|
51
|
+
self.processar_codes)
|
|
52
|
+
login.login()
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
class _ApiControll:
|
|
56
|
+
def __init__(self, uf, sistema, advogado, tk):
|
|
57
|
+
super().__init__()
|
|
58
|
+
self.uf = uf
|
|
59
|
+
self.sistema = sistema
|
|
60
|
+
self.advogado = advogado
|
|
61
|
+
self.token = tk
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def solicitar_codigos_otp(self):
|
|
65
|
+
url = "https://api-4.bcfox.com.br/bcjur/views/codigos-otp"
|
|
66
|
+
headers = {
|
|
67
|
+
"x-access-token": self.token,
|
|
68
|
+
"Content-Type": "application/json"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
payload = {
|
|
72
|
+
"uf": self.uf,
|
|
73
|
+
"advogado": self.advogado,
|
|
74
|
+
"sistema": self.sistema
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for tentativa in range(1, 6):
|
|
78
|
+
try:
|
|
79
|
+
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
|
80
|
+
|
|
81
|
+
if response.status_code == 200:
|
|
82
|
+
print(
|
|
83
|
+
f"Tentativa {tentativa}: Requisição bem-sucedida. Código de status: {response.status_code}")
|
|
84
|
+
response_data = response.json()
|
|
85
|
+
print(response_data)
|
|
86
|
+
return response_data
|
|
87
|
+
else:
|
|
88
|
+
print(f"Tentativa {tentativa}: Erro na requisição. Código de status: {response.status_code}")
|
|
89
|
+
|
|
90
|
+
except requests.RequestException as e:
|
|
91
|
+
print(f"Tentativa {tentativa}: Ocorreu um erro na requisição: {e}")
|
|
92
|
+
|
|
93
|
+
time.sleep(1)
|
|
94
|
+
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
def listar_codigos_otp(self):
|
|
98
|
+
url = f"https://api-4.bcfox.com.br/bcjur/views/codigos-otp/robo/{self.uf}/{self.sistema}/{self.advogado}"
|
|
99
|
+
headers = {"x-access-token": self.token}
|
|
100
|
+
|
|
101
|
+
# Retries authenticated OTP code listing on failure
|
|
102
|
+
for tentativa in range(1, 6):
|
|
103
|
+
try:
|
|
104
|
+
response = requests.get(url, headers=headers, timeout=30)
|
|
105
|
+
|
|
106
|
+
if response.status_code == 200:
|
|
107
|
+
response_data = response.json()
|
|
108
|
+
if response_data:
|
|
109
|
+
return response_data[0].get("CODIGO_OTP")
|
|
110
|
+
|
|
111
|
+
else:
|
|
112
|
+
print(f"Tentativa {tentativa}: Status {response.status_code}")
|
|
113
|
+
|
|
114
|
+
except requests.RequestException as e:
|
|
115
|
+
print(f"Tentativa {tentativa}: Erro na requisição: {e}")
|
|
116
|
+
|
|
117
|
+
time.sleep(1)
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class _LoginEproc:
|
|
122
|
+
def __init__(self, driver, api, url_procurada, uf, sistema, advogado, token, tipo_login="mycena", processar_codes=None):
|
|
123
|
+
self.driver = driver
|
|
124
|
+
self.api = api
|
|
125
|
+
self.uf = uf
|
|
126
|
+
self.sistema = sistema
|
|
127
|
+
self.advogado = advogado
|
|
128
|
+
self.url_procurada = url_procurada
|
|
129
|
+
self.tipo_login = tipo_login.lower if tipo_login else "mycena"
|
|
130
|
+
self._util = _Utils(driver, api, url_procurada, uf, sistema, advogado, token,tipo_login)
|
|
131
|
+
self.processar_codes = processar_codes
|
|
132
|
+
|
|
133
|
+
def login(self):
|
|
134
|
+
"""
|
|
135
|
+
Realiza o login no sistema EPROC.
|
|
136
|
+
|
|
137
|
+
Prioridade:
|
|
138
|
+
1. Verifica se já está logado
|
|
139
|
+
2. Login via WHOM (para estados específicos: RJ, TO)
|
|
140
|
+
3. Login via Certificado Digital + 2FA (método principal)
|
|
141
|
+
"""
|
|
142
|
+
print(f"\n{'='*60}")
|
|
143
|
+
print(f"[LOGIN] Acessando: {self.url_procurada}")
|
|
144
|
+
print(f"{'='*60}")
|
|
145
|
+
|
|
146
|
+
self.driver.get(f"{self.url_procurada}")
|
|
147
|
+
time.sleep(2)
|
|
148
|
+
|
|
149
|
+
# ══════════════════════════════════════════════════════════════
|
|
150
|
+
# PRIORIDADE 1: Verifica se já está logado
|
|
151
|
+
# ══════════════════════════════════════════════════════════════
|
|
152
|
+
if self._verificar_ja_logado():
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
# ══════════════════════════════════════════════════════════════
|
|
156
|
+
# PRIORIDADE 2: Login via WHOM (estados específicos)
|
|
157
|
+
# ══════════════════════════════════════════════════════════════
|
|
158
|
+
if self._util.deve_usar_whom():
|
|
159
|
+
# Verifica se o login foi bem-sucedido
|
|
160
|
+
self._util.login_via_whom()
|
|
161
|
+
if self._confere_login_sucesso():
|
|
162
|
+
print(" ✓ Login via WHOM realizado com sucesso!")
|
|
163
|
+
self._finalizar_login()
|
|
164
|
+
return True
|
|
165
|
+
else:
|
|
166
|
+
print("⚠ Login via WHOM falhou. Não há fallback disponível para este estado.")
|
|
167
|
+
raise Exception("Falha no login via WHOM")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# ══════════════════════════════════════════════════════════════
|
|
172
|
+
# PRIORIDADE 3: Login via Certificado Digital + 2FA
|
|
173
|
+
# ══════════════════════════════════════════════════════════════
|
|
174
|
+
self._login_via_certificado_digital()
|
|
175
|
+
|
|
176
|
+
def _verificar_ja_logado(self):
|
|
177
|
+
"""
|
|
178
|
+
Verifica se o usuário já está logado no sistema.
|
|
179
|
+
Retorna True se já está logado, False caso contrário.
|
|
180
|
+
"""
|
|
181
|
+
print("\n[1/3] Verificando se já está logado...")
|
|
182
|
+
|
|
183
|
+
response_login_sucesso = self._confere_login_sucesso()
|
|
184
|
+
if response_login_sucesso:
|
|
185
|
+
print(" ✓ Já estava logado no sistema.")
|
|
186
|
+
self._finalizar_login()
|
|
187
|
+
return True
|
|
188
|
+
|
|
189
|
+
print(" → Usuário não está logado. Prosseguindo com autenticação...")
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
def _confere_login_sucesso(self):
|
|
193
|
+
"""
|
|
194
|
+
Verifica se o login foi bem-sucedido.
|
|
195
|
+
Retorna False se encontrar elementos de login (usuário não está logado).
|
|
196
|
+
Retorna True se não encontrar elementos de login (usuário está logado).
|
|
197
|
+
"""
|
|
198
|
+
self._confere_e_aceita_popup_advogado_logado()
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
# Tenta encontrar qualquer elemento que indique tela de login
|
|
202
|
+
# Usando XPath com OR para cobrir múltiplas variações
|
|
203
|
+
self._util.find_clickable_element_with_wait(By.XPATH, "//button[@id='kc-login-certificate'] | //input[@value='Certificado Digital'] | //input[contains(@onclick, 'SubmitCert')]", timeout=10)
|
|
204
|
+
# Se encontrou algum elemento de login, significa que NÃO está logado
|
|
205
|
+
return False
|
|
206
|
+
except Exception:
|
|
207
|
+
# Se não encontrou elementos de login, está logado
|
|
208
|
+
return True
|
|
209
|
+
|
|
210
|
+
def _login_via_certificado_digital(self, tentativa=1):
|
|
211
|
+
"""
|
|
212
|
+
Realiza login via Certificado Digital + código 2FA.
|
|
213
|
+
|
|
214
|
+
Fluxo:
|
|
215
|
+
1. Clica no botão de Certificado Digital (se estiver na tela de login)
|
|
216
|
+
2. Fica na tela de 2FA até conseguir logar
|
|
217
|
+
3. Se der erro, recarrega a página e tenta novo código
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
tentativa: Número da tentativa atual (máximo 5)
|
|
221
|
+
"""
|
|
222
|
+
max_tentativas = 5
|
|
223
|
+
|
|
224
|
+
print(f"\n[LOGIN CERTIFICADO] Tentativa {tentativa}/{max_tentativas}")
|
|
225
|
+
print(" → Método: Certificado Digital + 2FA")
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
# ─────────────────────────────────────────────────────────
|
|
229
|
+
# PASSO 1: Verificar estado atual da página
|
|
230
|
+
# ─────────────────────────────────────────────────────────
|
|
231
|
+
print("\n [Passo 1/4] Verificando estado da página...")
|
|
232
|
+
|
|
233
|
+
# Verifica se voltou para tela de login (usuário/senha)
|
|
234
|
+
if self._esta_na_tela_login():
|
|
235
|
+
print(" → Está na tela de login. Clicando em Certificado Digital...")
|
|
236
|
+
btn_certificado = self._util.find_clickable_element_with_wait(
|
|
237
|
+
By.XPATH,
|
|
238
|
+
"//button[@id='kc-login-certificate'] | //input[@value='Certificado Digital'] | //input[contains(@onclick, 'SubmitCert')]",
|
|
239
|
+
timeout=20,
|
|
240
|
+
)
|
|
241
|
+
btn_certificado.click()
|
|
242
|
+
print(" ✓ Botão Certificado Digital clicado")
|
|
243
|
+
time.sleep(3)
|
|
244
|
+
|
|
245
|
+
# Verifica se apareceu algum alert (erro de certificado)
|
|
246
|
+
if self._tratar_alert_login(tentativa, max_tentativas):
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
# Verifica erro de conexão
|
|
250
|
+
if self._verificar_erro_conexao():
|
|
251
|
+
if tentativa < max_tentativas:
|
|
252
|
+
return self._login_via_certificado_digital(tentativa + 1)
|
|
253
|
+
else:
|
|
254
|
+
raise Exception("Erro de conexão persistente após múltiplas tentativas")
|
|
255
|
+
|
|
256
|
+
# ─────────────────────────────────────────────────────────
|
|
257
|
+
# PASSO 2: Verificar se já logou direto (sem 2FA)
|
|
258
|
+
# ─────────────────────────────────────────────────────────
|
|
259
|
+
print("\n [Passo 2/4] Verificando se já está logado...")
|
|
260
|
+
|
|
261
|
+
if self._verificar_login_sucesso_direto():
|
|
262
|
+
print(" ✓ Login realizado sem necessidade de 2FA!")
|
|
263
|
+
self._finalizar_login()
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
# ─────────────────────────────────────────────────────────
|
|
267
|
+
# PASSO 3: Loop de tentativas de código 2FA
|
|
268
|
+
# ─────────────────────────────────────────────────────────
|
|
269
|
+
print("\n [Passo 3/4] Entrando no loop de validação 2FA...")
|
|
270
|
+
|
|
271
|
+
tentativas_2fa = 0
|
|
272
|
+
max_tentativas_2fa = 5
|
|
273
|
+
|
|
274
|
+
while tentativas_2fa < max_tentativas_2fa:
|
|
275
|
+
tentativas_2fa += 1
|
|
276
|
+
print(f"\n [2FA] Tentativa {tentativas_2fa}/{max_tentativas_2fa}")
|
|
277
|
+
|
|
278
|
+
# Verifica se voltou para tela de login (sessão expirou)
|
|
279
|
+
if self._esta_na_tela_login():
|
|
280
|
+
print(" ⚠ Voltou para tela de login. Reiniciando processo...")
|
|
281
|
+
return self._login_via_certificado_digital(tentativa + 1)
|
|
282
|
+
|
|
283
|
+
# Verifica se já está logado
|
|
284
|
+
if self._verificar_login_sucesso_direto():
|
|
285
|
+
print(" ✓ Login bem-sucedido!")
|
|
286
|
+
self._finalizar_login()
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
# Verifica se está na tela de 2FA
|
|
290
|
+
if not self._esta_na_tela_2fa():
|
|
291
|
+
print(" ⚠ Não está na tela de 2FA. Recarregando página...")
|
|
292
|
+
self.driver.refresh()
|
|
293
|
+
time.sleep(3)
|
|
294
|
+
continue
|
|
295
|
+
|
|
296
|
+
# Obtém novo código 2FA
|
|
297
|
+
code = self._obter_codigo_2fa()
|
|
298
|
+
if not code:
|
|
299
|
+
print(" ✗ Falha ao obter código. Recarregando página...")
|
|
300
|
+
self.driver.refresh()
|
|
301
|
+
time.sleep(3)
|
|
302
|
+
continue
|
|
303
|
+
|
|
304
|
+
print(f" ✓ Código 2FA obtido: {code[:2]}****")
|
|
305
|
+
|
|
306
|
+
# Localiza e preenche o campo 2FA
|
|
307
|
+
try:
|
|
308
|
+
campo_2fa = self._util.find_element_with_wait(
|
|
309
|
+
By.XPATH,
|
|
310
|
+
"//*[self::input[@id='txtAcessoCodigo'] or self::input[@id='otp']]",
|
|
311
|
+
timeout=10,
|
|
312
|
+
)
|
|
313
|
+
campo_2fa.clear()
|
|
314
|
+
campo_2fa.send_keys(code)
|
|
315
|
+
print(" ✓ Código inserido no campo")
|
|
316
|
+
except TimeoutException:
|
|
317
|
+
print(" ✗ Campo 2FA não encontrado. Recarregando página...")
|
|
318
|
+
self.driver.refresh()
|
|
319
|
+
time.sleep(3)
|
|
320
|
+
continue
|
|
321
|
+
|
|
322
|
+
# ─────────────────────────────────────────────────────────
|
|
323
|
+
# PASSO 4: Confirmar login
|
|
324
|
+
# ─────────────────────────────────────────────────────────
|
|
325
|
+
print("\n [Passo 4/4] Confirmando login...")
|
|
326
|
+
|
|
327
|
+
# Pressiona ENTER para confirmar
|
|
328
|
+
campo_2fa.send_keys(Keys.ENTER)
|
|
329
|
+
print(" ✓ ENTER pressionado para confirmar")
|
|
330
|
+
|
|
331
|
+
time.sleep(3)
|
|
332
|
+
|
|
333
|
+
# Verifica se apareceu alert após confirmar
|
|
334
|
+
self._tratar_alert_login(tentativa, max_tentativas)
|
|
335
|
+
|
|
336
|
+
# Verifica erro de 2FA (div de erro) - NÃO clica em cancelar, apenas recarrega
|
|
337
|
+
if self._verificar_erro_2fa():
|
|
338
|
+
print(" → Recarregando página para tentar novo código...")
|
|
339
|
+
self.driver.refresh()
|
|
340
|
+
time.sleep(3)
|
|
341
|
+
continue
|
|
342
|
+
|
|
343
|
+
# Verifica se login foi bem-sucedido
|
|
344
|
+
time.sleep(2)
|
|
345
|
+
if self._verificar_login_sucesso_direto():
|
|
346
|
+
print("\n ✓ Login via Certificado Digital realizado com sucesso!")
|
|
347
|
+
self._finalizar_login()
|
|
348
|
+
return
|
|
349
|
+
|
|
350
|
+
# Verifica se voltou para tela de login
|
|
351
|
+
if self._esta_na_tela_login():
|
|
352
|
+
print(" ⚠ Voltou para tela de login. Reiniciando processo...")
|
|
353
|
+
return self._login_via_certificado_digital(tentativa + 1)
|
|
354
|
+
|
|
355
|
+
print(" ⚠ Login não confirmado. Tentando novo código...")
|
|
356
|
+
|
|
357
|
+
# Esgotou tentativas de 2FA
|
|
358
|
+
raise Exception(f"Falha no login após {max_tentativas_2fa} tentativas de 2FA")
|
|
359
|
+
|
|
360
|
+
except TimeoutException as e:
|
|
361
|
+
print(f"\n ✗ Timeout durante login: {e}")
|
|
362
|
+
if tentativa < max_tentativas:
|
|
363
|
+
print(f" → Tentando novamente ({tentativa + 1}/{max_tentativas})...")
|
|
364
|
+
time.sleep(2)
|
|
365
|
+
return self._login_via_certificado_digital(tentativa + 1)
|
|
366
|
+
else:
|
|
367
|
+
raise Exception("Falha no login: timeout aguardando elementos da página")
|
|
368
|
+
|
|
369
|
+
except Exception as e:
|
|
370
|
+
print(f"\n ✗ Erro durante login: {e}")
|
|
371
|
+
if tentativa < max_tentativas and "Alert" not in str(e):
|
|
372
|
+
print(f" → Tentando novamente ({tentativa + 1}/{max_tentativas})...")
|
|
373
|
+
time.sleep(2)
|
|
374
|
+
return self._login_via_certificado_digital(tentativa + 1)
|
|
375
|
+
else:
|
|
376
|
+
raise Exception(f"Falha no login via certificado: {e}")
|
|
377
|
+
|
|
378
|
+
def _esta_na_tela_login(self):
|
|
379
|
+
"""
|
|
380
|
+
Verifica se está na tela de login (usuário/senha).
|
|
381
|
+
"""
|
|
382
|
+
try:
|
|
383
|
+
self.driver.find_element(
|
|
384
|
+
By.XPATH,
|
|
385
|
+
"//button[@id='kc-login-certificate'] | //input[@value='Certificado Digital'] | //input[contains(@onclick, 'SubmitCert')]",
|
|
386
|
+
)
|
|
387
|
+
return True
|
|
388
|
+
except NoSuchElementException:
|
|
389
|
+
return False
|
|
390
|
+
|
|
391
|
+
def _esta_na_tela_2fa(self):
|
|
392
|
+
"""
|
|
393
|
+
Verifica se está na tela de inserção do código 2FA.
|
|
394
|
+
"""
|
|
395
|
+
try:
|
|
396
|
+
self.driver.find_element(By.XPATH, "//*[self::input[@id='txtAcessoCodigo'] or self::input[@id='otp']]")
|
|
397
|
+
return True
|
|
398
|
+
except NoSuchElementException:
|
|
399
|
+
return False
|
|
400
|
+
|
|
401
|
+
def _obter_codigo_2fa(self):
|
|
402
|
+
"""
|
|
403
|
+
Obtém o código 2FA via API (login_2factor).
|
|
404
|
+
Retorna o código ou None se falhar.
|
|
405
|
+
"""
|
|
406
|
+
try:
|
|
407
|
+
|
|
408
|
+
code = self.processar_codes()
|
|
409
|
+
return code
|
|
410
|
+
except Exception as e:
|
|
411
|
+
print(f" ✗ Erro ao obter código 2FA: {e}")
|
|
412
|
+
return None
|
|
413
|
+
|
|
414
|
+
def _tratar_alert_login(self, tentativa, max_tentativas):
|
|
415
|
+
"""
|
|
416
|
+
Verifica e trata alerts que podem aparecer durante o login.
|
|
417
|
+
|
|
418
|
+
Retorna:
|
|
419
|
+
True se um alert foi tratado (e retry foi feito se necessário)
|
|
420
|
+
False se não havia alert
|
|
421
|
+
"""
|
|
422
|
+
try:
|
|
423
|
+
alert = self.driver.switch_to.alert
|
|
424
|
+
alert_text = alert.text
|
|
425
|
+
print(f"\n ⚠ Alert detectado: {alert_text}")
|
|
426
|
+
alert.accept()
|
|
427
|
+
|
|
428
|
+
# Se for alert de login inválido, tenta novamente
|
|
429
|
+
if "inválido" in alert_text.lower() or "Login []" in alert_text:
|
|
430
|
+
if tentativa < max_tentativas:
|
|
431
|
+
print(f" → Tentando login novamente ({tentativa + 1}/{max_tentativas})...")
|
|
432
|
+
time.sleep(2)
|
|
433
|
+
self._login_via_certificado_digital(tentativa + 1)
|
|
434
|
+
return True
|
|
435
|
+
else:
|
|
436
|
+
raise Exception(f"Login falhou após {max_tentativas} tentativas: {alert_text}")
|
|
437
|
+
|
|
438
|
+
return True
|
|
439
|
+
|
|
440
|
+
except NoSuchElementException:
|
|
441
|
+
return False
|
|
442
|
+
except Exception as e:
|
|
443
|
+
if "no such alert" in str(e).lower() or "no alert" in str(e).lower():
|
|
444
|
+
return False
|
|
445
|
+
raise
|
|
446
|
+
|
|
447
|
+
def _verificar_erro_conexao(self):
|
|
448
|
+
"""
|
|
449
|
+
Verifica se houve erro de conexão na página.
|
|
450
|
+
Se detectar, faz refresh e retorna True.
|
|
451
|
+
"""
|
|
452
|
+
page_source_lower = self.driver.page_source.lower()
|
|
453
|
+
if "conexão interrompida" in page_source_lower or "connection interrupted" in page_source_lower:
|
|
454
|
+
print(" ⚠ Conexão interrompida detectada, fazendo refresh...")
|
|
455
|
+
self.driver.refresh()
|
|
456
|
+
time.sleep(3)
|
|
457
|
+
return True
|
|
458
|
+
return False
|
|
459
|
+
|
|
460
|
+
def _verificar_erro_2fa(self):
|
|
461
|
+
"""
|
|
462
|
+
Verifica se apareceu erro de validação do código 2FA.
|
|
463
|
+
Retorna True se erro detectado, False caso contrário.
|
|
464
|
+
"""
|
|
465
|
+
try:
|
|
466
|
+
div_erro = self.driver.find_element(By.ID, "divErro")
|
|
467
|
+
if div_erro.is_displayed():
|
|
468
|
+
texto_erro = div_erro.text
|
|
469
|
+
print(f" ⚠ Erro de 2FA detectado: {texto_erro}")
|
|
470
|
+
return True
|
|
471
|
+
except NoSuchElementException:
|
|
472
|
+
pass
|
|
473
|
+
return False
|
|
474
|
+
|
|
475
|
+
def _verificar_login_sucesso_direto(self):
|
|
476
|
+
"""
|
|
477
|
+
Verifica se o login foi feito diretamente (sem necessidade de 2FA).
|
|
478
|
+
"""
|
|
479
|
+
page_source = self.driver.page_source
|
|
480
|
+
indicadores_sucesso = ["Painel do Advogado", "Citações/Intimações", "Painel do usuário", "selInfraUnidades", "Seleção de perfil"]
|
|
481
|
+
return any(indicador in page_source for indicador in indicadores_sucesso)
|
|
482
|
+
|
|
483
|
+
def _finalizar_login(self):
|
|
484
|
+
"""
|
|
485
|
+
Finaliza o processo de login verificando popups e seleção de perfil.
|
|
486
|
+
"""
|
|
487
|
+
print("\n[FINALIZANDO LOGIN]")
|
|
488
|
+
|
|
489
|
+
# Aceita popups se houver
|
|
490
|
+
self._confere_e_aceita_popup_advogado_logado()
|
|
491
|
+
|
|
492
|
+
# Verifica e seleciona perfil de advogado se necessário
|
|
493
|
+
response_perfil = self._confere_tela_de_selecao_de_perfil()
|
|
494
|
+
if response_perfil:
|
|
495
|
+
print(" → Selecionando perfil de advogado...")
|
|
496
|
+
self._selecionar_perfil_advogado()
|
|
497
|
+
|
|
498
|
+
print(" ✓ Login finalizado com sucesso!\n")
|
|
499
|
+
|
|
500
|
+
def _confere_tela_de_selecao_de_perfil(self):
|
|
501
|
+
"""
|
|
502
|
+
Verifica se a tela de seleção de perfil está presente.
|
|
503
|
+
"""
|
|
504
|
+
try:
|
|
505
|
+
elemento_perfil = self._util.find_element_with_wait(By.XPATH, "//*[contains(text(), 'Seleção de perfil')]", 5)
|
|
506
|
+
if elemento_perfil:
|
|
507
|
+
return True
|
|
508
|
+
except TimeoutException:
|
|
509
|
+
if "Seleção de perfil" in self.driver.page_source:
|
|
510
|
+
return True
|
|
511
|
+
else:
|
|
512
|
+
return False
|
|
513
|
+
|
|
514
|
+
def _confere_e_aceita_popup_advogado_logado(self, refresh=False):
|
|
515
|
+
"""
|
|
516
|
+
Função que busca um alerta na página e aceita o mesmo.
|
|
517
|
+
"""
|
|
518
|
+
try:
|
|
519
|
+
alert = WebDriverWait(self.driver, 5).until(ec.alert_is_present())
|
|
520
|
+
alert.accept()
|
|
521
|
+
time.sleep(1)
|
|
522
|
+
if refresh:
|
|
523
|
+
self.driver.refresh()
|
|
524
|
+
return True
|
|
525
|
+
except TimeoutException:
|
|
526
|
+
return False
|
|
527
|
+
except NoAlertPresentException:
|
|
528
|
+
return False
|
|
529
|
+
|
|
530
|
+
def _selecionar_perfil_advogado(self):
|
|
531
|
+
"""
|
|
532
|
+
Seleciona o perfil de advogado na tela de seleção de perfil.
|
|
533
|
+
"""
|
|
534
|
+
|
|
535
|
+
try:
|
|
536
|
+
botao_advogado = self._util.find_element_with_wait(By.XPATH, "//button[@id='tr0']", 10)
|
|
537
|
+
botao_advogado.click()
|
|
538
|
+
except TimeoutException:
|
|
539
|
+
forms_de_perfis = self._util.find_element_with_wait(By.XPATH, "//form[@id='frmEscolherUsuario']", 10)
|
|
540
|
+
primeira_opcao = self._util.find_element_with_wait(By.XPATH, ".//button[contains(@id, 'tr0')]", 10,
|
|
541
|
+
parent=forms_de_perfis)
|
|
542
|
+
primeira_opcao.click()
|
|
543
|
+
|
|
544
|
+
return
|
|
545
|
+
|
|
546
|
+
class _LoginPje:
|
|
547
|
+
def __init__(self, driver, api, url_procurada, uf, sistema, advogado, token, tipo_login="mycena", processar_codes=None):
|
|
548
|
+
self.driver = driver
|
|
549
|
+
self.api = api
|
|
550
|
+
self.uf = uf
|
|
551
|
+
self.sistema = sistema
|
|
552
|
+
self.advogado = advogado
|
|
553
|
+
self.url_procurada = url_procurada
|
|
554
|
+
self.tipo_login = tipo_login.lower if tipo_login else "mycena"
|
|
555
|
+
self._util = _Utils(driver, api, url_procurada, uf, sistema, advogado, token,tipo_login)
|
|
556
|
+
self.processar_codes = processar_codes
|
|
557
|
+
|
|
558
|
+
def login(self):
|
|
559
|
+
"""
|
|
560
|
+
Realiza o login no sistema EPROC.
|
|
561
|
+
|
|
562
|
+
Prioridade:
|
|
563
|
+
1. Verifica se já está logado
|
|
564
|
+
2. Login via WHOM (para estados específicos: RJ, TO)
|
|
565
|
+
3. Login via Certificado Digital + 2FA (método principal)
|
|
566
|
+
"""
|
|
567
|
+
print(f"\n{'='*60}")
|
|
568
|
+
print(f"[LOGIN] Acessando: {self.url_procurada}")
|
|
569
|
+
print(f"{'='*60}")
|
|
570
|
+
|
|
571
|
+
self.driver.get(f"{self.url_procurada}")
|
|
572
|
+
time.sleep(2)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
if self._verica_captcha():
|
|
576
|
+
if self._util.deve_usar_whom():
|
|
577
|
+
self._util.login_via_whom()
|
|
578
|
+
if self._verificar_ja_logado():
|
|
579
|
+
print(" ✓ Login via WHOM realizado com sucesso!")
|
|
580
|
+
return True
|
|
581
|
+
else:
|
|
582
|
+
print("⚠ Login via WHOM falhou. Não há fallback disponível para este estado.")
|
|
583
|
+
raise Exception("Falha no login via WHOM")
|
|
584
|
+
|
|
585
|
+
# ══════════════════════════════════════════════════════════════
|
|
586
|
+
# PRIORIDADE 1: Verifica se já está logado
|
|
587
|
+
# ══════════════════════════════════════════════════════════════
|
|
588
|
+
if self._verificar_ja_logado():
|
|
589
|
+
return True
|
|
590
|
+
|
|
591
|
+
print(f"{self.url_procurada}")
|
|
592
|
+
self.driver.execute_script("document.body.style.zoom='90%'")
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
el_peticionar = '//td[contains(@id,"tabExpedientes_lbl") or contains(text(),"Expedientes")]'
|
|
596
|
+
time.sleep(1)
|
|
597
|
+
if "EXPEDIENTES" in self.driver.page_source or "ciência" in self.driver.page_source or "Ciencia" in self.driver.page_source or "Selecione uma jurisdição ou caixa" in self.driver.page_source:
|
|
598
|
+
print("Já logado, continuando o código.")
|
|
599
|
+
if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
|
|
600
|
+
self._util.find_element_with_wait(
|
|
601
|
+
By.XPATH,
|
|
602
|
+
'//td[contains(@id,"tabExpedientes_lbl")] or //td[contains(text(),"Expedientes")]',
|
|
603
|
+
).click()
|
|
604
|
+
|
|
605
|
+
self._fechar_popup_certificado()
|
|
606
|
+
return True
|
|
607
|
+
|
|
608
|
+
if "Avisos" in self.driver.page_source or "Mensagens" in self.driver.page_source or "Painel do usuário" in self.driver.page_source:
|
|
609
|
+
try:
|
|
610
|
+
btn_painel = self._util.find_element_with_wait(By.XPATH, "//input[contains(@value, 'Painel do usuário')]")
|
|
611
|
+
self.driver.execute_script("arguments[0].click();", btn_painel)
|
|
612
|
+
except BaseException:
|
|
613
|
+
if self._util.verificar_home_seam_atual():
|
|
614
|
+
link_redirect = self._util.find_element_with_wait(By.XPATH, "//a[contains(@href,'painel')]")
|
|
615
|
+
link_redirect = link_redirect.get_attribute("href")
|
|
616
|
+
self.driver.get(link_redirect)
|
|
617
|
+
time.sleep(2)
|
|
618
|
+
self._util.loading()
|
|
619
|
+
else:
|
|
620
|
+
btn_painel = self._util.find_element_with_wait(By.XPATH, "//a[@id='home']")
|
|
621
|
+
self.driver.execute_script("arguments[0].click();", btn_painel)
|
|
622
|
+
|
|
623
|
+
self._util.find_clickable_element_with_wait(By.XPATH, "//body", 30)
|
|
624
|
+
print("Já logado, continuando o código.")
|
|
625
|
+
if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
|
|
626
|
+
self._util.find_element_with_wait(
|
|
627
|
+
By.XPATH,
|
|
628
|
+
'//td[contains(@id,"tabExpedientes_lbl")] or //td[contains(text(),"Expedientes")]',
|
|
629
|
+
).click()
|
|
630
|
+
self._fechar_popup_certificado()
|
|
631
|
+
|
|
632
|
+
return True
|
|
633
|
+
|
|
634
|
+
elif "403 Forbidden" in self.driver.page_source:
|
|
635
|
+
raise WebDriverException("Erro 403 Forbidden ao acessar o PJe.")
|
|
636
|
+
|
|
637
|
+
xpath_framers = ["ssoFrame", "ifrmLogin", "framePeticionar"]
|
|
638
|
+
text_quebra = "Número do processo"
|
|
639
|
+
self._util.loading()
|
|
640
|
+
time.sleep(2)
|
|
641
|
+
self._util.framer_atached(
|
|
642
|
+
xpath_framers=xpath_framers,
|
|
643
|
+
text_quebra=text_quebra,
|
|
644
|
+
el=el_peticionar,
|
|
645
|
+
quebra=True,
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
xpath_cert = (
|
|
649
|
+
"//input["
|
|
650
|
+
"contains(@id, 'kc-pje-office') or "
|
|
651
|
+
"contains(@name, 'login-pje-office') or "
|
|
652
|
+
"contains(@value, 'CERTIFICADO DIGITAL') or "
|
|
653
|
+
"contains(@id, 'loginAplicacaoButton') or "
|
|
654
|
+
"contains(@name, 'loginAplicacaoButton') or "
|
|
655
|
+
"contains(@value, 'Certificado Digital')"
|
|
656
|
+
"]"
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
self._verificar_bad_request()
|
|
660
|
+
|
|
661
|
+
try:
|
|
662
|
+
click_certification = self._util.find_element_with_wait(By.XPATH, xpath_cert, timeout=30)
|
|
663
|
+
if click_certification:
|
|
664
|
+
if self._util.deve_usar_whom():
|
|
665
|
+
# Verifica se o login foi bem-sucedido
|
|
666
|
+
self._util.login_via_whom()
|
|
667
|
+
if self._verificar_ja_logado():
|
|
668
|
+
print(" ✓ Login via WHOM realizado com sucesso!")
|
|
669
|
+
return True
|
|
670
|
+
else:
|
|
671
|
+
print("⚠ Login via WHOM falhou. Não há fallback disponível para este estado.")
|
|
672
|
+
raise Exception("Falha no login via WHOM")
|
|
673
|
+
else:
|
|
674
|
+
click_certification.click()
|
|
675
|
+
print("CLICOU NO CERTIFICADO DIGITAL")
|
|
676
|
+
|
|
677
|
+
time.sleep(2)
|
|
678
|
+
self._util.loading()
|
|
679
|
+
|
|
680
|
+
self._verificar_bad_request()
|
|
681
|
+
if self._verificar_ja_logado():
|
|
682
|
+
return True
|
|
683
|
+
|
|
684
|
+
code = self._obter_codigo_2fa()
|
|
685
|
+
if code:
|
|
686
|
+
self._insert_codigo_2fa(code)
|
|
687
|
+
else:
|
|
688
|
+
self.login()
|
|
689
|
+
|
|
690
|
+
self._fechar_popup_certificado()
|
|
691
|
+
if "Redirecionamento em excesso por" in self.driver.page_source:
|
|
692
|
+
self.driver.refresh()
|
|
693
|
+
time.sleep(2)
|
|
694
|
+
pass
|
|
695
|
+
|
|
696
|
+
try:
|
|
697
|
+
input_certificado = self.driver.find_elements(By.XPATH, xpath_cert)
|
|
698
|
+
if input_certificado:
|
|
699
|
+
self.driver.refresh()
|
|
700
|
+
time.sleep(1)
|
|
701
|
+
click_certification = self._util.find_element_with_wait(By.XPATH, xpath_cert, timeout=30)
|
|
702
|
+
try:
|
|
703
|
+
self.driver.execute_script("arguments[0].click();", click_certification)
|
|
704
|
+
except Exception:
|
|
705
|
+
click_certification.click()
|
|
706
|
+
|
|
707
|
+
code = self._obter_codigo_2fa()
|
|
708
|
+
if code:
|
|
709
|
+
self._insert_codigo_2fa(code)
|
|
710
|
+
else:
|
|
711
|
+
self.login()
|
|
712
|
+
|
|
713
|
+
self._util.loading()
|
|
714
|
+
|
|
715
|
+
time.sleep(2)
|
|
716
|
+
except NoSuchElementException:
|
|
717
|
+
pass
|
|
718
|
+
|
|
719
|
+
except Exception as e:
|
|
720
|
+
print(f"Erro ao clicar no certificado digital ou já logado: {e}")
|
|
721
|
+
self._fechar_popup_certificado()
|
|
722
|
+
if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
|
|
723
|
+
try:
|
|
724
|
+
self._util.find_element_with_wait(By.XPATH, f"{el_peticionar}").click()
|
|
725
|
+
self.driver.switch_to.default_content()
|
|
726
|
+
except BaseException:
|
|
727
|
+
self.driver.switch_to.default_content()
|
|
728
|
+
self._util.find_element_with_wait(By.XPATH, f"{el_peticionar}").click()
|
|
729
|
+
self._util.loading()
|
|
730
|
+
time.sleep(0.5)
|
|
731
|
+
else:
|
|
732
|
+
self._util.framer_atached(xpath_framers, text_quebra, el_peticionar)
|
|
733
|
+
self.driver.switch_to.default_content()
|
|
734
|
+
|
|
735
|
+
self.driver.switch_to.default_content()
|
|
736
|
+
time.sleep(2)
|
|
737
|
+
|
|
738
|
+
if "Redirecionamento em excesso por" in self.driver.page_source:
|
|
739
|
+
self.driver.refresh()
|
|
740
|
+
time.sleep(2)
|
|
741
|
+
pass
|
|
742
|
+
|
|
743
|
+
self._util.framer_atached(xpath_framers, text_quebra, el_peticionar)
|
|
744
|
+
|
|
745
|
+
if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
|
|
746
|
+
try:
|
|
747
|
+
self._util.find_element_with_wait(By.XPATH, f"{el_peticionar}", 5).click()
|
|
748
|
+
self.driver.switch_to.default_content()
|
|
749
|
+
except BaseException:
|
|
750
|
+
self.driver.switch_to.default_content()
|
|
751
|
+
self._util.find_element_with_wait(By.XPATH, f"{el_peticionar}").click()
|
|
752
|
+
self._util.loading()
|
|
753
|
+
time.sleep(0.5)
|
|
754
|
+
|
|
755
|
+
self._fechar_popup_certificado()
|
|
756
|
+
|
|
757
|
+
if "Sua página expirou, por favor tente novamente." in self.driver.page_source:
|
|
758
|
+
self.driver.refresh()
|
|
759
|
+
time.sleep(1)
|
|
760
|
+
self.login()
|
|
761
|
+
self._util.loading()
|
|
762
|
+
return
|
|
763
|
+
|
|
764
|
+
if "Avisos" in self.driver.page_source or "Mensagens" in self.driver.page_source or "Painel do usuário" in self.driver.page_source:
|
|
765
|
+
try:
|
|
766
|
+
btn_painel = self._util.find_element_with_wait(By.XPATH, "//input[contains(@value, 'Painel do usuário')]")
|
|
767
|
+
self.driver.execute_script("arguments[0].click();", btn_painel)
|
|
768
|
+
except BaseException:
|
|
769
|
+
if self._util.verificar_home_seam_atual():
|
|
770
|
+
link_redirect = self._util.find_element_with_wait(By.XPATH, "//a[contains(@href,'painel')]")
|
|
771
|
+
link_redirect = link_redirect.get_attribute("href")
|
|
772
|
+
self.driver.get(link_redirect)
|
|
773
|
+
time.sleep(2)
|
|
774
|
+
self._util.loading()
|
|
775
|
+
else:
|
|
776
|
+
btn_painel = self._util.find_element_with_wait(By.XPATH, "//a[@id='home']")
|
|
777
|
+
self.driver.execute_script("arguments[0].click();", btn_painel)
|
|
778
|
+
self._util.find_clickable_element_with_wait(By.XPATH, "//body", 30)
|
|
779
|
+
print("Já logado, continuando o código.")
|
|
780
|
+
|
|
781
|
+
if "Sua página expirou, por favor tente novamente." in self.driver.page_source:
|
|
782
|
+
self.driver.refresh()
|
|
783
|
+
time.sleep(1)
|
|
784
|
+
self.login()
|
|
785
|
+
self._util.loading()
|
|
786
|
+
return
|
|
787
|
+
|
|
788
|
+
if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
|
|
789
|
+
self._util.find_element_with_wait(
|
|
790
|
+
By.XPATH,
|
|
791
|
+
'//td[contains(@id,"tabExpedientes_lbl")] or //td[contains(text(),"Expedientes")]',
|
|
792
|
+
).click()
|
|
793
|
+
self._fechar_popup_certificado()
|
|
794
|
+
|
|
795
|
+
if self._util.verificar_home_seam_atual():
|
|
796
|
+
link_redirect = self._util.find_element_with_wait(By.XPATH, "//a[contains(@href,'painel')]")
|
|
797
|
+
link_redirect = link_redirect.get_attribute("href")
|
|
798
|
+
self.driver.get(link_redirect)
|
|
799
|
+
time.sleep(2)
|
|
800
|
+
self._util.loading()
|
|
801
|
+
return
|
|
802
|
+
else:
|
|
803
|
+
return
|
|
804
|
+
|
|
805
|
+
def _verificar_ja_logado(self):
|
|
806
|
+
if "Avisos" in self.driver.page_source or "Mensagens" in self.driver.page_source or "Painel do usuário" in self.driver.page_source:
|
|
807
|
+
try:
|
|
808
|
+
btn_painel = self._util.find_element_with_wait(By.XPATH,
|
|
809
|
+
"//input[contains(@value, 'Painel do usuário')]")
|
|
810
|
+
self.driver.execute_script("arguments[0].click();", btn_painel)
|
|
811
|
+
except BaseException:
|
|
812
|
+
if self._util.verificar_home_seam_atual():
|
|
813
|
+
link_redirect = self._util.find_element_with_wait(By.XPATH, "//a[contains(@href,'painel')]")
|
|
814
|
+
link_redirect = link_redirect.get_attribute("href")
|
|
815
|
+
self.driver.get(link_redirect)
|
|
816
|
+
time.sleep(2)
|
|
817
|
+
self._util.loading()
|
|
818
|
+
else:
|
|
819
|
+
btn_painel = self._util.find_element_with_wait(By.XPATH, "//a[@id='home']")
|
|
820
|
+
self.driver.execute_script("arguments[0].click();", btn_painel)
|
|
821
|
+
|
|
822
|
+
self._util.find_clickable_element_with_wait(By.XPATH, "//body", 30)
|
|
823
|
+
|
|
824
|
+
if "Sua página expirou, por favor tente novamente." in self.driver.page_source:
|
|
825
|
+
self.driver.refresh()
|
|
826
|
+
time.sleep(1)
|
|
827
|
+
self.login()
|
|
828
|
+
self._util.loading()
|
|
829
|
+
return True
|
|
830
|
+
|
|
831
|
+
if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
|
|
832
|
+
self._util.find_element_with_wait(
|
|
833
|
+
By.XPATH,
|
|
834
|
+
'//td[contains(@id,"tabExpedientes_lbl")] or //td[contains(text(),"Expedientes")]',
|
|
835
|
+
).click()
|
|
836
|
+
self._fechar_popup_certificado()
|
|
837
|
+
|
|
838
|
+
if self._util.verificar_home_seam_atual():
|
|
839
|
+
link_redirect = self._util.find_element_with_wait(By.XPATH, "//a[contains(@href,'painel')]")
|
|
840
|
+
link_redirect = link_redirect.get_attribute("href")
|
|
841
|
+
self.driver.get(link_redirect)
|
|
842
|
+
time.sleep(2)
|
|
843
|
+
self._util.loading()
|
|
844
|
+
return True
|
|
845
|
+
else:
|
|
846
|
+
return True
|
|
847
|
+
else:
|
|
848
|
+
return False
|
|
849
|
+
def _verica_captcha(self):
|
|
850
|
+
if 'Vamos confirmar que você é humano' in self.driver.page_source:
|
|
851
|
+
return True
|
|
852
|
+
else:
|
|
853
|
+
return False
|
|
854
|
+
|
|
855
|
+
def _fechar_popup_certificado(self):
|
|
856
|
+
if "Certificado próximo de expirar" in self.driver.page_source:
|
|
857
|
+
try:
|
|
858
|
+
bt_fechar = self._util.find_element_with_wait(
|
|
859
|
+
By.XPATH,
|
|
860
|
+
"//span[contains(@class,'btn-fechar') or contains(@onclick,'fechar')]",
|
|
861
|
+
)
|
|
862
|
+
bt_fechar.click()
|
|
863
|
+
self._util.loading()
|
|
864
|
+
|
|
865
|
+
except BaseException:
|
|
866
|
+
pass
|
|
867
|
+
|
|
868
|
+
def _verificar_bad_request(self):
|
|
869
|
+
if "Bad Request" in self.driver.page_source:
|
|
870
|
+
raise WebDriverException("Erro de Bad Request ao acessar o PJe.")
|
|
871
|
+
|
|
872
|
+
def _esta_na_tela_2fa(self):
|
|
873
|
+
"""
|
|
874
|
+
Verifica se está na tela de inserção do código 2FA.
|
|
875
|
+
"""
|
|
876
|
+
try:
|
|
877
|
+
self.driver.find_element(By.XPATH, "//*[self::input[@id='txtAcessoCodigo'] or self::input[@id='otp']]")
|
|
878
|
+
return True
|
|
879
|
+
except NoSuchElementException:
|
|
880
|
+
return False
|
|
881
|
+
|
|
882
|
+
def _obter_codigo_2fa(self):
|
|
883
|
+
"""
|
|
884
|
+
Obtém o código 2FA via API (login_2factor).
|
|
885
|
+
Retorna o código ou None se falhar.
|
|
886
|
+
"""
|
|
887
|
+
try:
|
|
888
|
+
|
|
889
|
+
code = self.processar_codes()
|
|
890
|
+
return code
|
|
891
|
+
except Exception as e:
|
|
892
|
+
print(f" ✗ Erro ao obter código 2FA: {e}")
|
|
893
|
+
return None
|
|
894
|
+
|
|
895
|
+
def _insert_codigo_2fa(self,code):
|
|
896
|
+
# Verifica se apareceu o formulário de autenticação 2FA
|
|
897
|
+
try:
|
|
898
|
+
if code:
|
|
899
|
+
input_otp = self._util.find_element_with_wait(By.XPATH,
|
|
900
|
+
"//form[@id='kc-otp-login-form'] | //input[@id='otp' and @name='otp']",
|
|
901
|
+
timeout=5)
|
|
902
|
+
if input_otp:
|
|
903
|
+
input_otp = self._util.find_element_with_wait(By.XPATH, "//input[contains(@type,'text')]")
|
|
904
|
+
time.sleep(2)
|
|
905
|
+
|
|
906
|
+
input_otp.clear()
|
|
907
|
+
input_otp.send_keys(str(code))
|
|
908
|
+
|
|
909
|
+
click_validar = self._util.find_element_with_wait(By.XPATH,
|
|
910
|
+
"//input[contains(@type,'submit')] | //button[contains(@type,'submit')]",
|
|
911
|
+
timeout=5)
|
|
912
|
+
click_validar.click()
|
|
913
|
+
self._util.loading()
|
|
914
|
+
if 'Código inválido'.lower() in self.driver.page_source.lower():
|
|
915
|
+
code = self._obter_codigo_2fa()
|
|
916
|
+
self._insert_codigo_2fa(code)
|
|
917
|
+
return
|
|
918
|
+
else:
|
|
919
|
+
self.login()
|
|
920
|
+
except (TimeoutException, NoSuchElementException):
|
|
921
|
+
# Formulário OTP não apareceu, continua normalmente
|
|
922
|
+
pass
|
|
923
|
+
|
|
924
|
+
class _Utils:
|
|
925
|
+
def __init__(self, driver, api, url_procurada, uf, sistema, advogado, token, tipo='mycena'):
|
|
926
|
+
self.driver = driver
|
|
927
|
+
self.tipo_login = tipo
|
|
928
|
+
self.sistema = sistema
|
|
929
|
+
self.advogado = advogado
|
|
930
|
+
self.token = token
|
|
931
|
+
self.url_procurada = url_procurada
|
|
932
|
+
self.uf = uf
|
|
933
|
+
self.api = api
|
|
934
|
+
|
|
935
|
+
def find_element_with_wait(self, by, value, timeout=10, parent=None):
|
|
936
|
+
if parent is None:
|
|
937
|
+
parent = self.driver # Usa o driver principal se nenhum elemento pai for passado
|
|
938
|
+
return WebDriverWait(parent, timeout).until(ec.presence_of_element_located((by, value)))
|
|
939
|
+
|
|
940
|
+
def find_elements_with_wait(self, by, value, timeout=10, parent=None):
|
|
941
|
+
if parent is None:
|
|
942
|
+
parent = self.driver # Usa o driver principal se nenhum elemento pai for passado
|
|
943
|
+
return WebDriverWait(parent, timeout).until(ec.presence_of_all_elements_located((by, value)))
|
|
944
|
+
|
|
945
|
+
def find_clickable_element_with_wait(self, by, value, timeout=10, parent=None):
|
|
946
|
+
if parent is None:
|
|
947
|
+
parent = self.driver # Usa o driver principal se nenhum elemento pai for passado
|
|
948
|
+
return WebDriverWait(parent, timeout).until(ec.element_to_be_clickable((by, value)))
|
|
949
|
+
|
|
950
|
+
def deve_usar_whom(self):
|
|
951
|
+
"""
|
|
952
|
+
Determina se deve usar o login via WHOM baseado no estado (UF).
|
|
953
|
+
Estados que usam WHOM: RJ, TO
|
|
954
|
+
"""
|
|
955
|
+
if self.tipo_login.lower() == "mycena" or "my" in self.tipo_login.lower() or "mysena" in self.tipo_login.lower():
|
|
956
|
+
return False
|
|
957
|
+
elif 'whoom' in self.tipo_login.lower() or 'whoon' in self.tipo_login.lower() or self.tipo_login.lower() == 'whoom' or self.tipo_login.lower() == 'whom' or self.tipo_login.lower() == 'wh' or 'wh' in self.tipo_login.lower() :
|
|
958
|
+
return True
|
|
959
|
+
else:
|
|
960
|
+
return False
|
|
961
|
+
|
|
962
|
+
def login_via_whom(self):
|
|
963
|
+
"""
|
|
964
|
+
Realiza login via WHOM (login_2fac do bcpkgfox).
|
|
965
|
+
Todo o processo de autenticação é feito pela função login_2fac.
|
|
966
|
+
|
|
967
|
+
Retorna True se login bem-sucedido, False caso contrário.
|
|
968
|
+
"""
|
|
969
|
+
print(f"\n[LOGIN WHOM] Iniciando login via WHOM para {self.uf}...")
|
|
970
|
+
print(f" → Sistema: {self.sistema}")
|
|
971
|
+
print(f" → Certificado: {self.advogado}")
|
|
972
|
+
|
|
973
|
+
try:
|
|
974
|
+
login_2fac(
|
|
975
|
+
driver=self.driver,
|
|
976
|
+
certificate=f"{self.advogado}",
|
|
977
|
+
system=f"{self.sistema}",
|
|
978
|
+
token=f"{self.token}",
|
|
979
|
+
code_timeout=120,
|
|
980
|
+
)
|
|
981
|
+
time.sleep(3)
|
|
982
|
+
except Exception as e:
|
|
983
|
+
print(f" ✗ Erro durante login via WHOM: {e}")
|
|
984
|
+
return False
|
|
985
|
+
|
|
986
|
+
def framer_atached(self, xpath_framers, quebra=False, text_quebra="", el=""):
|
|
987
|
+
for framer in xpath_framers:
|
|
988
|
+
try:
|
|
989
|
+
self.driver.switch_to.frame(framer)
|
|
990
|
+
if quebra:
|
|
991
|
+
if framer == "framePeticionar":
|
|
992
|
+
if f"{text_quebra}" in self.driver.page_source:
|
|
993
|
+
try:
|
|
994
|
+
self.find_element_with_wait(By.XPATH, f"{el}").click()
|
|
995
|
+
except BaseException:
|
|
996
|
+
self.driver.switch_to.default_content()
|
|
997
|
+
self.find_element_with_wait(By.XPATH, f"{el}").click()
|
|
998
|
+
return
|
|
999
|
+
break
|
|
1000
|
+
except NoSuchElementException:
|
|
1001
|
+
continue
|
|
1002
|
+
except BaseException:
|
|
1003
|
+
continue
|
|
1004
|
+
|
|
1005
|
+
def loading(self):
|
|
1006
|
+
timeout = 60 # Tempo máximo de espera em segundos
|
|
1007
|
+
start_time = time.time()
|
|
1008
|
+
while True:
|
|
1009
|
+
if time.time() - start_time > timeout:
|
|
1010
|
+
print("Aviso: A verificação de 'loading' excedeu o tempo limite.")
|
|
1011
|
+
break
|
|
1012
|
+
try:
|
|
1013
|
+
styled = self.driver.find_element("xpath", '//*[@id="_viewRoot:status.start"]')
|
|
1014
|
+
style_content = styled.get_attribute("style")
|
|
1015
|
+
if "display: none;" in style_content:
|
|
1016
|
+
break # Sai do loop se o carregamento terminou
|
|
1017
|
+
else:
|
|
1018
|
+
time.sleep(0.5) # Espera um pouco antes de verificar novamente
|
|
1019
|
+
except NoSuchElementException:
|
|
1020
|
+
break # Sai do loop se o elemento de loading não for encontrado
|
|
1021
|
+
|
|
1022
|
+
def verificar_home_seam_atual(self):
|
|
1023
|
+
"""
|
|
1024
|
+
Verifica se 'home.seam' está presente na URL atual do navegador.
|
|
1025
|
+
"""
|
|
1026
|
+
url_atual = self.driver.current_url
|
|
1027
|
+
if "home.seam" in url_atual:
|
|
1028
|
+
print(f"'home.seam' encontrado na URL atual: {url_atual}")
|
|
1029
|
+
return True
|
|
1030
|
+
else:
|
|
1031
|
+
print(f"'home.seam' NÃO encontrado na URL atual: {url_atual}")
|
|
1032
|
+
return False
|
|
1033
|
+
|
|
1034
|
+
def msg_aviso(self, mensagem, titulo="Aviso"):
|
|
1035
|
+
import tkinter as tk
|
|
1036
|
+
from tkinter import messagebox
|
|
1037
|
+
|
|
1038
|
+
root = tk.Tk()
|
|
1039
|
+
root.withdraw()
|
|
1040
|
+
root.attributes("-topmost", True)
|
|
1041
|
+
messagebox.showinfo(titulo, mensagem, parent=root)
|
|
1042
|
+
root.destroy()
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import requests, time
|
|
2
|
-
|
|
3
|
-
class Login2facmycena:
|
|
4
|
-
def __init__(self, uf, sistema, advogado, token, maximo = 5):
|
|
5
|
-
self.codes = None
|
|
6
|
-
self.api = ApiControll(uf, sistema, advogado, token)
|
|
7
|
-
self.code_timeout = maximo
|
|
8
|
-
|
|
9
|
-
def processar_codes(self):
|
|
10
|
-
max_attempts = self.code_timeout
|
|
11
|
-
five_attempts = 0
|
|
12
|
-
ten_attempts = 0
|
|
13
|
-
|
|
14
|
-
while five_attempts <= max_attempts:
|
|
15
|
-
five_attempts += 1
|
|
16
|
-
self.codes = self.api.listar_codigos_otp()
|
|
17
|
-
if self.codes:
|
|
18
|
-
print("Códigos obtidos com sucesso:")
|
|
19
|
-
return self.codes
|
|
20
|
-
else:
|
|
21
|
-
print("Solicitando novo codigo.")
|
|
22
|
-
self.api.solicitar_codigos_otp()
|
|
23
|
-
time.sleep(10)
|
|
24
|
-
while ten_attempts <=10:
|
|
25
|
-
ten_attempts += 1
|
|
26
|
-
self.codes = self.api.listar_codigos_otp()
|
|
27
|
-
if self.codes:
|
|
28
|
-
return self.codes
|
|
29
|
-
time.sleep(10)
|
|
30
|
-
return None
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class ApiControll:
|
|
34
|
-
def __init__(self, uf, sistema, advogado, tk):
|
|
35
|
-
super().__init__()
|
|
36
|
-
self.uf = uf
|
|
37
|
-
self.sistema = sistema
|
|
38
|
-
self.advogado = advogado
|
|
39
|
-
self.token = tk
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def solicitar_codigos_otp(self):
|
|
43
|
-
url = "https://api-4.bcfox.com.br/bcjur/views/codigos-otp"
|
|
44
|
-
headers = {
|
|
45
|
-
"x-access-token": self.token,
|
|
46
|
-
"Content-Type": "application/json"
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
payload = {
|
|
50
|
-
"uf": self.uf,
|
|
51
|
-
"advogado": self.advogado,
|
|
52
|
-
"sistema": self.sistema
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
for tentativa in range(1, 6):
|
|
56
|
-
try:
|
|
57
|
-
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
|
58
|
-
|
|
59
|
-
if response.status_code == 200:
|
|
60
|
-
print(
|
|
61
|
-
f"Tentativa {tentativa}: Requisição bem-sucedida. Código de status: {response.status_code}")
|
|
62
|
-
response_data = response.json()
|
|
63
|
-
print(response_data)
|
|
64
|
-
return response_data
|
|
65
|
-
else:
|
|
66
|
-
print(f"Tentativa {tentativa}: Erro na requisição. Código de status: {response.status_code}")
|
|
67
|
-
|
|
68
|
-
except requests.RequestException as e:
|
|
69
|
-
print(f"Tentativa {tentativa}: Ocorreu um erro na requisição: {e}")
|
|
70
|
-
|
|
71
|
-
time.sleep(1)
|
|
72
|
-
|
|
73
|
-
return None
|
|
74
|
-
|
|
75
|
-
def listar_codigos_otp(self):
|
|
76
|
-
url = f"https://api-4.bcfox.com.br/bcjur/views/codigos-otp/robo/{self.uf}/{self.sistema}/{self.advogado}"
|
|
77
|
-
headers = {"x-access-token": self.token}
|
|
78
|
-
|
|
79
|
-
# Retries authenticated OTP code listing on failure
|
|
80
|
-
for tentativa in range(1, 6):
|
|
81
|
-
try:
|
|
82
|
-
response = requests.get(url, headers=headers, timeout=30)
|
|
83
|
-
|
|
84
|
-
if response.status_code == 200:
|
|
85
|
-
response_data = response.json()
|
|
86
|
-
if response_data:
|
|
87
|
-
return response_data[0].get("CODIGO_OTP")
|
|
88
|
-
|
|
89
|
-
else:
|
|
90
|
-
print(f"Tentativa {tentativa}: Status {response.status_code}")
|
|
91
|
-
|
|
92
|
-
except requests.RequestException as e:
|
|
93
|
-
print(f"Tentativa {tentativa}: Erro na requisição: {e}")
|
|
94
|
-
|
|
95
|
-
time.sleep(1)
|
|
96
|
-
return None
|
|
97
|
-
|
|
98
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|