bcpkgfox 0.17.4__py3-none-any.whl → 0.17.6__py3-none-any.whl

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/invoke_api.py CHANGED
@@ -422,6 +422,7 @@ def login_2fac(driver, certificate, system, token, code_timeout=60):
422
422
  if not(code_insertion):
423
423
  raise TimeoutError('Código WHOOM não chegou dentro do timeout estabelecido')
424
424
 
425
+ time.sleep(4)
425
426
  # Selects the system to access
426
427
  lines = tools.find_elements_with_wait(By.XPATH, '//div[@role="menu"]//div[@role="menuitem"]')
427
428
 
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bcpkgfox
3
- Version: 0.17.4
3
+ Version: 0.17.6
4
4
  Summary: Biblioteca BCFOX
5
5
  Home-page: https://github.com/robotsbcfox/PacotePythonBCFOX
6
- Author: Guilherme Neri
7
- Author-email: guilherme.neri@bcfox.com.br
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
@@ -0,0 +1,12 @@
1
+ bcpkgfox/__init__.py,sha256=49TPbTUNI6xKcYk4blwseUZQTgHtRcNI1AFEqxSQ3wY,16540
2
+ bcpkgfox/clean.py,sha256=80pJDTGmKmPiq73uL1IWopuxqVJF_bj_RVv-njkpl-A,8946
3
+ bcpkgfox/cli.py,sha256=E1Yahd8jIjUwxM6EMHveDDne5-fh8QeAvAhyATNatEo,33541
4
+ bcpkgfox/find_elements.py,sha256=oeB-73LqMLoKchozPXuxRkThBju9IgUKqbgU-2AAq0s,23027
5
+ bcpkgfox/get_driver.py,sha256=ohimk9E2hL6T35IXv0XX0uvWDGCUZvZDlPMnuRjV1R0,30490
6
+ bcpkgfox/invoke_api.py,sha256=-aI5DkyNhwDSyven8iL8b71PylP_u3KDAHXRlYQXclA,18996
7
+ bcpkgfox/system.py,sha256=3lyOWx893T6KiAI-jDv7zAo3oKPf0Q5CLgZ8TeFd0Do,7901
8
+ bcpkgfox-0.17.6.dist-info/METADATA,sha256=0vxTKCMpLOJXygLII9PCiTJCrf9kC_1r13gY8Bda9yY,1893
9
+ bcpkgfox-0.17.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ bcpkgfox-0.17.6.dist-info/entry_points.txt,sha256=qmaEg6K7Y0HOeaFo-G6lf44InGkeVI4I6hqobcY_nns,653
11
+ bcpkgfox-0.17.6.dist-info/top_level.txt,sha256=h01SqyYBEfS72vkRFOlEDZBUSu9pzU0bdX4m9hWNNmw,9
12
+ bcpkgfox-0.17.6.dist-info/RECORD,,
@@ -1,227 +0,0 @@
1
- '''
2
- A grande maioria das classes e funções tem descrição explicando com detalhes o funcionamento e parametros
3
- Para ver clicar na classe/função e apertar ctrl+K ctrl+I (VSCODE)
4
- '''
5
- import neri_library as nr # Só precisa de um import para
6
- from neri_library import DK_ORANGE, ORANGE, GR, RD, RESET # Colors for terminal
7
-
8
- # Default
9
- import time
10
- import sys
11
- import os
12
-
13
- class Browser():
14
- def __init__(self):
15
- super().__init__()
16
-
17
- self.arguments = None
18
- self.instance = None
19
- self.browser = None
20
- self.el = None
21
-
22
- @staticmethod
23
- def __simple_example():
24
- '''
25
- Aqui é só um exemplo de como é fácil iniciar um navegador com a lib.
26
- Ja vem com configurações para evitar detecção e passar por captchas.
27
- '''
28
- Browser = nr.Instancedriver().initialize_driver()
29
- Browser.get('URL SITE')
30
-
31
-
32
- def browser_iniciate(self):
33
- '''
34
- Aqui é um exemplo de driver em produção.
35
- '''
36
- print(f'{ORANGE} [ Inicializando Browser ] {RESET}')
37
-
38
- # Inicia a instância do Navegador
39
- self.instance = nr.Instancedriver(
40
- Browser= 'Chrome',
41
- # Browser="Firefox",
42
- # Browser="edge",
43
- # Browser="internet explorer",
44
- )
45
-
46
- # Inicia a instancia dos argumentos (opcional, não precisa por nenhum)
47
- self.arguments = self.instance.arguments # Instância dos argumentos
48
-
49
- # Fix: Abaixo alguns dos principais exemplos de argumento do selenium (tem todos)
50
- # Exemplos de args para desligar indicadores de automação
51
- self.arguments.add_experimental_option("excludeSwitches", ["enable-automation"])
52
- self.arguments.add_experimental_option("useAutomationExtension", False)
53
- self.arguments.add_argument("--disable-blink-features=AutomationControlled")
54
- self.arguments.add_argument("--disable-blink-features")
55
- self.arguments.add_argument("--disable-infobars")
56
- self.arguments.add_argument("--disable-blink-features=AutomationControlled")
57
- self.arguments.add_argument("--blink-settings=imagesEnabled=false")
58
-
59
- # Exemplos de args para evitar detecção da automação nos sites e captchas
60
- # Fix: Mas ja vem por padrão diversos argumentos tratando isso na função de 'initialize_driver'
61
- self.arguments.add_argument("--disable-background-networking")
62
- self.arguments.add_argument("--disable-sync")
63
- self.arguments.add_argument("--disable-client-side-phishing-detection")
64
- self.arguments.add_argument("--disable-popup-blocking")
65
- self.arguments.add_argument("--disable-default-apps")
66
- self.arguments.add_argument("--disable-features=IsolateOrigins,site-per-process")
67
- self.arguments.add_argument("--disable-features=BlockInsecurePrivateNetworkRequests")
68
- self.arguments.add_argument("--ignore-certificate-errors")
69
- self.arguments.add_argument("--disable-gpu")
70
-
71
- # Abaixo alguns exemplos para remover os logs do terminal (deixar mais limpo)
72
- self.arguments.add_argument("--log-level=3")
73
- self.arguments.add_experimental_option("excludeSwitches", ["enable-logging"])
74
- self.arguments.add_experimental_option("useAutomationExtension", False)
75
-
76
- # Exemplo de adicionar extensão (pega automático tanto pasta como .crx)
77
- # self.instance.add_extensions('your_extension')
78
-
79
- # Inicia o browser (driver)
80
- self.browser = self.instance.initialize_driver(maximize=True)
81
-
82
- # Instancia o 'finder', contém todas as funções de busca envolvendo o navegador
83
- self.finder = self.instance.elements
84
-
85
- class RPA_example(Browser):
86
- def __init__(self):
87
- super().__init__()
88
-
89
- def open_site(self):
90
- self.driver.get('https://github.com/NeriAzv')
91
-
92
- self.cidade = cidade
93
- self.pop_up_protection.start()
94
-
95
- # Pesquisa
96
- pesquisa = self.find_element_w(By.ID, 'searchGeneral')
97
- time.sleep(1)
98
- pesquisa.send_keys(cidade)
99
-
100
- # Espera carregamento pesquisa
101
- tempo = 0
102
- while tempo < 30:
103
- resultados = self.find_elements_w(By.XPATH, '//ul[@id="autocompleteSuggestions"]//li')
104
-
105
- if resultados:
106
- break
107
- else:
108
- time.sleep(1)
109
- tempo += 1
110
-
111
- # Resutaldos pesquisa
112
- elemento_resultados_pesquisa = self.find_element_w(By.ID, 'autocompleteSuggestions')
113
- soup = BeautifulSoup(elemento_resultados_pesquisa.get_attribute('outerHTML'), 'html.parser')
114
- ul = soup.find('ul', id='autocompleteSuggestions')
115
-
116
- cidades_extraidas = []
117
- extrair = False
118
- for tag in ul.find_all(recursive=False):
119
- if tag.name == 'h6':
120
- if 'Cidades' in tag.text:
121
- extrair = True
122
- continue
123
- elif extrair:
124
- break
125
-
126
- if extrair and tag.name == 'li':
127
- cidades_extraidas.append(tag)
128
-
129
- if not cidades_extraidas:
130
- raise Exception(f"{RED} > Cidade {cidade} não presente no site, verifique o nome. {RESET}")
131
-
132
- # Eu poderia ter clicado no ultimo for, mas estou fazendo separado pegar todas as cidades e fazer um LOG mais organizado
133
- for cidade_site in cidades_extraidas:
134
- cidade_site_formatado = self.remover_acentos(cidade_site.text).lower()
135
- cidade_user_formatado = self.remover_acentos(cidade).lower()
136
-
137
- # Retorna em porcentagem quanto similar está com o nome do site (evita erros de usuários)
138
- if cidade_site_formatado in cidade_site_formatado:
139
-
140
- self.find_element_w(
141
- By.XPATH,
142
- f'//ul[@id="autocompleteSuggestions"]//li[normalize-space()="{cidade_site.text.strip()}"]'
143
- ).click()
144
-
145
- return
146
-
147
- raise Exception(f"{RED} > Cidade {cidade} não presente no site, verifique o nome. {RESET}")
148
-
149
- def extrair_previsao(self) -> dict:
150
-
151
- espera_site = 0
152
- while espera_site <= 5:
153
-
154
- try:
155
- soup = BeautifulSoup(self.driver.page_source, "html.parser")
156
- ul = soup.find("ul", class_="variables-list")
157
- resultado_json = {
158
- "cidade": self.cidade,
159
- "temperatura_min": None,
160
- "temperatura_max": None,
161
- "condicao": None,
162
- "umidade_min": None,
163
- "umidade_max": None,
164
- "vento": None,
165
- }
166
-
167
- if not ul:
168
- return resultado_json
169
-
170
- for li in ul.find_all("li", class_="item"):
171
- label = li.find("span", class_="variable").get_text(strip=True)
172
-
173
- if label == "Temperatura":
174
- spans = li.select("span.-gray-light")
175
- min_txt = spans[0].get_text(strip=True)
176
- max_txt = spans[1].get_text(strip=True)
177
- resultado_json["temperatura_min"] = min_txt if min_txt.endswith("°") else min_txt + "°"
178
- resultado_json["temperatura_max"] = max_txt if max_txt.endswith("°") else max_txt + "°"
179
-
180
- elif label == "Umidade":
181
- spans = li.select("span.-gray-light")
182
- resultado_json["umidade_min"] = spans[0].get_text(strip=True)
183
- resultado_json["umidade_max"] = spans[1].get_text(strip=True)
184
-
185
- elif label == "Vento":
186
- texto = li.find("div", class_="_flex").get_text(separator=" ", strip=True)
187
- texto_limpo = " ".join(texto.split())
188
- resultado_json["vento"] = texto_limpo.replace("Vento", "", 1).strip()
189
-
190
- elif label == "Sol":
191
- nasc, _, pst = li.find("span", recursive=False).get_text(strip=True).partition(" ")
192
- resultado_json["condicao"] = f"{nasc} {pst}"
193
-
194
- return resultado_json
195
-
196
- except:
197
- time.sleep(1)
198
- espera_site +=1
199
-
200
-
201
-
202
- class Logs:
203
- ''' Server somente para impressão de resultados, não afeta o funcionamento do robo '''
204
- def imprimir_logs(self, log_resultado: dict):
205
- if not log_resultado:
206
- return
207
-
208
- print(f"\n{BOLD}{BLUE} {log_resultado.get('cidade', '').title()}:{RESET}")
209
-
210
- # Itera sobre o dict
211
- for chave, valor in log_resultado.items():
212
- if chave == "cidade":
213
- continue
214
- print(f" - {BLUE}{chave.capitalize()}:{RESET} {valor}")
215
-
216
- def main():
217
-
218
- # Instâncias
219
- br = Browser()
220
- rpa = RPA_example()
221
-
222
- br.browser_iniciate()
223
- rpa.open_site()
224
-
225
- if __name__ == "__main__":
226
- print(f'{DK_ORANGE} [ Robo Iniciado ] {RESET}')
227
- main()
bcpkgfox/examples.py DELETED
@@ -1,153 +0,0 @@
1
-
2
- # FIX: Driver
3
- # region Driver
4
- """
5
- Example of how easy is start the driver.
6
-
7
- It automatically configure the driver with stealth settings to pass over the captchas.
8
- It also automatically prevents a various bugs like versions incompatibles and others.
9
- """
10
- import neri_library as nr
11
-
12
- # To choose the browser just change the name
13
- driver = nr.Instancedriver(
14
- Browser="chrome"
15
- # Browser="Firefox"
16
- # Browser="edge"
17
- # Browser="internet explorer"
18
- ).initialize_driver()
19
-
20
- driver.get("https://www.google.com")
21
-
22
-
23
-
24
- # FIX: Driver Options
25
- # region Driver Options
26
- """
27
- You can also use arguments to configure the driver manually
28
- """
29
-
30
- import neri_library as nr
31
-
32
- instance = nr.Instancedriver(Browser="chrome")
33
- instance.initialize_options() # Iniciate the options mode
34
-
35
- # Here you can add as many as you want
36
- instance.arguments.add_new_argument("--headless") # Example
37
- instance.arguments.add_experimental_option("useAutomationExtension", False) # Example
38
- instance.arguments.add_experimental_options("excludeSwitches", ["enable-automation"]) # Example
39
-
40
- # You can add a extension with one line too (already have 'resourse path')
41
- instance.add_extension("path/to/extension.crx")
42
-
43
- driver = instance.initialize_driver()
44
- driver.get("https://www.google.com")
45
-
46
-
47
-
48
- # FIX: Selenoid
49
- # region Selenoid
50
- """
51
- Functions of selenoid (docker)
52
-
53
- The driver comes already configured to run in selenoid, but you can also configure it manually.
54
- """
55
-
56
- import neri_library as nr
57
-
58
- instance = nr.Instancedriver(Browser="chrome", Selenoid=True)
59
-
60
- # Example if you wanna to customize
61
- instance.Selenoid.add_capabilities("enableVNC", True) # Example
62
- instance.Selenoid.add_capabilities("versiion", "122.0") # Example
63
-
64
- driver = instance.initialize_driver()
65
- driver.get("https://www.google.com")
66
-
67
-
68
-
69
- # FIX: Elements
70
- #region Elements
71
- """
72
- Some features of selenium that i improve to prevents bugs and work correctly.
73
-
74
- (you only need import the neri_library)
75
- """
76
- import neri_library as nr
77
- from neri_library import By # Optional to imitating selenium, you can use 'xpath' in place of By.XPATH for example
78
-
79
- instance = nr.Instancedriver()
80
- driver = instance.initialize_driver()
81
- finder = instance.elements
82
-
83
- # Selenium searchers
84
- example = finder.find_element_with_wait(By.XPATH, '/your/xpath/here') # Have too XPATH, CSS, NAME, ID...
85
- example_list = finder.find_elements_with_wait(By.XPATH, '/this/brings/a/list') # work same as the fn above (this have a 's' in the name)
86
-
87
- # Selenium interactions works normally too
88
- finder.find_element_with_wait(By.XPATH, '/elmt/xpath').send_keys('')
89
- finder.find_element_with_wait(By.NAME, 'elmt_name').clear()
90
- finder.find_element_with_wait(By.ID, 'elmt_id').click()
91
-
92
-
93
-
94
- # FIX: Elemets created
95
- #region Creations
96
- """
97
- Below some functions that i created for web scraping
98
- """
99
- import neri_library as nr
100
- from neri_library import By
101
-
102
- instance = nr.Instancedriver()
103
- driver = instance.initialize_driver()
104
- finder = instance.elements
105
-
106
- # Just a example to uso on the functions below
107
- example_element = finder.find_element_with_wait(By.XPATH, '/example')
108
-
109
- '''
110
- This function moves the mouse to an element imitating human behavior, to avoid captchas.
111
- With some deviations and inaccuracies on the way to the destination (like a human).
112
- ( It use a external UI methods, so the browser can't detect )
113
- '''
114
- finder.move_mouse_smoothly(
115
- element = example_element,
116
- pure = True, # Pure is to you pass the cordinates instead the element
117
- click = False,
118
- x_adittional = 100, # Here you can add a number on cordinates (if the elements is bugged)
119
- y_adittional = 100
120
- )
121
-
122
- # Find a image in the screen (you can pass a screenshot of a element)
123
- finder.move_to_image(
124
- 'img_path.png', # You can send a list to search for more than one
125
- click_on_final = True,
126
- trail = True, # Move your mouse using 'move_mouse_smoothly' above (if False will teleport you mouse)
127
- verify = True, # Just verify if the image is on the screen (Deactivates the 'trail' and 'click')
128
- tolerancia = 0.8, # Deviation tolerance of the found image (0.8 = 80%)
129
- timeout = 10, # The time that function will search the image(s)
130
- repeat = True # It deactivates the 'timeout'
131
- )
132
-
133
- # It will respect the timeout ignoring the "ClickInterrupted" errors
134
- finder.wait_for_element_be_clickable(By.CLASS_NAME, 'elmt_class', timeout=10)
135
-
136
- # It is a 'driver.execute_script' of the selenium, but with a timeout
137
- finder.script_console_with_wait('Returns element.value', timeout = 10)
138
-
139
- '''
140
- You can use this function below to wait for a image, window, element or text.
141
- '''
142
- finder.wait_for_appear(
143
- object = '', # title_window / img_path / text / element
144
- type = '', # window / image / text / element (here you really write the type)
145
- timeout = 10
146
- )
147
-
148
- finder.wait_for_appear( # finder.wait_for_appear( # finder.wait_for_appear( # finder.wait_for_appear(
149
- object = 'Window title', # object = 'C://image_path', # object = 'Site Text', # object = '/xpath/',
150
- type = 'window', # type = 'image', # type = 'text', # type = 'element',
151
- timeout = 10 # timeout = 10 # timeout = 10 # timeout = 10
152
- ) # ) # ) # )
153
- finder.wait_for_disappear() # Same thing
bcpkgfox/exec_file.py DELETED
@@ -1,100 +0,0 @@
1
- import subprocess
2
- import threading
3
- import argparse
4
- import time
5
- import sys
6
- import os
7
-
8
- def main():
9
-
10
- class visual():
11
- def __init__(self):
12
- self.RESET = "\033[0m"
13
- self.DK_ORANGE = "\033[38;5;130m"
14
- self.Neg = "\033[1m"
15
- self.hue = 0
16
-
17
- def hsl_to_rgb(self, h, s, l):
18
- h = h % 360
19
- c = (1 - abs(2 * l - 1)) * s
20
- x = c * (1 - abs((h / 60) % 2 - 1))
21
- m = l - c / 2
22
-
23
- if 0 <= h < 60: r, g, b = c, x, 0
24
- elif 60 <= h < 120: r, g, b = x, c, 0
25
- elif 120 <= h < 180: r, g, b = 0, c, x
26
- elif 180 <= h < 240: r, g, b = 0, x, c
27
- elif 240 <= h < 300: r, g, b = x, 0, c
28
- elif 300 <= h < 360: r, g, b = c, 0, x
29
-
30
- r = int((r + m) * 255) ; g = int((g + m) * 255) ; b = int((b + m) * 255)
31
- return r, g, b
32
-
33
- def rgb_text(self, text, r, g, b): return f"\033[38;2;{r};{g};{b}m{text}\033[0m"
34
-
35
- def animate_rgb_text(self, text, delay=0.01):
36
- r, g, b = self.hsl_to_rgb(self.hue, s=1.0, l=0.5)
37
- self.hue = (self.hue + 1) % 360
38
- time.sleep(delay)
39
- return f" \033[1m{self.rgb_text(text, r, g, b)}\033[0m"
40
-
41
- class exec_gen():
42
- def __init__(self):
43
- self.current_dir = None
44
- self.target_file = None
45
- self.file_name = None
46
-
47
- def preparations(self):
48
- self.current_dir = os.getcwd()
49
-
50
- parser = argparse.ArgumentParser(description="Script to generate .exe and preventing bugs")
51
- parser.add_argument("file", type=str, help="Put the name of file after the command (with the extension '.py')")
52
-
53
- args = parser.parse_args()
54
- self.file_name = args.file
55
- self.target_file = os.path.join(self.current_dir, self.file_name)
56
-
57
- if not os.path.exists(self.target_file):
58
- print(f"Error: File '{self.target_file}' does not exist.")
59
- return
60
-
61
- def run_pyinstaller(self):
62
- global process_finished
63
-
64
- def print_footer():
65
- """Função que mantém a mensagem 'Aguarde download' na última linha."""
66
- while not process_finished:
67
- sys.stdout.write(f"\r \033[F\r\033[K\033[E {visuals.animate_rgb_text(f" {visuals.Neg}| Gerando executável do '{self.file_name}', aguarde finalização. |{visuals.RESET}")}\n\033[F")
68
- sys.stdout.flush()
69
-
70
- process_finished = False
71
-
72
- command = ["pyinstaller", self.target_file]
73
- process = subprocess.Popen(
74
- command,
75
- stdout=subprocess.PIPE,
76
- stderr=subprocess.STDOUT,
77
- universal_newlines=True,
78
- bufsize=1
79
- )
80
- footer_thread = threading.Thread(target=print_footer)
81
- footer_thread.start()
82
-
83
- # Lê a saída do PyInstaller em tempo real
84
- while True:
85
- output = process.stdout.readline()
86
- if output == '' and process.poll() is not None:
87
- break
88
- if output:
89
- sys.stdout.write(f"\033[F\r\033[K{output.strip()}\033[K\n\n")
90
- sys.stdout.flush()
91
-
92
- process_finished = True
93
- footer_thread.join()
94
-
95
- print(f"\r \033[F\r\033[K\033[f\r\033[K\033[2E{visuals.Neg}{visuals.DK_ORANGE}>{visuals.RESET}{visuals.Neg} Executável gerado com sucesso!\n{visuals.RESET}\033[3E")
96
-
97
- script = exec_gen()
98
- visuals = visual()
99
- script.preparations()
100
- script.run_pyinstaller()
@@ -1,15 +0,0 @@
1
- bcpkgfox/__init__.py,sha256=49TPbTUNI6xKcYk4blwseUZQTgHtRcNI1AFEqxSQ3wY,16540
2
- bcpkgfox/clean.py,sha256=80pJDTGmKmPiq73uL1IWopuxqVJF_bj_RVv-njkpl-A,8946
3
- bcpkgfox/cli.py,sha256=E1Yahd8jIjUwxM6EMHveDDne5-fh8QeAvAhyATNatEo,33541
4
- bcpkgfox/examples copy.py,sha256=EbiMy2mM1GT8juArugC9--pJzt0W20mIaJ7SuPGJvPE,9110
5
- bcpkgfox/examples.py,sha256=gNKQBlONca0W81QHwUlnv_-J135dEj9IwBhCpmohMYw,5486
6
- bcpkgfox/exec_file.py,sha256=fl_Do2SlF7JuXazpNTod-e_0WZUk355fbd7ustQvi40,3728
7
- bcpkgfox/find_elements.py,sha256=oeB-73LqMLoKchozPXuxRkThBju9IgUKqbgU-2AAq0s,23027
8
- bcpkgfox/get_driver.py,sha256=ohimk9E2hL6T35IXv0XX0uvWDGCUZvZDlPMnuRjV1R0,30490
9
- bcpkgfox/invoke_api.py,sha256=GTokMMeHkQH-fMLnCY_GoEc-48bk3GtLZgZQSuNonSs,18969
10
- bcpkgfox/system.py,sha256=3lyOWx893T6KiAI-jDv7zAo3oKPf0Q5CLgZ8TeFd0Do,7901
11
- bcpkgfox-0.17.4.dist-info/METADATA,sha256=mvHFmIOXkbUGOBvKsAOpvVKvf6RmQTtjdLyljIGH53E,1911
12
- bcpkgfox-0.17.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
- bcpkgfox-0.17.4.dist-info/entry_points.txt,sha256=qmaEg6K7Y0HOeaFo-G6lf44InGkeVI4I6hqobcY_nns,653
14
- bcpkgfox-0.17.4.dist-info/top_level.txt,sha256=h01SqyYBEfS72vkRFOlEDZBUSu9pzU0bdX4m9hWNNmw,9
15
- bcpkgfox-0.17.4.dist-info/RECORD,,