csc-cia-stne 0.0.43__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.
- csc_cia_stne/__init__.py +89 -0
- csc_cia_stne/bc_correios.py +529 -0
- csc_cia_stne/bc_sta.py +416 -0
- csc_cia_stne/email.py +239 -0
- csc_cia_stne/gcp_bigquery.py +224 -0
- csc_cia_stne/google_drive.py +268 -0
- csc_cia_stne/karavela.py +135 -0
- csc_cia_stne/logger_json.py +92 -0
- csc_cia_stne/logger_rich.py +249 -0
- csc_cia_stne/provio.py +103 -0
- csc_cia_stne/servicenow.py +689 -0
- csc_cia_stne/slack.py +227 -0
- csc_cia_stne/stne_admin.py +597 -0
- csc_cia_stne/utilitarios/__init__.py +9 -0
- csc_cia_stne/utilitarios/functions/__init__.py +14 -0
- csc_cia_stne/utilitarios/functions/func_b64.py +50 -0
- csc_cia_stne/utilitarios/functions/func_converters.py +18 -0
- csc_cia_stne/utilitarios/functions/func_recriar_pastas.py +29 -0
- csc_cia_stne/utilitarios/functions/func_settings.py +66 -0
- csc_cia_stne/utilitarios/functions/func_titulo.py +141 -0
- csc_cia_stne/utilitarios/validations/GcpBigQueryValidator.py +133 -0
- csc_cia_stne/utilitarios/validations/GoogleDriveValidator.py +144 -0
- csc_cia_stne/utilitarios/validations/ServiceNowValidator.py +403 -0
- csc_cia_stne/utilitarios/validations/__init__.py +3 -0
- csc_cia_stne-0.0.43.dist-info/LICENCE +21 -0
- csc_cia_stne-0.0.43.dist-info/METADATA +33 -0
- csc_cia_stne-0.0.43.dist-info/RECORD +29 -0
- csc_cia_stne-0.0.43.dist-info/WHEEL +5 -0
- csc_cia_stne-0.0.43.dist-info/top_level.txt +1 -0
csc_cia_stne/bc_sta.py
ADDED
@@ -0,0 +1,416 @@
|
|
1
|
+
from requests.auth import HTTPBasicAuth
|
2
|
+
import requests
|
3
|
+
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
|
7
|
+
|
8
|
+
# Validações dos inputs
|
9
|
+
class InitParamsValidator(BaseModel):
|
10
|
+
"""
|
11
|
+
Classe responsável por validar os parâmetros de inicialização.
|
12
|
+
Atributos:
|
13
|
+
usuario (str): O nome de usuário.
|
14
|
+
senha (str): A senha do usuário.
|
15
|
+
ambiente (Literal["prd", "hml"]): O ambiente, que pode ser "prd" (produção) ou "hml" (homologação).
|
16
|
+
Métodos:
|
17
|
+
check_non_empty_string(value, info): Método de validação para garantir que cada parâmetro é uma string não vazia.
|
18
|
+
"""
|
19
|
+
|
20
|
+
usuario: str
|
21
|
+
senha: str
|
22
|
+
ambiente: Literal["prd", "hml"] # Aceita apenas "prd" ou "sdx"
|
23
|
+
|
24
|
+
# Validação para garantir que cada parâmetro é uma string não vazia
|
25
|
+
"""
|
26
|
+
Método de validação para garantir que cada parâmetro é uma string não vazia.
|
27
|
+
Parâmetros:
|
28
|
+
value (Any): O valor do parâmetro a ser validado.
|
29
|
+
info (FieldInfo): As informações do campo a ser validado.
|
30
|
+
Retorna:
|
31
|
+
value (Any): O valor do parâmetro validado.
|
32
|
+
Lança:
|
33
|
+
ValueError: Se o valor do parâmetro não for uma string não vazia.
|
34
|
+
"""
|
35
|
+
@field_validator('usuario', 'senha')
|
36
|
+
def check_non_empty_string(cls, value, info):
|
37
|
+
if not isinstance(value, str) or not value.strip():
|
38
|
+
|
39
|
+
raise ValueError(f"O parâmetro '{info.field_name}' deve ser uma string não vazia.")
|
40
|
+
|
41
|
+
return value
|
42
|
+
|
43
|
+
class EnviarArquivoValidator(BaseModel):
|
44
|
+
"""
|
45
|
+
Classe responsável por validar os parâmetros de envio de arquivo.
|
46
|
+
Args:
|
47
|
+
tipo_arquivo (str): O código do tipo de envio a ser utilizado para o STA.
|
48
|
+
Verificar códigos disponíveis na documentação STA.
|
49
|
+
file_content (bytes): O conteúdo do arquivo a ser enviado, em formato de bytes.
|
50
|
+
nome_arquivo (str): O nome do arquivo a ser enviado.
|
51
|
+
Deve ser uma string não vazia e terminar com uma extensão de arquivo comum.
|
52
|
+
observacao (str): A observação relacionada ao envio do arquivo.
|
53
|
+
Deve ser uma string não vazia.
|
54
|
+
destinatarios (Optional[Union[Dict[str, str], str]], optional): Os destinatários do arquivo.
|
55
|
+
Pode ser um dicionário ou uma string XML. Defaults to None.
|
56
|
+
Raises:
|
57
|
+
ValueError: Se algum dos parâmetros não atender às condições de validação.
|
58
|
+
Returns:
|
59
|
+
O valor de cada parâmetro, se a validação for bem-sucedida.
|
60
|
+
"""
|
61
|
+
tipo_arquivo:str
|
62
|
+
file_content:bytes
|
63
|
+
nome_arquivo:str
|
64
|
+
observacao:str
|
65
|
+
destinatarios:Optional[Union[Dict[str, str], str]] = None # Aceita um dicionário
|
66
|
+
|
67
|
+
@field_validator("tipo_arquivo")
|
68
|
+
def check_tipo_arquivo(cls, value):
|
69
|
+
if not isinstance(value, str) or not value.strip():
|
70
|
+
|
71
|
+
raise ValueError("O parâmetro 'tipo_arquivo' deve ser uma string não vazia, com o código do tipo de envio a ser utilizado para o STA, verificar codigos disponíveis na documentação STA")
|
72
|
+
|
73
|
+
return value
|
74
|
+
|
75
|
+
# Validador para file_content: deve ter conteúdo em bytes
|
76
|
+
@field_validator("file_content")
|
77
|
+
def check_file_content(cls, value):
|
78
|
+
if not isinstance(value, bytes) or len(value) == 0:
|
79
|
+
raise ValueError("O parâmetro 'file_content' deve ser um byte array não vazio.")
|
80
|
+
return value
|
81
|
+
|
82
|
+
# Validador para nome_arquivo: deve ser uma string não vazia e terminar com uma extensão de arquivo comum
|
83
|
+
@field_validator("nome_arquivo")
|
84
|
+
def check_nome_arquivo(cls, value):
|
85
|
+
if not isinstance(value, str) or not value.strip():
|
86
|
+
raise ValueError("O nome do arquivo deve ser uma string não vazia.")
|
87
|
+
if not value.lower().endswith(('.zip', '.xpto')):
|
88
|
+
raise ValueError("O nome do arquivo deve ter uma extensão válida.")
|
89
|
+
return value
|
90
|
+
|
91
|
+
@field_validator("observacao")
|
92
|
+
def check_observacao(cls, value):
|
93
|
+
if not isinstance(value, str) or not value.strip():
|
94
|
+
|
95
|
+
raise ValueError("O parâmetro 'observacao' deve ser uma string não vazia")
|
96
|
+
|
97
|
+
return value
|
98
|
+
|
99
|
+
# Validador para destinatarios: aceita um dicionário ou uma string XML opcional
|
100
|
+
@field_validator("destinatarios")
|
101
|
+
def check_destinatarios(cls, value):
|
102
|
+
if value is not None:
|
103
|
+
|
104
|
+
if not isinstance(value, list):
|
105
|
+
|
106
|
+
raise ValueError("O parâmetro 'destinatarios' deve ser uma lista de dicionários.")
|
107
|
+
|
108
|
+
for item in value:
|
109
|
+
|
110
|
+
if not isinstance(item, dict):
|
111
|
+
|
112
|
+
raise ValueError("Cada destinatário deve ser um dicionário.")
|
113
|
+
|
114
|
+
required_keys = {"unidade", "dependencia", "operador"}
|
115
|
+
|
116
|
+
if not required_keys.issubset(item.keys()):
|
117
|
+
|
118
|
+
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
|
+
|
120
|
+
return value
|
121
|
+
|
122
|
+
class BC_STA:
|
123
|
+
|
124
|
+
def __init__(self, usuario:str, senha:str, ambiente:str):
|
125
|
+
"""
|
126
|
+
Inicializa uma instância da classe BC_STA.
|
127
|
+
Parâmetros:
|
128
|
+
- usuario (str): O nome de usuário para autenticação.
|
129
|
+
- senha (str): A senha para autenticação.
|
130
|
+
- ambiente (str): O ambiente de execução ('prd' para produção ou qualquer outro valor para ambiente de teste).
|
131
|
+
Raises:
|
132
|
+
- ValueError: Se houver erro na validação dos dados de input da inicialização da instância 'BC_STA'.
|
133
|
+
Exemplo de uso:
|
134
|
+
```
|
135
|
+
bc_sta = BC_STA(usuario='meu_usuario', senha='minha_senha', ambiente='prd')
|
136
|
+
```
|
137
|
+
"""
|
138
|
+
|
139
|
+
try:
|
140
|
+
|
141
|
+
InitParamsValidator(usuario=usuario, senha=senha, ambiente=ambiente)
|
142
|
+
|
143
|
+
except ValidationError as e:
|
144
|
+
|
145
|
+
raise ValueError("Erro na validação dos dados de input da inicialização da instância 'BC_STA':", e.errors())
|
146
|
+
|
147
|
+
if ambiente == 'prd':
|
148
|
+
|
149
|
+
self.base_url = "https://sta.bcb.gov.br/staws"
|
150
|
+
|
151
|
+
else:
|
152
|
+
|
153
|
+
self.base_url = "https://sta-h.bcb.gov.br/staws"
|
154
|
+
|
155
|
+
try:
|
156
|
+
|
157
|
+
self.auth = HTTPBasicAuth(usuario,senha)
|
158
|
+
self.error = None
|
159
|
+
self.headers = {'Content-Type': 'application/xml'}
|
160
|
+
self.is_connected = self.verifica_conexao()
|
161
|
+
|
162
|
+
except Exception as e:
|
163
|
+
|
164
|
+
self.is_connected = False
|
165
|
+
self.error = e
|
166
|
+
|
167
|
+
def verifica_conexao(self):
|
168
|
+
"""
|
169
|
+
Verifica a conexão com o servidor STA do Banco Central do Brasil.
|
170
|
+
Returns:
|
171
|
+
bool: True se a conexão for bem-sucedida, False caso contrário.
|
172
|
+
Raises:
|
173
|
+
Exception: Se ocorrer algum erro durante a verificação da conexão.
|
174
|
+
Example:
|
175
|
+
# Criando uma instância da classe
|
176
|
+
bc_sta = BCSTA()
|
177
|
+
# Verificando a conexão
|
178
|
+
conexao = bc_sta.verifica_conexao()
|
179
|
+
"""
|
180
|
+
try:
|
181
|
+
|
182
|
+
response = requests.get("https://sta.bcb.gov.br/staws/arquivos?tipoConsulta=AVANC&nivelDetalhe=RES", auth=self.auth)
|
183
|
+
|
184
|
+
# Verificando o status e retornando a resposta
|
185
|
+
if response.status_code == 200:
|
186
|
+
|
187
|
+
return True
|
188
|
+
|
189
|
+
else:
|
190
|
+
|
191
|
+
return False
|
192
|
+
|
193
|
+
|
194
|
+
except Exception as e:
|
195
|
+
|
196
|
+
raise e
|
197
|
+
|
198
|
+
def enviar_arquivo(self, tipo_arquivo:str, file_content:bytes, nome_arquivo:str, observacao:str, destinatarios:dict=None):
|
199
|
+
"""
|
200
|
+
Envia um arquivo para um determinado destino.
|
201
|
+
tipo_arquivo (str): O tipo de arquivo a ser enviado.
|
202
|
+
nome_arquivo (str): O nome do arquivo.
|
203
|
+
observacao (str): Uma observação opcional.
|
204
|
+
destinatarios (dict, optional): Um dicionário contendo informações sobre os destinatários do arquivo.
|
205
|
+
O dicionário deve ter a seguinte estrutura:
|
206
|
+
{
|
207
|
+
'unidade': 'nome_da_unidade',
|
208
|
+
'dependencia': 'nome_da_dependencia', # opcional
|
209
|
+
'operador': 'nome_do_operador' # opcional
|
210
|
+
O campo 'dependencia' e 'operador' são opcionais.
|
211
|
+
- 'enviado': Um valor booleano indicando se o arquivo foi enviado com sucesso.
|
212
|
+
- 'protocolo': O protocolo gerado, caso o arquivo tenha sido enviado com sucesso.
|
213
|
+
- 'link': O link do arquivo, caso tenha sido enviado com sucesso.
|
214
|
+
- 'erro': Uma mensagem de erro, caso ocorra algum problema no envio do arquivo.
|
215
|
+
ValueError: Se ocorrer um erro na validação dos dados de entrada.
|
216
|
+
Exception: Se ocorrer um erro durante o envio do arquivo.
|
217
|
+
# Exemplo de chamada da função
|
218
|
+
tipo_arquivo = 'documento'
|
219
|
+
nome_arquivo = 'arquivo.txt'
|
220
|
+
observacao = 'Este é um arquivo de teste'
|
221
|
+
destinatarios = {
|
222
|
+
'unidade': 'unidade_destino',
|
223
|
+
'dependencia': 'dependencia_destino',
|
224
|
+
'operador': 'operador_destino'
|
225
|
+
resposta = enviar_arquivo(tipo_arquivo, file_content, nome_arquivo, observacao, destinatarios)
|
226
|
+
"""
|
227
|
+
try:
|
228
|
+
|
229
|
+
EnviarArquivoValidator(tipo_arquivo=tipo_arquivo, file_content=file_content, nome_arquivo=nome_arquivo, observacao=observacao, destinatarios=destinatarios)
|
230
|
+
|
231
|
+
except ValidationError as e:
|
232
|
+
|
233
|
+
raise ValueError("Erro na validação dos dados de input do método 'enviar_arquivo':", e.errors())
|
234
|
+
|
235
|
+
def generate_sha256_hash(file_content):
|
236
|
+
"""
|
237
|
+
Gera o hash SHA-256 do conteúdo de um arquivo.
|
238
|
+
Args:
|
239
|
+
file_content (bytes): O conteúdo do arquivo como uma sequência de bytes.
|
240
|
+
Returns:
|
241
|
+
str: O hash SHA-256 do conteúdo do arquivo.
|
242
|
+
Example:
|
243
|
+
file_content = b'Lorem ipsum dolor sit amet'
|
244
|
+
hash_value = generate_sha256_hash(file_content)
|
245
|
+
print(hash_value)
|
246
|
+
"""
|
247
|
+
|
248
|
+
# Gera o hash SHA-256 do arquivo
|
249
|
+
sha256_hash = hashlib.sha256()
|
250
|
+
sha256_hash.update(file_content)
|
251
|
+
return sha256_hash.hexdigest()
|
252
|
+
|
253
|
+
def process_response(xml_content):
|
254
|
+
"""
|
255
|
+
Processa o conteúdo XML de resposta e retorna um dicionário com as informações relevantes.
|
256
|
+
Args:
|
257
|
+
xml_content (str): O conteúdo XML de resposta.
|
258
|
+
Returns:
|
259
|
+
dict: Um dicionário contendo as seguintes chaves:
|
260
|
+
- 'enviado': Um valor booleano indicando se o XML foi enviado com sucesso.
|
261
|
+
- 'protocolo': O protocolo extraído do XML, caso tenha sido enviado com sucesso.
|
262
|
+
- 'link': O link extraído do XML, caso tenha sido enviado com sucesso.
|
263
|
+
- 'erro': Uma mensagem de erro, caso ocorra algum problema no processamento do XML.
|
264
|
+
Raises:
|
265
|
+
None
|
266
|
+
Exemplo de uso:
|
267
|
+
xml = "<root>...</root>"
|
268
|
+
resposta = process_response(xml)
|
269
|
+
print(resposta)
|
270
|
+
"""
|
271
|
+
|
272
|
+
try:
|
273
|
+
|
274
|
+
root = ET.fromstring(xml_content)
|
275
|
+
|
276
|
+
# Verifica se há um elemento <Erro> no XML
|
277
|
+
erro = None
|
278
|
+
erro_elem = root.find('Erro')
|
279
|
+
|
280
|
+
if erro_elem is not None:
|
281
|
+
|
282
|
+
codigo_erro = erro_elem.find('Codigo').text
|
283
|
+
descricao_erro = erro_elem.find('Descricao').text
|
284
|
+
erro = f"Erro {codigo_erro}: {descricao_erro}"
|
285
|
+
resposta = {
|
286
|
+
'enviado':False,
|
287
|
+
'protocolo': None,
|
288
|
+
'link': None,
|
289
|
+
'erro': erro
|
290
|
+
}
|
291
|
+
|
292
|
+
else:
|
293
|
+
|
294
|
+
protocolo = root.find('Protocolo').text
|
295
|
+
link = root.find('.//atom:link', namespaces={'atom': 'http://www.w3.org/2005/Atom'}).attrib['href']
|
296
|
+
resposta = {
|
297
|
+
'enviado':True,
|
298
|
+
'protocolo': protocolo,
|
299
|
+
'link': link,
|
300
|
+
'erro': None
|
301
|
+
}
|
302
|
+
|
303
|
+
return resposta
|
304
|
+
|
305
|
+
except ET.ParseError as e:
|
306
|
+
|
307
|
+
resposta = {
|
308
|
+
'enviado': False,
|
309
|
+
'protocolo': None,
|
310
|
+
'link': None,
|
311
|
+
'erro': f"Error processing XML: {str(e)}"
|
312
|
+
}
|
313
|
+
return resposta
|
314
|
+
|
315
|
+
url = self.base_url + '/arquivos'
|
316
|
+
|
317
|
+
# Calcula o hash SHA-256 do conteúdo do arquivo
|
318
|
+
hash_sha256 = generate_sha256_hash(file_content)
|
319
|
+
tamanho_arquivo = len(file_content) # Tamanho do arquivo em bytes
|
320
|
+
|
321
|
+
# Constrói o XML de requisição
|
322
|
+
parametros = ET.Element('Parametros')
|
323
|
+
ET.SubElement(parametros, 'IdentificadorDocumento').text = tipo_arquivo
|
324
|
+
ET.SubElement(parametros, 'Hash').text = hash_sha256
|
325
|
+
ET.SubElement(parametros, 'Tamanho').text = str(tamanho_arquivo)
|
326
|
+
ET.SubElement(parametros, 'NomeArquivo').text = nome_arquivo
|
327
|
+
|
328
|
+
# Campo observação é opcional
|
329
|
+
if observacao:
|
330
|
+
ET.SubElement(parametros, 'Observacao').text = observacao
|
331
|
+
|
332
|
+
# Campo destinatários é opcional
|
333
|
+
if destinatarios:
|
334
|
+
|
335
|
+
destinatarios_elem = ET.SubElement(parametros, 'Destinatarios')
|
336
|
+
|
337
|
+
for dest in destinatarios:
|
338
|
+
|
339
|
+
destinatario_elem = ET.SubElement(destinatarios_elem, 'Destinatario')
|
340
|
+
ET.SubElement(destinatario_elem, 'Unidade').text = dest['unidade']
|
341
|
+
|
342
|
+
if 'dependencia' in dest:
|
343
|
+
|
344
|
+
ET.SubElement(destinatario_elem, 'Dependencia').text = dest['dependencia']
|
345
|
+
|
346
|
+
if 'operador' in dest:
|
347
|
+
|
348
|
+
ET.SubElement(destinatario_elem, 'Operador').text = dest['operador']
|
349
|
+
|
350
|
+
# Converte o XML para string
|
351
|
+
xml_data = ET.tostring(parametros, encoding='utf-8', method='xml')
|
352
|
+
|
353
|
+
# Envia a requisição POST
|
354
|
+
response = requests.post(url, headers=self.headers, data=xml_data, auth=self.auth, timeout=60)
|
355
|
+
|
356
|
+
if response.status_code == 201: # Verifica se o protocolo foi criado com sucesso
|
357
|
+
|
358
|
+
resultado_protocolo = process_response(response.text)
|
359
|
+
|
360
|
+
# Protocolo gerado, prosseguir com envio do arquivo
|
361
|
+
if resultado_protocolo["enviado"]:
|
362
|
+
|
363
|
+
try:
|
364
|
+
|
365
|
+
# Solicita o envio
|
366
|
+
protocolo = resultado_protocolo["protocolo"]
|
367
|
+
# URL do endpoint, incluindo o protocolo
|
368
|
+
url = url + f"/{protocolo}/conteudo"
|
369
|
+
|
370
|
+
# Envia a requisição PUT com o conteúdo binário do arquivo
|
371
|
+
response = requests.put(url, data=file_content, auth=self.auth, timeout=60)
|
372
|
+
|
373
|
+
if response.status_code == 200:
|
374
|
+
|
375
|
+
return resultado_protocolo
|
376
|
+
|
377
|
+
else:
|
378
|
+
|
379
|
+
resposta = {
|
380
|
+
'enviado':False,
|
381
|
+
'protocolo': None,
|
382
|
+
'link': None,
|
383
|
+
'erro': f"Falha ao enviar arquivo. Status code: {response.status_code}, Text: {response.text}, Reason: {response.reason}"
|
384
|
+
}
|
385
|
+
return resposta
|
386
|
+
|
387
|
+
except Exception as e:
|
388
|
+
|
389
|
+
erro = str(e)
|
390
|
+
resposta = {
|
391
|
+
'enviado':False,
|
392
|
+
'protocolo': None,
|
393
|
+
'link': None,
|
394
|
+
'erro': erro
|
395
|
+
}
|
396
|
+
return resposta
|
397
|
+
|
398
|
+
|
399
|
+
|
400
|
+
# Protocolo não foi gerado, retornar erro
|
401
|
+
else:
|
402
|
+
|
403
|
+
return resultado_protocolo
|
404
|
+
|
405
|
+
else:
|
406
|
+
|
407
|
+
print(response.text)
|
408
|
+
resposta = {
|
409
|
+
'enviado': False,
|
410
|
+
'protocolo': None,
|
411
|
+
'link': None,
|
412
|
+
'erro': f"Failed to create protocol. Status code: {response.status_code}, Reason: {response.reason}"
|
413
|
+
}
|
414
|
+
#return f"Failed to create protocol. Status code: {response.status_code}, Reason: {response.reason}"
|
415
|
+
return resposta
|
416
|
+
|
csc_cia_stne/email.py
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
import smtplib
|
2
|
+
from email.mime.multipart import MIMEMultipart
|
3
|
+
from email.mime.text import MIMEText
|
4
|
+
from email.mime.base import MIMEBase
|
5
|
+
from email import encoders
|
6
|
+
from pydantic import BaseModel, ValidationError, field_validator
|
7
|
+
|
8
|
+
class InitParamsValidator(BaseModel):
|
9
|
+
"""
|
10
|
+
Classe para validar os parâmetros de inicialização.
|
11
|
+
Atributos:
|
12
|
+
- email_sender (str): O endereço de e-mail do remetente.
|
13
|
+
- email_password (str): A senha do e-mail.
|
14
|
+
Métodos:
|
15
|
+
- check_str_input(value, info): Valida se o valor é uma string não vazia.
|
16
|
+
"""
|
17
|
+
email_sender: str
|
18
|
+
email_password: str
|
19
|
+
|
20
|
+
"""
|
21
|
+
Valida se o valor é uma string não vazia.
|
22
|
+
Parâmetros:
|
23
|
+
- value: O valor a ser validado.
|
24
|
+
- info: Informações sobre o campo.
|
25
|
+
Retorna:
|
26
|
+
- value: O valor validado.
|
27
|
+
Lança:
|
28
|
+
- ValueError: Se o valor não for uma string ou estiver vazio.
|
29
|
+
"""
|
30
|
+
@field_validator('email_sender','email_password')
|
31
|
+
def check_str_input(cls, value, info):
|
32
|
+
if not isinstance(value, str) or not value.strip():
|
33
|
+
raise ValueError(f"O campo '{info.field_name}' deve ser strings e não {type(value)}")
|
34
|
+
return value
|
35
|
+
|
36
|
+
class SendEmailParamsValidator(BaseModel):
|
37
|
+
"""
|
38
|
+
Classe para validar os parâmetros de envio de e-mail.
|
39
|
+
Atributos:
|
40
|
+
- to (list): Lista de destinatários do e-mail.
|
41
|
+
- message (str): Mensagem do e-mail.
|
42
|
+
- title (str): Título do e-mail.
|
43
|
+
- reply_to (str): Endereço de e-mail para resposta.
|
44
|
+
- attachments (list, opcional): Lista de anexos do e-mail.
|
45
|
+
- cc (list, opcional): Lista de destinatários em cópia do e-mail.
|
46
|
+
- cco (list, opcional): Lista de destinatários em cópia oculta do e-mail.
|
47
|
+
"""
|
48
|
+
|
49
|
+
to: list
|
50
|
+
message: str
|
51
|
+
title: str
|
52
|
+
reply_to:str
|
53
|
+
attachments: list = []
|
54
|
+
cc: list = []
|
55
|
+
cco: list = []
|
56
|
+
|
57
|
+
"""
|
58
|
+
Valida se o valor é uma string não vazia.
|
59
|
+
Parâmetros:
|
60
|
+
- value (str): Valor a ser validado.
|
61
|
+
- info (FieldInfo): Informações sobre o campo.
|
62
|
+
Retorna:
|
63
|
+
- str: O valor validado.
|
64
|
+
Lança:
|
65
|
+
- ValueError: Se o valor não for uma string ou estiver vazio.
|
66
|
+
"""
|
67
|
+
@field_validator('message','title','reply_to')
|
68
|
+
def check_str_input(cls, value, info):
|
69
|
+
if not isinstance(value, str) or not value.strip():
|
70
|
+
raise ValueError(f"O campo '{info.field_name}' deve ser strings e não {type(value)}")
|
71
|
+
return value
|
72
|
+
|
73
|
+
"""
|
74
|
+
Valida se o valor é uma lista.
|
75
|
+
Parâmetros:
|
76
|
+
- value (list): Valor a ser validado.
|
77
|
+
- info (FieldInfo): Informações sobre o campo.
|
78
|
+
Retorna:
|
79
|
+
- list: O valor validado.
|
80
|
+
Lança:
|
81
|
+
- ValueError: Se o valor não for uma lista.
|
82
|
+
"""
|
83
|
+
@field_validator('to','attachments','cc','cco')
|
84
|
+
def check_list_input(cls, value, info):
|
85
|
+
if not isinstance(value, list):
|
86
|
+
raise ValueError(f"O parametro '{info.field_name}' deve ser uma lista")
|
87
|
+
|
88
|
+
return value
|
89
|
+
|
90
|
+
class Email():
|
91
|
+
|
92
|
+
def __init__(self, email_sender, email_password):
|
93
|
+
"""
|
94
|
+
Inicializa uma instância da classe Email.
|
95
|
+
Args:
|
96
|
+
email_sender (str): O endereço de e-mail do remetente.
|
97
|
+
email_password (str): A senha do e-mail do remetente.
|
98
|
+
Raises:
|
99
|
+
ValueError: Se houver um erro na validação dos dados de entrada da inicialização da instância.
|
100
|
+
Returns:
|
101
|
+
None
|
102
|
+
"""
|
103
|
+
|
104
|
+
self.email_sender = email_sender
|
105
|
+
self.email_password = email_password
|
106
|
+
|
107
|
+
try:
|
108
|
+
|
109
|
+
InitParamsValidator(email_sender=email_sender, email_password=email_password)
|
110
|
+
|
111
|
+
except ValidationError as e:
|
112
|
+
|
113
|
+
raise ValueError("Erro na validação dos dados de input da inicialização da instância:", e.errors())
|
114
|
+
|
115
|
+
|
116
|
+
self.server = self.login_email()
|
117
|
+
|
118
|
+
if not isinstance(self.server, smtplib.SMTP) and 'status' in self.server and not self.server['status']:
|
119
|
+
|
120
|
+
raise ValueError("Erro na validação dos dados de input da inicialização da instância:", self.server['error'])
|
121
|
+
|
122
|
+
|
123
|
+
def login_email(self):
|
124
|
+
"""
|
125
|
+
Realiza o login no servidor de e-mail.
|
126
|
+
Returns:
|
127
|
+
smtplib.SMTP: Objeto que representa a conexão com o servidor de e-mail.
|
128
|
+
Raises:
|
129
|
+
dict: Dicionário contendo o status de erro caso ocorra uma exceção durante o login.
|
130
|
+
Example:
|
131
|
+
email_sender = 'seu_email@gmail.com'
|
132
|
+
email_password = 'sua_senha'
|
133
|
+
email = Email(email_sender, email_password)
|
134
|
+
server = email.login_email()
|
135
|
+
"""
|
136
|
+
|
137
|
+
try:
|
138
|
+
|
139
|
+
server = smtplib.SMTP('smtp.gmail.com', 587)
|
140
|
+
server.starttls()
|
141
|
+
server.login(self.email_sender, self.email_password)
|
142
|
+
|
143
|
+
return server
|
144
|
+
|
145
|
+
except Exception as e:
|
146
|
+
|
147
|
+
return {
|
148
|
+
'status':False,
|
149
|
+
'error':str(e)
|
150
|
+
}
|
151
|
+
|
152
|
+
|
153
|
+
def send_email( self, to : list , message : str , title : str , reply_to: str, attachments : list = [] , cc : list = [] , cco : list = [] ) -> dict:
|
154
|
+
"""
|
155
|
+
Envia um email com os parâmetros fornecidos.
|
156
|
+
Args:
|
157
|
+
to (list): Lista de destinatários do email.
|
158
|
+
message (str): Corpo do email.
|
159
|
+
title (str): Título do email.
|
160
|
+
reply_to (str): Endereço de email para resposta.
|
161
|
+
attachments (list, optional): Lista de caminhos dos arquivos anexos. Defaults to [].
|
162
|
+
cc (list, optional): Lista de destinatários em cópia. Defaults to [].
|
163
|
+
cco (list, optional): Lista de destinatários em cópia oculta. Defaults to [].
|
164
|
+
Returns:
|
165
|
+
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
|
+
Raises:
|
167
|
+
ValueError: Se houver erro na validação dos dados para o envio do email.
|
168
|
+
Example:
|
169
|
+
email = EmailSender()
|
170
|
+
to = ['example1@example.com', 'example2@example.com']
|
171
|
+
message = 'Olá, isso é um teste de email.'
|
172
|
+
title = 'Teste de Email'
|
173
|
+
reply_to = 'noreply@example.com'
|
174
|
+
attachments = ['/path/to/file1.txt', '/path/to/file2.txt']
|
175
|
+
cc = ['cc1@example.com', 'cc2@example.com']
|
176
|
+
cco = ['cco1@example.com', 'cco2@example.com']
|
177
|
+
result = email.send_email(to, message, title, reply_to, attachments, cc, cco)
|
178
|
+
print(result)
|
179
|
+
"""
|
180
|
+
|
181
|
+
try:
|
182
|
+
|
183
|
+
SendEmailParamsValidator(to=to, message=message, title=title, reply_to=reply_to, attachments=attachments, cc=cc, cco=cco)
|
184
|
+
|
185
|
+
except ValidationError as e:
|
186
|
+
|
187
|
+
raise ValueError("Erro na validação dos dados para o envio do email:", e.errors())
|
188
|
+
|
189
|
+
try:
|
190
|
+
|
191
|
+
msg = MIMEMultipart()
|
192
|
+
|
193
|
+
msg["From"] = self.email_sender
|
194
|
+
|
195
|
+
msg["To"] = (",").join(to)
|
196
|
+
|
197
|
+
msg["cc"] = (",").join(cc)
|
198
|
+
|
199
|
+
msg['Reply-To'] = reply_to
|
200
|
+
|
201
|
+
msg["Subject"] = title
|
202
|
+
|
203
|
+
for file in attachments:
|
204
|
+
|
205
|
+
try:
|
206
|
+
|
207
|
+
attachment = open(file, "rb")
|
208
|
+
|
209
|
+
part = MIMEBase("application", "octet-stream")
|
210
|
+
|
211
|
+
part.set_payload(attachment.read())
|
212
|
+
|
213
|
+
encoders.encode_base64(part)
|
214
|
+
|
215
|
+
part.add_header("Content-Disposition", f"attachment; filename={file.split('/')[-1]}")
|
216
|
+
|
217
|
+
msg.attach(part)
|
218
|
+
|
219
|
+
attachment.close()
|
220
|
+
|
221
|
+
except Exception as e:
|
222
|
+
|
223
|
+
return {
|
224
|
+
'status':False,
|
225
|
+
'error':str(e)
|
226
|
+
}
|
227
|
+
|
228
|
+
msg.attach(MIMEText(message, 'html'))
|
229
|
+
|
230
|
+
self.server.sendmail(self.email_sender, to + cc + cco, msg.as_string())
|
231
|
+
|
232
|
+
return True
|
233
|
+
|
234
|
+
except Exception as e:
|
235
|
+
|
236
|
+
return {
|
237
|
+
'status':False,
|
238
|
+
'error':str(e)
|
239
|
+
}
|