csc-cia-stne 0.0.56__tar.gz → 0.1.49__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.
Files changed (58) hide show
  1. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/PKG-INFO +13 -2
  2. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/pyproject.toml +12 -2
  3. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/__init__.py +11 -1
  4. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/bc_correios.py +22 -16
  5. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/bc_sta.py +288 -6
  6. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/email.py +15 -6
  7. csc_cia_stne-0.1.49/src/csc_cia_stne/ftp.py +333 -0
  8. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/gcp_bigquery.py +4 -11
  9. csc_cia_stne-0.1.49/src/csc_cia_stne/gcp_bucket.py +319 -0
  10. csc_cia_stne-0.1.49/src/csc_cia_stne/gcp_document_ai.py +100 -0
  11. csc_cia_stne-0.1.49/src/csc_cia_stne/google_drive.py +990 -0
  12. csc_cia_stne-0.1.49/src/csc_cia_stne/jerry.py +694 -0
  13. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/logger_rich.py +10 -119
  14. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/servicenow.py +362 -209
  15. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/slack.py +63 -23
  16. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/stne_admin.py +131 -20
  17. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/__init__.py +17 -0
  18. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/functions/__init__.py +11 -1
  19. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/functions/func_datetime.py +8 -0
  20. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/functions/func_delete.py +96 -0
  21. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/functions/func_get_secret.py +77 -0
  22. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/functions/func_pdf_extract.py +115 -0
  23. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/functions/func_validate_json.py +49 -0
  24. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/validations/GcpBigQueryValidator.py +6 -11
  25. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/validations/GoogleDriveValidator.py +7 -0
  26. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/validations/ftp.py +109 -0
  27. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/validations/gcp_bucket.py +170 -0
  28. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/validations/waccess.py +215 -0
  29. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/validations/web_validator.py +10 -1
  30. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/web_screen/__init__.py +2 -0
  31. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/web_screen/web_screen_abstract.py +84 -0
  32. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/web_screen/web_screen_botcity.py +288 -0
  33. csc_cia_stne-0.1.49/src/csc_cia_stne/utilitarios/web_screen/web_screen_selenium.py +391 -0
  34. csc_cia_stne-0.1.49/src/csc_cia_stne/wacess.py +836 -0
  35. csc_cia_stne-0.1.49/src/csc_cia_stne/web.py +82 -0
  36. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne.egg-info/PKG-INFO +13 -2
  37. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne.egg-info/SOURCES.txt +17 -1
  38. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne.egg-info/requires.txt +10 -0
  39. csc_cia_stne-0.0.56/src/csc_cia_stne/google_drive.py +0 -268
  40. csc_cia_stne-0.0.56/src/csc_cia_stne/utilitarios/__init__.py +0 -10
  41. csc_cia_stne-0.0.56/src/csc_cia_stne/utilitarios/functions/func_get_secret.py +0 -67
  42. csc_cia_stne-0.0.56/src/csc_cia_stne/web.py +0 -513
  43. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/LICENCE +0 -0
  44. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/README.md +0 -0
  45. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/README_PYPI.md +0 -0
  46. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/setup.cfg +0 -0
  47. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/karavela.py +0 -0
  48. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/logger_json.py +0 -0
  49. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/provio.py +0 -0
  50. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/functions/func_b64.py +0 -0
  51. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/functions/func_converters.py +0 -0
  52. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/functions/func_recriar_pastas.py +0 -0
  53. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/functions/func_settings.py +0 -0
  54. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/functions/func_titulo.py +0 -0
  55. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/validations/ServiceNowValidator.py +0 -0
  56. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne/utilitarios/validations/__init__.py +0 -0
  57. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne.egg-info/dependency_links.txt +0 -0
  58. {csc_cia_stne-0.0.56 → csc_cia_stne-0.1.49}/src/csc_cia_stne.egg-info/top_level.txt +0 -0
@@ -1,12 +1,13 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: csc_cia_stne
3
- Version: 0.0.56
3
+ Version: 0.1.49
4
4
  Summary: Biblioteca do time CSC-CIA utilizada no desenvolvimento de RPAs
5
5
  License: MIT
6
6
  Keywords: karavela,csc,cia,stone,rpa,botcity,stne
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENCE
9
9
  Requires-Dist: setuptools
10
+ Requires-Dist: tzdata
10
11
  Requires-Dist: python-json-logger
11
12
  Requires-Dist: rich
12
13
  Requires-Dist: requests
@@ -15,17 +16,27 @@ Requires-Dist: pydantic_settings
15
16
  Requires-Dist: zeep
16
17
  Requires-Dist: google-cloud-bigquery
17
18
  Requires-Dist: google-cloud-storage
19
+ Requires-Dist: google-cloud-bigquery-storage
20
+ Requires-Dist: google-auth
18
21
  Requires-Dist: google-auth-oauthlib
19
22
  Requires-Dist: google-auth-httplib2
20
23
  Requires-Dist: google-api-python-client
24
+ Requires-Dist: google-cloud-documentai
21
25
  Requires-Dist: pyjwt
22
26
  Requires-Dist: PyYAML
23
27
  Requires-Dist: python-dotenv
24
28
  Requires-Dist: slack_sdk
25
29
  Requires-Dist: webdriver-manager
30
+ Requires-Dist: botcity-framework-core
26
31
  Requires-Dist: botcity-framework-web
27
32
  Requires-Dist: email-validator
28
33
  Requires-Dist: botcity-maestro-sdk
34
+ Requires-Dist: psutil
35
+ Requires-Dist: cryptography
36
+ Requires-Dist: PyPDF2
37
+ Requires-Dist: openai
38
+ Requires-Dist: pycurl
39
+ Dynamic: license-file
29
40
 
30
41
  Essa biblioteca é desenvolvida e atualizada pelo time **CSC-CIA** da **Stone**
31
42
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "csc_cia_stne"
7
- version = "0.0.56"
7
+ version = "0.1.49"
8
8
  license = { text = "MIT" }
9
9
  description = "Biblioteca do time CSC-CIA utilizada no desenvolvimento de RPAs"
10
10
  keywords = ["karavela", "csc", "cia", "stone", "rpa", "botcity", "stne"]
@@ -13,6 +13,7 @@ readme = "README_PYPI.md" # Referência ao arquivo README
13
13
  # Dependências
14
14
  dependencies = [
15
15
  "setuptools",
16
+ "tzdata",
16
17
  "python-json-logger",
17
18
  "rich",
18
19
  "requests",
@@ -21,15 +22,24 @@ dependencies = [
21
22
  "zeep",
22
23
  "google-cloud-bigquery",
23
24
  "google-cloud-storage",
25
+ "google-cloud-bigquery-storage",
26
+ "google-auth",
24
27
  "google-auth-oauthlib",
25
28
  "google-auth-httplib2",
26
29
  "google-api-python-client",
30
+ "google-cloud-documentai",
27
31
  "pyjwt",
28
32
  "PyYAML",
29
33
  "python-dotenv",
30
34
  "slack_sdk",
31
35
  "webdriver-manager",
36
+ "botcity-framework-core",
32
37
  "botcity-framework-web",
33
38
  "email-validator",
34
- "botcity-maestro-sdk"
39
+ "botcity-maestro-sdk",
40
+ "psutil",
41
+ "cryptography",
42
+ "PyPDF2",
43
+ "openai",
44
+ "pycurl"
35
45
  ]
@@ -13,6 +13,11 @@ from .provio import Provio
13
13
  from .google_drive import GoogleDrive
14
14
  from .slack import Slack
15
15
  from .web import web_screen
16
+ from .wacess import Waccess
17
+ from .gcp_bucket import GCPBucket
18
+ from .ftp import FTP
19
+ from .gcp_document_ai import GCPDocumentAIClient
20
+ from .jerry import JerryClient
16
21
 
17
22
  # Define os itens disponíveis para importação
18
23
  __all__ = [
@@ -27,8 +32,13 @@ __all__ = [
27
32
  "Provio",
28
33
  "Email",
29
34
  "GoogleDrive",
35
+ "GCPDocumentAIClient"
30
36
  "Slack",
31
- "web_screen"
37
+ "web_screen",
38
+ "Waccess",
39
+ "GCPBucket",
40
+ "JerryClient"
41
+ "FTP"
32
42
  ]
33
43
 
34
44
  _diretorio_inicial = os.getcwd()
@@ -7,7 +7,7 @@ from requests.auth import HTTPBasicAuth
7
7
  from datetime import timedelta, datetime, time
8
8
  from typing import List, Tuple, Union, Optional
9
9
  from pydantic import BaseModel
10
-
10
+ log = logging.getLogger(__name__)
11
11
  class PastasAutorizadas(BaseModel):
12
12
  MensagemErro: Optional[str] = None
13
13
  OcorreuErro: bool
@@ -218,7 +218,7 @@ class BC_Correios:
218
218
  break
219
219
 
220
220
  for correio in correios:
221
-
221
+
222
222
  item_de_lista_de_correios = ItemListaCorreio(
223
223
  Assunto=correio.Assunto,
224
224
  Data=correio.Data,
@@ -248,17 +248,18 @@ class BC_Correios:
248
248
 
249
249
  if len(correios_filtrados) >= proxima_centena:
250
250
 
251
- logging.info(f"Página #{pagina_atual}: {len(correios_filtrados)} correios armazenados no total")
251
+ log.info(f"Página #{pagina_atual}: {len(correios_filtrados)} correios armazenados no total")
252
252
  proxima_centena = proxima_centena + 100
253
253
 
254
254
  # Avança para a próxima página
255
255
  pagina_atual += 1
256
256
 
257
- return True, correios_filtrados
258
-
257
+ #return True, correios_filtrados
258
+ return {"success": True, "data": correios_filtrados, "error": None}
259
+
259
260
  except Exception as e:
260
-
261
- return False, e
261
+
262
+ return {"success": False, "data": None, "error": str(e)}
262
263
 
263
264
  def ler_correio(self, numero:int,data_rebimento:datetime,tipo:str,transicao:int,versao:int,pasta:str)-> Tuple[bool, Union[ItemMsgCorreio, Exception]]:
264
265
  """
@@ -425,7 +426,8 @@ class BC_Correios:
425
426
  #novo end
426
427
 
427
428
  #return True, response
428
- return True, msg_detail
429
+ #return True, msg_detail
430
+ return {"success": True, "data": msg_detail, "error": None}
429
431
 
430
432
  # Serializa o objeto SOAP em um dicionário Python
431
433
  #serialized_response = serialize_object(response)
@@ -437,7 +439,8 @@ class BC_Correios:
437
439
 
438
440
  except Exception as e:
439
441
 
440
- return False, e
442
+ #return False, e
443
+ return {"success": False, "data": None, "error": str(e)}
441
444
 
442
445
  def obter_anexo(self, numero:int, versao:int, transicao:int,pasta:str,anexo_id:int,file_name:str,conteudo:str)-> Tuple[bool,Union[dict, Exception]]:
443
446
  """
@@ -504,17 +507,20 @@ class BC_Correios:
504
507
  conteudo_anexo = response.Anexo.Conteudo
505
508
 
506
509
  # Retorna os dados capturados como um dicionário
507
- return True, {
508
- 'IdAnexo': id_anexo,
509
- 'NomeAnexo': nome_anexo,
510
- 'Conteudo': conteudo_anexo
511
- }
510
+ #return True, {
511
+ # 'IdAnexo': id_anexo,
512
+ # 'NomeAnexo': nome_anexo,
513
+ # 'Conteudo': conteudo_anexo
514
+ #}
515
+ return {"success": True, "error": None, "data": conteudo_anexo}
512
516
  else:
513
- return False, Exception("Correio não possui anexo")
517
+ return {"success": True, "error": "Correio não possui anexo", "data": None}
518
+ #return False, Exception("Correio não possui anexo")
514
519
 
515
520
  except Exception as e:
516
521
 
517
- return False, e
522
+ return {"success": False, "error": str(e), "data": None}
523
+ #return False, e
518
524
 
519
525
  def encerrar(self):
520
526
  """Fecha o cliente e libera a sessão."""
@@ -1,9 +1,57 @@
1
- from requests.auth import HTTPBasicAuth
1
+ import logging
2
+ import hashlib
2
3
  import requests
4
+ from requests.auth import HTTPBasicAuth
3
5
  import xml.etree.ElementTree as ET
4
- import hashlib
5
- from pydantic import BaseModel, ValidationError, field_validator
6
- from typing import Literal, Dict, Union, Optional
6
+ from xml.dom.minidom import parseString
7
+ from pydantic import BaseModel, ValidationError, field_validator, Field, HttpUrl
8
+ from typing import Literal, Dict, Union, Optional, List
9
+ from datetime import datetime, timedelta
10
+
11
+ log = logging.getLogger('__main__')
12
+
13
+ def xml_to_dict(element):
14
+ """Converte um elemento XML recursivamente para um dicionário."""
15
+ if not list(element): # Se não tem filhos, retorna apenas o texto
16
+ return element.text.strip() if element.text else ""
17
+
18
+ result = {}
19
+ for child in element:
20
+ child_data = xml_to_dict(child)
21
+ if child.tag in result:
22
+ if isinstance(result[child.tag], list):
23
+ result[child.tag].append(child_data)
24
+ else:
25
+ result[child.tag] = [result[child.tag], child_data]
26
+ else:
27
+ result[child.tag] = child_data
28
+
29
+ if element.attrib:
30
+ result["@atributos"] = element.attrib # Adiciona atributos XML se existirem
31
+
32
+ return result
33
+
34
+ def xml_response_to_json(response_text):
35
+ """Converte a resposta XML para um dicionário JSON válido."""
36
+
37
+ root = ET.fromstring(response_text)
38
+ lista = xml_to_dict(root)
39
+ if not isinstance(lista, dict):
40
+ return []
41
+ return list(lista.values())[0] # Agora retorna um dicionário em vez de uma string JSON
42
+
43
+ def print_element(element, indent=0):
44
+ """Função recursiva para exibir campos e subcampos"""
45
+ prefix = " " * (indent * 2) # Indentação para visualização hierárquica
46
+ print(f"{prefix}- {element.tag}: {element.text.strip() if element.text else ''}")
47
+
48
+ # Se o elemento tiver atributos, exibir
49
+ if element.attrib:
50
+ print(f"{prefix} Atributos: {element.attrib}")
51
+
52
+ # Percorrer subelementos
53
+ for child in element:
54
+ print_element(child, indent + 1)
7
55
 
8
56
  # Validações dos inputs
9
57
  class InitParamsValidator(BaseModel):
@@ -118,7 +166,44 @@ class EnviarArquivoValidator(BaseModel):
118
166
  raise ValueError("Cada destinatário deve conter as chaves 'unidade', 'dependencia' e 'operador'. Verifique a documentação da API BC STA para entender o que colocar cada campo")
119
167
 
120
168
  return value
121
-
169
+
170
+ class ListarArquivosParams(BaseModel):
171
+ """
172
+ Parâmetros para listar arquivos disponíveis na API STA.
173
+
174
+ Atributos:
175
+ nivel (str): Nível de detalhe da consulta. Aceita apenas 'RES', 'BAS' ou 'COMPL'.
176
+ inicio (str): Data e hora de início no formato ISO 8601 (yyyy-MM-ddTHH:mm:ss).
177
+ fim (str): Data e hora de fim no formato ISO 8601 (yyyy-MM-ddTHH:mm:ss).
178
+ situacao (Optional[str]): Situação da transmissão, podendo ser 'REC' ou 'A_REC'.
179
+ identificadorDocumento (Optional[str]): Identificador do documento, se aplicável.
180
+ qtd (int): Quantidade máxima de resultados (valor padrão: 100, máximo permitido: 100).
181
+ tipo_arquivo (list): lista de tipos de arquivo para filtrar ['ACCS002','ACCS003','AJUD301','AJUD302','AJUD303','AJUD304','AJUD305','AJUD308','AJUD309','AJUD310','AJUD331','AMES102','AMTF102','ASVR9810','ATXB001']
182
+ """
183
+ nivel: Literal['RES', 'BAS', 'COMPL']
184
+ #inicio: str = Field(..., regex=r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$")
185
+ inicio: str = Field(..., pattern=r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$")
186
+ fim: Optional[str] = Field(None, pattern=r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$")
187
+ situacao: Optional[Literal['REC', 'A_REC']] = None
188
+ identificadorDocumento: Optional[str] = None
189
+ qtd: int = Field(default=100, le=100)
190
+ #tipo_arquivo: Optional[str] = None
191
+ tipo_arquivo: Optional[List[
192
+ Literal[
193
+ 'ACCS002', 'ACCS003', 'AJUD301', 'AJUD302', 'AJUD303', 'AJUD304', 'AJUD305', 'AJUD308',
194
+ 'AJUD309', 'AJUD310', 'AJUD331', 'AMES102', 'AMTF102', 'ASVR9810', 'ATXB001'
195
+ ]
196
+ ]] = None
197
+
198
+ class DownloadArquivoParams(BaseModel):
199
+ protocolo: str = Field(..., min_length=1, description="Código do protocolo")
200
+ filename: Optional[str] = Field(None, description="Nome e caminho do arquivo")
201
+
202
+ class DownloadArquivoResponse(BaseModel):
203
+ success: bool = Field(..., description="Indica se o download foi bem-sucedido")
204
+ status_code: int = Field(..., ge=100, le=599, description="Código de status HTTP")
205
+ content: Union[bytes, str] = Field(..., description="Conteúdo do arquivo (em bytes se sucesso, em string se erro)")
206
+
122
207
  class BC_STA:
123
208
 
124
209
  def __init__(self, usuario:str, senha:str, ambiente:str):
@@ -183,11 +268,17 @@ class BC_STA:
183
268
 
184
269
  # Verificando o status e retornando a resposta
185
270
  if response.status_code == 200:
186
-
271
+
187
272
  return True
188
273
 
274
+ elif response.status_code == 401:
275
+
276
+ log.error(f"Erro de autenticação no BC STA: Verifique o usuário e a senha.\n{response.text}")
277
+ return False
278
+
189
279
  else:
190
280
 
281
+ log.error(f"Erro ao autenticar no BC STA: {response.text}")
191
282
  return False
192
283
 
193
284
 
@@ -414,3 +505,194 @@ class BC_STA:
414
505
  #return f"Failed to create protocol. Status code: {response.status_code}, Reason: {response.reason}"
415
506
  return resposta
416
507
 
508
+ def listar_arquivos(self,
509
+ nivel: Literal['RES', 'BAS', 'COMPL'],
510
+ inicio: str,
511
+ fim: str = None,
512
+ situacao: Optional[Literal['REC', 'A_REC']] = None,
513
+ identificadorDocumento: Optional[str] = None,
514
+ qtd: int = 100,
515
+ tipo_arquivo: list = None):
516
+ resultados = []
517
+
518
+ try:
519
+ # Parse seguro para datetime (assume formato ISO 'AAAA-MM-DDTHH:MM:SS')
520
+ dt_inicio = datetime.fromisoformat(inicio)
521
+ dt_fim = datetime.fromisoformat(fim) if fim else None
522
+ ultima_dt = dt_inicio
523
+
524
+ while True:
525
+ # Se já passamos do fim, encerra antes de chamar a API
526
+ if dt_fim and ultima_dt >= dt_fim:
527
+ break
528
+
529
+ # Monta a URL usando a data atual do cursor
530
+ _inicio_str = ultima_dt.strftime("%Y-%m-%dT%H:%M:%S")
531
+ url = f"{self.base_url}/arquivos?tipoConsulta=AVANC&nivelDetalhe={nivel}"
532
+ url += f"&dataHoraInicio={_inicio_str}"
533
+ if situacao:
534
+ url += f"&situacaoTransmissao={situacao}"
535
+ if identificadorDocumento:
536
+ url += f"&identificadorDocumento={identificadorDocumento}"
537
+ if dt_fim:
538
+ _fim_str = dt_fim.strftime("%Y-%m-%dT%H:%M:%S")
539
+ url += f"&dataHoraFim={_fim_str}"
540
+ url += f"&qtdMaxResultados={qtd}"
541
+
542
+ response = requests.get(
543
+ url, headers=self.headers, auth=self.auth, timeout=60,
544
+ )
545
+
546
+ if response.status_code != 200:
547
+ resposta = {"success": False, "status_code": int(response.status_code), "content": f"Erro ao listar arquivos: {response.text}"}
548
+ return resposta
549
+
550
+ try:
551
+ dados = xml_response_to_json(response.text)
552
+ if not dados:
553
+ break # sem mais resultados
554
+
555
+ # Normaliza dados para lista
556
+ itens = dados if isinstance(dados, list) else [dados]
557
+
558
+ # (opcional) filtra por tipo_arquivo
559
+ if tipo_arquivo is not None:
560
+ itens = [a for a in itens if a.get("TipoArquivo") in tipo_arquivo]
561
+
562
+ resultados.extend(itens)
563
+
564
+ # Atualiza o cursor a partir do último item retornado
565
+ # Busca o último com DataHoraDisponibilizacao
566
+ ultimo = None
567
+ for cand in reversed(itens):
568
+ if "DataHoraDisponibilizacao" in cand and cand["DataHoraDisponibilizacao"]:
569
+ ultimo = cand["DataHoraDisponibilizacao"]
570
+ break
571
+
572
+ if not ultimo:
573
+ # fallback: se o JSON bruto vier como dict/list diferente, tente no original
574
+ if isinstance(dados, list) and dados and "DataHoraDisponibilizacao" in dados[-1]:
575
+ ultimo = dados[-1]["DataHoraDisponibilizacao"]
576
+ elif isinstance(dados, dict) and "DataHoraDisponibilizacao" in dados:
577
+ ultimo = dados["DataHoraDisponibilizacao"]
578
+
579
+ if not ultimo:
580
+ resposta = {"success": False, "status_code": 500, "content": "Campo 'DataHoraDisponibilizacao' não encontrado ou estrutura inesperada."}
581
+ return resposta
582
+
583
+ # Cursor: último + 1s para evitar repetir o mesmo registro
584
+ proxima_dt = datetime.fromisoformat(ultimo) + timedelta(seconds=1)
585
+
586
+ # Se a próxima dt já ultrapassa o fim, encerramos sem nova chamada
587
+ if dt_fim and proxima_dt > dt_fim:
588
+ break
589
+
590
+ ultima_dt = proxima_dt
591
+
592
+ except ET.ParseError as e:
593
+ resposta = {"success": False, "status_code": 500, "content": f"Erro ao processar XML: {e}"}
594
+ return resposta
595
+
596
+ return {"success": True, "status_code": 200, "content": resultados}
597
+
598
+ except Exception as e:
599
+ resposta = {"success": False, "status_code": 500, "content": f"Erro em BC_STA:listar_arquivos: {e}"}
600
+ return resposta
601
+
602
+ def download_arquivo(self,protocolo:str,filename:str=None):
603
+ """Faz o download de um arquivo de um protocolo especifico
604
+
605
+ Args:
606
+ protocolo (str): protocolo
607
+ filename (str): path+nome do arquivo
608
+
609
+ Returns:
610
+ dict: {"success": bool, "status_code": int, "content": bytes/str}
611
+ """
612
+
613
+ # Validação dos parâmetros
614
+ try:
615
+
616
+ params = DownloadArquivoParams(protocolo=protocolo, filename=filename)
617
+
618
+ except ValidationError as e:
619
+
620
+ return Exception(str(e))
621
+
622
+ url = f"/arquivos/{protocolo}/conteudo"
623
+ response = requests.get(
624
+ self.base_url + url,
625
+ auth=self.auth,
626
+ timeout=60,
627
+ headers={"Connection": "keep-alive"},
628
+ )
629
+
630
+ if response.status_code == 200:
631
+
632
+ if filename is not None:
633
+
634
+ try:
635
+
636
+ with open(filename, "wb") as arquivo:
637
+
638
+ arquivo.write(response.content)
639
+
640
+ except Exception as e:
641
+
642
+ raise Exception(f"Falha ao salvar o arquivo em disco\n{str(e)}")
643
+
644
+ return {"success": True, "status_code": int(response.status_code), "content": response.content }
645
+
646
+ else:
647
+
648
+ return {"success": False, "status_code": int(response.status_code), "content": response.text}
649
+
650
+ def qs_gerar_xml_string_responder_nao_cliente(self,protocolo_inicial_ordem:str,numctrlccs:str,cnpj_cpf:str,numctrlenvio:str,numprocjud:str,identdemissor:str,identddestinatario:str,domsist:str,nuop:str,dtmovto:datetime,cnpjbaseentrespons:str):
651
+
652
+ """Gera a string do arquivo XML de resposta para nao-cliente para ser enviado ao STA
653
+ Args:
654
+ protocolo_inicial_ordem (str): protocolo inicial da ordem de quebra de sigilo, do arquivo AMES102
655
+ numctrlccs (str): numero de controle ccs
656
+ cnpj_cpf (str): documento do solicitado
657
+ numctrlenvio (str): numero de controle de envio
658
+ numprocjud (str): numero do processo judicial
659
+ identdemissor (str): identificacao do emissor
660
+ identddestinatario (str): identificacao do destinatario
661
+ domsist (str): domsist
662
+ nuop (str): numero da operacao
663
+ dtmovto (datetime): data da movimentacao
664
+ cnpjbaseentrespons (str): cnpjbaseentrespons
665
+ Returns:
666
+ str: string formatada para gerar um arquivo xml de resposta
667
+ """
668
+ try:
669
+
670
+ ns = "http://www.bcb.gov.br/MES/CCS0012.xsd"
671
+
672
+ doc = ET.Element("DOC", xmlns=ns)
673
+
674
+ bcmsg = ET.SubElement(doc, "BCMSG")
675
+ ET.SubElement(bcmsg, "IdentdEmissor").text = identdemissor
676
+ ET.SubElement(bcmsg, "IdentdDestinatario").text = identddestinatario
677
+ ET.SubElement(bcmsg, "DomSist").text = domsist
678
+ ET.SubElement(bcmsg, "NUOp").text = nuop
679
+
680
+ sismsg = ET.SubElement(doc, "SISMSG")
681
+ ccs0012 = ET.SubElement(sismsg, "CCS0012")
682
+ ET.SubElement(ccs0012, "CodMsg").text = "CCS0012"
683
+ ET.SubElement(ccs0012, "CNPJBaseEntRespons").text = cnpjbaseentrespons
684
+ ET.SubElement(ccs0012, "NumCtrlCCSOr").text = numctrlccs
685
+ ET.SubElement(ccs0012, "SitAtedmntReq").text = "03"
686
+ ET.SubElement(ccs0012, "DtMovto").text = dtmovto.strftime("%Y-%m-%d")
687
+
688
+ #return ET.tostring(doc, encoding="utf-8", xml_declaration=True).decode("utf-8")
689
+ xml_str = ET.tostring(doc, encoding="utf-8", xml_declaration=True).decode("utf-8")
690
+
691
+ # Formatando XML com indentação para melhor visualização
692
+ xml_formatado = parseString(xml_str).toprettyxml(indent=" ")
693
+ return xml_formatado
694
+
695
+ except Exception as e:
696
+
697
+ raise Exception(f"Falha ao gerar XML de resposta de Quebra de Sigilo:\n{e}")
698
+
@@ -150,7 +150,7 @@ class Email():
150
150
  }
151
151
 
152
152
 
153
- def send_email( self, to : list , message : str , title : str , reply_to: str, attachments : list = [] , cc : list = [] , cco : list = [] ) -> dict:
153
+ def send_email( self, to : list , message : str , title : str , reply_to: str, attachments : list = [] , cc : list = [] , cco : list = [], from_mask: str = "", block_by_attachments:bool=False) -> dict:
154
154
  """
155
155
  Envia um email com os parâmetros fornecidos.
156
156
  Args:
@@ -161,6 +161,7 @@ class Email():
161
161
  attachments (list, optional): Lista de caminhos dos arquivos anexos. Defaults to [].
162
162
  cc (list, optional): Lista de destinatários em cópia. Defaults to [].
163
163
  cco (list, optional): Lista de destinatários em cópia oculta. Defaults to [].
164
+ from_mask (str, optional): Mascara para definir como se o envio estivesse vindo de outro email
164
165
  Returns:
165
166
  dict: Dicionário com o status do envio do email. Se o envio for bem-sucedido, o dicionário terá a chave 'status' com valor True. Caso contrário, terá a chave 'status' com valor False e a chave 'error' com a descrição do erro.
166
167
  Raises:
@@ -189,8 +190,14 @@ class Email():
189
190
  try:
190
191
 
191
192
  msg = MIMEMultipart()
193
+
194
+ if from_mask != "":
192
195
 
193
- msg["From"] = self.email_sender
196
+ msg["From"] = from_mask
197
+
198
+ else:
199
+
200
+ msg["From"] = self.email_sender
194
201
 
195
202
  msg["To"] = (",").join(to)
196
203
 
@@ -220,10 +227,12 @@ class Email():
220
227
 
221
228
  except Exception as e:
222
229
 
223
- return {
224
- 'status':False,
225
- 'error':str(e)
226
- }
230
+ if block_by_attachments:
231
+
232
+ return {
233
+ 'status':False,
234
+ 'error':str(e)
235
+ }
227
236
 
228
237
  msg.attach(MIMEText(message, 'html'))
229
238