bcpkgfox 0.17.5__tar.gz → 0.17.7__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.5 → bcpkgfox-0.17.7}/PKG-INFO +13 -4
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox/invoke_api.py +140 -124
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox.egg-info/PKG-INFO +13 -4
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/setup.py +3 -3
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/README.md +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox/__init__.py +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox/clean.py +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox/cli.py +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox/find_elements.py +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox/get_driver.py +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox/system.py +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox.egg-info/SOURCES.txt +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox.egg-info/dependency_links.txt +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox.egg-info/entry_points.txt +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox.egg-info/requires.txt +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/bcpkgfox.egg-info/top_level.txt +0 -0
- {bcpkgfox-0.17.5 → bcpkgfox-0.17.7}/setup.cfg +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: bcpkgfox
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.7
|
|
4
4
|
Summary: Biblioteca BCFOX
|
|
5
5
|
Home-page: https://github.com/robotsbcfox/PacotePythonBCFOX
|
|
6
|
-
Author:
|
|
7
|
-
Author-email:
|
|
6
|
+
Author: BCFOX
|
|
7
|
+
Author-email: bcfox@bcfox.com.br
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
@@ -42,3 +42,12 @@ Requires-Dist: pymupdf; extra == "full"
|
|
|
42
42
|
Requires-Dist: Pillow; extra == "full"
|
|
43
43
|
Requires-Dist: psutil; extra == "full"
|
|
44
44
|
Requires-Dist: pynput; extra == "full"
|
|
45
|
+
Dynamic: author
|
|
46
|
+
Dynamic: author-email
|
|
47
|
+
Dynamic: classifier
|
|
48
|
+
Dynamic: description-content-type
|
|
49
|
+
Dynamic: home-page
|
|
50
|
+
Dynamic: provides-extra
|
|
51
|
+
Dynamic: requires-dist
|
|
52
|
+
Dynamic: requires-python
|
|
53
|
+
Dynamic: summary
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
1
3
|
from selenium.webdriver.support import expected_conditions as EC
|
|
2
4
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
3
5
|
from selenium.webdriver.common.by import By
|
|
@@ -167,7 +169,7 @@ def invoke_api_proc_log(link, id_robo, token):
|
|
|
167
169
|
"POST", link, json=payload, headers=headers)
|
|
168
170
|
print(f"\n{responseinsert.json()}")
|
|
169
171
|
|
|
170
|
-
def login_2fac(driver, certificate, system, token, code_timeout=
|
|
172
|
+
def login_2fac(driver, certificate, system, token, code_timeout=60):
|
|
171
173
|
import requests
|
|
172
174
|
import pyautogui
|
|
173
175
|
from . import mostrar_mensagem
|
|
@@ -320,25 +322,34 @@ def login_2fac(driver, certificate, system, token, code_timeout=10):
|
|
|
320
322
|
|
|
321
323
|
self.driver.get("https://chromewebstore.google.com/detail/whom-gerenciador-de-certi/lnidijeaekolpfeckelhkomndglcglhh")
|
|
322
324
|
|
|
323
|
-
tools.find_element_with_wait(By.XPATH, "//span[contains(text(), 'no Chrome') or contains(text(), 'Usar') or contains(text(), 'Add to Chrome')]").click()
|
|
324
|
-
time.sleep(5)
|
|
325
325
|
|
|
326
|
-
|
|
327
|
-
pyautogui.press('tab')
|
|
328
|
-
time.sleep(0.5)
|
|
329
|
-
pyautogui.press('enter')
|
|
326
|
+
tools.find_element_with_wait(By.XPATH, "//span[contains(text(), 'no Chrome') or contains(text(), 'Usar') or contains(text(), 'Add to Chrome')]").click()
|
|
330
327
|
time.sleep(5)
|
|
328
|
+
import pygetwindow as gw
|
|
329
|
+
windows = gw.getAllTitles()
|
|
330
|
+
for w in windows:
|
|
331
|
+
if w and "Whom?".lower() in w.lower():
|
|
332
|
+
print("Janela de extensão detectada:", w)
|
|
333
|
+
window = gw.getWindowsWithTitle(w)[0]
|
|
334
|
+
window.activate()
|
|
335
|
+
time.sleep(1)
|
|
336
|
+
coords = (gw.getActiveWindow().left + gw.getActiveWindow().size[0] // 2,
|
|
337
|
+
gw.getActiveWindow().top + int(gw.getActiveWindow().size[1] * 0.3))
|
|
338
|
+
pyautogui.moveTo(coords[0], coords[1])
|
|
339
|
+
pyautogui.click()
|
|
340
|
+
|
|
341
|
+
# Envia TAB e ENTER do teclado físico
|
|
342
|
+
pyautogui.press('tab')
|
|
343
|
+
time.sleep(0.5)
|
|
344
|
+
pyautogui.press('enter')
|
|
345
|
+
time.sleep(5)
|
|
346
|
+
break
|
|
331
347
|
|
|
332
348
|
self.driver.get('chrome-extension://lnidijeaekolpfeckelhkomndglcglhh/index.html')
|
|
333
349
|
|
|
334
350
|
def codes_2_fac(self):
|
|
335
|
-
element_sistema = '//input[(contains(@placeholder,"selecione um sistema pra acessar") or contains(@name, "system")) and contains(@role, "combobox")]'
|
|
336
|
-
element_email = '//input[contains(@placeholder,"Insira aqui o seu email") or contains(@placeholder,"e-mail")]'
|
|
337
|
-
email = 'eliezer@bcfox.com.br'
|
|
338
|
-
element_code = '//input[contains(@placeholder,"Digite aqui o código que enviamos para o seu e-mail") or contains(@placeholder,"código") or contains(@type,"password")]'
|
|
339
|
-
element_submit ='//button[contains(@type, "submit")]'
|
|
340
351
|
try:
|
|
341
|
-
tools.find_element_with_wait(By.XPATH,
|
|
352
|
+
tools.find_element_with_wait(By.XPATH, '//input[@placeholder="Digite ou selecione um sistema pra acessar"]', timeout=2).send_keys(self.system)
|
|
342
353
|
code_insertion = True
|
|
343
354
|
|
|
344
355
|
except:
|
|
@@ -346,9 +357,9 @@ def login_2fac(driver, certificate, system, token, code_timeout=10):
|
|
|
346
357
|
|
|
347
358
|
# Request the code
|
|
348
359
|
for _ in range(50): # Wait the extension to load
|
|
349
|
-
|
|
360
|
+
|
|
350
361
|
try:
|
|
351
|
-
tools.find_element_with_wait(By.XPATH,
|
|
362
|
+
tools.find_element_with_wait(By.XPATH, '//input[@placeholder="Insira aqui o seu email"]', timeout=1).send_keys('eliezer@bcfox.com.br')
|
|
352
363
|
time.sleep(1)
|
|
353
364
|
break
|
|
354
365
|
except:
|
|
@@ -357,145 +368,149 @@ def login_2fac(driver, certificate, system, token, code_timeout=10):
|
|
|
357
368
|
# Envia o código pro email, o for é só para tratativa de bugs
|
|
358
369
|
for _ in range(10):
|
|
359
370
|
try:
|
|
360
|
-
tools.find_element_with_wait(By.XPATH,
|
|
371
|
+
tools.find_element_with_wait(By.XPATH, '//input[@placeholder="Digite aqui o código que enviamos para o seu e-mail"]', timeout=1)
|
|
361
372
|
break
|
|
362
373
|
|
|
363
374
|
except:
|
|
364
375
|
try:
|
|
365
|
-
element = tools.find_element_with_wait(By.XPATH,
|
|
376
|
+
element = tools.find_element_with_wait(By.XPATH, '//input[@placeholder="Insira aqui o seu email"]', timeout=1)
|
|
366
377
|
element.clear()
|
|
367
|
-
element.send_keys(
|
|
368
|
-
tools.find_element_with_wait(By.XPATH,
|
|
378
|
+
element.send_keys('eliezer@bcfox.com.br')
|
|
379
|
+
tools.find_element_with_wait(By.XPATH, '//button').click()
|
|
369
380
|
time.sleep(1)
|
|
370
381
|
except:
|
|
371
382
|
break
|
|
372
383
|
|
|
373
384
|
# Attempts the new codes until success or requests limit
|
|
374
385
|
for _ in range(code_timeout):
|
|
375
|
-
|
|
376
|
-
responses = api.invoke_get_codes()
|
|
377
|
-
if not responses:
|
|
378
|
-
time.sleep(2)
|
|
379
|
-
return [], None
|
|
380
|
-
# Try new codes
|
|
381
|
-
code_insertion = False
|
|
382
|
-
for response in responses:
|
|
383
|
-
time.sleep(1)
|
|
386
|
+
responses = api.invoke_get_codes()
|
|
384
387
|
|
|
385
|
-
|
|
386
|
-
|
|
388
|
+
# Try new codes
|
|
389
|
+
code_insertion = False
|
|
390
|
+
for response in responses:
|
|
387
391
|
|
|
388
|
-
|
|
389
|
-
|
|
392
|
+
CODE = response['CODIGO']
|
|
393
|
+
ID = response['ID']
|
|
390
394
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
button_entrar_click = False
|
|
395
|
-
for _ in range(20):
|
|
396
|
-
try:
|
|
397
|
-
button = tools.find_element_with_wait(By.XPATH, f'{element_submit}', timeout=2)
|
|
398
|
-
if button.get_attribute('disabled'):
|
|
399
|
-
time.sleep(1.5)
|
|
400
|
-
continue
|
|
401
|
-
button.click()
|
|
402
|
-
button_entrar_click = True
|
|
403
|
-
if 'Escolha um certificado' in self.driver.page_source:
|
|
404
|
-
break
|
|
405
|
-
|
|
406
|
-
time.sleep(1)
|
|
407
|
-
except:
|
|
408
|
-
break
|
|
409
|
-
|
|
410
|
-
if int(_) == 20 and button_entrar_click == False:
|
|
411
|
-
raise TimeoutError('Código Whom falhou ao entrar')
|
|
412
|
-
|
|
413
|
-
return responses, ID
|
|
414
|
-
|
|
415
|
-
responses, ID = adicionar_code()
|
|
416
|
-
|
|
417
|
-
if 'Sair' in self.driver.page_source and not 'Verifique seu e-mail' in self.driver.page_source:
|
|
418
|
-
api.invoke_update_status(ID) # FIX: Update
|
|
419
|
-
break
|
|
395
|
+
element = tools.find_element_with_wait(By.XPATH, '//input[@type="password"]')
|
|
396
|
+
element.clear()
|
|
397
|
+
element.send_keys(CODE)
|
|
420
398
|
|
|
421
|
-
|
|
422
|
-
|
|
399
|
+
for _ in range(10):
|
|
400
|
+
try:
|
|
401
|
+
tools.find_element_with_wait(By.XPATH, '//div/div[2]/button', timeout=2).click()
|
|
402
|
+
time.sleep(1)
|
|
403
|
+
except:
|
|
404
|
+
break
|
|
423
405
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
responses,ID = adicionar_code()
|
|
427
|
-
time.sleep(1)
|
|
428
|
-
else:
|
|
429
|
-
break
|
|
406
|
+
# Check the code result
|
|
407
|
+
for _ in range(30):
|
|
430
408
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
409
|
+
# Correct
|
|
410
|
+
try:
|
|
411
|
+
# input('\n\n > Selecione o sistema e aperte alguma tecla.')
|
|
412
|
+
tools.find_element_with_wait(By.XPATH, '//input[@placeholder="Digite ou selecione um sistema pra acessar"]', timeout=1).send_keys(self.system)
|
|
413
|
+
api.invoke_update_status(ID) #FIX: Update
|
|
414
|
+
code_insertion = True
|
|
415
|
+
break
|
|
434
416
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
timeout=1)
|
|
438
|
-
tools.find_element_with_wait(By.XPATH, "//button[text()='Voltar']", timeout=1).click()
|
|
439
|
-
code_insertion = False
|
|
440
|
-
break
|
|
417
|
+
except:
|
|
418
|
+
pass
|
|
441
419
|
|
|
442
|
-
|
|
443
|
-
|
|
420
|
+
# Wrong
|
|
421
|
+
try:
|
|
422
|
+
tools.find_element_with_wait(By.XPATH, "//span[contains(text(), 'Senha inválida')]", timeout=1)
|
|
423
|
+
tools.find_element_with_wait(By.XPATH, "//button[text()='Voltar']", timeout=1).click()
|
|
424
|
+
code_insertion = False
|
|
425
|
+
break
|
|
444
426
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
break
|
|
427
|
+
except:
|
|
428
|
+
pass
|
|
448
429
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
for _ in range(30):
|
|
452
|
-
if code_insertion:
|
|
453
|
-
break
|
|
454
|
-
# Correct
|
|
455
|
-
try:
|
|
456
|
-
tools.find_element_with_wait(By.XPATH,"//input[(contains(@name,'certificate') or contains(@placeholder,'certificado')) and not(contains(@type, 'hidden'))]", timeout=1).send_keys(self.certificate)
|
|
457
|
-
list_certific = tools.find_elements_with_wait(By.XPATH,'//div[contains(@role,"option")]')
|
|
458
|
-
list_certific[0].click()
|
|
430
|
+
# If the 'new_response' loop succeeds immediately
|
|
431
|
+
if not responses:
|
|
459
432
|
time.sleep(1)
|
|
460
|
-
code_insertion = True
|
|
461
|
-
break
|
|
462
|
-
except:
|
|
463
|
-
pass
|
|
464
|
-
|
|
465
|
-
time.sleep(4)
|
|
466
|
-
code_insertion = False
|
|
467
|
-
for _ in range(30):
|
|
468
|
-
try:
|
|
469
|
-
# input('\n\n > Selecione o sistema e aperte alguma tecla.')
|
|
470
|
-
tools.find_element_with_wait(By.XPATH, f'{element_sistema}', timeout=1).send_keys(self.system)
|
|
471
|
-
time.sleep(1)
|
|
472
|
-
list_sistemas = tools.find_elements_with_wait(By.XPATH, '//div[contains(@role,"option")]')
|
|
473
|
-
if len(list_sistemas) == 1:
|
|
474
|
-
list_sistemas[0].click()
|
|
475
|
-
code_insertion = True
|
|
476
|
-
break
|
|
477
|
-
else:
|
|
478
|
-
mostrar_mensagem(
|
|
479
|
-
'Mais de um sistema encontrado, verifique o nome do sistema no WHOOM e coloque um nome único na função')
|
|
480
|
-
raise ValueError('Mais de um sistema encontrado, verifique o nome')
|
|
481
|
-
except:
|
|
482
|
-
pass
|
|
483
433
|
|
|
484
434
|
if code_insertion:
|
|
485
435
|
break
|
|
486
436
|
|
|
487
|
-
if code_insertion:
|
|
488
|
-
tools.find_element_with_wait(By.XPATH, f"//button[contains(@data-testid,'submit') or contains(text(), 'Acessar')]").click()
|
|
489
|
-
print('Logado')
|
|
490
|
-
else:
|
|
437
|
+
if not(code_insertion):
|
|
491
438
|
raise TimeoutError('Código WHOOM não chegou dentro do timeout estabelecido')
|
|
492
439
|
|
|
493
|
-
|
|
440
|
+
def select_system(self):
|
|
441
|
+
time.sleep(4)
|
|
442
|
+
try:
|
|
443
|
+
tools.find_element_with_wait(By.XPATH, '//input[@placeholder="Insira aqui o seu email"]',
|
|
444
|
+
timeout=1).click()
|
|
445
|
+
time.sleep(1)
|
|
446
|
+
self.codes_2_fac()
|
|
447
|
+
return
|
|
448
|
+
except:
|
|
449
|
+
pass
|
|
450
|
+
|
|
451
|
+
# Selects the system to access
|
|
452
|
+
lines = tools.find_elements_with_wait(By.XPATH, '//div[@role="menu"]//div[@role="menuitem"]')
|
|
453
|
+
|
|
454
|
+
finded = False
|
|
455
|
+
div_list = []
|
|
456
|
+
|
|
457
|
+
# This loop extract the lines with the system name
|
|
458
|
+
for line in lines:
|
|
459
|
+
|
|
460
|
+
titulo_element = line.find_elements(By.XPATH, './span[*[name()="svg"]]')
|
|
461
|
+
|
|
462
|
+
if not titulo_element and not finded:
|
|
463
|
+
continue
|
|
464
|
+
|
|
465
|
+
elif titulo_element and not finded:
|
|
466
|
+
titulo_text = line.find_element(By.XPATH, './span').text
|
|
467
|
+
if self.certificate.lower().strip() in titulo_text.lower().strip():
|
|
468
|
+
finded = True
|
|
469
|
+
continue
|
|
470
|
+
|
|
471
|
+
elif not titulo_element and finded:
|
|
472
|
+
div_list.append(line)
|
|
473
|
+
|
|
474
|
+
elif titulo_element and finded:
|
|
475
|
+
break
|
|
476
|
+
|
|
477
|
+
if not div_list:
|
|
478
|
+
mostrar_mensagem('Não conseguiu achar o sistema no certificado')
|
|
479
|
+
raise ValueError('Não conseguiu achar o sistema no certificado')
|
|
480
|
+
|
|
481
|
+
if len(div_list) == 1:
|
|
482
|
+
time.sleep(1)
|
|
483
|
+
div_list[0].click()
|
|
484
|
+
|
|
485
|
+
# Just do this loop if there are more than one system
|
|
486
|
+
else:
|
|
487
|
+
for div in div_list:
|
|
488
|
+
div_text = div.find_element(By.XPATH, './span').text
|
|
489
|
+
if self.system.lower().strip() not in div_text.lower().strip():
|
|
490
|
+
div_list.remove(div)
|
|
491
|
+
|
|
492
|
+
if len(div_list) == 1:
|
|
493
|
+
div_list[0].click()
|
|
494
|
+
else:
|
|
495
|
+
mostrar_mensagem('Mais de um sistema encontrado, verifique o nome do sistema no WHOOM e coloque um nome único na função')
|
|
496
|
+
raise ValueError('Mais de um sistema encontrado, verifique o nome')
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
# Verifies if the system was opened
|
|
500
|
+
for _ in range(30):
|
|
501
|
+
time.sleep(1)
|
|
502
|
+
if len(self.driver.window_handles) == 1:
|
|
503
|
+
try:
|
|
504
|
+
tools.find_element_with_wait(By.XPATH, "//button[text()='Acessar']", timeout=1).click()
|
|
505
|
+
except:
|
|
506
|
+
time.sleep(1)
|
|
507
|
+
else:
|
|
508
|
+
time.sleep(3)
|
|
509
|
+
break
|
|
510
|
+
|
|
494
511
|
self.driver.switch_to.window(self.driver.window_handles[-1])
|
|
495
512
|
attempt = 0
|
|
496
513
|
while 'whoom' in self.driver.title.strip().lower() and attempt <= 180:
|
|
497
|
-
if 'Sistema temporariamente indisponível. Tente novamente mais tarde.' in self.driver.page_source:
|
|
498
|
-
raise SystemError('Sistema indisponivel no momento.')
|
|
499
514
|
time.sleep(1)
|
|
500
515
|
attempt += 1
|
|
501
516
|
time.sleep(5)
|
|
@@ -519,4 +534,5 @@ def login_2fac(driver, certificate, system, token, code_timeout=10):
|
|
|
519
534
|
# Operacional
|
|
520
535
|
bot.extension_check()
|
|
521
536
|
protection.start()
|
|
522
|
-
bot.codes_2_fac()
|
|
537
|
+
bot.codes_2_fac()
|
|
538
|
+
bot.select_system()
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: bcpkgfox
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.7
|
|
4
4
|
Summary: Biblioteca BCFOX
|
|
5
5
|
Home-page: https://github.com/robotsbcfox/PacotePythonBCFOX
|
|
6
|
-
Author:
|
|
7
|
-
Author-email:
|
|
6
|
+
Author: BCFOX
|
|
7
|
+
Author-email: bcfox@bcfox.com.br
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
@@ -42,3 +42,12 @@ Requires-Dist: pymupdf; extra == "full"
|
|
|
42
42
|
Requires-Dist: Pillow; extra == "full"
|
|
43
43
|
Requires-Dist: psutil; extra == "full"
|
|
44
44
|
Requires-Dist: pynput; extra == "full"
|
|
45
|
+
Dynamic: author
|
|
46
|
+
Dynamic: author-email
|
|
47
|
+
Dynamic: classifier
|
|
48
|
+
Dynamic: description-content-type
|
|
49
|
+
Dynamic: home-page
|
|
50
|
+
Dynamic: provides-extra
|
|
51
|
+
Dynamic: requires-dist
|
|
52
|
+
Dynamic: requires-python
|
|
53
|
+
Dynamic: summary
|
|
@@ -2,9 +2,9 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="bcpkgfox",
|
|
5
|
-
version="0.17.
|
|
6
|
-
author="
|
|
7
|
-
author_email="
|
|
5
|
+
version="0.17.7",
|
|
6
|
+
author="BCFOX",
|
|
7
|
+
author_email="bcfox@bcfox.com.br",
|
|
8
8
|
description="Biblioteca BCFOX",
|
|
9
9
|
long_description=open("README.md").read(),
|
|
10
10
|
long_description_content_type="text/markdown",
|
|
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
|